xref: /linux/net/smc/smc_llc.c (revision 555da9af827d95134656fa459c8f3ece04dd867a)
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"
209bf9abeaSUrsula Braun 
210f627126SStefan Raspl #define SMC_LLC_DATA_LEN		40
220f627126SStefan Raspl 
230f627126SStefan Raspl struct smc_llc_hdr {
240f627126SStefan Raspl 	struct smc_wr_rx_hdr common;
250f627126SStefan Raspl 	u8 length;	/* 44 */
2652bedf37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD)
2752bedf37SKarsten Graul 	u8 reserved:4,
2852bedf37SKarsten Graul 	   add_link_rej_rsn:4;
2952bedf37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD)
3052bedf37SKarsten Graul 	u8 add_link_rej_rsn:4,
3152bedf37SKarsten Graul 	   reserved:4;
3252bedf37SKarsten Graul #endif
330f627126SStefan Raspl 	u8 flags;
340f627126SStefan Raspl };
350f627126SStefan Raspl 
3675d320d6SKarsten Graul #define SMC_LLC_FLAG_NO_RMBE_EYEC	0x03
3775d320d6SKarsten Graul 
380f627126SStefan Raspl struct smc_llc_msg_confirm_link {	/* type 0x01 */
390f627126SStefan Raspl 	struct smc_llc_hdr hd;
400f627126SStefan Raspl 	u8 sender_mac[ETH_ALEN];
410f627126SStefan Raspl 	u8 sender_gid[SMC_GID_SIZE];
420f627126SStefan Raspl 	u8 sender_qp_num[3];
430f627126SStefan Raspl 	u8 link_num;
440f627126SStefan Raspl 	u8 link_uid[SMC_LGR_ID_SIZE];
450f627126SStefan Raspl 	u8 max_links;
460f627126SStefan Raspl 	u8 reserved[9];
470f627126SStefan Raspl };
480f627126SStefan Raspl 
4952bedf37SKarsten Graul #define SMC_LLC_FLAG_ADD_LNK_REJ	0x40
5052bedf37SKarsten Graul #define SMC_LLC_REJ_RSN_NO_ALT_PATH	1
5152bedf37SKarsten Graul 
5252bedf37SKarsten Graul #define SMC_LLC_ADD_LNK_MAX_LINKS	2
5352bedf37SKarsten Graul 
5452bedf37SKarsten Graul struct smc_llc_msg_add_link {		/* type 0x02 */
5552bedf37SKarsten Graul 	struct smc_llc_hdr hd;
5652bedf37SKarsten Graul 	u8 sender_mac[ETH_ALEN];
5752bedf37SKarsten Graul 	u8 reserved2[2];
5852bedf37SKarsten Graul 	u8 sender_gid[SMC_GID_SIZE];
5952bedf37SKarsten Graul 	u8 sender_qp_num[3];
6052bedf37SKarsten Graul 	u8 link_num;
6152bedf37SKarsten Graul 	u8 flags2;	/* QP mtu */
6252bedf37SKarsten Graul 	u8 initial_psn[3];
6352bedf37SKarsten Graul 	u8 reserved[8];
6452bedf37SKarsten Graul };
6552bedf37SKarsten Graul 
6652bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ALL	0x40
6752bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ORDERLY	0x20
6852bedf37SKarsten Graul 
6952bedf37SKarsten Graul struct smc_llc_msg_del_link {		/* type 0x04 */
7052bedf37SKarsten Graul 	struct smc_llc_hdr hd;
7152bedf37SKarsten Graul 	u8 link_num;
7252bedf37SKarsten Graul 	__be32 reason;
7352bedf37SKarsten Graul 	u8 reserved[35];
7452bedf37SKarsten Graul } __packed;			/* format defined in RFC7609 */
7552bedf37SKarsten Graul 
76313164daSKarsten Graul struct smc_llc_msg_test_link {		/* type 0x07 */
77313164daSKarsten Graul 	struct smc_llc_hdr hd;
78313164daSKarsten Graul 	u8 user_data[16];
79313164daSKarsten Graul 	u8 reserved[24];
80313164daSKarsten Graul };
81313164daSKarsten Graul 
824ed75de5SKarsten Graul struct smc_rmb_rtoken {
834ed75de5SKarsten Graul 	union {
844ed75de5SKarsten Graul 		u8 num_rkeys;	/* first rtoken byte of CONFIRM LINK msg */
854ed75de5SKarsten Graul 				/* is actually the num of rtokens, first */
864ed75de5SKarsten Graul 				/* rtoken is always for the current link */
874ed75de5SKarsten Graul 		u8 link_id;	/* link id of the rtoken */
884ed75de5SKarsten Graul 	};
894ed75de5SKarsten Graul 	__be32 rmb_key;
904ed75de5SKarsten Graul 	__be64 rmb_vaddr;
914ed75de5SKarsten Graul } __packed;			/* format defined in RFC7609 */
924ed75de5SKarsten Graul 
934ed75de5SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG	3
944ed75de5SKarsten Graul 
954ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey {	/* type 0x06 */
964ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
974ed75de5SKarsten Graul 	struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
984ed75de5SKarsten Graul 	u8 reserved;
994ed75de5SKarsten Graul };
1004ed75de5SKarsten Graul 
1014ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey_cont {	/* type 0x08 */
1024ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1034ed75de5SKarsten Graul 	u8 num_rkeys;
1044ed75de5SKarsten Graul 	struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
1054ed75de5SKarsten Graul };
1064ed75de5SKarsten Graul 
1074ed75de5SKarsten Graul #define SMC_LLC_DEL_RKEY_MAX	8
1084ed75de5SKarsten Graul #define SMC_LLC_FLAG_RKEY_NEG	0x20
1094ed75de5SKarsten Graul 
1104ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey {	/* type 0x09 */
1114ed75de5SKarsten Graul 	struct smc_llc_hdr hd;
1124ed75de5SKarsten Graul 	u8 num_rkeys;
1134ed75de5SKarsten Graul 	u8 err_mask;
1144ed75de5SKarsten Graul 	u8 reserved[2];
1154ed75de5SKarsten Graul 	__be32 rkey[8];
1164ed75de5SKarsten Graul 	u8 reserved2[4];
1174ed75de5SKarsten Graul };
1184ed75de5SKarsten Graul 
1190f627126SStefan Raspl union smc_llc_msg {
1200f627126SStefan Raspl 	struct smc_llc_msg_confirm_link confirm_link;
12152bedf37SKarsten Graul 	struct smc_llc_msg_add_link add_link;
12252bedf37SKarsten Graul 	struct smc_llc_msg_del_link delete_link;
1234ed75de5SKarsten Graul 
1244ed75de5SKarsten Graul 	struct smc_llc_msg_confirm_rkey confirm_rkey;
1254ed75de5SKarsten Graul 	struct smc_llc_msg_confirm_rkey_cont confirm_rkey_cont;
1264ed75de5SKarsten Graul 	struct smc_llc_msg_delete_rkey delete_rkey;
1274ed75de5SKarsten Graul 
128313164daSKarsten Graul 	struct smc_llc_msg_test_link test_link;
1290f627126SStefan Raspl 	struct {
1300f627126SStefan Raspl 		struct smc_llc_hdr hdr;
1310f627126SStefan Raspl 		u8 data[SMC_LLC_DATA_LEN];
1320f627126SStefan Raspl 	} raw;
1330f627126SStefan Raspl };
1340f627126SStefan Raspl 
1350f627126SStefan Raspl #define SMC_LLC_FLAG_RESP		0x80
1360f627126SStefan Raspl 
1376c8968c4SKarsten Graul struct smc_llc_qentry {
1386c8968c4SKarsten Graul 	struct list_head list;
1396c8968c4SKarsten Graul 	struct smc_link *link;
1406c8968c4SKarsten Graul 	union smc_llc_msg msg;
1416c8968c4SKarsten Graul };
1426c8968c4SKarsten Graul 
143*555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
144*555da9afSKarsten Graul {
145*555da9afSKarsten Graul 	struct smc_llc_qentry *qentry = flow->qentry;
146*555da9afSKarsten Graul 
147*555da9afSKarsten Graul 	flow->qentry = NULL;
148*555da9afSKarsten Graul 	return qentry;
149*555da9afSKarsten Graul }
150*555da9afSKarsten Graul 
151*555da9afSKarsten Graul void smc_llc_flow_qentry_del(struct smc_llc_flow *flow)
152*555da9afSKarsten Graul {
153*555da9afSKarsten Graul 	struct smc_llc_qentry *qentry;
154*555da9afSKarsten Graul 
155*555da9afSKarsten Graul 	if (flow->qentry) {
156*555da9afSKarsten Graul 		qentry = flow->qentry;
157*555da9afSKarsten Graul 		flow->qentry = NULL;
158*555da9afSKarsten Graul 		kfree(qentry);
159*555da9afSKarsten Graul 	}
160*555da9afSKarsten Graul }
161*555da9afSKarsten Graul 
162*555da9afSKarsten Graul static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow,
163*555da9afSKarsten Graul 					   struct smc_llc_qentry *qentry)
164*555da9afSKarsten Graul {
165*555da9afSKarsten Graul 	flow->qentry = qentry;
166*555da9afSKarsten Graul }
167*555da9afSKarsten Graul 
168*555da9afSKarsten Graul /* try to start a new llc flow, initiated by an incoming llc msg */
169*555da9afSKarsten Graul static bool smc_llc_flow_start(struct smc_llc_flow *flow,
170*555da9afSKarsten Graul 			       struct smc_llc_qentry *qentry)
171*555da9afSKarsten Graul {
172*555da9afSKarsten Graul 	struct smc_link_group *lgr = qentry->link->lgr;
173*555da9afSKarsten Graul 
174*555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
175*555da9afSKarsten Graul 	if (flow->type) {
176*555da9afSKarsten Graul 		/* a flow is already active */
177*555da9afSKarsten Graul 		if ((qentry->msg.raw.hdr.common.type == SMC_LLC_ADD_LINK ||
178*555da9afSKarsten Graul 		     qentry->msg.raw.hdr.common.type == SMC_LLC_DELETE_LINK) &&
179*555da9afSKarsten Graul 		    !lgr->delayed_event) {
180*555da9afSKarsten Graul 			lgr->delayed_event = qentry;
181*555da9afSKarsten Graul 		} else {
182*555da9afSKarsten Graul 			/* forget this llc request */
183*555da9afSKarsten Graul 			kfree(qentry);
184*555da9afSKarsten Graul 		}
185*555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
186*555da9afSKarsten Graul 		return false;
187*555da9afSKarsten Graul 	}
188*555da9afSKarsten Graul 	switch (qentry->msg.raw.hdr.common.type) {
189*555da9afSKarsten Graul 	case SMC_LLC_ADD_LINK:
190*555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_ADD_LINK;
191*555da9afSKarsten Graul 		break;
192*555da9afSKarsten Graul 	case SMC_LLC_DELETE_LINK:
193*555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_DEL_LINK;
194*555da9afSKarsten Graul 		break;
195*555da9afSKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
196*555da9afSKarsten Graul 	case SMC_LLC_DELETE_RKEY:
197*555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_RKEY;
198*555da9afSKarsten Graul 		break;
199*555da9afSKarsten Graul 	default:
200*555da9afSKarsten Graul 		flow->type = SMC_LLC_FLOW_NONE;
201*555da9afSKarsten Graul 	}
202*555da9afSKarsten Graul 	if (qentry == lgr->delayed_event)
203*555da9afSKarsten Graul 		lgr->delayed_event = NULL;
204*555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
205*555da9afSKarsten Graul 	smc_llc_flow_qentry_set(flow, qentry);
206*555da9afSKarsten Graul 	return true;
207*555da9afSKarsten Graul }
208*555da9afSKarsten Graul 
209*555da9afSKarsten Graul /* start a new local llc flow, wait till current flow finished */
210*555da9afSKarsten Graul int smc_llc_flow_initiate(struct smc_link_group *lgr,
211*555da9afSKarsten Graul 			  enum smc_llc_flowtype type)
212*555da9afSKarsten Graul {
213*555da9afSKarsten Graul 	enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE;
214*555da9afSKarsten Graul 	int rc;
215*555da9afSKarsten Graul 
216*555da9afSKarsten Graul 	/* all flows except confirm_rkey and delete_rkey are exclusive,
217*555da9afSKarsten Graul 	 * confirm/delete rkey flows can run concurrently (local and remote)
218*555da9afSKarsten Graul 	 */
219*555da9afSKarsten Graul 	if (type == SMC_LLC_FLOW_RKEY)
220*555da9afSKarsten Graul 		allowed_remote = SMC_LLC_FLOW_RKEY;
221*555da9afSKarsten Graul again:
222*555da9afSKarsten Graul 	if (list_empty(&lgr->list))
223*555da9afSKarsten Graul 		return -ENODEV;
224*555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
225*555da9afSKarsten Graul 	if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
226*555da9afSKarsten Graul 	    (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
227*555da9afSKarsten Graul 	     lgr->llc_flow_rmt.type == allowed_remote)) {
228*555da9afSKarsten Graul 		lgr->llc_flow_lcl.type = type;
229*555da9afSKarsten Graul 		spin_unlock_bh(&lgr->llc_flow_lock);
230*555da9afSKarsten Graul 		return 0;
231*555da9afSKarsten Graul 	}
232*555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
233*555da9afSKarsten Graul 	rc = wait_event_interruptible_timeout(lgr->llc_waiter,
234*555da9afSKarsten Graul 			(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
235*555da9afSKarsten Graul 			 (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
236*555da9afSKarsten Graul 			  lgr->llc_flow_rmt.type == allowed_remote)),
237*555da9afSKarsten Graul 			SMC_LLC_WAIT_TIME);
238*555da9afSKarsten Graul 	if (!rc)
239*555da9afSKarsten Graul 		return -ETIMEDOUT;
240*555da9afSKarsten Graul 	goto again;
241*555da9afSKarsten Graul }
242*555da9afSKarsten Graul 
243*555da9afSKarsten Graul /* finish the current llc flow */
244*555da9afSKarsten Graul void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)
245*555da9afSKarsten Graul {
246*555da9afSKarsten Graul 	spin_lock_bh(&lgr->llc_flow_lock);
247*555da9afSKarsten Graul 	memset(flow, 0, sizeof(*flow));
248*555da9afSKarsten Graul 	flow->type = SMC_LLC_FLOW_NONE;
249*555da9afSKarsten Graul 	spin_unlock_bh(&lgr->llc_flow_lock);
250*555da9afSKarsten Graul 	if (!list_empty(&lgr->list) && lgr->delayed_event &&
251*555da9afSKarsten Graul 	    flow == &lgr->llc_flow_lcl)
252*555da9afSKarsten Graul 		schedule_work(&lgr->llc_event_work);
253*555da9afSKarsten Graul 	else
254*555da9afSKarsten Graul 		wake_up_interruptible(&lgr->llc_waiter);
255*555da9afSKarsten Graul }
256*555da9afSKarsten Graul 
257*555da9afSKarsten Graul /* lnk is optional and used for early wakeup when link goes down, useful in
258*555da9afSKarsten Graul  * cases where we wait for a response on the link after we sent a request
259*555da9afSKarsten Graul  */
260*555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr,
261*555da9afSKarsten Graul 				    struct smc_link *lnk,
262*555da9afSKarsten Graul 				    int time_out, u8 exp_msg)
263*555da9afSKarsten Graul {
264*555da9afSKarsten Graul 	struct smc_llc_flow *flow = &lgr->llc_flow_lcl;
265*555da9afSKarsten Graul 
266*555da9afSKarsten Graul 	wait_event_interruptible_timeout(lgr->llc_waiter,
267*555da9afSKarsten Graul 					 (flow->qentry ||
268*555da9afSKarsten Graul 					  (lnk && !smc_link_usable(lnk)) ||
269*555da9afSKarsten Graul 					  list_empty(&lgr->list)),
270*555da9afSKarsten Graul 					 time_out);
271*555da9afSKarsten Graul 	if (!flow->qentry ||
272*555da9afSKarsten Graul 	    (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) {
273*555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
274*555da9afSKarsten Graul 		goto out;
275*555da9afSKarsten Graul 	}
276*555da9afSKarsten Graul 	if (exp_msg && flow->qentry->msg.raw.hdr.common.type != exp_msg) {
277*555da9afSKarsten Graul 		if (exp_msg == SMC_LLC_ADD_LINK &&
278*555da9afSKarsten Graul 		    flow->qentry->msg.raw.hdr.common.type ==
279*555da9afSKarsten Graul 		    SMC_LLC_DELETE_LINK) {
280*555da9afSKarsten Graul 			/* flow_start will delay the unexpected msg */
281*555da9afSKarsten Graul 			smc_llc_flow_start(&lgr->llc_flow_lcl,
282*555da9afSKarsten Graul 					   smc_llc_flow_qentry_clr(flow));
283*555da9afSKarsten Graul 			return NULL;
284*555da9afSKarsten Graul 		}
285*555da9afSKarsten Graul 		smc_llc_flow_qentry_del(flow);
286*555da9afSKarsten Graul 	}
287*555da9afSKarsten Graul out:
288*555da9afSKarsten Graul 	return flow->qentry;
289*555da9afSKarsten Graul }
290*555da9afSKarsten Graul 
2919bf9abeaSUrsula Braun /********************************** send *************************************/
2929bf9abeaSUrsula Braun 
2939bf9abeaSUrsula Braun struct smc_llc_tx_pend {
2949bf9abeaSUrsula Braun };
2959bf9abeaSUrsula Braun 
2969bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */
2979bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend,
2989bf9abeaSUrsula Braun 			       struct smc_link *link,
2999bf9abeaSUrsula Braun 			       enum ib_wc_status wc_status)
3009bf9abeaSUrsula Braun {
3019bf9abeaSUrsula Braun 	/* future work: handle wc_status error for recovery and failover */
3029bf9abeaSUrsula Braun }
3039bf9abeaSUrsula Braun 
3049bf9abeaSUrsula Braun /**
3059bf9abeaSUrsula Braun  * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits
3069bf9abeaSUrsula Braun  * @link: Pointer to SMC link used for sending LLC control message.
3079bf9abeaSUrsula Braun  * @wr_buf: Out variable returning pointer to work request payload buffer.
3089bf9abeaSUrsula Braun  * @pend: Out variable returning pointer to private pending WR tracking.
3099bf9abeaSUrsula Braun  *	  It's the context the transmit complete handler will get.
3109bf9abeaSUrsula Braun  *
3119bf9abeaSUrsula Braun  * Reserves and pre-fills an entry for a pending work request send/tx.
3129bf9abeaSUrsula Braun  * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx.
3139bf9abeaSUrsula Braun  * Can sleep due to smc_get_ctrl_buf (if not in softirq context).
3149bf9abeaSUrsula Braun  *
3159bf9abeaSUrsula Braun  * Return: 0 on success, otherwise an error value.
3169bf9abeaSUrsula Braun  */
3179bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link,
3189bf9abeaSUrsula Braun 				    struct smc_wr_buf **wr_buf,
3199bf9abeaSUrsula Braun 				    struct smc_wr_tx_pend_priv **pend)
3209bf9abeaSUrsula Braun {
3219bf9abeaSUrsula Braun 	int rc;
3229bf9abeaSUrsula Braun 
323ad6f317fSUrsula Braun 	rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL,
324ad6f317fSUrsula Braun 				     pend);
3259bf9abeaSUrsula Braun 	if (rc < 0)
3269bf9abeaSUrsula Braun 		return rc;
3279bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3289bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE,
3299bf9abeaSUrsula Braun 		"must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)");
3309bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3319bf9abeaSUrsula Braun 		sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE,
3329bf9abeaSUrsula 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()");
3339bf9abeaSUrsula Braun 	BUILD_BUG_ON_MSG(
3349bf9abeaSUrsula Braun 		sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
3359bf9abeaSUrsula Braun 		"must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)");
3369bf9abeaSUrsula Braun 	return 0;
3379bf9abeaSUrsula Braun }
3389bf9abeaSUrsula Braun 
3399bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */
340947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link,
3419bf9abeaSUrsula Braun 			      enum smc_llc_reqresp reqresp)
3429bf9abeaSUrsula Braun {
34300e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
3449bf9abeaSUrsula Braun 	struct smc_llc_msg_confirm_link *confllc;
3459bf9abeaSUrsula Braun 	struct smc_wr_tx_pend_priv *pend;
3469bf9abeaSUrsula Braun 	struct smc_wr_buf *wr_buf;
3479bf9abeaSUrsula Braun 	int rc;
3489bf9abeaSUrsula Braun 
3499bf9abeaSUrsula Braun 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
3509bf9abeaSUrsula Braun 	if (rc)
3519bf9abeaSUrsula Braun 		return rc;
3529bf9abeaSUrsula Braun 	confllc = (struct smc_llc_msg_confirm_link *)wr_buf;
3539bf9abeaSUrsula Braun 	memset(confllc, 0, sizeof(*confllc));
3549bf9abeaSUrsula Braun 	confllc->hd.common.type = SMC_LLC_CONFIRM_LINK;
3559bf9abeaSUrsula Braun 	confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link);
35675d320d6SKarsten Graul 	confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC;
3579bf9abeaSUrsula Braun 	if (reqresp == SMC_LLC_RESP)
3589bf9abeaSUrsula Braun 		confllc->hd.flags |= SMC_LLC_FLAG_RESP;
359947541f3SUrsula Braun 	memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1],
360947541f3SUrsula Braun 	       ETH_ALEN);
3617005ada6SUrsula Braun 	memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE);
3629bf9abeaSUrsula Braun 	hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
3632be922f3SKarsten Graul 	confllc->link_num = link->link_id;
3649bf9abeaSUrsula Braun 	memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE);
36552bedf37SKarsten Graul 	confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; /* enforce peer resp. */
36652bedf37SKarsten Graul 	/* send llc message */
36752bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
36852bedf37SKarsten Graul 	return rc;
36952bedf37SKarsten Graul }
37052bedf37SKarsten Graul 
37144aa81ceSKarsten Graul /* send LLC confirm rkey request */
37244aa81ceSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *link,
37344aa81ceSKarsten Graul 				     struct smc_buf_desc *rmb_desc)
37444aa81ceSKarsten Graul {
37544aa81ceSKarsten Graul 	struct smc_llc_msg_confirm_rkey *rkeyllc;
37644aa81ceSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
37744aa81ceSKarsten Graul 	struct smc_wr_buf *wr_buf;
37844aa81ceSKarsten Graul 	int rc;
37944aa81ceSKarsten Graul 
38044aa81ceSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
38144aa81ceSKarsten Graul 	if (rc)
38244aa81ceSKarsten Graul 		return rc;
38344aa81ceSKarsten Graul 	rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf;
38444aa81ceSKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
38544aa81ceSKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY;
38644aa81ceSKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey);
38744aa81ceSKarsten Graul 	rkeyllc->rtoken[0].rmb_key =
388387707fdSKarsten Graul 		htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
38944aa81ceSKarsten Graul 	rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
390387707fdSKarsten Graul 		(u64)sg_dma_address(rmb_desc->sgt[link->link_idx].sgl));
39144aa81ceSKarsten Graul 	/* send llc message */
39244aa81ceSKarsten Graul 	rc = smc_wr_tx_send(link, pend);
39344aa81ceSKarsten Graul 	return rc;
39444aa81ceSKarsten Graul }
39544aa81ceSKarsten Graul 
39660e03c62SKarsten Graul /* send LLC delete rkey request */
39760e03c62SKarsten Graul static int smc_llc_send_delete_rkey(struct smc_link *link,
39860e03c62SKarsten Graul 				    struct smc_buf_desc *rmb_desc)
39960e03c62SKarsten Graul {
40060e03c62SKarsten Graul 	struct smc_llc_msg_delete_rkey *rkeyllc;
40160e03c62SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
40260e03c62SKarsten Graul 	struct smc_wr_buf *wr_buf;
40360e03c62SKarsten Graul 	int rc;
40460e03c62SKarsten Graul 
40560e03c62SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
40660e03c62SKarsten Graul 	if (rc)
40760e03c62SKarsten Graul 		return rc;
40860e03c62SKarsten Graul 	rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf;
40960e03c62SKarsten Graul 	memset(rkeyllc, 0, sizeof(*rkeyllc));
41060e03c62SKarsten Graul 	rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY;
41160e03c62SKarsten Graul 	rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey);
41260e03c62SKarsten Graul 	rkeyllc->num_rkeys = 1;
413387707fdSKarsten Graul 	rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
41460e03c62SKarsten Graul 	/* send llc message */
41560e03c62SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
41660e03c62SKarsten Graul 	return rc;
41760e03c62SKarsten Graul }
41860e03c62SKarsten Graul 
4192a4c57a9SKarsten Graul /* prepare an add link message */
4202a4c57a9SKarsten Graul static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc,
4217005ada6SUrsula Braun 				  struct smc_link *link, u8 mac[], u8 gid[],
4222a4c57a9SKarsten Graul 				  enum smc_llc_reqresp reqresp)
4232a4c57a9SKarsten Graul {
4242a4c57a9SKarsten Graul 	memset(addllc, 0, sizeof(*addllc));
4252a4c57a9SKarsten Graul 	addllc->hd.common.type = SMC_LLC_ADD_LINK;
4262a4c57a9SKarsten Graul 	addllc->hd.length = sizeof(struct smc_llc_msg_add_link);
4272a4c57a9SKarsten Graul 	if (reqresp == SMC_LLC_RESP) {
4282a4c57a9SKarsten Graul 		addllc->hd.flags |= SMC_LLC_FLAG_RESP;
4292a4c57a9SKarsten Graul 		/* always reject more links for now */
4302a4c57a9SKarsten Graul 		addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
4312a4c57a9SKarsten Graul 		addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
4322a4c57a9SKarsten Graul 	}
4332a4c57a9SKarsten Graul 	memcpy(addllc->sender_mac, mac, ETH_ALEN);
4342a4c57a9SKarsten Graul 	memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
4352a4c57a9SKarsten Graul }
4362a4c57a9SKarsten Graul 
43752bedf37SKarsten Graul /* send ADD LINK request or response */
4387005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
43952bedf37SKarsten Graul 			  enum smc_llc_reqresp reqresp)
44052bedf37SKarsten Graul {
44152bedf37SKarsten Graul 	struct smc_llc_msg_add_link *addllc;
44252bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
44352bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
44452bedf37SKarsten Graul 	int rc;
44552bedf37SKarsten Graul 
44652bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
44752bedf37SKarsten Graul 	if (rc)
44852bedf37SKarsten Graul 		return rc;
44952bedf37SKarsten Graul 	addllc = (struct smc_llc_msg_add_link *)wr_buf;
4502a4c57a9SKarsten Graul 	smc_llc_prep_add_link(addllc, link, mac, gid, reqresp);
45152bedf37SKarsten Graul 	/* send llc message */
45252bedf37SKarsten Graul 	rc = smc_wr_tx_send(link, pend);
45352bedf37SKarsten Graul 	return rc;
45452bedf37SKarsten Graul }
45552bedf37SKarsten Graul 
4562a4c57a9SKarsten Graul /* prepare a delete link message */
4572a4c57a9SKarsten Graul static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc,
4582a4c57a9SKarsten Graul 				     struct smc_link *link,
4590d18a0cbSKarsten Graul 				     enum smc_llc_reqresp reqresp, bool orderly)
4602a4c57a9SKarsten Graul {
4612a4c57a9SKarsten Graul 	memset(delllc, 0, sizeof(*delllc));
4622a4c57a9SKarsten Graul 	delllc->hd.common.type = SMC_LLC_DELETE_LINK;
4632a4c57a9SKarsten Graul 	delllc->hd.length = sizeof(struct smc_llc_msg_add_link);
4642a4c57a9SKarsten Graul 	if (reqresp == SMC_LLC_RESP)
4652a4c57a9SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_RESP;
4662a4c57a9SKarsten Graul 	/* DEL_LINK_ALL because only 1 link supported */
4672a4c57a9SKarsten Graul 	delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
4680d18a0cbSKarsten Graul 	if (orderly)
4692a4c57a9SKarsten Graul 		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
4702a4c57a9SKarsten Graul 	delllc->link_num = link->link_id;
4712a4c57a9SKarsten Graul }
4722a4c57a9SKarsten Graul 
47352bedf37SKarsten Graul /* send DELETE LINK request or response */
47452bedf37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link,
4750d18a0cbSKarsten Graul 			     enum smc_llc_reqresp reqresp, bool orderly)
47652bedf37SKarsten Graul {
47752bedf37SKarsten Graul 	struct smc_llc_msg_del_link *delllc;
47852bedf37SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
47952bedf37SKarsten Graul 	struct smc_wr_buf *wr_buf;
48052bedf37SKarsten Graul 	int rc;
48152bedf37SKarsten Graul 
48252bedf37SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
48352bedf37SKarsten Graul 	if (rc)
48452bedf37SKarsten Graul 		return rc;
48552bedf37SKarsten Graul 	delllc = (struct smc_llc_msg_del_link *)wr_buf;
4860d18a0cbSKarsten Graul 	smc_llc_prep_delete_link(delllc, link, reqresp, orderly);
4879bf9abeaSUrsula Braun 	/* send llc message */
4889bf9abeaSUrsula Braun 	rc = smc_wr_tx_send(link, pend);
4899bf9abeaSUrsula Braun 	return rc;
4909bf9abeaSUrsula Braun }
4919bf9abeaSUrsula Braun 
492d97935faSKarsten Graul /* send LLC test link request */
493d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
494313164daSKarsten Graul {
495313164daSKarsten Graul 	struct smc_llc_msg_test_link *testllc;
496313164daSKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
497313164daSKarsten Graul 	struct smc_wr_buf *wr_buf;
498313164daSKarsten Graul 	int rc;
499313164daSKarsten Graul 
500313164daSKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
501313164daSKarsten Graul 	if (rc)
502313164daSKarsten Graul 		return rc;
503313164daSKarsten Graul 	testllc = (struct smc_llc_msg_test_link *)wr_buf;
504313164daSKarsten Graul 	memset(testllc, 0, sizeof(*testllc));
505313164daSKarsten Graul 	testllc->hd.common.type = SMC_LLC_TEST_LINK;
506313164daSKarsten Graul 	testllc->hd.length = sizeof(struct smc_llc_msg_test_link);
507313164daSKarsten Graul 	memcpy(testllc->user_data, user_data, sizeof(testllc->user_data));
508313164daSKarsten Graul 	/* send llc message */
509313164daSKarsten Graul 	rc = smc_wr_tx_send(link, pend);
510313164daSKarsten Graul 	return rc;
511313164daSKarsten Graul }
512313164daSKarsten Graul 
5136c8968c4SKarsten Graul /* schedule an llc send on link, may wait for buffers */
5146c8968c4SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf)
5154ed75de5SKarsten Graul {
5164ed75de5SKarsten Graul 	struct smc_wr_tx_pend_priv *pend;
5174ed75de5SKarsten Graul 	struct smc_wr_buf *wr_buf;
5184ed75de5SKarsten Graul 	int rc;
5194ed75de5SKarsten Graul 
5206c8968c4SKarsten Graul 	if (!smc_link_usable(link))
5216c8968c4SKarsten Graul 		return -ENOLINK;
5226c8968c4SKarsten Graul 	rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
5234ed75de5SKarsten Graul 	if (rc)
5246c8968c4SKarsten Graul 		return rc;
5256c8968c4SKarsten Graul 	memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
5266c8968c4SKarsten Graul 	return smc_wr_tx_send(link, pend);
5274ed75de5SKarsten Graul }
5284ed75de5SKarsten Graul 
5299bf9abeaSUrsula Braun /********************************* receive ***********************************/
5309bf9abeaSUrsula Braun 
5319bf9abeaSUrsula Braun static void smc_llc_rx_confirm_link(struct smc_link *link,
5329bf9abeaSUrsula Braun 				    struct smc_llc_msg_confirm_link *llc)
5339bf9abeaSUrsula Braun {
53400e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
535ef79d439SKarsten Graul 	int conf_rc = 0;
5369bf9abeaSUrsula Braun 
53775d320d6SKarsten Graul 	/* RMBE eyecatchers are not supported */
538ef79d439SKarsten Graul 	if (!(llc->hd.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
53975d320d6SKarsten Graul 		conf_rc = ENOTSUPP;
54075d320d6SKarsten Graul 
54152bedf37SKarsten Graul 	if (lgr->role == SMC_CLNT &&
54252bedf37SKarsten Graul 	    link->state == SMC_LNK_ACTIVATING) {
54375d320d6SKarsten Graul 		link->llc_confirm_rc = conf_rc;
5449bf9abeaSUrsula Braun 		link->link_id = llc->link_num;
5459bf9abeaSUrsula Braun 		complete(&link->llc_confirm);
5469bf9abeaSUrsula Braun 	}
5479bf9abeaSUrsula Braun }
5489bf9abeaSUrsula Braun 
54952bedf37SKarsten Graul static void smc_llc_rx_add_link(struct smc_link *link,
55052bedf37SKarsten Graul 				struct smc_llc_msg_add_link *llc)
55152bedf37SKarsten Graul {
55200e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
55352bedf37SKarsten Graul 
55452bedf37SKarsten Graul 	if (link->state == SMC_LNK_ACTIVATING) {
55552bedf37SKarsten Graul 		complete(&link->llc_add);
55652bedf37SKarsten Graul 		return;
55752bedf37SKarsten Graul 	}
55852bedf37SKarsten Graul 
55952bedf37SKarsten Graul 	if (lgr->role == SMC_SERV) {
5602a4c57a9SKarsten Graul 		smc_llc_prep_add_link(llc, link,
56152bedf37SKarsten Graul 				link->smcibdev->mac[link->ibport - 1],
5627005ada6SUrsula Braun 				link->gid, SMC_LLC_REQ);
56352bedf37SKarsten Graul 
56452bedf37SKarsten Graul 	} else {
5652a4c57a9SKarsten Graul 		smc_llc_prep_add_link(llc, link,
56652bedf37SKarsten Graul 				link->smcibdev->mac[link->ibport - 1],
5677005ada6SUrsula Braun 				link->gid, SMC_LLC_RESP);
56852bedf37SKarsten Graul 	}
5696c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
57052bedf37SKarsten Graul }
57152bedf37SKarsten Graul 
57252bedf37SKarsten Graul static void smc_llc_rx_delete_link(struct smc_link *link,
57352bedf37SKarsten Graul 				   struct smc_llc_msg_del_link *llc)
57452bedf37SKarsten Graul {
57500e5fb26SStefan Raspl 	struct smc_link_group *lgr = smc_get_lgr(link);
57652bedf37SKarsten Graul 
5779651b934SKarsten Graul 	smc_lgr_forget(lgr);
5780d18a0cbSKarsten Graul 	smc_llc_link_deleting(link);
5790d18a0cbSKarsten Graul 	if (lgr->role == SMC_SERV) {
5800d18a0cbSKarsten Graul 		/* client asks to delete this link, send request */
5810d18a0cbSKarsten Graul 		smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ, true);
58252bedf37SKarsten Graul 	} else {
5830d18a0cbSKarsten Graul 		/* server requests to delete this link, send response */
5840d18a0cbSKarsten Graul 		smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true);
58552bedf37SKarsten Graul 	}
5866c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
587f528ba24SUrsula Braun 	smc_lgr_terminate_sched(lgr);
58852bedf37SKarsten Graul }
58952bedf37SKarsten Graul 
590313164daSKarsten Graul static void smc_llc_rx_test_link(struct smc_link *link,
591313164daSKarsten Graul 				 struct smc_llc_msg_test_link *llc)
592313164daSKarsten Graul {
593d97935faSKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
5946c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
595313164daSKarsten Graul }
596313164daSKarsten Graul 
5974ed75de5SKarsten Graul static void smc_llc_rx_confirm_rkey(struct smc_link *link,
5984ed75de5SKarsten Graul 				    struct smc_llc_msg_confirm_rkey *llc)
5994ed75de5SKarsten Graul {
6004ed75de5SKarsten Graul 	int rc;
6014ed75de5SKarsten Graul 
602387707fdSKarsten Graul 	rc = smc_rtoken_add(link,
6034ed75de5SKarsten Graul 			    llc->rtoken[0].rmb_vaddr,
6044ed75de5SKarsten Graul 			    llc->rtoken[0].rmb_key);
6054ed75de5SKarsten Graul 
6064ed75de5SKarsten Graul 	/* ignore rtokens for other links, we have only one link */
6074ed75de5SKarsten Graul 
6084ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
6094ed75de5SKarsten Graul 	if (rc < 0)
6104ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
6116c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
6124ed75de5SKarsten Graul }
6134ed75de5SKarsten Graul 
6144ed75de5SKarsten Graul static void smc_llc_rx_confirm_rkey_cont(struct smc_link *link,
6154ed75de5SKarsten Graul 				      struct smc_llc_msg_confirm_rkey_cont *llc)
6164ed75de5SKarsten Graul {
6174ed75de5SKarsten Graul 	/* ignore rtokens for other links, we have only one link */
6184ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
6196c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
6204ed75de5SKarsten Graul }
6214ed75de5SKarsten Graul 
6224ed75de5SKarsten Graul static void smc_llc_rx_delete_rkey(struct smc_link *link,
6234ed75de5SKarsten Graul 				   struct smc_llc_msg_delete_rkey *llc)
6244ed75de5SKarsten Graul {
6254ed75de5SKarsten Graul 	u8 err_mask = 0;
6264ed75de5SKarsten Graul 	int i, max;
6274ed75de5SKarsten Graul 
6284ed75de5SKarsten Graul 	max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
6294ed75de5SKarsten Graul 	for (i = 0; i < max; i++) {
630387707fdSKarsten Graul 		if (smc_rtoken_delete(link, llc->rkey[i]))
6314ed75de5SKarsten Graul 			err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
6324ed75de5SKarsten Graul 	}
6334ed75de5SKarsten Graul 
6344ed75de5SKarsten Graul 	if (err_mask) {
6354ed75de5SKarsten Graul 		llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
6364ed75de5SKarsten Graul 		llc->err_mask = err_mask;
6374ed75de5SKarsten Graul 	}
6384ed75de5SKarsten Graul 
6394ed75de5SKarsten Graul 	llc->hd.flags |= SMC_LLC_FLAG_RESP;
6406c8968c4SKarsten Graul 	smc_llc_send_message(link, llc);
6414ed75de5SKarsten Graul }
6424ed75de5SKarsten Graul 
6436c8968c4SKarsten Graul /* flush the llc event queue */
64400a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr)
6459bf9abeaSUrsula Braun {
6466c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry, *q;
6479bf9abeaSUrsula Braun 
6486c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
6496c8968c4SKarsten Graul 	list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
6506c8968c4SKarsten Graul 		list_del_init(&qentry->list);
6516c8968c4SKarsten Graul 		kfree(qentry);
6526c8968c4SKarsten Graul 	}
6536c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
6546c8968c4SKarsten Graul }
6556c8968c4SKarsten Graul 
6566c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
6576c8968c4SKarsten Graul {
6586c8968c4SKarsten Graul 	union smc_llc_msg *llc = &qentry->msg;
6596c8968c4SKarsten Graul 	struct smc_link *link = qentry->link;
6606c8968c4SKarsten Graul 
661d854fcbfSKarsten Graul 	if (!smc_link_usable(link))
6626c8968c4SKarsten Graul 		goto out;
663313164daSKarsten Graul 
664313164daSKarsten Graul 	switch (llc->raw.hdr.common.type) {
665313164daSKarsten Graul 	case SMC_LLC_TEST_LINK:
666313164daSKarsten Graul 		smc_llc_rx_test_link(link, &llc->test_link);
667313164daSKarsten Graul 		break;
668313164daSKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
6699bf9abeaSUrsula Braun 		smc_llc_rx_confirm_link(link, &llc->confirm_link);
670313164daSKarsten Graul 		break;
67152bedf37SKarsten Graul 	case SMC_LLC_ADD_LINK:
67252bedf37SKarsten Graul 		smc_llc_rx_add_link(link, &llc->add_link);
67352bedf37SKarsten Graul 		break;
67452bedf37SKarsten Graul 	case SMC_LLC_DELETE_LINK:
67552bedf37SKarsten Graul 		smc_llc_rx_delete_link(link, &llc->delete_link);
67652bedf37SKarsten Graul 		break;
6774ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
6784ed75de5SKarsten Graul 		smc_llc_rx_confirm_rkey(link, &llc->confirm_rkey);
6794ed75de5SKarsten Graul 		break;
6804ed75de5SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
6814ed75de5SKarsten Graul 		smc_llc_rx_confirm_rkey_cont(link, &llc->confirm_rkey_cont);
6824ed75de5SKarsten Graul 		break;
6834ed75de5SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
6844ed75de5SKarsten Graul 		smc_llc_rx_delete_rkey(link, &llc->delete_rkey);
6854ed75de5SKarsten Graul 		break;
686313164daSKarsten Graul 	}
6876c8968c4SKarsten Graul out:
6886c8968c4SKarsten Graul 	kfree(qentry);
6896c8968c4SKarsten Graul }
6906c8968c4SKarsten Graul 
6916c8968c4SKarsten Graul /* worker to process llc messages on the event queue */
6926c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work)
6936c8968c4SKarsten Graul {
6946c8968c4SKarsten Graul 	struct smc_link_group *lgr = container_of(work, struct smc_link_group,
6956c8968c4SKarsten Graul 						  llc_event_work);
6966c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
6976c8968c4SKarsten Graul 
698*555da9afSKarsten Graul 	if (!lgr->llc_flow_lcl.type && lgr->delayed_event) {
699*555da9afSKarsten Graul 		if (smc_link_usable(lgr->delayed_event->link)) {
700*555da9afSKarsten Graul 			smc_llc_event_handler(lgr->delayed_event);
701*555da9afSKarsten Graul 		} else {
702*555da9afSKarsten Graul 			qentry = lgr->delayed_event;
703*555da9afSKarsten Graul 			lgr->delayed_event = NULL;
704*555da9afSKarsten Graul 			kfree(qentry);
705*555da9afSKarsten Graul 		}
706*555da9afSKarsten Graul 	}
707*555da9afSKarsten Graul 
7086c8968c4SKarsten Graul again:
7096c8968c4SKarsten Graul 	spin_lock_bh(&lgr->llc_event_q_lock);
7106c8968c4SKarsten Graul 	if (!list_empty(&lgr->llc_event_q)) {
7116c8968c4SKarsten Graul 		qentry = list_first_entry(&lgr->llc_event_q,
7126c8968c4SKarsten Graul 					  struct smc_llc_qentry, list);
7136c8968c4SKarsten Graul 		list_del_init(&qentry->list);
7146c8968c4SKarsten Graul 		spin_unlock_bh(&lgr->llc_event_q_lock);
7156c8968c4SKarsten Graul 		smc_llc_event_handler(qentry);
7166c8968c4SKarsten Graul 		goto again;
7176c8968c4SKarsten Graul 	}
7186c8968c4SKarsten Graul 	spin_unlock_bh(&lgr->llc_event_q_lock);
7196c8968c4SKarsten Graul }
7206c8968c4SKarsten Graul 
721ef79d439SKarsten Graul /* process llc responses in tasklet context */
722ef79d439SKarsten Graul static void smc_llc_rx_response(struct smc_link *link, union smc_llc_msg *llc)
723ef79d439SKarsten Graul {
724ef79d439SKarsten Graul 	int rc = 0;
725ef79d439SKarsten Graul 
726ef79d439SKarsten Graul 	switch (llc->raw.hdr.common.type) {
727ef79d439SKarsten Graul 	case SMC_LLC_TEST_LINK:
728ef79d439SKarsten Graul 		if (link->state == SMC_LNK_ACTIVE)
729ef79d439SKarsten Graul 			complete(&link->llc_testlink_resp);
730ef79d439SKarsten Graul 		break;
731ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_LINK:
732ef79d439SKarsten Graul 		if (!(llc->raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
733ef79d439SKarsten Graul 			rc = ENOTSUPP;
734ef79d439SKarsten Graul 		if (link->lgr->role == SMC_SERV &&
735ef79d439SKarsten Graul 		    link->state == SMC_LNK_ACTIVATING) {
736ef79d439SKarsten Graul 			link->llc_confirm_resp_rc = rc;
737ef79d439SKarsten Graul 			complete(&link->llc_confirm_resp);
738ef79d439SKarsten Graul 		}
739ef79d439SKarsten Graul 		break;
740ef79d439SKarsten Graul 	case SMC_LLC_ADD_LINK:
741ef79d439SKarsten Graul 		if (link->state == SMC_LNK_ACTIVATING)
742ef79d439SKarsten Graul 			complete(&link->llc_add_resp);
743ef79d439SKarsten Graul 		break;
744ef79d439SKarsten Graul 	case SMC_LLC_DELETE_LINK:
745ef79d439SKarsten Graul 		if (link->lgr->role == SMC_SERV)
746ef79d439SKarsten Graul 			smc_lgr_schedule_free_work_fast(link->lgr);
747ef79d439SKarsten Graul 		break;
748ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY:
749ef79d439SKarsten Graul 		link->llc_confirm_rkey_resp_rc = llc->raw.hdr.flags &
750ef79d439SKarsten Graul 						 SMC_LLC_FLAG_RKEY_NEG;
751ef79d439SKarsten Graul 		complete(&link->llc_confirm_rkey_resp);
752ef79d439SKarsten Graul 		break;
753ef79d439SKarsten Graul 	case SMC_LLC_CONFIRM_RKEY_CONT:
754ef79d439SKarsten Graul 		/* unused as long as we don't send this type of msg */
755ef79d439SKarsten Graul 		break;
756ef79d439SKarsten Graul 	case SMC_LLC_DELETE_RKEY:
757ef79d439SKarsten Graul 		link->llc_delete_rkey_resp_rc = llc->raw.hdr.flags &
758ef79d439SKarsten Graul 						SMC_LLC_FLAG_RKEY_NEG;
759ef79d439SKarsten Graul 		complete(&link->llc_delete_rkey_resp);
760ef79d439SKarsten Graul 		break;
761ef79d439SKarsten Graul 	}
762ef79d439SKarsten Graul }
763ef79d439SKarsten Graul 
7646c8968c4SKarsten Graul /* copy received msg and add it to the event queue */
7656c8968c4SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
7666c8968c4SKarsten Graul {
7676c8968c4SKarsten Graul 	struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
7686c8968c4SKarsten Graul 	struct smc_link_group *lgr = link->lgr;
7696c8968c4SKarsten Graul 	struct smc_llc_qentry *qentry;
7706c8968c4SKarsten Graul 	union smc_llc_msg *llc = buf;
7716c8968c4SKarsten Graul 	unsigned long flags;
7726c8968c4SKarsten Graul 
7736c8968c4SKarsten Graul 	if (wc->byte_len < sizeof(*llc))
7746c8968c4SKarsten Graul 		return; /* short message */
7756c8968c4SKarsten Graul 	if (llc->raw.hdr.length != sizeof(*llc))
7766c8968c4SKarsten Graul 		return; /* invalid message */
7776c8968c4SKarsten Graul 
778ef79d439SKarsten Graul 	/* process responses immediately */
779ef79d439SKarsten Graul 	if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) {
780ef79d439SKarsten Graul 		smc_llc_rx_response(link, llc);
781ef79d439SKarsten Graul 		return;
782ef79d439SKarsten Graul 	}
783ef79d439SKarsten Graul 
7846c8968c4SKarsten Graul 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
7856c8968c4SKarsten Graul 	if (!qentry)
7866c8968c4SKarsten Graul 		return;
7876c8968c4SKarsten Graul 	qentry->link = link;
7886c8968c4SKarsten Graul 	INIT_LIST_HEAD(&qentry->list);
7896c8968c4SKarsten Graul 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
7906c8968c4SKarsten Graul 	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
7916c8968c4SKarsten Graul 	list_add_tail(&qentry->list, &lgr->llc_event_q);
7926c8968c4SKarsten Graul 	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
7936c8968c4SKarsten Graul 	schedule_work(&link->lgr->llc_event_work);
7949bf9abeaSUrsula Braun }
7959bf9abeaSUrsula Braun 
79644aa81ceSKarsten Graul /***************************** worker, utils *********************************/
797877ae5beSKarsten Graul 
798877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work)
799877ae5beSKarsten Graul {
800877ae5beSKarsten Graul 	struct smc_link *link = container_of(to_delayed_work(work),
801877ae5beSKarsten Graul 					     struct smc_link, llc_testlink_wrk);
802877ae5beSKarsten Graul 	unsigned long next_interval;
803877ae5beSKarsten Graul 	unsigned long expire_time;
804877ae5beSKarsten Graul 	u8 user_data[16] = { 0 };
805877ae5beSKarsten Graul 	int rc;
806877ae5beSKarsten Graul 
807877ae5beSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
808877ae5beSKarsten Graul 		return;		/* don't reschedule worker */
809877ae5beSKarsten Graul 	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
810877ae5beSKarsten Graul 	if (time_is_after_jiffies(expire_time)) {
811877ae5beSKarsten Graul 		next_interval = expire_time - jiffies;
812877ae5beSKarsten Graul 		goto out;
813877ae5beSKarsten Graul 	}
814877ae5beSKarsten Graul 	reinit_completion(&link->llc_testlink_resp);
815d97935faSKarsten Graul 	smc_llc_send_test_link(link, user_data);
816877ae5beSKarsten Graul 	/* receive TEST LINK response over RoCE fabric */
817877ae5beSKarsten Graul 	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
818877ae5beSKarsten Graul 						       SMC_LLC_WAIT_TIME);
8191020e1efSKarsten Graul 	if (link->state != SMC_LNK_ACTIVE)
8201020e1efSKarsten Graul 		return;		/* link state changed */
821877ae5beSKarsten Graul 	if (rc <= 0) {
8225f78fe96SKarsten Graul 		smc_lgr_terminate_sched(smc_get_lgr(link));
823877ae5beSKarsten Graul 		return;
824877ae5beSKarsten Graul 	}
825877ae5beSKarsten Graul 	next_interval = link->llc_testlink_time;
826877ae5beSKarsten Graul out:
8271020e1efSKarsten Graul 	schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
828877ae5beSKarsten Graul }
829877ae5beSKarsten Graul 
83000a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
83100a049cfSKarsten Graul {
83200a049cfSKarsten Graul 	struct net *net = sock_net(smc->clcsock->sk);
83300a049cfSKarsten Graul 
83400a049cfSKarsten Graul 	INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
83500a049cfSKarsten Graul 	INIT_LIST_HEAD(&lgr->llc_event_q);
83600a049cfSKarsten Graul 	spin_lock_init(&lgr->llc_event_q_lock);
837*555da9afSKarsten Graul 	spin_lock_init(&lgr->llc_flow_lock);
838*555da9afSKarsten Graul 	init_waitqueue_head(&lgr->llc_waiter);
83900a049cfSKarsten Graul 	lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;
84000a049cfSKarsten Graul }
84100a049cfSKarsten Graul 
84200a049cfSKarsten Graul /* called after lgr was removed from lgr_list */
84300a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr)
84400a049cfSKarsten Graul {
84500a049cfSKarsten Graul 	smc_llc_event_flush(lgr);
846*555da9afSKarsten Graul 	wake_up_interruptible_all(&lgr->llc_waiter);
84700a049cfSKarsten Graul 	cancel_work_sync(&lgr->llc_event_work);
848*555da9afSKarsten Graul 	if (lgr->delayed_event) {
849*555da9afSKarsten Graul 		kfree(lgr->delayed_event);
850*555da9afSKarsten Graul 		lgr->delayed_event = NULL;
851*555da9afSKarsten Graul 	}
85200a049cfSKarsten Graul }
85300a049cfSKarsten Graul 
8542a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link)
855877ae5beSKarsten Graul {
856b32cf4abSKarsten Graul 	init_completion(&link->llc_confirm);
857b32cf4abSKarsten Graul 	init_completion(&link->llc_confirm_resp);
858b32cf4abSKarsten Graul 	init_completion(&link->llc_add);
859b32cf4abSKarsten Graul 	init_completion(&link->llc_add_resp);
860ef79d439SKarsten Graul 	init_completion(&link->llc_confirm_rkey_resp);
861ef79d439SKarsten Graul 	init_completion(&link->llc_delete_rkey_resp);
86260e03c62SKarsten Graul 	mutex_init(&link->llc_delete_rkey_mutex);
863877ae5beSKarsten Graul 	init_completion(&link->llc_testlink_resp);
864877ae5beSKarsten Graul 	INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
8652a4c57a9SKarsten Graul 	return 0;
866b32cf4abSKarsten Graul }
867b32cf4abSKarsten Graul 
86800a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link)
869b32cf4abSKarsten Graul {
870877ae5beSKarsten Graul 	link->state = SMC_LNK_ACTIVE;
87100a049cfSKarsten Graul 	if (link->lgr->llc_testlink_time) {
87200a049cfSKarsten Graul 		link->llc_testlink_time = link->lgr->llc_testlink_time * HZ;
8731020e1efSKarsten Graul 		schedule_delayed_work(&link->llc_testlink_wrk,
874877ae5beSKarsten Graul 				      link->llc_testlink_time);
875877ae5beSKarsten Graul 	}
876877ae5beSKarsten Graul }
877877ae5beSKarsten Graul 
8780d18a0cbSKarsten Graul void smc_llc_link_deleting(struct smc_link *link)
8790d18a0cbSKarsten Graul {
8800d18a0cbSKarsten Graul 	link->state = SMC_LNK_DELETING;
88115e1b99aSUrsula Braun 	smc_wr_wakeup_tx_wait(link);
8820d18a0cbSKarsten Graul }
8830d18a0cbSKarsten Graul 
884877ae5beSKarsten Graul /* called in worker context */
8852a4c57a9SKarsten Graul void smc_llc_link_clear(struct smc_link *link)
886877ae5beSKarsten Graul {
8872140ac26SKarsten Graul 	complete(&link->llc_testlink_resp);
8882140ac26SKarsten Graul 	cancel_delayed_work_sync(&link->llc_testlink_wrk);
8892140ac26SKarsten Graul 	smc_wr_wakeup_reg_wait(link);
8902140ac26SKarsten Graul 	smc_wr_wakeup_tx_wait(link);
891877ae5beSKarsten Graul }
892877ae5beSKarsten Graul 
89344aa81ceSKarsten Graul /* register a new rtoken at the remote peer */
89444aa81ceSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *link,
89544aa81ceSKarsten Graul 			    struct smc_buf_desc *rmb_desc)
89644aa81ceSKarsten Graul {
89744aa81ceSKarsten Graul 	int rc;
89844aa81ceSKarsten Graul 
89960e03c62SKarsten Graul 	/* protected by mutex smc_create_lgr_pending */
900ef79d439SKarsten Graul 	reinit_completion(&link->llc_confirm_rkey_resp);
9014600cfc3SKarsten Graul 	rc = smc_llc_send_confirm_rkey(link, rmb_desc);
9024600cfc3SKarsten Graul 	if (rc)
9034600cfc3SKarsten Graul 		return rc;
90444aa81ceSKarsten Graul 	/* receive CONFIRM RKEY response from server over RoCE fabric */
905ef79d439SKarsten Graul 	rc = wait_for_completion_interruptible_timeout(
906ef79d439SKarsten Graul 			&link->llc_confirm_rkey_resp, SMC_LLC_WAIT_TIME);
907ef79d439SKarsten Graul 	if (rc <= 0 || link->llc_confirm_rkey_resp_rc)
90844aa81ceSKarsten Graul 		return -EFAULT;
90944aa81ceSKarsten Graul 	return 0;
91044aa81ceSKarsten Graul }
91144aa81ceSKarsten Graul 
91260e03c62SKarsten Graul /* unregister an rtoken at the remote peer */
91360e03c62SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link *link,
91460e03c62SKarsten Graul 			   struct smc_buf_desc *rmb_desc)
91560e03c62SKarsten Graul {
9160b29ec64SUrsula Braun 	int rc = 0;
91760e03c62SKarsten Graul 
91860e03c62SKarsten Graul 	mutex_lock(&link->llc_delete_rkey_mutex);
9190b29ec64SUrsula Braun 	if (link->state != SMC_LNK_ACTIVE)
9200b29ec64SUrsula Braun 		goto out;
921ef79d439SKarsten Graul 	reinit_completion(&link->llc_delete_rkey_resp);
92260e03c62SKarsten Graul 	rc = smc_llc_send_delete_rkey(link, rmb_desc);
92360e03c62SKarsten Graul 	if (rc)
92460e03c62SKarsten Graul 		goto out;
92560e03c62SKarsten Graul 	/* receive DELETE RKEY response from server over RoCE fabric */
926ef79d439SKarsten Graul 	rc = wait_for_completion_interruptible_timeout(
927ef79d439SKarsten Graul 			&link->llc_delete_rkey_resp, SMC_LLC_WAIT_TIME);
928ef79d439SKarsten Graul 	if (rc <= 0 || link->llc_delete_rkey_resp_rc)
92960e03c62SKarsten Graul 		rc = -EFAULT;
93060e03c62SKarsten Graul 	else
93160e03c62SKarsten Graul 		rc = 0;
93260e03c62SKarsten Graul out:
93360e03c62SKarsten Graul 	mutex_unlock(&link->llc_delete_rkey_mutex);
93460e03c62SKarsten Graul 	return rc;
93560e03c62SKarsten Graul }
93660e03c62SKarsten Graul 
9379bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/
9389bf9abeaSUrsula Braun 
9399bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
9409bf9abeaSUrsula Braun 	{
9419bf9abeaSUrsula Braun 		.handler	= smc_llc_rx_handler,
9429bf9abeaSUrsula Braun 		.type		= SMC_LLC_CONFIRM_LINK
9439bf9abeaSUrsula Braun 	},
9449bf9abeaSUrsula Braun 	{
945313164daSKarsten Graul 		.handler	= smc_llc_rx_handler,
946313164daSKarsten Graul 		.type		= SMC_LLC_TEST_LINK
947313164daSKarsten Graul 	},
948313164daSKarsten Graul 	{
9494ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
95052bedf37SKarsten Graul 		.type		= SMC_LLC_ADD_LINK
95152bedf37SKarsten Graul 	},
95252bedf37SKarsten Graul 	{
95352bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
95452bedf37SKarsten Graul 		.type		= SMC_LLC_DELETE_LINK
95552bedf37SKarsten Graul 	},
95652bedf37SKarsten Graul 	{
95752bedf37SKarsten Graul 		.handler	= smc_llc_rx_handler,
9584ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY
9594ed75de5SKarsten Graul 	},
9604ed75de5SKarsten Graul 	{
9614ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
9624ed75de5SKarsten Graul 		.type		= SMC_LLC_CONFIRM_RKEY_CONT
9634ed75de5SKarsten Graul 	},
9644ed75de5SKarsten Graul 	{
9654ed75de5SKarsten Graul 		.handler	= smc_llc_rx_handler,
9664ed75de5SKarsten Graul 		.type		= SMC_LLC_DELETE_RKEY
9674ed75de5SKarsten Graul 	},
9684ed75de5SKarsten Graul 	{
9699bf9abeaSUrsula Braun 		.handler	= NULL,
9709bf9abeaSUrsula Braun 	}
9719bf9abeaSUrsula Braun };
9729bf9abeaSUrsula Braun 
9739bf9abeaSUrsula Braun int __init smc_llc_init(void)
9749bf9abeaSUrsula Braun {
9759bf9abeaSUrsula Braun 	struct smc_wr_rx_handler *handler;
9769bf9abeaSUrsula Braun 	int rc = 0;
9779bf9abeaSUrsula Braun 
9789bf9abeaSUrsula Braun 	for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
9799bf9abeaSUrsula Braun 		INIT_HLIST_NODE(&handler->list);
9809bf9abeaSUrsula Braun 		rc = smc_wr_rx_register_handler(handler);
9819bf9abeaSUrsula Braun 		if (rc)
9829bf9abeaSUrsula Braun 			break;
9839bf9abeaSUrsula Braun 	}
9849bf9abeaSUrsula Braun 	return rc;
9859bf9abeaSUrsula Braun }
986