Commit 8cbc52d2 authored by Linus Torvalds's avatar Linus Torvalds

Be more careful about semaphore contention memory ordering.

Don't touch the wakee stack after marking it runnable.
parent 612af3ad
...@@ -45,12 +45,13 @@ void fastcall init_rwsem(struct rw_semaphore *sem) ...@@ -45,12 +45,13 @@ void fastcall init_rwsem(struct rw_semaphore *sem)
* - the 'active count' _reached_ zero * - the 'active count' _reached_ zero
* - the 'waiting count' is non-zero * - the 'waiting count' is non-zero
* - the spinlock must be held by the caller * - the spinlock must be held by the caller
* - woken process blocks are discarded from the list after having flags zeroised * - woken process blocks are discarded from the list after having task zeroed
* - writers are only woken if wakewrite is non-zero * - writers are only woken if wakewrite is non-zero
*/ */
static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int wakewrite) static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int wakewrite)
{ {
struct rwsem_waiter *waiter; struct rwsem_waiter *waiter;
struct task_struct *tsk;
int woken; int woken;
rwsemtrace(sem,"Entering __rwsem_do_wake"); rwsemtrace(sem,"Entering __rwsem_do_wake");
...@@ -70,8 +71,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int ...@@ -70,8 +71,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int
if (waiter->flags & RWSEM_WAITING_FOR_WRITE) { if (waiter->flags & RWSEM_WAITING_FOR_WRITE) {
sem->activity = -1; sem->activity = -1;
list_del(&waiter->list); list_del(&waiter->list);
waiter->flags = 0; mb();
wake_up_process(waiter->task); tsk = waiter->task;
waiter->task = NULL;
wake_up_process(tsk);
goto out; goto out;
} }
...@@ -82,8 +85,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int ...@@ -82,8 +85,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int
struct list_head *next = waiter->list.next; struct list_head *next = waiter->list.next;
list_del(&waiter->list); list_del(&waiter->list);
waiter->flags = 0; mb();
wake_up_process(waiter->task); tsk = waiter->task;
waiter->task = NULL;
wake_up_process(tsk);
woken++; woken++;
if (list_empty(&sem->wait_list)) if (list_empty(&sem->wait_list))
break; break;
...@@ -103,14 +108,17 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int ...@@ -103,14 +108,17 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int
static inline struct rw_semaphore *__rwsem_wake_one_writer(struct rw_semaphore *sem) static inline struct rw_semaphore *__rwsem_wake_one_writer(struct rw_semaphore *sem)
{ {
struct rwsem_waiter *waiter; struct rwsem_waiter *waiter;
struct task_struct *tsk;
sem->activity = -1; sem->activity = -1;
waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list); waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list);
list_del(&waiter->list); list_del(&waiter->list);
waiter->flags = 0; mb();
wake_up_process(waiter->task); tsk = waiter->task;
waiter->task = NULL;
wake_up_process(tsk);
return sem; return sem;
} }
...@@ -147,7 +155,7 @@ void fastcall __down_read(struct rw_semaphore *sem) ...@@ -147,7 +155,7 @@ void fastcall __down_read(struct rw_semaphore *sem)
/* wait to be given the lock */ /* wait to be given the lock */
for (;;) { for (;;) {
if (!waiter.flags) if (!waiter.task)
break; break;
schedule(); schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE); set_task_state(tsk, TASK_UNINTERRUPTIBLE);
...@@ -215,7 +223,7 @@ void fastcall __down_write(struct rw_semaphore *sem) ...@@ -215,7 +223,7 @@ void fastcall __down_write(struct rw_semaphore *sem)
/* wait to be given the lock */ /* wait to be given the lock */
for (;;) { for (;;) {
if (!waiter.flags) if (!waiter.task)
break; break;
schedule(); schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE); set_task_state(tsk, TASK_UNINTERRUPTIBLE);
......
...@@ -34,12 +34,13 @@ void rwsemtrace(struct rw_semaphore *sem, const char *str) ...@@ -34,12 +34,13 @@ void rwsemtrace(struct rw_semaphore *sem, const char *str)
* - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so) * - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so)
* - there must be someone on the queue * - there must be someone on the queue
* - the spinlock must be held by the caller * - the spinlock must be held by the caller
* - woken process blocks are discarded from the list after having flags zeroised * - woken process blocks are discarded from the list after having task zeroed
* - writers are only woken if wakewrite is non-zero * - writers are only woken if wakewrite is non-zero
*/ */
static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int wakewrite) static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int wakewrite)
{ {
struct rwsem_waiter *waiter; struct rwsem_waiter *waiter;
struct task_struct *tsk;
struct list_head *next; struct list_head *next;
signed long oldcount, woken, loop; signed long oldcount, woken, loop;
...@@ -64,8 +65,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int ...@@ -64,8 +65,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int
goto readers_only; goto readers_only;
list_del(&waiter->list); list_del(&waiter->list);
waiter->flags = 0; mb();
wake_up_process(waiter->task); tsk = waiter->task;
waiter->task = NULL;
wake_up_process(tsk);
goto out; goto out;
/* don't want to wake any writers */ /* don't want to wake any writers */
...@@ -99,8 +102,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int ...@@ -99,8 +102,10 @@ static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem, int
for (; loop>0; loop--) { for (; loop>0; loop--) {
waiter = list_entry(next,struct rwsem_waiter,list); waiter = list_entry(next,struct rwsem_waiter,list);
next = waiter->list.next; next = waiter->list.next;
waiter->flags = 0; mb();
wake_up_process(waiter->task); tsk = waiter->task;
waiter->task = NULL;
wake_up_process(tsk);
} }
sem->wait_list.next = next; sem->wait_list.next = next;
...@@ -148,7 +153,7 @@ static inline struct rw_semaphore *rwsem_down_failed_common(struct rw_semaphore ...@@ -148,7 +153,7 @@ static inline struct rw_semaphore *rwsem_down_failed_common(struct rw_semaphore
/* wait to be given the lock */ /* wait to be given the lock */
for (;;) { for (;;) {
if (!waiter->flags) if (!waiter->task)
break; break;
schedule(); schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE); set_task_state(tsk, TASK_UNINTERRUPTIBLE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment