@@ -186,6 +186,12 @@ 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,
@@ -4145,20 +4151,148 @@ 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_NOP) {
+ PMD_DRV_LOG(ERR, "arg shouldn't be NULL for operation %u\n",
+ filter_op);
+ return -EINVAL;
+ }
+
+ switch (filter_op) {
+ case RTE_ETH_FILTER_NOP:
+ ret = 0;
+ break;
+ case RTE_ETH_FILTER_ADD:
+ ret = i40e_ctrl_pkt_filter_set(pf,
+ (struct rte_ctrl_pkt_filter *)arg,
+ TRUE);
+ break;
+ case RTE_ETH_FILTER_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;
+}
+
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 = 0;
- (void)filter_op;
- (void)arg;
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(WARNING, "Filter type (%d) not supported",
filter_type);