diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-14 11:59:49 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-14 11:59:49 -0400 |
commit | 45242006e19c6f99945554e4d7d5680caf6ed9ad (patch) | |
tree | 7b3561169050d242469df55ed338e752c9a35c48 | |
parent | 80f506918fdaaca6b574ba931536a58ce015c7be (diff) |
Make flush_to_ldisc properly handle parallel calls
-rw-r--r-- | drivers/char/tty_buffer.c | 29 |
1 files changed, 13 insertions, 16 deletions
diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 3108991c5c8b..0296612cc7df 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c | |||
@@ -402,28 +402,26 @@ static void flush_to_ldisc(struct work_struct *work) | |||
402 | container_of(work, struct tty_struct, buf.work.work); | 402 | container_of(work, struct tty_struct, buf.work.work); |
403 | unsigned long flags; | 403 | unsigned long flags; |
404 | struct tty_ldisc *disc; | 404 | struct tty_ldisc *disc; |
405 | struct tty_buffer *tbuf, *head; | ||
406 | char *char_buf; | ||
407 | unsigned char *flag_buf; | ||
408 | 405 | ||
409 | disc = tty_ldisc_ref(tty); | 406 | disc = tty_ldisc_ref(tty); |
410 | if (disc == NULL) /* !TTY_LDISC */ | 407 | if (disc == NULL) /* !TTY_LDISC */ |
411 | return; | 408 | return; |
412 | 409 | ||
413 | spin_lock_irqsave(&tty->buf.lock, flags); | 410 | spin_lock_irqsave(&tty->buf.lock, flags); |
414 | /* So we know a flush is running */ | 411 | |
415 | set_bit(TTY_FLUSHING, &tty->flags); | 412 | if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { |
416 | head = tty->buf.head; | 413 | struct tty_buffer *head; |
417 | if (head != NULL) { | 414 | while ((head = tty->buf.head) != NULL) { |
418 | tty->buf.head = NULL; | 415 | int count; |
419 | for (;;) { | 416 | char *char_buf; |
420 | int count = head->commit - head->read; | 417 | unsigned char *flag_buf; |
418 | |||
419 | count = head->commit - head->read; | ||
421 | if (!count) { | 420 | if (!count) { |
422 | if (head->next == NULL) | 421 | if (head->next == NULL) |
423 | break; | 422 | break; |
424 | tbuf = head; | 423 | tty->buf.head = head->next; |
425 | head = head->next; | 424 | tty_buffer_free(tty, head); |
426 | tty_buffer_free(tty, tbuf); | ||
427 | continue; | 425 | continue; |
428 | } | 426 | } |
429 | /* Ldisc or user is trying to flush the buffers | 427 | /* Ldisc or user is trying to flush the buffers |
@@ -445,9 +443,9 @@ static void flush_to_ldisc(struct work_struct *work) | |||
445 | flag_buf, count); | 443 | flag_buf, count); |
446 | spin_lock_irqsave(&tty->buf.lock, flags); | 444 | spin_lock_irqsave(&tty->buf.lock, flags); |
447 | } | 445 | } |
448 | /* Restore the queue head */ | 446 | clear_bit(TTY_FLUSHING, &tty->flags); |
449 | tty->buf.head = head; | ||
450 | } | 447 | } |
448 | |||
451 | /* We may have a deferred request to flush the input buffer, | 449 | /* We may have a deferred request to flush the input buffer, |
452 | if so pull the chain under the lock and empty the queue */ | 450 | if so pull the chain under the lock and empty the queue */ |
453 | if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { | 451 | if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { |
@@ -455,7 +453,6 @@ static void flush_to_ldisc(struct work_struct *work) | |||
455 | clear_bit(TTY_FLUSHPENDING, &tty->flags); | 453 | clear_bit(TTY_FLUSHPENDING, &tty->flags); |
456 | wake_up(&tty->read_wait); | 454 | wake_up(&tty->read_wait); |
457 | } | 455 | } |
458 | clear_bit(TTY_FLUSHING, &tty->flags); | ||
459 | spin_unlock_irqrestore(&tty->buf.lock, flags); | 456 | spin_unlock_irqrestore(&tty->buf.lock, flags); |
460 | 457 | ||
461 | tty_ldisc_deref(disc); | 458 | tty_ldisc_deref(disc); |