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

Vladimir Davydov vdavydov at parallels.com
Thu Jul 30 09:33:47 PDT 2015


On Thu, Jul 30, 2015 at 03:51:55PM +0300, Cyrill Gorcunov wrote:

> 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>

For me, vzt is inevitably associated with our test suit. A test for
testing it would be called vzt-vzt I suppose :-) May be we'd better call
it vtty to avoid confusion?

>  
>  #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"

I don't think we need this in uapi. Both "ptmx" and "tty" are hardcoded,
why should we be different?

> +
> +#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/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)

No need in this macro - you can use !driver_is_master instead.

> +#define tty_is_master(t)	driver_is_master((t)->driver)

> +#define tty_is_slave(t)		driver_is_slave((t)->driver)

ditto

> +
> +/*
> + * 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);

What is this for? vzt_idr[veid] must already equal map.

> +	} 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 (!map)
	return ERR_PTR(-ENXIO);

will free you from the responsibility to check if map == NULL below.

> +
> +	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);

It's difficult to read. Rewrite it w/o using ?: please.

> +	} else {
> +		tty = map ? map->master[idx] : NULL;
> +		if (tty && tty_is_exiting(tty))

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))

IS_ERR(map)

?

> +			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);

AFAIU this function is always called under tty_mutex. What's the point
in the vzt_mutex then?

> +
> +	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();

Why?

And please comment what you're trying to achieve here.

> +		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);

Why is TASK_KILLABLE safe?

> +
> +		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);

A comment explaining why you perform flush here would be nice to have.

> +	}
> +
> +	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)

Please add a comment explaining why you distinguish master tty.

Also, a general comment on what you're trying to achieve here would be
good.

> +		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);

OK, this is copied from pty_set_termios. Please mention it here and
everywhere you copy-n-paste from pty.c. BTW, wouldn't it be better to
keep the code in pty.c in order to avoid these code duplications?

> +}
> +
> +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);

Why do you export it?

> +
> +int vzt_match(dev_t device)
> +{
> +	return MAJOR(device) == TTY_MAJOR && MINOR(device) < MAX_NR_VZT_CONSOLES;
> +}

> +EXPORT_SYMBOL_GPL(vzt_match);

ditto

> +
> +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);

ditto

> +
> +static void ve_vzt_fini(void *data)
> +{
> +	struct ve_struct *ve = data;
> +	tty_map_remove(tty_map_lookup(ve->veid));

I think we'd better make tty_map_t ref-countable and free it once the
last tty using it is destroyed.

> +}
> +
> +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);

It can't be a module, because tty_io.c depends on it.

> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Virtual terminals emulation for VE environment");
> 



More information about the Devel mailing list