Restructure unbind_request(): Before, unbind_request was called by cardmgr on the following occasions: a) if the CS_EVENT_CARD_INSERTION event failed b) during do_remove(), which is called on 1) when cardmgr is informed of a CS_EVENT_CARD_REMOVAL event 2) when cardmgr is informed of a CS_EVENT_EJECTION_REQUEST event, if do_check() succeeds 3) cardmgr exit (SIGINT/SIGTERM), if do_check() succeeds We can ignore a), as the user is informed of the problem anyway, and can take appropriate action then (eject the card, update config, write new driver, insert card...). b1) can be done directly, even before the userspace cardmgr is informed. This speeds up the call to ->detach(). b2) All drivers I checked were based on the assumption that a CS_EVENT_CARD_REMOVAL event is received _first_, before a call to ->detach(). Most notably, some drivers issue first a call to their release() function [which else is called during EVENT_CARD_REMOVAL] during ->detach() if it hasn't been issued before. So, it doesn't hurt if unbind is only called during the EVENT_CARD_REMOVAL step, and not during EJECTION_REQUEST. The REMOVAL step is only called anyway if EJECTION_REQUEST succeeds, and the latter can only succeed if do_check() succeeds. b3) If cardmgr exits from daemon mode, ds_release() is called. I can't see a reason why this is good behaviour, especially as cards don't need cardmgr while running, only for setup. Consequences: - call unbind_request during CARD_REMOVAL handling, even before userspace is informed. - return "0" if UNBIND_REQUEST is called from userspace. - the driver's event handler is called with CARD_REMOVAL _always_ before ->detach() is called. Signed-off-by: Dominik Brodowski Index: 2.6.10-rc3/drivers/pcmcia/ds.c =================================================================== --- 2.6.10-rc3.orig/drivers/pcmcia/ds.c 2004-12-13 15:59:43.277232719 +0100 +++ 2.6.10-rc3/drivers/pcmcia/ds.c 2004-12-13 15:59:45.279953936 +0100 @@ -113,6 +113,8 @@ static int major_dev = -1; +static int unbind_request(struct pcmcia_bus_socket *s); + /*====================================================================*/ /* code which was in cs.c before */ @@ -354,7 +356,6 @@ static void pcmcia_release_dev(struct device *dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - p_dev->socket->pcmcia->device_count = 0; pcmcia_put_bus_socket(p_dev->socket->pcmcia); kfree(p_dev); } @@ -466,8 +467,6 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) { struct pcmcia_bus_socket *s = skt->pcmcia; - struct pcmcia_device *p_dev; - unsigned long flags; int ret = 0; ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n", @@ -478,11 +477,8 @@ case CS_EVENT_CARD_REMOVAL: s->state &= ~DS_SOCKET_PRESENT; send_event(skt, event, priority); + unbind_request(s); handle_event(s, event); - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(p_dev, &s->devices_list, socket_device_list) - p_dev->client->state |= CLIENT_STALE; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); break; case CS_EVENT_CARD_INSERTION: @@ -859,38 +855,42 @@ /*====================================================================*/ -static int unbind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) +/* unbind _all_ devices attached to a given pcmcia_bus_socket. The + * drivers have been called with EVENT_CARD_REMOVAL before. + */ +static int unbind_request(struct pcmcia_bus_socket *s) { struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; unsigned long flags; - ds_dbg(2, "unbind_request(%d, '%s')\n", s->parent->sock, - (char *)bind_info->dev_info); - - restart: - /* unregister the pcmcia_device */ - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { - if (p_dev->func == bind_info->function) { - list_del(&p_dev->socket_device_list); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + ds_dbg(2, "unbind_request(%d)\n", s->parent->sock); - /* detach the "instance" */ - p_drv = to_pcmcia_drv(p_dev->dev.driver); - if (p_drv) { - if ((p_drv->detach) && (p_dev->instance)) - p_drv->detach(p_dev->instance); - module_put(p_drv->owner); - } + s->device_count = 0; - device_unregister(&p_dev->dev); + for (;;) { + /* unregister all pcmcia_devices registered with this socket*/ + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + if (list_empty(&s->devices_list)) { + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + return 0; + } + p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list); + list_del(&p_dev->socket_device_list); + p_dev->client->state |= CLIENT_STALE; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - /* multiple devices may be registered to this "function" */ - goto restart; + /* detach the "instance" */ + p_drv = to_pcmcia_drv(p_dev->dev.driver); + if (p_drv) { + if ((p_drv->detach) && (p_dev->instance)) + p_drv->detach(p_dev->instance); + module_put(p_drv->owner); } + + device_unregister(&p_dev->dev); } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + return 0; } /* unbind_request */ @@ -1253,7 +1253,7 @@ err = get_device_info(s, &buf.bind_info, 0); break; case DS_UNBIND_REQUEST: - err = unbind_request(s, &buf.bind_info); + err = 0; break; case DS_BIND_MTD: if (!capable(CAP_SYS_ADMIN)) return -EPERM;