Skip to contentSkip to conversation

cr: pty: don't use required_id, change ptmx_open()

RFC In review Serge E. Hallyn 2 participants 4 messages Sep 2, 2009 – Sep 10, 2009 2 messages not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

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
index 3db18f7..b1fdbd7 100644
@@ -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
index afdab5e..79e86e4 100644
@@ -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
index 5e90ef9..0cbe8c4 100644
@@ -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
index 14948ee..a15d23c 100644
@@ -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
index f6f1350..0e67de7 100644
@@ -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]

#2 lore raw

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 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
index 3db18f7..b1fdbd7 100644
@@ -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
index afdab5e..79e86e4 100644
@@ -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
index b8f8d79..d27ec36 100644
@@ -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]

#4 lore raw
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 from fs/open.c and fs/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

#5 lore raw
In reply to a message not in the archive
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 from fs/open.c and fs/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
index 3db18f7..b1fdbd7 100644
@@ -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
index dd98e80..c65229a 100644
@@ -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
index 5e90ef9..0cbe8c4 100644
@@ -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
index 14948ee..ddb418d 100644
@@ -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
index d870ae2..fae5159 100644
@@ -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	0x1000
 
 extern int user_path_at(int, const char __user *, unsigned, struct path *);
 
include/linux/sched.h +0 -1
index f6f1350..0e67de7 100644
@@ -1481,7 +1481,6 @@ struct task_struct {
 #endif /* CONFIG_TRACING */
 #ifdef CONFIG_CHECKPOINT
 	struct ckpt_ctx *checkpoint_ctx;
-	unsigned long required_id;
 #endif
 };