diff options
author | Jiri Slaby <jslaby@suse.cz> | 2011-08-10 08:59:28 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-23 13:10:38 -0400 |
commit | 24d406a6bf736f7aebdc8fa0f0ec86e0890c6d24 (patch) | |
tree | 3dfb2053f7785508304fd29d6c1bb6e6a42a7d5c | |
parent | dbb3b1ca5609d1f3848cd387d06cc60aaacf7f98 (diff) |
TTY: pty, fix pty counting
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>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/tty/pty.c | 17 | ||||
-rw-r--r-- | drivers/tty/tty_io.c | 3 | ||||
-rw-r--r-- | include/linux/tty.h | 2 | ||||
-rw-r--r-- | include/linux/tty_driver.h | 3 |
4 files changed, 21 insertions, 4 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 98b6e3bdb000..e809e9d4683c 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c | |||
@@ -446,8 +446,19 @@ static inline void legacy_pty_init(void) { } | |||
446 | int pty_limit = NR_UNIX98_PTY_DEFAULT; | 446 | int pty_limit = NR_UNIX98_PTY_DEFAULT; |
447 | static int pty_limit_min; | 447 | static int pty_limit_min; |
448 | static int pty_limit_max = NR_UNIX98_PTY_MAX; | 448 | static int pty_limit_max = NR_UNIX98_PTY_MAX; |
449 | static int tty_count; | ||
449 | static int pty_count; | 450 | static int pty_count; |
450 | 451 | ||
452 | static inline void pty_inc_count(void) | ||
453 | { | ||
454 | pty_count = (++tty_count) / 2; | ||
455 | } | ||
456 | |||
457 | static inline void pty_dec_count(void) | ||
458 | { | ||
459 | pty_count = (--tty_count) / 2; | ||
460 | } | ||
461 | |||
451 | static struct cdev ptmx_cdev; | 462 | static struct cdev ptmx_cdev; |
452 | 463 | ||
453 | static struct ctl_table pty_table[] = { | 464 | static struct ctl_table pty_table[] = { |
@@ -542,6 +553,7 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, | |||
542 | 553 | ||
543 | static void pty_unix98_shutdown(struct tty_struct *tty) | 554 | static 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; |
593 | err_free_mem: | 606 | err_free_mem: |
594 | deinitialize_tty_struct(o_tty); | 607 | deinitialize_tty_struct(o_tty); |
@@ -602,7 +615,7 @@ err_free_tty: | |||
602 | 615 | ||
603 | static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) | 616 | static 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 | ||
608 | static const struct tty_operations ptm_unix98_ops = { | 621 | static const struct tty_operations ptm_unix98_ops = { |
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 150e4f747c7d..4f1fc81112e6 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c | |||
@@ -1295,8 +1295,7 @@ static int tty_driver_install_tty(struct tty_driver *driver, | |||
1295 | * | 1295 | * |
1296 | * Locking: tty_mutex for now | 1296 | * Locking: tty_mutex for now |
1297 | */ | 1297 | */ |
1298 | static void tty_driver_remove_tty(struct tty_driver *driver, | 1298 | void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty) |
1299 | struct tty_struct *tty) | ||
1300 | { | 1299 | { |
1301 | if (driver->ops->remove) | 1300 | if (driver->ops->remove) |
1302 | driver->ops->remove(driver, tty); | 1301 | driver->ops->remove(driver, tty); |
diff --git a/include/linux/tty.h b/include/linux/tty.h index 44bc0c5617e1..5f2ede82b3d6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -421,6 +421,8 @@ extern void tty_driver_flush_buffer(struct tty_struct *tty); | |||
421 | extern void tty_throttle(struct tty_struct *tty); | 421 | extern void tty_throttle(struct tty_struct *tty); |
422 | extern void tty_unthrottle(struct tty_struct *tty); | 422 | extern void tty_unthrottle(struct tty_struct *tty); |
423 | extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); | 423 | extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); |
424 | extern void tty_driver_remove_tty(struct tty_driver *driver, | ||
425 | struct tty_struct *tty); | ||
424 | extern void tty_shutdown(struct tty_struct *tty); | 426 | extern void tty_shutdown(struct tty_struct *tty); |
425 | extern void tty_free_termios(struct tty_struct *tty); | 427 | extern void tty_free_termios(struct tty_struct *tty); |
426 | extern int is_current_pgrp_orphaned(void); | 428 | extern int is_current_pgrp_orphaned(void); |
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 9deeac855240..ecdaeb98b293 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); |