aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2008-05-30 04:03:30 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-05-30 04:03:35 -0400
commitd4820e44b0ae6830b1d634e6d0a425d839388c06 (patch)
tree8f2e5e43623bd37056321de3940820a25d8a6bcf
parentc1bb7f31eaef6ed6b9f895b99d9ea12e6b853606 (diff)
[S390] sclp_vt220: fix scheduling while atomic bug.
The driver incorrectly assumed that putchar will only be called from schedulable process context and therefore blocked and waited if no free output buffers where available. Since putchar may also be called from BH context this may lead to deadlocks. To fix this just return the number of characters accepted and let the upper layer handle the rest. The console write function will busy wait (sclp_sync_wait) until a buffer is available again. Cc: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--drivers/s390/char/sclp_vt220.c27
1 files changed, 7 insertions, 20 deletions
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 35707c04e613..62576af36f47 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -71,9 +71,6 @@ static struct list_head sclp_vt220_outqueue;
71/* Number of requests in outqueue */ 71/* Number of requests in outqueue */
72static int sclp_vt220_outqueue_count; 72static int sclp_vt220_outqueue_count;
73 73
74/* Wait queue used to delay write requests while we've run out of buffers */
75static wait_queue_head_t sclp_vt220_waitq;
76
77/* Timer used for delaying write requests to merge subsequent messages into 74/* Timer used for delaying write requests to merge subsequent messages into
78 * a single buffer */ 75 * a single buffer */
79static struct timer_list sclp_vt220_timer; 76static struct timer_list sclp_vt220_timer;
@@ -133,7 +130,6 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request)
133 } while (request && __sclp_vt220_emit(request)); 130 } while (request && __sclp_vt220_emit(request));
134 if (request == NULL && sclp_vt220_flush_later) 131 if (request == NULL && sclp_vt220_flush_later)
135 sclp_vt220_emit_current(); 132 sclp_vt220_emit_current();
136 wake_up(&sclp_vt220_waitq);
137 /* Check if the tty needs a wake up call */ 133 /* Check if the tty needs a wake up call */
138 if (sclp_vt220_tty != NULL) { 134 if (sclp_vt220_tty != NULL) {
139 tty_wakeup(sclp_vt220_tty); 135 tty_wakeup(sclp_vt220_tty);
@@ -383,7 +379,7 @@ sclp_vt220_timeout(unsigned long data)
383 */ 379 */
384static int 380static int
385__sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, 381__sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
386 int convertlf, int may_schedule) 382 int convertlf, int may_fail)
387{ 383{
388 unsigned long flags; 384 unsigned long flags;
389 void *page; 385 void *page;
@@ -395,15 +391,14 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
395 overall_written = 0; 391 overall_written = 0;
396 spin_lock_irqsave(&sclp_vt220_lock, flags); 392 spin_lock_irqsave(&sclp_vt220_lock, flags);
397 do { 393 do {
398 /* Create a sclp output buffer if none exists yet */ 394 /* Create an sclp output buffer if none exists yet */
399 if (sclp_vt220_current_request == NULL) { 395 if (sclp_vt220_current_request == NULL) {
400 while (list_empty(&sclp_vt220_empty)) { 396 while (list_empty(&sclp_vt220_empty)) {
401 spin_unlock_irqrestore(&sclp_vt220_lock, flags); 397 spin_unlock_irqrestore(&sclp_vt220_lock, flags);
402 if (in_interrupt() || !may_schedule) 398 if (may_fail)
403 sclp_sync_wait(); 399 goto out;
404 else 400 else
405 wait_event(sclp_vt220_waitq, 401 sclp_sync_wait();
406 !list_empty(&sclp_vt220_empty));
407 spin_lock_irqsave(&sclp_vt220_lock, flags); 402 spin_lock_irqsave(&sclp_vt220_lock, flags);
408 } 403 }
409 page = (void *) sclp_vt220_empty.next; 404 page = (void *) sclp_vt220_empty.next;
@@ -437,6 +432,7 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
437 add_timer(&sclp_vt220_timer); 432 add_timer(&sclp_vt220_timer);
438 } 433 }
439 spin_unlock_irqrestore(&sclp_vt220_lock, flags); 434 spin_unlock_irqrestore(&sclp_vt220_lock, flags);
435out:
440 return overall_written; 436 return overall_written;
441} 437}
442 438
@@ -520,19 +516,11 @@ sclp_vt220_close(struct tty_struct *tty, struct file *filp)
520 * character to the tty device. If the kernel uses this routine, 516 * character to the tty device. If the kernel uses this routine,
521 * it must call the flush_chars() routine (if defined) when it is 517 * it must call the flush_chars() routine (if defined) when it is
522 * done stuffing characters into the driver. 518 * done stuffing characters into the driver.
523 *
524 * NOTE: include/linux/tty_driver.h specifies that a character should be
525 * ignored if there is no room in the queue. This driver implements a different
526 * semantic in that it will block when there is no more room left.
527 *
528 * FIXME: putchar can currently be called from BH and other non blocking
529 * handlers so this semantic isn't a good idea.
530 */ 519 */
531static int 520static int
532sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch) 521sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
533{ 522{
534 __sclp_vt220_write(&ch, 1, 0, 0, 1); 523 return __sclp_vt220_write(&ch, 1, 0, 0, 1);
535 return 1;
536} 524}
537 525
538/* 526/*
@@ -653,7 +641,6 @@ static int __init __sclp_vt220_init(void)
653 spin_lock_init(&sclp_vt220_lock); 641 spin_lock_init(&sclp_vt220_lock);
654 INIT_LIST_HEAD(&sclp_vt220_empty); 642 INIT_LIST_HEAD(&sclp_vt220_empty);
655 INIT_LIST_HEAD(&sclp_vt220_outqueue); 643 INIT_LIST_HEAD(&sclp_vt220_outqueue);
656 init_waitqueue_head(&sclp_vt220_waitq);
657 init_timer(&sclp_vt220_timer); 644 init_timer(&sclp_vt220_timer);
658 sclp_vt220_current_request = NULL; 645 sclp_vt220_current_request = NULL;
659 sclp_vt220_buffered_chars = 0; 646 sclp_vt220_buffered_chars = 0;