162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This software is available to you under a choice of one of two
662306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
962306a36Sopenharmony_ci * OpenIB.org BSD license below:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1262306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1362306a36Sopenharmony_ci *     conditions are met:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1662306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1762306a36Sopenharmony_ci *        disclaimer.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2062306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2162306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2262306a36Sopenharmony_ci *        provided with the distribution.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3162306a36Sopenharmony_ci * SOFTWARE.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/slab.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "ipoib.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciint ipoib_mcast_attach(struct net_device *dev, struct ib_device *hca,
3962306a36Sopenharmony_ci		       union ib_gid *mgid, u16 mlid, int set_qkey, u32 qkey)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct ipoib_dev_priv *priv = ipoib_priv(dev);
4262306a36Sopenharmony_ci	struct ib_qp_attr *qp_attr = NULL;
4362306a36Sopenharmony_ci	int ret;
4462306a36Sopenharmony_ci	u16 pkey_index;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (ib_find_pkey(priv->ca, priv->port, priv->pkey, &pkey_index)) {
4762306a36Sopenharmony_ci		clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
4862306a36Sopenharmony_ci		ret = -ENXIO;
4962306a36Sopenharmony_ci		goto out;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci	set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (set_qkey) {
5462306a36Sopenharmony_ci		ret = -ENOMEM;
5562306a36Sopenharmony_ci		qp_attr = kmalloc(sizeof(*qp_attr), GFP_KERNEL);
5662306a36Sopenharmony_ci		if (!qp_attr)
5762306a36Sopenharmony_ci			goto out;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		/* set correct QKey for QP */
6062306a36Sopenharmony_ci		qp_attr->qkey = qkey;
6162306a36Sopenharmony_ci		ret = ib_modify_qp(priv->qp, qp_attr, IB_QP_QKEY);
6262306a36Sopenharmony_ci		if (ret) {
6362306a36Sopenharmony_ci			ipoib_warn(priv, "failed to modify QP, ret = %d\n", ret);
6462306a36Sopenharmony_ci			goto out;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* attach QP to multicast group */
6962306a36Sopenharmony_ci	ret = ib_attach_mcast(priv->qp, mgid, mlid);
7062306a36Sopenharmony_ci	if (ret)
7162306a36Sopenharmony_ci		ipoib_warn(priv, "failed to attach to multicast group, ret = %d\n", ret);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciout:
7462306a36Sopenharmony_ci	kfree(qp_attr);
7562306a36Sopenharmony_ci	return ret;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciint ipoib_mcast_detach(struct net_device *dev, struct ib_device *hca,
7962306a36Sopenharmony_ci		       union ib_gid *mgid, u16 mlid)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct ipoib_dev_priv *priv = ipoib_priv(dev);
8262306a36Sopenharmony_ci	int ret;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	ret = ib_detach_mcast(priv->qp, mgid, mlid);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return ret;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciint ipoib_init_qp(struct net_device *dev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct ipoib_dev_priv *priv = ipoib_priv(dev);
9262306a36Sopenharmony_ci	int ret;
9362306a36Sopenharmony_ci	struct ib_qp_attr qp_attr;
9462306a36Sopenharmony_ci	int attr_mask;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags))
9762306a36Sopenharmony_ci		return -1;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	qp_attr.qp_state = IB_QPS_INIT;
10062306a36Sopenharmony_ci	qp_attr.qkey = 0;
10162306a36Sopenharmony_ci	qp_attr.port_num = priv->port;
10262306a36Sopenharmony_ci	qp_attr.pkey_index = priv->pkey_index;
10362306a36Sopenharmony_ci	attr_mask =
10462306a36Sopenharmony_ci	    IB_QP_QKEY |
10562306a36Sopenharmony_ci	    IB_QP_PORT |
10662306a36Sopenharmony_ci	    IB_QP_PKEY_INDEX |
10762306a36Sopenharmony_ci	    IB_QP_STATE;
10862306a36Sopenharmony_ci	ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
10962306a36Sopenharmony_ci	if (ret) {
11062306a36Sopenharmony_ci		ipoib_warn(priv, "failed to modify QP to init, ret = %d\n", ret);
11162306a36Sopenharmony_ci		goto out_fail;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	qp_attr.qp_state = IB_QPS_RTR;
11562306a36Sopenharmony_ci	/* Can't set this in a INIT->RTR transition */
11662306a36Sopenharmony_ci	attr_mask &= ~IB_QP_PORT;
11762306a36Sopenharmony_ci	ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
11862306a36Sopenharmony_ci	if (ret) {
11962306a36Sopenharmony_ci		ipoib_warn(priv, "failed to modify QP to RTR, ret = %d\n", ret);
12062306a36Sopenharmony_ci		goto out_fail;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	qp_attr.qp_state = IB_QPS_RTS;
12462306a36Sopenharmony_ci	qp_attr.sq_psn = 0;
12562306a36Sopenharmony_ci	attr_mask |= IB_QP_SQ_PSN;
12662306a36Sopenharmony_ci	attr_mask &= ~IB_QP_PKEY_INDEX;
12762306a36Sopenharmony_ci	ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
12862306a36Sopenharmony_ci	if (ret) {
12962306a36Sopenharmony_ci		ipoib_warn(priv, "failed to modify QP to RTS, ret = %d\n", ret);
13062306a36Sopenharmony_ci		goto out_fail;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciout_fail:
13662306a36Sopenharmony_ci	qp_attr.qp_state = IB_QPS_RESET;
13762306a36Sopenharmony_ci	if (ib_modify_qp(priv->qp, &qp_attr, IB_QP_STATE))
13862306a36Sopenharmony_ci		ipoib_warn(priv, "Failed to modify QP to RESET state\n");
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return ret;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ciint ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct ipoib_dev_priv *priv = ipoib_priv(dev);
14662306a36Sopenharmony_ci	struct ib_qp_init_attr init_attr = {
14762306a36Sopenharmony_ci		.cap = {
14862306a36Sopenharmony_ci			.max_send_wr  = ipoib_sendq_size,
14962306a36Sopenharmony_ci			.max_recv_wr  = ipoib_recvq_size,
15062306a36Sopenharmony_ci			.max_send_sge = min_t(u32, priv->ca->attrs.max_send_sge,
15162306a36Sopenharmony_ci					      MAX_SKB_FRAGS + 1),
15262306a36Sopenharmony_ci			.max_recv_sge = IPOIB_UD_RX_SG
15362306a36Sopenharmony_ci		},
15462306a36Sopenharmony_ci		.sq_sig_type = IB_SIGNAL_ALL_WR,
15562306a36Sopenharmony_ci		.qp_type     = IB_QPT_UD
15662306a36Sopenharmony_ci	};
15762306a36Sopenharmony_ci	struct ib_cq_init_attr cq_attr = {};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	int ret, size, req_vec;
16062306a36Sopenharmony_ci	int i;
16162306a36Sopenharmony_ci	static atomic_t counter;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	size = ipoib_recvq_size + 1;
16462306a36Sopenharmony_ci	ret = ipoib_cm_dev_init(dev);
16562306a36Sopenharmony_ci	if (!ret) {
16662306a36Sopenharmony_ci		size += ipoib_sendq_size;
16762306a36Sopenharmony_ci		if (ipoib_cm_has_srq(dev))
16862306a36Sopenharmony_ci			size += ipoib_recvq_size + 1; /* 1 extra for rx_drain_qp */
16962306a36Sopenharmony_ci		else
17062306a36Sopenharmony_ci			size += ipoib_recvq_size * ipoib_max_conn_qp;
17162306a36Sopenharmony_ci	} else
17262306a36Sopenharmony_ci		if (ret != -EOPNOTSUPP)
17362306a36Sopenharmony_ci			return ret;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	req_vec = atomic_inc_return(&counter) * 2;
17662306a36Sopenharmony_ci	cq_attr.cqe = size;
17762306a36Sopenharmony_ci	cq_attr.comp_vector = req_vec % priv->ca->num_comp_vectors;
17862306a36Sopenharmony_ci	priv->recv_cq = ib_create_cq(priv->ca, ipoib_ib_rx_completion, NULL,
17962306a36Sopenharmony_ci				     priv, &cq_attr);
18062306a36Sopenharmony_ci	if (IS_ERR(priv->recv_cq)) {
18162306a36Sopenharmony_ci		pr_warn("%s: failed to create receive CQ\n", ca->name);
18262306a36Sopenharmony_ci		goto out_cm_dev_cleanup;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	cq_attr.cqe = ipoib_sendq_size;
18662306a36Sopenharmony_ci	cq_attr.comp_vector = (req_vec + 1) % priv->ca->num_comp_vectors;
18762306a36Sopenharmony_ci	priv->send_cq = ib_create_cq(priv->ca, ipoib_ib_tx_completion, NULL,
18862306a36Sopenharmony_ci				     priv, &cq_attr);
18962306a36Sopenharmony_ci	if (IS_ERR(priv->send_cq)) {
19062306a36Sopenharmony_ci		pr_warn("%s: failed to create send CQ\n", ca->name);
19162306a36Sopenharmony_ci		goto out_free_recv_cq;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (ib_req_notify_cq(priv->recv_cq, IB_CQ_NEXT_COMP))
19562306a36Sopenharmony_ci		goto out_free_send_cq;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	init_attr.send_cq = priv->send_cq;
19862306a36Sopenharmony_ci	init_attr.recv_cq = priv->recv_cq;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (priv->kernel_caps & IBK_UD_TSO)
20162306a36Sopenharmony_ci		init_attr.create_flags |= IB_QP_CREATE_IPOIB_UD_LSO;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (priv->kernel_caps & IBK_BLOCK_MULTICAST_LOOPBACK)
20462306a36Sopenharmony_ci		init_attr.create_flags |= IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (priv->hca_caps & IB_DEVICE_MANAGED_FLOW_STEERING)
20762306a36Sopenharmony_ci		init_attr.create_flags |= IB_QP_CREATE_NETIF_QP;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (priv->kernel_caps & IBK_RDMA_NETDEV_OPA)
21062306a36Sopenharmony_ci		init_attr.create_flags |= IB_QP_CREATE_NETDEV_USE;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	priv->qp = ib_create_qp(priv->pd, &init_attr);
21362306a36Sopenharmony_ci	if (IS_ERR(priv->qp)) {
21462306a36Sopenharmony_ci		pr_warn("%s: failed to create QP\n", ca->name);
21562306a36Sopenharmony_ci		goto out_free_send_cq;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (ib_req_notify_cq(priv->send_cq, IB_CQ_NEXT_COMP))
21962306a36Sopenharmony_ci		goto out_free_send_cq;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	for (i = 0; i < MAX_SKB_FRAGS + 1; ++i)
22262306a36Sopenharmony_ci		priv->tx_sge[i].lkey = priv->pd->local_dma_lkey;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	priv->tx_wr.wr.opcode		= IB_WR_SEND;
22562306a36Sopenharmony_ci	priv->tx_wr.wr.sg_list		= priv->tx_sge;
22662306a36Sopenharmony_ci	priv->tx_wr.wr.send_flags	= IB_SEND_SIGNALED;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	priv->rx_sge[0].lkey = priv->pd->local_dma_lkey;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	priv->rx_sge[0].length = IPOIB_UD_BUF_SIZE(priv->max_ib_mtu);
23162306a36Sopenharmony_ci	priv->rx_wr.num_sge = 1;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	priv->rx_wr.next = NULL;
23462306a36Sopenharmony_ci	priv->rx_wr.sg_list = priv->rx_sge;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (init_attr.cap.max_send_sge > 1)
23762306a36Sopenharmony_ci		dev->features |= NETIF_F_SG;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	priv->max_send_sge = init_attr.cap.max_send_sge;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return 0;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ciout_free_send_cq:
24462306a36Sopenharmony_ci	ib_destroy_cq(priv->send_cq);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciout_free_recv_cq:
24762306a36Sopenharmony_ci	ib_destroy_cq(priv->recv_cq);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciout_cm_dev_cleanup:
25062306a36Sopenharmony_ci	ipoib_cm_dev_cleanup(dev);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return -ENODEV;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_civoid ipoib_transport_dev_cleanup(struct net_device *dev)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct ipoib_dev_priv *priv = ipoib_priv(dev);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (priv->qp) {
26062306a36Sopenharmony_ci		if (ib_destroy_qp(priv->qp))
26162306a36Sopenharmony_ci			ipoib_warn(priv, "ib_qp_destroy failed\n");
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		priv->qp = NULL;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ib_destroy_cq(priv->send_cq);
26762306a36Sopenharmony_ci	ib_destroy_cq(priv->recv_cq);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_civoid ipoib_event(struct ib_event_handler *handler,
27162306a36Sopenharmony_ci		 struct ib_event *record)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct ipoib_dev_priv *priv =
27462306a36Sopenharmony_ci		container_of(handler, struct ipoib_dev_priv, event_handler);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (record->element.port_num != priv->port)
27762306a36Sopenharmony_ci		return;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	ipoib_dbg(priv, "Event %d on device %s port %d\n", record->event,
28062306a36Sopenharmony_ci		  dev_name(&record->device->dev), record->element.port_num);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (record->event == IB_EVENT_CLIENT_REREGISTER) {
28362306a36Sopenharmony_ci		queue_work(ipoib_workqueue, &priv->flush_light);
28462306a36Sopenharmony_ci	} else if (record->event == IB_EVENT_PORT_ERR ||
28562306a36Sopenharmony_ci		   record->event == IB_EVENT_PORT_ACTIVE ||
28662306a36Sopenharmony_ci		   record->event == IB_EVENT_LID_CHANGE) {
28762306a36Sopenharmony_ci		queue_work(ipoib_workqueue, &priv->flush_normal);
28862306a36Sopenharmony_ci	} else if (record->event == IB_EVENT_PKEY_CHANGE) {
28962306a36Sopenharmony_ci		queue_work(ipoib_workqueue, &priv->flush_heavy);
29062306a36Sopenharmony_ci	} else if (record->event == IB_EVENT_GID_CHANGE &&
29162306a36Sopenharmony_ci		   !test_bit(IPOIB_FLAG_DEV_ADDR_SET, &priv->flags)) {
29262306a36Sopenharmony_ci		queue_work(ipoib_workqueue, &priv->flush_light);
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci}
295