aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEliad Peller <eliad@wizery.com>2014-11-04 09:57:06 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-11-11 10:15:04 -0500
commitf4cf8680dfa42c80476a9cc5b90f933b12cf6d29 (patch)
tree7e9301164e06fd69d7e3d1c6de74d8472d661155
parent1507fb757a0a706a9aae002d45ea7c12b076a482 (diff)
iwlwifi: mvm/trans: abort d0i3_enter in case of held ref
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>
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-op-mode.h3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c20
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h1
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c12
4 files changed, 35 insertions, 1 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
index b6d666ee8359..17de6d46222a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
@@ -138,7 +138,8 @@ struct iwl_cfg;
138 * @nic_config: configure NIC, called before firmware is started. 138 * @nic_config: configure NIC, called before firmware is started.
139 * May sleep 139 * May sleep
140 * @wimax_active: invoked when WiMax becomes active. May sleep 140 * @wimax_active: invoked when WiMax becomes active. May sleep
141 * @enter_d0i3: configure the fw to enter d0i3. May sleep. 141 * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3
142 * entrance is aborted (e.g. due to held reference). May sleep.
142 * @exit_d0i3: configure the fw to exit d0i3. May sleep. 143 * @exit_d0i3: configure the fw to exit d0i3. May sleep.
143 */ 144 */
144struct iwl_op_mode_ops { 145struct iwl_op_mode_ops {
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 3276b31898da..4f9f77f1371d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -254,6 +254,26 @@ static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
254 spin_unlock_bh(&mvm->refs_lock); 254 spin_unlock_bh(&mvm->refs_lock);
255} 255}
256 256
257bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
258{
259 int i;
260 bool taken = false;
261
262 if (!iwl_mvm_is_d0i3_supported(mvm))
263 return true;
264
265 spin_lock_bh(&mvm->refs_lock);
266 for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
267 if (mvm->refs[i]) {
268 taken = true;
269 break;
270 }
271 }
272 spin_unlock_bh(&mvm->refs_lock);
273
274 return taken;
275}
276
257int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) 277int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
258{ 278{
259 iwl_mvm_ref(mvm, ref_type); 279 iwl_mvm_ref(mvm, ref_type);
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 1fc94e1db015..c89ac95c3288 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -1070,6 +1070,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
1070void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); 1070void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
1071void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); 1071void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
1072int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); 1072int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
1073bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
1073void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); 1074void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
1074int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); 1075int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
1075 1076
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 8c5bdf9360f9..be3dd4f5c7e1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -1041,6 +1041,18 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
1041 set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); 1041 set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
1042 synchronize_net(); 1042 synchronize_net();
1043 1043
1044 /*
1045 * iwl_mvm_ref_sync takes a reference before checking the flag.
1046 * so by checking there is no held reference we prevent a state
1047 * in which iwl_mvm_ref_sync continues successfully while we
1048 * configure the firmware to enter d0i3
1049 */
1050 if (iwl_mvm_ref_taken(mvm)) {
1051 IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n");
1052 clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
1053 return 1;
1054 }
1055
1044 ieee80211_iterate_active_interfaces_atomic(mvm->hw, 1056 ieee80211_iterate_active_interfaces_atomic(mvm->hw,
1045 IEEE80211_IFACE_ITER_NORMAL, 1057 IEEE80211_IFACE_ITER_NORMAL,
1046 iwl_mvm_enter_d0i3_iterator, 1058 iwl_mvm_enter_d0i3_iterator,