rfkill: mutex fixes
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>
Tue, 22 Jul 2008 00:18:20 +0000 (21:18 -0300)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 29 Jul 2008 20:36:35 +0000 (16:36 -0400)
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: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Acked-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/rfkill/rfkill.c

index fc3a4fd88995a3525009ab3c2025b067834af392..ac205eceaf42b9167c71d257165a0eb1fddbd184 100644 (file)
@@ -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);
 }
 
 /**