[Devel] [PATCH rh7] ve/tty: vt -- Implement per VE support for virtual consoles
Konstantin Khorenko
khorenko at virtuozzo.com
Thu Jul 16 06:14:47 PDT 2015
Andrey, please review.
--
Best regards,
Konstantin Khorenko,
Virtuozzo Linux Kernel Team
On 07/14/2015 04:53 PM, 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
>
> 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