Commit 04de0816 authored by Dominik Brodowski's avatar Dominik Brodowski

pcmcia: pcmcia_dev_present bugfix

pcmcia_dev_present is in and by itself buggy. Add a note specifying
why it is broken, and replace the broken locking -- taking a mutex
is a bad idea in IRQ context, from which this function is rarely
called -- by an atomic_t.
Signed-off-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
parent 05ce7bfe
...@@ -335,7 +335,6 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le ...@@ -335,7 +335,6 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
mutex_lock(&s->ops_mutex); mutex_lock(&s->ops_mutex);
list_del(&p_dev->socket_device_list); list_del(&p_dev->socket_device_list);
p_dev->_removed = 1;
mutex_unlock(&s->ops_mutex); mutex_unlock(&s->ops_mutex);
dev_dbg(&p_dev->dev, "unregistering device\n"); dev_dbg(&p_dev->dev, "unregistering device\n");
...@@ -654,14 +653,7 @@ static int pcmcia_requery_callback(struct device *dev, void * _data) ...@@ -654,14 +653,7 @@ static int pcmcia_requery_callback(struct device *dev, void * _data)
static void pcmcia_requery(struct pcmcia_socket *s) static void pcmcia_requery(struct pcmcia_socket *s)
{ {
int present, has_pfc; int has_pfc;
mutex_lock(&s->ops_mutex);
present = s->pcmcia_state.present;
mutex_unlock(&s->ops_mutex);
if (!present)
return;
if (s->functions == 0) { if (s->functions == 0) {
pcmcia_card_add(s); pcmcia_card_add(s);
...@@ -1260,9 +1252,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) ...@@ -1260,9 +1252,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
switch (event) { switch (event) {
case CS_EVENT_CARD_REMOVAL: case CS_EVENT_CARD_REMOVAL:
mutex_lock(&s->ops_mutex); atomic_set(&skt->present, 0);
s->pcmcia_state.present = 0;
mutex_unlock(&s->ops_mutex);
pcmcia_card_remove(skt, NULL); pcmcia_card_remove(skt, NULL);
handle_event(skt, event); handle_event(skt, event);
mutex_lock(&s->ops_mutex); mutex_lock(&s->ops_mutex);
...@@ -1271,9 +1261,9 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) ...@@ -1271,9 +1261,9 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
break; break;
case CS_EVENT_CARD_INSERTION: case CS_EVENT_CARD_INSERTION:
atomic_set(&skt->present, 1);
mutex_lock(&s->ops_mutex); mutex_lock(&s->ops_mutex);
s->pcmcia_state.has_pfc = 0; s->pcmcia_state.has_pfc = 0;
s->pcmcia_state.present = 1;
destroy_cis_cache(s); /* to be on the safe side... */ destroy_cis_cache(s); /* to be on the safe side... */
mutex_unlock(&s->ops_mutex); mutex_unlock(&s->ops_mutex);
pcmcia_card_add(skt); pcmcia_card_add(skt);
...@@ -1313,7 +1303,13 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) ...@@ -1313,7 +1303,13 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
return 0; return 0;
} /* ds_event */ } /* ds_event */
/*
* NOTE: This is racy. There's no guarantee the card will still be
* physically present, even if the call to this function returns
* non-NULL. Furthermore, the device driver most likely is unbound
* almost immediately, so the timeframe where pcmcia_dev_present
* returns NULL is probably really really small.
*/
struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
{ {
struct pcmcia_device *p_dev; struct pcmcia_device *p_dev;
...@@ -1323,22 +1319,9 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) ...@@ -1323,22 +1319,9 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
if (!p_dev) if (!p_dev)
return NULL; return NULL;
mutex_lock(&p_dev->socket->ops_mutex); if (atomic_read(&p_dev->socket->present) != 0)
if (!p_dev->socket->pcmcia_state.present)
goto out;
if (p_dev->socket->pcmcia_state.dead)
goto out;
if (p_dev->_removed)
goto out;
if (p_dev->suspended)
goto out;
ret = p_dev; ret = p_dev;
out:
mutex_unlock(&p_dev->socket->ops_mutex);
pcmcia_put_dev(p_dev); pcmcia_put_dev(p_dev);
return ret; return ret;
} }
...@@ -1388,6 +1371,8 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, ...@@ -1388,6 +1371,8 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev,
return ret; return ret;
} }
atomic_set(&socket->present, 0);
return 0; return 0;
} }
...@@ -1399,10 +1384,6 @@ static void pcmcia_bus_remove_socket(struct device *dev, ...@@ -1399,10 +1384,6 @@ static void pcmcia_bus_remove_socket(struct device *dev,
if (!socket) if (!socket)
return; return;
mutex_lock(&socket->ops_mutex);
socket->pcmcia_state.dead = 1;
mutex_unlock(&socket->ops_mutex);
pccard_register_pcmcia(socket, NULL); pccard_register_pcmcia(socket, NULL);
/* unregister any unbound devices */ /* unregister any unbound devices */
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/device.h> #include <linux/device.h>
#include <pcmcia/ss.h> #include <pcmcia/ss.h>
#include <asm/atomic.h>
/* /*
* PCMCIA device drivers (16-bit cards only; 32-bit cards require CardBus * PCMCIA device drivers (16-bit cards only; 32-bit cards require CardBus
...@@ -94,10 +95,8 @@ struct pcmcia_device { ...@@ -94,10 +95,8 @@ struct pcmcia_device {
config_req_t conf; config_req_t conf;
window_handle_t win; window_handle_t win;
/* Is the device suspended, or in the process of /* Is the device suspended? */
* being removed? */
u16 suspended:1; u16 suspended:1;
u16 _removed:1;
/* Flags whether io, irq, win configurations were /* Flags whether io, irq, win configurations were
* requested, and whether the configuration is "locked" */ * requested, and whether the configuration is "locked" */
...@@ -115,7 +114,7 @@ struct pcmcia_device { ...@@ -115,7 +114,7 @@ struct pcmcia_device {
u16 has_card_id:1; u16 has_card_id:1;
u16 has_func_id:1; u16 has_func_id:1;
u16 reserved:3; u16 reserved:4;
u8 func_id; u8 func_id;
u16 manf_id; u16 manf_id;
......
...@@ -224,18 +224,16 @@ struct pcmcia_socket { ...@@ -224,18 +224,16 @@ struct pcmcia_socket {
/* 16-bit state: */ /* 16-bit state: */
struct { struct {
/* PCMCIA card is present in socket */
u8 present:1;
/* "master" ioctl is used */ /* "master" ioctl is used */
u8 busy:1; u8 busy:1;
/* pcmcia module is being unloaded */
u8 dead:1;
/* the PCMCIA card consists of two pseudo devices */ /* the PCMCIA card consists of two pseudo devices */
u8 has_pfc:1; u8 has_pfc:1;
u8 reserved:4; u8 reserved:6;
} pcmcia_state; } pcmcia_state;
/* non-zero if PCMCIA card is present */
atomic_t present;
#ifdef CONFIG_PCMCIA_IOCTL #ifdef CONFIG_PCMCIA_IOCTL
struct user_info_t *user; struct user_info_t *user;
......
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