[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