Subject: Re: [PATCH net-next 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct

From: Lorenzo Bianconi <[email-masked]>
Date: Thu, 28 May 2026 14:49:14 +0200

> Since now multiple net_devices connected to different QDMA blocks can
> share the same GDM port, qos_sq_bmap field can be overwritten with the
> configuration obtained from a net_device connected to a different QDMA
> block. In order to fix the issue move qos_sq_bmap field from
> airoha_gdm_port struct to airoha_gdm_dev one.
> Add qos_channel_map bitmap in airoha_qdma struct to track if a shared
> QDMA channel is already in use by another net_device.
> 
> Tested-by: Xuegang Lu <[email-masked]>
> Signed-off-by: Lorenzo Bianconi <[email-masked]>
> ---
>  drivers/net/ethernet/airoha/airoha_eth.c | 58 ++++++++++++++++++++------------
>  drivers/net/ethernet/airoha/airoha_eth.h |  6 ++--
>  2 files changed, 40 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index c5d25f7640ac..4f77a0c22162 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2602,30 +2602,40 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
>  	return 0;
>  }
>  
> -static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> -					  struct tc_htb_qopt_offload *opt)
> +static int airoha_tc_htb_modify_queue(struct net_device *dev,
> +				      struct tc_htb_qopt_offload *opt)
>  {
>  	u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
>  	u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
> -	int err, num_tx_queues = netdev->real_num_tx_queues;
> -	struct airoha_gdm_dev *dev = netdev_priv(netdev);
> -	struct airoha_gdm_port *port = dev->port;
>  
>  	if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
>  		NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
>  		return -EINVAL;
>  	}
>  
> -	err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
> -					    opt->quantum);
> -	if (err) {
> +	return airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
> +}
> +
> +static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> +					  struct tc_htb_qopt_offload *opt)
> +{
> +	u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> +	int err, num_tx_queues = netdev->real_num_tx_queues;
> +	struct airoha_gdm_dev *dev = netdev_priv(netdev);
> +	struct airoha_qdma *qdma = dev->qdma;
> +
> +	/* Here we need to check the requested QDMA channel is not already
> +	 * in use by another net_device running on the same QDMA block.
> +	 */
> +	if (test_and_set_bit(channel, qdma->qos_channel_map)) {
>  		NL_SET_ERR_MSG_MOD(opt->extack,
> -				   "failed configuring htb offload");
> -		return err;
> +				   "qdma qos channel already in use");
> +		return -EBUSY;
>  	}
>  
> -	if (opt->command == TC_HTB_NODE_MODIFY)
> -		return 0;
> +	err = airoha_tc_htb_modify_queue(netdev, opt);
> +	if (err)
> +		goto error;
>  
>  	err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
>  	if (err) {
> @@ -2633,13 +2643,17 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
>  					      opt->quantum);
>  		NL_SET_ERR_MSG_MOD(opt->extack,
>  				   "failed setting real_num_tx_queues");
> -		return err;
> +		goto error;
>  	}
>  
> -	set_bit(channel, port->qos_sq_bmap);
> +	set_bit(channel, dev->qos_sq_bmap);
>  	opt->qid = AIROHA_NUM_TX_RING + channel;
>  
>  	return 0;
> +error:
> +	clear_bit(channel, qdma->qos_channel_map);
> +
> +	return err;
>  }
>  
>  static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
> @@ -2820,11 +2834,13 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
>  static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
>  {
>  	struct airoha_gdm_dev *dev = netdev_priv(netdev);
> -	struct airoha_gdm_port *port = dev->port;
> +	struct airoha_qdma *qdma = dev->qdma;
>  
>  	netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
>  	airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
> -	clear_bit(queue, port->qos_sq_bmap);
> +
> +	clear_bit(queue, qdma->qos_channel_map);
> +	clear_bit(queue, dev->qos_sq_bmap);
>  }
>  
>  static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
> @@ -2832,9 +2848,8 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
>  {
>  	u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
>  	struct airoha_gdm_dev *dev = netdev_priv(netdev);
> -	struct airoha_gdm_port *port = dev->port;
>  
> -	if (!test_bit(channel, port->qos_sq_bmap)) {
> +	if (!test_bit(channel, dev->qos_sq_bmap)) {
>  		NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
>  		return -EINVAL;
>  	}
> @@ -2847,10 +2862,9 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
>  static int airoha_tc_htb_destroy(struct net_device *netdev)
>  {
>  	struct airoha_gdm_dev *dev = netdev_priv(netdev);
> -	struct airoha_gdm_port *port = dev->port;
>  	int q;
>  
> -	for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
> +	for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
>  		airoha_tc_remove_htb_queue(netdev, q);
>  
>  	return 0;
> @@ -2861,9 +2875,8 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
>  {
>  	u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
>  	struct airoha_gdm_dev *dev = netdev_priv(netdev);
> -	struct airoha_gdm_port *port = dev->port;
>  
> -	if (!test_bit(channel, port->qos_sq_bmap)) {
> +	if (!test_bit(channel, dev->qos_sq_bmap)) {
>  		NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
>  		return -EINVAL;
>  	}
> @@ -2882,6 +2895,7 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
>  	case TC_HTB_DESTROY:
>  		return airoha_tc_htb_destroy(dev);
>  	case TC_HTB_NODE_MODIFY:
> +		return airoha_tc_htb_modify_queue(dev, opt);
>  	case TC_HTB_LEAF_ALLOC_QUEUE:
>  		return airoha_tc_htb_alloc_leaf_queue(dev, opt);
>  	case TC_HTB_LEAF_DEL:
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index f6f59d25abd9..a308a770116b 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -533,6 +533,8 @@ struct airoha_qdma {
>  
>  	struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
>  	struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
> +
> +	DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
>  };
>  
>  struct airoha_gdm_dev {
> @@ -540,6 +542,8 @@ struct airoha_gdm_dev {
>  	struct airoha_qdma *qdma;
>  	struct net_device *dev;
>  	struct airoha_eth *eth;
> +
> +	DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
>  };
>  
>  struct airoha_gdm_port {
> @@ -549,8 +553,6 @@ struct airoha_gdm_port {
>  
>  	struct airoha_hw_stats stats;
>  
> -	DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
> -
>  	/* qos stats counters */
>  	u64 cpu_tx_packets;
>  	u64 fwd_tx_packets;
> 
> -- 
> 2.54.0
> 

commenting on sashiko's report:
https://sashiko.dev/#/patchset/20260527-airoha-eth-multi-serdes-preliminary-v1-0-ec6ed73ef7fc%40kernel.org
- This isn't a bug introduced by this patch, but does this modify operation
  skip verifying if the requesting net_device actually owns the channel?
  - Since this issue has not been itroduced by this patch, I will fix it with a
    dedicated patch.

- This is a pre-existing issue, but does this manage real_num_tx_queues as an
  active queue counter rather than a bounding limit?
  - Since this issue has not been itroduced by this patch, I will fix it with a
    dedicated patch.

- This isn't a bug introduced by this patch, but is there an off-by-one error
  when clearing the TX rate limit for the removed queue?
  - Since this issue has not been itroduced by this patch, I will fix it with a
    dedicated patch.

Regards,
Lorenzo