diff options
-rw-r--r-- | ipc/sem.c | 75 |
1 files changed, 31 insertions, 44 deletions
@@ -403,58 +403,45 @@ undo: | |||
403 | */ | 403 | */ |
404 | static void update_queue (struct sem_array * sma) | 404 | static void update_queue (struct sem_array * sma) |
405 | { | 405 | { |
406 | int error; | 406 | struct sem_queue *q, *tq; |
407 | struct sem_queue * q; | 407 | |
408 | again: | ||
409 | list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { | ||
410 | int error; | ||
411 | int alter; | ||
408 | 412 | ||
409 | q = list_entry(sma->sem_pending.next, struct sem_queue, list); | ||
410 | while (&q->list != &sma->sem_pending) { | ||
411 | error = try_atomic_semop(sma, q->sops, q->nsops, | 413 | error = try_atomic_semop(sma, q->sops, q->nsops, |
412 | q->undo, q->pid); | 414 | q->undo, q->pid); |
413 | 415 | ||
414 | /* Does q->sleeper still need to sleep? */ | 416 | /* Does q->sleeper still need to sleep? */ |
415 | if (error <= 0) { | 417 | if (error > 0) |
416 | struct sem_queue *n; | 418 | continue; |
417 | 419 | ||
418 | /* | 420 | list_del(&q->list); |
419 | * Continue scanning. The next operation | ||
420 | * that must be checked depends on the type of the | ||
421 | * completed operation: | ||
422 | * - if the operation modified the array, then | ||
423 | * restart from the head of the queue and | ||
424 | * check for threads that might be waiting | ||
425 | * for semaphore values to become 0. | ||
426 | * - if the operation didn't modify the array, | ||
427 | * then just continue. | ||
428 | * The order of list_del() and reading ->next | ||
429 | * is crucial: In the former case, the list_del() | ||
430 | * must be done first [because we might be the | ||
431 | * first entry in ->sem_pending], in the latter | ||
432 | * case the list_del() must be done last | ||
433 | * [because the list is invalid after the list_del()] | ||
434 | */ | ||
435 | if (q->alter) { | ||
436 | list_del(&q->list); | ||
437 | n = list_entry(sma->sem_pending.next, | ||
438 | struct sem_queue, list); | ||
439 | } else { | ||
440 | n = list_entry(q->list.next, struct sem_queue, | ||
441 | list); | ||
442 | list_del(&q->list); | ||
443 | } | ||
444 | 421 | ||
445 | /* wake up the waiting thread */ | 422 | /* |
446 | q->status = IN_WAKEUP; | 423 | * The next operation that must be checked depends on the type |
424 | * of the completed operation: | ||
425 | * - if the operation modified the array, then restart from the | ||
426 | * head of the queue and check for threads that might be | ||
427 | * waiting for semaphore values to become 0. | ||
428 | * - if the operation didn't modify the array, then just | ||
429 | * continue. | ||
430 | */ | ||
431 | alter = q->alter; | ||
432 | |||
433 | /* wake up the waiting thread */ | ||
434 | q->status = IN_WAKEUP; | ||
447 | 435 | ||
448 | wake_up_process(q->sleeper); | 436 | wake_up_process(q->sleeper); |
449 | /* hands-off: q will disappear immediately after | 437 | /* hands-off: q will disappear immediately after |
450 | * writing q->status. | 438 | * writing q->status. |
451 | */ | 439 | */ |
452 | smp_wmb(); | 440 | smp_wmb(); |
453 | q->status = error; | 441 | q->status = error; |
454 | q = n; | 442 | |
455 | } else { | 443 | if (alter) |
456 | q = list_entry(q->list.next, struct sem_queue, list); | 444 | goto again; |
457 | } | ||
458 | } | 445 | } |
459 | } | 446 | } |
460 | 447 | ||