aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2018-02-28 04:59:27 -0500
committerDavid S. Miller <davem@davemloft.net>2018-02-28 12:20:13 -0500
commit8230819494b3bf284ca7262ac5f877333147b937 (patch)
tree5bcaf5acf1575508499e7d68d5d573670c62a9fc
parentcccc200fcaf04cff4342036a72e51d6adf6c98c1 (diff)
ipvlan: use per device spinlock to protect addrs list updates
This changeset moves ipvlan address under RCU protection, using a per ipvlan device spinlock to protect list mutation and RCU read access to protect list traversal. Also explicitly use RCU read lock to traverse the per port ipvlans list, so that we can now perform a full address lookup without asserting the RTNL lock. Overall this allows the ipvlan driver to check fully for duplicate addresses - before this commit ipv6 addresses assigned by autoconf via prefix delegation where accepted without any check - and avoid the following rntl assertion failure still in the same code path: RTNL: assertion failed at drivers/net/ipvlan/ipvlan_core.c (124) WARNING: CPU: 15 PID: 0 at drivers/net/ipvlan/ipvlan_core.c:124 ipvlan_addr_busy+0x97/0xa0 [ipvlan] Modules linked in: ipvlan(E) ixgbe CPU: 15 PID: 0 Comm: swapper/15 Tainted: G E 4.16.0-rc2.ipvlan+ #1782 Hardware name: Dell Inc. PowerEdge R730/072T6D, BIOS 2.1.7 06/16/2016 RIP: 0010:ipvlan_addr_busy+0x97/0xa0 [ipvlan] RSP: 0018:ffff881ff9e03768 EFLAGS: 00010286 RAX: 0000000000000000 RBX: ffff881fdf2a9000 RCX: 0000000000000000 RDX: 0000000000000001 RSI: 00000000000000f6 RDI: 0000000000000300 RBP: ffff881fdf2a8000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000001 R11: ffff881ff9e034c0 R12: ffff881fe07bcc00 R13: 0000000000000001 R14: ffffffffa02002b0 R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff881ff9e00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fc5c1a4f248 CR3: 000000207e012005 CR4: 00000000001606e0 Call Trace: <IRQ> ipvlan_addr6_event+0x6c/0xd0 [ipvlan] notifier_call_chain+0x49/0x90 atomic_notifier_call_chain+0x6a/0x100 ipv6_add_addr+0x5f9/0x720 addrconf_prefix_rcv_add_addr+0x244/0x3c0 addrconf_prefix_rcv+0x2f3/0x790 ndisc_router_discovery+0x633/0xb70 ndisc_rcv+0x155/0x180 icmpv6_rcv+0x4ac/0x5f0 ip6_input_finish+0x138/0x6a0 ip6_input+0x41/0x1f0 ipv6_rcv+0x4db/0x8d0 __netif_receive_skb_core+0x3d5/0xe40 netif_receive_skb_internal+0x89/0x370 napi_gro_receive+0x14f/0x1e0 ixgbe_clean_rx_irq+0x4ce/0x1020 [ixgbe] ixgbe_poll+0x31a/0x7a0 [ixgbe] net_rx_action+0x296/0x4f0 __do_softirq+0xcf/0x4f5 irq_exit+0xf5/0x110 do_IRQ+0x62/0x110 common_interrupt+0x91/0x91 </IRQ> v1 -> v2: drop unneeded in_softirq check in ipvlan_addr6_validator_event() Fixes: e9997c2938b2 ("ipvlan: fix check for IP addresses in control path") Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ipvlan/ipvlan.h1
-rw-r--r--drivers/net/ipvlan/ipvlan_core.c30
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c60
3 files changed, 56 insertions, 35 deletions
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h
index 5166575a164d..a115f12bf130 100644
--- a/drivers/net/ipvlan/ipvlan.h
+++ b/drivers/net/ipvlan/ipvlan.h
@@ -74,6 +74,7 @@ struct ipvl_dev {
74 DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE); 74 DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
75 netdev_features_t sfeatures; 75 netdev_features_t sfeatures;
76 u32 msg_enable; 76 u32 msg_enable;
77 spinlock_t addrs_lock;
77}; 78};
78 79
79struct ipvl_addr { 80struct ipvl_addr {
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index dbbb884eb81a..17daebd19e65 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -109,25 +109,33 @@ void ipvlan_ht_addr_del(struct ipvl_addr *addr)
109struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan, 109struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
110 const void *iaddr, bool is_v6) 110 const void *iaddr, bool is_v6)
111{ 111{
112 struct ipvl_addr *addr; 112 struct ipvl_addr *addr, *ret = NULL;
113 113
114 list_for_each_entry(addr, &ipvlan->addrs, anode) 114 rcu_read_lock();
115 if (addr_equal(is_v6, addr, iaddr)) 115 list_for_each_entry_rcu(addr, &ipvlan->addrs, anode) {
116 return addr; 116 if (addr_equal(is_v6, addr, iaddr)) {
117 return NULL; 117 ret = addr;
118 break;
119 }
120 }
121 rcu_read_unlock();
122 return ret;
118} 123}
119 124
120bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6) 125bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
121{ 126{
122 struct ipvl_dev *ipvlan; 127 struct ipvl_dev *ipvlan;
128 bool ret = false;
123 129
124 ASSERT_RTNL(); 130 rcu_read_lock();
125 131 list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {
126 list_for_each_entry(ipvlan, &port->ipvlans, pnode) { 132 if (ipvlan_find_addr(ipvlan, iaddr, is_v6)) {
127 if (ipvlan_find_addr(ipvlan, iaddr, is_v6)) 133 ret = true;
128 return true; 134 break;
135 }
129 } 136 }
130 return false; 137 rcu_read_unlock();
138 return ret;
131} 139}
132 140
133static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type) 141static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type)
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index d05b902c925b..3efc1c92c6a7 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -227,8 +227,10 @@ static int ipvlan_open(struct net_device *dev)
227 else 227 else
228 dev->flags &= ~IFF_NOARP; 228 dev->flags &= ~IFF_NOARP;
229 229
230 list_for_each_entry(addr, &ipvlan->addrs, anode) 230 rcu_read_lock();
231 list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
231 ipvlan_ht_addr_add(ipvlan, addr); 232 ipvlan_ht_addr_add(ipvlan, addr);
233 rcu_read_unlock();
232 234
233 return dev_uc_add(phy_dev, phy_dev->dev_addr); 235 return dev_uc_add(phy_dev, phy_dev->dev_addr);
234} 236}
@@ -244,8 +246,10 @@ static int ipvlan_stop(struct net_device *dev)
244 246
245 dev_uc_del(phy_dev, phy_dev->dev_addr); 247 dev_uc_del(phy_dev, phy_dev->dev_addr);
246 248
247 list_for_each_entry(addr, &ipvlan->addrs, anode) 249 rcu_read_lock();
250 list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
248 ipvlan_ht_addr_del(addr); 251 ipvlan_ht_addr_del(addr);
252 rcu_read_unlock();
249 253
250 return 0; 254 return 0;
251} 255}
@@ -588,6 +592,7 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
588 ipvlan->sfeatures = IPVLAN_FEATURES; 592 ipvlan->sfeatures = IPVLAN_FEATURES;
589 ipvlan_adjust_mtu(ipvlan, phy_dev); 593 ipvlan_adjust_mtu(ipvlan, phy_dev);
590 INIT_LIST_HEAD(&ipvlan->addrs); 594 INIT_LIST_HEAD(&ipvlan->addrs);
595 spin_lock_init(&ipvlan->addrs_lock);
591 596
592 /* TODO Probably put random address here to be presented to the 597 /* TODO Probably put random address here to be presented to the
593 * world but keep using the physical-dev address for the outgoing 598 * world but keep using the physical-dev address for the outgoing
@@ -665,11 +670,13 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
665 struct ipvl_dev *ipvlan = netdev_priv(dev); 670 struct ipvl_dev *ipvlan = netdev_priv(dev);
666 struct ipvl_addr *addr, *next; 671 struct ipvl_addr *addr, *next;
667 672
673 spin_lock_bh(&ipvlan->addrs_lock);
668 list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) { 674 list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
669 ipvlan_ht_addr_del(addr); 675 ipvlan_ht_addr_del(addr);
670 list_del(&addr->anode); 676 list_del_rcu(&addr->anode);
671 kfree_rcu(addr, rcu); 677 kfree_rcu(addr, rcu);
672 } 678 }
679 spin_unlock_bh(&ipvlan->addrs_lock);
673 680
674 ida_simple_remove(&ipvlan->port->ida, dev->dev_id); 681 ida_simple_remove(&ipvlan->port->ida, dev->dev_id);
675 list_del_rcu(&ipvlan->pnode); 682 list_del_rcu(&ipvlan->pnode);
@@ -760,8 +767,7 @@ static int ipvlan_device_event(struct notifier_block *unused,
760 if (dev->reg_state != NETREG_UNREGISTERING) 767 if (dev->reg_state != NETREG_UNREGISTERING)
761 break; 768 break;
762 769
763 list_for_each_entry_safe(ipvlan, next, &port->ipvlans, 770 list_for_each_entry_safe(ipvlan, next, &port->ipvlans, pnode)
764 pnode)
765 ipvlan->dev->rtnl_link_ops->dellink(ipvlan->dev, 771 ipvlan->dev->rtnl_link_ops->dellink(ipvlan->dev,
766 &lst_kill); 772 &lst_kill);
767 unregister_netdevice_many(&lst_kill); 773 unregister_netdevice_many(&lst_kill);
@@ -793,6 +799,7 @@ static int ipvlan_device_event(struct notifier_block *unused,
793 return NOTIFY_DONE; 799 return NOTIFY_DONE;
794} 800}
795 801
802/* the caller must held the addrs lock */
796static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) 803static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
797{ 804{
798 struct ipvl_addr *addr; 805 struct ipvl_addr *addr;
@@ -811,7 +818,8 @@ static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
811 addr->atype = IPVL_IPV6; 818 addr->atype = IPVL_IPV6;
812#endif 819#endif
813 } 820 }
814 list_add_tail(&addr->anode, &ipvlan->addrs); 821
822 list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
815 823
816 /* If the interface is not up, the address will be added to the hash 824 /* If the interface is not up, the address will be added to the hash
817 * list by ipvlan_open. 825 * list by ipvlan_open.
@@ -826,15 +834,17 @@ static void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
826{ 834{
827 struct ipvl_addr *addr; 835 struct ipvl_addr *addr;
828 836
837 spin_lock_bh(&ipvlan->addrs_lock);
829 addr = ipvlan_find_addr(ipvlan, iaddr, is_v6); 838 addr = ipvlan_find_addr(ipvlan, iaddr, is_v6);
830 if (!addr) 839 if (!addr) {
840 spin_unlock_bh(&ipvlan->addrs_lock);
831 return; 841 return;
842 }
832 843
833 ipvlan_ht_addr_del(addr); 844 ipvlan_ht_addr_del(addr);
834 list_del(&addr->anode); 845 list_del_rcu(&addr->anode);
846 spin_unlock_bh(&ipvlan->addrs_lock);
835 kfree_rcu(addr, rcu); 847 kfree_rcu(addr, rcu);
836
837 return;
838} 848}
839 849
840static bool ipvlan_is_valid_dev(const struct net_device *dev) 850static bool ipvlan_is_valid_dev(const struct net_device *dev)
@@ -853,14 +863,17 @@ static bool ipvlan_is_valid_dev(const struct net_device *dev)
853#if IS_ENABLED(CONFIG_IPV6) 863#if IS_ENABLED(CONFIG_IPV6)
854static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) 864static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
855{ 865{
856 if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) { 866 int ret = -EINVAL;
867
868 spin_lock_bh(&ipvlan->addrs_lock);
869 if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true))
857 netif_err(ipvlan, ifup, ipvlan->dev, 870 netif_err(ipvlan, ifup, ipvlan->dev,
858 "Failed to add IPv6=%pI6c addr for %s intf\n", 871 "Failed to add IPv6=%pI6c addr for %s intf\n",
859 ip6_addr, ipvlan->dev->name); 872 ip6_addr, ipvlan->dev->name);
860 return -EINVAL; 873 else
861 } 874 ret = ipvlan_add_addr(ipvlan, ip6_addr, true);
862 875 spin_unlock_bh(&ipvlan->addrs_lock);
863 return ipvlan_add_addr(ipvlan, ip6_addr, true); 876 return ret;
864} 877}
865 878
866static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) 879static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
@@ -899,10 +912,6 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused,
899 struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev; 912 struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev;
900 struct ipvl_dev *ipvlan = netdev_priv(dev); 913 struct ipvl_dev *ipvlan = netdev_priv(dev);
901 914
902 /* FIXME IPv6 autoconf calls us from bh without RTNL */
903 if (in_softirq())
904 return NOTIFY_DONE;
905
906 if (!ipvlan_is_valid_dev(dev)) 915 if (!ipvlan_is_valid_dev(dev))
907 return NOTIFY_DONE; 916 return NOTIFY_DONE;
908 917
@@ -922,14 +931,17 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused,
922 931
923static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) 932static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
924{ 933{
925 if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) { 934 int ret = -EINVAL;
935
936 spin_lock_bh(&ipvlan->addrs_lock);
937 if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false))
926 netif_err(ipvlan, ifup, ipvlan->dev, 938 netif_err(ipvlan, ifup, ipvlan->dev,
927 "Failed to add IPv4=%pI4 on %s intf.\n", 939 "Failed to add IPv4=%pI4 on %s intf.\n",
928 ip4_addr, ipvlan->dev->name); 940 ip4_addr, ipvlan->dev->name);
929 return -EINVAL; 941 else
930 } 942 ret = ipvlan_add_addr(ipvlan, ip4_addr, false);
931 943 spin_unlock_bh(&ipvlan->addrs_lock);
932 return ipvlan_add_addr(ipvlan, ip4_addr, false); 944 return ret;
933} 945}
934 946
935static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) 947static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)