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; 26b4ba4652SKarsten Graul union { 27b4ba4652SKarsten Graul struct { 280f627126SStefan Raspl u8 length; /* 44 */ 2952bedf37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD) 3052bedf37SKarsten Graul u8 reserved:4, 3152bedf37SKarsten Graul add_link_rej_rsn:4; 3252bedf37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD) 3352bedf37SKarsten Graul u8 add_link_rej_rsn:4, 3452bedf37SKarsten Graul reserved:4; 3552bedf37SKarsten Graul #endif 360f627126SStefan Raspl }; 37b4ba4652SKarsten Graul u16 length_v2; /* 44 - 8192*/ 38b4ba4652SKarsten Graul }; 39b4ba4652SKarsten Graul u8 flags; 40b4ba4652SKarsten Graul } __packed; /* format defined in 41b4ba4652SKarsten Graul * IBM Shared Memory Communications Version 2 42b4ba4652SKarsten Graul * (https://www.ibm.com/support/pages/node/6326337) 43b4ba4652SKarsten Graul */ 440f627126SStefan Raspl 4575d320d6SKarsten Graul #define SMC_LLC_FLAG_NO_RMBE_EYEC 0x03 4675d320d6SKarsten Graul 470f627126SStefan Raspl struct smc_llc_msg_confirm_link { /* type 0x01 */ 480f627126SStefan Raspl struct smc_llc_hdr hd; 490f627126SStefan Raspl u8 sender_mac[ETH_ALEN]; 500f627126SStefan Raspl u8 sender_gid[SMC_GID_SIZE]; 510f627126SStefan Raspl u8 sender_qp_num[3]; 520f627126SStefan Raspl u8 link_num; 530f627126SStefan Raspl u8 link_uid[SMC_LGR_ID_SIZE]; 540f627126SStefan Raspl u8 max_links; 550f627126SStefan Raspl u8 reserved[9]; 560f627126SStefan Raspl }; 570f627126SStefan Raspl 5852bedf37SKarsten Graul #define SMC_LLC_FLAG_ADD_LNK_REJ 0x40 5952bedf37SKarsten Graul #define SMC_LLC_REJ_RSN_NO_ALT_PATH 1 6052bedf37SKarsten Graul 6152bedf37SKarsten Graul #define SMC_LLC_ADD_LNK_MAX_LINKS 2 6252bedf37SKarsten Graul 6352bedf37SKarsten Graul struct smc_llc_msg_add_link { /* type 0x02 */ 6452bedf37SKarsten Graul struct smc_llc_hdr hd; 6552bedf37SKarsten Graul u8 sender_mac[ETH_ALEN]; 6652bedf37SKarsten Graul u8 reserved2[2]; 6752bedf37SKarsten Graul u8 sender_gid[SMC_GID_SIZE]; 6852bedf37SKarsten Graul u8 sender_qp_num[3]; 6952bedf37SKarsten Graul u8 link_num; 70fbed3b37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD) 71fbed3b37SKarsten Graul u8 reserved3 : 4, 72fbed3b37SKarsten Graul qp_mtu : 4; 73fbed3b37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD) 74fbed3b37SKarsten Graul u8 qp_mtu : 4, 75fbed3b37SKarsten Graul reserved3 : 4; 76fbed3b37SKarsten Graul #endif 7752bedf37SKarsten Graul u8 initial_psn[3]; 7852bedf37SKarsten Graul u8 reserved[8]; 7952bedf37SKarsten Graul }; 8052bedf37SKarsten Graul 8187f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont_rt { 8287f88cdaSKarsten Graul __be32 rmb_key; 8387f88cdaSKarsten Graul __be32 rmb_key_new; 8487f88cdaSKarsten Graul __be64 rmb_vaddr_new; 8587f88cdaSKarsten Graul }; 8687f88cdaSKarsten Graul 87b4ba4652SKarsten Graul struct smc_llc_msg_add_link_v2_ext { 88b4ba4652SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD) 89b4ba4652SKarsten Graul u8 v2_direct : 1, 90b4ba4652SKarsten Graul reserved : 7; 91b4ba4652SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD) 92b4ba4652SKarsten Graul u8 reserved : 7, 93b4ba4652SKarsten Graul v2_direct : 1; 94b4ba4652SKarsten Graul #endif 95b4ba4652SKarsten Graul u8 reserved2; 96b4ba4652SKarsten Graul u8 client_target_gid[SMC_GID_SIZE]; 97b4ba4652SKarsten Graul u8 reserved3[8]; 98b4ba4652SKarsten Graul u16 num_rkeys; 99b4ba4652SKarsten Graul struct smc_llc_msg_add_link_cont_rt rt[]; 100b4ba4652SKarsten Graul } __packed; /* format defined in 101b4ba4652SKarsten Graul * IBM Shared Memory Communications Version 2 102b4ba4652SKarsten Graul * (https://www.ibm.com/support/pages/node/6326337) 103b4ba4652SKarsten Graul */ 104b4ba4652SKarsten Graul 105b4ba4652SKarsten Graul struct smc_llc_msg_req_add_link_v2 { 106b4ba4652SKarsten Graul struct smc_llc_hdr hd; 107b4ba4652SKarsten Graul u8 reserved[20]; 108b4ba4652SKarsten Graul u8 gid_cnt; 109b4ba4652SKarsten Graul u8 reserved2[3]; 110b4ba4652SKarsten Graul u8 gid[][SMC_GID_SIZE]; 111b4ba4652SKarsten Graul }; 112b4ba4652SKarsten Graul 11387f88cdaSKarsten Graul #define SMC_LLC_RKEYS_PER_CONT_MSG 2 11487f88cdaSKarsten Graul 11587f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont { /* type 0x03 */ 11687f88cdaSKarsten Graul struct smc_llc_hdr hd; 11787f88cdaSKarsten Graul u8 link_num; 11887f88cdaSKarsten Graul u8 num_rkeys; 11987f88cdaSKarsten Graul u8 reserved2[2]; 12087f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG]; 12187f88cdaSKarsten Graul u8 reserved[4]; 12287f88cdaSKarsten Graul } __packed; /* format defined in RFC7609 */ 12387f88cdaSKarsten Graul 12452bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ALL 0x40 12552bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ORDERLY 0x20 12652bedf37SKarsten Graul 12752bedf37SKarsten Graul struct smc_llc_msg_del_link { /* type 0x04 */ 12852bedf37SKarsten Graul struct smc_llc_hdr hd; 12952bedf37SKarsten Graul u8 link_num; 13052bedf37SKarsten Graul __be32 reason; 13152bedf37SKarsten Graul u8 reserved[35]; 13252bedf37SKarsten Graul } __packed; /* format defined in RFC7609 */ 13352bedf37SKarsten Graul 134313164daSKarsten Graul struct smc_llc_msg_test_link { /* type 0x07 */ 135313164daSKarsten Graul struct smc_llc_hdr hd; 136313164daSKarsten Graul u8 user_data[16]; 137313164daSKarsten Graul u8 reserved[24]; 138313164daSKarsten Graul }; 139313164daSKarsten Graul 1404ed75de5SKarsten Graul struct smc_rmb_rtoken { 1414ed75de5SKarsten Graul union { 1424ed75de5SKarsten Graul u8 num_rkeys; /* first rtoken byte of CONFIRM LINK msg */ 1434ed75de5SKarsten Graul /* is actually the num of rtokens, first */ 1444ed75de5SKarsten Graul /* rtoken is always for the current link */ 1454ed75de5SKarsten Graul u8 link_id; /* link id of the rtoken */ 1464ed75de5SKarsten Graul }; 1474ed75de5SKarsten Graul __be32 rmb_key; 1484ed75de5SKarsten Graul __be64 rmb_vaddr; 1494ed75de5SKarsten Graul } __packed; /* format defined in RFC7609 */ 1504ed75de5SKarsten Graul 1514ed75de5SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG 3 152b4ba4652SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG_V2 255 1534ed75de5SKarsten Graul 1544ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey { /* type 0x06 */ 1554ed75de5SKarsten Graul struct smc_llc_hdr hd; 1564ed75de5SKarsten Graul struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; 1574ed75de5SKarsten Graul u8 reserved; 1584ed75de5SKarsten Graul }; 1594ed75de5SKarsten Graul 1604ed75de5SKarsten Graul #define SMC_LLC_DEL_RKEY_MAX 8 1613bc67e09SKarsten Graul #define SMC_LLC_FLAG_RKEY_RETRY 0x10 1624ed75de5SKarsten Graul #define SMC_LLC_FLAG_RKEY_NEG 0x20 1634ed75de5SKarsten Graul 1644ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey { /* type 0x09 */ 1654ed75de5SKarsten Graul struct smc_llc_hdr hd; 1664ed75de5SKarsten Graul u8 num_rkeys; 1674ed75de5SKarsten Graul u8 err_mask; 1684ed75de5SKarsten Graul u8 reserved[2]; 1694ed75de5SKarsten Graul __be32 rkey[8]; 1704ed75de5SKarsten Graul u8 reserved2[4]; 1714ed75de5SKarsten Graul }; 1724ed75de5SKarsten Graul 173b4ba4652SKarsten Graul struct smc_llc_msg_delete_rkey_v2 { /* type 0x29 */ 174b4ba4652SKarsten Graul struct smc_llc_hdr hd; 175b4ba4652SKarsten Graul u8 num_rkeys; 176b4ba4652SKarsten Graul u8 num_inval_rkeys; 177b4ba4652SKarsten Graul u8 reserved[2]; 178b4ba4652SKarsten Graul __be32 rkey[]; 179b4ba4652SKarsten Graul }; 180b4ba4652SKarsten Graul 1810f627126SStefan Raspl union smc_llc_msg { 1820f627126SStefan Raspl struct smc_llc_msg_confirm_link confirm_link; 18352bedf37SKarsten Graul struct smc_llc_msg_add_link add_link; 184b4ba4652SKarsten Graul struct smc_llc_msg_req_add_link_v2 req_add_link; 18587f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont add_link_cont; 18652bedf37SKarsten Graul struct smc_llc_msg_del_link delete_link; 1874ed75de5SKarsten Graul 1884ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey confirm_rkey; 1894ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey delete_rkey; 1904ed75de5SKarsten Graul 191313164daSKarsten Graul struct smc_llc_msg_test_link test_link; 1920f627126SStefan Raspl struct { 1930f627126SStefan Raspl struct smc_llc_hdr hdr; 1940f627126SStefan Raspl u8 data[SMC_LLC_DATA_LEN]; 1950f627126SStefan Raspl } raw; 1960f627126SStefan Raspl }; 1970f627126SStefan Raspl 1980f627126SStefan Raspl #define SMC_LLC_FLAG_RESP 0x80 1990f627126SStefan Raspl 2006c8968c4SKarsten Graul struct smc_llc_qentry { 2016c8968c4SKarsten Graul struct list_head list; 2026c8968c4SKarsten Graul struct smc_link *link; 2036c8968c4SKarsten Graul union smc_llc_msg msg; 2046c8968c4SKarsten Graul }; 2056c8968c4SKarsten Graul 2064dadd151SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc); 2074dadd151SKarsten Graul 208555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow) 209555da9afSKarsten Graul { 210555da9afSKarsten Graul struct smc_llc_qentry *qentry = flow->qentry; 211555da9afSKarsten Graul 212555da9afSKarsten Graul flow->qentry = NULL; 213555da9afSKarsten Graul return qentry; 214555da9afSKarsten Graul } 215555da9afSKarsten Graul 216555da9afSKarsten Graul void smc_llc_flow_qentry_del(struct smc_llc_flow *flow) 217555da9afSKarsten Graul { 218555da9afSKarsten Graul struct smc_llc_qentry *qentry; 219555da9afSKarsten Graul 220555da9afSKarsten Graul if (flow->qentry) { 221555da9afSKarsten Graul qentry = flow->qentry; 222555da9afSKarsten Graul flow->qentry = NULL; 223555da9afSKarsten Graul kfree(qentry); 224555da9afSKarsten Graul } 225555da9afSKarsten Graul } 226555da9afSKarsten Graul 227555da9afSKarsten Graul static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow, 228555da9afSKarsten Graul struct smc_llc_qentry *qentry) 229555da9afSKarsten Graul { 230555da9afSKarsten Graul flow->qentry = qentry; 231555da9afSKarsten Graul } 232555da9afSKarsten Graul 2336778a6beSKarsten Graul static void smc_llc_flow_parallel(struct smc_link_group *lgr, u8 flow_type, 2346778a6beSKarsten Graul struct smc_llc_qentry *qentry) 2356778a6beSKarsten Graul { 236b4ba4652SKarsten Graul u8 msg_type = qentry->msg.raw.hdr.common.llc_type; 2376778a6beSKarsten Graul 2386778a6beSKarsten Graul if ((msg_type == SMC_LLC_ADD_LINK || msg_type == SMC_LLC_DELETE_LINK) && 2396778a6beSKarsten Graul flow_type != msg_type && !lgr->delayed_event) { 2406778a6beSKarsten Graul lgr->delayed_event = qentry; 2416778a6beSKarsten Graul return; 2426778a6beSKarsten Graul } 2436778a6beSKarsten Graul /* drop parallel or already-in-progress llc requests */ 2446778a6beSKarsten Graul if (flow_type != msg_type) 245de2fea7bSTony Lu pr_warn_once("smc: SMC-R lg %*phN net %llu dropped parallel " 2466778a6beSKarsten Graul "LLC msg: msg %d flow %d role %d\n", 2476778a6beSKarsten Graul SMC_LGR_ID_SIZE, &lgr->id, 248de2fea7bSTony Lu lgr->net->net_cookie, 2496778a6beSKarsten Graul qentry->msg.raw.hdr.common.type, 2506778a6beSKarsten Graul flow_type, lgr->role); 2516778a6beSKarsten Graul kfree(qentry); 2526778a6beSKarsten Graul } 2536778a6beSKarsten Graul 254555da9afSKarsten Graul /* try to start a new llc flow, initiated by an incoming llc msg */ 255555da9afSKarsten Graul static bool smc_llc_flow_start(struct smc_llc_flow *flow, 256555da9afSKarsten Graul struct smc_llc_qentry *qentry) 257555da9afSKarsten Graul { 258555da9afSKarsten Graul struct smc_link_group *lgr = qentry->link->lgr; 259555da9afSKarsten Graul 260555da9afSKarsten Graul spin_lock_bh(&lgr->llc_flow_lock); 261555da9afSKarsten Graul if (flow->type) { 262555da9afSKarsten Graul /* a flow is already active */ 2636778a6beSKarsten Graul smc_llc_flow_parallel(lgr, flow->type, qentry); 264555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 265555da9afSKarsten Graul return false; 266555da9afSKarsten Graul } 267b4ba4652SKarsten Graul switch (qentry->msg.raw.hdr.common.llc_type) { 268555da9afSKarsten Graul case SMC_LLC_ADD_LINK: 269555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_ADD_LINK; 270555da9afSKarsten Graul break; 271555da9afSKarsten Graul case SMC_LLC_DELETE_LINK: 272555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_DEL_LINK; 273555da9afSKarsten Graul break; 274555da9afSKarsten Graul case SMC_LLC_CONFIRM_RKEY: 275555da9afSKarsten Graul case SMC_LLC_DELETE_RKEY: 276555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_RKEY; 277555da9afSKarsten Graul break; 278555da9afSKarsten Graul default: 279555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_NONE; 280555da9afSKarsten Graul } 281555da9afSKarsten Graul smc_llc_flow_qentry_set(flow, qentry); 2826778a6beSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 283555da9afSKarsten Graul return true; 284555da9afSKarsten Graul } 285555da9afSKarsten Graul 286555da9afSKarsten Graul /* start a new local llc flow, wait till current flow finished */ 287555da9afSKarsten Graul int smc_llc_flow_initiate(struct smc_link_group *lgr, 288555da9afSKarsten Graul enum smc_llc_flowtype type) 289555da9afSKarsten Graul { 290555da9afSKarsten Graul enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE; 291555da9afSKarsten Graul int rc; 292555da9afSKarsten Graul 293555da9afSKarsten Graul /* all flows except confirm_rkey and delete_rkey are exclusive, 294555da9afSKarsten Graul * confirm/delete rkey flows can run concurrently (local and remote) 295555da9afSKarsten Graul */ 296555da9afSKarsten Graul if (type == SMC_LLC_FLOW_RKEY) 297555da9afSKarsten Graul allowed_remote = SMC_LLC_FLOW_RKEY; 298555da9afSKarsten Graul again: 299555da9afSKarsten Graul if (list_empty(&lgr->list)) 300555da9afSKarsten Graul return -ENODEV; 301555da9afSKarsten Graul spin_lock_bh(&lgr->llc_flow_lock); 302555da9afSKarsten Graul if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 303555da9afSKarsten Graul (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 304555da9afSKarsten Graul lgr->llc_flow_rmt.type == allowed_remote)) { 305555da9afSKarsten Graul lgr->llc_flow_lcl.type = type; 306555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 307555da9afSKarsten Graul return 0; 308555da9afSKarsten Graul } 309555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 3106778a6beSKarsten Graul rc = wait_event_timeout(lgr->llc_flow_waiter, (list_empty(&lgr->list) || 311555da9afSKarsten Graul (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 312555da9afSKarsten Graul (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 3136778a6beSKarsten Graul lgr->llc_flow_rmt.type == allowed_remote))), 3146778a6beSKarsten Graul SMC_LLC_WAIT_TIME * 10); 315555da9afSKarsten Graul if (!rc) 316555da9afSKarsten Graul return -ETIMEDOUT; 317555da9afSKarsten Graul goto again; 318555da9afSKarsten Graul } 319555da9afSKarsten Graul 320555da9afSKarsten Graul /* finish the current llc flow */ 321555da9afSKarsten Graul void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow) 322555da9afSKarsten Graul { 323555da9afSKarsten Graul spin_lock_bh(&lgr->llc_flow_lock); 324555da9afSKarsten Graul memset(flow, 0, sizeof(*flow)); 325555da9afSKarsten Graul flow->type = SMC_LLC_FLOW_NONE; 326555da9afSKarsten Graul spin_unlock_bh(&lgr->llc_flow_lock); 327555da9afSKarsten Graul if (!list_empty(&lgr->list) && lgr->delayed_event && 328555da9afSKarsten Graul flow == &lgr->llc_flow_lcl) 329555da9afSKarsten Graul schedule_work(&lgr->llc_event_work); 330555da9afSKarsten Graul else 3316778a6beSKarsten Graul wake_up(&lgr->llc_flow_waiter); 332555da9afSKarsten Graul } 333555da9afSKarsten Graul 334555da9afSKarsten Graul /* lnk is optional and used for early wakeup when link goes down, useful in 335555da9afSKarsten Graul * cases where we wait for a response on the link after we sent a request 336555da9afSKarsten Graul */ 337555da9afSKarsten Graul struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr, 338555da9afSKarsten Graul struct smc_link *lnk, 339555da9afSKarsten Graul int time_out, u8 exp_msg) 340555da9afSKarsten Graul { 341555da9afSKarsten Graul struct smc_llc_flow *flow = &lgr->llc_flow_lcl; 3426778a6beSKarsten Graul u8 rcv_msg; 343555da9afSKarsten Graul 3446778a6beSKarsten Graul wait_event_timeout(lgr->llc_msg_waiter, 345555da9afSKarsten Graul (flow->qentry || 346555da9afSKarsten Graul (lnk && !smc_link_usable(lnk)) || 347555da9afSKarsten Graul list_empty(&lgr->list)), 348555da9afSKarsten Graul time_out); 349555da9afSKarsten Graul if (!flow->qentry || 350555da9afSKarsten Graul (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) { 351555da9afSKarsten Graul smc_llc_flow_qentry_del(flow); 352555da9afSKarsten Graul goto out; 353555da9afSKarsten Graul } 354b4ba4652SKarsten Graul rcv_msg = flow->qentry->msg.raw.hdr.common.llc_type; 3556778a6beSKarsten Graul if (exp_msg && rcv_msg != exp_msg) { 356555da9afSKarsten Graul if (exp_msg == SMC_LLC_ADD_LINK && 3576778a6beSKarsten Graul rcv_msg == SMC_LLC_DELETE_LINK) { 358555da9afSKarsten Graul /* flow_start will delay the unexpected msg */ 359555da9afSKarsten Graul smc_llc_flow_start(&lgr->llc_flow_lcl, 360555da9afSKarsten Graul smc_llc_flow_qentry_clr(flow)); 361555da9afSKarsten Graul return NULL; 362555da9afSKarsten Graul } 363de2fea7bSTony Lu pr_warn_once("smc: SMC-R lg %*phN net %llu dropped unexpected LLC msg: " 3646778a6beSKarsten Graul "msg %d exp %d flow %d role %d flags %x\n", 365de2fea7bSTony Lu SMC_LGR_ID_SIZE, &lgr->id, lgr->net->net_cookie, 366de2fea7bSTony Lu rcv_msg, exp_msg, 3676778a6beSKarsten Graul flow->type, lgr->role, 3686778a6beSKarsten Graul flow->qentry->msg.raw.hdr.flags); 369555da9afSKarsten Graul smc_llc_flow_qentry_del(flow); 370555da9afSKarsten Graul } 371555da9afSKarsten Graul out: 372555da9afSKarsten Graul return flow->qentry; 373555da9afSKarsten Graul } 374555da9afSKarsten Graul 3759bf9abeaSUrsula Braun /********************************** send *************************************/ 3769bf9abeaSUrsula Braun 3779bf9abeaSUrsula Braun struct smc_llc_tx_pend { 3789bf9abeaSUrsula Braun }; 3799bf9abeaSUrsula Braun 3809bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */ 3819bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend, 3829bf9abeaSUrsula Braun struct smc_link *link, 3839bf9abeaSUrsula Braun enum ib_wc_status wc_status) 3849bf9abeaSUrsula Braun { 3859bf9abeaSUrsula Braun /* future work: handle wc_status error for recovery and failover */ 3869bf9abeaSUrsula Braun } 3879bf9abeaSUrsula Braun 3889bf9abeaSUrsula Braun /** 3899bf9abeaSUrsula Braun * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits 3909bf9abeaSUrsula Braun * @link: Pointer to SMC link used for sending LLC control message. 3919bf9abeaSUrsula Braun * @wr_buf: Out variable returning pointer to work request payload buffer. 3929bf9abeaSUrsula Braun * @pend: Out variable returning pointer to private pending WR tracking. 3939bf9abeaSUrsula Braun * It's the context the transmit complete handler will get. 3949bf9abeaSUrsula Braun * 3959bf9abeaSUrsula Braun * Reserves and pre-fills an entry for a pending work request send/tx. 3969bf9abeaSUrsula Braun * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx. 3979bf9abeaSUrsula Braun * Can sleep due to smc_get_ctrl_buf (if not in softirq context). 3989bf9abeaSUrsula Braun * 3999bf9abeaSUrsula Braun * Return: 0 on success, otherwise an error value. 4009bf9abeaSUrsula Braun */ 4019bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link, 4029bf9abeaSUrsula Braun struct smc_wr_buf **wr_buf, 4039bf9abeaSUrsula Braun struct smc_wr_tx_pend_priv **pend) 4049bf9abeaSUrsula Braun { 4059bf9abeaSUrsula Braun int rc; 4069bf9abeaSUrsula Braun 407ad6f317fSUrsula Braun rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL, 408ad6f317fSUrsula Braun pend); 4099bf9abeaSUrsula Braun if (rc < 0) 4109bf9abeaSUrsula Braun return rc; 4119bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 4129bf9abeaSUrsula Braun sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE, 4139bf9abeaSUrsula Braun "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)"); 4149bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 4159bf9abeaSUrsula Braun sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE, 4169bf9abeaSUrsula 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()"); 4179bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 4189bf9abeaSUrsula Braun sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, 4199bf9abeaSUrsula Braun "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)"); 4209bf9abeaSUrsula Braun return 0; 4219bf9abeaSUrsula Braun } 4229bf9abeaSUrsula Braun 423b4ba4652SKarsten Graul static int smc_llc_add_pending_send_v2(struct smc_link *link, 424b4ba4652SKarsten Graul struct smc_wr_v2_buf **wr_buf, 425b4ba4652SKarsten Graul struct smc_wr_tx_pend_priv **pend) 426b4ba4652SKarsten Graul { 427b4ba4652SKarsten Graul int rc; 428b4ba4652SKarsten Graul 429b4ba4652SKarsten Graul rc = smc_wr_tx_get_v2_slot(link, smc_llc_tx_handler, wr_buf, pend); 430b4ba4652SKarsten Graul if (rc < 0) 431b4ba4652SKarsten Graul return rc; 432b4ba4652SKarsten Graul return 0; 433b4ba4652SKarsten Graul } 434b4ba4652SKarsten Graul 435b4ba4652SKarsten Graul static void smc_llc_init_msg_hdr(struct smc_llc_hdr *hdr, 436b4ba4652SKarsten Graul struct smc_link_group *lgr, size_t len) 437b4ba4652SKarsten Graul { 438b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2) { 439b4ba4652SKarsten Graul hdr->common.llc_version = SMC_V2; 440b4ba4652SKarsten Graul hdr->length_v2 = len; 441b4ba4652SKarsten Graul } else { 442b4ba4652SKarsten Graul hdr->common.llc_version = 0; 443b4ba4652SKarsten Graul hdr->length = len; 444b4ba4652SKarsten Graul } 445b4ba4652SKarsten Graul } 446b4ba4652SKarsten Graul 4479bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */ 448947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link, 4499bf9abeaSUrsula Braun enum smc_llc_reqresp reqresp) 4509bf9abeaSUrsula Braun { 4519bf9abeaSUrsula Braun struct smc_llc_msg_confirm_link *confllc; 4529bf9abeaSUrsula Braun struct smc_wr_tx_pend_priv *pend; 4539bf9abeaSUrsula Braun struct smc_wr_buf *wr_buf; 4549bf9abeaSUrsula Braun int rc; 4559bf9abeaSUrsula Braun 45695f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 45795f7f3e7SKarsten Graul return -ENOLINK; 4589bf9abeaSUrsula Braun rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 4599bf9abeaSUrsula Braun if (rc) 46095f7f3e7SKarsten Graul goto put_out; 4619bf9abeaSUrsula Braun confllc = (struct smc_llc_msg_confirm_link *)wr_buf; 4629bf9abeaSUrsula Braun memset(confllc, 0, sizeof(*confllc)); 463b4ba4652SKarsten Graul confllc->hd.common.llc_type = SMC_LLC_CONFIRM_LINK; 464b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&confllc->hd, link->lgr, sizeof(*confllc)); 46575d320d6SKarsten Graul confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC; 4669bf9abeaSUrsula Braun if (reqresp == SMC_LLC_RESP) 4679bf9abeaSUrsula Braun confllc->hd.flags |= SMC_LLC_FLAG_RESP; 468947541f3SUrsula Braun memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1], 469947541f3SUrsula Braun ETH_ALEN); 4707005ada6SUrsula Braun memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE); 4719bf9abeaSUrsula Braun hton24(confllc->sender_qp_num, link->roce_qp->qp_num); 4722be922f3SKarsten Graul confllc->link_num = link->link_id; 47345fa8da0SKarsten Graul memcpy(confllc->link_uid, link->link_uid, SMC_LGR_ID_SIZE); 474b1570a87SKarsten Graul confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; 47552bedf37SKarsten Graul /* send llc message */ 47652bedf37SKarsten Graul rc = smc_wr_tx_send(link, pend); 47795f7f3e7SKarsten Graul put_out: 47895f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 47952bedf37SKarsten Graul return rc; 48052bedf37SKarsten Graul } 48152bedf37SKarsten Graul 48244aa81ceSKarsten Graul /* send LLC confirm rkey request */ 4833d88a21bSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *send_link, 48444aa81ceSKarsten Graul struct smc_buf_desc *rmb_desc) 48544aa81ceSKarsten Graul { 48644aa81ceSKarsten Graul struct smc_llc_msg_confirm_rkey *rkeyllc; 48744aa81ceSKarsten Graul struct smc_wr_tx_pend_priv *pend; 48844aa81ceSKarsten Graul struct smc_wr_buf *wr_buf; 4893d88a21bSKarsten Graul struct smc_link *link; 4903d88a21bSKarsten Graul int i, rc, rtok_ix; 49144aa81ceSKarsten Graul 49295f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(send_link)) 49395f7f3e7SKarsten Graul return -ENOLINK; 4943d88a21bSKarsten Graul rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend); 49544aa81ceSKarsten Graul if (rc) 49695f7f3e7SKarsten Graul goto put_out; 49744aa81ceSKarsten Graul rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf; 49844aa81ceSKarsten Graul memset(rkeyllc, 0, sizeof(*rkeyllc)); 499b4ba4652SKarsten Graul rkeyllc->hd.common.llc_type = SMC_LLC_CONFIRM_RKEY; 500b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&rkeyllc->hd, send_link->lgr, sizeof(*rkeyllc)); 5013d88a21bSKarsten Graul 5023d88a21bSKarsten Graul rtok_ix = 1; 5033d88a21bSKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 5043d88a21bSKarsten Graul link = &send_link->lgr->lnk[i]; 505741a49a4SKarsten Graul if (smc_link_active(link) && link != send_link) { 5063d88a21bSKarsten Graul rkeyllc->rtoken[rtok_ix].link_id = link->link_id; 5073d88a21bSKarsten Graul rkeyllc->rtoken[rtok_ix].rmb_key = 508b8d19945SWen Gu htonl(rmb_desc->mr[link->link_idx]->rkey); 509b8d19945SWen Gu rkeyllc->rtoken[rtok_ix].rmb_vaddr = rmb_desc->is_vm ? 510b8d19945SWen Gu cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) : 511b8d19945SWen Gu cpu_to_be64((u64)sg_dma_address 512b8d19945SWen Gu (rmb_desc->sgt[link->link_idx].sgl)); 5133d88a21bSKarsten Graul rtok_ix++; 5143d88a21bSKarsten Graul } 5153d88a21bSKarsten Graul } 5163d88a21bSKarsten Graul /* rkey of send_link is in rtoken[0] */ 5173d88a21bSKarsten Graul rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1; 5183d88a21bSKarsten Graul rkeyllc->rtoken[0].rmb_key = 519b8d19945SWen Gu htonl(rmb_desc->mr[send_link->link_idx]->rkey); 520b8d19945SWen Gu rkeyllc->rtoken[0].rmb_vaddr = rmb_desc->is_vm ? 521b8d19945SWen Gu cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) : 522b8d19945SWen Gu cpu_to_be64((u64)sg_dma_address 523b8d19945SWen Gu (rmb_desc->sgt[send_link->link_idx].sgl)); 52444aa81ceSKarsten Graul /* send llc message */ 5253d88a21bSKarsten Graul rc = smc_wr_tx_send(send_link, pend); 52695f7f3e7SKarsten Graul put_out: 52795f7f3e7SKarsten Graul smc_wr_tx_link_put(send_link); 52844aa81ceSKarsten Graul return rc; 52944aa81ceSKarsten Graul } 53044aa81ceSKarsten Graul 53160e03c62SKarsten Graul /* send LLC delete rkey request */ 53260e03c62SKarsten Graul static int smc_llc_send_delete_rkey(struct smc_link *link, 53360e03c62SKarsten Graul struct smc_buf_desc *rmb_desc) 53460e03c62SKarsten Graul { 53560e03c62SKarsten Graul struct smc_llc_msg_delete_rkey *rkeyllc; 53660e03c62SKarsten Graul struct smc_wr_tx_pend_priv *pend; 53760e03c62SKarsten Graul struct smc_wr_buf *wr_buf; 53860e03c62SKarsten Graul int rc; 53960e03c62SKarsten Graul 54095f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 54195f7f3e7SKarsten Graul return -ENOLINK; 54260e03c62SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 54360e03c62SKarsten Graul if (rc) 54495f7f3e7SKarsten Graul goto put_out; 54560e03c62SKarsten Graul rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf; 54660e03c62SKarsten Graul memset(rkeyllc, 0, sizeof(*rkeyllc)); 547b4ba4652SKarsten Graul rkeyllc->hd.common.llc_type = SMC_LLC_DELETE_RKEY; 548b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&rkeyllc->hd, link->lgr, sizeof(*rkeyllc)); 54960e03c62SKarsten Graul rkeyllc->num_rkeys = 1; 550b8d19945SWen Gu rkeyllc->rkey[0] = htonl(rmb_desc->mr[link->link_idx]->rkey); 55160e03c62SKarsten Graul /* send llc message */ 55260e03c62SKarsten Graul rc = smc_wr_tx_send(link, pend); 55395f7f3e7SKarsten Graul put_out: 55495f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 55560e03c62SKarsten Graul return rc; 55660e03c62SKarsten Graul } 55760e03c62SKarsten Graul 558b4ba4652SKarsten Graul /* return first buffer from any of the next buf lists */ 559b4ba4652SKarsten Graul static struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr, 560b4ba4652SKarsten Graul int *buf_lst) 561b4ba4652SKarsten Graul { 562b4ba4652SKarsten Graul struct smc_buf_desc *buf_pos; 563b4ba4652SKarsten Graul 564b4ba4652SKarsten Graul while (*buf_lst < SMC_RMBE_SIZES) { 565b4ba4652SKarsten Graul buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst], 566b4ba4652SKarsten Graul struct smc_buf_desc, list); 567b4ba4652SKarsten Graul if (buf_pos) 568b4ba4652SKarsten Graul return buf_pos; 569b4ba4652SKarsten Graul (*buf_lst)++; 570b4ba4652SKarsten Graul } 571b4ba4652SKarsten Graul return NULL; 572b4ba4652SKarsten Graul } 573b4ba4652SKarsten Graul 574b4ba4652SKarsten Graul /* return next rmb from buffer lists */ 575b4ba4652SKarsten Graul static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr, 576b4ba4652SKarsten Graul int *buf_lst, 577b4ba4652SKarsten Graul struct smc_buf_desc *buf_pos) 578b4ba4652SKarsten Graul { 579b4ba4652SKarsten Graul struct smc_buf_desc *buf_next; 580b4ba4652SKarsten Graul 581b4ba4652SKarsten Graul if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) { 582b4ba4652SKarsten Graul (*buf_lst)++; 583b4ba4652SKarsten Graul return _smc_llc_get_next_rmb(lgr, buf_lst); 584b4ba4652SKarsten Graul } 585b4ba4652SKarsten Graul buf_next = list_next_entry(buf_pos, list); 586b4ba4652SKarsten Graul return buf_next; 587b4ba4652SKarsten Graul } 588b4ba4652SKarsten Graul 589b4ba4652SKarsten Graul static struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr, 590b4ba4652SKarsten Graul int *buf_lst) 591b4ba4652SKarsten Graul { 592b4ba4652SKarsten Graul *buf_lst = 0; 593b4ba4652SKarsten Graul return smc_llc_get_next_rmb(lgr, buf_lst, NULL); 594b4ba4652SKarsten Graul } 595b4ba4652SKarsten Graul 596b4ba4652SKarsten Graul static int smc_llc_fill_ext_v2(struct smc_llc_msg_add_link_v2_ext *ext, 597b4ba4652SKarsten Graul struct smc_link *link, struct smc_link *link_new) 598b4ba4652SKarsten Graul { 599b4ba4652SKarsten Graul struct smc_link_group *lgr = link->lgr; 600b4ba4652SKarsten Graul struct smc_buf_desc *buf_pos; 601b4ba4652SKarsten Graul int prim_lnk_idx, lnk_idx, i; 602b4ba4652SKarsten Graul struct smc_buf_desc *rmb; 603b4ba4652SKarsten Graul int len = sizeof(*ext); 604b4ba4652SKarsten Graul int buf_lst; 605b4ba4652SKarsten Graul 606b4ba4652SKarsten Graul ext->v2_direct = !lgr->uses_gateway; 607b4ba4652SKarsten Graul memcpy(ext->client_target_gid, link_new->gid, SMC_GID_SIZE); 608b4ba4652SKarsten Graul 609b4ba4652SKarsten Graul prim_lnk_idx = link->link_idx; 610b4ba4652SKarsten Graul lnk_idx = link_new->link_idx; 611b4ba4652SKarsten Graul mutex_lock(&lgr->rmbs_lock); 612b4ba4652SKarsten Graul ext->num_rkeys = lgr->conns_num; 613b4ba4652SKarsten Graul if (!ext->num_rkeys) 614b4ba4652SKarsten Graul goto out; 615b4ba4652SKarsten Graul buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 616b4ba4652SKarsten Graul for (i = 0; i < ext->num_rkeys; i++) { 617b4ba4652SKarsten Graul if (!buf_pos) 618b4ba4652SKarsten Graul break; 619b4ba4652SKarsten Graul rmb = buf_pos; 620b8d19945SWen Gu ext->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey); 621b8d19945SWen Gu ext->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey); 622b8d19945SWen Gu ext->rt[i].rmb_vaddr_new = rmb->is_vm ? 623b8d19945SWen Gu cpu_to_be64((uintptr_t)rmb->cpu_addr) : 624b4ba4652SKarsten Graul cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); 625b4ba4652SKarsten Graul buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); 626b4ba4652SKarsten Graul while (buf_pos && !(buf_pos)->used) 627b4ba4652SKarsten Graul buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); 628b4ba4652SKarsten Graul } 629b4ba4652SKarsten Graul len += i * sizeof(ext->rt[0]); 630b4ba4652SKarsten Graul out: 631b4ba4652SKarsten Graul mutex_unlock(&lgr->rmbs_lock); 632b4ba4652SKarsten Graul return len; 633b4ba4652SKarsten Graul } 634b4ba4652SKarsten Graul 63552bedf37SKarsten Graul /* send ADD LINK request or response */ 6367005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[], 637fbed3b37SKarsten Graul struct smc_link *link_new, 63852bedf37SKarsten Graul enum smc_llc_reqresp reqresp) 63952bedf37SKarsten Graul { 640b4ba4652SKarsten Graul struct smc_llc_msg_add_link_v2_ext *ext = NULL; 64152bedf37SKarsten Graul struct smc_llc_msg_add_link *addllc; 64252bedf37SKarsten Graul struct smc_wr_tx_pend_priv *pend; 643b4ba4652SKarsten Graul int len = sizeof(*addllc); 64452bedf37SKarsten Graul int rc; 64552bedf37SKarsten Graul 64695f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 64795f7f3e7SKarsten Graul return -ENOLINK; 648b4ba4652SKarsten Graul if (link->lgr->smc_version == SMC_V2) { 649b4ba4652SKarsten Graul struct smc_wr_v2_buf *wr_buf; 650b4ba4652SKarsten Graul 651b4ba4652SKarsten Graul rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend); 652b4ba4652SKarsten Graul if (rc) 653b4ba4652SKarsten Graul goto put_out; 654b4ba4652SKarsten Graul addllc = (struct smc_llc_msg_add_link *)wr_buf; 655b4ba4652SKarsten Graul ext = (struct smc_llc_msg_add_link_v2_ext *) 656b4ba4652SKarsten Graul &wr_buf->raw[sizeof(*addllc)]; 657b4ba4652SKarsten Graul memset(ext, 0, SMC_WR_TX_SIZE); 658b4ba4652SKarsten Graul } else { 659b4ba4652SKarsten Graul struct smc_wr_buf *wr_buf; 660b4ba4652SKarsten Graul 66152bedf37SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 66252bedf37SKarsten Graul if (rc) 66395f7f3e7SKarsten Graul goto put_out; 66452bedf37SKarsten Graul addllc = (struct smc_llc_msg_add_link *)wr_buf; 665b4ba4652SKarsten Graul } 666fbed3b37SKarsten Graul 667fbed3b37SKarsten Graul memset(addllc, 0, sizeof(*addllc)); 668b4ba4652SKarsten Graul addllc->hd.common.llc_type = SMC_LLC_ADD_LINK; 669fbed3b37SKarsten Graul if (reqresp == SMC_LLC_RESP) 670fbed3b37SKarsten Graul addllc->hd.flags |= SMC_LLC_FLAG_RESP; 671fbed3b37SKarsten Graul memcpy(addllc->sender_mac, mac, ETH_ALEN); 672fbed3b37SKarsten Graul memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); 673fbed3b37SKarsten Graul if (link_new) { 674fbed3b37SKarsten Graul addllc->link_num = link_new->link_id; 675fbed3b37SKarsten Graul hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num); 676fbed3b37SKarsten Graul hton24(addllc->initial_psn, link_new->psn_initial); 677fbed3b37SKarsten Graul if (reqresp == SMC_LLC_REQ) 678fbed3b37SKarsten Graul addllc->qp_mtu = link_new->path_mtu; 679fbed3b37SKarsten Graul else 680fbed3b37SKarsten Graul addllc->qp_mtu = min(link_new->path_mtu, 681fbed3b37SKarsten Graul link_new->peer_mtu); 682fbed3b37SKarsten Graul } 683b4ba4652SKarsten Graul if (ext && link_new) 684b4ba4652SKarsten Graul len += smc_llc_fill_ext_v2(ext, link, link_new); 685b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&addllc->hd, link->lgr, len); 68652bedf37SKarsten Graul /* send llc message */ 687b4ba4652SKarsten Graul if (link->lgr->smc_version == SMC_V2) 688b4ba4652SKarsten Graul rc = smc_wr_tx_v2_send(link, pend, len); 689b4ba4652SKarsten Graul else 69052bedf37SKarsten Graul rc = smc_wr_tx_send(link, pend); 69195f7f3e7SKarsten Graul put_out: 69295f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 69352bedf37SKarsten Graul return rc; 69452bedf37SKarsten Graul } 69552bedf37SKarsten Graul 69652bedf37SKarsten Graul /* send DELETE LINK request or response */ 697fbed3b37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id, 698fbed3b37SKarsten Graul enum smc_llc_reqresp reqresp, bool orderly, 699fbed3b37SKarsten Graul u32 reason) 70052bedf37SKarsten Graul { 70152bedf37SKarsten Graul struct smc_llc_msg_del_link *delllc; 70252bedf37SKarsten Graul struct smc_wr_tx_pend_priv *pend; 70352bedf37SKarsten Graul struct smc_wr_buf *wr_buf; 70452bedf37SKarsten Graul int rc; 70552bedf37SKarsten Graul 70695f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 70795f7f3e7SKarsten Graul return -ENOLINK; 70852bedf37SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 70952bedf37SKarsten Graul if (rc) 71095f7f3e7SKarsten Graul goto put_out; 71152bedf37SKarsten Graul delllc = (struct smc_llc_msg_del_link *)wr_buf; 712fbed3b37SKarsten Graul 713fbed3b37SKarsten Graul memset(delllc, 0, sizeof(*delllc)); 714b4ba4652SKarsten Graul delllc->hd.common.llc_type = SMC_LLC_DELETE_LINK; 715b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&delllc->hd, link->lgr, sizeof(*delllc)); 716fbed3b37SKarsten Graul if (reqresp == SMC_LLC_RESP) 717fbed3b37SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_RESP; 718fbed3b37SKarsten Graul if (orderly) 719fbed3b37SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 720fbed3b37SKarsten Graul if (link_del_id) 721fbed3b37SKarsten Graul delllc->link_num = link_del_id; 722fbed3b37SKarsten Graul else 723fbed3b37SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 724fbed3b37SKarsten Graul delllc->reason = htonl(reason); 7259bf9abeaSUrsula Braun /* send llc message */ 7269bf9abeaSUrsula Braun rc = smc_wr_tx_send(link, pend); 72795f7f3e7SKarsten Graul put_out: 72895f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 7299bf9abeaSUrsula Braun return rc; 7309bf9abeaSUrsula Braun } 7319bf9abeaSUrsula Braun 732d97935faSKarsten Graul /* send LLC test link request */ 733d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16]) 734313164daSKarsten Graul { 735313164daSKarsten Graul struct smc_llc_msg_test_link *testllc; 736313164daSKarsten Graul struct smc_wr_tx_pend_priv *pend; 737313164daSKarsten Graul struct smc_wr_buf *wr_buf; 738313164daSKarsten Graul int rc; 739313164daSKarsten Graul 74095f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 74195f7f3e7SKarsten Graul return -ENOLINK; 742313164daSKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 743313164daSKarsten Graul if (rc) 74495f7f3e7SKarsten Graul goto put_out; 745313164daSKarsten Graul testllc = (struct smc_llc_msg_test_link *)wr_buf; 746313164daSKarsten Graul memset(testllc, 0, sizeof(*testllc)); 747b4ba4652SKarsten Graul testllc->hd.common.llc_type = SMC_LLC_TEST_LINK; 748b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&testllc->hd, link->lgr, sizeof(*testllc)); 749313164daSKarsten Graul memcpy(testllc->user_data, user_data, sizeof(testllc->user_data)); 750313164daSKarsten Graul /* send llc message */ 751313164daSKarsten Graul rc = smc_wr_tx_send(link, pend); 75295f7f3e7SKarsten Graul put_out: 75395f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 754313164daSKarsten Graul return rc; 755313164daSKarsten Graul } 756313164daSKarsten Graul 7576c8968c4SKarsten Graul /* schedule an llc send on link, may wait for buffers */ 7586c8968c4SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf) 7594ed75de5SKarsten Graul { 7604ed75de5SKarsten Graul struct smc_wr_tx_pend_priv *pend; 7614ed75de5SKarsten Graul struct smc_wr_buf *wr_buf; 7624ed75de5SKarsten Graul int rc; 7634ed75de5SKarsten Graul 76495f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 7656c8968c4SKarsten Graul return -ENOLINK; 7666c8968c4SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 7674ed75de5SKarsten Graul if (rc) 76895f7f3e7SKarsten Graul goto put_out; 7696c8968c4SKarsten Graul memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); 77095f7f3e7SKarsten Graul rc = smc_wr_tx_send(link, pend); 77195f7f3e7SKarsten Graul put_out: 77295f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 77395f7f3e7SKarsten Graul return rc; 7744ed75de5SKarsten Graul } 7754ed75de5SKarsten Graul 776f3811fd7SKarsten Graul /* schedule an llc send on link, may wait for buffers, 777f3811fd7SKarsten Graul * and wait for send completion notification. 778f3811fd7SKarsten Graul * @return 0 on success 779f3811fd7SKarsten Graul */ 780f3811fd7SKarsten Graul static int smc_llc_send_message_wait(struct smc_link *link, void *llcbuf) 781f3811fd7SKarsten Graul { 782f3811fd7SKarsten Graul struct smc_wr_tx_pend_priv *pend; 783f3811fd7SKarsten Graul struct smc_wr_buf *wr_buf; 784f3811fd7SKarsten Graul int rc; 785f3811fd7SKarsten Graul 78695f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 787f3811fd7SKarsten Graul return -ENOLINK; 788f3811fd7SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 789f3811fd7SKarsten Graul if (rc) 79095f7f3e7SKarsten Graul goto put_out; 791f3811fd7SKarsten Graul memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); 79295f7f3e7SKarsten Graul rc = smc_wr_tx_send_wait(link, pend, SMC_LLC_WAIT_TIME); 79395f7f3e7SKarsten Graul put_out: 79495f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 79595f7f3e7SKarsten Graul return rc; 796f3811fd7SKarsten Graul } 797f3811fd7SKarsten Graul 7989bf9abeaSUrsula Braun /********************************* receive ***********************************/ 7999bf9abeaSUrsula Braun 800336ba09fSKarsten Graul static int smc_llc_alloc_alt_link(struct smc_link_group *lgr, 801336ba09fSKarsten Graul enum smc_lgr_type lgr_new_t) 802336ba09fSKarsten Graul { 803336ba09fSKarsten Graul int i; 804336ba09fSKarsten Graul 805336ba09fSKarsten Graul if (lgr->type == SMC_LGR_SYMMETRIC || 806336ba09fSKarsten Graul (lgr->type != SMC_LGR_SINGLE && 807336ba09fSKarsten Graul (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 808336ba09fSKarsten Graul lgr_new_t == SMC_LGR_ASYMMETRIC_PEER))) 809336ba09fSKarsten Graul return -EMLINK; 810336ba09fSKarsten Graul 811336ba09fSKarsten Graul if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 812336ba09fSKarsten Graul lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) { 813336ba09fSKarsten Graul for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--) 814336ba09fSKarsten Graul if (lgr->lnk[i].state == SMC_LNK_UNUSED) 815336ba09fSKarsten Graul return i; 816336ba09fSKarsten Graul } else { 817336ba09fSKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) 818336ba09fSKarsten Graul if (lgr->lnk[i].state == SMC_LNK_UNUSED) 819336ba09fSKarsten Graul return i; 820336ba09fSKarsten Graul } 821336ba09fSKarsten Graul return -EMLINK; 822336ba09fSKarsten Graul } 823336ba09fSKarsten Graul 82487f88cdaSKarsten Graul /* send one add_link_continue msg */ 82587f88cdaSKarsten Graul static int smc_llc_add_link_cont(struct smc_link *link, 82687f88cdaSKarsten Graul struct smc_link *link_new, u8 *num_rkeys_todo, 82787f88cdaSKarsten Graul int *buf_lst, struct smc_buf_desc **buf_pos) 82887f88cdaSKarsten Graul { 82987f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont *addc_llc; 83087f88cdaSKarsten Graul struct smc_link_group *lgr = link->lgr; 83187f88cdaSKarsten Graul int prim_lnk_idx, lnk_idx, i, rc; 83287f88cdaSKarsten Graul struct smc_wr_tx_pend_priv *pend; 83387f88cdaSKarsten Graul struct smc_wr_buf *wr_buf; 83487f88cdaSKarsten Graul struct smc_buf_desc *rmb; 83587f88cdaSKarsten Graul u8 n; 83687f88cdaSKarsten Graul 83795f7f3e7SKarsten Graul if (!smc_wr_tx_link_hold(link)) 83895f7f3e7SKarsten Graul return -ENOLINK; 83987f88cdaSKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 84087f88cdaSKarsten Graul if (rc) 84195f7f3e7SKarsten Graul goto put_out; 84287f88cdaSKarsten Graul addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf; 84387f88cdaSKarsten Graul memset(addc_llc, 0, sizeof(*addc_llc)); 84487f88cdaSKarsten Graul 84587f88cdaSKarsten Graul prim_lnk_idx = link->link_idx; 84687f88cdaSKarsten Graul lnk_idx = link_new->link_idx; 84787f88cdaSKarsten Graul addc_llc->link_num = link_new->link_id; 84887f88cdaSKarsten Graul addc_llc->num_rkeys = *num_rkeys_todo; 84987f88cdaSKarsten Graul n = *num_rkeys_todo; 85087f88cdaSKarsten Graul for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) { 85187f88cdaSKarsten Graul if (!*buf_pos) { 85287f88cdaSKarsten Graul addc_llc->num_rkeys = addc_llc->num_rkeys - 85387f88cdaSKarsten Graul *num_rkeys_todo; 85487f88cdaSKarsten Graul *num_rkeys_todo = 0; 85587f88cdaSKarsten Graul break; 85687f88cdaSKarsten Graul } 85787f88cdaSKarsten Graul rmb = *buf_pos; 85887f88cdaSKarsten Graul 859b8d19945SWen Gu addc_llc->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey); 860b8d19945SWen Gu addc_llc->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey); 861b8d19945SWen Gu addc_llc->rt[i].rmb_vaddr_new = rmb->is_vm ? 862b8d19945SWen Gu cpu_to_be64((uintptr_t)rmb->cpu_addr) : 86387f88cdaSKarsten Graul cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); 86487f88cdaSKarsten Graul 86587f88cdaSKarsten Graul (*num_rkeys_todo)--; 86687f88cdaSKarsten Graul *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 86787f88cdaSKarsten Graul while (*buf_pos && !(*buf_pos)->used) 86887f88cdaSKarsten Graul *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 86987f88cdaSKarsten Graul } 870b4ba4652SKarsten Graul addc_llc->hd.common.llc_type = SMC_LLC_ADD_LINK_CONT; 87187f88cdaSKarsten Graul addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont); 87287f88cdaSKarsten Graul if (lgr->role == SMC_CLNT) 87387f88cdaSKarsten Graul addc_llc->hd.flags |= SMC_LLC_FLAG_RESP; 87495f7f3e7SKarsten Graul rc = smc_wr_tx_send(link, pend); 87595f7f3e7SKarsten Graul put_out: 87695f7f3e7SKarsten Graul smc_wr_tx_link_put(link); 87795f7f3e7SKarsten Graul return rc; 87887f88cdaSKarsten Graul } 87987f88cdaSKarsten Graul 88087f88cdaSKarsten Graul static int smc_llc_cli_rkey_exchange(struct smc_link *link, 88187f88cdaSKarsten Graul struct smc_link *link_new) 88287f88cdaSKarsten Graul { 88387f88cdaSKarsten Graul struct smc_llc_msg_add_link_cont *addc_llc; 88487f88cdaSKarsten Graul struct smc_link_group *lgr = link->lgr; 88587f88cdaSKarsten Graul u8 max, num_rkeys_send, num_rkeys_recv; 88687f88cdaSKarsten Graul struct smc_llc_qentry *qentry; 88787f88cdaSKarsten Graul struct smc_buf_desc *buf_pos; 88887f88cdaSKarsten Graul int buf_lst; 88987f88cdaSKarsten Graul int rc = 0; 89087f88cdaSKarsten Graul int i; 89187f88cdaSKarsten Graul 89287f88cdaSKarsten Graul mutex_lock(&lgr->rmbs_lock); 89387f88cdaSKarsten Graul num_rkeys_send = lgr->conns_num; 89487f88cdaSKarsten Graul buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 89587f88cdaSKarsten Graul do { 89687f88cdaSKarsten Graul qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME, 89787f88cdaSKarsten Graul SMC_LLC_ADD_LINK_CONT); 89887f88cdaSKarsten Graul if (!qentry) { 89987f88cdaSKarsten Graul rc = -ETIMEDOUT; 90087f88cdaSKarsten Graul break; 90187f88cdaSKarsten Graul } 90287f88cdaSKarsten Graul addc_llc = &qentry->msg.add_link_cont; 90387f88cdaSKarsten Graul num_rkeys_recv = addc_llc->num_rkeys; 90487f88cdaSKarsten Graul max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 90587f88cdaSKarsten Graul for (i = 0; i < max; i++) { 90687f88cdaSKarsten Graul smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 90787f88cdaSKarsten Graul addc_llc->rt[i].rmb_key, 90887f88cdaSKarsten Graul addc_llc->rt[i].rmb_vaddr_new, 90987f88cdaSKarsten Graul addc_llc->rt[i].rmb_key_new); 91087f88cdaSKarsten Graul num_rkeys_recv--; 91187f88cdaSKarsten Graul } 91287f88cdaSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 91387f88cdaSKarsten Graul rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 91487f88cdaSKarsten Graul &buf_lst, &buf_pos); 91587f88cdaSKarsten Graul if (rc) 91687f88cdaSKarsten Graul break; 91787f88cdaSKarsten Graul } while (num_rkeys_send || num_rkeys_recv); 91887f88cdaSKarsten Graul 91987f88cdaSKarsten Graul mutex_unlock(&lgr->rmbs_lock); 92087f88cdaSKarsten Graul return rc; 92187f88cdaSKarsten Graul } 92287f88cdaSKarsten Graul 923336ba09fSKarsten Graul /* prepare and send an add link reject response */ 924336ba09fSKarsten Graul static int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry) 925336ba09fSKarsten Graul { 926336ba09fSKarsten Graul qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 927336ba09fSKarsten Graul qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; 928336ba09fSKarsten Graul qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; 929b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr, 930b4ba4652SKarsten Graul sizeof(qentry->msg)); 931336ba09fSKarsten Graul return smc_llc_send_message(qentry->link, &qentry->msg); 932336ba09fSKarsten Graul } 933336ba09fSKarsten Graul 934b1570a87SKarsten Graul static int smc_llc_cli_conf_link(struct smc_link *link, 935b1570a87SKarsten Graul struct smc_init_info *ini, 936b1570a87SKarsten Graul struct smc_link *link_new, 937b1570a87SKarsten Graul enum smc_lgr_type lgr_new_t) 938b1570a87SKarsten Graul { 939b1570a87SKarsten Graul struct smc_link_group *lgr = link->lgr; 940b1570a87SKarsten Graul struct smc_llc_qentry *qentry = NULL; 941b1570a87SKarsten Graul int rc = 0; 942b1570a87SKarsten Graul 943b1570a87SKarsten Graul /* receive CONFIRM LINK request over RoCE fabric */ 944b1570a87SKarsten Graul qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0); 945b1570a87SKarsten Graul if (!qentry) { 946b1570a87SKarsten Graul rc = smc_llc_send_delete_link(link, link_new->link_id, 947b1570a87SKarsten Graul SMC_LLC_REQ, false, 948b1570a87SKarsten Graul SMC_LLC_DEL_LOST_PATH); 949b1570a87SKarsten Graul return -ENOLINK; 950b1570a87SKarsten Graul } 951b4ba4652SKarsten Graul if (qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) { 952b1570a87SKarsten Graul /* received DELETE_LINK instead */ 953b1570a87SKarsten Graul qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 954b1570a87SKarsten Graul smc_llc_send_message(link, &qentry->msg); 955b1570a87SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 956b1570a87SKarsten Graul return -ENOLINK; 957b1570a87SKarsten Graul } 958649758ffSKarsten Graul smc_llc_save_peer_uid(qentry); 959b1570a87SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 960b1570a87SKarsten Graul 961b1570a87SKarsten Graul rc = smc_ib_modify_qp_rts(link_new); 962b1570a87SKarsten Graul if (rc) { 963b1570a87SKarsten Graul smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 964b1570a87SKarsten Graul false, SMC_LLC_DEL_LOST_PATH); 965b1570a87SKarsten Graul return -ENOLINK; 966b1570a87SKarsten Graul } 967b1570a87SKarsten Graul smc_wr_remember_qp_attr(link_new); 968b1570a87SKarsten Graul 969b1570a87SKarsten Graul rc = smcr_buf_reg_lgr(link_new); 970b1570a87SKarsten Graul if (rc) { 971b1570a87SKarsten Graul smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 972b1570a87SKarsten Graul false, SMC_LLC_DEL_LOST_PATH); 973b1570a87SKarsten Graul return -ENOLINK; 974b1570a87SKarsten Graul } 975b1570a87SKarsten Graul 976b1570a87SKarsten Graul /* send CONFIRM LINK response over RoCE fabric */ 977b1570a87SKarsten Graul rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP); 978b1570a87SKarsten Graul if (rc) { 979b1570a87SKarsten Graul smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 980b1570a87SKarsten Graul false, SMC_LLC_DEL_LOST_PATH); 981b1570a87SKarsten Graul return -ENOLINK; 982b1570a87SKarsten Graul } 983b1570a87SKarsten Graul smc_llc_link_active(link_new); 984ad6c111bSKarsten Graul if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 985ad6c111bSKarsten Graul lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) 986ad6c111bSKarsten Graul smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); 987ad6c111bSKarsten Graul else 988ad6c111bSKarsten Graul smcr_lgr_set_type(lgr, lgr_new_t); 989b1570a87SKarsten Graul return 0; 990b1570a87SKarsten Graul } 991b1570a87SKarsten Graul 992b4ba4652SKarsten Graul static void smc_llc_save_add_link_rkeys(struct smc_link *link, 993b4ba4652SKarsten Graul struct smc_link *link_new) 994b4ba4652SKarsten Graul { 995b4ba4652SKarsten Graul struct smc_llc_msg_add_link_v2_ext *ext; 996b4ba4652SKarsten Graul struct smc_link_group *lgr = link->lgr; 997b4ba4652SKarsten Graul int max, i; 998b4ba4652SKarsten Graul 999b4ba4652SKarsten Graul ext = (struct smc_llc_msg_add_link_v2_ext *)((u8 *)lgr->wr_rx_buf_v2 + 1000b4ba4652SKarsten Graul SMC_WR_TX_SIZE); 1001b4ba4652SKarsten Graul max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); 1002b4ba4652SKarsten Graul mutex_lock(&lgr->rmbs_lock); 1003b4ba4652SKarsten Graul for (i = 0; i < max; i++) { 1004b4ba4652SKarsten Graul smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 1005b4ba4652SKarsten Graul ext->rt[i].rmb_key, 1006b4ba4652SKarsten Graul ext->rt[i].rmb_vaddr_new, 1007b4ba4652SKarsten Graul ext->rt[i].rmb_key_new); 1008b4ba4652SKarsten Graul } 1009b4ba4652SKarsten Graul mutex_unlock(&lgr->rmbs_lock); 1010b4ba4652SKarsten Graul } 1011b4ba4652SKarsten Graul 1012336ba09fSKarsten Graul static void smc_llc_save_add_link_info(struct smc_link *link, 1013336ba09fSKarsten Graul struct smc_llc_msg_add_link *add_llc) 1014336ba09fSKarsten Graul { 1015336ba09fSKarsten Graul link->peer_qpn = ntoh24(add_llc->sender_qp_num); 1016336ba09fSKarsten Graul memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE); 1017336ba09fSKarsten Graul memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN); 1018336ba09fSKarsten Graul link->peer_psn = ntoh24(add_llc->initial_psn); 1019336ba09fSKarsten Graul link->peer_mtu = add_llc->qp_mtu; 1020336ba09fSKarsten Graul } 1021336ba09fSKarsten Graul 1022336ba09fSKarsten Graul /* as an SMC client, process an add link request */ 1023336ba09fSKarsten Graul int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry) 1024336ba09fSKarsten Graul { 1025336ba09fSKarsten Graul struct smc_llc_msg_add_link *llc = &qentry->msg.add_link; 1026336ba09fSKarsten Graul enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 1027336ba09fSKarsten Graul struct smc_link_group *lgr = smc_get_lgr(link); 1028ed990df2SKarsten Graul struct smc_init_info *ini = NULL; 1029336ba09fSKarsten Graul struct smc_link *lnk_new = NULL; 1030336ba09fSKarsten Graul int lnk_idx, rc = 0; 1031336ba09fSKarsten Graul 1032fffe83c8SKarsten Graul if (!llc->qp_mtu) 1033fffe83c8SKarsten Graul goto out_reject; 1034fffe83c8SKarsten Graul 1035ed990df2SKarsten Graul ini = kzalloc(sizeof(*ini), GFP_KERNEL); 1036ed990df2SKarsten Graul if (!ini) { 1037ed990df2SKarsten Graul rc = -ENOMEM; 1038ed990df2SKarsten Graul goto out_reject; 1039ed990df2SKarsten Graul } 1040ed990df2SKarsten Graul 1041ed990df2SKarsten Graul ini->vlan_id = lgr->vlan_id; 1042b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2) { 1043b4ba4652SKarsten Graul ini->check_smcrv2 = true; 1044b4ba4652SKarsten Graul ini->smcrv2.saddr = lgr->saddr; 1045b4ba4652SKarsten Graul ini->smcrv2.daddr = smc_ib_gid_to_ipv4(llc->sender_gid); 1046b4ba4652SKarsten Graul } 1047ed990df2SKarsten Graul smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); 1048336ba09fSKarsten Graul if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 1049b4ba4652SKarsten Graul (lgr->smc_version == SMC_V2 || 1050b4ba4652SKarsten Graul !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN))) { 1051b4ba4652SKarsten Graul if (!ini->ib_dev && !ini->smcrv2.ib_dev_v2) 1052336ba09fSKarsten Graul goto out_reject; 1053336ba09fSKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 1054336ba09fSKarsten Graul } 1055b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) { 1056b4ba4652SKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 1057b4ba4652SKarsten Graul ini->smcrv2.ib_dev_v2 = link->smcibdev; 1058b4ba4652SKarsten Graul ini->smcrv2.ib_port_v2 = link->ibport; 1059b4ba4652SKarsten Graul } else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) { 1060336ba09fSKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 1061ed990df2SKarsten Graul ini->ib_dev = link->smcibdev; 1062ed990df2SKarsten Graul ini->ib_port = link->ibport; 1063336ba09fSKarsten Graul } 1064336ba09fSKarsten Graul lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 1065336ba09fSKarsten Graul if (lnk_idx < 0) 1066336ba09fSKarsten Graul goto out_reject; 1067336ba09fSKarsten Graul lnk_new = &lgr->lnk[lnk_idx]; 1068ed990df2SKarsten Graul rc = smcr_link_init(lgr, lnk_new, lnk_idx, ini); 1069336ba09fSKarsten Graul if (rc) 1070336ba09fSKarsten Graul goto out_reject; 1071336ba09fSKarsten Graul smc_llc_save_add_link_info(lnk_new, llc); 107245fa8da0SKarsten Graul lnk_new->link_id = llc->link_num; /* SMC server assigns link id */ 107345fa8da0SKarsten Graul smc_llc_link_set_uid(lnk_new); 1074336ba09fSKarsten Graul 1075336ba09fSKarsten Graul rc = smc_ib_ready_link(lnk_new); 1076336ba09fSKarsten Graul if (rc) 1077336ba09fSKarsten Graul goto out_clear_lnk; 1078336ba09fSKarsten Graul 1079336ba09fSKarsten Graul rc = smcr_buf_map_lgr(lnk_new); 1080336ba09fSKarsten Graul if (rc) 1081336ba09fSKarsten Graul goto out_clear_lnk; 1082336ba09fSKarsten Graul 1083336ba09fSKarsten Graul rc = smc_llc_send_add_link(link, 1084b4ba4652SKarsten Graul lnk_new->smcibdev->mac[lnk_new->ibport - 1], 1085336ba09fSKarsten Graul lnk_new->gid, lnk_new, SMC_LLC_RESP); 1086336ba09fSKarsten Graul if (rc) 1087336ba09fSKarsten Graul goto out_clear_lnk; 1088b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2) { 1089b4ba4652SKarsten Graul smc_llc_save_add_link_rkeys(link, lnk_new); 1090b4ba4652SKarsten Graul } else { 109187f88cdaSKarsten Graul rc = smc_llc_cli_rkey_exchange(link, lnk_new); 1092336ba09fSKarsten Graul if (rc) { 1093336ba09fSKarsten Graul rc = 0; 1094336ba09fSKarsten Graul goto out_clear_lnk; 1095336ba09fSKarsten Graul } 1096b4ba4652SKarsten Graul } 1097ed990df2SKarsten Graul rc = smc_llc_cli_conf_link(link, ini, lnk_new, lgr_new_t); 1098336ba09fSKarsten Graul if (!rc) 1099336ba09fSKarsten Graul goto out; 1100336ba09fSKarsten Graul out_clear_lnk: 11018f3d65c1SKarsten Graul lnk_new->state = SMC_LNK_INACTIVE; 11020a99be43SKarsten Graul smcr_link_clear(lnk_new, false); 1103336ba09fSKarsten Graul out_reject: 1104336ba09fSKarsten Graul smc_llc_cli_add_link_reject(qentry); 1105336ba09fSKarsten Graul out: 1106ed990df2SKarsten Graul kfree(ini); 1107336ba09fSKarsten Graul kfree(qentry); 1108336ba09fSKarsten Graul return rc; 1109336ba09fSKarsten Graul } 1110336ba09fSKarsten Graul 1111b4ba4652SKarsten Graul static void smc_llc_send_request_add_link(struct smc_link *link) 1112b4ba4652SKarsten Graul { 1113b4ba4652SKarsten Graul struct smc_llc_msg_req_add_link_v2 *llc; 1114b4ba4652SKarsten Graul struct smc_wr_tx_pend_priv *pend; 1115b4ba4652SKarsten Graul struct smc_wr_v2_buf *wr_buf; 1116b4ba4652SKarsten Graul struct smc_gidlist gidlist; 1117b4ba4652SKarsten Graul int rc, len, i; 1118b4ba4652SKarsten Graul 1119b4ba4652SKarsten Graul if (!smc_wr_tx_link_hold(link)) 1120b4ba4652SKarsten Graul return; 1121b4ba4652SKarsten Graul if (link->lgr->type == SMC_LGR_SYMMETRIC || 1122b4ba4652SKarsten Graul link->lgr->type == SMC_LGR_ASYMMETRIC_PEER) 1123b4ba4652SKarsten Graul goto put_out; 1124b4ba4652SKarsten Graul 1125b4ba4652SKarsten Graul smc_fill_gid_list(link->lgr, &gidlist, link->smcibdev, link->gid); 1126b4ba4652SKarsten Graul if (gidlist.len <= 1) 1127b4ba4652SKarsten Graul goto put_out; 1128b4ba4652SKarsten Graul 1129b4ba4652SKarsten Graul rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend); 1130b4ba4652SKarsten Graul if (rc) 1131b4ba4652SKarsten Graul goto put_out; 1132b4ba4652SKarsten Graul llc = (struct smc_llc_msg_req_add_link_v2 *)wr_buf; 1133b4ba4652SKarsten Graul memset(llc, 0, SMC_WR_TX_SIZE); 1134b4ba4652SKarsten Graul 1135b4ba4652SKarsten Graul llc->hd.common.llc_type = SMC_LLC_REQ_ADD_LINK; 1136b4ba4652SKarsten Graul for (i = 0; i < gidlist.len; i++) 1137b4ba4652SKarsten Graul memcpy(llc->gid[i], gidlist.list[i], sizeof(gidlist.list[0])); 1138b4ba4652SKarsten Graul llc->gid_cnt = gidlist.len; 1139b4ba4652SKarsten Graul len = sizeof(*llc) + (gidlist.len * sizeof(gidlist.list[0])); 1140b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&llc->hd, link->lgr, len); 1141b4ba4652SKarsten Graul rc = smc_wr_tx_v2_send(link, pend, len); 1142b4ba4652SKarsten Graul if (!rc) 1143b4ba4652SKarsten Graul /* set REQ_ADD_LINK flow and wait for response from peer */ 1144b4ba4652SKarsten Graul link->lgr->llc_flow_lcl.type = SMC_LLC_FLOW_REQ_ADD_LINK; 1145b4ba4652SKarsten Graul put_out: 1146b4ba4652SKarsten Graul smc_wr_tx_link_put(link); 1147b4ba4652SKarsten Graul } 1148b4ba4652SKarsten Graul 1149c48254faSKarsten Graul /* as an SMC client, invite server to start the add_link processing */ 1150c48254faSKarsten Graul static void smc_llc_cli_add_link_invite(struct smc_link *link, 1151c48254faSKarsten Graul struct smc_llc_qentry *qentry) 1152c48254faSKarsten Graul { 1153c48254faSKarsten Graul struct smc_link_group *lgr = smc_get_lgr(link); 1154ed990df2SKarsten Graul struct smc_init_info *ini = NULL; 1155c48254faSKarsten Graul 1156b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2) { 1157b4ba4652SKarsten Graul smc_llc_send_request_add_link(link); 1158b4ba4652SKarsten Graul goto out; 1159b4ba4652SKarsten Graul } 1160b4ba4652SKarsten Graul 1161c48254faSKarsten Graul if (lgr->type == SMC_LGR_SYMMETRIC || 1162c48254faSKarsten Graul lgr->type == SMC_LGR_ASYMMETRIC_PEER) 1163c48254faSKarsten Graul goto out; 1164c48254faSKarsten Graul 1165ed990df2SKarsten Graul ini = kzalloc(sizeof(*ini), GFP_KERNEL); 1166ed990df2SKarsten Graul if (!ini) 1167c48254faSKarsten Graul goto out; 1168c48254faSKarsten Graul 1169ed990df2SKarsten Graul ini->vlan_id = lgr->vlan_id; 1170ed990df2SKarsten Graul smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); 1171ed990df2SKarsten Graul if (!ini->ib_dev) 1172ed990df2SKarsten Graul goto out; 1173ed990df2SKarsten Graul 1174ed990df2SKarsten Graul smc_llc_send_add_link(link, ini->ib_dev->mac[ini->ib_port - 1], 1175ed990df2SKarsten Graul ini->ib_gid, NULL, SMC_LLC_REQ); 1176c48254faSKarsten Graul out: 1177ed990df2SKarsten Graul kfree(ini); 1178c48254faSKarsten Graul kfree(qentry); 1179c48254faSKarsten Graul } 1180c48254faSKarsten Graul 1181fffe83c8SKarsten Graul static bool smc_llc_is_empty_llc_message(union smc_llc_msg *llc) 1182fffe83c8SKarsten Graul { 1183fffe83c8SKarsten Graul int i; 1184fffe83c8SKarsten Graul 1185fffe83c8SKarsten Graul for (i = 0; i < ARRAY_SIZE(llc->raw.data); i++) 1186fffe83c8SKarsten Graul if (llc->raw.data[i]) 1187fffe83c8SKarsten Graul return false; 1188fffe83c8SKarsten Graul return true; 1189fffe83c8SKarsten Graul } 1190fffe83c8SKarsten Graul 1191c48254faSKarsten Graul static bool smc_llc_is_local_add_link(union smc_llc_msg *llc) 1192c48254faSKarsten Graul { 1193b4ba4652SKarsten Graul if (llc->raw.hdr.common.llc_type == SMC_LLC_ADD_LINK && 1194fffe83c8SKarsten Graul smc_llc_is_empty_llc_message(llc)) 1195c48254faSKarsten Graul return true; 1196c48254faSKarsten Graul return false; 1197c48254faSKarsten Graul } 1198c48254faSKarsten Graul 1199b1570a87SKarsten Graul static void smc_llc_process_cli_add_link(struct smc_link_group *lgr) 1200b1570a87SKarsten Graul { 1201b1570a87SKarsten Graul struct smc_llc_qentry *qentry; 1202b1570a87SKarsten Graul 1203b1570a87SKarsten Graul qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 1204b1570a87SKarsten Graul 1205*b5dd4d69SD. Wythe down_write(&lgr->llc_conf_mutex); 1206c48254faSKarsten Graul if (smc_llc_is_local_add_link(&qentry->msg)) 1207c48254faSKarsten Graul smc_llc_cli_add_link_invite(qentry->link, qentry); 1208c48254faSKarsten Graul else 1209b1570a87SKarsten Graul smc_llc_cli_add_link(qentry->link, qentry); 1210*b5dd4d69SD. Wythe up_write(&lgr->llc_conf_mutex); 1211b1570a87SKarsten Graul } 1212b1570a87SKarsten Graul 12139c416878SKarsten Graul static int smc_llc_active_link_count(struct smc_link_group *lgr) 12149c416878SKarsten Graul { 12159c416878SKarsten Graul int i, link_count = 0; 12169c416878SKarsten Graul 12179c416878SKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 1218741a49a4SKarsten Graul if (!smc_link_active(&lgr->lnk[i])) 12199c416878SKarsten Graul continue; 12209c416878SKarsten Graul link_count++; 12219c416878SKarsten Graul } 12229c416878SKarsten Graul return link_count; 12239c416878SKarsten Graul } 12249c416878SKarsten Graul 1225c9a5d243SKarsten Graul /* find the asymmetric link when 3 links are established */ 1226c9a5d243SKarsten Graul static struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr) 1227c9a5d243SKarsten Graul { 1228c9a5d243SKarsten Graul int asym_idx = -ENOENT; 1229c9a5d243SKarsten Graul int i, j, k; 1230c9a5d243SKarsten Graul bool found; 1231c9a5d243SKarsten Graul 1232c9a5d243SKarsten Graul /* determine asymmetric link */ 1233c9a5d243SKarsten Graul found = false; 1234c9a5d243SKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 1235c9a5d243SKarsten Graul for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) { 1236c9a5d243SKarsten Graul if (!smc_link_usable(&lgr->lnk[i]) || 1237c9a5d243SKarsten Graul !smc_link_usable(&lgr->lnk[j])) 1238c9a5d243SKarsten Graul continue; 1239c9a5d243SKarsten Graul if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid, 1240c9a5d243SKarsten Graul SMC_GID_SIZE)) { 1241c9a5d243SKarsten Graul found = true; /* asym_lnk is i or j */ 1242c9a5d243SKarsten Graul break; 1243c9a5d243SKarsten Graul } 1244c9a5d243SKarsten Graul } 1245c9a5d243SKarsten Graul if (found) 1246c9a5d243SKarsten Graul break; 1247c9a5d243SKarsten Graul } 1248c9a5d243SKarsten Graul if (!found) 1249c9a5d243SKarsten Graul goto out; /* no asymmetric link */ 1250c9a5d243SKarsten Graul for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) { 1251c9a5d243SKarsten Graul if (!smc_link_usable(&lgr->lnk[k])) 1252c9a5d243SKarsten Graul continue; 1253c9a5d243SKarsten Graul if (k != i && 1254c9a5d243SKarsten Graul !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid, 1255c9a5d243SKarsten Graul SMC_GID_SIZE)) { 1256c9a5d243SKarsten Graul asym_idx = i; 1257c9a5d243SKarsten Graul break; 1258c9a5d243SKarsten Graul } 1259c9a5d243SKarsten Graul if (k != j && 1260c9a5d243SKarsten Graul !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid, 1261c9a5d243SKarsten Graul SMC_GID_SIZE)) { 1262c9a5d243SKarsten Graul asym_idx = j; 1263c9a5d243SKarsten Graul break; 1264c9a5d243SKarsten Graul } 1265c9a5d243SKarsten Graul } 1266c9a5d243SKarsten Graul out: 1267c9a5d243SKarsten Graul return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx]; 1268c9a5d243SKarsten Graul } 1269c9a5d243SKarsten Graul 1270c9a5d243SKarsten Graul static void smc_llc_delete_asym_link(struct smc_link_group *lgr) 1271c9a5d243SKarsten Graul { 1272c9a5d243SKarsten Graul struct smc_link *lnk_new = NULL, *lnk_asym; 1273c9a5d243SKarsten Graul struct smc_llc_qentry *qentry; 1274c9a5d243SKarsten Graul int rc; 1275c9a5d243SKarsten Graul 1276c9a5d243SKarsten Graul lnk_asym = smc_llc_find_asym_link(lgr); 1277c9a5d243SKarsten Graul if (!lnk_asym) 1278c9a5d243SKarsten Graul return; /* no asymmetric link */ 1279c9a5d243SKarsten Graul if (!smc_link_downing(&lnk_asym->state)) 1280c9a5d243SKarsten Graul return; 1281c6f02ebeSKarsten Graul lnk_new = smc_switch_conns(lgr, lnk_asym, false); 1282c9a5d243SKarsten Graul smc_wr_tx_wait_no_pending_sends(lnk_asym); 1283c9a5d243SKarsten Graul if (!lnk_new) 1284c9a5d243SKarsten Graul goto out_free; 1285c9a5d243SKarsten Graul /* change flow type from ADD_LINK into DEL_LINK */ 1286c9a5d243SKarsten Graul lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK; 1287c9a5d243SKarsten Graul rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ, 1288c9a5d243SKarsten Graul true, SMC_LLC_DEL_NO_ASYM_NEEDED); 1289c9a5d243SKarsten Graul if (rc) { 1290c9a5d243SKarsten Graul smcr_link_down_cond(lnk_new); 1291c9a5d243SKarsten Graul goto out_free; 1292c9a5d243SKarsten Graul } 1293c9a5d243SKarsten Graul qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME, 1294c9a5d243SKarsten Graul SMC_LLC_DELETE_LINK); 1295c9a5d243SKarsten Graul if (!qentry) { 1296c9a5d243SKarsten Graul smcr_link_down_cond(lnk_new); 1297c9a5d243SKarsten Graul goto out_free; 1298c9a5d243SKarsten Graul } 1299c9a5d243SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 1300c9a5d243SKarsten Graul out_free: 13010a99be43SKarsten Graul smcr_link_clear(lnk_asym, true); 1302c9a5d243SKarsten Graul } 1303c9a5d243SKarsten Graul 130457b49924SKarsten Graul static int smc_llc_srv_rkey_exchange(struct smc_link *link, 130557b49924SKarsten Graul struct smc_link *link_new) 130657b49924SKarsten Graul { 130757b49924SKarsten Graul struct smc_llc_msg_add_link_cont *addc_llc; 130857b49924SKarsten Graul struct smc_link_group *lgr = link->lgr; 130957b49924SKarsten Graul u8 max, num_rkeys_send, num_rkeys_recv; 131057b49924SKarsten Graul struct smc_llc_qentry *qentry = NULL; 131157b49924SKarsten Graul struct smc_buf_desc *buf_pos; 131257b49924SKarsten Graul int buf_lst; 131357b49924SKarsten Graul int rc = 0; 131457b49924SKarsten Graul int i; 131557b49924SKarsten Graul 131657b49924SKarsten Graul mutex_lock(&lgr->rmbs_lock); 131757b49924SKarsten Graul num_rkeys_send = lgr->conns_num; 131857b49924SKarsten Graul buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 131957b49924SKarsten Graul do { 132057b49924SKarsten Graul smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 132157b49924SKarsten Graul &buf_lst, &buf_pos); 132257b49924SKarsten Graul qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, 132357b49924SKarsten Graul SMC_LLC_ADD_LINK_CONT); 132457b49924SKarsten Graul if (!qentry) { 132557b49924SKarsten Graul rc = -ETIMEDOUT; 132657b49924SKarsten Graul goto out; 132757b49924SKarsten Graul } 132857b49924SKarsten Graul addc_llc = &qentry->msg.add_link_cont; 132957b49924SKarsten Graul num_rkeys_recv = addc_llc->num_rkeys; 133057b49924SKarsten Graul max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 133157b49924SKarsten Graul for (i = 0; i < max; i++) { 133257b49924SKarsten Graul smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 133357b49924SKarsten Graul addc_llc->rt[i].rmb_key, 133457b49924SKarsten Graul addc_llc->rt[i].rmb_vaddr_new, 133557b49924SKarsten Graul addc_llc->rt[i].rmb_key_new); 133657b49924SKarsten Graul num_rkeys_recv--; 133757b49924SKarsten Graul } 133857b49924SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 133957b49924SKarsten Graul } while (num_rkeys_send || num_rkeys_recv); 134057b49924SKarsten Graul out: 134157b49924SKarsten Graul mutex_unlock(&lgr->rmbs_lock); 134257b49924SKarsten Graul return rc; 134357b49924SKarsten Graul } 134457b49924SKarsten Graul 13451551c95bSKarsten Graul static int smc_llc_srv_conf_link(struct smc_link *link, 13461551c95bSKarsten Graul struct smc_link *link_new, 13471551c95bSKarsten Graul enum smc_lgr_type lgr_new_t) 13481551c95bSKarsten Graul { 13491551c95bSKarsten Graul struct smc_link_group *lgr = link->lgr; 13501551c95bSKarsten Graul struct smc_llc_qentry *qentry = NULL; 13511551c95bSKarsten Graul int rc; 13521551c95bSKarsten Graul 13531551c95bSKarsten Graul /* send CONFIRM LINK request over the RoCE fabric */ 13541551c95bSKarsten Graul rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ); 13551551c95bSKarsten Graul if (rc) 13561551c95bSKarsten Graul return -ENOLINK; 13571551c95bSKarsten Graul /* receive CONFIRM LINK response over the RoCE fabric */ 1358a35fffbfSKarsten Graul qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, 0); 1359a35fffbfSKarsten Graul if (!qentry || 1360b4ba4652SKarsten Graul qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) { 13611551c95bSKarsten Graul /* send DELETE LINK */ 13621551c95bSKarsten Graul smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 13631551c95bSKarsten Graul false, SMC_LLC_DEL_LOST_PATH); 1364a35fffbfSKarsten Graul if (qentry) 1365a35fffbfSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 13661551c95bSKarsten Graul return -ENOLINK; 13671551c95bSKarsten Graul } 1368649758ffSKarsten Graul smc_llc_save_peer_uid(qentry); 13691551c95bSKarsten Graul smc_llc_link_active(link_new); 1370ad6c111bSKarsten Graul if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 1371ad6c111bSKarsten Graul lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) 1372ad6c111bSKarsten Graul smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); 1373ad6c111bSKarsten Graul else 1374ad6c111bSKarsten Graul smcr_lgr_set_type(lgr, lgr_new_t); 13751551c95bSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 13761551c95bSKarsten Graul return 0; 13771551c95bSKarsten Graul } 13781551c95bSKarsten Graul 1379b4ba4652SKarsten Graul static void smc_llc_send_req_add_link_response(struct smc_llc_qentry *qentry) 1380b4ba4652SKarsten Graul { 1381b4ba4652SKarsten Graul qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 1382b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr, 1383b4ba4652SKarsten Graul sizeof(qentry->msg)); 1384b4ba4652SKarsten Graul memset(&qentry->msg.raw.data, 0, sizeof(qentry->msg.raw.data)); 1385b4ba4652SKarsten Graul smc_llc_send_message(qentry->link, &qentry->msg); 1386b4ba4652SKarsten Graul } 1387b4ba4652SKarsten Graul 1388b4ba4652SKarsten Graul int smc_llc_srv_add_link(struct smc_link *link, 1389b4ba4652SKarsten Graul struct smc_llc_qentry *req_qentry) 13902d2209f2SKarsten Graul { 13912d2209f2SKarsten Graul enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 13922d2209f2SKarsten Graul struct smc_link_group *lgr = link->lgr; 13932d2209f2SKarsten Graul struct smc_llc_msg_add_link *add_llc; 13942d2209f2SKarsten Graul struct smc_llc_qentry *qentry = NULL; 1395b4ba4652SKarsten Graul bool send_req_add_link_resp = false; 1396ed990df2SKarsten Graul struct smc_link *link_new = NULL; 1397b4ba4652SKarsten Graul struct smc_init_info *ini = NULL; 13982d2209f2SKarsten Graul int lnk_idx, rc = 0; 13992d2209f2SKarsten Graul 1400b4ba4652SKarsten Graul if (req_qentry && 1401b4ba4652SKarsten Graul req_qentry->msg.raw.hdr.common.llc_type == SMC_LLC_REQ_ADD_LINK) 1402b4ba4652SKarsten Graul send_req_add_link_resp = true; 1403b4ba4652SKarsten Graul 1404ed990df2SKarsten Graul ini = kzalloc(sizeof(*ini), GFP_KERNEL); 1405b4ba4652SKarsten Graul if (!ini) { 1406b4ba4652SKarsten Graul rc = -ENOMEM; 1407b4ba4652SKarsten Graul goto out; 1408b4ba4652SKarsten Graul } 1409ed990df2SKarsten Graul 14102d2209f2SKarsten Graul /* ignore client add link recommendation, start new flow */ 1411ed990df2SKarsten Graul ini->vlan_id = lgr->vlan_id; 1412b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2) { 1413b4ba4652SKarsten Graul ini->check_smcrv2 = true; 1414b4ba4652SKarsten Graul ini->smcrv2.saddr = lgr->saddr; 1415b4ba4652SKarsten Graul if (send_req_add_link_resp) { 1416b4ba4652SKarsten Graul struct smc_llc_msg_req_add_link_v2 *req_add = 1417b4ba4652SKarsten Graul &req_qentry->msg.req_add_link; 1418b4ba4652SKarsten Graul 1419b4ba4652SKarsten Graul ini->smcrv2.daddr = smc_ib_gid_to_ipv4(req_add->gid[0]); 1420b4ba4652SKarsten Graul } 1421b4ba4652SKarsten Graul } 1422ed990df2SKarsten Graul smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); 1423b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) { 1424b4ba4652SKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 1425b4ba4652SKarsten Graul ini->smcrv2.ib_dev_v2 = link->smcibdev; 1426b4ba4652SKarsten Graul ini->smcrv2.ib_port_v2 = link->ibport; 1427b4ba4652SKarsten Graul } else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) { 14282d2209f2SKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 1429ed990df2SKarsten Graul ini->ib_dev = link->smcibdev; 1430ed990df2SKarsten Graul ini->ib_port = link->ibport; 14312d2209f2SKarsten Graul } 14322d2209f2SKarsten Graul lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 1433ed990df2SKarsten Graul if (lnk_idx < 0) { 1434ed990df2SKarsten Graul rc = 0; 1435ed990df2SKarsten Graul goto out; 1436ed990df2SKarsten Graul } 14372d2209f2SKarsten Graul 1438ed990df2SKarsten Graul rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, ini); 14392d2209f2SKarsten Graul if (rc) 1440ed990df2SKarsten Graul goto out; 14412d2209f2SKarsten Graul link_new = &lgr->lnk[lnk_idx]; 1442b4ba4652SKarsten Graul 1443b4ba4652SKarsten Graul rc = smcr_buf_map_lgr(link_new); 1444b4ba4652SKarsten Graul if (rc) 1445b4ba4652SKarsten Graul goto out_err; 1446b4ba4652SKarsten Graul 14472d2209f2SKarsten Graul rc = smc_llc_send_add_link(link, 1448b4ba4652SKarsten Graul link_new->smcibdev->mac[link_new->ibport-1], 14492d2209f2SKarsten Graul link_new->gid, link_new, SMC_LLC_REQ); 14502d2209f2SKarsten Graul if (rc) 14512d2209f2SKarsten Graul goto out_err; 1452b4ba4652SKarsten Graul send_req_add_link_resp = false; 14532d2209f2SKarsten Graul /* receive ADD LINK response over the RoCE fabric */ 14542d2209f2SKarsten Graul qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK); 14552d2209f2SKarsten Graul if (!qentry) { 14562d2209f2SKarsten Graul rc = -ETIMEDOUT; 14572d2209f2SKarsten Graul goto out_err; 14582d2209f2SKarsten Graul } 14592d2209f2SKarsten Graul add_llc = &qentry->msg.add_link; 14602d2209f2SKarsten Graul if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) { 14612d2209f2SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 14622d2209f2SKarsten Graul rc = -ENOLINK; 14632d2209f2SKarsten Graul goto out_err; 14642d2209f2SKarsten Graul } 14652d2209f2SKarsten Graul if (lgr->type == SMC_LGR_SINGLE && 14662d2209f2SKarsten Graul (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 1467b4ba4652SKarsten Graul (lgr->smc_version == SMC_V2 || 1468b4ba4652SKarsten Graul !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN)))) { 14692d2209f2SKarsten Graul lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 14702d2209f2SKarsten Graul } 14712d2209f2SKarsten Graul smc_llc_save_add_link_info(link_new, add_llc); 14722d2209f2SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 14732d2209f2SKarsten Graul 14742d2209f2SKarsten Graul rc = smc_ib_ready_link(link_new); 14752d2209f2SKarsten Graul if (rc) 14762d2209f2SKarsten Graul goto out_err; 14772d2209f2SKarsten Graul rc = smcr_buf_reg_lgr(link_new); 14782d2209f2SKarsten Graul if (rc) 14792d2209f2SKarsten Graul goto out_err; 1480b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2) { 1481b4ba4652SKarsten Graul smc_llc_save_add_link_rkeys(link, link_new); 1482b4ba4652SKarsten Graul } else { 148357b49924SKarsten Graul rc = smc_llc_srv_rkey_exchange(link, link_new); 14842d2209f2SKarsten Graul if (rc) 14852d2209f2SKarsten Graul goto out_err; 1486b4ba4652SKarsten Graul } 14871551c95bSKarsten Graul rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t); 14882d2209f2SKarsten Graul if (rc) 14892d2209f2SKarsten Graul goto out_err; 1490ed990df2SKarsten Graul kfree(ini); 14912d2209f2SKarsten Graul return 0; 14922d2209f2SKarsten Graul out_err: 1493ed990df2SKarsten Graul if (link_new) { 14948f3d65c1SKarsten Graul link_new->state = SMC_LNK_INACTIVE; 14950a99be43SKarsten Graul smcr_link_clear(link_new, false); 1496ed990df2SKarsten Graul } 1497ed990df2SKarsten Graul out: 1498ed990df2SKarsten Graul kfree(ini); 1499b4ba4652SKarsten Graul if (send_req_add_link_resp) 1500b4ba4652SKarsten Graul smc_llc_send_req_add_link_response(req_qentry); 15012d2209f2SKarsten Graul return rc; 15022d2209f2SKarsten Graul } 15032d2209f2SKarsten Graul 15042d2209f2SKarsten Graul static void smc_llc_process_srv_add_link(struct smc_link_group *lgr) 15052d2209f2SKarsten Graul { 15062d2209f2SKarsten Graul struct smc_link *link = lgr->llc_flow_lcl.qentry->link; 1507b4ba4652SKarsten Graul struct smc_llc_qentry *qentry; 15082d2209f2SKarsten Graul int rc; 15092d2209f2SKarsten Graul 1510b4ba4652SKarsten Graul qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 15112d2209f2SKarsten Graul 1512*b5dd4d69SD. Wythe down_write(&lgr->llc_conf_mutex); 1513b4ba4652SKarsten Graul rc = smc_llc_srv_add_link(link, qentry); 15142d2209f2SKarsten Graul if (!rc && lgr->type == SMC_LGR_SYMMETRIC) { 15152d2209f2SKarsten Graul /* delete any asymmetric link */ 1516c9a5d243SKarsten Graul smc_llc_delete_asym_link(lgr); 15172d2209f2SKarsten Graul } 1518*b5dd4d69SD. Wythe up_write(&lgr->llc_conf_mutex); 1519b4ba4652SKarsten Graul kfree(qentry); 15202d2209f2SKarsten Graul } 15212d2209f2SKarsten Graul 1522c48254faSKarsten Graul /* enqueue a local add_link req to trigger a new add_link flow */ 1523c48254faSKarsten Graul void smc_llc_add_link_local(struct smc_link *link) 15244dadd151SKarsten Graul { 152516cb3653SPujin Shi struct smc_llc_msg_add_link add_llc = {}; 15264dadd151SKarsten Graul 1527b4ba4652SKarsten Graul add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK; 1528b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc)); 1529c48254faSKarsten Graul /* no dev and port needed */ 15304dadd151SKarsten Graul smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc); 15314dadd151SKarsten Graul } 15324dadd151SKarsten Graul 1533b45e7f98SKarsten Graul /* worker to process an add link message */ 1534b45e7f98SKarsten Graul static void smc_llc_add_link_work(struct work_struct *work) 1535b45e7f98SKarsten Graul { 1536b45e7f98SKarsten Graul struct smc_link_group *lgr = container_of(work, struct smc_link_group, 1537b45e7f98SKarsten Graul llc_add_link_work); 1538b45e7f98SKarsten Graul 1539b45e7f98SKarsten Graul if (list_empty(&lgr->list)) { 1540b45e7f98SKarsten Graul /* link group is terminating */ 1541b45e7f98SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 1542b45e7f98SKarsten Graul goto out; 1543b45e7f98SKarsten Graul } 1544b45e7f98SKarsten Graul 1545b1570a87SKarsten Graul if (lgr->role == SMC_CLNT) 1546b1570a87SKarsten Graul smc_llc_process_cli_add_link(lgr); 15472d2209f2SKarsten Graul else 15482d2209f2SKarsten Graul smc_llc_process_srv_add_link(lgr); 1549b45e7f98SKarsten Graul out: 1550b4ba4652SKarsten Graul if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_REQ_ADD_LINK) 1551b45e7f98SKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 1552b45e7f98SKarsten Graul } 1553b45e7f98SKarsten Graul 15544dadd151SKarsten Graul /* enqueue a local del_link msg to trigger a new del_link flow, 15554dadd151SKarsten Graul * called only for role SMC_SERV 15564dadd151SKarsten Graul */ 15574dadd151SKarsten Graul void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id) 15584dadd151SKarsten Graul { 155916cb3653SPujin Shi struct smc_llc_msg_del_link del_llc = {}; 15604dadd151SKarsten Graul 1561b4ba4652SKarsten Graul del_llc.hd.common.llc_type = SMC_LLC_DELETE_LINK; 1562b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&del_llc.hd, link->lgr, sizeof(del_llc)); 15634dadd151SKarsten Graul del_llc.link_num = del_link_id; 15644dadd151SKarsten Graul del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH); 15654dadd151SKarsten Graul del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 15664dadd151SKarsten Graul smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc); 15674dadd151SKarsten Graul } 15684dadd151SKarsten Graul 15699c416878SKarsten Graul static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr) 15709c416878SKarsten Graul { 15719c416878SKarsten Graul struct smc_link *lnk_del = NULL, *lnk_asym, *lnk; 15729c416878SKarsten Graul struct smc_llc_msg_del_link *del_llc; 15739c416878SKarsten Graul struct smc_llc_qentry *qentry; 15749c416878SKarsten Graul int active_links; 15759c416878SKarsten Graul int lnk_idx; 15769c416878SKarsten Graul 15779c416878SKarsten Graul qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 15789c416878SKarsten Graul lnk = qentry->link; 15799c416878SKarsten Graul del_llc = &qentry->msg.delete_link; 15809c416878SKarsten Graul 15819c416878SKarsten Graul if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { 15829c416878SKarsten Graul smc_lgr_terminate_sched(lgr); 15839c416878SKarsten Graul goto out; 15849c416878SKarsten Graul } 1585*b5dd4d69SD. Wythe down_write(&lgr->llc_conf_mutex); 15869c416878SKarsten Graul /* delete single link */ 15879c416878SKarsten Graul for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) { 15889c416878SKarsten Graul if (lgr->lnk[lnk_idx].link_id != del_llc->link_num) 15899c416878SKarsten Graul continue; 15909c416878SKarsten Graul lnk_del = &lgr->lnk[lnk_idx]; 15919c416878SKarsten Graul break; 15929c416878SKarsten Graul } 15939c416878SKarsten Graul del_llc->hd.flags |= SMC_LLC_FLAG_RESP; 15949c416878SKarsten Graul if (!lnk_del) { 15959c416878SKarsten Graul /* link was not found */ 15969c416878SKarsten Graul del_llc->reason = htonl(SMC_LLC_DEL_NOLNK); 15979c416878SKarsten Graul smc_llc_send_message(lnk, &qentry->msg); 15989c416878SKarsten Graul goto out_unlock; 15999c416878SKarsten Graul } 16009c416878SKarsten Graul lnk_asym = smc_llc_find_asym_link(lgr); 16019c416878SKarsten Graul 16029c416878SKarsten Graul del_llc->reason = 0; 16039c416878SKarsten Graul smc_llc_send_message(lnk, &qentry->msg); /* response */ 16049c416878SKarsten Graul 16058f3d65c1SKarsten Graul if (smc_link_downing(&lnk_del->state)) 16068f3d65c1SKarsten Graul smc_switch_conns(lgr, lnk_del, false); 16070a99be43SKarsten Graul smcr_link_clear(lnk_del, true); 16089c416878SKarsten Graul 16099c416878SKarsten Graul active_links = smc_llc_active_link_count(lgr); 16109c416878SKarsten Graul if (lnk_del == lnk_asym) { 16119c416878SKarsten Graul /* expected deletion of asym link, don't change lgr state */ 16129c416878SKarsten Graul } else if (active_links == 1) { 1613ad6c111bSKarsten Graul smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); 16149c416878SKarsten Graul } else if (!active_links) { 1615ad6c111bSKarsten Graul smcr_lgr_set_type(lgr, SMC_LGR_NONE); 16169c416878SKarsten Graul smc_lgr_terminate_sched(lgr); 16179c416878SKarsten Graul } 16189c416878SKarsten Graul out_unlock: 1619*b5dd4d69SD. Wythe up_write(&lgr->llc_conf_mutex); 16209c416878SKarsten Graul out: 16219c416878SKarsten Graul kfree(qentry); 16229c416878SKarsten Graul } 16239c416878SKarsten Graul 1624f3811fd7SKarsten Graul /* try to send a DELETE LINK ALL request on any active link, 1625f3811fd7SKarsten Graul * waiting for send completion 1626f3811fd7SKarsten Graul */ 1627f3811fd7SKarsten Graul void smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord, u32 rsn) 1628f3811fd7SKarsten Graul { 16297e94e46cSPujin Shi struct smc_llc_msg_del_link delllc = {}; 1630f3811fd7SKarsten Graul int i; 1631f3811fd7SKarsten Graul 1632b4ba4652SKarsten Graul delllc.hd.common.llc_type = SMC_LLC_DELETE_LINK; 1633b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&delllc.hd, lgr, sizeof(delllc)); 1634f3811fd7SKarsten Graul if (ord) 1635f3811fd7SKarsten Graul delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 1636f3811fd7SKarsten Graul delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 1637f3811fd7SKarsten Graul delllc.reason = htonl(rsn); 1638f3811fd7SKarsten Graul 1639f3811fd7SKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 164090cee52fSDust Li if (!smc_link_sendable(&lgr->lnk[i])) 1641f3811fd7SKarsten Graul continue; 1642f3811fd7SKarsten Graul if (!smc_llc_send_message_wait(&lgr->lnk[i], &delllc)) 1643f3811fd7SKarsten Graul break; 1644f3811fd7SKarsten Graul } 1645f3811fd7SKarsten Graul } 1646f3811fd7SKarsten Graul 164708ae27ddSKarsten Graul static void smc_llc_process_srv_delete_link(struct smc_link_group *lgr) 164808ae27ddSKarsten Graul { 164908ae27ddSKarsten Graul struct smc_llc_msg_del_link *del_llc; 165008ae27ddSKarsten Graul struct smc_link *lnk, *lnk_del; 165108ae27ddSKarsten Graul struct smc_llc_qentry *qentry; 165208ae27ddSKarsten Graul int active_links; 165308ae27ddSKarsten Graul int i; 165408ae27ddSKarsten Graul 1655*b5dd4d69SD. Wythe down_write(&lgr->llc_conf_mutex); 165608ae27ddSKarsten Graul qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 165708ae27ddSKarsten Graul lnk = qentry->link; 165808ae27ddSKarsten Graul del_llc = &qentry->msg.delete_link; 165908ae27ddSKarsten Graul 166008ae27ddSKarsten Graul if (qentry->msg.delete_link.hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { 166108ae27ddSKarsten Graul /* delete entire lgr */ 1662f3811fd7SKarsten Graul smc_llc_send_link_delete_all(lgr, true, ntohl( 1663f3811fd7SKarsten Graul qentry->msg.delete_link.reason)); 166408ae27ddSKarsten Graul smc_lgr_terminate_sched(lgr); 166508ae27ddSKarsten Graul goto out; 166608ae27ddSKarsten Graul } 166708ae27ddSKarsten Graul /* delete single link */ 166808ae27ddSKarsten Graul lnk_del = NULL; 166908ae27ddSKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 167008ae27ddSKarsten Graul if (lgr->lnk[i].link_id == del_llc->link_num) { 167108ae27ddSKarsten Graul lnk_del = &lgr->lnk[i]; 167208ae27ddSKarsten Graul break; 167308ae27ddSKarsten Graul } 167408ae27ddSKarsten Graul } 167508ae27ddSKarsten Graul if (!lnk_del) 167608ae27ddSKarsten Graul goto out; /* asymmetric link already deleted */ 167708ae27ddSKarsten Graul 167808ae27ddSKarsten Graul if (smc_link_downing(&lnk_del->state)) { 1679b7eede75SKarsten Graul if (smc_switch_conns(lgr, lnk_del, false)) 168008ae27ddSKarsten Graul smc_wr_tx_wait_no_pending_sends(lnk_del); 168108ae27ddSKarsten Graul } 168208ae27ddSKarsten Graul if (!list_empty(&lgr->list)) { 168308ae27ddSKarsten Graul /* qentry is either a request from peer (send it back to 168408ae27ddSKarsten Graul * initiate the DELETE_LINK processing), or a locally 168508ae27ddSKarsten Graul * enqueued DELETE_LINK request (forward it) 168608ae27ddSKarsten Graul */ 168708ae27ddSKarsten Graul if (!smc_llc_send_message(lnk, &qentry->msg)) { 168808ae27ddSKarsten Graul struct smc_llc_qentry *qentry2; 168908ae27ddSKarsten Graul 169008ae27ddSKarsten Graul qentry2 = smc_llc_wait(lgr, lnk, SMC_LLC_WAIT_TIME, 169108ae27ddSKarsten Graul SMC_LLC_DELETE_LINK); 1692ca7e3edcSYueHaibing if (qentry2) 169308ae27ddSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 169408ae27ddSKarsten Graul } 169508ae27ddSKarsten Graul } 16960a99be43SKarsten Graul smcr_link_clear(lnk_del, true); 169708ae27ddSKarsten Graul 169808ae27ddSKarsten Graul active_links = smc_llc_active_link_count(lgr); 169908ae27ddSKarsten Graul if (active_links == 1) { 1700ad6c111bSKarsten Graul smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); 170108ae27ddSKarsten Graul } else if (!active_links) { 1702ad6c111bSKarsten Graul smcr_lgr_set_type(lgr, SMC_LGR_NONE); 170308ae27ddSKarsten Graul smc_lgr_terminate_sched(lgr); 170408ae27ddSKarsten Graul } 170508ae27ddSKarsten Graul 170608ae27ddSKarsten Graul if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) { 170708ae27ddSKarsten Graul /* trigger setup of asymm alt link */ 1708c48254faSKarsten Graul smc_llc_add_link_local(lnk); 170908ae27ddSKarsten Graul } 171008ae27ddSKarsten Graul out: 1711*b5dd4d69SD. Wythe up_write(&lgr->llc_conf_mutex); 171208ae27ddSKarsten Graul kfree(qentry); 171308ae27ddSKarsten Graul } 171408ae27ddSKarsten Graul 17159ec6bf19SKarsten Graul static void smc_llc_delete_link_work(struct work_struct *work) 171652bedf37SKarsten Graul { 17179ec6bf19SKarsten Graul struct smc_link_group *lgr = container_of(work, struct smc_link_group, 17189ec6bf19SKarsten Graul llc_del_link_work); 171952bedf37SKarsten Graul 17209ec6bf19SKarsten Graul if (list_empty(&lgr->list)) { 17219ec6bf19SKarsten Graul /* link group is terminating */ 17229ec6bf19SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 17239ec6bf19SKarsten Graul goto out; 172452bedf37SKarsten Graul } 17259c416878SKarsten Graul 17269c416878SKarsten Graul if (lgr->role == SMC_CLNT) 17279c416878SKarsten Graul smc_llc_process_cli_delete_link(lgr); 172808ae27ddSKarsten Graul else 172908ae27ddSKarsten Graul smc_llc_process_srv_delete_link(lgr); 17309ec6bf19SKarsten Graul out: 17319ec6bf19SKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 173252bedf37SKarsten Graul } 173352bedf37SKarsten Graul 17343bc67e09SKarsten Graul /* process a confirm_rkey request from peer, remote flow */ 17353bc67e09SKarsten Graul static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr) 17364ed75de5SKarsten Graul { 17373bc67e09SKarsten Graul struct smc_llc_msg_confirm_rkey *llc; 17383bc67e09SKarsten Graul struct smc_llc_qentry *qentry; 17393bc67e09SKarsten Graul struct smc_link *link; 17403bc67e09SKarsten Graul int num_entries; 17413bc67e09SKarsten Graul int rk_idx; 17423bc67e09SKarsten Graul int i; 17434ed75de5SKarsten Graul 17443bc67e09SKarsten Graul qentry = lgr->llc_flow_rmt.qentry; 17453bc67e09SKarsten Graul llc = &qentry->msg.confirm_rkey; 17463bc67e09SKarsten Graul link = qentry->link; 17473bc67e09SKarsten Graul 17483bc67e09SKarsten Graul num_entries = llc->rtoken[0].num_rkeys; 1749b4ba4652SKarsten Graul if (num_entries > SMC_LLC_RKEYS_PER_MSG) 1750b4ba4652SKarsten Graul goto out_err; 17513bc67e09SKarsten Graul /* first rkey entry is for receiving link */ 17523bc67e09SKarsten Graul rk_idx = smc_rtoken_add(link, 17534ed75de5SKarsten Graul llc->rtoken[0].rmb_vaddr, 17544ed75de5SKarsten Graul llc->rtoken[0].rmb_key); 17553bc67e09SKarsten Graul if (rk_idx < 0) 17563bc67e09SKarsten Graul goto out_err; 17574ed75de5SKarsten Graul 17583bc67e09SKarsten Graul for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++) 17593bc67e09SKarsten Graul smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id, 17603bc67e09SKarsten Graul llc->rtoken[i].rmb_vaddr, 17613bc67e09SKarsten Graul llc->rtoken[i].rmb_key); 17623bc67e09SKarsten Graul /* max links is 3 so there is no need to support conf_rkey_cont msgs */ 17633bc67e09SKarsten Graul goto out; 17643bc67e09SKarsten Graul out_err: 17654ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 17663bc67e09SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY; 17673bc67e09SKarsten Graul out: 17683bc67e09SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 1769b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc)); 17703bc67e09SKarsten Graul smc_llc_send_message(link, &qentry->msg); 17713bc67e09SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 17724ed75de5SKarsten Graul } 17734ed75de5SKarsten Graul 1774218b24feSKarsten Graul /* process a delete_rkey request from peer, remote flow */ 1775218b24feSKarsten Graul static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr) 17764ed75de5SKarsten Graul { 1777218b24feSKarsten Graul struct smc_llc_msg_delete_rkey *llc; 1778218b24feSKarsten Graul struct smc_llc_qentry *qentry; 1779218b24feSKarsten Graul struct smc_link *link; 17804ed75de5SKarsten Graul u8 err_mask = 0; 17814ed75de5SKarsten Graul int i, max; 17824ed75de5SKarsten Graul 1783218b24feSKarsten Graul qentry = lgr->llc_flow_rmt.qentry; 1784218b24feSKarsten Graul llc = &qentry->msg.delete_rkey; 1785218b24feSKarsten Graul link = qentry->link; 1786218b24feSKarsten Graul 1787b4ba4652SKarsten Graul if (lgr->smc_version == SMC_V2) { 1788b4ba4652SKarsten Graul struct smc_llc_msg_delete_rkey_v2 *llcv2; 1789b4ba4652SKarsten Graul 1790b4ba4652SKarsten Graul memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc)); 1791b4ba4652SKarsten Graul llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2; 1792b4ba4652SKarsten Graul llcv2->num_inval_rkeys = 0; 1793b4ba4652SKarsten Graul 1794b4ba4652SKarsten Graul max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); 1795b4ba4652SKarsten Graul for (i = 0; i < max; i++) { 1796b4ba4652SKarsten Graul if (smc_rtoken_delete(link, llcv2->rkey[i])) 1797b4ba4652SKarsten Graul llcv2->num_inval_rkeys++; 1798b4ba4652SKarsten Graul } 1799b4ba4652SKarsten Graul memset(&llc->rkey[0], 0, sizeof(llc->rkey)); 1800b4ba4652SKarsten Graul memset(&llc->reserved2, 0, sizeof(llc->reserved2)); 1801b4ba4652SKarsten Graul smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc)); 1802b4ba4652SKarsten Graul if (llcv2->num_inval_rkeys) { 1803b4ba4652SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 1804b4ba4652SKarsten Graul llc->err_mask = llcv2->num_inval_rkeys; 1805b4ba4652SKarsten Graul } 1806b4ba4652SKarsten Graul goto finish; 1807b4ba4652SKarsten Graul } 1808b4ba4652SKarsten Graul 18094ed75de5SKarsten Graul max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX); 18104ed75de5SKarsten Graul for (i = 0; i < max; i++) { 1811387707fdSKarsten Graul if (smc_rtoken_delete(link, llc->rkey[i])) 18124ed75de5SKarsten Graul err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i); 18134ed75de5SKarsten Graul } 18144ed75de5SKarsten Graul if (err_mask) { 18154ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 18164ed75de5SKarsten Graul llc->err_mask = err_mask; 18174ed75de5SKarsten Graul } 1818b4ba4652SKarsten Graul finish: 1819218b24feSKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 1820218b24feSKarsten Graul smc_llc_send_message(link, &qentry->msg); 1821218b24feSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 1822218b24feSKarsten Graul } 18234ed75de5SKarsten Graul 18243e0c40afSKarsten Graul static void smc_llc_protocol_violation(struct smc_link_group *lgr, u8 type) 18253e0c40afSKarsten Graul { 1826de2fea7bSTony Lu pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu LLC protocol violation: " 1827de2fea7bSTony Lu "llc_type %d\n", SMC_LGR_ID_SIZE, &lgr->id, 1828de2fea7bSTony Lu lgr->net->net_cookie, type); 18293e0c40afSKarsten Graul smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_PROT_VIOL); 18303e0c40afSKarsten Graul smc_lgr_terminate_sched(lgr); 18313e0c40afSKarsten Graul } 18323e0c40afSKarsten Graul 18336c8968c4SKarsten Graul /* flush the llc event queue */ 183400a049cfSKarsten Graul static void smc_llc_event_flush(struct smc_link_group *lgr) 18359bf9abeaSUrsula Braun { 18366c8968c4SKarsten Graul struct smc_llc_qentry *qentry, *q; 18379bf9abeaSUrsula Braun 18386c8968c4SKarsten Graul spin_lock_bh(&lgr->llc_event_q_lock); 18396c8968c4SKarsten Graul list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) { 18406c8968c4SKarsten Graul list_del_init(&qentry->list); 18416c8968c4SKarsten Graul kfree(qentry); 18426c8968c4SKarsten Graul } 18436c8968c4SKarsten Graul spin_unlock_bh(&lgr->llc_event_q_lock); 18446c8968c4SKarsten Graul } 18456c8968c4SKarsten Graul 18466c8968c4SKarsten Graul static void smc_llc_event_handler(struct smc_llc_qentry *qentry) 18476c8968c4SKarsten Graul { 18486c8968c4SKarsten Graul union smc_llc_msg *llc = &qentry->msg; 18496c8968c4SKarsten Graul struct smc_link *link = qentry->link; 18500fb0b02bSKarsten Graul struct smc_link_group *lgr = link->lgr; 18516c8968c4SKarsten Graul 1852d854fcbfSKarsten Graul if (!smc_link_usable(link)) 18536c8968c4SKarsten Graul goto out; 1854313164daSKarsten Graul 1855b4ba4652SKarsten Graul switch (llc->raw.hdr.common.llc_type) { 1856313164daSKarsten Graul case SMC_LLC_TEST_LINK: 185756e8091cSKarsten Graul llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP; 185856e8091cSKarsten Graul smc_llc_send_message(link, llc); 1859313164daSKarsten Graul break; 186052bedf37SKarsten Graul case SMC_LLC_ADD_LINK: 18610fb0b02bSKarsten Graul if (list_empty(&lgr->list)) 18620fb0b02bSKarsten Graul goto out; /* lgr is terminating */ 18630fb0b02bSKarsten Graul if (lgr->role == SMC_CLNT) { 1864c48254faSKarsten Graul if (smc_llc_is_local_add_link(llc)) { 1865c48254faSKarsten Graul if (lgr->llc_flow_lcl.type == 1866c48254faSKarsten Graul SMC_LLC_FLOW_ADD_LINK) 1867c48254faSKarsten Graul break; /* add_link in progress */ 1868c48254faSKarsten Graul if (smc_llc_flow_start(&lgr->llc_flow_lcl, 1869c48254faSKarsten Graul qentry)) { 1870c48254faSKarsten Graul schedule_work(&lgr->llc_add_link_work); 1871c48254faSKarsten Graul } 1872c48254faSKarsten Graul return; 1873c48254faSKarsten Graul } 1874c48254faSKarsten Graul if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && 1875c48254faSKarsten Graul !lgr->llc_flow_lcl.qentry) { 18760fb0b02bSKarsten Graul /* a flow is waiting for this message */ 18770fb0b02bSKarsten Graul smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, 18780fb0b02bSKarsten Graul qentry); 18796778a6beSKarsten Graul wake_up(&lgr->llc_msg_waiter); 1880b4ba4652SKarsten Graul return; 1881b4ba4652SKarsten Graul } 1882b4ba4652SKarsten Graul if (lgr->llc_flow_lcl.type == 1883b4ba4652SKarsten Graul SMC_LLC_FLOW_REQ_ADD_LINK) { 1884b4ba4652SKarsten Graul /* server started add_link processing */ 1885b4ba4652SKarsten Graul lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK; 1886b4ba4652SKarsten Graul smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, 1887b4ba4652SKarsten Graul qentry); 1888b4ba4652SKarsten Graul schedule_work(&lgr->llc_add_link_work); 1889b4ba4652SKarsten Graul return; 1890b4ba4652SKarsten Graul } 1891b4ba4652SKarsten Graul if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 1892b45e7f98SKarsten Graul schedule_work(&lgr->llc_add_link_work); 18930fb0b02bSKarsten Graul } 18940fb0b02bSKarsten Graul } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 18950fb0b02bSKarsten Graul /* as smc server, handle client suggestion */ 1896b45e7f98SKarsten Graul schedule_work(&lgr->llc_add_link_work); 18970fb0b02bSKarsten Graul } 18980fb0b02bSKarsten Graul return; 18990fb0b02bSKarsten Graul case SMC_LLC_CONFIRM_LINK: 190087f88cdaSKarsten Graul case SMC_LLC_ADD_LINK_CONT: 19010fb0b02bSKarsten Graul if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { 19020fb0b02bSKarsten Graul /* a flow is waiting for this message */ 19030fb0b02bSKarsten Graul smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); 19046778a6beSKarsten Graul wake_up(&lgr->llc_msg_waiter); 19050fb0b02bSKarsten Graul return; 19060fb0b02bSKarsten Graul } 190752bedf37SKarsten Graul break; 190852bedf37SKarsten Graul case SMC_LLC_DELETE_LINK: 19099ec6bf19SKarsten Graul if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && 19109ec6bf19SKarsten Graul !lgr->llc_flow_lcl.qentry) { 19119ec6bf19SKarsten Graul /* DEL LINK REQ during ADD LINK SEQ */ 1912b9979c2eSKarsten Graul smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); 19136778a6beSKarsten Graul wake_up(&lgr->llc_msg_waiter); 1914b9979c2eSKarsten Graul } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 19159ec6bf19SKarsten Graul schedule_work(&lgr->llc_del_link_work); 19169ec6bf19SKarsten Graul } 19179ec6bf19SKarsten Graul return; 19184ed75de5SKarsten Graul case SMC_LLC_CONFIRM_RKEY: 19193bc67e09SKarsten Graul /* new request from remote, assign to remote flow */ 19203bc67e09SKarsten Graul if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 19213bc67e09SKarsten Graul /* process here, does not wait for more llc msgs */ 19223bc67e09SKarsten Graul smc_llc_rmt_conf_rkey(lgr); 19233bc67e09SKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 19243bc67e09SKarsten Graul } 19253bc67e09SKarsten Graul return; 19264ed75de5SKarsten Graul case SMC_LLC_CONFIRM_RKEY_CONT: 192742d18accSKarsten Graul /* not used because max links is 3, and 3 rkeys fit into 192842d18accSKarsten Graul * one CONFIRM_RKEY message 192942d18accSKarsten Graul */ 19304ed75de5SKarsten Graul break; 19314ed75de5SKarsten Graul case SMC_LLC_DELETE_RKEY: 1932218b24feSKarsten Graul /* new request from remote, assign to remote flow */ 1933218b24feSKarsten Graul if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 1934218b24feSKarsten Graul /* process here, does not wait for more llc msgs */ 1935218b24feSKarsten Graul smc_llc_rmt_delete_rkey(lgr); 1936218b24feSKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 1937218b24feSKarsten Graul } 1938218b24feSKarsten Graul return; 1939b4ba4652SKarsten Graul case SMC_LLC_REQ_ADD_LINK: 1940b4ba4652SKarsten Graul /* handle response here, smc_llc_flow_stop() cannot be called 1941b4ba4652SKarsten Graul * in tasklet context 1942b4ba4652SKarsten Graul */ 1943b4ba4652SKarsten Graul if (lgr->role == SMC_CLNT && 1944b4ba4652SKarsten Graul lgr->llc_flow_lcl.type == SMC_LLC_FLOW_REQ_ADD_LINK && 1945b4ba4652SKarsten Graul (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP)) { 1946b4ba4652SKarsten Graul smc_llc_flow_stop(link->lgr, &lgr->llc_flow_lcl); 1947b4ba4652SKarsten Graul } else if (lgr->role == SMC_SERV) { 1948b4ba4652SKarsten Graul if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 1949b4ba4652SKarsten Graul /* as smc server, handle client suggestion */ 1950b4ba4652SKarsten Graul lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK; 1951b4ba4652SKarsten Graul schedule_work(&lgr->llc_add_link_work); 1952b4ba4652SKarsten Graul } 1953b4ba4652SKarsten Graul return; 1954b4ba4652SKarsten Graul } 1955b4ba4652SKarsten Graul break; 19563e0c40afSKarsten Graul default: 19573e0c40afSKarsten Graul smc_llc_protocol_violation(lgr, llc->raw.hdr.common.type); 19583e0c40afSKarsten Graul break; 1959313164daSKarsten Graul } 19606c8968c4SKarsten Graul out: 19616c8968c4SKarsten Graul kfree(qentry); 19626c8968c4SKarsten Graul } 19636c8968c4SKarsten Graul 19646c8968c4SKarsten Graul /* worker to process llc messages on the event queue */ 19656c8968c4SKarsten Graul static void smc_llc_event_work(struct work_struct *work) 19666c8968c4SKarsten Graul { 19676c8968c4SKarsten Graul struct smc_link_group *lgr = container_of(work, struct smc_link_group, 19686c8968c4SKarsten Graul llc_event_work); 19696c8968c4SKarsten Graul struct smc_llc_qentry *qentry; 19706c8968c4SKarsten Graul 1971555da9afSKarsten Graul if (!lgr->llc_flow_lcl.type && lgr->delayed_event) { 1972555da9afSKarsten Graul qentry = lgr->delayed_event; 1973555da9afSKarsten Graul lgr->delayed_event = NULL; 1974d535ca13SKarsten Graul if (smc_link_usable(qentry->link)) 1975d535ca13SKarsten Graul smc_llc_event_handler(qentry); 1976d535ca13SKarsten Graul else 1977555da9afSKarsten Graul kfree(qentry); 1978555da9afSKarsten Graul } 1979555da9afSKarsten Graul 19806c8968c4SKarsten Graul again: 19816c8968c4SKarsten Graul spin_lock_bh(&lgr->llc_event_q_lock); 19826c8968c4SKarsten Graul if (!list_empty(&lgr->llc_event_q)) { 19836c8968c4SKarsten Graul qentry = list_first_entry(&lgr->llc_event_q, 19846c8968c4SKarsten Graul struct smc_llc_qentry, list); 19856c8968c4SKarsten Graul list_del_init(&qentry->list); 19866c8968c4SKarsten Graul spin_unlock_bh(&lgr->llc_event_q_lock); 19876c8968c4SKarsten Graul smc_llc_event_handler(qentry); 19886c8968c4SKarsten Graul goto again; 19896c8968c4SKarsten Graul } 19906c8968c4SKarsten Graul spin_unlock_bh(&lgr->llc_event_q_lock); 19916c8968c4SKarsten Graul } 19926c8968c4SKarsten Graul 1993ef79d439SKarsten Graul /* process llc responses in tasklet context */ 1994a6688d91SKarsten Graul static void smc_llc_rx_response(struct smc_link *link, 1995a6688d91SKarsten Graul struct smc_llc_qentry *qentry) 1996ef79d439SKarsten Graul { 19972ff08678SKarsten Graul enum smc_llc_flowtype flowtype = link->lgr->llc_flow_lcl.type; 19982ff08678SKarsten Graul struct smc_llc_flow *flow = &link->lgr->llc_flow_lcl; 1999b4ba4652SKarsten Graul u8 llc_type = qentry->msg.raw.hdr.common.llc_type; 2000ef79d439SKarsten Graul 2001a6688d91SKarsten Graul switch (llc_type) { 2002ef79d439SKarsten Graul case SMC_LLC_TEST_LINK: 2003741a49a4SKarsten Graul if (smc_link_active(link)) 2004ef79d439SKarsten Graul complete(&link->llc_testlink_resp); 2005ef79d439SKarsten Graul break; 2006ef79d439SKarsten Graul case SMC_LLC_ADD_LINK: 200787f88cdaSKarsten Graul case SMC_LLC_ADD_LINK_CONT: 20082ff08678SKarsten Graul case SMC_LLC_CONFIRM_LINK: 20092ff08678SKarsten Graul if (flowtype != SMC_LLC_FLOW_ADD_LINK || flow->qentry) 20102ff08678SKarsten Graul break; /* drop out-of-flow response */ 20112ff08678SKarsten Graul goto assign; 20122ff08678SKarsten Graul case SMC_LLC_DELETE_LINK: 20132ff08678SKarsten Graul if (flowtype != SMC_LLC_FLOW_DEL_LINK || flow->qentry) 20142ff08678SKarsten Graul break; /* drop out-of-flow response */ 20152ff08678SKarsten Graul goto assign; 20163d88a21bSKarsten Graul case SMC_LLC_CONFIRM_RKEY: 20176d74c3a8SKarsten Graul case SMC_LLC_DELETE_RKEY: 20182ff08678SKarsten Graul if (flowtype != SMC_LLC_FLOW_RKEY || flow->qentry) 20192ff08678SKarsten Graul break; /* drop out-of-flow response */ 20202ff08678SKarsten Graul goto assign; 2021ef79d439SKarsten Graul case SMC_LLC_CONFIRM_RKEY_CONT: 202242d18accSKarsten Graul /* not used because max links is 3 */ 2023ef79d439SKarsten Graul break; 20243e0c40afSKarsten Graul default: 2025b4ba4652SKarsten Graul smc_llc_protocol_violation(link->lgr, 2026b4ba4652SKarsten Graul qentry->msg.raw.hdr.common.type); 20273e0c40afSKarsten Graul break; 2028ef79d439SKarsten Graul } 2029a6688d91SKarsten Graul kfree(qentry); 20302ff08678SKarsten Graul return; 20312ff08678SKarsten Graul assign: 20322ff08678SKarsten Graul /* assign responses to the local flow, we requested them */ 20332ff08678SKarsten Graul smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry); 20342ff08678SKarsten Graul wake_up(&link->lgr->llc_msg_waiter); 2035ef79d439SKarsten Graul } 2036ef79d439SKarsten Graul 2037a6688d91SKarsten Graul static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc) 20386c8968c4SKarsten Graul { 20396c8968c4SKarsten Graul struct smc_link_group *lgr = link->lgr; 20406c8968c4SKarsten Graul struct smc_llc_qentry *qentry; 20416c8968c4SKarsten Graul unsigned long flags; 20426c8968c4SKarsten Graul 20436c8968c4SKarsten Graul qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC); 20446c8968c4SKarsten Graul if (!qentry) 20456c8968c4SKarsten Graul return; 20466c8968c4SKarsten Graul qentry->link = link; 20476c8968c4SKarsten Graul INIT_LIST_HEAD(&qentry->list); 20486c8968c4SKarsten Graul memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg)); 2049a6688d91SKarsten Graul 2050a6688d91SKarsten Graul /* process responses immediately */ 2051b4ba4652SKarsten Graul if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) && 2052b4ba4652SKarsten Graul llc->raw.hdr.common.llc_type != SMC_LLC_REQ_ADD_LINK) { 2053a6688d91SKarsten Graul smc_llc_rx_response(link, qentry); 2054a6688d91SKarsten Graul return; 2055a6688d91SKarsten Graul } 2056a6688d91SKarsten Graul 2057a6688d91SKarsten Graul /* add requests to event queue */ 20586c8968c4SKarsten Graul spin_lock_irqsave(&lgr->llc_event_q_lock, flags); 20596c8968c4SKarsten Graul list_add_tail(&qentry->list, &lgr->llc_event_q); 20606c8968c4SKarsten Graul spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags); 206122ef473dSKarsten Graul queue_work(system_highpri_wq, &lgr->llc_event_work); 20629bf9abeaSUrsula Braun } 20639bf9abeaSUrsula Braun 2064a6688d91SKarsten Graul /* copy received msg and add it to the event queue */ 2065a6688d91SKarsten Graul static void smc_llc_rx_handler(struct ib_wc *wc, void *buf) 2066a6688d91SKarsten Graul { 2067a6688d91SKarsten Graul struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 2068a6688d91SKarsten Graul union smc_llc_msg *llc = buf; 2069a6688d91SKarsten Graul 2070a6688d91SKarsten Graul if (wc->byte_len < sizeof(*llc)) 2071a6688d91SKarsten Graul return; /* short message */ 2072b4ba4652SKarsten Graul if (!llc->raw.hdr.common.llc_version) { 2073a6688d91SKarsten Graul if (llc->raw.hdr.length != sizeof(*llc)) 2074a6688d91SKarsten Graul return; /* invalid message */ 2075b4ba4652SKarsten Graul } else { 2076b4ba4652SKarsten Graul if (llc->raw.hdr.length_v2 < sizeof(*llc)) 2077b4ba4652SKarsten Graul return; /* invalid message */ 2078b4ba4652SKarsten Graul } 2079a6688d91SKarsten Graul 2080a6688d91SKarsten Graul smc_llc_enqueue(link, llc); 2081a6688d91SKarsten Graul } 2082a6688d91SKarsten Graul 208344aa81ceSKarsten Graul /***************************** worker, utils *********************************/ 2084877ae5beSKarsten Graul 2085877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work) 2086877ae5beSKarsten Graul { 2087877ae5beSKarsten Graul struct smc_link *link = container_of(to_delayed_work(work), 2088877ae5beSKarsten Graul struct smc_link, llc_testlink_wrk); 2089877ae5beSKarsten Graul unsigned long next_interval; 2090877ae5beSKarsten Graul unsigned long expire_time; 2091877ae5beSKarsten Graul u8 user_data[16] = { 0 }; 2092877ae5beSKarsten Graul int rc; 2093877ae5beSKarsten Graul 2094741a49a4SKarsten Graul if (!smc_link_active(link)) 2095877ae5beSKarsten Graul return; /* don't reschedule worker */ 2096877ae5beSKarsten Graul expire_time = link->wr_rx_tstamp + link->llc_testlink_time; 2097877ae5beSKarsten Graul if (time_is_after_jiffies(expire_time)) { 2098877ae5beSKarsten Graul next_interval = expire_time - jiffies; 2099877ae5beSKarsten Graul goto out; 2100877ae5beSKarsten Graul } 2101877ae5beSKarsten Graul reinit_completion(&link->llc_testlink_resp); 2102d97935faSKarsten Graul smc_llc_send_test_link(link, user_data); 2103877ae5beSKarsten Graul /* receive TEST LINK response over RoCE fabric */ 2104877ae5beSKarsten Graul rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp, 2105877ae5beSKarsten Graul SMC_LLC_WAIT_TIME); 2106741a49a4SKarsten Graul if (!smc_link_active(link)) 21071020e1efSKarsten Graul return; /* link state changed */ 2108877ae5beSKarsten Graul if (rc <= 0) { 210987523930SKarsten Graul smcr_link_down_cond_sched(link); 2110877ae5beSKarsten Graul return; 2111877ae5beSKarsten Graul } 2112877ae5beSKarsten Graul next_interval = link->llc_testlink_time; 2113877ae5beSKarsten Graul out: 21141020e1efSKarsten Graul schedule_delayed_work(&link->llc_testlink_wrk, next_interval); 2115877ae5beSKarsten Graul } 2116877ae5beSKarsten Graul 211700a049cfSKarsten Graul void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc) 211800a049cfSKarsten Graul { 211900a049cfSKarsten Graul struct net *net = sock_net(smc->clcsock->sk); 212000a049cfSKarsten Graul 212100a049cfSKarsten Graul INIT_WORK(&lgr->llc_event_work, smc_llc_event_work); 2122b45e7f98SKarsten Graul INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work); 21239ec6bf19SKarsten Graul INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work); 212400a049cfSKarsten Graul INIT_LIST_HEAD(&lgr->llc_event_q); 212500a049cfSKarsten Graul spin_lock_init(&lgr->llc_event_q_lock); 2126555da9afSKarsten Graul spin_lock_init(&lgr->llc_flow_lock); 21276778a6beSKarsten Graul init_waitqueue_head(&lgr->llc_flow_waiter); 21286778a6beSKarsten Graul init_waitqueue_head(&lgr->llc_msg_waiter); 2129*b5dd4d69SD. Wythe init_rwsem(&lgr->llc_conf_mutex); 213077eee325SWen Gu lgr->llc_testlink_time = READ_ONCE(net->smc.sysctl_smcr_testlink_time); 213100a049cfSKarsten Graul } 213200a049cfSKarsten Graul 213300a049cfSKarsten Graul /* called after lgr was removed from lgr_list */ 213400a049cfSKarsten Graul void smc_llc_lgr_clear(struct smc_link_group *lgr) 213500a049cfSKarsten Graul { 213600a049cfSKarsten Graul smc_llc_event_flush(lgr); 21376778a6beSKarsten Graul wake_up_all(&lgr->llc_flow_waiter); 21386778a6beSKarsten Graul wake_up_all(&lgr->llc_msg_waiter); 213900a049cfSKarsten Graul cancel_work_sync(&lgr->llc_event_work); 2140b45e7f98SKarsten Graul cancel_work_sync(&lgr->llc_add_link_work); 21419ec6bf19SKarsten Graul cancel_work_sync(&lgr->llc_del_link_work); 2142555da9afSKarsten Graul if (lgr->delayed_event) { 2143555da9afSKarsten Graul kfree(lgr->delayed_event); 2144555da9afSKarsten Graul lgr->delayed_event = NULL; 2145555da9afSKarsten Graul } 214600a049cfSKarsten Graul } 214700a049cfSKarsten Graul 21482a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link) 2149877ae5beSKarsten Graul { 2150877ae5beSKarsten Graul init_completion(&link->llc_testlink_resp); 2151877ae5beSKarsten Graul INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); 21522a4c57a9SKarsten Graul return 0; 2153b32cf4abSKarsten Graul } 2154b32cf4abSKarsten Graul 215500a049cfSKarsten Graul void smc_llc_link_active(struct smc_link *link) 2156b32cf4abSKarsten Graul { 2157de2fea7bSTony Lu pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link added: id %*phN, " 21580a99be43SKarsten Graul "peerid %*phN, ibdev %s, ibport %d\n", 21590a99be43SKarsten Graul SMC_LGR_ID_SIZE, &link->lgr->id, 2160de2fea7bSTony Lu link->lgr->net->net_cookie, 21610a99be43SKarsten Graul SMC_LGR_ID_SIZE, &link->link_uid, 21620a99be43SKarsten Graul SMC_LGR_ID_SIZE, &link->peer_link_uid, 21630a99be43SKarsten Graul link->smcibdev->ibdev->name, link->ibport); 2164877ae5beSKarsten Graul link->state = SMC_LNK_ACTIVE; 216500a049cfSKarsten Graul if (link->lgr->llc_testlink_time) { 2166c4a146c7STony Lu link->llc_testlink_time = link->lgr->llc_testlink_time; 21671020e1efSKarsten Graul schedule_delayed_work(&link->llc_testlink_wrk, 2168877ae5beSKarsten Graul link->llc_testlink_time); 2169877ae5beSKarsten Graul } 2170877ae5beSKarsten Graul } 2171877ae5beSKarsten Graul 2172877ae5beSKarsten Graul /* called in worker context */ 21730a99be43SKarsten Graul void smc_llc_link_clear(struct smc_link *link, bool log) 2174877ae5beSKarsten Graul { 21750a99be43SKarsten Graul if (log) 2176de2fea7bSTony Lu pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link removed: id %*phN" 21770a99be43SKarsten Graul ", peerid %*phN, ibdev %s, ibport %d\n", 21780a99be43SKarsten Graul SMC_LGR_ID_SIZE, &link->lgr->id, 2179de2fea7bSTony Lu link->lgr->net->net_cookie, 21800a99be43SKarsten Graul SMC_LGR_ID_SIZE, &link->link_uid, 21810a99be43SKarsten Graul SMC_LGR_ID_SIZE, &link->peer_link_uid, 21820a99be43SKarsten Graul link->smcibdev->ibdev->name, link->ibport); 21832140ac26SKarsten Graul complete(&link->llc_testlink_resp); 21842140ac26SKarsten Graul cancel_delayed_work_sync(&link->llc_testlink_wrk); 2185877ae5beSKarsten Graul } 2186877ae5beSKarsten Graul 21873d88a21bSKarsten Graul /* register a new rtoken at the remote peer (for all links) */ 21883d88a21bSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *send_link, 218944aa81ceSKarsten Graul struct smc_buf_desc *rmb_desc) 219044aa81ceSKarsten Graul { 21913d88a21bSKarsten Graul struct smc_link_group *lgr = send_link->lgr; 21923d88a21bSKarsten Graul struct smc_llc_qentry *qentry = NULL; 21933d88a21bSKarsten Graul int rc = 0; 219444aa81ceSKarsten Graul 21953d88a21bSKarsten Graul rc = smc_llc_send_confirm_rkey(send_link, rmb_desc); 21963d88a21bSKarsten Graul if (rc) 21973d88a21bSKarsten Graul goto out; 219844aa81ceSKarsten Graul /* receive CONFIRM RKEY response from server over RoCE fabric */ 21993d88a21bSKarsten Graul qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 22003d88a21bSKarsten Graul SMC_LLC_CONFIRM_RKEY); 22013d88a21bSKarsten Graul if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 22023d88a21bSKarsten Graul rc = -EFAULT; 22033d88a21bSKarsten Graul out: 22043d88a21bSKarsten Graul if (qentry) 22053d88a21bSKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 22063d88a21bSKarsten Graul return rc; 220744aa81ceSKarsten Graul } 220844aa81ceSKarsten Graul 220960e03c62SKarsten Graul /* unregister an rtoken at the remote peer */ 22106d74c3a8SKarsten Graul int smc_llc_do_delete_rkey(struct smc_link_group *lgr, 221160e03c62SKarsten Graul struct smc_buf_desc *rmb_desc) 221260e03c62SKarsten Graul { 22136d74c3a8SKarsten Graul struct smc_llc_qentry *qentry = NULL; 22146d74c3a8SKarsten Graul struct smc_link *send_link; 22150b29ec64SUrsula Braun int rc = 0; 221660e03c62SKarsten Graul 22176d74c3a8SKarsten Graul send_link = smc_llc_usable_link(lgr); 22186d74c3a8SKarsten Graul if (!send_link) 22196d74c3a8SKarsten Graul return -ENOLINK; 22206d74c3a8SKarsten Graul 22216d74c3a8SKarsten Graul /* protected by llc_flow control */ 22226d74c3a8SKarsten Graul rc = smc_llc_send_delete_rkey(send_link, rmb_desc); 222360e03c62SKarsten Graul if (rc) 222460e03c62SKarsten Graul goto out; 222560e03c62SKarsten Graul /* receive DELETE RKEY response from server over RoCE fabric */ 22266d74c3a8SKarsten Graul qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 22276d74c3a8SKarsten Graul SMC_LLC_DELETE_RKEY); 22286d74c3a8SKarsten Graul if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 222960e03c62SKarsten Graul rc = -EFAULT; 223060e03c62SKarsten Graul out: 22316d74c3a8SKarsten Graul if (qentry) 22326d74c3a8SKarsten Graul smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 223360e03c62SKarsten Graul return rc; 223460e03c62SKarsten Graul } 223560e03c62SKarsten Graul 223645fa8da0SKarsten Graul void smc_llc_link_set_uid(struct smc_link *link) 223745fa8da0SKarsten Graul { 223845fa8da0SKarsten Graul __be32 link_uid; 223945fa8da0SKarsten Graul 224045fa8da0SKarsten Graul link_uid = htonl(*((u32 *)link->lgr->id) + link->link_id); 224145fa8da0SKarsten Graul memcpy(link->link_uid, &link_uid, SMC_LGR_ID_SIZE); 224245fa8da0SKarsten Graul } 224345fa8da0SKarsten Graul 2244649758ffSKarsten Graul /* save peers link user id, used for debug purposes */ 2245649758ffSKarsten Graul void smc_llc_save_peer_uid(struct smc_llc_qentry *qentry) 2246649758ffSKarsten Graul { 2247649758ffSKarsten Graul memcpy(qentry->link->peer_link_uid, qentry->msg.confirm_link.link_uid, 2248649758ffSKarsten Graul SMC_LGR_ID_SIZE); 2249649758ffSKarsten Graul } 2250649758ffSKarsten Graul 225192334cfcSKarsten Graul /* evaluate confirm link request or response */ 225292334cfcSKarsten Graul int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry, 225392334cfcSKarsten Graul enum smc_llc_reqresp type) 225492334cfcSKarsten Graul { 225545fa8da0SKarsten Graul if (type == SMC_LLC_REQ) { /* SMC server assigns link_id */ 225692334cfcSKarsten Graul qentry->link->link_id = qentry->msg.confirm_link.link_num; 225745fa8da0SKarsten Graul smc_llc_link_set_uid(qentry->link); 225845fa8da0SKarsten Graul } 225992334cfcSKarsten Graul if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC)) 226092334cfcSKarsten Graul return -ENOTSUPP; 226192334cfcSKarsten Graul return 0; 226292334cfcSKarsten Graul } 226392334cfcSKarsten Graul 22649bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/ 22659bf9abeaSUrsula Braun 22669bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = { 22679bf9abeaSUrsula Braun { 22689bf9abeaSUrsula Braun .handler = smc_llc_rx_handler, 22699bf9abeaSUrsula Braun .type = SMC_LLC_CONFIRM_LINK 22709bf9abeaSUrsula Braun }, 22719bf9abeaSUrsula Braun { 2272313164daSKarsten Graul .handler = smc_llc_rx_handler, 2273313164daSKarsten Graul .type = SMC_LLC_TEST_LINK 2274313164daSKarsten Graul }, 2275313164daSKarsten Graul { 22764ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 227752bedf37SKarsten Graul .type = SMC_LLC_ADD_LINK 227852bedf37SKarsten Graul }, 227952bedf37SKarsten Graul { 228052bedf37SKarsten Graul .handler = smc_llc_rx_handler, 228187f88cdaSKarsten Graul .type = SMC_LLC_ADD_LINK_CONT 228287f88cdaSKarsten Graul }, 228387f88cdaSKarsten Graul { 228487f88cdaSKarsten Graul .handler = smc_llc_rx_handler, 228552bedf37SKarsten Graul .type = SMC_LLC_DELETE_LINK 228652bedf37SKarsten Graul }, 228752bedf37SKarsten Graul { 228852bedf37SKarsten Graul .handler = smc_llc_rx_handler, 22894ed75de5SKarsten Graul .type = SMC_LLC_CONFIRM_RKEY 22904ed75de5SKarsten Graul }, 22914ed75de5SKarsten Graul { 22924ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 22934ed75de5SKarsten Graul .type = SMC_LLC_CONFIRM_RKEY_CONT 22944ed75de5SKarsten Graul }, 22954ed75de5SKarsten Graul { 22964ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 22974ed75de5SKarsten Graul .type = SMC_LLC_DELETE_RKEY 22984ed75de5SKarsten Graul }, 2299b4ba4652SKarsten Graul /* V2 types */ 2300b4ba4652SKarsten Graul { 2301b4ba4652SKarsten Graul .handler = smc_llc_rx_handler, 2302b4ba4652SKarsten Graul .type = SMC_LLC_CONFIRM_LINK_V2 2303b4ba4652SKarsten Graul }, 2304b4ba4652SKarsten Graul { 2305b4ba4652SKarsten Graul .handler = smc_llc_rx_handler, 2306b4ba4652SKarsten Graul .type = SMC_LLC_TEST_LINK_V2 2307b4ba4652SKarsten Graul }, 2308b4ba4652SKarsten Graul { 2309b4ba4652SKarsten Graul .handler = smc_llc_rx_handler, 2310b4ba4652SKarsten Graul .type = SMC_LLC_ADD_LINK_V2 2311b4ba4652SKarsten Graul }, 2312b4ba4652SKarsten Graul { 2313b4ba4652SKarsten Graul .handler = smc_llc_rx_handler, 2314b4ba4652SKarsten Graul .type = SMC_LLC_DELETE_LINK_V2 2315b4ba4652SKarsten Graul }, 2316b4ba4652SKarsten Graul { 2317b4ba4652SKarsten Graul .handler = smc_llc_rx_handler, 2318b4ba4652SKarsten Graul .type = SMC_LLC_REQ_ADD_LINK_V2 2319b4ba4652SKarsten Graul }, 2320b4ba4652SKarsten Graul { 2321b4ba4652SKarsten Graul .handler = smc_llc_rx_handler, 2322b4ba4652SKarsten Graul .type = SMC_LLC_CONFIRM_RKEY_V2 2323b4ba4652SKarsten Graul }, 2324b4ba4652SKarsten Graul { 2325b4ba4652SKarsten Graul .handler = smc_llc_rx_handler, 2326b4ba4652SKarsten Graul .type = SMC_LLC_DELETE_RKEY_V2 2327b4ba4652SKarsten Graul }, 23284ed75de5SKarsten Graul { 23299bf9abeaSUrsula Braun .handler = NULL, 23309bf9abeaSUrsula Braun } 23319bf9abeaSUrsula Braun }; 23329bf9abeaSUrsula Braun 23339bf9abeaSUrsula Braun int __init smc_llc_init(void) 23349bf9abeaSUrsula Braun { 23359bf9abeaSUrsula Braun struct smc_wr_rx_handler *handler; 23369bf9abeaSUrsula Braun int rc = 0; 23379bf9abeaSUrsula Braun 23389bf9abeaSUrsula Braun for (handler = smc_llc_rx_handlers; handler->handler; handler++) { 23399bf9abeaSUrsula Braun INIT_HLIST_NODE(&handler->list); 23409bf9abeaSUrsula Braun rc = smc_wr_rx_register_handler(handler); 23419bf9abeaSUrsula Braun if (rc) 23429bf9abeaSUrsula Braun break; 23439bf9abeaSUrsula Braun } 23449bf9abeaSUrsula Braun return rc; 23459bf9abeaSUrsula Braun } 2346