aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-04-04 17:26:54 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-04-04 17:26:54 -0400
commita5660b41af6a28f8004e70eb261e1202ad55c5e3 (patch)
tree5192bb81189953e2551ea7d66012b90cb5b1396b /drivers
parentd7c764c4c7b782c660b4600b0bff2e3509892a4d (diff)
tty: fix endless work loop when the buffer fills up
Commit f23eb2b2b285 ('tty: stop using "delayed_work" in the tty layer') ended up causing hung machines on UP with no preemption, because the work routine to flip the buffer data to the ldisc would endlessly re-arm itself if the destination buffer had filled up. With the delayed work, that only caused a timer-driving polling of the tty state every timer tick, but without the delay we just ended up with basically a busy loop instead. Stop the insane polling, and instead make the code that opens up the receive room re-schedule the buffer flip work. That's what we should have been doing anyway. This same "poll for tty room" issue is almost certainly also the cause of excessive kworker activity when idle reported by Dave Jones, who also reported "flush_to_ldisc executing 2500 times a second" back in Nov 2010: http://lkml.org/lkml/2010/11/30/592 which is that silly flushing done every timer tick. Wasting both power and CPU for no good reason. Reported-and-tested-by: Alexander Beregalov <a.beregalov@gmail.com> Reported-and-tested-by: Sitsofe Wheeler <sitsofe@yahoo.com> Cc: Greg KH <gregkh@suse.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Dave Jones <davej@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/tty/n_tty.c6
-rw-r--r--drivers/tty/tty_buffer.c4
2 files changed, 7 insertions, 3 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 428f4fe0b5f7..0ad32888091c 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -95,6 +95,7 @@ static void n_tty_set_room(struct tty_struct *tty)
95{ 95{
96 /* tty->read_cnt is not read locked ? */ 96 /* tty->read_cnt is not read locked ? */
97 int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; 97 int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
98 int old_left;
98 99
99 /* 100 /*
100 * If we are doing input canonicalization, and there are no 101 * If we are doing input canonicalization, and there are no
@@ -104,7 +105,12 @@ static void n_tty_set_room(struct tty_struct *tty)
104 */ 105 */
105 if (left <= 0) 106 if (left <= 0)
106 left = tty->icanon && !tty->canon_data; 107 left = tty->icanon && !tty->canon_data;
108 old_left = tty->receive_room;
107 tty->receive_room = left; 109 tty->receive_room = left;
110
111 /* Did this open up the receive buffer? We may need to flip */
112 if (left && !old_left)
113 schedule_work(&tty->buf.work);
108} 114}
109 115
110static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) 116static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty)
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index b9451219528b..f1a7918d71aa 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -442,10 +442,8 @@ static void flush_to_ldisc(struct work_struct *work)
442 line discipline as we want to empty the queue */ 442 line discipline as we want to empty the queue */
443 if (test_bit(TTY_FLUSHPENDING, &tty->flags)) 443 if (test_bit(TTY_FLUSHPENDING, &tty->flags))
444 break; 444 break;
445 if (!tty->receive_room || seen_tail) { 445 if (!tty->receive_room || seen_tail)
446 schedule_work(&tty->buf.work);
447 break; 446 break;
448 }
449 if (count > tty->receive_room) 447 if (count > tty->receive_room)
450 count = tty->receive_room; 448 count = tty->receive_room;
451 char_buf = head->char_buf_ptr + head->read; 449 char_buf = head->char_buf_ptr + head->read;