diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2016-05-02 08:53:29 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2016-05-04 10:29:53 -0400 |
commit | 8340ab60b3624386eaa24fa21bdb4e6775066ccf (patch) | |
tree | d0e7952f4a44d6733e448006f8a8fc442f1c08c5 | |
parent | 2e63a3a66655d5fe5d85c090b009979870436c00 (diff) |
s390/3270: avoid endless I/O loop with disconnected 3270 terminals
If a 3270 terminal is disconnected while the tty view is active
the 3270 driver goes into an endless loop of failed I/O requests
until the terminal is connected again.
Add code to the raw3270 interrupt handler to check for unit checks
due to failed I/O requests and put the device to sleep with the
RAW3270_FLAGS_BUSY flag until a unsolicited device end interrupt
indicates that the device can be used again. while we are at it
simplify the 3270 irq handling and remove unnecessary code.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | drivers/s390/char/con3270.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/fs3270.c | 3 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.c | 101 | ||||
-rw-r--r-- | drivers/s390/char/raw3270.h | 8 | ||||
-rw-r--r-- | drivers/s390/char/tty3270.c | 3 |
5 files changed, 23 insertions, 95 deletions
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 4d7a9badfede..6b1577c73fe7 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c | |||
@@ -400,7 +400,7 @@ con3270_deactivate(struct raw3270_view *view) | |||
400 | del_timer(&cp->timer); | 400 | del_timer(&cp->timer); |
401 | } | 401 | } |
402 | 402 | ||
403 | static int | 403 | static void |
404 | con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) | 404 | con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) |
405 | { | 405 | { |
406 | /* Handle ATTN. Schedule tasklet to read aid. */ | 406 | /* Handle ATTN. Schedule tasklet to read aid. */ |
@@ -418,7 +418,6 @@ con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) | |||
418 | cp->update_flags = CON_UPDATE_ALL; | 418 | cp->update_flags = CON_UPDATE_ALL; |
419 | con3270_set_timer(cp, 1); | 419 | con3270_set_timer(cp, 1); |
420 | } | 420 | } |
421 | return RAW3270_IO_DONE; | ||
422 | } | 421 | } |
423 | 422 | ||
424 | /* Console view to a 3270 device. */ | 423 | /* Console view to a 3270 device. */ |
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 71e974738014..85eca1cef063 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c | |||
@@ -217,7 +217,7 @@ fs3270_deactivate(struct raw3270_view *view) | |||
217 | fp->init->callback(fp->init, NULL); | 217 | fp->init->callback(fp->init, NULL); |
218 | } | 218 | } |
219 | 219 | ||
220 | static int | 220 | static void |
221 | fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) | 221 | fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) |
222 | { | 222 | { |
223 | /* Handle ATTN. Set indication and wake waiters for attention. */ | 223 | /* Handle ATTN. Set indication and wake waiters for attention. */ |
@@ -233,7 +233,6 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) | |||
233 | /* Normal end. Copy residual count. */ | 233 | /* Normal end. Copy residual count. */ |
234 | rq->rescnt = irb->scsw.cmd.count; | 234 | rq->rescnt = irb->scsw.cmd.count; |
235 | } | 235 | } |
236 | return RAW3270_IO_DONE; | ||
237 | } | 236 | } |
238 | 237 | ||
239 | /* | 238 | /* |
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 220acb4cbee5..0743f13101ee 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c | |||
@@ -229,29 +229,6 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) | |||
229 | } | 229 | } |
230 | 230 | ||
231 | /* | 231 | /* |
232 | * Stop running ccw. | ||
233 | */ | ||
234 | static int | ||
235 | __raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) | ||
236 | { | ||
237 | int retries; | ||
238 | int rc; | ||
239 | |||
240 | if (raw3270_request_final(rq)) | ||
241 | return 0; | ||
242 | /* Check if interrupt has already been processed */ | ||
243 | for (retries = 0; retries < 5; retries++) { | ||
244 | if (retries < 2) | ||
245 | rc = ccw_device_halt(rp->cdev, (long) rq); | ||
246 | else | ||
247 | rc = ccw_device_clear(rp->cdev, (long) rq); | ||
248 | if (rc == 0) | ||
249 | break; /* termination successful */ | ||
250 | } | ||
251 | return rc; | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * Add the request to the request queue, try to start it if the | 232 | * Add the request to the request queue, try to start it if the |
256 | * 3270 device is idle. Return without waiting for end of i/o. | 233 | * 3270 device is idle. Return without waiting for end of i/o. |
257 | */ | 234 | */ |
@@ -342,7 +319,6 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | |||
342 | struct raw3270 *rp; | 319 | struct raw3270 *rp; |
343 | struct raw3270_view *view; | 320 | struct raw3270_view *view; |
344 | struct raw3270_request *rq; | 321 | struct raw3270_request *rq; |
345 | int rc; | ||
346 | 322 | ||
347 | rp = dev_get_drvdata(&cdev->dev); | 323 | rp = dev_get_drvdata(&cdev->dev); |
348 | if (!rp) | 324 | if (!rp) |
@@ -350,55 +326,27 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | |||
350 | rq = (struct raw3270_request *) intparm; | 326 | rq = (struct raw3270_request *) intparm; |
351 | view = rq ? rq->view : rp->view; | 327 | view = rq ? rq->view : rp->view; |
352 | 328 | ||
353 | if (IS_ERR(irb)) | 329 | if (!IS_ERR(irb)) { |
354 | rc = RAW3270_IO_RETRY; | ||
355 | else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) { | ||
356 | rq->rc = -EIO; | ||
357 | rc = RAW3270_IO_DONE; | ||
358 | } else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END | | ||
359 | DEV_STAT_UNIT_EXCEP)) { | ||
360 | /* Handle CE-DE-UE and subsequent UDE */ | 330 | /* Handle CE-DE-UE and subsequent UDE */ |
361 | set_bit(RAW3270_FLAGS_BUSY, &rp->flags); | 331 | if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) |
362 | rc = RAW3270_IO_BUSY; | ||
363 | } else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { | ||
364 | /* Wait for UDE if busy flag is set. */ | ||
365 | if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { | ||
366 | clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); | 332 | clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); |
367 | /* Got it, now retry. */ | 333 | if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | |
368 | rc = RAW3270_IO_RETRY; | 334 | DEV_STAT_DEV_END | |
369 | } else | 335 | DEV_STAT_UNIT_EXCEP)) |
370 | rc = RAW3270_IO_BUSY; | 336 | set_bit(RAW3270_FLAGS_BUSY, &rp->flags); |
371 | } else if (view) | 337 | /* Handle disconnected devices */ |
372 | rc = view->fn->intv(view, rq, irb); | 338 | if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && |
373 | else | 339 | (irb->ecw[0] & SNS0_INTERVENTION_REQ)) |
374 | rc = RAW3270_IO_DONE; | 340 | set_bit(RAW3270_FLAGS_BUSY, &rp->flags); |
341 | /* Call interrupt handler of the view */ | ||
342 | if (view) | ||
343 | view->fn->intv(view, rq, irb); | ||
344 | } | ||
375 | 345 | ||
376 | switch (rc) { | 346 | if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) |
377 | case RAW3270_IO_DONE: | 347 | /* Device busy, do not start I/O */ |
378 | break; | ||
379 | case RAW3270_IO_BUSY: | ||
380 | /* | ||
381 | * Intervention required by the operator. We have to wait | ||
382 | * for unsolicited device end. | ||
383 | */ | ||
384 | return; | 348 | return; |
385 | case RAW3270_IO_RETRY: | 349 | |
386 | if (!rq) | ||
387 | break; | ||
388 | rq->rc = ccw_device_start(rp->cdev, &rq->ccw, | ||
389 | (unsigned long) rq, 0, 0); | ||
390 | if (rq->rc == 0) | ||
391 | return; /* Successfully restarted. */ | ||
392 | break; | ||
393 | case RAW3270_IO_STOP: | ||
394 | if (!rq) | ||
395 | break; | ||
396 | __raw3270_halt_io(rp, rq); | ||
397 | rq->rc = -EIO; | ||
398 | break; | ||
399 | default: | ||
400 | BUG(); | ||
401 | } | ||
402 | if (rq) { | 350 | if (rq) { |
403 | BUG_ON(list_empty(&rq->list)); | 351 | BUG_ON(list_empty(&rq->list)); |
404 | /* The request completed, remove from queue and do callback. */ | 352 | /* The request completed, remove from queue and do callback. */ |
@@ -408,6 +356,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | |||
408 | /* Do put_device for get_device in raw3270_start. */ | 356 | /* Do put_device for get_device in raw3270_start. */ |
409 | raw3270_put_view(view); | 357 | raw3270_put_view(view); |
410 | } | 358 | } |
359 | |||
411 | /* | 360 | /* |
412 | * Try to start each request on request queue until one is | 361 | * Try to start each request on request queue until one is |
413 | * started successful. | 362 | * started successful. |
@@ -685,23 +634,12 @@ raw3270_reset(struct raw3270_view *view) | |||
685 | return rc; | 634 | return rc; |
686 | } | 635 | } |
687 | 636 | ||
688 | static int | 637 | static void |
689 | raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, | 638 | raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, |
690 | struct irb *irb) | 639 | struct irb *irb) |
691 | { | 640 | { |
692 | struct raw3270 *rp; | 641 | struct raw3270 *rp; |
693 | 642 | ||
694 | /* | ||
695 | * Unit-Check Processing: | ||
696 | * Expect Command Reject or Intervention Required. | ||
697 | */ | ||
698 | if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { | ||
699 | /* Request finished abnormally. */ | ||
700 | if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { | ||
701 | set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); | ||
702 | return RAW3270_IO_BUSY; | ||
703 | } | ||
704 | } | ||
705 | if (rq) { | 643 | if (rq) { |
706 | if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { | 644 | if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { |
707 | if (irb->ecw[0] & SNS0_CMD_REJECT) | 645 | if (irb->ecw[0] & SNS0_CMD_REJECT) |
@@ -715,7 +653,6 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, | |||
715 | rp = view->dev; | 653 | rp = view->dev; |
716 | raw3270_read_modified(rp); | 654 | raw3270_read_modified(rp); |
717 | } | 655 | } |
718 | return RAW3270_IO_DONE; | ||
719 | } | 656 | } |
720 | 657 | ||
721 | static struct raw3270_fn raw3270_init_fn = { | 658 | static struct raw3270_fn raw3270_init_fn = { |
diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index e1e41c2861fb..56519cbb165c 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h | |||
@@ -125,19 +125,13 @@ raw3270_request_final(struct raw3270_request *rq) | |||
125 | 125 | ||
126 | void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); | 126 | void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); |
127 | 127 | ||
128 | /* Return value of *intv (see raw3270_fn below) can be one of the following: */ | ||
129 | #define RAW3270_IO_DONE 0 /* request finished */ | ||
130 | #define RAW3270_IO_BUSY 1 /* request still active */ | ||
131 | #define RAW3270_IO_RETRY 2 /* retry current request */ | ||
132 | #define RAW3270_IO_STOP 3 /* kill current request */ | ||
133 | |||
134 | /* | 128 | /* |
135 | * Functions of a 3270 view. | 129 | * Functions of a 3270 view. |
136 | */ | 130 | */ |
137 | struct raw3270_fn { | 131 | struct raw3270_fn { |
138 | int (*activate)(struct raw3270_view *); | 132 | int (*activate)(struct raw3270_view *); |
139 | void (*deactivate)(struct raw3270_view *); | 133 | void (*deactivate)(struct raw3270_view *); |
140 | int (*intv)(struct raw3270_view *, | 134 | void (*intv)(struct raw3270_view *, |
141 | struct raw3270_request *, struct irb *); | 135 | struct raw3270_request *, struct irb *); |
142 | void (*release)(struct raw3270_view *); | 136 | void (*release)(struct raw3270_view *); |
143 | void (*free)(struct raw3270_view *); | 137 | void (*free)(struct raw3270_view *); |
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 54ea5a01e30d..d6da18612ba8 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c | |||
@@ -645,7 +645,7 @@ tty3270_deactivate(struct raw3270_view *view) | |||
645 | del_timer(&tp->timer); | 645 | del_timer(&tp->timer); |
646 | } | 646 | } |
647 | 647 | ||
648 | static int | 648 | static void |
649 | tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) | 649 | tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) |
650 | { | 650 | { |
651 | /* Handle ATTN. Schedule tasklet to read aid. */ | 651 | /* Handle ATTN. Schedule tasklet to read aid. */ |
@@ -667,7 +667,6 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) | |||
667 | tp->update_flags = TTY_UPDATE_ALL; | 667 | tp->update_flags = TTY_UPDATE_ALL; |
668 | tty3270_set_timer(tp, 1); | 668 | tty3270_set_timer(tp, 1); |
669 | } | 669 | } |
670 | return RAW3270_IO_DONE; | ||
671 | } | 670 | } |
672 | 671 | ||
673 | /* | 672 | /* |