mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
vxlan: convert remotes list to list_rcu
Based on initial work by Mike Rapoport <mike.rapoport@ravellosystems.com> Use list macros and RCU for tracking multiple remotes. Note: this code assumes list always has at least one entry, because delete is not supported. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
This commit is contained in:
parent
4ad169300a
commit
3e61aa8f0a
1 changed files with 55 additions and 42 deletions
|
@ -102,7 +102,7 @@ struct vxlan_rdst {
|
|||
__be16 remote_port;
|
||||
u32 remote_vni;
|
||||
u32 remote_ifindex;
|
||||
struct vxlan_rdst *remote_next;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* Forwarding table entry */
|
||||
|
@ -111,7 +111,7 @@ struct vxlan_fdb {
|
|||
struct rcu_head rcu;
|
||||
unsigned long updated; /* jiffies */
|
||||
unsigned long used;
|
||||
struct vxlan_rdst remote;
|
||||
struct list_head remotes;
|
||||
u16 state; /* see ndm_state */
|
||||
u8 flags; /* see ndm_flags */
|
||||
u8 eth_addr[ETH_ALEN];
|
||||
|
@ -170,6 +170,14 @@ static inline struct hlist_head *vs_head(struct net *net, __be16 port)
|
|||
return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];
|
||||
}
|
||||
|
||||
/* First remote destination for a forwarding entry.
|
||||
* Guaranteed to be non-NULL because remotes are never deleted.
|
||||
*/
|
||||
static inline struct vxlan_rdst *first_remote(struct vxlan_fdb *fdb)
|
||||
{
|
||||
return list_first_or_null_rcu(&fdb->remotes, struct vxlan_rdst, list);
|
||||
}
|
||||
|
||||
/* Find VXLAN socket based on network namespace and UDP port */
|
||||
static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port)
|
||||
{
|
||||
|
@ -275,7 +283,7 @@ static inline size_t vxlan_nlmsg_size(void)
|
|||
}
|
||||
|
||||
static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
|
||||
const struct vxlan_fdb *fdb, int type)
|
||||
struct vxlan_fdb *fdb, int type)
|
||||
{
|
||||
struct net *net = dev_net(vxlan->dev);
|
||||
struct sk_buff *skb;
|
||||
|
@ -285,7 +293,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
|
|||
if (skb == NULL)
|
||||
goto errout;
|
||||
|
||||
err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, &fdb->remote);
|
||||
err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, first_remote(fdb));
|
||||
if (err < 0) {
|
||||
/* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */
|
||||
WARN_ON(err == -EMSGSIZE);
|
||||
|
@ -304,11 +312,16 @@ static void vxlan_ip_miss(struct net_device *dev, __be32 ipa)
|
|||
{
|
||||
struct vxlan_dev *vxlan = netdev_priv(dev);
|
||||
struct vxlan_fdb f;
|
||||
struct vxlan_rdst remote;
|
||||
|
||||
memset(&f, 0, sizeof f);
|
||||
f.state = NUD_STALE;
|
||||
f.remote.remote_ip = ipa; /* goes to NDA_DST */
|
||||
f.remote.remote_vni = VXLAN_N_VID;
|
||||
|
||||
remote.remote_ip = ipa; /* goes to NDA_DST */
|
||||
remote.remote_vni = VXLAN_N_VID;
|
||||
|
||||
INIT_LIST_HEAD(&f.remotes);
|
||||
list_add_rcu(&remote.list, &f.remotes);
|
||||
|
||||
vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
|
||||
}
|
||||
|
@ -318,6 +331,7 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
|
|||
struct vxlan_fdb f;
|
||||
|
||||
memset(&f, 0, sizeof f);
|
||||
INIT_LIST_HEAD(&f.remotes);
|
||||
f.state = NUD_STALE;
|
||||
memcpy(f.eth_addr, eth_addr, ETH_ALEN);
|
||||
|
||||
|
@ -377,17 +391,17 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
|
|||
static int vxlan_fdb_append(struct vxlan_fdb *f,
|
||||
__be32 ip, __be16 port, __u32 vni, __u32 ifindex)
|
||||
{
|
||||
struct vxlan_rdst *rd_prev, *rd;
|
||||
struct vxlan_rdst *rd;
|
||||
|
||||
rd_prev = NULL;
|
||||
for (rd = &f->remote; rd; rd = rd->remote_next) {
|
||||
/* protected by vxlan->hash_lock */
|
||||
list_for_each_entry(rd, &f->remotes, list) {
|
||||
if (rd->remote_ip == ip &&
|
||||
rd->remote_port == port &&
|
||||
rd->remote_vni == vni &&
|
||||
rd->remote_ifindex == ifindex)
|
||||
return 0;
|
||||
rd_prev = rd;
|
||||
}
|
||||
|
||||
rd = kmalloc(sizeof(*rd), GFP_ATOMIC);
|
||||
if (rd == NULL)
|
||||
return -ENOBUFS;
|
||||
|
@ -395,8 +409,9 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
|
|||
rd->remote_port = port;
|
||||
rd->remote_vni = vni;
|
||||
rd->remote_ifindex = ifindex;
|
||||
rd->remote_next = NULL;
|
||||
rd_prev->remote_next = rd;
|
||||
|
||||
list_add_tail_rcu(&rd->list, &f->remotes);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -448,16 +463,14 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
|
|||
return -ENOMEM;
|
||||
|
||||
notify = 1;
|
||||
f->remote.remote_ip = ip;
|
||||
f->remote.remote_port = port;
|
||||
f->remote.remote_vni = vni;
|
||||
f->remote.remote_ifindex = ifindex;
|
||||
f->remote.remote_next = NULL;
|
||||
f->state = state;
|
||||
f->flags = ndm_flags;
|
||||
f->updated = f->used = jiffies;
|
||||
INIT_LIST_HEAD(&f->remotes);
|
||||
memcpy(f->eth_addr, mac, ETH_ALEN);
|
||||
|
||||
vxlan_fdb_append(f, ip, port, vni, ifindex);
|
||||
|
||||
++vxlan->addrcnt;
|
||||
hlist_add_head_rcu(&f->hlist,
|
||||
vxlan_fdb_head(vxlan, mac));
|
||||
|
@ -472,13 +485,10 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
|
|||
static void vxlan_fdb_free(struct rcu_head *head)
|
||||
{
|
||||
struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu);
|
||||
struct vxlan_rdst *rd, *nd;
|
||||
|
||||
while (f->remote.remote_next) {
|
||||
struct vxlan_rdst *rd = f->remote.remote_next;
|
||||
|
||||
f->remote.remote_next = rd->remote_next;
|
||||
list_for_each_entry_safe(rd, nd, &f->remotes, list)
|
||||
kfree(rd);
|
||||
}
|
||||
kfree(f);
|
||||
}
|
||||
|
||||
|
@ -588,23 +598,24 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
|
|||
|
||||
hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) {
|
||||
struct vxlan_rdst *rd;
|
||||
for (rd = &f->remote; rd; rd = rd->remote_next) {
|
||||
if (idx < cb->args[0])
|
||||
goto skip;
|
||||
|
||||
if (idx < cb->args[0])
|
||||
goto skip;
|
||||
|
||||
list_for_each_entry_rcu(rd, &f->remotes, list) {
|
||||
err = vxlan_fdb_info(skb, vxlan, f,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq,
|
||||
RTM_NEWNEIGH,
|
||||
NLM_F_MULTI, rd);
|
||||
if (err < 0)
|
||||
break;
|
||||
skip:
|
||||
++idx;
|
||||
goto out;
|
||||
}
|
||||
skip:
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
@ -620,7 +631,9 @@ static bool vxlan_snoop(struct net_device *dev,
|
|||
|
||||
f = vxlan_find_mac(vxlan, src_mac);
|
||||
if (likely(f)) {
|
||||
if (likely(f->remote.remote_ip == src_ip))
|
||||
struct vxlan_rdst *rdst = first_remote(f);
|
||||
|
||||
if (likely(rdst->remote_ip == src_ip))
|
||||
return false;
|
||||
|
||||
/* Don't migrate static entries, drop packets */
|
||||
|
@ -630,9 +643,9 @@ static bool vxlan_snoop(struct net_device *dev,
|
|||
if (net_ratelimit())
|
||||
netdev_info(dev,
|
||||
"%pM migrated from %pI4 to %pI4\n",
|
||||
src_mac, &f->remote.remote_ip, &src_ip);
|
||||
src_mac, &rdst->remote_ip, &src_ip);
|
||||
|
||||
f->remote.remote_ip = src_ip;
|
||||
rdst->remote_ip = src_ip;
|
||||
f->updated = jiffies;
|
||||
vxlan_fdb_notify(vxlan, f, RTM_NEWNEIGH);
|
||||
} else {
|
||||
|
@ -866,7 +879,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
f = vxlan_find_mac(vxlan, n->ha);
|
||||
if (f && f->remote.remote_ip == htonl(INADDR_ANY)) {
|
||||
if (f && first_remote(f)->remote_ip == htonl(INADDR_ANY)) {
|
||||
/* bridge-local neighbor */
|
||||
neigh_release(n);
|
||||
goto out;
|
||||
|
@ -1165,17 +1178,17 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
(vxlan->flags & VXLAN_F_L2MISS) &&
|
||||
!is_multicast_ether_addr(eth->h_dest))
|
||||
vxlan_fdb_miss(vxlan, eth->h_dest);
|
||||
} else
|
||||
rdst0 = &f->remote;
|
||||
} else {
|
||||
rdst = rdst0 = first_remote(f);
|
||||
|
||||
/* if there are multiple destinations, send copies */
|
||||
list_for_each_entry_continue_rcu(rdst, &f->remotes, list) {
|
||||
struct sk_buff *skb1;
|
||||
|
||||
/* if there are multiple destinations, send copies */
|
||||
for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) {
|
||||
struct sk_buff *skb1;
|
||||
|
||||
skb1 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb1)
|
||||
vxlan_xmit_one(skb1, dev, rdst, did_rsc);
|
||||
skb1 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb1)
|
||||
vxlan_xmit_one(skb1, dev, rdst, did_rsc);
|
||||
}
|
||||
}
|
||||
|
||||
vxlan_xmit_one(skb, dev, rdst0, did_rsc);
|
||||
|
|
Loading…
Reference in a new issue