aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/sem.h12
-rw-r--r--ipc/sem.c92
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. */
103struct sem_queue { 102struct 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};
diff --git a/ipc/sem.c b/ipc/sem.c
index d5ce4000ca17..3ca232736b31 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -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 */
337static 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
344static 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
355static 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
506static int count_semzcnt (struct sem_array * sma, ushort semnum) 488static 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)
529static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 511static 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
1218out_unlock_free: 1196out_unlock_free: