[S390] cio: Allow adapter interrupt handlers per isc.

Enhance the adapter interruption API so that device drivers can
register a handler for a specific interruption subclass. This
will allow different device drivers to move to differently
prioritized subclasses in order to avoid congestion.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index b7a07a8..fe6cea1 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -15,6 +15,7 @@
 #include <linux/rcupdate.h>
 
 #include <asm/airq.h>
+#include <asm/isc.h>
 
 #include "cio.h"
 #include "cio_debug.h"
@@ -33,15 +34,15 @@
 	void *drv_data;
 };
 
-static union indicator_t indicators;
-static struct airq_t *airqs[NR_AIRQS];
+static union indicator_t indicators[MAX_ISC];
+static struct airq_t *airqs[MAX_ISC][NR_AIRQS];
 
-static int register_airq(struct airq_t *airq)
+static int register_airq(struct airq_t *airq, u8 isc)
 {
 	int i;
 
 	for (i = 0; i < NR_AIRQS; i++)
-		if (!cmpxchg(&airqs[i], NULL, airq))
+		if (!cmpxchg(&airqs[isc][i], NULL, airq))
 			return i;
 	return -ENOMEM;
 }
@@ -50,18 +51,21 @@
  * s390_register_adapter_interrupt() - register adapter interrupt handler
  * @handler: adapter handler to be registered
  * @drv_data: driver data passed with each call to the handler
+ * @isc: isc for which the handler should be called
  *
  * Returns:
  *  Pointer to the indicator to be used on success
  *  ERR_PTR() if registration failed
  */
 void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
-				      void *drv_data)
+				      void *drv_data, u8 isc)
 {
 	struct airq_t *airq;
 	char dbf_txt[16];
 	int ret;
 
+	if (isc > MAX_ISC)
+		return ERR_PTR(-EINVAL);
 	airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
 	if (!airq) {
 		ret = -ENOMEM;
@@ -69,34 +73,35 @@
 	}
 	airq->handler = handler;
 	airq->drv_data = drv_data;
-	ret = register_airq(airq);
-	if (ret < 0)
-		kfree(airq);
+
+	ret = register_airq(airq, isc);
 out:
 	snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
 	CIO_TRACE_EVENT(4, dbf_txt);
-	if (ret < 0)
+	if (ret < 0) {
+		kfree(airq);
 		return ERR_PTR(ret);
-	else
-		return &indicators.byte[ret];
+	} else
+		return &indicators[isc].byte[ret];
 }
 EXPORT_SYMBOL(s390_register_adapter_interrupt);
 
 /**
  * s390_unregister_adapter_interrupt - unregister adapter interrupt handler
  * @ind: indicator for which the handler is to be unregistered
+ * @isc: interruption subclass
  */
-void s390_unregister_adapter_interrupt(void *ind)
+void s390_unregister_adapter_interrupt(void *ind, u8 isc)
 {
 	struct airq_t *airq;
 	char dbf_txt[16];
 	int i;
 
-	i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]);
+	i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]);
 	snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
 	CIO_TRACE_EVENT(4, dbf_txt);
-	indicators.byte[i] = 0;
-	airq = xchg(&airqs[i], NULL);
+	indicators[isc].byte[i] = 0;
+	airq = xchg(&airqs[isc][i], NULL);
 	/*
 	 * Allow interrupts to complete. This will ensure that the airq handle
 	 * is no longer referenced by any interrupt handler.
@@ -108,7 +113,7 @@
 
 #define INDICATOR_MASK	(0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
 
-void do_adapter_IO(void)
+void do_adapter_IO(u8 isc)
 {
 	int w;
 	int i;
@@ -120,22 +125,22 @@
 	 * fetch operations.
 	 */
 	for (w = 0; w < NR_AIRQ_WORDS; w++) {
-		word = indicators.word[w];
+		word = indicators[isc].word[w];
 		i = w * NR_AIRQS_PER_WORD;
 		/*
 		 * Check bytes within word for active indicators.
 		 */
 		while (word) {
 			if (word & INDICATOR_MASK) {
-				airq = airqs[i];
+				airq = airqs[isc][i];
 				if (likely(airq))
-					airq->handler(&indicators.byte[i],
+					airq->handler(&indicators[isc].byte[i],
 						      airq->drv_data);
 				else
 					/*
 					 * Reset ill-behaved indicator.
 					 */
-					indicators.byte[i] = 0;
+					indicators[isc].byte[i] = 0;
 			}
 			word <<= 8;
 			i++;