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

Andrew Vagin avagin at gmail.com
Mon Jul 20 06:35:17 PDT 2015


On Tue, Jul 14, 2015 at 04:53:54PM +0300, Cyrill Gorcunov wrote:
> 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
> 

Reviewed-by: Andrew Vagin <avagin at odin.com>

> 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