forked from mirrors/linux
		
	xen: implement CPU hotplugging
Note the changes from 2.6.18-xen CPU hotplugging: A vcpu_down request from the remote admin via Xenbus both hotunplugs the CPU, and disables it by removing it from the cpu_present map, and removing its entry in /sys. A vcpu_up request from the remote admin only re-enables the CPU, and does not immediately bring the CPU up. A udev event is emitted, which can be caught by the user if he wishes to automatically re-up CPUs when available, or implement a more complex policy. Signed-off-by: Alex Nixon <alex.nixon@citrix.com> Acked-by: Jeremy Fitzhardinge <jeremy@goop.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
		
							parent
							
								
									8227dce7dc
								
							
						
					
					
						commit
						d68d82afd4
					
				
					 7 changed files with 162 additions and 13 deletions
				
			
		|  | @ -11,8 +11,6 @@ | ||||||
|  * useful topology information for the kernel to make use of.  As a |  * useful topology information for the kernel to make use of.  As a | ||||||
|  * result, all CPUs are treated as if they're single-core and |  * result, all CPUs are treated as if they're single-core and | ||||||
|  * single-threaded. |  * single-threaded. | ||||||
|  * |  | ||||||
|  * This does not handle HOTPLUG_CPU yet. |  | ||||||
|  */ |  */ | ||||||
| #include <linux/sched.h> | #include <linux/sched.h> | ||||||
| #include <linux/err.h> | #include <linux/err.h> | ||||||
|  | @ -61,11 +59,12 @@ static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id) | ||||||
| 	return IRQ_HANDLED; | 	return IRQ_HANDLED; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static __cpuinit void cpu_bringup_and_idle(void) | static __cpuinit void cpu_bringup(void) | ||||||
| { | { | ||||||
| 	int cpu = smp_processor_id(); | 	int cpu = smp_processor_id(); | ||||||
| 
 | 
 | ||||||
| 	cpu_init(); | 	cpu_init(); | ||||||
|  | 	touch_softlockup_watchdog(); | ||||||
| 	preempt_disable(); | 	preempt_disable(); | ||||||
| 
 | 
 | ||||||
| 	xen_enable_sysenter(); | 	xen_enable_sysenter(); | ||||||
|  | @ -86,6 +85,11 @@ static __cpuinit void cpu_bringup_and_idle(void) | ||||||
| 	local_irq_enable(); | 	local_irq_enable(); | ||||||
| 
 | 
 | ||||||
| 	wmb();			/* make sure everything is out */ | 	wmb();			/* make sure everything is out */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __cpuinit void cpu_bringup_and_idle(void) | ||||||
|  | { | ||||||
|  | 	cpu_bringup(); | ||||||
| 	cpu_idle(); | 	cpu_idle(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -209,8 +213,6 @@ static void __init xen_smp_prepare_cpus(unsigned int max_cpus) | ||||||
| 
 | 
 | ||||||
| 		cpu_set(cpu, cpu_present_map); | 		cpu_set(cpu, cpu_present_map); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	//init_xenbus_allowed_cpumask();
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static __cpuinit int | static __cpuinit int | ||||||
|  | @ -278,12 +280,6 @@ static int __cpuinit xen_cpu_up(unsigned int cpu) | ||||||
| 	struct task_struct *idle = idle_task(cpu); | 	struct task_struct *idle = idle_task(cpu); | ||||||
| 	int rc; | 	int rc; | ||||||
| 
 | 
 | ||||||
| #if 0 |  | ||||||
| 	rc = cpu_up_check(cpu); |  | ||||||
| 	if (rc) |  | ||||||
| 		return rc; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef CONFIG_X86_64 | #ifdef CONFIG_X86_64 | ||||||
| 	/* Allocate node local memory for AP pdas */ | 	/* Allocate node local memory for AP pdas */ | ||||||
| 	WARN_ON(cpu == 0); | 	WARN_ON(cpu == 0); | ||||||
|  | @ -336,6 +332,42 @@ static void xen_smp_cpus_done(unsigned int max_cpus) | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int xen_cpu_disable(void) | ||||||
|  | { | ||||||
|  | 	unsigned int cpu = smp_processor_id(); | ||||||
|  | 	if (cpu == 0) | ||||||
|  | 		return -EBUSY; | ||||||
|  | 
 | ||||||
|  | 	cpu_disable_common(); | ||||||
|  | 
 | ||||||
|  | 	load_cr3(swapper_pg_dir); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void xen_cpu_die(unsigned int cpu) | ||||||
|  | { | ||||||
|  | 	while (HYPERVISOR_vcpu_op(VCPUOP_is_up, cpu, NULL)) { | ||||||
|  | 		current->state = TASK_UNINTERRUPTIBLE; | ||||||
|  | 		schedule_timeout(HZ/10); | ||||||
|  | 	} | ||||||
|  | 	unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); | ||||||
|  | 	unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); | ||||||
|  | 	unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL); | ||||||
|  | 	unbind_from_irqhandler(per_cpu(callfuncsingle_irq, cpu), NULL); | ||||||
|  | 	xen_uninit_lock_cpu(cpu); | ||||||
|  | 	xen_teardown_timer(cpu); | ||||||
|  | 
 | ||||||
|  | 	if (num_online_cpus() == 1) | ||||||
|  | 		alternatives_smp_switch(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void xen_play_dead(void) | ||||||
|  | { | ||||||
|  | 	play_dead_common(); | ||||||
|  | 	HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); | ||||||
|  | 	cpu_bringup(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void stop_self(void *v) | static void stop_self(void *v) | ||||||
| { | { | ||||||
| 	int cpu = smp_processor_id(); | 	int cpu = smp_processor_id(); | ||||||
|  | @ -419,9 +451,13 @@ static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id) | ||||||
| static const struct smp_ops xen_smp_ops __initdata = { | static const struct smp_ops xen_smp_ops __initdata = { | ||||||
| 	.smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, | 	.smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, | ||||||
| 	.smp_prepare_cpus = xen_smp_prepare_cpus, | 	.smp_prepare_cpus = xen_smp_prepare_cpus, | ||||||
| 	.cpu_up = xen_cpu_up, |  | ||||||
| 	.smp_cpus_done = xen_smp_cpus_done, | 	.smp_cpus_done = xen_smp_cpus_done, | ||||||
| 
 | 
 | ||||||
|  | 	.cpu_up = xen_cpu_up, | ||||||
|  | 	.cpu_die = xen_cpu_die, | ||||||
|  | 	.cpu_disable = xen_cpu_disable, | ||||||
|  | 	.play_dead = xen_play_dead, | ||||||
|  | 
 | ||||||
| 	.smp_send_stop = xen_smp_send_stop, | 	.smp_send_stop = xen_smp_send_stop, | ||||||
| 	.smp_send_reschedule = xen_smp_send_reschedule, | 	.smp_send_reschedule = xen_smp_send_reschedule, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -357,6 +357,11 @@ void __cpuinit xen_init_lock_cpu(int cpu) | ||||||
| 	printk("cpu %d spinlock event irq %d\n", cpu, irq); | 	printk("cpu %d spinlock event irq %d\n", cpu, irq); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void xen_uninit_lock_cpu(int cpu) | ||||||
|  | { | ||||||
|  | 	unbind_from_irqhandler(per_cpu(lock_kicker_irq, cpu), NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void __init xen_init_spinlocks(void) | void __init xen_init_spinlocks(void) | ||||||
| { | { | ||||||
| 	pv_lock_ops.spin_is_locked = xen_spin_is_locked; | 	pv_lock_ops.spin_is_locked = xen_spin_is_locked; | ||||||
|  |  | ||||||
|  | @ -450,6 +450,14 @@ void xen_setup_timer(int cpu) | ||||||
| 	setup_runstate_info(cpu); | 	setup_runstate_info(cpu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void xen_teardown_timer(int cpu) | ||||||
|  | { | ||||||
|  | 	struct clock_event_device *evt; | ||||||
|  | 	BUG_ON(cpu == 0); | ||||||
|  | 	evt = &per_cpu(xen_clock_events, cpu); | ||||||
|  | 	unbind_from_irqhandler(evt->irq, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void xen_setup_cpu_clockevents(void) | void xen_setup_cpu_clockevents(void) | ||||||
| { | { | ||||||
| 	BUG_ON(preemptible()); | 	BUG_ON(preemptible()); | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ void __init xen_build_dynamic_phys_to_machine(void); | ||||||
| 
 | 
 | ||||||
| void xen_init_irq_ops(void); | void xen_init_irq_ops(void); | ||||||
| void xen_setup_timer(int cpu); | void xen_setup_timer(int cpu); | ||||||
|  | void xen_teardown_timer(int cpu); | ||||||
| cycle_t xen_clocksource_read(void); | cycle_t xen_clocksource_read(void); | ||||||
| void xen_setup_cpu_clockevents(void); | void xen_setup_cpu_clockevents(void); | ||||||
| unsigned long xen_tsc_khz(void); | unsigned long xen_tsc_khz(void); | ||||||
|  | @ -50,11 +51,16 @@ void xen_mark_init_mm_pinned(void); | ||||||
| 
 | 
 | ||||||
| void __init xen_setup_vcpu_info_placement(void); | void __init xen_setup_vcpu_info_placement(void); | ||||||
| 
 | 
 | ||||||
|  | void xen_play_dead(void); | ||||||
|  | void xen_cpu_die(unsigned int cpu); | ||||||
|  | int xen_cpu_disable(void); | ||||||
|  | 
 | ||||||
| #ifdef CONFIG_SMP | #ifdef CONFIG_SMP | ||||||
| void xen_smp_init(void); | void xen_smp_init(void); | ||||||
| 
 | 
 | ||||||
| void __init xen_init_spinlocks(void); | void __init xen_init_spinlocks(void); | ||||||
| __cpuinit void xen_init_lock_cpu(int cpu); | __cpuinit void xen_init_lock_cpu(int cpu); | ||||||
|  | void xen_uninit_lock_cpu(int cpu); | ||||||
| 
 | 
 | ||||||
| extern cpumask_t xen_cpu_initialized_map; | extern cpumask_t xen_cpu_initialized_map; | ||||||
| #else | #else | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| obj-y	+= grant-table.o features.o events.o manage.o | obj-y	+= grant-table.o features.o events.o manage.o cpu_hotplug.o | ||||||
| obj-y	+= xenbus/ | obj-y	+= xenbus/ | ||||||
| obj-$(CONFIG_XEN_XENCOMM)	+= xencomm.o | obj-$(CONFIG_XEN_XENCOMM)	+= xencomm.o | ||||||
| obj-$(CONFIG_XEN_BALLOON)	+= balloon.o | obj-$(CONFIG_XEN_BALLOON)	+= balloon.o | ||||||
|  |  | ||||||
							
								
								
									
										90
									
								
								drivers/xen/cpu_hotplug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								drivers/xen/cpu_hotplug.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,90 @@ | ||||||
|  | #include <linux/notifier.h> | ||||||
|  | 
 | ||||||
|  | #include <xen/xenbus.h> | ||||||
|  | 
 | ||||||
|  | #include <asm-x86/xen/hypervisor.h> | ||||||
|  | #include <asm/cpu.h> | ||||||
|  | 
 | ||||||
|  | static void enable_hotplug_cpu(int cpu) | ||||||
|  | { | ||||||
|  | 	if (!cpu_present(cpu)) | ||||||
|  | 		arch_register_cpu(cpu); | ||||||
|  | 
 | ||||||
|  | 	cpu_set(cpu, cpu_present_map); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void disable_hotplug_cpu(int cpu) | ||||||
|  | { | ||||||
|  | 	if (cpu_present(cpu)) | ||||||
|  | 		arch_unregister_cpu(cpu); | ||||||
|  | 
 | ||||||
|  | 	cpu_clear(cpu, cpu_present_map); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void vcpu_hotplug(unsigned int cpu) | ||||||
|  | { | ||||||
|  | 	int err; | ||||||
|  | 	char dir[32], state[32]; | ||||||
|  | 
 | ||||||
|  | 	if (!cpu_possible(cpu)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	sprintf(dir, "cpu/%u", cpu); | ||||||
|  | 	err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state); | ||||||
|  | 	if (err != 1) { | ||||||
|  | 		printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (strcmp(state, "online") == 0) { | ||||||
|  | 		enable_hotplug_cpu(cpu); | ||||||
|  | 	} else if (strcmp(state, "offline") == 0) { | ||||||
|  | 		(void)cpu_down(cpu); | ||||||
|  | 		disable_hotplug_cpu(cpu); | ||||||
|  | 	} else { | ||||||
|  | 		printk(KERN_ERR "XENBUS: unknown state(%s) on CPU%d\n", | ||||||
|  | 		       state, cpu); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_vcpu_hotplug_event(struct xenbus_watch *watch, | ||||||
|  | 					const char **vec, unsigned int len) | ||||||
|  | { | ||||||
|  | 	unsigned int cpu; | ||||||
|  | 	char *cpustr; | ||||||
|  | 	const char *node = vec[XS_WATCH_PATH]; | ||||||
|  | 
 | ||||||
|  | 	cpustr = strstr(node, "cpu/"); | ||||||
|  | 	if (cpustr != NULL) { | ||||||
|  | 		sscanf(cpustr, "cpu/%u", &cpu); | ||||||
|  | 		vcpu_hotplug(cpu); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int setup_cpu_watcher(struct notifier_block *notifier, | ||||||
|  | 			      unsigned long event, void *data) | ||||||
|  | { | ||||||
|  | 	static struct xenbus_watch cpu_watch = { | ||||||
|  | 		.node = "cpu", | ||||||
|  | 		.callback = handle_vcpu_hotplug_event}; | ||||||
|  | 
 | ||||||
|  | 	(void)register_xenbus_watch(&cpu_watch); | ||||||
|  | 
 | ||||||
|  | 	return NOTIFY_DONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int __init setup_vcpu_hotplug_event(void) | ||||||
|  | { | ||||||
|  | 	static struct notifier_block xsn_cpu = { | ||||||
|  | 		.notifier_call = setup_cpu_watcher }; | ||||||
|  | 
 | ||||||
|  | 	if (!is_running_on_xen()) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	register_xenstore_notifier(&xsn_cpu); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | arch_initcall(setup_vcpu_hotplug_event); | ||||||
|  | 
 | ||||||
|  | @ -360,6 +360,10 @@ static void unbind_from_irq(unsigned int irq) | ||||||
| 			per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) | 			per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) | ||||||
| 				[index_from_irq(irq)] = -1; | 				[index_from_irq(irq)] = -1; | ||||||
| 			break; | 			break; | ||||||
|  | 		case IRQT_IPI: | ||||||
|  | 			per_cpu(ipi_to_irq, cpu_from_evtchn(evtchn)) | ||||||
|  | 				[index_from_irq(irq)] = -1; | ||||||
|  | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Alex Nixon
						Alex Nixon