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

Commit 494fff56 authored by Yuval Mintz's avatar Yuval Mintz Committed by David S. Miller
Browse files

ipmr, ip6mr: Make mfc_cache a common structure



mfc_cache and mfc6_cache are almost identical - the main difference is
in the origin/group addresses and comparison-key. Make a common
structure encapsulating most of the multicast routing logic  - mr_mfc
and convert both ipmr and ip6mr into using it.

For easy conversion [casting, in this case] mr_mfc has to be the first
field inside every multicast routing abstraction utilizing it.

Signed-off-by: default avatarYuval Mintz <yuvalm@mellanox.com>
Acked-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0bbbf0e7
Loading
Loading
Loading
Loading
+11 −10
Original line number Diff line number Diff line
@@ -126,8 +126,8 @@ mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route)

	switch (mr_route->mr_table->proto) {
	case MLXSW_SP_L3_PROTO_IPV4:
		ivif = mr_route->mfc4->mfc_parent;
		return mr_route->mfc4->mfc_un.res.ttls[ivif] != 255;
		ivif = mr_route->mfc4->_c.mfc_parent;
		return mr_route->mfc4->_c.mfc_un.res.ttls[ivif] != 255;
	case MLXSW_SP_L3_PROTO_IPV6:
		/* fall through */
	default:
@@ -364,7 +364,7 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
	mr_route->mfc4 = mfc;
	mr_route->mr_table = mr_table;
	for (i = 0; i < MAXVIFS; i++) {
		if (mfc->mfc_un.res.ttls[i] != 255) {
		if (mfc->_c.mfc_un.res.ttls[i] != 255) {
			err = mlxsw_sp_mr_route_evif_link(mr_route,
							  &mr_table->vifs[i]);
			if (err)
@@ -374,7 +374,8 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table,
				mr_route->min_mtu = mr_table->vifs[i].dev->mtu;
		}
	}
	mlxsw_sp_mr_route_ivif_link(mr_route, &mr_table->vifs[mfc->mfc_parent]);
	mlxsw_sp_mr_route_ivif_link(mr_route,
				    &mr_table->vifs[mfc->_c.mfc_parent]);

	mr_route->route_action = mlxsw_sp_mr_route_action(mr_route);
	return mr_route;
@@ -418,9 +419,9 @@ static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route,
	switch (mr_route->mr_table->proto) {
	case MLXSW_SP_L3_PROTO_IPV4:
		if (offload)
			mr_route->mfc4->mfc_flags |= MFC_OFFLOAD;
			mr_route->mfc4->_c.mfc_flags |= MFC_OFFLOAD;
		else
			mr_route->mfc4->mfc_flags &= ~MFC_OFFLOAD;
			mr_route->mfc4->_c.mfc_flags &= ~MFC_OFFLOAD;
		break;
	case MLXSW_SP_L3_PROTO_IPV6:
		/* fall through */
@@ -943,10 +944,10 @@ static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp,

	switch (mr_route->mr_table->proto) {
	case MLXSW_SP_L3_PROTO_IPV4:
		if (mr_route->mfc4->mfc_un.res.pkt != packets)
			mr_route->mfc4->mfc_un.res.lastuse = jiffies;
		mr_route->mfc4->mfc_un.res.pkt = packets;
		mr_route->mfc4->mfc_un.res.bytes = bytes;
		if (mr_route->mfc4->_c.mfc_un.res.pkt != packets)
			mr_route->mfc4->_c.mfc_un.res.lastuse = jiffies;
		mr_route->mfc4->_c.mfc_un.res.pkt = packets;
		mr_route->mfc4->_c.mfc_un.res.bytes = bytes;
		break;
	case MLXSW_SP_L3_PROTO_IPV6:
		/* fall through */
+4 −41
Original line number Diff line number Diff line
@@ -81,28 +81,13 @@ struct mfc_cache_cmp_arg {

/**
 * struct mfc_cache - multicast routing entries
 * @mnode: rhashtable list
 * @_c: Common multicast routing information; has to be first [for casting]
 * @mfc_mcastgrp: destination multicast group address
 * @mfc_origin: source address
 * @cmparg: used for rhashtable comparisons
 * @mfc_parent: source interface (iif)
 * @mfc_flags: entry flags
 * @expires: unresolved entry expire time
 * @unresolved: unresolved cached skbs
 * @last_assert: time of last assert
 * @minvif: minimum VIF id
 * @maxvif: maximum VIF id
 * @bytes: bytes that have passed for this entry
 * @pkt: packets that have passed for this entry
 * @wrong_if: number of wrong source interface hits
 * @lastuse: time of last use of the group (traffic or update)
 * @ttls: OIF TTL threshold array
 * @refcount: reference count for this entry
 * @list: global entry list
 * @rcu: used for entry destruction
 */
struct mfc_cache {
	struct rhlist_head mnode;
	struct mr_mfc _c;
	union {
		struct {
			__be32 mfc_mcastgrp;
@@ -110,28 +95,6 @@ struct mfc_cache {
		};
		struct mfc_cache_cmp_arg cmparg;
	};
	vifi_t mfc_parent;
	int mfc_flags;

	union {
		struct {
			unsigned long expires;
			struct sk_buff_head unresolved;
		} unres;
		struct {
			unsigned long last_assert;
			int minvif;
			int maxvif;
			unsigned long bytes;
			unsigned long pkt;
			unsigned long wrong_if;
			unsigned long lastuse;
			unsigned char ttls[MAXVIFS];
			refcount_t refcount;
		} res;
	} mfc_un;
	struct list_head list;
	struct rcu_head	rcu;
};

struct mfc_entry_notifier_info {
@@ -155,12 +118,12 @@ static inline void ipmr_cache_free(struct mfc_cache *mfc_cache)

static inline void ipmr_cache_put(struct mfc_cache *c)
{
	if (refcount_dec_and_test(&c->mfc_un.res.refcount))
	if (refcount_dec_and_test(&c->_c.mfc_un.res.refcount))
		ipmr_cache_free(c);
}
static inline void ipmr_cache_hold(struct mfc_cache *c)
{
	refcount_inc(&c->mfc_un.res.refcount);
	refcount_inc(&c->_c.mfc_un.res.refcount);
}

#endif
+1 −22
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ struct mfc6_cache_cmp_arg {
};

struct mfc6_cache {
	struct rhlist_head mnode;
	struct mr_mfc _c;
	union {
		struct {
			struct in6_addr mf6c_mcastgrp;
@@ -79,27 +79,6 @@ struct mfc6_cache {
		};
		struct mfc6_cache_cmp_arg cmparg;
	};
	mifi_t mf6c_parent;			/* Source interface		*/
	int mfc_flags;				/* Flags on line		*/

	union {
		struct {
			unsigned long expires;
			struct sk_buff_head unresolved;	/* Unresolved buffers		*/
		} unres;
		struct {
			unsigned long last_assert;
			int minvif;
			int maxvif;
			unsigned long bytes;
			unsigned long pkt;
			unsigned long wrong_if;
			unsigned long lastuse;
			unsigned char ttls[MAXMIFS];	/* TTL thresholds		*/
		} res;
	} mfc_un;
	struct list_head list;
	struct rcu_head rcu;
};

#define MFC_STATIC		1
+45 −0
Original line number Diff line number Diff line
@@ -44,6 +44,51 @@ struct vif_device {

#define VIF_EXISTS(_mrt, _idx) (!!((_mrt)->vif_table[_idx].dev))

/**
 * struct mr_mfc - common multicast routing entries
 * @mnode: rhashtable list
 * @mfc_parent: source interface (iif)
 * @mfc_flags: entry flags
 * @expires: unresolved entry expire time
 * @unresolved: unresolved cached skbs
 * @last_assert: time of last assert
 * @minvif: minimum VIF id
 * @maxvif: maximum VIF id
 * @bytes: bytes that have passed for this entry
 * @pkt: packets that have passed for this entry
 * @wrong_if: number of wrong source interface hits
 * @lastuse: time of last use of the group (traffic or update)
 * @ttls: OIF TTL threshold array
 * @refcount: reference count for this entry
 * @list: global entry list
 * @rcu: used for entry destruction
 */
struct mr_mfc {
	struct rhlist_head mnode;
	unsigned short mfc_parent;
	int mfc_flags;

	union {
		struct {
			unsigned long expires;
			struct sk_buff_head unresolved;
		} unres;
		struct {
			unsigned long last_assert;
			int minvif;
			int maxvif;
			unsigned long bytes;
			unsigned long pkt;
			unsigned long wrong_if;
			unsigned long lastuse;
			unsigned char ttls[MAXVIFS];
			refcount_t refcount;
		} res;
	} mfc_un;
	struct list_head list;
	struct rcu_head	rcu;
};

/**
 * struct mr_table - a multicast routing table
 * @list: entry within a list of multicast routing tables
+125 −108
Original line number Diff line number Diff line
@@ -106,7 +106,7 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
static int ipmr_cache_report(struct mr_table *mrt,
			     struct sk_buff *pkt, vifi_t vifi, int assert);
static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
			      struct mfc_cache *c, struct rtmsg *rtm);
			      struct mr_mfc *c, struct rtmsg *rtm);
static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
				 int cmd);
static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt);
@@ -343,7 +343,7 @@ static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg,
}

static const struct rhashtable_params ipmr_rht_params = {
	.head_offset = offsetof(struct mfc_cache, mnode),
	.head_offset = offsetof(struct mr_mfc, mnode),
	.key_offset = offsetof(struct mfc_cache, cmparg),
	.key_len = sizeof(struct mfc_cache_cmp_arg),
	.nelem_hint = 3,
@@ -752,14 +752,14 @@ static int vif_delete(struct mr_table *mrt, int vifi, int notify,

static void ipmr_cache_free_rcu(struct rcu_head *head)
{
	struct mfc_cache *c = container_of(head, struct mfc_cache, rcu);
	struct mr_mfc *c = container_of(head, struct mr_mfc, rcu);

	kmem_cache_free(mrt_cachep, c);
	kmem_cache_free(mrt_cachep, (struct mfc_cache *)c);
}

void ipmr_cache_free(struct mfc_cache *c)
{
	call_rcu(&c->rcu, ipmr_cache_free_rcu);
	call_rcu(&c->_c.rcu, ipmr_cache_free_rcu);
}
EXPORT_SYMBOL(ipmr_cache_free);

@@ -774,7 +774,7 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)

	atomic_dec(&mrt->cache_resolve_queue_len);

	while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
	while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved))) {
		if (ip_hdr(skb)->version == 0) {
			struct nlmsghdr *nlh = skb_pull(skb,
							sizeof(struct iphdr));
@@ -798,9 +798,9 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
static void ipmr_expire_process(struct timer_list *t)
{
	struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer);
	unsigned long now;
	struct mr_mfc *c, *next;
	unsigned long expires;
	struct mfc_cache *c, *next;
	unsigned long now;

	if (!spin_trylock(&mfc_unres_lock)) {
		mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10);
@@ -822,8 +822,8 @@ static void ipmr_expire_process(struct timer_list *t)
		}

		list_del(&c->list);
		mroute_netlink_event(mrt, c, RTM_DELROUTE);
		ipmr_destroy_unres(mrt, c);
		mroute_netlink_event(mrt, (struct mfc_cache *)c, RTM_DELROUTE);
		ipmr_destroy_unres(mrt, (struct mfc_cache *)c);
	}

	if (!list_empty(&mrt->mfc_unres_queue))
@@ -834,7 +834,7 @@ static void ipmr_expire_process(struct timer_list *t)
}

/* Fill oifs list. It is called under write locked mrt_lock. */
static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
static void ipmr_update_thresholds(struct mr_table *mrt, struct mr_mfc *cache,
				   unsigned char *ttls)
{
	int vifi;
@@ -974,11 +974,11 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
			.mfc_origin = origin
	};
	struct rhlist_head *tmp, *list;
	struct mfc_cache *c;
	struct mr_mfc *c;

	list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
	rhl_for_each_entry_rcu(c, tmp, list, mnode)
		return c;
		return (struct mfc_cache *)c;

	return NULL;
}
@@ -992,12 +992,12 @@ static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt,
			.mfc_origin = htonl(INADDR_ANY)
	};
	struct rhlist_head *tmp, *list;
	struct mfc_cache *c;
	struct mr_mfc *c;

	list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
	rhl_for_each_entry_rcu(c, tmp, list, mnode)
		if (c->mfc_un.res.ttls[vifi] < 255)
			return c;
			return (struct mfc_cache *)c;

	return NULL;
}
@@ -1011,20 +1011,22 @@ static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt,
			.mfc_origin = htonl(INADDR_ANY)
	};
	struct rhlist_head *tmp, *list;
	struct mfc_cache *c, *proxy;
	struct mr_mfc *c;

	if (mcastgrp == htonl(INADDR_ANY))
		goto skip;

	list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
	rhl_for_each_entry_rcu(c, tmp, list, mnode) {
		struct mfc_cache *proxy;

		if (c->mfc_un.res.ttls[vifi] < 255)
			return c;
			return (struct mfc_cache *)c;

		/* It's ok if the vifi is part of the static tree */
		proxy = ipmr_cache_find_any_parent(mrt, c->mfc_parent);
		if (proxy && proxy->mfc_un.res.ttls[vifi] < 255)
			return c;
		if (proxy && proxy->_c.mfc_un.res.ttls[vifi] < 255)
			return (struct mfc_cache *)c;
	}

skip:
@@ -1041,12 +1043,12 @@ static struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt,
			.mfc_origin = origin,
	};
	struct rhlist_head *tmp, *list;
	struct mfc_cache *c;
	struct mr_mfc *c;

	list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params);
	rhl_for_each_entry_rcu(c, tmp, list, mnode)
		if (parent == -1 || parent == c->mfc_parent)
			return c;
			return (struct mfc_cache *)c;

	return NULL;
}
@@ -1057,9 +1059,9 @@ static struct mfc_cache *ipmr_cache_alloc(void)
	struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);

	if (c) {
		c->mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
		c->mfc_un.res.minvif = MAXVIFS;
		refcount_set(&c->mfc_un.res.refcount, 1);
		c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
		c->_c.mfc_un.res.minvif = MAXVIFS;
		refcount_set(&c->_c.mfc_un.res.refcount, 1);
	}
	return c;
}
@@ -1069,8 +1071,8 @@ static struct mfc_cache *ipmr_cache_alloc_unres(void)
	struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);

	if (c) {
		skb_queue_head_init(&c->mfc_un.unres.unresolved);
		c->mfc_un.unres.expires = jiffies + 10*HZ;
		skb_queue_head_init(&c->_c.mfc_un.unres.unresolved);
		c->_c.mfc_un.unres.expires = jiffies + 10 * HZ;
	}
	return c;
}
@@ -1083,12 +1085,13 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
	struct nlmsgerr *e;

	/* Play the pending entries through our router */
	while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
	while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) {
		if (ip_hdr(skb)->version == 0) {
			struct nlmsghdr *nlh = skb_pull(skb,
							sizeof(struct iphdr));

			if (__ipmr_fill_mroute(mrt, skb, c, nlmsg_data(nlh)) > 0) {
			if (__ipmr_fill_mroute(mrt, skb, &c->_c,
					       nlmsg_data(nlh)) > 0) {
				nlh->nlmsg_len = skb_tail_pointer(skb) -
						 (u8 *)nlh;
			} else {
@@ -1196,7 +1199,7 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
	int err;

	spin_lock_bh(&mfc_unres_lock);
	list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
	list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) {
		if (c->mfc_mcastgrp == iph->daddr &&
		    c->mfc_origin == iph->saddr) {
			found = true;
@@ -1215,12 +1218,13 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
		}

		/* Fill in the new cache entry */
		c->mfc_parent	= -1;
		c->_c.mfc_parent = -1;
		c->mfc_origin	= iph->saddr;
		c->mfc_mcastgrp	= iph->daddr;

		/* Reflect first query at mrouted. */
		err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);

		if (err < 0) {
			/* If the report failed throw the cache entry
			   out - Brad Parker
@@ -1233,15 +1237,16 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
		}

		atomic_inc(&mrt->cache_resolve_queue_len);
		list_add(&c->list, &mrt->mfc_unres_queue);
		list_add(&c->_c.list, &mrt->mfc_unres_queue);
		mroute_netlink_event(mrt, c, RTM_NEWROUTE);

		if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
			mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
			mod_timer(&mrt->ipmr_expire_timer,
				  c->_c.mfc_un.unres.expires);
	}

	/* See if we can append the packet */
	if (c->mfc_un.unres.unresolved.qlen > 3) {
	if (c->_c.mfc_un.unres.unresolved.qlen > 3) {
		kfree_skb(skb);
		err = -ENOBUFS;
	} else {
@@ -1249,7 +1254,7 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
			skb->dev = dev;
			skb->skb_iif = dev->ifindex;
		}
		skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
		skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb);
		err = 0;
	}

@@ -1271,8 +1276,8 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
	rcu_read_unlock();
	if (!c)
		return -ENOENT;
	rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params);
	list_del_rcu(&c->list);
	rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ipmr_rht_params);
	list_del_rcu(&c->_c.list);
	call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id);
	mroute_netlink_event(mrt, c, RTM_DELROUTE);
	ipmr_cache_put(c);
@@ -1284,6 +1289,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
			struct mfcctl *mfc, int mrtsock, int parent)
{
	struct mfc_cache *uc, *c;
	struct mr_mfc *_uc;
	bool found;
	int ret;

@@ -1297,10 +1303,10 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
	rcu_read_unlock();
	if (c) {
		write_lock_bh(&mrt_lock);
		c->mfc_parent = mfc->mfcc_parent;
		ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
		c->_c.mfc_parent = mfc->mfcc_parent;
		ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls);
		if (!mrtsock)
			c->mfc_flags |= MFC_STATIC;
			c->_c.mfc_flags |= MFC_STATIC;
		write_unlock_bh(&mrt_lock);
		call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c,
					      mrt->id);
@@ -1318,28 +1324,29 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,

	c->mfc_origin = mfc->mfcc_origin.s_addr;
	c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
	c->mfc_parent = mfc->mfcc_parent;
	ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
	c->_c.mfc_parent = mfc->mfcc_parent;
	ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls);
	if (!mrtsock)
		c->mfc_flags |= MFC_STATIC;
		c->_c.mfc_flags |= MFC_STATIC;

	ret = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->mnode,
	ret = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode,
				  ipmr_rht_params);
	if (ret) {
		pr_err("ipmr: rhtable insert error %d\n", ret);
		ipmr_cache_free(c);
		return ret;
	}
	list_add_tail_rcu(&c->list, &mrt->mfc_cache_list);
	list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list);
	/* Check to see if we resolved a queued list. If so we
	 * need to send on the frames and tidy up.
	 */
	found = false;
	spin_lock_bh(&mfc_unres_lock);
	list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
	list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) {
		uc = (struct mfc_cache *)_uc;
		if (uc->mfc_origin == c->mfc_origin &&
		    uc->mfc_mcastgrp == c->mfc_mcastgrp) {
			list_del(&uc->list);
			list_del(&_uc->list);
			atomic_dec(&mrt->cache_resolve_queue_len);
			found = true;
			break;
@@ -1362,7 +1369,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
static void mroute_clean_tables(struct mr_table *mrt, bool all)
{
	struct net *net = read_pnet(&mrt->net);
	struct mfc_cache *c, *tmp;
	struct mr_mfc *c, *tmp;
	struct mfc_cache *cache;
	LIST_HEAD(list);
	int i;

@@ -1380,18 +1388,20 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
			continue;
		rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params);
		list_del_rcu(&c->list);
		call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c,
		cache = (struct mfc_cache *)c;
		call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache,
					      mrt->id);
		mroute_netlink_event(mrt, c, RTM_DELROUTE);
		ipmr_cache_put(c);
		mroute_netlink_event(mrt, cache, RTM_DELROUTE);
		ipmr_cache_put(cache);
	}

	if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
		spin_lock_bh(&mfc_unres_lock);
		list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
			list_del(&c->list);
			mroute_netlink_event(mrt, c, RTM_DELROUTE);
			ipmr_destroy_unres(mrt, c);
			cache = (struct mfc_cache *)c;
			mroute_netlink_event(mrt, cache, RTM_DELROUTE);
			ipmr_destroy_unres(mrt, cache);
		}
		spin_unlock_bh(&mfc_unres_lock);
	}
@@ -1683,9 +1693,9 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
		rcu_read_lock();
		c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
		if (c) {
			sr.pktcnt = c->mfc_un.res.pkt;
			sr.bytecnt = c->mfc_un.res.bytes;
			sr.wrong_if = c->mfc_un.res.wrong_if;
			sr.pktcnt = c->_c.mfc_un.res.pkt;
			sr.bytecnt = c->_c.mfc_un.res.bytes;
			sr.wrong_if = c->_c.mfc_un.res.wrong_if;
			rcu_read_unlock();

			if (copy_to_user(arg, &sr, sizeof(sr)))
@@ -1757,9 +1767,9 @@ int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
		rcu_read_lock();
		c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
		if (c) {
			sr.pktcnt = c->mfc_un.res.pkt;
			sr.bytecnt = c->mfc_un.res.bytes;
			sr.wrong_if = c->mfc_un.res.wrong_if;
			sr.pktcnt = c->_c.mfc_un.res.pkt;
			sr.bytecnt = c->_c.mfc_un.res.bytes;
			sr.wrong_if = c->_c.mfc_un.res.wrong_if;
			rcu_read_unlock();

			if (copy_to_user(arg, &sr, sizeof(sr)))
@@ -1983,18 +1993,18 @@ static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
/* "local" means that we should preserve one skb (for local delivery) */
static void ip_mr_forward(struct net *net, struct mr_table *mrt,
			  struct net_device *dev, struct sk_buff *skb,
			  struct mfc_cache *cache, int local)
			  struct mfc_cache *c, int local)
{
	int true_vifi = ipmr_find_vif(mrt, dev);
	int psend = -1;
	int vif, ct;

	vif = cache->mfc_parent;
	cache->mfc_un.res.pkt++;
	cache->mfc_un.res.bytes += skb->len;
	cache->mfc_un.res.lastuse = jiffies;
	vif = c->_c.mfc_parent;
	c->_c.mfc_un.res.pkt++;
	c->_c.mfc_un.res.bytes += skb->len;
	c->_c.mfc_un.res.lastuse = jiffies;

	if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) {
	if (c->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) {
		struct mfc_cache *cache_proxy;

		/* For an (*,G) entry, we only check that the incomming
@@ -2002,7 +2012,7 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
		 */
		cache_proxy = ipmr_cache_find_any_parent(mrt, vif);
		if (cache_proxy &&
		    cache_proxy->mfc_un.res.ttls[true_vifi] < 255)
		    cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255)
			goto forward;
	}

@@ -2023,7 +2033,7 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
			goto dont_forward;
		}

		cache->mfc_un.res.wrong_if++;
		c->_c.mfc_un.res.wrong_if++;

		if (true_vifi >= 0 && mrt->mroute_do_assert &&
		    /* pimsm uses asserts, when switching from RPT to SPT,
@@ -2032,10 +2042,11 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
		     * large chunk of pimd to kernel. Ough... --ANK
		     */
		    (mrt->mroute_do_pim ||
		     cache->mfc_un.res.ttls[true_vifi] < 255) &&
		     c->_c.mfc_un.res.ttls[true_vifi] < 255) &&
		    time_after(jiffies,
			       cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
			cache->mfc_un.res.last_assert = jiffies;
			       c->_c.mfc_un.res.last_assert +
			       MFC_ASSERT_THRESH)) {
			c->_c.mfc_un.res.last_assert = jiffies;
			ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
		}
		goto dont_forward;
@@ -2046,33 +2057,33 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
	mrt->vif_table[vif].bytes_in += skb->len;

	/* Forward the frame */
	if (cache->mfc_origin == htonl(INADDR_ANY) &&
	    cache->mfc_mcastgrp == htonl(INADDR_ANY)) {
	if (c->mfc_origin == htonl(INADDR_ANY) &&
	    c->mfc_mcastgrp == htonl(INADDR_ANY)) {
		if (true_vifi >= 0 &&
		    true_vifi != cache->mfc_parent &&
		    true_vifi != c->_c.mfc_parent &&
		    ip_hdr(skb)->ttl >
				cache->mfc_un.res.ttls[cache->mfc_parent]) {
				c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) {
			/* It's an (*,*) entry and the packet is not coming from
			 * the upstream: forward the packet to the upstream
			 * only.
			 */
			psend = cache->mfc_parent;
			psend = c->_c.mfc_parent;
			goto last_forward;
		}
		goto dont_forward;
	}
	for (ct = cache->mfc_un.res.maxvif - 1;
	     ct >= cache->mfc_un.res.minvif; ct--) {
	for (ct = c->_c.mfc_un.res.maxvif - 1;
	     ct >= c->_c.mfc_un.res.minvif; ct--) {
		/* For (*,G) entry, don't forward to the incoming interface */
		if ((cache->mfc_origin != htonl(INADDR_ANY) ||
		if ((c->mfc_origin != htonl(INADDR_ANY) ||
		     ct != true_vifi) &&
		    ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
		    ip_hdr(skb)->ttl > c->_c.mfc_un.res.ttls[ct]) {
			if (psend != -1) {
				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);

				if (skb2)
					ipmr_queue_xmit(net, mrt, true_vifi,
							skb2, cache, psend);
							skb2, c, psend);
			}
			psend = ct;
		}
@@ -2084,9 +2095,9 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,

			if (skb2)
				ipmr_queue_xmit(net, mrt, true_vifi, skb2,
						cache, psend);
						c, psend);
		} else {
			ipmr_queue_xmit(net, mrt, true_vifi, skb, cache, psend);
			ipmr_queue_xmit(net, mrt, true_vifi, skb, c, psend);
			return;
		}
	}
@@ -2285,7 +2296,7 @@ static int pim_rcv(struct sk_buff *skb)
#endif

static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
			      struct mfc_cache *c, struct rtmsg *rtm)
			      struct mr_mfc *c, struct rtmsg *rtm)
{
	struct rta_mfc_stats mfcs;
	struct nlattr *mp_attr;
@@ -2401,7 +2412,7 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
	}

	read_lock(&mrt_lock);
	err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
	err = __ipmr_fill_mroute(mrt, skb, &cache->_c, rtm);
	read_unlock(&mrt_lock);
	rcu_read_unlock();
	return err;
@@ -2429,7 +2440,7 @@ static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
		goto nla_put_failure;
	rtm->rtm_type     = RTN_MULTICAST;
	rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
	if (c->mfc_flags & MFC_STATIC)
	if (c->_c.mfc_flags & MFC_STATIC)
		rtm->rtm_protocol = RTPROT_STATIC;
	else
		rtm->rtm_protocol = RTPROT_MROUTED;
@@ -2438,7 +2449,7 @@ static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
	if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) ||
	    nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp))
		goto nla_put_failure;
	err = __ipmr_fill_mroute(mrt, skb, c, rtm);
	err = __ipmr_fill_mroute(mrt, skb, &c->_c, rtm);
	/* do not break the dump if cache is unresolved */
	if (err < 0 && err != -ENOENT)
		goto nla_put_failure;
@@ -2479,7 +2490,8 @@ static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
	struct sk_buff *skb;
	int err = -ENOBUFS;

	skb = nlmsg_new(mroute_msgsize(mfc->mfc_parent >= MAXVIFS, mrt->maxvif),
	skb = nlmsg_new(mroute_msgsize(mfc->_c.mfc_parent >= MAXVIFS,
				       mrt->maxvif),
			GFP_ATOMIC);
	if (!skb)
		goto errout;
@@ -2624,10 +2636,10 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct net *net = sock_net(skb->sk);
	struct mr_table *mrt;
	struct mfc_cache *mfc;
	unsigned int t = 0, s_t;
	unsigned int e = 0, s_e;
	struct mr_table *mrt;
	struct mr_mfc *mfc;

	s_t = cb->args[0];
	s_e = cb->args[1];
@@ -2642,8 +2654,8 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
			if (ipmr_fill_mroute(mrt, skb,
					     NETLINK_CB(cb->skb).portid,
					     cb->nlh->nlmsg_seq,
					     mfc, RTM_NEWROUTE,
					     NLM_F_MULTI) < 0)
					     (struct mfc_cache *)mfc,
					     RTM_NEWROUTE, NLM_F_MULTI) < 0)
				goto done;
next_entry:
			e++;
@@ -2658,8 +2670,8 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
			if (ipmr_fill_mroute(mrt, skb,
					     NETLINK_CB(cb->skb).portid,
					     cb->nlh->nlmsg_seq,
					     mfc, RTM_NEWROUTE,
					     NLM_F_MULTI) < 0) {
					     (struct mfc_cache *)mfc,
					     RTM_NEWROUTE, NLM_F_MULTI) < 0) {
				spin_unlock_bh(&mfc_unres_lock);
				goto done;
			}
@@ -3051,20 +3063,21 @@ static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
					  struct ipmr_mfc_iter *it, loff_t pos)
{
	struct mr_table *mrt = it->mrt;
	struct mfc_cache *mfc;
	struct mr_mfc *mfc;

	rcu_read_lock();
	it->cache = &mrt->mfc_cache_list;
	list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list)
		if (pos-- == 0)
			return mfc;
			return (struct mfc_cache *)mfc;
	rcu_read_unlock();

	spin_lock_bh(&mfc_unres_lock);
	it->cache = &mrt->mfc_unres_queue;
	list_for_each_entry(mfc, it->cache, list)
		if (pos-- == 0)
			return mfc;
			return (struct mfc_cache *)mfc;

	spin_unlock_bh(&mfc_unres_lock);

	it->cache = NULL;
@@ -3100,8 +3113,9 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
	if (v == SEQ_START_TOKEN)
		return ipmr_mfc_seq_idx(net, seq->private, 0);

	if (mfc->list.next != it->cache)
		return list_entry(mfc->list.next, struct mfc_cache, list);
	if (mfc->_c.list.next != it->cache)
		return (struct mfc_cache *)(list_entry(mfc->_c.list.next,
						       struct mr_mfc, list));

	if (it->cache == &mrt->mfc_unres_queue)
		goto end_of_list;
@@ -3112,7 +3126,9 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)

	spin_lock_bh(&mfc_unres_lock);
	if (!list_empty(it->cache))
		return list_first_entry(it->cache, struct mfc_cache, list);
		return (struct mfc_cache *)(list_first_entry(it->cache,
							     struct mr_mfc,
							     list));

end_of_list:
	spin_unlock_bh(&mfc_unres_lock);
@@ -3147,20 +3163,20 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
		seq_printf(seq, "%08X %08X %-3hd",
			   (__force u32) mfc->mfc_mcastgrp,
			   (__force u32) mfc->mfc_origin,
			   mfc->mfc_parent);
			   mfc->_c.mfc_parent);

		if (it->cache != &mrt->mfc_unres_queue) {
			seq_printf(seq, " %8lu %8lu %8lu",
				   mfc->mfc_un.res.pkt,
				   mfc->mfc_un.res.bytes,
				   mfc->mfc_un.res.wrong_if);
			for (n = mfc->mfc_un.res.minvif;
			     n < mfc->mfc_un.res.maxvif; n++) {
				   mfc->_c.mfc_un.res.pkt,
				   mfc->_c.mfc_un.res.bytes,
				   mfc->_c.mfc_un.res.wrong_if);
			for (n = mfc->_c.mfc_un.res.minvif;
			     n < mfc->_c.mfc_un.res.maxvif; n++) {
				if (VIF_EXISTS(mrt, n) &&
				    mfc->mfc_un.res.ttls[n] < 255)
				    mfc->_c.mfc_un.res.ttls[n] < 255)
					seq_printf(seq,
					   " %2d:%-3d",
					   n, mfc->mfc_un.res.ttls[n]);
					   n, mfc->_c.mfc_un.res.ttls[n]);
			}
		} else {
			/* unresolved mfc_caches don't contain
@@ -3219,7 +3235,7 @@ static int ipmr_dump(struct net *net, struct notifier_block *nb)

	ipmr_for_each_table(mrt, net) {
		struct vif_device *v = &mrt->vif_table[0];
		struct mfc_cache *mfc;
		struct mr_mfc *mfc;
		int vifi;

		/* Notifiy on table VIF entries */
@@ -3236,7 +3252,8 @@ static int ipmr_dump(struct net *net, struct notifier_block *nb)
		/* Notify on table MFC entries */
		list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list)
			call_ipmr_mfc_entry_notifier(nb, net,
						     FIB_EVENT_ENTRY_ADD, mfc,
						     FIB_EVENT_ENTRY_ADD,
						     (struct mfc_cache *)mfc,
						     mrt->id);
	}

Loading