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: Ursula Braun <ursula.braun@de.ibm.com> Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
050276ab8c
commit
1549d13f4c
3 changed files with 31 additions and 28 deletions
|
@ -325,11 +325,13 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int,
|
|||
int, int, unsigned long);
|
||||
|
||||
/* qdio errors reported to the upper-layer program */
|
||||
#define QDIO_ERROR_SIGA_TARGET 0x02
|
||||
#define QDIO_ERROR_SIGA_ACCESS_EXCEPTION 0x10
|
||||
#define QDIO_ERROR_SIGA_BUSY 0x20
|
||||
#define QDIO_ERROR_ACTIVATE_CHECK_CONDITION 0x40
|
||||
#define QDIO_ERROR_SLSB_STATE 0x80
|
||||
#define QDIO_ERROR_ACTIVATE 0x0001
|
||||
#define QDIO_ERROR_GET_BUF_STATE 0x0002
|
||||
#define QDIO_ERROR_SET_BUF_STATE 0x0004
|
||||
#define QDIO_ERROR_SLSB_STATE 0x0100
|
||||
|
||||
#define QDIO_ERROR_FATAL 0x00ff
|
||||
#define QDIO_ERROR_TEMPORARY 0xff00
|
||||
|
||||
/* for qdio_cleanup */
|
||||
#define QDIO_FLAG_CLEANUP_USING_CLEAR 0x01
|
||||
|
|
|
@ -63,7 +63,7 @@ static inline int do_siga_input(unsigned long schid, unsigned int mask,
|
|||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (cc)
|
||||
: "d" (__fc), "d" (__schid), "d" (__mask) : "cc", "memory");
|
||||
: "d" (__fc), "d" (__schid), "d" (__mask) : "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
|
||||
* @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.
|
||||
*/
|
||||
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 __mask asm("2") = mask;
|
||||
register unsigned long __aob asm("3") = aob;
|
||||
int cc = QDIO_ERROR_SIGA_ACCESS_EXCEPTION;
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
" siga 0\n"
|
||||
"0: ipm %0\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"1:\n"
|
||||
EX_TABLE(0b, 1b)
|
||||
: "+d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask),
|
||||
"+d" (__aob)
|
||||
: : "cc", "memory");
|
||||
*bb = ((unsigned int) __fc) >> 31;
|
||||
: "=d" (cc), "+d" (__fc), "+d" (__aob)
|
||||
: "d" (__schid), "d" (__mask)
|
||||
: "cc");
|
||||
*bb = __fc >> 31;
|
||||
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("%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);
|
||||
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("%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);
|
||||
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);
|
||||
if (unlikely(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)
|
||||
|
@ -384,7 +382,7 @@ static inline int qdio_siga_input(struct qdio_q *q)
|
|||
cc = do_siga_input(schid, q->mask, fc);
|
||||
if (unlikely(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)
|
||||
|
@ -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 :
|
||||
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 */
|
||||
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);
|
||||
|
||||
if ((bufnr != q->last_move) || q->qdio_error) {
|
||||
if (bufnr != q->last_move) {
|
||||
q->last_move = bufnr;
|
||||
if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR)
|
||||
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);
|
||||
|
||||
if ((bufnr != q->last_move) || q->qdio_error) {
|
||||
if (bufnr != q->last_move) {
|
||||
q->last_move = bufnr;
|
||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
|
||||
return 1;
|
||||
|
@ -894,13 +892,16 @@ static int qdio_kick_outbound_q(struct qdio_q *q, unsigned long aob)
|
|||
goto retry;
|
||||
}
|
||||
DBF_ERROR("%4x cc2 BBC:%1d", SCH_NO(q), q->nr);
|
||||
cc |= QDIO_ERROR_SIGA_BUSY;
|
||||
} else
|
||||
cc = -EBUSY;
|
||||
} else {
|
||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
|
||||
cc = -ENOBUFS;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
|
||||
cc = -EIO;
|
||||
break;
|
||||
}
|
||||
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);
|
||||
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);
|
||||
no_handler:
|
||||
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);
|
||||
|
||||
if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
|
||||
return -EBUSY;
|
||||
return -EIO;
|
||||
if (!count)
|
||||
return 0;
|
||||
if (callflags & QDIO_FLAG_SYNC_INPUT)
|
||||
|
|
|
@ -3339,7 +3339,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
|
|||
if (rc) {
|
||||
queue->card->stats.tx_errors += count;
|
||||
/* ignore temporary SIGA errors without busy condition */
|
||||
if (rc == QDIO_ERROR_SIGA_TARGET)
|
||||
if (rc == -ENOBUFS)
|
||||
return;
|
||||
QETH_CARD_TEXT(queue->card, 2, "flushbuf");
|
||||
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;
|
||||
|
||||
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");
|
||||
netif_stop_queue(card->dev);
|
||||
qeth_schedule_recovery(card);
|
||||
|
|
Loading…
Reference in a new issue