On Tue, 15 Sep 2015 14:40:48 -0700 Alexander Duyck <alexander.du...@gmail.com> wrote:
> On 09/15/2015 01:29 PM, Peter Nørlund wrote: > > Replaces the per-packet multipath with a hash-based multipath using > > source and destination address. > > > > Signed-off-by: Peter Nørlund <p...@ordbogen.com> > > --- > > include/net/ip_fib.h | 11 ++-- > > net/ipv4/fib_semantics.c | 137 > > +++++++++++++++++++++++++---------------------- > > net/ipv4/route.c | 23 +++++++- 3 files changed, 102 > > insertions(+), 69 deletions(-) > > > > diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h > > index a37d043..c335dd4 100644 > > --- a/include/net/ip_fib.h > > +++ b/include/net/ip_fib.h > > @@ -79,7 +79,7 @@ struct fib_nh { > > unsigned char nh_scope; > > #ifdef CONFIG_IP_ROUTE_MULTIPATH > > int nh_weight; > > - int nh_power; > > + atomic_t nh_upper_bound; > > #endif > > #ifdef CONFIG_IP_ROUTE_CLASSID > > __u32 nh_tclassid; > > @@ -118,7 +118,7 @@ struct fib_info { > > #define fib_advmss fib_metrics[RTAX_ADVMSS-1] > > int fib_nhs; > > #ifdef CONFIG_IP_ROUTE_MULTIPATH > > - int fib_power; > > + int fib_weight; > > #endif > > struct rcu_head rcu; > > struct fib_nh fib_nh[0]; > > @@ -312,7 +312,12 @@ int ip_fib_check_default(__be32 gw, struct > > net_device *dev); int fib_sync_down_dev(struct net_device *dev, > > unsigned long event); int fib_sync_down_addr(struct net *net, > > __be32 local); int fib_sync_up(struct net_device *dev, unsigned int > > nh_flags); -void fib_select_multipath(struct fib_result *res); > > + > > +extern u32 fib_multipath_secret __read_mostly; > > + > > +typedef int (*multipath_hash_func_t)(void *ctx); > > +void fib_select_multipath(struct fib_result *res, > > + multipath_hash_func_t hash_func, void > > *ctx); > > > > /* Exported by fib_trie.c */ > > void fib_trie_init(void); > > diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c > > index 064bd3c..64d3e0e 100644 > > --- a/net/ipv4/fib_semantics.c > > +++ b/net/ipv4/fib_semantics.c > > @@ -57,8 +57,7 @@ static unsigned int fib_info_cnt; > > static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE]; > > > > #ifdef CONFIG_IP_ROUTE_MULTIPATH > > - > > -static DEFINE_SPINLOCK(fib_multipath_lock); > > +u32 fib_multipath_secret __read_mostly; > > > > #define for_nexthops(fi) > > { \ int nhsel; const > > struct fib_nh *nh; \ @@ -468,6 > > +467,55 @@ static int fib_count_nexthops(struct rtnexthop *rtnh, > > int remaining) return remaining > 0 ? 0 : nhs; } > > > > +static void fib_rebalance(struct fib_info *fi) > > +{ > > + int total; > > + int w; > > + struct in_device *in_dev; > > + > > + if (fi->fib_nhs < 2) > > + return; > > + > > + total = 0; > > + for_nexthops(fi) { > > + if (nh->nh_flags & RTNH_F_DEAD) > > + continue; > > + > > + in_dev = __in_dev_get_rcu(nh->nh_dev); > > + > > + if (in_dev && > > + IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && > > + nh->nh_flags & RTNH_F_LINKDOWN) > > + continue; > > + > > + total += nh->nh_weight; > > + } endfor_nexthops(fi); > > + > > + w = 0; > > + change_nexthops(fi) { > > + int upper_bound; > > + > > + in_dev = __in_dev_get_rcu(nexthop_nh->nh_dev); > > + > > + if (nexthop_nh->nh_flags & RTNH_F_DEAD) { > > + upper_bound = -1; > > + } else if (in_dev && > > + > > IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && > > + nexthop_nh->nh_flags & RTNH_F_LINKDOWN) > > { > > + upper_bound = -1; > > + } else { > > + w += nexthop_nh->nh_weight; > > + upper_bound = > > DIV_ROUND_CLOSEST(2147483648LL * w, > > + total) - 1; > > + } > > + > > + atomic_set(&nexthop_nh->nh_upper_bound, > > upper_bound); > > + } endfor_nexthops(fi); > > + > > + net_get_random_once(&fib_multipath_secret, > > + sizeof(fib_multipath_secret)); > > +} > > + > > static int fib_get_nhs(struct fib_info *fi, struct rtnexthop > > *rtnh, int remaining, struct fib_config *cfg) > > { > > @@ -1094,8 +1142,15 @@ struct fib_info *fib_create_info(struct > > fib_config *cfg) > > > > change_nexthops(fi) { > > fib_info_update_nh_saddr(net, nexthop_nh); > > +#ifdef CONFIG_IP_ROUTE_MULTIPATH > > + fi->fib_weight += nexthop_nh->nh_weight; > > +#endif > > } endfor_nexthops(fi) > > > > +#ifdef CONFIG_IP_ROUTE_MULTIPATH > > + fib_rebalance(fi); > > +#endif > > + > > link_it: > > ofi = fib_find_info(fi); > > if (ofi) { > > @@ -1317,12 +1372,6 @@ int fib_sync_down_dev(struct net_device > > *dev, unsigned long event) nexthop_nh->nh_flags |= RTNH_F_LINKDOWN; > > break; > > } > > -#ifdef CONFIG_IP_ROUTE_MULTIPATH > > - spin_lock_bh(&fib_multipath_lock); > > - fi->fib_power -= > > nexthop_nh->nh_power; > > - nexthop_nh->nh_power = 0; > > - > > spin_unlock_bh(&fib_multipath_lock); -#endif > > dead++; > > } > > #ifdef CONFIG_IP_ROUTE_MULTIPATH > > @@ -1345,6 +1394,10 @@ int fib_sync_down_dev(struct net_device > > *dev, unsigned long event) } > > ret++; > > } > > + > > +#ifdef CONFIG_IP_ROUTE_MULTIPATH > > + fib_rebalance(fi); > > +#endif > > } > > > > return ret; > > @@ -1467,20 +1520,17 @@ int fib_sync_up(struct net_device *dev, > > unsigned int nh_flags) !__in_dev_get_rtnl(dev)) > > continue; > > alive++; > > -#ifdef CONFIG_IP_ROUTE_MULTIPATH > > - spin_lock_bh(&fib_multipath_lock); > > - nexthop_nh->nh_power = 0; > > nexthop_nh->nh_flags &= ~nh_flags; > > - spin_unlock_bh(&fib_multipath_lock); > > -#else > > - nexthop_nh->nh_flags &= ~nh_flags; > > -#endif > > } endfor_nexthops(fi) > > > > if (alive > 0) { > > fi->fib_flags &= ~nh_flags; > > ret++; > > } > > + > > +#ifdef CONFIG_IP_ROUTE_MULTIPATH > > + fib_rebalance(fi); > > +#endif > > } > > > > return ret; > > @@ -1488,62 +1538,21 @@ int fib_sync_up(struct net_device *dev, > > unsigned int nh_flags) > > > > #ifdef CONFIG_IP_ROUTE_MULTIPATH > > > > -/* > > - * The algorithm is suboptimal, but it provides really > > - * fair weighted route distribution. > > - */ > > -void fib_select_multipath(struct fib_result *res) > > +void fib_select_multipath(struct fib_result *res, > > + multipath_hash_func_t hash_func, void > > *ctx) { > > struct fib_info *fi = res->fi; > > - struct in_device *in_dev; > > - int w; > > - > > - spin_lock_bh(&fib_multipath_lock); > > - if (fi->fib_power <= 0) { > > - int power = 0; > > - change_nexthops(fi) { > > - in_dev = > > __in_dev_get_rcu(nexthop_nh->nh_dev); > > - if (nexthop_nh->nh_flags & RTNH_F_DEAD) > > - continue; > > - if (in_dev && > > - > > IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && > > - nexthop_nh->nh_flags & RTNH_F_LINKDOWN) > > - continue; > > - power += nexthop_nh->nh_weight; > > - nexthop_nh->nh_power = > > nexthop_nh->nh_weight; > > - } endfor_nexthops(fi); > > - fi->fib_power = power; > > - if (power <= 0) { > > - spin_unlock_bh(&fib_multipath_lock); > > - /* Race condition: route has just become > > dead. */ > > - res->nh_sel = 0; > > - return; > > - } > > - } > > - > > + int h = hash_func(ctx) & 0x7FFFFFFF; > > > > So if I understand this correctly you use the ctx and hash function > at the very start of the fib_select_multipath function call, do I > have that right? Correct > > > - /* w should be random number [0..fi->fib_power-1], > > - * it is pretty bad approximation. > > - */ > > - > > - w = jiffies % fi->fib_power; > > + for_nexthops(fi) { > > + if (h > atomic_read(&nh->nh_upper_bound)) > > + continue; > > > > - change_nexthops(fi) { > > - if (!(nexthop_nh->nh_flags & RTNH_F_DEAD) && > > - nexthop_nh->nh_power) { > > - w -= nexthop_nh->nh_power; > > - if (w <= 0) { > > - nexthop_nh->nh_power--; > > - fi->fib_power--; > > - res->nh_sel = nhsel; > > - > > spin_unlock_bh(&fib_multipath_lock); > > - return; > > - } > > - } > > + res->nh_sel = nhsel; > > + return; > > } endfor_nexthops(fi); > > > > /* Race condition: route has just become dead. */ > > res->nh_sel = 0; > > - spin_unlock_bh(&fib_multipath_lock); > > } > > #endif > > diff --git a/net/ipv4/route.c b/net/ipv4/route.c > > index 5f4a556..41d977c 100644 > > --- a/net/ipv4/route.c > > +++ b/net/ipv4/route.c > > @@ -1646,6 +1646,25 @@ out: > > return err; > > } > > > > +#ifdef CONFIG_IP_ROUTE_MULTIPATH > > + > > +static int ip_multipath_hash_skb(void *ctx) > > +{ > > + const struct sk_buff *skb = (struct sk_buff *)ctx; > > + const struct iphdr *iph = ip_hdr(skb); > > + > > + return jhash_2words(iph->saddr, iph->daddr, > > fib_multipath_secret); +} > > + > > +static int ip_multipath_hash_fl4(void *ctx) > > +{ > > + const struct flowi4 *fl4 = (const struct flowi4 *)ctx; > > + > > + return jhash_2words(fl4->saddr, fl4->daddr, > > fib_multipath_secret); +} > > + > > +#endif /* CONFIG_IP_ROUTE_MULTIPATH */ > > + > > static int ip_mkroute_input(struct sk_buff *skb, > > struct fib_result *res, > > const struct flowi4 *fl4, > > @@ -1654,7 +1673,7 @@ static int ip_mkroute_input(struct sk_buff > > *skb, { > > #ifdef CONFIG_IP_ROUTE_MULTIPATH > > if (res->fi && res->fi->fib_nhs > 1) > > - fib_select_multipath(res); > > + fib_select_multipath(res, ip_multipath_hash_skb, > > skb); #endif > > > > /* create a routing cache entry */ > > @@ -2200,7 +2219,7 @@ struct rtable *__ip_route_output_key(struct > > net *net, struct flowi4 *fl4) > > > > #ifdef CONFIG_IP_ROUTE_MULTIPATH > > if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0) > > - fib_select_multipath(&res); > > + fib_select_multipath(&res, ip_multipath_hash_fl4, > > fl4); else > > #endif > > if (!res.prefixlen && > > > > If you are using the hash right at the start of the function anyway > why not just call jhash_2words first, then pass the resultant hash to > fib_select_multipath instead of passing the function pointer and the > void pointer. You can probably save yourself quite a few cycles this > way. Good point! A quick proof-of-concept showed I saved 17 cycles (from 97 to 80 cycles/packet). Thanks > > - Alex > -- > To unsubscribe from this list: send the line "unsubscribe netdev" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html