diff options
-rw-r--r-- | include/linux/sem.h | 12 | ||||
-rw-r--r-- | ipc/sem.c | 92 |
2 files changed, 40 insertions, 64 deletions
diff --git a/include/linux/sem.h b/include/linux/sem.h index 87756ef1198e..d42599395d79 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h | |||
@@ -93,21 +93,19 @@ struct sem_array { | |||
93 | time_t sem_otime; /* last semop time */ | 93 | time_t sem_otime; /* last semop time */ |
94 | time_t sem_ctime; /* last change time */ | 94 | time_t sem_ctime; /* last change time */ |
95 | struct sem *sem_base; /* ptr to first semaphore in array */ | 95 | struct sem *sem_base; /* ptr to first semaphore in array */ |
96 | struct sem_queue *sem_pending; /* pending operations to be processed */ | 96 | struct list_head sem_pending; /* pending operations to be processed */ |
97 | struct sem_queue **sem_pending_last; /* last pending operation */ | ||
98 | struct list_head list_id; /* undo requests on this array */ | 97 | struct list_head list_id; /* undo requests on this array */ |
99 | unsigned long sem_nsems; /* no. of semaphores in array */ | 98 | unsigned long sem_nsems; /* no. of semaphores in array */ |
100 | }; | 99 | }; |
101 | 100 | ||
102 | /* One queue for each sleeping process in the system. */ | 101 | /* One queue for each sleeping process in the system. */ |
103 | struct sem_queue { | 102 | struct sem_queue { |
104 | struct sem_queue * next; /* next entry in the queue */ | 103 | struct list_head list; /* queue of pending operations */ |
105 | struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */ | 104 | struct task_struct *sleeper; /* this process */ |
106 | struct task_struct* sleeper; /* this process */ | 105 | struct sem_undo *undo; /* undo structure */ |
107 | struct sem_undo * undo; /* undo structure */ | ||
108 | int pid; /* process id of requesting process */ | 106 | int pid; /* process id of requesting process */ |
109 | int status; /* completion status of operation */ | 107 | int status; /* completion status of operation */ |
110 | struct sembuf * sops; /* array of pending operations */ | 108 | struct sembuf *sops; /* array of pending operations */ |
111 | int nsops; /* number of operations */ | 109 | int nsops; /* number of operations */ |
112 | int alter; /* does the operation alter the array? */ | 110 | int alter; /* does the operation alter the array? */ |
113 | }; | 111 | }; |
@@ -272,8 +272,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) | |||
272 | ns->used_sems += nsems; | 272 | ns->used_sems += nsems; |
273 | 273 | ||
274 | sma->sem_base = (struct sem *) &sma[1]; | 274 | sma->sem_base = (struct sem *) &sma[1]; |
275 | /* sma->sem_pending = NULL; */ | 275 | INIT_LIST_HEAD(&sma->sem_pending); |
276 | sma->sem_pending_last = &sma->sem_pending; | ||
277 | INIT_LIST_HEAD(&sma->list_id); | 276 | INIT_LIST_HEAD(&sma->list_id); |
278 | sma->sem_nsems = nsems; | 277 | sma->sem_nsems = nsems; |
279 | sma->sem_ctime = get_seconds(); | 278 | sma->sem_ctime = get_seconds(); |
@@ -331,38 +330,6 @@ asmlinkage long sys_semget(key_t key, int nsems, int semflg) | |||
331 | return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); | 330 | return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); |
332 | } | 331 | } |
333 | 332 | ||
334 | /* Manage the doubly linked list sma->sem_pending as a FIFO: | ||
335 | * insert new queue elements at the tail sma->sem_pending_last. | ||
336 | */ | ||
337 | static inline void append_to_queue (struct sem_array * sma, | ||
338 | struct sem_queue * q) | ||
339 | { | ||
340 | *(q->prev = sma->sem_pending_last) = q; | ||
341 | *(sma->sem_pending_last = &q->next) = NULL; | ||
342 | } | ||
343 | |||
344 | static inline void prepend_to_queue (struct sem_array * sma, | ||
345 | struct sem_queue * q) | ||
346 | { | ||
347 | q->next = sma->sem_pending; | ||
348 | *(q->prev = &sma->sem_pending) = q; | ||
349 | if (q->next) | ||
350 | q->next->prev = &q->next; | ||
351 | else /* sma->sem_pending_last == &sma->sem_pending */ | ||
352 | sma->sem_pending_last = &q->next; | ||
353 | } | ||
354 | |||
355 | static inline void remove_from_queue (struct sem_array * sma, | ||
356 | struct sem_queue * q) | ||
357 | { | ||
358 | *(q->prev) = q->next; | ||
359 | if (q->next) | ||
360 | q->next->prev = q->prev; | ||
361 | else /* sma->sem_pending_last == &q->next */ | ||
362 | sma->sem_pending_last = q->prev; | ||
363 | q->prev = NULL; /* mark as removed */ | ||
364 | } | ||
365 | |||
366 | /* | 333 | /* |
367 | * Determine whether a sequence of semaphore operations would succeed | 334 | * Determine whether a sequence of semaphore operations would succeed |
368 | * all at once. Return 0 if yes, 1 if need to sleep, else return error code. | 335 | * all at once. Return 0 if yes, 1 if need to sleep, else return error code. |
@@ -438,16 +405,15 @@ static void update_queue (struct sem_array * sma) | |||
438 | int error; | 405 | int error; |
439 | struct sem_queue * q; | 406 | struct sem_queue * q; |
440 | 407 | ||
441 | q = sma->sem_pending; | 408 | q = list_entry(sma->sem_pending.next, struct sem_queue, list); |
442 | while(q) { | 409 | while (&q->list != &sma->sem_pending) { |
443 | error = try_atomic_semop(sma, q->sops, q->nsops, | 410 | error = try_atomic_semop(sma, q->sops, q->nsops, |
444 | q->undo, q->pid); | 411 | q->undo, q->pid); |
445 | 412 | ||
446 | /* Does q->sleeper still need to sleep? */ | 413 | /* Does q->sleeper still need to sleep? */ |
447 | if (error <= 0) { | 414 | if (error <= 0) { |
448 | struct sem_queue *n; | 415 | struct sem_queue *n; |
449 | remove_from_queue(sma,q); | 416 | |
450 | q->status = IN_WAKEUP; | ||
451 | /* | 417 | /* |
452 | * Continue scanning. The next operation | 418 | * Continue scanning. The next operation |
453 | * that must be checked depends on the type of the | 419 | * that must be checked depends on the type of the |
@@ -458,11 +424,26 @@ static void update_queue (struct sem_array * sma) | |||
458 | * for semaphore values to become 0. | 424 | * for semaphore values to become 0. |
459 | * - if the operation didn't modify the array, | 425 | * - if the operation didn't modify the array, |
460 | * then just continue. | 426 | * then just continue. |
427 | * The order of list_del() and reading ->next | ||
428 | * is crucial: In the former case, the list_del() | ||
429 | * must be done first [because we might be the | ||
430 | * first entry in ->sem_pending], in the latter | ||
431 | * case the list_del() must be done last | ||
432 | * [because the list is invalid after the list_del()] | ||
461 | */ | 433 | */ |
462 | if (q->alter) | 434 | if (q->alter) { |
463 | n = sma->sem_pending; | 435 | list_del(&q->list); |
464 | else | 436 | n = list_entry(sma->sem_pending.next, |
465 | n = q->next; | 437 | struct sem_queue, list); |
438 | } else { | ||
439 | n = list_entry(q->list.next, struct sem_queue, | ||
440 | list); | ||
441 | list_del(&q->list); | ||
442 | } | ||
443 | |||
444 | /* wake up the waiting thread */ | ||
445 | q->status = IN_WAKEUP; | ||
446 | |||
466 | wake_up_process(q->sleeper); | 447 | wake_up_process(q->sleeper); |
467 | /* hands-off: q will disappear immediately after | 448 | /* hands-off: q will disappear immediately after |
468 | * writing q->status. | 449 | * writing q->status. |
@@ -471,7 +452,7 @@ static void update_queue (struct sem_array * sma) | |||
471 | q->status = error; | 452 | q->status = error; |
472 | q = n; | 453 | q = n; |
473 | } else { | 454 | } else { |
474 | q = q->next; | 455 | q = list_entry(q->list.next, struct sem_queue, list); |
475 | } | 456 | } |
476 | } | 457 | } |
477 | } | 458 | } |
@@ -491,7 +472,7 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) | |||
491 | struct sem_queue * q; | 472 | struct sem_queue * q; |
492 | 473 | ||
493 | semncnt = 0; | 474 | semncnt = 0; |
494 | for (q = sma->sem_pending; q; q = q->next) { | 475 | list_for_each_entry(q, &sma->sem_pending, list) { |
495 | struct sembuf * sops = q->sops; | 476 | struct sembuf * sops = q->sops; |
496 | int nsops = q->nsops; | 477 | int nsops = q->nsops; |
497 | int i; | 478 | int i; |
@@ -503,13 +484,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) | |||
503 | } | 484 | } |
504 | return semncnt; | 485 | return semncnt; |
505 | } | 486 | } |
487 | |||
506 | static int count_semzcnt (struct sem_array * sma, ushort semnum) | 488 | static int count_semzcnt (struct sem_array * sma, ushort semnum) |
507 | { | 489 | { |
508 | int semzcnt; | 490 | int semzcnt; |
509 | struct sem_queue * q; | 491 | struct sem_queue * q; |
510 | 492 | ||
511 | semzcnt = 0; | 493 | semzcnt = 0; |
512 | for (q = sma->sem_pending; q; q = q->next) { | 494 | list_for_each_entry(q, &sma->sem_pending, list) { |
513 | struct sembuf * sops = q->sops; | 495 | struct sembuf * sops = q->sops; |
514 | int nsops = q->nsops; | 496 | int nsops = q->nsops; |
515 | int i; | 497 | int i; |
@@ -529,7 +511,7 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) | |||
529 | static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | 511 | static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) |
530 | { | 512 | { |
531 | struct sem_undo *un; | 513 | struct sem_undo *un; |
532 | struct sem_queue *q; | 514 | struct sem_queue *q, *t; |
533 | struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); | 515 | struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); |
534 | 516 | ||
535 | /* Invalidate the existing undo structures for this semaphore set. | 517 | /* Invalidate the existing undo structures for this semaphore set. |
@@ -541,17 +523,14 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) | |||
541 | un->semid = -1; | 523 | un->semid = -1; |
542 | 524 | ||
543 | /* Wake up all pending processes and let them fail with EIDRM. */ | 525 | /* Wake up all pending processes and let them fail with EIDRM. */ |
544 | q = sma->sem_pending; | 526 | |
545 | while(q) { | 527 | list_for_each_entry_safe(q, t, &sma->sem_pending, list) { |
546 | struct sem_queue *n; | 528 | list_del(&q->list); |
547 | /* lazy remove_from_queue: we are killing the whole queue */ | 529 | |
548 | q->prev = NULL; | ||
549 | n = q->next; | ||
550 | q->status = IN_WAKEUP; | 530 | q->status = IN_WAKEUP; |
551 | wake_up_process(q->sleeper); /* doesn't sleep */ | 531 | wake_up_process(q->sleeper); /* doesn't sleep */ |
552 | smp_wmb(); | 532 | smp_wmb(); |
553 | q->status = -EIDRM; /* hands-off q */ | 533 | q->status = -EIDRM; /* hands-off q */ |
554 | q = n; | ||
555 | } | 534 | } |
556 | 535 | ||
557 | /* Remove the semaphore set from the IDR */ | 536 | /* Remove the semaphore set from the IDR */ |
@@ -1166,9 +1145,9 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, | |||
1166 | queue.pid = task_tgid_vnr(current); | 1145 | queue.pid = task_tgid_vnr(current); |
1167 | queue.alter = alter; | 1146 | queue.alter = alter; |
1168 | if (alter) | 1147 | if (alter) |
1169 | append_to_queue(sma ,&queue); | 1148 | list_add_tail(&queue.list, &sma->sem_pending); |
1170 | else | 1149 | else |
1171 | prepend_to_queue(sma ,&queue); | 1150 | list_add(&queue.list, &sma->sem_pending); |
1172 | 1151 | ||
1173 | queue.status = -EINTR; | 1152 | queue.status = -EINTR; |
1174 | queue.sleeper = current; | 1153 | queue.sleeper = current; |
@@ -1194,7 +1173,6 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, | |||
1194 | 1173 | ||
1195 | sma = sem_lock(ns, semid); | 1174 | sma = sem_lock(ns, semid); |
1196 | if (IS_ERR(sma)) { | 1175 | if (IS_ERR(sma)) { |
1197 | BUG_ON(queue.prev != NULL); | ||
1198 | error = -EIDRM; | 1176 | error = -EIDRM; |
1199 | goto out_free; | 1177 | goto out_free; |
1200 | } | 1178 | } |
@@ -1212,7 +1190,7 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, | |||
1212 | */ | 1190 | */ |
1213 | if (timeout && jiffies_left == 0) | 1191 | if (timeout && jiffies_left == 0) |
1214 | error = -EAGAIN; | 1192 | error = -EAGAIN; |
1215 | remove_from_queue(sma,&queue); | 1193 | list_del(&queue.list); |
1216 | goto out_unlock_free; | 1194 | goto out_unlock_free; |
1217 | 1195 | ||
1218 | out_unlock_free: | 1196 | out_unlock_free: |