Index: head/lib/libkvm/Makefile =================================================================== --- head/lib/libkvm/Makefile (revision 204493) +++ head/lib/libkvm/Makefile (revision 204494) @@ -1,30 +1,31 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 # $FreeBSD$ LIB= kvm SHLIBDIR?= /lib CFLAGS+=-DLIBC_SCCS -I${.CURDIR} .if ${MACHINE} == "sun4v" CFLAGS+=-DSUN4V .endif WARNS?= 0 SRCS= kvm.c kvm_${MACHINE_ARCH}.c kvm_cptime.c kvm_file.c kvm_getloadavg.c \ kvm_getswapinfo.c kvm_pcpu.c kvm_proc.c kvm_vnet.c .if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "arm" SRCS+= kvm_minidump_${MACHINE_ARCH}.c .endif INCS= kvm.h MAN= kvm.3 kvm_getcptime.3 kvm_geterr.3 kvm_getfiles.3 kvm_getloadavg.3 \ kvm_getpcpu.3 kvm_getprocs.3 kvm_getswapinfo.3 kvm_nlist.3 kvm_open.3 \ kvm_read.3 MLINKS+=kvm_getpcpu.3 kvm_getmaxcpu.3 +MLINKS+=kvm_getpcpu.3 kvm_dpcpu_setcpu.3 MLINKS+=kvm_getprocs.3 kvm_getargv.3 kvm_getprocs.3 kvm_getenvv.3 MLINKS+=kvm_open.3 kvm_close.3 kvm_open.3 kvm_openfiles.3 MLINKS+=kvm_read.3 kvm_write.3 .include Index: head/lib/libkvm/kvm.c =================================================================== --- head/lib/libkvm/kvm.c (revision 204493) +++ head/lib/libkvm/kvm.c (revision 204494) @@ -1,603 +1,620 @@ /*- * Copyright (c) 1989, 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software developed by the Computer Systems * Engineering group at Lawrence Berkeley Laboratory under DARPA contract * BG 91-66 and contributed to Berkeley. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 __FBSDID("$FreeBSD$"); #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94"; #endif #endif /* LIBC_SCCS and not lint */ #include #define _WANT_VNET #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kvm_private.h" /* from src/lib/libc/gen/nlist.c */ int __fdnlist(int, struct nlist *); char * kvm_geterr(kd) kvm_t *kd; { return (kd->errbuf); } #include /* * Report an error using printf style arguments. "program" is kd->program * on hard errors, and 0 on soft errors, so that under sun error emulation, * only hard errors are printed out (otherwise, programs like gdb will * generate tons of error messages when trying to access bogus pointers). */ void _kvm_err(kvm_t *kd, const char *program, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (program != NULL) { (void)fprintf(stderr, "%s: ", program); (void)vfprintf(stderr, fmt, ap); (void)fputc('\n', stderr); } else (void)vsnprintf(kd->errbuf, sizeof(kd->errbuf), (char *)fmt, ap); va_end(ap); } void _kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); if (program != NULL) { (void)fprintf(stderr, "%s: ", program); (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, ": %s\n", strerror(errno)); } else { char *cp = kd->errbuf; (void)vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap); n = strlen(cp); (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s", strerror(errno)); } va_end(ap); } void * _kvm_malloc(kd, n) kvm_t *kd; size_t n; { void *p; if ((p = calloc(n, sizeof(char))) == NULL) _kvm_err(kd, kd->program, "can't allocate %u bytes: %s", n, strerror(errno)); return (p); } static kvm_t * _kvm_open(kd, uf, mf, flag, errout) kvm_t *kd; const char *uf; const char *mf; int flag; char *errout; { struct stat st; kd->vmfd = -1; kd->pmfd = -1; kd->nlfd = -1; kd->vmst = 0; kd->procbase = 0; kd->argspc = 0; kd->argv = 0; if (uf == 0) uf = getbootfile(); else if (strlen(uf) >= MAXPATHLEN) { _kvm_err(kd, kd->program, "exec file name too long"); goto failed; } if (flag & ~O_RDWR) { _kvm_err(kd, kd->program, "bad flags arg"); goto failed; } if (mf == 0) mf = _PATH_MEM; if ((kd->pmfd = open(mf, flag, 0)) < 0) { _kvm_syserr(kd, kd->program, "%s", mf); goto failed; } if (fstat(kd->pmfd, &st) < 0) { _kvm_syserr(kd, kd->program, "%s", mf); goto failed; } if (S_ISREG(st.st_mode) && st.st_size <= 0) { errno = EINVAL; _kvm_syserr(kd, kd->program, "empty file"); goto failed; } if (fcntl(kd->pmfd, F_SETFD, FD_CLOEXEC) < 0) { _kvm_syserr(kd, kd->program, "%s", mf); goto failed; } if (S_ISCHR(st.st_mode)) { /* * If this is a character special device, then check that * it's /dev/mem. If so, open kmem too. (Maybe we should * make it work for either /dev/mem or /dev/kmem -- in either * case you're working with a live kernel.) */ if (strcmp(mf, _PATH_DEVNULL) == 0) { kd->vmfd = open(_PATH_DEVNULL, O_RDONLY); return (kd); } else if (strcmp(mf, _PATH_MEM) == 0) { if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) { _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM); goto failed; } if (fcntl(kd->vmfd, F_SETFD, FD_CLOEXEC) < 0) { _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM); goto failed; } return (kd); } } /* * This is a crash dump. * Initialize the virtual address translation machinery, * but first setup the namelist fd. */ if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) { _kvm_syserr(kd, kd->program, "%s", uf); goto failed; } if (fcntl(kd->nlfd, F_SETFD, FD_CLOEXEC) < 0) { _kvm_syserr(kd, kd->program, "%s", uf); goto failed; } if (strncmp(mf, _PATH_FWMEM, strlen(_PATH_FWMEM)) == 0) kd->rawdump = 1; if (_kvm_initvtop(kd) < 0) goto failed; return (kd); failed: /* * Copy out the error if doing sane error semantics. */ if (errout != 0) strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX); (void)kvm_close(kd); return (0); } kvm_t * kvm_openfiles(uf, mf, sf, flag, errout) const char *uf; const char *mf; const char *sf __unused; int flag; char *errout; { kvm_t *kd; if ((kd = calloc(1, sizeof(*kd))) == NULL) { (void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX); return (0); } kd->program = 0; return (_kvm_open(kd, uf, mf, flag, errout)); } kvm_t * kvm_open(uf, mf, sf, flag, errstr) const char *uf; const char *mf; const char *sf __unused; int flag; const char *errstr; { kvm_t *kd; if ((kd = calloc(1, sizeof(*kd))) == NULL) { if (errstr != NULL) (void)fprintf(stderr, "%s: %s\n", errstr, strerror(errno)); return (0); } kd->program = errstr; return (_kvm_open(kd, uf, mf, flag, NULL)); } int kvm_close(kd) kvm_t *kd; { int error = 0; if (kd->pmfd >= 0) error |= close(kd->pmfd); if (kd->vmfd >= 0) error |= close(kd->vmfd); if (kd->nlfd >= 0) error |= close(kd->nlfd); if (kd->vmst) _kvm_freevtop(kd); if (kd->procbase != 0) free((void *)kd->procbase); if (kd->argbuf != 0) free((void *) kd->argbuf); if (kd->argspc != 0) free((void *) kd->argspc); if (kd->argv != 0) free((void *)kd->argv); free((void *)kd); return (0); } /* * Walk the list of unresolved symbols, generate a new list and prefix the * symbol names, try again, and merge back what we could resolve. */ static int kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix, uintptr_t (*validate_fn)(kvm_t *, uintptr_t)) { struct nlist *n, *np, *p; char *cp, *ce; size_t len; int unresolved; /* * Calculate the space we need to malloc for nlist and names. * We are going to store the name twice for later lookups: once * with the prefix and once the unmodified name delmited by \0. */ len = 0; unresolved = 0; for (p = nl; p->n_name && p->n_name[0]; ++p) { if (p->n_type != N_UNDF) continue; len += sizeof(struct nlist) + strlen(prefix) + 2 * (strlen(p->n_name) + 1); unresolved++; } if (unresolved == 0) return (unresolved); /* Add space for the terminating nlist entry. */ len += sizeof(struct nlist); unresolved++; /* Alloc one chunk for (nlist, [names]) and setup pointers. */ n = np = malloc(len); bzero(n, len); if (n == NULL) return (missing); cp = ce = (char *)np; cp += unresolved * sizeof(struct nlist); ce += len; /* Generate shortened nlist with special prefix. */ unresolved = 0; for (p = nl; p->n_name && p->n_name[0]; ++p) { if (p->n_type != N_UNDF) continue; bcopy(p, np, sizeof(struct nlist)); /* Save the new\0orig. name so we can later match it again. */ len = snprintf(cp, ce - cp, "%s%s%c%s", prefix, (prefix[0] != '\0' && p->n_name[0] == '_') ? (p->n_name + 1) : p->n_name, '\0', p->n_name); if (len >= ce - cp) continue; np->n_name = cp; cp += len + 1; np++; unresolved++; } /* Do lookup on the reduced list. */ np = n; unresolved = __fdnlist(kd->nlfd, np); /* Check if we could resolve further symbols and update the list. */ if (unresolved >= 0 && unresolved < missing) { /* Find the first freshly resolved entry. */ for (; np->n_name && np->n_name[0]; np++) if (np->n_type != N_UNDF) break; /* * The lists are both in the same order, * so we can walk them in parallel. */ for (p = nl; np->n_name && np->n_name[0] && p->n_name && p->n_name[0]; ++p) { if (p->n_type != N_UNDF) continue; /* Skip expanded name and compare to orig. one. */ cp = np->n_name + strlen(np->n_name) + 1; if (strcmp(cp, p->n_name)) continue; /* Update nlist with new, translated results. */ p->n_type = np->n_type; p->n_other = np->n_other; p->n_desc = np->n_desc; if (validate_fn) p->n_value = (*validate_fn)(kd, np->n_value); else p->n_value = np->n_value; missing--; /* Find next freshly resolved entry. */ for (np++; np->n_name && np->n_name[0]; np++) if (np->n_type != N_UNDF) break; } } /* We could assert missing = unresolved here. */ free(n); return (unresolved); } int _kvm_nlist(kvm_t *kd, struct nlist *nl, int initialize) { struct nlist *p; int nvalid; struct kld_sym_lookup lookup; int error; char *prefix = "", symname[1024]; /* XXX-BZ symbol name length limit? */ + int tried_vnet, tried_dpcpu; + /* * If we can't use the kld symbol lookup, revert to the * slow library call. */ if (!ISALIVE(kd)) { error = __fdnlist(kd->nlfd, nl); if (error <= 0) /* Hard error or success. */ return (error); if (_kvm_vnet_initialized(kd, initialize)) error = kvm_fdnlist_prefix(kd, nl, error, VNET_SYMPREFIX, _kvm_vnet_validaddr); + if (error > 0 && _kvm_dpcpu_initialized(kd, initialize)) + error = kvm_fdnlist_prefix(kd, nl, error, + "pcpu_entry_", _kvm_dpcpu_validaddr); + return (error); } /* * We can use the kld lookup syscall. Go through each nlist entry * and look it up with a kldsym(2) syscall. */ nvalid = 0; + tried_vnet = 0; + tried_dpcpu = 0; again: for (p = nl; p->n_name && p->n_name[0]; ++p) { if (p->n_type != N_UNDF) continue; lookup.version = sizeof(lookup); lookup.symvalue = 0; lookup.symsize = 0; error = snprintf(symname, sizeof(symname), "%s%s", prefix, (prefix[0] != '\0' && p->n_name[0] == '_') ? (p->n_name + 1) : p->n_name); if (error >= sizeof(symname)) continue; lookup.symname = symname; if (lookup.symname[0] == '_') lookup.symname++; if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) { p->n_type = N_TEXT; p->n_other = 0; p->n_desc = 0; if (_kvm_vnet_initialized(kd, initialize) && !strcmp(prefix, VNET_SYMPREFIX)) p->n_value = _kvm_vnet_validaddr(kd, lookup.symvalue); + else if (_kvm_dpcpu_initialized(kd, initialize) && + !strcmp(prefix, "pcpu_entry_")) + p->n_value = + _kvm_dpcpu_validaddr(kd, lookup.symvalue); else p->n_value = lookup.symvalue; ++nvalid; /* lookup.symsize */ } } /* * Check the number of entries that weren't found. If they exist, - * try again with a prefix for virtualized symbol names. + * try again with a prefix for virtualized or DPCPU symbol names. */ error = ((p - nl) - nvalid); - if (error && _kvm_vnet_initialized(kd, initialize) && - strcmp(prefix, VNET_SYMPREFIX)) { + if (error && _kvm_vnet_initialized(kd, initialize) && !tried_vnet) { + tried_vnet = 1; prefix = VNET_SYMPREFIX; + goto again; + } + if (error && _kvm_dpcpu_initialized(kd, initialize) && !tried_dpcpu) { + tried_dpcpu = 1; + prefix = "pcpu_entry_"; goto again; } /* * Return the number of entries that weren't found. If they exist, * also fill internal error buffer. */ error = ((p - nl) - nvalid); if (error) _kvm_syserr(kd, kd->program, "kvm_nlist"); return (error); } int kvm_nlist(kd, nl) kvm_t *kd; struct nlist *nl; { /* * If called via the public interface, permit intialization of * further virtualized modules on demand. */ return (_kvm_nlist(kd, nl, 1)); } ssize_t kvm_read(kd, kva, buf, len) kvm_t *kd; u_long kva; void *buf; size_t len; { int cc; char *cp; if (ISALIVE(kd)) { /* * We're using /dev/kmem. Just read straight from the * device and let the active kernel do the address translation. */ errno = 0; if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { _kvm_err(kd, 0, "invalid address (%x)", kva); return (-1); } cc = read(kd->vmfd, buf, len); if (cc < 0) { _kvm_syserr(kd, 0, "kvm_read"); return (-1); } else if (cc < len) _kvm_err(kd, kd->program, "short read"); return (cc); } else { cp = buf; while (len > 0) { off_t pa; cc = _kvm_kvatop(kd, kva, &pa); if (cc == 0) return (-1); if (cc > len) cc = len; errno = 0; if (lseek(kd->pmfd, pa, 0) == -1 && errno != 0) { _kvm_syserr(kd, 0, _PATH_MEM); break; } cc = read(kd->pmfd, cp, cc); if (cc < 0) { _kvm_syserr(kd, kd->program, "kvm_read"); break; } /* * If kvm_kvatop returns a bogus value or our core * file is truncated, we might wind up seeking beyond * the end of the core file in which case the read will * return 0 (EOF). */ if (cc == 0) break; cp += cc; kva += cc; len -= cc; } return (cp - (char *)buf); } /* NOTREACHED */ } ssize_t kvm_write(kd, kva, buf, len) kvm_t *kd; u_long kva; const void *buf; size_t len; { int cc; if (ISALIVE(kd)) { /* * Just like kvm_read, only we write. */ errno = 0; if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { _kvm_err(kd, 0, "invalid address (%x)", kva); return (-1); } cc = write(kd->vmfd, buf, len); if (cc < 0) { _kvm_syserr(kd, 0, "kvm_write"); return (-1); } else if (cc < len) _kvm_err(kd, kd->program, "short write"); return (cc); } else { _kvm_err(kd, kd->program, "kvm_write not implemented for dead kernels"); return (-1); } /* NOTREACHED */ } Index: head/lib/libkvm/kvm.h =================================================================== --- head/lib/libkvm/kvm.h (revision 204493) +++ head/lib/libkvm/kvm.h (revision 204494) @@ -1,94 +1,95 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)kvm.h 8.1 (Berkeley) 6/2/93 * $FreeBSD$ */ #ifndef _KVM_H_ #define _KVM_H_ #include #include #include /* Default version symbol. */ #define VRS_SYM "_version" #define VRS_KEY "VERSION" #ifndef _SIZE_T_DECLARED typedef __size_t size_t; #define _SIZE_T_DECLARED #endif #ifndef _SSIZE_T_DECLARED typedef __ssize_t ssize_t; #define _SSIZE_T_DECLARED #endif typedef struct __kvm kvm_t; struct kinfo_proc; struct proc; struct kvm_swap { char ksw_devname[32]; int ksw_used; int ksw_total; int ksw_flags; int ksw_reserved1; int ksw_reserved2; }; #define SWIF_DEV_PREFIX 0x0002 __BEGIN_DECLS int kvm_close(kvm_t *); +int kvm_dpcpu_setcpu(kvm_t *, u_int); char **kvm_getargv(kvm_t *, const struct kinfo_proc *, int); int kvm_getcptime(kvm_t *, long *); char **kvm_getenvv(kvm_t *, const struct kinfo_proc *, int); char *kvm_geterr(kvm_t *); char *kvm_getfiles(kvm_t *, int, int, int *); int kvm_getloadavg(kvm_t *, double [], int); int kvm_getmaxcpu(kvm_t *); void *kvm_getpcpu(kvm_t *, int); struct kinfo_proc * kvm_getprocs(kvm_t *, int, int, int *); int kvm_getswapinfo(kvm_t *, struct kvm_swap *, int, int); int kvm_nlist(kvm_t *, struct nlist *); kvm_t *kvm_open (const char *, const char *, const char *, int, const char *); kvm_t *kvm_openfiles (const char *, const char *, const char *, int, char *); ssize_t kvm_read(kvm_t *, unsigned long, void *, size_t); ssize_t kvm_uread (kvm_t *, struct kinfo_proc *, unsigned long, char *, size_t); ssize_t kvm_write(kvm_t *, unsigned long, const void *, size_t); __END_DECLS #endif /* !_KVM_H_ */ Index: head/lib/libkvm/kvm_getpcpu.3 =================================================================== --- head/lib/libkvm/kvm_getpcpu.3 (revision 204493) +++ head/lib/libkvm/kvm_getpcpu.3 (revision 204494) @@ -1,101 +1,130 @@ .\" Copyright (c) 2008 Yahoo!, Inc. .\" All rights reserved. .\" Written by: John Baldwin .\" .\" 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. .\" 3. Neither the name of the author nor the names of any co-contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" 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$ .\" -.Dd August 19, 2008 +.Dd February 28, 2010 .Dt KVM_GETPCPU 3 .Os .Sh NAME +.Nm kvm_dpcpu_setcpu .Nm kvm_getmaxcpu , .Nm kvm_getpcpu .Nd access per-CPU data .Sh LIBRARY .Lb libkvm .Sh SYNOPSIS .In sys/param.h .In sys/pcpu.h .In sys/sysctl.h .In kvm.h .Ft int +.Fn kvm_dpcpu_setcpu "kvm_t *kd" "u_int cpu" +.Ft int .Fn kvm_getmaxcpu "kvm_t *kd" .Ft void * .Fn kvm_getpcpu "kvm_t *kd" "int cpu" .Sh DESCRIPTION The -.Fn kvm_getmaxcpu +.Fn kvm_dpcpu_setcpu , +.Fn kvm_getmaxcpu , and .Fn kvm_getpcpu functions are used to access the per-CPU data of active processors in the kernel indicated by .Fa kd . +Per-CPU storage comes in two flavours: data stored directly in a +.Vt "struct pcpu" +associated with each CPU, and dynamic per-CPU storage (DPCPU), in which a +single kernel symbol refers to different data depending on what CPU it is +accessed from. +.Pp The .Fn kvm_getmaxcpu function returns the maximum number of CPUs supported by the kernel. +.Pp The .Fn kvm_getpcpu function returns a buffer holding the per-CPU data for a single CPU. This buffer is described by the .Vt "struct pcpu" type. The caller is responsible for releasing the buffer via a call to .Xr free 3 when it is no longer needed. If .Fa cpu is not active, then .Dv NULL is returned instead. +.Pp +Symbols for dynamic per-CPU data are accessed via +.Xr kvm_nlist 3 +as with other symbols. +.Nm libkvm +maintains a notion of the "current CPU", set by +.Xr kvm_dpcpu_setcpu , +which defaults to 0. +Once another CPU is selected, +.Xr kvm_nlist 3 +will return pointers to that data on the appropriate CPU. .Sh CACHING -These functions cache the nlist values for various kernel variables which are +.Fn kvm_getmaxcpu +and +.Vn kvm_getpcpu +cache the nlist values for various kernel variables which are reused in successive calls. You may call either function with .Fa kd set to .Dv NULL to clear this cache. .Sh RETURN VALUES On success, the .Fn kvm_getmaxcpu function returns the maximum number of CPUs supported by the kernel. If an error occurs, it returns -1 instead. .Pp On success, the .Fn kvm_getpcpu function returns a pointer to an allocated buffer or .Dv NULL. If an error occurs, it returns -1 instead. .Pp -If either function encounters an error, +On success, the +.Fn kvm_dpcpu_setcpu +call returns 0; if an error occurs, it returns -1 instead. +.Pp +If any function encounters an error, then an error message may be retrieved via .Xr kvm_geterr 3. .Sh SEE ALSO .Xr free 3 , .Xr kvm 3 Index: head/lib/libkvm/kvm_pcpu.c =================================================================== --- head/lib/libkvm/kvm_pcpu.c (revision 204493) +++ head/lib/libkvm/kvm_pcpu.c (revision 204494) @@ -1,152 +1,292 @@ /*- + * Copyright (c) 2010 Juniper Networks, Inc. + * Copyright (c) 2009 Robert N. M. Watson + * Copyright (c) 2009 Bjoern A. Zeeb * Copyright (c) 2008 Yahoo!, Inc. * All rights reserved. + * * Written by: John Baldwin * + * This software was developed by Robert N. M. Watson under contract + * to Juniper Networks, 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. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "kvm_private.h" static struct nlist kvm_pcpu_nl[] = { { "_cpuid_to_pcpu" }, { "_mp_maxcpus" }, { NULL }, }; /* * Kernel per-CPU data state. We cache this stuff on the first * access. + * + * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the + * consumer has multiple handles in flight to differently configured + * kernels/crashdumps. */ static void **pcpu_data; static int maxcpu; #define NL_CPUID_TO_PCPU 0 #define NL_MP_MAXCPUS 1 static int _kvm_pcpu_init(kvm_t *kd) { size_t len; int max; void *data; if (kvm_nlist(kd, kvm_pcpu_nl) < 0) return (-1); if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) { _kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu"); return (-1); } if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) { _kvm_err(kd, kd->program, "unable to find mp_maxcpus"); return (-1); } if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max, sizeof(max)) != sizeof(max)) { _kvm_err(kd, kd->program, "cannot read mp_maxcpus"); return (-1); } len = max * sizeof(void *); data = malloc(len); if (data == NULL) { _kvm_err(kd, kd->program, "out of memory"); return (-1); } if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) != len) { _kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array"); free(data); return (-1); } pcpu_data = data; maxcpu = max; return (0); } static void _kvm_pcpu_clear(void) { maxcpu = 0; free(pcpu_data); pcpu_data = NULL; } void * kvm_getpcpu(kvm_t *kd, int cpu) { char *buf; int i; if (kd == NULL) { _kvm_pcpu_clear(); return (NULL); } if (maxcpu == 0) if (_kvm_pcpu_init(kd) < 0) return ((void *)-1); if (cpu >= maxcpu || pcpu_data[cpu] == NULL) return (NULL); buf = malloc(sizeof(struct pcpu)); if (buf == NULL) { _kvm_err(kd, kd->program, "out of memory"); return ((void *)-1); } if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, sizeof(struct pcpu)) != sizeof(struct pcpu)) { _kvm_err(kd, kd->program, "unable to read per-CPU data"); free(buf); return ((void *)-1); } return (buf); } int kvm_getmaxcpu(kvm_t *kd) { if (kd == NULL) { _kvm_pcpu_clear(); return (0); } if (maxcpu == 0) if (_kvm_pcpu_init(kd) < 0) return (-1); return (maxcpu); +} + +static int +_kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) +{ + + if (!kd->dpcpu_initialized) { + if (report_error) + _kvm_err(kd, kd->program, "%s: not initialized", + __func__); + return (-1); + } + if (cpu >= kd->dpcpu_maxcpus) { + if (report_error) + _kvm_err(kd, kd->program, "%s: CPU %u too big", + __func__, cpu); + return (-1); + } + if (kd->dpcpu_off[cpu] == 0) { + if (report_error) + _kvm_err(kd, kd->program, "%s: CPU %u not found", + __func__, cpu); + return (-1); + } + kd->dpcpu_curcpu = cpu; + kd->dpcpu_curoff = kd->dpcpu_off[cpu]; + return (0); +} + +/* + * Set up libkvm to handle dynamic per-CPU memory. + */ +static int +_kvm_dpcpu_init(kvm_t *kd) +{ + struct nlist nl[] = { +#define NLIST_START_SET_PCPU 0 + { "___start_set_pcpu" }, +#define NLIST_STOP_SET_PCPU 1 + { "___stop_set_pcpu" }, +#define NLIST_DPCPU_OFF 2 + { "_dpcpu_off" }, +#define NLIST_MP_MAXCPUS 3 + { "_mp_maxcpus" }, + { NULL }, + }; + uintptr_t *dpcpu_off_buf; + size_t len; + u_int dpcpu_maxcpus; + + /* + * Locate and cache locations of important symbols using the internal + * version of _kvm_nlist, turning off initialization to avoid + * recursion in case of unresolveable symbols. + */ + if (_kvm_nlist(kd, nl, 0) != 0) + return (-1); + if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, + sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) + return (-1); + len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); + dpcpu_off_buf = malloc(len); + if (dpcpu_off_buf == NULL) + return (-1); + if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != + len) { + free(dpcpu_off_buf); + return (-1); + } + kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; + kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; + kd->dpcpu_maxcpus = dpcpu_maxcpus; + kd->dpcpu_off = dpcpu_off_buf; + kd->dpcpu_initialized = 1; + (void)_kvm_dpcpu_setcpu(kd, 0, 0); + return (0); +} + +/* + * Check whether the dpcpu module has been initialized sucessfully or not, + * initialize it if permitted. + */ +int +_kvm_dpcpu_initialized(kvm_t *kd, int intialize) +{ + + if (kd->dpcpu_initialized || !intialize) + return (kd->dpcpu_initialized); + + (void)_kvm_dpcpu_init(kd); + + return (kd->dpcpu_initialized); +} + +/* + * Check whether the value is within the dpcpu symbol range and only if so + * adjust the offset relative to the current offset. + */ +uintptr_t +_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value) +{ + + if (value == 0) + return (value); + + if (!kd->dpcpu_initialized) + return (value); + + if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) + return (value); + + return (kd->dpcpu_curoff + value); +} + +int +kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) +{ + int ret; + + if (!kd->dpcpu_initialized) { + ret = _kvm_dpcpu_init(kd); + if (ret != 0) { + _kvm_err(kd, kd->program, "%s: init failed", + __func__); + return (ret); + } + } + + return (_kvm_dpcpu_setcpu(kd, cpu, 1)); } Index: head/lib/libkvm/kvm_private.h =================================================================== --- head/lib/libkvm/kvm_private.h (revision 204493) +++ head/lib/libkvm/kvm_private.h (revision 204494) @@ -1,96 +1,111 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software developed by the Computer Systems * Engineering group at Lawrence Berkeley Laboratory under DARPA contract * BG 91-66 and contributed to Berkeley. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)kvm_private.h 8.1 (Berkeley) 6/4/93 * $FreeBSD$ */ struct __kvm { /* * a string to be prepended to error messages * provided for compatibility with sun's interface * if this value is null, errors are saved in errbuf[] */ const char *program; char *errp; /* XXX this can probably go away */ char errbuf[_POSIX2_LINE_MAX]; #define ISALIVE(kd) ((kd)->vmfd >= 0) int pmfd; /* physical memory file (or crashdump) */ int vmfd; /* virtual memory file (-1 if crashdump) */ int unused; /* was: swap file (e.g., /dev/drum) */ int nlfd; /* namelist file (e.g., /kernel) */ struct kinfo_proc *procbase; char *argspc; /* (dynamic) storage for argv strings */ int arglen; /* length of the above */ char **argv; /* (dynamic) storage for argv pointers */ int argc; /* length of above (not actual # present) */ char *argbuf; /* (dynamic) temporary storage */ /* * Kernel virtual address translation state. This only gets filled * in for dead kernels; otherwise, the running kernel (i.e. kmem) * will do the translations for us. It could be big, so we * only allocate it if necessary. */ struct vmstate *vmst; int rawdump; /* raw dump format */ int vnet_initialized; /* vnet fields set up */ uintptr_t vnet_start; /* start of kernel's vnet region */ uintptr_t vnet_stop; /* stop of kernel's vnet region */ uintptr_t vnet_current; /* vnet we're working with */ uintptr_t vnet_base; /* vnet base of current vnet */ + + /* + * Dynamic per-CPU kernel memory. We translate symbols, on-demand, + * to the data associated with dpcpu_curcpu, set with + * kvm_dpcpu_setcpu(). + */ + int dpcpu_initialized; /* dpcpu fields set up */ + uintptr_t dpcpu_start; /* start of kernel's dpcpu region */ + uintptr_t dpcpu_stop; /* stop of kernel's dpcpu region */ + u_int dpcpu_maxcpus; /* size of base array */ + uintptr_t *dpcpu_off; /* base array, indexed by CPU ID */ + u_int dpcpu_curcpu; /* CPU we're currently working with */ + uintptr_t dpcpu_curoff; /* dpcpu base of current CPU */ }; /* * Functions used internally by kvm, but across kvm modules. */ void _kvm_err(kvm_t *kd, const char *program, const char *fmt, ...) __printflike(3, 4); void _kvm_freeprocs(kvm_t *kd); void _kvm_freevtop(kvm_t *); int _kvm_initvtop(kvm_t *); int _kvm_kvatop(kvm_t *, u_long, off_t *); void *_kvm_malloc(kvm_t *kd, size_t); int _kvm_nlist(kvm_t *, struct nlist *, int); void *_kvm_realloc(kvm_t *kd, void *, size_t); void _kvm_syserr (kvm_t *kd, const char *program, const char *fmt, ...) __printflike(3, 4); int _kvm_uvatop(kvm_t *, const struct proc *, u_long, u_long *); int _kvm_vnet_selectpid(kvm_t *, pid_t); int _kvm_vnet_initialized(kvm_t *, int); uintptr_t _kvm_vnet_validaddr(kvm_t *, uintptr_t); +int _kvm_dpcpu_initialized(kvm_t *, int); +uintptr_t _kvm_dpcpu_validaddr(kvm_t *, uintptr_t); #if defined(__amd64__) || defined(__i386__) || defined(__arm__) void _kvm_minidump_freevtop(kvm_t *); int _kvm_minidump_initvtop(kvm_t *); int _kvm_minidump_kvatop(kvm_t *, u_long, off_t *); #endif