mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: sched: introduce helpers to work with filter chains
Introduce struct tcf_chain object and set of helpers around it. Wraps up insertion, deletion and search in the filter chain. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									7961973a00
								
							
						
					
					
						commit
						2190d1d094
					
				
					 2 changed files with 113 additions and 42 deletions
				
			
		| 
						 | 
					@ -248,10 +248,15 @@ struct qdisc_skb_cb {
 | 
				
			||||||
	unsigned char		data[QDISC_CB_PRIV_LEN];
 | 
						unsigned char		data[QDISC_CB_PRIV_LEN];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct tcf_block {
 | 
					struct tcf_chain {
 | 
				
			||||||
 | 
						struct tcf_proto __rcu *filter_chain;
 | 
				
			||||||
	struct tcf_proto __rcu **p_filter_chain;
 | 
						struct tcf_proto __rcu **p_filter_chain;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tcf_block {
 | 
				
			||||||
 | 
						struct tcf_chain *chain;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
 | 
					static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct qdisc_skb_cb *qcb;
 | 
						struct qdisc_skb_cb *qcb;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,13 +106,12 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
 | 
					static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
 | 
				
			||||||
				 struct nlmsghdr *n,
 | 
									 struct nlmsghdr *n,
 | 
				
			||||||
				 struct tcf_proto __rcu **chain, int event)
 | 
									 struct tcf_chain *chain, int event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tcf_proto __rcu **it_chain;
 | 
					 | 
				
			||||||
	struct tcf_proto *tp;
 | 
						struct tcf_proto *tp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (it_chain = chain; (tp = rtnl_dereference(*it_chain)) != NULL;
 | 
						for (tp = rtnl_dereference(chain->filter_chain);
 | 
				
			||||||
	     it_chain = &tp->next)
 | 
						     tp; tp = rtnl_dereference(tp->next))
 | 
				
			||||||
		tfilter_notify(net, oskb, n, tp, 0, event, false);
 | 
							tfilter_notify(net, oskb, n, tp, 0, event, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -187,26 +186,49 @@ static void tcf_proto_destroy(struct tcf_proto *tp)
 | 
				
			||||||
	kfree_rcu(tp, rcu);
 | 
						kfree_rcu(tp, rcu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tcf_chain_destroy(struct tcf_proto __rcu **fl)
 | 
					static struct tcf_chain *tcf_chain_create(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return kzalloc(sizeof(struct tcf_chain), GFP_KERNEL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tcf_chain_destroy(struct tcf_chain *chain)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tcf_proto *tp;
 | 
						struct tcf_proto *tp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while ((tp = rtnl_dereference(*fl)) != NULL) {
 | 
						while ((tp = rtnl_dereference(chain->filter_chain)) != NULL) {
 | 
				
			||||||
		RCU_INIT_POINTER(*fl, tp->next);
 | 
							RCU_INIT_POINTER(chain->filter_chain, tp->next);
 | 
				
			||||||
		tcf_proto_destroy(tp);
 | 
							tcf_proto_destroy(tp);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						kfree(chain);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					tcf_chain_filter_chain_ptr_set(struct tcf_chain *chain,
 | 
				
			||||||
 | 
								       struct tcf_proto __rcu **p_filter_chain)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						chain->p_filter_chain = p_filter_chain;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int tcf_block_get(struct tcf_block **p_block,
 | 
					int tcf_block_get(struct tcf_block **p_block,
 | 
				
			||||||
		  struct tcf_proto __rcu **p_filter_chain)
 | 
							  struct tcf_proto __rcu **p_filter_chain)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
 | 
						struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!block)
 | 
						if (!block)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	block->p_filter_chain = p_filter_chain;
 | 
						block->chain = tcf_chain_create();
 | 
				
			||||||
 | 
						if (!block->chain) {
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
							goto err_chain_create;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tcf_chain_filter_chain_ptr_set(block->chain, p_filter_chain);
 | 
				
			||||||
	*p_block = block;
 | 
						*p_block = block;
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_chain_create:
 | 
				
			||||||
 | 
						kfree(block);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(tcf_block_get);
 | 
					EXPORT_SYMBOL(tcf_block_get);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -214,7 +236,7 @@ void tcf_block_put(struct tcf_block *block)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!block)
 | 
						if (!block)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	tcf_chain_destroy(block->p_filter_chain);
 | 
						tcf_chain_destroy(block->chain);
 | 
				
			||||||
	kfree(block);
 | 
						kfree(block);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(tcf_block_put);
 | 
					EXPORT_SYMBOL(tcf_block_put);
 | 
				
			||||||
| 
						 | 
					@ -267,6 +289,65 @@ int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(tcf_classify);
 | 
					EXPORT_SYMBOL(tcf_classify);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tcf_chain_info {
 | 
				
			||||||
 | 
						struct tcf_proto __rcu **pprev;
 | 
				
			||||||
 | 
						struct tcf_proto __rcu *next;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain_info *chain_info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return rtnl_dereference(*chain_info->pprev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tcf_chain_tp_insert(struct tcf_chain *chain,
 | 
				
			||||||
 | 
									struct tcf_chain_info *chain_info,
 | 
				
			||||||
 | 
									struct tcf_proto *tp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (chain->p_filter_chain &&
 | 
				
			||||||
 | 
						    *chain_info->pprev == chain->filter_chain)
 | 
				
			||||||
 | 
							*chain->p_filter_chain = tp;
 | 
				
			||||||
 | 
						RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain_info));
 | 
				
			||||||
 | 
						rcu_assign_pointer(*chain_info->pprev, tp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tcf_chain_tp_remove(struct tcf_chain *chain,
 | 
				
			||||||
 | 
									struct tcf_chain_info *chain_info,
 | 
				
			||||||
 | 
									struct tcf_proto *tp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct tcf_proto *next = rtnl_dereference(chain_info->next);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (chain->p_filter_chain && tp == chain->filter_chain)
 | 
				
			||||||
 | 
							*chain->p_filter_chain = next;
 | 
				
			||||||
 | 
						RCU_INIT_POINTER(*chain_info->pprev, next);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
 | 
				
			||||||
 | 
										   struct tcf_chain_info *chain_info,
 | 
				
			||||||
 | 
										   u32 protocol, u32 prio,
 | 
				
			||||||
 | 
										   bool prio_allocate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct tcf_proto **pprev;
 | 
				
			||||||
 | 
						struct tcf_proto *tp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check the chain for existence of proto-tcf with this priority */
 | 
				
			||||||
 | 
						for (pprev = &chain->filter_chain;
 | 
				
			||||||
 | 
						     (tp = rtnl_dereference(*pprev)); pprev = &tp->next) {
 | 
				
			||||||
 | 
							if (tp->prio >= prio) {
 | 
				
			||||||
 | 
								if (tp->prio == prio) {
 | 
				
			||||||
 | 
									if (prio_allocate ||
 | 
				
			||||||
 | 
									    (tp->protocol != protocol && protocol))
 | 
				
			||||||
 | 
										return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									tp = NULL;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						chain_info->pprev = pprev;
 | 
				
			||||||
 | 
						chain_info->next = tp ? tp->next : NULL;
 | 
				
			||||||
 | 
						return tp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Add/change/delete/get a filter node */
 | 
					/* Add/change/delete/get a filter node */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
					static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
| 
						 | 
					@ -281,10 +362,9 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
	u32 parent;
 | 
						u32 parent;
 | 
				
			||||||
	struct net_device *dev;
 | 
						struct net_device *dev;
 | 
				
			||||||
	struct Qdisc  *q;
 | 
						struct Qdisc  *q;
 | 
				
			||||||
	struct tcf_proto __rcu **back;
 | 
						struct tcf_chain_info chain_info;
 | 
				
			||||||
	struct tcf_proto __rcu **chain;
 | 
						struct tcf_chain *chain;
 | 
				
			||||||
	struct tcf_block *block;
 | 
						struct tcf_block *block;
 | 
				
			||||||
	struct tcf_proto *next;
 | 
					 | 
				
			||||||
	struct tcf_proto *tp;
 | 
						struct tcf_proto *tp;
 | 
				
			||||||
	const struct Qdisc_class_ops *cops;
 | 
						const struct Qdisc_class_ops *cops;
 | 
				
			||||||
	unsigned long cl;
 | 
						unsigned long cl;
 | 
				
			||||||
| 
						 | 
					@ -369,7 +449,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
		err = -EINVAL;
 | 
							err = -EINVAL;
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	chain = block->p_filter_chain;
 | 
						chain = block->chain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
 | 
						if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
 | 
				
			||||||
		tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
 | 
							tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
 | 
				
			||||||
| 
						 | 
					@ -378,22 +458,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check the chain for existence of proto-tcf with this priority */
 | 
						tp = tcf_chain_tp_find(chain, &chain_info, protocol,
 | 
				
			||||||
	for (back = chain;
 | 
								       prio, prio_allocate);
 | 
				
			||||||
	     (tp = rtnl_dereference(*back)) != NULL;
 | 
						if (IS_ERR(tp)) {
 | 
				
			||||||
	     back = &tp->next) {
 | 
							err = PTR_ERR(tp);
 | 
				
			||||||
		if (tp->prio >= prio) {
 | 
							goto errout;
 | 
				
			||||||
			if (tp->prio == prio) {
 | 
					 | 
				
			||||||
				if (prio_allocate ||
 | 
					 | 
				
			||||||
				    (tp->protocol != protocol && protocol)) {
 | 
					 | 
				
			||||||
					err = -EINVAL;
 | 
					 | 
				
			||||||
					goto errout;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				tp = NULL;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tp == NULL) {
 | 
						if (tp == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -411,7 +480,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (prio_allocate)
 | 
							if (prio_allocate)
 | 
				
			||||||
			prio = tcf_auto_prio(rtnl_dereference(*back));
 | 
								prio = tcf_auto_prio(tcf_chain_tp_prev(&chain_info));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tp = tcf_proto_create(nla_data(tca[TCA_KIND]),
 | 
							tp = tcf_proto_create(nla_data(tca[TCA_KIND]),
 | 
				
			||||||
				      protocol, prio, parent, q, block);
 | 
									      protocol, prio, parent, q, block);
 | 
				
			||||||
| 
						 | 
					@ -429,8 +498,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fh == 0) {
 | 
						if (fh == 0) {
 | 
				
			||||||
		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
 | 
							if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
 | 
				
			||||||
			next = rtnl_dereference(tp->next);
 | 
								tcf_chain_tp_remove(chain, &chain_info, tp);
 | 
				
			||||||
			RCU_INIT_POINTER(*back, next);
 | 
					 | 
				
			||||||
			tfilter_notify(net, skb, n, tp, fh,
 | 
								tfilter_notify(net, skb, n, tp, fh,
 | 
				
			||||||
				       RTM_DELTFILTER, false);
 | 
									       RTM_DELTFILTER, false);
 | 
				
			||||||
			tcf_proto_destroy(tp);
 | 
								tcf_proto_destroy(tp);
 | 
				
			||||||
| 
						 | 
					@ -459,11 +527,10 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
			err = tp->ops->delete(tp, fh, &last);
 | 
								err = tp->ops->delete(tp, fh, &last);
 | 
				
			||||||
			if (err)
 | 
								if (err)
 | 
				
			||||||
				goto errout;
 | 
									goto errout;
 | 
				
			||||||
			next = rtnl_dereference(tp->next);
 | 
					 | 
				
			||||||
			tfilter_notify(net, skb, n, tp, t->tcm_handle,
 | 
								tfilter_notify(net, skb, n, tp, t->tcm_handle,
 | 
				
			||||||
				       RTM_DELTFILTER, false);
 | 
									       RTM_DELTFILTER, false);
 | 
				
			||||||
			if (last) {
 | 
								if (last) {
 | 
				
			||||||
				RCU_INIT_POINTER(*back, next);
 | 
									tcf_chain_tp_remove(chain, &chain_info, tp);
 | 
				
			||||||
				tcf_proto_destroy(tp);
 | 
									tcf_proto_destroy(tp);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			goto errout;
 | 
								goto errout;
 | 
				
			||||||
| 
						 | 
					@ -480,10 +547,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 | 
				
			||||||
	err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
 | 
						err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
 | 
				
			||||||
			      n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
 | 
								      n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
 | 
				
			||||||
	if (err == 0) {
 | 
						if (err == 0) {
 | 
				
			||||||
		if (tp_created) {
 | 
							if (tp_created)
 | 
				
			||||||
			RCU_INIT_POINTER(tp->next, rtnl_dereference(*back));
 | 
								tcf_chain_tp_insert(chain, &chain_info, tp);
 | 
				
			||||||
			rcu_assign_pointer(*back, tp);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
 | 
							tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (tp_created)
 | 
							if (tp_created)
 | 
				
			||||||
| 
						 | 
					@ -584,7 +649,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
	struct net_device *dev;
 | 
						struct net_device *dev;
 | 
				
			||||||
	struct Qdisc *q;
 | 
						struct Qdisc *q;
 | 
				
			||||||
	struct tcf_block *block;
 | 
						struct tcf_block *block;
 | 
				
			||||||
	struct tcf_proto *tp, __rcu **chain;
 | 
						struct tcf_proto *tp;
 | 
				
			||||||
 | 
						struct tcf_chain *chain;
 | 
				
			||||||
	struct tcmsg *tcm = nlmsg_data(cb->nlh);
 | 
						struct tcmsg *tcm = nlmsg_data(cb->nlh);
 | 
				
			||||||
	unsigned long cl = 0;
 | 
						unsigned long cl = 0;
 | 
				
			||||||
	const struct Qdisc_class_ops *cops;
 | 
						const struct Qdisc_class_ops *cops;
 | 
				
			||||||
| 
						 | 
					@ -615,11 +681,11 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
	block = cops->tcf_block(q, cl);
 | 
						block = cops->tcf_block(q, cl);
 | 
				
			||||||
	if (!block)
 | 
						if (!block)
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
	chain = block->p_filter_chain;
 | 
						chain = block->chain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s_t = cb->args[0];
 | 
						s_t = cb->args[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (tp = rtnl_dereference(*chain), t = 0;
 | 
						for (tp = rtnl_dereference(chain->filter_chain), t = 0;
 | 
				
			||||||
	     tp; tp = rtnl_dereference(tp->next), t++) {
 | 
						     tp; tp = rtnl_dereference(tp->next), t++) {
 | 
				
			||||||
		if (t < s_t)
 | 
							if (t < s_t)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue