i40e: ATR policy change to flush the table to clean stale ATR rules
authorAnjali Singhai Jain <anjali.singhai@intel.com>
Thu, 10 Jul 2014 08:03:26 +0000 (08:03 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Wed, 27 Aug 2014 06:51:37 +0000 (23:51 -0700)
Instead of disabling ATR when we get a programming error, we now
will wait it out to see if some room gets created by ATR rule deletion.
If we still have too many errors and ATR filter count did not change
much, its time to flush and replay. We no more auto-disable ATR when
we have errors in programming.
The disabling of ATR when we get programming error was buggy and
was still adding new rules and causing continuous errors. With this
policy change we flush instead when we see too many errors.
ATR is still disabled if we add a SB rule for TCP/IPv4 flow type,
more logic is added to re-enable it once all SB TCP/IPv4 rules are gone.

Change-ID: I77edcbeab9500c72a7e0bd7b5c5b113ced133a9c
Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com>
Tested-by: Jim Young <jamesx.m.young@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c

index 801da392a20e23dc84e176579720f0a106695776..0c6a9b97f7e2a5060430396359c4377b2681c4e0 100644 (file)
@@ -144,6 +144,7 @@ enum i40e_state_t {
        __I40E_PTP_TX_IN_PROGRESS,
        __I40E_BAD_EEPROM,
        __I40E_DOWN_REQUESTED,
+       __I40E_FD_FLUSH_REQUESTED,
 };
 
 enum i40e_interrupt_policy {
@@ -250,6 +251,10 @@ struct i40e_pf {
        u16 fdir_pf_active_filters;
        u16 fd_sb_cnt_idx;
        u16 fd_atr_cnt_idx;
+       unsigned long fd_flush_timestamp;
+       u32 fd_add_err;
+       u32 fd_atr_cnt;
+       u32 fd_tcp_rule;
 
 #ifdef CONFIG_I40E_VXLAN
        __be16  vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
@@ -608,6 +613,7 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi,
 void i40e_fdir_check_and_reenable(struct i40e_pf *pf);
 int i40e_get_current_fd_count(struct i40e_pf *pf);
 int i40e_get_cur_guaranteed_fd_count(struct i40e_pf *pf);
+int i40e_get_current_atr_cnt(struct i40e_pf *pf);
 bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features);
 void i40e_set_ethtool_ops(struct net_device *netdev);
 struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
index e8ba7470700af1abaaf33826c7bffc8409666b9e..7204e293dc71d39cfc2c86ddec91439f0135cf87 100644 (file)
@@ -1977,6 +1977,9 @@ static int i40e_del_fdir_entry(struct i40e_vsi *vsi,
        struct i40e_pf *pf = vsi->back;
        int ret = 0;
 
+       if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state))
+               return -EBUSY;
+
        ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd);
 
        i40e_fdir_check_and_reenable(pf);
@@ -2010,6 +2013,9 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
        if (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)
                return -ENOSPC;
 
+       if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state))
+               return -EBUSY;
+
        fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
 
        if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort +
index eddec6ba095b7105118962438c9914d40acd97b4..cab9c9e3e694039b0ac207c5671c1436561729bd 100644 (file)
@@ -4483,8 +4483,16 @@ static int i40e_up_complete(struct i40e_vsi *vsi)
        }
 
        /* replay FDIR SB filters */
-       if (vsi->type == I40E_VSI_FDIR)
+       if (vsi->type == I40E_VSI_FDIR) {
+               /* reset fd counters */
+               pf->fd_add_err = pf->fd_atr_cnt = 0;
+               if (pf->fd_tcp_rule > 0) {
+                       pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+                       dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 exist\n");
+                       pf->fd_tcp_rule = 0;
+               }
                i40e_fdir_filter_restore(vsi);
+       }
        i40e_service_event_schedule(pf);
 
        return 0;
@@ -5125,6 +5133,7 @@ int i40e_get_current_fd_count(struct i40e_pf *pf)
                      I40E_PFQF_FDSTAT_BEST_CNT_SHIFT);
        return fcnt_prog;
 }
+
 /**
  * i40e_fdir_check_and_reenable - Function to reenabe FD ATR or SB if disabled
  * @pf: board private structure
@@ -5133,15 +5142,17 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
 {
        u32 fcnt_prog, fcnt_avail;
 
+       if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state))
+               return;
+
        /* Check if, FD SB or ATR was auto disabled and if there is enough room
         * to re-enable
         */
-       if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
-           (pf->flags & I40E_FLAG_FD_SB_ENABLED))
-               return;
        fcnt_prog = i40e_get_cur_guaranteed_fd_count(pf);
        fcnt_avail = pf->fdir_pf_filter_count;
-       if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) {
+       if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) ||
+           (pf->fd_add_err == 0) ||
+           (i40e_get_current_atr_cnt(pf) < pf->fd_atr_cnt)) {
                if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
                    (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) {
                        pf->auto_disable_flags &= ~I40E_FLAG_FD_SB_ENABLED;
@@ -5158,23 +5169,83 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
        }
 }
 
+#define I40E_MIN_FD_FLUSH_INTERVAL 10
+/**
+ * i40e_fdir_flush_and_replay - Function to flush all FD filters and replay SB
+ * @pf: board private structure
+ **/
+static void i40e_fdir_flush_and_replay(struct i40e_pf *pf)
+{
+       int flush_wait_retry = 50;
+       int reg;
+
+       if (time_after(jiffies, pf->fd_flush_timestamp +
+                               (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) {
+               set_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state);
+               pf->fd_flush_timestamp = jiffies;
+               pf->auto_disable_flags |= I40E_FLAG_FD_SB_ENABLED;
+               pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+               /* flush all filters */
+               wr32(&pf->hw, I40E_PFQF_CTL_1,
+                    I40E_PFQF_CTL_1_CLEARFDTABLE_MASK);
+               i40e_flush(&pf->hw);
+               pf->fd_add_err = 0;
+               do {
+                       /* Check FD flush status every 5-6msec */
+                       usleep_range(5000, 6000);
+                       reg = rd32(&pf->hw, I40E_PFQF_CTL_1);
+                       if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK))
+                               break;
+               } while (flush_wait_retry--);
+               if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) {
+                       dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n");
+               } else {
+                       /* replay sideband filters */
+                       i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]);
+
+                       pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+                       pf->auto_disable_flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+                       pf->auto_disable_flags &= ~I40E_FLAG_FD_SB_ENABLED;
+                       clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state);
+                       dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n");
+               }
+       }
+}
+
+/**
+ * i40e_get_current_atr_count - Get the count of total FD ATR filters programmed
+ * @pf: board private structure
+ **/
+int i40e_get_current_atr_cnt(struct i40e_pf *pf)
+{
+       return i40e_get_current_fd_count(pf) - pf->fdir_pf_active_filters;
+}
+
+/* We can see up to 256 filter programming desc in transit if the filters are
+ * being applied really fast; before we see the first
+ * filter miss error on Rx queue 0. Accumulating enough error messages before
+ * reacting will make sure we don't cause flush too often.
+ */
+#define I40E_MAX_FD_PROGRAM_ERROR 256
+
 /**
  * i40e_fdir_reinit_subtask - Worker thread to reinit FDIR filter table
  * @pf: board private structure
  **/
 static void i40e_fdir_reinit_subtask(struct i40e_pf *pf)
 {
-       if (!(pf->flags & I40E_FLAG_FDIR_REQUIRES_REINIT))
-               return;
 
        /* if interface is down do nothing */
        if (test_bit(__I40E_DOWN, &pf->state))
                return;
+
+       if ((pf->fd_add_err >= I40E_MAX_FD_PROGRAM_ERROR) &&
+           (i40e_get_current_atr_cnt(pf) >= pf->fd_atr_cnt) &&
+           (i40e_get_current_atr_cnt(pf) > pf->fdir_pf_filter_count))
+               i40e_fdir_flush_and_replay(pf);
+
        i40e_fdir_check_and_reenable(pf);
 
-       if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
-           (pf->flags & I40E_FLAG_FD_SB_ENABLED))
-               pf->flags &= ~I40E_FLAG_FDIR_REQUIRES_REINIT;
 }
 
 /**
@@ -7086,6 +7157,11 @@ bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features)
                }
                pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
                pf->auto_disable_flags &= ~I40E_FLAG_FD_SB_ENABLED;
+               /* reset fd counters */
+               pf->fd_add_err = pf->fd_atr_cnt = pf->fd_tcp_rule = 0;
+               pf->fdir_pf_active_filters = 0;
+               pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+               dev_info(&pf->pdev->dev, "ATR re-enabled.\n");
                /* if ATR was auto disabled it can be re-enabled. */
                if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
                    (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED))
index 267c0e609a02d4a640429377b30eb978fb979c0a..366624a5122917208b433fcce31cbd31734f9e2e 100644 (file)
@@ -280,10 +280,18 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
        tcp->source = fd_data->src_port;
 
        if (add) {
+               pf->fd_tcp_rule++;
                if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
                        dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n");
                        pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
                }
+       } else {
+               pf->fd_tcp_rule = (pf->fd_tcp_rule > 0) ?
+                                 (pf->fd_tcp_rule - 1) : 0;
+               if (pf->fd_tcp_rule == 0) {
+                       pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
+                       dev_info(&pf->pdev->dev, "ATR re-enabled due to no sideband TCP/IPv4 rules\n");
+               }
        }
 
        fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
@@ -462,6 +470,10 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
                        dev_warn(&pdev->dev, "ntuple filter loc = %d, could not be added\n",
                                 rx_desc->wb.qword0.hi_dword.fd_id);
 
+               pf->fd_add_err++;
+               /* store the current atr filter count */
+               pf->fd_atr_cnt = i40e_get_current_atr_cnt(pf);
+
                /* filter programming failed most likely due to table full */
                fcnt_prog = i40e_get_cur_guaranteed_fd_count(pf);
                fcnt_avail = pf->fdir_pf_filter_count;
@@ -470,21 +482,12 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
                 * FD ATR/SB and then re-enable it when there is room.
                 */
                if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) {
-                       /* Turn off ATR first */
-                       if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
+                       if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
                            !(pf->auto_disable_flags &
-                             I40E_FLAG_FD_ATR_ENABLED)) {
-                               dev_warn(&pdev->dev, "FD filter space full, ATR for further flows will be turned off\n");
-                               pf->auto_disable_flags |=
-                                                      I40E_FLAG_FD_ATR_ENABLED;
-                               pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
-                       } else if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
-                                  !(pf->auto_disable_flags &
                                     I40E_FLAG_FD_SB_ENABLED)) {
                                dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n");
                                pf->auto_disable_flags |=
                                                        I40E_FLAG_FD_SB_ENABLED;
-                               pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
                        }
                } else {
                        dev_info(&pdev->dev,