summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorStanislav Fomichev <sdf.kernel@gmail.com>2026-04-16 11:56:59 -0700
committerPaolo Abeni <pabeni@redhat.com>2026-04-21 12:50:03 +0200
commit3554b4345d855089ab7af5e3557f5dc3262d14c9 (patch)
treeb5d5f75c0764beab5cd048f6f3a146a746f01b79 /include
parentdb9e726525e45dbd713c07897a4d20bc18333ccc (diff)
net: introduce ndo_set_rx_mode_async and netdev_rx_mode_work
Add ndo_set_rx_mode_async callback that drivers can implement instead of the legacy ndo_set_rx_mode. The legacy callback runs under the netif_addr_lock spinlock with BHs disabled, preventing drivers from sleeping. The async variant runs from a work queue with rtnl_lock and netdev_lock_ops held, in fully sleepable context. When __dev_set_rx_mode() sees ndo_set_rx_mode_async, it schedules netdev_rx_mode_work instead of calling the driver inline. The work function takes two snapshots of each address list (uc/mc) under the addr_lock, then drops the lock and calls the driver with the work copies. After the driver returns, it reconciles the snapshots back to the real lists under the lock. Add netif_rx_mode_sync() to opportunistically execute the pending workqueue update inline, so that rx mode changes are committed before returning to userspace: - dev_change_flags (SIOCSIFFLAGS / RTM_NEWLINK) - dev_set_promiscuity - dev_set_allmulti - dev_ifsioc SIOCADDMULTI / SIOCDELMULTI - do_setlink (RTM_SETLINK) Note that some deep hierarchies still do skip the lower updates via: - dev_uc_sync - dev_mc_sync If we do end up hitting user-visible issues, we can add more calls to netif_rx_mode_sync in specific places. But hopefully we should not, the actual user-visible lists are still synced, it's that just HW state that might be lagging. Signed-off-by: Stanislav Fomichev <sdf@fomichev.me> Link: https://patch.msgid.link/20260416185712.2155425-3-sdf@fomichev.me Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'include')
-rw-r--r--include/linux/netdevice.h18
1 files changed, 18 insertions, 0 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a84c55488b8c..6ed97f4c3bc6 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1119,6 +1119,16 @@ struct netdev_net_notifier {
* This function is called device changes address list filtering.
* If driver handles unicast address filtering, it should set
* IFF_UNICAST_FLT in its priv_flags.
+ * Cannot sleep, called with netif_addr_lock_bh held.
+ * Deprecated in favor of ndo_set_rx_mode_async.
+ *
+ * void (*ndo_set_rx_mode_async)(struct net_device *dev,
+ * struct netdev_hw_addr_list *uc,
+ * struct netdev_hw_addr_list *mc);
+ * Async version of ndo_set_rx_mode which runs in process context
+ * with rtnl_lock and netdev_lock_ops(dev) held. The uc/mc parameters
+ * are snapshots of the address lists - iterate with
+ * netdev_hw_addr_list_for_each(ha, uc).
*
* int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
* This function is called when the Media Access Control address
@@ -1439,6 +1449,10 @@ struct net_device_ops {
void (*ndo_change_rx_flags)(struct net_device *dev,
int flags);
void (*ndo_set_rx_mode)(struct net_device *dev);
+ void (*ndo_set_rx_mode_async)(
+ struct net_device *dev,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc);
int (*ndo_set_mac_address)(struct net_device *dev,
void *addr);
int (*ndo_validate_addr)(struct net_device *dev);
@@ -1903,6 +1917,8 @@ enum netdev_reg_state {
* has been enabled due to the need to listen to
* additional unicast addresses in a device that
* does not implement ndo_set_rx_mode()
+ * @rx_mode_node: List entry for rx_mode work processing
+ * @rx_mode_tracker: Refcount tracker for rx_mode work
* @uc: unicast mac addresses
* @mc: multicast mac addresses
* @dev_addrs: list of device hw addresses
@@ -2294,6 +2310,8 @@ struct net_device {
unsigned int promiscuity;
unsigned int allmulti;
bool uc_promisc;
+ struct list_head rx_mode_node;
+ netdevice_tracker rx_mode_tracker;
#ifdef CONFIG_LOCKDEP
unsigned char nested_level;
#endif