Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+14 −33
Original line number Diff line number Diff line
@@ -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 (p_dev->socket->pcmcia_state.dead)
		goto out;

	if (p_dev->_removed)
		goto out;

	if (p_dev->suspended)
		goto out;

	if (atomic_read(&p_dev->socket->present) != 0)
		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 */
+3 −4
Original line number Diff line number Diff line
@@ -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;
+3 −5
Original line number Diff line number Diff line
@@ -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;