+
+#define I40E_VSI_TSR(_i) (0x00050800 + ((_i) * 4))
+#define I40E_VSI_TSR_QINQ_CONFIG 0xc030
+#define I40E_VSI_L2TAGSTXVALID(_i) (0x00042800 + ((_i) * 4))
+#define I40E_VSI_L2TAGSTXVALID_QINQ 0xab
+static int
+i40e_config_qinq(struct i40e_hw *hw, struct i40e_vsi *vsi)
+{
+ uint32_t reg;
+ int ret;
+
+ if (vsi->vsi_id >= I40E_MAX_NUM_VSIS) {
+ PMD_DRV_LOG(ERR, "VSI ID exceeds the maximum");
+ return -EINVAL;
+ }
+
+ /* Configure for double VLAN RX stripping */
+ reg = I40E_READ_REG(hw, I40E_VSI_TSR(vsi->vsi_id));
+ if ((reg & I40E_VSI_TSR_QINQ_CONFIG) != I40E_VSI_TSR_QINQ_CONFIG) {
+ reg |= I40E_VSI_TSR_QINQ_CONFIG;
+ ret = i40e_aq_debug_write_register(hw,
+ I40E_VSI_TSR(vsi->vsi_id),
+ reg, NULL);
+ if (ret < 0) {
+ PMD_DRV_LOG(ERR, "Failed to update VSI_TSR[%d]",
+ vsi->vsi_id);
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ /* Configure for double VLAN TX insertion */
+ reg = I40E_READ_REG(hw, I40E_VSI_L2TAGSTXVALID(vsi->vsi_id));
+ if ((reg & 0xff) != I40E_VSI_L2TAGSTXVALID_QINQ) {
+ reg = I40E_VSI_L2TAGSTXVALID_QINQ;
+ ret = i40e_aq_debug_write_register(hw,
+ I40E_VSI_L2TAGSTXVALID(
+ vsi->vsi_id), reg, NULL);
+ if (ret < 0) {
+ PMD_DRV_LOG(ERR,
+ "Failed to update VSI_L2TAGSTXVALID[%d]",
+ vsi->vsi_id);
+ return I40E_ERR_CONFIG;
+ }
+ }
+
+ 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_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);
+ memset(&cmd, 0, sizeof(cmd));
+
+ 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);
+
+ rte_memcpy(&desc.params.raw, &cmd, sizeof(cmd));
+ 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;
+ 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);
+ memset(&cmd, 0, sizeof(cmd));
+ 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);
+
+ rte_memcpy(&desc.params.raw, &cmd, sizeof(cmd));
+ 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;
+}
+
+static uint64_t
+i40e_read_systime_cyclecounter(struct rte_eth_dev *dev)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint64_t systim_cycles;
+
+ systim_cycles = (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_L);
+ systim_cycles |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TIME_H)
+ << 32;
+
+ return systim_cycles;
+}
+
+static uint64_t
+i40e_read_rx_tstamp_cyclecounter(struct rte_eth_dev *dev, uint8_t index)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint64_t rx_tstamp;
+
+ rx_tstamp = (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
+ rx_tstamp |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index))
+ << 32;
+
+ return rx_tstamp;
+}
+
+static uint64_t
+i40e_read_tx_tstamp_cyclecounter(struct rte_eth_dev *dev)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint64_t tx_tstamp;
+
+ tx_tstamp = (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
+ tx_tstamp |= (uint64_t)I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H)
+ << 32;
+
+ return tx_tstamp;
+}
+
+static void
+i40e_start_timecounters(struct rte_eth_dev *dev)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct i40e_adapter *adapter =
+ (struct i40e_adapter *)dev->data->dev_private;
+ struct rte_eth_link link;
+ uint32_t tsync_inc_l;
+ uint32_t tsync_inc_h;
+
+ /* Get current link speed. */
+ memset(&link, 0, sizeof(link));
+ i40e_dev_link_update(dev, 1);
+ rte_i40e_dev_atomic_read_link_status(dev, &link);
+
+ switch (link.link_speed) {
+ case ETH_SPEED_NUM_40G:
+ tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
+ tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
+ break;
+ case ETH_SPEED_NUM_10G:
+ tsync_inc_l = I40E_PTP_10GB_INCVAL & 0xFFFFFFFF;
+ tsync_inc_h = I40E_PTP_10GB_INCVAL >> 32;
+ break;
+ case ETH_SPEED_NUM_1G:
+ tsync_inc_l = I40E_PTP_1GB_INCVAL & 0xFFFFFFFF;
+ tsync_inc_h = I40E_PTP_1GB_INCVAL >> 32;
+ break;
+ default:
+ tsync_inc_l = 0x0;
+ tsync_inc_h = 0x0;
+ }
+
+ /* Set the timesync increment value. */
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+ memset(&adapter->systime_tc, 0, sizeof(struct rte_timecounter));
+ memset(&adapter->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+ memset(&adapter->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
+
+ adapter->systime_tc.cc_mask = I40E_CYCLECOUNTER_MASK;
+ adapter->systime_tc.cc_shift = 0;
+ adapter->systime_tc.nsec_mask = 0;
+
+ adapter->rx_tstamp_tc.cc_mask = I40E_CYCLECOUNTER_MASK;
+ adapter->rx_tstamp_tc.cc_shift = 0;
+ adapter->rx_tstamp_tc.nsec_mask = 0;
+
+ adapter->tx_tstamp_tc.cc_mask = I40E_CYCLECOUNTER_MASK;
+ adapter->tx_tstamp_tc.cc_shift = 0;
+ adapter->tx_tstamp_tc.nsec_mask = 0;
+}
+
+static int
+i40e_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct i40e_adapter *adapter =
+ (struct i40e_adapter *)dev->data->dev_private;
+
+ adapter->systime_tc.nsec += delta;
+ adapter->rx_tstamp_tc.nsec += delta;
+ adapter->tx_tstamp_tc.nsec += delta;
+
+ return 0;
+}
+
+static int
+i40e_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ uint64_t ns;
+ struct i40e_adapter *adapter =
+ (struct i40e_adapter *)dev->data->dev_private;
+
+ ns = rte_timespec_to_ns(ts);
+
+ /* Set the timecounters to a new value. */
+ adapter->systime_tc.nsec = ns;
+ adapter->rx_tstamp_tc.nsec = ns;
+ adapter->tx_tstamp_tc.nsec = ns;
+
+ return 0;
+}
+
+static int
+i40e_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ uint64_t ns, systime_cycles;
+ struct i40e_adapter *adapter =
+ (struct i40e_adapter *)dev->data->dev_private;
+
+ systime_cycles = i40e_read_systime_cyclecounter(dev);
+ ns = rte_timecounter_update(&adapter->systime_tc, systime_cycles);
+ *ts = rte_ns_to_timespec(ns);
+
+ return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint32_t tsync_ctl_l;
+ uint32_t tsync_ctl_h;
+
+ /* Stop the timesync system time. */
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
+ /* Reset the timesync system time value. */
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_TIME_L, 0x0);
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_TIME_H, 0x0);
+
+ i40e_start_timecounters(dev);
+
+ /* Clear timesync registers. */
+ I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
+ I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
+ I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(0));
+ I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(1));
+ I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(2));
+ I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(3));
+
+ /* Enable timestamping of PTP packets. */
+ tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
+ tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
+
+ tsync_ctl_h = I40E_READ_REG(hw, I40E_PRTTSYN_CTL1);
+ tsync_ctl_h |= I40E_PRTTSYN_TSYNENA;
+ tsync_ctl_h |= I40E_PRTTSYN_TSYNTYPE;
+
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
+
+ return 0;
+}
+
+static int
+i40e_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint32_t tsync_ctl_l;
+ uint32_t tsync_ctl_h;
+
+ /* Disable timestamping of transmitted PTP packets. */
+ tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
+ tsync_ctl_l &= ~I40E_PRTTSYN_TSYNENA;
+
+ tsync_ctl_h = I40E_READ_REG(hw, I40E_PRTTSYN_CTL1);
+ tsync_ctl_h &= ~I40E_PRTTSYN_TSYNENA;
+
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
+
+ /* Reset the timesync increment value. */
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
+ I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
+
+ return 0;
+}
+
+static int
+i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp, uint32_t flags)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct i40e_adapter *adapter =
+ (struct i40e_adapter *)dev->data->dev_private;
+
+ uint32_t sync_status;
+ uint32_t index = flags & 0x03;
+ uint64_t rx_tstamp_cycles;
+ uint64_t ns;
+
+ sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
+ if ((sync_status & (1 << index)) == 0)
+ return -EINVAL;
+
+ rx_tstamp_cycles = i40e_read_rx_tstamp_cyclecounter(dev, index);
+ ns = rte_timecounter_update(&adapter->rx_tstamp_tc, rx_tstamp_cycles);
+ *timestamp = rte_ns_to_timespec(ns);
+
+ return 0;
+}
+
+static int
+i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct i40e_adapter *adapter =
+ (struct i40e_adapter *)dev->data->dev_private;
+
+ uint32_t sync_status;
+ uint64_t tx_tstamp_cycles;
+ uint64_t ns;
+
+ sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
+ if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
+ return -EINVAL;
+
+ tx_tstamp_cycles = i40e_read_tx_tstamp_cyclecounter(dev);
+ ns = rte_timecounter_update(&adapter->tx_tstamp_tc, tx_tstamp_cycles);
+ *timestamp = rte_ns_to_timespec(ns);
+
+ return 0;
+}
+
+/*
+ * i40e_parse_dcb_configure - parse dcb configure from user
+ * @dev: the device being configured
+ * @dcb_cfg: pointer of the result of parse
+ * @*tc_map: bit map of enabled traffic classes
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static int
+i40e_parse_dcb_configure(struct rte_eth_dev *dev,
+ struct i40e_dcbx_config *dcb_cfg,
+ uint8_t *tc_map)
+{
+ struct rte_eth_dcb_rx_conf *dcb_rx_conf;
+ uint8_t i, tc_bw, bw_lf;
+
+ memset(dcb_cfg, 0, sizeof(struct i40e_dcbx_config));
+
+ dcb_rx_conf = &dev->data->dev_conf.rx_adv_conf.dcb_rx_conf;
+ if (dcb_rx_conf->nb_tcs > I40E_MAX_TRAFFIC_CLASS) {
+ PMD_INIT_LOG(ERR, "number of tc exceeds max.");
+ return -EINVAL;
+ }
+
+ /* assume each tc has the same bw */
+ tc_bw = I40E_MAX_PERCENT / dcb_rx_conf->nb_tcs;
+ for (i = 0; i < dcb_rx_conf->nb_tcs; i++)
+ dcb_cfg->etscfg.tcbwtable[i] = tc_bw;
+ /* to ensure the sum of tcbw is equal to 100 */
+ bw_lf = I40E_MAX_PERCENT % dcb_rx_conf->nb_tcs;
+ for (i = 0; i < bw_lf; i++)
+ dcb_cfg->etscfg.tcbwtable[i]++;
+
+ /* assume each tc has the same Transmission Selection Algorithm */
+ for (i = 0; i < dcb_rx_conf->nb_tcs; i++)
+ dcb_cfg->etscfg.tsatable[i] = I40E_IEEE_TSA_ETS;
+
+ for (i = 0; i < I40E_MAX_USER_PRIORITY; i++)
+ dcb_cfg->etscfg.prioritytable[i] =
+ dcb_rx_conf->dcb_tc[i];
+
+ /* FW needs one App to configure HW */
+ dcb_cfg->numapps = I40E_DEFAULT_DCB_APP_NUM;
+ dcb_cfg->app[0].selector = I40E_APP_SEL_ETHTYPE;
+ dcb_cfg->app[0].priority = I40E_DEFAULT_DCB_APP_PRIO;
+ dcb_cfg->app[0].protocolid = I40E_APP_PROTOID_FCOE;
+
+ if (dcb_rx_conf->nb_tcs == 0)
+ *tc_map = 1; /* tc0 only */
+ else
+ *tc_map = RTE_LEN2MASK(dcb_rx_conf->nb_tcs, uint8_t);
+
+ if (dev->data->dev_conf.dcb_capability_en & ETH_DCB_PFC_SUPPORT) {
+ dcb_cfg->pfc.willing = 0;
+ dcb_cfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS;
+ dcb_cfg->pfc.pfcenable = *tc_map;
+ }
+ return 0;
+}
+
+
+static enum i40e_status_code
+i40e_vsi_update_queue_mapping(struct i40e_vsi *vsi,
+ struct i40e_aqc_vsi_properties_data *info,
+ uint8_t enabled_tcmap)
+{
+ enum i40e_status_code ret;
+ int i, total_tc = 0;
+ uint16_t qpnum_per_tc, bsf, qp_idx;
+ struct rte_eth_dev_data *dev_data = I40E_VSI_TO_DEV_DATA(vsi);
+ struct i40e_pf *pf = I40E_VSI_TO_PF(vsi);
+ uint16_t used_queues;
+
+ ret = validate_tcmap_parameter(vsi, enabled_tcmap);
+ if (ret != I40E_SUCCESS)
+ return ret;
+
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ if (enabled_tcmap & (1 << i))
+ total_tc++;
+ }
+ if (total_tc == 0)
+ total_tc = 1;
+ vsi->enabled_tc = enabled_tcmap;
+
+ /* different VSI has different queues assigned */
+ if (vsi->type == I40E_VSI_MAIN)
+ used_queues = dev_data->nb_rx_queues -
+ pf->nb_cfg_vmdq_vsi * RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM;
+ else if (vsi->type == I40E_VSI_VMDQ2)
+ used_queues = RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM;
+ else {
+ PMD_INIT_LOG(ERR, "unsupported VSI type.");
+ return I40E_ERR_NO_AVAILABLE_VSI;
+ }
+
+ qpnum_per_tc = used_queues / total_tc;
+ /* Number of queues per enabled TC */
+ if (qpnum_per_tc == 0) {
+ PMD_INIT_LOG(ERR, " number of queues is less that tcs.");
+ return I40E_ERR_INVALID_QP_ID;
+ }
+ qpnum_per_tc = RTE_MIN(i40e_align_floor(qpnum_per_tc),
+ I40E_MAX_Q_PER_TC);
+ bsf = rte_bsf32(qpnum_per_tc);
+
+ /**
+ * Configure TC and queue mapping parameters, for enabled TC,
+ * allocate qpnum_per_tc queues to this traffic. For disabled TC,
+ * default queue will serve it.
+ */
+ qp_idx = 0;
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ if (vsi->enabled_tc & (1 << i)) {
+ info->tc_mapping[i] = rte_cpu_to_le_16((qp_idx <<
+ I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) |
+ (bsf << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT));
+ qp_idx += qpnum_per_tc;
+ } else
+ info->tc_mapping[i] = 0;
+ }
+
+ /* Associate queue number with VSI, Keep vsi->nb_qps unchanged */
+ if (vsi->type == I40E_VSI_SRIOV) {
+ info->mapping_flags |=
+ rte_cpu_to_le_16(I40E_AQ_VSI_QUE_MAP_NONCONTIG);
+ for (i = 0; i < vsi->nb_qps; i++)
+ info->queue_mapping[i] =
+ rte_cpu_to_le_16(vsi->base_queue + i);
+ } else {
+ info->mapping_flags |=
+ rte_cpu_to_le_16(I40E_AQ_VSI_QUE_MAP_CONTIG);
+ info->queue_mapping[0] = rte_cpu_to_le_16(vsi->base_queue);
+ }
+ info->valid_sections |=
+ rte_cpu_to_le_16(I40E_AQ_VSI_PROP_QUEUE_MAP_VALID);
+
+ return I40E_SUCCESS;
+}
+
+/*
+ * i40e_config_switch_comp_tc - Configure VEB tc setting for given TC map
+ * @veb: VEB to be configured
+ * @tc_map: enabled TC bitmap
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static enum i40e_status_code
+i40e_config_switch_comp_tc(struct i40e_veb *veb, uint8_t tc_map)
+{
+ struct i40e_aqc_configure_switching_comp_bw_config_data veb_bw;
+ struct i40e_aqc_query_switching_comp_bw_config_resp bw_query;
+ struct i40e_aqc_query_switching_comp_ets_config_resp ets_query;
+ struct i40e_hw *hw = I40E_VSI_TO_HW(veb->associate_vsi);
+ enum i40e_status_code ret = I40E_SUCCESS;
+ int i;
+ uint32_t bw_max;
+
+ /* Check if enabled_tc is same as existing or new TCs */
+ if (veb->enabled_tc == tc_map)
+ return ret;
+
+ /* configure tc bandwidth */
+ memset(&veb_bw, 0, sizeof(veb_bw));
+ veb_bw.tc_valid_bits = tc_map;
+ /* Enable ETS TCs with equal BW Share for now across all VSIs */
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ if (tc_map & BIT_ULL(i))
+ veb_bw.tc_bw_share_credits[i] = 1;
+ }
+ ret = i40e_aq_config_switch_comp_bw_config(hw, veb->seid,
+ &veb_bw, NULL);
+ if (ret) {
+ PMD_INIT_LOG(ERR,
+ "AQ command Config switch_comp BW allocation per TC failed = %d",
+ hw->aq.asq_last_status);
+ return ret;
+ }
+
+ memset(&ets_query, 0, sizeof(ets_query));
+ ret = i40e_aq_query_switch_comp_ets_config(hw, veb->seid,
+ &ets_query, NULL);
+ if (ret != I40E_SUCCESS) {
+ PMD_DRV_LOG(ERR,
+ "Failed to get switch_comp ETS configuration %u",
+ hw->aq.asq_last_status);
+ return ret;
+ }
+ memset(&bw_query, 0, sizeof(bw_query));
+ ret = i40e_aq_query_switch_comp_bw_config(hw, veb->seid,
+ &bw_query, NULL);
+ if (ret != I40E_SUCCESS) {
+ PMD_DRV_LOG(ERR,
+ "Failed to get switch_comp bandwidth configuration %u",
+ hw->aq.asq_last_status);
+ return ret;
+ }
+
+ /* store and print out BW info */
+ veb->bw_info.bw_limit = rte_le_to_cpu_16(ets_query.port_bw_limit);
+ veb->bw_info.bw_max = ets_query.tc_bw_max;
+ PMD_DRV_LOG(DEBUG, "switch_comp bw limit:%u", veb->bw_info.bw_limit);
+ PMD_DRV_LOG(DEBUG, "switch_comp max_bw:%u", veb->bw_info.bw_max);
+ bw_max = rte_le_to_cpu_16(bw_query.tc_bw_max[0]) |
+ (rte_le_to_cpu_16(bw_query.tc_bw_max[1]) <<
+ I40E_16_BIT_WIDTH);
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ veb->bw_info.bw_ets_share_credits[i] =
+ bw_query.tc_bw_share_credits[i];
+ veb->bw_info.bw_ets_credits[i] =
+ rte_le_to_cpu_16(bw_query.tc_bw_limits[i]);
+ /* 4 bits per TC, 4th bit is reserved */
+ veb->bw_info.bw_ets_max[i] =
+ (uint8_t)((bw_max >> (i * I40E_4_BIT_WIDTH)) &
+ RTE_LEN2MASK(3, uint8_t));
+ PMD_DRV_LOG(DEBUG, "\tVEB TC%u:share credits %u", i,
+ veb->bw_info.bw_ets_share_credits[i]);
+ PMD_DRV_LOG(DEBUG, "\tVEB TC%u:credits %u", i,
+ veb->bw_info.bw_ets_credits[i]);
+ PMD_DRV_LOG(DEBUG, "\tVEB TC%u: max credits: %u", i,
+ veb->bw_info.bw_ets_max[i]);
+ }
+
+ veb->enabled_tc = tc_map;
+
+ return ret;
+}
+
+
+/*
+ * i40e_vsi_config_tc - Configure VSI tc setting for given TC map
+ * @vsi: VSI to be configured
+ * @tc_map: enabled TC bitmap
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static enum i40e_status_code
+i40e_vsi_config_tc(struct i40e_vsi *vsi, uint8_t tc_map)
+{
+ struct i40e_aqc_configure_vsi_tc_bw_data bw_data;
+ struct i40e_vsi_context ctxt;
+ struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+ enum i40e_status_code ret = I40E_SUCCESS;
+ int i;
+
+ /* Check if enabled_tc is same as existing or new TCs */
+ if (vsi->enabled_tc == tc_map)
+ return ret;
+
+ /* configure tc bandwidth */
+ memset(&bw_data, 0, sizeof(bw_data));
+ bw_data.tc_valid_bits = tc_map;
+ /* Enable ETS TCs with equal BW Share for now across all VSIs */
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ if (tc_map & BIT_ULL(i))
+ bw_data.tc_bw_credits[i] = 1;
+ }
+ ret = i40e_aq_config_vsi_tc_bw(hw, vsi->seid, &bw_data, NULL);
+ if (ret) {
+ PMD_INIT_LOG(ERR,
+ "AQ command Config VSI BW allocation per TC failed = %d",
+ hw->aq.asq_last_status);
+ goto out;
+ }
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
+ vsi->info.qs_handle[i] = bw_data.qs_handles[i];
+
+ /* Update Queue Pairs Mapping for currently enabled UPs */
+ ctxt.seid = vsi->seid;
+ ctxt.pf_num = hw->pf_id;
+ ctxt.vf_num = 0;
+ ctxt.uplink_seid = vsi->uplink_seid;
+ ctxt.info = vsi->info;
+ i40e_get_cap(hw);
+ ret = i40e_vsi_update_queue_mapping(vsi, &ctxt.info, tc_map);
+ if (ret)
+ goto out;
+
+ /* Update the VSI after updating the VSI queue-mapping information */
+ ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+ if (ret) {
+ PMD_INIT_LOG(ERR, "Failed to configure TC queue mapping = %d",
+ hw->aq.asq_last_status);
+ goto out;
+ }
+ /* update the local VSI info with updated queue map */
+ rte_memcpy(&vsi->info.tc_mapping, &ctxt.info.tc_mapping,
+ sizeof(vsi->info.tc_mapping));
+ rte_memcpy(&vsi->info.queue_mapping,
+ &ctxt.info.queue_mapping,
+ sizeof(vsi->info.queue_mapping));
+ vsi->info.mapping_flags = ctxt.info.mapping_flags;
+ vsi->info.valid_sections = 0;
+
+ /* query and update current VSI BW information */
+ ret = i40e_vsi_get_bw_config(vsi);
+ if (ret) {
+ PMD_INIT_LOG(ERR,
+ "Failed updating vsi bw info, err %s aq_err %s",
+ i40e_stat_str(hw, ret),
+ i40e_aq_str(hw, hw->aq.asq_last_status));
+ goto out;
+ }
+
+ vsi->enabled_tc = tc_map;
+
+out:
+ return ret;
+}
+
+/*
+ * i40e_dcb_hw_configure - program the dcb setting to hw
+ * @pf: pf the configuration is taken on
+ * @new_cfg: new configuration
+ * @tc_map: enabled TC bitmap
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static enum i40e_status_code
+i40e_dcb_hw_configure(struct i40e_pf *pf,
+ struct i40e_dcbx_config *new_cfg,
+ uint8_t tc_map)
+{
+ struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+ struct i40e_dcbx_config *old_cfg = &hw->local_dcbx_config;
+ struct i40e_vsi *main_vsi = pf->main_vsi;
+ struct i40e_vsi_list *vsi_list;
+ enum i40e_status_code ret;
+ int i;
+ uint32_t val;
+
+ /* Use the FW API if FW > v4.4*/
+ if (!(((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver >= 4)) ||
+ (hw->aq.fw_maj_ver >= 5))) {
+ PMD_INIT_LOG(ERR,
+ "FW < v4.4, can not use FW LLDP API to configure DCB");
+ return I40E_ERR_FIRMWARE_API_VERSION;
+ }
+
+ /* Check if need reconfiguration */
+ if (!memcmp(new_cfg, old_cfg, sizeof(struct i40e_dcbx_config))) {
+ PMD_INIT_LOG(ERR, "No Change in DCB Config required.");
+ return I40E_SUCCESS;
+ }
+
+ /* Copy the new config to the current config */
+ *old_cfg = *new_cfg;
+ old_cfg->etsrec = old_cfg->etscfg;
+ ret = i40e_set_dcb_config(hw);
+ if (ret) {
+ PMD_INIT_LOG(ERR, "Set DCB Config failed, err %s aq_err %s",
+ i40e_stat_str(hw, ret),
+ i40e_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
+ }
+ /* set receive Arbiter to RR mode and ETS scheme by default */
+ for (i = 0; i <= I40E_PRTDCB_RETSTCC_MAX_INDEX; i++) {
+ val = I40E_READ_REG(hw, I40E_PRTDCB_RETSTCC(i));
+ val &= ~(I40E_PRTDCB_RETSTCC_BWSHARE_MASK |
+ I40E_PRTDCB_RETSTCC_UPINTC_MODE_MASK |
+ I40E_PRTDCB_RETSTCC_ETSTC_SHIFT);
+ val |= ((uint32_t)old_cfg->etscfg.tcbwtable[i] <<
+ I40E_PRTDCB_RETSTCC_BWSHARE_SHIFT) &
+ I40E_PRTDCB_RETSTCC_BWSHARE_MASK;
+ val |= ((uint32_t)1 << I40E_PRTDCB_RETSTCC_UPINTC_MODE_SHIFT) &
+ I40E_PRTDCB_RETSTCC_UPINTC_MODE_MASK;
+ val |= ((uint32_t)1 << I40E_PRTDCB_RETSTCC_ETSTC_SHIFT) &
+ I40E_PRTDCB_RETSTCC_ETSTC_MASK;
+ I40E_WRITE_REG(hw, I40E_PRTDCB_RETSTCC(i), val);
+ }
+ /* get local mib to check whether it is configured correctly */
+ /* IEEE mode */
+ hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE;
+ /* Get Local DCB Config */
+ i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0,
+ &hw->local_dcbx_config);
+
+ /* if Veb is created, need to update TC of it at first */
+ if (main_vsi->veb) {
+ ret = i40e_config_switch_comp_tc(main_vsi->veb, tc_map);
+ if (ret)
+ PMD_INIT_LOG(WARNING,
+ "Failed configuring TC for VEB seid=%d",
+ main_vsi->veb->seid);
+ }
+ /* Update each VSI */
+ i40e_vsi_config_tc(main_vsi, tc_map);
+ if (main_vsi->veb) {
+ TAILQ_FOREACH(vsi_list, &main_vsi->veb->head, list) {
+ /* Beside main VSI and VMDQ VSIs, only enable default
+ * TC for other VSIs
+ */
+ if (vsi_list->vsi->type == I40E_VSI_VMDQ2)
+ ret = i40e_vsi_config_tc(vsi_list->vsi,
+ tc_map);
+ else
+ ret = i40e_vsi_config_tc(vsi_list->vsi,
+ I40E_DEFAULT_TCMAP);
+ if (ret)
+ PMD_INIT_LOG(WARNING,
+ "Failed configuring TC for VSI seid=%d",
+ vsi_list->vsi->seid);
+ /* continue */
+ }
+ }
+ return I40E_SUCCESS;
+}
+
+/*
+ * i40e_dcb_init_configure - initial dcb config
+ * @dev: device being configured
+ * @sw_dcb: indicate whether dcb is sw configured or hw offload
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static int
+i40e_dcb_init_configure(struct rte_eth_dev *dev, bool sw_dcb)
+{
+ 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);
+ int i, ret = 0;
+
+ if ((pf->flags & I40E_FLAG_DCB) == 0) {
+ PMD_INIT_LOG(ERR, "HW doesn't support DCB");
+ return -ENOTSUP;
+ }
+
+ /* DCB initialization:
+ * Update DCB configuration from the Firmware and configure
+ * LLDP MIB change event.
+ */
+ if (sw_dcb == TRUE) {
+ ret = i40e_init_dcb(hw);
+ /* If lldp agent is stopped, the return value from
+ * i40e_init_dcb we expect is failure with I40E_AQ_RC_EPERM
+ * adminq status. Otherwise, it should return success.
+ */
+ if ((ret == I40E_SUCCESS) || (ret != I40E_SUCCESS &&
+ hw->aq.asq_last_status == I40E_AQ_RC_EPERM)) {
+ memset(&hw->local_dcbx_config, 0,
+ sizeof(struct i40e_dcbx_config));
+ /* set dcb default configuration */
+ hw->local_dcbx_config.etscfg.willing = 0;
+ hw->local_dcbx_config.etscfg.maxtcs = 0;
+ hw->local_dcbx_config.etscfg.tcbwtable[0] = 100;
+ hw->local_dcbx_config.etscfg.tsatable[0] =
+ I40E_IEEE_TSA_ETS;
+ /* all UPs mapping to TC0 */
+ for (i = 0; i < I40E_MAX_USER_PRIORITY; i++)
+ hw->local_dcbx_config.etscfg.prioritytable[i] = 0;
+ hw->local_dcbx_config.etsrec =
+ hw->local_dcbx_config.etscfg;
+ hw->local_dcbx_config.pfc.willing = 0;
+ hw->local_dcbx_config.pfc.pfccap =
+ I40E_MAX_TRAFFIC_CLASS;
+ /* FW needs one App to configure HW */
+ hw->local_dcbx_config.numapps = 1;
+ hw->local_dcbx_config.app[0].selector =
+ I40E_APP_SEL_ETHTYPE;
+ hw->local_dcbx_config.app[0].priority = 3;
+ hw->local_dcbx_config.app[0].protocolid =
+ I40E_APP_PROTOID_FCOE;
+ ret = i40e_set_dcb_config(hw);
+ if (ret) {
+ PMD_INIT_LOG(ERR,
+ "default dcb config fails. err = %d, aq_err = %d.",
+ ret, hw->aq.asq_last_status);
+ return -ENOSYS;
+ }
+ } else {
+ PMD_INIT_LOG(ERR,
+ "DCB initialization in FW fails, err = %d, aq_err = %d.",
+ ret, hw->aq.asq_last_status);
+ return -ENOTSUP;
+ }
+ } else {
+ ret = i40e_aq_start_lldp(hw, NULL);
+ if (ret != I40E_SUCCESS)
+ PMD_INIT_LOG(DEBUG, "Failed to start lldp");
+
+ ret = i40e_init_dcb(hw);
+ if (!ret) {
+ if (hw->dcbx_status == I40E_DCBX_STATUS_DISABLED) {
+ PMD_INIT_LOG(ERR,
+ "HW doesn't support DCBX offload.");
+ return -ENOTSUP;
+ }
+ } else {
+ PMD_INIT_LOG(ERR,
+ "DCBX configuration failed, err = %d, aq_err = %d.",
+ ret, hw->aq.asq_last_status);
+ return -ENOTSUP;
+ }
+ }
+ return 0;
+}
+
+/*
+ * i40e_dcb_setup - setup dcb related config
+ * @dev: device being configured
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static int
+i40e_dcb_setup(struct rte_eth_dev *dev)
+{
+ struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+ struct i40e_dcbx_config dcb_cfg;
+ uint8_t tc_map = 0;
+ int ret = 0;
+
+ if ((pf->flags & I40E_FLAG_DCB) == 0) {
+ PMD_INIT_LOG(ERR, "HW doesn't support DCB");
+ return -ENOTSUP;
+ }
+
+ if (pf->vf_num != 0)
+ PMD_INIT_LOG(DEBUG, " DCB only works on pf and vmdq vsis.");
+
+ ret = i40e_parse_dcb_configure(dev, &dcb_cfg, &tc_map);
+ if (ret) {
+ PMD_INIT_LOG(ERR, "invalid dcb config");
+ return -EINVAL;
+ }
+ ret = i40e_dcb_hw_configure(pf, &dcb_cfg, tc_map);
+ if (ret) {
+ PMD_INIT_LOG(ERR, "dcb sw configure fails");
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static int
+i40e_dev_get_dcb_info(struct rte_eth_dev *dev,
+ struct rte_eth_dcb_info *dcb_info)
+{
+ 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_vsi *vsi = pf->main_vsi;
+ struct i40e_dcbx_config *dcb_cfg = &hw->local_dcbx_config;
+ uint16_t bsf, tc_mapping;
+ int i, j = 0;
+
+ if (dev->data->dev_conf.rxmode.mq_mode & ETH_MQ_RX_DCB_FLAG)
+ dcb_info->nb_tcs = rte_bsf32(vsi->enabled_tc + 1);
+ else
+ dcb_info->nb_tcs = 1;
+ for (i = 0; i < I40E_MAX_USER_PRIORITY; i++)
+ dcb_info->prio_tc[i] = dcb_cfg->etscfg.prioritytable[i];
+ for (i = 0; i < dcb_info->nb_tcs; i++)
+ dcb_info->tc_bws[i] = dcb_cfg->etscfg.tcbwtable[i];
+
+ /* get queue mapping if vmdq is disabled */
+ if (!pf->nb_cfg_vmdq_vsi) {
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ if (!(vsi->enabled_tc & (1 << i)))
+ continue;
+ tc_mapping = rte_le_to_cpu_16(vsi->info.tc_mapping[i]);
+ dcb_info->tc_queue.tc_rxq[j][i].base =
+ (tc_mapping & I40E_AQ_VSI_TC_QUE_OFFSET_MASK) >>
+ I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT;
+ dcb_info->tc_queue.tc_txq[j][i].base =
+ dcb_info->tc_queue.tc_rxq[j][i].base;
+ bsf = (tc_mapping & I40E_AQ_VSI_TC_QUE_NUMBER_MASK) >>
+ I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT;
+ dcb_info->tc_queue.tc_rxq[j][i].nb_queue = 1 << bsf;
+ dcb_info->tc_queue.tc_txq[j][i].nb_queue =
+ dcb_info->tc_queue.tc_rxq[j][i].nb_queue;
+ }
+ return 0;
+ }
+
+ /* get queue mapping if vmdq is enabled */
+ do {
+ vsi = pf->vmdq[j].vsi;
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ if (!(vsi->enabled_tc & (1 << i)))
+ continue;
+ tc_mapping = rte_le_to_cpu_16(vsi->info.tc_mapping[i]);
+ dcb_info->tc_queue.tc_rxq[j][i].base =
+ (tc_mapping & I40E_AQ_VSI_TC_QUE_OFFSET_MASK) >>
+ I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT;
+ dcb_info->tc_queue.tc_txq[j][i].base =
+ dcb_info->tc_queue.tc_rxq[j][i].base;
+ bsf = (tc_mapping & I40E_AQ_VSI_TC_QUE_NUMBER_MASK) >>
+ I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT;
+ dcb_info->tc_queue.tc_rxq[j][i].nb_queue = 1 << bsf;
+ dcb_info->tc_queue.tc_txq[j][i].nb_queue =
+ dcb_info->tc_queue.tc_rxq[j][i].nb_queue;
+ }
+ j++;
+ } while (j < RTE_MIN(pf->nb_cfg_vmdq_vsi, ETH_MAX_VMDQ_POOL));
+ return 0;
+}
+
+static int
+i40e_dev_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id)
+{
+ struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint16_t interval =
+ i40e_calc_itr_interval(RTE_LIBRTE_I40E_ITR_INTERVAL);
+ uint16_t msix_intr;
+
+ msix_intr = intr_handle->intr_vec[queue_id];
+ if (msix_intr == I40E_MISC_VEC_ID)
+ I40E_WRITE_REG(hw, I40E_PFINT_DYN_CTL0,
+ I40E_PFINT_DYN_CTLN_INTENA_MASK |
+ I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
+ (0 << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT) |
+ (interval <<
+ I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT));
+ else
+ I40E_WRITE_REG(hw,
+ I40E_PFINT_DYN_CTLN(msix_intr -
+ I40E_RX_VEC_START),
+ I40E_PFINT_DYN_CTLN_INTENA_MASK |
+ I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
+ (0 << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT) |
+ (interval <<
+ I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT));
+
+ I40E_WRITE_FLUSH(hw);
+ rte_intr_enable(&pci_dev->intr_handle);
+
+ return 0;
+}
+
+static int
+i40e_dev_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id)
+{
+ struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint16_t msix_intr;
+
+ msix_intr = intr_handle->intr_vec[queue_id];
+ if (msix_intr == I40E_MISC_VEC_ID)
+ I40E_WRITE_REG(hw, I40E_PFINT_DYN_CTL0, 0);
+ else
+ I40E_WRITE_REG(hw,
+ I40E_PFINT_DYN_CTLN(msix_intr -
+ I40E_RX_VEC_START),
+ 0);
+ I40E_WRITE_FLUSH(hw);
+
+ return 0;
+}
+
+static int i40e_get_regs(struct rte_eth_dev *dev,
+ struct rte_dev_reg_info *regs)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint32_t *ptr_data = regs->data;
+ uint32_t reg_idx, arr_idx, arr_idx2, reg_offset;
+ const struct i40e_reg_info *reg_info;
+
+ if (ptr_data == NULL) {
+ regs->length = I40E_GLGEN_STAT_CLEAR + 4;
+ regs->width = sizeof(uint32_t);
+ return 0;
+ }
+
+ /* The first few registers have to be read using AQ operations */
+ reg_idx = 0;
+ while (i40e_regs_adminq[reg_idx].name) {
+ reg_info = &i40e_regs_adminq[reg_idx++];
+ for (arr_idx = 0; arr_idx <= reg_info->count1; arr_idx++)
+ for (arr_idx2 = 0;
+ arr_idx2 <= reg_info->count2;
+ arr_idx2++) {
+ reg_offset = arr_idx * reg_info->stride1 +
+ arr_idx2 * reg_info->stride2;
+ reg_offset += reg_info->base_addr;
+ ptr_data[reg_offset >> 2] =
+ i40e_read_rx_ctl(hw, reg_offset);
+ }
+ }
+
+ /* The remaining registers can be read using primitives */
+ reg_idx = 0;
+ while (i40e_regs_others[reg_idx].name) {
+ reg_info = &i40e_regs_others[reg_idx++];
+ for (arr_idx = 0; arr_idx <= reg_info->count1; arr_idx++)
+ for (arr_idx2 = 0;
+ arr_idx2 <= reg_info->count2;
+ arr_idx2++) {
+ reg_offset = arr_idx * reg_info->stride1 +
+ arr_idx2 * reg_info->stride2;
+ reg_offset += reg_info->base_addr;
+ ptr_data[reg_offset >> 2] =
+ I40E_READ_REG(hw, reg_offset);
+ }
+ }
+
+ return 0;
+}
+
+static int i40e_get_eeprom_length(struct rte_eth_dev *dev)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+ /* Convert word count to byte count */
+ return hw->nvm.sr_size << 1;
+}
+
+static int i40e_get_eeprom(struct rte_eth_dev *dev,
+ struct rte_dev_eeprom_info *eeprom)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ uint16_t *data = eeprom->data;
+ uint16_t offset, length, cnt_words;
+ int ret_code;
+
+ offset = eeprom->offset >> 1;
+ length = eeprom->length >> 1;
+ cnt_words = length;
+
+ if (offset > hw->nvm.sr_size ||
+ offset + length > hw->nvm.sr_size) {
+ PMD_DRV_LOG(ERR, "Requested EEPROM bytes out of range.");
+ return -EINVAL;
+ }
+
+ eeprom->magic = hw->vendor_id | (hw->device_id << 16);
+
+ ret_code = i40e_read_nvm_buffer(hw, offset, &cnt_words, data);
+ if (ret_code != I40E_SUCCESS || cnt_words != length) {
+ PMD_DRV_LOG(ERR, "EEPROM read failed.");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void i40e_set_default_mac_addr(struct rte_eth_dev *dev,
+ struct ether_addr *mac_addr)
+{
+ struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+ if (!is_valid_assigned_ether_addr(mac_addr)) {
+ PMD_DRV_LOG(ERR, "Tried to set invalid MAC address.");
+ return;
+ }
+
+ /* Flags: 0x3 updates port address */
+ i40e_aq_mac_address_write(hw, 0x3, mac_addr->addr_bytes, NULL);
+}
+
+static int
+i40e_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+ struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+ struct rte_eth_dev_data *dev_data = pf->dev_data;
+ uint32_t frame_size = mtu + I40E_ETH_OVERHEAD;
+ int ret = 0;
+
+ /* check if mtu is within the allowed range */
+ if ((mtu < ETHER_MIN_MTU) || (frame_size > I40E_FRAME_SIZE_MAX))
+ return -EINVAL;
+
+ /* mtu setting is forbidden if port is start */
+ if (dev_data->dev_started) {
+ PMD_DRV_LOG(ERR, "port %d must be stopped before configuration",
+ dev_data->port_id);
+ return -EBUSY;
+ }
+
+ if (frame_size > ETHER_MAX_LEN)
+ dev_data->dev_conf.rxmode.jumbo_frame = 1;
+ else
+ dev_data->dev_conf.rxmode.jumbo_frame = 0;
+
+ dev_data->dev_conf.rxmode.max_rx_pkt_len = frame_size;
+
+ return ret;
+}
+
+/* Restore ethertype filter */
+static void
+i40e_ethertype_filter_restore(struct i40e_pf *pf)
+{
+ struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+ struct i40e_ethertype_filter_list
+ *ethertype_list = &pf->ethertype.ethertype_list;
+ struct i40e_ethertype_filter *f;
+ struct i40e_control_filter_stats stats;
+ uint16_t flags;
+
+ TAILQ_FOREACH(f, ethertype_list, rules) {
+ flags = 0;
+ if (!(f->flags & RTE_ETHTYPE_FLAGS_MAC))
+ flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC;
+ if (f->flags & RTE_ETHTYPE_FLAGS_DROP)
+ flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP;
+ flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TO_QUEUE;
+
+ memset(&stats, 0, sizeof(stats));
+ i40e_aq_add_rem_control_packet_filter(hw,
+ f->input.mac_addr.addr_bytes,
+ f->input.ether_type,
+ flags, pf->main_vsi->seid,
+ f->queue, 1, &stats, NULL);
+ }
+ PMD_DRV_LOG(INFO, "Ethertype filter:"
+ " mac_etype_used = %u, etype_used = %u,"
+ " mac_etype_free = %u, etype_free = %u",
+ stats.mac_etype_used, stats.etype_used,
+ stats.mac_etype_free, stats.etype_free);
+}
+
+/* Restore tunnel filter */
+static void
+i40e_tunnel_filter_restore(struct i40e_pf *pf)
+{
+ struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+ struct i40e_vsi *vsi;
+ struct i40e_pf_vf *vf;
+ struct i40e_tunnel_filter_list
+ *tunnel_list = &pf->tunnel.tunnel_list;
+ struct i40e_tunnel_filter *f;
+ struct i40e_aqc_add_rm_cloud_filt_elem_ext cld_filter;
+ bool big_buffer = 0;
+
+ TAILQ_FOREACH(f, tunnel_list, rules) {
+ if (!f->is_to_vf)
+ vsi = pf->main_vsi;
+ else {
+ vf = &pf->vfs[f->vf_id];
+ vsi = vf->vsi;
+ }
+ memset(&cld_filter, 0, sizeof(cld_filter));
+ ether_addr_copy((struct ether_addr *)&f->input.outer_mac,
+ (struct ether_addr *)&cld_filter.element.outer_mac);
+ ether_addr_copy((struct ether_addr *)&f->input.inner_mac,
+ (struct ether_addr *)&cld_filter.element.inner_mac);
+ cld_filter.element.inner_vlan = f->input.inner_vlan;
+ cld_filter.element.flags = f->input.flags;
+ cld_filter.element.tenant_id = f->input.tenant_id;
+ cld_filter.element.queue_number = f->queue;
+ rte_memcpy(cld_filter.general_fields,
+ f->input.general_fields,
+ sizeof(f->input.general_fields));
+
+ if (((f->input.flags &
+ I40E_AQC_ADD_CLOUD_FILTER_0X11) ==
+ I40E_AQC_ADD_CLOUD_FILTER_0X11) ||
+ ((f->input.flags &
+ I40E_AQC_ADD_CLOUD_FILTER_0X12) ==
+ I40E_AQC_ADD_CLOUD_FILTER_0X12) ||
+ ((f->input.flags &
+ I40E_AQC_ADD_CLOUD_FILTER_0X10) ==
+ I40E_AQC_ADD_CLOUD_FILTER_0X10))
+ big_buffer = 1;
+
+ if (big_buffer)
+ i40e_aq_add_cloud_filters_big_buffer(hw,
+ vsi->seid, &cld_filter, 1);
+ else
+ i40e_aq_add_cloud_filters(hw, vsi->seid,
+ &cld_filter.element, 1);
+ }
+}
+
+static void
+i40e_filter_restore(struct i40e_pf *pf)
+{
+ i40e_ethertype_filter_restore(pf);
+ i40e_tunnel_filter_restore(pf);
+ i40e_fdir_filter_restore(pf);
+}
+
+static bool
+is_device_supported(struct rte_eth_dev *dev, struct rte_pci_driver *drv)
+{
+ if (strcmp(dev->device->driver->name, drv->driver.name))
+ return false;
+
+ return true;
+}
+
+bool
+is_i40e_supported(struct rte_eth_dev *dev)
+{
+ return is_device_supported(dev, &rte_i40e_pmd);
+}
+
+struct i40e_customized_pctype*
+i40e_find_customized_pctype(struct i40e_pf *pf, uint8_t index)
+{
+ int i;
+
+ for (i = 0; i < I40E_CUSTOMIZED_MAX; i++) {
+ if (pf->customized_pctype[i].index == index)
+ return &pf->customized_pctype[i];
+ }
+ return NULL;
+}
+
+static int
+i40e_update_customized_pctype(struct rte_eth_dev *dev, uint8_t *pkg,
+ uint32_t pkg_size, uint32_t proto_num,
+ struct rte_pmd_i40e_proto_info *proto)
+{
+ struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+ uint32_t pctype_num;
+ struct rte_pmd_i40e_ptype_info *pctype;
+ uint32_t buff_size;
+ struct i40e_customized_pctype *new_pctype = NULL;
+ uint8_t proto_id;
+ uint8_t pctype_value;
+ char name[64];
+ uint32_t i, j, n;
+ int ret;
+
+ ret = rte_pmd_i40e_get_ddp_info(pkg, pkg_size,
+ (uint8_t *)&pctype_num, sizeof(pctype_num),
+ RTE_PMD_I40E_PKG_INFO_PCTYPE_NUM);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get pctype number");
+ return -1;
+ }
+ if (!pctype_num) {
+ PMD_DRV_LOG(INFO, "No new pctype added");
+ return -1;
+ }
+
+ buff_size = pctype_num * sizeof(struct rte_pmd_i40e_proto_info);
+ pctype = rte_zmalloc("new_pctype", buff_size, 0);
+ if (!pctype) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory");
+ return -1;
+ }
+ /* get information about new pctype list */
+ ret = rte_pmd_i40e_get_ddp_info(pkg, pkg_size,
+ (uint8_t *)pctype, buff_size,
+ RTE_PMD_I40E_PKG_INFO_PCTYPE_LIST);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get pctype list");
+ rte_free(pctype);
+ return -1;
+ }
+
+ /* Update customized pctype. */
+ for (i = 0; i < pctype_num; i++) {
+ pctype_value = pctype[i].ptype_id;
+ memset(name, 0, sizeof(name));
+ for (j = 0; j < RTE_PMD_I40E_PROTO_NUM; j++) {
+ proto_id = pctype[i].protocols[j];
+ if (proto_id == RTE_PMD_I40E_PROTO_UNUSED)
+ continue;
+ for (n = 0; n < proto_num; n++) {
+ if (proto[n].proto_id != proto_id)
+ continue;
+ strcat(name, proto[n].name);
+ strcat(name, "_");
+ break;
+ }
+ }
+ name[strlen(name) - 1] = '\0';
+ if (!strcmp(name, "GTPC"))
+ new_pctype =
+ i40e_find_customized_pctype(pf,
+ I40E_CUSTOMIZED_GTPC);
+ else if (!strcmp(name, "GTPU_IPV4"))
+ new_pctype =
+ i40e_find_customized_pctype(pf,
+ I40E_CUSTOMIZED_GTPU_IPV4);
+ else if (!strcmp(name, "GTPU_IPV6"))
+ new_pctype =
+ i40e_find_customized_pctype(pf,
+ I40E_CUSTOMIZED_GTPU_IPV6);
+ else if (!strcmp(name, "GTPU"))
+ new_pctype =
+ i40e_find_customized_pctype(pf,
+ I40E_CUSTOMIZED_GTPU);
+ if (new_pctype) {
+ new_pctype->pctype = pctype_value;
+ new_pctype->valid = true;
+ }
+ }
+
+ rte_free(pctype);
+ return 0;
+}
+
+static int
+i40e_update_customized_ptype(struct rte_eth_dev *dev, uint8_t *pkg,
+ uint32_t pkg_size, uint32_t proto_num,
+ struct rte_pmd_i40e_proto_info *proto)
+{
+ struct rte_pmd_i40e_ptype_mapping *ptype_mapping;
+ uint8_t port_id = dev->data->port_id;
+ uint32_t ptype_num;
+ struct rte_pmd_i40e_ptype_info *ptype;
+ uint32_t buff_size;
+ uint8_t proto_id;
+ char name[16];
+ uint32_t i, j, n;
+ bool inner_ip;
+ int ret;
+
+ /* get information about new ptype num */
+ ret = rte_pmd_i40e_get_ddp_info(pkg, pkg_size,
+ (uint8_t *)&ptype_num, sizeof(ptype_num),
+ RTE_PMD_I40E_PKG_INFO_PTYPE_NUM);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get ptype number");
+ return ret;
+ }
+ if (!ptype_num) {
+ PMD_DRV_LOG(INFO, "No new ptype added");
+ return -1;
+ }
+
+ buff_size = ptype_num * sizeof(struct rte_pmd_i40e_ptype_info);
+ ptype = rte_zmalloc("new_ptype", buff_size, 0);
+ if (!ptype) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory");
+ return -1;
+ }
+
+ /* get information about new ptype list */
+ ret = rte_pmd_i40e_get_ddp_info(pkg, pkg_size,
+ (uint8_t *)ptype, buff_size,
+ RTE_PMD_I40E_PKG_INFO_PTYPE_LIST);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get ptype list");
+ rte_free(ptype);
+ return ret;
+ }
+
+ buff_size = ptype_num * sizeof(struct rte_pmd_i40e_ptype_mapping);
+ ptype_mapping = rte_zmalloc("ptype_mapping", buff_size, 0);
+ if (!ptype_mapping) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory");
+ rte_free(ptype);
+ return -1;
+ }
+
+ /* Update ptype mapping table. */
+ for (i = 0; i < ptype_num; i++) {
+ ptype_mapping[i].hw_ptype = ptype[i].ptype_id;
+ ptype_mapping[i].sw_ptype = 0;
+ inner_ip = false;
+ for (j = 0; j < RTE_PMD_I40E_PROTO_NUM; j++) {
+ proto_id = ptype[i].protocols[j];
+ if (proto_id == RTE_PMD_I40E_PROTO_UNUSED)
+ continue;
+ for (n = 0; n < proto_num; n++) {
+ if (proto[n].proto_id != proto_id)
+ continue;
+ memset(name, 0, sizeof(name));
+ strcpy(name, proto[n].name);
+ if (!strncmp(name, "IPV4", 4) && !inner_ip) {
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
+ inner_ip = true;
+ } else if (!strncmp(name, "IPV4", 4) &&
+ inner_ip) {
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN;
+ } else if (!strncmp(name, "IPV6", 4) &&
+ !inner_ip) {
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+ inner_ip = true;
+ } else if (!strncmp(name, "IPV6", 4) &&
+ inner_ip) {
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN;
+ } else if (!strncmp(name, "IPV4FRAG", 8)) {
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN;
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L4_FRAG;
+ } else if (!strncmp(name, "IPV6FRAG", 8)) {
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN;
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L4_FRAG;
+ } else if (!strncmp(name, "GTPC", 4))
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_TUNNEL_GTPC;
+ else if (!strncmp(name, "GTPU", 4))
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_TUNNEL_GTPU;
+ else if (!strncmp(name, "UDP", 3))
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L4_UDP;
+ else if (!strncmp(name, "TCP", 3))
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L4_TCP;
+ else if (!strncmp(name, "SCTP", 4))
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L4_SCTP;
+ else if (!strncmp(name, "ICMP", 4) ||
+ !strncmp(name, "ICMPV6", 6))
+ ptype_mapping[i].sw_ptype |=
+ RTE_PTYPE_INNER_L4_ICMP;
+
+ break;
+ }
+ }
+ }
+
+ ret = rte_pmd_i40e_ptype_mapping_update(port_id, ptype_mapping,
+ ptype_num, 0);
+ if (ret)
+ PMD_DRV_LOG(ERR, "Failed to update mapping table.");
+
+ rte_free(ptype_mapping);
+ rte_free(ptype);
+ return ret;
+}
+
+void
+i40e_update_customized_info(struct rte_eth_dev *dev, uint8_t *pkg,
+ uint32_t pkg_size)
+{
+ struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+ uint32_t proto_num;
+ struct rte_pmd_i40e_proto_info *proto;
+ uint32_t buff_size;
+ uint32_t i;
+ int ret;
+
+ /* get information about protocol number */
+ ret = rte_pmd_i40e_get_ddp_info(pkg, pkg_size,
+ (uint8_t *)&proto_num, sizeof(proto_num),
+ RTE_PMD_I40E_PKG_INFO_PROTOCOL_NUM);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get protocol number");
+ return;
+ }
+ if (!proto_num) {
+ PMD_DRV_LOG(INFO, "No new protocol added");
+ return;
+ }
+
+ buff_size = proto_num * sizeof(struct rte_pmd_i40e_proto_info);
+ proto = rte_zmalloc("new_proto", buff_size, 0);
+ if (!proto) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory");
+ return;
+ }
+
+ /* get information about protocol list */
+ ret = rte_pmd_i40e_get_ddp_info(pkg, pkg_size,
+ (uint8_t *)proto, buff_size,
+ RTE_PMD_I40E_PKG_INFO_PROTOCOL_LIST);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get protocol list");
+ rte_free(proto);
+ return;
+ }
+
+ /* Check if GTP is supported. */
+ for (i = 0; i < proto_num; i++) {
+ if (!strncmp(proto[i].name, "GTP", 3)) {
+ pf->gtp_support = true;
+ break;
+ }
+ }
+
+ /* Update customized pctype info */
+ ret = i40e_update_customized_pctype(dev, pkg, pkg_size,
+ proto_num, proto);
+ if (ret)
+ PMD_DRV_LOG(INFO, "No pctype is updated.");
+
+ /* Update customized ptype info */
+ ret = i40e_update_customized_ptype(dev, pkg, pkg_size,
+ proto_num, proto);
+ if (ret)
+ PMD_DRV_LOG(INFO, "No ptype is updated.");
+
+ rte_free(proto);
+}
+
+/* Create a QinQ cloud filter
+ *
+ * The Fortville NIC has limited resources for tunnel filters,
+ * so we can only reuse existing filters.
+ *
+ * In step 1 we define which Field Vector fields can be used for
+ * filter types.
+ * As we do not have the inner tag defined as a field,
+ * we have to define it first, by reusing one of L1 entries.
+ *
+ * In step 2 we are replacing one of existing filter types with
+ * a new one for QinQ.
+ * As we reusing L1 and replacing L2, some of the default filter
+ * types will disappear,which depends on L1 and L2 entries we reuse.
+ *
+ * Step 1: Create L1 filter of outer vlan (12b) + inner vlan (12b)
+ *
+ * 1. Create L1 filter of outer vlan (12b) which will be in use
+ * later when we define the cloud filter.
+ * a. Valid_flags.replace_cloud = 0
+ * b. Old_filter = 10 (Stag_Inner_Vlan)
+ * c. New_filter = 0x10
+ * d. TR bit = 0xff (optional, not used here)
+ * e. Buffer – 2 entries:
+ * i. Byte 0 = 8 (outer vlan FV index).
+ * Byte 1 = 0 (rsv)
+ * Byte 2-3 = 0x0fff
+ * ii. Byte 0 = 37 (inner vlan FV index).
+ * Byte 1 =0 (rsv)
+ * Byte 2-3 = 0x0fff
+ *
+ * Step 2:
+ * 2. Create cloud filter using two L1 filters entries: stag and
+ * new filter(outer vlan+ inner vlan)
+ * a. Valid_flags.replace_cloud = 1
+ * b. Old_filter = 1 (instead of outer IP)
+ * c. New_filter = 0x10
+ * d. Buffer – 2 entries:
+ * i. Byte 0 = 0x80 | 7 (valid | Stag).
+ * Byte 1-3 = 0 (rsv)
+ * ii. Byte 8 = 0x80 | 0x10 (valid | new l1 filter step1)
+ * Byte 9-11 = 0 (rsv)
+ */
+static int
+i40e_cloud_filter_qinq_create(struct i40e_pf *pf)
+{
+ int ret = -ENOTSUP;
+ struct i40e_aqc_replace_cloud_filters_cmd filter_replace;
+ struct i40e_aqc_replace_cloud_filters_cmd_buf filter_replace_buf;
+ struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+
+ /* Init */
+ memset(&filter_replace, 0,
+ sizeof(struct i40e_aqc_replace_cloud_filters_cmd));
+ memset(&filter_replace_buf, 0,
+ sizeof(struct i40e_aqc_replace_cloud_filters_cmd_buf));
+
+ /* create L1 filter */
+ filter_replace.old_filter_type =
+ I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN;
+ filter_replace.new_filter_type = I40E_AQC_ADD_CLOUD_FILTER_0X10;
+ filter_replace.tr_bit = 0;
+
+ /* Prepare the buffer, 2 entries */
+ filter_replace_buf.data[0] = I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN;
+ filter_replace_buf.data[0] |=
+ I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED;
+ /* Field Vector 12b mask */
+ filter_replace_buf.data[2] = 0xff;
+ filter_replace_buf.data[3] = 0x0f;
+ filter_replace_buf.data[4] =
+ I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN;
+ filter_replace_buf.data[4] |=
+ I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED;
+ /* Field Vector 12b mask */
+ filter_replace_buf.data[6] = 0xff;
+ filter_replace_buf.data[7] = 0x0f;
+ ret = i40e_aq_replace_cloud_filters(hw, &filter_replace,
+ &filter_replace_buf);
+ if (ret != I40E_SUCCESS)
+ return ret;
+
+ /* Apply the second L2 cloud filter */
+ memset(&filter_replace, 0,
+ sizeof(struct i40e_aqc_replace_cloud_filters_cmd));
+ memset(&filter_replace_buf, 0,
+ sizeof(struct i40e_aqc_replace_cloud_filters_cmd_buf));
+
+ /* create L2 filter, input for L2 filter will be L1 filter */
+ filter_replace.valid_flags = I40E_AQC_REPLACE_CLOUD_FILTER;
+ filter_replace.old_filter_type = I40E_AQC_ADD_CLOUD_FILTER_OIP;
+ filter_replace.new_filter_type = I40E_AQC_ADD_CLOUD_FILTER_0X10;
+
+ /* Prepare the buffer, 2 entries */
+ filter_replace_buf.data[0] = I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG;
+ filter_replace_buf.data[0] |=
+ I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED;
+ filter_replace_buf.data[4] = I40E_AQC_ADD_CLOUD_FILTER_0X10;
+ filter_replace_buf.data[4] |=
+ I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED;
+ ret = i40e_aq_replace_cloud_filters(hw, &filter_replace,
+ &filter_replace_buf);
+ return ret;
+}
+
+RTE_INIT(i40e_init_log);
+static void
+i40e_init_log(void)
+{
+ i40e_logtype_init = rte_log_register("pmd.i40e.init");
+ if (i40e_logtype_init >= 0)
+ rte_log_set_level(i40e_logtype_init, RTE_LOG_NOTICE);
+ i40e_logtype_driver = rte_log_register("pmd.i40e.driver");
+ if (i40e_logtype_driver >= 0)
+ rte_log_set_level(i40e_logtype_driver, RTE_LOG_NOTICE);
+}