18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Shared Memory Communications over RDMA (SMC-R) and RoCE
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  IB infrastructure:
68c2ecf20Sopenharmony_ci *  Establish SMC-R as an Infiniband Client to be notified about added and
78c2ecf20Sopenharmony_ci *  removed IB devices of type RDMA.
88c2ecf20Sopenharmony_ci *  Determine device and port characteristics for these IB devices.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2016
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/random.h>
168c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
178c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
188c2ecf20Sopenharmony_ci#include <linux/wait.h>
198c2ecf20Sopenharmony_ci#include <linux/mutex.h>
208c2ecf20Sopenharmony_ci#include <rdma/ib_verbs.h>
218c2ecf20Sopenharmony_ci#include <rdma/ib_cache.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "smc_pnet.h"
248c2ecf20Sopenharmony_ci#include "smc_ib.h"
258c2ecf20Sopenharmony_ci#include "smc_core.h"
268c2ecf20Sopenharmony_ci#include "smc_wr.h"
278c2ecf20Sopenharmony_ci#include "smc.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define SMC_MAX_CQE 32766	/* max. # of completion queue elements */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define SMC_QP_MIN_RNR_TIMER		5
328c2ecf20Sopenharmony_ci#define SMC_QP_TIMEOUT			15 /* 4096 * 2 ** timeout usec */
338c2ecf20Sopenharmony_ci#define SMC_QP_RETRY_CNT			7 /* 7: infinite */
348c2ecf20Sopenharmony_ci#define SMC_QP_RNR_RETRY			7 /* 7: infinite */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct smc_ib_devices smc_ib_devices = {	/* smc-registered ib devices */
378c2ecf20Sopenharmony_ci	.mutex = __MUTEX_INITIALIZER(smc_ib_devices.mutex),
388c2ecf20Sopenharmony_ci	.list = LIST_HEAD_INIT(smc_ib_devices.list),
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciu8 local_systemid[SMC_SYSTEMID_LEN];		/* unique system identifier */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int smc_ib_modify_qp_init(struct smc_link *lnk)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct ib_qp_attr qp_attr;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	memset(&qp_attr, 0, sizeof(qp_attr));
488c2ecf20Sopenharmony_ci	qp_attr.qp_state = IB_QPS_INIT;
498c2ecf20Sopenharmony_ci	qp_attr.pkey_index = 0;
508c2ecf20Sopenharmony_ci	qp_attr.port_num = lnk->ibport;
518c2ecf20Sopenharmony_ci	qp_attr.qp_access_flags = IB_ACCESS_LOCAL_WRITE
528c2ecf20Sopenharmony_ci				| IB_ACCESS_REMOTE_WRITE;
538c2ecf20Sopenharmony_ci	return ib_modify_qp(lnk->roce_qp, &qp_attr,
548c2ecf20Sopenharmony_ci			    IB_QP_STATE | IB_QP_PKEY_INDEX |
558c2ecf20Sopenharmony_ci			    IB_QP_ACCESS_FLAGS | IB_QP_PORT);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int smc_ib_modify_qp_rtr(struct smc_link *lnk)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	enum ib_qp_attr_mask qp_attr_mask =
618c2ecf20Sopenharmony_ci		IB_QP_STATE | IB_QP_AV | IB_QP_PATH_MTU | IB_QP_DEST_QPN |
628c2ecf20Sopenharmony_ci		IB_QP_RQ_PSN | IB_QP_MAX_DEST_RD_ATOMIC | IB_QP_MIN_RNR_TIMER;
638c2ecf20Sopenharmony_ci	struct ib_qp_attr qp_attr;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	memset(&qp_attr, 0, sizeof(qp_attr));
668c2ecf20Sopenharmony_ci	qp_attr.qp_state = IB_QPS_RTR;
678c2ecf20Sopenharmony_ci	qp_attr.path_mtu = min(lnk->path_mtu, lnk->peer_mtu);
688c2ecf20Sopenharmony_ci	qp_attr.ah_attr.type = RDMA_AH_ATTR_TYPE_ROCE;
698c2ecf20Sopenharmony_ci	rdma_ah_set_port_num(&qp_attr.ah_attr, lnk->ibport);
708c2ecf20Sopenharmony_ci	rdma_ah_set_grh(&qp_attr.ah_attr, NULL, 0, lnk->sgid_index, 1, 0);
718c2ecf20Sopenharmony_ci	rdma_ah_set_dgid_raw(&qp_attr.ah_attr, lnk->peer_gid);
728c2ecf20Sopenharmony_ci	memcpy(&qp_attr.ah_attr.roce.dmac, lnk->peer_mac,
738c2ecf20Sopenharmony_ci	       sizeof(lnk->peer_mac));
748c2ecf20Sopenharmony_ci	qp_attr.dest_qp_num = lnk->peer_qpn;
758c2ecf20Sopenharmony_ci	qp_attr.rq_psn = lnk->peer_psn; /* starting receive packet seq # */
768c2ecf20Sopenharmony_ci	qp_attr.max_dest_rd_atomic = 1; /* max # of resources for incoming
778c2ecf20Sopenharmony_ci					 * requests
788c2ecf20Sopenharmony_ci					 */
798c2ecf20Sopenharmony_ci	qp_attr.min_rnr_timer = SMC_QP_MIN_RNR_TIMER;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return ib_modify_qp(lnk->roce_qp, &qp_attr, qp_attr_mask);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciint smc_ib_modify_qp_rts(struct smc_link *lnk)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct ib_qp_attr qp_attr;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	memset(&qp_attr, 0, sizeof(qp_attr));
898c2ecf20Sopenharmony_ci	qp_attr.qp_state = IB_QPS_RTS;
908c2ecf20Sopenharmony_ci	qp_attr.timeout = SMC_QP_TIMEOUT;	/* local ack timeout */
918c2ecf20Sopenharmony_ci	qp_attr.retry_cnt = SMC_QP_RETRY_CNT;	/* retry count */
928c2ecf20Sopenharmony_ci	qp_attr.rnr_retry = SMC_QP_RNR_RETRY;	/* RNR retries, 7=infinite */
938c2ecf20Sopenharmony_ci	qp_attr.sq_psn = lnk->psn_initial;	/* starting send packet seq # */
948c2ecf20Sopenharmony_ci	qp_attr.max_rd_atomic = 1;	/* # of outstanding RDMA reads and
958c2ecf20Sopenharmony_ci					 * atomic ops allowed
968c2ecf20Sopenharmony_ci					 */
978c2ecf20Sopenharmony_ci	return ib_modify_qp(lnk->roce_qp, &qp_attr,
988c2ecf20Sopenharmony_ci			    IB_QP_STATE | IB_QP_TIMEOUT | IB_QP_RETRY_CNT |
998c2ecf20Sopenharmony_ci			    IB_QP_SQ_PSN | IB_QP_RNR_RETRY |
1008c2ecf20Sopenharmony_ci			    IB_QP_MAX_QP_RD_ATOMIC);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ciint smc_ib_modify_qp_error(struct smc_link *lnk)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct ib_qp_attr qp_attr;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	memset(&qp_attr, 0, sizeof(qp_attr));
1088c2ecf20Sopenharmony_ci	qp_attr.qp_state = IB_QPS_ERR;
1098c2ecf20Sopenharmony_ci	return ib_modify_qp(lnk->roce_qp, &qp_attr, IB_QP_STATE);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciint smc_ib_ready_link(struct smc_link *lnk)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = smc_get_lgr(lnk);
1158c2ecf20Sopenharmony_ci	int rc = 0;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	rc = smc_ib_modify_qp_init(lnk);
1188c2ecf20Sopenharmony_ci	if (rc)
1198c2ecf20Sopenharmony_ci		goto out;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	rc = smc_ib_modify_qp_rtr(lnk);
1228c2ecf20Sopenharmony_ci	if (rc)
1238c2ecf20Sopenharmony_ci		goto out;
1248c2ecf20Sopenharmony_ci	smc_wr_remember_qp_attr(lnk);
1258c2ecf20Sopenharmony_ci	rc = ib_req_notify_cq(lnk->smcibdev->roce_cq_recv,
1268c2ecf20Sopenharmony_ci			      IB_CQ_SOLICITED_MASK);
1278c2ecf20Sopenharmony_ci	if (rc)
1288c2ecf20Sopenharmony_ci		goto out;
1298c2ecf20Sopenharmony_ci	rc = smc_wr_rx_post_init(lnk);
1308c2ecf20Sopenharmony_ci	if (rc)
1318c2ecf20Sopenharmony_ci		goto out;
1328c2ecf20Sopenharmony_ci	smc_wr_remember_qp_attr(lnk);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (lgr->role == SMC_SERV) {
1358c2ecf20Sopenharmony_ci		rc = smc_ib_modify_qp_rts(lnk);
1368c2ecf20Sopenharmony_ci		if (rc)
1378c2ecf20Sopenharmony_ci			goto out;
1388c2ecf20Sopenharmony_ci		smc_wr_remember_qp_attr(lnk);
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ciout:
1418c2ecf20Sopenharmony_ci	return rc;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int smc_ib_fill_mac(struct smc_ib_device *smcibdev, u8 ibport)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	const struct ib_gid_attr *attr;
1478c2ecf20Sopenharmony_ci	int rc;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	attr = rdma_get_gid_attr(smcibdev->ibdev, ibport, 0);
1508c2ecf20Sopenharmony_ci	if (IS_ERR(attr))
1518c2ecf20Sopenharmony_ci		return -ENODEV;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	rc = rdma_read_gid_l2_fields(attr, NULL, smcibdev->mac[ibport - 1]);
1548c2ecf20Sopenharmony_ci	rdma_put_gid_attr(attr);
1558c2ecf20Sopenharmony_ci	return rc;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* Create an identifier unique for this instance of SMC-R.
1598c2ecf20Sopenharmony_ci * The MAC-address of the first active registered IB device
1608c2ecf20Sopenharmony_ci * plus a random 2-byte number is used to create this identifier.
1618c2ecf20Sopenharmony_ci * This name is delivered to the peer during connection initialization.
1628c2ecf20Sopenharmony_ci */
1638c2ecf20Sopenharmony_cistatic inline void smc_ib_define_local_systemid(struct smc_ib_device *smcibdev,
1648c2ecf20Sopenharmony_ci						u8 ibport)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	memcpy(&local_systemid[2], &smcibdev->mac[ibport - 1],
1678c2ecf20Sopenharmony_ci	       sizeof(smcibdev->mac[ibport - 1]));
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cibool smc_ib_is_valid_local_systemid(void)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	return !is_zero_ether_addr(&local_systemid[2]);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void smc_ib_init_local_systemid(void)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	get_random_bytes(&local_systemid[0], 2);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cibool smc_ib_port_active(struct smc_ib_device *smcibdev, u8 ibport)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	return smcibdev->pattr[ibport - 1].state == IB_PORT_ACTIVE;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/* determine the gid for an ib-device port and vlan id */
1868c2ecf20Sopenharmony_ciint smc_ib_determine_gid(struct smc_ib_device *smcibdev, u8 ibport,
1878c2ecf20Sopenharmony_ci			 unsigned short vlan_id, u8 gid[], u8 *sgid_index)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	const struct ib_gid_attr *attr;
1908c2ecf20Sopenharmony_ci	const struct net_device *ndev;
1918c2ecf20Sopenharmony_ci	int i;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	for (i = 0; i < smcibdev->pattr[ibport - 1].gid_tbl_len; i++) {
1948c2ecf20Sopenharmony_ci		attr = rdma_get_gid_attr(smcibdev->ibdev, ibport, i);
1958c2ecf20Sopenharmony_ci		if (IS_ERR(attr))
1968c2ecf20Sopenharmony_ci			continue;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		rcu_read_lock();
1998c2ecf20Sopenharmony_ci		ndev = rdma_read_gid_attr_ndev_rcu(attr);
2008c2ecf20Sopenharmony_ci		if (!IS_ERR(ndev) &&
2018c2ecf20Sopenharmony_ci		    ((!vlan_id && !is_vlan_dev(ndev)) ||
2028c2ecf20Sopenharmony_ci		     (vlan_id && is_vlan_dev(ndev) &&
2038c2ecf20Sopenharmony_ci		      vlan_dev_vlan_id(ndev) == vlan_id)) &&
2048c2ecf20Sopenharmony_ci		    attr->gid_type == IB_GID_TYPE_ROCE) {
2058c2ecf20Sopenharmony_ci			rcu_read_unlock();
2068c2ecf20Sopenharmony_ci			if (gid)
2078c2ecf20Sopenharmony_ci				memcpy(gid, &attr->gid, SMC_GID_SIZE);
2088c2ecf20Sopenharmony_ci			if (sgid_index)
2098c2ecf20Sopenharmony_ci				*sgid_index = attr->index;
2108c2ecf20Sopenharmony_ci			rdma_put_gid_attr(attr);
2118c2ecf20Sopenharmony_ci			return 0;
2128c2ecf20Sopenharmony_ci		}
2138c2ecf20Sopenharmony_ci		rcu_read_unlock();
2148c2ecf20Sopenharmony_ci		rdma_put_gid_attr(attr);
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	return -ENODEV;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int smc_ib_remember_port_attr(struct smc_ib_device *smcibdev, u8 ibport)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	int rc;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	memset(&smcibdev->pattr[ibport - 1], 0,
2248c2ecf20Sopenharmony_ci	       sizeof(smcibdev->pattr[ibport - 1]));
2258c2ecf20Sopenharmony_ci	rc = ib_query_port(smcibdev->ibdev, ibport,
2268c2ecf20Sopenharmony_ci			   &smcibdev->pattr[ibport - 1]);
2278c2ecf20Sopenharmony_ci	if (rc)
2288c2ecf20Sopenharmony_ci		goto out;
2298c2ecf20Sopenharmony_ci	/* the SMC protocol requires specification of the RoCE MAC address */
2308c2ecf20Sopenharmony_ci	rc = smc_ib_fill_mac(smcibdev, ibport);
2318c2ecf20Sopenharmony_ci	if (rc)
2328c2ecf20Sopenharmony_ci		goto out;
2338c2ecf20Sopenharmony_ci	if (!smc_ib_is_valid_local_systemid() &&
2348c2ecf20Sopenharmony_ci	    smc_ib_port_active(smcibdev, ibport))
2358c2ecf20Sopenharmony_ci		/* create unique system identifier */
2368c2ecf20Sopenharmony_ci		smc_ib_define_local_systemid(smcibdev, ibport);
2378c2ecf20Sopenharmony_ciout:
2388c2ecf20Sopenharmony_ci	return rc;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/* process context wrapper for might_sleep smc_ib_remember_port_attr */
2428c2ecf20Sopenharmony_cistatic void smc_ib_port_event_work(struct work_struct *work)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct smc_ib_device *smcibdev = container_of(
2458c2ecf20Sopenharmony_ci		work, struct smc_ib_device, port_event_work);
2468c2ecf20Sopenharmony_ci	u8 port_idx;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	for_each_set_bit(port_idx, &smcibdev->port_event_mask, SMC_MAX_PORTS) {
2498c2ecf20Sopenharmony_ci		smc_ib_remember_port_attr(smcibdev, port_idx + 1);
2508c2ecf20Sopenharmony_ci		clear_bit(port_idx, &smcibdev->port_event_mask);
2518c2ecf20Sopenharmony_ci		if (!smc_ib_port_active(smcibdev, port_idx + 1)) {
2528c2ecf20Sopenharmony_ci			set_bit(port_idx, smcibdev->ports_going_away);
2538c2ecf20Sopenharmony_ci			smcr_port_err(smcibdev, port_idx + 1);
2548c2ecf20Sopenharmony_ci		} else {
2558c2ecf20Sopenharmony_ci			clear_bit(port_idx, smcibdev->ports_going_away);
2568c2ecf20Sopenharmony_ci			smcr_port_add(smcibdev, port_idx + 1);
2578c2ecf20Sopenharmony_ci		}
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/* can be called in IRQ context */
2628c2ecf20Sopenharmony_cistatic void smc_ib_global_event_handler(struct ib_event_handler *handler,
2638c2ecf20Sopenharmony_ci					struct ib_event *ibevent)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct smc_ib_device *smcibdev;
2668c2ecf20Sopenharmony_ci	bool schedule = false;
2678c2ecf20Sopenharmony_ci	u8 port_idx;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	smcibdev = container_of(handler, struct smc_ib_device, event_handler);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	switch (ibevent->event) {
2728c2ecf20Sopenharmony_ci	case IB_EVENT_DEVICE_FATAL:
2738c2ecf20Sopenharmony_ci		/* terminate all ports on device */
2748c2ecf20Sopenharmony_ci		for (port_idx = 0; port_idx < SMC_MAX_PORTS; port_idx++) {
2758c2ecf20Sopenharmony_ci			set_bit(port_idx, &smcibdev->port_event_mask);
2768c2ecf20Sopenharmony_ci			if (!test_and_set_bit(port_idx,
2778c2ecf20Sopenharmony_ci					      smcibdev->ports_going_away))
2788c2ecf20Sopenharmony_ci				schedule = true;
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci		if (schedule)
2818c2ecf20Sopenharmony_ci			schedule_work(&smcibdev->port_event_work);
2828c2ecf20Sopenharmony_ci		break;
2838c2ecf20Sopenharmony_ci	case IB_EVENT_PORT_ACTIVE:
2848c2ecf20Sopenharmony_ci		port_idx = ibevent->element.port_num - 1;
2858c2ecf20Sopenharmony_ci		if (port_idx >= SMC_MAX_PORTS)
2868c2ecf20Sopenharmony_ci			break;
2878c2ecf20Sopenharmony_ci		set_bit(port_idx, &smcibdev->port_event_mask);
2888c2ecf20Sopenharmony_ci		if (test_and_clear_bit(port_idx, smcibdev->ports_going_away))
2898c2ecf20Sopenharmony_ci			schedule_work(&smcibdev->port_event_work);
2908c2ecf20Sopenharmony_ci		break;
2918c2ecf20Sopenharmony_ci	case IB_EVENT_PORT_ERR:
2928c2ecf20Sopenharmony_ci		port_idx = ibevent->element.port_num - 1;
2938c2ecf20Sopenharmony_ci		if (port_idx >= SMC_MAX_PORTS)
2948c2ecf20Sopenharmony_ci			break;
2958c2ecf20Sopenharmony_ci		set_bit(port_idx, &smcibdev->port_event_mask);
2968c2ecf20Sopenharmony_ci		if (!test_and_set_bit(port_idx, smcibdev->ports_going_away))
2978c2ecf20Sopenharmony_ci			schedule_work(&smcibdev->port_event_work);
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci	case IB_EVENT_GID_CHANGE:
3008c2ecf20Sopenharmony_ci		port_idx = ibevent->element.port_num - 1;
3018c2ecf20Sopenharmony_ci		if (port_idx >= SMC_MAX_PORTS)
3028c2ecf20Sopenharmony_ci			break;
3038c2ecf20Sopenharmony_ci		set_bit(port_idx, &smcibdev->port_event_mask);
3048c2ecf20Sopenharmony_ci		schedule_work(&smcibdev->port_event_work);
3058c2ecf20Sopenharmony_ci		break;
3068c2ecf20Sopenharmony_ci	default:
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_civoid smc_ib_dealloc_protection_domain(struct smc_link *lnk)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	if (lnk->roce_pd)
3148c2ecf20Sopenharmony_ci		ib_dealloc_pd(lnk->roce_pd);
3158c2ecf20Sopenharmony_ci	lnk->roce_pd = NULL;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ciint smc_ib_create_protection_domain(struct smc_link *lnk)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int rc;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	lnk->roce_pd = ib_alloc_pd(lnk->smcibdev->ibdev, 0);
3238c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(lnk->roce_pd);
3248c2ecf20Sopenharmony_ci	if (IS_ERR(lnk->roce_pd))
3258c2ecf20Sopenharmony_ci		lnk->roce_pd = NULL;
3268c2ecf20Sopenharmony_ci	return rc;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void smc_ib_qp_event_handler(struct ib_event *ibevent, void *priv)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct smc_link *lnk = (struct smc_link *)priv;
3328c2ecf20Sopenharmony_ci	struct smc_ib_device *smcibdev = lnk->smcibdev;
3338c2ecf20Sopenharmony_ci	u8 port_idx;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	switch (ibevent->event) {
3368c2ecf20Sopenharmony_ci	case IB_EVENT_QP_FATAL:
3378c2ecf20Sopenharmony_ci	case IB_EVENT_QP_ACCESS_ERR:
3388c2ecf20Sopenharmony_ci		port_idx = ibevent->element.qp->port - 1;
3398c2ecf20Sopenharmony_ci		if (port_idx >= SMC_MAX_PORTS)
3408c2ecf20Sopenharmony_ci			break;
3418c2ecf20Sopenharmony_ci		set_bit(port_idx, &smcibdev->port_event_mask);
3428c2ecf20Sopenharmony_ci		if (!test_and_set_bit(port_idx, smcibdev->ports_going_away))
3438c2ecf20Sopenharmony_ci			schedule_work(&smcibdev->port_event_work);
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci	default:
3468c2ecf20Sopenharmony_ci		break;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_civoid smc_ib_destroy_queue_pair(struct smc_link *lnk)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	if (lnk->roce_qp)
3538c2ecf20Sopenharmony_ci		ib_destroy_qp(lnk->roce_qp);
3548c2ecf20Sopenharmony_ci	lnk->roce_qp = NULL;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/* create a queue pair within the protection domain for a link */
3588c2ecf20Sopenharmony_ciint smc_ib_create_queue_pair(struct smc_link *lnk)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct ib_qp_init_attr qp_attr = {
3618c2ecf20Sopenharmony_ci		.event_handler = smc_ib_qp_event_handler,
3628c2ecf20Sopenharmony_ci		.qp_context = lnk,
3638c2ecf20Sopenharmony_ci		.send_cq = lnk->smcibdev->roce_cq_send,
3648c2ecf20Sopenharmony_ci		.recv_cq = lnk->smcibdev->roce_cq_recv,
3658c2ecf20Sopenharmony_ci		.srq = NULL,
3668c2ecf20Sopenharmony_ci		.cap = {
3678c2ecf20Sopenharmony_ci				/* include unsolicited rdma_writes as well,
3688c2ecf20Sopenharmony_ci				 * there are max. 2 RDMA_WRITE per 1 WR_SEND
3698c2ecf20Sopenharmony_ci				 */
3708c2ecf20Sopenharmony_ci			.max_send_wr = SMC_WR_BUF_CNT * 3,
3718c2ecf20Sopenharmony_ci			.max_recv_wr = SMC_WR_BUF_CNT * 3,
3728c2ecf20Sopenharmony_ci			.max_send_sge = SMC_IB_MAX_SEND_SGE,
3738c2ecf20Sopenharmony_ci			.max_recv_sge = 1,
3748c2ecf20Sopenharmony_ci		},
3758c2ecf20Sopenharmony_ci		.sq_sig_type = IB_SIGNAL_REQ_WR,
3768c2ecf20Sopenharmony_ci		.qp_type = IB_QPT_RC,
3778c2ecf20Sopenharmony_ci	};
3788c2ecf20Sopenharmony_ci	int rc;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	lnk->roce_qp = ib_create_qp(lnk->roce_pd, &qp_attr);
3818c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(lnk->roce_qp);
3828c2ecf20Sopenharmony_ci	if (IS_ERR(lnk->roce_qp))
3838c2ecf20Sopenharmony_ci		lnk->roce_qp = NULL;
3848c2ecf20Sopenharmony_ci	else
3858c2ecf20Sopenharmony_ci		smc_wr_remember_qp_attr(lnk);
3868c2ecf20Sopenharmony_ci	return rc;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_civoid smc_ib_put_memory_region(struct ib_mr *mr)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	ib_dereg_mr(mr);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int smc_ib_map_mr_sg(struct smc_buf_desc *buf_slot, u8 link_idx)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	unsigned int offset = 0;
3978c2ecf20Sopenharmony_ci	int sg_num;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* map the largest prefix of a dma mapped SG list */
4008c2ecf20Sopenharmony_ci	sg_num = ib_map_mr_sg(buf_slot->mr_rx[link_idx],
4018c2ecf20Sopenharmony_ci			      buf_slot->sgt[link_idx].sgl,
4028c2ecf20Sopenharmony_ci			      buf_slot->sgt[link_idx].orig_nents,
4038c2ecf20Sopenharmony_ci			      &offset, PAGE_SIZE);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	return sg_num;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/* Allocate a memory region and map the dma mapped SG list of buf_slot */
4098c2ecf20Sopenharmony_ciint smc_ib_get_memory_region(struct ib_pd *pd, int access_flags,
4108c2ecf20Sopenharmony_ci			     struct smc_buf_desc *buf_slot, u8 link_idx)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	if (buf_slot->mr_rx[link_idx])
4138c2ecf20Sopenharmony_ci		return 0; /* already done */
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	buf_slot->mr_rx[link_idx] =
4168c2ecf20Sopenharmony_ci		ib_alloc_mr(pd, IB_MR_TYPE_MEM_REG, 1 << buf_slot->order);
4178c2ecf20Sopenharmony_ci	if (IS_ERR(buf_slot->mr_rx[link_idx])) {
4188c2ecf20Sopenharmony_ci		int rc;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		rc = PTR_ERR(buf_slot->mr_rx[link_idx]);
4218c2ecf20Sopenharmony_ci		buf_slot->mr_rx[link_idx] = NULL;
4228c2ecf20Sopenharmony_ci		return rc;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (smc_ib_map_mr_sg(buf_slot, link_idx) != 1)
4268c2ecf20Sopenharmony_ci		return -EINVAL;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/* synchronize buffer usage for cpu access */
4328c2ecf20Sopenharmony_civoid smc_ib_sync_sg_for_cpu(struct smc_link *lnk,
4338c2ecf20Sopenharmony_ci			    struct smc_buf_desc *buf_slot,
4348c2ecf20Sopenharmony_ci			    enum dma_data_direction data_direction)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct scatterlist *sg;
4378c2ecf20Sopenharmony_ci	unsigned int i;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* for now there is just one DMA address */
4408c2ecf20Sopenharmony_ci	for_each_sg(buf_slot->sgt[lnk->link_idx].sgl, sg,
4418c2ecf20Sopenharmony_ci		    buf_slot->sgt[lnk->link_idx].nents, i) {
4428c2ecf20Sopenharmony_ci		if (!sg_dma_len(sg))
4438c2ecf20Sopenharmony_ci			break;
4448c2ecf20Sopenharmony_ci		ib_dma_sync_single_for_cpu(lnk->smcibdev->ibdev,
4458c2ecf20Sopenharmony_ci					   sg_dma_address(sg),
4468c2ecf20Sopenharmony_ci					   sg_dma_len(sg),
4478c2ecf20Sopenharmony_ci					   data_direction);
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/* synchronize buffer usage for device access */
4528c2ecf20Sopenharmony_civoid smc_ib_sync_sg_for_device(struct smc_link *lnk,
4538c2ecf20Sopenharmony_ci			       struct smc_buf_desc *buf_slot,
4548c2ecf20Sopenharmony_ci			       enum dma_data_direction data_direction)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct scatterlist *sg;
4578c2ecf20Sopenharmony_ci	unsigned int i;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* for now there is just one DMA address */
4608c2ecf20Sopenharmony_ci	for_each_sg(buf_slot->sgt[lnk->link_idx].sgl, sg,
4618c2ecf20Sopenharmony_ci		    buf_slot->sgt[lnk->link_idx].nents, i) {
4628c2ecf20Sopenharmony_ci		if (!sg_dma_len(sg))
4638c2ecf20Sopenharmony_ci			break;
4648c2ecf20Sopenharmony_ci		ib_dma_sync_single_for_device(lnk->smcibdev->ibdev,
4658c2ecf20Sopenharmony_ci					      sg_dma_address(sg),
4668c2ecf20Sopenharmony_ci					      sg_dma_len(sg),
4678c2ecf20Sopenharmony_ci					      data_direction);
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/* Map a new TX or RX buffer SG-table to DMA */
4728c2ecf20Sopenharmony_ciint smc_ib_buf_map_sg(struct smc_link *lnk,
4738c2ecf20Sopenharmony_ci		      struct smc_buf_desc *buf_slot,
4748c2ecf20Sopenharmony_ci		      enum dma_data_direction data_direction)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	int mapped_nents;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	mapped_nents = ib_dma_map_sg(lnk->smcibdev->ibdev,
4798c2ecf20Sopenharmony_ci				     buf_slot->sgt[lnk->link_idx].sgl,
4808c2ecf20Sopenharmony_ci				     buf_slot->sgt[lnk->link_idx].orig_nents,
4818c2ecf20Sopenharmony_ci				     data_direction);
4828c2ecf20Sopenharmony_ci	if (!mapped_nents)
4838c2ecf20Sopenharmony_ci		return -ENOMEM;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	return mapped_nents;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_civoid smc_ib_buf_unmap_sg(struct smc_link *lnk,
4898c2ecf20Sopenharmony_ci			 struct smc_buf_desc *buf_slot,
4908c2ecf20Sopenharmony_ci			 enum dma_data_direction data_direction)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	if (!buf_slot->sgt[lnk->link_idx].sgl->dma_address)
4938c2ecf20Sopenharmony_ci		return; /* already unmapped */
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	ib_dma_unmap_sg(lnk->smcibdev->ibdev,
4968c2ecf20Sopenharmony_ci			buf_slot->sgt[lnk->link_idx].sgl,
4978c2ecf20Sopenharmony_ci			buf_slot->sgt[lnk->link_idx].orig_nents,
4988c2ecf20Sopenharmony_ci			data_direction);
4998c2ecf20Sopenharmony_ci	buf_slot->sgt[lnk->link_idx].sgl->dma_address = 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cilong smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct ib_cq_init_attr cqattr =	{
5058c2ecf20Sopenharmony_ci		.cqe = SMC_MAX_CQE, .comp_vector = 0 };
5068c2ecf20Sopenharmony_ci	int cqe_size_order, smc_order;
5078c2ecf20Sopenharmony_ci	long rc;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	mutex_lock(&smcibdev->mutex);
5108c2ecf20Sopenharmony_ci	rc = 0;
5118c2ecf20Sopenharmony_ci	if (smcibdev->initialized)
5128c2ecf20Sopenharmony_ci		goto out;
5138c2ecf20Sopenharmony_ci	/* the calculated number of cq entries fits to mlx5 cq allocation */
5148c2ecf20Sopenharmony_ci	cqe_size_order = cache_line_size() == 128 ? 7 : 6;
5158c2ecf20Sopenharmony_ci	smc_order = MAX_ORDER - cqe_size_order - 1;
5168c2ecf20Sopenharmony_ci	if (SMC_MAX_CQE + 2 > (0x00000001 << smc_order) * PAGE_SIZE)
5178c2ecf20Sopenharmony_ci		cqattr.cqe = (0x00000001 << smc_order) * PAGE_SIZE - 2;
5188c2ecf20Sopenharmony_ci	smcibdev->roce_cq_send = ib_create_cq(smcibdev->ibdev,
5198c2ecf20Sopenharmony_ci					      smc_wr_tx_cq_handler, NULL,
5208c2ecf20Sopenharmony_ci					      smcibdev, &cqattr);
5218c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(smcibdev->roce_cq_send);
5228c2ecf20Sopenharmony_ci	if (IS_ERR(smcibdev->roce_cq_send)) {
5238c2ecf20Sopenharmony_ci		smcibdev->roce_cq_send = NULL;
5248c2ecf20Sopenharmony_ci		goto out;
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci	smcibdev->roce_cq_recv = ib_create_cq(smcibdev->ibdev,
5278c2ecf20Sopenharmony_ci					      smc_wr_rx_cq_handler, NULL,
5288c2ecf20Sopenharmony_ci					      smcibdev, &cqattr);
5298c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(smcibdev->roce_cq_recv);
5308c2ecf20Sopenharmony_ci	if (IS_ERR(smcibdev->roce_cq_recv)) {
5318c2ecf20Sopenharmony_ci		smcibdev->roce_cq_recv = NULL;
5328c2ecf20Sopenharmony_ci		goto err;
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci	smc_wr_add_dev(smcibdev);
5358c2ecf20Sopenharmony_ci	smcibdev->initialized = 1;
5368c2ecf20Sopenharmony_ci	goto out;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cierr:
5398c2ecf20Sopenharmony_ci	ib_destroy_cq(smcibdev->roce_cq_send);
5408c2ecf20Sopenharmony_ciout:
5418c2ecf20Sopenharmony_ci	mutex_unlock(&smcibdev->mutex);
5428c2ecf20Sopenharmony_ci	return rc;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic void smc_ib_cleanup_per_ibdev(struct smc_ib_device *smcibdev)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	mutex_lock(&smcibdev->mutex);
5488c2ecf20Sopenharmony_ci	if (!smcibdev->initialized)
5498c2ecf20Sopenharmony_ci		goto out;
5508c2ecf20Sopenharmony_ci	smcibdev->initialized = 0;
5518c2ecf20Sopenharmony_ci	ib_destroy_cq(smcibdev->roce_cq_recv);
5528c2ecf20Sopenharmony_ci	ib_destroy_cq(smcibdev->roce_cq_send);
5538c2ecf20Sopenharmony_ci	smc_wr_remove_dev(smcibdev);
5548c2ecf20Sopenharmony_ciout:
5558c2ecf20Sopenharmony_ci	mutex_unlock(&smcibdev->mutex);
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic struct ib_client smc_ib_client;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci/* callback function for ib_register_client() */
5618c2ecf20Sopenharmony_cistatic int smc_ib_add_dev(struct ib_device *ibdev)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct smc_ib_device *smcibdev;
5648c2ecf20Sopenharmony_ci	u8 port_cnt;
5658c2ecf20Sopenharmony_ci	int i;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (ibdev->node_type != RDMA_NODE_IB_CA)
5688c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	smcibdev = kzalloc(sizeof(*smcibdev), GFP_KERNEL);
5718c2ecf20Sopenharmony_ci	if (!smcibdev)
5728c2ecf20Sopenharmony_ci		return -ENOMEM;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	smcibdev->ibdev = ibdev;
5758c2ecf20Sopenharmony_ci	INIT_WORK(&smcibdev->port_event_work, smc_ib_port_event_work);
5768c2ecf20Sopenharmony_ci	atomic_set(&smcibdev->lnk_cnt, 0);
5778c2ecf20Sopenharmony_ci	init_waitqueue_head(&smcibdev->lnks_deleted);
5788c2ecf20Sopenharmony_ci	mutex_init(&smcibdev->mutex);
5798c2ecf20Sopenharmony_ci	mutex_lock(&smc_ib_devices.mutex);
5808c2ecf20Sopenharmony_ci	list_add_tail(&smcibdev->list, &smc_ib_devices.list);
5818c2ecf20Sopenharmony_ci	mutex_unlock(&smc_ib_devices.mutex);
5828c2ecf20Sopenharmony_ci	ib_set_client_data(ibdev, &smc_ib_client, smcibdev);
5838c2ecf20Sopenharmony_ci	INIT_IB_EVENT_HANDLER(&smcibdev->event_handler, smcibdev->ibdev,
5848c2ecf20Sopenharmony_ci			      smc_ib_global_event_handler);
5858c2ecf20Sopenharmony_ci	ib_register_event_handler(&smcibdev->event_handler);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	/* trigger reading of the port attributes */
5888c2ecf20Sopenharmony_ci	port_cnt = smcibdev->ibdev->phys_port_cnt;
5898c2ecf20Sopenharmony_ci	pr_warn_ratelimited("smc: adding ib device %s with port count %d\n",
5908c2ecf20Sopenharmony_ci			    smcibdev->ibdev->name, port_cnt);
5918c2ecf20Sopenharmony_ci	for (i = 0;
5928c2ecf20Sopenharmony_ci	     i < min_t(size_t, port_cnt, SMC_MAX_PORTS);
5938c2ecf20Sopenharmony_ci	     i++) {
5948c2ecf20Sopenharmony_ci		set_bit(i, &smcibdev->port_event_mask);
5958c2ecf20Sopenharmony_ci		/* determine pnetids of the port */
5968c2ecf20Sopenharmony_ci		if (smc_pnetid_by_dev_port(ibdev->dev.parent, i,
5978c2ecf20Sopenharmony_ci					   smcibdev->pnetid[i]))
5988c2ecf20Sopenharmony_ci			smc_pnetid_by_table_ib(smcibdev, i + 1);
5998c2ecf20Sopenharmony_ci		pr_warn_ratelimited("smc:    ib device %s port %d has pnetid "
6008c2ecf20Sopenharmony_ci				    "%.16s%s\n",
6018c2ecf20Sopenharmony_ci				    smcibdev->ibdev->name, i + 1,
6028c2ecf20Sopenharmony_ci				    smcibdev->pnetid[i],
6038c2ecf20Sopenharmony_ci				    smcibdev->pnetid_by_user[i] ?
6048c2ecf20Sopenharmony_ci				     " (user defined)" :
6058c2ecf20Sopenharmony_ci				     "");
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci	schedule_work(&smcibdev->port_event_work);
6088c2ecf20Sopenharmony_ci	return 0;
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/* callback function for ib_unregister_client() */
6128c2ecf20Sopenharmony_cistatic void smc_ib_remove_dev(struct ib_device *ibdev, void *client_data)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	struct smc_ib_device *smcibdev = client_data;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	mutex_lock(&smc_ib_devices.mutex);
6178c2ecf20Sopenharmony_ci	list_del_init(&smcibdev->list); /* remove from smc_ib_devices */
6188c2ecf20Sopenharmony_ci	mutex_unlock(&smc_ib_devices.mutex);
6198c2ecf20Sopenharmony_ci	pr_warn_ratelimited("smc: removing ib device %s\n",
6208c2ecf20Sopenharmony_ci			    smcibdev->ibdev->name);
6218c2ecf20Sopenharmony_ci	smc_smcr_terminate_all(smcibdev);
6228c2ecf20Sopenharmony_ci	smc_ib_cleanup_per_ibdev(smcibdev);
6238c2ecf20Sopenharmony_ci	ib_unregister_event_handler(&smcibdev->event_handler);
6248c2ecf20Sopenharmony_ci	cancel_work_sync(&smcibdev->port_event_work);
6258c2ecf20Sopenharmony_ci	kfree(smcibdev);
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic struct ib_client smc_ib_client = {
6298c2ecf20Sopenharmony_ci	.name	= "smc_ib",
6308c2ecf20Sopenharmony_ci	.add	= smc_ib_add_dev,
6318c2ecf20Sopenharmony_ci	.remove = smc_ib_remove_dev,
6328c2ecf20Sopenharmony_ci};
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ciint __init smc_ib_register_client(void)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	smc_ib_init_local_systemid();
6378c2ecf20Sopenharmony_ci	return ib_register_client(&smc_ib_client);
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_civoid smc_ib_unregister_client(void)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	ib_unregister_client(&smc_ib_client);
6438c2ecf20Sopenharmony_ci}
644