|
@@ -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:
|