xref: /openbmc/linux/net/smc/smc_llc.c (revision 9c4168789cc635e1f0d265157b7617259d56bfee)
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*9c416878SKarsten Graul static int smc_llc_active_link_count(struct smc_link_group *lgr)
867*9c416878SKarsten Graul {
868*9c416878SKarsten Graul 	int i, link_count = 0;
869*9c416878SKarsten Graul 
870*9c416878SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
871*9c416878SKarsten Graul 		if (!smc_link_usable(&lgr->lnk[i]))
872*9c416878SKarsten Graul 			continue;
873*9c416878SKarsten Graul 		link_count++;
874*9c416878SKarsten Graul 	}
875*9c416878SKarsten Graul 	return link_count;
876*9c416878SKarsten Graul }
877*9c416878SKarsten Graul 
878c9a5d243SKarsten Graul /* find the asymmetric link when 3 links are established  */
879c9a5d243SKarsten Graul static struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr)
880c9a5d243SKarsten Graul {
881c9a5d243SKarsten Graul 	int asym_idx = -ENOENT;
882c9a5d243SKarsten Graul 	int i, j, k;
883c9a5d243SKarsten Graul 	bool found;
884c9a5d243SKarsten Graul 
885c9a5d243SKarsten Graul 	/* determine asymmetric link */
886c9a5d243SKarsten Graul 	found = false;
887c9a5d243SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
888c9a5d243SKarsten Graul 		for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) {
889c9a5d243SKarsten Graul 			if (!smc_link_usable(&lgr->lnk[i]) ||
890c9a5d243SKarsten Graul 			    !smc_link_usable(&lgr->lnk[j]))
891c9a5d243SKarsten Graul 				continue;
892c9a5d243SKarsten Graul 			if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid,
893c9a5d243SKarsten Graul 				    SMC_GID_SIZE)) {
894c9a5d243SKarsten Graul 				found = true;	/* asym_lnk is i or j */
895c9a5d243SKarsten Graul 				break;
896c9a5d243SKarsten Graul 			}
897c9a5d243SKarsten Graul 		}
898c9a5d243SKarsten Graul 		if (found)
899c9a5d243SKarsten Graul 			break;
900c9a5d243SKarsten Graul 	}
901c9a5d243SKarsten Graul 	if (!found)
902c9a5d243SKarsten Graul 		goto out; /* no asymmetric link */
903c9a5d243SKarsten Graul 	for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) {
904c9a5d243SKarsten Graul 		if (!smc_link_usable(&lgr->lnk[k]))
905c9a5d243SKarsten Graul 			continue;
906c9a5d243SKarsten Graul 		if (k != i &&
907c9a5d243SKarsten Graul 		    !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid,
908c9a5d243SKarsten Graul 			    SMC_GID_SIZE)) {
909c9a5d243SKarsten Graul 			asym_idx = i;
910c9a5d243SKarsten Graul 			break;
911c9a5d243SKarsten Graul 		}
912c9a5d243SKarsten Graul 		if (k != j &&
913c9a5d243SKarsten Graul 		    !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid,
914c9a5d243SKarsten Graul 			    SMC_GID_SIZE)) {
915c9a5d243SKarsten Graul 			asym_idx = j;
916c9a5d243SKarsten Graul 			break;
917c9a5d243SKarsten Graul 		}
918c9a5d243SKarsten Graul 	}
919c9a5d243SKarsten Graul out:
920c9a5d243SKarsten Graul 	return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx];
921c9a5d243SKarsten Graul }
922c9a5d243SKarsten Graul 
923c9a5d243SKarsten Graul static void smc_llc_delete_asym_link(struct smc_link_group *lgr)
924c9a5d243SKarsten Graul {
925c9a5d243SKarsten Graul 	struct smc_link *lnk_new = NULL, *lnk_asym;
926c9a5d243SKarsten Graul 	struct smc_llc_qentry *qentry;
927c9a5d243SKarsten Graul 	int rc;
928c9a5d243SKarsten Graul 
929c9a5d243SKarsten Graul 	lnk_asym = smc_llc_find_asym_link(lgr);
930c9a5d243SKarsten Graul 	if (!lnk_asym)
931c9a5d243SKarsten Graul 		return; /* no asymmetric link */
932c9a5d243SKarsten Graul 	if (!smc_link_downing(&lnk_asym->state))
933c9a5d243SKarsten Graul 		return;
934c9a5d243SKarsten Graul 	/* tbd: lnk_new = smc_switch_conns(lgr, lnk_asym, false); */
935c9a5d243SKarsten Graul 	smc_wr_tx_wait_no_pending_sends(lnk_asym);
936c9a5d243SKarsten Graul 	if (!lnk_new)
937c9a5d243SKarsten Graul 		goto out_free;
938c9a5d243SKarsten Graul 	/* change flow type from ADD_LINK into DEL_LINK */
939c9a5d243SKarsten Graul 	lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK;
940c9a5d243SKarsten Graul 	rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ,
941c9a5d243SKarsten Graul 				      true, SMC_LLC_DEL_NO_ASYM_NEEDED);
942c9a5d243SKarsten Graul 	if (rc) {
943c9a5d243SKarsten Graul 		smcr_link_down_cond(lnk_new);
944c9a5d243SKarsten Graul 		goto out_free;
945c9a5d243SKarsten Graul 	}
946c9a5d243SKarsten Graul 	qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME,
947c9a5d243SKarsten Graul 			      SMC_LLC_DELETE_LINK);
948c9a5d243SKarsten Graul 	if (!qentry) {
949c9a5d243SKarsten Graul 		smcr_link_down_cond(lnk_new);
950c9a5d243SKarsten Graul 		goto out_free;
951c9a5d243SKarsten Graul 	}
952c9a5d243SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
953c9a5d243SKarsten Graul out_free:
954c9a5d243SKarsten Graul 	smcr_link_clear(lnk_asym);
955c9a5d243SKarsten Graul }
956c9a5d243SKarsten Graul 
95757b49924SKarsten Graul static int smc_llc_srv_rkey_exchange(struct smc_link *link,
95857b49924SKarsten Graul 				     struct smc_link *link_new)
95957b49924SKarsten Graul {
96057b49924SKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
96157b49924SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
96257b49924SKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
96357b49924SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
96457b49924SKarsten Graul 	struct smc_buf_desc *buf_pos;
96557b49924SKarsten Graul 	int buf_lst;
96657b49924SKarsten Graul 	int rc = 0;
96757b49924SKarsten Graul 	int i;
96857b49924SKarsten Graul 
96957b49924SKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
97057b49924SKarsten Graul 	num_rkeys_send = lgr->conns_num;
97157b49924SKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
97257b49924SKarsten Graul 	do {
97357b49924SKarsten Graul 		smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
97457b49924SKarsten Graul 				      &buf_lst, &buf_pos);
97557b49924SKarsten Graul 		qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME,
97657b49924SKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
97757b49924SKarsten Graul 		if (!qentry) {
97857b49924SKarsten Graul 			rc = -ETIMEDOUT;
97957b49924SKarsten Graul 			goto out;
98057b49924SKarsten Graul 		}
98157b49924SKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
98257b49924SKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
98357b49924SKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
98457b49924SKarsten Graul 		for (i = 0; i < max; i++) {
98557b49924SKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
98657b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key,
98757b49924SKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
98857b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
98957b49924SKarsten Graul 			num_rkeys_recv--;
99057b49924SKarsten Graul 		}
99157b49924SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
99257b49924SKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
99357b49924SKarsten Graul out:
99457b49924SKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
99557b49924SKarsten Graul 	return rc;
99657b49924SKarsten Graul }
99757b49924SKarsten Graul 
9981551c95bSKarsten Graul static int smc_llc_srv_conf_link(struct smc_link *link,
9991551c95bSKarsten Graul 				 struct smc_link *link_new,
10001551c95bSKarsten Graul 				 enum smc_lgr_type lgr_new_t)
10011551c95bSKarsten Graul {
10021551c95bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
10031551c95bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
10041551c95bSKarsten Graul 	int rc;
10051551c95bSKarsten Graul 
10061551c95bSKarsten Graul 	/* send CONFIRM LINK request over the RoCE fabric */
10071551c95bSKarsten Graul 	rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ);
10081551c95bSKarsten Graul 	if (rc)
10091551c95bSKarsten Graul 		return -ENOLINK;
10101551c95bSKarsten Graul 	/* receive CONFIRM LINK response over the RoCE fabric */
10111551c95bSKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME,
10121551c95bSKarsten Graul 			      SMC_LLC_CONFIRM_LINK);
10131551c95bSKarsten Graul 	if (!qentry) {
10141551c95bSKarsten Graul 		/* send DELETE LINK */
10151551c95bSKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
10161551c95bSKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
10171551c95bSKarsten Graul 		return -ENOLINK;
10181551c95bSKarsten Graul 	}
10191551c95bSKarsten Graul 	smc_llc_link_active(link_new);
10201551c95bSKarsten Graul 	lgr->type = lgr_new_t;
10211551c95bSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
10221551c95bSKarsten Graul 	return 0;
10231551c95bSKarsten Graul }
10241551c95bSKarsten Graul 
10252d2209f2SKarsten Graul int smc_llc_srv_add_link(struct smc_link *link)
10262d2209f2SKarsten Graul {
10272d2209f2SKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
10282d2209f2SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
10292d2209f2SKarsten Graul 	struct smc_llc_msg_add_link *add_llc;
10302d2209f2SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
10312d2209f2SKarsten Graul 	struct smc_link *link_new;
10322d2209f2SKarsten Graul 	struct smc_init_info ini;
10332d2209f2SKarsten Graul 	int lnk_idx, rc = 0;
10342d2209f2SKarsten Graul 
10352d2209f2SKarsten Graul 	/* ignore client add link recommendation, start new flow */
10362d2209f2SKarsten Graul 	ini.vlan_id = lgr->vlan_id;
10372d2209f2SKarsten Graul 	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
10382d2209f2SKarsten Graul 	if (!ini.ib_dev) {
10392d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
10402d2209f2SKarsten Graul 		ini.ib_dev = link->smcibdev;
10412d2209f2SKarsten Graul 		ini.ib_port = link->ibport;
10422d2209f2SKarsten Graul 	}
10432d2209f2SKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
10442d2209f2SKarsten Graul 	if (lnk_idx < 0)
10452d2209f2SKarsten Graul 		return 0;
10462d2209f2SKarsten Graul 
10472d2209f2SKarsten Graul 	rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, &ini);
10482d2209f2SKarsten Graul 	if (rc)
10492d2209f2SKarsten Graul 		return rc;
10502d2209f2SKarsten Graul 	link_new = &lgr->lnk[lnk_idx];
10512d2209f2SKarsten Graul 	rc = smc_llc_send_add_link(link,
10522d2209f2SKarsten Graul 				   link_new->smcibdev->mac[ini.ib_port - 1],
10532d2209f2SKarsten Graul 				   link_new->gid, link_new, SMC_LLC_REQ);
10542d2209f2SKarsten Graul 	if (rc)
10552d2209f2SKarsten Graul 		goto out_err;
10562d2209f2SKarsten Graul 	/* receive ADD LINK response over the RoCE fabric */
10572d2209f2SKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK);
10582d2209f2SKarsten Graul 	if (!qentry) {
10592d2209f2SKarsten Graul 		rc = -ETIMEDOUT;
10602d2209f2SKarsten Graul 		goto out_err;
10612d2209f2SKarsten Graul 	}
10622d2209f2SKarsten Graul 	add_llc = &qentry->msg.add_link;
10632d2209f2SKarsten Graul 	if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
10642d2209f2SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
10652d2209f2SKarsten Graul 		rc = -ENOLINK;
10662d2209f2SKarsten Graul 		goto out_err;
10672d2209f2SKarsten Graul 	}
10682d2209f2SKarsten Graul 	if (lgr->type == SMC_LGR_SINGLE &&
10692d2209f2SKarsten Graul 	    (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
10702d2209f2SKarsten Graul 	     !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN))) {
10712d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
10722d2209f2SKarsten Graul 	}
10732d2209f2SKarsten Graul 	smc_llc_save_add_link_info(link_new, add_llc);
10742d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
10752d2209f2SKarsten Graul 
10762d2209f2SKarsten Graul 	rc = smc_ib_ready_link(link_new);
10772d2209f2SKarsten Graul 	if (rc)
10782d2209f2SKarsten Graul 		goto out_err;
10792d2209f2SKarsten Graul 	rc = smcr_buf_map_lgr(link_new);
10802d2209f2SKarsten Graul 	if (rc)
10812d2209f2SKarsten Graul 		goto out_err;
10822d2209f2SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
10832d2209f2SKarsten Graul 	if (rc)
10842d2209f2SKarsten Graul 		goto out_err;
108557b49924SKarsten Graul 	rc = smc_llc_srv_rkey_exchange(link, link_new);
10862d2209f2SKarsten Graul 	if (rc)
10872d2209f2SKarsten Graul 		goto out_err;
10881551c95bSKarsten Graul 	rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
10892d2209f2SKarsten Graul 	if (rc)
10902d2209f2SKarsten Graul 		goto out_err;
10912d2209f2SKarsten Graul 	return 0;
10922d2209f2SKarsten Graul out_err:
10932d2209f2SKarsten Graul 	smcr_link_clear(link_new);
10942d2209f2SKarsten Graul 	return rc;
10952d2209f2SKarsten Graul }
10962d2209f2SKarsten Graul 
10972d2209f2SKarsten Graul static void smc_llc_process_srv_add_link(struct smc_link_group *lgr)
10982d2209f2SKarsten Graul {
10992d2209f2SKarsten Graul 	struct smc_link *link = lgr->llc_flow_lcl.qentry->link;
11002d2209f2SKarsten Graul 	int rc;
11012d2209f2SKarsten Graul 
11022d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
11032d2209f2SKarsten Graul 
11042d2209f2SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
11052d2209f2SKarsten Graul 	rc = smc_llc_srv_add_link(link);
11062d2209f2SKarsten Graul 	if (!rc && lgr->type == SMC_LGR_SYMMETRIC) {
11072d2209f2SKarsten Graul 		/* delete any asymmetric link */
1108c9a5d243SKarsten Graul 		smc_llc_delete_asym_link(lgr);
11092d2209f2SKarsten Graul 	}
11102d2209f2SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
11112d2209f2SKarsten Graul }
11122d2209f2SKarsten Graul 
1113b45e7f98SKarsten Graul /* worker to process an add link message */
1114b45e7f98SKarsten Graul static void smc_llc_add_link_work(struct work_struct *work)
1115b45e7f98SKarsten Graul {
1116b45e7f98SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
1117b45e7f98SKarsten Graul 						  llc_add_link_work);
1118b45e7f98SKarsten Graul 
1119b45e7f98SKarsten Graul 	if (list_empty(&lgr->list)) {
1120b45e7f98SKarsten Graul 		/* link group is terminating */
1121b45e7f98SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
1122b45e7f98SKarsten Graul 		goto out;
1123b45e7f98SKarsten Graul 	}
1124b45e7f98SKarsten Graul 
1125b1570a87SKarsten Graul 	if (lgr->role == SMC_CLNT)
1126b1570a87SKarsten Graul 		smc_llc_process_cli_add_link(lgr);
11272d2209f2SKarsten Graul 	else
11282d2209f2SKarsten Graul 		smc_llc_process_srv_add_link(lgr);
1129b45e7f98SKarsten Graul out:
1130b45e7f98SKarsten Graul 	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
1131b45e7f98SKarsten Graul }
1132b45e7f98SKarsten Graul 
1133*9c416878SKarsten Graul static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
1134*9c416878SKarsten Graul {
1135*9c416878SKarsten Graul 	struct smc_link *lnk_del = NULL, *lnk_asym, *lnk;
1136*9c416878SKarsten Graul 	struct smc_llc_msg_del_link *del_llc;
1137*9c416878SKarsten Graul 	struct smc_llc_qentry *qentry;
1138*9c416878SKarsten Graul 	int active_links;
1139*9c416878SKarsten Graul 	int lnk_idx;
1140*9c416878SKarsten Graul 
1141*9c416878SKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
1142*9c416878SKarsten Graul 	lnk = qentry->link;
1143*9c416878SKarsten Graul 	del_llc = &qentry->msg.delete_link;
1144*9c416878SKarsten Graul 
1145*9c416878SKarsten Graul 	if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) {
1146*9c416878SKarsten Graul 		smc_lgr_terminate_sched(lgr);
1147*9c416878SKarsten Graul 		goto out;
1148*9c416878SKarsten Graul 	}
1149*9c416878SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
1150*9c416878SKarsten Graul 	/* delete single link */
1151*9c416878SKarsten Graul 	for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) {
1152*9c416878SKarsten Graul 		if (lgr->lnk[lnk_idx].link_id != del_llc->link_num)
1153*9c416878SKarsten Graul 			continue;
1154*9c416878SKarsten Graul 		lnk_del = &lgr->lnk[lnk_idx];
1155*9c416878SKarsten Graul 		break;
1156*9c416878SKarsten Graul 	}
1157*9c416878SKarsten Graul 	del_llc->hd.flags |= SMC_LLC_FLAG_RESP;
1158*9c416878SKarsten Graul 	if (!lnk_del) {
1159*9c416878SKarsten Graul 		/* link was not found */
1160*9c416878SKarsten Graul 		del_llc->reason = htonl(SMC_LLC_DEL_NOLNK);
1161*9c416878SKarsten Graul 		smc_llc_send_message(lnk, &qentry->msg);
1162*9c416878SKarsten Graul 		goto out_unlock;
1163*9c416878SKarsten Graul 	}
1164*9c416878SKarsten Graul 	lnk_asym = smc_llc_find_asym_link(lgr);
1165*9c416878SKarsten Graul 
1166*9c416878SKarsten Graul 	del_llc->reason = 0;
1167*9c416878SKarsten Graul 	smc_llc_send_message(lnk, &qentry->msg); /* response */
1168*9c416878SKarsten Graul 
1169*9c416878SKarsten Graul 	if (smc_link_downing(&lnk_del->state)) {
1170*9c416878SKarsten Graul 		/* tbd: call smc_switch_conns(lgr, lnk_del, false); */
1171*9c416878SKarsten Graul 		smc_wr_tx_wait_no_pending_sends(lnk_del);
1172*9c416878SKarsten Graul 	}
1173*9c416878SKarsten Graul 	smcr_link_clear(lnk_del);
1174*9c416878SKarsten Graul 
1175*9c416878SKarsten Graul 	active_links = smc_llc_active_link_count(lgr);
1176*9c416878SKarsten Graul 	if (lnk_del == lnk_asym) {
1177*9c416878SKarsten Graul 		/* expected deletion of asym link, don't change lgr state */
1178*9c416878SKarsten Graul 	} else if (active_links == 1) {
1179*9c416878SKarsten Graul 		lgr->type = SMC_LGR_SINGLE;
1180*9c416878SKarsten Graul 	} else if (!active_links) {
1181*9c416878SKarsten Graul 		lgr->type = SMC_LGR_NONE;
1182*9c416878SKarsten Graul 		smc_lgr_terminate_sched(lgr);
1183*9c416878SKarsten Graul 	}
1184*9c416878SKarsten Graul out_unlock:
1185*9c416878SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
1186*9c416878SKarsten Graul out:
1187*9c416878SKarsten Graul 	kfree(qentry);
1188*9c416878SKarsten Graul }
1189*9c416878SKarsten Graul 
11909ec6bf19SKarsten Graul static void smc_llc_delete_link_work(struct work_struct *work)
119152bedf37SKarsten Graul {
11929ec6bf19SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
11939ec6bf19SKarsten Graul 						  llc_del_link_work);
119452bedf37SKarsten Graul 
11959ec6bf19SKarsten Graul 	if (list_empty(&lgr->list)) {
11969ec6bf19SKarsten Graul 		/* link group is terminating */
11979ec6bf19SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
11989ec6bf19SKarsten Graul 		goto out;
119952bedf37SKarsten Graul 	}
1200*9c416878SKarsten Graul 
1201*9c416878SKarsten Graul 	if (lgr->role == SMC_CLNT)
1202*9c416878SKarsten Graul 		smc_llc_process_cli_delete_link(lgr);
12039ec6bf19SKarsten Graul out:
12049ec6bf19SKarsten Graul 	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
120552bedf37SKarsten Graul }
120652bedf37SKarsten Graul 
12073bc67e09SKarsten Graul /* process a confirm_rkey request from peer, remote flow */
12083bc67e09SKarsten Graul static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr)
12094ed75de5SKarsten Graul {
12103bc67e09SKarsten Graul 	struct smc_llc_msg_confirm_rkey *llc;
12113bc67e09SKarsten Graul 	struct smc_llc_qentry *qentry;
12123bc67e09SKarsten Graul 	struct smc_link *link;
12133bc67e09SKarsten Graul 	int num_entries;
12143bc67e09SKarsten Graul 	int rk_idx;
12153bc67e09SKarsten Graul 	int i;
12164ed75de5SKarsten Graul 
12173bc67e09SKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
12183bc67e09SKarsten Graul 	llc = &qentry->msg.confirm_rkey;
12193bc67e09SKarsten Graul 	link = qentry->link;
12203bc67e09SKarsten Graul 
12213bc67e09SKarsten Graul 	num_entries = llc->rtoken[0].num_rkeys;
12223bc67e09SKarsten Graul 	/* first rkey entry is for receiving link */
12233bc67e09SKarsten Graul 	rk_idx = smc_rtoken_add(link,
12244ed75de5SKarsten Graul 				llc->rtoken[0].rmb_vaddr,
12254ed75de5SKarsten Graul 				llc->rtoken[0].rmb_key);
12263bc67e09SKarsten Graul 	if (rk_idx < 0)
12273bc67e09SKarsten Graul 		goto out_err;
12284ed75de5SKarsten Graul 
12293bc67e09SKarsten Graul 	for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++)
12303bc67e09SKarsten Graul 		smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id,
12313bc67e09SKarsten Graul 				llc->rtoken[i].rmb_vaddr,
12323bc67e09SKarsten Graul 				llc->rtoken[i].rmb_key);
12333bc67e09SKarsten Graul 	/* max links is 3 so there is no need to support conf_rkey_cont msgs */
12343bc67e09SKarsten Graul 	goto out;
12353bc67e09SKarsten Graul out_err:
12364ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
12373bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY;
12383bc67e09SKarsten Graul out:
12393bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
12403bc67e09SKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
12413bc67e09SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
12424ed75de5SKarsten Graul }
12434ed75de5SKarsten Graul 
1244218b24feSKarsten Graul /* process a delete_rkey request from peer, remote flow */
1245218b24feSKarsten Graul static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
12464ed75de5SKarsten Graul {
1247218b24feSKarsten Graul 	struct smc_llc_msg_delete_rkey *llc;
1248218b24feSKarsten Graul 	struct smc_llc_qentry *qentry;
1249218b24feSKarsten Graul 	struct smc_link *link;
12504ed75de5SKarsten Graul 	u8 err_mask = 0;
12514ed75de5SKarsten Graul 	int i, max;
12524ed75de5SKarsten Graul 
1253218b24feSKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
1254218b24feSKarsten Graul 	llc = &qentry->msg.delete_rkey;
1255218b24feSKarsten Graul 	link = qentry->link;
1256218b24feSKarsten Graul 
12574ed75de5SKarsten Graul 	max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
12584ed75de5SKarsten Graul 	for (i = 0; i < max; i++) {
1259387707fdSKarsten Graul 		if (smc_rtoken_delete(link, llc->rkey[i]))
12604ed75de5SKarsten Graul 			err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
12614ed75de5SKarsten Graul 	}
12624ed75de5SKarsten Graul 	if (err_mask) {
12634ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
12644ed75de5SKarsten Graul 		llc->err_mask = err_mask;
12654ed75de5SKarsten Graul 	}
1266218b24feSKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
1267218b24feSKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
1268218b24feSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
1269218b24feSKarsten Graul }
12704ed75de5SKarsten Graul 
12716c8968c4SKarsten Graul /* flush the llc event queue */
127200a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr)
12739bf9abeaSUrsula Braun {
12746c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry, *q;
12759bf9abeaSUrsula Braun 
12766c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
12776c8968c4SKarsten Graul 	list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
12786c8968c4SKarsten Graul 		list_del_init(&qentry->list);
12796c8968c4SKarsten Graul 		kfree(qentry);
12806c8968c4SKarsten Graul 	}
12816c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
12826c8968c4SKarsten Graul }
12836c8968c4SKarsten Graul 
12846c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
12856c8968c4SKarsten Graul {
12866c8968c4SKarsten Graul 	union smc_llc_msg *llc = &qentry->msg;
12876c8968c4SKarsten Graul 	struct smc_link *link = qentry->link;
12880fb0b02bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
12896c8968c4SKarsten Graul 
1290d854fcbfSKarsten Graul 	if (!smc_link_usable(link))
12916c8968c4SKarsten Graul 		goto out;
1292313164daSKarsten Graul 
1293313164daSKarsten Graul 	switch (llc->raw.hdr.common.type) {
1294313164daSKarsten Graul 	case SMC_LLC_TEST_LINK:
129556e8091cSKarsten Graul 		llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP;
129656e8091cSKarsten Graul 		smc_llc_send_message(link, llc);
1297313164daSKarsten Graul 		break;
129852bedf37SKarsten Graul 	case SMC_LLC_ADD_LINK:
12990fb0b02bSKarsten Graul 		if (list_empty(&lgr->list))
13000fb0b02bSKarsten Graul 			goto out;	/* lgr is terminating */
13010fb0b02bSKarsten Graul 		if (lgr->role == SMC_CLNT) {
13020fb0b02bSKarsten Graul 			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK) {
13030fb0b02bSKarsten Graul 				/* a flow is waiting for this message */
13040fb0b02bSKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
13050fb0b02bSKarsten Graul 							qentry);
13060fb0b02bSKarsten Graul 				wake_up_interruptible(&lgr->llc_waiter);
13070fb0b02bSKarsten Graul 			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
13080fb0b02bSKarsten Graul 						      qentry)) {
1309b45e7f98SKarsten Graul 				schedule_work(&lgr->llc_add_link_work);
13100fb0b02bSKarsten Graul 			}
13110fb0b02bSKarsten Graul 		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
13120fb0b02bSKarsten Graul 			/* as smc server, handle client suggestion */
1313b45e7f98SKarsten Graul 			schedule_work(&lgr->llc_add_link_work);
13140fb0b02bSKarsten Graul 		}
13150fb0b02bSKarsten Graul 		return;
13160fb0b02bSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
131787f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
13180fb0b02bSKarsten Graul 		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
13190fb0b02bSKarsten Graul 			/* a flow is waiting for this message */
13200fb0b02bSKarsten Graul 			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
13210fb0b02bSKarsten Graul 			wake_up_interruptible(&lgr->llc_waiter);
13220fb0b02bSKarsten Graul 			return;
13230fb0b02bSKarsten Graul 		}
132452bedf37SKarsten Graul 		break;
132552bedf37SKarsten Graul 	case SMC_LLC_DELETE_LINK:
13269ec6bf19SKarsten Graul 		if (lgr->role == SMC_CLNT) {
13279ec6bf19SKarsten Graul 			/* server requests to delete this link, send response */
13289ec6bf19SKarsten Graul 			if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
13299ec6bf19SKarsten Graul 				/* DEL LINK REQ during ADD LINK SEQ */
13309ec6bf19SKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
13319ec6bf19SKarsten Graul 							qentry);
13329ec6bf19SKarsten Graul 				wake_up_interruptible(&lgr->llc_waiter);
13339ec6bf19SKarsten Graul 			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
13349ec6bf19SKarsten Graul 						      qentry)) {
13359ec6bf19SKarsten Graul 				schedule_work(&lgr->llc_del_link_work);
13369ec6bf19SKarsten Graul 			}
13379ec6bf19SKarsten Graul 		} else {
13389ec6bf19SKarsten Graul 			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK &&
13399ec6bf19SKarsten Graul 			    !lgr->llc_flow_lcl.qentry) {
13409ec6bf19SKarsten Graul 				/* DEL LINK REQ during ADD LINK SEQ */
13419ec6bf19SKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
13429ec6bf19SKarsten Graul 							qentry);
13439ec6bf19SKarsten Graul 				wake_up_interruptible(&lgr->llc_waiter);
13449ec6bf19SKarsten Graul 			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
13459ec6bf19SKarsten Graul 						      qentry)) {
13469ec6bf19SKarsten Graul 				schedule_work(&lgr->llc_del_link_work);
13479ec6bf19SKarsten Graul 			}
13489ec6bf19SKarsten Graul 		}
13499ec6bf19SKarsten Graul 		return;
13504ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
13513bc67e09SKarsten Graul 		/* new request from remote, assign to remote flow */
13523bc67e09SKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
13533bc67e09SKarsten Graul 			/* process here, does not wait for more llc msgs */
13543bc67e09SKarsten Graul 			smc_llc_rmt_conf_rkey(lgr);
13553bc67e09SKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
13563bc67e09SKarsten Graul 		}
13573bc67e09SKarsten Graul 		return;
13584ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
135942d18accSKarsten Graul 		/* not used because max links is 3, and 3 rkeys fit into
136042d18accSKarsten Graul 		 * one CONFIRM_RKEY message
136142d18accSKarsten Graul 		 */
13624ed75de5SKarsten Graul 		break;
13634ed75de5SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
1364218b24feSKarsten Graul 		/* new request from remote, assign to remote flow */
1365218b24feSKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
1366218b24feSKarsten Graul 			/* process here, does not wait for more llc msgs */
1367218b24feSKarsten Graul 			smc_llc_rmt_delete_rkey(lgr);
1368218b24feSKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
1369218b24feSKarsten Graul 		}
1370218b24feSKarsten Graul 		return;
1371313164daSKarsten Graul 	}
13726c8968c4SKarsten Graul out:
13736c8968c4SKarsten Graul 	kfree(qentry);
13746c8968c4SKarsten Graul }
13756c8968c4SKarsten Graul 
13766c8968c4SKarsten Graul /* worker to process llc messages on the event queue */
13776c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work)
13786c8968c4SKarsten Graul {
13796c8968c4SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
13806c8968c4SKarsten Graul 						  llc_event_work);
13816c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
13826c8968c4SKarsten Graul 
1383555da9afSKarsten Graul 	if (!lgr->llc_flow_lcl.type && lgr->delayed_event) {
1384555da9afSKarsten Graul 		if (smc_link_usable(lgr->delayed_event->link)) {
1385555da9afSKarsten Graul 			smc_llc_event_handler(lgr->delayed_event);
1386555da9afSKarsten Graul 		} else {
1387555da9afSKarsten Graul 			qentry = lgr->delayed_event;
1388555da9afSKarsten Graul 			lgr->delayed_event = NULL;
1389555da9afSKarsten Graul 			kfree(qentry);
1390555da9afSKarsten Graul 		}
1391555da9afSKarsten Graul 	}
1392555da9afSKarsten Graul 
13936c8968c4SKarsten Graul again:
13946c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
13956c8968c4SKarsten Graul 	if (!list_empty(&lgr->llc_event_q)) {
13966c8968c4SKarsten Graul 		qentry = list_first_entry(&lgr->llc_event_q,
13976c8968c4SKarsten Graul 					  struct smc_llc_qentry, list);
13986c8968c4SKarsten Graul 		list_del_init(&qentry->list);
13996c8968c4SKarsten Graul 		spin_unlock_bh(&lgr->llc_event_q_lock);
14006c8968c4SKarsten Graul 		smc_llc_event_handler(qentry);
14016c8968c4SKarsten Graul 		goto again;
14026c8968c4SKarsten Graul 	}
14036c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
14046c8968c4SKarsten Graul }
14056c8968c4SKarsten Graul 
1406ef79d439SKarsten Graul /* process llc responses in tasklet context */
1407a6688d91SKarsten Graul static void smc_llc_rx_response(struct smc_link *link,
1408a6688d91SKarsten Graul 				struct smc_llc_qentry *qentry)
1409ef79d439SKarsten Graul {
1410a6688d91SKarsten Graul 	u8 llc_type = qentry->msg.raw.hdr.common.type;
1411ef79d439SKarsten Graul 
1412a6688d91SKarsten Graul 	switch (llc_type) {
1413ef79d439SKarsten Graul 	case SMC_LLC_TEST_LINK:
1414ef79d439SKarsten Graul 		if (link->state == SMC_LNK_ACTIVE)
1415ef79d439SKarsten Graul 			complete(&link->llc_testlink_resp);
1416ef79d439SKarsten Graul 		break;
1417ef79d439SKarsten Graul 	case SMC_LLC_ADD_LINK:
14189ec6bf19SKarsten Graul 	case SMC_LLC_DELETE_LINK:
14194667bb4aSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
142087f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
14213d88a21bSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
14226d74c3a8SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
14234667bb4aSKarsten Graul 		/* assign responses to the local flow, we requested them */
14244667bb4aSKarsten Graul 		smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry);
14254667bb4aSKarsten Graul 		wake_up_interruptible(&link->lgr->llc_waiter);
14264667bb4aSKarsten Graul 		return;
1427ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
142842d18accSKarsten Graul 		/* not used because max links is 3 */
1429ef79d439SKarsten Graul 		break;
1430ef79d439SKarsten Graul 	}
1431a6688d91SKarsten Graul 	kfree(qentry);
1432ef79d439SKarsten Graul }
1433ef79d439SKarsten Graul 
1434a6688d91SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
14356c8968c4SKarsten Graul {
14366c8968c4SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
14376c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
14386c8968c4SKarsten Graul 	unsigned long flags;
14396c8968c4SKarsten Graul 
14406c8968c4SKarsten Graul 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
14416c8968c4SKarsten Graul 	if (!qentry)
14426c8968c4SKarsten Graul 		return;
14436c8968c4SKarsten Graul 	qentry->link = link;
14446c8968c4SKarsten Graul 	INIT_LIST_HEAD(&qentry->list);
14456c8968c4SKarsten Graul 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
1446a6688d91SKarsten Graul 
1447a6688d91SKarsten Graul 	/* process responses immediately */
1448a6688d91SKarsten Graul 	if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) {
1449a6688d91SKarsten Graul 		smc_llc_rx_response(link, qentry);
1450a6688d91SKarsten Graul 		return;
1451a6688d91SKarsten Graul 	}
1452a6688d91SKarsten Graul 
1453a6688d91SKarsten Graul 	/* add requests to event queue */
14546c8968c4SKarsten Graul 	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
14556c8968c4SKarsten Graul 	list_add_tail(&qentry->list, &lgr->llc_event_q);
14566c8968c4SKarsten Graul 	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
14576c8968c4SKarsten Graul 	schedule_work(&link->lgr->llc_event_work);
14589bf9abeaSUrsula Braun }
14599bf9abeaSUrsula Braun 
1460a6688d91SKarsten Graul /* copy received msg and add it to the event queue */
1461a6688d91SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
1462a6688d91SKarsten Graul {
1463a6688d91SKarsten Graul 	struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
1464a6688d91SKarsten Graul 	union smc_llc_msg *llc = buf;
1465a6688d91SKarsten Graul 
1466a6688d91SKarsten Graul 	if (wc->byte_len < sizeof(*llc))
1467a6688d91SKarsten Graul 		return; /* short message */
1468a6688d91SKarsten Graul 	if (llc->raw.hdr.length != sizeof(*llc))
1469a6688d91SKarsten Graul 		return; /* invalid message */
1470a6688d91SKarsten Graul 
1471a6688d91SKarsten Graul 	smc_llc_enqueue(link, llc);
1472a6688d91SKarsten Graul }
1473a6688d91SKarsten Graul 
147444aa81ceSKarsten Graul /***************************** worker, utils *********************************/
1475877ae5beSKarsten Graul 
1476877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work)
1477877ae5beSKarsten Graul {
1478877ae5beSKarsten Graul 	struct smc_link *link = container_of(to_delayed_work(work),
1479877ae5beSKarsten Graul 					     struct smc_link, llc_testlink_wrk);
1480877ae5beSKarsten Graul 	unsigned long next_interval;
1481877ae5beSKarsten Graul 	unsigned long expire_time;
1482877ae5beSKarsten Graul 	u8 user_data[16] = { 0 };
1483877ae5beSKarsten Graul 	int rc;
1484877ae5beSKarsten Graul 
1485877ae5beSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
1486877ae5beSKarsten Graul 		return;		/* don't reschedule worker */
1487877ae5beSKarsten Graul 	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
1488877ae5beSKarsten Graul 	if (time_is_after_jiffies(expire_time)) {
1489877ae5beSKarsten Graul 		next_interval = expire_time - jiffies;
1490877ae5beSKarsten Graul 		goto out;
1491877ae5beSKarsten Graul 	}
1492877ae5beSKarsten Graul 	reinit_completion(&link->llc_testlink_resp);
1493d97935faSKarsten Graul 	smc_llc_send_test_link(link, user_data);
1494877ae5beSKarsten Graul 	/* receive TEST LINK response over RoCE fabric */
1495877ae5beSKarsten Graul 	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
1496877ae5beSKarsten Graul 						       SMC_LLC_WAIT_TIME);
14971020e1efSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
14981020e1efSKarsten Graul 		return;		/* link state changed */
1499877ae5beSKarsten Graul 	if (rc <= 0) {
150087523930SKarsten Graul 		smcr_link_down_cond_sched(link);
1501877ae5beSKarsten Graul 		return;
1502877ae5beSKarsten Graul 	}
1503877ae5beSKarsten Graul 	next_interval = link->llc_testlink_time;
1504877ae5beSKarsten Graul out:
15051020e1efSKarsten Graul 	schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
1506877ae5beSKarsten Graul }
1507877ae5beSKarsten Graul 
150800a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
150900a049cfSKarsten Graul {
151000a049cfSKarsten Graul 	struct net *net = sock_net(smc->clcsock->sk);
151100a049cfSKarsten Graul 
151200a049cfSKarsten Graul 	INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
1513b45e7f98SKarsten Graul 	INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work);
15149ec6bf19SKarsten Graul 	INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work);
151500a049cfSKarsten Graul 	INIT_LIST_HEAD(&lgr->llc_event_q);
151600a049cfSKarsten Graul 	spin_lock_init(&lgr->llc_event_q_lock);
1517555da9afSKarsten Graul 	spin_lock_init(&lgr->llc_flow_lock);
1518555da9afSKarsten Graul 	init_waitqueue_head(&lgr->llc_waiter);
1519d5500667SKarsten Graul 	mutex_init(&lgr->llc_conf_mutex);
152000a049cfSKarsten Graul 	lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;
152100a049cfSKarsten Graul }
152200a049cfSKarsten Graul 
152300a049cfSKarsten Graul /* called after lgr was removed from lgr_list */
152400a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr)
152500a049cfSKarsten Graul {
152600a049cfSKarsten Graul 	smc_llc_event_flush(lgr);
1527555da9afSKarsten Graul 	wake_up_interruptible_all(&lgr->llc_waiter);
152800a049cfSKarsten Graul 	cancel_work_sync(&lgr->llc_event_work);
1529b45e7f98SKarsten Graul 	cancel_work_sync(&lgr->llc_add_link_work);
15309ec6bf19SKarsten Graul 	cancel_work_sync(&lgr->llc_del_link_work);
1531555da9afSKarsten Graul 	if (lgr->delayed_event) {
1532555da9afSKarsten Graul 		kfree(lgr->delayed_event);
1533555da9afSKarsten Graul 		lgr->delayed_event = NULL;
1534555da9afSKarsten Graul 	}
153500a049cfSKarsten Graul }
153600a049cfSKarsten Graul 
15372a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link)
1538877ae5beSKarsten Graul {
1539877ae5beSKarsten Graul 	init_completion(&link->llc_testlink_resp);
1540877ae5beSKarsten Graul 	INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
15412a4c57a9SKarsten Graul 	return 0;
1542b32cf4abSKarsten Graul }
1543b32cf4abSKarsten Graul 
154400a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link)
1545b32cf4abSKarsten Graul {
1546877ae5beSKarsten Graul 	link->state = SMC_LNK_ACTIVE;
154700a049cfSKarsten Graul 	if (link->lgr->llc_testlink_time) {
154800a049cfSKarsten Graul 		link->llc_testlink_time = link->lgr->llc_testlink_time * HZ;
15491020e1efSKarsten Graul 		schedule_delayed_work(&link->llc_testlink_wrk,
1550877ae5beSKarsten Graul 				      link->llc_testlink_time);
1551877ae5beSKarsten Graul 	}
1552877ae5beSKarsten Graul }
1553877ae5beSKarsten Graul 
1554877ae5beSKarsten Graul /* called in worker context */
15552a4c57a9SKarsten Graul void smc_llc_link_clear(struct smc_link *link)
1556877ae5beSKarsten Graul {
15572140ac26SKarsten Graul 	complete(&link->llc_testlink_resp);
15582140ac26SKarsten Graul 	cancel_delayed_work_sync(&link->llc_testlink_wrk);
15592140ac26SKarsten Graul 	smc_wr_wakeup_reg_wait(link);
15602140ac26SKarsten Graul 	smc_wr_wakeup_tx_wait(link);
1561877ae5beSKarsten Graul }
1562877ae5beSKarsten Graul 
15633d88a21bSKarsten Graul /* register a new rtoken at the remote peer (for all links) */
15643d88a21bSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *send_link,
156544aa81ceSKarsten Graul 			    struct smc_buf_desc *rmb_desc)
156644aa81ceSKarsten Graul {
15673d88a21bSKarsten Graul 	struct smc_link_group *lgr = send_link->lgr;
15683d88a21bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
15693d88a21bSKarsten Graul 	int rc = 0;
157044aa81ceSKarsten Graul 
15713d88a21bSKarsten Graul 	rc = smc_llc_send_confirm_rkey(send_link, rmb_desc);
15723d88a21bSKarsten Graul 	if (rc)
15733d88a21bSKarsten Graul 		goto out;
157444aa81ceSKarsten Graul 	/* receive CONFIRM RKEY response from server over RoCE fabric */
15753d88a21bSKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
15763d88a21bSKarsten Graul 			      SMC_LLC_CONFIRM_RKEY);
15773d88a21bSKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
15783d88a21bSKarsten Graul 		rc = -EFAULT;
15793d88a21bSKarsten Graul out:
15803d88a21bSKarsten Graul 	if (qentry)
15813d88a21bSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
15823d88a21bSKarsten Graul 	return rc;
158344aa81ceSKarsten Graul }
158444aa81ceSKarsten Graul 
158560e03c62SKarsten Graul /* unregister an rtoken at the remote peer */
15866d74c3a8SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link_group *lgr,
158760e03c62SKarsten Graul 			   struct smc_buf_desc *rmb_desc)
158860e03c62SKarsten Graul {
15896d74c3a8SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
15906d74c3a8SKarsten Graul 	struct smc_link *send_link;
15910b29ec64SUrsula Braun 	int rc = 0;
159260e03c62SKarsten Graul 
15936d74c3a8SKarsten Graul 	send_link = smc_llc_usable_link(lgr);
15946d74c3a8SKarsten Graul 	if (!send_link)
15956d74c3a8SKarsten Graul 		return -ENOLINK;
15966d74c3a8SKarsten Graul 
15976d74c3a8SKarsten Graul 	/* protected by llc_flow control */
15986d74c3a8SKarsten Graul 	rc = smc_llc_send_delete_rkey(send_link, rmb_desc);
159960e03c62SKarsten Graul 	if (rc)
160060e03c62SKarsten Graul 		goto out;
160160e03c62SKarsten Graul 	/* receive DELETE RKEY response from server over RoCE fabric */
16026d74c3a8SKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
16036d74c3a8SKarsten Graul 			      SMC_LLC_DELETE_RKEY);
16046d74c3a8SKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
160560e03c62SKarsten Graul 		rc = -EFAULT;
160660e03c62SKarsten Graul out:
16076d74c3a8SKarsten Graul 	if (qentry)
16086d74c3a8SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
160960e03c62SKarsten Graul 	return rc;
161060e03c62SKarsten Graul }
161160e03c62SKarsten Graul 
161292334cfcSKarsten Graul /* evaluate confirm link request or response */
161392334cfcSKarsten Graul int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry,
161492334cfcSKarsten Graul 			   enum smc_llc_reqresp type)
161592334cfcSKarsten Graul {
161692334cfcSKarsten Graul 	if (type == SMC_LLC_REQ)	/* SMC server assigns link_id */
161792334cfcSKarsten Graul 		qentry->link->link_id = qentry->msg.confirm_link.link_num;
161892334cfcSKarsten Graul 	if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
161992334cfcSKarsten Graul 		return -ENOTSUPP;
162092334cfcSKarsten Graul 	return 0;
162192334cfcSKarsten Graul }
162292334cfcSKarsten Graul 
16239bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/
16249bf9abeaSUrsula Braun 
16259bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
16269bf9abeaSUrsula Braun 	{
16279bf9abeaSUrsula Braun 		.handler	= smc_llc_rx_handler,
16289bf9abeaSUrsula Braun 		.type		= SMC_LLC_CONFIRM_LINK
16299bf9abeaSUrsula Braun 	},
16309bf9abeaSUrsula Braun 	{
1631313164daSKarsten Graul 		.handler	= smc_llc_rx_handler,
1632313164daSKarsten Graul 		.type		= SMC_LLC_TEST_LINK
1633313164daSKarsten Graul 	},
1634313164daSKarsten Graul 	{
16354ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
163652bedf37SKarsten Graul 		.type		= SMC_LLC_ADD_LINK
163752bedf37SKarsten Graul 	},
163852bedf37SKarsten Graul 	{
163952bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
164087f88cdaSKarsten Graul 		.type		= SMC_LLC_ADD_LINK_CONT
164187f88cdaSKarsten Graul 	},
164287f88cdaSKarsten Graul 	{
164387f88cdaSKarsten Graul 		.handler	= smc_llc_rx_handler,
164452bedf37SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK
164552bedf37SKarsten Graul 	},
164652bedf37SKarsten Graul 	{
164752bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
16484ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY
16494ed75de5SKarsten Graul 	},
16504ed75de5SKarsten Graul 	{
16514ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
16524ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_CONT
16534ed75de5SKarsten Graul 	},
16544ed75de5SKarsten Graul 	{
16554ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
16564ed75de5SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY
16574ed75de5SKarsten Graul 	},
16584ed75de5SKarsten Graul 	{
16599bf9abeaSUrsula Braun 		.handler	= NULL,
16609bf9abeaSUrsula Braun 	}
16619bf9abeaSUrsula Braun };
16629bf9abeaSUrsula Braun 
16639bf9abeaSUrsula Braun int __init smc_llc_init(void)
16649bf9abeaSUrsula Braun {
16659bf9abeaSUrsula Braun 	struct smc_wr_rx_handler *handler;
16669bf9abeaSUrsula Braun 	int rc = 0;
16679bf9abeaSUrsula Braun 
16689bf9abeaSUrsula Braun 	for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
16699bf9abeaSUrsula Braun 		INIT_HLIST_NODE(&handler->list);
16709bf9abeaSUrsula Braun 		rc = smc_wr_rx_register_handler(handler);
16719bf9abeaSUrsula Braun 		if (rc)
16729bf9abeaSUrsula Braun 			break;
16739bf9abeaSUrsula Braun 	}
16749bf9abeaSUrsula Braun 	return rc;
16759bf9abeaSUrsula Braun }
1676