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 = {
};
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;
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;
}
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)
{
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);
if (probe_mem && s->state & SOCKET_PRESENT)
validate_mem(s);
if (s->state & SOCKET_PRESENT)
validate_mem(s, probe_mask);
up(&s->skt_sem);
}
up(&rsrc_sem);
}
}
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