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

Commit 064af111 authored by Henrique de Moraes Holschuh's avatar Henrique de Moraes Holschuh Committed by John W. Linville
Browse files

rfkill: mutex fixes



There are two mutexes in rfkill:

rfkill->mutex, which protects some of the fields of a rfkill struct, and is
also used for callback serialization.

rfkill_mutex, which protects the global state, the list of registered
rfkill structs and rfkill->claim.

Make sure to use the correct mutex, and to not miss locking rfkill->mutex
even when we already took rfkill_mutex.

Signed-off-by: default avatarHenrique de Moraes Holschuh <hmh@hmh.eng.br>
Acked-by: default avatarIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent f1b23361
Loading
Loading
Loading
Loading
+19 −10
Original line number Diff line number Diff line
@@ -150,7 +150,7 @@ static void update_rfkill_state(struct rfkill *rfkill)
 * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to
 * give the driver a hint that it should double-BLOCK the transmitter.
 *
 * Caller must have aquired rfkill_mutex.
 * Caller must have acquired rfkill->mutex.
 */
static int rfkill_toggle_radio(struct rfkill *rfkill,
				enum rfkill_state state,
@@ -216,8 +216,11 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
	rfkill_states[type] = state;

	list_for_each_entry(rfkill, &rfkill_list, node) {
		if ((!rfkill->user_claim) && (rfkill->type == type))
		if ((!rfkill->user_claim) && (rfkill->type == type)) {
			mutex_lock(&rfkill->mutex);
			rfkill_toggle_radio(rfkill, state, 0);
			mutex_unlock(&rfkill->mutex);
		}
	}

	mutex_unlock(&rfkill_mutex);
@@ -228,7 +231,7 @@ EXPORT_SYMBOL(rfkill_switch_all);
 * rfkill_epo - emergency power off all transmitters
 *
 * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring
 * everything in its path but rfkill_mutex.
 * everything in its path but rfkill_mutex and rfkill->mutex.
 */
void rfkill_epo(void)
{
@@ -236,7 +239,9 @@ void rfkill_epo(void)

	mutex_lock(&rfkill_mutex);
	list_for_each_entry(rfkill, &rfkill_list, node) {
		mutex_lock(&rfkill->mutex);
		rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
		mutex_unlock(&rfkill->mutex);
	}
	mutex_unlock(&rfkill_mutex);
}
@@ -372,6 +377,9 @@ static ssize_t rfkill_claim_store(struct device *dev,
	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	if (rfkill->user_claim_unsupported)
		return -EOPNOTSUPP;

	/*
	 * Take the global lock to make sure the kernel is not in
	 * the middle of rfkill_switch_all
@@ -380,19 +388,17 @@ static ssize_t rfkill_claim_store(struct device *dev,
	if (error)
		return error;

	if (rfkill->user_claim_unsupported) {
		error = -EOPNOTSUPP;
		goto out_unlock;
	}
	if (rfkill->user_claim != claim) {
		if (!claim)
		if (!claim) {
			mutex_lock(&rfkill->mutex);
			rfkill_toggle_radio(rfkill,
					    rfkill_states[rfkill->type],
					    0);
			mutex_unlock(&rfkill->mutex);
		}
		rfkill->user_claim = claim;
	}

out_unlock:
	mutex_unlock(&rfkill_mutex);

	return error ? error : count;
@@ -521,8 +527,11 @@ static void rfkill_remove_switch(struct rfkill *rfkill)
{
	mutex_lock(&rfkill_mutex);
	list_del_init(&rfkill->node);
	rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
	mutex_unlock(&rfkill_mutex);

	mutex_lock(&rfkill->mutex);
	rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
	mutex_unlock(&rfkill->mutex);
}

/**