Blob Blame History Raw
From: Julian Wiedmann <jwi@linux.ibm.com>
Date: Tue, 21 Apr 2020 11:35:00 +0100
Subject: s390/qdio: reduce SLSB writes during Input Queue processing
Git-commit: a87ee11607b853a31c8612d9f47b7fe974953b77
Patch-mainline: v5.8-rc2
References: jsc#SLE-13690

Streamline the processing of QDIO Input Queues, and remove some
intermittent SLSB updates (no deleting of old ACKs, no redundant
transitions through NOT_INIT).

Rather than counting ACKs, we now keep track of the whole batch of
SBALs that were completed during the current polling cycle.
Most completed SBALs stay in their initial state (ie. PRIMED or ERROR),
except that the most recent SBAL in each sub-run is ACKed for
IRQ reduction.

The only logic changes happen in inbound_handle_work(), the other
delta is just a renaming of the variables that track the SBAL batch.

Note that in particular we don't need to flip the _oldest_ SBAL to
an idle state (eg. NOT_INIT or ACKed) as a guard against catching our
own tail. Since get_inbound_buffer_frontier() will never scan more than
the remaining nr_buf_used SBALs, this scenario just doesn't occur.

Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 drivers/s390/cio/qdio.h       |  7 +++--
 drivers/s390/cio/qdio_debug.c |  4 +--
 drivers/s390/cio/qdio_main.c  | 59 +++++++++++--------------------------------
 3 files changed, 20 insertions(+), 50 deletions(-)

diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index eb13c479e11d..bb1c8402c67d 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -182,10 +182,9 @@ enum qdio_irq_poll_states {
 };
 
 struct qdio_input_q {
-	/* first ACK'ed buffer */
-	int ack_start;
-	/* how many SBALs are acknowledged */
-	int ack_count;
+	/* Batch of SBALs that we processed while polling the queue: */
+	unsigned int batch_start;
+	unsigned int batch_count;
 	/* last time of noticing incoming data */
 	u64 timestamp;
 };
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index 286b044fb027..da95c923d81a 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -110,8 +110,8 @@ static int qstat_show(struct seq_file *m, void *v)
 	seq_printf(m, "nr_used: %d  ftc: %d\n",
 		   atomic_read(&q->nr_buf_used), q->first_to_check);
 	if (q->is_input_q) {
-		seq_printf(m, "ack start: %d  ack count: %d\n",
-			   q->u.in.ack_start, q->u.in.ack_count);
+		seq_printf(m, "batch start: %u  batch count: %u\n",
+			   q->u.in.batch_start, q->u.in.batch_count);
 		seq_printf(m, "DSCI: %x   IRQs disabled: %u\n",
 			   *(u8 *)q->irq_ptr->dsci,
 			   test_bit(QDIO_IRQ_DISABLED,
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index bb137f962c3f..1aa94683d789 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -400,15 +400,15 @@ int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr,
 
 static inline void qdio_stop_polling(struct qdio_q *q)
 {
-	if (!q->u.in.ack_count)
+	if (!q->u.in.batch_count)
 		return;
 
 	qperf_inc(q, stop_polling);
 
 	/* show the card that we are not polling anymore */
-	set_buf_states(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT,
-		       q->u.in.ack_count);
-	q->u.in.ack_count = 0;
+	set_buf_states(q, q->u.in.batch_start, SLSB_P_INPUT_NOT_INIT,
+		       q->u.in.batch_count);
+	q->u.in.batch_count = 0;
 }
 
 static inline void account_sbals(struct qdio_q *q, unsigned int count)
@@ -448,42 +448,13 @@ static void process_buffer_error(struct qdio_q *q, unsigned int start,
 static inline void inbound_handle_work(struct qdio_q *q, unsigned int start,
 				       int count, bool auto_ack)
 {
-	int new;
+	/* ACK the newest SBAL: */
+	if (!auto_ack)
+		set_buf_state(q, add_buf(start, count - 1), SLSB_P_INPUT_ACK);
 
-	if (auto_ack) {
-		if (!q->u.in.ack_count) {
-			q->u.in.ack_count = count;
-			q->u.in.ack_start = start;
-			return;
-		}
-
-		/* delete the previous ACK's */
-		set_buf_states(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT,
-			       q->u.in.ack_count);
-		q->u.in.ack_count = count;
-		q->u.in.ack_start = start;
-		return;
-	}
-
-	/*
-	 * ACK the newest buffer. The ACK will be removed in qdio_stop_polling
-	 * or by the next inbound run.
-	 */
-	new = add_buf(start, count - 1);
-	set_buf_state(q, new, SLSB_P_INPUT_ACK);
-
-	/* delete the previous ACKs */
-	if (q->u.in.ack_count)
-		set_buf_states(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT,
-			       q->u.in.ack_count);
-
-	q->u.in.ack_count = 1;
-	q->u.in.ack_start = new;
-	count--;
-	if (!count)
-		return;
-	/* need to change ALL buffers to get more interrupts */
-	set_buf_states(q, start, SLSB_P_INPUT_NOT_INIT, count);
+	if (!q->u.in.batch_count)
+		q->u.in.batch_start = start;
+	q->u.in.batch_count += count;
 }
 
 static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start)
@@ -1453,12 +1424,12 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags,
 
 	qperf_inc(q, inbound_call);
 
-	/* If any ACKed SBALs are returned to HW, adjust ACK tracking: */
-	overlap = min(count - sub_buf(q->u.in.ack_start, bufnr),
-		      q->u.in.ack_count);
+	/* If any processed SBALs are returned to HW, adjust our tracking: */
+	overlap = min_t(int, count - sub_buf(q->u.in.batch_start, bufnr),
+			     q->u.in.batch_count);
 	if (overlap > 0) {
-		q->u.in.ack_start = add_buf(q->u.in.ack_start, overlap);
-		q->u.in.ack_count -= overlap;
+		q->u.in.batch_start = add_buf(q->u.in.batch_start, overlap);
+		q->u.in.batch_count -= overlap;
 	}
 
 	count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count);