diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-04-20 16:49:01 +0400 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-04-21 10:09:17 +0400 |
commit | 04de0816173c86948b75da93a6344a0a02bbec4d (patch) | |
tree | ed4b274c1ce5da03a9c37f5858a331529e5b150f | |
parent | 05ce7bfe547c9fa967d9cab6c37867a9cb6fb3fa (diff) | |
download | linux-04de0816173c86948b75da93a6344a0a02bbec4d.tar.xz |
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: Dominik Brodowski <linux@dominikbrodowski.net>
-rw-r--r-- | drivers/pcmcia/ds.c | 47 | ||||
-rw-r--r-- | include/pcmcia/ds.h | 7 | ||||
-rw-r--r-- | include/pcmcia/ss.h | 8 |
3 files changed, 20 insertions, 42 deletions
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 4014cf8e4a26..92a5af8aa0b4 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -335,7 +335,6 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le mutex_lock(&s->ops_mutex); list_del(&p_dev->socket_device_list); - p_dev->_removed = 1; mutex_unlock(&s->ops_mutex); dev_dbg(&p_dev->dev, "unregistering device\n"); @@ -654,14 +653,7 @@ static int pcmcia_requery_callback(struct device *dev, void * _data) static void pcmcia_requery(struct pcmcia_socket *s) { - int present, has_pfc; - - mutex_lock(&s->ops_mutex); - present = s->pcmcia_state.present; - mutex_unlock(&s->ops_mutex); - - if (!present) - return; + int has_pfc; if (s->functions == 0) { pcmcia_card_add(s); @@ -1260,9 +1252,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) switch (event) { case CS_EVENT_CARD_REMOVAL: - mutex_lock(&s->ops_mutex); - s->pcmcia_state.present = 0; - mutex_unlock(&s->ops_mutex); + atomic_set(&skt->present, 0); pcmcia_card_remove(skt, NULL); handle_event(skt, event); mutex_lock(&s->ops_mutex); @@ -1271,9 +1261,9 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) break; case CS_EVENT_CARD_INSERTION: + atomic_set(&skt->present, 1); mutex_lock(&s->ops_mutex); s->pcmcia_state.has_pfc = 0; - s->pcmcia_state.present = 1; destroy_cis_cache(s); /* to be on the safe side... */ mutex_unlock(&s->ops_mutex); pcmcia_card_add(skt); @@ -1313,7 +1303,13 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) return 0; } /* 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 *p_dev; @@ -1323,22 +1319,9 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) if (!p_dev) return NULL; - mutex_lock(&p_dev->socket->ops_mutex); - if (!p_dev->socket->pcmcia_state.present) - goto out; + if (atomic_read(&p_dev->socket->present) != 0) + ret = p_dev; - if (p_dev->socket->pcmcia_state.dead) - goto out; - - if (p_dev->_removed) - goto out; - - if (p_dev->suspended) - goto out; - - ret = p_dev; - out: - mutex_unlock(&p_dev->socket->ops_mutex); pcmcia_put_dev(p_dev); return ret; } @@ -1388,6 +1371,8 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, return ret; } + atomic_set(&socket->present, 0); + return 0; } @@ -1399,10 +1384,6 @@ static void pcmcia_bus_remove_socket(struct device *dev, if (!socket) return; - mutex_lock(&socket->ops_mutex); - socket->pcmcia_state.dead = 1; - mutex_unlock(&socket->ops_mutex); - pccard_register_pcmcia(socket, NULL); /* unregister any unbound devices */ diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index d57847f2f6c1..aab3c13dc310 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -26,6 +26,7 @@ #ifdef __KERNEL__ #include <linux/device.h> #include <pcmcia/ss.h> +#include <asm/atomic.h> /* * PCMCIA device drivers (16-bit cards only; 32-bit cards require CardBus @@ -94,10 +95,8 @@ struct pcmcia_device { config_req_t conf; window_handle_t win; - /* Is the device suspended, or in the process of - * being removed? */ + /* Is the device suspended? */ u16 suspended:1; - u16 _removed:1; /* Flags whether io, irq, win configurations were * requested, and whether the configuration is "locked" */ @@ -115,7 +114,7 @@ struct pcmcia_device { u16 has_card_id:1; u16 has_func_id:1; - u16 reserved:3; + u16 reserved:4; u8 func_id; u16 manf_id; diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index 2e488b60bc76..344705cb42f4 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -224,18 +224,16 @@ struct pcmcia_socket { /* 16-bit state: */ struct { - /* PCMCIA card is present in socket */ - u8 present:1; /* "master" ioctl is used */ u8 busy:1; - /* pcmcia module is being unloaded */ - u8 dead:1; /* the PCMCIA card consists of two pseudo devices */ u8 has_pfc:1; - u8 reserved:4; + u8 reserved:6; } pcmcia_state; + /* non-zero if PCMCIA card is present */ + atomic_t present; #ifdef CONFIG_PCMCIA_IOCTL struct user_info_t *user; |