[patches 6/8] pcmcia: remove racy try_irq()

Dominik Brodowski linux at dominikbrodowski.de
Wed Jan 12 06:05:40 EST 2005


How about this patch? It tries to address the issues you mentioned.



Remove the racy try_irq/check_irq/undo_irq interface, and try to register
the correct handler directly in pcmcia_request_irq(). Also, simplify the
IRQ usage database, but avoid using the same IRQ twice.

Signed-off-by: Dominik Brodowski <linux at brodo.de>
---

 drivers/pcmcia/cs.c          |  131 +++++++++++++++++++++++++++----------------
 drivers/pcmcia/cs_internal.h |    2
 drivers/pcmcia/rsrc_mgr.c    |  118 --------------------------------------
 3 files changed, 84 insertions(+), 167 deletions(-)

Index: 2.6.11-rc1/drivers/pcmcia/cs.c
===================================================================
--- 2.6.11-rc1.orig/drivers/pcmcia/cs.c	2005-01-12 11:36:37.000000000 +0100
+++ 2.6.11-rc1/drivers/pcmcia/cs.c	2005-01-12 11:56:32.000000000 +0100
@@ -118,6 +118,11 @@
 EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
 
 
+#ifdef CONFIG_PCMCIA_PROBE
+/* mask ofIRQs already reserved by other cards, we should avoid using them */
+static u8 pcmcia_used_irq[NR_IRQS];
+#endif
+
 /*====================================================================
 
     Low-level PC Card interface drivers need to register with Card
@@ -1297,10 +1302,9 @@
     }
 
 #ifdef CONFIG_PCMCIA_PROBE
-    if (req->AssignedIRQ != s->pci_irq)
-	undo_irq(req->Attributes, req->AssignedIRQ);
+    pcmcia_used_irq[req->AssignedIRQ]--;
 #endif
-    
+
     return CS_SUCCESS;
 } /* cs_release_irq */
 
@@ -1532,65 +1536,96 @@
     
 ======================================================================*/
 
+#ifdef CONFIG_PCMCIA_PROBE
+static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+	return IRQ_NONE;
+}
+#endif
+
 int pcmcia_request_irq(client_handle_t handle, irq_req_t *req)
 {
-    struct pcmcia_socket *s;
-    config_t *c;
-    int ret = CS_IN_USE, irq = 0;
-    struct pcmcia_device *p_dev = handle_to_pdev(handle);
-    
-    if (CHECK_HANDLE(handle))
-	return CS_BAD_HANDLE;
-    s = SOCKET(handle);
-    if (!(s->state & SOCKET_PRESENT))
-	return CS_NO_CARD;
-    c = CONFIG(handle);
-    if (c->state & CONFIG_LOCKED)
-	return CS_CONFIGURATION_LOCKED;
-    if (c->state & CONFIG_IRQ_REQ)
-	return CS_IN_USE;
+	struct pcmcia_socket *s;
+	config_t *c;
+	int ret = CS_IN_USE, irq = 0;
+	struct pcmcia_device *p_dev = handle_to_pdev(handle);
+
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	if (!(s->state & SOCKET_PRESENT))
+		return CS_NO_CARD;
+	c = CONFIG(handle);
+	if (c->state & CONFIG_LOCKED)
+		return CS_CONFIGURATION_LOCKED;
+	if (c->state & CONFIG_IRQ_REQ)
+		return CS_IN_USE;
 
 #ifdef CONFIG_PCMCIA_PROBE
 	if (s->irq.AssignedIRQ != 0) {
 		/* If the interrupt is already assigned, it must be the same */
 		irq = s->irq.AssignedIRQ;
 	} else {
-		u_int try, mask = s->irq_mask;
-		for (try = 0; try < 2; try++) {
-			for (irq = 0; irq < 32; irq++) {
-				if ((mask >> irq) & 1) {
-					ret = try_irq(req->Attributes, irq, try);
-					if (!ret)
-						break;
-				}
-			}
-			if (!ret)
+		int try;
+		u32 mask = s->irq_mask;
+		void *data = NULL;
+
+		for (try = 0; try < 64; try++) {
+			irq = try % 32;
+
+			/* marked as available by driver, and not blocked by userspace? */
+			if (!((mask >> irq) & 1))
+				continue;
+
+			/* avoid an IRQ which is already used by a PCMCIA card */
+			if ((try < 32) && pcmcia_used_irq[irq])
+				continue;
+
+			/* register the correct driver, if possible, of check whether
+			 * registering a dummy handle works, i.e. if the IRQ isn't
+			 * marked as used by the kernel resource management core */
+			ret = request_irq(irq,
+					  (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action,
+					  ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
+					   (s->functions > 1) ||
+					   (irq == s->pci_irq)) ? SA_SHIRQ : 0,
+					  p_dev->dev.bus_id,
+					  (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data);
+			if (!ret) {
+				if (!(req->Attributes & IRQ_HANDLE_PRESENT))
+					free_irq(irq, data);
 				break;
+			}
 		}
 	}
 #endif
-    if (ret != 0) {
-	if (!s->pci_irq)
-	    return ret;
-	irq = s->pci_irq;
-    }
+	if (ret) {
+		if (!s->pci_irq)
+			return ret;
+		irq = s->pci_irq;
+	}
+
+	if (ret && req->Attributes & IRQ_HANDLE_PRESENT) {
+		if (request_irq(irq, req->Handler,
+				((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
+				 (s->functions > 1) ||
+				 (irq == s->pci_irq)) ? SA_SHIRQ : 0,
+				p_dev->dev.bus_id, req->Instance))
+			return CS_IN_USE;
+	}
+
+	c->irq.Attributes = req->Attributes;
+	s->irq.AssignedIRQ = req->AssignedIRQ = irq;
+	s->irq.Config++;
 
-    if (req->Attributes & IRQ_HANDLE_PRESENT) {
-	if (request_irq(irq, req->Handler,
-			    ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || 
-			     (s->functions > 1) ||
-			     (irq == s->pci_irq)) ? SA_SHIRQ : 0,
-			     p_dev->dev.bus_id, req->Instance))
-	    return CS_IN_USE;
-    }
+	c->state |= CONFIG_IRQ_REQ;
+	handle->state |= CLIENT_IRQ_REQ;
 
-    c->irq.Attributes = req->Attributes;
-    s->irq.AssignedIRQ = req->AssignedIRQ = irq;
-    s->irq.Config++;
-    
-    c->state |= CONFIG_IRQ_REQ;
-    handle->state |= CLIENT_IRQ_REQ;
-    return CS_SUCCESS;
+#ifdef CONFIG_PCMCIA_PROBE
+	pcmcia_used_irq[irq]++;
+#endif
+
+	return CS_SUCCESS;
 } /* pcmcia_request_irq */
 
 /*======================================================================
Index: 2.6.11-rc1/drivers/pcmcia/cs_internal.h
===================================================================
--- 2.6.11-rc1.orig/drivers/pcmcia/cs_internal.h	2005-01-12 10:49:03.000000000 +0100
+++ 2.6.11-rc1/drivers/pcmcia/cs_internal.h	2005-01-12 11:38:04.000000000 +0100
@@ -140,8 +140,6 @@
 		     unsigned long r_end, struct pcmcia_socket *s);
 struct resource *find_mem_region(u_long base, u_long num, u_long align,
 		    int low, struct pcmcia_socket *s);
-int try_irq(u_int Attributes, int irq, int specific);
-void undo_irq(u_int Attributes, int irq);
 int adjust_resource_info(client_handle_t handle, adjust_t *adj);
 void release_resource_db(struct pcmcia_socket *s);
 
Index: 2.6.11-rc1/drivers/pcmcia/rsrc_mgr.c
===================================================================
--- 2.6.11-rc1.orig/drivers/pcmcia/rsrc_mgr.c	2005-01-12 11:37:04.000000000 +0100
+++ 2.6.11-rc1/drivers/pcmcia/rsrc_mgr.c	2005-01-12 11:38:04.000000000 +0100
@@ -34,122 +34,6 @@
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"
 
-static DECLARE_MUTEX(rsrc_sem);
-
-#ifdef CONFIG_PCMCIA_PROBE
-
-typedef struct irq_info_t {
-    u_int			Attributes;
-    int				time_share, dyn_share;
-    struct pcmcia_socket	*Socket;
-} irq_info_t;
-
-/* Table of IRQ assignments */
-static irq_info_t irq_table[NR_IRQS];
-
-#endif
-
-
-/*======================================================================
-
-    This checks to see if an interrupt is available, with support
-    for interrupt sharing.  We don't support reserving interrupts
-    yet.  If the interrupt is available, we allocate it.
-    
-======================================================================*/
-
-#ifdef CONFIG_PCMCIA_PROBE
-
-static irqreturn_t fake_irq(int i, void *d, struct pt_regs *r) { return IRQ_NONE; }
-static inline int check_irq(int irq)
-{
-    if (request_irq(irq, fake_irq, 0, "bogus", NULL) != 0)
-	return -1;
-    free_irq(irq, NULL);
-    return 0;
-}
-
-int try_irq(u_int Attributes, int irq, int specific)
-{
-    irq_info_t *info = &irq_table[irq];
-    int ret = 0;
-
-    down(&rsrc_sem);
-    if (info->Attributes & RES_ALLOCATED) {
-	switch (Attributes & IRQ_TYPE) {
-	case IRQ_TYPE_EXCLUSIVE:
-	    ret = CS_IN_USE;
-	    break;
-	case IRQ_TYPE_DYNAMIC_SHARING:
-	    if ((info->Attributes & RES_IRQ_TYPE)
-		!= RES_IRQ_TYPE_DYNAMIC) {
-		ret = CS_IN_USE;
-		break;
-	    }
-	    if (Attributes & IRQ_FIRST_SHARED) {
-		ret = CS_BAD_ATTRIBUTE;
-		break;
-	    }
-	    info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED;
-	    info->dyn_share++;
-	    break;
-	}
-    } else {
-	if ((info->Attributes & RES_RESERVED) && !specific) {
-	    ret = CS_IN_USE;
-	    goto out;
-	}
-	if (check_irq(irq) != 0) {
-	    ret = CS_IN_USE;
-	    goto out;
-	}
-	switch (Attributes & IRQ_TYPE) {
-	case IRQ_TYPE_EXCLUSIVE:
-	    info->Attributes |= RES_ALLOCATED;
-	    break;
-	case IRQ_TYPE_DYNAMIC_SHARING:
-	    if (!(Attributes & IRQ_FIRST_SHARED)) {
-		ret = CS_BAD_ATTRIBUTE;
-		break;
-	    }
-	    info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED;
-	    info->dyn_share = 1;
-	    break;
-	}
-    }
- out:
-    up(&rsrc_sem);
-    return ret;
-}
-
-#endif
-
-/*====================================================================*/
-
-#ifdef CONFIG_PCMCIA_PROBE
-
-void undo_irq(u_int Attributes, int irq)
-{
-    irq_info_t *info;
-
-    info = &irq_table[irq];
-    down(&rsrc_sem);
-    switch (Attributes & IRQ_TYPE) {
-    case IRQ_TYPE_EXCLUSIVE:
-	info->Attributes &= RES_RESERVED;
-	break;
-    case IRQ_TYPE_DYNAMIC_SHARING:
-	info->dyn_share--;
-	if (info->dyn_share == 0)
-	    info->Attributes &= RES_RESERVED;
-	break;
-    }
-    up(&rsrc_sem);
-}
-
-#endif
-
-/*====================================================================*/
 
 #ifdef CONFIG_PCMCIA_PROBE
 
@@ -167,7 +51,7 @@
 
 	mask = 1 << irq;
 
-	if !(s->irq_mask & mask)
+	if (!(s->irq_mask & mask))
 		return 0;
 
 	s->irq_mask &= ~mask;



More information about the linux-pcmcia mailing list