From 3d0b738fc5adf9f380702ac1424672e4b32c3781 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 12 Dec 2022 11:56:45 +0800 Subject: [PATCH 1/3] bonding: add missed __rcu annotation for curr_active_slave There is one direct accesses to bond->curr_active_slave in bond_miimon_commit(). Protected it by rcu_access_pointer() since the later of this function also use this one. Signed-off-by: Hangbin Liu Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index f7767afe116b3..6a4bbd5aa3e09 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2699,7 +2699,7 @@ static void bond_miimon_commit(struct bonding *bond) bond_miimon_link_change(bond, slave, BOND_LINK_UP); - if (!bond->curr_active_slave || slave == primary) + if (!rcu_access_pointer(bond->curr_active_slave) || slave == primary) goto do_failover; continue; From e95cc44763a41d5c715ef16742bcb1d8e6524a62 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 12 Dec 2022 11:56:46 +0800 Subject: [PATCH 2/3] bonding: do failover when high prio link up Currently, when a high prio link enslaved, or when current link down, the high prio port could be selected. But when high prio link up, the new active slave reselection is not triggered. Fix it by checking link's prio when getting up. Making the do_failover after looping all slaves as there may be multi high prio slaves up. Reported-by: Liang Li Fixes: 0a2ff7cc8ad4 ("Bonding: add per-port priority for failover re-selection") Signed-off-by: Hangbin Liu Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_main.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6a4bbd5aa3e09..b4c65783960a5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2654,8 +2654,9 @@ static void bond_miimon_link_change(struct bonding *bond, static void bond_miimon_commit(struct bonding *bond) { - struct list_head *iter; struct slave *slave, *primary; + bool do_failover = false; + struct list_head *iter; bond_for_each_slave(bond, slave, iter) { switch (slave->link_new_state) { @@ -2699,8 +2700,9 @@ static void bond_miimon_commit(struct bonding *bond) bond_miimon_link_change(bond, slave, BOND_LINK_UP); - if (!rcu_access_pointer(bond->curr_active_slave) || slave == primary) - goto do_failover; + if (!rcu_access_pointer(bond->curr_active_slave) || slave == primary || + slave->prio > rcu_dereference(bond->curr_active_slave)->prio) + do_failover = true; continue; @@ -2721,7 +2723,7 @@ static void bond_miimon_commit(struct bonding *bond) bond_miimon_link_change(bond, slave, BOND_LINK_DOWN); if (slave == rcu_access_pointer(bond->curr_active_slave)) - goto do_failover; + do_failover = true; continue; @@ -2732,8 +2734,9 @@ static void bond_miimon_commit(struct bonding *bond) continue; } + } -do_failover: + if (do_failover) { block_netpoll_tx(); bond_select_active_slave(bond); unblock_netpoll_tx(); @@ -3531,6 +3534,7 @@ static int bond_ab_arp_inspect(struct bonding *bond) */ static void bond_ab_arp_commit(struct bonding *bond) { + bool do_failover = false; struct list_head *iter; unsigned long last_tx; struct slave *slave; @@ -3560,8 +3564,9 @@ static void bond_ab_arp_commit(struct bonding *bond) slave_info(bond->dev, slave->dev, "link status definitely up\n"); if (!rtnl_dereference(bond->curr_active_slave) || - slave == rtnl_dereference(bond->primary_slave)) - goto do_failover; + slave == rtnl_dereference(bond->primary_slave) || + slave->prio > rtnl_dereference(bond->curr_active_slave)->prio) + do_failover = true; } @@ -3580,7 +3585,7 @@ static void bond_ab_arp_commit(struct bonding *bond) if (slave == rtnl_dereference(bond->curr_active_slave)) { RCU_INIT_POINTER(bond->current_arp_slave, NULL); - goto do_failover; + do_failover = true; } continue; @@ -3604,8 +3609,9 @@ static void bond_ab_arp_commit(struct bonding *bond) slave->link_new_state); continue; } + } -do_failover: + if (do_failover) { block_netpoll_tx(); bond_select_active_slave(bond); unblock_netpoll_tx(); From 42a8d4aaea8414f60eb2ed2d92df89a6e2db4615 Mon Sep 17 00:00:00 2001 From: Liang Li Date: Mon, 12 Dec 2022 11:56:47 +0800 Subject: [PATCH 3/3] selftests: bonding: add bonding prio option test Add a test for bonding prio option. Here is the test result: ]# ./option_prio.sh TEST: prio_test (Test bonding option 'prio' with mode=1 monitor=arp_ip_target and primary_reselect=0) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=1 monitor=arp_ip_target and primary_reselect=1) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=1 monitor=arp_ip_target and primary_reselect=2) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=1 monitor=miimon and primary_reselect=0) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=1 monitor=miimon and primary_reselect=1) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=1 monitor=miimon and primary_reselect=2) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=5 monitor=miimon and primary_reselect=0) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=5 monitor=miimon and primary_reselect=1) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=5 monitor=miimon and primary_reselect=2) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=6 monitor=miimon and primary_reselect=0) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=6 monitor=miimon and primary_reselect=1) [ OK ] TEST: prio_test (Test bonding option 'prio' with mode=6 monitor=miimon and primary_reselect=2) [ OK ] Signed-off-by: Liang Li Signed-off-by: Hangbin Liu Signed-off-by: Jakub Kicinski --- .../selftests/drivers/net/bonding/Makefile | 3 +- .../drivers/net/bonding/option_prio.sh | 245 ++++++++++++++++++ 2 files changed, 247 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/drivers/net/bonding/option_prio.sh diff --git a/tools/testing/selftests/drivers/net/bonding/Makefile b/tools/testing/selftests/drivers/net/bonding/Makefile index 0f3921908b078..8e3b786a748f9 100644 --- a/tools/testing/selftests/drivers/net/bonding/Makefile +++ b/tools/testing/selftests/drivers/net/bonding/Makefile @@ -7,7 +7,8 @@ TEST_PROGS := \ bond-lladdr-target.sh \ dev_addr_lists.sh \ mode-1-recovery-updelay.sh \ - mode-2-recovery-updelay.sh + mode-2-recovery-updelay.sh \ + option_prio.sh TEST_FILES := \ lag_lib.sh \ diff --git a/tools/testing/selftests/drivers/net/bonding/option_prio.sh b/tools/testing/selftests/drivers/net/bonding/option_prio.sh new file mode 100755 index 0000000000000..c32eebff5005d --- /dev/null +++ b/tools/testing/selftests/drivers/net/bonding/option_prio.sh @@ -0,0 +1,245 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test bonding option prio +# + +ALL_TESTS=" + prio_arp_ip_target_test + prio_miimon_test +" + +REQUIRE_MZ=no +REQUIRE_JQ=no +NUM_NETIFS=0 +lib_dir=$(dirname "$0") +source "$lib_dir"/net_forwarding_lib.sh + +destroy() +{ + ip link del bond0 &>/dev/null + ip link del br0 &>/dev/null + ip link del veth0 &>/dev/null + ip link del veth1 &>/dev/null + ip link del veth2 &>/dev/null + ip netns del ns1 &>/dev/null + ip link del veth3 &>/dev/null +} + +cleanup() +{ + pre_cleanup + + destroy +} + +skip() +{ + local skip=1 + ip link add name bond0 type bond mode 1 miimon 100 &>/dev/null + ip link add name veth0 type veth peer name veth0_p + ip link set veth0 master bond0 + + # check if iproute support prio option + ip link set dev veth0 type bond_slave prio 10 + [[ $? -ne 0 ]] && skip=0 + + # check if bonding support prio option + ip -d link show veth0 | grep -q "prio 10" + [[ $? -ne 0 ]] && skip=0 + + ip link del bond0 &>/dev/null + ip link del veth0 + + return $skip +} + +active_slave="" +check_active_slave() +{ + local target_active_slave=$1 + active_slave="$(cat /sys/class/net/bond0/bonding/active_slave)" + test "$active_slave" = "$target_active_slave" + check_err $? "Current active slave is $active_slave but not $target_active_slave" +} + + +# Test bonding prio option with mode=$mode monitor=$monitor +# and primary_reselect=$primary_reselect +prio_test() +{ + RET=0 + + local monitor=$1 + local mode=$2 + local primary_reselect=$3 + + local bond_ip4="192.169.1.2" + local peer_ip4="192.169.1.1" + local bond_ip6="2009:0a:0b::02" + local peer_ip6="2009:0a:0b::01" + + + # create veths + ip link add name veth0 type veth peer name veth0_p + ip link add name veth1 type veth peer name veth1_p + ip link add name veth2 type veth peer name veth2_p + + # create bond + if [[ "$monitor" == "miimon" ]];then + ip link add name bond0 type bond mode $mode miimon 100 primary veth1 primary_reselect $primary_reselect + elif [[ "$monitor" == "arp_ip_target" ]];then + ip link add name bond0 type bond mode $mode arp_interval 1000 arp_ip_target $peer_ip4 primary veth1 primary_reselect $primary_reselect + elif [[ "$monitor" == "ns_ip6_target" ]];then + ip link add name bond0 type bond mode $mode arp_interval 1000 ns_ip6_target $peer_ip6 primary veth1 primary_reselect $primary_reselect + fi + ip link set bond0 up + ip link set veth0 master bond0 + ip link set veth1 master bond0 + ip link set veth2 master bond0 + # check bonding member prio value + ip link set dev veth0 type bond_slave prio 0 + ip link set dev veth1 type bond_slave prio 10 + ip link set dev veth2 type bond_slave prio 11 + ip -d link show veth0 | grep -q 'prio 0' + check_err $? "veth0 prio is not 0" + ip -d link show veth1 | grep -q 'prio 10' + check_err $? "veth0 prio is not 10" + ip -d link show veth2 | grep -q 'prio 11' + check_err $? "veth0 prio is not 11" + + ip link set veth0 up + ip link set veth1 up + ip link set veth2 up + ip link set veth0_p up + ip link set veth1_p up + ip link set veth2_p up + + # prepare ping target + ip link add name br0 type bridge + ip link set br0 up + ip link set veth0_p master br0 + ip link set veth1_p master br0 + ip link set veth2_p master br0 + ip link add name veth3 type veth peer name veth3_p + ip netns add ns1 + ip link set veth3_p master br0 up + ip link set veth3 netns ns1 up + ip netns exec ns1 ip addr add $peer_ip4/24 dev veth3 + ip netns exec ns1 ip addr add $peer_ip6/64 dev veth3 + ip addr add $bond_ip4/24 dev bond0 + ip addr add $bond_ip6/64 dev bond0 + sleep 5 + + ping $peer_ip4 -c5 -I bond0 &>/dev/null + check_err $? "ping failed 1." + ping6 $peer_ip6 -c5 -I bond0 &>/dev/null + check_err $? "ping6 failed 1." + + # active salve should be the primary slave + check_active_slave veth1 + + # active slave should be the higher prio slave + ip link set $active_slave down + ping $peer_ip4 -c5 -I bond0 &>/dev/null + check_err $? "ping failed 2." + check_active_slave veth2 + + # when only 1 slave is up + ip link set $active_slave down + ping $peer_ip4 -c5 -I bond0 &>/dev/null + check_err $? "ping failed 3." + check_active_slave veth0 + + # when a higher prio slave change to up + ip link set veth2 up + ping $peer_ip4 -c5 -I bond0 &>/dev/null + check_err $? "ping failed 4." + case $primary_reselect in + "0") + check_active_slave "veth2" + ;; + "1") + check_active_slave "veth0" + ;; + "2") + check_active_slave "veth0" + ;; + esac + local pre_active_slave=$active_slave + + # when the primary slave change to up + ip link set veth1 up + ping $peer_ip4 -c5 -I bond0 &>/dev/null + check_err $? "ping failed 5." + case $primary_reselect in + "0") + check_active_slave "veth1" + ;; + "1") + check_active_slave "$pre_active_slave" + ;; + "2") + check_active_slave "$pre_active_slave" + ip link set $active_slave down + ping $peer_ip4 -c5 -I bond0 &>/dev/null + check_err $? "ping failed 6." + check_active_slave "veth1" + ;; + esac + + # Test changing bond salve prio + if [[ "$primary_reselect" == "0" ]];then + ip link set dev veth0 type bond_slave prio 1000000 + ip link set dev veth1 type bond_slave prio 0 + ip link set dev veth2 type bond_slave prio -50 + ip -d link show veth0 | grep -q 'prio 1000000' + check_err $? "veth0 prio is not 1000000" + ip -d link show veth1 | grep -q 'prio 0' + check_err $? "veth1 prio is not 0" + ip -d link show veth2 | grep -q 'prio -50' + check_err $? "veth3 prio is not -50" + check_active_slave "veth1" + + ip link set $active_slave down + ping $peer_ip4 -c5 -I bond0 &>/dev/null + check_err $? "ping failed 7." + check_active_slave "veth0" + fi + + cleanup + + log_test "prio_test" "Test bonding option 'prio' with mode=$mode monitor=$monitor and primary_reselect=$primary_reselect" +} + +prio_miimon_test() +{ + local mode + local primary_reselect + + for mode in 1 5 6; do + for primary_reselect in 0 1 2; do + prio_test "miimon" $mode $primary_reselect + done + done +} + +prio_arp_ip_target_test() +{ + local primary_reselect + + for primary_reselect in 0 1 2; do + prio_test "arp_ip_target" 1 $primary_reselect + done +} + +if skip;then + log_test_skip "option_prio.sh" "Current iproute doesn't support 'prio'." + exit 0 +fi + +trap cleanup EXIT + +tests_run + +exit "$EXIT_STATUS"