aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
authorHeiko Carstens2008-07-14 09:59:46 +0200
committerHeiko Carstens2008-07-14 10:02:25 +0200
commit5e34599fc8ba1e8889095bd56a71fd9802ed5a51 (patch)
tree180691e2c05ac92fd111e32e2eb1f3339c302b42 /drivers/s390
parent095761d28ae43eae7d4504d49b0b952cf02b0188 (diff)
[S390] sclp_tty: Fix scheduling while atomic bug.
Finally fixes a possible scheduling while in atomic context bug. The driver used to wait on a waitqueue if no empty buffer was available. This could lead to a deadlock if the driver was called from non-schedulable context. So fix this. The write operation may fail now. It returns the number of characters accepted. put_char will never fail, since it writes characters to an intermediate buffer which gets flushed as soon as it is full. That means the driver now can busy wait if something is in the intermediate buffer and a write_string operation follows. Seems to be an acceptable compromise, since that shouldn't happen too often. Cc: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/char/sclp_tty.c36
1 files changed, 17 insertions, 19 deletions
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index bcf691af5336..434ba04b1309 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -13,7 +13,6 @@
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -48,8 +47,6 @@ static int sclp_tty_buffer_count;
static struct sclp_buffer *sclp_ttybuf;
/* Timer for delayed output of console messages. */
static struct timer_list sclp_tty_timer;
-/* Waitqueue to wait for buffers to get empty. */
-static wait_queue_head_t sclp_tty_waitq;
static struct tty_struct *sclp_tty;
static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
@@ -128,7 +125,6 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
struct sclp_buffer, list);
spin_unlock_irqrestore(&sclp_tty_lock, flags);
} while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback));
- wake_up(&sclp_tty_waitq);
/* check if the tty needs a wake up call */
if (sclp_tty != NULL) {
tty_wakeup(sclp_tty);
@@ -176,27 +172,27 @@ sclp_tty_timeout(unsigned long data)
/*
* Write a string to the sclp tty.
*/
-static void
-sclp_tty_write_string(const unsigned char *str, int count)
+static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail)
{
unsigned long flags;
void *page;
int written;
+ int overall_written;
struct sclp_buffer *buf;
if (count <= 0)
- return;
+ return 0;
+ overall_written = 0;
spin_lock_irqsave(&sclp_tty_lock, flags);
do {
/* Create a sclp output buffer if none exists yet */
if (sclp_ttybuf == NULL) {
while (list_empty(&sclp_tty_pages)) {
spin_unlock_irqrestore(&sclp_tty_lock, flags);
- if (in_interrupt())
- sclp_sync_wait();
+ if (may_fail)
+ goto out;
else
- wait_event(sclp_tty_waitq,
- !list_empty(&sclp_tty_pages));
+ sclp_sync_wait();
spin_lock_irqsave(&sclp_tty_lock, flags);
}
page = sclp_tty_pages.next;
@@ -206,6 +202,7 @@ sclp_tty_write_string(const unsigned char *str, int count)
}
/* try to write the string to the current output buffer */
written = sclp_write(sclp_ttybuf, str, count);
+ overall_written += written;
if (written == count)
break;
/*
@@ -231,6 +228,8 @@ sclp_tty_write_string(const unsigned char *str, int count)
add_timer(&sclp_tty_timer);
}
spin_unlock_irqrestore(&sclp_tty_lock, flags);
+out:
+ return overall_written;
}
/*
@@ -242,11 +241,10 @@ static int
sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
if (sclp_tty_chars_count > 0) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
- sclp_tty_write_string(buf, count);
- return count;
+ return sclp_tty_write_string(buf, count, 1);
}
/*
@@ -264,9 +262,10 @@ sclp_tty_put_char(struct tty_struct *tty, unsigned char ch)
{
sclp_tty_chars[sclp_tty_chars_count++] = ch;
if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
- } return 1;
+ }
+ return 1;
}
/*
@@ -277,7 +276,7 @@ static void
sclp_tty_flush_chars(struct tty_struct *tty)
{
if (sclp_tty_chars_count > 0) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
}
@@ -316,7 +315,7 @@ static void
sclp_tty_flush_buffer(struct tty_struct *tty)
{
if (sclp_tty_chars_count > 0) {
- sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+ sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
}
@@ -577,7 +576,6 @@ sclp_tty_init(void)
}
INIT_LIST_HEAD(&sclp_tty_outqueue);
spin_lock_init(&sclp_tty_lock);
- init_waitqueue_head(&sclp_tty_waitq);
init_timer(&sclp_tty_timer);
sclp_ttybuf = NULL;
sclp_tty_buffer_count = 0;