1e5582ca2SRusty Russell /* Copyright 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation. 2e5582ca2SRusty Russell * GPL v2 and any later version. 3e5582ca2SRusty Russell */ 41da177e4SLinus Torvalds #include <linux/cpu.h> 51da177e4SLinus Torvalds #include <linux/err.h> 6ee527cd3SPrarit Bhargava #include <linux/kthread.h> 7ee527cd3SPrarit Bhargava #include <linux/module.h> 8ee527cd3SPrarit Bhargava #include <linux/sched.h> 9ee527cd3SPrarit Bhargava #include <linux/stop_machine.h> 101da177e4SLinus Torvalds #include <linux/syscalls.h> 11a12bb444SBenjamin Herrenschmidt #include <linux/interrupt.h> 12a12bb444SBenjamin Herrenschmidt 131da177e4SLinus Torvalds #include <asm/atomic.h> 141da177e4SLinus Torvalds #include <asm/semaphore.h> 151da177e4SLinus Torvalds #include <asm/uaccess.h> 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds /* Since we effect priority and affinity (both of which are visible 181da177e4SLinus Torvalds * to, and settable by outside processes) we do indirection via a 191da177e4SLinus Torvalds * kthread. */ 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds /* Thread to stop each CPU in user context. */ 221da177e4SLinus Torvalds enum stopmachine_state { 231da177e4SLinus Torvalds STOPMACHINE_WAIT, 241da177e4SLinus Torvalds STOPMACHINE_PREPARE, 251da177e4SLinus Torvalds STOPMACHINE_DISABLE_IRQ, 261da177e4SLinus Torvalds STOPMACHINE_EXIT, 271da177e4SLinus Torvalds }; 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds static enum stopmachine_state stopmachine_state; 301da177e4SLinus Torvalds static unsigned int stopmachine_num_threads; 311da177e4SLinus Torvalds static atomic_t stopmachine_thread_ack; 321da177e4SLinus Torvalds static DECLARE_MUTEX(stopmachine_mutex); 331da177e4SLinus Torvalds 34d8cb7c1dSAndrew Morton static int stopmachine(void *cpu) 351da177e4SLinus Torvalds { 361da177e4SLinus Torvalds int irqs_disabled = 0; 371da177e4SLinus Torvalds int prepared = 0; 381da177e4SLinus Torvalds 39d8cb7c1dSAndrew Morton set_cpus_allowed(current, cpumask_of_cpu((int)(long)cpu)); 40d8cb7c1dSAndrew Morton 411da177e4SLinus Torvalds /* Ack: we are alive */ 42d59dd462Sakpm@osdl.org smp_mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */ 431da177e4SLinus Torvalds atomic_inc(&stopmachine_thread_ack); 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds /* Simple state machine */ 461da177e4SLinus Torvalds while (stopmachine_state != STOPMACHINE_EXIT) { 471da177e4SLinus Torvalds if (stopmachine_state == STOPMACHINE_DISABLE_IRQ 481da177e4SLinus Torvalds && !irqs_disabled) { 491da177e4SLinus Torvalds local_irq_disable(); 50a12bb444SBenjamin Herrenschmidt hard_irq_disable(); 511da177e4SLinus Torvalds irqs_disabled = 1; 521da177e4SLinus Torvalds /* Ack: irqs disabled. */ 53d59dd462Sakpm@osdl.org smp_mb(); /* Must read state first. */ 541da177e4SLinus Torvalds atomic_inc(&stopmachine_thread_ack); 551da177e4SLinus Torvalds } else if (stopmachine_state == STOPMACHINE_PREPARE 561da177e4SLinus Torvalds && !prepared) { 571da177e4SLinus Torvalds /* Everyone is in place, hold CPU. */ 581da177e4SLinus Torvalds preempt_disable(); 591da177e4SLinus Torvalds prepared = 1; 60d59dd462Sakpm@osdl.org smp_mb(); /* Must read state first. */ 611da177e4SLinus Torvalds atomic_inc(&stopmachine_thread_ack); 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds /* Yield in first stage: migration threads need to 641da177e4SLinus Torvalds * help our sisters onto their CPUs. */ 651da177e4SLinus Torvalds if (!prepared && !irqs_disabled) 661da177e4SLinus Torvalds yield(); 671da177e4SLinus Torvalds else 681da177e4SLinus Torvalds cpu_relax(); 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* Ack: we are exiting. */ 72d59dd462Sakpm@osdl.org smp_mb(); /* Must read state first. */ 731da177e4SLinus Torvalds atomic_inc(&stopmachine_thread_ack); 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds if (irqs_disabled) 761da177e4SLinus Torvalds local_irq_enable(); 771da177e4SLinus Torvalds if (prepared) 781da177e4SLinus Torvalds preempt_enable(); 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds return 0; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds /* Change the thread state */ 841da177e4SLinus Torvalds static void stopmachine_set_state(enum stopmachine_state state) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds atomic_set(&stopmachine_thread_ack, 0); 87d59dd462Sakpm@osdl.org smp_wmb(); 881da177e4SLinus Torvalds stopmachine_state = state; 891da177e4SLinus Torvalds while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) 901da177e4SLinus Torvalds cpu_relax(); 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds static int stop_machine(void) 941da177e4SLinus Torvalds { 95d8cb7c1dSAndrew Morton int i, ret = 0; 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds atomic_set(&stopmachine_thread_ack, 0); 981da177e4SLinus Torvalds stopmachine_num_threads = 0; 991da177e4SLinus Torvalds stopmachine_state = STOPMACHINE_WAIT; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds for_each_online_cpu(i) { 10239c715b7SIngo Molnar if (i == raw_smp_processor_id()) 1031da177e4SLinus Torvalds continue; 104d8cb7c1dSAndrew Morton ret = kernel_thread(stopmachine, (void *)(long)i,CLONE_KERNEL); 105d8cb7c1dSAndrew Morton if (ret < 0) 1061da177e4SLinus Torvalds break; 1071da177e4SLinus Torvalds stopmachine_num_threads++; 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds /* Wait for them all to come to life. */ 1111da177e4SLinus Torvalds while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) 1121da177e4SLinus Torvalds yield(); 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds /* If some failed, kill them all. */ 1151da177e4SLinus Torvalds if (ret < 0) { 1161da177e4SLinus Torvalds stopmachine_set_state(STOPMACHINE_EXIT); 1171da177e4SLinus Torvalds return ret; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds /* Now they are all started, make them hold the CPUs, ready. */ 1214557398fSKirill Korotaev preempt_disable(); 1221da177e4SLinus Torvalds stopmachine_set_state(STOPMACHINE_PREPARE); 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds /* Make them disable irqs. */ 1254557398fSKirill Korotaev local_irq_disable(); 126a12bb444SBenjamin Herrenschmidt hard_irq_disable(); 1271da177e4SLinus Torvalds stopmachine_set_state(STOPMACHINE_DISABLE_IRQ); 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds return 0; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds static void restart_machine(void) 1331da177e4SLinus Torvalds { 1341da177e4SLinus Torvalds stopmachine_set_state(STOPMACHINE_EXIT); 1351da177e4SLinus Torvalds local_irq_enable(); 1364557398fSKirill Korotaev preempt_enable_no_resched(); 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds struct stop_machine_data 1401da177e4SLinus Torvalds { 1411da177e4SLinus Torvalds int (*fn)(void *); 1421da177e4SLinus Torvalds void *data; 1431da177e4SLinus Torvalds struct completion done; 1441da177e4SLinus Torvalds }; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static int do_stop(void *_smdata) 1471da177e4SLinus Torvalds { 1481da177e4SLinus Torvalds struct stop_machine_data *smdata = _smdata; 1491da177e4SLinus Torvalds int ret; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds ret = stop_machine(); 1521da177e4SLinus Torvalds if (ret == 0) { 1531da177e4SLinus Torvalds ret = smdata->fn(smdata->data); 1541da177e4SLinus Torvalds restart_machine(); 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds /* We're done: you can kthread_stop us now */ 1581da177e4SLinus Torvalds complete(&smdata->done); 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds /* Wait for kthread_stop */ 1611da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 1621da177e4SLinus Torvalds while (!kthread_should_stop()) { 1631da177e4SLinus Torvalds schedule(); 1641da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds __set_current_state(TASK_RUNNING); 1671da177e4SLinus Torvalds return ret; 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, 1711da177e4SLinus Torvalds unsigned int cpu) 1721da177e4SLinus Torvalds { 1731da177e4SLinus Torvalds struct stop_machine_data smdata; 1741da177e4SLinus Torvalds struct task_struct *p; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds smdata.fn = fn; 1771da177e4SLinus Torvalds smdata.data = data; 1781da177e4SLinus Torvalds init_completion(&smdata.done); 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds down(&stopmachine_mutex); 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds /* If they don't care which CPU fn runs on, bind to any online one. */ 1831da177e4SLinus Torvalds if (cpu == NR_CPUS) 18439c715b7SIngo Molnar cpu = raw_smp_processor_id(); 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds p = kthread_create(do_stop, &smdata, "kstopmachine"); 1871da177e4SLinus Torvalds if (!IS_ERR(p)) { 18885653af7SSatoru Takeuchi struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; 18985653af7SSatoru Takeuchi 19085653af7SSatoru Takeuchi /* One high-prio thread per cpu. We'll do this one. */ 19185653af7SSatoru Takeuchi sched_setscheduler(p, SCHED_FIFO, ¶m); 1921da177e4SLinus Torvalds kthread_bind(p, cpu); 1931da177e4SLinus Torvalds wake_up_process(p); 1941da177e4SLinus Torvalds wait_for_completion(&smdata.done); 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds up(&stopmachine_mutex); 1971da177e4SLinus Torvalds return p; 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) 2011da177e4SLinus Torvalds { 2021da177e4SLinus Torvalds struct task_struct *p; 2031da177e4SLinus Torvalds int ret; 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds /* No CPUs can come up or down during this. */ 206*86ef5c9aSGautham R Shenoy get_online_cpus(); 2071da177e4SLinus Torvalds p = __stop_machine_run(fn, data, cpu); 2081da177e4SLinus Torvalds if (!IS_ERR(p)) 2091da177e4SLinus Torvalds ret = kthread_stop(p); 2101da177e4SLinus Torvalds else 2111da177e4SLinus Torvalds ret = PTR_ERR(p); 212*86ef5c9aSGautham R Shenoy put_online_cpus(); 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds return ret; 2151da177e4SLinus Torvalds } 216ee527cd3SPrarit Bhargava EXPORT_SYMBOL_GPL(stop_machine_run); 217