On 6/18/19 7:20 AM, Stefano Brivio wrote: > diff --git a/net/ipv6/route.c b/net/ipv6/route.c > index 0f60eb3a2873..7375f3b7d310 100644 > --- a/net/ipv6/route.c > +++ b/net/ipv6/route.c > @@ -4854,33 +4854,94 @@ static bool fib6_info_uses_dev(const struct fib6_info > *f6i, > return false; > } > > -int rt6_dump_route(struct fib6_info *rt, void *p_arg) > +/* Return -1 if done with node, number of handled routes on partial dump */ > +int rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip)
Changing the return code of rt6_dump_route should be a separate patch. > { > struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; > struct fib_dump_filter *filter = &arg->filter; > + struct rt6_exception_bucket *bucket; > unsigned int flags = NLM_F_MULTI; > + struct rt6_exception *rt6_ex; > struct net *net = arg->net; > + int i, count = 0; > > if (rt == net->ipv6.fib6_null_entry) > - return 0; > + return -1; > > if ((filter->flags & RTM_F_PREFIX) && > !(rt->fib6_flags & RTF_PREFIX_RT)) { > /* success since this is not a prefix route */ > - return 1; > + return -1; > } > - if (filter->filter_set) { > - if ((filter->rt_type && rt->fib6_type != filter->rt_type) || > - (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) || > - (filter->protocol && rt->fib6_protocol != > filter->protocol)) { > - return 1; > - } > + if (filter->filter_set && > + ((filter->rt_type && rt->fib6_type != filter->rt_type) || > + (filter->dev && !fib6_info_uses_dev(rt, filter->dev)) || > + (filter->protocol && rt->fib6_protocol != filter->protocol))) { > + return -1; > + } > + > + if (filter->filter_set || > + !filter->dump_routes || !filter->dump_exceptions) { > flags |= NLM_F_DUMP_FILTERED; > } > > - return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0, > - RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid, > - arg->cb->nlh->nlmsg_seq, flags); > + if (filter->dump_routes) { > + if (skip) { > + skip--; > + } else { > + if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, > + 0, RTM_NEWROUTE, > + NETLINK_CB(arg->cb->skb).portid, > + arg->cb->nlh->nlmsg_seq, flags)) { > + return 0; > + } > + count++; > + } > + } > + > + if (!filter->dump_exceptions) > + return -1; > + And the dump of the exception bucket should be a standalone function. You will see why with net-next (it is per fib6_nh). > + bucket = rcu_dereference(rt->rt6i_exception_bucket); > + if (!bucket) > + return -1; > + > + for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { > + hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { > + if (skip) { > + skip--; > + continue; > + } > + > + /* Expiration of entries doesn't bump sernum, insertion > + * does. Removal is triggered by insertion, so we can > + * rely on the fact that if entries change between two > + * partial dumps, this node is scanned again completely, > + * see rt6_insert_exception() and fib6_dump_table(). > + * > + * Count expired entries we go through as handled > + * entries that we'll skip next time, in case of partial > + * node dump. Otherwise, if entries expire meanwhile, > + * we'll skip the wrong amount. > + */ > + if (rt6_check_expired(rt6_ex->rt6i)) { > + count++; > + continue; > + } > + > + if (rt6_fill_node(net, arg->skb, rt, &rt6_ex->rt6i->dst, > + NULL, NULL, 0, RTM_NEWROUTE, > + NETLINK_CB(arg->cb->skb).portid, > + arg->cb->nlh->nlmsg_seq, flags)) { > + return count; > + } > + > + count++; > + } > + bucket++; > + } > + > + return -1; > } > > static int inet6_rtm_valid_getroute_req(struct sk_buff *skb, >