]> git.hungrycats.org Git - linux/commitdiff
[PCMCIA] Improve locking for memory resource probing.
authorRussell King <rmk@flint.arm.linux.org.uk>
Sat, 9 Oct 2004 00:48:34 +0000 (01:48 +0100)
committerRussell King <rmk@flint.arm.linux.org.uk>
Sat, 9 Oct 2004 00:48:34 +0000 (01:48 +0100)
- Pass a mask of the regions we wish to be probed to validate_mem
- Only take s->skt_sem if we need to probe resources on a socket

This prevents a deadlock reported by Adam Belay caused by ds.c waiting
with the socket semaphore held for userspace to respond.  Meanwhile,
userspace may under some rare circumstances issue a request to read
tuples from the card, which tries to take the socket semaphore again.

drivers/pcmcia/rsrc_mgr.c

index 4ad32c25e0b95953ebfe97d1532db9fcd616ccbb..32b264fc402395a440b44d921a39e8b824072608 100644 (file)
@@ -88,6 +88,9 @@ static resource_map_t io_db = {
 };
 
 static DECLARE_MUTEX(rsrc_sem);
+static unsigned int rsrc_mem_probe;
+#define MEM_PROBE_LOW  (1 << 0)
+#define MEM_PROBE_HIGH (1 << 1)
 
 #ifdef CONFIG_PCMCIA_PROBE
 
@@ -451,24 +454,22 @@ static u_long inv_probe(resource_map_t *m, struct pcmcia_socket *s)
     return do_mem_probe(m->base, m->num, s);
 }
 
-static void validate_mem(struct pcmcia_socket *s)
+static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 {
     resource_map_t *m, mm;
     static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
     static int hi = 0, lo = 0;
     u_long b, i, ok = 0;
-    int force_low = !(s->features & SS_CAP_PAGE_REGS);
 
-    down(&rsrc_sem);
     /* We do up to four passes through the list */
-    if (!force_low) {
-       if (hi++ || (inv_probe(mem_db.next, s) > 0))
-           goto out;
+    if (probe_mask & MEM_PROBE_HIGH) {
+       if (inv_probe(mem_db.next, s) > 0)
+           return;
        printk(KERN_NOTICE "cs: warning: no high memory space "
               "available!\n");
     }
-    if (lo++)
-       goto out;
+    if ((probe_mask & MEM_PROBE_LOW) == 0)
+       return;
     for (m = mem_db.next; m != &mem_db; m = mm.next) {
        mm = *m;
        /* Only probe < 1 MB */
@@ -488,38 +489,51 @@ static void validate_mem(struct pcmcia_socket *s)
            }
        }
     }
- out:
-    up(&rsrc_sem);
 }
 
 #else /* CONFIG_PCMCIA_PROBE */
 
-static void validate_mem(struct pcmcia_socket *s)
+static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 {
-    resource_map_t *m, mm;
-    static int done = 0;
+       resource_map_t *m, mm;
     
-    if (done++ == 0) {
-       down(&rsrc_sem);
        for (m = mem_db.next; m != &mem_db; m = mm.next) {
-           mm = *m;
-           if (do_mem_probe(mm.base, mm.num, s))
-               break;
+               mm = *m;
+               if (do_mem_probe(mm.base, mm.num, s))
+                       break;
        }
-       up(&rsrc_sem);
-    }
 }
 
 #endif /* CONFIG_PCMCIA_PROBE */
 
+/*
+ * Locking note: this is the only place where we take
+ * both rsrc_sem and skt_sem.
+ */
 void pcmcia_validate_mem(struct pcmcia_socket *s)
 {
-       down(&s->skt_sem);
+       if (probe_mem) {
+               unsigned int probe_mask;
+
+               down(&rsrc_sem);
+
+               probe_mask = MEM_PROBE_LOW;
+               if (s->features & SS_CAP_PAGE_REGS)
+                       probe_mask = MEM_PROBE_HIGH;
+
+               if (probe_mask & ~rsrc_mem_probe) {
+                       rsrc_mem_probe |= probe_mask;
 
-       if (probe_mem && s->state & SOCKET_PRESENT)
-               validate_mem(s);
+                       down(&s->skt_sem);
 
-       up(&s->skt_sem);
+                       if (s->state & SOCKET_PRESENT)
+                               validate_mem(s, probe_mask);
+
+                       up(&s->skt_sem);
+               }
+
+               up(&rsrc_sem);
+       }
 }
 
 EXPORT_SYMBOL(pcmcia_validate_mem);