Skip to contentSkip to conversation

net: avoid nested UP notifier events

PATCH In review Jakub Kicinski 5 participants 15 messages Jun 24 – Jun 25

Subsystems (5)
lore
Share & tools
lore

Jakub Kicinski

author maintainer
#0 lore raw

cover 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.

Jakub Kicinski (4):
net: turn the rx_mode work into a generic netdev_work facility
net: add the driver-facing netdev_work scheduling API
vlan: defer real device state propagation to netdev_work
selftests: bonding: add a test for VLAN propagation over a bonded real
device

 Documentation/networking/netdevices.rst       |   2 +
 net/core/Makefile                             |   2 +-
 .../selftests/drivers/net/bonding/Makefile    |   1 +
 include/linux/netdevice.h                     |  21 +-
 net/8021q/vlan.h                              |  11 ++
 net/core/dev.h                                |  11 +-
 net/8021q/vlan.c                              |  76 +-------
 net/8021q/vlan_dev.c                          |  60 ++++++
 net/core/dev.c                                |   2 +
 net/core/dev_addr_lists.c                     |  77 +-------
 net/core/netdev_work.c                        | 162 ++++++++++++++++
 .../drivers/net/bonding/bond_vlan_real_dev.sh | 180 ++++++++++++++++++
 12 files changed, 457 insertions(+), 148 deletions(-)
 create mode 100644 net/core/netdev_work.c
 create mode 100755 tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh
Signature · 1 line
2.54.0
author maintainer
#1 lore raw

PATCH 1/4 net: turn the rx_mode work into a generic netdev_work facility

The rx_mode update runs from a workqueue: drivers have their ndo_set_rx_mode_async() callback executed by a single global work item under RTNL and ops lock. This is a useful pattern.

Support multiple "events" that need to be serviced and make RX_MODE sync the first one. Call the events "core" because later on we will let drivers define and schedule their own.

---
 net/core/Makefile         |   2 +-
 include/linux/netdevice.h |  10 ++--
 net/core/dev.h            |  11 +++-
 net/core/dev.c            |   1 +
 net/core/dev_addr_lists.c |  77 +------------------------
 net/core/netdev_work.c    | 117 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 138 insertions(+), 80 deletions(-)
 create mode 100644 net/core/netdev_work.c
net/core/Makefile +1 -1
index dc17c5a61e9a..b3fdcb4e355f 100644
@@ -13,7 +13,7 @@ obj-y		     += dev.o dev_api.o dev_addr_lists.o dst.o netevent.o \
 			neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
 			sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
 			fib_notifier.o xdp.o flow_offload.o gro.o \
-			netdev-genl.o netdev-genl-gen.o gso.o
+			netdev-genl.o netdev-genl-gen.o netdev_work.o gso.o
 
 obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o
 
include/linux/netdevice.h +6 -4
index b67a12541eac..732506787db3 100644
@@ -1930,8 +1930,9 @@ 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_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 +2327,9 @@ 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_core_pending;
 	struct netdev_hw_addr_list	rx_mode_addr_cache;
 	struct timer_list	rx_mode_retry_timer;
 	unsigned int		rx_mode_retry_count;
net/core/dev.h +10 -1
index 4121c50e7c88..5d0b0305d3ba 100644
@@ -167,10 +167,19 @@ int dev_change_carrier(struct net_device *dev, bool new_carrier);
 void __dev_set_rx_mode(struct net_device *dev);
 int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify);
 void netif_rx_mode_init(struct net_device *dev);
-bool netif_rx_mode_clean(struct net_device *dev);
+void netif_rx_mode_run(struct net_device *dev);
 void netif_rx_mode_sync(struct net_device *dev);
 void netif_rx_mode_cancel_retry(struct net_device *dev);
 
+/* Events for the async netdev work, tracked in netdev->work_core_pending. */
+enum netdev_work_core {
+	NETDEV_WORK_RX_MODE	= BIT(0),	/* run the rx_mode update */
+};
+
+void __netdev_work_core_sched(struct net_device *dev, unsigned long event);
+unsigned long
+__netdev_work_core_cancel(struct net_device *dev, unsigned long mask);
+
 void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
 			unsigned int gchanges, u32 portid,
 			const struct nlmsghdr *nlh);
net/core/dev.c +1 -0
index 5c01dfaa6c44..e1d8af0ef6ab 100644
@@ -12093,6 +12093,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
 	INIT_LIST_HEAD(&dev->ptype_all);
 	INIT_LIST_HEAD(&dev->ptype_specific);
 	INIT_LIST_HEAD(&dev->net_notifier_list);
+	INIT_LIST_HEAD(&dev->work_node);
 #ifdef CONFIG_NET_SCHED
 	hash_init(dev->qdisc_hash);
 #endif
net/core/dev_addr_lists.c +3 -74

118 lines not on this page · raw message

net/core/netdev_work.c new file +117 -0

117 lines not on this page · raw message

Signature · 1 line
2.54.0
Reviewed-by on v1 1/4
#2 lore raw

Re: [PATCH net 1/4] net: turn the rx_mode work into a generic netdev_work facility

Quoted 10 lines · Jakub Kicinski

The rx_mode update runs from a workqueue: drivers have their ndo_set_rx_mode_async() callback executed by a single global work item under RTNL and ops lock. This is a useful pattern.

Support multiple "events" that need to be serviced and make RX_MODE sync the first one. Call the events "core" because later on we will let drivers define and schedule their own.

Oh very nice !

I was drafting almost the same change for dev_set_rx_mode() in mcast path and some ipvlan changes.

author maintainer
#3 lore raw

Re: [PATCH net 1/4] net: turn the rx_mode work into a generic netdev_work facility

Quoted 4 lines · On Wed, 24 Jun 2026 22:55:06 -0700 Kuniyuki Iwashima wrote:

Oh very nice !

I was drafting almost the same change for dev_set_rx_mode() in mcast path and some ipvlan changes.

Glad to hear! I wasn't 100% convinced by the added complexity in the core :S

Acked-by on v1 1/4
#4 lore raw

Re: [PATCH net 1/4] net: turn the rx_mode work into a generic netdev_work facility

In reply to Jakub Kicinski
Quoted 9 lines · Jakub Kicinski

The rx_mode update runs from a workqueue: drivers have their ndo_set_rx_mode_async() callback executed by a single global work item under RTNL and ops lock. This is a useful pattern.

Support multiple "events" that need to be serviced and make RX_MODE sync the first one. Call the events "core" because later on we will let drivers define and schedule their own.

author maintainer
#5 lore raw

PATCH 2/4 net: add the driver-facing netdev_work scheduling API

In reply to Jakub Kicinski

With an extra event mask we can easily extend the netdev work to also service driver-defined events. For advanced drivers this is probably not a perfect match, but it makes running deferred work easier in simple cases.

Expose the netdev_work facility to drivers. Add helpers to schedule work and a dedicated ndo to perform the driver- -scheduled actions.

---
 include/linux/netdevice.h | 11 ++++++
 net/core/netdev_work.c    | 81 ++++++++++++++++++++++++++++++---------
 2 files changed, 74 insertions(+), 18 deletions(-)
include/linux/netdevice.h +11 -0
index 732506787db3..9981d637f8b5 100644
@@ -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);
@@ -1932,6 +1937,8 @@ enum netdev_reg_state {
  *				does not implement ndo_set_rx_mode()
  *	@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
@@ -2329,6 +2336,7 @@ struct net_device {
 	bool			uc_promisc;
 	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;
@@ -5178,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,
net/core/netdev_work.c +63 -18

146 lines not on this page · raw message

Signature · 1 line
2.54.0
Reviewed-by on v1 2/4
#6 lore raw

Re: [PATCH net 2/4] net: add the driver-facing netdev_work scheduling API

Quoted 11 lines · Jakub Kicinski

With an extra event mask we can easily extend the netdev work to also service driver-defined events. For advanced drivers this is probably not a perfect match, but it makes running deferred work easier in simple cases.

Expose the netdev_work facility to drivers. Add helpers to schedule work and a dedicated ndo to perform the driver- -scheduled actions.

Acked-by on v1 2/4
#7 lore raw

Re: [PATCH net 2/4] net: add the driver-facing netdev_work scheduling API

In reply to Jakub Kicinski
Quoted 10 lines · Jakub Kicinski

With an extra event mask we can easily extend the netdev work to also service driver-defined events. For advanced drivers this is probably not a perfect match, but it makes running deferred work easier in simple cases.

Expose the netdev_work facility to drivers. Add helpers to schedule work and a dedicated ndo to perform the driver- -scheduled actions.

author maintainer
#8 lore raw

PATCH 3/4 vlan: defer real device state propagation to netdev_work

In reply to Jakub Kicinski

vlan_device_event() generates nested UP/DOWN, MTU and feature change events. It executes an event for the VLAN device directly from the notifier - while the locks of the lower device are held.

This causes deadlocks, for example:

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()

The dummy device is ops locked, vlan creates a nested event (2), then bond wants to ask vlan for link state (3). bond uses the "I'm already holding the instance lock" flavor of API. But in this case the lock held refers to vlan itself. We hit vlan's link settings trampoline (4) and call __ethtool_get_link_ksettings() which tries to lock dummy. Deadlock. There's no clean way for us to tell the vlan_ethtool_get_link_ksettings() that the caller is already in lower device's critical section.

Defer the propagation to the per-netdev work facility instead:
the notifier only schedules netdev_work_sched(vlandev, VLAN_WORK_*), and ndo_work (vlan_dev_work) applies the change later. Hopefully nobody expects the VLAN state changes to be instantaneous.

If someone does expect the changes to be instantaneous we will have to do the same thing Stan did for rx_mode and "strategically" place sync calls, to make sure such delayed works are executed after we drop the ops lock but before we drop rtnl_lock.

Stan suggests that if we need that down the line we may consider reshaping the mechanism into "async notifications". AFAICT only vlan does this sort of netdev open chaining, so as a first try I think that sticking the complexity into the vlan code makes sense.

One corner case is that we need to cancel the event if user explicitly changes the state before work could run. Consider the following operations with vlan0 on top of dummy0:

  ip link set dev dummy0 up    # queues work to up vlan0
  ip link set dev vlan0 down   # user explicitly downs the vlan
  ndo_work                     # acts on the stale event
Reported-by [email protected]
Reported-by [email protected]
Reported-by [email protected]
Fixes 9f275c2e9020 ("net: ethtool: make sure __ethtool_get_link_ksettings() is ops-locked")
---
 Documentation/networking/netdevices.rst |  2 +
 net/8021q/vlan.h                        | 11 ++++
 net/8021q/vlan.c                        | 76 +++----------------------
 net/8021q/vlan_dev.c                    | 60 +++++++++++++++++++
 net/core/dev.c                          |  1 +
 5 files changed, 82 insertions(+), 68 deletions(-)
Documentation/networking/netdevices.rst +2 -0
index fde601acd1d2..d2a238f8cc8b 100644
@@ -433,6 +433,8 @@ exceptions) notifiers run under the instance lock. Please extend this
 documentation whenever you make explicit assumption about lock being held
 from a notifier.
 
+Drivers **must not** generate nested notifications of the ops-locked types.
+
 NETDEV_INTERNAL symbol namespace
 ================================
 
net/8021q/vlan.h +11 -0
index c7ffe591d593..c41caaf94095 100644
@@ -125,6 +125,17 @@ static inline netdev_features_t vlan_tnl_features(struct net_device *real_dev)
 int vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto);
 void vlan_filter_drop_vids(struct vlan_info *vlan_info, __be16 proto);
 
+/* netdev_work events propagated from the real device, see vlan_dev_work(). */
+enum {
+	VLAN_WORK_LINK_STATE	= BIT(0), /* sync up/down with real_dev */
+	VLAN_WORK_MTU		= BIT(1), /* clamp mtu to real_dev's */
+	VLAN_WORK_FEATURES	= BIT(2), /* re-inherit real_dev features */
+};
+
+void vlan_stacked_transfer_operstate(const struct net_device *rootdev,
+				     struct net_device *dev,
+				     struct vlan_dev_priv *vlan);
+
 /* found in vlan_dev.c */
 void vlan_dev_set_ingress_priority(const struct net_device *dev,
 				   u32 skb_prio, u16 vlan_prio);
net/8021q/vlan.c +8 -68

113 lines not on this page · raw message

net/8021q/vlan_dev.c +60 -0

84 lines not on this page · raw message

net/core/dev.c +1 -0
index e1d8af0ef6ab..4b3d5cfdf6e0 100644
@@ -9822,6 +9822,7 @@ int netif_change_flags(struct net_device *dev, unsigned int flags,
 	__dev_notify_flags(dev, old_flags, changes, 0, NULL);
 	return ret;
 }
+EXPORT_SYMBOL(netif_change_flags);
 
 int __netif_set_mtu(struct net_device *dev, int new_mtu)
 {
Signature · 1 line
2.54.0
Reviewed-by on v1 3/4
#9 lore raw

Re: [PATCH net 3/4] vlan: defer real device state propagation to netdev_work

Quoted 46 lines · Jakub Kicinski

vlan_device_event() generates nested UP/DOWN, MTU and feature change events. It executes an event for the VLAN device directly from the notifier - while the locks of the lower device are held.

This causes deadlocks, for example:

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()

The dummy device is ops locked, vlan creates a nested event (2), then bond wants to ask vlan for link state (3). bond uses the "I'm already holding the instance lock" flavor of API. But in this case the lock held refers to vlan itself. We hit vlan's link settings trampoline (4) and call __ethtool_get_link_ksettings() which tries to lock dummy. Deadlock. There's no clean way for us to tell the vlan_ethtool_get_link_ksettings() that the caller is already in lower device's critical section.

Defer the propagation to the per-netdev work facility instead:
the notifier only schedules netdev_work_sched(vlandev, VLAN_WORK_*), and ndo_work (vlan_dev_work) applies the change later. Hopefully nobody expects the VLAN state changes to be instantaneous.

If someone does expect the changes to be instantaneous we will have to do the same thing Stan did for rx_mode and "strategically" place sync calls, to make sure such delayed works are executed after we drop the ops lock but before we drop rtnl_lock.

Stan suggests that if we need that down the line we may consider reshaping the mechanism into "async notifications". AFAICT only vlan does this sort of netdev open chaining, so as a first try I think that sticking the complexity into the vlan code makes sense.

One corner case is that we need to cancel the event if user explicitly changes the state before work could run. Consider the following operations with vlan0 on top of dummy0:

  ip link set dev dummy0 up    # queues work to up vlan0
  ip link set dev vlan0 down   # user explicitly downs the vlan
  ndo_work                     # acts on the stale event
Quoted 5 lines
Reported-by [email protected]
Reported-by [email protected]
Reported-by [email protected]
Fixes 9f275c2e9020 ("net: ethtool: make sure __ethtool_get_link_ksettings() is ops-locked")

Thanks !

Reviewed-by on v1 3/4
#10 lore raw

Re: [PATCH net 3/4] vlan: defer real device state propagation to netdev_work

In reply to Jakub Kicinski
Quoted 45 lines · Jakub Kicinski

vlan_device_event() generates nested UP/DOWN, MTU and feature change events. It executes an event for the VLAN device directly from the notifier - while the locks of the lower device are held.

This causes deadlocks, for example:

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()

The dummy device is ops locked, vlan creates a nested event (2), then bond wants to ask vlan for link state (3). bond uses the "I'm already holding the instance lock" flavor of API. But in this case the lock held refers to vlan itself. We hit vlan's link settings trampoline (4) and call __ethtool_get_link_ksettings() which tries to lock dummy. Deadlock. There's no clean way for us to tell the vlan_ethtool_get_link_ksettings() that the caller is already in lower device's critical section.

Defer the propagation to the per-netdev work facility instead:
the notifier only schedules netdev_work_sched(vlandev, VLAN_WORK_*), and ndo_work (vlan_dev_work) applies the change later. Hopefully nobody expects the VLAN state changes to be instantaneous.

If someone does expect the changes to be instantaneous we will have to do the same thing Stan did for rx_mode and "strategically" place sync calls, to make sure such delayed works are executed after we drop the ops lock but before we drop rtnl_lock.

Stan suggests that if we need that down the line we may consider reshaping the mechanism into "async notifications". AFAICT only vlan does this sort of netdev open chaining, so as a first try I think that sticking the complexity into the vlan code makes sense.

One corner case is that we need to cancel the event if user explicitly changes the state before work could run. Consider the following operations with vlan0 on top of dummy0:

  ip link set dev dummy0 up    # queues work to up vlan0
  ip link set dev vlan0 down   # user explicitly downs the vlan
  ndo_work                     # acts on the stale event
Quoted 8 lines
Reported-by [email protected]
Reported-by [email protected]
Reported-by [email protected]
Fixes 9f275c2e9020 ("net: ethtool: make sure __ethtool_get_link_ksettings() is ops-locked")

---

[...]

Thanks
Nicolai

Acked-by on v1 3/4
#11 lore raw

Re: [PATCH net 3/4] vlan: defer real device state propagation to netdev_work

In reply to Jakub Kicinski
Quoted 45 lines · Jakub Kicinski

vlan_device_event() generates nested UP/DOWN, MTU and feature change events. It executes an event for the VLAN device directly from the notifier - while the locks of the lower device are held.

This causes deadlocks, for example:

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()

The dummy device is ops locked, vlan creates a nested event (2), then bond wants to ask vlan for link state (3). bond uses the "I'm already holding the instance lock" flavor of API. But in this case the lock held refers to vlan itself. We hit vlan's link settings trampoline (4) and call __ethtool_get_link_ksettings() which tries to lock dummy. Deadlock. There's no clean way for us to tell the vlan_ethtool_get_link_ksettings() that the caller is already in lower device's critical section.

Defer the propagation to the per-netdev work facility instead:
the notifier only schedules netdev_work_sched(vlandev, VLAN_WORK_*), and ndo_work (vlan_dev_work) applies the change later. Hopefully nobody expects the VLAN state changes to be instantaneous.

If someone does expect the changes to be instantaneous we will have to do the same thing Stan did for rx_mode and "strategically" place sync calls, to make sure such delayed works are executed after we drop the ops lock but before we drop rtnl_lock.

Stan suggests that if we need that down the line we may consider reshaping the mechanism into "async notifications". AFAICT only vlan does this sort of netdev open chaining, so as a first try I think that sticking the complexity into the vlan code makes sense.

One corner case is that we need to cancel the event if user explicitly changes the state before work could run. Consider the following operations with vlan0 on top of dummy0:

  ip link set dev dummy0 up    # queues work to up vlan0
  ip link set dev vlan0 down   # user explicitly downs the vlan
  ndo_work                     # acts on the stale event
Quoted 5 lines
Reported-by [email protected]
Reported-by [email protected]
Reported-by [email protected]
Fixes 9f275c2e9020 ("net: ethtool: make sure __ethtool_get_link_ksettings() is ops-locked")
author maintainer
#12 lore raw

PATCH 4/4 selftests: bonding: add a test for VLAN propagation over a bonded real device

In reply to Jakub Kicinski

Add a regression test for the VLAN notifier handling that the netdev_work deferral fixed.

A VLAN's real device propagates its UP/DOWN, MTU and feature changes onto the VLANs stacked on top of it. This used to be done synchronously from the real device's notifier and deadlocked when the real device was brought up while enslaved to a bond (instance lock held across NETDEV_UP) and the VLAN on top was itself a bond member: the synchronous propagation re-entered the stack and took the same instance lock again.

The test covers both halves:
- that the deferred UP/DOWN, MTU and feature propagation actually lands on the VLAN (link state and MTU use an ops-locked dummy, i.e. the deferral path; features use veth, which exports vlan_features to inherit), and
- that the deadlock-prone topology - a VLAN on a dummy, with the VLAN and the dummy each enslaved to a different bond - can be built without hanging.

---
 .../selftests/drivers/net/bonding/Makefile    |   1 +
 .../drivers/net/bonding/bond_vlan_real_dev.sh | 180 ++++++++++++++++++
 2 files changed, 181 insertions(+)
 create mode 100755 tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh
tools/testing/selftests/drivers/net/bonding/Makefile +1 -0
index be130bf585a4..6364ca02642d 100644
@@ -13,6 +13,7 @@ TEST_PROGS := \
 	bond_options.sh \
 	bond_passive_lacp.sh \
 	bond_stacked_header_parse.sh \
+	bond_vlan_real_dev.sh \
 	dev_addr_lists.sh \
 	mode-1-recovery-updelay.sh \
 	mode-2-recovery-updelay.sh \
tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh new file +180 -0

180 lines not on this page · raw message

Signature · 1 line
2.54.0
Reviewed-by on v1 4/4
#13 lore raw

RE: [PATCH net 4/4] selftests: bonding: add a test for VLAN propagation over a bonded real device

Quoted 69 lines

-----Original Message-----
From: Jakub Kicinski <[email protected]>
Sent: Wednesday, June 24, 2026 8:20 PM
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; nb@tipi- net.de; Loktionov, Aleksandr <[email protected]>; [email protected]; Jakub Kicinski <[email protected]>
Subject: [PATCH net 4/4] selftests: bonding: add a test for VLAN propagation over a bonded real device

Add a regression test for the VLAN notifier handling that the netdev_work deferral fixed.

A VLAN's real device propagates its UP/DOWN, MTU and feature changes onto the VLANs stacked on top of it. This used to be done synchronously from the real device's notifier and deadlocked when the real device was brought up while enslaved to a bond (instance lock held across NETDEV_UP) and the VLAN on top was itself a bond member:
the synchronous propagation re-entered the stack and took the same instance lock again.

The test covers both halves:
- that the deferred UP/DOWN, MTU and feature propagation actually
lands on
the VLAN (link state and MTU use an ops-locked dummy, i.e. the
deferral
path; features use veth, which exports vlan_features to inherit),
and
- that the deadlock-prone topology - a VLAN on a dummy, with the VLAN
and
the dummy each enslaved to a different bond - can be built without hanging.

---
 .../selftests/drivers/net/bonding/Makefile    |   1 +
 .../drivers/net/bonding/bond_vlan_real_dev.sh | 180
++++++++++++++++++
 2 files changed, 181 insertions(+)
 create mode 100755

tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh

tools/testing/selftests/drivers/net/bonding/Makefile +1 -0
index be130bf585a4..6364ca02642d 100644
@@ -13,6 +13,7 @@ TEST_PROGS := \
 	bond_options.sh \
 	bond_passive_lacp.sh \
 	bond_stacked_header_parse.sh \
+	bond_vlan_real_dev.sh \
 	dev_addr_lists.sh \
 	mode-1-recovery-updelay.sh \
 	mode-2-recovery-updelay.sh \

diff --git a/tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh b/tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh new file mode 100755
index 000000000000..542d9ffc4819
--- /dev/null
+++
b/tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh

tools/testing/selftests/drivers/net/bonding/Makefile +0 -0
@@ -0,0 +1,180 @@

...

Quoted 3 lines

+exit "$EXIT_STATUS"
--
2.54.0

Acked-by on v1 4/4
#14 lore raw

Re: [PATCH net 4/4] selftests: bonding: add a test for VLAN propagation over a bonded real device

In reply to Jakub Kicinski
Quoted 18 lines · Jakub Kicinski

Add a regression test for the VLAN notifier handling that the netdev_work deferral fixed.

A VLAN's real device propagates its UP/DOWN, MTU and feature changes onto the VLANs stacked on top of it. This used to be done synchronously from the real device's notifier and deadlocked when the real device was brought up while enslaved to a bond (instance lock held across NETDEV_UP) and the VLAN on top was itself a bond member: the synchronous propagation re-entered the stack and took the same instance lock again.

The test covers both halves:
- that the deferred UP/DOWN, MTU and feature propagation actually lands on the VLAN (link state and MTU use an ops-locked dummy, i.e. the deferral path; features use veth, which exports vlan_features to inherit), and
- that the deadlock-prone topology - a VLAN on a dummy, with the VLAN and the dummy each enslaved to a different bond - can be built without hanging.

Quoted 1 line