forked from mirrors/linux
		
	net: sched: fw use RCU
RCU'ify fw classifier. 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
							
								
									70da9f0bf9
								
							
						
					
					
						commit
						e35a8ee599
					
				
					 1 changed files with 77 additions and 34 deletions
				
			
		|  | @ -33,17 +33,20 @@ | |||
| 
 | ||||
| struct fw_head { | ||||
| 	u32			mask; | ||||
| 	struct fw_filter	*ht[HTSIZE]; | ||||
| 	struct fw_filter __rcu	*ht[HTSIZE]; | ||||
| 	struct rcu_head		rcu; | ||||
| }; | ||||
| 
 | ||||
| struct fw_filter { | ||||
| 	struct fw_filter	*next; | ||||
| 	struct fw_filter __rcu	*next; | ||||
| 	u32			id; | ||||
| 	struct tcf_result	res; | ||||
| #ifdef CONFIG_NET_CLS_IND | ||||
| 	int			ifindex; | ||||
| #endif /* CONFIG_NET_CLS_IND */ | ||||
| 	struct tcf_exts		exts; | ||||
| 	struct tcf_proto	*tp; | ||||
| 	struct rcu_head		rcu; | ||||
| }; | ||||
| 
 | ||||
| static u32 fw_hash(u32 handle) | ||||
|  | @ -56,14 +59,16 @@ static u32 fw_hash(u32 handle) | |||
| static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, | ||||
| 			  struct tcf_result *res) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rcu_dereference_bh(tp->root); | ||||
| 	struct fw_filter *f; | ||||
| 	int r; | ||||
| 	u32 id = skb->mark; | ||||
| 
 | ||||
| 	if (head != NULL) { | ||||
| 		id &= head->mask; | ||||
| 		for (f = head->ht[fw_hash(id)]; f; f = f->next) { | ||||
| 
 | ||||
| 		for (f = rcu_dereference_bh(head->ht[fw_hash(id)]); f; | ||||
| 		     f = rcu_dereference_bh(f->next)) { | ||||
| 			if (f->id == id) { | ||||
| 				*res = f->res; | ||||
| #ifdef CONFIG_NET_CLS_IND | ||||
|  | @ -92,13 +97,14 @@ static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, | |||
| 
 | ||||
| static unsigned long fw_get(struct tcf_proto *tp, u32 handle) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rtnl_dereference(tp->root); | ||||
| 	struct fw_filter *f; | ||||
| 
 | ||||
| 	if (head == NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	for (f = head->ht[fw_hash(handle)]; f; f = f->next) { | ||||
| 	f = rtnl_dereference(head->ht[fw_hash(handle)]); | ||||
| 	for (; f; f = rtnl_dereference(f->next)) { | ||||
| 		if (f->id == handle) | ||||
| 			return (unsigned long)f; | ||||
| 	} | ||||
|  | @ -114,8 +120,11 @@ static int fw_init(struct tcf_proto *tp) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f) | ||||
| static void fw_delete_filter(struct rcu_head *head) | ||||
| { | ||||
| 	struct fw_filter *f = container_of(head, struct fw_filter, rcu); | ||||
| 	struct tcf_proto *tp = f->tp; | ||||
| 
 | ||||
| 	tcf_unbind_filter(tp, &f->res); | ||||
| 	tcf_exts_destroy(tp, &f->exts); | ||||
| 	kfree(f); | ||||
|  | @ -123,7 +132,7 @@ static void fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f) | |||
| 
 | ||||
| static void fw_destroy(struct tcf_proto *tp) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rtnl_dereference(tp->root); | ||||
| 	struct fw_filter *f; | ||||
| 	int h; | ||||
| 
 | ||||
|  | @ -131,29 +140,33 @@ static void fw_destroy(struct tcf_proto *tp) | |||
| 		return; | ||||
| 
 | ||||
| 	for (h = 0; h < HTSIZE; h++) { | ||||
| 		while ((f = head->ht[h]) != NULL) { | ||||
| 			head->ht[h] = f->next; | ||||
| 			fw_delete_filter(tp, f); | ||||
| 		while ((f = rtnl_dereference(head->ht[h])) != NULL) { | ||||
| 			RCU_INIT_POINTER(head->ht[h], | ||||
| 					 rtnl_dereference(f->next)); | ||||
| 			call_rcu(&f->rcu, fw_delete_filter); | ||||
| 		} | ||||
| 	} | ||||
| 	kfree(head); | ||||
| 	RCU_INIT_POINTER(tp->root, NULL); | ||||
| 	kfree_rcu(head, rcu); | ||||
| } | ||||
| 
 | ||||
| static int fw_delete(struct tcf_proto *tp, unsigned long arg) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rtnl_dereference(tp->root); | ||||
| 	struct fw_filter *f = (struct fw_filter *)arg; | ||||
| 	struct fw_filter **fp; | ||||
| 	struct fw_filter __rcu **fp; | ||||
| 	struct fw_filter *pfp; | ||||
| 
 | ||||
| 	if (head == NULL || f == NULL) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	for (fp = &head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) { | ||||
| 		if (*fp == f) { | ||||
| 			tcf_tree_lock(tp); | ||||
| 			*fp = f->next; | ||||
| 			tcf_tree_unlock(tp); | ||||
| 			fw_delete_filter(tp, f); | ||||
| 	fp = &head->ht[fw_hash(f->id)]; | ||||
| 
 | ||||
| 	for (pfp = rtnl_dereference(*fp); pfp; | ||||
| 	     fp = &pfp->next, pfp = rtnl_dereference(*fp)) { | ||||
| 		if (pfp == f) { | ||||
| 			RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); | ||||
| 			call_rcu(&f->rcu, fw_delete_filter); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | @ -171,7 +184,7 @@ static int | |||
| fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, | ||||
| 	struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rtnl_dereference(tp->root); | ||||
| 	struct tcf_exts e; | ||||
| 	u32 mask; | ||||
| 	int err; | ||||
|  | @ -220,7 +233,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, | |||
| 		     struct nlattr **tca, | ||||
| 		     unsigned long *arg, bool ovr) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rtnl_dereference(tp->root); | ||||
| 	struct fw_filter *f = (struct fw_filter *) *arg; | ||||
| 	struct nlattr *opt = tca[TCA_OPTIONS]; | ||||
| 	struct nlattr *tb[TCA_FW_MAX + 1]; | ||||
|  | @ -233,10 +246,42 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, | |||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	if (f != NULL) { | ||||
| 	if (f) { | ||||
| 		struct fw_filter *pfp, *fnew; | ||||
| 		struct fw_filter __rcu **fp; | ||||
| 
 | ||||
| 		if (f->id != handle && handle) | ||||
| 			return -EINVAL; | ||||
| 		return fw_change_attrs(net, tp, f, tb, tca, base, ovr); | ||||
| 
 | ||||
| 		fnew = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); | ||||
| 		if (!fnew) | ||||
| 			return -ENOBUFS; | ||||
| 
 | ||||
| 		fnew->id = f->id; | ||||
| 		fnew->res = f->res; | ||||
| #ifdef CONFIG_NET_CLS_IND | ||||
| 		fnew->ifindex = f->ifindex; | ||||
| #endif /* CONFIG_NET_CLS_IND */ | ||||
| 		fnew->tp = f->tp; | ||||
| 
 | ||||
| 		err = fw_change_attrs(net, tp, fnew, tb, tca, base, ovr); | ||||
| 		if (err < 0) { | ||||
| 			kfree(fnew); | ||||
| 			return err; | ||||
| 		} | ||||
| 
 | ||||
| 		fp = &head->ht[fw_hash(fnew->id)]; | ||||
| 		for (pfp = rtnl_dereference(*fp); pfp; | ||||
| 		     fp = &pfp->next, pfp = rtnl_dereference(*fp)) | ||||
| 			if (pfp == f) | ||||
| 				break; | ||||
| 
 | ||||
| 		RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next)); | ||||
| 		rcu_assign_pointer(*fp, fnew); | ||||
| 		call_rcu(&f->rcu, fw_delete_filter); | ||||
| 
 | ||||
| 		*arg = (unsigned long)fnew; | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!handle) | ||||
|  | @ -252,9 +297,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, | |||
| 			return -ENOBUFS; | ||||
| 		head->mask = mask; | ||||
| 
 | ||||
| 		tcf_tree_lock(tp); | ||||
| 		tp->root = head; | ||||
| 		tcf_tree_unlock(tp); | ||||
| 		rcu_assign_pointer(tp->root, head); | ||||
| 	} | ||||
| 
 | ||||
| 	f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); | ||||
|  | @ -263,15 +306,14 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, | |||
| 
 | ||||
| 	tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); | ||||
| 	f->id = handle; | ||||
| 	f->tp = tp; | ||||
| 
 | ||||
| 	err = fw_change_attrs(net, tp, f, tb, tca, base, ovr); | ||||
| 	if (err < 0) | ||||
| 		goto errout; | ||||
| 
 | ||||
| 	f->next = head->ht[fw_hash(handle)]; | ||||
| 	tcf_tree_lock(tp); | ||||
| 	head->ht[fw_hash(handle)] = f; | ||||
| 	tcf_tree_unlock(tp); | ||||
| 	RCU_INIT_POINTER(f->next, head->ht[fw_hash(handle)]); | ||||
| 	rcu_assign_pointer(head->ht[fw_hash(handle)], f); | ||||
| 
 | ||||
| 	*arg = (unsigned long)f; | ||||
| 	return 0; | ||||
|  | @ -283,7 +325,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, | |||
| 
 | ||||
| static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rtnl_dereference(tp->root); | ||||
| 	int h; | ||||
| 
 | ||||
| 	if (head == NULL) | ||||
|  | @ -295,7 +337,8 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) | |||
| 	for (h = 0; h < HTSIZE; h++) { | ||||
| 		struct fw_filter *f; | ||||
| 
 | ||||
| 		for (f = head->ht[h]; f; f = f->next) { | ||||
| 		for (f = rtnl_dereference(head->ht[h]); f; | ||||
| 		     f = rtnl_dereference(f->next)) { | ||||
| 			if (arg->count < arg->skip) { | ||||
| 				arg->count++; | ||||
| 				continue; | ||||
|  | @ -312,7 +355,7 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) | |||
| static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, | ||||
| 		   struct sk_buff *skb, struct tcmsg *t) | ||||
| { | ||||
| 	struct fw_head *head = tp->root; | ||||
| 	struct fw_head *head = rtnl_dereference(tp->root); | ||||
| 	struct fw_filter *f = (struct fw_filter *)fh; | ||||
| 	unsigned char *b = skb_tail_pointer(skb); | ||||
| 	struct nlattr *nest; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 John Fastabend
						John Fastabend