diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-14 18:34:55 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-14 18:34:55 -0400 |
| commit | d6047d79b90f6122fcbf2c3e783b507f7dd78fc6 (patch) | |
| tree | 4091ff408dd6873609e3f3341b0b3972155b7201 | |
| parent | 601adfedba37326410760d8d60f5da99a0adfd2f (diff) | |
| parent | 514fc01d389cf5d364ee3b5fec4ac1487448803e (diff) | |
Merge branch 'tty-fixes'
* branch 'tty-fixes':
tty: use the new 'flush_delayed_work()' helper to do ldisc flush
workqueue: add 'flush_delayed_work()' to run and wait for delayed work
Make flush_to_ldisc properly handle parallel calls
| -rw-r--r-- | drivers/char/tty_buffer.c | 31 | ||||
| -rw-r--r-- | include/linux/workqueue.h | 1 | ||||
| -rw-r--r-- | kernel/workqueue.c | 18 |
3 files changed, 33 insertions, 17 deletions
diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 3108991c5c8b..66fa4e10d76b 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); |
| @@ -471,7 +468,7 @@ static void flush_to_ldisc(struct work_struct *work) | |||
| 471 | */ | 468 | */ |
| 472 | void tty_flush_to_ldisc(struct tty_struct *tty) | 469 | void tty_flush_to_ldisc(struct tty_struct *tty) |
| 473 | { | 470 | { |
| 474 | flush_to_ldisc(&tty->buf.work.work); | 471 | flush_delayed_work(&tty->buf.work); |
| 475 | } | 472 | } |
| 476 | 473 | ||
| 477 | /** | 474 | /** |
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 7ef0c7b94f31..cf24c20de9e4 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h | |||
| @@ -207,6 +207,7 @@ extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, | |||
| 207 | 207 | ||
| 208 | extern void flush_workqueue(struct workqueue_struct *wq); | 208 | extern void flush_workqueue(struct workqueue_struct *wq); |
| 209 | extern void flush_scheduled_work(void); | 209 | extern void flush_scheduled_work(void); |
| 210 | extern void flush_delayed_work(struct delayed_work *work); | ||
| 210 | 211 | ||
| 211 | extern int schedule_work(struct work_struct *work); | 212 | extern int schedule_work(struct work_struct *work); |
| 212 | extern int schedule_work_on(int cpu, struct work_struct *work); | 213 | extern int schedule_work_on(int cpu, struct work_struct *work); |
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index addfe2df93b1..ccefe574dcf7 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
| @@ -640,6 +640,24 @@ int schedule_delayed_work(struct delayed_work *dwork, | |||
| 640 | EXPORT_SYMBOL(schedule_delayed_work); | 640 | EXPORT_SYMBOL(schedule_delayed_work); |
| 641 | 641 | ||
| 642 | /** | 642 | /** |
| 643 | * flush_delayed_work - block until a dwork_struct's callback has terminated | ||
| 644 | * @dwork: the delayed work which is to be flushed | ||
| 645 | * | ||
| 646 | * Any timeout is cancelled, and any pending work is run immediately. | ||
| 647 | */ | ||
| 648 | void flush_delayed_work(struct delayed_work *dwork) | ||
| 649 | { | ||
| 650 | if (del_timer(&dwork->timer)) { | ||
| 651 | struct cpu_workqueue_struct *cwq; | ||
| 652 | cwq = wq_per_cpu(keventd_wq, get_cpu()); | ||
| 653 | __queue_work(cwq, &dwork->work); | ||
| 654 | put_cpu(); | ||
| 655 | } | ||
| 656 | flush_work(&dwork->work); | ||
| 657 | } | ||
| 658 | EXPORT_SYMBOL(flush_delayed_work); | ||
| 659 | |||
| 660 | /** | ||
| 643 | * schedule_delayed_work_on - queue work in global workqueue on CPU after delay | 661 | * schedule_delayed_work_on - queue work in global workqueue on CPU after delay |
| 644 | * @cpu: cpu to use | 662 | * @cpu: cpu to use |
| 645 | * @dwork: job to be done | 663 | * @dwork: job to be done |
