mlxsw: spectrum_router: Introduce loopback RIFs
authorPetr Machata <petrm@mellanox.com>
Sat, 2 Sep 2017 21:49:19 +0000 (23:49 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 4 Sep 2017 03:23:25 +0000 (20:23 -0700)
When offloading L3 tunnels, an adjacency entry is created that loops the
packet back into the underlay router. Loopback interfaces then hold the
corresponding information and are created for IP-in-IP netdevices.

Signed-off-by: Petr Machata <petrm@mellanox.com>
Reviewed-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h

index f8c7f7e930c58aefe058f2419fe02735c51537d9..84ce83acdc199bf4c311c325edc5f7ccba1af375 100644 (file)
@@ -77,6 +77,7 @@ enum mlxsw_sp_rif_type {
        MLXSW_SP_RIF_TYPE_SUBPORT,
        MLXSW_SP_RIF_TYPE_VLAN,
        MLXSW_SP_RIF_TYPE_FID,
+       MLXSW_SP_RIF_TYPE_IPIP_LB, /* IP-in-IP loopback. */
        MLXSW_SP_RIF_TYPE_MAX,
 };
 
index 38477c5f7d4dfd42a54cda697a98168c6ecad4f1..72e386b647b98d5ee356bc4fdabfba9216e2b8ee 100644 (file)
@@ -1,9 +1,10 @@
 /*
  * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
- * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
  * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
  * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
+ * Copyright (c) 2017 Petr Machata <petrm@mellanox.com>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -51,6 +52,7 @@
 #include <net/ip_fib.h>
 #include <net/ip6_fib.h>
 #include <net/fib_rules.h>
+#include <net/ip_tunnels.h>
 #include <net/l3mdev.h>
 #include <net/addrconf.h>
 #include <net/ndisc.h>
@@ -131,6 +133,17 @@ struct mlxsw_sp_rif_subport {
        bool lag;
 };
 
+struct mlxsw_sp_rif_ipip_lb {
+       struct mlxsw_sp_rif common;
+       struct mlxsw_sp_rif_ipip_lb_config lb_config;
+       u16 ul_vr_id; /* Reserved for Spectrum-2. */
+};
+
+struct mlxsw_sp_rif_params_ipip_lb {
+       struct mlxsw_sp_rif_params common;
+       struct mlxsw_sp_rif_ipip_lb_config lb_config;
+};
+
 struct mlxsw_sp_rif_ops {
        enum mlxsw_sp_rif_type type;
        size_t rif_size;
@@ -883,6 +896,25 @@ static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp)
        kfree(mlxsw_sp->router->vrs);
 }
 
+static struct net_device *
+__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
+{
+       struct ip_tunnel *tun = netdev_priv(ol_dev);
+       struct net *net = dev_net(ol_dev);
+
+       return __dev_get_by_index(net, tun->parms.link);
+}
+
+static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
+{
+       struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev);
+
+       if (d)
+               return l3mdev_fib_table(d) ? : RT_TABLE_MAIN;
+       else
+               return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN;
+}
+
 struct mlxsw_sp_neigh_key {
        struct neighbour *n;
 };
@@ -2236,6 +2268,25 @@ static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp,
        neigh_release(n);
 }
 
+static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp,
+                                     const struct net_device *dev,
+                                     enum mlxsw_sp_ipip_type *p_type)
+{
+       struct mlxsw_sp_router *router = mlxsw_sp->router;
+       const struct mlxsw_sp_ipip_ops *ipip_ops;
+       enum mlxsw_sp_ipip_type ipipt;
+
+       for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) {
+               ipip_ops = router->ipip_ops_arr[ipipt];
+               if (dev->type == ipip_ops->dev_type) {
+                       if (p_type)
+                               *p_type = ipipt;
+                       return true;
+               }
+       }
+       return false;
+}
+
 static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_nexthop_group *nh_grp,
                                  struct mlxsw_sp_nexthop *nh,
@@ -4374,7 +4425,10 @@ mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp,
 {
        enum mlxsw_sp_fid_type type;
 
-       /* RIF type is derived from the type of the underlying FID */
+       if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL))
+               return MLXSW_SP_RIF_TYPE_IPIP_LB;
+
+       /* Otherwise RIF type is derived from the type of the underlying FID. */
        if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev)))
                type = MLXSW_SP_FID_TYPE_8021Q;
        else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev))
@@ -5164,10 +5218,104 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
        .fid_get                = mlxsw_sp_rif_fid_fid_get,
 };
 
+static struct mlxsw_sp_rif_ipip_lb *
+mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
+{
+       return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common);
+}
+
+static void
+mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif,
+                          const struct mlxsw_sp_rif_params *params)
+{
+       struct mlxsw_sp_rif_params_ipip_lb *params_lb;
+       struct mlxsw_sp_rif_ipip_lb *rif_lb;
+
+       params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb,
+                                common);
+       rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif);
+       rif_lb->lb_config = params_lb->lb_config;
+}
+
+static int
+mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif,
+                       struct mlxsw_sp_vr *ul_vr, bool enable)
+{
+       struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config;
+       struct mlxsw_sp_rif *rif = &lb_rif->common;
+       struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
+       char ritr_pl[MLXSW_REG_RITR_LEN];
+       u32 saddr4;
+
+       switch (lb_cf.ul_protocol) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               saddr4 = be32_to_cpu(lb_cf.saddr.addr4);
+               mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF,
+                                   rif->rif_index, rif->vr_id, rif->dev->mtu);
+               mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt,
+                           MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET,
+                           ul_vr->id, saddr4, lb_cf.okey);
+               break;
+
+       case MLXSW_SP_L3_PROTO_IPV6:
+               return -EAFNOSUPPORT;
+       }
+
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+static int
+mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif)
+{
+       struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
+       u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev);
+       struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
+       struct mlxsw_sp_vr *ul_vr;
+       int err;
+
+       ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id);
+       if (IS_ERR(ul_vr))
+               return PTR_ERR(ul_vr);
+
+       err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true);
+       if (err)
+               goto err_loopback_op;
+
+       lb_rif->ul_vr_id = ul_vr->id;
+       ++ul_vr->rif_count;
+       return 0;
+
+err_loopback_op:
+       mlxsw_sp_vr_put(ul_vr);
+       return err;
+}
+
+static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif)
+{
+       struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif);
+       struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
+       struct mlxsw_sp_vr *ul_vr;
+
+       ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id];
+       mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false);
+
+       --ul_vr->rif_count;
+       mlxsw_sp_vr_put(ul_vr);
+}
+
+static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
+       .type                   = MLXSW_SP_RIF_TYPE_IPIP_LB,
+       .rif_size               = sizeof(struct mlxsw_sp_rif_ipip_lb),
+       .setup                  = mlxsw_sp_rif_ipip_lb_setup,
+       .configure              = mlxsw_sp_rif_ipip_lb_configure,
+       .deconfigure            = mlxsw_sp_rif_ipip_lb_deconfigure,
+};
+
 static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
        [MLXSW_SP_RIF_TYPE_SUBPORT]     = &mlxsw_sp_rif_subport_ops,
        [MLXSW_SP_RIF_TYPE_VLAN]        = &mlxsw_sp_rif_vlan_ops,
        [MLXSW_SP_RIF_TYPE_FID]         = &mlxsw_sp_rif_fid_ops,
+       [MLXSW_SP_RIF_TYPE_IPIP_LB]     = &mlxsw_sp_rif_ipip_lb_ops,
 };
 
 static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp)
index 67c6c1d5ac93a251b5ba0abe0498e50b3c3b6e12..a5732aa87b9a9ff6bb7dc0d9e12937d09f856053 100644 (file)
 #define _MLXSW_ROUTER_H_
 
 #include "spectrum.h"
+#include "reg.h"
 
 enum mlxsw_sp_l3proto {
        MLXSW_SP_L3_PROTO_IPV4,
        MLXSW_SP_L3_PROTO_IPV6,
 };
 
+union mlxsw_sp_l3addr {
+       __be32 addr4;
+       struct in6_addr addr6;
+};
+
+struct mlxsw_sp_rif_ipip_lb_config {
+       enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt;
+       u32 okey;
+       enum mlxsw_sp_l3proto ul_protocol; /* Underlay. */
+       union mlxsw_sp_l3addr saddr;
+};
+
 enum mlxsw_sp_rif_counter_dir {
        MLXSW_SP_RIF_COUNTER_INGRESS,
        MLXSW_SP_RIF_COUNTER_EGRESS,