@@ -205,6 +205,15 @@ static int i40e_dev_rss_hash_update(struct rte_eth_dev *dev,
struct rte_eth_rss_conf *rss_conf);
static int i40e_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
struct rte_eth_rss_conf *rss_conf);
+static int i40e_ctrl_pkt_filter_set(struct i40e_pf *pf,
+ struct rte_ctrl_pkt_filter *cp_filter,
+ bool add);
+static int i40e_ctrl_pkt_filter_handle(struct i40e_pf *pf,
+ enum rte_filter_op filter_op,
+ void *arg);
+static int i40e_dev_filter_ctrl(struct rte_eth_dev *dev,
+ enum rte_filter_type filter_type,
+ enum rte_filter_op filter_op, void *arg);
/* Default hash key buffer for RSS */
static uint32_t rss_key_default[I40E_PFQF_HKEY_MAX_INDEX + 1];
@@ -256,6 +265,7 @@ static struct eth_dev_ops i40e_eth_dev_ops = {
.reta_query = i40e_dev_rss_reta_query,
.rss_hash_update = i40e_dev_rss_hash_update,
.rss_hash_conf_get = i40e_dev_rss_hash_conf_get,
+ .filter_ctrl = i40e_dev_filter_ctrl,
};
static struct eth_driver rte_i40e_pmd = {
@@ -4131,3 +4141,154 @@ i40e_pf_config_mq_rx(struct i40e_pf *pf)
return 0;
}
+
+/* Look up vsi by vsi_id */
+static struct i40e_vsi *
+i40e_vsi_lookup_by_id(struct i40e_vsi *uplink_vsi, uint16_t id)
+{
+ struct i40e_vsi *vsi = NULL;
+ struct i40e_vsi_list *vsi_list;
+
+ if (uplink_vsi == NULL)
+ return NULL;
+
+ if (uplink_vsi->vsi_id == id)
+ return vsi;
+
+ /* if VSI has child to attach*/
+ if (uplink_vsi->veb) {
+ TAILQ_FOREACH(vsi_list, &uplink_vsi->veb->head, list) {
+ vsi = i40e_vsi_lookup_by_id(vsi_list->vsi, id);
+ if (vsi)
+ return vsi;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Configure control packet filter, which can director packet by filtering
+ * with mac address and ether_type or only ether_type
+ */
+static int
+i40e_ctrl_pkt_filter_set(struct i40e_pf *pf,
+ struct rte_ctrl_pkt_filter *cp_filter,
+ bool add)
+{
+ struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+ struct i40e_control_filter_stats stats;
+ struct i40e_vsi *vsi = NULL;
+ uint16_t seid;
+ uint16_t flags = 0;
+ int ret;
+
+ if (cp_filter->ether_type == ETHER_TYPE_IPv4 ||
+ cp_filter->ether_type == ETHER_TYPE_IPv6) {
+ PMD_DRV_LOG(ERR, "unsupported ether_type(0x%04x) in"
+ " control packet filter.", cp_filter->ether_type);
+ return -EINVAL;
+ }
+ if (cp_filter->ether_type == ETHER_TYPE_VLAN)
+ PMD_DRV_LOG(WARNING, "filter vlan ether_type in first tag is"
+ " not supported.");
+
+ if (cp_filter->dest_id == 0)
+ /* Use LAN VSI Id if not programmed by user */
+ vsi = pf->main_vsi;
+ else {
+ vsi = i40e_vsi_lookup_by_id(pf->main_vsi, cp_filter->dest_id);
+ if (vsi == NULL || vsi->type == I40E_VSI_FDIR) {
+ PMD_DRV_LOG(ERR, "VSI arg is invalid\n");
+ return -EINVAL;
+ }
+ }
+
+ seid = vsi->seid;
+ memset(&stats, 0, sizeof(stats));
+
+ if (cp_filter->flags & RTE_CONTROL_PACKET_FLAGS_TX)
+ flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TX;
+ if (cp_filter->flags & RTE_CONTROL_PACKET_FLAGS_IGNORE_MAC)
+ flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC;
+ if (cp_filter->flags & RTE_CONTROL_PACKET_FLAGS_TO_QUEUE)
+ flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TO_QUEUE;
+ if (cp_filter->flags & RTE_CONTROL_PACKET_FLAGS_DROP)
+ flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP;
+
+ ret = i40e_aq_add_rem_control_packet_filter(hw,
+ cp_filter->mac_addr.addr_bytes,
+ cp_filter->ether_type, flags,
+ seid, cp_filter->queue, add, &stats, NULL);
+
+ PMD_DRV_LOG(INFO, "add/rem control packet filter, return %d,"
+ " mac_etype_used = %u, etype_used = %u,"
+ " mac_etype_free = %u, etype_free = %u\n",
+ ret, stats.mac_etype_used, stats.etype_used,
+ stats.mac_etype_free, stats.etype_free);
+ if (ret < 0)
+ return -ENOSYS;
+ return 0;
+}
+
+/*
+ * Handle operations for control packte filter type.
+ */
+static int
+i40e_ctrl_pkt_filter_handle(struct i40e_pf *pf,
+ enum rte_filter_op filter_op,
+ void *arg)
+{
+ int ret = 0;
+
+ if (arg == NULL && filter_op != RTE_ETH_FILTER_OP_NONE) {
+ PMD_DRV_LOG(ERR, "arg shouldn't be NULL for operation %u\n",
+ filter_op);
+ return -EINVAL;
+ }
+
+ switch (filter_op) {
+ case RTE_ETH_FILTER_OP_NONE:
+ ret = 0;
+ break;
+ case RTE_ETH_FILTER_OP_ADD:
+ ret = i40e_ctrl_pkt_filter_set(pf,
+ (struct rte_ctrl_pkt_filter *)arg,
+ TRUE);
+ break;
+ case RTE_ETH_FILTER_OP_DELETE:
+ ret = i40e_ctrl_pkt_filter_set(pf,
+ (struct rte_ctrl_pkt_filter *)arg,
+ FALSE);
+ break;
+ default:
+ PMD_DRV_LOG(ERR, "unsupported operation %u\n", filter_op);
+ ret = -ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Take operations to assigned filter type on NIC.
+ */
+static int
+i40e_dev_filter_ctrl(struct rte_eth_dev *dev, enum rte_filter_type filter_type,
+ enum rte_filter_op filter_op, void *arg)
+{
+ struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+ int ret = I40E_SUCCESS;
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ switch (filter_type) {
+ case RTE_ETH_FILTER_CTRL_PKT:
+ ret = i40e_ctrl_pkt_filter_handle(pf, filter_op, arg);
+ break;
+ default:
+ PMD_DRV_LOG(ERR, "unsupported filter type %u.", filter_type);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}