kvm_pcpu.c revision 223758
1193323Sed/*-
2193323Sed * Copyright (c) 2010 Juniper Networks, Inc.
3193323Sed * Copyright (c) 2009 Robert N. M. Watson
4193323Sed * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org>
5193323Sed * Copyright (c) 2008 Yahoo!, Inc.
6193323Sed * All rights reserved.
7193323Sed *
8193323Sed * Written by: John Baldwin <jhb@FreeBSD.org>
9193323Sed *
10193323Sed * This software was developed by Robert N. M. Watson under contract
11193323Sed * to Juniper Networks, Inc.
12193323Sed *
13193323Sed * Redistribution and use in source and binary forms, with or without
14193323Sed * modification, are permitted provided that the following conditions
15193323Sed * are met:
16193323Sed * 1. Redistributions of source code must retain the above copyright
17198090Srdivacky *    notice, this list of conditions and the following disclaimer.
18193323Sed * 2. Redistributions in binary form must reproduce the above copyright
19193323Sed *    notice, this list of conditions and the following disclaimer in the
20193323Sed *    documentation and/or other materials provided with the distribution.
21193323Sed * 3. Neither the name of the author nor the names of any co-contributors
22193323Sed *    may be used to endorse or promote products derived from this software
23193323Sed *    without specific prior written permission.
24193323Sed *
25193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28193323Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32198090Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35193323Sed * SUCH DAMAGE.
36193323Sed */
37221337Sdim
38221337Sdim#include <sys/cdefs.h>
39193323Sed__FBSDID("$FreeBSD: head/lib/libkvm/kvm_pcpu.c 223758 2011-07-04 12:04:52Z attilio $");
40193323Sed
41221337Sdim#include <sys/param.h>
42221337Sdim#include <sys/pcpu.h>
43198090Srdivacky#include <sys/sysctl.h>
44198090Srdivacky#include <kvm.h>
45193323Sed#include <limits.h>
46212793Sdim#include <stdlib.h>
47193323Sed
48193323Sed#include "kvm_private.h"
49193323Sed
50193323Sedstatic struct nlist kvm_pcpu_nl[] = {
51212793Sdim	{ .n_name = "_cpuid_to_pcpu" },
52212793Sdim	{ .n_name = "_mp_maxcpus" },
53207618Srdivacky	{ .n_name = NULL },
54193323Sed};
55193323Sed
56193323Sed/*
57193323Sed * Kernel per-CPU data state.  We cache this stuff on the first
58193323Sed * access.
59193323Sed *
60193323Sed * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the
61212793Sdim * consumer has multiple handles in flight to differently configured
62212793Sdim * kernels/crashdumps.
63207618Srdivacky */
64193323Sedstatic void **pcpu_data;
65193323Sedstatic int maxcpu;
66198090Srdivacky
67198090Srdivacky#define	NL_CPUID_TO_PCPU	0
68193323Sed#define	NL_MP_MAXCPUS		1
69193323Sed
70212793Sdimstatic int
71212793Sdim_kvm_pcpu_init(kvm_t *kd)
72198090Srdivacky{
73198090Srdivacky	size_t len;
74193323Sed	int max;
75212793Sdim	void *data;
76193323Sed
77193323Sed	if (kvm_nlist(kd, kvm_pcpu_nl) < 0)
78221337Sdim		return (-1);
79193323Sed	if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) {
80212793Sdim		_kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu");
81212793Sdim		return (-1);
82207618Srdivacky	}
83207618Srdivacky	if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) {
84207618Srdivacky		_kvm_err(kd, kd->program, "unable to find mp_maxcpus");
85198090Srdivacky		return (-1);
86193323Sed	}
87198090Srdivacky	if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max,
88198090Srdivacky	    sizeof(max)) != sizeof(max)) {
89193323Sed		_kvm_err(kd, kd->program, "cannot read mp_maxcpus");
90193323Sed		return (-1);
91193323Sed	}
92193323Sed	len = max * sizeof(void *);
93198090Srdivacky	data = malloc(len);
94193323Sed	if (data == NULL) {
95193323Sed		_kvm_err(kd, kd->program, "out of memory");
96193323Sed		return (-1);
97193323Sed	}
98193323Sed	if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) !=
99198090Srdivacky	   (ssize_t)len) {
100198090Srdivacky		_kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array");
101193323Sed		free(data);
102193323Sed		return (-1);
103193323Sed	}
104193323Sed	pcpu_data = data;
105193323Sed	maxcpu = max;
106193323Sed	return (0);
107193323Sed}
108212793Sdim
109212793Sdimstatic void
110198090Srdivacky_kvm_pcpu_clear(void)
111198090Srdivacky{
112193323Sed
113212793Sdim	maxcpu = 0;
114193323Sed	free(pcpu_data);
115193323Sed	pcpu_data = NULL;
116193323Sed}
117193323Sed
118212793Sdimvoid *
119207618Srdivackykvm_getpcpu(kvm_t *kd, int cpu)
120207618Srdivacky{
121207618Srdivacky	char *buf;
122198090Srdivacky
123193323Sed	if (kd == NULL) {
124193323Sed		_kvm_pcpu_clear();
125193323Sed		return (NULL);
126198090Srdivacky	}
127193323Sed
128193323Sed	if (maxcpu == 0)
129193323Sed		if (_kvm_pcpu_init(kd) < 0)
130193323Sed			return ((void *)-1);
131208599Srdivacky
132208599Srdivacky	if (cpu >= maxcpu || pcpu_data[cpu] == NULL)
133193323Sed		return (NULL);
134198090Srdivacky
135198090Srdivacky	buf = malloc(sizeof(struct pcpu));
136193323Sed	if (buf == NULL) {
137193323Sed		_kvm_err(kd, kd->program, "out of memory");
138193323Sed		return ((void *)-1);
139193323Sed	}
140193323Sed	if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf,
141193323Sed	    sizeof(struct pcpu)) != sizeof(struct pcpu)) {
142193323Sed		_kvm_err(kd, kd->program, "unable to read per-CPU data");
143193323Sed		free(buf);
144198090Srdivacky		return ((void *)-1);
145193323Sed	}
146193323Sed	return (buf);
147193323Sed}
148208599Srdivacky
149212793Sdimint
150212793Sdimkvm_getmaxcpu(kvm_t *kd)
151198090Srdivacky{
152198090Srdivacky
153193323Sed	if (kd == NULL) {
154212793Sdim		_kvm_pcpu_clear();
155193323Sed		return (0);
156193323Sed	}
157193323Sed
158193323Sed	if (maxcpu == 0)
159212793Sdim		if (_kvm_pcpu_init(kd) < 0)
160212793Sdim			return (-1);
161207618Srdivacky	return (maxcpu);
162207618Srdivacky}
163207618Srdivacky
164198090Srdivackystatic int
165193323Sed_kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error)
166193323Sed{
167193323Sed
168193323Sed	if (!kd->dpcpu_initialized) {
169198090Srdivacky		if (report_error)
170208599Srdivacky			_kvm_err(kd, kd->program, "%s: not initialized",
171208599Srdivacky			    __func__);
172193323Sed		return (-1);
173193323Sed	}
174193323Sed	if (cpu >= kd->dpcpu_maxcpus) {
175193323Sed		if (report_error)
176193323Sed			_kvm_err(kd, kd->program, "%s: CPU %u too big",
177193323Sed			    __func__, cpu);
178207618Srdivacky		return (-1);
179193323Sed	}
180193323Sed	if (kd->dpcpu_off[cpu] == 0) {
181207618Srdivacky		if (report_error)
182207618Srdivacky			_kvm_err(kd, kd->program, "%s: CPU %u not found",
183193323Sed			    __func__, cpu);
184193323Sed		return (-1);
185193323Sed	}
186207618Srdivacky	kd->dpcpu_curcpu = cpu;
187207618Srdivacky	kd->dpcpu_curoff = kd->dpcpu_off[cpu];
188207618Srdivacky	return (0);
189207618Srdivacky}
190207618Srdivacky
191207618Srdivacky/*
192207618Srdivacky * Set up libkvm to handle dynamic per-CPU memory.
193207618Srdivacky */
194207618Srdivackystatic int
195207618Srdivacky_kvm_dpcpu_init(kvm_t *kd)
196207618Srdivacky{
197207618Srdivacky	struct nlist nl[] = {
198207618Srdivacky#define	NLIST_START_SET_PCPU	0
199207618Srdivacky		{ .n_name = "___start_" DPCPU_SETNAME },
200207618Srdivacky#define	NLIST_STOP_SET_PCPU	1
201207618Srdivacky		{ .n_name = "___stop_" DPCPU_SETNAME },
202193323Sed#define	NLIST_DPCPU_OFF		2
203193323Sed		{ .n_name = "_dpcpu_off" },
204193323Sed#define	NLIST_MP_MAXCPUS	3
205212793Sdim		{ .n_name = "_mp_maxcpus" },
206193323Sed		{ .n_name = NULL },
207193323Sed	};
208193323Sed	uintptr_t *dpcpu_off_buf;
209193323Sed	size_t len;
210212793Sdim	u_int dpcpu_maxcpus;
211212793Sdim
212212793Sdim	/*
213212793Sdim	 * Locate and cache locations of important symbols using the internal
214212793Sdim	 * version of _kvm_nlist, turning off initialization to avoid
215193323Sed	 * recursion in case of unresolveable symbols.
216212793Sdim	 */
217212793Sdim	if (_kvm_nlist(kd, nl, 0) != 0)
218212793Sdim		return (-1);
219193323Sed	if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus,
220193323Sed	    sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus))
221193323Sed		return (-1);
222193323Sed	len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf);
223193323Sed	dpcpu_off_buf = malloc(len);
224193323Sed	if (dpcpu_off_buf == NULL)
225226584Sdim		return (-1);
226198090Srdivacky	if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) !=
227198090Srdivacky	    (ssize_t)len) {
228193323Sed		free(dpcpu_off_buf);
229193323Sed		return (-1);
230193323Sed	}
231193323Sed	kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value;
232212793Sdim	kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value;
233212793Sdim	kd->dpcpu_maxcpus = dpcpu_maxcpus;
234207618Srdivacky	kd->dpcpu_off = dpcpu_off_buf;
235212793Sdim	kd->dpcpu_initialized = 1;
236212793Sdim	(void)_kvm_dpcpu_setcpu(kd, 0, 0);
237212793Sdim	return (0);
238207618Srdivacky}
239212793Sdim
240193323Sed/*
241193323Sed * Check whether the dpcpu module has been initialized sucessfully or not,
242193323Sed * initialize it if permitted.
243193323Sed */
244193323Sedint
245193323Sed_kvm_dpcpu_initialized(kvm_t *kd, int intialize)
246212793Sdim{
247212793Sdim
248193323Sed	if (kd->dpcpu_initialized || !intialize)
249193323Sed		return (kd->dpcpu_initialized);
250198090Srdivacky
251198090Srdivacky	(void)_kvm_dpcpu_init(kd);
252198090Srdivacky
253198090Srdivacky	return (kd->dpcpu_initialized);
254193323Sed}
255198090Srdivacky
256193323Sed/*
257212793Sdim * Check whether the value is within the dpcpu symbol range and only if so
258212793Sdim * adjust the offset relative to the current offset.
259212793Sdim */
260212793Sdimuintptr_t
261212793Sdim_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value)
262212793Sdim{
263212793Sdim
264218885Sdim	if (value == 0)
265212793Sdim		return (value);
266212793Sdim
267212793Sdim	if (!kd->dpcpu_initialized)
268212793Sdim		return (value);
269212793Sdim
270212793Sdim	if (value < kd->dpcpu_start || value >= kd->dpcpu_stop)
271212793Sdim		return (value);
272212793Sdim
273212793Sdim	return (kd->dpcpu_curoff + value);
274193323Sed}
275212793Sdim
276210006Srdivackyint
277212793Sdimkvm_dpcpu_setcpu(kvm_t *kd, u_int cpu)
278210006Srdivacky{
279193323Sed	int ret;
280193323Sed
281212793Sdim	if (!kd->dpcpu_initialized) {
282212793Sdim		ret = _kvm_dpcpu_init(kd);
283212793Sdim		if (ret != 0) {
284212793Sdim			_kvm_err(kd, kd->program, "%s: init failed",
285212793Sdim			    __func__);
286193323Sed			return (ret);
287193323Sed		}
288202878Srdivacky	}
289193323Sed
290193323Sed	return (_kvm_dpcpu_setcpu(kd, cpu, 1));
291193323Sed}
292202878Srdivacky