@@ -381,7 +381,6 @@ static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
381381 sop -- ;
382382 }
383383
384- sma -> sem_otime = get_seconds ();
385384 return 0 ;
386385
387386out_of_range :
@@ -404,25 +403,51 @@ static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
404403 return result ;
405404}
406405
407- /*
408- * Wake up a process waiting on the sem queue with a given error.
409- * The queue is invalid (may not be accessed) after the function returns.
406+ /** wake_up_sem_queue_prepare(q, error): Prepare wake-up
407+ * @q: queue entry that must be signaled
408+ * @error: Error value for the signal
409+ *
410+ * Prepare the wake-up of the queue entry q.
410411 */
411- static void wake_up_sem_queue (struct sem_queue * q , int error )
412+ static void wake_up_sem_queue_prepare (struct list_head * pt ,
413+ struct sem_queue * q , int error )
412414{
413- /*
414- * Hold preempt off so that we don't get preempted and have the
415- * wakee busy-wait until we're scheduled back on. We're holding
416- * locks here so it may not strictly be needed, however if the
417- * locks become preemptible then this prevents such a problem.
418- */
419- preempt_disable ();
415+ if ( list_empty ( pt )) {
416+ /*
417+ * Hold preempt off so that we don't get preempted and have the
418+ * wakee busy-wait until we're scheduled back on.
419+ */
420+ preempt_disable ();
421+ }
420422 q -> status = IN_WAKEUP ;
421- wake_up_process (q -> sleeper );
422- /* hands-off: q can disappear immediately after writing q->status. */
423- smp_wmb ();
424- q -> status = error ;
425- preempt_enable ();
423+ q -> pid = error ;
424+
425+ list_add_tail (& q -> simple_list , pt );
426+ }
427+
428+ /**
429+ * wake_up_sem_queue_do(pt) - do the actual wake-up
430+ * @pt: list of tasks to be woken up
431+ *
432+ * Do the actual wake-up.
433+ * The function is called without any locks held, thus the semaphore array
434+ * could be destroyed already and the tasks can disappear as soon as the
435+ * status is set to the actual return code.
436+ */
437+ static void wake_up_sem_queue_do (struct list_head * pt )
438+ {
439+ struct sem_queue * q , * t ;
440+ int did_something ;
441+
442+ did_something = !list_empty (pt );
443+ list_for_each_entry_safe (q , t , pt , simple_list ) {
444+ wake_up_process (q -> sleeper );
445+ /* q can disappear immediately after writing q->status. */
446+ smp_wmb ();
447+ q -> status = q -> pid ;
448+ }
449+ if (did_something )
450+ preempt_enable ();
426451}
427452
428453static void unlink_queue (struct sem_array * sma , struct sem_queue * q )
@@ -502,17 +527,22 @@ static int check_restart(struct sem_array *sma, struct sem_queue *q)
502527 * update_queue(sma, semnum): Look for tasks that can be completed.
503528 * @sma: semaphore array.
504529 * @semnum: semaphore that was modified.
530+ * @pt: list head for the tasks that must be woken up.
505531 *
506532 * update_queue must be called after a semaphore in a semaphore array
507533 * was modified. If multiple semaphore were modified, then @semnum
508534 * must be set to -1.
535+ * The tasks that must be woken up are added to @pt. The return code
536+ * is stored in q->pid.
537+ * The function return 1 if at least one semop was completed successfully.
509538 */
510- static void update_queue (struct sem_array * sma , int semnum )
539+ static int update_queue (struct sem_array * sma , int semnum , struct list_head * pt )
511540{
512541 struct sem_queue * q ;
513542 struct list_head * walk ;
514543 struct list_head * pending_list ;
515544 int offset ;
545+ int semop_completed = 0 ;
516546
517547 /* if there are complex operations around, then knowing the semaphore
518548 * that was modified doesn't help us. Assume that multiple semaphores
@@ -557,40 +587,55 @@ static void update_queue(struct sem_array *sma, int semnum)
557587
558588 unlink_queue (sma , q );
559589
560- if (error )
590+ if (error ) {
561591 restart = 0 ;
562- else
592+ } else {
593+ semop_completed = 1 ;
563594 restart = check_restart (sma , q );
595+ }
564596
565- wake_up_sem_queue ( q , error );
597+ wake_up_sem_queue_prepare ( pt , q , error );
566598 if (restart )
567599 goto again ;
568600 }
601+ return semop_completed ;
569602}
570603
571- /** do_smart_update(sma, sops, nsops): Optimized update_queue
604+ /**
605+ * do_smart_update(sma, sops, nsops, otime, pt) - optimized update_queue
572606 * @sma: semaphore array
573607 * @sops: operations that were performed
574608 * @nsops: number of operations
609+ * @otime: force setting otime
610+ * @pt: list head of the tasks that must be woken up.
575611 *
576612 * do_smart_update() does the required called to update_queue, based on the
577613 * actual changes that were performed on the semaphore array.
614+ * Note that the function does not do the actual wake-up: the caller is
615+ * responsible for calling wake_up_sem_queue_do(@pt).
616+ * It is safe to perform this call after dropping all locks.
578617 */
579- static void do_smart_update (struct sem_array * sma , struct sembuf * sops , int nsops )
618+ static void do_smart_update (struct sem_array * sma , struct sembuf * sops , int nsops ,
619+ int otime , struct list_head * pt )
580620{
581621 int i ;
582622
583623 if (sma -> complex_count || sops == NULL ) {
584- update_queue (sma , -1 );
585- return ;
624+ if (update_queue (sma , -1 , pt ))
625+ otime = 1 ;
626+ goto done ;
586627 }
587628
588629 for (i = 0 ; i < nsops ; i ++ ) {
589630 if (sops [i ].sem_op > 0 ||
590631 (sops [i ].sem_op < 0 &&
591632 sma -> sem_base [sops [i ].sem_num ].semval == 0 ))
592- update_queue (sma , sops [i ].sem_num );
633+ if (update_queue (sma , sops [i ].sem_num , pt ))
634+ otime = 1 ;
593635 }
636+ done :
637+ if (otime )
638+ sma -> sem_otime = get_seconds ();
594639}
595640
596641
@@ -656,6 +701,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
656701 struct sem_undo * un , * tu ;
657702 struct sem_queue * q , * tq ;
658703 struct sem_array * sma = container_of (ipcp , struct sem_array , sem_perm );
704+ struct list_head tasks ;
659705
660706 /* Free the existing undo structures for this semaphore set. */
661707 assert_spin_locked (& sma -> sem_perm .lock );
@@ -669,15 +715,17 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
669715 }
670716
671717 /* Wake up all pending processes and let them fail with EIDRM. */
718+ INIT_LIST_HEAD (& tasks );
672719 list_for_each_entry_safe (q , tq , & sma -> sem_pending , list ) {
673720 unlink_queue (sma , q );
674- wake_up_sem_queue ( q , - EIDRM );
721+ wake_up_sem_queue_prepare ( & tasks , q , - EIDRM );
675722 }
676723
677724 /* Remove the semaphore set from the IDR */
678725 sem_rmid (ns , sma );
679726 sem_unlock (sma );
680727
728+ wake_up_sem_queue_do (& tasks );
681729 ns -> used_sems -= sma -> sem_nsems ;
682730 security_sem_free (sma );
683731 ipc_rcu_putref (sma );
@@ -799,11 +847,13 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
799847 ushort fast_sem_io [SEMMSL_FAST ];
800848 ushort * sem_io = fast_sem_io ;
801849 int nsems ;
850+ struct list_head tasks ;
802851
803852 sma = sem_lock_check (ns , semid );
804853 if (IS_ERR (sma ))
805854 return PTR_ERR (sma );
806855
856+ INIT_LIST_HEAD (& tasks );
807857 nsems = sma -> sem_nsems ;
808858
809859 err = - EACCES ;
@@ -891,7 +941,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
891941 }
892942 sma -> sem_ctime = get_seconds ();
893943 /* maybe some queued-up processes were waiting for this */
894- update_queue (sma , -1 );
944+ do_smart_update (sma , NULL , 0 , 0 , & tasks );
895945 err = 0 ;
896946 goto out_unlock ;
897947 }
@@ -933,13 +983,15 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
933983 curr -> sempid = task_tgid_vnr (current );
934984 sma -> sem_ctime = get_seconds ();
935985 /* maybe some queued-up processes were waiting for this */
936- update_queue (sma , semnum );
986+ do_smart_update (sma , NULL , 0 , 0 , & tasks );
937987 err = 0 ;
938988 goto out_unlock ;
939989 }
940990 }
941991out_unlock :
942992 sem_unlock (sma );
993+ wake_up_sem_queue_do (& tasks );
994+
943995out_free :
944996 if (sem_io != fast_sem_io )
945997 ipc_free (sem_io , sizeof (ushort )* nsems );
@@ -1213,6 +1265,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
12131265 struct sem_queue queue ;
12141266 unsigned long jiffies_left = 0 ;
12151267 struct ipc_namespace * ns ;
1268+ struct list_head tasks ;
12161269
12171270 ns = current -> nsproxy -> ipc_ns ;
12181271
@@ -1261,6 +1314,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
12611314 } else
12621315 un = NULL ;
12631316
1317+ INIT_LIST_HEAD (& tasks );
1318+
12641319 sma = sem_lock_check (ns , semid );
12651320 if (IS_ERR (sma )) {
12661321 if (un )
@@ -1309,7 +1364,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
13091364 error = try_atomic_semop (sma , sops , nsops , un , task_tgid_vnr (current ));
13101365 if (error <= 0 ) {
13111366 if (alter && error == 0 )
1312- do_smart_update (sma , sops , nsops );
1367+ do_smart_update (sma , sops , nsops , 1 , & tasks );
13131368
13141369 goto out_unlock_free ;
13151370 }
@@ -1386,6 +1441,8 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
13861441
13871442out_unlock_free :
13881443 sem_unlock (sma );
1444+
1445+ wake_up_sem_queue_do (& tasks );
13891446out_free :
13901447 if (sops != fast_sops )
13911448 kfree (sops );
@@ -1446,6 +1503,7 @@ void exit_sem(struct task_struct *tsk)
14461503 for (;;) {
14471504 struct sem_array * sma ;
14481505 struct sem_undo * un ;
1506+ struct list_head tasks ;
14491507 int semid ;
14501508 int i ;
14511509
@@ -1509,10 +1567,11 @@ void exit_sem(struct task_struct *tsk)
15091567 semaphore -> sempid = task_tgid_vnr (current );
15101568 }
15111569 }
1512- sma -> sem_otime = get_seconds ();
15131570 /* maybe some queued-up processes were waiting for this */
1514- update_queue (sma , -1 );
1571+ INIT_LIST_HEAD (& tasks );
1572+ do_smart_update (sma , NULL , 0 , 1 , & tasks );
15151573 sem_unlock (sma );
1574+ wake_up_sem_queue_do (& tasks );
15161575
15171576 call_rcu (& un -> rcu , free_un );
15181577 }
0 commit comments