#include <rte_malloc.h>
#include "mlx5.h"
+#include "mlx5_prm.h"
static int
mlx5_flow_create_eth(const struct rte_flow_item *item,
struct ibv_exp_wq *wq; /**< Verbs work queue. */
struct ibv_cq *cq; /**< Verbs completion queue. */
struct rxq *rxq; /**< Pointer to the queue, NULL if drop queue. */
+ uint32_t mark:1; /**< Set if the flow is marked. */
};
/** Static initializer for items. */
const enum rte_flow_action_type *const actions;
/** Bit-masks corresponding to the possibilities for the item. */
const void *mask;
+ /**
+ * Default bit-masks to use when item->mask is not provided. When
+ * \default_mask is also NULL, the full supported bit-mask (\mask) is
+ * used instead.
+ */
+ const void *default_mask;
/** Bit-masks size in bytes. */
const unsigned int mask_sz;
/**
static const enum rte_flow_action_type valid_actions[] = {
RTE_FLOW_ACTION_TYPE_DROP,
RTE_FLOW_ACTION_TYPE_QUEUE,
+ RTE_FLOW_ACTION_TYPE_MARK,
RTE_FLOW_ACTION_TYPE_END,
};
.mask = &(const struct rte_flow_item_eth){
.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
.src.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+ .type = -1,
},
+ .default_mask = &rte_flow_item_eth_mask,
.mask_sz = sizeof(struct rte_flow_item_eth),
.convert = mlx5_flow_create_eth,
.dst_sz = sizeof(struct ibv_exp_flow_spec_eth),
.mask = &(const struct rte_flow_item_vlan){
.tci = -1,
},
+ .default_mask = &rte_flow_item_vlan_mask,
.mask_sz = sizeof(struct rte_flow_item_vlan),
.convert = mlx5_flow_create_vlan,
.dst_sz = 0,
.hdr = {
.src_addr = -1,
.dst_addr = -1,
+ .type_of_service = -1,
+ .next_proto_id = -1,
},
},
+ .default_mask = &rte_flow_item_ipv4_mask,
.mask_sz = sizeof(struct rte_flow_item_ipv4),
.convert = mlx5_flow_create_ipv4,
- .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv4),
+ .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv4_ext),
},
[RTE_FLOW_ITEM_TYPE_IPV6] = {
.items = ITEMS(RTE_FLOW_ITEM_TYPE_UDP,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
},
+ .vtc_flow = -1,
+ .proto = -1,
+ .hop_limits = -1,
},
},
+ .default_mask = &rte_flow_item_ipv6_mask,
.mask_sz = sizeof(struct rte_flow_item_ipv6),
.convert = mlx5_flow_create_ipv6,
- .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv6),
+ .dst_sz = sizeof(struct ibv_exp_flow_spec_ipv6_ext),
},
[RTE_FLOW_ITEM_TYPE_UDP] = {
.items = ITEMS(RTE_FLOW_ITEM_TYPE_VXLAN),
.dst_port = -1,
},
},
+ .default_mask = &rte_flow_item_udp_mask,
.mask_sz = sizeof(struct rte_flow_item_udp),
.convert = mlx5_flow_create_udp,
.dst_sz = sizeof(struct ibv_exp_flow_spec_tcp_udp),
.dst_port = -1,
},
},
+ .default_mask = &rte_flow_item_tcp_mask,
.mask_sz = sizeof(struct rte_flow_item_tcp),
.convert = mlx5_flow_create_tcp,
.dst_sz = sizeof(struct ibv_exp_flow_spec_tcp_udp),
.mask = &(const struct rte_flow_item_vxlan){
.vni = "\xff\xff\xff",
},
+ .default_mask = &rte_flow_item_vxlan_mask,
.mask_sz = sizeof(struct rte_flow_item_vxlan),
.convert = mlx5_flow_create_vxlan,
.dst_sz = sizeof(struct ibv_exp_flow_spec_tunnel),
struct mlx5_flow_action {
uint32_t queue:1; /**< Target is a receive queue. */
uint32_t drop:1; /**< Target is a drop queue. */
+ uint32_t mark:1; /**< Mark is present in the flow. */
uint32_t queue_id; /**< Identifier of the queue. */
+ uint32_t mark_id; /**< Mark identifier. */
};
/**
struct mlx5_flow_action action = {
.queue = 0,
.drop = 0,
+ .mark = 0,
};
(void)priv;
if (items->type == RTE_FLOW_ITEM_TYPE_VOID)
continue;
- /* Handle special situation for VLAN. */
- if (items->type == RTE_FLOW_ITEM_TYPE_VLAN) {
- if (((const struct rte_flow_item_vlan *)items)->tci >
- ETHER_MAX_VLAN_ID) {
- rte_flow_error_set(error, ENOTSUP,
- RTE_FLOW_ERROR_TYPE_ITEM,
- items,
- "wrong VLAN id value");
- return -rte_errno;
- }
- }
for (i = 0;
cur_item->items &&
cur_item->items[i] != RTE_FLOW_ITEM_TYPE_END;
cur_item = token;
err = mlx5_flow_item_validate(items,
(const uint8_t *)cur_item->mask,
- sizeof(cur_item->mask_sz));
+ cur_item->mask_sz);
if (err)
goto exit_item_not_supported;
if (flow->ibv_attr && cur_item->convert) {
- err = cur_item->convert(items, cur_item->mask, flow);
+ err = cur_item->convert(items,
+ (cur_item->default_mask ?
+ cur_item->default_mask :
+ cur_item->mask),
+ flow);
if (err)
goto exit_item_not_supported;
}
if (!queue || (queue->index > (priv->rxqs_n - 1)))
goto exit_action_not_supported;
action.queue = 1;
+ } else if (actions->type == RTE_FLOW_ACTION_TYPE_MARK) {
+ const struct rte_flow_action_mark *mark =
+ (const struct rte_flow_action_mark *)
+ actions->conf;
+
+ if (!mark) {
+ rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ actions,
+ "mark must be defined");
+ return -rte_errno;
+ } else if (mark->id >= MLX5_FLOW_MARK_MAX) {
+ rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ actions,
+ "mark must be between 0"
+ " and 16777199");
+ return -rte_errno;
+ }
+ action.mark = 1;
} else {
goto exit_action_not_supported;
}
}
+ if (action.mark && !flow->ibv_attr && !action.drop)
+ flow->offset += sizeof(struct ibv_exp_flow_spec_action_tag);
if (!action.queue && !action.drop) {
rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
NULL, "no valid action");
mask = default_mask;
memcpy(eth->val.dst_mac, spec->dst.addr_bytes, ETHER_ADDR_LEN);
memcpy(eth->val.src_mac, spec->src.addr_bytes, ETHER_ADDR_LEN);
+ eth->val.ether_type = spec->type;
memcpy(eth->mask.dst_mac, mask->dst.addr_bytes, ETHER_ADDR_LEN);
memcpy(eth->mask.src_mac, mask->src.addr_bytes, ETHER_ADDR_LEN);
+ eth->mask.ether_type = mask->type;
/* Remove unwanted bits from values. */
for (i = 0; i < ETHER_ADDR_LEN; ++i) {
eth->val.dst_mac[i] &= eth->mask.dst_mac[i];
eth->val.src_mac[i] &= eth->mask.src_mac[i];
}
+ eth->val.ether_type &= eth->mask.ether_type;
return 0;
}
const struct rte_flow_item_ipv4 *spec = item->spec;
const struct rte_flow_item_ipv4 *mask = item->mask;
struct mlx5_flow *flow = (struct mlx5_flow *)data;
- struct ibv_exp_flow_spec_ipv4 *ipv4;
- unsigned int ipv4_size = sizeof(struct ibv_exp_flow_spec_ipv4);
+ struct ibv_exp_flow_spec_ipv4_ext *ipv4;
+ unsigned int ipv4_size = sizeof(struct ibv_exp_flow_spec_ipv4_ext);
++flow->ibv_attr->num_of_specs;
flow->ibv_attr->priority = 1;
ipv4 = (void *)((uintptr_t)flow->ibv_attr + flow->offset);
- *ipv4 = (struct ibv_exp_flow_spec_ipv4) {
- .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV4,
+ *ipv4 = (struct ibv_exp_flow_spec_ipv4_ext) {
+ .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV4_EXT,
.size = ipv4_size,
};
if (!spec)
return 0;
if (!mask)
mask = default_mask;
- ipv4->val = (struct ibv_exp_flow_ipv4_filter){
+ ipv4->val = (struct ibv_exp_flow_ipv4_ext_filter){
.src_ip = spec->hdr.src_addr,
.dst_ip = spec->hdr.dst_addr,
+ .proto = spec->hdr.next_proto_id,
+ .tos = spec->hdr.type_of_service,
};
- ipv4->mask = (struct ibv_exp_flow_ipv4_filter){
+ ipv4->mask = (struct ibv_exp_flow_ipv4_ext_filter){
.src_ip = mask->hdr.src_addr,
.dst_ip = mask->hdr.dst_addr,
+ .proto = mask->hdr.next_proto_id,
+ .tos = mask->hdr.type_of_service,
};
/* Remove unwanted bits from values. */
ipv4->val.src_ip &= ipv4->mask.src_ip;
ipv4->val.dst_ip &= ipv4->mask.dst_ip;
+ ipv4->val.proto &= ipv4->mask.proto;
+ ipv4->val.tos &= ipv4->mask.tos;
return 0;
}
const struct rte_flow_item_ipv6 *spec = item->spec;
const struct rte_flow_item_ipv6 *mask = item->mask;
struct mlx5_flow *flow = (struct mlx5_flow *)data;
- struct ibv_exp_flow_spec_ipv6 *ipv6;
- unsigned int ipv6_size = sizeof(struct ibv_exp_flow_spec_ipv6);
- unsigned int i;
+ struct ibv_exp_flow_spec_ipv6_ext *ipv6;
+ unsigned int ipv6_size = sizeof(struct ibv_exp_flow_spec_ipv6_ext);
++flow->ibv_attr->num_of_specs;
flow->ibv_attr->priority = 1;
ipv6 = (void *)((uintptr_t)flow->ibv_attr + flow->offset);
- *ipv6 = (struct ibv_exp_flow_spec_ipv6) {
- .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV6,
+ *ipv6 = (struct ibv_exp_flow_spec_ipv6_ext) {
+ .type = flow->inner | IBV_EXP_FLOW_SPEC_IPV6_EXT,
.size = ipv6_size,
};
if (!spec)
RTE_DIM(ipv6->mask.src_ip));
memcpy(ipv6->mask.dst_ip, mask->hdr.dst_addr,
RTE_DIM(ipv6->mask.dst_ip));
- /* Remove unwanted bits from values. */
- for (i = 0; i < RTE_DIM(ipv6->val.src_ip); ++i) {
- ipv6->val.src_ip[i] &= ipv6->mask.src_ip[i];
- ipv6->val.dst_ip[i] &= ipv6->mask.dst_ip[i];
- }
+ ipv6->mask.flow_label = mask->hdr.vtc_flow;
+ ipv6->mask.next_hdr = mask->hdr.proto;
+ ipv6->mask.hop_limit = mask->hdr.hop_limits;
+ ipv6->val.flow_label &= ipv6->mask.flow_label;
+ ipv6->val.next_hdr &= ipv6->mask.next_hdr;
+ ipv6->val.hop_limit &= ipv6->mask.hop_limit;
return 0;
}
return 0;
}
+/**
+ * Convert mark/flag action to Verbs specification.
+ *
+ * @param flow
+ * Pointer to MLX5 flow structure.
+ * @param mark_id
+ * Mark identifier.
+ */
+static int
+mlx5_flow_create_flag_mark(struct mlx5_flow *flow, uint32_t mark_id)
+{
+ struct ibv_exp_flow_spec_action_tag *tag;
+ unsigned int size = sizeof(struct ibv_exp_flow_spec_action_tag);
+
+ tag = (void *)((uintptr_t)flow->ibv_attr + flow->offset);
+ *tag = (struct ibv_exp_flow_spec_action_tag){
+ .type = IBV_EXP_FLOW_SPEC_ACTION_TAG,
+ .size = size,
+ .tag_id = mlx5_flow_mark_set(mark_id),
+ };
+ ++flow->ibv_attr->num_of_specs;
+ return 0;
+}
+
/**
* Complete flow rule creation.
*
.pd = priv->pd,
.cq = rte_flow->cq,
});
+ if (!rte_flow->wq) {
+ rte_flow_error_set(error, ENOMEM,
+ RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "cannot allocate WQ");
+ goto error;
+ }
} else {
rxq = container_of((*priv->rxqs)[action->queue_id],
struct rxq_ctrl, rxq);
rte_flow->rxq = &rxq->rxq;
+ rxq->rxq.mark |= action->mark;
rte_flow->wq = rxq->wq;
}
+ rte_flow->mark = action->mark;
rte_flow->ibv_attr = ibv_attr;
rte_flow->ind_table = ibv_exp_create_rwq_ind_table(
priv->ctx,
NULL, "cannot allocate QP");
goto error;
}
+ if (!priv->started)
+ return rte_flow;
rte_flow->ibv_flow = ibv_exp_create_flow(rte_flow->qp,
rte_flow->ibv_attr);
if (!rte_flow->ibv_flow) {
ibv_exp_destroy_wq(rte_flow->wq);
if (!rte_flow->rxq && rte_flow->cq)
ibv_destroy_cq(rte_flow->cq);
- rte_free(rte_flow->ibv_attr);
rte_free(rte_flow);
return NULL;
}
action = (struct mlx5_flow_action){
.queue = 0,
.drop = 0,
+ .mark = 0,
+ .mark_id = MLX5_FLOW_MARK_DEFAULT,
};
for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
if (actions->type == RTE_FLOW_ACTION_TYPE_VOID) {
actions->conf)->index;
} else if (actions->type == RTE_FLOW_ACTION_TYPE_DROP) {
action.drop = 1;
+ action.mark = 0;
+ } else if (actions->type == RTE_FLOW_ACTION_TYPE_MARK) {
+ const struct rte_flow_action_mark *mark =
+ (const struct rte_flow_action_mark *)
+ actions->conf;
+
+ if (mark)
+ action.mark_id = mark->id;
+ action.mark = !action.drop;
} else {
rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
goto exit;
}
}
+ if (action.mark) {
+ mlx5_flow_create_flag_mark(&flow, action.mark_id);
+ flow.offset += sizeof(struct ibv_exp_flow_spec_action_tag);
+ }
rte_flow = priv_flow_create_action_queue(priv, flow.ibv_attr,
&action, error);
+ if (!rte_flow)
+ goto exit;
return rte_flow;
exit:
rte_free(flow.ibv_attr);
claim_zero(ibv_exp_destroy_wq(flow->wq));
if (!flow->rxq && flow->cq)
claim_zero(ibv_destroy_cq(flow->cq));
+ if (flow->mark) {
+ struct rte_flow *tmp;
+ uint32_t mark_n = 0;
+
+ for (tmp = LIST_FIRST(&priv->flows);
+ tmp;
+ tmp = LIST_NEXT(tmp, next)) {
+ if ((flow->rxq == tmp->rxq) && tmp->mark)
+ ++mark_n;
+ }
+ flow->rxq->mark = !!mark_n;
+ }
rte_free(flow->ibv_attr);
DEBUG("Flow destroyed %p", (void *)flow);
rte_free(flow);
flow = LIST_NEXT(flow, next)) {
claim_zero(ibv_exp_destroy_flow(flow->ibv_flow));
flow->ibv_flow = NULL;
+ if (flow->mark)
+ flow->rxq->mark = 0;
DEBUG("Flow %p removed", (void *)flow);
}
}
return rte_errno;
}
DEBUG("Flow %p applied", (void *)flow);
+ if (flow->rxq)
+ flow->rxq->mark |= flow->mark;
}
return 0;
}