[RFC] tty: pty -- Add ability to setup next pty number for ptmx open

From: Cyrill Gorcunov
Date: Fri Mar 17 2017 - 06:57:02 EST


When we restore PTY terminals we do iterate over open() calls to reach
the index used at checkpoint time (say application opened a number of
terminals, then closed all other except last one) which of course very
inconvenient.

The problem get more notable in scope of controlling terminal represented
by /dev/tty device: current CRIU restore scheme is lazy, we don't open
files immedeately after the forks but rather defer until the whole
process tree is created, then every process opens own files in async
way and in result /dev/tty is not propagated into children because
it is not yet opened.

Both problems can be solved via criu itself but will require very
much efforts. So I would be very happy if we can open desired PTY
peer with index needed in one call instead. Which would allow us:

- open a sole control terminal right after the fork and
propagate it automatically into children

- drop out our open(/dev/ptmx) sequence to reach index needed,
improving performance and shrinking code

The API in the patch is trivial -- one have to open(/dev/ptmx)
and automatically get index 0, then simply do
ioctl(fd, TIOCSPTN, index) and next open(/dev/ptmx)
will return pair with point number requested.

CC: Andrey Vagin <avagin@xxxxxxxxxx>
CC: Pavel Emelyanov <xemul@xxxxxxxxxxxxx>
CC: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx>
CC: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
CC: Jiri Slaby <jslaby@xxxxxxxx>
CC: "H. Peter Anvin" <hpa@xxxxxxxxx>
CC: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Cyrill Gorcunov <gorcunov@xxxxxxxxxx>
---
drivers/tty/pty.c | 14 ++++++++++++++
fs/devpts/inode.c | 22 ++++++++++++++++++++--
include/linux/devpts_fs.h | 1 +
include/uapi/asm-generic/ioctls.h | 1 +
4 files changed, 36 insertions(+), 2 deletions(-)

Index: linux-ml.git/drivers/tty/pty.c
===================================================================
--- linux-ml.git.orig/drivers/tty/pty.c
+++ linux-ml.git/drivers/tty/pty.c
@@ -593,6 +593,18 @@ static inline void legacy_pty_init(void)

static struct cdev ptmx_cdev;

+static int pty_set_next_ptn(struct tty_struct *tty, int next_ptn)
+{
+ if (tty->driver->subtype == PTY_TYPE_MASTER) {
+ struct pts_fs_info *fsi;
+
+ fsi = tty->driver_data;
+ return devpts_set_next_index(fsi, next_ptn);
+ }
+
+ return -ENOIOCTLCMD;
+}
+
static int pty_unix98_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -607,6 +619,8 @@ static int pty_unix98_ioctl(struct tty_s
return pty_get_pktmode(tty, (int __user *)arg);
case TIOCGPTN: /* Get PT Number */
return put_user(tty->index, (unsigned int __user *)arg);
+ case TIOCSPTN:
+ return pty_set_next_ptn(tty, (unsigned int)arg);
case TIOCSIG: /* Send signal to other side of pty */
return pty_signal(tty, (int) arg);
}
Index: linux-ml.git/fs/devpts/inode.c
===================================================================
--- linux-ml.git.orig/fs/devpts/inode.c
+++ linux-ml.git/fs/devpts/inode.c
@@ -126,6 +126,7 @@ struct pts_fs_info {
struct pts_mount_opts mount_opts;
struct super_block *sb;
struct dentry *ptmx_dentry;
+ int pty_next_idx;
};

static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
@@ -486,7 +487,8 @@ retry:
return -ENOSPC;
}

- ida_ret = ida_get_new(&fsi->allocated_ptys, &index);
+ ida_ret = ida_get_new_above(&fsi->allocated_ptys,
+ fsi->pty_next_idx, &index);
if (ida_ret < 0) {
mutex_unlock(&allocated_ptys_lock);
if (ida_ret == -EAGAIN)
@@ -494,7 +496,9 @@ retry:
return -EIO;
}

- if (index >= fsi->mount_opts.max) {
+ if (index >= fsi->mount_opts.max ||
+ (fsi->pty_next_idx &&
+ fsi->pty_next_idx != index)) {
ida_remove(&fsi->allocated_ptys, index);
mutex_unlock(&allocated_ptys_lock);
return -ENOSPC;
@@ -512,6 +516,20 @@ void devpts_kill_index(struct pts_fs_inf
mutex_unlock(&allocated_ptys_lock);
}

+int devpts_set_next_index(struct pts_fs_info *fsi, int next_idx)
+{
+ int ret = 0;
+
+ mutex_lock(&allocated_ptys_lock);
+ if (fsi->mount_opts.max > next_idx && next_idx >= 0)
+ fsi->pty_next_idx = next_idx;
+ else
+ ret = -ENOSPC;
+ mutex_unlock(&allocated_ptys_lock);
+
+ return ret;
+}
+
/**
* devpts_pty_new -- create a new inode in /dev/pts/
* @ptmx_inode: inode of the master
Index: linux-ml.git/include/linux/devpts_fs.h
===================================================================
--- linux-ml.git.orig/include/linux/devpts_fs.h
+++ linux-ml.git/include/linux/devpts_fs.h
@@ -24,6 +24,7 @@ void devpts_release(struct pts_fs_info *

int devpts_new_index(struct pts_fs_info *);
void devpts_kill_index(struct pts_fs_info *, int);
+int devpts_set_next_index(struct pts_fs_info *, int);

/* mknod in devpts */
struct dentry *devpts_pty_new(struct pts_fs_info *, int, void *);
Index: linux-ml.git/include/uapi/asm-generic/ioctls.h
===================================================================
--- linux-ml.git.orig/include/uapi/asm-generic/ioctls.h
+++ linux-ml.git/include/uapi/asm-generic/ioctls.h
@@ -68,6 +68,7 @@
#define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */
+#define TIOCSPTN _IOW('T', 0x33, unsigned int) /* Set next Pty Number (of pty-mux device) */
#define TCGETX 0x5432 /* SYS5 TCGETX compatibility */
#define TCSETX 0x5433
#define TCSETXF 0x5434