Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[pandora-kernel.git] / ipc / shm.c
index 89fc354..7fc9f9f 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -178,6 +178,7 @@ static void shm_rcu_free(struct rcu_head *head)
 
 static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
 {
+       list_del(&s->shm_clist);
        ipc_rmid(&shm_ids(ns), &s->shm_perm);
 }
 
@@ -267,37 +268,6 @@ static void shm_close(struct vm_area_struct *vma)
        up_write(&shm_ids(ns).rwsem);
 }
 
-/* Called with ns->shm_ids(ns).rwsem locked */
-static int shm_try_destroy_current(int id, void *p, void *data)
-{
-       struct ipc_namespace *ns = data;
-       struct kern_ipc_perm *ipcp = p;
-       struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm);
-
-       if (shp->shm_creator != current)
-               return 0;
-
-       /*
-        * Mark it as orphaned to destroy the segment when
-        * kernel.shm_rmid_forced is changed.
-        * It is noop if the following shm_may_destroy() returns true.
-        */
-       shp->shm_creator = NULL;
-
-       /*
-        * Don't even try to destroy it.  If shm_rmid_forced=0 and IPC_RMID
-        * is not set, it shouldn't be deleted here.
-        */
-       if (!ns->shm_rmid_forced)
-               return 0;
-
-       if (shm_may_destroy(ns, shp)) {
-               shm_lock_by_ptr(shp);
-               shm_destroy(ns, shp);
-       }
-       return 0;
-}
-
 /* Called with ns->shm_ids(ns).rwsem locked */
 static int shm_try_destroy_orphaned(int id, void *p, void *data)
 {
@@ -329,18 +299,50 @@ void shm_destroy_orphaned(struct ipc_namespace *ns)
        up_write(&shm_ids(ns).rwsem);
 }
 
-
+/* Locking assumes this will only be called with task == current */
 void exit_shm(struct task_struct *task)
 {
        struct ipc_namespace *ns = task->nsproxy->ipc_ns;
+       struct shmid_kernel *shp, *n;
 
-       if (shm_ids(ns).in_use == 0)
+       if (list_empty(&task->sysvshm.shm_clist))
                return;
 
-       /* Destroy all already created segments, but not mapped yet */
+       /*
+        * If kernel.shm_rmid_forced is not set then only keep track of
+        * which shmids are orphaned, so that a later set of the sysctl
+        * can clean them up.
+        */
+       if (!ns->shm_rmid_forced) {
+               down_read(&shm_ids(ns).rwsem);
+               list_for_each_entry(shp, &task->sysvshm.shm_clist, shm_clist)
+                       shp->shm_creator = NULL;
+               /*
+                * Only under read lock but we are only called on current
+                * so no entry on the list will be shared.
+                */
+               list_del(&task->sysvshm.shm_clist);
+               up_read(&shm_ids(ns).rwsem);
+               return;
+       }
+
+       /*
+        * Destroy all already created segments, that were not yet mapped,
+        * and mark any mapped as orphan to cover the sysctl toggling.
+        * Destroy is skipped if shm_may_destroy() returns false.
+        */
        down_write(&shm_ids(ns).rwsem);
-       if (shm_ids(ns).in_use)
-               idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns);
+       list_for_each_entry_safe(shp, n, &task->sysvshm.shm_clist, shm_clist) {
+               shp->shm_creator = NULL;
+
+               if (shm_may_destroy(ns, shp)) {
+                       shm_lock_by_ptr(shp);
+                       shm_destroy(ns, shp);
+               }
+       }
+
+       /* Remove the list head from any segments still attached. */
+       list_del(&task->sysvshm.shm_clist);
        up_write(&shm_ids(ns).rwsem);
 }
 
@@ -561,6 +563,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        shp->shm_nattch = 0;
        shp->shm_file = file;
        shp->shm_creator = current;
+       list_add(&shp->shm_clist, &current->sysvshm.shm_clist);
 
        /*
         * shmid gets reported as "inode#" in /proc/pid/maps.