Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e61aa8f authored by Stephen Hemminger's avatar Stephen Hemminger
Browse files

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: default avatarStephen Hemminger <stephen@networkplumber.org>
parent 4ad16930
Loading
Loading
Loading
Loading
+55 −42
Original line number Diff line number Diff line
@@ -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;

			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;
					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,18 +1178,18 @@ 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 */
	for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) {
		list_for_each_entry_continue_rcu(rdst, &f->remotes, list) {
			struct sk_buff *skb1;

			skb1 = skb_clone(skb, GFP_ATOMIC);
			if (skb1)
				vxlan_xmit_one(skb1, dev, rdst, did_rsc);
		}
	}

	vxlan_xmit_one(skb, dev, rdst0, did_rsc);
	return NETDEV_TX_OK;