[PATCH ath-next v3 5/9] wifi: ath11k: schedule TXQs from the driver
Julius Bairaktaris <[email protected]>
| Newsgroups | org.infradead.lists.ath11k,org.kernel.vger.linux-kernel,org.kernel.vger.linux-wireless |
|---|---|
| Message-ID | <[email protected]> |
ieee80211_handle_wake_tx_queue() has exactly one entry point, the wake_tx_queue callback, so a round starts only where mac80211 calls that callback: a frame arriving from the network stack, a block ack session starting or stopping, a station leaving powersave, or a stopped hardware queue restarting. Every round runs under local->handle_wake_tx_queue_lock, which is shared by all four access categories of the hardware. That is the wrong shape for a driver whose only backpressure is the airtime queue limit. When ieee80211_txq_airtime_check() refuses a station, ieee80211_tx_dequeue() stops handing out frames and the station's intermediate queue keeps its backlog until the next enqueue, not until the airtime it is waiting on comes back from the hardware. Returning that airtime does not reschedule anything. Open-code the round in the driver so that a second context can drive it. The walk is the same deficit round-robin over the active TXQs of one access category that the generic handler performs. A selection ends where the airtime queue limit binds, because ieee80211_tx_dequeue() applies the limit itself, and drains the station's queue where it does not, which is what the generic handler does today. mac80211 documents that a driver must not run concurrent scheduling rounds, and the next patch adds a second context that starts one. The round therefore takes a per-category lock rather than the hardware-wide one. ath10k and mt76 also drive the round from the driver; ath10k takes its per-category lock on the wake path alone, and mt76 serialises instead by running its rounds from one per-device tx worker. A custom .wake_tx_queue with a tx-completion driven push was proposed for ath11k once before [1]; the design was right and the measurement that justifies it is in this series. Link: https://lore.kernel.org/all/[email protected]/ [1] Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1 Assisted-by: Claude:claude-opus-5 Signed-off-by: Julius Bairaktaris <[email protected]> --- drivers/net/wireless/ath/ath11k/core.h | 5 +++ drivers/net/wireless/ath/ath11k/mac.c | 44 ++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index a0d725923ef2..40895d276de5 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -709,6 +709,11 @@ struct ath11k { */ spinlock_t data_lock; + /* serialises one scheduling round per access category against the + * other contexts that drive it + */ + spinlock_t txq_lock[IEEE80211_NUM_ACS]; + struct list_head arvifs; /* should never be NULL; needed for regular htt rx */ struct ieee80211_channel *rx_channel; diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 40a2b6d2f804..342b38308468 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6546,6 +6546,43 @@ static void ath11k_mac_op_tx(struct ieee80211_hw *hw, } } +static void ath11k_mac_tx_push_txq(struct ath11k *ar, struct ieee80211_txq *txq) +{ + struct ieee80211_tx_control control = { .sta = txq->sta }; + struct sk_buff *skb; + + /* ieee80211_tx_dequeue() applies the airtime queue limit itself, so a + * selection ends where that limit binds, where the hardware queue is + * stopped, or when the queue empties, as the generic handler does + * today. + */ + while ((skb = ieee80211_tx_dequeue(ar->hw, txq))) + ath11k_mac_op_tx(ar->hw, &control, skb); +} + +static void ath11k_mac_schedule_txq(struct ath11k *ar, u8 ac) +{ + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_txq *txq; + + spin_lock_bh(&ar->txq_lock[ac]); + + ieee80211_txq_schedule_start(hw, ac); + while ((txq = ieee80211_next_txq(hw, ac))) { + ath11k_mac_tx_push_txq(ar, txq); + ieee80211_return_txq(hw, txq, false); + } + ieee80211_txq_schedule_end(hw, ac); + + spin_unlock_bh(&ar->txq_lock[ac]); +} + +static void ath11k_mac_op_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + ath11k_mac_schedule_txq(hw->priv, txq->ac); +} + void ath11k_mac_drain_tx(struct ath11k *ar) { /* make sure rcu-protected mac80211 tx path itself is drained */ @@ -10067,7 +10104,7 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, static const struct ieee80211_ops ath11k_ops = { .tx = ath11k_mac_op_tx, - .wake_tx_queue = ieee80211_handle_wake_tx_queue, + .wake_tx_queue = ath11k_mac_op_wake_tx_queue, .start = ath11k_mac_op_start, .stop = ath11k_mac_op_stop, .reconfig_complete = ath11k_mac_op_reconfig_complete, @@ -10757,7 +10794,7 @@ int ath11k_mac_allocate(struct ath11k_base *ab) struct ath11k *ar; struct ath11k_pdev *pdev; int ret; - int i; + int i, j; if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) return 0; @@ -10791,6 +10828,9 @@ int ath11k_mac_allocate(struct ath11k_base *ab) pdev->ar = ar; spin_lock_init(&ar->data_lock); + + for (j = 0; j < IEEE80211_NUM_ACS; j++) + spin_lock_init(&ar->txq_lock[j]); INIT_LIST_HEAD(&ar->arvifs); INIT_LIST_HEAD(&ar->ppdu_stats_info); mutex_init(&ar->conf_mutex); -- 2.53.0