Commit 3c720f5b authored by Russell King's avatar Russell King

[PCMCIA] Improve locking for memory resource probing.

- 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.
parent 52d9bf0b
...@@ -88,6 +88,9 @@ static resource_map_t io_db = { ...@@ -88,6 +88,9 @@ static resource_map_t io_db = {
}; };
static DECLARE_MUTEX(rsrc_sem); 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 #ifdef CONFIG_PCMCIA_PROBE
...@@ -451,24 +454,22 @@ static u_long inv_probe(resource_map_t *m, struct pcmcia_socket *s) ...@@ -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); 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; resource_map_t *m, mm;
static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
static int hi = 0, lo = 0; static int hi = 0, lo = 0;
u_long b, i, ok = 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 */ /* We do up to four passes through the list */
if (!force_low) { if (probe_mask & MEM_PROBE_HIGH) {
if (hi++ || (inv_probe(mem_db.next, s) > 0)) if (inv_probe(mem_db.next, s) > 0)
goto out; return;
printk(KERN_NOTICE "cs: warning: no high memory space " printk(KERN_NOTICE "cs: warning: no high memory space "
"available!\n"); "available!\n");
} }
if (lo++) if ((probe_mask & MEM_PROBE_LOW) == 0)
goto out; return;
for (m = mem_db.next; m != &mem_db; m = mm.next) { for (m = mem_db.next; m != &mem_db; m = mm.next) {
mm = *m; mm = *m;
/* Only probe < 1 MB */ /* Only probe < 1 MB */
...@@ -488,38 +489,51 @@ static void validate_mem(struct pcmcia_socket *s) ...@@ -488,38 +489,51 @@ static void validate_mem(struct pcmcia_socket *s)
} }
} }
} }
out:
up(&rsrc_sem);
} }
#else /* CONFIG_PCMCIA_PROBE */ #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; resource_map_t *m, mm;
static int done = 0;
if (done++ == 0) {
down(&rsrc_sem);
for (m = mem_db.next; m != &mem_db; m = mm.next) { for (m = mem_db.next; m != &mem_db; m = mm.next) {
mm = *m; mm = *m;
if (do_mem_probe(mm.base, mm.num, s)) if (do_mem_probe(mm.base, mm.num, s))
break; break;
} }
up(&rsrc_sem);
}
} }
#endif /* CONFIG_PCMCIA_PROBE */ #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) void pcmcia_validate_mem(struct pcmcia_socket *s)
{ {
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;
down(&s->skt_sem); down(&s->skt_sem);
if (probe_mem && s->state & SOCKET_PRESENT) if (s->state & SOCKET_PRESENT)
validate_mem(s); validate_mem(s, probe_mask);
up(&s->skt_sem); up(&s->skt_sem);
}
up(&rsrc_sem);
}
} }
EXPORT_SYMBOL(pcmcia_validate_mem); EXPORT_SYMBOL(pcmcia_validate_mem);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment