#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. */
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,
};
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 (!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 && (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)
+ 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");
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.
*
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,
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;
+ } 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 = 1;
} 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);
return rte_flow;
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;
}
#ifndef RTE_PMD_MLX5_PRM_H_
#define RTE_PMD_MLX5_PRM_H_
+#include <assert.h>
+
/* Verbs header. */
/* ISO C doesn't support unnamed structs/unions, disabling -pedantic. */
#ifdef PEDANTIC
/* Outer UDP header and checksum OK. */
#define MLX5_CQE_RX_OUTER_TCP_UDP_CSUM_OK (1u << 6)
+/* INVALID is used by packets matching no flow rules. */
+#define MLX5_FLOW_MARK_INVALID 0
+
+/* Maximum allowed value to mark a packet. */
+#define MLX5_FLOW_MARK_MAX 0xfffff0
+
+/* Default mark value used when none is provided. */
+#define MLX5_FLOW_MARK_DEFAULT 0xffffff
+
/* Subset of struct mlx5_wqe_eth_seg. */
struct mlx5_wqe_eth_seg_small {
uint32_t rsvd0;
uint8_t rsvd2[12];
uint32_t byte_cnt;
uint64_t timestamp;
- uint8_t rsvd3[4];
+ uint32_t sop_drop_qpn;
uint16_t wqe_counter;
uint8_t rsvd4;
uint8_t op_own;
};
+/**
+ * Convert a user mark to flow mark.
+ *
+ * @param val
+ * Mark value to convert.
+ *
+ * @return
+ * Converted mark value.
+ */
+static inline uint32_t
+mlx5_flow_mark_set(uint32_t val)
+{
+ uint32_t ret;
+
+ /*
+ * Add one to the user value to differentiate un-marked flows from
+ * marked flows.
+ */
+ ++val;
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+ /*
+ * Mark is 24 bits (minus reserved values) but is stored on a 32 bit
+ * word, byte-swapped by the kernel on little-endian systems. In this
+ * case, left-shifting the resulting big-endian value ensures the
+ * least significant 24 bits are retained when converting it back.
+ */
+ ret = rte_cpu_to_be_32(val) >> 8;
+#else
+ ret = val;
+#endif
+ assert(ret <= MLX5_FLOW_MARK_MAX);
+ return ret;
+}
+
+/**
+ * Convert a mark to user mark.
+ *
+ * @param val
+ * Mark value to convert.
+ *
+ * @return
+ * Converted mark value.
+ */
+static inline uint32_t
+mlx5_flow_mark_get(uint32_t val)
+{
+ /*
+ * Subtract one from the retrieved value. It was added by
+ * mlx5_flow_mark_set() to distinguish unmarked flows.
+ */
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+ return (val >> 8) - 1;
+#else
+ return val - 1;
+#endif
+}
+
#endif /* RTE_PMD_MLX5_PRM_H_ */