be2net: VxLAN offload should be re-enabled when only 1 UDP port is left
authorSriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
Mon, 17 Apr 2017 16:03:13 +0000 (21:33 +0530)
committerDavid S. Miller <davem@davemloft.net>
Tue, 18 Apr 2017 19:56:25 +0000 (15:56 -0400)
We disable VxLAN offload when more than 1 UDP port is added to the driver,
since Skyhawk doesn't support offload with multiple ports. The existing
driver design expects the user to delete all port configurations and create
a configuration with a single UDP port for VxLAN offload to be re-enabled.
Remove this restriction by tracking the ports added and re-enabling offload
when ports get deleted and only 1 port is left.

Signed-off-by: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
Reviewed-by: Sathya Perla <sathya.perla@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/emulex/benet/be.h
drivers/net/ethernet/emulex/benet/be_main.c

index d49528ad7821d202355ef9b1a18d58e56794b162..50566243e6fa2222595866f0789ad495721f93de 100644 (file)
@@ -567,6 +567,12 @@ struct be_error_recovery {
 /* Ethtool priv_flags */
 #define        BE_DISABLE_TPE_RECOVERY 0x1
 
+struct be_vxlan_port {
+       struct list_head list;
+       __be16 port;            /* VxLAN UDP dst port */
+       int port_aliases;       /* alias count */
+};
+
 struct be_adapter {
        struct pci_dev *pdev;
        struct net_device *netdev;
@@ -671,9 +677,9 @@ struct be_adapter {
        u32 sli_family;
        u8 hba_port_num;
        u16 pvid;
-       __be16 vxlan_port;
-       int vxlan_port_count;
-       int vxlan_port_aliases;
+       __be16 vxlan_port;              /* offloaded vxlan port num */
+       int vxlan_port_count;           /* active vxlan port count */
+       struct list_head vxlan_port_list;       /* vxlan port list */
        struct phy_info phy;
        u8 wol_cap;
        bool wol_en;
index 6be3b9aba8ed38875574f9e2fa018e80cc948cab..8702661b99c06f78a4a891b7afd94b955ee3dc58 100644 (file)
@@ -3857,6 +3857,44 @@ static void be_cancel_err_detection(struct be_adapter *adapter)
        }
 }
 
+static int be_enable_vxlan_offloads(struct be_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       struct device *dev = &adapter->pdev->dev;
+       struct be_vxlan_port *vxlan_port;
+       __be16 port;
+       int status;
+
+       vxlan_port = list_first_entry(&adapter->vxlan_port_list,
+                                     struct be_vxlan_port, list);
+       port = vxlan_port->port;
+
+       status = be_cmd_manage_iface(adapter, adapter->if_handle,
+                                    OP_CONVERT_NORMAL_TO_TUNNEL);
+       if (status) {
+               dev_warn(dev, "Failed to convert normal interface to tunnel\n");
+               return status;
+       }
+       adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS;
+
+       status = be_cmd_set_vxlan_port(adapter, port);
+       if (status) {
+               dev_warn(dev, "Failed to add VxLAN port\n");
+               return status;
+       }
+       adapter->vxlan_port = port;
+
+       netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+                                  NETIF_F_TSO | NETIF_F_TSO6 |
+                                  NETIF_F_GSO_UDP_TUNNEL;
+       netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
+       netdev->features |= NETIF_F_GSO_UDP_TUNNEL;
+
+       dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n",
+                be16_to_cpu(port));
+       return 0;
+}
+
 static void be_disable_vxlan_offloads(struct be_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
@@ -4903,63 +4941,59 @@ static struct be_cmd_work *be_alloc_work(struct be_adapter *adapter,
  * those other tunnels are unexported on the fly through ndo_features_check().
  *
  * Skyhawk supports VxLAN offloads only for one UDP dport. So, if the stack
- * adds more than one port, disable offloads and don't re-enable them again
- * until after all the tunnels are removed.
+ * adds more than one port, disable offloads and re-enable them again when
+ * there's only one port left. We maintain a list of ports for this purpose.
  */
 static void be_work_add_vxlan_port(struct work_struct *work)
 {
        struct be_cmd_work *cmd_work =
                                container_of(work, struct be_cmd_work, work);
        struct be_adapter *adapter = cmd_work->adapter;
-       struct net_device *netdev = adapter->netdev;
        struct device *dev = &adapter->pdev->dev;
        __be16 port = cmd_work->info.vxlan_port;
+       struct be_vxlan_port *vxlan_port;
        int status;
 
-       if (adapter->vxlan_port == port && adapter->vxlan_port_count) {
-               adapter->vxlan_port_aliases++;
-               goto done;
+       /* Bump up the alias count if it is an existing port */
+       list_for_each_entry(vxlan_port, &adapter->vxlan_port_list, list) {
+               if (vxlan_port->port == port) {
+                       vxlan_port->port_aliases++;
+                       goto done;
+               }
        }
 
+       /* Add a new port to our list. We don't need a lock here since port
+        * add/delete are done only in the context of a single-threaded work
+        * queue (be_wq).
+        */
+       vxlan_port = kzalloc(sizeof(*vxlan_port), GFP_KERNEL);
+       if (!vxlan_port)
+               goto done;
+
+       vxlan_port->port = port;
+       INIT_LIST_HEAD(&vxlan_port->list);
+       list_add_tail(&vxlan_port->list, &adapter->vxlan_port_list);
+       adapter->vxlan_port_count++;
+
        if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) {
                dev_info(dev,
                         "Only one UDP port supported for VxLAN offloads\n");
                dev_info(dev, "Disabling VxLAN offloads\n");
-               adapter->vxlan_port_count++;
                goto err;
        }
 
-       if (adapter->vxlan_port_count++ >= 1)
+       if (adapter->vxlan_port_count > 1)
                goto done;
 
-       status = be_cmd_manage_iface(adapter, adapter->if_handle,
-                                    OP_CONVERT_NORMAL_TO_TUNNEL);
-       if (status) {
-               dev_warn(dev, "Failed to convert normal interface to tunnel\n");
-               goto err;
-       }
-
-       status = be_cmd_set_vxlan_port(adapter, port);
-       if (status) {
-               dev_warn(dev, "Failed to add VxLAN port\n");
-               goto err;
-       }
-       adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS;
-       adapter->vxlan_port = port;
-
-       netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
-                                  NETIF_F_TSO | NETIF_F_TSO6 |
-                                  NETIF_F_GSO_UDP_TUNNEL;
-       netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
-       netdev->features |= NETIF_F_GSO_UDP_TUNNEL;
+       status = be_enable_vxlan_offloads(adapter);
+       if (!status)
+               goto done;
 
-       dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n",
-                be16_to_cpu(port));
-       goto done;
 err:
        be_disable_vxlan_offloads(adapter);
 done:
        kfree(cmd_work);
+       return;
 }
 
 static void be_work_del_vxlan_port(struct work_struct *work)
@@ -4968,23 +5002,40 @@ static void be_work_del_vxlan_port(struct work_struct *work)
                                container_of(work, struct be_cmd_work, work);
        struct be_adapter *adapter = cmd_work->adapter;
        __be16 port = cmd_work->info.vxlan_port;
+       struct be_vxlan_port *vxlan_port;
 
-       if (adapter->vxlan_port != port)
-               goto done;
+       /* Nothing to be done if a port alias is being deleted */
+       list_for_each_entry(vxlan_port, &adapter->vxlan_port_list, list) {
+               if (vxlan_port->port == port) {
+                       if (vxlan_port->port_aliases) {
+                               vxlan_port->port_aliases--;
+                               goto done;
+                       }
+                       break;
+               }
+       }
+
+       /* No port aliases left; delete the port from the list */
+       list_del(&vxlan_port->list);
+       adapter->vxlan_port_count--;
 
-       if (adapter->vxlan_port_aliases) {
-               adapter->vxlan_port_aliases--;
+       /* Disable VxLAN offload if this is the offloaded port */
+       if (adapter->vxlan_port == vxlan_port->port) {
+               WARN_ON(adapter->vxlan_port_count);
+               be_disable_vxlan_offloads(adapter);
+               dev_info(&adapter->pdev->dev,
+                        "Disabled VxLAN offloads for UDP port %d\n",
+                        be16_to_cpu(port));
                goto out;
        }
 
-       be_disable_vxlan_offloads(adapter);
+       /* If only 1 port is left, re-enable VxLAN offload */
+       if (adapter->vxlan_port_count == 1)
+               be_enable_vxlan_offloads(adapter);
 
-       dev_info(&adapter->pdev->dev,
-                "Disabled VxLAN offloads for UDP port %d\n",
-                be16_to_cpu(port));
-done:
-       adapter->vxlan_port_count--;
 out:
+       kfree(vxlan_port);
+done:
        kfree(cmd_work);
 }
 
@@ -5626,6 +5677,7 @@ static int be_drv_init(struct be_adapter *adapter)
        /* Must be a power of 2 or else MODULO will BUG_ON */
        adapter->be_get_temp_freq = 64;
 
+       INIT_LIST_HEAD(&adapter->vxlan_port_list);
        return 0;
 
 free_rx_filter: