]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
iavf: Introduce new state machines for flow director
authorPiotr Gardocki <piotrx.gardocki@intel.com>
Wed, 22 Nov 2023 03:47:15 +0000 (22:47 -0500)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Tue, 12 Dec 2023 19:20:40 +0000 (11:20 -0800)
New states introduced:

 IAVF_FDIR_FLTR_DIS_REQUEST
 IAVF_FDIR_FLTR_DIS_PENDING
 IAVF_FDIR_FLTR_INACTIVE

Current FDIR state machines (SM) are not adequate to handle a few
scenarios in the link DOWN/UP event, reset event and ntuple-feature.

For example, when VF link goes DOWN and comes back UP administratively,
the expectation is that previously installed filters should also be
restored. But with current SM, filters are not restored.
So with new SM, during link DOWN filters are marked as INACTIVE in
the iavf list but removed from PF. After link UP, SM will transition
from INACTIVE to ADD_REQUEST to restore the filter.

Similarly, with VF reset, filters will be removed from the PF, but
marked as INACTIVE in the iavf list. Filters will be restored after
reset completion.

Steps to reproduce:
-------------------

1. Create a VF. Here VF is enp8s0.

2. Assign IP addresses to VF and link partner and ping continuously
from remote. Here remote IP is 1.1.1.1.

3. Check default RX Queue of traffic.

ethtool -S enp8s0 | grep -E "rx-[[:digit:]]+\.packets"

4. Add filter - change default RX Queue (to 15 here)

ethtool -U ens8s0 flow-type ip4 src-ip 1.1.1.1 action 15 loc 5

5. Ensure filter gets added and traffic is received on RX queue 15 now.

Link event testing:
-------------------
6. Bring VF link down and up. If traffic flows to configured queue 15,
test is success, otherwise it is a failure.

Reset event testing:
--------------------
7. Reset the VF. If traffic flows to configured queue 15, test is success,
otherwise it is a failure.

Fixes: 0dbfbabb840d ("iavf: Add framework to enable ethtool ntuple filters")
Signed-off-by: Piotr Gardocki <piotrx.gardocki@intel.com>
Reviewed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Ranganatha Rao <ranganatha.rao@intel.com>
Tested-by: Rafal Romanowski <rafal.romanowski@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_ethtool.c
drivers/net/ethernet/intel/iavf/iavf_fdir.h
drivers/net/ethernet/intel/iavf/iavf_main.c
drivers/net/ethernet/intel/iavf/iavf_virtchnl.c

index e7ab89dc883aa63927e15e1e1383ae271e38348f..63b45c61cc4aa3631a3e5715af64d60c9b48d326 100644 (file)
@@ -292,6 +292,7 @@ struct iavf_adapter {
 #define IAVF_FLAG_QUEUES_DISABLED              BIT(17)
 #define IAVF_FLAG_SETUP_NETDEV_FEATURES                BIT(18)
 #define IAVF_FLAG_REINIT_MSIX_NEEDED           BIT(20)
+#define IAVF_FLAG_FDIR_ENABLED                 BIT(21)
 /* duplicates for common code */
 #define IAVF_FLAG_DCB_ENABLED                  0
        /* flags for admin queue service task */
index 19cbfe554689f4718f8366d0792b2d19c678f2e3..dc499fe7734ec9afee6c5794f568e7e6d189f721 100644 (file)
@@ -1061,7 +1061,7 @@ iavf_get_ethtool_fdir_entry(struct iavf_adapter *adapter,
        struct iavf_fdir_fltr *rule = NULL;
        int ret = 0;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        spin_lock_bh(&adapter->fdir_fltr_lock);
@@ -1203,7 +1203,7 @@ iavf_get_fdir_fltr_ids(struct iavf_adapter *adapter, struct ethtool_rxnfc *cmd,
        unsigned int cnt = 0;
        int val = 0;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        cmd->data = IAVF_MAX_FDIR_FILTERS;
@@ -1395,7 +1395,7 @@ static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
        int count = 50;
        int err;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        if (fsp->flow_type & FLOW_MAC_EXT)
@@ -1436,12 +1436,16 @@ static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
        spin_lock_bh(&adapter->fdir_fltr_lock);
        iavf_fdir_list_add_fltr(adapter, fltr);
        adapter->fdir_active_fltr++;
-       fltr->state = IAVF_FDIR_FLTR_ADD_REQUEST;
-       adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+       if (adapter->link_up) {
+               fltr->state = IAVF_FDIR_FLTR_ADD_REQUEST;
+               adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+       } else {
+               fltr->state = IAVF_FDIR_FLTR_INACTIVE;
+       }
        spin_unlock_bh(&adapter->fdir_fltr_lock);
 
-       mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
-
+       if (adapter->link_up)
+               mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
 ret:
        if (err && fltr)
                kfree(fltr);
@@ -1463,7 +1467,7 @@ static int iavf_del_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
        struct iavf_fdir_fltr *fltr = NULL;
        int err = 0;
 
-       if (!FDIR_FLTR_SUPPORT(adapter))
+       if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                return -EOPNOTSUPP;
 
        spin_lock_bh(&adapter->fdir_fltr_lock);
@@ -1472,6 +1476,11 @@ static int iavf_del_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
                if (fltr->state == IAVF_FDIR_FLTR_ACTIVE) {
                        fltr->state = IAVF_FDIR_FLTR_DEL_REQUEST;
                        adapter->aq_required |= IAVF_FLAG_AQ_DEL_FDIR_FILTER;
+               } else if (fltr->state == IAVF_FDIR_FLTR_INACTIVE) {
+                       list_del(&fltr->list);
+                       kfree(fltr);
+                       adapter->fdir_active_fltr--;
+                       fltr = NULL;
                } else {
                        err = -EBUSY;
                }
@@ -1780,7 +1789,7 @@ static int iavf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
                ret = 0;
                break;
        case ETHTOOL_GRXCLSRLCNT:
-               if (!FDIR_FLTR_SUPPORT(adapter))
+               if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
                        break;
                spin_lock_bh(&adapter->fdir_fltr_lock);
                cmd->rule_cnt = adapter->fdir_active_fltr;
index 9eb9f73f6adf3a52b42d0751032adff5fad9bcc0..d31bd923ba8cbf2d86272f6955847cc92d2db5e6 100644 (file)
@@ -6,12 +6,25 @@
 
 struct iavf_adapter;
 
-/* State of Flow Director filter */
+/* State of Flow Director filter
+ *
+ * *_REQUEST states are used to mark filter to be sent to PF driver to perform
+ * an action (either add or delete filter). *_PENDING states are an indication
+ * that request was sent to PF and the driver is waiting for response.
+ *
+ * Both DELETE and DISABLE states are being used to delete a filter in PF.
+ * The difference is that after a successful response filter in DEL_PENDING
+ * state is being deleted from VF driver as well and filter in DIS_PENDING state
+ * is being changed to INACTIVE state.
+ */
 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_DIS_REQUEST,     /* Filter scheduled to be disabled */
+       IAVF_FDIR_FLTR_DIS_PENDING,     /* Filter pending disable by the PF */
+       IAVF_FDIR_FLTR_INACTIVE,        /* Filter inactive on link down */
        IAVF_FDIR_FLTR_ACTIVE,          /* Filter is active */
 };
 
index c862ebcd2e392ef0a086c4033fadfae9fc8ceb63..567435e2393645e4ffae4c320d6a2b8c2dd3cbfd 100644 (file)
@@ -1353,18 +1353,20 @@ static void iavf_clear_cloud_filters(struct iavf_adapter *adapter)
  **/
 static void iavf_clear_fdir_filters(struct iavf_adapter *adapter)
 {
-       struct iavf_fdir_fltr *fdir, *fdirtmp;
+       struct iavf_fdir_fltr *fdir;
 
        /* remove all Flow Director filters */
        spin_lock_bh(&adapter->fdir_fltr_lock);
-       list_for_each_entry_safe(fdir, fdirtmp, &adapter->fdir_list_head,
-                                list) {
+       list_for_each_entry(fdir, &adapter->fdir_list_head, list) {
                if (fdir->state == IAVF_FDIR_FLTR_ADD_REQUEST) {
-                       list_del(&fdir->list);
-                       kfree(fdir);
-                       adapter->fdir_active_fltr--;
-               } else {
-                       fdir->state = IAVF_FDIR_FLTR_DEL_REQUEST;
+                       /* Cancel a request, keep filter as inactive */
+                       fdir->state = IAVF_FDIR_FLTR_INACTIVE;
+               } else if (fdir->state == IAVF_FDIR_FLTR_ADD_PENDING ||
+                        fdir->state == IAVF_FDIR_FLTR_ACTIVE) {
+                       /* Disable filters which are active or have a pending
+                        * request to PF to be added
+                        */
+                       fdir->state = IAVF_FDIR_FLTR_DIS_REQUEST;
                }
        }
        spin_unlock_bh(&adapter->fdir_fltr_lock);
@@ -4112,6 +4114,33 @@ static int iavf_setup_tc(struct net_device *netdev, enum tc_setup_type type,
        }
 }
 
+/**
+ * iavf_restore_fdir_filters
+ * @adapter: board private structure
+ *
+ * Restore existing FDIR filters when VF netdev comes back up.
+ **/
+static void iavf_restore_fdir_filters(struct iavf_adapter *adapter)
+{
+       struct iavf_fdir_fltr *f;
+
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry(f, &adapter->fdir_list_head, list) {
+               if (f->state == IAVF_FDIR_FLTR_DIS_REQUEST) {
+                       /* Cancel a request, keep filter as active */
+                       f->state = IAVF_FDIR_FLTR_ACTIVE;
+               } else if (f->state == IAVF_FDIR_FLTR_DIS_PENDING ||
+                          f->state == IAVF_FDIR_FLTR_INACTIVE) {
+                       /* Add filters which are inactive or have a pending
+                        * request to PF to be deleted
+                        */
+                       f->state = IAVF_FDIR_FLTR_ADD_REQUEST;
+                       adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+               }
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+}
+
 /**
  * iavf_open - Called when a network interface is made active
  * @netdev: network interface device structure
@@ -4179,8 +4208,9 @@ static int iavf_open(struct net_device *netdev)
 
        spin_unlock_bh(&adapter->mac_vlan_list_lock);
 
-       /* Restore VLAN filters that were removed with IFF_DOWN */
+       /* Restore filters that were removed with IFF_DOWN */
        iavf_restore_filters(adapter);
+       iavf_restore_fdir_filters(adapter);
 
        iavf_configure(adapter);
 
index 64c4443dbef9caca468d919c4cf5b1a4c6dfa7ca..2d9366be0ec506c9b69c3b25d69044a4e47ab03b 100644 (file)
@@ -1735,8 +1735,8 @@ void iavf_add_fdir_filter(struct iavf_adapter *adapter)
  **/
 void iavf_del_fdir_filter(struct iavf_adapter *adapter)
 {
+       struct virtchnl_fdir_del f = {};
        struct iavf_fdir_fltr *fdir;
-       struct virtchnl_fdir_del f;
        bool process_fltr = false;
        int len;
 
@@ -1753,11 +1753,16 @@ void iavf_del_fdir_filter(struct iavf_adapter *adapter)
        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;
+               } else if (fdir->state == IAVF_FDIR_FLTR_DIS_REQUEST) {
+                       process_fltr = true;
+                       f.vsi_id = fdir->vc_add_msg.vsi_id;
+                       f.flow_id = fdir->flow_id;
+                       fdir->state = IAVF_FDIR_FLTR_DIS_PENDING;
+                       break;
                }
        }
        spin_unlock_bh(&adapter->fdir_fltr_lock);
@@ -1901,6 +1906,48 @@ static void iavf_netdev_features_vlan_strip_set(struct net_device *netdev,
                netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
 }
 
+/**
+ * iavf_activate_fdir_filters - Reactivate all FDIR filters after a reset
+ * @adapter: private adapter structure
+ *
+ * Called after a reset to re-add all FDIR filters and delete some of them
+ * if they were pending to be deleted.
+ */
+static void iavf_activate_fdir_filters(struct iavf_adapter *adapter)
+{
+       struct iavf_fdir_fltr *f, *ftmp;
+       bool add_filters = false;
+
+       spin_lock_bh(&adapter->fdir_fltr_lock);
+       list_for_each_entry_safe(f, ftmp, &adapter->fdir_list_head, list) {
+               if (f->state == IAVF_FDIR_FLTR_ADD_REQUEST ||
+                   f->state == IAVF_FDIR_FLTR_ADD_PENDING ||
+                   f->state == IAVF_FDIR_FLTR_ACTIVE) {
+                       /* All filters and requests have been removed in PF,
+                        * restore them
+                        */
+                       f->state = IAVF_FDIR_FLTR_ADD_REQUEST;
+                       add_filters = true;
+               } else if (f->state == IAVF_FDIR_FLTR_DIS_REQUEST ||
+                          f->state == IAVF_FDIR_FLTR_DIS_PENDING) {
+                       /* Link down state, leave filters as inactive */
+                       f->state = IAVF_FDIR_FLTR_INACTIVE;
+               } else if (f->state == IAVF_FDIR_FLTR_DEL_REQUEST ||
+                          f->state == IAVF_FDIR_FLTR_DEL_PENDING) {
+                       /* Delete filters that were pending to be deleted, the
+                        * list on PF is already cleared after a reset
+                        */
+                       list_del(&f->list);
+                       kfree(f);
+                       adapter->fdir_active_fltr--;
+               }
+       }
+       spin_unlock_bh(&adapter->fdir_fltr_lock);
+
+       if (add_filters)
+               adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
+}
+
 /**
  * iavf_virtchnl_completion
  * @adapter: adapter structure
@@ -2078,7 +2125,8 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                        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) {
+                               if (fdir->state == IAVF_FDIR_FLTR_DEL_PENDING ||
+                                   fdir->state == IAVF_FDIR_FLTR_DIS_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,
@@ -2214,6 +2262,8 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
 
                spin_unlock_bh(&adapter->mac_vlan_list_lock);
 
+               iavf_activate_fdir_filters(adapter);
+
                iavf_parse_vf_resource_msg(adapter);
 
                /* negotiated VIRTCHNL_VF_OFFLOAD_VLAN_V2, so wait for the
@@ -2390,7 +2440,9 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                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) {
+                               if (del_fltr->status == VIRTCHNL_FDIR_SUCCESS ||
+                                   del_fltr->status ==
+                                   VIRTCHNL_FDIR_FAILURE_RULE_NONEXIST) {
                                        dev_info(&adapter->pdev->dev, "Flow Director filter with location %u is deleted\n",
                                                 fdir->loc);
                                        list_del(&fdir->list);
@@ -2402,6 +2454,17 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                                                 del_fltr->status);
                                        iavf_print_fdir_fltr(adapter, fdir);
                                }
+                       } else if (fdir->state == IAVF_FDIR_FLTR_DIS_PENDING) {
+                               if (del_fltr->status == VIRTCHNL_FDIR_SUCCESS ||
+                                   del_fltr->status ==
+                                   VIRTCHNL_FDIR_FAILURE_RULE_NONEXIST) {
+                                       fdir->state = IAVF_FDIR_FLTR_INACTIVE;
+                               } else {
+                                       fdir->state = IAVF_FDIR_FLTR_ACTIVE;
+                                       dev_info(&adapter->pdev->dev, "Failed to disable Flow Director filter with status: %d\n",
+                                                del_fltr->status);
+                                       iavf_print_fdir_fltr(adapter, fdir);
+                               }
                        }
                }
                spin_unlock_bh(&adapter->fdir_fltr_lock);