forked from mirrors/linux
		
	ipv6: remove RTNL protection from inet6_dump_fib()
No longer hold RTNL while calling inet6_dump_fib(). Also change return value for a completed dump, so that NLMSG_DONE can be appended to current skb, saving one recvmsg() system call. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: David Ahern <dsahern@kernel.org> Link: https://lore.kernel.org/r/20240329183053.644630-1-edumazet@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									edaa34e68c
								
							
						
					
					
						commit
						5fc68320c1
					
				
					 1 changed files with 26 additions and 25 deletions
				
			
		|  | @ -623,23 +623,22 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | ||||||
| 	struct rt6_rtnl_dump_arg arg = { | 	struct rt6_rtnl_dump_arg arg = { | ||||||
| 		.filter.dump_exceptions = true, | 		.filter.dump_exceptions = true, | ||||||
| 		.filter.dump_routes = true, | 		.filter.dump_routes = true, | ||||||
| 		.filter.rtnl_held = true, | 		.filter.rtnl_held = false, | ||||||
| 	}; | 	}; | ||||||
| 	const struct nlmsghdr *nlh = cb->nlh; | 	const struct nlmsghdr *nlh = cb->nlh; | ||||||
| 	struct net *net = sock_net(skb->sk); | 	struct net *net = sock_net(skb->sk); | ||||||
| 	unsigned int h, s_h; |  | ||||||
| 	unsigned int e = 0, s_e; | 	unsigned int e = 0, s_e; | ||||||
|  | 	struct hlist_head *head; | ||||||
| 	struct fib6_walker *w; | 	struct fib6_walker *w; | ||||||
| 	struct fib6_table *tb; | 	struct fib6_table *tb; | ||||||
| 	struct hlist_head *head; | 	unsigned int h, s_h; | ||||||
| 	int res = 0; | 	int err = 0; | ||||||
| 
 | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
| 	if (cb->strict_check) { | 	if (cb->strict_check) { | ||||||
| 		int err; |  | ||||||
| 
 |  | ||||||
| 		err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb); | 		err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb); | ||||||
| 		if (err < 0) | 		if (err < 0) | ||||||
| 			return err; | 			goto unlock; | ||||||
| 	} else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) { | 	} else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) { | ||||||
| 		struct rtmsg *rtm = nlmsg_data(nlh); | 		struct rtmsg *rtm = nlmsg_data(nlh); | ||||||
| 
 | 
 | ||||||
|  | @ -660,8 +659,10 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | ||||||
| 		 * 2. allocate and initialize walker. | 		 * 2. allocate and initialize walker. | ||||||
| 		 */ | 		 */ | ||||||
| 		w = kzalloc(sizeof(*w), GFP_ATOMIC); | 		w = kzalloc(sizeof(*w), GFP_ATOMIC); | ||||||
| 		if (!w) | 		if (!w) { | ||||||
| 			return -ENOMEM; | 			err = -ENOMEM; | ||||||
|  | 			goto unlock; | ||||||
|  | 		} | ||||||
| 		w->func = fib6_dump_node; | 		w->func = fib6_dump_node; | ||||||
| 		cb->args[2] = (long)w; | 		cb->args[2] = (long)w; | ||||||
| 	} | 	} | ||||||
|  | @ -675,46 +676,46 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | ||||||
| 		tb = fib6_get_table(net, arg.filter.table_id); | 		tb = fib6_get_table(net, arg.filter.table_id); | ||||||
| 		if (!tb) { | 		if (!tb) { | ||||||
| 			if (rtnl_msg_family(cb->nlh) != PF_INET6) | 			if (rtnl_msg_family(cb->nlh) != PF_INET6) | ||||||
| 				goto out; | 				goto unlock; | ||||||
| 
 | 
 | ||||||
| 			NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist"); | 			NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist"); | ||||||
| 			return -ENOENT; | 			err = -ENOENT; | ||||||
|  | 			goto unlock; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (!cb->args[0]) { | 		if (!cb->args[0]) { | ||||||
| 			res = fib6_dump_table(tb, skb, cb); | 			err = fib6_dump_table(tb, skb, cb); | ||||||
| 			if (!res) | 			if (!err) | ||||||
| 				cb->args[0] = 1; | 				cb->args[0] = 1; | ||||||
| 		} | 		} | ||||||
| 		goto out; | 		goto unlock; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s_h = cb->args[0]; | 	s_h = cb->args[0]; | ||||||
| 	s_e = cb->args[1]; | 	s_e = cb->args[1]; | ||||||
| 
 | 
 | ||||||
| 	rcu_read_lock(); |  | ||||||
| 	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { | 	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { | ||||||
| 		e = 0; | 		e = 0; | ||||||
| 		head = &net->ipv6.fib_table_hash[h]; | 		head = &net->ipv6.fib_table_hash[h]; | ||||||
| 		hlist_for_each_entry_rcu(tb, head, tb6_hlist) { | 		hlist_for_each_entry_rcu(tb, head, tb6_hlist) { | ||||||
| 			if (e < s_e) | 			if (e < s_e) | ||||||
| 				goto next; | 				goto next; | ||||||
| 			res = fib6_dump_table(tb, skb, cb); | 			err = fib6_dump_table(tb, skb, cb); | ||||||
| 			if (res != 0) | 			if (err != 0) | ||||||
| 				goto out_unlock; | 				goto out; | ||||||
| next: | next: | ||||||
| 			e++; | 			e++; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| out_unlock: | out: | ||||||
| 	rcu_read_unlock(); |  | ||||||
| 	cb->args[1] = e; | 	cb->args[1] = e; | ||||||
| 	cb->args[0] = h; | 	cb->args[0] = h; | ||||||
| out: | 
 | ||||||
| 	res = res < 0 ? res : skb->len; | unlock: | ||||||
| 	if (res <= 0) | 	rcu_read_unlock(); | ||||||
|  | 	if (err <= 0) | ||||||
| 		fib6_dump_end(cb); | 		fib6_dump_end(cb); | ||||||
| 	return res; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void fib6_metric_set(struct fib6_info *f6i, int metric, u32 val) | void fib6_metric_set(struct fib6_info *f6i, int metric, u32 val) | ||||||
|  | @ -2506,7 +2507,7 @@ int __init fib6_init(void) | ||||||
| 		goto out_kmem_cache_create; | 		goto out_kmem_cache_create; | ||||||
| 
 | 
 | ||||||
| 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, NULL, | 	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, NULL, | ||||||
| 				   inet6_dump_fib, 0); | 				   inet6_dump_fib, RTNL_FLAG_DUMP_UNLOCKED); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto out_unregister_subsys; | 		goto out_unregister_subsys; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric Dumazet
						Eric Dumazet