[Devel] [PATCH RHEL8 COMMIT] ve/tty: vt -- Implement per VE support for console and terminals

Konstantin Khorenko khorenko at virtuozzo.com
Mon Jul 26 20:15:24 MSK 2021


The commit is pushed to "branch-rh8-4.18.0-305.3.1.vz8.7.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-305.3.1.el8
------>
commit 2ff1e5576913fc32927869e21e63bb37a1b8ddae
Author: Cyrill Gorcunov <gorcunov at virtuozzo.com>
Date:   Mon Jul 26 20:15:24 2021 +0300

    ve/tty: vt -- Implement per VE support for console and terminals
    
    Previously in commit 8674c044330fad1458bd59b02f9037fb97e8b7af stubs for
    virtual terminals have been added, they support writes from kernel side
    which simply drops into the void.
    
    In the patch the code has been moved from kernel/ve/console.c
    to drivers/tty/pty.c to reuse a couple of pty helpers.
    
    Now we support up to MAX_NR_VTTY_CONSOLES virtual consoles inside container.
    For /dev/console we reserve the first virtual terminal.
    
    Some details on the driver itself:
    
     - The drivers carries per-VE tty instances in @vtty_idr map, once
       VE tries to open a terminal we allocate tty map internally and
       keep it intact until VE destructed, this allow us to not bind
       into device namespaces (ie not rely on tty_class);
    
     - Unlike buffered IO to unix98 driver once internal port buffer
       get full we don't block write operations if there is no reader
       assigned yet but zap them. This is done intentionally to behave
       closely to native consoles;
    
     - The kernel choose which VE request terminal using get_exec_env
       helper, but for opening master peer from the nodes ve0 it uses
       vtty_set_context/vtty_get_context/vtty_drop_context to notify
       tty layer which @vtty_idr to use instead of get_exec_env.
    
    https://jira.sw.ru/browse/PSBM-34533
    https://jira.sw.ru/browse/PSBM-34532
    https://jira.sw.ru/browse/PSBM-34107
    https://jira.sw.ru/browse/PSBM-32686
    https://jira.sw.ru/browse/PSBM-32685
    
    v2:
     - Rename terminals from vtz to vtty
     - Merge code into /drivers/tty/pty.c to reuse some of
       pty functionality
     - Get rid of two array of indices, use one for master
       peers and fetch slaves via @link
     - Drop TTY_VT_OPEN and wait() on it
     - Add vtty_open_slave helper
    
    v3:
     - Reverse the scheme, the peers opened from inside of
       container are the slave peers as it were in pcs6
     - Add vtty_set_context/vtty_drop_context/vtty_get_context
       to open needed tty from ve0 context
     - In vtty_open_master reuse existing vtty_lookup, vtty_open
       helpers
     - In ve_vtty_fini zap active tty tracking, such ttys are
       sitting here because the node has been opening the console
       and didn't release file descriptors yet with tty associated.
       The kernel will clean them up once they are closed but the
       tacking map pointer should be zapped to escape nil dereference
    
    v4:
     - Use lockdep_assert_held in vtty @map operations to make sure
       we're under @tty_mutex
     - vtty_install now requests for port memory earlier for vtty_install_peer
       simplification
     - Drop tty_vhangup call from vtty_close, as been found it doesn't
       bring any benefit
     - Drop TTY_BUFFER_PAGE and fix typo in vtty_write_room
     - Rework tty counting to be the same as in pcs6: drivers
       became TTY_DRIVER_TYPE_PTY and @count adjusted accordingly
    
    v5:
     - Treat zero as unused flag in vtty_get_context
     - vtty_printk helpers are dropped off
     - Don't test for exit state in lookup procedure: the kernel
       will do that on its own when slave is opened from inside
       of a container and for ioctl call we do such test explicitly
     - When pair is to open from the node and the existing peer is
       exiting we're allocating new pair early removin old one from
       per-VE ttys map, this is done to speedup open from the node
     - vtty_match is no longer exported into the rest of the tty code
     - When peer is to be closed we use own per-VE spinlock to read
       and modify own and peer counters, this is because the general
       tty->close routine is called without tty-mutex held and only
       one peer is locked thus such modifications are unsafe if do
       them locklessly. In current vanilla kernel there is no need
       for such lock if Unix ptys are used because master peers are
       always opened first and always get closed in constrast to the
       our driver where any peer end may be opened sole
    
    v6:
     - Reworked tty counting: no need for extra reference but make
       it close to how native Unix98 ptys are working: once master
       is opened it takes new TTY_PINNED flags and when it getting
       closed with active slave peer we defer tty destruction until
       both ends are spare.
    
    v7:
     - Move MAX_NR_VTTY_CONSOLES from header into pty.c
     - Drop vtty_zap_tty_map
     - Assign @driver_data in vtty_map_set
     - Rename vtty_map_del to vtty_map_clear
     - Merge map cleaning into vtty_map_free
     - Rename @current_veid to @vtty_context_veid
     - Rename TTY_PINNED to TTY_PINNED_BY_OTHER
     - Assing TTY_PINNED_BY_OTHER early in pair creation
     - Wake both ends of a peer in vtty_close
    
    vdavydov:
     - Remove kernel/ve/console.c
     - Drop CONFIG_VTTYS
     - Export vtty_open_master
    
    Signed-off-by: Cyrill Gorcunov <gorcunov at virtuozzo.com>
    
    Reviewed-by: Vladimir Davydov <vdavydov at parallels.com>
    CC: Konstantin Khorenko <khorenko at virtuozzo.com>
    
    +++
    ve/tty: WARN_ON commented out in vtty_open_master()
    
    i've just commented it out for now because of compilation issues
    and the fact we are going to rework tty code anyway.
    
    Reason of the problem: RedHat applied
    36697529b5bbe36911e39a6309e7a7c9250d280a
    ("tty: Replace ldisc locking with ldisc_sem").
    
    (related to rebase to 3.10.0-327.3.1.el7)
    
    To be merged into 9ad5a211409ee78dbd7c92696c87ceca6e435622
    ("ve/tty: vt -- Implement per VE support for console and terminals").
    
    Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
    
    tty/pty: lockdep warning fixed in vtty_open_master()
    
    I expect it should fix PSBM-80049
    
    [   96.177828] [ INFO: possible recursive locking detected ]
    [   96.179329] 3.10.0-862.ovz.rh7-3.10.0-862.el7 #34 Tainted: G        W      ------------
    [   96.181556] ---------------------------------------------
    [   96.182956] prl_vzvncserver/2795 is trying to acquire lock:
    [   96.184406]  (&tty->legacy_mutex){+.+.+.}, at: [<ffffffff95fa5717>] tty_lock+0x57/0xb0
    [   96.186611]
    [   96.186611] but task is already holding lock:
    [   96.188133]  (&tty->legacy_mutex){+.+.+.}, at: [<ffffffff95fa5717>] tty_lock+0x57/0xb0
    [   96.190424]
    [   96.190424] other info that might help us debug this:
    [   96.192114]  Possible unsafe locking scenario:
    [   96.192114]
    [   96.193656]        CPU0
    [   96.194307]        ----
    [   96.194968]   lock(&tty->legacy_mutex);
    [   96.196074]   lock(&tty->legacy_mutex);
    [   96.197181]
    [   96.197181]  *** DEADLOCK ***
    [   96.197181]
    [   96.198737]  May be due to missing lock nesting notation
    [   96.198737]
    [   96.200518] 1 lock held by prl_vzvncserver/2795:
    [   96.201743]  #0:  (&tty->legacy_mutex){+.+.+.}, at: [<ffffffff95fa5717>] tty_lock+0x57/0xb0
    
    vtty slave should be marked properly to hide lockdep warning
    
    Signed-off-by: Vasily Averin <vvs at virtuozzo.com>
    
    Acked-by: Cyrill Gorcunov <gorcunov at openvz.org>
    
    +++
    tty/pty: drop dead vtty code
    
    code below is dead since rebase to 3.10.0-514.el7, i.e. ~year ago,
    but we still have no related complains.
    
    Let's drop it.
    
    Signed-off-by: Vasily Averin <vvs at virtuozzo.com>
    
    Acked-by: Cyrill Gorcunov <gorcunov at openvz.org>
    
    +++
    vtty: fixed error path in vtty_map_alloc
    
    found by smatch:
    drivers/tty/pty.c:935 vtty_map_alloc() warn:
     unsigned 'veid' is never less than zero.
    
    Signed-off-by: Vasily Averin <vvs at virtuozzo.com>
    
    +++
    vtty: possible ERR_PTR dereferencing in vtty_open_master
    
    found by swatch:
    drivers/tty/pty.c:1306 vtty_open_master() error:
     'tty' dereferencing possible ERR_PTR()
    
    Signed-off-by: Vasily Averin <vvs at virtuozzo.com>
    Acked-by: Konstantin Khorenko <khorenko at virtuozzo.com>
    
    v2: do set proper "ret" values on error paths
    (cherry-picked form 368f1f2e66d928394c157b505d642439c4450782)
    Signed-off-by: Valeriy Vdovin <valeriy.vdovin at virtuozzo.com>
    
    https://jira.sw.ru/browse/PSBM-132299
---
 drivers/tty/pty.c    | 509 +++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_io.c |  19 ++
 include/linux/tty.h  |   3 +
 include/linux/ve.h   |   6 +
 kernel/ve/vecalls.c  |   3 +
 5 files changed, 540 insertions(+)

diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index b8175c0a0538..a7f9bb404d70 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -601,6 +601,514 @@ static void __init legacy_pty_init(void)
 static inline void legacy_pty_init(void) { }
 #endif
 
+#if defined(CONFIG_VE)
+
+/*
+ * VTTY architecture overview.
+ *
+ * With VTTY we make /dev/console and /dev/tty[X] virtualized
+ * per container (note the real names may vary because the
+ * kernel itself uses major:minor numbers to distinguish
+ * devices and doesn't care how they are named inside /dev.
+ * /dev/console stands for TTYAUX_MAJOR:1 while /dev/tty[X]
+ * stands for TTY_MAJOR:[0:12]. That said from inside of
+ * VTTY /dev/console is the same as /dev/tty0.
+ *
+ * For every container here is a tty map represented by
+ * vtty_map_t. It carries @veid of VE and associated slave
+ * tty peers.
+ *
+ * map
+ *  veid -> CTID
+ *    vttys -> [ 0 ]
+ *               `- @slave -> link -> @master
+ *             [ 1 ]
+ *               `- @slave -> link -> @master
+ */
+
+#include <linux/ve.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+
+static struct tty_driver *vttym_driver;
+static struct tty_driver *vttys_driver;
+static DEFINE_IDR(vtty_idr);
+
+static struct file_operations vtty_fops;
+
+#define MAX_NR_VTTY_CONSOLES	(12)
+#define vtty_match_index(idx)	((idx) >= 0 && (idx) < MAX_NR_VTTY_CONSOLES)
+
+typedef struct {
+	envid_t			veid;
+	struct tty_struct	*vttys[MAX_NR_VTTY_CONSOLES];
+} vtty_map_t;
+
+static vtty_map_t *vtty_map_lookup(envid_t veid)
+{
+	lockdep_assert_held(&tty_mutex);
+	return idr_find(&vtty_idr, veid);
+}
+
+static void vtty_map_set(vtty_map_t *map, struct tty_struct *tty)
+{
+	lockdep_assert_held(&tty_mutex);
+	WARN_ON(map->vttys[tty->index]);
+
+	tty->driver_data = tty->link->driver_data = map;
+	map->vttys[tty->index] = tty;
+}
+
+static void vtty_map_clear(struct tty_struct *tty)
+{
+	vtty_map_t *map = tty->driver_data;
+
+	lockdep_assert_held(&tty_mutex);
+	if (map) {
+		struct tty_struct *p = map->vttys[tty->index];
+
+		WARN_ON(p != (tty->driver == vttys_driver ? tty : tty->link));
+		map->vttys[tty->index] = NULL;
+		tty->driver_data = tty->link->driver_data = NULL;
+	}
+}
+
+static void vtty_map_free(vtty_map_t *map)
+{
+	int i;
+
+	lockdep_assert_held(&tty_mutex);
+
+	for (i = 0; i < MAX_NR_VTTY_CONSOLES; i++) {
+		struct tty_struct *tty = map->vttys[i];
+		if (!tty)
+			continue;
+		tty->driver_data = tty->link->driver_data = NULL;
+	}
+
+	idr_remove(&vtty_idr, map->veid);
+	kfree(map);
+}
+
+static vtty_map_t *vtty_map_alloc(envid_t veid)
+{
+	vtty_map_t *map = kzalloc(sizeof(*map), GFP_KERNEL);
+
+	lockdep_assert_held(&tty_mutex);
+	if (map) {
+		int id;
+
+		map->veid = veid;
+		id = idr_alloc(&vtty_idr, map, veid, veid + 1, GFP_KERNEL);
+		if (id < 0) {
+			kfree(map);
+			return ERR_PTR(id);
+		}
+	} else
+		map = ERR_PTR(-ENOMEM);
+	return map;
+}
+
+/*
+ * vttys are never supposed to be opened from inside
+ * of VE0 except special ioctl call, so treat zero as
+ * "unused" sign.
+ */
+static envid_t vtty_context_veid;
+
+static void vtty_set_context(envid_t veid)
+{
+	lockdep_assert_held(&tty_mutex);
+	WARN_ON(!veid);
+	vtty_context_veid = veid;
+}
+
+static void vtty_drop_context(void)
+{
+	lockdep_assert_held(&tty_mutex);
+	vtty_context_veid = 0;
+}
+
+static envid_t vtty_get_context(void)
+{
+	lockdep_assert_held(&tty_mutex);
+	return vtty_context_veid ?: get_exec_env()->veid;
+}
+
+static struct tty_struct *vtty_lookup(struct tty_driver *driver,
+				      struct file *file, int idx)
+{
+	vtty_map_t *map = vtty_map_lookup(vtty_get_context());
+	struct tty_struct *tty;
+
+	if (!vtty_match_index(idx))
+		return ERR_PTR(-EIO);
+
+	/*
+	 * Nothing ever been opened yet, allocate a new
+	 * tty map together with both peers from the scratch
+	 * in install procedure.
+	 */
+	if (!map)
+		return NULL;
+
+	tty = map->vttys[idx];
+	if (tty) {
+		if (driver == vttym_driver)
+			tty = tty->link;
+		WARN_ON(!tty);
+	}
+	return tty;
+}
+
+static void vtty_standard_install(struct tty_driver *driver,
+				  struct tty_struct *tty)
+{
+	tty_init_termios(tty);
+
+	tty_driver_kref_get(driver);
+	tty_port_init(tty->port);
+	tty->port->itty = tty;
+}
+
+static struct tty_struct *vtty_install_peer(struct tty_driver *driver,
+					    struct tty_port *port, int index)
+{
+	struct tty_struct *tty;
+
+	tty = alloc_tty_struct(driver, index);
+	if (!tty)
+		return ERR_PTR(-ENOMEM);
+	tty->port = port;
+	vtty_standard_install(driver, tty);
+	return tty;
+}
+
+static int vtty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	envid_t veid = vtty_get_context();
+	struct tty_port *peer_port;
+	struct tty_struct *peer;
+	vtty_map_t *map;
+	int ret;
+
+	WARN_ON_ONCE(driver != vttys_driver);
+
+	map = vtty_map_lookup(veid);
+	if (!map) {
+		map = vtty_map_alloc(veid);
+		if (IS_ERR(map))
+			return PTR_ERR(map);
+	}
+
+	tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
+	peer_port = kzalloc(sizeof(*peer_port), GFP_KERNEL);
+	if (!tty->port || !peer_port) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	peer = vtty_install_peer(vttym_driver, peer_port, tty->index);
+	if (IS_ERR(peer)) {
+		ret = PTR_ERR(peer);
+		goto err_free;
+	}
+
+	vtty_standard_install(vttys_driver, tty);
+	tty->count++;
+
+	tty->link = peer;
+	peer->link = tty;
+
+	/*
+	 * Defer master closing if a slave peer
+	 * will be alive at this moment.
+	 */
+	set_bit(TTY_PINNED_BY_OTHER, &peer->flags);
+
+	vtty_map_set(map, tty);
+	return 0;
+
+err_free:
+	kfree(tty->port);
+	kfree(peer_port);
+	return ret;
+}
+
+static int vtty_open(struct tty_struct *tty, struct file *filp)
+{
+	set_bit(TTY_THROTTLED, &tty->flags);
+	return 0;
+}
+
+static void vtty_close(struct tty_struct *tty, struct file *filp)
+{
+	if (tty->count <= (tty->driver == vttys_driver) ? 2 : 1) {
+		wake_up_interruptible(&tty->read_wait);
+		wake_up_interruptible(&tty->write_wait);
+
+		wake_up_interruptible(&tty->link->read_wait);
+		wake_up_interruptible(&tty->link->write_wait);
+	}
+}
+
+static void vtty_shutdown(struct tty_struct *tty)
+{
+	vtty_map_clear(tty);
+}
+
+static int vtty_write(struct tty_struct *tty,
+		      const unsigned char *buf, int count)
+{
+	struct tty_struct *peer = tty->link;
+
+	if (tty->stopped)
+		return 0;
+
+	if (count > 0) {
+		count = tty_insert_flip_string(peer->port, buf, count);
+		if (count) {
+			tty_flip_buffer_push(peer->port);
+			tty_wakeup(tty);
+		} else {
+			/*
+			 * Flush the slave reader if noone
+			 * is actually hooked on. Otherwise
+			 * wait until reader fetch all data.
+			 */
+			if (peer->count <
+			    (tty->driver == vttym_driver) ? 2 : 1)
+				tty_perform_flush(peer, TCIFLUSH);
+		}
+	}
+
+	return count;
+}
+
+static int vtty_write_room(struct tty_struct *tty)
+{
+	struct tty_struct *peer = tty->link;
+
+	if (tty->stopped)
+		return 0;
+
+	if (peer->count <
+	    (tty->driver == vttym_driver) ? 2 : 1)
+		return 2048;
+
+	return tty_buffer_space_avail(peer->port);
+}
+
+static void vtty_remove(struct tty_driver *driver, struct tty_struct *tty)
+{
+}
+
+static const struct tty_operations vtty_ops = {
+	.lookup		= vtty_lookup,
+	.install	= vtty_install,
+	.open		= vtty_open,
+	.close		= vtty_close,
+	.shutdown	= vtty_shutdown,
+	.cleanup	= pty_cleanup,
+	.write		= vtty_write,
+	.write_room	= vtty_write_room,
+	.set_termios	= pty_set_termios,
+	.unthrottle	= pty_unthrottle,
+	.remove		= vtty_remove,
+};
+
+struct tty_driver *vtty_console_driver(int *index)
+{
+	*index = 0;
+	return vttys_driver;
+}
+
+struct tty_driver *vtty_driver(dev_t dev, int *index)
+{
+	if (MAJOR(dev) == TTY_MAJOR &&
+	    MINOR(dev) < MAX_NR_VTTY_CONSOLES) {
+		*index = MINOR(dev);
+		return vttys_driver;
+	}
+	return NULL;
+}
+
+static void ve_vtty_fini(void *data)
+{
+	struct ve_struct *ve = data;
+	vtty_map_t *map;
+
+	mutex_lock(&tty_mutex);
+	map = vtty_map_lookup(ve->veid);
+	if (map)
+		vtty_map_free(map);
+	mutex_unlock(&tty_mutex);
+}
+
+static struct ve_hook vtty_hook = {
+	.fini           = ve_vtty_fini,
+	.priority       = HOOK_PRIO_DEFAULT,
+	.owner          = THIS_MODULE,
+};
+
+static int __init vtty_init(void)
+{
+#define VTTY_DRIVER_ALLOC_FLAGS			\
+	(TTY_DRIVER_REAL_RAW		|	\
+	 TTY_DRIVER_RESET_TERMIOS	|	\
+	 TTY_DRIVER_DYNAMIC_DEV		|	\
+	 TTY_DRIVER_INSTALLED		|	\
+	 TTY_DRIVER_DEVPTS_MEM)
+
+	vttym_driver = tty_alloc_driver(MAX_NR_VTTY_CONSOLES,
+					VTTY_DRIVER_ALLOC_FLAGS);
+	if (IS_ERR(vttym_driver))
+		panic(pr_fmt("Can't allocate master vtty driver\n"));
+
+	vttys_driver = tty_alloc_driver(MAX_NR_VTTY_CONSOLES,
+					VTTY_DRIVER_ALLOC_FLAGS);
+	if (IS_ERR(vttys_driver))
+		panic(pr_fmt("Can't allocate slave vtty driver\n"));
+
+	vttym_driver->driver_name		= "vtty_master";
+	vttym_driver->name			= "vttym";
+	vttym_driver->name_base			= 0;
+	vttym_driver->major			= 0;
+	vttym_driver->minor_start		= 0;
+	vttym_driver->type			= TTY_DRIVER_TYPE_PTY;
+	vttym_driver->subtype			= PTY_TYPE_MASTER;
+	vttym_driver->init_termios		= tty_std_termios;
+	vttym_driver->init_termios.c_iflag	= 0;
+	vttym_driver->init_termios.c_oflag	= 0;
+
+	/* 38400 boud rate, 8 bit char size, enable receiver */
+	vttym_driver->init_termios.c_cflag	= B38400 | CS8 | CREAD;
+	vttym_driver->init_termios.c_lflag	= 0;
+	vttym_driver->init_termios.c_ispeed	= 38400;
+	vttym_driver->init_termios.c_ospeed	= 38400;
+	tty_set_operations(vttym_driver, &vtty_ops);
+
+	vttys_driver->driver_name		= "vtty_slave";
+	vttys_driver->name			= "vttys";
+	vttys_driver->name_base			= 0;
+	vttys_driver->major			= 0;
+	vttys_driver->minor_start		= 0;
+	vttys_driver->type			= TTY_DRIVER_TYPE_PTY;
+	vttys_driver->subtype			= PTY_TYPE_SLAVE;
+	vttys_driver->init_termios		= tty_std_termios;
+	vttys_driver->init_termios.c_iflag	= 0;
+	vttys_driver->init_termios.c_oflag	= 0;
+	vttys_driver->init_termios.c_cflag	= B38400 | CS8 | CREAD;
+	vttys_driver->init_termios.c_lflag	= 0;
+	vttys_driver->init_termios.c_ispeed	= 38400;
+	vttys_driver->init_termios.c_ospeed	= 38400;
+	tty_set_operations(vttys_driver, &vtty_ops);
+
+	if (tty_register_driver(vttym_driver))
+		panic(pr_fmt("Can't register master vtty driver\n"));
+
+	if (tty_register_driver(vttys_driver))
+		panic(pr_fmt("Can't register slave vtty driver\n"));
+
+	ve_hook_register(VE_SS_CHAIN, &vtty_hook);
+	tty_default_fops(&vtty_fops);
+	return 0;
+}
+
+int vtty_open_master(envid_t veid, int idx)
+{
+	struct tty_struct *tty;
+	struct file *file;
+	char devname[64];
+	int fd, ret;
+
+	if (!vtty_match_index(idx))
+		return -EIO;
+
+	fd = get_unused_fd_flags(0);
+	if (fd < 0)
+		return fd;
+
+	snprintf(devname, sizeof(devname), "v%utty%d", veid, idx);
+	file = anon_inode_getfile(devname, &vtty_fops, NULL, O_RDWR);
+	if (IS_ERR(file)) {
+		ret = PTR_ERR(file);
+		goto err_put_unused_fd;
+	}
+	nonseekable_open(NULL, file);
+
+	ret = tty_alloc_file(file);
+	if (ret)
+		goto err_fput;
+
+	/*
+	 * Opening comes from ve0 context so
+	 * setup VE's context until master fetched.
+	 * This is done under @tty_mutex so noone
+	 * else would access it while we're holding
+	 * the lock.
+	 */
+	mutex_lock(&tty_mutex);
+	vtty_set_context(veid);
+
+	tty = vtty_lookup(vttym_driver, NULL, idx);
+	if (IS_ERR(tty)) {
+		ret = PTR_ERR(tty);
+		goto err_install;
+	}
+
+	if (!tty) {
+		tty = tty_init_dev(vttys_driver, idx);
+		if (IS_ERR(tty)) {
+			ret = PTR_ERR(tty);
+			goto err_install;
+		}
+		tty->count--;
+		tty_unlock(tty);
+		tty_set_lock_subclass(tty);
+		tty = tty->link;
+	}
+
+	/* One master at a time */
+	if (tty->count >= 1) {
+		ret = -EBUSY;
+		goto err_install;
+	}
+
+	vtty_drop_context();
+
+	/*
+	 * We're the master peer so increment
+	 * slave counter as well.
+	 */
+	tty_add_file(tty, file);
+	tty->count++;
+	tty->link->count++;
+	fd_install(fd, file);
+	vtty_open(tty, file);
+
+	mutex_unlock(&tty_mutex);
+	ret = fd;
+out:
+	return ret;
+
+err_install:
+	vtty_drop_context();
+	mutex_unlock(&tty_mutex);
+	tty_free_file(file);
+err_fput:
+	file->f_op = NULL;
+	fput(file);
+err_put_unused_fd:
+	put_unused_fd(fd);
+	goto out;
+}
+EXPORT_SYMBOL(vtty_open_master);
+#else
+static void vtty_init(void) { };
+#endif /* CONFIG_VE */
+
+
 /* Unix98 devices */
 #ifdef CONFIG_UNIX98_PTYS
 static struct cdev ptmx_cdev;
@@ -953,6 +1461,7 @@ static int __init pty_init(void)
 {
 	legacy_pty_init();
 	unix98_pty_init();
+	vtty_init();
 	return 0;
 }
 device_initcall(pty_init);
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 7858f006b1ae..ac237a60200c 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -106,6 +106,7 @@
 
 #include <linux/kmod.h>
 #include <linux/nsproxy.h>
+#include <linux/ve.h>
 
 #undef TTY_DEBUG_HANGUP
 #ifdef TTY_DEBUG_HANGUP
@@ -1832,6 +1833,20 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
 {
 	struct tty_driver *driver;
 
+#ifdef CONFIG_VE
+	struct ve_struct *ve = get_exec_env();
+
+	if (!ve_is_super(ve)) {
+		driver = vtty_driver(device, index);
+		if (driver)
+			/*
+			 * noctty = 1 has been removed at porting in hope that
+			 * at tty_open noctty will be set as expected.
+			 */
+			return tty_driver_kref_get(driver);
+	}
+#endif
+
 	switch (device) {
 #ifdef CONFIG_VT
 	case MKDEV(TTY_MAJOR, 0): {
@@ -1843,6 +1858,10 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
 #endif
 	case MKDEV(TTYAUX_MAJOR, 1): {
 		struct tty_driver *console_driver = console_device(index);
+#ifdef CONFIG_VE
+		if (!ve_is_super(ve))
+			console_driver = vtty_console_driver(index);
+#endif
 		if (console_driver) {
 			driver = tty_driver_kref_get(console_driver);
 			if (driver && filp) {
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 808fbfe86f85..91cbc2188b4a 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -367,6 +367,9 @@ struct tty_file_private {
 #define TTY_HUPPED 		18	/* Post driver->hangup() */
 #define TTY_HUPPING		19	/* Hangup in progress */
 #define TTY_LDISC_HALTED	22	/* Line discipline is halted */
+#ifdef CONFIG_VE
+#define TTY_PINNED_BY_OTHER	24	/* TTY is pinned by other link end, defer closing */
+#endif
 
 /* Values for tty->flow_change */
 #define TTY_THROTTLE_SAFE 1
diff --git a/include/linux/ve.h b/include/linux/ve.h
index d3f77467cdf6..df4a0077212b 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -203,6 +203,12 @@ extern void monotonic_ve_to_abs(clockid_t which_clock, struct timespec64 *tp);
 extern bool current_user_ns_initial(void);
 struct user_namespace *ve_init_user_ns(void);
 
+#ifdef CONFIG_TTY
+extern struct tty_driver *vtty_driver(dev_t dev, int *index);
+extern struct tty_driver *vtty_console_driver(int *index);
+extern int vtty_open_master(envid_t veid, int idx);
+#endif /* CONFIG_TTY */
+
 extern struct cgroup *cgroup_get_ve_root1(struct cgroup *cgrp);
 
 #define ve_uevent_seqnum       (get_exec_env()->_uevent_seqnum)
diff --git a/kernel/ve/vecalls.c b/kernel/ve/vecalls.c
index 6ef36c76f643..a2a1b93ab49d 100644
--- a/kernel/ve/vecalls.c
+++ b/kernel/ve/vecalls.c
@@ -370,6 +370,9 @@ static int ve_configure(envid_t veid, unsigned int key,
 	struct ve_struct *ve;
 	int err = -ENOKEY;
 
+	if (key == VE_CONFIGURE_OPEN_TTY)
+		return vtty_open_master(veid, val);
+
 	ve = get_ve_by_id(veid);
 	if (!ve)
 		return -EINVAL;


More information about the Devel mailing list