Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −2
Original line number Original line Diff line number Diff line
@@ -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)
		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. */
+1 −2
Original line number Original line Diff line number Diff line
@@ -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)
			/* 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;
}
}


/*
/*
+19 −82
Original line number Original line Diff line number Diff line
@@ -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)
	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)
	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)
		/* 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)
	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,
		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 = {
+1 −7
Original line number Original line Diff line number Diff line
@@ -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 *);
+1 −2
Original line number Original line Diff line number Diff line
@@ -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)
		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;
}
}


/*
/*