[PATCH 21/32] leon, genirq: SMP support for PCI peripherals

Daniel Hellstrom daniel at gaisler.com
Thu May 11 14:26:06 UTC 2017


The common interrupt layer for GRLIB PCI perihperals is prepared for SMP
support by this patch. The existing locking (interrupt disabling) is
replaced by a new requirement on the user to implement locking before
calling the genirq API. This approach avoids taking more locks than
necessary.

The split up of the locks also introduces that the user must allocate
memory to describe ISR handlers, to avoid calling malloc()/free() while
possessing a spin-lock and interrupts are globally disabled.
---
 c/src/lib/libbsp/sparc/shared/include/genirq.h | 47 ++++++++++++++++-----
 c/src/lib/libbsp/sparc/shared/irq/genirq.c     | 58 ++++++++++++--------------
 2 files changed, 63 insertions(+), 42 deletions(-)

diff --git a/c/src/lib/libbsp/sparc/shared/include/genirq.h b/c/src/lib/libbsp/sparc/shared/include/genirq.h
index d03b812..673be17 100644
--- a/c/src/lib/libbsp/sparc/shared/include/genirq.h
+++ b/c/src/lib/libbsp/sparc/shared/include/genirq.h
@@ -35,7 +35,7 @@ struct genirq_stats {
 extern genirq_t genirq_init(int number_of_irqs);
 
 /* Free the dynamically allocated memory that the genirq interface has 
- * allocated.
+ * allocated. Also the handlers will be freed.
  *
  * Returns zero on success, otherwise failure.
  */
@@ -47,33 +47,54 @@ extern void genirq_destroy(genirq_t d);
  */
 extern int genirq_check(genirq_t d, int irq);
 
-/* Register shared interrupt handler.
+/* Allocate one ISR handler and initialize it. Input to genirq_register().
  *
- * \param irq    The interrupt number to register ISR on
  * \param isr    The interrupt service routine called upon IRQ
  * \param arg    The argument given to isr() when called.
  *
+ * Returns a pointer on success, on failure NULL is returned.
+ */
+extern void *genirq_alloc_handler(genirq_handler isr, void *arg);
+
+/* Free handler memory */
+#define genirq_free_handler(handler) free(handler)
+
+/* Register shared interrupt handler previously initialized with
+ * genirq_alloc_handler().
+ *
+ * NOTE: internal list structures are accessed and needs to be protected by
+ *       spin-locks/IRQ disable by the user to guarantee a correct behaviour.
+ *
+ * \param irq    The interrupt number to register ISR on
+ * \param handler Install the pre- allocated and initialized handler.
+ *
  * Return Values
  * -1  = Failed
  * 0   = Handler registered Successfully, first handler on this IRQ
  * 1   = Handler registered Successfully, _not_ first handler on this IRQ
  */
-extern int genirq_register(genirq_t d, int irq, genirq_handler isr, void *arg);
+extern int genirq_register(genirq_t d, int irq, void *handler);
 
-/* Unregister an previous registered interrupt handler 
+/* Unregister an previous registered interrupt handler. It is the user's
+ * responsibility to free the handler returned by genirq_unregister().
+ *
+ * NOTE: internal list structures are accessed and needs to be protected by
+ *       spin-locks/IRQ disable by the user to guarantee a correct behaviour.
  *
  * Return Values
- *  -1 = ISR not registered before
- *  0  = ISR unregistered
- *  1  = Unable to unregister enabled ISR
+ * NULL    = ISR not registered before or unable to unregister enabled ISR
+ * Pointer = ISR sucessfully unregistered. Returned is the handler pointer
+ *           previously allocated with genirq_alloc_handler().
  */
-extern int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg);
+extern void *genirq_unregister(genirq_t d, int irq,
+				genirq_handler isr, void *arg);
 
 /* Enables IRQ only for this isr[arg] combination. Records if this 
  * is the first interrupt enable, only then must interrupts be enabled
  * on the interrupt controller.
  *
- * IRQs must be disabled before entering this function.
+ * NOTE: internal list structures are accessed and needs to be protected by
+ *       spin-locks/IRQ disable by the user to guarantee a correct behaviour.
  *
  * Return values
  *  -1 = Failure, for example isr[arg] not registered on this irq
@@ -86,7 +107,8 @@ extern int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg);
  * is the only interrupt handler that is enabled on this IRQ, only then
  * must interrupts be disabled on the interrupt controller.
  *
- * IRQs must be disabled before entering this function.
+ * NOTE: internal list structures are accessed and needs to be protected by
+ *       spin-locks/IRQ disable by the user to guarantee a correct behaviour.
  *
  * Return values
  *  -1 = Failure, for example isr[arg] not registered on this irq
@@ -97,6 +119,9 @@ extern int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg);
 
 /* Must be called by user when an IRQ has fired, the argument 'irq' 
  * is the IRQ number of the IRQ which was fired.
+ *
+ * NOTE: internal list structures are accessed and needs to be protected by
+ *       spin-locks/IRQ disable by the user to guarantee a correct behaviour.
  */
 extern void genirq_doirq(genirq_t d, int irq);
 
diff --git a/c/src/lib/libbsp/sparc/shared/irq/genirq.c b/c/src/lib/libbsp/sparc/shared/irq/genirq.c
index ef6272c..bba642e 100644
--- a/c/src/lib/libbsp/sparc/shared/irq/genirq.c
+++ b/c/src/lib/libbsp/sparc/shared/irq/genirq.c
@@ -1,6 +1,6 @@
 /*
  * Generic interrupt helpers mainly for GRLIB PCI peripherals
- * 
+ *
  * COPYRIGHT (c) 2008.
  * Cobham Gaisler AB.
  *
@@ -47,6 +47,7 @@ genirq_t genirq_init(int number_of_irqs)
 		return NULL;
 	memset(priv, 0, size);
 	priv->genirq_max = number_of_irqs - 1;
+
 	return priv;
 }
 
@@ -64,7 +65,7 @@ void genirq_destroy(genirq_t d)
 		while ( isrentry ) {
 			tmp = isrentry;
 			isrentry = isrentry->next;
-			free(tmp);
+			genirq_free_handler(tmp);
 		}
 	}
 
@@ -81,56 +82,53 @@ int genirq_check(genirq_t d, int irq)
 		return 0;
 }
 
-int genirq_register(genirq_t d, int irq, genirq_handler isr, void *arg)
+void *genirq_alloc_handler(genirq_handler isr, void *arg)
+{
+	struct genirq_handler_entry *newentry;
+
+	newentry = malloc(sizeof(struct genirq_handler_entry));
+	if ( newentry ) {
+		/* Initialize ISR entry */
+		newentry->isr     = isr;
+		newentry->arg     = arg;
+		newentry->enabled = 0;
+	}
+	return newentry;
+}
+
+int genirq_register(genirq_t d, int irq, void *handler)
 {
 	struct genirq_priv *priv = d;
 	struct genirq_irq_entry *irqentry;
-	struct genirq_handler_entry *isrentry, *newentry;
-	rtems_interrupt_level level;
-	
-	if ( genirq_check(d, irq) )
-		return -1;
+	struct genirq_handler_entry *isrentry, *newentry = handler;
 
-	newentry = malloc(sizeof(*newentry));
-	if ( !newentry )
+	if ( genirq_check(d, irq) )
 		return -1;
 
-	/* Initialize ISR entry */
-	newentry->isr     = isr;
-	newentry->arg     = arg;
-	newentry->enabled = 0;
-
-	rtems_interrupt_disable(level);
-
 	/* Insert new ISR entry first into table */
 	irqentry = &priv->genirq_table[irq];
 	isrentry = irqentry->head;
 	irqentry->head = newentry;
 	newentry->next = isrentry;
 
-	rtems_interrupt_enable(level);
-
 	if ( isrentry )
 		return 1; /* This is the first handler on this IRQ */
 	return 0;
 }
 
-int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
+void *genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
 {
 	struct genirq_priv *priv = d;
 	struct genirq_irq_entry *irqentry;
 	struct genirq_handler_entry *isrentry, **prev;
-	rtems_interrupt_level level;
-	int ret;
+	void *ret;
 
 	if ( genirq_check(d, irq) )
-		return -1;
+		return NULL;
 
 	/* Remove isr[arg] from ISR list */
 	irqentry = &priv->genirq_table[irq];
-	ret = -1;
-
-	rtems_interrupt_disable(level);
+	ret = NULL;
 
 	prev = &irqentry->head;
 	isrentry = irqentry->head;
@@ -139,19 +137,17 @@ int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
 			/* Found ISR, remove it from list */
 			if ( isrentry->enabled ) {
 				/* Can not remove enabled ISRs, disable first */
-				ret = 1;
+				ret = NULL;
 				break;
 			}
 			*prev = isrentry->next;
-			ret = 0;
+			ret = isrentry;
 			break;
 		}
 		prev = &isrentry->next;
 		isrentry = isrentry->next;
 	}
 
-	rtems_interrupt_enable(level);
-
 	return ret;
 }
 
@@ -226,7 +222,7 @@ void genirq_doirq(genirq_t d, int irq)
 
 	irqentry = &priv->genirq_table[irq];
 	irqentry->stats.irq_cnt++;
-	
+
 	enabled = 0;
 
 	isrentry = irqentry->head;
-- 
2.7.4




More information about the devel mailing list