}
}
+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;
* 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)
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);
}
/* 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: