diff options
| author | Nick Piggin <npiggin@suse.de> | 2009-12-15 19:47:30 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-16 10:20:09 -0500 |
| commit | d4212093dca95c1f52197017d969cc66d5d962aa (patch) | |
| tree | f52484ae3c7b3034715b5745ae2b7db3906ab022 /ipc | |
| parent | 9cad200c7686708b326520a45dd680a4147568a6 (diff) | |
ipc/sem.c: sem preempt improve
The strange sysv semaphore wakeup scheme has a kind of busy-wait lock
involved, which could deadlock if preemption is enabled during the "lock".
It is an implementation detail (due to a spinlock being held) that this is
actually the case. However if "spinlocks" are made preemptible, or if the
sem lock is changed to a sleeping lock for example, then the wakeup would
become buggy. So this might be a bugfix for -rt kernels.
Imagine waker being preempted by wakee and never clearing IN_WAKEUP -- if
wakee has higher RT priority then there is a priority inversion deadlock.
Even if there is not a priority inversion to cause a deadlock, then there
is still time wasted spinning.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
Cc: Pierre Peiffer <peifferp@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
| -rw-r--r-- | ipc/sem.c | 38 |
1 files changed, 23 insertions, 15 deletions
| @@ -398,6 +398,27 @@ undo: | |||
| 398 | return result; | 398 | return result; |
| 399 | } | 399 | } |
| 400 | 400 | ||
| 401 | /* | ||
| 402 | * Wake up a process waiting on the sem queue with a given error. | ||
| 403 | * The queue is invalid (may not be accessed) after the function returns. | ||
| 404 | */ | ||
| 405 | static void wake_up_sem_queue(struct sem_queue *q, int error) | ||
| 406 | { | ||
| 407 | /* | ||
| 408 | * Hold preempt off so that we don't get preempted and have the | ||
| 409 | * wakee busy-wait until we're scheduled back on. We're holding | ||
| 410 | * locks here so it may not strictly be needed, however if the | ||
| 411 | * locks become preemptible then this prevents such a problem. | ||
| 412 | */ | ||
| 413 | preempt_disable(); | ||
| 414 | q->status = IN_WAKEUP; | ||
| 415 | wake_up_process(q->sleeper); | ||
| 416 | /* hands-off: q can disappear immediately after writing q->status. */ | ||
| 417 | smp_wmb(); | ||
| 418 | q->status = error; | ||
| 419 | preempt_enable(); | ||
| 420 | } | ||
| 421 | |||
| 401 | /* Go through the pending queue for the indicated semaphore | 422 | /* Go through the pending queue for the indicated semaphore |
| 402 | * looking for tasks that can be completed. | 423 | * looking for tasks that can be completed. |
| 403 | */ | 424 | */ |
| @@ -429,17 +450,7 @@ again: | |||
| 429 | * continue. | 450 | * continue. |
| 430 | */ | 451 | */ |
| 431 | alter = q->alter; | 452 | alter = q->alter; |
| 432 | 453 | wake_up_sem_queue(q, error); | |
| 433 | /* wake up the waiting thread */ | ||
| 434 | q->status = IN_WAKEUP; | ||
| 435 | |||
| 436 | wake_up_process(q->sleeper); | ||
| 437 | /* hands-off: q will disappear immediately after | ||
| 438 | * writing q->status. | ||
| 439 | */ | ||
| 440 | smp_wmb(); | ||
| 441 | q->status = error; | ||
| 442 | |||
| 443 | if (alter) | 454 | if (alter) |
| 444 | goto again; | 455 | goto again; |
| 445 | } | 456 | } |
| @@ -523,10 +534,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | |||
| 523 | list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { | 534 | list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { |
| 524 | list_del(&q->list); | 535 | list_del(&q->list); |
| 525 | 536 | ||
| 526 | q->status = IN_WAKEUP; | 537 | wake_up_sem_queue(q, -EIDRM); |
| 527 | wake_up_process(q->sleeper); /* doesn't sleep */ | ||
| 528 | smp_wmb(); | ||
| 529 | q->status = -EIDRM; /* hands-off q */ | ||
| 530 | } | 538 | } |
| 531 | 539 | ||
| 532 | /* Remove the semaphore set from the IDR */ | 540 | /* Remove the semaphore set from the IDR */ |
