iwlwifi: mvm: wait for handlers when stopping scans
[deliverable/linux.git] / ipc / sem.c
index d73b3eff80f08582c211975f1705d45ba8597d42..454f6c6020a8d98dccb167e46d3a5225d5f4ce2d 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -109,6 +109,7 @@ struct sem_queue {
        int                     pid;     /* process id of requesting process */
        int                     status;  /* completion status of operation */
        struct sembuf           *sops;   /* array of pending operations */
+       struct sembuf           *blocking; /* the operation that blocked */
        int                     nsops;   /* number of operations */
        int                     alter;   /* does *sops alter the array? */
 };
@@ -585,21 +586,23 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
 /**
  * perform_atomic_semop - Perform (if possible) a semaphore operation
  * @sma: semaphore array
- * @sops: array with operations that should be checked
- * @nsops: number of operations
- * @un: undo array
- * @pid: pid that did the change
+ * @q: struct sem_queue that describes the operation
  *
  * Returns 0 if the operation was possible.
  * Returns 1 if the operation is impossible, the caller must sleep.
  * Negative values are error codes.
  */
-static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,
-                            int nsops, struct sem_undo *un, int pid)
+static int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q)
 {
-       int result, sem_op;
+       int result, sem_op, nsops, pid;
        struct sembuf *sop;
        struct sem *curr;
+       struct sembuf *sops;
+       struct sem_undo *un;
+
+       sops = q->sops;
+       nsops = q->nsops;
+       un = q->undo;
 
        for (sop = sops; sop < sops + nsops; sop++) {
                curr = sma->sem_base + sop->sem_num;
@@ -627,6 +630,7 @@ static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,
        }
 
        sop--;
+       pid = q->pid;
        while (sop >= sops) {
                sma->sem_base[sop->sem_num].sempid = pid;
                sop--;
@@ -639,6 +643,8 @@ out_of_range:
        goto undo;
 
 would_block:
+       q->blocking = sop;
+
        if (sop->sem_flg & IPC_NOWAIT)
                result = -EAGAIN;
        else
@@ -779,8 +785,7 @@ static int wake_const_ops(struct sem_array *sma, int semnum,
                q = container_of(walk, struct sem_queue, list);
                walk = walk->next;
 
-               error = perform_atomic_semop(sma, q->sops, q->nsops,
-                                                q->undo, q->pid);
+               error = perform_atomic_semop(sma, q);
 
                if (error <= 0) {
                        /* operation completed, remove from queue & wakeup */
@@ -892,8 +897,7 @@ again:
                if (semnum != -1 && sma->sem_base[semnum].semval == 0)
                        break;
 
-               error = perform_atomic_semop(sma, q->sops, q->nsops,
-                                        q->undo, q->pid);
+               error = perform_atomic_semop(sma, q);
 
                /* Does q->sleeper still need to sleep? */
                if (error > 0)
@@ -989,38 +993,41 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop
 }
 
 /*
- * check_qop: Test how often a queued operation sleeps on the semaphore semnum
+ * check_qop: Test if a queued operation sleeps on the semaphore semnum
  */
 static int check_qop(struct sem_array *sma, int semnum, struct sem_queue *q,
                        bool count_zero)
 {
-       struct sembuf *sops = q->sops;
-       int nsops = q->nsops;
-       int i, semcnt;
+       struct sembuf *sop = q->blocking;
 
-       semcnt = 0;
+       /*
+        * Linux always (since 0.99.10) reported a task as sleeping on all
+        * semaphores. This violates SUS, therefore it was changed to the
+        * standard compliant behavior.
+        * Give the administrators a chance to notice that an application
+        * might misbehave because it relies on the Linux behavior.
+        */
+       pr_info_once("semctl(GETNCNT/GETZCNT) is since 3.16 Single Unix Specification compliant.\n"
+                       "The task %s (%d) triggered the difference, watch for misbehavior.\n",
+                       current->comm, task_pid_nr(current));
 
-       for (i = 0; i < nsops; i++) {
-               if (sops[i].sem_num != semnum)
-                       continue;
-               if (sops[i].sem_flg & IPC_NOWAIT)
-                       continue;
-               if (count_zero && sops[i].sem_op == 0)
-                       semcnt++;
-               if (!count_zero && sops[i].sem_op < 0)
-                       semcnt++;
-       }
-       return semcnt;
+       if (sop->sem_num != semnum)
+               return 0;
+
+       if (count_zero && sop->sem_op == 0)
+               return 1;
+       if (!count_zero && sop->sem_op < 0)
+               return 1;
+
+       return 0;
 }
 
 /* The following counts are associated to each semaphore:
  *   semncnt        number of tasks waiting on semval being nonzero
  *   semzcnt        number of tasks waiting on semval being zero
- * This model assumes that a task waits on exactly one semaphore.
- * Since semaphore operations are to be performed atomically, tasks actually
- * wait on a whole sequence of semaphores simultaneously.
- * The counts we return here are a rough approximation, but still
- * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
+ *
+ * Per definition, a task waits only on the semaphore of the first semop
+ * that cannot proceed, even if additional operation would block, too.
  */
 static int count_semcnt(struct sem_array *sma, ushort semnum,
                        bool count_zero)
@@ -1871,8 +1878,13 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
        if (un && un->semid == -1)
                goto out_unlock_free;
 
-       error = perform_atomic_semop(sma, sops, nsops, un,
-                                       task_tgid_vnr(current));
+       queue.sops = sops;
+       queue.nsops = nsops;
+       queue.undo = un;
+       queue.pid = task_tgid_vnr(current);
+       queue.alter = alter;
+
+       error = perform_atomic_semop(sma, &queue);
        if (error == 0) {
                /* If the operation was successful, then do
                 * the required updates.
@@ -1889,12 +1901,6 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
         * task into the pending queue and go to sleep.
         */
 
-       queue.sops = sops;
-       queue.nsops = nsops;
-       queue.undo = un;
-       queue.pid = task_tgid_vnr(current);
-       queue.alter = alter;
-
        if (nsops == 1) {
                struct sem *curr;
                curr = &sma->sem_base[sops->sem_num];
This page took 0.047147 seconds and 5 git commands to generate.