* BSD LICENSE
*
* Copyright 2017 6WIND S.A.
- * Copyright 2017 Mellanox.
+ * Copyright 2017 Mellanox
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+/**
+ * @file
+ * Flow API operations for mlx4 driver.
+ */
+
+#include <arpa/inet.h>
#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/queue.h>
+
+/* Verbs headers do not support -pedantic. */
+#ifdef PEDANTIC
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+#include <infiniband/verbs.h>
+#ifdef PEDANTIC
+#pragma GCC diagnostic error "-Wpedantic"
+#endif
+#include <rte_errno.h>
+#include <rte_eth_ctrl.h>
+#include <rte_ethdev.h>
#include <rte_flow.h>
#include <rte_flow_driver.h>
#include <rte_malloc.h>
-/* Generated configuration header. */
-#include "mlx4_autoconf.h"
-
/* PMD headers. */
#include "mlx4.h"
#include "mlx4_flow.h"
+#include "mlx4_rxtx.h"
+#include "mlx4_utils.h"
/** Static initializer for items. */
#define ITEMS(...) \
const enum rte_flow_item_type *const items;
};
+struct rte_flow_drop {
+ struct ibv_qp *qp; /**< Verbs queue pair. */
+ struct ibv_cq *cq; /**< Verbs completion queue. */
+};
+
/** Valid action for this PMD. */
static const enum rte_flow_action_type valid_actions[] = {
RTE_FLOW_ACTION_TYPE_DROP,
};
/**
- * Validate a flow supported by the NIC.
+ * Make sure a flow rule is supported and initialize associated structure.
*
* @param priv
* Pointer to private structure.
* 0 on success, a negative errno value otherwise and rte_errno is set.
*/
static int
-priv_flow_validate(struct priv *priv,
- const struct rte_flow_attr *attr,
- const struct rte_flow_item items[],
- const struct rte_flow_action actions[],
- struct rte_flow_error *error,
- struct mlx4_flow *flow)
+mlx4_flow_prepare(struct priv *priv,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item items[],
+ const struct rte_flow_action actions[],
+ struct rte_flow_error *error,
+ struct mlx4_flow *flow)
{
const struct mlx4_flow_items *cur_item = mlx4_flow_items;
struct mlx4_flow_action action = {
(const struct rte_flow_action_queue *)
actions->conf;
- if (!queue || (queue->index > (priv->rxqs_n - 1)))
+ if (!queue || (queue->index >
+ (priv->dev->data->nb_rx_queues - 1)))
goto exit_action_not_supported;
action.queue = 1;
} else {
* @see rte_flow_validate()
* @see rte_flow_ops
*/
-int
+static int
mlx4_flow_validate(struct rte_eth_dev *dev,
const struct rte_flow_attr *attr,
const struct rte_flow_item items[],
struct rte_flow_error *error)
{
struct priv *priv = dev->data->dev_private;
- int ret;
struct mlx4_flow flow = { .offset = sizeof(struct ibv_flow_attr) };
- priv_lock(priv);
- ret = priv_flow_validate(priv, attr, items, actions, error, &flow);
- priv_unlock(priv);
- return ret;
+ return mlx4_flow_prepare(priv, attr, items, actions, error, &flow);
+}
+
+/**
+ * Destroy a drop queue.
+ *
+ * @param priv
+ * Pointer to private structure.
+ */
+static void
+mlx4_flow_destroy_drop_queue(struct priv *priv)
+{
+ if (priv->flow_drop_queue) {
+ struct rte_flow_drop *fdq = priv->flow_drop_queue;
+
+ priv->flow_drop_queue = NULL;
+ claim_zero(ibv_destroy_qp(fdq->qp));
+ claim_zero(ibv_destroy_cq(fdq->cq));
+ rte_free(fdq);
+ }
+}
+
+/**
+ * Create a single drop queue for all drop flows.
+ *
+ * @param priv
+ * Pointer to private structure.
+ *
+ * @return
+ * 0 on success, negative value otherwise.
+ */
+static int
+mlx4_flow_create_drop_queue(struct priv *priv)
+{
+ struct ibv_qp *qp;
+ struct ibv_cq *cq;
+ struct rte_flow_drop *fdq;
+
+ fdq = rte_calloc(__func__, 1, sizeof(*fdq), 0);
+ if (!fdq) {
+ ERROR("Cannot allocate memory for drop struct");
+ goto err;
+ }
+ cq = ibv_create_cq(priv->ctx, 1, NULL, NULL, 0);
+ if (!cq) {
+ ERROR("Cannot create drop CQ");
+ goto err_create_cq;
+ }
+ qp = ibv_create_qp(priv->pd,
+ &(struct ibv_qp_init_attr){
+ .send_cq = cq,
+ .recv_cq = cq,
+ .cap = {
+ .max_recv_wr = 1,
+ .max_recv_sge = 1,
+ },
+ .qp_type = IBV_QPT_RAW_PACKET,
+ });
+ if (!qp) {
+ ERROR("Cannot create drop QP");
+ goto err_create_qp;
+ }
+ *fdq = (struct rte_flow_drop){
+ .qp = qp,
+ .cq = cq,
+ };
+ priv->flow_drop_queue = fdq;
+ return 0;
+err_create_qp:
+ claim_zero(ibv_destroy_cq(cq));
+err_create_cq:
+ rte_free(fdq);
+err:
+ return -1;
}
/**
* A flow if the rule could be created.
*/
static struct rte_flow *
-priv_flow_create_action_queue(struct priv *priv,
+mlx4_flow_create_action_queue(struct priv *priv,
struct ibv_flow_attr *ibv_attr,
struct mlx4_flow_action *action,
struct rte_flow_error *error)
{
- struct rxq *rxq;
struct ibv_qp *qp;
struct rte_flow *rte_flow;
NULL, "cannot allocate flow memory");
return NULL;
}
- rxq = (*priv->rxqs)[action->queue_id];
if (action->drop) {
- rte_flow->cq =
- ibv_exp_create_cq(priv->ctx, 1, NULL, NULL, 0,
- &(struct ibv_exp_cq_init_attr){
- .comp_mask = 0,
- });
- if (!rte_flow->cq) {
- rte_flow_error_set(error, ENOMEM,
- RTE_FLOW_ERROR_TYPE_HANDLE,
- NULL, "cannot allocate CQ");
- goto error;
- }
- rte_flow->qp = ibv_exp_create_qp(
- priv->ctx,
- &(struct ibv_exp_qp_init_attr){
- .send_cq = rte_flow->cq,
- .recv_cq = rte_flow->cq,
- .cap = {
- .max_recv_wr = 1,
- .max_recv_sge = 1,
- },
- .qp_type = IBV_QPT_RAW_PACKET,
- .comp_mask =
- IBV_EXP_QP_INIT_ATTR_PD |
- IBV_EXP_QP_INIT_ATTR_PORT |
- IBV_EXP_QP_INIT_ATTR_RES_DOMAIN,
- .pd = priv->pd,
- .res_domain = rxq->rd,
- .port_num = priv->port,
- });
- if (!rte_flow->qp) {
- rte_flow_error_set(error, ENOMEM,
- RTE_FLOW_ERROR_TYPE_HANDLE,
- NULL, "cannot allocate QP");
- goto error;
- }
- qp = rte_flow->qp;
+ qp = priv->flow_drop_queue ? priv->flow_drop_queue->qp : NULL;
} else {
- rte_flow->rxq = rxq;
+ struct rxq *rxq = priv->dev->data->rx_queues[action->queue_id];
+
qp = rxq->qp;
+ rte_flow->qp = qp;
}
rte_flow->ibv_attr = ibv_attr;
+ if (!priv->started)
+ return rte_flow;
rte_flow->ibv_flow = ibv_create_flow(qp, rte_flow->ibv_attr);
if (!rte_flow->ibv_flow) {
rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
goto error;
}
return rte_flow;
-
error:
- assert(rte_flow);
- if (rte_flow->cq)
- ibv_destroy_cq(rte_flow->cq);
- if (rte_flow->qp)
- ibv_destroy_qp(rte_flow->qp);
- rte_free(rte_flow->ibv_attr);
rte_free(rte_flow);
return NULL;
}
/**
- * Convert a flow.
- *
- * @param priv
- * Pointer to private structure.
- * @param[in] attr
- * Flow rule attributes.
- * @param[in] items
- * Pattern specification (list terminated by the END pattern item).
- * @param[in] actions
- * Associated actions (list terminated by the END action).
- * @param[out] error
- * Perform verbose error reporting if not NULL.
+ * Create a flow.
*
- * @return
- * A flow on success, NULL otherwise.
+ * @see rte_flow_create()
+ * @see rte_flow_ops
*/
static struct rte_flow *
-priv_flow_create(struct priv *priv,
+mlx4_flow_create(struct rte_eth_dev *dev,
const struct rte_flow_attr *attr,
const struct rte_flow_item items[],
const struct rte_flow_action actions[],
struct rte_flow_error *error)
{
+ struct priv *priv = dev->data->dev_private;
struct rte_flow *rte_flow;
struct mlx4_flow_action action;
struct mlx4_flow flow = { .offset = sizeof(struct ibv_flow_attr), };
int err;
- err = priv_flow_validate(priv, attr, items, actions, error, &flow);
+ err = mlx4_flow_prepare(priv, attr, items, actions, error, &flow);
if (err)
return NULL;
flow.ibv_attr = rte_malloc(__func__, flow.offset, 0);
.port = priv->port,
.flags = 0,
};
- claim_zero(priv_flow_validate(priv, attr, items, actions,
- error, &flow));
+ claim_zero(mlx4_flow_prepare(priv, attr, items, actions,
+ error, &flow));
action = (struct mlx4_flow_action){
.queue = 0,
.drop = 0,
goto exit;
}
}
- rte_flow = priv_flow_create_action_queue(priv, flow.ibv_attr,
+ rte_flow = mlx4_flow_create_action_queue(priv, flow.ibv_attr,
&action, error);
- return rte_flow;
+ if (rte_flow) {
+ LIST_INSERT_HEAD(&priv->flows, rte_flow, next);
+ DEBUG("Flow created %p", (void *)rte_flow);
+ return rte_flow;
+ }
exit:
rte_free(flow.ibv_attr);
return NULL;
}
/**
- * Create a flow.
+ * Configure isolated mode.
*
- * @see rte_flow_create()
+ * @see rte_flow_isolate()
* @see rte_flow_ops
*/
-struct rte_flow *
-mlx4_flow_create(struct rte_eth_dev *dev,
- const struct rte_flow_attr *attr,
- const struct rte_flow_item items[],
- const struct rte_flow_action actions[],
- struct rte_flow_error *error)
+static int
+mlx4_flow_isolate(struct rte_eth_dev *dev,
+ int enable,
+ struct rte_flow_error *error)
{
struct priv *priv = dev->data->dev_private;
- struct rte_flow *flow;
- priv_lock(priv);
- flow = priv_flow_create(priv, attr, items, actions, error);
- if (flow) {
- LIST_INSERT_HEAD(&priv->flows, flow, next);
- DEBUG("Flow created %p", (void *)flow);
+ if (!!enable == !!priv->isolated)
+ return 0;
+ priv->isolated = !!enable;
+ if (enable) {
+ mlx4_mac_addr_del(priv);
+ } else if (mlx4_mac_addr_add(priv) < 0) {
+ priv->isolated = 1;
+ return rte_flow_error_set(error, rte_errno,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+ NULL, "cannot leave isolated mode");
}
- priv_unlock(priv);
- return flow;
-}
-
-/**
- * Destroy a flow.
- *
- * @param priv
- * Pointer to private structure.
- * @param[in] flow
- * Flow to destroy.
- */
-static void
-priv_flow_destroy(struct priv *priv, struct rte_flow *flow)
-{
- (void)priv;
- LIST_REMOVE(flow, next);
- if (flow->ibv_flow)
- claim_zero(ibv_destroy_flow(flow->ibv_flow));
- if (flow->qp)
- claim_zero(ibv_destroy_qp(flow->qp));
- if (flow->cq)
- claim_zero(ibv_destroy_cq(flow->cq));
- rte_free(flow->ibv_attr);
- DEBUG("Flow destroyed %p", (void *)flow);
- rte_free(flow);
+ return 0;
}
/**
* @see rte_flow_destroy()
* @see rte_flow_ops
*/
-int
+static int
mlx4_flow_destroy(struct rte_eth_dev *dev,
struct rte_flow *flow,
struct rte_flow_error *error)
{
- struct priv *priv = dev->data->dev_private;
-
+ (void)dev;
(void)error;
- priv_lock(priv);
- priv_flow_destroy(priv, flow);
- priv_unlock(priv);
+ LIST_REMOVE(flow, next);
+ if (flow->ibv_flow)
+ claim_zero(ibv_destroy_flow(flow->ibv_flow));
+ rte_free(flow->ibv_attr);
+ DEBUG("Flow destroyed %p", (void *)flow);
+ rte_free(flow);
return 0;
}
-/**
- * Destroy all flows.
- *
- * @param priv
- * Pointer to private structure.
- */
-static void
-priv_flow_flush(struct priv *priv)
-{
- while (!LIST_EMPTY(&priv->flows)) {
- struct rte_flow *flow;
-
- flow = LIST_FIRST(&priv->flows);
- priv_flow_destroy(priv, flow);
- }
-}
-
/**
* Destroy all flows.
*
* @see rte_flow_flush()
* @see rte_flow_ops
*/
-int
+static int
mlx4_flow_flush(struct rte_eth_dev *dev,
struct rte_flow_error *error)
{
struct priv *priv = dev->data->dev_private;
- (void)error;
- priv_lock(priv);
- priv_flow_flush(priv);
- priv_unlock(priv);
+ while (!LIST_EMPTY(&priv->flows)) {
+ struct rte_flow *flow;
+
+ flow = LIST_FIRST(&priv->flows);
+ mlx4_flow_destroy(dev, flow, error);
+ }
return 0;
}
* Pointer to private structure.
*/
void
-mlx4_priv_flow_stop(struct priv *priv)
+mlx4_flow_stop(struct priv *priv)
{
struct rte_flow *flow;
flow->ibv_flow = NULL;
DEBUG("Flow %p removed", (void *)flow);
}
+ mlx4_flow_destroy_drop_queue(priv);
}
/**
* 0 on success, a errno value otherwise and rte_errno is set.
*/
int
-mlx4_priv_flow_start(struct priv *priv)
+mlx4_flow_start(struct priv *priv)
{
+ int ret;
struct ibv_qp *qp;
struct rte_flow *flow;
+ ret = mlx4_flow_create_drop_queue(priv);
+ if (ret)
+ return -1;
for (flow = LIST_FIRST(&priv->flows);
flow;
flow = LIST_NEXT(flow, next)) {
- qp = flow->qp ? flow->qp : flow->rxq->qp;
+ qp = flow->qp ? flow->qp : priv->flow_drop_queue->qp;
flow->ibv_flow = ibv_create_flow(qp, flow->ibv_attr);
if (!flow->ibv_flow) {
DEBUG("Flow %p cannot be applied", (void *)flow);
}
return 0;
}
+
+static const struct rte_flow_ops mlx4_flow_ops = {
+ .validate = mlx4_flow_validate,
+ .create = mlx4_flow_create,
+ .destroy = mlx4_flow_destroy,
+ .flush = mlx4_flow_flush,
+ .isolate = mlx4_flow_isolate,
+};
+
+/**
+ * Manage filter operations.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ * @param filter_type
+ * Filter type.
+ * @param filter_op
+ * Operation to perform.
+ * @param arg
+ * Pointer to operation-specific structure.
+ *
+ * @return
+ * 0 on success, negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx4_filter_ctrl(struct rte_eth_dev *dev,
+ enum rte_filter_type filter_type,
+ enum rte_filter_op filter_op,
+ void *arg)
+{
+ switch (filter_type) {
+ case RTE_ETH_FILTER_GENERIC:
+ if (filter_op != RTE_ETH_FILTER_GET)
+ break;
+ *(const void **)arg = &mlx4_flow_ops;
+ return 0;
+ default:
+ ERROR("%p: filter type (%d) not supported",
+ (void *)dev, filter_type);
+ break;
+ }
+ rte_errno = ENOTSUP;
+ return -rte_errno;
+}