]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
iavf: Add framework to enable ethtool ntuple filters
authorHaiyue Wang <haiyue.wang@intel.com>
Tue, 9 Mar 2021 03:08:11 +0000 (11:08 +0800)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Mon, 22 Mar 2021 18:32:12 +0000 (11:32 -0700)
Enable ethtool ntuple filter support on the VF driver using the virtchnl
interface to the PF driver and the Flow director functionality in the
hardware.

Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
Tested-by: Chen Bo <BoX.C.Chen@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/iavf/iavf.h
drivers/net/ethernet/intel/iavf/iavf_fdir.h [new file with mode: 0644]
drivers/net/ethernet/intel/iavf/iavf_main.c
drivers/net/ethernet/intel/iavf/iavf_virtchnl.c

index 8a65525a7c0d26233b14f8ae87816409e141211d..bda2a900df8e7bba55b4122370ece8e108bf0d1c 100644 (file)
@@ -37,6 +37,7 @@
 #include "iavf_type.h"
 #include <linux/avf/virtchnl.h>
 #include "iavf_txrx.h"
+#include "iavf_fdir.h"
 
 #define DEFAULT_DEBUG_LEVEL_SHIFT 3
 #define PFX "iavf: "
@@ -300,6 +301,8 @@ struct iavf_adapter {
 #define IAVF_FLAG_AQ_DISABLE_CHANNELS          BIT(22)
 #define IAVF_FLAG_AQ_ADD_CLOUD_FILTER          BIT(23)
 #define IAVF_FLAG_AQ_DEL_CLOUD_FILTER          BIT(24)
+#define IAVF_FLAG_AQ_ADD_FDIR_FILTER           BIT(25)
+#define IAVF_FLAG_AQ_DEL_FDIR_FILTER           BIT(26)
 
        /* OS defined structs */
        struct net_device *netdev;
@@ -340,6 +343,8 @@ struct iavf_adapter {
                          VIRTCHNL_VF_OFFLOAD_VLAN)
 #define ADV_LINK_SUPPORT(_a) ((_a)->vf_res->vf_cap_flags & \
                              VIRTCHNL_VF_CAP_ADV_LINK_SPEED)
+#define FDIR_FLTR_SUPPORT(_a) ((_a)->vf_res->vf_cap_flags & \
+                              VIRTCHNL_VF_OFFLOAD_FDIR_PF)
        struct virtchnl_vf_resource *vf_res; /* incl. all VSIs */
        struct virtchnl_vsi_resource *vsi_res; /* our LAN VSI */
        struct virtchnl_version_info pf_version;
@@ -362,6 +367,11 @@ struct iavf_adapter {
        /* lock to protect access to the cloud filter list */
        spinlock_t cloud_filter_list_lock;
        u16 num_cloud_filters;
+
+#define IAVF_MAX_FDIR_FILTERS 128      /* max allowed Flow Director filters */
+       u16 fdir_active_fltr;
+       struct list_head fdir_list_head;
+       spinlock_t fdir_fltr_lock;      /* protect the Flow Director filter list */
 };
 
 
@@ -432,6 +442,8 @@ void iavf_enable_channels(struct iavf_adapter *adapter);
 void iavf_disable_channels(struct iavf_adapter *adapter);
 void iavf_add_cloud_filter(struct iavf_adapter *adapter);
 void iavf_del_cloud_filter(struct iavf_adapter *adapter);
+void iavf_add_fdir_filter(struct iavf_adapter *adapter);
+void iavf_del_fdir_filter(struct iavf_adapter *adapter);
 struct iavf_mac_filter *iavf_add_filter(struct iavf_adapter *adapter,
                                        const u8 *macaddr);
 #endif /* _IAVF_H_ */
diff --git a/drivers/net/ethernet/intel/iavf/iavf_fdir.h b/drivers/net/ethernet/intel/iavf/iavf_fdir.h
new file mode 100644 (file)
index 0000000..4e6494b
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2021, Intel Corporation. */
+
+#ifndef _IAVF_FDIR_H_
+#define _IAVF_FDIR_H_
+
+struct iavf_adapter;
+
+/* State of Flow Director filter */
+enum iavf_fdir_fltr_state_t {
+       IAVF_FDIR_FLTR_ADD_REQUEST,     /* User requests to add filter */
+       IAVF_FDIR_FLTR_ADD_PENDING,     /* Filter pending add by the PF */
+       IAVF_FDIR_FLTR_DEL_REQUEST,     /* User requests to delete filter */
+       IAVF_FDIR_FLTR_DEL_PENDING,     /* Filter pending delete by the PF */
+       IAVF_FDIR_FLTR_ACTIVE,          /* Filter is active */
+};
+
+/* bookkeeping of Flow Director filters */
+struct iavf_fdir_fltr {
+       enum iavf_fdir_fltr_state_t state;
+       struct list_head list;
+
+       u32 flow_id;
+
+       struct virtchnl_fdir_add vc_add_msg;
+};
+
+int iavf_fill_fdir_add_msg(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
+void iavf_print_fdir_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
+bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
+void iavf_fdir_list_add_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr);
+struct iavf_fdir_fltr *iavf_find_fdir_fltr_by_loc(struct iavf_adapter *adapter, u32 loc);
+#endif /* _IAVF_FDIR_H_ */
index dc5b3c06d1e01451917d75e5bedf83bdf8bec8da..d0c16117484f55996b40f6b7b086bc3e6cd2a58c 100644 (file)
@@ -959,8 +959,9 @@ void iavf_down(struct iavf_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
        struct iavf_vlan_filter *vlf;
-       struct iavf_mac_filter *f;
        struct iavf_cloud_filter *cf;
+       struct iavf_fdir_fltr *fdir;
+       struct iavf_mac_filter *f;
 
        if (adapter->state <= __IAVF_DOWN_PENDING)
                return;
@@ -996,6 +997,13 @@ void iavf_down(struct iavf_adapter *adapter)
        }
        spin_unlock_bh(&adapter->cloud_filter_list_lock);
 
+       /* remove all Flow Director filters */
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry(fdir, &adapter->fdir_list_head, list) {
+               fdir->state = IAVF_FDIR_FLTR_DEL_REQUEST;
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+
        if (!(adapter->flags & IAVF_FLAG_PF_COMMS_FAILED) &&
            adapter->state != __IAVF_RESETTING) {
                /* cancel any current operation */
@@ -1007,6 +1015,7 @@ void iavf_down(struct iavf_adapter *adapter)
                adapter->aq_required = IAVF_FLAG_AQ_DEL_MAC_FILTER;
                adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER;
                adapter->aq_required |= IAVF_FLAG_AQ_DEL_CLOUD_FILTER;
+               adapter->aq_required |= IAVF_FLAG_AQ_DEL_FDIR_FILTER;
                adapter->aq_required |= IAVF_FLAG_AQ_DISABLE_QUEUES;
        }
 
@@ -1629,6 +1638,14 @@ static int iavf_process_aq_command(struct iavf_adapter *adapter)
                iavf_add_cloud_filter(adapter);
                return 0;
        }
+       if (adapter->aq_required & IAVF_FLAG_AQ_ADD_FDIR_FILTER) {
+               iavf_add_fdir_filter(adapter);
+               return IAVF_SUCCESS;
+       }
+       if (adapter->aq_required & IAVF_FLAG_AQ_DEL_FDIR_FILTER) {
+               iavf_del_fdir_filter(adapter);
+               return IAVF_SUCCESS;
+       }
        return -EAGAIN;
 }
 
@@ -3738,10 +3755,12 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        spin_lock_init(&adapter->mac_vlan_list_lock);
        spin_lock_init(&adapter->cloud_filter_list_lock);
+       spin_lock_init(&adapter->fdir_fltr_lock);
 
        INIT_LIST_HEAD(&adapter->mac_filter_list);
        INIT_LIST_HEAD(&adapter->vlan_filter_list);
        INIT_LIST_HEAD(&adapter->cloud_filter_list);
+       INIT_LIST_HEAD(&adapter->fdir_list_head);
 
        INIT_WORK(&adapter->reset_task, iavf_reset_task);
        INIT_WORK(&adapter->adminq_task, iavf_adminq_task);
@@ -3845,6 +3864,7 @@ static void iavf_remove(struct pci_dev *pdev)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct iavf_adapter *adapter = netdev_priv(netdev);
+       struct iavf_fdir_fltr *fdir, *fdirtmp;
        struct iavf_vlan_filter *vlf, *vlftmp;
        struct iavf_mac_filter *f, *ftmp;
        struct iavf_cloud_filter *cf, *cftmp;
@@ -3926,6 +3946,13 @@ static void iavf_remove(struct pci_dev *pdev)
        }
        spin_unlock_bh(&adapter->cloud_filter_list_lock);
 
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry_safe(fdir, fdirtmp, &adapter->fdir_list_head, list) {
+               list_del(&fdir->list);
+               kfree(fdir);
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+
        free_netdev(netdev);
 
        pci_disable_pcie_error_reporting(pdev);
index 647e7fde11b40daeb4950a66e9acef06e73bee1b..9b8a5b700cff10eda401ee4f280a304514b9872d 100644 (file)
@@ -140,6 +140,7 @@ int iavf_send_vf_config_msg(struct iavf_adapter *adapter)
               VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM |
               VIRTCHNL_VF_OFFLOAD_REQ_QUEUES |
               VIRTCHNL_VF_OFFLOAD_ADQ |
+              VIRTCHNL_VF_OFFLOAD_FDIR_PF |
               VIRTCHNL_VF_CAP_ADV_LINK_SPEED;
 
        adapter->current_op = VIRTCHNL_OP_GET_VF_RESOURCES;
@@ -1197,6 +1198,101 @@ void iavf_del_cloud_filter(struct iavf_adapter *adapter)
        kfree(f);
 }
 
+/**
+ * iavf_add_fdir_filter
+ * @adapter: the VF adapter structure
+ *
+ * Request that the PF add Flow Director filters as specified
+ * by the user via ethtool.
+ **/
+void iavf_add_fdir_filter(struct iavf_adapter *adapter)
+{
+       struct iavf_fdir_fltr *fdir;
+       struct virtchnl_fdir_add *f;
+       bool process_fltr = false;
+       int len;
+
+       if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
+               /* bail because we already have a command pending */
+               dev_err(&adapter->pdev->dev, "Cannot add Flow Director filter, command %d pending\n",
+                       adapter->current_op);
+               return;
+       }
+
+       len = sizeof(struct virtchnl_fdir_add);
+       f = kzalloc(len, GFP_KERNEL);
+       if (!f)
+               return;
+
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry(fdir, &adapter->fdir_list_head, list) {
+               if (fdir->state == IAVF_FDIR_FLTR_ADD_REQUEST) {
+                       process_fltr = true;
+                       fdir->state = IAVF_FDIR_FLTR_ADD_PENDING;
+                       memcpy(f, &fdir->vc_add_msg, len);
+                       break;
+               }
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+
+       if (!process_fltr) {
+               /* prevent iavf_add_fdir_filter() from being called when there
+                * are no filters to add
+                */
+               adapter->aq_required &= ~IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+               kfree(f);
+               return;
+       }
+       adapter->current_op = VIRTCHNL_OP_ADD_FDIR_FILTER;
+       iavf_send_pf_msg(adapter, VIRTCHNL_OP_ADD_FDIR_FILTER, (u8 *)f, len);
+       kfree(f);
+}
+
+/**
+ * iavf_del_fdir_filter
+ * @adapter: the VF adapter structure
+ *
+ * Request that the PF delete Flow Director filters as specified
+ * by the user via ethtool.
+ **/
+void iavf_del_fdir_filter(struct iavf_adapter *adapter)
+{
+       struct iavf_fdir_fltr *fdir;
+       struct virtchnl_fdir_del f;
+       bool process_fltr = false;
+       int len;
+
+       if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) {
+               /* bail because we already have a command pending */
+               dev_err(&adapter->pdev->dev, "Cannot remove Flow Director filter, command %d pending\n",
+                       adapter->current_op);
+               return;
+       }
+
+       len = sizeof(struct virtchnl_fdir_del);
+
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry(fdir, &adapter->fdir_list_head, list) {
+               if (fdir->state == IAVF_FDIR_FLTR_DEL_REQUEST) {
+                       process_fltr = true;
+                       memset(&f, 0, len);
+                       f.vsi_id = fdir->vc_add_msg.vsi_id;
+                       f.flow_id = fdir->flow_id;
+                       fdir->state = IAVF_FDIR_FLTR_DEL_PENDING;
+                       break;
+               }
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+
+       if (!process_fltr) {
+               adapter->aq_required &= ~IAVF_FLAG_AQ_DEL_FDIR_FILTER;
+               return;
+       }
+
+       adapter->current_op = VIRTCHNL_OP_DEL_FDIR_FILTER;
+       iavf_send_pf_msg(adapter, VIRTCHNL_OP_DEL_FDIR_FILTER, (u8 *)&f, len);
+}
+
 /**
  * iavf_request_reset
  * @adapter: adapter structure
@@ -1357,6 +1453,48 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                        }
                        }
                        break;
+               case VIRTCHNL_OP_ADD_FDIR_FILTER: {
+                       struct iavf_fdir_fltr *fdir, *fdir_tmp;
+
+                       spin_lock_bh(&adapter->fdir_fltr_lock);
+                       list_for_each_entry_safe(fdir, fdir_tmp,
+                                                &adapter->fdir_list_head,
+                                                list) {
+                               if (fdir->state == IAVF_FDIR_FLTR_ADD_PENDING) {
+                                       dev_info(&adapter->pdev->dev, "Failed to add Flow Director filter, error %s\n",
+                                                iavf_stat_str(&adapter->hw,
+                                                              v_retval));
+                                       if (msglen)
+                                               dev_err(&adapter->pdev->dev,
+                                                       "%s\n", msg);
+                                       list_del(&fdir->list);
+                                       kfree(fdir);
+                                       adapter->fdir_active_fltr--;
+                               }
+                       }
+                       spin_unlock_bh(&adapter->fdir_fltr_lock);
+                       }
+                       break;
+               case VIRTCHNL_OP_DEL_FDIR_FILTER: {
+                       struct iavf_fdir_fltr *fdir;
+
+                       spin_lock_bh(&adapter->fdir_fltr_lock);
+                       list_for_each_entry(fdir, &adapter->fdir_list_head,
+                                           list) {
+                               if (fdir->state == IAVF_FDIR_FLTR_DEL_PENDING) {
+                                       fdir->state = IAVF_FDIR_FLTR_ACTIVE;
+                                       dev_info(&adapter->pdev->dev, "Failed to del Flow Director filter, error %s\n",
+                                                iavf_stat_str(&adapter->hw,
+                                                              v_retval));
+                               }
+                       }
+                       spin_unlock_bh(&adapter->fdir_fltr_lock);
+                       }
+                       break;
+               case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
+               case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
+                       dev_warn(&adapter->pdev->dev, "Changing VLAN Stripping is not allowed when Port VLAN is configured\n");
+                       break;
                default:
                        dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n",
                                v_retval, iavf_stat_str(&adapter->hw, v_retval),
@@ -1490,6 +1628,52 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                }
                }
                break;
+       case VIRTCHNL_OP_ADD_FDIR_FILTER: {
+               struct virtchnl_fdir_add *add_fltr = (struct virtchnl_fdir_add *)msg;
+               struct iavf_fdir_fltr *fdir, *fdir_tmp;
+
+               spin_lock_bh(&adapter->fdir_fltr_lock);
+               list_for_each_entry_safe(fdir, fdir_tmp,
+                                        &adapter->fdir_list_head,
+                                        list) {
+                       if (fdir->state == IAVF_FDIR_FLTR_ADD_PENDING) {
+                               if (add_fltr->status == VIRTCHNL_FDIR_SUCCESS) {
+                                       fdir->state = IAVF_FDIR_FLTR_ACTIVE;
+                                       fdir->flow_id = add_fltr->flow_id;
+                               } else {
+                                       dev_info(&adapter->pdev->dev, "Failed to add Flow Director filter with status: %d\n",
+                                                add_fltr->status);
+                                       list_del(&fdir->list);
+                                       kfree(fdir);
+                                       adapter->fdir_active_fltr--;
+                               }
+                       }
+               }
+               spin_unlock_bh(&adapter->fdir_fltr_lock);
+               }
+               break;
+       case VIRTCHNL_OP_DEL_FDIR_FILTER: {
+               struct virtchnl_fdir_del *del_fltr = (struct virtchnl_fdir_del *)msg;
+               struct iavf_fdir_fltr *fdir, *fdir_tmp;
+
+               spin_lock_bh(&adapter->fdir_fltr_lock);
+               list_for_each_entry_safe(fdir, fdir_tmp, &adapter->fdir_list_head,
+                                        list) {
+                       if (fdir->state == IAVF_FDIR_FLTR_DEL_PENDING) {
+                               if (del_fltr->status == VIRTCHNL_FDIR_SUCCESS) {
+                                       list_del(&fdir->list);
+                                       kfree(fdir);
+                                       adapter->fdir_active_fltr--;
+                               } else {
+                                       fdir->state = IAVF_FDIR_FLTR_ACTIVE;
+                                       dev_info(&adapter->pdev->dev, "Failed to delete Flow Director filter with status: %d\n",
+                                                del_fltr->status);
+                               }
+                       }
+               }
+               spin_unlock_bh(&adapter->fdir_fltr_lock);
+               }
+               break;
        default:
                if (adapter->current_op && (v_opcode != adapter->current_op))
                        dev_warn(&adapter->pdev->dev, "Expected response %d from PF, received %d\n",