diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 61fef376ed2e6161299719f8b3a7cfaf4f246b33..a946176db638c7198705a20a7d2fd715279dfc2d 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -282,6 +282,43 @@ do { \ 1 : ({ local_irq_restore(flags); 0; }); \ }) +/* + * Locks two spinlocks l1 and l2. + * l1_first indicates if spinlock l1 should be taken first. + */ +static inline void double_spin_lock(spinlock_t *l1, spinlock_t *l2, + bool l1_first) + __acquires(l1) + __acquires(l2) +{ + if (l1_first) { + spin_lock(l1); + spin_lock(l2); + } else { + spin_lock(l2); + spin_lock(l1); + } +} + +/* + * Unlocks two spinlocks l1 and l2. + * l1_taken_first indicates if spinlock l1 was taken first and therefore + * should be released after spinlock l2. + */ +static inline void double_spin_unlock(spinlock_t *l1, spinlock_t *l2, + bool l1_taken_first) + __releases(l1) + __releases(l2) +{ + if (l1_taken_first) { + spin_unlock(l2); + spin_unlock(l1); + } else { + spin_unlock(l1); + spin_unlock(l2); + } +} + /* * Pull the atomic_t declaration: * (asm-mips/atomic.h needs above definitions) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 476cb0c0b4a432bce65c1f113011aa60cd3060b0..de93a8176ca61209c342ff68eb14174230f74e0b 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1355,17 +1355,16 @@ static void migrate_hrtimers(int cpu) tick_cancel_sched_timer(cpu); local_irq_disable(); - - spin_lock(&new_base->lock); - spin_lock(&old_base->lock); + double_spin_lock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { migrate_hrtimer_list(&old_base->clock_base[i], &new_base->clock_base[i]); } - spin_unlock(&old_base->lock); - spin_unlock(&new_base->lock); + double_spin_unlock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); local_irq_enable(); put_cpu_var(hrtimer_bases); } diff --git a/kernel/timer.c b/kernel/timer.c index 6663a87f7304742b7f8cef44a39f2ea0873c511b..8ad384253ef2692e58060c7087ed58cf4161ce93 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1651,8 +1651,8 @@ static void __devinit migrate_timers(int cpu) new_base = get_cpu_var(tvec_bases); local_irq_disable(); - spin_lock(&new_base->lock); - spin_lock(&old_base->lock); + double_spin_lock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); BUG_ON(old_base->running_timer); @@ -1665,8 +1665,8 @@ static void __devinit migrate_timers(int cpu) migrate_timer_list(new_base, old_base->tv5.vec + i); } - spin_unlock(&old_base->lock); - spin_unlock(&new_base->lock); + double_spin_unlock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); local_irq_enable(); put_cpu_var(tvec_bases); }