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