[BUG? kernel 2.6.32-git10] 3com 3c562 multifunction card does not work
Dominik Brodowski
linux at dominikbrodowski.net
Fri Jan 8 14:06:29 EST 2010
Hey,
On Thu, Jan 07, 2010 at 09:56:59PM +0900, Komuro wrote:
> I got the kernel-oops by pcmcia-check-broken-cis
> at kernel 2.6.33-rc3.
>
> Do you think this is the same as CIS problem?
I have a bad feeling about this... might it be that cistpl.c isn't thread
safe at all?
Could you test out this patch, please?
Thanks,
Dominik
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 25b1cd2..b7eec6e 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -64,6 +64,7 @@ module_param(cis_width, int, 0444);
void release_cis_mem(struct pcmcia_socket *s)
{
+ mutex_lock(&s->card_mutex);
if (s->cis_mem.flags & MAP_ACTIVE) {
s->cis_mem.flags &= ~MAP_ACTIVE;
s->ops->set_mem_map(s, &s->cis_mem);
@@ -75,6 +76,7 @@ void release_cis_mem(struct pcmcia_socket *s)
iounmap(s->cis_virt);
s->cis_virt = NULL;
}
+ mutex_unlock(&s->card_mutex);
}
EXPORT_SYMBOL(release_cis_mem);
@@ -89,11 +91,13 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag
pccard_mem_map *mem = &s->cis_mem;
int ret;
+ mutex_lock(&s->card_mutex);
if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s);
if (mem->res == NULL) {
dev_printk(KERN_NOTICE, &s->dev,
"cs: unable to map card memory!\n");
+ mutex_unlock(&s->card_mutex);
return NULL;
}
s->cis_virt = NULL;
@@ -109,6 +113,7 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag
if (ret) {
iounmap(s->cis_virt);
s->cis_virt = NULL;
+ mutex_unlock(&s->card_mutex);
return NULL;
}
@@ -117,6 +122,7 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag
iounmap(s->cis_virt);
s->cis_virt = ioremap(mem->static_start, s->map_size);
}
+ mutex_unlock(&s->card_mutex);
return s->cis_virt;
}
@@ -271,27 +277,34 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
struct cis_cache_entry *cis;
int ret;
+ mutex_lock(&s->card_mutex);
if (s->fake_cis) {
if (s->fake_cis_len >= addr+len)
memcpy(ptr, s->fake_cis+addr, len);
else
memset(ptr, 0xff, len);
+ mutex_unlock(&s->card_mutex);
return;
}
list_for_each_entry(cis, &s->cis_cache, node) {
if (cis->addr == addr && cis->len == len && cis->attr == attr) {
memcpy(ptr, cis->cache, len);
+ mutex_unlock(&s->card_mutex);
return;
}
}
+ mutex_unlock(&s->card_mutex);
#ifdef CONFIG_CARDBUS
if (s->state & SOCKET_CARDBUS)
ret = read_cb_mem(s, attr, addr, len, ptr);
else
#endif
+ {
+ mutex_lock(&s->card_mutex);
ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
+ }
if (ret == 0) {
/* Copy data into the cache */
@@ -304,6 +317,7 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
list_add(&cis->node, &s->cis_cache);
}
}
+ mutex_unlock(&s->card_mutex);
}
static void
@@ -311,24 +325,28 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
{
struct cis_cache_entry *cis;
+ mutex_lock(&s->card_mutex);
list_for_each_entry(cis, &s->cis_cache, node)
if (cis->addr == addr && cis->len == len && cis->attr == attr) {
list_del(&cis->node);
kfree(cis);
break;
}
+ mutex_unlock(&s->card_mutex);
}
void destroy_cis_cache(struct pcmcia_socket *s)
{
struct list_head *l, *n;
+ mutex_lock(&s->card_mutex);
list_for_each_safe(l, n, &s->cis_cache) {
struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node);
list_del(&cis->node);
kfree(cis);
}
+ mutex_unlock(&s->card_mutex);
/*
* If there was a fake CIS, destroy that as well.
@@ -356,6 +374,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
"no memory for verifying CIS\n");
return -ENOMEM;
}
+ mutex_lock(&s->card_mutex);
list_for_each_entry(cis, &s->cis_cache, node) {
int len = cis->len;
@@ -369,10 +388,12 @@ int verify_cis_cache(struct pcmcia_socket *s)
pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
if (memcmp(buf, cis->cache, len) != 0) {
+ mutex_unlock(&s->card_mutex);
kfree(buf);
return -1;
}
}
+ mutex_unlock(&s->card_mutex);
kfree(buf);
return 0;
}
@@ -391,14 +412,17 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
return -EINVAL;
}
+ mutex_lock(&s->card_mutex);
kfree(s->fake_cis);
s->fake_cis = kmalloc(len, GFP_KERNEL);
if (s->fake_cis == NULL) {
dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
+ mutex_unlock(&s->card_mutex);
return -ENOMEM;
}
s->fake_cis_len = len;
memcpy(s->fake_cis, data, len);
+ mutex_unlock(&s->card_mutex);
return 0;
}
EXPORT_SYMBOL(pcmcia_replace_cis);
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 6d6f82b..0b694b5 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -228,6 +228,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
init_completion(&socket->socket_released);
init_completion(&socket->thread_done);
mutex_init(&socket->skt_mutex);
+ mutex_init(&socket->card_mutex);
spin_lock_init(&socket->thread_lock);
if (socket->resource_ops->init) {
diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h
index cbfba88..fa76a6b 100644
--- a/include/pcmcia/ss.h
+++ b/include/pcmcia/ss.h
@@ -237,6 +237,9 @@ struct pcmcia_socket {
/* for adding further pseudo-multifunction devices */
struct work_struct device_add;
+ /* protects card h/w state */
+ struct mutex card_mutex;
+
#ifdef CONFIG_PCMCIA_IOCTL
struct user_info_t *user;
wait_queue_head_t queue;
More information about the linux-pcmcia
mailing list