[S390] qdio: report SIGA errors directly
authorJan Glauber <jang@linux.vnet.ibm.com>
Thu, 26 Mar 2009 14:24:31 +0000 (15:24 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 26 Mar 2009 14:24:22 +0000 (15:24 +0100)
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>
arch/s390/include/asm/qdio.h
drivers/s390/cio/qdio.h
drivers/s390/cio/qdio_main.c
drivers/s390/cio/qdio_setup.c
drivers/s390/net/qeth_core_main.c

index 27fc1746de1578a7611f97ce92e252d2227ed3c4..402d6dcf0d26dbff39470e728432b4bbd0896911 100644 (file)
@@ -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
index 57807f5ffe840889f631e0ac24e6cdfcd2c53d72..41171d741f38faabde03324e1e22c9194abc4223 100644 (file)
@@ -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;
index 31b9318149bac951ae2e1aa25f295a24a1b4e876..e53ac67e1e48fab708ccd849f166b014b1dfa9d9 100644 (file)
@@ -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);
 
index c08356b95bf5f9d2f41ef93e77ecd1b360781908..18d54fc21ce90b43b9e43418a4b3a47e9e6e15ae 100644 (file)
@@ -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,
index d1b5bebea7fbb3a36ad29b5eff6f231ce2a3f4d5..2489bcebf5ec728ab662261ec5d08a6d72124e99 100644 (file)
@@ -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);