aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Cox <alan@redhat.com>2008-10-13 05:41:30 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-10-13 12:51:42 -0400
commitfeebed6515a113eeb33919e9557a8b9710ea627c (patch)
tree0461178ee0c5e16ea90023e4b6386cb5c57d3391
parentbf7a06bcce205705ea5c7675cbb8ea9239ea30a0 (diff)
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: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/char/pty.c15
-rw-r--r--drivers/char/tty_io.c49
-rw-r--r--drivers/char/vt.c34
-rw-r--r--include/linux/tty.h3
-rw-r--r--include/linux/tty_driver.h6
5 files changed, 65 insertions, 42 deletions
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 76b27932d229..ec09c1cd4fe9 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -388,7 +388,14 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
388 return -ENOIOCTLCMD; 388 return -ENOIOCTLCMD;
389} 389}
390 390
391static const struct tty_operations pty_unix98_ops = { 391static void pty_shutdown(struct tty_struct *tty)
392{
393 /* We have our own method as we don't use the tty index */
394 kfree(tty->termios);
395 kfree(tty->termios_locked);
396}
397
398static const struct tty_operations ptm_unix98_ops = {
392 .open = pty_open, 399 .open = pty_open,
393 .close = pty_close, 400 .close = pty_close,
394 .write = pty_write, 401 .write = pty_write,
@@ -397,10 +404,10 @@ static const struct tty_operations pty_unix98_ops = {
397 .chars_in_buffer = pty_chars_in_buffer, 404 .chars_in_buffer = pty_chars_in_buffer,
398 .unthrottle = pty_unthrottle, 405 .unthrottle = pty_unthrottle,
399 .set_termios = pty_set_termios, 406 .set_termios = pty_set_termios,
400 .ioctl = pty_unix98_ioctl 407 .ioctl = pty_unix98_ioctl,
408 .shutdown = pty_shutdown
401}; 409};
402 410
403
404static void __init unix98_pty_init(void) 411static void __init unix98_pty_init(void)
405{ 412{
406 ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); 413 ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
@@ -427,7 +434,7 @@ static void __init unix98_pty_init(void)
427 ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | 434 ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
428 TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; 435 TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
429 ptm_driver->other = pts_driver; 436 ptm_driver->other = pts_driver;
430 tty_set_operations(ptm_driver, &pty_unix98_ops); 437 tty_set_operations(ptm_driver, &ptm_unix98_ops);
431 438
432 pts_driver->owner = THIS_MODULE; 439 pts_driver->owner = THIS_MODULE;
433 pts_driver->driver_name = "pty_slave"; 440 pts_driver->driver_name = "pty_slave";
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 2e96ce0fddc5..f91704d57a4e 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1482,6 +1482,31 @@ release_mem_out:
1482 goto end_init; 1482 goto end_init;
1483} 1483}
1484 1484
1485void tty_free_termios(struct tty_struct *tty)
1486{
1487 struct ktermios *tp;
1488 int idx = tty->index;
1489 /* Kill this flag and push into drivers for locking etc */
1490 if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
1491 /* FIXME: Locking on ->termios array */
1492 tp = tty->termios;
1493 tty->driver->termios[idx] = NULL;
1494 kfree(tp);
1495
1496 tp = tty->termios_locked;
1497 tty->driver->termios_locked[idx] = NULL;
1498 kfree(tp);
1499 }
1500}
1501EXPORT_SYMBOL(tty_free_termios);
1502
1503void tty_shutdown(struct tty_struct *tty)
1504{
1505 tty->driver->ttys[tty->index] = NULL;
1506 tty_free_termios(tty);
1507}
1508EXPORT_SYMBOL(tty_shutdown);
1509
1485/** 1510/**
1486 * release_one_tty - release tty structure memory 1511 * release_one_tty - release tty structure memory
1487 * @kref: kref of tty we are obliterating 1512 * @kref: kref of tty we are obliterating
@@ -1499,27 +1524,11 @@ static void release_one_tty(struct kref *kref)
1499{ 1524{
1500 struct tty_struct *tty = container_of(kref, struct tty_struct, kref); 1525 struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
1501 struct tty_driver *driver = tty->driver; 1526 struct tty_driver *driver = tty->driver;
1502 int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
1503 struct ktermios *tp;
1504 int idx = tty->index;
1505
1506 if (!devpts)
1507 tty->driver->ttys[idx] = NULL;
1508
1509 if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
1510 /* FIXME: Locking on ->termios array */
1511 tp = tty->termios;
1512 if (!devpts)
1513 tty->driver->termios[idx] = NULL;
1514 kfree(tp);
1515
1516 tp = tty->termios_locked;
1517 if (!devpts)
1518 tty->driver->termios_locked[idx] = NULL;
1519 kfree(tp);
1520 }
1521
1522 1527
1528 if (tty->ops->shutdown)
1529 tty->ops->shutdown(tty);
1530 else
1531 tty_shutdown(tty);
1523 tty->magic = 0; 1532 tty->magic = 0;
1524 /* FIXME: locking on tty->driver->refcount */ 1533 /* FIXME: locking on tty->driver->refcount */
1525 tty->driver->refcount--; 1534 tty->driver->refcount--;
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index ec94521c3118..37a45db5bae0 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -2758,6 +2758,12 @@ static int con_open(struct tty_struct *tty, struct file *filp)
2758 ret = vc_allocate(currcons); 2758 ret = vc_allocate(currcons);
2759 if (ret == 0) { 2759 if (ret == 0) {
2760 struct vc_data *vc = vc_cons[currcons].d; 2760 struct vc_data *vc = vc_cons[currcons].d;
2761
2762 /* Still being freed */
2763 if (vc->vc_tty) {
2764 release_console_sem();
2765 return -ERESTARTSYS;
2766 }
2761 tty->driver_data = vc; 2767 tty->driver_data = vc;
2762 vc->vc_tty = tty; 2768 vc->vc_tty = tty;
2763 2769
@@ -2787,25 +2793,18 @@ static int con_open(struct tty_struct *tty, struct file *filp)
2787 */ 2793 */
2788static void con_close(struct tty_struct *tty, struct file *filp) 2794static void con_close(struct tty_struct *tty, struct file *filp)
2789{ 2795{
2790 mutex_lock(&tty_mutex); 2796 /* Nothing to do - we defer to shutdown */
2791 acquire_console_sem(); 2797}
2792 if (tty && tty->count == 1) {
2793 struct vc_data *vc = tty->driver_data;
2794 2798
2795 if (vc) 2799static void con_shutdown(struct tty_struct *tty)
2796 vc->vc_tty = NULL; 2800{
2797 tty->driver_data = NULL; 2801 struct vc_data *vc = tty->driver_data;
2798 vcs_remove_sysfs(tty); 2802 BUG_ON(vc == NULL);
2799 release_console_sem(); 2803 acquire_console_sem();
2800 mutex_unlock(&tty_mutex); 2804 vc->vc_tty = NULL;
2801 /* 2805 vcs_remove_sysfs(tty);
2802 * tty_mutex is released, but we still hold BKL, so there is
2803 * still exclusion against init_dev()
2804 */
2805 return;
2806 }
2807 release_console_sem(); 2806 release_console_sem();
2808 mutex_unlock(&tty_mutex); 2807 tty_shutdown(tty);
2809} 2808}
2810 2809
2811static int default_italic_color = 2; // green (ASCII) 2810static int default_italic_color = 2; // green (ASCII)
@@ -2930,6 +2929,7 @@ static const struct tty_operations con_ops = {
2930 .throttle = con_throttle, 2929 .throttle = con_throttle,
2931 .unthrottle = con_unthrottle, 2930 .unthrottle = con_unthrottle,
2932 .resize = vt_resize, 2931 .resize = vt_resize,
2932 .shutdown = con_shutdown
2933}; 2933};
2934 2934
2935int __init vty_init(void) 2935int __init vty_init(void)
diff --git a/include/linux/tty.h b/include/linux/tty.h
index e00393a3d1c9..6e39c705b9b6 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -354,7 +354,8 @@ extern void tty_throttle(struct tty_struct *tty);
354extern void tty_unthrottle(struct tty_struct *tty); 354extern void tty_unthrottle(struct tty_struct *tty);
355extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, 355extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty,
356 struct winsize *ws); 356 struct winsize *ws);
357 357extern void tty_shutdown(struct tty_struct *tty);
358extern void tty_free_termios(struct tty_struct *tty);
358extern int is_current_pgrp_orphaned(void); 359extern int is_current_pgrp_orphaned(void);
359extern struct pid *tty_get_pgrp(struct tty_struct *tty); 360extern struct pid *tty_get_pgrp(struct tty_struct *tty);
360extern int is_ignored(int sig); 361extern int is_ignored(int sig);
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index ac6e58e26b73..2322313a8589 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -21,6 +21,11 @@
21 * 21 *
22 * Required method. 22 * Required method.
23 * 23 *
24 * void (*shutdown)(struct tty_struct * tty);
25 *
26 * This routine is called when a particular tty device is closed for
27 * the last time freeing up the resources.
28 *
24 * int (*write)(struct tty_struct * tty, 29 * int (*write)(struct tty_struct * tty,
25 * const unsigned char *buf, int count); 30 * const unsigned char *buf, int count);
26 * 31 *
@@ -200,6 +205,7 @@ struct tty_driver;
200struct tty_operations { 205struct tty_operations {
201 int (*open)(struct tty_struct * tty, struct file * filp); 206 int (*open)(struct tty_struct * tty, struct file * filp);
202 void (*close)(struct tty_struct * tty, struct file * filp); 207 void (*close)(struct tty_struct * tty, struct file * filp);
208 void (*shutdown)(struct tty_struct *tty);
203 int (*write)(struct tty_struct * tty, 209 int (*write)(struct tty_struct * tty,
204 const unsigned char *buf, int count); 210 const unsigned char *buf, int count);
205 int (*put_char)(struct tty_struct *tty, unsigned char ch); 211 int (*put_char)(struct tty_struct *tty, unsigned char ch);