Skip to contentSkip to conversation

posix mqueue namespace (v11)

RFC PATCH In review Serge E. Hallyn 2 participants 5 messages Dec 15, 2008 – Dec 16, 2008 2 messages not in the archive

lore
Share & tools
lore
#0 lore raw

cover posix mqueue namespace (v11)

(Ok I don't know what the actual version number is - it's high but 11 is probably safe)

Cedric and Nadia took several approaches to making posix message queues per-namespace. I ended up mamking some deep changes so am not retaining their Signed-off-by:s on this version, but this is definately very much based on work by both of them.

Patch 2 hopefully explains my approach. Briefly,

1. sysv and posix ipc are both under CLONE_NEWIPC
2. the mqueue sb is per-ipc-namespace

So to create a new ipc namespace, you would

	unshare(CLONE_NEWIPC|CLONE_NEWNS);
	umount /dev/mqueue
	mount -t mqueue mqueue /dev/mqueue

It's perfectly valid to do vfs operations on files in another ipc_namespace's /dev/mqueue, but any use of mq_open(3) and friends will act in your own ipc_ns.

thanks,
-serge

A message that is not in the archive (sent off-list or not yet received): 20081215233747.GA27553-r/[email protected]

#2 lore raw

RFC PATCH 1/2 mqueue ns: move mqueue_mnt into struct ipc_namespace

Move mqueue vfsmount plus a few tunables into the ipc_namespace struct. The CONFIG_IPC_NS boolean and the ipc_namespace struct will serve both the posix message queue namespaces and the SYSV ipc namespaces.

Largely based on previous version by Cedric Le Goater <clg-NmTC/[email protected]> and Nadia Derbey <Nadia.Derbey-6ktuUTfB/[email protected]>.

---
 include/linux/ipc_namespace.h |   32 ++++++++++--
 init/Kconfig                  |    4 +-
 ipc/mqueue.c                  |  116 ++++++++++++++++++++++-------------------
 ipc/msgutil.c                 |   21 +++++++
 ipc/namespace.c               |    2 +
 ipc/util.c                    |    9 ---
 ipc/util.h                    |   15 +++++
 7 files changed, 131 insertions(+), 68 deletions(-)
include/linux/ipc_namespace.h +28 -4
index ea330f9..532598f 100644
@@ -44,24 +44,48 @@ struct ipc_namespace {
 	int		shm_tot;
 
 	struct notifier_block ipcns_nb;
+
+	struct vfsmount	*mq_mnt;
+	unsigned int    mq_queues_count;
+	unsigned int    mq_queues_max;
+	unsigned int    mq_msg_max;
+	unsigned int    mq_msgsize_max;
+
 };
 
 extern struct ipc_namespace init_ipc_ns;
 extern atomic_t nr_ipc_ns;
 
-#ifdef CONFIG_SYSVIPC
+#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
 #define INIT_IPC_NS(ns)		.ns		= &init_ipc_ns,
+#else
+#define INIT_IPC_NS(ns)
+#endif
 
+#ifdef CONFIG_SYSVIPC
 extern int register_ipcns_notifier(struct ipc_namespace *);
 extern int cond_register_ipcns_notifier(struct ipc_namespace *);
 extern void unregister_ipcns_notifier(struct ipc_namespace *);
 extern int ipcns_notify(unsigned long);
-
 #else /* CONFIG_SYSVIPC */
-#define INIT_IPC_NS(ns)
+#define register_ipcns_notifier(ns)
+#define cond_register_ipcns_notifier(ns)
+#define unregister_ipcns_notifier(ns)
+#define ipcns_notify(l)
 #endif /* CONFIG_SYSVIPC */
 
-#if defined(CONFIG_SYSVIPC) && defined(CONFIG_IPC_NS)
+#ifdef CONFIG_POSIX_MQUEUE
+extern void mq_init_ns(struct ipc_namespace *ns);
+/* default values */
+#define DFLT_QUEUESMAX 256     /* max number of message queues */
+#define DFLT_MSGMAX    10      /* max number of messages in each queue */
+#define HARD_MSGMAX    (131072/sizeof(void *))
+#define DFLT_MSGSIZEMAX 8192   /* max message size */
+#else
+#define mq_init_ns(ns)
+#endif
+
+#if defined(CONFIG_IPC_NS)
 extern void free_ipc_ns(struct kref *kref);
 extern struct ipc_namespace *copy_ipcs(unsigned long flags,
 				       struct ipc_namespace *ns);
init/Kconfig +2 -2
index ce75d2d..32c6315 100644
@@ -489,10 +489,10 @@ config UTS_NS
 
 config IPC_NS
 	bool "IPC namespace"
-	depends on NAMESPACES && SYSVIPC
+	depends on NAMESPACES && (SYSVIPC || POSIX_MQUEUE)
 	help
 	  In this namespace tasks work with IPC ids which correspond to
-	  different IPC objects in different namespaces
+	  different IPC objects in different namespaces.
 
 config USER_NS
 	bool "User namespace (EXPERIMENTAL)"
ipc/mqueue.c +63 -53

336 lines not on this page · raw message

ipc/msgutil.c +21 -0
index c82c215..c197cd1 100644
@@ -13,10 +13,31 @@
 #include <linux/security.h>
 #include <linux/slab.h>
 #include <linux/ipc.h>
+#include <linux/ipc_namespace.h>
 #include <asm/uaccess.h>
 
 #include "util.h"
 
+/*
+ * The next 2 defines are here bc this is the only file
+ * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
+ * and not CONFIG_IPC_NS.
+ */
+struct ipc_namespace init_ipc_ns = {
+	.kref = {
+		.refcount	= ATOMIC_INIT(2),
+	},
+#ifdef CONFIG_POSIX_MQUEUE
+	.mq_mnt          = NULL,
+	.mq_queues_count = 0,
+	.mq_queues_max   = DFLT_QUEUESMAX,
+	.mq_msg_max      = DFLT_MSGMAX,
+	.mq_msgsize_max  = DFLT_MSGSIZEMAX,
+#endif
+};
+
+atomic_t nr_ipc_ns = ATOMIC_INIT(1);
+
 struct msg_msgseg {
 	struct msg_msgseg* next;
 	/* the next part of the message follows immediately */
ipc/namespace.c +2 -0
index 9171d94..4b4dc6d 100644
@@ -25,6 +25,7 @@ static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
 	sem_init_ns(ns);
 	msg_init_ns(ns);
 	shm_init_ns(ns);
+	mq_init_ns(ns);
 
 	/*
 	 * msgmni has already been computed for the new ipc ns.
@@ -101,6 +102,7 @@ void free_ipc_ns(struct kref *kref)
 	sem_exit_ns(ns);
 	msg_exit_ns(ns);
 	shm_exit_ns(ns);
+	mq_exit_ns(ns);
 	kfree(ns);
 	atomic_dec(&nr_ipc_ns);
 
ipc/util.c +0 -9
index 5a1808c..ac971ce 100644
@@ -47,15 +47,6 @@ struct ipc_proc_iface {
 	int (*show)(struct seq_file *, void *);
 };
 
-struct ipc_namespace init_ipc_ns = {
-	.kref = {
-		.refcount	= ATOMIC_INIT(2),
-	},
-};
-
-atomic_t nr_ipc_ns = ATOMIC_INIT(1);
-
-
 #ifdef CONFIG_MEMORY_HOTPLUG
 
 static void ipc_memory_notifier(struct work_struct *work)
ipc/util.h +15 -0
index 3646b45..52755c1 100644
@@ -20,6 +20,13 @@ void shm_init (void);
 
 struct ipc_namespace;
 
+#ifdef CONFIG_POSIX_MQUEUE
+void mq_exit_ns(struct ipc_namespace *ns);
+#else
+#define mq_exit_ns(ns)
+#endif
+
+#ifdef CONFIG_SYSVIPC
 void sem_init_ns(struct ipc_namespace *ns);
 void msg_init_ns(struct ipc_namespace *ns);
 void shm_init_ns(struct ipc_namespace *ns);
@@ -27,6 +34,14 @@ void shm_init_ns(struct ipc_namespace *ns);
 void sem_exit_ns(struct ipc_namespace *ns);
 void msg_exit_ns(struct ipc_namespace *ns);
 void shm_exit_ns(struct ipc_namespace *ns);
+#else
+#define sem_init_ns(ns)
+#define msg_init_ns(ns)
+#define shm_init_ns(ns)
+#define sem_exit_ns(ns)
+#define msg_exit_ns(ns)
+#define shm_exit_ns(ns)
+#endif
 
 /*
  * Structure that holds the parameters needed by the ipc operations
Signature · 1 line
1.5.4.3
#3 lore raw

RFC PATCH 2/2 ipc namespaces: implement support for posix msqueues

In reply to a message not in the archive

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.

Largely based on previous version by Cedric Le Goater <clg-NmTC/[email protected]> and Nadia Derbey <Nadia.Derbey-6ktuUTfB/[email protected]>.

---
 include/linux/ipc_namespace.h |   16 ++---
 ipc/mqueue.c                  |  150 +++++++++++++++++++++++++++++++++--------
 ipc/msgutil.c                 |    8 +--
 ipc/namespace.c               |   25 +++++--
 ipc/util.h                    |    6 +-
 5 files changed, 154 insertions(+), 51 deletions(-)
include/linux/ipc_namespace.h +7 -9
index 532598f..74f1ae2 100644
@@ -25,7 +25,7 @@ struct ipc_ids {
 };
 
 struct ipc_namespace {
-	struct kref	kref;
+	atomic_t	count;
 	struct ipc_ids	ids[3];
 
 	int		sem_ctls[4];
@@ -56,6 +56,7 @@ struct ipc_namespace {
 extern struct ipc_namespace init_ipc_ns;
 extern atomic_t nr_ipc_ns;
 
+extern spinlock_t mq_lock;
 #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
 #define INIT_IPC_NS(ns)		.ns		= &init_ipc_ns,
 #else
@@ -75,18 +76,18 @@ extern int ipcns_notify(unsigned long);
 #endif /* CONFIG_SYSVIPC */
 
 #ifdef CONFIG_POSIX_MQUEUE
-extern void mq_init_ns(struct ipc_namespace *ns);
+extern int mq_init_ns(struct ipc_namespace *ns);
 /* default values */
 #define DFLT_QUEUESMAX 256     /* max number of message queues */
 #define DFLT_MSGMAX    10      /* max number of messages in each queue */
 #define HARD_MSGMAX    (131072/sizeof(void *))
 #define DFLT_MSGSIZEMAX 8192   /* max message size */
 #else
-#define mq_init_ns(ns)
+#define mq_init_ns(ns) (0)
 #endif
 
 #if defined(CONFIG_IPC_NS)
-extern void free_ipc_ns(struct kref *kref);
+extern void free_ipc_ns(struct ipc_namespace *ns);
 extern struct ipc_namespace *copy_ipcs(unsigned long flags,
 				       struct ipc_namespace *ns);
 extern void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
@@ -96,14 +97,11 @@ extern void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
 static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns)
 {
 	if (ns)
-		kref_get(&ns->kref);
+		atomic_inc(&ns->count);
 	return ns;
 }
 
-static inline void put_ipc_ns(struct ipc_namespace *ns)
-{
-	kref_put(&ns->kref, free_ipc_ns);
-}
+extern void put_ipc_ns(struct ipc_namespace *ns);
 #else
 static inline struct ipc_namespace *copy_ipcs(unsigned long flags,
 		struct ipc_namespace *ns)
ipc/mqueue.c +121 -29

268 lines not on this page · raw message

ipc/msgutil.c +3 -5
index c197cd1..21475b0 100644
@@ -18,18 +18,16 @@
 
 #include "util.h"
 
+spinlock_t mq_lock;
+
 /*
  * The next 2 defines are here bc this is the only file
  * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE
  * and not CONFIG_IPC_NS.
  */
 struct ipc_namespace init_ipc_ns = {
-	.kref = {
-		.refcount	= ATOMIC_INIT(2),
-	},
+	.count		= ATOMIC_INIT(2),
 #ifdef CONFIG_POSIX_MQUEUE
-	.mq_mnt          = NULL,
-	.mq_queues_count = 0,
 	.mq_queues_max   = DFLT_QUEUESMAX,
 	.mq_msg_max      = DFLT_MSGMAX,
 	.mq_msgsize_max  = DFLT_MSGSIZEMAX,
ipc/namespace.c +19 -6
index 4b4dc6d..a4f36ba 100644
@@ -9,23 +9,31 @@
 #include <linux/rcupdate.h>
 #include <linux/nsproxy.h>
 #include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
 
 #include "util.h"
 
 static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
 {
 	struct ipc_namespace *ns;
+	int err;
 
 	ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
 	if (ns == NULL)
 		return ERR_PTR(-ENOMEM);
 
+	atomic_set(&ns->count, 1);
+	err = mq_init_ns(ns);
+	if (err) {
+		kfree(ns);
+		return ERR_PTR(err);
+	}
 	atomic_inc(&nr_ipc_ns);
 
 	sem_init_ns(ns);
 	msg_init_ns(ns);
 	shm_init_ns(ns);
-	mq_init_ns(ns);
 
 	/*
 	 * 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)
 	ipcns_notify(IPCNS_CREATED);
 	register_ipcns_notifier(ns);
 
-	kref_init(&ns->kref);
 	return ns;
 }
 
@@ -85,11 +92,18 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids,
 	up_write(&ids->rw_mutex);
 }
 
-void free_ipc_ns(struct kref *kref)
+void put_ipc_ns(struct ipc_namespace *ns)
 {
-	struct ipc_namespace *ns;
+	if (ns && atomic_dec_and_lock(&ns->count, &mq_lock)) {
+		mq_clear_sbinfo(ns);
+		spin_unlock(&mq_lock);
+		mq_put_mnt(ns);
+		free_ipc_ns(ns);
+	}
+}
 
-	ns = container_of(kref, struct ipc_namespace, kref);
+void free_ipc_ns(struct ipc_namespace *ns)
+{
 	/*
 	 * Unregistering the hotplug notifier at the beginning guarantees
 	 * that the ipc namespace won't be freed while we are inside the
@@ -102,7 +116,6 @@ void free_ipc_ns(struct kref *kref)
 	sem_exit_ns(ns);
 	msg_exit_ns(ns);
 	shm_exit_ns(ns);
-	mq_exit_ns(ns);
 	kfree(ns);
 	atomic_dec(&nr_ipc_ns);
 
ipc/util.h +4 -2
index 52755c1..b4d213f 100644
@@ -21,9 +21,11 @@ void shm_init (void);
 struct ipc_namespace;
 
 #ifdef CONFIG_POSIX_MQUEUE
-void mq_exit_ns(struct ipc_namespace *ns);
+extern void mq_clear_sbinfo(struct ipc_namespace *ns);
+extern void mq_put_mnt(struct ipc_namespace *ns);
 #else
-#define mq_exit_ns(ns)
+#define mq_clear_sbinfo(ns)
+#define mq_put_mnt(ns)
 #endif
 
 #ifdef CONFIG_SYSVIPC
Signature · 1 line
1.5.4.3
#4 lore raw
In reply to a message not in the archive
Quoted 8 lines · Serge E. Hallyn

(Ok I don't know what the actual version number is - it's high but 11 is probably safe)

Cedric and Nadia took several approaches to making posix message queues per-namespace. I ended up mamking some deep changes so am not retaining their Signed-off-by:s on this version, but this is definately very much based on work by both of them.

you can keep mine. i have had a similar version on 2.6.26.

http://legoater.free.fr/patches/2.6.26/2.6.26/

and it's easier to track where the patches go.

Quoted 8 lines

Patch 2 hopefully explains my approach. Briefly,

1. sysv and posix ipc are both under CLONE_NEWIPC
2. the mqueue sb is per-ipc-namespace

So to create a new ipc namespace, you would

	unshare(CLONE_NEWIPC|CLONE_NEWNS);

does CLONE_NEWIPC requires CLONE_NEWNS ?

Quoted 2 lines

umount /dev/mqueue
mount -t mqueue mqueue /dev/mqueue

the semantic looks good, much better than a 'newinstance' mount option.

if CLONE_NEWNS is not required, what happens to the user mount (and the mq_ns below it) when the task dies. that's the big issue. if CLONE_NEWNS is required were safe, but I think Pavel made some objection to that.

Quoted 3 lines

It's perfectly valid to do vfs operations on files in another ipc_namespace's /dev/mqueue, but any use of mq_open(3) and friends will act in your own ipc_ns.

ok.

C.

A message that is not in the archive (sent off-list or not yet received): 4947629D.9040807-NmTC/[email protected]

#6 lore raw
Quoted 10 lines · Cedric Le Goater
Quoted 8 lines · Serge E. Hallyn

(Ok I don't know what the actual version number is - it's high but 11 is probably safe)

Cedric and Nadia took several approaches to making posix message queues per-namespace. I ended up mamking some deep changes so am not retaining their Signed-off-by:s on this version, but this is definately very much based on work by both of them.

Quoted 7 lines

you can keep mine. i have had a similar version on 2.6.26.

http://legoater.free.fr/patches/2.6.26/2.6.26/

and it's easier to track where the patches go.

Quoted 1 line

Patch 2 hopefully explains my approach. Briefly,

Thanks, Cedric, will put those back.

Quoted 8 lines
Quoted 6 lines

1. sysv and posix ipc are both under CLONE_NEWIPC
2. the mqueue sb is per-ipc-namespace

So to create a new ipc namespace, you would

	unshare(CLONE_NEWIPC|CLONE_NEWNS);

does CLONE_NEWIPC requires CLONE_NEWNS ?

No, the mq_* syscalls don't need the fs to be actually mounted, and a container could just chroot("/vs1"); and mount -t mqueue under /vs1/dev/mqueue, not requiring a new mounts namespace.

Quoted 5 lines
Quoted 2 lines

umount /dev/mqueue
mount -t mqueue mqueue /dev/mqueue

the semantic looks good, much better than a 'newinstance' mount option.

Agreed. newinstance works for a pure filesystem like devpts, but it simply isn't a good fit for mqueue.

Quoted 4 lines

if CLONE_NEWNS is not required, what happens to the user mount (and the mq_ns below it) when the task dies. that's the big issue. if CLONE_NEWNS is required were safe, but I think Pavel made some objection to that.

(Huh, I just noticed get_ns_from_sb() doesn't seem to be called anywhere <scribble><scribble>)

Short version:
The user mount hangs around until someone umounts it. Now of course I expect that most users WILL want to do CLONE_NEWIPC|CLONE_NEWNS.

Long version:
Any VFS actions through mqueuefs will do:

	spin_lock(&mq_lock);
	ipc_ns = get_ipc_ns(inode->i_sb->s_fs_info);
	spin_unlock(&mq_lock);
where s_fs_info is the ipc_ns.  Freeing an ipc_ns does
	if (atomic_dec_and_lock(&ipc_ns->count, &mq_lock)) {
		mq_ns->mnt->mnt_sb->s_fs_info = NULL;
		spin_unlock(&mq_lock);
		mntput(mq_ns->mnt);
	}

So if a vfs_create() by a task in another ipc_ns is racing with the task exit of the last task in the ipc_ns, then either
1. the vfs_create() manages to pin the ipc_ns before the other task exits. So the task exit won't free the ipc_ns. The put_ipc_ns() at the end of vfs_create() will.
or
2. the task exits first, vfs_create() finds s_fs_info NULL, and returns -EACCES. Unlink simply succeeds.

Pavel, please let me know if you have issues with my approach.

Quoted 5 lines
Quoted 3 lines

It's perfectly valid to do vfs operations on files in another ipc_namespace's /dev/mqueue, but any use of mq_open(3) and friends will act in your own ipc_ns.

ok.

Nadia had written a cool set of ltp tests. They were based around the mount -o newinstance semantics so i'll have to see which ones are still relevant and rework some others, then will post them and repost the kernel patchset.

Thanks for taking a look, Cedric, and for getting this set going before.

-serge