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