RHEL4/kernel/stop_machine.c
<<
>>
Prefs
   1#include <linux/stop_machine.h>
   2#include <linux/kthread.h>
   3#include <linux/sched.h>
   4#include <linux/cpu.h>
   5#include <linux/err.h>
   6#include <linux/syscalls.h>
   7#include <asm/atomic.h>
   8#include <asm/semaphore.h>
   9
  10/* Since we effect priority and affinity (both of which are visible
  11 * to, and settable by outside processes) we do indirection via a
  12 * kthread. */
  13
  14/* Thread to stop each CPU in user context. */
  15enum stopmachine_state {
  16        STOPMACHINE_WAIT,
  17        STOPMACHINE_PREPARE,
  18        STOPMACHINE_DISABLE_IRQ,
  19        STOPMACHINE_EXIT,
  20};
  21
  22static enum stopmachine_state stopmachine_state;
  23static unsigned int stopmachine_num_threads;
  24static atomic_t stopmachine_thread_ack;
  25static DECLARE_MUTEX(stopmachine_mutex);
  26
  27static int stopmachine(void *cpu)
  28{
  29        int irqs_disabled = 0;
  30        int prepared = 0;
  31
  32        set_cpus_allowed(current, cpumask_of_cpu((int)(long)cpu));
  33
  34        /* Ack: we are alive */
  35        mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */
  36        atomic_inc(&stopmachine_thread_ack);
  37
  38        /* Simple state machine */
  39        while (stopmachine_state != STOPMACHINE_EXIT) {
  40                if (stopmachine_state == STOPMACHINE_DISABLE_IRQ 
  41                    && !irqs_disabled) {
  42                        local_irq_disable();
  43                        irqs_disabled = 1;
  44                        /* Ack: irqs disabled. */
  45                        mb(); /* Must read state first. */
  46                        atomic_inc(&stopmachine_thread_ack);
  47                } else if (stopmachine_state == STOPMACHINE_PREPARE
  48                           && !prepared) {
  49                        /* Everyone is in place, hold CPU. */
  50                        preempt_disable();
  51                        prepared = 1;
  52                        mb(); /* Must read state first. */
  53                        atomic_inc(&stopmachine_thread_ack);
  54                }
  55                cpu_relax();
  56        }
  57
  58        /* Ack: we are exiting. */
  59        mb(); /* Must read state first. */
  60        atomic_inc(&stopmachine_thread_ack);
  61
  62        if (irqs_disabled)
  63                local_irq_enable();
  64        if (prepared)
  65                preempt_enable();
  66
  67        return 0;
  68}
  69
  70/* Change the thread state */
  71static void stopmachine_set_state(enum stopmachine_state state)
  72{
  73        atomic_set(&stopmachine_thread_ack, 0);
  74        wmb();
  75        stopmachine_state = state;
  76        while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads)
  77                cpu_relax();
  78}
  79
  80static int stop_machine(void)
  81{
  82        int i, ret = 0;
  83        struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
  84
  85        /* One high-prio thread per cpu.  We'll do this one. */
  86        sys_sched_setscheduler(current->pid, SCHED_FIFO, &param);
  87
  88        atomic_set(&stopmachine_thread_ack, 0);
  89        stopmachine_num_threads = 0;
  90        stopmachine_state = STOPMACHINE_WAIT;
  91
  92        for_each_online_cpu(i) {
  93                if (i == smp_processor_id())
  94                        continue;
  95                ret = kernel_thread(stopmachine, (void *)(long)i,CLONE_KERNEL);
  96                if (ret < 0)
  97                        break;
  98                stopmachine_num_threads++;
  99        }
 100
 101        /* Wait for them all to come to life. */
 102        while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads)
 103                yield();
 104
 105        /* If some failed, kill them all. */
 106        if (ret < 0) {
 107                stopmachine_set_state(STOPMACHINE_EXIT);
 108                up(&stopmachine_mutex);
 109                return ret;
 110        }
 111
 112        /* Don't schedule us away at this point, please. */
 113        local_irq_disable();
 114
 115        /* Now they are all started, make them hold the CPUs, ready. */
 116        stopmachine_set_state(STOPMACHINE_PREPARE);
 117
 118        /* Make them disable irqs. */
 119        stopmachine_set_state(STOPMACHINE_DISABLE_IRQ);
 120
 121        return 0;
 122}
 123
 124static void restart_machine(void)
 125{
 126        stopmachine_set_state(STOPMACHINE_EXIT);
 127        local_irq_enable();
 128}
 129
 130struct stop_machine_data
 131{
 132        int (*fn)(void *);
 133        void *data;
 134        struct completion done;
 135};
 136
 137static int do_stop(void *_smdata)
 138{
 139        struct stop_machine_data *smdata = _smdata;
 140        int ret;
 141
 142        ret = stop_machine();
 143        if (ret == 0) {
 144                ret = smdata->fn(smdata->data);
 145                restart_machine();
 146        }
 147
 148        /* We're done: you can kthread_stop us now */
 149        complete(&smdata->done);
 150
 151        /* Wait for kthread_stop */
 152        set_current_state(TASK_INTERRUPTIBLE);
 153        while (!kthread_should_stop()) {
 154                schedule();
 155                set_current_state(TASK_INTERRUPTIBLE);
 156        }
 157        __set_current_state(TASK_RUNNING);
 158        return ret;
 159}
 160
 161struct task_struct *__stop_machine_run(int (*fn)(void *), void *data,
 162                                       unsigned int cpu)
 163{
 164        struct stop_machine_data smdata;
 165        struct task_struct *p;
 166
 167        smdata.fn = fn;
 168        smdata.data = data;
 169        init_completion(&smdata.done);
 170
 171        down(&stopmachine_mutex);
 172
 173        /* If they don't care which CPU fn runs on, bind to any online one. */
 174        if (cpu == NR_CPUS)
 175                cpu = smp_processor_id();
 176
 177        p = kthread_create(do_stop, &smdata, "kstopmachine");
 178        if (!IS_ERR(p)) {
 179                kthread_bind(p, cpu);
 180                wake_up_process(p);
 181                wait_for_completion(&smdata.done);
 182        }
 183        up(&stopmachine_mutex);
 184        return p;
 185}
 186
 187int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
 188{
 189        struct task_struct *p;
 190        int ret;
 191
 192        /* No CPUs can come up or down during this. */
 193        lock_cpu_hotplug();
 194        p = __stop_machine_run(fn, data, cpu);
 195        if (!IS_ERR(p))
 196                ret = kthread_stop(p);
 197        else
 198                ret = PTR_ERR(p);
 199        unlock_cpu_hotplug();
 200
 201        return ret;
 202}
 203