xref: /linux/net/smc/smc_llc.c (revision 57b499242cb888a32815f8663b60338bcb0b5747)
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 
866*57b49924SKarsten Graul static int smc_llc_srv_rkey_exchange(struct smc_link *link,
867*57b49924SKarsten Graul 				     struct smc_link *link_new)
868*57b49924SKarsten Graul {
869*57b49924SKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
870*57b49924SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
871*57b49924SKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
872*57b49924SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
873*57b49924SKarsten Graul 	struct smc_buf_desc *buf_pos;
874*57b49924SKarsten Graul 	int buf_lst;
875*57b49924SKarsten Graul 	int rc = 0;
876*57b49924SKarsten Graul 	int i;
877*57b49924SKarsten Graul 
878*57b49924SKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
879*57b49924SKarsten Graul 	num_rkeys_send = lgr->conns_num;
880*57b49924SKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
881*57b49924SKarsten Graul 	do {
882*57b49924SKarsten Graul 		smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
883*57b49924SKarsten Graul 				      &buf_lst, &buf_pos);
884*57b49924SKarsten Graul 		qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME,
885*57b49924SKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
886*57b49924SKarsten Graul 		if (!qentry) {
887*57b49924SKarsten Graul 			rc = -ETIMEDOUT;
888*57b49924SKarsten Graul 			goto out;
889*57b49924SKarsten Graul 		}
890*57b49924SKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
891*57b49924SKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
892*57b49924SKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
893*57b49924SKarsten Graul 		for (i = 0; i < max; i++) {
894*57b49924SKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
895*57b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key,
896*57b49924SKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
897*57b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
898*57b49924SKarsten Graul 			num_rkeys_recv--;
899*57b49924SKarsten Graul 		}
900*57b49924SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
901*57b49924SKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
902*57b49924SKarsten Graul out:
903*57b49924SKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
904*57b49924SKarsten Graul 	return rc;
905*57b49924SKarsten Graul }
906*57b49924SKarsten Graul 
9072d2209f2SKarsten Graul int smc_llc_srv_add_link(struct smc_link *link)
9082d2209f2SKarsten Graul {
9092d2209f2SKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
9102d2209f2SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
9112d2209f2SKarsten Graul 	struct smc_llc_msg_add_link *add_llc;
9122d2209f2SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
9132d2209f2SKarsten Graul 	struct smc_link *link_new;
9142d2209f2SKarsten Graul 	struct smc_init_info ini;
9152d2209f2SKarsten Graul 	int lnk_idx, rc = 0;
9162d2209f2SKarsten Graul 
9172d2209f2SKarsten Graul 	/* ignore client add link recommendation, start new flow */
9182d2209f2SKarsten Graul 	ini.vlan_id = lgr->vlan_id;
9192d2209f2SKarsten Graul 	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
9202d2209f2SKarsten Graul 	if (!ini.ib_dev) {
9212d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
9222d2209f2SKarsten Graul 		ini.ib_dev = link->smcibdev;
9232d2209f2SKarsten Graul 		ini.ib_port = link->ibport;
9242d2209f2SKarsten Graul 	}
9252d2209f2SKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
9262d2209f2SKarsten Graul 	if (lnk_idx < 0)
9272d2209f2SKarsten Graul 		return 0;
9282d2209f2SKarsten Graul 
9292d2209f2SKarsten Graul 	rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, &ini);
9302d2209f2SKarsten Graul 	if (rc)
9312d2209f2SKarsten Graul 		return rc;
9322d2209f2SKarsten Graul 	link_new = &lgr->lnk[lnk_idx];
9332d2209f2SKarsten Graul 	rc = smc_llc_send_add_link(link,
9342d2209f2SKarsten Graul 				   link_new->smcibdev->mac[ini.ib_port - 1],
9352d2209f2SKarsten Graul 				   link_new->gid, link_new, SMC_LLC_REQ);
9362d2209f2SKarsten Graul 	if (rc)
9372d2209f2SKarsten Graul 		goto out_err;
9382d2209f2SKarsten Graul 	/* receive ADD LINK response over the RoCE fabric */
9392d2209f2SKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK);
9402d2209f2SKarsten Graul 	if (!qentry) {
9412d2209f2SKarsten Graul 		rc = -ETIMEDOUT;
9422d2209f2SKarsten Graul 		goto out_err;
9432d2209f2SKarsten Graul 	}
9442d2209f2SKarsten Graul 	add_llc = &qentry->msg.add_link;
9452d2209f2SKarsten Graul 	if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
9462d2209f2SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
9472d2209f2SKarsten Graul 		rc = -ENOLINK;
9482d2209f2SKarsten Graul 		goto out_err;
9492d2209f2SKarsten Graul 	}
9502d2209f2SKarsten Graul 	if (lgr->type == SMC_LGR_SINGLE &&
9512d2209f2SKarsten Graul 	    (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
9522d2209f2SKarsten Graul 	     !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN))) {
9532d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
9542d2209f2SKarsten Graul 	}
9552d2209f2SKarsten Graul 	smc_llc_save_add_link_info(link_new, add_llc);
9562d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
9572d2209f2SKarsten Graul 
9582d2209f2SKarsten Graul 	rc = smc_ib_ready_link(link_new);
9592d2209f2SKarsten Graul 	if (rc)
9602d2209f2SKarsten Graul 		goto out_err;
9612d2209f2SKarsten Graul 	rc = smcr_buf_map_lgr(link_new);
9622d2209f2SKarsten Graul 	if (rc)
9632d2209f2SKarsten Graul 		goto out_err;
9642d2209f2SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
9652d2209f2SKarsten Graul 	if (rc)
9662d2209f2SKarsten Graul 		goto out_err;
967*57b49924SKarsten Graul 	rc = smc_llc_srv_rkey_exchange(link, link_new);
9682d2209f2SKarsten Graul 	if (rc)
9692d2209f2SKarsten Graul 		goto out_err;
9702d2209f2SKarsten Graul 	/* tbd: rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t); */
9712d2209f2SKarsten Graul 	if (rc)
9722d2209f2SKarsten Graul 		goto out_err;
9732d2209f2SKarsten Graul 	return 0;
9742d2209f2SKarsten Graul out_err:
9752d2209f2SKarsten Graul 	smcr_link_clear(link_new);
9762d2209f2SKarsten Graul 	return rc;
9772d2209f2SKarsten Graul }
9782d2209f2SKarsten Graul 
9792d2209f2SKarsten Graul static void smc_llc_process_srv_add_link(struct smc_link_group *lgr)
9802d2209f2SKarsten Graul {
9812d2209f2SKarsten Graul 	struct smc_link *link = lgr->llc_flow_lcl.qentry->link;
9822d2209f2SKarsten Graul 	int rc;
9832d2209f2SKarsten Graul 
9842d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
9852d2209f2SKarsten Graul 
9862d2209f2SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
9872d2209f2SKarsten Graul 	rc = smc_llc_srv_add_link(link);
9882d2209f2SKarsten Graul 	if (!rc && lgr->type == SMC_LGR_SYMMETRIC) {
9892d2209f2SKarsten Graul 		/* delete any asymmetric link */
9902d2209f2SKarsten Graul 		/* tbd: smc_llc_delete_asym_link(lgr); */
9912d2209f2SKarsten Graul 	}
9922d2209f2SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
9932d2209f2SKarsten Graul }
9942d2209f2SKarsten Graul 
995b45e7f98SKarsten Graul /* worker to process an add link message */
996b45e7f98SKarsten Graul static void smc_llc_add_link_work(struct work_struct *work)
997b45e7f98SKarsten Graul {
998b45e7f98SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
999b45e7f98SKarsten Graul 						  llc_add_link_work);
1000b45e7f98SKarsten Graul 
1001b45e7f98SKarsten Graul 	if (list_empty(&lgr->list)) {
1002b45e7f98SKarsten Graul 		/* link group is terminating */
1003b45e7f98SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
1004b45e7f98SKarsten Graul 		goto out;
1005b45e7f98SKarsten Graul 	}
1006b45e7f98SKarsten Graul 
1007b1570a87SKarsten Graul 	if (lgr->role == SMC_CLNT)
1008b1570a87SKarsten Graul 		smc_llc_process_cli_add_link(lgr);
10092d2209f2SKarsten Graul 	else
10102d2209f2SKarsten Graul 		smc_llc_process_srv_add_link(lgr);
1011b45e7f98SKarsten Graul out:
1012b45e7f98SKarsten Graul 	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
1013b45e7f98SKarsten Graul }
1014b45e7f98SKarsten Graul 
101552bedf37SKarsten Graul static void smc_llc_rx_delete_link(struct smc_link *link,
101652bedf37SKarsten Graul 				   struct smc_llc_msg_del_link *llc)
101752bedf37SKarsten Graul {
101800e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
101952bedf37SKarsten Graul 
10209651b934SKarsten Graul 	smc_lgr_forget(lgr);
10210d18a0cbSKarsten Graul 	if (lgr->role == SMC_SERV) {
10220d18a0cbSKarsten Graul 		/* client asks to delete this link, send request */
1023fbed3b37SKarsten Graul 		smc_llc_send_delete_link(link, 0, SMC_LLC_REQ, true,
1024fbed3b37SKarsten Graul 					 SMC_LLC_DEL_PROG_INIT_TERM);
102552bedf37SKarsten Graul 	} else {
10260d18a0cbSKarsten Graul 		/* server requests to delete this link, send response */
1027fbed3b37SKarsten Graul 		smc_llc_send_delete_link(link, 0, SMC_LLC_RESP, true,
1028fbed3b37SKarsten Graul 					 SMC_LLC_DEL_PROG_INIT_TERM);
102952bedf37SKarsten Graul 	}
103087523930SKarsten Graul 	smcr_link_down_cond(link);
103152bedf37SKarsten Graul }
103252bedf37SKarsten Graul 
10333bc67e09SKarsten Graul /* process a confirm_rkey request from peer, remote flow */
10343bc67e09SKarsten Graul static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr)
10354ed75de5SKarsten Graul {
10363bc67e09SKarsten Graul 	struct smc_llc_msg_confirm_rkey *llc;
10373bc67e09SKarsten Graul 	struct smc_llc_qentry *qentry;
10383bc67e09SKarsten Graul 	struct smc_link *link;
10393bc67e09SKarsten Graul 	int num_entries;
10403bc67e09SKarsten Graul 	int rk_idx;
10413bc67e09SKarsten Graul 	int i;
10424ed75de5SKarsten Graul 
10433bc67e09SKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
10443bc67e09SKarsten Graul 	llc = &qentry->msg.confirm_rkey;
10453bc67e09SKarsten Graul 	link = qentry->link;
10463bc67e09SKarsten Graul 
10473bc67e09SKarsten Graul 	num_entries = llc->rtoken[0].num_rkeys;
10483bc67e09SKarsten Graul 	/* first rkey entry is for receiving link */
10493bc67e09SKarsten Graul 	rk_idx = smc_rtoken_add(link,
10504ed75de5SKarsten Graul 				llc->rtoken[0].rmb_vaddr,
10514ed75de5SKarsten Graul 				llc->rtoken[0].rmb_key);
10523bc67e09SKarsten Graul 	if (rk_idx < 0)
10533bc67e09SKarsten Graul 		goto out_err;
10544ed75de5SKarsten Graul 
10553bc67e09SKarsten Graul 	for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++)
10563bc67e09SKarsten Graul 		smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id,
10573bc67e09SKarsten Graul 				llc->rtoken[i].rmb_vaddr,
10583bc67e09SKarsten Graul 				llc->rtoken[i].rmb_key);
10593bc67e09SKarsten Graul 	/* max links is 3 so there is no need to support conf_rkey_cont msgs */
10603bc67e09SKarsten Graul 	goto out;
10613bc67e09SKarsten Graul out_err:
10624ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
10633bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY;
10643bc67e09SKarsten Graul out:
10653bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
10663bc67e09SKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
10673bc67e09SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
10684ed75de5SKarsten Graul }
10694ed75de5SKarsten Graul 
1070218b24feSKarsten Graul /* process a delete_rkey request from peer, remote flow */
1071218b24feSKarsten Graul static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
10724ed75de5SKarsten Graul {
1073218b24feSKarsten Graul 	struct smc_llc_msg_delete_rkey *llc;
1074218b24feSKarsten Graul 	struct smc_llc_qentry *qentry;
1075218b24feSKarsten Graul 	struct smc_link *link;
10764ed75de5SKarsten Graul 	u8 err_mask = 0;
10774ed75de5SKarsten Graul 	int i, max;
10784ed75de5SKarsten Graul 
1079218b24feSKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
1080218b24feSKarsten Graul 	llc = &qentry->msg.delete_rkey;
1081218b24feSKarsten Graul 	link = qentry->link;
1082218b24feSKarsten Graul 
10834ed75de5SKarsten Graul 	max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
10844ed75de5SKarsten Graul 	for (i = 0; i < max; i++) {
1085387707fdSKarsten Graul 		if (smc_rtoken_delete(link, llc->rkey[i]))
10864ed75de5SKarsten Graul 			err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
10874ed75de5SKarsten Graul 	}
10884ed75de5SKarsten Graul 	if (err_mask) {
10894ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
10904ed75de5SKarsten Graul 		llc->err_mask = err_mask;
10914ed75de5SKarsten Graul 	}
1092218b24feSKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
1093218b24feSKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
1094218b24feSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
1095218b24feSKarsten Graul }
10964ed75de5SKarsten Graul 
10976c8968c4SKarsten Graul /* flush the llc event queue */
109800a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr)
10999bf9abeaSUrsula Braun {
11006c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry, *q;
11019bf9abeaSUrsula Braun 
11026c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
11036c8968c4SKarsten Graul 	list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
11046c8968c4SKarsten Graul 		list_del_init(&qentry->list);
11056c8968c4SKarsten Graul 		kfree(qentry);
11066c8968c4SKarsten Graul 	}
11076c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
11086c8968c4SKarsten Graul }
11096c8968c4SKarsten Graul 
11106c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
11116c8968c4SKarsten Graul {
11126c8968c4SKarsten Graul 	union smc_llc_msg *llc = &qentry->msg;
11136c8968c4SKarsten Graul 	struct smc_link *link = qentry->link;
11140fb0b02bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
11156c8968c4SKarsten Graul 
1116d854fcbfSKarsten Graul 	if (!smc_link_usable(link))
11176c8968c4SKarsten Graul 		goto out;
1118313164daSKarsten Graul 
1119313164daSKarsten Graul 	switch (llc->raw.hdr.common.type) {
1120313164daSKarsten Graul 	case SMC_LLC_TEST_LINK:
112156e8091cSKarsten Graul 		llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP;
112256e8091cSKarsten Graul 		smc_llc_send_message(link, llc);
1123313164daSKarsten Graul 		break;
112452bedf37SKarsten Graul 	case SMC_LLC_ADD_LINK:
11250fb0b02bSKarsten Graul 		if (list_empty(&lgr->list))
11260fb0b02bSKarsten Graul 			goto out;	/* lgr is terminating */
11270fb0b02bSKarsten Graul 		if (lgr->role == SMC_CLNT) {
11280fb0b02bSKarsten Graul 			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK) {
11290fb0b02bSKarsten Graul 				/* a flow is waiting for this message */
11300fb0b02bSKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
11310fb0b02bSKarsten Graul 							qentry);
11320fb0b02bSKarsten Graul 				wake_up_interruptible(&lgr->llc_waiter);
11330fb0b02bSKarsten Graul 			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
11340fb0b02bSKarsten Graul 						      qentry)) {
1135b45e7f98SKarsten Graul 				schedule_work(&lgr->llc_add_link_work);
11360fb0b02bSKarsten Graul 			}
11370fb0b02bSKarsten Graul 		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
11380fb0b02bSKarsten Graul 			/* as smc server, handle client suggestion */
1139b45e7f98SKarsten Graul 			schedule_work(&lgr->llc_add_link_work);
11400fb0b02bSKarsten Graul 		}
11410fb0b02bSKarsten Graul 		return;
11420fb0b02bSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
114387f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
11440fb0b02bSKarsten Graul 		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
11450fb0b02bSKarsten Graul 			/* a flow is waiting for this message */
11460fb0b02bSKarsten Graul 			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
11470fb0b02bSKarsten Graul 			wake_up_interruptible(&lgr->llc_waiter);
11480fb0b02bSKarsten Graul 			return;
11490fb0b02bSKarsten Graul 		}
115052bedf37SKarsten Graul 		break;
115152bedf37SKarsten Graul 	case SMC_LLC_DELETE_LINK:
115252bedf37SKarsten Graul 		smc_llc_rx_delete_link(link, &llc->delete_link);
115352bedf37SKarsten Graul 		break;
11544ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
11553bc67e09SKarsten Graul 		/* new request from remote, assign to remote flow */
11563bc67e09SKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
11573bc67e09SKarsten Graul 			/* process here, does not wait for more llc msgs */
11583bc67e09SKarsten Graul 			smc_llc_rmt_conf_rkey(lgr);
11593bc67e09SKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
11603bc67e09SKarsten Graul 		}
11613bc67e09SKarsten Graul 		return;
11624ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
116342d18accSKarsten Graul 		/* not used because max links is 3, and 3 rkeys fit into
116442d18accSKarsten Graul 		 * one CONFIRM_RKEY message
116542d18accSKarsten Graul 		 */
11664ed75de5SKarsten Graul 		break;
11674ed75de5SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
1168218b24feSKarsten Graul 		/* new request from remote, assign to remote flow */
1169218b24feSKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
1170218b24feSKarsten Graul 			/* process here, does not wait for more llc msgs */
1171218b24feSKarsten Graul 			smc_llc_rmt_delete_rkey(lgr);
1172218b24feSKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
1173218b24feSKarsten Graul 		}
1174218b24feSKarsten Graul 		return;
1175313164daSKarsten Graul 	}
11766c8968c4SKarsten Graul out:
11776c8968c4SKarsten Graul 	kfree(qentry);
11786c8968c4SKarsten Graul }
11796c8968c4SKarsten Graul 
11806c8968c4SKarsten Graul /* worker to process llc messages on the event queue */
11816c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work)
11826c8968c4SKarsten Graul {
11836c8968c4SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
11846c8968c4SKarsten Graul 						  llc_event_work);
11856c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
11866c8968c4SKarsten Graul 
1187555da9afSKarsten Graul 	if (!lgr->llc_flow_lcl.type && lgr->delayed_event) {
1188555da9afSKarsten Graul 		if (smc_link_usable(lgr->delayed_event->link)) {
1189555da9afSKarsten Graul 			smc_llc_event_handler(lgr->delayed_event);
1190555da9afSKarsten Graul 		} else {
1191555da9afSKarsten Graul 			qentry = lgr->delayed_event;
1192555da9afSKarsten Graul 			lgr->delayed_event = NULL;
1193555da9afSKarsten Graul 			kfree(qentry);
1194555da9afSKarsten Graul 		}
1195555da9afSKarsten Graul 	}
1196555da9afSKarsten Graul 
11976c8968c4SKarsten Graul again:
11986c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
11996c8968c4SKarsten Graul 	if (!list_empty(&lgr->llc_event_q)) {
12006c8968c4SKarsten Graul 		qentry = list_first_entry(&lgr->llc_event_q,
12016c8968c4SKarsten Graul 					  struct smc_llc_qentry, list);
12026c8968c4SKarsten Graul 		list_del_init(&qentry->list);
12036c8968c4SKarsten Graul 		spin_unlock_bh(&lgr->llc_event_q_lock);
12046c8968c4SKarsten Graul 		smc_llc_event_handler(qentry);
12056c8968c4SKarsten Graul 		goto again;
12066c8968c4SKarsten Graul 	}
12076c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
12086c8968c4SKarsten Graul }
12096c8968c4SKarsten Graul 
1210ef79d439SKarsten Graul /* process llc responses in tasklet context */
1211a6688d91SKarsten Graul static void smc_llc_rx_response(struct smc_link *link,
1212a6688d91SKarsten Graul 				struct smc_llc_qentry *qentry)
1213ef79d439SKarsten Graul {
1214a6688d91SKarsten Graul 	u8 llc_type = qentry->msg.raw.hdr.common.type;
1215ef79d439SKarsten Graul 
1216a6688d91SKarsten Graul 	switch (llc_type) {
1217ef79d439SKarsten Graul 	case SMC_LLC_TEST_LINK:
1218ef79d439SKarsten Graul 		if (link->state == SMC_LNK_ACTIVE)
1219ef79d439SKarsten Graul 			complete(&link->llc_testlink_resp);
1220ef79d439SKarsten Graul 		break;
1221ef79d439SKarsten Graul 	case SMC_LLC_ADD_LINK:
12224667bb4aSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
122387f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
12243d88a21bSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
12256d74c3a8SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
12264667bb4aSKarsten Graul 		/* assign responses to the local flow, we requested them */
12274667bb4aSKarsten Graul 		smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry);
12284667bb4aSKarsten Graul 		wake_up_interruptible(&link->lgr->llc_waiter);
12294667bb4aSKarsten Graul 		return;
1230ef79d439SKarsten Graul 	case SMC_LLC_DELETE_LINK:
1231ef79d439SKarsten Graul 		if (link->lgr->role == SMC_SERV)
1232ef79d439SKarsten Graul 			smc_lgr_schedule_free_work_fast(link->lgr);
1233ef79d439SKarsten Graul 		break;
1234ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
123542d18accSKarsten Graul 		/* not used because max links is 3 */
1236ef79d439SKarsten Graul 		break;
1237ef79d439SKarsten Graul 	}
1238a6688d91SKarsten Graul 	kfree(qentry);
1239ef79d439SKarsten Graul }
1240ef79d439SKarsten Graul 
1241a6688d91SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
12426c8968c4SKarsten Graul {
12436c8968c4SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
12446c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
12456c8968c4SKarsten Graul 	unsigned long flags;
12466c8968c4SKarsten Graul 
12476c8968c4SKarsten Graul 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
12486c8968c4SKarsten Graul 	if (!qentry)
12496c8968c4SKarsten Graul 		return;
12506c8968c4SKarsten Graul 	qentry->link = link;
12516c8968c4SKarsten Graul 	INIT_LIST_HEAD(&qentry->list);
12526c8968c4SKarsten Graul 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
1253a6688d91SKarsten Graul 
1254a6688d91SKarsten Graul 	/* process responses immediately */
1255a6688d91SKarsten Graul 	if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) {
1256a6688d91SKarsten Graul 		smc_llc_rx_response(link, qentry);
1257a6688d91SKarsten Graul 		return;
1258a6688d91SKarsten Graul 	}
1259a6688d91SKarsten Graul 
1260a6688d91SKarsten Graul 	/* add requests to event queue */
12616c8968c4SKarsten Graul 	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
12626c8968c4SKarsten Graul 	list_add_tail(&qentry->list, &lgr->llc_event_q);
12636c8968c4SKarsten Graul 	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
12646c8968c4SKarsten Graul 	schedule_work(&link->lgr->llc_event_work);
12659bf9abeaSUrsula Braun }
12669bf9abeaSUrsula Braun 
1267a6688d91SKarsten Graul /* copy received msg and add it to the event queue */
1268a6688d91SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
1269a6688d91SKarsten Graul {
1270a6688d91SKarsten Graul 	struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
1271a6688d91SKarsten Graul 	union smc_llc_msg *llc = buf;
1272a6688d91SKarsten Graul 
1273a6688d91SKarsten Graul 	if (wc->byte_len < sizeof(*llc))
1274a6688d91SKarsten Graul 		return; /* short message */
1275a6688d91SKarsten Graul 	if (llc->raw.hdr.length != sizeof(*llc))
1276a6688d91SKarsten Graul 		return; /* invalid message */
1277a6688d91SKarsten Graul 
1278a6688d91SKarsten Graul 	smc_llc_enqueue(link, llc);
1279a6688d91SKarsten Graul }
1280a6688d91SKarsten Graul 
128144aa81ceSKarsten Graul /***************************** worker, utils *********************************/
1282877ae5beSKarsten Graul 
1283877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work)
1284877ae5beSKarsten Graul {
1285877ae5beSKarsten Graul 	struct smc_link *link = container_of(to_delayed_work(work),
1286877ae5beSKarsten Graul 					     struct smc_link, llc_testlink_wrk);
1287877ae5beSKarsten Graul 	unsigned long next_interval;
1288877ae5beSKarsten Graul 	unsigned long expire_time;
1289877ae5beSKarsten Graul 	u8 user_data[16] = { 0 };
1290877ae5beSKarsten Graul 	int rc;
1291877ae5beSKarsten Graul 
1292877ae5beSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
1293877ae5beSKarsten Graul 		return;		/* don't reschedule worker */
1294877ae5beSKarsten Graul 	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
1295877ae5beSKarsten Graul 	if (time_is_after_jiffies(expire_time)) {
1296877ae5beSKarsten Graul 		next_interval = expire_time - jiffies;
1297877ae5beSKarsten Graul 		goto out;
1298877ae5beSKarsten Graul 	}
1299877ae5beSKarsten Graul 	reinit_completion(&link->llc_testlink_resp);
1300d97935faSKarsten Graul 	smc_llc_send_test_link(link, user_data);
1301877ae5beSKarsten Graul 	/* receive TEST LINK response over RoCE fabric */
1302877ae5beSKarsten Graul 	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
1303877ae5beSKarsten Graul 						       SMC_LLC_WAIT_TIME);
13041020e1efSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
13051020e1efSKarsten Graul 		return;		/* link state changed */
1306877ae5beSKarsten Graul 	if (rc <= 0) {
130787523930SKarsten Graul 		smcr_link_down_cond_sched(link);
1308877ae5beSKarsten Graul 		return;
1309877ae5beSKarsten Graul 	}
1310877ae5beSKarsten Graul 	next_interval = link->llc_testlink_time;
1311877ae5beSKarsten Graul out:
13121020e1efSKarsten Graul 	schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
1313877ae5beSKarsten Graul }
1314877ae5beSKarsten Graul 
131500a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
131600a049cfSKarsten Graul {
131700a049cfSKarsten Graul 	struct net *net = sock_net(smc->clcsock->sk);
131800a049cfSKarsten Graul 
131900a049cfSKarsten Graul 	INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
1320b45e7f98SKarsten Graul 	INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work);
132100a049cfSKarsten Graul 	INIT_LIST_HEAD(&lgr->llc_event_q);
132200a049cfSKarsten Graul 	spin_lock_init(&lgr->llc_event_q_lock);
1323555da9afSKarsten Graul 	spin_lock_init(&lgr->llc_flow_lock);
1324555da9afSKarsten Graul 	init_waitqueue_head(&lgr->llc_waiter);
1325d5500667SKarsten Graul 	mutex_init(&lgr->llc_conf_mutex);
132600a049cfSKarsten Graul 	lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;
132700a049cfSKarsten Graul }
132800a049cfSKarsten Graul 
132900a049cfSKarsten Graul /* called after lgr was removed from lgr_list */
133000a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr)
133100a049cfSKarsten Graul {
133200a049cfSKarsten Graul 	smc_llc_event_flush(lgr);
1333555da9afSKarsten Graul 	wake_up_interruptible_all(&lgr->llc_waiter);
133400a049cfSKarsten Graul 	cancel_work_sync(&lgr->llc_event_work);
1335b45e7f98SKarsten Graul 	cancel_work_sync(&lgr->llc_add_link_work);
1336555da9afSKarsten Graul 	if (lgr->delayed_event) {
1337555da9afSKarsten Graul 		kfree(lgr->delayed_event);
1338555da9afSKarsten Graul 		lgr->delayed_event = NULL;
1339555da9afSKarsten Graul 	}
134000a049cfSKarsten Graul }
134100a049cfSKarsten Graul 
13422a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link)
1343877ae5beSKarsten Graul {
1344877ae5beSKarsten Graul 	init_completion(&link->llc_testlink_resp);
1345877ae5beSKarsten Graul 	INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
13462a4c57a9SKarsten Graul 	return 0;
1347b32cf4abSKarsten Graul }
1348b32cf4abSKarsten Graul 
134900a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link)
1350b32cf4abSKarsten Graul {
1351877ae5beSKarsten Graul 	link->state = SMC_LNK_ACTIVE;
135200a049cfSKarsten Graul 	if (link->lgr->llc_testlink_time) {
135300a049cfSKarsten Graul 		link->llc_testlink_time = link->lgr->llc_testlink_time * HZ;
13541020e1efSKarsten Graul 		schedule_delayed_work(&link->llc_testlink_wrk,
1355877ae5beSKarsten Graul 				      link->llc_testlink_time);
1356877ae5beSKarsten Graul 	}
1357877ae5beSKarsten Graul }
1358877ae5beSKarsten Graul 
1359877ae5beSKarsten Graul /* called in worker context */
13602a4c57a9SKarsten Graul void smc_llc_link_clear(struct smc_link *link)
1361877ae5beSKarsten Graul {
13622140ac26SKarsten Graul 	complete(&link->llc_testlink_resp);
13632140ac26SKarsten Graul 	cancel_delayed_work_sync(&link->llc_testlink_wrk);
13642140ac26SKarsten Graul 	smc_wr_wakeup_reg_wait(link);
13652140ac26SKarsten Graul 	smc_wr_wakeup_tx_wait(link);
1366877ae5beSKarsten Graul }
1367877ae5beSKarsten Graul 
13683d88a21bSKarsten Graul /* register a new rtoken at the remote peer (for all links) */
13693d88a21bSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *send_link,
137044aa81ceSKarsten Graul 			    struct smc_buf_desc *rmb_desc)
137144aa81ceSKarsten Graul {
13723d88a21bSKarsten Graul 	struct smc_link_group *lgr = send_link->lgr;
13733d88a21bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
13743d88a21bSKarsten Graul 	int rc = 0;
137544aa81ceSKarsten Graul 
13763d88a21bSKarsten Graul 	rc = smc_llc_send_confirm_rkey(send_link, rmb_desc);
13773d88a21bSKarsten Graul 	if (rc)
13783d88a21bSKarsten Graul 		goto out;
137944aa81ceSKarsten Graul 	/* receive CONFIRM RKEY response from server over RoCE fabric */
13803d88a21bSKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
13813d88a21bSKarsten Graul 			      SMC_LLC_CONFIRM_RKEY);
13823d88a21bSKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
13833d88a21bSKarsten Graul 		rc = -EFAULT;
13843d88a21bSKarsten Graul out:
13853d88a21bSKarsten Graul 	if (qentry)
13863d88a21bSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
13873d88a21bSKarsten Graul 	return rc;
138844aa81ceSKarsten Graul }
138944aa81ceSKarsten Graul 
139060e03c62SKarsten Graul /* unregister an rtoken at the remote peer */
13916d74c3a8SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link_group *lgr,
139260e03c62SKarsten Graul 			   struct smc_buf_desc *rmb_desc)
139360e03c62SKarsten Graul {
13946d74c3a8SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
13956d74c3a8SKarsten Graul 	struct smc_link *send_link;
13960b29ec64SUrsula Braun 	int rc = 0;
139760e03c62SKarsten Graul 
13986d74c3a8SKarsten Graul 	send_link = smc_llc_usable_link(lgr);
13996d74c3a8SKarsten Graul 	if (!send_link)
14006d74c3a8SKarsten Graul 		return -ENOLINK;
14016d74c3a8SKarsten Graul 
14026d74c3a8SKarsten Graul 	/* protected by llc_flow control */
14036d74c3a8SKarsten Graul 	rc = smc_llc_send_delete_rkey(send_link, rmb_desc);
140460e03c62SKarsten Graul 	if (rc)
140560e03c62SKarsten Graul 		goto out;
140660e03c62SKarsten Graul 	/* receive DELETE RKEY response from server over RoCE fabric */
14076d74c3a8SKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
14086d74c3a8SKarsten Graul 			      SMC_LLC_DELETE_RKEY);
14096d74c3a8SKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
141060e03c62SKarsten Graul 		rc = -EFAULT;
141160e03c62SKarsten Graul out:
14126d74c3a8SKarsten Graul 	if (qentry)
14136d74c3a8SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
141460e03c62SKarsten Graul 	return rc;
141560e03c62SKarsten Graul }
141660e03c62SKarsten Graul 
141792334cfcSKarsten Graul /* evaluate confirm link request or response */
141892334cfcSKarsten Graul int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry,
141992334cfcSKarsten Graul 			   enum smc_llc_reqresp type)
142092334cfcSKarsten Graul {
142192334cfcSKarsten Graul 	if (type == SMC_LLC_REQ)	/* SMC server assigns link_id */
142292334cfcSKarsten Graul 		qentry->link->link_id = qentry->msg.confirm_link.link_num;
142392334cfcSKarsten Graul 	if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
142492334cfcSKarsten Graul 		return -ENOTSUPP;
142592334cfcSKarsten Graul 	return 0;
142692334cfcSKarsten Graul }
142792334cfcSKarsten Graul 
14289bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/
14299bf9abeaSUrsula Braun 
14309bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
14319bf9abeaSUrsula Braun 	{
14329bf9abeaSUrsula Braun 		.handler	= smc_llc_rx_handler,
14339bf9abeaSUrsula Braun 		.type		= SMC_LLC_CONFIRM_LINK
14349bf9abeaSUrsula Braun 	},
14359bf9abeaSUrsula Braun 	{
1436313164daSKarsten Graul 		.handler	= smc_llc_rx_handler,
1437313164daSKarsten Graul 		.type		= SMC_LLC_TEST_LINK
1438313164daSKarsten Graul 	},
1439313164daSKarsten Graul 	{
14404ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
144152bedf37SKarsten Graul 		.type		= SMC_LLC_ADD_LINK
144252bedf37SKarsten Graul 	},
144352bedf37SKarsten Graul 	{
144452bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
144587f88cdaSKarsten Graul 		.type		= SMC_LLC_ADD_LINK_CONT
144687f88cdaSKarsten Graul 	},
144787f88cdaSKarsten Graul 	{
144887f88cdaSKarsten Graul 		.handler	= smc_llc_rx_handler,
144952bedf37SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK
145052bedf37SKarsten Graul 	},
145152bedf37SKarsten Graul 	{
145252bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
14534ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY
14544ed75de5SKarsten Graul 	},
14554ed75de5SKarsten Graul 	{
14564ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
14574ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_CONT
14584ed75de5SKarsten Graul 	},
14594ed75de5SKarsten Graul 	{
14604ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
14614ed75de5SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY
14624ed75de5SKarsten Graul 	},
14634ed75de5SKarsten Graul 	{
14649bf9abeaSUrsula Braun 		.handler	= NULL,
14659bf9abeaSUrsula Braun 	}
14669bf9abeaSUrsula Braun };
14679bf9abeaSUrsula Braun 
14689bf9abeaSUrsula Braun int __init smc_llc_init(void)
14699bf9abeaSUrsula Braun {
14709bf9abeaSUrsula Braun 	struct smc_wr_rx_handler *handler;
14719bf9abeaSUrsula Braun 	int rc = 0;
14729bf9abeaSUrsula Braun 
14739bf9abeaSUrsula Braun 	for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
14749bf9abeaSUrsula Braun 		INIT_HLIST_NODE(&handler->list);
14759bf9abeaSUrsula Braun 		rc = smc_wr_rx_register_handler(handler);
14769bf9abeaSUrsula Braun 		if (rc)
14779bf9abeaSUrsula Braun 			break;
14789bf9abeaSUrsula Braun 	}
14799bf9abeaSUrsula Braun 	return rc;
14809bf9abeaSUrsula Braun }
1481