Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ipvlan-addrs_lock-made-per-port'

Dmitry Skorodumov says:

====================
ipvlan: addrs_lock made per port

First patch fixes a rather minor issues that sometimes
ipvlan-addrs are modified without lock (because
for IPv6 addr can be sometimes added without RTNL)
====================

Link: https://patch.msgid.link/20260112142417.4039566-1-skorodumov.dmitry@huawei.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+208 -30
+1 -1
drivers/net/ipvlan/ipvlan.h
··· 69 69 DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE); 70 70 netdev_features_t sfeatures; 71 71 u32 msg_enable; 72 - spinlock_t addrs_lock; 73 72 }; 74 73 75 74 struct ipvl_addr { ··· 89 90 struct net_device *dev; 90 91 possible_net_t pnet; 91 92 struct hlist_head hlhead[IPVLAN_HASH_SIZE]; 93 + spinlock_t addrs_lock; /* guards hash-table and addrs */ 92 94 struct list_head ipvlans; 93 95 u16 mode; 94 96 u16 flags;
+7 -9
drivers/net/ipvlan/ipvlan_core.c
··· 107 107 struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan, 108 108 const void *iaddr, bool is_v6) 109 109 { 110 - struct ipvl_addr *addr, *ret = NULL; 110 + struct ipvl_addr *addr; 111 111 112 - rcu_read_lock(); 113 - list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) { 114 - if (addr_equal(is_v6, addr, iaddr)) { 115 - ret = addr; 116 - break; 117 - } 112 + assert_spin_locked(&ipvlan->port->addrs_lock); 113 + 114 + list_for_each_entry(addr, &ipvlan->addrs, anode) { 115 + if (addr_equal(is_v6, addr, iaddr)) 116 + return addr; 118 117 } 119 - rcu_read_unlock(); 120 - return ret; 118 + return NULL; 121 119 } 122 120 123 121 bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
+29 -20
drivers/net/ipvlan/ipvlan_main.c
··· 75 75 for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++) 76 76 INIT_HLIST_HEAD(&port->hlhead[idx]); 77 77 78 + spin_lock_init(&port->addrs_lock); 78 79 skb_queue_head_init(&port->backlog); 79 80 INIT_WORK(&port->wq, ipvlan_process_multicast); 80 81 ida_init(&port->ida); ··· 182 181 static int ipvlan_open(struct net_device *dev) 183 182 { 184 183 struct ipvl_dev *ipvlan = netdev_priv(dev); 184 + struct ipvl_port *port = ipvlan->port; 185 185 struct ipvl_addr *addr; 186 186 187 187 if (ipvlan->port->mode == IPVLAN_MODE_L3 || ··· 191 189 else 192 190 dev->flags &= ~IFF_NOARP; 193 191 194 - rcu_read_lock(); 195 - list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) 192 + spin_lock_bh(&port->addrs_lock); 193 + list_for_each_entry(addr, &ipvlan->addrs, anode) 196 194 ipvlan_ht_addr_add(ipvlan, addr); 197 - rcu_read_unlock(); 195 + spin_unlock_bh(&port->addrs_lock); 198 196 199 197 return 0; 200 198 } ··· 208 206 dev_uc_unsync(phy_dev, dev); 209 207 dev_mc_unsync(phy_dev, dev); 210 208 211 - rcu_read_lock(); 212 - list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) 209 + spin_lock_bh(&ipvlan->port->addrs_lock); 210 + list_for_each_entry(addr, &ipvlan->addrs, anode) 213 211 ipvlan_ht_addr_del(addr); 214 - rcu_read_unlock(); 212 + spin_unlock_bh(&ipvlan->port->addrs_lock); 215 213 216 214 return 0; 217 215 } ··· 581 579 if (!tb[IFLA_MTU]) 582 580 ipvlan_adjust_mtu(ipvlan, phy_dev); 583 581 INIT_LIST_HEAD(&ipvlan->addrs); 584 - spin_lock_init(&ipvlan->addrs_lock); 585 582 586 583 /* TODO Probably put random address here to be presented to the 587 584 * world but keep using the physical-dev address for the outgoing ··· 658 657 struct ipvl_dev *ipvlan = netdev_priv(dev); 659 658 struct ipvl_addr *addr, *next; 660 659 661 - spin_lock_bh(&ipvlan->addrs_lock); 660 + spin_lock_bh(&ipvlan->port->addrs_lock); 662 661 list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) { 663 662 ipvlan_ht_addr_del(addr); 664 663 list_del_rcu(&addr->anode); 665 664 kfree_rcu(addr, rcu); 666 665 } 667 - spin_unlock_bh(&ipvlan->addrs_lock); 666 + spin_unlock_bh(&ipvlan->port->addrs_lock); 668 667 669 668 ida_free(&ipvlan->port->ida, dev->dev_id); 670 669 list_del_rcu(&ipvlan->pnode); ··· 818 817 { 819 818 struct ipvl_addr *addr; 820 819 820 + assert_spin_locked(&ipvlan->port->addrs_lock); 821 + 821 822 addr = kzalloc(sizeof(struct ipvl_addr), GFP_ATOMIC); 822 823 if (!addr) 823 824 return -ENOMEM; ··· 850 847 { 851 848 struct ipvl_addr *addr; 852 849 853 - spin_lock_bh(&ipvlan->addrs_lock); 850 + spin_lock_bh(&ipvlan->port->addrs_lock); 854 851 addr = ipvlan_find_addr(ipvlan, iaddr, is_v6); 855 852 if (!addr) { 856 - spin_unlock_bh(&ipvlan->addrs_lock); 853 + spin_unlock_bh(&ipvlan->port->addrs_lock); 857 854 return; 858 855 } 859 856 860 857 ipvlan_ht_addr_del(addr); 861 858 list_del_rcu(&addr->anode); 862 - spin_unlock_bh(&ipvlan->addrs_lock); 859 + spin_unlock_bh(&ipvlan->port->addrs_lock); 863 860 kfree_rcu(addr, rcu); 864 861 } 865 862 ··· 881 878 { 882 879 int ret = -EINVAL; 883 880 884 - spin_lock_bh(&ipvlan->addrs_lock); 881 + spin_lock_bh(&ipvlan->port->addrs_lock); 885 882 if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) 886 883 netif_err(ipvlan, ifup, ipvlan->dev, 887 884 "Failed to add IPv6=%pI6c addr for %s intf\n", 888 885 ip6_addr, ipvlan->dev->name); 889 886 else 890 887 ret = ipvlan_add_addr(ipvlan, ip6_addr, true); 891 - spin_unlock_bh(&ipvlan->addrs_lock); 888 + spin_unlock_bh(&ipvlan->port->addrs_lock); 892 889 return ret; 893 890 } 894 891 ··· 927 924 struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr; 928 925 struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev; 929 926 struct ipvl_dev *ipvlan = netdev_priv(dev); 927 + int ret = NOTIFY_OK; 930 928 931 929 if (!ipvlan_is_valid_dev(dev)) 932 930 return NOTIFY_DONE; 933 931 934 932 switch (event) { 935 933 case NETDEV_UP: 934 + spin_lock_bh(&ipvlan->port->addrs_lock); 936 935 if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) { 937 936 NL_SET_ERR_MSG(i6vi->extack, 938 937 "Address already assigned to an ipvlan device"); 939 - return notifier_from_errno(-EADDRINUSE); 938 + ret = notifier_from_errno(-EADDRINUSE); 940 939 } 940 + spin_unlock_bh(&ipvlan->port->addrs_lock); 941 941 break; 942 942 } 943 943 944 - return NOTIFY_OK; 944 + return ret; 945 945 } 946 946 #endif 947 947 ··· 952 946 { 953 947 int ret = -EINVAL; 954 948 955 - spin_lock_bh(&ipvlan->addrs_lock); 949 + spin_lock_bh(&ipvlan->port->addrs_lock); 956 950 if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) 957 951 netif_err(ipvlan, ifup, ipvlan->dev, 958 952 "Failed to add IPv4=%pI4 on %s intf.\n", 959 953 ip4_addr, ipvlan->dev->name); 960 954 else 961 955 ret = ipvlan_add_addr(ipvlan, ip4_addr, false); 962 - spin_unlock_bh(&ipvlan->addrs_lock); 956 + spin_unlock_bh(&ipvlan->port->addrs_lock); 963 957 return ret; 964 958 } 965 959 ··· 1001 995 struct in_validator_info *ivi = (struct in_validator_info *)ptr; 1002 996 struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev; 1003 997 struct ipvl_dev *ipvlan = netdev_priv(dev); 998 + int ret = NOTIFY_OK; 1004 999 1005 1000 if (!ipvlan_is_valid_dev(dev)) 1006 1001 return NOTIFY_DONE; 1007 1002 1008 1003 switch (event) { 1009 1004 case NETDEV_UP: 1005 + spin_lock_bh(&ipvlan->port->addrs_lock); 1010 1006 if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) { 1011 1007 NL_SET_ERR_MSG(ivi->extack, 1012 1008 "Address already assigned to an ipvlan device"); 1013 - return notifier_from_errno(-EADDRINUSE); 1009 + ret = notifier_from_errno(-EADDRINUSE); 1014 1010 } 1011 + spin_unlock_bh(&ipvlan->port->addrs_lock); 1015 1012 break; 1016 1013 } 1017 1014 1018 - return NOTIFY_OK; 1015 + return ret; 1019 1016 } 1020 1017 1021 1018 static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {
+1
tools/testing/selftests/net/Makefile
··· 48 48 ipv6_flowlabel.sh \ 49 49 ipv6_force_forwarding.sh \ 50 50 ipv6_route_update_soft_lockup.sh \ 51 + ipvtap_test.sh \ 51 52 l2_tos_ttl_inherit.sh \ 52 53 l2tp.sh \ 53 54 link_netns.py \
+2
tools/testing/selftests/net/config
··· 48 48 CONFIG_IPV6_SIT=y 49 49 CONFIG_IPV6_VTI=y 50 50 CONFIG_IPVLAN=m 51 + CONFIG_IPVTAP=m 51 52 CONFIG_KALLSYMS=y 52 53 CONFIG_L2TP=m 53 54 CONFIG_L2TP_ETH=m ··· 117 116 CONFIG_PSAMPLE=m 118 117 CONFIG_RPS=y 119 118 CONFIG_SYSFS=y 119 + CONFIG_TAP=m 120 120 CONFIG_TCP_MD5SIG=y 121 121 CONFIG_TEST_BLACKHOLE_DEV=m 122 122 CONFIG_TEST_BPF=m
+168
tools/testing/selftests/net/ipvtap_test.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Simple tests for ipvtap 5 + 6 + 7 + # 8 + # The testing environment looks this way: 9 + # 10 + # |------HNS-------| |------PHY-------| 11 + # | veth<----------------->veth | 12 + # |------|--|------| |----------------| 13 + # | | 14 + # | | |-----TST0-------| 15 + # | |------------|----ipvlan | 16 + # | |----------------| 17 + # | 18 + # | |-----TST1-------| 19 + # |---------------|----ipvlan | 20 + # |----------------| 21 + # 22 + 23 + ALL_TESTS=" 24 + test_ip_set 25 + " 26 + 27 + source lib.sh 28 + 29 + DEBUG=0 30 + 31 + VETH_HOST=vethtst.h 32 + VETH_PHY=vethtst.p 33 + 34 + NS_COUNT=32 35 + IP_ITERATIONS=1024 36 + IPSET_TIMEOUT="60s" 37 + 38 + ns_run() { 39 + ns=$1 40 + shift 41 + if [[ "$ns" == "global" ]]; then 42 + "$@" >/dev/null 43 + else 44 + ip netns exec "$ns" "$@" >/dev/null 45 + fi 46 + } 47 + 48 + test_ip_setup_env() { 49 + setup_ns NS_PHY 50 + setup_ns HST_NS 51 + 52 + # setup simulated other-host (phy) and host itself 53 + ns_run "$HST_NS" ip link add $VETH_HOST type veth peer name $VETH_PHY \ 54 + netns "$NS_PHY" >/dev/null 55 + ns_run "$HST_NS" ip link set $VETH_HOST up 56 + ns_run "$NS_PHY" ip link set $VETH_PHY up 57 + 58 + for ((i=0; i<NS_COUNT; i++)); do 59 + setup_ns ipvlan_ns_$i 60 + ns="ipvlan_ns_$i" 61 + if [ "$DEBUG" = "1" ]; then 62 + echo "created NS ${!ns}" 63 + fi 64 + if ! ns_run "$HST_NS" ip link add netns ${!ns} ipvlan0 \ 65 + link $VETH_HOST \ 66 + type ipvtap mode l2 bridge; then 67 + exit_error "FAIL: Failed to configure ipvlan link." 68 + fi 69 + done 70 + } 71 + 72 + test_ip_cleanup_env() { 73 + ns_run "$HST_NS" ip link del $VETH_HOST 74 + cleanup_all_ns 75 + } 76 + 77 + exit_error() { 78 + echo "$1" 79 + exit $ksft_fail 80 + } 81 + 82 + rnd() { 83 + echo $(( RANDOM % 32 + 16 )) 84 + } 85 + 86 + test_ip_set_thread() { 87 + # Here we are trying to create some IP conflicts between namespaces. 88 + # If just add/remove IP, nothing interesting will happen. 89 + # But if add random IP and then remove random IP, 90 + # eventually conflicts start to apear. 91 + ip link set ipvlan0 up 92 + for ((i=0; i<IP_ITERATIONS; i++)); do 93 + v=$(rnd) 94 + ip a a "172.25.0.$v/24" dev ipvlan0 2>/dev/null 95 + ip a a "fc00::$v/64" dev ipvlan0 2>/dev/null 96 + v=$(rnd) 97 + ip a d "172.25.0.$v/24" dev ipvlan0 2>/dev/null 98 + ip a d "fc00::$v/64" dev ipvlan0 2>/dev/null 99 + done 100 + } 101 + 102 + test_ip_set() { 103 + RET=0 104 + 105 + trap test_ip_cleanup_env EXIT 106 + 107 + test_ip_setup_env 108 + 109 + declare -A ns_pids 110 + for ((i=0; i<NS_COUNT; i++)); do 111 + ns="ipvlan_ns_$i" 112 + ns_run ${!ns} timeout "$IPSET_TIMEOUT" \ 113 + bash -c "$0 test_ip_set_thread"& 114 + ns_pids[$i]=$! 115 + done 116 + 117 + for ((i=0; i<NS_COUNT; i++)); do 118 + wait "${ns_pids[$i]}" 119 + done 120 + 121 + declare -A all_ips 122 + for ((i=0; i<NS_COUNT; i++)); do 123 + ns="ipvlan_ns_$i" 124 + ip_output=$(ip netns exec ${!ns} ip a l dev ipvlan0 | grep inet) 125 + while IFS= read -r nsip_out; do 126 + if [[ -z $nsip_out ]]; then 127 + continue; 128 + fi 129 + nsip=$(awk '{print $2}' <<< "$nsip_out") 130 + if [[ -v all_ips[$nsip] ]]; then 131 + RET=$ksft_fail 132 + log_test "conflict for $nsip" 133 + return "$RET" 134 + else 135 + all_ips[$nsip]=$i 136 + fi 137 + done <<< "$ip_output" 138 + done 139 + 140 + if [ "$DEBUG" = "1" ]; then 141 + for key in "${!all_ips[@]}"; do 142 + echo "$key: ${all_ips[$key]}" 143 + done 144 + fi 145 + 146 + trap - EXIT 147 + test_ip_cleanup_env 148 + 149 + log_test "test multithreaded ip set" 150 + } 151 + 152 + if [[ "$1" == "-d" ]]; then 153 + DEBUG=1 154 + shift 155 + fi 156 + 157 + if [[ "$1" == "-t" ]]; then 158 + shift 159 + TESTS="$*" 160 + fi 161 + 162 + if [[ "$1" == "test_ip_set_thread" ]]; then 163 + test_ip_set_thread 164 + else 165 + require_command ip 166 + 167 + tests_run 168 + fi