xref: /linux/net/smc/smc_llc.c (revision 00a049cfde95931c6832edad19d9a4be441cacf5)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
29bf9abeaSUrsula Braun /*
39bf9abeaSUrsula Braun  *  Shared Memory Communications over RDMA (SMC-R) and RoCE
49bf9abeaSUrsula Braun  *
59bf9abeaSUrsula Braun  *  Link Layer Control (LLC)
69bf9abeaSUrsula Braun  *
79bf9abeaSUrsula Braun  *  Copyright IBM Corp. 2016
89bf9abeaSUrsula Braun  *
99bf9abeaSUrsula Braun  *  Author(s):  Klaus Wacker <Klaus.Wacker@de.ibm.com>
109bf9abeaSUrsula Braun  *              Ursula Braun <ubraun@linux.vnet.ibm.com>
119bf9abeaSUrsula Braun  */
129bf9abeaSUrsula Braun 
139bf9abeaSUrsula Braun #include <net/tcp.h>
149bf9abeaSUrsula Braun #include <rdma/ib_verbs.h>
159bf9abeaSUrsula Braun 
169bf9abeaSUrsula Braun #include "smc.h"
179bf9abeaSUrsula Braun #include "smc_core.h"
189bf9abeaSUrsula Braun #include "smc_clc.h"
199bf9abeaSUrsula Braun #include "smc_llc.h"
209bf9abeaSUrsula Braun 
210f627126SStefan Raspl #define SMC_LLC_DATA_LEN		40
220f627126SStefan Raspl 
230f627126SStefan Raspl struct smc_llc_hdr {
240f627126SStefan Raspl 	struct smc_wr_rx_hdr common;
250f627126SStefan Raspl 	u8 length;	/* 44 */
2652bedf37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD)
2752bedf37SKarsten Graul 	u8 reserved:4,
2852bedf37SKarsten Graul 	   add_link_rej_rsn:4;
2952bedf37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD)
3052bedf37SKarsten Graul 	u8 add_link_rej_rsn:4,
3152bedf37SKarsten Graul 	   reserved:4;
3252bedf37SKarsten Graul #endif
330f627126SStefan Raspl 	u8 flags;
340f627126SStefan Raspl };
350f627126SStefan Raspl 
3675d320d6SKarsten Graul #define SMC_LLC_FLAG_NO_RMBE_EYEC	0x03
3775d320d6SKarsten Graul 
380f627126SStefan Raspl struct smc_llc_msg_confirm_link {	/* type 0x01 */
390f627126SStefan Raspl 	struct smc_llc_hdr hd;
400f627126SStefan Raspl 	u8 sender_mac[ETH_ALEN];
410f627126SStefan Raspl 	u8 sender_gid[SMC_GID_SIZE];
420f627126SStefan Raspl 	u8 sender_qp_num[3];
430f627126SStefan Raspl 	u8 link_num;
440f627126SStefan Raspl 	u8 link_uid[SMC_LGR_ID_SIZE];
450f627126SStefan Raspl 	u8 max_links;
460f627126SStefan Raspl 	u8 reserved[9];
470f627126SStefan Raspl };
480f627126SStefan Raspl 
4952bedf37SKarsten Graul #define SMC_LLC_FLAG_ADD_LNK_REJ	0x40
5052bedf37SKarsten Graul #define SMC_LLC_REJ_RSN_NO_ALT_PATH	1
5152bedf37SKarsten Graul 
5252bedf37SKarsten Graul #define SMC_LLC_ADD_LNK_MAX_LINKS	2
5352bedf37SKarsten Graul 
5452bedf37SKarsten Graul struct smc_llc_msg_add_link {		/* type 0x02 */
5552bedf37SKarsten Graul 	struct smc_llc_hdr hd;
5652bedf37SKarsten Graul 	u8 sender_mac[ETH_ALEN];
5752bedf37SKarsten Graul 	u8 reserved2[2];
5852bedf37SKarsten Graul 	u8 sender_gid[SMC_GID_SIZE];
5952bedf37SKarsten Graul 	u8 sender_qp_num[3];
6052bedf37SKarsten Graul 	u8 link_num;
6152bedf37SKarsten Graul 	u8 flags2;	/* QP mtu */
6252bedf37SKarsten Graul 	u8 initial_psn[3];
6352bedf37SKarsten Graul 	u8 reserved[8];
6452bedf37SKarsten Graul };
6552bedf37SKarsten Graul 
6652bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ALL	0x40
6752bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ORDERLY	0x20
6852bedf37SKarsten Graul 
6952bedf37SKarsten Graul struct smc_llc_msg_del_link {		/* type 0x04 */
7052bedf37SKarsten Graul 	struct smc_llc_hdr hd;
7152bedf37SKarsten Graul 	u8 link_num;
7252bedf37SKarsten Graul 	__be32 reason;
7352bedf37SKarsten Graul 	u8 reserved[35];
7452bedf37SKarsten Graul } __packed;			/* format defined in RFC7609 */
7552bedf37SKarsten Graul 
76313164daSKarsten Graul struct smc_llc_msg_test_link {		/* type 0x07 */
77313164daSKarsten Graul 	struct smc_llc_hdr hd;
78313164daSKarsten Graul 	u8 user_data[16];
79313164daSKarsten Graul 	u8 reserved[24];
80313164daSKarsten Graul };
81313164daSKarsten Graul 
824ed75de5SKarsten Graul struct smc_rmb_rtoken {
834ed75de5SKarsten Graul 	union {
844ed75de5SKarsten Graul 		u8 num_rkeys;	/* first rtoken byte of CONFIRM LINK msg */
854ed75de5SKarsten Graul 				/* is actually the num of rtokens, first */
864ed75de5SKarsten Graul 				/* rtoken is always for the current link */
874ed75de5SKarsten Graul 		u8 link_id;	/* link id of the rtoken */
884ed75de5SKarsten Graul 	};
894ed75de5SKarsten Graul 	__be32 rmb_key;
904ed75de5SKarsten Graul 	__be64 rmb_vaddr;
914ed75de5SKarsten Graul } __packed;			/* format defined in RFC7609 */
924ed75de5SKarsten Graul 
934ed75de5SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG	3
944ed75de5SKarsten Graul 
954ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey {	/* type 0x06 */
964ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
974ed75de5SKarsten Graul 	struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
984ed75de5SKarsten Graul 	u8 reserved;
994ed75de5SKarsten Graul };
1004ed75de5SKarsten Graul 
1014ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey_cont {	/* type 0x08 */
1024ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1034ed75de5SKarsten Graul 	u8 num_rkeys;
1044ed75de5SKarsten Graul 	struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
1054ed75de5SKarsten Graul };
1064ed75de5SKarsten Graul 
1074ed75de5SKarsten Graul #define SMC_LLC_DEL_RKEY_MAX	8
1084ed75de5SKarsten Graul #define SMC_LLC_FLAG_RKEY_NEG	0x20
1094ed75de5SKarsten Graul 
1104ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey {	/* type 0x09 */
1114ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1124ed75de5SKarsten Graul 	u8 num_rkeys;
1134ed75de5SKarsten Graul 	u8 err_mask;
1144ed75de5SKarsten Graul 	u8 reserved[2];
1154ed75de5SKarsten Graul 	__be32 rkey[8];
1164ed75de5SKarsten Graul 	u8 reserved2[4];
1174ed75de5SKarsten Graul };
1184ed75de5SKarsten Graul 
1190f627126SStefan Raspl union smc_llc_msg {
1200f627126SStefan Raspl 	struct smc_llc_msg_confirm_link confirm_link;
12152bedf37SKarsten Graul 	struct smc_llc_msg_add_link add_link;
12252bedf37SKarsten Graul 	struct smc_llc_msg_del_link delete_link;
1234ed75de5SKarsten Graul 
1244ed75de5SKarsten Graul 	struct smc_llc_msg_confirm_rkey confirm_rkey;
1254ed75de5SKarsten Graul 	struct smc_llc_msg_confirm_rkey_cont confirm_rkey_cont;
1264ed75de5SKarsten Graul 	struct smc_llc_msg_delete_rkey delete_rkey;
1274ed75de5SKarsten Graul 
128313164daSKarsten Graul 	struct smc_llc_msg_test_link test_link;
1290f627126SStefan Raspl 	struct {
1300f627126SStefan Raspl 		struct smc_llc_hdr hdr;
1310f627126SStefan Raspl 		u8 data[SMC_LLC_DATA_LEN];
1320f627126SStefan Raspl 	} raw;
1330f627126SStefan Raspl };
1340f627126SStefan Raspl 
1350f627126SStefan Raspl #define SMC_LLC_FLAG_RESP		0x80
1360f627126SStefan Raspl 
1376c8968c4SKarsten Graul struct smc_llc_qentry {
1386c8968c4SKarsten Graul 	struct list_head list;
1396c8968c4SKarsten Graul 	struct smc_link *link;
1406c8968c4SKarsten Graul 	union smc_llc_msg msg;
1416c8968c4SKarsten Graul };
1426c8968c4SKarsten Graul 
1439bf9abeaSUrsula Braun /********************************** send *************************************/
1449bf9abeaSUrsula Braun 
1459bf9abeaSUrsula Braun struct smc_llc_tx_pend {
1469bf9abeaSUrsula Braun };
1479bf9abeaSUrsula Braun 
1489bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */
1499bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend,
1509bf9abeaSUrsula Braun 			       struct smc_link *link,
1519bf9abeaSUrsula Braun 			       enum ib_wc_status wc_status)
1529bf9abeaSUrsula Braun {
1539bf9abeaSUrsula Braun 	/* future work: handle wc_status error for recovery and failover */
1549bf9abeaSUrsula Braun }
1559bf9abeaSUrsula Braun 
1569bf9abeaSUrsula Braun /**
1579bf9abeaSUrsula Braun  * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits
1589bf9abeaSUrsula Braun  * @link: Pointer to SMC link used for sending LLC control message.
1599bf9abeaSUrsula Braun  * @wr_buf: Out variable returning pointer to work request payload buffer.
1609bf9abeaSUrsula Braun  * @pend: Out variable returning pointer to private pending WR tracking.
1619bf9abeaSUrsula Braun  *	  It's the context the transmit complete handler will get.
1629bf9abeaSUrsula Braun  *
1639bf9abeaSUrsula Braun  * Reserves and pre-fills an entry for a pending work request send/tx.
1649bf9abeaSUrsula Braun  * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx.
1659bf9abeaSUrsula Braun  * Can sleep due to smc_get_ctrl_buf (if not in softirq context).
1669bf9abeaSUrsula Braun  *
1679bf9abeaSUrsula Braun  * Return: 0 on success, otherwise an error value.
1689bf9abeaSUrsula Braun  */
1699bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link,
1709bf9abeaSUrsula Braun 				    struct smc_wr_buf **wr_buf,
1719bf9abeaSUrsula Braun 				    struct smc_wr_tx_pend_priv **pend)
1729bf9abeaSUrsula Braun {
1739bf9abeaSUrsula Braun 	int rc;
1749bf9abeaSUrsula Braun 
175ad6f317fSUrsula Braun 	rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL,
176ad6f317fSUrsula Braun 				     pend);
1779bf9abeaSUrsula Braun 	if (rc < 0)
1789bf9abeaSUrsula Braun 		return rc;
1799bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
1809bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE,
1819bf9abeaSUrsula Braun 		"must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)");
1829bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
1839bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE,
1849bf9abeaSUrsula Braun 		"must adapt SMC_WR_TX_SIZE to sizeof(struct smc_llc_msg); if not all smc_wr upper layer protocols use the same message size any more, must start to set link->wr_tx_sges[i].length on each individual smc_wr_tx_send()");
1859bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
1869bf9abeaSUrsula Braun 		sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
1879bf9abeaSUrsula Braun 		"must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)");
1889bf9abeaSUrsula Braun 	return 0;
1899bf9abeaSUrsula Braun }
1909bf9abeaSUrsula Braun 
1919bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */
192947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link,
1939bf9abeaSUrsula Braun 			      enum smc_llc_reqresp reqresp)
1949bf9abeaSUrsula Braun {
19500e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
1969bf9abeaSUrsula Braun 	struct smc_llc_msg_confirm_link *confllc;
1979bf9abeaSUrsula Braun 	struct smc_wr_tx_pend_priv *pend;
1989bf9abeaSUrsula Braun 	struct smc_wr_buf *wr_buf;
1999bf9abeaSUrsula Braun 	int rc;
2009bf9abeaSUrsula Braun 
2019bf9abeaSUrsula Braun 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
2029bf9abeaSUrsula Braun 	if (rc)
2039bf9abeaSUrsula Braun 		return rc;
2049bf9abeaSUrsula Braun 	confllc = (struct smc_llc_msg_confirm_link *)wr_buf;
2059bf9abeaSUrsula Braun 	memset(confllc, 0, sizeof(*confllc));
2069bf9abeaSUrsula Braun 	confllc->hd.common.type = SMC_LLC_CONFIRM_LINK;
2079bf9abeaSUrsula Braun 	confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link);
20875d320d6SKarsten Graul 	confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC;
2099bf9abeaSUrsula Braun 	if (reqresp == SMC_LLC_RESP)
2109bf9abeaSUrsula Braun 		confllc->hd.flags |= SMC_LLC_FLAG_RESP;
211947541f3SUrsula Braun 	memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1],
212947541f3SUrsula Braun 	       ETH_ALEN);
2137005ada6SUrsula Braun 	memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE);
2149bf9abeaSUrsula Braun 	hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
2152be922f3SKarsten Graul 	confllc->link_num = link->link_id;
2169bf9abeaSUrsula Braun 	memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE);
21752bedf37SKarsten Graul 	confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; /* enforce peer resp. */
21852bedf37SKarsten Graul 	/* send llc message */
21952bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
22052bedf37SKarsten Graul 	return rc;
22152bedf37SKarsten Graul }
22252bedf37SKarsten Graul 
22344aa81ceSKarsten Graul /* send LLC confirm rkey request */
22444aa81ceSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *link,
22544aa81ceSKarsten Graul 				     struct smc_buf_desc *rmb_desc)
22644aa81ceSKarsten Graul {
22744aa81ceSKarsten Graul 	struct smc_llc_msg_confirm_rkey *rkeyllc;
22844aa81ceSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
22944aa81ceSKarsten Graul 	struct smc_wr_buf *wr_buf;
23044aa81ceSKarsten Graul 	int rc;
23144aa81ceSKarsten Graul 
23244aa81ceSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
23344aa81ceSKarsten Graul 	if (rc)
23444aa81ceSKarsten Graul 		return rc;
23544aa81ceSKarsten Graul 	rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf;
23644aa81ceSKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
23744aa81ceSKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY;
23844aa81ceSKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey);
23944aa81ceSKarsten Graul 	rkeyllc->rtoken[0].rmb_key =
240387707fdSKarsten Graul 		htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
24144aa81ceSKarsten Graul 	rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
242387707fdSKarsten Graul 		(u64)sg_dma_address(rmb_desc->sgt[link->link_idx].sgl));
24344aa81ceSKarsten Graul 	/* send llc message */
24444aa81ceSKarsten Graul 	rc = smc_wr_tx_send(link, pend);
24544aa81ceSKarsten Graul 	return rc;
24644aa81ceSKarsten Graul }
24744aa81ceSKarsten Graul 
24860e03c62SKarsten Graul /* send LLC delete rkey request */
24960e03c62SKarsten Graul static int smc_llc_send_delete_rkey(struct smc_link *link,
25060e03c62SKarsten Graul 				    struct smc_buf_desc *rmb_desc)
25160e03c62SKarsten Graul {
25260e03c62SKarsten Graul 	struct smc_llc_msg_delete_rkey *rkeyllc;
25360e03c62SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
25460e03c62SKarsten Graul 	struct smc_wr_buf *wr_buf;
25560e03c62SKarsten Graul 	int rc;
25660e03c62SKarsten Graul 
25760e03c62SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
25860e03c62SKarsten Graul 	if (rc)
25960e03c62SKarsten Graul 		return rc;
26060e03c62SKarsten Graul 	rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf;
26160e03c62SKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
26260e03c62SKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY;
26360e03c62SKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey);
26460e03c62SKarsten Graul 	rkeyllc->num_rkeys = 1;
265387707fdSKarsten Graul 	rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
26660e03c62SKarsten Graul 	/* send llc message */
26760e03c62SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
26860e03c62SKarsten Graul 	return rc;
26960e03c62SKarsten Graul }
27060e03c62SKarsten Graul 
2712a4c57a9SKarsten Graul /* prepare an add link message */
2722a4c57a9SKarsten Graul static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc,
2737005ada6SUrsula Braun 				  struct smc_link *link, u8 mac[], u8 gid[],
2742a4c57a9SKarsten Graul 				  enum smc_llc_reqresp reqresp)
2752a4c57a9SKarsten Graul {
2762a4c57a9SKarsten Graul 	memset(addllc, 0, sizeof(*addllc));
2772a4c57a9SKarsten Graul 	addllc->hd.common.type = SMC_LLC_ADD_LINK;
2782a4c57a9SKarsten Graul 	addllc->hd.length = sizeof(struct smc_llc_msg_add_link);
2792a4c57a9SKarsten Graul 	if (reqresp == SMC_LLC_RESP) {
2802a4c57a9SKarsten Graul 		addllc->hd.flags |= SMC_LLC_FLAG_RESP;
2812a4c57a9SKarsten Graul 		/* always reject more links for now */
2822a4c57a9SKarsten Graul 		addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
2832a4c57a9SKarsten Graul 		addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
2842a4c57a9SKarsten Graul 	}
2852a4c57a9SKarsten Graul 	memcpy(addllc->sender_mac, mac, ETH_ALEN);
2862a4c57a9SKarsten Graul 	memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
2872a4c57a9SKarsten Graul }
2882a4c57a9SKarsten Graul 
28952bedf37SKarsten Graul /* send ADD LINK request or response */
2907005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
29152bedf37SKarsten Graul 			  enum smc_llc_reqresp reqresp)
29252bedf37SKarsten Graul {
29352bedf37SKarsten Graul 	struct smc_llc_msg_add_link *addllc;
29452bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
29552bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
29652bedf37SKarsten Graul 	int rc;
29752bedf37SKarsten Graul 
29852bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
29952bedf37SKarsten Graul 	if (rc)
30052bedf37SKarsten Graul 		return rc;
30152bedf37SKarsten Graul 	addllc = (struct smc_llc_msg_add_link *)wr_buf;
3022a4c57a9SKarsten Graul 	smc_llc_prep_add_link(addllc, link, mac, gid, reqresp);
30352bedf37SKarsten Graul 	/* send llc message */
30452bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
30552bedf37SKarsten Graul 	return rc;
30652bedf37SKarsten Graul }
30752bedf37SKarsten Graul 
3082a4c57a9SKarsten Graul /* prepare a delete link message */
3092a4c57a9SKarsten Graul static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc,
3102a4c57a9SKarsten Graul 				     struct smc_link *link,
3110d18a0cbSKarsten Graul 				     enum smc_llc_reqresp reqresp, bool orderly)
3122a4c57a9SKarsten Graul {
3132a4c57a9SKarsten Graul 	memset(delllc, 0, sizeof(*delllc));
3142a4c57a9SKarsten Graul 	delllc->hd.common.type = SMC_LLC_DELETE_LINK;
3152a4c57a9SKarsten Graul 	delllc->hd.length = sizeof(struct smc_llc_msg_add_link);
3162a4c57a9SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
3172a4c57a9SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_RESP;
3182a4c57a9SKarsten Graul 	/* DEL_LINK_ALL because only 1 link supported */
3192a4c57a9SKarsten Graul 	delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
3200d18a0cbSKarsten Graul 	if (orderly)
3212a4c57a9SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
3222a4c57a9SKarsten Graul 	delllc->link_num = link->link_id;
3232a4c57a9SKarsten Graul }
3242a4c57a9SKarsten Graul 
32552bedf37SKarsten Graul /* send DELETE LINK request or response */
32652bedf37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link,
3270d18a0cbSKarsten Graul 			     enum smc_llc_reqresp reqresp, bool orderly)
32852bedf37SKarsten Graul {
32952bedf37SKarsten Graul 	struct smc_llc_msg_del_link *delllc;
33052bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
33152bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
33252bedf37SKarsten Graul 	int rc;
33352bedf37SKarsten Graul 
33452bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
33552bedf37SKarsten Graul 	if (rc)
33652bedf37SKarsten Graul 		return rc;
33752bedf37SKarsten Graul 	delllc = (struct smc_llc_msg_del_link *)wr_buf;
3380d18a0cbSKarsten Graul 	smc_llc_prep_delete_link(delllc, link, reqresp, orderly);
3399bf9abeaSUrsula Braun 	/* send llc message */
3409bf9abeaSUrsula Braun 	rc = smc_wr_tx_send(link, pend);
3419bf9abeaSUrsula Braun 	return rc;
3429bf9abeaSUrsula Braun }
3439bf9abeaSUrsula Braun 
344d97935faSKarsten Graul /* send LLC test link request */
345d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
346313164daSKarsten Graul {
347313164daSKarsten Graul 	struct smc_llc_msg_test_link *testllc;
348313164daSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
349313164daSKarsten Graul 	struct smc_wr_buf *wr_buf;
350313164daSKarsten Graul 	int rc;
351313164daSKarsten Graul 
352313164daSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
353313164daSKarsten Graul 	if (rc)
354313164daSKarsten Graul 		return rc;
355313164daSKarsten Graul 	testllc = (struct smc_llc_msg_test_link *)wr_buf;
356313164daSKarsten Graul 	memset(testllc, 0, sizeof(*testllc));
357313164daSKarsten Graul 	testllc->hd.common.type = SMC_LLC_TEST_LINK;
358313164daSKarsten Graul 	testllc->hd.length = sizeof(struct smc_llc_msg_test_link);
359313164daSKarsten Graul 	memcpy(testllc->user_data, user_data, sizeof(testllc->user_data));
360313164daSKarsten Graul 	/* send llc message */
361313164daSKarsten Graul 	rc = smc_wr_tx_send(link, pend);
362313164daSKarsten Graul 	return rc;
363313164daSKarsten Graul }
364313164daSKarsten Graul 
3656c8968c4SKarsten Graul /* schedule an llc send on link, may wait for buffers */
3666c8968c4SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf)
3674ed75de5SKarsten Graul {
3684ed75de5SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
3694ed75de5SKarsten Graul 	struct smc_wr_buf *wr_buf;
3704ed75de5SKarsten Graul 	int rc;
3714ed75de5SKarsten Graul 
3726c8968c4SKarsten Graul 	if (!smc_link_usable(link))
3736c8968c4SKarsten Graul 		return -ENOLINK;
3746c8968c4SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
3754ed75de5SKarsten Graul 	if (rc)
3766c8968c4SKarsten Graul 		return rc;
3776c8968c4SKarsten Graul 	memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
3786c8968c4SKarsten Graul 	return smc_wr_tx_send(link, pend);
3794ed75de5SKarsten Graul }
3804ed75de5SKarsten Graul 
3819bf9abeaSUrsula Braun /********************************* receive ***********************************/
3829bf9abeaSUrsula Braun 
3839bf9abeaSUrsula Braun static void smc_llc_rx_confirm_link(struct smc_link *link,
3849bf9abeaSUrsula Braun 				    struct smc_llc_msg_confirm_link *llc)
3859bf9abeaSUrsula Braun {
38600e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
387ef79d439SKarsten Graul 	int conf_rc = 0;
3889bf9abeaSUrsula Braun 
38975d320d6SKarsten Graul 	/* RMBE eyecatchers are not supported */
390ef79d439SKarsten Graul 	if (!(llc->hd.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
39175d320d6SKarsten Graul 		conf_rc = ENOTSUPP;
39275d320d6SKarsten Graul 
39352bedf37SKarsten Graul 	if (lgr->role == SMC_CLNT &&
39452bedf37SKarsten Graul 	    link->state == SMC_LNK_ACTIVATING) {
39575d320d6SKarsten Graul 		link->llc_confirm_rc = conf_rc;
3969bf9abeaSUrsula Braun 		link->link_id = llc->link_num;
3979bf9abeaSUrsula Braun 		complete(&link->llc_confirm);
3989bf9abeaSUrsula Braun 	}
3999bf9abeaSUrsula Braun }
4009bf9abeaSUrsula Braun 
40152bedf37SKarsten Graul static void smc_llc_rx_add_link(struct smc_link *link,
40252bedf37SKarsten Graul 				struct smc_llc_msg_add_link *llc)
40352bedf37SKarsten Graul {
40400e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
40552bedf37SKarsten Graul 
40652bedf37SKarsten Graul 	if (link->state == SMC_LNK_ACTIVATING) {
40752bedf37SKarsten Graul 		complete(&link->llc_add);
40852bedf37SKarsten Graul 		return;
40952bedf37SKarsten Graul 	}
41052bedf37SKarsten Graul 
41152bedf37SKarsten Graul 	if (lgr->role == SMC_SERV) {
4122a4c57a9SKarsten Graul 		smc_llc_prep_add_link(llc, link,
41352bedf37SKarsten Graul 				link->smcibdev->mac[link->ibport - 1],
4147005ada6SUrsula Braun 				link->gid, SMC_LLC_REQ);
41552bedf37SKarsten Graul 
41652bedf37SKarsten Graul 	} else {
4172a4c57a9SKarsten Graul 		smc_llc_prep_add_link(llc, link,
41852bedf37SKarsten Graul 				link->smcibdev->mac[link->ibport - 1],
4197005ada6SUrsula Braun 				link->gid, SMC_LLC_RESP);
42052bedf37SKarsten Graul 	}
4216c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
42252bedf37SKarsten Graul }
42352bedf37SKarsten Graul 
42452bedf37SKarsten Graul static void smc_llc_rx_delete_link(struct smc_link *link,
42552bedf37SKarsten Graul 				   struct smc_llc_msg_del_link *llc)
42652bedf37SKarsten Graul {
42700e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
42852bedf37SKarsten Graul 
4299651b934SKarsten Graul 	smc_lgr_forget(lgr);
4300d18a0cbSKarsten Graul 	smc_llc_link_deleting(link);
4310d18a0cbSKarsten Graul 	if (lgr->role == SMC_SERV) {
4320d18a0cbSKarsten Graul 		/* client asks to delete this link, send request */
4330d18a0cbSKarsten Graul 		smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ, true);
43452bedf37SKarsten Graul 	} else {
4350d18a0cbSKarsten Graul 		/* server requests to delete this link, send response */
4360d18a0cbSKarsten Graul 		smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true);
43752bedf37SKarsten Graul 	}
4386c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
439f528ba24SUrsula Braun 	smc_lgr_terminate_sched(lgr);
44052bedf37SKarsten Graul }
44152bedf37SKarsten Graul 
442313164daSKarsten Graul static void smc_llc_rx_test_link(struct smc_link *link,
443313164daSKarsten Graul 				 struct smc_llc_msg_test_link *llc)
444313164daSKarsten Graul {
445d97935faSKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
4466c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
447313164daSKarsten Graul }
448313164daSKarsten Graul 
4494ed75de5SKarsten Graul static void smc_llc_rx_confirm_rkey(struct smc_link *link,
4504ed75de5SKarsten Graul 				    struct smc_llc_msg_confirm_rkey *llc)
4514ed75de5SKarsten Graul {
4524ed75de5SKarsten Graul 	int rc;
4534ed75de5SKarsten Graul 
454387707fdSKarsten Graul 	rc = smc_rtoken_add(link,
4554ed75de5SKarsten Graul 			    llc->rtoken[0].rmb_vaddr,
4564ed75de5SKarsten Graul 			    llc->rtoken[0].rmb_key);
4574ed75de5SKarsten Graul 
4584ed75de5SKarsten Graul 	/* ignore rtokens for other links, we have only one link */
4594ed75de5SKarsten Graul 
4604ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
4614ed75de5SKarsten Graul 	if (rc < 0)
4624ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
4636c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
4644ed75de5SKarsten Graul }
4654ed75de5SKarsten Graul 
4664ed75de5SKarsten Graul static void smc_llc_rx_confirm_rkey_cont(struct smc_link *link,
4674ed75de5SKarsten Graul 				      struct smc_llc_msg_confirm_rkey_cont *llc)
4684ed75de5SKarsten Graul {
4694ed75de5SKarsten Graul 	/* ignore rtokens for other links, we have only one link */
4704ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
4716c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
4724ed75de5SKarsten Graul }
4734ed75de5SKarsten Graul 
4744ed75de5SKarsten Graul static void smc_llc_rx_delete_rkey(struct smc_link *link,
4754ed75de5SKarsten Graul 				   struct smc_llc_msg_delete_rkey *llc)
4764ed75de5SKarsten Graul {
4774ed75de5SKarsten Graul 	u8 err_mask = 0;
4784ed75de5SKarsten Graul 	int i, max;
4794ed75de5SKarsten Graul 
4804ed75de5SKarsten Graul 	max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
4814ed75de5SKarsten Graul 	for (i = 0; i < max; i++) {
482387707fdSKarsten Graul 		if (smc_rtoken_delete(link, llc->rkey[i]))
4834ed75de5SKarsten Graul 			err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
4844ed75de5SKarsten Graul 	}
4854ed75de5SKarsten Graul 
4864ed75de5SKarsten Graul 	if (err_mask) {
4874ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
4884ed75de5SKarsten Graul 		llc->err_mask = err_mask;
4894ed75de5SKarsten Graul 	}
4904ed75de5SKarsten Graul 
4914ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
4926c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
4934ed75de5SKarsten Graul }
4944ed75de5SKarsten Graul 
4956c8968c4SKarsten Graul /* flush the llc event queue */
496*00a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr)
4979bf9abeaSUrsula Braun {
4986c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry, *q;
4999bf9abeaSUrsula Braun 
5006c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
5016c8968c4SKarsten Graul 	list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
5026c8968c4SKarsten Graul 		list_del_init(&qentry->list);
5036c8968c4SKarsten Graul 		kfree(qentry);
5046c8968c4SKarsten Graul 	}
5056c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
5066c8968c4SKarsten Graul }
5076c8968c4SKarsten Graul 
5086c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
5096c8968c4SKarsten Graul {
5106c8968c4SKarsten Graul 	union smc_llc_msg *llc = &qentry->msg;
5116c8968c4SKarsten Graul 	struct smc_link *link = qentry->link;
5126c8968c4SKarsten Graul 
513d854fcbfSKarsten Graul 	if (!smc_link_usable(link))
5146c8968c4SKarsten Graul 		goto out;
515313164daSKarsten Graul 
516313164daSKarsten Graul 	switch (llc->raw.hdr.common.type) {
517313164daSKarsten Graul 	case SMC_LLC_TEST_LINK:
518313164daSKarsten Graul 		smc_llc_rx_test_link(link, &llc->test_link);
519313164daSKarsten Graul 		break;
520313164daSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
5219bf9abeaSUrsula Braun 		smc_llc_rx_confirm_link(link, &llc->confirm_link);
522313164daSKarsten Graul 		break;
52352bedf37SKarsten Graul 	case SMC_LLC_ADD_LINK:
52452bedf37SKarsten Graul 		smc_llc_rx_add_link(link, &llc->add_link);
52552bedf37SKarsten Graul 		break;
52652bedf37SKarsten Graul 	case SMC_LLC_DELETE_LINK:
52752bedf37SKarsten Graul 		smc_llc_rx_delete_link(link, &llc->delete_link);
52852bedf37SKarsten Graul 		break;
5294ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
5304ed75de5SKarsten Graul 		smc_llc_rx_confirm_rkey(link, &llc->confirm_rkey);
5314ed75de5SKarsten Graul 		break;
5324ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
5334ed75de5SKarsten Graul 		smc_llc_rx_confirm_rkey_cont(link, &llc->confirm_rkey_cont);
5344ed75de5SKarsten Graul 		break;
5354ed75de5SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
5364ed75de5SKarsten Graul 		smc_llc_rx_delete_rkey(link, &llc->delete_rkey);
5374ed75de5SKarsten Graul 		break;
538313164daSKarsten Graul 	}
5396c8968c4SKarsten Graul out:
5406c8968c4SKarsten Graul 	kfree(qentry);
5416c8968c4SKarsten Graul }
5426c8968c4SKarsten Graul 
5436c8968c4SKarsten Graul /* worker to process llc messages on the event queue */
5446c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work)
5456c8968c4SKarsten Graul {
5466c8968c4SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
5476c8968c4SKarsten Graul 						  llc_event_work);
5486c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
5496c8968c4SKarsten Graul 
5506c8968c4SKarsten Graul again:
5516c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
5526c8968c4SKarsten Graul 	if (!list_empty(&lgr->llc_event_q)) {
5536c8968c4SKarsten Graul 		qentry = list_first_entry(&lgr->llc_event_q,
5546c8968c4SKarsten Graul 					  struct smc_llc_qentry, list);
5556c8968c4SKarsten Graul 		list_del_init(&qentry->list);
5566c8968c4SKarsten Graul 		spin_unlock_bh(&lgr->llc_event_q_lock);
5576c8968c4SKarsten Graul 		smc_llc_event_handler(qentry);
5586c8968c4SKarsten Graul 		goto again;
5596c8968c4SKarsten Graul 	}
5606c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
5616c8968c4SKarsten Graul }
5626c8968c4SKarsten Graul 
563ef79d439SKarsten Graul /* process llc responses in tasklet context */
564ef79d439SKarsten Graul static void smc_llc_rx_response(struct smc_link *link, union smc_llc_msg *llc)
565ef79d439SKarsten Graul {
566ef79d439SKarsten Graul 	int rc = 0;
567ef79d439SKarsten Graul 
568ef79d439SKarsten Graul 	switch (llc->raw.hdr.common.type) {
569ef79d439SKarsten Graul 	case SMC_LLC_TEST_LINK:
570ef79d439SKarsten Graul 		if (link->state == SMC_LNK_ACTIVE)
571ef79d439SKarsten Graul 			complete(&link->llc_testlink_resp);
572ef79d439SKarsten Graul 		break;
573ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
574ef79d439SKarsten Graul 		if (!(llc->raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
575ef79d439SKarsten Graul 			rc = ENOTSUPP;
576ef79d439SKarsten Graul 		if (link->lgr->role == SMC_SERV &&
577ef79d439SKarsten Graul 		    link->state == SMC_LNK_ACTIVATING) {
578ef79d439SKarsten Graul 			link->llc_confirm_resp_rc = rc;
579ef79d439SKarsten Graul 			complete(&link->llc_confirm_resp);
580ef79d439SKarsten Graul 		}
581ef79d439SKarsten Graul 		break;
582ef79d439SKarsten Graul 	case SMC_LLC_ADD_LINK:
583ef79d439SKarsten Graul 		if (link->state == SMC_LNK_ACTIVATING)
584ef79d439SKarsten Graul 			complete(&link->llc_add_resp);
585ef79d439SKarsten Graul 		break;
586ef79d439SKarsten Graul 	case SMC_LLC_DELETE_LINK:
587ef79d439SKarsten Graul 		if (link->lgr->role == SMC_SERV)
588ef79d439SKarsten Graul 			smc_lgr_schedule_free_work_fast(link->lgr);
589ef79d439SKarsten Graul 		break;
590ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
591ef79d439SKarsten Graul 		link->llc_confirm_rkey_resp_rc = llc->raw.hdr.flags &
592ef79d439SKarsten Graul 						 SMC_LLC_FLAG_RKEY_NEG;
593ef79d439SKarsten Graul 		complete(&link->llc_confirm_rkey_resp);
594ef79d439SKarsten Graul 		break;
595ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
596ef79d439SKarsten Graul 		/* unused as long as we don't send this type of msg */
597ef79d439SKarsten Graul 		break;
598ef79d439SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
599ef79d439SKarsten Graul 		link->llc_delete_rkey_resp_rc = llc->raw.hdr.flags &
600ef79d439SKarsten Graul 						SMC_LLC_FLAG_RKEY_NEG;
601ef79d439SKarsten Graul 		complete(&link->llc_delete_rkey_resp);
602ef79d439SKarsten Graul 		break;
603ef79d439SKarsten Graul 	}
604ef79d439SKarsten Graul }
605ef79d439SKarsten Graul 
6066c8968c4SKarsten Graul /* copy received msg and add it to the event queue */
6076c8968c4SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
6086c8968c4SKarsten Graul {
6096c8968c4SKarsten Graul 	struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
6106c8968c4SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
6116c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
6126c8968c4SKarsten Graul 	union smc_llc_msg *llc = buf;
6136c8968c4SKarsten Graul 	unsigned long flags;
6146c8968c4SKarsten Graul 
6156c8968c4SKarsten Graul 	if (wc->byte_len < sizeof(*llc))
6166c8968c4SKarsten Graul 		return; /* short message */
6176c8968c4SKarsten Graul 	if (llc->raw.hdr.length != sizeof(*llc))
6186c8968c4SKarsten Graul 		return; /* invalid message */
6196c8968c4SKarsten Graul 
620ef79d439SKarsten Graul 	/* process responses immediately */
621ef79d439SKarsten Graul 	if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) {
622ef79d439SKarsten Graul 		smc_llc_rx_response(link, llc);
623ef79d439SKarsten Graul 		return;
624ef79d439SKarsten Graul 	}
625ef79d439SKarsten Graul 
6266c8968c4SKarsten Graul 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
6276c8968c4SKarsten Graul 	if (!qentry)
6286c8968c4SKarsten Graul 		return;
6296c8968c4SKarsten Graul 	qentry->link = link;
6306c8968c4SKarsten Graul 	INIT_LIST_HEAD(&qentry->list);
6316c8968c4SKarsten Graul 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
6326c8968c4SKarsten Graul 	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
6336c8968c4SKarsten Graul 	list_add_tail(&qentry->list, &lgr->llc_event_q);
6346c8968c4SKarsten Graul 	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
6356c8968c4SKarsten Graul 	schedule_work(&link->lgr->llc_event_work);
6369bf9abeaSUrsula Braun }
6379bf9abeaSUrsula Braun 
63844aa81ceSKarsten Graul /***************************** worker, utils *********************************/
639877ae5beSKarsten Graul 
640877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work)
641877ae5beSKarsten Graul {
642877ae5beSKarsten Graul 	struct smc_link *link = container_of(to_delayed_work(work),
643877ae5beSKarsten Graul 					     struct smc_link, llc_testlink_wrk);
644877ae5beSKarsten Graul 	unsigned long next_interval;
645877ae5beSKarsten Graul 	unsigned long expire_time;
646877ae5beSKarsten Graul 	u8 user_data[16] = { 0 };
647877ae5beSKarsten Graul 	int rc;
648877ae5beSKarsten Graul 
649877ae5beSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
650877ae5beSKarsten Graul 		return;		/* don't reschedule worker */
651877ae5beSKarsten Graul 	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
652877ae5beSKarsten Graul 	if (time_is_after_jiffies(expire_time)) {
653877ae5beSKarsten Graul 		next_interval = expire_time - jiffies;
654877ae5beSKarsten Graul 		goto out;
655877ae5beSKarsten Graul 	}
656877ae5beSKarsten Graul 	reinit_completion(&link->llc_testlink_resp);
657d97935faSKarsten Graul 	smc_llc_send_test_link(link, user_data);
658877ae5beSKarsten Graul 	/* receive TEST LINK response over RoCE fabric */
659877ae5beSKarsten Graul 	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
660877ae5beSKarsten Graul 						       SMC_LLC_WAIT_TIME);
6611020e1efSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
6621020e1efSKarsten Graul 		return;		/* link state changed */
663877ae5beSKarsten Graul 	if (rc <= 0) {
6645f78fe96SKarsten Graul 		smc_lgr_terminate_sched(smc_get_lgr(link));
665877ae5beSKarsten Graul 		return;
666877ae5beSKarsten Graul 	}
667877ae5beSKarsten Graul 	next_interval = link->llc_testlink_time;
668877ae5beSKarsten Graul out:
6691020e1efSKarsten Graul 	schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
670877ae5beSKarsten Graul }
671877ae5beSKarsten Graul 
672*00a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
673*00a049cfSKarsten Graul {
674*00a049cfSKarsten Graul 	struct net *net = sock_net(smc->clcsock->sk);
675*00a049cfSKarsten Graul 
676*00a049cfSKarsten Graul 	INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
677*00a049cfSKarsten Graul 	INIT_LIST_HEAD(&lgr->llc_event_q);
678*00a049cfSKarsten Graul 	spin_lock_init(&lgr->llc_event_q_lock);
679*00a049cfSKarsten Graul 	lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;
680*00a049cfSKarsten Graul }
681*00a049cfSKarsten Graul 
682*00a049cfSKarsten Graul /* called after lgr was removed from lgr_list */
683*00a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr)
684*00a049cfSKarsten Graul {
685*00a049cfSKarsten Graul 	smc_llc_event_flush(lgr);
686*00a049cfSKarsten Graul 	cancel_work_sync(&lgr->llc_event_work);
687*00a049cfSKarsten Graul }
688*00a049cfSKarsten Graul 
6892a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link)
690877ae5beSKarsten Graul {
691b32cf4abSKarsten Graul 	init_completion(&link->llc_confirm);
692b32cf4abSKarsten Graul 	init_completion(&link->llc_confirm_resp);
693b32cf4abSKarsten Graul 	init_completion(&link->llc_add);
694b32cf4abSKarsten Graul 	init_completion(&link->llc_add_resp);
695ef79d439SKarsten Graul 	init_completion(&link->llc_confirm_rkey_resp);
696ef79d439SKarsten Graul 	init_completion(&link->llc_delete_rkey_resp);
69760e03c62SKarsten Graul 	mutex_init(&link->llc_delete_rkey_mutex);
698877ae5beSKarsten Graul 	init_completion(&link->llc_testlink_resp);
699877ae5beSKarsten Graul 	INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
7002a4c57a9SKarsten Graul 	return 0;
701b32cf4abSKarsten Graul }
702b32cf4abSKarsten Graul 
703*00a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link)
704b32cf4abSKarsten Graul {
705877ae5beSKarsten Graul 	link->state = SMC_LNK_ACTIVE;
706*00a049cfSKarsten Graul 	if (link->lgr->llc_testlink_time) {
707*00a049cfSKarsten Graul 		link->llc_testlink_time = link->lgr->llc_testlink_time * HZ;
7081020e1efSKarsten Graul 		schedule_delayed_work(&link->llc_testlink_wrk,
709877ae5beSKarsten Graul 				      link->llc_testlink_time);
710877ae5beSKarsten Graul 	}
711877ae5beSKarsten Graul }
712877ae5beSKarsten Graul 
7130d18a0cbSKarsten Graul void smc_llc_link_deleting(struct smc_link *link)
7140d18a0cbSKarsten Graul {
7150d18a0cbSKarsten Graul 	link->state = SMC_LNK_DELETING;
71615e1b99aSUrsula Braun 	smc_wr_wakeup_tx_wait(link);
7170d18a0cbSKarsten Graul }
7180d18a0cbSKarsten Graul 
719877ae5beSKarsten Graul /* called in worker context */
7202a4c57a9SKarsten Graul void smc_llc_link_clear(struct smc_link *link)
721877ae5beSKarsten Graul {
7222140ac26SKarsten Graul 	complete(&link->llc_testlink_resp);
7232140ac26SKarsten Graul 	cancel_delayed_work_sync(&link->llc_testlink_wrk);
7242140ac26SKarsten Graul 	smc_wr_wakeup_reg_wait(link);
7252140ac26SKarsten Graul 	smc_wr_wakeup_tx_wait(link);
726877ae5beSKarsten Graul }
727877ae5beSKarsten Graul 
72844aa81ceSKarsten Graul /* register a new rtoken at the remote peer */
72944aa81ceSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *link,
73044aa81ceSKarsten Graul 			    struct smc_buf_desc *rmb_desc)
73144aa81ceSKarsten Graul {
73244aa81ceSKarsten Graul 	int rc;
73344aa81ceSKarsten Graul 
73460e03c62SKarsten Graul 	/* protected by mutex smc_create_lgr_pending */
735ef79d439SKarsten Graul 	reinit_completion(&link->llc_confirm_rkey_resp);
7364600cfc3SKarsten Graul 	rc = smc_llc_send_confirm_rkey(link, rmb_desc);
7374600cfc3SKarsten Graul 	if (rc)
7384600cfc3SKarsten Graul 		return rc;
73944aa81ceSKarsten Graul 	/* receive CONFIRM RKEY response from server over RoCE fabric */
740ef79d439SKarsten Graul 	rc = wait_for_completion_interruptible_timeout(
741ef79d439SKarsten Graul 			&link->llc_confirm_rkey_resp, SMC_LLC_WAIT_TIME);
742ef79d439SKarsten Graul 	if (rc <= 0 || link->llc_confirm_rkey_resp_rc)
74344aa81ceSKarsten Graul 		return -EFAULT;
74444aa81ceSKarsten Graul 	return 0;
74544aa81ceSKarsten Graul }
74644aa81ceSKarsten Graul 
74760e03c62SKarsten Graul /* unregister an rtoken at the remote peer */
74860e03c62SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link *link,
74960e03c62SKarsten Graul 			   struct smc_buf_desc *rmb_desc)
75060e03c62SKarsten Graul {
7510b29ec64SUrsula Braun 	int rc = 0;
75260e03c62SKarsten Graul 
75360e03c62SKarsten Graul 	mutex_lock(&link->llc_delete_rkey_mutex);
7540b29ec64SUrsula Braun 	if (link->state != SMC_LNK_ACTIVE)
7550b29ec64SUrsula Braun 		goto out;
756ef79d439SKarsten Graul 	reinit_completion(&link->llc_delete_rkey_resp);
75760e03c62SKarsten Graul 	rc = smc_llc_send_delete_rkey(link, rmb_desc);
75860e03c62SKarsten Graul 	if (rc)
75960e03c62SKarsten Graul 		goto out;
76060e03c62SKarsten Graul 	/* receive DELETE RKEY response from server over RoCE fabric */
761ef79d439SKarsten Graul 	rc = wait_for_completion_interruptible_timeout(
762ef79d439SKarsten Graul 			&link->llc_delete_rkey_resp, SMC_LLC_WAIT_TIME);
763ef79d439SKarsten Graul 	if (rc <= 0 || link->llc_delete_rkey_resp_rc)
76460e03c62SKarsten Graul 		rc = -EFAULT;
76560e03c62SKarsten Graul 	else
76660e03c62SKarsten Graul 		rc = 0;
76760e03c62SKarsten Graul out:
76860e03c62SKarsten Graul 	mutex_unlock(&link->llc_delete_rkey_mutex);
76960e03c62SKarsten Graul 	return rc;
77060e03c62SKarsten Graul }
77160e03c62SKarsten Graul 
7729bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/
7739bf9abeaSUrsula Braun 
7749bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
7759bf9abeaSUrsula Braun 	{
7769bf9abeaSUrsula Braun 		.handler	= smc_llc_rx_handler,
7779bf9abeaSUrsula Braun 		.type		= SMC_LLC_CONFIRM_LINK
7789bf9abeaSUrsula Braun 	},
7799bf9abeaSUrsula Braun 	{
780313164daSKarsten Graul 		.handler	= smc_llc_rx_handler,
781313164daSKarsten Graul 		.type		= SMC_LLC_TEST_LINK
782313164daSKarsten Graul 	},
783313164daSKarsten Graul 	{
7844ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
78552bedf37SKarsten Graul 		.type		= SMC_LLC_ADD_LINK
78652bedf37SKarsten Graul 	},
78752bedf37SKarsten Graul 	{
78852bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
78952bedf37SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK
79052bedf37SKarsten Graul 	},
79152bedf37SKarsten Graul 	{
79252bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
7934ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY
7944ed75de5SKarsten Graul 	},
7954ed75de5SKarsten Graul 	{
7964ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
7974ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_CONT
7984ed75de5SKarsten Graul 	},
7994ed75de5SKarsten Graul 	{
8004ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
8014ed75de5SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY
8024ed75de5SKarsten Graul 	},
8034ed75de5SKarsten Graul 	{
8049bf9abeaSUrsula Braun 		.handler	= NULL,
8059bf9abeaSUrsula Braun 	}
8069bf9abeaSUrsula Braun };
8079bf9abeaSUrsula Braun 
8089bf9abeaSUrsula Braun int __init smc_llc_init(void)
8099bf9abeaSUrsula Braun {
8109bf9abeaSUrsula Braun 	struct smc_wr_rx_handler *handler;
8119bf9abeaSUrsula Braun 	int rc = 0;
8129bf9abeaSUrsula Braun 
8139bf9abeaSUrsula Braun 	for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
8149bf9abeaSUrsula Braun 		INIT_HLIST_NODE(&handler->list);
8159bf9abeaSUrsula Braun 		rc = smc_wr_rx_register_handler(handler);
8169bf9abeaSUrsula Braun 		if (rc)
8179bf9abeaSUrsula Braun 			break;
8189bf9abeaSUrsula Braun 	}
8199bf9abeaSUrsula Braun 	return rc;
8209bf9abeaSUrsula Braun }
821