Clone PTY namespaces
Subsystems (4)
v2 cover Clone PTY namespaces
Devpts namespace patchset
In continuation of the implementation of containers in mainline, we need to support multiple PTY namespaces so that the PTY index (ie the tty names) in one container is independent of the PTY indices of other containers. For instance this would allow each container to have a '/dev/pts/0' PTY and refer to different terminals.
[PATCH 1/7]: Propagate error code from devpts_pty_new [PATCH 2/7]: Factor out PTY index allocation [PATCH 3/7]: Enable multiple mounts of /dev/pts [PATCH 4/7]: Allow mknod of ptmx and tty in devpts [PATCH 5/7]: Implement get_pts_ns() and put_pts_ns() [PATCH 6/7]: Determine pts_ns from a pty's inode [PATCH 7/7]: Enable cloning PTY namespaces
Todo:
- This patchset depends on availability of additional clone flags. and relies on on Cedric's clone64 patchset.
- Needs some cleanup and more testing.
- Ensure patchset is bisect-safe
Changelog[v2]:
(Patches 4 and 6 differ significantly from [v1]. Others are mostly the same)
- [Alexey Dobriyan, Pavel Emelyanov] Removed the hack to check for user-space mount.
- [Serge Hallyn] Added rcu locking around access to sb->s_fs_info.
- [Serge Hallyn] Allow creation of /dev/pts/ptmx and /dev/pts/tty devices to simplify the process of finding the 'owning' pts-ns of the device (specially when accessed from parent-pts-ns) See patches 4 and 6 for details.
Changelog[v1]:
- Fixed circular reference by not caching the pts_ns in sb->s_fs_info (without incrementing reference count) and clearing the sb->s_fs_info when destroying the pts_ns
- To allow access to a child container's ptys from parent container, determine the 'pts_ns' of a 'pty' from its inode.
- Added a check (hack) to ensure user-space mount of /dev/pts is done before creating PTYs in a new pts-ns.
- Reorganized the patchset and removed redundant changes.
- Ported to work wih Cedric Le Goater's clone64() system call now that we are out of clone_flags.
Changelog[v0]:
This patchset is based on earlier versions developed by Serge Hallyn and Matt Helsley.
A message that is not in the archive (sent off-list or not yet received): 20080403070233.GA12262-r/[email protected]
PATCH v2 1/7 : Propagate error code from devpts_pty_new
From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 1/7][v2]: Propagate error code from devpts_pty_new
Have ptmx_open() propagate any error code returned by devpts_pty_new().
--- drivers/char/tty_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/char/tty_io.c +2 -2
@@ -2835,8 +2835,8 @@ static int ptmx_open(struct inode *inode filp->private_data = tty;file_move (filp, &tty->tty_files); - retval = -ENOMEM ; -if (devpts_pty_new (tty->link)) + retval =devpts_pty_new (tty->link); +if (retval) goto out1;check_tty_count (tty,"tty_open" );
PATCH v2 2/7 : Factor out PTY index allocation
In reply to a message not in the archiveFrom: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 2/7][v2]: Factor out PTY index allocation
Factor out the code used to allocate/free a pts index into new interfaces, devpts_new_index() and devpts_kill_index(). This localizes the external data structures used in managing the pts indices.
--- drivers/char/tty_io.c | 40 ++++++---------------------------------- fs/devpts/inode.c | 42 +++++++++++++++++++++++++++++++++++++++++- include/linux/devpts_fs.h | 4 ++++ 3 files changed, 51 insertions(+), 35 deletions(-)
include/linux/devpts_fs.h +4 -0
@@ -17,6 +17,8 @@#ifdef CONFIG_UNIX98_PTYS +int devpts_new_index (void ); +void devpts_kill_index (int idx);int devpts_pty_new (struct tty_struct *tty);/* mknod in devpts */ struct tty_struct *devpts_get_tty (int number);/* get tty structure */ void devpts_pty_kill (int number);/* unlink */
@@ -24,6 +26,8 @@ void devpts_pty_kill(int number); /* u#else /* Dummy stubs in the no-pty case */ +static inline int devpts_new_index (void ) {return -EINVAL ; } +static inline void devpts_kill_index (int idx) { }static inline int devpts_pty_new (struct tty_struct *tty) {return -EINVAL ; }static inline struct tty_struct *devpts_get_tty (int number) {return NULL ; }static inline void devpts_pty_kill (int number) { }
drivers/char/tty_io.c +6 -34
@@ -91,7 +91,6 @@#include <linux/module.h> #include <linux/smp_lock.h> #include <linux/device.h> -#include <linux/idr.h> #include <linux/wait.h> #include <linux/bitops.h> #include <linux/delay.h>
@@ -137,9 +136,6 @@ EXPORT_SYMBOL(tty_mutex);#ifdef CONFIG_UNIX98_PTYS extern struct tty_driver *ptm_driver;/* Unix98 pty masters; for /dev/ptmx */ -extern int pty_limit;/* Config limit on Unix98 ptys */ -static DEFINE_IDR (allocated_ptys); -static DEFINE_MUTEX (allocated_ptys_lock);static int ptmx_open (struct inode *,struct file *);#endif
@@ -2636,15 +2632,9 @@ static void release_dev(struct file *fil*/ release_tty (tty, idx); -#ifdef CONFIG_UNIX98_PTYS /* Make this pty number available for reallocation */ -if (devpts) { -mutex_lock (&allocated_ptys_lock); -idr_remove (&allocated_ptys, idx); -mutex_unlock (&allocated_ptys_lock); - } -#endif - +if (devpts) +devpts_kill_index (idx); }/**
@@ -2800,29 +2790,13 @@ static int ptmx_open(struct inode *inodestruct tty_struct *tty;int retval;int index; -int idr_ret;nonseekable_open (inode, filp);/* find a device that is not in use. */ -mutex_lock (&allocated_ptys_lock); -if (!idr_pre_get (&allocated_ptys,GFP_KERNEL )) { -mutex_unlock (&allocated_ptys_lock); -return -ENOMEM ; - } - idr_ret =idr_get_new (&allocated_ptys,NULL , &index); -if (idr_ret <0 ) { -mutex_unlock (&allocated_ptys_lock); -if (idr_ret == -EAGAIN ) -return -ENOMEM ; -return -EIO ; - } -if (index >= pty_limit) { -idr_remove (&allocated_ptys, index); -mutex_unlock (&allocated_ptys_lock); -return -EIO ; - } -mutex_unlock (&allocated_ptys_lock); + index =devpts_new_index (); +if (index <0 ) +return index;mutex_lock (&tty_mutex); retval =init_dev (ptm_driver, index, &tty);
@@ -2847,9 +2821,7 @@ out1:release_dev (filp);return retval; out: -mutex_lock (&allocated_ptys_lock); -idr_remove (&allocated_ptys, index); -mutex_unlock (&allocated_ptys_lock); +devpts_kill_index (index); return retval; }#endif
fs/devpts/inode.c +41 -1
62 lines not on this page · raw message
PATCH v2 3/7 : Enable multiple mounts of /dev/pts
In reply to a message not in the archiveFrom: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 3/7][v2]: Enable multiple mounts of /dev/pts
To support multiple PTY namespaces, we should be allow multiple mounts of /dev/pts, once within each PTY namespace.
This patch removes the get_sb_single() in devpts_get_sb() and uses test and set sb interfaces to allow remounting /dev/pts. The patch also removes the globals, 'devpts_mnt', 'devpts_root' and uses a skeletal 'init_pts_ns' to store the vfsmount.
Changelog [v3]:
- Removed some unnecessary comments from devpts_set_sb()
Changelog [v2]:
- (Pavel Emelianov/Serge Hallyn) Remove reference to pts_ns from sb->s_fs_info to fix the circular reference (/dev/pts is not unmounted unless the pts_ns is destroyed, so we don't need a reference to the pts_ns).
--- fs/devpts/inode.c | 151 +++++++++++++++++++++++++++++++++++++--------- include/linux/devpts_fs.h | 11 +++ 2 files changed, 134 insertions(+), 28 deletions(-)
include/linux/devpts_fs.h +11 -0
@@ -14,6 +14,17 @@#define _LINUX_DEVPTS_FS_H#include <linux/errno.h> +#include <linux/nsproxy.h> +#include <linux/kref.h> +#include <linux/idr.h> + +struct pts_namespace { +struct kref kref; +struct idr allocated_ptys; +struct vfsmount *mnt; +}; + +extern struct pts_namespace init_pts_ns;#ifdef CONFIG_UNIX98_PTYS
fs/devpts/inode.c +123 -28
277 lines not on this page · raw message
PATCH v2 4/7 : Allow mknod of ptmx and tty in devpts
In reply to a message not in the archiveFrom: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 4/7][v2]: Allow mknod of ptmx and tty in devpts
We want to allow administrators to access PTYs in a descendant pts-namespaces, for instance "echo foo > /vserver/vserver1/dev/pts/0". To enable such access we must hold a reference to the pts-ns in which the device (ptmx or the slave pty) exists.
Note that we cannot use the pts-ns of the 'current' process since that pts-ns could be different from the pts-ns in which the PTY device was created. So we find the pts-ns from the inode of the PTY (inode->i_sb->s_fs_info). While this would work for the slave PTY devices like /dev/pts/0, it would not work for either the master PTY device (/dev/ptmx) or controlling terminal (/dev/tty).
To uniformly handle the master, slave and controlling ttys, we allow creation of 'ptmx' and 'tty' devices in /dev/pts. When creating containers, the administrator can then:
$ umount /dev/pts $ mount -t devpts lxcpts /dev/pts $ mknod /dev/pts/ptmx c 5 2 $ mknod /dev/pts/tty c 5 0 $ rm /dev/ptmx /dev/tty $ ln -s /dev/pts/ptmx /dev/ptmx $ ln -s /dev/pts/tty /dev/tty
With this, even if the 'ptmx' is accessed from parent pts-ns we still find and hold the pts-ns in which 'ptmx' actually belongs.
This patch merely allows creation of /dev/pts/ptmx and /dev/pts/tty. We hold a reference to the dentries for these nodes to pin them in memory and use 'kill_litter_super()' while unmounting to ensure we drop these dentries.
TODO: Ability to unlink the /dev/pts/ptmx and /dev/pts/tty nodes.
Note: if /dev/ptmx is a symlink to /vserver/vserver1/dev/pts/ptmx an open
of /dev/ptmx in init-pts-ns will create a PTY in 'vserver1' !
--- fs/devpts/inode.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-)
fs/devpts/inode.c +51 -4
@@ -58,7 +58,6 @@ struct pts_namespace init_pts_ns = { .mnt = NULL, }; -static int devpts_remount (struct super_block *sb,int *flags,char *data) {char *p;
@@ -122,6 +121,54 @@ static const struct super_operations dev .show_options = devpts_show_options, }; + +static int devpts_mknod (struct inode *dir,struct dentry *dentry, +int mode,dev_t rdev) +{ +int inum; +struct inode *inode; +struct super_block *sb = dir->i_sb; + +if (dentry->d_inode) +return -EEXIST ; + +if (!S_ISCHR (mode)) +return -EPERM ; + +if (rdev ==MKDEV (TTYAUX_MAJOR ,0 )) + inum =2 ; +else if (rdev ==MKDEV (TTYAUX_MAJOR ,2 )) + inum =3 ; +else +return -EPERM ; + + inode =new_inode (sb); +if (!inode) +return -ENOMEM ; + + inode->i_ino = inum; + inode->i_uid = inode->i_gid =0 ; + inode->i_blocks =0 ; + inode->i_mtime = inode->i_atime = inode->i_ctime =CURRENT_TIME ; + +init_special_inode (inode, mode, rdev); + +d_instantiate (dentry, inode); +/* +* Get a reference to the dentry so the device-nodes persist +* even when there are no active references to them. We use +* kill_litter_super() to remove this entry when unmounting +* devpts. +*/ +dget (dentry); +return 0 ; +} + +const struct inode_operations devpts_dir_inode_operations = { + .lookup = simple_lookup, + .mknod = devpts_mknod, +}; +static int devpts_fill_super (struct super_block *s,void *data,int silent) {
@@ -141,7 +188,7 @@ devpts_fill_super(struct super_block *s, inode->i_blocks =0 ; inode->i_uid = inode->i_gid =0 ; inode->i_mode =S_IFDIR |S_IRUGO |S_IXUGO |S_IWUSR ; - inode->i_op = &simple_dir_inode_operations; + inode->i_op = &devpts_dir_inode_operations; inode->i_fop = &simple_dir_operations; inode->i_nlink =2 ;
@@ -214,7 +261,7 @@ static int devpts_get_sb(struct file_sysstatic void devpts_kill_sb (struct super_block *sb) { sb->s_fs_info =NULL ; -kill_anon_super (sb); +kill_litter_super (sb); }static struct file_system_type devpts_fs_type = {
@@ -303,7 +350,7 @@ int devpts_pty_new(struct tty_struct *ttif (!inode)return -ENOMEM ; - inode->i_ino = number+2 ; + inode->i_ino = number+4 ; inode->i_uid = config.setuid ? config.uid : current->fsuid; inode->i_gid = config.setgid ? config.gid : current->fsgid; inode->i_mtime = inode->i_atime = inode->i_ctime =CURRENT_TIME ;
PATCH v2 5/7 : Implement get_pts_ns() and put_pts_ns()
In reply to a message not in the archiveFrom: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 5/7][v2]: Implement get_pts_ns() and put_pts_ns()
Implement get_pts_ns() and put_pts_ns() interfaces.
--- include/linux/devpts_fs.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-)
include/linux/devpts_fs.h +20 -1
@@ -27,13 +27,26 @@ struct pts_namespace {extern struct pts_namespace init_pts_ns;#ifdef CONFIG_UNIX98_PTYS -int devpts_new_index (void );void devpts_kill_index (int idx);int devpts_pty_new (struct tty_struct *tty);/* mknod in devpts */ struct tty_struct *devpts_get_tty (int number);/* get tty structure */ void devpts_pty_kill (int number);/* unlink */ +static inline void free_pts_ns (struct kref *ns_kref) { } + +static inline struct pts_namespace *get_pts_ns (struct pts_namespace *ns) +{ +if (ns && (ns != &init_pts_ns)) +kref_get (&ns->kref); +return ns; +} +static inline void put_pts_ns (struct pts_namespace *ns) +{ +if (ns && (ns != &init_pts_ns)) +kref_put (&ns->kref, free_pts_ns); +} +#else /* Dummy stubs in the no-pty case */
@@ -43,6 +56,12 @@ static inline int devpts_pty_new(structstatic inline struct tty_struct *devpts_get_tty (int number) {return NULL ; }static inline void devpts_pty_kill (int number) { } +static inline struct pts_namespace *get_pts_ns (struct pts_namespace *ns) +{ +return &init_pts_ns; +} + +static inline void put_pts_ns (struct pts_namespace *ns) { }#endif
PATCH v2 6/7 : Determine pts_ns from a pty's inode
In reply to a message not in the archiveFrom: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 6/7][v2]: Determine pts_ns from a pty's inode.
The devpts interfaces currently operate on a specific pts namespace which they get from the 'current' task.
With implementation of containers and cloning of PTS namespaces, we want to be able to access PTYs in a child-pts-ns from a parent-pts-ns. For instance we could bind-mount and pivot-root the child container on '/vserver/vserver1' and then access the "pts/0" of 'vserver1' using
$ echo foo > /vserver/vserver1/dev/pts/0
The task doing the above 'echo' could be in parent-pts-ns. So we find the 'pts-ns' of the above file from the inode representing the above file rather than from the 'current' task.
Note that we need to find and hold a reference to the pts_ns to prevent the pts_ns from being freed while it is being accessed from 'outside'.
This patch implements, 'pts_ns_from_inode()' which returns the pts_ns using 'inode->i_sb->s_fs_info'.
Since, the 'inode' information is not visible inside devpts code itself, this patch modifies the tty driver code to determine the pts_ns and passes it into devpts.
Changelog [v2]:
[Serge Hallyn] Use rcu to access sb->s_fs_info.
[Serge Hallyn] Simplify handling of ptmx and tty devices by expecting
user to create them in /dev/pts (see also devpts-mknod patch)
--- drivers/char/pty.c | 13 +++++- drivers/char/tty_io.c | 93 +++++++++++++++++++++++++++++++++++++++------- fs/devpts/inode.c | 19 +++------ include/linux/devpts_fs.h | 38 ++++++++++++++++-- 4 files changed, 131 insertions(+), 32 deletions(-)
include/linux/devpts_fs.h +33 -5
@@ -17,6 +17,7 @@#include <linux/nsproxy.h> #include <linux/kref.h> #include <linux/idr.h> +#include <linux/fs.h> struct pts_namespace {struct kref kref;
@@ -26,12 +27,39 @@ struct pts_namespace {extern struct pts_namespace init_pts_ns; +#define DEVPTS_SUPER_MAGIC 0x1cd1 + +static inline struct pts_namespace *current_pts_ns (void ) +{ +return &init_pts_ns; +} + +static inline struct pts_namespace *pts_ns_from_inode (struct inode *inode) +{ +/* +* If this file exists on devpts, return the pts_ns from the +* devpts super-block. Otherwise just use the pts-ns of the +* calling task. +*/ +if (inode->i_sb->s_magic ==DEVPTS_SUPER_MAGIC ) +return rcu_dereference (inode->i_sb->s_fs_info); + +return current_pts_ns (); +} + +#ifdef CONFIG_UNIX98_PTYS -int devpts_new_index (void ); -void devpts_kill_index (int idx); -int devpts_pty_new (struct tty_struct *tty);/* mknod in devpts */ -struct tty_struct *devpts_get_tty (int number);/* get tty structure */ -void devpts_pty_kill (int number);/* unlink */ +int devpts_new_index (struct pts_namespace *pts_ns); +void devpts_kill_index (struct pts_namespace *pts_ns,int idx); + +/* mknod in devpts */ +int devpts_pty_new (struct pts_namespace *pts_ns,struct tty_struct *tty); + +/* get tty structure */ +struct tty_struct *devpts_get_tty (struct pts_namespace *pts_ns,int number); + +/* unlink */ +void devpts_pty_kill (struct pts_namespace *pts_ns,int number);static inline void free_pts_ns (struct kref *ns_kref) { }
drivers/char/tty_io.c +80 -13
176 lines not on this page · raw message
fs/devpts/inode.c +7 -12
@@ -23,8 +23,6 @@#include <linux/fsnotify.h> #include <linux/seq_file.h> -#define DEVPTS_SUPER_MAGIC 0x1cd1 -#define DEVPTS_DEFAULT_MODE 0600extern int pty_limit;/* Config limit on Unix98 ptys */
@@ -283,11 +281,10 @@ static struct dentry *get_node(struct dereturn lookup_one_len (s, root,sprintf (s,"%d" , num)); } -int devpts_new_index (void ) +int devpts_new_index (struct pts_namespace *pts_ns) {int index;int idr_ret; -struct pts_namespace *pts_ns = &init_pts_ns; retry:if (!idr_pre_get (&pts_ns->allocated_ptys,GFP_KERNEL )) {
@@ -312,16 +309,15 @@ retry:return index; } -void devpts_kill_index (int idx) +void devpts_kill_index (struct pts_namespace *pts_ns,int idx) { -struct pts_namespace *pts_ns = &init_pts_ns;down (&allocated_ptys_lock);idr_remove (&pts_ns->allocated_ptys, idx);up (&allocated_ptys_lock); } -int devpts_pty_new (struct tty_struct *tty) +int devpts_pty_new (struct pts_namespace *pts_ns,struct tty_struct *tty) {int number = tty->index;/* tty layer puts index from devpts_new_index() in here */ struct tty_driver *driver = tty->driver;
@@ -330,7 +326,6 @@ int devpts_pty_new(struct tty_struct *ttstruct dentry *root;struct vfsmount *mnt;struct inode *inode; -struct pts_namespace *pts_ns = &init_pts_ns;/* We're supposed to be given the slave end of a pty */ BUG_ON (driver->type !=TTY_DRIVER_TYPE_PTY );
@@ -369,13 +364,13 @@ int devpts_pty_new(struct tty_struct *ttreturn 0 ; } -struct tty_struct *devpts_get_tty (int number) +struct tty_struct *devpts_get_tty (struct pts_namespace *pts_ns,int number) {struct vfsmount *mnt;struct dentry *dentry;struct tty_struct *tty; - mnt = init_pts_ns.mnt; + mnt = pts_ns->mnt; dentry =get_node (mnt->mnt_root, number);
@@ -391,12 +386,12 @@ struct tty_struct *devpts_get_tty(int nureturn tty; } -void devpts_pty_kill (int number) +void devpts_pty_kill (struct pts_namespace *pts_ns,int number) {struct dentry *dentry;struct dentry *root; - root = init_pts_ns.mnt->mnt_root; + root = pts_ns->mnt->mnt_root; dentry =get_node (root, number);
drivers/char/pty.c +11 -2
@@ -37,6 +37,9 @@ static struct tty_driver *pts_driver;static void pty_close (struct tty_struct * tty,struct file * filp) { +struct inode *inode; +struct pts_namespace *pts_ns; +if (!tty)return ;if (tty->driver->subtype ==PTY_TYPE_MASTER ) {
@@ -58,8 +61,14 @@ static void pty_close(struct tty_structif (tty->driver->subtype ==PTY_TYPE_MASTER ) {set_bit (TTY_OTHER_CLOSED , &tty->flags);#ifdef CONFIG_UNIX98_PTYS -if (tty->driver == ptm_driver) -devpts_pty_kill (tty->index); +if (tty->driver == ptm_driver) { + inode = filp->f_path.dentry->d_inode; +rcu_read_lock (); + pts_ns =pts_ns_from_inode (inode); +rcu_read_unlock (); + +devpts_pty_kill (pts_ns, tty->index); + }#endif tty_vhangup (tty->link); }
A message that is not in the archive (sent off-list or not yet received): 20080403071107.GF12429-r/[email protected]
Re: [PATCH 6/7][v2]: Determine pts_ns from a pty's inode
Quoted 218 lines · Quoting sukadev-r/[email protected]…
From: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 6/7][v2]: Determinepts_nsfrom a pty's inode.The devpts interfaces currently operate on a specific pts namespace which they get from the 'current' task.
With implementation of containers and cloning of PTS namespaces, we want to be able to access PTYs in a child-pts-ns from a parent-pts-ns. For instance we could bind-mount and pivot-root the child container on '/vserver/vserver1' and then access the "pts/0" of 'vserver1' using
$ echo foo > /vserver/vserver1/dev/pts/0The task doing the above 'echo' could be in parent-pts-ns. So we find the 'pts-ns' of the above file from the inode representing the above file rather than from the 'current' task.
Note that we need to find and hold a reference to the
pts_nsto prevent thepts_nsfrom being freed while it is being accessed from 'outside'.This patch implements, '
pts_ns_from_inode()' which returns thepts_nsusing 'inode->i_sb->s_fs_info'.Since, the 'inode' information is not visible inside devpts code itself, this patch modifies the tty driver code to determine the
pts_nsand passes it into devpts.Changelog [v2]:
[Serge Hallyn] Use rcu to accesssb->s_fs_info.[Serge Hallyn] Simplify handling of ptmx and tty devices by expecting
user to create them in/dev/pts(see also devpts-mknod patch)Signed-off-by Sukadev Bhattiprolu <sukadev-r/[email protected]>--- drivers/char/pty.c | 13 +++++- drivers/char/tty_io.c | 93 +++++++++++++++++++++++++++++++++++++++------- fs/devpts/inode.c | 19 +++------ include/linux/devpts_fs.h | 38 ++++++++++++++++-- 4 files changed, 131 insertions(+), 32 deletions(-)Index:
2.6.25-rc5-mm1/include/linux/devpts_fs.h
===================================================================include/linux/devpts_fs.h +33
-5@@ -17,6 +17,7 @@#include <linux/nsproxy.h> #include <linux/kref.h> #include <linux/idr.h> +#include <linux/fs.h> struct pts_namespace {struct kref kref;@@ -26,12 +27,39 @@ struct pts_namespace {extern struct pts_namespace init_pts_ns; +#define DEVPTS_SUPER_MAGIC 0x1cd1 + +static inline struct pts_namespace *current_pts_ns (void ) +{ +return &init_pts_ns; +} + +static inline struct pts_namespace *pts_ns_from_inode (struct inode *inode) +{ +/* +* If this file exists on devpts, return the pts_ns from the +* devpts super-block. Otherwise just use the pts-ns of the +* calling task. +*/ +if (inode->i_sb->s_magic ==DEVPTS_SUPER_MAGIC ) +return rcu_dereference (inode->i_sb->s_fs_info); + +return current_pts_ns (); +} + +#ifdef CONFIG_UNIX98_PTYS -int devpts_new_index (void ); -void devpts_kill_index (int idx); -int devpts_pty_new (struct tty_struct *tty);/* mknod in devpts */ -struct tty_struct *devpts_get_tty (int number);/* get tty structure */ -void devpts_pty_kill (int number);/* unlink */ +int devpts_new_index (struct pts_namespace *pts_ns); +void devpts_kill_index (struct pts_namespace *pts_ns,int idx); + +/* mknod in devpts */ +int devpts_pty_new (struct pts_namespace *pts_ns,struct tty_struct *tty); + +/* get tty structure */ +struct tty_struct *devpts_get_tty (struct pts_namespace *pts_ns,int number); + +/* unlink */ +void devpts_pty_kill (struct pts_namespace *pts_ns,int number);static inline void free_pts_ns (struct kref *ns_kref) { }Index:
2.6.25-rc5-mm1/drivers/char/tty_io.c
===================================================================drivers/char/tty_io.c +48
-6@@ -2064,8 +2064,8 @@ static void tty_line_name(struct tty_dri* relaxed for the (most common) case of reopening a tty. */ -static int init_dev (struct tty_driver *driver,int idx, -struct tty_struct **ret_tty) +static int init_dev (struct tty_driver *driver,struct pts_namespace *pts_ns, +int idx,struct tty_struct **ret_tty) {struct tty_struct *tty, *o_tty;struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;@@ -2074,7 +2074,11 @@ static int init_dev(struct tty_driver *d/* check whether we're reopening an existing tty */ if (driver->flags &TTY_DRIVER_DEVPTS_MEM ) { - tty =devpts_get_tty (idx); + tty =devpts_get_tty (pts_ns, idx); +if (IS_ERR (tty)) { + retval =PTR_ERR (tty); + goto end_init; + }/* * If we don't have a tty here on a slave open, it's because * the master already started the close process and there's @@ -2361,6 +2365,31 @@ static void release_tty(struct tty_struc }/* +* When opening /dev/tty and /dev/ptmx, use the pts-ns of the calling +* process. For any other pts device, use the pts-ns, in which the +* device was created. This latter case is needed when the pty is +* being accessed from a parent container. +* +* Eg: Suppose the user used bind-mount and pivot-root to mount a +* child- container's root on /vs/vs1. Then "/vs/vs1/dev/pts/0" +* in parent container and "/dev/pts/0" in child container would +* refer to the same device. +* +* When parent-container opens, "/vs/vs1/dev/pts/0" we find and +* grab/drop reference to child container's pts-ns (using @filp). +*/ +struct pts_namespace *pty_pts_ns (struct tty_driver *driver,struct inode *inode) +{ +struct pts_namespace *pts_ns; + + pts_ns =NULL ; +if (driver->flags &TTY_DRIVER_DEVPTS_MEM ) + pts_ns =pts_ns_from_inode (inode); + +return pts_ns; +} + +/* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. @@ -2376,10 +2405,12 @@ static void release_dev(struct file *filint idx;char buf[64 ];unsigned long flags; +struct pts_namespace *pts_ns; +struct inode *inode; + inode = filp->f_path.dentry->d_inode; tty = (struct tty_struct *)filp->private_data; -if (tty_paranoia_check (tty, filp->f_path.dentry->d_inode, -"release_dev" )) +if (tty_paranoia_check (tty, inode,"release_dev" ))return ;check_tty_count (tty,"release_dev" );@@ -2392,6 +2423,12 @@ static void release_dev(struct file *fil devpts = (tty->driver->flags &TTY_DRIVER_DEVPTS_MEM ) !=0 ; o_tty = tty->link; +/* +* We already have a reference to pts_ns here, so it cannot +* be going away. +*/ + pts_ns =pty_pts_ns (tty->driver, inode); +#ifdef TTY_PARANOIA_CHECK if (idx <0 || idx >= tty->driver->num) { printk(KERN_DEBUG "release_dev: bad idx when trying to "@@ -2569,6 +2606,10 @@ static void release_dev(struct file *filmutex_unlock (&tty_mutex); +/* drop the reference from ptmx_open/tty_open() */ +if (devpts) +put_pts_ns (pts_ns); +/* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing))return ;@@ -2634,7 +2675,7 @@ static void release_dev(struct file *fil/* Make this pty number available for reallocation */ if (devpts) -devpts_kill_index (idx); +devpts_kill_index (pts_ns, idx); }/** @@ -2666,6 +2707,7 @@ static int tty_open(struct inode *inode,int index;dev_t device = inode->i_rdev;unsigned short saved_flags = filp->f_flags; +struct pts_namespace *pts_ns;nonseekable_open (inode, filp);@@ -2715,10 +2757,27 @@ retry_open:return -ENODEV ;
drivers/char/tty_io.c retry_open: · line 2766
Quoted 10 lines
drivers/char/tty_io.c +7
-1} got_driver: - retval =init_dev (driver, index, &tty); + +/* +* What pts-ns do we want to use when opening "/dev/tty" ? +* Sounds like current_pts_ns(), but what should happen +* if parent pts ns does: +* +* echo foo > /vs/vs1/dev/tty
You'll want to remove this comment, right? Your patch 4 solved this problem?
-serge
Quoted 178 lines
drivers/char/tty_io.c +24
-6+* (See Serge's setupvs1 script for the /vs/vs1...) +*/ +rcu_read_lock (); + pts_ns =pty_pts_ns (driver, inode); +get_pts_ns (pts_ns); +rcu_read_unlock (); + + retval =init_dev (driver, pts_ns, index, &tty);mutex_unlock (&tty_mutex); -if (retval) +if (retval) { +put_pts_ns (pts_ns);return retval; + } filp->private_data = tty;file_move (filp, &tty->tty_files);@@ -2790,16 +2849,22 @@ static int ptmx_open(struct inode *inodestruct tty_struct *tty;int retval;int index; +struct pts_namespace *pts_ns;nonseekable_open (inode, filp); +rcu_read_lock (); + pts_ns =pts_ns_from_inode (inode); +get_pts_ns (pts_ns); +rcu_read_unlock (); +/* find a device that is not in use. */ - index =devpts_new_index (); + retval = index =devpts_new_index (pts_ns);if (index <0 ) -return index; + goto drop_ns;mutex_lock (&tty_mutex); - retval =init_dev (ptm_driver, index, &tty); + retval =init_dev (ptm_driver, pts_ns, index, &tty);mutex_unlock (&tty_mutex);if (retval)@@ -2809,7 +2874,7 @@ static int ptmx_open(struct inode *inode filp->private_data = tty;file_move (filp, &tty->tty_files); - retval =devpts_pty_new (tty->link); + retval =devpts_pty_new (pts_ns, tty->link);if (retval) goto out1;@@ -2821,7 +2886,9 @@ out1:release_dev (filp);return retval; out: -devpts_kill_index (index); +devpts_kill_index (pts_ns, index); +drop_ns: +put_pts_ns (pts_ns); return retval; }#endif Index:
2.6.25-rc5-mm1/fs/devpts/inode.c
===================================================================fs/devpts/inode.c +7
-12@@ -23,8 +23,6 @@#include <linux/fsnotify.h> #include <linux/seq_file.h> -#define DEVPTS_SUPER_MAGIC 0x1cd1 -#define DEVPTS_DEFAULT_MODE 0600extern int pty_limit;/* Config limit on Unix98 ptys */ @@ -283,11 +281,10 @@ static struct dentry *get_node(struct dereturn lookup_one_len (s, root,sprintf (s,"%d" , num)); } -int devpts_new_index (void ) +int devpts_new_index (struct pts_namespace *pts_ns) {int index;int idr_ret; -struct pts_namespace *pts_ns = &init_pts_ns; retry:if (!idr_pre_get (&pts_ns->allocated_ptys,GFP_KERNEL )) {@@ -312,16 +309,15 @@ retry:return index; } -void devpts_kill_index (int idx) +void devpts_kill_index (struct pts_namespace *pts_ns,int idx) { -struct pts_namespace *pts_ns = &init_pts_ns;down (&allocated_ptys_lock);idr_remove (&pts_ns->allocated_ptys, idx);up (&allocated_ptys_lock); } -int devpts_pty_new (struct tty_struct *tty) +int devpts_pty_new (struct pts_namespace *pts_ns,struct tty_struct *tty) {int number = tty->index;/* tty layer puts index from devpts_new_index() in here */ struct tty_driver *driver = tty->driver;@@ -330,7 +326,6 @@ int devpts_pty_new(struct tty_struct *ttstruct dentry *root;struct vfsmount *mnt;struct inode *inode; -struct pts_namespace *pts_ns = &init_pts_ns;/* We're supposed to be given the slave end of a pty */ BUG_ON (driver->type !=TTY_DRIVER_TYPE_PTY );@@ -369,13 +364,13 @@ int devpts_pty_new(struct tty_struct *ttreturn 0 ; } -struct tty_struct *devpts_get_tty (int number) +struct tty_struct *devpts_get_tty (struct pts_namespace *pts_ns,int number) {struct vfsmount *mnt;struct dentry *dentry;struct tty_struct *tty; - mnt = init_pts_ns.mnt; + mnt = pts_ns->mnt; dentry =get_node (mnt->mnt_root, number);@@ -391,12 +386,12 @@ struct tty_struct *devpts_get_tty(int nureturn tty; } -void devpts_pty_kill (int number) +void devpts_pty_kill (struct pts_namespace *pts_ns,int number) {struct dentry *dentry;struct dentry *root; - root = init_pts_ns.mnt->mnt_root; + root = pts_ns->mnt->mnt_root; dentry =get_node (root, number);Index:
2.6.25-rc5-mm1/drivers/char/pty.c
===================================================================drivers/char/pty.c +11
-2@@ -37,6 +37,9 @@ static struct tty_driver *pts_driver;static void pty_close (struct tty_struct * tty,struct file * filp) { +struct inode *inode; +struct pts_namespace *pts_ns; +if (!tty)return ;if (tty->driver->subtype ==PTY_TYPE_MASTER ) {@@ -58,8 +61,14 @@ static void pty_close(struct tty_structif (tty->driver->subtype ==PTY_TYPE_MASTER ) {set_bit (TTY_OTHER_CLOSED , &tty->flags);#ifdef CONFIG_UNIX98_PTYS -if (tty->driver == ptm_driver) -devpts_pty_kill (tty->index); +if (tty->driver == ptm_driver) { + inode = filp->f_path.dentry->d_inode; +rcu_read_lock (); + pts_ns =pts_ns_from_inode (inode); +rcu_read_unlock (); + +devpts_pty_kill (pts_ns, tty->index); + }#endif tty_vhangup (tty->link); }
A message that is not in the archive (sent off-list or not yet received): 20080404163219.GB12015-6s5zFf/[email protected]
Re: [PATCH 6/7][v2]: Determine pts_ns from a pty's inode
Serge E. Hallyn [serue-r/[email protected]] wrote:
| > + | > + /* | > + * What pts-ns do we want to use when opening "/dev/tty" ? | > + * Sounds like current_pts_ns(), but what should happen | > + * if parent pts ns does: | > + * | > + * echo foo > /vs/vs1/dev/tty | | You'll want to remove this comment, right? Your patch 4 solved | this problem?
Yes, Will remove while porting to rc8-mm1.
Should I go ahead and post as RFC to lkml ?
Sukadev
A message that is not in the archive (sent off-list or not yet received): 20080404173740.GA29504-r/[email protected]
Re: [PATCH 6/7][v2]: Determine pts_ns from a pty's inode
Quoted 12 lines · Quoting sukadev-r/[email protected]…
Serge E. Hallyn [serue-r/[email protected]] wrote:
| > + | > + /* | > + * What pts-ns do we want to use when opening "/dev/tty" ? | > + * Sounds like current_pts_ns(), but what should happen | > + * if parent pts ns does: | > + * | > + * echo foo > /vs/vs1/dev/tty | | You'll want to remove this comment, right? Your patch 4 solved | this problem?
Quoted 5 lines
Yes, Will remove while porting to rc8-mm1.
Should I go ahead and post as RFC to lkml ?
Sukadev
Well I haven't given it enough scrutiny to really do justice to the change you've made. But the overall approach looks right, and since I'll be on the road, don't hold off on my account.
-serge
PATCH v2 7/7 : Enable cloning PTY namespaces
In reply to a message not in the archiveFrom: Sukadev Bhattiprolu <sukadev-r/[email protected]>
Subject: [PATCH 7/7][v2]: Enable cloning PTY namespaces
Enable cloning PTY namespaces.
Changelog[v2]:
[Serge Hallyn]: Use rcu to access sb->s_fs_info.
--- fs/devpts/inode.c | 84 ++++++++++++++++++++++++++++++++++++++++++++-- include/linux/devpts_fs.h | 22 ++++++++++-- include/linux/init_task.h | 1 include/linux/nsproxy.h | 2 + include/linux/sched.h | 1 kernel/fork.c | 2 - kernel/nsproxy.c | 17 ++++++++- 7 files changed, 122 insertions(+), 7 deletions(-)
include/linux/sched.h +1 -0
@@ -28,6 +28,7 @@#define CLONE_NEWPID 0x20000000/* New pid namespace */ #define CLONE_NEWNET 0x40000000/* New network namespace */ #define CLONE_IO 0x80000000/* Clone io context */ +#define CLONE_NEWPTS 0x0000000200000000ULL/* Clone pts ns */ /* * Scheduling policies
include/linux/nsproxy.h +2 -0
@@ -8,6 +8,7 @@ struct mnt_namespace;struct uts_namespace ;struct ipc_namespace ;struct pid_namespace ; +struct pts_namespace ;/* * A structure to contain pointers to all per-process
@@ -29,6 +30,7 @@ struct nsproxy {struct pid_namespace *pid_ns;struct user_namespace *user_ns;struct net *net_ns; +struct pts_namespace *pts_ns; };extern struct nsproxy init_nsproxy;
include/linux/init_task.h +1 -0
@@ -78,6 +78,7 @@ extern struct nsproxy init_nsproxy; .mnt_ns = NULL, \ INIT_NET_NS(net_ns) \ INIT_IPC_NS(ipc_ns) \ + .pts_ns = &init_pts_ns, \ .user_ns = &init_user_ns, \ }
include/linux/devpts_fs.h +20 -2
@@ -31,7 +31,7 @@ extern struct pts_namespace init_pts_ns;static inline struct pts_namespace *current_pts_ns (void ) { -return &init_pts_ns; +return current->nsproxy->pts_ns; }static inline struct pts_namespace *pts_ns_from_inode (struct inode *inode)
@@ -61,7 +61,8 @@ struct tty_struct *devpts_get_tty(struct/* unlink */ void devpts_pty_kill (struct pts_namespace *pts_ns,int number); -static inline void free_pts_ns (struct kref *ns_kref) { } +extern struct pts_namespace *new_pts_ns (void ); +extern void free_pts_ns (struct kref *kref);static inline struct pts_namespace *get_pts_ns (struct pts_namespace *ns) {
@@ -75,6 +76,15 @@ static inline void put_pts_ns(struct pts kref_put(&ns->kref, free_pts_ns); } +static inline struct pts_namespace *copy_pts_ns (u64 flags, +struct pts_namespace *old_ns) +{ +if (flags &CLONE_NEWPTS ) +return new_pts_ns (); +else +return get_pts_ns (old_ns); +} +#else /* Dummy stubs in the no-pty case */
@@ -90,6 +100,14 @@ static inline struct pts_namespace *get_ }static inline void put_pts_ns (struct pts_namespace *ns) { } + +static inline struct pts_namespace *copy_pts_ns (u64 flags, +struct pts_namespace *old_ns) +{ +if (flags &CLONE_NEWPTS ) +return ERR_PTR (-EINVAL ); +return old_ns; +}#endif
fs/devpts/inode.c +82 -2
138 lines not on this page · raw message
kernel/fork.c +1 -1
@@ -1713,7 +1713,7 @@ static long do_unshare(u64 unshare_flagsif (unshare_flags & ~(CLONE_THREAD |CLONE_FS |CLONE_NEWNS |CLONE_SIGHAND |CLONE_VM |CLONE_FILES |CLONE_SYSVSEM |CLONE_NEWUTS |CLONE_NEWIPC |CLONE_NEWUSER | -CLONE_NEWNET )) +CLONE_NEWNET |CLONE_NEWPTS )) goto bad_unshare_out;if ((err =unshare_thread (unshare_flags)))
kernel/nsproxy.c +15 -2
@@ -21,6 +21,7 @@#include <linux/utsname.h> #include <linux/pid_namespace.h> #include <net/net_namespace.h> +#include <linux/devpts_fs.h> #include <linux/ipc_namespace.h> static struct kmem_cache *nsproxy_cachep;
@@ -93,8 +94,17 @@ static struct nsproxy *create_new_namesp goto out_net; } + new_nsp->pts_ns =copy_pts_ns (flags, tsk->nsproxy->pts_ns); +if (IS_ERR (new_nsp->pts_ns)) { + err =PTR_ERR (new_nsp->pts_ns); + goto out_pts; + } +return new_nsp; +out_pts: +if (new_nsp->net_ns) +put_net (new_nsp->net_ns); out_net:if (new_nsp->user_ns)put_user_ns (new_nsp->user_ns);
@@ -131,7 +141,8 @@ int copy_namespaces(u64 flags, struct taget_nsproxy (old_ns);if (!(flags & (CLONE_NEWNS |CLONE_NEWUTS |CLONE_NEWIPC | -CLONE_NEWUSER |CLONE_NEWPID |CLONE_NEWNET ))) +CLONE_NEWUSER |CLONE_NEWPID |CLONE_NEWNET | +CLONE_NEWPTS )))return 0 ;if (!capable (CAP_SYS_ADMIN )) {
@@ -170,6 +181,8 @@ void free_nsproxy(struct nsproxy *ns)put_pid_ns (ns->pid_ns);if (ns->user_ns)put_user_ns (ns->user_ns); +if (ns->pts_ns) +put_pts_ns (ns->pts_ns);put_net (ns->net_ns);kmem_cache_free (nsproxy_cachep, ns); }
@@ -184,7 +197,7 @@ int unshare_nsproxy_namespaces(u64 unshaint err =0 ;if (!(unshare_flags & (CLONE_NEWNS |CLONE_NEWUTS |CLONE_NEWIPC | -CLONE_NEWUSER |CLONE_NEWNET ))) +CLONE_NEWUSER |CLONE_NEWNET |CLONE_NEWPTS )))return 0 ;if (!capable (CAP_SYS_ADMIN ))