arch_topology.c 16.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
3
4
5
6
7
8
9
/*
 * Arch specific cpu topology information
 *
 * Copyright (C) 2016, ARM Ltd.
 * Written by: Juri Lelli, ARM Ltd.
 */

#include <linux/acpi.h>
10
#include <linux/arch_topology.h>
11
12
13
14
15
16
17
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sched/topology.h>
18
#include <linux/cpuset.h>
19
20
21
#include <linux/cpumask.h>
#include <linux/init.h>
#include <linux/percpu.h>
22
#include <linux/rcupdate.h>
23
24
#include <linux/sched.h>
#include <linux/smp.h>
25

26
static DEFINE_PER_CPU(struct scale_freq_data __rcu *, sft_data);
27
28
29
30
31
32
33
34
static struct cpumask scale_freq_counters_mask;
static bool scale_freq_invariant;

static bool supports_scale_freq_counters(const struct cpumask *cpus)
{
	return cpumask_subset(cpus, &scale_freq_counters_mask);
}

35
36
37
bool topology_scale_freq_invariant(void)
{
	return cpufreq_supports_freq_invariance() ||
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
	       supports_scale_freq_counters(cpu_online_mask);
}

static void update_scale_freq_invariant(bool status)
{
	if (scale_freq_invariant == status)
		return;

	/*
	 * Task scheduler behavior depends on frequency invariance support,
	 * either cpufreq or counter driven. If the support status changes as
	 * a result of counter initialisation and use, retrigger the build of
	 * scheduling domains to ensure the information is propagated properly.
	 */
	if (topology_scale_freq_invariant() == status) {
		scale_freq_invariant = status;
		rebuild_sched_domains_energy();
	}
}

void topology_set_scale_freq_source(struct scale_freq_data *data,
				    const struct cpumask *cpus)
{
	struct scale_freq_data *sfd;
	int cpu;

	/*
	 * Avoid calling rebuild_sched_domains() unnecessarily if FIE is
	 * supported by cpufreq.
	 */
	if (cpumask_empty(&scale_freq_counters_mask))
		scale_freq_invariant = topology_scale_freq_invariant();

71
72
	rcu_read_lock();

73
	for_each_cpu(cpu, cpus) {
74
		sfd = rcu_dereference(*per_cpu_ptr(&sft_data, cpu));
75
76
77

		/* Use ARCH provided counters whenever possible */
		if (!sfd || sfd->source != SCALE_FREQ_SOURCE_ARCH) {
78
			rcu_assign_pointer(per_cpu(sft_data, cpu), data);
79
80
81
82
			cpumask_set_cpu(cpu, &scale_freq_counters_mask);
		}
	}

83
84
	rcu_read_unlock();

85
	update_scale_freq_invariant(true);
86
}
87
EXPORT_SYMBOL_GPL(topology_set_scale_freq_source);
88

89
90
void topology_clear_scale_freq_source(enum scale_freq_source source,
				      const struct cpumask *cpus)
91
{
92
93
94
	struct scale_freq_data *sfd;
	int cpu;

95
96
	rcu_read_lock();

97
	for_each_cpu(cpu, cpus) {
98
		sfd = rcu_dereference(*per_cpu_ptr(&sft_data, cpu));
99
100

		if (sfd && sfd->source == source) {
101
			rcu_assign_pointer(per_cpu(sft_data, cpu), NULL);
102
103
104
105
			cpumask_clear_cpu(cpu, &scale_freq_counters_mask);
		}
	}

106
107
108
109
110
111
112
113
	rcu_read_unlock();

	/*
	 * Make sure all references to previous sft_data are dropped to avoid
	 * use-after-free races.
	 */
	synchronize_rcu();

114
	update_scale_freq_invariant(false);
115
}
116
EXPORT_SYMBOL_GPL(topology_clear_scale_freq_source);
117
118
119

void topology_scale_freq_tick(void)
{
120
	struct scale_freq_data *sfd = rcu_dereference_sched(*this_cpu_ptr(&sft_data));
121
122
123
124
125

	if (sfd)
		sfd->set_freq_scale();
}

126
DEFINE_PER_CPU(unsigned long, arch_freq_scale) = SCHED_CAPACITY_SCALE;
127
EXPORT_PER_CPU_SYMBOL_GPL(arch_freq_scale);
128

129
130
void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq,
			     unsigned long max_freq)
131
{
132
133
134
	unsigned long scale;
	int i;

135
136
137
	if (WARN_ON_ONCE(!cur_freq || !max_freq))
		return;

138
139
140
141
142
	/*
	 * If the use of counters for FIE is enabled, just return as we don't
	 * want to update the scale factor with information from CPUFREQ.
	 * Instead the scale factor will be updated from arch_scale_freq_tick.
	 */
143
	if (supports_scale_freq_counters(cpus))
144
145
		return;

146
147
148
	scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq;

	for_each_cpu(i, cpus)
149
		per_cpu(arch_freq_scale, i) = scale;
150
151
}

152
static DEFINE_MUTEX(cpu_scale_mutex);
153
DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
154

155
void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity)
156
157
158
159
{
	per_cpu(cpu_scale, cpu) = capacity;
}

160
161
162
163
164
165
166
167
168
169
170
DEFINE_PER_CPU(unsigned long, thermal_pressure);

void topology_set_thermal_pressure(const struct cpumask *cpus,
			       unsigned long th_pressure)
{
	int cpu;

	for_each_cpu(cpu, cpus)
		WRITE_ONCE(per_cpu(thermal_pressure, cpu), th_pressure);
}

171
172
173
174
175
176
static ssize_t cpu_capacity_show(struct device *dev,
				 struct device_attribute *attr,
				 char *buf)
{
	struct cpu *cpu = container_of(dev, struct cpu, dev);

177
	return sysfs_emit(buf, "%lu\n", topology_get_cpu_scale(cpu->dev.id));
178
179
}

180
181
182
static void update_topology_flags_workfn(struct work_struct *work);
static DECLARE_WORK(update_topology_flags_work, update_topology_flags_workfn);

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
static ssize_t cpu_capacity_store(struct device *dev,
				  struct device_attribute *attr,
				  const char *buf,
				  size_t count)
{
	struct cpu *cpu = container_of(dev, struct cpu, dev);
	int this_cpu = cpu->dev.id;
	unsigned long new_capacity;
	ssize_t ret;

	if (!count)
		return 0;

	ret = kstrtoul(buf, 0, &new_capacity);
	if (ret)
		return ret;
	if (new_capacity > SCHED_CAPACITY_SCALE)
		return -EINVAL;

	mutex_lock(&cpu_scale_mutex);
	topology_set_cpu_scale(this_cpu, new_capacity);
	mutex_unlock(&cpu_scale_mutex);

	schedule_work(&update_topology_flags_work);

	return count;
}

static DEVICE_ATTR_RW(cpu_capacity);
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

static int register_cpu_capacity_sysctl(void)
{
	int i;
	struct device *cpu;

	for_each_possible_cpu(i) {
		cpu = get_cpu_device(i);
		if (!cpu) {
			pr_err("%s: too early to get CPU%d device!\n",
			       __func__, i);
			continue;
		}
		device_create_file(cpu, &dev_attr_cpu_capacity);
	}

	return 0;
}
subsys_initcall(register_cpu_capacity_sysctl);

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
static int update_topology;

int topology_update_cpu_topology(void)
{
	return update_topology;
}

/*
 * Updating the sched_domains can't be done directly from cpufreq callbacks
 * due to locking, so queue the work for later.
 */
static void update_topology_flags_workfn(struct work_struct *work)
{
	update_topology = 1;
	rebuild_sched_domains();
	pr_debug("sched_domain hierarchy rebuilt, flags updated\n");
	update_topology = 0;
}

251
static DEFINE_PER_CPU(u32, freq_factor) = 1;
252
static u32 *raw_capacity;
253

254
static int free_raw_capacity(void)
255
256
257
258
259
260
{
	kfree(raw_capacity);
	raw_capacity = NULL;

	return 0;
}
261

262
void topology_normalize_cpu_scale(void)
263
264
{
	u64 capacity;
265
	u64 capacity_scale;
266
267
	int cpu;

268
	if (!raw_capacity)
269
270
		return;

271
	capacity_scale = 1;
272
	for_each_possible_cpu(cpu) {
273
274
275
276
277
		capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
		capacity_scale = max(capacity, capacity_scale);
	}

	pr_debug("cpu_capacity: capacity_scale=%llu\n", capacity_scale);
278
	mutex_lock(&cpu_scale_mutex);
279
280
281
282
	for_each_possible_cpu(cpu) {
		capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
		capacity = div64_u64(capacity << SCHED_CAPACITY_SHIFT,
			capacity_scale);
283
		topology_set_cpu_scale(cpu, capacity);
284
		pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
285
			cpu, topology_get_cpu_scale(cpu));
286
	}
287
	mutex_unlock(&cpu_scale_mutex);
288
289
}

290
bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
291
{
292
	struct clk *cpu_clk;
293
	static bool cap_parsing_failed;
294
	int ret;
295
296
297
	u32 cpu_capacity;

	if (cap_parsing_failed)
298
		return false;
299

300
	ret = of_property_read_u32(cpu_node, "capacity-dmips-mhz",
301
302
303
304
305
306
307
308
				   &cpu_capacity);
	if (!ret) {
		if (!raw_capacity) {
			raw_capacity = kcalloc(num_possible_cpus(),
					       sizeof(*raw_capacity),
					       GFP_KERNEL);
			if (!raw_capacity) {
				cap_parsing_failed = true;
309
				return false;
310
311
312
			}
		}
		raw_capacity[cpu] = cpu_capacity;
313
314
		pr_debug("cpu_capacity: %pOF cpu_capacity=%u (raw)\n",
			cpu_node, raw_capacity[cpu]);
315
316
317
318
319
320
321
322

		/*
		 * Update freq_factor for calculating early boot cpu capacities.
		 * For non-clk CPU DVFS mechanism, there's no way to get the
		 * frequency value now, assuming they are running at the same
		 * frequency (by keeping the initial freq_factor value).
		 */
		cpu_clk = of_clk_get(cpu_node, 0);
323
		if (!PTR_ERR_OR_ZERO(cpu_clk)) {
324
325
			per_cpu(freq_factor, cpu) =
				clk_get_rate(cpu_clk) / 1000;
326
327
			clk_put(cpu_clk);
		}
328
329
	} else {
		if (raw_capacity) {
330
331
			pr_err("cpu_capacity: missing %pOF raw capacity\n",
				cpu_node);
332
333
334
			pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
		}
		cap_parsing_failed = true;
335
		free_raw_capacity();
336
337
338
339
340
341
	}

	return !ret;
}

#ifdef CONFIG_CPU_FREQ
342
343
344
static cpumask_var_t cpus_to_visit;
static void parsing_done_workfn(struct work_struct *work);
static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
345

346
static int
347
348
349
350
351
352
353
init_cpu_capacity_callback(struct notifier_block *nb,
			   unsigned long val,
			   void *data)
{
	struct cpufreq_policy *policy = data;
	int cpu;

354
	if (!raw_capacity)
355
356
		return 0;

357
	if (val != CPUFREQ_CREATE_POLICY)
358
359
360
361
362
363
364
365
		return 0;

	pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n",
		 cpumask_pr_args(policy->related_cpus),
		 cpumask_pr_args(cpus_to_visit));

	cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus);

366
367
	for_each_cpu(cpu, policy->related_cpus)
		per_cpu(freq_factor, cpu) = policy->cpuinfo.max_freq / 1000;
368
369
370

	if (cpumask_empty(cpus_to_visit)) {
		topology_normalize_cpu_scale();
371
		schedule_work(&update_topology_flags_work);
372
		free_raw_capacity();
373
374
375
376
		pr_debug("cpu_capacity: parsing done\n");
		schedule_work(&parsing_done_work);
	}

377
378
379
	return 0;
}

380
static struct notifier_block init_cpu_capacity_notifier = {
381
382
383
384
385
	.notifier_call = init_cpu_capacity_callback,
};

static int __init register_cpufreq_notifier(void)
{
386
387
	int ret;

388
389
390
391
392
	/*
	 * on ACPI-based systems we need to use the default cpu capacity
	 * until we have the necessary code to parse the cpu capacity, so
	 * skip registering cpufreq notifier.
	 */
393
	if (!acpi_disabled || !raw_capacity)
394
395
		return -EINVAL;

396
	if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL))
397
398
399
400
		return -ENOMEM;

	cpumask_copy(cpus_to_visit, cpu_possible_mask);

401
402
403
404
405
406
407
	ret = cpufreq_register_notifier(&init_cpu_capacity_notifier,
					CPUFREQ_POLICY_NOTIFIER);

	if (ret)
		free_cpumask_var(cpus_to_visit);

	return ret;
408
409
410
}
core_initcall(register_cpufreq_notifier);

411
static void parsing_done_workfn(struct work_struct *work)
412
413
414
{
	cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
					 CPUFREQ_POLICY_NOTIFIER);
415
	free_cpumask_var(cpus_to_visit);
416
417
418
419
420
}

#else
core_initcall(free_raw_capacity);
#endif
421
422

#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
423
424
425
426
427
428
429
430
431
432
/*
 * This function returns the logic cpu number of the node.
 * There are basically three kinds of return values:
 * (1) logic cpu number which is > 0.
 * (2) -ENODEV when the device tree(DT) node is valid and found in the DT but
 * there is no possible logical CPU in the kernel to match. This happens
 * when CONFIG_NR_CPUS is configure to be smaller than the number of
 * CPU nodes in DT. We need to just ignore this case.
 * (3) -1 if the node does not exist in the device tree
 */
433
434
435
436
437
438
439
440
441
442
443
444
445
static int __init get_cpu_for_node(struct device_node *node)
{
	struct device_node *cpu_node;
	int cpu;

	cpu_node = of_parse_phandle(node, "cpu", 0);
	if (!cpu_node)
		return -1;

	cpu = of_cpu_node_to_id(cpu_node);
	if (cpu >= 0)
		topology_parse_cpu_capacity(cpu_node, cpu);
	else
446
447
		pr_info("CPU node for %pOF exist but the possible cpu range is :%*pbl\n",
			cpu_node, cpumask_pr_args(cpu_possible_mask));
448
449
450
451
452
453
454
455

	of_node_put(cpu_node);
	return cpu;
}

static int __init parse_core(struct device_node *core, int package_id,
			     int core_id)
{
456
	char name[20];
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
	bool leaf = true;
	int i = 0;
	int cpu;
	struct device_node *t;

	do {
		snprintf(name, sizeof(name), "thread%d", i);
		t = of_get_child_by_name(core, name);
		if (t) {
			leaf = false;
			cpu = get_cpu_for_node(t);
			if (cpu >= 0) {
				cpu_topology[cpu].package_id = package_id;
				cpu_topology[cpu].core_id = core_id;
				cpu_topology[cpu].thread_id = i;
472
473
			} else if (cpu != -ENODEV) {
				pr_err("%pOF: Can't get CPU for thread\n", t);
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
				of_node_put(t);
				return -EINVAL;
			}
			of_node_put(t);
		}
		i++;
	} while (t);

	cpu = get_cpu_for_node(core);
	if (cpu >= 0) {
		if (!leaf) {
			pr_err("%pOF: Core has both threads and CPU\n",
			       core);
			return -EINVAL;
		}

		cpu_topology[cpu].package_id = package_id;
		cpu_topology[cpu].core_id = core_id;
492
	} else if (leaf && cpu != -ENODEV) {
493
494
495
496
497
498
499
500
501
		pr_err("%pOF: Can't get CPU for leaf core\n", core);
		return -EINVAL;
	}

	return 0;
}

static int __init parse_cluster(struct device_node *cluster, int depth)
{
502
	char name[20];
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
	bool leaf = true;
	bool has_cores = false;
	struct device_node *c;
	static int package_id __initdata;
	int core_id = 0;
	int i, ret;

	/*
	 * First check for child clusters; we currently ignore any
	 * information about the nesting of clusters and present the
	 * scheduler with a flat list of them.
	 */
	i = 0;
	do {
		snprintf(name, sizeof(name), "cluster%d", i);
		c = of_get_child_by_name(cluster, name);
		if (c) {
			leaf = false;
			ret = parse_cluster(c, depth + 1);
			of_node_put(c);
			if (ret != 0)
				return ret;
		}
		i++;
	} while (c);

	/* Now check for cores */
	i = 0;
	do {
		snprintf(name, sizeof(name), "core%d", i);
		c = of_get_child_by_name(cluster, name);
		if (c) {
			has_cores = true;

			if (depth == 0) {
				pr_err("%pOF: cpu-map children should be clusters\n",
				       c);
				of_node_put(c);
				return -EINVAL;
			}

			if (leaf) {
				ret = parse_core(c, package_id, core_id++);
			} else {
				pr_err("%pOF: Non-leaf cluster with core %s\n",
				       cluster, name);
				ret = -EINVAL;
			}

			of_node_put(c);
			if (ret != 0)
				return ret;
		}
		i++;
	} while (c);

	if (leaf && !has_cores)
		pr_warn("%pOF: empty cluster\n", cluster);

	if (leaf)
		package_id++;

	return 0;
}

static int __init parse_dt_topology(void)
{
	struct device_node *cn, *map;
	int ret = 0;
	int cpu;

	cn = of_find_node_by_path("/cpus");
	if (!cn) {
		pr_err("No CPU information found in DT\n");
		return 0;
	}

	/*
	 * When topology is provided cpu-map is essentially a root
	 * cluster with restricted subnodes.
	 */
	map = of_get_child_by_name(cn, "cpu-map");
	if (!map)
		goto out;

	ret = parse_cluster(map, 0);
	if (ret != 0)
		goto out_map;

	topology_normalize_cpu_scale();

	/*
	 * Check that all cores are in the topology; the SMP code will
	 * only mark cores described in the DT as possible.
	 */
	for_each_possible_cpu(cpu)
		if (cpu_topology[cpu].package_id == -1)
			ret = -EINVAL;

out_map:
	of_node_put(map);
out:
	of_node_put(cn);
	return ret;
}
608
#endif
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673

/*
 * cpu topology table
 */
struct cpu_topology cpu_topology[NR_CPUS];
EXPORT_SYMBOL_GPL(cpu_topology);

const struct cpumask *cpu_coregroup_mask(int cpu)
{
	const cpumask_t *core_mask = cpumask_of_node(cpu_to_node(cpu));

	/* Find the smaller of NUMA, core or LLC siblings */
	if (cpumask_subset(&cpu_topology[cpu].core_sibling, core_mask)) {
		/* not numa in package, lets use the package siblings */
		core_mask = &cpu_topology[cpu].core_sibling;
	}
	if (cpu_topology[cpu].llc_id != -1) {
		if (cpumask_subset(&cpu_topology[cpu].llc_sibling, core_mask))
			core_mask = &cpu_topology[cpu].llc_sibling;
	}

	return core_mask;
}

void update_siblings_masks(unsigned int cpuid)
{
	struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
	int cpu;

	/* update core and thread sibling masks */
	for_each_online_cpu(cpu) {
		cpu_topo = &cpu_topology[cpu];

		if (cpuid_topo->llc_id == cpu_topo->llc_id) {
			cpumask_set_cpu(cpu, &cpuid_topo->llc_sibling);
			cpumask_set_cpu(cpuid, &cpu_topo->llc_sibling);
		}

		if (cpuid_topo->package_id != cpu_topo->package_id)
			continue;

		cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
		cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);

		if (cpuid_topo->core_id != cpu_topo->core_id)
			continue;

		cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
		cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
	}
}

static void clear_cpu_topology(int cpu)
{
	struct cpu_topology *cpu_topo = &cpu_topology[cpu];

	cpumask_clear(&cpu_topo->llc_sibling);
	cpumask_set_cpu(cpu, &cpu_topo->llc_sibling);

	cpumask_clear(&cpu_topo->core_sibling);
	cpumask_set_cpu(cpu, &cpu_topo->core_sibling);
	cpumask_clear(&cpu_topo->thread_sibling);
	cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
}

674
void __init reset_cpu_topology(void)
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
{
	unsigned int cpu;

	for_each_possible_cpu(cpu) {
		struct cpu_topology *cpu_topo = &cpu_topology[cpu];

		cpu_topo->thread_id = -1;
		cpu_topo->core_id = -1;
		cpu_topo->package_id = -1;
		cpu_topo->llc_id = -1;

		clear_cpu_topology(cpu);
	}
}

void remove_cpu_topology(unsigned int cpu)
{
	int sibling;

	for_each_cpu(sibling, topology_core_cpumask(cpu))
		cpumask_clear_cpu(cpu, topology_core_cpumask(sibling));
	for_each_cpu(sibling, topology_sibling_cpumask(cpu))
		cpumask_clear_cpu(cpu, topology_sibling_cpumask(sibling));
	for_each_cpu(sibling, topology_llc_cpumask(cpu))
		cpumask_clear_cpu(cpu, topology_llc_cpumask(sibling));

	clear_cpu_topology(cpu);
}

__weak int __init parse_acpi_topology(void)
{
	return 0;
}

709
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
710
711
712
713
714
715
716
717
718
719
720
721
722
723
void __init init_cpu_topology(void)
{
	reset_cpu_topology();

	/*
	 * Discard anything that was parsed if we hit an error so we
	 * don't use partial information.
	 */
	if (parse_acpi_topology())
		reset_cpu_topology();
	else if (of_have_populated_dt() && parse_dt_topology())
		reset_cpu_topology();
}
#endif