mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-18 15:48:59 +00:00
[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: Jan Glauber <jang@linux.vnet.ibm.com>
This commit is contained in:
parent
9e890ad880
commit
d303b6fd85
@ -314,6 +314,7 @@ 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
|
||||
|
@ -247,7 +247,6 @@ struct qdio_q {
|
||||
|
||||
struct qdio_irq *irq_ptr;
|
||||
struct tasklet_struct tasklet;
|
||||
spinlock_t lock;
|
||||
|
||||
/* error condition during a data transfer */
|
||||
unsigned int qdio_error;
|
||||
|
@ -706,13 +706,13 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
|
||||
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;
|
||||
int cc;
|
||||
|
||||
if (!need_siga_out(q))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
|
||||
qdio_perf_stat_inc(&perf_stats.siga_out);
|
||||
@ -724,19 +724,16 @@ static void qdio_kick_outbound_q(struct qdio_q *q)
|
||||
case 2:
|
||||
if (busy_bit) {
|
||||
DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
|
||||
q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY;
|
||||
} else {
|
||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d",
|
||||
q->nr);
|
||||
q->qdio_error = cc;
|
||||
}
|
||||
cc |= QDIO_ERROR_SIGA_BUSY;
|
||||
} else
|
||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
|
||||
q->qdio_error = cc;
|
||||
break;
|
||||
}
|
||||
return cc;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
|
||||
spin_lock_irqsave(&q->lock, flags);
|
||||
|
||||
BUG_ON(atomic_read(&q->nr_buf_used) < 0);
|
||||
|
||||
if (qdio_outbound_q_moved(q))
|
||||
qdio_kick_outbound_handler(q);
|
||||
|
||||
spin_unlock_irqrestore(&q->lock, flags);
|
||||
|
||||
if (queue_type(q) == QDIO_ZFCP_QFMT)
|
||||
if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
|
||||
goto sched;
|
||||
@ -1457,10 +1448,10 @@ static inline int buf_in_between(int bufnr, int start, int count)
|
||||
* @bufnr: first buffer to process
|
||||
* @count: how many buffers are emptied
|
||||
*/
|
||||
static void handle_inbound(struct qdio_q *q, unsigned int callflags,
|
||||
int bufnr, int count)
|
||||
static int handle_inbound(struct qdio_q *q, unsigned int callflags,
|
||||
int bufnr, int count)
|
||||
{
|
||||
int used, cc, diff;
|
||||
int used, diff;
|
||||
|
||||
if (!q->u.in.polling)
|
||||
goto set;
|
||||
@ -1497,13 +1488,11 @@ set:
|
||||
|
||||
/* no need to signal as long as the adapter had free buffers */
|
||||
if (used)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (need_siga_in(q)) {
|
||||
cc = qdio_siga_input(q);
|
||||
if (cc)
|
||||
q->qdio_error = cc;
|
||||
}
|
||||
if (need_siga_in(q))
|
||||
return qdio_siga_input(q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1513,11 +1502,11 @@ set:
|
||||
* @bufnr: first buffer to process
|
||||
* @count: how many buffers are filled
|
||||
*/
|
||||
static void handle_outbound(struct qdio_q *q, unsigned int callflags,
|
||||
int bufnr, int count)
|
||||
static int handle_outbound(struct qdio_q *q, unsigned int callflags,
|
||||
int bufnr, int count)
|
||||
{
|
||||
unsigned char state;
|
||||
int used;
|
||||
int used, rc = 0;
|
||||
|
||||
qdio_perf_stat_inc(&perf_stats.outbound_handler);
|
||||
|
||||
@ -1532,27 +1521,26 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
|
||||
|
||||
if (queue_type(q) == QDIO_IQDIO_QFMT) {
|
||||
if (multicast_outbound(q))
|
||||
qdio_kick_outbound_q(q);
|
||||
rc = qdio_kick_outbound_q(q);
|
||||
else
|
||||
if ((q->irq_ptr->ssqd_desc.mmwc > 1) &&
|
||||
(count > 1) &&
|
||||
(count <= q->irq_ptr->ssqd_desc.mmwc)) {
|
||||
/* exploit enhanced SIGA */
|
||||
q->u.out.use_enh_siga = 1;
|
||||
qdio_kick_outbound_q(q);
|
||||
rc = qdio_kick_outbound_q(q);
|
||||
} else {
|
||||
/*
|
||||
* One siga-w per buffer required for unicast
|
||||
* HiperSockets.
|
||||
*/
|
||||
q->u.out.use_enh_siga = 0;
|
||||
while (count--)
|
||||
qdio_kick_outbound_q(q);
|
||||
while (count--) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1564,13 +1552,14 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
|
||||
/* try to fast requeue buffers */
|
||||
get_buf_state(q, prev_buf(bufnr), &state, 0);
|
||||
if (state != SLSB_CU_OUTPUT_PRIMED)
|
||||
qdio_kick_outbound_q(q);
|
||||
rc = qdio_kick_outbound_q(q);
|
||||
else {
|
||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req");
|
||||
qdio_perf_stat_inc(&perf_stats.fast_requeue);
|
||||
}
|
||||
out:
|
||||
tasklet_schedule(&q->tasklet);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1609,14 +1598,12 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
|
||||
return -EBUSY;
|
||||
|
||||
if (callflags & QDIO_FLAG_SYNC_INPUT)
|
||||
handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr,
|
||||
count);
|
||||
return handle_inbound(irq_ptr->input_qs[q_nr],
|
||||
callflags, bufnr, count);
|
||||
else if (callflags & QDIO_FLAG_SYNC_OUTPUT)
|
||||
handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr,
|
||||
count);
|
||||
else
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
return handle_outbound(irq_ptr->output_qs[q_nr],
|
||||
callflags, bufnr, count);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(do_QDIO);
|
||||
|
||||
|
@ -117,7 +117,6 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
|
||||
q->mask = 1 << (31 - i);
|
||||
q->nr = i;
|
||||
q->handler = handler;
|
||||
spin_lock_init(&q->lock);
|
||||
}
|
||||
|
||||
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,
|
||||
struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err)
|
||||
{
|
||||
int sbalf15 = buffer->buffer->element[15].flags & 0xff;
|
||||
int cc = qdio_err & 3;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 6, "hdsnderr");
|
||||
qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr");
|
||||
switch (cc) {
|
||||
case 0:
|
||||
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;
|
||||
}
|
||||
|
||||
if (!qdio_err)
|
||||
return QETH_SEND_ERROR_NONE;
|
||||
case 2:
|
||||
if (qdio_err & QDIO_ERROR_SIGA_BUSY) {
|
||||
QETH_DBF_TEXT(TRACE, 1, "SIGAcc2B");
|
||||
QETH_DBF_TEXT_(TRACE, 1, "%s", CARD_BUS_ID(card));
|
||||
return QETH_SEND_ERROR_KICK_IT;
|
||||
}
|
||||
if ((sbalf15 >= 15) && (sbalf15 <= 31))
|
||||
return QETH_SEND_ERROR_RETRY;
|
||||
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;
|
||||
}
|
||||
|
||||
if ((sbalf15 >= 15) && (sbalf15 <= 31))
|
||||
return QETH_SEND_ERROR_RETRY;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2862,10 +2843,14 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
|
||||
qeth_get_micros() -
|
||||
queue->card->perf_stats.outbound_do_qdio_start_time;
|
||||
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, " err%d", rc);
|
||||
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
|
||||
* happens something is really wrong -> recover */
|
||||
qeth_schedule_recovery(queue->card);
|
||||
@ -2940,13 +2925,7 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,
|
||||
}
|
||||
for (i = first_element; i < (first_element + count); ++i) {
|
||||
buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
|
||||
/*we only handle the KICK_IT error by doing a recovery */
|
||||
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_handle_send_error(card, buffer, qdio_error);
|
||||
qeth_clear_output_buffer(queue, buffer);
|
||||
}
|
||||
atomic_sub(count, &queue->used_buffers);
|
||||
|
Loading…
x
Reference in New Issue
Block a user