i40e: Implementation of VXLAN ndo's
authorJeff Kirsher <jeffrey.t.kirsher@intel.com>
Sat, 28 Dec 2013 07:32:18 +0000 (07:32 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Sat, 4 Jan 2014 04:11:44 +0000 (20:11 -0800)
This adds the implementation for the VXLAN ndo's.  This allows the
hardware to do RX checksum offload for inner packets on the UDP ports
that VXLAN notifies us about.

Signed-off-by: Joseph Gasparakis <joseph.gasparakis@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/Kconfig
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40e/i40e_common.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_prototype.h
drivers/net/ethernet/intel/i40e/i40e_type.h

index 94adb89c36686da792a4845c3e70127b5b62d150..9fb2eb8cf15250375487dfe7b807dad0bd89d204 100644 (file)
@@ -259,6 +259,19 @@ config I40E
          To compile this driver as a module, choose M here. The module
          will be called i40e.
 
+config I40E_VXLAN
+       bool "Virtual eXtensible Local Area Network Support"
+       default n
+       depends on I40E && VXLAN && !(I40E=y && VXLAN=m)
+       ---help---
+         This allows one to create VXLAN virtual interfaces that provide
+         Layer 2 Networks over Layer 3 Networks. VXLAN is often used
+         to tunnel virtual network infrastructure in virtualized environments.
+         Say Y here if you want to use Virtual eXtensible Local Area Network
+         (VXLAN) in the driver.
+
+         If unsure, say N.
+
 config I40EVF
        tristate "Intel(R) XL710 X710 Virtual Function Ethernet support"
        depends on PCI_MSI
index 31dd2651155325e6d7a6389a8e274edf86206ea4..6f1edc17a04c2b90e2c63bd0cb2d83b9adcf69e1 100644 (file)
@@ -207,6 +207,11 @@ struct i40e_pf {
        u8 atr_sample_rate;
        bool wol_en;
 
+#ifdef CONFIG_I40E_VXLAN
+       __be16  vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
+       u16 pending_vxlan_bitmap;
+
+#endif
        enum i40e_interrupt_policy int_policy;
        u16 rx_itr_default;
        u16 tx_itr_default;
@@ -238,7 +243,10 @@ struct i40e_pf {
 #define I40E_FLAG_DCB_ENABLED                  (u64)(1 << 21)
 #define I40E_FLAG_FDIR_ENABLED                 (u64)(1 << 22)
 #define I40E_FLAG_FDIR_ATR_ENABLED             (u64)(1 << 23)
-#define I40E_FLAG_MFP_ENABLED                  (u64)(1 << 27)
+#define I40E_FLAG_MFP_ENABLED                  (u64)(1 << 26)
+#ifdef CONFIG_I40E_VXLAN
+#define I40E_FLAG_VXLAN_FILTER_SYNC            (u64)(1 << 27)
+#endif
 
        u16 num_tx_queues;
        u16 num_rx_queues;
index e61ebdd5a5f933222fe2675e6b5ba8bba3bf87d4..2859377abca14dd6bc07eb2b268906713f77a0c1 100644 (file)
@@ -1918,6 +1918,7 @@ struct i40e_aqc_add_udp_tunnel {
        u8     protocol_index;
 #define I40E_AQC_TUNNEL_TYPE_MAC    0x0
 #define I40E_AQC_TUNNEL_TYPE_UDP    0x1
+#define I40E_AQC_TUNNEL_TYPE_VXLAN  0x2
        u8     reserved[12];
 };
 
index a69959ef61957e5d5ff516adbeb0fcc34754471f..e9aab1cfb4d298de8da9a7130a51adff300850dc 100644 (file)
@@ -1670,6 +1670,63 @@ i40e_status i40e_aq_start_lldp(struct i40e_hw *hw,
        return status;
 }
 
+/**
+ * i40e_aq_add_udp_tunnel
+ * @hw: pointer to the hw struct
+ * @udp_port: the UDP port to add
+ * @header_len: length of the tunneling header length in DWords
+ * @protocol_index: protocol index type
+ * @cmd_details: pointer to command details structure or NULL
+ **/
+i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw,
+                               u16 udp_port, u8 header_len,
+                               u8 protocol_index, u8 *filter_index,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_add_udp_tunnel *cmd =
+               (struct i40e_aqc_add_udp_tunnel *)&desc.params.raw;
+       struct i40e_aqc_del_udp_tunnel_completion *resp =
+               (struct i40e_aqc_del_udp_tunnel_completion *)&desc.params.raw;
+       i40e_status status;
+
+       i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_udp_tunnel);
+
+       cmd->udp_port = cpu_to_le16(udp_port);
+       cmd->header_len = header_len;
+       cmd->protocol_index = protocol_index;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       if (!status)
+               *filter_index = resp->index;
+
+       return status;
+}
+
+/**
+ * i40e_aq_del_udp_tunnel
+ * @hw: pointer to the hw struct
+ * @index: filter index
+ * @cmd_details: pointer to command details structure or NULL
+ **/
+i40e_status i40e_aq_del_udp_tunnel(struct i40e_hw *hw, u8 index,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_remove_udp_tunnel *cmd =
+               (struct i40e_aqc_remove_udp_tunnel *)&desc.params.raw;
+       i40e_status status;
+
+       i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_del_udp_tunnel);
+
+       cmd->index = index;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       return status;
+}
+
 /**
  * i40e_aq_delete_element - Delete switch element
  * @hw: pointer to the hw struct
index b0cfb4c81b60a70c530ce537d8b7588a844ba85a..97add1f2a1d6885b840ecec78cac90088c694dc4 100644 (file)
@@ -27,6 +27,9 @@
 
 /* Local includes */
 #include "i40e.h"
+#ifdef CONFIG_I40E_VXLAN
+#include <net/vxlan.h>
+#endif
 
 const char i40e_driver_name[] = "i40e";
 static const char i40e_driver_string[] =
@@ -3993,6 +3996,9 @@ static int i40e_open(struct net_device *netdev)
                                    "couldn't set broadcast err %d aq_err %d\n",
                                    err, pf->hw.aq.asq_last_status);
        }
+#ifdef CONFIG_I40E_VXLAN
+       vxlan_get_rx_port(netdev);
+#endif
 
        return 0;
 
@@ -5016,6 +5022,52 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf)
        i40e_flush(hw);
 }
 
+#ifdef CONFIG_I40E_VXLAN
+/**
+ * i40e_sync_vxlan_filters_subtask - Sync the VSI filter list with HW
+ * @pf: board private structure
+ **/
+static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
+{
+       const int vxlan_hdr_qwords = 4;
+       struct i40e_hw *hw = &pf->hw;
+       i40e_status ret;
+       u8 filter_index;
+       __be16 port;
+       int i;
+
+       if (!(pf->flags & I40E_FLAG_VXLAN_FILTER_SYNC))
+               return;
+
+       pf->flags &= ~I40E_FLAG_VXLAN_FILTER_SYNC;
+
+       for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
+               if (pf->pending_vxlan_bitmap & (1 << i)) {
+                       pf->pending_vxlan_bitmap &= ~(1 << i);
+                       port = pf->vxlan_ports[i];
+                       ret = port ?
+                             i40e_aq_add_udp_tunnel(hw, ntohs(port),
+                                                    vxlan_hdr_qwords,
+                                                    I40E_AQC_TUNNEL_TYPE_VXLAN,
+                                                    &filter_index, NULL)
+                             : i40e_aq_del_udp_tunnel(hw, i, NULL);
+
+                       if (ret) {
+                               dev_info(&pf->pdev->dev, "Failed to execute AQ command for %s port %d with index %d\n",
+                                        port ? "adding" : "deleting",
+                                        ntohs(port), port ? i : i);
+
+                               pf->vxlan_ports[i] = 0;
+                       } else {
+                               dev_info(&pf->pdev->dev, "%s port %d with AQ command with index %d\n",
+                                        port ? "Added" : "Deleted",
+                                        ntohs(port), port ? i : filter_index);
+                       }
+               }
+       }
+}
+
+#endif
 /**
  * i40e_service_task - Run the driver's async subtasks
  * @work: pointer to work_struct containing our data
@@ -5034,6 +5086,9 @@ static void i40e_service_task(struct work_struct *work)
        i40e_fdir_reinit_subtask(pf);
        i40e_check_hang_subtask(pf);
        i40e_sync_filters_subtask(pf);
+#ifdef CONFIG_I40E_VXLAN
+       i40e_sync_vxlan_filters_subtask(pf);
+#endif
        i40e_clean_adminq_subtask(pf);
 
        i40e_service_event_complete(pf);
@@ -5900,6 +5955,104 @@ static int i40e_set_features(struct net_device *netdev,
        return 0;
 }
 
+#ifdef CONFIG_I40E_VXLAN
+/**
+ * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port
+ * @pf: board private structure
+ * @port: The UDP port to look up
+ *
+ * Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found
+ **/
+static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port)
+{
+       u8 i;
+
+       for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
+               if (pf->vxlan_ports[i] == port)
+                       return i;
+       }
+
+       return i;
+}
+
+/**
+ * i40e_add_vxlan_port - Get notifications about VXLAN ports that come up
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that VXLAN is notifying us about
+ * @port: New UDP port number that VXLAN started listening to
+ **/
+static void i40e_add_vxlan_port(struct net_device *netdev,
+                               sa_family_t sa_family, __be16 port)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       u8 next_idx;
+       u8 idx;
+
+       if (sa_family == AF_INET6)
+               return;
+
+       idx = i40e_get_vxlan_port_idx(pf, port);
+
+       /* Check if port already exists */
+       if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+               netdev_info(netdev, "Port %d already offloaded\n", ntohs(port));
+               return;
+       }
+
+       /* Now check if there is space to add the new port */
+       next_idx = i40e_get_vxlan_port_idx(pf, 0);
+
+       if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+               netdev_info(netdev, "Maximum number of UDP ports reached, not adding port %d\n",
+                           ntohs(port));
+               return;
+       }
+
+       /* New port: add it and mark its index in the bitmap */
+       pf->vxlan_ports[next_idx] = port;
+       pf->pending_vxlan_bitmap |= (1 << next_idx);
+
+       pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+}
+
+/**
+ * i40e_del_vxlan_port - Get notifications about VXLAN ports that go away
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that VXLAN is notifying us about
+ * @port: UDP port number that VXLAN stopped listening to
+ **/
+static void i40e_del_vxlan_port(struct net_device *netdev,
+                               sa_family_t sa_family, __be16 port)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       u8 idx;
+
+       if (sa_family == AF_INET6)
+               return;
+
+       idx = i40e_get_vxlan_port_idx(pf, port);
+
+       /* Check if port already exists */
+       if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+               /* if port exists, set it to 0 (mark for deletion)
+                * and make it pending
+                */
+               pf->vxlan_ports[idx] = 0;
+
+               pf->pending_vxlan_bitmap |= (1 << idx);
+
+               pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+       } else {
+               netdev_warn(netdev, "Port %d was not found, not deleting\n",
+                           ntohs(port));
+       }
+}
+
+#endif
 static const struct net_device_ops i40e_netdev_ops = {
        .ndo_open               = i40e_open,
        .ndo_stop               = i40e_close,
@@ -5921,6 +6074,10 @@ static const struct net_device_ops i40e_netdev_ops = {
        .ndo_set_vf_vlan        = i40e_ndo_set_vf_port_vlan,
        .ndo_set_vf_tx_rate     = i40e_ndo_set_vf_bw,
        .ndo_get_vf_config      = i40e_ndo_get_vf_config,
+#ifdef CONFIG_I40E_VXLAN
+       .ndo_add_vxlan_port     = i40e_add_vxlan_port,
+       .ndo_del_vxlan_port     = i40e_del_vxlan_port,
+#endif
 };
 
 /**
index db7bf93efdd20682d6fd546e0c6acf92ea4b9fbf..5c458bb6892a6a7e1e0b52a8100f17843fff8a06 100644 (file)
@@ -157,6 +157,12 @@ i40e_status i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_start_lldp(struct i40e_hw *hw,
                                struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw,
+                               u16 udp_port, u8 header_len,
+                               u8 protocol_index, u8 *filter_index,
+                               struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_del_udp_tunnel(struct i40e_hw *hw, u8 index,
+                               struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_delete_element(struct i40e_hw *hw, u16 seid,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_mac_address_write(struct i40e_hw *hw,
index 8bf1cac2d9005efb8c37116a26939c2599e12fe1..b3a659cb16c46421fe09d457934b478fbd613429 100644 (file)
@@ -59,6 +59,7 @@
 #define I40E_MAX_VSI_QP                        16
 #define I40E_MAX_VF_VSI                        3
 #define I40E_MAX_CHAINED_RX_BUFFERS    5
+#define I40E_MAX_PF_UDP_OFFLOAD_PORTS  16
 
 /* Max default timeout in ms, */
 #define I40E_MAX_NVM_TIMEOUT           18000