kvm_pcpu.c revision 222813
1/*- 2 * Copyright (c) 2010 Juniper Networks, Inc. 3 * Copyright (c) 2009 Robert N. M. Watson 4 * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org> 5 * Copyright (c) 2008 Yahoo!, Inc. 6 * All rights reserved. 7 * 8 * Written by: John Baldwin <jhb@FreeBSD.org> 9 * 10 * This software was developed by Robert N. M. Watson under contract 11 * to Juniper Networks, Inc. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the author nor the names of any co-contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#include <sys/cdefs.h> 39__FBSDID("$FreeBSD: head/lib/libkvm/kvm_pcpu.c 222813 2011-06-07 08:46:13Z attilio $"); 40 41#include <sys/param.h> 42#include <sys/cpuset.h> 43#include <sys/pcpu.h> 44#include <sys/sysctl.h> 45#include <kvm.h> 46#include <limits.h> 47#include <stdlib.h> 48#include <unistd.h> 49 50#include "kvm_private.h" 51 52static struct nlist kvm_pcpu_nl[] = { 53 { .n_name = "_cpuid_to_pcpu" }, 54 { .n_name = "_mp_maxcpus" }, 55 { .n_name = NULL }, 56}; 57 58/* 59 * Kernel per-CPU data state. We cache this stuff on the first 60 * access. 61 * 62 * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the 63 * consumer has multiple handles in flight to differently configured 64 * kernels/crashdumps. 65 */ 66static void **pcpu_data; 67static int maxcpu; 68 69#define NL_CPUID_TO_PCPU 0 70#define NL_MP_MAXCPUS 1 71 72static int 73_kvm_pcpu_init(kvm_t *kd) 74{ 75 size_t len; 76 int max; 77 void *data; 78 79 if (kvm_nlist(kd, kvm_pcpu_nl) < 0) 80 return (-1); 81 if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) { 82 _kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu"); 83 return (-1); 84 } 85 if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) { 86 _kvm_err(kd, kd->program, "unable to find mp_maxcpus"); 87 return (-1); 88 } 89 if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max, 90 sizeof(max)) != sizeof(max)) { 91 _kvm_err(kd, kd->program, "cannot read mp_maxcpus"); 92 return (-1); 93 } 94 len = max * sizeof(void *); 95 data = malloc(len); 96 if (data == NULL) { 97 _kvm_err(kd, kd->program, "out of memory"); 98 return (-1); 99 } 100 if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) != 101 (ssize_t)len) { 102 _kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array"); 103 free(data); 104 return (-1); 105 } 106 pcpu_data = data; 107 maxcpu = max; 108 return (0); 109} 110 111static void 112_kvm_pcpu_clear(void) 113{ 114 115 maxcpu = 0; 116 free(pcpu_data); 117 pcpu_data = NULL; 118} 119 120void * 121kvm_getpcpu(kvm_t *kd, int cpu) 122{ 123 long kcpusetsize; 124 ssize_t nbytes; 125 uintptr_t readptr; 126 char *buf; 127 128 if (kd == NULL) { 129 _kvm_pcpu_clear(); 130 return (NULL); 131 } 132 133 kcpusetsize = sysconf(_SC_CPUSET_SIZE); 134 if (kcpusetsize == -1 || (u_long)kcpusetsize > sizeof(cpuset_t)) 135 return ((void *)-1); 136 137 if (maxcpu == 0) 138 if (_kvm_pcpu_init(kd) < 0) 139 return ((void *)-1); 140 141 if (cpu >= maxcpu || pcpu_data[cpu] == NULL) 142 return (NULL); 143 144 buf = malloc(sizeof(struct pcpu)); 145 if (buf == NULL) { 146 _kvm_err(kd, kd->program, "out of memory"); 147 return ((void *)-1); 148 } 149 nbytes = sizeof(struct pcpu) - 2 * kcpusetsize; 150 readptr = (uintptr_t)pcpu_data[cpu]; 151 if (kvm_read(kd, readptr, buf, nbytes) != nbytes) { 152 _kvm_err(kd, kd->program, "unable to read per-CPU data"); 153 free(buf); 154 return ((void *)-1); 155 } 156 157 /* Fetch the valid cpuset_t objects. */ 158 CPU_ZERO((cpuset_t *)(buf + nbytes)); 159 CPU_ZERO((cpuset_t *)(buf + nbytes + sizeof(cpuset_t))); 160 readptr += nbytes; 161 if (kvm_read(kd, readptr, buf + nbytes, kcpusetsize) != kcpusetsize) { 162 _kvm_err(kd, kd->program, "unable to read per-CPU data"); 163 free(buf); 164 return ((void *)-1); 165 } 166 readptr += kcpusetsize; 167 if (kvm_read(kd, readptr, buf + nbytes + sizeof(cpuset_t), 168 kcpusetsize) != kcpusetsize) { 169 _kvm_err(kd, kd->program, "unable to read per-CPU data"); 170 free(buf); 171 return ((void *)-1); 172 } 173 return (buf); 174} 175 176int 177kvm_getmaxcpu(kvm_t *kd) 178{ 179 180 if (kd == NULL) { 181 _kvm_pcpu_clear(); 182 return (0); 183 } 184 185 if (maxcpu == 0) 186 if (_kvm_pcpu_init(kd) < 0) 187 return (-1); 188 return (maxcpu); 189} 190 191static int 192_kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) 193{ 194 195 if (!kd->dpcpu_initialized) { 196 if (report_error) 197 _kvm_err(kd, kd->program, "%s: not initialized", 198 __func__); 199 return (-1); 200 } 201 if (cpu >= kd->dpcpu_maxcpus) { 202 if (report_error) 203 _kvm_err(kd, kd->program, "%s: CPU %u too big", 204 __func__, cpu); 205 return (-1); 206 } 207 if (kd->dpcpu_off[cpu] == 0) { 208 if (report_error) 209 _kvm_err(kd, kd->program, "%s: CPU %u not found", 210 __func__, cpu); 211 return (-1); 212 } 213 kd->dpcpu_curcpu = cpu; 214 kd->dpcpu_curoff = kd->dpcpu_off[cpu]; 215 return (0); 216} 217 218/* 219 * Set up libkvm to handle dynamic per-CPU memory. 220 */ 221static int 222_kvm_dpcpu_init(kvm_t *kd) 223{ 224 struct nlist nl[] = { 225#define NLIST_START_SET_PCPU 0 226 { .n_name = "___start_" DPCPU_SETNAME }, 227#define NLIST_STOP_SET_PCPU 1 228 { .n_name = "___stop_" DPCPU_SETNAME }, 229#define NLIST_DPCPU_OFF 2 230 { .n_name = "_dpcpu_off" }, 231#define NLIST_MP_MAXCPUS 3 232 { .n_name = "_mp_maxcpus" }, 233 { .n_name = NULL }, 234 }; 235 uintptr_t *dpcpu_off_buf; 236 size_t len; 237 u_int dpcpu_maxcpus; 238 239 /* 240 * Locate and cache locations of important symbols using the internal 241 * version of _kvm_nlist, turning off initialization to avoid 242 * recursion in case of unresolveable symbols. 243 */ 244 if (_kvm_nlist(kd, nl, 0) != 0) 245 return (-1); 246 if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, 247 sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) 248 return (-1); 249 len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); 250 dpcpu_off_buf = malloc(len); 251 if (dpcpu_off_buf == NULL) 252 return (-1); 253 if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != 254 (ssize_t)len) { 255 free(dpcpu_off_buf); 256 return (-1); 257 } 258 kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; 259 kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; 260 kd->dpcpu_maxcpus = dpcpu_maxcpus; 261 kd->dpcpu_off = dpcpu_off_buf; 262 kd->dpcpu_initialized = 1; 263 (void)_kvm_dpcpu_setcpu(kd, 0, 0); 264 return (0); 265} 266 267/* 268 * Check whether the dpcpu module has been initialized sucessfully or not, 269 * initialize it if permitted. 270 */ 271int 272_kvm_dpcpu_initialized(kvm_t *kd, int intialize) 273{ 274 275 if (kd->dpcpu_initialized || !intialize) 276 return (kd->dpcpu_initialized); 277 278 (void)_kvm_dpcpu_init(kd); 279 280 return (kd->dpcpu_initialized); 281} 282 283/* 284 * Check whether the value is within the dpcpu symbol range and only if so 285 * adjust the offset relative to the current offset. 286 */ 287uintptr_t 288_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value) 289{ 290 291 if (value == 0) 292 return (value); 293 294 if (!kd->dpcpu_initialized) 295 return (value); 296 297 if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) 298 return (value); 299 300 return (kd->dpcpu_curoff + value); 301} 302 303int 304kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) 305{ 306 int ret; 307 308 if (!kd->dpcpu_initialized) { 309 ret = _kvm_dpcpu_init(kd); 310 if (ret != 0) { 311 _kvm_err(kd, kd->program, "%s: init failed", 312 __func__); 313 return (ret); 314 } 315 } 316 317 return (_kvm_dpcpu_setcpu(kd, cpu, 1)); 318} 319