qdio: extend API to allow polling

Extend the qdio API to allow polling in the upper-layer driver. This
is needed by qeth to use NAPI.

To use the new interface the upper-layer driver must specify the
queue_start_poll(). This callback is used to signal the upper-layer
driver that is has initiative and must process the inbound queue by
calling qdio_get_next_buffers(). If the upper-layer driver wants to
stop polling it calls qdio_start_irq().

Since adapter interrupts are not completely stoppable qdio implements
a software bit QDIO_QUEUE_IRQS_DISABLED to safely disable interrupts for an
input queue.

The old interface is preserved and will be used as is by zfcp.

Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 8daf1b9..752dbee 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -25,24 +25,20 @@
  */
 #define TIQDIO_NR_NONSHARED_IND		63
 #define TIQDIO_NR_INDICATORS		(TIQDIO_NR_NONSHARED_IND + 1)
-#define TIQDIO_SHARED_IND		63
 
 /* list of thin interrupt input queues */
 static LIST_HEAD(tiq_list);
 DEFINE_MUTEX(tiq_list_lock);
 
 /* adapter local summary indicator */
-static unsigned char *tiqdio_alsi;
+static u8 *tiqdio_alsi;
 
-/* device state change indicators */
-struct indicator_t {
-	u32 ind;	/* u32 because of compare-and-swap performance */
-	atomic_t count; /* use count, 0 or 1 for non-shared indicators */
-};
-static struct indicator_t *q_indicators;
+struct indicator_t *q_indicators;
 
 static int css_qdio_omit_svs;
 
+static u64 last_ai_time;
+
 static inline unsigned long do_clear_global_summary(void)
 {
 	register unsigned long __fn asm("1") = 3;
@@ -116,59 +112,73 @@
 	}
 }
 
-static inline int shared_ind(struct qdio_irq *irq_ptr)
+static inline int shared_ind_used(void)
 {
-	return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
+	return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count);
 }
 
 /**
  * tiqdio_thinint_handler - thin interrupt handler for qdio
- * @ind: pointer to adapter local summary indicator
- * @drv_data: NULL
+ * @alsi: pointer to adapter local summary indicator
+ * @data: NULL
  */
-static void tiqdio_thinint_handler(void *ind, void *drv_data)
+static void tiqdio_thinint_handler(void *alsi, void *data)
 {
 	struct qdio_q *q;
 
+	last_ai_time = S390_lowcore.int_clock;
+
 	/*
 	 * SVS only when needed: issue SVS to benefit from iqdio interrupt
-	 * avoidance (SVS clears adapter interrupt suppression overwrite)
+	 * avoidance (SVS clears adapter interrupt suppression overwrite).
 	 */
 	if (!css_qdio_omit_svs)
 		do_clear_global_summary();
 
-	/*
-	 * reset local summary indicator (tiqdio_alsi) to stop adapter
-	 * interrupts for now
-	 */
-	xchg((u8 *)ind, 0);
+	/* reset local summary indicator */
+	if (shared_ind_used())
+		xchg(tiqdio_alsi, 0);
 
 	/* protect tiq_list entries, only changed in activate or shutdown */
 	rcu_read_lock();
 
 	/* check for work on all inbound thinint queues */
-	list_for_each_entry_rcu(q, &tiq_list, entry)
-		/* only process queues from changed sets */
-		if (*q->irq_ptr->dsci) {
-			qperf_inc(q, adapter_int);
+	list_for_each_entry_rcu(q, &tiq_list, entry) {
 
+		/* only process queues from changed sets */
+		if (!*q->irq_ptr->dsci)
+			continue;
+
+		if (q->u.in.queue_start_poll) {
+			/* skip if polling is enabled or already in work */
+			if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
+					     &q->u.in.queue_irq_state)) {
+				qperf_inc(q, int_discarded);
+				continue;
+			}
+
+			/* avoid dsci clear here, done after processing */
+			q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
+						 q->irq_ptr->int_parm);
+		} else {
 			/* only clear it if the indicator is non-shared */
 			if (!shared_ind(q->irq_ptr))
 				xchg(q->irq_ptr->dsci, 0);
 			/*
-			 * don't call inbound processing directly since
-			 * that could starve other thinint queues
+			 * Call inbound processing but not directly
+			 * since that could starve other thinint queues.
 			 */
 			tasklet_schedule(&q->tasklet);
 		}
-
+		qperf_inc(q, adapter_int);
+	}
 	rcu_read_unlock();
 
 	/*
-	 * if we used the shared indicator clear it now after all queues
-	 * were processed
+	 * If the shared indicator was used clear it now after all queues
+	 * were processed.
 	 */
-	if (atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) {
+	if (shared_ind_used()) {
 		xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
 
 		/* prevent racing */