diff options
225 files changed, 6479 insertions, 2573 deletions
diff --git a/bin/ls/ls.c b/bin/ls/ls.c index ea0ba6d..e482e22 100644 --- a/bin/ls/ls.c +++ b/bin/ls/ls.c @@ -113,6 +113,7 @@ static int f_listdir; /* list actual directory, not contents */ static int f_listdot; /* list files beginning with . */ static int f_noautodot; /* do not automatically enable -A for root */ int f_longform; /* long listing format */ +static int f_nofollow; /* don't follow symbolic link arguments */ int f_nonprint; /* show unprintables as ? */ static int f_nosort; /* don't sort output */ int f_notabs; /* don't use tab-separated multi-col output */ @@ -234,6 +235,7 @@ main(int argc, char *argv[]) break; case 'H': fts_options |= FTS_COMFOLLOW; + f_nofollow = 0; break; case 'G': setenv("CLICOLOR", "", 1); @@ -241,11 +243,13 @@ main(int argc, char *argv[]) case 'L': fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; + f_nofollow = 0; break; case 'P': fts_options &= ~FTS_COMFOLLOW; fts_options &= ~FTS_LOGICAL; fts_options |= FTS_PHYSICAL; + f_nofollow = 1; break; case 'R': f_recursive = 1; @@ -396,10 +400,10 @@ main(int argc, char *argv[]) fts_options |= FTS_NOSTAT; /* - * If not -F, -d or -l options, follow any symbolic links listed on + * If not -F, -P, -d or -l options, follow any symbolic links listed on * the command line. */ - if (!f_longform && !f_listdir && (!f_type || f_slash)) + if (!f_nofollow && !f_longform && !f_listdir && (!f_type || f_slash)) fts_options |= FTS_COMFOLLOW; /* diff --git a/bin/pkill/pkill.1 b/bin/pkill/pkill.1 index 1db38f0..5eec10f 100644 --- a/bin/pkill/pkill.1 +++ b/bin/pkill/pkill.1 @@ -36,7 +36,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd May 16, 2009 +.Dd February 11, 2010 .Dt PKILL 1 .Os .Sh NAME @@ -44,7 +44,7 @@ .Nd find or signal processes by name .Sh SYNOPSIS .Nm pgrep -.Op Fl LSafilnovx +.Op Fl LSafilnoqvx .Op Fl F Ar pidfile .Op Fl G Ar gid .Op Fl M Ar core @@ -175,6 +175,8 @@ command. Select only the newest (most recently started) of the matching processes. .It Fl o Select only the oldest (least recently started) of the matching processes. +.It Fl q +Do not write anything to standard output. .It Fl s Ar sid Restrict matches to processes with a session ID in the comma-separated list diff --git a/bin/pkill/pkill.c b/bin/pkill/pkill.c index 9105c8c..a2d5f47 100644 --- a/bin/pkill/pkill.c +++ b/bin/pkill/pkill.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/user.h> +#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <limits.h> @@ -110,6 +111,7 @@ static int matchargs; static int fullmatch; static int kthreads; static int cflags = REG_EXTENDED; +static int quiet; static kvm_t *kd; static pid_t mypid; @@ -180,9 +182,11 @@ main(int argc, char **argv) debug_opt = 0; pidfile = NULL; pidfilelock = 0; - execf = coref = _PATH_DEVNULL; + quiet = 0; + execf = NULL; + coref = _PATH_DEVNULL; - while ((ch = getopt(argc, argv, "DF:G:ILM:N:P:SU:ad:fg:ij:lnos:t:u:vx")) != -1) + while ((ch = getopt(argc, argv, "DF:G:ILM:N:P:SU:ad:fg:ij:lnoqs:t:u:vx")) != -1) switch (ch) { case 'D': debug_opt++; @@ -257,6 +261,11 @@ main(int argc, char **argv) oldest = 1; criteria = 1; break; + case 'q': + if (!pgrep) + usage(); + quiet = 1; + break; case 's': makelist(&sidlist, LT_SID, optarg); criteria = 1; @@ -548,7 +557,7 @@ usage(void) const char *ustr; if (pgrep) - ustr = "[-LSfilnovx] [-d delim]"; + ustr = "[-LSfilnoqvx] [-d delim]"; else ustr = "[-signal] [-ILfinovx]"; @@ -566,6 +575,10 @@ show_process(const struct kinfo_proc *kp) { char **argv; + if (quiet) { + assert(pgrep); + return; + } if ((longfmt || !pgrep) && matchargs && (argv = kvm_getargv(kd, kp, 0)) != NULL) { printf("%d ", (int)kp->ki_pid); @@ -622,7 +635,8 @@ grepact(const struct kinfo_proc *kp) { show_process(kp); - printf("%s", delim); + if (!quiet) + printf("%s", delim); return (1); } diff --git a/bin/ps/ps.c b/bin/ps/ps.c index 904a430..0667db9 100644 --- a/bin/ps/ps.c +++ b/bin/ps/ps.c @@ -212,7 +212,8 @@ main(int argc, char *argv[]) init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id"); init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty"); init_list(&uidlist, addelem_uid, sizeof(uid_t), "user"); - memf = nlistf = _PATH_DEVNULL; + memf = _PATH_DEVNULL; + nlistf = NULL; while ((ch = getopt(argc, argv, PS_ARGS)) != -1) switch (ch) { case 'A': diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index cae0d51..7d0a7d2 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -118,6 +118,8 @@ firewall_type="UNKNOWN" # Firewall type (see /etc/rc.firewall) firewall_quiet="NO" # Set to YES to suppress rule display firewall_logging="NO" # Set to YES to enable events logging firewall_flags="" # Flags passed to ipfw when type is a file +firewall_coscripts="" # List of executables/scripts to run after + # firewall starts/stops firewall_client_net="192.0.2.0/24" # IPv4 Network address for "client" # firewall. #firewall_client_net_ipv6="2001:db8:2:1::/64" # IPv6 network prefix for diff --git a/etc/rc.d/ipfw b/etc/rc.d/ipfw index dd7ab55..689905d 100755 --- a/etc/rc.d/ipfw +++ b/etc/rc.d/ipfw @@ -14,6 +14,7 @@ name="ipfw" rcvar="firewall_enable" start_cmd="ipfw_start" start_precmd="ipfw_prestart" +start_postcmd="ipfw_poststart" stop_cmd="ipfw_stop" required_modules="ipfw" @@ -42,9 +43,6 @@ ipfw_start() [ -z "${firewall_script}" ] && firewall_script=/etc/rc.firewall if [ -r "${firewall_script}" ]; then - if [ -f /etc/rc.d/natd ] ; then - /etc/rc.d/natd quietstart - fi /bin/sh "${firewall_script}" "${_firewall_type}" echo 'Firewall rules loaded.' elif [ "`ipfw list 65535`" = "65535 deny ip from any to any" ]; then @@ -59,6 +57,19 @@ ipfw_start() echo 'Firewall logging enabled.' sysctl net.inet.ip.fw.verbose=1 >/dev/null fi +} + +ipfw_poststart() +{ + local _coscript + + # Start firewall coscripts + # + for _coscript in ${firewall_coscripts} ; do + if [ -f "${_coscript}" ]; then + ${_coscript} quietstart + fi + done # Enable the firewall # @@ -75,16 +86,25 @@ ipfw_start() ipfw_stop() { + local _coscript + # Disable the firewall # ${SYSCTL_W} net.inet.ip.fw.enable=0 if afexists inet6; then ${SYSCTL_W} net.inet6.ip6.fw.enable=0 fi - if [ -f /etc/rc.d/natd ] ; then - /etc/rc.d/natd quietstop - fi + + # Stop firewall coscripts + # + for _coscript in `reverse_list ${firewall_coscripts}` ; do + if [ -f "${_coscript}" ]; then + ${_coscript} quietstop + fi + done } load_rc_config $name +firewall_coscripts="/etc/rc.d/natd ${firewall_coscripts}" + run_rc_command $* diff --git a/games/fortune/datfiles/fortunes b/games/fortune/datfiles/fortunes index 06ec724..b04ba66 100644 --- a/games/fortune/datfiles/fortunes +++ b/games/fortune/datfiles/fortunes @@ -33335,6 +33335,12 @@ Miss, n.: % Mistakes are often the stepping stones to utter failure. % +Mistakeholder, n.: + A person who depends on accidental features or + implementation errors and so now has a vested + interest in keeping things from being fixed. + -- Chip Morningstar +% Mistrust first impulses; they are always right. % MIT: diff --git a/games/grdc/grdc.c b/games/grdc/grdc.c index d27b10e..c467b53 100644 --- a/games/grdc/grdc.c +++ b/games/grdc/grdc.c @@ -59,6 +59,7 @@ main(argc, argv) int argc; char **argv; { +struct timespec ts; long t, a; int i, j, s, k; int n; @@ -136,9 +137,9 @@ int t12; attrset(COLOR_PAIR(2)); } + time(&now); do { mask = 0; - time(&now); tm = localtime(&now); set(tm->tm_sec%10, 0); set(tm->tm_sec/10, 4); @@ -193,7 +194,19 @@ int t12; } movto(6, 0); refresh(); - sleep(1); + clock_gettime(CLOCK_REALTIME_FAST, &ts); + if (ts.tv_sec == now) { + if (ts.tv_nsec > 0) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000000 - ts.tv_nsec; + } else { + ts.tv_sec = 1; + ts.tv_nsec = 0; + } + nanosleep(&ts, NULL); + now = ts.tv_sec + 1; + } else + now = ts.tv_sec; if (sigtermed) { standend(); clear(); diff --git a/gnu/usr.bin/groff/tmac/mdoc.local b/gnu/usr.bin/groff/tmac/mdoc.local index 0be5920..4c83635 100644 --- a/gnu/usr.bin/groff/tmac/mdoc.local +++ b/gnu/usr.bin/groff/tmac/mdoc.local @@ -43,6 +43,7 @@ .ds doc-str-Lb-libdevstat Device Statistics Library (libdevstat, \-ldevstat) .ds doc-str-Lb-libdisk Interface to Slice and Partition Labels Library (libdisk, \-ldisk) .ds doc-str-Lb-libedit Line Editor and History Library (libedit, \-ledit) +.ds doc-str-Lb-libefi EFI Runtime Services Library (libefi, \-lefi) .ds doc-str-Lb-libelf ELF Parsing Library (libelf, \-lelf) .ds doc-str-Lb-libfetch File Transfer Library (libfetch, \-lfetch) .ds doc-str-Lb-libgeom Userland API Library for kernel GEOM subsystem (libgeom, \-lgeom) diff --git a/lib/libc/nls/msgcat.c b/lib/libc/nls/msgcat.c index 0b008ea..910c185 100644 --- a/lib/libc/nls/msgcat.c +++ b/lib/libc/nls/msgcat.c @@ -77,19 +77,22 @@ __FBSDID("$FreeBSD$"); #define NLERR ((nl_catd) -1) #define NLRETERR(errc) { errno = errc; return (NLERR); } -#define SAVEFAIL(n, e) { WLOCK(NLERR); \ - np = malloc(sizeof(struct catentry)); \ - if (np != NULL) { \ - np->name = strdup(n); \ - np->caterrno = e; \ - SLIST_INSERT_HEAD(&cache, np, list); \ - } \ - UNLOCK; \ - } +#define SAVEFAIL(n, l, e) { WLOCK(NLERR); \ + np = malloc(sizeof(struct catentry)); \ + if (np != NULL) { \ + np->name = strdup(n); \ + np->path = NULL; \ + np->lang = (l == NULL) ? NULL : strdup(l); \ + np->caterrno = e; \ + SLIST_INSERT_HEAD(&cache, np, list); \ + } \ + UNLOCK; \ + errno = e; \ + } static nl_catd load_msgcat(const char *, const char *, const char *); -static pthread_rwlock_t rwlock; +static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; struct catentry { SLIST_ENTRY(catentry) list; @@ -114,10 +117,12 @@ catopen(const char *name, int type) int saverr, spcleft; char path[PATH_MAX]; + /* sanity checking */ if (name == NULL || *name == '\0') NLRETERR(EINVAL); if (strchr(name, '/') != NULL) + /* have a pathname */ lang = NULL; else { if (type == NL_CAT_LOCALE) @@ -135,12 +140,14 @@ catopen(const char *name, int type) /* Try to get it from the cache first */ RLOCK(NLERR); SLIST_FOREACH(np, &cache, list) { - if (strcmp(np->name, name) == 0) { + if ((strcmp(np->name, name) == 0) && + ((lang != NULL && np->lang != NULL && + strcmp(np->lang, lang) == 0) || (np->lang == lang))) { if (np->caterrno != 0) { /* Found cached failing entry */ UNLOCK; NLRETERR(np->caterrno); - } else if (strcmp(np->lang, lang) == 0) { + } else { /* Found cached successful entry */ np->refcount++; UNLOCK; @@ -154,6 +161,7 @@ catopen(const char *name, int type) if (strchr(name, '/') != NULL) return (load_msgcat(name, name, lang)); + /* sanity checking */ if ((plang = cptr1 = strdup(lang)) == NULL) return (NLERR); if ((cptr = strchr(cptr1, '@')) != NULL) @@ -218,6 +226,7 @@ catopen(const char *name, int type) too_long: free(plang); free(base); + SAVEFAIL(name, lang, ENAMETOOLONG); NLRETERR(ENAMETOOLONG); } pathP += strlen(tmpptr); @@ -241,6 +250,7 @@ catopen(const char *name, int type) } free(plang); free(base); + SAVEFAIL(name, lang, ENOENT); NLRETERR(ENOENT); } @@ -317,6 +327,7 @@ catclose(nl_catd catd) { struct catentry *np; + /* sanity checking */ if (catd == NULL || catd == NLERR) { errno = EBADF; return (-1); @@ -325,13 +336,15 @@ catclose(nl_catd catd) /* Remove from cache if not referenced any more */ WLOCK(-1); SLIST_FOREACH(np, &cache, list) { - if ((np->catd->__size == catd->__size) && - memcmp((const void *)np->catd, (const void *)catd, np->catd->__size) == 0) { + if (catd == np->catd) { np->refcount--; if (np->refcount == 0) { munmap(catd->__data, (size_t)catd->__size); free(catd); SLIST_REMOVE(&cache, np, catentry, list); + free(np->name); + free(np->path); + free(np->lang); free(np); } break; @@ -357,10 +370,11 @@ load_msgcat(const char *path, const char *name, const char *lang) /* path/name will never be NULL here */ /* One more try in cache; if it was not found by name, - it might still be found by absolute path. */ + * it might still be found by absolute path. + */ RLOCK(NLERR); SLIST_FOREACH(np, &cache, list) { - if (strcmp(np->path, path) == 0) { + if ((np->path != NULL) && (strcmp(np->path, path) == 0)) { np->refcount++; UNLOCK; return (np->catd); @@ -369,36 +383,46 @@ load_msgcat(const char *path, const char *name, const char *lang) UNLOCK; if ((fd = _open(path, O_RDONLY)) == -1) { - SAVEFAIL(name, errno); - return (NLERR); + SAVEFAIL(name, lang, errno); + NLRETERR(errno); } if (_fstat(fd, &st) != 0) { - SAVEFAIL(name, errno); _close(fd); - return (NLERR); + SAVEFAIL(name, lang, EFTYPE); + NLRETERR(EFTYPE); } - data = mmap(0, (size_t)st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, - (off_t)0); - _close(fd); + /* If the file size cannot be held in size_t we cannot mmap() + * it to the memory. Probably, this will not be a problem given + * that catalog files are usually small. + */ + if (st.st_size > SIZE_T_MAX) { + _close(fd); + SAVEFAIL(name, lang, EFBIG); + NLRETERR(EFBIG); + } - if (data == MAP_FAILED) { - SAVEFAIL(name, errno); - return (NLERR); + if ((data = mmap(0, (size_t)st.st_size, PROT_READ, + MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) { + int saved_errno = errno; + _close(fd); + SAVEFAIL(name, lang, saved_errno); + NLRETERR(saved_errno); } + _close(fd); if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) != _NLS_MAGIC) { - SAVEFAIL(name, errno); munmap(data, (size_t)st.st_size); - NLRETERR(EINVAL); + SAVEFAIL(name, lang, EFTYPE); + NLRETERR(EFTYPE); } if ((catd = malloc(sizeof (*catd))) == NULL) { - SAVEFAIL(name, errno); munmap(data, (size_t)st.st_size); - return (NLERR); + SAVEFAIL(name, lang, ENOMEM); + NLRETERR(ENOMEM); } catd->__data = data; diff --git a/lib/libc/posix1e/mac.3 b/lib/libc/posix1e/mac.3 index c570998..6499d6b 100644 --- a/lib/libc/posix1e/mac.3 +++ b/lib/libc/posix1e/mac.3 @@ -154,8 +154,8 @@ system objects, but without policy-specific knowledge. .Sh STANDARDS These APIs are loosely based on the APIs described in POSIX.1e, as described in IEEE POSIX.1e draft 17. -However, the resemblence of these APIS to the POSIX APIs is loose, as the -PSOXI APIS were unable to express some notinos required for flexible and +However, the resemblence of these APIs to the POSIX APIs is loose, as the +POSIX APIs were unable to express some notions required for flexible and extensible access control. .Sh HISTORY Support for Mandatory Access Control was introduced in diff --git a/lib/libc/stdlib/strfmon.c b/lib/libc/stdlib/strfmon.c index f12c8de..6c28254 100644 --- a/lib/libc/stdlib/strfmon.c +++ b/lib/libc/stdlib/strfmon.c @@ -413,7 +413,7 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space, *cs_precedes = lc->int_n_cs_precedes; *sep_by_space = lc->int_n_sep_by_space; *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn; - *signstr = (lc->negative_sign == '\0') ? "-" + *signstr = (lc->negative_sign[0] == '\0') ? "-" : lc->negative_sign; } else if (flags & USE_INTL_CURRENCY) { *cs_precedes = lc->int_p_cs_precedes; @@ -424,7 +424,7 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space, *cs_precedes = lc->n_cs_precedes; *sep_by_space = lc->n_sep_by_space; *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn; - *signstr = (lc->negative_sign == '\0') ? "-" + *signstr = (lc->negative_sign[0] == '\0') ? "-" : lc->negative_sign; } else { *cs_precedes = lc->p_cs_precedes; diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2 index 9d8f550..8265fb6 100644 --- a/lib/libc/sys/ptrace.2 +++ b/lib/libc/sys/ptrace.2 @@ -2,7 +2,7 @@ .\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $ .\" .\" This file is in the public domain. -.Dd March 27, 2009 +.Dd February 11, 2010 .Dt PTRACE 2 .Os .Sh NAME @@ -327,6 +327,68 @@ This request will trace the specified process on each system call exit. .It PT_SYSCALL This request will trace the specified process on each system call entry and exit. +.It PT_VM_TIMESTAMP +This request returns the generation number or timestamp of the memory map of +the traced process as the return value from +.Fn ptrace . +This provides a low-cost way for the tracing process to determine if the +VM map changed since the last time this request was made. +.It PT_VM_ENTRY +This request is used to iterate over the entries of the VM map of the traced +process. +The +.Fa addr +argument specifies a pointer to a +.Vt "struct ptrace_vm_entry" , +which is defined as follows: +.Bd -literal +struct ptrace_vm_entry { + int pve_entry; + int pve_timestamp; + u_long pve_start; + u_long pve_end; + u_long pve_offset; + u_int pve_prot; + u_int pve_pathlen; + long pve_fileid; + uint32_t pve_fsid; + char *pve_path; +}; +.Ed +.Pp +The first entry is returned by setting +.Va pve_entry +to zero. +Subsequent entries are returned by leaving +.Va pve_entry +unmodified from the value returned by previous requests. +The +.Va pve_timestamp +field can be used to detect changes to the VM map while iterating over the +entries. +The tracing process can then take appropriate action, such as restarting. +By setting +.Va pve_pathlen +to a non-zero value on entry, the pathname of the backing object is returned +in the buffer pointed to by +.Va pve_path , +provided the entry is backed by a vnode. +The +.Va pve_pathlen +field is updated with the actual length of the pathname (including the +terminating null character). +The +.Va pve_offset +field is the offset within the backing object at which the range starts. +The range is located in the VM space at +.Va pve_start +and extends up to +.Va pve_end +(inclusive). +.Pp +The +.Fa data +argument is ignored. .El .Pp Additionally, machine-specific requests can exist. @@ -376,6 +438,11 @@ or .Dv PT_SETDBREGS was attempted on a process with no valid register set. (This is normally true only of system processes.) +.It +.Dv PT_VM_ENTRY +was given an invalid value for +.Fa pve_entry . +This can also be caused by changes to the VM map of the process. .El .It Bq Er EBUSY .Bl -bullet -compact @@ -405,6 +472,22 @@ on a process in violation of the requirements listed under .Dv PT_ATTACH above. .El +.It Bq Er ENOENT +.Bl -bullet -compact +.It +.Dv PT_VM_ENTRY +previously returned the last entry of the memory map. +No more entries exist. +.El +.It Bq Er ENAMETOOLONG +.Bl -bullet -compact +.It +.Dv PT_VM_ENTRY +cannot return the pathname of the backing object because the buffer is not big +enough. +.Fa pve_pathlen +holds the minimum buffer size required on return. +.El .El .Sh SEE ALSO .Xr execve 2 , diff --git a/lib/libefi/libefi.3 b/lib/libefi/libefi.3 index d42bd6b..a8d6128 100644 --- a/lib/libefi/libefi.3 +++ b/lib/libefi/libefi.3 @@ -25,24 +25,28 @@ .\" .\" $FreeBSD$ .\" -.Dd Januari 29, 2010 +.Dd January 29, 2010 .Dt LIBEFI 3 .Os .Sh NAME .Nm efi_getvar , efi_nextvarname , efi_setvar -.Nd Interface for accessing the EFI variable services +.Nd "interface for accessing the EFI variable services" .Sh LIBRARY .Lb libefi .Sh SYNOPSIS .In libefi.h .Ft int -.Fn efi_getvar "char *name" "uuid_t *vendor" "uint32_t *attrib" \ - "size_t *datasize" "void *data" +.Fo efi_getvar +.Fa "char *name" "uuid_t *vendor" "uint32_t *attrib" +.Fa "size_t *datasize" "void *data" +.Fc .Ft int .Fn efi_nextvarname "size_t *namesize" "char *name" "uuid_t *vendor" .Ft int -.Fn efi_setvar "char *name" "uuid_t *vendor" "uint32_t attrib" \ - "size_t datasize" "void *data" +.Fo efi_setvar +.Fa "char *name" "uuid_t *vendor" "uint32_t attrib" +.Fa "size_t datasize" "void *data" +.Fc .Sh DESCRIPTION The .Nm libefi @@ -53,18 +57,19 @@ The .Fn efi_nextvarname function is used to enumerate the variables. The -.Nm namesize +.Fa namesize parameter needs to be set to the size of the buffer pointed to by -.Nm name . +.Fa name . On return, -.Nm namesize -is set to the length of the variable name (including the terminating '\\0') +.Fa namesize +is set to the length of the variable name (including the terminating +.Ql \e0 ) irrespective of whether the buffer was big enough. The buffer pointed to by -.Nm name +.Fa name contains the full or partial variable name on return. Only on successful completion of the request is the -.Nm vendor +.Fa vendor updated. The values returned should be passed to successive calls to .Fn efi_nextvarname @@ -76,11 +81,11 @@ can be passed to .Fn efi_getvar to obtain the value and attribute of the variable. The buffer that is to contain the value is specified by -.Nm data +.Fa data and the size of the buffer is given by -.Nm datasize . +.Fa datasize . The attribute pointed to by -.Nm attrib +.Fa attrib consists of the bit values defined by the EFI specification. .Pp Variables can be created, modified and deleted using the @@ -91,15 +96,17 @@ order for the request to succeed. Note that for runtime accessable variables the boottime accessable bit must be set as well. To delete a variable, set -.Nm datasize +.Fa datasize to 0. .Pp The vendor UUID is used to avoid collisions between variable names of different vendors. -Variables created for use by FreeBSD should use the -.Nm EFI_FREEBSD_VARIABLE +Variables created for use by +.Fx +should use the +.Dv EFI_FREEBSD_VARIABLE UUID as defined in the -.Nm libefi +.In libefi.h header file. .Sh RETURN VALUES Upon successful completion, these functions return 0. @@ -127,10 +134,10 @@ the buffer provided. The .Nm libefi library first appeared in -.Fx 9 +.Fx 9.0 for the ia64 architecture. .Sh AUTHORS The .Nm libefi -library and corresponding manual page were written by +library and this manual page were written by .An Marcel Moolenaar Aq marcel@FreeBSD.org . diff --git a/lib/libufs/cgroup.c b/lib/libufs/cgroup.c index 2185682..28e5ea8 100644 --- a/lib/libufs/cgroup.c +++ b/lib/libufs/cgroup.c @@ -59,7 +59,7 @@ cgread1(struct uufsd *disk, int c) fs = &disk->d_fs; - if (c >= fs->fs_ncg) { + if ((unsigned)c >= fs->fs_ncg) { return (0); } ccg = fsbtodb(fs, cgtod(fs, c)) * disk->d_bsize; diff --git a/lib/libufs/sblock.c b/lib/libufs/sblock.c index c9125f2..8986290 100644 --- a/lib/libufs/sblock.c +++ b/lib/libufs/sblock.c @@ -93,7 +93,7 @@ int sbwrite(struct uufsd *disk, int all) { struct fs *fs; - int i; + unsigned i; ERROR(disk, NULL); diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h index bdf2812..7981862 100644 --- a/lib/libusb/libusb.h +++ b/lib/libusb/libusb.h @@ -29,12 +29,7 @@ #include <sys/time.h> #include <sys/types.h> -#include <sys/endian.h> -#include <stdint.h> -#include <time.h> -#include <string.h> -#include <pthread.h> #ifdef __cplusplus diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index 25520d2..7e9e885 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -25,17 +25,16 @@ * SUCH DAMAGE. */ +#include <assert.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <poll.h> #include <pthread.h> -#include <time.h> #include <errno.h> #include <sys/ioctl.h> -#include <sys/filio.h> +#include <sys/fcntl.h> #include <sys/queue.h> -#include <sys/endian.h> #include "libusb20.h" #include "libusb20_desc.h" @@ -73,6 +72,7 @@ libusb_init(libusb_context **context) { struct libusb_context *ctx; char *debug; + int flag; int ret; ctx = malloc(sizeof(*ctx)); @@ -103,10 +103,12 @@ libusb_init(libusb_context **context) return (LIBUSB_ERROR_OTHER); } /* set non-blocking mode on the control pipe to avoid deadlock */ - ret = 1; - ioctl(ctx->ctrl_pipe[0], FIONBIO, &ret); - ret = 1; - ioctl(ctx->ctrl_pipe[1], FIONBIO, &ret); + flag = 1; + ret = fcntl(ctx->ctrl_pipe[0], O_NONBLOCK, &flag); + assert(ret != -1 && "Couldn't set O_NONBLOCK for ctx->ctrl_pipe[0]"); + flag = 1; + ret = fcntl(ctx->ctrl_pipe[1], O_NONBLOCK, &flag); + assert(ret != -1 && "Couldn't set O_NONBLOCK for ctx->ctrl_pipe[1]"); libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->ctrl_pipe[0], POLLIN); diff --git a/lib/libusb/libusb10_desc.c b/lib/libusb/libusb10_desc.c index 0215bce..18a8a8b 100644 --- a/lib/libusb/libusb10_desc.c +++ b/lib/libusb/libusb10_desc.c @@ -26,7 +26,6 @@ #include <stdlib.h> #include <stdio.h> -#include <pthread.h> #include <sys/queue.h> #include "libusb20.h" diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c index 3d6daef..119132e 100644 --- a/lib/libusb/libusb10_io.c +++ b/lib/libusb/libusb10_io.c @@ -32,7 +32,6 @@ #include <time.h> #include <errno.h> #include <sys/queue.h> -#include <sys/endian.h> #include "libusb20.h" #include "libusb20_desc.h" diff --git a/lib/libusb/libusb20_compat01.c b/lib/libusb/libusb20_compat01.c index 48a7dd5..2fbbae7 100644 --- a/lib/libusb/libusb20_compat01.c +++ b/lib/libusb/libusb20_compat01.c @@ -829,6 +829,7 @@ usb_find_devices(void) struct libusb20_device *pdev; struct usb_device *udev; struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + int devnum; int err; /* cleanup after last device search */ @@ -855,6 +856,7 @@ usb_find_devices(void) } /* iterate all devices */ + devnum = 1; pdev = NULL; while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) { udev = malloc(sizeof(*udev)); @@ -891,6 +893,7 @@ usb_find_devices(void) /* truncate number of configurations */ udev->descriptor.bNumConfigurations = USB_MAXCONFIG; } + udev->devnum = devnum++; /* link together the two structures */ udev->dev = pdev; pdev->privLuData = udev; diff --git a/lib/libusb/libusb20_desc.c b/lib/libusb/libusb20_desc.c index 5206cf4..7d462a6 100644 --- a/lib/libusb/libusb20_desc.c +++ b/lib/libusb/libusb20_desc.c @@ -27,7 +27,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <poll.h> #include <sys/queue.h> #include "libusb20.h" diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c index 892ba0e..76e96d5 100644 --- a/lib/libusb/libusb20_ugen20.c +++ b/lib/libusb/libusb20_ugen20.c @@ -31,7 +31,6 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> -#include <poll.h> #include <fcntl.h> #include <errno.h> diff --git a/lib/libusb/usb.h b/lib/libusb/usb.h index 3963a9f..5d9c603 100644 --- a/lib/libusb/usb.h +++ b/lib/libusb/usb.h @@ -27,8 +27,7 @@ #ifndef _LIBUSB20_COMPAT_01_H_ #define _LIBUSB20_COMPAT_01_H_ -#include <sys/stdint.h> -#include <sys/endian.h> +#include <stdint.h> #include <sys/types.h> #include <sys/param.h> diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c index 82683f5..711234f 100644 --- a/libexec/ftpd/logwtmp.c +++ b/libexec/ftpd/logwtmp.c @@ -75,6 +75,7 @@ ftpd_logwtmp(char *id, char *user, struct sockaddr *addr) ut.ut_pid = getpid(); gettimeofday(&ut.ut_tv, NULL); (void)strncpy(ut.ut_id, id, sizeof(ut.ut_id)); + (void)strncpy(ut.ut_line, "ftpd", sizeof(ut.ut_line)); pututxline(&ut); } diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c index ee37a88..a715ec1 100644 --- a/sbin/growfs/growfs.c +++ b/sbin/growfs/growfs.c @@ -174,10 +174,9 @@ static void growfs(int fsi, int fso, unsigned int Nflag) { DBG_FUNC("growfs") - int i; - int cylno, j; time_t utime; - int width; + uint cylno; + int i, j, width; char tmpbuf[100]; #ifdef FSIRAND static int randinit=0; @@ -373,10 +372,11 @@ initcg(int cylno, time_t utime, int fso, unsigned int Nflag) { DBG_FUNC("initcg") static void *iobuf; - long d, dlower, dupper, blkno, start; + long blkno, start; ufs2_daddr_t i, cbase, dmax; struct ufs1_dinode *dp1; struct csum *cs; + uint d, dupper, dlower; if (iobuf == NULL && (iobuf = malloc(sblock.fs_bsize)) == NULL) { errx(37, "panic: cannot allocate I/O buffer"); @@ -436,7 +436,7 @@ initcg(int cylno, time_t utime, int fso, unsigned int Nflag) acg.cg_nextfreeoff = acg.cg_clusteroff + howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); } - if (acg.cg_nextfreeoff > sblock.fs_cgsize) { + if (acg.cg_nextfreeoff > (unsigned)sblock.fs_cgsize) { /* * This should never happen as we would have had that panic * already on file system creation @@ -751,7 +751,7 @@ updjcg(int cylno, time_t utime, int fsi, int fso, unsigned int Nflag) * needed, update the free space in the superblock. */ acg.cg_time = utime; - if (cylno == sblock.fs_ncg - 1) { + if ((unsigned)cylno == sblock.fs_ncg - 1) { /* * This is still the last cylinder group. */ @@ -945,8 +945,8 @@ updcsloc(time_t utime, int fsi, int fso, unsigned int Nflag) int ocscg, ncscg; int blocks; ufs2_daddr_t cbase, dupper, odupper, d, f, g; - int ind; - int cylno, inc; + int ind, inc; + uint cylno; struct gfs_bpp *bp; int i, l; int lcs=0; diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 index ea01455..d3f6717 100644 --- a/sbin/mount/mount.8 +++ b/sbin/mount/mount.8 @@ -28,7 +28,7 @@ .\" @(#)mount.8 8.8 (Berkeley) 6/16/94 .\" $FreeBSD$ .\" -.Dd March 11, 2008 +.Dd February 10, 2010 .Dt MOUNT 8 .Os .Sh NAME @@ -433,8 +433,8 @@ However, for the following file system types: .Cm cd9660 , .Cm mfs , .Cm msdosfs , +.Cm newnfs , .Cm nfs , -.Cm nfs4 , .Cm ntfs , .Cm nwfs , .Cm nullfs , diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c index 85777f3..6663b18 100644 --- a/sbin/newfs/mkfs.c +++ b/sbin/newfs/mkfs.c @@ -114,10 +114,13 @@ void mkfs(struct partition *pp, char *fsys) { int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg; - long i, j, cylno, csfrags; + long i, j, csfrags; + uint cg; time_t utime; quad_t sizepb; int width; + ino_t maxinum; + int minfragsperinode; /* minimum ratio of frags to inodes */ char tmpbuf[100]; /* XXX this will break in about 2,500 years */ union { struct fs fdummy; @@ -170,6 +173,8 @@ mkfs(struct partition *pp, char *fsys) if (sblock.fs_avgfpdir <= 0) printf("illegal expected number of files per directory %d\n", sblock.fs_avgfpdir), exit(15); + +restart: /* * collect and verify the block and fragment sizes */ @@ -216,6 +221,8 @@ mkfs(struct partition *pp, char *fsys) sblock.fs_fsize, MAXFRAG, sblock.fs_bsize / MAXFRAG); sblock.fs_fsize = sblock.fs_bsize / MAXFRAG; } + if (maxbsize == 0) + maxbsize = bsize; if (maxbsize < bsize || !POWEROF2(maxbsize)) { sblock.fs_maxbsize = sblock.fs_bsize; printf("Extent size set to %d\n", sblock.fs_maxbsize); @@ -225,6 +232,14 @@ mkfs(struct partition *pp, char *fsys) } else { sblock.fs_maxbsize = maxbsize; } + /* + * Maxcontig sets the default for the maximum number of blocks + * that may be allocated sequentially. With file system clustering + * it is possible to allocate contiguous blocks up to the maximum + * transfer size permitted by the controller or buffering. + */ + if (maxcontig == 0) + maxcontig = MAX(1, MAXPHYS / bsize); sblock.fs_maxcontig = maxcontig; if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) { sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize; @@ -315,9 +330,26 @@ mkfs(struct partition *pp, char *fsys) * can put into each cylinder group. If this is too big, we reduce * the density until it fits. */ + maxinum = (((int64_t)(1)) << 32) - INOPB(&sblock); + minfragsperinode = 1 + fssize / maxinum; + if (density == 0) { + density = MAX(NFPI, minfragsperinode) * fsize; + } else if (density < minfragsperinode * fsize) { + origdensity = density; + density = minfragsperinode * fsize; + fprintf(stderr, "density increased from %d to %d\n", + origdensity, density); + } origdensity = density; for (;;) { fragsperinode = MAX(numfrags(&sblock, density), 1); + if (fragsperinode < minfragsperinode) { + bsize <<= 1; + fsize <<= 1; + printf("Block size too small for a file system %s %d\n", + "of this size. Increasing blocksize to", bsize); + goto restart; + } minfpg = fragsperinode * INOPB(&sblock); if (minfpg > sblock.fs_size) minfpg = sblock.fs_size; @@ -406,7 +438,10 @@ mkfs(struct partition *pp, char *fsys) if (sblock.fs_sbsize > SBLOCKSIZE) sblock.fs_sbsize = SBLOCKSIZE; sblock.fs_minfree = minfree; - sblock.fs_maxbpg = maxbpg; + if (maxbpg == 0) + sblock.fs_maxbpg = MAXBLKPG(sblock.fs_bsize); + else + sblock.fs_maxbpg = maxbpg; sblock.fs_optim = opt; sblock.fs_cgrotor = 0; sblock.fs_pendingblocks = 0; @@ -476,9 +511,9 @@ mkfs(struct partition *pp, char *fsys) fsdummy.fs_magic = 0; bwrite(&disk, part_ofs + SBLOCK_UFS1 / disk.d_bsize, chdummy, SBLOCKSIZE); - for (i = 0; i < fsdummy.fs_ncg; i++) + for (cg = 0; cg < fsdummy.fs_ncg; cg++) bwrite(&disk, part_ofs + fsbtodb(&fsdummy, - cgsblock(&fsdummy, i)), chdummy, SBLOCKSIZE); + cgsblock(&fsdummy, cg)), chdummy, SBLOCKSIZE); } } if (!Nflag) @@ -516,11 +551,11 @@ mkfs(struct partition *pp, char *fsys) * writing out in each cylinder group. */ bcopy((char *)&sblock, iobuf, SBLOCKSIZE); - for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { - initcg(cylno, utime); + for (cg = 0; cg < sblock.fs_ncg; cg++) { + initcg(cg, utime); j = snprintf(tmpbuf, sizeof(tmpbuf), " %jd%s", - (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)), - cylno < (sblock.fs_ncg-1) ? "," : ""); + (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cg)), + cg < (sblock.fs_ncg-1) ? "," : ""); if (j < 0) tmpbuf[j = 0] = '\0'; if (i + j >= width) { @@ -574,7 +609,8 @@ mkfs(struct partition *pp, char *fsys) void initcg(int cylno, time_t utime) { - long i, j, d, dlower, dupper, blkno, start; + long blkno, start; + uint i, j, d, dlower, dupper; ufs2_daddr_t cbase, dmax; struct ufs1_dinode *dp1; struct ufs2_dinode *dp2; @@ -631,7 +667,7 @@ initcg(int cylno, time_t utime) acg.cg_nextfreeoff = acg.cg_clusteroff + howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); } - if (acg.cg_nextfreeoff > sblock.fs_cgsize) { + if (acg.cg_nextfreeoff > (unsigned)sblock.fs_cgsize) { printf("Panic: cylinder group too big\n"); exit(37); } @@ -880,7 +916,8 @@ makedir(struct direct *protodir, int entries) ufs2_daddr_t alloc(int size, int mode) { - int i, d, blkno, frag; + int i, blkno, frag; + uint d; bread(&disk, part_ofs + fsbtodb(&sblock, cgtod(&sblock, 0)), (char *)&acg, sblock.fs_cgsize); diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c index b70ce1b..8867306 100644 --- a/sbin/newfs/newfs.c +++ b/sbin/newfs/newfs.c @@ -79,38 +79,6 @@ __FBSDID("$FreeBSD$"); #include "newfs.h" -/* - * The following two constants set the default block and fragment sizes. - * Both constants must be a power of 2 and meet the following constraints: - * MINBSIZE <= DESBLKSIZE <= MAXBSIZE - * sectorsize <= DESFRAGSIZE <= DESBLKSIZE - * DESBLKSIZE / DESFRAGSIZE <= 8 - */ -#define DFL_FRAGSIZE 2048 -#define DFL_BLKSIZE 16384 - -/* - * Cylinder groups may have up to MAXBLKSPERCG blocks. The actual - * number used depends upon how much information can be stored - * in a cylinder group map which must fit in a single file system - * block. The default is to use as many as possible blocks per group. - */ -#define MAXBLKSPERCG 0x7fffffff /* desired fs_fpg ("infinity") */ - -/* - * MAXBLKPG determines the maximum number of data blocks which are - * placed in a single cylinder group. The default is one indirect - * block worth of data blocks. - */ -#define MAXBLKPG(bsize) ((bsize) / sizeof(ufs2_daddr_t)) - -/* - * Each file system has a number of inodes statically allocated. - * We allocate one inode slot per NFPI fragments, expecting this - * to be far more than we will ever need. - */ -#define NFPI 4 - int Eflag; /* Erase previous disk contents */ int Lflag; /* add a volume label */ int Nflag; /* run without writing file system */ @@ -387,25 +355,11 @@ main(int argc, char *argv[]) fsize = MAX(DFL_FRAGSIZE, sectorsize); if (bsize <= 0) bsize = MIN(DFL_BLKSIZE, 8 * fsize); - if (maxbsize == 0) - maxbsize = bsize; - /* - * Maxcontig sets the default for the maximum number of blocks - * that may be allocated sequentially. With file system clustering - * it is possible to allocate contiguous blocks up to the maximum - * transfer size permitted by the controller or buffering. - */ - if (maxcontig == 0) - maxcontig = MAX(1, MAXPHYS / bsize); - if (density == 0) - density = NFPI * fsize; if (minfree < MINFREE && opt != FS_OPTSPACE) { fprintf(stderr, "Warning: changing optimization to space "); fprintf(stderr, "because minfree is less than %d%%\n", MINFREE); opt = FS_OPTSPACE; } - if (maxbpg == 0) - maxbpg = MAXBLKPG(bsize); realsectorsize = sectorsize; if (sectorsize != DEV_BSIZE) { /* XXX */ int secperblk = sectorsize / DEV_BSIZE; diff --git a/sbin/newfs/newfs.h b/sbin/newfs/newfs.h index 0d7cb62..9da3226 100644 --- a/sbin/newfs/newfs.h +++ b/sbin/newfs/newfs.h @@ -41,6 +41,38 @@ #include <libufs.h> /* + * The following two constants set the default block and fragment sizes. + * Both constants must be a power of 2 and meet the following constraints: + * MINBSIZE <= DESBLKSIZE <= MAXBSIZE + * sectorsize <= DESFRAGSIZE <= DESBLKSIZE + * DESBLKSIZE / DESFRAGSIZE <= 8 + */ +#define DFL_FRAGSIZE 2048 +#define DFL_BLKSIZE 16384 + +/* + * Cylinder groups may have up to MAXBLKSPERCG blocks. The actual + * number used depends upon how much information can be stored + * in a cylinder group map which must fit in a single file system + * block. The default is to use as many as possible blocks per group. + */ +#define MAXBLKSPERCG 0x7fffffff /* desired fs_fpg ("infinity") */ + +/* + * MAXBLKPG determines the maximum number of data blocks which are + * placed in a single cylinder group. The default is one indirect + * block worth of data blocks. + */ +#define MAXBLKPG(bsize) ((bsize) / sizeof(ufs2_daddr_t)) + +/* + * Each file system has a number of inodes statically allocated. + * We allocate one inode slot per NFPI fragments, expecting this + * to be far more than we will ever need. + */ +#define NFPI 4 + +/* * variables set up by front end. */ extern int Eflag; /* Erase previous disk contents */ diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index 616b4cd..b450f2d 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -75,7 +75,7 @@ usage(void) { (void)fprintf(stderr, "%s\n%s\n", - "usage: sysctl [-bdehNnoqx] name[=value] ...", + "usage: sysctl [-bdehiNnoqx] name[=value] ...", " sysctl [-bdehNnoqx] -a"); exit(1); } diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c index 1b5f21a..e4adb52 100644 --- a/sbin/tunefs/tunefs.c +++ b/sbin/tunefs/tunefs.c @@ -301,7 +301,7 @@ main(int argc, char *argv[]) } if (fflag) { name = "average file size"; - if (sblock.fs_avgfilesize == fvalue) { + if (sblock.fs_avgfilesize == (unsigned)fvalue) { warnx("%s remains unchanged as %d", name, fvalue); } else { @@ -427,7 +427,7 @@ main(int argc, char *argv[]) } if (sflag) { name = "expected number of files per directory"; - if (sblock.fs_avgfpdir == svalue) { + if (sblock.fs_avgfpdir == (unsigned)svalue) { warnx("%s remains unchanged as %d", name, svalue); } else { diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index b4d0581..ceb3859 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -29,8 +29,10 @@ MAN= aac.4 \ aio.4 \ alc.4 \ ale.4 \ + alpm.4 \ altq.4 \ amd.4 \ + amdpm.4 \ ${_amdsbwd.4} \ ${_amdsmb.4} \ ${_amdtemp.4} \ @@ -199,6 +201,7 @@ MAN= aac.4 \ mac_stub.4 \ mac_test.4 \ malo.4 \ + mcd.4 \ md.4 \ mem.4 \ meteor.4 \ @@ -297,10 +300,12 @@ MAN= aac.4 \ ${_nxge.4} \ ohci.4 \ orm.4 \ + ${_padlock.4} \ pass.4 \ patm.4 \ pccard.4 \ pccbb.4 \ + pcf.4 \ pci.4 \ pcib.4 \ pcic.4 \ @@ -334,6 +339,7 @@ MAN= aac.4 \ sbp.4 \ sbp_targ.4 \ scc.4 \ + scd.4 \ sched_4bsd.4 \ sched_ule.4 \ screen.4 \ @@ -453,6 +459,7 @@ MAN= aac.4 \ uvscom.4 \ vga.4 \ vge.4 \ + viapm.4 \ vinum.4 \ vkbd.4 \ vlan.4 \ @@ -649,6 +656,7 @@ _nfsmb.4= nfsmb.4 _nve.4= nve.4 _nvram.4= nvram.4 _nxge.4= nxge.4 +_padlock.4= padlock.4 _rr232x.4= rr232x.4 _speaker.4= speaker.4 _spkr.4= spkr.4 diff --git a/share/man/man4/acpi_asus.4 b/share/man/man4/acpi_asus.4 index 3fa7765..f3b99da 100644 --- a/share/man/man4/acpi_asus.4 +++ b/share/man/man4/acpi_asus.4 @@ -25,8 +25,8 @@ .\" .\" $FreeBSD$ .\" -.Dd December 17, 2008 -.Dt ACPI_ASUS 4 i386 +.Dd February 8, 2010 +.Dt ACPI_ASUS 4 .Os .Sh NAME .Nm acpi_asus diff --git a/share/man/man4/acpi_fujitsu.4 b/share/man/man4/acpi_fujitsu.4 index 1959bf6..ef72fee 100644 --- a/share/man/man4/acpi_fujitsu.4 +++ b/share/man/man4/acpi_fujitsu.4 @@ -25,8 +25,8 @@ .\" .\" $FreeBSD$ .\" -.Dd March 20, 2005 -.Dt ACPI_FUJITSU 4 i386 +.Dd February 8, 2010 +.Dt ACPI_FUJITSU 4 .Os .Sh NAME .Nm acpi_fujitsu diff --git a/share/man/man4/acpi_hp.4 b/share/man/man4/acpi_hp.4 index efa9d0b..94828d7 100644 --- a/share/man/man4/acpi_hp.4 +++ b/share/man/man4/acpi_hp.4 @@ -24,8 +24,8 @@ .\" .\" $FreeBSD$ .\" -.Dd June 30, 2009 -.Dt ACPI_HP 4 i386 +.Dd February 8, 2010 +.Dt ACPI_HP 4 .Os .Sh NAME .Nm acpi_hp diff --git a/share/man/man4/acpi_ibm.4 b/share/man/man4/acpi_ibm.4 index f0c165d..376b64d 100644 --- a/share/man/man4/acpi_ibm.4 +++ b/share/man/man4/acpi_ibm.4 @@ -25,8 +25,8 @@ .\" .\" $FreeBSD$ .\" -.Dd January 14, 2006 -.Dt ACPI_IBM 4 i386 +.Dd February 8, 2010 +.Dt ACPI_IBM 4 .Os .Sh NAME .Nm acpi_ibm diff --git a/share/man/man4/acpi_panasonic.4 b/share/man/man4/acpi_panasonic.4 index 5d8c86a..4bd7038 100644 --- a/share/man/man4/acpi_panasonic.4 +++ b/share/man/man4/acpi_panasonic.4 @@ -25,8 +25,8 @@ .\" .\" $FreeBSD$ .\" -.Dd September 17, 2009 -.Dt ACPI_PANASONIC 4 i386 +.Dd February 8, 2010 +.Dt ACPI_PANASONIC 4 .Os .Sh NAME .Nm acpi_panasonic diff --git a/share/man/man4/acpi_sony.4 b/share/man/man4/acpi_sony.4 index 3a06070..f0fb0e0 100644 --- a/share/man/man4/acpi_sony.4 +++ b/share/man/man4/acpi_sony.4 @@ -24,8 +24,8 @@ .\" .\" $FreeBSD$ .\" -.Dd February 7, 2010 -.Dt ACPI_SONY 4 i386 +.Dd February 8, 2010 +.Dt ACPI_SONY 4 .Os .Sh NAME .Nm acpi_sony diff --git a/share/man/man4/acpi_toshiba.4 b/share/man/man4/acpi_toshiba.4 index cb26842..134ecc8 100644 --- a/share/man/man4/acpi_toshiba.4 +++ b/share/man/man4/acpi_toshiba.4 @@ -25,8 +25,8 @@ .\" .\" $FreeBSD$ .\" -.Dd February 19, 2004 -.Dt ACPI_TOSHIBA 4 i386 +.Dd February 8, 2010 +.Dt ACPI_TOSHIBA 4 .Os .Sh NAME .Nm acpi_toshiba diff --git a/share/man/man4/acpi_wmi.4 b/share/man/man4/acpi_wmi.4 index c73307c..cc61527 100644 --- a/share/man/man4/acpi_wmi.4 +++ b/share/man/man4/acpi_wmi.4 @@ -24,8 +24,8 @@ .\" .\" $FreeBSD$ .\" -.Dd June 30, 2009 -.Dt ACPI_WMI 4 i386 +.Dd February 8, 2010 +.Dt ACPI_WMI 4 .Os .Sh NAME .Nm acpi_wmi diff --git a/share/man/man4/man4.i386/alpm.4 b/share/man/man4/alpm.4 index f288b72..43ee459 100644 --- a/share/man/man4/man4.i386/alpm.4 +++ b/share/man/man4/alpm.4 @@ -25,7 +25,7 @@ .\" $FreeBSD$ .\" .Dd February 13, 1999 -.Dt ALPM 4 i386 +.Dt ALPM 4 .Os .Sh NAME .Nm alpm diff --git a/share/man/man4/man4.i386/amdpm.4 b/share/man/man4/amdpm.4 index f8937b3..f791a28 100644 --- a/share/man/man4/man4.i386/amdpm.4 +++ b/share/man/man4/amdpm.4 @@ -26,7 +26,7 @@ .\" $FreeBSD$ .\" .Dd December 31, 2005 -.Dt AMDPM 4 i386 +.Dt AMDPM 4 .Os .Sh NAME .Nm amdpm diff --git a/share/man/man4/io.4 b/share/man/man4/io.4 index 15c0c4d..a54b9fd 100644 --- a/share/man/man4/io.4 +++ b/share/man/man4/io.4 @@ -27,8 +27,8 @@ .\" .\" $FreeBSD$ .\" -.Dd October 3, 2004 -.Dt IO 4 i386 +.Dd February 8, 2010 +.Dt IO 4 .Os .Sh NAME .Nm io diff --git a/share/man/man4/iwn.4 b/share/man/man4/iwn.4 index 1edb425..b73331a 100644 --- a/share/man/man4/iwn.4 +++ b/share/man/man4/iwn.4 @@ -25,12 +25,13 @@ .\" .\" $FreeBSD$ .\" -.Dd October 25, 2009 +.Dd February 8, 2010 .Os .Dt IWN 4 .Sh NAME .Nm iwn -.Nd "Intel Wireless WiFi Link 4965/5000 IEEE 802.11n driver" +.Nd Intel Wireless WiFi Link 4965/1000/5000/5150/5300/6000/6050 +IEEE 802.11n driver .Sh SYNOPSIS To compile this driver into the kernel, include the following lines in your @@ -46,8 +47,10 @@ You also need to select a firmware for your device. Choose one from: .Bd -ragged -offset indent .Cd "device iwn4965fw" +.Cd "device iwn1000fw" .Cd "device iwn5000fw" -.Cd "device iwn5100fw" +.Cd "device iwn5150fw" +.Cd "device iwn6000fw" .Ed .Pp Or you can use @@ -63,15 +66,18 @@ module at boot time, place the following lines in .Bd -literal -offset indent if_iwn_load="YES" iwn4965fw_load="YES" +iwn1000fw_load="YES" iwn5000fw_load="YES" -iwn5100fw_load="YES" +iwn5150fw_load="YES" +iwn6000fw_load="YES" .Ed .Sh DESCRIPTION The .Nm driver provides support for .Tn Intel -Wireless WiFi Link 4965 and 5000 series of PCI-Express network adapters. +Wireless WiFi Link 4965, 1000, 5000 and 6000 series of +PCI-Express network adapters. .Nm supports .Cm station , diff --git a/share/man/man4/iwnfw.4 b/share/man/man4/iwnfw.4 index 700d20a..b0450c6 100644 --- a/share/man/man4/iwnfw.4 +++ b/share/man/man4/iwnfw.4 @@ -22,7 +22,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 25, 2009 +.Dd February 7, 2010 .Dt IWNFW 4 .Os .Sh NAME @@ -41,8 +41,10 @@ If you want to pick only the firmware image for your network adapter choose one of the following: .Bd -ragged -offset indent .Cd "device iwn4965fw" +.Cd "device iwn1000fw" .Cd "device iwn5000fw" -.Cd "device iwn5100fw" +.Cd "device iwn5150fw" +.Cd "device iwn6000fw" .Ed .Pp Alternatively, to load the driver as a @@ -50,12 +52,15 @@ module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent iwn4965fw_load="YES" +iwn1000fw_load="YES" iwn5000fw_load="YES" -iwn5100fw_load="YES" +iwn5150fw_load="YES" +iwn6000fw_load="YES" .Ed .Sh DESCRIPTION This module provides access to firmware sets for the -Intel Wireless WiFi Link 4965 and 5000 series of IEEE 802.11n adapters. +Intel Wireless WiFi Link 4965, 1000, 5000 and 6000 series of +IEEE 802.11n adapters. It may be statically linked into the kernel, or loaded as a module. .Sh SEE ALSO diff --git a/share/man/man4/linux.4 b/share/man/man4/linux.4 index 8c368b3..f89b14bd 100644 --- a/share/man/man4/linux.4 +++ b/share/man/man4/linux.4 @@ -24,8 +24,8 @@ .\" .\" $FreeBSD$ .\" -.Dd March 17, 2008 -.Dt LINUX 4 i386 +.Dd February 8, 2010 +.Dt LINUX 4 .Os .Sh NAME .Nm linux diff --git a/share/man/man4/man4.i386/Makefile b/share/man/man4/man4.i386/Makefile index c474399..0134d57 100644 --- a/share/man/man4/man4.i386/Makefile +++ b/share/man/man4/man4.i386/Makefile @@ -1,8 +1,6 @@ # $FreeBSD$ MAN= aic.4 \ - alpm.4 \ - amdpm.4 \ apm.4 \ ce.4 \ cp.4 \ @@ -17,23 +15,18 @@ MAN= aic.4 \ glxsb.4 \ ie.4 \ longrun.4 \ - mcd.4 \ mse.4 \ npx.4 \ - padlock.4 \ pae.4 \ pbio.4 \ - pcf.4 \ perfmon.4 \ pnp.4 \ pnpbios.4 \ sbni.4 \ - scd.4 \ smapi.4 \ snc.4 \ streams.4 \ svr4.4 \ - viapm.4 \ vpd.4 \ vx.4 \ wl.4 diff --git a/share/man/man4/man4.i386/mcd.4 b/share/man/man4/mcd.4 index a4a93dd..8efa7db 100644 --- a/share/man/man4/man4.i386/mcd.4 +++ b/share/man/man4/mcd.4 @@ -27,7 +27,7 @@ .\" $FreeBSD$ .\" .Dd December 8, 1994 -.Dt MCD 4 i386 +.Dt MCD 4 .Os .Sh NAME .Nm mcd diff --git a/share/man/man4/ndis.4 b/share/man/man4/ndis.4 index 0abd0d5..71377ad 100644 --- a/share/man/man4/ndis.4 +++ b/share/man/man4/ndis.4 @@ -30,8 +30,8 @@ .\" .\" $FreeBSD$ .\" -.Dd October 13, 2006 -.Dt NDIS 4 i386 +.Dd February 8, 2010 +.Dt NDIS 4 .Os .Sh NAME .Nm ndis diff --git a/share/man/man4/nvram.4 b/share/man/man4/nvram.4 index a3a361e..25d6852 100644 --- a/share/man/man4/nvram.4 +++ b/share/man/man4/nvram.4 @@ -26,8 +26,8 @@ .\" .\" $FreeBSD$ .\" -.Dd January 27, 2010 -.Dt NVRAM 4 i386 +.Dd February 8, 2010 +.Dt NVRAM 4 .Os .Sh NAME .Nm nvram diff --git a/share/man/man4/man4.i386/padlock.4 b/share/man/man4/padlock.4 index e93d3d9..2a74f63 100644 --- a/share/man/man4/man4.i386/padlock.4 +++ b/share/man/man4/padlock.4 @@ -24,8 +24,8 @@ .\" .\" $FreeBSD$ .\" -.Dd August 1, 2007 -.Dt PADLOCK 4 i386 +.Dd February 8, 2010 +.Dt PADLOCK 4 .Os .Sh NAME .Nm padlock diff --git a/share/man/man4/man4.i386/pcf.4 b/share/man/man4/pcf.4 index f5e9088..cc8915f 100644 --- a/share/man/man4/man4.i386/pcf.4 +++ b/share/man/man4/pcf.4 @@ -25,7 +25,7 @@ .\" $FreeBSD$ .\" .Dd August 6, 1998 -.Dt PCF 4 i386 +.Dt PCF 4 .Os .Sh NAME .Nm pcf diff --git a/share/man/man4/man4.i386/scd.4 b/share/man/man4/scd.4 index daefa3c..dbbb18f 100644 --- a/share/man/man4/man4.i386/scd.4 +++ b/share/man/man4/scd.4 @@ -27,7 +27,7 @@ .\" $FreeBSD$ .\" .Dd March 17, 2008 -.Dt SCD 4 i386 +.Dt SCD 4 .Os .Sh NAME .Nm scd diff --git a/share/man/man4/sctp.4 b/share/man/man4/sctp.4 index 226fc3c..975192c 100644 --- a/share/man/man4/sctp.4 +++ b/share/man/man4/sctp.4 @@ -154,8 +154,8 @@ also supports the following extensions: This extension allows one to have message be skipped and not delivered based on some user specified parameters. .It "sctp dynamic addressing" - This extension allows addresses to be added and deleted -dynammically from an existing association. +This extension allows addresses to be added and deleted +dynamically from an existing association. .It "sctp authentication" This extension allows the user to authenticate specific peer chunks (including data) to validate that the peer @@ -164,7 +164,7 @@ association. A shared key option is also provided for so that two stacks can pre-share keys. .It "packet drop" - Some routers support a special satellite protocol that +Some routers support a special satellite protocol that will report losses due to corruption. This allows retransmissions without subsequent loss in bandwidth utilization. diff --git a/share/man/man4/man4.i386/viapm.4 b/share/man/man4/viapm.4 index 7457952..851c1fc 100644 --- a/share/man/man4/man4.i386/viapm.4 +++ b/share/man/man4/viapm.4 @@ -25,7 +25,7 @@ .\" $FreeBSD$ .\" .Dd April 20, 2002 -.Dt VIAPM 4 i386 +.Dt VIAPM 4 .Os .Sh NAME .Nm viapm diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5 index 8dc6708..f8d265b 100644 --- a/share/man/man5/rc.conf.5 +++ b/share/man/man5/rc.conf.5 @@ -507,6 +507,10 @@ specifies a filename. .Pq Vt str The IPv6 equivalent of .Va firewall_flags . +.It Va firewall_coscripts +.Pq Vt str +List of executables and/or rc scripts to run after firewall starts/stops. +Default is empty. .\" ----- firewall_nat_enable setting -------------------------------- .It Va firewall_nat_enable .Pq Vt bool diff --git a/share/man/man9/VFS.9 b/share/man/man9/VFS.9 index 54bdd0c..30f8918 100644 --- a/share/man/man9/VFS.9 +++ b/share/man/man9/VFS.9 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 4, 2010 +.Dd February 9, 2010 .Os .Dt VFS 9 .Sh NAME @@ -53,7 +53,6 @@ rather than implementing empty functions or casting to .Xr VFS_SYNC 9 , .Xr VFS_UNMOUNT 9 , .Xr VFS_VGET 9 , -.Xr VOP_VPTOFH 9 , .Xr vnode 9 .Sh AUTHORS This manual page was written by diff --git a/share/man/man9/alloc_unr.9 b/share/man/man9/alloc_unr.9 index ee805a3..2ba93e6 100644 --- a/share/man/man9/alloc_unr.9 +++ b/share/man/man9/alloc_unr.9 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 23, 2005 +.Dd February 7, 2010 .Dt ALLOC_UNR 9 .Os .Sh NAME @@ -81,7 +81,7 @@ is returned. Same as .Fn alloc_unr except that mutex is assumed to be already locked and thus is not used. -.It Fn free_unr uh +.It Fn free_unr uh item Free a previously allocated unit number. This function may require allocating memory, and thus it can sleep. There is no pre-locked variant. diff --git a/share/man/man9/locking.9 b/share/man/man9/locking.9 index 280140c..843ca6f 100644 --- a/share/man/man9/locking.9 +++ b/share/man/man9/locking.9 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 29, 2010 +.Dd February 10, 2010 .Dt LOCKING 9 .Os .Sh NAME @@ -152,7 +152,8 @@ Shared/exclusive locks are similar to reader/writer locks; the main difference between them is that shared/exclusive locks may be held during unbounded sleep (and may thus perform an unbounded sleep). They are inherently less efficient than mutexes, reader/writer locks -and read-mostly locks. They don't support priority propagation. +and read-mostly locks. +They don't support priority propagation. They should be considered to be closely related to .Xr sleep 9 . In fact it could in some cases be @@ -191,13 +192,16 @@ Giant is an instance of a mutex, with some special characteristics: .It It is recursive. .It -Drivers can request that Giant be locked around them, but this is -going away. -.It -You can sleep while it has recursed, but other recursive locks cannot. +Drivers and filesystems can request that Giant be locked around them +by not marking themselves MPSAFE. +Note that infrastructure to do this is slowly going away as non-MPSAFE +drivers either became properly locked or disappear. .It Giant must be locked first before other locks. .It +It is OK to hold Giant while performing unbounded sleep; in such case, +Giant will be dropped before sleeping and picked up after wakeup. +.It There are places in the kernel that drop Giant and pick it back up again. Sleep locks will do this before sleeping. @@ -315,6 +319,9 @@ If any caller above you has any mutex or rwlock, your sleep, will cause a panic. If the sleep only happens rarely it may be years before the bad code path is found. +.Pp +It is an error to do any operation that could result in unbounded sleep when +running inside an interrupt thread. .Ss Interaction table The following table shows what you can and can not do if you hold one of the synchronization primitives discussed here: diff --git a/share/man/man9/vnode.9 b/share/man/man9/vnode.9 index b55b19b..96aec73 100644 --- a/share/man/man9/vnode.9 +++ b/share/man/man9/vnode.9 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 20, 2003 +.Dd February 9, 2010 .Os .Dt VNODE 9 .Sh NAME @@ -161,6 +161,38 @@ interlock, will cause a LOR (Lock Order Reversal) due to the intertwining of VM Objects and Vnodes. .Sh SEE ALSO .Xr malloc 9 , +.Xr VOP_ACCESS 9 , +.Xr VOP_ACLCHECK 9 , +.Xr VOP_ADVLOCK 9 , +.Xr VOP_ATTRIB 9 , +.Xr VOP_BWRITE 9 , +.Xr VOP_CREATE 9 , +.Xr VOP_FSYNC 9 , +.Xr VOP_GETACL 9 , +.Xr VOP_GETEXTATTR 9 , +.Xr VOP_GETPAGES 9 , +.Xr VOP_GETVOBJECT 9 , +.Xr VOP_INACTIVE 9 , +.Xr VOP_IOCTL 9 , +.Xr VOP_LINK 9 , +.Xr VOP_LISTEXTATTR 9 , +.Xr VOP_LOCK 9 , +.Xr VOP_LOOKUP 9 , +.Xr VOP_OPENCLOSE 9 , +.Xr VOP_PATHCONF 9 , +.Xr VOP_PRINT 9 , +.Xr VOP_RDWR 9 , +.Xr VOP_READDIR 9 , +.Xr VOP_READLINK 9 , +.Xr VOP_REALLOCBLKS 9 , +.Xr VOP_REMOVE 9 , +.Xr VOP_RENAME 9 , +.Xr VOP_REVOKE 9 , +.Xr VOP_SETACL 9 , +.Xr VOP_SETEXTATTR 9 , +.Xr VOP_STRATEGY 9 , +.Xr VOP_VPTOCNP 9 , +.Xr VOP_VPTOFH 9 , .Xr VFS 9 .Sh AUTHORS This manual page was written by diff --git a/share/zoneinfo/Makefile b/share/zoneinfo/Makefile index 326f3b4..6bbc989 100644 --- a/share/zoneinfo/Makefile +++ b/share/zoneinfo/Makefile @@ -68,7 +68,7 @@ afterinstall: optC="-C ${DESTDIR}"; \ fi; \ echo "Updating /etc/localtime"; \ - tzsetup ${optC} -r; \ + tzsetup $${optC} -r; \ fi; \ else \ echo "Run tzsetup(8) manually to update /etc/localtime."; \ diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 1876c64..ae617be 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -72,6 +72,7 @@ options INCLUDE_CONFIG_FILE # Include this file in kernel options KDB # Enable kernel debugger support. options DDB # Support DDB. options GDB # Support remote GDB. +options DEADLKRES # Enable the deadlock resolver options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES index c1e7327..8b56e54 100644 --- a/sys/amd64/conf/NOTES +++ b/sys/amd64/conf/NOTES @@ -274,23 +274,69 @@ options DRM_DEBUG # Include debug printfs (slow) # HP PC Lan+, various PC Card devices # (requires miibus) # ipw: Intel PRO/Wireless 2100 IEEE 802.11 adapter +# Requires the ipw firmware module # iwi: Intel PRO/Wireless 2200BG/2225BG/2915ABG IEEE 802.11 adapters -# iwn: Intel Wireless WiFi Link 4965AGN 802.11 network adapters +# Requires the iwi firmware module +# iwn: Intel Wireless WiFi Link 4965/1000/5000/6000 802.11 network adapters +# Requires the iwn firmware module +# mwl: Marvell 88W8363 IEEE 802.11 adapter +# Requires the mwl firmware module # nfe: nVidia nForce MCP on-board Ethernet Networking (BSD open source) # nve: nVidia nForce MCP on-board Ethernet Networking # wpi: Intel 3945ABG Wireless LAN controller +# Requires the wpi firmware module device ed options ED_3C503 options ED_HPP options ED_SIC +device ipw device iwi device iwn -device ipw +device mwl device nfe device nve device wpi +# IEEE 802.11 adapter firmware modules + +# Intel PRO/Wireless 2100 firmware: +# ipwfw: BSS/IBSS/monitor mode firmware +# ipwbssfw: BSS mode firmware +# ipwibssfw: IBSS mode firmware +# ipwmonitorfw: Monitor mode firmware +# Intel PRO/Wireless 2200BG/2225BG/2915ABG firmware: +# iwifw: BSS/IBSS/monitor mode firmware +# iwibssfw: BSS mode firmware +# iwiibssfw: IBSS mode firmware +# iwimonitorfw: Monitor mode firmware +# Intel Wireless WiFi Link 4965/1000/5000/6000 series firmware: +# iwnfw: Single module to support the 4965/1000/5000/5150/6000 +# iwn4965fw: Specific module for the 4965 only +# iwn1000fw: Specific module for the 1000 only +# iwn5000fw: Specific module for the 5000 only +# iwn5150fw: Specific module for the 5150 only +# iwn6000fw: Specific module for the 6000 only +# mwlfw: Marvell 88W8363 firmware +# wpifw: Intel 3945ABG Wireless LAN Controller firmware + +device iwifw +device iwibssfw +device iwiibssfw +device iwimonitorfw +device ipwfw +device ipwbssfw +device ipwibssfw +device ipwmonitorfw +device iwnfw +device iwn4965fw +device iwn1000fw +device iwn5000fw +device iwn5150fw +device iwn6000fw +device mwlfw +device wpifw + # #XXX this stores pointers in a 32bit field that is defined by the hardware #device pst diff --git a/sys/amd64/conf/XENHVM b/sys/amd64/conf/XENHVM index 4c1981c..717f7b3 100644 --- a/sys/amd64/conf/XENHVM +++ b/sys/amd64/conf/XENHVM @@ -70,6 +70,7 @@ options NO_ADAPTIVE_RWLOCKS options KDB # Enable kernel debugger support. options DDB # Support DDB. options GDB # Support remote GDB. +options DEADLKRES # Enable the deadlock resolver options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles diff --git a/sys/arm/arm/pmap.c b/sys/arm/arm/pmap.c index 3b70a41..d3e89000 100644 --- a/sys/arm/arm/pmap.c +++ b/sys/arm/arm/pmap.c @@ -1157,79 +1157,38 @@ pmap_tlb_flushD(pmap_t pm) cpu_tlb_flushD(); } -static PMAP_INLINE void -pmap_l2cache_wbinv_range(pmap_t pm, vm_offset_t va, vm_size_t len) +static int +pmap_has_valid_mapping(pmap_t pm, vm_offset_t va) { - vm_size_t rest; pd_entry_t *pde; pt_entry_t *ptep; - rest = MIN(PAGE_SIZE - (va & PAGE_MASK), len); - - while (len > 0) { - CTR4(KTR_PMAP, "pmap_l2cache_wbinv_range: pmap %p is_kernel %d " - "va 0x%08x len 0x%x ", pm, pm == pmap_kernel(), va, rest); - if (pmap_get_pde_pte(pm, va, &pde, &ptep) && l2pte_valid(*ptep)) - cpu_l2cache_wbinv_range(va, rest); - - len -= rest; - va += rest; + if (pmap_get_pde_pte(pm, va, &pde, &ptep) && + ptep && ((*ptep & L2_TYPE_MASK) != L2_TYPE_INV)) + return (1); - rest = MIN(PAGE_SIZE, len); - } + return (0); } static PMAP_INLINE void pmap_idcache_wbinv_range(pmap_t pm, vm_offset_t va, vm_size_t len) { - - if (pmap_is_current(pm)) { - cpu_idcache_wbinv_range(va, len); - pmap_l2cache_wbinv_range(pm, va, len); - } -} - -static PMAP_INLINE void -pmap_l2cache_wb_range(pmap_t pm, vm_offset_t va, vm_size_t len) -{ vm_size_t rest; - pd_entry_t *pde; - pt_entry_t *ptep; - - rest = MIN(PAGE_SIZE - (va & PAGE_MASK), len); - - while (len > 0) { - CTR4(KTR_PMAP, "pmap_l2cache_wb_range: pmap %p is_kernel %d " - "va 0x%08x len 0x%x ", pm, pm == pmap_kernel(), va, rest); - if (pmap_get_pde_pte(pm, va, &pde, &ptep) && l2pte_valid(*ptep)) - cpu_l2cache_wb_range(va, rest); - len -= rest; - va += rest; + CTR4(KTR_PMAP, "pmap_dcache_wbinv_range: pmap %p is_kernel %d va 0x%08x" + " len 0x%x ", pm, pm == pmap_kernel(), va, len); - rest = MIN(PAGE_SIZE, len); - } -} - -static PMAP_INLINE void -pmap_l2cache_inv_range(pmap_t pm, vm_offset_t va, vm_size_t len) -{ - vm_size_t rest; - pd_entry_t *pde; - pt_entry_t *ptep; - - rest = MIN(PAGE_SIZE - (va & PAGE_MASK), len); - - while (len > 0) { - CTR4(KTR_PMAP, "pmap_l2cache_wb_range: pmap %p is_kernel %d " - "va 0x%08x len 0x%x ", pm, pm == pmap_kernel(), va, rest); - if (pmap_get_pde_pte(pm, va, &pde, &ptep) && l2pte_valid(*ptep)) - cpu_l2cache_inv_range(va, rest); - - len -= rest; - va += rest; - - rest = MIN(PAGE_SIZE, len); + if (pmap_is_current(pm) || pm == pmap_kernel()) { + rest = MIN(PAGE_SIZE - (va & PAGE_MASK), len); + while (len > 0) { + if (pmap_has_valid_mapping(pm, va)) { + cpu_idcache_wbinv_range(va, rest); + cpu_l2cache_wbinv_range(va, rest); + } + len -= rest; + va += rest; + rest = MIN(PAGE_SIZE, len); + } } } @@ -1237,24 +1196,31 @@ static PMAP_INLINE void pmap_dcache_wb_range(pmap_t pm, vm_offset_t va, vm_size_t len, boolean_t do_inv, boolean_t rd_only) { + vm_size_t rest; CTR4(KTR_PMAP, "pmap_dcache_wb_range: pmap %p is_kernel %d va 0x%08x " "len 0x%x ", pm, pm == pmap_kernel(), va, len); CTR2(KTR_PMAP, " do_inv %d rd_only %d", do_inv, rd_only); if (pmap_is_current(pm)) { - if (do_inv) { - if (rd_only) { - cpu_dcache_inv_range(va, len); - pmap_l2cache_inv_range(pm, va, len); - } - else { - cpu_dcache_wbinv_range(va, len); - pmap_l2cache_wbinv_range(pm, va, len); + rest = MIN(PAGE_SIZE - (va & PAGE_MASK), len); + while (len > 0) { + if (pmap_has_valid_mapping(pm, va)) { + if (do_inv && rd_only) { + cpu_dcache_inv_range(va, rest); + cpu_l2cache_inv_range(va, rest); + } else if (do_inv) { + cpu_dcache_wbinv_range(va, rest); + cpu_l2cache_wbinv_range(va, rest); + } else if (!rd_only) { + cpu_dcache_wb_range(va, rest); + cpu_l2cache_wb_range(va, rest); + } } - } else if (!rd_only) { - cpu_dcache_wb_range(va, len); - pmap_l2cache_wb_range(pm, va, len); + len -= rest; + va += rest; + + rest = MIN(PAGE_SIZE, len); } } } @@ -3185,7 +3151,8 @@ pmap_remove_all(vm_page_t m) */ if (pmap_is_current(pv->pv_pmap)) { cpu_dcache_inv_range(pv->pv_va, PAGE_SIZE); - cpu_l2cache_inv_range(pv->pv_va, PAGE_SIZE); + if (pmap_has_valid_mapping(pv->pv_pmap, pv->pv_va)) + cpu_l2cache_inv_range(pv->pv_va, PAGE_SIZE); } if (pv->pv_flags & PVF_UNMAN) { @@ -3472,9 +3439,10 @@ do_l2b_alloc: if (pmap_is_current(pmap) && (oflags & PVF_NC) == 0 && (opte & L2_S_PROT_W) != 0 && - (prot & VM_PROT_WRITE) == 0) { + (prot & VM_PROT_WRITE) == 0 && + (opte & L2_TYPE_MASK) != L2_TYPE_INV) { cpu_dcache_wb_range(va, PAGE_SIZE); - pmap_l2cache_wb_range(pmap, va, PAGE_SIZE); + cpu_l2cache_wb_range(va, PAGE_SIZE); } } else { /* diff --git a/sys/arm/conf/AVILA b/sys/arm/conf/AVILA index b665659..f9014ed 100644 --- a/sys/arm/conf/AVILA +++ b/sys/arm/conf/AVILA @@ -39,6 +39,7 @@ options DEVICE_POLLING options KDB #options GDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/BWCT b/sys/arm/conf/BWCT index b92e626..f25a3be 100644 --- a/sys/arm/conf/BWCT +++ b/sys/arm/conf/BWCT @@ -79,6 +79,7 @@ device mii device rlswitch # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/CAMBRIA b/sys/arm/conf/CAMBRIA index 4de9b2c..7bd7d49 100644 --- a/sys/arm/conf/CAMBRIA +++ b/sys/arm/conf/CAMBRIA @@ -39,6 +39,7 @@ options DEVICE_POLLING options KDB #options GDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/CNS11XXNAS b/sys/arm/conf/CNS11XXNAS index 29791a3..7aa4fbb 100644 --- a/sys/arm/conf/CNS11XXNAS +++ b/sys/arm/conf/CNS11XXNAS @@ -39,6 +39,7 @@ options DEVICE_POLLING options KDB #options GDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/CRB b/sys/arm/conf/CRB index f865473..3598b44 100644 --- a/sys/arm/conf/CRB +++ b/sys/arm/conf/CRB @@ -93,6 +93,7 @@ device pty # Debugging for use in -current options KDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/DB-78XXX b/sys/arm/conf/DB-78XXX index afa6ec9..939ae2b 100644 --- a/sys/arm/conf/DB-78XXX +++ b/sys/arm/conf/DB-78XXX @@ -39,6 +39,7 @@ options NO_SWAPPING # Debugging options ALT_BREAK_TO_DEBUGGER options DDB +#options DEADLKRES #Enable the deadlock resolver options DIAGNOSTIC #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS diff --git a/sys/arm/conf/DB-88F5XXX b/sys/arm/conf/DB-88F5XXX index 1297229..cfbdbb4 100644 --- a/sys/arm/conf/DB-88F5XXX +++ b/sys/arm/conf/DB-88F5XXX @@ -39,6 +39,7 @@ options NO_SWAPPING # Debugging options ALT_BREAK_TO_DEBUGGER options DDB +#options DEADLKRES #Enable the deadlock resolver options DIAGNOSTIC #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS diff --git a/sys/arm/conf/DB-88F6XXX b/sys/arm/conf/DB-88F6XXX index cbf7abd..16ff7d3 100644 --- a/sys/arm/conf/DB-88F6XXX +++ b/sys/arm/conf/DB-88F6XXX @@ -39,6 +39,7 @@ options NO_SWAPPING # Debugging options ALT_BREAK_TO_DEBUGGER options DDB +#options DEADLKRES #Enable the deadlock resolver options DIAGNOSTIC #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS diff --git a/sys/arm/conf/EP80219 b/sys/arm/conf/EP80219 index 4efc432..4b36590 100644 --- a/sys/arm/conf/EP80219 +++ b/sys/arm/conf/EP80219 @@ -89,6 +89,7 @@ device dma # I80321 DMA Controller # Debugging for use in -current options KDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/GUMSTIX b/sys/arm/conf/GUMSTIX index a122a5e..4ed8df9 100644 --- a/sys/arm/conf/GUMSTIX +++ b/sys/arm/conf/GUMSTIX @@ -80,6 +80,7 @@ device pty # Debugging for use in -current options KDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/HL200 b/sys/arm/conf/HL200 index 8d0f4c1..c4ec835 100644 --- a/sys/arm/conf/HL200 +++ b/sys/arm/conf/HL200 @@ -74,6 +74,7 @@ device mii device lxtphy # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/IQ31244 b/sys/arm/conf/IQ31244 index 4621756..2cb7a28 100644 --- a/sys/arm/conf/IQ31244 +++ b/sys/arm/conf/IQ31244 @@ -94,6 +94,7 @@ device "iq31244_7seg" # IQ31244 7 seg # Debugging for use in -current options KDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/KB920X b/sys/arm/conf/KB920X index a6d6ef2..adff9fc 100644 --- a/sys/arm/conf/KB920X +++ b/sys/arm/conf/KB920X @@ -75,6 +75,7 @@ device mii device lxtphy # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/NSLU b/sys/arm/conf/NSLU index f14a779..4cd0315 100644 --- a/sys/arm/conf/NSLU +++ b/sys/arm/conf/NSLU @@ -44,6 +44,7 @@ options DEVICE_POLLING options KDB #options GDB options DDB #Enable the kernel debugger +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/SIMICS b/sys/arm/conf/SIMICS index d4f6c14..229c60f 100644 --- a/sys/arm/conf/SIMICS +++ b/sys/arm/conf/SIMICS @@ -61,6 +61,7 @@ device rl device uart # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/conf/SKYEYE b/sys/arm/conf/SKYEYE index 10aedd5..51ad63a 100644 --- a/sys/arm/conf/SKYEYE +++ b/sys/arm/conf/SKYEYE @@ -68,6 +68,7 @@ device uart # output. Adds ~215k to driver. # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/arm/xscale/ixp425/cambria_fled.c b/sys/arm/xscale/ixp425/cambria_fled.c index ede1577..7faa310 100644 --- a/sys/arm/xscale/ixp425/cambria_fled.c +++ b/sys/arm/xscale/ixp425/cambria_fled.c @@ -74,6 +74,8 @@ fled_attach(device_t dev) sc->sc_led = led_create(fled_cb, dev, "front"); + led_func(sc, 1); /* Turn on LED */ + return 0; } diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master index 485f7a6..3abd6e7 100644 --- a/sys/compat/freebsd32/syscalls.master +++ b/sys/compat/freebsd32/syscalls.master @@ -13,7 +13,7 @@ ; case where the event exists, but we don't want auditing, the ; event should be #defined to AUE_NULL in audit_kevents.h. ; type one of STD, OBSOL, UNIMPL, COMPAT, COMPAT4, COMPAT6, -; COMPAT7, LIBCOMPAT, NODEF, NOARGS, NOPROTO, NOSTD +; COMPAT7, NODEF, NOARGS, NOPROTO, NOSTD ; The COMPAT* options may be combined with one or more NO* ; options separated by '|' with no spaces (e.g. COMPAT|NOARGS) ; name psuedo-prototype of syscall routine @@ -29,7 +29,6 @@ ; COMPAT4 included on COMPAT4 #ifdef (FreeBSD 4 compat) ; COMPAT6 included on COMPAT6 #ifdef (FreeBSD 6 compat) ; COMPAT7 included on COMPAT7 #ifdef (FreeBSD 7 compat) -; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h ; OBSOL obsolete, not included in system, only specifies name ; UNIMPL not implemented, placeholder only ; NOSTD implemented but as a lkm that can be statically diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index 396a71e..d94d926 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -128,7 +128,7 @@ do_sa_get(struct sockaddr **sap, const struct osockaddr *osa, int *osalen, bdom = linux_to_bsd_domain(kosa->sa_family); if (bdom == -1) { - error = EINVAL; + error = EAFNOSUPPORT; goto out; } @@ -157,8 +157,13 @@ do_sa_get(struct sockaddr **sap, const struct osockaddr *osa, int *osalen, } } else #endif - if (bdom == AF_INET) + if (bdom == AF_INET) { alloclen = sizeof(struct sockaddr_in); + if (*osalen < alloclen) { + error = EINVAL; + goto out; + } + } sa = (struct sockaddr *) kosa; sa->sa_family = bdom; diff --git a/sys/compat/svr4/syscalls.master b/sys/compat/svr4/syscalls.master index 0a88a45..2d6afee 100644 --- a/sys/compat/svr4/syscalls.master +++ b/sys/compat/svr4/syscalls.master @@ -22,7 +22,6 @@ ; types: ; STD always included ; COMPAT included on COMPAT #ifdef -; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h ; OBSOL obsolete, not included in system, only specifies name ; UNIMPL not implemented, placeholder only diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 8dc74f2..4d7c596f 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -386,6 +386,11 @@ options KDTRACE_HOOKS options SYSCTL_DEBUG # +# NO_SYSCTL_DESCR omits the sysctl node descriptions to save space in the +# resulting kernel. +options NO_SYSCTL_DESCR + +# # DEBUG_MEMGUARD builds and enables memguard(9), a replacement allocator # for the kernel used to detect modify-after-free scenarios. See the # memguard(9) man page for more information on usage. @@ -2596,8 +2601,10 @@ device uhid device ukbd # USB printer device ulpt -# USB Iomega Zip 100 Drive (Requires scbus and da) +# USB mass storage driver (Requires scbus and da) device umass +# USB mass storage driver for device-side mode +device usfs # USB support for Belkin F5U109 and Magic Control Technology serial adapters device umct # USB modem support diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index a04d49e..1734358 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -208,7 +208,7 @@ dev/hwpmc/hwpmc_core.c optional hwpmc dev/hwpmc/hwpmc_piv.c optional hwpmc dev/hwpmc/hwpmc_tsc.c optional hwpmc dev/hwpmc/hwpmc_x86.c optional hwpmc -dev/kbd/kbd.c optional atkbd | sc | ukbd | usb2_input_kbd +dev/kbd/kbd.c optional atkbd | sc | ukbd dev/lindev/full.c optional lindev dev/lindev/lindev.c optional lindev dev/mem/memutil.c optional mem diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 5095f4e..6c440ea 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -200,7 +200,7 @@ dev/ipmi/ipmi_smbios.c optional ipmi dev/ipmi/ipmi_ssif.c optional ipmi smbus dev/ipmi/ipmi_pci.c optional ipmi pci dev/ipmi/ipmi_linux.c optional ipmi compat_linux -dev/kbd/kbd.c optional atkbd | sc | ukbd | usb2_input_kbd +dev/kbd/kbd.c optional atkbd | sc | ukbd dev/le/if_le_isa.c optional le isa dev/lindev/full.c optional lindev dev/lindev/lindev.c optional lindev diff --git a/sys/conf/files.ia64 b/sys/conf/files.ia64 index 4c0c9dd..86ca963 100644 --- a/sys/conf/files.ia64 +++ b/sys/conf/files.ia64 @@ -55,7 +55,7 @@ dev/fb/fb.c optional fb | vga dev/fb/vga.c optional vga dev/hwpmc/hwpmc_ia64.c optional hwpmc dev/io/iodev.c optional io -dev/kbd/kbd.c optional atkbd | sc | ukbd | usb2_input_kbd +dev/kbd/kbd.c optional atkbd | sc | ukbd dev/syscons/scterm-teken.c optional sc dev/syscons/scvgarndr.c optional sc vga dev/syscons/scvtb.c optional sc diff --git a/sys/conf/files.mips b/sys/conf/files.mips index 89da2a7..6b2ece4 100644 --- a/sys/conf/files.mips +++ b/sys/conf/files.mips @@ -21,6 +21,7 @@ # ---------------------------------------------------------------------- mips/mips/machdep.c standard mips/mips/mp_machdep.c optional smp +mips/mips/mpboot.S optional smp mips/mips/psraccess.S standard # ---------------------------------------------------------------------- # Phase 3 diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index fee2363..b4e171d 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -105,7 +105,7 @@ dev/hwpmc/hwpmc_ppro.c optional hwpmc dev/hwpmc/hwpmc_tsc.c optional hwpmc dev/hwpmc/hwpmc_x86.c optional hwpmc dev/io/iodev.c optional io -dev/kbd/kbd.c optional pckbd | sc | ukbd | usb2_input_kbd +dev/kbd/kbd.c optional pckbd | sc | ukbd dev/le/if_le_cbus.c optional le isa dev/lindev/full.c optional lindev dev/lindev/lindev.c optional lindev diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64 index 8b771ae..e9766fa 100644 --- a/sys/conf/files.sparc64 +++ b/sys/conf/files.sparc64 @@ -38,7 +38,7 @@ dev/fb/fb.c optional sc dev/fb/gallant12x22.c optional sc dev/fb/machfb.c optional machfb sc dev/hwpmc/hwpmc_sparc64.c optional hwpmc -dev/kbd/kbd.c optional atkbd | sc | ukbd | usb2_input_kbd +dev/kbd/kbd.c optional atkbd | sc | ukbd dev/le/if_le_lebuffer.c optional le sbus dev/le/if_le_ledma.c optional le sbus dev/le/lebuffer_sbus.c optional le sbus diff --git a/sys/conf/options.mips b/sys/conf/options.mips index 4091759..d3b8767 100644 --- a/sys/conf/options.mips +++ b/sys/conf/options.mips @@ -61,14 +61,6 @@ TICK_USE_YAMON_FREQ opt_global.h TICK_USE_MALTA_RTC opt_global.h # -# The MIPS architecture does not have separate memory and i/o address space -# like x86. However some MIPS processors provide a memory-mapped window that -# maps onto the PCI I/O space. -# -PCI_IOSPACE_SIZE opt_global.h -PCI_IOSPACE_ADDR opt_global.h - -# # The highest memory address that can be used by the kernel in units of KB. # MAXMEM opt_global.h diff --git a/sys/ddb/db_thread.c b/sys/ddb/db_thread.c index e127eea..bf59f68 100644 --- a/sys/ddb/db_thread.c +++ b/sys/ddb/db_thread.c @@ -48,7 +48,7 @@ db_print_thread(void) pid = -1; if (kdb_thread->td_proc != NULL) pid = kdb_thread->td_proc->p_pid; - db_printf("[thread pid %d tid %ld ]\n", pid, (long)kdb_thread->td_tid); + db_printf("[ thread pid %d tid %ld ]\n", pid, (long)kdb_thread->td_tid); } void diff --git a/sys/dev/aac/aac.c b/sys/dev/aac/aac.c index f4c57e6..5839895 100644 --- a/sys/dev/aac/aac.c +++ b/sys/dev/aac/aac.c @@ -107,28 +107,6 @@ static int aac_dequeue_fib(struct aac_softc *sc, int queue, static int aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib); -/* Falcon/PPC interface */ -static int aac_fa_get_fwstatus(struct aac_softc *sc); -static void aac_fa_qnotify(struct aac_softc *sc, int qbit); -static int aac_fa_get_istatus(struct aac_softc *sc); -static void aac_fa_clear_istatus(struct aac_softc *sc, int mask); -static void aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command, - u_int32_t arg0, u_int32_t arg1, - u_int32_t arg2, u_int32_t arg3); -static int aac_fa_get_mailbox(struct aac_softc *sc, int mb); -static void aac_fa_set_interrupts(struct aac_softc *sc, int enable); - -struct aac_interface aac_fa_interface = { - aac_fa_get_fwstatus, - aac_fa_qnotify, - aac_fa_get_istatus, - aac_fa_clear_istatus, - aac_fa_set_mailbox, - aac_fa_get_mailbox, - aac_fa_set_interrupts, - NULL, NULL, NULL -}; - /* StrongARM interface */ static int aac_sa_get_fwstatus(struct aac_softc *sc); static void aac_sa_qnotify(struct aac_softc *sc, int qbit); @@ -2417,17 +2395,6 @@ aac_rx_get_fwstatus(struct aac_softc *sc) } static int -aac_fa_get_fwstatus(struct aac_softc *sc) -{ - int val; - - fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); - - val = AAC_MEM0_GETREG4(sc, AAC_FA_FWSTATUS); - return (val); -} - -static int aac_rkt_get_fwstatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); @@ -2457,15 +2424,6 @@ aac_rx_qnotify(struct aac_softc *sc, int qbit) } static void -aac_fa_qnotify(struct aac_softc *sc, int qbit) -{ - fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); - - AAC_MEM0_SETREG2(sc, AAC_FA_DOORBELL1, qbit); - AAC_FA_HACK(sc); -} - -static void aac_rkt_qnotify(struct aac_softc *sc, int qbit) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); @@ -2493,17 +2451,6 @@ aac_rx_get_istatus(struct aac_softc *sc) } static int -aac_fa_get_istatus(struct aac_softc *sc) -{ - int val; - - fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); - - val = AAC_MEM0_GETREG2(sc, AAC_FA_DOORBELL0); - return (val); -} - -static int aac_rkt_get_istatus(struct aac_softc *sc) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); @@ -2531,15 +2478,6 @@ aac_rx_clear_istatus(struct aac_softc *sc, int mask) } static void -aac_fa_clear_istatus(struct aac_softc *sc, int mask) -{ - fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); - - AAC_MEM0_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask); - AAC_FA_HACK(sc); -} - -static void aac_rkt_clear_istatus(struct aac_softc *sc, int mask) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); @@ -2577,24 +2515,6 @@ aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command, } static void -aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command, - u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) -{ - fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); - - AAC_MEM1_SETREG4(sc, AAC_FA_MAILBOX, command); - AAC_FA_HACK(sc); - AAC_MEM1_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0); - AAC_FA_HACK(sc); - AAC_MEM1_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1); - AAC_FA_HACK(sc); - AAC_MEM1_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2); - AAC_FA_HACK(sc); - AAC_MEM1_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3); - AAC_FA_HACK(sc); -} - -static void aac_rkt_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) { @@ -2627,17 +2547,6 @@ aac_rx_get_mailbox(struct aac_softc *sc, int mb) } static int -aac_fa_get_mailbox(struct aac_softc *sc, int mb) -{ - int val; - - fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); - - val = AAC_MEM1_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4)); - return (val); -} - -static int aac_rkt_get_mailbox(struct aac_softc *sc, int mb) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, ""); @@ -2676,20 +2585,6 @@ aac_rx_set_interrupts(struct aac_softc *sc, int enable) } static void -aac_fa_set_interrupts(struct aac_softc *sc, int enable) -{ - fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis"); - - if (enable) { - AAC_MEM0_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS); - AAC_FA_HACK(sc); - } else { - AAC_MEM0_SETREG2((sc), AAC_FA_MASK0, ~0); - AAC_FA_HACK(sc); - } -} - -static void aac_rkt_set_interrupts(struct aac_softc *sc, int enable) { fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "%sable interrupts", enable ? "en" : "dis"); diff --git a/sys/dev/aac/aac_pci.c b/sys/dev/aac/aac_pci.c index 17731a8..bc99379 100644 --- a/sys/dev/aac/aac_pci.c +++ b/sys/dev/aac/aac_pci.c @@ -435,10 +435,6 @@ aac_pci_attach(device_t dev) fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "set hardware up for StrongARM"); sc->aac_if = aac_sa_interface; break; - case AAC_HWIF_FALCON: - fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "set hardware up for Falcon/PPC"); - sc->aac_if = aac_fa_interface; - break; case AAC_HWIF_RKT: fwprintf(sc, HBA_FLAGS_DBG_INIT_B, "set hardware up for Rocket/MIPS"); sc->aac_if = aac_rkt_interface; diff --git a/sys/dev/aac/aacreg.h b/sys/dev/aac/aacreg.h index d45634a..3235881 100644 --- a/sys/dev/aac/aacreg.h +++ b/sys/dev/aac/aacreg.h @@ -1453,24 +1453,6 @@ enum { }; /* - * Register set for adapters based on the Falcon bridge and PPC core - */ - -#define AAC_FA_DOORBELL0_CLEAR 0x00 -#define AAC_FA_DOORBELL1_CLEAR 0x02 -#define AAC_FA_DOORBELL0 0x04 -#define AAC_FA_DOORBELL1 0x06 -#define AAC_FA_MASK0_CLEAR 0x08 -#define AAC_FA_MASK1_CLEAR 0x0a -#define AAC_FA_MASK0 0x0c -#define AAC_FA_MASK1 0x0e -#define AAC_FA_MAILBOX 0x10 -#define AAC_FA_FWSTATUS 0x2c /* Mailbox 7 */ -#define AAC_FA_INTSRC 0x900 - -#define AAC_FA_HACK(sc) (void)AAC_MEM0_GETREG4(sc, AAC_FA_INTSRC) - -/* * Register definitions for the Adaptec AAC-364 'Jalapeno I/II' adapters, based * on the SA110 'StrongArm'. */ diff --git a/sys/dev/aac/aacvar.h b/sys/dev/aac/aacvar.h index 3f9714d..23ec83c 100644 --- a/sys/dev/aac/aacvar.h +++ b/sys/dev/aac/aacvar.h @@ -319,7 +319,6 @@ struct aac_softc int aac_hwif; #define AAC_HWIF_I960RX 0 #define AAC_HWIF_STRONGARM 1 -#define AAC_HWIF_FALCON 2 #define AAC_HWIF_RKT 3 #define AAC_HWIF_NARK 4 #define AAC_HWIF_UNKNOWN -1 diff --git a/sys/dev/acpi_support/acpi_asus.c b/sys/dev/acpi_support/acpi_asus.c index 51ca6fa..f9b9389 100644 --- a/sys/dev/acpi_support/acpi_asus.c +++ b/sys/dev/acpi_support/acpi_asus.c @@ -270,8 +270,8 @@ static struct acpi_asus_model acpi_asus_models[] = { .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", - .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN", - .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", + .lcd_get = "GBTL", + .lcd_set = "SBTL", .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD", .disp_set = "SDSP", }, @@ -1134,26 +1134,7 @@ acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) return (FALSE); case ACPI_ASUS_METHOD_LCD: if (sc->model->lcd_get) { - if (strncmp(sc->model->name, "G2K", 3) == 0) { - ACPI_BUFFER Buf; - ACPI_OBJECT Arg, Obj; - ACPI_OBJECT_LIST Args; - - Arg.Type = ACPI_TYPE_INTEGER; - Arg.Integer.Value = 0x11; - Args.Count = 1; - Args.Pointer = &Arg; - Buf.Length = sizeof(Obj); - Buf.Pointer = &Obj; - - status = AcpiEvaluateObject(sc->handle, - sc->model->lcd_get, &Args, &Buf); - if (ACPI_SUCCESS(status) && - Obj.Type == ACPI_TYPE_INTEGER) { - sc->s_lcd = Obj.Integer.Value; - return (TRUE); - } - } else if (strncmp(sc->model->name, "L3H", 3) == 0) { + if (strncmp(sc->model->name, "L3H", 3) == 0) { ACPI_BUFFER Buf; ACPI_OBJECT Arg[2], Obj; ACPI_OBJECT_LIST Args; diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c index a446ea8..69f0739 100644 --- a/sys/dev/acpica/acpi.c +++ b/sys/dev/acpica/acpi.c @@ -1653,14 +1653,7 @@ acpi_probe_children(device_t bus) bus_generic_probe(bus); /* Probe/attach all children, created staticly and from the namespace. */ - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "first bus_generic_attach\n")); - bus_generic_attach(bus); - - /* - * Some of these children may have attached others as part of their attach - * process (eg. the root PCI bus driver), so rescan. - */ - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "second bus_generic_attach\n")); + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "acpi bus_generic_attach\n")); bus_generic_attach(bus); /* Attach wake sysctls. */ @@ -1685,14 +1678,14 @@ acpi_probe_order(ACPI_HANDLE handle, int *order) * 100000. CPUs */ AcpiGetType(handle, &type); - if (acpi_MatchHid(handle, "PNP0C01") || acpi_MatchHid(handle, "PNP0C02")) + if (type == ACPI_TYPE_PROCESSOR) *order = 1; - else if (acpi_MatchHid(handle, "PNP0C09")) + else if (acpi_MatchHid(handle, "PNP0C01") || acpi_MatchHid(handle, "PNP0C02")) *order = 2; - else if (acpi_MatchHid(handle, "PNP0C0F")) + else if (acpi_MatchHid(handle, "PNP0C09")) *order = 3; - else if (type == ACPI_TYPE_PROCESSOR) - *order = 100000; + else if (acpi_MatchHid(handle, "PNP0C0F")) + *order = 4; } /* diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c index bbbd239..06c2954 100644 --- a/sys/dev/acpica/acpi_cpu.c +++ b/sys/dev/acpica/acpi_cpu.c @@ -384,13 +384,31 @@ acpi_cpu_attach(device_t dev) /* Probe for Cx state support. */ acpi_cpu_cx_probe(sc); - /* Finally, call identify and probe/attach for child devices. */ - bus_generic_probe(dev); - bus_generic_attach(dev); - return (0); } +static void +acpi_cpu_postattach(void *unused __unused) +{ + device_t *devices; + int err; + int i, n; + + err = devclass_get_devices(acpi_cpu_devclass, &devices, &n); + if (err != 0) { + printf("devclass_get_devices(acpi_cpu_devclass) failed\n"); + return; + } + for (i = 0; i < n; i++) + bus_generic_probe(devices[i]); + for (i = 0; i < n; i++) + bus_generic_attach(devices[i]); + free(devices, M_TEMP); +} + +SYSINIT(acpi_cpu, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, + acpi_cpu_postattach, NULL); + /* * Disable any entry to the idle function during suspend and re-enable it * during resume. diff --git a/sys/dev/acpica/acpi_video.c b/sys/dev/acpica/acpi_video.c index 2bcf97f..ada6b1e 100644 --- a/sys/dev/acpica/acpi_video.c +++ b/sys/dev/acpica/acpi_video.c @@ -83,6 +83,7 @@ static struct acpi_video_output *acpi_video_vo_init(UINT32); static void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); static void acpi_video_vo_destroy(struct acpi_video_output *); static int acpi_video_vo_check_level(struct acpi_video_output *, int); +static void acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *); static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); @@ -93,6 +94,7 @@ static void vid_set_switch_policy(ACPI_HANDLE, UINT32); static int vid_enum_outputs(ACPI_HANDLE, void(*)(ACPI_HANDLE, UINT32, void *), void *); static int vo_get_brightness_levels(ACPI_HANDLE, int **); +static int vo_get_brightness(ACPI_HANDLE); static void vo_set_brightness(ACPI_HANDLE, int); static UINT32 vo_get_device_status(ACPI_HANDLE); static UINT32 vo_get_graphics_state(ACPI_HANDLE); @@ -101,6 +103,8 @@ static void vo_set_device_state(ACPI_HANDLE, UINT32); /* events */ #define VID_NOTIFY_SWITCHED 0x80 #define VID_NOTIFY_REPROBE 0x81 +#define VID_NOTIFY_INC_BRN 0x86 +#define VID_NOTIFY_DEC_BRN 0x87 /* _DOS (Enable/Disable Output Switching) argument bits */ #define DOS_SWITCH_MASK 3 @@ -577,6 +581,9 @@ acpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) /* XXX - see above. */ vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; } + if (vo->vo_levels != NULL) + AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, + acpi_video_vo_notify_handler, vo); ACPI_SERIAL_END(video_output); } @@ -590,8 +597,11 @@ acpi_video_vo_destroy(struct acpi_video_output *vo) vo->vo_sysctl_tree = NULL; sysctl_ctx_free(&vo->vo_sysctl_ctx); } - if (vo->vo_levels != NULL) + if (vo->vo_levels != NULL) { + AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, + acpi_video_vo_notify_handler); AcpiOsFree(vo->vo_levels); + } switch (vo->adr & DOD_DEVID_MASK) { case DOD_DEVID_MONITOR: @@ -627,6 +637,52 @@ acpi_video_vo_check_level(struct acpi_video_output *vo, int level) return (EINVAL); } +static void +acpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) +{ + struct acpi_video_output *vo; + int i, j, level, new_level; + + vo = context; + ACPI_SERIAL_BEGIN(video_output); + if (vo->handle == NULL) { + ACPI_SERIAL_END(video_output); + return; + } + + switch (notify) { + case VID_NOTIFY_INC_BRN: + case VID_NOTIFY_DEC_BRN: + if (vo->vo_levels == NULL) + break; + level = vo_get_brightness(vo->handle); + if (level < 0) + break; + new_level = level; + for (i = 0; i < vo->vo_numlevels; i++) { + j = vo->vo_levels[i]; + if (notify == VID_NOTIFY_INC_BRN) { + if (j > level && + (j < new_level || level == new_level)) + new_level = j; + } else { + if (j < level && + (j > new_level || level == new_level)) + new_level = j; + } + } + if (new_level != level) { + vo_set_brightness(vo->handle, new_level); + vo->vo_brightness = new_level; + } + break; + default: + printf("%s: unknown notify event 0x%x\n", + acpi_name(vo->handle), notify); + } + ACPI_SERIAL_END(video_output); +} + /* ARGSUSED */ static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) @@ -900,6 +956,25 @@ out: return (num); } +static int +vo_get_brightness(ACPI_HANDLE handle) +{ + UINT32 level; + ACPI_STATUS status; + + ACPI_SERIAL_ASSERT(video_output); + status = acpi_GetInteger(handle, "_BQC", &level); + if (ACPI_FAILURE(status)) { + printf("can't evaluate %s._BQC - %s\n", acpi_name(handle), + AcpiFormatException(status)); + return (-1); + } + if (level > 100) + return (-1); + + return (level); +} + static void vo_set_brightness(ACPI_HANDLE handle, int level) { diff --git a/sys/dev/aic7xxx/aic79xx_osm.c b/sys/dev/aic7xxx/aic79xx_osm.c index 81eade6..375de63 100644 --- a/sys/dev/aic7xxx/aic79xx_osm.c +++ b/sys/dev/aic7xxx/aic79xx_osm.c @@ -83,11 +83,13 @@ static const char *ahd_sysctl_node_elements[] = { "debug" }; +#ifndef NO_SYSCTL_DESCR static const char *ahd_sysctl_node_descriptions[] = { "root error collection for aic79xx controllers", "summary collection for aic79xx controllers", "debug collection for aic79xx controllers" }; +#endif static const char *ahd_sysctl_errors_elements[] = { "Cerrors", @@ -95,11 +97,13 @@ static const char *ahd_sysctl_errors_elements[] = { "Ferrors" }; +#ifndef NO_SYSCTL_DESCR static const char *ahd_sysctl_errors_descriptions[] = { "Correctable errors", "Uncorrectable errors", "Fatal errors" }; +#endif static int ahd_set_debugcounters(SYSCTL_HANDLER_ARGS) diff --git a/sys/dev/ath/ath_hal/ah_eeprom_v14.h b/sys/dev/ath/ath_hal/ah_eeprom_v14.h index 90e2c74..d2caa80 100644 --- a/sys/dev/ath/ath_hal/ah_eeprom_v14.h +++ b/sys/dev/ath/ath_hal/ah_eeprom_v14.h @@ -14,7 +14,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: ah_eeprom_v14.h,v 1.3 2008/11/10 04:08:00 sam Exp $ + * $FreeBSD$ */ #ifndef _AH_EEPROM_V14_H_ #define _AH_EEPROM_V14_H_ @@ -76,6 +76,7 @@ #define AR5416_EEPMISC_BIG_ENDIAN 0x01 #define FREQ2FBIN(x,y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5)) #define AR5416_MAX_CHAINS 3 +#define AR5416_PWR_TABLE_OFFSET_DB -5 #define AR5416_ANT_16S 25 #define AR5416_NUM_ANT_CHAIN_FIELDS 7 diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c b/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c index 98f4c2d..56731f4 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c @@ -172,12 +172,11 @@ ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan) ichan = ath_hal_checkchannel(ah, chan); HALASSERT(ichan != AH_NULL); - if (AR_SREV_KITE_12_OR_LATER(ah)) { - /* Clear the carrier leak cal bit */ OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); - if (IEEE80211_IS_CHAN_HT20(chan)) { + OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, + AR_PHY_CL_CAL_ENABLE); OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); OS_REG_SET_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); @@ -191,7 +190,7 @@ ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan) if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, - "%s: offset calibration failed to " + "%s: HT offset calibration failed to " "complete in 1ms; noisy environment?\n", __func__); return AH_FALSE; @@ -201,14 +200,10 @@ ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan) AR_PHY_PARALLEL_CAL_ENABLE); } OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); - - /* Enable Rx Filter Cal */ OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); OS_REG_SET_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); - - /* kick off the cal */ OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration complete */ @@ -219,8 +214,6 @@ ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan) "noisy environment?\n", __func__); return AH_FALSE; } - /* Set the cl cal bit and rerun the cal a 2nd time */ - /* Enable Rx Filter Cal */ OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, @@ -258,9 +251,10 @@ ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan) OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration complete */ - if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { + if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, - "%s: offset calibration did not complete in 1ms; " + "%s: AGC offset calibration did not complete in 1ms; " "noisy environment?\n", __func__); return AH_FALSE; } diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c b/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c index e17db99..ac6009b 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c @@ -30,7 +30,7 @@ #include "ar5416/ar5416phy.h" /* - * Return the wireless modes (a,b,g,t) supported by hardware. + * Return the wireless modes (a,b,g,n,t) supported by hardware. * * This value is what is actually supported by the hardware * and is unaffected by regulatory/country code settings. diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c index da9b4ac..d2beb02 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c @@ -898,6 +898,8 @@ ar5416SetTransmitPower(struct ath_hal *ah, ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]); if (ratesArray[i] > AR5416_MAX_RATE_POWER) ratesArray[i] = AR5416_MAX_RATE_POWER; + if (AR_SREV_MERLIN_10_OR_LATER(ah)) + ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; } #ifdef AH_EEPROM_DUMP @@ -1225,7 +1227,7 @@ ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) const struct ar5416eeprom_4k *eep4k; const MODAL_EEP_HEADER *pModal; const MODAL_EEP4K_HEADER *pModal4k; - int i, regChainOffset; + int i, regChainOffset = 0; uint8_t txRxAttenLocal; /* workaround for eeprom versions <= 14.2 */ HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); @@ -1253,9 +1255,9 @@ ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) (pModal) ? pModal->antCtrlCommon : pModal4k->antCtrlCommon); for (i = 0; i < AR5416_MAX_CHAINS; i++) { - if (AR_SREV_KITE(ah) && i >= 1) break; - if (AR_SREV_MERLIN(ah) && i >= 2) break; - if (AR_SREV_OWL_20_OR_LATER(ah) && +// if (AR_SREV_KITE(ah) && i >= 2) break; + if (AR_SREV_MERLIN(ah) && i >= 2) break; + if (AR_SREV_OWL_20_OR_LATER(ah) && (AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) && i != 0) { /* Regs are swapped from chain 2 to 1 for 5416 2_0 with @@ -1289,15 +1291,23 @@ ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) } else txRxAtten = txRxAttenLocal; - OS_REG_WRITE(ah, AR_PHY_RXGAIN + regChainOffset, - (OS_REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & ~AR_PHY_RXGAIN_TXRX_ATTEN) | - SM(txRxAtten, AR_PHY_RXGAIN_TXRX_ATTEN)); - - OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | - SM((pModal) ? pModal->rxTxMarginCh[i]: - pModal4k->rxTxMarginCh[i], - AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); + if (AR_SREV_MERLIN_10_OR_LATER(ah)) { + OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAtten); + OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_MARGIN, + (pModal) ? pModal->rxTxMarginCh[i] : pModal4k->rxTxMarginCh[i]); + } else { + OS_REG_WRITE(ah, AR_PHY_RXGAIN + regChainOffset, + (OS_REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & ~AR_PHY_RXGAIN_TXRX_ATTEN) | + SM(txRxAtten, AR_PHY_RXGAIN_TXRX_ATTEN)); + + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | + SM((pModal) ? pModal->rxTxMarginCh[i]: + pModal4k->rxTxMarginCh[i], + AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); + } } } @@ -1360,6 +1370,20 @@ ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | SM(pModal->bswAtten[1], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); } else { + if (AR_SREV_MERLIN_10_OR_LATER(ah)) { + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, + (pModal) ? pModal->bswMargin[i] : pModal4k->bswMargin[i]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, + (pModal) ? pModal->bswAtten[i] : pModal4k->bswAtten[i]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + (pModal) ? pModal->xatten2Margin[i] : pModal4k->xatten2Margin[i]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, + (pModal) ? pModal->xatten2Db[i] : pModal4k->xatten2Db[i]); + } else { OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | SM((pModal) ? pModal->bswMargin[1] : pModal4k->bswMargin[1], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); @@ -1372,6 +1396,7 @@ ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | SM((pModal) ? pModal->bswAtten[2] : pModal4k->bswAtten[2], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + } } OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_BSW_MARGIN, (pModal) ? pModal->bswMargin[0] : pModal4k->bswMargin[0]); @@ -1440,10 +1465,7 @@ ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[1]), pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[2]); else - twiceLargestAntenna = AH_MAX(AH_MAX( - pEepData4k->modalHeader.antennaGainCh[0], - pEepData4k->modalHeader.antennaGainCh[1]), - pEepData4k->modalHeader.antennaGainCh[2]); + twiceLargestAntenna = pEepData4k->modalHeader.antennaGainCh[0]; #if 0 /* Turn it back on if we need to calculate per chain antenna gain reduction */ /* Use only if the expected gain > 6dbi */ @@ -1577,15 +1599,15 @@ ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, freq = centers.ctl_center; } - ctlIndex = (pEepData) ? pEepData->ctlIndex[0] : - pEepData4k->ctlIndex[0]; /* walk through each CTL index stored in EEPROM */ - for (i = 0; (i < numctls) && ctlIndex; i++) { + for (i = 0; i < numctls; i++) { uint16_t twiceMinEdgePower; CAL_CTL_EDGES *ctlEdge; ctlIndex = (pEepData) ? pEepData->ctlIndex[i] : pEepData4k->ctlIndex[i]; + if (!ctlIndex) + break; /* compare test group from regulatory channel list with test mode from pCtlMode list */ if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ctlIndex) || @@ -1943,7 +1965,7 @@ ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, SM(xpdGainValues[1], AR_PHY_TPCRG1_PD_GAIN_2) | SM(xpdGainValues[2], AR_PHY_TPCRG1_PD_GAIN_3)); for (i = 0; i < AR5416_MAX_CHAINS; i++) { - if (AR_SREV_KITE(ah) && i >= AR5416_4K_MAX_CHAINS) break; +// if (AR_SREV_KITE(ah) && i >= AR5416_4K_MAX_CHAINS) break; if (AR_SREV_OWL_20_OR_LATER(ah) && ( AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) && (i != 0)) { /* Regs are swapped from chain 2 to 1 for 5416 2_0 with @@ -1981,7 +2003,6 @@ ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, * negative or greater than 0. Need to offset the power * values by the amount of minPower for griffin */ - OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | @@ -2149,7 +2170,10 @@ ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, /* Find starting index for this pdGain */ if (i == 0) { - ss = 0; /* for the first pdGain, start from index 0 */ + if (AR_SREV_MERLIN_10_OR_LATER(ah)) + ss = (int16_t)(0 - (minPwrT4[i] / 2)); + else + ss = 0; /* for the first pdGain, start from index 0 */ } else { /* need overlap entries extrapolated below. */ ss = (int16_t)((pPdGainBoundaries[i-1] - (minPwrT4[i] / 2)) - tPdGainOverlap + 1 + minDelta); @@ -2165,7 +2189,7 @@ ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, ss++; } - sizeCurrVpdTable = (uint8_t)((maxPwrT4[i] - minPwrT4[i]) / 2 +1); + sizeCurrVpdTable = (uint8_t)((maxPwrT4[i] - minPwrT4[i]) / 2 + 1); tgtIndex = (uint8_t)(pPdGainBoundaries[i] + tPdGainOverlap - (minPwrT4[i] / 2)); maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; @@ -2314,13 +2338,20 @@ ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t phymode; HAL_HT_MACMODE macmode; /* MAC - 20/40 mode */ + uint32_t dacFifo; if (!IEEE80211_IS_CHAN_HT(chan)) return; + if (AR_SREV_KITE_10_OR_LATER(ah)) + dacFifo = OS_REG_READ(ah, AR_PHY_TURBO) + & AR_PHY_FC_ENABLE_DAC_FIFO; + else + dacFifo = 0; + /* Enable 11n HT, 20 MHz */ phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 - | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH; + | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH | dacFifo; /* Configure baseband for dynamic 20/40 operation */ if (IEEE80211_IS_CHAN_HT40(chan)) { diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416phy.h b/sys/dev/ath/ath_hal/ar5416/ar5416phy.h index 65b496e..d5185b6 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416phy.h +++ b/sys/dev/ath/ath_hal/ar5416/ar5416phy.h @@ -48,6 +48,7 @@ #define AR_PHY_FC_SHORT_GI_40 0x00000080 /* allow short GI for HT 40 */ #define AR_PHY_FC_WALSH 0x00000100 /* walsh spatial spreading for 2 chains,2 streams TX */ #define AR_PHY_FC_SINGLE_HT_LTF1 0x00000200 /* single length (4us) 1st HT long training symbol */ +#define AR_PHY_FC_ENABLE_DAC_FIFO 0x00000800 #define AR_PHY_TIMING2 0x9810 /* Timing Control 2 */ #define AR_PHY_TIMING2_USE_FORCE 0x00001000 @@ -78,6 +79,20 @@ #define AR_PHY_GAIN_2GHZ_BSW_ATTEN 0x0000001F #define AR_PHY_GAIN_2GHZ_BSW_ATTEN_S 0 +#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN 0x003E0000 +#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S 17 +#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN 0x0001F000 +#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S 12 +#define AR_PHY_GAIN_2GHZ_XATTEN2_DB 0x00000FC0 +#define AR_PHY_GAIN_2GHZ_XATTEN2_DB_S 6 +#define AR_PHY_GAIN_2GHZ_XATTEN1_DB 0x0000003F +#define AR_PHY_GAIN_2GHZ_XATTEN1_DB_S 0 + +#define AR9280_PHY_RXGAIN_TXRX_ATTEN 0x00003F80 +#define AR9280_PHY_RXGAIN_TXRX_ATTEN_S 7 +#define AR9280_PHY_RXGAIN_TXRX_MARGIN 0x001FC000 +#define AR9280_PHY_RXGAIN_TXRX_MARGIN_S 14 + #define AR_PHY_EXT_CCA 0x99bc #define AR_PHY_EXT_CCA_CYCPWR_THR1 0x0000FE00 #define AR_PHY_EXT_CCA_CYCPWR_THR1_S 9 diff --git a/sys/dev/ath/ath_hal/ar5416/ar9280.c b/sys/dev/ath/ath_hal/ar5416/ar9280.c index 72720f7..dd69311 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar9280.c +++ b/sys/dev/ath/ath_hal/ar5416/ar9280.c @@ -277,12 +277,15 @@ ar9280GetNoiseFloor(struct ath_hal *ah, int16_t nfarray[]) "NF calibrated [ctl] [chain 0] is %d\n", nf); nfarray[0] = nf; - nf = MS(OS_REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR); - if (nf & 0x100) - nf = 0 - ((nf ^ 0x1ff) + 1); - HALDEBUG(ah, HAL_DEBUG_NFCAL, - "NF calibrated [ctl] [chain 1] is %d\n", nf); - nfarray[1] = nf; + if (!AR_SREV_KITE(ah)) { + nf = MS(OS_REG_READ(ah, AR_PHY_CH1_CCA), + AR9280_PHY_CH1_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + HALDEBUG(ah, HAL_DEBUG_NFCAL, + "NF calibrated [ctl] [chain 1] is %d\n", nf); + nfarray[1] = nf; + } nf = MS(OS_REG_READ(ah, AR_PHY_EXT_CCA), AR9280_PHY_EXT_MINCCA_PWR); if (nf & 0x100) @@ -291,12 +294,15 @@ ar9280GetNoiseFloor(struct ath_hal *ah, int16_t nfarray[]) "NF calibrated [ext] [chain 0] is %d\n", nf); nfarray[3] = nf; - nf = MS(OS_REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR9280_PHY_CH1_EXT_MINCCA_PWR); - if (nf & 0x100) + if (AR_SREV_KITE(ah)) { + nf = MS(OS_REG_READ(ah, AR_PHY_CH1_EXT_CCA), + AR9280_PHY_CH1_EXT_MINCCA_PWR); + if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); - HALDEBUG(ah, HAL_DEBUG_NFCAL, - "NF calibrated [ext] [chain 1] is %d\n", nf); - nfarray[4] = nf; + HALDEBUG(ah, HAL_DEBUG_NFCAL, + "NF calibrated [ext] [chain 1] is %d\n", nf); + nfarray[4] = nf; + } } /* diff --git a/sys/dev/ath/ath_hal/ar5416/ar9280.h b/sys/dev/ath/ath_hal/ar5416/ar9280.h index f2e97d96..2bc7f82 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar9280.h +++ b/sys/dev/ath/ath_hal/ar5416/ar9280.h @@ -30,7 +30,9 @@ struct ath_hal_9280 { #define AH9280(_ah) ((struct ath_hal_9280 *)(_ah)) #define AR9280_DEFAULT_RXCHAINMASK 3 +#define AR9285_DEFAULT_RXCHAINMASK 1 #define AR9280_DEFAULT_TXCHAINMASK 1 +#define AR9285_DEFAULT_TXCHAINMASK 1 HAL_BOOL ar9280RfAttach(struct ath_hal *, HAL_STATUS *); diff --git a/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c b/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c index 66fe595..bac8a55 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c +++ b/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c @@ -123,6 +123,10 @@ ar9280Attach(uint16_t devid, HAL_SOFTC sc, AH5416(ah)->ah_writeIni = ar9280WriteIni; AH5416(ah)->ah_rx_chainmask = AR9280_DEFAULT_RXCHAINMASK; AH5416(ah)->ah_tx_chainmask = AR9280_DEFAULT_TXCHAINMASK; + if (AR_SREV_KITE(ah)) { + AH5416(ah)->ah_rx_chainmask = AR9285_DEFAULT_RXCHAINMASK; + AH5416(ah)->ah_tx_chainmask = AR9285_DEFAULT_TXCHAINMASK; + } if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index ed69a7d..e600111 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -620,6 +620,13 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) sc->sc_wmetkipmic = 1; } sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR); + /* + * Check for multicast key search support. + */ + if (ath_hal_hasmcastkeysearch(sc->sc_ah) && + !ath_hal_getmcastkeysearch(sc->sc_ah)) { + ath_hal_setmcastkeysearch(sc->sc_ah, 1); + } sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah); /* * Mark key cache slots associated with global keys @@ -2038,7 +2045,7 @@ ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) { /* * Group keys on hardware that supports multicast frame - * key search use a mac that is the sender's address with + * key search use a MAC that is the sender's address with * the high bit set instead of the app-specified address. */ IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr); @@ -2218,8 +2225,10 @@ ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, * it permits us to support multiple users for adhoc and/or * multi-station operation. */ - if (k->wk_keyix != IEEE80211_KEYIX_NONE || /* global key */ - ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey)) { + if (k->wk_keyix != IEEE80211_KEYIX_NONE) { + /* + * Only global keys should have key index assigned. + */ if (!(&vap->iv_nw_keys[0] <= k && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { /* should not happen */ @@ -2227,12 +2236,21 @@ ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, "%s: bogus group key\n", __func__); return 0; } + if (vap->iv_opmode != IEEE80211_M_HOSTAP || + !(k->wk_flags & IEEE80211_KEY_GROUP) || + !sc->sc_mcastkey) { + /* + * XXX we pre-allocate the global keys so + * have no way to check if they've already + * been allocated. + */ + *keyix = *rxkeyix = k - vap->iv_nw_keys; + return 1; + } /* - * XXX we pre-allocate the global keys so - * have no way to check if they've already been allocated. + * Group key and device supports multicast key search. */ - *keyix = *rxkeyix = k - vap->iv_nw_keys; - return 1; + k->wk_keyix = IEEE80211_KEYIX_NONE; } /* @@ -6944,6 +6962,8 @@ ath_announce(struct ath_softc *sc) if_printf(ifp, "using %u rx buffers\n", ath_rxbuf); if (ath_txbuf != ATH_TXBUF) if_printf(ifp, "using %u tx buffers\n", ath_txbuf); + if (sc->sc_mcastkey && bootverbose) + if_printf(ifp, "using multicast key search\n"); } #ifdef IEEE80211_SUPPORT_TDMA diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h index 29efc4c..31b1d4c 100644 --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -580,14 +580,12 @@ void ath_intr(void *); ath_hal_setcapability(_ah, HAL_CAP_TPC, 1, _v, NULL) #define ath_hal_hasbursting(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_BURST, 0, NULL) == HAL_OK) -#ifdef notyet +#define ath_hal_setmcastkeysearch(_ah, _v) \ + ath_hal_setcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 0, _v, NULL) #define ath_hal_hasmcastkeysearch(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL) == HAL_OK) #define ath_hal_getmcastkeysearch(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 1, NULL) == HAL_OK) -#else -#define ath_hal_getmcastkeysearch(_ah) 0 -#endif #define ath_hal_hasfastframes(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_FASTFRAME, 0, NULL) == HAL_OK) #define ath_hal_hasbssidmask(_ah) \ diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c index e08ace8..1eddbe0 100644 --- a/sys/dev/bge/if_bge.c +++ b/sys/dev/bge/if_bge.c @@ -1993,10 +1993,6 @@ bge_probe(device_t dev) snprintf(buf, 96, "%s, %sASIC rev. %#08x", model, br != NULL ? "" : "unknown ", id); device_set_desc_copy(dev, buf); - if (pci_get_subvendor(dev) == DELL_VENDORID) - sc->bge_flags |= BGE_FLAG_NO_3LED; - if (did == BCOM_DEVICEID_BCM5755M) - sc->bge_flags |= BGE_FLAG_ADJUST_TRIM; return (0); } t++; @@ -2607,6 +2603,10 @@ bge_attach(device_t dev) sc->bge_flags |= BGE_FLAG_ADC_BUG; if (sc->bge_chipid == BGE_CHIPID_BCM5704_A0) sc->bge_flags |= BGE_FLAG_5704_A0_BUG; + if (pci_get_subvendor(dev) == DELL_VENDORID) + sc->bge_flags |= BGE_FLAG_NO_3LED; + if (pci_get_device(dev) == BCOM_DEVICEID_BCM5755M) + sc->bge_flags |= BGE_FLAG_ADJUST_TRIM; if (BGE_IS_5705_PLUS(sc) && !(sc->bge_flags & BGE_FLAG_ADJUST_TRIM)) { if (sc->bge_asicrev == BGE_ASICREV_BCM5755 || diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c index dfbeb02..069a8c0 100644 --- a/sys/dev/sound/usb/uaudio.c +++ b/sys/dev/sound/usb/uaudio.c @@ -155,16 +155,16 @@ struct uaudio_chan { struct pcmchan_caps pcm_cap; /* capabilities */ struct snd_dbuf *pcm_buf; - const struct usb_config *usb2_cfg; + const struct usb_config *usb_cfg; struct mtx *pcm_mtx; /* lock protecting this structure */ struct uaudio_softc *priv_sc; struct pcm_channel *pcm_ch; struct usb_xfer *xfer[UAUDIO_NCHANBUFS]; - const struct usb2_audio_streaming_interface_descriptor *p_asid; - const struct usb2_audio_streaming_type1_descriptor *p_asf1d; - const struct usb2_audio_streaming_endpoint_descriptor *p_sed; - const usb2_endpoint_descriptor_audio_t *p_ed1; - const usb2_endpoint_descriptor_audio_t *p_ed2; + const struct usb_audio_streaming_interface_descriptor *p_asid; + const struct usb_audio_streaming_type1_descriptor *p_asf1d; + const struct usb_audio_streaming_endpoint_descriptor *p_sed; + const usb_endpoint_descriptor_audio_t *p_ed1; + const usb_endpoint_descriptor_audio_t *p_ed2; const struct uaudio_format *p_fmt; uint8_t *buf; /* pointer to buffer */ @@ -278,13 +278,13 @@ struct uaudio_search_result { struct uaudio_terminal_node { union { const struct usb_descriptor *desc; - const struct usb2_audio_input_terminal *it; - const struct usb2_audio_output_terminal *ot; - const struct usb2_audio_mixer_unit_0 *mu; - const struct usb2_audio_selector_unit *su; - const struct usb2_audio_feature_unit *fu; - const struct usb2_audio_processing_unit_0 *pu; - const struct usb2_audio_extension_unit_0 *eu; + const struct usb_audio_input_terminal *it; + const struct usb_audio_output_terminal *ot; + const struct usb_audio_mixer_unit_0 *mu; + const struct usb_audio_selector_unit *su; + const struct usb_audio_feature_unit *fu; + const struct usb_audio_processing_unit_0 *pu; + const struct usb_audio_extension_unit_0 *eu; } u; struct uaudio_search_result usr; struct uaudio_terminal_node *root; @@ -359,7 +359,7 @@ static void uaudio_mixer_add_mixer(struct uaudio_softc *, static void uaudio_mixer_add_selector(struct uaudio_softc *, const struct uaudio_terminal_node *, int); static uint32_t uaudio_mixer_feature_get_bmaControls( - const struct usb2_audio_feature_unit *, uint8_t); + const struct usb_audio_feature_unit *, uint8_t); static void uaudio_mixer_add_feature(struct uaudio_softc *, const struct uaudio_terminal_node *, int); static void uaudio_mixer_add_processing_updown(struct uaudio_softc *, @@ -368,7 +368,7 @@ static void uaudio_mixer_add_processing(struct uaudio_softc *, const struct uaudio_terminal_node *, int); static void uaudio_mixer_add_extension(struct uaudio_softc *, const struct uaudio_terminal_node *, int); -static struct usb2_audio_cluster uaudio_mixer_get_cluster(uint8_t, +static struct usb_audio_cluster uaudio_mixer_get_cluster(uint8_t, const struct uaudio_terminal_node *); static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *, struct uaudio_mixer_node *); @@ -408,7 +408,7 @@ static int32_t umidi_detach(device_t dev); #if USB_DEBUG static void uaudio_chan_dump_ep_desc( - const usb2_endpoint_descriptor_audio_t *); + const usb_endpoint_descriptor_audio_t *); static void uaudio_mixer_dump_cluster(uint8_t, const struct uaudio_terminal_node *); static const char *uaudio_mixer_get_terminal_name(uint16_t); @@ -782,7 +782,7 @@ uaudio_detach(device_t dev) #if USB_DEBUG static void -uaudio_chan_dump_ep_desc(const usb2_endpoint_descriptor_audio_t *ed) +uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed) { if (ed) { DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n" @@ -803,11 +803,11 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, uint32_t rate, uint8_t channels, uint8_t bit_resolution) { struct usb_descriptor *desc = NULL; - const struct usb2_audio_streaming_interface_descriptor *asid = NULL; - const struct usb2_audio_streaming_type1_descriptor *asf1d = NULL; - const struct usb2_audio_streaming_endpoint_descriptor *sed = NULL; - const usb2_endpoint_descriptor_audio_t *ed1 = NULL; - const usb2_endpoint_descriptor_audio_t *ed2 = NULL; + const struct usb_audio_streaming_interface_descriptor *asid = NULL; + const struct usb_audio_streaming_type1_descriptor *asf1d = NULL; + const struct usb_audio_streaming_endpoint_descriptor *sed = NULL; + const usb_endpoint_descriptor_audio_t *ed1 = NULL; + const usb_endpoint_descriptor_audio_t *ed2 = NULL; struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); struct usb_interface_descriptor *id; const struct uaudio_format *p_fmt; @@ -1045,10 +1045,10 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, chan->iface_alt_index = alt_index; if (ep_dir == UE_DIR_IN) - chan->usb2_cfg = + chan->usb_cfg = uaudio_cfg_record; else - chan->usb2_cfg = + chan->usb_cfg = uaudio_cfg_play; chan->sample_size = (( @@ -1429,7 +1429,7 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, } } if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer, - ch->usb2_cfg, UAUDIO_NCHANBUFS, ch, ch->pcm_mtx)) { + ch->usb_cfg, UAUDIO_NCHANBUFS, ch, ch->pcm_mtx)) { DPRINTF("could not allocate USB transfers!\n"); goto error; } @@ -1709,7 +1709,7 @@ uaudio_mixer_add_input(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { #if USB_DEBUG - const struct usb2_audio_input_terminal *d = iot[id].u.it; + const struct usb_audio_input_terminal *d = iot[id].u.it; DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " @@ -1725,7 +1725,7 @@ uaudio_mixer_add_output(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { #if USB_DEBUG - const struct usb2_audio_output_terminal *d = iot[id].u.ot; + const struct usb_audio_output_terminal *d = iot[id].u.ot; DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", @@ -1740,8 +1740,8 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc, { struct uaudio_mixer_node mix; - const struct usb2_audio_mixer_unit_0 *d0 = iot[id].u.mu; - const struct usb2_audio_mixer_unit_1 *d1; + const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu; + const struct usb_audio_mixer_unit_1 *d1; uint32_t bno; /* bit number */ uint32_t p; /* bit number accumulator */ @@ -1823,7 +1823,7 @@ static void uaudio_mixer_add_selector(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb2_audio_selector_unit *d = iot[id].u.su; + const struct usb_audio_selector_unit *d = iot[id].u.su; struct uaudio_mixer_node mix; uint16_t i; @@ -1864,7 +1864,7 @@ uaudio_mixer_add_selector(struct uaudio_softc *sc, } static uint32_t -uaudio_mixer_feature_get_bmaControls(const struct usb2_audio_feature_unit *d, +uaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d, uint8_t index) { uint32_t temp = 0; @@ -1889,7 +1889,7 @@ static void uaudio_mixer_add_feature(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb2_audio_feature_unit *d = iot[id].u.fu; + const struct usb_audio_feature_unit *d = iot[id].u.fu; struct uaudio_mixer_node mix; uint32_t fumask; uint32_t mmask; @@ -2015,10 +2015,10 @@ static void uaudio_mixer_add_processing_updown(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb2_audio_processing_unit_0 *d0 = iot[id].u.pu; - const struct usb2_audio_processing_unit_1 *d1 = + const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu; + const struct usb_audio_processing_unit_1 *d1 = (const void *)(d0->baSourceId + d0->bNrInPins); - const struct usb2_audio_processing_unit_updown *ud = + const struct usb_audio_processing_unit_updown *ud = (const void *)(d1->bmControls + d1->bControlSize); struct uaudio_mixer_node mix; uint8_t i; @@ -2057,8 +2057,8 @@ static void uaudio_mixer_add_processing(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb2_audio_processing_unit_0 *d0 = iot[id].u.pu; - const struct usb2_audio_processing_unit_1 *d1 = + const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu; + const struct usb_audio_processing_unit_1 *d1 = (const void *)(d0->baSourceId + d0->bNrInPins); struct uaudio_mixer_node mix; uint16_t ptype; @@ -2102,8 +2102,8 @@ static void uaudio_mixer_add_extension(struct uaudio_softc *sc, const struct uaudio_terminal_node *iot, int id) { - const struct usb2_audio_extension_unit_0 *d0 = iot[id].u.eu; - const struct usb2_audio_extension_unit_1 *d1 = + const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu; + const struct usb_audio_extension_unit_1 *d1 = (const void *)(d0->baSourceId + d0->bNrInPins); struct uaudio_mixer_node mix; @@ -2133,19 +2133,19 @@ uaudio_mixer_add_extension(struct uaudio_softc *sc, static const void * uaudio_mixer_verify_desc(const void *arg, uint32_t len) { - const struct usb2_audio_mixer_unit_1 *d1; - const struct usb2_audio_extension_unit_1 *e1; - const struct usb2_audio_processing_unit_1 *u1; + const struct usb_audio_mixer_unit_1 *d1; + const struct usb_audio_extension_unit_1 *e1; + const struct usb_audio_processing_unit_1 *u1; union { const struct usb_descriptor *desc; - const struct usb2_audio_input_terminal *it; - const struct usb2_audio_output_terminal *ot; - const struct usb2_audio_mixer_unit_0 *mu; - const struct usb2_audio_selector_unit *su; - const struct usb2_audio_feature_unit *fu; - const struct usb2_audio_processing_unit_0 *pu; - const struct usb2_audio_extension_unit_0 *eu; + const struct usb_audio_input_terminal *it; + const struct usb_audio_output_terminal *ot; + const struct usb_audio_mixer_unit_0 *mu; + const struct usb_audio_selector_unit *su; + const struct usb_audio_feature_unit *fu; + const struct usb_audio_processing_unit_0 *pu; + const struct usb_audio_extension_unit_0 *eu; } u; u.desc = arg; @@ -2269,7 +2269,7 @@ uaudio_mixer_dump_cluster(uint8_t id, const struct uaudio_terminal_node *iot) }; uint16_t cc; uint8_t i; - const struct usb2_audio_cluster cl = uaudio_mixer_get_cluster(id, iot); + const struct usb_audio_cluster cl = uaudio_mixer_get_cluster(id, iot); cc = UGETW(cl.wChannelConfig); @@ -2286,10 +2286,10 @@ uaudio_mixer_dump_cluster(uint8_t id, const struct uaudio_terminal_node *iot) #endif -static struct usb2_audio_cluster +static struct usb_audio_cluster uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) { - struct usb2_audio_cluster r; + struct usb_audio_cluster r; const struct usb_descriptor *dp; uint8_t i; @@ -2311,7 +2311,7 @@ uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) break; case UDESCSUB_AC_MIXER: - r = *(const struct usb2_audio_cluster *) + r = *(const struct usb_audio_cluster *) &iot[id].u.mu->baSourceId[iot[id].u.mu-> bNrInPins]; goto done; @@ -2328,13 +2328,13 @@ uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) break; case UDESCSUB_AC_PROCESSING: - r = *((const struct usb2_audio_cluster *) + r = *((const struct usb_audio_cluster *) &iot[id].u.pu->baSourceId[iot[id].u.pu-> bNrInPins]); goto done; case UDESCSUB_AC_EXTENSION: - r = *((const struct usb2_audio_cluster *) + r = *((const struct usb_audio_cluster *) &iot[id].u.eu->baSourceId[iot[id].u.eu-> bNrInPins]); goto done; @@ -2760,10 +2760,10 @@ static void uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev, void *desc) { - const struct usb2_audio_control_descriptor *acdp; + const struct usb_audio_control_descriptor *acdp; struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); const struct usb_descriptor *dp; - const struct usb2_audio_unit *au; + const struct usb_audio_unit *au; struct uaudio_terminal_node *iot = NULL; uint16_t wTotalLen; uint8_t ID_max = 0; /* inclusive */ diff --git a/sys/dev/sound/usb/uaudioreg.h b/sys/dev/sound/usb/uaudioreg.h index 495f94a..0df4ea5 100644 --- a/sys/dev/sound/usb/uaudioreg.h +++ b/sys/dev/sound/usb/uaudioreg.h @@ -64,9 +64,9 @@ typedef struct { */ uByte bRefresh; uByte bSynchAddress; -} __packed usb2_endpoint_descriptor_audio_t; +} __packed usb_endpoint_descriptor_audio_t; -struct usb2_audio_control_descriptor { +struct usb_audio_control_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -76,7 +76,7 @@ struct usb2_audio_control_descriptor { uByte baInterfaceNr[1]; } __packed; -struct usb2_audio_streaming_interface_descriptor { +struct usb_audio_streaming_interface_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -85,7 +85,7 @@ struct usb2_audio_streaming_interface_descriptor { uWord wFormatTag; } __packed; -struct usb2_audio_streaming_endpoint_descriptor { +struct usb_audio_streaming_endpoint_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -97,7 +97,7 @@ struct usb2_audio_streaming_endpoint_descriptor { uWord wLockDelay; } __packed; -struct usb2_audio_streaming_type1_descriptor { +struct usb_audio_streaming_type1_descriptor { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -115,7 +115,7 @@ struct usb2_audio_streaming_type1_descriptor { #define UA_SAMP_HI(p) UA_GETSAMP(p, 1) } __packed; -struct usb2_audio_cluster { +struct usb_audio_cluster { uByte bNrChannels; uWord wChannelConfig; #define UA_CHANNEL_LEFT 0x0001 @@ -134,7 +134,7 @@ struct usb2_audio_cluster { } __packed; /* Shared by all units and terminals */ -struct usb2_audio_unit { +struct usb_audio_unit { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -142,7 +142,7 @@ struct usb2_audio_unit { }; /* UDESCSUB_AC_INPUT */ -struct usb2_audio_input_terminal { +struct usb_audio_input_terminal { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -156,7 +156,7 @@ struct usb2_audio_input_terminal { } __packed; /* UDESCSUB_AC_OUTPUT */ -struct usb2_audio_output_terminal { +struct usb_audio_output_terminal { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -168,16 +168,16 @@ struct usb2_audio_output_terminal { } __packed; /* UDESCSUB_AC_MIXER */ -struct usb2_audio_mixer_unit_0 { +struct usb_audio_mixer_unit_0 { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; uByte bUnitId; uByte bNrInPins; uByte baSourceId[0]; /* [bNrInPins] */ - /* struct usb2_audio_mixer_unit_1 */ + /* struct usb_audio_mixer_unit_1 */ } __packed; -struct usb2_audio_mixer_unit_1 { +struct usb_audio_mixer_unit_1 { uByte bNrChannels; uWord wChannelConfig; uByte iChannelNames; @@ -186,7 +186,7 @@ struct usb2_audio_mixer_unit_1 { } __packed; /* UDESCSUB_AC_SELECTOR */ -struct usb2_audio_selector_unit { +struct usb_audio_selector_unit { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -197,7 +197,7 @@ struct usb2_audio_selector_unit { } __packed; /* UDESCSUB_AC_FEATURE */ -struct usb2_audio_feature_unit { +struct usb_audio_feature_unit { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -209,7 +209,7 @@ struct usb2_audio_feature_unit { } __packed; /* UDESCSUB_AC_PROCESSING */ -struct usb2_audio_processing_unit_0 { +struct usb_audio_processing_unit_0 { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -217,9 +217,9 @@ struct usb2_audio_processing_unit_0 { uWord wProcessType; uByte bNrInPins; uByte baSourceId[0]; /* [bNrInPins] */ - /* struct usb2_audio_processing_unit_1 */ + /* struct usb_audio_processing_unit_1 */ } __packed; -struct usb2_audio_processing_unit_1 { +struct usb_audio_processing_unit_1 { uByte bNrChannels; uWord wChannelConfig; uByte iChannelNames; @@ -228,14 +228,14 @@ struct usb2_audio_processing_unit_1 { #define UA_PROC_ENABLE_MASK 1 } __packed; -struct usb2_audio_processing_unit_updown { +struct usb_audio_processing_unit_updown { uByte iProcessing; uByte bNrModes; uWord waModes[0]; /* [bNrModes] */ } __packed; /* UDESCSUB_AC_EXTENSION */ -struct usb2_audio_extension_unit_0 { +struct usb_audio_extension_unit_0 { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -243,9 +243,9 @@ struct usb2_audio_extension_unit_0 { uWord wExtensionCode; uByte bNrInPins; uByte baSourceId[0]; /* [bNrInPins] */ - /* struct usb2_audio_extension_unit_1 */ + /* struct usb_audio_extension_unit_1 */ } __packed; -struct usb2_audio_extension_unit_1 { +struct usb_audio_extension_unit_1 { uByte bNrChannels; uWord wChannelConfig; uByte iChannelNames; diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c index 03ecc37..28ad987 100644 --- a/sys/dev/usb/controller/ehci.c +++ b/sys/dev/usb/controller/ehci.c @@ -92,15 +92,23 @@ __FBSDID("$FreeBSD$"); #if USB_DEBUG static int ehcidebug = 0; static int ehcinohighspeed = 0; +static int ehciiaadbug = 0; +static int ehcilostintrbug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW, &ehcidebug, 0, "Debug level"); SYSCTL_INT(_hw_usb_ehci, OID_AUTO, no_hs, CTLFLAG_RW, &ehcinohighspeed, 0, "Disable High Speed USB"); +SYSCTL_INT(_hw_usb_ehci, OID_AUTO, iaadbug, CTLFLAG_RW, + &ehciiaadbug, 0, "Enable doorbell bug workaround"); +SYSCTL_INT(_hw_usb_ehci, OID_AUTO, lostintrbug, CTLFLAG_RW, + &ehcilostintrbug, 0, "Enable lost interrupt bug workaround"); TUNABLE_INT("hw.usb.ehci.debug", &ehcidebug); TUNABLE_INT("hw.usb.ehci.no_hs", &ehcinohighspeed); +TUNABLE_INT("hw.usb.ehci.iaadbug", &ehciiaadbug); +TUNABLE_INT("hw.usb.ehci.lostintrbug", &ehcilostintrbug); static void ehci_dump_regs(ehci_softc_t *sc); static void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *sqh); @@ -251,6 +259,10 @@ ehci_init(ehci_softc_t *sc) usb_callout_init_mtx(&sc->sc_tmo_poll, &sc->sc_bus.bus_mtx, 0); #if USB_DEBUG + if (ehciiaadbug) + sc->sc_flags |= EHCI_SCFLG_IAADBUG; + if (ehcilostintrbug) + sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG; if (ehcidebug > 2) { ehci_dump_regs(sc); } @@ -2280,6 +2292,13 @@ ehci_device_bulk_start(struct usb_xfer *xfer) /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); + /* + * XXX Certain nVidia chipsets choke when using the IAAD + * feature too frequently. + */ + if (sc->sc_flags & EHCI_SCFLG_IAADBUG) + return; + /* XXX Performance quirk: Some Host Controllers have a too low * interrupt rate. Issue an IAAD to stimulate the Host * Controller after queueing the BULK transfer. diff --git a/sys/dev/usb/controller/ehci.h b/sys/dev/usb/controller/ehci.h index a3d8406..b6426c4 100644 --- a/sys/dev/usb/controller/ehci.h +++ b/sys/dev/usb/controller/ehci.h @@ -350,6 +350,7 @@ typedef struct ehci_softc { #define EHCI_SCFLG_BIGEMMIO 0x0010 /* big-endian byte order MMIO */ #define EHCI_SCFLG_TT 0x0020 /* transaction translator present */ #define EHCI_SCFLG_LOSTINTRBUG 0x0040 /* workaround for VIA / ATI chipsets */ +#define EHCI_SCFLG_IAADBUG 0x0080 /* workaround for nVidia chipsets */ uint8_t sc_offs; /* offset to operational registers */ uint8_t sc_doorbell_disable; /* set on doorbell failure */ diff --git a/sys/dev/usb/controller/ehci_pci.c b/sys/dev/usb/controller/ehci_pci.c index 17ec65a..c81122b 100644 --- a/sys/dev/usb/controller/ehci_pci.c +++ b/sys/dev/usb/controller/ehci_pci.c @@ -466,6 +466,19 @@ ehci_pci_attach(device_t self) break; } + /* Doorbell feature workaround */ + switch (pci_get_vendor(self)) { + case PCI_EHCI_VENDORID_NVIDIA: + case PCI_EHCI_VENDORID_NVIDIA2: + sc->sc_flags |= EHCI_SCFLG_IAADBUG; + if (bootverbose) + device_printf(self, + "Doorbell workaround enabled\n"); + break; + default: + break; + } + err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index eebd737..0078539 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -72,6 +72,7 @@ options INCLUDE_CONFIG_FILE # Include this file in kernel options KDB # Enable kernel debugger support. options DDB # Support DDB. options GDB # Support remote GDB. +options DEADLKRES # Enable the deadlock resolver options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 79fe305..af3da83 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -552,12 +552,17 @@ hint.mse.0.irq="5" # Intel EtherExpress # ipw: Intel PRO/Wireless 2100 IEEE 802.11 adapter # iwi: Intel PRO/Wireless 2200BG/2225BG/2915ABG IEEE 802.11 adapters +# Requires the iwi firmware module # iwn: Intel Wireless WiFi Link 4965AGN 802.11 network adapters +# Requires the iwn firmware module +# mwl: Marvell 88W8363 IEEE 802.11 adapter +# Requires the mwl firmware module # nfe: nVidia nForce MCP on-board Ethernet Networking (BSD open source) # nve: nVidia nForce MCP on-board Ethernet Networking # sbni: Granch SBNI12-xx ISA and PCI adapters # wl: Lucent Wavelan (ISA card only). # wpi: Intel 3945ABG Wireless LAN controller +# Requires the wpi firmware module # Order for ISA/EISA devices is important here @@ -585,14 +590,15 @@ hint.ie.2.at="isa" hint.ie.2.port="0x300" hint.ie.2.irq="5" hint.ie.2.maddr="0xd0000" +device ipw device iwi device iwn -device ipw # Hint for the i386-only ISA front-end of le(4). hint.le.0.at="isa" hint.le.0.port="0x280" hint.le.0.irq="10" hint.le.0.drq="0" +device mwl device nfe # nVidia nForce MCP on-board Ethernet Networking device nve # nVidia nForce MCP on-board Ethernet Networking device sbni @@ -607,6 +613,45 @@ options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output device wpi +# IEEE 802.11 adapter firmware modules + +# Intel PRO/Wireless 2100 firmware: +# ipwfw: BSS/IBSS/monitor mode firmware +# ipwbssfw: BSS mode firmware +# ipwibssfw: IBSS mode firmware +# ipwmonitorfw: Monitor mode firmware +# Intel PRO/Wireless 2200BG/2225BG/2915ABG firmware: +# iwifw: BSS/IBSS/monitor mode firmware +# iwibssfw: BSS mode firmware +# iwiibssfw: IBSS mode firmware +# iwimonitorfw: Monitor mode firmware +# Intel Wireless WiFi Link 4965/1000/5000/6000 series firmware: +# iwnfw: Single module to support the 4965/1000/5000/5150/6000 +# iwn4965fw: Specific module for the 4965 only +# iwn1000fw: Specific module for the 1000 only +# iwn5000fw: Specific module for the 5000 only +# iwn5150fw: Specific module for the 5150 only +# iwn6000fw: Specific module for the 6000 only +# mwlfw: Marvell 88W8363 firmware +# wpifw: Intel 3945ABG Wireless LAN Controller firmware + +device iwifw +device iwibssfw +device iwiibssfw +device iwimonitorfw +device ipwfw +device ipwbssfw +device ipwibssfw +device ipwmonitorfw +device iwnfw +device iwn4965fw +device iwn1000fw +device iwn5000fw +device iwn5150fw +device iwn6000fw +device mwlfw +device wpifw + # # ATA raid adapters # diff --git a/sys/i386/conf/XEN b/sys/i386/conf/XEN index 6c949663..de704e5 100644 --- a/sys/i386/conf/XEN +++ b/sys/i386/conf/XEN @@ -49,6 +49,7 @@ options AUDIT # Security event auditing options KDB # Enable kernel debugger support. options DDB # Support DDB. options GDB # Support remote GDB. +options DEADLKRES # Enable the deadlock resolver options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles diff --git a/sys/i386/ibcs2/syscalls.master b/sys/i386/ibcs2/syscalls.master index 23d5853..9d0eda6 100644 --- a/sys/i386/ibcs2/syscalls.master +++ b/sys/i386/ibcs2/syscalls.master @@ -22,7 +22,6 @@ ; types: ; STD always included ; COMPAT included on COMPAT #ifdef -; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h ; OBSOL obsolete, not included in system, only specifies name ; UNIMPL not implemented, placeholder only diff --git a/sys/ia64/conf/GENERIC b/sys/ia64/conf/GENERIC index d7b23a08..c1501d9 100644 --- a/sys/ia64/conf/GENERIC +++ b/sys/ia64/conf/GENERIC @@ -31,6 +31,7 @@ options COMPAT_43TTY # BSD 4.3 TTY compat (sgtty) options COMPAT_FREEBSD6 # Compatible with FreeBSD6 options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options DDB # Support DDB +options DEADLKRES # Enable the deadlock resolver options FFS # Berkeley Fast Filesystem options GDB # Support remote GDB options GEOM_LABEL # Provides labelization diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c index 80559ba..14a79a3 100644 --- a/sys/kern/kern_umtx.c +++ b/sys/kern/kern_umtx.c @@ -420,7 +420,7 @@ umtxq_insert_queue(struct umtx_q *uq, int q) uc = umtxq_getchain(&uq->uq_key); UMTXQ_LOCKED_ASSERT(uc); KASSERT((uq->uq_flags & UQF_UMTXQ) == 0, ("umtx_q is already on queue")); - uh = umtxq_queue_lookup(&uq->uq_key, UMTX_SHARED_QUEUE); + uh = umtxq_queue_lookup(&uq->uq_key, q); if (uh != NULL) { LIST_INSERT_HEAD(&uc->uc_spare_queue, uq->uq_spare_queue, link); } else { @@ -2853,6 +2853,8 @@ do_sem_wait(struct thread *td, struct _usem *sem, struct timespec *timeout) umtxq_insert(uq); umtxq_unlock(&uq->uq_key); + suword32(__DEVOLATILE(uint32_t *, &sem->_has_waiters), 1); + count = fuword32(__DEVOLATILE(uint32_t *, &sem->_count)); if (count != 0) { umtxq_lock(&uq->uq_key); @@ -2863,11 +2865,6 @@ do_sem_wait(struct thread *td, struct _usem *sem, struct timespec *timeout) return (0); } - /* - * set waiters byte and sleep. - */ - suword32(__DEVOLATILE(uint32_t *, &sem->_has_waiters), 1); - umtxq_lock(&uq->uq_key); umtxq_unbusy(&uq->uq_key); umtxq_unlock(&uq->uq_key); diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh index 41a89c6..64cdcdf 100644 --- a/sys/kern/makesyscalls.sh +++ b/sys/kern/makesyscalls.sh @@ -497,23 +497,6 @@ s/\$//g syscall++ next } - type("LIBCOMPAT") { - ncompat++ - parseline() - printf("%s\to%s();\n", rettype, funcname) > syscompatdcl - printf("\t{ compat(%s,%s), %s, NULL, 0, 0, %s },", - argssize, funcname, auditev, flags) > sysent - align_sysent_comment(8 + 9 + \ - length(argssize) + 1 + length(funcname) + length(auditev) + length(flags) + 4) - printf("/* %d = old %s */\n", syscall, funcalias) > sysent - printf("\t\"old.%s\",\t\t/* %d = old %s */\n", - funcalias, syscall, funcalias) > sysnames - printf("#define\t%s%s\t%d\t/* compatibility; still used by libc */\n", - syscallprefix, funcalias, syscall) > syshdr - printf(" \\\n\t%s.o", funcalias) > sysmk - syscall++ - next - } type("OBSOL") { printf("\t{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0 },") > sysent align_sysent_comment(34) diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 3c6394c..d4b5d4d 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -73,6 +73,20 @@ struct ptrace_io_desc32 { u_int32_t piod_addr; u_int32_t piod_len; }; + +struct ptrace_vm_entry32 { + int pve_entry; + int pve_timestamp; + uint32_t pve_start; + uint32_t pve_end; + uint32_t pve_offset; + u_int pve_prot; + u_int pve_pathlen; + int32_t pve_fileid; + u_int pve_fsid; + uint32_t pve_path; +}; + #endif /* @@ -346,6 +360,148 @@ proc_rwmem(struct proc *p, struct uio *uio) return (error); } +static int +ptrace_vm_entry(struct thread *td, struct proc *p, struct ptrace_vm_entry *pve) +{ + struct vattr vattr; + vm_map_t map; + vm_map_entry_t entry; + vm_object_t obj, tobj, lobj; + struct vmspace *vm; + struct vnode *vp; + char *freepath, *fullpath; + u_int pathlen; + int error, index, vfslocked; + + error = 0; + obj = NULL; + + vm = vmspace_acquire_ref(p); + map = &vm->vm_map; + vm_map_lock_read(map); + + do { + entry = map->header.next; + index = 0; + while (index < pve->pve_entry && entry != &map->header) { + entry = entry->next; + index++; + } + if (index != pve->pve_entry) { + error = EINVAL; + break; + } + while (entry != &map->header && + (entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0) { + entry = entry->next; + index++; + } + if (entry == &map->header) { + error = ENOENT; + break; + } + + /* We got an entry. */ + pve->pve_entry = index + 1; + pve->pve_timestamp = map->timestamp; + pve->pve_start = entry->start; + pve->pve_end = entry->end - 1; + pve->pve_offset = entry->offset; + pve->pve_prot = entry->protection; + + /* Backing object's path needed? */ + if (pve->pve_pathlen == 0) + break; + + pathlen = pve->pve_pathlen; + pve->pve_pathlen = 0; + + obj = entry->object.vm_object; + if (obj != NULL) + VM_OBJECT_LOCK(obj); + } while (0); + + vm_map_unlock_read(map); + vmspace_free(vm); + + pve->pve_fsid = VNOVAL; + pve->pve_fileid = VNOVAL; + + if (error == 0 && obj != NULL) { + lobj = obj; + for (tobj = obj; tobj != NULL; tobj = tobj->backing_object) { + if (tobj != obj) + VM_OBJECT_LOCK(tobj); + if (lobj != obj) + VM_OBJECT_UNLOCK(lobj); + lobj = tobj; + pve->pve_offset += tobj->backing_object_offset; + } + vp = (lobj->type == OBJT_VNODE) ? lobj->handle : NULL; + if (vp != NULL) + vref(vp); + if (lobj != obj) + VM_OBJECT_UNLOCK(lobj); + VM_OBJECT_UNLOCK(obj); + + if (vp != NULL) { + freepath = NULL; + fullpath = NULL; + vn_fullpath(td, vp, &fullpath, &freepath); + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_SHARED | LK_RETRY); + if (VOP_GETATTR(vp, &vattr, td->td_ucred) == 0) { + pve->pve_fileid = vattr.va_fileid; + pve->pve_fsid = vattr.va_fsid; + } + vput(vp); + VFS_UNLOCK_GIANT(vfslocked); + + if (fullpath != NULL) { + pve->pve_pathlen = strlen(fullpath) + 1; + if (pve->pve_pathlen <= pathlen) { + error = copyout(fullpath, pve->pve_path, + pve->pve_pathlen); + } else + error = ENAMETOOLONG; + } + if (freepath != NULL) + free(freepath, M_TEMP); + } + } + + return (error); +} + +#ifdef COMPAT_IA32 +static int +ptrace_vm_entry32(struct thread *td, struct proc *p, + struct ptrace_vm_entry32 *pve32) +{ + struct ptrace_vm_entry pve; + int error; + + pve.pve_entry = pve32->pve_entry; + pve.pve_pathlen = pve32->pve_pathlen; + pve.pve_path = (void *)(uintptr_t)pve32->pve_path; + + error = ptrace_vm_entry(td, p, &pve); + if (error == 0) { + pve32->pve_entry = pve.pve_entry; + pve32->pve_timestamp = pve.pve_timestamp; + pve32->pve_start = pve.pve_start; + pve32->pve_end = pve.pve_end; + pve32->pve_offset = pve.pve_offset; + pve32->pve_prot = pve.pve_prot; + pve32->pve_fileid = pve.pve_fileid; + pve32->pve_fsid = pve.pve_fsid; + } + + pve32->pve_pathlen = pve.pve_pathlen; + return (error); +} +#endif /* COMPAT_IA32 */ + /* * Process debugging system call. */ @@ -389,6 +545,7 @@ ptrace(struct thread *td, struct ptrace_args *uap) union { struct ptrace_io_desc piod; struct ptrace_lwpinfo pl; + struct ptrace_vm_entry pve; struct dbreg dbreg; struct fpreg fpreg; struct reg reg; @@ -397,6 +554,7 @@ ptrace(struct thread *td, struct ptrace_args *uap) struct fpreg32 fpreg32; struct reg32 reg32; struct ptrace_io_desc32 piod32; + struct ptrace_vm_entry32 pve32; #endif } r; void *addr; @@ -429,6 +587,9 @@ ptrace(struct thread *td, struct ptrace_args *uap) case PT_IO: error = COPYIN(uap->addr, &r.piod, sizeof r.piod); break; + case PT_VM_ENTRY: + error = COPYIN(uap->addr, &r.pve, sizeof r.pve); + break; default: addr = uap->addr; break; @@ -441,6 +602,9 @@ ptrace(struct thread *td, struct ptrace_args *uap) return (error); switch (uap->req) { + case PT_VM_ENTRY: + error = COPYOUT(&r.pve, uap->addr, sizeof r.pve); + break; case PT_IO: error = COPYOUT(&r.piod, uap->addr, sizeof r.piod); break; @@ -977,6 +1141,21 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) PROC_LOCK(p); break; + case PT_VM_TIMESTAMP: + td->td_retval[0] = p->p_vmspace->vm_map.timestamp; + break; + + case PT_VM_ENTRY: + PROC_UNLOCK(p); +#ifdef COMPAT_IA32 + if (wrap32) + error = ptrace_vm_entry32(td, p, addr); + else +#endif + error = ptrace_vm_entry(td, p, addr); + PROC_LOCK(p); + break; + default: #ifdef __HAVE_PTRACE_MACHDEP if (req >= PT_FIRSTMACH) { diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index ac64a75..6a0ea65 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -12,7 +12,7 @@ ; case where the event exists, but we don't want auditing, the ; event should be #defined to AUE_NULL in audit_kevents.h. ; type one of STD, OBSOL, UNIMPL, COMPAT, COMPAT4, COMPAT6, -; COMPAT7, LIBCOMPAT, NODEF, NOARGS, NOPROTO, NOSTD +; COMPAT7, NODEF, NOARGS, NOPROTO, NOSTD ; The COMPAT* options may be combined with one or more NO* ; options separated by '|' with no spaces (e.g. COMPAT|NOARGS) ; name psuedo-prototype of syscall routine @@ -28,7 +28,6 @@ ; COMPAT4 included on COMPAT4 #ifdef (FreeBSD 4 compat) ; COMPAT6 included on COMPAT6 #ifdef (FreeBSD 6 compat) ; COMPAT7 included on COMPAT7 #ifdef (FreeBSD 7 compat) -; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h ; OBSOL obsolete, not included in system, only specifies name ; UNIMPL not implemented, placeholder only ; NOSTD implemented but as a lkm that can be statically diff --git a/sys/mips/conf/ADM5120 b/sys/mips/conf/ADM5120 index fc6b679..b0cf614 100644 --- a/sys/mips/conf/ADM5120 +++ b/sys/mips/conf/ADM5120 @@ -55,6 +55,7 @@ options BOOTP_COMPAT options ROOTDEVNAME=\"nfs:10.0.0.1:/mnt/bsd\" # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/conf/ALCHEMY b/sys/mips/conf/ALCHEMY index b8b6470..ccef37a 100644 --- a/sys/mips/conf/ALCHEMY +++ b/sys/mips/conf/ALCHEMY @@ -55,6 +55,7 @@ options ROOTDEVNAME=\"nfs:10.0.0.1:/mnt/bsd\" # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/conf/AR71XX b/sys/mips/conf/AR71XX index 6760e18..30126bf 100644 --- a/sys/mips/conf/AR71XX +++ b/sys/mips/conf/AR71XX @@ -29,6 +29,7 @@ options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions # options NFS_LEGACYRPC # Debugging for use in -current +# options DEADLKRES # options INVARIANTS # options INVARIANT_SUPPORT # options WITNESS diff --git a/sys/mips/conf/IDT b/sys/mips/conf/IDT index 3082d1c..116c249 100644 --- a/sys/mips/conf/IDT +++ b/sys/mips/conf/IDT @@ -27,6 +27,7 @@ options BOOTP_WIRED_TO=kr0 options BOOTP_COMPAT # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS diff --git a/sys/mips/conf/MALTA b/sys/mips/conf/MALTA index 1d65cfc..06fa2b0 100644 --- a/sys/mips/conf/MALTA +++ b/sys/mips/conf/MALTA @@ -58,6 +58,7 @@ options ROOTDEVNAME=\"ufs:ad0s1a\" # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/conf/MALTA64 b/sys/mips/conf/MALTA64 index 756dc76..4b11eb0 100644 --- a/sys/mips/conf/MALTA64 +++ b/sys/mips/conf/MALTA64 @@ -59,6 +59,7 @@ options ROOTDEVNAME=\"ufs:ad0s1a\" # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/conf/OCTEON1 b/sys/mips/conf/OCTEON1 index f313347..6dfdc7c 100644 --- a/sys/mips/conf/OCTEON1 +++ b/sys/mips/conf/OCTEON1 @@ -67,6 +67,7 @@ options UFS_DIRHASH #Improve performance on big directories # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/conf/OCTEON1-32 b/sys/mips/conf/OCTEON1-32 index 8556403..2197d82 100644 --- a/sys/mips/conf/OCTEON1-32 +++ b/sys/mips/conf/OCTEON1-32 @@ -56,6 +56,7 @@ options UFS_DIRHASH #Improve performance on big directories # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/conf/QEMU b/sys/mips/conf/QEMU index 5042848..3088524 100644 --- a/sys/mips/conf/QEMU +++ b/sys/mips/conf/QEMU @@ -43,6 +43,7 @@ options PSEUDOFS #Pseudo-filesystem framework options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions # Debugging for use in -current +#options DEADLKRES #Enable the deadlock resolver #options INVARIANTS #Enable calls of extra sanity checking #options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/conf/SENTRY5 b/sys/mips/conf/SENTRY5 index c035350..8579c4c 100644 --- a/sys/mips/conf/SENTRY5 +++ b/sys/mips/conf/SENTRY5 @@ -56,6 +56,7 @@ options PSEUDOFS #Pseudo-filesystem framework options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions # Debugging for use in -current +#options DEADLKRES options INVARIANTS options INVARIANT_SUPPORT diff --git a/sys/mips/conf/SWARM b/sys/mips/conf/SWARM index ff855e5..2c55533 100644 --- a/sys/mips/conf/SWARM +++ b/sys/mips/conf/SWARM @@ -8,9 +8,6 @@ options CPU_SB1 files "../sibyte/files.sibyte" hints "SWARM.hints" -options PCI_IOSPACE_ADDR=0xFC000000 -options PCI_IOSPACE_SIZE=0x02000000 - # # 32-bit kernel cannot deal with physical memory beyond 4GB # XXX pmap assumes that all the memory can be mapped using KSEG0 @@ -50,6 +47,7 @@ options PSEUDOFS #Pseudo-filesystem framework options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions # Debugging for use in -current +#options DEADLKRES options INVARIANTS options INVARIANT_SUPPORT options WITNESS diff --git a/sys/mips/conf/SWARM_SMP b/sys/mips/conf/SWARM_SMP new file mode 100644 index 0000000..ec76ce4 --- /dev/null +++ b/sys/mips/conf/SWARM_SMP @@ -0,0 +1,7 @@ +# +# $FreeBSD$ +# +options SMP +options PRINTF_BUFR_SIZE=128 + +include SWARM diff --git a/sys/mips/conf/XLR b/sys/mips/conf/XLR index 5450db1..d7c54bd 100644 --- a/sys/mips/conf/XLR +++ b/sys/mips/conf/XLR @@ -96,6 +96,7 @@ options DDB options KDB options GDB options ALT_BREAK_TO_DEBUGGER +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/mips/include/asm.h b/sys/mips/include/asm.h index db6929d..3dd9aa4 100644 --- a/sys/mips/include/asm.h +++ b/sys/mips/include/asm.h @@ -497,17 +497,8 @@ _C_LABEL(x): #define DYNAMIC_STATUS_MASK_TOUSER(sr,scratch1) #endif -#ifdef SMP - /* - * FREEBSD_DEVELOPERS_FIXME - * In multiprocessor case, store/retrieve the pcpu structure - * address for current CPU in scratch register for fast access. - */ -#error "Write GET_CPU_PCPU for SMP" -#else #define GET_CPU_PCPU(reg) \ lw reg, _C_LABEL(pcpup); -#endif /* * Description of the setjmp buffer diff --git a/sys/mips/include/cpu.h b/sys/mips/include/cpu.h index c802bee..5a1cb9d 100644 --- a/sys/mips/include/cpu.h +++ b/sys/mips/include/cpu.h @@ -122,6 +122,8 @@ #define SOFT_INT_MASK (SOFT_INT_MASK_0 | SOFT_INT_MASK_1) #define HW_INT_MASK (ALL_INT_MASK & ~SOFT_INT_MASK) +#define soft_int_mask(softintr) (1 << ((softintr) + 8)) +#define hard_int_mask(hardintr) (1 << ((hardintr) + 10)) /* * The bits in the cause register. diff --git a/sys/mips/include/hwfunc.h b/sys/mips/include/hwfunc.h index 16b1439..bbf3086 100644 --- a/sys/mips/include/hwfunc.h +++ b/sys/mips/include/hwfunc.h @@ -47,4 +47,51 @@ unsigned platform_get_timecount(struct timecounter *); /* For hardware specific CPU initialization */ void platform_cpu_init(void); void platform_secondary_init(void); + +#ifdef SMP + +/* + * Spin up the AP so that it starts executing MP bootstrap entry point: mpentry + * + * Returns 0 on sucess and non-zero on failure. + */ +int platform_start_ap(int processor_id); + +/* + * Platform-specific initialization that needs to be done when an AP starts + * running. This function is called from the MP bootstrap code in mpboot.S + */ +void platform_init_ap(int processor_id); + +/* + * Return a plaform-specific interrrupt number that is used to deliver IPIs. + * + * This hardware interrupt is used to deliver IPIs exclusively and must + * not be used for any other interrupt source. + */ +int platform_ipi_intrnum(void); + +/* + * Trigger a IPI interrupt on 'cpuid'. + */ +void platform_ipi_send(int cpuid); + +/* + * Quiesce the IPI interrupt source on the current cpu. + */ +void platform_ipi_clear(void); + +/* + * Return the processor id. + * + * Note that this function is called in early boot when stack is not available. + */ +extern int platform_processor_id(void); + +/* + * Return the number of processors available on this platform. + */ +extern int platform_num_processors(void); + +#endif /* SMP */ #endif /* !_MACHINE_HWFUNC_H_ */ diff --git a/sys/mips/include/intr_machdep.h b/sys/mips/include/intr_machdep.h index d72828e..60e969d 100644 --- a/sys/mips/include/intr_machdep.h +++ b/sys/mips/include/intr_machdep.h @@ -60,6 +60,16 @@ void cpu_establish_softintr(const char *, driver_filter_t *, void (*)(void*), void cpu_intr(struct trapframe *); /* + * Allow a platform to override the default hard interrupt mask and unmask + * functions. The 'arg' can be cast safely to an 'int' and holds the mips + * hard interrupt number to mask or unmask. + */ +typedef void (*cpu_intr_mask_t)(void *arg); +typedef void (*cpu_intr_unmask_t)(void *arg); +void cpu_set_hardintr_mask_func(cpu_intr_mask_t func); +void cpu_set_hardintr_unmask_func(cpu_intr_unmask_t func); + +/* * Opaque datatype that represents intr counter */ typedef unsigned long* mips_intrcnt_t; diff --git a/sys/mips/include/pcpu.h b/sys/mips/include/pcpu.h index 3eb552d..b8928a1 100644 --- a/sys/mips/include/pcpu.h +++ b/sys/mips/include/pcpu.h @@ -55,6 +55,13 @@ extern struct pcpu *pcpup; #define PCPU_SET(member,value) (PCPUP->pc_ ## member = (value)) #define PCPU_LAZY_INC(member) (++PCPUP->pc_ ## member) +#ifdef SMP +/* + * Instantiate the wired TLB entry at PCPU_TLB_ENTRY to map 'pcpu' at 'pcpup'. + */ +void mips_pcpu_tlb_init(struct pcpu *pcpu); +#endif + #endif /* _KERNEL */ #endif /* !_MACHINE_PCPU_H_ */ diff --git a/sys/mips/include/smp.h b/sys/mips/include/smp.h index d614dd3..346c863 100644 --- a/sys/mips/include/smp.h +++ b/sys/mips/include/smp.h @@ -20,7 +20,6 @@ /* * Interprocessor interrupts for SMP. */ -#define IPI_INVLTLB 0x0001 #define IPI_RENDEZVOUS 0x0002 #define IPI_AST 0x0004 #define IPI_STOP 0x0008 @@ -28,13 +27,9 @@ #ifndef LOCORE -extern u_int32_t boot_cpu_id; - -void ipi_selected(u_int cpus, u_int32_t ipi); -void ipi_all_but_self(u_int32_t ipi); -intrmask_t smp_handle_ipi(struct trapframe *frame); +void ipi_selected(cpumask_t cpus, int ipi); void smp_init_secondary(u_int32_t cpuid); -void mips_ipi_send(int thread_id); +void mpentry(void); #endif /* !LOCORE */ #endif /* _KERNEL */ diff --git a/sys/mips/mips/exception.S b/sys/mips/mips/exception.S index 1d511ee..262701e 100644 --- a/sys/mips/mips/exception.S +++ b/sys/mips/mips/exception.S @@ -728,6 +728,18 @@ NNON_LEAF(MipsUserIntr, STAND_FRAME_SIZE, ra) sw a3, STAND_RA_OFFSET(sp) # for debugging /* + * Enable interrupts before doing ast(). + * + * On SMP kernels the AST processing might trigger IPI to other processors. + * If that processor is also doing AST processing with interrupts disabled + * then we may deadlock. + */ + mfc0 a0, COP_0_STATUS_REG + or a0, a0, SR_INT_ENAB + mtc0 a0, COP_0_STATUS_REG + ITLBNOPFIX + +/* * DO_AST enabled interrupts */ DO_AST diff --git a/sys/mips/mips/intr_machdep.c b/sys/mips/mips/intr_machdep.c index 530cc08..2dc302a 100644 --- a/sys/mips/mips/intr_machdep.c +++ b/sys/mips/mips/intr_machdep.c @@ -50,6 +50,9 @@ static mips_intrcnt_t mips_intr_counters[NSOFT_IRQS + NHARD_IRQS]; static int intrcnt_index; +static cpu_intr_mask_t hardintr_mask_func; +static cpu_intr_unmask_t hardintr_unmask_func; + mips_intrcnt_t mips_intrcnt_create(const char* name) { @@ -128,38 +131,54 @@ cpu_init_interrupts() } void +cpu_set_hardintr_mask_func(cpu_intr_mask_t func) +{ + + hardintr_mask_func = func; +} + +void +cpu_set_hardintr_unmask_func(cpu_intr_unmask_t func) +{ + + hardintr_unmask_func = func; +} + +void cpu_establish_hardintr(const char *name, driver_filter_t *filt, void (*handler)(void*), void *arg, int irq, int flags, void **cookiep) { struct intr_event *event; int error; -#if 0 - printf("Establish HARD IRQ %d: filt %p handler %p arg %p\n", - irq, filt, handler, arg); -#endif /* * We have 6 levels, but thats 0 - 5 (not including 6) */ if (irq < 0 || irq >= NHARD_IRQS) panic("%s called for unknown hard intr %d", __func__, irq); + if (hardintr_mask_func == NULL) + hardintr_mask_func = mips_mask_hard_irq; + + if (hardintr_unmask_func == NULL) + hardintr_unmask_func = mips_unmask_hard_irq; + event = hardintr_events[irq]; if (event == NULL) { error = intr_event_create(&event, (void *)(uintptr_t)irq, 0, - irq, mips_mask_hard_irq, mips_unmask_hard_irq, + irq, hardintr_mask_func, hardintr_unmask_func, NULL, NULL, "int%d", irq); if (error) return; hardintr_events[irq] = event; + mips_unmask_hard_irq((void*)(uintptr_t)irq); } intr_event_add_handler(event, name, filt, handler, arg, intr_priority(flags), flags, cookiep); - mips_intrcnt_setname(mips_intr_counters[NSOFT_IRQS + irq], event->ie_fullname); - - mips_unmask_hard_irq((void*)(uintptr_t)irq); + mips_intrcnt_setname(mips_intr_counters[NSOFT_IRQS + irq], + event->ie_fullname); } void @@ -185,14 +204,13 @@ cpu_establish_softintr(const char *name, driver_filter_t *filt, if (error) return; softintr_events[irq] = event; + mips_unmask_soft_irq((void*)(uintptr_t)irq); } intr_event_add_handler(event, name, filt, handler, arg, intr_priority(flags), flags, cookiep); mips_intrcnt_setname(mips_intr_counters[irq], event->ie_fullname); - - mips_unmask_soft_irq((void*)(uintptr_t)irq); } void diff --git a/sys/mips/mips/machdep.c b/sys/mips/mips/machdep.c index ba56057..ad1dab5 100644 --- a/sys/mips/mips/machdep.c +++ b/sys/mips/mips/machdep.c @@ -133,11 +133,7 @@ vm_offset_t kstack0; char pcpu_space[MAXCPU][PAGE_SIZE * 2] \ __aligned(PAGE_SIZE * 2) __section(".data"); -#ifdef SMP -struct pcpu *pcpup = 0; /* initialized in pmap_bootstrap() */ -#else struct pcpu *pcpup = (struct pcpu *)pcpu_space; -#endif vm_offset_t phys_avail[PHYS_AVAIL_ENTRIES + 2]; vm_offset_t physmem_desc[PHYS_AVAIL_ENTRIES + 2]; @@ -419,22 +415,14 @@ mips_generic_reset() ((void(*)(void))(intptr_t)MIPS_VEC_RESET)(); } -/* - * Initialise a struct pcpu. - */ +#ifdef SMP void -cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) +mips_pcpu_tlb_init(struct pcpu *pcpu) { -#ifdef SMP vm_paddr_t pa; struct tlb tlb; int lobits; -#endif - - pcpu->pc_next_asid = 1; - pcpu->pc_asid_generation = 1; -#ifdef SMP /* * Map the pcpu structure at the virtual address 'pcpup'. * We use a wired tlb index to do this one-time mapping. @@ -446,6 +434,21 @@ cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) tlb.tlb_lo0 = mips_paddr_to_tlbpfn(pa) | lobits; tlb.tlb_lo1 = mips_paddr_to_tlbpfn(pa + PAGE_SIZE) | lobits; Mips_TLBWriteIndexed(PCPU_TLB_ENTRY, &tlb); +} +#endif + +/* + * Initialise a struct pcpu. + */ +void +cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t size) +{ + + pcpu->pc_next_asid = 1; + pcpu->pc_asid_generation = 1; +#ifdef SMP + if ((vm_offset_t)pcpup >= VM_MIN_KERNEL_ADDRESS) + mips_pcpu_tlb_init(pcpu); #endif } diff --git a/sys/mips/mips/mp_machdep.c b/sys/mips/mips/mp_machdep.c index bf32392..d8520cc 100644 --- a/sys/mips/mips/mp_machdep.c +++ b/sys/mips/mips/mp_machdep.c @@ -1,124 +1,110 @@ +/*- + * Copyright (c) 2009 Neelkanth Natu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include "opt_kstack_pages.h" - #include <sys/param.h> #include <sys/systm.h> #include <sys/ktr.h> #include <sys/proc.h> -#include <sys/cons.h> #include <sys/lock.h> -#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/kernel.h> #include <sys/pcpu.h> #include <sys/smp.h> -#include <sys/sysctl.h> +#include <sys/sched.h> #include <sys/bus.h> #include <vm/vm.h> #include <vm/pmap.h> -#include <vm/vm_map.h> +#include <vm/vm_extern.h> +#include <vm/vm_kern.h> -#include <machine/atomic.h> #include <machine/clock.h> -#include <machine/md_var.h> -#include <machine/pcb.h> -#include <machine/pmap.h> #include <machine/smp.h> +#include <machine/hwfunc.h> +#include <machine/intr_machdep.h> +#include <machine/cache.h> +static void *dpcpu; static struct mtx ap_boot_mtx; -extern struct pcpu __pcpu[]; -extern int num_tlbentries; -void mips_start_timer(void); -static volatile int aps_ready = 0; - -u_int32_t boot_cpu_id; +static volatile int aps_ready; +static volatile int mp_naps; -void -cpu_mp_announce(void) +static void +ipi_send(struct pcpu *pc, int ipi) { -} -/* - * To implement IPIs on MIPS CPU, we use the Interrupt Line 2 ( bit 4 of cause - * register) and a bitmap to avoid redundant IPI interrupts. To interrupt a - * set of CPUs, the sender routine runs in a ' loop ' sending interrupts to - * all the specified CPUs. A single Mutex (smp_ipi_mtx) is used for all IPIs - * that spinwait for delivery. This includes the following IPIs - * IPI_RENDEZVOUS - * IPI_INVLPG - * IPI_INVLTLB - * IPI_INVLRNG - */ - -/* - * send an IPI to a set of cpus. - */ -void -ipi_selected(u_int32_t cpus, u_int ipi) -{ - struct pcpu *pcpu; - u_int cpuid, new_pending, old_pending; + CTR3(KTR_SMP, "%s: cpu=%d, ipi=%x", __func__, pc->pc_cpuid, ipi); - CTR3(KTR_SMP, "%s: cpus: %x, ipi: %x\n", __func__, cpus, ipi); + atomic_set_32(&pc->pc_pending_ipis, ipi); + platform_ipi_send(pc->pc_cpuid); - while ((cpuid = ffs(cpus)) != 0) { - cpuid--; - cpus &= ~(1 << cpuid); - pcpu = pcpu_find(cpuid); - - if (pcpu) { - do { - old_pending = pcpu->pc_pending_ipis; - new_pending = old_pending | ipi; - } while (!atomic_cmpset_int(&pcpu->pc_pending_ipis, - old_pending, new_pending)); - - if (old_pending) - continue; - - mips_ipi_send (cpuid); - } - } + CTR1(KTR_SMP, "%s: sent", __func__); } -/* - * send an IPI to all CPUs EXCEPT myself - */ +/* Send an IPI to a set of cpus. */ void -ipi_all_but_self(u_int ipi) +ipi_selected(cpumask_t cpus, int ipi) { + struct pcpu *pc; - ipi_selected(PCPU_GET(other_cpus), ipi); + CTR3(KTR_SMP, "%s: cpus: %x, ipi: %x\n", __func__, cpus, ipi); + + SLIST_FOREACH(pc, &cpuhead, pc_allcpu) { + if ((cpus & pc->pc_cpumask) != 0) + ipi_send(pc, ipi); + } } /* * Handle an IPI sent to this processor. */ -intrmask_t -smp_handle_ipi(struct trapframe *frame) +static int +mips_ipi_handler(void *arg) { - cpumask_t cpumask; /* This cpu mask */ + cpumask_t cpumask; u_int ipi, ipi_bitmap; + int bit; + + platform_ipi_clear(); /* quiesce the pending ipi interrupt */ ipi_bitmap = atomic_readandclear_int(PCPU_PTR(pending_ipis)); - cpumask = PCPU_GET(cpumask); + if (ipi_bitmap == 0) + return (FILTER_STRAY); CTR1(KTR_SMP, "smp_handle_ipi(), ipi_bitmap=%x", ipi_bitmap); - while (ipi_bitmap) { - /* - * Find the lowest set bit. - */ - ipi = ipi_bitmap & ~(ipi_bitmap - 1); + + while ((bit = ffs(ipi_bitmap))) { + bit = bit - 1; + ipi = 1 << bit; ipi_bitmap &= ~ipi; switch (ipi) { - case IPI_INVLTLB: - CTR0(KTR_SMP, "IPI_INVLTLB"); - break; - case IPI_RENDEZVOUS: CTR0(KTR_SMP, "IPI_RENDEZVOUS"); smp_rendezvous_action(); @@ -129,51 +115,136 @@ smp_handle_ipi(struct trapframe *frame) break; case IPI_STOP: - /* * IPI_STOP_HARD is mapped to IPI_STOP so it is not * necessary to add it in the switch. */ CTR0(KTR_SMP, "IPI_STOP or IPI_STOP_HARD"); + cpumask = PCPU_GET(cpumask); atomic_set_int(&stopped_cpus, cpumask); - while ((started_cpus & cpumask) == 0) - ; + cpu_spinwait(); atomic_clear_int(&started_cpus, cpumask); atomic_clear_int(&stopped_cpus, cpumask); + CTR0(KTR_SMP, "IPI_STOP (restart)"); break; + default: + panic("Unknown IPI 0x%0x on cpu %d", ipi, curcpu); } } - return CR_INT_IPI; + + return (FILTER_HANDLED); +} + +static int +start_ap(int cpuid) +{ + int cpus, ms; + + cpus = mp_naps; + dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE); + + if (platform_start_ap(cpuid) != 0) + return (-1); /* could not start AP */ + + for (ms = 0; ms < 5000; ++ms) { + if (mp_naps > cpus) + return (0); /* success */ + else + DELAY(1000); + } + + return (-2); /* timeout initializing AP */ } void cpu_mp_setmaxid(void) { - mp_maxid = MAXCPU - 1; + mp_ncpus = platform_num_processors(); + if (mp_ncpus <= 0) + mp_ncpus = 1; + + mp_maxid = min(mp_ncpus, MAXCPU) - 1; +} + +void +cpu_mp_announce(void) +{ + /* NOTHING */ +} + +struct cpu_group * +cpu_topo(void) +{ + + return (smp_topo_none()); +} + +int +cpu_mp_probe(void) +{ + + return (mp_ncpus > 1); +} + +void +cpu_mp_start(void) +{ + int error, cpuid; + + mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); + + all_cpus = 1; /* BSP */ + for (cpuid = 1; cpuid < platform_num_processors(); ++cpuid) { + if (cpuid >= MAXCPU) { + printf("cpu_mp_start: ignoring AP #%d.\n", cpuid); + continue; + } + + if ((error = start_ap(cpuid)) != 0) { + printf("AP #%d failed to start: %d\n", cpuid, error); + continue; + } + + if (bootverbose) + printf("AP #%d started!\n", cpuid); + + all_cpus |= 1 << cpuid; + } + + PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask)); } void smp_init_secondary(u_int32_t cpuid) { + int ipi_int_mask, clock_int_mask; - if (cpuid >= MAXCPU) - panic ("cpu id exceeds MAXCPU\n"); + /* TLB */ + Mips_SetWIRED(0); + Mips_TLBFlush(num_tlbentries); + Mips_SetWIRED(VMWIRED_ENTRIES); + + /* + * We assume that the L1 cache on the APs is identical to the one + * on the BSP. + */ + mips_dcache_wbinv_all(); + mips_icache_sync_all(); - /* tlb init */ - R4K_SetWIRED(0); - R4K_TLBFlush(num_tlbentries); - R4K_SetWIRED(VMWIRED_ENTRIES); MachSetPID(0); - Mips_SyncCache(); + pcpu_init(PCPU_ADDR(cpuid), cpuid, sizeof(struct pcpu)); + dpcpu_init(dpcpu, cpuid); + + /* The AP has initialized successfully - allow the BSP to proceed */ + ++mp_naps; - mips_cp0_status_write(0); + /* Spin until the BSP is ready to release the APs */ while (!aps_ready) ; - mips_sync(); mips_sync(); /* Initialize curthread. */ KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread")); PCPU_SET(curthread, PCPU_GET(idlethread)); @@ -182,15 +253,16 @@ smp_init_secondary(u_int32_t cpuid) smp_cpus++; - CTR1(KTR_SMP, "SMP: AP CPU #%d Launched", PCPU_GET(cpuid)); + CTR1(KTR_SMP, "SMP: AP CPU #%d launched", PCPU_GET(cpuid)); /* Build our map of 'other' CPUs. */ PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask)); - printf("SMP: AP CPU #%d Launched!\n", PCPU_GET(cpuid)); + if (bootverbose) + printf("SMP: AP CPU #%d launched.\n", PCPU_GET(cpuid)); if (smp_cpus == mp_ncpus) { - smp_started = 1; + atomic_store_rel_int(&smp_started, 1); smp_active = 1; } @@ -198,103 +270,46 @@ smp_init_secondary(u_int32_t cpuid) while (smp_started == 0) ; /* nothing */ - /* Enable Interrupt */ - mips_cp0_status_write(SR_INT_ENAB); - /* ok, now grab sched_lock and enter the scheduler */ - mtx_lock_spin(&sched_lock); /* - * Correct spinlock nesting. The idle thread context that we are - * borrowing was created so that it would start out with a single - * spin lock (sched_lock) held in fork_trampoline(). Since we've - * explicitly acquired locks in this function, the nesting count - * is now 2 rather than 1. Since we are nested, calling - * spinlock_exit() will simply adjust the counts without allowing - * spin lock using code to interrupt us. + * Unmask the clock and ipi interrupts. */ - spinlock_exit(); - KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count")); - - binuptime(PCPU_PTR(switchtime)); - PCPU_SET(switchticks, ticks); - - /* kick off the clock on this cpu */ - mips_start_timer(); - cpu_throw(NULL, choosethread()); /* doesn't return */ - - panic("scheduler returned us to %s", __func__); -} - -static int -smp_start_secondary(int cpuid) -{ - struct pcpu *pcpu; - void *dpcpu; - int i; - - if (bootverbose) - printf("smp_start_secondary: starting cpu %d\n", cpuid); + clock_int_mask = hard_int_mask(5); + ipi_int_mask = hard_int_mask(platform_ipi_intrnum()); + set_intr_mask(ALL_INT_MASK & ~(ipi_int_mask | clock_int_mask)); - dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE); - pcpu_init(&__pcpu[cpuid], cpuid, sizeof(struct pcpu)); - dpcpu_init(dpcpu, cpuid); - - if (bootverbose) - printf("smp_start_secondary: cpu %d started\n", cpuid); - - return 1; -} - -int -cpu_mp_probe(void) -{ - int i, cpus; + /* + * Bootstrap the compare register. + */ + mips_wr_compare(mips_rd_count() + counter_freq / hz); - /* XXX: Need to check for valid platforms here. */ + enableintr(); - boot_cpu_id = PCPU_GET(cpuid); - KASSERT(boot_cpu_id == 0, ("cpu_mp_probe() called on non-primary CPU")); - all_cpus = PCPU_GET(cpumask); - mp_ncpus = 1; + /* enter the scheduler */ + sched_throw(NULL); - /* Make sure we have at least one secondary CPU. */ - cpus = 0; - for (i = 0; i < MAXCPU; i++) { - cpus++; - } - return (cpus); + panic("scheduler returned us to %s", __func__); + /* NOTREACHED */ } -void -cpu_mp_start(void) +static void +release_aps(void *dummy __unused) { - int i, cpuid; - - mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); + int ipi_irq; - cpuid = 1; - for (i = 0; i < MAXCPU; i++) { + if (mp_ncpus == 1) + return; - if (i == boot_cpu_id) - continue; - if (smp_start_secondary(i)) { - all_cpus |= (1 << cpuid); - mp_ncpus++; - cpuid++; - } - } - idle_mask |= CR_INT_IPI; - PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask)); -} + /* + * IPI handler + */ + ipi_irq = platform_ipi_intrnum(); + cpu_establish_hardintr("ipi", mips_ipi_handler, NULL, NULL, ipi_irq, + INTR_TYPE_MISC | INTR_EXCL | INTR_FAST, NULL); -static void -release_aps(void *dummy __unused) -{ - if (bootverbose && mp_ncpus > 1) - printf("%s: releasing secondary CPUs\n", __func__); atomic_store_rel_int(&aps_ready, 1); - while (mp_ncpus > 1 && smp_started == 0) + while (smp_started == 0) ; /* nothing */ } diff --git a/sys/mips/mips/mpboot.S b/sys/mips/mips/mpboot.S new file mode 100644 index 0000000..6828847 --- /dev/null +++ b/sys/mips/mips/mpboot.S @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2010 Neelkanth Natu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <machine/asm.h> +#include <machine/cpu.h> +#include <machine/cpuregs.h> + +#include "assym.s" + + .text + .set noat + .set noreorder + +GLOBAL(mpentry) + mtc0 zero, COP_0_STATUS_REG /* disable interrupts */ + + mtc0 zero, COP_0_CAUSE_REG /* clear soft interrupts */ + + li t0, CFG_K0_CACHED /* make sure kseg0 is cached */ + mtc0 t0, MIPS_COP_0_CONFIG + COP0_SYNC + + jal platform_processor_id /* get the processor number */ + nop + move s0, v0 + + /* + * Initialize stack and call machine startup + */ + PTR_LA sp, _C_LABEL(pcpu_space) + addiu sp, (NBPG * 2) - START_FRAME + sll t0, s0, PAGE_SHIFT + 1 + addu sp, sp, t0 + + /* Zero out old ra and old fp for debugger */ + sw zero, START_FRAME - 4(sp) + sw zero, START_FRAME - 8(sp) + + PTR_LA gp, _C_LABEL(_gp) + + jal platform_init_ap + move a0, s0 + + jal smp_init_secondary + move a0, s0 + + PANIC("AP startup failed!") diff --git a/sys/mips/mips/nexus.c b/sys/mips/mips/nexus.c index a325f3e..7d3f190 100644 --- a/sys/mips/mips/nexus.c +++ b/sys/mips/mips/nexus.c @@ -77,7 +77,6 @@ struct nexus_device { static struct rman irq_rman; static struct rman mem_rman; -static struct rman port_rman; static struct resource * nexus_alloc_resource(device_t, device_t, int, int *, u_long, @@ -161,21 +160,6 @@ nexus_probe(device_t dev) panic("%s: mem_rman", __func__); } - /* - * MIPS has no concept of the x86 I/O address space but some cpus - * provide a memory mapped window to access the PCI I/O BARs. - */ - port_rman.rm_start = 0; -#ifdef PCI_IOSPACE_SIZE - port_rman.rm_end = PCI_IOSPACE_SIZE - 1; -#endif - port_rman.rm_type = RMAN_ARRAY; - port_rman.rm_descr = "I/O ports"; - if (rman_init(&port_rman) != 0 || - rman_manage_region(&port_rman, 0, port_rman.rm_end) != 0) - panic("%s: port_rman", __func__); - - return (0); } @@ -241,7 +225,6 @@ nexus_print_all_resources(device_t dev) retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); - retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); return (retval); } @@ -368,9 +351,6 @@ nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, case SYS_RES_MEMORY: rm = &mem_rman; break; - case SYS_RES_IOPORT: - rm = &port_rman; - break; default: printf("%s: unknown resource type %d\n", __func__, type); return (0); @@ -400,21 +380,17 @@ static int nexus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { + void *vaddr; + u_int32_t paddr, psize; + /* * If this is a memory resource, track the direct mapping * in the uncached MIPS KSEG1 segment. */ - /* XXX we shouldn't be supporting sys_res_ioport here */ - if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { - caddr_t vaddr = 0; - u_int32_t paddr; - u_int32_t psize; - u_int32_t poffs; - + if (type == SYS_RES_MEMORY) { paddr = rman_get_start(r); psize = rman_get_size(r); - poffs = paddr - trunc_page(paddr); - vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; + vaddr = pmap_mapdev(paddr, psize); rman_set_virtual(r, vaddr); rman_set_bustag(r, mips_bus_space_generic); @@ -501,7 +477,7 @@ nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, { vm_offset_t va; - if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { + if (type == SYS_RES_MEMORY) { va = (vm_offset_t)rman_get_virtual(r); pmap_unmapdev(va, rman_get_size(r)); } diff --git a/sys/mips/mips/pmap.c b/sys/mips/mips/pmap.c index 6fc0fe2..4a267dc 100644 --- a/sys/mips/mips/pmap.c +++ b/sys/mips/mips/pmap.c @@ -362,6 +362,15 @@ again: virtual_avail = roundup2(virtual_avail, PAGE_SIZE * 2); pcpup = (struct pcpu *)virtual_avail; virtual_avail += PAGE_SIZE * 2; + + /* + * Initialize the wired TLB entry mapping the pcpu region for + * the BSP at 'pcpup'. Up until this point we were operating + * with the 'pcpup' for the BSP pointing to a virtual address + * in KSEG0 so there was no need for a TLB mapping. + */ + mips_pcpu_tlb_init(PCPU_ADDR(0)); + if (bootverbose) printf("pcpu is available at virtual address %p.\n", pcpup); #endif diff --git a/sys/mips/mips/tick.c b/sys/mips/mips/tick.c index b374cae..bf147c5 100644 --- a/sys/mips/mips/tick.c +++ b/sys/mips/mips/tick.c @@ -47,21 +47,19 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/timetc.h> +#include <machine/hwfunc.h> #include <machine/clock.h> #include <machine/locore.h> #include <machine/md_var.h> uint64_t counter_freq; -uint64_t cycles_per_tick; -uint64_t cycles_per_usec; -uint64_t cycles_per_sec; -uint64_t cycles_per_hz; -u_int32_t counter_upper = 0; -u_int32_t counter_lower_last = 0; -int tick_started = 0; +static uint64_t cycles_per_tick; +static uint64_t cycles_per_usec; +static uint64_t cycles_per_hz, cycles_per_stathz, cycles_per_profhz; -void platform_initclocks(void); +static u_int32_t counter_upper = 0; +static u_int32_t counter_lower_last = 0; struct clk_ticks { @@ -103,10 +101,8 @@ mips_timer_early_init(uint64_t clock_hz) void platform_initclocks(void) { - if (!tick_started) { - tc_init(&counter_timecounter); - tick_started++; - } + + tc_init(&counter_timecounter); } static uint64_t @@ -136,6 +132,9 @@ void mips_timer_init_params(uint64_t platform_counter_freq, int double_count) { + stathz = hz; + profhz = hz; + /* * XXX: Do not use printf here: uart code 8250 may use DELAY so this * function should be called before cninit. @@ -151,18 +150,20 @@ mips_timer_init_params(uint64_t platform_counter_freq, int double_count) cycles_per_tick = counter_freq / 1000; cycles_per_hz = counter_freq / hz; + cycles_per_stathz = counter_freq / stathz; + cycles_per_profhz = counter_freq / profhz; cycles_per_usec = counter_freq / (1 * 1000 * 1000); - cycles_per_sec = counter_freq ; counter_timecounter.tc_frequency = counter_freq; - printf("hz=%d cyl_per_hz:%jd cyl_per_usec:%jd freq:%jd cyl_per_hz:%jd cyl_per_sec:%jd\n", + printf("hz=%d cyl_per_tick:%jd cyl_per_usec:%jd freq:%jd " + "cyl_per_hz:%jd cyl_per_stathz:%jd cyl_per_profhz:%jd\n", hz, cycles_per_tick, cycles_per_usec, counter_freq, cycles_per_hz, - cycles_per_sec - ); + cycles_per_stathz, + cycles_per_profhz); set_cputicker(tick_ticker, counter_freq, 1); } @@ -280,17 +281,18 @@ clock_intr(void *arg) else hardclock_cpu(USERMODE(tf->sr)); } + /* Fire statclock at stathz. */ - cpu_ticks->stat_ticks += stathz; - if (cpu_ticks->stat_ticks >= cycles_per_hz) { - cpu_ticks->stat_ticks -= cycles_per_hz; + cpu_ticks->stat_ticks += cycles_per_tick; + if (cpu_ticks->stat_ticks >= cycles_per_stathz) { + cpu_ticks->stat_ticks -= cycles_per_stathz; statclock(USERMODE(tf->sr)); } /* Fire profclock at profhz, but only when needed. */ - cpu_ticks->prof_ticks += profhz; - if (cpu_ticks->prof_ticks >= cycles_per_hz) { - cpu_ticks->prof_ticks -= cycles_per_hz; + cpu_ticks->prof_ticks += cycles_per_tick; + if (cpu_ticks->prof_ticks >= cycles_per_profhz) { + cpu_ticks->prof_ticks -= cycles_per_profhz; if (profprocs != 0) profclock(USERMODE(tf->sr), tf->pc); } @@ -298,7 +300,7 @@ clock_intr(void *arg) #if 0 /* TARGET_OCTEON */ /* Run the FreeBSD display once every hz ticks */ wheel_run += cycles_per_tick; - if (wheel_run >= cycles_per_sec) { + if (wheel_run >= cycles_per_usec * 1000000ULL) { wheel_run = 0; octeon_led_run_wheel(); } diff --git a/sys/mips/rmi/dev/xlr/rge.c b/sys/mips/rmi/dev/xlr/rge.c index 679987e..c62c0de 100644 --- a/sys/mips/rmi/dev/xlr/rge.c +++ b/sys/mips/rmi/dev/xlr/rge.c @@ -577,6 +577,8 @@ build_frag_list(struct mbuf *m_head, struct msgrng_msg *p2p_msg, struct p2d_tx_d taddr = (vm_offset_t)m->m_data + len1; p2 = vtophys(taddr); len2 = m->m_len - len1; + if (len2 == 0) + continue; if (nfrag >= XLR_MAX_TX_FRAGS) panic("TX frags exceeded"); diff --git a/sys/mips/sibyte/sb_asm.S b/sys/mips/sibyte/sb_asm.S index b81c067..19d00dd 100644 --- a/sys/mips/sibyte/sb_asm.S +++ b/sys/mips/sibyte/sb_asm.S @@ -27,6 +27,7 @@ */ #include <machine/asm.h> +#include <machine/cpuregs.h> /* * We compile a 32-bit kernel to run on the SB-1 processor which is a 64-bit @@ -80,3 +81,20 @@ LEAF(sb_store64) jr ra sd t0, 0(a0) END(sb_store64) + +#ifdef SMP +/* + * This function must be implemented in assembly because it is called early + * in AP boot without a valid stack. + * + * This cpu number is available in bits 25 to 27 of the coprocessor 0 PRID + * register. This is not documented in the BCM1250 user manual but can be + * gleaned from the CFE source code - see sb1250_altcpu.S + */ +LEAF(platform_processor_id) + mfc0 v0, MIPS_COP_0_PRID + srl v0, v0, 25 + jr ra + and v0, v0, 7 +END(platform_processor_id) +#endif /* SMP */ diff --git a/sys/mips/sibyte/sb_machdep.c b/sys/mips/sibyte/sb_machdep.c index c544b18..c6043b8 100644 --- a/sys/mips/sibyte/sb_machdep.c +++ b/sys/mips/sibyte/sb_machdep.c @@ -74,6 +74,10 @@ __FBSDID("$FreeBSD$"); #include <machine/trap.h> #include <machine/vmparam.h> +#ifdef SMP +#include <machine/smp.h> +#endif + #ifdef CFE #include <dev/cfe/cfe_api.h> #endif @@ -114,6 +118,19 @@ sb_intr_init(int cpuid) intrnum = sb_route_intsrc(intsrc); sb_disable_intsrc(cpuid, intsrc); sb_write_intmap(cpuid, intsrc, intrnum); +#ifdef SMP + /* + * Set up the mailbox interrupt mapping. + * + * The mailbox interrupt is "special" in that it is not shared + * with any other interrupt source. + */ + if (intsrc == INTSRC_MAILBOX3) { + intrnum = platform_ipi_intrnum(); + sb_write_intmap(cpuid, INTSRC_MAILBOX3, intrnum); + sb_enable_intsrc(cpuid, INTSRC_MAILBOX3); + } +#endif } } @@ -282,6 +299,64 @@ kseg0_map_coherent(void) mips_wr_config(config); } +#ifdef SMP +void +platform_ipi_send(int cpuid) +{ + KASSERT(cpuid == 0 || cpuid == 1, + ("platform_ipi_send: invalid cpuid %d", cpuid)); + + sb_set_mailbox(cpuid, 1ULL); +} + +void +platform_ipi_clear(void) +{ + int cpuid; + + cpuid = PCPU_GET(cpuid); + sb_clear_mailbox(cpuid, 1ULL); +} + +int +platform_ipi_intrnum(void) +{ + + return (4); +} + +void +platform_init_ap(int cpuid) +{ + + KASSERT(cpuid == 1, ("AP has an invalid cpu id %d", cpuid)); + + /* + * Make sure that kseg0 is mapped cacheable-coherent + */ + kseg0_map_coherent(); + + sb_intr_init(cpuid); +} + +int +platform_start_ap(int cpuid) +{ +#ifdef CFE + int error; + + if ((error = cfe_cpu_start(cpuid, mpentry, 0, 0, 0))) { + printf("cfe_cpu_start error: %d\n", error); + return (-1); + } else { + return (0); + } +#else + return (-1); +#endif /* CFE */ +} +#endif /* SMP */ + void platform_start(__register_t a0, __register_t a1, __register_t a2, __register_t a3) diff --git a/sys/mips/sibyte/sb_scd.c b/sys/mips/sibyte/sb_scd.c index c499822..007e149 100644 --- a/sys/mips/sibyte/sb_scd.c +++ b/sys/mips/sibyte/sb_scd.c @@ -34,7 +34,7 @@ __FBSDID("$FreeBSD$"); #include <sys/bus.h> #include <machine/resource.h> -#include <machine/intr_machdep.h> +#include <machine/hwfunc.h> #include "sb_scd.h" @@ -189,11 +189,51 @@ sb_route_intsrc(int intsrc) * Interrupt 5 is used by sources internal to the CPU (e.g. timer). * Use a deterministic mapping for the remaining sources. */ +#ifdef SMP + KASSERT(platform_ipi_intrnum() == 4, + ("Unexpected interrupt number used for IPI")); + intrnum = intsrc % 4; +#else intrnum = intsrc % 5; +#endif return (intrnum); } +#ifdef SMP +static uint64_t +sb_read_sysrev(void) +{ + + return (sb_load64(SYSREV_ADDR)); +} + +void +sb_set_mailbox(int cpu, uint64_t val) +{ + uint32_t regaddr; + + regaddr = MAILBOX_SET_ADDR(cpu); + sb_store64(regaddr, val); +} + +void +sb_clear_mailbox(int cpu, uint64_t val) +{ + uint32_t regaddr; + + regaddr = MAILBOX_CLEAR_ADDR(cpu); + sb_store64(regaddr, val); +} + +int +platform_num_processors(void) +{ + + return (SYSREV_NUM_PROCESSORS(sb_read_sysrev())); +} +#endif /* SMP */ + #define SCD_PHYSADDR 0x10000000 #define SCD_SIZE 0x00060000 diff --git a/sys/mips/sibyte/sb_scd.h b/sys/mips/sibyte/sb_scd.h index 8f60716..03d2681 100644 --- a/sys/mips/sibyte/sb_scd.h +++ b/sys/mips/sibyte/sb_scd.h @@ -42,4 +42,10 @@ void sb_write_intsrc_mask(int cpu, uint64_t mask); void sb_write_intmap(int cpu, int intsrc, int intrnum); int sb_read_intmap(int cpu, int intsrc); +#ifdef SMP +#define INTSRC_MAILBOX3 29 +void sb_set_mailbox(int cpuid, uint64_t val); +void sb_clear_mailbox(int cpuid, uint64_t val); +#endif + #endif /* _SB_SCD_H_ */ diff --git a/sys/mips/sibyte/sb_zbbus.c b/sys/mips/sibyte/sb_zbbus.c index bede796..cd16856 100644 --- a/sys/mips/sibyte/sb_zbbus.c +++ b/sys/mips/sibyte/sb_zbbus.c @@ -24,6 +24,9 @@ * SUCH DAMAGE. */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> @@ -31,21 +34,29 @@ #include <sys/bus.h> #include <sys/malloc.h> #include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> #include <machine/resource.h> #include <machine/intr_machdep.h> #include "sb_scd.h" -__FBSDID("$FreeBSD$"); - static MALLOC_DEFINE(M_INTMAP, "sb1250 intmap", "Sibyte 1250 Interrupt Mapper"); -#define NUM_HARD_IRQS 6 +static struct mtx zbbus_intr_mtx; +MTX_SYSINIT(zbbus_intr_mtx, &zbbus_intr_mtx, "zbbus_intr_mask/unmask lock", + MTX_SPIN); + +/* + * This array holds the mapping between a MIPS hard interrupt and the + * interrupt sources that feed into that it. + */ +static uint64_t hardint_to_intsrc_mask[NHARD_IRQS]; struct sb_intmap { int intsrc; /* interrupt mapper register number (0 - 63) */ - int hardint; /* cpu interrupt from 0 to NUM_HARD_IRQS - 1 */ + int hardint; /* cpu interrupt from 0 to NHARD_IRQS - 1 */ /* * The device that the interrupt belongs to. Note that multiple @@ -86,7 +97,7 @@ sb_intmap_add(int intrnum, device_t dev, int rid, int intsrc) { struct sb_intmap *map; - KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS, + KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS, ("intrnum is out of range: %d", intrnum)); map = sb_intmap_lookup(intrnum, dev, rid); @@ -113,12 +124,18 @@ sb_intmap_activate(int intrnum, device_t dev, int rid) { struct sb_intmap *map; - KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS, + KASSERT(intrnum >= 0 && intrnum < NHARD_IRQS, ("intrnum is out of range: %d", intrnum)); map = sb_intmap_lookup(intrnum, dev, rid); if (map) { + /* + * Deliver all interrupts to CPU0. + */ + mtx_lock_spin(&zbbus_intr_mtx); + hardint_to_intsrc_mask[intrnum] |= 1ULL << map->intsrc; sb_enable_intsrc(0, map->intsrc); + mtx_unlock_spin(&zbbus_intr_mtx); } else { /* * In zbbus_setup_intr() we blindly call sb_intmap_activate() @@ -133,6 +150,52 @@ sb_intmap_activate(int intrnum, device_t dev, int rid) } } +/* + * Replace the default interrupt mask and unmask routines in intr_machdep.c + * with routines that are SMP-friendly. In contrast to the default mask/unmask + * routines in intr_machdep.c these routines do not change the SR.int_mask bits. + * + * Instead they use the interrupt mapper to either mask or unmask all + * interrupt sources feeding into a particular interrupt line of the processor. + * + * This means that these routines have an identical effect irrespective of + * which cpu is executing them. This is important because the ithread may + * be scheduled to run on either of the cpus. + */ +static void +zbbus_intr_mask(void *arg) +{ + uint64_t mask; + int irq; + + irq = (uintptr_t)arg; + + mtx_lock_spin(&zbbus_intr_mtx); + + mask = sb_read_intsrc_mask(0); + mask |= hardint_to_intsrc_mask[irq]; + sb_write_intsrc_mask(0, mask); + + mtx_unlock_spin(&zbbus_intr_mtx); +} + +static void +zbbus_intr_unmask(void *arg) +{ + uint64_t mask; + int irq; + + irq = (uintptr_t)arg; + + mtx_lock_spin(&zbbus_intr_mtx); + + mask = sb_read_intsrc_mask(0); + mask &= ~hardint_to_intsrc_mask[irq]; + sb_write_intsrc_mask(0, mask); + + mtx_unlock_spin(&zbbus_intr_mtx); +} + struct zbbus_devinfo { struct resource_list resources; }; @@ -155,6 +218,9 @@ zbbus_attach(device_t dev) device_printf(dev, "attached.\n"); } + cpu_set_hardintr_mask_func(zbbus_intr_mask); + cpu_set_hardintr_unmask_func(zbbus_intr_unmask); + bus_generic_probe(dev); bus_enumerate_hinted_children(dev); bus_generic_attach(dev); diff --git a/sys/mips/sibyte/sb_zbpci.c b/sys/mips/sibyte/sb_zbpci.c index 4e57407..6107b36 100644 --- a/sys/mips/sibyte/sb_zbpci.c +++ b/sys/mips/sibyte/sb_zbpci.c @@ -30,6 +30,7 @@ #include <sys/systm.h> #include <sys/module.h> #include <sys/bus.h> +#include <sys/rman.h> #include <sys/pcpu.h> #include <sys/smp.h> @@ -45,6 +46,7 @@ #include <machine/pmap.h> #include <machine/resource.h> +#include <machine/bus.h> #include "pcib_if.h" @@ -58,7 +60,11 @@ static struct { } zbpci_config_space[MAXCPU]; static const vm_paddr_t CFG_PADDR_BASE = 0xFE000000; - +static const u_long PCI_IOSPACE_ADDR = 0xFC000000; +static const u_long PCI_IOSPACE_SIZE = 0x02000000; + +static struct rman port_rman; + static int zbpci_probe(device_t dev) { @@ -73,13 +79,32 @@ zbpci_attach(device_t dev) int n, rid, size; vm_offset_t va; struct resource *res; + + /* + * Reserve the physical memory window used to map PCI I/O space. + */ + rid = 0; + res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + PCI_IOSPACE_ADDR, + PCI_IOSPACE_ADDR + PCI_IOSPACE_SIZE - 1, + PCI_IOSPACE_SIZE, 0); + if (res == NULL) + panic("Cannot allocate resource for PCI I/O space mapping."); + + port_rman.rm_start = 0; + port_rman.rm_end = PCI_IOSPACE_SIZE - 1; + port_rman.rm_type = RMAN_ARRAY; + port_rman.rm_descr = "PCI I/O ports"; + if (rman_init(&port_rman) != 0 || + rman_manage_region(&port_rman, 0, PCI_IOSPACE_SIZE - 1) != 0) + panic("%s: port_rman", __func__); /* * Reserve the the physical memory that is used to read/write to the * pci config space but don't activate it. We are using a page worth * of KVA as a window over this region. */ - rid = 0; + rid = 1; size = (PCI_BUSMAX + 1) * (PCI_SLOTMAX + 1) * (PCI_FUNCMAX + 1) * 256; res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, CFG_PADDR_BASE, CFG_PADDR_BASE + size - 1, size, 0); @@ -115,6 +140,101 @@ zbpci_attach(device_t dev) return (bus_generic_attach(dev)); } +static struct resource * +zbpci_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct resource *res; + + /* + * Handle PCI I/O port resources here and pass everything else to nexus. + */ + if (type != SYS_RES_IOPORT) { + res = bus_generic_alloc_resource(bus, child, type, rid, + start, end, count, flags); + return (res); + } + + res = rman_reserve_resource(&port_rman, start, end, count, + flags, child); + if (res == NULL) + return (NULL); + + rman_set_rid(res, *rid); + + /* Activate the resource is requested */ + if (flags & RF_ACTIVE) { + if (bus_activate_resource(child, type, *rid, res) != 0) { + rman_release_resource(res); + return (NULL); + } + } + + return (res); +} + +static int +zbpci_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + void *vaddr; + u_long paddr, psize; + + if (type != SYS_RES_IOPORT) { + return (bus_generic_activate_resource(bus, child, type, + rid, res)); + } + + /* + * Map the I/O space resource through the memory window starting + * at PCI_IOSPACE_ADDR. + */ + paddr = rman_get_start(res) + PCI_IOSPACE_ADDR; + psize = rman_get_size(res); + vaddr = pmap_mapdev(paddr, psize); + + rman_set_virtual(res, vaddr); + rman_set_bustag(res, mips_bus_space_generic); + rman_set_bushandle(res, (bus_space_handle_t)vaddr); + + return (rman_activate_resource(res)); +} + +static int +zbpci_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + int error; + + if (type != SYS_RES_IOPORT) + return (bus_generic_release_resource(bus, child, type, rid, r)); + + if (rman_get_flags(r) & RF_ACTIVE) { + error = bus_deactivate_resource(child, type, rid, r); + if (error) + return (error); + } + + return (rman_release_resource(r)); +} + +static int +zbpci_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + vm_offset_t va; + + if (type != SYS_RES_IOPORT) { + return (bus_generic_deactivate_resource(bus, child, type, + rid, r)); + } + + va = (vm_offset_t)rman_get_virtual(r); + pmap_unmapdev(va, rman_get_size(r)); + + return (rman_deactivate_resource(r)); +} + static int zbpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { @@ -248,10 +368,10 @@ static device_method_t zbpci_methods[] ={ /* Bus interface */ DEVMETHOD(bus_read_ivar, zbpci_read_ivar), DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), - DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), - DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), - DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), - DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_alloc_resource, zbpci_alloc_resource), + DEVMETHOD(bus_activate_resource, zbpci_activate_resource), + DEVMETHOD(bus_deactivate_resource, zbpci_deactivate_resource), + DEVMETHOD(bus_release_resource, zbpci_release_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_add_child, bus_generic_add_child), diff --git a/sys/net/vnet.c b/sys/net/vnet.c index 26635db..323ed08 100644 --- a/sys/net/vnet.c +++ b/sys/net/vnet.c @@ -38,11 +38,13 @@ __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_kdb.h" +#include "opt_kdtrace.h" #include <sys/param.h> #include <sys/kdb.h> #include <sys/kernel.h> #include <sys/jail.h> +#include <sys/sdt.h> #include <sys/systm.h> #include <sys/sysctl.h> #include <sys/linker_set.h> @@ -55,6 +57,7 @@ __FBSDID("$FreeBSD$"); #ifdef DDB #include <ddb/ddb.h> +#include <ddb/db_sym.h> #endif #include <net/if.h> @@ -210,6 +213,17 @@ static TAILQ_HEAD(, vnet_data_free) vnet_data_free_head = TAILQ_HEAD_INITIALIZER(vnet_data_free_head); static struct sx vnet_data_free_lock; +SDT_PROVIDER_DEFINE(vnet); +SDT_PROBE_DEFINE1(vnet, functions, vnet_alloc, entry, "int"); +SDT_PROBE_DEFINE2(vnet, functions, vnet_alloc, alloc, "int", "struct vnet *"); +SDT_PROBE_DEFINE2(vnet, functions, vnet_alloc, return, "int", "struct vnet *"); +SDT_PROBE_DEFINE2(vnet, functions, vnet_destroy, entry, "int", "struct vnet *"); +SDT_PROBE_DEFINE1(vnet, functions, vnet_destroy, return, "int"); + +#ifdef DDB +static void db_show_vnet_print_vs(struct vnet_sysinit *, int); +#endif + /* * Allocate a virtual network stack. */ @@ -218,8 +232,10 @@ vnet_alloc(void) { struct vnet *vnet; + SDT_PROBE1(vnet, functions, vnet_alloc, entry, __LINE__); vnet = malloc(sizeof(struct vnet), M_VNET, M_WAITOK | M_ZERO); vnet->vnet_magic_n = VNET_MAGIC_N; + SDT_PROBE2(vnet, functions, vnet_alloc, alloc, __LINE__, vnet); /* * Allocate storage for virtualized global variables and copy in @@ -244,6 +260,7 @@ vnet_alloc(void) LIST_INSERT_HEAD(&vnet_head, vnet, vnet_le); VNET_LIST_WUNLOCK(); + SDT_PROBE2(vnet, functions, vnet_alloc, return, __LINE__, vnet); return (vnet); } @@ -255,6 +272,7 @@ vnet_destroy(struct vnet *vnet) { struct ifnet *ifp, *nifp; + SDT_PROBE2(vnet, functions, vnet_destroy, entry, __LINE__, vnet); KASSERT(vnet->vnet_sockcnt == 0, ("%s: vnet still has sockets", __func__)); @@ -281,6 +299,7 @@ vnet_destroy(struct vnet *vnet) vnet->vnet_data_base = 0; vnet->vnet_magic_n = 0xdeadbeef; free(vnet, M_VNET); + SDT_PROBE1(vnet, functions, vnet_destroy, return, __LINE__); } /* @@ -699,6 +718,64 @@ DB_SHOW_COMMAND(vnets, db_show_vnets) } } +static void +db_show_vnet_print_vs(struct vnet_sysinit *vs, int ddb) +{ + const char *vsname, *funcname; + c_db_sym_t sym; + db_expr_t offset; + +#define xprint(...) \ + if (ddb) \ + db_printf(__VA_ARGS__); \ + else \ + printf(__VA_ARGS__) + + if (vs == NULL) { + xprint("%s: no vnet_sysinit * given\n", __func__); + return; + } + + sym = db_search_symbol((vm_offset_t)vs, DB_STGY_ANY, &offset); + db_symbol_values(sym, &vsname, NULL); + sym = db_search_symbol((vm_offset_t)vs->func, DB_STGY_PROC, &offset); + db_symbol_values(sym, &funcname, NULL); + xprint("%s(%p)\n", (vsname != NULL) ? vsname : "", vs); + xprint(" 0x%08x 0x%08x\n", vs->subsystem, vs->order); + xprint(" %p(%s)(%p)\n", + vs->func, (funcname != NULL) ? funcname : "", vs->arg); +#undef xprint +} + +DB_SHOW_COMMAND(vnet_sysinit, db_show_vnet_sysinit) +{ + struct vnet_sysinit *vs; + + db_printf("VNET_SYSINIT vs Name(Ptr)\n"); + db_printf(" Subsystem Order\n"); + db_printf(" Function(Name)(Arg)\n"); + TAILQ_FOREACH(vs, &vnet_constructors, link) { + db_show_vnet_print_vs(vs, 1); + if (db_pager_quit) + break; + } +} + +DB_SHOW_COMMAND(vnet_sysuninit, db_show_vnet_sysuninit) +{ + struct vnet_sysinit *vs; + + db_printf("VNET_SYSUNINIT vs Name(Ptr)\n"); + db_printf(" Subsystem Order\n"); + db_printf(" Function(Name)(Arg)\n"); + TAILQ_FOREACH_REVERSE(vs, &vnet_destructors, vnet_sysuninit_head, + link) { + db_show_vnet_print_vs(vs, 1); + if (db_pager_quit) + break; + } +} + #ifdef VNET_DEBUG DB_SHOW_COMMAND(vnetrcrs, db_show_vnetrcrs) { diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c index f949255..6e1fda1 100644 --- a/sys/net80211/ieee80211_crypto_tkip.c +++ b/sys/net80211/ieee80211_crypto_tkip.c @@ -144,6 +144,7 @@ tkip_setkey(struct ieee80211_key *k) return 0; } k->wk_keytsc = 1; /* TSC starts at 1 */ + ctx->rx_phase1_done = 0; return 1; } diff --git a/sys/netinet/tcp_hostcache.c b/sys/netinet/tcp_hostcache.c index acbc860..a0c4012 100644 --- a/sys/netinet/tcp_hostcache.c +++ b/sys/netinet/tcp_hostcache.c @@ -115,6 +115,7 @@ static VNET_DEFINE(struct callout, tcp_hc_callout); static struct hc_metrics *tcp_hc_lookup(struct in_conninfo *); static struct hc_metrics *tcp_hc_insert(struct in_conninfo *); static int sysctl_tcp_hc_list(SYSCTL_HANDLER_ARGS); +static void tcp_hc_purge_internal(int); static void tcp_hc_purge(void *); SYSCTL_NODE(_net_inet_tcp, OID_AUTO, hostcache, CTLFLAG_RW, 0, @@ -235,10 +236,19 @@ tcp_hc_init(void) void tcp_hc_destroy(void) { - - /* XXX TODO walk the hashtable and free all entries */ + int i; callout_drain(&V_tcp_hc_callout); + + /* Purge all hc entries. */ + tcp_hc_purge_internal(1); + + /* Free the uma zone and the allocated hash table. */ + uma_zdestroy(V_tcp_hostcache.zone); + + for (i = 0; i < V_tcp_hostcache.hashsize; i++) + mtx_destroy(&V_tcp_hostcache.hashbase[i].hch_mtx); + free(V_tcp_hostcache.hashbase, M_HOSTCACHE); } #endif @@ -633,22 +643,14 @@ sysctl_tcp_hc_list(SYSCTL_HANDLER_ARGS) } /* - * Expire and purge (old|all) entries in the tcp_hostcache. Runs - * periodically from the callout. + * Caller has to make sure the curvnet is set properly. */ static void -tcp_hc_purge(void *arg) +tcp_hc_purge_internal(int all) { - CURVNET_SET((struct vnet *) arg); struct hc_metrics *hc_entry, *hc_next; - int all = 0; int i; - if (V_tcp_hostcache.purgeall) { - all = 1; - V_tcp_hostcache.purgeall = 0; - } - for (i = 0; i < V_tcp_hostcache.hashsize; i++) { THC_LOCK(&V_tcp_hostcache.hashbase[i].hch_mtx); TAILQ_FOREACH_SAFE(hc_entry, @@ -664,6 +666,24 @@ tcp_hc_purge(void *arg) } THC_UNLOCK(&V_tcp_hostcache.hashbase[i].hch_mtx); } +} + +/* + * Expire and purge (old|all) entries in the tcp_hostcache. Runs + * periodically from the callout. + */ +static void +tcp_hc_purge(void *arg) +{ + CURVNET_SET((struct vnet *) arg); + int all = 0; + + if (V_tcp_hostcache.purgeall) { + all = 1; + V_tcp_hostcache.purgeall = 0; + } + + tcp_hc_purge_internal(all); callout_reset(&V_tcp_hc_callout, V_tcp_hostcache.prune * hz, tcp_hc_purge, arg); diff --git a/sys/nfs/nfs_common.c b/sys/nfs/nfs_common.c index b2c231f..abf575f 100644 --- a/sys/nfs/nfs_common.c +++ b/sys/nfs/nfs_common.c @@ -37,7 +37,7 @@ __FBSDID("$FreeBSD$"); /* * These functions support the macros and help fiddle mbuf chains for - * the nfs op functions. They do things like create the rpc header and + * the nfs op functions. They do things like create the rpc header and * copy data between mbuf chains and uio lists. */ @@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/sysent.h> #include <sys/syscall.h> +#include <sys/sysctl.h> #include <vm/vm.h> #include <vm/vm_object.h> @@ -75,10 +76,21 @@ nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON }; -static void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how); +static void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, + int how); + +SYSCTL_DECL(_vfs_nfs); + +static int nfs_realign_test; +SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_test, CTLFLAG_RD, &nfs_realign_test, + 0, "Number of realign tests done"); + +static int nfs_realign_count; +SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_count, CTLFLAG_RD, &nfs_realign_count, + 0, "Number of mbuf realignments done"); u_quad_t -nfs_curusec(void) +nfs_curusec(void) { struct timeval tv; @@ -176,7 +188,7 @@ nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how) while (left == 0) { *mdp = mp = mp->m_next; if (mp == NULL) - return NULL; + return (NULL); left = mp->m_len; *dposp = mtod(mp, caddr_t); } @@ -184,13 +196,13 @@ nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how) ret = *dposp; *dposp += siz; } else if (mp->m_next == NULL) { - return NULL; + return (NULL); } else if (siz > MHLEN) { panic("nfs S too big"); } else { MGET(mp2, how, MT_DATA); if (mp2 == NULL) - return NULL; + return (NULL); mp2->m_len = siz; mp2->m_next = mp->m_next; mp->m_next = mp2; @@ -206,7 +218,7 @@ nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how) /* Loop around copying up the siz2 bytes */ while (siz2 > 0) { if (mp2 == NULL) - return NULL; + return (NULL); xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2; if (xfer > 0) { bcopy(mtod(mp2, caddr_t), ptr, xfer); @@ -229,7 +241,7 @@ nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how) *dposp = npos; } } - return ret; + return (ret); } /* @@ -273,19 +285,21 @@ nfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos) ret = *bpos; (*mb)->m_len += s; *bpos += s; - return ret; + return (ret); } void * nfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos) { - return nfsm_dissect_xx_sub(s, md, dpos, M_WAIT); + + return (nfsm_dissect_xx_sub(s, md, dpos, M_WAIT)); } void * nfsm_dissect_xx_nonblock(int s, struct mbuf **md, caddr_t *dpos) { - return nfsm_dissect_xx_sub(s, md, dpos, M_DONTWAIT); + + return (nfsm_dissect_xx_sub(s, md, dpos, M_DONTWAIT)); } static void * @@ -299,10 +313,10 @@ nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how) if (t1 >= s) { ret = *dpos; *dpos += s; - return ret; + return (ret); } - cp2 = nfsm_disct(md, dpos, s, t1, how); - return cp2; + cp2 = nfsm_disct(md, dpos, s, t1, how); + return (cp2); } int @@ -312,11 +326,11 @@ nfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos) tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos); if (tl == NULL) - return EBADRPC; + return (EBADRPC); *s = fxdr_unsigned(int32_t, *tl); if (*s > m) - return EBADRPC; - return 0; + return (EBADRPC); + return (0); } int @@ -327,10 +341,66 @@ nfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos) t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos; if (t1 >= s) { *dpos += s; - return 0; + return (0); } t1 = nfs_adv(md, dpos, s, t1); if (t1) - return t1; - return 0; + return (t1); + return (0); +} + +/* + * Check for badly aligned mbuf data and realign by copying the unaligned + * portion of the data into a new mbuf chain and freeing the portions of the + * old chain that were replaced. + * + * We cannot simply realign the data within the existing mbuf chain because + * the underlying buffers may contain other rpc commands and we cannot afford + * to overwrite them. + * + * We would prefer to avoid this situation entirely. The situation does not + * occur with NFS/UDP and is supposed to only occassionally occur with TCP. + * Use vfs.nfs.realign_count and realign_test to check this. + */ +int +nfs_realign(struct mbuf **pm, int how) +{ + struct mbuf *m, *n; + int off; + + ++nfs_realign_test; + while ((m = *pm) != NULL) { + if (!nfsm_aligned(m->m_len, u_int32_t) || + !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) { + /* + * NB: we can't depend on m_pkthdr.len to help us + * decide what to do here. May not be worth doing + * the m_length calculation as m_copyback will + * expand the mbuf chain below as needed. + */ + if (m_length(m, NULL) >= MINCLSIZE) { + /* NB: m_copyback handles space > MCLBYTES */ + n = m_getcl(how, MT_DATA, 0); + } else + n = m_get(how, MT_DATA); + if (n == NULL) + return (ENOMEM); + /* + * Align the remainder of the mbuf chain. + */ + n->m_len = 0; + off = 0; + while (m != NULL) { + m_copyback(n, off, m->m_len, mtod(m, caddr_t)); + off += m->m_len; + m = m->m_next; + } + m_freem(*pm); + *pm = n; + ++nfs_realign_count; + break; + } + pm = &m->m_next; + } + return (0); } diff --git a/sys/nfs/nfs_common.h b/sys/nfs/nfs_common.h index 7c06130..1c0b315 100644 --- a/sys/nfs/nfs_common.h +++ b/sys/nfs/nfs_common.h @@ -33,7 +33,6 @@ * $FreeBSD$ */ - #ifndef _NFS_NFS_COMMON_H_ #define _NFS_NFS_COMMON_H_ @@ -49,6 +48,7 @@ extern nfstype nfsv3_type[]; int nfs_adv(struct mbuf **, caddr_t *, int, int); u_quad_t nfs_curusec(void); void *nfsm_disct(struct mbuf **, caddr_t *, int, int, int); +int nfs_realign(struct mbuf **, int); /* ****************************** */ /* Build request/reply phase macros */ @@ -86,7 +86,7 @@ do { \ goto nfsmout; \ } \ } while (0) - + #define nfsm_dissect(c, s) \ ({ \ void *ret; \ diff --git a/sys/nfsclient/nfs_krpc.c b/sys/nfsclient/nfs_krpc.c index 098c46b..bfb1ea1 100644 --- a/sys/nfsclient/nfs_krpc.c +++ b/sys/nfsclient/nfs_krpc.c @@ -87,8 +87,6 @@ uint32_t nfsclient_nfs3_start_probes[NFS_NPROCS]; uint32_t nfsclient_nfs3_done_probes[NFS_NPROCS]; #endif -static int nfs_realign_test; -static int nfs_realign_count; static int nfs_bufpackets = 4; static int nfs_reconnects; static int nfs3_jukebox_delay = 10; @@ -97,17 +95,15 @@ static int fake_wchan; SYSCTL_DECL(_vfs_nfs); -SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_test, CTLFLAG_RW, &nfs_realign_test, 0, - "Number of realign tests done"); -SYSCTL_INT(_vfs_nfs, OID_AUTO, realign_count, CTLFLAG_RW, &nfs_realign_count, 0, - "Number of mbuf realignments done"); SYSCTL_INT(_vfs_nfs, OID_AUTO, bufpackets, CTLFLAG_RW, &nfs_bufpackets, 0, "Buffer reservation size 2 < x < 64"); SYSCTL_INT(_vfs_nfs, OID_AUTO, reconnects, CTLFLAG_RD, &nfs_reconnects, 0, "Number of times the nfs client has had to reconnect"); -SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs3_jukebox_delay, CTLFLAG_RW, &nfs3_jukebox_delay, 0, +SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs3_jukebox_delay, CTLFLAG_RW, + &nfs3_jukebox_delay, 0, "Number of seconds to delay a retry after receiving EJUKEBOX"); -SYSCTL_INT(_vfs_nfs, OID_AUTO, skip_wcc_data_onerr, CTLFLAG_RW, &nfs_skip_wcc_data_onerr, 0, +SYSCTL_INT(_vfs_nfs, OID_AUTO, skip_wcc_data_onerr, CTLFLAG_RW, + &nfs_skip_wcc_data_onerr, 0, "Disable weak cache consistency checking when server returns an error"); static void nfs_down(struct nfsmount *, struct thread *, const char *, @@ -160,7 +156,8 @@ static enum nfs_rto_timer_t nfs_proct[NFS_NPROCS] = { static inline enum nfs_rto_timer_t nfs_rto_timer(u_int32_t procnum) { - return nfs_proct[procnum]; + + return (nfs_proct[procnum]); } /* @@ -225,7 +222,7 @@ nfs_connect(struct nfsmount *nmp) nconf = getnetconfigent("udp6"); else nconf = getnetconfigent("tcp6"); - + /* * Get buffer reservation size from sysctl, but impose reasonable * limits. @@ -273,9 +270,8 @@ nfs_connect(struct nfsmount *nmp) * Someone else already connected. */ CLNT_RELEASE(client); - } else { + } else nmp->nm_client = client; - } /* * Protocols that do not require connections may be optionally left @@ -284,22 +280,21 @@ nfs_connect(struct nfsmount *nmp) if (!(nmp->nm_flag & NFSMNT_NOCONN)) { mtx_unlock(&nmp->nm_mtx); CLNT_CONTROL(client, CLSET_CONNECT, &one); - } else { + } else mtx_unlock(&nmp->nm_mtx); - } /* Restore current thread's credentials. */ td->td_ucred = origcred; mtx_lock(&nmp->nm_mtx); - /* Initialize other non-zero congestion variables */ + /* Initialize other non-zero congestion variables. */ nfs_init_rtt(nmp); mtx_unlock(&nmp->nm_mtx); return (0); } /* - * NFS disconnect. Clean up and unlink. + * NFS disconnect. Clean up and unlink. */ void nfs_disconnect(struct nfsmount *nmp) @@ -316,9 +311,8 @@ nfs_disconnect(struct nfsmount *nmp) #endif CLNT_CLOSE(client); CLNT_RELEASE(client); - } else { + } else mtx_unlock(&nmp->nm_mtx); - } } void @@ -341,11 +335,10 @@ nfs_getauth(struct nfsmount *nmp, struct ucred *cred) case RPCSEC_GSS_KRB5: case RPCSEC_GSS_KRB5I: case RPCSEC_GSS_KRB5P: - if (!nmp->nm_mech_oid) { + if (!nmp->nm_mech_oid) if (!rpc_gss_mech_to_oid("kerberosv5", - &nmp->nm_mech_oid)) + &nmp->nm_mech_oid)) return (NULL); - } if (nmp->nm_secflavor == RPCSEC_GSS_KRB5) svc = rpc_gss_svc_none; else if (nmp->nm_secflavor == RPCSEC_GSS_KRB5I) @@ -404,65 +397,6 @@ nfs_feedback(int type, int proc, void *arg) } /* - * nfs_realign: - * - * Check for badly aligned mbuf data and realign by copying the unaligned - * portion of the data into a new mbuf chain and freeing the portions - * of the old chain that were replaced. - * - * We cannot simply realign the data within the existing mbuf chain - * because the underlying buffers may contain other rpc commands and - * we cannot afford to overwrite them. - * - * We would prefer to avoid this situation entirely. The situation does - * not occur with NFS/UDP and is supposed to only occassionally occur - * with TCP. Use vfs.nfs.realign_count and realign_test to check this. - * - */ -static int -nfs_realign(struct mbuf **pm, int hsiz) -{ - struct mbuf *m, *n; - int off, space; - - ++nfs_realign_test; - while ((m = *pm) != NULL) { - if ((m->m_len & 0x3) || (mtod(m, intptr_t) & 0x3)) { - /* - * NB: we can't depend on m_pkthdr.len to help us - * decide what to do here. May not be worth doing - * the m_length calculation as m_copyback will - * expand the mbuf chain below as needed. - */ - space = m_length(m, NULL); - if (space >= MINCLSIZE) { - /* NB: m_copyback handles space > MCLBYTES */ - n = m_getcl(M_DONTWAIT, MT_DATA, 0); - } else - n = m_get(M_DONTWAIT, MT_DATA); - if (n == NULL) - return (ENOMEM); - /* - * Align the remainder of the mbuf chain. - */ - n->m_len = 0; - off = 0; - while (m != NULL) { - m_copyback(n, off, m->m_len, mtod(m, caddr_t)); - off += m->m_len; - m = m->m_next; - } - m_freem(*pm); - *pm = n; - ++nfs_realign_count; - break; - } - pm = &m->m_next; - } - return (0); -} - -/* * nfs_request - goes something like this * - fill in request struct * - links it into list @@ -503,10 +437,10 @@ nfs_request(struct vnode *vp, struct mbuf *mreq, int procnum, nf.nf_td = td; getmicrouptime(&now); nf.nf_lastmsg = now.tv_sec - - ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay)); + ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay)); /* - * XXX if not already connected call nfs_connect now. Longer + * XXX if not already connected call nfs_connect now. Longer * term, change nfs_mount to call nfs_connect unconditionally * and let clnt_reconnect_create handle reconnects. */ @@ -526,18 +460,17 @@ nfs_request(struct vnode *vp, struct mbuf *mreq, int procnum, /* * Use a conservative timeout for RPCs other than getattr, - * lookup, read or write. The justification for doing "other" + * lookup, read or write. The justification for doing "other" * this way is that these RPCs happen so infrequently that * timer est. would probably be stale. Also, since many of * these RPCs are non-idempotent, a conservative timeout is * desired. */ timer = nfs_rto_timer(procnum); - if (timer != NFS_DEFAULT_TIMER) { + if (timer != NFS_DEFAULT_TIMER) ext.rc_timers = &nmp->nm_timers[timer - 1]; - } else { + else ext.rc_timers = NULL; - } #ifdef KDTRACE_HOOKS if (dtrace_nfsclient_nfs23_start_probe != NULL) { @@ -570,17 +503,16 @@ tryagain: * If there was a successful reply and a tprintf msg. * tprintf a response. */ - if (stat == RPC_SUCCESS) { + if (stat == RPC_SUCCESS) error = 0; - } else if (stat == RPC_TIMEDOUT) { + else if (stat == RPC_TIMEDOUT) error = ETIMEDOUT; - } else if (stat == RPC_VERSMISMATCH) { + else if (stat == RPC_VERSMISMATCH) error = EOPNOTSUPP; - } else if (stat == RPC_PROGVERSMISMATCH) { + else if (stat == RPC_PROGVERSMISMATCH) error = EPROTONOSUPPORT; - } else { + else error = EACCES; - } if (error) goto nfsmout; @@ -592,7 +524,7 @@ tryagain: * These could cause pointer alignment problems, so copy them to * well aligned mbufs. */ - error = nfs_realign(&mrep, 2 * NFSX_UNSIGNED); + error = nfs_realign(&mrep, M_DONTWAIT); if (error == ENOMEM) { m_freem(mrep); AUTH_DESTROY(auth); @@ -609,9 +541,9 @@ tryagain: m_freem(mrep); error = 0; waituntil = time_second + nfs3_jukebox_delay; - while (time_second < waituntil) { - (void) tsleep(&fake_wchan, PSOCK, "nqnfstry", hz); - } + while (time_second < waituntil) + (void)tsleep(&fake_wchan, PSOCK, "nqnfstry", + hz); goto tryagain; } @@ -622,12 +554,13 @@ tryagain: if (error == ESTALE) nfs_purgecache(vp); /* - * Skip wcc data on NFS errors for now. NetApp filers + * Skip wcc data on NFS errors for now. NetApp filers * return corrupt postop attrs in the wcc data for NFS - * err EROFS. Not sure if they could return corrupt + * err EROFS. Not sure if they could return corrupt * postop attrs for others errors. */ - if ((nmp->nm_flag & NFSMNT_NFSV3) && !nfs_skip_wcc_data_onerr) { + if ((nmp->nm_flag & NFSMNT_NFSV3) && + !nfs_skip_wcc_data_onerr) { *mrp = mrep; *mdp = md; *dposp = dpos; @@ -689,7 +622,7 @@ nfsmout: /* * Mark all of an nfs mount's outstanding requests with R_SOFTTERM and - * wait for all requests to complete. This is used by forced unmounts + * wait for all requests to complete. This is used by forced unmounts * to terminate any outstanding RPCs. */ int @@ -703,7 +636,7 @@ nfs_nmcancelreqs(struct nfsmount *nmp) /* * Any signal that can interrupt an NFS operation in an intr mount - * should be added to this set. SIGSTOP and SIGKILL cannot be masked. + * should be added to this set. SIGSTOP and SIGKILL cannot be masked. */ int nfs_sig_set[] = { SIGINT, @@ -722,16 +655,16 @@ static int nfs_sig_pending(sigset_t set) { int i; - + for (i = 0 ; i < sizeof(nfs_sig_set)/sizeof(int) ; i++) if (SIGISMEMBER(set, nfs_sig_set[i])) return (1); return (0); } - + /* * The set/restore sigmask functions are used to (temporarily) overwrite - * the process p_sigmask during an RPC call (for example). These are also + * the process p_sigmask during an RPC call (for example). These are also * used in other places in the NFS client that might tsleep(). */ void @@ -740,18 +673,18 @@ nfs_set_sigmask(struct thread *td, sigset_t *oldset) sigset_t newset; int i; struct proc *p; - + SIGFILLSET(newset); if (td == NULL) td = curthread; /* XXX */ p = td->td_proc; - /* Remove the NFS set of signals from newset */ + /* Remove the NFS set of signals from newset. */ PROC_LOCK(p); mtx_lock(&p->p_sigacts->ps_mtx); for (i = 0 ; i < sizeof(nfs_sig_set)/sizeof(int) ; i++) { /* * But make sure we leave the ones already masked - * by the process, ie. remove the signal from the + * by the process, i.e. remove the signal from the * temporary signalmask only if it wasn't already * in p_sigmask. */ @@ -777,12 +710,13 @@ nfs_restore_sigmask(struct thread *td, sigset_t *set) * old one after msleep() returns. */ int -nfs_msleep(struct thread *td, void *ident, struct mtx *mtx, int priority, char *wmesg, int timo) +nfs_msleep(struct thread *td, void *ident, struct mtx *mtx, int priority, + char *wmesg, int timo) { sigset_t oldset; int error; struct proc *p; - + if ((priority & PCATCH) == 0) return msleep(ident, mtx, priority, wmesg, timo); if (td == NULL) @@ -803,7 +737,7 @@ nfs_sigintr(struct nfsmount *nmp, struct thread *td) { struct proc *p; sigset_t tmpset; - + /* Terminate all requests while attempting a forced unmount. */ if (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) return (EIO); @@ -834,12 +768,11 @@ nfs_msg(struct thread *td, const char *server, const char *msg, int error) struct proc *p; p = td ? td->td_proc : NULL; - if (error) { + if (error) tprintf(p, LOG_INFO, "nfs server %s: %s, error %d\n", server, msg, error); - } else { + else tprintf(p, LOG_INFO, "nfs server %s: %s\n", server, msg); - } return (0); } @@ -858,7 +791,8 @@ nfs_down(struct nfsmount *nmp, struct thread *td, const char *msg, } else mtx_unlock(&nmp->nm_mtx); mtx_lock(&nmp->nm_mtx); - if ((flags & NFSSTA_LOCKTIMEO) && !(nmp->nm_state & NFSSTA_LOCKTIMEO)) { + if ((flags & NFSSTA_LOCKTIMEO) && + !(nmp->nm_state & NFSSTA_LOCKTIMEO)) { nmp->nm_state |= NFSSTA_LOCKTIMEO; mtx_unlock(&nmp->nm_mtx); vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid, @@ -874,9 +808,8 @@ nfs_up(struct nfsmount *nmp, struct thread *td, const char *msg, { if (nmp == NULL) return; - if (tprintfmsg) { + if (tprintfmsg) nfs_msg(td, nmp->nm_mountp->mnt_stat.f_mntfromname, msg, 0); - } mtx_lock(&nmp->nm_mtx); if ((flags & NFSSTA_TIMEO) && (nmp->nm_state & NFSSTA_TIMEO)) { @@ -886,9 +819,10 @@ nfs_up(struct nfsmount *nmp, struct thread *td, const char *msg, VQ_NOTRESP, 1); } else mtx_unlock(&nmp->nm_mtx); - + mtx_lock(&nmp->nm_mtx); - if ((flags & NFSSTA_LOCKTIMEO) && (nmp->nm_state & NFSSTA_LOCKTIMEO)) { + if ((flags & NFSSTA_LOCKTIMEO) && + (nmp->nm_state & NFSSTA_LOCKTIMEO)) { nmp->nm_state &= ~NFSSTA_LOCKTIMEO; mtx_unlock(&nmp->nm_mtx); vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid, diff --git a/sys/nfsserver/nfs.h b/sys/nfsserver/nfs.h index 00dbe5b..bb1893e 100644 --- a/sys/nfsserver/nfs.h +++ b/sys/nfsserver/nfs.h @@ -239,7 +239,6 @@ extern int nfs_debug; #endif -void nfs_realign(struct mbuf **); struct mbuf *nfs_rephead(int, struct nfsrv_descript *, int, struct mbuf **, caddr_t *); void nfsm_srvfattr(struct nfsrv_descript *, struct vattr *, diff --git a/sys/nfsserver/nfs_fha.c b/sys/nfsserver/nfs_fha.c index 25e930f..76537d7 100644 --- a/sys/nfsserver/nfs_fha.c +++ b/sys/nfsserver/nfs_fha.c @@ -202,7 +202,9 @@ fha_extract_info(struct svc_req *req, struct fha_info *i) procnum == NFSPROC_NULL) goto out; - nfs_realign(&req->rq_args); + error = nfs_realign(&req->rq_args, M_DONTWAIT); + if (error) + goto out; md = req->rq_args; dpos = mtod(md, caddr_t); diff --git a/sys/nfsserver/nfs_srvkrpc.c b/sys/nfsserver/nfs_srvkrpc.c index bdfe424..512373b 100644 --- a/sys/nfsserver/nfs_srvkrpc.c +++ b/sys/nfsserver/nfs_srvkrpc.c @@ -96,8 +96,6 @@ SYSCTL_DECL(_vfs_nfsrv); SVCPOOL *nfsrv_pool; int nfsd_waiting = 0; int nfsrv_numnfsd = 0; -static int nfs_realign_test; -static int nfs_realign_count; struct callout nfsrv_callout; static eventhandler_tag nfsrv_nmbclusters_tag; @@ -111,10 +109,6 @@ SYSCTL_INT(_vfs_nfsrv, OID_AUTO, gatherdelay, CTLFLAG_RW, SYSCTL_INT(_vfs_nfsrv, OID_AUTO, gatherdelay_v3, CTLFLAG_RW, &nfsrvw_procrastinate_v3, 0, "Delay in seconds for NFSv3 write gathering"); -SYSCTL_INT(_vfs_nfsrv, OID_AUTO, realign_test, CTLFLAG_RW, - &nfs_realign_test, 0, ""); -SYSCTL_INT(_vfs_nfsrv, OID_AUTO, realign_count, CTLFLAG_RW, - &nfs_realign_count, 0, ""); static int nfssvc_addsock(struct file *, struct thread *); static int nfssvc_nfsd(struct thread *, struct nfsd_nfsd_args *); @@ -250,57 +244,6 @@ nfs_rephead(int siz, struct nfsrv_descript *nd, int err, return (mreq); } -/* - * nfs_realign: - * - * Check for badly aligned mbuf data and realign by copying the unaligned - * portion of the data into a new mbuf chain and freeing the portions - * of the old chain that were replaced. - * - * We cannot simply realign the data within the existing mbuf chain - * because the underlying buffers may contain other rpc commands and - * we cannot afford to overwrite them. - * - * We would prefer to avoid this situation entirely. The situation does - * not occur with NFS/UDP and is supposed to only occassionally occur - * with TCP. Use vfs.nfs.realign_count and realign_test to check this. - */ -void -nfs_realign(struct mbuf **pm) /* XXX COMMON */ -{ - struct mbuf *m; - struct mbuf *n = NULL; - int off = 0; - - ++nfs_realign_test; - while ((m = *pm) != NULL) { - if ((m->m_len & 0x3) || (mtod(m, intptr_t) & 0x3)) { - MGET(n, M_WAIT, MT_DATA); - if (m->m_len >= MINCLSIZE) { - MCLGET(n, M_WAIT); - } - n->m_len = 0; - break; - } - pm = &m->m_next; - } - - /* - * If n is non-NULL, loop on m copying data, then replace the - * portion of the chain that had to be realigned. - */ - if (n != NULL) { - ++nfs_realign_count; - while (m) { - m_copyback(n, off, m->m_len, mtod(m, caddr_t)); - off += m->m_len; - m = m->m_next; - } - m_freem(*pm); - *pm = n; - } -} - static void nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) { @@ -334,7 +277,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) mreq = mrep = NULL; mreq = rqst->rq_args; rqst->rq_args = NULL; - nfs_realign(&mreq); + (void)nfs_realign(&mreq, M_WAIT); /* * Note: we want rq_addr, not svc_getrpccaller for nd_nam2 - diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC index 6787d87..5c29727 100644 --- a/sys/pc98/conf/GENERIC +++ b/sys/pc98/conf/GENERIC @@ -72,6 +72,7 @@ options INCLUDE_CONFIG_FILE # Include this file in kernel options KDB # Enable kernel debugger support. options DDB # Support DDB. options GDB # Support remote GDB. +options DEADLKRES # Enable the deadlock resolver options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC index 4fda8cb..03f3fec 100644 --- a/sys/powerpc/conf/GENERIC +++ b/sys/powerpc/conf/GENERIC @@ -68,6 +68,7 @@ options INCLUDE_CONFIG_FILE # Include this file in kernel # Debugging for use in -current options KDB #Enable the kernel debugger options DDB #Support DDB +#options DEADLKRES #Enable the deadlock resolver options INVARIANTS #Enable calls of extra sanity checking options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS options WITNESS #Enable checks to detect deadlocks and cycles diff --git a/sys/powerpc/conf/MPC85XX b/sys/powerpc/conf/MPC85XX index 8e6badc..2164f43 100644 --- a/sys/powerpc/conf/MPC85XX +++ b/sys/powerpc/conf/MPC85XX @@ -22,6 +22,7 @@ options BOOTP_WIRED_TO=tsec0 options CD9660 options COMPAT_43 options DDB +#options DEADLKRES options DEVICE_POLLING options HZ=1000 #options DIAGNOSTIC diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC index 87d4e85..1c4d45b 100644 --- a/sys/sparc64/conf/GENERIC +++ b/sys/sparc64/conf/GENERIC @@ -69,6 +69,7 @@ options INCLUDE_CONFIG_FILE # Include this file in kernel options KDB # Enable kernel debugger support. options DDB # Support DDB. options GDB # Support remote GDB. +options DEADLKRES # Enable the deadlock resolver options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles diff --git a/sys/sun4v/conf/GENERIC b/sys/sun4v/conf/GENERIC index 2ff3fa5..e0accf1 100644 --- a/sys/sun4v/conf/GENERIC +++ b/sys/sun4v/conf/GENERIC @@ -75,6 +75,7 @@ options DDB # Support DDB. #options TRAP_TRACING # Enable trap tracing. #options TRAP_TRACE_ENTRIES=256 # Trap trace buffer entries. #options GDB # Support remote GDB. +#options DEADLKRES # Enable the deadlock resolver #options INVARIANTS # Enable calls of extra sanity checking #options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS # Enable checks to detect deadlocks and cycles diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h index 8fd52cb..e3653b6 100644 --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -67,6 +67,10 @@ #define PT_SETFPREGS 36 /* set floating-point registers */ #define PT_GETDBREGS 37 /* get debugging registers */ #define PT_SETDBREGS 38 /* set debugging registers */ + +#define PT_VM_TIMESTAMP 40 /* Get VM version (timestamp) */ +#define PT_VM_ENTRY 41 /* Get VM map (entry) */ + #define PT_FIRSTMACH 64 /* for machine-specific requests */ #include <machine/ptrace.h> /* machine-specific requests, if any */ @@ -98,6 +102,20 @@ struct ptrace_lwpinfo { sigset_t pl_siglist; /* LWP pending signal */ }; +/* Argument structure for PT_VM_ENTRY. */ +struct ptrace_vm_entry { + int pve_entry; /* Entry number used for iteration. */ + int pve_timestamp; /* Generation number of VM map. */ + u_long pve_start; /* Start VA of range. */ + u_long pve_end; /* End VA of range (incl). */ + u_long pve_offset; /* Offset in backing object. */ + u_int pve_prot; /* Protection of memory range. */ + u_int pve_pathlen; /* Size of path. */ + long pve_fileid; /* File ID. */ + uint32_t pve_fsid; /* File system ID. */ + char *pve_path; /* Path name of object. */ +}; + #ifdef _KERNEL #define PTRACESTOP_SC(p, td, flag) \ diff --git a/sys/teken/teken_scs.h b/sys/teken/teken_scs.h index 815e1d7..03e88f2 100644 --- a/sys/teken/teken_scs.h +++ b/sys/teken/teken_scs.h @@ -41,12 +41,12 @@ static const uint16_t teken_boxdrawing_unicode[31] = { 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7 }; -/* CP437 points for VT100 box drawing. */ +/* ASCII points for VT100 box drawing. */ static const uint8_t teken_boxdrawing_8bit[31] = { - 0x04, 0xb1, 0x48, 0x46, 0x43, 0x4c, 0xf8, 0xf1, - 0x4e, 0x56, 0xd9, 0xbf, 0xda, 0xc0, 0xc5, 0xc4, - 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, 0xb4, 0xc1, 0xc2, - 0xb3, 0xf3, 0xf2, 0xe3, 0xd8, 0x9c, 0xfa, + '?', '?', 'H', 'F', 'C', 'L', '?', '?', + 'N', 'V', '+', '+', '+', '+', '+', '-', + '-', '-', '-', '-', '+', '+', '+', '+', + '|', '?', '?', '?', '?', '?', '?', }; static teken_char_t diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 1363474..36952cc 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -93,24 +93,25 @@ __FBSDID("$FreeBSD$"); #include <ufs/ffs/fs.h> #include <ufs/ffs/ffs_extern.h> -typedef ufs2_daddr_t allocfcn_t(struct inode *ip, int cg, ufs2_daddr_t bpref, +typedef ufs2_daddr_t allocfcn_t(struct inode *ip, u_int cg, ufs2_daddr_t bpref, int size); -static ufs2_daddr_t ffs_alloccg(struct inode *, int, ufs2_daddr_t, int); +static ufs2_daddr_t ffs_alloccg(struct inode *, u_int, ufs2_daddr_t, int); static ufs2_daddr_t ffs_alloccgblk(struct inode *, struct buf *, ufs2_daddr_t); #ifdef INVARIANTS static int ffs_checkblk(struct inode *, ufs2_daddr_t, long); #endif -static ufs2_daddr_t ffs_clusteralloc(struct inode *, int, ufs2_daddr_t, int); +static ufs2_daddr_t ffs_clusteralloc(struct inode *, u_int, ufs2_daddr_t, int); static void ffs_clusteracct(struct ufsmount *, struct fs *, struct cg *, ufs1_daddr_t, int); static ino_t ffs_dirpref(struct inode *); -static ufs2_daddr_t ffs_fragextend(struct inode *, int, ufs2_daddr_t, int, int); +static ufs2_daddr_t ffs_fragextend(struct inode *, u_int, ufs2_daddr_t, + int, int); static void ffs_fserr(struct fs *, ino_t, char *); static ufs2_daddr_t ffs_hashalloc - (struct inode *, int, ufs2_daddr_t, int, allocfcn_t *); -static ufs2_daddr_t ffs_nodealloccg(struct inode *, int, ufs2_daddr_t, int); + (struct inode *, u_int, ufs2_daddr_t, int, allocfcn_t *); +static ufs2_daddr_t ffs_nodealloccg(struct inode *, u_int, ufs2_daddr_t, int); static ufs1_daddr_t ffs_mapsearch(struct fs *, struct cg *, ufs2_daddr_t, int); static int ffs_reallocblks_ufs1(struct vop_reallocblks_args *); static int ffs_reallocblks_ufs2(struct vop_reallocblks_args *); @@ -145,7 +146,7 @@ ffs_alloc(ip, lbn, bpref, size, flags, cred, bnp) struct fs *fs; struct ufsmount *ump; ufs2_daddr_t bno; - int cg, reclaimed; + u_int cg, reclaimed; static struct timeval lastfail; static int curfail; int64_t delta; @@ -248,7 +249,8 @@ ffs_realloccg(ip, lbprev, bprev, bpref, osize, nsize, flags, cred, bpp) struct fs *fs; struct buf *bp; struct ufsmount *ump; - int cg, request, error, reclaimed; + u_int cg, request, reclaimed; + int error; ufs2_daddr_t bno; static struct timeval lastfail; static int curfail; @@ -933,7 +935,8 @@ ffs_valloc(pvp, mode, cred, vpp) struct timespec ts; struct ufsmount *ump; ino_t ino, ipref; - int cg, error, error1; + u_int cg; + int error, error1; static struct timeval lastfail; static int curfail; @@ -1043,11 +1046,11 @@ ffs_dirpref(pip) struct inode *pip; { struct fs *fs; - int cg, prefcg, dirsize, cgsize; - int avgifree, avgbfree, avgndir, curdirsize; - int minifree, minbfree, maxndir; - int mincg, minndir; - int maxcontigdirs; + u_int cg, prefcg, dirsize, cgsize; + u_int avgifree, avgbfree, avgndir, curdirsize; + u_int minifree, minbfree, maxndir; + u_int mincg, minndir; + u_int maxcontigdirs; mtx_assert(UFS_MTX(pip->i_ump), MA_OWNED); fs = pip->i_fs; @@ -1171,8 +1174,8 @@ ffs_blkpref_ufs1(ip, lbn, indx, bap) ufs1_daddr_t *bap; { struct fs *fs; - int cg; - int avgbfree, startcg; + u_int cg; + u_int avgbfree, startcg; mtx_assert(UFS_MTX(ip->i_ump), MA_OWNED); fs = ip->i_fs; @@ -1221,8 +1224,8 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap) ufs2_daddr_t *bap; { struct fs *fs; - int cg; - int avgbfree, startcg; + u_int cg; + u_int avgbfree, startcg; mtx_assert(UFS_MTX(ip->i_ump), MA_OWNED); fs = ip->i_fs; @@ -1275,14 +1278,14 @@ ffs_blkpref_ufs2(ip, lbn, indx, bap) static ufs2_daddr_t ffs_hashalloc(ip, cg, pref, size, allocator) struct inode *ip; - int cg; + u_int cg; ufs2_daddr_t pref; int size; /* size for data blocks, mode for inodes */ allocfcn_t *allocator; { struct fs *fs; ufs2_daddr_t result; - int i, icg = cg; + u_int i, icg = cg; mtx_assert(UFS_MTX(ip->i_ump), MA_OWNED); #ifdef INVARIANTS @@ -1333,7 +1336,7 @@ ffs_hashalloc(ip, cg, pref, size, allocator) static ufs2_daddr_t ffs_fragextend(ip, cg, bprev, osize, nsize) struct inode *ip; - int cg; + u_int cg; ufs2_daddr_t bprev; int osize, nsize; { @@ -1416,7 +1419,7 @@ fail: static ufs2_daddr_t ffs_alloccg(ip, cg, bpref, size) struct inode *ip; - int cg; + u_int cg; ufs2_daddr_t bpref; int size; { @@ -1586,7 +1589,7 @@ gotit: static ufs2_daddr_t ffs_clusteralloc(ip, cg, bpref, len) struct inode *ip; - int cg; + u_int cg; ufs2_daddr_t bpref; int len; { @@ -1710,7 +1713,7 @@ fail: static ufs2_daddr_t ffs_nodealloccg(ip, cg, ipref, mode) struct inode *ip; - int cg; + u_int cg; ufs2_daddr_t ipref; int mode; { @@ -1811,7 +1814,7 @@ gotit: bdwrite(bp); if (ibp != NULL) bawrite(ibp); - return (cg * fs->fs_ipg + ipref); + return ((ino_t)(cg * fs->fs_ipg + ipref)); } /* @@ -1856,7 +1859,8 @@ ffs_blkfree(ump, fs, devvp, bno, size, inum) struct buf *bp; ufs1_daddr_t fragno, cgbno; ufs2_daddr_t cgblkno; - int i, cg, blk, frags, bbase; + int i, blk, frags, bbase; + u_int cg; u_int8_t *blksfree; struct cdev *dev; @@ -2054,7 +2058,8 @@ ffs_freefile(ump, fs, devvp, ino, mode) struct cg *cgp; struct buf *bp; ufs2_daddr_t cgbno; - int error, cg; + int error; + u_int cg; u_int8_t *inosused; struct cdev *dev; @@ -2068,7 +2073,7 @@ ffs_freefile(ump, fs, devvp, ino, mode) dev = devvp->v_rdev; cgbno = fsbtodb(fs, cgtod(fs, cg)); } - if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg) + if (ino >= fs->fs_ipg * fs->fs_ncg) panic("ffs_freefile: range: dev = %s, ino = %lu, fs = %s", devtoname(dev), (u_long)ino, fs->fs_fsmnt); if ((error = bread(devvp, cgbno, (int)fs->fs_cgsize, NOCRED, &bp))) { @@ -2085,8 +2090,8 @@ ffs_freefile(ump, fs, devvp, ino, mode) inosused = cg_inosused(cgp); ino %= fs->fs_ipg; if (isclr(inosused, ino)) { - printf("dev = %s, ino = %lu, fs = %s\n", devtoname(dev), - (u_long)ino + cg * fs->fs_ipg, fs->fs_fsmnt); + printf("dev = %s, ino = %u, fs = %s\n", devtoname(dev), + ino + cg * fs->fs_ipg, fs->fs_fsmnt); if (fs->fs_ronly == 0) panic("ffs_freefile: freeing free inode"); } @@ -2121,7 +2126,8 @@ ffs_checkfreefile(fs, devvp, ino) struct cg *cgp; struct buf *bp; ufs2_daddr_t cgbno; - int ret, cg; + int ret; + u_int cg; u_int8_t *inosused; cg = ino_to_cg(fs, ino); @@ -2132,7 +2138,7 @@ ffs_checkfreefile(fs, devvp, ino) /* devvp is a normal disk device */ cgbno = fsbtodb(fs, cgtod(fs, cg)); } - if ((u_int)ino >= fs->fs_ipg * fs->fs_ncg) + if (ino >= fs->fs_ipg * fs->fs_ncg) return (1); if (bread(devvp, cgbno, (int)fs->fs_cgsize, NOCRED, &bp)) { brelse(bp); diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 6eb8964..8aa9f9c 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -138,7 +138,7 @@ ffs_mount(struct mount *mp) struct ufsmount *ump = 0; struct fs *fs; int error, flags; - u_int mntorflags, mntandnotflags; + u_int mntorflags; accmode_t accmode; struct nameidata ndp; char *fspec; @@ -163,7 +163,6 @@ ffs_mount(struct mount *mp) return (error); mntorflags = 0; - mntandnotflags = 0; if (vfs_getopt(mp->mnt_optnew, "acls", NULL, NULL) == 0) mntorflags |= MNT_ACLS; @@ -187,7 +186,7 @@ ffs_mount(struct mount *mp) } MNT_ILOCK(mp); - mp->mnt_flag = (mp->mnt_flag | mntorflags) & ~mntandnotflags; + mp->mnt_flag |= mntorflags; MNT_IUNLOCK(mp); /* * If updating, check whether changing from read-only to diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h index cc1d357..5452e2b 100644 --- a/sys/ufs/ffs/fs.h +++ b/sys/ufs/ffs/fs.h @@ -264,7 +264,7 @@ struct fs { int32_t fs_old_time; /* last time written */ int32_t fs_old_size; /* number of blocks in fs */ int32_t fs_old_dsize; /* number of data blocks in fs */ - int32_t fs_ncg; /* number of cylinder groups */ + u_int32_t fs_ncg; /* number of cylinder groups */ int32_t fs_bsize; /* size of basic blocks in fs */ int32_t fs_fsize; /* size of frag blocks in fs */ int32_t fs_frag; /* number of frags in a block in fs */ @@ -287,7 +287,7 @@ struct fs { int32_t fs_spare1[2]; /* old fs_csmask */ /* old fs_csshift */ int32_t fs_nindir; /* value of NINDIR */ - int32_t fs_inopb; /* value of INOPB */ + u_int32_t fs_inopb; /* value of INOPB */ int32_t fs_old_nspf; /* value of NSPF */ /* yet another configuration parameter */ int32_t fs_optim; /* optimization preference, see below */ @@ -304,7 +304,7 @@ struct fs { int32_t fs_old_spc; /* sectors per cylinder */ int32_t fs_old_ncyl; /* cylinders in filesystem */ int32_t fs_old_cpg; /* cylinders per group */ - int32_t fs_ipg; /* inodes per group */ + u_int32_t fs_ipg; /* inodes per group */ int32_t fs_fpg; /* blocks per group * fs_frag */ /* this data must be re-computed after crashes */ struct csum fs_old_cstotal; /* cylinder summary information */ @@ -335,10 +335,10 @@ struct fs { int64_t fs_dsize; /* number of data blocks in fs */ ufs2_daddr_t fs_csaddr; /* blk addr of cyl grp summary area */ int64_t fs_pendingblocks; /* (u) blocks being freed */ - int32_t fs_pendinginodes; /* (u) inodes being freed */ - int32_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */ - int32_t fs_avgfilesize; /* expected average file size */ - int32_t fs_avgfpdir; /* expected # of files per directory */ + u_int32_t fs_pendinginodes; /* (u) inodes being freed */ + ino_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */ + u_int32_t fs_avgfilesize; /* expected average file size */ + u_int32_t fs_avgfpdir; /* expected # of files per directory */ int32_t fs_save_cgsize; /* save real cg size to use fs_bsize */ int32_t fs_sparecon32[26]; /* reserved for future constants */ int32_t fs_flags; /* see FS_ flags below */ @@ -463,26 +463,26 @@ struct cg { int32_t cg_firstfield; /* historic cyl groups linked list */ int32_t cg_magic; /* magic number */ int32_t cg_old_time; /* time last written */ - int32_t cg_cgx; /* we are the cgx'th cylinder group */ + u_int32_t cg_cgx; /* we are the cgx'th cylinder group */ int16_t cg_old_ncyl; /* number of cyl's this cg */ int16_t cg_old_niblk; /* number of inode blocks this cg */ - int32_t cg_ndblk; /* number of data blocks this cg */ - struct csum cg_cs; /* cylinder summary information */ - int32_t cg_rotor; /* position of last used block */ - int32_t cg_frotor; /* position of last used frag */ - int32_t cg_irotor; /* position of last used inode */ - int32_t cg_frsum[MAXFRAG]; /* counts of available frags */ + u_int32_t cg_ndblk; /* number of data blocks this cg */ + struct csum cg_cs; /* cylinder summary information */ + u_int32_t cg_rotor; /* position of last used block */ + u_int32_t cg_frotor; /* position of last used frag */ + u_int32_t cg_irotor; /* position of last used inode */ + u_int32_t cg_frsum[MAXFRAG]; /* counts of available frags */ int32_t cg_old_btotoff; /* (int32) block totals per cylinder */ int32_t cg_old_boff; /* (u_int16) free block positions */ - int32_t cg_iusedoff; /* (u_int8) used inode map */ - int32_t cg_freeoff; /* (u_int8) free block map */ - int32_t cg_nextfreeoff; /* (u_int8) next available space */ - int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */ - int32_t cg_clusteroff; /* (u_int8) free cluster map */ - int32_t cg_nclusterblks; /* number of clusters this cg */ - int32_t cg_niblk; /* number of inode blocks this cg */ - int32_t cg_initediblk; /* last initialized inode */ - int32_t cg_unrefs; /* number of unreferenced inodes */ + u_int32_t cg_iusedoff; /* (u_int8) used inode map */ + u_int32_t cg_freeoff; /* (u_int8) free block map */ + u_int32_t cg_nextfreeoff; /* (u_int8) next available space */ + u_int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */ + u_int32_t cg_clusteroff; /* (u_int8) free cluster map */ + u_int32_t cg_nclusterblks; /* number of clusters this cg */ + u_int32_t cg_niblk; /* number of inode blocks this cg */ + u_int32_t cg_initediblk; /* last initialized inode */ + u_int32_t cg_unrefs; /* number of unreferenced inodes */ int32_t cg_sparecon32[2]; /* reserved for future use */ ufs_time_t cg_time; /* time last written */ int64_t cg_sparecon64[3]; /* reserved for future use */ @@ -529,11 +529,11 @@ struct cg { * inode number to cylinder group number. * inode number to filesystem block address. */ -#define ino_to_cg(fs, x) ((x) / (fs)->fs_ipg) +#define ino_to_cg(fs, x) (((ino_t)(x)) / (fs)->fs_ipg) #define ino_to_fsba(fs, x) \ - ((ufs2_daddr_t)(cgimin(fs, ino_to_cg(fs, x)) + \ - (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs)))))) -#define ino_to_fsbo(fs, x) ((x) % INOPB(fs)) + ((ufs2_daddr_t)(cgimin(fs, ino_to_cg(fs, (ino_t)(x))) + \ + (blkstofrags((fs), ((((ino_t)(x)) % (fs)->fs_ipg) / INOPB(fs)))))) +#define ino_to_fsbo(fs, x) (((ino_t)(x)) % INOPB(fs)) /* * Give cylinder group number for a filesystem block. diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index b00470d..2912bbf 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -1539,7 +1539,6 @@ OLD_FILES+=usr/share/man/man8/pac.8.gz .if ${MK_MAIL} == no OLD_FILES+=usr/bin/Mail OLD_FILES+=usr/bin/biff -OLD_FILES+=usr/bin/fmt OLD_FILES+=usr/bin/from OLD_FILES+=usr/bin/mail OLD_FILES+=usr/bin/mailx @@ -1560,7 +1559,9 @@ OLD_FILES+=usr/share/misc/mail.tildehelp .if ${MK_MAILWRAPPER} == no OLD_FILES+=etc/mail/mailer.conf +.if ${MK_SENDMAIL} == no OLD_FILES+=usr/sbin/mailwrapper +.endif OLD_FILES+=usr/share/man/man8/mailwrapper.8.gz .endif diff --git a/tools/regression/bin/sh/parameters/mail2.0 b/tools/regression/bin/sh/parameters/mail2.0 index 4f14849..e3b7da3 100644 --- a/tools/regression/bin/sh/parameters/mail2.0 +++ b/tools/regression/bin/sh/parameters/mail2.0 @@ -4,7 +4,7 @@ goodfile=/var/empty/sh-test-goodfile mailfile=/var/empty/sh-test-mailfile T=$(mktemp sh-test.XXXXXX) || exit -MAIL=$mailfile ktrace -i -f "$T" sh +m -i </dev/null >/dev/null 2>&1 +ENV=$goodfile MAIL=$mailfile ktrace -i -f "$T" sh +m -i </dev/null >/dev/null 2>&1 if ! grep -q $goodfile "$T"; then # ktrace problem rc=0 diff --git a/tools/regression/file/newfileops_on_fork/newfileops_on_fork.c b/tools/regression/file/newfileops_on_fork/newfileops_on_fork.c index 42a6af7..8713a82 100644 --- a/tools/regression/file/newfileops_on_fork/newfileops_on_fork.c +++ b/tools/regression/file/newfileops_on_fork/newfileops_on_fork.c @@ -113,7 +113,7 @@ main(__unused int argc, __unused char *argv[]) err(-1, "bind"); if (listen(listen_fd, -1) <0) err(-1, "listen"); - if (pthread_create(&accept_thread, NULL, do_accept, NULL) < 0) + if (pthread_create(&accept_thread, NULL, do_accept, NULL) != 0) err(-1, "pthread_create"); sleep(1); /* Easier than using a CV. */; do_fork(); diff --git a/tools/regression/gaithrstress/gaithrstress.c b/tools/regression/gaithrstress/gaithrstress.c index 023e0b5..c76f94f 100644 --- a/tools/regression/gaithrstress/gaithrstress.c +++ b/tools/regression/gaithrstress/gaithrstress.c @@ -241,7 +241,7 @@ usage: fflush(stdout); for (i = 0; i < nworkers; i++) { if (pthread_create(&workers[i].w_thread, NULL, work, - &workers[i]) == -1) + &workers[i]) != 0) err(1, "creating worker %u", i); printf("%u%s", i, i == nworkers - 1 ? ".\n" : ", "); fflush(stdout); diff --git a/tools/regression/usr.bin/pkill/pgrep-q.t b/tools/regression/usr.bin/pkill/pgrep-q.t new file mode 100644 index 0000000..02a89fc --- /dev/null +++ b/tools/regression/usr.bin/pkill/pgrep-q.t @@ -0,0 +1,38 @@ +#!/bin/sh +# $FreeBSD$ + +base=`basename $0` + +echo "1..4" + +name="pgrep -q" +sleep0=`mktemp /tmp/$base.XXXXXX` || exit 1 +sleep1=`mktemp /tmp/$base.XXXXXX` || exit 1 +ln -sf /bin/sleep $sleep0 +$sleep0 5 & +sleep 0.3 +pid=$! +out="`pgrep -q -f $sleep0 2>&1`" +if [ $? -eq 0 ]; then + echo "ok 1 - $name" +else + echo "not ok 1 - $name" +fi +if [ -z "${out}" ]; then + echo "ok 2 - $name" +else + echo "not ok 2 - $name" +fi +out="`pgrep -q -f $sleep1 2>&1`" +if [ $? -ne 0 ]; then + echo "ok 3 - $name" +else + echo "not ok 3 - $name" +fi +if [ -z "${out}" ]; then + echo "ok 4 - $name" +else + echo "not ok 4 - $name" +fi +kill $pid +rm -f $sleep0 $sleep1 diff --git a/tools/tools/mctest/mctest.cc b/tools/tools/mctest/mctest.cc index 7140b62..2bd4b3e 100644 --- a/tools/tools/mctest/mctest.cc +++ b/tools/tools/mctest/mctest.cc @@ -368,7 +368,7 @@ int source(char *interface, struct in_addr *group, int pkt_size, args[i].packets = received[i]; args[i].number = number / clients; args[i].client = base_port + i; - if (pthread_create(&thread[i], NULL, server, &args[i]) < 0) { + if (pthread_create(&thread[i], NULL, server, &args[i]) != 0) { perror("failed to create server thread"); return -1; } @@ -393,7 +393,7 @@ int source(char *interface, struct in_addr *group, int pkt_size, } for (int i = 0; i < clients; i++) { - if (pthread_join(thread[i], NULL) < 0) { + if (pthread_join(thread[i], NULL) != 0) { perror("failed to join thread"); return -1; } diff --git a/tools/tools/netrate/http/http.c b/tools/tools/netrate/http/http.c index 5010703..ea9431f 100644 --- a/tools/tools/netrate/http/http.c +++ b/tools/tools/netrate/http/http.c @@ -300,15 +300,15 @@ main(int argc, char *argv[]) if (threaded) { if (pthread_barrier_init(&statep->start_barrier, NULL, - numthreads) < 0) - err(-1, "pthread_mutex_init"); + numthreads) != 0) + err(-1, "pthread_barrier_init"); } for (i = 0; i < numthreads; i++) { statep->hwd[i].hwd_count = 0; if (threaded) { if (pthread_create(&statep->hwd[i].hwd_thread, NULL, - http_worker, &statep->hwd[i]) < 0) + http_worker, &statep->hwd[i]) != 0) err(-1, "pthread_create"); } else { curthread = i; @@ -339,7 +339,7 @@ main(int argc, char *argv[]) for (i = 0; i < numthreads; i++) { if (threaded) { if (pthread_join(statep->hwd[i].hwd_thread, NULL) - < 0) + != 0) err(-1, "pthread_join"); } else { pid = waitpid(statep->hwd[i].hwd_pid, NULL, 0); diff --git a/tools/tools/netrate/httpd/httpd.c b/tools/tools/netrate/httpd/httpd.c index 73422b1..3e7a672 100644 --- a/tools/tools/netrate/httpd/httpd.c +++ b/tools/tools/netrate/httpd/httpd.c @@ -280,7 +280,7 @@ main(int argc, char *argv[]) for (i = 0; i < THREADS; i++) { if (threaded) { if (pthread_create(&statep->hts[i].hts_thread, NULL, - httpd_worker, &statep->hts[i]) < 0) + httpd_worker, &statep->hts[i]) != 0) err(-1, "pthread_create"); } else { pid = fork(); @@ -299,7 +299,7 @@ main(int argc, char *argv[]) for (i = 0; i < THREADS; i++) { if (threaded) { if (pthread_join(statep->hts[i].hts_thread, NULL) - < 0) + != 0) err(-1, "pthread_join"); } else { pid = waitpid(statep->hts[i].hts_pid, NULL, 0); diff --git a/tools/tools/netrate/juggle/juggle.c b/tools/tools/netrate/juggle/juggle.c index d0bb496..67bb233 100644 --- a/tools/tools/netrate/juggle/juggle.c +++ b/tools/tools/netrate/juggle/juggle.c @@ -301,15 +301,15 @@ juggling_thread(void *arg) fd2 = *(int *)arg; - if (pthread_mutex_lock(&threaded_mtx) < 0) + if (pthread_mutex_lock(&threaded_mtx) != 0) err(-1, "juggling_thread: pthread_mutex_lock"); threaded_child_ready = 1; - if (pthread_cond_signal(&threaded_cond) < 0) + if (pthread_cond_signal(&threaded_cond) != 0) err(-1, "juggling_thread: pthread_cond_signal"); - if (pthread_mutex_unlock(&threaded_mtx) < 0) + if (pthread_mutex_unlock(&threaded_mtx) != 0) err(-1, "juggling_thread: pthread_mutex_unlock"); for (i = 0; i < NUMCYCLES; i++) { @@ -334,21 +334,21 @@ thread_juggle(int fd1, int fd2, int pipeline) threaded_pipeline = pipeline; - if (pthread_mutex_init(&threaded_mtx, NULL) < 0) + if (pthread_mutex_init(&threaded_mtx, NULL) != 0) err(-1, "thread_juggle: pthread_mutex_init"); - if (pthread_create(&thread, NULL, juggling_thread, &fd2) < 0) + if (pthread_create(&thread, NULL, juggling_thread, &fd2) != 0) err(-1, "thread_juggle: pthread_create"); - if (pthread_mutex_lock(&threaded_mtx) < 0) + if (pthread_mutex_lock(&threaded_mtx) != 0) err(-1, "thread_juggle: pthread_mutex_lock"); while (!threaded_child_ready) { - if (pthread_cond_wait(&threaded_cond, &threaded_mtx) < 0) + if (pthread_cond_wait(&threaded_cond, &threaded_mtx) != 0) err(-1, "thread_juggle: pthread_cond_wait"); } - if (pthread_mutex_unlock(&threaded_mtx) < 0) + if (pthread_mutex_unlock(&threaded_mtx) != 0) err(-1, "thread_juggle: pthread_mutex_unlock"); if (clock_gettime(CLOCK_REALTIME, &tstart) < 0) @@ -369,7 +369,7 @@ thread_juggle(int fd1, int fd2, int pipeline) if (clock_gettime(CLOCK_REALTIME, &tfinish) < 0) err(-1, "thread_juggle: clock_gettime"); - if (pthread_join(thread, NULL) < 0) + if (pthread_join(thread, NULL) != 0) err(-1, "thread_juggle: pthread_join"); timespecsub(&tfinish, &tstart); diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 4a85571..d619824 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -66,7 +66,7 @@ SUBDIR= alias \ ${_file2c} \ find \ finger \ - ${_fmt} \ + fmt \ fold \ ${_from} \ fstat \ @@ -296,7 +296,6 @@ _locate= locate # XXX msgs? .if ${MK_MAIL} != "no" _biff= biff -_fmt= fmt _from= from _mail= mail _msgs= msgs diff --git a/usr.bin/ee/Makefile b/usr.bin/ee/Makefile index 43f0432..ffa47b9 100644 --- a/usr.bin/ee/Makefile +++ b/usr.bin/ee/Makefile @@ -14,7 +14,7 @@ LDADD= -lncurses WARNS?= 2 NLS= en_US.US-ASCII fr_FR.ISO8859-1 de_DE.ISO8859-1 pl_PL.ISO8859-2 \ - uk_UA.KOI8-U ru_RU.KOI8-R hu_HU.ISO8859-2 + uk_UA.KOI8-U pt_BR.ISO8859-1 ru_RU.KOI8-R hu_HU.ISO8859-2 NLSLINKS_en_US.US-ASCII= en_US.ISO8859-1 en_US.ISO8859-15 NLSLINKS_fr_FR.ISO8859-1= fr_BE.ISO8859-1 fr_BE.ISO8859-15 \ @@ -22,6 +22,7 @@ NLSLINKS_fr_FR.ISO8859-1= fr_BE.ISO8859-1 fr_BE.ISO8859-15 \ fr_FR.ISO8859-15 NLSLINKS_de_DE.ISO8859-1= de_AT.ISO8859-1 de_AT.ISO8859-15 de_CH.ISO8859-1 \ de_CH.ISO8859-15 de_DE.ISO8859-15 +NLSLINKS_pt_BR.ISO8859-1= pt_PT.ISO8859-1 NLSSRCFILES=ee.msg .for lang in ${NLS} diff --git a/usr.bin/ee/nls/pt_BR.ISO8859-1/ee.msg b/usr.bin/ee/nls/pt_BR.ISO8859-1/ee.msg new file mode 100644 index 0000000..592ea17 --- /dev/null +++ b/usr.bin/ee/nls/pt_BR.ISO8859-1/ee.msg @@ -0,0 +1,186 @@ +$ This file contains the messages for ee ("easy editor"). See the file +$ ee.i18n.guide for more information +$ +$ For ee patchlevel 3 +$ +$ $Header: /home/hugh/sources/old_ae/RCS/ee.msg,v 1.8 1996/11/30 03:23:40 hugh Exp $ +$ $FreeBSD$ +$ +$ +$set 1 +$quote " +1 "modo menu" +2 "tabs para espaços " +3 "busca com case sensitive " +4 "observar margens " +5 "formatação de auto-parágrafo" +6 "caracteres de oito bits " +7 "informação da janela " +8 "margem direita " +9 "deixar o menu" +10 "salvar mudanças" +11 "sem salvar" +12 "menu arquivo" +13 "ler um arquivo" +14 "escrever uma arquivo" +15 "salvar arquivo" +16 "editar conteúdo de impressão" +17 "menu localizar" +18 "localizar por ..." +19 "localizar" +20 "menu dicionário" +21 "usar 'spell'" +22 "usar 'ispell'" +23 "menu diversos" +24 "formatação de parágrafo" +25 "comando shell" +26 "checar dicionário" +27 "menu principal" +28 "deixar editor" +29 "ajuda" +30 "operações com arquivos" +31 "redesenhar tela" +32 "configurações" +33 "localizar" +34 "diversos" +35 "Teclas de controle: " +36 "^a código ascii ^i tab ^r direita " +37 "^b botão de texto ^j nova linha ^t início do texto " +38 "^c comando ^k deletar caracter ^u para acima " +39 "^d para baixo ^l esquerda ^v restaurar palavra " +40 "^e localizar prompt ^m nova linha ^w deletar palavra " +41 "^f restaurar caracter ^n próxima página ^x localizar " +42 "^g início da linha ^o fim da linha ^y deletar linha " +43 "^h backspace ^p página anterior ^z restaurar linha " +44 "^[ (escape) menu " +45 " " +46 "Comandos: " +47 "help : obter esta informação file : imprimir nome do arquivo " +48 "read : ler um arquivo char : código ascii de caracter " +49 "write : escrever a arquivo case : localizar com case sensitive " +50 "exit : salva e sair nocase : localizar sem case insensitive " +51 "quit : sair, sem salvar !cmd : executar \"cmd\" no shell " +52 "line : visualizar linhas # 0-9 : ir para linha \"#\" " +53 "expand : expandir tabs noexpand: não expande tabs " +54 " " +55 " ee [+#] [-i] [-e] [-h] [arquivos(s)] " +56 "+# :ir para linha # -i :sem informação da janela -e : não expandir tabs -h :sem destaque " +57 "^[ (escape) menu ^e prompt localizar ^y deletar linha ^u para cima ^p página anterior " +58 "^a código ascii ^x localizar ^z restaurar linha ^d para baixo ^n próxima página " +59 "^b botão de texto ^g início da linha ^w deletar palavra ^l esquerda " +60 "^t início do texto ^o fim da linha ^v restaurar palavra ^r direita " +61 "^c comando ^k deletar caracter ^f restaurar caracter " +62 "help : obter ajuda |file : imprimir nome do arquivo |line : imprimir linha # " +63 "read : ler um arquivo |char : código ascii de caracter |0-9 : ir para linha \"#\"" +64 "write: escrever um arquivo |case : localizar com case sensitive |exit : salvar e sair " +65 "!cmd : shell \"cmd\" |nocase: ignorar case na busca |quit : sair, sem salvar" +66 "expand: expandir tabs |noexpand: não expandir tabs " +67 " pressione Escape (^[) para menu" +68 "sem arquivo" +69 "código ascii: " +70 "enviar conteúdo do buffer para \"%s\" " +71 "comando: " +72 "nome do arquivo para escrever: " +73 "nome do arquivo para leitura: " +74 "caracter = %d" +75 "comando desconhecido \"%s\"" +76 "mais de um comando digitado" +77 "linha %d " +78 "comprimento = %d" +79 "arquivo atual \"%s\" " +80 "uso: %s [-i] [-e] [-h] [+número_da_linha] [arquivo(s)]\n" +81 " -i desligar informações da janela\n" +82 " -e não converter tabs para espaços\n" +83 " -h não usar destaque\n" +84 "arquivo \"%s\" é um diretório" +85 "novo arquivo \"%s\"" +86 "não posso abrir \"%s\"" +87 "arquivos \"%s\", %d linhas" +88 "finalizar leitura de arquivo \"%s\"" +89 "lendo arquivo \"%s\"" +90 ", somente leitura" +91 "arquivo \"%s\", %d linhas" +92 "digite o nome do arquivo: " +93 "nenhum arquivo digitado: arquivo não foi salvo" +94 "Foram feitas alterações, você tem certeza? (y/n [n]) " +95 "y" +96 "arquivo já existe, sobrescrever? (y/n) [n] " +97 "impossível criar arquivo \"%s\"" +98 "escrevendo arquivo \"%s\"" +99 "\"%s\" %d linhas, %d caracteres" +100 " ...localizando" +101 "string \"%s\" não encontrada" +102 "localizar por: " +103 "execução não permitida %s\n" +104 "pressione enter para continuar " +105 "pressione Esc para cancelar" +106 "menu muito grande para a janela" +107 "pressione qualquer tecla para continuar " +108 "comando shell: " +109 "...formatando parágrafo..." +110 "<!echo 'lista de palavras não reconhecidas'; echo -=-=-=-=-=-" +111 "enviando conteúdo no buffer do editor para 'correção'" +112 "margem direita é: " +113 "modo restrito: incapaz de executar a operação solicitada" +114 "LIGADO" +115 "DESLIGADO" +116 "AJUDA" +117 "ESCRITA" +118 "LEITURA" +119 "LINHA" +120 "ARQUIVO" +121 "CARACTER" +122 "REDESENHAR" +123 "RESEQUENCIA" +124 "AUTOR" +125 "VERSÃO" +126 "CASE" +127 "SEMCASE" +128 "EXPANDIR" +129 "NÃOEXPANDIR" +130 "SAIRSALVANDO" +131 "SAIRSEMSALVAR" +132 "INFO" +133 "SEMINFO" +134 "MARGENS" +135 "SEMMARGENS" +136 "AUTOFORMATAÇÃO" +137 "SEMAUTOFORMATAÇÃO" +138 "ECHO" +139 "IMPRIMIRCOMANDO" +140 "MARGEMDIREITA" +141 "DESTAQUE" +142 "SEMDESTAQUE" +143 "OITOBIT" +144 "SEMOITOBIT" +145 "vínculo com teclas emacs " +146 "^a início da linha ^i tab ^r restaurar palavra " +147 "^b voltar 1 caracter ^j resturar caracter ^t início do texto " +148 "^c comando ^k deletar linha ^u botão de texto " +149 "^d deletar caracter ^l restaurar linha ^v próxima página " +150 "^e fim da linha ^m nova linha ^w deletar palavra " +151 "^f avançar 1 caracter ^n próxima linha ^x localizar " +152 "^g voltar 1 página ^o inserir caracter ascii ^y prompt localizar " +153 "^h backspace ^p linha anterior ^z próxima palavra " +154 "^[ (escape) menu ^y prompt localizar ^k deletar linha ^p linha anterior ^g página anterior" +155 "^o código ascii ^x localizar ^l restaurar linha ^n próxima linha ^v próxima página " +156 "^u fim do arquivo ^a início da linha ^w deletar palavra ^b regressar 1 caracter " +157 "^t início do texto ^e fim da linha ^r restaurar palavra ^f avançar 1 caracter " +158 "^c comando ^d deletar caracter ^j restaurar caracter ^z próxima palavra " +159 "EMACS" +160 "SEMEMACS" +161 " +# colocar cursor na linha #\n" +162 "impossível abrir .init.ee para escrita, nenhuma configuração salva!" +163 "ee configuração salva no arquivo %s" +164 "salvar configurações do editor" +165 "salvar configurações do ee" +166 "salvar no diretório corrente" +167 "salvar no diretório home" +168 "configurações do ee não salvas" +169 "ao invocar ree, deve-se especificar um arquivo" +180 "menu muito grande para a janela" +181 "^^mais^^" +182 "VVmaisVV" +183 "caracteres de 16 bit " +184 "16BIT" +185 "SEM16BIT" diff --git a/usr.bin/elf2aout/elf2aout.1 b/usr.bin/elf2aout/elf2aout.1 index 7b153e9..0f4be22 100644 --- a/usr.bin/elf2aout/elf2aout.1 +++ b/usr.bin/elf2aout/elf2aout.1 @@ -32,7 +32,7 @@ .Nd "Convert ELF binary to a.out format" .Sh SYNOPSIS .Nm -.Op Fl o outfile +.Op Fl o Ar outfile .Ar infile .Sh DESCRIPTION The diff --git a/usr.bin/elf2aout/elf2aout.c b/usr.bin/elf2aout/elf2aout.c index 4168f4e..7e1ece6 100644 --- a/usr.bin/elf2aout/elf2aout.c +++ b/usr.bin/elf2aout/elf2aout.c @@ -35,6 +35,8 @@ __FBSDID("$FreeBSD$"); #include <err.h> #include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -155,5 +157,6 @@ static void usage(void) { - errx(1, "usage: elf2aout [-o outfile] infile"); + fprintf(stderr, "usage: elf2aout [-o outfile] infile\n"); + exit(1); } diff --git a/usr.bin/find/Makefile b/usr.bin/find/Makefile index 1208260..0c7bb70 100644 --- a/usr.bin/find/Makefile +++ b/usr.bin/find/Makefile @@ -4,7 +4,6 @@ PROG= find SRCS= find.c function.c ls.c main.c misc.c operator.c option.c \ getdate.y -CFLAGS+= -DHAVE_SYS_TIMEB_H -I${.CURDIR} YFLAGS= .include <bsd.prog.mk> diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h index 716c1f5..cc6143c 100644 --- a/usr.bin/find/extern.h +++ b/usr.bin/find/extern.h @@ -43,8 +43,7 @@ PLAN *find_formplan(char **); PLAN *not_squish(PLAN *); PLAN *or_squish(PLAN *); PLAN *paren_squish(PLAN *); -struct timeb; -time_t get_date(char *, struct timeb *); +time_t get_date(char *); struct stat; void printlong(char *, char *, struct stat *); int queryuser(char **); diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c index 5d8ad3d..51d5153 100644 --- a/usr.bin/find/function.c +++ b/usr.bin/find/function.c @@ -50,7 +50,6 @@ __FBSDID("$FreeBSD$"); #include <sys/acl.h> #include <sys/wait.h> #include <sys/mount.h> -#include <sys/timeb.h> #include <dirent.h> #include <err.h> @@ -1155,7 +1154,7 @@ c_newer(OPTION *option, char ***argvp) new = palloc(option); /* compare against what */ if (option->flags & F_TIME2_T) { - new->t_data = get_date(fn_or_tspec, (struct timeb *) 0); + new->t_data = get_date(fn_or_tspec); if (new->t_data == (time_t) -1) errx(1, "Can't parse date/time: %s", fn_or_tspec); } else { diff --git a/usr.bin/find/getdate.y b/usr.bin/find/getdate.y index de7750c..81a9c47 100644 --- a/usr.bin/find/getdate.y +++ b/usr.bin/find/getdate.y @@ -28,7 +28,6 @@ __FBSDID("$FreeBSD$"); #else /* defined(vms) */ # include <sys/types.h> # include <sys/time.h> -# include <sys/timeb.h> #endif /* !defined(vms) */ #if defined (__STDC__) || defined (USG) @@ -69,7 +68,7 @@ static int yyparse(void); static int yylex(void); static int yyerror(const char *); -time_t get_date(char *, struct timeb *); +time_t get_date(char *); #define EPOCH 1970 #define HOUR(x) ((time_t)(x) * 60) @@ -849,58 +848,50 @@ difftm (struct tm *a, struct tm *b) } time_t -get_date(char *p, struct timeb *now) +get_date(char *p) { - struct tm *tm, gmt; - struct timeb ftz; + struct tm *tm, *gmt_ptr, gmt; + int tzoff; time_t Start; time_t tod; time_t nowtime; bzero (&gmt, sizeof(struct tm)); yyInput = p; - if (now == NULL) { - struct tm *gmt_ptr; - now = &ftz; - (void)time (&nowtime); + (void)time (&nowtime); - gmt_ptr = gmtime (&nowtime); - if (gmt_ptr != NULL) - { - /* Make a copy, in case localtime modifies *tm (I think - that comment now applies to *gmt_ptr, but I am too - lazy to dig into how gmtime and locatime allocate the - structures they return pointers to). */ - gmt = *gmt_ptr; - } + gmt_ptr = gmtime (&nowtime); + if (gmt_ptr != NULL) + { + /* Make a copy, in case localtime modifies *tm (I think + that comment now applies to *gmt_ptr, but I am too + lazy to dig into how gmtime and locatime allocate the + structures they return pointers to). */ + gmt = *gmt_ptr; + } - if (! (tm = localtime (&nowtime))) - return -1; + if (! (tm = localtime (&nowtime))) + return -1; - if (gmt_ptr != NULL) - ftz.timezone = difftm (&gmt, tm) / 60; - else - /* We are on a system like VMS, where the system clock is - in local time and the system has no concept of timezones. - Hopefully we can fake this out (for the case in which the - user specifies no timezone) by just saying the timezone - is zero. */ - ftz.timezone = 0; - - if(tm->tm_isdst) - ftz.timezone += 60; - } + if (gmt_ptr != NULL) + tzoff = difftm (&gmt, tm) / 60; else - { - nowtime = now->time; - } + /* We are on a system like VMS, where the system clock is + in local time and the system has no concept of timezones. + Hopefully we can fake this out (for the case in which the + user specifies no timezone) by just saying the timezone + is zero. */ + tzoff = 0; + + if(tm->tm_isdst) + tzoff += 60; tm = localtime(&nowtime); yyYear = tm->tm_year + 1900; yyMonth = tm->tm_mon + 1; yyDay = tm->tm_mday; - yyTimezone = now->timezone; + yyTimezone = tzoff; yyDSTmode = DSTmaybe; yyHour = 0; yyMinutes = 0; @@ -956,7 +947,7 @@ main(int ac, char *av[]) (void)printf("Enter date, or blank line to exit.\n\t> "); (void)fflush(stdout); while (gets(buff) && buff[0]) { - d = get_date(buff, (struct timeb *)NULL); + d = get_date(buff); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else diff --git a/usr.bin/uname/uname.1 b/usr.bin/uname/uname.1 index 332ce6e..f78e437c 100644 --- a/usr.bin/uname/uname.1 +++ b/usr.bin/uname/uname.1 @@ -40,7 +40,7 @@ .Nd display information about the system .Sh SYNOPSIS .Nm -.Op Fl aimnprsv +.Op Fl aimnoprsv .Sh DESCRIPTION The .Nm diff --git a/usr.bin/uname/uname.c b/usr.bin/uname/uname.c index a33b557..0e120b9 100644 --- a/usr.bin/uname/uname.c +++ b/usr.bin/uname/uname.c @@ -245,6 +245,6 @@ NATIVE_SYSCTLNAME_GET(ident, "kern.ident") { void usage(void) { - fprintf(stderr, "usage: uname [-aimnprsv]\n"); + fprintf(stderr, "usage: uname [-aimnoprsv]\n"); exit(1); } diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c index 0612cd8..fb97e8a 100644 --- a/usr.bin/w/w.c +++ b/usr.bin/w/w.c @@ -158,7 +158,8 @@ main(int argc, char *argv[]) } dropgid = 0; - memf = nlistf = _PATH_DEVNULL; + memf = _PATH_DEVNULL; + nlistf = NULL; while ((ch = getopt(argc, argv, p)) != -1) switch (ch) { case 'd': diff --git a/usr.sbin/pmcstat/Makefile b/usr.sbin/pmcstat/Makefile index c115c42..a29b016 100644 --- a/usr.sbin/pmcstat/Makefile +++ b/usr.sbin/pmcstat/Makefile @@ -6,8 +6,9 @@ PROG= pmcstat MAN= pmcstat.8 DPADD= ${LIBELF} ${LIBKVM} ${LIBPMC} ${LIBM} -LDADD= -lelf -lkvm -lpmc -lm +LDADD= -lelf -lkvm -lpmc -lm -lncurses -SRCS= pmcstat.c pmcstat.h pmcstat_log.c +SRCS= pmcstat.c pmcstat.h pmcstat_log.c \ +pmcpl_callgraph.c pmcpl_gprof.c pmcpl_annotate.c pmcpl_calltree.c .include <bsd.prog.mk> diff --git a/usr.sbin/pmcstat/pmcpl_annotate.c b/usr.sbin/pmcstat/pmcpl_annotate.c new file mode 100644 index 0000000..802983c --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_annotate.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2005-2007, Joseph Koshy + * Copyright (c) 2007 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Transform a hwpmc(4) log into human readable form, and into + * gprof(1) compatible profiles. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/gmon.h> +#include <sys/imgact_aout.h> +#include <sys/imgact_elf.h> +#include <sys/mman.h> +#include <sys/pmc.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <gelf.h> +#include <libgen.h> +#include <limits.h> +#include <netdb.h> +#include <pmc.h> +#include <pmclog.h> +#include <sysexits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pmcstat.h" +#include "pmcstat_log.h" +#include "pmcpl_annotate.h" + +/* + * Record a callchain. + */ + +void +pmcpl_annotate_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) +{ + struct pmcstat_pcmap *map; + struct pmcstat_symbol *sym; + uintfptr_t newpc; + struct pmcstat_image *image; + + (void) pmcr; (void) nsamples; (void) usermode; (void) cpu; + + map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[0]); + if (map == NULL) { + /* Unknown offset. */ + pmcstat_stats.ps_samples_unknown_offset++; + return; + } + + assert(cc[0] >= map->ppm_lowpc && cc[0] < map->ppm_highpc); + + image = map->ppm_image; + newpc = cc[0] - (map->ppm_lowpc + + (image->pi_vaddr - image->pi_start)); + sym = pmcstat_symbol_search(image, newpc); + if (sym == NULL) + return; + + fprintf(args.pa_graphfile, "%p %s 0x%jx 0x%jx\n", + (void *)cc[0], + pmcstat_string_unintern(sym->ps_name), + (uintmax_t)(sym->ps_start + + image->pi_vaddr), (uintmax_t)(sym->ps_end + + image->pi_vaddr)); +} diff --git a/usr.sbin/pmcstat/pmcpl_annotate.h b/usr.sbin/pmcstat/pmcpl_annotate.h new file mode 100644 index 0000000..482bcd4 --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_annotate.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2005-2007, Joseph Koshy + * Copyright (c) 2007 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PMCSTAT_PL_ANNOTATE_H_ +#define _PMCSTAT_PL_ANNOTATE_H_ + +/* Function prototypes */ +void pmcpl_annotate_process( + struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu); + +#endif /* _PMCSTAT_PL_ANNOTATE_H_ */ diff --git a/usr.sbin/pmcstat/pmcpl_callgraph.c b/usr.sbin/pmcstat/pmcpl_callgraph.c new file mode 100644 index 0000000..d6f1a9d --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_callgraph.c @@ -0,0 +1,682 @@ +/*- + * Copyright (c) 2005-2007, Joseph Koshy + * Copyright (c) 2007 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Transform a hwpmc(4) log into human readable form, and into + * gprof(1) compatible profiles. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/gmon.h> +#include <sys/imgact_aout.h> +#include <sys/imgact_elf.h> +#include <sys/mman.h> +#include <sys/pmc.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <assert.h> +#include <curses.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <gelf.h> +#include <libgen.h> +#include <limits.h> +#include <netdb.h> +#include <pmc.h> +#include <pmclog.h> +#include <sysexits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pmcstat.h" +#include "pmcstat_log.h" +#include "pmcstat_top.h" +#include "pmcpl_callgraph.h" + +/* Get the sample value in percent related to nsamples. */ +#define PMCPL_CG_COUNTP(a) \ + ((a)->pcg_count * 100.0 / nsamples) + +/* + * The toplevel CG nodes (i.e., with rank == 0) are placed in a hash table. + */ + +struct pmcstat_cgnode_hash_list pmcstat_cgnode_hash[PMCSTAT_NHASH]; +int pmcstat_cgnode_hash_count; + +static pmcstat_interned_string pmcstat_previous_filename_printed; + +static struct pmcstat_cgnode * +pmcstat_cgnode_allocate(struct pmcstat_image *image, uintfptr_t pc) +{ + struct pmcstat_cgnode *cg; + + if ((cg = malloc(sizeof(*cg))) == NULL) + err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); + + cg->pcg_image = image; + cg->pcg_func = pc; + + cg->pcg_count = 0; + cg->pcg_nchildren = 0; + LIST_INIT(&cg->pcg_children); + + return (cg); +} + +/* + * Free a node and its children. + */ +static void +pmcstat_cgnode_free(struct pmcstat_cgnode *cg) +{ + struct pmcstat_cgnode *cgc, *cgtmp; + + LIST_FOREACH_SAFE(cgc, &cg->pcg_children, pcg_sibling, cgtmp) + pmcstat_cgnode_free(cgc); + free(cg); +} + +/* + * Look for a callgraph node associated with pmc `pmcid' in the global + * hash table that corresponds to the given `pc' value in the process + * `pp'. + */ +static struct pmcstat_cgnode * +pmcstat_cgnode_hash_lookup_pc(struct pmcstat_process *pp, pmc_id_t pmcid, + uintfptr_t pc, int usermode) +{ + struct pmcstat_pcmap *ppm; + struct pmcstat_symbol *sym; + struct pmcstat_image *image; + struct pmcstat_cgnode *cg; + struct pmcstat_cgnode_hash *h; + uintfptr_t loadaddress; + unsigned int i, hash; + + ppm = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, pc); + if (ppm == NULL) + return (NULL); + + image = ppm->ppm_image; + + loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start; + pc -= loadaddress; /* Convert to an offset in the image. */ + + /* + * Try determine the function at this offset. If we can't + * find a function round leave the `pc' value alone. + */ + if ((sym = pmcstat_symbol_search(image, pc)) != NULL) + pc = sym->ps_start; + + for (hash = i = 0; i < sizeof(uintfptr_t); i++) + hash += (pc >> i) & 0xFF; + + hash &= PMCSTAT_HASH_MASK; + + cg = NULL; + LIST_FOREACH(h, &pmcstat_cgnode_hash[hash], pch_next) + { + if (h->pch_pmcid != pmcid) + continue; + + cg = h->pch_cgnode; + + assert(cg != NULL); + + if (cg->pcg_image == image && cg->pcg_func == pc) + return (cg); + } + + /* + * We haven't seen this (pmcid, pc) tuple yet, so allocate a + * new callgraph node and a new hash table entry for it. + */ + cg = pmcstat_cgnode_allocate(image, pc); + if ((h = malloc(sizeof(*h))) == NULL) + err(EX_OSERR, "ERROR: Could not allocate callgraph node"); + + h->pch_pmcid = pmcid; + h->pch_cgnode = cg; + LIST_INSERT_HEAD(&pmcstat_cgnode_hash[hash], h, pch_next); + + pmcstat_cgnode_hash_count++; + + return (cg); +} + +/* + * Compare two callgraph nodes for sorting. + */ +static int +pmcstat_cgnode_compare(const void *a, const void *b) +{ + const struct pmcstat_cgnode *const *pcg1, *const *pcg2, *cg1, *cg2; + + pcg1 = (const struct pmcstat_cgnode *const *) a; + cg1 = *pcg1; + pcg2 = (const struct pmcstat_cgnode *const *) b; + cg2 = *pcg2; + + /* Sort in reverse order */ + if (cg1->pcg_count < cg2->pcg_count) + return (1); + if (cg1->pcg_count > cg2->pcg_count) + return (-1); + return (0); +} + +/* + * Find (allocating if a needed) a callgraph node in the given + * parent with the same (image, pcoffset) pair. + */ + +static struct pmcstat_cgnode * +pmcstat_cgnode_find(struct pmcstat_cgnode *parent, struct pmcstat_image *image, + uintfptr_t pcoffset) +{ + struct pmcstat_cgnode *child; + + LIST_FOREACH(child, &parent->pcg_children, pcg_sibling) { + if (child->pcg_image == image && + child->pcg_func == pcoffset) + return (child); + } + + /* + * Allocate a new structure. + */ + + child = pmcstat_cgnode_allocate(image, pcoffset); + + /* + * Link it into the parent. + */ + LIST_INSERT_HEAD(&parent->pcg_children, child, pcg_sibling); + parent->pcg_nchildren++; + + return (child); +} + +/* + * Print one callgraph node. The output format is: + * + * indentation %(parent's samples) #nsamples function@object + */ +static void +pmcstat_cgnode_print(struct pmcstat_cgnode *cg, int depth, uint32_t total) +{ + uint32_t n; + const char *space; + struct pmcstat_symbol *sym; + struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; + + space = " "; + + if (depth > 0) + (void) fprintf(args.pa_graphfile, "%*s", depth, space); + + if (cg->pcg_count == total) + (void) fprintf(args.pa_graphfile, "100.0%% "); + else + (void) fprintf(args.pa_graphfile, "%05.2f%% ", + 100.0 * cg->pcg_count / total); + + n = fprintf(args.pa_graphfile, " [%u] ", cg->pcg_count); + + /* #samples is a 12 character wide field. */ + if (n < 12) + (void) fprintf(args.pa_graphfile, "%*s", 12 - n, space); + + if (depth > 0) + (void) fprintf(args.pa_graphfile, "%*s", depth, space); + + sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); + if (sym) + (void) fprintf(args.pa_graphfile, "%s", + pmcstat_string_unintern(sym->ps_name)); + else + (void) fprintf(args.pa_graphfile, "%p", + (void *) (cg->pcg_image->pi_vaddr + cg->pcg_func)); + + if (pmcstat_previous_filename_printed != + cg->pcg_image->pi_fullpath) { + pmcstat_previous_filename_printed = cg->pcg_image->pi_fullpath; + (void) fprintf(args.pa_graphfile, " @ %s\n", + pmcstat_string_unintern( + pmcstat_previous_filename_printed)); + } else + (void) fprintf(args.pa_graphfile, "\n"); + + if (cg->pcg_nchildren == 0) + return; + + if ((sortbuffer = (struct pmcstat_cgnode **) + malloc(sizeof(struct pmcstat_cgnode *) * + cg->pcg_nchildren)) == NULL) + err(EX_OSERR, "ERROR: Cannot print callgraph"); + cgn = sortbuffer; + + LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) + *cgn++ = pcg; + + assert(cgn - sortbuffer == (int) cg->pcg_nchildren); + + qsort(sortbuffer, cg->pcg_nchildren, sizeof(struct pmcstat_cgnode *), + pmcstat_cgnode_compare); + + for (cgn = sortbuffer, n = 0; n < cg->pcg_nchildren; n++, cgn++) + pmcstat_cgnode_print(*cgn, depth+1, cg->pcg_count); + + free(sortbuffer); +} + +/* + * Record a callchain. + */ + +void +pmcpl_cg_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) +{ + uintfptr_t pc, loadaddress; + uint32_t n; + struct pmcstat_image *image; + struct pmcstat_pcmap *ppm; + struct pmcstat_symbol *sym; + struct pmcstat_cgnode *parent, *child; + struct pmcstat_process *km; + pmc_id_t pmcid; + + (void) cpu; + + /* + * Find the callgraph node recorded in the global hash table + * for this (pmcid, pc). + */ + + pc = cc[0]; + pmcid = pmcr->pr_pmcid; + parent = pmcstat_cgnode_hash_lookup_pc(pp, pmcid, pc, usermode); + if (parent == NULL) { + pmcstat_stats.ps_callchain_dubious_frames++; + return; + } + + parent->pcg_count++; + + /* + * For each return address in the call chain record, subject + * to the maximum depth desired. + * - Find the image associated with the sample. Stop if there + * there is no valid image at that address. + * - Find the function that overlaps the return address. + * - If found: use the start address of the function. + * If not found (say an object's symbol table is not present or + * is incomplete), round down to th gprof bucket granularity. + * - Convert return virtual address to an offset in the image. + * - Look for a child with the same {offset,image} tuple, + * inserting one if needed. + * - Increment the count of occurrences of the child. + */ + km = pmcstat_kernproc; + + for (n = 1; n < (uint32_t) args.pa_graphdepth && n < nsamples; n++, + parent = child) { + pc = cc[n]; + + ppm = pmcstat_process_find_map(usermode ? pp : km, pc); + if (ppm == NULL) { + /* Detect full frame capture (kernel + user). */ + if (!usermode) { + ppm = pmcstat_process_find_map(pp, pc); + if (ppm != NULL) + km = pp; + } + } + if (ppm == NULL) + return; + + image = ppm->ppm_image; + loadaddress = ppm->ppm_lowpc + image->pi_vaddr - + image->pi_start; + pc -= loadaddress; + + if ((sym = pmcstat_symbol_search(image, pc)) != NULL) + pc = sym->ps_start; + + child = pmcstat_cgnode_find(parent, image, pc); + child->pcg_count++; + } +} + +/* + * Printing a callgraph for a PMC. + */ +static void +pmcstat_callgraph_print_for_pmcid(struct pmcstat_pmcrecord *pmcr) +{ + int n, nentries; + uint32_t nsamples; + pmc_id_t pmcid; + struct pmcstat_cgnode **sortbuffer, **cgn; + struct pmcstat_cgnode_hash *pch; + + /* + * We pull out all callgraph nodes in the top-level hash table + * with a matching PMC id. We then sort these based on the + * frequency of occurrence. Each callgraph node is then + * printed. + */ + + nsamples = 0; + pmcid = pmcr->pr_pmcid; + if ((sortbuffer = (struct pmcstat_cgnode **) + malloc(sizeof(struct pmcstat_cgnode *) * + pmcstat_cgnode_hash_count)) == NULL) + err(EX_OSERR, "ERROR: Cannot sort callgraph"); + cgn = sortbuffer; + + for (n = 0; n < PMCSTAT_NHASH; n++) + LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) + if (pch->pch_pmcid == pmcid) { + nsamples += pch->pch_cgnode->pcg_count; + *cgn++ = pch->pch_cgnode; + } + + nentries = cgn - sortbuffer; + assert(nentries <= pmcstat_cgnode_hash_count); + + if (nentries == 0) { + free(sortbuffer); + return; + } + + qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), + pmcstat_cgnode_compare); + + (void) fprintf(args.pa_graphfile, + "@ %s [%u samples]\n\n", + pmcstat_string_unintern(pmcr->pr_pmcname), + nsamples); + + for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { + pmcstat_previous_filename_printed = NULL; + pmcstat_cgnode_print(*cgn, 0, nsamples); + (void) fprintf(args.pa_graphfile, "\n"); + } + + free(sortbuffer); +} + +/* + * Print out callgraphs. + */ + +static void +pmcstat_callgraph_print(void) +{ + struct pmcstat_pmcrecord *pmcr; + + LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) + pmcstat_callgraph_print_for_pmcid(pmcr); +} + +static void +pmcstat_cgnode_topprint(struct pmcstat_cgnode *cg, + int depth, uint32_t nsamples) +{ + int v_attrs, vs_len, ns_len, width, len, n, nchildren; + float v; + char ns[30], vs[10]; + struct pmcstat_symbol *sym; + struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; + + (void) depth; + + /* Format value. */ + v = PMCPL_CG_COUNTP(cg); + snprintf(vs, sizeof(vs), "%.1f", v); + v_attrs = PMCSTAT_ATTRPERCENT(v); + + /* Format name. */ + sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); + if (sym != NULL) { + snprintf(ns, sizeof(ns), "%s", + pmcstat_string_unintern(sym->ps_name)); + } else + snprintf(ns, sizeof(ns), "%p", + (void *)cg->pcg_func); + + PMCSTAT_ATTRON(v_attrs); + PMCSTAT_PRINTW("%5.5s", vs); + PMCSTAT_ATTROFF(v_attrs); + PMCSTAT_PRINTW(" %-10.10s %-20.20s", + pmcstat_string_unintern(cg->pcg_image->pi_name), + ns); + + nchildren = cg->pcg_nchildren; + if (nchildren == 0) { + PMCSTAT_PRINTW("\n"); + return; + } + + width = pmcstat_displaywidth - 40; + + if ((sortbuffer = (struct pmcstat_cgnode **) + malloc(sizeof(struct pmcstat_cgnode *) * + nchildren)) == NULL) + err(EX_OSERR, "ERROR: Cannot print callgraph"); + cgn = sortbuffer; + + LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) + *cgn++ = pcg; + + assert(cgn - sortbuffer == (int)nchildren); + + qsort(sortbuffer, nchildren, sizeof(struct pmcstat_cgnode *), + pmcstat_cgnode_compare); + + /* Count how many callers. */ + for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) { + pcg = *cgn; + + v = PMCPL_CG_COUNTP(pcg); + if (v < pmcstat_threshold) + break; + } + nchildren = n; + + for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) { + pcg = *cgn; + + /* Format value. */ + if (nchildren > 1) { + v = PMCPL_CG_COUNTP(pcg); + vs_len = snprintf(vs, sizeof(vs), ":%.1f", v); + v_attrs = PMCSTAT_ATTRPERCENT(v); + } else + vs_len = 0; + + /* Format name. */ + sym = pmcstat_symbol_search(pcg->pcg_image, pcg->pcg_func); + if (sym != NULL) { + ns_len = snprintf(ns, sizeof(ns), "%s", + pmcstat_string_unintern(sym->ps_name)); + } else + ns_len = snprintf(ns, sizeof(ns), "%p", + (void *)pcg->pcg_func); + + len = ns_len + vs_len + 1; + if (width - len < 0) { + PMCSTAT_PRINTW("..."); + break; + } + width -= len; + + PMCSTAT_PRINTW(" %s", ns); + if (nchildren > 1) { + PMCSTAT_ATTRON(v_attrs); + PMCSTAT_PRINTW("%s", vs); + PMCSTAT_ATTROFF(v_attrs); + } + } + PMCSTAT_PRINTW("\n"); + free(sortbuffer); +} + +/* + * Top mode display. + */ + +void +pmcpl_cg_topdisplay(void) +{ + int n, nentries; + uint32_t nsamples; + struct pmcstat_cgnode **sortbuffer, **cgn; + struct pmcstat_cgnode_hash *pch; + struct pmcstat_pmcrecord *pmcr; + + pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); + + /* + * We pull out all callgraph nodes in the top-level hash table + * with a matching PMC index. We then sort these based on the + * frequency of occurrence. Each callgraph node is then + * printed. + */ + + nsamples = 0; + + if ((sortbuffer = (struct pmcstat_cgnode **) + malloc(sizeof(struct pmcstat_cgnode *) * + pmcstat_cgnode_hash_count)) == NULL) + err(EX_OSERR, "ERROR: Cannot sort callgraph"); + cgn = sortbuffer; + + for (n = 0; n < PMCSTAT_NHASH; n++) + LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) + if (pmcr == NULL || pch->pch_pmcid == pmcr->pr_pmcid) { + nsamples += pch->pch_cgnode->pcg_count; + *cgn++ = pch->pch_cgnode; + } + + nentries = cgn - sortbuffer; + assert(nentries <= pmcstat_cgnode_hash_count); + + if (nentries == 0) { + free(sortbuffer); + return; + } + + qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), + pmcstat_cgnode_compare); + + PMCSTAT_PRINTW("%5.5s %-10.10s %-20.20s %s\n", + "%SAMP", "IMAGE", "FUNCTION", "CALLERS"); + + nentries = min(pmcstat_displayheight - 2, nentries); + + for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { + if (PMCPL_CG_COUNTP(*cgn) < pmcstat_threshold) + break; + pmcstat_cgnode_topprint(*cgn, 0, nsamples); + } + + free(sortbuffer); +} + +/* + * Handle top mode keypress. + */ + +int +pmcpl_cg_topkeypress(int c, WINDOW *w) +{ + + (void) c; (void) w; + + return 0; +} + +int +pmcpl_cg_init(void) +{ + int i; + + pmcstat_cgnode_hash_count = 0; + pmcstat_previous_filename_printed = NULL; + + for (i = 0; i < PMCSTAT_NHASH; i++) { + LIST_INIT(&pmcstat_cgnode_hash[i]); + } + + return (0); +} + +void +pmcpl_cg_shutdown(FILE *mf) +{ + int i; + struct pmcstat_cgnode_hash *pch, *pchtmp; + + (void) mf; + + if (args.pa_flags & FLAG_DO_CALLGRAPHS) + pmcstat_callgraph_print(); + + /* + * Free memory. + */ + for (i = 0; i < PMCSTAT_NHASH; i++) { + LIST_FOREACH_SAFE(pch, &pmcstat_cgnode_hash[i], pch_next, + pchtmp) { + pmcstat_cgnode_free(pch->pch_cgnode); + LIST_REMOVE(pch, pch_next); + free(pch); + } + } +} + diff --git a/usr.sbin/pmcstat/pmcpl_callgraph.h b/usr.sbin/pmcstat/pmcpl_callgraph.h new file mode 100644 index 0000000..aaf0e1b --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_callgraph.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2005-2007, Joseph Koshy + * Copyright (c) 2007 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PMCSTAT_PL_CALLGRAPH_H_ +#define _PMCSTAT_PL_CALLGRAPH_H_ + +/* + * Each call graph node is tracked by a pmcstat_cgnode struct. + */ + +struct pmcstat_cgnode { + struct pmcstat_image *pcg_image; + uintfptr_t pcg_func; + uint32_t pcg_count; + uint32_t pcg_nchildren; + LIST_ENTRY(pmcstat_cgnode) pcg_sibling; + LIST_HEAD(,pmcstat_cgnode) pcg_children; +}; + +struct pmcstat_cgnode_hash { + struct pmcstat_cgnode *pch_cgnode; + pmc_id_t pch_pmcid; + LIST_ENTRY(pmcstat_cgnode_hash) pch_next; +}; +extern LIST_HEAD(pmcstat_cgnode_hash_list, pmcstat_cgnode_hash) pmcstat_cgnode_hash[PMCSTAT_NHASH]; +extern int pmcstat_cgnode_hash_count; + +/* Function prototypes */ +int pmcpl_cg_init(void); +void pmcpl_cg_shutdown(FILE *mf); +void pmcpl_cg_process( + struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu); +int pmcpl_cg_topkeypress(int c, WINDOW *w); +void pmcpl_cg_topdisplay(void); +void pmcpl_cg_configure(char *opt); + +#endif /* _PMCSTAT_PL_CALLGRAPH_H_ */ diff --git a/usr.sbin/pmcstat/pmcpl_calltree.c b/usr.sbin/pmcstat/pmcpl_calltree.c new file mode 100644 index 0000000..498092d --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_calltree.c @@ -0,0 +1,1000 @@ +/*- + * Copyright (c) 2009, Fabien Thomas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Process hwpmc(4) samples as calltree. + * + * Output file format compatible with Kcachegrind (kdesdk). + * Handle top mode with a sorted tree display. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/queue.h> + +#include <assert.h> +#include <curses.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <pmc.h> +#include <pmclog.h> +#include <sysexits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sysexits.h> + +#include "pmcstat.h" +#include "pmcstat_log.h" +#include "pmcstat_top.h" +#include "pmcpl_calltree.h" + +#define PMCPL_CT_GROWSIZE 4 + +static pmcstat_interned_string pmcpl_ct_prevfn; + +static int pmcstat_skiplink = 0; + +struct pmcpl_ct_node; + +/* Get the sample value for PMC a. */ +#define PMCPL_CT_SAMPLE(a, b) \ + ((a) < (b)->npmcs ? (b)->sb[a] : 0) + +/* Get the sample value in percent related to rsamples. */ +#define PMCPL_CT_SAMPLEP(a, b) \ + (PMCPL_CT_SAMPLE(a, b) * 100.0 / rsamples->sb[a]) + +struct pmcpl_ct_sample { + int npmcs; /* Max pmc index available. */ + unsigned *sb; /* Sample buffer for 0..npmcs. */ +}; + +struct pmcpl_ct_arc { + struct pmcpl_ct_sample pcta_samples; + struct pmcpl_ct_sample pcta_callid; + unsigned pcta_call; + struct pmcpl_ct_node *pcta_child; +}; + +struct pmcpl_ct_instr { + uintfptr_t pctf_func; + struct pmcpl_ct_sample pctf_samples; +}; + +/* + * Each calltree node is tracked by a pmcpl_ct_node struct. + */ +struct pmcpl_ct_node { +#define PMCPL_PCT_TAG 0x00000001 /* Loop detection. */ + uint32_t pct_flags; + struct pmcstat_image *pct_image; + uintfptr_t pct_func; + struct pmcpl_ct_sample pct_samples; + + int pct_narc; + int pct_arc_c; + struct pmcpl_ct_arc *pct_arc; + + /* TODO: optimize for large number of items. */ + int pct_ninstr; + int pct_instr_c; + struct pmcpl_ct_instr *pct_instr; +}; + +struct pmcpl_ct_node_hash { + struct pmcpl_ct_node *pch_ctnode; + LIST_ENTRY(pmcpl_ct_node_hash) pch_next; +}; + +struct pmcpl_ct_sample pmcpl_ct_callid; + +#define PMCPL_CT_MAXCOL PMC_CALLCHAIN_DEPTH_MAX +#define PMCPL_CT_MAXLINE 256 +struct pmcpl_ct_node *pmcpl_ct_topscreen[PMCPL_CT_MAXCOL][PMCPL_CT_MAXLINE]; + +/* + * All nodes indexed by function/image name are placed in a hash table. + */ +static LIST_HEAD(,pmcpl_ct_node_hash) pmcpl_ct_node_hash[PMCSTAT_NHASH]; + +/* + * Root node for the graph. + */ +static struct pmcpl_ct_node *pmcpl_ct_root; + +/* + * Prototypes + */ + +/* + * Initialize a samples. + */ + +static void +pmcpl_ct_samples_init(struct pmcpl_ct_sample *samples) +{ + + samples->npmcs = 0; + samples->sb = NULL; +} + +/* + * Free a samples. + */ + +static void +pmcpl_ct_samples_free(struct pmcpl_ct_sample *samples) +{ + + samples->npmcs = 0; + free(samples->sb); + samples->sb = NULL; +} + +/* + * Grow a sample block to store pmcstat_npmcs PMCs. + */ + +static void +pmcpl_ct_samples_grow(struct pmcpl_ct_sample *samples) +{ + int npmcs; + + /* Enough storage. */ + if (pmcstat_npmcs <= samples->npmcs) + return; + + npmcs = samples->npmcs + + max(pmcstat_npmcs - samples->npmcs, PMCPL_CT_GROWSIZE); + samples->sb = realloc(samples->sb, npmcs * sizeof(unsigned)); + if (samples->sb == NULL) + errx(EX_SOFTWARE, "ERROR: out of memory"); + bzero((char *)samples->sb + samples->npmcs * sizeof(unsigned), + (npmcs - samples->npmcs) * sizeof(unsigned)); + samples->npmcs = npmcs; +} + +/* + * Compute the sum of all root arcs. + */ + +static void +pmcpl_ct_samples_root(struct pmcpl_ct_sample *samples) +{ + int i, pmcin; + + pmcpl_ct_samples_init(samples); + pmcpl_ct_samples_grow(samples); + + for (i = 0; i < pmcpl_ct_root->pct_narc; i++) + for (pmcin = 0; pmcin < pmcstat_npmcs; pmcin++) + samples->sb[pmcin] += PMCPL_CT_SAMPLE(pmcin, + &pmcpl_ct_root->pct_arc[i].pcta_samples); +} + +/* + * Grow the arc table. + */ + +static void +pmcpl_ct_arc_grow(int cursize, int *maxsize, struct pmcpl_ct_arc **items) +{ + int nmaxsize; + + if (cursize < *maxsize) + return; + + nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE); + *items = realloc(*items, nmaxsize * sizeof(struct pmcpl_ct_arc)); + if (*items == NULL) + errx(EX_SOFTWARE, "ERROR: out of memory"); + bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_arc), + (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_arc)); + *maxsize = nmaxsize; +} + +/* + * Compare two arc by samples value. + */ +static int +pmcpl_ct_arc_compare(void *thunk, const void *a, const void *b) +{ + const struct pmcpl_ct_arc *ct1, *ct2; + int pmcin = *(int *)thunk; + + ct1 = (const struct pmcpl_ct_arc *) a; + ct2 = (const struct pmcpl_ct_arc *) b; + + /* Sort in reverse order */ + if (PMCPL_CT_SAMPLE(pmcin, &ct1->pcta_samples) < + PMCPL_CT_SAMPLE(pmcin, &ct2->pcta_samples)) + return (1); + if (PMCPL_CT_SAMPLE(pmcin, &ct1->pcta_samples) > + PMCPL_CT_SAMPLE(pmcin, &ct2->pcta_samples)) + return (-1); + return (0); +} + +/* + * Grow the instr table. + */ + +static void +pmcpl_ct_instr_grow(int cursize, int *maxsize, struct pmcpl_ct_instr **items) +{ + int nmaxsize; + + if (cursize < *maxsize) + return; + + nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE); + *items = realloc(*items, nmaxsize * sizeof(struct pmcpl_ct_instr)); + if (*items == NULL) + errx(EX_SOFTWARE, "ERROR: out of memory"); + bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_instr), + (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_instr)); + *maxsize = nmaxsize; +} + +/* + * Add a new instruction sample to given node. + */ + +static void +pmcpl_ct_instr_add(struct pmcpl_ct_node *ct, int pmcin, uintfptr_t pc) +{ + int i; + struct pmcpl_ct_instr *in; + + for (i = 0; i<ct->pct_ninstr; i++) { + if (ct->pct_instr[i].pctf_func == pc) { + in = &ct->pct_instr[i]; + pmcpl_ct_samples_grow(&in->pctf_samples); + in->pctf_samples.sb[pmcin]++; + return; + } + } + + pmcpl_ct_instr_grow(ct->pct_ninstr, &ct->pct_instr_c, &ct->pct_instr); + in = &ct->pct_instr[ct->pct_ninstr]; + in->pctf_func = pc; + pmcpl_ct_samples_init(&in->pctf_samples); + pmcpl_ct_samples_grow(&in->pctf_samples); + in->pctf_samples.sb[pmcin] = 1; + ct->pct_ninstr++; +} + +/* + * Allocate a new node. + */ + +static struct pmcpl_ct_node * +pmcpl_ct_node_allocate(struct pmcstat_image *image, uintfptr_t pc) +{ + struct pmcpl_ct_node *ct; + + if ((ct = malloc(sizeof(*ct))) == NULL) + err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); + + ct->pct_flags = 0; + ct->pct_image = image; + ct->pct_func = pc; + + pmcpl_ct_samples_init(&ct->pct_samples); + + ct->pct_narc = 0; + ct->pct_arc_c = 0; + ct->pct_arc = NULL; + + ct->pct_ninstr = 0; + ct->pct_instr_c = 0; + ct->pct_instr = NULL; + + return (ct); +} + +/* + * Free a node. + */ + +static void +pmcpl_ct_node_free(struct pmcpl_ct_node *ct) +{ + int i; + + for (i = 0; i < ct->pct_narc; i++) { + pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_samples); + pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_callid); + } + + pmcpl_ct_samples_free(&ct->pct_samples); + free(ct->pct_arc); + free(ct->pct_instr); + free(ct); +} + +/* + * Clear the graph tag on each node. + */ +static void +pmcpl_ct_node_cleartag(void) +{ + int i; + struct pmcpl_ct_node_hash *pch; + + for (i = 0; i < PMCSTAT_NHASH; i++) + LIST_FOREACH(pch, &pmcpl_ct_node_hash[i], pch_next) + pch->pch_ctnode->pct_flags &= ~PMCPL_PCT_TAG; + + pmcpl_ct_root->pct_flags &= ~PMCPL_PCT_TAG; +} + +/* + * Print the callchain line by line with maximum cost at top. + */ + +static int +pmcpl_ct_node_dumptop(int pmcin, struct pmcpl_ct_node *ct, + struct pmcpl_ct_sample *rsamples, int x, int *y) +{ + int i; + + if (ct->pct_flags & PMCPL_PCT_TAG) + return 0; + + ct->pct_flags |= PMCPL_PCT_TAG; + + if (x >= PMCPL_CT_MAXCOL) { + pmcpl_ct_topscreen[x][*y] = NULL; + return 1; + } + pmcpl_ct_topscreen[x][*y] = ct; + + /* + * This is a terminal node + */ + if (ct->pct_narc == 0) { + pmcpl_ct_topscreen[x+1][*y] = NULL; + if (*y >= PMCPL_CT_MAXLINE || + *y >= pmcstat_displaywidth) + return 1; + *y = *y + 1; + for (i=0; i < x; i++) + pmcpl_ct_topscreen[i][*y] = + pmcpl_ct_topscreen[i][*y - 1]; + return 0; + } + + /* + * Quicksort the arcs. + */ + qsort_r(ct->pct_arc, ct->pct_narc, sizeof(struct pmcpl_ct_arc), + &pmcin, pmcpl_ct_arc_compare); + + for (i = 0; i < ct->pct_narc; i++) { + if (PMCPL_CT_SAMPLEP(pmcin, + &ct->pct_arc[i].pcta_samples) > pmcstat_threshold) { + if (pmcpl_ct_node_dumptop(pmcin, + ct->pct_arc[i].pcta_child, + rsamples, x+1, y)) + return 1; + } + } + + return 0; +} + +/* + * Format and display given PMC index. + */ + +static void +pmcpl_ct_node_printtop(struct pmcpl_ct_sample *rsamples, int pmcin, int maxy) +{ + int v_attrs, ns_len, vs_len, is_len, width, indentwidth, x, y; + float v; + char ns[30], vs[10], is[20]; + struct pmcpl_ct_node *ct; + struct pmcstat_symbol *sym; + const char *space = " "; + + for (y = 0; y < maxy; y++) { + /* Output image. */ + ct = pmcpl_ct_topscreen[0][y]; + snprintf(is, sizeof(is), "%-10.10s", + pmcstat_string_unintern(ct->pct_image->pi_name)); + PMCSTAT_PRINTW("%s ", is); + width = indentwidth = 11; + + for (x = 0; pmcpl_ct_topscreen[x][y] !=NULL; x++) { + + ct = pmcpl_ct_topscreen[x][y]; + + ns[0] = '\0'; ns_len = 0; + vs[0] = '\0'; vs_len = 0; + is[0] = '\0'; is_len = 0; + + /* Format value. */ + v = PMCPL_CT_SAMPLEP(pmcin, &ct->pct_samples); + if (v > pmcstat_threshold) + vs_len = snprintf(vs, sizeof(vs), "(%.1f%%)", v); + v_attrs = PMCSTAT_ATTRPERCENT(v); + + if (pmcstat_skiplink && v <= pmcstat_threshold) { + PMCSTAT_PRINTW(". "); + width += 2; + continue; + } + sym = pmcstat_symbol_search(ct->pct_image, ct->pct_func); + if (sym != NULL) { + ns_len = snprintf(ns, sizeof(ns), "%s", + pmcstat_string_unintern(sym->ps_name)); + } else + ns_len = snprintf(ns, sizeof(ns), "%p", + (void *)ct->pct_func); + + /* Format image. */ + if (x > 0 && pmcpl_ct_topscreen[x-1][y]->pct_image != ct->pct_image) + is_len = snprintf(is, sizeof(is), "@%s", + pmcstat_string_unintern(ct->pct_image->pi_name)); + + /* Check for line wrap. */ + width += ns_len + is_len + vs_len + 1; + if (width >= pmcstat_displaywidth) { + PMCSTAT_PRINTW("\n%*s", indentwidth, space); + width = indentwidth + ns_len + is_len + vs_len; + } + + PMCSTAT_ATTRON(v_attrs); + PMCSTAT_PRINTW("%s%s%s ", ns, is, vs); + PMCSTAT_ATTROFF(v_attrs); + } + PMCSTAT_PRINTW("\n"); + } +} + +/* + * Output top mode snapshot. + */ + +void +pmcpl_ct_topdisplay(void) +{ + int i, x, y, pmcin; + struct pmcpl_ct_sample rsamples; + + pmcpl_ct_samples_root(&rsamples); + + PMCSTAT_PRINTW("%-10.10s %s\n", "IMAGE", "CALLTREE"); + + for (pmcin = 0; pmcin < pmcstat_npmcs; pmcin++) { + /* Filter PMCs. */ + if (pmcstat_pmcinfilter != pmcin) + continue; + + pmcpl_ct_node_cleartag(); + + /* Quicksort the arcs. */ + qsort_r(pmcpl_ct_root->pct_arc, + pmcpl_ct_root->pct_narc, + sizeof(struct pmcpl_ct_arc), + &pmcin, pmcpl_ct_arc_compare); + + x = y = 0; + for (i = 0; i < pmcpl_ct_root->pct_narc; i++) { + if (pmcpl_ct_node_dumptop(pmcin, + pmcpl_ct_root->pct_arc[i].pcta_child, + &rsamples, x, &y)) { + break; + } + } + + pmcpl_ct_node_printtop(&rsamples, pmcin, y); + } + pmcpl_ct_samples_free(&rsamples); +} + +/* + * Handle top mode keypress. + */ + +int +pmcpl_ct_topkeypress(int c, WINDOW *w) +{ + + switch (c) { + case 'f': + pmcstat_skiplink = !pmcstat_skiplink; + wprintw(w, "skip empty link %s", pmcstat_skiplink ? "on" : "off"); + break; + } + + return 0; +} + +/* + * Look for a callgraph node associated with pmc `pmcid' in the global + * hash table that corresponds to the given `pc' value in the process map + * `ppm'. + */ + +static struct pmcpl_ct_node * +pmcpl_ct_node_hash_lookup_pc(struct pmcpl_ct_node *parent, + struct pmcstat_pcmap *ppm, uintfptr_t pc, int pmcin) +{ + struct pmcstat_symbol *sym; + struct pmcstat_image *image; + struct pmcpl_ct_node *ct; + struct pmcpl_ct_node_hash *h; + struct pmcpl_ct_arc *arc; + uintfptr_t loadaddress; + int i; + unsigned int hash; + + assert(parent != NULL); + + image = ppm->ppm_image; + + loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start; + pc -= loadaddress; /* Convert to an offset in the image. */ + + /* + * Try determine the function at this offset. If we can't + * find a function round leave the `pc' value alone. + */ + if ((sym = pmcstat_symbol_search(image, pc)) != NULL) + pc = sym->ps_start; + + for (hash = i = 0; i < (int)sizeof(uintfptr_t); i++) + hash += (pc >> i) & 0xFF; + + hash &= PMCSTAT_HASH_MASK; + + ct = NULL; + LIST_FOREACH(h, &pmcpl_ct_node_hash[hash], pch_next) { + ct = h->pch_ctnode; + + assert(ct != NULL); + + if (ct->pct_image == image && ct->pct_func == pc) { + /* + * Find related arc in parent node and + * increment the sample count. + */ + for (i = 0; i < parent->pct_narc; i++) { + if (parent->pct_arc[i].pcta_child == ct) { + arc = &parent->pct_arc[i]; + pmcpl_ct_samples_grow(&arc->pcta_samples); + arc->pcta_samples.sb[pmcin]++; + /* Estimate call count. */ + pmcpl_ct_samples_grow(&arc->pcta_callid); + if (pmcpl_ct_callid.sb[pmcin] - + arc->pcta_callid.sb[pmcin] > 1) + arc->pcta_call++; + arc->pcta_callid.sb[pmcin] = + pmcpl_ct_callid.sb[pmcin]; + return (ct); + } + } + + /* + * No arc found for us, add ourself to the parent. + */ + pmcpl_ct_arc_grow(parent->pct_narc, + &parent->pct_arc_c, &parent->pct_arc); + arc = &parent->pct_arc[parent->pct_narc]; + pmcpl_ct_samples_grow(&arc->pcta_samples); + arc->pcta_samples.sb[pmcin] = 1; + arc->pcta_call = 1; + pmcpl_ct_samples_grow(&arc->pcta_callid); + arc->pcta_callid.sb[pmcin] = pmcpl_ct_callid.sb[pmcin]; + arc->pcta_child = ct; + parent->pct_narc++; + return (ct); + } + } + + /* + * We haven't seen this (pmcid, pc) tuple yet, so allocate a + * new callgraph node and a new hash table entry for it. + */ + ct = pmcpl_ct_node_allocate(image, pc); + if ((h = malloc(sizeof(*h))) == NULL) + err(EX_OSERR, "ERROR: Could not allocate callgraph node"); + + h->pch_ctnode = ct; + LIST_INSERT_HEAD(&pmcpl_ct_node_hash[hash], h, pch_next); + + pmcpl_ct_arc_grow(parent->pct_narc, + &parent->pct_arc_c, &parent->pct_arc); + arc = &parent->pct_arc[parent->pct_narc]; + pmcpl_ct_samples_grow(&arc->pcta_samples); + arc->pcta_samples.sb[pmcin] = 1; + arc->pcta_call = 1; + pmcpl_ct_samples_grow(&arc->pcta_callid); + arc->pcta_callid.sb[pmcin] = pmcpl_ct_callid.sb[pmcin]; + arc->pcta_child = ct; + parent->pct_narc++; + return (ct); +} + +/* + * Record a callchain. + */ + +void +pmcpl_ct_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) +{ + int n, pmcin; + struct pmcstat_pcmap *ppm[PMC_CALLCHAIN_DEPTH_MAX]; + struct pmcstat_process *km; + struct pmcpl_ct_node *parent, *child; + + (void) cpu; + + assert(nsamples>0 && nsamples<=PMC_CALLCHAIN_DEPTH_MAX); + + /* Get the PMC index. */ + pmcin = pmcr->pr_pmcin; + + /* + * Validate mapping for the callchain. + * Go from bottom to first invalid entry. + */ + km = pmcstat_kernproc; + for (n = 0; n < (int)nsamples; n++) { + ppm[n] = pmcstat_process_find_map(usermode ? + pp : km, cc[n]); + if (ppm[n] == NULL) { + /* Detect full frame capture (kernel + user). */ + if (!usermode) { + ppm[n] = pmcstat_process_find_map(pp, cc[n]); + if (ppm[n] != NULL) + km = pp; + } + } + if (ppm[n] == NULL) + break; + } + if (n-- == 0) { + pmcstat_stats.ps_callchain_dubious_frames++; + return; + } + + /* Increase the call generation counter. */ + pmcpl_ct_samples_grow(&pmcpl_ct_callid); + pmcpl_ct_callid.sb[pmcin]++; + + /* + * Iterate remaining addresses. + */ + for (parent = pmcpl_ct_root, child = NULL; n >= 0; n--) { + child = pmcpl_ct_node_hash_lookup_pc(parent, ppm[n], cc[n], + pmcin); + if (child == NULL) { + pmcstat_stats.ps_callchain_dubious_frames++; + continue; + } + parent = child; + } + + /* + * Increment the sample count for this PMC. + */ + if (child != NULL) { + pmcpl_ct_samples_grow(&child->pct_samples); + child->pct_samples.sb[pmcin]++; + + /* Update per instruction sample if required. */ + if (args.pa_ctdumpinstr) + pmcpl_ct_instr_add(child, pmcin, cc[0] - + (ppm[0]->ppm_lowpc + ppm[0]->ppm_image->pi_vaddr - + ppm[0]->ppm_image->pi_start)); + } +} + +/* + * Print node self cost. + */ + +static void +pmcpl_ct_node_printself(struct pmcpl_ct_node *ct) +{ + int i, j, line; + uintptr_t addr; + struct pmcstat_symbol *sym; + char sourcefile[PATH_MAX]; + char funcname[PATH_MAX]; + + /* + * Object binary. + */ +#ifdef PMCPL_CT_OPTIMIZEFN + if (pmcpl_ct_prevfn != ct->pct_image->pi_fullpath) { +#endif + pmcpl_ct_prevfn = ct->pct_image->pi_fullpath; + fprintf(args.pa_graphfile, "ob=%s\n", + pmcstat_string_unintern(pmcpl_ct_prevfn)); +#ifdef PMCPL_CT_OPTIMIZEFN + } +#endif + + /* + * Function name. + */ + if (pmcstat_image_addr2line(ct->pct_image, ct->pct_func, + sourcefile, sizeof(sourcefile), &line, + funcname, sizeof(funcname))) { + fprintf(args.pa_graphfile, "fn=%s\n", + funcname); + } else { + sym = pmcstat_symbol_search(ct->pct_image, ct->pct_func); + if (sym != NULL) + fprintf(args.pa_graphfile, "fn=%s\n", + pmcstat_string_unintern(sym->ps_name)); + else + fprintf(args.pa_graphfile, "fn=%p\n", + (void *)(ct->pct_image->pi_vaddr + ct->pct_func)); + } + + /* + * Self cost. + */ + if (ct->pct_ninstr > 0) { + for (i = 0; i < ct->pct_ninstr; i++) { + addr = ct->pct_image->pi_vaddr + + ct->pct_instr[i].pctf_func; + line = 0; + if (pmcstat_image_addr2line(ct->pct_image, addr, + sourcefile, sizeof(sourcefile), &line, + funcname, sizeof(funcname))) + fprintf(args.pa_graphfile, "fl=%s\n", sourcefile); + fprintf(args.pa_graphfile, "%p %u", (void *)addr, line); + for (j = 0; j<pmcstat_npmcs; j++) + fprintf(args.pa_graphfile, " %u", + PMCPL_CT_SAMPLE(j, + &ct->pct_instr[i].pctf_samples)); + fprintf(args.pa_graphfile, "\n"); + } + } else { + addr = ct->pct_image->pi_vaddr + ct->pct_func; + line = 0; + if (pmcstat_image_addr2line(ct->pct_image, addr, + sourcefile, sizeof(sourcefile), &line, + funcname, sizeof(funcname))) + fprintf(args.pa_graphfile, "fl=%s\n", sourcefile); + fprintf(args.pa_graphfile, "* *"); + for (i = 0; i<pmcstat_npmcs ; i++) + fprintf(args.pa_graphfile, " %u", + PMCPL_CT_SAMPLE(i, &ct->pct_samples)); + fprintf(args.pa_graphfile, "\n"); + } +} + +/* + * Print node child cost. + */ + +static void +pmcpl_ct_node_printchild(struct pmcpl_ct_node *ct) +{ + int i, j, line; + uintptr_t addr; + struct pmcstat_symbol *sym; + struct pmcpl_ct_node *child; + char sourcefile[PATH_MAX]; + char funcname[PATH_MAX]; + + /* + * Child cost. + * TODO: attach child cost to the real position in the funtion. + * TODO: cfn=<fn> / call <ncall> addr(<fn>) / addr(call <fn>) <arccost> + */ + for (i=0 ; i<ct->pct_narc; i++) { + child = ct->pct_arc[i].pcta_child; + + /* Object binary. */ +#ifdef PMCPL_CT_OPTIMIZEFN + if (pmcpl_ct_prevfn != child->pct_image->pi_fullpath) { +#endif + pmcpl_ct_prevfn = child->pct_image->pi_fullpath; + fprintf(args.pa_graphfile, "cob=%s\n", + pmcstat_string_unintern(pmcpl_ct_prevfn)); +#if PMCPL_CT_OPTIMIZEFN + } +#endif + /* Child function name. */ + addr = child->pct_image->pi_vaddr + child->pct_func; + /* Child function source file. */ + if (pmcstat_image_addr2line(child->pct_image, addr, + sourcefile, sizeof(sourcefile), &line, + funcname, sizeof(funcname))) { + fprintf(args.pa_graphfile, "cfn=%s\n", funcname); + fprintf(args.pa_graphfile, "cfl=%s\n", sourcefile); + } else { + sym = pmcstat_symbol_search(child->pct_image, + child->pct_func); + if (sym != NULL) + fprintf(args.pa_graphfile, "cfn=%s\n", + pmcstat_string_unintern(sym->ps_name)); + else + fprintf(args.pa_graphfile, "cfn=%p\n", (void *)addr); + } + + /* Child function address, line and call count. */ + fprintf(args.pa_graphfile, "calls=%u %p %u\n", + ct->pct_arc[i].pcta_call, (void *)addr, line); + + if (ct->pct_image != NULL) { + /* Call address, line, sample. */ + addr = ct->pct_image->pi_vaddr + ct->pct_func; + line = 0; + pmcstat_image_addr2line(ct->pct_image, addr, sourcefile, + sizeof(sourcefile), &line, + funcname, sizeof(funcname)); + fprintf(args.pa_graphfile, "%p %u", (void *)addr, line); + } + else + fprintf(args.pa_graphfile, "* *"); + for (j = 0; j<pmcstat_npmcs; j++) + fprintf(args.pa_graphfile, " %u", + PMCPL_CT_SAMPLE(j, &ct->pct_arc[i].pcta_samples)); + fprintf(args.pa_graphfile, "\n"); + } +} + +/* + * Clean the PMC name for Kcachegrind formula + */ + +static void +pmcpl_ct_fixup_pmcname(char *s) +{ + char *p; + + for (p = s; *p; p++) + if (!isalnum(*p)) + *p = '_'; +} + +/* + * Print a calltree (KCachegrind) for all PMCs. + */ + +static void +pmcpl_ct_print(void) +{ + int n, i; + struct pmcpl_ct_node_hash *pch; + struct pmcpl_ct_sample rsamples; + char name[40]; + + pmcpl_ct_samples_root(&rsamples); + pmcpl_ct_prevfn = NULL; + + fprintf(args.pa_graphfile, + "version: 1\n" + "creator: pmcstat\n" + "positions: instr line\n" + "events:"); + for (i=0; i<pmcstat_npmcs; i++) { + snprintf(name, sizeof(name), "%s_%d", + pmcstat_pmcindex_to_name(i), i); + pmcpl_ct_fixup_pmcname(name); + fprintf(args.pa_graphfile, " %s", name); + } + fprintf(args.pa_graphfile, "\nsummary:"); + for (i=0; i<pmcstat_npmcs ; i++) + fprintf(args.pa_graphfile, " %u", + PMCPL_CT_SAMPLE(i, &rsamples)); + fprintf(args.pa_graphfile, "\n\n"); + + /* + * Fake root node + */ + fprintf(args.pa_graphfile, "ob=FreeBSD\n"); + fprintf(args.pa_graphfile, "fn=ROOT\n"); + fprintf(args.pa_graphfile, "* *"); + for (i = 0; i<pmcstat_npmcs ; i++) + fprintf(args.pa_graphfile, " 0"); + fprintf(args.pa_graphfile, "\n"); + pmcpl_ct_node_printchild(pmcpl_ct_root); + + for (n = 0; n < PMCSTAT_NHASH; n++) + LIST_FOREACH(pch, &pmcpl_ct_node_hash[n], pch_next) { + pmcpl_ct_node_printself(pch->pch_ctnode); + pmcpl_ct_node_printchild(pch->pch_ctnode); + } + + pmcpl_ct_samples_free(&rsamples); +} + +int +pmcpl_ct_configure(char *opt) +{ + + if (strncmp(opt, "skiplink=", 9) == 0) { + pmcstat_skiplink = atoi(opt+9); + } else + return (0); + + return (1); +} + +int +pmcpl_ct_init(void) +{ + int i; + + pmcpl_ct_prevfn = NULL; + pmcpl_ct_root = pmcpl_ct_node_allocate(NULL, 0); + + for (i = 0; i < PMCSTAT_NHASH; i++) + LIST_INIT(&pmcpl_ct_node_hash[i]); + + pmcpl_ct_samples_init(&pmcpl_ct_callid); + + return (0); +} + +void +pmcpl_ct_shutdown(FILE *mf) +{ + int i; + struct pmcpl_ct_node_hash *pch, *pchtmp; + + (void) mf; + + if (args.pa_flags & FLAG_DO_CALLGRAPHS) + pmcpl_ct_print(); + + /* + * Free memory. + */ + + for (i = 0; i < PMCSTAT_NHASH; i++) { + LIST_FOREACH_SAFE(pch, &pmcpl_ct_node_hash[i], pch_next, + pchtmp) { + pmcpl_ct_node_free(pch->pch_ctnode); + free(pch); + } + } + + pmcpl_ct_node_free(pmcpl_ct_root); + pmcpl_ct_root = NULL; + + pmcpl_ct_samples_free(&pmcpl_ct_callid); +} + diff --git a/usr.sbin/pmcstat/pmcpl_calltree.h b/usr.sbin/pmcstat/pmcpl_calltree.h new file mode 100644 index 0000000..f54957f --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_calltree.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2009, Fabien Thomas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PMCSTAT_PL_CALLTREE_H_ +#define _PMCSTAT_PL_CALLTREE_H_ + +/* Function prototypes */ +int pmcpl_ct_init(void); +void pmcpl_ct_shutdown(FILE *mf); +void pmcpl_ct_process( + struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu); +int pmcpl_ct_topkeypress(int c, WINDOW *w); +void pmcpl_ct_topdisplay(void); +int pmcpl_ct_configure(char *opt); + +#endif /* _PMCSTAT_PL_CALLTREE_H_ */ diff --git a/usr.sbin/pmcstat/pmcpl_gprof.c b/usr.sbin/pmcstat/pmcpl_gprof.c new file mode 100644 index 0000000..9327eb9 --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_gprof.c @@ -0,0 +1,533 @@ +/*- + * Copyright (c) 2005-2007, Joseph Koshy + * Copyright (c) 2007 The FreeBSD Foundation + * Copyright (c) 2009, Fabien Thomas + * All rights reserved. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Transform a hwpmc(4) log into human readable form, and into + * gprof(1) compatible profiles. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/gmon.h> +#include <sys/imgact_aout.h> +#include <sys/imgact_elf.h> +#include <sys/mman.h> +#include <sys/pmc.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <assert.h> +#include <curses.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <gelf.h> +#include <libgen.h> +#include <limits.h> +#include <netdb.h> +#include <pmc.h> +#include <pmclog.h> +#include <sysexits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pmcstat.h" +#include "pmcstat_log.h" +#include "pmcpl_callgraph.h" +#include "pmcpl_gprof.h" + +/* + * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These + * files are mmap()'ed in as needed. + */ + +struct pmcstat_gmonfile { + LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ + int pgf_overflow; /* whether a count overflowed */ + pmc_id_t pgf_pmcid; /* id of the associated pmc */ + size_t pgf_nbuckets; /* #buckets in this gmon.out */ + unsigned int pgf_nsamples; /* #samples in this gmon.out */ + pmcstat_interned_string pgf_name; /* pathname of gmon.out file */ + size_t pgf_ndatabytes; /* number of bytes mapped */ + void *pgf_gmondata; /* pointer to mmap'ed data */ + FILE *pgf_file; /* used when writing gmon arcs */ +}; + +/* + * Prototypes + */ + +static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, + struct pmcstat_image *_image); +static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, + struct pmcstat_image *_img, pmc_id_t _pmcid); +static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); +static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); + +static struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct + pmcstat_image *_i, pmc_id_t _id); + +/* + * Create a gmon.out file and size it. + */ + +static void +pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, + struct pmcstat_image *image) +{ + int fd; + size_t count; + struct gmonhdr gm; + const char *pathname; + char buffer[DEFAULT_BUFFER_SIZE]; + + pathname = pmcstat_string_unintern(pgf->pgf_name); + if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) + err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname); + + gm.lpc = image->pi_start; + gm.hpc = image->pi_end; + gm.ncnt = (pgf->pgf_nbuckets * sizeof(HISTCOUNTER)) + + sizeof(struct gmonhdr); + gm.version = GMONVERSION; + gm.profrate = 0; /* use ticks */ + gm.histcounter_type = 0; /* compatibility with moncontrol() */ + gm.spare[0] = gm.spare[1] = 0; + + /* Write out the gmon header */ + if (write(fd, &gm, sizeof(gm)) < 0) + goto error; + + /* Zero fill the samples[] array */ + (void) memset(buffer, 0, sizeof(buffer)); + + count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); + while (count > sizeof(buffer)) { + if (write(fd, &buffer, sizeof(buffer)) < 0) + goto error; + count -= sizeof(buffer); + } + + if (write(fd, &buffer, count) < 0) + goto error; + + (void) close(fd); + + return; + + error: + err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname); +} + +/* + * Determine the full pathname of a gmon.out file for a given + * (image,pmcid) combination. Return the interned string. + */ + +pmcstat_interned_string +pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, + pmc_id_t pmcid) +{ + const char *pmcname; + char fullpath[PATH_MAX]; + + pmcname = pmcstat_pmcid_to_name(pmcid); + + (void) snprintf(fullpath, sizeof(fullpath), + "%s/%s/%s", samplesdir, pmcname, + pmcstat_string_unintern(image->pi_samplename)); + + return (pmcstat_string_intern(fullpath)); +} + + +/* + * Mmap in a gmon.out file for processing. + */ + +static void +pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) +{ + int fd; + const char *pathname; + + pathname = pmcstat_string_unintern(pgf->pgf_name); + + /* the gmon.out file must already exist */ + if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0) + err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname); + + pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, + PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); + + if (pgf->pgf_gmondata == MAP_FAILED) + err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname); + + (void) close(fd); +} + +/* + * Unmap a gmon.out file after sync'ing its data to disk. + */ + +static void +pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) +{ + (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, + MS_SYNC); + (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); + pgf->pgf_gmondata = NULL; +} + +static void +pmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid, + uintptr_t rawfrom, uintptr_t rawto, uint32_t count) +{ + struct rawarc arc; /* from <sys/gmon.h> */ + const char *pathname; + struct pmcstat_gmonfile *pgf; + + if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL) + return; + + if (pgf->pgf_file == NULL) { + pathname = pmcstat_string_unintern(pgf->pgf_name); + if ((pgf->pgf_file = fopen(pathname, "a")) == NULL) + return; + } + + arc.raw_frompc = rawfrom + image->pi_vaddr; + arc.raw_selfpc = rawto + image->pi_vaddr; + arc.raw_count = count; + + (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file); + +} + +static struct pmcstat_gmonfile * +pmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid) +{ + struct pmcstat_gmonfile *pgf; + LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) + if (pgf->pgf_pmcid == pmcid) + return (pgf); + return (NULL); +} + +static void +pmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid) +{ + struct pmcstat_cgnode *cgc; + + /* + * Look for child nodes that belong to the same image. + */ + + LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) { + if (cgc->pcg_image == cg->pcg_image) + pmcstat_gmon_append_arc(cg->pcg_image, pmcid, + cgc->pcg_func, cg->pcg_func, cgc->pcg_count); + if (cgc->pcg_nchildren > 0) + pmcstat_cgnode_do_gmon_arcs(cgc, pmcid); + } +} + +static void +pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid) +{ + int n; + struct pmcstat_cgnode_hash *pch; + + for (n = 0; n < PMCSTAT_NHASH; n++) + LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) + if (pch->pch_pmcid == pmcid && + pch->pch_cgnode->pcg_nchildren > 1) + pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode, + pmcid); +} + + +static void +pmcstat_callgraph_do_gmon_arcs(void) +{ + struct pmcstat_pmcrecord *pmcr; + + LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) + pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid); +} + +void +pmcpl_gmon_initimage(struct pmcstat_image *pi) +{ + int count, nlen; + char *sn; + char name[NAME_MAX]; + + /* + * Look for a suitable name for the sample files associated + * with this image: if `basename(path)`+".gmon" is available, + * we use that, otherwise we try iterating through + * `basename(path)`+ "~" + NNN + ".gmon" till we get a free + * entry. + */ + if ((sn = basename(pmcstat_string_unintern(pi->pi_execpath))) == NULL) + err(EX_OSERR, "ERROR: Cannot process \"%s\"", + pmcstat_string_unintern(pi->pi_execpath)); + + nlen = strlen(sn); + nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); + + snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); + + /* try use the unabridged name first */ + if (pmcstat_string_lookup(name) == NULL) + pi->pi_samplename = pmcstat_string_intern(name); + else { + /* + * Otherwise use a prefix from the original name and + * upto 3 digits. + */ + nlen = strlen(sn); + nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); + count = 0; + do { + if (++count > 999) + errx(EX_CANTCREAT, "ERROR: cannot create a " + "gmon file for \"%s\"", name); + snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", + nlen, sn, count); + if (pmcstat_string_lookup(name) == NULL) { + pi->pi_samplename = + pmcstat_string_intern(name); + count = 0; + } + } while (count > 0); + } + + LIST_INIT(&pi->pi_gmlist); +} + +void +pmcpl_gmon_shutdownimage(struct pmcstat_image *pi) +{ + struct pmcstat_gmonfile *pgf, *pgftmp; + + LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, pgftmp) { + if (pgf->pgf_file) + (void) fclose(pgf->pgf_file); + LIST_REMOVE(pgf, pgf_next); + free(pgf); + } +} + +void +pmcpl_gmon_newpmc(pmcstat_interned_string ps, struct pmcstat_pmcrecord *pr) +{ + struct stat st; + char fullpath[PATH_MAX]; + + (void) pr; + + /* + * Create the appropriate directory to hold gmon.out files. + */ + + (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", args.pa_samplesdir, + pmcstat_string_unintern(ps)); + + /* If the path name exists, it should be a directory */ + if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) + return; + + if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) + err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", + fullpath); +} + +/* + * Increment the bucket in the gmon.out file corresponding to 'pmcid' + * and 'pc'. + */ + +void +pmcpl_gmon_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) +{ + struct pmcstat_pcmap *map; + struct pmcstat_image *image; + struct pmcstat_gmonfile *pgf; + uintfptr_t bucket; + HISTCOUNTER *hc; + pmc_id_t pmcid; + + (void) nsamples; (void) usermode; (void) cpu; + + map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[0]); + if (map == NULL) { + /* Unknown offset. */ + pmcstat_stats.ps_samples_unknown_offset++; + return; + } + + assert(cc[0] >= map->ppm_lowpc && cc[0] < map->ppm_highpc); + + image = map->ppm_image; + pmcid = pmcr->pr_pmcid; + + /* + * If this is the first time we are seeing a sample for + * this executable image, try determine its parameters. + */ + if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) + pmcstat_image_determine_type(image); + + assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); + + /* Ignore samples in images that we know nothing about. */ + if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { + pmcstat_stats.ps_samples_indeterminable++; + return; + } + + /* + * Find the gmon file corresponding to 'pmcid', creating it if + * needed. + */ + pgf = pmcstat_image_find_gmonfile(image, pmcid); + if (pgf == NULL) { + if ((pgf = calloc(1, sizeof(*pgf))) == NULL) + err(EX_OSERR, "ERROR:"); + + pgf->pgf_gmondata = NULL; /* mark as unmapped */ + pgf->pgf_name = pmcstat_gmon_create_name(args.pa_samplesdir, + image, pmcid); + pgf->pgf_pmcid = pmcid; + assert(image->pi_end > image->pi_start); + pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / + FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ + pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + + pgf->pgf_nbuckets * sizeof(HISTCOUNTER); + pgf->pgf_nsamples = 0; + pgf->pgf_file = NULL; + + pmcstat_gmon_create_file(pgf, image); + + LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); + } + + /* + * Map the gmon file in if needed. It may have been mapped + * out under memory pressure. + */ + if (pgf->pgf_gmondata == NULL) + pmcstat_gmon_map_file(pgf); + + assert(pgf->pgf_gmondata != NULL); + + /* + * + */ + + bucket = (cc[0] - map->ppm_lowpc) / FUNCTION_ALIGNMENT; + + assert(bucket < pgf->pgf_nbuckets); + + hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + + sizeof(struct gmonhdr)); + + /* saturating add */ + if (hc[bucket] < 0xFFFFU) /* XXX tie this to sizeof(HISTCOUNTER) */ + hc[bucket]++; + else /* mark that an overflow occurred */ + pgf->pgf_overflow = 1; + + pgf->pgf_nsamples++; +} + +/* + * Shutdown module. + */ + +void +pmcpl_gmon_shutdown(FILE *mf) +{ + int i; + struct pmcstat_gmonfile *pgf; + struct pmcstat_image *pi; + + /* + * Sync back all gprof flat profile data. + */ + for (i = 0; i < PMCSTAT_NHASH; i++) { + LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { + if (mf) + (void) fprintf(mf, " \"%s\" => \"%s\"", + pmcstat_string_unintern(pi->pi_execpath), + pmcstat_string_unintern( + pi->pi_samplename)); + + /* flush gmon.out data to disk */ + LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { + pmcstat_gmon_unmap_file(pgf); + if (mf) + (void) fprintf(mf, " %s/%d", + pmcstat_pmcid_to_name( + pgf->pgf_pmcid), + pgf->pgf_nsamples); + if (pgf->pgf_overflow && args.pa_verbosity >= 1) + warnx("WARNING: profile \"%s\" " + "overflowed.", + pmcstat_string_unintern( + pgf->pgf_name)); + } + + if (mf) + (void) fprintf(mf, "\n"); + } + } + + /* + * Compute arcs and add these to the gprof files. + */ + if (args.pa_flags & FLAG_DO_GPROF && args.pa_graphdepth > 1) + pmcstat_callgraph_do_gmon_arcs(); +} diff --git a/usr.sbin/pmcstat/pmcpl_gprof.h b/usr.sbin/pmcstat/pmcpl_gprof.h new file mode 100644 index 0000000..069082f --- /dev/null +++ b/usr.sbin/pmcstat/pmcpl_gprof.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2005-2007, Joseph Koshy + * Copyright (c) 2007 The FreeBSD Foundation + * Copyright (c) 2009, Fabien Thomas + * All rights reserved. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PMCSTAT_PL_GPROF_H_ +#define _PMCSTAT_PL_GPROF_H_ + +/* Function prototypes */ +void pmcpl_gmon_shutdown(FILE *mf); +void pmcpl_gmon_process( + struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, + uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu); +void pmcpl_gmon_initimage(struct pmcstat_image *pi); +void pmcpl_gmon_shutdownimage(struct pmcstat_image *pi); +void pmcpl_gmon_newpmc(pmcstat_interned_string ps, + struct pmcstat_pmcrecord *pr); + +#endif /* _PMCSTAT_PL_GPROF_H_ */ diff --git a/usr.sbin/pmcstat/pmcstat.8 b/usr.sbin/pmcstat/pmcstat.8 index a4e6f1f..309eb3e 100644 --- a/usr.sbin/pmcstat/pmcstat.8 +++ b/usr.sbin/pmcstat/pmcstat.8 @@ -36,6 +36,7 @@ .Op Fl C .Op Fl D Ar pathname .Op Fl E +.Op Fl F Ar pathname .Op Fl G Ar pathname .Op Fl M Ar mapfilename .Op Fl N @@ -43,9 +44,11 @@ .Op Fl P Ar event-spec .Op Fl R Ar logfilename .Op Fl S Ar event-spec +.Op Fl T .Op Fl W .Op Fl c Ar cpu-spec .Op Fl d +.Op Fl f Ar pluginopt .Op Fl g .Op Fl k Ar kerneldir .Op Fl m Ar pathname @@ -129,6 +132,16 @@ complex pipeline of processes when used in conjunction with the .Fl d option. The default is to not to enable per-process tracking. +.It Fl F Ar pathname +Print calltree (Kcachegrind) information to file +.Ar pathname . +If argument +.Ar pathname +is a +.Dq Li - +this information is sent to the output file specified by the +.Fl o +option. .It Fl G Ar pathname Print callchain information to file .Ar pathname . @@ -195,6 +208,12 @@ Perform offline analysis using sampling data in file Allocate a system mode sampling PMC measuring hardware events specified in .Ar event-spec . +.It Fl T +Use a top like mode for sampling PMCs. The following hotkeys +can be used: 'c+a' switch to accumulative mode, 'c+d' switch +to delta mode, 'm' merge PMCs, 'n' change view, 'p' show next +PMC, ' ' pause, 'q' quit. calltree only: 'f' cost under threshold +is seen as a dot. .It Fl W Toggle logging the incremental counts seen by the threads of a tracked process each time they are scheduled on a CPU. @@ -218,6 +237,12 @@ Toggle between process mode PMCs measuring events for the target process' current and future children or only measuring events for the target process. The default is to measure events for the target process alone. +.It Fl f Ar pluginopt +Pass option string to the active plugin. +.br +threshold=<float> do not display cost under specified value (Top). +.br +skiplink=0|1 replace node with cost under threshold by a dot (Top). .It Fl g Produce profiles in a format compatible with .Xr gprof 1 . @@ -286,7 +311,8 @@ regular expression for selecting processes based on their command names. .It Fl v Increase verbosity. .It Fl w Ar secs -Print the values of all counting mode PMCs every +Print the values of all counting mode PMCs or sampling mode PMCs +for top mode every .Ar secs seconds. The argument diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c index 6496ddb..a73d293 100644 --- a/usr.sbin/pmcstat/pmcstat.c +++ b/usr.sbin/pmcstat/pmcstat.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/wait.h> #include <assert.h> +#include <curses.h> #include <err.h> #include <errno.h> #include <fcntl.h> @@ -106,13 +107,15 @@ __FBSDID("$FreeBSD$"); int pmcstat_interrupt = 0; int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; +int pmcstat_displaywidth = DEFAULT_DISPLAY_WIDTH; int pmcstat_sockpair[NSOCKPAIRFD]; int pmcstat_kq; kvm_t *pmcstat_kvm; struct kinfo_proc *pmcstat_plist; +struct pmcstat_args args; void -pmcstat_attach_pmcs(struct pmcstat_args *a) +pmcstat_attach_pmcs(void) { struct pmcstat_ev *ev; struct pmcstat_target *pt; @@ -120,10 +123,10 @@ pmcstat_attach_pmcs(struct pmcstat_args *a) /* Attach all process PMCs to target processes. */ count = 0; - STAILQ_FOREACH(ev, &a->pa_events, ev_next) { + STAILQ_FOREACH(ev, &args.pa_events, ev_next) { if (PMC_IS_SYSTEM_MODE(ev->ev_mode)) continue; - SLIST_FOREACH(pt, &a->pa_targets, pt_next) + SLIST_FOREACH(pt, &args.pa_targets, pt_next) if (pmc_attach(ev->ev_pmcid, pt->pt_pid) == 0) count++; else if (errno != ESRCH) @@ -138,12 +141,12 @@ pmcstat_attach_pmcs(struct pmcstat_args *a) void -pmcstat_cleanup(struct pmcstat_args *a) +pmcstat_cleanup(void) { struct pmcstat_ev *ev, *tmp; /* release allocated PMCs. */ - STAILQ_FOREACH_SAFE(ev, &a->pa_events, ev_next, tmp) + STAILQ_FOREACH_SAFE(ev, &args.pa_events, ev_next, tmp) if (ev->ev_pmcid != PMC_ID_INVALID) { if (pmc_stop(ev->ev_pmcid) < 0) err(EX_OSERR, "ERROR: cannot stop pmc 0x%x " @@ -153,25 +156,25 @@ pmcstat_cleanup(struct pmcstat_args *a) "0x%x \"%s\"", ev->ev_pmcid, ev->ev_name); free(ev->ev_name); free(ev->ev_spec); - STAILQ_REMOVE(&a->pa_events, ev, pmcstat_ev, ev_next); + STAILQ_REMOVE(&args.pa_events, ev, pmcstat_ev, ev_next); free(ev); } /* de-configure the log file if present. */ - if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) + if (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) (void) pmc_configure_logfile(-1); - if (a->pa_logparser) { - pmclog_close(a->pa_logparser); - a->pa_logparser = NULL; + if (args.pa_logparser) { + pmclog_close(args.pa_logparser); + args.pa_logparser = NULL; } - if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) - pmcstat_shutdown_logging(a); + if (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) + pmcstat_shutdown_logging(); } void -pmcstat_clone_event_descriptor(struct pmcstat_args *a, struct pmcstat_ev *ev, +pmcstat_clone_event_descriptor(struct pmcstat_ev *ev, uint32_t cpumask) { int cpu; @@ -194,14 +197,14 @@ pmcstat_clone_event_descriptor(struct pmcstat_args *a, struct pmcstat_ev *ev, ev_clone->ev_saved = ev->ev_saved; ev_clone->ev_spec = strdup(ev->ev_spec); - STAILQ_INSERT_TAIL(&a->pa_events, ev_clone, ev_next); + STAILQ_INSERT_TAIL(&args.pa_events, ev_clone, ev_next); cpumask &= ~(1 << cpu); } } void -pmcstat_create_process(struct pmcstat_args *a) +pmcstat_create_process(void) { char token; pid_t pid; @@ -229,10 +232,10 @@ pmcstat_create_process(struct pmcstat_args *a) (void) close(pmcstat_sockpair[CHILDSOCKET]); /* exec() the program requested */ - execvp(*a->pa_argv, a->pa_argv); + execvp(*args.pa_argv, args.pa_argv); /* and if that fails, notify the parent */ kill(getppid(), SIGCHLD); - err(EX_OSERR, "ERROR: execvp \"%s\" failed", *a->pa_argv); + err(EX_OSERR, "ERROR: execvp \"%s\" failed", *args.pa_argv); /*NOTREACHED*/ default: /* parent */ @@ -250,7 +253,7 @@ pmcstat_create_process(struct pmcstat_args *a) errx(EX_SOFTWARE, "ERROR: Out of memory."); pt->pt_pid = pid; - SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next); + SLIST_INSERT_HEAD(&args.pa_targets, pt, pt_next); /* Wait for the child to signal that its ready to go. */ if (read(pmcstat_sockpair[PARENTSOCKET], &token, 1) < 0) @@ -260,7 +263,7 @@ pmcstat_create_process(struct pmcstat_args *a) } void -pmcstat_find_targets(struct pmcstat_args *a, const char *spec) +pmcstat_find_targets(const char *spec) { int n, nproc, pid, rv; struct pmcstat_target *pt; @@ -275,7 +278,7 @@ pmcstat_find_targets(struct pmcstat_args *a, const char *spec) if ((pt = malloc(sizeof(*pt))) == NULL) goto outofmemory; pt->pt_pid = pid; - SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next); + SLIST_INSERT_HEAD(&args.pa_targets, pt, pt_next); return; } @@ -302,7 +305,7 @@ pmcstat_find_targets(struct pmcstat_args *a, const char *spec) if ((pt = malloc(sizeof(*pt))) == NULL) goto outofmemory; pt->pt_pid = kp->ki_pid; - SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next); + SLIST_INSERT_HEAD(&args.pa_targets, pt, pt_next); } else if (rv != REG_NOMATCH) { regerror(rv, ®, errbuf, sizeof(errbuf)); errx(EX_SOFTWARE, "ERROR: Regex evalation failed: %s", @@ -343,17 +346,17 @@ pmcstat_get_cpumask(const char *cpuspec) } void -pmcstat_kill_process(struct pmcstat_args *a) +pmcstat_kill_process(void) { struct pmcstat_target *pt; - assert(a->pa_flags & FLAG_HAS_COMMANDLINE); + assert(args.pa_flags & FLAG_HAS_COMMANDLINE); /* * If a command line was specified, it would be the very first * in the list, before any other processes specified by -t. */ - pt = SLIST_FIRST(&a->pa_targets); + pt = SLIST_FIRST(&args.pa_targets); assert(pt != NULL); if (kill(pt->pt_pid, SIGINT) != 0) @@ -361,7 +364,7 @@ pmcstat_kill_process(struct pmcstat_args *a) } void -pmcstat_start_pmcs(struct pmcstat_args *a) +pmcstat_start_pmcs(void) { struct pmcstat_ev *ev; @@ -372,7 +375,7 @@ pmcstat_start_pmcs(struct pmcstat_args *a) if (pmc_start(ev->ev_pmcid) < 0) { warn("ERROR: Cannot start pmc 0x%x \"%s\"", ev->ev_pmcid, ev->ev_name); - pmcstat_cleanup(a); + pmcstat_cleanup(); exit(EX_OSERR); } } @@ -380,37 +383,37 @@ pmcstat_start_pmcs(struct pmcstat_args *a) } void -pmcstat_print_headers(struct pmcstat_args *a) +pmcstat_print_headers(void) { struct pmcstat_ev *ev; int c, w; - (void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX); + (void) fprintf(args.pa_printfile, PRINT_HEADER_PREFIX); - STAILQ_FOREACH(ev, &a->pa_events, ev_next) { + STAILQ_FOREACH(ev, &args.pa_events, ev_next) { if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) continue; c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p'; if (ev->ev_fieldskip != 0) - (void) fprintf(a->pa_printfile, "%*s", + (void) fprintf(args.pa_printfile, "%*s", ev->ev_fieldskip, ""); w = ev->ev_fieldwidth - ev->ev_fieldskip - 2; if (c == 's') - (void) fprintf(a->pa_printfile, "s/%02d/%-*s ", + (void) fprintf(args.pa_printfile, "s/%02d/%-*s ", ev->ev_cpu, w-3, ev->ev_name); else - (void) fprintf(a->pa_printfile, "p/%*s ", w, + (void) fprintf(args.pa_printfile, "p/%*s ", w, ev->ev_name); } - (void) fflush(a->pa_printfile); + (void) fflush(args.pa_printfile); } void -pmcstat_print_counters(struct pmcstat_args *a) +pmcstat_print_counters(void) { int extra_width; struct pmcstat_ev *ev; @@ -418,7 +421,7 @@ pmcstat_print_counters(struct pmcstat_args *a) extra_width = sizeof(PRINT_HEADER_PREFIX) - 1; - STAILQ_FOREACH(ev, &a->pa_events, ev_next) { + STAILQ_FOREACH(ev, &args.pa_events, ev_next) { /* skip sampling mode counters */ if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) @@ -428,7 +431,7 @@ pmcstat_print_counters(struct pmcstat_args *a) err(EX_OSERR, "ERROR: Cannot read pmc " "\"%s\"", ev->ev_name); - (void) fprintf(a->pa_printfile, "%*ju ", + (void) fprintf(args.pa_printfile, "%*ju ", ev->ev_fieldwidth + extra_width, (uintmax_t) ev->ev_cumulative ? value : (value - ev->ev_saved)); @@ -438,7 +441,7 @@ pmcstat_print_counters(struct pmcstat_args *a) extra_width = 0; } - (void) fflush(a->pa_printfile); + (void) fflush(args.pa_printfile); } /* @@ -446,20 +449,20 @@ pmcstat_print_counters(struct pmcstat_args *a) */ void -pmcstat_print_pmcs(struct pmcstat_args *a) +pmcstat_print_pmcs(void) { static int linecount = 0; /* check if we need to print a header line */ if (++linecount > pmcstat_displayheight) { - (void) fprintf(a->pa_printfile, "\n"); + (void) fprintf(args.pa_printfile, "\n"); linecount = 1; } if (linecount == 1) - pmcstat_print_headers(a); - (void) fprintf(a->pa_printfile, "\n"); + pmcstat_print_headers(); + (void) fprintf(args.pa_printfile, "\n"); - pmcstat_print_counters(a); + pmcstat_print_counters(); return; } @@ -493,6 +496,8 @@ pmcstat_show_usage(void) "\t -C\t\t (toggle) show cumulative counts\n" "\t -D path\t create profiles in directory \"path\"\n" "\t -E\t\t (toggle) show counts at process exit\n" + "\t -F file\t write a system-wide callgraph (Kcachegrind format)" + " to \"file\"\n" "\t -G file\t write a system-wide callgraph to \"file\"\n" "\t -M file\t print executable/gmon file map to \"file\"\n" "\t -N\t\t (toggle) capture callchains\n" @@ -500,9 +505,11 @@ pmcstat_show_usage(void) "\t -P spec\t allocate a process-private sampling PMC\n" "\t -R file\t read events from \"file\"\n" "\t -S spec\t allocate a system-wide sampling PMC\n" + "\t -T\t\t start in top mode\n" "\t -W\t\t (toggle) show counts per context switch\n" "\t -c cpu-list\t set cpus for subsequent system-wide PMCs\n" "\t -d\t\t (toggle) track descendants\n" + "\t -f spec\t pass \"spec\" to as plugin option\n" "\t -g\t\t produce gprof(1) compatible profiles\n" "\t -k dir\t\t set the path to the kernel\n" "\t -n rate\t set sampling rate\n" @@ -520,6 +527,24 @@ pmcstat_show_usage(void) } /* + * At exit handler for top mode + */ + +void +pmcstat_topexit(void) +{ + if (!args.pa_toptty) + return; + + /* + * Shutdown ncurses. + */ + clrtoeol(); + refresh(); + endwin(); +} + +/* * Main */ @@ -535,6 +560,7 @@ main(int argc, char **argv) int graphdepth; int pipefd[2]; int use_cumulative_counts; + short cf, cb; uint32_t cpumask; char *end, *tmp; const char *errmsg, *graphfilename; @@ -570,6 +596,13 @@ main(int argc, char **argv) args.pa_mapfilename = NULL; args.pa_inputpath = NULL; args.pa_outputpath = NULL; + args.pa_pplugin = PMCSTAT_PL_NONE; + args.pa_plugin = PMCSTAT_PL_NONE; + args.pa_ctdumpinstr = 1; + args.pa_topmode = PMCSTAT_TOP_DELTA; + args.pa_toptty = 0; + args.pa_topcolor = 0; + args.pa_mergepmc = 0; STAILQ_INIT(&args.pa_events); SLIST_INIT(&args.pa_targets); bzero(&ds_start, sizeof(ds_start)); @@ -594,7 +627,7 @@ main(int argc, char **argv) } while ((option = getopt(argc, argv, - "CD:EG:M:NO:P:R:S:Wc:dgk:m:n:o:p:qr:s:t:vw:z:")) != -1) + "CD:EF:G:M:NO:P:R:S:TWc:df:gk:m:n:o:p:qr:s:t:vw:z:")) != -1) switch (option) { case 'C': /* cumulative values */ use_cumulative_counts = !use_cumulative_counts; @@ -628,13 +661,28 @@ main(int argc, char **argv) args.pa_required |= FLAG_HAS_PROCESS_PMCS; break; + case 'F': /* produce a system-wide calltree */ + args.pa_flags |= FLAG_DO_CALLGRAPHS; + args.pa_plugin = PMCSTAT_PL_CALLTREE; + graphfilename = optarg; + break; + + case 'f': /* plugins options */ + if (args.pa_plugin == PMCSTAT_PL_NONE) + err(EX_USAGE, "ERROR: Need -g/-G/-m/-T."); + pmcstat_pluginconfigure_log(optarg); + break; + case 'G': /* produce a system-wide callgraph */ args.pa_flags |= FLAG_DO_CALLGRAPHS; + args.pa_plugin = PMCSTAT_PL_CALLGRAPH; graphfilename = optarg; break; case 'g': /* produce gprof compatible profiles */ args.pa_flags |= FLAG_DO_GPROF; + args.pa_pplugin = PMCSTAT_PL_CALLGRAPH; + args.pa_plugin = PMCSTAT_PL_GPROF; break; case 'k': /* pathname to the kernel */ @@ -645,8 +693,9 @@ main(int argc, char **argv) break; case 'm': - args.pa_flags |= FLAG_WANTS_MAPPINGS; - graphfilename = optarg; + args.pa_flags |= FLAG_DO_ANNOTATE; + args.pa_plugin = PMCSTAT_PL_ANNOTATE; + graphfilename = optarg; break; case 'E': /* log process exit */ @@ -732,7 +781,7 @@ main(int argc, char **argv) STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next); if (option == 's' || option == 'S') - pmcstat_clone_event_descriptor(&args, ev, + pmcstat_clone_event_descriptor(ev, cpumask & ~(1 << ev->ev_cpu)); break; @@ -782,12 +831,21 @@ main(int argc, char **argv) break; case 't': /* target pid or process name */ - pmcstat_find_targets(&args, optarg); + pmcstat_find_targets(optarg); args.pa_flags |= FLAG_HAS_TARGET; args.pa_required |= FLAG_HAS_PROCESS_PMCS; break; + case 'T': /* top mode */ + args.pa_flags |= FLAG_DO_TOP; + args.pa_plugin = PMCSTAT_PL_CALLGRAPH; + args.pa_ctdumpinstr = 0; + args.pa_mergepmc = 1; + if (args.pa_printfile == stderr) + args.pa_printfile = stdout; + break; + case 'v': /* verbose */ args.pa_verbosity++; break; @@ -798,7 +856,6 @@ main(int argc, char **argv) errx(EX_USAGE, "ERROR: Illegal wait interval " "value \"%s\".", optarg); args.pa_flags |= FLAG_HAS_WAIT_INTERVAL; - args.pa_required |= FLAG_HAS_COUNTING_PMCS; args.pa_interval = interval; break; @@ -833,7 +890,7 @@ main(int argc, char **argv) args.pa_flags |= FLAG_HAS_COMMANDLINE; if (args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS | - FLAG_WANTS_MAPPINGS)) + FLAG_DO_ANNOTATE | FLAG_DO_TOP)) args.pa_flags |= FLAG_DO_ANALYSIS; /* @@ -846,11 +903,11 @@ main(int argc, char **argv) "exclusive."); /* -m option is allowed with -R only. */ - if (args.pa_flags & FLAG_WANTS_MAPPINGS && args.pa_inputpath == NULL) + if (args.pa_flags & FLAG_DO_ANNOTATE && args.pa_inputpath == NULL) errx(EX_USAGE, "ERROR: option -m requires an input file"); /* -m option is not allowed combined with -g or -G. */ - if (args.pa_flags & FLAG_WANTS_MAPPINGS && + if (args.pa_flags & FLAG_DO_ANNOTATE && args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS)) errx(EX_USAGE, "ERROR: option -m and -g | -G are mutually " "exclusive"); @@ -904,7 +961,7 @@ main(int argc, char **argv) /* check for counting mode options without a counting PMC */ if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) && (args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0) - errx(EX_USAGE, "ERROR: options -C, -W, -o and -w require at " + errx(EX_USAGE, "ERROR: options -C, -W and -o require at " "least one counting mode PMC to be specified."); /* check for sampling mode options without a sampling PMC spec */ @@ -913,10 +970,10 @@ main(int argc, char **argv) errx(EX_USAGE, "ERROR: options -N, -n and -O require at " "least one sampling mode PMC to be specified."); - /* check if -g/-G are being used correctly */ + /* check if -g/-G/-m/-T are being used correctly */ if ((args.pa_flags & FLAG_DO_ANALYSIS) && !(args.pa_flags & (FLAG_HAS_SAMPLING_PMCS|FLAG_READ_LOGFILE))) - errx(EX_USAGE, "ERROR: options -g/-G require sampling PMCs " + errx(EX_USAGE, "ERROR: options -g/-G/-m/-T require sampling PMCs " "or -R to be specified."); /* check if -O was spuriously specified */ @@ -926,11 +983,11 @@ main(int argc, char **argv) "ERROR: option -O is used only with options " "-E, -P, -S and -W."); - /* -k kernel path require -g/-G or -R */ + /* -k kernel path require -g/-G/-m/-T or -R */ if ((args.pa_flags & FLAG_HAS_KERNELPATH) && (args.pa_flags & FLAG_DO_ANALYSIS) == 0 && (args.pa_flags & FLAG_READ_LOGFILE) == 0) - errx(EX_USAGE, "ERROR: option -k is only used with -g/-R."); + errx(EX_USAGE, "ERROR: option -k is only used with -g/-R/-m/-T."); /* -D only applies to gprof output mode (-g) */ if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) && @@ -943,6 +1000,11 @@ main(int argc, char **argv) (args.pa_flags & FLAG_READ_LOGFILE) == 0) errx(EX_USAGE, "ERROR: option -M is only used with -g/-R."); + /* -T is incompatible with -R (replay logfile is a TODO) */ + if ((args.pa_flags & FLAG_DO_TOP) && + (args.pa_flags & FLAG_READ_LOGFILE)) + errx(EX_USAGE, "ERROR: option -T is incompatible with -R."); + /* * Disallow textual output of sampling PMCs if counting PMCs * have also been asked for, mostly because the combined output @@ -996,7 +1058,7 @@ main(int argc, char **argv) "for writing", graphfilename); } } - if (args.pa_flags & FLAG_WANTS_MAPPINGS) { + if (args.pa_flags & FLAG_DO_ANNOTATE) { args.pa_graphfile = fopen(graphfilename, "w"); if (args.pa_graphfile == NULL) err(EX_OSERR, "ERROR: cannot open \"%s\" for writing", @@ -1012,13 +1074,13 @@ main(int argc, char **argv) if ((args.pa_flags & FLAG_DO_ANALYSIS) == 0) args.pa_flags |= FLAG_DO_PRINT; - pmcstat_initialize_logging(&args); + pmcstat_initialize_logging(); args.pa_logfd = pmcstat_open_log(args.pa_inputpath, PMCSTAT_OPEN_FOR_READ); if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL) err(EX_OSERR, "ERROR: Cannot create parser"); - pmcstat_process_log(&args); - pmcstat_shutdown_logging(&args); + pmcstat_process_log(); + pmcstat_shutdown_logging(); exit(EX_OK); } @@ -1062,7 +1124,9 @@ main(int argc, char **argv) args.pa_logfd = pipefd[WRITEPIPEFD]; - args.pa_flags |= (FLAG_HAS_PIPE | FLAG_DO_PRINT); + args.pa_flags |= FLAG_HAS_PIPE; + if ((args.pa_flags & FLAG_DO_TOP) == 0) + args.pa_flags |= FLAG_DO_PRINT; args.pa_logparser = pmclog_open(pipefd[READPIPEFD]); } @@ -1126,12 +1190,24 @@ main(int argc, char **argv) err(EX_OSERR, "ERROR: Cannot determine window size"); pmcstat_displayheight = ws.ws_row - 1; + pmcstat_displaywidth = ws.ws_col - 1; EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent for " "SIGWINCH"); + + args.pa_toptty = 1; + } + + /* + * Listen to key input in top mode. + */ + if (args.pa_flags & FLAG_DO_TOP) { + EV_SET(&kev, fileno(stdin), EVFILT_READ, EV_ADD, 0, 0, NULL); + if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) + err(EX_OSERR, "ERROR: Cannot register kevent"); } EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); @@ -1152,9 +1228,13 @@ main(int argc, char **argv) if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD"); - /* setup a timer if we have counting mode PMCs needing to be printed */ - if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && - (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { + /* + * Setup a timer if we have counting mode PMCs needing to be printed or + * top mode plugin is active. + */ + if (((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && + (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) || + (args.pa_flags & FLAG_DO_TOP)) { EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, args.pa_interval * 1000, NULL); @@ -1165,7 +1245,7 @@ main(int argc, char **argv) /* attach PMCs to the target process, starting it if specified */ if (args.pa_flags & FLAG_HAS_COMMANDLINE) - pmcstat_create_process(&args); + pmcstat_create_process(); if (check_driver_stats && pmc_get_driver_stats(&ds_start) < 0) err(EX_OSERR, "ERROR: Cannot retrieve driver statistics"); @@ -1176,7 +1256,7 @@ main(int argc, char **argv) errx(EX_DATAERR, "ERROR: No matching target " "processes."); if (args.pa_flags & FLAG_HAS_PROCESS_PMCS) - pmcstat_attach_pmcs(&args); + pmcstat_attach_pmcs(); if (pmcstat_kvm) { kvm_close(pmcstat_kvm); @@ -1185,16 +1265,16 @@ main(int argc, char **argv) } /* start the pmcs */ - pmcstat_start_pmcs(&args); + pmcstat_start_pmcs(); /* start the (commandline) process if needed */ if (args.pa_flags & FLAG_HAS_COMMANDLINE) pmcstat_start_process(); /* initialize logging if printing the configured log */ - if ((args.pa_flags & FLAG_DO_PRINT) && + if ((args.pa_flags & (FLAG_DO_PRINT | FLAG_DO_TOP)) && (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))) - pmcstat_initialize_logging(&args); + pmcstat_initialize_logging(); /* Handle SIGINT using the kqueue loop */ sa.sa_handler = SIG_IGN; @@ -1205,6 +1285,37 @@ main(int argc, char **argv) err(EX_OSERR, "ERROR: Cannot install signal handler"); /* + * Setup the top mode display. + */ + if (args.pa_flags & FLAG_DO_TOP) { + args.pa_flags &= ~FLAG_DO_PRINT; + + if (args.pa_toptty) { + /* + * Init ncurses. + */ + initscr(); + if(has_colors() == TRUE) { + args.pa_topcolor = 1; + start_color(); + use_default_colors(); + pair_content(0, &cf, &cb); + init_pair(1, COLOR_RED, cb); + init_pair(2, COLOR_YELLOW, cb); + init_pair(3, COLOR_GREEN, cb); + } + cbreak(); + noecho(); + nonl(); + nodelay(stdscr, 1); + intrflush(stdscr, FALSE); + keypad(stdscr, TRUE); + clear(); + atexit(pmcstat_topexit); + } + } + + /* * loop till either the target process (if any) exits, or we * are killed by a SIGINT. */ @@ -1225,14 +1336,18 @@ main(int argc, char **argv) case EVFILT_PROC: /* target has exited */ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) - runstate = pmcstat_close_log(&args); + runstate = pmcstat_close_log(); else runstate = PMCSTAT_FINISHED; do_print = 1; break; case EVFILT_READ: /* log file data is present */ - runstate = pmcstat_process_log(&args); + if (kev.ident == (unsigned)fileno(stdin)) { + if (pmcstat_keypress_log()) + runstate = pmcstat_close_log(); + } else + runstate = pmcstat_process_log(); break; case EVFILT_SIGNAL: @@ -1253,17 +1368,17 @@ main(int argc, char **argv) */ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) { - runstate = pmcstat_close_log(&args); + runstate = pmcstat_close_log(); if (args.pa_flags & (FLAG_DO_PRINT|FLAG_DO_ANALYSIS)) - pmcstat_process_log(&args); + pmcstat_process_log(); } do_print = 1; /* print PMCs at exit */ runstate = PMCSTAT_FINISHED; } else if (kev.ident == SIGINT) { /* Kill the child process if we started it */ if (args.pa_flags & FLAG_HAS_COMMANDLINE) - pmcstat_kill_process(&args); + pmcstat_kill_process(); /* Close the pipe to self, if present. */ if (args.pa_flags & FLAG_HAS_PIPE) (void) close(pipefd[READPIPEFD]); @@ -1274,6 +1389,7 @@ main(int argc, char **argv) err(EX_OSERR, "ERROR: Cannot determine " "window size"); pmcstat_displayheight = ws.ws_row - 1; + pmcstat_displaywidth = ws.ws_col - 1; } else assert(0); @@ -1285,22 +1401,30 @@ main(int argc, char **argv) } - if (do_print && - (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { - pmcstat_print_pmcs(&args); - if (runstate == PMCSTAT_FINISHED && /* final newline */ - (args.pa_flags & FLAG_DO_PRINT) == 0) - (void) fprintf(args.pa_printfile, "\n"); + if (do_print) { + if ((args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { + pmcstat_print_pmcs(); + if (runstate == PMCSTAT_FINISHED && /* final newline */ + (args.pa_flags & FLAG_DO_PRINT) == 0) + (void) fprintf(args.pa_printfile, "\n"); + } + if (args.pa_flags & FLAG_DO_TOP) + pmcstat_display_log(); do_print = 0; } } while (runstate != PMCSTAT_FINISHED); + if ((args.pa_flags & FLAG_DO_TOP) && args.pa_toptty) { + pmcstat_topexit(); + args.pa_toptty = 0; + } + /* flush any pending log entries */ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) pmc_flush_logfile(); - pmcstat_cleanup(&args); + pmcstat_cleanup(); free(args.pa_kernel); diff --git a/usr.sbin/pmcstat/pmcstat.h b/usr.sbin/pmcstat/pmcstat.h index 5d6f5f2..1bb7862 100644 --- a/usr.sbin/pmcstat/pmcstat.h +++ b/usr.sbin/pmcstat/pmcstat.h @@ -47,13 +47,15 @@ #define FLAG_HAS_SAMPLESDIR 0x00000800 /* -D dir */ #define FLAG_HAS_KERNELPATH 0x00001000 /* -k kernel */ #define FLAG_DO_PRINT 0x00002000 /* -o */ -#define FLAG_DO_CALLGRAPHS 0x00004000 /* -G */ -#define FLAG_DO_ANALYSIS 0x00008000 /* -g or -G */ -#define FLAG_WANTS_MAPPINGS 0x00010000 /* -m */ +#define FLAG_DO_CALLGRAPHS 0x00004000 /* -G or -F */ +#define FLAG_DO_ANNOTATE 0x00008000 /* -m */ +#define FLAG_DO_TOP 0x00010000 /* -T */ +#define FLAG_DO_ANALYSIS 0x00020000 /* -g or -G or -m or -T */ #define DEFAULT_SAMPLE_COUNT 65536 #define DEFAULT_WAIT_INTERVAL 5.0 -#define DEFAULT_DISPLAY_HEIGHT 23 +#define DEFAULT_DISPLAY_HEIGHT 256 /* file virtual height */ +#define DEFAULT_DISPLAY_WIDTH 1024 /* file virtual width */ #define DEFAULT_BUFFER_SIZE 4096 #define DEFAULT_CALLGRAPH_DEPTH 4 @@ -75,12 +77,24 @@ #define PMCSTAT_LDD_COMMAND "/usr/bin/ldd" -#define PMCSTAT_PRINT_ENTRY(A,T,...) do { \ - (void) fprintf((A)->pa_printfile, "%-9s", T); \ - (void) fprintf((A)->pa_printfile, " " __VA_ARGS__); \ - (void) fprintf((A)->pa_printfile, "\n"); \ +#define PMCSTAT_PRINT_ENTRY(T,...) do { \ + (void) fprintf(args.pa_printfile, "%-9s", T); \ + (void) fprintf(args.pa_printfile, " " __VA_ARGS__); \ + (void) fprintf(args.pa_printfile, "\n"); \ } while (0) +#define PMCSTAT_PL_NONE 0 +#define PMCSTAT_PL_CALLGRAPH 1 +#define PMCSTAT_PL_GPROF 2 +#define PMCSTAT_PL_ANNOTATE 3 +#define PMCSTAT_PL_CALLTREE 4 + +#define PMCSTAT_TOP_DELTA 0 +#define PMCSTAT_TOP_ACCUM 1 + +#define min(A,B) ((A) < (B) ? (A) : (B)) +#define max(A,B) ((A) > (B) ? (A) : (B)) + enum pmcstat_state { PMCSTAT_FINISHED = 0, PMCSTAT_EXITING = 1, @@ -110,6 +124,8 @@ struct pmcstat_target { struct pmcstat_args { int pa_flags; /* argument flags */ int pa_required; /* required features */ + int pa_pplugin; /* pre-processing plugin */ + int pa_plugin; /* analysis plugin */ int pa_verbosity; /* verbosity level */ FILE *pa_printfile; /* where to send printed output */ int pa_logfd; /* output log file */ @@ -124,31 +140,44 @@ struct pmcstat_args { int pa_graphdepth; /* print depth for callgraphs */ double pa_interval; /* printing interval in seconds */ uint32_t pa_cpumask; /* filter for CPUs analysed */ + int pa_ctdumpinstr; /* dump instructions with calltree */ + int pa_topmode; /* delta or accumulative */ + int pa_toptty; /* output to tty or file */ + int pa_topcolor; /* terminal support color */ + int pa_mergepmc; /* merge PMC with same name */ int pa_argc; char **pa_argv; STAILQ_HEAD(, pmcstat_ev) pa_events; SLIST_HEAD(, pmcstat_target) pa_targets; -} args; +}; + +extern int pmcstat_displayheight; /* current terminal height */ +extern int pmcstat_displaywidth; /* current terminal width */ +extern struct pmcstat_args args; /* command line args */ /* Function prototypes */ -void pmcstat_attach_pmcs(struct pmcstat_args *_a); -void pmcstat_cleanup(struct pmcstat_args *_a); -void pmcstat_clone_event_descriptor(struct pmcstat_args *_a, +void pmcstat_attach_pmcs(void); +void pmcstat_cleanup(void); +void pmcstat_clone_event_descriptor( struct pmcstat_ev *_ev, uint32_t _cpumask); -int pmcstat_close_log(struct pmcstat_args *_a); -void pmcstat_create_process(struct pmcstat_args *_a); -void pmcstat_find_targets(struct pmcstat_args *_a, const char *_arg); -void pmcstat_initialize_logging(struct pmcstat_args *_a); -void pmcstat_kill_process(struct pmcstat_args *_a); +int pmcstat_close_log(void); +void pmcstat_create_process(void); +void pmcstat_find_targets(const char *_arg); +void pmcstat_initialize_logging(void); +void pmcstat_kill_process(void); int pmcstat_open_log(const char *_p, int _mode); -void pmcstat_print_counters(struct pmcstat_args *_a); -void pmcstat_print_headers(struct pmcstat_args *_a); -void pmcstat_print_pmcs(struct pmcstat_args *_a); +void pmcstat_print_counters(void); +void pmcstat_print_headers(void); +void pmcstat_print_pmcs(void); void pmcstat_show_usage(void); -void pmcstat_shutdown_logging(struct pmcstat_args *_a); -void pmcstat_start_pmcs(struct pmcstat_args *_a); +void pmcstat_shutdown_logging(void); +void pmcstat_start_pmcs(void); void pmcstat_start_process(void); -int pmcstat_process_log(struct pmcstat_args *_a); +int pmcstat_process_log(void); +int pmcstat_keypress_log(void); +void pmcstat_display_log(void); +void pmcstat_pluginconfigure_log(char *_opt); uint32_t pmcstat_get_cpumask(const char *_a); +void pmcstat_topexit(void); #endif /* _PMCSTAT_H_ */ diff --git a/usr.sbin/pmcstat/pmcstat_log.c b/usr.sbin/pmcstat/pmcstat_log.c index a403852..5811af3 100644 --- a/usr.sbin/pmcstat/pmcstat_log.c +++ b/usr.sbin/pmcstat/pmcstat_log.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include <netinet/in.h> #include <assert.h> +#include <curses.h> #include <err.h> #include <errno.h> #include <fcntl.h> @@ -68,9 +69,8 @@ __FBSDID("$FreeBSD$"); #include <unistd.h> #include "pmcstat.h" - -#define min(A,B) ((A) < (B) ? (A) : (B)) -#define max(A,B) ((A) > (B) ? (A) : (B)) +#include "pmcstat_log.h" +#include "pmcstat_top.h" #define PMCSTAT_ALLOCATE 1 @@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$"); * pmcstat_shutdown_logging() orderly shutdown, called last * pmcstat_open_log() open an eventlog for processing * pmcstat_process_log() print/convert an event log + * pmcstat_display_log() top mode display for the log * pmcstat_close_log() finish processing an event log * * IMPLEMENTATION NOTES @@ -127,236 +128,125 @@ __FBSDID("$FreeBSD$"); * also given a 'rank' that reflects its depth in the call stack. */ -typedef const void *pmcstat_interned_string; - -/* - * 'pmcstat_pmcrecord' is a mapping from PMC ids to human-readable - * names. - */ - -struct pmcstat_pmcrecord { - LIST_ENTRY(pmcstat_pmcrecord) pr_next; - pmc_id_t pr_pmcid; - pmcstat_interned_string pr_pmcname; -}; - -static LIST_HEAD(,pmcstat_pmcrecord) pmcstat_pmcs = - LIST_HEAD_INITIALIZER(pmcstat_pmcs); - - -/* - * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These - * files are mmap()'ed in as needed. - */ - -struct pmcstat_gmonfile { - LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ - int pgf_overflow; /* whether a count overflowed */ - pmc_id_t pgf_pmcid; /* id of the associated pmc */ - size_t pgf_nbuckets; /* #buckets in this gmon.out */ - unsigned int pgf_nsamples; /* #samples in this gmon.out */ - pmcstat_interned_string pgf_name; /* pathname of gmon.out file */ - size_t pgf_ndatabytes; /* number of bytes mapped */ - void *pgf_gmondata; /* pointer to mmap'ed data */ - FILE *pgf_file; /* used when writing gmon arcs */ -}; - -/* - * A 'pmcstat_image' structure describes an executable program on - * disk. 'pi_execpath' is a cookie representing the pathname of - * the executable. 'pi_start' and 'pi_end' are the least and greatest - * virtual addresses for the text segments in the executable. - * 'pi_gmonlist' contains a linked list of gmon.out files associated - * with this image. - */ - -enum pmcstat_image_type { - PMCSTAT_IMAGE_UNKNOWN = 0, /* never looked at the image */ - PMCSTAT_IMAGE_INDETERMINABLE, /* can't tell what the image is */ - PMCSTAT_IMAGE_ELF32, /* ELF 32 bit object */ - PMCSTAT_IMAGE_ELF64, /* ELF 64 bit object */ - PMCSTAT_IMAGE_AOUT /* AOUT object */ -}; - -struct pmcstat_image { - LIST_ENTRY(pmcstat_image) pi_next; /* hash link */ - TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */ - pmcstat_interned_string pi_execpath; /* cookie */ - pmcstat_interned_string pi_samplename; /* sample path name */ - pmcstat_interned_string pi_fullpath; /* path to FS object */ - - enum pmcstat_image_type pi_type; /* executable type */ - - /* - * Executables have pi_start and pi_end; these are zero - * for shared libraries. - */ - uintfptr_t pi_start; /* start address (inclusive) */ - uintfptr_t pi_end; /* end address (exclusive) */ - uintfptr_t pi_entry; /* entry address */ - uintfptr_t pi_vaddr; /* virtual address where loaded */ - int pi_isdynamic; /* whether a dynamic object */ - int pi_iskernelmodule; - pmcstat_interned_string pi_dynlinkerpath; /* path in .interp */ - - /* All symbols associated with this object. */ - struct pmcstat_symbol *pi_symbols; - size_t pi_symcount; - - /* - * An image can be associated with one or more gmon.out files; - * one per PMC. - */ - LIST_HEAD(,pmcstat_gmonfile) pi_gmlist; -}; +struct pmcstat_pmcs pmcstat_pmcs = LIST_HEAD_INITIALIZER(pmcstat_pmcs); /* * All image descriptors are kept in a hash table. */ -static LIST_HEAD(,pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH]; - -/* - * A 'pmcstat_pcmap' structure maps a virtual address range to an - * underlying 'pmcstat_image' descriptor. - */ -struct pmcstat_pcmap { - TAILQ_ENTRY(pmcstat_pcmap) ppm_next; - uintfptr_t ppm_lowpc; - uintfptr_t ppm_highpc; - struct pmcstat_image *ppm_image; -}; +struct pmcstat_image_hash_list pmcstat_image_hash[PMCSTAT_NHASH]; /* - * A 'pmcstat_process' structure models processes. Each process is - * associated with a set of pmcstat_pcmap structures that map - * addresses inside it to executable objects. This set is implemented - * as a list, kept sorted in ascending order of mapped addresses. - * - * 'pp_pid' holds the pid of the process. When a process exits, the - * 'pp_isactive' field is set to zero, but the process structure is - * not immediately reclaimed because there may still be samples in the - * log for this process. + * All process descriptors are kept in a hash table. */ +struct pmcstat_process_hash_list pmcstat_process_hash[PMCSTAT_NHASH]; -struct pmcstat_process { - LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */ - pid_t pp_pid; /* associated pid */ - int pp_isactive; /* whether active */ - uintfptr_t pp_entryaddr; /* entry address */ - TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */ -}; +struct pmcstat_stats pmcstat_stats; /* statistics */ -/* - * All process descriptors are kept in a hash table. - */ -static LIST_HEAD(,pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH]; +struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ -static struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ +#include "pmcpl_gprof.h" +#include "pmcpl_callgraph.h" +#include "pmcpl_annotate.h" +#include "pmcpl_calltree.h" -/* - * Each function symbol tracked by pmcstat(8). - */ +struct pmc_plugins { + const char *pl_name; /* name */ -struct pmcstat_symbol { - pmcstat_interned_string ps_name; - uint64_t ps_start; - uint64_t ps_end; -}; + /* configure */ + int (*pl_configure)(char *opt); -/* - * Each call graph node is tracked by a pmcstat_cgnode struct. - */ + /* init and shutdown */ + int (*pl_init)(void); + void (*pl_shutdown)(FILE *mf); -struct pmcstat_cgnode { - struct pmcstat_image *pcg_image; - uintfptr_t pcg_func; - uint32_t pcg_count; - uint32_t pcg_nchildren; - LIST_ENTRY(pmcstat_cgnode) pcg_sibling; - LIST_HEAD(,pmcstat_cgnode) pcg_children; -}; + /* sample processing */ + void (*pl_process)(struct pmcstat_process *pp, + struct pmcstat_pmcrecord *pmcr, uint32_t nsamples, + uintfptr_t *cc, int usermode, uint32_t cpu); -struct pmcstat_cgnode_hash { - struct pmcstat_cgnode *pch_cgnode; - uint32_t pch_pmcid; - LIST_ENTRY(pmcstat_cgnode_hash) pch_next; -}; + /* image */ + void (*pl_initimage)(struct pmcstat_image *pi); + void (*pl_shutdownimage)(struct pmcstat_image *pi); -static int pmcstat_cgnode_hash_count; -static pmcstat_interned_string pmcstat_previous_filename_printed; + /* pmc */ + void (*pl_newpmc)(pmcstat_interned_string ps, + struct pmcstat_pmcrecord *pr); + + /* top display */ + void (*pl_topdisplay)(void); -/* - * The toplevel CG nodes (i.e., with rank == 0) are placed in a hash table. - */ + /* top keypress */ + int (*pl_topkeypress)(int c, WINDOW *w); -static LIST_HEAD(,pmcstat_cgnode_hash) pmcstat_cgnode_hash[PMCSTAT_NHASH]; +} plugins[] = { + { + .pl_name = "none", + }, + { + .pl_name = "callgraph", + .pl_init = pmcpl_cg_init, + .pl_shutdown = pmcpl_cg_shutdown, + .pl_process = pmcpl_cg_process, + .pl_topkeypress = pmcpl_cg_topkeypress, + .pl_topdisplay = pmcpl_cg_topdisplay + }, + { + .pl_name = "gprof", + .pl_shutdown = pmcpl_gmon_shutdown, + .pl_process = pmcpl_gmon_process, + .pl_initimage = pmcpl_gmon_initimage, + .pl_shutdownimage = pmcpl_gmon_shutdownimage, + .pl_newpmc = pmcpl_gmon_newpmc + }, + { + .pl_name = "annotate", + .pl_process = pmcpl_annotate_process + }, + { + .pl_name = "calltree", + .pl_configure = pmcpl_ct_configure, + .pl_init = pmcpl_ct_init, + .pl_shutdown = pmcpl_ct_shutdown, + .pl_process = pmcpl_ct_process, + .pl_topkeypress = pmcpl_ct_topkeypress, + .pl_topdisplay = pmcpl_ct_topdisplay + }, + { + .pl_name = NULL + } +}; -/* Misc. statistics */ -static struct pmcstat_stats { - int ps_exec_aout; /* # a.out executables seen */ - int ps_exec_elf; /* # elf executables seen */ - int ps_exec_errors; /* # errors processing executables */ - int ps_exec_indeterminable; /* # unknown executables seen */ - int ps_samples_total; /* total number of samples processed */ - int ps_samples_skipped; /* #samples filtered out for any reason */ - int ps_samples_unknown_offset; /* #samples of rank 0 not in a map */ - int ps_samples_indeterminable; /* #samples in indeterminable images */ - int ps_callchain_dubious_frames;/* #dubious frame pointers seen */ -} pmcstat_stats; +int pmcstat_mergepmc; +int pmcstat_pmcinfilter = 0; /* PMC filter for top mode. */ +float pmcstat_threshold = 0.5; /* Cost filter for top mode. */ /* * Prototypes */ -static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, - struct pmcstat_image *_image); -static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, - struct pmcstat_image *_img, pmc_id_t _pmcid); -static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); -static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); - -static void pmcstat_image_determine_type(struct pmcstat_image *_image, - struct pmcstat_args *_a); -static struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct - pmcstat_image *_i, pmc_id_t _id); static struct pmcstat_image *pmcstat_image_from_path(pmcstat_interned_string _path, int _iskernelmodule); -static void pmcstat_image_get_aout_params(struct pmcstat_image *_image, - struct pmcstat_args *_a); -static void pmcstat_image_get_elf_params(struct pmcstat_image *_image, - struct pmcstat_args *_a); -static void pmcstat_image_increment_bucket(struct pmcstat_pcmap *_pcm, - uintfptr_t _pc, pmc_id_t _pmcid, struct pmcstat_args *_a); +static void pmcstat_image_get_aout_params(struct pmcstat_image *_image); +static void pmcstat_image_get_elf_params(struct pmcstat_image *_image); static void pmcstat_image_link(struct pmcstat_process *_pp, struct pmcstat_image *_i, uintfptr_t _lpc); static void pmcstat_pmcid_add(pmc_id_t _pmcid, - pmcstat_interned_string _name, struct pmcstat_args *_a); -static const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid); + pmcstat_interned_string _name); static void pmcstat_process_aout_exec(struct pmcstat_process *_pp, - struct pmcstat_image *_image, uintfptr_t _entryaddr, - struct pmcstat_args *_a); + struct pmcstat_image *_image, uintfptr_t _entryaddr); static void pmcstat_process_elf_exec(struct pmcstat_process *_pp, - struct pmcstat_image *_image, uintfptr_t _entryaddr, - struct pmcstat_args *_a); + struct pmcstat_image *_image, uintfptr_t _entryaddr); static void pmcstat_process_exec(struct pmcstat_process *_pp, - pmcstat_interned_string _path, uintfptr_t _entryaddr, - struct pmcstat_args *_ao); + pmcstat_interned_string _path, uintfptr_t _entryaddr); static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, int _allocate); -static struct pmcstat_pcmap *pmcstat_process_find_map( - struct pmcstat_process *_p, uintfptr_t _pc); - static int pmcstat_string_compute_hash(const char *_string); static void pmcstat_string_initialize(void); -static pmcstat_interned_string pmcstat_string_intern(const char *_s); -static pmcstat_interned_string pmcstat_string_lookup(const char *_s); static int pmcstat_string_lookup_hash(pmcstat_interned_string _is); static void pmcstat_string_shutdown(void); -static const char *pmcstat_string_unintern(pmcstat_interned_string _is); - /* * A simple implementation of interned strings. Each interned string @@ -375,6 +265,16 @@ struct pmcstat_string { static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH]; /* + * PMC count. + */ +int pmcstat_npmcs; + +/* + * PMC Top mode pause state. + */ +int pmcstat_pause; + +/* * Compute a 'hash' value for a string. */ @@ -394,7 +294,7 @@ pmcstat_string_compute_hash(const char *s) * interned structure. */ -static pmcstat_interned_string +pmcstat_interned_string pmcstat_string_intern(const char *s) { struct pmcstat_string *ps; @@ -416,7 +316,7 @@ pmcstat_string_intern(const char *s) return ((pmcstat_interned_string) ps); } -static const char * +const char * pmcstat_string_unintern(pmcstat_interned_string str) { const char *s; @@ -425,7 +325,7 @@ pmcstat_string_unintern(pmcstat_interned_string str) return (s); } -static pmcstat_interned_string +pmcstat_interned_string pmcstat_string_lookup(const char *s) { struct pmcstat_string *ps; @@ -483,163 +383,13 @@ pmcstat_string_shutdown(void) } /* - * Create a gmon.out file and size it. - */ - -static void -pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, - struct pmcstat_image *image) -{ - int fd; - size_t count; - struct gmonhdr gm; - const char *pathname; - char buffer[DEFAULT_BUFFER_SIZE]; - - pathname = pmcstat_string_unintern(pgf->pgf_name); - if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) - err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname); - - gm.lpc = image->pi_start; - gm.hpc = image->pi_end; - gm.ncnt = (pgf->pgf_nbuckets * sizeof(HISTCOUNTER)) + - sizeof(struct gmonhdr); - gm.version = GMONVERSION; - gm.profrate = 0; /* use ticks */ - gm.histcounter_type = 0; /* compatibility with moncontrol() */ - gm.spare[0] = gm.spare[1] = 0; - - /* Write out the gmon header */ - if (write(fd, &gm, sizeof(gm)) < 0) - goto error; - - /* Zero fill the samples[] array */ - (void) memset(buffer, 0, sizeof(buffer)); - - count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); - while (count > sizeof(buffer)) { - if (write(fd, &buffer, sizeof(buffer)) < 0) - goto error; - count -= sizeof(buffer); - } - - if (write(fd, &buffer, count) < 0) - goto error; - - (void) close(fd); - - return; - - error: - err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname); -} - -/* - * Determine the full pathname of a gmon.out file for a given - * (image,pmcid) combination. Return the interned string. - */ - -pmcstat_interned_string -pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, - pmc_id_t pmcid) -{ - const char *pmcname; - char fullpath[PATH_MAX]; - - pmcname = pmcstat_pmcid_to_name(pmcid); - - (void) snprintf(fullpath, sizeof(fullpath), - "%s/%s/%s", samplesdir, pmcname, - pmcstat_string_unintern(image->pi_samplename)); - - return (pmcstat_string_intern(fullpath)); -} - - -/* - * Mmap in a gmon.out file for processing. - */ - -static void -pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) -{ - int fd; - const char *pathname; - - pathname = pmcstat_string_unintern(pgf->pgf_name); - - /* the gmon.out file must already exist */ - if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0) - err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname); - - pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, - PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); - - if (pgf->pgf_gmondata == MAP_FAILED) - err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname); - - (void) close(fd); -} - -/* - * Unmap a gmon.out file after sync'ing its data to disk. - */ - -static void -pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) -{ - (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, - MS_SYNC); - (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); - pgf->pgf_gmondata = NULL; -} - -static void -pmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid, - uintptr_t rawfrom, uintptr_t rawto, uint32_t count) -{ - struct rawarc arc; /* from <sys/gmon.h> */ - const char *pathname; - struct pmcstat_gmonfile *pgf; - - if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL) - return; - - if (pgf->pgf_file == NULL) { - pathname = pmcstat_string_unintern(pgf->pgf_name); - if ((pgf->pgf_file = fopen(pathname, "a")) == NULL) - return; - } - - arc.raw_frompc = rawfrom + image->pi_vaddr; - arc.raw_selfpc = rawto + image->pi_vaddr; - arc.raw_count = count; - - (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file); - -} - -static struct pmcstat_gmonfile * -pmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid) -{ - struct pmcstat_gmonfile *pgf; - LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) - if (pgf->pgf_pmcid == pmcid) - return (pgf); - return (NULL); -} - - -/* * Determine whether a given executable image is an A.OUT object, and * if so, fill in its parameters from the text file. * Sets image->pi_type. */ static void -pmcstat_image_get_aout_params(struct pmcstat_image *image, - struct pmcstat_args *a) +pmcstat_image_get_aout_params(struct pmcstat_image *image) { int fd; ssize_t nbytes; @@ -655,7 +405,7 @@ pmcstat_image_get_aout_params(struct pmcstat_image *image, "unsupported \"%s\"", path); (void) snprintf(buffer, sizeof(buffer), "%s%s", - a->pa_fsroot, path); + args.pa_fsroot, path); if ((fd = open(buffer, O_RDONLY, 0)) < 0 || (nbytes = read(fd, &ex, sizeof(ex))) < 0) { @@ -702,7 +452,7 @@ pmcstat_symbol_compare(const void *a, const void *b) * Map an address to a symbol in an image. */ -static struct pmcstat_symbol * +struct pmcstat_symbol * pmcstat_symbol_search(struct pmcstat_image *image, uintfptr_t addr) { struct pmcstat_symbol sym; @@ -825,12 +575,12 @@ pmcstat_image_add_symbols(struct pmcstat_image *image, Elf *e, */ static void -pmcstat_image_get_elf_params(struct pmcstat_image *image, - struct pmcstat_args *a) +pmcstat_image_get_elf_params(struct pmcstat_image *image) { int fd; size_t i, nph, nsh; const char *path, *elfbase; + char *p, *endp; uintfptr_t minva, maxva; Elf *e; Elf_Scn *scn; @@ -858,10 +608,10 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, */ if (image->pi_iskernelmodule) (void) snprintf(buffer, sizeof(buffer), "%s%s/%s", - a->pa_fsroot, a->pa_kernel, path); + args.pa_fsroot, args.pa_kernel, path); else (void) snprintf(buffer, sizeof(buffer), "%s%s", - a->pa_fsroot, path); + args.pa_fsroot, path); e = NULL; if ((fd = open(buffer, O_RDONLY, 0)) < 0 || @@ -960,6 +710,14 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, image->pi_type = image_type; image->pi_fullpath = pmcstat_string_intern(buffer); + /* Build display name + */ + endp = buffer; + for (p = buffer; *p; p++) + if (*p == '/') + endp = p+1; + image->pi_name = pmcstat_string_intern(endp); + done: (void) elf_end(e); if (fd >= 0) @@ -972,17 +730,16 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, * If no handler claims the image, set its type to 'INDETERMINABLE'. */ -static void -pmcstat_image_determine_type(struct pmcstat_image *image, - struct pmcstat_args *a) +void +pmcstat_image_determine_type(struct pmcstat_image *image) { assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN); /* Try each kind of handler in turn */ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_get_elf_params(image, a); + pmcstat_image_get_elf_params(image); if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_get_aout_params(image, a); + pmcstat_image_get_aout_params(image); /* * Otherwise, remember that we tried to determine @@ -1006,10 +763,8 @@ static struct pmcstat_image * pmcstat_image_from_path(pmcstat_interned_string internedpath, int iskernelmodule) { - int count, hash, nlen; + int hash; struct pmcstat_image *pi; - char *sn; - char name[NAME_MAX]; hash = pmcstat_string_lookup_hash(internedpath); @@ -1038,50 +793,12 @@ pmcstat_image_from_path(pmcstat_interned_string internedpath, pi->pi_dynlinkerpath = NULL; pi->pi_symbols = NULL; pi->pi_symcount = 0; + pi->pi_addr2line = NULL; - /* - * Look for a suitable name for the sample files associated - * with this image: if `basename(path)`+".gmon" is available, - * we use that, otherwise we try iterating through - * `basename(path)`+ "~" + NNN + ".gmon" till we get a free - * entry. - */ - if ((sn = basename(pmcstat_string_unintern(internedpath))) == NULL) - err(EX_OSERR, "ERROR: Cannot process \"%s\"", - pmcstat_string_unintern(internedpath)); - - nlen = strlen(sn); - nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); - - snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); - - /* try use the unabridged name first */ - if (pmcstat_string_lookup(name) == NULL) - pi->pi_samplename = pmcstat_string_intern(name); - else { - /* - * Otherwise use a prefix from the original name and - * upto 3 digits. - */ - nlen = strlen(sn); - nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); - count = 0; - do { - if (++count > 999) - errx(EX_CANTCREAT, "ERROR: cannot create a " - "gmon file for \"%s\"", name); - snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", - nlen, sn, count); - if (pmcstat_string_lookup(name) == NULL) { - pi->pi_samplename = - pmcstat_string_intern(name); - count = 0; - } - } while (count > 0); - } - - - LIST_INIT(&pi->pi_gmlist); + if (plugins[args.pa_pplugin].pl_initimage != NULL) + plugins[args.pa_pplugin].pl_initimage(pi); + if (plugins[args.pa_plugin].pl_initimage != NULL) + plugins[args.pa_plugin].pl_initimage(pi); LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next); @@ -1089,94 +806,6 @@ pmcstat_image_from_path(pmcstat_interned_string internedpath, } /* - * Increment the bucket in the gmon.out file corresponding to 'pmcid' - * and 'pc'. - */ - -static void -pmcstat_image_increment_bucket(struct pmcstat_pcmap *map, uintfptr_t pc, - pmc_id_t pmcid, struct pmcstat_args *a) -{ - struct pmcstat_image *image; - struct pmcstat_gmonfile *pgf; - uintfptr_t bucket; - HISTCOUNTER *hc; - - assert(pc >= map->ppm_lowpc && pc < map->ppm_highpc); - - image = map->ppm_image; - - /* - * If this is the first time we are seeing a sample for - * this executable image, try determine its parameters. - */ - if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_determine_type(image, a); - - assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); - - /* Ignore samples in images that we know nothing about. */ - if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { - pmcstat_stats.ps_samples_indeterminable++; - return; - } - - /* - * Find the gmon file corresponding to 'pmcid', creating it if - * needed. - */ - pgf = pmcstat_image_find_gmonfile(image, pmcid); - if (pgf == NULL) { - if ((pgf = calloc(1, sizeof(*pgf))) == NULL) - err(EX_OSERR, "ERROR:"); - - pgf->pgf_gmondata = NULL; /* mark as unmapped */ - pgf->pgf_name = pmcstat_gmon_create_name(a->pa_samplesdir, - image, pmcid); - pgf->pgf_pmcid = pmcid; - assert(image->pi_end > image->pi_start); - pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / - FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ - pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + - pgf->pgf_nbuckets * sizeof(HISTCOUNTER); - pgf->pgf_nsamples = 0; - pgf->pgf_file = NULL; - - pmcstat_gmon_create_file(pgf, image); - - LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); - } - - /* - * Map the gmon file in if needed. It may have been mapped - * out under memory pressure. - */ - if (pgf->pgf_gmondata == NULL) - pmcstat_gmon_map_file(pgf); - - assert(pgf->pgf_gmondata != NULL); - - /* - * - */ - - bucket = (pc - map->ppm_lowpc) / FUNCTION_ALIGNMENT; - - assert(bucket < pgf->pgf_nbuckets); - - hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + - sizeof(struct gmonhdr)); - - /* saturating add */ - if (hc[bucket] < 0xFFFFU) /* XXX tie this to sizeof(HISTCOUNTER) */ - hc[bucket]++; - else /* mark that an overflow occurred */ - pgf->pgf_overflow = 1; - - pgf->pgf_nsamples++; -} - -/* * Record the fact that PC values from 'start' to 'end' come from * image 'image'. */ @@ -1284,72 +913,181 @@ pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start, } /* + * Resolve file name and line number for the given address. + */ +int +pmcstat_image_addr2line(struct pmcstat_image *image, uintfptr_t addr, + char *sourcefile, size_t sourcefile_len, unsigned *sourceline, + char *funcname, size_t funcname_len) +{ + static int addr2line_warn = 0; + + char *sep, cmdline[PATH_MAX], imagepath[PATH_MAX]; + int fd; + + if (image->pi_addr2line == NULL) { + snprintf(imagepath, sizeof(imagepath), "%s.symbols", + pmcstat_string_unintern(image->pi_fullpath)); + fd = open(imagepath, O_RDONLY); + if (fd < 0) { + snprintf(imagepath, sizeof(imagepath), "%s", + pmcstat_string_unintern(image->pi_fullpath)); + } else + close(fd); + snprintf(cmdline, sizeof(cmdline), "addr2line -Cfe \"%s\"", + imagepath); + image->pi_addr2line = popen(cmdline, "r+"); + if (image->pi_addr2line == NULL) { + if (!addr2line_warn) { + addr2line_warn = 1; + warnx("WARNING: addr2line is needed" + "for source code information."); + } + return (0); + } + } + + if (feof(image->pi_addr2line) || ferror(image->pi_addr2line)) { + warnx("WARNING: addr2line pipe error"); + pclose(image->pi_addr2line); + image->pi_addr2line = NULL; + return (0); + } + + fprintf(image->pi_addr2line, "%p\n", (void *)addr); + + if (fgets(funcname, funcname_len, image->pi_addr2line) == NULL) { + warnx("WARNING: addr2line function name read error"); + return (0); + } + sep = strchr(funcname, '\n'); + if (sep != NULL) + *sep = '\0'; + + if (fgets(sourcefile, sourcefile_len, image->pi_addr2line) == NULL) { + warnx("WARNING: addr2line source file read error"); + return (0); + } + sep = strchr(sourcefile, ':'); + if (sep == NULL) { + warnx("WARNING: addr2line source line separator missing"); + return (0); + } + *sep = '\0'; + *sourceline = atoi(sep+1); + if (*sourceline == 0) + return (0); + + return (1); +} + +/* * Add a {pmcid,name} mapping. */ static void -pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps, - struct pmcstat_args *a) +pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps) { - struct pmcstat_pmcrecord *pr; - struct stat st; - char fullpath[PATH_MAX]; + struct pmcstat_pmcrecord *pr, *prm; /* Replace an existing name for the PMC. */ + prm = NULL; LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) - if (pr->pr_pmcid == pmcid) { - pr->pr_pmcname = ps; - return; - } + if (pr->pr_pmcid == pmcid) { + pr->pr_pmcname = ps; + return; + } else if (pr->pr_pmcname == ps) + prm = pr; /* - * Otherwise, allocate a new descriptor and create the - * appropriate directory to hold gmon.out files. + * Otherwise, allocate a new descriptor and call the + * plugins hook. */ if ((pr = malloc(sizeof(*pr))) == NULL) err(EX_OSERR, "ERROR: Cannot allocate pmc record"); pr->pr_pmcid = pmcid; pr->pr_pmcname = ps; - LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); + pr->pr_pmcin = pmcstat_npmcs++; + pr->pr_merge = prm == NULL ? pr : prm; - (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", a->pa_samplesdir, - pmcstat_string_unintern(ps)); - - /* If the path name exists, it should be a directory */ - if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) - return; + LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); - if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) - err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", - fullpath); + if (plugins[args.pa_pplugin].pl_newpmc != NULL) + plugins[args.pa_pplugin].pl_newpmc(ps, pr); + if (plugins[args.pa_plugin].pl_newpmc != NULL) + plugins[args.pa_plugin].pl_newpmc(ps, pr); } /* * Given a pmcid in use, find its human-readable name. */ -static const char * +const char * pmcstat_pmcid_to_name(pmc_id_t pmcid) { struct pmcstat_pmcrecord *pr; - char fullpath[PATH_MAX]; LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) if (pr->pr_pmcid == pmcid) return (pmcstat_string_unintern(pr->pr_pmcname)); - /* create a default name and add this entry */ - if ((pr = malloc(sizeof(*pr))) == NULL) - err(EX_OSERR, "ERROR: "); - pr->pr_pmcid = pmcid; + err(EX_SOFTWARE, "ERROR: cannot find pmcid"); + return NULL; +} - (void) snprintf(fullpath, sizeof(fullpath), "%X", (unsigned int) pmcid); - pr->pr_pmcname = pmcstat_string_intern(fullpath); +/* + * Convert PMC index to name. + */ - LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next); +const char * +pmcstat_pmcindex_to_name(int pmcin) +{ + struct pmcstat_pmcrecord *pr; + + LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) + if (pr->pr_pmcin == pmcin) + return pmcstat_string_unintern(pr->pr_pmcname); + + err(EX_SOFTWARE, "ERROR: cannot find pmcid name"); + return NULL; +} + +/* + * Return PMC record with given index. + */ + +struct pmcstat_pmcrecord * +pmcstat_pmcindex_to_pmcr(int pmcin) +{ + struct pmcstat_pmcrecord *pr; + + LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) + if (pr->pr_pmcin == pmcin) + return pr; - return (pmcstat_string_unintern(pr->pr_pmcname)); + err(EX_SOFTWARE, "ERROR: invalid pmcindex"); + return NULL; +} + +/* + * Get PMC record by id, apply merge policy. + */ + +static struct pmcstat_pmcrecord * +pmcstat_lookup_pmcid(pmc_id_t pmcid) +{ + struct pmcstat_pmcrecord *pr; + + LIST_FOREACH(pr, &pmcstat_pmcs, pr_next) { + if (pr->pr_pmcid == pmcid) { + if (pmcstat_mergepmc) + return pr->pr_merge; + return pr; + } + } + + return NULL; } /* @@ -1358,13 +1096,11 @@ pmcstat_pmcid_to_name(pmc_id_t pmcid) static void pmcstat_process_aout_exec(struct pmcstat_process *pp, - struct pmcstat_image *image, uintfptr_t entryaddr, - struct pmcstat_args *a) + struct pmcstat_image *image, uintfptr_t entryaddr) { (void) pp; (void) image; (void) entryaddr; - (void) a; /* TODO Implement a.out handling */ } @@ -1374,8 +1110,7 @@ pmcstat_process_aout_exec(struct pmcstat_process *pp, static void pmcstat_process_elf_exec(struct pmcstat_process *pp, - struct pmcstat_image *image, uintfptr_t entryaddr, - struct pmcstat_args *a) + struct pmcstat_image *image, uintfptr_t entryaddr) { uintmax_t libstart; struct pmcstat_image *rtldimage; @@ -1414,8 +1149,7 @@ pmcstat_process_elf_exec(struct pmcstat_process *pp, * this we can figure out the address where the * runtime loader's file object had been mapped to. */ - rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath, - 0); + rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath, 0); if (rtldimage == NULL) { warnx("WARNING: Cannot find image for \"%s\".", pmcstat_string_unintern(image->pi_dynlinkerpath)); @@ -1424,7 +1158,7 @@ pmcstat_process_elf_exec(struct pmcstat_process *pp, } if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_get_elf_params(rtldimage, a); + pmcstat_image_get_elf_params(rtldimage); if (rtldimage->pi_type != PMCSTAT_IMAGE_ELF32 && rtldimage->pi_type != PMCSTAT_IMAGE_ELF64) { @@ -1495,8 +1229,7 @@ pmcstat_process_lookup(pid_t pid, int allocate) static void pmcstat_process_exec(struct pmcstat_process *pp, - pmcstat_interned_string path, uintfptr_t entryaddr, - struct pmcstat_args *a) + pmcstat_interned_string path, uintfptr_t entryaddr) { struct pmcstat_image *image; @@ -1506,7 +1239,7 @@ pmcstat_process_exec(struct pmcstat_process *pp, } if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_determine_type(image, a); + pmcstat_image_determine_type(image); assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); @@ -1514,12 +1247,12 @@ pmcstat_process_exec(struct pmcstat_process *pp, case PMCSTAT_IMAGE_ELF32: case PMCSTAT_IMAGE_ELF64: pmcstat_stats.ps_exec_elf++; - pmcstat_process_elf_exec(pp, image, entryaddr, a); + pmcstat_process_elf_exec(pp, image, entryaddr); break; case PMCSTAT_IMAGE_AOUT: pmcstat_stats.ps_exec_aout++; - pmcstat_process_aout_exec(pp, image, entryaddr, a); + pmcstat_process_aout_exec(pp, image, entryaddr); break; case PMCSTAT_IMAGE_INDETERMINABLE: @@ -1537,7 +1270,7 @@ pmcstat_process_exec(struct pmcstat_process *pp, * Find the map entry associated with process 'p' at PC value 'pc'. */ -static struct pmcstat_pcmap * +struct pmcstat_pcmap * pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) { struct pmcstat_pcmap *ppm; @@ -1552,444 +1285,36 @@ pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc) return (NULL); } -static struct pmcstat_cgnode * -pmcstat_cgnode_allocate(struct pmcstat_image *image, uintfptr_t pc) -{ - struct pmcstat_cgnode *cg; - - if ((cg = malloc(sizeof(*cg))) == NULL) - err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); - - cg->pcg_image = image; - cg->pcg_func = pc; - - cg->pcg_count = 0; - cg->pcg_nchildren = 0; - LIST_INIT(&cg->pcg_children); - - return (cg); -} - -/* - * Free a node and its children. - */ -static void -pmcstat_cgnode_free(struct pmcstat_cgnode *cg) -{ - struct pmcstat_cgnode *cgc, *cgtmp; - - LIST_FOREACH_SAFE(cgc, &cg->pcg_children, pcg_sibling, cgtmp) - pmcstat_cgnode_free(cgc); - free(cg); -} - -/* - * Look for a callgraph node associated with pmc `pmcid' in the global - * hash table that corresponds to the given `pc' value in the process - * `pp'. - */ -static struct pmcstat_cgnode * -pmcstat_cgnode_hash_lookup_pc(struct pmcstat_process *pp, uint32_t pmcid, - uintfptr_t pc, int usermode) -{ - struct pmcstat_pcmap *ppm; - struct pmcstat_symbol *sym; - struct pmcstat_image *image; - struct pmcstat_cgnode *cg; - struct pmcstat_cgnode_hash *h; - uintfptr_t loadaddress; - unsigned int i, hash; - - ppm = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, pc); - if (ppm == NULL) - return (NULL); - - image = ppm->ppm_image; - - loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start; - pc -= loadaddress; /* Convert to an offset in the image. */ - - /* - * Try determine the function at this offset. If we can't - * find a function round leave the `pc' value alone. - */ - if ((sym = pmcstat_symbol_search(image, pc)) != NULL) - pc = sym->ps_start; - - for (hash = i = 0; i < sizeof(uintfptr_t); i++) - hash += (pc >> i) & 0xFF; - - hash &= PMCSTAT_HASH_MASK; - - cg = NULL; - LIST_FOREACH(h, &pmcstat_cgnode_hash[hash], pch_next) - { - if (h->pch_pmcid != pmcid) - continue; - - cg = h->pch_cgnode; - - assert(cg != NULL); - - if (cg->pcg_image == image && cg->pcg_func == pc) - return (cg); - } - - /* - * We haven't seen this (pmcid, pc) tuple yet, so allocate a - * new callgraph node and a new hash table entry for it. - */ - cg = pmcstat_cgnode_allocate(image, pc); - if ((h = malloc(sizeof(*h))) == NULL) - err(EX_OSERR, "ERROR: Could not allocate callgraph node"); - - h->pch_pmcid = pmcid; - h->pch_cgnode = cg; - LIST_INSERT_HEAD(&pmcstat_cgnode_hash[hash], h, pch_next); - - pmcstat_cgnode_hash_count++; - - return (cg); -} - -/* - * Compare two callgraph nodes for sorting. - */ -static int -pmcstat_cgnode_compare(const void *a, const void *b) -{ - const struct pmcstat_cgnode *const *pcg1, *const *pcg2, *cg1, *cg2; - - pcg1 = (const struct pmcstat_cgnode *const *) a; - cg1 = *pcg1; - pcg2 = (const struct pmcstat_cgnode *const *) b; - cg2 = *pcg2; - - /* Sort in reverse order */ - if (cg1->pcg_count < cg2->pcg_count) - return (1); - if (cg1->pcg_count > cg2->pcg_count) - return (-1); - return (0); -} - -/* - * Find (allocating if a needed) a callgraph node in the given - * parent with the same (image, pcoffset) pair. - */ - -static struct pmcstat_cgnode * -pmcstat_cgnode_find(struct pmcstat_cgnode *parent, struct pmcstat_image *image, - uintfptr_t pcoffset) -{ - struct pmcstat_cgnode *child; - - LIST_FOREACH(child, &parent->pcg_children, pcg_sibling) { - if (child->pcg_image == image && - child->pcg_func == pcoffset) - return (child); - } - - /* - * Allocate a new structure. - */ - - child = pmcstat_cgnode_allocate(image, pcoffset); - - /* - * Link it into the parent. - */ - LIST_INSERT_HEAD(&parent->pcg_children, child, pcg_sibling); - parent->pcg_nchildren++; - - return (child); -} - -/* - * Print one callgraph node. The output format is: - * - * indentation %(parent's samples) #nsamples function@object - */ -static void -pmcstat_cgnode_print(struct pmcstat_args *a, struct pmcstat_cgnode *cg, - int depth, uint32_t total) -{ - uint32_t n; - const char *space; - struct pmcstat_symbol *sym; - struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; - - space = " "; - - if (depth > 0) - (void) fprintf(a->pa_graphfile, "%*s", depth, space); - - if (cg->pcg_count == total) - (void) fprintf(a->pa_graphfile, "100.0%% "); - else - (void) fprintf(a->pa_graphfile, "%05.2f%% ", - 100.0 * cg->pcg_count / total); - - n = fprintf(a->pa_graphfile, " [%u] ", cg->pcg_count); - - /* #samples is a 12 character wide field. */ - if (n < 12) - (void) fprintf(a->pa_graphfile, "%*s", 12 - n, space); - - if (depth > 0) - (void) fprintf(a->pa_graphfile, "%*s", depth, space); - - sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); - if (sym) - (void) fprintf(a->pa_graphfile, "%s", - pmcstat_string_unintern(sym->ps_name)); - else - (void) fprintf(a->pa_graphfile, "%p", - (void *) (cg->pcg_image->pi_vaddr + cg->pcg_func)); - - if (pmcstat_previous_filename_printed != - cg->pcg_image->pi_fullpath) { - pmcstat_previous_filename_printed = cg->pcg_image->pi_fullpath; - (void) fprintf(a->pa_graphfile, " @ %s\n", - pmcstat_string_unintern( - pmcstat_previous_filename_printed)); - } else - (void) fprintf(a->pa_graphfile, "\n"); - - if (cg->pcg_nchildren == 0) - return; - - if ((sortbuffer = (struct pmcstat_cgnode **) - malloc(sizeof(struct pmcstat_cgnode *) * - cg->pcg_nchildren)) == NULL) - err(EX_OSERR, "ERROR: Cannot print callgraph"); - cgn = sortbuffer; - - LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) - *cgn++ = pcg; - - assert(cgn - sortbuffer == (int) cg->pcg_nchildren); - - qsort(sortbuffer, cg->pcg_nchildren, sizeof(struct pmcstat_cgnode *), - pmcstat_cgnode_compare); - - for (cgn = sortbuffer, n = 0; n < cg->pcg_nchildren; n++, cgn++) - pmcstat_cgnode_print(a, *cgn, depth+1, cg->pcg_count); - - free(sortbuffer); -} - -/* - * Record a callchain. - */ - -static void -pmcstat_record_callchain(struct pmcstat_process *pp, uint32_t pmcid, - uint32_t nsamples, uintfptr_t *cc, int usermode, struct pmcstat_args *a) -{ - uintfptr_t pc, loadaddress; - uint32_t n; - struct pmcstat_image *image; - struct pmcstat_pcmap *ppm; - struct pmcstat_symbol *sym; - struct pmcstat_cgnode *parent, *child; - - /* - * Find the callgraph node recorded in the global hash table - * for this (pmcid, pc). - */ - - pc = cc[0]; - parent = pmcstat_cgnode_hash_lookup_pc(pp, pmcid, pc, usermode); - if (parent == NULL) { - pmcstat_stats.ps_callchain_dubious_frames++; - return; - } - - parent->pcg_count++; - - /* - * For each return address in the call chain record, subject - * to the maximum depth desired. - * - Find the image associated with the sample. Stop if there - * there is no valid image at that address. - * - Find the function that overlaps the return address. - * - If found: use the start address of the function. - * If not found (say an object's symbol table is not present or - * is incomplete), round down to th gprof bucket granularity. - * - Convert return virtual address to an offset in the image. - * - Look for a child with the same {offset,image} tuple, - * inserting one if needed. - * - Increment the count of occurrences of the child. - */ - - for (n = 1; n < (uint32_t) a->pa_graphdepth && n < nsamples; n++, - parent = child) { - pc = cc[n]; - - ppm = pmcstat_process_find_map(usermode ? pp : - pmcstat_kernproc, pc); - if (ppm == NULL) - return; - - image = ppm->ppm_image; - loadaddress = ppm->ppm_lowpc + image->pi_vaddr - - image->pi_start; - pc -= loadaddress; - - if ((sym = pmcstat_symbol_search(image, pc)) != NULL) - pc = sym->ps_start; - - child = pmcstat_cgnode_find(parent, image, pc); - child->pcg_count++; - } -} - -/* - * Printing a callgraph for a PMC. - */ -static void -pmcstat_callgraph_print_for_pmcid(struct pmcstat_args *a, - struct pmcstat_pmcrecord *pmcr) -{ - int n, nentries; - uint32_t nsamples, pmcid; - struct pmcstat_cgnode **sortbuffer, **cgn; - struct pmcstat_cgnode_hash *pch; - - /* - * We pull out all callgraph nodes in the top-level hash table - * with a matching PMC id. We then sort these based on the - * frequency of occurrence. Each callgraph node is then - * printed. - */ - - nsamples = 0; - pmcid = pmcr->pr_pmcid; - if ((sortbuffer = (struct pmcstat_cgnode **) - malloc(sizeof(struct pmcstat_cgnode *) * - pmcstat_cgnode_hash_count)) == NULL) - err(EX_OSERR, "ERROR: Cannot sort callgraph"); - cgn = sortbuffer; - - memset(sortbuffer, 0xFF, pmcstat_cgnode_hash_count * - sizeof(struct pmcstat_cgnode **)); - - for (n = 0; n < PMCSTAT_NHASH; n++) - LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) - if (pch->pch_pmcid == pmcid) { - nsamples += pch->pch_cgnode->pcg_count; - *cgn++ = pch->pch_cgnode; - } - - nentries = cgn - sortbuffer; - assert(nentries <= pmcstat_cgnode_hash_count); - - if (nentries == 0) - return; - - qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), - pmcstat_cgnode_compare); - - (void) fprintf(a->pa_graphfile, - "@ %s [%u samples]\n\n", - pmcstat_string_unintern(pmcr->pr_pmcname), - nsamples); - - for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { - pmcstat_previous_filename_printed = NULL; - pmcstat_cgnode_print(a, *cgn, 0, nsamples); - (void) fprintf(a->pa_graphfile, "\n"); - } - - free(sortbuffer); -} - -/* - * Print out callgraphs. - */ - -static void -pmcstat_callgraph_print(struct pmcstat_args *a) -{ - struct pmcstat_pmcrecord *pmcr; - - LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) - pmcstat_callgraph_print_for_pmcid(a, pmcr); -} - -static void -pmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid) -{ - struct pmcstat_cgnode *cgc; - - /* - * Look for child nodes that belong to the same image. - */ - - LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) { - if (cgc->pcg_image == cg->pcg_image) - pmcstat_gmon_append_arc(cg->pcg_image, pmcid, - cgc->pcg_func, cg->pcg_func, cgc->pcg_count); - if (cgc->pcg_nchildren > 0) - pmcstat_cgnode_do_gmon_arcs(cgc, pmcid); - } -} - -static void -pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid) -{ - int n; - struct pmcstat_cgnode_hash *pch; - - for (n = 0; n < PMCSTAT_NHASH; n++) - LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) - if (pch->pch_pmcid == pmcid && - pch->pch_cgnode->pcg_nchildren > 1) - pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode, - pmcid); -} - - -static void -pmcstat_callgraph_do_gmon_arcs(void) -{ - struct pmcstat_pmcrecord *pmcr; - - LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) - pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid); -} - /* * Convert a hwpmc(4) log to profile information. A system-wide * callgraph is generated if FLAG_DO_CALLGRAPHS is set. gmon.out * files usable by gprof(1) are created if FLAG_DO_GPROF is set. */ static int -pmcstat_analyze_log(struct pmcstat_args *a) +pmcstat_analyze_log(void) { uint32_t cpu, cpuflags; - uintfptr_t pc, newpc; + uintfptr_t pc; pid_t pid; struct pmcstat_image *image; - struct pmcstat_symbol *sym; struct pmcstat_process *pp, *ppnew; struct pmcstat_pcmap *ppm, *ppmtmp; struct pmclog_ev ev; + struct pmcstat_pmcrecord *pmcr; pmcstat_interned_string image_path; - assert(a->pa_flags & FLAG_DO_ANALYSIS); + assert(args.pa_flags & FLAG_DO_ANALYSIS); if (elf_version(EV_CURRENT) == EV_NONE) err(EX_UNAVAILABLE, "Elf library intialization failed"); - while (pmclog_read(a->pa_logparser, &ev) == 0) { + while (pmclog_read(args.pa_logparser, &ev) == 0) { assert(ev.pl_state == PMCLOG_OK); switch (ev.pl_type) { case PMCLOG_TYPE_INITIALIZE: if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != - PMC_VERSION_MAJOR << 24 && a->pa_verbosity > 0) + PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) warnx("WARNING: Log version 0x%x does not " "match compiled version 0x%x.", ev.pl_u.pl_i.pl_version, @@ -2019,7 +1344,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) pl_pathname); image = pmcstat_image_from_path(image_path, pid == -1); if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) - pmcstat_image_determine_type(image, a); + pmcstat_image_determine_type(image); if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE) pmcstat_image_link(pp, image, ev.pl_u.pl_mi.pl_start); @@ -2059,16 +1384,23 @@ pmcstat_analyze_log(struct pmcstat_args *a) pc = ev.pl_u.pl_s.pl_pc; pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, PMCSTAT_ALLOCATE); - if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL && - (ppm = pmcstat_process_find_map(pmcstat_kernproc, - pc)) == NULL) { /* unknown process,offset pair */ - pmcstat_stats.ps_samples_unknown_offset++; - break; - } - pmcstat_image_increment_bucket(ppm, pc, - ev.pl_u.pl_s.pl_pmcid, a); + /* Get PMC record. */ + pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_s.pl_pmcid); + assert(pmcr != NULL); + /* + * Call the plugins processing + * TODO: move pmcstat_process_find_map inside plugins + */ + + if (plugins[args.pa_pplugin].pl_process != NULL) + plugins[args.pa_pplugin].pl_process( + pp, pmcr, 1, &pc, + pmcstat_process_find_map(pp, pc) != NULL, 0); + plugins[args.pa_plugin].pl_process( + pp, pmcr, 1, &pc, + pmcstat_process_find_map(pp, pc) != NULL, 0); break; case PMCLOG_TYPE_CALLCHAIN: @@ -2078,7 +1410,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags); /* Filter on the CPU id. */ - if ((a->pa_cpumask & (1 << cpu)) == 0) { + if ((args.pa_cpumask & (1 << cpu)) == 0) { pmcstat_stats.ps_samples_skipped++; break; } @@ -2086,45 +1418,27 @@ pmcstat_analyze_log(struct pmcstat_args *a) pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid, PMCSTAT_ALLOCATE); - if ((a->pa_flags & FLAG_WANTS_MAPPINGS) == 0) - pmcstat_record_callchain(pp, - ev.pl_u.pl_cc.pl_pmcid, - ev.pl_u.pl_cc.pl_npc, ev.pl_u.pl_cc.pl_pc, - PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), a); - - if ((a->pa_flags & - (FLAG_DO_GPROF | FLAG_WANTS_MAPPINGS)) == 0) - break; - - pc = ev.pl_u.pl_cc.pl_pc[0]; - if (PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags) == 0) - pp = pmcstat_kernproc; - ppm = pmcstat_process_find_map(pp, pc); - if (ppm == NULL) { - - /* Unknown offset. */ - pmcstat_stats.ps_samples_unknown_offset++; - break; - } - if (a->pa_flags & FLAG_WANTS_MAPPINGS) { - image = ppm->ppm_image; - newpc = pc - (ppm->ppm_lowpc + - (image->pi_vaddr - image->pi_start)); - sym = pmcstat_symbol_search(image, newpc); - if (sym == NULL) - break; - fprintf(a->pa_graphfile, "%p %s 0x%jx 0x%jx\n", - (void *)pc, - pmcstat_string_unintern(sym->ps_name), - (uintmax_t)(sym->ps_start + - image->pi_vaddr), (uintmax_t)(sym->ps_end + - image->pi_vaddr)); - break; - } + /* Get PMC record. */ + pmcr = pmcstat_lookup_pmcid(ev.pl_u.pl_cc.pl_pmcid); + assert(pmcr != NULL); - pmcstat_image_increment_bucket(ppm, pc, - ev.pl_u.pl_cc.pl_pmcid, a); + /* + * Call the plugins processing + */ + if (plugins[args.pa_pplugin].pl_process != NULL) + plugins[args.pa_pplugin].pl_process( + pp, pmcr, + ev.pl_u.pl_cc.pl_npc, + ev.pl_u.pl_cc.pl_pc, + PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), + cpu); + plugins[args.pa_plugin].pl_process( + pp, pmcr, + ev.pl_u.pl_cc.pl_npc, + ev.pl_u.pl_cc.pl_pc, + PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), + cpu); break; case PMCLOG_TYPE_PMCALLOCATE: @@ -2133,7 +1447,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) * PMC and its name. */ pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid, - pmcstat_string_intern(ev.pl_u.pl_a.pl_evname), a); + pmcstat_string_intern(ev.pl_u.pl_a.pl_evname)); break; case PMCLOG_TYPE_PROCEXEC: @@ -2156,7 +1470,7 @@ pmcstat_analyze_log(struct pmcstat_args *a) ev.pl_u.pl_x.pl_pathname); assert(image_path != NULL); pmcstat_process_exec(pp, image_path, - ev.pl_u.pl_x.pl_entryaddr, a); + ev.pl_u.pl_x.pl_entryaddr); break; case PMCLOG_TYPE_PROCEXIT: @@ -2224,16 +1538,16 @@ pmcstat_analyze_log(struct pmcstat_args *a) */ static int -pmcstat_print_log(struct pmcstat_args *a) +pmcstat_print_log(void) { struct pmclog_ev ev; uint32_t npc; - while (pmclog_read(a->pa_logparser, &ev) == 0) { + while (pmclog_read(args.pa_logparser, &ev) == 0) { assert(ev.pl_state == PMCLOG_OK); switch (ev.pl_type) { case PMCLOG_TYPE_CALLCHAIN: - PMCSTAT_PRINT_ENTRY(a, "callchain", + PMCSTAT_PRINT_ENTRY("callchain", "%d 0x%x %d %d %c", ev.pl_u.pl_cc.pl_pid, ev.pl_u.pl_cc.pl_pmcid, PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc. \ @@ -2241,95 +1555,95 @@ pmcstat_print_log(struct pmcstat_args *a) PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(ev.pl_u.pl_cc.\ pl_cpuflags) ? 'u' : 's'); for (npc = 0; npc < ev.pl_u.pl_cc.pl_npc; npc++) - PMCSTAT_PRINT_ENTRY(a, "...", "%p", + PMCSTAT_PRINT_ENTRY("...", "%p", (void *) ev.pl_u.pl_cc.pl_pc[npc]); break; case PMCLOG_TYPE_CLOSELOG: - PMCSTAT_PRINT_ENTRY(a,"closelog",); + PMCSTAT_PRINT_ENTRY("closelog",); break; case PMCLOG_TYPE_DROPNOTIFY: - PMCSTAT_PRINT_ENTRY(a,"drop",); + PMCSTAT_PRINT_ENTRY("drop",); break; case PMCLOG_TYPE_INITIALIZE: - PMCSTAT_PRINT_ENTRY(a,"initlog","0x%x \"%s\"", + PMCSTAT_PRINT_ENTRY("initlog","0x%x \"%s\"", ev.pl_u.pl_i.pl_version, pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch)); if ((ev.pl_u.pl_i.pl_version & 0xFF000000) != - PMC_VERSION_MAJOR << 24 && a->pa_verbosity > 0) + PMC_VERSION_MAJOR << 24 && args.pa_verbosity > 0) warnx("WARNING: Log version 0x%x != expected " "version 0x%x.", ev.pl_u.pl_i.pl_version, PMC_VERSION); break; case PMCLOG_TYPE_MAP_IN: - PMCSTAT_PRINT_ENTRY(a,"map-in","%d %p \"%s\"", + PMCSTAT_PRINT_ENTRY("map-in","%d %p \"%s\"", ev.pl_u.pl_mi.pl_pid, (void *) ev.pl_u.pl_mi.pl_start, ev.pl_u.pl_mi.pl_pathname); break; case PMCLOG_TYPE_MAP_OUT: - PMCSTAT_PRINT_ENTRY(a,"map-out","%d %p %p", + PMCSTAT_PRINT_ENTRY("map-out","%d %p %p", ev.pl_u.pl_mo.pl_pid, (void *) ev.pl_u.pl_mo.pl_start, (void *) ev.pl_u.pl_mo.pl_end); break; case PMCLOG_TYPE_PCSAMPLE: - PMCSTAT_PRINT_ENTRY(a,"sample","0x%x %d %p %c", + PMCSTAT_PRINT_ENTRY("sample","0x%x %d %p %c", ev.pl_u.pl_s.pl_pmcid, ev.pl_u.pl_s.pl_pid, (void *) ev.pl_u.pl_s.pl_pc, ev.pl_u.pl_s.pl_usermode ? 'u' : 's'); break; case PMCLOG_TYPE_PMCALLOCATE: - PMCSTAT_PRINT_ENTRY(a,"allocate","0x%x \"%s\" 0x%x", + PMCSTAT_PRINT_ENTRY("allocate","0x%x \"%s\" 0x%x", ev.pl_u.pl_a.pl_pmcid, ev.pl_u.pl_a.pl_evname, ev.pl_u.pl_a.pl_flags); break; case PMCLOG_TYPE_PMCATTACH: - PMCSTAT_PRINT_ENTRY(a,"attach","0x%x %d \"%s\"", + PMCSTAT_PRINT_ENTRY("attach","0x%x %d \"%s\"", ev.pl_u.pl_t.pl_pmcid, ev.pl_u.pl_t.pl_pid, ev.pl_u.pl_t.pl_pathname); break; case PMCLOG_TYPE_PMCDETACH: - PMCSTAT_PRINT_ENTRY(a,"detach","0x%x %d", + PMCSTAT_PRINT_ENTRY("detach","0x%x %d", ev.pl_u.pl_d.pl_pmcid, ev.pl_u.pl_d.pl_pid); break; case PMCLOG_TYPE_PROCCSW: - PMCSTAT_PRINT_ENTRY(a,"cswval","0x%x %d %jd", + PMCSTAT_PRINT_ENTRY("cswval","0x%x %d %jd", ev.pl_u.pl_c.pl_pmcid, ev.pl_u.pl_c.pl_pid, ev.pl_u.pl_c.pl_value); break; case PMCLOG_TYPE_PROCEXEC: - PMCSTAT_PRINT_ENTRY(a,"exec","0x%x %d %p \"%s\"", + PMCSTAT_PRINT_ENTRY("exec","0x%x %d %p \"%s\"", ev.pl_u.pl_x.pl_pmcid, ev.pl_u.pl_x.pl_pid, (void *) ev.pl_u.pl_x.pl_entryaddr, ev.pl_u.pl_x.pl_pathname); break; case PMCLOG_TYPE_PROCEXIT: - PMCSTAT_PRINT_ENTRY(a,"exitval","0x%x %d %jd", + PMCSTAT_PRINT_ENTRY("exitval","0x%x %d %jd", ev.pl_u.pl_e.pl_pmcid, ev.pl_u.pl_e.pl_pid, ev.pl_u.pl_e.pl_value); break; case PMCLOG_TYPE_PROCFORK: - PMCSTAT_PRINT_ENTRY(a,"fork","%d %d", + PMCSTAT_PRINT_ENTRY("fork","%d %d", ev.pl_u.pl_f.pl_oldpid, ev.pl_u.pl_f.pl_newpid); break; case PMCLOG_TYPE_USERDATA: - PMCSTAT_PRINT_ENTRY(a,"userdata","0x%x", + PMCSTAT_PRINT_ENTRY("userdata","0x%x", ev.pl_u.pl_u.pl_userdata); break; case PMCLOG_TYPE_SYSEXIT: - PMCSTAT_PRINT_ENTRY(a,"exit","%d", + PMCSTAT_PRINT_ENTRY("exit","%d", ev.pl_u.pl_se.pl_pid); break; default: - fprintf(a->pa_printfile, "unknown event (type %d).\n", + fprintf(args.pa_printfile, "unknown event (type %d).\n", ev.pl_type); } } @@ -2354,13 +1668,13 @@ pmcstat_print_log(struct pmcstat_args *a) */ int -pmcstat_close_log(struct pmcstat_args *a) +pmcstat_close_log(void) { if (pmc_flush_logfile() < 0 || pmc_configure_logfile(-1) < 0) err(EX_OSERR, "ERROR: logging failed"); - a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE); - return (a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : + args.pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE); + return (args.pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : PMCSTAT_FINISHED); } @@ -2456,17 +1770,208 @@ pmcstat_open_log(const char *path, int mode) */ int -pmcstat_process_log(struct pmcstat_args *a) +pmcstat_process_log(void) { /* * If analysis has not been asked for, just print the log to * the current output file. */ - if (a->pa_flags & FLAG_DO_PRINT) - return (pmcstat_print_log(a)); + if (args.pa_flags & FLAG_DO_PRINT) + return (pmcstat_print_log()); else - return (pmcstat_analyze_log(a)); + return (pmcstat_analyze_log()); +} + +/* + * Refresh top display. + */ + +static void +pmcstat_refresh_top(void) +{ + char pmcname[40]; + + /* If in pause mode do not refresh display. */ + if (pmcstat_pause) + return; + + /* Format PMC name. */ + if (pmcstat_mergepmc) + snprintf(pmcname, sizeof(pmcname), "[%s]", + pmcstat_pmcindex_to_name(pmcstat_pmcinfilter)); + else + snprintf(pmcname, sizeof(pmcname), "%s.%d", + pmcstat_pmcindex_to_name(pmcstat_pmcinfilter), + pmcstat_pmcinfilter); + + PMCSTAT_PRINTBEGIN(); + PMCSTAT_PRINTW("PMC: %s Samples: %u processed, %u invalid\n\n", + pmcname, + pmcstat_stats.ps_samples_total, + pmcstat_stats.ps_samples_unknown_offset + + pmcstat_stats.ps_samples_indeterminable + + pmcstat_stats.ps_callchain_dubious_frames); + if (plugins[args.pa_plugin].pl_topdisplay != NULL) + plugins[args.pa_plugin].pl_topdisplay(); + PMCSTAT_PRINTEND(); +} + +/* + * Find the next pmc index to display. + */ + +static void +pmcstat_changefilter(void) +{ + int pmcin; + struct pmcstat_pmcrecord *pmcr; + + /* + * Find the next merge target. + */ + if (pmcstat_mergepmc) { + pmcin = pmcstat_pmcinfilter; + + do { + pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); + if (pmcr == pmcr->pr_merge) + break; + + pmcstat_pmcinfilter++; + if (pmcstat_pmcinfilter >= pmcstat_npmcs) + pmcstat_pmcinfilter = 0; + + } while (pmcstat_pmcinfilter != pmcin); + } +} + +/* + * Top mode keypress. + */ + +int +pmcstat_keypress_log(void) +{ + int c, ret = 0; + WINDOW *w; + + w = newwin(1, 0, 1, 0); + c = wgetch(w); + wprintw(w, "Key: %c => ", c); + switch (c) { + case 'c': + wprintw(w, "enter mode 'd' or 'a' => "); + c = wgetch(w); + if (c == 'd') { + args.pa_topmode = PMCSTAT_TOP_DELTA; + wprintw(w, "switching to delta mode"); + } else { + args.pa_topmode = PMCSTAT_TOP_ACCUM; + wprintw(w, "switching to accumulation mode"); + } + break; + case 'm': + pmcstat_mergepmc = !pmcstat_mergepmc; + /* + * Changing merge state require data reset. + */ + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(NULL); + bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); + + /* Update filter to be on a merge target. */ + pmcstat_changefilter(); + wprintw(w, "merge PMC %s", pmcstat_mergepmc ? "on" : "off"); + break; + case 'n': + /* Close current plugin. */ + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(NULL); + + /* Find next top display available. */ + do { + args.pa_plugin++; + if (plugins[args.pa_plugin].pl_name == NULL) + args.pa_plugin = 0; + } while (plugins[args.pa_plugin].pl_topdisplay == NULL); + + /* Open new plugin. */ + bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); + wprintw(w, "switching to plugin %s", + plugins[args.pa_plugin].pl_name); + break; + case 'p': + pmcstat_pmcinfilter++; + if (pmcstat_pmcinfilter >= pmcstat_npmcs) + pmcstat_pmcinfilter = 0; + pmcstat_changefilter(); + wprintw(w, "switching to PMC %s.%d", + pmcstat_pmcindex_to_name(pmcstat_pmcinfilter), + pmcstat_pmcinfilter); + break; + case ' ': + pmcstat_pause = !pmcstat_pause; + if (pmcstat_pause) + wprintw(w, "pause => press space again to continue"); + break; + case 'q': + wprintw(w, "exiting..."); + ret = 1; + default: + if (plugins[args.pa_plugin].pl_topkeypress != NULL) + if (plugins[args.pa_plugin].pl_topkeypress(c, w)) + ret = 1; + } + + wrefresh(w); + delwin(w); + return ret; +} + + +/* + * Top mode display. + */ + +void +pmcstat_display_log(void) +{ + + pmcstat_refresh_top(); + + /* Reset everythings if delta mode. */ + if (args.pa_topmode == PMCSTAT_TOP_DELTA) { + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(NULL); + bzero(&pmcstat_stats, sizeof(struct pmcstat_stats)); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); + } + +} + +/* + * Configure a plugins. + */ + +void +pmcstat_pluginconfigure_log(char *opt) +{ + + if (strncmp(opt, "threshold=", 10) == 0) { + pmcstat_threshold = atof(opt+10); + } else { + if (plugins[args.pa_plugin].pl_configure != NULL) { + if (!plugins[args.pa_plugin].pl_configure(opt)) + err(EX_USAGE, + "ERROR: unknown option <%s>.", opt); + } + } } /* @@ -2474,12 +1979,10 @@ pmcstat_process_log(struct pmcstat_args *a) */ void -pmcstat_initialize_logging(struct pmcstat_args *a) +pmcstat_initialize_logging(void) { int i; - (void) a; - /* use a convenient format for 'ldd' output */ if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0) err(EX_OSERR, "ERROR: Cannot setenv"); @@ -2499,6 +2002,21 @@ pmcstat_initialize_logging(struct pmcstat_args *a) if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, PMCSTAT_ALLOCATE)) == NULL) err(EX_OSERR, "ERROR: Cannot initialize logging"); + + /* PMC count. */ + pmcstat_npmcs = 0; + + /* Merge PMC with same name. */ + pmcstat_mergepmc = args.pa_mergepmc; + + /* + * Initialize plugins + */ + + if (plugins[args.pa_pplugin].pl_init != NULL) + plugins[args.pa_pplugin].pl_init(); + if (plugins[args.pa_plugin].pl_init != NULL) + plugins[args.pa_plugin].pl_init(); } /* @@ -2506,99 +2024,57 @@ pmcstat_initialize_logging(struct pmcstat_args *a) */ void -pmcstat_shutdown_logging(struct pmcstat_args *a) +pmcstat_shutdown_logging(void) { int i; FILE *mf; - struct pmcstat_gmonfile *pgf, *pgftmp; struct pmcstat_image *pi, *pitmp; struct pmcstat_process *pp, *pptmp; - struct pmcstat_cgnode_hash *pch, *pchtmp; + struct pmcstat_pcmap *ppm, *ppmtmp; /* determine where to send the map file */ mf = NULL; - if (a->pa_mapfilename != NULL) - mf = (strcmp(a->pa_mapfilename, "-") == 0) ? - a->pa_printfile : fopen(a->pa_mapfilename, "w"); + if (args.pa_mapfilename != NULL) + mf = (strcmp(args.pa_mapfilename, "-") == 0) ? + args.pa_printfile : fopen(args.pa_mapfilename, "w"); - if (mf == NULL && a->pa_flags & FLAG_DO_GPROF && - a->pa_verbosity >= 2) - mf = a->pa_printfile; + if (mf == NULL && args.pa_flags & FLAG_DO_GPROF && + args.pa_verbosity >= 2) + mf = args.pa_printfile; if (mf) (void) fprintf(mf, "MAP:\n"); - - if (a->pa_flags & FLAG_DO_CALLGRAPHS) - pmcstat_callgraph_print(a); - - /* - * Sync back all gprof flat profile data. - */ - for (i = 0; i < PMCSTAT_NHASH; i++) { - LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { - if (mf) - (void) fprintf(mf, " \"%s\" => \"%s\"", - pmcstat_string_unintern(pi->pi_execpath), - pmcstat_string_unintern( - pi->pi_samplename)); - - /* flush gmon.out data to disk */ - LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { - pmcstat_gmon_unmap_file(pgf); - if (mf) - (void) fprintf(mf, " %s/%d", - pmcstat_pmcid_to_name( - pgf->pgf_pmcid), - pgf->pgf_nsamples); - if (pgf->pgf_overflow && a->pa_verbosity >= 1) - warnx("WARNING: profile \"%s\" " - "overflowed.", - pmcstat_string_unintern( - pgf->pgf_name)); - } - - if (mf) - (void) fprintf(mf, "\n"); - } - } - /* - * Compute arcs and add these to the gprof files. + * Shutdown the plugins */ - if (a->pa_flags & FLAG_DO_GPROF && a->pa_graphdepth > 1) - pmcstat_callgraph_do_gmon_arcs(); - /* - * Free memory. - */ - for (i = 0; i < PMCSTAT_NHASH; i++) { - LIST_FOREACH_SAFE(pch, &pmcstat_cgnode_hash[i], pch_next, - pchtmp) { - pmcstat_cgnode_free(pch->pch_cgnode); - free(pch); - } - } + if (plugins[args.pa_plugin].pl_shutdown != NULL) + plugins[args.pa_plugin].pl_shutdown(mf); + if (plugins[args.pa_pplugin].pl_shutdown != NULL) + plugins[args.pa_pplugin].pl_shutdown(mf); for (i = 0; i < PMCSTAT_NHASH; i++) { - LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, pitmp) - { - LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, - pgftmp) { - if (pgf->pgf_file) - (void) fclose(pgf->pgf_file); - LIST_REMOVE(pgf, pgf_next); - free(pgf); - } - if (pi->pi_symbols) - free(pi->pi_symbols); - + LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, + pitmp) { + if (plugins[args.pa_plugin].pl_shutdownimage != NULL) + plugins[args.pa_plugin].pl_shutdownimage(pi); + if (plugins[args.pa_pplugin].pl_shutdownimage != NULL) + plugins[args.pa_pplugin].pl_shutdownimage(pi); + + free(pi->pi_symbols); + if (pi->pi_addr2line != NULL) + pclose(pi->pi_addr2line); LIST_REMOVE(pi, pi_next); free(pi); } LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next, pptmp) { + TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) { + TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next); + free(ppm); + } LIST_REMOVE(pp, pp_next); free(pp); } @@ -2610,23 +2086,23 @@ pmcstat_shutdown_logging(struct pmcstat_args *a) * Print errors unless -q was specified. Print all statistics * if verbosity > 1. */ -#define PRINT(N,V,A) do { \ - if (pmcstat_stats.ps_##V || (A)->pa_verbosity >= 2) \ - (void) fprintf((A)->pa_printfile, " %-40s %d\n",\ +#define PRINT(N,V) do { \ + if (pmcstat_stats.ps_##V || args.pa_verbosity >= 2) \ + (void) fprintf(args.pa_printfile, " %-40s %d\n",\ N, pmcstat_stats.ps_##V); \ } while (0) - if (a->pa_verbosity >= 1 && a->pa_flags & FLAG_DO_GPROF) { - (void) fprintf(a->pa_printfile, "CONVERSION STATISTICS:\n"); - PRINT("#exec/a.out", exec_aout, a); - PRINT("#exec/elf", exec_elf, a); - PRINT("#exec/unknown", exec_indeterminable, a); - PRINT("#exec handling errors", exec_errors, a); - PRINT("#samples/total", samples_total, a); - PRINT("#samples/unclaimed", samples_unknown_offset, a); - PRINT("#samples/unknown-object", samples_indeterminable, a); - PRINT("#callchain/dubious-frames", callchain_dubious_frames, - a); + if (args.pa_verbosity >= 1 && (args.pa_flags & FLAG_DO_ANALYSIS) && + (args.pa_flags & FLAG_DO_TOP) == 0) { + (void) fprintf(args.pa_printfile, "CONVERSION STATISTICS:\n"); + PRINT("#exec/a.out", exec_aout); + PRINT("#exec/elf", exec_elf); + PRINT("#exec/unknown", exec_indeterminable); + PRINT("#exec handling errors", exec_errors); + PRINT("#samples/total", samples_total); + PRINT("#samples/unclaimed", samples_unknown_offset); + PRINT("#samples/unknown-object", samples_indeterminable); + PRINT("#callchain/dubious-frames", callchain_dubious_frames); } if (mf) diff --git a/usr.sbin/pmcstat/pmcstat_log.h b/usr.sbin/pmcstat/pmcstat_log.h new file mode 100644 index 0000000..de92649 --- /dev/null +++ b/usr.sbin/pmcstat/pmcstat_log.h @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2005-2007, Joseph Koshy + * Copyright (c) 2007 The FreeBSD Foundation + * Copyright (c) 2009, Fabien Thomas + * All rights reserved. + * + * Portions of this software were developed by A. Joseph Koshy under + * sponsorship from the FreeBSD Foundation and Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PMCSTAT_LOG_H_ +#define _PMCSTAT_LOG_H_ + +typedef const void *pmcstat_interned_string; + +/* + * A 'pmcstat_process' structure models processes. Each process is + * associated with a set of pmcstat_pcmap structures that map + * addresses inside it to executable objects. This set is implemented + * as a list, kept sorted in ascending order of mapped addresses. + * + * 'pp_pid' holds the pid of the process. When a process exits, the + * 'pp_isactive' field is set to zero, but the process structure is + * not immediately reclaimed because there may still be samples in the + * log for this process. + */ + +struct pmcstat_process { + LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */ + pid_t pp_pid; /* associated pid */ + int pp_isactive; /* whether active */ + uintfptr_t pp_entryaddr; /* entry address */ + TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */ +}; +extern LIST_HEAD(pmcstat_process_hash_list, pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH]; + +/* + * A 'pmcstat_image' structure describes an executable program on + * disk. 'pi_execpath' is a cookie representing the pathname of + * the executable. 'pi_start' and 'pi_end' are the least and greatest + * virtual addresses for the text segments in the executable. + * 'pi_gmonlist' contains a linked list of gmon.out files associated + * with this image. + */ + +enum pmcstat_image_type { + PMCSTAT_IMAGE_UNKNOWN = 0, /* never looked at the image */ + PMCSTAT_IMAGE_INDETERMINABLE, /* can't tell what the image is */ + PMCSTAT_IMAGE_ELF32, /* ELF 32 bit object */ + PMCSTAT_IMAGE_ELF64, /* ELF 64 bit object */ + PMCSTAT_IMAGE_AOUT /* AOUT object */ +}; + +struct pmcstat_image { + LIST_ENTRY(pmcstat_image) pi_next; /* hash link */ + TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */ + pmcstat_interned_string pi_execpath; /* cookie */ + pmcstat_interned_string pi_samplename; /* sample path name */ + pmcstat_interned_string pi_fullpath; /* path to FS object */ + pmcstat_interned_string pi_name; /* display name */ + + enum pmcstat_image_type pi_type; /* executable type */ + + /* + * Executables have pi_start and pi_end; these are zero + * for shared libraries. + */ + uintfptr_t pi_start; /* start address (inclusive) */ + uintfptr_t pi_end; /* end address (exclusive) */ + uintfptr_t pi_entry; /* entry address */ + uintfptr_t pi_vaddr; /* virtual address where loaded */ + int pi_isdynamic; /* whether a dynamic object */ + int pi_iskernelmodule; + pmcstat_interned_string pi_dynlinkerpath; /* path in .interp */ + + /* All symbols associated with this object. */ + struct pmcstat_symbol *pi_symbols; + size_t pi_symcount; + + /* Handle to addr2line for this image. */ + FILE *pi_addr2line; + + /* + * Plugins private data + */ + + /* gprof: + * An image can be associated with one or more gmon.out files; + * one per PMC. + */ + LIST_HEAD(,pmcstat_gmonfile) pi_gmlist; +}; +extern LIST_HEAD(pmcstat_image_hash_list, pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH]; + +/* + * A 'pmcstat_pcmap' structure maps a virtual address range to an + * underlying 'pmcstat_image' descriptor. + */ +struct pmcstat_pcmap { + TAILQ_ENTRY(pmcstat_pcmap) ppm_next; + uintfptr_t ppm_lowpc; + uintfptr_t ppm_highpc; + struct pmcstat_image *ppm_image; +}; + +/* + * Each function symbol tracked by pmcstat(8). + */ + +struct pmcstat_symbol { + pmcstat_interned_string ps_name; + uint64_t ps_start; + uint64_t ps_end; +}; + +/* + * 'pmcstat_pmcrecord' is a mapping from PMC ids to human-readable + * names. + */ + +struct pmcstat_pmcrecord { + LIST_ENTRY(pmcstat_pmcrecord) pr_next; + pmc_id_t pr_pmcid; + int pr_pmcin; + pmcstat_interned_string pr_pmcname; + struct pmcstat_pmcrecord *pr_merge; +}; +extern LIST_HEAD(pmcstat_pmcs, pmcstat_pmcrecord) pmcstat_pmcs; /* PMC list */ + +/* + * Misc. statistics + */ +struct pmcstat_stats { + int ps_exec_aout; /* # a.out executables seen */ + int ps_exec_elf; /* # elf executables seen */ + int ps_exec_errors; /* # errors processing executables */ + int ps_exec_indeterminable; /* # unknown executables seen */ + int ps_samples_total; /* total number of samples processed */ + int ps_samples_skipped; /* #samples filtered out for any reason */ + int ps_samples_unknown_offset; /* #samples of rank 0 not in a map */ + int ps_samples_indeterminable; /* #samples in indeterminable images */ + int ps_callchain_dubious_frames;/* #dubious frame pointers seen */ +}; +extern struct pmcstat_stats pmcstat_stats; /* statistics */ + +extern struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */ + +extern int pmcstat_npmcs; /* PMC count. */ + +/* + * Top mode global options. + */ +float pmcstat_threshold; /* Threshold to filter node. */ +int pmcstat_pmcinfilter; /* PMC index displayed. */ + +/* Function prototypes */ +const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid); +const char *pmcstat_pmcindex_to_name(int pmcin); +struct pmcstat_pmcrecord *pmcstat_pmcindex_to_pmcr(int pmcin); +struct pmcstat_pcmap *pmcstat_process_find_map(struct pmcstat_process *_p, + uintfptr_t _pc); +struct pmcstat_symbol *pmcstat_symbol_search(struct pmcstat_image *image, + uintfptr_t addr); +const char *pmcstat_string_unintern(pmcstat_interned_string _is); +pmcstat_interned_string pmcstat_string_intern(const char *_s); +void pmcstat_image_determine_type(struct pmcstat_image *_image); +pmcstat_interned_string pmcstat_string_lookup(const char *_s); +int pmcstat_image_addr2line(struct pmcstat_image *image, uintfptr_t addr, + char *sourcefile, size_t sourcefile_len, unsigned *sourceline, + char *funcname, size_t funcname_len); + +#endif /* _PMCSTAT_LOG_H_ */ + diff --git a/usr.sbin/pmcstat/pmcstat_top.h b/usr.sbin/pmcstat/pmcstat_top.h new file mode 100644 index 0000000..281410b --- /dev/null +++ b/usr.sbin/pmcstat/pmcstat_top.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2009, Fabien Thomas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PMCSTAT_TOP_H_ +#define _PMCSTAT_TOP_H_ + +/* Return the ncurses attributes for the given value. */ +#define PMCSTAT_ATTRPERCENT(b) \ + ((b) > 10.0 ? (args.pa_topcolor ? COLOR_PAIR(1) : A_BOLD) : \ + ((b) > 5.0 ? (args.pa_topcolor ? COLOR_PAIR(2) : 0) : \ + ((b) > 2.5 ? (args.pa_topcolor ? COLOR_PAIR(3) : 0) : 0))) + +/* Print to the default ncurse windows if on a terminal or to the file. */ +#define PMCSTAT_PRINTW(...) do { \ + if (args.pa_toptty) \ + printw(__VA_ARGS__); \ + else \ + fprintf(args.pa_printfile, __VA_ARGS__);\ +} while (0) + +/* If ncurses mode active set attributes. */ +#define PMCSTAT_ATTRON(b) do { \ + if (args.pa_toptty) \ + attron(b); \ +} while (0) + +/* If ncurses mode active unset attributes. */ +#define PMCSTAT_ATTROFF(b) do { \ + if (args.pa_toptty) \ + attroff(b); \ +} while (0) + +/* Erase screen and set cursor to top left. */ +#define PMCSTAT_PRINTBEGIN() do { \ + if (args.pa_toptty) \ + clear(); \ +} while (0) + +/* Flush buffer to backend. */ +#define PMCSTAT_PRINTEND() do { \ + if (!args.pa_toptty) { \ + PMCSTAT_PRINTW("---\n"); \ + fflush(args.pa_printfile); \ + } else \ + refresh(); \ +} while (0) + +/* Function prototypes */ + +#endif /* _PMCSTAT_TOP_H_ */ diff --git a/usr.sbin/rpcbind/rpcbind.c b/usr.sbin/rpcbind/rpcbind.c index b601da5..5a76a68 100644 --- a/usr.sbin/rpcbind/rpcbind.c +++ b/usr.sbin/rpcbind/rpcbind.c @@ -92,6 +92,7 @@ int oldstyle_local = 0; int verboselog = 0; char **hosts = NULL; +struct sockaddr **bound_sa; int ipv6_only = 0; int nhosts = 0; int on = 1; @@ -119,6 +120,7 @@ static void rbllist_add(rpcprog_t, rpcvers_t, struct netconfig *, struct netbuf *); static void terminate(int); static void parseargs(int, char *[]); +static void update_bound_sa(void); int main(int argc, char *argv[]) @@ -130,6 +132,8 @@ main(int argc, char *argv[]) parseargs(argc, argv); + update_bound_sa(); + /* Check that another rpcbind isn't already running. */ if ((rpcbindlockfd = (open(RPCBINDDLOCK, O_RDONLY|O_CREAT, 0444))) == -1) @@ -323,8 +327,7 @@ init_transport(struct netconfig *nconf) * If no hosts were specified, just bind to INADDR_ANY. * Otherwise make sure 127.0.0.1 is added to the list. */ - nhostsbak = nhosts; - nhostsbak++; + nhostsbak = nhosts + 1; hosts = realloc(hosts, nhostsbak * sizeof(char *)); if (nhostsbak == 1) hosts[0] = "*"; @@ -657,6 +660,75 @@ error: return (1); } +/* + * Create the list of addresses that we're bound to. Normally, this + * list is empty because we're listening on the wildcard address + * (nhost == 0). If -h is specified on the command line, then + * bound_sa will have a list of the addresses that the program binds + * to specifically. This function takes that list and converts them to + * struct sockaddr * and stores them in bound_sa. + */ +static void +update_bound_sa(void) +{ + struct addrinfo hints, *res = NULL; + int i; + + if (nhosts == 0) + return; + bound_sa = malloc(sizeof(*bound_sa) * nhosts); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + for (i = 0; i < nhosts; i++) { + if (getaddrinfo(hosts[i], NULL, &hints, &res) != 0) + continue; + bound_sa[i] = malloc(res->ai_addrlen); + memcpy(bound_sa[i], res->ai_addr, res->ai_addrlen); + } +} + +/* + * Match the sa against the list of addresses we've bound to. If + * we've not specifically bound to anything, we match everything. + * Otherwise, if the IPv4 or IPv6 address matches one of the addresses + * in bound_sa, we return true. If not, we return false. + */ +int +listen_addr(const struct sockaddr *sa) +{ + int i; + + /* + * If nhosts == 0, then there were no -h options on the + * command line, so all addresses are addresses we're + * listening to. + */ + if (nhosts == 0) + return 1; + for (i = 0; i < nhosts; i++) { + if (bound_sa[i] == NULL || + sa->sa_family != bound_sa[i]->sa_family) + continue; + switch (sa->sa_family) { + case AF_INET: + if (memcmp(&SA2SINADDR(sa), &SA2SINADDR(bound_sa[i]), + sizeof(struct in_addr)) == 0) + return (1); + break; +#ifdef INET6 + case AF_INET6: + if (memcmp(&SA2SIN6ADDR(sa), &SA2SIN6ADDR(bound_sa[i]), + sizeof(struct in6_addr)) == 0) + return (1); + break; +#endif + default: + break; + } + } + return (0); +} + static void rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, struct netbuf *addr) diff --git a/usr.sbin/rpcbind/rpcbind.h b/usr.sbin/rpcbind/rpcbind.h index 5537ce4..717bb3b 100644 --- a/usr.sbin/rpcbind/rpcbind.h +++ b/usr.sbin/rpcbind/rpcbind.h @@ -134,6 +134,7 @@ void read_warmstart(void); char *addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, char *netid); +int listen_addr(const struct sockaddr *sa); void network_init(void); struct sockaddr *local_sa(int); @@ -141,4 +142,12 @@ struct sockaddr *local_sa(int); #define RPCB_ALLVERS 0 #define RPCB_ONEVERS 1 +/* To convert a struct sockaddr to IPv4 or IPv6 address */ +#define SA2SIN(sa) ((struct sockaddr_in *)(sa)) +#define SA2SINADDR(sa) (SA2SIN(sa)->sin_addr) +#ifdef INET6 +#define SA2SIN6(sa) ((struct sockaddr_in6 *)(sa)) +#define SA2SIN6ADDR(sa) (SA2SIN6(sa)->sin6_addr) +#endif + #endif /* rpcbind_h */ diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c index 66797a7..9cdfa70 100644 --- a/usr.sbin/rpcbind/util.c +++ b/usr.sbin/rpcbind/util.c @@ -58,13 +58,6 @@ #include "rpcbind.h" -#define SA2SIN(sa) ((struct sockaddr_in *)(sa)) -#define SA2SINADDR(sa) (SA2SIN(sa)->sin_addr) -#ifdef INET6 -#define SA2SIN6(sa) ((struct sockaddr_in6 *)(sa)) -#define SA2SIN6ADDR(sa) (SA2SIN6(sa)->sin6_addr) -#endif - static struct sockaddr_in *local_in4; #ifdef INET6 static struct sockaddr_in6 *local_in6; @@ -176,9 +169,13 @@ addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, goto freeit; /* - * Loop through all interfaces. For each interface, see if the - * network portion of its address is equal to that of the client. - * If so, we have found the interface that we want to use. + * Loop through all interfaces. For each interface, see if it + * is either the loopback interface (which we always listen + * on) or is one of the addresses the program bound to (the + * wildcard by default, or a subset if -h is specified) and + * the network portion of its address is equal to that of the + * client. If so, we have found the interface that we want to + * use. */ bestif = NULL; for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { @@ -189,6 +186,9 @@ addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, !(ifap->ifa_flags & IFF_UP)) continue; + if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa)) + continue; + switch (hint_sa->sa_family) { case AF_INET: /* diff --git a/usr.sbin/sysinstall/package.c b/usr.sbin/sysinstall/package.c index 212b654..9aa4589 100644 --- a/usr.sbin/sysinstall/package.c +++ b/usr.sbin/sysinstall/package.c @@ -139,7 +139,7 @@ package_extract(Device *dev, char *name, Boolean depended) /* If necessary, initialize the ldconfig hints */ if (!file_readable("/var/run/ld-elf.so.hints")) - vsystem("ldconfig /usr/lib /usr/lib/compat /usr/local/lib /usr/X11R6/lib"); + vsystem("ldconfig /usr/lib /usr/lib/compat /usr/local/lib"); /* Be initially optimistic */ ret = DITEM_SUCCESS; diff --git a/usr.sbin/wake/Makefile b/usr.sbin/wake/Makefile index 938fc66..f75d469 100644 --- a/usr.sbin/wake/Makefile +++ b/usr.sbin/wake/Makefile @@ -3,4 +3,6 @@ PROG= wake MAN= wake.8 +WARNS?= 3 + .include <bsd.prog.mk> diff --git a/usr.sbin/wake/wake.8 b/usr.sbin/wake/wake.8 index 340dd9d..ce30abf 100644 --- a/usr.sbin/wake/wake.8 +++ b/usr.sbin/wake/wake.8 @@ -1,7 +1,7 @@ .\" .\" $FreeBSD$ .\" -.\" Copyright (c) 2009 Marc Balmer <marc@msys.ch> +.\" Copyright (c) 2009, 2010 Marc Balmer <marc@msys.ch> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -23,7 +23,7 @@ .Nd send Wake on LAN frames to hosts on a local Ethernet network .Sh SYNOPSIS .Nm -.Ar interface +.Op Ar interface .Ar lladdr .Op Ar lladdr ... .Sh DESCRIPTION @@ -37,21 +37,27 @@ and can be used to power on machines from a remote system without having physical access to them. .Pp .Ar interface -is a network interface of the local machine. +is an Ethernet interface of the local machine and is used to send the +Wake on LAN frames over it. +If there is only one Ethernet device available that is up and running, then the +.Ar interface +argument can be omitted. .Ar lladdr -are the link layer addresses of the remote machines -and can be specified as the actual hardware address +is the link layer address of the remote machine. +This can be specified as the actual hardware address (six hexadecimal numbers separated by colons) -or a hostname entry in +or as a hostname entry in .Pa /etc/ethers . -Link layer addresses can be determined and set on -.Fx -machines using +.Nm +accepts multiple +.Ar lladdr +addresses. +Link layer addresses can be determined and set using .Xr ifconfig 8 . .Sh FILES .Bl -tag -width "/etc/ethers" -compact .It /etc/ethers -Ethernet host name database. +Ethernet host name data base. .El .Sh SEE ALSO .Xr ethers 5 , diff --git a/usr.sbin/wake/wake.c b/usr.sbin/wake/wake.c index 1feda96..d6ba931 100644 --- a/usr.sbin/wake/wake.c +++ b/usr.sbin/wake/wake.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Marc Balmer <marc@msys.ch> + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Marc Balmer <marc@msys.ch> * Copyright (C) 2000 Eugene M. Kim. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,11 +32,14 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <net/bpf.h> #include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> #include <netinet/in.h> #include <netinet/if_ether.h> #include <err.h> #include <fcntl.h> +#include <ifaddrs.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -53,38 +56,29 @@ __FBSDID("$FreeBSD$"); #endif static int bind_if_to_bpf(char const *ifname, int bpf); +static int find_ether(char *dst, size_t len); static int get_ether(char const *text, struct ether_addr *addr); static int send_wakeup(int bpf, struct ether_addr const *addr); static void usage(void); -static int wake(const char *iface, const char *host); +static int wake(int bpf, const char *host); static void usage(void) { - (void)fprintf(stderr, "usage: wake interface lladdr [lladdr ...]\n"); + (void)fprintf(stderr, "usage: wake [interface] lladdr [lladdr ...]\n"); exit(1); } static int -wake(const char *iface, const char *host) +wake(int bpf, const char *host) { struct ether_addr macaddr; - int bpf, res; - bpf = open(_PATH_BPF, O_RDWR); - if (bpf == -1) { - warn("no bpf"); + if (get_ether(host, &macaddr) == -1) return (-1); - } - if (bind_if_to_bpf(iface, bpf) == -1 || - get_ether(host, &macaddr) == -1) { - (void)close(bpf); - return (-1); - } - res = send_wakeup(bpf, &macaddr); - (void)close(bpf); - return (res); + + return send_wakeup(bpf, &macaddr); } static int @@ -94,26 +88,50 @@ bind_if_to_bpf(char const *ifname, int bpf) u_int dlt; if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= - sizeof(ifr.ifr_name)) { - warnx("interface name too long: %s", ifname); + sizeof(ifr.ifr_name)) return (-1); - } - if (ioctl(bpf, BIOCSETIF, &ifr) == -1) { - warn("ioctl(%s)", "BIOCSETIF"); + + if (ioctl(bpf, BIOCSETIF, &ifr) == -1) return (-1); - } - if (ioctl(bpf, BIOCGDLT, &dlt) == -1) { - warn("ioctl(%s)", "BIOCGDLT"); + + if (ioctl(bpf, BIOCGDLT, &dlt) == -1) return (-1); - } - if (dlt != DLT_EN10MB) { - warnx("incompatible media"); + + if (dlt != DLT_EN10MB) return (-1); - } + return (0); } static int +find_ether(char *dst, size_t len) +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_dl *sdl = NULL; + int nifs; + + if (dst == NULL || len == 0) + return 0; + + if (getifaddrs(&ifap) != 0) + return -1; + + /* XXX also check the link state */ + for (nifs = 0, ifa = ifap; ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK && + ifa->ifa_flags & IFF_UP && ifa->ifa_flags & IFF_RUNNING) { + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_type == IFT_ETHER) { + strlcpy(dst, ifa->ifa_name, len); + nifs++; + } + } + + freeifaddrs(ifap); + return nifs == 1 ? 0 : -1; +} + +static int get_ether(char const *text, struct ether_addr *addr) { struct ether_addr *paddr; @@ -165,14 +183,31 @@ send_wakeup(int bpf, struct ether_addr const *addr) int main(int argc, char *argv[]) { - int n; + int bpf, n; + char ifname[IF_NAMESIZE]; + + if (argc < 2) + usage(); + + if ((bpf = open(_PATH_BPF, O_RDWR)) == -1) + err(1, "Cannot open bpf interface"); + + n = 2; + if (bind_if_to_bpf(argv[1], bpf) == -1) { + if (find_ether(ifname, sizeof(ifname))) + err(1, "Failed to determine ethernet interface"); + if (bind_if_to_bpf(ifname, bpf) == -1) + err(1, "Cannot bind to interface `%s'", ifname); + --n; + } else + strlcpy(ifname, argv[1], sizeof(ifname)); - if (argc < 3) + if (n >= argc) usage(); + for (; n < argc; n++) + if (wake(bpf, argv[n])) + warn("Cannot send Wake on LAN frame over `%s' to `%s'", + ifname, argv[n]); - for (n = 2; n < argc; n++) - if (wake(argv[1], argv[n])) - warnx("error sending Wake on LAN frame over %s to %s", - argv[1], argv[n]); return (0); } |