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

Vladimir Davydov vdavydov at odin.com
Mon Sep 7 03:15:48 PDT 2015


The commit is pushed to "branch-rh7-3.10.0-229.7.2-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-229.7.2.vz7.6.7
------>
commit 198d52034aa3ed7c9e3695ace34df8c16aaa7225
Author: Cyrill Gorcunov <gorcunov at virtuozzo.com>
Date:   Mon Sep 7 14:15:48 2015 +0400

    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>
---
 configs/kernel-3.10.0-x86_64-debug.config |   1 -
 configs/kernel-3.10.0-x86_64.config       |   1 -
 drivers/tty/pty.c                         | 511 ++++++++++++++++++++++++++++++
 drivers/tty/tty_io.c                      |  36 +--
 include/linux/tty.h                       |   3 +
 include/linux/ve.h                        |  20 +-
 kernel/Kconfig.openvz                     |   5 -
 kernel/ve/Makefile                        |   2 +-
 kernel/ve/console.c                       | 382 ----------------------
 kernel/ve/vecalls.c                       |   3 +
 10 files changed, 540 insertions(+), 424 deletions(-)

diff --git a/configs/kernel-3.10.0-x86_64-debug.config b/configs/kernel-3.10.0-x86_64-debug.config
index 7474e1f3d744..dd0447be5492 100644
--- a/configs/kernel-3.10.0-x86_64-debug.config
+++ b/configs/kernel-3.10.0-x86_64-debug.config
@@ -5384,7 +5384,6 @@ CONFIG_VZ_DEV=m
 CONFIG_VE_IPTABLES=y
 CONFIG_VZ_WDOG=m
 CONFIG_VZ_EVENT=m
-CONFIG_VTTYS=y
 
 CONFIG_VZ_FAIRSCHED=y
 CONFIG_BLK_DEV_PLOOP=m
diff --git a/configs/kernel-3.10.0-x86_64.config b/configs/kernel-3.10.0-x86_64.config
index 92723f4e944c..7fc400bc9991 100644
--- a/configs/kernel-3.10.0-x86_64.config
+++ b/configs/kernel-3.10.0-x86_64.config
@@ -5355,7 +5355,6 @@ CONFIG_VZ_DEV=m
 CONFIG_VE_IPTABLES=y
 CONFIG_VZ_WDOG=m
 CONFIG_VZ_EVENT=m
-CONFIG_VTTYS=y
 
 CONFIG_VZ_FAIRSCHED=y
 CONFIG_BLK_DEV_PLOOP=m
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 529046b79b0d..b74ddcad6e7f 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -915,10 +915,521 @@ static void __init unix98_pty_init(void)
 static inline void unix98_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) {
+		map->veid = veid;
+		veid = idr_alloc(&vtty_idr, map, veid, veid + 1, GFP_KERNEL);
+		if (veid < 0) {
+			kfree(map);
+			return ERR_PTR(veid);
+		}
+	} 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 inode *inode, 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)
+{
+	WARN_ON(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();
+	if (!tty)
+		return ERR_PTR(-ENOMEM);
+	initialize_tty_struct(tty, driver, index);
+
+	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 pty_space(peer);
+}
+
+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 (!tty ||
+	    (test_bit(TTY_CLOSING, &tty->flags) ||
+	     test_bit(TTY_CLOSING, &tty->link->flags))) {
+		/*
+		 * The previous connection is about to
+		 * be closed so drop it from the map and
+		 * allocate a new one.
+		 */
+		if (tty)
+			vtty_map_clear(tty);
+		tty = tty_init_dev(vttys_driver, idx);
+		if (IS_ERR(tty))
+			goto err_install;
+		tty->count--;
+		tty_unlock(tty);
+		tty = tty->link;
+	}
+
+	/* One master at a time */
+	if (tty->count >= 1) {
+		ret = -EBUSY;
+		goto err_install;
+	}
+
+	vtty_drop_context();
+
+	WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
+
+	/*
+	 * 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 */
+
 static int __init pty_init(void)
 {
 	legacy_pty_init();
 	unix98_pty_init();
+	vtty_init();
 	return 0;
 }
 module_init(pty_init);
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 4a96ec999e30..8fc8334364e9 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1756,6 +1756,11 @@ int tty_release(struct inode *inode, struct file *filp)
 			(o_tty->count <= (pty_master ? 1 : 0));
 		do_sleep = 0;
 
+#ifdef CONFIG_VE
+		if (!o_tty_closing &&
+		    test_bit(TTY_PINNED_BY_OTHER, &tty->flags))
+			tty_closing = 0;
+#endif
 		if (tty_closing) {
 			if (waitqueue_active(&tty->read_wait)) {
 				wake_up_poll(&tty->read_wait, POLLIN);
@@ -1931,14 +1936,17 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
 	struct tty_driver *driver;
 
 #ifdef CONFIG_VE
-	extern struct tty_driver *vz_vt_device(struct ve_struct *ve, dev_t dev, int *index);
-	if (!ve_is_super(get_exec_env()) &&
-	    (MAJOR(device) == TTY_MAJOR && MINOR(device) < VZ_VT_MAX_DEVS)) {
-		driver = tty_driver_kref_get(vz_vt_device(get_exec_env(), device, index));
-		*noctty = 1;
-		return driver;
+	struct ve_struct *ve = get_exec_env();
+
+	if (!ve_is_super(ve)) {
+		driver = vtty_driver(device, index);
+		if (driver) {
+			*noctty = 1;
+			return tty_driver_kref_get(driver);
+		}
 	}
 #endif
+
 	switch (device) {
 #ifdef CONFIG_VT
 	case MKDEV(TTY_MAJOR, 0): {
@@ -1952,10 +1960,8 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
 	case MKDEV(TTYAUX_MAJOR, 1): {
 		struct tty_driver *console_driver = console_device(index);
 #ifdef CONFIG_VE
-		if (!ve_is_super(get_exec_env())) {
-			extern struct tty_driver *vz_console_device(int *index);
-			console_driver = vz_console_device(index);
-		}
+		if (!ve_is_super(ve))
+			console_driver = vtty_console_driver(index);
 #endif
 		if (console_driver) {
 			driver = tty_driver_kref_get(console_driver);
@@ -3628,9 +3634,6 @@ static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL);
 
 #ifdef CONFIG_VE
 
-extern int vz_con_ve_init(struct ve_struct *ve);
-extern void vz_con_ve_fini(struct ve_struct *ve);
-
 void console_sysfs_notify(void)
 {
 	struct ve_struct *ve = get_exec_env();
@@ -3647,7 +3650,6 @@ void ve_tty_console_fini(struct ve_struct *ve)
 	device_remove_file(consdev, &dev_attr_active);
 	device_destroy_namespace(tty_class, MKDEV(TTYAUX_MAJOR, 1), ve);
 	device_destroy_namespace(tty_class, MKDEV(TTYAUX_MAJOR, 0), ve);
-	vz_con_ve_fini(ve);
 }
 
 int ve_tty_console_init(struct ve_struct *ve)
@@ -3669,15 +3671,9 @@ int ve_tty_console_init(struct ve_struct *ve)
 	if (err)
 		goto err_consfile;
 
-	err = vz_con_ve_init(ve);
-	if (err)
-		goto err_vzcon;
-
 	ve->consdev = dev;
 	return 0;
 
-err_vzcon:
-	device_remove_file(dev, &dev_attr_active);
 err_consfile:
 	device_destroy_namespace(tty_class, MKDEV(TTYAUX_MAJOR, 1), ve);
 err_consdev:
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 025cb0bb7b2e..d31dc8030a37 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -322,6 +322,9 @@ struct tty_file_private {
 #define TTY_HUPPING 		21	/* ->hangup() in progress */
 #define TTY_LDISC_HALTED	22	/* Line discipline is halted */
 #define TTY_CHARGED		23	/* Charged as ub resource */
+#ifdef CONFIG_VE
+#define TTY_PINNED_BY_OTHER	24	/* TTY is pinned by other link end, defer closing */
+#endif
 
 #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
 
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 1758d5101244..10c150a01fd0 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -67,13 +67,6 @@ struct ve_struct {
 	struct binfmt_misc	*binfmt_misc;
 #endif
 
-#define VZ_VT_MAX_DEVS		12
-	struct tty_driver	*vz_vt_driver;
-	struct tty_struct	*vz_tty_vt[VZ_VT_MAX_DEVS];
-
-	struct tty_struct	*vz_tty_conm;
-	struct tty_struct	*vz_tty_cons;
-
 #ifdef CONFIG_LEGACY_PTYS
 	struct tty_driver	*pty_driver, *pty_slave_driver;
 #endif
@@ -217,17 +210,16 @@ extern unsigned long long ve_relative_clock(struct timespec * ts);
 extern void monotonic_abs_to_ve(clockid_t which_clock, struct timespec *tp);
 extern void monotonic_ve_to_abs(clockid_t which_clock, struct timespec *tp);
 
-#ifdef CONFIG_VTTYS
-extern int vtty_open_master(int veid, int idx);
-extern struct tty_driver *vtty_driver;
-#else
-static inline int vtty_open_master(int veid, int idx) { return -ENODEV; }
-#endif
-
 void ve_stop_ns(struct pid_namespace *ns);
 void ve_exit_ns(struct pid_namespace *ns);
 int ve_start_container(struct ve_struct *ve);
 
+#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 */
+
 #else	/* CONFIG_VE */
 
 #define ve_uevent_seqnum uevent_seqnum
diff --git a/kernel/Kconfig.openvz b/kernel/Kconfig.openvz
index d694a290eea6..2465d82ef1dc 100644
--- a/kernel/Kconfig.openvz
+++ b/kernel/Kconfig.openvz
@@ -94,11 +94,6 @@ config VZ_EVENT
  	  networking code does. By now just the notifications of
  	  the VE essensial status changes are being sent.
 
-config VTTYS
-	bool "Virtual tty device"
-	depends on VE && VZ_DEV
-	default y
-
 endmenu
 
 
diff --git a/kernel/ve/Makefile b/kernel/ve/Makefile
index c32e03dc8b7b..718cc36e51f0 100644
--- a/kernel/ve/Makefile
+++ b/kernel/ve/Makefile
@@ -4,7 +4,7 @@
 # Copyright (c) 2000-2015 Parallels IP Holdings GmbH
 #
 
-obj-$(CONFIG_VE) = ve.o veowner.o hooks.o vzstat_core.o ve-kobject.o console.o
+obj-$(CONFIG_VE) = ve.o veowner.o hooks.o vzstat_core.o ve-kobject.o
 obj-$(CONFIG_VZ_WDOG) += vzwdog.o
 obj-$(CONFIG_VE_CALLS) += vzmon.o
 
diff --git a/kernel/ve/console.c b/kernel/ve/console.c
deleted file mode 100644
index bc7d752a4084..000000000000
--- a/kernel/ve/console.c
+++ /dev/null
@@ -1,382 +0,0 @@
-#define pr_fmt(fmt) "vz con: " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/mutex.h>
-#include <linux/init.h>
-
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-
-#include <linux/ve.h>
-
-#define VZ_CON_INDEX		(0)
-#define VZ_CON_SLAVE_NAME	"vzcons"
-
-static struct tty_driver *vz_conm_driver;
-static struct tty_driver *vz_cons_driver;
-
-extern struct class *tty_class;
-
-static char *vzcon_devnode(struct device *dev, umode_t *mode)
-{
-	if (mode)
-		*mode = 0600;
-	return NULL;
-}
-
-static struct class vz_con_class_base = {
-	.name		= "vzcon",
-	.devnode	= vzcon_devnode,
-	.ns_type	= &ve_ns_type_operations,
-	.namespace	= ve_namespace,
-	.owner		= THIS_MODULE,
-};
-
-static struct class *vz_con_class = &vz_con_class_base;
-
-static struct tty_struct *vz_tty_lookup(struct tty_driver *driver,
-					struct inode *inode, int idx)
-{
-	struct ve_struct *ve = get_exec_env();
-
-	BUG_ON(driver != vz_conm_driver &&
-	       driver != vz_cons_driver);
-
-	if (idx != VZ_CON_INDEX || driver == vz_cons_driver)
-		return ERR_PTR(-EIO);
-
-	return ve->vz_tty_vt[idx];
-}
-
-static int vz_tty_install(struct tty_driver *driver, struct tty_struct *tty)
-{
-	struct ve_struct *ve = get_exec_env();
-
-	BUG_ON(driver != vz_conm_driver);
-
-	tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
-	if (!tty->port)
-		return -ENOMEM;
-	tty_port_init(tty->port);
-	tty->termios = driver->init_termios;
-
-	ve->vz_tty_vt[tty->index] = tty;
-
-	tty_driver_kref_get(driver);
-	tty->count++;
-	return 0;
-}
-
-static void vz_tty_remove(struct tty_driver *driver, struct tty_struct *tty)
-{
-	struct ve_struct *ve = get_exec_env();
-
-	BUG_ON(driver != vz_conm_driver);
-	ve->vz_tty_vt[tty->index] = NULL;
-}
-
-static int vz_tty_open(struct tty_struct *tty, struct file *filp)
-{
-	set_bit(TTY_THROTTLED, &tty->flags);
-	return 0;
-}
-
-static void vz_tty_close(struct tty_struct *tty, struct file *filp)
-{
-}
-
-static void vz_tty_shutdown(struct tty_struct *tty)
-{
-}
-
-static void vz_tty_cleanup(struct tty_struct *tty)
-{
-	tty_port_put(tty->port);
-}
-
-static int vz_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
-	return count;
-}
-
-static int vz_tty_write_room(struct tty_struct *tty)
-{
-	return 4096;
-}
-
-static void vz_tty_unthrottle(struct tty_struct *tty)
-{
-	set_bit(TTY_THROTTLED, &tty->flags);
-}
-
-static const struct tty_operations vz_tty_fops = {
-	.lookup		= vz_tty_lookup,
-	.install	= vz_tty_install,
-	.remove		= vz_tty_remove,
-	.open		= vz_tty_open,
-	.close		= vz_tty_close,
-	.shutdown	= vz_tty_shutdown,
-	.cleanup	= vz_tty_cleanup,
-	.write		= vz_tty_write,
-	.write_room	= vz_tty_write_room,
-	.unthrottle	= vz_tty_unthrottle,
-};
-
-static struct tty_struct *vz_vt_lookup(struct tty_driver *driver,
-				       struct inode *inode, int idx)
-{
-	return driver->ttys[idx];
-}
-
-static int vz_vt_install(struct tty_driver *driver, struct tty_struct *tty)
-{
-	tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
-	if (!tty->port)
-		return -ENOMEM;
-	tty_standard_install(driver, tty);
-	tty_port_init(tty->port);
-	return 0;
-}
-
-static void vz_vt_cleanup(struct tty_struct *tty)
-{
-	kfree(tty->port);
-	tty->port = NULL;
-}
-
-const static struct tty_operations vt_tty_fops = {
-	.lookup		= vz_vt_lookup,
-	.install	= vz_vt_install,
-	.open		= vz_tty_open,
-	.cleanup	= vz_vt_cleanup,
-	.write		= vz_tty_write,
-	.write_room	= vz_tty_write_room,
-	.unthrottle	= vz_tty_unthrottle,
-};
-
-static int __vz_vt_ve_init(struct ve_struct *ve)
-{
-#define TTY_DRIVER_ALLOC_FLAGS			\
-	(TTY_DRIVER_REAL_RAW		|	\
-	 TTY_DRIVER_RESET_TERMIOS	|	\
-	 TTY_DRIVER_DYNAMIC_DEV		|	\
-	 TTY_DRIVER_CONTAINERIZED)
-
-	struct tty_driver *driver;
-	int ret = 0;
-	int i;
-
-	driver = tty_alloc_driver(VZ_VT_MAX_DEVS, TTY_DRIVER_ALLOC_FLAGS);
-	if (IS_ERR(driver)) {
-		ret = PTR_ERR(driver);
-		pr_err("Can't allocate VT master driver\n");
-		return ret;
-	}
-
-	driver->driver_name	= "vt_master";
-	driver->name		= "tty";
-	driver->name_base	= 1;
-	driver->major		= 0;
-	driver->minor_start	= 1;
-	driver->type		= TTY_DRIVER_TYPE_CONSOLE;
-	driver->init_termios	= tty_std_termios;
-	driver->ve		= ve;
-	tty_set_operations(driver, &vt_tty_fops);
-
-	ret = tty_register_driver(driver);
-	if (ret) {
-		pr_err("Can't register vt master driver\n");
-		put_tty_driver(driver);
-		return ret;
-	}
-
-	for (i = 0; i < VZ_VT_MAX_DEVS; i++) {
-		dev_t dev = MKDEV(TTY_MAJOR, i);
-		struct device *d;
-
-		d = device_create(tty_class, NULL, dev, ve, "tty%i", i);
-		if (IS_ERR(d)) {
-			for (i--; i >= 0; i--)
-				device_destroy_namespace(tty_class, dev, ve);
-			tty_unregister_driver(driver);
-			put_tty_driver(driver);
-			return PTR_ERR(d);
-		}
-	}
-	ve->vz_vt_driver = driver;
-
-	return 0;
-#undef TTY_DRIVER_ALLOC_FLAGS
-}
-
-static void __vz_vt_ve_fini(struct ve_struct *ve)
-{
-	int i;
-
-	if (!ve->vz_vt_driver)
-		return;
-
-	for (i = 0; i < VZ_VT_MAX_DEVS; i++) {
-		dev_t dev = MKDEV(TTY_MAJOR, i);
-		device_destroy_namespace(tty_class, dev, ve);
-	}
-
-	tty_unregister_driver(ve->vz_vt_driver);
-	put_tty_driver(ve->vz_vt_driver);
-}
-
-static int __vz_con_ve_init(struct ve_struct *ve)
-{
-	struct device *d;
-	dev_t dev;
-
-	dev = MKDEV(vz_cons_driver->major, vz_cons_driver->minor_start);
-	d = device_create(vz_con_class, NULL, dev, ve, VZ_CON_SLAVE_NAME);
-
-	return IS_ERR(d) ? PTR_ERR(d) : 0;
-}
-
-int vz_con_ve_init(struct ve_struct *ve)
-{
-	int ret = 0;
-
-	if (ve != get_ve0()) {
-		ret = __vz_con_ve_init(ve);
-		if (!ret)
-			ret = __vz_vt_ve_init(ve);
-	}
-	return ret;
-}
-EXPORT_SYMBOL_GPL(vz_con_ve_init);
-
-static void __vz_con_ve_fini(struct ve_struct *ve)
-{
-	dev_t dev = MKDEV(vz_cons_driver->major, vz_cons_driver->minor_start);
-	device_destroy_namespace(vz_con_class, dev, ve);
-	__vz_vt_ve_fini(ve);
-}
-
-void vz_con_ve_fini(struct ve_struct *ve)
-{
-	if (ve != get_ve0())
-		return __vz_con_ve_fini(ve);
-}
-EXPORT_SYMBOL_GPL(vz_con_ve_fini);
-
-static int __init vz_con_init(void)
-{
-#define TTY_DRIVER_ALLOC_FLAGS			\
-	(TTY_DRIVER_REAL_RAW		|	\
-	 TTY_DRIVER_RESET_TERMIOS	|	\
-	 TTY_DRIVER_UNNUMBERED_NODE	|	\
-	 TTY_DRIVER_DEVPTS_MEM		|	\
-	 TTY_DRIVER_DYNAMIC_ALLOC	|	\
-	 TTY_DRIVER_DYNAMIC_DEV		|	\
-	 TTY_DRIVER_CONTAINERIZED)
-
-	int ret = 0;
-
-	ret = class_register(&vz_con_class_base);
-	if (ret) {
-		pr_err("Can't register vzcon class\n");
-		return ret;
-	}
-
-	vz_conm_driver = tty_alloc_driver(1, TTY_DRIVER_ALLOC_FLAGS);
-	if (IS_ERR(vz_conm_driver)) {
-		ret = PTR_ERR(vz_conm_driver);
-		pr_err("Can't allocate vzcon master driver\n");
-		goto err_class_unregister;
-	}
-
-	vz_cons_driver = tty_alloc_driver(1, TTY_DRIVER_ALLOC_FLAGS);
-	if (IS_ERR(vz_cons_driver)) {
-		ret = PTR_ERR(vz_cons_driver);
-		pr_err("Can't allocate vzcon slave driver\n");
-		goto err_put_master;
-	}
-
-	vz_conm_driver->driver_name	= "vzcon_master";
-	vz_conm_driver->name		= "vzconm";
-	vz_conm_driver->name_base	= 1;
-	vz_conm_driver->major		= 0;
-	vz_conm_driver->minor_start	= 1;
-	vz_conm_driver->type		= TTY_DRIVER_TYPE_CONSOLE;
-	vz_conm_driver->subtype		= PTY_TYPE_MASTER;
-	vz_conm_driver->init_termios	= tty_std_termios;
-	vz_conm_driver->ve		= get_ve0();
-	tty_set_operations(vz_conm_driver, &vz_tty_fops);
-
-	vz_cons_driver->driver_name	= "vzcon_slave";
-	vz_cons_driver->name		= "vzcons";
-	vz_cons_driver->name_base	= 1;
-	vz_cons_driver->major		= 0;
-	vz_cons_driver->minor_start	= 1;
-	vz_cons_driver->type		= TTY_DRIVER_TYPE_CONSOLE;
-	vz_conm_driver->subtype		= PTY_TYPE_SLAVE;
-	vz_cons_driver->init_termios	= tty_std_termios;
-	vz_cons_driver->ve		= get_ve0();
-	tty_set_operations(vz_cons_driver, &vz_tty_fops);
-
-	ret = tty_register_driver(vz_conm_driver);
-	if (ret) {
-		pr_err("Can't register vzcon master driver\n");
-		goto err_put_slave;
-	}
-	ret = tty_register_driver(vz_cons_driver);
-	if (ret) {
-		pr_err("Can't register vzcon slave driver\n");
-		goto err_unregister_master;
-	}
-
-	ret = __vz_con_ve_init(get_ve0());
-	if (ret) {
-		pr_err("Can't init for node\n");
-		goto err_unregister_slave;
-	}
-
-	return 0;
-
-err_unregister_slave:
-	tty_unregister_driver(vz_cons_driver);
-err_unregister_master:
-	tty_unregister_driver(vz_conm_driver);
-err_put_slave:
-	put_tty_driver(vz_cons_driver);
-err_put_master:
-	put_tty_driver(vz_conm_driver);
-err_class_unregister:
-	class_unregister(&vz_con_class_base);
-	return ret;
-#undef TTY_DRIVER_ALLOC_FLAGS
-}
-module_init(vz_con_init);
-
-static void __exit vz_con_exit(void)
-{
-	__vz_con_ve_fini(get_ve0());
-	tty_unregister_driver(vz_conm_driver);
-	tty_unregister_driver(vz_cons_driver);
-	put_tty_driver(vz_conm_driver);
-	put_tty_driver(vz_cons_driver);
-	class_unregister(&vz_con_class_base);
-}
-module_exit(vz_con_exit)
-
-struct tty_driver *vz_console_device(int *index)
-{
-	*index = VZ_CON_INDEX;
-	return vz_conm_driver;
-}
-EXPORT_SYMBOL_GPL(vz_console_device);
-
-struct tty_driver *vz_vt_device(struct ve_struct *ve, dev_t dev, int *index)
-{
-	BUG_ON(MINOR(dev) > VZ_VT_MAX_DEVS);
-
-	*index = MINOR(dev) ? MINOR(dev) - 1 : 0;
-	return ve->vz_vt_driver;
-}
-EXPORT_SYMBOL_GPL(vz_vt_device);
diff --git a/kernel/ve/vecalls.c b/kernel/ve/vecalls.c
index f4c3190a6e7e..9e866153cc2d 100644
--- a/kernel/ve/vecalls.c
+++ b/kernel/ve/vecalls.c
@@ -998,6 +998,9 @@ static int ve_configure(envid_t veid, unsigned int key,
 	case VE_CONFIGURE_OS_RELEASE:
 		err = init_ve_osrelease(ve, data);
 		break;
+	case VE_CONFIGURE_OPEN_TTY:
+		err = vtty_open_master(ve->veid, val);
+		break;
 	}
 
 	put_ve(ve);



More information about the Devel mailing list