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