|  | /* 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); |