| Subject: stomp-machine-raw-lock.patch |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Wed, 29 Jun 2011 11:01:51 +0200 |
| |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| --- |
| kernel/stop_machine.c | 58 +++++++++++++++++++++++++++++++++++--------------- |
| 1 file changed, 41 insertions(+), 17 deletions(-) |
| |
| --- a/kernel/stop_machine.c |
| +++ b/kernel/stop_machine.c |
| @@ -29,12 +29,12 @@ struct cpu_stop_done { |
| atomic_t nr_todo; /* nr left to execute */ |
| bool executed; /* actually executed? */ |
| int ret; /* collected return value */ |
| - struct completion completion; /* fired if nr_todo reaches 0 */ |
| + struct task_struct *waiter; /* woken when nr_todo reaches 0 */ |
| }; |
| |
| /* the actual stopper, one per every possible cpu, enabled on online cpus */ |
| struct cpu_stopper { |
| - spinlock_t lock; |
| + raw_spinlock_t lock; |
| bool enabled; /* is this stopper enabled? */ |
| struct list_head works; /* list of pending works */ |
| struct task_struct *thread; /* stopper thread */ |
| @@ -47,7 +47,7 @@ static void cpu_stop_init_done(struct cp |
| { |
| memset(done, 0, sizeof(*done)); |
| atomic_set(&done->nr_todo, nr_todo); |
| - init_completion(&done->completion); |
| + done->waiter = current; |
| } |
| |
| /* signal completion unless @done is NULL */ |
| @@ -56,8 +56,10 @@ static void cpu_stop_signal_done(struct |
| if (done) { |
| if (executed) |
| done->executed = true; |
| - if (atomic_dec_and_test(&done->nr_todo)) |
| - complete(&done->completion); |
| + if (atomic_dec_and_test(&done->nr_todo)) { |
| + wake_up_process(done->waiter); |
| + done->waiter = NULL; |
| + } |
| } |
| } |
| |
| @@ -67,7 +69,7 @@ static void cpu_stop_queue_work(struct c |
| { |
| unsigned long flags; |
| |
| - spin_lock_irqsave(&stopper->lock, flags); |
| + raw_spin_lock_irqsave(&stopper->lock, flags); |
| |
| if (stopper->enabled) { |
| list_add_tail(&work->list, &stopper->works); |
| @@ -75,7 +77,23 @@ static void cpu_stop_queue_work(struct c |
| } else |
| cpu_stop_signal_done(work->done, false); |
| |
| - spin_unlock_irqrestore(&stopper->lock, flags); |
| + raw_spin_unlock_irqrestore(&stopper->lock, flags); |
| +} |
| + |
| +static void wait_for_stop_done(struct cpu_stop_done *done) |
| +{ |
| + set_current_state(TASK_UNINTERRUPTIBLE); |
| + while (atomic_read(&done->nr_todo)) { |
| + schedule(); |
| + set_current_state(TASK_UNINTERRUPTIBLE); |
| + } |
| + /* |
| + * We need to wait until cpu_stop_signal_done() has cleared |
| + * done->waiter. |
| + */ |
| + while (done->waiter) |
| + cpu_relax(); |
| + set_current_state(TASK_RUNNING); |
| } |
| |
| /** |
| @@ -109,7 +127,7 @@ int stop_one_cpu(unsigned int cpu, cpu_s |
| |
| cpu_stop_init_done(&done, 1); |
| cpu_stop_queue_work(&per_cpu(cpu_stopper, cpu), &work); |
| - wait_for_completion(&done.completion); |
| + wait_for_stop_done(&done); |
| return done.executed ? done.ret : -ENOENT; |
| } |
| |
| @@ -171,7 +189,7 @@ static int __stop_cpus(const struct cpum |
| |
| cpu_stop_init_done(&done, cpumask_weight(cpumask)); |
| queue_stop_cpus_work(cpumask, fn, arg, &done); |
| - wait_for_completion(&done.completion); |
| + wait_for_stop_done(&done); |
| return done.executed ? done.ret : -ENOENT; |
| } |
| |
| @@ -259,13 +277,13 @@ repeat: |
| } |
| |
| work = NULL; |
| - spin_lock_irq(&stopper->lock); |
| + raw_spin_lock_irq(&stopper->lock); |
| if (!list_empty(&stopper->works)) { |
| work = list_first_entry(&stopper->works, |
| struct cpu_stop_work, list); |
| list_del_init(&work->list); |
| } |
| - spin_unlock_irq(&stopper->lock); |
| + raw_spin_unlock_irq(&stopper->lock); |
| |
| if (work) { |
| cpu_stop_fn_t fn = work->fn; |
| @@ -299,7 +317,13 @@ repeat: |
| kallsyms_lookup((unsigned long)fn, NULL, NULL, NULL, |
| ksym_buf), arg); |
| |
| + /* |
| + * Make sure that the wakeup and setting done->waiter |
| + * to NULL is atomic. |
| + */ |
| + local_irq_disable(); |
| cpu_stop_signal_done(done, true); |
| + local_irq_enable(); |
| } else |
| schedule(); |
| |
| @@ -337,9 +361,9 @@ static int __cpuinit cpu_stop_cpu_callba |
| /* strictly unnecessary, as first user will wake it */ |
| wake_up_process(stopper->thread); |
| /* mark enabled */ |
| - spin_lock_irq(&stopper->lock); |
| + raw_spin_lock_irq(&stopper->lock); |
| stopper->enabled = true; |
| - spin_unlock_irq(&stopper->lock); |
| + raw_spin_unlock_irq(&stopper->lock); |
| break; |
| |
| #ifdef CONFIG_HOTPLUG_CPU |
| @@ -352,11 +376,11 @@ static int __cpuinit cpu_stop_cpu_callba |
| /* kill the stopper */ |
| kthread_stop(stopper->thread); |
| /* drain remaining works */ |
| - spin_lock_irq(&stopper->lock); |
| + raw_spin_lock_irq(&stopper->lock); |
| list_for_each_entry(work, &stopper->works, list) |
| cpu_stop_signal_done(work->done, false); |
| stopper->enabled = false; |
| - spin_unlock_irq(&stopper->lock); |
| + raw_spin_unlock_irq(&stopper->lock); |
| /* release the stopper */ |
| put_task_struct(stopper->thread); |
| stopper->thread = NULL; |
| @@ -387,7 +411,7 @@ static int __init cpu_stop_init(void) |
| for_each_possible_cpu(cpu) { |
| struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); |
| |
| - spin_lock_init(&stopper->lock); |
| + raw_spin_lock_init(&stopper->lock); |
| INIT_LIST_HEAD(&stopper->works); |
| } |
| |
| @@ -581,7 +605,7 @@ int stop_machine_from_inactive_cpu(int ( |
| ret = stop_machine_cpu_stop(&smdata); |
| |
| /* Busy wait for completion. */ |
| - while (!completion_done(&done.completion)) |
| + while (atomic_read(&done.nr_todo)) |
| cpu_relax(); |
| |
| mutex_unlock(&stop_cpus_mutex); |