}
/* 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;
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
{
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;
}
}
+#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;
}
/**
}
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))
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;
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;
* 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,