Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.6.24-rc3 /ipc/sem.c

Filename:/ipc/sem.c
Lines Added:155
Lines Deleted:153
Also changed in: (Previous) 2.6.24-rc2  2.6.24-rc1  2.6.23-git19  2.6.23-git18  2.6.23-git17  2.6.23-git16 
(Following) 2.6.24-rc4  2.6.24-rc5  2.6.24-rc6  2.6.24-rc7  2.6.24-rc8  2.6.24 

Location
[  2.6.24-rc3
  [  ipc
     o  sem.c

Patch

diff --git a/ipc/sem.c b/ipc/sem.c
index b676fef..35952c0 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -80,7 +80,7 @@
 #include <linux/audit.h>
 #include <linux/capability.h>
 #include <linux/seq_file.h>
-#include <linux/mutex.h>
+#include <linux/rwsem.h>
 #include <linux/nsproxy.h>
 
 #include <asm/uaccess.h>
@@ -88,18 +88,14 @@
 
 #define sem_ids(ns)   (*((ns)->ids[IPC_SEM_IDS]))
 
-#define sem_lock(ns, id)   ((struct sem_array*)ipc_lock(&sem_ids(ns), id))
 #define sem_unlock(sma)      ipc_unlock(&(sma)->sem_perm)
-#define sem_rmid(ns, id)   ((struct sem_array*)ipc_rmid(&sem_ids(ns), id))
-#define sem_checkid(ns, sma, semid)   \
-   ipc_checkid(&sem_ids(ns),&sma->sem_perm,semid)
-#define sem_buildid(ns, id, seq) \
-   ipc_buildid(&sem_ids(ns), id, seq)
+#define sem_checkid(sma, semid)   ipc_checkid(&sma->sem_perm, semid)
+#define sem_buildid(id, seq)   ipc_buildid(id, seq)
 
 static struct ipc_ids init_sem_ids;
 
-static int newary(struct ipc_namespace *, key_t, int, int);
-static void freeary(struct ipc_namespace *ns, struct sem_array *sma, int id);
+static int newary(struct ipc_namespace *, struct ipc_params *);
+static void freeary(struct ipc_namespace *, struct sem_array *);
 #ifdef CONFIG_PROC_FS
 static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
 #endif
@@ -129,7 +125,7 @@ static void __sem_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids)
    ns->sc_semopm = SEMOPM;
    ns->sc_semmni = SEMMNI;
    ns->used_sems = 0;
-   ipc_init_ids(ids, ns->sc_semmni);
+   ipc_init_ids(ids);
 }
 
 int sem_init_ns(struct ipc_namespace *ns)
@@ -146,20 +142,24 @@ int sem_init_ns(struct ipc_namespace *ns)
 
 void sem_exit_ns(struct ipc_namespace *ns)
 {
-   int i;
    struct sem_array *sma;
+   int next_id;
+   int total, in_use;
 
-   mutex_lock(&sem_ids(ns).mutex);
-   for (i = 0; i <= sem_ids(ns).max_id; i++) {
-      sma = sem_lock(ns, i);
+   down_write(&sem_ids(ns).rw_mutex);
+
+   in_use = sem_ids(ns).in_use;
+
+   for (total = 0, next_id = 0; total < in_use; next_id++) {
+      sma = idr_find(&sem_ids(ns).ipcs_idr, next_id);
       if (sma == NULL)
          continue;
-
-      freeary(ns, sma, i);
+      ipc_lock_by_ptr(&sma->sem_perm);
+      freeary(ns, sma);
+      total++;
    }
-   mutex_unlock(&sem_ids(ns).mutex);
+   up_write(&sem_ids(ns).rw_mutex);
 
-   ipc_fini_ids(ns->ids[IPC_SEM_IDS]);
    kfree(ns->ids[IPC_SEM_IDS]);
    ns->ids[IPC_SEM_IDS] = NULL;
 }
@@ -173,6 +173,42 @@ void __init sem_init (void)
 }
 
 /*
+ * This routine is called in the paths where the rw_mutex is held to protect
+ * access to the idr tree.
+ */
+static inline struct sem_array *sem_lock_check_down(struct ipc_namespace *ns,
+                  int id)
+{
+   struct kern_ipc_perm *ipcp = ipc_lock_check_down(&sem_ids(ns), id);
+
+   return container_of(ipcp, struct sem_array, sem_perm);
+}
+
+/*
+ * sem_lock_(check_) routines are called in the paths where the rw_mutex
+ * is not held.
+ */
+static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id)
+{
+   struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id);
+
+   return container_of(ipcp, struct sem_array, sem_perm);
+}
+
+static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns,
+                  int id)
+{
+   struct kern_ipc_perm *ipcp = ipc_lock_check(&sem_ids(ns), id);
+
+   return container_of(ipcp, struct sem_array, sem_perm);
+}
+
+static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
+{
+   ipc_rmid(&sem_ids(ns), &s->sem_perm);
+}
+
+/*
  * Lockless wakeup algorithm:
  * Without the check/retry algorithm a lockless wakeup is possible:
  * - queue.status is initialized to -EINTR before blocking.
@@ -206,12 +242,23 @@ void __init sem_init (void)
  */
 #define IN_WAKEUP   1
 
-static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg)
+/**
+ * newary - Create a new semaphore set
+ * @ns: namespace
+ * @params: ptr to the structure that contains key, semflg and nsems
+ *
+ * Called with sem_ids.rw_mutex held (as a writer)
+ */
+
+static int newary(struct ipc_namespace *ns, struct ipc_params *params)
 {
    int id;
    int retval;
    struct sem_array *sma;
    int size;
+   key_t key = params->key;
+   int nsems = params->u.nsems;
+   int semflg = params->flg;
 
    if (!nsems)
       return -EINVAL;
@@ -236,14 +283,14 @@ static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg)
    }
 
    id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
-   if(id == -1) {
+   if (id < 0) {
       security_sem_free(sma);
       ipc_rcu_putref(sma);
-      return -ENOSPC;
+      return id;
    }
    ns->used_sems += nsems;
 
-   sma->sem_id = sem_buildid(ns, id, sma->sem_perm.seq);
+   sma->sem_perm.id = sem_buildid(id, sma->sem_perm.seq);
    sma->sem_base = (struct sem *) &sma[1];
    /* sma->sem_pending = NULL; */
    sma->sem_pending_last = &sma->sem_pending;
@@ -252,48 +299,56 @@ static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg)
    sma->sem_ctime = get_seconds();
    sem_unlock(sma);
 
-   return sma->sem_id;
+   return sma->sem_perm.id;
+}
+
+
+/*
+ * Called with sem_ids.rw_mutex and ipcp locked.
+ */
+static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
+{
+   struct sem_array *sma;
+
+   sma = container_of(ipcp, struct sem_array, sem_perm);
+   return security_sem_associate(sma, semflg);
 }
 
-asmlinkage long sys_semget (key_t key, int nsems, int semflg)
+/*
+ * Called with sem_ids.rw_mutex and ipcp locked.
+ */
+static inline int sem_more_checks(struct kern_ipc_perm *ipcp,
+            struct ipc_params *params)
 {
-   int id, err = -EINVAL;
    struct sem_array *sma;
+
+   sma = container_of(ipcp, struct sem_array, sem_perm);
+   if (params->u.nsems > sma->sem_nsems)
+      return -EINVAL;
+
+   return 0;
+}
+
+asmlinkage long sys_semget(key_t key, int nsems, int semflg)
+{
    struct ipc_namespace *ns;
+   struct ipc_ops sem_ops;
+   struct ipc_params sem_params;
 
    ns = current->nsproxy->ipc_ns;
 
    if (nsems < 0 || nsems > ns->sc_semmsl)
       return -EINVAL;
-   mutex_lock(&sem_ids(ns).mutex);
-   
-   if (key == IPC_PRIVATE) {
-      err = newary(ns, key, nsems, semflg);
-   } else if ((id = ipc_findkey(&sem_ids(ns), key)) == -1) {  /* key not used */
-      if (!(semflg & IPC_CREAT))
-         err = -ENOENT;
-      else
-         err = newary(ns, key, nsems, semflg);
-   } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
-      err = -EEXIST;
-   } else {
-      sma = sem_lock(ns, id);
-      BUG_ON(sma==NULL);
-      if (nsems > sma->sem_nsems)
-         err = -EINVAL;
-      else if (ipcperms(&sma->sem_perm, semflg))
-         err = -EACCES;
-      else {
-         int semid = sem_buildid(ns, id, sma->sem_perm.seq);
-         err = security_sem_associate(sma, semflg);
-         if (!err)
-            err = semid;
-      }
-      sem_unlock(sma);
-   }
 
-   mutex_unlock(&sem_ids(ns).mutex);
-   return err;
+   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;
+
+   return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
 }
 
 /* Manage the doubly linked list sma->sem_pending as a FIFO:
@@ -487,15 +542,14 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)
    return semzcnt;
 }
 
-/* Free a semaphore set. freeary() is called with sem_ids.mutex locked and
- * the spinlock for this semaphore set hold. sem_ids.mutex remains locked
- * on exit.
+/* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked
+ * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex
+ * remains locked on exit.
  */
-static void freeary (struct ipc_namespace *ns, struct sem_array *sma, int id)
+static void freeary(struct ipc_namespace *ns, struct sem_array *sma)
 {
    struct sem_undo *un;
    struct sem_queue *q;
-   int size;
 
    /* Invalidate the existing undo structures for this semaphore set.
     * (They will be freed without any further action in exit_sem()
@@ -518,12 +572,11 @@ static void freeary (struct ipc_namespace *ns, struct sem_array *sma, int id)
       q = n;
    }
 
-   /* Remove the semaphore set from the ID array*/
-   sma = sem_rmid(ns, id);
+   /* Remove the semaphore set from the IDR */
+   sem_rmid(ns, sma);
    sem_unlock(sma);
 
    ns->used_sems -= sma->sem_nsems;
-   size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
    security_sem_free(sma);
    ipc_rcu_putref(sma);
 }
@@ -576,7 +629,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum,
       seminfo.semmnu = SEMMNU;
       seminfo.semmap = SEMMAP;
       seminfo.semume = SEMUME;
-      mutex_lock(&sem_ids(ns).mutex);
+      down_read(&sem_ids(ns).rw_mutex);
       if (cmd == SEM_INFO) {
          seminfo.semusz = sem_ids(ns).in_use;
          seminfo.semaem = ns->used_sems;
@@ -584,8 +637,8 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum,
          seminfo.semusz = SEMUSZ;
          seminfo.semaem = SEMAEM;
       }
-      max_id = sem_ids(ns).max_id;
-      mutex_unlock(&sem_ids(ns).mutex);
+      max_id = ipc_get_maxid(&sem_ids(ns));
+      up_read(&sem_ids(ns).rw_mutex);
       if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) 
          return -EFAULT;
       return (max_id < 0) ? 0: max_id;
@@ -595,14 +648,9 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum,
       struct semid64_ds tbuf;
       int id;
 
-      if(semid >= sem_ids(ns).entries->size)
-         return -EINVAL;
-
-      memset(&tbuf,0,sizeof(tbuf));
-
       sma = sem_lock(ns, semid);
-      if(sma == NULL)
-         return -EINVAL;
+      if (IS_ERR(sma))
+         return PTR_ERR(sma);
 
       err = -EACCES;
       if (ipcperms (&sma->sem_perm, S_IRUGO))
@@ -612,7 +660,9 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum,
       if (err)
          goto out_unlock;
 
-      id = sem_buildid(ns, semid, sma->sem_perm.seq);
+      id = sma->sem_perm.id;
+
+      memset(&tbuf, 0, sizeof(tbuf));
 
       kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
       tbuf.sem_otime  = sma->sem_otime;
@@ -642,16 +692,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
    ushort* sem_io = fast_sem_io;
    int nsems;
 
-   sma = sem_lock(ns, semid);
-   if(sma==NULL)
-      return -EINVAL;
+   sma = sem_lock_check(ns, semid);
+   if (IS_ERR(sma))
+      return PTR_ERR(sma);
 
    nsems = sma->sem_nsems;
 
-   err=-EIDRM;
-   if (sem_checkid(ns,sma,semid))
-      goto out_unlock;
-
    err = -EACCES;
    if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
       goto out_unlock;
@@ -795,7 +841,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
       for (un = sma->undo; un; un = un->id_next)
          un->semadj[semnum] = 0;
       curr->semval = val;
-      curr->sempid = current->tgid;
+      curr->sempid = task_tgid_vnr(current);
       sma->sem_ctime = get_seconds();
       /* maybe some queued-up processes were waiting for this */
       update_queue(sma);
@@ -863,14 +909,10 @@ static int semctl_down(struct ipc_namespace *ns, int semid, int semnum,
       if(copy_semid_from_user (&setbuf, arg.buf, version))
          return -EFAULT;
    }
-   sma = sem_lock(ns, semid);
-   if(sma==NULL)
-      return -EINVAL;
+   sma = sem_lock_check_down(ns, semid);
+   if (IS_ERR(sma))
+      return PTR_ERR(sma);
 
-   if (sem_checkid(ns,sma,semid)) {
-      err=-EIDRM;
-      goto out_unlock;
-   }   
    ipcp = &sma->sem_perm;
 
    err = audit_ipc_obj(ipcp);
@@ -894,7 +936,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, int semnum,
 
    switch(cmd){
    case IPC_RMID:
-      freeary(ns, sma, semid);
+      freeary(ns, sma);
       err = 0;
       break;
    case IPC_SET:
@@ -948,45 +990,15 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
       return err;
    case IPC_RMID:
    case IPC_SET:
-      mutex_lock(&sem_ids(ns).mutex);
+      down_write(&sem_ids(ns).rw_mutex);
       err = semctl_down(ns,semid,semnum,cmd,version,arg);
-      mutex_unlock(&sem_ids(ns).mutex);
+      up_write(&sem_ids(ns).rw_mutex);
       return err;
    default:
       return -EINVAL;
    }
 }
 
-static inline void lock_semundo(void)
-{
-   struct sem_undo_list *undo_list;
-
-   undo_list = current->sysvsem.undo_list;
-   if (undo_list)
-      spin_lock(&undo_list->lock);
-}
-
-/* This code has an interaction with copy_semundo().
- * Consider; two tasks are sharing the undo_list. task1
- * acquires the undo_list lock in lock_semundo().  If task2 now
- * exits before task1 releases the lock (by calling
- * unlock_semundo()), then task1 will never call spin_unlock().
- * This leave the sem_undo_list in a locked state.  If task1 now creats task3
- * and once again shares the sem_undo_list, the sem_undo_list will still be
- * locked, and future SEM_UNDO operations will deadlock.  This case is
- * dealt with in copy_semundo() by having it reinitialize the spin lock when 
- * the refcnt goes from 1 to 2.
- */
-static inline void unlock_semundo(void)
-{
-   struct sem_undo_list *undo_list;
-
-   undo_list = current->sysvsem.undo_list;
-   if (undo_list)
-      spin_unlock(&undo_list->lock);
-}
-
-
 /* If the task doesn't already have a undo_list, then allocate one
  * here.  We guarantee there is only one thread using this undo list,
  * and current is THE ONE
@@ -1047,22 +1059,17 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
    if (error)
       return ERR_PTR(error);
 
-   lock_semundo();
+   spin_lock(&ulp->lock);
    un = lookup_undo(ulp, semid);
-   unlock_semundo();
+   spin_unlock(&ulp->lock);
    if (likely(un!=NULL))
       goto out;
 
    /* no undo structure around - allocate one. */
-   sma = sem_lock(ns, semid);
-   un = ERR_PTR(-EINVAL);
-   if(sma==NULL)
-      goto out;
-   un = ERR_PTR(-EIDRM);
-   if (sem_checkid(ns,sma,semid)) {
-      sem_unlock(sma);
-      goto out;
-   }
+   sma = sem_lock_check(ns, semid);
+   if (IS_ERR(sma))
+      return ERR_PTR(PTR_ERR(sma));
+
    nsems = sma->sem_nsems;
    ipc_rcu_getref(sma);
    sem_unlock(sma);
@@ -1077,10 +1084,10 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
    new->semadj = (short *) &new[1];
    new->semid = semid;
 
-   lock_semundo();
+   spin_lock(&ulp->lock);
    un = lookup_undo(ulp, semid);
    if (un) {
-      unlock_semundo();
+      spin_unlock(&ulp->lock);
       kfree(new);
       ipc_lock_by_ptr(&sma->sem_perm);
       ipc_rcu_putref(sma);
@@ -1091,7 +1098,7 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
    ipc_rcu_putref(sma);
    if (sma->sem_perm.deleted) {
       sem_unlock(sma);
-      unlock_semundo();
+      spin_unlock(&ulp->lock);
       kfree(new);
       un = ERR_PTR(-EIDRM);
       goto out;
@@ -1102,7 +1109,7 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
    sma->undo = new;
    sem_unlock(sma);
    un = new;
-   unlock_semundo();
+   spin_unlock(&ulp->lock);
 out:
    return un;
 }
@@ -1168,15 +1175,14 @@ retry_undos:
    } else
       un = NULL;
 
-   sma = sem_lock(ns, semid);
-   error=-EINVAL;
-   if(sma==NULL)
+   sma = sem_lock_check(ns, semid);
+   if (IS_ERR(sma)) {
+      error = PTR_ERR(sma);
       goto out_free;
-   error = -EIDRM;
-   if (sem_checkid(ns,sma,semid))
-      goto out_unlock_free;
+   }
+
    /*
-    * semid identifies are not unique - find_undo may have
+    * semid identifiers are not unique - find_undo may have
     * allocated an undo structure, it was invalidated by an RMID
     * and now a new array with received the same id. Check and retry.
     */
@@ -1196,7 +1202,7 @@ retry_undos:
    if (error)
       goto out_unlock_free;
 
-   error = try_atomic_semop (sma, sops, nsops, un, current->tgid);
+   error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
    if (error <= 0) {
       if (alter && error == 0)
          update_queue (sma);
@@ -1211,7 +1217,7 @@ retry_undos:
    queue.sops = sops;
    queue.nsops = nsops;
    queue.undo = un;
-   queue.pid = current->tgid;
+   queue.pid = task_tgid_vnr(current);
    queue.id = semid;
    queue.alter = alter;
    if (alter)
@@ -1242,7 +1248,7 @@ retry_undos:
    }
 
    sma = sem_lock(ns, semid);
-   if(sma==NULL) {
+   if (IS_ERR(sma)) {
       BUG_ON(queue.prev != NULL);
       error = -EIDRM;
       goto out_free;
@@ -1279,10 +1285,6 @@ asmlinkage long sys_semop (int semid, struct sembuf __user *tsops, unsigned nsop
 
 /* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between
  * parent and child tasks.
- *
- * See the notes above unlock_semundo() regarding the spin_lock_init()
- * in this code.  Initialize the undo_list->lock here instead of get_undo_list()
- * because of the reasoning in the comment above unlock_semundo.
  */
 
 int copy_semundo(unsigned long clone_flags, struct task_struct *tsk)
@@ -1342,13 +1344,13 @@ void exit_sem(struct task_struct *tsk)
       if(semid == -1)
          continue;
       sma = sem_lock(ns, semid);
-      if (sma == NULL)
+      if (IS_ERR(sma))
          continue;
 
       if (u->semid == -1)
          goto next_entry;
 
-      BUG_ON(sem_checkid(ns,sma,u->semid));
+      BUG_ON(sem_checkid(sma, u->semid));
 
       /* remove u from the sma->undo list */
       for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
@@ -1382,7 +1384,7 @@ found:
                semaphore->semval = 0;
             if (semaphore->semval > SEMVMX)
                semaphore->semval = SEMVMX;
-            semaphore->sempid = current->tgid;
+            semaphore->sempid = task_tgid_vnr(current);
          }
       }
       sma->sem_otime = get_seconds();
@@ -1402,7 +1404,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
    return seq_printf(s,
            "%10d %10d  %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
            sma->sem_perm.key,
-           sma->sem_id,
+           sma->sem_perm.id,
            sma->sem_perm.mode,
            sma->sem_nsems,
            sma->sem_perm.uid,


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.