[Devel] [RFC rh7 v2] ve/tty: vt -- Implement per VE support for console and terminals

Cyrill Gorcunov gorcunov at virtuozzo.com
Mon Aug 3 12:08:19 PDT 2015


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 first virtual terminal. Terminals represent
the following mapping

	 | master					slave
	 | /dev/cosole|/dev/tty0|/dev/vttym0		/dev/vttys0
	 | ...
	 | /dev/tty11|/dev/vttym11			/dev/vttys11

major:minor numbers for peers are allocated dynamically by the kenel,
thus if we need to connect to container's slave peer from the node
we have to

 - scan node's /proc/tty/drivers and find the numbers for
   "vtty_slave" driver, for example

	 | [root at pcs7 ~]# cat /proc/tty/drivers
	 | ...
	 | vtty_slave            /dev/vttys     252 0-11 console
	 | vtty_master           /dev/vttym     253 0-11 console
	 | ...

 - add major:minor pairs into allowed devices, for example

	 | echo 'c 253:* rwm' > /sys/fs/cgroup/devices/$ctid/devices.allow

Once the bullets above is done the userspace utility may
open up slave peer and read/write data to/from. Note the
slave may be opened if only if a master peer been previously
opened.

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 thus to connect to a slave peer from a node one have to
   "enter" VE cgroup first.

   Same time here is vtty_open_slave() helper provided which should
   allow to connect to a slave peer from a node directly using ioctl
   call (this is to implement yet).

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

N.B.: I've done some manual testing with python help

 1) Run container (ctid=200)
 2) Run the script on the node

	| #!/bin/sh
	| master=`cat /proc/tty/drivers | grep vtty_master | awk '{ print $3; }'`
	| slave=`cat /proc/tty/drivers | grep vtty_slave | awk '{ print $3; }'i`
	| echo "c $master:* rwm" > /sys/fs/cgroup/devices/$1/devices.allow
	| echo "c $slave:* rwm" > /sys/fs/cgroup/devices/$1/devices.allow
	| vzctl exec $1 mknod /dev/vttym5 c $master 5
	| vzctl exec $1 mknod /dev/vttys5 c $slave 5
 3) In container run two instances of python where each does
    a)
        | >>> f=open("/dev/vttym5", "r+", 0)
        | >>> f.write("aaa")
    b)
        | >>> f=open("/dev/vttys5", "r+", 0)
        | >>> f.read(3)
        | >>> 'aaa'

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

Signed-off-by: Cyrill Gorcunov <gorcunov at virtuozzo.com>
CC: Vladimir Davydov <vdavydov at virtuozzo.com>
CC: Konstantin Khorenko <khorenko at virtuozzo.com>
CC: Pavel Emelyanov <xemul at virtuozzo.com>
CC: Andrey Vagin <avagin at virtuozzo.com>
CC: Igor Sukhih <igor at parallels.com>
---
 drivers/tty/pty.c    |  543 +++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/tty/tty_io.c |   29 +-
 include/linux/tty.h  |    3 
 include/linux/ve.h   |   28 +-
 kernel/ve/Makefile   |    2 
 kernel/ve/console.c  |  382 -----------------------------------
 6 files changed, 572 insertions(+), 415 deletions(-)

FWIW I didn't test yet the opening slave from the node  because
it requires vzctl patching together with the kernel. Will do
that on top.

Index: linux-pcs7.git/drivers/tty/pty.c
===================================================================
--- linux-pcs7.git.orig/drivers/tty/pty.c
+++ linux-pcs7.git/drivers/tty/pty.c
@@ -26,12 +26,21 @@
 
 #include <bc/misc.h>
 
+#include <linux/ve.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+
 #ifdef CONFIG_UNIX98_PTYS
 static struct tty_driver *ptm_driver;
 static struct tty_driver *pts_driver;
 static DEFINE_MUTEX(devpts_mutex);
 #endif
 
+#if defined(CONFIG_VE) && defined(CONFIG_UNIX98_PTYS)
+static int __init vtty_init(void);
+static struct file_operations vtty_fops;
+#endif
+
 static void pty_close(struct tty_struct *tty, struct file *filp)
 {
 	BUG_ON(!tty);
@@ -824,6 +833,540 @@ static int __init pty_init(void)
 {
 	legacy_pty_init();
 	unix98_pty_init();
+#ifdef CONFIG_VE
+	vtty_init();
+#endif
 	return 0;
 }
 module_init(pty_init);
+
+#if defined(CONFIG_VE) && defined(CONFIG_UNIX98_PTYS)
+
+/*
+ * 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 id of VE and associated masters
+ * tty peers. Upon any master peer open we create
+ * associated slave peer and mark master inside tty map
+ * which allows to reopen it on user request, unlike traditional
+ * Unix98 terminals where only one master is allowed. In turn
+ * when user asks to open a slave peer we lookup for master
+ * first and then use @link member to fetch a slave.
+ *
+ * The major number for vtty is allocated dynamically upon
+ * kernel start and should be found via /proc/tty/drivers output
+ * using vtty[m|s]_driver->driver_name. While the primary purpose
+ * to containerize console and terminals the kernel creates real
+ * vttym[0-11] and vttys[0-11] devices for master and slave peers
+ * appropriately. Thus to read console output one have to open
+ * vttys0 device from inside of container's context.
+ *
+ * Same time to be able to open slave peer from a node we
+ * provide vtty_open_slave helper.
+ */
+static struct tty_driver *vttym_driver;
+static struct tty_driver *vttys_driver;
+static DEFINE_IDR(vtty_idr);
+
+#undef VTTY_DEBUG
+#ifdef VTTY_DEBUG
+#define vtty_printk(fmt, ...)								\
+	printk("%20s: %4d: " fmt,							\
+	       __func__, __LINE__, ##__VA_ARGS__)
+#define vtty_printk_one(__tty)								\
+	vtty_printk("tty %p count %4d flags 0x%-8lx\n",					\
+		    __tty, __tty->count, __tty->flags)
+#define vtty_printk_pair(__tty)								\
+	vtty_printk("tty %p count %4d flags 0x%-8lx link %p count %4d flags 0x%-8lx\n",	\
+		    __tty, __tty->count, __tty->flags,					\
+		    __tty->link, __tty->link->count, __tty->link->flags)
+#else
+#define vtty_printk(fmt, ...)
+#define vtty_printk_one(__tty)
+#define vtty_printk_pair(__tty)
+#endif
+
+#define driver_is_master(d)	((d) == vttym_driver)
+#define tty_is_master(t)	driver_is_master((t)->driver)
+
+typedef struct {
+	envid_t			veid;
+	struct tty_struct	*vttys[MAX_NR_VTTY_CONSOLES];
+	spinlock_t		lock;
+} vtty_map_t;
+
+static vtty_map_t *vtty_map_lookup(envid_t veid)
+{
+	return idr_find(&vtty_idr, veid);
+}
+
+static void vtty_map_remove(vtty_map_t *map)
+{
+	vtty_printk("map %p id %d\n", map, map ? map->veid : -1);
+	if (map) {
+		idr_remove(&vtty_idr, map->veid);
+		kfree(map);
+	}
+}
+
+/*
+ * While vtty_install and vtty_shutdown are ordered by tty_mutex,
+ * vtty_cleanup can be called in any order so use spinlock to
+ * not throttle in a midle of zapping.
+ */
+static void vtty_map_set(vtty_map_t *map, struct tty_struct *tty, int index)
+{
+	vtty_printk("map %p id %d tty %p index %d\n",
+		    map, map->veid, tty, index);
+	spin_lock(&map->lock);
+	map->vttys[index] = tty;
+	spin_unlock(&map->lock);
+}
+
+static void vtty_map_zap(vtty_map_t *map, struct tty_struct *tty, int index)
+{
+	vtty_printk("map %p id %d tty %p index %d\n", map, map->veid, tty, index);
+	spin_lock(&map->lock);
+	if (map->vttys[index] == tty)
+		map->vttys[index] = NULL;
+	spin_unlock(&map->lock);
+}
+
+static vtty_map_t *vtty_map_alloc(envid_t veid)
+{
+	vtty_map_t *map = kzalloc(sizeof(*map), GFP_KERNEL);
+
+	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);
+		}
+		spin_lock_init(&map->lock);
+	} else
+		map = ERR_PTR(-ENOMEM);
+
+	vtty_printk("map %p id %d\n", map, veid);
+	return map;
+}
+
+static inline int tty_is_exiting(struct tty_struct *tty)
+{
+	return test_bit(TTY_CLOSING, &tty->flags)		||
+		test_bit(TTY_HUPPING, &tty->flags)		||
+		test_bit(TTY_LDISC_CHANGING, &tty->flags)	||
+		tty->count < 1;
+}
+
+static struct tty_struct *vtty_lookup(struct tty_driver *driver,
+				      struct inode *inode, int idx)
+{
+	vtty_map_t *map = vtty_map_lookup(get_exec_env()->veid);
+	struct tty_struct *tty;
+
+	if (idx < 0 || idx >= MAX_NR_VTTY_CONSOLES)
+		return ERR_PTR(-EIO);
+
+	vtty_printk("driver %s index %d\n", driver->driver_name, idx);
+	/*
+	 * 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) {
+		/*
+		 * Fetching existing tty is a bit tricky:
+		 * the pair may exist but one of the peer
+		 * may start exiting so the caller should
+		 * wait until exit complete and re-ask for
+		 * a new peer pair. For this we check the
+		 * master peer to be alive first and then
+		 * the slave peer state if been requested.
+		 */
+		if (tty_is_exiting(tty))
+			tty = ERR_PTR(-ENXIO);
+		else if (!driver_is_master(driver)) {
+			tty = tty->link;
+			if (tty_is_exiting(tty))
+				tty = ERR_PTR(-ENXIO);
+		}
+	}
+	vtty_printk("tty %p count %4d\n",
+		    tty, IS_ERR_OR_NULL(tty) ? -1 : tty->count);
+	return tty;
+}
+
+static int vtty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	int ret = tty_init_termios(tty);
+	if (ret)
+		return ret;
+
+	tty_driver_kref_get(driver);
+	tty->count++;
+	if (driver_is_master(driver))
+		vtty_map_set(tty->driver_data, tty, tty->index);
+	return 0;
+}
+
+static struct tty_struct *vtty_install_slave(vtty_map_t *map, int index)
+{
+	struct tty_struct *tty;
+
+	tty = alloc_tty_struct();
+	if (!tty)
+		return ERR_PTR(-ENOMEM);
+	initialize_tty_struct(tty, vttys_driver, index);
+
+	tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
+	if (!tty->port) {
+		deinitialize_tty_struct(tty);
+		free_tty_struct(tty);
+		return ERR_PTR(-ENOMEM);
+	}
+	tty->driver_data = map;
+	WARN_ON(vtty_standard_install(vttys_driver, tty));
+	tty_port_init(tty->port);
+	tty->port->itty = tty;
+
+	set_bit(TTY_EXTRA_REF, &tty->flags);
+	return tty;
+}
+
+static int vtty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	envid_t veid = get_exec_env()->veid;
+	struct tty_struct *peer;
+	vtty_map_t *map;
+
+	map = vtty_map_lookup(veid);
+	if (!map) {
+		map = vtty_map_alloc(veid);
+		if (IS_ERR(map))
+			return PTR_ERR(map);
+	}
+	tty->driver_data = map;
+
+	tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
+	if (!tty->port)
+		return -ENOMEM;
+
+	peer = vtty_install_slave(map, tty->index);
+	if (IS_ERR(peer)) {
+		vtty_map_zap(map, tty, tty->index);
+		kfree(tty->port);
+		return PTR_ERR(peer);
+	}
+	WARN_ON(vtty_standard_install(driver, tty));
+	tty_port_init(tty->port);
+	tty->port->itty = tty;
+
+	tty->link = peer;
+	peer->link = tty;
+
+	vtty_printk_pair(tty);
+	return 0;
+}
+
+static int vtty_open(struct tty_struct *tty, struct file *filp)
+{
+	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+		return -EIO;
+
+	clear_bit(TTY_IO_ERROR, &tty->flags);
+	clear_bit(TTY_OTHER_CLOSED, &tty->flags);
+	clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
+	set_bit(TTY_THROTTLED, &tty->flags);
+
+	vtty_printk_pair(tty);
+	return 0;
+}
+
+static void vtty_close(struct tty_struct *tty, struct file *filp)
+{
+	struct tty_struct *peer = tty->link;
+
+	if (tty->count > 1)
+		return;
+
+	if (test_bit(TTY_IO_ERROR, &tty->flags))
+		return;
+
+	wake_up_interruptible(&tty->read_wait);
+	wake_up_interruptible(&tty->write_wait);
+
+	wake_up_interruptible(&peer->read_wait);
+	wake_up_interruptible(&peer->write_wait);
+
+	set_bit(TTY_IO_ERROR, &tty->flags);
+	set_bit(TTY_OTHER_CLOSED, &peer->flags);
+
+	/*
+	 * TTY_EXTRA_REF set for the peers which
+	 * carries additional @count (ie peers
+	 * which were created as a pair to a master
+	 * one, thus when master is closed we drop
+	 * the reference so slave will be closed
+	 * as well).
+	 */
+	if (test_bit(TTY_EXTRA_REF, &peer->flags)) {
+		clear_bit(TTY_EXTRA_REF, &peer->flags);
+		peer->count--;
+	}
+
+	if (tty_is_master(tty)) {
+		tty_unlock(tty);
+		tty_vhangup(peer);
+		tty_lock(tty);
+	}
+
+	vtty_printk_pair(tty);
+}
+
+static void vtty_cleanup(struct tty_struct *tty)
+{
+	cancel_work_sync(&tty->port->buf.work);
+
+	/*
+	 * Make sure line discipline is off already.
+	 * It is just for debug reasons.
+	 */
+	WARN_ON_ONCE(!test_bit(TTY_LDISC_HALTED, &tty->flags));
+	WARN_ON_ONCE(test_bit(TTY_LDISC, &tty->flags));
+
+	if (tty_is_master(tty))
+		vtty_map_zap(tty->driver_data, tty, tty->index);
+
+	tty_port_put(tty->port);
+	vtty_printk_one(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 {
+			int _count = test_bit(TTY_EXTRA_REF, &tty->flags) ? 2 : 1;
+			/*
+			 * Flush the slave reader if noone
+			 * is actually hooked on. Otherwise
+			 * wait until reader fetch all data.
+			 */
+			if (peer->count < _count)
+				tty_perform_flush(peer, TCIFLUSH);
+		}
+	}
+
+	return count;
+}
+
+static const struct tty_operations vtty_ops = {
+	.lookup		= vtty_lookup,
+	.install	= vtty_install,
+	.open		= vtty_open,
+	.close		= vtty_close,
+	.cleanup	= vtty_cleanup,
+	.write		= vtty_write,
+	.write_room	= pty_space,
+	.set_termios	= pty_set_termios,
+	.unthrottle	= pty_unthrottle,
+	.remove		= pty_unix98_remove,
+};
+
+struct tty_driver *vtty_console_driver(int *index)
+{
+	*index = 0;
+	return vttym_driver;
+}
+
+int vtty_match(dev_t device)
+{
+	return MAJOR(device) == TTY_MAJOR && MINOR(device) < MAX_NR_VTTY_CONSOLES;
+}
+
+struct tty_driver *vtty_driver(dev_t dev, int *index)
+{
+	BUG_ON(MINOR(dev) >= MAX_NR_VTTY_CONSOLES);
+
+	*index = MINOR(dev);
+	return vttym_driver;
+}
+
+static void ve_vtty_fini(void *data)
+{
+	struct ve_struct *ve = data;
+	vtty_map_remove(vtty_map_lookup(ve->veid));
+}
+
+static struct ve_hook vtty_hook = {
+	.fini           = ve_vtty_fini,
+	.priority       = HOOK_PRIO_DEFAULT,
+	.owner          = THIS_MODULE,
+};
+
+static int __init vtty_init(void)
+{
+#define TTY_DRIVER_ALLOC_FLAGS			\
+	(TTY_DRIVER_REAL_RAW		|	\
+	 TTY_DRIVER_RESET_TERMIOS	|	\
+	 TTY_DRIVER_DEVPTS_MEM)
+
+	vttym_driver = tty_alloc_driver(MAX_NR_VTTY_CONSOLES, TTY_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, TTY_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_CONSOLE;
+	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_CONSOLE;
+	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;
+#undef TTY_DRIVER_ALLOC_FLAGS
+}
+
+int vtty_open_slave(envid_t veid, int idx)
+{
+	vtty_map_t *map = vtty_map_lookup(veid);
+	struct tty_struct *tty;
+	struct file *file;
+	char devname[64];
+	int fd, ret;
+
+	fd = get_unused_fd_flags(0);
+	if (fd < 0)
+		return fd;
+
+	snprintf(devname, sizeof(devname),
+		 "ve%d-%s%d", veid, vttym_driver->name, idx);
+	file = anon_inode_getfile(devname, &vtty_fops, NULL, O_RDWR);
+	if (IS_ERR(file)) {
+		ret = PTR_ERR(file);
+		goto err_put_unused_fd;
+	}
+
+	ret = tty_alloc_file(file);
+	if (ret)
+		goto err_fput;
+
+	mutex_lock(&tty_mutex);
+	/*
+	 * Unlike openting vtty in traditional way
+	 * opening via ioctl brings own requirements:
+	 *
+	 * 1) It should return a slave tty peer
+	 * 2) If appropriate index has been already borrowed
+	 *    (say some program already keep /dev/console opened)
+	 *    we should reuse an existing tty
+	 * 3) If noone has ever opened a tty yet -- we need to allocate
+	 *    a new tty pair and return a slave peer to a caller.
+	 *
+	 *    Note that we have to assign additional @count on
+	 *    master peer in such scenario: otherwise tty layer
+	 *    check code will complain on @count imbalance.
+	 */
+	tty = map ? map->vttys[idx] : NULL;
+	if (tty) {
+		if (tty_is_exiting(tty) || tty_is_exiting(tty->link)) {
+			ret = -ENODEV;
+			goto err_install;
+		}
+	} else {
+		tty = tty_init_dev(vttym_driver, idx);
+		if (IS_ERR(tty)) {
+			ret = PTR_ERR(tty);
+			goto err_install;
+		}
+		set_bit(TTY_EXTRA_REF, &tty->flags);
+		tty->count++;
+	}
+
+	/*
+	 * We need a slave peer to return. Note we
+	 * drop TTY_EXTRA_REF here because we assign
+	 * a real file over the slave, but in turn
+	 * master peer assigned with TTY_EXTRA_REF
+	 * together with @count incremented.
+	 */
+	tty = tty->link;
+
+	clear_bit(TTY_EXTRA_REF, &tty->flags);
+	tty_add_file(tty, file);
+	fd_install(fd, file);
+	mutex_unlock(&tty_mutex);
+	ret = fd;
+out:
+	return ret;
+
+err_install:
+	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;
+}
+
+#endif /* CONFIG_VE && CONFIG_UNIX98_PTYS */
Index: linux-pcs7.git/drivers/tty/tty_io.c
===================================================================
--- linux-pcs7.git.orig/drivers/tty/tty_io.c
+++ linux-pcs7.git/drivers/tty/tty_io.c
@@ -306,6 +306,10 @@ static int check_tty_count(struct tty_st
 	    tty->driver->subtype == PTY_TYPE_SLAVE &&
 	    tty->link && tty->link->count)
 		count++;
+#ifdef CONFIG_VE
+	if (test_bit(TTY_EXTRA_REF, &tty->flags))
+		count++;
+#endif
 	if (tty->count != count) {
 		printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
 				    "!= #fd's(%d) in %s\n",
@@ -1931,14 +1935,15 @@ static struct tty_driver *tty_lookup_dri
 	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));
+	struct ve_struct *ve = get_exec_env();
+
+	if (!ve_is_super(ve) && vtty_match(device)) {
+		driver = tty_driver_kref_get(vtty_driver(device, index));
 		*noctty = 1;
 		return driver;
 	}
 #endif
+
 	switch (device) {
 #ifdef CONFIG_VT
 	case MKDEV(TTY_MAJOR, 0): {
@@ -1952,10 +1957,8 @@ static struct tty_driver *tty_lookup_dri
 	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 +3631,6 @@ static DEVICE_ATTR(active, S_IRUGO, show
 
 #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 +3647,6 @@ void ve_tty_console_fini(struct ve_struc
 	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 +3668,9 @@ int ve_tty_console_init(struct ve_struct
 	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:
Index: linux-pcs7.git/include/linux/tty.h
===================================================================
--- linux-pcs7.git.orig/include/linux/tty.h
+++ linux-pcs7.git/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_EXTRA_REF		24	/* Carries extra reference */
+#endif
 
 #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
 
Index: linux-pcs7.git/include/linux/ve.h
===================================================================
--- linux-pcs7.git.orig/include/linux/ve.h
+++ linux-pcs7.git/include/linux/ve.h
@@ -66,13 +66,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_TTY
 	struct device		*consdev;
 #endif
@@ -209,17 +202,24 @@ extern unsigned long long ve_relative_cl
 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);
 
+#define MAX_NR_VTTY_CONSOLES		(12)
+
+#ifdef CONFIG_TTY
+extern int vtty_match(dev_t device);
+extern struct tty_driver *vtty_driver(dev_t dev, int *index);
+extern struct tty_driver *vtty_console_driver(int *index);
+extern int vtty_open_slave(envid_t veid, int idx);
+#else /* CONFIG_TTY */
+static inline int vtty_match(dev_t device) { return 0; }
+static inline struct tty_driver *vtty_driver(dev_t dev, int *index) { return NULL; }
+static inline struct tty_driver *vtty_console_driver(int *index) { return NULL; }
+static inline int vtty_open_slave(envid_t veid, int idx) { return -ENODEV; }
+#endif /* CONFIG_TTY */
+
 #else	/* CONFIG_VE */
 
 #define ve_uevent_seqnum uevent_seqnum
Index: linux-pcs7.git/kernel/ve/Makefile
===================================================================
--- linux-pcs7.git.orig/kernel/ve/Makefile
+++ linux-pcs7.git/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
 
Index: linux-pcs7.git/kernel/ve/console.c
===================================================================
--- linux-pcs7.git.orig/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_conm;
-}
-
-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_conm = 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_conm = 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);



More information about the Devel mailing list