Skip to content

Commit

Permalink
iwlwifi: mvm: cleanup pending frames in DQA mode
Browse files Browse the repository at this point in the history
[ Upstream commit 9a3fcf9 ]

When a station is asleep, the fw will set it as "asleep".
All queues that are used only by one station will be stopped by
the fw.

In pre-DQA mode this was relevant for aggregation queues. However,
in DQA mode a queue is owned by one station only, so all queues
will be stopped.
As a result, we don't expect to get filtered frames back to
mac80211 and don't have to maintain the entire pending_frames
state logic, the same way as we do in aggregations.

The correct behavior is to align DQA behavior with the aggregation
queue behaviour pre-DQA:
- Don't count pending frames.
- Let mac80211 know we have frames in these queues so that it can
properly handle trigger frames.

When a trigger frame is received, mac80211 tells the driver to send
frames from the queues using release_buffered_frames.
The driver will tell the fw to let frames out even if the station
is asleep. This is done by iwl_mvm_sta_modify_sleep_tx_count.

Reported-and-tested-by: Jens Axboe <axboe@kernel.dk>
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
sara-s authored and gregkh committed Dec 20, 2017
1 parent a524bb5 commit 8d3a318
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 31 deletions.
5 changes: 3 additions & 2 deletions drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -2320,7 +2320,7 @@ iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);

/* Called when we need to transmit (a) frame(s) from agg queue */
/* Called when we need to transmit (a) frame(s) from agg or dqa queue */

iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
tids, more_data, true);
Expand All @@ -2340,7 +2340,8 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];

if (tid_data->state != IWL_AGG_ON &&
if (!iwl_mvm_is_dqa_supported(mvm) &&
tid_data->state != IWL_AGG_ON &&
tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
continue;

Expand Down
11 changes: 6 additions & 5 deletions drivers/net/wireless/intel/iwlwifi/mvm/sta.c
Original file line number Diff line number Diff line change
Expand Up @@ -3032,7 +3032,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_frame_release_type reason,
u16 cnt, u16 tids, bool more_data,
bool agg)
bool single_sta_queue)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_add_sta_cmd cmd = {
Expand All @@ -3052,14 +3052,14 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT)
cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]);

/* If we're releasing frames from aggregation queues then check if the
* all queues combined that we're releasing frames from have
/* If we're releasing frames from aggregation or dqa queues then check
* if all the queues that we're releasing frames from, combined, have:
* - more frames than the service period, in which case more_data
* needs to be set
* - fewer than 'cnt' frames, in which case we need to adjust the
* firmware command (but do that unconditionally)
*/
if (agg) {
if (single_sta_queue) {
int remaining = cnt;
int sleep_tx_count;

Expand All @@ -3069,7 +3069,8 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
u16 n_queued;

tid_data = &mvmsta->tid_data[tid];
if (WARN(tid_data->state != IWL_AGG_ON &&
if (WARN(!iwl_mvm_is_dqa_supported(mvm) &&
tid_data->state != IWL_AGG_ON &&
tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA,
"TID %d state is %d\n",
tid, tid_data->state)) {
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/wireless/intel/iwlwifi/mvm/sta.h
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum ieee80211_frame_release_type reason,
u16 cnt, u16 tids, bool more_data,
bool agg);
bool single_sta_queue);
int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain);
void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
Expand Down
41 changes: 18 additions & 23 deletions drivers/net/wireless/intel/iwlwifi/mvm/tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016 Intel Deutschland GmbH
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
Expand All @@ -34,6 +34,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -621,8 +622,10 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
* values.
* Note that we don't need to make sure it isn't agg'd, since we're
* TXing non-sta
* For DQA mode - we shouldn't increase it though
*/
atomic_inc(&mvm->pending_frames[sta_id]);
if (!iwl_mvm_is_dqa_supported(mvm))
atomic_inc(&mvm->pending_frames[sta_id]);

return 0;
}
Expand Down Expand Up @@ -1009,11 +1012,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,

spin_unlock(&mvmsta->lock);

/* Increase pending frames count if this isn't AMPDU */
if ((iwl_mvm_is_dqa_supported(mvm) &&
mvmsta->tid_data[tx_cmd->tid_tspec].state != IWL_AGG_ON &&
mvmsta->tid_data[tx_cmd->tid_tspec].state != IWL_AGG_STARTING) ||
(!iwl_mvm_is_dqa_supported(mvm) && !is_ampdu))
/* Increase pending frames count if this isn't AMPDU or DQA queue */
if (!iwl_mvm_is_dqa_supported(mvm) && !is_ampdu)
atomic_inc(&mvm->pending_frames[mvmsta->sta_id]);

return 0;
Expand Down Expand Up @@ -1083,12 +1083,13 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
lockdep_assert_held(&mvmsta->lock);

if ((tid_data->state == IWL_AGG_ON ||
tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA ||
iwl_mvm_is_dqa_supported(mvm)) &&
iwl_mvm_tid_queued(tid_data) == 0) {
/*
* Now that this aggregation queue is empty tell mac80211 so it
* knows we no longer have frames buffered for the station on
* this TID (for the TIM bitmap calculation.)
* Now that this aggregation or DQA queue is empty tell
* mac80211 so it knows we no longer have frames buffered for
* the station on this TID (for the TIM bitmap calculation.)
*/
ieee80211_sta_set_buffered(sta, tid, false);
}
Expand Down Expand Up @@ -1261,7 +1262,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
u8 skb_freed = 0;
u16 next_reclaimed, seq_ctl;
bool is_ndp = false;
bool txq_agg = false; /* Is this TXQ aggregated */

__skb_queue_head_init(&skbs);

Expand All @@ -1287,6 +1287,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
info->flags |= IEEE80211_TX_STAT_ACK;
break;
case TX_STATUS_FAIL_DEST_PS:
/* In DQA, the FW should have stopped the queue and not
* return this status
*/
WARN_ON(iwl_mvm_is_dqa_supported(mvm));
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
break;
default:
Expand Down Expand Up @@ -1391,15 +1395,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
bool send_eosp_ndp = false;

spin_lock_bh(&mvmsta->lock);
if (iwl_mvm_is_dqa_supported(mvm)) {
enum iwl_mvm_agg_state state;

state = mvmsta->tid_data[tid].state;
txq_agg = (state == IWL_AGG_ON ||
state == IWL_EMPTYING_HW_QUEUE_DELBA);
} else {
txq_agg = txq_id >= mvm->first_agg_queue;
}

if (!is_ndp) {
tid_data->next_reclaimed = next_reclaimed;
Expand Down Expand Up @@ -1456,11 +1451,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
* If the txq is not an AMPDU queue, there is no chance we freed
* several skbs. Check that out...
*/
if (txq_agg)
if (iwl_mvm_is_dqa_supported(mvm) || txq_id >= mvm->first_agg_queue)
goto out;

/* We can't free more than one frame at once on a shared queue */
WARN_ON(!iwl_mvm_is_dqa_supported(mvm) && (skb_freed > 1));
WARN_ON(skb_freed > 1);

/* If we have still frames for this STA nothing to do here */
if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
Expand Down

0 comments on commit 8d3a318

Please sign in to comment.