]> git.karo-electronics.de Git - linux-beck.git/commitdiff
iwlwifi: mvm/trans: abort d0i3_enter in case of held ref
authorEliad Peller <eliad@wizery.com>
Tue, 4 Nov 2014 14:57:06 +0000 (16:57 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Tue, 11 Nov 2014 15:15:04 +0000 (17:15 +0200)
Other contexts might call iwl_mvm_ref_sync() right before
we set IWL_MVM_STATUS_IN_D0I3, and then assume the fw/bus
is not in d0i3 state.

However, since we currently don't check for held references
in the d0i3_enter flow, we might enter d0i3 although there
is an active reference.

Solve it by aborting the d0i3 enter flow if there is an
active reference. Since users are assumed to use
iwl_mvm_ref_sync, which takes a ref before checking the
flag, we don't need further locking.

Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c

index b6d666ee8359df46c5052135fd7ddcfb82525868..17de6d46222ae7c803605017c066453d73076f01 100644 (file)
@@ -138,7 +138,8 @@ struct iwl_cfg;
  * @nic_config: configure NIC, called before firmware is started.
  *     May sleep
  * @wimax_active: invoked when WiMax becomes active. May sleep
- * @enter_d0i3: configure the fw to enter d0i3. May sleep.
+ * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3
+ *     entrance is aborted (e.g. due to held reference). May sleep.
  * @exit_d0i3: configure the fw to exit d0i3. May sleep.
  */
 struct iwl_op_mode_ops {
index 3276b31898da2d557cf44984d9cb79d698e422e6..4f9f77f1371de7edb134a120efbbe5a5e4689ac9 100644 (file)
@@ -254,6 +254,26 @@ static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
        spin_unlock_bh(&mvm->refs_lock);
 }
 
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
+{
+       int i;
+       bool taken = false;
+
+       if (!iwl_mvm_is_d0i3_supported(mvm))
+               return true;
+
+       spin_lock_bh(&mvm->refs_lock);
+       for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+               if (mvm->refs[i]) {
+                       taken = true;
+                       break;
+               }
+       }
+       spin_unlock_bh(&mvm->refs_lock);
+
+       return taken;
+}
+
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
 {
        iwl_mvm_ref(mvm, ref_type);
index 1fc94e1db0158273a8dd92aa25ec6e77ceca8fed..c89ac95c3288eef6a3db802c5420d07971c5a715 100644 (file)
@@ -1070,6 +1070,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
 int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
 
index 8c5bdf9360f9d45827e63e3995dec7bd0b8dd365..be3dd4f5c7e11b885d41feb6435cf35abd211b8c 100644 (file)
@@ -1041,6 +1041,18 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
        set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
        synchronize_net();
 
+       /*
+        * iwl_mvm_ref_sync takes a reference before checking the flag.
+        * so by checking there is no held reference we prevent a state
+        * in which iwl_mvm_ref_sync continues successfully while we
+        * configure the firmware to enter d0i3
+        */
+       if (iwl_mvm_ref_taken(mvm)) {
+               IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n");
+               clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+               return 1;
+       }
+
        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
                                                   IEEE80211_IFACE_ITER_NORMAL,
                                                   iwl_mvm_enter_d0i3_iterator,