cr: pty: don't use required_id, change ptmx_open()
Subsystems (3)
RFC 1/1 cr: pty: don't use required_id, change ptmx_open()
Ok so here is the patch to do unix98 pty restore by making invasive changes to ptmx_open() instead of using task_struct->required_id to pass an index. Patch generated against the cr tree with the original pty patches, as I assume that will be easier to read.
Break ptmx_open() into a ptmx_create() which is used both by ptmx_open itself, and fs/devpts/inode.c:open_create_pty(). The latter function is used only during sys_restart() when a unix98 pty must be restored.
Either with or without this patch, the pty test at
git://git.sr71.net/~hallyn/cr_tests.git
under the pty/ directory. The question then is which is a more likely approach to be acceptable upstream. The task->required_id approach is imo 'hacky', but it actually does a much better job of re-using existing helpers, and therefore is more maintainable. So part of me DOES prefer the original required_id approach.
This is purely RFC, so comments very much appreciated.
--
checkpoint/sys.c | 8 --- drivers/char/pty.c | 16 +++--- drivers/char/tty_io.c | 112 +++++++++++++++++++++++++++++++++++---------- fs/devpts/inode.c | 55 ++++++++++++++++++++-- include/linux/checkpoint.h | 2 include/linux/devpts_fs.h | 2 include/linux/sched.h | 1 7 files changed, 151 insertions(+), 45 deletions(-)
checkpoint/sys.c +0 -8
@@ -339,14 +339,6 @@ SYSCALL_DEFINE3(restart, pid_t, pid, int, fd, unsigned long, flags)return ret; } -static int __init checkpoint_init (void ) -{ - init_task.required_id =CKPT_REQUIRED_NONE ; -return 0 ; -} -__initcall (checkpoint_init); - -/* 'ckpt_debug_level' controls the verbosity level of c/r code */ #ifdef CONFIG_CHECKPOINT_DEBUG
drivers/char/pty.c +8 -8
@@ -28,6 +28,8 @@#include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/devpts_fs.h> +#include <linux/file.h> +#include <linux/mount.h> #include <asm/system.h>
@@ -629,20 +631,13 @@ static const struct tty_operations pty_unix98_ops = {* allocated_ptys_lock handles the list of free pty numbers */ -static int __ptmx_open (struct inode *inode,struct file *filp) +int ptmx_create (struct inode *inode,struct file *filp,int index) {struct tty_struct *tty;int retval; -int index =-1 ;nonseekable_open (inode, filp); -#ifdef CONFIG_CHECKPOINT -/* when restarting, request specific index */ -if (current->flags &PF_RESTARTING ) - index = (int ) current->required_id; -#endif -/* find a device that is not in use. */ index =devpts_new_index (inode, index);if (index <0 )return index;
@@ -675,6 +670,11 @@ out:return retval; } +static int __ptmx_open (struct inode *inode,struct file *filp) +{ +return ptmx_create (inode, filp,UNSPECIFIED_PTY_INDEX ); +} +static int ptmx_open (struct inode *inode,struct file *filp) {int ret;
drivers/char/tty_io.c +89 -23
136 lines not on this page · raw message
fs/devpts/inode.c +52 -3
91 lines not on this page · raw message
include/linux/checkpoint.h +0 -2
@@ -46,8 +46,6 @@#define CHECKPOINT_USER_FLAGS CHECKPOINT_SUBTREE#define RESTART_USER_FLAGS (RESTART_TASKSELF | RESTART_FROZEN) -#define CKPT_REQUIRED_NONE ((unsigned long) -1L) -extern void exit_checkpoint (struct task_struct *tsk);extern int ckpt_kwrite (struct ckpt_ctx *ctx,void *buf,int count);
include/linux/devpts_fs.h +2 -0
@@ -15,10 +15,12 @@#include <linux/errno.h> +#define UNSPECIFIED_PTY_INDEX -1#ifdef CONFIG_UNIX98_PTYS int devpts_new_index (struct inode *ptmx_inode,int req_idx);void devpts_kill_index (struct inode *ptmx_inode,int idx); +int open_create_pty (struct file *fdir,int idx,struct file *file);/* mknod in devpts */ int devpts_pty_new (struct inode *ptmx_inode,struct tty_struct *tty);/* get tty structure */
include/linux/sched.h +0 -1
@@ -1481,7 +1481,6 @@ struct task_struct {#endif /* CONFIG_TRACING */ #ifdef CONFIG_CHECKPOINT struct ckpt_ctx *checkpoint_ctx; -unsigned long required_id;#endif };
A message that is not in the archive (sent off-list or not yet received): 20090902233111.GA27125-r/[email protected]
I assume this will be the preferred approach. See comments below.
Quoted 112 lines · Serge E. Hallyn
Ok so here is the patch to do unix98 pty restore by making invasive changes to
ptmx_open()instead of usingtask_struct->required_idto pass an index. Patch generated against the cr tree with the original pty patches, as I assume that will be easier to read.Break
ptmx_open()into aptmx_create()which is used both byptmx_openitself, and fs/devpts/inode.c:open_create_pty(). The latter function is used only duringsys_restart()when a unix98 pty must be restored.Either with or without this patch, the pty test at
git://git.sr71.net/~hallyn/cr_tests.gitunder the pty/ directory. The question then is which is a more likely approach to be acceptable upstream. The
task->required_idapproach is imo 'hacky', but it actually does a much better job of re-using existing helpers, and therefore is more maintainable. So part of me DOES prefer the originalrequired_idapproach.This is purely RFC, so comments very much appreciated.
Signed-off-by Serge Hallyn <serue-r/[email protected]>--
checkpoint/sys.c | 8 --- drivers/char/pty.c | 16 +++--- drivers/char/tty_io.c | 112 +++++++++++++++++++++++++++++++++++---------- fs/devpts/inode.c | 55 ++++++++++++++++++++-- include/linux/checkpoint.h | 2 include/linux/devpts_fs.h | 2 include/linux/sched.h | 1 7 files changed, 151 insertions(+), 45 deletions(-)checkpoint/sys.c +0
-8@@ -339,14 +339,6 @@ SYSCALL_DEFINE3(restart, pid_t, pid, int, fd, unsigned long, flags)return ret; } -static int __init checkpoint_init (void ) -{ - init_task.required_id =CKPT_REQUIRED_NONE ; -return 0 ; -} -__initcall (checkpoint_init); - -/* 'ckpt_debug_level' controls the verbosity level of c/r code */ #ifdef CONFIG_CHECKPOINT_DEBUG drivers/char/pty.c +8
-8@@ -28,6 +28,8 @@#include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/devpts_fs.h> +#include <linux/file.h> +#include <linux/mount.h> #include <asm/system.h> @@ -629,20 +631,13 @@ static const struct tty_operations pty_unix98_ops = {* allocated_ptys_lock handles the list of free pty numbers */ -static int __ptmx_open (struct inode *inode,struct file *filp) +int ptmx_create (struct inode *inode,struct file *filp,int index) {struct tty_struct *tty;int retval; -int index =-1 ;nonseekable_open (inode, filp); -#ifdef CONFIG_CHECKPOINT -/* when restarting, request specific index */ -if (current->flags &PF_RESTARTING ) - index = (int ) current->required_id; -#endif -/* find a device that is not in use. */ index =devpts_new_index (inode, index);if (index <0 )return index;@@ -675,6 +670,11 @@ out:return retval; } +static int __ptmx_open (struct inode *inode,struct file *filp) +{ +return ptmx_create (inode, filp,UNSPECIFIED_PTY_INDEX ); +} +static int ptmx_open (struct inode *inode,struct file *filp) {int ret;drivers/char/tty_io.c +1
-0@@ -79,6 +79,7 @@#include <linux/devpts_fs.h> #include <linux/file.h> #include <linux/fdtable.h> +#include <linux/namei.h> #include <linux/console.h> #include <linux/timer.h> #include <linux/ctype.h>
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 2988
Quoted 7 lines
drivers/char/tty_io.c +3
-1@@ -2982,13 +2983,97 @@ static int restore_tty_ldisc(struct ckpt_ctx *ctx,return 0 ; } -#define CKPT_PTMX_PATH "/dev/ptmx" +#define PTMXNAME "ptmx" +struct tty_struct *pty_open_by_master_in_ns (struct ckpt_ctx *ctx, +struct ckpt_hdr_tty *h,struct file *fdir)
I assume you split this to two functions so that in the future you could feed a different "base" directory (for a different devpts mount). To simplify this patch, perhaps we can defer it until we do that ?
Nit: s/pty_open_by_master_in_ns/pty_open_by_index/ ?
Instead of passing ckpt_hdr_tty, maybe instead make it return the file pointer, and pass the desired index instead:
struct file *pty_open_by_index(char *path, int index)
By letting the caller decide on the path, and what to do with the resulting file pointer, I hope the c/r code will be more separate and therefore easier to follow ?
Quoted 12 lines
drivers/char/tty_io.c +12
-0+{ +struct tty_struct *tty; +struct file *file; +int ret; +struct qstr ptmxname; +struct dentry *ptmxdentry; +struct nameidata nd; + + ret =file_permission (fdir,MAY_EXEC ); +if (ret) +return ERR_PTR (ret); + nd.path = fdir->f_path;
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3010
Quoted 10 lines
drivers/char/tty_io.c +10
-0+path_get (&nd.path); + ptmxname.name =PTMXNAME ; + ptmxname.len =strlen (ptmxname.name); +mutex_lock (&fdir->f_dentry->d_inode->i_mutex); + ptmxdentry =d_hash_and_lookup (fdir->f_dentry, &ptmxname); +mutex_unlock (&fdir->f_dentry->d_inode->i_mutex); +if (!ptmxdentry) { +path_put (&nd.path); +return ERR_PTR (-ENOENT ); + }
Need to verify that the resulting dentry points to a ptmx device. (With current code, user can arrange for something else in /dev/pts)
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3012
Quoted 2 lines
drivers/char/tty_io.c +2
-0+ +/* should get mode from the file handle... */
Should be ok, since restore_file_common() should restore the mode...
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3025
Quoted 13 lines
drivers/char/tty_io.c +13
-0+ file =alloc_file (nd.path.mnt, ptmxdentry,FMODE_READ |FMODE_WRITE , &tty_fops); +path_put (&nd.path); +if (!file) { +dput (ptmxdentry); +return ERR_PTR (-EINVAL ); + } + + ret =security_dentry_open (file,current_cred ()); +if (ret) { +fput (file); +return ERR_PTR (ret); + } +/* check write access perms to file */
I bet the above can be done by reusing lookup and __dentry_open() functions from fs/open.c and fs/namei.c ...
Quoted 10 lines
drivers/char/tty_io.c +10
-0+ +lock_kernel ();/* tty_open does it... */ + ret =open_create_pty (fdir, h->index, file); +unlock_kernel (); +if (ret) { +fput (file); +return ERR_PTR (ret); + } +ckpt_debug ("master file %p (obj %d)\n" , file, h->file_objref); +
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3045
Quoted 10 lines
drivers/char/tty_io.c +10
-0+/* +* Add file to objhash to ensure proper cleanup later +* (it isn't referenced elsewhere). Use h->file_objref +* which was explicitly during checkpoint for this. +*/ + ret =ckpt_obj_insert (ctx, file, h->file_objref,CKPT_OBJ_FILE ); +if (ret <0 ) { +fput (file); +return ERR_PTR (ret); + }
Can do this in caller. So
Quoted 10 lines
drivers/char/tty_io.c +10
-0+ + tty = file->private_data; +fput (file);/* objhash took a ref */ + +return tty; +} + +struct tty_struct *pty_open_by_master (struct ckpt_ctx *ctx, +struct ckpt_hdr_tty *h) +{
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3065
Quoted 10 lines
drivers/char/tty_io.c +10
-0+struct file *fdir; +struct tty_struct *tty; +/* +* we need to pick a way to specify which devpts +* mountpoint to use. For now, we'll just use +* whatever /dev/ptmx points to +*/ + fdir =filp_open ("/dev/pts" ,O_RDONLY ,0 ); +if (IS_ERR (fdir)) +return ERR_PTR (PTR_ERR (fdir));
Does this mean that to restore a subtree on a system without devpts-ns it won't work ? (no /dev/pts/ptmx)
[...]
Oren.
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 8 lines · Oren Laadan
Quoted 8 lines
drivers/char/tty_io.c +8
-0+ file =alloc_file (nd.path.mnt, ptmxdentry,FMODE_READ |FMODE_WRITE , &tty_fops); +path_put (&nd.path); +if (!file) { +dput (ptmxdentry); +return ERR_PTR (-EINVAL ); + } + + ret =security_dentry_open (file,current_cred ());
Quoted 8 lines
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3025
Quoted 5 lines
drivers/char/tty_io.c +5
-0+if (ret) { +fput (file); +return ERR_PTR (ret); + } +/* check write access perms to file */ I bet the above can be done by reusing lookup and
__dentry_open()functions fromfs/open.candfs/namei.c...
With lookup_create()? Hmm, yeah - I think there are reasons I can't do that, but I'll just try and see where it gets us.
Quoted 38 lines
Quoted 10 lines
drivers/char/tty_io.c +10
-0+ +lock_kernel ();/* tty_open does it... */ + ret =open_create_pty (fdir, h->index, file); +unlock_kernel (); +if (ret) { +fput (file); +return ERR_PTR (ret); + } +ckpt_debug ("master file %p (obj %d)\n" , file, h->file_objref); +drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3045
Quoted 10 lines
drivers/char/tty_io.c +10
-0+/* +* Add file to objhash to ensure proper cleanup later +* (it isn't referenced elsewhere). Use h->file_objref +* which was explicitly during checkpoint for this. +*/ + ret =ckpt_obj_insert (ctx, file, h->file_objref,CKPT_OBJ_FILE ); +if (ret <0 ) { +fput (file); +return ERR_PTR (ret); + }Can do this in caller. So
Quoted 15 lines
drivers/char/tty_io.c +15
-0+ + tty = file->private_data; +fput (file);/* objhash took a ref */ + +return tty; +} + +struct tty_struct *pty_open_by_master (struct ckpt_ctx *ctx, +struct ckpt_hdr_tty *h) +{ +struct file *fdir; +struct tty_struct *tty; +/* +* we need to pick a way to specify which devpts +* mountpoint to use. For now, we'll just use
Quoted 8 lines
drivers/char/tty_io.c static int restore_tty_ldisc(struct ckpt_ctx *ctx, · line 3065
Quoted 5 lines
drivers/char/tty_io.c +5
-0+* whatever /dev/ptmx points to +*/ + fdir =filp_open ("/dev/pts" ,O_RDONLY ,0 ); +if (IS_ERR (fdir)) +return ERR_PTR (PTR_ERR (fdir));Does this mean that to restore a subtree on a system without devpts-ns it won't work ? (no
/dev/pts/ptmx)
Yes. I figured once we settle on devpts ns handling, we can trivially special-case the DEVPTS_MULTIPLE_INSTANCES=n case to use /dev/ptmx.
Since we're punting on devpts namespaces anyway I guess the pty_open_by_master() can just open /dev/ptmx directly for now.
Or, maybe we're better off handling the namespaces. Because without doing that, we are hard-coding pathnames in the kernel left and right, and that's Bad(tm).
thanks,
-serge
Quoted 8 lines · Oren Laadan
Quoted 8 lines
(quoted diff) +8
-0+ file = alloc_file(nd.path.mnt, ptmxdentry, FMODE_READ|FMODE_WRITE, &tty_fops); + path_put(&nd.path); + if (!file) { + dput(ptmxdentry); + return ERR_PTR(-EINVAL); + } + + ret = security_dentry_open(file, current_cred());
Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ if (ret) { + fput(file); + return ERR_PTR(ret); + } + /* check write access perms to file */I bet the above can be done by reusing lookup and
__dentry_open()functions fromfs/open.candfs/namei.c...
All right, so I tried the below patch (lots of debugging left in) but the master end doesn't seem to end up right. After restart, pty_write() continues to succeed on the re-opened /dev/pts/N, but no reads happen on the master.
What I'm doing is passing a new flag LOOKUP_CALLER_OPEN to filp_open(), to tell it not to actually call the fops->open() routine (ptmx_open). Then I call ptmx_create() (the renamed ptmx_open) by hand. Seems like it certainly ought to be working... But I'm getting nowhere figuring out what's happening to the major file, so putting it aside for a bit.
Following is the patch against the tree with Oren's original pty patches.
-serge
--- checkpoint/sys.c | 8 ----- drivers/char/pty.c | 43 ++++++++++++++++++++-------- drivers/char/tty_io.c | 69 ++++++++++++++++++++++++++++++++++++--------- fs/devpts/inode.c | 60 +++++++++++++++++++++++++++++++++++++-- fs/open.c | 19 +++++++++++- include/linux/checkpoint.h | 2 - include/linux/devpts_fs.h | 3 + include/linux/namei.h | 1 include/linux/sched.h | 1 9 files changed, 167 insertions(+), 39 deletions(-)
checkpoint/sys.c +0 -8
@@ -339,14 +339,6 @@ SYSCALL_DEFINE3(restart, pid_t, pid, int, fd, unsigned long, flags)return ret; } -static int __init checkpoint_init (void ) -{ - init_task.required_id =CKPT_REQUIRED_NONE ; -return 0 ; -} -__initcall (checkpoint_init); - -/* 'ckpt_debug_level' controls the verbosity level of c/r code */ #ifdef CONFIG_CHECKPOINT_DEBUG
drivers/char/pty.c +32 -11
102 lines not on this page · raw message
drivers/char/tty_io.c +56 -13
140 lines not on this page · raw message
fs/devpts/inode.c +57 -3
96 lines not on this page · raw message
fs/open.c +18 -1
@@ -924,6 +924,17 @@ out_err: }EXPORT_SYMBOL_GPL (lookup_instantiate_filp); +/* +* Passed to nameidata_to_filp to keep it from running +* the file-specified open routine. +* We pass this along if LOOKUP_CALLER_OPEN was passed to +* filp_open, meaning the caller will open the file by hand +*/ +static int __dummy_open (struct inode *inode,struct file *filp) +{ +return 0 ; +} +/** * nameidata_to_filp - convert a nameidata to an open filp. * @nd: pointer to nameidata
@@ -935,13 +946,19 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags) {const struct cred *cred =current_cred ();struct file *filp; +int (*open)(struct inode *,struct file *) =NULL ; + +if (flags &LOOKUP_CALLER_OPEN ) { +printk (KERN_NOTICE "%s: I was passed LOOKUP_CALLER_OPEN\n" , __func__); + open = __dummy_open; + }/* Pick up the filp from the open intent */ filp = nd->intent.open.file;/* Has the filesystem initialised the file for us? */ if (filp->f_path.dentry ==NULL ) filp =__dentry_open (nd->path.dentry, nd->path.mnt, flags, filp, -NULL , cred); + open, cred);else path_put (&nd->path);return filp;
include/linux/checkpoint.h +0 -2
@@ -46,8 +46,6 @@#define CHECKPOINT_USER_FLAGS CHECKPOINT_SUBTREE#define RESTART_USER_FLAGS (RESTART_TASKSELF | RESTART_FROZEN) -#define CKPT_REQUIRED_NONE ((unsigned long) -1L) -extern void exit_checkpoint (struct task_struct *tsk);extern int ckpt_kwrite (struct ckpt_ctx *ctx,void *buf,int count);
include/linux/devpts_fs.h +3 -0
@@ -15,10 +15,13 @@#include <linux/errno.h> +#define UNSPECIFIED_PTY_INDEX -1#ifdef CONFIG_UNIX98_PTYS int devpts_new_index (struct inode *ptmx_inode,int req_idx);void devpts_kill_index (struct inode *ptmx_inode,int idx); +int open_create_pty (struct file *ptmxfile,int idx); +int file_is_ptmx (struct file *file);/* mknod in devpts */ int devpts_pty_new (struct inode *ptmx_inode,struct tty_struct *tty);/* get tty structure */
include/linux/namei.h +1 -0
@@ -56,6 +56,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};#define LOOKUP_CREATE 0x0200#define LOOKUP_EXCL 0x0400#define LOOKUP_RENAME_TARGET 0x0800 +#define LOOKUP_CALLER_OPEN 0x1000extern int user_path_at (int ,const char __user *,unsigned ,struct path *);
include/linux/sched.h +0 -1
@@ -1481,7 +1481,6 @@ struct task_struct {#endif /* CONFIG_TRACING */ #ifdef CONFIG_CHECKPOINT struct ckpt_ctx *checkpoint_ctx; -unsigned long required_id;#endif };