diff options
author | Liad Kaufman <liad.kaufman@intel.com> | 2015-08-19 17:34:28 +0300 |
---|---|---|
committer | Luca Coelho <luciano.coelho@intel.com> | 2016-07-06 00:14:27 +0300 |
commit | 9794c64f302d6d544acbb5ab69a327d694a70fcb (patch) | |
tree | 8b3da996e69d2c3257872255f4165320a961f8b8 /drivers/net/wireless/intel/iwlwifi/mvm/utils.c | |
parent | ca221c9b946cd4a9ea67375c8d90379a0e65179d (diff) |
iwlwifi: mvm: support dqa queue inactivation upon timeout
Support marking queues as inactive upon a timeout expiring,
and allow inactive queues to be re-assigned to other RA/TIDs
if no other queue is free.
This is done by keeping a timestamp of the latest frame TXed
for every RA/TID, and then going over the queues currently in
use when a new queue is needed, inactivating all those that
are inactive.
Signed-off-by: Liad Kaufman <liad.kaufman@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/utils.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 166 |
1 files changed, 165 insertions, 1 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 161b99efd63d..a0cb5ca4c9b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -579,17 +579,29 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) iwl_mvm_dump_umac_error_log(mvm); } -int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq) +int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq) { int i; lockdep_assert_held(&mvm->queue_info_lock); + /* Start by looking for a free queue */ for (i = minq; i <= maxq; i++) if (mvm->queue_info[i].hw_queue_refcount == 0 && mvm->queue_info[i].status == IWL_MVM_QUEUE_FREE) return i; + /* + * If no free queue found - settle for an inactive one to reconfigure + * Make sure that the inactive queue either already belongs to this STA, + * or that if it belongs to another one - it isn't the reserved queue + */ + for (i = minq; i <= maxq; i++) + if (mvm->queue_info[i].status == IWL_MVM_QUEUE_INACTIVE && + (sta_id == mvm->queue_info[i].ra_sta_id || + !mvm->queue_info[i].reserved)) + return i; + return -ENOSPC; } @@ -650,6 +662,7 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, else mvm->queue_info[queue].ra_sta_id = cfg->sta_id; mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); + mvm->queue_info[queue].ra_sta_id = cfg->sta_id; IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", @@ -752,6 +765,9 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, mvm->queue_info[queue].tid_bitmap = 0; mvm->queue_info[queue].hw_queue_to_mac80211 = 0; + /* Regardless if this is a reserved TXQ for a STA - mark it as false */ + mvm->queue_info[queue].reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); iwl_trans_txq_disable(mvm->trans, queue, false); @@ -1039,6 +1055,154 @@ out: ieee80211_connection_loss(vif); } +/* + * Remove inactive TIDs of a given queue. + * If all queue TIDs are inactive - mark the queue as inactive + * If only some the queue TIDs are inactive - unmap them from the queue + */ +static void iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, int queue, + unsigned long tid_bitmap) +{ + int tid; + + lockdep_assert_held(&mvmsta->lock); + lockdep_assert_held(&mvm->queue_info_lock); + + /* Go over all non-active TIDs, incl. IWL_MAX_TID_COUNT (for mgmt) */ + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + /* If some TFDs are still queued - don't mark TID as inactive */ + if (iwl_mvm_tid_queued(&mvmsta->tid_data[tid])) + tid_bitmap &= ~BIT(tid); + } + + /* If all TIDs in the queue are inactive - mark queue as inactive. */ + if (tid_bitmap == mvm->queue_info[queue].tid_bitmap) { + mvm->queue_info[queue].status = IWL_MVM_QUEUE_INACTIVE; + + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) + mvmsta->tid_data[tid].is_tid_active = false; + + IWL_DEBUG_TX_QUEUES(mvm, "Queue %d marked as inactive\n", + queue); + return; + } + + /* + * If we are here, this is a shared queue and not all TIDs timed-out. + * Remove the ones that did. + */ + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + int mac_queue = mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]; + + mvmsta->tid_data[tid].txq_id = IEEE80211_INVAL_HW_QUEUE; + mvm->queue_info[queue].hw_queue_to_mac80211 &= ~BIT(mac_queue); + mvm->queue_info[queue].hw_queue_refcount--; + mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); + mvmsta->tid_data[tid].is_tid_active = false; + + IWL_DEBUG_TX_QUEUES(mvm, + "Removing inactive TID %d from shared Q:%d\n", + tid, queue); + } + + IWL_DEBUG_TX_QUEUES(mvm, + "TXQ #%d left with tid bitmap 0x%x\n", queue, + mvm->queue_info[queue].tid_bitmap); + + /* + * There may be different TIDs with the same mac queues, so make + * sure all TIDs have existing corresponding mac queues enabled + */ + tid_bitmap = mvm->queue_info[queue].tid_bitmap; + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + mvm->queue_info[queue].hw_queue_to_mac80211 |= + BIT(mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]); + } + + /* TODO: if queue was shared - need to re-enable AGGs */ +} + +void iwl_mvm_inactivity_check(struct iwl_mvm *mvm) +{ + unsigned long timeout_queues_map = 0; + unsigned long now = jiffies; + int i; + + spin_lock_bh(&mvm->queue_info_lock); + for (i = 0; i < IWL_MAX_HW_QUEUES; i++) + if (mvm->queue_info[i].hw_queue_refcount > 0) + timeout_queues_map |= BIT(i); + spin_unlock_bh(&mvm->queue_info_lock); + + rcu_read_lock(); + + /* + * If a queue time outs - mark it as INACTIVE (don't remove right away + * if we don't have to.) This is an optimization in case traffic comes + * later, and we don't HAVE to use a currently-inactive queue + */ + for_each_set_bit(i, &timeout_queues_map, IWL_MAX_HW_QUEUES) { + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + u8 sta_id; + int tid; + unsigned long inactive_tid_bitmap = 0; + unsigned long queue_tid_bitmap; + + spin_lock_bh(&mvm->queue_info_lock); + queue_tid_bitmap = mvm->queue_info[i].tid_bitmap; + + /* If TXQ isn't in active use anyway - nothing to do here... */ + if (mvm->queue_info[i].status != IWL_MVM_QUEUE_READY) { + spin_unlock_bh(&mvm->queue_info_lock); + continue; + } + + /* Check to see if there are inactive TIDs on this queue */ + for_each_set_bit(tid, &queue_tid_bitmap, + IWL_MAX_TID_COUNT + 1) { + if (time_after(mvm->queue_info[i].last_frame_time[tid] + + IWL_MVM_DQA_QUEUE_TIMEOUT, now)) + continue; + + inactive_tid_bitmap |= BIT(tid); + } + spin_unlock_bh(&mvm->queue_info_lock); + + /* If all TIDs are active - finish check on this queue */ + if (!inactive_tid_bitmap) + continue; + + /* + * If we are here - the queue hadn't been served recently and is + * in use + */ + + sta_id = mvm->queue_info[i].ra_sta_id; + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + /* + * If the STA doesn't exist anymore, it isn't an error. It could + * be that it was removed since getting the queues, and in this + * case it should've inactivated its queues anyway. + */ + if (IS_ERR_OR_NULL(sta)) + continue; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + spin_lock_bh(&mvmsta->lock); + spin_lock(&mvm->queue_info_lock); + iwl_mvm_remove_inactive_tids(mvm, mvmsta, i, + inactive_tid_bitmap); + spin_unlock(&mvm->queue_info_lock); + spin_unlock_bh(&mvmsta->lock); + } + + rcu_read_unlock(); +} + int iwl_mvm_send_lqm_cmd(struct ieee80211_vif *vif, enum iwl_lqm_cmd_operatrions operation, u32 duration, u32 timeout) |