aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/namespace.c
diff options
context:
space:
mode:
authorSerge E. Hallyn <serue@us.ibm.com>2009-04-06 22:01:10 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-07 11:31:09 -0400
commit7eafd7c74c3f2e67c27621b987b28397110d643f (patch)
treeb4621aab78b6303f20386096c230b993044a4db7 /ipc/namespace.c
parent614b84cf4e4a920d2af32b8f147ea1e3b8c27ea6 (diff)
namespaces: ipc namespaces: implement support for posix msqueues
Implement multiple mounts of the mqueue file system, and link it to usage of CLONE_NEWIPC. Each ipc ns has a corresponding mqueuefs superblock. When a user does clone(CLONE_NEWIPC) or unshare(CLONE_NEWIPC), the unshare will cause an internal mount of a new mqueuefs sb linked to the new ipc ns. When a user does 'mount -t mqueue mqueue /dev/mqueue', he mounts the mqueuefs superblock. Posix message queues can be worked with both through the mq_* system calls (see mq_overview(7)), and through the VFS through the mqueue mount. Any usage of mq_open() and friends will work with the acting task's ipc namespace. Any actions through the VFS will work with the mqueuefs in which the file was created. So if a user doesn't remount mqueuefs after unshare(CLONE_NEWIPC), mq_open("/ab") will not be reflected in "ls /dev/mqueue". If task a mounts mqueue for ipc_ns:1, then clones task b with a new ipcns, ipcns:2, and then task a is the last task in ipc_ns:1 to exit, then (1) ipc_ns:1 will be freed, (2) it's superblock will live on until task b umounts the corresponding mqueuefs, and vfs actions will continue to succeed, but (3) sb->s_fs_info will be NULL for the sb corresponding to the deceased ipc_ns:1. To make this happen, we must protect the ipc reference count when a) a task exits and drops its ipcns->count, since it might be dropping it to 0 and freeing the ipcns b) a task accesses the ipcns through its mqueuefs interface, since it bumps the ipcns refcount and might race with the last task in the ipcns exiting. So the kref is changed to an atomic_t so we can use atomic_dec_and_lock(&ns->count,mq_lock), and every access to the ipcns through ns = mqueuefs_sb->s_fs_info is protected by the same lock. Signed-off-by: Cedric Le Goater <clg@fr.ibm.com> Signed-off-by: Serge E. Hallyn <serue@us.ibm.com> Cc: Alexey Dobriyan <adobriyan@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc/namespace.c')
-rw-r--r--ipc/namespace.c41
1 files changed, 35 insertions, 6 deletions
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 4b4dc6d847f1..4a5e752a9276 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -9,23 +9,31 @@
9#include <linux/rcupdate.h> 9#include <linux/rcupdate.h>
10#include <linux/nsproxy.h> 10#include <linux/nsproxy.h>
11#include <linux/slab.h> 11#include <linux/slab.h>
12#include <linux/fs.h>
13#include <linux/mount.h>
12 14
13#include "util.h" 15#include "util.h"
14 16
15static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns) 17static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
16{ 18{
17 struct ipc_namespace *ns; 19 struct ipc_namespace *ns;
20 int err;
18 21
19 ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL); 22 ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
20 if (ns == NULL) 23 if (ns == NULL)
21 return ERR_PTR(-ENOMEM); 24 return ERR_PTR(-ENOMEM);
22 25
26 atomic_set(&ns->count, 1);
27 err = mq_init_ns(ns);
28 if (err) {
29 kfree(ns);
30 return ERR_PTR(err);
31 }
23 atomic_inc(&nr_ipc_ns); 32 atomic_inc(&nr_ipc_ns);
24 33
25 sem_init_ns(ns); 34 sem_init_ns(ns);
26 msg_init_ns(ns); 35 msg_init_ns(ns);
27 shm_init_ns(ns); 36 shm_init_ns(ns);
28 mq_init_ns(ns);
29 37
30 /* 38 /*
31 * msgmni has already been computed for the new ipc ns. 39 * msgmni has already been computed for the new ipc ns.
@@ -35,7 +43,6 @@ static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
35 ipcns_notify(IPCNS_CREATED); 43 ipcns_notify(IPCNS_CREATED);
36 register_ipcns_notifier(ns); 44 register_ipcns_notifier(ns);
37 45
38 kref_init(&ns->kref);
39 return ns; 46 return ns;
40} 47}
41 48
@@ -85,11 +92,34 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
85 up_write(&ids->rw_mutex); 92 up_write(&ids->rw_mutex);
86} 93}
87 94
88void free_ipc_ns(struct kref *kref) 95/*
96 * put_ipc_ns - drop a reference to an ipc namespace.
97 * @ns: the namespace to put
98 *
99 * If this is the last task in the namespace exiting, and
100 * it is dropping the refcount to 0, then it can race with
101 * a task in another ipc namespace but in a mounts namespace
102 * which has this ipcns's mqueuefs mounted, doing some action
103 * with one of the mqueuefs files. That can raise the refcount.
104 * So dropping the refcount, and raising the refcount when
105 * accessing it through the VFS, are protected with mq_lock.
106 *
107 * (Clearly, a task raising the refcount on its own ipc_ns
108 * needn't take mq_lock since it can't race with the last task
109 * in the ipcns exiting).
110 */
111void put_ipc_ns(struct ipc_namespace *ns)
89{ 112{
90 struct ipc_namespace *ns; 113 if (atomic_dec_and_lock(&ns->count, &mq_lock)) {
114 mq_clear_sbinfo(ns);
115 spin_unlock(&mq_lock);
116 mq_put_mnt(ns);
117 free_ipc_ns(ns);
118 }
119}
91 120
92 ns = container_of(kref, struct ipc_namespace, kref); 121void free_ipc_ns(struct ipc_namespace *ns)
122{
93 /* 123 /*
94 * Unregistering the hotplug notifier at the beginning guarantees 124 * Unregistering the hotplug notifier at the beginning guarantees
95 * that the ipc namespace won't be freed while we are inside the 125 * that the ipc namespace won't be freed while we are inside the
@@ -102,7 +132,6 @@ void free_ipc_ns(struct kref *kref)
102 sem_exit_ns(ns); 132 sem_exit_ns(ns);
103 msg_exit_ns(ns); 133 msg_exit_ns(ns);
104 shm_exit_ns(ns); 134 shm_exit_ns(ns);
105 mq_exit_ns(ns);
106 kfree(ns); 135 kfree(ns);
107 atomic_dec(&nr_ipc_ns); 136 atomic_dec(&nr_ipc_ns);
108 137