net/mlx5: add VXLAN encap action to Direct Verbs

This patch implements the VXLAN encap action in DV flow for MLX5 PMD.

Signed-off-by: Dekel Peled <dekelp@mellanox.com>
Acked-by: Shahaf Shuler <shahafs@mellanox.com>
This commit is contained in:
Dekel Peled 2018-11-01 11:37:28 +02:00 committed by Ferruh Yigit
parent 56ef2c5815
commit 34d41b7aa3
2 changed files with 347 additions and 5 deletions

@ -92,6 +92,7 @@
#define MLX5_FLOW_ACTION_DEC_TTL (1u << 19)
#define MLX5_FLOW_ACTION_SET_MAC_SRC (1u << 20)
#define MLX5_FLOW_ACTION_SET_MAC_DST (1u << 21)
#define MLX5_FLOW_ACTION_VXLAN_ENCAP (1u << 22)
#define MLX5_FLOW_FATE_ACTIONS \
(MLX5_FLOW_ACTION_DROP | MLX5_FLOW_ACTION_QUEUE | MLX5_FLOW_ACTION_RSS)
@ -181,6 +182,8 @@ struct mlx5_flow_dv {
#ifdef HAVE_IBV_FLOW_DV_SUPPORT
struct mlx5dv_flow_action_attr actions[MLX5_DV_MAX_NUMBER_OF_ACTIONS];
/**< Action list. */
struct ibv_flow_action *encap_decap_verbs_action;
/**< Verbs encap/decap object. */
#endif
int actions_n; /**< number of actions. */
};

@ -34,6 +34,8 @@
#ifdef HAVE_IBV_FLOW_DV_SUPPORT
#define MLX5_ENCAP_MAX_LEN 132
/**
* Validate META item.
*
@ -95,6 +97,305 @@ flow_dv_validate_item_meta(struct rte_eth_dev *dev,
return 0;
}
/**
* Validate the L2 encap action.
*
* @param[in] action_flags
* Holds the actions detected until now.
* @param[in] action
* Pointer to the encap action.
* @param[in] attr
* Pointer to flow attributes
* @param[out] error
* Pointer to error structure.
*
* @return
* 0 on success, a negative errno value otherwise and rte_errno is set.
*/
static int
flow_dv_validate_action_l2_encap(uint64_t action_flags,
const struct rte_flow_action *action,
const struct rte_flow_attr *attr,
struct rte_flow_error *error)
{
if (!(action->conf))
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION, action,
"configuration cannot be null");
if (action_flags & MLX5_FLOW_ACTION_DROP)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
"can't drop and encap in same flow");
if (action_flags & MLX5_FLOW_ACTION_VXLAN_ENCAP)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION, NULL,
"can only have a single encap"
" action in a flow");
if (attr->ingress)
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
NULL,
"encap action not supported for "
"ingress");
return 0;
}
/**
* Get the size of specific rte_flow_item_type
*
* @param[in] item_type
* Tested rte_flow_item_type.
*
* @return
* sizeof struct item_type, 0 if void or irrelevant.
*/
static size_t
flow_dv_get_item_len(const enum rte_flow_item_type item_type)
{
size_t retval;
switch (item_type) {
case RTE_FLOW_ITEM_TYPE_ETH:
retval = sizeof(struct rte_flow_item_eth);
break;
case RTE_FLOW_ITEM_TYPE_VLAN:
retval = sizeof(struct rte_flow_item_vlan);
break;
case RTE_FLOW_ITEM_TYPE_IPV4:
retval = sizeof(struct rte_flow_item_ipv4);
break;
case RTE_FLOW_ITEM_TYPE_IPV6:
retval = sizeof(struct rte_flow_item_ipv6);
break;
case RTE_FLOW_ITEM_TYPE_UDP:
retval = sizeof(struct rte_flow_item_udp);
break;
case RTE_FLOW_ITEM_TYPE_TCP:
retval = sizeof(struct rte_flow_item_tcp);
break;
case RTE_FLOW_ITEM_TYPE_VXLAN:
retval = sizeof(struct rte_flow_item_vxlan);
break;
case RTE_FLOW_ITEM_TYPE_GRE:
retval = sizeof(struct rte_flow_item_gre);
break;
case RTE_FLOW_ITEM_TYPE_NVGRE:
retval = sizeof(struct rte_flow_item_nvgre);
break;
case RTE_FLOW_ITEM_TYPE_VXLAN_GPE:
retval = sizeof(struct rte_flow_item_vxlan_gpe);
break;
case RTE_FLOW_ITEM_TYPE_MPLS:
retval = sizeof(struct rte_flow_item_mpls);
break;
case RTE_FLOW_ITEM_TYPE_VOID: /* Fall through. */
default:
retval = 0;
break;
}
return retval;
}
#define MLX5_ENCAP_IPV4_VERSION 0x40
#define MLX5_ENCAP_IPV4_IHL_MIN 0x05
#define MLX5_ENCAP_IPV4_TTL_DEF 0x40
#define MLX5_ENCAP_IPV6_VTC_FLOW 0x60000000
#define MLX5_ENCAP_IPV6_HOP_LIMIT 0xff
#define MLX5_ENCAP_VXLAN_FLAGS 0x08000000
#define MLX5_ENCAP_VXLAN_GPE_FLAGS 0x04
/**
* Convert the encap action data from list of rte_flow_item to raw buffer
*
* @param[in] items
* Pointer to rte_flow_item objects list.
* @param[out] buf
* Pointer to the output buffer.
* @param[out] size
* Pointer to the output buffer size.
* @param[out] error
* Pointer to the error structure.
*
* @return
* 0 on success, a negative errno value otherwise and rte_errno is set.
*/
static int
flow_dv_convert_encap_data(const struct rte_flow_item *items, uint8_t *buf,
size_t *size, struct rte_flow_error *error)
{
struct ether_hdr *eth = NULL;
struct vlan_hdr *vlan = NULL;
struct ipv4_hdr *ipv4 = NULL;
struct ipv6_hdr *ipv6 = NULL;
struct udp_hdr *udp = NULL;
struct vxlan_hdr *vxlan = NULL;
struct vxlan_gpe_hdr *vxlan_gpe = NULL;
size_t len;
size_t temp_size = 0;
if (!items)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
NULL, "invalid empty data");
for (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) {
len = flow_dv_get_item_len(items->type);
if (len + temp_size > MLX5_ENCAP_MAX_LEN)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"items total size is too big"
" for encap action");
rte_memcpy((void *)&buf[temp_size], items->spec, len);
switch (items->type) {
case RTE_FLOW_ITEM_TYPE_ETH:
eth = (struct ether_hdr *)&buf[temp_size];
break;
case RTE_FLOW_ITEM_TYPE_VLAN:
vlan = (struct vlan_hdr *)&buf[temp_size];
if (!eth)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"eth header not found");
if (!eth->ether_type)
eth->ether_type = RTE_BE16(ETHER_TYPE_VLAN);
break;
case RTE_FLOW_ITEM_TYPE_IPV4:
ipv4 = (struct ipv4_hdr *)&buf[temp_size];
if (!vlan && !eth)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"neither eth nor vlan"
" header found");
if (vlan && !vlan->eth_proto)
vlan->eth_proto = RTE_BE16(ETHER_TYPE_IPv4);
else if (eth && !eth->ether_type)
eth->ether_type = RTE_BE16(ETHER_TYPE_IPv4);
if (!ipv4->version_ihl)
ipv4->version_ihl = MLX5_ENCAP_IPV4_VERSION |
MLX5_ENCAP_IPV4_IHL_MIN;
if (!ipv4->time_to_live)
ipv4->time_to_live = MLX5_ENCAP_IPV4_TTL_DEF;
break;
case RTE_FLOW_ITEM_TYPE_IPV6:
ipv6 = (struct ipv6_hdr *)&buf[temp_size];
if (!vlan && !eth)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"neither eth nor vlan"
" header found");
if (vlan && !vlan->eth_proto)
vlan->eth_proto = RTE_BE16(ETHER_TYPE_IPv6);
else if (eth && !eth->ether_type)
eth->ether_type = RTE_BE16(ETHER_TYPE_IPv6);
if (!ipv6->vtc_flow)
ipv6->vtc_flow =
RTE_BE32(MLX5_ENCAP_IPV6_VTC_FLOW);
if (!ipv6->hop_limits)
ipv6->hop_limits = MLX5_ENCAP_IPV6_HOP_LIMIT;
break;
case RTE_FLOW_ITEM_TYPE_UDP:
udp = (struct udp_hdr *)&buf[temp_size];
if (!ipv4 && !ipv6)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"ip header not found");
if (ipv4 && !ipv4->next_proto_id)
ipv4->next_proto_id = IPPROTO_UDP;
else if (ipv6 && !ipv6->proto)
ipv6->proto = IPPROTO_UDP;
break;
case RTE_FLOW_ITEM_TYPE_VXLAN:
vxlan = (struct vxlan_hdr *)&buf[temp_size];
if (!udp)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"udp header not found");
if (!udp->dst_port)
udp->dst_port = RTE_BE16(MLX5_UDP_PORT_VXLAN);
if (!vxlan->vx_flags)
vxlan->vx_flags =
RTE_BE32(MLX5_ENCAP_VXLAN_FLAGS);
break;
case RTE_FLOW_ITEM_TYPE_VXLAN_GPE:
vxlan_gpe = (struct vxlan_gpe_hdr *)&buf[temp_size];
if (!udp)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"udp header not found");
if (!vxlan_gpe->proto)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"next protocol not found");
if (!udp->dst_port)
udp->dst_port =
RTE_BE16(MLX5_UDP_PORT_VXLAN_GPE);
if (!vxlan_gpe->vx_flags)
vxlan_gpe->vx_flags =
MLX5_ENCAP_VXLAN_GPE_FLAGS;
break;
case RTE_FLOW_ITEM_TYPE_VOID:
break;
default:
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
(void *)items->type,
"unsupported item type");
break;
}
temp_size += len;
}
*size = temp_size;
return 0;
}
/**
* Convert L2 encap action to DV specification.
*
* @param[in] dev
* Pointer to rte_eth_dev structure.
* @param[in] action
* Pointer to action structure.
* @param[out] error
* Pointer to the error structure.
*
* @return
* Pointer to action on success, NULL otherwise and rte_errno is set.
*/
static struct ibv_flow_action *
flow_dv_create_action_l2_encap(struct rte_eth_dev *dev,
const struct rte_flow_action *action,
struct rte_flow_error *error)
{
struct ibv_flow_action *verbs_action = NULL;
const struct rte_flow_item *encap_data;
struct priv *priv = dev->data->dev_private;
uint8_t buf[MLX5_ENCAP_MAX_LEN];
size_t size = 0;
int convert_result = 0;
encap_data = ((const struct rte_flow_action_vxlan_encap *)
action->conf)->definition;
convert_result = flow_dv_convert_encap_data(encap_data, buf,
&size, error);
if (convert_result)
return NULL;
verbs_action = mlx5_glue->dv_create_flow_action_packet_reformat
(priv->ctx, size, buf,
MLX5DV_FLOW_ACTION_PACKET_REFORMAT_TYPE_L2_TO_L2_TUNNEL,
MLX5DV_FLOW_TABLE_TYPE_NIC_TX);
if (!verbs_action)
rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION,
NULL, "cannot create L2 encap action");
return verbs_action;
}
/**
* Verify the @p attributes will be correctly understood by the NIC and store
* them in the @p flow if everything is correct.
@ -339,6 +640,16 @@ flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
action_flags |= MLX5_FLOW_ACTION_COUNT;
++actions_n;
break;
case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:
ret = flow_dv_validate_action_l2_encap(action_flags,
actions, attr,
error);
if (ret < 0)
return ret;
action_flags |= MLX5_FLOW_ACTION_VXLAN_ENCAP;
++actions_n;
break;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
@ -1045,14 +1356,23 @@ flow_dv_create_item(void *matcher, void *key,
/**
* Store the requested actions in an array.
*
* @param[in] dev
* Pointer to rte_eth_dev structure.
* @param[in] action
* Flow action to translate.
* @param[in, out] dev_flow
* Pointer to the mlx5_flow.
* @param[out] error
* Pointer to the error structure.
*
* @return
* 0 on success, a negative errno value otherwise and rte_errno is set.
*/
static void
flow_dv_create_action(const struct rte_flow_action *action,
struct mlx5_flow *dev_flow)
static int
flow_dv_create_action(struct rte_eth_dev *dev,
const struct rte_flow_action *action,
struct mlx5_flow *dev_flow,
struct rte_flow_error *error)
{
const struct rte_flow_action_queue *queue;
const struct rte_flow_action_rss *rss;
@ -1100,10 +1420,24 @@ flow_dv_create_action(const struct rte_flow_action *action,
/* Added to array only in apply since we need the QP */
flow->actions |= MLX5_FLOW_ACTION_RSS;
break;
case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:
dev_flow->dv.actions[actions_n].type =
MLX5DV_FLOW_ACTION_IBV_FLOW_ACTION;
dev_flow->dv.actions[actions_n].action =
flow_dv_create_action_l2_encap(dev, action,
error);
if (!(dev_flow->dv.actions[actions_n].action))
return -rte_errno;
dev_flow->dv.encap_decap_verbs_action =
dev_flow->dv.actions[actions_n].action;
flow->actions |= MLX5_FLOW_ACTION_VXLAN_ENCAP;
actions_n++;
break;
default:
break;
}
dev_flow->dv.actions_n = actions_n;
return 0;
}
static uint32_t matcher_zero[MLX5_ST_SZ_DW(fte_match_param)] = { 0 };
@ -1219,7 +1553,6 @@ flow_dv_matcher_register(struct rte_eth_dev *dev,
return 0;
}
/**
* Fill the flow with DV spec.
*
@ -1274,7 +1607,8 @@ flow_dv_translate(struct rte_eth_dev *dev,
if (flow_dv_matcher_register(dev, &matcher, dev_flow, error))
return -rte_errno;
for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++)
flow_dv_create_action(actions, dev_flow);
if (flow_dv_create_action(dev, actions, dev_flow, error))
return -rte_errno;
return 0;
}
@ -1459,6 +1793,11 @@ flow_dv_destroy(struct rte_eth_dev *dev, struct rte_flow *flow)
LIST_REMOVE(dev_flow, next);
if (dev_flow->dv.matcher)
flow_dv_matcher_release(dev, dev_flow);
if (dev_flow->dv.encap_decap_verbs_action) {
claim_zero(mlx5_glue->destroy_flow_action
(dev_flow->dv.encap_decap_verbs_action));
dev_flow->dv.encap_decap_verbs_action = NULL;
}
rte_free(dev_flow);
}
}