Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH net-next v3 06/13] mlx5: convert to ndo_set_rx_mode_async
From: Stanislav Fomichev @ 2026-03-20 15:42 UTC (permalink / raw)
  To: Cosmin Ratiu
  Cc: netdev@vger.kernel.org, sdf@fomichev.me,
	intel-wired-lan@lists.osuosl.org, bestswngs@gmail.com,
	kernel-team@meta.com, przemyslaw.kitszel@intel.com,
	davem@davemloft.net, pabeni@redhat.com, horms@kernel.org,
	corbet@lwn.net, anthony.l.nguyen@intel.com, willemb@google.com,
	linux-kernel@vger.kernel.org, skhawaja@google.com, Dragos Tatulea,
	kees@kernel.org, Jianbo Liu, alexanderduyck@fb.com,
	kuba@kernel.org, leon@kernel.org, Saeed Mahameed,
	andrew+netdev@lunn.ch, michael.chan@broadcom.com, Mark Bloch,
	sd@queasysnail.net, Tariq Toukan, jacob.e.keller@intel.com,
	skhan@linuxfoundation.org, mohsin.bashr@gmail.com,
	edumazet@google.com, pavan.chebbi@broadcom.com,
	linux-kselftest@vger.kernel.org, linux-rdma@vger.kernel.org,
	johannes@sipsolutions.net, linux-doc@vger.kernel.org,
	aleksandr.loktionov@intel.com, linux-wireless@vger.kernel.org
In-Reply-To: <c0915086dc876f59e3c69886a8629efa3540d737.camel@nvidia.com>

On 03/20, Cosmin Ratiu wrote:
> On Thu, 2026-03-19 at 18:24 -0700, Stanislav Fomichev wrote:
> > Convert mlx5 from ndo_set_rx_mode to ndo_set_rx_mode_async. The
> > driver's mlx5e_set_rx_mode now receives uc/mc snapshots and calls
> > mlx5e_fs_set_rx_mode_work directly instead of queueing work.
> > 
> > mlx5e_sync_netdev_addr and mlx5e_handle_netdev_addr now take
> > explicit uc/mc list parameters and iterate with
> > netdev_hw_addr_list_for_each instead of netdev_for_each_{uc,mc}_addr.
> > 
> > Fallback to netdev's uc/mc in a few places and grab addr lock.
> > 
> > Cc: Saeed Mahameed <saeedm@nvidia.com>
> > Cc: Tariq Toukan <tariqt@nvidia.com>
> > Cc: Cosmin Ratiu <cratiu@nvidia.com>
> > Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> > Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
> > ---
> >  .../net/ethernet/mellanox/mlx5/core/en/fs.h   |  5 +++-
> >  .../net/ethernet/mellanox/mlx5/core/en_fs.c   | 30 ++++++++++++-----
> > --
> >  .../net/ethernet/mellanox/mlx5/core/en_main.c | 16 +++++++---
> >  3 files changed, 36 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> > b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> > index c3408b3f7010..091b80a67189 100644
> > --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> > +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> > @@ -201,7 +201,10 @@ int mlx5e_add_vlan_trap(struct
> > mlx5e_flow_steering *fs, int  trap_id, int tir_nu
> >  void mlx5e_remove_vlan_trap(struct mlx5e_flow_steering *fs);
> >  int mlx5e_add_mac_trap(struct mlx5e_flow_steering *fs, int  trap_id,
> > int tir_num);
> >  void mlx5e_remove_mac_trap(struct mlx5e_flow_steering *fs);
> > -void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs,
> > struct net_device *netdev);
> > +void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs,
> > +			       struct net_device *netdev,
> > +			       struct netdev_hw_addr_list *uc,
> > +			       struct netdev_hw_addr_list *mc);
> >  int mlx5e_fs_vlan_rx_add_vid(struct mlx5e_flow_steering *fs,
> >  			     struct net_device *netdev,
> >  			     __be16 proto, u16 vid);
> > diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> > b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> > index 55255fe6e415..a9daefbd8f8f 100644
> > --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> > +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> > @@ -609,20 +609,26 @@ static void mlx5e_execute_l2_action(struct
> > mlx5e_flow_steering *fs,
> >  }
> >  
> >  static void mlx5e_sync_netdev_addr(struct mlx5e_flow_steering *fs,
> > -				   struct net_device *netdev)
> > +				   struct net_device *netdev,
> > +				   struct netdev_hw_addr_list *uc,
> > +				   struct netdev_hw_addr_list *mc)
> >  {
> >  	struct netdev_hw_addr *ha;
> >  
> > -	netif_addr_lock_bh(netdev);
> > +	if (!uc || !mc) {
> > +		netif_addr_lock_bh(netdev);
> > +		mlx5e_sync_netdev_addr(fs, netdev, &netdev->uc,
> > &netdev->mc);
> > +		netif_addr_unlock_bh(netdev);
> > +		return;
> > +	}
> >  
> >  	mlx5e_add_l2_to_hash(fs->l2.netdev_uc, netdev->dev_addr);
> > -	netdev_for_each_uc_addr(ha, netdev)
> > +
> > +	netdev_hw_addr_list_for_each(ha, uc)
> >  		mlx5e_add_l2_to_hash(fs->l2.netdev_uc, ha->addr);
> >  
> > -	netdev_for_each_mc_addr(ha, netdev)
> > +	netdev_hw_addr_list_for_each(ha, mc)
> >  		mlx5e_add_l2_to_hash(fs->l2.netdev_mc, ha->addr);
> > -
> > -	netif_addr_unlock_bh(netdev);
> >  }
> >  
> >  static void mlx5e_fill_addr_array(struct mlx5e_flow_steering *fs,
> > int list_type,
> > @@ -724,7 +730,9 @@ static void mlx5e_apply_netdev_addr(struct
> > mlx5e_flow_steering *fs)
> >  }
> >  
> >  static void mlx5e_handle_netdev_addr(struct mlx5e_flow_steering *fs,
> > -				     struct net_device *netdev)
> > +				     struct net_device *netdev,
> > +				     struct netdev_hw_addr_list *uc,
> > +				     struct netdev_hw_addr_list *mc)
> >  {
> >  	struct mlx5e_l2_hash_node *hn;
> >  	struct hlist_node *tmp;
> > @@ -736,7 +744,7 @@ static void mlx5e_handle_netdev_addr(struct
> > mlx5e_flow_steering *fs,
> >  		hn->action = MLX5E_ACTION_DEL;
> >  
> >  	if (fs->state_destroy)
> > -		mlx5e_sync_netdev_addr(fs, netdev);
> > +		mlx5e_sync_netdev_addr(fs, netdev, uc, mc);
> >  
> >  	mlx5e_apply_netdev_addr(fs);
> >  }
> > @@ -820,7 +828,9 @@ static void mlx5e_destroy_promisc_table(struct
> > mlx5e_flow_steering *fs)
> >  }
> >  
> >  void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs,
> > -			       struct net_device *netdev)
> > +			       struct net_device *netdev,
> > +			       struct netdev_hw_addr_list *uc,
> > +			       struct netdev_hw_addr_list *mc)
> >  {
> >  	struct mlx5e_l2_table *ea = &fs->l2;
> >  
> > @@ -850,7 +860,7 @@ void mlx5e_fs_set_rx_mode_work(struct
> > mlx5e_flow_steering *fs,
> >  	if (enable_broadcast)
> >  		mlx5e_add_l2_flow_rule(fs, &ea->broadcast,
> > MLX5E_FULLMATCH);
> >  
> > -	mlx5e_handle_netdev_addr(fs, netdev);
> > +	mlx5e_handle_netdev_addr(fs, netdev, uc, mc);
> >  
> >  	if (disable_broadcast)
> >  		mlx5e_del_l2_flow_rule(fs, &ea->broadcast);
> > diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> > b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> > index f7009da94f0b..e86cf1ee108d 100644
> > --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> > +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> > @@ -4108,11 +4108,16 @@ static void mlx5e_nic_set_rx_mode(struct
> > mlx5e_priv *priv)
> >  	queue_work(priv->wq, &priv->set_rx_mode_work);
> >  }
> >  
> > -static void mlx5e_set_rx_mode(struct net_device *dev)
> > +static void mlx5e_set_rx_mode(struct net_device *dev,
> > +			      struct netdev_hw_addr_list *uc,
> > +			      struct netdev_hw_addr_list *mc)
> >  {
> >  	struct mlx5e_priv *priv = netdev_priv(dev);
> >  
> > -	mlx5e_nic_set_rx_mode(priv);
> > +	if (mlx5e_is_uplink_rep(priv))
> > +		return; /* no rx mode for uplink rep */
> > +
> > +	mlx5e_fs_set_rx_mode_work(priv->fs, dev, uc, mc);
> 
> While this chunk is correct, I think there's a logical conflict waiting
> to happen with Saeed's pending patch touching this area ([1]).
> 
> You have inlined mlx5e_nic_set_rx_mode here, but after Saeed's patch
> the mlx5e_is_uplink_rep condition added here should be dropped.
> 
> Not sure the automatic merge will do that.
> 
> [1]
> https://lore.kernel.org/netdev/20260319005456.82745-1-saeed@kernel.org/T/#u

Thanks for the heads up, will try to wait for this to be pulled before
reposting!

^ permalink raw reply

* [PATCH rtw-next] wifi: rtw89: Add support for Buffalo WI-U3-2400XE2
From: Zenm Chen @ 2026-03-20 15:41 UTC (permalink / raw)
  To: linux-wireless, pkshih, rtl8821cerfe2; +Cc: usbwifi2024, Zenm Chen

Add the ID 0411:03a6 to the table to support an additional RTL8832CU
adapter: Buffalo WI-U3-2400XE2.

Link: https://github.com/morrownr/rtw89/commit/506d193b8cb7d6394509aebcf8de1531629f6100
Signed-off-by: Zenm Chen <zenmchen@gmail.com>
---
 drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
index 0c5aebaed..86be0981a 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -39,6 +39,8 @@ static const struct rtw89_driver_info rtw89_8852cu_info = {
 };
 
 static const struct usb_device_id rtw_8852cu_id_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03a6, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc832, 0xff, 0xff, 0xff),
 	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85a, 0xff, 0xff, 0xff),
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH net-next v3 04/13] net: move promiscuity handling into dev_rx_mode_work
From: Stanislav Fomichev @ 2026-03-20 15:41 UTC (permalink / raw)
  To: Loktionov, Aleksandr
  Cc: Stanislav Fomichev, netdev@vger.kernel.org, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	horms@kernel.org, corbet@lwn.net, skhan@linuxfoundation.org,
	andrew+netdev@lunn.ch, michael.chan@broadcom.com,
	pavan.chebbi@broadcom.com, Nguyen, Anthony L, Kitszel, Przemyslaw,
	saeedm@nvidia.com, tariqt@nvidia.com, mbloch@nvidia.com,
	alexanderduyck@fb.com, kernel-team@meta.com,
	johannes@sipsolutions.net, sd@queasysnail.net, jianbol@nvidia.com,
	dtatulea@nvidia.com, mohsin.bashr@gmail.com, Keller, Jacob E,
	willemb@google.com, skhawaja@google.com, bestswngs@gmail.com,
	kees@kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, intel-wired-lan@lists.osuosl.org,
	linux-rdma@vger.kernel.org, linux-wireless@vger.kernel.org,
	linux-kselftest@vger.kernel.org, leon@kernel.org
In-Reply-To: <IA3PR11MB89866C27B28AE7D7D807F37EE54CA@IA3PR11MB8986.namprd11.prod.outlook.com>

On 03/20, Loktionov, Aleksandr wrote:
> 
> 
> > -----Original Message-----
> > From: Stanislav Fomichev <sdf@fomichev.me>
> > Sent: Friday, March 20, 2026 2:25 AM
> > To: netdev@vger.kernel.org
> > Cc: davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> > pabeni@redhat.com; horms@kernel.org; corbet@lwn.net;
> > skhan@linuxfoundation.org; andrew+netdev@lunn.ch;
> > michael.chan@broadcom.com; pavan.chebbi@broadcom.com; Nguyen, Anthony
> > L <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> > <przemyslaw.kitszel@intel.com>; saeedm@nvidia.com; tariqt@nvidia.com;
> > mbloch@nvidia.com; alexanderduyck@fb.com; kernel-team@meta.com;
> > johannes@sipsolutions.net; sd@queasysnail.net; jianbol@nvidia.com;
> > dtatulea@nvidia.com; sdf@fomichev.me; mohsin.bashr@gmail.com; Keller,
> > Jacob E <jacob.e.keller@intel.com>; willemb@google.com;
> > skhawaja@google.com; bestswngs@gmail.com; Loktionov, Aleksandr
> > <aleksandr.loktionov@intel.com>; kees@kernel.org; linux-
> > doc@vger.kernel.org; linux-kernel@vger.kernel.org; intel-wired-
> > lan@lists.osuosl.org; linux-rdma@vger.kernel.org; linux-
> > wireless@vger.kernel.org; linux-kselftest@vger.kernel.org;
> > leon@kernel.org
> > Subject: [PATCH net-next v3 04/13] net: move promiscuity handling into
> > dev_rx_mode_work
> > 
> > Move unicast promiscuity tracking into dev_rx_mode_work so it runs
> > under netdev_ops_lock instead of under the addr_lock spinlock. This is
> > required because __dev_set_promiscuity calls dev_change_rx_flags and
> > __dev_notify_flags, both of which may need to sleep.
> > 
> > Change ASSERT_RTNL() to netdev_ops_assert_locked() in
> > __dev_set_promiscuity, netif_set_allmulti and __dev_change_flags since
> > these are now called from the work queue under the ops lock.
> > 
> > Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> > Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
> > ---
> >  Documentation/networking/netdevices.rst |  4 ++
> >  net/core/dev.c                          | 79 +++++++++++++++++-------
> > -
> >  2 files changed, 57 insertions(+), 26 deletions(-)
> > 
> > diff --git a/Documentation/networking/netdevices.rst
> > b/Documentation/networking/netdevices.rst
> > index dc83d78d3b27..5cdaa1a3dcc8 100644
> > --- a/Documentation/networking/netdevices.rst
> > +++ b/Documentation/networking/netdevices.rst
> > @@ -298,6 +298,10 @@ struct net_device synchronization rules
> >  	Notes: Sleepable version of ndo_set_rx_mode. Receives snapshots
> >  	of the unicast and multicast address lists.
> > 
> > +ndo_change_rx_flags:
> > +	Synchronization: rtnl_lock() semaphore. In addition, netdev
> > instance
> > +	lock if the driver implements queue management or shaper API.
> > +
> >  ndo_setup_tc:
> >  	``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` are running under NFT
> > locks
> >  	(i.e. no ``rtnl_lock`` and no device instance lock). The rest
> > of diff --git a/net/core/dev.c b/net/core/dev.c index
> > fedc423306fc..fc5c9b14faa0 100644
> > --- a/net/core/dev.c
> > +++ b/net/core/dev.c
> > @@ -9574,7 +9574,7 @@ static int __dev_set_promiscuity(struct
> > net_device *dev, int inc, bool notify)
> >  	kuid_t uid;
> >  	kgid_t gid;
> > 
> > -	ASSERT_RTNL();
> > +	netdev_ops_assert_locked(dev);
> Can you explain why do you add new hard precondition of ops lock must be held?

The context is that in f792709e0baa ("selftests: net: validate team flags
propagation") I had to add locking around NETDEV_CHANGE notifiers and
add that ugly `if (notify) netdev_ops_assert_locked` check. After this
patch I believe we are consistently calling __dev_set_promiscuity
with the ops lock (for ops locked netdev), so we can cleanup this enforcement
part. 

> >  	promiscuity = dev->promiscuity + inc;
> >  	if (promiscuity == 0) {
> > @@ -9610,16 +9610,8 @@ static int __dev_set_promiscuity(struct
> > net_device *dev, int inc, bool notify)
> > 
> >  		dev_change_rx_flags(dev, IFF_PROMISC);
> >  	}
> 
> ...
> 
> >  	__hw_addr_init(&uc_snap);
> > @@ -9704,16 +9720,29 @@ static void dev_rx_mode_work(struct
> > work_struct *work)
> >  		if (!err)
> >  			err = __hw_addr_list_snapshot(&mc_ref, &dev->mc,
> >  						      dev->addr_len);
> > -		netif_addr_unlock_bh(dev);
> > 
> >  		if (err) {
> >  			netdev_WARN(dev, "failed to sync uc/mc
> > addresses\n");
> >  			__hw_addr_flush(&uc_snap);
> >  			__hw_addr_flush(&uc_ref);
> >  			__hw_addr_flush(&mc_snap);
> > +			netif_addr_unlock_bh(dev);
> >  			goto out;
> >  		}
> > 
> > +		promisc_inc = dev_uc_promisc_update(dev);
> > +
> > +		netif_addr_unlock_bh(dev);
> > +	} else {
> > +		netif_addr_lock_bh(dev);
> > +		promisc_inc = dev_uc_promisc_update(dev);
> > +		netif_addr_unlock_bh(dev);
> > +	}
> > +
> > +	if (promisc_inc)
> > +		__dev_set_promiscuity(dev, promisc_inc, false);
> But it's being called here without any netdev_lock_ops(dev) ?

We have the following at the start of dev_rx_mode_work:
  rtnl_lock();
  netdev_lock_ops(dev);

Or am I looking at something else?

^ permalink raw reply

* Re: [PATCH net-next v3 06/13] mlx5: convert to ndo_set_rx_mode_async
From: Cosmin Ratiu @ 2026-03-20 14:47 UTC (permalink / raw)
  To: netdev@vger.kernel.org, sdf@fomichev.me
  Cc: intel-wired-lan@lists.osuosl.org, bestswngs@gmail.com,
	kernel-team@meta.com, przemyslaw.kitszel@intel.com,
	davem@davemloft.net, pabeni@redhat.com, horms@kernel.org,
	corbet@lwn.net, anthony.l.nguyen@intel.com, willemb@google.com,
	linux-kernel@vger.kernel.org, skhawaja@google.com, Dragos Tatulea,
	kees@kernel.org, Jianbo Liu, alexanderduyck@fb.com,
	kuba@kernel.org, leon@kernel.org, Saeed Mahameed,
	andrew+netdev@lunn.ch, michael.chan@broadcom.com, Mark Bloch,
	sd@queasysnail.net, Tariq Toukan, jacob.e.keller@intel.com,
	skhan@linuxfoundation.org, mohsin.bashr@gmail.com,
	edumazet@google.com, pavan.chebbi@broadcom.com,
	linux-kselftest@vger.kernel.org, linux-rdma@vger.kernel.org,
	johannes@sipsolutions.net, linux-doc@vger.kernel.org,
	aleksandr.loktionov@intel.com, linux-wireless@vger.kernel.org
In-Reply-To: <20260320012501.2033548-7-sdf@fomichev.me>

On Thu, 2026-03-19 at 18:24 -0700, Stanislav Fomichev wrote:
> Convert mlx5 from ndo_set_rx_mode to ndo_set_rx_mode_async. The
> driver's mlx5e_set_rx_mode now receives uc/mc snapshots and calls
> mlx5e_fs_set_rx_mode_work directly instead of queueing work.
> 
> mlx5e_sync_netdev_addr and mlx5e_handle_netdev_addr now take
> explicit uc/mc list parameters and iterate with
> netdev_hw_addr_list_for_each instead of netdev_for_each_{uc,mc}_addr.
> 
> Fallback to netdev's uc/mc in a few places and grab addr lock.
> 
> Cc: Saeed Mahameed <saeedm@nvidia.com>
> Cc: Tariq Toukan <tariqt@nvidia.com>
> Cc: Cosmin Ratiu <cratiu@nvidia.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
> ---
>  .../net/ethernet/mellanox/mlx5/core/en/fs.h   |  5 +++-
>  .../net/ethernet/mellanox/mlx5/core/en_fs.c   | 30 ++++++++++++-----
> --
>  .../net/ethernet/mellanox/mlx5/core/en_main.c | 16 +++++++---
>  3 files changed, 36 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> index c3408b3f7010..091b80a67189 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
> @@ -201,7 +201,10 @@ int mlx5e_add_vlan_trap(struct
> mlx5e_flow_steering *fs, int  trap_id, int tir_nu
>  void mlx5e_remove_vlan_trap(struct mlx5e_flow_steering *fs);
>  int mlx5e_add_mac_trap(struct mlx5e_flow_steering *fs, int  trap_id,
> int tir_num);
>  void mlx5e_remove_mac_trap(struct mlx5e_flow_steering *fs);
> -void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs,
> struct net_device *netdev);
> +void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs,
> +			       struct net_device *netdev,
> +			       struct netdev_hw_addr_list *uc,
> +			       struct netdev_hw_addr_list *mc);
>  int mlx5e_fs_vlan_rx_add_vid(struct mlx5e_flow_steering *fs,
>  			     struct net_device *netdev,
>  			     __be16 proto, u16 vid);
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> index 55255fe6e415..a9daefbd8f8f 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
> @@ -609,20 +609,26 @@ static void mlx5e_execute_l2_action(struct
> mlx5e_flow_steering *fs,
>  }
>  
>  static void mlx5e_sync_netdev_addr(struct mlx5e_flow_steering *fs,
> -				   struct net_device *netdev)
> +				   struct net_device *netdev,
> +				   struct netdev_hw_addr_list *uc,
> +				   struct netdev_hw_addr_list *mc)
>  {
>  	struct netdev_hw_addr *ha;
>  
> -	netif_addr_lock_bh(netdev);
> +	if (!uc || !mc) {
> +		netif_addr_lock_bh(netdev);
> +		mlx5e_sync_netdev_addr(fs, netdev, &netdev->uc,
> &netdev->mc);
> +		netif_addr_unlock_bh(netdev);
> +		return;
> +	}
>  
>  	mlx5e_add_l2_to_hash(fs->l2.netdev_uc, netdev->dev_addr);
> -	netdev_for_each_uc_addr(ha, netdev)
> +
> +	netdev_hw_addr_list_for_each(ha, uc)
>  		mlx5e_add_l2_to_hash(fs->l2.netdev_uc, ha->addr);
>  
> -	netdev_for_each_mc_addr(ha, netdev)
> +	netdev_hw_addr_list_for_each(ha, mc)
>  		mlx5e_add_l2_to_hash(fs->l2.netdev_mc, ha->addr);
> -
> -	netif_addr_unlock_bh(netdev);
>  }
>  
>  static void mlx5e_fill_addr_array(struct mlx5e_flow_steering *fs,
> int list_type,
> @@ -724,7 +730,9 @@ static void mlx5e_apply_netdev_addr(struct
> mlx5e_flow_steering *fs)
>  }
>  
>  static void mlx5e_handle_netdev_addr(struct mlx5e_flow_steering *fs,
> -				     struct net_device *netdev)
> +				     struct net_device *netdev,
> +				     struct netdev_hw_addr_list *uc,
> +				     struct netdev_hw_addr_list *mc)
>  {
>  	struct mlx5e_l2_hash_node *hn;
>  	struct hlist_node *tmp;
> @@ -736,7 +744,7 @@ static void mlx5e_handle_netdev_addr(struct
> mlx5e_flow_steering *fs,
>  		hn->action = MLX5E_ACTION_DEL;
>  
>  	if (fs->state_destroy)
> -		mlx5e_sync_netdev_addr(fs, netdev);
> +		mlx5e_sync_netdev_addr(fs, netdev, uc, mc);
>  
>  	mlx5e_apply_netdev_addr(fs);
>  }
> @@ -820,7 +828,9 @@ static void mlx5e_destroy_promisc_table(struct
> mlx5e_flow_steering *fs)
>  }
>  
>  void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs,
> -			       struct net_device *netdev)
> +			       struct net_device *netdev,
> +			       struct netdev_hw_addr_list *uc,
> +			       struct netdev_hw_addr_list *mc)
>  {
>  	struct mlx5e_l2_table *ea = &fs->l2;
>  
> @@ -850,7 +860,7 @@ void mlx5e_fs_set_rx_mode_work(struct
> mlx5e_flow_steering *fs,
>  	if (enable_broadcast)
>  		mlx5e_add_l2_flow_rule(fs, &ea->broadcast,
> MLX5E_FULLMATCH);
>  
> -	mlx5e_handle_netdev_addr(fs, netdev);
> +	mlx5e_handle_netdev_addr(fs, netdev, uc, mc);
>  
>  	if (disable_broadcast)
>  		mlx5e_del_l2_flow_rule(fs, &ea->broadcast);
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> index f7009da94f0b..e86cf1ee108d 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> @@ -4108,11 +4108,16 @@ static void mlx5e_nic_set_rx_mode(struct
> mlx5e_priv *priv)
>  	queue_work(priv->wq, &priv->set_rx_mode_work);
>  }
>  
> -static void mlx5e_set_rx_mode(struct net_device *dev)
> +static void mlx5e_set_rx_mode(struct net_device *dev,
> +			      struct netdev_hw_addr_list *uc,
> +			      struct netdev_hw_addr_list *mc)
>  {
>  	struct mlx5e_priv *priv = netdev_priv(dev);
>  
> -	mlx5e_nic_set_rx_mode(priv);
> +	if (mlx5e_is_uplink_rep(priv))
> +		return; /* no rx mode for uplink rep */
> +
> +	mlx5e_fs_set_rx_mode_work(priv->fs, dev, uc, mc);

While this chunk is correct, I think there's a logical conflict waiting
to happen with Saeed's pending patch touching this area ([1]).

You have inlined mlx5e_nic_set_rx_mode here, but after Saeed's patch
the mlx5e_is_uplink_rep condition added here should be dropped.

Not sure the automatic merge will do that.

[1]
https://lore.kernel.org/netdev/20260319005456.82745-1-saeed@kernel.org/T/#u


>  }
>  
>  static int mlx5e_set_mac(struct net_device *netdev, void *addr)
> @@ -5287,7 +5292,7 @@ const struct net_device_ops mlx5e_netdev_ops =
> {
>  	.ndo_setup_tc            = mlx5e_setup_tc,
>  	.ndo_select_queue        = mlx5e_select_queue,
>  	.ndo_get_stats64         = mlx5e_get_stats,
> -	.ndo_set_rx_mode         = mlx5e_set_rx_mode,
> +	.ndo_set_rx_mode_async   = mlx5e_set_rx_mode,
>  	.ndo_set_mac_address     = mlx5e_set_mac,
>  	.ndo_vlan_rx_add_vid     = mlx5e_vlan_rx_add_vid,
>  	.ndo_vlan_rx_kill_vid    = mlx5e_vlan_rx_kill_vid,
> @@ -6272,8 +6277,11 @@ void mlx5e_set_rx_mode_work(struct work_struct
> *work)
>  {
>  	struct mlx5e_priv *priv = container_of(work, struct
> mlx5e_priv,
>  					       set_rx_mode_work);
> +	struct net_device *dev = priv->netdev;
>  
> -	return mlx5e_fs_set_rx_mode_work(priv->fs, priv->netdev);
> +	netdev_lock_ops(dev);
> +	mlx5e_fs_set_rx_mode_work(priv->fs, dev, NULL, NULL);
> +	netdev_unlock_ops(dev);
>  }
>  
>  /* mlx5e generic netdev management API (move to en_common.c) */


^ permalink raw reply

* [PATCH wireless-next] wifi: mac80211: cleanup error path of ieee80211_do_open
From: Miri Korenblit @ 2026-03-20 12:20 UTC (permalink / raw)
  To: linux-wireless

If we failed on drv_start, we currently cleanup AP_VLAN reference to
bss.
But this is not needed, since AP_VLAN must be tied to a pre-existing AP
interface, so open_count cannot be 0, so we will never call drv_start
for AP_VLAN interfaces.

Remove these cleanup and return immediately instead.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 net/mac80211/iface.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 232fc0b80e44..234de4762be5 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1361,8 +1361,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 		break;
 		}
 	case NL80211_IFTYPE_AP:
-		sdata->bss = &sdata->u.ap;
-		break;
 	case NL80211_IFTYPE_MESH_POINT:
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_MONITOR:
@@ -1387,8 +1385,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 		local->reconfig_failure = false;
 
 		res = drv_start(local);
-		if (res)
-			goto err_del_bss;
+		if (res) {
+			/*
+			 * no need to worry about AP_VLAN cleanup since in that
+			 * case we can't have open_count == 0
+			 */
+			return res;
+		}
 		ieee80211_led_radio(local, true);
 		ieee80211_mod_tpt_led_trig(local,
 					   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
@@ -1459,6 +1462,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 		netif_carrier_on(dev);
 		list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list);
 		break;
+	case NL80211_IFTYPE_AP:
+		sdata->bss = &sdata->u.ap;
+		fallthrough;
 	default:
 		if (coming_up) {
 			ieee80211_del_virtual_monitor(local);
@@ -1547,10 +1553,10 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
  err_stop:
 	if (!local->open_count)
 		drv_stop(local, false);
- err_del_bss:
-	sdata->bss = NULL;
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 		list_del(&sdata->u.vlan.list);
+	/* Might not be initialized yet, but it is harmless */
+	sdata->bss = NULL;
 	return res;
 }
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next] wifi: mac80211: don't consider the sband when processing capabilities
From: Miri Korenblit @ 2026-03-20 12:15 UTC (permalink / raw)
  To: linux-wireless

In NAN, we have one set of (HT, VHT, HE) capabilities for all bands,
which means that we will need to process those capabilities without a
given sband.

To prepare for that, remove the sband argument from
ieee80211_ht_cap_ie_to_sta_ht_cap and ieee80211_he_cap_ie_to_sta_he_cap
and pass our own capabilities instead.

For ieee80211_vht_cap_ie_to_sta_vht_cap, make the sband argument
optional, since it is also used to check if there is at least one channel
that supports 80 MHz.
(Note that this check doesn't make much sense, but this can be handled in
 a different patch.)

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 net/mac80211/cfg.c         |  3 ++-
 net/mac80211/he.c          | 37 ++++++++++++++++++++++++-------------
 net/mac80211/ht.c          |  6 +++---
 net/mac80211/ibss.c        |  4 +++-
 net/mac80211/ieee80211_i.h |  9 ++++++++-
 net/mac80211/mesh_plink.c  |  3 ++-
 net/mac80211/mlme.c        |  3 ++-
 net/mac80211/vht.c         | 33 ++++++++++++++++++---------------
 8 files changed, 62 insertions(+), 36 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index ee64ac8e0f61..9aa4ae0621be 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2140,12 +2140,13 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
 		return -EINVAL;
 
 	if (params->ht_capa)
-		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
 						  params->ht_capa, link_sta);
 
 	/* VHT can override some HT caps such as the A-MSDU max length */
 	if (params->vht_capa)
 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+						    &sband->vht_cap,
 						    params->vht_capa, NULL,
 						    link_sta);
 
diff --git a/net/mac80211/he.c b/net/mac80211/he.c
index f7b05e59374c..93e0342cff4f 100644
--- a/net/mac80211/he.c
+++ b/net/mac80211/he.c
@@ -108,14 +108,13 @@ static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx,
 }
 
 void
-ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
-				  struct ieee80211_supported_band *sband,
-				  const u8 *he_cap_ie, u8 he_cap_len,
-				  const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
-				  struct link_sta_info *link_sta)
+_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+				   const struct ieee80211_sta_he_cap *own_he_cap_ptr,
+				   const u8 *he_cap_ie, u8 he_cap_len,
+				   const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+				   struct link_sta_info *link_sta)
 {
 	struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
-	const struct ieee80211_sta_he_cap *own_he_cap_ptr;
 	struct ieee80211_sta_he_cap own_he_cap;
 	struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie;
 	u8 he_ppe_size;
@@ -125,12 +124,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
 
 	memset(he_cap, 0, sizeof(*he_cap));
 
-	if (!he_cap_ie)
-		return;
-
-	own_he_cap_ptr =
-		ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
-	if (!own_he_cap_ptr)
+	if (!he_cap_ie || !own_he_cap_ptr || !own_he_cap_ptr->has_he)
 		return;
 
 	own_he_cap = *own_he_cap_ptr;
@@ -164,7 +158,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
 	link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
 	link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
 
-	if (sband->band == NL80211_BAND_6GHZ && he_6ghz_capa)
+	if (he_6ghz_capa)
 		ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta);
 
 	ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80,
@@ -207,6 +201,23 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
 	}
 }
 
+void
+ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+				  struct ieee80211_supported_band *sband,
+				  const u8 *he_cap_ie, u8 he_cap_len,
+				  const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+				  struct link_sta_info *link_sta)
+{
+	const struct ieee80211_sta_he_cap *own_he_cap =
+		ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
+
+	_ieee80211_he_cap_ie_to_sta_he_cap(sdata, own_he_cap, he_cap_ie,
+					   he_cap_len,
+					   (sband->band == NL80211_BAND_6GHZ) ?
+						he_6ghz_capa : NULL,
+					   link_sta);
+}
+
 void
 ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif,
 			const struct ieee80211_he_operation *he_op_ie)
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 33f1e1b235e9..410e2354f33a 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -136,7 +136,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
 
 
 bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
-				       struct ieee80211_supported_band *sband,
+				       const struct ieee80211_sta_ht_cap *own_cap_ptr,
 				       const struct ieee80211_ht_cap *ht_cap_ie,
 				       struct link_sta_info *link_sta)
 {
@@ -151,12 +151,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
 
 	memset(&ht_cap, 0, sizeof(ht_cap));
 
-	if (!ht_cap_ie || !sband->ht_cap.ht_supported)
+	if (!ht_cap_ie || !own_cap_ptr->ht_supported)
 		goto apply;
 
 	ht_cap.ht_supported = true;
 
-	own_cap = sband->ht_cap;
+	own_cap = *own_cap_ptr;
 
 	/*
 	 * If user has specified capability over-rides, take care
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 0298272c37ec..1e1ab25d9d8d 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -1014,7 +1014,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
 		ieee80211_chandef_ht_oper(elems->ht_operation, &chandef);
 
 		memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
-		rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+		rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata,
+								   &sband->ht_cap,
 								   &htcap_ie,
 								   &sta->deflink);
 
@@ -1033,6 +1034,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
 						   &chandef);
 			memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
 			ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+							    &sband->vht_cap,
 							    &cap_ie, NULL,
 							    &sta->deflink);
 			if (memcmp(&cap, &sta->sta.deflink.vht_cap, sizeof(cap)))
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index fe53812eca95..bacb49ad2817 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2188,7 +2188,7 @@ void ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
 				     struct ieee80211_sta_ht_cap *ht_cap);
 bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
-				       struct ieee80211_supported_band *sband,
+				       const struct ieee80211_sta_ht_cap *own_cap,
 				       const struct ieee80211_ht_cap *ht_cap_ie,
 				       struct link_sta_info *link_sta);
 void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
@@ -2273,6 +2273,7 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local,
 void
 ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 				    struct ieee80211_supported_band *sband,
+				    const struct ieee80211_sta_vht_cap *own_vht_cap,
 				    const struct ieee80211_vht_cap *vht_cap_ie,
 				    const struct ieee80211_vht_cap *vht_cap_ie2,
 				    struct link_sta_info *link_sta);
@@ -2313,6 +2314,12 @@ ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *sta);
 
 /* HE */
 void
+_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+				   const struct ieee80211_sta_he_cap *own_he_cap,
+				   const u8 *he_cap_ie, u8 he_cap_len,
+				   const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+				   struct link_sta_info *link_sta);
+void
 ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
 				  struct ieee80211_supported_band *sband,
 				  const u8 *he_cap_ie, u8 he_cap_len,
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 7d823a55636f..803106fc3134 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -450,12 +450,13 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
 		changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
 	sta->sta.deflink.supp_rates[sband->band] = rates;
 
-	if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+	if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
 					      elems->ht_cap_elem,
 					      &sta->deflink))
 		changed |= IEEE80211_RC_BW_CHANGED;
 
 	ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+					    &sband->vht_cap,
 					    elems->vht_cap_elem, NULL,
 					    &sta->deflink);
 
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d40b7d43b14d..7fc5616cb244 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5586,7 +5586,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
 
 	/* Set up internal HT/VHT capabilities */
 	if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT)
-		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
 						  elems->ht_cap_elem,
 						  link_sta);
 
@@ -5622,6 +5622,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
 		}
 
 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+						    &sband->vht_cap,
 						    elems->vht_cap_elem,
 						    bss_vht_cap, link_sta);
 		rcu_read_unlock();
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 80120f9f17b6..a6570781740a 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -4,7 +4,7 @@
  *
  * Portions of this file
  * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2018-2026 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -115,6 +115,7 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
 void
 ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 				    struct ieee80211_supported_band *sband,
+				    const struct ieee80211_sta_vht_cap *own_vht_cap,
 				    const struct ieee80211_vht_cap *vht_cap_ie,
 				    const struct ieee80211_vht_cap *vht_cap_ie2,
 				    struct link_sta_info *link_sta)
@@ -122,7 +123,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 	struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
 	struct ieee80211_sta_vht_cap own_cap;
 	u32 cap_info, i;
-	bool have_80mhz;
 	u32 mpdu_len;
 
 	memset(vht_cap, 0, sizeof(*vht_cap));
@@ -130,22 +130,25 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 	if (!link_sta->pub->ht_cap.ht_supported)
 		return;
 
-	if (!vht_cap_ie || !sband->vht_cap.vht_supported)
+	if (!vht_cap_ie || !own_vht_cap->vht_supported)
 		return;
 
-	/* Allow VHT if at least one channel on the sband supports 80 MHz */
-	have_80mhz = false;
-	for (i = 0; i < sband->n_channels; i++) {
-		if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
-						IEEE80211_CHAN_NO_80MHZ))
-			continue;
+	if (sband) {
+		/* Allow VHT if at least one channel on the sband supports 80 MHz */
+		bool have_80mhz = false;
 
-		have_80mhz = true;
-		break;
-	}
+		for (i = 0; i < sband->n_channels; i++) {
+			if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
+							IEEE80211_CHAN_NO_80MHZ))
+				continue;
 
-	if (!have_80mhz)
-		return;
+			have_80mhz = true;
+			break;
+		}
+
+		if (!have_80mhz)
+			return;
+	}
 
 	/*
 	 * A VHT STA must support 40 MHz, but if we verify that here
@@ -156,7 +159,7 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
 
 	vht_cap->vht_supported = true;
 
-	own_cap = sband->vht_cap;
+	own_cap = *own_vht_cap;
 	/*
 	 * If user has specified capability overrides, take care
 	 * of that if the station we're setting up is the AP that
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next] wifi: mac80211: properly handle error in ieee80211_add_virtual_monitor
From: Miri Korenblit @ 2026-03-20 12:13 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

In case of an error in ieee80211_add_virtual_monitor,
SDATA_STATE_RUNNING should be cleared as it was set in this function.
Do it there instead of in the error path of ieee80211_do_open.

Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 net/mac80211/iface.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 40ce0bb72726..232fc0b80e44 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1222,14 +1222,14 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
 		}
 	}
 
-	set_bit(SDATA_STATE_RUNNING, &sdata->state);
-
 	ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
 	if (ret) {
 		kfree(sdata);
 		return ret;
 	}
 
+	set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
 	mutex_lock(&local->iflist_mtx);
 	rcu_assign_pointer(local->monitor_sdata, sdata);
 	mutex_unlock(&local->iflist_mtx);
@@ -1242,6 +1242,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
 		mutex_unlock(&local->iflist_mtx);
 		synchronize_net();
 		drv_remove_interface(local, sdata);
+		clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 		kfree(sdata);
 		return ret;
 	}
@@ -1550,8 +1551,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 	sdata->bss = NULL;
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 		list_del(&sdata->u.vlan.list);
-	/* might already be clear but that doesn't matter */
-	clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 	return res;
 }
 
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH wireless-next 14/35] wifi: mm81x: add mac.c
From: Arien Judge @ 2026-03-20 10:06 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Lachlan Hodges, Dan Callaghan, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, ayman.grais,
	linux-wireless, linux-kernel
In-Reply-To: <7339202222542df9858308d3a42f438b4abf516d.camel@sipsolutions.net>

Hi Johannes,

> I'd still somewhat prefer to have the initial commit as a pull request,
> but if you really don't have a public git tree now you can also send a
> big single commit with all the right things to me in private, and then
> I'll put it somewhere and send it to the list. We can figure this out
> either way, no big deal.

We have a public git tree we can repurpose with a wireless-next branch for
a one off pull request at [1], but we're not yet committed to make this a 
T: entry at this time - processes are yet to be determined.

We will be sending a v2 as a patchset up shortly, in a per-file format 
addressing some fixes and dropping dt related code for now to reduce
review surface area. Once given the OK with those changes we can send a
PR.

[1] https://github.com/MorseMicro/linux

Cheers,
Arien

^ permalink raw reply

* ath12k: desc_va endianness problem
From: Alexander Wilhelm @ 2026-03-20  9:52 UTC (permalink / raw)
  To: Jeff Johnson; +Cc: ath12k, linux-wireless, linux-kernel

Hello ath12k developers,

I have another fix for the big endian platform, but unfortunately the data types
do not match here, so I need your support. The problem is the following: the
structs `hal_reo_dest_ring`, `hal_wbm_completion_ring`, and
`hal_wbm_release_ring_cc_rx` all define the members `buf_va_lo` and `buf_va_hi`
as `__le32`. At first glance this seems correct, because the entire structure
contains only little endian fields. The local variable `desc_va` in each
function (see patch below) is of type `u64`, so it makes sense that I would need
to convert from little endian to CPU endian. Unfortunately, this leads to the
following crashes, in `tx_completion` and `rx_process_wbm`, respectivally:


    Kernel attempted to read user page (40dcdf) - exploit attempt? (uid: 0)
    BUG: Unable to handle kernel data access on read at 0x0040dcdf
    Faulting instruction address: 0xe209290c
    Oops: Kernel access of bad area, sig: 11 [#1]
    BE PAGE_SIZE=4K SMP NR_CPUS=4 CoreNet Generic
    Modules linked in: ath12k(O) mac80211(O) cfg80211(O) compat(O) ...
    CPU: 1 PID: 10200 Comm: jshn Tainted: G           O       6.6.73 #0
    Hardware name: CyBoxAP-A e5500 0x80241021 CoreNet Generic
    NIP:  e209290c LR: e2092854 CTR: c08d3190
    REGS: dffe3d40 TRAP: 0300   Tainted: G           O        (6.6.73)
    MSR:  00029002 <CE,EE,ME>  CR: 44004804  XER: 00000000
    DEAR: 0040dcdf ESR: 00000000 
    GPR00: e2092854 dffe3e30 c328a500 e2092854 0040dcce 00000008 00070000 cf900000 
    GPR08: 00000000 cf900004 40000000 c8e52c4c c08d3190 1002801c 0fcf5000 c0ab85f8 
    GPR16: d0d1f7a0 c12a9080 00000001 df7b7f80 00000003 cf900000 e1bc0000 e1ccb988 
    GPR24: ffffffff c8ed0000 e1cc0220 00000000 c8ec0000 c8ec0000 c8ec0f50 c8ec0000 
    NIP [e209290c] ath12k_dp_tx_completion_handler+0x22c/0x720 [ath12k]
    LR [e2092854] ath12k_dp_tx_completion_handler+0x174/0x720 [ath12k]
    Call Trace:
    [dffe3e30] [e2092854] ath12k_dp_tx_completion_handler+0x174/0x720 [ath12k] (unreliable)
    [dffe3e80] [e208fe18] ath12k_dp_service_srng+0x58/0x380 [ath12k]
    [dffe3ed0] [e20a1490] ath12k_pci_hif_resume+0x520/0x8a0 [ath12k]
    [dffe3f00] [c067404c] __napi_poll+0x4c/0x260
    [dffe3f30] [c06746f8] net_rx_action+0x188/0x340
    [dffe3fa0] [c003a3d8] handle_softirqs+0x128/0x280
    [dffe3ff0] [c00045b0] do_softirq_own_stack+0x30/0x50
    [d0f2fb70] [00000000] 0x0
    [d0f2fb90] [c003a7d0] irq_exit+0x70/0xa0
    [d0f2fba0] [c0000c84] ExternalInput+0x144/0x160
    --- interrupt: 500 at percpu_counter_add_batch+0x9c/0x150
    NIP:  c0425e8c LR: c01a5964 CTR: c01764e0
    REGS: d0f2fbb0 TRAP: 0500   Tainted: G           O        (6.6.73)
    MSR:  00029002 <CE,EE,ME>  CR: 48008802  XER: 20000000

    GPR00: c01a5a00 d0f2fca0 c328a500 c1db7300 dffc0f20 00000000 fffffffc 00021002 
    GPR08: 1e763000 e1091054 00000007 c12b0530 88002808 1002801c 0fcf5000 c0ab85f8 
    GPR16: d0d1f7a0 dffc0f20 00000000 000003fe 00000000 f92412bd 00000003 c9525480 
    GPR24: d0f2fd74 c8a501f8 c12b0530 00029002 00000007 00000000 0000000b c1db7300 
    NIP [c0425e8c] percpu_counter_add_batch+0x9c/0x150
    LR [c01a5964] unmap_page_range+0x484/0x820
    --- interrupt: 500
    [d0f2fca0] [00000001] 0x1 (unreliable)
    [d0f2fcd0] [c01a5a00] unmap_page_range+0x520/0x820
    [d0f2fd60] [c01a5d9c] unmap_vmas+0x9c/0xe0
    [d0f2fda0] [c01afef4] exit_mmap+0xb4/0x2a0
    [d0f2fe40] [c0031610] mmput+0x40/0x140
    [d0f2fe60] [c0038df4] do_exit+0x2b4/0x990
    [d0f2feb0] [c00396c4] do_group_exit+0x34/0xa0
    [d0f2fed0] [c0039748] sys_exit_group+0x18/0x20
    [d0f2fee0] [c000dbac] system_call_exception+0xac/0x1f0
    [d0f2ff00] [c00110e8] ret_from_syscall+0x0/0x28
    --- interrupt: c00 at 0xfded438
    NIP:  0fded438 LR: 0ff23958 CTR: 0fd94930
    REGS: d0f2ff10 TRAP: 0c00   Tainted: G           O        (6.6.73)
    MSR:  0002f902 <CE,EE,PR,FP,ME>  CR: 28002402  XER: 20000000

    GPR00: 000000ea bff93390 b0316520 00000000 113e8af0 113e8af0 00000000 00000000 
    GPR08: 00000000 00000000 00000000 ffffffff b02ccb04 1002801c 100a0000 bfbc4260 
    GPR16: 114974b0 00000000 114a4de0 00000000 b02cc900 00000001 00000000 00000001 
    GPR24: 0ff239a0 00000000 00000001 00000000 b030f52c fffff000 0ff23958 00000000 
    NIP [0fded438] 0xfded438
    LR [0ff23958] 0xff23958
    --- interrupt: c00
    Code: 512a421e 2e140000 512a463e 40f20008 555b9f3e 39350004 754a4000 7c804c2c 41c20224 7c87442c 2c040000 41c20230 <88a40011> 7fc3f378 83a40008 8a640010
    ---[ end trace 0000000000000000 ]---

    Kernel panic - not syncing: Fatal exception
    ---[ end Kernel panic - not syncing: Fatal exception ]---


    user@root:~# Kernel attempted to read user page (c011de) - exploit attempt? (uid: 0)
    BUG: Unable to handle kernel data access on read at 0x00c011de
    Faulting instruction address: 0xe1e3dc44
    Oops: Kernel access of bad area, sig: 11 [#1]
    BE PAGE_SIZE=4K SMP NR_CPUS=4 CoreNet Generic
    Modules linked in: ...
    CPU: 1 PID: 0 Comm: swapper/1 Tainted: G           O       6.6.73 #0
    Hardware name: CyBoxAP-A e5500 0x80241021 CoreNet Generic
    NIP:  e1e3dc44 LR: e1e3dc30 CTR: c08d40e0
    REGS: dffe3ce0 TRAP: 0300   Tainted: G           O        (6.6.73)
    MSR:  00029002 <CE,EE,ME>  CR: 44004402  XER: 00000000
    DEAR: 00c011de ESR: 00000000 
    GPR00: e1e33154 dffe3dd0 c1870000 00000000 cebe0000 00000000 00000000 00c011ce 
    GPR08: 00000001 00000000 00020000 c30a294c c08d40e0 00000000 00000001 00000000 
    GPR16: e1ce2668 c9270000 c9269a18 c92664d0 e1ce26dc 00000000 babababa dffe3df4 
    GPR24: 00000040 00000000 c9266480 dffe3dec dffe3e04 c9260000 00c011ce c9269a18 
    NIP [e1e3dc44] ath12k_dp_rx_process_wbm_err+0x124/0x600 [ath12k]
    LR [e1e3dc30] ath12k_dp_rx_process_wbm_err+0x110/0x600 [ath12k]
    Call Trace:
    [dffe3dd0] [c0ab8e30] 0xc0ab8e30 (unreliable)
    [dffe3e80] [e1e33154] ath12k_dp_service_srng+0x314/0x380 [ath12k]
    [dffe3ed0] [e1e44540] ath12k_pci_hif_resume+0x520/0x8a0 [ath12k]
    [dffe3f00] [c0674c7c] __napi_poll+0x4c/0x260
    [dffe3f30] [c0675328] net_rx_action+0x188/0x340
    [dffe3fa0] [c003a3d8] handle_softirqs+0x128/0x280
    [dffe3ff0] [c00045b0] do_softirq_own_stack+0x30/0x50
    [c18c7e10] [c12b040c] 0xc12b040c
    [c18c7e30] [c003a7d0] irq_exit+0x70/0xa0
    [c18c7e40] [c0000c84] ExternalInput+0x144/0x160
    --- interrupt: 500 at arch_cpu_idle+0x24/0x50
    NIP:  c00071f4 LR: c00071f4 CTR: c000fe14
    REGS: c18c7e50 TRAP: 0500   Tainted: G           O        (6.6.73)
    MSR:  0002b002 <CE,EE,FP,ME>  CR: 84000402  XER: 00000000

    GPR00: c08cc978 c18c7f40 c1870000 00000005 00000001 40000000 c328becc c12b0530 
    GPR08: c12b0530 c000fe14 0098ca91 00154674 24000402 00000000 00000001 00000000 
    GPR16: 00000000 00000000 c00119a0 dffee5f0 00000001 00000000 ffffffff c1050254 
    GPR24: c12c0000 c0011970 c0011940 c12d0000 00000004 c12b040c c12b0000 00000001 
    NIP [c00071f4] arch_cpu_idle+0x24/0x50
    LR [c00071f4] arch_cpu_idle+0x24/0x50
    --- interrupt: 500
    [c18c7f40] [c0a367e0] 0xc0a367e0 (unreliable)
    [c18c7f50] [c08cc978] default_idle_call+0x38/0x58
    [c18c7f60] [c007b3b0] do_idle+0xf0/0x130
    [c18c7f80] [c007b580] cpu_startup_entry+0x30/0x40
    [c18c7fa0] [c001325c] start_secondary+0x48c/0x930
    [c18c7ff0] [c0002870] __secondary_start+0x90/0xdc
    Code: 7fa3eb78 4bfcba59 7c641b79 41c20144 38a10044 7fa3eb78 4bfcdb85 7c651b79 40c2026c 83c10058 2c1e0000 41c202d0 <813e0010> 7c09b000 41c20010 7e84a378
    ---[ end trace 0000000000000000 ]---

    Kernel panic - not syncing: Fatal exception
    ---[ end Kernel panic - not syncing: Fatal exception ]---


My fix, as shown in the patch below, is to remove the conversion. But then the
member variables `buf_va_lo` and `buf_va_hi` must be `u32`, which is obviously
wrong. Alternatively, `desc_va` must be `__le64`, but that is likely also
incorrect, because the address is simply dereferenced, and this clearly requires
CPU endianness. What I also do not fully understand is who actually fills these
addresses and at which stage this happens. I hope you can help clarify this so
that I can provide a correct patch for this issue afterward.


Best regards
Alexander Wilhelm
---
 drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c  | 12 ++++++------
 drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c  |  4 ++--
 drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c |  4 ++--
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
index e6a934d74e85..8df41ee12025 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
@@ -678,8 +678,8 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id,
 		hw_link_id = le32_get_bits(desc->info0,
 					   HAL_REO_DEST_RING_INFO0_SRC_LINK_ID);
 
-		desc_va = ((u64)le32_to_cpu(desc->buf_va_hi) << 32 |
-			   le32_to_cpu(desc->buf_va_lo));
+		desc_va = ((u64)desc->buf_va_hi << 32 |
+			   desc->buf_va_lo);
 		desc_info = (struct ath12k_rx_desc_info *)((unsigned long)desc_va);
 
 		device_id = hw_links[hw_link_id].device_id;
@@ -1275,8 +1275,8 @@ ath12k_wifi7_dp_process_rx_err_buf(struct ath12k_pdev_dp *dp_pdev,
 	struct ath12k_rx_desc_info *desc_info;
 	u64 desc_va;
 
-	desc_va = ((u64)le32_to_cpu(desc->buf_va_hi) << 32 |
-		   le32_to_cpu(desc->buf_va_lo));
+	desc_va = ((u64)desc->buf_va_hi << 32 |
+		   desc->buf_va_lo);
 	desc_info = (struct ath12k_rx_desc_info *)((unsigned long)desc_va);
 
 	/* retry manual desc retrieval */
@@ -1354,8 +1354,8 @@ static int ath12k_dp_h_msdu_buffer_type(struct ath12k_dp *dp,
 
 	dp->device_stats.reo_excep_msdu_buf_type++;
 
-	desc_va = (u64)le32_to_cpu(desc->buf_va_hi) << 32 |
-		  le32_to_cpu(desc->buf_va_lo);
+	desc_va = (u64)desc->buf_va_hi << 32 |
+		  desc->buf_va_lo;
 	desc_info = (struct ath12k_rx_desc_info *)(uintptr_t)desc_va;
 	if (!desc_info) {
 		u32 cookie;
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c
index 629084aa36d8..4794e15f0e45 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c
@@ -871,8 +871,8 @@ void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id)
 
 		if (le32_get_bits(tx_status->info0, HAL_WBM_COMPL_TX_INFO0_CC_DONE)) {
 			/* HW done cookie conversion */
-			desc_va = ((u64)le32_to_cpu(tx_status->buf_va_hi) << 32 |
-				   le32_to_cpu(tx_status->buf_va_lo));
+			desc_va = ((u64)tx_status->buf_va_hi << 32 |
+				   tx_status->buf_va_lo);
 			tx_desc = (struct ath12k_tx_desc_info *)((unsigned long)desc_va);
 		} else {
 			/* SW does cookie conversion to VA */
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c
index 49c693289709..31e41a3d0fc1 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c
@@ -398,8 +398,8 @@ int ath12k_wifi7_hal_wbm_desc_parse_err(struct ath12k_dp *dp, void *desc,
 		rel_info->cookie = le32_get_bits(wbm_cc_desc->info1,
 						 HAL_WBM_RELEASE_RX_CC_INFO1_COOKIE);
 
-		desc_va = ((u64)le32_to_cpu(wbm_cc_desc->buf_va_hi) << 32 |
-			   le32_to_cpu(wbm_cc_desc->buf_va_lo));
+		desc_va = ((u64)wbm_cc_desc->buf_va_hi << 32 |
+			   wbm_cc_desc->buf_va_lo);
 		rel_info->rx_desc =
 			(struct ath12k_rx_desc_info *)((unsigned long)desc_va);
 	}
-- 
2.43.0

^ permalink raw reply related

* [PATCH rtw-next] wifi: rtw89: Add support for TP-Link Archer TX50U
From: Zenm Chen @ 2026-03-20  9:31 UTC (permalink / raw)
  To: linux-wireless, pkshih, rtl8821cerfe2; +Cc: al.williams, usbwifi2024, zenmchen

Add the ID 37ad:0103 to the table to support an additional RTL8832CU
adapter: TP-Link Archer TX50U.

Link: https://github.com/morrownr/rtl8852cu-20251113/issues/2
Signed-off-by: Zenm Chen <zenmchen@gmail.com>
---
 drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
index 0c5aebaed..4755c483e 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -55,6 +55,8 @@ static const struct usb_device_id rtw_8852cu_id_table[] = {
 	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0102, 0xff, 0xff, 0xff),
 	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0103, 0xff, 0xff, 0xff),
+	  .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
 	{},
 };
 MODULE_DEVICE_TABLE(usb, rtw_8852cu_id_table);
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v7 1/3] wifi: iwlwifi: pcie: migrate to modern pci_alloc_irq_vectors API
From: Johannes Berg @ 2026-03-20  8:28 UTC (permalink / raw)
  To: Adrián García Casado, Miri Korenblit
  Cc: linux-wireless, linux-kernel, Miguel Ojeda
In-Reply-To: <20260319182925.19436-1-adriangarciacasado42@gmail.com>

Hi,

So given your exchange with Miguel, I was not going to reply, but

On Thu, 2026-03-19 at 19:29 +0100, Adrián García Casado wrote:
> Johannes Berg suggested using pci_alloc_irq_vectors() and delegating
> affinity management to the kernel.

I don't know where this came from, I never suggested that?

I _did_ suggest that if this change is somehow necessary (which you've
not convinced anyone of), the driver should allocate fewer queues rather
than just spreading the same number of queues over fewer cores and
double-booking one of the cores.

Please take a step back, (re-)read all the feedback you've been given,
figure out why you want this change, and come back with a coherent
explanation and changes addressing feedback (or explanations why not.)

johannes

^ permalink raw reply

* [PATCH wireless-next] wifi: cfg80211: support UNII-9 channels in ieee80211_channel_to_freq_khz
From: Miri Korenblit @ 2026-03-20  8:20 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach, Johannes Berg

From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

Devices that support UNII-9 will call ieee80211_channel_to_freq_khz
with a channel number that can go up to 253.
Allow the new channel numbers in ieee80211_channel_to_freq_khz.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 net/wireless/util.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/wireless/util.c b/net/wireless/util.c
index 0a0cea018fc5..1a861a6ea380 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -90,7 +90,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 		/* see 802.11ax D6.1 27.3.23.2 */
 		if (chan == 2)
 			return MHZ_TO_KHZ(5935);
-		if (chan <= 233)
+		if (chan <= 253)
 			return MHZ_TO_KHZ(5950 + chan * 5);
 		break;
 	case NL80211_BAND_60GHZ:
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next] wifi: mac80211: extract channel logic from link logic
From: Miri Korenblit @ 2026-03-20  8:20 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

The logic that tries to reuse an existing chanctx or create a new one if
such doesn't exist will be used for other types of chanctx users.
Extract this logic from _ieee80211_link_use_channel.

Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 net/mac80211/chan.c | 54 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 39 insertions(+), 15 deletions(-)

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 1bcf501cfe8e..1669ebf77346 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -2031,6 +2031,36 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link,
 		ieee80211_vif_use_reserved_switch(local);
 }
 
+static struct ieee80211_chanctx *
+ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata,
+				 const struct ieee80211_chan_req *chanreq,
+				 enum ieee80211_chanctx_mode mode,
+				 bool assign_on_failure,
+				 bool *reused_ctx)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_chanctx *ctx;
+	int radio_idx;
+
+	lockdep_assert_wiphy(local->hw.wiphy);
+
+	ctx = ieee80211_find_chanctx(local, chanreq, mode);
+	if (ctx) {
+		*reused_ctx = true;
+		return ctx;
+	}
+
+	*reused_ctx = false;
+
+	if (!ieee80211_find_available_radio(local, chanreq,
+					    sdata->wdev.radio_mask,
+					    &radio_idx))
+		return ERR_PTR(-EBUSY);
+
+	return ieee80211_new_chanctx(local, chanreq, mode,
+				     assign_on_failure, radio_idx);
+}
+
 int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
 				const struct ieee80211_chan_req *chanreq,
 				enum ieee80211_chanctx_mode mode,
@@ -2039,9 +2069,8 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
 	struct ieee80211_sub_if_data *sdata = link->sdata;
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_chanctx *ctx;
-	u8 radar_detect_width = 0;
-	bool reserved = false;
-	int radio_idx;
+	u8 radar_detect_width = 0;	
+	bool reused_ctx = false;
 	int ret;
 
 	lockdep_assert_wiphy(local->hw.wiphy);
@@ -2069,17 +2098,8 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
 	if (!local->in_reconfig)
 		__ieee80211_link_release_channel(link, false);
 
-	ctx = ieee80211_find_chanctx(local, chanreq, mode);
-	/* Note: context will_be_used flag is now set */
-	if (ctx)
-		reserved = true;
-	else if (!ieee80211_find_available_radio(local, chanreq,
-						 sdata->wdev.radio_mask,
-						 &radio_idx))
-		ctx = ERR_PTR(-EBUSY);
-	else
-		ctx = ieee80211_new_chanctx(local, chanreq, mode,
-					    assign_on_failure, radio_idx);
+	ctx = ieee80211_find_or_create_chanctx(sdata, chanreq, mode,
+					       assign_on_failure, &reused_ctx);
 	if (IS_ERR(ctx)) {
 		ret = PTR_ERR(ctx);
 		goto out;
@@ -2089,7 +2109,11 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
 
 	ret = ieee80211_assign_link_chanctx(link, ctx, assign_on_failure);
 
-	if (reserved) {
+	/*
+	 * In case an existing channel context is being used, we marked it as
+	 * will_be_used, now that it is assigned - clear this indication
+	 */
+	if (reused_ctx) {
 		WARN_ON(!ctx->will_be_used);
 		ctx->will_be_used = false;
 	}
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next] wifi: mac80211: make ieee80211_find_chanctx link-unaware
From: Miri Korenblit @ 2026-03-20  8:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

Currently we have only one user for a channel context: the link.
With NAN, a new type of the channel context user will be added - the NAN
channel.
To prepare for this, we need to separate the channel context code from
the link code.

Removes the link argument from ieee80211_find_chanctx. Since the issue that led
to commit 5e0c422d12b5 ("wifi: mac80211: reserve chanctx during find") - that
added the link argument - is relevant for any user of the channel context, add
a boolean to the chanctx itself, indicating that the chanctx is in the process
of getting used.

When this indication is set, the reference count of the channel context
will be incremented by one, so even if it is getting released from a link
(or another user) it won't be freed.

Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 net/mac80211/chan.c        | 26 ++++++++++++++------------
 net/mac80211/ieee80211_i.h |  3 +++
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index b7604118bf57..1bcf501cfe8e 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -166,6 +166,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
 	for_each_chanctx_user_all(local, ctx, &iter)
 		num++;
 
+	/*
+	 * This ctx is in the process of getting used,
+	 * take it into consideration
+	 */
+	if (ctx->will_be_used)
+		num++;
+
 	return num;
 }
 
@@ -769,10 +776,9 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,
 	_ieee80211_change_chanctx(local, ctx, old_ctx, chanreq, NULL);
 }
 
-/* Note: if successful, the returned chanctx is reserved for the link */
+/* Note: if successful, the returned chanctx will_be_used flag is set */
 static struct ieee80211_chanctx *
 ieee80211_find_chanctx(struct ieee80211_local *local,
-		       struct ieee80211_link_data *link,
 		       const struct ieee80211_chan_req *chanreq,
 		       enum ieee80211_chanctx_mode mode)
 {
@@ -784,9 +790,6 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
 		return NULL;
 
-	if (WARN_ON(link->reserved_chanctx))
-		return NULL;
-
 	list_for_each_entry(ctx, &local->chanctx_list, list) {
 		const struct ieee80211_chan_req *compat;
 
@@ -807,12 +810,12 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
 			continue;
 
 		/*
-		 * Reserve the chanctx temporarily, as the driver might change
+		 * Mark the chanctx as will be used, as the driver might change
 		 * active links during callbacks we make into it below and/or
 		 * later during assignment, which could (otherwise) cause the
 		 * context to actually be removed.
 		 */
-		link->reserved_chanctx = ctx;
+		ctx->will_be_used = true;
 
 		ieee80211_change_chanctx(local, ctx, ctx, compat);
 
@@ -2066,8 +2069,8 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
 	if (!local->in_reconfig)
 		__ieee80211_link_release_channel(link, false);
 
-	ctx = ieee80211_find_chanctx(local, link, chanreq, mode);
-	/* Note: context is now reserved */
+	ctx = ieee80211_find_chanctx(local, chanreq, mode);
+	/* Note: context will_be_used flag is now set */
 	if (ctx)
 		reserved = true;
 	else if (!ieee80211_find_available_radio(local, chanreq,
@@ -2087,9 +2090,8 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
 	ret = ieee80211_assign_link_chanctx(link, ctx, assign_on_failure);
 
 	if (reserved) {
-		/* remove reservation */
-		WARN_ON(link->reserved_chanctx != ctx);
-		link->reserved_chanctx = NULL;
+		WARN_ON(!ctx->will_be_used);
+		ctx->will_be_used = false;
 	}
 
 	if (ret) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d71e0c6d2165..fe53812eca95 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -928,6 +928,9 @@ struct ieee80211_chanctx {
 
 	bool radar_detected;
 
+	/* This chanctx is in process of getting used */
+	bool will_be_used;
+
 	/* MUST be last - ends in a flexible-array member. */
 	struct ieee80211_chanctx_conf conf;
 };
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 wireless-next] wifi: mac80211: ignore reserved bits in reconfiguration status
From: Miri Korenblit @ 2026-03-20  8:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: Benjamin Berg, Ilan Peer

From: Benjamin Berg <benjamin.berg@intel.com>

The Link ID Info field in the Reconfiguration Status Duple subfield of
the Reconfiguration Response frame only uses the lower four bits for the
link ID. The upper bits are reserved and should therefore be ignored.

Fixes: 529766edcdbd ("wifi: mac80211: Support dynamic link addition and removal")
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 include/linux/ieee80211.h |  7 +++++++
 net/mac80211/mlme.c       | 14 ++++++++------
 2 files changed, 15 insertions(+), 6 deletions(-)

--
v2: added structs and macros, as required.

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 52db36120314..b5d649db123f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1194,6 +1194,13 @@ struct ieee80211_mgmt {
 
 #define IEEE80211_MIN_ACTION_SIZE(type)	offsetofend(struct ieee80211_mgmt, u.action.type)
 
+/* Link Reconfiguration Status Duple field */
+struct ieee80211_ml_reconf_status {
+	u8 info;
+	__le16 status;
+} __packed;
+
+#define IEEE80211_ML_RECONF_LINK_ID_MASK	0xf
 
 /* Management MIC information element (IEEE 802.11w) for CMAC */
 struct ieee80211_mmie {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 0cd8d07bf668..d40b7d43b14d 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -10458,8 +10458,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
 	pos = mgmt->u.action.ml_reconf_resp.variable;
 	len -= offsetofend(typeof(*mgmt), u.action.ml_reconf_resp);
 
-	/* each status duple is 3 octets */
-	if (len < mgmt->u.action.ml_reconf_resp.count * 3) {
+	if (len < mgmt->u.action.ml_reconf_resp.count *
+				sizeof(struct ieee80211_ml_reconf_status)) {
 		sdata_info(sdata,
 			   "mlo: reconf: unexpected len=%zu, count=%u\n",
 			   len, mgmt->u.action.ml_reconf_resp.count);
@@ -10468,9 +10468,11 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
 
 	link_mask = sta_changed_links;
 	for (i = 0; i < mgmt->u.action.ml_reconf_resp.count; i++) {
-		u16 status = get_unaligned_le16(pos + 1);
+		struct ieee80211_ml_reconf_status *reconf_status = (void *)pos;
+		u16 status = le16_to_cpu(reconf_status->status);
 
-		link_id = *pos;
+		link_id = u8_get_bits(reconf_status->info,
+				      IEEE80211_ML_RECONF_LINK_ID_MASK);
 
 		if (!(link_mask & BIT(link_id))) {
 			sdata_info(sdata,
@@ -10505,8 +10507,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
 			sdata->u.mgd.reconf.added_links &= ~BIT(link_id);
 		}
 
-		pos += 3;
-		len -= 3;
+		pos += sizeof(*reconf_status);
+		len -= sizeof(*reconf_status);
 	}
 
 	if (link_mask) {
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next] wifi: radiotap: add definitions for the new UHR TLVs
From: Miri Korenblit @ 2026-03-20  8:16 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Add the necessary definitions to create radiotap UHR TLVs
for UHR sniffers.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 include/net/ieee80211_radiotap.h | 190 +++++++++++++++++++++++++++++++
 1 file changed, 190 insertions(+)

diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index c60867e7e43c..6c2210a253cd 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -95,6 +95,8 @@ enum ieee80211_radiotap_presence {
 	IEEE80211_RADIOTAP_EXT = 31,
 	IEEE80211_RADIOTAP_EHT_USIG = 33,
 	IEEE80211_RADIOTAP_EHT = 34,
+	IEEE80211_RADIOTAP_UHR_ELR = 37,
+	IEEE80211_RADIOTAP_UHR = 38,
 };
 
 /* for IEEE80211_RADIOTAP_FLAGS */
@@ -602,6 +604,194 @@ enum ieee80211_radiotap_eht_usig_tb {
 	IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL		= 0xfc000000,
 };
 
+/*
+ * ieee80211_radiotap_uhr_elr - content of UHR-ELR TLV (type 35)
+ * see https://www.radiotap.org/fields/UHR-ELR for details
+ */
+struct ieee80211_radiotap_uhr_elr {
+	__le32 known;
+	__le32 sig1, sig2, mark;
+} __packed;
+
+enum ieee80211_radiotap_uhr_elr_known {
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_VERSION_ID		= 0x00000001,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_UL_DL			= 0x00000002,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_MCS			= 0x00000004,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_CODING			= 0x00000008,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_LENGTH			= 0x00000010,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_LDPC_EXTRA_OFDM_SYM	= 0x00000020,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_CRC		= 0x00000040,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_TAIL		= 0x00000080,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_STA_ID			= 0x00000100,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_DISREGARD		= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_CRC		= 0x00000400,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_TAIL		= 0x00000800,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_CRC_CHECKED	= 0x00001000,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_CRC_CHECKED	= 0x00002000,
+	IEEE80211_RADIOTAP_UHR_ELR_KNOWN_MARK_BSS_COLOR		= 0x00010000,
+};
+
+enum ieee80211_radiotap_uhr_elr_sig1 {
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_VERSION_ID		= 0x00000001,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_UL_DL			= 0x00000002,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_MCS			= 0x00000004,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_CODING			= 0x00000008,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_LENGTH			= 0x00001FF0,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_LDPC_EXTRA_OFDM_SYM	= 0x00002000,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_CRC			= 0x0003C000,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_TAIL			= 0x00FC0000,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG1_CRC_VALID		= 0x80000000,
+};
+
+enum ieee80211_radiotap_uhr_elr_sig2 {
+	IEEE80211_RADIOTAP_UHR_ELR_SIG2_STA_ID			= 0x000007FF,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG2_DISREGARD		= 0x00003800,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG2_CRC			= 0x0003C000,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG2_TAIL			= 0x00FC0000,
+	IEEE80211_RADIOTAP_UHR_ELR_SIG2_CRC_VALID		= 0x80000000,
+};
+
+enum ieee80211_radiotap_uhr_elr_mark {
+	IEEE80211_RADIOTAP_UHR_ELR_MARK_BSS_COLOR		= 0x0000003F,
+};
+
+/*
+ * ieee80211_radiotap_uhr - content of UHR TLV (type 36)
+ * see https://www.radiotap.org/fields/UHR for details
+ */
+struct ieee80211_radiotap_uhr {
+	__le32 known;
+	__le32 data[9];
+	struct {
+		__le32 known, info;
+	} user[];
+} __packed;
+
+enum ieee80211_radiotap_uhr_known {
+	IEEE80211_RADIOTAP_UHR_KNOWN_SPATIAL_REUSE		= 0x00000001,
+	IEEE80211_RADIOTAP_UHR_KNOWN_GI_LTF_SIZE		= 0x00000002,
+	IEEE80211_RADIOTAP_UHR_KNOWN_NUMBER_OF_UHR_LTF_SYMBOLS	= 0x00000004,
+	IEEE80211_RADIOTAP_UHR_KNOWN_LDPC_EXTRA_SYMBOL_SEGMENT	= 0x00000008,
+	IEEE80211_RADIOTAP_UHR_KNOWN_PRE_FEC_PADDING_FACTOR	= 0x00000010,
+	IEEE80211_RADIOTAP_UHR_KNOWN_PE_DISAMBIGUITY		= 0x00000020,
+	IEEE80211_RADIOTAP_UHR_KNOWN_DISREGARD_OFDMA		= 0x00000040,
+	IEEE80211_RADIOTAP_UHR_KNOWN_CRC1			= 0x00000080,
+	IEEE80211_RADIOTAP_UHR_KNOWN_TAIL1			= 0x00000100,
+	IEEE80211_RADIOTAP_UHR_KNOWN_CRC2			= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_KNOWN_TAIL2			= 0x00000400,
+	IEEE80211_RADIOTAP_UHR_KNOWN_INTERFERENCE_MITIGATION	= 0x00000800,
+	IEEE80211_RADIOTAP_UHR_KNOWN_DISREGARD_NON_OFDMA	= 0x00001000,
+	IEEE80211_RADIOTAP_UHR_KNOWN_NUMBER_OF_NON_OFDMA_USERS	= 0x00002000,
+	IEEE80211_RADIOTAP_UHR_KNOWN_COMMON_ENCODING_BLOCK_CRC	= 0x00004000,
+	IEEE80211_RADIOTAP_UHR_KNOWN_COMMON_ENCODING_BLOCK_TAIL	= 0x00008000,
+	IEEE80211_RADIOTAP_UHR_KNOWN_RU_MRU_DRU_SIZE		= 0x00010000,
+	IEEE80211_RADIOTAP_UHR_KNOWN_RU_MRU_INDEX		= 0x00020000,
+	IEEE80211_RADIOTAP_UHR_KNOWN_DRU_RRU_ALLOC_TB_FMT	= 0x00040000,
+	IEEE80211_RADIOTAP_UHR_KNOWN_PRI80_CHAN_POS		= 0x00080000,
+};
+
+enum ieee80211_radiotap_uhr_data {
+	/* data[0] */
+	IEEE80211_RADIOTAP_UHR_DATA0_SPATIAL_REUSE		= 0x0000000F,
+	IEEE80211_RADIOTAP_UHR_DATA0_GI_LTF_SIZE		= 0x00000030,
+	IEEE80211_RADIOTAP_UHR_DATA0_NUMBER_OF_LTF_SYMBOLS	= 0x00000700,
+	IEEE80211_RADIOTAP_UHR_DATA0_LDPC_EXTRA_SYMBOL_SEGMENT	= 0x00000800,
+	IEEE80211_RADIOTAP_UHR_DATA0_PRE_FEC_PADDING_FACTOR	= 0x00003000,
+	IEEE80211_RADIOTAP_UHR_DATA0_PE_DISAMBIGUITY		= 0x00004000,
+	IEEE80211_RADIOTAP_UHR_DATA0_DISREGARD_OFDMA		= 0x00078000,
+	IEEE80211_RADIOTAP_UHR_DATA0_CRC1			= 0x00780000,
+	IEEE80211_RADIOTAP_UHR_DATA0_TAIL1			= 0x1f800000,
+	/* data[1] */
+	IEEE80211_RADIOTAP_UHR_DATA1_RU_MRU_DRU_SIZE		= 0x0000001f,
+	IEEE80211_RADIOTAP_UHR_DATA1_RU_MRU_INDEX		= 0x00001fe0,
+	IEEE80211_RADIOTAP_UHR_DATA1_RU_ALLOC_CC_1_1_1		= 0x003fe000,
+	IEEE80211_RADIOTAP_UHR_DATA1_RU_ALLOC_CC_1_1_1_KNOWN	= 0x00400000,
+	IEEE80211_RADIOTAP_UHR_DATA1_PRI80_CHAN_POS		= 0xc0000000,
+	/* data[2] */
+	IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_1		= 0x000001ff,
+	IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_1_KNOWN	= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_1_1_2		= 0x0007fc00,
+	IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_1_1_2_KNOWN	= 0x00080000,
+	IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_2		= 0x1ff00000,
+	IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_2_KNOWN	= 0x20000000,
+	/* data[3] */
+	IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_1		= 0x000001ff,
+	IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_1_KNOWN	= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_2_2_1		= 0x0007fc00,
+	IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_2_2_1_KNOWN	= 0x00080000,
+	IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_2		= 0x1ff00000,
+	IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_2_KNOWN	= 0x20000000,
+	/* data[4] */
+	IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_2		= 0x000001ff,
+	IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_2_KNOWN	= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_1_2_3		= 0x0007fc00,
+	IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_1_2_3_KNOWN	= 0x00080000,
+	IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_3		= 0x1ff00000,
+	IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_3_KNOWN	= 0x20000000,
+	/* data[5] */
+	IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_4		= 0x000001ff,
+	IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_4_KNOWN	= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_2_2_4		= 0x0007fc00,
+	IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_2_2_4_KNOWN	= 0x00080000,
+	IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_5		= 0x1ff00000,
+	IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_5_KNOWN	= 0x20000000,
+	/* data[6] */
+	IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_5		= 0x000001ff,
+	IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_5_KNOWN	= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_1_2_6		= 0x0007fc00,
+	IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_1_2_6_KNOWN	= 0x00080000,
+	IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_6		= 0x1ff00000,
+	IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_6_KNOWN	= 0x20000000,
+	/* data[7] */
+	IEEE80211_RADIOTAP_UHR_DATA7_CRC2			= 0x0000000f,
+	IEEE80211_RADIOTAP_UHR_DATA7_TAIL2			= 0x000003f0,
+	IEEE80211_RADIOTAP_UHR_DATA7_INTERFERENCE_MITIGATION	= 0x00000400,
+	IEEE80211_RADIOTAP_UHR_DATA7_DISREGARD_NON_OFDMA	= 0x00001800,
+	IEEE80211_RADIOTAP_UHR_DATA7_NUMBER_OF_NON_OFDMA_USERS	= 0x0000e000,
+	IEEE80211_RADIOTAP_UHR_DATA7_COMMON_ENCODING_BLOCK_CRC	= 0x000f0000,
+	IEEE80211_RADIOTAP_UHR_DATA7_COMMON_ENCODING_BLOCK_TAIL	= 0x03f00000,
+	/* data[8] */
+	IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_PS_160= 0x00000001,
+	IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_B0	= 0x00000002,
+	IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_B7_B1	= 0x000001fc,
+	IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_INDICATION		= 0x00000200,
+};
+
+enum ieee80211_radiotap_uhr_user_known {
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_STA_ID		= 0x00000001,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_MCS			= 0x00000002,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_NSS			= 0x00000004,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_UEQM			= 0x00000008,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_BF			= 0x00000010,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_CODING		= 0x00000020,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_UEQM_PATTERN		= 0x00000040,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_2X_LDPC		= 0x00000080,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_SPATIAL_CONFIG	= 0x00000100,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_DISREGARD		= 0x00000200,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_BSS_COLOR_INDICATION	= 0x00000400,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_USR_ENC_BLK_CRC	= 0x00000800,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_USR_ENC_BLK_TAIL	= 0x00001000,
+	/* really 'known' but actual data */
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_DATA_USR_ENC_BLK_CRC	= 0x000f0000,
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_DATA_USR_ENC_BLK_TAIL	= 0x03f00000,
+	/* indicates this user was captured */
+	IEEE80211_RADIOTAP_UHR_USER_KNOWN_USER_CAPTURED		= 0x80000000,
+};
+
+enum ieee80211_radiotap_uhr_user_info {
+	IEEE80211_RADIOTAP_UHR_USER_INFO_STA_ID			= 0x000007ff,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_MCS			= 0x0000f800,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_NSS			= 0x00070000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_SPATIAL_CONFIG		= 0x000f0000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_UEQM			= 0x00100000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_DISREGARD		= 0x00100000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_BF			= 0x00200000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_BSS_COLOR_INDICATION	= 0x00200000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_UEQM_PATTERN		= 0x00c00000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_CODING			= 0x01000000,
+	IEEE80211_RADIOTAP_UHR_USER_INFO_2X_LDPC		= 0x02000000,
+};
+
 /**
  * ieee80211_get_radiotap_len - get radiotap header length
  * @data: pointer to the header
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next] wifi: mac80211_hwsim: advertise basic UHR support
From: Miri Korenblit @ 2026-03-20  8:16 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Emmanuel Grumbach

From: Johannes Berg <johannes.berg@intel.com>

Just add support for ELR, and nothing else since the spec
isn't really all that well-specified yet.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/virtual/mac80211_hwsim.c | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 82adcc848189..fdadc6fa89bd 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -4641,6 +4641,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
 			},
 			/* PPE threshold information is not supported */
 		},
+		.uhr_cap = {
+			.has_uhr = true,
+			.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+				   IEEE80211_UHR_PHY_CAP_ELR_TX,
+		},
 	},
 	{
 		.types_mask = BIT(NL80211_IFTYPE_AP) |
@@ -4749,6 +4754,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
 			},
 			/* PPE threshold information is not supported */
 		},
+		.uhr_cap = {
+			.has_uhr = true,
+			.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+				   IEEE80211_UHR_PHY_CAP_ELR_TX,
+		},
 	},
 #ifdef CONFIG_MAC80211_MESH
 	{
@@ -4918,6 +4928,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
 			},
 			/* PPE threshold information is not supported */
 		},
+		.uhr_cap = {
+			.has_uhr = true,
+			.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+				   IEEE80211_UHR_PHY_CAP_ELR_TX,
+		},
 	},
 	{
 		.types_mask = BIT(NL80211_IFTYPE_AP) |
@@ -5043,6 +5058,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
 			},
 			/* PPE threshold information is not supported */
 		},
+		.uhr_cap = {
+			.has_uhr = true,
+			.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+				   IEEE80211_UHR_PHY_CAP_ELR_TX,
+		},
 	},
 #ifdef CONFIG_MAC80211_MESH
 	{
@@ -5236,6 +5256,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
 			},
 			/* PPE threshold information is not supported */
 		},
+		.uhr_cap = {
+			.has_uhr = true,
+			.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+				   IEEE80211_UHR_PHY_CAP_ELR_TX,
+		},
 	},
 	{
 		.types_mask = BIT(NL80211_IFTYPE_AP) |
@@ -5382,6 +5407,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
 			},
 			/* PPE threshold information is not supported */
 		},
+		.uhr_cap = {
+			.has_uhr = true,
+			.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+				   IEEE80211_UHR_PHY_CAP_ELR_TX,
+		},
 	},
 #ifdef CONFIG_MAC80211_MESH
 	{
@@ -5473,6 +5503,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
 			},
 			/* PPE threshold information is not supported */
 		},
+		.uhr_cap = {
+			.has_uhr = true,
+			.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+				   IEEE80211_UHR_PHY_CAP_ELR_TX,
+		},
 	},
 #endif
 };
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next] wifi: mac80211: use for_each_chanctx_user_* in one more place
From: Miri Korenblit @ 2026-03-20  8:16 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

for_each_chanctx_user_* is an iterator that visits all types of chanctx
users, including the (to be added) NAN channels, and not only the link.

ieee80211_get_chanctx_max_required_bw wasn't changed to use this new
iterator, do it now.

Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 net/mac80211/chan.c | 123 +++++++++++++++++++++++++-------------------
 1 file changed, 71 insertions(+), 52 deletions(-)

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 2f0c93f3ace6..b7604118bf57 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -447,74 +447,93 @@ ieee80211_get_max_required_bw(struct ieee80211_link_data *link)
 	return max_bw;
 }
 
+static enum nl80211_chan_width
+ieee80211_get_width_of_link(struct ieee80211_link_data *link)
+{
+	struct ieee80211_local *local = link->sdata->local;
+
+	switch (link->sdata->vif.type) {
+	case NL80211_IFTYPE_STATION:
+		if (!link->sdata->vif.cfg.assoc) {
+			/*
+			 * The AP's sta->bandwidth may not yet be set
+			 * at this point (pre-association), so simply
+			 * take the width from the chandef. We cannot
+			 * have TDLS peers yet (only after association).
+			 */
+			return link->conf->chanreq.oper.width;
+		}
+		/*
+		 * otherwise just use min_def like in AP, depending on what
+		 * we currently think the AP STA (and possibly TDLS peers)
+		 * require(s)
+		 */
+		fallthrough;
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_AP_VLAN:
+		return ieee80211_get_max_required_bw(link);
+	case NL80211_IFTYPE_P2P_DEVICE:
+	case NL80211_IFTYPE_NAN:
+		break;
+	case NL80211_IFTYPE_MONITOR:
+		WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
+						 NO_VIRTUAL_MONITOR));
+		fallthrough;
+	case NL80211_IFTYPE_ADHOC:
+	case NL80211_IFTYPE_MESH_POINT:
+	case NL80211_IFTYPE_OCB:
+		return link->conf->chanreq.oper.width;
+	case NL80211_IFTYPE_WDS:
+	case NL80211_IFTYPE_UNSPECIFIED:
+	case NUM_NL80211_IFTYPES:
+	case NL80211_IFTYPE_P2P_CLIENT:
+	case NL80211_IFTYPE_P2P_GO:
+		WARN_ON_ONCE(1);
+		break;
+	}
+
+	/* Take the lowest possible, so it won't change the max width */
+	return NL80211_CHAN_WIDTH_20_NOHT;
+}
+
 static enum nl80211_chan_width
 ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
 				      struct ieee80211_chanctx *ctx,
 				      struct ieee80211_link_data *rsvd_for,
 				      bool check_reserved)
 {
-	struct ieee80211_sub_if_data *sdata;
-	struct ieee80211_link_data *link;
 	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+	struct ieee80211_chanctx_user_iter iter;
+	struct ieee80211_sub_if_data *sdata;
+	enum nl80211_chan_width width;
 
 	if (WARN_ON(check_reserved && rsvd_for))
 		return ctx->conf.def.width;
 
-	for_each_sdata_link(local, link) {
-		enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
-
-		if (check_reserved) {
-			if (link->reserved_chanctx != ctx)
-				continue;
-		} else if (link != rsvd_for &&
-			   rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf)
-			continue;
-
-		switch (link->sdata->vif.type) {
-		case NL80211_IFTYPE_STATION:
-			if (!link->sdata->vif.cfg.assoc) {
-				/*
-				 * The AP's sta->bandwidth may not yet be set
-				 * at this point (pre-association), so simply
-				 * take the width from the chandef. We cannot
-				 * have TDLS peers yet (only after association).
-				 */
-				width = link->conf->chanreq.oper.width;
-				break;
-			}
-			/*
-			 * otherwise just use min_def like in AP, depending on what
-			 * we currently think the AP STA (and possibly TDLS peers)
-			 * require(s)
-			 */
-			fallthrough;
-		case NL80211_IFTYPE_AP:
-		case NL80211_IFTYPE_AP_VLAN:
-			width = ieee80211_get_max_required_bw(link);
-			break;
-		case NL80211_IFTYPE_P2P_DEVICE:
-		case NL80211_IFTYPE_NAN:
-			continue;
-		case NL80211_IFTYPE_MONITOR:
-			WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
-							 NO_VIRTUAL_MONITOR));
-			fallthrough;
-		case NL80211_IFTYPE_ADHOC:
-		case NL80211_IFTYPE_MESH_POINT:
-		case NL80211_IFTYPE_OCB:
-			width = link->conf->chanreq.oper.width;
-			break;
-		case NL80211_IFTYPE_WDS:
-		case NL80211_IFTYPE_UNSPECIFIED:
-		case NUM_NL80211_IFTYPES:
-		case NL80211_IFTYPE_P2P_CLIENT:
-		case NL80211_IFTYPE_P2P_GO:
-			WARN_ON_ONCE(1);
+	/* When this is true we only care about the reserving links */
+	if (check_reserved) {
+		for_each_chanctx_user_reserved(local, ctx, &iter) {
+			width = ieee80211_get_width_of_link(iter.link);
+			max_bw = max(max_bw, width);
 		}
+		goto check_monitor;
+	}
 
+	/* Consider all assigned links */
+	for_each_chanctx_user_assigned(local, ctx, &iter) {
+		width = ieee80211_get_width_of_link(iter.link);
 		max_bw = max(max_bw, width);
 	}
 
+	if (!rsvd_for ||
+	    rsvd_for->sdata == rcu_access_pointer(local->monitor_sdata))
+		goto check_monitor;
+
+	/* Consider the link for which this chanctx is reserved/going to be assigned */
+	width = ieee80211_get_width_of_link(rsvd_for);
+	max_bw = max(max_bw, width);
+
+check_monitor:
 	/* use the configured bandwidth in case of monitor interface */
 	sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
 	if (sdata &&
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 15/15] wifi: iwlwifi: mld: remove type argument from iwl_mld_add_sta()
From: Miri Korenblit @ 2026-03-20  8:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260320080918.2567780-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

This is used only in a single place, and the caller always sets
the type to STATION_TYPE_PEER right now. We need to change some
of this for NAN in the future, removing the type argument will
simplify that.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/sta.c      | 4 ++--
 drivers/net/wireless/intel/iwlwifi/mld/sta.h      | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 5cd3cdffb570..358320051d1e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1751,7 +1751,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
 				return -EBUSY;
 		}
 
-		ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER);
+		ret = iwl_mld_add_sta(mld, sta, vif);
 		if (ret)
 			return ret;
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index f40c49377466..619f302076ad 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -755,14 +755,14 @@ iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
 }
 
 int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
-		    struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
+		    struct ieee80211_vif *vif)
 {
 	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
 	struct ieee80211_link_sta *link_sta;
 	int link_id;
 	int ret;
 
-	ret = iwl_mld_init_sta(mld, sta, vif, type);
+	ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER);
 	if (ret)
 		return ret;
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
index 1897b121aae2..5f6c440bf058 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
@@ -190,7 +190,7 @@ iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta)
 }
 
 int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
-		    struct ieee80211_vif *vif, enum iwl_fw_sta_type type);
+		    struct ieee80211_vif *vif);
 void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta);
 int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
 				    struct ieee80211_link_sta *link_sta);
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 14/15] wifi: iwlwifi: mld: make iwl_mld_mac80211_iftype_to_fw() static
From: Miri Korenblit @ 2026-03-20  8:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260320080918.2567780-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

This function is only used within the file, so make it static.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/iface.c | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/iface.h | 2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index dc96214671d9..472592aa97fd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -74,7 +74,7 @@ static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,
 	return ret;
 }
 
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
+static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
 {
 	switch (vif->type) {
 	case NL80211_IFTYPE_STATION:
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index 62fca166afd1..3e106c93f0db 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -219,8 +219,6 @@ iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf)
 	return iwl_mld_link_dereference_check(mld_vif, bss_conf->link_id);
 }
 
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif);
-
 /* Cleanup function for struct iwl_mld_vif, will be called in restart */
 void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
 int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 13/15] wifi: iwlwifi: pcie: don't dump on reset handshake in dump
From: Miri Korenblit @ 2026-03-20  8:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Emmanuel Grumbach
In-Reply-To: <20260320080918.2567780-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

When a FW dump happens, possibly even because of a reset handshake
timeout, there's no point in attempting to dump again. Since all the
callers of the function outside the transport itself are from the FW
dump infrastructure, just split the internal function and make the
external one not dump on timeout.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 .../wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
index b15c5d486527..a50e845cea42 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
@@ -95,7 +95,9 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
 			      CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 }
 
-void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+static void
+_iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans,
+				   bool dump_on_timeout)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 	int ret;
@@ -133,7 +135,7 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
 			"timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n",
 			inta_hw, reset_done);
 
-		if (!reset_done) {
+		if (!reset_done && dump_on_timeout) {
 			struct iwl_fw_error_dump_mode mode = {
 				.type = IWL_ERR_TYPE_RESET_HS_TIMEOUT,
 				.context = IWL_ERR_CONTEXT_FROM_OPMODE,
@@ -147,6 +149,11 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
 	trans_pcie->fw_reset_state = FW_RESET_IDLE;
 }
 
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+{
+	_iwl_trans_pcie_fw_reset_handshake(trans, false);
+}
+
 static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -163,7 +170,7 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
 		 * should assume that the firmware is already dead.
 		 */
 		trans->state = IWL_TRANS_NO_FW;
-		iwl_trans_pcie_fw_reset_handshake(trans);
+		_iwl_trans_pcie_fw_reset_handshake(trans, true);
 	}
 
 	trans_pcie->is_down = true;
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 12/15] wifi: iwlwifi: use IWL_FW_CHECK for sync timeout
From: Miri Korenblit @ 2026-03-20  8:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Emmanuel Grumbach
In-Reply-To: <20260320080918.2567780-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

This could be a firmware issue, it didn't send all the responses
quickly enough. There are other potential issues (interrupts not
being delivered, etc.) but the FW debug data will at least give
some better information, and it's not a WARN condition anyway.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/rx.c       | 5 +++--
 drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 7 ++++---
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index ff6e71e3ff6e..6f40d6e47083 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -2211,8 +2211,9 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld,
 	ret = wait_event_timeout(mld->rxq_sync.waitq,
 				 READ_ONCE(mld->rxq_sync.state) == 0,
 				 SYNC_RX_QUEUE_TIMEOUT);
-	WARN_ONCE(!ret, "RXQ sync failed: state=0x%lx, cookie=%d\n",
-		  mld->rxq_sync.state, mld->rxq_sync.cookie);
+	IWL_FW_CHECK(mld, !ret,
+		     "RXQ sync failed: state=0x%lx, cookie=%d\n",
+		     mld->rxq_sync.state, mld->rxq_sync.cookie);
 
 out:
 	mld->rxq_sync.state = 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 090791fe0638..1ec9807e4827 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -6229,9 +6229,10 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
 		ret = wait_event_timeout(mvm->rx_sync_waitq,
 					 READ_ONCE(mvm->queue_sync_state) == 0,
 					 SYNC_RX_QUEUE_TIMEOUT);
-		WARN_ONCE(!ret, "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
-			  mvm->queue_sync_state,
-			  mvm->queue_sync_cookie);
+		IWL_FW_CHECK(mvm, !ret,
+			     "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
+			     mvm->queue_sync_state,
+			     mvm->queue_sync_cookie);
 	}
 
 out:
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 11/15] wifi: iwlwifi: mld: add BIOS revision compatibility check for PPAG command
From: Miri Korenblit @ 2026-03-20  8:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Pagadala Yesu Anjaneyulu, Emmanuel Grumbach, Johannes Berg
In-Reply-To: <20260320080918.2567780-1-miriam.rachel.korenblit@intel.com>

From: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>

Prevent potential issues when newer BIOS revisions
are used with firmware that doesn't support them for
PER_PLATFORM_ANT_GAIN_CMD.

Without this check, the driver may attempt to use
BIOS configurations that are incompatible with the
current firmware version, leading to dropping of
command in firmware without any failure notification
to driver.

Signed-off-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 .../net/wireless/intel/iwlwifi/mld/regulatory.c   | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
index f91f61ca9b2e..1633c8aa2e5d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
@@ -214,6 +214,7 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
 	u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD);
 	int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 1);
 	int cmd_len = sizeof(cmd.v8);
+	u8 cmd_bios_rev;
 	int ret;
 
 	BUILD_BUG_ON(offsetof(typeof(cmd), v8.ppag_config_info.hdr) !=
@@ -249,6 +250,10 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
 			}
 		}
 		cmd_len = sizeof(cmd.v7);
+		cmd_bios_rev =
+			iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+								  fwrt->ppag_bios_source,
+								  cmd_id, 4);
 	} else if (cmd_ver == 8) {
 		for (int chain = 0; chain < ARRAY_SIZE(cmd.v8.gain); chain++) {
 			for (int subband = 0;
@@ -262,12 +267,22 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
 						cmd.v8.gain[chain][subband]);
 			}
 		}
+		cmd_bios_rev =
+			iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+								  fwrt->ppag_bios_source,
+								  cmd_id, 5);
 	} else {
 		WARN(1, "Bad version for PER_PLATFORM_ANT_GAIN_CMD %d\n",
 		     cmd_ver);
 		return -EINVAL;
 	}
 
+	if (cmd_bios_rev < fwrt->ppag_bios_rev) {
+		IWL_ERR(mld, "BIOS revision compatibility check failed - Supported: %d, Current: %d\n",
+			cmd_bios_rev, fwrt->ppag_bios_rev);
+		return 0;
+	}
+
 	IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
 	ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_len);
 	if (ret < 0)
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 10/15] wifi: iwlwifi: validate the channels received in iwl_mcc_update_resp_v*
From: Miri Korenblit @ 2026-03-20  8:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260320080918.2567780-1-miriam.rachel.korenblit@intel.com>

Check with IWL_FW_CHECK that the FW doesn't send a channel that we don't
support. Otherwise, the center frequency will be 0, leading to a
warning since is_valid_reg_rule will return false, of course.
Although the warning is verbose enough, the IWL_FW_CHECK will spare some
of the debug.

Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index e8f7d258b622..b78805ef3c8b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -23,6 +23,8 @@
 #include "fw/api/commands.h"
 #include "fw/api/cmdhdr.h"
 #include "fw/img.h"
+#include "fw/dbg.h"
+
 #include "mei/iwl-mei.h"
 
 /* NVM offsets (in words) definitions */
@@ -1737,6 +1739,11 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
 							     band);
 		new_rule = false;
 
+		if (IWL_FW_CHECK(trans, !center_freq,
+				 "Invalid channel %d (idx %d) in NVM\n",
+				 nvm_chan[ch_idx], ch_idx))
+			continue;
+
 		if (!(ch_flags & NVM_CHANNEL_VALID)) {
 			iwl_nvm_print_channel_flags(dev, IWL_DL_LAR,
 						    nvm_chan[ch_idx], ch_flags);
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 09/15] wifi: iwlwifi: add CQM event support for per-link RSSI changes
From: Miri Korenblit @ 2026-03-20  8:09 UTC (permalink / raw)
  To: linux-wireless
  Cc: Avinash Bhatt, Emmanuel Grumbach, Johannes Berg,
	Pagadala Yesu Anjaneyulu
In-Reply-To: <20260320080918.2567780-1-miriam.rachel.korenblit@intel.com>

From: Avinash Bhatt <avinash.bhatt@intel.com>

Implement CQM RSSI threshold handling by tracking the last reported RSSI
and issuing CQM low/high events when the RSSI crosses the configured
threshold with the required hysteresis. This provides proper CQM support
and enables userspace to receive per-link RSSI notifications.

Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Pagadala Yesu Anjaneyulu <pagadala.yesu.anjaneyulu@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/link.h |  2 ++
 .../net/wireless/intel/iwlwifi/mld/stats.c    | 26 ++++++++++++++++++-
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h
index 9e4da8e4de93..ca691259fc5e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h
@@ -40,6 +40,7 @@ struct iwl_probe_resp_data {
  * @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS.
  * @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS.
  * @mon_sta: station used for TX injection in monitor interface.
+ * @last_cqm_rssi_event: rssi of the last cqm rssi event
  * @average_beacon_energy: average beacon energy for beacons received during
  *	client connections
  * @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs,
@@ -66,6 +67,7 @@ struct iwl_mld_link {
 	struct iwl_mld_int_sta bcast_sta;
 	struct iwl_mld_int_sta mcast_sta;
 	struct iwl_mld_int_sta mon_sta;
+	int last_cqm_rssi_event;
 
 	/* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */
 	struct ieee80211_key_conf *ap_early_keys[6];
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
index 7b8709716324..9b3149b9d2c2 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
@@ -369,15 +369,39 @@ static void iwl_mld_stats_recalc_traffic_load(struct iwl_mld *mld,
 static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig,
 				    struct ieee80211_bss_conf *bss_conf)
 {
+	struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
 	struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
 	int exit_emlsr_thresh;
+	int last_event;
 
 	if (sig == 0) {
 		IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n");
 		return;
 	}
 
-	/* TODO: task=statistics handle CQM notifications */
+	if (WARN_ON(!link))
+		return;
+
+	/* CQM Notification */
+	if (vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) {
+		int thold = bss_conf->cqm_rssi_thold;
+		int hyst = bss_conf->cqm_rssi_hyst;
+
+		last_event = link->last_cqm_rssi_event;
+		if (thold && sig < thold &&
+		    (last_event == 0 || sig < last_event - hyst)) {
+			link->last_cqm_rssi_event = sig;
+			ieee80211_cqm_rssi_notify(vif,
+						  NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+						  sig, GFP_KERNEL);
+		} else if (sig > thold &&
+			   (last_event == 0 || sig > last_event + hyst)) {
+			link->last_cqm_rssi_event = sig;
+			ieee80211_cqm_rssi_notify(vif,
+						  NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+						  sig, GFP_KERNEL);
+		}
+	}
 
 	if (!iwl_mld_emlsr_active(vif)) {
 		/* We're not in EMLSR and our signal is bad,
-- 
2.34.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox