aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Slaby <jslaby@suse.cz>2011-08-10 08:59:28 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-10-03 14:39:49 -0400
commita38df1a01320298198c7cb2e3e8a61fc54459d6a (patch)
tree358a4b7c1a12a3ac49fa41c9baea128de9577734
parent64da3499c911698c004a2d47fd0af6ad683a811a (diff)
TTY: pty, fix pty counting
commit 24d406a6bf736f7aebdc8fa0f0ec86e0890c6d24 upstream. tty_operations->remove is normally called like: queue_release_one_tty ->tty_shutdown ->tty_driver_remove_tty ->tty_operations->remove However tty_shutdown() is called from queue_release_one_tty() only if tty_operations->shutdown is NULL. But for pty, it is not. pty_unix98_shutdown() is used there as ->shutdown. So tty_operations->remove of pty (i.e. pty_unix98_remove()) is never called. This results in invalid pty_count. I.e. what can be seen in /proc/sys/kernel/pty/nr. I see this was already reported at: https://lkml.org/lkml/2009/11/5/370 But it was not fixed since then. This patch is kind of a hackish way. The problem lies in ->install. We allocate there another tty (so-called tty->link). So ->install is called once, but ->remove twice, for both tty and tty->link. The fix here is to count both tty and tty->link and divide the count by 2 for user. And to have ->remove called, let's make tty_driver_remove_tty() global and call that from pty_unix98_shutdown() (tty_operations->shutdown). While at it, let's document that when ->shutdown is defined, tty_shutdown() is not called. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: Alan Cox <alan@linux.intel.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/tty/pty.c17
-rw-r--r--drivers/tty/tty_io.c3
-rw-r--r--include/linux/tty.h2
-rw-r--r--include/linux/tty_driver.h3
4 files changed, 21 insertions, 4 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 98b6e3bdb00..e809e9d4683 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -446,8 +446,19 @@ static inline void legacy_pty_init(void) { }
446int pty_limit = NR_UNIX98_PTY_DEFAULT; 446int pty_limit = NR_UNIX98_PTY_DEFAULT;
447static int pty_limit_min; 447static int pty_limit_min;
448static int pty_limit_max = NR_UNIX98_PTY_MAX; 448static int pty_limit_max = NR_UNIX98_PTY_MAX;
449static int tty_count;
449static int pty_count; 450static int pty_count;
450 451
452static inline void pty_inc_count(void)
453{
454 pty_count = (++tty_count) / 2;
455}
456
457static inline void pty_dec_count(void)
458{
459 pty_count = (--tty_count) / 2;
460}
461
451static struct cdev ptmx_cdev; 462static struct cdev ptmx_cdev;
452 463
453static struct ctl_table pty_table[] = { 464static struct ctl_table pty_table[] = {
@@ -542,6 +553,7 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
542 553
543static void pty_unix98_shutdown(struct tty_struct *tty) 554static void pty_unix98_shutdown(struct tty_struct *tty)
544{ 555{
556 tty_driver_remove_tty(tty->driver, tty);
545 /* We have our own method as we don't use the tty index */ 557 /* We have our own method as we don't use the tty index */
546 kfree(tty->termios); 558 kfree(tty->termios);
547} 559}
@@ -588,7 +600,8 @@ static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
588 */ 600 */
589 tty_driver_kref_get(driver); 601 tty_driver_kref_get(driver);
590 tty->count++; 602 tty->count++;
591 pty_count++; 603 pty_inc_count(); /* tty */
604 pty_inc_count(); /* tty->link */
592 return 0; 605 return 0;
593err_free_mem: 606err_free_mem:
594 deinitialize_tty_struct(o_tty); 607 deinitialize_tty_struct(o_tty);
@@ -602,7 +615,7 @@ err_free_tty:
602 615
603static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) 616static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
604{ 617{
605 pty_count--; 618 pty_dec_count();
606} 619}
607 620
608static const struct tty_operations ptm_unix98_ops = { 621static const struct tty_operations ptm_unix98_ops = {
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 6556f7452ba..b6f92d3001a 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1294,8 +1294,7 @@ static int tty_driver_install_tty(struct tty_driver *driver,
1294 * 1294 *
1295 * Locking: tty_mutex for now 1295 * Locking: tty_mutex for now
1296 */ 1296 */
1297static void tty_driver_remove_tty(struct tty_driver *driver, 1297void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty)
1298 struct tty_struct *tty)
1299{ 1298{
1300 if (driver->ops->remove) 1299 if (driver->ops->remove)
1301 driver->ops->remove(driver, tty); 1300 driver->ops->remove(driver, tty);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index d6f05292e45..6660c41949b 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -420,6 +420,8 @@ extern void tty_driver_flush_buffer(struct tty_struct *tty);
420extern void tty_throttle(struct tty_struct *tty); 420extern void tty_throttle(struct tty_struct *tty);
421extern void tty_unthrottle(struct tty_struct *tty); 421extern void tty_unthrottle(struct tty_struct *tty);
422extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); 422extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws);
423extern void tty_driver_remove_tty(struct tty_driver *driver,
424 struct tty_struct *tty);
423extern void tty_shutdown(struct tty_struct *tty); 425extern void tty_shutdown(struct tty_struct *tty);
424extern void tty_free_termios(struct tty_struct *tty); 426extern void tty_free_termios(struct tty_struct *tty);
425extern int is_current_pgrp_orphaned(void); 427extern int is_current_pgrp_orphaned(void);
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index 9deeac85524..ecdaeb98b29 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -47,6 +47,9 @@
47 * 47 *
48 * This routine is called synchronously when a particular tty device 48 * This routine is called synchronously when a particular tty device
49 * is closed for the last time freeing up the resources. 49 * is closed for the last time freeing up the resources.
50 * Note that tty_shutdown() is not called if ops->shutdown is defined.
51 * This means one is responsible to take care of calling ops->remove (e.g.
52 * via tty_driver_remove_tty) and releasing tty->termios.
50 * 53 *
51 * 54 *
52 * void (*cleanup)(struct tty_struct * tty); 55 * void (*cleanup)(struct tty_struct * tty);