aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2008-07-14 03:59:46 -0400
committerHeiko Carstens <heiko.carstens@de.ibm.com>2008-07-14 04:02:25 -0400
commit5e34599fc8ba1e8889095bd56a71fd9802ed5a51 (patch)
tree180691e2c05ac92fd111e32e2eb1f3339c302b42
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>
-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 @@
13#include <linux/tty.h> 13#include <linux/tty.h>
14#include <linux/tty_driver.h> 14#include <linux/tty_driver.h>
15#include <linux/tty_flip.h> 15#include <linux/tty_flip.h>
16#include <linux/wait.h>
17#include <linux/slab.h> 16#include <linux/slab.h>
18#include <linux/err.h> 17#include <linux/err.h>
19#include <linux/init.h> 18#include <linux/init.h>
@@ -48,8 +47,6 @@ static int sclp_tty_buffer_count;
48static struct sclp_buffer *sclp_ttybuf; 47static struct sclp_buffer *sclp_ttybuf;
49/* Timer for delayed output of console messages. */ 48/* Timer for delayed output of console messages. */
50static struct timer_list sclp_tty_timer; 49static struct timer_list sclp_tty_timer;
51/* Waitqueue to wait for buffers to get empty. */
52static wait_queue_head_t sclp_tty_waitq;
53 50
54static struct tty_struct *sclp_tty; 51static struct tty_struct *sclp_tty;
55static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; 52static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
@@ -128,7 +125,6 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
128 struct sclp_buffer, list); 125 struct sclp_buffer, list);
129 spin_unlock_irqrestore(&sclp_tty_lock, flags); 126 spin_unlock_irqrestore(&sclp_tty_lock, flags);
130 } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); 127 } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback));
131 wake_up(&sclp_tty_waitq);
132 /* check if the tty needs a wake up call */ 128 /* check if the tty needs a wake up call */
133 if (sclp_tty != NULL) { 129 if (sclp_tty != NULL) {
134 tty_wakeup(sclp_tty); 130 tty_wakeup(sclp_tty);
@@ -176,27 +172,27 @@ sclp_tty_timeout(unsigned long data)
176/* 172/*
177 * Write a string to the sclp tty. 173 * Write a string to the sclp tty.
178 */ 174 */
179static void 175static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail)
180sclp_tty_write_string(const unsigned char *str, int count)
181{ 176{
182 unsigned long flags; 177 unsigned long flags;
183 void *page; 178 void *page;
184 int written; 179 int written;
180 int overall_written;
185 struct sclp_buffer *buf; 181 struct sclp_buffer *buf;
186 182
187 if (count <= 0) 183 if (count <= 0)
188 return; 184 return 0;
185 overall_written = 0;
189 spin_lock_irqsave(&sclp_tty_lock, flags); 186 spin_lock_irqsave(&sclp_tty_lock, flags);
190 do { 187 do {
191 /* Create a sclp output buffer if none exists yet */ 188 /* Create a sclp output buffer if none exists yet */
192 if (sclp_ttybuf == NULL) { 189 if (sclp_ttybuf == NULL) {
193 while (list_empty(&sclp_tty_pages)) { 190 while (list_empty(&sclp_tty_pages)) {
194 spin_unlock_irqrestore(&sclp_tty_lock, flags); 191 spin_unlock_irqrestore(&sclp_tty_lock, flags);
195 if (in_interrupt()) 192 if (may_fail)
196 sclp_sync_wait(); 193 goto out;
197 else 194 else
198 wait_event(sclp_tty_waitq, 195 sclp_sync_wait();
199 !list_empty(&sclp_tty_pages));
200 spin_lock_irqsave(&sclp_tty_lock, flags); 196 spin_lock_irqsave(&sclp_tty_lock, flags);
201 } 197 }
202 page = sclp_tty_pages.next; 198 page = sclp_tty_pages.next;
@@ -206,6 +202,7 @@ sclp_tty_write_string(const unsigned char *str, int count)
206 } 202 }
207 /* try to write the string to the current output buffer */ 203 /* try to write the string to the current output buffer */
208 written = sclp_write(sclp_ttybuf, str, count); 204 written = sclp_write(sclp_ttybuf, str, count);
205 overall_written += written;
209 if (written == count) 206 if (written == count)
210 break; 207 break;
211 /* 208 /*
@@ -231,6 +228,8 @@ sclp_tty_write_string(const unsigned char *str, int count)
231 add_timer(&sclp_tty_timer); 228 add_timer(&sclp_tty_timer);
232 } 229 }
233 spin_unlock_irqrestore(&sclp_tty_lock, flags); 230 spin_unlock_irqrestore(&sclp_tty_lock, flags);
231out:
232 return overall_written;
234} 233}
235 234
236/* 235/*
@@ -242,11 +241,10 @@ static int
242sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) 241sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
243{ 242{
244 if (sclp_tty_chars_count > 0) { 243 if (sclp_tty_chars_count > 0) {
245 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); 244 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
246 sclp_tty_chars_count = 0; 245 sclp_tty_chars_count = 0;
247 } 246 }
248 sclp_tty_write_string(buf, count); 247 return sclp_tty_write_string(buf, count, 1);
249 return count;
250} 248}
251 249
252/* 250/*
@@ -264,9 +262,10 @@ sclp_tty_put_char(struct tty_struct *tty, unsigned char ch)
264{ 262{
265 sclp_tty_chars[sclp_tty_chars_count++] = ch; 263 sclp_tty_chars[sclp_tty_chars_count++] = ch;
266 if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { 264 if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
267 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); 265 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
268 sclp_tty_chars_count = 0; 266 sclp_tty_chars_count = 0;
269 } return 1; 267 }
268 return 1;
270} 269}
271 270
272/* 271/*
@@ -277,7 +276,7 @@ static void
277sclp_tty_flush_chars(struct tty_struct *tty) 276sclp_tty_flush_chars(struct tty_struct *tty)
278{ 277{
279 if (sclp_tty_chars_count > 0) { 278 if (sclp_tty_chars_count > 0) {
280 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); 279 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
281 sclp_tty_chars_count = 0; 280 sclp_tty_chars_count = 0;
282 } 281 }
283} 282}
@@ -316,7 +315,7 @@ static void
316sclp_tty_flush_buffer(struct tty_struct *tty) 315sclp_tty_flush_buffer(struct tty_struct *tty)
317{ 316{
318 if (sclp_tty_chars_count > 0) { 317 if (sclp_tty_chars_count > 0) {
319 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); 318 sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
320 sclp_tty_chars_count = 0; 319 sclp_tty_chars_count = 0;
321 } 320 }
322} 321}
@@ -577,7 +576,6 @@ sclp_tty_init(void)
577 } 576 }
578 INIT_LIST_HEAD(&sclp_tty_outqueue); 577 INIT_LIST_HEAD(&sclp_tty_outqueue);
579 spin_lock_init(&sclp_tty_lock); 578 spin_lock_init(&sclp_tty_lock);
580 init_waitqueue_head(&sclp_tty_waitq);
581 init_timer(&sclp_tty_timer); 579 init_timer(&sclp_tty_timer);
582 sclp_ttybuf = NULL; 580 sclp_ttybuf = NULL;
583 sclp_tty_buffer_count = 0; 581 sclp_tty_buffer_count = 0;