diff mbox series

[RFC,net-next,07/20] net/ipv6: Move nexthop data to fib6_nh

Message ID 20180225194730.30063-8-dsahern@gmail.com
State RFC, archived
Delegated to: David Miller
Headers show
Series net/ipv6: Separate data structures for FIB and data path | expand

Commit Message

David Ahern Feb. 25, 2018, 7:47 p.m. UTC
Introduce fib6_nh structure and move nexthop related data from
rt6_info and rt6_info.dst to fib6_nh. References to dev, gateway or
lwtstate from a FIB lookup perspective are converted to use fib6_nh;
datapath references to dst version are left as is.

Signed-off-by: David Ahern <dsahern@gmail.com>
---
 .../net/ethernet/mellanox/mlxsw/spectrum_router.c  |  32 ++--
 include/net/ip6_fib.h                              |  16 +-
 include/net/ip6_route.h                            |   6 +-
 net/ipv6/addrconf.c                                |   2 +-
 net/ipv6/ip6_fib.c                                 |   6 +-
 net/ipv6/route.c                                   | 164 ++++++++++++---------
 6 files changed, 127 insertions(+), 99 deletions(-)

Comments

Wei Wang Feb. 26, 2018, 10:28 p.m. UTC | #1
On Sun, Feb 25, 2018 at 11:47 AM, David Ahern <dsahern@gmail.com> wrote:
> Introduce fib6_nh structure and move nexthop related data from
> rt6_info and rt6_info.dst to fib6_nh. References to dev, gateway or
> lwtstate from a FIB lookup perspective are converted to use fib6_nh;
> datapath references to dst version are left as is.
>

My understanding is that after your whole patch series, sibling routes
will still have their own fib6_info. Does it make sense to make this
fib6_nh as an array in fib6_info so that sibling routes will share
fib6_info but will have their own fib6_nh as a future improvement? It
matches ipv4 behavior. And I think it will make the sibling route
handling code easier?

> Signed-off-by: David Ahern <dsahern@gmail.com>
> ---
>  .../net/ethernet/mellanox/mlxsw/spectrum_router.c  |  32 ++--
>  include/net/ip6_fib.h                              |  16 +-
>  include/net/ip6_route.h                            |   6 +-
>  net/ipv6/addrconf.c                                |   2 +-
>  net/ipv6/ip6_fib.c                                 |   6 +-
>  net/ipv6/route.c                                   | 164 ++++++++++++---------
>  6 files changed, 127 insertions(+), 99 deletions(-)
>
> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> index 05146970c19c..90d01df783b3 100644
> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> @@ -2700,9 +2700,9 @@ mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
>                 struct in6_addr *gw;
>                 int ifindex, weight;
>
> -               ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
> -               weight = mlxsw_sp_rt6->rt->rt6i_nh_weight;
> -               gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
> +               ifindex = mlxsw_sp_rt6->rt->fib6_nh.nh_dev->ifindex;
> +               weight = mlxsw_sp_rt6->rt->fib6_nh.nh_weight;
> +               gw = &mlxsw_sp_rt6->rt->fib6_nh.nh_gw;
>                 if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex,
>                                                          weight))
>                         return false;
> @@ -2768,7 +2768,7 @@ mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
>         struct net_device *dev;
>
>         list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
> -               dev = mlxsw_sp_rt6->rt->dst.dev;
> +               dev = mlxsw_sp_rt6->rt->fib6_nh.nh_dev;
>                 val ^= dev->ifindex;
>         }
>
> @@ -3766,9 +3766,9 @@ mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
>                 struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
>                 struct rt6_info *rt = mlxsw_sp_rt6->rt;
>
> -               if (nh->rif && nh->rif->dev == rt->dst.dev &&
> +               if (nh->rif && nh->rif->dev == rt->fib6_nh.nh_dev &&
>                     ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
> -                                   &rt->rt6i_gateway))
> +                                   &rt->fib6_nh.nh_gw))
>                         return nh;
>                 continue;
>         }
> @@ -3825,7 +3825,7 @@ mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
>
>         if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
>                 list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
> -                                list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
> +                                list)->rt->fib6_nh.nh_flags |= RTNH_F_OFFLOAD;
>                 return;
>         }
>
> @@ -3835,9 +3835,9 @@ mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
>
>                 nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
>                 if (nh && nh->offloaded)
> -                       mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
> +                       mlxsw_sp_rt6->rt->fib6_nh.nh_flags |= RTNH_F_OFFLOAD;
>                 else
> -                       mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
> +                       mlxsw_sp_rt6->rt->fib6_nh.nh_flags &= ~RTNH_F_OFFLOAD;
>         }
>  }
>
> @@ -3852,7 +3852,7 @@ mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
>         list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
>                 struct rt6_info *rt = mlxsw_sp_rt6->rt;
>
> -               rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
> +               rt->fib6_nh.nh_flags &= ~RTNH_F_OFFLOAD;
>         }
>  }
>
> @@ -4748,8 +4748,8 @@ static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
>                                         const struct rt6_info *rt,
>                                         enum mlxsw_sp_ipip_type *ret)
>  {
> -       return rt->dst.dev &&
> -              mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
> +       return rt->fib6_nh.nh_dev &&
> +              mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->fib6_nh.nh_dev, ret);
>  }
>
>  static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
> @@ -4759,7 +4759,7 @@ static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
>  {
>         const struct mlxsw_sp_ipip_ops *ipip_ops;
>         struct mlxsw_sp_ipip_entry *ipip_entry;
> -       struct net_device *dev = rt->dst.dev;
> +       struct net_device *dev = rt->fib6_nh.nh_dev;
>         struct mlxsw_sp_rif *rif;
>         int err;
>
> @@ -4802,11 +4802,11 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
>                                   struct mlxsw_sp_nexthop *nh,
>                                   const struct rt6_info *rt)
>  {
> -       struct net_device *dev = rt->dst.dev;
> +       struct net_device *dev = rt->fib6_nh.nh_dev;
>
>         nh->nh_grp = nh_grp;
> -       nh->nh_weight = rt->rt6i_nh_weight;
> -       memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
> +       nh->nh_weight = rt->fib6_nh.nh_weight;
> +       memcpy(&nh->gw_addr, &rt->fib6_nh.nh_gw, sizeof(nh->gw_addr));
>         mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
>
>         list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
> diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
> index 4badcf105bd9..7678ae3de44a 100644
> --- a/include/net/ip6_fib.h
> +++ b/include/net/ip6_fib.h
> @@ -127,6 +127,16 @@ struct rt6_exception {
>  #define FIB6_EXCEPTION_BUCKET_SIZE (1 << FIB6_EXCEPTION_BUCKET_SIZE_SHIFT)
>  #define FIB6_MAX_DEPTH 5
>
> +struct fib6_nh {
> +       struct in6_addr         nh_gw;
> +       struct net_device       *nh_dev;
> +       struct lwtunnel_state   *nh_lwtstate;
> +
> +       unsigned int            nh_flags;
> +       atomic_t                nh_upper_bound;
> +       int                     nh_weight;
> +};
> +
>  struct rt6_info {
>         struct dst_entry                dst;
>         struct rt6_info __rcu           *rt6_next;
> @@ -149,12 +159,9 @@ struct rt6_info {
>          */
>         struct list_head                rt6i_siblings;
>         unsigned int                    rt6i_nsiblings;
> -       atomic_t                        rt6i_nh_upper_bound;
>
>         atomic_t                        rt6i_ref;
>
> -       unsigned int                    rt6i_nh_flags;
> -
>         /* These are in a separate cache line. */
>         struct rt6key                   rt6i_dst ____cacheline_aligned_in_smp;
>         u32                             rt6i_flags;
> @@ -171,13 +178,14 @@ struct rt6_info {
>         u32                             rt6i_metric;
>         u32                             rt6i_pmtu;
>         /* more non-fragment space at head required */
> -       int                             rt6i_nh_weight;
>         unsigned short                  rt6i_nfheader_len;
>         u8                              rt6i_protocol;
>         u8                              fib6_type;
>         u8                              exception_bucket_flushed:1,
>                                         should_flush:1,
>                                         unused:6;
> +
> +       struct fib6_nh                  fib6_nh;
>  };
>
>  #define for_each_fib6_node_rt_rcu(fn)                                  \
> diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
> index ef0d8977e2d0..bd82ec4a57e6 100644
> --- a/include/net/ip6_route.h
> +++ b/include/net/ip6_route.h
> @@ -263,9 +263,9 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt,
>
>  static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b)
>  {
> -       return a->dst.dev == b->dst.dev &&
> +       return a->fib6_nh.nh_dev == b->fib6_nh.nh_dev &&
>                a->rt6i_idev == b->rt6i_idev &&
> -              ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
> -              !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
> +              ipv6_addr_equal(&a->fib6_nh.nh_gw, &b->fib6_nh.nh_gw) &&
> +              !lwtunnel_cmp_encap(a->fib6_nh.nh_lwtstate, b->fib6_nh.nh_lwtstate);
>  }
>  #endif
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 82862d3f0295..eeecef2b83a4 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2344,7 +2344,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
>                 goto out;
>
>         for_each_fib6_node_rt_rcu(fn) {
> -               if (rt->dst.dev->ifindex != dev->ifindex)
> +               if (rt->fib6_nh.nh_dev->ifindex != dev->ifindex)
>                         continue;
>                 if ((rt->rt6i_flags & flags) != flags)
>                         continue;
> diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
> index 51d2d5a7ba89..134d86483f3c 100644
> --- a/net/ipv6/ip6_fib.c
> +++ b/net/ipv6/ip6_fib.c
> @@ -2213,6 +2213,7 @@ static int ipv6_route_seq_show(struct seq_file *seq, void *v)
>  {
>         struct rt6_info *rt = v;
>         struct ipv6_route_iter *iter = seq->private;
> +       const struct net_device *dev;
>
>         seq_printf(seq, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
>
> @@ -2222,14 +2223,15 @@ static int ipv6_route_seq_show(struct seq_file *seq, void *v)
>         seq_puts(seq, "00000000000000000000000000000000 00 ");
>  #endif
>         if (rt->rt6i_flags & RTF_GATEWAY)
> -               seq_printf(seq, "%pi6", &rt->rt6i_gateway);
> +               seq_printf(seq, "%pi6", &rt->fib6_nh.nh_gw);
>         else
>                 seq_puts(seq, "00000000000000000000000000000000");
>
> +       dev = rt->fib6_nh.nh_dev;
>         seq_printf(seq, " %08x %08x %08x %08x %8s\n",
>                    rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
>                    rt->dst.__use, rt->rt6i_flags,
> -                  rt->dst.dev ? rt->dst.dev->name : "");
> +                  dev ? dev->name : "");
>         iter->w.leaf = NULL;
>         return 0;
>  }
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index 2de9af0bead1..ff809ee930c7 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -464,12 +464,15 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
>         if (!fl6->mp_hash)
>                 fl6->mp_hash = rt6_multipath_hash(fl6, NULL);
>
> -       if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound))
> +       if (fl6->mp_hash <= atomic_read(&match->fib6_nh.nh_upper_bound))
>                 return match;
>
>         list_for_each_entry_safe(sibling, next_sibling, &match->rt6i_siblings,
>                                  rt6i_siblings) {
> -               if (fl6->mp_hash > atomic_read(&sibling->rt6i_nh_upper_bound))
> +               int nh_upper_bound;
> +
> +               nh_upper_bound = atomic_read(&sibling->fib6_nh.nh_upper_bound);
> +               if (fl6->mp_hash > nh_upper_bound)
>                         continue;
>                 if (rt6_score_route(sibling, oif, strict) < 0)
>                         break;
> @@ -493,13 +496,14 @@ static inline struct rt6_info *rt6_device_match(struct net *net,
>         struct rt6_info *local = NULL;
>         struct rt6_info *sprt;
>
> -       if (!oif && ipv6_addr_any(saddr) && !(rt->rt6i_nh_flags & RTNH_F_DEAD))
> +       if (!oif && ipv6_addr_any(saddr) &&
> +           !(rt->fib6_nh.nh_flags & RTNH_F_DEAD))
>                 return rt;
>
>         for (sprt = rt; sprt; sprt = rcu_dereference(sprt->rt6_next)) {
> -               struct net_device *dev = sprt->dst.dev;
> +               const struct net_device *dev = sprt->fib6_nh.nh_dev;
>
> -               if (sprt->rt6i_nh_flags & RTNH_F_DEAD)
> +               if (sprt->fib6_nh.nh_flags & RTNH_F_DEAD)
>                         continue;
>
>                 if (oif) {
> @@ -531,7 +535,7 @@ static inline struct rt6_info *rt6_device_match(struct net *net,
>                         return net->ipv6.ip6_null_entry;
>         }
>
> -       return rt->rt6i_nh_flags & RTNH_F_DEAD ? net->ipv6.ip6_null_entry : rt;
> +       return rt->fib6_nh.nh_flags & RTNH_F_DEAD ? net->ipv6.ip6_null_entry : rt;
>  }
>
>  #ifdef CONFIG_IPV6_ROUTER_PREF
> @@ -556,7 +560,10 @@ static void rt6_probe_deferred(struct work_struct *w)
>  static void rt6_probe(struct rt6_info *rt)
>  {
>         struct __rt6_probe_work *work;
> +       const struct in6_addr *nh_gw;
>         struct neighbour *neigh;
> +       struct net_device *dev;
> +
>         /*
>          * Okay, this does not seem to be appropriate
>          * for now, however, we need to check if it
> @@ -567,8 +574,11 @@ static void rt6_probe(struct rt6_info *rt)
>          */
>         if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
>                 return;
> +
> +       nh_gw = &rt->fib6_nh.nh_gw;
> +       dev = rt->fib6_nh.nh_dev;
>         rcu_read_lock_bh();
> -       neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
> +       neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
>         if (neigh) {
>                 if (neigh->nud_state & NUD_VALID)
>                         goto out;
> @@ -590,9 +600,9 @@ static void rt6_probe(struct rt6_info *rt)
>
>         if (work) {
>                 INIT_WORK(&work->work, rt6_probe_deferred);
> -               work->target = rt->rt6i_gateway;
> -               dev_hold(rt->dst.dev);
> -               work->dev = rt->dst.dev;
> +               work->target = *nh_gw;
> +               dev_hold(dev);
> +               work->dev = dev;
>                 schedule_work(&work->work);
>         }
>
> @@ -610,7 +620,8 @@ static inline void rt6_probe(struct rt6_info *rt)
>   */
>  static inline int rt6_check_dev(struct rt6_info *rt, int oif)
>  {
> -       struct net_device *dev = rt->dst.dev;
> +       const struct net_device *dev = rt->fib6_nh.nh_dev;
> +
>         if (!oif || dev->ifindex == oif)
>                 return 2;
>         if ((dev->flags & IFF_LOOPBACK) &&
> @@ -621,15 +632,16 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
>
>  static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
>  {
> -       struct neighbour *neigh;
>         enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
> +       struct neighbour *neigh;
>
>         if (rt->rt6i_flags & RTF_NONEXTHOP ||
>             !(rt->rt6i_flags & RTF_GATEWAY))
>                 return RT6_NUD_SUCCEED;
>
>         rcu_read_lock_bh();
> -       neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
> +       neigh = __ipv6_neigh_lookup_noref(rt->fib6_nh.nh_dev,
> +                                         &rt->fib6_nh.nh_gw);
>         if (neigh) {
>                 read_lock(&neigh->lock);
>                 if (neigh->nud_state & NUD_VALID)
> @@ -677,11 +689,11 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
>         bool match_do_rr = false;
>         struct inet6_dev *idev = rt->rt6i_idev;
>
> -       if (rt->rt6i_nh_flags & RTNH_F_DEAD)
> +       if (rt->fib6_nh.nh_flags & RTNH_F_DEAD)
>                 goto out;
>
>         if (idev->cnf.ignore_routes_with_linkdown &&
> -           rt->rt6i_nh_flags & RTNH_F_LINKDOWN &&
> +           rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN &&
>             !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
>                 goto out;
>
> @@ -886,7 +898,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
>  /* called with rcu_lock held */
>  static struct net_device *ip6_rt_get_dev_rcu(struct rt6_info *rt)
>  {
> -       struct net_device *dev = rt->dst.dev;
> +       struct net_device *dev = rt->fib6_nh.nh_dev;
>
>         if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) {
>                 /* for copies of local routes, dst->dev needs to be the
> @@ -926,7 +938,7 @@ static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
>         if (rt->rt6i_idev)
>                 in6_dev_hold(rt->rt6i_idev);
>         rt->dst.lastuse = jiffies;
> -       rt->rt6i_gateway = ort->rt6i_gateway;
> +       rt->rt6i_gateway = ort->fib6_nh.nh_gw;
>         rt->rt6i_flags = ort->rt6i_flags;
>         rt6_set_from(rt, ort);
>         rt->rt6i_metric = ort->rt6i_metric;
> @@ -935,7 +947,7 @@ static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
>  #endif
>         rt->rt6i_prefsrc = ort->rt6i_prefsrc;
>         rt->rt6i_table = ort->rt6i_table;
> -       rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
> +       rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.nh_lwtstate);
>  }
>
>  static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
> @@ -1300,7 +1312,7 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
>  static int rt6_insert_exception(struct rt6_info *nrt,
>                                 struct rt6_info *ort)
>  {
> -       struct net *net = dev_net(ort->dst.dev);
> +       struct net *net = dev_net(nrt->dst.dev);
>         struct rt6_exception_bucket *bucket;
>         struct in6_addr *src_key = NULL;
>         struct rt6_exception *rt6_ex;
> @@ -2194,7 +2206,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
>         fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
>  restart:
>         for_each_fib6_node_rt_rcu(fn) {
> -               if (rt->rt6i_nh_flags & RTNH_F_DEAD)
> +               if (rt->fib6_nh.nh_flags & RTNH_F_DEAD)
>                         continue;
>                 if (rt6_check_expired(rt))
>                         continue;
> @@ -2202,14 +2214,14 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
>                         break;
>                 if (!(rt->rt6i_flags & RTF_GATEWAY))
>                         continue;
> -               if (fl6->flowi6_oif != rt->dst.dev->ifindex)
> +               if (fl6->flowi6_oif != rt->fib6_nh.nh_dev->ifindex)
>                         continue;
>                 /* rt_cache's gateway might be different from its 'parent'
>                  * in the case of an ip redirect.
>                  * So we keep searching in the exception table if the gateway
>                  * is different.
>                  */
> -               if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) {
> +               if (!ipv6_addr_equal(&rdfl->gateway, &rt->fib6_nh.nh_gw)) {
>                         rt_cache = rt6_find_cached_rt(rt,
>                                                       &fl6->daddr,
>                                                       &fl6->saddr);
> @@ -2712,7 +2724,7 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
>                                            &lwtstate, extack);
>                 if (err)
>                         goto out;
> -               rt->dst.lwtstate = lwtstate_get(lwtstate);
> +               rt->fib6_nh.nh_lwtstate = lwtstate_get(lwtstate);
>                 lwtunnel_set_redirect(&rt->dst);
>         }
>
> @@ -2727,7 +2739,7 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
>  #endif
>
>         rt->rt6i_metric = cfg->fc_metric;
> -       rt->rt6i_nh_weight = 1;
> +       rt->fib6_nh.nh_weight = 1;
>
>         rt->fib6_type = cfg->fc_type;
>
> @@ -2796,7 +2808,9 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
>                         NL_SET_ERR_MSG(extack, "Invalid gateway address");
>                         goto out;
>                 }
> -               rt->rt6i_gateway = *gw_addr;
> +               rt->fib6_nh.nh_gw = *gw_addr;
> +
> +               rt->rt6i_gateway = rt->fib6_nh.nh_gw;
>
>                 if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
>                         /* IPv6 strictly inhibits using not link-local
> @@ -2861,9 +2875,9 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
>  install_route:
>         if (!(rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
>             !netif_carrier_ok(dev))
> -               rt->rt6i_nh_flags |= RTNH_F_LINKDOWN;
> -       rt->rt6i_nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK);
> -       rt->dst.dev = dev;
> +               rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN;
> +       rt->fib6_nh.nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK);
> +       rt->fib6_nh.nh_dev = rt->dst.dev = dev;
>         rt->rt6i_idev = idev;
>         rt->rt6i_table = table;
>
> @@ -3022,11 +3036,11 @@ static int ip6_route_del(struct fib6_config *cfg,
>                                 rt = rt_cache;
>                         }
>                         if (cfg->fc_ifindex &&
> -                           (!rt->dst.dev ||
> -                            rt->dst.dev->ifindex != cfg->fc_ifindex))
> +                           (!rt->fib6_nh.nh_dev ||
> +                            rt->fib6_nh.nh_dev->ifindex != cfg->fc_ifindex))
>                                 continue;
>                         if (cfg->fc_flags & RTF_GATEWAY &&
> -                           !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
> +                           !ipv6_addr_equal(&cfg->fc_gateway, &rt->fib6_nh.nh_gw))
>                                 continue;
>                         if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
>                                 continue;
> @@ -3188,11 +3202,11 @@ static struct rt6_info *rt6_get_route_info(struct net *net,
>                 goto out;
>
>         for_each_fib6_node_rt_rcu(fn) {
> -               if (rt->dst.dev->ifindex != ifindex)
> +               if (rt->fib6_nh.nh_dev->ifindex != ifindex)
>                         continue;
>                 if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
>                         continue;
> -               if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
> +               if (!ipv6_addr_equal(&rt->fib6_nh.nh_gw, gwaddr))
>                         continue;
>                 ip6_hold_safe(NULL, &rt, false);
>                 break;
> @@ -3249,9 +3263,9 @@ struct rt6_info *rt6_get_dflt_router(struct net *net,
>
>         rcu_read_lock();
>         for_each_fib6_node_rt_rcu(&table->tb6_root) {
> -               if (dev == rt->dst.dev &&
> +               if (dev == rt->fib6_nh.nh_dev &&
>                     ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
> -                   ipv6_addr_equal(&rt->rt6i_gateway, addr))
> +                   ipv6_addr_equal(&rt->fib6_nh.nh_gw, addr))
>                         break;
>         }
>         if (rt)
> @@ -3477,6 +3491,8 @@ struct rt6_info *addrconf_dst_alloc(struct net *net,
>                 rt->rt6i_flags |= RTF_LOCAL;
>         }
>
> +       rt->fib6_nh.nh_gw = *addr;
> +       rt->fib6_nh.nh_dev = dev;
>         rt->rt6i_gateway  = *addr;
>         rt->rt6i_dst.addr = *addr;
>         rt->rt6i_dst.plen = 128;
> @@ -3499,7 +3515,7 @@ static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
>         struct net *net = ((struct arg_dev_net_ip *)arg)->net;
>         struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
>
> -       if (((void *)rt->dst.dev == dev || !dev) &&
> +       if (((void *)rt->fib6_nh.nh_dev == dev || !dev) &&
>             rt != net->ipv6.ip6_null_entry &&
>             ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
>                 spin_lock_bh(&rt6_exception_lock);
> @@ -3531,7 +3547,7 @@ static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
>         struct in6_addr *gateway = (struct in6_addr *)arg;
>
>         if (((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
> -           ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
> +           ipv6_addr_equal(gateway, &rt->fib6_nh.nh_gw)) {
>                 return -1;
>         }
>
> @@ -3579,8 +3595,8 @@ static struct rt6_info *rt6_multipath_first_sibling(const struct rt6_info *rt)
>
>  static bool rt6_is_dead(const struct rt6_info *rt)
>  {
> -       if (rt->rt6i_nh_flags & RTNH_F_DEAD ||
> -           (rt->rt6i_nh_flags & RTNH_F_LINKDOWN &&
> +       if (rt->fib6_nh.nh_flags & RTNH_F_DEAD ||
> +           (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN &&
>              rt->rt6i_idev->cnf.ignore_routes_with_linkdown))
>                 return true;
>
> @@ -3593,11 +3609,11 @@ static int rt6_multipath_total_weight(const struct rt6_info *rt)
>         int total = 0;
>
>         if (!rt6_is_dead(rt))
> -               total += rt->rt6i_nh_weight;
> +               total += rt->fib6_nh.nh_weight;
>
>         list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) {
>                 if (!rt6_is_dead(iter))
> -                       total += iter->rt6i_nh_weight;
> +                       total += iter->fib6_nh.nh_weight;
>         }
>
>         return total;
> @@ -3608,11 +3624,11 @@ static void rt6_upper_bound_set(struct rt6_info *rt, int *weight, int total)
>         int upper_bound = -1;
>
>         if (!rt6_is_dead(rt)) {
> -               *weight += rt->rt6i_nh_weight;
> +               *weight += rt->fib6_nh.nh_weight;
>                 upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
>                                                     total) - 1;
>         }
> -       atomic_set(&rt->rt6i_nh_upper_bound, upper_bound);
> +       atomic_set(&rt->fib6_nh.nh_upper_bound, upper_bound);
>  }
>
>  static void rt6_multipath_upper_bound_set(struct rt6_info *rt, int total)
> @@ -3655,8 +3671,8 @@ static int fib6_ifup(struct rt6_info *rt, void *p_arg)
>         const struct arg_netdev_event *arg = p_arg;
>         struct net *net = dev_net(arg->dev);
>
> -       if (rt != net->ipv6.ip6_null_entry && rt->dst.dev == arg->dev) {
> -               rt->rt6i_nh_flags &= ~arg->nh_flags;
> +       if (rt != net->ipv6.ip6_null_entry && rt->fib6_nh.nh_dev == arg->dev) {
> +               rt->fib6_nh.nh_flags &= ~arg->nh_flags;
>                 fib6_update_sernum_upto_root(net, rt);
>                 rt6_multipath_rebalance(rt);
>         }
> @@ -3684,10 +3700,10 @@ static bool rt6_multipath_uses_dev(const struct rt6_info *rt,
>  {
>         struct rt6_info *iter;
>
> -       if (rt->dst.dev == dev)
> +       if (rt->fib6_nh.nh_dev == dev)
>                 return true;
>         list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings)
> -               if (iter->dst.dev == dev)
> +               if (iter->fib6_nh.nh_dev == dev)
>                         return true;
>
>         return false;
> @@ -3708,11 +3724,12 @@ static unsigned int rt6_multipath_dead_count(const struct rt6_info *rt,
>         struct rt6_info *iter;
>         unsigned int dead = 0;
>
> -       if (rt->dst.dev == down_dev || rt->rt6i_nh_flags & RTNH_F_DEAD)
> +       if (rt->fib6_nh.nh_dev == down_dev ||
> +           rt->fib6_nh.nh_flags & RTNH_F_DEAD)
>                 dead++;
>         list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings)
> -               if (iter->dst.dev == down_dev ||
> -                   iter->rt6i_nh_flags & RTNH_F_DEAD)
> +               if (iter->fib6_nh.nh_dev == down_dev ||
> +                   iter->fib6_nh.nh_flags & RTNH_F_DEAD)
>                         dead++;
>
>         return dead;
> @@ -3724,11 +3741,11 @@ static void rt6_multipath_nh_flags_set(struct rt6_info *rt,
>  {
>         struct rt6_info *iter;
>
> -       if (rt->dst.dev == dev)
> -               rt->rt6i_nh_flags |= nh_flags;
> +       if (rt->fib6_nh.nh_dev == dev)
> +               rt->fib6_nh.nh_flags |= nh_flags;
>         list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings)
> -               if (iter->dst.dev == dev)
> -                       iter->rt6i_nh_flags |= nh_flags;
> +               if (iter->fib6_nh.nh_dev == dev)
> +                       iter->fib6_nh.nh_flags |= nh_flags;
>  }
>
>  /* called with write lock held for table with rt */
> @@ -3743,12 +3760,12 @@ static int fib6_ifdown(struct rt6_info *rt, void *p_arg)
>
>         switch (arg->event) {
>         case NETDEV_UNREGISTER:
> -               return rt->dst.dev == dev ? -1 : 0;
> +               return rt->fib6_nh.nh_dev == dev ? -1 : 0;
>         case NETDEV_DOWN:
>                 if (rt->should_flush)
>                         return -1;
>                 if (!rt->rt6i_nsiblings)
> -                       return rt->dst.dev == dev ? -1 : 0;
> +                       return rt->fib6_nh.nh_dev == dev ? -1 : 0;
>                 if (rt6_multipath_uses_dev(rt, dev)) {
>                         unsigned int count;
>
> @@ -3764,10 +3781,10 @@ static int fib6_ifdown(struct rt6_info *rt, void *p_arg)
>                 }
>                 return -2;
>         case NETDEV_CHANGE:
> -               if (rt->dst.dev != dev ||
> +               if (rt->fib6_nh.nh_dev != dev ||
>                     rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
>                         break;
> -               rt->rt6i_nh_flags |= RTNH_F_LINKDOWN;
> +               rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN;
>                 rt6_multipath_rebalance(rt);
>                 break;
>         }
> @@ -3828,7 +3845,7 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
>            also have the lowest MTU, TOO BIG MESSAGE will be lead to
>            PMTU discovery.
>          */
> -       if (rt->dst.dev == arg->dev &&
> +       if (rt->fib6_nh.nh_dev == arg->dev &&
>             dst_metric_raw(&rt->dst, RTAX_MTU) &&
>             !dst_metric_locked(&rt->dst, RTAX_MTU)) {
>                 spin_lock_bh(&rt6_exception_lock);
> @@ -4116,7 +4133,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
>                         goto cleanup;
>                 }
>
> -               rt->rt6i_nh_weight = rtnh->rtnh_hops + 1;
> +               rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1;
>
>                 err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
>                 if (err) {
> @@ -4273,7 +4290,7 @@ static size_t rt6_nlmsg_size(struct rt6_info *rt)
>                 nexthop_len = nla_total_size(0)  /* RTA_MULTIPATH */
>                             + NLA_ALIGN(sizeof(struct rtnexthop))
>                             + nla_total_size(16) /* RTA_GATEWAY */
> -                           + lwtunnel_get_encap_size(rt->dst.lwtstate);
> +                           + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate);
>
>                 nexthop_len *= rt->rt6i_nsiblings;
>         }
> @@ -4291,38 +4308,38 @@ static size_t rt6_nlmsg_size(struct rt6_info *rt)
>                + nla_total_size(sizeof(struct rta_cacheinfo))
>                + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
>                + nla_total_size(1) /* RTA_PREF */
> -              + lwtunnel_get_encap_size(rt->dst.lwtstate)
> +              + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate)
>                + nexthop_len;
>  }
>
>  static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt,
>                             unsigned int *flags, bool skip_oif)
>  {
> -       if (rt->rt6i_nh_flags & RTNH_F_DEAD)
> +       if (rt->fib6_nh.nh_flags & RTNH_F_DEAD)
>                 *flags |= RTNH_F_DEAD;
>
> -       if (rt->rt6i_nh_flags & RTNH_F_LINKDOWN) {
> +       if (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN) {
>                 *flags |= RTNH_F_LINKDOWN;
>                 if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
>                         *flags |= RTNH_F_DEAD;
>         }
>
>         if (rt->rt6i_flags & RTF_GATEWAY) {
> -               if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
> +               if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->fib6_nh.nh_gw) < 0)
>                         goto nla_put_failure;
>         }
>
> -       *flags |= (rt->rt6i_nh_flags & RTNH_F_ONLINK);
> -       if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD)
> +       *flags |= (rt->fib6_nh.nh_flags & RTNH_F_ONLINK);
> +       if (rt->fib6_nh.nh_flags & RTNH_F_OFFLOAD)
>                 *flags |= RTNH_F_OFFLOAD;
>
>         /* not needed for multipath encoding b/c it has a rtnexthop struct */
> -       if (!skip_oif && rt->dst.dev &&
> -           nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
> +       if (!skip_oif && rt->fib6_nh.nh_dev &&
> +           nla_put_u32(skb, RTA_OIF, rt->fib6_nh.nh_dev->ifindex))
>                 goto nla_put_failure;
>
> -       if (rt->dst.lwtstate &&
> -           lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
> +       if (rt->fib6_nh.nh_lwtstate &&
> +           lwtunnel_fill_encap(skb, rt->fib6_nh.nh_lwtstate) < 0)
>                 goto nla_put_failure;
>
>         return 0;
> @@ -4334,6 +4351,7 @@ static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt,
>  /* add multipath next hop */
>  static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
>  {
> +       const struct net_device *dev = rt->fib6_nh.nh_dev;
>         struct rtnexthop *rtnh;
>         unsigned int flags = 0;
>
> @@ -4341,8 +4359,8 @@ static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
>         if (!rtnh)
>                 goto nla_put_failure;
>
> -       rtnh->rtnh_hops = rt->rt6i_nh_weight - 1;
> -       rtnh->rtnh_ifindex = rt->dst.dev ? rt->dst.dev->ifindex : 0;
> +       rtnh->rtnh_hops = rt->fib6_nh.nh_weight - 1;
> +       rtnh->rtnh_ifindex = dev ? dev->ifindex : 0;
>
>         if (rt6_nexthop_info(skb, rt, &flags, true) < 0)
>                 goto nla_put_failure;
> --
> 2.11.0
>
David Ahern Feb. 26, 2018, 10:47 p.m. UTC | #2
On 2/26/18 3:28 PM, Wei Wang wrote:
> On Sun, Feb 25, 2018 at 11:47 AM, David Ahern <dsahern@gmail.com> wrote:
>> Introduce fib6_nh structure and move nexthop related data from
>> rt6_info and rt6_info.dst to fib6_nh. References to dev, gateway or
>> lwtstate from a FIB lookup perspective are converted to use fib6_nh;
>> datapath references to dst version are left as is.
>>
> 
> My understanding is that after your whole patch series, sibling routes
> will still have their own fib6_info. Does it make sense to make this
> fib6_nh as an array in fib6_info so that sibling routes will share
> fib6_info but will have their own fib6_nh as a future improvement? It
> matches ipv4 behavior. And I think it will make the sibling route
> handling code easier?

I was not planning to. IPv6 allowing individual nexthops to be added and
deleted is very convenient. I do agree the existing sibling route
linkage makes the code much more complicated than it needs to be.

After this set, I plan to send patches for nexthops as separate objects
- which will have an impact on how multipath routes are done. With
nexthop objects there will be 1 prefix route pointing to a nexthop
object that is multipath (meaning it points in turn to a series of
nexthop objects). This provides the simplification (no sibling linkage)
without losing the individual nexhtop add / delete option.
Wei Wang Feb. 26, 2018, 11:05 p.m. UTC | #3
On Mon, Feb 26, 2018 at 2:47 PM, David Ahern <dsahern@gmail.com> wrote:
> On 2/26/18 3:28 PM, Wei Wang wrote:
>> On Sun, Feb 25, 2018 at 11:47 AM, David Ahern <dsahern@gmail.com> wrote:
>>> Introduce fib6_nh structure and move nexthop related data from
>>> rt6_info and rt6_info.dst to fib6_nh. References to dev, gateway or
>>> lwtstate from a FIB lookup perspective are converted to use fib6_nh;
>>> datapath references to dst version are left as is.
>>>
>>
>> My understanding is that after your whole patch series, sibling routes
>> will still have their own fib6_info. Does it make sense to make this
>> fib6_nh as an array in fib6_info so that sibling routes will share
>> fib6_info but will have their own fib6_nh as a future improvement? It
>> matches ipv4 behavior. And I think it will make the sibling route
>> handling code easier?
>
> I was not planning to. IPv6 allowing individual nexthops to be added and
> deleted is very convenient. I do agree the existing sibling route
> linkage makes the code much more complicated than it needs to be.
>
> After this set, I plan to send patches for nexthops as separate objects
> - which will have an impact on how multipath routes are done. With
> nexthop objects there will be 1 prefix route pointing to a nexthop
> object that is multipath (meaning it points in turn to a series of
> nexthop objects). This provides the simplification (no sibling linkage)
> without losing the individual nexhtop add / delete option.

Got it. Thanks for the explanation.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 05146970c19c..90d01df783b3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -2700,9 +2700,9 @@  mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp,
 		struct in6_addr *gw;
 		int ifindex, weight;
 
-		ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex;
-		weight = mlxsw_sp_rt6->rt->rt6i_nh_weight;
-		gw = &mlxsw_sp_rt6->rt->rt6i_gateway;
+		ifindex = mlxsw_sp_rt6->rt->fib6_nh.nh_dev->ifindex;
+		weight = mlxsw_sp_rt6->rt->fib6_nh.nh_weight;
+		gw = &mlxsw_sp_rt6->rt->fib6_nh.nh_gw;
 		if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex,
 							 weight))
 			return false;
@@ -2768,7 +2768,7 @@  mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
 	struct net_device *dev;
 
 	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
-		dev = mlxsw_sp_rt6->rt->dst.dev;
+		dev = mlxsw_sp_rt6->rt->fib6_nh.nh_dev;
 		val ^= dev->ifindex;
 	}
 
@@ -3766,9 +3766,9 @@  mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
 		struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i];
 		struct rt6_info *rt = mlxsw_sp_rt6->rt;
 
-		if (nh->rif && nh->rif->dev == rt->dst.dev &&
+		if (nh->rif && nh->rif->dev == rt->fib6_nh.nh_dev &&
 		    ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr,
-				    &rt->rt6i_gateway))
+				    &rt->fib6_nh.nh_gw))
 			return nh;
 		continue;
 	}
@@ -3825,7 +3825,7 @@  mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
 
 	if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
 		list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
-				 list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
+				 list)->rt->fib6_nh.nh_flags |= RTNH_F_OFFLOAD;
 		return;
 	}
 
@@ -3835,9 +3835,9 @@  mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
 
 		nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6);
 		if (nh && nh->offloaded)
-			mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD;
+			mlxsw_sp_rt6->rt->fib6_nh.nh_flags |= RTNH_F_OFFLOAD;
 		else
-			mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
+			mlxsw_sp_rt6->rt->fib6_nh.nh_flags &= ~RTNH_F_OFFLOAD;
 	}
 }
 
@@ -3852,7 +3852,7 @@  mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry)
 	list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
 		struct rt6_info *rt = mlxsw_sp_rt6->rt;
 
-		rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD;
+		rt->fib6_nh.nh_flags &= ~RTNH_F_OFFLOAD;
 	}
 }
 
@@ -4748,8 +4748,8 @@  static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp,
 					const struct rt6_info *rt,
 					enum mlxsw_sp_ipip_type *ret)
 {
-	return rt->dst.dev &&
-	       mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret);
+	return rt->fib6_nh.nh_dev &&
+	       mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->fib6_nh.nh_dev, ret);
 }
 
 static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
@@ -4759,7 +4759,7 @@  static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp,
 {
 	const struct mlxsw_sp_ipip_ops *ipip_ops;
 	struct mlxsw_sp_ipip_entry *ipip_entry;
-	struct net_device *dev = rt->dst.dev;
+	struct net_device *dev = rt->fib6_nh.nh_dev;
 	struct mlxsw_sp_rif *rif;
 	int err;
 
@@ -4802,11 +4802,11 @@  static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
 				  struct mlxsw_sp_nexthop *nh,
 				  const struct rt6_info *rt)
 {
-	struct net_device *dev = rt->dst.dev;
+	struct net_device *dev = rt->fib6_nh.nh_dev;
 
 	nh->nh_grp = nh_grp;
-	nh->nh_weight = rt->rt6i_nh_weight;
-	memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr));
+	nh->nh_weight = rt->fib6_nh.nh_weight;
+	memcpy(&nh->gw_addr, &rt->fib6_nh.nh_gw, sizeof(nh->gw_addr));
 	mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
 
 	list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list);
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 4badcf105bd9..7678ae3de44a 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -127,6 +127,16 @@  struct rt6_exception {
 #define FIB6_EXCEPTION_BUCKET_SIZE (1 << FIB6_EXCEPTION_BUCKET_SIZE_SHIFT)
 #define FIB6_MAX_DEPTH 5
 
+struct fib6_nh {
+	struct in6_addr		nh_gw;
+	struct net_device	*nh_dev;
+	struct lwtunnel_state	*nh_lwtstate;
+
+	unsigned int		nh_flags;
+	atomic_t		nh_upper_bound;
+	int			nh_weight;
+};
+
 struct rt6_info {
 	struct dst_entry		dst;
 	struct rt6_info __rcu		*rt6_next;
@@ -149,12 +159,9 @@  struct rt6_info {
 	 */
 	struct list_head		rt6i_siblings;
 	unsigned int			rt6i_nsiblings;
-	atomic_t			rt6i_nh_upper_bound;
 
 	atomic_t			rt6i_ref;
 
-	unsigned int			rt6i_nh_flags;
-
 	/* These are in a separate cache line. */
 	struct rt6key			rt6i_dst ____cacheline_aligned_in_smp;
 	u32				rt6i_flags;
@@ -171,13 +178,14 @@  struct rt6_info {
 	u32				rt6i_metric;
 	u32				rt6i_pmtu;
 	/* more non-fragment space at head required */
-	int				rt6i_nh_weight;
 	unsigned short			rt6i_nfheader_len;
 	u8				rt6i_protocol;
 	u8				fib6_type;
 	u8				exception_bucket_flushed:1,
 					should_flush:1,
 					unused:6;
+
+	struct fib6_nh			fib6_nh;
 };
 
 #define for_each_fib6_node_rt_rcu(fn)					\
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index ef0d8977e2d0..bd82ec4a57e6 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -263,9 +263,9 @@  static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt,
 
 static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b)
 {
-	return a->dst.dev == b->dst.dev &&
+	return a->fib6_nh.nh_dev == b->fib6_nh.nh_dev &&
 	       a->rt6i_idev == b->rt6i_idev &&
-	       ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
-	       !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
+	       ipv6_addr_equal(&a->fib6_nh.nh_gw, &b->fib6_nh.nh_gw) &&
+	       !lwtunnel_cmp_encap(a->fib6_nh.nh_lwtstate, b->fib6_nh.nh_lwtstate);
 }
 #endif
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 82862d3f0295..eeecef2b83a4 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2344,7 +2344,7 @@  static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
 		goto out;
 
 	for_each_fib6_node_rt_rcu(fn) {
-		if (rt->dst.dev->ifindex != dev->ifindex)
+		if (rt->fib6_nh.nh_dev->ifindex != dev->ifindex)
 			continue;
 		if ((rt->rt6i_flags & flags) != flags)
 			continue;
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 51d2d5a7ba89..134d86483f3c 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -2213,6 +2213,7 @@  static int ipv6_route_seq_show(struct seq_file *seq, void *v)
 {
 	struct rt6_info *rt = v;
 	struct ipv6_route_iter *iter = seq->private;
+	const struct net_device *dev;
 
 	seq_printf(seq, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
 
@@ -2222,14 +2223,15 @@  static int ipv6_route_seq_show(struct seq_file *seq, void *v)
 	seq_puts(seq, "00000000000000000000000000000000 00 ");
 #endif
 	if (rt->rt6i_flags & RTF_GATEWAY)
-		seq_printf(seq, "%pi6", &rt->rt6i_gateway);
+		seq_printf(seq, "%pi6", &rt->fib6_nh.nh_gw);
 	else
 		seq_puts(seq, "00000000000000000000000000000000");
 
+	dev = rt->fib6_nh.nh_dev;
 	seq_printf(seq, " %08x %08x %08x %08x %8s\n",
 		   rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
 		   rt->dst.__use, rt->rt6i_flags,
-		   rt->dst.dev ? rt->dst.dev->name : "");
+		   dev ? dev->name : "");
 	iter->w.leaf = NULL;
 	return 0;
 }
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 2de9af0bead1..ff809ee930c7 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -464,12 +464,15 @@  static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
 	if (!fl6->mp_hash)
 		fl6->mp_hash = rt6_multipath_hash(fl6, NULL);
 
-	if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound))
+	if (fl6->mp_hash <= atomic_read(&match->fib6_nh.nh_upper_bound))
 		return match;
 
 	list_for_each_entry_safe(sibling, next_sibling, &match->rt6i_siblings,
 				 rt6i_siblings) {
-		if (fl6->mp_hash > atomic_read(&sibling->rt6i_nh_upper_bound))
+		int nh_upper_bound;
+
+		nh_upper_bound = atomic_read(&sibling->fib6_nh.nh_upper_bound);
+		if (fl6->mp_hash > nh_upper_bound)
 			continue;
 		if (rt6_score_route(sibling, oif, strict) < 0)
 			break;
@@ -493,13 +496,14 @@  static inline struct rt6_info *rt6_device_match(struct net *net,
 	struct rt6_info *local = NULL;
 	struct rt6_info *sprt;
 
-	if (!oif && ipv6_addr_any(saddr) && !(rt->rt6i_nh_flags & RTNH_F_DEAD))
+	if (!oif && ipv6_addr_any(saddr) &&
+	    !(rt->fib6_nh.nh_flags & RTNH_F_DEAD))
 		return rt;
 
 	for (sprt = rt; sprt; sprt = rcu_dereference(sprt->rt6_next)) {
-		struct net_device *dev = sprt->dst.dev;
+		const struct net_device *dev = sprt->fib6_nh.nh_dev;
 
-		if (sprt->rt6i_nh_flags & RTNH_F_DEAD)
+		if (sprt->fib6_nh.nh_flags & RTNH_F_DEAD)
 			continue;
 
 		if (oif) {
@@ -531,7 +535,7 @@  static inline struct rt6_info *rt6_device_match(struct net *net,
 			return net->ipv6.ip6_null_entry;
 	}
 
-	return rt->rt6i_nh_flags & RTNH_F_DEAD ? net->ipv6.ip6_null_entry : rt;
+	return rt->fib6_nh.nh_flags & RTNH_F_DEAD ? net->ipv6.ip6_null_entry : rt;
 }
 
 #ifdef CONFIG_IPV6_ROUTER_PREF
@@ -556,7 +560,10 @@  static void rt6_probe_deferred(struct work_struct *w)
 static void rt6_probe(struct rt6_info *rt)
 {
 	struct __rt6_probe_work *work;
+	const struct in6_addr *nh_gw;
 	struct neighbour *neigh;
+	struct net_device *dev;
+
 	/*
 	 * Okay, this does not seem to be appropriate
 	 * for now, however, we need to check if it
@@ -567,8 +574,11 @@  static void rt6_probe(struct rt6_info *rt)
 	 */
 	if (!rt || !(rt->rt6i_flags & RTF_GATEWAY))
 		return;
+
+	nh_gw = &rt->fib6_nh.nh_gw;
+	dev = rt->fib6_nh.nh_dev;
 	rcu_read_lock_bh();
-	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
+	neigh = __ipv6_neigh_lookup_noref(dev, nh_gw);
 	if (neigh) {
 		if (neigh->nud_state & NUD_VALID)
 			goto out;
@@ -590,9 +600,9 @@  static void rt6_probe(struct rt6_info *rt)
 
 	if (work) {
 		INIT_WORK(&work->work, rt6_probe_deferred);
-		work->target = rt->rt6i_gateway;
-		dev_hold(rt->dst.dev);
-		work->dev = rt->dst.dev;
+		work->target = *nh_gw;
+		dev_hold(dev);
+		work->dev = dev;
 		schedule_work(&work->work);
 	}
 
@@ -610,7 +620,8 @@  static inline void rt6_probe(struct rt6_info *rt)
  */
 static inline int rt6_check_dev(struct rt6_info *rt, int oif)
 {
-	struct net_device *dev = rt->dst.dev;
+	const struct net_device *dev = rt->fib6_nh.nh_dev;
+
 	if (!oif || dev->ifindex == oif)
 		return 2;
 	if ((dev->flags & IFF_LOOPBACK) &&
@@ -621,15 +632,16 @@  static inline int rt6_check_dev(struct rt6_info *rt, int oif)
 
 static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
 {
-	struct neighbour *neigh;
 	enum rt6_nud_state ret = RT6_NUD_FAIL_HARD;
+	struct neighbour *neigh;
 
 	if (rt->rt6i_flags & RTF_NONEXTHOP ||
 	    !(rt->rt6i_flags & RTF_GATEWAY))
 		return RT6_NUD_SUCCEED;
 
 	rcu_read_lock_bh();
-	neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway);
+	neigh = __ipv6_neigh_lookup_noref(rt->fib6_nh.nh_dev,
+					  &rt->fib6_nh.nh_gw);
 	if (neigh) {
 		read_lock(&neigh->lock);
 		if (neigh->nud_state & NUD_VALID)
@@ -677,11 +689,11 @@  static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
 	bool match_do_rr = false;
 	struct inet6_dev *idev = rt->rt6i_idev;
 
-	if (rt->rt6i_nh_flags & RTNH_F_DEAD)
+	if (rt->fib6_nh.nh_flags & RTNH_F_DEAD)
 		goto out;
 
 	if (idev->cnf.ignore_routes_with_linkdown &&
-	    rt->rt6i_nh_flags & RTNH_F_LINKDOWN &&
+	    rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN &&
 	    !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE))
 		goto out;
 
@@ -886,7 +898,7 @@  int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
 /* called with rcu_lock held */
 static struct net_device *ip6_rt_get_dev_rcu(struct rt6_info *rt)
 {
-	struct net_device *dev = rt->dst.dev;
+	struct net_device *dev = rt->fib6_nh.nh_dev;
 
 	if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) {
 		/* for copies of local routes, dst->dev needs to be the
@@ -926,7 +938,7 @@  static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
 	if (rt->rt6i_idev)
 		in6_dev_hold(rt->rt6i_idev);
 	rt->dst.lastuse = jiffies;
-	rt->rt6i_gateway = ort->rt6i_gateway;
+	rt->rt6i_gateway = ort->fib6_nh.nh_gw;
 	rt->rt6i_flags = ort->rt6i_flags;
 	rt6_set_from(rt, ort);
 	rt->rt6i_metric = ort->rt6i_metric;
@@ -935,7 +947,7 @@  static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort)
 #endif
 	rt->rt6i_prefsrc = ort->rt6i_prefsrc;
 	rt->rt6i_table = ort->rt6i_table;
-	rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate);
+	rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.nh_lwtstate);
 }
 
 static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
@@ -1300,7 +1312,7 @@  __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
 static int rt6_insert_exception(struct rt6_info *nrt,
 				struct rt6_info *ort)
 {
-	struct net *net = dev_net(ort->dst.dev);
+	struct net *net = dev_net(nrt->dst.dev);
 	struct rt6_exception_bucket *bucket;
 	struct in6_addr *src_key = NULL;
 	struct rt6_exception *rt6_ex;
@@ -2194,7 +2206,7 @@  static struct rt6_info *__ip6_route_redirect(struct net *net,
 	fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
 restart:
 	for_each_fib6_node_rt_rcu(fn) {
-		if (rt->rt6i_nh_flags & RTNH_F_DEAD)
+		if (rt->fib6_nh.nh_flags & RTNH_F_DEAD)
 			continue;
 		if (rt6_check_expired(rt))
 			continue;
@@ -2202,14 +2214,14 @@  static struct rt6_info *__ip6_route_redirect(struct net *net,
 			break;
 		if (!(rt->rt6i_flags & RTF_GATEWAY))
 			continue;
-		if (fl6->flowi6_oif != rt->dst.dev->ifindex)
+		if (fl6->flowi6_oif != rt->fib6_nh.nh_dev->ifindex)
 			continue;
 		/* rt_cache's gateway might be different from its 'parent'
 		 * in the case of an ip redirect.
 		 * So we keep searching in the exception table if the gateway
 		 * is different.
 		 */
-		if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) {
+		if (!ipv6_addr_equal(&rdfl->gateway, &rt->fib6_nh.nh_gw)) {
 			rt_cache = rt6_find_cached_rt(rt,
 						      &fl6->daddr,
 						      &fl6->saddr);
@@ -2712,7 +2724,7 @@  static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
 					   &lwtstate, extack);
 		if (err)
 			goto out;
-		rt->dst.lwtstate = lwtstate_get(lwtstate);
+		rt->fib6_nh.nh_lwtstate = lwtstate_get(lwtstate);
 		lwtunnel_set_redirect(&rt->dst);
 	}
 
@@ -2727,7 +2739,7 @@  static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
 #endif
 
 	rt->rt6i_metric = cfg->fc_metric;
-	rt->rt6i_nh_weight = 1;
+	rt->fib6_nh.nh_weight = 1;
 
 	rt->fib6_type = cfg->fc_type;
 
@@ -2796,7 +2808,9 @@  static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
 			NL_SET_ERR_MSG(extack, "Invalid gateway address");
 			goto out;
 		}
-		rt->rt6i_gateway = *gw_addr;
+		rt->fib6_nh.nh_gw = *gw_addr;
+
+		rt->rt6i_gateway = rt->fib6_nh.nh_gw;
 
 		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
 			/* IPv6 strictly inhibits using not link-local
@@ -2861,9 +2875,9 @@  static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
 install_route:
 	if (!(rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) &&
 	    !netif_carrier_ok(dev))
-		rt->rt6i_nh_flags |= RTNH_F_LINKDOWN;
-	rt->rt6i_nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK);
-	rt->dst.dev = dev;
+		rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN;
+	rt->fib6_nh.nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK);
+	rt->fib6_nh.nh_dev = rt->dst.dev = dev;
 	rt->rt6i_idev = idev;
 	rt->rt6i_table = table;
 
@@ -3022,11 +3036,11 @@  static int ip6_route_del(struct fib6_config *cfg,
 				rt = rt_cache;
 			}
 			if (cfg->fc_ifindex &&
-			    (!rt->dst.dev ||
-			     rt->dst.dev->ifindex != cfg->fc_ifindex))
+			    (!rt->fib6_nh.nh_dev ||
+			     rt->fib6_nh.nh_dev->ifindex != cfg->fc_ifindex))
 				continue;
 			if (cfg->fc_flags & RTF_GATEWAY &&
-			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
+			    !ipv6_addr_equal(&cfg->fc_gateway, &rt->fib6_nh.nh_gw))
 				continue;
 			if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
 				continue;
@@ -3188,11 +3202,11 @@  static struct rt6_info *rt6_get_route_info(struct net *net,
 		goto out;
 
 	for_each_fib6_node_rt_rcu(fn) {
-		if (rt->dst.dev->ifindex != ifindex)
+		if (rt->fib6_nh.nh_dev->ifindex != ifindex)
 			continue;
 		if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
 			continue;
-		if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr))
+		if (!ipv6_addr_equal(&rt->fib6_nh.nh_gw, gwaddr))
 			continue;
 		ip6_hold_safe(NULL, &rt, false);
 		break;
@@ -3249,9 +3263,9 @@  struct rt6_info *rt6_get_dflt_router(struct net *net,
 
 	rcu_read_lock();
 	for_each_fib6_node_rt_rcu(&table->tb6_root) {
-		if (dev == rt->dst.dev &&
+		if (dev == rt->fib6_nh.nh_dev &&
 		    ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
-		    ipv6_addr_equal(&rt->rt6i_gateway, addr))
+		    ipv6_addr_equal(&rt->fib6_nh.nh_gw, addr))
 			break;
 	}
 	if (rt)
@@ -3477,6 +3491,8 @@  struct rt6_info *addrconf_dst_alloc(struct net *net,
 		rt->rt6i_flags |= RTF_LOCAL;
 	}
 
+	rt->fib6_nh.nh_gw = *addr;
+	rt->fib6_nh.nh_dev = dev;
 	rt->rt6i_gateway  = *addr;
 	rt->rt6i_dst.addr = *addr;
 	rt->rt6i_dst.plen = 128;
@@ -3499,7 +3515,7 @@  static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
 	struct net *net = ((struct arg_dev_net_ip *)arg)->net;
 	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;
 
-	if (((void *)rt->dst.dev == dev || !dev) &&
+	if (((void *)rt->fib6_nh.nh_dev == dev || !dev) &&
 	    rt != net->ipv6.ip6_null_entry &&
 	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
 		spin_lock_bh(&rt6_exception_lock);
@@ -3531,7 +3547,7 @@  static int fib6_clean_tohost(struct rt6_info *rt, void *arg)
 	struct in6_addr *gateway = (struct in6_addr *)arg;
 
 	if (((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) &&
-	    ipv6_addr_equal(gateway, &rt->rt6i_gateway)) {
+	    ipv6_addr_equal(gateway, &rt->fib6_nh.nh_gw)) {
 		return -1;
 	}
 
@@ -3579,8 +3595,8 @@  static struct rt6_info *rt6_multipath_first_sibling(const struct rt6_info *rt)
 
 static bool rt6_is_dead(const struct rt6_info *rt)
 {
-	if (rt->rt6i_nh_flags & RTNH_F_DEAD ||
-	    (rt->rt6i_nh_flags & RTNH_F_LINKDOWN &&
+	if (rt->fib6_nh.nh_flags & RTNH_F_DEAD ||
+	    (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN &&
 	     rt->rt6i_idev->cnf.ignore_routes_with_linkdown))
 		return true;
 
@@ -3593,11 +3609,11 @@  static int rt6_multipath_total_weight(const struct rt6_info *rt)
 	int total = 0;
 
 	if (!rt6_is_dead(rt))
-		total += rt->rt6i_nh_weight;
+		total += rt->fib6_nh.nh_weight;
 
 	list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) {
 		if (!rt6_is_dead(iter))
-			total += iter->rt6i_nh_weight;
+			total += iter->fib6_nh.nh_weight;
 	}
 
 	return total;
@@ -3608,11 +3624,11 @@  static void rt6_upper_bound_set(struct rt6_info *rt, int *weight, int total)
 	int upper_bound = -1;
 
 	if (!rt6_is_dead(rt)) {
-		*weight += rt->rt6i_nh_weight;
+		*weight += rt->fib6_nh.nh_weight;
 		upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31,
 						    total) - 1;
 	}
-	atomic_set(&rt->rt6i_nh_upper_bound, upper_bound);
+	atomic_set(&rt->fib6_nh.nh_upper_bound, upper_bound);
 }
 
 static void rt6_multipath_upper_bound_set(struct rt6_info *rt, int total)
@@ -3655,8 +3671,8 @@  static int fib6_ifup(struct rt6_info *rt, void *p_arg)
 	const struct arg_netdev_event *arg = p_arg;
 	struct net *net = dev_net(arg->dev);
 
-	if (rt != net->ipv6.ip6_null_entry && rt->dst.dev == arg->dev) {
-		rt->rt6i_nh_flags &= ~arg->nh_flags;
+	if (rt != net->ipv6.ip6_null_entry && rt->fib6_nh.nh_dev == arg->dev) {
+		rt->fib6_nh.nh_flags &= ~arg->nh_flags;
 		fib6_update_sernum_upto_root(net, rt);
 		rt6_multipath_rebalance(rt);
 	}
@@ -3684,10 +3700,10 @@  static bool rt6_multipath_uses_dev(const struct rt6_info *rt,
 {
 	struct rt6_info *iter;
 
-	if (rt->dst.dev == dev)
+	if (rt->fib6_nh.nh_dev == dev)
 		return true;
 	list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings)
-		if (iter->dst.dev == dev)
+		if (iter->fib6_nh.nh_dev == dev)
 			return true;
 
 	return false;
@@ -3708,11 +3724,12 @@  static unsigned int rt6_multipath_dead_count(const struct rt6_info *rt,
 	struct rt6_info *iter;
 	unsigned int dead = 0;
 
-	if (rt->dst.dev == down_dev || rt->rt6i_nh_flags & RTNH_F_DEAD)
+	if (rt->fib6_nh.nh_dev == down_dev ||
+	    rt->fib6_nh.nh_flags & RTNH_F_DEAD)
 		dead++;
 	list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings)
-		if (iter->dst.dev == down_dev ||
-		    iter->rt6i_nh_flags & RTNH_F_DEAD)
+		if (iter->fib6_nh.nh_dev == down_dev ||
+		    iter->fib6_nh.nh_flags & RTNH_F_DEAD)
 			dead++;
 
 	return dead;
@@ -3724,11 +3741,11 @@  static void rt6_multipath_nh_flags_set(struct rt6_info *rt,
 {
 	struct rt6_info *iter;
 
-	if (rt->dst.dev == dev)
-		rt->rt6i_nh_flags |= nh_flags;
+	if (rt->fib6_nh.nh_dev == dev)
+		rt->fib6_nh.nh_flags |= nh_flags;
 	list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings)
-		if (iter->dst.dev == dev)
-			iter->rt6i_nh_flags |= nh_flags;
+		if (iter->fib6_nh.nh_dev == dev)
+			iter->fib6_nh.nh_flags |= nh_flags;
 }
 
 /* called with write lock held for table with rt */
@@ -3743,12 +3760,12 @@  static int fib6_ifdown(struct rt6_info *rt, void *p_arg)
 
 	switch (arg->event) {
 	case NETDEV_UNREGISTER:
-		return rt->dst.dev == dev ? -1 : 0;
+		return rt->fib6_nh.nh_dev == dev ? -1 : 0;
 	case NETDEV_DOWN:
 		if (rt->should_flush)
 			return -1;
 		if (!rt->rt6i_nsiblings)
-			return rt->dst.dev == dev ? -1 : 0;
+			return rt->fib6_nh.nh_dev == dev ? -1 : 0;
 		if (rt6_multipath_uses_dev(rt, dev)) {
 			unsigned int count;
 
@@ -3764,10 +3781,10 @@  static int fib6_ifdown(struct rt6_info *rt, void *p_arg)
 		}
 		return -2;
 	case NETDEV_CHANGE:
-		if (rt->dst.dev != dev ||
+		if (rt->fib6_nh.nh_dev != dev ||
 		    rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST))
 			break;
-		rt->rt6i_nh_flags |= RTNH_F_LINKDOWN;
+		rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN;
 		rt6_multipath_rebalance(rt);
 		break;
 	}
@@ -3828,7 +3845,7 @@  static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
 	   also have the lowest MTU, TOO BIG MESSAGE will be lead to
 	   PMTU discovery.
 	 */
-	if (rt->dst.dev == arg->dev &&
+	if (rt->fib6_nh.nh_dev == arg->dev &&
 	    dst_metric_raw(&rt->dst, RTAX_MTU) &&
 	    !dst_metric_locked(&rt->dst, RTAX_MTU)) {
 		spin_lock_bh(&rt6_exception_lock);
@@ -4116,7 +4133,7 @@  static int ip6_route_multipath_add(struct fib6_config *cfg,
 			goto cleanup;
 		}
 
-		rt->rt6i_nh_weight = rtnh->rtnh_hops + 1;
+		rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1;
 
 		err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg);
 		if (err) {
@@ -4273,7 +4290,7 @@  static size_t rt6_nlmsg_size(struct rt6_info *rt)
 		nexthop_len = nla_total_size(0)	 /* RTA_MULTIPATH */
 			    + NLA_ALIGN(sizeof(struct rtnexthop))
 			    + nla_total_size(16) /* RTA_GATEWAY */
-			    + lwtunnel_get_encap_size(rt->dst.lwtstate);
+			    + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate);
 
 		nexthop_len *= rt->rt6i_nsiblings;
 	}
@@ -4291,38 +4308,38 @@  static size_t rt6_nlmsg_size(struct rt6_info *rt)
 	       + nla_total_size(sizeof(struct rta_cacheinfo))
 	       + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
 	       + nla_total_size(1) /* RTA_PREF */
-	       + lwtunnel_get_encap_size(rt->dst.lwtstate)
+	       + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate)
 	       + nexthop_len;
 }
 
 static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt,
 			    unsigned int *flags, bool skip_oif)
 {
-	if (rt->rt6i_nh_flags & RTNH_F_DEAD)
+	if (rt->fib6_nh.nh_flags & RTNH_F_DEAD)
 		*flags |= RTNH_F_DEAD;
 
-	if (rt->rt6i_nh_flags & RTNH_F_LINKDOWN) {
+	if (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN) {
 		*flags |= RTNH_F_LINKDOWN;
 		if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
 			*flags |= RTNH_F_DEAD;
 	}
 
 	if (rt->rt6i_flags & RTF_GATEWAY) {
-		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
+		if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->fib6_nh.nh_gw) < 0)
 			goto nla_put_failure;
 	}
 
-	*flags |= (rt->rt6i_nh_flags & RTNH_F_ONLINK);
-	if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD)
+	*flags |= (rt->fib6_nh.nh_flags & RTNH_F_ONLINK);
+	if (rt->fib6_nh.nh_flags & RTNH_F_OFFLOAD)
 		*flags |= RTNH_F_OFFLOAD;
 
 	/* not needed for multipath encoding b/c it has a rtnexthop struct */
-	if (!skip_oif && rt->dst.dev &&
-	    nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
+	if (!skip_oif && rt->fib6_nh.nh_dev &&
+	    nla_put_u32(skb, RTA_OIF, rt->fib6_nh.nh_dev->ifindex))
 		goto nla_put_failure;
 
-	if (rt->dst.lwtstate &&
-	    lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
+	if (rt->fib6_nh.nh_lwtstate &&
+	    lwtunnel_fill_encap(skb, rt->fib6_nh.nh_lwtstate) < 0)
 		goto nla_put_failure;
 
 	return 0;
@@ -4334,6 +4351,7 @@  static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt,
 /* add multipath next hop */
 static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
 {
+	const struct net_device *dev = rt->fib6_nh.nh_dev;
 	struct rtnexthop *rtnh;
 	unsigned int flags = 0;
 
@@ -4341,8 +4359,8 @@  static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
 	if (!rtnh)
 		goto nla_put_failure;
 
-	rtnh->rtnh_hops = rt->rt6i_nh_weight - 1;
-	rtnh->rtnh_ifindex = rt->dst.dev ? rt->dst.dev->ifindex : 0;
+	rtnh->rtnh_hops = rt->fib6_nh.nh_weight - 1;
+	rtnh->rtnh_ifindex = dev ? dev->ifindex : 0;
 
 	if (rt6_nexthop_info(skb, rt, &flags, true) < 0)
 		goto nla_put_failure;