cxgb4: implement udp tunnel callbacks
authorGanesh Goudar <ganeshgr@chelsio.com>
Wed, 10 Jan 2018 12:45:08 +0000 (18:15 +0530)
committerDavid S. Miller <davem@davemloft.net>
Thu, 11 Jan 2018 15:58:55 +0000 (10:58 -0500)
Implement ndo_udp_tunnel_add and ndo_udp_tunnel_del
to support vxlan tunnelling.

Original work by: Santosh Rastapur <santosh@chelsio.com>
Signed-off-by: Ganesh Goudar <ganeshgr@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c

index 1ff7182..f56e893 100644 (file)
@@ -825,6 +825,10 @@ struct mbox_list {
        struct list_head list;
 };
 
+struct mps_encap_entry {
+       atomic_t refcnt;
+};
+
 struct adapter {
        void __iomem *regs;
        void __iomem *bar2;
@@ -839,6 +843,8 @@ struct adapter {
        enum chip_type chip;
 
        int msg_enable;
+       __be16 vxlan_port;
+       u8 vxlan_port_cnt;
 
        struct adapter_params params;
        struct cxgb4_virt_res vres;
@@ -868,7 +874,10 @@ struct adapter {
        unsigned int clipt_start;
        unsigned int clipt_end;
        struct clip_tbl *clipt;
+       unsigned int rawf_start;
+       unsigned int rawf_cnt;
        struct smt_data *smt;
+       struct mps_encap_entry *mps_encap;
        struct cxgb4_uld_info *uld;
        void *uld_handle[CXGB4_ULD_MAX];
        unsigned int num_uld;
@@ -1637,6 +1646,12 @@ int t4_free_vi(struct adapter *adap, unsigned int mbox,
 int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid,
                int mtu, int promisc, int all_multi, int bcast, int vlanex,
                bool sleep_ok);
+int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                        const u8 *addr, const u8 *mask, unsigned int idx,
+                        u8 lookup_type, u8 port_id, bool sleep_ok);
+int t4_alloc_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                         const u8 *addr, const u8 *mask, unsigned int idx,
+                         u8 lookup_type, u8 port_id, bool sleep_ok);
 int t4_alloc_mac_filt(struct adapter *adap, unsigned int mbox,
                      unsigned int viid, bool free, unsigned int naddr,
                      const u8 **addr, u16 *idx, u64 *hash, bool sleep_ok);
index 87ac1e4..8097762 100644 (file)
@@ -65,6 +65,7 @@
 #include <net/addrconf.h>
 #include <linux/uaccess.h>
 #include <linux/crash_dump.h>
+#include <net/udp_tunnel.h>
 
 #include "cxgb4.h"
 #include "cxgb4_filter.h"
@@ -2987,6 +2988,133 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type,
        }
 }
 
+static void cxgb_del_udp_tunnel(struct net_device *netdev,
+                               struct udp_tunnel_info *ti)
+{
+       struct port_info *pi = netdev_priv(netdev);
+       struct adapter *adapter = pi->adapter;
+       unsigned int chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip);
+       u8 match_all_mac[] = { 0, 0, 0, 0, 0, 0 };
+       int ret = 0, i;
+
+       if (chip_ver < CHELSIO_T6)
+               return;
+
+       switch (ti->type) {
+       case UDP_TUNNEL_TYPE_VXLAN:
+               if (!adapter->vxlan_port_cnt ||
+                   adapter->vxlan_port != ti->port)
+                       return; /* Invalid VxLAN destination port */
+
+               adapter->vxlan_port_cnt--;
+               if (adapter->vxlan_port_cnt)
+                       return;
+
+               adapter->vxlan_port = 0;
+               t4_write_reg(adapter, MPS_RX_VXLAN_TYPE_A, 0);
+               break;
+       default:
+               return;
+       }
+
+       /* Matchall mac entries can be deleted only after all tunnel ports
+        * are brought down or removed.
+        */
+       if (!adapter->rawf_cnt)
+               return;
+       for_each_port(adapter, i) {
+               pi = adap2pinfo(adapter, i);
+               ret = t4_free_raw_mac_filt(adapter, pi->viid,
+                                          match_all_mac, match_all_mac,
+                                          adapter->rawf_start +
+                                           pi->port_id,
+                                          1, pi->port_id, true);
+               if (ret < 0) {
+                       netdev_info(netdev, "Failed to free mac filter entry, for port %d\n",
+                                   i);
+                       return;
+               }
+               atomic_dec(&adapter->mps_encap[adapter->rawf_start +
+                          pi->port_id].refcnt);
+       }
+}
+
+static void cxgb_add_udp_tunnel(struct net_device *netdev,
+                               struct udp_tunnel_info *ti)
+{
+       struct port_info *pi = netdev_priv(netdev);
+       struct adapter *adapter = pi->adapter;
+       unsigned int chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip);
+       u8 match_all_mac[] = { 0, 0, 0, 0, 0, 0 };
+       int i, ret;
+
+       if (chip_ver < CHELSIO_T6)
+               return;
+
+       switch (ti->type) {
+       case UDP_TUNNEL_TYPE_VXLAN:
+               /* For T6 fw reserves last 2 entries for
+                * storing match all mac filter (config file entry).
+                */
+               if (!adapter->rawf_cnt)
+                       return;
+
+               /* Callback for adding vxlan port can be called with the same
+                * port for both IPv4 and IPv6. We should not disable the
+                * offloading when the same port for both protocols is added
+                * and later one of them is removed.
+                */
+               if (adapter->vxlan_port_cnt &&
+                   adapter->vxlan_port == ti->port) {
+                       adapter->vxlan_port_cnt++;
+                       return;
+               }
+
+               /* We will support only one VxLAN port */
+               if (adapter->vxlan_port_cnt) {
+                       netdev_info(netdev, "UDP port %d already offloaded, not adding port %d\n",
+                                   be16_to_cpu(adapter->vxlan_port),
+                                   be16_to_cpu(ti->port));
+                       return;
+               }
+
+               adapter->vxlan_port = ti->port;
+               adapter->vxlan_port_cnt = 1;
+
+               t4_write_reg(adapter, MPS_RX_VXLAN_TYPE_A,
+                            VXLAN_V(be16_to_cpu(ti->port)) | VXLAN_EN_F);
+               break;
+       default:
+               return;
+       }
+
+       /* Create a 'match all' mac filter entry for inner mac,
+        * if raw mac interface is supported. Once the linux kernel provides
+        * driver entry points for adding/deleting the inner mac addresses,
+        * we will remove this 'match all' entry and fallback to adding
+        * exact match filters.
+        */
+       if (adapter->rawf_cnt) {
+               for_each_port(adapter, i) {
+                       pi = adap2pinfo(adapter, i);
+
+                       ret = t4_alloc_raw_mac_filt(adapter, pi->viid,
+                                                   match_all_mac,
+                                                   match_all_mac,
+                                                   adapter->rawf_start +
+                                                   pi->port_id,
+                                                   1, pi->port_id, true);
+                       if (ret < 0) {
+                               netdev_info(netdev, "Failed to allocate a mac filter entry, not adding port %d\n",
+                                           be16_to_cpu(ti->port));
+                               cxgb_del_udp_tunnel(netdev, ti);
+                               return;
+                       }
+                       atomic_inc(&adapter->mps_encap[ret].refcnt);
+               }
+       }
+}
+
 static netdev_features_t cxgb_fix_features(struct net_device *dev,
                                           netdev_features_t features)
 {
@@ -3018,6 +3146,8 @@ static const struct net_device_ops cxgb4_netdev_ops = {
 #endif /* CONFIG_CHELSIO_T4_FCOE */
        .ndo_set_tx_maxrate   = cxgb_set_tx_maxrate,
        .ndo_setup_tc         = cxgb_setup_tc,
+       .ndo_udp_tunnel_add   = cxgb_add_udp_tunnel,
+       .ndo_udp_tunnel_del   = cxgb_del_udp_tunnel,
        .ndo_fix_features     = cxgb_fix_features,
 };
 
index 0e9f64a..6d76851 100644 (file)
@@ -7467,6 +7467,112 @@ int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid,
 }
 
 /**
+ *     t4_free_raw_mac_filt - Frees a raw mac entry in mps tcam
+ *     @adap: the adapter
+ *     @viid: the VI id
+ *     @addr: the MAC address
+ *     @mask: the mask
+ *     @idx: index of the entry in mps tcam
+ *     @lookup_type: MAC address for inner (1) or outer (0) header
+ *     @port_id: the port index
+ *     @sleep_ok: call is allowed to sleep
+ *
+ *     Removes the mac entry at the specified index using raw mac interface.
+ *
+ *     Returns a negative error number on failure.
+ */
+int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                        const u8 *addr, const u8 *mask, unsigned int idx,
+                        u8 lookup_type, u8 port_id, bool sleep_ok)
+{
+       struct fw_vi_mac_cmd c;
+       struct fw_vi_mac_raw *p = &c.u.raw;
+       u32 val;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) |
+                                  FW_CMD_REQUEST_F | FW_CMD_WRITE_F |
+                                  FW_CMD_EXEC_V(0) |
+                                  FW_VI_MAC_CMD_VIID_V(viid));
+       val = FW_CMD_LEN16_V(1) |
+             FW_VI_MAC_CMD_ENTRY_TYPE_V(FW_VI_MAC_TYPE_RAW);
+       c.freemacs_to_len16 = cpu_to_be32(FW_VI_MAC_CMD_FREEMACS_V(0) |
+                                         FW_CMD_LEN16_V(val));
+
+       p->raw_idx_pkd = cpu_to_be32(FW_VI_MAC_CMD_RAW_IDX_V(idx) |
+                                    FW_VI_MAC_ID_BASED_FREE);
+
+       /* Lookup Type. Outer header: 0, Inner header: 1 */
+       p->data0_pkd = cpu_to_be32(DATALKPTYPE_V(lookup_type) |
+                                  DATAPORTNUM_V(port_id));
+       /* Lookup mask and port mask */
+       p->data0m_pkd = cpu_to_be64(DATALKPTYPE_V(DATALKPTYPE_M) |
+                                   DATAPORTNUM_V(DATAPORTNUM_M));
+
+       /* Copy the address and the mask */
+       memcpy((u8 *)&p->data1[0] + 2, addr, ETH_ALEN);
+       memcpy((u8 *)&p->data1m[0] + 2, mask, ETH_ALEN);
+
+       return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+}
+
+/**
+ *     t4_alloc_raw_mac_filt - Adds a mac entry in mps tcam
+ *     @adap: the adapter
+ *     @viid: the VI id
+ *     @mac: the MAC address
+ *     @mask: the mask
+ *     @idx: index at which to add this entry
+ *     @port_id: the port index
+ *     @lookup_type: MAC address for inner (1) or outer (0) header
+ *     @sleep_ok: call is allowed to sleep
+ *
+ *     Adds the mac entry at the specified index using raw mac interface.
+ *
+ *     Returns a negative error number or the allocated index for this mac.
+ */
+int t4_alloc_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                         const u8 *addr, const u8 *mask, unsigned int idx,
+                         u8 lookup_type, u8 port_id, bool sleep_ok)
+{
+       int ret = 0;
+       struct fw_vi_mac_cmd c;
+       struct fw_vi_mac_raw *p = &c.u.raw;
+       u32 val;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) |
+                                  FW_CMD_REQUEST_F | FW_CMD_WRITE_F |
+                                  FW_VI_MAC_CMD_VIID_V(viid));
+       val = FW_CMD_LEN16_V(1) |
+             FW_VI_MAC_CMD_ENTRY_TYPE_V(FW_VI_MAC_TYPE_RAW);
+       c.freemacs_to_len16 = cpu_to_be32(val);
+
+       /* Specify that this is an inner mac address */
+       p->raw_idx_pkd = cpu_to_be32(FW_VI_MAC_CMD_RAW_IDX_V(idx));
+
+       /* Lookup Type. Outer header: 0, Inner header: 1 */
+       p->data0_pkd = cpu_to_be32(DATALKPTYPE_V(lookup_type) |
+                                  DATAPORTNUM_V(port_id));
+       /* Lookup mask and port mask */
+       p->data0m_pkd = cpu_to_be64(DATALKPTYPE_V(DATALKPTYPE_M) |
+                                   DATAPORTNUM_V(DATAPORTNUM_M));
+
+       /* Copy the address and the mask */
+       memcpy((u8 *)&p->data1[0] + 2, addr, ETH_ALEN);
+       memcpy((u8 *)&p->data1m[0] + 2, mask, ETH_ALEN);
+
+       ret = t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+       if (ret == 0) {
+               ret = FW_VI_MAC_CMD_RAW_IDX_G(be32_to_cpu(p->raw_idx_pkd));
+               if (ret != idx)
+                       ret = -ENOMEM;
+       }
+
+       return ret;
+}
+
+/**
  *     t4_alloc_mac_filt - allocates exact-match filters for MAC addresses
  *     @adap: the adapter
  *     @mbox: mailbox to use for the FW command