]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/cpu_topology.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / i386 / cpu_topology.c
CommitLineData
2d21ac55 1/*
d1ecb069 2 * Copyright (c) 2007-2010 Apple Inc. All rights reserved.
2d21ac55
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
0a7de745 25 *
2d21ac55
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <mach/machine.h>
30#include <mach/processor.h>
31#include <kern/kalloc.h>
32#include <i386/cpu_affinity.h>
33#include <i386/cpu_topology.h>
2d21ac55
A
34#include <i386/cpu_threads.h>
35#include <i386/machine_cpu.h>
fe8ab488 36#include <i386/bit_routines.h>
b0d623f7 37#include <i386/cpu_data.h>
593a1d5f 38#include <i386/lapic.h>
b0d623f7 39#include <i386/machine_routines.h>
a39ff7e2 40#include <stddef.h>
2d21ac55 41
2d21ac55 42__private_extern__ void qsort(
0a7de745
A
43 void * array,
44 size_t nmembers,
45 size_t member_size,
46 int (*)(const void *, const void *));
2d21ac55
A
47
48static int lapicid_cmp(const void *x, const void *y);
49static x86_affinity_set_t *find_cache_affinity(x86_cpu_cache_t *L2_cachep);
50
0a7de745
A
51x86_affinity_set_t *x86_affinities = NULL;
52static int x86_affinity_count = 0;
2d21ac55 53
5c9f4661 54extern cpu_data_t cpshadows[];
0a7de745
A
55
56#if DEVELOPMENT || DEBUG
57void iotrace_init(int ncpus);
94ff46dc 58void traptrace_init(int ncpus);
0a7de745
A
59#endif /* DEVELOPMENT || DEBUG */
60
61
5c9f4661
A
62/* Re-sort double-mapped CPU data shadows after topology discovery sorts the
63 * primary CPU data structures by physical/APIC CPU ID.
64 */
0a7de745
A
65static void
66cpu_shadow_sort(int ncpus)
67{
5c9f4661 68 for (int i = 0; i < ncpus; i++) {
0a7de745
A
69 cpu_data_t *cpup = cpu_datap(i);
70 ptrdiff_t coff = cpup - cpu_datap(0);
5c9f4661
A
71
72 cpup->cd_shadow = &cpshadows[coff];
73 }
74}
75
2d21ac55 76/*
a39ff7e2 77 * cpu_topology_sort() is called after all processors have been registered but
0a7de745 78 * before any non-boot processor is started. We establish canonical logical
a39ff7e2
A
79 * processor numbering - logical cpus must be contiguous, zero-based and
80 * assigned in physical (local apic id) order. This step is required because
81 * the discovery/registration order is non-deterministic - cores are registered
82 * in differing orders over boots. Enforcing canonical numbering simplifies
83 * identification of processors.
84 */
2d21ac55 85void
b0d623f7 86cpu_topology_sort(int ncpus)
2d21ac55 87{
0a7de745
A
88 int i;
89 boolean_t istate;
90 processor_t lprim = NULL;
2d21ac55
A
91
92 assert(machine_info.physical_cpu == 1);
93 assert(machine_info.logical_cpu == 1);
94 assert(master_cpu == 0);
95 assert(cpu_number() == 0);
96 assert(cpu_datap(0)->cpu_number == 0);
b0d623f7 97
f427ee49
A
98 uint32_t cpus_per_pset = 0;
99
100#if DEVELOPMENT || DEBUG
101 PE_parse_boot_argn("cpus_per_pset", &cpus_per_pset, sizeof(cpus_per_pset));
102#endif
103
2d21ac55
A
104 /* Lights out for this */
105 istate = ml_set_interrupts_enabled(FALSE);
106
7ddcb079
A
107 if (topo_dbg) {
108 TOPO_DBG("cpu_topology_start() %d cpu%s registered\n",
0a7de745 109 ncpus, (ncpus > 1) ? "s" : "");
7ddcb079 110 for (i = 0; i < ncpus; i++) {
0a7de745 111 cpu_data_t *cpup = cpu_datap(i);
7ddcb079 112 TOPO_DBG("\tcpu_data[%d]:%p local apic 0x%x\n",
0a7de745 113 i, (void *) cpup, cpup->cpu_phys_number);
7ddcb079 114 }
2d21ac55 115 }
7ddcb079 116
2d21ac55
A
117 /*
118 * Re-order the cpu_data_ptr vector sorting by physical id.
119 * Skip the boot processor, it's required to be correct.
120 */
121 if (ncpus > 1) {
122 qsort((void *) &cpu_data_ptr[1],
0a7de745
A
123 ncpus - 1,
124 sizeof(cpu_data_t *),
125 lapicid_cmp);
2d21ac55 126 }
7ddcb079
A
127 if (topo_dbg) {
128 TOPO_DBG("cpu_topology_start() after sorting:\n");
129 for (i = 0; i < ncpus; i++) {
0a7de745 130 cpu_data_t *cpup = cpu_datap(i);
7ddcb079 131 TOPO_DBG("\tcpu_data[%d]:%p local apic 0x%x\n",
0a7de745 132 i, (void *) cpup, cpup->cpu_phys_number);
7ddcb079 133 }
2d21ac55 134 }
2d21ac55
A
135
136 /*
39236c6e 137 * Finalize logical numbers and map kept by the lapic code.
2d21ac55 138 */
39236c6e 139 for (i = 0; i < ncpus; i++) {
0a7de745 140 cpu_data_t *cpup = cpu_datap(i);
2d21ac55
A
141
142 if (cpup->cpu_number != i) {
b0d623f7 143 kprintf("cpu_datap(%d):%p local apic id 0x%x "
0a7de745
A
144 "remapped from %d\n",
145 i, cpup, cpup->cpu_phys_number,
146 cpup->cpu_number);
2d21ac55
A
147 }
148 cpup->cpu_number = i;
2d21ac55 149 lapic_cpu_map(cpup->cpu_phys_number, i);
39236c6e 150 x86_set_logical_topology(&cpup->lcpu, cpup->cpu_phys_number, i);
2d21ac55
A
151 }
152
5c9f4661 153 cpu_shadow_sort(ncpus);
39236c6e 154 x86_validate_topology();
593a1d5f 155
2d21ac55 156 ml_set_interrupts_enabled(istate);
7ddcb079 157 TOPO_DBG("cpu_topology_start() LLC is L%d\n", topoParms.LLCDepth + 1);
2d21ac55 158
0a7de745
A
159#if DEVELOPMENT || DEBUG
160 iotrace_init(ncpus);
94ff46dc 161 traptrace_init(ncpus);
0a7de745
A
162#endif /* DEVELOPMENT || DEBUG */
163
d1ecb069
A
164 /*
165 * Let the CPU Power Management know that the topology is stable.
166 */
167 topoParms.stable = TRUE;
168 pmCPUStateInit();
169
2d21ac55
A
170 /*
171 * Iterate over all logical cpus finding or creating the affinity set
593a1d5f 172 * for their LLC cache. Each affinity set possesses a processor set
2d21ac55
A
173 * into which each logical processor is added.
174 */
f427ee49
A
175 TOPO_DBG("cpu_topology_start() creating affinity sets:ncpus=%d max_cpus=%d\n", ncpus, machine_info.max_cpus);
176 for (i = 0; i < machine_info.max_cpus; i++) {
0a7de745
A
177 cpu_data_t *cpup = cpu_datap(i);
178 x86_lcpu_t *lcpup = cpu_to_lcpu(i);
179 x86_cpu_cache_t *LLC_cachep;
180 x86_affinity_set_t *aset;
2d21ac55 181
593a1d5f
A
182 LLC_cachep = lcpup->caches[topoParms.LLCDepth];
183 assert(LLC_cachep->type == CPU_CACHE_TYPE_UNIF);
0a7de745 184 aset = find_cache_affinity(LLC_cachep);
f427ee49 185 if ((aset == NULL) || ((cpus_per_pset != 0) && (i % cpus_per_pset) == 0)) {
2d21ac55 186 aset = (x86_affinity_set_t *) kalloc(sizeof(*aset));
0a7de745 187 if (aset == NULL) {
2d21ac55 188 panic("cpu_topology_start() failed aset alloc");
0a7de745 189 }
2d21ac55
A
190 aset->next = x86_affinities;
191 x86_affinities = aset;
192 aset->num = x86_affinity_count++;
593a1d5f 193 aset->cache = LLC_cachep;
2d21ac55 194 aset->pset = (i == master_cpu) ?
0a7de745
A
195 processor_pset(master_processor) :
196 pset_create(pset_node_root());
197 if (aset->pset == PROCESSOR_SET_NULL) {
2d21ac55 198 panic("cpu_topology_start: pset_create");
0a7de745 199 }
7ddcb079 200 TOPO_DBG("\tnew set %p(%d) pset %p for cache %p\n",
0a7de745 201 aset, aset->num, aset->pset, aset->cache);
2d21ac55
A
202 }
203
7ddcb079 204 TOPO_DBG("\tprocessor_init set %p(%d) lcpup %p(%d) cpu %p processor %p\n",
0a7de745 205 aset, aset->num, lcpup, lcpup->cpu_num, cpup, cpup->cpu_processor);
2d21ac55 206
0a7de745 207 if (i != master_cpu) {
2d21ac55 208 processor_init(cpup->cpu_processor, i, aset->pset);
0a7de745 209 }
b0d623f7
A
210
211 if (lcpup->core->num_lcpus > 1) {
0a7de745 212 if (lcpup->lnum == 0) {
b0d623f7 213 lprim = cpup->cpu_processor;
0a7de745 214 }
b0d623f7 215
fe8ab488 216 processor_set_primary(cpup->cpu_processor, lprim);
b0d623f7 217 }
2d21ac55 218 }
f427ee49
A
219
220 if (machine_info.max_cpus < machine_info.logical_cpu_max) {
221 /* boot-args cpus=n is set, so adjust max numbers to match */
222 int logical_max = machine_info.max_cpus;
223 int physical_max = logical_max;
224 if (machine_info.logical_cpu_max != machine_info.physical_cpu_max) {
225 physical_max = (logical_max + 1) / 2;
226 }
227 machine_info.logical_cpu_max = logical_max;
228 machine_info.physical_cpu_max = physical_max;
229 }
b0d623f7 230}
2d21ac55 231
b0d623f7
A
232/* We got a request to start a CPU. Check that this CPU is within the
233 * max cpu limit set before we do.
234 */
235kern_return_t
236cpu_topology_start_cpu( int cpunum )
237{
0a7de745
A
238 int ncpus = machine_info.max_cpus;
239 int i = cpunum;
b0d623f7
A
240
241 /* Decide whether to start a CPU, and actually start it */
7ddcb079 242 TOPO_DBG("cpu_topology_start() processor_start():\n");
0a7de745 243 if (i < ncpus) {
7ddcb079 244 TOPO_DBG("\tlcpu %d\n", cpu_datap(i)->cpu_number);
0a7de745 245 processor_start(cpu_datap(i)->cpu_processor);
b0d623f7 246 return KERN_SUCCESS;
0a7de745
A
247 } else {
248 return KERN_FAILURE;
2d21ac55
A
249 }
250}
251
252static int
253lapicid_cmp(const void *x, const void *y)
254{
0a7de745
A
255 cpu_data_t *cpu_x = *((cpu_data_t **)(uintptr_t)x);
256 cpu_data_t *cpu_y = *((cpu_data_t **)(uintptr_t)y);
2d21ac55 257
7ddcb079 258 TOPO_DBG("lapicid_cmp(%p,%p) (%d,%d)\n",
0a7de745
A
259 x, y, cpu_x->cpu_phys_number, cpu_y->cpu_phys_number);
260 if (cpu_x->cpu_phys_number < cpu_y->cpu_phys_number) {
2d21ac55 261 return -1;
0a7de745
A
262 }
263 if (cpu_x->cpu_phys_number == cpu_y->cpu_phys_number) {
2d21ac55 264 return 0;
0a7de745 265 }
2d21ac55
A
266 return 1;
267}
268
269static x86_affinity_set_t *
270find_cache_affinity(x86_cpu_cache_t *l2_cachep)
271{
0a7de745 272 x86_affinity_set_t *aset;
2d21ac55
A
273
274 for (aset = x86_affinities; aset != NULL; aset = aset->next) {
0a7de745 275 if (l2_cachep == aset->cache) {
2d21ac55 276 break;
0a7de745 277 }
2d21ac55 278 }
0a7de745 279 return aset;
2d21ac55
A
280}
281
282int
283ml_get_max_affinity_sets(void)
284{
285 return x86_affinity_count;
286}
287
288processor_set_t
0a7de745 289ml_affinity_to_pset(uint32_t affinity_num)
2d21ac55 290{
0a7de745 291 x86_affinity_set_t *aset;
2d21ac55
A
292
293 for (aset = x86_affinities; aset != NULL; aset = aset->next) {
0a7de745 294 if (affinity_num == aset->num) {
2d21ac55 295 break;
0a7de745 296 }
2d21ac55 297 }
593a1d5f 298 return (aset == NULL) ? PROCESSOR_SET_NULL : aset->pset;
2d21ac55
A
299}
300
301uint64_t
302ml_cpu_cache_size(unsigned int level)
303{
0a7de745 304 x86_cpu_cache_t *cachep;
2d21ac55
A
305
306 if (level == 0) {
307 return machine_info.max_mem;
0a7de745
A
308 } else if (1 <= level && level <= MAX_CACHE_DEPTH) {
309 cachep = current_cpu_datap()->lcpu.caches[level - 1];
2d21ac55
A
310 return cachep ? cachep->cache_size : 0;
311 } else {
312 return 0;
313 }
314}
315
316uint64_t
317ml_cpu_cache_sharing(unsigned int level)
318{
0a7de745 319 x86_cpu_cache_t *cachep;
2d21ac55
A
320
321 if (level == 0) {
322 return machine_info.max_cpus;
0a7de745
A
323 } else if (1 <= level && level <= MAX_CACHE_DEPTH) {
324 cachep = current_cpu_datap()->lcpu.caches[level - 1];
2d21ac55
A
325 return cachep ? cachep->nlcpus : 0;
326 } else {
327 return 0;
328 }
329}
330
0a7de745
A
331#if DEVELOPMENT || DEBUG
332volatile int mmiotrace_enabled = 1;
333int iotrace_generators = 0;
334int iotrace_entries_per_cpu = 0;
335int *iotrace_next;
336iotrace_entry_t **iotrace_ring;
337
94ff46dc
A
338volatile int traptrace_enabled = 1;
339int traptrace_generators = 0;
340int traptrace_entries_per_cpu = 0;
341int *traptrace_next;
342traptrace_entry_t **traptrace_ring;
343
344static void
345init_trace_bufs(int cpucnt, int entries_per_cpu, void ***ring, int entry_size,
346 int **next_array, int *allocated_entries_per_cpu, int *allocated_generator_count)
0a7de745
A
347{
348 int i;
349
94ff46dc
A
350 *next_array = kalloc_tag(cpucnt * sizeof(int), VM_KERN_MEMORY_DIAG);
351 if (__improbable(*next_array == NULL)) {
352 *allocated_generator_count = 0;
0a7de745
A
353 return;
354 } else {
94ff46dc 355 bzero(*next_array, cpucnt * sizeof(int));
0a7de745
A
356 }
357
94ff46dc
A
358 *ring = kalloc_tag(cpucnt * sizeof(void *), VM_KERN_MEMORY_DIAG);
359 if (__improbable(*ring == NULL)) {
360 kfree(*next_array, cpucnt * sizeof(int));
361 *next_array = NULL;
362 *allocated_generator_count = 0;
0a7de745
A
363 return;
364 }
365 for (i = 0; i < cpucnt; i++) {
94ff46dc
A
366 (*ring)[i] = kalloc_tag(entries_per_cpu * entry_size, VM_KERN_MEMORY_DIAG);
367 if (__improbable((*ring)[i] == NULL)) {
368 kfree(*next_array, cpucnt * sizeof(int));
369 *next_array = NULL;
0a7de745 370 for (int j = 0; j < i; j++) {
94ff46dc 371 kfree((*ring)[j], entries_per_cpu * entry_size);
0a7de745 372 }
94ff46dc
A
373 kfree(*ring, cpucnt * sizeof(void *));
374 *ring = NULL;
0a7de745
A
375 return;
376 }
94ff46dc 377 bzero((*ring)[i], entries_per_cpu * entry_size);
0a7de745
A
378 }
379
94ff46dc
A
380 *allocated_entries_per_cpu = entries_per_cpu;
381 *allocated_generator_count = cpucnt;
0a7de745
A
382}
383
94ff46dc
A
384
385static void
386init_iotrace_bufs(int cpucnt, int entries_per_cpu)
0a7de745 387{
94ff46dc
A
388 init_trace_bufs(cpucnt, entries_per_cpu, (void ***)&iotrace_ring, sizeof(iotrace_entry_t),
389 &iotrace_next, &iotrace_entries_per_cpu, &iotrace_generators);
390}
391
392static void
393init_traptrace_bufs(int cpucnt, int entries_per_cpu)
394{
395 init_trace_bufs(cpucnt, entries_per_cpu, (void ***)&traptrace_ring, sizeof(traptrace_entry_t),
396 &traptrace_next, &traptrace_entries_per_cpu, &traptrace_generators);
397}
0a7de745 398
94ff46dc
A
399static void
400gentrace_configure_from_bootargs(const char *ena_prop, int *ena_valp, const char *epc_prop,
401 int *epcp, int max_epc, int def_epc, int override)
402{
403 if (kern_feature_override(override)) {
404 *ena_valp = 0;
0a7de745
A
405 }
406
94ff46dc
A
407 (void) PE_parse_boot_argn(ena_prop, ena_valp, sizeof(*ena_valp));
408
409 if (*ena_valp == 0) {
0a7de745
A
410 return;
411 }
412
94ff46dc
A
413 if (PE_parse_boot_argn(epc_prop, epcp, sizeof(*epcp)) &&
414 (*epcp < 1 || *epcp > max_epc)) {
415 *epcp = def_epc;
0a7de745 416 }
94ff46dc
A
417}
418
419void
420iotrace_init(int ncpus)
421{
422 int entries_per_cpu = DEFAULT_IOTRACE_ENTRIES_PER_CPU;
423 int enable = mmiotrace_enabled;
0a7de745 424
94ff46dc
A
425 gentrace_configure_from_bootargs("iotrace", &enable, "iotrace_epc", &entries_per_cpu,
426 IOTRACE_MAX_ENTRIES_PER_CPU, DEFAULT_IOTRACE_ENTRIES_PER_CPU, KF_IOTRACE_OVRD);
427
428 mmiotrace_enabled = enable;
429
430 if (mmiotrace_enabled) {
431 init_iotrace_bufs(ncpus, entries_per_cpu);
432 }
0a7de745 433}
94ff46dc
A
434
435void
436traptrace_init(int ncpus)
437{
438 int entries_per_cpu = DEFAULT_TRAPTRACE_ENTRIES_PER_CPU;
439 int enable = traptrace_enabled;
440
441 gentrace_configure_from_bootargs("traptrace", &enable, "traptrace_epc", &entries_per_cpu,
442 TRAPTRACE_MAX_ENTRIES_PER_CPU, DEFAULT_TRAPTRACE_ENTRIES_PER_CPU, KF_TRAPTRACE_OVRD);
443
444 traptrace_enabled = enable;
445
446 if (traptrace_enabled) {
447 init_traptrace_bufs(ncpus, entries_per_cpu);
448 }
449}
450
0a7de745 451#endif /* DEVELOPMENT || DEBUG */