summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/drivers/net/bonding/bond_vlan_real_dev.sh
blob: 542d9ffc4819f914b8dbf3e8a9fe7a88f3545e19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test propagation of a real device's state to the VLANs stacked on top of it
# when the real device is (or becomes) a bond member.
#
# The kernel mirrors a real device's UP/DOWN, MTU and feature changes onto its
# VLANs.  This is done asynchronously (netdev_work): doing it synchronously from
# the real device's notifier could deadlock.  If the real device is brought up
# while enslaved to a bond - so its instance lock is held across NETDEV_UP - and
# a VLAN on top of it is itself a bond member, the synchronous propagation
# re-entered the stack and tried to take the same instance lock again.
#
# Cover both halves:
#  - 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),
#  - 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.

ALL_TESTS="
	vlan_link_state
	vlan_mtu
	vlan_features
	vlan_real_dev_enslave
"

REQUIRE_MZ=no
NUM_NETIFS=0
lib_dir=$(dirname "$0")
source "$lib_dir"/../../../net/forwarding/lib.sh

# Return 0 if $dev in netns $ns has flag $flag set (e.g. UP) in its <...> flags.
link_has_flag()
{
	local ns=$1 dev=$2 flag=$3

	ip -n "$ns" link show dev "$dev" 2>/dev/null | grep -q "[<,]${flag}[,>]"
}

link_lacks_flag()
{
	! link_has_flag "$@"
}

link_mtu_is()
{
	local ns=$1 dev=$2 want=$3 cur

	cur=$(ip -n "$ns" link show dev "$dev" 2>/dev/null | \
		sed -n 's/.* mtu \([0-9]\+\).*/\1/p')
	[ "$cur" = "$want" ]
}

vlan_feature_is()
{
	local ns=$1 dev=$2 feature=$3 value=$4

	ip netns exec "$ns" ethtool -k "$dev" 2>/dev/null | \
		grep -q "^$feature: $value"
}

link_has_master()
{
	local ns=$1 dev=$2 master=$3

	ip -n "$ns" -o link show dev "$dev" 2>/dev/null | grep -q "master $master"
}

vlan_link_state()
{
	RET=0

	ip -n "$NS" link add ls_dummy type dummy
	ip -n "$NS" link add link ls_dummy name ls_vlan type vlan id 100

	# Bringing the real device up must propagate UP to the VLAN.
	ip -n "$NS" link set ls_dummy up
	busywait "$BUSYWAIT_TIMEOUT" link_has_flag "$NS" ls_vlan UP
	check_err $? "VLAN did not go UP after the real device went UP"

	# ... and likewise for DOWN.
	ip -n "$NS" link set ls_dummy down
	busywait "$BUSYWAIT_TIMEOUT" link_lacks_flag "$NS" ls_vlan UP
	check_err $? "VLAN did not go DOWN after the real device went DOWN"

	ip -n "$NS" link del ls_vlan
	ip -n "$NS" link del ls_dummy

	log_test "VLAN link state follows the real device"
}

vlan_mtu()
{
	RET=0

	# The VLAN inherits the real device's MTU (2000) at creation time.
	ip -n "$NS" link add mtu_dummy mtu 2000 type dummy
	ip -n "$NS" link add link mtu_dummy name mtu_vlan type vlan id 100

	# Shrinking the real device's MTU must clamp the VLAN's MTU.
	ip -n "$NS" link set mtu_dummy mtu 1500
	busywait "$BUSYWAIT_TIMEOUT" link_mtu_is "$NS" mtu_vlan 1500
	check_err $? "VLAN MTU not clamped after the real device's MTU shrank"

	ip -n "$NS" link del mtu_vlan
	ip -n "$NS" link del mtu_dummy

	log_test "VLAN MTU clamped to the real device"
}

vlan_features()
{
	RET=0

	# Use veth as the real device: unlike dummy it exports vlan_features, so
	# the VLAN actually inherits a toggleable offload to assert on.
	ip -n "$NS" link add ft_veth type veth peer name ft_veth_pr
	ip -n "$NS" link add link ft_veth name ft_vlan type vlan id 100

	vlan_feature_is "$NS" ft_vlan scatter-gather on
	check_err $? "VLAN did not inherit scatter-gather from the real device"

	# Toggling the offload on the real device must propagate to the VLAN.
	ip netns exec "$NS" ethtool -K ft_veth sg off
	busywait "$BUSYWAIT_TIMEOUT" \
		vlan_feature_is "$NS" ft_vlan scatter-gather off
	check_err $? "VLAN scatter-gather still on after disabling it on real dev"

	ip netns exec "$NS" ethtool -K ft_veth sg on
	busywait "$BUSYWAIT_TIMEOUT" \
		vlan_feature_is "$NS" ft_vlan scatter-gather on
	check_err $? "VLAN scatter-gather still off after enabling it on real dev"

	ip -n "$NS" link del ft_vlan
	ip -n "$NS" link del ft_veth

	log_test "VLAN features follow the real device"
}

vlan_real_dev_enslave()
{
	RET=0

	# dummy <- VLAN -> bond0, then enslave the dummy itself to bond1.  The
	# last step brings the dummy up under bond1's instance lock, which used
	# to deadlock while synchronously propagating UP to the (bond-enslaved)
	# VLAN on top.
	ip -n "$NS" link add dl_dummy type dummy
	ip -n "$NS" link set dl_dummy up
	ip -n "$NS" link add link dl_dummy name dl_vlan type vlan id 100

	ip -n "$NS" link add dl_bond0 type bond mode active-backup
	ip -n "$NS" link set dl_vlan down
	ip -n "$NS" link set dl_vlan master dl_bond0
	check_err $? "could not enslave the VLAN to bond0"

	ip -n "$NS" link add dl_bond1 type bond mode active-backup
	ip -n "$NS" link set dl_dummy down
	ip -n "$NS" link set dl_dummy master dl_bond1
	check_err $? "could not enslave the real device to bond1"

	# If we got here the kernel did not deadlock; make sure it is still
	# responsive and the enslave really took effect.
	link_has_master "$NS" dl_dummy dl_bond1
	check_err $? "real device not enslaved to bond1"

	ip -n "$NS" link del dl_bond1
	ip -n "$NS" link del dl_bond0
	ip -n "$NS" link del dl_vlan
	ip -n "$NS" link del dl_dummy

	log_test "VLAN real device enslaved to a second bond"
}

setup_ns NS
trap 'cleanup_ns $NS' EXIT

tests_run

exit "$EXIT_STATUS"