aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipc/sem.c38
1 files changed, 23 insertions, 15 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index d377b3adfc3d..2705fbbc4374 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -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 */
405static 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 */