Commit 1d921834 authored by Russell King's avatar Russell King

[PCMCIA] Fix deadlocks caused between PCMCIA card fix and device model

The problem was that the semaphore which prevents ds interfering with
the sleepy card initialisation (skt_sem in pccardd) is blocking insmod
of the socket driver.  However, the socket driver is being called with
the PCI bus semaphore held by the driver model.

pccardd in turn discovered a cardbus card (with skt_sem held), so it
is trying to add the PCI devices to the PCI bus, and this requires the
driver model to grab the PCI bus semaphore, but its already locked.

We move the class device register into pccardd so we get a natural
ordering between the ds socket initialisation and pccardd trying to
detect inserted cards.

We also fix a potential use-after-free caused by rmmod'ing the socket
driver before ds has shut down.
parent fb26029e
...@@ -281,72 +281,29 @@ int pcmcia_socket_dev_resume(struct device *dev) ...@@ -281,72 +281,29 @@ int pcmcia_socket_dev_resume(struct device *dev)
EXPORT_SYMBOL(pcmcia_socket_dev_resume); EXPORT_SYMBOL(pcmcia_socket_dev_resume);
static int pccardd(void *__skt); static void pcmcia_release_socket(struct class_device *class_dev)
#define to_class_data(dev) dev->class_data
static int pcmcia_add_socket(struct class_device *class_dev)
{
struct pcmcia_socket *socket = class_get_devdata(class_dev);
int ret = 0;
/* base address = 0, map = 0 */
socket->cis_mem.flags = 0;
socket->cis_mem.speed = cis_speed;
socket->erase_busy.next = socket->erase_busy.prev = &socket->erase_busy;
INIT_LIST_HEAD(&socket->cis_cache);
spin_lock_init(&socket->lock);
init_completion(&socket->thread_done);
init_waitqueue_head(&socket->thread_wait);
init_MUTEX(&socket->skt_sem);
spin_lock_init(&socket->thread_lock);
socket->socket = dead_socket;
socket->ops->init(socket);
ret = kernel_thread(pccardd, socket, CLONE_KERNEL);
if (ret < 0)
return ret;
wait_for_completion(&socket->thread_done);
BUG_ON(!socket->thread);
pcmcia_parse_events(socket, SS_DETECT);
return 0;
}
static void pcmcia_remove_socket(struct class_device *class_dev)
{ {
struct pcmcia_socket *socket = class_get_devdata(class_dev); struct pcmcia_socket *socket = class_get_devdata(class_dev);
client_t *client; client_t *client;
if (socket->thread) {
init_completion(&socket->thread_done);
socket->thread = NULL;
wake_up(&socket->thread_wait);
wait_for_completion(&socket->thread_done);
}
release_cis_mem(socket);
while (socket->clients) { while (socket->clients) {
client = socket->clients; client = socket->clients;
socket->clients = socket->clients->next; socket->clients = socket->clients->next;
kfree(client); kfree(client);
} }
socket->ops = NULL;
}
static void pcmcia_release_socket(struct class_device *class_dev)
{
struct pcmcia_socket *socket = class_get_devdata(class_dev);
complete(&socket->socket_released); complete(&socket->socket_released);
} }
static int pccardd(void *__skt);
/** /**
* pcmcia_register_socket - add a new pcmcia socket device * pcmcia_register_socket - add a new pcmcia socket device
*/ */
int pcmcia_register_socket(struct pcmcia_socket *socket) int pcmcia_register_socket(struct pcmcia_socket *socket)
{ {
int ret;
if (!socket || !socket->ops || !socket->dev.dev) if (!socket || !socket->ops || !socket->dev.dev)
return -EINVAL; return -EINVAL;
...@@ -381,15 +338,34 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) ...@@ -381,15 +338,34 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
socket->dev.class = &pcmcia_socket_class; socket->dev.class = &pcmcia_socket_class;
snprintf(socket->dev.class_id, BUS_ID_SIZE, "pcmcia_socket%u", socket->sock); snprintf(socket->dev.class_id, BUS_ID_SIZE, "pcmcia_socket%u", socket->sock);
/* register with the device core */ /* base address = 0, map = 0 */
if (class_device_register(&socket->dev)) { socket->cis_mem.flags = 0;
down_write(&pcmcia_socket_list_rwsem); socket->cis_mem.speed = cis_speed;
list_del(&socket->socket_list); socket->erase_busy.next = socket->erase_busy.prev = &socket->erase_busy;
up_write(&pcmcia_socket_list_rwsem); INIT_LIST_HEAD(&socket->cis_cache);
return -EINVAL; spin_lock_init(&socket->lock);
}
init_completion(&socket->socket_released);
init_completion(&socket->thread_done);
init_waitqueue_head(&socket->thread_wait);
init_MUTEX(&socket->skt_sem);
spin_lock_init(&socket->thread_lock);
ret = kernel_thread(pccardd, socket, CLONE_KERNEL);
if (ret < 0)
goto err;
wait_for_completion(&socket->thread_done);
BUG_ON(!socket->thread);
pcmcia_parse_events(socket, SS_DETECT);
return 0; return 0;
err:
down_write(&pcmcia_socket_list_rwsem);
list_del(&socket->socket_list);
up_write(&pcmcia_socket_list_rwsem);
return ret;
} /* pcmcia_register_socket */ } /* pcmcia_register_socket */
EXPORT_SYMBOL(pcmcia_register_socket); EXPORT_SYMBOL(pcmcia_register_socket);
...@@ -404,10 +380,13 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) ...@@ -404,10 +380,13 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket)
DEBUG(0, "cs: pcmcia_unregister_socket(0x%p)\n", socket->ops); DEBUG(0, "cs: pcmcia_unregister_socket(0x%p)\n", socket->ops);
init_completion(&socket->socket_released); if (socket->thread) {
init_completion(&socket->thread_done);
/* remove from the device core */ socket->thread = NULL;
class_device_unregister(&socket->dev); wake_up(&socket->thread_wait);
wait_for_completion(&socket->thread_done);
}
release_cis_mem(socket);
/* remove from our own list */ /* remove from our own list */
down_write(&pcmcia_socket_list_rwsem); down_write(&pcmcia_socket_list_rwsem);
...@@ -783,11 +762,22 @@ static int pccardd(void *__skt) ...@@ -783,11 +762,22 @@ static int pccardd(void *__skt)
{ {
struct pcmcia_socket *skt = __skt; struct pcmcia_socket *skt = __skt;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
int ret;
daemonize("pccardd"); daemonize("pccardd");
skt->thread = current; skt->thread = current;
complete(&skt->thread_done); complete(&skt->thread_done);
skt->socket = dead_socket;
skt->ops->init(skt);
/* register with the device core */
ret = class_device_register(&skt->dev);
if (ret) {
printk(KERN_WARNING "PCMCIA: unable to register socket 0x%p\n",
skt);
}
add_wait_queue(&skt->thread_wait, &wait); add_wait_queue(&skt->thread_wait, &wait);
for (;;) { for (;;) {
unsigned long flags; unsigned long flags;
...@@ -823,6 +813,9 @@ static int pccardd(void *__skt) ...@@ -823,6 +813,9 @@ static int pccardd(void *__skt)
} }
remove_wait_queue(&skt->thread_wait, &wait); remove_wait_queue(&skt->thread_wait, &wait);
/* remove from the device core */
class_device_unregister(&skt->dev);
complete_and_exit(&skt->thread_done, 0); complete_and_exit(&skt->thread_done, 0);
} }
...@@ -2501,12 +2494,6 @@ struct class pcmcia_socket_class = { ...@@ -2501,12 +2494,6 @@ struct class pcmcia_socket_class = {
}; };
EXPORT_SYMBOL(pcmcia_socket_class); EXPORT_SYMBOL(pcmcia_socket_class);
static struct class_interface pcmcia_socket = {
.class = &pcmcia_socket_class,
.add = &pcmcia_add_socket,
.remove = &pcmcia_remove_socket,
};
static int __init init_pcmcia_cs(void) static int __init init_pcmcia_cs(void)
{ {
...@@ -2514,7 +2501,6 @@ static int __init init_pcmcia_cs(void) ...@@ -2514,7 +2501,6 @@ static int __init init_pcmcia_cs(void)
printk(KERN_INFO " %s\n", options); printk(KERN_INFO " %s\n", options);
DEBUG(0, "%s\n", version); DEBUG(0, "%s\n", version);
class_register(&pcmcia_socket_class); class_register(&pcmcia_socket_class);
class_interface_register(&pcmcia_socket);
return 0; return 0;
} }
...@@ -2523,7 +2509,6 @@ static void __exit exit_pcmcia_cs(void) ...@@ -2523,7 +2509,6 @@ static void __exit exit_pcmcia_cs(void)
{ {
printk(KERN_INFO "unloading Kernel Card Services\n"); printk(KERN_INFO "unloading Kernel Card Services\n");
release_resource_db(); release_resource_db();
class_interface_unregister(&pcmcia_socket);
class_unregister(&pcmcia_socket_class); class_unregister(&pcmcia_socket_class);
} }
......
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