Commit 81de916f authored by Linus Torvalds's avatar Linus Torvalds

tty_buffer: get rid of 'seen_tail' logic in flush_to_ldisc

The flush_to_ldisc() work entry has special logic to notice when it has
seen the original tail of the data queue, and it avoids continuing the
flush if it sees that _original_ tail rather than the current tail.

This logic can trigger in case somebody is constantly adding new data to
the tty while the flushing is active - and the intent is to avoid
excessive CPU usage while flushing the tty, especially as we used to do
this from a softirq context which made it non-preemptible.

However, since we no longer re-arm the work-queue from within itself
(because that causes other trouble: see commit a5660b41 "tty: fix
endless work loop when the buffer fills up"), this just leads to
possible hung tty's (most easily seen in SMP and with a test-program
that floods a pty with data - nobody seems to have reported this for any
real-life situation yet).

And since the workqueue isn't done from timers and softirq's any more,
it's doubtful whether the CPU useage issue is really relevant any more.
So just remove the logic entirely, and see if anybody ever notices.

Alternatively, we might want to re-introduce the "re-arm the work" for
just this case, but then we'd have to re-introduce the delayed work
model or some explicit timer, which really doesn't seem worth it for
this.
Reported-and-tested-by: default avatarGuillaume Chazarain <guichaz@gmail.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent cb0a02ec
...@@ -413,8 +413,7 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -413,8 +413,7 @@ static void flush_to_ldisc(struct work_struct *work)
spin_lock_irqsave(&tty->buf.lock, flags); spin_lock_irqsave(&tty->buf.lock, flags);
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
struct tty_buffer *head, *tail = tty->buf.tail; struct tty_buffer *head;
int seen_tail = 0;
while ((head = tty->buf.head) != NULL) { while ((head = tty->buf.head) != NULL) {
int count; int count;
char *char_buf; char *char_buf;
...@@ -424,15 +423,6 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -424,15 +423,6 @@ static void flush_to_ldisc(struct work_struct *work)
if (!count) { if (!count) {
if (head->next == NULL) if (head->next == NULL)
break; break;
/*
There's a possibility tty might get new buffer
added during the unlock window below. We could
end up spinning in here forever hogging the CPU
completely. To avoid this let's have a rest each
time we processed the tail buffer.
*/
if (tail == head)
seen_tail = 1;
tty->buf.head = head->next; tty->buf.head = head->next;
tty_buffer_free(tty, head); tty_buffer_free(tty, head);
continue; continue;
...@@ -442,7 +432,7 @@ static void flush_to_ldisc(struct work_struct *work) ...@@ -442,7 +432,7 @@ static void flush_to_ldisc(struct work_struct *work)
line discipline as we want to empty the queue */ line discipline as we want to empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags)) if (test_bit(TTY_FLUSHPENDING, &tty->flags))
break; break;
if (!tty->receive_room || seen_tail) if (!tty->receive_room)
break; break;
if (count > tty->receive_room) if (count > tty->receive_room)
count = tty->receive_room; count = tty->receive_room;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment