Now that stop_cpus() is not "exclusive" if cpumask's don't overlap, we
can turn stop_two_cpus() into a trivial wrapper on top of stop_cpus().

Note: if zalloc_cpumask_var(cpumask, GFP_KERNEL) is not an option, we
can reimplement this function using stop_work_{alloc,free}_one().

Signed-off-by: Oleg Nesterov <[email protected]>
---
 include/linux/lglock.h  |    5 ---
 kernel/locking/lglock.c |   22 ----------------
 kernel/stop_machine.c   |   64 ++++++++--------------------------------------
 3 files changed, 11 insertions(+), 80 deletions(-)

diff --git a/include/linux/lglock.h b/include/linux/lglock.h
index c92ebd1..0081f00 100644
--- a/include/linux/lglock.h
+++ b/include/linux/lglock.h
@@ -52,15 +52,10 @@ struct lglock {
        static struct lglock name = { .lock = &name ## _lock }
 
 void lg_lock_init(struct lglock *lg, char *name);
-
 void lg_local_lock(struct lglock *lg);
 void lg_local_unlock(struct lglock *lg);
 void lg_local_lock_cpu(struct lglock *lg, int cpu);
 void lg_local_unlock_cpu(struct lglock *lg, int cpu);
-
-void lg_double_lock(struct lglock *lg, int cpu1, int cpu2);
-void lg_double_unlock(struct lglock *lg, int cpu1, int cpu2);
-
 void lg_global_lock(struct lglock *lg);
 void lg_global_unlock(struct lglock *lg);
 
diff --git a/kernel/locking/lglock.c b/kernel/locking/lglock.c
index 951cfcd..86ae2ae 100644
--- a/kernel/locking/lglock.c
+++ b/kernel/locking/lglock.c
@@ -60,28 +60,6 @@ void lg_local_unlock_cpu(struct lglock *lg, int cpu)
 }
 EXPORT_SYMBOL(lg_local_unlock_cpu);
 
-void lg_double_lock(struct lglock *lg, int cpu1, int cpu2)
-{
-       BUG_ON(cpu1 == cpu2);
-
-       /* lock in cpu order, just like lg_global_lock */
-       if (cpu2 < cpu1)
-               swap(cpu1, cpu2);
-
-       preempt_disable();
-       lock_acquire_shared(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_);
-       arch_spin_lock(per_cpu_ptr(lg->lock, cpu1));
-       arch_spin_lock(per_cpu_ptr(lg->lock, cpu2));
-}
-
-void lg_double_unlock(struct lglock *lg, int cpu1, int cpu2)
-{
-       lock_release(&lg->lock_dep_map, 1, _RET_IP_);
-       arch_spin_unlock(per_cpu_ptr(lg->lock, cpu1));
-       arch_spin_unlock(per_cpu_ptr(lg->lock, cpu2));
-       preempt_enable();
-}
-
 void lg_global_lock(struct lglock *lg)
 {
        int i;
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c
index 4b23875..572abc9 100644
--- a/kernel/stop_machine.c
+++ b/kernel/stop_machine.c
@@ -20,7 +20,6 @@
 #include <linux/kallsyms.h>
 #include <linux/smpboot.h>
 #include <linux/atomic.h>
-#include <linux/lglock.h>
 
 /*
  * Structure to determine completion condition and record errors.  May
@@ -97,14 +96,6 @@ static bool stop_work_alloc(const struct cpumask *cpumask, 
bool wait)
        return true;
 }
 
-/*
- * Avoids a race between stop_two_cpus and global stop_cpus, where
- * the stoppers could get queued up in reverse order, leading to
- * system deadlock. Using an lglock means stop_two_cpus remains
- * relatively cheap.
- */
-DEFINE_STATIC_LGLOCK(stop_cpus_lock);
-
 static void cpu_stop_init_done(struct cpu_stop_done *done, unsigned int 
nr_todo)
 {
        memset(done, 0, sizeof(*done));
@@ -276,50 +267,17 @@ static int multi_cpu_stop(void *data)
  */
 int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void 
*arg)
 {
-       struct cpu_stop_done done;
-       struct cpu_stop_work work1, work2;
-       struct multi_stop_data msdata;
-
-       preempt_disable();
-       msdata = (struct multi_stop_data){
-               .fn = fn,
-               .data = arg,
-               .num_threads = 2,
-               .active_cpus = cpumask_of(cpu1),
-       };
-
-       work1 = work2 = (struct cpu_stop_work){
-               .fn = multi_cpu_stop,
-               .arg = &msdata,
-               .done = &done
-       };
-
-       cpu_stop_init_done(&done, 2);
-       set_state(&msdata, MULTI_STOP_PREPARE);
-
-       /*
-        * If we observe both CPUs active we know _cpu_down() cannot yet have
-        * queued its stop_machine works and therefore ours will get executed
-        * first. Or its not either one of our CPUs that's getting unplugged,
-        * in which case we don't care.
-        *
-        * This relies on the stopper workqueues to be FIFO.
-        */
-       if (!cpu_active(cpu1) || !cpu_active(cpu2)) {
-               preempt_enable();
-               return -ENOENT;
-       }
-
-       lg_double_lock(&stop_cpus_lock, cpu1, cpu2);
-       cpu_stop_queue_work(cpu1, &work1);
-       cpu_stop_queue_work(cpu2, &work2);
-       lg_double_unlock(&stop_cpus_lock, cpu1, cpu2);
-
-       preempt_enable();
+       cpumask_var_t cpumask;
+       int ret;
 
-       wait_for_completion(&done.completion);
+       if (!zalloc_cpumask_var(&cpumask, GFP_KERNEL))
+               return -ENOMEM;
 
-       return done.executed ? done.ret : -ENOENT;
+       cpumask_set_cpu(cpu1, cpumask);
+       cpumask_set_cpu(cpu2, cpumask);
+       ret = stop_cpus(cpumask, fn, arg);
+       free_cpumask_var(cpumask);
+       return ret;
 }
 
 /**
@@ -355,7 +313,7 @@ static void queue_stop_cpus_work(const struct cpumask 
*cpumask,
         * preempted by a stopper which might wait for other stoppers
         * to enter @fn which can lead to deadlock.
         */
-       lg_global_lock(&stop_cpus_lock);
+       preempt_disable();
        for_each_cpu(cpu, cpumask) {
                work = &per_cpu(cpu_stopper.stop_work, cpu);
                work->fn = fn;
@@ -363,7 +321,7 @@ static void queue_stop_cpus_work(const struct cpumask 
*cpumask,
                work->done = done;
                cpu_stop_queue_work(cpu, work);
        }
-       lg_global_unlock(&stop_cpus_lock);
+       preempt_enable();
 }
 
 static int __stop_cpus(const struct cpumask *cpumask,
-- 
1.5.5.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to