iwlwifi: mvm: don't allow negative reference count
authorSara Sharon <sara.sharon@intel.com>
Mon, 4 Apr 2016 16:30:05 +0000 (19:30 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Tue, 10 May 2016 19:14:52 +0000 (22:14 +0300)
Currently code allows mvm reference to become negative and
only warns in case mvm reference is released while reference
counting is 0.
However, we better prevent this from happening at all since
iwl_mvm_unref() may race against iwl_mvm_unref_all_except()
which is called on restart.
As a result we might get the same reference unreferenced twice
ending with a negative value:
An example for an easily reproduced log:
    [ 2689.909166] iwl_mvm_ref Take mvm reference - type 8
    [ 2690.732716] iwl_mvm_unref_all_except Cleanup: remove mvm ref type 8 (1)
    [ 2690.849708] iwl_mvm_unref Leave mvm reference - type 8
    [ 2690.849721] WARNING: ... iwl_mvm_unref+0xb0/0xc0 [iwlmvm]()

If there will be yet another another restart iwl_mvm_unref_all_except
will run from 0 up to ref count, and since it is unsigned, we will throw
the transport ref count completely out of balance:
    iwl_mvm_unref_all_except[I] -- Cleanup: remove mvm ref type 8 (255)
    iwl_trans_slv_unref[I] -- rpm counter: 0
    iwl_trans_slv_unref[I] -- rpm counter: -1
    iwl_trans_slv_unref[I] -- rpm counter: -2
 ...
    iwl_trans_slv_unref[I] -- rpm counter: -253
    iwl_trans_slv_unref[I] -- rpm counter: -254

As there is no valid scenario where we can get to a negative
reference count - prevent it from happening.

Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c

index 5648e3751205e8d9962df3343cf91c7a31bf8840..cd710176cea0852a0371dc2d801653f21ad41a77 100644 (file)
@@ -229,7 +229,11 @@ void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
 
        IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
        spin_lock_bh(&mvm->refs_lock);
-       WARN_ON(!mvm->refs[ref_type]--);
+       if (WARN_ON(!mvm->refs[ref_type])) {
+               spin_unlock_bh(&mvm->refs_lock);
+               return;
+       }
+       mvm->refs[ref_type]--;
        spin_unlock_bh(&mvm->refs_lock);
        iwl_trans_unref(mvm->trans);
 }