aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/locking
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2016-05-12 07:57:45 -0400
committerIngo Molnar <mingo@kernel.org>2016-05-15 16:55:00 -0400
commit04cafed7fc19a8010771c788708ac97c405fc3de (patch)
treed1e92439bc057cbb202fdf1138be1c4f2d3d0156 /kernel/locking
parent00fb16e26ac8559e69c3bb14284f4a548d28ee0d (diff)
locking/rwsem: Fix down_write_killable()
The new signal_pending exit path in __rwsem_down_write_failed_common() was fingered as breaking his kernel by Tetsuo Handa. Upon inspection it was found that there are two things wrong with it; - it forgets to remove WAITING_BIAS if it leaves the list empty, or - it forgets to wake further waiters that were blocked on the now removed waiter. Especially the first issue causes new lock attempts to block and stall indefinitely, as the code assumes that pending waiters mean there is an owner that will wake when it releases the lock. Reported-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Tested-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Tested-by: Michal Hocko <mhocko@kernel.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Chris Zankel <chris@zankel.net> Cc: David S. Miller <davem@davemloft.net> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Max Filippov <jcmvbkbc@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tony Luck <tony.luck@intel.com> Cc: Vince Weaver <vincent.weaver@maine.edu> Cc: Waiman Long <Waiman.Long@hpe.com> Link: http://lkml.kernel.org/r/20160512115745.GP3192@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/locking')
-rw-r--r--kernel/locking/rwsem-xadd.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index df4dcb883b50..09e30c6225e5 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -487,23 +487,32 @@ __rwsem_down_write_failed_common(struct rw_semaphore *sem, int state)
487 487
488 /* Block until there are no active lockers. */ 488 /* Block until there are no active lockers. */
489 do { 489 do {
490 if (signal_pending_state(state, current)) { 490 if (signal_pending_state(state, current))
491 raw_spin_lock_irq(&sem->wait_lock); 491 goto out_nolock;
492 ret = ERR_PTR(-EINTR); 492
493 goto out;
494 }
495 schedule(); 493 schedule();
496 set_current_state(state); 494 set_current_state(state);
497 } while ((count = sem->count) & RWSEM_ACTIVE_MASK); 495 } while ((count = sem->count) & RWSEM_ACTIVE_MASK);
498 496
499 raw_spin_lock_irq(&sem->wait_lock); 497 raw_spin_lock_irq(&sem->wait_lock);
500 } 498 }
501out:
502 __set_current_state(TASK_RUNNING); 499 __set_current_state(TASK_RUNNING);
503 list_del(&waiter.list); 500 list_del(&waiter.list);
504 raw_spin_unlock_irq(&sem->wait_lock); 501 raw_spin_unlock_irq(&sem->wait_lock);
505 502
506 return ret; 503 return ret;
504
505out_nolock:
506 __set_current_state(TASK_RUNNING);
507 raw_spin_lock_irq(&sem->wait_lock);
508 list_del(&waiter.list);
509 if (list_empty(&sem->wait_list))
510 rwsem_atomic_update(-RWSEM_WAITING_BIAS, sem);
511 else
512 __rwsem_do_wake(sem, RWSEM_WAKE_ANY);
513 raw_spin_unlock_irq(&sem->wait_lock);
514
515 return ERR_PTR(-EINTR);
507} 516}
508 517
509__visible struct rw_semaphore * __sched 518__visible struct rw_semaphore * __sched