xref: /linux/tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh (revision 2ed2e359dea752e7758d29a423033031a0b96584)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Test propagation of a real device's state to the VLANs stacked on top of it
5# when the real device is (or becomes) a bond member.
6#
7# The kernel mirrors a real device's UP/DOWN, MTU and feature changes onto its
8# VLANs.  This is done asynchronously (netdev_work): doing it synchronously from
9# the real device's notifier could deadlock.  If the real device is brought up
10# while enslaved to a bond - so its instance lock is held across NETDEV_UP - and
11# a VLAN on top of it is itself a bond member, the synchronous propagation
12# re-entered the stack and tried to take the same instance lock again.
13#
14# Cover both halves:
15#  - the deferred UP/DOWN, MTU and feature propagation actually lands on the
16#    VLAN (link state and MTU use an ops-locked dummy, i.e. the deferral path),
17#  - the deadlock-prone topology - a VLAN on a dummy, with the VLAN and the
18#    dummy each enslaved to a different bond - can be built without hanging.
19
20ALL_TESTS="
21	vlan_link_state
22	vlan_mtu
23	vlan_features
24	vlan_real_dev_enslave
25"
26
27REQUIRE_MZ=no
28NUM_NETIFS=0
29lib_dir=$(dirname "$0")
30source "$lib_dir"/../../../net/forwarding/lib.sh
31
32# Return 0 if $dev in netns $ns has flag $flag set (e.g. UP) in its <...> flags.
33link_has_flag()
34{
35	local ns=$1 dev=$2 flag=$3
36
37	ip -n "$ns" link show dev "$dev" 2>/dev/null | grep -q "[<,]${flag}[,>]"
38}
39
40link_lacks_flag()
41{
42	! link_has_flag "$@"
43}
44
45link_mtu_is()
46{
47	local ns=$1 dev=$2 want=$3 cur
48
49	cur=$(ip -n "$ns" link show dev "$dev" 2>/dev/null | \
50		sed -n 's/.* mtu \([0-9]\+\).*/\1/p')
51	[ "$cur" = "$want" ]
52}
53
54vlan_feature_is()
55{
56	local ns=$1 dev=$2 feature=$3 value=$4
57
58	ip netns exec "$ns" ethtool -k "$dev" 2>/dev/null | \
59		grep -q "^$feature: $value"
60}
61
62link_has_master()
63{
64	local ns=$1 dev=$2 master=$3
65
66	ip -n "$ns" -o link show dev "$dev" 2>/dev/null | grep -q "master $master"
67}
68
69vlan_link_state()
70{
71	RET=0
72
73	ip -n "$NS" link add ls_dummy type dummy
74	ip -n "$NS" link add link ls_dummy name ls_vlan type vlan id 100
75
76	# Bringing the real device up must propagate UP to the VLAN.
77	ip -n "$NS" link set ls_dummy up
78	busywait "$BUSYWAIT_TIMEOUT" link_has_flag "$NS" ls_vlan UP
79	check_err $? "VLAN did not go UP after the real device went UP"
80
81	# ... and likewise for DOWN.
82	ip -n "$NS" link set ls_dummy down
83	busywait "$BUSYWAIT_TIMEOUT" link_lacks_flag "$NS" ls_vlan UP
84	check_err $? "VLAN did not go DOWN after the real device went DOWN"
85
86	ip -n "$NS" link del ls_vlan
87	ip -n "$NS" link del ls_dummy
88
89	log_test "VLAN link state follows the real device"
90}
91
92vlan_mtu()
93{
94	RET=0
95
96	# The VLAN inherits the real device's MTU (2000) at creation time.
97	ip -n "$NS" link add mtu_dummy mtu 2000 type dummy
98	ip -n "$NS" link add link mtu_dummy name mtu_vlan type vlan id 100
99
100	# Shrinking the real device's MTU must clamp the VLAN's MTU.
101	ip -n "$NS" link set mtu_dummy mtu 1500
102	busywait "$BUSYWAIT_TIMEOUT" link_mtu_is "$NS" mtu_vlan 1500
103	check_err $? "VLAN MTU not clamped after the real device's MTU shrank"
104
105	ip -n "$NS" link del mtu_vlan
106	ip -n "$NS" link del mtu_dummy
107
108	log_test "VLAN MTU clamped to the real device"
109}
110
111vlan_features()
112{
113	RET=0
114
115	# Use veth as the real device: unlike dummy it exports vlan_features, so
116	# the VLAN actually inherits a toggleable offload to assert on.
117	ip -n "$NS" link add ft_veth type veth peer name ft_veth_pr
118	ip -n "$NS" link add link ft_veth name ft_vlan type vlan id 100
119
120	vlan_feature_is "$NS" ft_vlan scatter-gather on
121	check_err $? "VLAN did not inherit scatter-gather from the real device"
122
123	# Toggling the offload on the real device must propagate to the VLAN.
124	ip netns exec "$NS" ethtool -K ft_veth sg off
125	busywait "$BUSYWAIT_TIMEOUT" \
126		vlan_feature_is "$NS" ft_vlan scatter-gather off
127	check_err $? "VLAN scatter-gather still on after disabling it on real dev"
128
129	ip netns exec "$NS" ethtool -K ft_veth sg on
130	busywait "$BUSYWAIT_TIMEOUT" \
131		vlan_feature_is "$NS" ft_vlan scatter-gather on
132	check_err $? "VLAN scatter-gather still off after enabling it on real dev"
133
134	ip -n "$NS" link del ft_vlan
135	ip -n "$NS" link del ft_veth
136
137	log_test "VLAN features follow the real device"
138}
139
140vlan_real_dev_enslave()
141{
142	RET=0
143
144	# dummy <- VLAN -> bond0, then enslave the dummy itself to bond1.  The
145	# last step brings the dummy up under bond1's instance lock, which used
146	# to deadlock while synchronously propagating UP to the (bond-enslaved)
147	# VLAN on top.
148	ip -n "$NS" link add dl_dummy type dummy
149	ip -n "$NS" link set dl_dummy up
150	ip -n "$NS" link add link dl_dummy name dl_vlan type vlan id 100
151
152	ip -n "$NS" link add dl_bond0 type bond mode active-backup
153	ip -n "$NS" link set dl_vlan down
154	ip -n "$NS" link set dl_vlan master dl_bond0
155	check_err $? "could not enslave the VLAN to bond0"
156
157	ip -n "$NS" link add dl_bond1 type bond mode active-backup
158	ip -n "$NS" link set dl_dummy down
159	ip -n "$NS" link set dl_dummy master dl_bond1
160	check_err $? "could not enslave the real device to bond1"
161
162	# If we got here the kernel did not deadlock; make sure it is still
163	# responsive and the enslave really took effect.
164	link_has_master "$NS" dl_dummy dl_bond1
165	check_err $? "real device not enslaved to bond1"
166
167	ip -n "$NS" link del dl_bond1
168	ip -n "$NS" link del dl_bond0
169	ip -n "$NS" link del dl_vlan
170	ip -n "$NS" link del dl_dummy
171
172	log_test "VLAN real device enslaved to a second bond"
173}
174
175setup_ns NS
176trap 'cleanup_ns $NS' EXIT
177
178tests_run
179
180exit "$EXIT_STATUS"
181