kvm_pcpu.c revision 215315
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 215315 2010-11-14 20:14:25Z dim $");
40
41#include <sys/param.h>
42#include <sys/pcpu.h>
43#include <sys/sysctl.h>
44#include <kvm.h>
45#include <limits.h>
46#include <stdlib.h>
47
48#include "kvm_private.h"
49
50static struct nlist kvm_pcpu_nl[] = {
51	{ "_cpuid_to_pcpu" },
52	{ "_mp_maxcpus" },
53	{ NULL },
54};
55
56/*
57 * Kernel per-CPU data state.  We cache this stuff on the first
58 * access.
59 *
60 * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the
61 * consumer has multiple handles in flight to differently configured
62 * kernels/crashdumps.
63 */
64static void **pcpu_data;
65static int maxcpu;
66
67#define	NL_CPUID_TO_PCPU	0
68#define	NL_MP_MAXCPUS		1
69
70static int
71_kvm_pcpu_init(kvm_t *kd)
72{
73	size_t len;
74	int max;
75	void *data;
76
77	if (kvm_nlist(kd, kvm_pcpu_nl) < 0)
78		return (-1);
79	if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) {
80		_kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu");
81		return (-1);
82	}
83	if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) {
84		_kvm_err(kd, kd->program, "unable to find mp_maxcpus");
85		return (-1);
86	}
87	if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max,
88	    sizeof(max)) != sizeof(max)) {
89		_kvm_err(kd, kd->program, "cannot read mp_maxcpus");
90		return (-1);
91	}
92	len = max * sizeof(void *);
93	data = malloc(len);
94	if (data == NULL) {
95		_kvm_err(kd, kd->program, "out of memory");
96		return (-1);
97	}
98	if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) !=
99	    len) {
100		_kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array");
101		free(data);
102		return (-1);
103	}
104	pcpu_data = data;
105	maxcpu = max;
106	return (0);
107}
108
109static void
110_kvm_pcpu_clear(void)
111{
112
113	maxcpu = 0;
114	free(pcpu_data);
115	pcpu_data = NULL;
116}
117
118void *
119kvm_getpcpu(kvm_t *kd, int cpu)
120{
121	char *buf;
122	int i;
123
124	if (kd == NULL) {
125		_kvm_pcpu_clear();
126		return (NULL);
127	}
128
129	if (maxcpu == 0)
130		if (_kvm_pcpu_init(kd) < 0)
131			return ((void *)-1);
132
133	if (cpu >= maxcpu || pcpu_data[cpu] == NULL)
134		return (NULL);
135
136	buf = malloc(sizeof(struct pcpu));
137	if (buf == NULL) {
138		_kvm_err(kd, kd->program, "out of memory");
139		return ((void *)-1);
140	}
141	if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, sizeof(struct pcpu)) !=
142	    sizeof(struct pcpu)) {
143		_kvm_err(kd, kd->program, "unable to read per-CPU data");
144		free(buf);
145		return ((void *)-1);
146	}
147	return (buf);
148}
149
150int
151kvm_getmaxcpu(kvm_t *kd)
152{
153
154	if (kd == NULL) {
155		_kvm_pcpu_clear();
156		return (0);
157	}
158
159	if (maxcpu == 0)
160		if (_kvm_pcpu_init(kd) < 0)
161			return (-1);
162	return (maxcpu);
163}
164
165static int
166_kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error)
167{
168
169	if (!kd->dpcpu_initialized) {
170		if (report_error)
171			_kvm_err(kd, kd->program, "%s: not initialized",
172			    __func__);
173		return (-1);
174	}
175	if (cpu >= kd->dpcpu_maxcpus) {
176		if (report_error)
177			_kvm_err(kd, kd->program, "%s: CPU %u too big",
178			    __func__, cpu);
179		return (-1);
180	}
181	if (kd->dpcpu_off[cpu] == 0) {
182		if (report_error)
183			_kvm_err(kd, kd->program, "%s: CPU %u not found",
184			    __func__, cpu);
185		return (-1);
186	}
187	kd->dpcpu_curcpu = cpu;
188	kd->dpcpu_curoff = kd->dpcpu_off[cpu];
189	return (0);
190}
191
192/*
193 * Set up libkvm to handle dynamic per-CPU memory.
194 */
195static int
196_kvm_dpcpu_init(kvm_t *kd)
197{
198	struct nlist nl[] = {
199#define	NLIST_START_SET_PCPU	0
200		{ "___start_" DPCPU_SETNAME },
201#define	NLIST_STOP_SET_PCPU	1
202		{ "___stop_" DPCPU_SETNAME },
203#define	NLIST_DPCPU_OFF		2
204		{ "_dpcpu_off" },
205#define	NLIST_MP_MAXCPUS	3
206		{ "_mp_maxcpus" },
207		{ NULL },
208	};
209	uintptr_t *dpcpu_off_buf;
210	size_t len;
211	u_int dpcpu_maxcpus;
212
213	/*
214	 * Locate and cache locations of important symbols using the internal
215	 * version of _kvm_nlist, turning off initialization to avoid
216	 * recursion in case of unresolveable symbols.
217	 */
218	if (_kvm_nlist(kd, nl, 0) != 0)
219		return (-1);
220	if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus,
221	    sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus))
222		return (-1);
223	len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf);
224	dpcpu_off_buf = malloc(len);
225	if (dpcpu_off_buf == NULL)
226		return (-1);
227	if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) !=
228	    len) {
229		free(dpcpu_off_buf);
230		return (-1);
231	}
232	kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value;
233	kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value;
234	kd->dpcpu_maxcpus = dpcpu_maxcpus;
235	kd->dpcpu_off = dpcpu_off_buf;
236	kd->dpcpu_initialized = 1;
237	(void)_kvm_dpcpu_setcpu(kd, 0, 0);
238	return (0);
239}
240
241/*
242 * Check whether the dpcpu module has been initialized sucessfully or not,
243 * initialize it if permitted.
244 */
245int
246_kvm_dpcpu_initialized(kvm_t *kd, int intialize)
247{
248
249	if (kd->dpcpu_initialized || !intialize)
250		return (kd->dpcpu_initialized);
251
252	(void)_kvm_dpcpu_init(kd);
253
254	return (kd->dpcpu_initialized);
255}
256
257/*
258 * Check whether the value is within the dpcpu symbol range and only if so
259 * adjust the offset relative to the current offset.
260 */
261uintptr_t
262_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value)
263{
264
265	if (value == 0)
266		return (value);
267
268	if (!kd->dpcpu_initialized)
269		return (value);
270
271	if (value < kd->dpcpu_start || value >= kd->dpcpu_stop)
272		return (value);
273
274	return (kd->dpcpu_curoff + value);
275}
276
277int
278kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu)
279{
280	int ret;
281
282	if (!kd->dpcpu_initialized) {
283		ret = _kvm_dpcpu_init(kd);
284		if (ret != 0) {
285			_kvm_err(kd, kd->program, "%s: init failed",
286			    __func__);
287			return (ret);
288		}
289	}
290
291	return (_kvm_dpcpu_setcpu(kd, cpu, 1));
292}
293