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

Commit 1549d13f authored by Jan Glauber's avatar Jan Glauber Committed by Martin Schwidefsky
Browse files

s390/qdio: Cleanup error handling to drivers



Various improvements of qdio error reporting to the
upper-layer drivers (qeth, zfcp):

- Split QDIO_ERROR_ACTIVATE_CHECK_CONDITION into:

  QDIO_ERROR_ACTIVATE: qdio termination interrupt
  QDIO_ERROR_GET_BUF_STATE: QIOASSIST eqbs error
  QDIO_ERROR_SET_BUF_STATE: QIOASSIST sqbs error

  Add QDIO_ERROR_FATAL / QDIO_ERROR_TEMPORARY masks
  to ease recovery decision in upper-layer drivers.

- Don't (ab-)use qdio handler errors as return codes
  for do_QDIO but use standard error codes:

  -ENOBUFS: temporary target CC=2 condition
  -EBUSY: unresolved SIGA-W CC=2 busy condition
  -EIO: I/O error (CC=1, CC=3)

- Remove unneeded memory clobber from SIGA-R
- Remove EX_TABLE entry on SIGA-W, we want to see these errors

Reviewed-by: default avatarUrsula Braun <ursula.braun@de.ibm.com>
Signed-off-by: default avatarJan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 050276ab
Loading
Loading
Loading
Loading
+7 −5
Original line number Original line Diff line number Diff line
@@ -325,11 +325,13 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int,
			    int, int, unsigned long);
			    int, int, unsigned long);


/* qdio errors reported to the upper-layer program */
/* qdio errors reported to the upper-layer program */
#define QDIO_ERROR_SIGA_TARGET			0x02
#define QDIO_ERROR_ACTIVATE			0x0001
#define QDIO_ERROR_SIGA_ACCESS_EXCEPTION	0x10
#define QDIO_ERROR_GET_BUF_STATE		0x0002
#define QDIO_ERROR_SIGA_BUSY			0x20
#define QDIO_ERROR_SET_BUF_STATE		0x0004
#define QDIO_ERROR_ACTIVATE_CHECK_CONDITION	0x40
#define QDIO_ERROR_SLSB_STATE			0x0100
#define QDIO_ERROR_SLSB_STATE			0x80

#define QDIO_ERROR_FATAL			0x00ff
#define QDIO_ERROR_TEMPORARY			0xff00


/* for qdio_cleanup */
/* for qdio_cleanup */
#define QDIO_FLAG_CLEANUP_USING_CLEAR		0x01
#define QDIO_FLAG_CLEANUP_USING_CLEAR		0x01
+22 −21
Original line number Original line Diff line number Diff line
@@ -63,7 +63,7 @@ static inline int do_siga_input(unsigned long schid, unsigned int mask,
		"	ipm	%0\n"
		"	ipm	%0\n"
		"	srl	%0,28\n"
		"	srl	%0,28\n"
		: "=d" (cc)
		: "=d" (cc)
		: "d" (__fc), "d" (__schid), "d" (__mask) : "cc", "memory");
		: "d" (__fc), "d" (__schid), "d" (__mask) : "cc");
	return cc;
	return cc;
}
}


@@ -74,7 +74,7 @@ static inline int do_siga_input(unsigned long schid, unsigned int mask,
 * @bb: busy bit indicator, set only if SIGA-w/wt could not access a buffer
 * @bb: busy bit indicator, set only if SIGA-w/wt could not access a buffer
 * @fc: function code to perform
 * @fc: function code to perform
 *
 *
 * Returns cc or QDIO_ERROR_SIGA_ACCESS_EXCEPTION.
 * Returns condition code.
 * Note: For IQDC unicast queues only the highest priority queue is processed.
 * Note: For IQDC unicast queues only the highest priority queue is processed.
 */
 */
static inline int do_siga_output(unsigned long schid, unsigned long mask,
static inline int do_siga_output(unsigned long schid, unsigned long mask,
@@ -85,18 +85,16 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
	register unsigned long __schid asm("1") = schid;
	register unsigned long __schid asm("1") = schid;
	register unsigned long __mask asm("2") = mask;
	register unsigned long __mask asm("2") = mask;
	register unsigned long __aob asm("3") = aob;
	register unsigned long __aob asm("3") = aob;
	int cc = QDIO_ERROR_SIGA_ACCESS_EXCEPTION;
	int cc;


	asm volatile(
	asm volatile(
		"	siga	0\n"
		"	siga	0\n"
		"0:	ipm	%0\n"
		"	ipm	%0\n"
		"	srl	%0,28\n"
		"	srl	%0,28\n"
		"1:\n"
		: "=d" (cc), "+d" (__fc), "+d" (__aob)
		EX_TABLE(0b, 1b)
		: "d" (__schid), "d" (__mask)
		: "+d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask),
		: "cc");
		  "+d" (__aob)
	*bb = __fc >> 31;
		: : "cc", "memory");
	*bb = ((unsigned int) __fc) >> 31;
	return cc;
	return cc;
}
}


@@ -167,7 +165,7 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,


	DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
	DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
	DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
	DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
	q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
	q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE,
		   q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
		   q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
	return 0;
	return 0;
}
}
@@ -215,7 +213,7 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,


	DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
	DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
	DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
	DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
	q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
	q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE,
		   q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
		   q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
	return 0;
	return 0;
}
}
@@ -313,7 +311,7 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output,
	cc = do_siga_sync(schid, output, input, fc);
	cc = do_siga_sync(schid, output, input, fc);
	if (unlikely(cc))
	if (unlikely(cc))
		DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc);
		DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc);
	return cc;
	return (cc) ? -EIO : 0;
}
}


static inline int qdio_siga_sync_q(struct qdio_q *q)
static inline int qdio_siga_sync_q(struct qdio_q *q)
@@ -384,7 +382,7 @@ static inline int qdio_siga_input(struct qdio_q *q)
	cc = do_siga_input(schid, q->mask, fc);
	cc = do_siga_input(schid, q->mask, fc);
	if (unlikely(cc))
	if (unlikely(cc))
		DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc);
		DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc);
	return cc;
	return (cc) ? -EIO : 0;
}
}


#define qdio_siga_sync_out(q) qdio_siga_sync(q, ~0U, 0)
#define qdio_siga_sync_out(q) qdio_siga_sync(q, ~0U, 0)
@@ -443,7 +441,7 @@ static void process_buffer_error(struct qdio_q *q, int count)
	unsigned char state = (q->is_input_q) ? SLSB_P_INPUT_NOT_INIT :
	unsigned char state = (q->is_input_q) ? SLSB_P_INPUT_NOT_INIT :
					SLSB_P_OUTPUT_NOT_INIT;
					SLSB_P_OUTPUT_NOT_INIT;


	q->qdio_error |= QDIO_ERROR_SLSB_STATE;
	q->qdio_error = QDIO_ERROR_SLSB_STATE;


	/* special handling for no target buffer empty */
	/* special handling for no target buffer empty */
	if ((!q->is_input_q &&
	if ((!q->is_input_q &&
@@ -575,7 +573,7 @@ static int qdio_inbound_q_moved(struct qdio_q *q)


	bufnr = get_inbound_buffer_frontier(q);
	bufnr = get_inbound_buffer_frontier(q);


	if ((bufnr != q->last_move) || q->qdio_error) {
	if (bufnr != q->last_move) {
		q->last_move = bufnr;
		q->last_move = bufnr;
		if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR)
		if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR)
			q->u.in.timestamp = get_clock();
			q->u.in.timestamp = get_clock();
@@ -863,7 +861,7 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)


	bufnr = get_outbound_buffer_frontier(q);
	bufnr = get_outbound_buffer_frontier(q);


	if ((bufnr != q->last_move) || q->qdio_error) {
	if (bufnr != q->last_move) {
		q->last_move = bufnr;
		q->last_move = bufnr;
		DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
		DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
		return 1;
		return 1;
@@ -894,13 +892,16 @@ static int qdio_kick_outbound_q(struct qdio_q *q, unsigned long aob)
				goto retry;
				goto retry;
			}
			}
			DBF_ERROR("%4x cc2 BBC:%1d", SCH_NO(q), q->nr);
			DBF_ERROR("%4x cc2 BBC:%1d", SCH_NO(q), q->nr);
			cc |= QDIO_ERROR_SIGA_BUSY;
			cc = -EBUSY;
		} else
		} else {
			DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
			DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
			cc = -ENOBUFS;
		}
		break;
		break;
	case 1:
	case 1:
	case 3:
	case 3:
		DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
		DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
		cc = -EIO;
		break;
		break;
	}
	}
	if (retries) {
	if (retries) {
@@ -1090,7 +1091,7 @@ static void qdio_handle_activate_check(struct ccw_device *cdev,
	}
	}


	count = sub_buf(q->first_to_check, q->first_to_kick);
	count = sub_buf(q->first_to_check, q->first_to_kick);
	q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
	q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE,
		   q->nr, q->first_to_kick, count, irq_ptr->int_parm);
		   q->nr, q->first_to_kick, count, irq_ptr->int_parm);
no_handler:
no_handler:
	qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
	qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
@@ -1691,7 +1692,7 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
		      "do%02x b:%02x c:%02x", callflags, bufnr, count);
		      "do%02x b:%02x c:%02x", callflags, bufnr, count);


	if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
	if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
		return -EBUSY;
		return -EIO;
	if (!count)
	if (!count)
		return 0;
		return 0;
	if (callflags & QDIO_FLAG_SYNC_INPUT)
	if (callflags & QDIO_FLAG_SYNC_INPUT)
+2 −2
Original line number Original line Diff line number Diff line
@@ -3339,7 +3339,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
	if (rc) {
	if (rc) {
		queue->card->stats.tx_errors += count;
		queue->card->stats.tx_errors += count;
		/* ignore temporary SIGA errors without busy condition */
		/* ignore temporary SIGA errors without busy condition */
		if (rc == QDIO_ERROR_SIGA_TARGET)
		if (rc == -ENOBUFS)
			return;
			return;
		QETH_CARD_TEXT(queue->card, 2, "flushbuf");
		QETH_CARD_TEXT(queue->card, 2, "flushbuf");
		QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no);
		QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no);
@@ -3533,7 +3533,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,
	int i;
	int i;


	QETH_CARD_TEXT(card, 6, "qdouhdl");
	QETH_CARD_TEXT(card, 6, "qdouhdl");
	if (qdio_error & QDIO_ERROR_ACTIVATE_CHECK_CONDITION) {
	if (qdio_error & QDIO_ERROR_FATAL) {
		QETH_CARD_TEXT(card, 2, "achkcond");
		QETH_CARD_TEXT(card, 2, "achkcond");
		netif_stop_queue(card->dev);
		netif_stop_queue(card->dev);
		qeth_schedule_recovery(card);
		qeth_schedule_recovery(card);