Commit 50d26851 authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Linus Torvalds

[PATCH] pcmcia: direct-ordered unbind of devices

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: default avatarDominik Brodowski <linux@brodo.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6a35548e
...@@ -113,6 +113,8 @@ static spinlock_t pcmcia_dev_list_lock; ...@@ -113,6 +113,8 @@ static spinlock_t pcmcia_dev_list_lock;
static int major_dev = -1; static int major_dev = -1;
static int unbind_request(struct pcmcia_bus_socket *s);
/*====================================================================*/ /*====================================================================*/
/* code which was in cs.c before */ /* code which was in cs.c before */
...@@ -354,7 +356,6 @@ static void pcmcia_put_dev(struct pcmcia_device *p_dev) ...@@ -354,7 +356,6 @@ static void pcmcia_put_dev(struct pcmcia_device *p_dev)
static void pcmcia_release_dev(struct device *dev) static void pcmcia_release_dev(struct device *dev)
{ {
struct pcmcia_device *p_dev = to_pcmcia_dev(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); pcmcia_put_bus_socket(p_dev->socket->pcmcia);
kfree(p_dev); kfree(p_dev);
} }
...@@ -466,8 +467,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) ...@@ -466,8 +467,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
{ {
struct pcmcia_bus_socket *s = skt->pcmcia; struct pcmcia_bus_socket *s = skt->pcmcia;
struct pcmcia_device *p_dev;
unsigned long flags;
int ret = 0; int ret = 0;
ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n", ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n",
...@@ -478,11 +477,8 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) ...@@ -478,11 +477,8 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
case CS_EVENT_CARD_REMOVAL: case CS_EVENT_CARD_REMOVAL:
s->state &= ~DS_SOCKET_PRESENT; s->state &= ~DS_SOCKET_PRESENT;
send_event(skt, event, priority); send_event(skt, event, priority);
unbind_request(s);
handle_event(s, event); 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; break;
case CS_EVENT_CARD_INSERTION: case CS_EVENT_CARD_INSERTION:
...@@ -859,38 +855,42 @@ static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, ...@@ -859,38 +855,42 @@ static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info,
/*====================================================================*/ /*====================================================================*/
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_device *p_dev;
struct pcmcia_driver *p_drv; struct pcmcia_driver *p_drv;
unsigned long flags; unsigned long flags;
ds_dbg(2, "unbind_request(%d, '%s')\n", s->parent->sock, ds_dbg(2, "unbind_request(%d)\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);
/* detach the "instance" */ s->device_count = 0;
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); 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" */ /* detach the "instance" */
goto restart; 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; return 0;
} /* unbind_request */ } /* unbind_request */
...@@ -1253,7 +1253,7 @@ static int ds_ioctl(struct inode * inode, struct file * file, ...@@ -1253,7 +1253,7 @@ static int ds_ioctl(struct inode * inode, struct file * file,
err = get_device_info(s, &buf.bind_info, 0); err = get_device_info(s, &buf.bind_info, 0);
break; break;
case DS_UNBIND_REQUEST: case DS_UNBIND_REQUEST:
err = unbind_request(s, &buf.bind_info); err = 0;
break; break;
case DS_BIND_MTD: case DS_BIND_MTD:
if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!capable(CAP_SYS_ADMIN)) return -EPERM;
......
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