diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2008-07-14 03:59:46 -0400 |
---|---|---|
committer | Heiko Carstens <heiko.carstens@de.ibm.com> | 2008-07-14 04:02:25 -0400 |
commit | 5e34599fc8ba1e8889095bd56a71fd9802ed5a51 (patch) | |
tree | 180691e2c05ac92fd111e32e2eb1f3339c302b42 /drivers/s390/char | |
parent | 095761d28ae43eae7d4504d49b0b952cf02b0188 (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/char')
-rw-r--r-- | drivers/s390/char/sclp_tty.c | 36 |
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; | |||
48 | static struct sclp_buffer *sclp_ttybuf; | 47 | static struct sclp_buffer *sclp_ttybuf; |
49 | /* Timer for delayed output of console messages. */ | 48 | /* Timer for delayed output of console messages. */ |
50 | static struct timer_list sclp_tty_timer; | 49 | static struct timer_list sclp_tty_timer; |
51 | /* Waitqueue to wait for buffers to get empty. */ | ||
52 | static wait_queue_head_t sclp_tty_waitq; | ||
53 | 50 | ||
54 | static struct tty_struct *sclp_tty; | 51 | static struct tty_struct *sclp_tty; |
55 | static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; | 52 | static 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 | */ |
179 | static void | 175 | static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail) |
180 | sclp_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); |
231 | out: | ||
232 | return overall_written; | ||
234 | } | 233 | } |
235 | 234 | ||
236 | /* | 235 | /* |
@@ -242,11 +241,10 @@ static int | |||
242 | sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) | 241 | sclp_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 | |||
277 | sclp_tty_flush_chars(struct tty_struct *tty) | 276 | sclp_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 | |||
316 | sclp_tty_flush_buffer(struct tty_struct *tty) | 315 | sclp_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; |