xref: /linux/net/smc/smc_llc.c (revision b5dd4d6981717f7e2682c0419fe832328c7441cf)
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"
20336ba09fSKarsten Graul #include "smc_pnet.h"
219bf9abeaSUrsula Braun 
220f627126SStefan Raspl #define SMC_LLC_DATA_LEN		40
230f627126SStefan Raspl 
240f627126SStefan Raspl struct smc_llc_hdr {
250f627126SStefan Raspl 	struct smc_wr_rx_hdr common;
26b4ba4652SKarsten Graul 	union {
27b4ba4652SKarsten Graul 		struct {
280f627126SStefan Raspl 			u8 length;	/* 44 */
2952bedf37SKarsten Graul 	#if defined(__BIG_ENDIAN_BITFIELD)
3052bedf37SKarsten Graul 			u8 reserved:4,
3152bedf37SKarsten Graul 			   add_link_rej_rsn:4;
3252bedf37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD)
3352bedf37SKarsten Graul 			u8 add_link_rej_rsn:4,
3452bedf37SKarsten Graul 			   reserved:4;
3552bedf37SKarsten Graul #endif
360f627126SStefan Raspl 		};
37b4ba4652SKarsten Graul 		u16 length_v2;	/* 44 - 8192*/
38b4ba4652SKarsten Graul 	};
39b4ba4652SKarsten Graul 	u8 flags;
40b4ba4652SKarsten Graul } __packed;		/* format defined in
41b4ba4652SKarsten Graul 			 * IBM Shared Memory Communications Version 2
42b4ba4652SKarsten Graul 			 * (https://www.ibm.com/support/pages/node/6326337)
43b4ba4652SKarsten Graul 			 */
440f627126SStefan Raspl 
4575d320d6SKarsten Graul #define SMC_LLC_FLAG_NO_RMBE_EYEC	0x03
4675d320d6SKarsten Graul 
470f627126SStefan Raspl struct smc_llc_msg_confirm_link {	/* type 0x01 */
480f627126SStefan Raspl 	struct smc_llc_hdr hd;
490f627126SStefan Raspl 	u8 sender_mac[ETH_ALEN];
500f627126SStefan Raspl 	u8 sender_gid[SMC_GID_SIZE];
510f627126SStefan Raspl 	u8 sender_qp_num[3];
520f627126SStefan Raspl 	u8 link_num;
530f627126SStefan Raspl 	u8 link_uid[SMC_LGR_ID_SIZE];
540f627126SStefan Raspl 	u8 max_links;
550f627126SStefan Raspl 	u8 reserved[9];
560f627126SStefan Raspl };
570f627126SStefan Raspl 
5852bedf37SKarsten Graul #define SMC_LLC_FLAG_ADD_LNK_REJ	0x40
5952bedf37SKarsten Graul #define SMC_LLC_REJ_RSN_NO_ALT_PATH	1
6052bedf37SKarsten Graul 
6152bedf37SKarsten Graul #define SMC_LLC_ADD_LNK_MAX_LINKS	2
6252bedf37SKarsten Graul 
6352bedf37SKarsten Graul struct smc_llc_msg_add_link {		/* type 0x02 */
6452bedf37SKarsten Graul 	struct smc_llc_hdr hd;
6552bedf37SKarsten Graul 	u8 sender_mac[ETH_ALEN];
6652bedf37SKarsten Graul 	u8 reserved2[2];
6752bedf37SKarsten Graul 	u8 sender_gid[SMC_GID_SIZE];
6852bedf37SKarsten Graul 	u8 sender_qp_num[3];
6952bedf37SKarsten Graul 	u8 link_num;
70fbed3b37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD)
71fbed3b37SKarsten Graul 	u8 reserved3 : 4,
72fbed3b37SKarsten Graul 	   qp_mtu   : 4;
73fbed3b37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD)
74fbed3b37SKarsten Graul 	u8 qp_mtu   : 4,
75fbed3b37SKarsten Graul 	   reserved3 : 4;
76fbed3b37SKarsten Graul #endif
7752bedf37SKarsten Graul 	u8 initial_psn[3];
7852bedf37SKarsten Graul 	u8 reserved[8];
7952bedf37SKarsten Graul };
8052bedf37SKarsten Graul 
8187f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont_rt {
8287f88cdaSKarsten Graul 	__be32 rmb_key;
8387f88cdaSKarsten Graul 	__be32 rmb_key_new;
8487f88cdaSKarsten Graul 	__be64 rmb_vaddr_new;
8587f88cdaSKarsten Graul };
8687f88cdaSKarsten Graul 
87b4ba4652SKarsten Graul struct smc_llc_msg_add_link_v2_ext {
88b4ba4652SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD)
89b4ba4652SKarsten Graul 	u8 v2_direct : 1,
90b4ba4652SKarsten Graul 	   reserved  : 7;
91b4ba4652SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD)
92b4ba4652SKarsten Graul 	u8 reserved  : 7,
93b4ba4652SKarsten Graul 	   v2_direct : 1;
94b4ba4652SKarsten Graul #endif
95b4ba4652SKarsten Graul 	u8 reserved2;
96b4ba4652SKarsten Graul 	u8 client_target_gid[SMC_GID_SIZE];
97b4ba4652SKarsten Graul 	u8 reserved3[8];
98b4ba4652SKarsten Graul 	u16 num_rkeys;
99b4ba4652SKarsten Graul 	struct smc_llc_msg_add_link_cont_rt rt[];
100b4ba4652SKarsten Graul } __packed;		/* format defined in
101b4ba4652SKarsten Graul 			 * IBM Shared Memory Communications Version 2
102b4ba4652SKarsten Graul 			 * (https://www.ibm.com/support/pages/node/6326337)
103b4ba4652SKarsten Graul 			 */
104b4ba4652SKarsten Graul 
105b4ba4652SKarsten Graul struct smc_llc_msg_req_add_link_v2 {
106b4ba4652SKarsten Graul 	struct smc_llc_hdr hd;
107b4ba4652SKarsten Graul 	u8 reserved[20];
108b4ba4652SKarsten Graul 	u8 gid_cnt;
109b4ba4652SKarsten Graul 	u8 reserved2[3];
110b4ba4652SKarsten Graul 	u8 gid[][SMC_GID_SIZE];
111b4ba4652SKarsten Graul };
112b4ba4652SKarsten Graul 
11387f88cdaSKarsten Graul #define SMC_LLC_RKEYS_PER_CONT_MSG	2
11487f88cdaSKarsten Graul 
11587f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont {	/* type 0x03 */
11687f88cdaSKarsten Graul 	struct smc_llc_hdr hd;
11787f88cdaSKarsten Graul 	u8 link_num;
11887f88cdaSKarsten Graul 	u8 num_rkeys;
11987f88cdaSKarsten Graul 	u8 reserved2[2];
12087f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG];
12187f88cdaSKarsten Graul 	u8 reserved[4];
12287f88cdaSKarsten Graul } __packed;			/* format defined in RFC7609 */
12387f88cdaSKarsten Graul 
12452bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ALL	0x40
12552bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ORDERLY	0x20
12652bedf37SKarsten Graul 
12752bedf37SKarsten Graul struct smc_llc_msg_del_link {		/* type 0x04 */
12852bedf37SKarsten Graul 	struct smc_llc_hdr hd;
12952bedf37SKarsten Graul 	u8 link_num;
13052bedf37SKarsten Graul 	__be32 reason;
13152bedf37SKarsten Graul 	u8 reserved[35];
13252bedf37SKarsten Graul } __packed;			/* format defined in RFC7609 */
13352bedf37SKarsten Graul 
134313164daSKarsten Graul struct smc_llc_msg_test_link {		/* type 0x07 */
135313164daSKarsten Graul 	struct smc_llc_hdr hd;
136313164daSKarsten Graul 	u8 user_data[16];
137313164daSKarsten Graul 	u8 reserved[24];
138313164daSKarsten Graul };
139313164daSKarsten Graul 
1404ed75de5SKarsten Graul struct smc_rmb_rtoken {
1414ed75de5SKarsten Graul 	union {
1424ed75de5SKarsten Graul 		u8 num_rkeys;	/* first rtoken byte of CONFIRM LINK msg */
1434ed75de5SKarsten Graul 				/* is actually the num of rtokens, first */
1444ed75de5SKarsten Graul 				/* rtoken is always for the current link */
1454ed75de5SKarsten Graul 		u8 link_id;	/* link id of the rtoken */
1464ed75de5SKarsten Graul 	};
1474ed75de5SKarsten Graul 	__be32 rmb_key;
1484ed75de5SKarsten Graul 	__be64 rmb_vaddr;
1494ed75de5SKarsten Graul } __packed;			/* format defined in RFC7609 */
1504ed75de5SKarsten Graul 
1514ed75de5SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG		3
152b4ba4652SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG_V2	255
1534ed75de5SKarsten Graul 
1544ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey {	/* type 0x06 */
1554ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1564ed75de5SKarsten Graul 	struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
1574ed75de5SKarsten Graul 	u8 reserved;
1584ed75de5SKarsten Graul };
1594ed75de5SKarsten Graul 
1604ed75de5SKarsten Graul #define SMC_LLC_DEL_RKEY_MAX	8
1613bc67e09SKarsten Graul #define SMC_LLC_FLAG_RKEY_RETRY	0x10
1624ed75de5SKarsten Graul #define SMC_LLC_FLAG_RKEY_NEG	0x20
1634ed75de5SKarsten Graul 
1644ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey {	/* type 0x09 */
1654ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1664ed75de5SKarsten Graul 	u8 num_rkeys;
1674ed75de5SKarsten Graul 	u8 err_mask;
1684ed75de5SKarsten Graul 	u8 reserved[2];
1694ed75de5SKarsten Graul 	__be32 rkey[8];
1704ed75de5SKarsten Graul 	u8 reserved2[4];
1714ed75de5SKarsten Graul };
1724ed75de5SKarsten Graul 
173b4ba4652SKarsten Graul struct smc_llc_msg_delete_rkey_v2 {	/* type 0x29 */
174b4ba4652SKarsten Graul 	struct smc_llc_hdr hd;
175b4ba4652SKarsten Graul 	u8 num_rkeys;
176b4ba4652SKarsten Graul 	u8 num_inval_rkeys;
177b4ba4652SKarsten Graul 	u8 reserved[2];
178b4ba4652SKarsten Graul 	__be32 rkey[];
179b4ba4652SKarsten Graul };
180b4ba4652SKarsten Graul 
1810f627126SStefan Raspl union smc_llc_msg {
1820f627126SStefan Raspl 	struct smc_llc_msg_confirm_link confirm_link;
18352bedf37SKarsten Graul 	struct smc_llc_msg_add_link add_link;
184b4ba4652SKarsten Graul 	struct smc_llc_msg_req_add_link_v2 req_add_link;
18587f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont add_link_cont;
18652bedf37SKarsten Graul 	struct smc_llc_msg_del_link delete_link;
1874ed75de5SKarsten Graul 
1884ed75de5SKarsten Graul 	struct smc_llc_msg_confirm_rkey confirm_rkey;
1894ed75de5SKarsten Graul 	struct smc_llc_msg_delete_rkey delete_rkey;
1904ed75de5SKarsten Graul 
191313164daSKarsten Graul 	struct smc_llc_msg_test_link test_link;
1920f627126SStefan Raspl 	struct {
1930f627126SStefan Raspl 		struct smc_llc_hdr hdr;
1940f627126SStefan Raspl 		u8 data[SMC_LLC_DATA_LEN];
1950f627126SStefan Raspl 	} raw;
1960f627126SStefan Raspl };
1970f627126SStefan Raspl 
1980f627126SStefan Raspl #define SMC_LLC_FLAG_RESP		0x80
1990f627126SStefan Raspl 
2006c8968c4SKarsten Graul struct smc_llc_qentry {
2016c8968c4SKarsten Graul 	struct list_head list;
2026c8968c4SKarsten Graul 	struct smc_link *link;
2036c8968c4SKarsten Graul 	union smc_llc_msg msg;
2046c8968c4SKarsten Graul };
2056c8968c4SKarsten Graul 
2064dadd151SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc);
2074dadd151SKarsten Graul 
208555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
209555da9afSKarsten Graul {
210555da9afSKarsten Graul 	struct smc_llc_qentry *qentry = flow->qentry;
211555da9afSKarsten Graul 
212555da9afSKarsten Graul 	flow->qentry = NULL;
213555da9afSKarsten Graul 	return qentry;
214555da9afSKarsten Graul }
215555da9afSKarsten Graul 
216555da9afSKarsten Graul void smc_llc_flow_qentry_del(struct smc_llc_flow *flow)
217555da9afSKarsten Graul {
218555da9afSKarsten Graul 	struct smc_llc_qentry *qentry;
219555da9afSKarsten Graul 
220555da9afSKarsten Graul 	if (flow->qentry) {
221555da9afSKarsten Graul 		qentry = flow->qentry;
222555da9afSKarsten Graul 		flow->qentry = NULL;
223555da9afSKarsten Graul 		kfree(qentry);
224555da9afSKarsten Graul 	}
225555da9afSKarsten Graul }
226555da9afSKarsten Graul 
227555da9afSKarsten Graul static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow,
228555da9afSKarsten Graul 					   struct smc_llc_qentry *qentry)
229555da9afSKarsten Graul {
230555da9afSKarsten Graul 	flow->qentry = qentry;
231555da9afSKarsten Graul }
232555da9afSKarsten Graul 
2336778a6beSKarsten Graul static void smc_llc_flow_parallel(struct smc_link_group *lgr, u8 flow_type,
2346778a6beSKarsten Graul 				  struct smc_llc_qentry *qentry)
2356778a6beSKarsten Graul {
236b4ba4652SKarsten Graul 	u8 msg_type = qentry->msg.raw.hdr.common.llc_type;
2376778a6beSKarsten Graul 
2386778a6beSKarsten Graul 	if ((msg_type == SMC_LLC_ADD_LINK || msg_type == SMC_LLC_DELETE_LINK) &&
2396778a6beSKarsten Graul 	    flow_type != msg_type && !lgr->delayed_event) {
2406778a6beSKarsten Graul 		lgr->delayed_event = qentry;
2416778a6beSKarsten Graul 		return;
2426778a6beSKarsten Graul 	}
2436778a6beSKarsten Graul 	/* drop parallel or already-in-progress llc requests */
2446778a6beSKarsten Graul 	if (flow_type != msg_type)
245de2fea7bSTony Lu 		pr_warn_once("smc: SMC-R lg %*phN net %llu dropped parallel "
2466778a6beSKarsten Graul 			     "LLC msg: msg %d flow %d role %d\n",
2476778a6beSKarsten Graul 			     SMC_LGR_ID_SIZE, &lgr->id,
248de2fea7bSTony Lu 			     lgr->net->net_cookie,
2496778a6beSKarsten Graul 			     qentry->msg.raw.hdr.common.type,
2506778a6beSKarsten Graul 			     flow_type, lgr->role);
2516778a6beSKarsten Graul 	kfree(qentry);
2526778a6beSKarsten Graul }
2536778a6beSKarsten Graul 
254555da9afSKarsten Graul /* try to start a new llc flow, initiated by an incoming llc msg */
255555da9afSKarsten Graul static bool smc_llc_flow_start(struct smc_llc_flow *flow,
256555da9afSKarsten Graul 			       struct smc_llc_qentry *qentry)
257555da9afSKarsten Graul {
258555da9afSKarsten Graul 	struct smc_link_group *lgr = qentry->link->lgr;
259555da9afSKarsten Graul 
260555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
261555da9afSKarsten Graul 	if (flow->type) {
262555da9afSKarsten Graul 		/* a flow is already active */
2636778a6beSKarsten Graul 		smc_llc_flow_parallel(lgr, flow->type, qentry);
264555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
265555da9afSKarsten Graul 		return false;
266555da9afSKarsten Graul 	}
267b4ba4652SKarsten Graul 	switch (qentry->msg.raw.hdr.common.llc_type) {
268555da9afSKarsten Graul 	case SMC_LLC_ADD_LINK:
269555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_ADD_LINK;
270555da9afSKarsten Graul 		break;
271555da9afSKarsten Graul 	case SMC_LLC_DELETE_LINK:
272555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_DEL_LINK;
273555da9afSKarsten Graul 		break;
274555da9afSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
275555da9afSKarsten Graul 	case SMC_LLC_DELETE_RKEY:
276555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_RKEY;
277555da9afSKarsten Graul 		break;
278555da9afSKarsten Graul 	default:
279555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_NONE;
280555da9afSKarsten Graul 	}
281555da9afSKarsten Graul 	smc_llc_flow_qentry_set(flow, qentry);
2826778a6beSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
283555da9afSKarsten Graul 	return true;
284555da9afSKarsten Graul }
285555da9afSKarsten Graul 
286555da9afSKarsten Graul /* start a new local llc flow, wait till current flow finished */
287555da9afSKarsten Graul int smc_llc_flow_initiate(struct smc_link_group *lgr,
288555da9afSKarsten Graul 			  enum smc_llc_flowtype type)
289555da9afSKarsten Graul {
290555da9afSKarsten Graul 	enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE;
291555da9afSKarsten Graul 	int rc;
292555da9afSKarsten Graul 
293555da9afSKarsten Graul 	/* all flows except confirm_rkey and delete_rkey are exclusive,
294555da9afSKarsten Graul 	 * confirm/delete rkey flows can run concurrently (local and remote)
295555da9afSKarsten Graul 	 */
296555da9afSKarsten Graul 	if (type == SMC_LLC_FLOW_RKEY)
297555da9afSKarsten Graul 		allowed_remote = SMC_LLC_FLOW_RKEY;
298555da9afSKarsten Graul again:
299555da9afSKarsten Graul 	if (list_empty(&lgr->list))
300555da9afSKarsten Graul 		return -ENODEV;
301555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
302555da9afSKarsten Graul 	if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
303555da9afSKarsten Graul 	    (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
304555da9afSKarsten Graul 	     lgr->llc_flow_rmt.type == allowed_remote)) {
305555da9afSKarsten Graul 		lgr->llc_flow_lcl.type = type;
306555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
307555da9afSKarsten Graul 		return 0;
308555da9afSKarsten Graul 	}
309555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
3106778a6beSKarsten Graul 	rc = wait_event_timeout(lgr->llc_flow_waiter, (list_empty(&lgr->list) ||
311555da9afSKarsten Graul 				(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
312555da9afSKarsten Graul 				 (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
3136778a6beSKarsten Graul 				  lgr->llc_flow_rmt.type == allowed_remote))),
3146778a6beSKarsten Graul 				SMC_LLC_WAIT_TIME * 10);
315555da9afSKarsten Graul 	if (!rc)
316555da9afSKarsten Graul 		return -ETIMEDOUT;
317555da9afSKarsten Graul 	goto again;
318555da9afSKarsten Graul }
319555da9afSKarsten Graul 
320555da9afSKarsten Graul /* finish the current llc flow */
321555da9afSKarsten Graul void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)
322555da9afSKarsten Graul {
323555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
324555da9afSKarsten Graul 	memset(flow, 0, sizeof(*flow));
325555da9afSKarsten Graul 	flow->type = SMC_LLC_FLOW_NONE;
326555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
327555da9afSKarsten Graul 	if (!list_empty(&lgr->list) && lgr->delayed_event &&
328555da9afSKarsten Graul 	    flow == &lgr->llc_flow_lcl)
329555da9afSKarsten Graul 		schedule_work(&lgr->llc_event_work);
330555da9afSKarsten Graul 	else
3316778a6beSKarsten Graul 		wake_up(&lgr->llc_flow_waiter);
332555da9afSKarsten Graul }
333555da9afSKarsten Graul 
334555da9afSKarsten Graul /* lnk is optional and used for early wakeup when link goes down, useful in
335555da9afSKarsten Graul  * cases where we wait for a response on the link after we sent a request
336555da9afSKarsten Graul  */
337555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr,
338555da9afSKarsten Graul 				    struct smc_link *lnk,
339555da9afSKarsten Graul 				    int time_out, u8 exp_msg)
340555da9afSKarsten Graul {
341555da9afSKarsten Graul 	struct smc_llc_flow *flow = &lgr->llc_flow_lcl;
3426778a6beSKarsten Graul 	u8 rcv_msg;
343555da9afSKarsten Graul 
3446778a6beSKarsten Graul 	wait_event_timeout(lgr->llc_msg_waiter,
345555da9afSKarsten Graul 			   (flow->qentry ||
346555da9afSKarsten Graul 			    (lnk && !smc_link_usable(lnk)) ||
347555da9afSKarsten Graul 			    list_empty(&lgr->list)),
348555da9afSKarsten Graul 			   time_out);
349555da9afSKarsten Graul 	if (!flow->qentry ||
350555da9afSKarsten Graul 	    (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) {
351555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
352555da9afSKarsten Graul 		goto out;
353555da9afSKarsten Graul 	}
354b4ba4652SKarsten Graul 	rcv_msg = flow->qentry->msg.raw.hdr.common.llc_type;
3556778a6beSKarsten Graul 	if (exp_msg && rcv_msg != exp_msg) {
356555da9afSKarsten Graul 		if (exp_msg == SMC_LLC_ADD_LINK &&
3576778a6beSKarsten Graul 		    rcv_msg == SMC_LLC_DELETE_LINK) {
358555da9afSKarsten Graul 			/* flow_start will delay the unexpected msg */
359555da9afSKarsten Graul 			smc_llc_flow_start(&lgr->llc_flow_lcl,
360555da9afSKarsten Graul 					   smc_llc_flow_qentry_clr(flow));
361555da9afSKarsten Graul 			return NULL;
362555da9afSKarsten Graul 		}
363de2fea7bSTony Lu 		pr_warn_once("smc: SMC-R lg %*phN net %llu dropped unexpected LLC msg: "
3646778a6beSKarsten Graul 			     "msg %d exp %d flow %d role %d flags %x\n",
365de2fea7bSTony Lu 			     SMC_LGR_ID_SIZE, &lgr->id, lgr->net->net_cookie,
366de2fea7bSTony Lu 			     rcv_msg, exp_msg,
3676778a6beSKarsten Graul 			     flow->type, lgr->role,
3686778a6beSKarsten Graul 			     flow->qentry->msg.raw.hdr.flags);
369555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
370555da9afSKarsten Graul 	}
371555da9afSKarsten Graul out:
372555da9afSKarsten Graul 	return flow->qentry;
373555da9afSKarsten Graul }
374555da9afSKarsten Graul 
3759bf9abeaSUrsula Braun /********************************** send *************************************/
3769bf9abeaSUrsula Braun 
3779bf9abeaSUrsula Braun struct smc_llc_tx_pend {
3789bf9abeaSUrsula Braun };
3799bf9abeaSUrsula Braun 
3809bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */
3819bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend,
3829bf9abeaSUrsula Braun 			       struct smc_link *link,
3839bf9abeaSUrsula Braun 			       enum ib_wc_status wc_status)
3849bf9abeaSUrsula Braun {
3859bf9abeaSUrsula Braun 	/* future work: handle wc_status error for recovery and failover */
3869bf9abeaSUrsula Braun }
3879bf9abeaSUrsula Braun 
3889bf9abeaSUrsula Braun /**
3899bf9abeaSUrsula Braun  * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits
3909bf9abeaSUrsula Braun  * @link: Pointer to SMC link used for sending LLC control message.
3919bf9abeaSUrsula Braun  * @wr_buf: Out variable returning pointer to work request payload buffer.
3929bf9abeaSUrsula Braun  * @pend: Out variable returning pointer to private pending WR tracking.
3939bf9abeaSUrsula Braun  *	  It's the context the transmit complete handler will get.
3949bf9abeaSUrsula Braun  *
3959bf9abeaSUrsula Braun  * Reserves and pre-fills an entry for a pending work request send/tx.
3969bf9abeaSUrsula Braun  * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx.
3979bf9abeaSUrsula Braun  * Can sleep due to smc_get_ctrl_buf (if not in softirq context).
3989bf9abeaSUrsula Braun  *
3999bf9abeaSUrsula Braun  * Return: 0 on success, otherwise an error value.
4009bf9abeaSUrsula Braun  */
4019bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link,
4029bf9abeaSUrsula Braun 				    struct smc_wr_buf **wr_buf,
4039bf9abeaSUrsula Braun 				    struct smc_wr_tx_pend_priv **pend)
4049bf9abeaSUrsula Braun {
4059bf9abeaSUrsula Braun 	int rc;
4069bf9abeaSUrsula Braun 
407ad6f317fSUrsula Braun 	rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL,
408ad6f317fSUrsula Braun 				     pend);
4099bf9abeaSUrsula Braun 	if (rc < 0)
4109bf9abeaSUrsula Braun 		return rc;
4119bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
4129bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE,
4139bf9abeaSUrsula Braun 		"must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)");
4149bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
4159bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE,
4169bf9abeaSUrsula 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()");
4179bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
4189bf9abeaSUrsula Braun 		sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
4199bf9abeaSUrsula Braun 		"must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)");
4209bf9abeaSUrsula Braun 	return 0;
4219bf9abeaSUrsula Braun }
4229bf9abeaSUrsula Braun 
423b4ba4652SKarsten Graul static int smc_llc_add_pending_send_v2(struct smc_link *link,
424b4ba4652SKarsten Graul 				       struct smc_wr_v2_buf **wr_buf,
425b4ba4652SKarsten Graul 				       struct smc_wr_tx_pend_priv **pend)
426b4ba4652SKarsten Graul {
427b4ba4652SKarsten Graul 	int rc;
428b4ba4652SKarsten Graul 
429b4ba4652SKarsten Graul 	rc = smc_wr_tx_get_v2_slot(link, smc_llc_tx_handler, wr_buf, pend);
430b4ba4652SKarsten Graul 	if (rc < 0)
431b4ba4652SKarsten Graul 		return rc;
432b4ba4652SKarsten Graul 	return 0;
433b4ba4652SKarsten Graul }
434b4ba4652SKarsten Graul 
435b4ba4652SKarsten Graul static void smc_llc_init_msg_hdr(struct smc_llc_hdr *hdr,
436b4ba4652SKarsten Graul 				 struct smc_link_group *lgr, size_t len)
437b4ba4652SKarsten Graul {
438b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
439b4ba4652SKarsten Graul 		hdr->common.llc_version = SMC_V2;
440b4ba4652SKarsten Graul 		hdr->length_v2 = len;
441b4ba4652SKarsten Graul 	} else {
442b4ba4652SKarsten Graul 		hdr->common.llc_version = 0;
443b4ba4652SKarsten Graul 		hdr->length = len;
444b4ba4652SKarsten Graul 	}
445b4ba4652SKarsten Graul }
446b4ba4652SKarsten Graul 
4479bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */
448947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link,
4499bf9abeaSUrsula Braun 			      enum smc_llc_reqresp reqresp)
4509bf9abeaSUrsula Braun {
4519bf9abeaSUrsula Braun 	struct smc_llc_msg_confirm_link *confllc;
4529bf9abeaSUrsula Braun 	struct smc_wr_tx_pend_priv *pend;
4539bf9abeaSUrsula Braun 	struct smc_wr_buf *wr_buf;
4549bf9abeaSUrsula Braun 	int rc;
4559bf9abeaSUrsula Braun 
45695f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
45795f7f3e7SKarsten Graul 		return -ENOLINK;
4589bf9abeaSUrsula Braun 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
4599bf9abeaSUrsula Braun 	if (rc)
46095f7f3e7SKarsten Graul 		goto put_out;
4619bf9abeaSUrsula Braun 	confllc = (struct smc_llc_msg_confirm_link *)wr_buf;
4629bf9abeaSUrsula Braun 	memset(confllc, 0, sizeof(*confllc));
463b4ba4652SKarsten Graul 	confllc->hd.common.llc_type = SMC_LLC_CONFIRM_LINK;
464b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&confllc->hd, link->lgr, sizeof(*confllc));
46575d320d6SKarsten Graul 	confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC;
4669bf9abeaSUrsula Braun 	if (reqresp == SMC_LLC_RESP)
4679bf9abeaSUrsula Braun 		confllc->hd.flags |= SMC_LLC_FLAG_RESP;
468947541f3SUrsula Braun 	memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1],
469947541f3SUrsula Braun 	       ETH_ALEN);
4707005ada6SUrsula Braun 	memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE);
4719bf9abeaSUrsula Braun 	hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
4722be922f3SKarsten Graul 	confllc->link_num = link->link_id;
47345fa8da0SKarsten Graul 	memcpy(confllc->link_uid, link->link_uid, SMC_LGR_ID_SIZE);
474b1570a87SKarsten Graul 	confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS;
47552bedf37SKarsten Graul 	/* send llc message */
47652bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
47795f7f3e7SKarsten Graul put_out:
47895f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
47952bedf37SKarsten Graul 	return rc;
48052bedf37SKarsten Graul }
48152bedf37SKarsten Graul 
48244aa81ceSKarsten Graul /* send LLC confirm rkey request */
4833d88a21bSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *send_link,
48444aa81ceSKarsten Graul 				     struct smc_buf_desc *rmb_desc)
48544aa81ceSKarsten Graul {
48644aa81ceSKarsten Graul 	struct smc_llc_msg_confirm_rkey *rkeyllc;
48744aa81ceSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
48844aa81ceSKarsten Graul 	struct smc_wr_buf *wr_buf;
4893d88a21bSKarsten Graul 	struct smc_link *link;
4903d88a21bSKarsten Graul 	int i, rc, rtok_ix;
49144aa81ceSKarsten Graul 
49295f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(send_link))
49395f7f3e7SKarsten Graul 		return -ENOLINK;
4943d88a21bSKarsten Graul 	rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend);
49544aa81ceSKarsten Graul 	if (rc)
49695f7f3e7SKarsten Graul 		goto put_out;
49744aa81ceSKarsten Graul 	rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf;
49844aa81ceSKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
499b4ba4652SKarsten Graul 	rkeyllc->hd.common.llc_type = SMC_LLC_CONFIRM_RKEY;
500b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&rkeyllc->hd, send_link->lgr, sizeof(*rkeyllc));
5013d88a21bSKarsten Graul 
5023d88a21bSKarsten Graul 	rtok_ix = 1;
5033d88a21bSKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
5043d88a21bSKarsten Graul 		link = &send_link->lgr->lnk[i];
505741a49a4SKarsten Graul 		if (smc_link_active(link) && link != send_link) {
5063d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].link_id = link->link_id;
5073d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].rmb_key =
508b8d19945SWen Gu 				htonl(rmb_desc->mr[link->link_idx]->rkey);
509b8d19945SWen Gu 			rkeyllc->rtoken[rtok_ix].rmb_vaddr = rmb_desc->is_vm ?
510b8d19945SWen Gu 				cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) :
511b8d19945SWen Gu 				cpu_to_be64((u64)sg_dma_address
512b8d19945SWen Gu 					    (rmb_desc->sgt[link->link_idx].sgl));
5133d88a21bSKarsten Graul 			rtok_ix++;
5143d88a21bSKarsten Graul 		}
5153d88a21bSKarsten Graul 	}
5163d88a21bSKarsten Graul 	/* rkey of send_link is in rtoken[0] */
5173d88a21bSKarsten Graul 	rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1;
5183d88a21bSKarsten Graul 	rkeyllc->rtoken[0].rmb_key =
519b8d19945SWen Gu 		htonl(rmb_desc->mr[send_link->link_idx]->rkey);
520b8d19945SWen Gu 	rkeyllc->rtoken[0].rmb_vaddr = rmb_desc->is_vm ?
521b8d19945SWen Gu 		cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) :
522b8d19945SWen Gu 		cpu_to_be64((u64)sg_dma_address
523b8d19945SWen Gu 			    (rmb_desc->sgt[send_link->link_idx].sgl));
52444aa81ceSKarsten Graul 	/* send llc message */
5253d88a21bSKarsten Graul 	rc = smc_wr_tx_send(send_link, pend);
52695f7f3e7SKarsten Graul put_out:
52795f7f3e7SKarsten Graul 	smc_wr_tx_link_put(send_link);
52844aa81ceSKarsten Graul 	return rc;
52944aa81ceSKarsten Graul }
53044aa81ceSKarsten Graul 
53160e03c62SKarsten Graul /* send LLC delete rkey request */
53260e03c62SKarsten Graul static int smc_llc_send_delete_rkey(struct smc_link *link,
53360e03c62SKarsten Graul 				    struct smc_buf_desc *rmb_desc)
53460e03c62SKarsten Graul {
53560e03c62SKarsten Graul 	struct smc_llc_msg_delete_rkey *rkeyllc;
53660e03c62SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
53760e03c62SKarsten Graul 	struct smc_wr_buf *wr_buf;
53860e03c62SKarsten Graul 	int rc;
53960e03c62SKarsten Graul 
54095f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
54195f7f3e7SKarsten Graul 		return -ENOLINK;
54260e03c62SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
54360e03c62SKarsten Graul 	if (rc)
54495f7f3e7SKarsten Graul 		goto put_out;
54560e03c62SKarsten Graul 	rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf;
54660e03c62SKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
547b4ba4652SKarsten Graul 	rkeyllc->hd.common.llc_type = SMC_LLC_DELETE_RKEY;
548b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&rkeyllc->hd, link->lgr, sizeof(*rkeyllc));
54960e03c62SKarsten Graul 	rkeyllc->num_rkeys = 1;
550b8d19945SWen Gu 	rkeyllc->rkey[0] = htonl(rmb_desc->mr[link->link_idx]->rkey);
55160e03c62SKarsten Graul 	/* send llc message */
55260e03c62SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
55395f7f3e7SKarsten Graul put_out:
55495f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
55560e03c62SKarsten Graul 	return rc;
55660e03c62SKarsten Graul }
55760e03c62SKarsten Graul 
558b4ba4652SKarsten Graul /* return first buffer from any of the next buf lists */
559b4ba4652SKarsten Graul static struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr,
560b4ba4652SKarsten Graul 						  int *buf_lst)
561b4ba4652SKarsten Graul {
562b4ba4652SKarsten Graul 	struct smc_buf_desc *buf_pos;
563b4ba4652SKarsten Graul 
564b4ba4652SKarsten Graul 	while (*buf_lst < SMC_RMBE_SIZES) {
565b4ba4652SKarsten Graul 		buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst],
566b4ba4652SKarsten Graul 						   struct smc_buf_desc, list);
567b4ba4652SKarsten Graul 		if (buf_pos)
568b4ba4652SKarsten Graul 			return buf_pos;
569b4ba4652SKarsten Graul 		(*buf_lst)++;
570b4ba4652SKarsten Graul 	}
571b4ba4652SKarsten Graul 	return NULL;
572b4ba4652SKarsten Graul }
573b4ba4652SKarsten Graul 
574b4ba4652SKarsten Graul /* return next rmb from buffer lists */
575b4ba4652SKarsten Graul static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr,
576b4ba4652SKarsten Graul 						 int *buf_lst,
577b4ba4652SKarsten Graul 						 struct smc_buf_desc *buf_pos)
578b4ba4652SKarsten Graul {
579b4ba4652SKarsten Graul 	struct smc_buf_desc *buf_next;
580b4ba4652SKarsten Graul 
581b4ba4652SKarsten Graul 	if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) {
582b4ba4652SKarsten Graul 		(*buf_lst)++;
583b4ba4652SKarsten Graul 		return _smc_llc_get_next_rmb(lgr, buf_lst);
584b4ba4652SKarsten Graul 	}
585b4ba4652SKarsten Graul 	buf_next = list_next_entry(buf_pos, list);
586b4ba4652SKarsten Graul 	return buf_next;
587b4ba4652SKarsten Graul }
588b4ba4652SKarsten Graul 
589b4ba4652SKarsten Graul static struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr,
590b4ba4652SKarsten Graul 						  int *buf_lst)
591b4ba4652SKarsten Graul {
592b4ba4652SKarsten Graul 	*buf_lst = 0;
593b4ba4652SKarsten Graul 	return smc_llc_get_next_rmb(lgr, buf_lst, NULL);
594b4ba4652SKarsten Graul }
595b4ba4652SKarsten Graul 
596b4ba4652SKarsten Graul static int smc_llc_fill_ext_v2(struct smc_llc_msg_add_link_v2_ext *ext,
597b4ba4652SKarsten Graul 			       struct smc_link *link, struct smc_link *link_new)
598b4ba4652SKarsten Graul {
599b4ba4652SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
600b4ba4652SKarsten Graul 	struct smc_buf_desc *buf_pos;
601b4ba4652SKarsten Graul 	int prim_lnk_idx, lnk_idx, i;
602b4ba4652SKarsten Graul 	struct smc_buf_desc *rmb;
603b4ba4652SKarsten Graul 	int len = sizeof(*ext);
604b4ba4652SKarsten Graul 	int buf_lst;
605b4ba4652SKarsten Graul 
606b4ba4652SKarsten Graul 	ext->v2_direct = !lgr->uses_gateway;
607b4ba4652SKarsten Graul 	memcpy(ext->client_target_gid, link_new->gid, SMC_GID_SIZE);
608b4ba4652SKarsten Graul 
609b4ba4652SKarsten Graul 	prim_lnk_idx = link->link_idx;
610b4ba4652SKarsten Graul 	lnk_idx = link_new->link_idx;
611b4ba4652SKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
612b4ba4652SKarsten Graul 	ext->num_rkeys = lgr->conns_num;
613b4ba4652SKarsten Graul 	if (!ext->num_rkeys)
614b4ba4652SKarsten Graul 		goto out;
615b4ba4652SKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
616b4ba4652SKarsten Graul 	for (i = 0; i < ext->num_rkeys; i++) {
617b4ba4652SKarsten Graul 		if (!buf_pos)
618b4ba4652SKarsten Graul 			break;
619b4ba4652SKarsten Graul 		rmb = buf_pos;
620b8d19945SWen Gu 		ext->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey);
621b8d19945SWen Gu 		ext->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey);
622b8d19945SWen Gu 		ext->rt[i].rmb_vaddr_new = rmb->is_vm ?
623b8d19945SWen Gu 			cpu_to_be64((uintptr_t)rmb->cpu_addr) :
624b4ba4652SKarsten Graul 			cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl));
625b4ba4652SKarsten Graul 		buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos);
626b4ba4652SKarsten Graul 		while (buf_pos && !(buf_pos)->used)
627b4ba4652SKarsten Graul 			buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos);
628b4ba4652SKarsten Graul 	}
629b4ba4652SKarsten Graul 	len += i * sizeof(ext->rt[0]);
630b4ba4652SKarsten Graul out:
631b4ba4652SKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
632b4ba4652SKarsten Graul 	return len;
633b4ba4652SKarsten Graul }
634b4ba4652SKarsten Graul 
63552bedf37SKarsten Graul /* send ADD LINK request or response */
6367005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
637fbed3b37SKarsten Graul 			  struct smc_link *link_new,
63852bedf37SKarsten Graul 			  enum smc_llc_reqresp reqresp)
63952bedf37SKarsten Graul {
640b4ba4652SKarsten Graul 	struct smc_llc_msg_add_link_v2_ext *ext = NULL;
64152bedf37SKarsten Graul 	struct smc_llc_msg_add_link *addllc;
64252bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
643b4ba4652SKarsten Graul 	int len = sizeof(*addllc);
64452bedf37SKarsten Graul 	int rc;
64552bedf37SKarsten Graul 
64695f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
64795f7f3e7SKarsten Graul 		return -ENOLINK;
648b4ba4652SKarsten Graul 	if (link->lgr->smc_version == SMC_V2) {
649b4ba4652SKarsten Graul 		struct smc_wr_v2_buf *wr_buf;
650b4ba4652SKarsten Graul 
651b4ba4652SKarsten Graul 		rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend);
652b4ba4652SKarsten Graul 		if (rc)
653b4ba4652SKarsten Graul 			goto put_out;
654b4ba4652SKarsten Graul 		addllc = (struct smc_llc_msg_add_link *)wr_buf;
655b4ba4652SKarsten Graul 		ext = (struct smc_llc_msg_add_link_v2_ext *)
656b4ba4652SKarsten Graul 						&wr_buf->raw[sizeof(*addllc)];
657b4ba4652SKarsten Graul 		memset(ext, 0, SMC_WR_TX_SIZE);
658b4ba4652SKarsten Graul 	} else {
659b4ba4652SKarsten Graul 		struct smc_wr_buf *wr_buf;
660b4ba4652SKarsten Graul 
66152bedf37SKarsten Graul 		rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
66252bedf37SKarsten Graul 		if (rc)
66395f7f3e7SKarsten Graul 			goto put_out;
66452bedf37SKarsten Graul 		addllc = (struct smc_llc_msg_add_link *)wr_buf;
665b4ba4652SKarsten Graul 	}
666fbed3b37SKarsten Graul 
667fbed3b37SKarsten Graul 	memset(addllc, 0, sizeof(*addllc));
668b4ba4652SKarsten Graul 	addllc->hd.common.llc_type = SMC_LLC_ADD_LINK;
669fbed3b37SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
670fbed3b37SKarsten Graul 		addllc->hd.flags |= SMC_LLC_FLAG_RESP;
671fbed3b37SKarsten Graul 	memcpy(addllc->sender_mac, mac, ETH_ALEN);
672fbed3b37SKarsten Graul 	memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
673fbed3b37SKarsten Graul 	if (link_new) {
674fbed3b37SKarsten Graul 		addllc->link_num = link_new->link_id;
675fbed3b37SKarsten Graul 		hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num);
676fbed3b37SKarsten Graul 		hton24(addllc->initial_psn, link_new->psn_initial);
677fbed3b37SKarsten Graul 		if (reqresp == SMC_LLC_REQ)
678fbed3b37SKarsten Graul 			addllc->qp_mtu = link_new->path_mtu;
679fbed3b37SKarsten Graul 		else
680fbed3b37SKarsten Graul 			addllc->qp_mtu = min(link_new->path_mtu,
681fbed3b37SKarsten Graul 					     link_new->peer_mtu);
682fbed3b37SKarsten Graul 	}
683b4ba4652SKarsten Graul 	if (ext && link_new)
684b4ba4652SKarsten Graul 		len += smc_llc_fill_ext_v2(ext, link, link_new);
685b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&addllc->hd, link->lgr, len);
68652bedf37SKarsten Graul 	/* send llc message */
687b4ba4652SKarsten Graul 	if (link->lgr->smc_version == SMC_V2)
688b4ba4652SKarsten Graul 		rc = smc_wr_tx_v2_send(link, pend, len);
689b4ba4652SKarsten Graul 	else
69052bedf37SKarsten Graul 		rc = smc_wr_tx_send(link, pend);
69195f7f3e7SKarsten Graul put_out:
69295f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
69352bedf37SKarsten Graul 	return rc;
69452bedf37SKarsten Graul }
69552bedf37SKarsten Graul 
69652bedf37SKarsten Graul /* send DELETE LINK request or response */
697fbed3b37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id,
698fbed3b37SKarsten Graul 			     enum smc_llc_reqresp reqresp, bool orderly,
699fbed3b37SKarsten Graul 			     u32 reason)
70052bedf37SKarsten Graul {
70152bedf37SKarsten Graul 	struct smc_llc_msg_del_link *delllc;
70252bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
70352bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
70452bedf37SKarsten Graul 	int rc;
70552bedf37SKarsten Graul 
70695f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
70795f7f3e7SKarsten Graul 		return -ENOLINK;
70852bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
70952bedf37SKarsten Graul 	if (rc)
71095f7f3e7SKarsten Graul 		goto put_out;
71152bedf37SKarsten Graul 	delllc = (struct smc_llc_msg_del_link *)wr_buf;
712fbed3b37SKarsten Graul 
713fbed3b37SKarsten Graul 	memset(delllc, 0, sizeof(*delllc));
714b4ba4652SKarsten Graul 	delllc->hd.common.llc_type = SMC_LLC_DELETE_LINK;
715b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&delllc->hd, link->lgr, sizeof(*delllc));
716fbed3b37SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
717fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_RESP;
718fbed3b37SKarsten Graul 	if (orderly)
719fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
720fbed3b37SKarsten Graul 	if (link_del_id)
721fbed3b37SKarsten Graul 		delllc->link_num = link_del_id;
722fbed3b37SKarsten Graul 	else
723fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
724fbed3b37SKarsten Graul 	delllc->reason = htonl(reason);
7259bf9abeaSUrsula Braun 	/* send llc message */
7269bf9abeaSUrsula Braun 	rc = smc_wr_tx_send(link, pend);
72795f7f3e7SKarsten Graul put_out:
72895f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
7299bf9abeaSUrsula Braun 	return rc;
7309bf9abeaSUrsula Braun }
7319bf9abeaSUrsula Braun 
732d97935faSKarsten Graul /* send LLC test link request */
733d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
734313164daSKarsten Graul {
735313164daSKarsten Graul 	struct smc_llc_msg_test_link *testllc;
736313164daSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
737313164daSKarsten Graul 	struct smc_wr_buf *wr_buf;
738313164daSKarsten Graul 	int rc;
739313164daSKarsten Graul 
74095f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
74195f7f3e7SKarsten Graul 		return -ENOLINK;
742313164daSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
743313164daSKarsten Graul 	if (rc)
74495f7f3e7SKarsten Graul 		goto put_out;
745313164daSKarsten Graul 	testllc = (struct smc_llc_msg_test_link *)wr_buf;
746313164daSKarsten Graul 	memset(testllc, 0, sizeof(*testllc));
747b4ba4652SKarsten Graul 	testllc->hd.common.llc_type = SMC_LLC_TEST_LINK;
748b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&testllc->hd, link->lgr, sizeof(*testllc));
749313164daSKarsten Graul 	memcpy(testllc->user_data, user_data, sizeof(testllc->user_data));
750313164daSKarsten Graul 	/* send llc message */
751313164daSKarsten Graul 	rc = smc_wr_tx_send(link, pend);
75295f7f3e7SKarsten Graul put_out:
75395f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
754313164daSKarsten Graul 	return rc;
755313164daSKarsten Graul }
756313164daSKarsten Graul 
7576c8968c4SKarsten Graul /* schedule an llc send on link, may wait for buffers */
7586c8968c4SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf)
7594ed75de5SKarsten Graul {
7604ed75de5SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
7614ed75de5SKarsten Graul 	struct smc_wr_buf *wr_buf;
7624ed75de5SKarsten Graul 	int rc;
7634ed75de5SKarsten Graul 
76495f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
7656c8968c4SKarsten Graul 		return -ENOLINK;
7666c8968c4SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
7674ed75de5SKarsten Graul 	if (rc)
76895f7f3e7SKarsten Graul 		goto put_out;
7696c8968c4SKarsten Graul 	memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
77095f7f3e7SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
77195f7f3e7SKarsten Graul put_out:
77295f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
77395f7f3e7SKarsten Graul 	return rc;
7744ed75de5SKarsten Graul }
7754ed75de5SKarsten Graul 
776f3811fd7SKarsten Graul /* schedule an llc send on link, may wait for buffers,
777f3811fd7SKarsten Graul  * and wait for send completion notification.
778f3811fd7SKarsten Graul  * @return 0 on success
779f3811fd7SKarsten Graul  */
780f3811fd7SKarsten Graul static int smc_llc_send_message_wait(struct smc_link *link, void *llcbuf)
781f3811fd7SKarsten Graul {
782f3811fd7SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
783f3811fd7SKarsten Graul 	struct smc_wr_buf *wr_buf;
784f3811fd7SKarsten Graul 	int rc;
785f3811fd7SKarsten Graul 
78695f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
787f3811fd7SKarsten Graul 		return -ENOLINK;
788f3811fd7SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
789f3811fd7SKarsten Graul 	if (rc)
79095f7f3e7SKarsten Graul 		goto put_out;
791f3811fd7SKarsten Graul 	memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
79295f7f3e7SKarsten Graul 	rc = smc_wr_tx_send_wait(link, pend, SMC_LLC_WAIT_TIME);
79395f7f3e7SKarsten Graul put_out:
79495f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
79595f7f3e7SKarsten Graul 	return rc;
796f3811fd7SKarsten Graul }
797f3811fd7SKarsten Graul 
7989bf9abeaSUrsula Braun /********************************* receive ***********************************/
7999bf9abeaSUrsula Braun 
800336ba09fSKarsten Graul static int smc_llc_alloc_alt_link(struct smc_link_group *lgr,
801336ba09fSKarsten Graul 				  enum smc_lgr_type lgr_new_t)
802336ba09fSKarsten Graul {
803336ba09fSKarsten Graul 	int i;
804336ba09fSKarsten Graul 
805336ba09fSKarsten Graul 	if (lgr->type == SMC_LGR_SYMMETRIC ||
806336ba09fSKarsten Graul 	    (lgr->type != SMC_LGR_SINGLE &&
807336ba09fSKarsten Graul 	     (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
808336ba09fSKarsten Graul 	      lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)))
809336ba09fSKarsten Graul 		return -EMLINK;
810336ba09fSKarsten Graul 
811336ba09fSKarsten Graul 	if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
812336ba09fSKarsten Graul 	    lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) {
813336ba09fSKarsten Graul 		for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--)
814336ba09fSKarsten Graul 			if (lgr->lnk[i].state == SMC_LNK_UNUSED)
815336ba09fSKarsten Graul 				return i;
816336ba09fSKarsten Graul 	} else {
817336ba09fSKarsten Graul 		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
818336ba09fSKarsten Graul 			if (lgr->lnk[i].state == SMC_LNK_UNUSED)
819336ba09fSKarsten Graul 				return i;
820336ba09fSKarsten Graul 	}
821336ba09fSKarsten Graul 	return -EMLINK;
822336ba09fSKarsten Graul }
823336ba09fSKarsten Graul 
82487f88cdaSKarsten Graul /* send one add_link_continue msg */
82587f88cdaSKarsten Graul static int smc_llc_add_link_cont(struct smc_link *link,
82687f88cdaSKarsten Graul 				 struct smc_link *link_new, u8 *num_rkeys_todo,
82787f88cdaSKarsten Graul 				 int *buf_lst, struct smc_buf_desc **buf_pos)
82887f88cdaSKarsten Graul {
82987f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
83087f88cdaSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
83187f88cdaSKarsten Graul 	int prim_lnk_idx, lnk_idx, i, rc;
83287f88cdaSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
83387f88cdaSKarsten Graul 	struct smc_wr_buf *wr_buf;
83487f88cdaSKarsten Graul 	struct smc_buf_desc *rmb;
83587f88cdaSKarsten Graul 	u8 n;
83687f88cdaSKarsten Graul 
83795f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
83895f7f3e7SKarsten Graul 		return -ENOLINK;
83987f88cdaSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
84087f88cdaSKarsten Graul 	if (rc)
84195f7f3e7SKarsten Graul 		goto put_out;
84287f88cdaSKarsten Graul 	addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf;
84387f88cdaSKarsten Graul 	memset(addc_llc, 0, sizeof(*addc_llc));
84487f88cdaSKarsten Graul 
84587f88cdaSKarsten Graul 	prim_lnk_idx = link->link_idx;
84687f88cdaSKarsten Graul 	lnk_idx = link_new->link_idx;
84787f88cdaSKarsten Graul 	addc_llc->link_num = link_new->link_id;
84887f88cdaSKarsten Graul 	addc_llc->num_rkeys = *num_rkeys_todo;
84987f88cdaSKarsten Graul 	n = *num_rkeys_todo;
85087f88cdaSKarsten Graul 	for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) {
85187f88cdaSKarsten Graul 		if (!*buf_pos) {
85287f88cdaSKarsten Graul 			addc_llc->num_rkeys = addc_llc->num_rkeys -
85387f88cdaSKarsten Graul 					      *num_rkeys_todo;
85487f88cdaSKarsten Graul 			*num_rkeys_todo = 0;
85587f88cdaSKarsten Graul 			break;
85687f88cdaSKarsten Graul 		}
85787f88cdaSKarsten Graul 		rmb = *buf_pos;
85887f88cdaSKarsten Graul 
859b8d19945SWen Gu 		addc_llc->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey);
860b8d19945SWen Gu 		addc_llc->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey);
861b8d19945SWen Gu 		addc_llc->rt[i].rmb_vaddr_new = rmb->is_vm ?
862b8d19945SWen Gu 			cpu_to_be64((uintptr_t)rmb->cpu_addr) :
86387f88cdaSKarsten Graul 			cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl));
86487f88cdaSKarsten Graul 
86587f88cdaSKarsten Graul 		(*num_rkeys_todo)--;
86687f88cdaSKarsten Graul 		*buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
86787f88cdaSKarsten Graul 		while (*buf_pos && !(*buf_pos)->used)
86887f88cdaSKarsten Graul 			*buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
86987f88cdaSKarsten Graul 	}
870b4ba4652SKarsten Graul 	addc_llc->hd.common.llc_type = SMC_LLC_ADD_LINK_CONT;
87187f88cdaSKarsten Graul 	addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont);
87287f88cdaSKarsten Graul 	if (lgr->role == SMC_CLNT)
87387f88cdaSKarsten Graul 		addc_llc->hd.flags |= SMC_LLC_FLAG_RESP;
87495f7f3e7SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
87595f7f3e7SKarsten Graul put_out:
87695f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
87795f7f3e7SKarsten Graul 	return rc;
87887f88cdaSKarsten Graul }
87987f88cdaSKarsten Graul 
88087f88cdaSKarsten Graul static int smc_llc_cli_rkey_exchange(struct smc_link *link,
88187f88cdaSKarsten Graul 				     struct smc_link *link_new)
88287f88cdaSKarsten Graul {
88387f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
88487f88cdaSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
88587f88cdaSKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
88687f88cdaSKarsten Graul 	struct smc_llc_qentry *qentry;
88787f88cdaSKarsten Graul 	struct smc_buf_desc *buf_pos;
88887f88cdaSKarsten Graul 	int buf_lst;
88987f88cdaSKarsten Graul 	int rc = 0;
89087f88cdaSKarsten Graul 	int i;
89187f88cdaSKarsten Graul 
89287f88cdaSKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
89387f88cdaSKarsten Graul 	num_rkeys_send = lgr->conns_num;
89487f88cdaSKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
89587f88cdaSKarsten Graul 	do {
89687f88cdaSKarsten Graul 		qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME,
89787f88cdaSKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
89887f88cdaSKarsten Graul 		if (!qentry) {
89987f88cdaSKarsten Graul 			rc = -ETIMEDOUT;
90087f88cdaSKarsten Graul 			break;
90187f88cdaSKarsten Graul 		}
90287f88cdaSKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
90387f88cdaSKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
90487f88cdaSKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
90587f88cdaSKarsten Graul 		for (i = 0; i < max; i++) {
90687f88cdaSKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
90787f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_key,
90887f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
90987f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
91087f88cdaSKarsten Graul 			num_rkeys_recv--;
91187f88cdaSKarsten Graul 		}
91287f88cdaSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
91387f88cdaSKarsten Graul 		rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
91487f88cdaSKarsten Graul 					   &buf_lst, &buf_pos);
91587f88cdaSKarsten Graul 		if (rc)
91687f88cdaSKarsten Graul 			break;
91787f88cdaSKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
91887f88cdaSKarsten Graul 
91987f88cdaSKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
92087f88cdaSKarsten Graul 	return rc;
92187f88cdaSKarsten Graul }
92287f88cdaSKarsten Graul 
923336ba09fSKarsten Graul /* prepare and send an add link reject response */
924336ba09fSKarsten Graul static int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry)
925336ba09fSKarsten Graul {
926336ba09fSKarsten Graul 	qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
927336ba09fSKarsten Graul 	qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
928336ba09fSKarsten Graul 	qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
929b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr,
930b4ba4652SKarsten Graul 			     sizeof(qentry->msg));
931336ba09fSKarsten Graul 	return smc_llc_send_message(qentry->link, &qentry->msg);
932336ba09fSKarsten Graul }
933336ba09fSKarsten Graul 
934b1570a87SKarsten Graul static int smc_llc_cli_conf_link(struct smc_link *link,
935b1570a87SKarsten Graul 				 struct smc_init_info *ini,
936b1570a87SKarsten Graul 				 struct smc_link *link_new,
937b1570a87SKarsten Graul 				 enum smc_lgr_type lgr_new_t)
938b1570a87SKarsten Graul {
939b1570a87SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
940b1570a87SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
941b1570a87SKarsten Graul 	int rc = 0;
942b1570a87SKarsten Graul 
943b1570a87SKarsten Graul 	/* receive CONFIRM LINK request over RoCE fabric */
944b1570a87SKarsten Graul 	qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0);
945b1570a87SKarsten Graul 	if (!qentry) {
946b1570a87SKarsten Graul 		rc = smc_llc_send_delete_link(link, link_new->link_id,
947b1570a87SKarsten Graul 					      SMC_LLC_REQ, false,
948b1570a87SKarsten Graul 					      SMC_LLC_DEL_LOST_PATH);
949b1570a87SKarsten Graul 		return -ENOLINK;
950b1570a87SKarsten Graul 	}
951b4ba4652SKarsten Graul 	if (qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) {
952b1570a87SKarsten Graul 		/* received DELETE_LINK instead */
953b1570a87SKarsten Graul 		qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
954b1570a87SKarsten Graul 		smc_llc_send_message(link, &qentry->msg);
955b1570a87SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
956b1570a87SKarsten Graul 		return -ENOLINK;
957b1570a87SKarsten Graul 	}
958649758ffSKarsten Graul 	smc_llc_save_peer_uid(qentry);
959b1570a87SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
960b1570a87SKarsten Graul 
961b1570a87SKarsten Graul 	rc = smc_ib_modify_qp_rts(link_new);
962b1570a87SKarsten Graul 	if (rc) {
963b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
964b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
965b1570a87SKarsten Graul 		return -ENOLINK;
966b1570a87SKarsten Graul 	}
967b1570a87SKarsten Graul 	smc_wr_remember_qp_attr(link_new);
968b1570a87SKarsten Graul 
969b1570a87SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
970b1570a87SKarsten Graul 	if (rc) {
971b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
972b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
973b1570a87SKarsten Graul 		return -ENOLINK;
974b1570a87SKarsten Graul 	}
975b1570a87SKarsten Graul 
976b1570a87SKarsten Graul 	/* send CONFIRM LINK response over RoCE fabric */
977b1570a87SKarsten Graul 	rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP);
978b1570a87SKarsten Graul 	if (rc) {
979b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
980b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
981b1570a87SKarsten Graul 		return -ENOLINK;
982b1570a87SKarsten Graul 	}
983b1570a87SKarsten Graul 	smc_llc_link_active(link_new);
984ad6c111bSKarsten Graul 	if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
985ad6c111bSKarsten Graul 	    lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)
986ad6c111bSKarsten Graul 		smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx);
987ad6c111bSKarsten Graul 	else
988ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, lgr_new_t);
989b1570a87SKarsten Graul 	return 0;
990b1570a87SKarsten Graul }
991b1570a87SKarsten Graul 
992b4ba4652SKarsten Graul static void smc_llc_save_add_link_rkeys(struct smc_link *link,
993b4ba4652SKarsten Graul 					struct smc_link *link_new)
994b4ba4652SKarsten Graul {
995b4ba4652SKarsten Graul 	struct smc_llc_msg_add_link_v2_ext *ext;
996b4ba4652SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
997b4ba4652SKarsten Graul 	int max, i;
998b4ba4652SKarsten Graul 
999b4ba4652SKarsten Graul 	ext = (struct smc_llc_msg_add_link_v2_ext *)((u8 *)lgr->wr_rx_buf_v2 +
1000b4ba4652SKarsten Graul 						     SMC_WR_TX_SIZE);
1001b4ba4652SKarsten Graul 	max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
1002b4ba4652SKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
1003b4ba4652SKarsten Graul 	for (i = 0; i < max; i++) {
1004b4ba4652SKarsten Graul 		smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
1005b4ba4652SKarsten Graul 			       ext->rt[i].rmb_key,
1006b4ba4652SKarsten Graul 			       ext->rt[i].rmb_vaddr_new,
1007b4ba4652SKarsten Graul 			       ext->rt[i].rmb_key_new);
1008b4ba4652SKarsten Graul 	}
1009b4ba4652SKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
1010b4ba4652SKarsten Graul }
1011b4ba4652SKarsten Graul 
1012336ba09fSKarsten Graul static void smc_llc_save_add_link_info(struct smc_link *link,
1013336ba09fSKarsten Graul 				       struct smc_llc_msg_add_link *add_llc)
1014336ba09fSKarsten Graul {
1015336ba09fSKarsten Graul 	link->peer_qpn = ntoh24(add_llc->sender_qp_num);
1016336ba09fSKarsten Graul 	memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE);
1017336ba09fSKarsten Graul 	memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN);
1018336ba09fSKarsten Graul 	link->peer_psn = ntoh24(add_llc->initial_psn);
1019336ba09fSKarsten Graul 	link->peer_mtu = add_llc->qp_mtu;
1020336ba09fSKarsten Graul }
1021336ba09fSKarsten Graul 
1022336ba09fSKarsten Graul /* as an SMC client, process an add link request */
1023336ba09fSKarsten Graul int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
1024336ba09fSKarsten Graul {
1025336ba09fSKarsten Graul 	struct smc_llc_msg_add_link *llc = &qentry->msg.add_link;
1026336ba09fSKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
1027336ba09fSKarsten Graul 	struct smc_link_group *lgr = smc_get_lgr(link);
1028ed990df2SKarsten Graul 	struct smc_init_info *ini = NULL;
1029336ba09fSKarsten Graul 	struct smc_link *lnk_new = NULL;
1030336ba09fSKarsten Graul 	int lnk_idx, rc = 0;
1031336ba09fSKarsten Graul 
1032fffe83c8SKarsten Graul 	if (!llc->qp_mtu)
1033fffe83c8SKarsten Graul 		goto out_reject;
1034fffe83c8SKarsten Graul 
1035ed990df2SKarsten Graul 	ini = kzalloc(sizeof(*ini), GFP_KERNEL);
1036ed990df2SKarsten Graul 	if (!ini) {
1037ed990df2SKarsten Graul 		rc = -ENOMEM;
1038ed990df2SKarsten Graul 		goto out_reject;
1039ed990df2SKarsten Graul 	}
1040ed990df2SKarsten Graul 
1041ed990df2SKarsten Graul 	ini->vlan_id = lgr->vlan_id;
1042b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
1043b4ba4652SKarsten Graul 		ini->check_smcrv2 = true;
1044b4ba4652SKarsten Graul 		ini->smcrv2.saddr = lgr->saddr;
1045b4ba4652SKarsten Graul 		ini->smcrv2.daddr = smc_ib_gid_to_ipv4(llc->sender_gid);
1046b4ba4652SKarsten Graul 	}
1047ed990df2SKarsten Graul 	smc_pnet_find_alt_roce(lgr, ini, link->smcibdev);
1048336ba09fSKarsten Graul 	if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
1049b4ba4652SKarsten Graul 	    (lgr->smc_version == SMC_V2 ||
1050b4ba4652SKarsten Graul 	     !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN))) {
1051b4ba4652SKarsten Graul 		if (!ini->ib_dev && !ini->smcrv2.ib_dev_v2)
1052336ba09fSKarsten Graul 			goto out_reject;
1053336ba09fSKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
1054336ba09fSKarsten Graul 	}
1055b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) {
1056b4ba4652SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
1057b4ba4652SKarsten Graul 		ini->smcrv2.ib_dev_v2 = link->smcibdev;
1058b4ba4652SKarsten Graul 		ini->smcrv2.ib_port_v2 = link->ibport;
1059b4ba4652SKarsten Graul 	} else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) {
1060336ba09fSKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
1061ed990df2SKarsten Graul 		ini->ib_dev = link->smcibdev;
1062ed990df2SKarsten Graul 		ini->ib_port = link->ibport;
1063336ba09fSKarsten Graul 	}
1064336ba09fSKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
1065336ba09fSKarsten Graul 	if (lnk_idx < 0)
1066336ba09fSKarsten Graul 		goto out_reject;
1067336ba09fSKarsten Graul 	lnk_new = &lgr->lnk[lnk_idx];
1068ed990df2SKarsten Graul 	rc = smcr_link_init(lgr, lnk_new, lnk_idx, ini);
1069336ba09fSKarsten Graul 	if (rc)
1070336ba09fSKarsten Graul 		goto out_reject;
1071336ba09fSKarsten Graul 	smc_llc_save_add_link_info(lnk_new, llc);
107245fa8da0SKarsten Graul 	lnk_new->link_id = llc->link_num;	/* SMC server assigns link id */
107345fa8da0SKarsten Graul 	smc_llc_link_set_uid(lnk_new);
1074336ba09fSKarsten Graul 
1075336ba09fSKarsten Graul 	rc = smc_ib_ready_link(lnk_new);
1076336ba09fSKarsten Graul 	if (rc)
1077336ba09fSKarsten Graul 		goto out_clear_lnk;
1078336ba09fSKarsten Graul 
1079336ba09fSKarsten Graul 	rc = smcr_buf_map_lgr(lnk_new);
1080336ba09fSKarsten Graul 	if (rc)
1081336ba09fSKarsten Graul 		goto out_clear_lnk;
1082336ba09fSKarsten Graul 
1083336ba09fSKarsten Graul 	rc = smc_llc_send_add_link(link,
1084b4ba4652SKarsten Graul 				   lnk_new->smcibdev->mac[lnk_new->ibport - 1],
1085336ba09fSKarsten Graul 				   lnk_new->gid, lnk_new, SMC_LLC_RESP);
1086336ba09fSKarsten Graul 	if (rc)
1087336ba09fSKarsten Graul 		goto out_clear_lnk;
1088b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
1089b4ba4652SKarsten Graul 		smc_llc_save_add_link_rkeys(link, lnk_new);
1090b4ba4652SKarsten Graul 	} else {
109187f88cdaSKarsten Graul 		rc = smc_llc_cli_rkey_exchange(link, lnk_new);
1092336ba09fSKarsten Graul 		if (rc) {
1093336ba09fSKarsten Graul 			rc = 0;
1094336ba09fSKarsten Graul 			goto out_clear_lnk;
1095336ba09fSKarsten Graul 		}
1096b4ba4652SKarsten Graul 	}
1097ed990df2SKarsten Graul 	rc = smc_llc_cli_conf_link(link, ini, lnk_new, lgr_new_t);
1098336ba09fSKarsten Graul 	if (!rc)
1099336ba09fSKarsten Graul 		goto out;
1100336ba09fSKarsten Graul out_clear_lnk:
11018f3d65c1SKarsten Graul 	lnk_new->state = SMC_LNK_INACTIVE;
11020a99be43SKarsten Graul 	smcr_link_clear(lnk_new, false);
1103336ba09fSKarsten Graul out_reject:
1104336ba09fSKarsten Graul 	smc_llc_cli_add_link_reject(qentry);
1105336ba09fSKarsten Graul out:
1106ed990df2SKarsten Graul 	kfree(ini);
1107336ba09fSKarsten Graul 	kfree(qentry);
1108336ba09fSKarsten Graul 	return rc;
1109336ba09fSKarsten Graul }
1110336ba09fSKarsten Graul 
1111b4ba4652SKarsten Graul static void smc_llc_send_request_add_link(struct smc_link *link)
1112b4ba4652SKarsten Graul {
1113b4ba4652SKarsten Graul 	struct smc_llc_msg_req_add_link_v2 *llc;
1114b4ba4652SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
1115b4ba4652SKarsten Graul 	struct smc_wr_v2_buf *wr_buf;
1116b4ba4652SKarsten Graul 	struct smc_gidlist gidlist;
1117b4ba4652SKarsten Graul 	int rc, len, i;
1118b4ba4652SKarsten Graul 
1119b4ba4652SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
1120b4ba4652SKarsten Graul 		return;
1121b4ba4652SKarsten Graul 	if (link->lgr->type == SMC_LGR_SYMMETRIC ||
1122b4ba4652SKarsten Graul 	    link->lgr->type == SMC_LGR_ASYMMETRIC_PEER)
1123b4ba4652SKarsten Graul 		goto put_out;
1124b4ba4652SKarsten Graul 
1125b4ba4652SKarsten Graul 	smc_fill_gid_list(link->lgr, &gidlist, link->smcibdev, link->gid);
1126b4ba4652SKarsten Graul 	if (gidlist.len <= 1)
1127b4ba4652SKarsten Graul 		goto put_out;
1128b4ba4652SKarsten Graul 
1129b4ba4652SKarsten Graul 	rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend);
1130b4ba4652SKarsten Graul 	if (rc)
1131b4ba4652SKarsten Graul 		goto put_out;
1132b4ba4652SKarsten Graul 	llc = (struct smc_llc_msg_req_add_link_v2 *)wr_buf;
1133b4ba4652SKarsten Graul 	memset(llc, 0, SMC_WR_TX_SIZE);
1134b4ba4652SKarsten Graul 
1135b4ba4652SKarsten Graul 	llc->hd.common.llc_type = SMC_LLC_REQ_ADD_LINK;
1136b4ba4652SKarsten Graul 	for (i = 0; i < gidlist.len; i++)
1137b4ba4652SKarsten Graul 		memcpy(llc->gid[i], gidlist.list[i], sizeof(gidlist.list[0]));
1138b4ba4652SKarsten Graul 	llc->gid_cnt = gidlist.len;
1139b4ba4652SKarsten Graul 	len = sizeof(*llc) + (gidlist.len * sizeof(gidlist.list[0]));
1140b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&llc->hd, link->lgr, len);
1141b4ba4652SKarsten Graul 	rc = smc_wr_tx_v2_send(link, pend, len);
1142b4ba4652SKarsten Graul 	if (!rc)
1143b4ba4652SKarsten Graul 		/* set REQ_ADD_LINK flow and wait for response from peer */
1144b4ba4652SKarsten Graul 		link->lgr->llc_flow_lcl.type = SMC_LLC_FLOW_REQ_ADD_LINK;
1145b4ba4652SKarsten Graul put_out:
1146b4ba4652SKarsten Graul 	smc_wr_tx_link_put(link);
1147b4ba4652SKarsten Graul }
1148b4ba4652SKarsten Graul 
1149c48254faSKarsten Graul /* as an SMC client, invite server to start the add_link processing */
1150c48254faSKarsten Graul static void smc_llc_cli_add_link_invite(struct smc_link *link,
1151c48254faSKarsten Graul 					struct smc_llc_qentry *qentry)
1152c48254faSKarsten Graul {
1153c48254faSKarsten Graul 	struct smc_link_group *lgr = smc_get_lgr(link);
1154ed990df2SKarsten Graul 	struct smc_init_info *ini = NULL;
1155c48254faSKarsten Graul 
1156b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
1157b4ba4652SKarsten Graul 		smc_llc_send_request_add_link(link);
1158b4ba4652SKarsten Graul 		goto out;
1159b4ba4652SKarsten Graul 	}
1160b4ba4652SKarsten Graul 
1161c48254faSKarsten Graul 	if (lgr->type == SMC_LGR_SYMMETRIC ||
1162c48254faSKarsten Graul 	    lgr->type == SMC_LGR_ASYMMETRIC_PEER)
1163c48254faSKarsten Graul 		goto out;
1164c48254faSKarsten Graul 
1165ed990df2SKarsten Graul 	ini = kzalloc(sizeof(*ini), GFP_KERNEL);
1166ed990df2SKarsten Graul 	if (!ini)
1167c48254faSKarsten Graul 		goto out;
1168c48254faSKarsten Graul 
1169ed990df2SKarsten Graul 	ini->vlan_id = lgr->vlan_id;
1170ed990df2SKarsten Graul 	smc_pnet_find_alt_roce(lgr, ini, link->smcibdev);
1171ed990df2SKarsten Graul 	if (!ini->ib_dev)
1172ed990df2SKarsten Graul 		goto out;
1173ed990df2SKarsten Graul 
1174ed990df2SKarsten Graul 	smc_llc_send_add_link(link, ini->ib_dev->mac[ini->ib_port - 1],
1175ed990df2SKarsten Graul 			      ini->ib_gid, NULL, SMC_LLC_REQ);
1176c48254faSKarsten Graul out:
1177ed990df2SKarsten Graul 	kfree(ini);
1178c48254faSKarsten Graul 	kfree(qentry);
1179c48254faSKarsten Graul }
1180c48254faSKarsten Graul 
1181fffe83c8SKarsten Graul static bool smc_llc_is_empty_llc_message(union smc_llc_msg *llc)
1182fffe83c8SKarsten Graul {
1183fffe83c8SKarsten Graul 	int i;
1184fffe83c8SKarsten Graul 
1185fffe83c8SKarsten Graul 	for (i = 0; i < ARRAY_SIZE(llc->raw.data); i++)
1186fffe83c8SKarsten Graul 		if (llc->raw.data[i])
1187fffe83c8SKarsten Graul 			return false;
1188fffe83c8SKarsten Graul 	return true;
1189fffe83c8SKarsten Graul }
1190fffe83c8SKarsten Graul 
1191c48254faSKarsten Graul static bool smc_llc_is_local_add_link(union smc_llc_msg *llc)
1192c48254faSKarsten Graul {
1193b4ba4652SKarsten Graul 	if (llc->raw.hdr.common.llc_type == SMC_LLC_ADD_LINK &&
1194fffe83c8SKarsten Graul 	    smc_llc_is_empty_llc_message(llc))
1195c48254faSKarsten Graul 		return true;
1196c48254faSKarsten Graul 	return false;
1197c48254faSKarsten Graul }
1198c48254faSKarsten Graul 
1199b1570a87SKarsten Graul static void smc_llc_process_cli_add_link(struct smc_link_group *lgr)
1200b1570a87SKarsten Graul {
1201b1570a87SKarsten Graul 	struct smc_llc_qentry *qentry;
1202b1570a87SKarsten Graul 
1203b1570a87SKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
1204b1570a87SKarsten Graul 
1205*b5dd4d69SD. Wythe 	down_write(&lgr->llc_conf_mutex);
1206c48254faSKarsten Graul 	if (smc_llc_is_local_add_link(&qentry->msg))
1207c48254faSKarsten Graul 		smc_llc_cli_add_link_invite(qentry->link, qentry);
1208c48254faSKarsten Graul 	else
1209b1570a87SKarsten Graul 		smc_llc_cli_add_link(qentry->link, qentry);
1210*b5dd4d69SD. Wythe 	up_write(&lgr->llc_conf_mutex);
1211b1570a87SKarsten Graul }
1212b1570a87SKarsten Graul 
12139c416878SKarsten Graul static int smc_llc_active_link_count(struct smc_link_group *lgr)
12149c416878SKarsten Graul {
12159c416878SKarsten Graul 	int i, link_count = 0;
12169c416878SKarsten Graul 
12179c416878SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1218741a49a4SKarsten Graul 		if (!smc_link_active(&lgr->lnk[i]))
12199c416878SKarsten Graul 			continue;
12209c416878SKarsten Graul 		link_count++;
12219c416878SKarsten Graul 	}
12229c416878SKarsten Graul 	return link_count;
12239c416878SKarsten Graul }
12249c416878SKarsten Graul 
1225c9a5d243SKarsten Graul /* find the asymmetric link when 3 links are established  */
1226c9a5d243SKarsten Graul static struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr)
1227c9a5d243SKarsten Graul {
1228c9a5d243SKarsten Graul 	int asym_idx = -ENOENT;
1229c9a5d243SKarsten Graul 	int i, j, k;
1230c9a5d243SKarsten Graul 	bool found;
1231c9a5d243SKarsten Graul 
1232c9a5d243SKarsten Graul 	/* determine asymmetric link */
1233c9a5d243SKarsten Graul 	found = false;
1234c9a5d243SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1235c9a5d243SKarsten Graul 		for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) {
1236c9a5d243SKarsten Graul 			if (!smc_link_usable(&lgr->lnk[i]) ||
1237c9a5d243SKarsten Graul 			    !smc_link_usable(&lgr->lnk[j]))
1238c9a5d243SKarsten Graul 				continue;
1239c9a5d243SKarsten Graul 			if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid,
1240c9a5d243SKarsten Graul 				    SMC_GID_SIZE)) {
1241c9a5d243SKarsten Graul 				found = true;	/* asym_lnk is i or j */
1242c9a5d243SKarsten Graul 				break;
1243c9a5d243SKarsten Graul 			}
1244c9a5d243SKarsten Graul 		}
1245c9a5d243SKarsten Graul 		if (found)
1246c9a5d243SKarsten Graul 			break;
1247c9a5d243SKarsten Graul 	}
1248c9a5d243SKarsten Graul 	if (!found)
1249c9a5d243SKarsten Graul 		goto out; /* no asymmetric link */
1250c9a5d243SKarsten Graul 	for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) {
1251c9a5d243SKarsten Graul 		if (!smc_link_usable(&lgr->lnk[k]))
1252c9a5d243SKarsten Graul 			continue;
1253c9a5d243SKarsten Graul 		if (k != i &&
1254c9a5d243SKarsten Graul 		    !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid,
1255c9a5d243SKarsten Graul 			    SMC_GID_SIZE)) {
1256c9a5d243SKarsten Graul 			asym_idx = i;
1257c9a5d243SKarsten Graul 			break;
1258c9a5d243SKarsten Graul 		}
1259c9a5d243SKarsten Graul 		if (k != j &&
1260c9a5d243SKarsten Graul 		    !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid,
1261c9a5d243SKarsten Graul 			    SMC_GID_SIZE)) {
1262c9a5d243SKarsten Graul 			asym_idx = j;
1263c9a5d243SKarsten Graul 			break;
1264c9a5d243SKarsten Graul 		}
1265c9a5d243SKarsten Graul 	}
1266c9a5d243SKarsten Graul out:
1267c9a5d243SKarsten Graul 	return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx];
1268c9a5d243SKarsten Graul }
1269c9a5d243SKarsten Graul 
1270c9a5d243SKarsten Graul static void smc_llc_delete_asym_link(struct smc_link_group *lgr)
1271c9a5d243SKarsten Graul {
1272c9a5d243SKarsten Graul 	struct smc_link *lnk_new = NULL, *lnk_asym;
1273c9a5d243SKarsten Graul 	struct smc_llc_qentry *qentry;
1274c9a5d243SKarsten Graul 	int rc;
1275c9a5d243SKarsten Graul 
1276c9a5d243SKarsten Graul 	lnk_asym = smc_llc_find_asym_link(lgr);
1277c9a5d243SKarsten Graul 	if (!lnk_asym)
1278c9a5d243SKarsten Graul 		return; /* no asymmetric link */
1279c9a5d243SKarsten Graul 	if (!smc_link_downing(&lnk_asym->state))
1280c9a5d243SKarsten Graul 		return;
1281c6f02ebeSKarsten Graul 	lnk_new = smc_switch_conns(lgr, lnk_asym, false);
1282c9a5d243SKarsten Graul 	smc_wr_tx_wait_no_pending_sends(lnk_asym);
1283c9a5d243SKarsten Graul 	if (!lnk_new)
1284c9a5d243SKarsten Graul 		goto out_free;
1285c9a5d243SKarsten Graul 	/* change flow type from ADD_LINK into DEL_LINK */
1286c9a5d243SKarsten Graul 	lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK;
1287c9a5d243SKarsten Graul 	rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ,
1288c9a5d243SKarsten Graul 				      true, SMC_LLC_DEL_NO_ASYM_NEEDED);
1289c9a5d243SKarsten Graul 	if (rc) {
1290c9a5d243SKarsten Graul 		smcr_link_down_cond(lnk_new);
1291c9a5d243SKarsten Graul 		goto out_free;
1292c9a5d243SKarsten Graul 	}
1293c9a5d243SKarsten Graul 	qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME,
1294c9a5d243SKarsten Graul 			      SMC_LLC_DELETE_LINK);
1295c9a5d243SKarsten Graul 	if (!qentry) {
1296c9a5d243SKarsten Graul 		smcr_link_down_cond(lnk_new);
1297c9a5d243SKarsten Graul 		goto out_free;
1298c9a5d243SKarsten Graul 	}
1299c9a5d243SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
1300c9a5d243SKarsten Graul out_free:
13010a99be43SKarsten Graul 	smcr_link_clear(lnk_asym, true);
1302c9a5d243SKarsten Graul }
1303c9a5d243SKarsten Graul 
130457b49924SKarsten Graul static int smc_llc_srv_rkey_exchange(struct smc_link *link,
130557b49924SKarsten Graul 				     struct smc_link *link_new)
130657b49924SKarsten Graul {
130757b49924SKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
130857b49924SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
130957b49924SKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
131057b49924SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
131157b49924SKarsten Graul 	struct smc_buf_desc *buf_pos;
131257b49924SKarsten Graul 	int buf_lst;
131357b49924SKarsten Graul 	int rc = 0;
131457b49924SKarsten Graul 	int i;
131557b49924SKarsten Graul 
131657b49924SKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
131757b49924SKarsten Graul 	num_rkeys_send = lgr->conns_num;
131857b49924SKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
131957b49924SKarsten Graul 	do {
132057b49924SKarsten Graul 		smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
132157b49924SKarsten Graul 				      &buf_lst, &buf_pos);
132257b49924SKarsten Graul 		qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME,
132357b49924SKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
132457b49924SKarsten Graul 		if (!qentry) {
132557b49924SKarsten Graul 			rc = -ETIMEDOUT;
132657b49924SKarsten Graul 			goto out;
132757b49924SKarsten Graul 		}
132857b49924SKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
132957b49924SKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
133057b49924SKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
133157b49924SKarsten Graul 		for (i = 0; i < max; i++) {
133257b49924SKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
133357b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key,
133457b49924SKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
133557b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
133657b49924SKarsten Graul 			num_rkeys_recv--;
133757b49924SKarsten Graul 		}
133857b49924SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
133957b49924SKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
134057b49924SKarsten Graul out:
134157b49924SKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
134257b49924SKarsten Graul 	return rc;
134357b49924SKarsten Graul }
134457b49924SKarsten Graul 
13451551c95bSKarsten Graul static int smc_llc_srv_conf_link(struct smc_link *link,
13461551c95bSKarsten Graul 				 struct smc_link *link_new,
13471551c95bSKarsten Graul 				 enum smc_lgr_type lgr_new_t)
13481551c95bSKarsten Graul {
13491551c95bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
13501551c95bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
13511551c95bSKarsten Graul 	int rc;
13521551c95bSKarsten Graul 
13531551c95bSKarsten Graul 	/* send CONFIRM LINK request over the RoCE fabric */
13541551c95bSKarsten Graul 	rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ);
13551551c95bSKarsten Graul 	if (rc)
13561551c95bSKarsten Graul 		return -ENOLINK;
13571551c95bSKarsten Graul 	/* receive CONFIRM LINK response over the RoCE fabric */
1358a35fffbfSKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, 0);
1359a35fffbfSKarsten Graul 	if (!qentry ||
1360b4ba4652SKarsten Graul 	    qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) {
13611551c95bSKarsten Graul 		/* send DELETE LINK */
13621551c95bSKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
13631551c95bSKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
1364a35fffbfSKarsten Graul 		if (qentry)
1365a35fffbfSKarsten Graul 			smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
13661551c95bSKarsten Graul 		return -ENOLINK;
13671551c95bSKarsten Graul 	}
1368649758ffSKarsten Graul 	smc_llc_save_peer_uid(qentry);
13691551c95bSKarsten Graul 	smc_llc_link_active(link_new);
1370ad6c111bSKarsten Graul 	if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
1371ad6c111bSKarsten Graul 	    lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)
1372ad6c111bSKarsten Graul 		smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx);
1373ad6c111bSKarsten Graul 	else
1374ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, lgr_new_t);
13751551c95bSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
13761551c95bSKarsten Graul 	return 0;
13771551c95bSKarsten Graul }
13781551c95bSKarsten Graul 
1379b4ba4652SKarsten Graul static void smc_llc_send_req_add_link_response(struct smc_llc_qentry *qentry)
1380b4ba4652SKarsten Graul {
1381b4ba4652SKarsten Graul 	qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
1382b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr,
1383b4ba4652SKarsten Graul 			     sizeof(qentry->msg));
1384b4ba4652SKarsten Graul 	memset(&qentry->msg.raw.data, 0, sizeof(qentry->msg.raw.data));
1385b4ba4652SKarsten Graul 	smc_llc_send_message(qentry->link, &qentry->msg);
1386b4ba4652SKarsten Graul }
1387b4ba4652SKarsten Graul 
1388b4ba4652SKarsten Graul int smc_llc_srv_add_link(struct smc_link *link,
1389b4ba4652SKarsten Graul 			 struct smc_llc_qentry *req_qentry)
13902d2209f2SKarsten Graul {
13912d2209f2SKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
13922d2209f2SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
13932d2209f2SKarsten Graul 	struct smc_llc_msg_add_link *add_llc;
13942d2209f2SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
1395b4ba4652SKarsten Graul 	bool send_req_add_link_resp = false;
1396ed990df2SKarsten Graul 	struct smc_link *link_new = NULL;
1397b4ba4652SKarsten Graul 	struct smc_init_info *ini = NULL;
13982d2209f2SKarsten Graul 	int lnk_idx, rc = 0;
13992d2209f2SKarsten Graul 
1400b4ba4652SKarsten Graul 	if (req_qentry &&
1401b4ba4652SKarsten Graul 	    req_qentry->msg.raw.hdr.common.llc_type == SMC_LLC_REQ_ADD_LINK)
1402b4ba4652SKarsten Graul 		send_req_add_link_resp = true;
1403b4ba4652SKarsten Graul 
1404ed990df2SKarsten Graul 	ini = kzalloc(sizeof(*ini), GFP_KERNEL);
1405b4ba4652SKarsten Graul 	if (!ini) {
1406b4ba4652SKarsten Graul 		rc = -ENOMEM;
1407b4ba4652SKarsten Graul 		goto out;
1408b4ba4652SKarsten Graul 	}
1409ed990df2SKarsten Graul 
14102d2209f2SKarsten Graul 	/* ignore client add link recommendation, start new flow */
1411ed990df2SKarsten Graul 	ini->vlan_id = lgr->vlan_id;
1412b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
1413b4ba4652SKarsten Graul 		ini->check_smcrv2 = true;
1414b4ba4652SKarsten Graul 		ini->smcrv2.saddr = lgr->saddr;
1415b4ba4652SKarsten Graul 		if (send_req_add_link_resp) {
1416b4ba4652SKarsten Graul 			struct smc_llc_msg_req_add_link_v2 *req_add =
1417b4ba4652SKarsten Graul 				&req_qentry->msg.req_add_link;
1418b4ba4652SKarsten Graul 
1419b4ba4652SKarsten Graul 			ini->smcrv2.daddr = smc_ib_gid_to_ipv4(req_add->gid[0]);
1420b4ba4652SKarsten Graul 		}
1421b4ba4652SKarsten Graul 	}
1422ed990df2SKarsten Graul 	smc_pnet_find_alt_roce(lgr, ini, link->smcibdev);
1423b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) {
1424b4ba4652SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
1425b4ba4652SKarsten Graul 		ini->smcrv2.ib_dev_v2 = link->smcibdev;
1426b4ba4652SKarsten Graul 		ini->smcrv2.ib_port_v2 = link->ibport;
1427b4ba4652SKarsten Graul 	} else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) {
14282d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
1429ed990df2SKarsten Graul 		ini->ib_dev = link->smcibdev;
1430ed990df2SKarsten Graul 		ini->ib_port = link->ibport;
14312d2209f2SKarsten Graul 	}
14322d2209f2SKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
1433ed990df2SKarsten Graul 	if (lnk_idx < 0) {
1434ed990df2SKarsten Graul 		rc = 0;
1435ed990df2SKarsten Graul 		goto out;
1436ed990df2SKarsten Graul 	}
14372d2209f2SKarsten Graul 
1438ed990df2SKarsten Graul 	rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, ini);
14392d2209f2SKarsten Graul 	if (rc)
1440ed990df2SKarsten Graul 		goto out;
14412d2209f2SKarsten Graul 	link_new = &lgr->lnk[lnk_idx];
1442b4ba4652SKarsten Graul 
1443b4ba4652SKarsten Graul 	rc = smcr_buf_map_lgr(link_new);
1444b4ba4652SKarsten Graul 	if (rc)
1445b4ba4652SKarsten Graul 		goto out_err;
1446b4ba4652SKarsten Graul 
14472d2209f2SKarsten Graul 	rc = smc_llc_send_add_link(link,
1448b4ba4652SKarsten Graul 				   link_new->smcibdev->mac[link_new->ibport-1],
14492d2209f2SKarsten Graul 				   link_new->gid, link_new, SMC_LLC_REQ);
14502d2209f2SKarsten Graul 	if (rc)
14512d2209f2SKarsten Graul 		goto out_err;
1452b4ba4652SKarsten Graul 	send_req_add_link_resp = false;
14532d2209f2SKarsten Graul 	/* receive ADD LINK response over the RoCE fabric */
14542d2209f2SKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK);
14552d2209f2SKarsten Graul 	if (!qentry) {
14562d2209f2SKarsten Graul 		rc = -ETIMEDOUT;
14572d2209f2SKarsten Graul 		goto out_err;
14582d2209f2SKarsten Graul 	}
14592d2209f2SKarsten Graul 	add_llc = &qentry->msg.add_link;
14602d2209f2SKarsten Graul 	if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
14612d2209f2SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
14622d2209f2SKarsten Graul 		rc = -ENOLINK;
14632d2209f2SKarsten Graul 		goto out_err;
14642d2209f2SKarsten Graul 	}
14652d2209f2SKarsten Graul 	if (lgr->type == SMC_LGR_SINGLE &&
14662d2209f2SKarsten Graul 	    (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
1467b4ba4652SKarsten Graul 	     (lgr->smc_version == SMC_V2 ||
1468b4ba4652SKarsten Graul 	      !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN)))) {
14692d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
14702d2209f2SKarsten Graul 	}
14712d2209f2SKarsten Graul 	smc_llc_save_add_link_info(link_new, add_llc);
14722d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
14732d2209f2SKarsten Graul 
14742d2209f2SKarsten Graul 	rc = smc_ib_ready_link(link_new);
14752d2209f2SKarsten Graul 	if (rc)
14762d2209f2SKarsten Graul 		goto out_err;
14772d2209f2SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
14782d2209f2SKarsten Graul 	if (rc)
14792d2209f2SKarsten Graul 		goto out_err;
1480b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
1481b4ba4652SKarsten Graul 		smc_llc_save_add_link_rkeys(link, link_new);
1482b4ba4652SKarsten Graul 	} else {
148357b49924SKarsten Graul 		rc = smc_llc_srv_rkey_exchange(link, link_new);
14842d2209f2SKarsten Graul 		if (rc)
14852d2209f2SKarsten Graul 			goto out_err;
1486b4ba4652SKarsten Graul 	}
14871551c95bSKarsten Graul 	rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
14882d2209f2SKarsten Graul 	if (rc)
14892d2209f2SKarsten Graul 		goto out_err;
1490ed990df2SKarsten Graul 	kfree(ini);
14912d2209f2SKarsten Graul 	return 0;
14922d2209f2SKarsten Graul out_err:
1493ed990df2SKarsten Graul 	if (link_new) {
14948f3d65c1SKarsten Graul 		link_new->state = SMC_LNK_INACTIVE;
14950a99be43SKarsten Graul 		smcr_link_clear(link_new, false);
1496ed990df2SKarsten Graul 	}
1497ed990df2SKarsten Graul out:
1498ed990df2SKarsten Graul 	kfree(ini);
1499b4ba4652SKarsten Graul 	if (send_req_add_link_resp)
1500b4ba4652SKarsten Graul 		smc_llc_send_req_add_link_response(req_qentry);
15012d2209f2SKarsten Graul 	return rc;
15022d2209f2SKarsten Graul }
15032d2209f2SKarsten Graul 
15042d2209f2SKarsten Graul static void smc_llc_process_srv_add_link(struct smc_link_group *lgr)
15052d2209f2SKarsten Graul {
15062d2209f2SKarsten Graul 	struct smc_link *link = lgr->llc_flow_lcl.qentry->link;
1507b4ba4652SKarsten Graul 	struct smc_llc_qentry *qentry;
15082d2209f2SKarsten Graul 	int rc;
15092d2209f2SKarsten Graul 
1510b4ba4652SKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
15112d2209f2SKarsten Graul 
1512*b5dd4d69SD. Wythe 	down_write(&lgr->llc_conf_mutex);
1513b4ba4652SKarsten Graul 	rc = smc_llc_srv_add_link(link, qentry);
15142d2209f2SKarsten Graul 	if (!rc && lgr->type == SMC_LGR_SYMMETRIC) {
15152d2209f2SKarsten Graul 		/* delete any asymmetric link */
1516c9a5d243SKarsten Graul 		smc_llc_delete_asym_link(lgr);
15172d2209f2SKarsten Graul 	}
1518*b5dd4d69SD. Wythe 	up_write(&lgr->llc_conf_mutex);
1519b4ba4652SKarsten Graul 	kfree(qentry);
15202d2209f2SKarsten Graul }
15212d2209f2SKarsten Graul 
1522c48254faSKarsten Graul /* enqueue a local add_link req to trigger a new add_link flow */
1523c48254faSKarsten Graul void smc_llc_add_link_local(struct smc_link *link)
15244dadd151SKarsten Graul {
152516cb3653SPujin Shi 	struct smc_llc_msg_add_link add_llc = {};
15264dadd151SKarsten Graul 
1527b4ba4652SKarsten Graul 	add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK;
1528b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc));
1529c48254faSKarsten Graul 	/* no dev and port needed */
15304dadd151SKarsten Graul 	smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);
15314dadd151SKarsten Graul }
15324dadd151SKarsten Graul 
1533b45e7f98SKarsten Graul /* worker to process an add link message */
1534b45e7f98SKarsten Graul static void smc_llc_add_link_work(struct work_struct *work)
1535b45e7f98SKarsten Graul {
1536b45e7f98SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
1537b45e7f98SKarsten Graul 						  llc_add_link_work);
1538b45e7f98SKarsten Graul 
1539b45e7f98SKarsten Graul 	if (list_empty(&lgr->list)) {
1540b45e7f98SKarsten Graul 		/* link group is terminating */
1541b45e7f98SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
1542b45e7f98SKarsten Graul 		goto out;
1543b45e7f98SKarsten Graul 	}
1544b45e7f98SKarsten Graul 
1545b1570a87SKarsten Graul 	if (lgr->role == SMC_CLNT)
1546b1570a87SKarsten Graul 		smc_llc_process_cli_add_link(lgr);
15472d2209f2SKarsten Graul 	else
15482d2209f2SKarsten Graul 		smc_llc_process_srv_add_link(lgr);
1549b45e7f98SKarsten Graul out:
1550b4ba4652SKarsten Graul 	if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_REQ_ADD_LINK)
1551b45e7f98SKarsten Graul 		smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
1552b45e7f98SKarsten Graul }
1553b45e7f98SKarsten Graul 
15544dadd151SKarsten Graul /* enqueue a local del_link msg to trigger a new del_link flow,
15554dadd151SKarsten Graul  * called only for role SMC_SERV
15564dadd151SKarsten Graul  */
15574dadd151SKarsten Graul void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id)
15584dadd151SKarsten Graul {
155916cb3653SPujin Shi 	struct smc_llc_msg_del_link del_llc = {};
15604dadd151SKarsten Graul 
1561b4ba4652SKarsten Graul 	del_llc.hd.common.llc_type = SMC_LLC_DELETE_LINK;
1562b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&del_llc.hd, link->lgr, sizeof(del_llc));
15634dadd151SKarsten Graul 	del_llc.link_num = del_link_id;
15644dadd151SKarsten Graul 	del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH);
15654dadd151SKarsten Graul 	del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
15664dadd151SKarsten Graul 	smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc);
15674dadd151SKarsten Graul }
15684dadd151SKarsten Graul 
15699c416878SKarsten Graul static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
15709c416878SKarsten Graul {
15719c416878SKarsten Graul 	struct smc_link *lnk_del = NULL, *lnk_asym, *lnk;
15729c416878SKarsten Graul 	struct smc_llc_msg_del_link *del_llc;
15739c416878SKarsten Graul 	struct smc_llc_qentry *qentry;
15749c416878SKarsten Graul 	int active_links;
15759c416878SKarsten Graul 	int lnk_idx;
15769c416878SKarsten Graul 
15779c416878SKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
15789c416878SKarsten Graul 	lnk = qentry->link;
15799c416878SKarsten Graul 	del_llc = &qentry->msg.delete_link;
15809c416878SKarsten Graul 
15819c416878SKarsten Graul 	if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) {
15829c416878SKarsten Graul 		smc_lgr_terminate_sched(lgr);
15839c416878SKarsten Graul 		goto out;
15849c416878SKarsten Graul 	}
1585*b5dd4d69SD. Wythe 	down_write(&lgr->llc_conf_mutex);
15869c416878SKarsten Graul 	/* delete single link */
15879c416878SKarsten Graul 	for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) {
15889c416878SKarsten Graul 		if (lgr->lnk[lnk_idx].link_id != del_llc->link_num)
15899c416878SKarsten Graul 			continue;
15909c416878SKarsten Graul 		lnk_del = &lgr->lnk[lnk_idx];
15919c416878SKarsten Graul 		break;
15929c416878SKarsten Graul 	}
15939c416878SKarsten Graul 	del_llc->hd.flags |= SMC_LLC_FLAG_RESP;
15949c416878SKarsten Graul 	if (!lnk_del) {
15959c416878SKarsten Graul 		/* link was not found */
15969c416878SKarsten Graul 		del_llc->reason = htonl(SMC_LLC_DEL_NOLNK);
15979c416878SKarsten Graul 		smc_llc_send_message(lnk, &qentry->msg);
15989c416878SKarsten Graul 		goto out_unlock;
15999c416878SKarsten Graul 	}
16009c416878SKarsten Graul 	lnk_asym = smc_llc_find_asym_link(lgr);
16019c416878SKarsten Graul 
16029c416878SKarsten Graul 	del_llc->reason = 0;
16039c416878SKarsten Graul 	smc_llc_send_message(lnk, &qentry->msg); /* response */
16049c416878SKarsten Graul 
16058f3d65c1SKarsten Graul 	if (smc_link_downing(&lnk_del->state))
16068f3d65c1SKarsten Graul 		smc_switch_conns(lgr, lnk_del, false);
16070a99be43SKarsten Graul 	smcr_link_clear(lnk_del, true);
16089c416878SKarsten Graul 
16099c416878SKarsten Graul 	active_links = smc_llc_active_link_count(lgr);
16109c416878SKarsten Graul 	if (lnk_del == lnk_asym) {
16119c416878SKarsten Graul 		/* expected deletion of asym link, don't change lgr state */
16129c416878SKarsten Graul 	} else if (active_links == 1) {
1613ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
16149c416878SKarsten Graul 	} else if (!active_links) {
1615ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_NONE);
16169c416878SKarsten Graul 		smc_lgr_terminate_sched(lgr);
16179c416878SKarsten Graul 	}
16189c416878SKarsten Graul out_unlock:
1619*b5dd4d69SD. Wythe 	up_write(&lgr->llc_conf_mutex);
16209c416878SKarsten Graul out:
16219c416878SKarsten Graul 	kfree(qentry);
16229c416878SKarsten Graul }
16239c416878SKarsten Graul 
1624f3811fd7SKarsten Graul /* try to send a DELETE LINK ALL request on any active link,
1625f3811fd7SKarsten Graul  * waiting for send completion
1626f3811fd7SKarsten Graul  */
1627f3811fd7SKarsten Graul void smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord, u32 rsn)
1628f3811fd7SKarsten Graul {
16297e94e46cSPujin Shi 	struct smc_llc_msg_del_link delllc = {};
1630f3811fd7SKarsten Graul 	int i;
1631f3811fd7SKarsten Graul 
1632b4ba4652SKarsten Graul 	delllc.hd.common.llc_type = SMC_LLC_DELETE_LINK;
1633b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&delllc.hd, lgr, sizeof(delllc));
1634f3811fd7SKarsten Graul 	if (ord)
1635f3811fd7SKarsten Graul 		delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
1636f3811fd7SKarsten Graul 	delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
1637f3811fd7SKarsten Graul 	delllc.reason = htonl(rsn);
1638f3811fd7SKarsten Graul 
1639f3811fd7SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
164090cee52fSDust Li 		if (!smc_link_sendable(&lgr->lnk[i]))
1641f3811fd7SKarsten Graul 			continue;
1642f3811fd7SKarsten Graul 		if (!smc_llc_send_message_wait(&lgr->lnk[i], &delllc))
1643f3811fd7SKarsten Graul 			break;
1644f3811fd7SKarsten Graul 	}
1645f3811fd7SKarsten Graul }
1646f3811fd7SKarsten Graul 
164708ae27ddSKarsten Graul static void smc_llc_process_srv_delete_link(struct smc_link_group *lgr)
164808ae27ddSKarsten Graul {
164908ae27ddSKarsten Graul 	struct smc_llc_msg_del_link *del_llc;
165008ae27ddSKarsten Graul 	struct smc_link *lnk, *lnk_del;
165108ae27ddSKarsten Graul 	struct smc_llc_qentry *qentry;
165208ae27ddSKarsten Graul 	int active_links;
165308ae27ddSKarsten Graul 	int i;
165408ae27ddSKarsten Graul 
1655*b5dd4d69SD. Wythe 	down_write(&lgr->llc_conf_mutex);
165608ae27ddSKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
165708ae27ddSKarsten Graul 	lnk = qentry->link;
165808ae27ddSKarsten Graul 	del_llc = &qentry->msg.delete_link;
165908ae27ddSKarsten Graul 
166008ae27ddSKarsten Graul 	if (qentry->msg.delete_link.hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) {
166108ae27ddSKarsten Graul 		/* delete entire lgr */
1662f3811fd7SKarsten Graul 		smc_llc_send_link_delete_all(lgr, true, ntohl(
1663f3811fd7SKarsten Graul 					      qentry->msg.delete_link.reason));
166408ae27ddSKarsten Graul 		smc_lgr_terminate_sched(lgr);
166508ae27ddSKarsten Graul 		goto out;
166608ae27ddSKarsten Graul 	}
166708ae27ddSKarsten Graul 	/* delete single link */
166808ae27ddSKarsten Graul 	lnk_del = NULL;
166908ae27ddSKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
167008ae27ddSKarsten Graul 		if (lgr->lnk[i].link_id == del_llc->link_num) {
167108ae27ddSKarsten Graul 			lnk_del = &lgr->lnk[i];
167208ae27ddSKarsten Graul 			break;
167308ae27ddSKarsten Graul 		}
167408ae27ddSKarsten Graul 	}
167508ae27ddSKarsten Graul 	if (!lnk_del)
167608ae27ddSKarsten Graul 		goto out; /* asymmetric link already deleted */
167708ae27ddSKarsten Graul 
167808ae27ddSKarsten Graul 	if (smc_link_downing(&lnk_del->state)) {
1679b7eede75SKarsten Graul 		if (smc_switch_conns(lgr, lnk_del, false))
168008ae27ddSKarsten Graul 			smc_wr_tx_wait_no_pending_sends(lnk_del);
168108ae27ddSKarsten Graul 	}
168208ae27ddSKarsten Graul 	if (!list_empty(&lgr->list)) {
168308ae27ddSKarsten Graul 		/* qentry is either a request from peer (send it back to
168408ae27ddSKarsten Graul 		 * initiate the DELETE_LINK processing), or a locally
168508ae27ddSKarsten Graul 		 * enqueued DELETE_LINK request (forward it)
168608ae27ddSKarsten Graul 		 */
168708ae27ddSKarsten Graul 		if (!smc_llc_send_message(lnk, &qentry->msg)) {
168808ae27ddSKarsten Graul 			struct smc_llc_qentry *qentry2;
168908ae27ddSKarsten Graul 
169008ae27ddSKarsten Graul 			qentry2 = smc_llc_wait(lgr, lnk, SMC_LLC_WAIT_TIME,
169108ae27ddSKarsten Graul 					       SMC_LLC_DELETE_LINK);
1692ca7e3edcSYueHaibing 			if (qentry2)
169308ae27ddSKarsten Graul 				smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
169408ae27ddSKarsten Graul 		}
169508ae27ddSKarsten Graul 	}
16960a99be43SKarsten Graul 	smcr_link_clear(lnk_del, true);
169708ae27ddSKarsten Graul 
169808ae27ddSKarsten Graul 	active_links = smc_llc_active_link_count(lgr);
169908ae27ddSKarsten Graul 	if (active_links == 1) {
1700ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
170108ae27ddSKarsten Graul 	} else if (!active_links) {
1702ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_NONE);
170308ae27ddSKarsten Graul 		smc_lgr_terminate_sched(lgr);
170408ae27ddSKarsten Graul 	}
170508ae27ddSKarsten Graul 
170608ae27ddSKarsten Graul 	if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) {
170708ae27ddSKarsten Graul 		/* trigger setup of asymm alt link */
1708c48254faSKarsten Graul 		smc_llc_add_link_local(lnk);
170908ae27ddSKarsten Graul 	}
171008ae27ddSKarsten Graul out:
1711*b5dd4d69SD. Wythe 	up_write(&lgr->llc_conf_mutex);
171208ae27ddSKarsten Graul 	kfree(qentry);
171308ae27ddSKarsten Graul }
171408ae27ddSKarsten Graul 
17159ec6bf19SKarsten Graul static void smc_llc_delete_link_work(struct work_struct *work)
171652bedf37SKarsten Graul {
17179ec6bf19SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
17189ec6bf19SKarsten Graul 						  llc_del_link_work);
171952bedf37SKarsten Graul 
17209ec6bf19SKarsten Graul 	if (list_empty(&lgr->list)) {
17219ec6bf19SKarsten Graul 		/* link group is terminating */
17229ec6bf19SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
17239ec6bf19SKarsten Graul 		goto out;
172452bedf37SKarsten Graul 	}
17259c416878SKarsten Graul 
17269c416878SKarsten Graul 	if (lgr->role == SMC_CLNT)
17279c416878SKarsten Graul 		smc_llc_process_cli_delete_link(lgr);
172808ae27ddSKarsten Graul 	else
172908ae27ddSKarsten Graul 		smc_llc_process_srv_delete_link(lgr);
17309ec6bf19SKarsten Graul out:
17319ec6bf19SKarsten Graul 	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
173252bedf37SKarsten Graul }
173352bedf37SKarsten Graul 
17343bc67e09SKarsten Graul /* process a confirm_rkey request from peer, remote flow */
17353bc67e09SKarsten Graul static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr)
17364ed75de5SKarsten Graul {
17373bc67e09SKarsten Graul 	struct smc_llc_msg_confirm_rkey *llc;
17383bc67e09SKarsten Graul 	struct smc_llc_qentry *qentry;
17393bc67e09SKarsten Graul 	struct smc_link *link;
17403bc67e09SKarsten Graul 	int num_entries;
17413bc67e09SKarsten Graul 	int rk_idx;
17423bc67e09SKarsten Graul 	int i;
17434ed75de5SKarsten Graul 
17443bc67e09SKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
17453bc67e09SKarsten Graul 	llc = &qentry->msg.confirm_rkey;
17463bc67e09SKarsten Graul 	link = qentry->link;
17473bc67e09SKarsten Graul 
17483bc67e09SKarsten Graul 	num_entries = llc->rtoken[0].num_rkeys;
1749b4ba4652SKarsten Graul 	if (num_entries > SMC_LLC_RKEYS_PER_MSG)
1750b4ba4652SKarsten Graul 		goto out_err;
17513bc67e09SKarsten Graul 	/* first rkey entry is for receiving link */
17523bc67e09SKarsten Graul 	rk_idx = smc_rtoken_add(link,
17534ed75de5SKarsten Graul 				llc->rtoken[0].rmb_vaddr,
17544ed75de5SKarsten Graul 				llc->rtoken[0].rmb_key);
17553bc67e09SKarsten Graul 	if (rk_idx < 0)
17563bc67e09SKarsten Graul 		goto out_err;
17574ed75de5SKarsten Graul 
17583bc67e09SKarsten Graul 	for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++)
17593bc67e09SKarsten Graul 		smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id,
17603bc67e09SKarsten Graul 				llc->rtoken[i].rmb_vaddr,
17613bc67e09SKarsten Graul 				llc->rtoken[i].rmb_key);
17623bc67e09SKarsten Graul 	/* max links is 3 so there is no need to support conf_rkey_cont msgs */
17633bc67e09SKarsten Graul 	goto out;
17643bc67e09SKarsten Graul out_err:
17654ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
17663bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY;
17673bc67e09SKarsten Graul out:
17683bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
1769b4ba4652SKarsten Graul 	smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc));
17703bc67e09SKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
17713bc67e09SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
17724ed75de5SKarsten Graul }
17734ed75de5SKarsten Graul 
1774218b24feSKarsten Graul /* process a delete_rkey request from peer, remote flow */
1775218b24feSKarsten Graul static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
17764ed75de5SKarsten Graul {
1777218b24feSKarsten Graul 	struct smc_llc_msg_delete_rkey *llc;
1778218b24feSKarsten Graul 	struct smc_llc_qentry *qentry;
1779218b24feSKarsten Graul 	struct smc_link *link;
17804ed75de5SKarsten Graul 	u8 err_mask = 0;
17814ed75de5SKarsten Graul 	int i, max;
17824ed75de5SKarsten Graul 
1783218b24feSKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
1784218b24feSKarsten Graul 	llc = &qentry->msg.delete_rkey;
1785218b24feSKarsten Graul 	link = qentry->link;
1786218b24feSKarsten Graul 
1787b4ba4652SKarsten Graul 	if (lgr->smc_version == SMC_V2) {
1788b4ba4652SKarsten Graul 		struct smc_llc_msg_delete_rkey_v2 *llcv2;
1789b4ba4652SKarsten Graul 
1790b4ba4652SKarsten Graul 		memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc));
1791b4ba4652SKarsten Graul 		llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2;
1792b4ba4652SKarsten Graul 		llcv2->num_inval_rkeys = 0;
1793b4ba4652SKarsten Graul 
1794b4ba4652SKarsten Graul 		max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
1795b4ba4652SKarsten Graul 		for (i = 0; i < max; i++) {
1796b4ba4652SKarsten Graul 			if (smc_rtoken_delete(link, llcv2->rkey[i]))
1797b4ba4652SKarsten Graul 				llcv2->num_inval_rkeys++;
1798b4ba4652SKarsten Graul 		}
1799b4ba4652SKarsten Graul 		memset(&llc->rkey[0], 0, sizeof(llc->rkey));
1800b4ba4652SKarsten Graul 		memset(&llc->reserved2, 0, sizeof(llc->reserved2));
1801b4ba4652SKarsten Graul 		smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc));
1802b4ba4652SKarsten Graul 		if (llcv2->num_inval_rkeys) {
1803b4ba4652SKarsten Graul 			llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
1804b4ba4652SKarsten Graul 			llc->err_mask = llcv2->num_inval_rkeys;
1805b4ba4652SKarsten Graul 		}
1806b4ba4652SKarsten Graul 		goto finish;
1807b4ba4652SKarsten Graul 	}
1808b4ba4652SKarsten Graul 
18094ed75de5SKarsten Graul 	max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
18104ed75de5SKarsten Graul 	for (i = 0; i < max; i++) {
1811387707fdSKarsten Graul 		if (smc_rtoken_delete(link, llc->rkey[i]))
18124ed75de5SKarsten Graul 			err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
18134ed75de5SKarsten Graul 	}
18144ed75de5SKarsten Graul 	if (err_mask) {
18154ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
18164ed75de5SKarsten Graul 		llc->err_mask = err_mask;
18174ed75de5SKarsten Graul 	}
1818b4ba4652SKarsten Graul finish:
1819218b24feSKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
1820218b24feSKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
1821218b24feSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
1822218b24feSKarsten Graul }
18234ed75de5SKarsten Graul 
18243e0c40afSKarsten Graul static void smc_llc_protocol_violation(struct smc_link_group *lgr, u8 type)
18253e0c40afSKarsten Graul {
1826de2fea7bSTony Lu 	pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu LLC protocol violation: "
1827de2fea7bSTony Lu 			    "llc_type %d\n", SMC_LGR_ID_SIZE, &lgr->id,
1828de2fea7bSTony Lu 			    lgr->net->net_cookie, type);
18293e0c40afSKarsten Graul 	smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_PROT_VIOL);
18303e0c40afSKarsten Graul 	smc_lgr_terminate_sched(lgr);
18313e0c40afSKarsten Graul }
18323e0c40afSKarsten Graul 
18336c8968c4SKarsten Graul /* flush the llc event queue */
183400a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr)
18359bf9abeaSUrsula Braun {
18366c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry, *q;
18379bf9abeaSUrsula Braun 
18386c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
18396c8968c4SKarsten Graul 	list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
18406c8968c4SKarsten Graul 		list_del_init(&qentry->list);
18416c8968c4SKarsten Graul 		kfree(qentry);
18426c8968c4SKarsten Graul 	}
18436c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
18446c8968c4SKarsten Graul }
18456c8968c4SKarsten Graul 
18466c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
18476c8968c4SKarsten Graul {
18486c8968c4SKarsten Graul 	union smc_llc_msg *llc = &qentry->msg;
18496c8968c4SKarsten Graul 	struct smc_link *link = qentry->link;
18500fb0b02bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
18516c8968c4SKarsten Graul 
1852d854fcbfSKarsten Graul 	if (!smc_link_usable(link))
18536c8968c4SKarsten Graul 		goto out;
1854313164daSKarsten Graul 
1855b4ba4652SKarsten Graul 	switch (llc->raw.hdr.common.llc_type) {
1856313164daSKarsten Graul 	case SMC_LLC_TEST_LINK:
185756e8091cSKarsten Graul 		llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP;
185856e8091cSKarsten Graul 		smc_llc_send_message(link, llc);
1859313164daSKarsten Graul 		break;
186052bedf37SKarsten Graul 	case SMC_LLC_ADD_LINK:
18610fb0b02bSKarsten Graul 		if (list_empty(&lgr->list))
18620fb0b02bSKarsten Graul 			goto out;	/* lgr is terminating */
18630fb0b02bSKarsten Graul 		if (lgr->role == SMC_CLNT) {
1864c48254faSKarsten Graul 			if (smc_llc_is_local_add_link(llc)) {
1865c48254faSKarsten Graul 				if (lgr->llc_flow_lcl.type ==
1866c48254faSKarsten Graul 				    SMC_LLC_FLOW_ADD_LINK)
1867c48254faSKarsten Graul 					break;	/* add_link in progress */
1868c48254faSKarsten Graul 				if (smc_llc_flow_start(&lgr->llc_flow_lcl,
1869c48254faSKarsten Graul 						       qentry)) {
1870c48254faSKarsten Graul 					schedule_work(&lgr->llc_add_link_work);
1871c48254faSKarsten Graul 				}
1872c48254faSKarsten Graul 				return;
1873c48254faSKarsten Graul 			}
1874c48254faSKarsten Graul 			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK &&
1875c48254faSKarsten Graul 			    !lgr->llc_flow_lcl.qentry) {
18760fb0b02bSKarsten Graul 				/* a flow is waiting for this message */
18770fb0b02bSKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
18780fb0b02bSKarsten Graul 							qentry);
18796778a6beSKarsten Graul 				wake_up(&lgr->llc_msg_waiter);
1880b4ba4652SKarsten Graul 				return;
1881b4ba4652SKarsten Graul 			}
1882b4ba4652SKarsten Graul 			if (lgr->llc_flow_lcl.type ==
1883b4ba4652SKarsten Graul 					SMC_LLC_FLOW_REQ_ADD_LINK) {
1884b4ba4652SKarsten Graul 				/* server started add_link processing */
1885b4ba4652SKarsten Graul 				lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK;
1886b4ba4652SKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
1887b4ba4652SKarsten Graul 							qentry);
1888b4ba4652SKarsten Graul 				schedule_work(&lgr->llc_add_link_work);
1889b4ba4652SKarsten Graul 				return;
1890b4ba4652SKarsten Graul 			}
1891b4ba4652SKarsten Graul 			if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
1892b45e7f98SKarsten Graul 				schedule_work(&lgr->llc_add_link_work);
18930fb0b02bSKarsten Graul 			}
18940fb0b02bSKarsten Graul 		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
18950fb0b02bSKarsten Graul 			/* as smc server, handle client suggestion */
1896b45e7f98SKarsten Graul 			schedule_work(&lgr->llc_add_link_work);
18970fb0b02bSKarsten Graul 		}
18980fb0b02bSKarsten Graul 		return;
18990fb0b02bSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
190087f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
19010fb0b02bSKarsten Graul 		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
19020fb0b02bSKarsten Graul 			/* a flow is waiting for this message */
19030fb0b02bSKarsten Graul 			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
19046778a6beSKarsten Graul 			wake_up(&lgr->llc_msg_waiter);
19050fb0b02bSKarsten Graul 			return;
19060fb0b02bSKarsten Graul 		}
190752bedf37SKarsten Graul 		break;
190852bedf37SKarsten Graul 	case SMC_LLC_DELETE_LINK:
19099ec6bf19SKarsten Graul 		if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK &&
19109ec6bf19SKarsten Graul 		    !lgr->llc_flow_lcl.qentry) {
19119ec6bf19SKarsten Graul 			/* DEL LINK REQ during ADD LINK SEQ */
1912b9979c2eSKarsten Graul 			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
19136778a6beSKarsten Graul 			wake_up(&lgr->llc_msg_waiter);
1914b9979c2eSKarsten Graul 		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
19159ec6bf19SKarsten Graul 			schedule_work(&lgr->llc_del_link_work);
19169ec6bf19SKarsten Graul 		}
19179ec6bf19SKarsten Graul 		return;
19184ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
19193bc67e09SKarsten Graul 		/* new request from remote, assign to remote flow */
19203bc67e09SKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
19213bc67e09SKarsten Graul 			/* process here, does not wait for more llc msgs */
19223bc67e09SKarsten Graul 			smc_llc_rmt_conf_rkey(lgr);
19233bc67e09SKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
19243bc67e09SKarsten Graul 		}
19253bc67e09SKarsten Graul 		return;
19264ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
192742d18accSKarsten Graul 		/* not used because max links is 3, and 3 rkeys fit into
192842d18accSKarsten Graul 		 * one CONFIRM_RKEY message
192942d18accSKarsten Graul 		 */
19304ed75de5SKarsten Graul 		break;
19314ed75de5SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
1932218b24feSKarsten Graul 		/* new request from remote, assign to remote flow */
1933218b24feSKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
1934218b24feSKarsten Graul 			/* process here, does not wait for more llc msgs */
1935218b24feSKarsten Graul 			smc_llc_rmt_delete_rkey(lgr);
1936218b24feSKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
1937218b24feSKarsten Graul 		}
1938218b24feSKarsten Graul 		return;
1939b4ba4652SKarsten Graul 	case SMC_LLC_REQ_ADD_LINK:
1940b4ba4652SKarsten Graul 		/* handle response here, smc_llc_flow_stop() cannot be called
1941b4ba4652SKarsten Graul 		 * in tasklet context
1942b4ba4652SKarsten Graul 		 */
1943b4ba4652SKarsten Graul 		if (lgr->role == SMC_CLNT &&
1944b4ba4652SKarsten Graul 		    lgr->llc_flow_lcl.type == SMC_LLC_FLOW_REQ_ADD_LINK &&
1945b4ba4652SKarsten Graul 		    (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP)) {
1946b4ba4652SKarsten Graul 			smc_llc_flow_stop(link->lgr, &lgr->llc_flow_lcl);
1947b4ba4652SKarsten Graul 		} else if (lgr->role == SMC_SERV) {
1948b4ba4652SKarsten Graul 			if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
1949b4ba4652SKarsten Graul 				/* as smc server, handle client suggestion */
1950b4ba4652SKarsten Graul 				lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK;
1951b4ba4652SKarsten Graul 				schedule_work(&lgr->llc_add_link_work);
1952b4ba4652SKarsten Graul 			}
1953b4ba4652SKarsten Graul 			return;
1954b4ba4652SKarsten Graul 		}
1955b4ba4652SKarsten Graul 		break;
19563e0c40afSKarsten Graul 	default:
19573e0c40afSKarsten Graul 		smc_llc_protocol_violation(lgr, llc->raw.hdr.common.type);
19583e0c40afSKarsten Graul 		break;
1959313164daSKarsten Graul 	}
19606c8968c4SKarsten Graul out:
19616c8968c4SKarsten Graul 	kfree(qentry);
19626c8968c4SKarsten Graul }
19636c8968c4SKarsten Graul 
19646c8968c4SKarsten Graul /* worker to process llc messages on the event queue */
19656c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work)
19666c8968c4SKarsten Graul {
19676c8968c4SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
19686c8968c4SKarsten Graul 						  llc_event_work);
19696c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
19706c8968c4SKarsten Graul 
1971555da9afSKarsten Graul 	if (!lgr->llc_flow_lcl.type && lgr->delayed_event) {
1972555da9afSKarsten Graul 		qentry = lgr->delayed_event;
1973555da9afSKarsten Graul 		lgr->delayed_event = NULL;
1974d535ca13SKarsten Graul 		if (smc_link_usable(qentry->link))
1975d535ca13SKarsten Graul 			smc_llc_event_handler(qentry);
1976d535ca13SKarsten Graul 		else
1977555da9afSKarsten Graul 			kfree(qentry);
1978555da9afSKarsten Graul 	}
1979555da9afSKarsten Graul 
19806c8968c4SKarsten Graul again:
19816c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
19826c8968c4SKarsten Graul 	if (!list_empty(&lgr->llc_event_q)) {
19836c8968c4SKarsten Graul 		qentry = list_first_entry(&lgr->llc_event_q,
19846c8968c4SKarsten Graul 					  struct smc_llc_qentry, list);
19856c8968c4SKarsten Graul 		list_del_init(&qentry->list);
19866c8968c4SKarsten Graul 		spin_unlock_bh(&lgr->llc_event_q_lock);
19876c8968c4SKarsten Graul 		smc_llc_event_handler(qentry);
19886c8968c4SKarsten Graul 		goto again;
19896c8968c4SKarsten Graul 	}
19906c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
19916c8968c4SKarsten Graul }
19926c8968c4SKarsten Graul 
1993ef79d439SKarsten Graul /* process llc responses in tasklet context */
1994a6688d91SKarsten Graul static void smc_llc_rx_response(struct smc_link *link,
1995a6688d91SKarsten Graul 				struct smc_llc_qentry *qentry)
1996ef79d439SKarsten Graul {
19972ff08678SKarsten Graul 	enum smc_llc_flowtype flowtype = link->lgr->llc_flow_lcl.type;
19982ff08678SKarsten Graul 	struct smc_llc_flow *flow = &link->lgr->llc_flow_lcl;
1999b4ba4652SKarsten Graul 	u8 llc_type = qentry->msg.raw.hdr.common.llc_type;
2000ef79d439SKarsten Graul 
2001a6688d91SKarsten Graul 	switch (llc_type) {
2002ef79d439SKarsten Graul 	case SMC_LLC_TEST_LINK:
2003741a49a4SKarsten Graul 		if (smc_link_active(link))
2004ef79d439SKarsten Graul 			complete(&link->llc_testlink_resp);
2005ef79d439SKarsten Graul 		break;
2006ef79d439SKarsten Graul 	case SMC_LLC_ADD_LINK:
200787f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
20082ff08678SKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
20092ff08678SKarsten Graul 		if (flowtype != SMC_LLC_FLOW_ADD_LINK || flow->qentry)
20102ff08678SKarsten Graul 			break;	/* drop out-of-flow response */
20112ff08678SKarsten Graul 		goto assign;
20122ff08678SKarsten Graul 	case SMC_LLC_DELETE_LINK:
20132ff08678SKarsten Graul 		if (flowtype != SMC_LLC_FLOW_DEL_LINK || flow->qentry)
20142ff08678SKarsten Graul 			break;	/* drop out-of-flow response */
20152ff08678SKarsten Graul 		goto assign;
20163d88a21bSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
20176d74c3a8SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
20182ff08678SKarsten Graul 		if (flowtype != SMC_LLC_FLOW_RKEY || flow->qentry)
20192ff08678SKarsten Graul 			break;	/* drop out-of-flow response */
20202ff08678SKarsten Graul 		goto assign;
2021ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
202242d18accSKarsten Graul 		/* not used because max links is 3 */
2023ef79d439SKarsten Graul 		break;
20243e0c40afSKarsten Graul 	default:
2025b4ba4652SKarsten Graul 		smc_llc_protocol_violation(link->lgr,
2026b4ba4652SKarsten Graul 					   qentry->msg.raw.hdr.common.type);
20273e0c40afSKarsten Graul 		break;
2028ef79d439SKarsten Graul 	}
2029a6688d91SKarsten Graul 	kfree(qentry);
20302ff08678SKarsten Graul 	return;
20312ff08678SKarsten Graul assign:
20322ff08678SKarsten Graul 	/* assign responses to the local flow, we requested them */
20332ff08678SKarsten Graul 	smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry);
20342ff08678SKarsten Graul 	wake_up(&link->lgr->llc_msg_waiter);
2035ef79d439SKarsten Graul }
2036ef79d439SKarsten Graul 
2037a6688d91SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
20386c8968c4SKarsten Graul {
20396c8968c4SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
20406c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
20416c8968c4SKarsten Graul 	unsigned long flags;
20426c8968c4SKarsten Graul 
20436c8968c4SKarsten Graul 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
20446c8968c4SKarsten Graul 	if (!qentry)
20456c8968c4SKarsten Graul 		return;
20466c8968c4SKarsten Graul 	qentry->link = link;
20476c8968c4SKarsten Graul 	INIT_LIST_HEAD(&qentry->list);
20486c8968c4SKarsten Graul 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
2049a6688d91SKarsten Graul 
2050a6688d91SKarsten Graul 	/* process responses immediately */
2051b4ba4652SKarsten Graul 	if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) &&
2052b4ba4652SKarsten Graul 	    llc->raw.hdr.common.llc_type != SMC_LLC_REQ_ADD_LINK) {
2053a6688d91SKarsten Graul 		smc_llc_rx_response(link, qentry);
2054a6688d91SKarsten Graul 		return;
2055a6688d91SKarsten Graul 	}
2056a6688d91SKarsten Graul 
2057a6688d91SKarsten Graul 	/* add requests to event queue */
20586c8968c4SKarsten Graul 	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
20596c8968c4SKarsten Graul 	list_add_tail(&qentry->list, &lgr->llc_event_q);
20606c8968c4SKarsten Graul 	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
206122ef473dSKarsten Graul 	queue_work(system_highpri_wq, &lgr->llc_event_work);
20629bf9abeaSUrsula Braun }
20639bf9abeaSUrsula Braun 
2064a6688d91SKarsten Graul /* copy received msg and add it to the event queue */
2065a6688d91SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
2066a6688d91SKarsten Graul {
2067a6688d91SKarsten Graul 	struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
2068a6688d91SKarsten Graul 	union smc_llc_msg *llc = buf;
2069a6688d91SKarsten Graul 
2070a6688d91SKarsten Graul 	if (wc->byte_len < sizeof(*llc))
2071a6688d91SKarsten Graul 		return; /* short message */
2072b4ba4652SKarsten Graul 	if (!llc->raw.hdr.common.llc_version) {
2073a6688d91SKarsten Graul 		if (llc->raw.hdr.length != sizeof(*llc))
2074a6688d91SKarsten Graul 			return; /* invalid message */
2075b4ba4652SKarsten Graul 	} else {
2076b4ba4652SKarsten Graul 		if (llc->raw.hdr.length_v2 < sizeof(*llc))
2077b4ba4652SKarsten Graul 			return; /* invalid message */
2078b4ba4652SKarsten Graul 	}
2079a6688d91SKarsten Graul 
2080a6688d91SKarsten Graul 	smc_llc_enqueue(link, llc);
2081a6688d91SKarsten Graul }
2082a6688d91SKarsten Graul 
208344aa81ceSKarsten Graul /***************************** worker, utils *********************************/
2084877ae5beSKarsten Graul 
2085877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work)
2086877ae5beSKarsten Graul {
2087877ae5beSKarsten Graul 	struct smc_link *link = container_of(to_delayed_work(work),
2088877ae5beSKarsten Graul 					     struct smc_link, llc_testlink_wrk);
2089877ae5beSKarsten Graul 	unsigned long next_interval;
2090877ae5beSKarsten Graul 	unsigned long expire_time;
2091877ae5beSKarsten Graul 	u8 user_data[16] = { 0 };
2092877ae5beSKarsten Graul 	int rc;
2093877ae5beSKarsten Graul 
2094741a49a4SKarsten Graul 	if (!smc_link_active(link))
2095877ae5beSKarsten Graul 		return;		/* don't reschedule worker */
2096877ae5beSKarsten Graul 	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
2097877ae5beSKarsten Graul 	if (time_is_after_jiffies(expire_time)) {
2098877ae5beSKarsten Graul 		next_interval = expire_time - jiffies;
2099877ae5beSKarsten Graul 		goto out;
2100877ae5beSKarsten Graul 	}
2101877ae5beSKarsten Graul 	reinit_completion(&link->llc_testlink_resp);
2102d97935faSKarsten Graul 	smc_llc_send_test_link(link, user_data);
2103877ae5beSKarsten Graul 	/* receive TEST LINK response over RoCE fabric */
2104877ae5beSKarsten Graul 	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
2105877ae5beSKarsten Graul 						       SMC_LLC_WAIT_TIME);
2106741a49a4SKarsten Graul 	if (!smc_link_active(link))
21071020e1efSKarsten Graul 		return;		/* link state changed */
2108877ae5beSKarsten Graul 	if (rc <= 0) {
210987523930SKarsten Graul 		smcr_link_down_cond_sched(link);
2110877ae5beSKarsten Graul 		return;
2111877ae5beSKarsten Graul 	}
2112877ae5beSKarsten Graul 	next_interval = link->llc_testlink_time;
2113877ae5beSKarsten Graul out:
21141020e1efSKarsten Graul 	schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
2115877ae5beSKarsten Graul }
2116877ae5beSKarsten Graul 
211700a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
211800a049cfSKarsten Graul {
211900a049cfSKarsten Graul 	struct net *net = sock_net(smc->clcsock->sk);
212000a049cfSKarsten Graul 
212100a049cfSKarsten Graul 	INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
2122b45e7f98SKarsten Graul 	INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work);
21239ec6bf19SKarsten Graul 	INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work);
212400a049cfSKarsten Graul 	INIT_LIST_HEAD(&lgr->llc_event_q);
212500a049cfSKarsten Graul 	spin_lock_init(&lgr->llc_event_q_lock);
2126555da9afSKarsten Graul 	spin_lock_init(&lgr->llc_flow_lock);
21276778a6beSKarsten Graul 	init_waitqueue_head(&lgr->llc_flow_waiter);
21286778a6beSKarsten Graul 	init_waitqueue_head(&lgr->llc_msg_waiter);
2129*b5dd4d69SD. Wythe 	init_rwsem(&lgr->llc_conf_mutex);
213077eee325SWen Gu 	lgr->llc_testlink_time = READ_ONCE(net->smc.sysctl_smcr_testlink_time);
213100a049cfSKarsten Graul }
213200a049cfSKarsten Graul 
213300a049cfSKarsten Graul /* called after lgr was removed from lgr_list */
213400a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr)
213500a049cfSKarsten Graul {
213600a049cfSKarsten Graul 	smc_llc_event_flush(lgr);
21376778a6beSKarsten Graul 	wake_up_all(&lgr->llc_flow_waiter);
21386778a6beSKarsten Graul 	wake_up_all(&lgr->llc_msg_waiter);
213900a049cfSKarsten Graul 	cancel_work_sync(&lgr->llc_event_work);
2140b45e7f98SKarsten Graul 	cancel_work_sync(&lgr->llc_add_link_work);
21419ec6bf19SKarsten Graul 	cancel_work_sync(&lgr->llc_del_link_work);
2142555da9afSKarsten Graul 	if (lgr->delayed_event) {
2143555da9afSKarsten Graul 		kfree(lgr->delayed_event);
2144555da9afSKarsten Graul 		lgr->delayed_event = NULL;
2145555da9afSKarsten Graul 	}
214600a049cfSKarsten Graul }
214700a049cfSKarsten Graul 
21482a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link)
2149877ae5beSKarsten Graul {
2150877ae5beSKarsten Graul 	init_completion(&link->llc_testlink_resp);
2151877ae5beSKarsten Graul 	INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
21522a4c57a9SKarsten Graul 	return 0;
2153b32cf4abSKarsten Graul }
2154b32cf4abSKarsten Graul 
215500a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link)
2156b32cf4abSKarsten Graul {
2157de2fea7bSTony Lu 	pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link added: id %*phN, "
21580a99be43SKarsten Graul 			    "peerid %*phN, ibdev %s, ibport %d\n",
21590a99be43SKarsten Graul 			    SMC_LGR_ID_SIZE, &link->lgr->id,
2160de2fea7bSTony Lu 			    link->lgr->net->net_cookie,
21610a99be43SKarsten Graul 			    SMC_LGR_ID_SIZE, &link->link_uid,
21620a99be43SKarsten Graul 			    SMC_LGR_ID_SIZE, &link->peer_link_uid,
21630a99be43SKarsten Graul 			    link->smcibdev->ibdev->name, link->ibport);
2164877ae5beSKarsten Graul 	link->state = SMC_LNK_ACTIVE;
216500a049cfSKarsten Graul 	if (link->lgr->llc_testlink_time) {
2166c4a146c7STony Lu 		link->llc_testlink_time = link->lgr->llc_testlink_time;
21671020e1efSKarsten Graul 		schedule_delayed_work(&link->llc_testlink_wrk,
2168877ae5beSKarsten Graul 				      link->llc_testlink_time);
2169877ae5beSKarsten Graul 	}
2170877ae5beSKarsten Graul }
2171877ae5beSKarsten Graul 
2172877ae5beSKarsten Graul /* called in worker context */
21730a99be43SKarsten Graul void smc_llc_link_clear(struct smc_link *link, bool log)
2174877ae5beSKarsten Graul {
21750a99be43SKarsten Graul 	if (log)
2176de2fea7bSTony Lu 		pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link removed: id %*phN"
21770a99be43SKarsten Graul 				    ", peerid %*phN, ibdev %s, ibport %d\n",
21780a99be43SKarsten Graul 				    SMC_LGR_ID_SIZE, &link->lgr->id,
2179de2fea7bSTony Lu 				    link->lgr->net->net_cookie,
21800a99be43SKarsten Graul 				    SMC_LGR_ID_SIZE, &link->link_uid,
21810a99be43SKarsten Graul 				    SMC_LGR_ID_SIZE, &link->peer_link_uid,
21820a99be43SKarsten Graul 				    link->smcibdev->ibdev->name, link->ibport);
21832140ac26SKarsten Graul 	complete(&link->llc_testlink_resp);
21842140ac26SKarsten Graul 	cancel_delayed_work_sync(&link->llc_testlink_wrk);
2185877ae5beSKarsten Graul }
2186877ae5beSKarsten Graul 
21873d88a21bSKarsten Graul /* register a new rtoken at the remote peer (for all links) */
21883d88a21bSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *send_link,
218944aa81ceSKarsten Graul 			    struct smc_buf_desc *rmb_desc)
219044aa81ceSKarsten Graul {
21913d88a21bSKarsten Graul 	struct smc_link_group *lgr = send_link->lgr;
21923d88a21bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
21933d88a21bSKarsten Graul 	int rc = 0;
219444aa81ceSKarsten Graul 
21953d88a21bSKarsten Graul 	rc = smc_llc_send_confirm_rkey(send_link, rmb_desc);
21963d88a21bSKarsten Graul 	if (rc)
21973d88a21bSKarsten Graul 		goto out;
219844aa81ceSKarsten Graul 	/* receive CONFIRM RKEY response from server over RoCE fabric */
21993d88a21bSKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
22003d88a21bSKarsten Graul 			      SMC_LLC_CONFIRM_RKEY);
22013d88a21bSKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
22023d88a21bSKarsten Graul 		rc = -EFAULT;
22033d88a21bSKarsten Graul out:
22043d88a21bSKarsten Graul 	if (qentry)
22053d88a21bSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
22063d88a21bSKarsten Graul 	return rc;
220744aa81ceSKarsten Graul }
220844aa81ceSKarsten Graul 
220960e03c62SKarsten Graul /* unregister an rtoken at the remote peer */
22106d74c3a8SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link_group *lgr,
221160e03c62SKarsten Graul 			   struct smc_buf_desc *rmb_desc)
221260e03c62SKarsten Graul {
22136d74c3a8SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
22146d74c3a8SKarsten Graul 	struct smc_link *send_link;
22150b29ec64SUrsula Braun 	int rc = 0;
221660e03c62SKarsten Graul 
22176d74c3a8SKarsten Graul 	send_link = smc_llc_usable_link(lgr);
22186d74c3a8SKarsten Graul 	if (!send_link)
22196d74c3a8SKarsten Graul 		return -ENOLINK;
22206d74c3a8SKarsten Graul 
22216d74c3a8SKarsten Graul 	/* protected by llc_flow control */
22226d74c3a8SKarsten Graul 	rc = smc_llc_send_delete_rkey(send_link, rmb_desc);
222360e03c62SKarsten Graul 	if (rc)
222460e03c62SKarsten Graul 		goto out;
222560e03c62SKarsten Graul 	/* receive DELETE RKEY response from server over RoCE fabric */
22266d74c3a8SKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
22276d74c3a8SKarsten Graul 			      SMC_LLC_DELETE_RKEY);
22286d74c3a8SKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
222960e03c62SKarsten Graul 		rc = -EFAULT;
223060e03c62SKarsten Graul out:
22316d74c3a8SKarsten Graul 	if (qentry)
22326d74c3a8SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
223360e03c62SKarsten Graul 	return rc;
223460e03c62SKarsten Graul }
223560e03c62SKarsten Graul 
223645fa8da0SKarsten Graul void smc_llc_link_set_uid(struct smc_link *link)
223745fa8da0SKarsten Graul {
223845fa8da0SKarsten Graul 	__be32 link_uid;
223945fa8da0SKarsten Graul 
224045fa8da0SKarsten Graul 	link_uid = htonl(*((u32 *)link->lgr->id) + link->link_id);
224145fa8da0SKarsten Graul 	memcpy(link->link_uid, &link_uid, SMC_LGR_ID_SIZE);
224245fa8da0SKarsten Graul }
224345fa8da0SKarsten Graul 
2244649758ffSKarsten Graul /* save peers link user id, used for debug purposes */
2245649758ffSKarsten Graul void smc_llc_save_peer_uid(struct smc_llc_qentry *qentry)
2246649758ffSKarsten Graul {
2247649758ffSKarsten Graul 	memcpy(qentry->link->peer_link_uid, qentry->msg.confirm_link.link_uid,
2248649758ffSKarsten Graul 	       SMC_LGR_ID_SIZE);
2249649758ffSKarsten Graul }
2250649758ffSKarsten Graul 
225192334cfcSKarsten Graul /* evaluate confirm link request or response */
225292334cfcSKarsten Graul int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry,
225392334cfcSKarsten Graul 			   enum smc_llc_reqresp type)
225492334cfcSKarsten Graul {
225545fa8da0SKarsten Graul 	if (type == SMC_LLC_REQ) {	/* SMC server assigns link_id */
225692334cfcSKarsten Graul 		qentry->link->link_id = qentry->msg.confirm_link.link_num;
225745fa8da0SKarsten Graul 		smc_llc_link_set_uid(qentry->link);
225845fa8da0SKarsten Graul 	}
225992334cfcSKarsten Graul 	if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
226092334cfcSKarsten Graul 		return -ENOTSUPP;
226192334cfcSKarsten Graul 	return 0;
226292334cfcSKarsten Graul }
226392334cfcSKarsten Graul 
22649bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/
22659bf9abeaSUrsula Braun 
22669bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
22679bf9abeaSUrsula Braun 	{
22689bf9abeaSUrsula Braun 		.handler	= smc_llc_rx_handler,
22699bf9abeaSUrsula Braun 		.type		= SMC_LLC_CONFIRM_LINK
22709bf9abeaSUrsula Braun 	},
22719bf9abeaSUrsula Braun 	{
2272313164daSKarsten Graul 		.handler	= smc_llc_rx_handler,
2273313164daSKarsten Graul 		.type		= SMC_LLC_TEST_LINK
2274313164daSKarsten Graul 	},
2275313164daSKarsten Graul 	{
22764ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
227752bedf37SKarsten Graul 		.type		= SMC_LLC_ADD_LINK
227852bedf37SKarsten Graul 	},
227952bedf37SKarsten Graul 	{
228052bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
228187f88cdaSKarsten Graul 		.type		= SMC_LLC_ADD_LINK_CONT
228287f88cdaSKarsten Graul 	},
228387f88cdaSKarsten Graul 	{
228487f88cdaSKarsten Graul 		.handler	= smc_llc_rx_handler,
228552bedf37SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK
228652bedf37SKarsten Graul 	},
228752bedf37SKarsten Graul 	{
228852bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
22894ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY
22904ed75de5SKarsten Graul 	},
22914ed75de5SKarsten Graul 	{
22924ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
22934ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_CONT
22944ed75de5SKarsten Graul 	},
22954ed75de5SKarsten Graul 	{
22964ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
22974ed75de5SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY
22984ed75de5SKarsten Graul 	},
2299b4ba4652SKarsten Graul 	/* V2 types */
2300b4ba4652SKarsten Graul 	{
2301b4ba4652SKarsten Graul 		.handler	= smc_llc_rx_handler,
2302b4ba4652SKarsten Graul 		.type		= SMC_LLC_CONFIRM_LINK_V2
2303b4ba4652SKarsten Graul 	},
2304b4ba4652SKarsten Graul 	{
2305b4ba4652SKarsten Graul 		.handler	= smc_llc_rx_handler,
2306b4ba4652SKarsten Graul 		.type		= SMC_LLC_TEST_LINK_V2
2307b4ba4652SKarsten Graul 	},
2308b4ba4652SKarsten Graul 	{
2309b4ba4652SKarsten Graul 		.handler	= smc_llc_rx_handler,
2310b4ba4652SKarsten Graul 		.type		= SMC_LLC_ADD_LINK_V2
2311b4ba4652SKarsten Graul 	},
2312b4ba4652SKarsten Graul 	{
2313b4ba4652SKarsten Graul 		.handler	= smc_llc_rx_handler,
2314b4ba4652SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK_V2
2315b4ba4652SKarsten Graul 	},
2316b4ba4652SKarsten Graul 	{
2317b4ba4652SKarsten Graul 		.handler	= smc_llc_rx_handler,
2318b4ba4652SKarsten Graul 		.type		= SMC_LLC_REQ_ADD_LINK_V2
2319b4ba4652SKarsten Graul 	},
2320b4ba4652SKarsten Graul 	{
2321b4ba4652SKarsten Graul 		.handler	= smc_llc_rx_handler,
2322b4ba4652SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_V2
2323b4ba4652SKarsten Graul 	},
2324b4ba4652SKarsten Graul 	{
2325b4ba4652SKarsten Graul 		.handler	= smc_llc_rx_handler,
2326b4ba4652SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY_V2
2327b4ba4652SKarsten Graul 	},
23284ed75de5SKarsten Graul 	{
23299bf9abeaSUrsula Braun 		.handler	= NULL,
23309bf9abeaSUrsula Braun 	}
23319bf9abeaSUrsula Braun };
23329bf9abeaSUrsula Braun 
23339bf9abeaSUrsula Braun int __init smc_llc_init(void)
23349bf9abeaSUrsula Braun {
23359bf9abeaSUrsula Braun 	struct smc_wr_rx_handler *handler;
23369bf9abeaSUrsula Braun 	int rc = 0;
23379bf9abeaSUrsula Braun 
23389bf9abeaSUrsula Braun 	for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
23399bf9abeaSUrsula Braun 		INIT_HLIST_NODE(&handler->list);
23409bf9abeaSUrsula Braun 		rc = smc_wr_rx_register_handler(handler);
23419bf9abeaSUrsula Braun 		if (rc)
23429bf9abeaSUrsula Braun 			break;
23439bf9abeaSUrsula Braun 	}
23449bf9abeaSUrsula Braun 	return rc;
23459bf9abeaSUrsula Braun }
2346