1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 29bf9abeaSUrsula Braun /* 39bf9abeaSUrsula Braun * Shared Memory Communications over RDMA (SMC-R) and RoCE 49bf9abeaSUrsula Braun * 59bf9abeaSUrsula Braun * Link Layer Control (LLC) 69bf9abeaSUrsula Braun * 79bf9abeaSUrsula Braun * Copyright IBM Corp. 2016 89bf9abeaSUrsula Braun * 99bf9abeaSUrsula Braun * Author(s): Klaus Wacker <Klaus.Wacker@de.ibm.com> 109bf9abeaSUrsula Braun * Ursula Braun <ubraun@linux.vnet.ibm.com> 119bf9abeaSUrsula Braun */ 129bf9abeaSUrsula Braun 139bf9abeaSUrsula Braun #include <net/tcp.h> 149bf9abeaSUrsula Braun #include <rdma/ib_verbs.h> 159bf9abeaSUrsula Braun 169bf9abeaSUrsula Braun #include "smc.h" 179bf9abeaSUrsula Braun #include "smc_core.h" 189bf9abeaSUrsula Braun #include "smc_clc.h" 199bf9abeaSUrsula Braun #include "smc_llc.h" 20336ba09fSKarsten Graul #include "smc_pnet.h" 219bf9abeaSUrsula Braun 220f627126SStefan Raspl #define SMC_LLC_DATA_LEN 40 230f627126SStefan Raspl 240f627126SStefan Raspl struct smc_llc_hdr { 250f627126SStefan Raspl struct smc_wr_rx_hdr common; 260f627126SStefan Raspl u8 length; /* 44 */ 2752bedf37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD) 2852bedf37SKarsten Graul u8 reserved:4, 2952bedf37SKarsten Graul add_link_rej_rsn:4; 3052bedf37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD) 3152bedf37SKarsten Graul u8 add_link_rej_rsn:4, 3252bedf37SKarsten Graul reserved:4; 3352bedf37SKarsten Graul #endif 340f627126SStefan Raspl u8 flags; 350f627126SStefan Raspl }; 360f627126SStefan Raspl 3775d320d6SKarsten Graul #define SMC_LLC_FLAG_NO_RMBE_EYEC 0x03 3875d320d6SKarsten Graul 390f627126SStefan Raspl struct smc_llc_msg_confirm_link { /* type 0x01 */ 400f627126SStefan Raspl struct smc_llc_hdr hd; 410f627126SStefan Raspl u8 sender_mac[ETH_ALEN]; 420f627126SStefan Raspl u8 sender_gid[SMC_GID_SIZE]; 430f627126SStefan Raspl u8 sender_qp_num[3]; 440f627126SStefan Raspl u8 link_num; 450f627126SStefan Raspl u8 link_uid[SMC_LGR_ID_SIZE]; 460f627126SStefan Raspl u8 max_links; 470f627126SStefan Raspl u8 reserved[9]; 480f627126SStefan Raspl }; 490f627126SStefan Raspl 5052bedf37SKarsten Graul #define SMC_LLC_FLAG_ADD_LNK_REJ 0x40 5152bedf37SKarsten Graul #define SMC_LLC_REJ_RSN_NO_ALT_PATH 1 5252bedf37SKarsten Graul 5352bedf37SKarsten Graul #define SMC_LLC_ADD_LNK_MAX_LINKS 2 5452bedf37SKarsten Graul 5552bedf37SKarsten Graul struct smc_llc_msg_add_link { /* type 0x02 */ 5652bedf37SKarsten Graul struct smc_llc_hdr hd; 5752bedf37SKarsten Graul u8 sender_mac[ETH_ALEN]; 5852bedf37SKarsten Graul u8 reserved2[2]; 5952bedf37SKarsten Graul u8 sender_gid[SMC_GID_SIZE]; 6052bedf37SKarsten Graul u8 sender_qp_num[3]; 6152bedf37SKarsten Graul u8 link_num; 62fbed3b37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD) 63fbed3b37SKarsten Graul u8 reserved3 : 4, 64fbed3b37SKarsten Graul qp_mtu : 4; 65fbed3b37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD) 66fbed3b37SKarsten Graul u8 qp_mtu : 4, 67fbed3b37SKarsten Graul reserved3 : 4; 68fbed3b37SKarsten Graul #endif 6952bedf37SKarsten Graul u8 initial_psn[3]; 7052bedf37SKarsten Graul u8 reserved[8]; 7152bedf37SKarsten Graul }; 7252bedf37SKarsten Graul 7387f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont_rt { 7487f88cdaSKarsten Graul __be32 rmb_key; 7587f88cdaSKarsten Graul __be32 rmb_key_new; 7687f88cdaSKarsten Graul __be64 rmb_vaddr_new; 7787f88cdaSKarsten Graul }; 7887f88cdaSKarsten Graul 7987f88cdaSKarsten Graul #define SMC_LLC_RKEYS_PER_CONT_MSG 2 8087f88cdaSKarsten Graul 8187f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont { /* type 0x03 */ 8287f88cdaSKarsten Graul struct smc_llc_hdr hd; 8387f88cdaSKarsten Graul u8 link_num; 8487f88cdaSKarsten Graul u8 num_rkeys; 8587f88cdaSKarsten Graul u8 reserved2[2]; 8687f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG]; 8787f88cdaSKarsten Graul u8 reserved[4]; 8887f88cdaSKarsten Graul } __packed; /* format defined in RFC7609 */ 8987f88cdaSKarsten Graul 9052bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ALL 0x40 9152bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ORDERLY 0x20 9252bedf37SKarsten Graul 9352bedf37SKarsten Graul struct smc_llc_msg_del_link { /* type 0x04 */ 9452bedf37SKarsten Graul struct smc_llc_hdr hd; 9552bedf37SKarsten Graul u8 link_num; 9652bedf37SKarsten Graul __be32 reason; 9752bedf37SKarsten Graul u8 reserved[35]; 9852bedf37SKarsten Graul } __packed; /* format defined in RFC7609 */ 9952bedf37SKarsten Graul 100313164daSKarsten Graul struct smc_llc_msg_test_link { /* type 0x07 */ 101313164daSKarsten Graul struct smc_llc_hdr hd; 102313164daSKarsten Graul u8 user_data[16]; 103313164daSKarsten Graul u8 reserved[24]; 104313164daSKarsten Graul }; 105313164daSKarsten Graul 1064ed75de5SKarsten Graul struct smc_rmb_rtoken { 1074ed75de5SKarsten Graul union { 1084ed75de5SKarsten Graul u8 num_rkeys; /* first rtoken byte of CONFIRM LINK msg */ 1094ed75de5SKarsten Graul /* is actually the num of rtokens, first */ 1104ed75de5SKarsten Graul /* rtoken is always for the current link */ 1114ed75de5SKarsten Graul u8 link_id; /* link id of the rtoken */ 1124ed75de5SKarsten Graul }; 1134ed75de5SKarsten Graul __be32 rmb_key; 1144ed75de5SKarsten Graul __be64 rmb_vaddr; 1154ed75de5SKarsten Graul } __packed; /* format defined in RFC7609 */ 1164ed75de5SKarsten Graul 1174ed75de5SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG 3 1184ed75de5SKarsten Graul 1194ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey { /* type 0x06 */ 1204ed75de5SKarsten Graul struct smc_llc_hdr hd; 1214ed75de5SKarsten Graul struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; 1224ed75de5SKarsten Graul u8 reserved; 1234ed75de5SKarsten Graul }; 1244ed75de5SKarsten Graul 1254ed75de5SKarsten Graul #define SMC_LLC_DEL_RKEY_MAX 8 1263bc67e09SKarsten Graul #define SMC_LLC_FLAG_RKEY_RETRY 0x10 1274ed75de5SKarsten Graul #define SMC_LLC_FLAG_RKEY_NEG 0x20 1284ed75de5SKarsten Graul 1294ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey { /* type 0x09 */ 1304ed75de5SKarsten Graul struct smc_llc_hdr hd; 1314ed75de5SKarsten Graul u8 num_rkeys; 1324ed75de5SKarsten Graul u8 err_mask; 1334ed75de5SKarsten Graul u8 reserved[2]; 1344ed75de5SKarsten Graul __be32 rkey[8]; 1354ed75de5SKarsten Graul u8 reserved2[4]; 1364ed75de5SKarsten Graul }; 1374ed75de5SKarsten Graul 1380f627126SStefan Raspl union smc_llc_msg { 1390f627126SStefan Raspl struct smc_llc_msg_confirm_link confirm_link; 14052bedf37SKarsten Graul struct smc_llc_msg_add_link add_link; 14187f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont add_link_cont; 14252bedf37SKarsten Graul struct smc_llc_msg_del_link delete_link; 1434ed75de5SKarsten Graul 1444ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey confirm_rkey; 1454ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey delete_rkey; 1464ed75de5SKarsten Graul 147313164daSKarsten Graul struct smc_llc_msg_test_link test_link; 1480f627126SStefan Raspl struct { 1490f627126SStefan Raspl struct smc_llc_hdr hdr; 1500f627126SStefan Raspl u8 data[SMC_LLC_DATA_LEN]; 1510f627126SStefan Raspl } raw; 1520f627126SStefan Raspl }; 1530f627126SStefan Raspl 1540f627126SStefan Raspl #define SMC_LLC_FLAG_RESP 0x80 1550f627126SStefan Raspl 1566c8968c4SKarsten Graul struct smc_llc_qentry { 1576c8968c4SKarsten Graul struct list_head list; 1586c8968c4SKarsten Graul struct smc_link *link; 1596c8968c4SKarsten Graul union smc_llc_msg msg; 1606c8968c4SKarsten Graul }; 1616c8968c4SKarsten Graul 162555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow) 163555da9afSKarsten Graul { 164555da9afSKarsten Graul struct smc_llc_qentry *qentry = flow->qentry; 165555da9afSKarsten Graul 166555da9afSKarsten Graul flow->qentry = NULL; 167555da9afSKarsten Graul return qentry; 168555da9afSKarsten Graul } 169555da9afSKarsten Graul 170555da9afSKarsten Graul void smc_llc_flow_qentry_del(struct smc_llc_flow *flow) 171555da9afSKarsten Graul { 172555da9afSKarsten Graul struct smc_llc_qentry *qentry; 173555da9afSKarsten Graul 174555da9afSKarsten Graul if (flow->qentry) { 175555da9afSKarsten Graul qentry = flow->qentry; 176555da9afSKarsten Graul flow->qentry = NULL; 177555da9afSKarsten Graul kfree(qentry); 178555da9afSKarsten Graul } 179555da9afSKarsten Graul } 180555da9afSKarsten Graul 181555da9afSKarsten Graul static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow, 182555da9afSKarsten Graul struct smc_llc_qentry *qentry) 183555da9afSKarsten Graul { 184555da9afSKarsten Graul flow->qentry = qentry; 185555da9afSKarsten Graul } 186555da9afSKarsten Graul 187555da9afSKarsten Graul /* try to start a new llc flow, initiated by an incoming llc msg */ 188555da9afSKarsten Graul static bool smc_llc_flow_start(struct smc_llc_flow *flow, 189555da9afSKarsten Graul struct smc_llc_qentry *qentry) 190555da9afSKarsten Graul { 191555da9afSKarsten Graul struct smc_link_group *lgr = qentry->link->lgr; 192555da9afSKarsten Graul 193555da9afSKarsten Graul spin_lock_bh(&lgr->llc_flow_lock); 194555da9afSKarsten Graul if (flow->type) { 195555da9afSKarsten Graul /* a flow is already active */ 196555da9afSKarsten Graul if ((qentry->msg.raw.hdr.common.type == SMC_LLC_ADD_LINK || 197555da9afSKarsten Graul qentry->msg.raw.hdr.common.type == SMC_LLC_DELETE_LINK) && 198555da9afSKarsten Graul !lgr->delayed_event) { 199555da9afSKarsten Graul lgr->delayed_event = qentry; 200555da9afSKarsten Graul } else { 201555da9afSKarsten Graul /* forget this llc request */ 202555da9afSKarsten Graul kfree(qentry); 203555da9afSKarsten Graul } 204555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 205555da9afSKarsten Graul return false; 206555da9afSKarsten Graul } 207555da9afSKarsten Graul switch (qentry->msg.raw.hdr.common.type) { 208555da9afSKarsten Graul case SMC_LLC_ADD_LINK: 209555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_ADD_LINK; 210555da9afSKarsten Graul break; 211555da9afSKarsten Graul case SMC_LLC_DELETE_LINK: 212555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_DEL_LINK; 213555da9afSKarsten Graul break; 214555da9afSKarsten Graul case SMC_LLC_CONFIRM_RKEY: 215555da9afSKarsten Graul case SMC_LLC_DELETE_RKEY: 216555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_RKEY; 217555da9afSKarsten Graul break; 218555da9afSKarsten Graul default: 219555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_NONE; 220555da9afSKarsten Graul } 221555da9afSKarsten Graul if (qentry == lgr->delayed_event) 222555da9afSKarsten Graul lgr->delayed_event = NULL; 223555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 224555da9afSKarsten Graul smc_llc_flow_qentry_set(flow, qentry); 225555da9afSKarsten Graul return true; 226555da9afSKarsten Graul } 227555da9afSKarsten Graul 228555da9afSKarsten Graul /* start a new local llc flow, wait till current flow finished */ 229555da9afSKarsten Graul int smc_llc_flow_initiate(struct smc_link_group *lgr, 230555da9afSKarsten Graul enum smc_llc_flowtype type) 231555da9afSKarsten Graul { 232555da9afSKarsten Graul enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE; 233555da9afSKarsten Graul int rc; 234555da9afSKarsten Graul 235555da9afSKarsten Graul /* all flows except confirm_rkey and delete_rkey are exclusive, 236555da9afSKarsten Graul * confirm/delete rkey flows can run concurrently (local and remote) 237555da9afSKarsten Graul */ 238555da9afSKarsten Graul if (type == SMC_LLC_FLOW_RKEY) 239555da9afSKarsten Graul allowed_remote = SMC_LLC_FLOW_RKEY; 240555da9afSKarsten Graul again: 241555da9afSKarsten Graul if (list_empty(&lgr->list)) 242555da9afSKarsten Graul return -ENODEV; 243555da9afSKarsten Graul spin_lock_bh(&lgr->llc_flow_lock); 244555da9afSKarsten Graul if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 245555da9afSKarsten Graul (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 246555da9afSKarsten Graul lgr->llc_flow_rmt.type == allowed_remote)) { 247555da9afSKarsten Graul lgr->llc_flow_lcl.type = type; 248555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 249555da9afSKarsten Graul return 0; 250555da9afSKarsten Graul } 251555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 252555da9afSKarsten Graul rc = wait_event_interruptible_timeout(lgr->llc_waiter, 253555da9afSKarsten Graul (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 254555da9afSKarsten Graul (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 255555da9afSKarsten Graul lgr->llc_flow_rmt.type == allowed_remote)), 256555da9afSKarsten Graul SMC_LLC_WAIT_TIME); 257555da9afSKarsten Graul if (!rc) 258555da9afSKarsten Graul return -ETIMEDOUT; 259555da9afSKarsten Graul goto again; 260555da9afSKarsten Graul } 261555da9afSKarsten Graul 262555da9afSKarsten Graul /* finish the current llc flow */ 263555da9afSKarsten Graul void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow) 264555da9afSKarsten Graul { 265555da9afSKarsten Graul spin_lock_bh(&lgr->llc_flow_lock); 266555da9afSKarsten Graul memset(flow, 0, sizeof(*flow)); 267555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_NONE; 268555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 269555da9afSKarsten Graul if (!list_empty(&lgr->list) && lgr->delayed_event && 270555da9afSKarsten Graul flow == &lgr->llc_flow_lcl) 271555da9afSKarsten Graul schedule_work(&lgr->llc_event_work); 272555da9afSKarsten Graul else 273555da9afSKarsten Graul wake_up_interruptible(&lgr->llc_waiter); 274555da9afSKarsten Graul } 275555da9afSKarsten Graul 276555da9afSKarsten Graul /* lnk is optional and used for early wakeup when link goes down, useful in 277555da9afSKarsten Graul * cases where we wait for a response on the link after we sent a request 278555da9afSKarsten Graul */ 279555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr, 280555da9afSKarsten Graul struct smc_link *lnk, 281555da9afSKarsten Graul int time_out, u8 exp_msg) 282555da9afSKarsten Graul { 283555da9afSKarsten Graul struct smc_llc_flow *flow = &lgr->llc_flow_lcl; 284555da9afSKarsten Graul 285555da9afSKarsten Graul wait_event_interruptible_timeout(lgr->llc_waiter, 286555da9afSKarsten Graul (flow->qentry || 287555da9afSKarsten Graul (lnk && !smc_link_usable(lnk)) || 288555da9afSKarsten Graul list_empty(&lgr->list)), 289555da9afSKarsten Graul time_out); 290555da9afSKarsten Graul if (!flow->qentry || 291555da9afSKarsten Graul (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) { 292555da9afSKarsten Graul smc_llc_flow_qentry_del(flow); 293555da9afSKarsten Graul goto out; 294555da9afSKarsten Graul } 295555da9afSKarsten Graul if (exp_msg && flow->qentry->msg.raw.hdr.common.type != exp_msg) { 296555da9afSKarsten Graul if (exp_msg == SMC_LLC_ADD_LINK && 297555da9afSKarsten Graul flow->qentry->msg.raw.hdr.common.type == 298555da9afSKarsten Graul SMC_LLC_DELETE_LINK) { 299555da9afSKarsten Graul /* flow_start will delay the unexpected msg */ 300555da9afSKarsten Graul smc_llc_flow_start(&lgr->llc_flow_lcl, 301555da9afSKarsten Graul smc_llc_flow_qentry_clr(flow)); 302555da9afSKarsten Graul return NULL; 303555da9afSKarsten Graul } 304555da9afSKarsten Graul smc_llc_flow_qentry_del(flow); 305555da9afSKarsten Graul } 306555da9afSKarsten Graul out: 307555da9afSKarsten Graul return flow->qentry; 308555da9afSKarsten Graul } 309555da9afSKarsten Graul 3109bf9abeaSUrsula Braun /********************************** send *************************************/ 3119bf9abeaSUrsula Braun 3129bf9abeaSUrsula Braun struct smc_llc_tx_pend { 3139bf9abeaSUrsula Braun }; 3149bf9abeaSUrsula Braun 3159bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */ 3169bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend, 3179bf9abeaSUrsula Braun struct smc_link *link, 3189bf9abeaSUrsula Braun enum ib_wc_status wc_status) 3199bf9abeaSUrsula Braun { 3209bf9abeaSUrsula Braun /* future work: handle wc_status error for recovery and failover */ 3219bf9abeaSUrsula Braun } 3229bf9abeaSUrsula Braun 3239bf9abeaSUrsula Braun /** 3249bf9abeaSUrsula Braun * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits 3259bf9abeaSUrsula Braun * @link: Pointer to SMC link used for sending LLC control message. 3269bf9abeaSUrsula Braun * @wr_buf: Out variable returning pointer to work request payload buffer. 3279bf9abeaSUrsula Braun * @pend: Out variable returning pointer to private pending WR tracking. 3289bf9abeaSUrsula Braun * It's the context the transmit complete handler will get. 3299bf9abeaSUrsula Braun * 3309bf9abeaSUrsula Braun * Reserves and pre-fills an entry for a pending work request send/tx. 3319bf9abeaSUrsula Braun * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx. 3329bf9abeaSUrsula Braun * Can sleep due to smc_get_ctrl_buf (if not in softirq context). 3339bf9abeaSUrsula Braun * 3349bf9abeaSUrsula Braun * Return: 0 on success, otherwise an error value. 3359bf9abeaSUrsula Braun */ 3369bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link, 3379bf9abeaSUrsula Braun struct smc_wr_buf **wr_buf, 3389bf9abeaSUrsula Braun struct smc_wr_tx_pend_priv **pend) 3399bf9abeaSUrsula Braun { 3409bf9abeaSUrsula Braun int rc; 3419bf9abeaSUrsula Braun 342ad6f317fSUrsula Braun rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL, 343ad6f317fSUrsula Braun pend); 3449bf9abeaSUrsula Braun if (rc < 0) 3459bf9abeaSUrsula Braun return rc; 3469bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 3479bf9abeaSUrsula Braun sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE, 3489bf9abeaSUrsula Braun "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)"); 3499bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 3509bf9abeaSUrsula Braun sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE, 3519bf9abeaSUrsula Braun "must adapt SMC_WR_TX_SIZE to sizeof(struct smc_llc_msg); if not all smc_wr upper layer protocols use the same message size any more, must start to set link->wr_tx_sges[i].length on each individual smc_wr_tx_send()"); 3529bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 3539bf9abeaSUrsula Braun sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, 3549bf9abeaSUrsula Braun "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)"); 3559bf9abeaSUrsula Braun return 0; 3569bf9abeaSUrsula Braun } 3579bf9abeaSUrsula Braun 3589bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */ 359947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link, 3609bf9abeaSUrsula Braun enum smc_llc_reqresp reqresp) 3619bf9abeaSUrsula Braun { 36200e5fb26SStefan Raspl struct smc_link_group *lgr = smc_get_lgr(link); 3639bf9abeaSUrsula Braun struct smc_llc_msg_confirm_link *confllc; 3649bf9abeaSUrsula Braun struct smc_wr_tx_pend_priv *pend; 3659bf9abeaSUrsula Braun struct smc_wr_buf *wr_buf; 3669bf9abeaSUrsula Braun int rc; 3679bf9abeaSUrsula Braun 3689bf9abeaSUrsula Braun rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 3699bf9abeaSUrsula Braun if (rc) 3709bf9abeaSUrsula Braun return rc; 3719bf9abeaSUrsula Braun confllc = (struct smc_llc_msg_confirm_link *)wr_buf; 3729bf9abeaSUrsula Braun memset(confllc, 0, sizeof(*confllc)); 3739bf9abeaSUrsula Braun confllc->hd.common.type = SMC_LLC_CONFIRM_LINK; 3749bf9abeaSUrsula Braun confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link); 37575d320d6SKarsten Graul confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC; 3769bf9abeaSUrsula Braun if (reqresp == SMC_LLC_RESP) 3779bf9abeaSUrsula Braun confllc->hd.flags |= SMC_LLC_FLAG_RESP; 378947541f3SUrsula Braun memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1], 379947541f3SUrsula Braun ETH_ALEN); 3807005ada6SUrsula Braun memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE); 3819bf9abeaSUrsula Braun hton24(confllc->sender_qp_num, link->roce_qp->qp_num); 3822be922f3SKarsten Graul confllc->link_num = link->link_id; 3839bf9abeaSUrsula Braun memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE); 384b1570a87SKarsten Graul confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; 38552bedf37SKarsten Graul /* send llc message */ 38652bedf37SKarsten Graul rc = smc_wr_tx_send(link, pend); 38752bedf37SKarsten Graul return rc; 38852bedf37SKarsten Graul } 38952bedf37SKarsten Graul 39044aa81ceSKarsten Graul /* send LLC confirm rkey request */ 3913d88a21bSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *send_link, 39244aa81ceSKarsten Graul struct smc_buf_desc *rmb_desc) 39344aa81ceSKarsten Graul { 39444aa81ceSKarsten Graul struct smc_llc_msg_confirm_rkey *rkeyllc; 39544aa81ceSKarsten Graul struct smc_wr_tx_pend_priv *pend; 39644aa81ceSKarsten Graul struct smc_wr_buf *wr_buf; 3973d88a21bSKarsten Graul struct smc_link *link; 3983d88a21bSKarsten Graul int i, rc, rtok_ix; 39944aa81ceSKarsten Graul 4003d88a21bSKarsten Graul rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend); 40144aa81ceSKarsten Graul if (rc) 40244aa81ceSKarsten Graul return rc; 40344aa81ceSKarsten Graul rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf; 40444aa81ceSKarsten Graul memset(rkeyllc, 0, sizeof(*rkeyllc)); 40544aa81ceSKarsten Graul rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY; 40644aa81ceSKarsten Graul rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey); 4073d88a21bSKarsten Graul 4083d88a21bSKarsten Graul rtok_ix = 1; 4093d88a21bSKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 4103d88a21bSKarsten Graul link = &send_link->lgr->lnk[i]; 4113d88a21bSKarsten Graul if (link->state == SMC_LNK_ACTIVE && link != send_link) { 4123d88a21bSKarsten Graul rkeyllc->rtoken[rtok_ix].link_id = link->link_id; 4133d88a21bSKarsten Graul rkeyllc->rtoken[rtok_ix].rmb_key = 414387707fdSKarsten Graul htonl(rmb_desc->mr_rx[link->link_idx]->rkey); 4153d88a21bSKarsten Graul rkeyllc->rtoken[rtok_ix].rmb_vaddr = cpu_to_be64( 4163d88a21bSKarsten Graul (u64)sg_dma_address( 4173d88a21bSKarsten Graul rmb_desc->sgt[link->link_idx].sgl)); 4183d88a21bSKarsten Graul rtok_ix++; 4193d88a21bSKarsten Graul } 4203d88a21bSKarsten Graul } 4213d88a21bSKarsten Graul /* rkey of send_link is in rtoken[0] */ 4223d88a21bSKarsten Graul rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1; 4233d88a21bSKarsten Graul rkeyllc->rtoken[0].rmb_key = 4243d88a21bSKarsten Graul htonl(rmb_desc->mr_rx[send_link->link_idx]->rkey); 42544aa81ceSKarsten Graul rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64( 4263d88a21bSKarsten Graul (u64)sg_dma_address(rmb_desc->sgt[send_link->link_idx].sgl)); 42744aa81ceSKarsten Graul /* send llc message */ 4283d88a21bSKarsten Graul rc = smc_wr_tx_send(send_link, pend); 42944aa81ceSKarsten Graul return rc; 43044aa81ceSKarsten Graul } 43144aa81ceSKarsten Graul 43260e03c62SKarsten Graul /* send LLC delete rkey request */ 43360e03c62SKarsten Graul static int smc_llc_send_delete_rkey(struct smc_link *link, 43460e03c62SKarsten Graul struct smc_buf_desc *rmb_desc) 43560e03c62SKarsten Graul { 43660e03c62SKarsten Graul struct smc_llc_msg_delete_rkey *rkeyllc; 43760e03c62SKarsten Graul struct smc_wr_tx_pend_priv *pend; 43860e03c62SKarsten Graul struct smc_wr_buf *wr_buf; 43960e03c62SKarsten Graul int rc; 44060e03c62SKarsten Graul 44160e03c62SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 44260e03c62SKarsten Graul if (rc) 44360e03c62SKarsten Graul return rc; 44460e03c62SKarsten Graul rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf; 44560e03c62SKarsten Graul memset(rkeyllc, 0, sizeof(*rkeyllc)); 44660e03c62SKarsten Graul rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY; 44760e03c62SKarsten Graul rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey); 44860e03c62SKarsten Graul rkeyllc->num_rkeys = 1; 449387707fdSKarsten Graul rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey); 45060e03c62SKarsten Graul /* send llc message */ 45160e03c62SKarsten Graul rc = smc_wr_tx_send(link, pend); 45260e03c62SKarsten Graul return rc; 45360e03c62SKarsten Graul } 45460e03c62SKarsten Graul 45552bedf37SKarsten Graul /* send ADD LINK request or response */ 4567005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[], 457fbed3b37SKarsten Graul struct smc_link *link_new, 45852bedf37SKarsten Graul enum smc_llc_reqresp reqresp) 45952bedf37SKarsten Graul { 46052bedf37SKarsten Graul struct smc_llc_msg_add_link *addllc; 46152bedf37SKarsten Graul struct smc_wr_tx_pend_priv *pend; 46252bedf37SKarsten Graul struct smc_wr_buf *wr_buf; 46352bedf37SKarsten Graul int rc; 46452bedf37SKarsten Graul 46552bedf37SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 46652bedf37SKarsten Graul if (rc) 46752bedf37SKarsten Graul return rc; 46852bedf37SKarsten Graul addllc = (struct smc_llc_msg_add_link *)wr_buf; 469fbed3b37SKarsten Graul 470fbed3b37SKarsten Graul memset(addllc, 0, sizeof(*addllc)); 471fbed3b37SKarsten Graul addllc->hd.common.type = SMC_LLC_ADD_LINK; 472fbed3b37SKarsten Graul addllc->hd.length = sizeof(struct smc_llc_msg_add_link); 473fbed3b37SKarsten Graul if (reqresp == SMC_LLC_RESP) 474fbed3b37SKarsten Graul addllc->hd.flags |= SMC_LLC_FLAG_RESP; 475fbed3b37SKarsten Graul memcpy(addllc->sender_mac, mac, ETH_ALEN); 476fbed3b37SKarsten Graul memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); 477fbed3b37SKarsten Graul if (link_new) { 478fbed3b37SKarsten Graul addllc->link_num = link_new->link_id; 479fbed3b37SKarsten Graul hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num); 480fbed3b37SKarsten Graul hton24(addllc->initial_psn, link_new->psn_initial); 481fbed3b37SKarsten Graul if (reqresp == SMC_LLC_REQ) 482fbed3b37SKarsten Graul addllc->qp_mtu = link_new->path_mtu; 483fbed3b37SKarsten Graul else 484fbed3b37SKarsten Graul addllc->qp_mtu = min(link_new->path_mtu, 485fbed3b37SKarsten Graul link_new->peer_mtu); 486fbed3b37SKarsten Graul } 48752bedf37SKarsten Graul /* send llc message */ 48852bedf37SKarsten Graul rc = smc_wr_tx_send(link, pend); 48952bedf37SKarsten Graul return rc; 49052bedf37SKarsten Graul } 49152bedf37SKarsten Graul 49252bedf37SKarsten Graul /* send DELETE LINK request or response */ 493fbed3b37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id, 494fbed3b37SKarsten Graul enum smc_llc_reqresp reqresp, bool orderly, 495fbed3b37SKarsten Graul u32 reason) 49652bedf37SKarsten Graul { 49752bedf37SKarsten Graul struct smc_llc_msg_del_link *delllc; 49852bedf37SKarsten Graul struct smc_wr_tx_pend_priv *pend; 49952bedf37SKarsten Graul struct smc_wr_buf *wr_buf; 50052bedf37SKarsten Graul int rc; 50152bedf37SKarsten Graul 50252bedf37SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 50352bedf37SKarsten Graul if (rc) 50452bedf37SKarsten Graul return rc; 50552bedf37SKarsten Graul delllc = (struct smc_llc_msg_del_link *)wr_buf; 506fbed3b37SKarsten Graul 507fbed3b37SKarsten Graul memset(delllc, 0, sizeof(*delllc)); 508fbed3b37SKarsten Graul delllc->hd.common.type = SMC_LLC_DELETE_LINK; 509fbed3b37SKarsten Graul delllc->hd.length = sizeof(struct smc_llc_msg_del_link); 510fbed3b37SKarsten Graul if (reqresp == SMC_LLC_RESP) 511fbed3b37SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_RESP; 512fbed3b37SKarsten Graul if (orderly) 513fbed3b37SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 514fbed3b37SKarsten Graul if (link_del_id) 515fbed3b37SKarsten Graul delllc->link_num = link_del_id; 516fbed3b37SKarsten Graul else 517fbed3b37SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 518fbed3b37SKarsten Graul delllc->reason = htonl(reason); 5199bf9abeaSUrsula Braun /* send llc message */ 5209bf9abeaSUrsula Braun rc = smc_wr_tx_send(link, pend); 5219bf9abeaSUrsula Braun return rc; 5229bf9abeaSUrsula Braun } 5239bf9abeaSUrsula Braun 524d97935faSKarsten Graul /* send LLC test link request */ 525d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16]) 526313164daSKarsten Graul { 527313164daSKarsten Graul struct smc_llc_msg_test_link *testllc; 528313164daSKarsten Graul struct smc_wr_tx_pend_priv *pend; 529313164daSKarsten Graul struct smc_wr_buf *wr_buf; 530313164daSKarsten Graul int rc; 531313164daSKarsten Graul 532313164daSKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 533313164daSKarsten Graul if (rc) 534313164daSKarsten Graul return rc; 535313164daSKarsten Graul testllc = (struct smc_llc_msg_test_link *)wr_buf; 536313164daSKarsten Graul memset(testllc, 0, sizeof(*testllc)); 537313164daSKarsten Graul testllc->hd.common.type = SMC_LLC_TEST_LINK; 538313164daSKarsten Graul testllc->hd.length = sizeof(struct smc_llc_msg_test_link); 539313164daSKarsten Graul memcpy(testllc->user_data, user_data, sizeof(testllc->user_data)); 540313164daSKarsten Graul /* send llc message */ 541313164daSKarsten Graul rc = smc_wr_tx_send(link, pend); 542313164daSKarsten Graul return rc; 543313164daSKarsten Graul } 544313164daSKarsten Graul 5456c8968c4SKarsten Graul /* schedule an llc send on link, may wait for buffers */ 5466c8968c4SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf) 5474ed75de5SKarsten Graul { 5484ed75de5SKarsten Graul struct smc_wr_tx_pend_priv *pend; 5494ed75de5SKarsten Graul struct smc_wr_buf *wr_buf; 5504ed75de5SKarsten Graul int rc; 5514ed75de5SKarsten Graul 5526c8968c4SKarsten Graul if (!smc_link_usable(link)) 5536c8968c4SKarsten Graul return -ENOLINK; 5546c8968c4SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 5554ed75de5SKarsten Graul if (rc) 5566c8968c4SKarsten Graul return rc; 5576c8968c4SKarsten Graul memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); 5586c8968c4SKarsten Graul return smc_wr_tx_send(link, pend); 5594ed75de5SKarsten Graul } 5604ed75de5SKarsten Graul 5619bf9abeaSUrsula Braun /********************************* receive ***********************************/ 5629bf9abeaSUrsula Braun 563336ba09fSKarsten Graul static int smc_llc_alloc_alt_link(struct smc_link_group *lgr, 564336ba09fSKarsten Graul enum smc_lgr_type lgr_new_t) 565336ba09fSKarsten Graul { 566336ba09fSKarsten Graul int i; 567336ba09fSKarsten Graul 568336ba09fSKarsten Graul if (lgr->type == SMC_LGR_SYMMETRIC || 569336ba09fSKarsten Graul (lgr->type != SMC_LGR_SINGLE && 570336ba09fSKarsten Graul (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 571336ba09fSKarsten Graul lgr_new_t == SMC_LGR_ASYMMETRIC_PEER))) 572336ba09fSKarsten Graul return -EMLINK; 573336ba09fSKarsten Graul 574336ba09fSKarsten Graul if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 575336ba09fSKarsten Graul lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) { 576336ba09fSKarsten Graul for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--) 577336ba09fSKarsten Graul if (lgr->lnk[i].state == SMC_LNK_UNUSED) 578336ba09fSKarsten Graul return i; 579336ba09fSKarsten Graul } else { 580336ba09fSKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) 581336ba09fSKarsten Graul if (lgr->lnk[i].state == SMC_LNK_UNUSED) 582336ba09fSKarsten Graul return i; 583336ba09fSKarsten Graul } 584336ba09fSKarsten Graul return -EMLINK; 585336ba09fSKarsten Graul } 586336ba09fSKarsten Graul 58787f88cdaSKarsten Graul /* return first buffer from any of the next buf lists */ 58887f88cdaSKarsten Graul static struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr, 58987f88cdaSKarsten Graul int *buf_lst) 59087f88cdaSKarsten Graul { 59187f88cdaSKarsten Graul struct smc_buf_desc *buf_pos; 59287f88cdaSKarsten Graul 59387f88cdaSKarsten Graul while (*buf_lst < SMC_RMBE_SIZES) { 59487f88cdaSKarsten Graul buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst], 59587f88cdaSKarsten Graul struct smc_buf_desc, list); 59687f88cdaSKarsten Graul if (buf_pos) 59787f88cdaSKarsten Graul return buf_pos; 59887f88cdaSKarsten Graul (*buf_lst)++; 59987f88cdaSKarsten Graul } 60087f88cdaSKarsten Graul return NULL; 60187f88cdaSKarsten Graul } 60287f88cdaSKarsten Graul 60387f88cdaSKarsten Graul /* return next rmb from buffer lists */ 60487f88cdaSKarsten Graul static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr, 60587f88cdaSKarsten Graul int *buf_lst, 60687f88cdaSKarsten Graul struct smc_buf_desc *buf_pos) 60787f88cdaSKarsten Graul { 60887f88cdaSKarsten Graul struct smc_buf_desc *buf_next; 60987f88cdaSKarsten Graul 61087f88cdaSKarsten Graul if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) { 61187f88cdaSKarsten Graul (*buf_lst)++; 61287f88cdaSKarsten Graul return _smc_llc_get_next_rmb(lgr, buf_lst); 61387f88cdaSKarsten Graul } 61487f88cdaSKarsten Graul buf_next = list_next_entry(buf_pos, list); 61587f88cdaSKarsten Graul return buf_next; 61687f88cdaSKarsten Graul } 61787f88cdaSKarsten Graul 61887f88cdaSKarsten Graul static struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr, 61987f88cdaSKarsten Graul int *buf_lst) 62087f88cdaSKarsten Graul { 62187f88cdaSKarsten Graul *buf_lst = 0; 62287f88cdaSKarsten Graul return smc_llc_get_next_rmb(lgr, buf_lst, NULL); 62387f88cdaSKarsten Graul } 62487f88cdaSKarsten Graul 62587f88cdaSKarsten Graul /* send one add_link_continue msg */ 62687f88cdaSKarsten Graul static int smc_llc_add_link_cont(struct smc_link *link, 62787f88cdaSKarsten Graul struct smc_link *link_new, u8 *num_rkeys_todo, 62887f88cdaSKarsten Graul int *buf_lst, struct smc_buf_desc **buf_pos) 62987f88cdaSKarsten Graul { 63087f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont *addc_llc; 63187f88cdaSKarsten Graul struct smc_link_group *lgr = link->lgr; 63287f88cdaSKarsten Graul int prim_lnk_idx, lnk_idx, i, rc; 63387f88cdaSKarsten Graul struct smc_wr_tx_pend_priv *pend; 63487f88cdaSKarsten Graul struct smc_wr_buf *wr_buf; 63587f88cdaSKarsten Graul struct smc_buf_desc *rmb; 63687f88cdaSKarsten Graul u8 n; 63787f88cdaSKarsten Graul 63887f88cdaSKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 63987f88cdaSKarsten Graul if (rc) 64087f88cdaSKarsten Graul return rc; 64187f88cdaSKarsten Graul addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf; 64287f88cdaSKarsten Graul memset(addc_llc, 0, sizeof(*addc_llc)); 64387f88cdaSKarsten Graul 64487f88cdaSKarsten Graul prim_lnk_idx = link->link_idx; 64587f88cdaSKarsten Graul lnk_idx = link_new->link_idx; 64687f88cdaSKarsten Graul addc_llc->link_num = link_new->link_id; 64787f88cdaSKarsten Graul addc_llc->num_rkeys = *num_rkeys_todo; 64887f88cdaSKarsten Graul n = *num_rkeys_todo; 64987f88cdaSKarsten Graul for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) { 65087f88cdaSKarsten Graul if (!*buf_pos) { 65187f88cdaSKarsten Graul addc_llc->num_rkeys = addc_llc->num_rkeys - 65287f88cdaSKarsten Graul *num_rkeys_todo; 65387f88cdaSKarsten Graul *num_rkeys_todo = 0; 65487f88cdaSKarsten Graul break; 65587f88cdaSKarsten Graul } 65687f88cdaSKarsten Graul rmb = *buf_pos; 65787f88cdaSKarsten Graul 65887f88cdaSKarsten Graul addc_llc->rt[i].rmb_key = htonl(rmb->mr_rx[prim_lnk_idx]->rkey); 65987f88cdaSKarsten Graul addc_llc->rt[i].rmb_key_new = htonl(rmb->mr_rx[lnk_idx]->rkey); 66087f88cdaSKarsten Graul addc_llc->rt[i].rmb_vaddr_new = 66187f88cdaSKarsten Graul cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); 66287f88cdaSKarsten Graul 66387f88cdaSKarsten Graul (*num_rkeys_todo)--; 66487f88cdaSKarsten Graul *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 66587f88cdaSKarsten Graul while (*buf_pos && !(*buf_pos)->used) 66687f88cdaSKarsten Graul *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 66787f88cdaSKarsten Graul } 66887f88cdaSKarsten Graul addc_llc->hd.common.type = SMC_LLC_ADD_LINK_CONT; 66987f88cdaSKarsten Graul addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont); 67087f88cdaSKarsten Graul if (lgr->role == SMC_CLNT) 67187f88cdaSKarsten Graul addc_llc->hd.flags |= SMC_LLC_FLAG_RESP; 67287f88cdaSKarsten Graul return smc_wr_tx_send(link, pend); 67387f88cdaSKarsten Graul } 67487f88cdaSKarsten Graul 67587f88cdaSKarsten Graul static int smc_llc_cli_rkey_exchange(struct smc_link *link, 67687f88cdaSKarsten Graul struct smc_link *link_new) 67787f88cdaSKarsten Graul { 67887f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont *addc_llc; 67987f88cdaSKarsten Graul struct smc_link_group *lgr = link->lgr; 68087f88cdaSKarsten Graul u8 max, num_rkeys_send, num_rkeys_recv; 68187f88cdaSKarsten Graul struct smc_llc_qentry *qentry; 68287f88cdaSKarsten Graul struct smc_buf_desc *buf_pos; 68387f88cdaSKarsten Graul int buf_lst; 68487f88cdaSKarsten Graul int rc = 0; 68587f88cdaSKarsten Graul int i; 68687f88cdaSKarsten Graul 68787f88cdaSKarsten Graul mutex_lock(&lgr->rmbs_lock); 68887f88cdaSKarsten Graul num_rkeys_send = lgr->conns_num; 68987f88cdaSKarsten Graul buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 69087f88cdaSKarsten Graul do { 69187f88cdaSKarsten Graul qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME, 69287f88cdaSKarsten Graul SMC_LLC_ADD_LINK_CONT); 69387f88cdaSKarsten Graul if (!qentry) { 69487f88cdaSKarsten Graul rc = -ETIMEDOUT; 69587f88cdaSKarsten Graul break; 69687f88cdaSKarsten Graul } 69787f88cdaSKarsten Graul addc_llc = &qentry->msg.add_link_cont; 69887f88cdaSKarsten Graul num_rkeys_recv = addc_llc->num_rkeys; 69987f88cdaSKarsten Graul max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 70087f88cdaSKarsten Graul for (i = 0; i < max; i++) { 70187f88cdaSKarsten Graul smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 70287f88cdaSKarsten Graul addc_llc->rt[i].rmb_key, 70387f88cdaSKarsten Graul addc_llc->rt[i].rmb_vaddr_new, 70487f88cdaSKarsten Graul addc_llc->rt[i].rmb_key_new); 70587f88cdaSKarsten Graul num_rkeys_recv--; 70687f88cdaSKarsten Graul } 70787f88cdaSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 70887f88cdaSKarsten Graul rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 70987f88cdaSKarsten Graul &buf_lst, &buf_pos); 71087f88cdaSKarsten Graul if (rc) 71187f88cdaSKarsten Graul break; 71287f88cdaSKarsten Graul } while (num_rkeys_send || num_rkeys_recv); 71387f88cdaSKarsten Graul 71487f88cdaSKarsten Graul mutex_unlock(&lgr->rmbs_lock); 71587f88cdaSKarsten Graul return rc; 71687f88cdaSKarsten Graul } 71787f88cdaSKarsten Graul 718336ba09fSKarsten Graul /* prepare and send an add link reject response */ 719336ba09fSKarsten Graul static int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry) 720336ba09fSKarsten Graul { 721336ba09fSKarsten Graul qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 722336ba09fSKarsten Graul qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; 723336ba09fSKarsten Graul qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; 724336ba09fSKarsten Graul return smc_llc_send_message(qentry->link, &qentry->msg); 725336ba09fSKarsten Graul } 726336ba09fSKarsten Graul 727b1570a87SKarsten Graul static int smc_llc_cli_conf_link(struct smc_link *link, 728b1570a87SKarsten Graul struct smc_init_info *ini, 729b1570a87SKarsten Graul struct smc_link *link_new, 730b1570a87SKarsten Graul enum smc_lgr_type lgr_new_t) 731b1570a87SKarsten Graul { 732b1570a87SKarsten Graul struct smc_link_group *lgr = link->lgr; 733b1570a87SKarsten Graul struct smc_llc_msg_del_link *del_llc; 734b1570a87SKarsten Graul struct smc_llc_qentry *qentry = NULL; 735b1570a87SKarsten Graul int rc = 0; 736b1570a87SKarsten Graul 737b1570a87SKarsten Graul /* receive CONFIRM LINK request over RoCE fabric */ 738b1570a87SKarsten Graul qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0); 739b1570a87SKarsten Graul if (!qentry) { 740b1570a87SKarsten Graul rc = smc_llc_send_delete_link(link, link_new->link_id, 741b1570a87SKarsten Graul SMC_LLC_REQ, false, 742b1570a87SKarsten Graul SMC_LLC_DEL_LOST_PATH); 743b1570a87SKarsten Graul return -ENOLINK; 744b1570a87SKarsten Graul } 745b1570a87SKarsten Graul if (qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) { 746b1570a87SKarsten Graul /* received DELETE_LINK instead */ 747b1570a87SKarsten Graul del_llc = &qentry->msg.delete_link; 748b1570a87SKarsten Graul qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 749b1570a87SKarsten Graul smc_llc_send_message(link, &qentry->msg); 750b1570a87SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 751b1570a87SKarsten Graul return -ENOLINK; 752b1570a87SKarsten Graul } 753b1570a87SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 754b1570a87SKarsten Graul 755b1570a87SKarsten Graul rc = smc_ib_modify_qp_rts(link_new); 756b1570a87SKarsten Graul if (rc) { 757b1570a87SKarsten Graul smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 758b1570a87SKarsten Graul false, SMC_LLC_DEL_LOST_PATH); 759b1570a87SKarsten Graul return -ENOLINK; 760b1570a87SKarsten Graul } 761b1570a87SKarsten Graul smc_wr_remember_qp_attr(link_new); 762b1570a87SKarsten Graul 763b1570a87SKarsten Graul rc = smcr_buf_reg_lgr(link_new); 764b1570a87SKarsten Graul if (rc) { 765b1570a87SKarsten Graul smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 766b1570a87SKarsten Graul false, SMC_LLC_DEL_LOST_PATH); 767b1570a87SKarsten Graul return -ENOLINK; 768b1570a87SKarsten Graul } 769b1570a87SKarsten Graul 770b1570a87SKarsten Graul /* send CONFIRM LINK response over RoCE fabric */ 771b1570a87SKarsten Graul rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP); 772b1570a87SKarsten Graul if (rc) { 773b1570a87SKarsten Graul smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 774b1570a87SKarsten Graul false, SMC_LLC_DEL_LOST_PATH); 775b1570a87SKarsten Graul return -ENOLINK; 776b1570a87SKarsten Graul } 777b1570a87SKarsten Graul smc_llc_link_active(link_new); 778b1570a87SKarsten Graul lgr->type = lgr_new_t; 779b1570a87SKarsten Graul return 0; 780b1570a87SKarsten Graul } 781b1570a87SKarsten Graul 782336ba09fSKarsten Graul static void smc_llc_save_add_link_info(struct smc_link *link, 783336ba09fSKarsten Graul struct smc_llc_msg_add_link *add_llc) 784336ba09fSKarsten Graul { 785336ba09fSKarsten Graul link->peer_qpn = ntoh24(add_llc->sender_qp_num); 786336ba09fSKarsten Graul memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE); 787336ba09fSKarsten Graul memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN); 788336ba09fSKarsten Graul link->peer_psn = ntoh24(add_llc->initial_psn); 789336ba09fSKarsten Graul link->peer_mtu = add_llc->qp_mtu; 790336ba09fSKarsten Graul } 791336ba09fSKarsten Graul 792336ba09fSKarsten Graul /* as an SMC client, process an add link request */ 793336ba09fSKarsten Graul int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry) 794336ba09fSKarsten Graul { 795336ba09fSKarsten Graul struct smc_llc_msg_add_link *llc = &qentry->msg.add_link; 796336ba09fSKarsten Graul enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 797336ba09fSKarsten Graul struct smc_link_group *lgr = smc_get_lgr(link); 798336ba09fSKarsten Graul struct smc_link *lnk_new = NULL; 799336ba09fSKarsten Graul struct smc_init_info ini; 800336ba09fSKarsten Graul int lnk_idx, rc = 0; 801336ba09fSKarsten Graul 802336ba09fSKarsten Graul ini.vlan_id = lgr->vlan_id; 803336ba09fSKarsten Graul smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev); 804336ba09fSKarsten Graul if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 805336ba09fSKarsten Graul !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN)) { 806336ba09fSKarsten Graul if (!ini.ib_dev) 807336ba09fSKarsten Graul goto out_reject; 808336ba09fSKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 809336ba09fSKarsten Graul } 810336ba09fSKarsten Graul if (!ini.ib_dev) { 811336ba09fSKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 812336ba09fSKarsten Graul ini.ib_dev = link->smcibdev; 813336ba09fSKarsten Graul ini.ib_port = link->ibport; 814336ba09fSKarsten Graul } 815336ba09fSKarsten Graul lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 816336ba09fSKarsten Graul if (lnk_idx < 0) 817336ba09fSKarsten Graul goto out_reject; 818336ba09fSKarsten Graul lnk_new = &lgr->lnk[lnk_idx]; 819336ba09fSKarsten Graul rc = smcr_link_init(lgr, lnk_new, lnk_idx, &ini); 820336ba09fSKarsten Graul if (rc) 821336ba09fSKarsten Graul goto out_reject; 822336ba09fSKarsten Graul smc_llc_save_add_link_info(lnk_new, llc); 823336ba09fSKarsten Graul lnk_new->link_id = llc->link_num; 824336ba09fSKarsten Graul 825336ba09fSKarsten Graul rc = smc_ib_ready_link(lnk_new); 826336ba09fSKarsten Graul if (rc) 827336ba09fSKarsten Graul goto out_clear_lnk; 828336ba09fSKarsten Graul 829336ba09fSKarsten Graul rc = smcr_buf_map_lgr(lnk_new); 830336ba09fSKarsten Graul if (rc) 831336ba09fSKarsten Graul goto out_clear_lnk; 832336ba09fSKarsten Graul 833336ba09fSKarsten Graul rc = smc_llc_send_add_link(link, 834336ba09fSKarsten Graul lnk_new->smcibdev->mac[ini.ib_port - 1], 835336ba09fSKarsten Graul lnk_new->gid, lnk_new, SMC_LLC_RESP); 836336ba09fSKarsten Graul if (rc) 837336ba09fSKarsten Graul goto out_clear_lnk; 83887f88cdaSKarsten Graul rc = smc_llc_cli_rkey_exchange(link, lnk_new); 839336ba09fSKarsten Graul if (rc) { 840336ba09fSKarsten Graul rc = 0; 841336ba09fSKarsten Graul goto out_clear_lnk; 842336ba09fSKarsten Graul } 843b1570a87SKarsten Graul rc = smc_llc_cli_conf_link(link, &ini, lnk_new, lgr_new_t); 844336ba09fSKarsten Graul if (!rc) 845336ba09fSKarsten Graul goto out; 846336ba09fSKarsten Graul out_clear_lnk: 847336ba09fSKarsten Graul smcr_link_clear(lnk_new); 848336ba09fSKarsten Graul out_reject: 849336ba09fSKarsten Graul smc_llc_cli_add_link_reject(qentry); 850336ba09fSKarsten Graul out: 851336ba09fSKarsten Graul kfree(qentry); 852336ba09fSKarsten Graul return rc; 853336ba09fSKarsten Graul } 854336ba09fSKarsten Graul 855b1570a87SKarsten Graul static void smc_llc_process_cli_add_link(struct smc_link_group *lgr) 856b1570a87SKarsten Graul { 857b1570a87SKarsten Graul struct smc_llc_qentry *qentry; 858b1570a87SKarsten Graul 859b1570a87SKarsten Graul qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 860b1570a87SKarsten Graul 861b1570a87SKarsten Graul mutex_lock(&lgr->llc_conf_mutex); 862b1570a87SKarsten Graul smc_llc_cli_add_link(qentry->link, qentry); 863b1570a87SKarsten Graul mutex_unlock(&lgr->llc_conf_mutex); 864b1570a87SKarsten Graul } 865b1570a87SKarsten Graul 866*57b49924SKarsten Graul static int smc_llc_srv_rkey_exchange(struct smc_link *link, 867*57b49924SKarsten Graul struct smc_link *link_new) 868*57b49924SKarsten Graul { 869*57b49924SKarsten Graul struct smc_llc_msg_add_link_cont *addc_llc; 870*57b49924SKarsten Graul struct smc_link_group *lgr = link->lgr; 871*57b49924SKarsten Graul u8 max, num_rkeys_send, num_rkeys_recv; 872*57b49924SKarsten Graul struct smc_llc_qentry *qentry = NULL; 873*57b49924SKarsten Graul struct smc_buf_desc *buf_pos; 874*57b49924SKarsten Graul int buf_lst; 875*57b49924SKarsten Graul int rc = 0; 876*57b49924SKarsten Graul int i; 877*57b49924SKarsten Graul 878*57b49924SKarsten Graul mutex_lock(&lgr->rmbs_lock); 879*57b49924SKarsten Graul num_rkeys_send = lgr->conns_num; 880*57b49924SKarsten Graul buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 881*57b49924SKarsten Graul do { 882*57b49924SKarsten Graul smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 883*57b49924SKarsten Graul &buf_lst, &buf_pos); 884*57b49924SKarsten Graul qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, 885*57b49924SKarsten Graul SMC_LLC_ADD_LINK_CONT); 886*57b49924SKarsten Graul if (!qentry) { 887*57b49924SKarsten Graul rc = -ETIMEDOUT; 888*57b49924SKarsten Graul goto out; 889*57b49924SKarsten Graul } 890*57b49924SKarsten Graul addc_llc = &qentry->msg.add_link_cont; 891*57b49924SKarsten Graul num_rkeys_recv = addc_llc->num_rkeys; 892*57b49924SKarsten Graul max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 893*57b49924SKarsten Graul for (i = 0; i < max; i++) { 894*57b49924SKarsten Graul smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 895*57b49924SKarsten Graul addc_llc->rt[i].rmb_key, 896*57b49924SKarsten Graul addc_llc->rt[i].rmb_vaddr_new, 897*57b49924SKarsten Graul addc_llc->rt[i].rmb_key_new); 898*57b49924SKarsten Graul num_rkeys_recv--; 899*57b49924SKarsten Graul } 900*57b49924SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 901*57b49924SKarsten Graul } while (num_rkeys_send || num_rkeys_recv); 902*57b49924SKarsten Graul out: 903*57b49924SKarsten Graul mutex_unlock(&lgr->rmbs_lock); 904*57b49924SKarsten Graul return rc; 905*57b49924SKarsten Graul } 906*57b49924SKarsten Graul 9072d2209f2SKarsten Graul int smc_llc_srv_add_link(struct smc_link *link) 9082d2209f2SKarsten Graul { 9092d2209f2SKarsten Graul enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 9102d2209f2SKarsten Graul struct smc_link_group *lgr = link->lgr; 9112d2209f2SKarsten Graul struct smc_llc_msg_add_link *add_llc; 9122d2209f2SKarsten Graul struct smc_llc_qentry *qentry = NULL; 9132d2209f2SKarsten Graul struct smc_link *link_new; 9142d2209f2SKarsten Graul struct smc_init_info ini; 9152d2209f2SKarsten Graul int lnk_idx, rc = 0; 9162d2209f2SKarsten Graul 9172d2209f2SKarsten Graul /* ignore client add link recommendation, start new flow */ 9182d2209f2SKarsten Graul ini.vlan_id = lgr->vlan_id; 9192d2209f2SKarsten Graul smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev); 9202d2209f2SKarsten Graul if (!ini.ib_dev) { 9212d2209f2SKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 9222d2209f2SKarsten Graul ini.ib_dev = link->smcibdev; 9232d2209f2SKarsten Graul ini.ib_port = link->ibport; 9242d2209f2SKarsten Graul } 9252d2209f2SKarsten Graul lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 9262d2209f2SKarsten Graul if (lnk_idx < 0) 9272d2209f2SKarsten Graul return 0; 9282d2209f2SKarsten Graul 9292d2209f2SKarsten Graul rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, &ini); 9302d2209f2SKarsten Graul if (rc) 9312d2209f2SKarsten Graul return rc; 9322d2209f2SKarsten Graul link_new = &lgr->lnk[lnk_idx]; 9332d2209f2SKarsten Graul rc = smc_llc_send_add_link(link, 9342d2209f2SKarsten Graul link_new->smcibdev->mac[ini.ib_port - 1], 9352d2209f2SKarsten Graul link_new->gid, link_new, SMC_LLC_REQ); 9362d2209f2SKarsten Graul if (rc) 9372d2209f2SKarsten Graul goto out_err; 9382d2209f2SKarsten Graul /* receive ADD LINK response over the RoCE fabric */ 9392d2209f2SKarsten Graul qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK); 9402d2209f2SKarsten Graul if (!qentry) { 9412d2209f2SKarsten Graul rc = -ETIMEDOUT; 9422d2209f2SKarsten Graul goto out_err; 9432d2209f2SKarsten Graul } 9442d2209f2SKarsten Graul add_llc = &qentry->msg.add_link; 9452d2209f2SKarsten Graul if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) { 9462d2209f2SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 9472d2209f2SKarsten Graul rc = -ENOLINK; 9482d2209f2SKarsten Graul goto out_err; 9492d2209f2SKarsten Graul } 9502d2209f2SKarsten Graul if (lgr->type == SMC_LGR_SINGLE && 9512d2209f2SKarsten Graul (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 9522d2209f2SKarsten Graul !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN))) { 9532d2209f2SKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 9542d2209f2SKarsten Graul } 9552d2209f2SKarsten Graul smc_llc_save_add_link_info(link_new, add_llc); 9562d2209f2SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 9572d2209f2SKarsten Graul 9582d2209f2SKarsten Graul rc = smc_ib_ready_link(link_new); 9592d2209f2SKarsten Graul if (rc) 9602d2209f2SKarsten Graul goto out_err; 9612d2209f2SKarsten Graul rc = smcr_buf_map_lgr(link_new); 9622d2209f2SKarsten Graul if (rc) 9632d2209f2SKarsten Graul goto out_err; 9642d2209f2SKarsten Graul rc = smcr_buf_reg_lgr(link_new); 9652d2209f2SKarsten Graul if (rc) 9662d2209f2SKarsten Graul goto out_err; 967*57b49924SKarsten Graul rc = smc_llc_srv_rkey_exchange(link, link_new); 9682d2209f2SKarsten Graul if (rc) 9692d2209f2SKarsten Graul goto out_err; 9702d2209f2SKarsten Graul /* tbd: rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t); */ 9712d2209f2SKarsten Graul if (rc) 9722d2209f2SKarsten Graul goto out_err; 9732d2209f2SKarsten Graul return 0; 9742d2209f2SKarsten Graul out_err: 9752d2209f2SKarsten Graul smcr_link_clear(link_new); 9762d2209f2SKarsten Graul return rc; 9772d2209f2SKarsten Graul } 9782d2209f2SKarsten Graul 9792d2209f2SKarsten Graul static void smc_llc_process_srv_add_link(struct smc_link_group *lgr) 9802d2209f2SKarsten Graul { 9812d2209f2SKarsten Graul struct smc_link *link = lgr->llc_flow_lcl.qentry->link; 9822d2209f2SKarsten Graul int rc; 9832d2209f2SKarsten Graul 9842d2209f2SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 9852d2209f2SKarsten Graul 9862d2209f2SKarsten Graul mutex_lock(&lgr->llc_conf_mutex); 9872d2209f2SKarsten Graul rc = smc_llc_srv_add_link(link); 9882d2209f2SKarsten Graul if (!rc && lgr->type == SMC_LGR_SYMMETRIC) { 9892d2209f2SKarsten Graul /* delete any asymmetric link */ 9902d2209f2SKarsten Graul /* tbd: smc_llc_delete_asym_link(lgr); */ 9912d2209f2SKarsten Graul } 9922d2209f2SKarsten Graul mutex_unlock(&lgr->llc_conf_mutex); 9932d2209f2SKarsten Graul } 9942d2209f2SKarsten Graul 995b45e7f98SKarsten Graul /* worker to process an add link message */ 996b45e7f98SKarsten Graul static void smc_llc_add_link_work(struct work_struct *work) 997b45e7f98SKarsten Graul { 998b45e7f98SKarsten Graul struct smc_link_group *lgr = container_of(work, struct smc_link_group, 999b45e7f98SKarsten Graul llc_add_link_work); 1000b45e7f98SKarsten Graul 1001b45e7f98SKarsten Graul if (list_empty(&lgr->list)) { 1002b45e7f98SKarsten Graul /* link group is terminating */ 1003b45e7f98SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 1004b45e7f98SKarsten Graul goto out; 1005b45e7f98SKarsten Graul } 1006b45e7f98SKarsten Graul 1007b1570a87SKarsten Graul if (lgr->role == SMC_CLNT) 1008b1570a87SKarsten Graul smc_llc_process_cli_add_link(lgr); 10092d2209f2SKarsten Graul else 10102d2209f2SKarsten Graul smc_llc_process_srv_add_link(lgr); 1011b45e7f98SKarsten Graul out: 1012b45e7f98SKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 1013b45e7f98SKarsten Graul } 1014b45e7f98SKarsten Graul 101552bedf37SKarsten Graul static void smc_llc_rx_delete_link(struct smc_link *link, 101652bedf37SKarsten Graul struct smc_llc_msg_del_link *llc) 101752bedf37SKarsten Graul { 101800e5fb26SStefan Raspl struct smc_link_group *lgr = smc_get_lgr(link); 101952bedf37SKarsten Graul 10209651b934SKarsten Graul smc_lgr_forget(lgr); 10210d18a0cbSKarsten Graul if (lgr->role == SMC_SERV) { 10220d18a0cbSKarsten Graul /* client asks to delete this link, send request */ 1023fbed3b37SKarsten Graul smc_llc_send_delete_link(link, 0, SMC_LLC_REQ, true, 1024fbed3b37SKarsten Graul SMC_LLC_DEL_PROG_INIT_TERM); 102552bedf37SKarsten Graul } else { 10260d18a0cbSKarsten Graul /* server requests to delete this link, send response */ 1027fbed3b37SKarsten Graul smc_llc_send_delete_link(link, 0, SMC_LLC_RESP, true, 1028fbed3b37SKarsten Graul SMC_LLC_DEL_PROG_INIT_TERM); 102952bedf37SKarsten Graul } 103087523930SKarsten Graul smcr_link_down_cond(link); 103152bedf37SKarsten Graul } 103252bedf37SKarsten Graul 10333bc67e09SKarsten Graul /* process a confirm_rkey request from peer, remote flow */ 10343bc67e09SKarsten Graul static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr) 10354ed75de5SKarsten Graul { 10363bc67e09SKarsten Graul struct smc_llc_msg_confirm_rkey *llc; 10373bc67e09SKarsten Graul struct smc_llc_qentry *qentry; 10383bc67e09SKarsten Graul struct smc_link *link; 10393bc67e09SKarsten Graul int num_entries; 10403bc67e09SKarsten Graul int rk_idx; 10413bc67e09SKarsten Graul int i; 10424ed75de5SKarsten Graul 10433bc67e09SKarsten Graul qentry = lgr->llc_flow_rmt.qentry; 10443bc67e09SKarsten Graul llc = &qentry->msg.confirm_rkey; 10453bc67e09SKarsten Graul link = qentry->link; 10463bc67e09SKarsten Graul 10473bc67e09SKarsten Graul num_entries = llc->rtoken[0].num_rkeys; 10483bc67e09SKarsten Graul /* first rkey entry is for receiving link */ 10493bc67e09SKarsten Graul rk_idx = smc_rtoken_add(link, 10504ed75de5SKarsten Graul llc->rtoken[0].rmb_vaddr, 10514ed75de5SKarsten Graul llc->rtoken[0].rmb_key); 10523bc67e09SKarsten Graul if (rk_idx < 0) 10533bc67e09SKarsten Graul goto out_err; 10544ed75de5SKarsten Graul 10553bc67e09SKarsten Graul for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++) 10563bc67e09SKarsten Graul smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id, 10573bc67e09SKarsten Graul llc->rtoken[i].rmb_vaddr, 10583bc67e09SKarsten Graul llc->rtoken[i].rmb_key); 10593bc67e09SKarsten Graul /* max links is 3 so there is no need to support conf_rkey_cont msgs */ 10603bc67e09SKarsten Graul goto out; 10613bc67e09SKarsten Graul out_err: 10624ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 10633bc67e09SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY; 10643bc67e09SKarsten Graul out: 10653bc67e09SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 10663bc67e09SKarsten Graul smc_llc_send_message(link, &qentry->msg); 10673bc67e09SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 10684ed75de5SKarsten Graul } 10694ed75de5SKarsten Graul 1070218b24feSKarsten Graul /* process a delete_rkey request from peer, remote flow */ 1071218b24feSKarsten Graul static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr) 10724ed75de5SKarsten Graul { 1073218b24feSKarsten Graul struct smc_llc_msg_delete_rkey *llc; 1074218b24feSKarsten Graul struct smc_llc_qentry *qentry; 1075218b24feSKarsten Graul struct smc_link *link; 10764ed75de5SKarsten Graul u8 err_mask = 0; 10774ed75de5SKarsten Graul int i, max; 10784ed75de5SKarsten Graul 1079218b24feSKarsten Graul qentry = lgr->llc_flow_rmt.qentry; 1080218b24feSKarsten Graul llc = &qentry->msg.delete_rkey; 1081218b24feSKarsten Graul link = qentry->link; 1082218b24feSKarsten Graul 10834ed75de5SKarsten Graul max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX); 10844ed75de5SKarsten Graul for (i = 0; i < max; i++) { 1085387707fdSKarsten Graul if (smc_rtoken_delete(link, llc->rkey[i])) 10864ed75de5SKarsten Graul err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i); 10874ed75de5SKarsten Graul } 10884ed75de5SKarsten Graul if (err_mask) { 10894ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 10904ed75de5SKarsten Graul llc->err_mask = err_mask; 10914ed75de5SKarsten Graul } 1092218b24feSKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 1093218b24feSKarsten Graul smc_llc_send_message(link, &qentry->msg); 1094218b24feSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 1095218b24feSKarsten Graul } 10964ed75de5SKarsten Graul 10976c8968c4SKarsten Graul /* flush the llc event queue */ 109800a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr) 10999bf9abeaSUrsula Braun { 11006c8968c4SKarsten Graul struct smc_llc_qentry *qentry, *q; 11019bf9abeaSUrsula Braun 11026c8968c4SKarsten Graul spin_lock_bh(&lgr->llc_event_q_lock); 11036c8968c4SKarsten Graul list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) { 11046c8968c4SKarsten Graul list_del_init(&qentry->list); 11056c8968c4SKarsten Graul kfree(qentry); 11066c8968c4SKarsten Graul } 11076c8968c4SKarsten Graul spin_unlock_bh(&lgr->llc_event_q_lock); 11086c8968c4SKarsten Graul } 11096c8968c4SKarsten Graul 11106c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry) 11116c8968c4SKarsten Graul { 11126c8968c4SKarsten Graul union smc_llc_msg *llc = &qentry->msg; 11136c8968c4SKarsten Graul struct smc_link *link = qentry->link; 11140fb0b02bSKarsten Graul struct smc_link_group *lgr = link->lgr; 11156c8968c4SKarsten Graul 1116d854fcbfSKarsten Graul if (!smc_link_usable(link)) 11176c8968c4SKarsten Graul goto out; 1118313164daSKarsten Graul 1119313164daSKarsten Graul switch (llc->raw.hdr.common.type) { 1120313164daSKarsten Graul case SMC_LLC_TEST_LINK: 112156e8091cSKarsten Graul llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP; 112256e8091cSKarsten Graul smc_llc_send_message(link, llc); 1123313164daSKarsten Graul break; 112452bedf37SKarsten Graul case SMC_LLC_ADD_LINK: 11250fb0b02bSKarsten Graul if (list_empty(&lgr->list)) 11260fb0b02bSKarsten Graul goto out; /* lgr is terminating */ 11270fb0b02bSKarsten Graul if (lgr->role == SMC_CLNT) { 11280fb0b02bSKarsten Graul if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK) { 11290fb0b02bSKarsten Graul /* a flow is waiting for this message */ 11300fb0b02bSKarsten Graul smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, 11310fb0b02bSKarsten Graul qentry); 11320fb0b02bSKarsten Graul wake_up_interruptible(&lgr->llc_waiter); 11330fb0b02bSKarsten Graul } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, 11340fb0b02bSKarsten Graul qentry)) { 1135b45e7f98SKarsten Graul schedule_work(&lgr->llc_add_link_work); 11360fb0b02bSKarsten Graul } 11370fb0b02bSKarsten Graul } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 11380fb0b02bSKarsten Graul /* as smc server, handle client suggestion */ 1139b45e7f98SKarsten Graul schedule_work(&lgr->llc_add_link_work); 11400fb0b02bSKarsten Graul } 11410fb0b02bSKarsten Graul return; 11420fb0b02bSKarsten Graul case SMC_LLC_CONFIRM_LINK: 114387f88cdaSKarsten Graul case SMC_LLC_ADD_LINK_CONT: 11440fb0b02bSKarsten Graul if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { 11450fb0b02bSKarsten Graul /* a flow is waiting for this message */ 11460fb0b02bSKarsten Graul smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); 11470fb0b02bSKarsten Graul wake_up_interruptible(&lgr->llc_waiter); 11480fb0b02bSKarsten Graul return; 11490fb0b02bSKarsten Graul } 115052bedf37SKarsten Graul break; 115152bedf37SKarsten Graul case SMC_LLC_DELETE_LINK: 115252bedf37SKarsten Graul smc_llc_rx_delete_link(link, &llc->delete_link); 115352bedf37SKarsten Graul break; 11544ed75de5SKarsten Graul case SMC_LLC_CONFIRM_RKEY: 11553bc67e09SKarsten Graul /* new request from remote, assign to remote flow */ 11563bc67e09SKarsten Graul if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 11573bc67e09SKarsten Graul /* process here, does not wait for more llc msgs */ 11583bc67e09SKarsten Graul smc_llc_rmt_conf_rkey(lgr); 11593bc67e09SKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 11603bc67e09SKarsten Graul } 11613bc67e09SKarsten Graul return; 11624ed75de5SKarsten Graul case SMC_LLC_CONFIRM_RKEY_CONT: 116342d18accSKarsten Graul /* not used because max links is 3, and 3 rkeys fit into 116442d18accSKarsten Graul * one CONFIRM_RKEY message 116542d18accSKarsten Graul */ 11664ed75de5SKarsten Graul break; 11674ed75de5SKarsten Graul case SMC_LLC_DELETE_RKEY: 1168218b24feSKarsten Graul /* new request from remote, assign to remote flow */ 1169218b24feSKarsten Graul if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 1170218b24feSKarsten Graul /* process here, does not wait for more llc msgs */ 1171218b24feSKarsten Graul smc_llc_rmt_delete_rkey(lgr); 1172218b24feSKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 1173218b24feSKarsten Graul } 1174218b24feSKarsten Graul return; 1175313164daSKarsten Graul } 11766c8968c4SKarsten Graul out: 11776c8968c4SKarsten Graul kfree(qentry); 11786c8968c4SKarsten Graul } 11796c8968c4SKarsten Graul 11806c8968c4SKarsten Graul /* worker to process llc messages on the event queue */ 11816c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work) 11826c8968c4SKarsten Graul { 11836c8968c4SKarsten Graul struct smc_link_group *lgr = container_of(work, struct smc_link_group, 11846c8968c4SKarsten Graul llc_event_work); 11856c8968c4SKarsten Graul struct smc_llc_qentry *qentry; 11866c8968c4SKarsten Graul 1187555da9afSKarsten Graul if (!lgr->llc_flow_lcl.type && lgr->delayed_event) { 1188555da9afSKarsten Graul if (smc_link_usable(lgr->delayed_event->link)) { 1189555da9afSKarsten Graul smc_llc_event_handler(lgr->delayed_event); 1190555da9afSKarsten Graul } else { 1191555da9afSKarsten Graul qentry = lgr->delayed_event; 1192555da9afSKarsten Graul lgr->delayed_event = NULL; 1193555da9afSKarsten Graul kfree(qentry); 1194555da9afSKarsten Graul } 1195555da9afSKarsten Graul } 1196555da9afSKarsten Graul 11976c8968c4SKarsten Graul again: 11986c8968c4SKarsten Graul spin_lock_bh(&lgr->llc_event_q_lock); 11996c8968c4SKarsten Graul if (!list_empty(&lgr->llc_event_q)) { 12006c8968c4SKarsten Graul qentry = list_first_entry(&lgr->llc_event_q, 12016c8968c4SKarsten Graul struct smc_llc_qentry, list); 12026c8968c4SKarsten Graul list_del_init(&qentry->list); 12036c8968c4SKarsten Graul spin_unlock_bh(&lgr->llc_event_q_lock); 12046c8968c4SKarsten Graul smc_llc_event_handler(qentry); 12056c8968c4SKarsten Graul goto again; 12066c8968c4SKarsten Graul } 12076c8968c4SKarsten Graul spin_unlock_bh(&lgr->llc_event_q_lock); 12086c8968c4SKarsten Graul } 12096c8968c4SKarsten Graul 1210ef79d439SKarsten Graul /* process llc responses in tasklet context */ 1211a6688d91SKarsten Graul static void smc_llc_rx_response(struct smc_link *link, 1212a6688d91SKarsten Graul struct smc_llc_qentry *qentry) 1213ef79d439SKarsten Graul { 1214a6688d91SKarsten Graul u8 llc_type = qentry->msg.raw.hdr.common.type; 1215ef79d439SKarsten Graul 1216a6688d91SKarsten Graul switch (llc_type) { 1217ef79d439SKarsten Graul case SMC_LLC_TEST_LINK: 1218ef79d439SKarsten Graul if (link->state == SMC_LNK_ACTIVE) 1219ef79d439SKarsten Graul complete(&link->llc_testlink_resp); 1220ef79d439SKarsten Graul break; 1221ef79d439SKarsten Graul case SMC_LLC_ADD_LINK: 12224667bb4aSKarsten Graul case SMC_LLC_CONFIRM_LINK: 122387f88cdaSKarsten Graul case SMC_LLC_ADD_LINK_CONT: 12243d88a21bSKarsten Graul case SMC_LLC_CONFIRM_RKEY: 12256d74c3a8SKarsten Graul case SMC_LLC_DELETE_RKEY: 12264667bb4aSKarsten Graul /* assign responses to the local flow, we requested them */ 12274667bb4aSKarsten Graul smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry); 12284667bb4aSKarsten Graul wake_up_interruptible(&link->lgr->llc_waiter); 12294667bb4aSKarsten Graul return; 1230ef79d439SKarsten Graul case SMC_LLC_DELETE_LINK: 1231ef79d439SKarsten Graul if (link->lgr->role == SMC_SERV) 1232ef79d439SKarsten Graul smc_lgr_schedule_free_work_fast(link->lgr); 1233ef79d439SKarsten Graul break; 1234ef79d439SKarsten Graul case SMC_LLC_CONFIRM_RKEY_CONT: 123542d18accSKarsten Graul /* not used because max links is 3 */ 1236ef79d439SKarsten Graul break; 1237ef79d439SKarsten Graul } 1238a6688d91SKarsten Graul kfree(qentry); 1239ef79d439SKarsten Graul } 1240ef79d439SKarsten Graul 1241a6688d91SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc) 12426c8968c4SKarsten Graul { 12436c8968c4SKarsten Graul struct smc_link_group *lgr = link->lgr; 12446c8968c4SKarsten Graul struct smc_llc_qentry *qentry; 12456c8968c4SKarsten Graul unsigned long flags; 12466c8968c4SKarsten Graul 12476c8968c4SKarsten Graul qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC); 12486c8968c4SKarsten Graul if (!qentry) 12496c8968c4SKarsten Graul return; 12506c8968c4SKarsten Graul qentry->link = link; 12516c8968c4SKarsten Graul INIT_LIST_HEAD(&qentry->list); 12526c8968c4SKarsten Graul memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg)); 1253a6688d91SKarsten Graul 1254a6688d91SKarsten Graul /* process responses immediately */ 1255a6688d91SKarsten Graul if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) { 1256a6688d91SKarsten Graul smc_llc_rx_response(link, qentry); 1257a6688d91SKarsten Graul return; 1258a6688d91SKarsten Graul } 1259a6688d91SKarsten Graul 1260a6688d91SKarsten Graul /* add requests to event queue */ 12616c8968c4SKarsten Graul spin_lock_irqsave(&lgr->llc_event_q_lock, flags); 12626c8968c4SKarsten Graul list_add_tail(&qentry->list, &lgr->llc_event_q); 12636c8968c4SKarsten Graul spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags); 12646c8968c4SKarsten Graul schedule_work(&link->lgr->llc_event_work); 12659bf9abeaSUrsula Braun } 12669bf9abeaSUrsula Braun 1267a6688d91SKarsten Graul /* copy received msg and add it to the event queue */ 1268a6688d91SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf) 1269a6688d91SKarsten Graul { 1270a6688d91SKarsten Graul struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 1271a6688d91SKarsten Graul union smc_llc_msg *llc = buf; 1272a6688d91SKarsten Graul 1273a6688d91SKarsten Graul if (wc->byte_len < sizeof(*llc)) 1274a6688d91SKarsten Graul return; /* short message */ 1275a6688d91SKarsten Graul if (llc->raw.hdr.length != sizeof(*llc)) 1276a6688d91SKarsten Graul return; /* invalid message */ 1277a6688d91SKarsten Graul 1278a6688d91SKarsten Graul smc_llc_enqueue(link, llc); 1279a6688d91SKarsten Graul } 1280a6688d91SKarsten Graul 128144aa81ceSKarsten Graul /***************************** worker, utils *********************************/ 1282877ae5beSKarsten Graul 1283877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work) 1284877ae5beSKarsten Graul { 1285877ae5beSKarsten Graul struct smc_link *link = container_of(to_delayed_work(work), 1286877ae5beSKarsten Graul struct smc_link, llc_testlink_wrk); 1287877ae5beSKarsten Graul unsigned long next_interval; 1288877ae5beSKarsten Graul unsigned long expire_time; 1289877ae5beSKarsten Graul u8 user_data[16] = { 0 }; 1290877ae5beSKarsten Graul int rc; 1291877ae5beSKarsten Graul 1292877ae5beSKarsten Graul if (link->state != SMC_LNK_ACTIVE) 1293877ae5beSKarsten Graul return; /* don't reschedule worker */ 1294877ae5beSKarsten Graul expire_time = link->wr_rx_tstamp + link->llc_testlink_time; 1295877ae5beSKarsten Graul if (time_is_after_jiffies(expire_time)) { 1296877ae5beSKarsten Graul next_interval = expire_time - jiffies; 1297877ae5beSKarsten Graul goto out; 1298877ae5beSKarsten Graul } 1299877ae5beSKarsten Graul reinit_completion(&link->llc_testlink_resp); 1300d97935faSKarsten Graul smc_llc_send_test_link(link, user_data); 1301877ae5beSKarsten Graul /* receive TEST LINK response over RoCE fabric */ 1302877ae5beSKarsten Graul rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp, 1303877ae5beSKarsten Graul SMC_LLC_WAIT_TIME); 13041020e1efSKarsten Graul if (link->state != SMC_LNK_ACTIVE) 13051020e1efSKarsten Graul return; /* link state changed */ 1306877ae5beSKarsten Graul if (rc <= 0) { 130787523930SKarsten Graul smcr_link_down_cond_sched(link); 1308877ae5beSKarsten Graul return; 1309877ae5beSKarsten Graul } 1310877ae5beSKarsten Graul next_interval = link->llc_testlink_time; 1311877ae5beSKarsten Graul out: 13121020e1efSKarsten Graul schedule_delayed_work(&link->llc_testlink_wrk, next_interval); 1313877ae5beSKarsten Graul } 1314877ae5beSKarsten Graul 131500a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc) 131600a049cfSKarsten Graul { 131700a049cfSKarsten Graul struct net *net = sock_net(smc->clcsock->sk); 131800a049cfSKarsten Graul 131900a049cfSKarsten Graul INIT_WORK(&lgr->llc_event_work, smc_llc_event_work); 1320b45e7f98SKarsten Graul INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work); 132100a049cfSKarsten Graul INIT_LIST_HEAD(&lgr->llc_event_q); 132200a049cfSKarsten Graul spin_lock_init(&lgr->llc_event_q_lock); 1323555da9afSKarsten Graul spin_lock_init(&lgr->llc_flow_lock); 1324555da9afSKarsten Graul init_waitqueue_head(&lgr->llc_waiter); 1325d5500667SKarsten Graul mutex_init(&lgr->llc_conf_mutex); 132600a049cfSKarsten Graul lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time; 132700a049cfSKarsten Graul } 132800a049cfSKarsten Graul 132900a049cfSKarsten Graul /* called after lgr was removed from lgr_list */ 133000a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr) 133100a049cfSKarsten Graul { 133200a049cfSKarsten Graul smc_llc_event_flush(lgr); 1333555da9afSKarsten Graul wake_up_interruptible_all(&lgr->llc_waiter); 133400a049cfSKarsten Graul cancel_work_sync(&lgr->llc_event_work); 1335b45e7f98SKarsten Graul cancel_work_sync(&lgr->llc_add_link_work); 1336555da9afSKarsten Graul if (lgr->delayed_event) { 1337555da9afSKarsten Graul kfree(lgr->delayed_event); 1338555da9afSKarsten Graul lgr->delayed_event = NULL; 1339555da9afSKarsten Graul } 134000a049cfSKarsten Graul } 134100a049cfSKarsten Graul 13422a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link) 1343877ae5beSKarsten Graul { 1344877ae5beSKarsten Graul init_completion(&link->llc_testlink_resp); 1345877ae5beSKarsten Graul INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); 13462a4c57a9SKarsten Graul return 0; 1347b32cf4abSKarsten Graul } 1348b32cf4abSKarsten Graul 134900a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link) 1350b32cf4abSKarsten Graul { 1351877ae5beSKarsten Graul link->state = SMC_LNK_ACTIVE; 135200a049cfSKarsten Graul if (link->lgr->llc_testlink_time) { 135300a049cfSKarsten Graul link->llc_testlink_time = link->lgr->llc_testlink_time * HZ; 13541020e1efSKarsten Graul schedule_delayed_work(&link->llc_testlink_wrk, 1355877ae5beSKarsten Graul link->llc_testlink_time); 1356877ae5beSKarsten Graul } 1357877ae5beSKarsten Graul } 1358877ae5beSKarsten Graul 1359877ae5beSKarsten Graul /* called in worker context */ 13602a4c57a9SKarsten Graul void smc_llc_link_clear(struct smc_link *link) 1361877ae5beSKarsten Graul { 13622140ac26SKarsten Graul complete(&link->llc_testlink_resp); 13632140ac26SKarsten Graul cancel_delayed_work_sync(&link->llc_testlink_wrk); 13642140ac26SKarsten Graul smc_wr_wakeup_reg_wait(link); 13652140ac26SKarsten Graul smc_wr_wakeup_tx_wait(link); 1366877ae5beSKarsten Graul } 1367877ae5beSKarsten Graul 13683d88a21bSKarsten Graul /* register a new rtoken at the remote peer (for all links) */ 13693d88a21bSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *send_link, 137044aa81ceSKarsten Graul struct smc_buf_desc *rmb_desc) 137144aa81ceSKarsten Graul { 13723d88a21bSKarsten Graul struct smc_link_group *lgr = send_link->lgr; 13733d88a21bSKarsten Graul struct smc_llc_qentry *qentry = NULL; 13743d88a21bSKarsten Graul int rc = 0; 137544aa81ceSKarsten Graul 13763d88a21bSKarsten Graul rc = smc_llc_send_confirm_rkey(send_link, rmb_desc); 13773d88a21bSKarsten Graul if (rc) 13783d88a21bSKarsten Graul goto out; 137944aa81ceSKarsten Graul /* receive CONFIRM RKEY response from server over RoCE fabric */ 13803d88a21bSKarsten Graul qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 13813d88a21bSKarsten Graul SMC_LLC_CONFIRM_RKEY); 13823d88a21bSKarsten Graul if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 13833d88a21bSKarsten Graul rc = -EFAULT; 13843d88a21bSKarsten Graul out: 13853d88a21bSKarsten Graul if (qentry) 13863d88a21bSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 13873d88a21bSKarsten Graul return rc; 138844aa81ceSKarsten Graul } 138944aa81ceSKarsten Graul 139060e03c62SKarsten Graul /* unregister an rtoken at the remote peer */ 13916d74c3a8SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link_group *lgr, 139260e03c62SKarsten Graul struct smc_buf_desc *rmb_desc) 139360e03c62SKarsten Graul { 13946d74c3a8SKarsten Graul struct smc_llc_qentry *qentry = NULL; 13956d74c3a8SKarsten Graul struct smc_link *send_link; 13960b29ec64SUrsula Braun int rc = 0; 139760e03c62SKarsten Graul 13986d74c3a8SKarsten Graul send_link = smc_llc_usable_link(lgr); 13996d74c3a8SKarsten Graul if (!send_link) 14006d74c3a8SKarsten Graul return -ENOLINK; 14016d74c3a8SKarsten Graul 14026d74c3a8SKarsten Graul /* protected by llc_flow control */ 14036d74c3a8SKarsten Graul rc = smc_llc_send_delete_rkey(send_link, rmb_desc); 140460e03c62SKarsten Graul if (rc) 140560e03c62SKarsten Graul goto out; 140660e03c62SKarsten Graul /* receive DELETE RKEY response from server over RoCE fabric */ 14076d74c3a8SKarsten Graul qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 14086d74c3a8SKarsten Graul SMC_LLC_DELETE_RKEY); 14096d74c3a8SKarsten Graul if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 141060e03c62SKarsten Graul rc = -EFAULT; 141160e03c62SKarsten Graul out: 14126d74c3a8SKarsten Graul if (qentry) 14136d74c3a8SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 141460e03c62SKarsten Graul return rc; 141560e03c62SKarsten Graul } 141660e03c62SKarsten Graul 141792334cfcSKarsten Graul /* evaluate confirm link request or response */ 141892334cfcSKarsten Graul int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry, 141992334cfcSKarsten Graul enum smc_llc_reqresp type) 142092334cfcSKarsten Graul { 142192334cfcSKarsten Graul if (type == SMC_LLC_REQ) /* SMC server assigns link_id */ 142292334cfcSKarsten Graul qentry->link->link_id = qentry->msg.confirm_link.link_num; 142392334cfcSKarsten Graul if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC)) 142492334cfcSKarsten Graul return -ENOTSUPP; 142592334cfcSKarsten Graul return 0; 142692334cfcSKarsten Graul } 142792334cfcSKarsten Graul 14289bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/ 14299bf9abeaSUrsula Braun 14309bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = { 14319bf9abeaSUrsula Braun { 14329bf9abeaSUrsula Braun .handler = smc_llc_rx_handler, 14339bf9abeaSUrsula Braun .type = SMC_LLC_CONFIRM_LINK 14349bf9abeaSUrsula Braun }, 14359bf9abeaSUrsula Braun { 1436313164daSKarsten Graul .handler = smc_llc_rx_handler, 1437313164daSKarsten Graul .type = SMC_LLC_TEST_LINK 1438313164daSKarsten Graul }, 1439313164daSKarsten Graul { 14404ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 144152bedf37SKarsten Graul .type = SMC_LLC_ADD_LINK 144252bedf37SKarsten Graul }, 144352bedf37SKarsten Graul { 144452bedf37SKarsten Graul .handler = smc_llc_rx_handler, 144587f88cdaSKarsten Graul .type = SMC_LLC_ADD_LINK_CONT 144687f88cdaSKarsten Graul }, 144787f88cdaSKarsten Graul { 144887f88cdaSKarsten Graul .handler = smc_llc_rx_handler, 144952bedf37SKarsten Graul .type = SMC_LLC_DELETE_LINK 145052bedf37SKarsten Graul }, 145152bedf37SKarsten Graul { 145252bedf37SKarsten Graul .handler = smc_llc_rx_handler, 14534ed75de5SKarsten Graul .type = SMC_LLC_CONFIRM_RKEY 14544ed75de5SKarsten Graul }, 14554ed75de5SKarsten Graul { 14564ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 14574ed75de5SKarsten Graul .type = SMC_LLC_CONFIRM_RKEY_CONT 14584ed75de5SKarsten Graul }, 14594ed75de5SKarsten Graul { 14604ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 14614ed75de5SKarsten Graul .type = SMC_LLC_DELETE_RKEY 14624ed75de5SKarsten Graul }, 14634ed75de5SKarsten Graul { 14649bf9abeaSUrsula Braun .handler = NULL, 14659bf9abeaSUrsula Braun } 14669bf9abeaSUrsula Braun }; 14679bf9abeaSUrsula Braun 14689bf9abeaSUrsula Braun int __init smc_llc_init(void) 14699bf9abeaSUrsula Braun { 14709bf9abeaSUrsula Braun struct smc_wr_rx_handler *handler; 14719bf9abeaSUrsula Braun int rc = 0; 14729bf9abeaSUrsula Braun 14739bf9abeaSUrsula Braun for (handler = smc_llc_rx_handlers; handler->handler; handler++) { 14749bf9abeaSUrsula Braun INIT_HLIST_NODE(&handler->list); 14759bf9abeaSUrsula Braun rc = smc_wr_rx_register_handler(handler); 14769bf9abeaSUrsula Braun if (rc) 14779bf9abeaSUrsula Braun break; 14789bf9abeaSUrsula Braun } 14799bf9abeaSUrsula Braun return rc; 14809bf9abeaSUrsula Braun } 1481