mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: sched: cls_flow use RCU
Signed-off-by: John Fastabend <john.r.fastabend@intel.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									952313bd62
								
							
						
					
					
						commit
						70da9f0bf9
					
				
					 1 changed files with 84 additions and 61 deletions
				
			
		| 
						 | 
					@ -34,12 +34,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct flow_head {
 | 
					struct flow_head {
 | 
				
			||||||
	struct list_head	filters;
 | 
						struct list_head	filters;
 | 
				
			||||||
 | 
						struct rcu_head		rcu;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct flow_filter {
 | 
					struct flow_filter {
 | 
				
			||||||
	struct list_head	list;
 | 
						struct list_head	list;
 | 
				
			||||||
	struct tcf_exts		exts;
 | 
						struct tcf_exts		exts;
 | 
				
			||||||
	struct tcf_ematch_tree	ematches;
 | 
						struct tcf_ematch_tree	ematches;
 | 
				
			||||||
 | 
						struct tcf_proto	*tp;
 | 
				
			||||||
	struct timer_list	perturb_timer;
 | 
						struct timer_list	perturb_timer;
 | 
				
			||||||
	u32			perturb_period;
 | 
						u32			perturb_period;
 | 
				
			||||||
	u32			handle;
 | 
						u32			handle;
 | 
				
			||||||
| 
						 | 
					@ -54,6 +56,7 @@ struct flow_filter {
 | 
				
			||||||
	u32			divisor;
 | 
						u32			divisor;
 | 
				
			||||||
	u32			baseclass;
 | 
						u32			baseclass;
 | 
				
			||||||
	u32			hashrnd;
 | 
						u32			hashrnd;
 | 
				
			||||||
 | 
						struct rcu_head		rcu;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline u32 addr_fold(void *addr)
 | 
					static inline u32 addr_fold(void *addr)
 | 
				
			||||||
| 
						 | 
					@ -276,14 +279,14 @@ static u32 flow_key_get(struct sk_buff *skb, int key, struct flow_keys *flow)
 | 
				
			||||||
static int flow_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 | 
					static int flow_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 | 
				
			||||||
			 struct tcf_result *res)
 | 
								 struct tcf_result *res)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct flow_head *head = tp->root;
 | 
						struct flow_head *head = rcu_dereference_bh(tp->root);
 | 
				
			||||||
	struct flow_filter *f;
 | 
						struct flow_filter *f;
 | 
				
			||||||
	u32 keymask;
 | 
						u32 keymask;
 | 
				
			||||||
	u32 classid;
 | 
						u32 classid;
 | 
				
			||||||
	unsigned int n, key;
 | 
						unsigned int n, key;
 | 
				
			||||||
	int r;
 | 
						int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(f, &head->filters, list) {
 | 
						list_for_each_entry_rcu(f, &head->filters, list) {
 | 
				
			||||||
		u32 keys[FLOW_KEY_MAX + 1];
 | 
							u32 keys[FLOW_KEY_MAX + 1];
 | 
				
			||||||
		struct flow_keys flow_keys;
 | 
							struct flow_keys flow_keys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -346,13 +349,23 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = {
 | 
				
			||||||
	[TCA_FLOW_PERTURB]	= { .type = NLA_U32 },
 | 
						[TCA_FLOW_PERTURB]	= { .type = NLA_U32 },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void flow_destroy_filter(struct rcu_head *head)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct flow_filter *f = container_of(head, struct flow_filter, rcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						del_timer_sync(&f->perturb_timer);
 | 
				
			||||||
 | 
						tcf_exts_destroy(f->tp, &f->exts);
 | 
				
			||||||
 | 
						tcf_em_tree_destroy(f->tp, &f->ematches);
 | 
				
			||||||
 | 
						kfree(f);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int flow_change(struct net *net, struct sk_buff *in_skb,
 | 
					static int flow_change(struct net *net, struct sk_buff *in_skb,
 | 
				
			||||||
		       struct tcf_proto *tp, unsigned long base,
 | 
							       struct tcf_proto *tp, unsigned long base,
 | 
				
			||||||
		       u32 handle, struct nlattr **tca,
 | 
							       u32 handle, struct nlattr **tca,
 | 
				
			||||||
		       unsigned long *arg, bool ovr)
 | 
							       unsigned long *arg, bool ovr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct flow_head *head = tp->root;
 | 
						struct flow_head *head = rtnl_dereference(tp->root);
 | 
				
			||||||
	struct flow_filter *f;
 | 
						struct flow_filter *fold, *fnew;
 | 
				
			||||||
	struct nlattr *opt = tca[TCA_OPTIONS];
 | 
						struct nlattr *opt = tca[TCA_OPTIONS];
 | 
				
			||||||
	struct nlattr *tb[TCA_FLOW_MAX + 1];
 | 
						struct nlattr *tb[TCA_FLOW_MAX + 1];
 | 
				
			||||||
	struct tcf_exts e;
 | 
						struct tcf_exts e;
 | 
				
			||||||
| 
						 | 
					@ -401,20 +414,42 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		goto err1;
 | 
							goto err1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	f = (struct flow_filter *)*arg;
 | 
						err = -ENOBUFS;
 | 
				
			||||||
	if (f != NULL) {
 | 
						fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!fnew)
 | 
				
			||||||
 | 
							goto err2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fold = (struct flow_filter *)*arg;
 | 
				
			||||||
 | 
						if (fold) {
 | 
				
			||||||
		err = -EINVAL;
 | 
							err = -EINVAL;
 | 
				
			||||||
		if (f->handle != handle && handle)
 | 
							if (fold->handle != handle && handle)
 | 
				
			||||||
			goto err2;
 | 
								goto err2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mode = f->mode;
 | 
							/* Copy fold into fnew */
 | 
				
			||||||
 | 
							fnew->handle = fold->handle;
 | 
				
			||||||
 | 
							fnew->keymask = fold->keymask;
 | 
				
			||||||
 | 
							fnew->tp = fold->tp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fnew->handle = fold->handle;
 | 
				
			||||||
 | 
							fnew->nkeys = fold->nkeys;
 | 
				
			||||||
 | 
							fnew->keymask = fold->keymask;
 | 
				
			||||||
 | 
							fnew->mode = fold->mode;
 | 
				
			||||||
 | 
							fnew->mask = fold->mask;
 | 
				
			||||||
 | 
							fnew->xor = fold->xor;
 | 
				
			||||||
 | 
							fnew->rshift = fold->rshift;
 | 
				
			||||||
 | 
							fnew->addend = fold->addend;
 | 
				
			||||||
 | 
							fnew->divisor = fold->divisor;
 | 
				
			||||||
 | 
							fnew->baseclass = fold->baseclass;
 | 
				
			||||||
 | 
							fnew->hashrnd = fold->hashrnd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mode = fold->mode;
 | 
				
			||||||
		if (tb[TCA_FLOW_MODE])
 | 
							if (tb[TCA_FLOW_MODE])
 | 
				
			||||||
			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
 | 
								mode = nla_get_u32(tb[TCA_FLOW_MODE]);
 | 
				
			||||||
		if (mode != FLOW_MODE_HASH && nkeys > 1)
 | 
							if (mode != FLOW_MODE_HASH && nkeys > 1)
 | 
				
			||||||
			goto err2;
 | 
								goto err2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (mode == FLOW_MODE_HASH)
 | 
							if (mode == FLOW_MODE_HASH)
 | 
				
			||||||
			perturb_period = f->perturb_period;
 | 
								perturb_period = fold->perturb_period;
 | 
				
			||||||
		if (tb[TCA_FLOW_PERTURB]) {
 | 
							if (tb[TCA_FLOW_PERTURB]) {
 | 
				
			||||||
			if (mode != FLOW_MODE_HASH)
 | 
								if (mode != FLOW_MODE_HASH)
 | 
				
			||||||
				goto err2;
 | 
									goto err2;
 | 
				
			||||||
| 
						 | 
					@ -444,83 +479,70 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
 | 
				
			||||||
		if (TC_H_MIN(baseclass) == 0)
 | 
							if (TC_H_MIN(baseclass) == 0)
 | 
				
			||||||
			baseclass = TC_H_MAKE(baseclass, 1);
 | 
								baseclass = TC_H_MAKE(baseclass, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = -ENOBUFS;
 | 
							fnew->handle = handle;
 | 
				
			||||||
		f = kzalloc(sizeof(*f), GFP_KERNEL);
 | 
							fnew->mask  = ~0U;
 | 
				
			||||||
		if (f == NULL)
 | 
							fnew->tp = tp;
 | 
				
			||||||
			goto err2;
 | 
							get_random_bytes(&fnew->hashrnd, 4);
 | 
				
			||||||
 | 
							tcf_exts_init(&fnew->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE);
 | 
				
			||||||
		f->handle = handle;
 | 
					 | 
				
			||||||
		f->mask	  = ~0U;
 | 
					 | 
				
			||||||
		tcf_exts_init(&f->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		get_random_bytes(&f->hashrnd, 4);
 | 
					 | 
				
			||||||
		f->perturb_timer.function = flow_perturbation;
 | 
					 | 
				
			||||||
		f->perturb_timer.data = (unsigned long)f;
 | 
					 | 
				
			||||||
		init_timer_deferrable(&f->perturb_timer);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tcf_exts_change(tp, &f->exts, &e);
 | 
						fnew->perturb_timer.function = flow_perturbation;
 | 
				
			||||||
	tcf_em_tree_change(tp, &f->ematches, &t);
 | 
						fnew->perturb_timer.data = (unsigned long)fnew;
 | 
				
			||||||
 | 
						init_timer_deferrable(&fnew->perturb_timer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tcf_tree_lock(tp);
 | 
						tcf_exts_change(tp, &fnew->exts, &e);
 | 
				
			||||||
 | 
						tcf_em_tree_change(tp, &fnew->ematches, &t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tb[TCA_FLOW_KEYS]) {
 | 
						if (tb[TCA_FLOW_KEYS]) {
 | 
				
			||||||
		f->keymask = keymask;
 | 
							fnew->keymask = keymask;
 | 
				
			||||||
		f->nkeys   = nkeys;
 | 
							fnew->nkeys   = nkeys;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	f->mode = mode;
 | 
						fnew->mode = mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tb[TCA_FLOW_MASK])
 | 
						if (tb[TCA_FLOW_MASK])
 | 
				
			||||||
		f->mask = nla_get_u32(tb[TCA_FLOW_MASK]);
 | 
							fnew->mask = nla_get_u32(tb[TCA_FLOW_MASK]);
 | 
				
			||||||
	if (tb[TCA_FLOW_XOR])
 | 
						if (tb[TCA_FLOW_XOR])
 | 
				
			||||||
		f->xor = nla_get_u32(tb[TCA_FLOW_XOR]);
 | 
							fnew->xor = nla_get_u32(tb[TCA_FLOW_XOR]);
 | 
				
			||||||
	if (tb[TCA_FLOW_RSHIFT])
 | 
						if (tb[TCA_FLOW_RSHIFT])
 | 
				
			||||||
		f->rshift = nla_get_u32(tb[TCA_FLOW_RSHIFT]);
 | 
							fnew->rshift = nla_get_u32(tb[TCA_FLOW_RSHIFT]);
 | 
				
			||||||
	if (tb[TCA_FLOW_ADDEND])
 | 
						if (tb[TCA_FLOW_ADDEND])
 | 
				
			||||||
		f->addend = nla_get_u32(tb[TCA_FLOW_ADDEND]);
 | 
							fnew->addend = nla_get_u32(tb[TCA_FLOW_ADDEND]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tb[TCA_FLOW_DIVISOR])
 | 
						if (tb[TCA_FLOW_DIVISOR])
 | 
				
			||||||
		f->divisor = nla_get_u32(tb[TCA_FLOW_DIVISOR]);
 | 
							fnew->divisor = nla_get_u32(tb[TCA_FLOW_DIVISOR]);
 | 
				
			||||||
	if (baseclass)
 | 
						if (baseclass)
 | 
				
			||||||
		f->baseclass = baseclass;
 | 
							fnew->baseclass = baseclass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	f->perturb_period = perturb_period;
 | 
						fnew->perturb_period = perturb_period;
 | 
				
			||||||
	del_timer(&f->perturb_timer);
 | 
					 | 
				
			||||||
	if (perturb_period)
 | 
						if (perturb_period)
 | 
				
			||||||
		mod_timer(&f->perturb_timer, jiffies + perturb_period);
 | 
							mod_timer(&fnew->perturb_timer, jiffies + perturb_period);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (*arg == 0)
 | 
						if (*arg == 0)
 | 
				
			||||||
		list_add_tail(&f->list, &head->filters);
 | 
							list_add_tail_rcu(&fnew->list, &head->filters);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							list_replace_rcu(&fnew->list, &fold->list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tcf_tree_unlock(tp);
 | 
						*arg = (unsigned long)fnew;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*arg = (unsigned long)f;
 | 
						if (fold)
 | 
				
			||||||
 | 
							call_rcu(&fold->rcu, flow_destroy_filter);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
err2:
 | 
					err2:
 | 
				
			||||||
	tcf_em_tree_destroy(tp, &t);
 | 
						tcf_em_tree_destroy(tp, &t);
 | 
				
			||||||
 | 
						kfree(fnew);
 | 
				
			||||||
err1:
 | 
					err1:
 | 
				
			||||||
	tcf_exts_destroy(tp, &e);
 | 
						tcf_exts_destroy(tp, &e);
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void flow_destroy_filter(struct tcf_proto *tp, struct flow_filter *f)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	del_timer_sync(&f->perturb_timer);
 | 
					 | 
				
			||||||
	tcf_exts_destroy(tp, &f->exts);
 | 
					 | 
				
			||||||
	tcf_em_tree_destroy(tp, &f->ematches);
 | 
					 | 
				
			||||||
	kfree(f);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int flow_delete(struct tcf_proto *tp, unsigned long arg)
 | 
					static int flow_delete(struct tcf_proto *tp, unsigned long arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct flow_filter *f = (struct flow_filter *)arg;
 | 
						struct flow_filter *f = (struct flow_filter *)arg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tcf_tree_lock(tp);
 | 
						list_del_rcu(&f->list);
 | 
				
			||||||
	list_del(&f->list);
 | 
						call_rcu(&f->rcu, flow_destroy_filter);
 | 
				
			||||||
	tcf_tree_unlock(tp);
 | 
					 | 
				
			||||||
	flow_destroy_filter(tp, f);
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -532,28 +554,29 @@ static int flow_init(struct tcf_proto *tp)
 | 
				
			||||||
	if (head == NULL)
 | 
						if (head == NULL)
 | 
				
			||||||
		return -ENOBUFS;
 | 
							return -ENOBUFS;
 | 
				
			||||||
	INIT_LIST_HEAD(&head->filters);
 | 
						INIT_LIST_HEAD(&head->filters);
 | 
				
			||||||
	tp->root = head;
 | 
						rcu_assign_pointer(tp->root, head);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void flow_destroy(struct tcf_proto *tp)
 | 
					static void flow_destroy(struct tcf_proto *tp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct flow_head *head = tp->root;
 | 
						struct flow_head *head = rtnl_dereference(tp->root);
 | 
				
			||||||
	struct flow_filter *f, *next;
 | 
						struct flow_filter *f, *next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry_safe(f, next, &head->filters, list) {
 | 
						list_for_each_entry_safe(f, next, &head->filters, list) {
 | 
				
			||||||
		list_del(&f->list);
 | 
							list_del_rcu(&f->list);
 | 
				
			||||||
		flow_destroy_filter(tp, f);
 | 
							call_rcu(&f->rcu, flow_destroy_filter);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	kfree(head);
 | 
						RCU_INIT_POINTER(tp->root, NULL);
 | 
				
			||||||
 | 
						kfree_rcu(head, rcu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static unsigned long flow_get(struct tcf_proto *tp, u32 handle)
 | 
					static unsigned long flow_get(struct tcf_proto *tp, u32 handle)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct flow_head *head = tp->root;
 | 
						struct flow_head *head = rtnl_dereference(tp->root);
 | 
				
			||||||
	struct flow_filter *f;
 | 
						struct flow_filter *f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(f, &head->filters, list)
 | 
						list_for_each_entry_rcu(f, &head->filters, list)
 | 
				
			||||||
		if (f->handle == handle)
 | 
							if (f->handle == handle)
 | 
				
			||||||
			return (unsigned long)f;
 | 
								return (unsigned long)f;
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -626,10 +649,10 @@ static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 | 
					static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct flow_head *head = tp->root;
 | 
						struct flow_head *head = rtnl_dereference(tp->root);
 | 
				
			||||||
	struct flow_filter *f;
 | 
						struct flow_filter *f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(f, &head->filters, list) {
 | 
						list_for_each_entry_rcu(f, &head->filters, list) {
 | 
				
			||||||
		if (arg->count < arg->skip)
 | 
							if (arg->count < arg->skip)
 | 
				
			||||||
			goto skip;
 | 
								goto skip;
 | 
				
			||||||
		if (arg->fn(tp, (unsigned long)f, arg) < 0) {
 | 
							if (arg->fn(tp, (unsigned long)f, arg) < 0) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue