[Devel] [PATCH rh7] ve/tty: vt -- Implement per VE support for virtual consoles

Cyrill Gorcunov gorcunov at virtuozzo.com
Tue Jul 14 06:53:54 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 kernel/ve/ve-vt.c and userspace parth is planted into
ve-vt.h file.

Now we support up to MAX_NR_VE_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/vzvts0
 ...
 /dev/tty11			/dev/vzvts11

major:minor numbers for slave peers are allocated by the
kenel dynamically 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
   "vt_slave" driver, for example

	[root at localhost ~]# cat /proc/tty/drivers
	...
	vt_slave             /dev/vts      253 0-11 console
	vt_master            /dev/vtm        0 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 onlt a master peer been previously
opened.

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

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/tty_io.c       |   30 +--
 include/linux/tty.h        |    3 
 include/linux/ve.h         |   12 -
 include/uapi/linux/Kbuild  |    1 
 include/uapi/linux/ve-vt.h |   40 ++++
 kernel/ve/Makefile         |    5 
 kernel/ve/console.c        |  382 --------------------------------------
 kernel/ve/ve-vt.c          |  445 +++++++++++++++++++++++++++++++++++++++++++++
 kernel/ve/ve.c             |   10 -
 9 files changed, 520 insertions(+), 408 deletions(-)

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
@@ -104,6 +104,7 @@
 #include <linux/kmod.h>
 #include <linux/nsproxy.h>
 #include <linux/ve.h>
+#include <uapi/linux/ve-vt.h>
 
 #undef TTY_DEBUG_HANGUP
 
@@ -306,6 +307,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",
@@ -1930,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_vt_match(ve, device)) {
+		driver = tty_driver_kref_get(ve_vt_driver(ve, device, index));
 		*noctty = 1;
 		return driver;
 	}
 #endif
+
 	switch (device) {
 #ifdef CONFIG_VT
 	case MKDEV(TTY_MAJOR, 0): {
@@ -1951,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(get_exec_env()))
+			console_driver = ve_console_driver(get_exec_env(), index);
 #endif
 		if (console_driver) {
 			driver = tty_driver_kref_get(console_driver);
@@ -3617,9 +3621,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();
@@ -3636,7 +3637,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)
@@ -3658,15 +3658,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
@@ -321,6 +321,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
@@ -25,6 +25,7 @@
 #include <net/inet_frag.h>
 #include <linux/cgroup.h>
 #include <linux/kmapset.h>
+#include <uapi/linux/ve-vt.h>
 
 struct tty_driver;
 struct file_system_type;
@@ -66,12 +67,11 @@ 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 tty_driver	*vtm_driver;
+	struct tty_driver	*vts_driver;
+	struct mutex		vt_mutex;
+#endif
 
 #ifdef CONFIG_LEGACY_PTYS
 	struct tty_driver	*pty_driver, *pty_slave_driver;
Index: linux-pcs7.git/include/uapi/linux/Kbuild
===================================================================
--- linux-pcs7.git.orig/include/uapi/linux/Kbuild
+++ linux-pcs7.git/include/uapi/linux/Kbuild
@@ -401,6 +401,7 @@ header-y += v4l2-dv-timings.h
 header-y += v4l2-mediabus.h
 header-y += v4l2-subdev.h
 header-y += veth.h
+header-y += ve-vt.h
 header-y += vfio.h
 header-y += vhost.h
 header-y += videodev2.h
Index: linux-pcs7.git/include/uapi/linux/ve-vt.h
===================================================================
--- /dev/null
+++ linux-pcs7.git/include/uapi/linux/ve-vt.h
@@ -0,0 +1,40 @@
+#ifndef _UAPI_LINUX_VE_VT_H
+#define _UAPI_LINUX_VE_VT_H
+
+#define MAX_NR_VE_CONSOLES		(12)
+#define VE_VT_SLAVE_NAME_PREFIX		"vzvts"
+#define VE_VT_SLAVE_FMT			VE_VT_SLAVE_NAME_PREFIX"%i"
+
+#define VE_VT_MASTER_DRIVER_NAME	"vt_master"
+#define VE_VT_MASTER_NAME		"vtm"
+
+#define VE_VT_SLAVE_DRIVER_NAME		"vt_slave"
+#define VE_VT_SLAVE_NAME		"vts"
+
+#ifdef __KERNEL__
+
+struct tty_driver;
+struct ve_struct;
+struct class;
+
+#ifdef CONFIG_TTY
+extern struct class *tty_class;
+
+extern int ve_vt_match(struct ve_struct *ve, dev_t device);
+extern struct tty_driver *ve_vt_driver(struct ve_struct *ve, dev_t dev, int *index);
+extern struct tty_driver *ve_console_driver(struct ve_struct *ve, int *index);
+
+extern int ve_vt_init(struct ve_struct *ve);
+extern void ve_vt_fini(struct ve_struct *ve);
+
+#else /* CONFIG_TTY */
+
+static inline int ve_vt_match(struct ve_struct *ve, dev_t device) { return 0; }
+static inline int ve_vt_init(struct ve_struct *ve) { return 0; }
+static inline void ve_vt_fini(struct ve_struct *ve) { }
+
+#endif /* CONFIG_TTY */
+
+#endif /* __KERNEL__ */
+
+#endif /* _UAPI_LINUX_VE_VT_H */
Index: linux-pcs7.git/kernel/ve/Makefile
===================================================================
--- linux-pcs7.git.orig/kernel/ve/Makefile
+++ linux-pcs7.git/kernel/ve/Makefile
@@ -4,7 +4,10 @@
 # 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
+ifdef CONFIG_TTY
+obj-$(CONFIG_VE) += ve-vt.o
+endif
 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);
Index: linux-pcs7.git/kernel/ve/ve-vt.c
===================================================================
--- /dev/null
+++ linux-pcs7.git/kernel/ve/ve-vt.c
@@ -0,0 +1,445 @@
+#define pr_fmt(fmt) "ve-vt: " 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>
+
+static const char vt_dev_chrdev_name[] = "vzvts";
+static int vt_dev_major;
+
+#define driver_is_master(d)	((d) == (d)->ve->vtm_driver)
+#define driver_is_slave(d)	((d) == (d)->ve->vts_driver)
+#define tty_is_master(t)	driver_is_master((t)->driver)
+#define tty_is_slave(t)		driver_is_slave((t)->driver)
+
+/*
+ * Reuse TTY_PTY_LOCK for own needs because we don't
+ * provide locking for these terminals.
+ */
+#define TTY_VT_OPEN	TTY_PTY_LOCK
+
+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_driver *vt_other(struct tty_driver *driver)
+{
+	return driver_is_master(driver) ? driver->ve->vts_driver : driver->ve->vtm_driver;
+}
+
+/*
+ * Creating new tty via lookup nil return is allowed for master
+ * peer only, the slave one should be opened iif master is there.
+ */
+static struct tty_struct *vt_tty_lookup(struct tty_driver *driver,
+					struct inode *inode, int idx)
+{
+	struct tty_struct *tty;
+
+	if (idx < 0 || idx >= MAX_NR_VE_CONSOLES)
+		return ERR_PTR(-EIO);
+
+	if (driver_is_slave(driver)) {
+		struct tty_struct *peer = vt_other(driver)->ttys[idx];
+		tty = (peer && !tty_is_exiting(peer)) ?
+			(driver->ttys[idx] && !tty_is_exiting(driver->ttys[idx]) ?
+			 driver->ttys[idx] : ERR_PTR(-ENXIO)) :
+			ERR_PTR(-ENXIO);
+	} else {
+		tty = driver->ttys[idx];
+		if (tty && tty_is_exiting(tty))
+			tty = ERR_PTR(-ENXIO);
+	}
+
+	return tty;
+}
+
+static struct tty_struct *vt_install_slave(struct tty_driver *driver_master, struct tty_struct *tty_master)
+{
+	struct tty_driver *driver = vt_other(driver_master);
+	int index = tty_master->index;
+	struct tty_struct *tty;
+
+	tty = alloc_tty_struct();
+	if (!tty)
+		return ERR_PTR(-ENOMEM);
+	initialize_tty_struct(tty, 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);
+	}
+	WARN_ON(tty_standard_install(driver, tty));
+	tty_port_init(tty->port);
+	tty->port->itty = tty;
+
+	set_bit(TTY_EXTRA_REF, &tty->flags);
+	return tty;
+}
+
+static int vt_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct tty_struct *peer;
+
+	tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
+	if (!tty->port)
+		return -ENOMEM;
+
+	peer = vt_install_slave(driver, tty);
+	if (IS_ERR(peer)) {
+		kfree(tty->port);
+		return PTR_ERR(peer);
+	}
+	WARN_ON(tty_standard_install(driver, tty));
+	tty_port_init(tty->port);
+
+	tty->link = peer;
+	peer->link = tty;
+
+	tty_kref_get(peer);
+	return 0;
+}
+
+static int vt_tty_open(struct tty_struct *tty, struct file *filp)
+{
+	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+		return -EIO;
+
+	if (tty_is_slave(tty))
+		set_bit(TTY_VT_OPEN, &tty->link->flags);
+
+	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);
+	return 0;
+}
+
+static void vt_tty_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);
+
+	if (tty_is_master(tty)) {
+		clear_bit(TTY_EXTRA_REF, &peer->flags);
+		peer->count--;
+
+		tty_unlock(tty);
+		tty_vhangup(peer);
+		tty_lock(tty);
+	}
+}
+
+static int vt_tty_sleep_fn(void *flags)
+{
+	schedule();
+	return 0;
+}
+
+static void vt_tty_shutdown(struct tty_struct *tty)
+{
+	mutex_lock(&tty->driver->ve->vt_mutex);
+
+	if (tty_is_slave(tty)) {
+		if (!test_bit(TTY_VT_OPEN, &tty->flags)) {
+			set_bit(TTY_VT_OPEN, &tty->flags);
+			tty_kref_put(tty);
+		}
+	} else {
+		if (!test_bit(TTY_VT_OPEN, &tty->link->flags)) {
+			set_bit(TTY_VT_OPEN, &tty->link->flags);
+			tty_kref_put(tty->link);
+		}
+	}
+
+	tty->port->itty = NULL;
+	tty->link->port->itty = NULL;
+
+	tty_driver_remove_tty(tty->driver, tty);
+	tty_driver_remove_tty(vt_other(tty->driver), tty);
+
+	mutex_unlock(&tty->driver->ve->vt_mutex);
+}
+
+static void vt_tty_cleanup(struct tty_struct *tty)
+{
+	if (tty_is_slave(tty)) {
+		smp_mb__before_clear_bit();
+		clear_bit(TTY_VT_OPEN, &tty->link->flags);
+		smp_mb__after_clear_bit();
+		wake_up_bit(&tty->link->flags, TTY_VT_OPEN);
+
+		tty_free_termios(tty);
+		cancel_work_sync(&tty->port->buf.work);
+	} else {
+		wait_on_bit(&tty->flags, TTY_VT_OPEN,
+			    vt_tty_sleep_fn, TASK_KILLABLE);
+
+		tty_free_termios(tty);
+		cancel_work_sync(&tty->port->buf.work);
+	}
+
+	WARN_ON_ONCE(test_bit(TTY_LDISC, &tty->flags));
+	WARN_ON_ONCE(!test_bit(TTY_LDISC_HALTED, &tty->flags));
+
+	tty_port_put(tty->port);
+}
+
+static int vt_tty_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
+			tty_perform_flush(peer, TCIFLUSH);
+	}
+
+	return count;
+}
+
+static int vt_tty_write_room(struct tty_struct *tty)
+{
+	struct tty_struct *peer = tty->link;
+	int n;
+
+	if (tty->stopped)
+		return 0;
+
+	if (peer->count < tty_is_master(tty) ? 1 : 2)
+		return TTY_BUFFER_PAGE;
+
+	n = TTY_BUFFER_PAGE - peer->port->buf.memory_used;
+	return n < 0 ? 0 : n;
+}
+
+static void vt_tty_set_termios(struct tty_struct *tty, struct ktermios * old)
+{
+	tty->termios.c_cflag &= ~(CSIZE | PARENB);
+	tty->termios.c_cflag |= (CS8 | CREAD);
+}
+
+static void vt_tty_unthrottle(struct tty_struct *tty)
+{
+	tty_wakeup(tty->link);
+	set_bit(TTY_THROTTLED, &tty->flags);
+}
+
+static const struct tty_operations vt_tty_fops = {
+	.lookup		= vt_tty_lookup,
+	.install	= vt_tty_install,
+	.open		= vt_tty_open,
+	.close		= vt_tty_close,
+	.shutdown	= vt_tty_shutdown,
+	.cleanup	= vt_tty_cleanup,
+	.write		= vt_tty_write,
+	.write_room	= vt_tty_write_room,
+	.set_termios	= vt_tty_set_termios,
+	.unthrottle	= vt_tty_unthrottle,
+};
+
+int ve_vt_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 *vtm_driver;
+	struct tty_driver *vts_driver;
+	int ret, i;
+
+	ve->vtm_driver = NULL;
+	ve->vts_driver = NULL;
+	mutex_init(&ve->vt_mutex);
+
+	if (unlikely(ve_is_super(ve)))
+		return 0;
+
+	vtm_driver = tty_alloc_driver(MAX_NR_VE_CONSOLES, TTY_DRIVER_ALLOC_FLAGS);
+	if (IS_ERR(vtm_driver)) {
+		ret = PTR_ERR(vtm_driver);
+		pr_err("Can't allocate vt master driver\n");
+		return ret;
+	}
+
+	vts_driver = tty_alloc_driver(MAX_NR_VE_CONSOLES, TTY_DRIVER_ALLOC_FLAGS);
+	if (IS_ERR(vts_driver)) {
+		ret = PTR_ERR(vts_driver);
+		pr_err("Can't allocate vt slave driver\n");
+		goto err_put_master;
+	}
+
+	vtm_driver->driver_name		= VE_VT_MASTER_DRIVER_NAME;
+	vtm_driver->name		= VE_VT_MASTER_NAME;
+	vtm_driver->name_base		= 1;
+	vtm_driver->major		= 0;
+	vtm_driver->minor_start		= 0;
+	vtm_driver->type		= TTY_DRIVER_TYPE_CONSOLE;
+	vtm_driver->init_termios	= tty_std_termios;
+	vtm_driver->ve			= ve;
+	tty_set_operations(vtm_driver, &vt_tty_fops);
+
+	vtm_driver->init_termios.c_iflag = 0;
+	vtm_driver->init_termios.c_oflag = 0;
+	vtm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+	vtm_driver->init_termios.c_lflag = 0;
+
+	vts_driver->driver_name		= VE_VT_SLAVE_DRIVER_NAME;
+	vts_driver->name		= VE_VT_SLAVE_NAME;
+	vts_driver->name_base		= 1;
+	vts_driver->major		= vt_dev_major;
+	vts_driver->minor_start		= 0;
+	vts_driver->type		= TTY_DRIVER_TYPE_CONSOLE;
+	vts_driver->init_termios	= tty_std_termios;
+	vts_driver->ve			= ve;
+	tty_set_operations(vts_driver, &vt_tty_fops);
+
+	vts_driver->init_termios.c_iflag = 0;
+	vts_driver->init_termios.c_oflag = 0;
+	vts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+	vts_driver->init_termios.c_lflag = 0;
+
+	ret = tty_register_driver(vtm_driver);
+	if (ret) {
+		pr_err("Can't register vt master driver\n");
+		goto err_put_slave;
+	}
+
+	ret = tty_register_driver(vts_driver);
+	if (ret) {
+		pr_err("Can't register vt master driver\n");
+		goto err_unregister_master;
+	}
+
+	for (i = 0; i < MAX_NR_VE_CONSOLES; i++) {
+		dev_t dev = MKDEV(vt_dev_major, i);
+		struct device *d;
+
+		d = device_create(tty_class, NULL, dev, ve, VE_VT_SLAVE_FMT, i);
+		if (IS_ERR(d)) {
+			for (--i; i >= 0; i--) {
+				dev = MKDEV(vt_dev_major, i);
+				device_destroy_namespace(tty_class, dev, ve);
+			}
+			ret = PTR_ERR(d);
+			pr_err("Can't register vt master devices\n");
+			goto err_unregister_slave;
+		}
+	}
+
+	ve->vtm_driver = vtm_driver;
+	ve->vts_driver = vts_driver;
+	mutex_init(&ve->vt_mutex);
+	return 0;
+
+err_unregister_slave:
+	tty_unregister_driver(ve->vts_driver);
+err_unregister_master:
+	tty_unregister_driver(ve->vtm_driver);
+err_put_slave:
+	put_tty_driver(ve->vts_driver);
+err_put_master:
+	put_tty_driver(ve->vtm_driver);
+	return ret;
+#undef TTY_DRIVER_ALLOC_FLAGS
+}
+EXPORT_SYMBOL_GPL(ve_vt_init);
+
+void ve_vt_fini(struct ve_struct *ve)
+{
+	int i;
+
+	if (ve_is_super(ve))
+		return;
+
+	for (i = MAX_NR_VE_CONSOLES - 1; i >= 0; i--) {
+		dev_t dev = MKDEV(vt_dev_major, i);
+		device_destroy_namespace(tty_class, dev, ve);
+	}
+
+	tty_unregister_driver(ve->vts_driver);
+	tty_unregister_driver(ve->vtm_driver);
+	put_tty_driver(ve->vts_driver);
+	put_tty_driver(ve->vtm_driver);
+}
+EXPORT_SYMBOL_GPL(ve_vt_fini);
+
+struct tty_driver *ve_console_driver(struct ve_struct *ve, int *index)
+{
+	*index = 0;
+	return ve->vtm_driver;
+}
+EXPORT_SYMBOL_GPL(ve_console_driver);
+
+int ve_vt_match(struct ve_struct *ve, dev_t device)
+{
+	return !ve_is_super(ve)			&&
+		MAJOR(device) == TTY_MAJOR	&&
+		MINOR(device) < MAX_NR_VE_CONSOLES;
+}
+EXPORT_SYMBOL_GPL(ve_vt_match);
+
+struct tty_driver *ve_vt_driver(struct ve_struct *ve, dev_t dev, int *index)
+{
+	BUG_ON(MINOR(dev) >= MAX_NR_VE_CONSOLES);
+
+	*index = MINOR(dev);
+	return ve->vtm_driver;
+}
+EXPORT_SYMBOL_GPL(ve_vt_driver);
+
+static int __init ve_vt_module_init(void)
+{
+	static struct file_operations tty_fops;
+
+	tty_default_fops(&tty_fops);
+
+	vt_dev_major = __register_chrdev(0, 0, MAX_NR_VE_CONSOLES, vt_dev_chrdev_name, &tty_fops);
+	if (vt_dev_major < 0) {
+		pr_err("Can't register chardev region\n");
+		return vt_dev_major;
+	}
+	return 0;
+}
+module_init(ve_vt_module_init);
+
+static void __exit ve_vt_module_fini(void)
+{
+	__unregister_chrdev(vt_dev_major, 0, MAX_NR_VE_CONSOLES, vt_dev_chrdev_name);
+}
+module_exit(ve_vt_module_fini);
Index: linux-pcs7.git/kernel/ve/ve.c
===================================================================
--- linux-pcs7.git.orig/kernel/ve/ve.c
+++ linux-pcs7.git/kernel/ve/ve.c
@@ -43,6 +43,7 @@
 #include <linux/ctype.h>
 
 #include <uapi/linux/vzcalluser.h>
+#include <uapi/linux/ve-vt.h>
 #include <linux/venet.h>
 #include <linux/vziptable_defs.h>
 
@@ -510,10 +511,14 @@ int ve_start_container(struct ve_struct
 	if (err)
 		goto err_tty_console;
 
-	err = ve_init_mem_class(ve);
+	err = ve_vt_init(ve);
 	if (err)
 		goto err_mem_class;
 
+	err = ve_init_mem_class(ve);
+	if (err)
+		goto err_vt;
+
 	err = ve_hook_iterate_init(VE_SS_CHAIN, ve);
 	if (err < 0)
 		goto err_iterate;
@@ -528,6 +533,8 @@ int ve_start_container(struct ve_struct
 
 err_iterate:
 	ve_mem_class_fini(ve);
+err_vt:
+	ve_vt_fini(ve);
 err_mem_class:
 	ve_tty_console_fini(ve);
 err_tty_console:
@@ -566,6 +573,7 @@ void ve_stop_ns(struct pid_namespace *pi
 	 */
 	ve->is_running = 0;
 
+	ve_vt_fini(ve);
 	ve_tty_console_fini(ve);
 	ve_unix98_pty_fini(ve);
 	ve_legacy_pty_fini(ve);



More information about the Devel mailing list