ipc/sem.c: remove code duplication
[sfrench/cifs-2.6.git] / ipc / sem.c
index bee5554173120780374e6b42228d13c786ceb315..d73b3eff80f08582c211975f1705d45ba8597d42 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -47,8 +47,7 @@
  *   Thus: Perfect SMP scaling between independent semaphore arrays.
  *         If multiple semaphores in one array are used, then cache line
  *         trashing on the semaphore array spinlock will limit the scaling.
- * - semncnt and semzcnt are calculated on demand in count_semncnt() and
- *   count_semzcnt()
+ * - semncnt and semzcnt are calculated on demand in count_semcnt()
  * - the task that performs a successful semop() scans the list of all
  *   sleeping tasks and completes any pending operations that can be fulfilled.
  *   Semaphores are actively given to waiting tasks (necessary for FIFO).
@@ -87,7 +86,7 @@
 #include <linux/nsproxy.h>
 #include <linux/ipc_namespace.h>
 
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include "util.h"
 
 /* One semaphore structure for each semaphore in the system. */
@@ -160,7 +159,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
  *     sem_array.pending{_alter,_cont},
  *     sem_array.sem_undo: global sem_lock() for read/write
  *     sem_undo.proc_next: only "current" is allowed to read/write that field.
- *     
+ *
  *     sem_array.sem_base[i].pending_{const,alter}:
  *             global or semaphore sem_lock() for read/write
  */
@@ -564,7 +563,11 @@ static inline int sem_more_checks(struct kern_ipc_perm *ipcp,
 SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
 {
        struct ipc_namespace *ns;
-       struct ipc_ops sem_ops;
+       static const struct ipc_ops sem_ops = {
+               .getnew = newary,
+               .associate = sem_security,
+               .more_checks = sem_more_checks,
+       };
        struct ipc_params sem_params;
 
        ns = current->nsproxy->ipc_ns;
@@ -572,10 +575,6 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
        if (nsems < 0 || nsems > ns->sc_semmsl)
                return -EINVAL;
 
-       sem_ops.getnew = newary;
-       sem_ops.associate = sem_security;
-       sem_ops.more_checks = sem_more_checks;
-
        sem_params.key = key;
        sem_params.flg = semflg;
        sem_params.u.nsems = nsems;
@@ -989,6 +988,31 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop
                set_semotime(sma, sops);
 }
 
+/*
+ * check_qop: Test how often 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;
+
+       semcnt = 0;
+
+       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;
+}
+
 /* 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
@@ -998,56 +1022,37 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop
  * The counts we return here are a rough approximation, but still
  * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
  */
-static int count_semncnt(struct sem_array *sma, ushort semnum)
+static int count_semcnt(struct sem_array *sma, ushort semnum,
+                       bool count_zero)
 {
-       int semncnt;
+       struct list_head *l;
        struct sem_queue *q;
+       int semcnt;
 
-       semncnt = 0;
-       list_for_each_entry(q, &sma->sem_base[semnum].pending_alter, list) {
-               struct sembuf *sops = q->sops;
-               BUG_ON(sops->sem_num != semnum);
-               if ((sops->sem_op < 0) && !(sops->sem_flg & IPC_NOWAIT))
-                       semncnt++;
-       }
+       semcnt = 0;
+       /* First: check the simple operations. They are easy to evaluate */
+       if (count_zero)
+               l = &sma->sem_base[semnum].pending_const;
+       else
+               l = &sma->sem_base[semnum].pending_alter;
 
-       list_for_each_entry(q, &sma->pending_alter, list) {
-               struct sembuf *sops = q->sops;
-               int nsops = q->nsops;
-               int i;
-               for (i = 0; i < nsops; i++)
-                       if (sops[i].sem_num == semnum
-                           && (sops[i].sem_op < 0)
-                           && !(sops[i].sem_flg & IPC_NOWAIT))
-                               semncnt++;
+       list_for_each_entry(q, l, list) {
+               /* all task on a per-semaphore list sleep on exactly
+                * that semaphore
+                */
+               semcnt++;
        }
-       return semncnt;
-}
-
-static int count_semzcnt(struct sem_array *sma, ushort semnum)
-{
-       int semzcnt;
-       struct sem_queue *q;
 
-       semzcnt = 0;
-       list_for_each_entry(q, &sma->sem_base[semnum].pending_const, list) {
-               struct sembuf *sops = q->sops;
-               BUG_ON(sops->sem_num != semnum);
-               if ((sops->sem_op == 0) && !(sops->sem_flg & IPC_NOWAIT))
-                       semzcnt++;
+       /* Then: check the complex operations. */
+       list_for_each_entry(q, &sma->pending_alter, list) {
+               semcnt += check_qop(sma, semnum, q, count_zero);
        }
-
-       list_for_each_entry(q, &sma->pending_const, list) {
-               struct sembuf *sops = q->sops;
-               int nsops = q->nsops;
-               int i;
-               for (i = 0; i < nsops; i++)
-                       if (sops[i].sem_num == semnum
-                           && (sops[i].sem_op == 0)
-                           && !(sops[i].sem_flg & IPC_NOWAIT))
-                               semzcnt++;
+       if (count_zero) {
+               list_for_each_entry(q, &sma->pending_const, list) {
+                       semcnt += check_qop(sma, semnum, q, count_zero);
+               }
        }
-       return semzcnt;
+       return semcnt;
 }
 
 /* Free a semaphore set. freeary() is called with sem_ids.rwsem locked
@@ -1161,7 +1166,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
                err = security_sem_semctl(NULL, cmd);
                if (err)
                        return err;
-               
+
                memset(&seminfo, 0, sizeof(seminfo));
                seminfo.semmni = ns->sc_semmni;
                seminfo.semmns = ns->sc_semmns;
@@ -1181,7 +1186,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid,
                }
                max_id = ipc_get_maxid(&sem_ids(ns));
                up_read(&sem_ids(ns).rwsem);
-               if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) 
+               if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
                        return -EFAULT;
                return (max_id < 0) ? 0 : max_id;
        }
@@ -1449,10 +1454,10 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
                err = curr->sempid;
                goto out_unlock;
        case GETNCNT:
-               err = count_semncnt(sma, semnum);
+               err = count_semcnt(sma, semnum, 0);
                goto out_unlock;
        case GETZCNT:
-               err = count_semzcnt(sma, semnum);
+               err = count_semcnt(sma, semnum, 1);
                goto out_unlock;
        }
 
@@ -1883,7 +1888,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
        /* We need to sleep on this operation, so we put the current
         * task into the pending queue and go to sleep.
         */
-               
+
        queue.sops = sops;
        queue.nsops = nsops;
        queue.undo = un;
@@ -2016,7 +2021,7 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk)
                        return error;
                atomic_inc(&undo_list->refcnt);
                tsk->sysvsem.undo_list = undo_list;
-       } else 
+       } else
                tsk->sysvsem.undo_list = NULL;
 
        return 0;