Commit feebed65 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

tty: shutdown method

Right now there are various drivers that try to use tty->count to know when
they get the final close. Aristeau Rozanski showed while debugging the vt
sysfs race that this isn't entirely safe.

Instead of driver side tricks to work around this introduce a shutdown which
is called when the tty is being destructed. This also means that the shutdown
method is tied into the refcounting.

Use this to rework the console close/sysfs logic.

Remove lots of special case code from the tty core code. The pty code can now
have a shutdown() method that replaces the special case hackery in the tree
free up paths.
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent bf7a06bc
...@@ -388,7 +388,14 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, ...@@ -388,7 +388,14 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
static const struct tty_operations pty_unix98_ops = { static void pty_shutdown(struct tty_struct *tty)
{
/* We have our own method as we don't use the tty index */
kfree(tty->termios);
kfree(tty->termios_locked);
}
static const struct tty_operations ptm_unix98_ops = {
.open = pty_open, .open = pty_open,
.close = pty_close, .close = pty_close,
.write = pty_write, .write = pty_write,
...@@ -397,10 +404,10 @@ static const struct tty_operations pty_unix98_ops = { ...@@ -397,10 +404,10 @@ static const struct tty_operations pty_unix98_ops = {
.chars_in_buffer = pty_chars_in_buffer, .chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle, .unthrottle = pty_unthrottle,
.set_termios = pty_set_termios, .set_termios = pty_set_termios,
.ioctl = pty_unix98_ioctl .ioctl = pty_unix98_ioctl,
.shutdown = pty_shutdown
}; };
static void __init unix98_pty_init(void) static void __init unix98_pty_init(void)
{ {
ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
...@@ -427,7 +434,7 @@ static void __init unix98_pty_init(void) ...@@ -427,7 +434,7 @@ static void __init unix98_pty_init(void)
ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
ptm_driver->other = pts_driver; ptm_driver->other = pts_driver;
tty_set_operations(ptm_driver, &pty_unix98_ops); tty_set_operations(ptm_driver, &ptm_unix98_ops);
pts_driver->owner = THIS_MODULE; pts_driver->owner = THIS_MODULE;
pts_driver->driver_name = "pty_slave"; pts_driver->driver_name = "pty_slave";
......
...@@ -1482,6 +1482,31 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -1482,6 +1482,31 @@ static int init_dev(struct tty_driver *driver, int idx,
goto end_init; goto end_init;
} }
void tty_free_termios(struct tty_struct *tty)
{
struct ktermios *tp;
int idx = tty->index;
/* Kill this flag and push into drivers for locking etc */
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
/* FIXME: Locking on ->termios array */
tp = tty->termios;
tty->driver->termios[idx] = NULL;
kfree(tp);
tp = tty->termios_locked;
tty->driver->termios_locked[idx] = NULL;
kfree(tp);
}
}
EXPORT_SYMBOL(tty_free_termios);
void tty_shutdown(struct tty_struct *tty)
{
tty->driver->ttys[tty->index] = NULL;
tty_free_termios(tty);
}
EXPORT_SYMBOL(tty_shutdown);
/** /**
* release_one_tty - release tty structure memory * release_one_tty - release tty structure memory
* @kref: kref of tty we are obliterating * @kref: kref of tty we are obliterating
...@@ -1499,27 +1524,11 @@ static void release_one_tty(struct kref *kref) ...@@ -1499,27 +1524,11 @@ static void release_one_tty(struct kref *kref)
{ {
struct tty_struct *tty = container_of(kref, struct tty_struct, kref); struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
struct tty_driver *driver = tty->driver; struct tty_driver *driver = tty->driver;
int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
struct ktermios *tp;
int idx = tty->index;
if (!devpts)
tty->driver->ttys[idx] = NULL;
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
/* FIXME: Locking on ->termios array */
tp = tty->termios;
if (!devpts)
tty->driver->termios[idx] = NULL;
kfree(tp);
tp = tty->termios_locked;
if (!devpts)
tty->driver->termios_locked[idx] = NULL;
kfree(tp);
}
if (tty->ops->shutdown)
tty->ops->shutdown(tty);
else
tty_shutdown(tty);
tty->magic = 0; tty->magic = 0;
/* FIXME: locking on tty->driver->refcount */ /* FIXME: locking on tty->driver->refcount */
tty->driver->refcount--; tty->driver->refcount--;
......
...@@ -2758,6 +2758,12 @@ static int con_open(struct tty_struct *tty, struct file *filp) ...@@ -2758,6 +2758,12 @@ static int con_open(struct tty_struct *tty, struct file *filp)
ret = vc_allocate(currcons); ret = vc_allocate(currcons);
if (ret == 0) { if (ret == 0) {
struct vc_data *vc = vc_cons[currcons].d; struct vc_data *vc = vc_cons[currcons].d;
/* Still being freed */
if (vc->vc_tty) {
release_console_sem();
return -ERESTARTSYS;
}
tty->driver_data = vc; tty->driver_data = vc;
vc->vc_tty = tty; vc->vc_tty = tty;
...@@ -2787,25 +2793,18 @@ static int con_open(struct tty_struct *tty, struct file *filp) ...@@ -2787,25 +2793,18 @@ static int con_open(struct tty_struct *tty, struct file *filp)
*/ */
static void con_close(struct tty_struct *tty, struct file *filp) static void con_close(struct tty_struct *tty, struct file *filp)
{ {
mutex_lock(&tty_mutex); /* Nothing to do - we defer to shutdown */
acquire_console_sem(); }
if (tty && tty->count == 1) {
struct vc_data *vc = tty->driver_data;
if (vc) static void con_shutdown(struct tty_struct *tty)
vc->vc_tty = NULL; {
tty->driver_data = NULL; struct vc_data *vc = tty->driver_data;
vcs_remove_sysfs(tty); BUG_ON(vc == NULL);
release_console_sem(); acquire_console_sem();
mutex_unlock(&tty_mutex); vc->vc_tty = NULL;
/* vcs_remove_sysfs(tty);
* tty_mutex is released, but we still hold BKL, so there is
* still exclusion against init_dev()
*/
return;
}
release_console_sem(); release_console_sem();
mutex_unlock(&tty_mutex); tty_shutdown(tty);
} }
static int default_italic_color = 2; // green (ASCII) static int default_italic_color = 2; // green (ASCII)
...@@ -2930,6 +2929,7 @@ static const struct tty_operations con_ops = { ...@@ -2930,6 +2929,7 @@ static const struct tty_operations con_ops = {
.throttle = con_throttle, .throttle = con_throttle,
.unthrottle = con_unthrottle, .unthrottle = con_unthrottle,
.resize = vt_resize, .resize = vt_resize,
.shutdown = con_shutdown
}; };
int __init vty_init(void) int __init vty_init(void)
......
...@@ -354,7 +354,8 @@ extern void tty_throttle(struct tty_struct *tty); ...@@ -354,7 +354,8 @@ extern void tty_throttle(struct tty_struct *tty);
extern void tty_unthrottle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty);
extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty,
struct winsize *ws); struct winsize *ws);
extern void tty_shutdown(struct tty_struct *tty);
extern void tty_free_termios(struct tty_struct *tty);
extern int is_current_pgrp_orphaned(void); extern int is_current_pgrp_orphaned(void);
extern struct pid *tty_get_pgrp(struct tty_struct *tty); extern struct pid *tty_get_pgrp(struct tty_struct *tty);
extern int is_ignored(int sig); extern int is_ignored(int sig);
......
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
* *
* Required method. * Required method.
* *
* void (*shutdown)(struct tty_struct * tty);
*
* This routine is called when a particular tty device is closed for
* the last time freeing up the resources.
*
* int (*write)(struct tty_struct * tty, * int (*write)(struct tty_struct * tty,
* const unsigned char *buf, int count); * const unsigned char *buf, int count);
* *
...@@ -200,6 +205,7 @@ struct tty_driver; ...@@ -200,6 +205,7 @@ struct tty_driver;
struct tty_operations { struct tty_operations {
int (*open)(struct tty_struct * tty, struct file * filp); int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty, int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count); const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch); int (*put_char)(struct tty_struct *tty, unsigned char ch);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment