[Devel] [RFC rh7 v2] ve/tty: vt -- Implement per VE support for virtual consoles
Cyrill Gorcunov
gorcunov at virtuozzo.com
Thu Jul 30 05:51:55 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/vzt.c and userspace parth is planted into vzt.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/vztm0 /dev/vzts0
| ...
| /dev/tty11|/dev/vztm11 /dev/vzts11
major:minor numbers for peers are allocated dynamically by the kenel,
thus if we need to connect to container's slave peer from the node
we have to
- scan node's /proc/tty/drivers and find the numbers for
"vzt_slave" driver, for example
| [root at pcs7 ~]# cat /proc/tty/drivers
| ...
| vzt_slave /dev/vzts 252 0-11 console
| vzt_master /dev/vztm 253 0-11 console
| ...
- add major:minor pairs into allowed devices, for example
| echo 'c 253:* rwm' > /sys/fs/cgroup/devices/$ctid/devices.allow
Once the bullets above is done the userspace utility may
open up slave peer and read/write data to/from. Note the
slave may be opened if only if a master peer been previously
opened.
Some details on the driver itself:
- The drivers carries per-VE tty instances in @vzt_idr map, once
VE tries to open a terminal we allocate tty map internally and
keep it intact until VE destructed, this allow us to not bind
into device namespaces (ie not rely on tty_class)
- Unlike buffered IO to unix98 driver once internal port buffer
get full we don't block write operations but zap them this is
done intentionally to behave closely to native consoles
https://jira.sw.ru/browse/PSBM-34533
https://jira.sw.ru/browse/PSBM-34532
https://jira.sw.ru/browse/PSBM-34107
https://jira.sw.ru/browse/PSBM-32686
https://jira.sw.ru/browse/PSBM-32685
N.B.: I've done some manual testing with python help
1) Run container (ctid=200)
2) Run the script on the node
| #!/bin/sh
| master=`cat /proc/tty/drivers | grep vzt_master | awk '{ print $3; }'`
| slave=`cat /proc/tty/drivers | grep vzt_slave | awk '{ print $3; }'i`
| echo "c $master:* rwm" > /sys/fs/cgroup/devices/$1/devices.allow
| echo "c $slave:* rwm" > /sys/fs/cgroup/devices/$1/devices.allow
| vzctl exec $1 mknod /dev/vzvtm5 c $master 5
| vzctl exec $1 mknod /dev/vzvts5 c $slave 5
3) In container run two instances of python where each does
a)
| >>> f=open("/dev/vzvtm5", "r+", 0)
| >>> f.write("aaa")
b)
| >>> f=open("/dev/vzvts5", "r+", 0)
| >>> f.read(3)
| >>> 'aaa'
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 | 8
include/uapi/linux/Kbuild | 1
include/uapi/linux/vzt.h | 30 ++
kernel/ve/Makefile | 5
kernel/ve/console.c | 382 ------------------------------------
kernel/ve/vzt.c | 485 ++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 536 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/vzt.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_is_super(ve) && vzt_match(device)) {
+ driver = tty_driver_kref_get(vzt_driver(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(ve))
+ console_driver = vzt_console_driver(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/vzt.h>
struct tty_driver;
struct file_system_type;
@@ -66,13 +67,6 @@ struct ve_struct {
struct binfmt_misc *binfmt_misc;
#endif
-#define VZ_VT_MAX_DEVS 12
- struct tty_driver *vz_vt_driver;
- struct tty_struct *vz_tty_vt[VZ_VT_MAX_DEVS];
-
- struct tty_struct *vz_tty_conm;
- struct tty_struct *vz_tty_cons;
-
#ifdef CONFIG_TTY
struct device *consdev;
#endif
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 += vzt.h
header-y += vfio.h
header-y += vhost.h
header-y += videodev2.h
Index: linux-pcs7.git/include/uapi/linux/vzt.h
===================================================================
--- /dev/null
+++ linux-pcs7.git/include/uapi/linux/vzt.h
@@ -0,0 +1,30 @@
+#ifndef _UAPI_LINUX_VZT_H
+#define _UAPI_LINUX_VZT_H
+
+#define MAX_NR_VZT_CONSOLES (12)
+
+#define VZT_MASTER_DRIVER_NAME "vzt_master"
+#define VZT_MASTER_NAME "vztm"
+
+#define VZT_SLAVE_DRIVER_NAME "vzt_slave"
+#define VZT_SLAVE_NAME "vzts"
+
+#ifdef __KERNEL__
+
+struct tty_driver;
+struct class;
+
+#ifdef CONFIG_TTY
+extern int vzt_match(dev_t device);
+extern struct tty_driver *vzt_driver(dev_t dev, int *index);
+extern struct tty_driver *vzt_console_driver(int *index);
+
+#else /* CONFIG_TTY */
+
+static inline int vzt_match(dev_t device) { return 0; }
+
+#endif /* CONFIG_TTY */
+
+#endif /* __KERNEL__ */
+
+#endif /* _UAPI_LINUX_VZT_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) += vzt.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/vzt.c
===================================================================
--- /dev/null
+++ linux-pcs7.git/kernel/ve/vzt.c
@@ -0,0 +1,485 @@
+#define pr_fmt(fmt) "vzt: " 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 struct tty_driver *vztm_driver;
+static struct tty_driver *vzts_driver;
+static DEFINE_MUTEX(vzt_mutex);
+static DEFINE_IDR(vzt_idr);
+
+#define driver_is_master(d) ((d) == vztm_driver)
+#define driver_is_slave(d) ((d) == vzts_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
+
+typedef struct {
+ envid_t veid;
+ struct tty_struct *master[MAX_NR_VZT_CONSOLES];
+ struct tty_struct *slave[MAX_NR_VZT_CONSOLES];
+} tty_map_t;
+
+static tty_map_t *tty_map_lookup(envid_t veid)
+{
+ return idr_find(&vzt_idr, veid);
+}
+
+static void tty_map_remove(tty_map_t *map)
+{
+ if (map)
+ idr_remove(&vzt_idr, map->veid);
+ kfree(map);
+}
+
+static void tty_map_zap(tty_map_t *map, int index)
+{
+ map->master[index] = map->slave[index] = NULL;
+}
+
+static tty_map_t *tty_map_alloc(envid_t veid)
+{
+ tty_map_t *map = kzalloc(sizeof(*map), GFP_KERNEL);
+
+ if (map) {
+ map->veid = veid;
+ veid = idr_alloc(&vzt_idr, map, veid, veid + 1, GFP_KERNEL);
+ if (veid < 0) {
+ kfree(map);
+ return ERR_PTR(veid);
+ }
+ idr_replace(&vzt_idr, map, veid);
+ } else
+ return ERR_PTR(-ENOMEM);
+ return map;
+}
+
+static inline int tty_is_exiting(struct tty_struct *tty)
+{
+ return test_bit(TTY_CLOSING, &tty->flags) ||
+ test_bit(TTY_HUPPING, &tty->flags) ||
+ test_bit(TTY_LDISC_CHANGING, &tty->flags) ||
+ tty->count < 1;
+}
+
+static struct tty_driver *vzt_other(struct tty_driver *driver)
+{
+ return driver_is_master(driver) ? vzts_driver : vztm_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 *vzt_tty_lookup(struct tty_driver *driver,
+ struct inode *inode, int idx)
+{
+ tty_map_t *map = tty_map_lookup(get_exec_env()->veid);
+ struct tty_struct *tty;
+
+ if (idx < 0 || idx >= MAX_NR_VZT_CONSOLES)
+ return ERR_PTR(-EIO);
+
+ if (driver_is_slave(driver)) {
+ struct tty_struct *peer = map ? map->master[idx] : NULL;
+ struct tty_struct *me = map ? map->slave[idx] : NULL;
+
+ tty = (peer && !tty_is_exiting(peer)) ?
+ (me && !tty_is_exiting(me) ?
+ me : ERR_PTR(-ENXIO)) :
+ ERR_PTR(-ENXIO);
+ } else {
+ tty = map ? map->master[idx] : NULL;
+ if (tty && tty_is_exiting(tty))
+ tty = ERR_PTR(-ENXIO);
+ }
+
+ return tty;
+}
+
+static int vzt_tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ tty_map_t *map = tty->driver_data;
+ int ret = tty_init_termios(tty);
+ if (ret)
+ return ret;
+
+ tty_driver_kref_get(driver);
+ tty->count++;
+ if (driver_is_master(driver))
+ map->master[tty->index] = tty;
+ else
+ map->slave[tty->index] = tty;
+ return 0;
+}
+
+static struct tty_struct *vzt_install_slave(struct tty_driver *driver_master, struct tty_struct *tty_master)
+{
+ struct tty_driver *driver = vzt_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);
+ }
+ tty->driver_data = tty_master->driver_data;
+ WARN_ON(vzt_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 vzt_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ envid_t veid = get_exec_env()->veid;
+ struct tty_struct *peer;
+ tty_map_t *map;
+
+ map = tty_map_lookup(veid);
+ if (!map) {
+ map = tty_map_alloc(veid);
+ if (IS_ERR_OR_NULL(tty))
+ return PTR_ERR(map);
+ }
+ tty->driver_data = map;
+
+ tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
+ if (!tty->port)
+ return -ENOMEM;
+
+ peer = vzt_install_slave(driver, tty);
+ if (IS_ERR(peer)) {
+ tty_map_zap(map, tty->index);
+ kfree(tty->port);
+ return PTR_ERR(peer);
+ }
+ WARN_ON(vzt_tty_standard_install(driver, tty));
+ tty_port_init(tty->port);
+ tty->port->itty = tty;
+
+ tty->link = peer;
+ peer->link = tty;
+
+ tty_kref_get(peer);
+ return 0;
+}
+
+static void vzt_tty_remove(struct tty_driver *driver, struct tty_struct *tty)
+{
+ tty_map_t *map = tty->driver_data;
+
+ if (driver_is_master(driver))
+ map->master[tty->index] = NULL;
+ else
+ map->slave[tty->index] = NULL;
+}
+
+static int vzt_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 vzt_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 vzt_tty_sleep_fn(void *flags)
+{
+ schedule();
+ return 0;
+}
+
+static void vzt_tty_shutdown(struct tty_struct *tty)
+{
+ mutex_lock(&vzt_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(vzt_other(tty->driver), tty);
+
+ mutex_unlock(&vzt_mutex);
+}
+
+static void vzt_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,
+ vzt_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 vzt_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 vzt_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 vzt_tty_set_termios(struct tty_struct *tty, struct ktermios * old)
+{
+ tty->termios.c_cflag &= ~(CSIZE | PARENB);
+ tty->termios.c_cflag |= (CS8 | CREAD);
+}
+
+static void vzt_tty_unthrottle(struct tty_struct *tty)
+{
+ tty_wakeup(tty->link);
+ set_bit(TTY_THROTTLED, &tty->flags);
+}
+
+static const struct tty_operations vzt_tty_fops = {
+ .lookup = vzt_tty_lookup,
+ .install = vzt_tty_install,
+ .open = vzt_tty_open,
+ .close = vzt_tty_close,
+ .shutdown = vzt_tty_shutdown,
+ .cleanup = vzt_tty_cleanup,
+ .write = vzt_tty_write,
+ .write_room = vzt_tty_write_room,
+ .set_termios = vzt_tty_set_termios,
+ .unthrottle = vzt_tty_unthrottle,
+ .remove = vzt_tty_remove,
+};
+
+struct tty_driver *vzt_console_driver(int *index)
+{
+ *index = 0;
+ return vztm_driver;
+}
+EXPORT_SYMBOL_GPL(vzt_console_driver);
+
+int vzt_match(dev_t device)
+{
+ return MAJOR(device) == TTY_MAJOR && MINOR(device) < MAX_NR_VZT_CONSOLES;
+}
+EXPORT_SYMBOL_GPL(vzt_match);
+
+struct tty_driver *vzt_driver(dev_t dev, int *index)
+{
+ BUG_ON(MINOR(dev) >= MAX_NR_VZT_CONSOLES);
+
+ *index = MINOR(dev);
+ return vztm_driver;
+}
+EXPORT_SYMBOL_GPL(vzt_driver);
+
+static void ve_vzt_fini(void *data)
+{
+ struct ve_struct *ve = data;
+ tty_map_remove(tty_map_lookup(ve->veid));
+}
+
+static struct ve_hook vzt_hook = {
+ .fini = ve_vzt_fini,
+ .priority = HOOK_PRIO_DEFAULT,
+ .owner = THIS_MODULE,
+};
+
+static int __init vzt_module_init(void)
+{
+#define TTY_DRIVER_ALLOC_FLAGS \
+ (TTY_DRIVER_REAL_RAW | \
+ TTY_DRIVER_RESET_TERMIOS | \
+ TTY_DRIVER_DEVPTS_MEM)
+
+ int ret, i;
+
+ vztm_driver = tty_alloc_driver(MAX_NR_VZT_CONSOLES, TTY_DRIVER_ALLOC_FLAGS);
+ if (IS_ERR(vztm_driver)) {
+ ret = PTR_ERR(vztm_driver);
+ pr_err("Can't allocate vt master driver\n");
+ return ret;
+ }
+
+ vzts_driver = tty_alloc_driver(MAX_NR_VZT_CONSOLES, TTY_DRIVER_ALLOC_FLAGS);
+ if (IS_ERR(vzts_driver)) {
+ ret = PTR_ERR(vzts_driver);
+ pr_err("Can't allocate vt slave driver\n");
+ goto err_put_master;
+ }
+
+ vztm_driver->driver_name = VZT_MASTER_DRIVER_NAME;
+ vztm_driver->name = VZT_MASTER_NAME;
+ vztm_driver->name_base = 0;
+ vztm_driver->major = 0;
+ vztm_driver->minor_start = 0;
+ vztm_driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ vztm_driver->init_termios = tty_std_termios;
+ vztm_driver->init_termios.c_iflag = 0;
+ vztm_driver->init_termios.c_oflag = 0;
+ vztm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+ vztm_driver->init_termios.c_lflag = 0;
+ vztm_driver->init_termios.c_ispeed = 38400;
+ vztm_driver->init_termios.c_ospeed = 38400;
+ tty_set_operations(vztm_driver, &vzt_tty_fops);
+
+ vzts_driver->driver_name = VZT_SLAVE_DRIVER_NAME;
+ vzts_driver->name = VZT_SLAVE_NAME;
+ vzts_driver->name_base = 0;
+ vzts_driver->major = 0;
+ vzts_driver->minor_start = 0;
+ vzts_driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ vzts_driver->init_termios = tty_std_termios;
+ vzts_driver->init_termios.c_iflag = 0;
+ vzts_driver->init_termios.c_oflag = 0;
+ vzts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+ vzts_driver->init_termios.c_lflag = 0;
+ vzts_driver->init_termios.c_ispeed = 38400;
+ vzts_driver->init_termios.c_ospeed = 38400;
+ tty_set_operations(vzts_driver, &vzt_tty_fops);
+
+ ret = tty_register_driver(vztm_driver);
+ if (ret) {
+ pr_err("Can't register vt master driver\n");
+ goto err_put_slave;
+ }
+
+ ret = tty_register_driver(vzts_driver);
+ if (ret) {
+ pr_err("Can't register vt master driver\n");
+ goto err_unregister_master;
+ }
+
+ ve_hook_register(VE_SS_CHAIN, &vzt_hook);
+ return 0;
+
+err_unregister_slave:
+ tty_unregister_driver(vzts_driver);
+err_unregister_master:
+ tty_unregister_driver(vztm_driver);
+err_put_slave:
+ put_tty_driver(vzts_driver);
+err_put_master:
+ put_tty_driver(vztm_driver);
+ return ret;
+#undef TTY_DRIVER_ALLOC_FLAGS
+}
+module_init(vzt_module_init);
+
+static void __exit vzt_module_fini(void)
+{
+ ve_hook_unregister(&vzt_hook);
+ tty_unregister_driver(vzts_driver);
+ tty_unregister_driver(vztm_driver);
+ put_tty_driver(vzts_driver);
+ put_tty_driver(vztm_driver);
+}
+module_exit(vzt_module_fini);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Virtual terminals emulation for VE environment");
More information about the Devel
mailing list