summaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/topology.c
blob: 6e60b5fe224481d01ca8662c6edd01432da08518 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
 * Populate sysfs with topology information
 *
 * Written by: Matthew Dobson, IBM Corporation
 * Original Code: Paul Dorwin, IBM Corporation, Patrick Mochel, OSDL
 *
 * Copyright (C) 2002, IBM Corp.
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Send feedback to <colpatch@us.ibm.com>
 */
#include <linux/nodemask.h>
#include <linux/export.h>
#include <linux/mmzone.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/irq.h>
#include <asm/cpu.h>

static DEFINE_PER_CPU(struct x86_cpu, cpu_devices);

#ifdef CONFIG_HOTPLUG_CPU

#ifdef CONFIG_BOOTPARAM_HOTPLUG_CPU0
static int cpu0_hotpluggable = 1;
#else
static int cpu0_hotpluggable;
static int __init enable_cpu0_hotplug(char *str)
{
	cpu0_hotpluggable = 1;
	return 1;
}

__setup("cpu0_hotplug", enable_cpu0_hotplug);
#endif

#ifdef CONFIG_DEBUG_HOTPLUG_CPU0
/*
 * This function offlines a CPU as early as possible and allows userspace to
 * boot up without the CPU. The CPU can be onlined back by user after boot.
 *
 * This is only called for debugging CPU offline/online feature.
 */
int __ref _debug_hotplug_cpu(int cpu, int action)
{
	struct device *dev = get_cpu_device(cpu);
	int ret;

	if (!cpu_is_hotpluggable(cpu))
		return -EINVAL;

	cpu_hotplug_driver_lock();

	switch (action) {
	case 0:
		ret = cpu_down(cpu);
		if (!ret) {
			pr_info("CPU %u is now offline\n", cpu);
			kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
		} else
			pr_debug("Can't offline CPU%d.\n", cpu);
		break;
	case 1:
		ret = cpu_up(cpu);
		if (!ret)
			kobject_uevent(&dev->kobj, KOBJ_ONLINE);
		else
			pr_debug("Can't online CPU%d.\n", cpu);
		break;
	default:
		ret = -EINVAL;
	}

	cpu_hotplug_driver_unlock();

	return ret;
}

static int __init debug_hotplug_cpu(void)
{
	_debug_hotplug_cpu(0, 0);
	return 0;
}

late_initcall_sync(debug_hotplug_cpu);
#endif /* CONFIG_DEBUG_HOTPLUG_CPU0 */

int __ref arch_register_cpu(int num)
{
	struct cpuinfo_x86 *c = &cpu_data(num);

	/*
	 * Currently CPU0 is only hotpluggable on Intel platforms. Other
	 * vendors can add hotplug support later.
	 */
	if (c->x86_vendor != X86_VENDOR_INTEL)
		cpu0_hotpluggable = 0;

	/*
	 * Two known BSP/CPU0 dependencies: Resume from suspend/hibernate
	 * depends on BSP. PIC interrupts depend on BSP.
	 *
	 * If the BSP depencies are under control, one can tell kernel to
	 * enable BSP hotplug. This basically adds a control file and
	 * one can attempt to offline BSP.
	 */
	if (num == 0 && cpu0_hotpluggable) {
		unsigned int irq;
		/*
		 * We won't take down the boot processor on i386 if some
		 * interrupts only are able to be serviced by the BSP in PIC.
		 */
		for_each_active_irq(irq) {
			if (!IO_APIC_IRQ(irq) && irq_has_action(irq)) {
				cpu0_hotpluggable = 0;
				break;
			}
		}
	}
	if (num || cpu0_hotpluggable)
		per_cpu(cpu_devices, num).cpu.hotpluggable = 1;

	return register_cpu(&per_cpu(cpu_devices, num).cpu, num);
}
EXPORT_SYMBOL(arch_register_cpu);

void arch_unregister_cpu(int num)
{
	unregister_cpu(&per_cpu(cpu_devices, num).cpu);
}
EXPORT_SYMBOL(arch_unregister_cpu);
#else /* CONFIG_HOTPLUG_CPU */

static int __init arch_register_cpu(int num)
{
	return register_cpu(&per_cpu(cpu_devices, num).cpu, num);
}
#endif /* CONFIG_HOTPLUG_CPU */

static int __init topology_init(void)
{
	int i;

#ifdef CONFIG_NUMA
	for_each_online_node(i)
		register_one_node(i);
#endif

	for_each_present_cpu(i)
		arch_register_cpu(i);

	return 0;
}
subsys_initcall(topology_init);