aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/tty_ldisc.c
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2013-03-11 16:44:28 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-18 19:38:58 -0400
commitcf5284765862ac65e4a3e5b34652e593ffda2bdf (patch)
tree7e87db80173d9a032cd4bb568728c6aa9a6b03d3 /drivers/tty/tty_ldisc.c
parent11cf48eab21887a120f7f47c67b44a829e3c371d (diff)
tty: Strengthen no-subsequent-use guarantee of tty_ldisc_halt()
In preparation for destructing and freeing the tty, the line discipline must first be brought to an inactive state before it can be destructed. This line discipline shutdown must: - disallow new users of the ldisc - wait for existing ldisc users to finish - only then, cancel/flush their pending/running work Factor tty_ldisc_wait_idle() from tty_set_ldisc() and tty_ldisc_kill() to ensure this shutdown order. Failure to provide this guarantee can result in scheduled work running after the tty has already been freed, as indicated in the following log message: [ 88.331234] WARNING: at drivers/tty/tty_buffer.c:435 flush_to_ldisc+0x194/0x1d0() [ 88.334505] Hardware name: Bochs [ 88.335618] tty is bad=-1 [ 88.335703] Modules linked in: netconsole configfs bnep rfcomm bluetooth ...... [ 88.345272] Pid: 39, comm: kworker/1:1 Tainted: G W 3.7.0-next-20121129+ttydebug-xeon #20121129+ttydebug [ 88.347736] Call Trace: [ 88.349024] [<ffffffff81058aff>] warn_slowpath_common+0x7f/0xc0 [ 88.350383] [<ffffffff81058bf6>] warn_slowpath_fmt+0x46/0x50 [ 88.351745] [<ffffffff81432bd4>] flush_to_ldisc+0x194/0x1d0 [ 88.353047] [<ffffffff816f7fe1>] ? _raw_spin_unlock_irq+0x21/0x50 [ 88.354190] [<ffffffff8108a809>] ? finish_task_switch+0x49/0xe0 [ 88.355436] [<ffffffff81077ad1>] process_one_work+0x121/0x490 [ 88.357674] [<ffffffff81432a40>] ? __tty_buffer_flush+0x90/0x90 [ 88.358954] [<ffffffff81078c84>] worker_thread+0x164/0x3e0 [ 88.360247] [<ffffffff81078b20>] ? manage_workers+0x120/0x120 [ 88.361282] [<ffffffff8107e230>] kthread+0xc0/0xd0 [ 88.362284] [<ffffffff816f0000>] ? cmos_do_probe+0x2eb/0x3bf [ 88.363391] [<ffffffff8107e170>] ? flush_kthread_worker+0xb0/0xb0 [ 88.364797] [<ffffffff816fff6c>] ret_from_fork+0x7c/0xb0 [ 88.366087] [<ffffffff8107e170>] ? flush_kthread_worker+0xb0/0xb0 [ 88.367266] ---[ end trace 453a7c9f38fbfec0 ]--- Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/tty_ldisc.c')
-rw-r--r--drivers/tty/tty_ldisc.c42
1 files changed, 24 insertions, 18 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index f691c7604d9a..525ee535c10d 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -530,24 +530,38 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout)
530/** 530/**
531 * tty_ldisc_halt - shut down the line discipline 531 * tty_ldisc_halt - shut down the line discipline
532 * @tty: tty device 532 * @tty: tty device
533 * @pending: returns true if work was scheduled when cancelled
534 * (can be set to NULL)
535 * @timeout: # of jiffies to wait for ldisc refs to be released
533 * 536 *
534 * Shut down the line discipline and work queue for this tty device. 537 * Shut down the line discipline and work queue for this tty device.
535 * The TTY_LDISC flag being cleared ensures no further references can 538 * The TTY_LDISC flag being cleared ensures no further references can
536 * be obtained while the delayed work queue halt ensures that no more 539 * be obtained while the delayed work queue halt ensures that no more
537 * data is fed to the ldisc. 540 * data is fed to the ldisc.
538 * 541 *
542 * Furthermore, guarantee that existing ldisc references have been
543 * released, which in turn, guarantees that no future buffer work
544 * can be rescheduled.
545 *
539 * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) 546 * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex)
540 * in order to make sure any currently executing ldisc work is also 547 * in order to make sure any currently executing ldisc work is also
541 * flushed. 548 * flushed.
542 */ 549 */
543 550
544static int tty_ldisc_halt(struct tty_struct *tty) 551static int tty_ldisc_halt(struct tty_struct *tty, int *pending, long timeout)
545{ 552{
546 int scheduled; 553 int scheduled, retval;
554
547 clear_bit(TTY_LDISC, &tty->flags); 555 clear_bit(TTY_LDISC, &tty->flags);
556 retval = tty_ldisc_wait_idle(tty, timeout);
557 if (retval)
558 return retval;
559
548 scheduled = cancel_work_sync(&tty->port->buf.work); 560 scheduled = cancel_work_sync(&tty->port->buf.work);
549 set_bit(TTY_LDISC_HALTED, &tty->flags); 561 set_bit(TTY_LDISC_HALTED, &tty->flags);
550 return scheduled; 562 if (pending)
563 *pending = scheduled;
564 return 0;
551} 565}
552 566
553/** 567/**
@@ -688,9 +702,9 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
688 * parallel to the change and re-referencing the tty. 702 * parallel to the change and re-referencing the tty.
689 */ 703 */
690 704
691 work = tty_ldisc_halt(tty); 705 retval = tty_ldisc_halt(tty, &work, 5 * HZ);
692 if (o_tty) 706 if (!retval && o_tty)
693 o_work = tty_ldisc_halt(o_tty); 707 retval = tty_ldisc_halt(o_tty, &o_work, 5 * HZ);
694 708
695 /* 709 /*
696 * Wait for ->hangup_work and ->buf.work handlers to terminate. 710 * Wait for ->hangup_work and ->buf.work handlers to terminate.
@@ -701,8 +715,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
701 715
702 tty_ldisc_flush_works(tty); 716 tty_ldisc_flush_works(tty);
703 717
704 retval = tty_ldisc_wait_idle(tty, 5 * HZ);
705
706 tty_lock(tty); 718 tty_lock(tty);
707 mutex_lock(&tty->ldisc_mutex); 719 mutex_lock(&tty->ldisc_mutex);
708 720
@@ -921,11 +933,6 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
921 933
922static void tty_ldisc_kill(struct tty_struct *tty) 934static void tty_ldisc_kill(struct tty_struct *tty)
923{ 935{
924 /* There cannot be users from userspace now. But there still might be
925 * drivers holding a reference via tty_ldisc_ref. Do not steal them the
926 * ldisc until they are done. */
927 tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT);
928
929 mutex_lock(&tty->ldisc_mutex); 936 mutex_lock(&tty->ldisc_mutex);
930 /* 937 /*
931 * Now kill off the ldisc 938 * Now kill off the ldisc
@@ -958,13 +965,12 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
958 * race with the set_ldisc code path. 965 * race with the set_ldisc code path.
959 */ 966 */
960 967
961 tty_ldisc_halt(tty); 968 tty_ldisc_halt(tty, NULL, MAX_SCHEDULE_TIMEOUT);
962 if (o_tty)
963 tty_ldisc_halt(o_tty);
964
965 tty_ldisc_flush_works(tty); 969 tty_ldisc_flush_works(tty);
966 if (o_tty) 970 if (o_tty) {
971 tty_ldisc_halt(o_tty, NULL, MAX_SCHEDULE_TIMEOUT);
967 tty_ldisc_flush_works(o_tty); 972 tty_ldisc_flush_works(o_tty);
973 }
968 974
969 tty_lock_pair(tty, o_tty); 975 tty_lock_pair(tty, o_tty);
970 /* This will need doing differently if we need to lock */ 976 /* This will need doing differently if we need to lock */