xref: /linux/net/smc/smc_llc.c (revision 1551c95b61242b1a20565bae8d711f35a601c4f3)
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;
260f627126SStefan Raspl 	u8 length;	/* 44 */
2752bedf37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD)
2852bedf37SKarsten Graul 	u8 reserved:4,
2952bedf37SKarsten Graul 	   add_link_rej_rsn:4;
3052bedf37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD)
3152bedf37SKarsten Graul 	u8 add_link_rej_rsn:4,
3252bedf37SKarsten Graul 	   reserved:4;
3352bedf37SKarsten Graul #endif
340f627126SStefan Raspl 	u8 flags;
350f627126SStefan Raspl };
360f627126SStefan Raspl 
3775d320d6SKarsten Graul #define SMC_LLC_FLAG_NO_RMBE_EYEC	0x03
3875d320d6SKarsten Graul 
390f627126SStefan Raspl struct smc_llc_msg_confirm_link {	/* type 0x01 */
400f627126SStefan Raspl 	struct smc_llc_hdr hd;
410f627126SStefan Raspl 	u8 sender_mac[ETH_ALEN];
420f627126SStefan Raspl 	u8 sender_gid[SMC_GID_SIZE];
430f627126SStefan Raspl 	u8 sender_qp_num[3];
440f627126SStefan Raspl 	u8 link_num;
450f627126SStefan Raspl 	u8 link_uid[SMC_LGR_ID_SIZE];
460f627126SStefan Raspl 	u8 max_links;
470f627126SStefan Raspl 	u8 reserved[9];
480f627126SStefan Raspl };
490f627126SStefan Raspl 
5052bedf37SKarsten Graul #define SMC_LLC_FLAG_ADD_LNK_REJ	0x40
5152bedf37SKarsten Graul #define SMC_LLC_REJ_RSN_NO_ALT_PATH	1
5252bedf37SKarsten Graul 
5352bedf37SKarsten Graul #define SMC_LLC_ADD_LNK_MAX_LINKS	2
5452bedf37SKarsten Graul 
5552bedf37SKarsten Graul struct smc_llc_msg_add_link {		/* type 0x02 */
5652bedf37SKarsten Graul 	struct smc_llc_hdr hd;
5752bedf37SKarsten Graul 	u8 sender_mac[ETH_ALEN];
5852bedf37SKarsten Graul 	u8 reserved2[2];
5952bedf37SKarsten Graul 	u8 sender_gid[SMC_GID_SIZE];
6052bedf37SKarsten Graul 	u8 sender_qp_num[3];
6152bedf37SKarsten Graul 	u8 link_num;
62fbed3b37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD)
63fbed3b37SKarsten Graul 	u8 reserved3 : 4,
64fbed3b37SKarsten Graul 	   qp_mtu   : 4;
65fbed3b37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD)
66fbed3b37SKarsten Graul 	u8 qp_mtu   : 4,
67fbed3b37SKarsten Graul 	   reserved3 : 4;
68fbed3b37SKarsten Graul #endif
6952bedf37SKarsten Graul 	u8 initial_psn[3];
7052bedf37SKarsten Graul 	u8 reserved[8];
7152bedf37SKarsten Graul };
7252bedf37SKarsten Graul 
7387f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont_rt {
7487f88cdaSKarsten Graul 	__be32 rmb_key;
7587f88cdaSKarsten Graul 	__be32 rmb_key_new;
7687f88cdaSKarsten Graul 	__be64 rmb_vaddr_new;
7787f88cdaSKarsten Graul };
7887f88cdaSKarsten Graul 
7987f88cdaSKarsten Graul #define SMC_LLC_RKEYS_PER_CONT_MSG	2
8087f88cdaSKarsten Graul 
8187f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont {	/* type 0x03 */
8287f88cdaSKarsten Graul 	struct smc_llc_hdr hd;
8387f88cdaSKarsten Graul 	u8 link_num;
8487f88cdaSKarsten Graul 	u8 num_rkeys;
8587f88cdaSKarsten Graul 	u8 reserved2[2];
8687f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG];
8787f88cdaSKarsten Graul 	u8 reserved[4];
8887f88cdaSKarsten Graul } __packed;			/* format defined in RFC7609 */
8987f88cdaSKarsten Graul 
9052bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ALL	0x40
9152bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ORDERLY	0x20
9252bedf37SKarsten Graul 
9352bedf37SKarsten Graul struct smc_llc_msg_del_link {		/* type 0x04 */
9452bedf37SKarsten Graul 	struct smc_llc_hdr hd;
9552bedf37SKarsten Graul 	u8 link_num;
9652bedf37SKarsten Graul 	__be32 reason;
9752bedf37SKarsten Graul 	u8 reserved[35];
9852bedf37SKarsten Graul } __packed;			/* format defined in RFC7609 */
9952bedf37SKarsten Graul 
100313164daSKarsten Graul struct smc_llc_msg_test_link {		/* type 0x07 */
101313164daSKarsten Graul 	struct smc_llc_hdr hd;
102313164daSKarsten Graul 	u8 user_data[16];
103313164daSKarsten Graul 	u8 reserved[24];
104313164daSKarsten Graul };
105313164daSKarsten Graul 
1064ed75de5SKarsten Graul struct smc_rmb_rtoken {
1074ed75de5SKarsten Graul 	union {
1084ed75de5SKarsten Graul 		u8 num_rkeys;	/* first rtoken byte of CONFIRM LINK msg */
1094ed75de5SKarsten Graul 				/* is actually the num of rtokens, first */
1104ed75de5SKarsten Graul 				/* rtoken is always for the current link */
1114ed75de5SKarsten Graul 		u8 link_id;	/* link id of the rtoken */
1124ed75de5SKarsten Graul 	};
1134ed75de5SKarsten Graul 	__be32 rmb_key;
1144ed75de5SKarsten Graul 	__be64 rmb_vaddr;
1154ed75de5SKarsten Graul } __packed;			/* format defined in RFC7609 */
1164ed75de5SKarsten Graul 
1174ed75de5SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG	3
1184ed75de5SKarsten Graul 
1194ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey {	/* type 0x06 */
1204ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1214ed75de5SKarsten Graul 	struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
1224ed75de5SKarsten Graul 	u8 reserved;
1234ed75de5SKarsten Graul };
1244ed75de5SKarsten Graul 
1254ed75de5SKarsten Graul #define SMC_LLC_DEL_RKEY_MAX	8
1263bc67e09SKarsten Graul #define SMC_LLC_FLAG_RKEY_RETRY	0x10
1274ed75de5SKarsten Graul #define SMC_LLC_FLAG_RKEY_NEG	0x20
1284ed75de5SKarsten Graul 
1294ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey {	/* type 0x09 */
1304ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1314ed75de5SKarsten Graul 	u8 num_rkeys;
1324ed75de5SKarsten Graul 	u8 err_mask;
1334ed75de5SKarsten Graul 	u8 reserved[2];
1344ed75de5SKarsten Graul 	__be32 rkey[8];
1354ed75de5SKarsten Graul 	u8 reserved2[4];
1364ed75de5SKarsten Graul };
1374ed75de5SKarsten Graul 
1380f627126SStefan Raspl union smc_llc_msg {
1390f627126SStefan Raspl 	struct smc_llc_msg_confirm_link confirm_link;
14052bedf37SKarsten Graul 	struct smc_llc_msg_add_link add_link;
14187f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont add_link_cont;
14252bedf37SKarsten Graul 	struct smc_llc_msg_del_link delete_link;
1434ed75de5SKarsten Graul 
1444ed75de5SKarsten Graul 	struct smc_llc_msg_confirm_rkey confirm_rkey;
1454ed75de5SKarsten Graul 	struct smc_llc_msg_delete_rkey delete_rkey;
1464ed75de5SKarsten Graul 
147313164daSKarsten Graul 	struct smc_llc_msg_test_link test_link;
1480f627126SStefan Raspl 	struct {
1490f627126SStefan Raspl 		struct smc_llc_hdr hdr;
1500f627126SStefan Raspl 		u8 data[SMC_LLC_DATA_LEN];
1510f627126SStefan Raspl 	} raw;
1520f627126SStefan Raspl };
1530f627126SStefan Raspl 
1540f627126SStefan Raspl #define SMC_LLC_FLAG_RESP		0x80
1550f627126SStefan Raspl 
1566c8968c4SKarsten Graul struct smc_llc_qentry {
1576c8968c4SKarsten Graul 	struct list_head list;
1586c8968c4SKarsten Graul 	struct smc_link *link;
1596c8968c4SKarsten Graul 	union smc_llc_msg msg;
1606c8968c4SKarsten Graul };
1616c8968c4SKarsten Graul 
162555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
163555da9afSKarsten Graul {
164555da9afSKarsten Graul 	struct smc_llc_qentry *qentry = flow->qentry;
165555da9afSKarsten Graul 
166555da9afSKarsten Graul 	flow->qentry = NULL;
167555da9afSKarsten Graul 	return qentry;
168555da9afSKarsten Graul }
169555da9afSKarsten Graul 
170555da9afSKarsten Graul void smc_llc_flow_qentry_del(struct smc_llc_flow *flow)
171555da9afSKarsten Graul {
172555da9afSKarsten Graul 	struct smc_llc_qentry *qentry;
173555da9afSKarsten Graul 
174555da9afSKarsten Graul 	if (flow->qentry) {
175555da9afSKarsten Graul 		qentry = flow->qentry;
176555da9afSKarsten Graul 		flow->qentry = NULL;
177555da9afSKarsten Graul 		kfree(qentry);
178555da9afSKarsten Graul 	}
179555da9afSKarsten Graul }
180555da9afSKarsten Graul 
181555da9afSKarsten Graul static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow,
182555da9afSKarsten Graul 					   struct smc_llc_qentry *qentry)
183555da9afSKarsten Graul {
184555da9afSKarsten Graul 	flow->qentry = qentry;
185555da9afSKarsten Graul }
186555da9afSKarsten Graul 
187555da9afSKarsten Graul /* try to start a new llc flow, initiated by an incoming llc msg */
188555da9afSKarsten Graul static bool smc_llc_flow_start(struct smc_llc_flow *flow,
189555da9afSKarsten Graul 			       struct smc_llc_qentry *qentry)
190555da9afSKarsten Graul {
191555da9afSKarsten Graul 	struct smc_link_group *lgr = qentry->link->lgr;
192555da9afSKarsten Graul 
193555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
194555da9afSKarsten Graul 	if (flow->type) {
195555da9afSKarsten Graul 		/* a flow is already active */
196555da9afSKarsten Graul 		if ((qentry->msg.raw.hdr.common.type == SMC_LLC_ADD_LINK ||
197555da9afSKarsten Graul 		     qentry->msg.raw.hdr.common.type == SMC_LLC_DELETE_LINK) &&
198555da9afSKarsten Graul 		    !lgr->delayed_event) {
199555da9afSKarsten Graul 			lgr->delayed_event = qentry;
200555da9afSKarsten Graul 		} else {
201555da9afSKarsten Graul 			/* forget this llc request */
202555da9afSKarsten Graul 			kfree(qentry);
203555da9afSKarsten Graul 		}
204555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
205555da9afSKarsten Graul 		return false;
206555da9afSKarsten Graul 	}
207555da9afSKarsten Graul 	switch (qentry->msg.raw.hdr.common.type) {
208555da9afSKarsten Graul 	case SMC_LLC_ADD_LINK:
209555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_ADD_LINK;
210555da9afSKarsten Graul 		break;
211555da9afSKarsten Graul 	case SMC_LLC_DELETE_LINK:
212555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_DEL_LINK;
213555da9afSKarsten Graul 		break;
214555da9afSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
215555da9afSKarsten Graul 	case SMC_LLC_DELETE_RKEY:
216555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_RKEY;
217555da9afSKarsten Graul 		break;
218555da9afSKarsten Graul 	default:
219555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_NONE;
220555da9afSKarsten Graul 	}
221555da9afSKarsten Graul 	if (qentry == lgr->delayed_event)
222555da9afSKarsten Graul 		lgr->delayed_event = NULL;
223555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
224555da9afSKarsten Graul 	smc_llc_flow_qentry_set(flow, qentry);
225555da9afSKarsten Graul 	return true;
226555da9afSKarsten Graul }
227555da9afSKarsten Graul 
228555da9afSKarsten Graul /* start a new local llc flow, wait till current flow finished */
229555da9afSKarsten Graul int smc_llc_flow_initiate(struct smc_link_group *lgr,
230555da9afSKarsten Graul 			  enum smc_llc_flowtype type)
231555da9afSKarsten Graul {
232555da9afSKarsten Graul 	enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE;
233555da9afSKarsten Graul 	int rc;
234555da9afSKarsten Graul 
235555da9afSKarsten Graul 	/* all flows except confirm_rkey and delete_rkey are exclusive,
236555da9afSKarsten Graul 	 * confirm/delete rkey flows can run concurrently (local and remote)
237555da9afSKarsten Graul 	 */
238555da9afSKarsten Graul 	if (type == SMC_LLC_FLOW_RKEY)
239555da9afSKarsten Graul 		allowed_remote = SMC_LLC_FLOW_RKEY;
240555da9afSKarsten Graul again:
241555da9afSKarsten Graul 	if (list_empty(&lgr->list))
242555da9afSKarsten Graul 		return -ENODEV;
243555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
244555da9afSKarsten Graul 	if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
245555da9afSKarsten Graul 	    (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
246555da9afSKarsten Graul 	     lgr->llc_flow_rmt.type == allowed_remote)) {
247555da9afSKarsten Graul 		lgr->llc_flow_lcl.type = type;
248555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
249555da9afSKarsten Graul 		return 0;
250555da9afSKarsten Graul 	}
251555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
252555da9afSKarsten Graul 	rc = wait_event_interruptible_timeout(lgr->llc_waiter,
253555da9afSKarsten Graul 			(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
254555da9afSKarsten Graul 			 (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
255555da9afSKarsten Graul 			  lgr->llc_flow_rmt.type == allowed_remote)),
256555da9afSKarsten Graul 			SMC_LLC_WAIT_TIME);
257555da9afSKarsten Graul 	if (!rc)
258555da9afSKarsten Graul 		return -ETIMEDOUT;
259555da9afSKarsten Graul 	goto again;
260555da9afSKarsten Graul }
261555da9afSKarsten Graul 
262555da9afSKarsten Graul /* finish the current llc flow */
263555da9afSKarsten Graul void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)
264555da9afSKarsten Graul {
265555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
266555da9afSKarsten Graul 	memset(flow, 0, sizeof(*flow));
267555da9afSKarsten Graul 	flow->type = SMC_LLC_FLOW_NONE;
268555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
269555da9afSKarsten Graul 	if (!list_empty(&lgr->list) && lgr->delayed_event &&
270555da9afSKarsten Graul 	    flow == &lgr->llc_flow_lcl)
271555da9afSKarsten Graul 		schedule_work(&lgr->llc_event_work);
272555da9afSKarsten Graul 	else
273555da9afSKarsten Graul 		wake_up_interruptible(&lgr->llc_waiter);
274555da9afSKarsten Graul }
275555da9afSKarsten Graul 
276555da9afSKarsten Graul /* lnk is optional and used for early wakeup when link goes down, useful in
277555da9afSKarsten Graul  * cases where we wait for a response on the link after we sent a request
278555da9afSKarsten Graul  */
279555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr,
280555da9afSKarsten Graul 				    struct smc_link *lnk,
281555da9afSKarsten Graul 				    int time_out, u8 exp_msg)
282555da9afSKarsten Graul {
283555da9afSKarsten Graul 	struct smc_llc_flow *flow = &lgr->llc_flow_lcl;
284555da9afSKarsten Graul 
285555da9afSKarsten Graul 	wait_event_interruptible_timeout(lgr->llc_waiter,
286555da9afSKarsten Graul 					 (flow->qentry ||
287555da9afSKarsten Graul 					  (lnk && !smc_link_usable(lnk)) ||
288555da9afSKarsten Graul 					  list_empty(&lgr->list)),
289555da9afSKarsten Graul 					 time_out);
290555da9afSKarsten Graul 	if (!flow->qentry ||
291555da9afSKarsten Graul 	    (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) {
292555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
293555da9afSKarsten Graul 		goto out;
294555da9afSKarsten Graul 	}
295555da9afSKarsten Graul 	if (exp_msg && flow->qentry->msg.raw.hdr.common.type != exp_msg) {
296555da9afSKarsten Graul 		if (exp_msg == SMC_LLC_ADD_LINK &&
297555da9afSKarsten Graul 		    flow->qentry->msg.raw.hdr.common.type ==
298555da9afSKarsten Graul 		    SMC_LLC_DELETE_LINK) {
299555da9afSKarsten Graul 			/* flow_start will delay the unexpected msg */
300555da9afSKarsten Graul 			smc_llc_flow_start(&lgr->llc_flow_lcl,
301555da9afSKarsten Graul 					   smc_llc_flow_qentry_clr(flow));
302555da9afSKarsten Graul 			return NULL;
303555da9afSKarsten Graul 		}
304555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
305555da9afSKarsten Graul 	}
306555da9afSKarsten Graul out:
307555da9afSKarsten Graul 	return flow->qentry;
308555da9afSKarsten Graul }
309555da9afSKarsten Graul 
3109bf9abeaSUrsula Braun /********************************** send *************************************/
3119bf9abeaSUrsula Braun 
3129bf9abeaSUrsula Braun struct smc_llc_tx_pend {
3139bf9abeaSUrsula Braun };
3149bf9abeaSUrsula Braun 
3159bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */
3169bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend,
3179bf9abeaSUrsula Braun 			       struct smc_link *link,
3189bf9abeaSUrsula Braun 			       enum ib_wc_status wc_status)
3199bf9abeaSUrsula Braun {
3209bf9abeaSUrsula Braun 	/* future work: handle wc_status error for recovery and failover */
3219bf9abeaSUrsula Braun }
3229bf9abeaSUrsula Braun 
3239bf9abeaSUrsula Braun /**
3249bf9abeaSUrsula Braun  * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits
3259bf9abeaSUrsula Braun  * @link: Pointer to SMC link used for sending LLC control message.
3269bf9abeaSUrsula Braun  * @wr_buf: Out variable returning pointer to work request payload buffer.
3279bf9abeaSUrsula Braun  * @pend: Out variable returning pointer to private pending WR tracking.
3289bf9abeaSUrsula Braun  *	  It's the context the transmit complete handler will get.
3299bf9abeaSUrsula Braun  *
3309bf9abeaSUrsula Braun  * Reserves and pre-fills an entry for a pending work request send/tx.
3319bf9abeaSUrsula Braun  * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx.
3329bf9abeaSUrsula Braun  * Can sleep due to smc_get_ctrl_buf (if not in softirq context).
3339bf9abeaSUrsula Braun  *
3349bf9abeaSUrsula Braun  * Return: 0 on success, otherwise an error value.
3359bf9abeaSUrsula Braun  */
3369bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link,
3379bf9abeaSUrsula Braun 				    struct smc_wr_buf **wr_buf,
3389bf9abeaSUrsula Braun 				    struct smc_wr_tx_pend_priv **pend)
3399bf9abeaSUrsula Braun {
3409bf9abeaSUrsula Braun 	int rc;
3419bf9abeaSUrsula Braun 
342ad6f317fSUrsula Braun 	rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL,
343ad6f317fSUrsula Braun 				     pend);
3449bf9abeaSUrsula Braun 	if (rc < 0)
3459bf9abeaSUrsula Braun 		return rc;
3469bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3479bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE,
3489bf9abeaSUrsula Braun 		"must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)");
3499bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3509bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE,
3519bf9abeaSUrsula 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()");
3529bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3539bf9abeaSUrsula Braun 		sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
3549bf9abeaSUrsula Braun 		"must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)");
3559bf9abeaSUrsula Braun 	return 0;
3569bf9abeaSUrsula Braun }
3579bf9abeaSUrsula Braun 
3589bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */
359947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link,
3609bf9abeaSUrsula Braun 			      enum smc_llc_reqresp reqresp)
3619bf9abeaSUrsula Braun {
36200e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
3639bf9abeaSUrsula Braun 	struct smc_llc_msg_confirm_link *confllc;
3649bf9abeaSUrsula Braun 	struct smc_wr_tx_pend_priv *pend;
3659bf9abeaSUrsula Braun 	struct smc_wr_buf *wr_buf;
3669bf9abeaSUrsula Braun 	int rc;
3679bf9abeaSUrsula Braun 
3689bf9abeaSUrsula Braun 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
3699bf9abeaSUrsula Braun 	if (rc)
3709bf9abeaSUrsula Braun 		return rc;
3719bf9abeaSUrsula Braun 	confllc = (struct smc_llc_msg_confirm_link *)wr_buf;
3729bf9abeaSUrsula Braun 	memset(confllc, 0, sizeof(*confllc));
3739bf9abeaSUrsula Braun 	confllc->hd.common.type = SMC_LLC_CONFIRM_LINK;
3749bf9abeaSUrsula Braun 	confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link);
37575d320d6SKarsten Graul 	confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC;
3769bf9abeaSUrsula Braun 	if (reqresp == SMC_LLC_RESP)
3779bf9abeaSUrsula Braun 		confllc->hd.flags |= SMC_LLC_FLAG_RESP;
378947541f3SUrsula Braun 	memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1],
379947541f3SUrsula Braun 	       ETH_ALEN);
3807005ada6SUrsula Braun 	memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE);
3819bf9abeaSUrsula Braun 	hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
3822be922f3SKarsten Graul 	confllc->link_num = link->link_id;
3839bf9abeaSUrsula Braun 	memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE);
384b1570a87SKarsten Graul 	confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS;
38552bedf37SKarsten Graul 	/* send llc message */
38652bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
38752bedf37SKarsten Graul 	return rc;
38852bedf37SKarsten Graul }
38952bedf37SKarsten Graul 
39044aa81ceSKarsten Graul /* send LLC confirm rkey request */
3913d88a21bSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *send_link,
39244aa81ceSKarsten Graul 				     struct smc_buf_desc *rmb_desc)
39344aa81ceSKarsten Graul {
39444aa81ceSKarsten Graul 	struct smc_llc_msg_confirm_rkey *rkeyllc;
39544aa81ceSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
39644aa81ceSKarsten Graul 	struct smc_wr_buf *wr_buf;
3973d88a21bSKarsten Graul 	struct smc_link *link;
3983d88a21bSKarsten Graul 	int i, rc, rtok_ix;
39944aa81ceSKarsten Graul 
4003d88a21bSKarsten Graul 	rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend);
40144aa81ceSKarsten Graul 	if (rc)
40244aa81ceSKarsten Graul 		return rc;
40344aa81ceSKarsten Graul 	rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf;
40444aa81ceSKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
40544aa81ceSKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY;
40644aa81ceSKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey);
4073d88a21bSKarsten Graul 
4083d88a21bSKarsten Graul 	rtok_ix = 1;
4093d88a21bSKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
4103d88a21bSKarsten Graul 		link = &send_link->lgr->lnk[i];
4113d88a21bSKarsten Graul 		if (link->state == SMC_LNK_ACTIVE && link != send_link) {
4123d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].link_id = link->link_id;
4133d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].rmb_key =
414387707fdSKarsten Graul 				htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
4153d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].rmb_vaddr = cpu_to_be64(
4163d88a21bSKarsten Graul 				(u64)sg_dma_address(
4173d88a21bSKarsten Graul 					rmb_desc->sgt[link->link_idx].sgl));
4183d88a21bSKarsten Graul 			rtok_ix++;
4193d88a21bSKarsten Graul 		}
4203d88a21bSKarsten Graul 	}
4213d88a21bSKarsten Graul 	/* rkey of send_link is in rtoken[0] */
4223d88a21bSKarsten Graul 	rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1;
4233d88a21bSKarsten Graul 	rkeyllc->rtoken[0].rmb_key =
4243d88a21bSKarsten Graul 		htonl(rmb_desc->mr_rx[send_link->link_idx]->rkey);
42544aa81ceSKarsten Graul 	rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
4263d88a21bSKarsten Graul 		(u64)sg_dma_address(rmb_desc->sgt[send_link->link_idx].sgl));
42744aa81ceSKarsten Graul 	/* send llc message */
4283d88a21bSKarsten Graul 	rc = smc_wr_tx_send(send_link, pend);
42944aa81ceSKarsten Graul 	return rc;
43044aa81ceSKarsten Graul }
43144aa81ceSKarsten Graul 
43260e03c62SKarsten Graul /* send LLC delete rkey request */
43360e03c62SKarsten Graul static int smc_llc_send_delete_rkey(struct smc_link *link,
43460e03c62SKarsten Graul 				    struct smc_buf_desc *rmb_desc)
43560e03c62SKarsten Graul {
43660e03c62SKarsten Graul 	struct smc_llc_msg_delete_rkey *rkeyllc;
43760e03c62SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
43860e03c62SKarsten Graul 	struct smc_wr_buf *wr_buf;
43960e03c62SKarsten Graul 	int rc;
44060e03c62SKarsten Graul 
44160e03c62SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
44260e03c62SKarsten Graul 	if (rc)
44360e03c62SKarsten Graul 		return rc;
44460e03c62SKarsten Graul 	rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf;
44560e03c62SKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
44660e03c62SKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY;
44760e03c62SKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey);
44860e03c62SKarsten Graul 	rkeyllc->num_rkeys = 1;
449387707fdSKarsten Graul 	rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
45060e03c62SKarsten Graul 	/* send llc message */
45160e03c62SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
45260e03c62SKarsten Graul 	return rc;
45360e03c62SKarsten Graul }
45460e03c62SKarsten Graul 
45552bedf37SKarsten Graul /* send ADD LINK request or response */
4567005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
457fbed3b37SKarsten Graul 			  struct smc_link *link_new,
45852bedf37SKarsten Graul 			  enum smc_llc_reqresp reqresp)
45952bedf37SKarsten Graul {
46052bedf37SKarsten Graul 	struct smc_llc_msg_add_link *addllc;
46152bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
46252bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
46352bedf37SKarsten Graul 	int rc;
46452bedf37SKarsten Graul 
46552bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
46652bedf37SKarsten Graul 	if (rc)
46752bedf37SKarsten Graul 		return rc;
46852bedf37SKarsten Graul 	addllc = (struct smc_llc_msg_add_link *)wr_buf;
469fbed3b37SKarsten Graul 
470fbed3b37SKarsten Graul 	memset(addllc, 0, sizeof(*addllc));
471fbed3b37SKarsten Graul 	addllc->hd.common.type = SMC_LLC_ADD_LINK;
472fbed3b37SKarsten Graul 	addllc->hd.length = sizeof(struct smc_llc_msg_add_link);
473fbed3b37SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
474fbed3b37SKarsten Graul 		addllc->hd.flags |= SMC_LLC_FLAG_RESP;
475fbed3b37SKarsten Graul 	memcpy(addllc->sender_mac, mac, ETH_ALEN);
476fbed3b37SKarsten Graul 	memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
477fbed3b37SKarsten Graul 	if (link_new) {
478fbed3b37SKarsten Graul 		addllc->link_num = link_new->link_id;
479fbed3b37SKarsten Graul 		hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num);
480fbed3b37SKarsten Graul 		hton24(addllc->initial_psn, link_new->psn_initial);
481fbed3b37SKarsten Graul 		if (reqresp == SMC_LLC_REQ)
482fbed3b37SKarsten Graul 			addllc->qp_mtu = link_new->path_mtu;
483fbed3b37SKarsten Graul 		else
484fbed3b37SKarsten Graul 			addllc->qp_mtu = min(link_new->path_mtu,
485fbed3b37SKarsten Graul 					     link_new->peer_mtu);
486fbed3b37SKarsten Graul 	}
48752bedf37SKarsten Graul 	/* send llc message */
48852bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
48952bedf37SKarsten Graul 	return rc;
49052bedf37SKarsten Graul }
49152bedf37SKarsten Graul 
49252bedf37SKarsten Graul /* send DELETE LINK request or response */
493fbed3b37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id,
494fbed3b37SKarsten Graul 			     enum smc_llc_reqresp reqresp, bool orderly,
495fbed3b37SKarsten Graul 			     u32 reason)
49652bedf37SKarsten Graul {
49752bedf37SKarsten Graul 	struct smc_llc_msg_del_link *delllc;
49852bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
49952bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
50052bedf37SKarsten Graul 	int rc;
50152bedf37SKarsten Graul 
50252bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
50352bedf37SKarsten Graul 	if (rc)
50452bedf37SKarsten Graul 		return rc;
50552bedf37SKarsten Graul 	delllc = (struct smc_llc_msg_del_link *)wr_buf;
506fbed3b37SKarsten Graul 
507fbed3b37SKarsten Graul 	memset(delllc, 0, sizeof(*delllc));
508fbed3b37SKarsten Graul 	delllc->hd.common.type = SMC_LLC_DELETE_LINK;
509fbed3b37SKarsten Graul 	delllc->hd.length = sizeof(struct smc_llc_msg_del_link);
510fbed3b37SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
511fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_RESP;
512fbed3b37SKarsten Graul 	if (orderly)
513fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
514fbed3b37SKarsten Graul 	if (link_del_id)
515fbed3b37SKarsten Graul 		delllc->link_num = link_del_id;
516fbed3b37SKarsten Graul 	else
517fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
518fbed3b37SKarsten Graul 	delllc->reason = htonl(reason);
5199bf9abeaSUrsula Braun 	/* send llc message */
5209bf9abeaSUrsula Braun 	rc = smc_wr_tx_send(link, pend);
5219bf9abeaSUrsula Braun 	return rc;
5229bf9abeaSUrsula Braun }
5239bf9abeaSUrsula Braun 
524d97935faSKarsten Graul /* send LLC test link request */
525d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
526313164daSKarsten Graul {
527313164daSKarsten Graul 	struct smc_llc_msg_test_link *testllc;
528313164daSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
529313164daSKarsten Graul 	struct smc_wr_buf *wr_buf;
530313164daSKarsten Graul 	int rc;
531313164daSKarsten Graul 
532313164daSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
533313164daSKarsten Graul 	if (rc)
534313164daSKarsten Graul 		return rc;
535313164daSKarsten Graul 	testllc = (struct smc_llc_msg_test_link *)wr_buf;
536313164daSKarsten Graul 	memset(testllc, 0, sizeof(*testllc));
537313164daSKarsten Graul 	testllc->hd.common.type = SMC_LLC_TEST_LINK;
538313164daSKarsten Graul 	testllc->hd.length = sizeof(struct smc_llc_msg_test_link);
539313164daSKarsten Graul 	memcpy(testllc->user_data, user_data, sizeof(testllc->user_data));
540313164daSKarsten Graul 	/* send llc message */
541313164daSKarsten Graul 	rc = smc_wr_tx_send(link, pend);
542313164daSKarsten Graul 	return rc;
543313164daSKarsten Graul }
544313164daSKarsten Graul 
5456c8968c4SKarsten Graul /* schedule an llc send on link, may wait for buffers */
5466c8968c4SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf)
5474ed75de5SKarsten Graul {
5484ed75de5SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
5494ed75de5SKarsten Graul 	struct smc_wr_buf *wr_buf;
5504ed75de5SKarsten Graul 	int rc;
5514ed75de5SKarsten Graul 
5526c8968c4SKarsten Graul 	if (!smc_link_usable(link))
5536c8968c4SKarsten Graul 		return -ENOLINK;
5546c8968c4SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
5554ed75de5SKarsten Graul 	if (rc)
5566c8968c4SKarsten Graul 		return rc;
5576c8968c4SKarsten Graul 	memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
5586c8968c4SKarsten Graul 	return smc_wr_tx_send(link, pend);
5594ed75de5SKarsten Graul }
5604ed75de5SKarsten Graul 
5619bf9abeaSUrsula Braun /********************************* receive ***********************************/
5629bf9abeaSUrsula Braun 
563336ba09fSKarsten Graul static int smc_llc_alloc_alt_link(struct smc_link_group *lgr,
564336ba09fSKarsten Graul 				  enum smc_lgr_type lgr_new_t)
565336ba09fSKarsten Graul {
566336ba09fSKarsten Graul 	int i;
567336ba09fSKarsten Graul 
568336ba09fSKarsten Graul 	if (lgr->type == SMC_LGR_SYMMETRIC ||
569336ba09fSKarsten Graul 	    (lgr->type != SMC_LGR_SINGLE &&
570336ba09fSKarsten Graul 	     (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
571336ba09fSKarsten Graul 	      lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)))
572336ba09fSKarsten Graul 		return -EMLINK;
573336ba09fSKarsten Graul 
574336ba09fSKarsten Graul 	if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
575336ba09fSKarsten Graul 	    lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) {
576336ba09fSKarsten Graul 		for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--)
577336ba09fSKarsten Graul 			if (lgr->lnk[i].state == SMC_LNK_UNUSED)
578336ba09fSKarsten Graul 				return i;
579336ba09fSKarsten Graul 	} else {
580336ba09fSKarsten Graul 		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
581336ba09fSKarsten Graul 			if (lgr->lnk[i].state == SMC_LNK_UNUSED)
582336ba09fSKarsten Graul 				return i;
583336ba09fSKarsten Graul 	}
584336ba09fSKarsten Graul 	return -EMLINK;
585336ba09fSKarsten Graul }
586336ba09fSKarsten Graul 
58787f88cdaSKarsten Graul /* return first buffer from any of the next buf lists */
58887f88cdaSKarsten Graul static struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr,
58987f88cdaSKarsten Graul 						  int *buf_lst)
59087f88cdaSKarsten Graul {
59187f88cdaSKarsten Graul 	struct smc_buf_desc *buf_pos;
59287f88cdaSKarsten Graul 
59387f88cdaSKarsten Graul 	while (*buf_lst < SMC_RMBE_SIZES) {
59487f88cdaSKarsten Graul 		buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst],
59587f88cdaSKarsten Graul 						   struct smc_buf_desc, list);
59687f88cdaSKarsten Graul 		if (buf_pos)
59787f88cdaSKarsten Graul 			return buf_pos;
59887f88cdaSKarsten Graul 		(*buf_lst)++;
59987f88cdaSKarsten Graul 	}
60087f88cdaSKarsten Graul 	return NULL;
60187f88cdaSKarsten Graul }
60287f88cdaSKarsten Graul 
60387f88cdaSKarsten Graul /* return next rmb from buffer lists */
60487f88cdaSKarsten Graul static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr,
60587f88cdaSKarsten Graul 						 int *buf_lst,
60687f88cdaSKarsten Graul 						 struct smc_buf_desc *buf_pos)
60787f88cdaSKarsten Graul {
60887f88cdaSKarsten Graul 	struct smc_buf_desc *buf_next;
60987f88cdaSKarsten Graul 
61087f88cdaSKarsten Graul 	if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) {
61187f88cdaSKarsten Graul 		(*buf_lst)++;
61287f88cdaSKarsten Graul 		return _smc_llc_get_next_rmb(lgr, buf_lst);
61387f88cdaSKarsten Graul 	}
61487f88cdaSKarsten Graul 	buf_next = list_next_entry(buf_pos, list);
61587f88cdaSKarsten Graul 	return buf_next;
61687f88cdaSKarsten Graul }
61787f88cdaSKarsten Graul 
61887f88cdaSKarsten Graul static struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr,
61987f88cdaSKarsten Graul 						  int *buf_lst)
62087f88cdaSKarsten Graul {
62187f88cdaSKarsten Graul 	*buf_lst = 0;
62287f88cdaSKarsten Graul 	return smc_llc_get_next_rmb(lgr, buf_lst, NULL);
62387f88cdaSKarsten Graul }
62487f88cdaSKarsten Graul 
62587f88cdaSKarsten Graul /* send one add_link_continue msg */
62687f88cdaSKarsten Graul static int smc_llc_add_link_cont(struct smc_link *link,
62787f88cdaSKarsten Graul 				 struct smc_link *link_new, u8 *num_rkeys_todo,
62887f88cdaSKarsten Graul 				 int *buf_lst, struct smc_buf_desc **buf_pos)
62987f88cdaSKarsten Graul {
63087f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
63187f88cdaSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
63287f88cdaSKarsten Graul 	int prim_lnk_idx, lnk_idx, i, rc;
63387f88cdaSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
63487f88cdaSKarsten Graul 	struct smc_wr_buf *wr_buf;
63587f88cdaSKarsten Graul 	struct smc_buf_desc *rmb;
63687f88cdaSKarsten Graul 	u8 n;
63787f88cdaSKarsten Graul 
63887f88cdaSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
63987f88cdaSKarsten Graul 	if (rc)
64087f88cdaSKarsten Graul 		return rc;
64187f88cdaSKarsten Graul 	addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf;
64287f88cdaSKarsten Graul 	memset(addc_llc, 0, sizeof(*addc_llc));
64387f88cdaSKarsten Graul 
64487f88cdaSKarsten Graul 	prim_lnk_idx = link->link_idx;
64587f88cdaSKarsten Graul 	lnk_idx = link_new->link_idx;
64687f88cdaSKarsten Graul 	addc_llc->link_num = link_new->link_id;
64787f88cdaSKarsten Graul 	addc_llc->num_rkeys = *num_rkeys_todo;
64887f88cdaSKarsten Graul 	n = *num_rkeys_todo;
64987f88cdaSKarsten Graul 	for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) {
65087f88cdaSKarsten Graul 		if (!*buf_pos) {
65187f88cdaSKarsten Graul 			addc_llc->num_rkeys = addc_llc->num_rkeys -
65287f88cdaSKarsten Graul 					      *num_rkeys_todo;
65387f88cdaSKarsten Graul 			*num_rkeys_todo = 0;
65487f88cdaSKarsten Graul 			break;
65587f88cdaSKarsten Graul 		}
65687f88cdaSKarsten Graul 		rmb = *buf_pos;
65787f88cdaSKarsten Graul 
65887f88cdaSKarsten Graul 		addc_llc->rt[i].rmb_key = htonl(rmb->mr_rx[prim_lnk_idx]->rkey);
65987f88cdaSKarsten Graul 		addc_llc->rt[i].rmb_key_new = htonl(rmb->mr_rx[lnk_idx]->rkey);
66087f88cdaSKarsten Graul 		addc_llc->rt[i].rmb_vaddr_new =
66187f88cdaSKarsten Graul 			cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl));
66287f88cdaSKarsten Graul 
66387f88cdaSKarsten Graul 		(*num_rkeys_todo)--;
66487f88cdaSKarsten Graul 		*buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
66587f88cdaSKarsten Graul 		while (*buf_pos && !(*buf_pos)->used)
66687f88cdaSKarsten Graul 			*buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
66787f88cdaSKarsten Graul 	}
66887f88cdaSKarsten Graul 	addc_llc->hd.common.type = SMC_LLC_ADD_LINK_CONT;
66987f88cdaSKarsten Graul 	addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont);
67087f88cdaSKarsten Graul 	if (lgr->role == SMC_CLNT)
67187f88cdaSKarsten Graul 		addc_llc->hd.flags |= SMC_LLC_FLAG_RESP;
67287f88cdaSKarsten Graul 	return smc_wr_tx_send(link, pend);
67387f88cdaSKarsten Graul }
67487f88cdaSKarsten Graul 
67587f88cdaSKarsten Graul static int smc_llc_cli_rkey_exchange(struct smc_link *link,
67687f88cdaSKarsten Graul 				     struct smc_link *link_new)
67787f88cdaSKarsten Graul {
67887f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
67987f88cdaSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
68087f88cdaSKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
68187f88cdaSKarsten Graul 	struct smc_llc_qentry *qentry;
68287f88cdaSKarsten Graul 	struct smc_buf_desc *buf_pos;
68387f88cdaSKarsten Graul 	int buf_lst;
68487f88cdaSKarsten Graul 	int rc = 0;
68587f88cdaSKarsten Graul 	int i;
68687f88cdaSKarsten Graul 
68787f88cdaSKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
68887f88cdaSKarsten Graul 	num_rkeys_send = lgr->conns_num;
68987f88cdaSKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
69087f88cdaSKarsten Graul 	do {
69187f88cdaSKarsten Graul 		qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME,
69287f88cdaSKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
69387f88cdaSKarsten Graul 		if (!qentry) {
69487f88cdaSKarsten Graul 			rc = -ETIMEDOUT;
69587f88cdaSKarsten Graul 			break;
69687f88cdaSKarsten Graul 		}
69787f88cdaSKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
69887f88cdaSKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
69987f88cdaSKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
70087f88cdaSKarsten Graul 		for (i = 0; i < max; i++) {
70187f88cdaSKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
70287f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_key,
70387f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
70487f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
70587f88cdaSKarsten Graul 			num_rkeys_recv--;
70687f88cdaSKarsten Graul 		}
70787f88cdaSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
70887f88cdaSKarsten Graul 		rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
70987f88cdaSKarsten Graul 					   &buf_lst, &buf_pos);
71087f88cdaSKarsten Graul 		if (rc)
71187f88cdaSKarsten Graul 			break;
71287f88cdaSKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
71387f88cdaSKarsten Graul 
71487f88cdaSKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
71587f88cdaSKarsten Graul 	return rc;
71687f88cdaSKarsten Graul }
71787f88cdaSKarsten Graul 
718336ba09fSKarsten Graul /* prepare and send an add link reject response */
719336ba09fSKarsten Graul static int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry)
720336ba09fSKarsten Graul {
721336ba09fSKarsten Graul 	qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
722336ba09fSKarsten Graul 	qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
723336ba09fSKarsten Graul 	qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
724336ba09fSKarsten Graul 	return smc_llc_send_message(qentry->link, &qentry->msg);
725336ba09fSKarsten Graul }
726336ba09fSKarsten Graul 
727b1570a87SKarsten Graul static int smc_llc_cli_conf_link(struct smc_link *link,
728b1570a87SKarsten Graul 				 struct smc_init_info *ini,
729b1570a87SKarsten Graul 				 struct smc_link *link_new,
730b1570a87SKarsten Graul 				 enum smc_lgr_type lgr_new_t)
731b1570a87SKarsten Graul {
732b1570a87SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
733b1570a87SKarsten Graul 	struct smc_llc_msg_del_link *del_llc;
734b1570a87SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
735b1570a87SKarsten Graul 	int rc = 0;
736b1570a87SKarsten Graul 
737b1570a87SKarsten Graul 	/* receive CONFIRM LINK request over RoCE fabric */
738b1570a87SKarsten Graul 	qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0);
739b1570a87SKarsten Graul 	if (!qentry) {
740b1570a87SKarsten Graul 		rc = smc_llc_send_delete_link(link, link_new->link_id,
741b1570a87SKarsten Graul 					      SMC_LLC_REQ, false,
742b1570a87SKarsten Graul 					      SMC_LLC_DEL_LOST_PATH);
743b1570a87SKarsten Graul 		return -ENOLINK;
744b1570a87SKarsten Graul 	}
745b1570a87SKarsten Graul 	if (qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) {
746b1570a87SKarsten Graul 		/* received DELETE_LINK instead */
747b1570a87SKarsten Graul 		del_llc = &qentry->msg.delete_link;
748b1570a87SKarsten Graul 		qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
749b1570a87SKarsten Graul 		smc_llc_send_message(link, &qentry->msg);
750b1570a87SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
751b1570a87SKarsten Graul 		return -ENOLINK;
752b1570a87SKarsten Graul 	}
753b1570a87SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
754b1570a87SKarsten Graul 
755b1570a87SKarsten Graul 	rc = smc_ib_modify_qp_rts(link_new);
756b1570a87SKarsten Graul 	if (rc) {
757b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
758b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
759b1570a87SKarsten Graul 		return -ENOLINK;
760b1570a87SKarsten Graul 	}
761b1570a87SKarsten Graul 	smc_wr_remember_qp_attr(link_new);
762b1570a87SKarsten Graul 
763b1570a87SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
764b1570a87SKarsten Graul 	if (rc) {
765b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
766b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
767b1570a87SKarsten Graul 		return -ENOLINK;
768b1570a87SKarsten Graul 	}
769b1570a87SKarsten Graul 
770b1570a87SKarsten Graul 	/* send CONFIRM LINK response over RoCE fabric */
771b1570a87SKarsten Graul 	rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP);
772b1570a87SKarsten Graul 	if (rc) {
773b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
774b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
775b1570a87SKarsten Graul 		return -ENOLINK;
776b1570a87SKarsten Graul 	}
777b1570a87SKarsten Graul 	smc_llc_link_active(link_new);
778b1570a87SKarsten Graul 	lgr->type = lgr_new_t;
779b1570a87SKarsten Graul 	return 0;
780b1570a87SKarsten Graul }
781b1570a87SKarsten Graul 
782336ba09fSKarsten Graul static void smc_llc_save_add_link_info(struct smc_link *link,
783336ba09fSKarsten Graul 				       struct smc_llc_msg_add_link *add_llc)
784336ba09fSKarsten Graul {
785336ba09fSKarsten Graul 	link->peer_qpn = ntoh24(add_llc->sender_qp_num);
786336ba09fSKarsten Graul 	memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE);
787336ba09fSKarsten Graul 	memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN);
788336ba09fSKarsten Graul 	link->peer_psn = ntoh24(add_llc->initial_psn);
789336ba09fSKarsten Graul 	link->peer_mtu = add_llc->qp_mtu;
790336ba09fSKarsten Graul }
791336ba09fSKarsten Graul 
792336ba09fSKarsten Graul /* as an SMC client, process an add link request */
793336ba09fSKarsten Graul int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
794336ba09fSKarsten Graul {
795336ba09fSKarsten Graul 	struct smc_llc_msg_add_link *llc = &qentry->msg.add_link;
796336ba09fSKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
797336ba09fSKarsten Graul 	struct smc_link_group *lgr = smc_get_lgr(link);
798336ba09fSKarsten Graul 	struct smc_link *lnk_new = NULL;
799336ba09fSKarsten Graul 	struct smc_init_info ini;
800336ba09fSKarsten Graul 	int lnk_idx, rc = 0;
801336ba09fSKarsten Graul 
802336ba09fSKarsten Graul 	ini.vlan_id = lgr->vlan_id;
803336ba09fSKarsten Graul 	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
804336ba09fSKarsten Graul 	if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
805336ba09fSKarsten Graul 	    !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN)) {
806336ba09fSKarsten Graul 		if (!ini.ib_dev)
807336ba09fSKarsten Graul 			goto out_reject;
808336ba09fSKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
809336ba09fSKarsten Graul 	}
810336ba09fSKarsten Graul 	if (!ini.ib_dev) {
811336ba09fSKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
812336ba09fSKarsten Graul 		ini.ib_dev = link->smcibdev;
813336ba09fSKarsten Graul 		ini.ib_port = link->ibport;
814336ba09fSKarsten Graul 	}
815336ba09fSKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
816336ba09fSKarsten Graul 	if (lnk_idx < 0)
817336ba09fSKarsten Graul 		goto out_reject;
818336ba09fSKarsten Graul 	lnk_new = &lgr->lnk[lnk_idx];
819336ba09fSKarsten Graul 	rc = smcr_link_init(lgr, lnk_new, lnk_idx, &ini);
820336ba09fSKarsten Graul 	if (rc)
821336ba09fSKarsten Graul 		goto out_reject;
822336ba09fSKarsten Graul 	smc_llc_save_add_link_info(lnk_new, llc);
823336ba09fSKarsten Graul 	lnk_new->link_id = llc->link_num;
824336ba09fSKarsten Graul 
825336ba09fSKarsten Graul 	rc = smc_ib_ready_link(lnk_new);
826336ba09fSKarsten Graul 	if (rc)
827336ba09fSKarsten Graul 		goto out_clear_lnk;
828336ba09fSKarsten Graul 
829336ba09fSKarsten Graul 	rc = smcr_buf_map_lgr(lnk_new);
830336ba09fSKarsten Graul 	if (rc)
831336ba09fSKarsten Graul 		goto out_clear_lnk;
832336ba09fSKarsten Graul 
833336ba09fSKarsten Graul 	rc = smc_llc_send_add_link(link,
834336ba09fSKarsten Graul 				   lnk_new->smcibdev->mac[ini.ib_port - 1],
835336ba09fSKarsten Graul 				   lnk_new->gid, lnk_new, SMC_LLC_RESP);
836336ba09fSKarsten Graul 	if (rc)
837336ba09fSKarsten Graul 		goto out_clear_lnk;
83887f88cdaSKarsten Graul 	rc = smc_llc_cli_rkey_exchange(link, lnk_new);
839336ba09fSKarsten Graul 	if (rc) {
840336ba09fSKarsten Graul 		rc = 0;
841336ba09fSKarsten Graul 		goto out_clear_lnk;
842336ba09fSKarsten Graul 	}
843b1570a87SKarsten Graul 	rc = smc_llc_cli_conf_link(link, &ini, lnk_new, lgr_new_t);
844336ba09fSKarsten Graul 	if (!rc)
845336ba09fSKarsten Graul 		goto out;
846336ba09fSKarsten Graul out_clear_lnk:
847336ba09fSKarsten Graul 	smcr_link_clear(lnk_new);
848336ba09fSKarsten Graul out_reject:
849336ba09fSKarsten Graul 	smc_llc_cli_add_link_reject(qentry);
850336ba09fSKarsten Graul out:
851336ba09fSKarsten Graul 	kfree(qentry);
852336ba09fSKarsten Graul 	return rc;
853336ba09fSKarsten Graul }
854336ba09fSKarsten Graul 
855b1570a87SKarsten Graul static void smc_llc_process_cli_add_link(struct smc_link_group *lgr)
856b1570a87SKarsten Graul {
857b1570a87SKarsten Graul 	struct smc_llc_qentry *qentry;
858b1570a87SKarsten Graul 
859b1570a87SKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
860b1570a87SKarsten Graul 
861b1570a87SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
862b1570a87SKarsten Graul 	smc_llc_cli_add_link(qentry->link, qentry);
863b1570a87SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
864b1570a87SKarsten Graul }
865b1570a87SKarsten Graul 
86657b49924SKarsten Graul static int smc_llc_srv_rkey_exchange(struct smc_link *link,
86757b49924SKarsten Graul 				     struct smc_link *link_new)
86857b49924SKarsten Graul {
86957b49924SKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
87057b49924SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
87157b49924SKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
87257b49924SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
87357b49924SKarsten Graul 	struct smc_buf_desc *buf_pos;
87457b49924SKarsten Graul 	int buf_lst;
87557b49924SKarsten Graul 	int rc = 0;
87657b49924SKarsten Graul 	int i;
87757b49924SKarsten Graul 
87857b49924SKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
87957b49924SKarsten Graul 	num_rkeys_send = lgr->conns_num;
88057b49924SKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
88157b49924SKarsten Graul 	do {
88257b49924SKarsten Graul 		smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
88357b49924SKarsten Graul 				      &buf_lst, &buf_pos);
88457b49924SKarsten Graul 		qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME,
88557b49924SKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
88657b49924SKarsten Graul 		if (!qentry) {
88757b49924SKarsten Graul 			rc = -ETIMEDOUT;
88857b49924SKarsten Graul 			goto out;
88957b49924SKarsten Graul 		}
89057b49924SKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
89157b49924SKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
89257b49924SKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
89357b49924SKarsten Graul 		for (i = 0; i < max; i++) {
89457b49924SKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
89557b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key,
89657b49924SKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
89757b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
89857b49924SKarsten Graul 			num_rkeys_recv--;
89957b49924SKarsten Graul 		}
90057b49924SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
90157b49924SKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
90257b49924SKarsten Graul out:
90357b49924SKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
90457b49924SKarsten Graul 	return rc;
90557b49924SKarsten Graul }
90657b49924SKarsten Graul 
907*1551c95bSKarsten Graul static int smc_llc_srv_conf_link(struct smc_link *link,
908*1551c95bSKarsten Graul 				 struct smc_link *link_new,
909*1551c95bSKarsten Graul 				 enum smc_lgr_type lgr_new_t)
910*1551c95bSKarsten Graul {
911*1551c95bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
912*1551c95bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
913*1551c95bSKarsten Graul 	int rc;
914*1551c95bSKarsten Graul 
915*1551c95bSKarsten Graul 	/* send CONFIRM LINK request over the RoCE fabric */
916*1551c95bSKarsten Graul 	rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ);
917*1551c95bSKarsten Graul 	if (rc)
918*1551c95bSKarsten Graul 		return -ENOLINK;
919*1551c95bSKarsten Graul 	/* receive CONFIRM LINK response over the RoCE fabric */
920*1551c95bSKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME,
921*1551c95bSKarsten Graul 			      SMC_LLC_CONFIRM_LINK);
922*1551c95bSKarsten Graul 	if (!qentry) {
923*1551c95bSKarsten Graul 		/* send DELETE LINK */
924*1551c95bSKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
925*1551c95bSKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
926*1551c95bSKarsten Graul 		return -ENOLINK;
927*1551c95bSKarsten Graul 	}
928*1551c95bSKarsten Graul 	smc_llc_link_active(link_new);
929*1551c95bSKarsten Graul 	lgr->type = lgr_new_t;
930*1551c95bSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
931*1551c95bSKarsten Graul 	return 0;
932*1551c95bSKarsten Graul }
933*1551c95bSKarsten Graul 
9342d2209f2SKarsten Graul int smc_llc_srv_add_link(struct smc_link *link)
9352d2209f2SKarsten Graul {
9362d2209f2SKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
9372d2209f2SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
9382d2209f2SKarsten Graul 	struct smc_llc_msg_add_link *add_llc;
9392d2209f2SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
9402d2209f2SKarsten Graul 	struct smc_link *link_new;
9412d2209f2SKarsten Graul 	struct smc_init_info ini;
9422d2209f2SKarsten Graul 	int lnk_idx, rc = 0;
9432d2209f2SKarsten Graul 
9442d2209f2SKarsten Graul 	/* ignore client add link recommendation, start new flow */
9452d2209f2SKarsten Graul 	ini.vlan_id = lgr->vlan_id;
9462d2209f2SKarsten Graul 	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
9472d2209f2SKarsten Graul 	if (!ini.ib_dev) {
9482d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
9492d2209f2SKarsten Graul 		ini.ib_dev = link->smcibdev;
9502d2209f2SKarsten Graul 		ini.ib_port = link->ibport;
9512d2209f2SKarsten Graul 	}
9522d2209f2SKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
9532d2209f2SKarsten Graul 	if (lnk_idx < 0)
9542d2209f2SKarsten Graul 		return 0;
9552d2209f2SKarsten Graul 
9562d2209f2SKarsten Graul 	rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, &ini);
9572d2209f2SKarsten Graul 	if (rc)
9582d2209f2SKarsten Graul 		return rc;
9592d2209f2SKarsten Graul 	link_new = &lgr->lnk[lnk_idx];
9602d2209f2SKarsten Graul 	rc = smc_llc_send_add_link(link,
9612d2209f2SKarsten Graul 				   link_new->smcibdev->mac[ini.ib_port - 1],
9622d2209f2SKarsten Graul 				   link_new->gid, link_new, SMC_LLC_REQ);
9632d2209f2SKarsten Graul 	if (rc)
9642d2209f2SKarsten Graul 		goto out_err;
9652d2209f2SKarsten Graul 	/* receive ADD LINK response over the RoCE fabric */
9662d2209f2SKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK);
9672d2209f2SKarsten Graul 	if (!qentry) {
9682d2209f2SKarsten Graul 		rc = -ETIMEDOUT;
9692d2209f2SKarsten Graul 		goto out_err;
9702d2209f2SKarsten Graul 	}
9712d2209f2SKarsten Graul 	add_llc = &qentry->msg.add_link;
9722d2209f2SKarsten Graul 	if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
9732d2209f2SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
9742d2209f2SKarsten Graul 		rc = -ENOLINK;
9752d2209f2SKarsten Graul 		goto out_err;
9762d2209f2SKarsten Graul 	}
9772d2209f2SKarsten Graul 	if (lgr->type == SMC_LGR_SINGLE &&
9782d2209f2SKarsten Graul 	    (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
9792d2209f2SKarsten Graul 	     !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN))) {
9802d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
9812d2209f2SKarsten Graul 	}
9822d2209f2SKarsten Graul 	smc_llc_save_add_link_info(link_new, add_llc);
9832d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
9842d2209f2SKarsten Graul 
9852d2209f2SKarsten Graul 	rc = smc_ib_ready_link(link_new);
9862d2209f2SKarsten Graul 	if (rc)
9872d2209f2SKarsten Graul 		goto out_err;
9882d2209f2SKarsten Graul 	rc = smcr_buf_map_lgr(link_new);
9892d2209f2SKarsten Graul 	if (rc)
9902d2209f2SKarsten Graul 		goto out_err;
9912d2209f2SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
9922d2209f2SKarsten Graul 	if (rc)
9932d2209f2SKarsten Graul 		goto out_err;
99457b49924SKarsten Graul 	rc = smc_llc_srv_rkey_exchange(link, link_new);
9952d2209f2SKarsten Graul 	if (rc)
9962d2209f2SKarsten Graul 		goto out_err;
997*1551c95bSKarsten Graul 	rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
9982d2209f2SKarsten Graul 	if (rc)
9992d2209f2SKarsten Graul 		goto out_err;
10002d2209f2SKarsten Graul 	return 0;
10012d2209f2SKarsten Graul out_err:
10022d2209f2SKarsten Graul 	smcr_link_clear(link_new);
10032d2209f2SKarsten Graul 	return rc;
10042d2209f2SKarsten Graul }
10052d2209f2SKarsten Graul 
10062d2209f2SKarsten Graul static void smc_llc_process_srv_add_link(struct smc_link_group *lgr)
10072d2209f2SKarsten Graul {
10082d2209f2SKarsten Graul 	struct smc_link *link = lgr->llc_flow_lcl.qentry->link;
10092d2209f2SKarsten Graul 	int rc;
10102d2209f2SKarsten Graul 
10112d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
10122d2209f2SKarsten Graul 
10132d2209f2SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
10142d2209f2SKarsten Graul 	rc = smc_llc_srv_add_link(link);
10152d2209f2SKarsten Graul 	if (!rc && lgr->type == SMC_LGR_SYMMETRIC) {
10162d2209f2SKarsten Graul 		/* delete any asymmetric link */
10172d2209f2SKarsten Graul 		/* tbd: smc_llc_delete_asym_link(lgr); */
10182d2209f2SKarsten Graul 	}
10192d2209f2SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
10202d2209f2SKarsten Graul }
10212d2209f2SKarsten Graul 
1022b45e7f98SKarsten Graul /* worker to process an add link message */
1023b45e7f98SKarsten Graul static void smc_llc_add_link_work(struct work_struct *work)
1024b45e7f98SKarsten Graul {
1025b45e7f98SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
1026b45e7f98SKarsten Graul 						  llc_add_link_work);
1027b45e7f98SKarsten Graul 
1028b45e7f98SKarsten Graul 	if (list_empty(&lgr->list)) {
1029b45e7f98SKarsten Graul 		/* link group is terminating */
1030b45e7f98SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
1031b45e7f98SKarsten Graul 		goto out;
1032b45e7f98SKarsten Graul 	}
1033b45e7f98SKarsten Graul 
1034b1570a87SKarsten Graul 	if (lgr->role == SMC_CLNT)
1035b1570a87SKarsten Graul 		smc_llc_process_cli_add_link(lgr);
10362d2209f2SKarsten Graul 	else
10372d2209f2SKarsten Graul 		smc_llc_process_srv_add_link(lgr);
1038b45e7f98SKarsten Graul out:
1039b45e7f98SKarsten Graul 	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
1040b45e7f98SKarsten Graul }
1041b45e7f98SKarsten Graul 
104252bedf37SKarsten Graul static void smc_llc_rx_delete_link(struct smc_link *link,
104352bedf37SKarsten Graul 				   struct smc_llc_msg_del_link *llc)
104452bedf37SKarsten Graul {
104500e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
104652bedf37SKarsten Graul 
10479651b934SKarsten Graul 	smc_lgr_forget(lgr);
10480d18a0cbSKarsten Graul 	if (lgr->role == SMC_SERV) {
10490d18a0cbSKarsten Graul 		/* client asks to delete this link, send request */
1050fbed3b37SKarsten Graul 		smc_llc_send_delete_link(link, 0, SMC_LLC_REQ, true,
1051fbed3b37SKarsten Graul 					 SMC_LLC_DEL_PROG_INIT_TERM);
105252bedf37SKarsten Graul 	} else {
10530d18a0cbSKarsten Graul 		/* server requests to delete this link, send response */
1054fbed3b37SKarsten Graul 		smc_llc_send_delete_link(link, 0, SMC_LLC_RESP, true,
1055fbed3b37SKarsten Graul 					 SMC_LLC_DEL_PROG_INIT_TERM);
105652bedf37SKarsten Graul 	}
105787523930SKarsten Graul 	smcr_link_down_cond(link);
105852bedf37SKarsten Graul }
105952bedf37SKarsten Graul 
10603bc67e09SKarsten Graul /* process a confirm_rkey request from peer, remote flow */
10613bc67e09SKarsten Graul static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr)
10624ed75de5SKarsten Graul {
10633bc67e09SKarsten Graul 	struct smc_llc_msg_confirm_rkey *llc;
10643bc67e09SKarsten Graul 	struct smc_llc_qentry *qentry;
10653bc67e09SKarsten Graul 	struct smc_link *link;
10663bc67e09SKarsten Graul 	int num_entries;
10673bc67e09SKarsten Graul 	int rk_idx;
10683bc67e09SKarsten Graul 	int i;
10694ed75de5SKarsten Graul 
10703bc67e09SKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
10713bc67e09SKarsten Graul 	llc = &qentry->msg.confirm_rkey;
10723bc67e09SKarsten Graul 	link = qentry->link;
10733bc67e09SKarsten Graul 
10743bc67e09SKarsten Graul 	num_entries = llc->rtoken[0].num_rkeys;
10753bc67e09SKarsten Graul 	/* first rkey entry is for receiving link */
10763bc67e09SKarsten Graul 	rk_idx = smc_rtoken_add(link,
10774ed75de5SKarsten Graul 				llc->rtoken[0].rmb_vaddr,
10784ed75de5SKarsten Graul 				llc->rtoken[0].rmb_key);
10793bc67e09SKarsten Graul 	if (rk_idx < 0)
10803bc67e09SKarsten Graul 		goto out_err;
10814ed75de5SKarsten Graul 
10823bc67e09SKarsten Graul 	for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++)
10833bc67e09SKarsten Graul 		smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id,
10843bc67e09SKarsten Graul 				llc->rtoken[i].rmb_vaddr,
10853bc67e09SKarsten Graul 				llc->rtoken[i].rmb_key);
10863bc67e09SKarsten Graul 	/* max links is 3 so there is no need to support conf_rkey_cont msgs */
10873bc67e09SKarsten Graul 	goto out;
10883bc67e09SKarsten Graul out_err:
10894ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
10903bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY;
10913bc67e09SKarsten Graul out:
10923bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
10933bc67e09SKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
10943bc67e09SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
10954ed75de5SKarsten Graul }
10964ed75de5SKarsten Graul 
1097218b24feSKarsten Graul /* process a delete_rkey request from peer, remote flow */
1098218b24feSKarsten Graul static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
10994ed75de5SKarsten Graul {
1100218b24feSKarsten Graul 	struct smc_llc_msg_delete_rkey *llc;
1101218b24feSKarsten Graul 	struct smc_llc_qentry *qentry;
1102218b24feSKarsten Graul 	struct smc_link *link;
11034ed75de5SKarsten Graul 	u8 err_mask = 0;
11044ed75de5SKarsten Graul 	int i, max;
11054ed75de5SKarsten Graul 
1106218b24feSKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
1107218b24feSKarsten Graul 	llc = &qentry->msg.delete_rkey;
1108218b24feSKarsten Graul 	link = qentry->link;
1109218b24feSKarsten Graul 
11104ed75de5SKarsten Graul 	max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
11114ed75de5SKarsten Graul 	for (i = 0; i < max; i++) {
1112387707fdSKarsten Graul 		if (smc_rtoken_delete(link, llc->rkey[i]))
11134ed75de5SKarsten Graul 			err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
11144ed75de5SKarsten Graul 	}
11154ed75de5SKarsten Graul 	if (err_mask) {
11164ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
11174ed75de5SKarsten Graul 		llc->err_mask = err_mask;
11184ed75de5SKarsten Graul 	}
1119218b24feSKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
1120218b24feSKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
1121218b24feSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
1122218b24feSKarsten Graul }
11234ed75de5SKarsten Graul 
11246c8968c4SKarsten Graul /* flush the llc event queue */
112500a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr)
11269bf9abeaSUrsula Braun {
11276c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry, *q;
11289bf9abeaSUrsula Braun 
11296c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
11306c8968c4SKarsten Graul 	list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
11316c8968c4SKarsten Graul 		list_del_init(&qentry->list);
11326c8968c4SKarsten Graul 		kfree(qentry);
11336c8968c4SKarsten Graul 	}
11346c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
11356c8968c4SKarsten Graul }
11366c8968c4SKarsten Graul 
11376c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
11386c8968c4SKarsten Graul {
11396c8968c4SKarsten Graul 	union smc_llc_msg *llc = &qentry->msg;
11406c8968c4SKarsten Graul 	struct smc_link *link = qentry->link;
11410fb0b02bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
11426c8968c4SKarsten Graul 
1143d854fcbfSKarsten Graul 	if (!smc_link_usable(link))
11446c8968c4SKarsten Graul 		goto out;
1145313164daSKarsten Graul 
1146313164daSKarsten Graul 	switch (llc->raw.hdr.common.type) {
1147313164daSKarsten Graul 	case SMC_LLC_TEST_LINK:
114856e8091cSKarsten Graul 		llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP;
114956e8091cSKarsten Graul 		smc_llc_send_message(link, llc);
1150313164daSKarsten Graul 		break;
115152bedf37SKarsten Graul 	case SMC_LLC_ADD_LINK:
11520fb0b02bSKarsten Graul 		if (list_empty(&lgr->list))
11530fb0b02bSKarsten Graul 			goto out;	/* lgr is terminating */
11540fb0b02bSKarsten Graul 		if (lgr->role == SMC_CLNT) {
11550fb0b02bSKarsten Graul 			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK) {
11560fb0b02bSKarsten Graul 				/* a flow is waiting for this message */
11570fb0b02bSKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
11580fb0b02bSKarsten Graul 							qentry);
11590fb0b02bSKarsten Graul 				wake_up_interruptible(&lgr->llc_waiter);
11600fb0b02bSKarsten Graul 			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
11610fb0b02bSKarsten Graul 						      qentry)) {
1162b45e7f98SKarsten Graul 				schedule_work(&lgr->llc_add_link_work);
11630fb0b02bSKarsten Graul 			}
11640fb0b02bSKarsten Graul 		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
11650fb0b02bSKarsten Graul 			/* as smc server, handle client suggestion */
1166b45e7f98SKarsten Graul 			schedule_work(&lgr->llc_add_link_work);
11670fb0b02bSKarsten Graul 		}
11680fb0b02bSKarsten Graul 		return;
11690fb0b02bSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
117087f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
11710fb0b02bSKarsten Graul 		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
11720fb0b02bSKarsten Graul 			/* a flow is waiting for this message */
11730fb0b02bSKarsten Graul 			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
11740fb0b02bSKarsten Graul 			wake_up_interruptible(&lgr->llc_waiter);
11750fb0b02bSKarsten Graul 			return;
11760fb0b02bSKarsten Graul 		}
117752bedf37SKarsten Graul 		break;
117852bedf37SKarsten Graul 	case SMC_LLC_DELETE_LINK:
117952bedf37SKarsten Graul 		smc_llc_rx_delete_link(link, &llc->delete_link);
118052bedf37SKarsten Graul 		break;
11814ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
11823bc67e09SKarsten Graul 		/* new request from remote, assign to remote flow */
11833bc67e09SKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
11843bc67e09SKarsten Graul 			/* process here, does not wait for more llc msgs */
11853bc67e09SKarsten Graul 			smc_llc_rmt_conf_rkey(lgr);
11863bc67e09SKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
11873bc67e09SKarsten Graul 		}
11883bc67e09SKarsten Graul 		return;
11894ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
119042d18accSKarsten Graul 		/* not used because max links is 3, and 3 rkeys fit into
119142d18accSKarsten Graul 		 * one CONFIRM_RKEY message
119242d18accSKarsten Graul 		 */
11934ed75de5SKarsten Graul 		break;
11944ed75de5SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
1195218b24feSKarsten Graul 		/* new request from remote, assign to remote flow */
1196218b24feSKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
1197218b24feSKarsten Graul 			/* process here, does not wait for more llc msgs */
1198218b24feSKarsten Graul 			smc_llc_rmt_delete_rkey(lgr);
1199218b24feSKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
1200218b24feSKarsten Graul 		}
1201218b24feSKarsten Graul 		return;
1202313164daSKarsten Graul 	}
12036c8968c4SKarsten Graul out:
12046c8968c4SKarsten Graul 	kfree(qentry);
12056c8968c4SKarsten Graul }
12066c8968c4SKarsten Graul 
12076c8968c4SKarsten Graul /* worker to process llc messages on the event queue */
12086c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work)
12096c8968c4SKarsten Graul {
12106c8968c4SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
12116c8968c4SKarsten Graul 						  llc_event_work);
12126c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
12136c8968c4SKarsten Graul 
1214555da9afSKarsten Graul 	if (!lgr->llc_flow_lcl.type && lgr->delayed_event) {
1215555da9afSKarsten Graul 		if (smc_link_usable(lgr->delayed_event->link)) {
1216555da9afSKarsten Graul 			smc_llc_event_handler(lgr->delayed_event);
1217555da9afSKarsten Graul 		} else {
1218555da9afSKarsten Graul 			qentry = lgr->delayed_event;
1219555da9afSKarsten Graul 			lgr->delayed_event = NULL;
1220555da9afSKarsten Graul 			kfree(qentry);
1221555da9afSKarsten Graul 		}
1222555da9afSKarsten Graul 	}
1223555da9afSKarsten Graul 
12246c8968c4SKarsten Graul again:
12256c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
12266c8968c4SKarsten Graul 	if (!list_empty(&lgr->llc_event_q)) {
12276c8968c4SKarsten Graul 		qentry = list_first_entry(&lgr->llc_event_q,
12286c8968c4SKarsten Graul 					  struct smc_llc_qentry, list);
12296c8968c4SKarsten Graul 		list_del_init(&qentry->list);
12306c8968c4SKarsten Graul 		spin_unlock_bh(&lgr->llc_event_q_lock);
12316c8968c4SKarsten Graul 		smc_llc_event_handler(qentry);
12326c8968c4SKarsten Graul 		goto again;
12336c8968c4SKarsten Graul 	}
12346c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
12356c8968c4SKarsten Graul }
12366c8968c4SKarsten Graul 
1237ef79d439SKarsten Graul /* process llc responses in tasklet context */
1238a6688d91SKarsten Graul static void smc_llc_rx_response(struct smc_link *link,
1239a6688d91SKarsten Graul 				struct smc_llc_qentry *qentry)
1240ef79d439SKarsten Graul {
1241a6688d91SKarsten Graul 	u8 llc_type = qentry->msg.raw.hdr.common.type;
1242ef79d439SKarsten Graul 
1243a6688d91SKarsten Graul 	switch (llc_type) {
1244ef79d439SKarsten Graul 	case SMC_LLC_TEST_LINK:
1245ef79d439SKarsten Graul 		if (link->state == SMC_LNK_ACTIVE)
1246ef79d439SKarsten Graul 			complete(&link->llc_testlink_resp);
1247ef79d439SKarsten Graul 		break;
1248ef79d439SKarsten Graul 	case SMC_LLC_ADD_LINK:
12494667bb4aSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
125087f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
12513d88a21bSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
12526d74c3a8SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
12534667bb4aSKarsten Graul 		/* assign responses to the local flow, we requested them */
12544667bb4aSKarsten Graul 		smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry);
12554667bb4aSKarsten Graul 		wake_up_interruptible(&link->lgr->llc_waiter);
12564667bb4aSKarsten Graul 		return;
1257ef79d439SKarsten Graul 	case SMC_LLC_DELETE_LINK:
1258ef79d439SKarsten Graul 		if (link->lgr->role == SMC_SERV)
1259ef79d439SKarsten Graul 			smc_lgr_schedule_free_work_fast(link->lgr);
1260ef79d439SKarsten Graul 		break;
1261ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
126242d18accSKarsten Graul 		/* not used because max links is 3 */
1263ef79d439SKarsten Graul 		break;
1264ef79d439SKarsten Graul 	}
1265a6688d91SKarsten Graul 	kfree(qentry);
1266ef79d439SKarsten Graul }
1267ef79d439SKarsten Graul 
1268a6688d91SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
12696c8968c4SKarsten Graul {
12706c8968c4SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
12716c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
12726c8968c4SKarsten Graul 	unsigned long flags;
12736c8968c4SKarsten Graul 
12746c8968c4SKarsten Graul 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
12756c8968c4SKarsten Graul 	if (!qentry)
12766c8968c4SKarsten Graul 		return;
12776c8968c4SKarsten Graul 	qentry->link = link;
12786c8968c4SKarsten Graul 	INIT_LIST_HEAD(&qentry->list);
12796c8968c4SKarsten Graul 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
1280a6688d91SKarsten Graul 
1281a6688d91SKarsten Graul 	/* process responses immediately */
1282a6688d91SKarsten Graul 	if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) {
1283a6688d91SKarsten Graul 		smc_llc_rx_response(link, qentry);
1284a6688d91SKarsten Graul 		return;
1285a6688d91SKarsten Graul 	}
1286a6688d91SKarsten Graul 
1287a6688d91SKarsten Graul 	/* add requests to event queue */
12886c8968c4SKarsten Graul 	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
12896c8968c4SKarsten Graul 	list_add_tail(&qentry->list, &lgr->llc_event_q);
12906c8968c4SKarsten Graul 	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
12916c8968c4SKarsten Graul 	schedule_work(&link->lgr->llc_event_work);
12929bf9abeaSUrsula Braun }
12939bf9abeaSUrsula Braun 
1294a6688d91SKarsten Graul /* copy received msg and add it to the event queue */
1295a6688d91SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
1296a6688d91SKarsten Graul {
1297a6688d91SKarsten Graul 	struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
1298a6688d91SKarsten Graul 	union smc_llc_msg *llc = buf;
1299a6688d91SKarsten Graul 
1300a6688d91SKarsten Graul 	if (wc->byte_len < sizeof(*llc))
1301a6688d91SKarsten Graul 		return; /* short message */
1302a6688d91SKarsten Graul 	if (llc->raw.hdr.length != sizeof(*llc))
1303a6688d91SKarsten Graul 		return; /* invalid message */
1304a6688d91SKarsten Graul 
1305a6688d91SKarsten Graul 	smc_llc_enqueue(link, llc);
1306a6688d91SKarsten Graul }
1307a6688d91SKarsten Graul 
130844aa81ceSKarsten Graul /***************************** worker, utils *********************************/
1309877ae5beSKarsten Graul 
1310877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work)
1311877ae5beSKarsten Graul {
1312877ae5beSKarsten Graul 	struct smc_link *link = container_of(to_delayed_work(work),
1313877ae5beSKarsten Graul 					     struct smc_link, llc_testlink_wrk);
1314877ae5beSKarsten Graul 	unsigned long next_interval;
1315877ae5beSKarsten Graul 	unsigned long expire_time;
1316877ae5beSKarsten Graul 	u8 user_data[16] = { 0 };
1317877ae5beSKarsten Graul 	int rc;
1318877ae5beSKarsten Graul 
1319877ae5beSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
1320877ae5beSKarsten Graul 		return;		/* don't reschedule worker */
1321877ae5beSKarsten Graul 	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
1322877ae5beSKarsten Graul 	if (time_is_after_jiffies(expire_time)) {
1323877ae5beSKarsten Graul 		next_interval = expire_time - jiffies;
1324877ae5beSKarsten Graul 		goto out;
1325877ae5beSKarsten Graul 	}
1326877ae5beSKarsten Graul 	reinit_completion(&link->llc_testlink_resp);
1327d97935faSKarsten Graul 	smc_llc_send_test_link(link, user_data);
1328877ae5beSKarsten Graul 	/* receive TEST LINK response over RoCE fabric */
1329877ae5beSKarsten Graul 	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
1330877ae5beSKarsten Graul 						       SMC_LLC_WAIT_TIME);
13311020e1efSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
13321020e1efSKarsten Graul 		return;		/* link state changed */
1333877ae5beSKarsten Graul 	if (rc <= 0) {
133487523930SKarsten Graul 		smcr_link_down_cond_sched(link);
1335877ae5beSKarsten Graul 		return;
1336877ae5beSKarsten Graul 	}
1337877ae5beSKarsten Graul 	next_interval = link->llc_testlink_time;
1338877ae5beSKarsten Graul out:
13391020e1efSKarsten Graul 	schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
1340877ae5beSKarsten Graul }
1341877ae5beSKarsten Graul 
134200a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
134300a049cfSKarsten Graul {
134400a049cfSKarsten Graul 	struct net *net = sock_net(smc->clcsock->sk);
134500a049cfSKarsten Graul 
134600a049cfSKarsten Graul 	INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
1347b45e7f98SKarsten Graul 	INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work);
134800a049cfSKarsten Graul 	INIT_LIST_HEAD(&lgr->llc_event_q);
134900a049cfSKarsten Graul 	spin_lock_init(&lgr->llc_event_q_lock);
1350555da9afSKarsten Graul 	spin_lock_init(&lgr->llc_flow_lock);
1351555da9afSKarsten Graul 	init_waitqueue_head(&lgr->llc_waiter);
1352d5500667SKarsten Graul 	mutex_init(&lgr->llc_conf_mutex);
135300a049cfSKarsten Graul 	lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;
135400a049cfSKarsten Graul }
135500a049cfSKarsten Graul 
135600a049cfSKarsten Graul /* called after lgr was removed from lgr_list */
135700a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr)
135800a049cfSKarsten Graul {
135900a049cfSKarsten Graul 	smc_llc_event_flush(lgr);
1360555da9afSKarsten Graul 	wake_up_interruptible_all(&lgr->llc_waiter);
136100a049cfSKarsten Graul 	cancel_work_sync(&lgr->llc_event_work);
1362b45e7f98SKarsten Graul 	cancel_work_sync(&lgr->llc_add_link_work);
1363555da9afSKarsten Graul 	if (lgr->delayed_event) {
1364555da9afSKarsten Graul 		kfree(lgr->delayed_event);
1365555da9afSKarsten Graul 		lgr->delayed_event = NULL;
1366555da9afSKarsten Graul 	}
136700a049cfSKarsten Graul }
136800a049cfSKarsten Graul 
13692a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link)
1370877ae5beSKarsten Graul {
1371877ae5beSKarsten Graul 	init_completion(&link->llc_testlink_resp);
1372877ae5beSKarsten Graul 	INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
13732a4c57a9SKarsten Graul 	return 0;
1374b32cf4abSKarsten Graul }
1375b32cf4abSKarsten Graul 
137600a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link)
1377b32cf4abSKarsten Graul {
1378877ae5beSKarsten Graul 	link->state = SMC_LNK_ACTIVE;
137900a049cfSKarsten Graul 	if (link->lgr->llc_testlink_time) {
138000a049cfSKarsten Graul 		link->llc_testlink_time = link->lgr->llc_testlink_time * HZ;
13811020e1efSKarsten Graul 		schedule_delayed_work(&link->llc_testlink_wrk,
1382877ae5beSKarsten Graul 				      link->llc_testlink_time);
1383877ae5beSKarsten Graul 	}
1384877ae5beSKarsten Graul }
1385877ae5beSKarsten Graul 
1386877ae5beSKarsten Graul /* called in worker context */
13872a4c57a9SKarsten Graul void smc_llc_link_clear(struct smc_link *link)
1388877ae5beSKarsten Graul {
13892140ac26SKarsten Graul 	complete(&link->llc_testlink_resp);
13902140ac26SKarsten Graul 	cancel_delayed_work_sync(&link->llc_testlink_wrk);
13912140ac26SKarsten Graul 	smc_wr_wakeup_reg_wait(link);
13922140ac26SKarsten Graul 	smc_wr_wakeup_tx_wait(link);
1393877ae5beSKarsten Graul }
1394877ae5beSKarsten Graul 
13953d88a21bSKarsten Graul /* register a new rtoken at the remote peer (for all links) */
13963d88a21bSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *send_link,
139744aa81ceSKarsten Graul 			    struct smc_buf_desc *rmb_desc)
139844aa81ceSKarsten Graul {
13993d88a21bSKarsten Graul 	struct smc_link_group *lgr = send_link->lgr;
14003d88a21bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
14013d88a21bSKarsten Graul 	int rc = 0;
140244aa81ceSKarsten Graul 
14033d88a21bSKarsten Graul 	rc = smc_llc_send_confirm_rkey(send_link, rmb_desc);
14043d88a21bSKarsten Graul 	if (rc)
14053d88a21bSKarsten Graul 		goto out;
140644aa81ceSKarsten Graul 	/* receive CONFIRM RKEY response from server over RoCE fabric */
14073d88a21bSKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
14083d88a21bSKarsten Graul 			      SMC_LLC_CONFIRM_RKEY);
14093d88a21bSKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
14103d88a21bSKarsten Graul 		rc = -EFAULT;
14113d88a21bSKarsten Graul out:
14123d88a21bSKarsten Graul 	if (qentry)
14133d88a21bSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
14143d88a21bSKarsten Graul 	return rc;
141544aa81ceSKarsten Graul }
141644aa81ceSKarsten Graul 
141760e03c62SKarsten Graul /* unregister an rtoken at the remote peer */
14186d74c3a8SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link_group *lgr,
141960e03c62SKarsten Graul 			   struct smc_buf_desc *rmb_desc)
142060e03c62SKarsten Graul {
14216d74c3a8SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
14226d74c3a8SKarsten Graul 	struct smc_link *send_link;
14230b29ec64SUrsula Braun 	int rc = 0;
142460e03c62SKarsten Graul 
14256d74c3a8SKarsten Graul 	send_link = smc_llc_usable_link(lgr);
14266d74c3a8SKarsten Graul 	if (!send_link)
14276d74c3a8SKarsten Graul 		return -ENOLINK;
14286d74c3a8SKarsten Graul 
14296d74c3a8SKarsten Graul 	/* protected by llc_flow control */
14306d74c3a8SKarsten Graul 	rc = smc_llc_send_delete_rkey(send_link, rmb_desc);
143160e03c62SKarsten Graul 	if (rc)
143260e03c62SKarsten Graul 		goto out;
143360e03c62SKarsten Graul 	/* receive DELETE RKEY response from server over RoCE fabric */
14346d74c3a8SKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
14356d74c3a8SKarsten Graul 			      SMC_LLC_DELETE_RKEY);
14366d74c3a8SKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
143760e03c62SKarsten Graul 		rc = -EFAULT;
143860e03c62SKarsten Graul out:
14396d74c3a8SKarsten Graul 	if (qentry)
14406d74c3a8SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
144160e03c62SKarsten Graul 	return rc;
144260e03c62SKarsten Graul }
144360e03c62SKarsten Graul 
144492334cfcSKarsten Graul /* evaluate confirm link request or response */
144592334cfcSKarsten Graul int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry,
144692334cfcSKarsten Graul 			   enum smc_llc_reqresp type)
144792334cfcSKarsten Graul {
144892334cfcSKarsten Graul 	if (type == SMC_LLC_REQ)	/* SMC server assigns link_id */
144992334cfcSKarsten Graul 		qentry->link->link_id = qentry->msg.confirm_link.link_num;
145092334cfcSKarsten Graul 	if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
145192334cfcSKarsten Graul 		return -ENOTSUPP;
145292334cfcSKarsten Graul 	return 0;
145392334cfcSKarsten Graul }
145492334cfcSKarsten Graul 
14559bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/
14569bf9abeaSUrsula Braun 
14579bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
14589bf9abeaSUrsula Braun 	{
14599bf9abeaSUrsula Braun 		.handler	= smc_llc_rx_handler,
14609bf9abeaSUrsula Braun 		.type		= SMC_LLC_CONFIRM_LINK
14619bf9abeaSUrsula Braun 	},
14629bf9abeaSUrsula Braun 	{
1463313164daSKarsten Graul 		.handler	= smc_llc_rx_handler,
1464313164daSKarsten Graul 		.type		= SMC_LLC_TEST_LINK
1465313164daSKarsten Graul 	},
1466313164daSKarsten Graul 	{
14674ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
146852bedf37SKarsten Graul 		.type		= SMC_LLC_ADD_LINK
146952bedf37SKarsten Graul 	},
147052bedf37SKarsten Graul 	{
147152bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
147287f88cdaSKarsten Graul 		.type		= SMC_LLC_ADD_LINK_CONT
147387f88cdaSKarsten Graul 	},
147487f88cdaSKarsten Graul 	{
147587f88cdaSKarsten Graul 		.handler	= smc_llc_rx_handler,
147652bedf37SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK
147752bedf37SKarsten Graul 	},
147852bedf37SKarsten Graul 	{
147952bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
14804ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY
14814ed75de5SKarsten Graul 	},
14824ed75de5SKarsten Graul 	{
14834ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
14844ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_CONT
14854ed75de5SKarsten Graul 	},
14864ed75de5SKarsten Graul 	{
14874ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
14884ed75de5SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY
14894ed75de5SKarsten Graul 	},
14904ed75de5SKarsten Graul 	{
14919bf9abeaSUrsula Braun 		.handler	= NULL,
14929bf9abeaSUrsula Braun 	}
14939bf9abeaSUrsula Braun };
14949bf9abeaSUrsula Braun 
14959bf9abeaSUrsula Braun int __init smc_llc_init(void)
14969bf9abeaSUrsula Braun {
14979bf9abeaSUrsula Braun 	struct smc_wr_rx_handler *handler;
14989bf9abeaSUrsula Braun 	int rc = 0;
14999bf9abeaSUrsula Braun 
15009bf9abeaSUrsula Braun 	for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
15019bf9abeaSUrsula Braun 		INIT_HLIST_NODE(&handler->list);
15029bf9abeaSUrsula Braun 		rc = smc_wr_rx_register_handler(handler);
15039bf9abeaSUrsula Braun 		if (rc)
15049bf9abeaSUrsula Braun 			break;
15059bf9abeaSUrsula Braun 	}
15069bf9abeaSUrsula Braun 	return rc;
15079bf9abeaSUrsula Braun }
1508