ソースを参照

[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>
Jan Glauber 16 年 前
コミット
d303b6fd85

+ 1 - 0
arch/s390/include/asm/qdio.h

@@ -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

+ 0 - 1
drivers/s390/cio/qdio.h

@@ -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;

+ 30 - 43
drivers/s390/cio/qdio_main.c

@@ -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);
 

+ 0 - 1
drivers/s390/cio/qdio_setup.c

@@ -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,

+ 17 - 38
drivers/s390/net/qeth_core_main.c

@@ -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);