xref: /linux/net/smc/smc_llc.c (revision 95f7f3e7dc6bd2e735cb5de11734ea2222b1e05a)
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 
1624dadd151SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc);
1634dadd151SKarsten Graul 
164555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
165555da9afSKarsten Graul {
166555da9afSKarsten Graul 	struct smc_llc_qentry *qentry = flow->qentry;
167555da9afSKarsten Graul 
168555da9afSKarsten Graul 	flow->qentry = NULL;
169555da9afSKarsten Graul 	return qentry;
170555da9afSKarsten Graul }
171555da9afSKarsten Graul 
172555da9afSKarsten Graul void smc_llc_flow_qentry_del(struct smc_llc_flow *flow)
173555da9afSKarsten Graul {
174555da9afSKarsten Graul 	struct smc_llc_qentry *qentry;
175555da9afSKarsten Graul 
176555da9afSKarsten Graul 	if (flow->qentry) {
177555da9afSKarsten Graul 		qentry = flow->qentry;
178555da9afSKarsten Graul 		flow->qentry = NULL;
179555da9afSKarsten Graul 		kfree(qentry);
180555da9afSKarsten Graul 	}
181555da9afSKarsten Graul }
182555da9afSKarsten Graul 
183555da9afSKarsten Graul static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow,
184555da9afSKarsten Graul 					   struct smc_llc_qentry *qentry)
185555da9afSKarsten Graul {
186555da9afSKarsten Graul 	flow->qentry = qentry;
187555da9afSKarsten Graul }
188555da9afSKarsten Graul 
1896778a6beSKarsten Graul static void smc_llc_flow_parallel(struct smc_link_group *lgr, u8 flow_type,
1906778a6beSKarsten Graul 				  struct smc_llc_qentry *qentry)
1916778a6beSKarsten Graul {
1926778a6beSKarsten Graul 	u8 msg_type = qentry->msg.raw.hdr.common.type;
1936778a6beSKarsten Graul 
1946778a6beSKarsten Graul 	if ((msg_type == SMC_LLC_ADD_LINK || msg_type == SMC_LLC_DELETE_LINK) &&
1956778a6beSKarsten Graul 	    flow_type != msg_type && !lgr->delayed_event) {
1966778a6beSKarsten Graul 		lgr->delayed_event = qentry;
1976778a6beSKarsten Graul 		return;
1986778a6beSKarsten Graul 	}
1996778a6beSKarsten Graul 	/* drop parallel or already-in-progress llc requests */
2006778a6beSKarsten Graul 	if (flow_type != msg_type)
2016778a6beSKarsten Graul 		pr_warn_once("smc: SMC-R lg %*phN dropped parallel "
2026778a6beSKarsten Graul 			     "LLC msg: msg %d flow %d role %d\n",
2036778a6beSKarsten Graul 			     SMC_LGR_ID_SIZE, &lgr->id,
2046778a6beSKarsten Graul 			     qentry->msg.raw.hdr.common.type,
2056778a6beSKarsten Graul 			     flow_type, lgr->role);
2066778a6beSKarsten Graul 	kfree(qentry);
2076778a6beSKarsten Graul }
2086778a6beSKarsten Graul 
209555da9afSKarsten Graul /* try to start a new llc flow, initiated by an incoming llc msg */
210555da9afSKarsten Graul static bool smc_llc_flow_start(struct smc_llc_flow *flow,
211555da9afSKarsten Graul 			       struct smc_llc_qentry *qentry)
212555da9afSKarsten Graul {
213555da9afSKarsten Graul 	struct smc_link_group *lgr = qentry->link->lgr;
214555da9afSKarsten Graul 
215555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
216555da9afSKarsten Graul 	if (flow->type) {
217555da9afSKarsten Graul 		/* a flow is already active */
2186778a6beSKarsten Graul 		smc_llc_flow_parallel(lgr, flow->type, qentry);
219555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
220555da9afSKarsten Graul 		return false;
221555da9afSKarsten Graul 	}
222555da9afSKarsten Graul 	switch (qentry->msg.raw.hdr.common.type) {
223555da9afSKarsten Graul 	case SMC_LLC_ADD_LINK:
224555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_ADD_LINK;
225555da9afSKarsten Graul 		break;
226555da9afSKarsten Graul 	case SMC_LLC_DELETE_LINK:
227555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_DEL_LINK;
228555da9afSKarsten Graul 		break;
229555da9afSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
230555da9afSKarsten Graul 	case SMC_LLC_DELETE_RKEY:
231555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_RKEY;
232555da9afSKarsten Graul 		break;
233555da9afSKarsten Graul 	default:
234555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_NONE;
235555da9afSKarsten Graul 	}
236555da9afSKarsten Graul 	smc_llc_flow_qentry_set(flow, qentry);
2376778a6beSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
238555da9afSKarsten Graul 	return true;
239555da9afSKarsten Graul }
240555da9afSKarsten Graul 
241555da9afSKarsten Graul /* start a new local llc flow, wait till current flow finished */
242555da9afSKarsten Graul int smc_llc_flow_initiate(struct smc_link_group *lgr,
243555da9afSKarsten Graul 			  enum smc_llc_flowtype type)
244555da9afSKarsten Graul {
245555da9afSKarsten Graul 	enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE;
246555da9afSKarsten Graul 	int rc;
247555da9afSKarsten Graul 
248555da9afSKarsten Graul 	/* all flows except confirm_rkey and delete_rkey are exclusive,
249555da9afSKarsten Graul 	 * confirm/delete rkey flows can run concurrently (local and remote)
250555da9afSKarsten Graul 	 */
251555da9afSKarsten Graul 	if (type == SMC_LLC_FLOW_RKEY)
252555da9afSKarsten Graul 		allowed_remote = SMC_LLC_FLOW_RKEY;
253555da9afSKarsten Graul again:
254555da9afSKarsten Graul 	if (list_empty(&lgr->list))
255555da9afSKarsten Graul 		return -ENODEV;
256555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
257555da9afSKarsten Graul 	if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
258555da9afSKarsten Graul 	    (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
259555da9afSKarsten Graul 	     lgr->llc_flow_rmt.type == allowed_remote)) {
260555da9afSKarsten Graul 		lgr->llc_flow_lcl.type = type;
261555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
262555da9afSKarsten Graul 		return 0;
263555da9afSKarsten Graul 	}
264555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
2656778a6beSKarsten Graul 	rc = wait_event_timeout(lgr->llc_flow_waiter, (list_empty(&lgr->list) ||
266555da9afSKarsten Graul 				(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
267555da9afSKarsten Graul 				 (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
2686778a6beSKarsten Graul 				  lgr->llc_flow_rmt.type == allowed_remote))),
2696778a6beSKarsten Graul 				SMC_LLC_WAIT_TIME * 10);
270555da9afSKarsten Graul 	if (!rc)
271555da9afSKarsten Graul 		return -ETIMEDOUT;
272555da9afSKarsten Graul 	goto again;
273555da9afSKarsten Graul }
274555da9afSKarsten Graul 
275555da9afSKarsten Graul /* finish the current llc flow */
276555da9afSKarsten Graul void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)
277555da9afSKarsten Graul {
278555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
279555da9afSKarsten Graul 	memset(flow, 0, sizeof(*flow));
280555da9afSKarsten Graul 	flow->type = SMC_LLC_FLOW_NONE;
281555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
282555da9afSKarsten Graul 	if (!list_empty(&lgr->list) && lgr->delayed_event &&
283555da9afSKarsten Graul 	    flow == &lgr->llc_flow_lcl)
284555da9afSKarsten Graul 		schedule_work(&lgr->llc_event_work);
285555da9afSKarsten Graul 	else
2866778a6beSKarsten Graul 		wake_up(&lgr->llc_flow_waiter);
287555da9afSKarsten Graul }
288555da9afSKarsten Graul 
289555da9afSKarsten Graul /* lnk is optional and used for early wakeup when link goes down, useful in
290555da9afSKarsten Graul  * cases where we wait for a response on the link after we sent a request
291555da9afSKarsten Graul  */
292555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr,
293555da9afSKarsten Graul 				    struct smc_link *lnk,
294555da9afSKarsten Graul 				    int time_out, u8 exp_msg)
295555da9afSKarsten Graul {
296555da9afSKarsten Graul 	struct smc_llc_flow *flow = &lgr->llc_flow_lcl;
2976778a6beSKarsten Graul 	u8 rcv_msg;
298555da9afSKarsten Graul 
2996778a6beSKarsten Graul 	wait_event_timeout(lgr->llc_msg_waiter,
300555da9afSKarsten Graul 			   (flow->qentry ||
301555da9afSKarsten Graul 			    (lnk && !smc_link_usable(lnk)) ||
302555da9afSKarsten Graul 			    list_empty(&lgr->list)),
303555da9afSKarsten Graul 			   time_out);
304555da9afSKarsten Graul 	if (!flow->qentry ||
305555da9afSKarsten Graul 	    (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) {
306555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
307555da9afSKarsten Graul 		goto out;
308555da9afSKarsten Graul 	}
3096778a6beSKarsten Graul 	rcv_msg = flow->qentry->msg.raw.hdr.common.type;
3106778a6beSKarsten Graul 	if (exp_msg && rcv_msg != exp_msg) {
311555da9afSKarsten Graul 		if (exp_msg == SMC_LLC_ADD_LINK &&
3126778a6beSKarsten Graul 		    rcv_msg == SMC_LLC_DELETE_LINK) {
313555da9afSKarsten Graul 			/* flow_start will delay the unexpected msg */
314555da9afSKarsten Graul 			smc_llc_flow_start(&lgr->llc_flow_lcl,
315555da9afSKarsten Graul 					   smc_llc_flow_qentry_clr(flow));
316555da9afSKarsten Graul 			return NULL;
317555da9afSKarsten Graul 		}
3186778a6beSKarsten Graul 		pr_warn_once("smc: SMC-R lg %*phN dropped unexpected LLC msg: "
3196778a6beSKarsten Graul 			     "msg %d exp %d flow %d role %d flags %x\n",
3206778a6beSKarsten Graul 			     SMC_LGR_ID_SIZE, &lgr->id, rcv_msg, exp_msg,
3216778a6beSKarsten Graul 			     flow->type, lgr->role,
3226778a6beSKarsten Graul 			     flow->qentry->msg.raw.hdr.flags);
323555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
324555da9afSKarsten Graul 	}
325555da9afSKarsten Graul out:
326555da9afSKarsten Graul 	return flow->qentry;
327555da9afSKarsten Graul }
328555da9afSKarsten Graul 
3299bf9abeaSUrsula Braun /********************************** send *************************************/
3309bf9abeaSUrsula Braun 
3319bf9abeaSUrsula Braun struct smc_llc_tx_pend {
3329bf9abeaSUrsula Braun };
3339bf9abeaSUrsula Braun 
3349bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */
3359bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend,
3369bf9abeaSUrsula Braun 			       struct smc_link *link,
3379bf9abeaSUrsula Braun 			       enum ib_wc_status wc_status)
3389bf9abeaSUrsula Braun {
3399bf9abeaSUrsula Braun 	/* future work: handle wc_status error for recovery and failover */
3409bf9abeaSUrsula Braun }
3419bf9abeaSUrsula Braun 
3429bf9abeaSUrsula Braun /**
3439bf9abeaSUrsula Braun  * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits
3449bf9abeaSUrsula Braun  * @link: Pointer to SMC link used for sending LLC control message.
3459bf9abeaSUrsula Braun  * @wr_buf: Out variable returning pointer to work request payload buffer.
3469bf9abeaSUrsula Braun  * @pend: Out variable returning pointer to private pending WR tracking.
3479bf9abeaSUrsula Braun  *	  It's the context the transmit complete handler will get.
3489bf9abeaSUrsula Braun  *
3499bf9abeaSUrsula Braun  * Reserves and pre-fills an entry for a pending work request send/tx.
3509bf9abeaSUrsula Braun  * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx.
3519bf9abeaSUrsula Braun  * Can sleep due to smc_get_ctrl_buf (if not in softirq context).
3529bf9abeaSUrsula Braun  *
3539bf9abeaSUrsula Braun  * Return: 0 on success, otherwise an error value.
3549bf9abeaSUrsula Braun  */
3559bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link,
3569bf9abeaSUrsula Braun 				    struct smc_wr_buf **wr_buf,
3579bf9abeaSUrsula Braun 				    struct smc_wr_tx_pend_priv **pend)
3589bf9abeaSUrsula Braun {
3599bf9abeaSUrsula Braun 	int rc;
3609bf9abeaSUrsula Braun 
361ad6f317fSUrsula Braun 	rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL,
362ad6f317fSUrsula Braun 				     pend);
3639bf9abeaSUrsula Braun 	if (rc < 0)
3649bf9abeaSUrsula Braun 		return rc;
3659bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3669bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE,
3679bf9abeaSUrsula Braun 		"must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)");
3689bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3699bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE,
3709bf9abeaSUrsula 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()");
3719bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3729bf9abeaSUrsula Braun 		sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
3739bf9abeaSUrsula Braun 		"must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)");
3749bf9abeaSUrsula Braun 	return 0;
3759bf9abeaSUrsula Braun }
3769bf9abeaSUrsula Braun 
3779bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */
378947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link,
3799bf9abeaSUrsula Braun 			      enum smc_llc_reqresp reqresp)
3809bf9abeaSUrsula Braun {
3819bf9abeaSUrsula Braun 	struct smc_llc_msg_confirm_link *confllc;
3829bf9abeaSUrsula Braun 	struct smc_wr_tx_pend_priv *pend;
3839bf9abeaSUrsula Braun 	struct smc_wr_buf *wr_buf;
3849bf9abeaSUrsula Braun 	int rc;
3859bf9abeaSUrsula Braun 
386*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
387*95f7f3e7SKarsten Graul 		return -ENOLINK;
3889bf9abeaSUrsula Braun 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
3899bf9abeaSUrsula Braun 	if (rc)
390*95f7f3e7SKarsten Graul 		goto put_out;
3919bf9abeaSUrsula Braun 	confllc = (struct smc_llc_msg_confirm_link *)wr_buf;
3929bf9abeaSUrsula Braun 	memset(confllc, 0, sizeof(*confllc));
3939bf9abeaSUrsula Braun 	confllc->hd.common.type = SMC_LLC_CONFIRM_LINK;
3949bf9abeaSUrsula Braun 	confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link);
39575d320d6SKarsten Graul 	confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC;
3969bf9abeaSUrsula Braun 	if (reqresp == SMC_LLC_RESP)
3979bf9abeaSUrsula Braun 		confllc->hd.flags |= SMC_LLC_FLAG_RESP;
398947541f3SUrsula Braun 	memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1],
399947541f3SUrsula Braun 	       ETH_ALEN);
4007005ada6SUrsula Braun 	memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE);
4019bf9abeaSUrsula Braun 	hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
4022be922f3SKarsten Graul 	confllc->link_num = link->link_id;
40345fa8da0SKarsten Graul 	memcpy(confllc->link_uid, link->link_uid, SMC_LGR_ID_SIZE);
404b1570a87SKarsten Graul 	confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS;
40552bedf37SKarsten Graul 	/* send llc message */
40652bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
407*95f7f3e7SKarsten Graul put_out:
408*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
40952bedf37SKarsten Graul 	return rc;
41052bedf37SKarsten Graul }
41152bedf37SKarsten Graul 
41244aa81ceSKarsten Graul /* send LLC confirm rkey request */
4133d88a21bSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *send_link,
41444aa81ceSKarsten Graul 				     struct smc_buf_desc *rmb_desc)
41544aa81ceSKarsten Graul {
41644aa81ceSKarsten Graul 	struct smc_llc_msg_confirm_rkey *rkeyllc;
41744aa81ceSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
41844aa81ceSKarsten Graul 	struct smc_wr_buf *wr_buf;
4193d88a21bSKarsten Graul 	struct smc_link *link;
4203d88a21bSKarsten Graul 	int i, rc, rtok_ix;
42144aa81ceSKarsten Graul 
422*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(send_link))
423*95f7f3e7SKarsten Graul 		return -ENOLINK;
4243d88a21bSKarsten Graul 	rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend);
42544aa81ceSKarsten Graul 	if (rc)
426*95f7f3e7SKarsten Graul 		goto put_out;
42744aa81ceSKarsten Graul 	rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf;
42844aa81ceSKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
42944aa81ceSKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY;
43044aa81ceSKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey);
4313d88a21bSKarsten Graul 
4323d88a21bSKarsten Graul 	rtok_ix = 1;
4333d88a21bSKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
4343d88a21bSKarsten Graul 		link = &send_link->lgr->lnk[i];
435741a49a4SKarsten Graul 		if (smc_link_active(link) && link != send_link) {
4363d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].link_id = link->link_id;
4373d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].rmb_key =
438387707fdSKarsten Graul 				htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
4393d88a21bSKarsten Graul 			rkeyllc->rtoken[rtok_ix].rmb_vaddr = cpu_to_be64(
4403d88a21bSKarsten Graul 				(u64)sg_dma_address(
4413d88a21bSKarsten Graul 					rmb_desc->sgt[link->link_idx].sgl));
4423d88a21bSKarsten Graul 			rtok_ix++;
4433d88a21bSKarsten Graul 		}
4443d88a21bSKarsten Graul 	}
4453d88a21bSKarsten Graul 	/* rkey of send_link is in rtoken[0] */
4463d88a21bSKarsten Graul 	rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1;
4473d88a21bSKarsten Graul 	rkeyllc->rtoken[0].rmb_key =
4483d88a21bSKarsten Graul 		htonl(rmb_desc->mr_rx[send_link->link_idx]->rkey);
44944aa81ceSKarsten Graul 	rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
4503d88a21bSKarsten Graul 		(u64)sg_dma_address(rmb_desc->sgt[send_link->link_idx].sgl));
45144aa81ceSKarsten Graul 	/* send llc message */
4523d88a21bSKarsten Graul 	rc = smc_wr_tx_send(send_link, pend);
453*95f7f3e7SKarsten Graul put_out:
454*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(send_link);
45544aa81ceSKarsten Graul 	return rc;
45644aa81ceSKarsten Graul }
45744aa81ceSKarsten Graul 
45860e03c62SKarsten Graul /* send LLC delete rkey request */
45960e03c62SKarsten Graul static int smc_llc_send_delete_rkey(struct smc_link *link,
46060e03c62SKarsten Graul 				    struct smc_buf_desc *rmb_desc)
46160e03c62SKarsten Graul {
46260e03c62SKarsten Graul 	struct smc_llc_msg_delete_rkey *rkeyllc;
46360e03c62SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
46460e03c62SKarsten Graul 	struct smc_wr_buf *wr_buf;
46560e03c62SKarsten Graul 	int rc;
46660e03c62SKarsten Graul 
467*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
468*95f7f3e7SKarsten Graul 		return -ENOLINK;
46960e03c62SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
47060e03c62SKarsten Graul 	if (rc)
471*95f7f3e7SKarsten Graul 		goto put_out;
47260e03c62SKarsten Graul 	rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf;
47360e03c62SKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
47460e03c62SKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY;
47560e03c62SKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey);
47660e03c62SKarsten Graul 	rkeyllc->num_rkeys = 1;
477387707fdSKarsten Graul 	rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
47860e03c62SKarsten Graul 	/* send llc message */
47960e03c62SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
480*95f7f3e7SKarsten Graul put_out:
481*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
48260e03c62SKarsten Graul 	return rc;
48360e03c62SKarsten Graul }
48460e03c62SKarsten Graul 
48552bedf37SKarsten Graul /* send ADD LINK request or response */
4867005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
487fbed3b37SKarsten Graul 			  struct smc_link *link_new,
48852bedf37SKarsten Graul 			  enum smc_llc_reqresp reqresp)
48952bedf37SKarsten Graul {
49052bedf37SKarsten Graul 	struct smc_llc_msg_add_link *addllc;
49152bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
49252bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
49352bedf37SKarsten Graul 	int rc;
49452bedf37SKarsten Graul 
495*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
496*95f7f3e7SKarsten Graul 		return -ENOLINK;
49752bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
49852bedf37SKarsten Graul 	if (rc)
499*95f7f3e7SKarsten Graul 		goto put_out;
50052bedf37SKarsten Graul 	addllc = (struct smc_llc_msg_add_link *)wr_buf;
501fbed3b37SKarsten Graul 
502fbed3b37SKarsten Graul 	memset(addllc, 0, sizeof(*addllc));
503fbed3b37SKarsten Graul 	addllc->hd.common.type = SMC_LLC_ADD_LINK;
504fbed3b37SKarsten Graul 	addllc->hd.length = sizeof(struct smc_llc_msg_add_link);
505fbed3b37SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
506fbed3b37SKarsten Graul 		addllc->hd.flags |= SMC_LLC_FLAG_RESP;
507fbed3b37SKarsten Graul 	memcpy(addllc->sender_mac, mac, ETH_ALEN);
508fbed3b37SKarsten Graul 	memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
509fbed3b37SKarsten Graul 	if (link_new) {
510fbed3b37SKarsten Graul 		addllc->link_num = link_new->link_id;
511fbed3b37SKarsten Graul 		hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num);
512fbed3b37SKarsten Graul 		hton24(addllc->initial_psn, link_new->psn_initial);
513fbed3b37SKarsten Graul 		if (reqresp == SMC_LLC_REQ)
514fbed3b37SKarsten Graul 			addllc->qp_mtu = link_new->path_mtu;
515fbed3b37SKarsten Graul 		else
516fbed3b37SKarsten Graul 			addllc->qp_mtu = min(link_new->path_mtu,
517fbed3b37SKarsten Graul 					     link_new->peer_mtu);
518fbed3b37SKarsten Graul 	}
51952bedf37SKarsten Graul 	/* send llc message */
52052bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
521*95f7f3e7SKarsten Graul put_out:
522*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
52352bedf37SKarsten Graul 	return rc;
52452bedf37SKarsten Graul }
52552bedf37SKarsten Graul 
52652bedf37SKarsten Graul /* send DELETE LINK request or response */
527fbed3b37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id,
528fbed3b37SKarsten Graul 			     enum smc_llc_reqresp reqresp, bool orderly,
529fbed3b37SKarsten Graul 			     u32 reason)
53052bedf37SKarsten Graul {
53152bedf37SKarsten Graul 	struct smc_llc_msg_del_link *delllc;
53252bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
53352bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
53452bedf37SKarsten Graul 	int rc;
53552bedf37SKarsten Graul 
536*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
537*95f7f3e7SKarsten Graul 		return -ENOLINK;
53852bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
53952bedf37SKarsten Graul 	if (rc)
540*95f7f3e7SKarsten Graul 		goto put_out;
54152bedf37SKarsten Graul 	delllc = (struct smc_llc_msg_del_link *)wr_buf;
542fbed3b37SKarsten Graul 
543fbed3b37SKarsten Graul 	memset(delllc, 0, sizeof(*delllc));
544fbed3b37SKarsten Graul 	delllc->hd.common.type = SMC_LLC_DELETE_LINK;
545fbed3b37SKarsten Graul 	delllc->hd.length = sizeof(struct smc_llc_msg_del_link);
546fbed3b37SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
547fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_RESP;
548fbed3b37SKarsten Graul 	if (orderly)
549fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
550fbed3b37SKarsten Graul 	if (link_del_id)
551fbed3b37SKarsten Graul 		delllc->link_num = link_del_id;
552fbed3b37SKarsten Graul 	else
553fbed3b37SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
554fbed3b37SKarsten Graul 	delllc->reason = htonl(reason);
5559bf9abeaSUrsula Braun 	/* send llc message */
5569bf9abeaSUrsula Braun 	rc = smc_wr_tx_send(link, pend);
557*95f7f3e7SKarsten Graul put_out:
558*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
5599bf9abeaSUrsula Braun 	return rc;
5609bf9abeaSUrsula Braun }
5619bf9abeaSUrsula Braun 
562d97935faSKarsten Graul /* send LLC test link request */
563d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
564313164daSKarsten Graul {
565313164daSKarsten Graul 	struct smc_llc_msg_test_link *testllc;
566313164daSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
567313164daSKarsten Graul 	struct smc_wr_buf *wr_buf;
568313164daSKarsten Graul 	int rc;
569313164daSKarsten Graul 
570*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
571*95f7f3e7SKarsten Graul 		return -ENOLINK;
572313164daSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
573313164daSKarsten Graul 	if (rc)
574*95f7f3e7SKarsten Graul 		goto put_out;
575313164daSKarsten Graul 	testllc = (struct smc_llc_msg_test_link *)wr_buf;
576313164daSKarsten Graul 	memset(testllc, 0, sizeof(*testllc));
577313164daSKarsten Graul 	testllc->hd.common.type = SMC_LLC_TEST_LINK;
578313164daSKarsten Graul 	testllc->hd.length = sizeof(struct smc_llc_msg_test_link);
579313164daSKarsten Graul 	memcpy(testllc->user_data, user_data, sizeof(testllc->user_data));
580313164daSKarsten Graul 	/* send llc message */
581313164daSKarsten Graul 	rc = smc_wr_tx_send(link, pend);
582*95f7f3e7SKarsten Graul put_out:
583*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
584313164daSKarsten Graul 	return rc;
585313164daSKarsten Graul }
586313164daSKarsten Graul 
5876c8968c4SKarsten Graul /* schedule an llc send on link, may wait for buffers */
5886c8968c4SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf)
5894ed75de5SKarsten Graul {
5904ed75de5SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
5914ed75de5SKarsten Graul 	struct smc_wr_buf *wr_buf;
5924ed75de5SKarsten Graul 	int rc;
5934ed75de5SKarsten Graul 
594*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
5956c8968c4SKarsten Graul 		return -ENOLINK;
5966c8968c4SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
5974ed75de5SKarsten Graul 	if (rc)
598*95f7f3e7SKarsten Graul 		goto put_out;
5996c8968c4SKarsten Graul 	memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
600*95f7f3e7SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
601*95f7f3e7SKarsten Graul put_out:
602*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
603*95f7f3e7SKarsten Graul 	return rc;
6044ed75de5SKarsten Graul }
6054ed75de5SKarsten Graul 
606f3811fd7SKarsten Graul /* schedule an llc send on link, may wait for buffers,
607f3811fd7SKarsten Graul  * and wait for send completion notification.
608f3811fd7SKarsten Graul  * @return 0 on success
609f3811fd7SKarsten Graul  */
610f3811fd7SKarsten Graul static int smc_llc_send_message_wait(struct smc_link *link, void *llcbuf)
611f3811fd7SKarsten Graul {
612f3811fd7SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
613f3811fd7SKarsten Graul 	struct smc_wr_buf *wr_buf;
614f3811fd7SKarsten Graul 	int rc;
615f3811fd7SKarsten Graul 
616*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
617f3811fd7SKarsten Graul 		return -ENOLINK;
618f3811fd7SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
619f3811fd7SKarsten Graul 	if (rc)
620*95f7f3e7SKarsten Graul 		goto put_out;
621f3811fd7SKarsten Graul 	memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
622*95f7f3e7SKarsten Graul 	rc = smc_wr_tx_send_wait(link, pend, SMC_LLC_WAIT_TIME);
623*95f7f3e7SKarsten Graul put_out:
624*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
625*95f7f3e7SKarsten Graul 	return rc;
626f3811fd7SKarsten Graul }
627f3811fd7SKarsten Graul 
6289bf9abeaSUrsula Braun /********************************* receive ***********************************/
6299bf9abeaSUrsula Braun 
630336ba09fSKarsten Graul static int smc_llc_alloc_alt_link(struct smc_link_group *lgr,
631336ba09fSKarsten Graul 				  enum smc_lgr_type lgr_new_t)
632336ba09fSKarsten Graul {
633336ba09fSKarsten Graul 	int i;
634336ba09fSKarsten Graul 
635336ba09fSKarsten Graul 	if (lgr->type == SMC_LGR_SYMMETRIC ||
636336ba09fSKarsten Graul 	    (lgr->type != SMC_LGR_SINGLE &&
637336ba09fSKarsten Graul 	     (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
638336ba09fSKarsten Graul 	      lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)))
639336ba09fSKarsten Graul 		return -EMLINK;
640336ba09fSKarsten Graul 
641336ba09fSKarsten Graul 	if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
642336ba09fSKarsten Graul 	    lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) {
643336ba09fSKarsten Graul 		for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--)
644336ba09fSKarsten Graul 			if (lgr->lnk[i].state == SMC_LNK_UNUSED)
645336ba09fSKarsten Graul 				return i;
646336ba09fSKarsten Graul 	} else {
647336ba09fSKarsten Graul 		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
648336ba09fSKarsten Graul 			if (lgr->lnk[i].state == SMC_LNK_UNUSED)
649336ba09fSKarsten Graul 				return i;
650336ba09fSKarsten Graul 	}
651336ba09fSKarsten Graul 	return -EMLINK;
652336ba09fSKarsten Graul }
653336ba09fSKarsten Graul 
65487f88cdaSKarsten Graul /* return first buffer from any of the next buf lists */
65587f88cdaSKarsten Graul static struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr,
65687f88cdaSKarsten Graul 						  int *buf_lst)
65787f88cdaSKarsten Graul {
65887f88cdaSKarsten Graul 	struct smc_buf_desc *buf_pos;
65987f88cdaSKarsten Graul 
66087f88cdaSKarsten Graul 	while (*buf_lst < SMC_RMBE_SIZES) {
66187f88cdaSKarsten Graul 		buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst],
66287f88cdaSKarsten Graul 						   struct smc_buf_desc, list);
66387f88cdaSKarsten Graul 		if (buf_pos)
66487f88cdaSKarsten Graul 			return buf_pos;
66587f88cdaSKarsten Graul 		(*buf_lst)++;
66687f88cdaSKarsten Graul 	}
66787f88cdaSKarsten Graul 	return NULL;
66887f88cdaSKarsten Graul }
66987f88cdaSKarsten Graul 
67087f88cdaSKarsten Graul /* return next rmb from buffer lists */
67187f88cdaSKarsten Graul static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr,
67287f88cdaSKarsten Graul 						 int *buf_lst,
67387f88cdaSKarsten Graul 						 struct smc_buf_desc *buf_pos)
67487f88cdaSKarsten Graul {
67587f88cdaSKarsten Graul 	struct smc_buf_desc *buf_next;
67687f88cdaSKarsten Graul 
67787f88cdaSKarsten Graul 	if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) {
67887f88cdaSKarsten Graul 		(*buf_lst)++;
67987f88cdaSKarsten Graul 		return _smc_llc_get_next_rmb(lgr, buf_lst);
68087f88cdaSKarsten Graul 	}
68187f88cdaSKarsten Graul 	buf_next = list_next_entry(buf_pos, list);
68287f88cdaSKarsten Graul 	return buf_next;
68387f88cdaSKarsten Graul }
68487f88cdaSKarsten Graul 
68587f88cdaSKarsten Graul static struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr,
68687f88cdaSKarsten Graul 						  int *buf_lst)
68787f88cdaSKarsten Graul {
68887f88cdaSKarsten Graul 	*buf_lst = 0;
68987f88cdaSKarsten Graul 	return smc_llc_get_next_rmb(lgr, buf_lst, NULL);
69087f88cdaSKarsten Graul }
69187f88cdaSKarsten Graul 
69287f88cdaSKarsten Graul /* send one add_link_continue msg */
69387f88cdaSKarsten Graul static int smc_llc_add_link_cont(struct smc_link *link,
69487f88cdaSKarsten Graul 				 struct smc_link *link_new, u8 *num_rkeys_todo,
69587f88cdaSKarsten Graul 				 int *buf_lst, struct smc_buf_desc **buf_pos)
69687f88cdaSKarsten Graul {
69787f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
69887f88cdaSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
69987f88cdaSKarsten Graul 	int prim_lnk_idx, lnk_idx, i, rc;
70087f88cdaSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
70187f88cdaSKarsten Graul 	struct smc_wr_buf *wr_buf;
70287f88cdaSKarsten Graul 	struct smc_buf_desc *rmb;
70387f88cdaSKarsten Graul 	u8 n;
70487f88cdaSKarsten Graul 
705*95f7f3e7SKarsten Graul 	if (!smc_wr_tx_link_hold(link))
706*95f7f3e7SKarsten Graul 		return -ENOLINK;
70787f88cdaSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
70887f88cdaSKarsten Graul 	if (rc)
709*95f7f3e7SKarsten Graul 		goto put_out;
71087f88cdaSKarsten Graul 	addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf;
71187f88cdaSKarsten Graul 	memset(addc_llc, 0, sizeof(*addc_llc));
71287f88cdaSKarsten Graul 
71387f88cdaSKarsten Graul 	prim_lnk_idx = link->link_idx;
71487f88cdaSKarsten Graul 	lnk_idx = link_new->link_idx;
71587f88cdaSKarsten Graul 	addc_llc->link_num = link_new->link_id;
71687f88cdaSKarsten Graul 	addc_llc->num_rkeys = *num_rkeys_todo;
71787f88cdaSKarsten Graul 	n = *num_rkeys_todo;
71887f88cdaSKarsten Graul 	for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) {
71987f88cdaSKarsten Graul 		if (!*buf_pos) {
72087f88cdaSKarsten Graul 			addc_llc->num_rkeys = addc_llc->num_rkeys -
72187f88cdaSKarsten Graul 					      *num_rkeys_todo;
72287f88cdaSKarsten Graul 			*num_rkeys_todo = 0;
72387f88cdaSKarsten Graul 			break;
72487f88cdaSKarsten Graul 		}
72587f88cdaSKarsten Graul 		rmb = *buf_pos;
72687f88cdaSKarsten Graul 
72787f88cdaSKarsten Graul 		addc_llc->rt[i].rmb_key = htonl(rmb->mr_rx[prim_lnk_idx]->rkey);
72887f88cdaSKarsten Graul 		addc_llc->rt[i].rmb_key_new = htonl(rmb->mr_rx[lnk_idx]->rkey);
72987f88cdaSKarsten Graul 		addc_llc->rt[i].rmb_vaddr_new =
73087f88cdaSKarsten Graul 			cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl));
73187f88cdaSKarsten Graul 
73287f88cdaSKarsten Graul 		(*num_rkeys_todo)--;
73387f88cdaSKarsten Graul 		*buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
73487f88cdaSKarsten Graul 		while (*buf_pos && !(*buf_pos)->used)
73587f88cdaSKarsten Graul 			*buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
73687f88cdaSKarsten Graul 	}
73787f88cdaSKarsten Graul 	addc_llc->hd.common.type = SMC_LLC_ADD_LINK_CONT;
73887f88cdaSKarsten Graul 	addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont);
73987f88cdaSKarsten Graul 	if (lgr->role == SMC_CLNT)
74087f88cdaSKarsten Graul 		addc_llc->hd.flags |= SMC_LLC_FLAG_RESP;
741*95f7f3e7SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
742*95f7f3e7SKarsten Graul put_out:
743*95f7f3e7SKarsten Graul 	smc_wr_tx_link_put(link);
744*95f7f3e7SKarsten Graul 	return rc;
74587f88cdaSKarsten Graul }
74687f88cdaSKarsten Graul 
74787f88cdaSKarsten Graul static int smc_llc_cli_rkey_exchange(struct smc_link *link,
74887f88cdaSKarsten Graul 				     struct smc_link *link_new)
74987f88cdaSKarsten Graul {
75087f88cdaSKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
75187f88cdaSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
75287f88cdaSKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
75387f88cdaSKarsten Graul 	struct smc_llc_qentry *qentry;
75487f88cdaSKarsten Graul 	struct smc_buf_desc *buf_pos;
75587f88cdaSKarsten Graul 	int buf_lst;
75687f88cdaSKarsten Graul 	int rc = 0;
75787f88cdaSKarsten Graul 	int i;
75887f88cdaSKarsten Graul 
75987f88cdaSKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
76087f88cdaSKarsten Graul 	num_rkeys_send = lgr->conns_num;
76187f88cdaSKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
76287f88cdaSKarsten Graul 	do {
76387f88cdaSKarsten Graul 		qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME,
76487f88cdaSKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
76587f88cdaSKarsten Graul 		if (!qentry) {
76687f88cdaSKarsten Graul 			rc = -ETIMEDOUT;
76787f88cdaSKarsten Graul 			break;
76887f88cdaSKarsten Graul 		}
76987f88cdaSKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
77087f88cdaSKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
77187f88cdaSKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
77287f88cdaSKarsten Graul 		for (i = 0; i < max; i++) {
77387f88cdaSKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
77487f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_key,
77587f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
77687f88cdaSKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
77787f88cdaSKarsten Graul 			num_rkeys_recv--;
77887f88cdaSKarsten Graul 		}
77987f88cdaSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
78087f88cdaSKarsten Graul 		rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
78187f88cdaSKarsten Graul 					   &buf_lst, &buf_pos);
78287f88cdaSKarsten Graul 		if (rc)
78387f88cdaSKarsten Graul 			break;
78487f88cdaSKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
78587f88cdaSKarsten Graul 
78687f88cdaSKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
78787f88cdaSKarsten Graul 	return rc;
78887f88cdaSKarsten Graul }
78987f88cdaSKarsten Graul 
790336ba09fSKarsten Graul /* prepare and send an add link reject response */
791336ba09fSKarsten Graul static int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry)
792336ba09fSKarsten Graul {
793336ba09fSKarsten Graul 	qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
794336ba09fSKarsten Graul 	qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
795336ba09fSKarsten Graul 	qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
796336ba09fSKarsten Graul 	return smc_llc_send_message(qentry->link, &qentry->msg);
797336ba09fSKarsten Graul }
798336ba09fSKarsten Graul 
799b1570a87SKarsten Graul static int smc_llc_cli_conf_link(struct smc_link *link,
800b1570a87SKarsten Graul 				 struct smc_init_info *ini,
801b1570a87SKarsten Graul 				 struct smc_link *link_new,
802b1570a87SKarsten Graul 				 enum smc_lgr_type lgr_new_t)
803b1570a87SKarsten Graul {
804b1570a87SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
805b1570a87SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
806b1570a87SKarsten Graul 	int rc = 0;
807b1570a87SKarsten Graul 
808b1570a87SKarsten Graul 	/* receive CONFIRM LINK request over RoCE fabric */
809b1570a87SKarsten Graul 	qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0);
810b1570a87SKarsten Graul 	if (!qentry) {
811b1570a87SKarsten Graul 		rc = smc_llc_send_delete_link(link, link_new->link_id,
812b1570a87SKarsten Graul 					      SMC_LLC_REQ, false,
813b1570a87SKarsten Graul 					      SMC_LLC_DEL_LOST_PATH);
814b1570a87SKarsten Graul 		return -ENOLINK;
815b1570a87SKarsten Graul 	}
816b1570a87SKarsten Graul 	if (qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) {
817b1570a87SKarsten Graul 		/* received DELETE_LINK instead */
818b1570a87SKarsten Graul 		qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
819b1570a87SKarsten Graul 		smc_llc_send_message(link, &qentry->msg);
820b1570a87SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
821b1570a87SKarsten Graul 		return -ENOLINK;
822b1570a87SKarsten Graul 	}
823649758ffSKarsten Graul 	smc_llc_save_peer_uid(qentry);
824b1570a87SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
825b1570a87SKarsten Graul 
826b1570a87SKarsten Graul 	rc = smc_ib_modify_qp_rts(link_new);
827b1570a87SKarsten Graul 	if (rc) {
828b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
829b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
830b1570a87SKarsten Graul 		return -ENOLINK;
831b1570a87SKarsten Graul 	}
832b1570a87SKarsten Graul 	smc_wr_remember_qp_attr(link_new);
833b1570a87SKarsten Graul 
834b1570a87SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
835b1570a87SKarsten Graul 	if (rc) {
836b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
837b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
838b1570a87SKarsten Graul 		return -ENOLINK;
839b1570a87SKarsten Graul 	}
840b1570a87SKarsten Graul 
841b1570a87SKarsten Graul 	/* send CONFIRM LINK response over RoCE fabric */
842b1570a87SKarsten Graul 	rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP);
843b1570a87SKarsten Graul 	if (rc) {
844b1570a87SKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
845b1570a87SKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
846b1570a87SKarsten Graul 		return -ENOLINK;
847b1570a87SKarsten Graul 	}
848b1570a87SKarsten Graul 	smc_llc_link_active(link_new);
849ad6c111bSKarsten Graul 	if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
850ad6c111bSKarsten Graul 	    lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)
851ad6c111bSKarsten Graul 		smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx);
852ad6c111bSKarsten Graul 	else
853ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, lgr_new_t);
854b1570a87SKarsten Graul 	return 0;
855b1570a87SKarsten Graul }
856b1570a87SKarsten Graul 
857336ba09fSKarsten Graul static void smc_llc_save_add_link_info(struct smc_link *link,
858336ba09fSKarsten Graul 				       struct smc_llc_msg_add_link *add_llc)
859336ba09fSKarsten Graul {
860336ba09fSKarsten Graul 	link->peer_qpn = ntoh24(add_llc->sender_qp_num);
861336ba09fSKarsten Graul 	memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE);
862336ba09fSKarsten Graul 	memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN);
863336ba09fSKarsten Graul 	link->peer_psn = ntoh24(add_llc->initial_psn);
864336ba09fSKarsten Graul 	link->peer_mtu = add_llc->qp_mtu;
865336ba09fSKarsten Graul }
866336ba09fSKarsten Graul 
867336ba09fSKarsten Graul /* as an SMC client, process an add link request */
868336ba09fSKarsten Graul int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
869336ba09fSKarsten Graul {
870336ba09fSKarsten Graul 	struct smc_llc_msg_add_link *llc = &qentry->msg.add_link;
871336ba09fSKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
872336ba09fSKarsten Graul 	struct smc_link_group *lgr = smc_get_lgr(link);
873336ba09fSKarsten Graul 	struct smc_link *lnk_new = NULL;
874336ba09fSKarsten Graul 	struct smc_init_info ini;
875336ba09fSKarsten Graul 	int lnk_idx, rc = 0;
876336ba09fSKarsten Graul 
877fffe83c8SKarsten Graul 	if (!llc->qp_mtu)
878fffe83c8SKarsten Graul 		goto out_reject;
879fffe83c8SKarsten Graul 
880336ba09fSKarsten Graul 	ini.vlan_id = lgr->vlan_id;
881336ba09fSKarsten Graul 	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
882336ba09fSKarsten Graul 	if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
883336ba09fSKarsten Graul 	    !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN)) {
884336ba09fSKarsten Graul 		if (!ini.ib_dev)
885336ba09fSKarsten Graul 			goto out_reject;
886336ba09fSKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
887336ba09fSKarsten Graul 	}
888336ba09fSKarsten Graul 	if (!ini.ib_dev) {
889336ba09fSKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
890336ba09fSKarsten Graul 		ini.ib_dev = link->smcibdev;
891336ba09fSKarsten Graul 		ini.ib_port = link->ibport;
892336ba09fSKarsten Graul 	}
893336ba09fSKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
894336ba09fSKarsten Graul 	if (lnk_idx < 0)
895336ba09fSKarsten Graul 		goto out_reject;
896336ba09fSKarsten Graul 	lnk_new = &lgr->lnk[lnk_idx];
897336ba09fSKarsten Graul 	rc = smcr_link_init(lgr, lnk_new, lnk_idx, &ini);
898336ba09fSKarsten Graul 	if (rc)
899336ba09fSKarsten Graul 		goto out_reject;
900336ba09fSKarsten Graul 	smc_llc_save_add_link_info(lnk_new, llc);
90145fa8da0SKarsten Graul 	lnk_new->link_id = llc->link_num;	/* SMC server assigns link id */
90245fa8da0SKarsten Graul 	smc_llc_link_set_uid(lnk_new);
903336ba09fSKarsten Graul 
904336ba09fSKarsten Graul 	rc = smc_ib_ready_link(lnk_new);
905336ba09fSKarsten Graul 	if (rc)
906336ba09fSKarsten Graul 		goto out_clear_lnk;
907336ba09fSKarsten Graul 
908336ba09fSKarsten Graul 	rc = smcr_buf_map_lgr(lnk_new);
909336ba09fSKarsten Graul 	if (rc)
910336ba09fSKarsten Graul 		goto out_clear_lnk;
911336ba09fSKarsten Graul 
912336ba09fSKarsten Graul 	rc = smc_llc_send_add_link(link,
913336ba09fSKarsten Graul 				   lnk_new->smcibdev->mac[ini.ib_port - 1],
914336ba09fSKarsten Graul 				   lnk_new->gid, lnk_new, SMC_LLC_RESP);
915336ba09fSKarsten Graul 	if (rc)
916336ba09fSKarsten Graul 		goto out_clear_lnk;
91787f88cdaSKarsten Graul 	rc = smc_llc_cli_rkey_exchange(link, lnk_new);
918336ba09fSKarsten Graul 	if (rc) {
919336ba09fSKarsten Graul 		rc = 0;
920336ba09fSKarsten Graul 		goto out_clear_lnk;
921336ba09fSKarsten Graul 	}
922b1570a87SKarsten Graul 	rc = smc_llc_cli_conf_link(link, &ini, lnk_new, lgr_new_t);
923336ba09fSKarsten Graul 	if (!rc)
924336ba09fSKarsten Graul 		goto out;
925336ba09fSKarsten Graul out_clear_lnk:
9268f3d65c1SKarsten Graul 	lnk_new->state = SMC_LNK_INACTIVE;
9270a99be43SKarsten Graul 	smcr_link_clear(lnk_new, false);
928336ba09fSKarsten Graul out_reject:
929336ba09fSKarsten Graul 	smc_llc_cli_add_link_reject(qentry);
930336ba09fSKarsten Graul out:
931336ba09fSKarsten Graul 	kfree(qentry);
932336ba09fSKarsten Graul 	return rc;
933336ba09fSKarsten Graul }
934336ba09fSKarsten Graul 
935c48254faSKarsten Graul /* as an SMC client, invite server to start the add_link processing */
936c48254faSKarsten Graul static void smc_llc_cli_add_link_invite(struct smc_link *link,
937c48254faSKarsten Graul 					struct smc_llc_qentry *qentry)
938c48254faSKarsten Graul {
939c48254faSKarsten Graul 	struct smc_link_group *lgr = smc_get_lgr(link);
940c48254faSKarsten Graul 	struct smc_init_info ini;
941c48254faSKarsten Graul 
942c48254faSKarsten Graul 	if (lgr->type == SMC_LGR_SYMMETRIC ||
943c48254faSKarsten Graul 	    lgr->type == SMC_LGR_ASYMMETRIC_PEER)
944c48254faSKarsten Graul 		goto out;
945c48254faSKarsten Graul 
946c48254faSKarsten Graul 	ini.vlan_id = lgr->vlan_id;
947c48254faSKarsten Graul 	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
948c48254faSKarsten Graul 	if (!ini.ib_dev)
949c48254faSKarsten Graul 		goto out;
950c48254faSKarsten Graul 
951c48254faSKarsten Graul 	smc_llc_send_add_link(link, ini.ib_dev->mac[ini.ib_port - 1],
952c48254faSKarsten Graul 			      ini.ib_gid, NULL, SMC_LLC_REQ);
953c48254faSKarsten Graul out:
954c48254faSKarsten Graul 	kfree(qentry);
955c48254faSKarsten Graul }
956c48254faSKarsten Graul 
957fffe83c8SKarsten Graul static bool smc_llc_is_empty_llc_message(union smc_llc_msg *llc)
958fffe83c8SKarsten Graul {
959fffe83c8SKarsten Graul 	int i;
960fffe83c8SKarsten Graul 
961fffe83c8SKarsten Graul 	for (i = 0; i < ARRAY_SIZE(llc->raw.data); i++)
962fffe83c8SKarsten Graul 		if (llc->raw.data[i])
963fffe83c8SKarsten Graul 			return false;
964fffe83c8SKarsten Graul 	return true;
965fffe83c8SKarsten Graul }
966fffe83c8SKarsten Graul 
967c48254faSKarsten Graul static bool smc_llc_is_local_add_link(union smc_llc_msg *llc)
968c48254faSKarsten Graul {
969c48254faSKarsten Graul 	if (llc->raw.hdr.common.type == SMC_LLC_ADD_LINK &&
970fffe83c8SKarsten Graul 	    smc_llc_is_empty_llc_message(llc))
971c48254faSKarsten Graul 		return true;
972c48254faSKarsten Graul 	return false;
973c48254faSKarsten Graul }
974c48254faSKarsten Graul 
975b1570a87SKarsten Graul static void smc_llc_process_cli_add_link(struct smc_link_group *lgr)
976b1570a87SKarsten Graul {
977b1570a87SKarsten Graul 	struct smc_llc_qentry *qentry;
978b1570a87SKarsten Graul 
979b1570a87SKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
980b1570a87SKarsten Graul 
981b1570a87SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
982c48254faSKarsten Graul 	if (smc_llc_is_local_add_link(&qentry->msg))
983c48254faSKarsten Graul 		smc_llc_cli_add_link_invite(qentry->link, qentry);
984c48254faSKarsten Graul 	else
985b1570a87SKarsten Graul 		smc_llc_cli_add_link(qentry->link, qentry);
986b1570a87SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
987b1570a87SKarsten Graul }
988b1570a87SKarsten Graul 
9899c416878SKarsten Graul static int smc_llc_active_link_count(struct smc_link_group *lgr)
9909c416878SKarsten Graul {
9919c416878SKarsten Graul 	int i, link_count = 0;
9929c416878SKarsten Graul 
9939c416878SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
994741a49a4SKarsten Graul 		if (!smc_link_active(&lgr->lnk[i]))
9959c416878SKarsten Graul 			continue;
9969c416878SKarsten Graul 		link_count++;
9979c416878SKarsten Graul 	}
9989c416878SKarsten Graul 	return link_count;
9999c416878SKarsten Graul }
10009c416878SKarsten Graul 
1001c9a5d243SKarsten Graul /* find the asymmetric link when 3 links are established  */
1002c9a5d243SKarsten Graul static struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr)
1003c9a5d243SKarsten Graul {
1004c9a5d243SKarsten Graul 	int asym_idx = -ENOENT;
1005c9a5d243SKarsten Graul 	int i, j, k;
1006c9a5d243SKarsten Graul 	bool found;
1007c9a5d243SKarsten Graul 
1008c9a5d243SKarsten Graul 	/* determine asymmetric link */
1009c9a5d243SKarsten Graul 	found = false;
1010c9a5d243SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1011c9a5d243SKarsten Graul 		for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) {
1012c9a5d243SKarsten Graul 			if (!smc_link_usable(&lgr->lnk[i]) ||
1013c9a5d243SKarsten Graul 			    !smc_link_usable(&lgr->lnk[j]))
1014c9a5d243SKarsten Graul 				continue;
1015c9a5d243SKarsten Graul 			if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid,
1016c9a5d243SKarsten Graul 				    SMC_GID_SIZE)) {
1017c9a5d243SKarsten Graul 				found = true;	/* asym_lnk is i or j */
1018c9a5d243SKarsten Graul 				break;
1019c9a5d243SKarsten Graul 			}
1020c9a5d243SKarsten Graul 		}
1021c9a5d243SKarsten Graul 		if (found)
1022c9a5d243SKarsten Graul 			break;
1023c9a5d243SKarsten Graul 	}
1024c9a5d243SKarsten Graul 	if (!found)
1025c9a5d243SKarsten Graul 		goto out; /* no asymmetric link */
1026c9a5d243SKarsten Graul 	for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) {
1027c9a5d243SKarsten Graul 		if (!smc_link_usable(&lgr->lnk[k]))
1028c9a5d243SKarsten Graul 			continue;
1029c9a5d243SKarsten Graul 		if (k != i &&
1030c9a5d243SKarsten Graul 		    !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid,
1031c9a5d243SKarsten Graul 			    SMC_GID_SIZE)) {
1032c9a5d243SKarsten Graul 			asym_idx = i;
1033c9a5d243SKarsten Graul 			break;
1034c9a5d243SKarsten Graul 		}
1035c9a5d243SKarsten Graul 		if (k != j &&
1036c9a5d243SKarsten Graul 		    !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid,
1037c9a5d243SKarsten Graul 			    SMC_GID_SIZE)) {
1038c9a5d243SKarsten Graul 			asym_idx = j;
1039c9a5d243SKarsten Graul 			break;
1040c9a5d243SKarsten Graul 		}
1041c9a5d243SKarsten Graul 	}
1042c9a5d243SKarsten Graul out:
1043c9a5d243SKarsten Graul 	return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx];
1044c9a5d243SKarsten Graul }
1045c9a5d243SKarsten Graul 
1046c9a5d243SKarsten Graul static void smc_llc_delete_asym_link(struct smc_link_group *lgr)
1047c9a5d243SKarsten Graul {
1048c9a5d243SKarsten Graul 	struct smc_link *lnk_new = NULL, *lnk_asym;
1049c9a5d243SKarsten Graul 	struct smc_llc_qentry *qentry;
1050c9a5d243SKarsten Graul 	int rc;
1051c9a5d243SKarsten Graul 
1052c9a5d243SKarsten Graul 	lnk_asym = smc_llc_find_asym_link(lgr);
1053c9a5d243SKarsten Graul 	if (!lnk_asym)
1054c9a5d243SKarsten Graul 		return; /* no asymmetric link */
1055c9a5d243SKarsten Graul 	if (!smc_link_downing(&lnk_asym->state))
1056c9a5d243SKarsten Graul 		return;
1057c6f02ebeSKarsten Graul 	lnk_new = smc_switch_conns(lgr, lnk_asym, false);
1058c9a5d243SKarsten Graul 	smc_wr_tx_wait_no_pending_sends(lnk_asym);
1059c9a5d243SKarsten Graul 	if (!lnk_new)
1060c9a5d243SKarsten Graul 		goto out_free;
1061c9a5d243SKarsten Graul 	/* change flow type from ADD_LINK into DEL_LINK */
1062c9a5d243SKarsten Graul 	lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK;
1063c9a5d243SKarsten Graul 	rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ,
1064c9a5d243SKarsten Graul 				      true, SMC_LLC_DEL_NO_ASYM_NEEDED);
1065c9a5d243SKarsten Graul 	if (rc) {
1066c9a5d243SKarsten Graul 		smcr_link_down_cond(lnk_new);
1067c9a5d243SKarsten Graul 		goto out_free;
1068c9a5d243SKarsten Graul 	}
1069c9a5d243SKarsten Graul 	qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME,
1070c9a5d243SKarsten Graul 			      SMC_LLC_DELETE_LINK);
1071c9a5d243SKarsten Graul 	if (!qentry) {
1072c9a5d243SKarsten Graul 		smcr_link_down_cond(lnk_new);
1073c9a5d243SKarsten Graul 		goto out_free;
1074c9a5d243SKarsten Graul 	}
1075c9a5d243SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
1076c9a5d243SKarsten Graul out_free:
10770a99be43SKarsten Graul 	smcr_link_clear(lnk_asym, true);
1078c9a5d243SKarsten Graul }
1079c9a5d243SKarsten Graul 
108057b49924SKarsten Graul static int smc_llc_srv_rkey_exchange(struct smc_link *link,
108157b49924SKarsten Graul 				     struct smc_link *link_new)
108257b49924SKarsten Graul {
108357b49924SKarsten Graul 	struct smc_llc_msg_add_link_cont *addc_llc;
108457b49924SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
108557b49924SKarsten Graul 	u8 max, num_rkeys_send, num_rkeys_recv;
108657b49924SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
108757b49924SKarsten Graul 	struct smc_buf_desc *buf_pos;
108857b49924SKarsten Graul 	int buf_lst;
108957b49924SKarsten Graul 	int rc = 0;
109057b49924SKarsten Graul 	int i;
109157b49924SKarsten Graul 
109257b49924SKarsten Graul 	mutex_lock(&lgr->rmbs_lock);
109357b49924SKarsten Graul 	num_rkeys_send = lgr->conns_num;
109457b49924SKarsten Graul 	buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
109557b49924SKarsten Graul 	do {
109657b49924SKarsten Graul 		smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
109757b49924SKarsten Graul 				      &buf_lst, &buf_pos);
109857b49924SKarsten Graul 		qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME,
109957b49924SKarsten Graul 				      SMC_LLC_ADD_LINK_CONT);
110057b49924SKarsten Graul 		if (!qentry) {
110157b49924SKarsten Graul 			rc = -ETIMEDOUT;
110257b49924SKarsten Graul 			goto out;
110357b49924SKarsten Graul 		}
110457b49924SKarsten Graul 		addc_llc = &qentry->msg.add_link_cont;
110557b49924SKarsten Graul 		num_rkeys_recv = addc_llc->num_rkeys;
110657b49924SKarsten Graul 		max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
110757b49924SKarsten Graul 		for (i = 0; i < max; i++) {
110857b49924SKarsten Graul 			smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
110957b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key,
111057b49924SKarsten Graul 				       addc_llc->rt[i].rmb_vaddr_new,
111157b49924SKarsten Graul 				       addc_llc->rt[i].rmb_key_new);
111257b49924SKarsten Graul 			num_rkeys_recv--;
111357b49924SKarsten Graul 		}
111457b49924SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
111557b49924SKarsten Graul 	} while (num_rkeys_send || num_rkeys_recv);
111657b49924SKarsten Graul out:
111757b49924SKarsten Graul 	mutex_unlock(&lgr->rmbs_lock);
111857b49924SKarsten Graul 	return rc;
111957b49924SKarsten Graul }
112057b49924SKarsten Graul 
11211551c95bSKarsten Graul static int smc_llc_srv_conf_link(struct smc_link *link,
11221551c95bSKarsten Graul 				 struct smc_link *link_new,
11231551c95bSKarsten Graul 				 enum smc_lgr_type lgr_new_t)
11241551c95bSKarsten Graul {
11251551c95bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
11261551c95bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
11271551c95bSKarsten Graul 	int rc;
11281551c95bSKarsten Graul 
11291551c95bSKarsten Graul 	/* send CONFIRM LINK request over the RoCE fabric */
11301551c95bSKarsten Graul 	rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ);
11311551c95bSKarsten Graul 	if (rc)
11321551c95bSKarsten Graul 		return -ENOLINK;
11331551c95bSKarsten Graul 	/* receive CONFIRM LINK response over the RoCE fabric */
1134a35fffbfSKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, 0);
1135a35fffbfSKarsten Graul 	if (!qentry ||
1136a35fffbfSKarsten Graul 	    qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) {
11371551c95bSKarsten Graul 		/* send DELETE LINK */
11381551c95bSKarsten Graul 		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
11391551c95bSKarsten Graul 					 false, SMC_LLC_DEL_LOST_PATH);
1140a35fffbfSKarsten Graul 		if (qentry)
1141a35fffbfSKarsten Graul 			smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
11421551c95bSKarsten Graul 		return -ENOLINK;
11431551c95bSKarsten Graul 	}
1144649758ffSKarsten Graul 	smc_llc_save_peer_uid(qentry);
11451551c95bSKarsten Graul 	smc_llc_link_active(link_new);
1146ad6c111bSKarsten Graul 	if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
1147ad6c111bSKarsten Graul 	    lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)
1148ad6c111bSKarsten Graul 		smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx);
1149ad6c111bSKarsten Graul 	else
1150ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, lgr_new_t);
11511551c95bSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
11521551c95bSKarsten Graul 	return 0;
11531551c95bSKarsten Graul }
11541551c95bSKarsten Graul 
11552d2209f2SKarsten Graul int smc_llc_srv_add_link(struct smc_link *link)
11562d2209f2SKarsten Graul {
11572d2209f2SKarsten Graul 	enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
11582d2209f2SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
11592d2209f2SKarsten Graul 	struct smc_llc_msg_add_link *add_llc;
11602d2209f2SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
11612d2209f2SKarsten Graul 	struct smc_link *link_new;
11622d2209f2SKarsten Graul 	struct smc_init_info ini;
11632d2209f2SKarsten Graul 	int lnk_idx, rc = 0;
11642d2209f2SKarsten Graul 
11652d2209f2SKarsten Graul 	/* ignore client add link recommendation, start new flow */
11662d2209f2SKarsten Graul 	ini.vlan_id = lgr->vlan_id;
11672d2209f2SKarsten Graul 	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
11682d2209f2SKarsten Graul 	if (!ini.ib_dev) {
11692d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
11702d2209f2SKarsten Graul 		ini.ib_dev = link->smcibdev;
11712d2209f2SKarsten Graul 		ini.ib_port = link->ibport;
11722d2209f2SKarsten Graul 	}
11732d2209f2SKarsten Graul 	lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
11742d2209f2SKarsten Graul 	if (lnk_idx < 0)
11752d2209f2SKarsten Graul 		return 0;
11762d2209f2SKarsten Graul 
11772d2209f2SKarsten Graul 	rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, &ini);
11782d2209f2SKarsten Graul 	if (rc)
11792d2209f2SKarsten Graul 		return rc;
11802d2209f2SKarsten Graul 	link_new = &lgr->lnk[lnk_idx];
11812d2209f2SKarsten Graul 	rc = smc_llc_send_add_link(link,
11822d2209f2SKarsten Graul 				   link_new->smcibdev->mac[ini.ib_port - 1],
11832d2209f2SKarsten Graul 				   link_new->gid, link_new, SMC_LLC_REQ);
11842d2209f2SKarsten Graul 	if (rc)
11852d2209f2SKarsten Graul 		goto out_err;
11862d2209f2SKarsten Graul 	/* receive ADD LINK response over the RoCE fabric */
11872d2209f2SKarsten Graul 	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK);
11882d2209f2SKarsten Graul 	if (!qentry) {
11892d2209f2SKarsten Graul 		rc = -ETIMEDOUT;
11902d2209f2SKarsten Graul 		goto out_err;
11912d2209f2SKarsten Graul 	}
11922d2209f2SKarsten Graul 	add_llc = &qentry->msg.add_link;
11932d2209f2SKarsten Graul 	if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
11942d2209f2SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
11952d2209f2SKarsten Graul 		rc = -ENOLINK;
11962d2209f2SKarsten Graul 		goto out_err;
11972d2209f2SKarsten Graul 	}
11982d2209f2SKarsten Graul 	if (lgr->type == SMC_LGR_SINGLE &&
11992d2209f2SKarsten Graul 	    (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
12002d2209f2SKarsten Graul 	     !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN))) {
12012d2209f2SKarsten Graul 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
12022d2209f2SKarsten Graul 	}
12032d2209f2SKarsten Graul 	smc_llc_save_add_link_info(link_new, add_llc);
12042d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
12052d2209f2SKarsten Graul 
12062d2209f2SKarsten Graul 	rc = smc_ib_ready_link(link_new);
12072d2209f2SKarsten Graul 	if (rc)
12082d2209f2SKarsten Graul 		goto out_err;
12092d2209f2SKarsten Graul 	rc = smcr_buf_map_lgr(link_new);
12102d2209f2SKarsten Graul 	if (rc)
12112d2209f2SKarsten Graul 		goto out_err;
12122d2209f2SKarsten Graul 	rc = smcr_buf_reg_lgr(link_new);
12132d2209f2SKarsten Graul 	if (rc)
12142d2209f2SKarsten Graul 		goto out_err;
121557b49924SKarsten Graul 	rc = smc_llc_srv_rkey_exchange(link, link_new);
12162d2209f2SKarsten Graul 	if (rc)
12172d2209f2SKarsten Graul 		goto out_err;
12181551c95bSKarsten Graul 	rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
12192d2209f2SKarsten Graul 	if (rc)
12202d2209f2SKarsten Graul 		goto out_err;
12212d2209f2SKarsten Graul 	return 0;
12222d2209f2SKarsten Graul out_err:
12238f3d65c1SKarsten Graul 	link_new->state = SMC_LNK_INACTIVE;
12240a99be43SKarsten Graul 	smcr_link_clear(link_new, false);
12252d2209f2SKarsten Graul 	return rc;
12262d2209f2SKarsten Graul }
12272d2209f2SKarsten Graul 
12282d2209f2SKarsten Graul static void smc_llc_process_srv_add_link(struct smc_link_group *lgr)
12292d2209f2SKarsten Graul {
12302d2209f2SKarsten Graul 	struct smc_link *link = lgr->llc_flow_lcl.qentry->link;
12312d2209f2SKarsten Graul 	int rc;
12322d2209f2SKarsten Graul 
12332d2209f2SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
12342d2209f2SKarsten Graul 
12352d2209f2SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
12362d2209f2SKarsten Graul 	rc = smc_llc_srv_add_link(link);
12372d2209f2SKarsten Graul 	if (!rc && lgr->type == SMC_LGR_SYMMETRIC) {
12382d2209f2SKarsten Graul 		/* delete any asymmetric link */
1239c9a5d243SKarsten Graul 		smc_llc_delete_asym_link(lgr);
12402d2209f2SKarsten Graul 	}
12412d2209f2SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
12422d2209f2SKarsten Graul }
12432d2209f2SKarsten Graul 
1244c48254faSKarsten Graul /* enqueue a local add_link req to trigger a new add_link flow */
1245c48254faSKarsten Graul void smc_llc_add_link_local(struct smc_link *link)
12464dadd151SKarsten Graul {
124716cb3653SPujin Shi 	struct smc_llc_msg_add_link add_llc = {};
12484dadd151SKarsten Graul 
12494dadd151SKarsten Graul 	add_llc.hd.length = sizeof(add_llc);
12504dadd151SKarsten Graul 	add_llc.hd.common.type = SMC_LLC_ADD_LINK;
1251c48254faSKarsten Graul 	/* no dev and port needed */
12524dadd151SKarsten Graul 	smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);
12534dadd151SKarsten Graul }
12544dadd151SKarsten Graul 
1255b45e7f98SKarsten Graul /* worker to process an add link message */
1256b45e7f98SKarsten Graul static void smc_llc_add_link_work(struct work_struct *work)
1257b45e7f98SKarsten Graul {
1258b45e7f98SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
1259b45e7f98SKarsten Graul 						  llc_add_link_work);
1260b45e7f98SKarsten Graul 
1261b45e7f98SKarsten Graul 	if (list_empty(&lgr->list)) {
1262b45e7f98SKarsten Graul 		/* link group is terminating */
1263b45e7f98SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
1264b45e7f98SKarsten Graul 		goto out;
1265b45e7f98SKarsten Graul 	}
1266b45e7f98SKarsten Graul 
1267b1570a87SKarsten Graul 	if (lgr->role == SMC_CLNT)
1268b1570a87SKarsten Graul 		smc_llc_process_cli_add_link(lgr);
12692d2209f2SKarsten Graul 	else
12702d2209f2SKarsten Graul 		smc_llc_process_srv_add_link(lgr);
1271b45e7f98SKarsten Graul out:
1272b45e7f98SKarsten Graul 	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
1273b45e7f98SKarsten Graul }
1274b45e7f98SKarsten Graul 
12754dadd151SKarsten Graul /* enqueue a local del_link msg to trigger a new del_link flow,
12764dadd151SKarsten Graul  * called only for role SMC_SERV
12774dadd151SKarsten Graul  */
12784dadd151SKarsten Graul void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id)
12794dadd151SKarsten Graul {
128016cb3653SPujin Shi 	struct smc_llc_msg_del_link del_llc = {};
12814dadd151SKarsten Graul 
12824dadd151SKarsten Graul 	del_llc.hd.length = sizeof(del_llc);
12834dadd151SKarsten Graul 	del_llc.hd.common.type = SMC_LLC_DELETE_LINK;
12844dadd151SKarsten Graul 	del_llc.link_num = del_link_id;
12854dadd151SKarsten Graul 	del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH);
12864dadd151SKarsten Graul 	del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
12874dadd151SKarsten Graul 	smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc);
12884dadd151SKarsten Graul }
12894dadd151SKarsten Graul 
12909c416878SKarsten Graul static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
12919c416878SKarsten Graul {
12929c416878SKarsten Graul 	struct smc_link *lnk_del = NULL, *lnk_asym, *lnk;
12939c416878SKarsten Graul 	struct smc_llc_msg_del_link *del_llc;
12949c416878SKarsten Graul 	struct smc_llc_qentry *qentry;
12959c416878SKarsten Graul 	int active_links;
12969c416878SKarsten Graul 	int lnk_idx;
12979c416878SKarsten Graul 
12989c416878SKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
12999c416878SKarsten Graul 	lnk = qentry->link;
13009c416878SKarsten Graul 	del_llc = &qentry->msg.delete_link;
13019c416878SKarsten Graul 
13029c416878SKarsten Graul 	if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) {
13039c416878SKarsten Graul 		smc_lgr_terminate_sched(lgr);
13049c416878SKarsten Graul 		goto out;
13059c416878SKarsten Graul 	}
13069c416878SKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
13079c416878SKarsten Graul 	/* delete single link */
13089c416878SKarsten Graul 	for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) {
13099c416878SKarsten Graul 		if (lgr->lnk[lnk_idx].link_id != del_llc->link_num)
13109c416878SKarsten Graul 			continue;
13119c416878SKarsten Graul 		lnk_del = &lgr->lnk[lnk_idx];
13129c416878SKarsten Graul 		break;
13139c416878SKarsten Graul 	}
13149c416878SKarsten Graul 	del_llc->hd.flags |= SMC_LLC_FLAG_RESP;
13159c416878SKarsten Graul 	if (!lnk_del) {
13169c416878SKarsten Graul 		/* link was not found */
13179c416878SKarsten Graul 		del_llc->reason = htonl(SMC_LLC_DEL_NOLNK);
13189c416878SKarsten Graul 		smc_llc_send_message(lnk, &qentry->msg);
13199c416878SKarsten Graul 		goto out_unlock;
13209c416878SKarsten Graul 	}
13219c416878SKarsten Graul 	lnk_asym = smc_llc_find_asym_link(lgr);
13229c416878SKarsten Graul 
13239c416878SKarsten Graul 	del_llc->reason = 0;
13249c416878SKarsten Graul 	smc_llc_send_message(lnk, &qentry->msg); /* response */
13259c416878SKarsten Graul 
13268f3d65c1SKarsten Graul 	if (smc_link_downing(&lnk_del->state))
13278f3d65c1SKarsten Graul 		smc_switch_conns(lgr, lnk_del, false);
13280a99be43SKarsten Graul 	smcr_link_clear(lnk_del, true);
13299c416878SKarsten Graul 
13309c416878SKarsten Graul 	active_links = smc_llc_active_link_count(lgr);
13319c416878SKarsten Graul 	if (lnk_del == lnk_asym) {
13329c416878SKarsten Graul 		/* expected deletion of asym link, don't change lgr state */
13339c416878SKarsten Graul 	} else if (active_links == 1) {
1334ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
13359c416878SKarsten Graul 	} else if (!active_links) {
1336ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_NONE);
13379c416878SKarsten Graul 		smc_lgr_terminate_sched(lgr);
13389c416878SKarsten Graul 	}
13399c416878SKarsten Graul out_unlock:
13409c416878SKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
13419c416878SKarsten Graul out:
13429c416878SKarsten Graul 	kfree(qentry);
13439c416878SKarsten Graul }
13449c416878SKarsten Graul 
1345f3811fd7SKarsten Graul /* try to send a DELETE LINK ALL request on any active link,
1346f3811fd7SKarsten Graul  * waiting for send completion
1347f3811fd7SKarsten Graul  */
1348f3811fd7SKarsten Graul void smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord, u32 rsn)
1349f3811fd7SKarsten Graul {
13507e94e46cSPujin Shi 	struct smc_llc_msg_del_link delllc = {};
1351f3811fd7SKarsten Graul 	int i;
1352f3811fd7SKarsten Graul 
1353f3811fd7SKarsten Graul 	delllc.hd.common.type = SMC_LLC_DELETE_LINK;
1354f3811fd7SKarsten Graul 	delllc.hd.length = sizeof(delllc);
1355f3811fd7SKarsten Graul 	if (ord)
1356f3811fd7SKarsten Graul 		delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
1357f3811fd7SKarsten Graul 	delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
1358f3811fd7SKarsten Graul 	delllc.reason = htonl(rsn);
1359f3811fd7SKarsten Graul 
1360f3811fd7SKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
1361f3811fd7SKarsten Graul 		if (!smc_link_usable(&lgr->lnk[i]))
1362f3811fd7SKarsten Graul 			continue;
1363f3811fd7SKarsten Graul 		if (!smc_llc_send_message_wait(&lgr->lnk[i], &delllc))
1364f3811fd7SKarsten Graul 			break;
1365f3811fd7SKarsten Graul 	}
1366f3811fd7SKarsten Graul }
1367f3811fd7SKarsten Graul 
136808ae27ddSKarsten Graul static void smc_llc_process_srv_delete_link(struct smc_link_group *lgr)
136908ae27ddSKarsten Graul {
137008ae27ddSKarsten Graul 	struct smc_llc_msg_del_link *del_llc;
137108ae27ddSKarsten Graul 	struct smc_link *lnk, *lnk_del;
137208ae27ddSKarsten Graul 	struct smc_llc_qentry *qentry;
137308ae27ddSKarsten Graul 	int active_links;
137408ae27ddSKarsten Graul 	int i;
137508ae27ddSKarsten Graul 
137608ae27ddSKarsten Graul 	mutex_lock(&lgr->llc_conf_mutex);
137708ae27ddSKarsten Graul 	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
137808ae27ddSKarsten Graul 	lnk = qentry->link;
137908ae27ddSKarsten Graul 	del_llc = &qentry->msg.delete_link;
138008ae27ddSKarsten Graul 
138108ae27ddSKarsten Graul 	if (qentry->msg.delete_link.hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) {
138208ae27ddSKarsten Graul 		/* delete entire lgr */
1383f3811fd7SKarsten Graul 		smc_llc_send_link_delete_all(lgr, true, ntohl(
1384f3811fd7SKarsten Graul 					      qentry->msg.delete_link.reason));
138508ae27ddSKarsten Graul 		smc_lgr_terminate_sched(lgr);
138608ae27ddSKarsten Graul 		goto out;
138708ae27ddSKarsten Graul 	}
138808ae27ddSKarsten Graul 	/* delete single link */
138908ae27ddSKarsten Graul 	lnk_del = NULL;
139008ae27ddSKarsten Graul 	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
139108ae27ddSKarsten Graul 		if (lgr->lnk[i].link_id == del_llc->link_num) {
139208ae27ddSKarsten Graul 			lnk_del = &lgr->lnk[i];
139308ae27ddSKarsten Graul 			break;
139408ae27ddSKarsten Graul 		}
139508ae27ddSKarsten Graul 	}
139608ae27ddSKarsten Graul 	if (!lnk_del)
139708ae27ddSKarsten Graul 		goto out; /* asymmetric link already deleted */
139808ae27ddSKarsten Graul 
139908ae27ddSKarsten Graul 	if (smc_link_downing(&lnk_del->state)) {
1400b7eede75SKarsten Graul 		if (smc_switch_conns(lgr, lnk_del, false))
140108ae27ddSKarsten Graul 			smc_wr_tx_wait_no_pending_sends(lnk_del);
140208ae27ddSKarsten Graul 	}
140308ae27ddSKarsten Graul 	if (!list_empty(&lgr->list)) {
140408ae27ddSKarsten Graul 		/* qentry is either a request from peer (send it back to
140508ae27ddSKarsten Graul 		 * initiate the DELETE_LINK processing), or a locally
140608ae27ddSKarsten Graul 		 * enqueued DELETE_LINK request (forward it)
140708ae27ddSKarsten Graul 		 */
140808ae27ddSKarsten Graul 		if (!smc_llc_send_message(lnk, &qentry->msg)) {
140908ae27ddSKarsten Graul 			struct smc_llc_qentry *qentry2;
141008ae27ddSKarsten Graul 
141108ae27ddSKarsten Graul 			qentry2 = smc_llc_wait(lgr, lnk, SMC_LLC_WAIT_TIME,
141208ae27ddSKarsten Graul 					       SMC_LLC_DELETE_LINK);
1413ca7e3edcSYueHaibing 			if (qentry2)
141408ae27ddSKarsten Graul 				smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
141508ae27ddSKarsten Graul 		}
141608ae27ddSKarsten Graul 	}
14170a99be43SKarsten Graul 	smcr_link_clear(lnk_del, true);
141808ae27ddSKarsten Graul 
141908ae27ddSKarsten Graul 	active_links = smc_llc_active_link_count(lgr);
142008ae27ddSKarsten Graul 	if (active_links == 1) {
1421ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
142208ae27ddSKarsten Graul 	} else if (!active_links) {
1423ad6c111bSKarsten Graul 		smcr_lgr_set_type(lgr, SMC_LGR_NONE);
142408ae27ddSKarsten Graul 		smc_lgr_terminate_sched(lgr);
142508ae27ddSKarsten Graul 	}
142608ae27ddSKarsten Graul 
142708ae27ddSKarsten Graul 	if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) {
142808ae27ddSKarsten Graul 		/* trigger setup of asymm alt link */
1429c48254faSKarsten Graul 		smc_llc_add_link_local(lnk);
143008ae27ddSKarsten Graul 	}
143108ae27ddSKarsten Graul out:
143208ae27ddSKarsten Graul 	mutex_unlock(&lgr->llc_conf_mutex);
143308ae27ddSKarsten Graul 	kfree(qentry);
143408ae27ddSKarsten Graul }
143508ae27ddSKarsten Graul 
14369ec6bf19SKarsten Graul static void smc_llc_delete_link_work(struct work_struct *work)
143752bedf37SKarsten Graul {
14389ec6bf19SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
14399ec6bf19SKarsten Graul 						  llc_del_link_work);
144052bedf37SKarsten Graul 
14419ec6bf19SKarsten Graul 	if (list_empty(&lgr->list)) {
14429ec6bf19SKarsten Graul 		/* link group is terminating */
14439ec6bf19SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
14449ec6bf19SKarsten Graul 		goto out;
144552bedf37SKarsten Graul 	}
14469c416878SKarsten Graul 
14479c416878SKarsten Graul 	if (lgr->role == SMC_CLNT)
14489c416878SKarsten Graul 		smc_llc_process_cli_delete_link(lgr);
144908ae27ddSKarsten Graul 	else
145008ae27ddSKarsten Graul 		smc_llc_process_srv_delete_link(lgr);
14519ec6bf19SKarsten Graul out:
14529ec6bf19SKarsten Graul 	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
145352bedf37SKarsten Graul }
145452bedf37SKarsten Graul 
14553bc67e09SKarsten Graul /* process a confirm_rkey request from peer, remote flow */
14563bc67e09SKarsten Graul static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr)
14574ed75de5SKarsten Graul {
14583bc67e09SKarsten Graul 	struct smc_llc_msg_confirm_rkey *llc;
14593bc67e09SKarsten Graul 	struct smc_llc_qentry *qentry;
14603bc67e09SKarsten Graul 	struct smc_link *link;
14613bc67e09SKarsten Graul 	int num_entries;
14623bc67e09SKarsten Graul 	int rk_idx;
14633bc67e09SKarsten Graul 	int i;
14644ed75de5SKarsten Graul 
14653bc67e09SKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
14663bc67e09SKarsten Graul 	llc = &qentry->msg.confirm_rkey;
14673bc67e09SKarsten Graul 	link = qentry->link;
14683bc67e09SKarsten Graul 
14693bc67e09SKarsten Graul 	num_entries = llc->rtoken[0].num_rkeys;
14703bc67e09SKarsten Graul 	/* first rkey entry is for receiving link */
14713bc67e09SKarsten Graul 	rk_idx = smc_rtoken_add(link,
14724ed75de5SKarsten Graul 				llc->rtoken[0].rmb_vaddr,
14734ed75de5SKarsten Graul 				llc->rtoken[0].rmb_key);
14743bc67e09SKarsten Graul 	if (rk_idx < 0)
14753bc67e09SKarsten Graul 		goto out_err;
14764ed75de5SKarsten Graul 
14773bc67e09SKarsten Graul 	for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++)
14783bc67e09SKarsten Graul 		smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id,
14793bc67e09SKarsten Graul 				llc->rtoken[i].rmb_vaddr,
14803bc67e09SKarsten Graul 				llc->rtoken[i].rmb_key);
14813bc67e09SKarsten Graul 	/* max links is 3 so there is no need to support conf_rkey_cont msgs */
14823bc67e09SKarsten Graul 	goto out;
14833bc67e09SKarsten Graul out_err:
14844ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
14853bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY;
14863bc67e09SKarsten Graul out:
14873bc67e09SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
14883bc67e09SKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
14893bc67e09SKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
14904ed75de5SKarsten Graul }
14914ed75de5SKarsten Graul 
1492218b24feSKarsten Graul /* process a delete_rkey request from peer, remote flow */
1493218b24feSKarsten Graul static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
14944ed75de5SKarsten Graul {
1495218b24feSKarsten Graul 	struct smc_llc_msg_delete_rkey *llc;
1496218b24feSKarsten Graul 	struct smc_llc_qentry *qentry;
1497218b24feSKarsten Graul 	struct smc_link *link;
14984ed75de5SKarsten Graul 	u8 err_mask = 0;
14994ed75de5SKarsten Graul 	int i, max;
15004ed75de5SKarsten Graul 
1501218b24feSKarsten Graul 	qentry = lgr->llc_flow_rmt.qentry;
1502218b24feSKarsten Graul 	llc = &qentry->msg.delete_rkey;
1503218b24feSKarsten Graul 	link = qentry->link;
1504218b24feSKarsten Graul 
15054ed75de5SKarsten Graul 	max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
15064ed75de5SKarsten Graul 	for (i = 0; i < max; i++) {
1507387707fdSKarsten Graul 		if (smc_rtoken_delete(link, llc->rkey[i]))
15084ed75de5SKarsten Graul 			err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
15094ed75de5SKarsten Graul 	}
15104ed75de5SKarsten Graul 	if (err_mask) {
15114ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
15124ed75de5SKarsten Graul 		llc->err_mask = err_mask;
15134ed75de5SKarsten Graul 	}
1514218b24feSKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
1515218b24feSKarsten Graul 	smc_llc_send_message(link, &qentry->msg);
1516218b24feSKarsten Graul 	smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
1517218b24feSKarsten Graul }
15184ed75de5SKarsten Graul 
15193e0c40afSKarsten Graul static void smc_llc_protocol_violation(struct smc_link_group *lgr, u8 type)
15203e0c40afSKarsten Graul {
15213e0c40afSKarsten Graul 	pr_warn_ratelimited("smc: SMC-R lg %*phN LLC protocol violation: "
15223e0c40afSKarsten Graul 			    "llc_type %d\n", SMC_LGR_ID_SIZE, &lgr->id, type);
15233e0c40afSKarsten Graul 	smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_PROT_VIOL);
15243e0c40afSKarsten Graul 	smc_lgr_terminate_sched(lgr);
15253e0c40afSKarsten Graul }
15263e0c40afSKarsten Graul 
15276c8968c4SKarsten Graul /* flush the llc event queue */
152800a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr)
15299bf9abeaSUrsula Braun {
15306c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry, *q;
15319bf9abeaSUrsula Braun 
15326c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
15336c8968c4SKarsten Graul 	list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
15346c8968c4SKarsten Graul 		list_del_init(&qentry->list);
15356c8968c4SKarsten Graul 		kfree(qentry);
15366c8968c4SKarsten Graul 	}
15376c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
15386c8968c4SKarsten Graul }
15396c8968c4SKarsten Graul 
15406c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
15416c8968c4SKarsten Graul {
15426c8968c4SKarsten Graul 	union smc_llc_msg *llc = &qentry->msg;
15436c8968c4SKarsten Graul 	struct smc_link *link = qentry->link;
15440fb0b02bSKarsten Graul 	struct smc_link_group *lgr = link->lgr;
15456c8968c4SKarsten Graul 
1546d854fcbfSKarsten Graul 	if (!smc_link_usable(link))
15476c8968c4SKarsten Graul 		goto out;
1548313164daSKarsten Graul 
1549313164daSKarsten Graul 	switch (llc->raw.hdr.common.type) {
1550313164daSKarsten Graul 	case SMC_LLC_TEST_LINK:
155156e8091cSKarsten Graul 		llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP;
155256e8091cSKarsten Graul 		smc_llc_send_message(link, llc);
1553313164daSKarsten Graul 		break;
155452bedf37SKarsten Graul 	case SMC_LLC_ADD_LINK:
15550fb0b02bSKarsten Graul 		if (list_empty(&lgr->list))
15560fb0b02bSKarsten Graul 			goto out;	/* lgr is terminating */
15570fb0b02bSKarsten Graul 		if (lgr->role == SMC_CLNT) {
1558c48254faSKarsten Graul 			if (smc_llc_is_local_add_link(llc)) {
1559c48254faSKarsten Graul 				if (lgr->llc_flow_lcl.type ==
1560c48254faSKarsten Graul 				    SMC_LLC_FLOW_ADD_LINK)
1561c48254faSKarsten Graul 					break;	/* add_link in progress */
1562c48254faSKarsten Graul 				if (smc_llc_flow_start(&lgr->llc_flow_lcl,
1563c48254faSKarsten Graul 						       qentry)) {
1564c48254faSKarsten Graul 					schedule_work(&lgr->llc_add_link_work);
1565c48254faSKarsten Graul 				}
1566c48254faSKarsten Graul 				return;
1567c48254faSKarsten Graul 			}
1568c48254faSKarsten Graul 			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK &&
1569c48254faSKarsten Graul 			    !lgr->llc_flow_lcl.qentry) {
15700fb0b02bSKarsten Graul 				/* a flow is waiting for this message */
15710fb0b02bSKarsten Graul 				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
15720fb0b02bSKarsten Graul 							qentry);
15736778a6beSKarsten Graul 				wake_up(&lgr->llc_msg_waiter);
15740fb0b02bSKarsten Graul 			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
15750fb0b02bSKarsten Graul 						      qentry)) {
1576b45e7f98SKarsten Graul 				schedule_work(&lgr->llc_add_link_work);
15770fb0b02bSKarsten Graul 			}
15780fb0b02bSKarsten Graul 		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
15790fb0b02bSKarsten Graul 			/* as smc server, handle client suggestion */
1580b45e7f98SKarsten Graul 			schedule_work(&lgr->llc_add_link_work);
15810fb0b02bSKarsten Graul 		}
15820fb0b02bSKarsten Graul 		return;
15830fb0b02bSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
158487f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
15850fb0b02bSKarsten Graul 		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
15860fb0b02bSKarsten Graul 			/* a flow is waiting for this message */
15870fb0b02bSKarsten Graul 			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
15886778a6beSKarsten Graul 			wake_up(&lgr->llc_msg_waiter);
15890fb0b02bSKarsten Graul 			return;
15900fb0b02bSKarsten Graul 		}
159152bedf37SKarsten Graul 		break;
159252bedf37SKarsten Graul 	case SMC_LLC_DELETE_LINK:
15939ec6bf19SKarsten Graul 		if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK &&
15949ec6bf19SKarsten Graul 		    !lgr->llc_flow_lcl.qentry) {
15959ec6bf19SKarsten Graul 			/* DEL LINK REQ during ADD LINK SEQ */
1596b9979c2eSKarsten Graul 			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
15976778a6beSKarsten Graul 			wake_up(&lgr->llc_msg_waiter);
1598b9979c2eSKarsten Graul 		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
15999ec6bf19SKarsten Graul 			schedule_work(&lgr->llc_del_link_work);
16009ec6bf19SKarsten Graul 		}
16019ec6bf19SKarsten Graul 		return;
16024ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
16033bc67e09SKarsten Graul 		/* new request from remote, assign to remote flow */
16043bc67e09SKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
16053bc67e09SKarsten Graul 			/* process here, does not wait for more llc msgs */
16063bc67e09SKarsten Graul 			smc_llc_rmt_conf_rkey(lgr);
16073bc67e09SKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
16083bc67e09SKarsten Graul 		}
16093bc67e09SKarsten Graul 		return;
16104ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
161142d18accSKarsten Graul 		/* not used because max links is 3, and 3 rkeys fit into
161242d18accSKarsten Graul 		 * one CONFIRM_RKEY message
161342d18accSKarsten Graul 		 */
16144ed75de5SKarsten Graul 		break;
16154ed75de5SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
1616218b24feSKarsten Graul 		/* new request from remote, assign to remote flow */
1617218b24feSKarsten Graul 		if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
1618218b24feSKarsten Graul 			/* process here, does not wait for more llc msgs */
1619218b24feSKarsten Graul 			smc_llc_rmt_delete_rkey(lgr);
1620218b24feSKarsten Graul 			smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
1621218b24feSKarsten Graul 		}
1622218b24feSKarsten Graul 		return;
16233e0c40afSKarsten Graul 	default:
16243e0c40afSKarsten Graul 		smc_llc_protocol_violation(lgr, llc->raw.hdr.common.type);
16253e0c40afSKarsten Graul 		break;
1626313164daSKarsten Graul 	}
16276c8968c4SKarsten Graul out:
16286c8968c4SKarsten Graul 	kfree(qentry);
16296c8968c4SKarsten Graul }
16306c8968c4SKarsten Graul 
16316c8968c4SKarsten Graul /* worker to process llc messages on the event queue */
16326c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work)
16336c8968c4SKarsten Graul {
16346c8968c4SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
16356c8968c4SKarsten Graul 						  llc_event_work);
16366c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
16376c8968c4SKarsten Graul 
1638555da9afSKarsten Graul 	if (!lgr->llc_flow_lcl.type && lgr->delayed_event) {
1639555da9afSKarsten Graul 		qentry = lgr->delayed_event;
1640555da9afSKarsten Graul 		lgr->delayed_event = NULL;
1641d535ca13SKarsten Graul 		if (smc_link_usable(qentry->link))
1642d535ca13SKarsten Graul 			smc_llc_event_handler(qentry);
1643d535ca13SKarsten Graul 		else
1644555da9afSKarsten Graul 			kfree(qentry);
1645555da9afSKarsten Graul 	}
1646555da9afSKarsten Graul 
16476c8968c4SKarsten Graul again:
16486c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
16496c8968c4SKarsten Graul 	if (!list_empty(&lgr->llc_event_q)) {
16506c8968c4SKarsten Graul 		qentry = list_first_entry(&lgr->llc_event_q,
16516c8968c4SKarsten Graul 					  struct smc_llc_qentry, list);
16526c8968c4SKarsten Graul 		list_del_init(&qentry->list);
16536c8968c4SKarsten Graul 		spin_unlock_bh(&lgr->llc_event_q_lock);
16546c8968c4SKarsten Graul 		smc_llc_event_handler(qentry);
16556c8968c4SKarsten Graul 		goto again;
16566c8968c4SKarsten Graul 	}
16576c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
16586c8968c4SKarsten Graul }
16596c8968c4SKarsten Graul 
1660ef79d439SKarsten Graul /* process llc responses in tasklet context */
1661a6688d91SKarsten Graul static void smc_llc_rx_response(struct smc_link *link,
1662a6688d91SKarsten Graul 				struct smc_llc_qentry *qentry)
1663ef79d439SKarsten Graul {
16642ff08678SKarsten Graul 	enum smc_llc_flowtype flowtype = link->lgr->llc_flow_lcl.type;
16652ff08678SKarsten Graul 	struct smc_llc_flow *flow = &link->lgr->llc_flow_lcl;
1666a6688d91SKarsten Graul 	u8 llc_type = qentry->msg.raw.hdr.common.type;
1667ef79d439SKarsten Graul 
1668a6688d91SKarsten Graul 	switch (llc_type) {
1669ef79d439SKarsten Graul 	case SMC_LLC_TEST_LINK:
1670741a49a4SKarsten Graul 		if (smc_link_active(link))
1671ef79d439SKarsten Graul 			complete(&link->llc_testlink_resp);
1672ef79d439SKarsten Graul 		break;
1673ef79d439SKarsten Graul 	case SMC_LLC_ADD_LINK:
167487f88cdaSKarsten Graul 	case SMC_LLC_ADD_LINK_CONT:
16752ff08678SKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
16762ff08678SKarsten Graul 		if (flowtype != SMC_LLC_FLOW_ADD_LINK || flow->qentry)
16772ff08678SKarsten Graul 			break;	/* drop out-of-flow response */
16782ff08678SKarsten Graul 		goto assign;
16792ff08678SKarsten Graul 	case SMC_LLC_DELETE_LINK:
16802ff08678SKarsten Graul 		if (flowtype != SMC_LLC_FLOW_DEL_LINK || flow->qentry)
16812ff08678SKarsten Graul 			break;	/* drop out-of-flow response */
16822ff08678SKarsten Graul 		goto assign;
16833d88a21bSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
16846d74c3a8SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
16852ff08678SKarsten Graul 		if (flowtype != SMC_LLC_FLOW_RKEY || flow->qentry)
16862ff08678SKarsten Graul 			break;	/* drop out-of-flow response */
16872ff08678SKarsten Graul 		goto assign;
1688ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
168942d18accSKarsten Graul 		/* not used because max links is 3 */
1690ef79d439SKarsten Graul 		break;
16913e0c40afSKarsten Graul 	default:
16923e0c40afSKarsten Graul 		smc_llc_protocol_violation(link->lgr, llc_type);
16933e0c40afSKarsten Graul 		break;
1694ef79d439SKarsten Graul 	}
1695a6688d91SKarsten Graul 	kfree(qentry);
16962ff08678SKarsten Graul 	return;
16972ff08678SKarsten Graul assign:
16982ff08678SKarsten Graul 	/* assign responses to the local flow, we requested them */
16992ff08678SKarsten Graul 	smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry);
17002ff08678SKarsten Graul 	wake_up(&link->lgr->llc_msg_waiter);
1701ef79d439SKarsten Graul }
1702ef79d439SKarsten Graul 
1703a6688d91SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
17046c8968c4SKarsten Graul {
17056c8968c4SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
17066c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
17076c8968c4SKarsten Graul 	unsigned long flags;
17086c8968c4SKarsten Graul 
17096c8968c4SKarsten Graul 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
17106c8968c4SKarsten Graul 	if (!qentry)
17116c8968c4SKarsten Graul 		return;
17126c8968c4SKarsten Graul 	qentry->link = link;
17136c8968c4SKarsten Graul 	INIT_LIST_HEAD(&qentry->list);
17146c8968c4SKarsten Graul 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
1715a6688d91SKarsten Graul 
1716a6688d91SKarsten Graul 	/* process responses immediately */
1717a6688d91SKarsten Graul 	if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) {
1718a6688d91SKarsten Graul 		smc_llc_rx_response(link, qentry);
1719a6688d91SKarsten Graul 		return;
1720a6688d91SKarsten Graul 	}
1721a6688d91SKarsten Graul 
1722a6688d91SKarsten Graul 	/* add requests to event queue */
17236c8968c4SKarsten Graul 	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
17246c8968c4SKarsten Graul 	list_add_tail(&qentry->list, &lgr->llc_event_q);
17256c8968c4SKarsten Graul 	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
172622ef473dSKarsten Graul 	queue_work(system_highpri_wq, &lgr->llc_event_work);
17279bf9abeaSUrsula Braun }
17289bf9abeaSUrsula Braun 
1729a6688d91SKarsten Graul /* copy received msg and add it to the event queue */
1730a6688d91SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
1731a6688d91SKarsten Graul {
1732a6688d91SKarsten Graul 	struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
1733a6688d91SKarsten Graul 	union smc_llc_msg *llc = buf;
1734a6688d91SKarsten Graul 
1735a6688d91SKarsten Graul 	if (wc->byte_len < sizeof(*llc))
1736a6688d91SKarsten Graul 		return; /* short message */
1737a6688d91SKarsten Graul 	if (llc->raw.hdr.length != sizeof(*llc))
1738a6688d91SKarsten Graul 		return; /* invalid message */
1739a6688d91SKarsten Graul 
1740a6688d91SKarsten Graul 	smc_llc_enqueue(link, llc);
1741a6688d91SKarsten Graul }
1742a6688d91SKarsten Graul 
174344aa81ceSKarsten Graul /***************************** worker, utils *********************************/
1744877ae5beSKarsten Graul 
1745877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work)
1746877ae5beSKarsten Graul {
1747877ae5beSKarsten Graul 	struct smc_link *link = container_of(to_delayed_work(work),
1748877ae5beSKarsten Graul 					     struct smc_link, llc_testlink_wrk);
1749877ae5beSKarsten Graul 	unsigned long next_interval;
1750877ae5beSKarsten Graul 	unsigned long expire_time;
1751877ae5beSKarsten Graul 	u8 user_data[16] = { 0 };
1752877ae5beSKarsten Graul 	int rc;
1753877ae5beSKarsten Graul 
1754741a49a4SKarsten Graul 	if (!smc_link_active(link))
1755877ae5beSKarsten Graul 		return;		/* don't reschedule worker */
1756877ae5beSKarsten Graul 	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
1757877ae5beSKarsten Graul 	if (time_is_after_jiffies(expire_time)) {
1758877ae5beSKarsten Graul 		next_interval = expire_time - jiffies;
1759877ae5beSKarsten Graul 		goto out;
1760877ae5beSKarsten Graul 	}
1761877ae5beSKarsten Graul 	reinit_completion(&link->llc_testlink_resp);
1762d97935faSKarsten Graul 	smc_llc_send_test_link(link, user_data);
1763877ae5beSKarsten Graul 	/* receive TEST LINK response over RoCE fabric */
1764877ae5beSKarsten Graul 	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
1765877ae5beSKarsten Graul 						       SMC_LLC_WAIT_TIME);
1766741a49a4SKarsten Graul 	if (!smc_link_active(link))
17671020e1efSKarsten Graul 		return;		/* link state changed */
1768877ae5beSKarsten Graul 	if (rc <= 0) {
176987523930SKarsten Graul 		smcr_link_down_cond_sched(link);
1770877ae5beSKarsten Graul 		return;
1771877ae5beSKarsten Graul 	}
1772877ae5beSKarsten Graul 	next_interval = link->llc_testlink_time;
1773877ae5beSKarsten Graul out:
17741020e1efSKarsten Graul 	schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
1775877ae5beSKarsten Graul }
1776877ae5beSKarsten Graul 
177700a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
177800a049cfSKarsten Graul {
177900a049cfSKarsten Graul 	struct net *net = sock_net(smc->clcsock->sk);
178000a049cfSKarsten Graul 
178100a049cfSKarsten Graul 	INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
1782b45e7f98SKarsten Graul 	INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work);
17839ec6bf19SKarsten Graul 	INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work);
178400a049cfSKarsten Graul 	INIT_LIST_HEAD(&lgr->llc_event_q);
178500a049cfSKarsten Graul 	spin_lock_init(&lgr->llc_event_q_lock);
1786555da9afSKarsten Graul 	spin_lock_init(&lgr->llc_flow_lock);
17876778a6beSKarsten Graul 	init_waitqueue_head(&lgr->llc_flow_waiter);
17886778a6beSKarsten Graul 	init_waitqueue_head(&lgr->llc_msg_waiter);
1789d5500667SKarsten Graul 	mutex_init(&lgr->llc_conf_mutex);
179000a049cfSKarsten Graul 	lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;
179100a049cfSKarsten Graul }
179200a049cfSKarsten Graul 
179300a049cfSKarsten Graul /* called after lgr was removed from lgr_list */
179400a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr)
179500a049cfSKarsten Graul {
179600a049cfSKarsten Graul 	smc_llc_event_flush(lgr);
17976778a6beSKarsten Graul 	wake_up_all(&lgr->llc_flow_waiter);
17986778a6beSKarsten Graul 	wake_up_all(&lgr->llc_msg_waiter);
179900a049cfSKarsten Graul 	cancel_work_sync(&lgr->llc_event_work);
1800b45e7f98SKarsten Graul 	cancel_work_sync(&lgr->llc_add_link_work);
18019ec6bf19SKarsten Graul 	cancel_work_sync(&lgr->llc_del_link_work);
1802555da9afSKarsten Graul 	if (lgr->delayed_event) {
1803555da9afSKarsten Graul 		kfree(lgr->delayed_event);
1804555da9afSKarsten Graul 		lgr->delayed_event = NULL;
1805555da9afSKarsten Graul 	}
180600a049cfSKarsten Graul }
180700a049cfSKarsten Graul 
18082a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link)
1809877ae5beSKarsten Graul {
1810877ae5beSKarsten Graul 	init_completion(&link->llc_testlink_resp);
1811877ae5beSKarsten Graul 	INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
18122a4c57a9SKarsten Graul 	return 0;
1813b32cf4abSKarsten Graul }
1814b32cf4abSKarsten Graul 
181500a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link)
1816b32cf4abSKarsten Graul {
18170a99be43SKarsten Graul 	pr_warn_ratelimited("smc: SMC-R lg %*phN link added: id %*phN, "
18180a99be43SKarsten Graul 			    "peerid %*phN, ibdev %s, ibport %d\n",
18190a99be43SKarsten Graul 			    SMC_LGR_ID_SIZE, &link->lgr->id,
18200a99be43SKarsten Graul 			    SMC_LGR_ID_SIZE, &link->link_uid,
18210a99be43SKarsten Graul 			    SMC_LGR_ID_SIZE, &link->peer_link_uid,
18220a99be43SKarsten Graul 			    link->smcibdev->ibdev->name, link->ibport);
1823877ae5beSKarsten Graul 	link->state = SMC_LNK_ACTIVE;
182400a049cfSKarsten Graul 	if (link->lgr->llc_testlink_time) {
182500a049cfSKarsten Graul 		link->llc_testlink_time = link->lgr->llc_testlink_time * HZ;
18261020e1efSKarsten Graul 		schedule_delayed_work(&link->llc_testlink_wrk,
1827877ae5beSKarsten Graul 				      link->llc_testlink_time);
1828877ae5beSKarsten Graul 	}
1829877ae5beSKarsten Graul }
1830877ae5beSKarsten Graul 
1831877ae5beSKarsten Graul /* called in worker context */
18320a99be43SKarsten Graul void smc_llc_link_clear(struct smc_link *link, bool log)
1833877ae5beSKarsten Graul {
18340a99be43SKarsten Graul 	if (log)
18350a99be43SKarsten Graul 		pr_warn_ratelimited("smc: SMC-R lg %*phN link removed: id %*phN"
18360a99be43SKarsten Graul 				    ", peerid %*phN, ibdev %s, ibport %d\n",
18370a99be43SKarsten Graul 				    SMC_LGR_ID_SIZE, &link->lgr->id,
18380a99be43SKarsten Graul 				    SMC_LGR_ID_SIZE, &link->link_uid,
18390a99be43SKarsten Graul 				    SMC_LGR_ID_SIZE, &link->peer_link_uid,
18400a99be43SKarsten Graul 				    link->smcibdev->ibdev->name, link->ibport);
18412140ac26SKarsten Graul 	complete(&link->llc_testlink_resp);
18422140ac26SKarsten Graul 	cancel_delayed_work_sync(&link->llc_testlink_wrk);
1843877ae5beSKarsten Graul }
1844877ae5beSKarsten Graul 
18453d88a21bSKarsten Graul /* register a new rtoken at the remote peer (for all links) */
18463d88a21bSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *send_link,
184744aa81ceSKarsten Graul 			    struct smc_buf_desc *rmb_desc)
184844aa81ceSKarsten Graul {
18493d88a21bSKarsten Graul 	struct smc_link_group *lgr = send_link->lgr;
18503d88a21bSKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
18513d88a21bSKarsten Graul 	int rc = 0;
185244aa81ceSKarsten Graul 
18533d88a21bSKarsten Graul 	rc = smc_llc_send_confirm_rkey(send_link, rmb_desc);
18543d88a21bSKarsten Graul 	if (rc)
18553d88a21bSKarsten Graul 		goto out;
185644aa81ceSKarsten Graul 	/* receive CONFIRM RKEY response from server over RoCE fabric */
18573d88a21bSKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
18583d88a21bSKarsten Graul 			      SMC_LLC_CONFIRM_RKEY);
18593d88a21bSKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
18603d88a21bSKarsten Graul 		rc = -EFAULT;
18613d88a21bSKarsten Graul out:
18623d88a21bSKarsten Graul 	if (qentry)
18633d88a21bSKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
18643d88a21bSKarsten Graul 	return rc;
186544aa81ceSKarsten Graul }
186644aa81ceSKarsten Graul 
186760e03c62SKarsten Graul /* unregister an rtoken at the remote peer */
18686d74c3a8SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link_group *lgr,
186960e03c62SKarsten Graul 			   struct smc_buf_desc *rmb_desc)
187060e03c62SKarsten Graul {
18716d74c3a8SKarsten Graul 	struct smc_llc_qentry *qentry = NULL;
18726d74c3a8SKarsten Graul 	struct smc_link *send_link;
18730b29ec64SUrsula Braun 	int rc = 0;
187460e03c62SKarsten Graul 
18756d74c3a8SKarsten Graul 	send_link = smc_llc_usable_link(lgr);
18766d74c3a8SKarsten Graul 	if (!send_link)
18776d74c3a8SKarsten Graul 		return -ENOLINK;
18786d74c3a8SKarsten Graul 
18796d74c3a8SKarsten Graul 	/* protected by llc_flow control */
18806d74c3a8SKarsten Graul 	rc = smc_llc_send_delete_rkey(send_link, rmb_desc);
188160e03c62SKarsten Graul 	if (rc)
188260e03c62SKarsten Graul 		goto out;
188360e03c62SKarsten Graul 	/* receive DELETE RKEY response from server over RoCE fabric */
18846d74c3a8SKarsten Graul 	qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
18856d74c3a8SKarsten Graul 			      SMC_LLC_DELETE_RKEY);
18866d74c3a8SKarsten Graul 	if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
188760e03c62SKarsten Graul 		rc = -EFAULT;
188860e03c62SKarsten Graul out:
18896d74c3a8SKarsten Graul 	if (qentry)
18906d74c3a8SKarsten Graul 		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
189160e03c62SKarsten Graul 	return rc;
189260e03c62SKarsten Graul }
189360e03c62SKarsten Graul 
189445fa8da0SKarsten Graul void smc_llc_link_set_uid(struct smc_link *link)
189545fa8da0SKarsten Graul {
189645fa8da0SKarsten Graul 	__be32 link_uid;
189745fa8da0SKarsten Graul 
189845fa8da0SKarsten Graul 	link_uid = htonl(*((u32 *)link->lgr->id) + link->link_id);
189945fa8da0SKarsten Graul 	memcpy(link->link_uid, &link_uid, SMC_LGR_ID_SIZE);
190045fa8da0SKarsten Graul }
190145fa8da0SKarsten Graul 
1902649758ffSKarsten Graul /* save peers link user id, used for debug purposes */
1903649758ffSKarsten Graul void smc_llc_save_peer_uid(struct smc_llc_qentry *qentry)
1904649758ffSKarsten Graul {
1905649758ffSKarsten Graul 	memcpy(qentry->link->peer_link_uid, qentry->msg.confirm_link.link_uid,
1906649758ffSKarsten Graul 	       SMC_LGR_ID_SIZE);
1907649758ffSKarsten Graul }
1908649758ffSKarsten Graul 
190992334cfcSKarsten Graul /* evaluate confirm link request or response */
191092334cfcSKarsten Graul int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry,
191192334cfcSKarsten Graul 			   enum smc_llc_reqresp type)
191292334cfcSKarsten Graul {
191345fa8da0SKarsten Graul 	if (type == SMC_LLC_REQ) {	/* SMC server assigns link_id */
191492334cfcSKarsten Graul 		qentry->link->link_id = qentry->msg.confirm_link.link_num;
191545fa8da0SKarsten Graul 		smc_llc_link_set_uid(qentry->link);
191645fa8da0SKarsten Graul 	}
191792334cfcSKarsten Graul 	if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
191892334cfcSKarsten Graul 		return -ENOTSUPP;
191992334cfcSKarsten Graul 	return 0;
192092334cfcSKarsten Graul }
192192334cfcSKarsten Graul 
19229bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/
19239bf9abeaSUrsula Braun 
19249bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
19259bf9abeaSUrsula Braun 	{
19269bf9abeaSUrsula Braun 		.handler	= smc_llc_rx_handler,
19279bf9abeaSUrsula Braun 		.type		= SMC_LLC_CONFIRM_LINK
19289bf9abeaSUrsula Braun 	},
19299bf9abeaSUrsula Braun 	{
1930313164daSKarsten Graul 		.handler	= smc_llc_rx_handler,
1931313164daSKarsten Graul 		.type		= SMC_LLC_TEST_LINK
1932313164daSKarsten Graul 	},
1933313164daSKarsten Graul 	{
19344ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
193552bedf37SKarsten Graul 		.type		= SMC_LLC_ADD_LINK
193652bedf37SKarsten Graul 	},
193752bedf37SKarsten Graul 	{
193852bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
193987f88cdaSKarsten Graul 		.type		= SMC_LLC_ADD_LINK_CONT
194087f88cdaSKarsten Graul 	},
194187f88cdaSKarsten Graul 	{
194287f88cdaSKarsten Graul 		.handler	= smc_llc_rx_handler,
194352bedf37SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK
194452bedf37SKarsten Graul 	},
194552bedf37SKarsten Graul 	{
194652bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
19474ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY
19484ed75de5SKarsten Graul 	},
19494ed75de5SKarsten Graul 	{
19504ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
19514ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_CONT
19524ed75de5SKarsten Graul 	},
19534ed75de5SKarsten Graul 	{
19544ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
19554ed75de5SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY
19564ed75de5SKarsten Graul 	},
19574ed75de5SKarsten Graul 	{
19589bf9abeaSUrsula Braun 		.handler	= NULL,
19599bf9abeaSUrsula Braun 	}
19609bf9abeaSUrsula Braun };
19619bf9abeaSUrsula Braun 
19629bf9abeaSUrsula Braun int __init smc_llc_init(void)
19639bf9abeaSUrsula Braun {
19649bf9abeaSUrsula Braun 	struct smc_wr_rx_handler *handler;
19659bf9abeaSUrsula Braun 	int rc = 0;
19669bf9abeaSUrsula Braun 
19679bf9abeaSUrsula Braun 	for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
19689bf9abeaSUrsula Braun 		INIT_HLIST_NODE(&handler->list);
19699bf9abeaSUrsula Braun 		rc = smc_wr_rx_register_handler(handler);
19709bf9abeaSUrsula Braun 		if (rc)
19719bf9abeaSUrsula Braun 			break;
19729bf9abeaSUrsula Braun 	}
19739bf9abeaSUrsula Braun 	return rc;
19749bf9abeaSUrsula Braun }
1975