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

Commit af461fc1 authored by Dominik Brodowski's avatar Dominik Brodowski
Browse files

pcmcia: delay re-scanning and re-querying of PCMCIA bus



After a CIS update -- or the finalization of the resource database --,
proceed with the re-scanning or re-querying of PCMCIA cards only in
a separate thread to avoid deadlocks.

Tested-by: default avatarWolfram Sang <w.sang@pengutronix.de>
Signed-off-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
parent f971dbd5
Loading
Loading
Loading
Loading
+1 −9
Original line number Diff line number Diff line
@@ -1670,15 +1670,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj,
	if (error)
		return -EIO;

	mutex_lock(&s->skt_mutex);
	if ((s->callback) && (s->state & SOCKET_PRESENT) &&
	    !(s->state & SOCKET_CARDBUS)) {
		if (try_module_get(s->callback->owner)) {
			s->callback->requery(s, 1);
			module_put(s->callback->owner);
		}
	}
	mutex_unlock(&s->skt_mutex);
	pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);

	return count;
}
+7 −1
Original line number Diff line number Diff line
@@ -728,6 +728,11 @@ static int pccardd(void *__skt)
				if (!ret)
					socket_suspend(skt);
			}
			if ((sysfs_events & PCMCIA_UEVENT_REQUERY) &&
				!(skt->state & SOCKET_CARDBUS)) {
				if (!ret && skt->callback)
					skt->callback->requery(skt);
			}
		}
		mutex_unlock(&skt->skt_mutex);

@@ -783,7 +788,8 @@ EXPORT_SYMBOL(pcmcia_parse_events);
 * userspace-issued insert, eject, suspend and resume commands must be
 * handled by pccardd to avoid any sysfs-related deadlocks. Valid events
 * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert),
 * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend).
 * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend)
 * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card).
 */
void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events)
{
+2 −1
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ struct pcmcia_callback{
	struct module	*owner;
	int		(*event) (struct pcmcia_socket *s,
				  event_t event, int priority);
	void		(*requery) (struct pcmcia_socket *s, int new_cis);
	void		(*requery) (struct pcmcia_socket *s);
	int		(*validate) (struct pcmcia_socket *s, unsigned int *i);
	int		(*suspend) (struct pcmcia_socket *s);
	int		(*resume) (struct pcmcia_socket *s);
@@ -129,6 +129,7 @@ void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events);
#define PCMCIA_UEVENT_INSERT	0x0002
#define PCMCIA_UEVENT_SUSPEND	0x0004
#define PCMCIA_UEVENT_RESUME	0x0008
#define PCMCIA_UEVENT_REQUERY	0x0010

struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
void pcmcia_put_socket(struct pcmcia_socket *skt);
+48 −32
Original line number Diff line number Diff line
@@ -432,16 +432,20 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)

	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL,
			       CISTPL_MANFID, &manf_id)) {
		mutex_lock(&p_dev->socket->ops_mutex);
		p_dev->manf_id = manf_id.manf;
		p_dev->card_id = manf_id.card;
		p_dev->has_manf_id = 1;
		p_dev->has_card_id = 1;
		mutex_unlock(&p_dev->socket->ops_mutex);
	}

	if (!pccard_read_tuple(p_dev->socket, p_dev->func,
			       CISTPL_FUNCID, &func_id)) {
		mutex_lock(&p_dev->socket->ops_mutex);
		p_dev->func_id = func_id.func;
		p_dev->has_func_id = 1;
		mutex_unlock(&p_dev->socket->ops_mutex);
	} else {
		/* rule of thumb: cards with no FUNCID, but with
		 * common memory device geometry information, are
@@ -458,14 +462,17 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
			dev_dbg(&p_dev->dev,
				   "mem device geometry probably means "
				   "FUNCID_MEMORY\n");
			mutex_lock(&p_dev->socket->ops_mutex);
			p_dev->func_id = CISTPL_FUNCID_MEMORY;
			p_dev->has_func_id = 1;
			mutex_unlock(&p_dev->socket->ops_mutex);
		}
		kfree(devgeo);
	}

	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1,
			       vers1)) {
		mutex_lock(&p_dev->socket->ops_mutex);
		for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) {
			char *tmp;
			unsigned int length;
@@ -484,6 +491,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
			p_dev->prod_id[i] = strncpy(p_dev->prod_id[i],
						    tmp, length);
		}
		mutex_unlock(&p_dev->socket->ops_mutex);
	}

	kfree(vers1);
@@ -660,7 +668,7 @@ static void pcmcia_delayed_add_device(struct work_struct *work)
	pcmcia_device_add(s, mfc_pfc);
}

static int pcmcia_requery(struct device *dev, void * _data)
static int pcmcia_requery_callback(struct device *dev, void * _data)
{
	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
	if (!p_dev->dev.driver) {
@@ -671,44 +679,57 @@ static int pcmcia_requery(struct device *dev, void * _data)
	return 0;
}

static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis)
static void pcmcia_requery(struct pcmcia_socket *s)
{
	int no_devices = 0;
	int ret = 0;

	/* must be called with skt_mutex held */
	dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock);
	int present;

	mutex_lock(&skt->ops_mutex);
	if (list_empty(&skt->devices_list))
		no_devices = 1;
	mutex_unlock(&skt->ops_mutex);
	mutex_lock(&s->ops_mutex);
	present = s->pcmcia_state.present;
	mutex_unlock(&s->ops_mutex);

	/* If this is because of a CIS override, start over */
	if (new_cis && !no_devices)
		pcmcia_card_remove(skt, NULL);
	if (!present)
		return;

	/* if no devices were added for this socket yet because of
	 * missing resource information or other trouble, we need to
	 * do this now. */
	if (no_devices || new_cis) {
		ret = pcmcia_card_add(skt);
		if (ret)
	if (s->functions == 0) {
		pcmcia_card_add(s);
		return;
	}

	/* some device information might have changed because of a CIS
	 * update or because we can finally read it correctly... so
	 * determine it again, overwriting old values if necessary. */
	bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery);
	bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback);

	/* if the CIS changed, we need to check whether the number of
	 * functions changed. */
	if (s->fake_cis) {
		int old_funcs, new_funcs;
		cistpl_longlink_mfc_t mfc;

		/* does this cis override add or remove functions? */
		old_funcs = s->functions;

		if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
					&mfc))
			new_funcs = mfc.nfn;
		else
			new_funcs = 1;
		if (old_funcs > new_funcs) {
			pcmcia_card_remove(s, NULL);
			pcmcia_card_add(s);
		} else if (new_funcs > old_funcs) {
			s->functions = new_funcs;
			pcmcia_device_add(s, 1);
		}
	}

	/* we re-scan all devices, not just the ones connected to this
	 * socket. This does not matter, though. */
	ret = bus_rescan_devices(&pcmcia_bus_type);
	if (ret)
		printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n");
	if (bus_rescan_devices(&pcmcia_bus_type))
		dev_warn(&s->dev, "rescanning the bus failed\n");
}


#ifdef CONFIG_PCMCIA_LOAD_CIS

/**
@@ -1085,7 +1106,6 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
	int ret;

	if (!count)
		return -EINVAL;
@@ -1093,11 +1113,7 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev,
	mutex_lock(&p_dev->socket->ops_mutex);
	p_dev->allow_func_id_match = 1;
	mutex_unlock(&p_dev->socket->ops_mutex);

	ret = bus_rescan_devices(&pcmcia_bus_type);
	if (ret)
		printk(KERN_INFO "pcmcia: bus_rescan_devices failed after "
		       "allowing func_id matches\n");
	pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY);

	return count;
}
@@ -1359,7 +1375,7 @@ EXPORT_SYMBOL(pcmcia_dev_present);
static struct pcmcia_callback pcmcia_bus_callback = {
	.owner = THIS_MODULE,
	.event = ds_event,
	.requery = pcmcia_bus_rescan,
	.requery = pcmcia_requery,
	.validate = pccard_validate_cis,
	.suspend = pcmcia_bus_suspend,
	.resume = pcmcia_bus_resume,
+1 −10
Original line number Diff line number Diff line
@@ -201,16 +201,7 @@ static ssize_t pccard_store_resource(struct device *dev,
		s->resource_setup_done = 1;
	mutex_unlock(&s->ops_mutex);

	mutex_lock(&s->skt_mutex);
	if ((s->callback) &&
	    (s->state & SOCKET_PRESENT) &&
	    !(s->state & SOCKET_CARDBUS)) {
		if (try_module_get(s->callback->owner)) {
			s->callback->requery(s, 0);
			module_put(s->callback->owner);
		}
	}
	mutex_unlock(&s->skt_mutex);
	pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);

	return count;
}