diff options
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e.h | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_main.c | 96 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_txrx.c | 23 |
4 files changed, 111 insertions, 20 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 801da392a20e..0c6a9b97f7e2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -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, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index e8ba7470700a..7204e293dc71 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -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 + diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index eddec6ba095b..cab9c9e3e694 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -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)) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 267c0e609a02..366624a51229 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -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, |