subr_cpu.c revision 1.5 1 /* $NetBSD: subr_cpu.c,v 1.5 2020/01/05 20:27:43 ad Exp $ */
2
3 /*-
4 * Copyright (c) 2007, 2008, 2009, 2010, 2012, 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*-
33 * Copyright (c)2007 YAMAMOTO Takashi,
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58 /*
59 * CPU related routines shared with rump.
60 */
61
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: subr_cpu.c,v 1.5 2020/01/05 20:27:43 ad Exp $");
64
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/sched.h>
68 #include <sys/conf.h>
69 #include <sys/cpu.h>
70 #include <sys/proc.h>
71 #include <sys/kernel.h>
72 #include <sys/kmem.h>
73
74 static void cpu_topology_fake1(struct cpu_info *);
75
76 kmutex_t cpu_lock __cacheline_aligned;
77 int ncpu __read_mostly;
78 int ncpuonline __read_mostly;
79 bool mp_online __read_mostly;
80 static bool cpu_topology_present __read_mostly;
81 int64_t cpu_counts[CPU_COUNT_MAX];
82
83 /* An array of CPUs. There are ncpu entries. */
84 struct cpu_info **cpu_infos __read_mostly;
85
86 /* Note: set on mi_cpu_attach() and idle_loop(). */
87 kcpuset_t * kcpuset_attached __read_mostly = NULL;
88 kcpuset_t * kcpuset_running __read_mostly = NULL;
89
90 static char cpu_model[128];
91
92 /*
93 * mi_cpu_init: early initialisation of MI CPU related structures.
94 *
95 * Note: may not block and memory allocator is not yet available.
96 */
97 void
98 mi_cpu_init(void)
99 {
100 struct cpu_info *ci;
101
102 mutex_init(&cpu_lock, MUTEX_DEFAULT, IPL_NONE);
103
104 kcpuset_create(&kcpuset_attached, true);
105 kcpuset_create(&kcpuset_running, true);
106 kcpuset_set(kcpuset_running, 0);
107
108 ci = curcpu();
109 cpu_topology_fake1(ci);
110 }
111
112 int
113 cpu_setmodel(const char *fmt, ...)
114 {
115 int len;
116 va_list ap;
117
118 va_start(ap, fmt);
119 len = vsnprintf(cpu_model, sizeof(cpu_model), fmt, ap);
120 va_end(ap);
121 return len;
122 }
123
124 const char *
125 cpu_getmodel(void)
126 {
127 return cpu_model;
128 }
129
130 bool
131 cpu_softintr_p(void)
132 {
133
134 return (curlwp->l_pflag & LP_INTR) != 0;
135 }
136
137 /*
138 * Collect CPU topology information as each CPU is attached. This can be
139 * called early during boot, so we need to be careful what we do.
140 */
141 void
142 cpu_topology_set(struct cpu_info *ci, u_int package_id, u_int core_id,
143 u_int smt_id, u_int numa_id)
144 {
145 enum cpu_rel rel;
146
147 cpu_topology_present = true;
148 ci->ci_package_id = package_id;
149 ci->ci_core_id = core_id;
150 ci->ci_smt_id = smt_id;
151 ci->ci_numa_id = numa_id;
152 for (rel = 0; rel < __arraycount(ci->ci_sibling); rel++) {
153 ci->ci_sibling[rel] = ci;
154 ci->ci_nsibling[rel] = 1;
155 }
156 }
157
158 /*
159 * Link a CPU into the given circular list.
160 */
161 static void
162 cpu_topology_link(struct cpu_info *ci, struct cpu_info *ci2, enum cpu_rel rel)
163 {
164 struct cpu_info *ci3;
165
166 /* Walk to the end of the existing circular list and append. */
167 for (ci3 = ci2;; ci3 = ci3->ci_sibling[rel]) {
168 ci3->ci_nsibling[rel]++;
169 if (ci3->ci_sibling[rel] == ci2) {
170 break;
171 }
172 }
173 ci->ci_sibling[rel] = ci2;
174 ci3->ci_sibling[rel] = ci;
175 ci->ci_nsibling[rel] = ci3->ci_nsibling[rel];
176 }
177
178 /*
179 * Print out the topology lists.
180 */
181 static void
182 cpu_topology_dump(void)
183 {
184 #if DEBUG
185 CPU_INFO_ITERATOR cii;
186 struct cpu_info *ci, *ci2;
187 const char *names[] = { "core", "package", "peer", "smt" };
188 enum cpu_rel rel;
189 int i;
190
191 for (CPU_INFO_FOREACH(cii, ci)) {
192 for (rel = 0; rel < __arraycount(ci->ci_sibling); rel++) {
193 printf("%s has %d %s siblings:", cpu_name(ci),
194 ci->ci_nsibling[rel], names[rel]);
195 ci2 = ci->ci_sibling[rel];
196 i = 0;
197 do {
198 printf(" %s", cpu_name(ci2));
199 ci2 = ci2->ci_sibling[rel];
200 } while (++i < 64 && ci2 != ci->ci_sibling[rel]);
201 if (i == 64) {
202 printf(" GAVE UP");
203 }
204 printf("\n");
205 }
206 }
207 #endif /* DEBUG */
208 }
209
210 /*
211 * Fake up topology info if we have none, or if what we got was bogus.
212 * Used early in boot, and by cpu_topology_fake().
213 */
214 static void
215 cpu_topology_fake1(struct cpu_info *ci)
216 {
217 enum cpu_rel rel;
218
219 for (rel = 0; rel < __arraycount(ci->ci_sibling); rel++) {
220 ci->ci_sibling[rel] = ci;
221 ci->ci_nsibling[rel] = 1;
222 }
223 if (!cpu_topology_present) {
224 ci->ci_package_id = cpu_index(ci);
225 }
226 ci->ci_smt_primary = ci;
227 ci->ci_schedstate.spc_flags |= SPCF_SMTPRIMARY;
228 cpu_topology_dump();
229 }
230
231 /*
232 * Fake up topology info if we have none, or if what we got was bogus.
233 * Don't override ci_package_id, etc, if cpu_topology_present is set.
234 * MD code also uses these.
235 */
236 static void
237 cpu_topology_fake(void)
238 {
239 CPU_INFO_ITERATOR cii;
240 struct cpu_info *ci;
241
242 for (CPU_INFO_FOREACH(cii, ci)) {
243 cpu_topology_fake1(ci);
244 }
245 cpu_topology_dump();
246 }
247
248 /*
249 * Fix up basic CPU topology info. Right now that means attach each CPU to
250 * circular lists of its siblings in the same core, and in the same package.
251 */
252 void
253 cpu_topology_init(void)
254 {
255 CPU_INFO_ITERATOR cii, cii2;
256 struct cpu_info *ci, *ci2, *ci3;
257 u_int ncore, npackage, npeer, minsmt;
258 bool symmetric;
259
260 if (!cpu_topology_present) {
261 cpu_topology_fake();
262 return;
263 }
264
265 /* Find siblings in same core and package. */
266 for (CPU_INFO_FOREACH(cii, ci)) {
267 for (CPU_INFO_FOREACH(cii2, ci2)) {
268 /* Avoid bad things happening. */
269 if (ci2->ci_package_id == ci->ci_package_id &&
270 ci2->ci_core_id == ci->ci_core_id &&
271 ci2->ci_smt_id == ci->ci_smt_id &&
272 ci2 != ci) {
273 printf("cpu_topology_init: info bogus, "
274 "faking it\n");
275 cpu_topology_fake();
276 return;
277 }
278 if (ci2 == ci ||
279 ci2->ci_package_id != ci->ci_package_id) {
280 continue;
281 }
282 /* Find CPUs in the same core. */
283 if (ci->ci_nsibling[CPUREL_CORE] == 1 &&
284 ci->ci_core_id == ci2->ci_core_id) {
285 cpu_topology_link(ci, ci2, CPUREL_CORE);
286 }
287 /* Find CPUs in the same package. */
288 if (ci->ci_nsibling[CPUREL_PACKAGE] == 1) {
289 cpu_topology_link(ci, ci2, CPUREL_PACKAGE);
290 }
291 if (ci->ci_nsibling[CPUREL_CORE] > 1 &&
292 ci->ci_nsibling[CPUREL_PACKAGE] > 1) {
293 break;
294 }
295 }
296 }
297
298 /* Find peers in other packages, and peer SMTs in same package. */
299 for (CPU_INFO_FOREACH(cii, ci)) {
300 if (ci->ci_nsibling[CPUREL_PEER] <= 1) {
301 for (CPU_INFO_FOREACH(cii2, ci2)) {
302 if (ci != ci2 &&
303 ci->ci_package_id != ci2->ci_package_id &&
304 ci->ci_core_id == ci2->ci_core_id &&
305 ci->ci_smt_id == ci2->ci_smt_id) {
306 cpu_topology_link(ci, ci2,
307 CPUREL_PEER);
308 break;
309 }
310 }
311 }
312 if (ci->ci_nsibling[CPUREL_SMT] <= 1) {
313 for (CPU_INFO_FOREACH(cii2, ci2)) {
314 if (ci != ci2 &&
315 ci->ci_package_id == ci2->ci_package_id &&
316 ci->ci_core_id != ci2->ci_core_id &&
317 ci->ci_smt_id == ci2->ci_smt_id) {
318 cpu_topology_link(ci, ci2,
319 CPUREL_SMT);
320 break;
321 }
322 }
323 }
324 }
325
326 /* Determine whether the topology is bogus/symmetric. */
327 npackage = curcpu()->ci_nsibling[CPUREL_PACKAGE];
328 ncore = curcpu()->ci_nsibling[CPUREL_CORE];
329 npeer = curcpu()->ci_nsibling[CPUREL_PEER];
330 symmetric = true;
331 for (CPU_INFO_FOREACH(cii, ci)) {
332 if (npackage != ci->ci_nsibling[CPUREL_PACKAGE] ||
333 ncore != ci->ci_nsibling[CPUREL_CORE] ||
334 npeer != ci->ci_nsibling[CPUREL_PEER]) {
335 symmetric = false;
336 }
337 }
338 cpu_topology_dump();
339 if (symmetric == false) {
340 printf("cpu_topology_init: not symmetric, faking it\n");
341 cpu_topology_fake();
342 return;
343 }
344
345 /* Identify SMT primary in each core. */
346 for (CPU_INFO_FOREACH(cii, ci)) {
347 ci2 = ci3 = ci;
348 minsmt = ci->ci_smt_id;
349 do {
350 if (ci2->ci_smt_id < minsmt) {
351 ci3 = ci2;
352 minsmt = ci2->ci_smt_id;
353 }
354 ci2 = ci2->ci_sibling[CPUREL_CORE];
355 } while (ci2 != ci);
356
357 /*
358 * Mark the SMT primary, and walk back over the list
359 * pointing secondaries to the primary.
360 */
361 ci3->ci_schedstate.spc_flags |= SPCF_SMTPRIMARY;
362 ci2 = ci;
363 do {
364 ci2->ci_smt_primary = ci3;
365 ci2 = ci2->ci_sibling[CPUREL_CORE];
366 } while (ci2 != ci);
367 }
368 }
369
370 /*
371 * Adjust one count, for a counter that's NOT updated from interrupt
372 * context. Hardly worth making an inline due to preemption stuff.
373 */
374 void
375 cpu_count(enum cpu_count idx, int64_t delta)
376 {
377 lwp_t *l = curlwp;
378 KPREEMPT_DISABLE(l);
379 l->l_cpu->ci_counts[idx] += delta;
380 KPREEMPT_ENABLE(l);
381 }
382
383 /*
384 * Fetch fresh sum total for all counts. Expensive - don't call often.
385 */
386 void
387 cpu_count_sync_all(void)
388 {
389 CPU_INFO_ITERATOR cii;
390 struct cpu_info *ci;
391 int64_t sum[CPU_COUNT_MAX], *ptr;
392 enum cpu_count i;
393 int s;
394
395 KASSERT(sizeof(ci->ci_counts) == sizeof(cpu_counts));
396
397 if (__predict_true(mp_online)) {
398 memset(sum, 0, sizeof(sum));
399 /*
400 * We want this to be reasonably quick, so any value we get
401 * isn't totally out of whack, so don't let the current LWP
402 * get preempted.
403 */
404 s = splvm();
405 curcpu()->ci_counts[CPU_COUNT_SYNC_ALL]++;
406 for (CPU_INFO_FOREACH(cii, ci)) {
407 ptr = ci->ci_counts;
408 for (i = 0; i < CPU_COUNT_MAX; i += 8) {
409 sum[i+0] += ptr[i+0];
410 sum[i+1] += ptr[i+1];
411 sum[i+2] += ptr[i+2];
412 sum[i+3] += ptr[i+3];
413 sum[i+4] += ptr[i+4];
414 sum[i+5] += ptr[i+5];
415 sum[i+6] += ptr[i+6];
416 sum[i+7] += ptr[i+7];
417 }
418 KASSERT(i == CPU_COUNT_MAX);
419 }
420 memcpy(cpu_counts, sum, sizeof(cpu_counts));
421 splx(s);
422 } else {
423 memcpy(cpu_counts, curcpu()->ci_counts, sizeof(cpu_counts));
424 }
425 }
426
427 /*
428 * Fetch a fresh sum total for one single count. Expensive - don't call often.
429 */
430 int64_t
431 cpu_count_sync(enum cpu_count count)
432 {
433 CPU_INFO_ITERATOR cii;
434 struct cpu_info *ci;
435 int64_t sum;
436 int s;
437
438 if (__predict_true(mp_online)) {
439 s = splvm();
440 curcpu()->ci_counts[CPU_COUNT_SYNC_ONE]++;
441 sum = 0;
442 for (CPU_INFO_FOREACH(cii, ci)) {
443 sum += ci->ci_counts[count];
444 }
445 splx(s);
446 } else {
447 /* XXX Early boot, iterator might not be available. */
448 sum = curcpu()->ci_counts[count];
449 }
450 return cpu_counts[count] = sum;
451 }
452