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