-/**
- * Convert a flow director filter to a generic flow.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param fdir_filter
- * Flow director filter to add.
- * @param attributes
- * Generic flow parameters structure.
- *
- * @return
- * 0 on success, a negative errno value otherwise and rte_errno is set.
- */
-static int
-flow_fdir_filter_convert(struct rte_eth_dev *dev,
- const struct rte_eth_fdir_filter *fdir_filter,
- struct mlx5_fdir *attributes)
-{
- struct mlx5_priv *priv = dev->data->dev_private;
- const struct rte_eth_fdir_input *input = &fdir_filter->input;
- const struct rte_eth_fdir_masks *mask =
- &dev->data->dev_conf.fdir_conf.mask;
-
- /* Validate queue number. */
- if (fdir_filter->action.rx_queue >= priv->rxqs_n) {
- DRV_LOG(ERR, "port %u invalid queue number %d",
- dev->data->port_id, fdir_filter->action.rx_queue);
- rte_errno = EINVAL;
- return -rte_errno;
- }
- attributes->attr.ingress = 1;
- attributes->items[0] = (struct rte_flow_item) {
- .type = RTE_FLOW_ITEM_TYPE_ETH,
- .spec = &attributes->l2,
- .mask = &attributes->l2_mask,
- };
- switch (fdir_filter->action.behavior) {
- case RTE_ETH_FDIR_ACCEPT:
- attributes->actions[0] = (struct rte_flow_action){
- .type = RTE_FLOW_ACTION_TYPE_QUEUE,
- .conf = &attributes->queue,
- };
- break;
- case RTE_ETH_FDIR_REJECT:
- attributes->actions[0] = (struct rte_flow_action){
- .type = RTE_FLOW_ACTION_TYPE_DROP,
- };
- break;
- default:
- DRV_LOG(ERR, "port %u invalid behavior %d",
- dev->data->port_id,
- fdir_filter->action.behavior);
- rte_errno = ENOTSUP;
- return -rte_errno;
- }
- attributes->queue.index = fdir_filter->action.rx_queue;
- /* Handle L3. */
- switch (fdir_filter->input.flow_type) {
- case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
- case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
- case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
- attributes->l3.ipv4.hdr = (struct rte_ipv4_hdr){
- .src_addr = input->flow.ip4_flow.src_ip,
- .dst_addr = input->flow.ip4_flow.dst_ip,
- .time_to_live = input->flow.ip4_flow.ttl,
- .type_of_service = input->flow.ip4_flow.tos,
- };
- attributes->l3_mask.ipv4.hdr = (struct rte_ipv4_hdr){
- .src_addr = mask->ipv4_mask.src_ip,
- .dst_addr = mask->ipv4_mask.dst_ip,
- .time_to_live = mask->ipv4_mask.ttl,
- .type_of_service = mask->ipv4_mask.tos,
- .next_proto_id = mask->ipv4_mask.proto,
- };
- attributes->items[1] = (struct rte_flow_item){
- .type = RTE_FLOW_ITEM_TYPE_IPV4,
- .spec = &attributes->l3,
- .mask = &attributes->l3_mask,
- };
- break;
- case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
- case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
- case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
- attributes->l3.ipv6.hdr = (struct rte_ipv6_hdr){
- .hop_limits = input->flow.ipv6_flow.hop_limits,
- .proto = input->flow.ipv6_flow.proto,
- };
-
- memcpy(attributes->l3.ipv6.hdr.src_addr,
- input->flow.ipv6_flow.src_ip,
- RTE_DIM(attributes->l3.ipv6.hdr.src_addr));
- memcpy(attributes->l3.ipv6.hdr.dst_addr,
- input->flow.ipv6_flow.dst_ip,
- RTE_DIM(attributes->l3.ipv6.hdr.src_addr));
- memcpy(attributes->l3_mask.ipv6.hdr.src_addr,
- mask->ipv6_mask.src_ip,
- RTE_DIM(attributes->l3_mask.ipv6.hdr.src_addr));
- memcpy(attributes->l3_mask.ipv6.hdr.dst_addr,
- mask->ipv6_mask.dst_ip,
- RTE_DIM(attributes->l3_mask.ipv6.hdr.src_addr));
- attributes->items[1] = (struct rte_flow_item){
- .type = RTE_FLOW_ITEM_TYPE_IPV6,
- .spec = &attributes->l3,
- .mask = &attributes->l3_mask,
- };
- break;
- default:
- DRV_LOG(ERR, "port %u invalid flow type%d",
- dev->data->port_id, fdir_filter->input.flow_type);
- rte_errno = ENOTSUP;
- return -rte_errno;
- }
- /* Handle L4. */
- switch (fdir_filter->input.flow_type) {
- case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
- attributes->l4.udp.hdr = (struct rte_udp_hdr){
- .src_port = input->flow.udp4_flow.src_port,
- .dst_port = input->flow.udp4_flow.dst_port,
- };
- attributes->l4_mask.udp.hdr = (struct rte_udp_hdr){
- .src_port = mask->src_port_mask,
- .dst_port = mask->dst_port_mask,
- };
- attributes->items[2] = (struct rte_flow_item){
- .type = RTE_FLOW_ITEM_TYPE_UDP,
- .spec = &attributes->l4,
- .mask = &attributes->l4_mask,
- };
- break;
- case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
- attributes->l4.tcp.hdr = (struct rte_tcp_hdr){
- .src_port = input->flow.tcp4_flow.src_port,
- .dst_port = input->flow.tcp4_flow.dst_port,
- };
- attributes->l4_mask.tcp.hdr = (struct rte_tcp_hdr){
- .src_port = mask->src_port_mask,
- .dst_port = mask->dst_port_mask,
- };
- attributes->items[2] = (struct rte_flow_item){
- .type = RTE_FLOW_ITEM_TYPE_TCP,
- .spec = &attributes->l4,
- .mask = &attributes->l4_mask,
- };
- break;
- case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
- attributes->l4.udp.hdr = (struct rte_udp_hdr){
- .src_port = input->flow.udp6_flow.src_port,
- .dst_port = input->flow.udp6_flow.dst_port,
- };
- attributes->l4_mask.udp.hdr = (struct rte_udp_hdr){
- .src_port = mask->src_port_mask,
- .dst_port = mask->dst_port_mask,
- };
- attributes->items[2] = (struct rte_flow_item){
- .type = RTE_FLOW_ITEM_TYPE_UDP,
- .spec = &attributes->l4,
- .mask = &attributes->l4_mask,
- };
- break;
- case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
- attributes->l4.tcp.hdr = (struct rte_tcp_hdr){
- .src_port = input->flow.tcp6_flow.src_port,
- .dst_port = input->flow.tcp6_flow.dst_port,
- };
- attributes->l4_mask.tcp.hdr = (struct rte_tcp_hdr){
- .src_port = mask->src_port_mask,
- .dst_port = mask->dst_port_mask,
- };
- attributes->items[2] = (struct rte_flow_item){
- .type = RTE_FLOW_ITEM_TYPE_TCP,
- .spec = &attributes->l4,
- .mask = &attributes->l4_mask,
- };
- break;
- case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
- case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
- break;
- default:
- DRV_LOG(ERR, "port %u invalid flow type%d",
- dev->data->port_id, fdir_filter->input.flow_type);
- rte_errno = ENOTSUP;
- return -rte_errno;
- }
- return 0;
-}
-
-#define FLOW_FDIR_CMP(f1, f2, fld) \
- memcmp(&(f1)->fld, &(f2)->fld, sizeof(f1->fld))
-
-/**
- * Compare two FDIR flows. If items and actions are identical, the two flows are
- * regarded as same.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param f1
- * FDIR flow to compare.
- * @param f2
- * FDIR flow to compare.
- *
- * @return
- * Zero on match, 1 otherwise.
- */
-static int
-flow_fdir_cmp(const struct mlx5_fdir *f1, const struct mlx5_fdir *f2)
-{
- if (FLOW_FDIR_CMP(f1, f2, attr) ||
- FLOW_FDIR_CMP(f1, f2, l2) ||
- FLOW_FDIR_CMP(f1, f2, l2_mask) ||
- FLOW_FDIR_CMP(f1, f2, l3) ||
- FLOW_FDIR_CMP(f1, f2, l3_mask) ||
- FLOW_FDIR_CMP(f1, f2, l4) ||
- FLOW_FDIR_CMP(f1, f2, l4_mask) ||
- FLOW_FDIR_CMP(f1, f2, actions[0].type))
- return 1;
- if (f1->actions[0].type == RTE_FLOW_ACTION_TYPE_QUEUE &&
- FLOW_FDIR_CMP(f1, f2, queue))
- return 1;
- return 0;
-}
-
-/**
- * Search device flow list to find out a matched FDIR flow.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param fdir_flow
- * FDIR flow to lookup.
- *
- * @return
- * Index of flow if found, 0 otherwise.
- */
-static uint32_t
-flow_fdir_filter_lookup(struct rte_eth_dev *dev, struct mlx5_fdir *fdir_flow)
-{
- struct mlx5_priv *priv = dev->data->dev_private;
- uint32_t flow_idx = 0;
- struct mlx5_fdir_flow *priv_fdir_flow = NULL;
-
- MLX5_ASSERT(fdir_flow);
- LIST_FOREACH(priv_fdir_flow, &priv->fdir_flows, next) {
- if (!flow_fdir_cmp(priv_fdir_flow->fdir, fdir_flow)) {
- DRV_LOG(DEBUG, "port %u found FDIR flow %u",
- dev->data->port_id, flow_idx);
- flow_idx = priv_fdir_flow->rix_flow;
- break;
- }
- }
- return flow_idx;
-}
-
-/**
- * Add new flow director filter and store it in list.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param fdir_filter
- * Flow director filter to add.
- *
- * @return
- * 0 on success, a negative errno value otherwise and rte_errno is set.
- */
-static int
-flow_fdir_filter_add(struct rte_eth_dev *dev,
- const struct rte_eth_fdir_filter *fdir_filter)
-{
- struct mlx5_priv *priv = dev->data->dev_private;
- struct mlx5_fdir *fdir_flow;
- struct rte_flow *flow;
- struct mlx5_fdir_flow *priv_fdir_flow = NULL;
- uint32_t flow_idx;
- int ret;
-
- fdir_flow = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*fdir_flow), 0,
- SOCKET_ID_ANY);
- if (!fdir_flow) {
- rte_errno = ENOMEM;
- return -rte_errno;
- }
- ret = flow_fdir_filter_convert(dev, fdir_filter, fdir_flow);
- if (ret)
- goto error;
- flow_idx = flow_fdir_filter_lookup(dev, fdir_flow);
- if (flow_idx) {
- rte_errno = EEXIST;
- goto error;
- }
- priv_fdir_flow = mlx5_malloc(MLX5_MEM_ZERO,
- sizeof(struct mlx5_fdir_flow),
- 0, SOCKET_ID_ANY);
- if (!priv_fdir_flow) {
- rte_errno = ENOMEM;
- goto error;
- }
- flow_idx = flow_list_create(dev, &priv->flows, &fdir_flow->attr,
- fdir_flow->items, fdir_flow->actions, true,
- NULL);
- flow = mlx5_ipool_get(priv->sh->ipool[MLX5_IPOOL_RTE_FLOW], flow_idx);
- if (!flow)
- goto error;
- flow->fdir = 1;
- priv_fdir_flow->fdir = fdir_flow;
- priv_fdir_flow->rix_flow = flow_idx;
- LIST_INSERT_HEAD(&priv->fdir_flows, priv_fdir_flow, next);
- DRV_LOG(DEBUG, "port %u created FDIR flow %p",
- dev->data->port_id, (void *)flow);
- return 0;
-error:
- mlx5_free(priv_fdir_flow);
- mlx5_free(fdir_flow);
- return -rte_errno;
-}
-
-/**
- * Delete specific filter.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param fdir_filter
- * Filter to be deleted.
- *
- * @return
- * 0 on success, a negative errno value otherwise and rte_errno is set.
- */
-static int
-flow_fdir_filter_delete(struct rte_eth_dev *dev,
- const struct rte_eth_fdir_filter *fdir_filter)
-{
- struct mlx5_priv *priv = dev->data->dev_private;
- uint32_t flow_idx;
- struct mlx5_fdir fdir_flow = {
- .attr.group = 0,
- };
- struct mlx5_fdir_flow *priv_fdir_flow = NULL;
- int ret;
-
- ret = flow_fdir_filter_convert(dev, fdir_filter, &fdir_flow);
- if (ret)
- return -rte_errno;
- LIST_FOREACH(priv_fdir_flow, &priv->fdir_flows, next) {
- /* Find the fdir in priv list */
- if (!flow_fdir_cmp(priv_fdir_flow->fdir, &fdir_flow))
- break;
- }
- if (!priv_fdir_flow)
- return 0;
- LIST_REMOVE(priv_fdir_flow, next);
- flow_idx = priv_fdir_flow->rix_flow;
- flow_list_destroy(dev, &priv->flows, flow_idx);
- mlx5_free(priv_fdir_flow->fdir);
- mlx5_free(priv_fdir_flow);
- DRV_LOG(DEBUG, "port %u deleted FDIR flow %u",
- dev->data->port_id, flow_idx);
- return 0;
-}
-
-/**
- * Update queue for specific filter.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param fdir_filter
- * Filter to be updated.
- *
- * @return
- * 0 on success, a negative errno value otherwise and rte_errno is set.
- */
-static int
-flow_fdir_filter_update(struct rte_eth_dev *dev,
- const struct rte_eth_fdir_filter *fdir_filter)
-{
- int ret;
-
- ret = flow_fdir_filter_delete(dev, fdir_filter);
- if (ret)
- return ret;
- return flow_fdir_filter_add(dev, fdir_filter);
-}
-
-/**
- * Flush all filters.
- *
- * @param dev
- * Pointer to Ethernet device.
- */
-static void
-flow_fdir_filter_flush(struct rte_eth_dev *dev)
-{
- struct mlx5_priv *priv = dev->data->dev_private;
- struct mlx5_fdir_flow *priv_fdir_flow = NULL;
-
- while (!LIST_EMPTY(&priv->fdir_flows)) {
- priv_fdir_flow = LIST_FIRST(&priv->fdir_flows);
- LIST_REMOVE(priv_fdir_flow, next);
- flow_list_destroy(dev, &priv->flows, priv_fdir_flow->rix_flow);
- mlx5_free(priv_fdir_flow->fdir);
- mlx5_free(priv_fdir_flow);
- }
-}
-
-/**
- * Get flow director information.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param[out] fdir_info
- * Resulting flow director information.
- */
-static void
-flow_fdir_info_get(struct rte_eth_dev *dev, struct rte_eth_fdir_info *fdir_info)
-{
- struct rte_eth_fdir_masks *mask =
- &dev->data->dev_conf.fdir_conf.mask;
-
- fdir_info->mode = dev->data->dev_conf.fdir_conf.mode;
- fdir_info->guarant_spc = 0;
- rte_memcpy(&fdir_info->mask, mask, sizeof(fdir_info->mask));
- fdir_info->max_flexpayload = 0;
- fdir_info->flow_types_mask[0] = 0;
- fdir_info->flex_payload_unit = 0;
- fdir_info->max_flex_payload_segment_num = 0;
- fdir_info->flex_payload_limit = 0;
- memset(&fdir_info->flex_conf, 0, sizeof(fdir_info->flex_conf));
-}
-
-/**
- * Deal with flow director operations.
- *
- * @param dev
- * Pointer to Ethernet device.
- * @param filter_op
- * Operation to perform.
- * @param arg
- * Pointer to operation-specific structure.
- *
- * @return
- * 0 on success, a negative errno value otherwise and rte_errno is set.
- */
-static int
-flow_fdir_ctrl_func(struct rte_eth_dev *dev, enum rte_filter_op filter_op,
- void *arg)
-{
- enum rte_fdir_mode fdir_mode =
- dev->data->dev_conf.fdir_conf.mode;
-
- if (filter_op == RTE_ETH_FILTER_NOP)
- return 0;
- if (fdir_mode != RTE_FDIR_MODE_PERFECT &&
- fdir_mode != RTE_FDIR_MODE_PERFECT_MAC_VLAN) {
- DRV_LOG(ERR, "port %u flow director mode %d not supported",
- dev->data->port_id, fdir_mode);
- rte_errno = EINVAL;
- return -rte_errno;
- }
- switch (filter_op) {
- case RTE_ETH_FILTER_ADD:
- return flow_fdir_filter_add(dev, arg);
- case RTE_ETH_FILTER_UPDATE:
- return flow_fdir_filter_update(dev, arg);
- case RTE_ETH_FILTER_DELETE:
- return flow_fdir_filter_delete(dev, arg);
- case RTE_ETH_FILTER_FLUSH:
- flow_fdir_filter_flush(dev);
- break;
- case RTE_ETH_FILTER_INFO:
- flow_fdir_info_get(dev, arg);
- break;
- default:
- DRV_LOG(DEBUG, "port %u unknown operation %u",
- dev->data->port_id, filter_op);
- rte_errno = EINVAL;
- return -rte_errno;
- }
- return 0;
-}
-