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

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
...@@ -150,7 +150,7 @@ static void update_rfkill_state(struct rfkill *rfkill) ...@@ -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 * 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. * 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, static int rfkill_toggle_radio(struct rfkill *rfkill,
enum rfkill_state state, enum rfkill_state state,
...@@ -216,8 +216,11 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) ...@@ -216,8 +216,11 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
rfkill_states[type] = state; rfkill_states[type] = state;
list_for_each_entry(rfkill, &rfkill_list, node) { 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); rfkill_toggle_radio(rfkill, state, 0);
mutex_unlock(&rfkill->mutex);
}
} }
mutex_unlock(&rfkill_mutex); mutex_unlock(&rfkill_mutex);
...@@ -228,7 +231,7 @@ EXPORT_SYMBOL(rfkill_switch_all); ...@@ -228,7 +231,7 @@ EXPORT_SYMBOL(rfkill_switch_all);
* rfkill_epo - emergency power off all transmitters * rfkill_epo - emergency power off all transmitters
* *
* This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring * 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) void rfkill_epo(void)
{ {
...@@ -236,7 +239,9 @@ void rfkill_epo(void) ...@@ -236,7 +239,9 @@ void rfkill_epo(void)
mutex_lock(&rfkill_mutex); mutex_lock(&rfkill_mutex);
list_for_each_entry(rfkill, &rfkill_list, node) { list_for_each_entry(rfkill, &rfkill_list, node) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
mutex_unlock(&rfkill->mutex);
} }
mutex_unlock(&rfkill_mutex); mutex_unlock(&rfkill_mutex);
} }
...@@ -372,6 +377,9 @@ static ssize_t rfkill_claim_store(struct device *dev, ...@@ -372,6 +377,9 @@ static ssize_t rfkill_claim_store(struct device *dev,
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
if (rfkill->user_claim_unsupported)
return -EOPNOTSUPP;
/* /*
* Take the global lock to make sure the kernel is not in * Take the global lock to make sure the kernel is not in
* the middle of rfkill_switch_all * the middle of rfkill_switch_all
...@@ -380,19 +388,17 @@ static ssize_t rfkill_claim_store(struct device *dev, ...@@ -380,19 +388,17 @@ static ssize_t rfkill_claim_store(struct device *dev,
if (error) if (error)
return error; return error;
if (rfkill->user_claim_unsupported) {
error = -EOPNOTSUPP;
goto out_unlock;
}
if (rfkill->user_claim != claim) { if (rfkill->user_claim != claim) {
if (!claim) if (!claim) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill, rfkill_toggle_radio(rfkill,
rfkill_states[rfkill->type], rfkill_states[rfkill->type],
0); 0);
mutex_unlock(&rfkill->mutex);
}
rfkill->user_claim = claim; rfkill->user_claim = claim;
} }
out_unlock:
mutex_unlock(&rfkill_mutex); mutex_unlock(&rfkill_mutex);
return error ? error : count; return error ? error : count;
...@@ -521,8 +527,11 @@ static void rfkill_remove_switch(struct rfkill *rfkill) ...@@ -521,8 +527,11 @@ static void rfkill_remove_switch(struct rfkill *rfkill)
{ {
mutex_lock(&rfkill_mutex); mutex_lock(&rfkill_mutex);
list_del_init(&rfkill->node); list_del_init(&rfkill->node);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
mutex_unlock(&rfkill_mutex); mutex_unlock(&rfkill_mutex);
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
mutex_unlock(&rfkill->mutex);
} }
/** /**
......
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