Commit d303b6fd authored by Jan Glauber's avatar Jan Glauber Committed by Martin Schwidefsky

[S390] qdio: report SIGA errors directly

Errors from SIGA instructions are stored in the per queue qdio_error
and reported back when the queue handler is called. That opens a race
when multiple error conditions occur simultanously.

Report SIGA errors immediately in the return value of do_QDIO so the
upper layer can react and SIGA errors no longer interfere with other
errors.

Move the SIGA error handling in qeth from the outbound handler to
qeth_flush_buffers.
Signed-off-by: default avatarJan Glauber <jang@linux.vnet.ibm.com>
parent 9e890ad8
...@@ -314,6 +314,7 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int, ...@@ -314,6 +314,7 @@ 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_SIGA_ACCESS_EXCEPTION 0x10 #define QDIO_ERROR_SIGA_ACCESS_EXCEPTION 0x10
#define QDIO_ERROR_SIGA_BUSY 0x20 #define QDIO_ERROR_SIGA_BUSY 0x20
#define QDIO_ERROR_ACTIVATE_CHECK_CONDITION 0x40 #define QDIO_ERROR_ACTIVATE_CHECK_CONDITION 0x40
......
...@@ -247,7 +247,6 @@ struct qdio_q { ...@@ -247,7 +247,6 @@ struct qdio_q {
struct qdio_irq *irq_ptr; struct qdio_irq *irq_ptr;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
spinlock_t lock;
/* error condition during a data transfer */ /* error condition during a data transfer */
unsigned int qdio_error; unsigned int qdio_error;
......
...@@ -706,13 +706,13 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) ...@@ -706,13 +706,13 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
return 0; return 0;
} }
static void qdio_kick_outbound_q(struct qdio_q *q) static int qdio_kick_outbound_q(struct qdio_q *q)
{ {
unsigned int busy_bit; unsigned int busy_bit;
int cc; int cc;
if (!need_siga_out(q)) if (!need_siga_out(q))
return; return 0;
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_out); qdio_perf_stat_inc(&perf_stats.siga_out);
...@@ -724,19 +724,16 @@ static void qdio_kick_outbound_q(struct qdio_q *q) ...@@ -724,19 +724,16 @@ static void qdio_kick_outbound_q(struct qdio_q *q)
case 2: case 2:
if (busy_bit) { if (busy_bit) {
DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY; cc |= QDIO_ERROR_SIGA_BUSY;
} else { } else
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
q->nr);
q->qdio_error = cc;
}
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);
q->qdio_error = cc;
break; break;
} }
return cc;
} }
static void qdio_kick_outbound_handler(struct qdio_q *q) static void qdio_kick_outbound_handler(struct qdio_q *q)
...@@ -766,18 +763,12 @@ static void qdio_kick_outbound_handler(struct qdio_q *q) ...@@ -766,18 +763,12 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)
static void __qdio_outbound_processing(struct qdio_q *q) static void __qdio_outbound_processing(struct qdio_q *q)
{ {
unsigned long flags;
qdio_perf_stat_inc(&perf_stats.tasklet_outbound); qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
spin_lock_irqsave(&q->lock, flags);
BUG_ON(atomic_read(&q->nr_buf_used) < 0); BUG_ON(atomic_read(&q->nr_buf_used) < 0);
if (qdio_outbound_q_moved(q)) if (qdio_outbound_q_moved(q))
qdio_kick_outbound_handler(q); qdio_kick_outbound_handler(q);
spin_unlock_irqrestore(&q->lock, flags);
if (queue_type(q) == QDIO_ZFCP_QFMT) if (queue_type(q) == QDIO_ZFCP_QFMT)
if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
goto sched; goto sched;
...@@ -1457,10 +1448,10 @@ static inline int buf_in_between(int bufnr, int start, int count) ...@@ -1457,10 +1448,10 @@ static inline int buf_in_between(int bufnr, int start, int count)
* @bufnr: first buffer to process * @bufnr: first buffer to process
* @count: how many buffers are emptied * @count: how many buffers are emptied
*/ */
static void handle_inbound(struct qdio_q *q, unsigned int callflags, static int handle_inbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count) int bufnr, int count)
{ {
int used, cc, diff; int used, diff;
if (!q->u.in.polling) if (!q->u.in.polling)
goto set; goto set;
...@@ -1497,13 +1488,11 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags, ...@@ -1497,13 +1488,11 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags,
/* no need to signal as long as the adapter had free buffers */ /* no need to signal as long as the adapter had free buffers */
if (used) if (used)
return; return 0;
if (need_siga_in(q)) { if (need_siga_in(q))
cc = qdio_siga_input(q); return qdio_siga_input(q);
if (cc) return 0;
q->qdio_error = cc;
}
} }
/** /**
...@@ -1513,11 +1502,11 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags, ...@@ -1513,11 +1502,11 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags,
* @bufnr: first buffer to process * @bufnr: first buffer to process
* @count: how many buffers are filled * @count: how many buffers are filled
*/ */
static void handle_outbound(struct qdio_q *q, unsigned int callflags, static int handle_outbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count) int bufnr, int count)
{ {
unsigned char state; unsigned char state;
int used; int used, rc = 0;
qdio_perf_stat_inc(&perf_stats.outbound_handler); qdio_perf_stat_inc(&perf_stats.outbound_handler);
...@@ -1532,27 +1521,26 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, ...@@ -1532,27 +1521,26 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
if (queue_type(q) == QDIO_IQDIO_QFMT) { if (queue_type(q) == QDIO_IQDIO_QFMT) {
if (multicast_outbound(q)) if (multicast_outbound(q))
qdio_kick_outbound_q(q); rc = qdio_kick_outbound_q(q);
else else
if ((q->irq_ptr->ssqd_desc.mmwc > 1) && if ((q->irq_ptr->ssqd_desc.mmwc > 1) &&
(count > 1) && (count > 1) &&
(count <= q->irq_ptr->ssqd_desc.mmwc)) { (count <= q->irq_ptr->ssqd_desc.mmwc)) {
/* exploit enhanced SIGA */ /* exploit enhanced SIGA */
q->u.out.use_enh_siga = 1; q->u.out.use_enh_siga = 1;
qdio_kick_outbound_q(q); rc = qdio_kick_outbound_q(q);
} else { } else {
/* /*
* One siga-w per buffer required for unicast * One siga-w per buffer required for unicast
* HiperSockets. * HiperSockets.
*/ */
q->u.out.use_enh_siga = 0; q->u.out.use_enh_siga = 0;
while (count--) while (count--) {
qdio_kick_outbound_q(q); rc = qdio_kick_outbound_q(q);
if (rc)
goto out;
}
} }
/* report CC=2 conditions synchronously */
if (q->qdio_error)
__qdio_outbound_processing(q);
goto out; goto out;
} }
...@@ -1564,13 +1552,14 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, ...@@ -1564,13 +1552,14 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
/* try to fast requeue buffers */ /* try to fast requeue buffers */
get_buf_state(q, prev_buf(bufnr), &state, 0); get_buf_state(q, prev_buf(bufnr), &state, 0);
if (state != SLSB_CU_OUTPUT_PRIMED) if (state != SLSB_CU_OUTPUT_PRIMED)
qdio_kick_outbound_q(q); rc = qdio_kick_outbound_q(q);
else { else {
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req"); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req");
qdio_perf_stat_inc(&perf_stats.fast_requeue); qdio_perf_stat_inc(&perf_stats.fast_requeue);
} }
out: out:
tasklet_schedule(&q->tasklet); tasklet_schedule(&q->tasklet);
return rc;
} }
/** /**
...@@ -1609,14 +1598,12 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, ...@@ -1609,14 +1598,12 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
return -EBUSY; return -EBUSY;
if (callflags & QDIO_FLAG_SYNC_INPUT) if (callflags & QDIO_FLAG_SYNC_INPUT)
handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr, return handle_inbound(irq_ptr->input_qs[q_nr],
count); callflags, bufnr, count);
else if (callflags & QDIO_FLAG_SYNC_OUTPUT) else if (callflags & QDIO_FLAG_SYNC_OUTPUT)
handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr, return handle_outbound(irq_ptr->output_qs[q_nr],
count); callflags, bufnr, count);
else return -EINVAL;
return -EINVAL;
return 0;
} }
EXPORT_SYMBOL_GPL(do_QDIO); EXPORT_SYMBOL_GPL(do_QDIO);
......
...@@ -117,7 +117,6 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr, ...@@ -117,7 +117,6 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
q->mask = 1 << (31 - i); q->mask = 1 << (31 - i);
q->nr = i; q->nr = i;
q->handler = handler; q->handler = handler;
spin_lock_init(&q->lock);
} }
static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
......
...@@ -2693,40 +2693,21 @@ static int qeth_handle_send_error(struct qeth_card *card, ...@@ -2693,40 +2693,21 @@ static int qeth_handle_send_error(struct qeth_card *card,
struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err)
{ {
int sbalf15 = buffer->buffer->element[15].flags & 0xff; int sbalf15 = buffer->buffer->element[15].flags & 0xff;
int cc = qdio_err & 3;
QETH_DBF_TEXT(TRACE, 6, "hdsnderr"); QETH_DBF_TEXT(TRACE, 6, "hdsnderr");
qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr"); qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr");
switch (cc) {
case 0: if (!qdio_err)
if (qdio_err) {
QETH_DBF_TEXT(TRACE, 1, "lnkfail");
QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
QETH_DBF_TEXT_(TRACE, 1, "%04x %02x",
(u16)qdio_err, (u8)sbalf15);
return QETH_SEND_ERROR_LINK_FAILURE;
}
return QETH_SEND_ERROR_NONE; return QETH_SEND_ERROR_NONE;
case 2:
if (qdio_err & QDIO_ERROR_SIGA_BUSY) { if ((sbalf15 >= 15) && (sbalf15 <= 31))
QETH_DBF_TEXT(TRACE, 1, "SIGAcc2B"); return QETH_SEND_ERROR_RETRY;
QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
return QETH_SEND_ERROR_KICK_IT; QETH_DBF_TEXT(TRACE, 1, "lnkfail");
} QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
if ((sbalf15 >= 15) && (sbalf15 <= 31)) QETH_DBF_TEXT_(TRACE, 1, "%04x %02x",
return QETH_SEND_ERROR_RETRY; (u16)qdio_err, (u8)sbalf15);
return QETH_SEND_ERROR_LINK_FAILURE; return QETH_SEND_ERROR_LINK_FAILURE;
/* look at qdio_error and sbalf 15 */
case 1:
QETH_DBF_TEXT(TRACE, 1, "SIGAcc1");
QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
return QETH_SEND_ERROR_LINK_FAILURE;
case 3:
default:
QETH_DBF_TEXT(TRACE, 1, "SIGAcc3");
QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
return QETH_SEND_ERROR_KICK_IT;
}
} }
/* /*
...@@ -2862,10 +2843,14 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, ...@@ -2862,10 +2843,14 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
qeth_get_micros() - qeth_get_micros() -
queue->card->perf_stats.outbound_do_qdio_start_time; queue->card->perf_stats.outbound_do_qdio_start_time;
if (rc) { if (rc) {
queue->card->stats.tx_errors += count;
/* ignore temporary SIGA errors without busy condition */
if (rc == QDIO_ERROR_SIGA_TARGET)
return;
QETH_DBF_TEXT(TRACE, 2, "flushbuf"); QETH_DBF_TEXT(TRACE, 2, "flushbuf");
QETH_DBF_TEXT_(TRACE, 2, " err%d", rc); QETH_DBF_TEXT_(TRACE, 2, " err%d", rc);
QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_DDEV_ID(queue->card)); QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_DDEV_ID(queue->card));
queue->card->stats.tx_errors += count;
/* this must not happen under normal circumstances. if it /* this must not happen under normal circumstances. if it
* happens something is really wrong -> recover */ * happens something is really wrong -> recover */
qeth_schedule_recovery(queue->card); qeth_schedule_recovery(queue->card);
...@@ -2940,13 +2925,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, ...@@ -2940,13 +2925,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,
} }
for (i = first_element; i < (first_element + count); ++i) { for (i = first_element; i < (first_element + count); ++i) {
buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]; buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
/*we only handle the KICK_IT error by doing a recovery */ qeth_handle_send_error(card, buffer, qdio_error);
if (qeth_handle_send_error(card, buffer, qdio_error)
== QETH_SEND_ERROR_KICK_IT){
netif_stop_queue(card->dev);
qeth_schedule_recovery(card);
return;
}
qeth_clear_output_buffer(queue, buffer); qeth_clear_output_buffer(queue, buffer);
} }
atomic_sub(count, &queue->used_buffers); atomic_sub(count, &queue->used_buffers);
......
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