i40e: refactor flow director

The i40e hardware was generating some inconsistent results
when using current programming methods.  This refactor
fixes the inconsistencies that were preventing clean
unloads of the driver, and moves the queues for handling
flow director errors into their own hardware VSI.

This patch also implements a corrected version of the
basic ethtool add ntuple rule, which will disable
the driver's automatic flow programming.  A future patch
adds remove/replay/list support for ntuple.

Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Tested-by: Kavindya Deegala <kavindya.s.deegala@intel.com>
Signed-off-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Anjali Singhai Jain 2014-01-17 15:36:35 -08:00 committed by David S. Miller
parent 60ea5f83cd
commit cbf6132524
3 changed files with 221 additions and 160 deletions

View File

@ -1003,6 +1003,22 @@ static void i40e_dbg_dump_veb_all(struct i40e_pf *pf)
}
}
/**
* i40e_dbg_cmd_fd_ctrl - Enable/disable FD sideband/ATR
* @pf: the pf that would be altered
* @flag: flag that needs enabling or disabling
* @enable: Enable/disable FD SD/ATR
**/
static void i40e_dbg_cmd_fd_ctrl(struct i40e_pf *pf, u64 flag, bool enable)
{
if (enable)
pf->flags |= flag;
else
pf->flags &= ~flag;
dev_info(&pf->pdev->dev, "requesting a pf reset\n");
i40e_do_reset_safe(pf, (1 << __I40E_PF_RESET_REQUESTED));
}
#define I40E_MAX_DEBUG_OUT_BUFFER (4096*4)
/**
* i40e_dbg_command_write - write into command datum
@ -1714,6 +1730,14 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
fd_data.raw_packet = NULL;
kfree(asc_packet);
asc_packet = NULL;
} else if (strncmp(cmd_buf, "fd-atr off", 10) == 0) {
i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, false);
} else if (strncmp(cmd_buf, "fd-atr on", 9) == 0) {
i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, true);
} else if (strncmp(cmd_buf, "fd-sb off", 9) == 0) {
i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_SB_ENABLED, false);
} else if (strncmp(cmd_buf, "fd-sb on", 8) == 0) {
i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_SB_ENABLED, true);
} else if (strncmp(cmd_buf, "lldp", 4) == 0) {
if (strncmp(&cmd_buf[5], "stop", 4) == 0) {
int ret;
@ -1900,6 +1924,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
dev_info(&pf->pdev->dev, " send indirect aq_cmd <flags> <opcode> <datalen> <retval> <cookie_h> <cookie_l> <param0> <param1> <param2> <param3> <buffer_len>\n");
dev_info(&pf->pdev->dev, " add fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n");
dev_info(&pf->pdev->dev, " rem fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n");
dev_info(&pf->pdev->dev, " fd-atr off\n");
dev_info(&pf->pdev->dev, " fd-atr on\n");
dev_info(&pf->pdev->dev, " fd-sb off\n");
dev_info(&pf->pdev->dev, " fd-sb on\n");
dev_info(&pf->pdev->dev, " lldp start\n");
dev_info(&pf->pdev->dev, " lldp stop\n");
dev_info(&pf->pdev->dev, " lldp get local\n");

View File

@ -1135,6 +1135,7 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
ret = i40e_get_rss_hash_opts(pf, cmd);
break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = 10;
ret = 0;
break;
case ETHTOOL_GRXCLSRULE:
@ -1369,6 +1370,13 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
ip->saddr = fsp->h_u.tcp_ip4_spec.ip4src;
tcp->source = fsp->h_u.tcp_ip4_spec.psrc;
if (add) {
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;
}
}
fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN;
ret = i40e_program_fdir_filter(fd_data, pf, add);
@ -1508,8 +1516,8 @@ static int i40e_add_del_fdir_ethtool(struct i40e_vsi *vsi,
fd_data.flex_off = 0;
fd_data.pctype = 0;
fd_data.dest_vsi = vsi->id;
fd_data.dest_ctl = 0;
fd_data.fd_status = 0;
fd_data.dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX;
fd_data.fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID;
fd_data.cnt_index = 0;
fd_data.fd_id = 0;

View File

@ -54,6 +54,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit);
static int i40e_setup_misc_vector(struct i40e_pf *pf);
static void i40e_determine_queue_usage(struct i40e_pf *pf);
static int i40e_setup_pf_filter_control(struct i40e_pf *pf);
static void i40e_fdir_sb_setup(struct i40e_pf *pf);
/* i40e_pci_tbl - PCI Device ID Table
*
@ -2635,23 +2636,6 @@ static irqreturn_t i40e_msix_clean_rings(int irq, void *data)
return IRQ_HANDLED;
}
/**
* i40e_fdir_clean_rings - Interrupt Handler for FDIR rings
* @irq: interrupt number
* @data: pointer to a q_vector
**/
static irqreturn_t i40e_fdir_clean_rings(int irq, void *data)
{
struct i40e_q_vector *q_vector = data;
if (!q_vector->tx.ring && !q_vector->rx.ring)
return IRQ_HANDLED;
pr_info("fdir ring cleaning needed\n");
return IRQ_HANDLED;
}
/**
* i40e_vsi_request_irq_msix - Initialize MSI-X interrupts
* @vsi: the VSI being configured
@ -2902,6 +2886,94 @@ enable_intr:
return ret;
}
/**
* i40e_clean_fdir_tx_irq - Reclaim resources after transmit completes
* @tx_ring: tx ring to clean
* @budget: how many cleans we're allowed
*
* Returns true if there's any budget left (e.g. the clean is finished)
**/
static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget)
{
struct i40e_vsi *vsi = tx_ring->vsi;
u16 i = tx_ring->next_to_clean;
struct i40e_tx_buffer *tx_buf;
struct i40e_tx_desc *tx_desc;
tx_buf = &tx_ring->tx_bi[i];
tx_desc = I40E_TX_DESC(tx_ring, i);
i -= tx_ring->count;
do {
struct i40e_tx_desc *eop_desc = tx_buf->next_to_watch;
/* if next_to_watch is not set then there is no work pending */
if (!eop_desc)
break;
/* prevent any other reads prior to eop_desc */
read_barrier_depends();
/* if the descriptor isn't done, no work yet to do */
if (!(eop_desc->cmd_type_offset_bsz &
cpu_to_le64(I40E_TX_DESC_DTYPE_DESC_DONE)))
break;
/* clear next_to_watch to prevent false hangs */
tx_buf->next_to_watch = NULL;
/* unmap skb header data */
dma_unmap_single(tx_ring->dev,
dma_unmap_addr(tx_buf, dma),
dma_unmap_len(tx_buf, len),
DMA_TO_DEVICE);
dma_unmap_len_set(tx_buf, len, 0);
/* move to the next desc and buffer to clean */
tx_buf++;
tx_desc++;
i++;
if (unlikely(!i)) {
i -= tx_ring->count;
tx_buf = tx_ring->tx_bi;
tx_desc = I40E_TX_DESC(tx_ring, 0);
}
/* update budget accounting */
budget--;
} while (likely(budget));
i += tx_ring->count;
tx_ring->next_to_clean = i;
if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) {
i40e_irq_dynamic_enable(vsi,
tx_ring->q_vector->v_idx + vsi->base_vector);
}
return budget > 0;
}
/**
* i40e_fdir_clean_ring - Interrupt Handler for FDIR SB ring
* @irq: interrupt number
* @data: pointer to a q_vector
**/
static irqreturn_t i40e_fdir_clean_ring(int irq, void *data)
{
struct i40e_q_vector *q_vector = data;
struct i40e_vsi *vsi;
if (!q_vector->tx.ring)
return IRQ_HANDLED;
vsi = q_vector->tx.ring->vsi;
i40e_clean_fdir_tx_irq(q_vector->tx.ring, vsi->work_limit);
return IRQ_HANDLED;
}
/**
* i40e_map_vector_to_qp - Assigns the queue pair to the vector
* @vsi: the VSI being configured
@ -4730,54 +4802,77 @@ static int i40e_get_capabilities(struct i40e_pf *pf)
return 0;
}
static int i40e_vsi_clear(struct i40e_vsi *vsi);
/**
* i40e_fdir_setup - initialize the Flow Director resources
* i40e_fdir_sb_setup - initialize the Flow Director resources for Sideband
* @pf: board private structure
**/
static void i40e_fdir_setup(struct i40e_pf *pf)
static void i40e_fdir_sb_setup(struct i40e_pf *pf)
{
struct i40e_vsi *vsi;
bool new_vsi = false;
int err, i;
if (!(pf->flags & (I40E_FLAG_FD_SB_ENABLED |
I40E_FLAG_FD_ATR_ENABLED)))
if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
return;
pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
/* find existing or make new FDIR VSI */
/* find existing VSI and see if it needs configuring */
vsi = NULL;
for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR)
for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
vsi = pf->vsi[i];
break;
}
}
/* create a new VSI if none exists */
if (!vsi) {
vsi = i40e_vsi_setup(pf, I40E_VSI_FDIR, pf->mac_seid, 0);
vsi = i40e_vsi_setup(pf, I40E_VSI_FDIR,
pf->vsi[pf->lan_vsi]->seid, 0);
if (!vsi) {
dev_info(&pf->pdev->dev, "Couldn't create FDir VSI\n");
pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
return;
goto err_vsi;
}
new_vsi = true;
}
WARN_ON(vsi->base_queue != I40E_FDIR_RING);
i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_rings);
i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_ring);
err = i40e_vsi_setup_tx_resources(vsi);
if (!err)
err = i40e_vsi_setup_rx_resources(vsi);
if (!err)
err = i40e_vsi_configure(vsi);
if (!err && new_vsi) {
if (err)
goto err_setup_tx;
err = i40e_vsi_setup_rx_resources(vsi);
if (err)
goto err_setup_rx;
if (new_vsi) {
char int_name[IFNAMSIZ + 9];
err = i40e_vsi_configure(vsi);
if (err)
goto err_setup_rx;
snprintf(int_name, sizeof(int_name) - 1, "%s-fdir",
dev_driver_string(&pf->pdev->dev));
err = i40e_vsi_request_irq(vsi, int_name);
}
if (!err)
if (err)
goto err_setup_rx;
err = i40e_up_complete(vsi);
if (err)
goto err_up_complete;
}
clear_bit(__I40E_NEEDS_RESTART, &vsi->state);
return;
err_up_complete:
i40e_down(vsi);
i40e_vsi_free_irq(vsi);
err_setup_rx:
i40e_vsi_free_rx_resources(vsi);
err_setup_tx:
i40e_vsi_free_tx_resources(vsi);
err_vsi:
pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
i40e_vsi_clear(vsi);
}
/**
@ -5865,6 +5960,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
if (pf->hw.func_caps.rss) {
pf->flags |= I40E_FLAG_RSS_ENABLED;
pf->rss_size = min_t(int, pf->rss_size_max, num_online_cpus());
pf->rss_size = rounddown_pow_of_two(pf->rss_size);
} else {
pf->rss_size = 1;
}
@ -5880,21 +5976,25 @@ static int i40e_sw_init(struct i40e_pf *pf)
else
pf->num_tc_qps = 0;
if (pf->hw.func_caps.fd) {
/* FW/NVM is not yet fixed in this regard */
if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
(pf->hw.func_caps.fd_filters_best_effort > 0)) {
pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
dev_info(&pf->pdev->dev,
"Flow Director ATR mode Enabled\n");
/* FW/NVM is not yet fixed in this regard */
if ((pf->hw.func_caps.fd_filters_guaranteed > 0) ||
(pf->hw.func_caps.fd_filters_best_effort > 0)) {
pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
dev_info(&pf->pdev->dev,
"Flow Director ATR mode Enabled\n");
if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) {
pf->flags |= I40E_FLAG_FD_SB_ENABLED;
dev_info(&pf->pdev->dev,
"Flow Director Side Band mode Enabled\n");
pf->fdir_pf_filter_count =
pf->hw.func_caps.fd_filters_guaranteed;
} else {
dev_info(&pf->pdev->dev,
"Flow Director Side Band mode Disabled in MFP mode\n");
}
} else {
pf->fdir_pf_filter_count = 0;
pf->fdir_pf_filter_count =
pf->hw.func_caps.fd_filters_guaranteed;
pf->hw.fdir_shared_filter_count =
pf->hw.func_caps.fd_filters_best_effort;
}
if (pf->hw.func_caps.vmdq) {
@ -6185,10 +6285,6 @@ static void i40e_vsi_delete(struct i40e_vsi *vsi)
if (vsi == vsi->back->vsi[vsi->back->lan_vsi])
return;
/* there is no HW VSI for FDIR */
if (vsi->type == I40E_VSI_FDIR)
return;
i40e_aq_delete_element(&vsi->back->hw, vsi->seid, NULL);
return;
}
@ -6272,12 +6368,12 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
break;
case I40E_VSI_FDIR:
/* no queue mapping or actual HW VSI needed */
vsi->info.valid_sections = 0;
vsi->seid = 0;
vsi->id = 0;
ctxt.pf_num = hw->pf_id;
ctxt.vf_num = 0;
ctxt.uplink_seid = vsi->uplink_seid;
ctxt.connection_type = 0x1; /* regular data port */
ctxt.flags = I40E_AQ_VSI_TYPE_PF;
i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true);
return 0;
break;
case I40E_VSI_VMDQ2:
@ -6646,6 +6742,8 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
if (v_idx < 0)
goto err_alloc;
vsi = pf->vsi[v_idx];
if (!vsi)
goto err_alloc;
vsi->type = type;
vsi->veb_idx = (veb ? veb->idx : I40E_NO_VEB);
@ -6654,7 +6752,8 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
else if (type == I40E_VSI_SRIOV)
vsi->vf_id = param1;
/* assign it some queues */
ret = i40e_get_lump(pf, pf->qp_pile, vsi->alloc_queue_pairs, vsi->idx);
ret = i40e_get_lump(pf, pf->qp_pile, vsi->alloc_queue_pairs,
vsi->idx);
if (ret < 0) {
dev_info(&pf->pdev->dev, "VSI %d get_lump failed %d\n",
vsi->seid, ret);
@ -7228,12 +7327,6 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
}
i40e_pf_reset_stats(pf);
/* fdir VSI must happen first to be sure it gets queue 0, but only
* if there is enough room for the fdir VSI
*/
if (pf->num_lan_qps > 1)
i40e_fdir_setup(pf);
/* first time setup */
if (pf->lan_vsi == I40E_NO_VSI || reinit) {
struct i40e_vsi *vsi = NULL;
@ -7264,6 +7357,8 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
}
i40e_vlan_stripping_disable(pf->vsi[pf->lan_vsi]);
i40e_fdir_sb_setup(pf);
/* Setup static PF queue filter control settings */
ret = i40e_setup_pf_filter_control(pf);
if (ret) {
@ -7346,34 +7441,16 @@ fc_complete:
return ret;
}
/**
* i40e_set_rss_size - helper to set rss_size
* @pf: board private structure
* @queues_left: how many queues
*/
static u16 i40e_set_rss_size(struct i40e_pf *pf, int queues_left)
{
int num_tc0;
num_tc0 = min_t(int, queues_left, pf->rss_size_max);
num_tc0 = min_t(int, num_tc0, num_online_cpus());
num_tc0 = rounddown_pow_of_two(num_tc0);
return num_tc0;
}
/**
* i40e_determine_queue_usage - Work out queue distribution
* @pf: board private structure
**/
static void i40e_determine_queue_usage(struct i40e_pf *pf)
{
int accum_tc_size;
int queues_left;
pf->num_lan_qps = 0;
pf->num_tc_qps = rounddown_pow_of_two(pf->num_tc_qps);
accum_tc_size = (I40E_MAX_TRAFFIC_CLASS - 1) * pf->num_tc_qps;
/* Find the max queues to be put into basic use. We'll always be
* using TC0, whether or not DCB is running, and TC0 will get the
@ -7381,81 +7458,15 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
*/
queues_left = pf->hw.func_caps.num_tx_qp;
if (!(pf->flags & I40E_FLAG_MSIX_ENABLED) ||
!(pf->flags & (I40E_FLAG_RSS_ENABLED |
I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_DCB_ENABLED)) ||
(queues_left == 1)) {
if ((queues_left == 1) ||
!(pf->flags & I40E_FLAG_MSIX_ENABLED) ||
!(pf->flags & (I40E_FLAG_RSS_ENABLED | I40E_FLAG_FD_SB_ENABLED |
I40E_FLAG_DCB_ENABLED))) {
/* one qp for PF, no queues for anything else */
queues_left = 0;
pf->rss_size = pf->num_lan_qps = 1;
/* make sure all the fancies are disabled */
} else if (pf->flags & I40E_FLAG_RSS_ENABLED &&
!(pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
!(pf->flags & I40E_FLAG_DCB_ENABLED)) {
pf->rss_size = i40e_set_rss_size(pf, queues_left);
queues_left -= pf->rss_size;
pf->num_lan_qps = pf->rss_size_max;
} else if (pf->flags & I40E_FLAG_RSS_ENABLED &&
!(pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
(pf->flags & I40E_FLAG_DCB_ENABLED)) {
/* save num_tc_qps queues for TCs 1 thru 7 and the rest
* are set up for RSS in TC0
*/
queues_left -= accum_tc_size;
pf->rss_size = i40e_set_rss_size(pf, queues_left);
queues_left -= pf->rss_size;
if (queues_left < 0) {
dev_info(&pf->pdev->dev, "not enough queues for DCB\n");
return;
}
pf->num_lan_qps = pf->rss_size_max + accum_tc_size;
} else if (pf->flags & I40E_FLAG_RSS_ENABLED &&
(pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
!(pf->flags & I40E_FLAG_DCB_ENABLED)) {
queues_left -= 1; /* save 1 queue for FD */
pf->rss_size = i40e_set_rss_size(pf, queues_left);
queues_left -= pf->rss_size;
if (queues_left < 0) {
dev_info(&pf->pdev->dev, "not enough queues for Flow Director\n");
return;
}
pf->num_lan_qps = pf->rss_size_max;
} else if (pf->flags & I40E_FLAG_RSS_ENABLED &&
(pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
(pf->flags & I40E_FLAG_DCB_ENABLED)) {
/* save 1 queue for TCs 1 thru 7,
* 1 queue for flow director,
* and the rest are set up for RSS in TC0
*/
queues_left -= 1;
queues_left -= accum_tc_size;
pf->rss_size = i40e_set_rss_size(pf, queues_left);
queues_left -= pf->rss_size;
if (queues_left < 0) {
dev_info(&pf->pdev->dev, "not enough queues for DCB and Flow Director\n");
return;
}
pf->num_lan_qps = pf->rss_size_max + accum_tc_size;
pf->flags &= ~(I40E_FLAG_RSS_ENABLED |
I40E_FLAG_FD_SB_ENABLED |
I40E_FLAG_FD_ATR_ENABLED |
@ -7463,15 +7474,29 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
I40E_FLAG_SRIOV_ENABLED |
I40E_FLAG_VMDQ_ENABLED);
} else {
dev_info(&pf->pdev->dev,
"Invalid configuration, flags=0x%08llx\n", pf->flags);
return;
/* Not enough queues for all TCs */
if ((pf->flags & I40E_FLAG_DCB_ENABLED) &&
(queues_left < I40E_MAX_TRAFFIC_CLASS)) {
pf->flags &= ~I40E_FLAG_DCB_ENABLED;
dev_info(&pf->pdev->dev, "not enough queues for DCB. DCB is disabled.\n");
}
pf->num_lan_qps = pf->rss_size_max;
queues_left -= pf->num_lan_qps;
}
if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
if (queues_left > 1) {
queues_left -= 1; /* save 1 queue for FD */
} else {
pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
dev_info(&pf->pdev->dev, "not enough queues for Flow Director. Flow Director feature is disabled\n");
}
}
if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
pf->num_vf_qps && pf->num_req_vfs && queues_left) {
pf->num_req_vfs = min_t(int, pf->num_req_vfs, (queues_left /
pf->num_vf_qps));
pf->num_req_vfs = min_t(int, pf->num_req_vfs,
(queues_left / pf->num_vf_qps));
queues_left -= (pf->num_req_vfs * pf->num_vf_qps);
}