i40e: enable port mirroring

enable mirror functionality in i40e driver
.mirror_rule_set
.mirror_rule_reset

Test report: http://dpdk.org/ml/archives/dev/2015-June/019118.html

Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
Acked-by: Jijiang Liu <jijiang.liu@intel.com>
Acked-by: Helin Zhang <helin.zhang@intel.com>
This commit is contained in:
Jingjing Wu 2015-06-10 14:24:32 +08:00 committed by Thomas Monjalon
parent 7ba29a76b1
commit a4def5edf0
2 changed files with 357 additions and 0 deletions

View File

@ -212,6 +212,10 @@ static int i40e_dev_filter_ctrl(struct rte_eth_dev *dev,
static void i40e_configure_registers(struct i40e_hw *hw);
static void i40e_hw_init(struct i40e_hw *hw);
static int i40e_config_qinq(struct i40e_hw *hw, struct i40e_vsi *vsi);
static int i40e_mirror_rule_set(struct rte_eth_dev *dev,
struct rte_eth_mirror_conf *mirror_conf,
uint8_t sw_id, uint8_t on);
static int i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id);
static const struct rte_pci_id pci_id_i40e_map[] = {
#define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
@ -263,6 +267,8 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
.udp_tunnel_add = i40e_dev_udp_tunnel_add,
.udp_tunnel_del = i40e_dev_udp_tunnel_del,
.filter_ctrl = i40e_dev_filter_ctrl,
.mirror_rule_set = i40e_mirror_rule_set,
.mirror_rule_reset = i40e_mirror_rule_reset,
};
static struct eth_driver rte_i40e_pmd = {
@ -564,6 +570,9 @@ eth_i40e_dev_init(struct rte_eth_dev *dev)
/* enable uio intr after callback register */
rte_intr_enable(&(pci_dev->intr_handle));
/* initialize mirror rule list */
TAILQ_INIT(&pf->mirror_list);
return 0;
err_mac_alloc:
@ -930,6 +939,7 @@ i40e_dev_stop(struct rte_eth_dev *dev)
{
struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
struct i40e_vsi *main_vsi = pf->main_vsi;
struct i40e_mirror_rule *p_mirror;
int i;
/* Disable all queues */
@ -954,6 +964,13 @@ i40e_dev_stop(struct rte_eth_dev *dev)
/* Set link down */
i40e_dev_set_link_down(dev);
/* Remove all mirror rules */
while ((p_mirror = TAILQ_FIRST(&pf->mirror_list))) {
TAILQ_REMOVE(&pf->mirror_list, p_mirror, rules);
rte_free(p_mirror);
}
pf->nb_mirror_rule = 0;
}
static void
@ -5749,3 +5766,320 @@ i40e_config_qinq(struct i40e_hw *hw, struct i40e_vsi *vsi)
return 0;
}
/**
* i40e_aq_add_mirror_rule
* @hw: pointer to the hardware structure
* @seid: VEB seid to add mirror rule to
* @dst_id: destination vsi seid
* @entries: Buffer which contains the entities to be mirrored
* @count: number of entities contained in the buffer
* @rule_id:the rule_id of the rule to be added
*
* Add a mirror rule for a given veb.
*
**/
static enum i40e_status_code
i40e_aq_add_mirror_rule(struct i40e_hw *hw,
uint16_t seid, uint16_t dst_id,
uint16_t rule_type, uint16_t *entries,
uint16_t count, uint16_t *rule_id)
{
struct i40e_aq_desc desc;
struct i40e_aqc_add_delete_mirror_rule *cmd =
(struct i40e_aqc_add_delete_mirror_rule *)&desc.params.raw;
struct i40e_aqc_add_delete_mirror_rule_completion *resp =
(struct i40e_aqc_add_delete_mirror_rule_completion *)
&desc.params.raw;
uint16_t buff_len;
enum i40e_status_code status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_add_mirror_rule);
buff_len = sizeof(uint16_t) * count;
desc.datalen = rte_cpu_to_le_16(buff_len);
if (buff_len > 0)
desc.flags |= rte_cpu_to_le_16(
(uint16_t)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
cmd->rule_type = rte_cpu_to_le_16(rule_type <<
I40E_AQC_MIRROR_RULE_TYPE_SHIFT);
cmd->num_entries = rte_cpu_to_le_16(count);
cmd->seid = rte_cpu_to_le_16(seid);
cmd->destination = rte_cpu_to_le_16(dst_id);
status = i40e_asq_send_command(hw, &desc, entries, buff_len, NULL);
PMD_DRV_LOG(INFO, "i40e_aq_add_mirror_rule, aq_status %d,"
"rule_id = %u"
" mirror_rules_used = %u, mirror_rules_free = %u,",
hw->aq.asq_last_status, resp->rule_id,
resp->mirror_rules_used, resp->mirror_rules_free);
*rule_id = rte_le_to_cpu_16(resp->rule_id);
return status;
}
/**
* i40e_aq_del_mirror_rule
* @hw: pointer to the hardware structure
* @seid: VEB seid to add mirror rule to
* @entries: Buffer which contains the entities to be mirrored
* @count: number of entities contained in the buffer
* @rule_id:the rule_id of the rule to be delete
*
* Delete a mirror rule for a given veb.
*
**/
static enum i40e_status_code
i40e_aq_del_mirror_rule(struct i40e_hw *hw,
uint16_t seid, uint16_t rule_type, uint16_t *entries,
uint16_t count, uint16_t rule_id)
{
struct i40e_aq_desc desc;
struct i40e_aqc_add_delete_mirror_rule *cmd =
(struct i40e_aqc_add_delete_mirror_rule *)&desc.params.raw;
uint16_t buff_len = 0;
enum i40e_status_code status;
void *buff = NULL;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_delete_mirror_rule);
if (rule_type == I40E_AQC_MIRROR_RULE_TYPE_VLAN) {
desc.flags |= rte_cpu_to_le_16((uint16_t)(I40E_AQ_FLAG_BUF |
I40E_AQ_FLAG_RD));
cmd->num_entries = count;
buff_len = sizeof(uint16_t) * count;
desc.datalen = rte_cpu_to_le_16(buff_len);
buff = (void *)entries;
} else
/* rule id is filled in destination field for deleting mirror rule */
cmd->destination = rte_cpu_to_le_16(rule_id);
cmd->rule_type = rte_cpu_to_le_16(rule_type <<
I40E_AQC_MIRROR_RULE_TYPE_SHIFT);
cmd->seid = rte_cpu_to_le_16(seid);
status = i40e_asq_send_command(hw, &desc, buff, buff_len, NULL);
return status;
}
/**
* i40e_mirror_rule_set
* @dev: pointer to the hardware structure
* @mirror_conf: mirror rule info
* @sw_id: mirror rule's sw_id
* @on: enable/disable
*
* set a mirror rule.
*
**/
static int
i40e_mirror_rule_set(struct rte_eth_dev *dev,
struct rte_eth_mirror_conf *mirror_conf,
uint8_t sw_id, uint8_t on)
{
struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct i40e_mirror_rule *it, *mirr_rule = NULL;
struct i40e_mirror_rule *parent = NULL;
uint16_t seid, dst_seid, rule_id;
uint16_t i, j = 0;
int ret;
PMD_DRV_LOG(DEBUG, "i40e_mirror_rule_set: sw_id = %d.", sw_id);
if (pf->main_vsi->veb == NULL || pf->vfs == NULL) {
PMD_DRV_LOG(ERR, "mirror rule can not be configured"
" without veb or vfs.");
return -ENOSYS;
}
if (pf->nb_mirror_rule > I40E_MAX_MIRROR_RULES) {
PMD_DRV_LOG(ERR, "mirror table is full.");
return -ENOSPC;
}
if (mirror_conf->dst_pool > pf->vf_num) {
PMD_DRV_LOG(ERR, "invalid destination pool %u.",
mirror_conf->dst_pool);
return -EINVAL;
}
seid = pf->main_vsi->veb->seid;
TAILQ_FOREACH(it, &pf->mirror_list, rules) {
if (sw_id <= it->index) {
mirr_rule = it;
break;
}
parent = it;
}
if (mirr_rule && sw_id == mirr_rule->index) {
if (on) {
PMD_DRV_LOG(ERR, "mirror rule exists.");
return -EEXIST;
} else {
ret = i40e_aq_del_mirror_rule(hw, seid,
mirr_rule->rule_type,
mirr_rule->entries,
mirr_rule->num_entries, mirr_rule->id);
if (ret < 0) {
PMD_DRV_LOG(ERR, "failed to remove mirror rule:"
" ret = %d, aq_err = %d.",
ret, hw->aq.asq_last_status);
return -ENOSYS;
}
TAILQ_REMOVE(&pf->mirror_list, mirr_rule, rules);
rte_free(mirr_rule);
pf->nb_mirror_rule--;
return 0;
}
} else if (!on) {
PMD_DRV_LOG(ERR, "mirror rule doesn't exist.");
return -ENOENT;
}
mirr_rule = rte_zmalloc("i40e_mirror_rule",
sizeof(struct i40e_mirror_rule) , 0);
if (!mirr_rule) {
PMD_DRV_LOG(ERR, "failed to allocate memory");
return I40E_ERR_NO_MEMORY;
}
switch (mirror_conf->rule_type) {
case ETH_MIRROR_VLAN:
for (i = 0, j = 0; i < ETH_MIRROR_MAX_VLANS; i++) {
if (mirror_conf->vlan.vlan_mask & (1ULL << i)) {
mirr_rule->entries[j] =
mirror_conf->vlan.vlan_id[i];
j++;
}
}
if (j == 0) {
PMD_DRV_LOG(ERR, "vlan is not specified.");
rte_free(mirr_rule);
return -EINVAL;
}
mirr_rule->rule_type = I40E_AQC_MIRROR_RULE_TYPE_VLAN;
break;
case ETH_MIRROR_VIRTUAL_POOL_UP:
case ETH_MIRROR_VIRTUAL_POOL_DOWN:
/* check if the specified pool bit is out of range */
if (mirror_conf->pool_mask > (uint64_t)(1ULL << (pf->vf_num + 1))) {
PMD_DRV_LOG(ERR, "pool mask is out of range.");
rte_free(mirr_rule);
return -EINVAL;
}
for (i = 0, j = 0; i < pf->vf_num; i++) {
if (mirror_conf->pool_mask & (1ULL << i)) {
mirr_rule->entries[j] = pf->vfs[i].vsi->seid;
j++;
}
}
if (mirror_conf->pool_mask & (1ULL << pf->vf_num)) {
/* add pf vsi to entries */
mirr_rule->entries[j] = pf->main_vsi_seid;
j++;
}
if (j == 0) {
PMD_DRV_LOG(ERR, "pool is not specified.");
rte_free(mirr_rule);
return -EINVAL;
}
/* egress and ingress in aq commands means from switch but not port */
mirr_rule->rule_type =
(mirror_conf->rule_type == ETH_MIRROR_VIRTUAL_POOL_UP) ?
I40E_AQC_MIRROR_RULE_TYPE_VPORT_EGRESS :
I40E_AQC_MIRROR_RULE_TYPE_VPORT_INGRESS;
break;
case ETH_MIRROR_UPLINK_PORT:
/* egress and ingress in aq commands means from switch but not port*/
mirr_rule->rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_EGRESS;
break;
case ETH_MIRROR_DOWNLINK_PORT:
mirr_rule->rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_INGRESS;
break;
default:
PMD_DRV_LOG(ERR, "unsupported mirror type %d.",
mirror_conf->rule_type);
rte_free(mirr_rule);
return -EINVAL;
}
/* If the dst_pool is equal to vf_num, consider it as PF */
if (mirror_conf->dst_pool == pf->vf_num)
dst_seid = pf->main_vsi_seid;
else
dst_seid = pf->vfs[mirror_conf->dst_pool].vsi->seid;
ret = i40e_aq_add_mirror_rule(hw, seid, dst_seid,
mirr_rule->rule_type, mirr_rule->entries,
j, &rule_id);
if (ret < 0) {
PMD_DRV_LOG(ERR, "failed to add mirror rule:"
" ret = %d, aq_err = %d.",
ret, hw->aq.asq_last_status);
rte_free(mirr_rule);
return -ENOSYS;
}
mirr_rule->index = sw_id;
mirr_rule->num_entries = j;
mirr_rule->id = rule_id;
mirr_rule->dst_vsi_seid = dst_seid;
if (parent)
TAILQ_INSERT_AFTER(&pf->mirror_list, parent, mirr_rule, rules);
else
TAILQ_INSERT_HEAD(&pf->mirror_list, mirr_rule, rules);
pf->nb_mirror_rule++;
return 0;
}
/**
* i40e_mirror_rule_reset
* @dev: pointer to the device
* @sw_id: mirror rule's sw_id
*
* reset a mirror rule.
*
**/
static int
i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
{
struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
struct i40e_mirror_rule *it, *mirr_rule = NULL;
uint16_t seid;
int ret;
PMD_DRV_LOG(DEBUG, "i40e_mirror_rule_reset: sw_id = %d.", sw_id);
seid = pf->main_vsi->veb->seid;
TAILQ_FOREACH(it, &pf->mirror_list, rules) {
if (sw_id == it->index) {
mirr_rule = it;
break;
}
}
if (mirr_rule) {
ret = i40e_aq_del_mirror_rule(hw, seid,
mirr_rule->rule_type,
mirr_rule->entries,
mirr_rule->num_entries, mirr_rule->id);
if (ret < 0) {
PMD_DRV_LOG(ERR, "failed to remove mirror rule:"
" status = %d, aq_err = %d.",
ret, hw->aq.asq_last_status);
return -ENOSYS;
}
TAILQ_REMOVE(&pf->mirror_list, mirr_rule, rules);
rte_free(mirr_rule);
pf->nb_mirror_rule--;
} else {
PMD_DRV_LOG(ERR, "mirror rule doesn't exist.");
return -ENOENT;
}
return 0;
}

View File

@ -325,6 +325,27 @@ struct i40e_fdir_info {
struct i40e_fdir_flex_mask flex_mask[I40E_FILTER_PCTYPE_MAX];
};
#define I40E_MIRROR_MAX_ENTRIES_PER_RULE 64
#define I40E_MAX_MIRROR_RULES 64
/*
* Mirror rule structure
*/
struct i40e_mirror_rule {
TAILQ_ENTRY(i40e_mirror_rule) rules;
uint8_t rule_type;
uint16_t index; /* the sw index of mirror rule */
uint16_t id; /* the rule id assigned by firmware */
uint16_t dst_vsi_seid; /* destination vsi for this mirror rule. */
uint16_t num_entries;
/* the info stores depend on the rule type.
If type is I40E_MIRROR_TYPE_VLAN, vlan ids are stored here.
If type is I40E_MIRROR_TYPE_VPORT_*, vsi's seid are stored.
*/
uint16_t entries[I40E_MIRROR_MAX_ENTRIES_PER_RULE];
};
TAILQ_HEAD(i40e_mirror_rule_list, i40e_mirror_rule);
/*
* Structure to store private data specific for PF instance.
*/
@ -364,6 +385,8 @@ struct i40e_pf {
struct i40e_vmdq_info *vmdq;
struct i40e_fdir_info fdir; /* flow director info */
struct i40e_mirror_rule_list mirror_list;
uint16_t nb_mirror_rule; /* The number of mirror rules */
};
enum pending_msg {