| /* Copyright 2008, 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation. | 
 |  * GPL v2 and any later version. | 
 |  */ | 
 | #include <linux/cpu.h> | 
 | #include <linux/err.h> | 
 | #include <linux/kthread.h> | 
 | #include <linux/module.h> | 
 | #include <linux/sched.h> | 
 | #include <linux/stop_machine.h> | 
 | #include <linux/syscalls.h> | 
 | #include <linux/interrupt.h> | 
 |  | 
 | #include <asm/atomic.h> | 
 | #include <asm/uaccess.h> | 
 |  | 
 | /* This controls the threads on each CPU. */ | 
 | enum stopmachine_state { | 
 | 	/* Dummy starting state for thread. */ | 
 | 	STOPMACHINE_NONE, | 
 | 	/* Awaiting everyone to be scheduled. */ | 
 | 	STOPMACHINE_PREPARE, | 
 | 	/* Disable interrupts. */ | 
 | 	STOPMACHINE_DISABLE_IRQ, | 
 | 	/* Run the function */ | 
 | 	STOPMACHINE_RUN, | 
 | 	/* Exit */ | 
 | 	STOPMACHINE_EXIT, | 
 | }; | 
 | static enum stopmachine_state state; | 
 |  | 
 | struct stop_machine_data { | 
 | 	int (*fn)(void *); | 
 | 	void *data; | 
 | 	int fnret; | 
 | }; | 
 |  | 
 | /* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */ | 
 | static unsigned int num_threads; | 
 | static atomic_t thread_ack; | 
 | static struct completion finished; | 
 | static DEFINE_MUTEX(lock); | 
 |  | 
 | static void set_state(enum stopmachine_state newstate) | 
 | { | 
 | 	/* Reset ack counter. */ | 
 | 	atomic_set(&thread_ack, num_threads); | 
 | 	smp_wmb(); | 
 | 	state = newstate; | 
 | } | 
 |  | 
 | /* Last one to ack a state moves to the next state. */ | 
 | static void ack_state(void) | 
 | { | 
 | 	if (atomic_dec_and_test(&thread_ack)) { | 
 | 		/* If we're the last one to ack the EXIT, we're finished. */ | 
 | 		if (state == STOPMACHINE_EXIT) | 
 | 			complete(&finished); | 
 | 		else | 
 | 			set_state(state + 1); | 
 | 	} | 
 | } | 
 |  | 
 | /* This is the actual thread which stops the CPU.  It exits by itself rather | 
 |  * than waiting for kthread_stop(), because it's easier for hotplug CPU. */ | 
 | static int stop_cpu(struct stop_machine_data *smdata) | 
 | { | 
 | 	enum stopmachine_state curstate = STOPMACHINE_NONE; | 
 |  | 
 | 	/* Simple state machine */ | 
 | 	do { | 
 | 		/* Chill out and ensure we re-read stopmachine_state. */ | 
 | 		cpu_relax(); | 
 | 		if (state != curstate) { | 
 | 			curstate = state; | 
 | 			switch (curstate) { | 
 | 			case STOPMACHINE_DISABLE_IRQ: | 
 | 				local_irq_disable(); | 
 | 				hard_irq_disable(); | 
 | 				break; | 
 | 			case STOPMACHINE_RUN: | 
 | 				/* |= allows error detection if functions on | 
 | 				 * multiple CPUs. */ | 
 | 				smdata->fnret |= smdata->fn(smdata->data); | 
 | 				break; | 
 | 			default: | 
 | 				break; | 
 | 			} | 
 | 			ack_state(); | 
 | 		} | 
 | 	} while (curstate != STOPMACHINE_EXIT); | 
 |  | 
 | 	local_irq_enable(); | 
 | 	do_exit(0); | 
 | } | 
 |  | 
 | /* Callback for CPUs which aren't supposed to do anything. */ | 
 | static int chill(void *unused) | 
 | { | 
 | 	return 0; | 
 | } | 
 |  | 
 | int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) | 
 | { | 
 | 	int i, err; | 
 | 	struct stop_machine_data active, idle; | 
 | 	struct task_struct **threads; | 
 |  | 
 | 	active.fn = fn; | 
 | 	active.data = data; | 
 | 	active.fnret = 0; | 
 | 	idle.fn = chill; | 
 | 	idle.data = NULL; | 
 |  | 
 | 	/* This could be too big for stack on large machines. */ | 
 | 	threads = kcalloc(NR_CPUS, sizeof(threads[0]), GFP_KERNEL); | 
 | 	if (!threads) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	/* Set up initial state. */ | 
 | 	mutex_lock(&lock); | 
 | 	init_completion(&finished); | 
 | 	num_threads = num_online_cpus(); | 
 | 	set_state(STOPMACHINE_PREPARE); | 
 |  | 
 | 	for_each_online_cpu(i) { | 
 | 		struct stop_machine_data *smdata = &idle; | 
 | 		struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; | 
 |  | 
 | 		if (!cpus) { | 
 | 			if (i == first_cpu(cpu_online_map)) | 
 | 				smdata = &active; | 
 | 		} else { | 
 | 			if (cpu_isset(i, *cpus)) | 
 | 				smdata = &active; | 
 | 		} | 
 |  | 
 | 		threads[i] = kthread_create((void *)stop_cpu, smdata, "kstop%u", | 
 | 					    i); | 
 | 		if (IS_ERR(threads[i])) { | 
 | 			err = PTR_ERR(threads[i]); | 
 | 			threads[i] = NULL; | 
 | 			goto kill_threads; | 
 | 		} | 
 |  | 
 | 		/* Place it onto correct cpu. */ | 
 | 		kthread_bind(threads[i], i); | 
 |  | 
 | 		/* Make it highest prio. */ | 
 | 		if (sched_setscheduler_nocheck(threads[i], SCHED_FIFO, ¶m)) | 
 | 			BUG(); | 
 | 	} | 
 |  | 
 | 	/* We've created all the threads.  Wake them all: hold this CPU so one | 
 | 	 * doesn't hit this CPU until we're ready. */ | 
 | 	get_cpu(); | 
 | 	for_each_online_cpu(i) | 
 | 		wake_up_process(threads[i]); | 
 |  | 
 | 	/* This will release the thread on our CPU. */ | 
 | 	put_cpu(); | 
 | 	wait_for_completion(&finished); | 
 | 	mutex_unlock(&lock); | 
 |  | 
 | 	kfree(threads); | 
 |  | 
 | 	return active.fnret; | 
 |  | 
 | kill_threads: | 
 | 	for_each_online_cpu(i) | 
 | 		if (threads[i]) | 
 | 			kthread_stop(threads[i]); | 
 | 	mutex_unlock(&lock); | 
 |  | 
 | 	kfree(threads); | 
 | 	return err; | 
 | } | 
 |  | 
 | int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	/* No CPUs can come up or down during this. */ | 
 | 	get_online_cpus(); | 
 | 	ret = __stop_machine(fn, data, cpus); | 
 | 	put_online_cpus(); | 
 |  | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(stop_machine); |