Messages in this thread Patch in this message | | | Date | Fri, 26 Jun 2015 04:15:22 +0200 | From | Oleg Nesterov <> | Subject | [RFC PATCH 5/6] stop_machine: change stop_two_cpus() just use stop_cpu(), kill lg_double_lock/unlock |
| |
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 <oleg@redhat.com> --- 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
| |