summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-06-25 10:09:02 -0700
committerJakub Kicinski <kuba@kernel.org>2026-06-25 10:18:41 -0700
commitfe9f4ee6c61a1410afd73bf011de5ae618004796 (patch)
tree4585457ad181200fa84bc605c3679656f8a17e84 /include/linux
parent1105ef941c1a28e115d1b97f17e1c85576884100 (diff)
parente83d0a2472617327e04b74272a61fca06f6f84ff (diff)
Merge branch 'net-avoid-nested-up-notifier-events'
Jakub Kicinski says: ==================== net: avoid nested UP notifier events syzbot reported that recent ethtool rework leads to deadlock on stacked devices. VLANs create nested notifications, confusing execution context. Bringing up dummy causes vlan to bring itself up as well. Which in turn causes bond to ask for link state - a call chain traveling in the opposite direction. bond (3) bond_update_speed_duplex(vlan) | ^ v vlan (2) UP(vlan) (4) vlan_ethtool_get_link_ksettings() | ^ v dummy (1) UP(dummy) (5) __ethtool_get_link_ksettings() We locked the instance lock of dummy at (1) and will will try to lock it again at (5) - which of course deadlocks. For non-nested notifications this is avoided because NETDEV_UP is always run ops-locked (so that bond asks for link using the netif_ API which assumes instance lock already held). The nesting, however, makes this problematic, we cannot carry the state of the whole chain back in the opposite direction. AFAICT vlan is the only driver which causes such issues. So let's try a localized fix of deferring vlan auto-open to a workqueue. ==================== Link: https://patch.msgid.link/20260624182018.2445732-1-kuba@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/netdevice.h21
1 files changed, 17 insertions, 4 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b67a12541eac..9981d637f8b5 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1131,6 +1131,9 @@ struct netdev_net_notifier {
* netdev_hw_addr_list_for_each(ha, uc). Return 0 on success or a
* negative errno to request a retry via the core backoff.
*
+ * void (*ndo_work)(struct net_device *dev, unsigned long events);
+ * Run deferred work scheduled with netdev_work_sched(@events).
+ *
* int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
* This function is called when the Media Access Control address
* needs to be changed. If this interface is not defined, the
@@ -1460,6 +1463,8 @@ struct net_device_ops {
struct net_device *dev,
struct netdev_hw_addr_list *uc,
struct netdev_hw_addr_list *mc);
+ void (*ndo_work)(struct net_device *dev,
+ unsigned long events);
int (*ndo_set_mac_address)(struct net_device *dev,
void *addr);
int (*ndo_validate_addr)(struct net_device *dev);
@@ -1930,8 +1935,11 @@ 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
+ * @work_node: List entry for async netdev_work processing
+ * @work_tracker: Refcount tracker for async netdev_work
+ * @work_pending: Driver-defined pending netdev_work, passed to
+ * ndo_work() (see netdev_work_sched())
+ * @work_core_pending: Core-defined pending netdev_work (NETDEV_WORK_*)
* @rx_mode_addr_cache: Recycled snapshot entries for rx_mode work
* @rx_mode_retry_timer: Timer that re-queues rx_mode work after failure
* @rx_mode_retry_count: Number of consecutive retries already scheduled
@@ -2326,8 +2334,10 @@ struct net_device {
unsigned int promiscuity;
unsigned int allmulti;
bool uc_promisc;
- struct list_head rx_mode_node;
- netdevice_tracker rx_mode_tracker;
+ struct list_head work_node;
+ netdevice_tracker work_tracker;
+ unsigned long work_pending;
+ unsigned long work_core_pending;
struct netdev_hw_addr_list rx_mode_addr_cache;
struct timer_list rx_mode_retry_timer;
unsigned int rx_mode_retry_count;
@@ -5176,6 +5186,9 @@ void dev_fetch_sw_netstats(struct rtnl_link_stats64 *s,
const struct pcpu_sw_netstats __percpu *netstats);
void dev_get_tstats64(struct net_device *dev, struct rtnl_link_stats64 *s);
+void netdev_work_sched(struct net_device *dev, unsigned long events);
+unsigned long netdev_work_cancel(struct net_device *dev, unsigned long mask);
+
enum {
NESTED_SYNC_IMM_BIT,
NESTED_SYNC_TODO_BIT,