Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3078f5f1 authored by Guy Levi's avatar Guy Levi Committed by Doug Ledford
Browse files

IB/mlx4: Add support for RSS QP



Add support to work with a RSS QP by using an indirection table object
upon QP creation. Other related QP verbs (e.g. modify/destroy/query) were
updated as well for that QP mode.

Notes:
- The RX hash properties are supplied as driver private data.
- The RSS QP port is used on the associated WQs in its indirection
  table. Applying different ports during WQ life time is not allowed.
- The expected RSS QP flow is: create, modify(RST->INIT),
  modify(RST->RTR), destroy.

Signed-off-by: default avatarGuy Levi <guyle@mellanox.com>
Reviewed-by: default avatarYishai Hadas <yishaih@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leon@kernel.org>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent b8d46ca0
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@

#include <linux/mlx4/device.h>
#include <linux/mlx4/doorbell.h>
#include <linux/mlx4/qp.h>

#define MLX4_IB_DRV_NAME	"mlx4_ib"

@@ -299,6 +300,12 @@ struct mlx4_wqn_range {
	struct list_head	list;
};

struct mlx4_ib_rss {
	unsigned int		base_qpn_tbl_sz;
	u8			flags;
	u8			rss_key[MLX4_EN_RSS_KEY_SIZE];
};

struct mlx4_ib_qp {
	union {
		struct ib_qp	ibqp;
@@ -345,6 +352,7 @@ struct mlx4_ib_qp {
	struct mlx4_wqn_range	*wqn_range;
	/* Number of RSS QP parents that uses this WQ */
	u32			rss_usecnt;
	struct mlx4_ib_rss	*rss_ctx;
};

struct mlx4_ib_srq {
+431 −22
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq,
			     struct mlx4_ib_cq *recv_cq);
static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq,
			       struct mlx4_ib_cq *recv_cq);
static int _mlx4_ib_modify_wq(struct ib_wq *ibwq, enum ib_wq_state new_state);

enum {
	MLX4_IB_ACK_REQ_FREQ	= 8,
@@ -650,6 +651,212 @@ static void mlx4_ib_free_qp_counter(struct mlx4_ib_dev *dev,
	qp->counter_index = NULL;
}

static int set_qp_rss(struct mlx4_ib_dev *dev, struct mlx4_ib_rss *rss_ctx,
		      struct ib_qp_init_attr *init_attr,
		      struct mlx4_ib_create_qp_rss *ucmd)
{
	rss_ctx->base_qpn_tbl_sz = init_attr->rwq_ind_tbl->ind_tbl[0]->wq_num |
		(init_attr->rwq_ind_tbl->log_ind_tbl_size << 24);

	if ((ucmd->rx_hash_function == MLX4_IB_RX_HASH_FUNC_TOEPLITZ) &&
	    (dev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS_TOP)) {
		memcpy(rss_ctx->rss_key, ucmd->rx_hash_key,
		       MLX4_EN_RSS_KEY_SIZE);
	} else {
		pr_debug("RX Hash function is not supported\n");
		return (-EOPNOTSUPP);
	}

	if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_IPV4) &&
	    (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_IPV4)) {
		rss_ctx->flags = MLX4_RSS_IPV4;
	} else if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_IPV4) ||
		   (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_IPV4)) {
		pr_debug("RX Hash fields_mask is not supported - both IPv4 SRC and DST must be set\n");
		return (-EOPNOTSUPP);
	}

	if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_IPV6) &&
	    (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_IPV6)) {
		rss_ctx->flags |= MLX4_RSS_IPV6;
	} else if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_IPV6) ||
		   (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_IPV6)) {
		pr_debug("RX Hash fields_mask is not supported - both IPv6 SRC and DST must be set\n");
		return (-EOPNOTSUPP);
	}

	if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_PORT_UDP) &&
	    (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_PORT_UDP)) {
		if (!(dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UDP_RSS)) {
			pr_debug("RX Hash fields_mask for UDP is not supported\n");
			return (-EOPNOTSUPP);
		}

		if (rss_ctx->flags & MLX4_RSS_IPV4) {
			rss_ctx->flags |= MLX4_RSS_UDP_IPV4;
		} else if (rss_ctx->flags & MLX4_RSS_IPV6) {
			rss_ctx->flags |= MLX4_RSS_UDP_IPV6;
		} else {
			pr_debug("RX Hash fields_mask is not supported - UDP must be set with IPv4 or IPv6\n");
			return (-EOPNOTSUPP);
		}
	} else if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_PORT_UDP) ||
		   (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_PORT_UDP)) {
		pr_debug("RX Hash fields_mask is not supported - both UDP SRC and DST must be set\n");
		return (-EOPNOTSUPP);
	}

	if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_PORT_TCP) &&
	    (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_PORT_TCP)) {
		if (rss_ctx->flags & MLX4_RSS_IPV4) {
			rss_ctx->flags |= MLX4_RSS_TCP_IPV4;
		} else if (rss_ctx->flags & MLX4_RSS_IPV6) {
			rss_ctx->flags |= MLX4_RSS_TCP_IPV6;
		} else {
			pr_debug("RX Hash fields_mask is not supported - TCP must be set with IPv4 or IPv6\n");
			return (-EOPNOTSUPP);
		}

	} else if ((ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_SRC_PORT_TCP) ||
		   (ucmd->rx_hash_fields_mask & MLX4_IB_RX_HASH_DST_PORT_TCP)) {
		pr_debug("RX Hash fields_mask is not supported - both TCP SRC and DST must be set\n");
		return (-EOPNOTSUPP);
	}

	return 0;
}

static int create_qp_rss(struct mlx4_ib_dev *dev, struct ib_pd *ibpd,
			 struct ib_qp_init_attr *init_attr,
			 struct mlx4_ib_create_qp_rss *ucmd,
			 struct mlx4_ib_qp *qp)
{
	int qpn;
	int err;

	qp->mqp.usage = MLX4_RES_USAGE_USER_VERBS;

	err = mlx4_qp_reserve_range(dev->dev, 1, 1, &qpn, 0, qp->mqp.usage);
	if (err)
		return err;

	err = mlx4_qp_alloc(dev->dev, qpn, &qp->mqp);
	if (err)
		goto err_qpn;

	mutex_init(&qp->mutex);

	INIT_LIST_HEAD(&qp->gid_list);
	INIT_LIST_HEAD(&qp->steering_rules);

	qp->mlx4_ib_qp_type = MLX4_IB_QPT_RAW_ETHERTYPE;
	qp->state = IB_QPS_RESET;

	/* Set dummy send resources to be compatible with HV and PRM */
	qp->sq_no_prefetch = 1;
	qp->sq.wqe_cnt = 1;
	qp->sq.wqe_shift = MLX4_IB_MIN_SQ_STRIDE;
	qp->buf_size = qp->sq.wqe_cnt << MLX4_IB_MIN_SQ_STRIDE;
	qp->mtt = (to_mqp(
		   (struct ib_qp *)init_attr->rwq_ind_tbl->ind_tbl[0]))->mtt;

	qp->rss_ctx = kzalloc(sizeof(*qp->rss_ctx), GFP_KERNEL);
	if (!qp->rss_ctx) {
		err = -ENOMEM;
		goto err_qp_alloc;
	}

	err = set_qp_rss(dev, qp->rss_ctx, init_attr, ucmd);
	if (err)
		goto err;

	return 0;

err:
	kfree(qp->rss_ctx);

err_qp_alloc:
	mlx4_qp_remove(dev->dev, &qp->mqp);
	mlx4_qp_free(dev->dev, &qp->mqp);

err_qpn:
	mlx4_qp_release_range(dev->dev, qpn, 1);
	return err;
}

static struct ib_qp *_mlx4_ib_create_qp_rss(struct ib_pd *pd,
					    struct ib_qp_init_attr *init_attr,
					    struct ib_udata *udata)
{
	struct mlx4_ib_qp *qp;
	struct mlx4_ib_create_qp_rss ucmd = {};
	size_t required_cmd_sz;
	int err;

	if (!udata) {
		pr_debug("RSS QP with NULL udata\n");
		return ERR_PTR(-EINVAL);
	}

	if (udata->outlen)
		return ERR_PTR(-EOPNOTSUPP);

	required_cmd_sz = offsetof(typeof(ucmd), reserved1) +
					sizeof(ucmd.reserved1);
	if (udata->inlen < required_cmd_sz) {
		pr_debug("invalid inlen\n");
		return ERR_PTR(-EINVAL);
	}

	if (ib_copy_from_udata(&ucmd, udata, min(sizeof(ucmd), udata->inlen))) {
		pr_debug("copy failed\n");
		return ERR_PTR(-EFAULT);
	}

	if (ucmd.comp_mask || ucmd.reserved1)
		return ERR_PTR(-EOPNOTSUPP);

	if (udata->inlen > sizeof(ucmd) &&
	    !ib_is_udata_cleared(udata, sizeof(ucmd),
				 udata->inlen - sizeof(ucmd))) {
		pr_debug("inlen is not supported\n");
		return ERR_PTR(-EOPNOTSUPP);
	}

	if (init_attr->qp_type != IB_QPT_RAW_PACKET) {
		pr_debug("RSS QP with unsupported QP type %d\n",
			 init_attr->qp_type);
		return ERR_PTR(-EOPNOTSUPP);
	}

	if (init_attr->create_flags) {
		pr_debug("RSS QP doesn't support create flags\n");
		return ERR_PTR(-EOPNOTSUPP);
	}

	if (init_attr->send_cq || init_attr->cap.max_send_wr) {
		pr_debug("RSS QP with unsupported send attributes\n");
		return ERR_PTR(-EOPNOTSUPP);
	}

	qp = kzalloc(sizeof(*qp), GFP_KERNEL);
	if (!qp)
		return ERR_PTR(-ENOMEM);

	qp->pri.vid = 0xFFFF;
	qp->alt.vid = 0xFFFF;

	err = create_qp_rss(to_mdev(pd->device), pd, init_attr, &ucmd, qp);
	if (err) {
		kfree(qp);
		return ERR_PTR(err);
	}

	qp->ibqp.qp_num = qp->mqp.qpn;

	return &qp->ibqp;
}

/*
 * This function allocates a WQN from a range which is consecutive and aligned
 * to its size. In case the range is full, then it creates a new range and
@@ -1186,6 +1393,36 @@ static void get_cqs(struct mlx4_ib_qp *qp, enum mlx4_ib_source_type src,
	}
}

static void destroy_qp_rss(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp)
{
	if (qp->state != IB_QPS_RESET) {
		int i;

		for (i = 0; i < (1 << qp->ibqp.rwq_ind_tbl->log_ind_tbl_size);
		     i++) {
			struct ib_wq *ibwq = qp->ibqp.rwq_ind_tbl->ind_tbl[i];
			struct mlx4_ib_qp *wq =	to_mqp((struct ib_qp *)ibwq);

			mutex_lock(&wq->mutex);

			wq->rss_usecnt--;

			mutex_unlock(&wq->mutex);
		}

		if (mlx4_qp_modify(dev->dev, NULL, to_mlx4_state(qp->state),
				   MLX4_QP_STATE_RST, NULL, 0, 0, &qp->mqp))
			pr_warn("modify QP %06x to RESET failed.\n",
				qp->mqp.qpn);
	}

	mlx4_qp_remove(dev->dev, &qp->mqp);
	mlx4_qp_free(dev->dev, &qp->mqp);
	mlx4_qp_release_range(dev->dev, qp->mqp.qpn, 1);
	del_gid_entries(qp);
	kfree(qp->rss_ctx);
}

static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
			      enum mlx4_ib_source_type src, int is_user)
{
@@ -1303,6 +1540,9 @@ static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd,
	int sup_u_create_flags = MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK;
	u16 xrcdn = 0;

	if (init_attr->rwq_ind_tbl)
		return _mlx4_ib_create_qp_rss(pd, init_attr, udata);

	/*
	 * We only support LSO, vendor flag1, and multicast loopback blocking,
	 * and only for kernel UD QPs.
@@ -1444,7 +1684,6 @@ static int _mlx4_ib_destroy_qp(struct ib_qp *qp)
{
	struct mlx4_ib_dev *dev = to_mdev(qp->device);
	struct mlx4_ib_qp *mqp = to_mqp(qp);
	struct mlx4_ib_pd *pd;

	if (is_qp0(dev, mqp))
		mlx4_CLOSE_PORT(dev->dev, mqp->port);
@@ -1459,8 +1698,14 @@ static int _mlx4_ib_destroy_qp(struct ib_qp *qp)
	if (mqp->counter_index)
		mlx4_ib_free_qp_counter(dev, mqp);

	if (qp->rwq_ind_tbl) {
		destroy_qp_rss(dev, mqp);
	} else {
		struct mlx4_ib_pd *pd;

		pd = get_pd(mqp);
		destroy_qp_common(dev, mqp, MLX4_IB_QP_SRC, !!pd->ibpd.uobject);
	}

	if (is_sqp(dev, mqp))
		kfree(to_msqp(mqp));
@@ -1783,12 +2028,116 @@ static u8 gid_type_to_qpc(enum ib_gid_type gid_type)
	}
}

/*
 * Go over all RSS QP's childes (WQs) and apply their HW state according to
 * their logic state if the RSS QP is the first RSS QP associated for the WQ.
 */
static int bringup_rss_rwqs(struct ib_rwq_ind_table *ind_tbl, u8 port_num)
{
	int i;
	int err;

	for (i = 0; i < (1 << ind_tbl->log_ind_tbl_size); i++) {
		struct ib_wq *ibwq = ind_tbl->ind_tbl[i];
		struct mlx4_ib_qp *wq = to_mqp((struct ib_qp *)ibwq);

		mutex_lock(&wq->mutex);

		/* Mlx4_ib restrictions:
		 * WQ's is associated to a port according to the RSS QP it is
		 * associates to.
		 * In case the WQ is associated to a different port by another
		 * RSS QP, return a failure.
		 */
		if ((wq->rss_usecnt > 0) && (wq->port != port_num)) {
			err = -EINVAL;
			mutex_unlock(&wq->mutex);
			break;
		}
		wq->port = port_num;
		if ((wq->rss_usecnt == 0) && (ibwq->state == IB_WQS_RDY)) {
			err = _mlx4_ib_modify_wq(ibwq, IB_WQS_RDY);
			if (err) {
				mutex_unlock(&wq->mutex);
				break;
			}
		}
		wq->rss_usecnt++;

		mutex_unlock(&wq->mutex);
	}

	if (i && err) {
		int j;

		for (j = (i - 1); j >= 0; j--) {
			struct ib_wq *ibwq = ind_tbl->ind_tbl[j];
			struct mlx4_ib_qp *wq = to_mqp((struct ib_qp *)ibwq);

			mutex_lock(&wq->mutex);

			if ((wq->rss_usecnt == 1) &&
			    (ibwq->state == IB_WQS_RDY))
				if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET))
					pr_warn("failed to reverse WQN=0x%06x\n",
						ibwq->wq_num);
			wq->rss_usecnt--;

			mutex_unlock(&wq->mutex);
		}
	}

	return err;
}

static void bring_down_rss_rwqs(struct ib_rwq_ind_table *ind_tbl)
{
	int i;

	for (i = 0; i < (1 << ind_tbl->log_ind_tbl_size); i++) {
		struct ib_wq *ibwq = ind_tbl->ind_tbl[i];
		struct mlx4_ib_qp *wq = to_mqp((struct ib_qp *)ibwq);

		mutex_lock(&wq->mutex);

		if ((wq->rss_usecnt == 1) && (ibwq->state == IB_WQS_RDY))
			if (_mlx4_ib_modify_wq(ibwq, IB_WQS_RESET))
				pr_warn("failed to reverse WQN=%x\n",
					ibwq->wq_num);
		wq->rss_usecnt--;

		mutex_unlock(&wq->mutex);
	}
}

static void fill_qp_rss_context(struct mlx4_qp_context *context,
				struct mlx4_ib_qp *qp)
{
	struct mlx4_rss_context *rss_context;

	rss_context = (void *)context + offsetof(struct mlx4_qp_context,
			pri_path) + MLX4_RSS_OFFSET_IN_QPC_PRI_PATH;

	rss_context->base_qpn = cpu_to_be32(qp->rss_ctx->base_qpn_tbl_sz);
	rss_context->default_qpn =
		cpu_to_be32(qp->rss_ctx->base_qpn_tbl_sz & 0xffffff);
	if (qp->rss_ctx->flags & (MLX4_RSS_UDP_IPV4 | MLX4_RSS_UDP_IPV6))
		rss_context->base_qpn_udp = rss_context->default_qpn;
	rss_context->flags = qp->rss_ctx->flags;
	/* Currently support just toeplitz */
	rss_context->hash_fn = MLX4_RSS_HASH_TOP;

	memcpy(rss_context->rss_key, qp->rss_ctx->rss_key,
	       MLX4_EN_RSS_KEY_SIZE);
}

static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
			       const struct ib_qp_attr *attr, int attr_mask,
			       enum ib_qp_state cur_state, enum ib_qp_state new_state)
{
	struct ib_uobject *ibuobject;
	struct ib_srq  *ibsrq;
	struct ib_rwq_ind_table *rwq_ind_tbl;
	enum ib_qp_type qp_type;
	struct mlx4_ib_dev *dev;
	struct mlx4_ib_qp *qp;
@@ -1807,6 +2156,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
		ibwq	    = (struct ib_wq *)src;
		ibuobject   = ibwq->uobject;
		ibsrq	    = NULL;
		rwq_ind_tbl = NULL;
		qp_type     = IB_QPT_RAW_PACKET;
		qp	    = to_mqp((struct ib_qp *)ibwq);
		dev	    = to_mdev(ibwq->device);
@@ -1817,6 +2167,7 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
		ibqp	    = (struct ib_qp *)src;
		ibuobject   = ibqp->uobject;
		ibsrq	    = ibqp->srq;
		rwq_ind_tbl = ibqp->rwq_ind_tbl;
		qp_type     = ibqp->qp_type;
		qp	    = to_mqp(ibqp);
		dev	    = to_mdev(ibqp->device);
@@ -1836,6 +2187,11 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
	context->flags = cpu_to_be32((to_mlx4_state(new_state) << 28) |
				     (to_mlx4_st(dev, qp->mlx4_ib_qp_type) << 16));

	if (rwq_ind_tbl) {
		fill_qp_rss_context(context, qp);
		context->flags |= cpu_to_be32(1 << MLX4_RSS_QPC_FLAG_OFFSET);
	}

	if (!(attr_mask & IB_QP_PATH_MIG_STATE))
		context->flags |= cpu_to_be32(MLX4_QP_PM_MIGRATED << 11);
	else {
@@ -1876,9 +2232,11 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
			ilog2(dev->dev->caps.max_msg_sz);
	}

	if (!rwq_ind_tbl) { /* PRM RSS receive side should be left zeros */
		if (qp->rq.wqe_cnt)
			context->rq_size_stride = ilog2(qp->rq.wqe_cnt) << 3;
		context->rq_size_stride |= qp->rq.wqe_shift - 4;
	}

	if (qp->sq.wqe_cnt)
		context->sq_size_stride = ilog2(qp->sq.wqe_cnt) << 3;
@@ -2031,8 +2389,14 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
		optpar |= MLX4_QP_OPTPAR_ALT_ADDR_PATH;
	}

	get_cqs(qp, src_type, &send_cq, &recv_cq);
	context->pd = cpu_to_be32(pd->pdn);

	if (!rwq_ind_tbl) {
		get_cqs(qp, src_type, &send_cq, &recv_cq);
	} else { /* Set dummy CQs to be compatible with HV and PRM */
		send_cq = to_mcq(rwq_ind_tbl->ind_tbl[0]->cq);
		recv_cq = send_cq;
	}
	context->cqn_send = cpu_to_be32(send_cq->mcq.cqn);
	context->cqn_recv = cpu_to_be32(recv_cq->mcq.cqn);
	context->params1  = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28);
@@ -2358,6 +2722,11 @@ static int __mlx4_ib_modify_qp(void *src, enum mlx4_ib_source_type src_type,
	return err;
}

enum {
	MLX4_IB_MODIFY_QP_RSS_SUP_ATTR_MSK = (IB_QP_STATE	|
					      IB_QP_PORT),
};

static int _mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
			      int attr_mask, struct ib_udata *udata)
{
@@ -2388,6 +2757,27 @@ static int _mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
		goto out;
	}

	if (ibqp->rwq_ind_tbl) {
		if (!(((cur_state == IB_QPS_RESET) &&
		       (new_state == IB_QPS_INIT)) ||
		      ((cur_state == IB_QPS_INIT)  &&
		       (new_state == IB_QPS_RTR)))) {
			pr_debug("qpn 0x%x: RSS QP unsupported transition %d to %d\n",
				 ibqp->qp_num, cur_state, new_state);

			err = -EOPNOTSUPP;
			goto out;
		}

		if (attr_mask & ~MLX4_IB_MODIFY_QP_RSS_SUP_ATTR_MSK) {
			pr_debug("qpn 0x%x: RSS QP unsupported attribute mask 0x%x for transition %d to %d\n",
				 ibqp->qp_num, attr_mask, cur_state, new_state);

			err = -EOPNOTSUPP;
			goto out;
		}
	}

	if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT)) {
		if ((cur_state == IB_QPS_RESET) && (new_state == IB_QPS_INIT)) {
			if ((ibqp->qp_type == IB_QPT_RC) ||
@@ -2452,9 +2842,18 @@ static int _mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
		goto out;
	}

	if (ibqp->rwq_ind_tbl && (new_state == IB_QPS_INIT)) {
		err = bringup_rss_rwqs(ibqp->rwq_ind_tbl, attr->port_num);
		if (err)
			goto out;
	}

	err = __mlx4_ib_modify_qp(ibqp, MLX4_IB_QP_SRC, attr, attr_mask,
				  cur_state, new_state);

	if (ibqp->rwq_ind_tbl && err)
		bring_down_rss_rwqs(ibqp->rwq_ind_tbl);

	if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT))
		attr->port_num = 1;

@@ -3643,6 +4042,9 @@ int mlx4_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr
	int mlx4_state;
	int err = 0;

	if (ibqp->rwq_ind_tbl)
		return -EOPNOTSUPP;

	mutex_lock(&qp->mutex);

	if (qp->state == IB_QPS_RESET) {
@@ -3917,6 +4319,11 @@ int mlx4_ib_modify_wq(struct ib_wq *ibwq, struct ib_wq_attr *wq_attr,
	if ((new_state == IB_WQS_ERR) && (cur_state == IB_WQS_RESET))
		return -EINVAL;

	/* Need to protect against the parent RSS which also may modify WQ
	 * state.
	 */
	mutex_lock(&qp->mutex);

	/* Can update HW state only if a RSS QP has already associated to this
	 * WQ, so we can apply its port on the WQ.
	 */
@@ -3926,6 +4333,8 @@ int mlx4_ib_modify_wq(struct ib_wq *ibwq, struct ib_wq_attr *wq_attr,
	if (!err)
		ibwq->state = new_state;

	mutex_unlock(&qp->mutex);

	return err;
}

+33 −0
Original line number Diff line number Diff line
@@ -95,6 +95,16 @@ struct mlx4_ib_create_srq_resp {
	__u32	reserved;
};

struct mlx4_ib_create_qp_rss {
	__u64   rx_hash_fields_mask;
	__u8    rx_hash_function;
	__u8    rx_key_len;
	__u8    reserved[6];
	__u8    rx_hash_key[40];
	__u32   comp_mask;
	__u32   reserved1;
};

struct mlx4_ib_create_qp {
	__u64	buf_addr;
	__u64	db_addr;
@@ -123,4 +133,27 @@ struct mlx4_ib_create_rwq_ind_tbl_resp {
	__u32	response_length;
	__u32	reserved;
};

/* RX Hash function flags */
enum mlx4_ib_rx_hash_function_flags {
	MLX4_IB_RX_HASH_FUNC_TOEPLITZ	= 1 << 0,
};

/*
 * RX Hash flags, these flags allows to set which incoming packet's field should
 * participates in RX Hash. Each flag represent certain packet's field,
 * when the flag is set the field that is represented by the flag will
 * participate in RX Hash calculation.
 */
enum mlx4_ib_rx_hash_fields {
	MLX4_IB_RX_HASH_SRC_IPV4	= 1 << 0,
	MLX4_IB_RX_HASH_DST_IPV4	= 1 << 1,
	MLX4_IB_RX_HASH_SRC_IPV6	= 1 << 2,
	MLX4_IB_RX_HASH_DST_IPV6	= 1 << 3,
	MLX4_IB_RX_HASH_SRC_PORT_TCP	= 1 << 4,
	MLX4_IB_RX_HASH_DST_PORT_TCP	= 1 << 5,
	MLX4_IB_RX_HASH_SRC_PORT_UDP	= 1 << 6,
	MLX4_IB_RX_HASH_DST_PORT_UDP	= 1 << 7
};

#endif /* MLX4_ABI_USER_H */