Linux wireless drivers development
 help / color / mirror / Atom feed
From: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
To: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>, jjohnson@kernel.org
Cc: ath11k@lists.infradead.org, ath12k@lists.infradead.org,
	linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v5 3/4] wifi: ath11k: implement custom wake_tx_queue with flow control
Date: Sat, 8 Aug 2026 11:42:38 -0700	[thread overview]
Message-ID: <2774127a-01ce-4d26-b8fc-e68f49da2bbc@oss.qualcomm.com> (raw)
In-Reply-To: <20260807105843.20203-4-jtornosm@redhat.com>

On 8/7/2026 3:58 AM, Jose Ignacio Tornos Martinez wrote:
> Under heavy traffic, ath11k experiences frequent -ENOMEM errors
> ("failed to transmit frame -12") when the hardware TCL ring fills up.
> This issue is more commonly observed in VMs with PCIe passthrough but
> also occurs on bare metal systems. It is particularly problematic on
> devices with a single shared TCL ring where all traffic classes
> compete for the same 512 descriptor slots.
> 
> Implement a custom wake_tx_queue operation that:
> 
> 1. Checks hardware ring space before dequeuing packets from mac80211
> 2. Uses per-ring locking (wake_tx_lock with spin_lock_bh) to serialize
>    concurrent wake_tx_queue calls targeting the same ring and to ensure
>    bottom halves are disabled as required by ieee80211_tx_dequeue()
> 3. Syncs with hardware state to get accurate free slot count
> 4. Resolves the target TCL ring using get_ring_selector(txq->ac),
>    which selects the ring based on the access category
> 5. Returns early during firmware crash in the same way as other
>    tx paths
> 
> This approach follows the pattern used in the iwlwifi driver, adapted
> for ath11k's hardware ring architecture.
> 
> This eliminates -ENOMEM errors and improves throughput by optimizing
> resource usage and preventing unnecessary packet drops.

Missing Tested-on tag

> 
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
> v5: no modification
> v4: https://lore.kernel.org/all/20260724053121.15759-4-jtornosm@redhat.com/
> 
>  drivers/net/wireless/ath/ath11k/dp.c  |  1 +
>  drivers/net/wireless/ath/ath11k/dp.h  |  2 ++
>  drivers/net/wireless/ath/ath11k/mac.c | 52 ++++++++++++++++++++++++++-
>  3 files changed, 54 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
> index f389b97acbdd..2e5978ec2b05 100644
> --- a/drivers/net/wireless/ath/ath11k/dp.c
> +++ b/drivers/net/wireless/ath/ath11k/dp.c
> @@ -1087,6 +1087,7 @@ int ath11k_dp_alloc(struct ath11k_base *ab)
>  	for (i = 0; i < ab->hw_params.hal_params->num_tx_rings; i++) {
>  		idr_init(&dp->tx_ring[i].txbuf_idr);
>  		spin_lock_init(&dp->tx_ring[i].tx_idr_lock);
> +		spin_lock_init(&dp->tx_ring[i].wake_tx_lock);
>  		dp->tx_ring[i].tcl_data_ring_id = i;
>  
>  		dp->tx_ring[i].tx_status_head = 0;
> diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h
> index 84f66839f0c6..6d99501aa269 100644
> --- a/drivers/net/wireless/ath/ath11k/dp.h
> +++ b/drivers/net/wireless/ath/ath11k/dp.h
> @@ -87,6 +87,8 @@ struct dp_tx_ring {
>  	struct idr txbuf_idr;
>  	/* Protects txbuf_idr and num_pending */
>  	spinlock_t tx_idr_lock;
> +	/* Serializes wake_tx_queue operations for this ring */
> +	spinlock_t wake_tx_lock;
>  	struct hal_wbm_release_ring *tx_status;
>  	int tx_status_head;
>  	int tx_status_tail;
> diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
> index 2d55cdc4d165..9813f7923c34 100644
> --- a/drivers/net/wireless/ath/ath11k/mac.c
> +++ b/drivers/net/wireless/ath/ath11k/mac.c
> @@ -10065,9 +10065,59 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
>  	return ret;
>  }
>  
> +static void ath11k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
> +					struct ieee80211_txq *txq)
> +{
> +	struct ieee80211_tx_control control = {
> +		.sta = txq->sta,
> +	};
> +	struct ath11k *ar = hw->priv;
> +	struct dp_tx_ring *tx_ring;
> +	struct hal_srng *tcl_ring;
> +	struct sk_buff *skb;
> +	u32 ring_selector;
> +	int num_free;
> +	u8 ring_id;
> +
> +	if (!ar)
> +		return;

ar = hw->priv and the hw->priv space is allocated when hw is allocated.
so if hw is valid, hw->priv is valid, and this check is innecessary

> +
> +	ring_selector = ar->ab->hw_params.hw_ops->get_ring_selector(txq->ac);
> +	ring_id = ring_selector % ar->ab->hw_params.hal_params->num_tx_rings;
> +	tx_ring = &ar->ab->dp.tx_ring[ring_id];
> +	tcl_ring = &ar->ab->hal.srng_list[tx_ring->tcl_data_ring.ring_id];
> +
> +	while (1) {
> +		if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH,
> +				      &ar->ab->dev_flags)))
> +			break;
> +
> +		spin_lock_bh(&tx_ring->wake_tx_lock);
> +
> +		spin_lock(&tcl_ring->lock);
> +		num_free = ath11k_hal_srng_src_num_free(ar->ab, tcl_ring, true);
> +		spin_unlock(&tcl_ring->lock);
> +
> +		if (num_free == 0) {
> +			spin_unlock_bh(&tx_ring->wake_tx_lock);
> +			break;
> +		}
> +
> +		skb = ieee80211_tx_dequeue(hw, txq);
> +		if (!skb) {
> +			spin_unlock_bh(&tx_ring->wake_tx_lock);
> +			break;
> +		}
> +
> +		ath11k_mac_op_tx(hw, &control, skb);
> +
> +		spin_unlock_bh(&tx_ring->wake_tx_lock);
> +	}
> +}
> +
>  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,


  reply	other threads:[~2026-08-08 18:42 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-07 10:58 [PATCH v5 0/4] ath11k/ath12k: implement TX flow control Jose Ignacio Tornos Martinez
2026-08-07 10:58 ` [PATCH v5 1/4] wifi: ath11k: use queue mapping for WCN6750 ring selection Jose Ignacio Tornos Martinez
2026-08-07 10:58 ` [PATCH v5 2/4] wifi: ath11k/ath12k: remove skb parameter from get_ring_selector Jose Ignacio Tornos Martinez
2026-08-07 10:58 ` [PATCH v5 3/4] wifi: ath11k: implement custom wake_tx_queue with flow control Jose Ignacio Tornos Martinez
2026-08-08 18:42   ` Jeff Johnson [this message]
2026-08-07 10:58 ` [PATCH v5 4/4] wifi: ath12k: " Jose Ignacio Tornos Martinez
2026-08-08 19:29   ` Jeff Johnson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2774127a-01ce-4d26-b8fc-e68f49da2bbc@oss.qualcomm.com \
    --to=jeff.johnson@oss.qualcomm.com \
    --cc=ath11k@lists.infradead.org \
    --cc=ath12k@lists.infradead.org \
    --cc=jjohnson@kernel.org \
    --cc=jtornosm@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox