Commit 8340ab60 authored by Martin Schwidefsky's avatar Martin Schwidefsky

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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 2e63a3a6
...@@ -400,7 +400,7 @@ con3270_deactivate(struct raw3270_view *view) ...@@ -400,7 +400,7 @@ con3270_deactivate(struct raw3270_view *view)
del_timer(&cp->timer); del_timer(&cp->timer);
} }
static int static void
con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
{ {
/* Handle ATTN. Schedule tasklet to read aid. */ /* Handle ATTN. Schedule tasklet to read aid. */
...@@ -418,7 +418,6 @@ con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) ...@@ -418,7 +418,6 @@ con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
cp->update_flags = CON_UPDATE_ALL; cp->update_flags = CON_UPDATE_ALL;
con3270_set_timer(cp, 1); con3270_set_timer(cp, 1);
} }
return RAW3270_IO_DONE;
} }
/* Console view to a 3270 device. */ /* Console view to a 3270 device. */
......
...@@ -217,7 +217,7 @@ fs3270_deactivate(struct raw3270_view *view) ...@@ -217,7 +217,7 @@ fs3270_deactivate(struct raw3270_view *view)
fp->init->callback(fp->init, NULL); fp->init->callback(fp->init, NULL);
} }
static int static void
fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
{ {
/* Handle ATTN. Set indication and wake waiters for attention. */ /* 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,7 +233,6 @@ fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
/* Normal end. Copy residual count. */ /* Normal end. Copy residual count. */
rq->rescnt = irb->scsw.cmd.count; rq->rescnt = irb->scsw.cmd.count;
} }
return RAW3270_IO_DONE;
} }
/* /*
......
...@@ -228,29 +228,6 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) ...@@ -228,29 +228,6 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib)
rq->ccw.flags |= CCW_FLAG_IDA; rq->ccw.flags |= CCW_FLAG_IDA;
} }
/*
* Stop running ccw.
*/
static int
__raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq)
{
int retries;
int rc;
if (raw3270_request_final(rq))
return 0;
/* Check if interrupt has already been processed */
for (retries = 0; retries < 5; retries++) {
if (retries < 2)
rc = ccw_device_halt(rp->cdev, (long) rq);
else
rc = ccw_device_clear(rp->cdev, (long) rq);
if (rc == 0)
break; /* termination successful */
}
return rc;
}
/* /*
* Add the request to the request queue, try to start it if the * Add the request to the request queue, try to start it if the
* 3270 device is idle. Return without waiting for end of i/o. * 3270 device is idle. Return without waiting for end of i/o.
...@@ -342,7 +319,6 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -342,7 +319,6 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
struct raw3270 *rp; struct raw3270 *rp;
struct raw3270_view *view; struct raw3270_view *view;
struct raw3270_request *rq; struct raw3270_request *rq;
int rc;
rp = dev_get_drvdata(&cdev->dev); rp = dev_get_drvdata(&cdev->dev);
if (!rp) if (!rp)
...@@ -350,55 +326,27 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -350,55 +326,27 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
rq = (struct raw3270_request *) intparm; rq = (struct raw3270_request *) intparm;
view = rq ? rq->view : rp->view; view = rq ? rq->view : rp->view;
if (IS_ERR(irb)) if (!IS_ERR(irb)) {
rc = RAW3270_IO_RETRY;
else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
rq->rc = -EIO;
rc = RAW3270_IO_DONE;
} else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
DEV_STAT_UNIT_EXCEP)) {
/* Handle CE-DE-UE and subsequent UDE */ /* Handle CE-DE-UE and subsequent UDE */
set_bit(RAW3270_FLAGS_BUSY, &rp->flags); if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END)
rc = RAW3270_IO_BUSY;
} else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) {
/* Wait for UDE if busy flag is set. */
if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); clear_bit(RAW3270_FLAGS_BUSY, &rp->flags);
/* Got it, now retry. */ if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END |
rc = RAW3270_IO_RETRY; DEV_STAT_DEV_END |
} else DEV_STAT_UNIT_EXCEP))
rc = RAW3270_IO_BUSY; set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
} else if (view) /* Handle disconnected devices */
rc = view->fn->intv(view, rq, irb); if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
else (irb->ecw[0] & SNS0_INTERVENTION_REQ))
rc = RAW3270_IO_DONE; set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
/* Call interrupt handler of the view */
if (view)
view->fn->intv(view, rq, irb);
}
switch (rc) { if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags))
case RAW3270_IO_DONE: /* Device busy, do not start I/O */
break;
case RAW3270_IO_BUSY:
/*
* Intervention required by the operator. We have to wait
* for unsolicited device end.
*/
return; return;
case RAW3270_IO_RETRY:
if (!rq)
break;
rq->rc = ccw_device_start(rp->cdev, &rq->ccw,
(unsigned long) rq, 0, 0);
if (rq->rc == 0)
return; /* Successfully restarted. */
break;
case RAW3270_IO_STOP:
if (!rq)
break;
__raw3270_halt_io(rp, rq);
rq->rc = -EIO;
break;
default:
BUG();
}
if (rq) { if (rq) {
BUG_ON(list_empty(&rq->list)); BUG_ON(list_empty(&rq->list));
/* The request completed, remove from queue and do callback. */ /* 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,6 +356,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
/* Do put_device for get_device in raw3270_start. */ /* Do put_device for get_device in raw3270_start. */
raw3270_put_view(view); raw3270_put_view(view);
} }
/* /*
* Try to start each request on request queue until one is * Try to start each request on request queue until one is
* started successful. * started successful.
...@@ -685,23 +634,12 @@ raw3270_reset(struct raw3270_view *view) ...@@ -685,23 +634,12 @@ raw3270_reset(struct raw3270_view *view)
return rc; return rc;
} }
static int static void
raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
struct irb *irb) struct irb *irb)
{ {
struct raw3270 *rp; struct raw3270 *rp;
/*
* Unit-Check Processing:
* Expect Command Reject or Intervention Required.
*/
if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
/* Request finished abnormally. */
if (irb->ecw[0] & SNS0_INTERVENTION_REQ) {
set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags);
return RAW3270_IO_BUSY;
}
}
if (rq) { if (rq) {
if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
if (irb->ecw[0] & SNS0_CMD_REJECT) if (irb->ecw[0] & SNS0_CMD_REJECT)
...@@ -715,7 +653,6 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, ...@@ -715,7 +653,6 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
rp = view->dev; rp = view->dev;
raw3270_read_modified(rp); raw3270_read_modified(rp);
} }
return RAW3270_IO_DONE;
} }
static struct raw3270_fn raw3270_init_fn = { static struct raw3270_fn raw3270_init_fn = {
......
...@@ -125,19 +125,13 @@ raw3270_request_final(struct raw3270_request *rq) ...@@ -125,19 +125,13 @@ raw3270_request_final(struct raw3270_request *rq)
void raw3270_buffer_address(struct raw3270 *, char *, unsigned short); void raw3270_buffer_address(struct raw3270 *, char *, unsigned short);
/* Return value of *intv (see raw3270_fn below) can be one of the following: */
#define RAW3270_IO_DONE 0 /* request finished */
#define RAW3270_IO_BUSY 1 /* request still active */
#define RAW3270_IO_RETRY 2 /* retry current request */
#define RAW3270_IO_STOP 3 /* kill current request */
/* /*
* Functions of a 3270 view. * Functions of a 3270 view.
*/ */
struct raw3270_fn { struct raw3270_fn {
int (*activate)(struct raw3270_view *); int (*activate)(struct raw3270_view *);
void (*deactivate)(struct raw3270_view *); void (*deactivate)(struct raw3270_view *);
int (*intv)(struct raw3270_view *, void (*intv)(struct raw3270_view *,
struct raw3270_request *, struct irb *); struct raw3270_request *, struct irb *);
void (*release)(struct raw3270_view *); void (*release)(struct raw3270_view *);
void (*free)(struct raw3270_view *); void (*free)(struct raw3270_view *);
......
...@@ -645,7 +645,7 @@ tty3270_deactivate(struct raw3270_view *view) ...@@ -645,7 +645,7 @@ tty3270_deactivate(struct raw3270_view *view)
del_timer(&tp->timer); del_timer(&tp->timer);
} }
static int static void
tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
{ {
/* Handle ATTN. Schedule tasklet to read aid. */ /* Handle ATTN. Schedule tasklet to read aid. */
...@@ -667,7 +667,6 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) ...@@ -667,7 +667,6 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
tp->update_flags = TTY_UPDATE_ALL; tp->update_flags = TTY_UPDATE_ALL;
tty3270_set_timer(tp, 1); tty3270_set_timer(tp, 1);
} }
return RAW3270_IO_DONE;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment