Skip to content

Commit d3ba321

Browse files
Dmitry Skorodumovkuba-moo
authored andcommitted
ipvlan: Make the addrs_lock be per port
Make the addrs_lock be per port, not per ipvlan dev. Initial code seems to be written in the assumption, that any address change must occur under RTNL. But it is not so for the case of IPv6. So 1) Introduce per-port addrs_lock. 2) It was needed to fix places where it was forgotten to take lock (ipvlan_open/ipvlan_close) This appears to be a very minor problem though. Since it's highly unlikely that ipvlan_add_addr() will be called on 2 CPU simultaneously. But nevertheless, this could cause: 1) False-negative of ipvlan_addr_busy(): one interface iterated through all port->ipvlans + ipvlan->addrs under some ipvlan spinlock, and another added IP under its own lock. Though this is only possible for IPv6, since looks like only ipvlan_addr6_event() can be called without rtnl_lock. 2) Race since ipvlan_ht_addr_add(port) is called under different ipvlan->addrs_lock locks This should not affect performance, since add/remove IP is a rare situation and spinlock is not taken on fast paths. Fixes: 8230819 ("ipvlan: use per device spinlock to protect addrs list updates") Signed-off-by: Dmitry Skorodumov <skorodumov.dmitry@huawei.com> Reviewed-by: Paolo Abeni <pabeni@redhat.com> Link: https://patch.msgid.link/20260112142417.4039566-2-skorodumov.dmitry@huawei.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 7a29f6b commit d3ba321

3 files changed

Lines changed: 37 additions & 30 deletions

File tree

drivers/net/ipvlan/ipvlan.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ struct ipvl_dev {
6969
DECLARE_BITMAP(mac_filters, IPVLAN_MAC_FILTER_SIZE);
7070
netdev_features_t sfeatures;
7171
u32 msg_enable;
72-
spinlock_t addrs_lock;
7372
};
7473

7574
struct ipvl_addr {
@@ -90,6 +89,7 @@ struct ipvl_port {
9089
struct net_device *dev;
9190
possible_net_t pnet;
9291
struct hlist_head hlhead[IPVLAN_HASH_SIZE];
92+
spinlock_t addrs_lock; /* guards hash-table and addrs */
9393
struct list_head ipvlans;
9494
u16 mode;
9595
u16 flags;

drivers/net/ipvlan/ipvlan_core.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,15 @@ void ipvlan_ht_addr_del(struct ipvl_addr *addr)
107107
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
108108
const void *iaddr, bool is_v6)
109109
{
110-
struct ipvl_addr *addr, *ret = NULL;
110+
struct ipvl_addr *addr;
111111

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;
118117
}
119-
rcu_read_unlock();
120-
return ret;
118+
return NULL;
121119
}
122120

123121
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)

drivers/net/ipvlan/ipvlan_main.c

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ static int ipvlan_port_create(struct net_device *dev)
7575
for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++)
7676
INIT_HLIST_HEAD(&port->hlhead[idx]);
7777

78+
spin_lock_init(&port->addrs_lock);
7879
skb_queue_head_init(&port->backlog);
7980
INIT_WORK(&port->wq, ipvlan_process_multicast);
8081
ida_init(&port->ida);
@@ -181,6 +182,7 @@ static void ipvlan_uninit(struct net_device *dev)
181182
static int ipvlan_open(struct net_device *dev)
182183
{
183184
struct ipvl_dev *ipvlan = netdev_priv(dev);
185+
struct ipvl_port *port = ipvlan->port;
184186
struct ipvl_addr *addr;
185187

186188
if (ipvlan->port->mode == IPVLAN_MODE_L3 ||
@@ -189,10 +191,10 @@ static int ipvlan_open(struct net_device *dev)
189191
else
190192
dev->flags &= ~IFF_NOARP;
191193

192-
rcu_read_lock();
193-
list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
194+
spin_lock_bh(&port->addrs_lock);
195+
list_for_each_entry(addr, &ipvlan->addrs, anode)
194196
ipvlan_ht_addr_add(ipvlan, addr);
195-
rcu_read_unlock();
197+
spin_unlock_bh(&port->addrs_lock);
196198

197199
return 0;
198200
}
@@ -206,10 +208,10 @@ static int ipvlan_stop(struct net_device *dev)
206208
dev_uc_unsync(phy_dev, dev);
207209
dev_mc_unsync(phy_dev, dev);
208210

209-
rcu_read_lock();
210-
list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
211+
spin_lock_bh(&ipvlan->port->addrs_lock);
212+
list_for_each_entry(addr, &ipvlan->addrs, anode)
211213
ipvlan_ht_addr_del(addr);
212-
rcu_read_unlock();
214+
spin_unlock_bh(&ipvlan->port->addrs_lock);
213215

214216
return 0;
215217
}
@@ -579,7 +581,6 @@ int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params,
579581
if (!tb[IFLA_MTU])
580582
ipvlan_adjust_mtu(ipvlan, phy_dev);
581583
INIT_LIST_HEAD(&ipvlan->addrs);
582-
spin_lock_init(&ipvlan->addrs_lock);
583584

584585
/* TODO Probably put random address here to be presented to the
585586
* world but keep using the physical-dev address for the outgoing
@@ -657,13 +658,13 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
657658
struct ipvl_dev *ipvlan = netdev_priv(dev);
658659
struct ipvl_addr *addr, *next;
659660

660-
spin_lock_bh(&ipvlan->addrs_lock);
661+
spin_lock_bh(&ipvlan->port->addrs_lock);
661662
list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
662663
ipvlan_ht_addr_del(addr);
663664
list_del_rcu(&addr->anode);
664665
kfree_rcu(addr, rcu);
665666
}
666-
spin_unlock_bh(&ipvlan->addrs_lock);
667+
spin_unlock_bh(&ipvlan->port->addrs_lock);
667668

668669
ida_free(&ipvlan->port->ida, dev->dev_id);
669670
list_del_rcu(&ipvlan->pnode);
@@ -817,6 +818,8 @@ static int ipvlan_add_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
817818
{
818819
struct ipvl_addr *addr;
819820

821+
assert_spin_locked(&ipvlan->port->addrs_lock);
822+
820823
addr = kzalloc(sizeof(struct ipvl_addr), GFP_ATOMIC);
821824
if (!addr)
822825
return -ENOMEM;
@@ -847,16 +850,16 @@ static void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
847850
{
848851
struct ipvl_addr *addr;
849852

850-
spin_lock_bh(&ipvlan->addrs_lock);
853+
spin_lock_bh(&ipvlan->port->addrs_lock);
851854
addr = ipvlan_find_addr(ipvlan, iaddr, is_v6);
852855
if (!addr) {
853-
spin_unlock_bh(&ipvlan->addrs_lock);
856+
spin_unlock_bh(&ipvlan->port->addrs_lock);
854857
return;
855858
}
856859

857860
ipvlan_ht_addr_del(addr);
858861
list_del_rcu(&addr->anode);
859-
spin_unlock_bh(&ipvlan->addrs_lock);
862+
spin_unlock_bh(&ipvlan->port->addrs_lock);
860863
kfree_rcu(addr, rcu);
861864
}
862865

@@ -878,14 +881,14 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
878881
{
879882
int ret = -EINVAL;
880883

881-
spin_lock_bh(&ipvlan->addrs_lock);
884+
spin_lock_bh(&ipvlan->port->addrs_lock);
882885
if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true))
883886
netif_err(ipvlan, ifup, ipvlan->dev,
884887
"Failed to add IPv6=%pI6c addr for %s intf\n",
885888
ip6_addr, ipvlan->dev->name);
886889
else
887890
ret = ipvlan_add_addr(ipvlan, ip6_addr, true);
888-
spin_unlock_bh(&ipvlan->addrs_lock);
891+
spin_unlock_bh(&ipvlan->port->addrs_lock);
889892
return ret;
890893
}
891894

@@ -924,36 +927,39 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused,
924927
struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr;
925928
struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev;
926929
struct ipvl_dev *ipvlan = netdev_priv(dev);
930+
int ret = NOTIFY_OK;
927931

928932
if (!ipvlan_is_valid_dev(dev))
929933
return NOTIFY_DONE;
930934

931935
switch (event) {
932936
case NETDEV_UP:
937+
spin_lock_bh(&ipvlan->port->addrs_lock);
933938
if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) {
934939
NL_SET_ERR_MSG(i6vi->extack,
935940
"Address already assigned to an ipvlan device");
936-
return notifier_from_errno(-EADDRINUSE);
941+
ret = notifier_from_errno(-EADDRINUSE);
937942
}
943+
spin_unlock_bh(&ipvlan->port->addrs_lock);
938944
break;
939945
}
940946

941-
return NOTIFY_OK;
947+
return ret;
942948
}
943949
#endif
944950

945951
static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
946952
{
947953
int ret = -EINVAL;
948954

949-
spin_lock_bh(&ipvlan->addrs_lock);
955+
spin_lock_bh(&ipvlan->port->addrs_lock);
950956
if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false))
951957
netif_err(ipvlan, ifup, ipvlan->dev,
952958
"Failed to add IPv4=%pI4 on %s intf.\n",
953959
ip4_addr, ipvlan->dev->name);
954960
else
955961
ret = ipvlan_add_addr(ipvlan, ip4_addr, false);
956-
spin_unlock_bh(&ipvlan->addrs_lock);
962+
spin_unlock_bh(&ipvlan->port->addrs_lock);
957963
return ret;
958964
}
959965

@@ -995,21 +1001,24 @@ static int ipvlan_addr4_validator_event(struct notifier_block *unused,
9951001
struct in_validator_info *ivi = (struct in_validator_info *)ptr;
9961002
struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev;
9971003
struct ipvl_dev *ipvlan = netdev_priv(dev);
1004+
int ret = NOTIFY_OK;
9981005

9991006
if (!ipvlan_is_valid_dev(dev))
10001007
return NOTIFY_DONE;
10011008

10021009
switch (event) {
10031010
case NETDEV_UP:
1011+
spin_lock_bh(&ipvlan->port->addrs_lock);
10041012
if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) {
10051013
NL_SET_ERR_MSG(ivi->extack,
10061014
"Address already assigned to an ipvlan device");
1007-
return notifier_from_errno(-EADDRINUSE);
1015+
ret = notifier_from_errno(-EADDRINUSE);
10081016
}
1017+
spin_unlock_bh(&ipvlan->port->addrs_lock);
10091018
break;
10101019
}
10111020

1012-
return NOTIFY_OK;
1021+
return ret;
10131022
}
10141023

10151024
static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {

0 commit comments

Comments
 (0)