[Devel] [PATCH RHEL7 COMMIT] ve/tty: console -- Enhance container console to support per VE ops

Konstantin Khorenko khorenko at virtuozzo.com
Mon Jun 22 02:28:35 PDT 2015


The commit is pushed to "branch-rh7-3.10.0-123.1.2-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-123.1.2.vz7.5.15
------>
commit 7b2cd88a773a638f7a0f814da31237be00f91b15
Author: Cyrill Gorcunov <gorcunov at virtuozzo.com>
Date:   Mon Jun 22 13:28:35 2015 +0400

    ve/tty: console -- Enhance container console to support per VE ops
    
    Currently in dummy console (84364c191199c50f1bc6fbf7d5f121750c5a1f66)
    there is a single character device for all containers, and due to
    simplicity sake the driver doesn't support multiple VE working at
    same time, which lead that agetty service from container poke same
    console from various VE making it stuck eventually.
    
    So I've had to enhance console to generate one device per VE
    so that every container grab own instance of a virtual console.
    
    Note the console is still dump and doesn't provide a way to
    fetch data written yet, this gonna be addressed in next
    patches. Meanwhile this should prevent containers from
    stucks.
    
    https://jira.sw.ru/browse/PSBM-34357
    
    Signed-off-by: Cyrill Gorcunov <gorcunov at virtuozzo.com>
    
    CC: Andrey Vagin <avagin at virtuozzo.com>
    CC: Vladimir Davydov <vdavydov at virtuozzo.com>
    CC: Konstantin Khorenko <khorenko at virtuozzo.com>
    CC: Pavel Emelyanov <xemul at virtuozzo.com>
---
 drivers/tty/tty_io.c |  11 +++
 include/linux/ve.h   |   4 +-
 kernel/ve/console.c  | 262 +++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 202 insertions(+), 75 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 8fac760..eb366b4 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -3605,6 +3605,10 @@ static ssize_t show_cons_active(struct device *dev,
 static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL);
 
 #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();
@@ -3621,6 +3625,7 @@ void ve_tty_console_fini(struct ve_struct *ve)
 	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)
@@ -3642,9 +3647,15 @@ int ve_tty_console_init(struct ve_struct *ve)
 	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:
diff --git a/include/linux/ve.h b/include/linux/ve.h
index e3fc636..6465e9c 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -66,8 +66,8 @@ struct ve_struct {
 	struct binfmt_misc	*binfmt_misc;
 #endif
 
-#define	MAX_NR_VTTY		12
-	struct tty_struct	*vtty[MAX_NR_VTTY];
+	struct tty_struct	*vz_tty_conm;
+	struct tty_struct	*vz_tty_cons;
 
 #ifdef CONFIG_LEGACY_PTYS
 	struct tty_driver	*pty_driver, *pty_slave_driver;
diff --git a/kernel/ve/console.c b/kernel/ve/console.c
index 9924922..4d2ed74 100644
--- a/kernel/ve/console.c
+++ b/kernel/ve/console.c
@@ -2,71 +2,96 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
 #include <linux/init.h>
 
-#include <linux/console.h>
-#include <linux/anon_inodes.h>
-#include <linux/file.h>
 #include <linux/tty.h>
+#include <linux/tty_flip.h>
+
 #include <linux/ve.h>
 
-static struct tty_driver *vz_con_driver;
+#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;
+
+static char *vzcon_devnode(struct device *dev, umode_t *mode)
+{
+	if (mode)
+		*mode = 0600;
+	return NULL;
+}
 
-struct vz_tty_priv {
-	struct tty_port		port;
-	struct ve_struct	*owner_ve;
+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)
 {
-	static const struct tty_port_operations vz_tty_port_ops;
-	struct vz_tty_priv *priv;
-	int ret;
+	struct ve_struct *ve = get_exec_env();
 
-	BUG_ON(tty->index != 0);
+	BUG_ON(driver != vz_conm_driver);
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
+	tty->port = kzalloc(sizeof(*tty->port), GFP_KERNEL);
+	if (!tty->port)
 		return -ENOMEM;
+	tty_port_init(tty->port);
+	tty->termios = driver->init_termios;
 
-	priv->owner_ve = get_exec_env();
-	tty_port_init(&priv->port);
-	priv->port.ops = &vz_tty_port_ops;
-	tty->driver_data = priv;
-
-	ret = tty_port_install(&priv->port, driver, tty);
-	if (ret) {
-		pr_err("Can't install tty port: %d\n", ret);
-		goto err;
-	}
+	ve->vz_tty_conm = tty;
 
+	tty_driver_kref_get(driver);
+	tty->count++;
 	return 0;
-err:
-	tty_port_destroy(&priv->port);
-	kfree(priv);
-	return ret;
+}
+
+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)
 {
-	struct vz_tty_priv *priv = tty->driver_data;
-	return tty_port_open(&priv->port, tty, filp);
+	set_bit(TTY_THROTTLED, &tty->flags);
+	return 0;
 }
 
 static void vz_tty_close(struct tty_struct *tty, struct file *filp)
 {
-	struct vz_tty_priv *priv = tty->driver_data;
-	tty_port_close(&priv->port, tty, filp);
 }
 
-static void vz_tty_cleanup(struct tty_struct *tty)
+static void vz_tty_shutdown(struct tty_struct *tty)
 {
-	struct vz_tty_priv *priv = tty->driver_data;
+}
 
-	tty->driver_data = NULL;
-	priv->owner_ve = NULL;
-	tty_port_destroy(&priv->port);
-	kfree(priv);
+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)
@@ -79,68 +104,159 @@ static int vz_tty_write_room(struct tty_struct *tty)
 	return 4096;
 }
 
-static void vz_tty_hangup(struct tty_struct *tty)
+static void vz_tty_unthrottle(struct tty_struct *tty)
 {
-	struct vz_tty_priv *priv = tty->driver_data;
-	tty_port_hangup(&priv->port);
+	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,
-	.hangup		= vz_tty_hangup,
+	.unthrottle	= vz_tty_unthrottle,
 };
 
-static void __exit vz_exit(void)
+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)
+{
+	if (ve != get_ve0())
+		return __vz_con_ve_init(ve);
+	return 0;
+}
+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);
+}
+
+void vz_con_ve_fini(struct ve_struct *ve)
 {
-	put_tty_driver(vz_con_driver);
+	if (ve != get_ve0())
+		return __vz_con_ve_fini(ve);
 }
-module_exit(vz_exit)
+EXPORT_SYMBOL_GPL(vz_con_ve_fini);
 
-static int __init init(void)
+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;
 
-	vz_con_driver = tty_alloc_driver(1,
-					 TTY_DRIVER_REAL_RAW		|
-					 TTY_DRIVER_RESET_TERMIOS	|
-					 TTY_DRIVER_CONTAINERIZED);
-	if (IS_ERR(vz_con_driver)) {
-		pr_err("Couldn't allocate vzcon driver\n");
-		return PTR_ERR(vz_con_driver);
+	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_con_driver->driver_name	= "vzcon driver";
-	vz_con_driver->name		= "vzcon";
-	vz_con_driver->name_base	= 1;
-	vz_con_driver->major		= 0;
-	vz_con_driver->minor_start	= 1;
-	vz_con_driver->type		= TTY_DRIVER_TYPE_CONSOLE;
-	vz_con_driver->init_termios	= tty_std_termios;
-	vz_con_driver->ve		= get_ve0();
-	tty_set_operations(vz_con_driver, &vz_tty_fops);
-
-	ret = tty_register_driver(vz_con_driver);
+	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("Couldn't register vzcon driver\n");
-		put_tty_driver(vz_con_driver);
-		return 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(init);
+module_init(vz_con_init);
 
-struct tty_driver *vz_console_device(int *index)
+static void __exit vz_con_exit(void)
 {
-	*index = 0;
-	return vz_con_driver;
+	__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);
 }
-EXPORT_SYMBOL(vz_console_device);
+module_exit(vz_con_exit)
 
-MODULE_DESCRIPTION("Virtuozzo Container console");
-MODULE_LICENSE("GPL v2");
+struct tty_driver *vz_console_device(int *index)
+{
+	*index = VZ_CON_INDEX;
+	return vz_conm_driver;
+}
+EXPORT_SYMBOL_GPL(vz_console_device);



More information about the Devel mailing list