18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Shared Memory Communications over RDMA (SMC-R) and RoCE 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Link Layer Control (LLC) 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2016 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author(s): Klaus Wacker <Klaus.Wacker@de.ibm.com> 108c2ecf20Sopenharmony_ci * Ursula Braun <ubraun@linux.vnet.ibm.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <net/tcp.h> 148c2ecf20Sopenharmony_ci#include <rdma/ib_verbs.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "smc.h" 178c2ecf20Sopenharmony_ci#include "smc_core.h" 188c2ecf20Sopenharmony_ci#include "smc_clc.h" 198c2ecf20Sopenharmony_ci#include "smc_llc.h" 208c2ecf20Sopenharmony_ci#include "smc_pnet.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define SMC_LLC_DATA_LEN 40 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct smc_llc_hdr { 258c2ecf20Sopenharmony_ci struct smc_wr_rx_hdr common; 268c2ecf20Sopenharmony_ci u8 length; /* 44 */ 278c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN_BITFIELD) 288c2ecf20Sopenharmony_ci u8 reserved:4, 298c2ecf20Sopenharmony_ci add_link_rej_rsn:4; 308c2ecf20Sopenharmony_ci#elif defined(__LITTLE_ENDIAN_BITFIELD) 318c2ecf20Sopenharmony_ci u8 add_link_rej_rsn:4, 328c2ecf20Sopenharmony_ci reserved:4; 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci u8 flags; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define SMC_LLC_FLAG_NO_RMBE_EYEC 0x03 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct smc_llc_msg_confirm_link { /* type 0x01 */ 408c2ecf20Sopenharmony_ci struct smc_llc_hdr hd; 418c2ecf20Sopenharmony_ci u8 sender_mac[ETH_ALEN]; 428c2ecf20Sopenharmony_ci u8 sender_gid[SMC_GID_SIZE]; 438c2ecf20Sopenharmony_ci u8 sender_qp_num[3]; 448c2ecf20Sopenharmony_ci u8 link_num; 458c2ecf20Sopenharmony_ci u8 link_uid[SMC_LGR_ID_SIZE]; 468c2ecf20Sopenharmony_ci u8 max_links; 478c2ecf20Sopenharmony_ci u8 reserved[9]; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define SMC_LLC_FLAG_ADD_LNK_REJ 0x40 518c2ecf20Sopenharmony_ci#define SMC_LLC_REJ_RSN_NO_ALT_PATH 1 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define SMC_LLC_ADD_LNK_MAX_LINKS 2 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct smc_llc_msg_add_link { /* type 0x02 */ 568c2ecf20Sopenharmony_ci struct smc_llc_hdr hd; 578c2ecf20Sopenharmony_ci u8 sender_mac[ETH_ALEN]; 588c2ecf20Sopenharmony_ci u8 reserved2[2]; 598c2ecf20Sopenharmony_ci u8 sender_gid[SMC_GID_SIZE]; 608c2ecf20Sopenharmony_ci u8 sender_qp_num[3]; 618c2ecf20Sopenharmony_ci u8 link_num; 628c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN_BITFIELD) 638c2ecf20Sopenharmony_ci u8 reserved3 : 4, 648c2ecf20Sopenharmony_ci qp_mtu : 4; 658c2ecf20Sopenharmony_ci#elif defined(__LITTLE_ENDIAN_BITFIELD) 668c2ecf20Sopenharmony_ci u8 qp_mtu : 4, 678c2ecf20Sopenharmony_ci reserved3 : 4; 688c2ecf20Sopenharmony_ci#endif 698c2ecf20Sopenharmony_ci u8 initial_psn[3]; 708c2ecf20Sopenharmony_ci u8 reserved[8]; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct smc_llc_msg_add_link_cont_rt { 748c2ecf20Sopenharmony_ci __be32 rmb_key; 758c2ecf20Sopenharmony_ci __be32 rmb_key_new; 768c2ecf20Sopenharmony_ci __be64 rmb_vaddr_new; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define SMC_LLC_RKEYS_PER_CONT_MSG 2 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistruct smc_llc_msg_add_link_cont { /* type 0x03 */ 828c2ecf20Sopenharmony_ci struct smc_llc_hdr hd; 838c2ecf20Sopenharmony_ci u8 link_num; 848c2ecf20Sopenharmony_ci u8 num_rkeys; 858c2ecf20Sopenharmony_ci u8 reserved2[2]; 868c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG]; 878c2ecf20Sopenharmony_ci u8 reserved[4]; 888c2ecf20Sopenharmony_ci} __packed; /* format defined in RFC7609 */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define SMC_LLC_FLAG_DEL_LINK_ALL 0x40 918c2ecf20Sopenharmony_ci#define SMC_LLC_FLAG_DEL_LINK_ORDERLY 0x20 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct smc_llc_msg_del_link { /* type 0x04 */ 948c2ecf20Sopenharmony_ci struct smc_llc_hdr hd; 958c2ecf20Sopenharmony_ci u8 link_num; 968c2ecf20Sopenharmony_ci __be32 reason; 978c2ecf20Sopenharmony_ci u8 reserved[35]; 988c2ecf20Sopenharmony_ci} __packed; /* format defined in RFC7609 */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct smc_llc_msg_test_link { /* type 0x07 */ 1018c2ecf20Sopenharmony_ci struct smc_llc_hdr hd; 1028c2ecf20Sopenharmony_ci u8 user_data[16]; 1038c2ecf20Sopenharmony_ci u8 reserved[24]; 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct smc_rmb_rtoken { 1078c2ecf20Sopenharmony_ci union { 1088c2ecf20Sopenharmony_ci u8 num_rkeys; /* first rtoken byte of CONFIRM LINK msg */ 1098c2ecf20Sopenharmony_ci /* is actually the num of rtokens, first */ 1108c2ecf20Sopenharmony_ci /* rtoken is always for the current link */ 1118c2ecf20Sopenharmony_ci u8 link_id; /* link id of the rtoken */ 1128c2ecf20Sopenharmony_ci }; 1138c2ecf20Sopenharmony_ci __be32 rmb_key; 1148c2ecf20Sopenharmony_ci __be64 rmb_vaddr; 1158c2ecf20Sopenharmony_ci} __packed; /* format defined in RFC7609 */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define SMC_LLC_RKEYS_PER_MSG 3 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistruct smc_llc_msg_confirm_rkey { /* type 0x06 */ 1208c2ecf20Sopenharmony_ci struct smc_llc_hdr hd; 1218c2ecf20Sopenharmony_ci struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; 1228c2ecf20Sopenharmony_ci u8 reserved; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define SMC_LLC_DEL_RKEY_MAX 8 1268c2ecf20Sopenharmony_ci#define SMC_LLC_FLAG_RKEY_RETRY 0x10 1278c2ecf20Sopenharmony_ci#define SMC_LLC_FLAG_RKEY_NEG 0x20 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistruct smc_llc_msg_delete_rkey { /* type 0x09 */ 1308c2ecf20Sopenharmony_ci struct smc_llc_hdr hd; 1318c2ecf20Sopenharmony_ci u8 num_rkeys; 1328c2ecf20Sopenharmony_ci u8 err_mask; 1338c2ecf20Sopenharmony_ci u8 reserved[2]; 1348c2ecf20Sopenharmony_ci __be32 rkey[8]; 1358c2ecf20Sopenharmony_ci u8 reserved2[4]; 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciunion smc_llc_msg { 1398c2ecf20Sopenharmony_ci struct smc_llc_msg_confirm_link confirm_link; 1408c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link add_link; 1418c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link_cont add_link_cont; 1428c2ecf20Sopenharmony_ci struct smc_llc_msg_del_link delete_link; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci struct smc_llc_msg_confirm_rkey confirm_rkey; 1458c2ecf20Sopenharmony_ci struct smc_llc_msg_delete_rkey delete_rkey; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci struct smc_llc_msg_test_link test_link; 1488c2ecf20Sopenharmony_ci struct { 1498c2ecf20Sopenharmony_ci struct smc_llc_hdr hdr; 1508c2ecf20Sopenharmony_ci u8 data[SMC_LLC_DATA_LEN]; 1518c2ecf20Sopenharmony_ci } raw; 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define SMC_LLC_FLAG_RESP 0x80 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistruct smc_llc_qentry { 1578c2ecf20Sopenharmony_ci struct list_head list; 1588c2ecf20Sopenharmony_ci struct smc_link *link; 1598c2ecf20Sopenharmony_ci union smc_llc_msg msg; 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistruct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry = flow->qentry; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci flow->qentry = NULL; 1698c2ecf20Sopenharmony_ci return qentry; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_civoid smc_llc_flow_qentry_del(struct smc_llc_flow *flow) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (flow->qentry) { 1778c2ecf20Sopenharmony_ci qentry = flow->qentry; 1788c2ecf20Sopenharmony_ci flow->qentry = NULL; 1798c2ecf20Sopenharmony_ci kfree(qentry); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow, 1848c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci flow->qentry = qentry; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void smc_llc_flow_parallel(struct smc_link_group *lgr, u8 flow_type, 1908c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci u8 msg_type = qentry->msg.raw.hdr.common.type; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if ((msg_type == SMC_LLC_ADD_LINK || msg_type == SMC_LLC_DELETE_LINK) && 1958c2ecf20Sopenharmony_ci flow_type != msg_type && !lgr->delayed_event) { 1968c2ecf20Sopenharmony_ci lgr->delayed_event = qentry; 1978c2ecf20Sopenharmony_ci return; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci /* drop parallel or already-in-progress llc requests */ 2008c2ecf20Sopenharmony_ci if (flow_type != msg_type) 2018c2ecf20Sopenharmony_ci pr_warn_once("smc: SMC-R lg %*phN dropped parallel " 2028c2ecf20Sopenharmony_ci "LLC msg: msg %d flow %d role %d\n", 2038c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &lgr->id, 2048c2ecf20Sopenharmony_ci qentry->msg.raw.hdr.common.type, 2058c2ecf20Sopenharmony_ci flow_type, lgr->role); 2068c2ecf20Sopenharmony_ci kfree(qentry); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* try to start a new llc flow, initiated by an incoming llc msg */ 2108c2ecf20Sopenharmony_cistatic bool smc_llc_flow_start(struct smc_llc_flow *flow, 2118c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct smc_link_group *lgr = qentry->link->lgr; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci spin_lock_bh(&lgr->llc_flow_lock); 2168c2ecf20Sopenharmony_ci if (flow->type) { 2178c2ecf20Sopenharmony_ci /* a flow is already active */ 2188c2ecf20Sopenharmony_ci smc_llc_flow_parallel(lgr, flow->type, qentry); 2198c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 2208c2ecf20Sopenharmony_ci return false; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci switch (qentry->msg.raw.hdr.common.type) { 2238c2ecf20Sopenharmony_ci case SMC_LLC_ADD_LINK: 2248c2ecf20Sopenharmony_ci flow->type = SMC_LLC_FLOW_ADD_LINK; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case SMC_LLC_DELETE_LINK: 2278c2ecf20Sopenharmony_ci flow->type = SMC_LLC_FLOW_DEL_LINK; 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY: 2308c2ecf20Sopenharmony_ci case SMC_LLC_DELETE_RKEY: 2318c2ecf20Sopenharmony_ci flow->type = SMC_LLC_FLOW_RKEY; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci default: 2348c2ecf20Sopenharmony_ci flow->type = SMC_LLC_FLOW_NONE; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci smc_llc_flow_qentry_set(flow, qentry); 2378c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 2388c2ecf20Sopenharmony_ci return true; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* start a new local llc flow, wait till current flow finished */ 2428c2ecf20Sopenharmony_ciint smc_llc_flow_initiate(struct smc_link_group *lgr, 2438c2ecf20Sopenharmony_ci enum smc_llc_flowtype type) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE; 2468c2ecf20Sopenharmony_ci int rc; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* all flows except confirm_rkey and delete_rkey are exclusive, 2498c2ecf20Sopenharmony_ci * confirm/delete rkey flows can run concurrently (local and remote) 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci if (type == SMC_LLC_FLOW_RKEY) 2528c2ecf20Sopenharmony_ci allowed_remote = SMC_LLC_FLOW_RKEY; 2538c2ecf20Sopenharmony_ciagain: 2548c2ecf20Sopenharmony_ci if (list_empty(&lgr->list)) 2558c2ecf20Sopenharmony_ci return -ENODEV; 2568c2ecf20Sopenharmony_ci spin_lock_bh(&lgr->llc_flow_lock); 2578c2ecf20Sopenharmony_ci if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 2588c2ecf20Sopenharmony_ci (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 2598c2ecf20Sopenharmony_ci lgr->llc_flow_rmt.type == allowed_remote)) { 2608c2ecf20Sopenharmony_ci lgr->llc_flow_lcl.type = type; 2618c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 2658c2ecf20Sopenharmony_ci rc = wait_event_timeout(lgr->llc_flow_waiter, (list_empty(&lgr->list) || 2668c2ecf20Sopenharmony_ci (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 2678c2ecf20Sopenharmony_ci (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 2688c2ecf20Sopenharmony_ci lgr->llc_flow_rmt.type == allowed_remote))), 2698c2ecf20Sopenharmony_ci SMC_LLC_WAIT_TIME * 10); 2708c2ecf20Sopenharmony_ci if (!rc) 2718c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2728c2ecf20Sopenharmony_ci goto again; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* finish the current llc flow */ 2768c2ecf20Sopenharmony_civoid smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci spin_lock_bh(&lgr->llc_flow_lock); 2798c2ecf20Sopenharmony_ci memset(flow, 0, sizeof(*flow)); 2808c2ecf20Sopenharmony_ci flow->type = SMC_LLC_FLOW_NONE; 2818c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 2828c2ecf20Sopenharmony_ci if (!list_empty(&lgr->list) && lgr->delayed_event && 2838c2ecf20Sopenharmony_ci flow == &lgr->llc_flow_lcl) 2848c2ecf20Sopenharmony_ci schedule_work(&lgr->llc_event_work); 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci wake_up(&lgr->llc_flow_waiter); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* lnk is optional and used for early wakeup when link goes down, useful in 2908c2ecf20Sopenharmony_ci * cases where we wait for a response on the link after we sent a request 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_cistruct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr, 2938c2ecf20Sopenharmony_ci struct smc_link *lnk, 2948c2ecf20Sopenharmony_ci int time_out, u8 exp_msg) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct smc_llc_flow *flow = &lgr->llc_flow_lcl; 2978c2ecf20Sopenharmony_ci u8 rcv_msg; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci wait_event_timeout(lgr->llc_msg_waiter, 3008c2ecf20Sopenharmony_ci (flow->qentry || 3018c2ecf20Sopenharmony_ci (lnk && !smc_link_usable(lnk)) || 3028c2ecf20Sopenharmony_ci list_empty(&lgr->list)), 3038c2ecf20Sopenharmony_ci time_out); 3048c2ecf20Sopenharmony_ci if (!flow->qentry || 3058c2ecf20Sopenharmony_ci (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) { 3068c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(flow); 3078c2ecf20Sopenharmony_ci goto out; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci rcv_msg = flow->qentry->msg.raw.hdr.common.type; 3108c2ecf20Sopenharmony_ci if (exp_msg && rcv_msg != exp_msg) { 3118c2ecf20Sopenharmony_ci if (exp_msg == SMC_LLC_ADD_LINK && 3128c2ecf20Sopenharmony_ci rcv_msg == SMC_LLC_DELETE_LINK) { 3138c2ecf20Sopenharmony_ci /* flow_start will delay the unexpected msg */ 3148c2ecf20Sopenharmony_ci smc_llc_flow_start(&lgr->llc_flow_lcl, 3158c2ecf20Sopenharmony_ci smc_llc_flow_qentry_clr(flow)); 3168c2ecf20Sopenharmony_ci return NULL; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci pr_warn_once("smc: SMC-R lg %*phN dropped unexpected LLC msg: " 3198c2ecf20Sopenharmony_ci "msg %d exp %d flow %d role %d flags %x\n", 3208c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &lgr->id, rcv_msg, exp_msg, 3218c2ecf20Sopenharmony_ci flow->type, lgr->role, 3228c2ecf20Sopenharmony_ci flow->qentry->msg.raw.hdr.flags); 3238c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(flow); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ciout: 3268c2ecf20Sopenharmony_ci return flow->qentry; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/********************************** send *************************************/ 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistruct smc_llc_tx_pend { 3328c2ecf20Sopenharmony_ci}; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* handler for send/transmission completion of an LLC msg */ 3358c2ecf20Sopenharmony_cistatic void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend, 3368c2ecf20Sopenharmony_ci struct smc_link *link, 3378c2ecf20Sopenharmony_ci enum ib_wc_status wc_status) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci /* future work: handle wc_status error for recovery and failover */ 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/** 3438c2ecf20Sopenharmony_ci * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits 3448c2ecf20Sopenharmony_ci * @link: Pointer to SMC link used for sending LLC control message. 3458c2ecf20Sopenharmony_ci * @wr_buf: Out variable returning pointer to work request payload buffer. 3468c2ecf20Sopenharmony_ci * @pend: Out variable returning pointer to private pending WR tracking. 3478c2ecf20Sopenharmony_ci * It's the context the transmit complete handler will get. 3488c2ecf20Sopenharmony_ci * 3498c2ecf20Sopenharmony_ci * Reserves and pre-fills an entry for a pending work request send/tx. 3508c2ecf20Sopenharmony_ci * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx. 3518c2ecf20Sopenharmony_ci * Can sleep due to smc_get_ctrl_buf (if not in softirq context). 3528c2ecf20Sopenharmony_ci * 3538c2ecf20Sopenharmony_ci * Return: 0 on success, otherwise an error value. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_cistatic int smc_llc_add_pending_send(struct smc_link *link, 3568c2ecf20Sopenharmony_ci struct smc_wr_buf **wr_buf, 3578c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv **pend) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci int rc; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL, 3628c2ecf20Sopenharmony_ci pend); 3638c2ecf20Sopenharmony_ci if (rc < 0) 3648c2ecf20Sopenharmony_ci return rc; 3658c2ecf20Sopenharmony_ci BUILD_BUG_ON_MSG( 3668c2ecf20Sopenharmony_ci sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE, 3678c2ecf20Sopenharmony_ci "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)"); 3688c2ecf20Sopenharmony_ci BUILD_BUG_ON_MSG( 3698c2ecf20Sopenharmony_ci sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE, 3708c2ecf20Sopenharmony_ci "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()"); 3718c2ecf20Sopenharmony_ci BUILD_BUG_ON_MSG( 3728c2ecf20Sopenharmony_ci sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, 3738c2ecf20Sopenharmony_ci "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)"); 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* high-level API to send LLC confirm link */ 3788c2ecf20Sopenharmony_ciint smc_llc_send_confirm_link(struct smc_link *link, 3798c2ecf20Sopenharmony_ci enum smc_llc_reqresp reqresp) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct smc_llc_msg_confirm_link *confllc; 3828c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 3838c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 3848c2ecf20Sopenharmony_ci int rc; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 3878c2ecf20Sopenharmony_ci return -ENOLINK; 3888c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 3898c2ecf20Sopenharmony_ci if (rc) 3908c2ecf20Sopenharmony_ci goto put_out; 3918c2ecf20Sopenharmony_ci confllc = (struct smc_llc_msg_confirm_link *)wr_buf; 3928c2ecf20Sopenharmony_ci memset(confllc, 0, sizeof(*confllc)); 3938c2ecf20Sopenharmony_ci confllc->hd.common.type = SMC_LLC_CONFIRM_LINK; 3948c2ecf20Sopenharmony_ci confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link); 3958c2ecf20Sopenharmony_ci confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC; 3968c2ecf20Sopenharmony_ci if (reqresp == SMC_LLC_RESP) 3978c2ecf20Sopenharmony_ci confllc->hd.flags |= SMC_LLC_FLAG_RESP; 3988c2ecf20Sopenharmony_ci memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1], 3998c2ecf20Sopenharmony_ci ETH_ALEN); 4008c2ecf20Sopenharmony_ci memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE); 4018c2ecf20Sopenharmony_ci hton24(confllc->sender_qp_num, link->roce_qp->qp_num); 4028c2ecf20Sopenharmony_ci confllc->link_num = link->link_id; 4038c2ecf20Sopenharmony_ci memcpy(confllc->link_uid, link->link_uid, SMC_LGR_ID_SIZE); 4048c2ecf20Sopenharmony_ci confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; 4058c2ecf20Sopenharmony_ci /* send llc message */ 4068c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 4078c2ecf20Sopenharmony_ciput_out: 4088c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 4098c2ecf20Sopenharmony_ci return rc; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/* send LLC confirm rkey request */ 4138c2ecf20Sopenharmony_cistatic int smc_llc_send_confirm_rkey(struct smc_link *send_link, 4148c2ecf20Sopenharmony_ci struct smc_buf_desc *rmb_desc) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct smc_llc_msg_confirm_rkey *rkeyllc; 4178c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 4188c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 4198c2ecf20Sopenharmony_ci struct smc_link *link; 4208c2ecf20Sopenharmony_ci int i, rc, rtok_ix; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(send_link)) 4238c2ecf20Sopenharmony_ci return -ENOLINK; 4248c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend); 4258c2ecf20Sopenharmony_ci if (rc) 4268c2ecf20Sopenharmony_ci goto put_out; 4278c2ecf20Sopenharmony_ci rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf; 4288c2ecf20Sopenharmony_ci memset(rkeyllc, 0, sizeof(*rkeyllc)); 4298c2ecf20Sopenharmony_ci rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY; 4308c2ecf20Sopenharmony_ci rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci rtok_ix = 1; 4338c2ecf20Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 4348c2ecf20Sopenharmony_ci link = &send_link->lgr->lnk[i]; 4358c2ecf20Sopenharmony_ci if (smc_link_active(link) && link != send_link) { 4368c2ecf20Sopenharmony_ci rkeyllc->rtoken[rtok_ix].link_id = link->link_id; 4378c2ecf20Sopenharmony_ci rkeyllc->rtoken[rtok_ix].rmb_key = 4388c2ecf20Sopenharmony_ci htonl(rmb_desc->mr_rx[link->link_idx]->rkey); 4398c2ecf20Sopenharmony_ci rkeyllc->rtoken[rtok_ix].rmb_vaddr = cpu_to_be64( 4408c2ecf20Sopenharmony_ci (u64)sg_dma_address( 4418c2ecf20Sopenharmony_ci rmb_desc->sgt[link->link_idx].sgl)); 4428c2ecf20Sopenharmony_ci rtok_ix++; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci /* rkey of send_link is in rtoken[0] */ 4468c2ecf20Sopenharmony_ci rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1; 4478c2ecf20Sopenharmony_ci rkeyllc->rtoken[0].rmb_key = 4488c2ecf20Sopenharmony_ci htonl(rmb_desc->mr_rx[send_link->link_idx]->rkey); 4498c2ecf20Sopenharmony_ci rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64( 4508c2ecf20Sopenharmony_ci (u64)sg_dma_address(rmb_desc->sgt[send_link->link_idx].sgl)); 4518c2ecf20Sopenharmony_ci /* send llc message */ 4528c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(send_link, pend); 4538c2ecf20Sopenharmony_ciput_out: 4548c2ecf20Sopenharmony_ci smc_wr_tx_link_put(send_link); 4558c2ecf20Sopenharmony_ci return rc; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/* send LLC delete rkey request */ 4598c2ecf20Sopenharmony_cistatic int smc_llc_send_delete_rkey(struct smc_link *link, 4608c2ecf20Sopenharmony_ci struct smc_buf_desc *rmb_desc) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct smc_llc_msg_delete_rkey *rkeyllc; 4638c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 4648c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 4658c2ecf20Sopenharmony_ci int rc; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 4688c2ecf20Sopenharmony_ci return -ENOLINK; 4698c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 4708c2ecf20Sopenharmony_ci if (rc) 4718c2ecf20Sopenharmony_ci goto put_out; 4728c2ecf20Sopenharmony_ci rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf; 4738c2ecf20Sopenharmony_ci memset(rkeyllc, 0, sizeof(*rkeyllc)); 4748c2ecf20Sopenharmony_ci rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY; 4758c2ecf20Sopenharmony_ci rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey); 4768c2ecf20Sopenharmony_ci rkeyllc->num_rkeys = 1; 4778c2ecf20Sopenharmony_ci rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey); 4788c2ecf20Sopenharmony_ci /* send llc message */ 4798c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 4808c2ecf20Sopenharmony_ciput_out: 4818c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 4828c2ecf20Sopenharmony_ci return rc; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/* send ADD LINK request or response */ 4868c2ecf20Sopenharmony_ciint smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[], 4878c2ecf20Sopenharmony_ci struct smc_link *link_new, 4888c2ecf20Sopenharmony_ci enum smc_llc_reqresp reqresp) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link *addllc; 4918c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 4928c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 4938c2ecf20Sopenharmony_ci int rc; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 4968c2ecf20Sopenharmony_ci return -ENOLINK; 4978c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 4988c2ecf20Sopenharmony_ci if (rc) 4998c2ecf20Sopenharmony_ci goto put_out; 5008c2ecf20Sopenharmony_ci addllc = (struct smc_llc_msg_add_link *)wr_buf; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci memset(addllc, 0, sizeof(*addllc)); 5038c2ecf20Sopenharmony_ci addllc->hd.common.type = SMC_LLC_ADD_LINK; 5048c2ecf20Sopenharmony_ci addllc->hd.length = sizeof(struct smc_llc_msg_add_link); 5058c2ecf20Sopenharmony_ci if (reqresp == SMC_LLC_RESP) 5068c2ecf20Sopenharmony_ci addllc->hd.flags |= SMC_LLC_FLAG_RESP; 5078c2ecf20Sopenharmony_ci memcpy(addllc->sender_mac, mac, ETH_ALEN); 5088c2ecf20Sopenharmony_ci memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); 5098c2ecf20Sopenharmony_ci if (link_new) { 5108c2ecf20Sopenharmony_ci addllc->link_num = link_new->link_id; 5118c2ecf20Sopenharmony_ci hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num); 5128c2ecf20Sopenharmony_ci hton24(addllc->initial_psn, link_new->psn_initial); 5138c2ecf20Sopenharmony_ci if (reqresp == SMC_LLC_REQ) 5148c2ecf20Sopenharmony_ci addllc->qp_mtu = link_new->path_mtu; 5158c2ecf20Sopenharmony_ci else 5168c2ecf20Sopenharmony_ci addllc->qp_mtu = min(link_new->path_mtu, 5178c2ecf20Sopenharmony_ci link_new->peer_mtu); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci /* send llc message */ 5208c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 5218c2ecf20Sopenharmony_ciput_out: 5228c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 5238c2ecf20Sopenharmony_ci return rc; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/* send DELETE LINK request or response */ 5278c2ecf20Sopenharmony_ciint smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id, 5288c2ecf20Sopenharmony_ci enum smc_llc_reqresp reqresp, bool orderly, 5298c2ecf20Sopenharmony_ci u32 reason) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct smc_llc_msg_del_link *delllc; 5328c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 5338c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 5348c2ecf20Sopenharmony_ci int rc; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 5378c2ecf20Sopenharmony_ci return -ENOLINK; 5388c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 5398c2ecf20Sopenharmony_ci if (rc) 5408c2ecf20Sopenharmony_ci goto put_out; 5418c2ecf20Sopenharmony_ci delllc = (struct smc_llc_msg_del_link *)wr_buf; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci memset(delllc, 0, sizeof(*delllc)); 5448c2ecf20Sopenharmony_ci delllc->hd.common.type = SMC_LLC_DELETE_LINK; 5458c2ecf20Sopenharmony_ci delllc->hd.length = sizeof(struct smc_llc_msg_del_link); 5468c2ecf20Sopenharmony_ci if (reqresp == SMC_LLC_RESP) 5478c2ecf20Sopenharmony_ci delllc->hd.flags |= SMC_LLC_FLAG_RESP; 5488c2ecf20Sopenharmony_ci if (orderly) 5498c2ecf20Sopenharmony_ci delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 5508c2ecf20Sopenharmony_ci if (link_del_id) 5518c2ecf20Sopenharmony_ci delllc->link_num = link_del_id; 5528c2ecf20Sopenharmony_ci else 5538c2ecf20Sopenharmony_ci delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 5548c2ecf20Sopenharmony_ci delllc->reason = htonl(reason); 5558c2ecf20Sopenharmony_ci /* send llc message */ 5568c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 5578c2ecf20Sopenharmony_ciput_out: 5588c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 5598c2ecf20Sopenharmony_ci return rc; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/* send LLC test link request */ 5638c2ecf20Sopenharmony_cistatic int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16]) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct smc_llc_msg_test_link *testllc; 5668c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 5678c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 5688c2ecf20Sopenharmony_ci int rc; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 5718c2ecf20Sopenharmony_ci return -ENOLINK; 5728c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 5738c2ecf20Sopenharmony_ci if (rc) 5748c2ecf20Sopenharmony_ci goto put_out; 5758c2ecf20Sopenharmony_ci testllc = (struct smc_llc_msg_test_link *)wr_buf; 5768c2ecf20Sopenharmony_ci memset(testllc, 0, sizeof(*testllc)); 5778c2ecf20Sopenharmony_ci testllc->hd.common.type = SMC_LLC_TEST_LINK; 5788c2ecf20Sopenharmony_ci testllc->hd.length = sizeof(struct smc_llc_msg_test_link); 5798c2ecf20Sopenharmony_ci memcpy(testllc->user_data, user_data, sizeof(testllc->user_data)); 5808c2ecf20Sopenharmony_ci /* send llc message */ 5818c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 5828c2ecf20Sopenharmony_ciput_out: 5838c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 5848c2ecf20Sopenharmony_ci return rc; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/* schedule an llc send on link, may wait for buffers */ 5888c2ecf20Sopenharmony_cistatic int smc_llc_send_message(struct smc_link *link, void *llcbuf) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 5918c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 5928c2ecf20Sopenharmony_ci int rc; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 5958c2ecf20Sopenharmony_ci return -ENOLINK; 5968c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 5978c2ecf20Sopenharmony_ci if (rc) 5988c2ecf20Sopenharmony_ci goto put_out; 5998c2ecf20Sopenharmony_ci memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); 6008c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 6018c2ecf20Sopenharmony_ciput_out: 6028c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 6038c2ecf20Sopenharmony_ci return rc; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci/* schedule an llc send on link, may wait for buffers, 6078c2ecf20Sopenharmony_ci * and wait for send completion notification. 6088c2ecf20Sopenharmony_ci * @return 0 on success 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_cistatic int smc_llc_send_message_wait(struct smc_link *link, void *llcbuf) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 6138c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 6148c2ecf20Sopenharmony_ci int rc; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 6178c2ecf20Sopenharmony_ci return -ENOLINK; 6188c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 6198c2ecf20Sopenharmony_ci if (rc) 6208c2ecf20Sopenharmony_ci goto put_out; 6218c2ecf20Sopenharmony_ci memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); 6228c2ecf20Sopenharmony_ci rc = smc_wr_tx_send_wait(link, pend, SMC_LLC_WAIT_TIME); 6238c2ecf20Sopenharmony_ciput_out: 6248c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 6258c2ecf20Sopenharmony_ci return rc; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/********************************* receive ***********************************/ 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int smc_llc_alloc_alt_link(struct smc_link_group *lgr, 6318c2ecf20Sopenharmony_ci enum smc_lgr_type lgr_new_t) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci int i; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (lgr->type == SMC_LGR_SYMMETRIC || 6368c2ecf20Sopenharmony_ci (lgr->type != SMC_LGR_SINGLE && 6378c2ecf20Sopenharmony_ci (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 6388c2ecf20Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER))) 6398c2ecf20Sopenharmony_ci return -EMLINK; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 6428c2ecf20Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) { 6438c2ecf20Sopenharmony_ci for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--) 6448c2ecf20Sopenharmony_ci if (lgr->lnk[i].state == SMC_LNK_UNUSED) 6458c2ecf20Sopenharmony_ci return i; 6468c2ecf20Sopenharmony_ci } else { 6478c2ecf20Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) 6488c2ecf20Sopenharmony_ci if (lgr->lnk[i].state == SMC_LNK_UNUSED) 6498c2ecf20Sopenharmony_ci return i; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci return -EMLINK; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/* return first buffer from any of the next buf lists */ 6558c2ecf20Sopenharmony_cistatic struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr, 6568c2ecf20Sopenharmony_ci int *buf_lst) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct smc_buf_desc *buf_pos; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci while (*buf_lst < SMC_RMBE_SIZES) { 6618c2ecf20Sopenharmony_ci buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst], 6628c2ecf20Sopenharmony_ci struct smc_buf_desc, list); 6638c2ecf20Sopenharmony_ci if (buf_pos) 6648c2ecf20Sopenharmony_ci return buf_pos; 6658c2ecf20Sopenharmony_ci (*buf_lst)++; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci return NULL; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci/* return next rmb from buffer lists */ 6718c2ecf20Sopenharmony_cistatic struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr, 6728c2ecf20Sopenharmony_ci int *buf_lst, 6738c2ecf20Sopenharmony_ci struct smc_buf_desc *buf_pos) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct smc_buf_desc *buf_next; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) { 6788c2ecf20Sopenharmony_ci (*buf_lst)++; 6798c2ecf20Sopenharmony_ci return _smc_llc_get_next_rmb(lgr, buf_lst); 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci buf_next = list_next_entry(buf_pos, list); 6828c2ecf20Sopenharmony_ci return buf_next; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr, 6868c2ecf20Sopenharmony_ci int *buf_lst) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci *buf_lst = 0; 6898c2ecf20Sopenharmony_ci return smc_llc_get_next_rmb(lgr, buf_lst, NULL); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* send one add_link_continue msg */ 6938c2ecf20Sopenharmony_cistatic int smc_llc_add_link_cont(struct smc_link *link, 6948c2ecf20Sopenharmony_ci struct smc_link *link_new, u8 *num_rkeys_todo, 6958c2ecf20Sopenharmony_ci int *buf_lst, struct smc_buf_desc **buf_pos) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link_cont *addc_llc; 6988c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 6998c2ecf20Sopenharmony_ci int prim_lnk_idx, lnk_idx, i, rc; 7008c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 7018c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf; 7028c2ecf20Sopenharmony_ci struct smc_buf_desc *rmb; 7038c2ecf20Sopenharmony_ci u8 n; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 7068c2ecf20Sopenharmony_ci return -ENOLINK; 7078c2ecf20Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 7088c2ecf20Sopenharmony_ci if (rc) 7098c2ecf20Sopenharmony_ci goto put_out; 7108c2ecf20Sopenharmony_ci addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf; 7118c2ecf20Sopenharmony_ci memset(addc_llc, 0, sizeof(*addc_llc)); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci prim_lnk_idx = link->link_idx; 7148c2ecf20Sopenharmony_ci lnk_idx = link_new->link_idx; 7158c2ecf20Sopenharmony_ci addc_llc->link_num = link_new->link_id; 7168c2ecf20Sopenharmony_ci addc_llc->num_rkeys = *num_rkeys_todo; 7178c2ecf20Sopenharmony_ci n = *num_rkeys_todo; 7188c2ecf20Sopenharmony_ci for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) { 7198c2ecf20Sopenharmony_ci while (*buf_pos && !(*buf_pos)->used) 7208c2ecf20Sopenharmony_ci *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 7218c2ecf20Sopenharmony_ci if (!*buf_pos) { 7228c2ecf20Sopenharmony_ci addc_llc->num_rkeys = addc_llc->num_rkeys - 7238c2ecf20Sopenharmony_ci *num_rkeys_todo; 7248c2ecf20Sopenharmony_ci *num_rkeys_todo = 0; 7258c2ecf20Sopenharmony_ci break; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci rmb = *buf_pos; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_key = htonl(rmb->mr_rx[prim_lnk_idx]->rkey); 7308c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_key_new = htonl(rmb->mr_rx[lnk_idx]->rkey); 7318c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_vaddr_new = 7328c2ecf20Sopenharmony_ci cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci (*num_rkeys_todo)--; 7358c2ecf20Sopenharmony_ci *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci addc_llc->hd.common.type = SMC_LLC_ADD_LINK_CONT; 7388c2ecf20Sopenharmony_ci addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont); 7398c2ecf20Sopenharmony_ci if (lgr->role == SMC_CLNT) 7408c2ecf20Sopenharmony_ci addc_llc->hd.flags |= SMC_LLC_FLAG_RESP; 7418c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 7428c2ecf20Sopenharmony_ciput_out: 7438c2ecf20Sopenharmony_ci smc_wr_tx_link_put(link); 7448c2ecf20Sopenharmony_ci return rc; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic int smc_llc_cli_rkey_exchange(struct smc_link *link, 7488c2ecf20Sopenharmony_ci struct smc_link *link_new) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link_cont *addc_llc; 7518c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 7528c2ecf20Sopenharmony_ci u8 max, num_rkeys_send, num_rkeys_recv; 7538c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 7548c2ecf20Sopenharmony_ci struct smc_buf_desc *buf_pos; 7558c2ecf20Sopenharmony_ci int buf_lst; 7568c2ecf20Sopenharmony_ci int rc = 0; 7578c2ecf20Sopenharmony_ci int i; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci mutex_lock(&lgr->rmbs_lock); 7608c2ecf20Sopenharmony_ci num_rkeys_send = lgr->conns_num; 7618c2ecf20Sopenharmony_ci buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 7628c2ecf20Sopenharmony_ci do { 7638c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME, 7648c2ecf20Sopenharmony_ci SMC_LLC_ADD_LINK_CONT); 7658c2ecf20Sopenharmony_ci if (!qentry) { 7668c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 7678c2ecf20Sopenharmony_ci break; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci addc_llc = &qentry->msg.add_link_cont; 7708c2ecf20Sopenharmony_ci num_rkeys_recv = addc_llc->num_rkeys; 7718c2ecf20Sopenharmony_ci max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 7728c2ecf20Sopenharmony_ci for (i = 0; i < max; i++) { 7738c2ecf20Sopenharmony_ci smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 7748c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_key, 7758c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_vaddr_new, 7768c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_key_new); 7778c2ecf20Sopenharmony_ci num_rkeys_recv--; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 7808c2ecf20Sopenharmony_ci rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 7818c2ecf20Sopenharmony_ci &buf_lst, &buf_pos); 7828c2ecf20Sopenharmony_ci if (rc) 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci } while (num_rkeys_send || num_rkeys_recv); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci mutex_unlock(&lgr->rmbs_lock); 7878c2ecf20Sopenharmony_ci return rc; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci/* prepare and send an add link reject response */ 7918c2ecf20Sopenharmony_cistatic int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 7948c2ecf20Sopenharmony_ci qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; 7958c2ecf20Sopenharmony_ci qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; 7968c2ecf20Sopenharmony_ci return smc_llc_send_message(qentry->link, &qentry->msg); 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic int smc_llc_cli_conf_link(struct smc_link *link, 8008c2ecf20Sopenharmony_ci struct smc_init_info *ini, 8018c2ecf20Sopenharmony_ci struct smc_link *link_new, 8028c2ecf20Sopenharmony_ci enum smc_lgr_type lgr_new_t) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 8058c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 8068c2ecf20Sopenharmony_ci int rc = 0; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* receive CONFIRM LINK request over RoCE fabric */ 8098c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0); 8108c2ecf20Sopenharmony_ci if (!qentry) { 8118c2ecf20Sopenharmony_ci rc = smc_llc_send_delete_link(link, link_new->link_id, 8128c2ecf20Sopenharmony_ci SMC_LLC_REQ, false, 8138c2ecf20Sopenharmony_ci SMC_LLC_DEL_LOST_PATH); 8148c2ecf20Sopenharmony_ci return -ENOLINK; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci if (qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) { 8178c2ecf20Sopenharmony_ci /* received DELETE_LINK instead */ 8188c2ecf20Sopenharmony_ci qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 8198c2ecf20Sopenharmony_ci smc_llc_send_message(link, &qentry->msg); 8208c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 8218c2ecf20Sopenharmony_ci return -ENOLINK; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci smc_llc_save_peer_uid(qentry); 8248c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci rc = smc_ib_modify_qp_rts(link_new); 8278c2ecf20Sopenharmony_ci if (rc) { 8288c2ecf20Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 8298c2ecf20Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 8308c2ecf20Sopenharmony_ci return -ENOLINK; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci smc_wr_remember_qp_attr(link_new); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci rc = smcr_buf_reg_lgr(link_new); 8358c2ecf20Sopenharmony_ci if (rc) { 8368c2ecf20Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 8378c2ecf20Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 8388c2ecf20Sopenharmony_ci return -ENOLINK; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* send CONFIRM LINK response over RoCE fabric */ 8428c2ecf20Sopenharmony_ci rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP); 8438c2ecf20Sopenharmony_ci if (rc) { 8448c2ecf20Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 8458c2ecf20Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 8468c2ecf20Sopenharmony_ci return -ENOLINK; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci smc_llc_link_active(link_new); 8498c2ecf20Sopenharmony_ci if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 8508c2ecf20Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) 8518c2ecf20Sopenharmony_ci smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); 8528c2ecf20Sopenharmony_ci else 8538c2ecf20Sopenharmony_ci smcr_lgr_set_type(lgr, lgr_new_t); 8548c2ecf20Sopenharmony_ci return 0; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic void smc_llc_save_add_link_info(struct smc_link *link, 8588c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link *add_llc) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci link->peer_qpn = ntoh24(add_llc->sender_qp_num); 8618c2ecf20Sopenharmony_ci memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE); 8628c2ecf20Sopenharmony_ci memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN); 8638c2ecf20Sopenharmony_ci link->peer_psn = ntoh24(add_llc->initial_psn); 8648c2ecf20Sopenharmony_ci link->peer_mtu = add_llc->qp_mtu; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* as an SMC client, process an add link request */ 8688c2ecf20Sopenharmony_ciint smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link *llc = &qentry->msg.add_link; 8718c2ecf20Sopenharmony_ci enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 8728c2ecf20Sopenharmony_ci struct smc_link_group *lgr = smc_get_lgr(link); 8738c2ecf20Sopenharmony_ci struct smc_link *lnk_new = NULL; 8748c2ecf20Sopenharmony_ci struct smc_init_info ini; 8758c2ecf20Sopenharmony_ci int lnk_idx, rc = 0; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (!llc->qp_mtu) 8788c2ecf20Sopenharmony_ci goto out_reject; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci ini.vlan_id = lgr->vlan_id; 8818c2ecf20Sopenharmony_ci smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev); 8828c2ecf20Sopenharmony_ci if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 8838c2ecf20Sopenharmony_ci !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN)) { 8848c2ecf20Sopenharmony_ci if (!ini.ib_dev) 8858c2ecf20Sopenharmony_ci goto out_reject; 8868c2ecf20Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci if (!ini.ib_dev) { 8898c2ecf20Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 8908c2ecf20Sopenharmony_ci ini.ib_dev = link->smcibdev; 8918c2ecf20Sopenharmony_ci ini.ib_port = link->ibport; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 8948c2ecf20Sopenharmony_ci if (lnk_idx < 0) 8958c2ecf20Sopenharmony_ci goto out_reject; 8968c2ecf20Sopenharmony_ci lnk_new = &lgr->lnk[lnk_idx]; 8978c2ecf20Sopenharmony_ci rc = smcr_link_init(lgr, lnk_new, lnk_idx, &ini); 8988c2ecf20Sopenharmony_ci if (rc) 8998c2ecf20Sopenharmony_ci goto out_reject; 9008c2ecf20Sopenharmony_ci smc_llc_save_add_link_info(lnk_new, llc); 9018c2ecf20Sopenharmony_ci lnk_new->link_id = llc->link_num; /* SMC server assigns link id */ 9028c2ecf20Sopenharmony_ci smc_llc_link_set_uid(lnk_new); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci rc = smc_ib_ready_link(lnk_new); 9058c2ecf20Sopenharmony_ci if (rc) 9068c2ecf20Sopenharmony_ci goto out_clear_lnk; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci rc = smcr_buf_map_lgr(lnk_new); 9098c2ecf20Sopenharmony_ci if (rc) 9108c2ecf20Sopenharmony_ci goto out_clear_lnk; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci rc = smc_llc_send_add_link(link, 9138c2ecf20Sopenharmony_ci lnk_new->smcibdev->mac[ini.ib_port - 1], 9148c2ecf20Sopenharmony_ci lnk_new->gid, lnk_new, SMC_LLC_RESP); 9158c2ecf20Sopenharmony_ci if (rc) 9168c2ecf20Sopenharmony_ci goto out_clear_lnk; 9178c2ecf20Sopenharmony_ci rc = smc_llc_cli_rkey_exchange(link, lnk_new); 9188c2ecf20Sopenharmony_ci if (rc) { 9198c2ecf20Sopenharmony_ci rc = 0; 9208c2ecf20Sopenharmony_ci goto out_clear_lnk; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci rc = smc_llc_cli_conf_link(link, &ini, lnk_new, lgr_new_t); 9238c2ecf20Sopenharmony_ci if (!rc) 9248c2ecf20Sopenharmony_ci goto out; 9258c2ecf20Sopenharmony_ciout_clear_lnk: 9268c2ecf20Sopenharmony_ci lnk_new->state = SMC_LNK_INACTIVE; 9278c2ecf20Sopenharmony_ci smcr_link_clear(lnk_new, false); 9288c2ecf20Sopenharmony_ciout_reject: 9298c2ecf20Sopenharmony_ci smc_llc_cli_add_link_reject(qentry); 9308c2ecf20Sopenharmony_ciout: 9318c2ecf20Sopenharmony_ci kfree(qentry); 9328c2ecf20Sopenharmony_ci return rc; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/* as an SMC client, invite server to start the add_link processing */ 9368c2ecf20Sopenharmony_cistatic void smc_llc_cli_add_link_invite(struct smc_link *link, 9378c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct smc_link_group *lgr = smc_get_lgr(link); 9408c2ecf20Sopenharmony_ci struct smc_init_info ini; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (lgr->type == SMC_LGR_SYMMETRIC || 9438c2ecf20Sopenharmony_ci lgr->type == SMC_LGR_ASYMMETRIC_PEER) 9448c2ecf20Sopenharmony_ci goto out; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ini.vlan_id = lgr->vlan_id; 9478c2ecf20Sopenharmony_ci smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev); 9488c2ecf20Sopenharmony_ci if (!ini.ib_dev) 9498c2ecf20Sopenharmony_ci goto out; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci smc_llc_send_add_link(link, ini.ib_dev->mac[ini.ib_port - 1], 9528c2ecf20Sopenharmony_ci ini.ib_gid, NULL, SMC_LLC_REQ); 9538c2ecf20Sopenharmony_ciout: 9548c2ecf20Sopenharmony_ci kfree(qentry); 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic bool smc_llc_is_empty_llc_message(union smc_llc_msg *llc) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci int i; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(llc->raw.data); i++) 9628c2ecf20Sopenharmony_ci if (llc->raw.data[i]) 9638c2ecf20Sopenharmony_ci return false; 9648c2ecf20Sopenharmony_ci return true; 9658c2ecf20Sopenharmony_ci} 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic bool smc_llc_is_local_add_link(union smc_llc_msg *llc) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci if (llc->raw.hdr.common.type == SMC_LLC_ADD_LINK && 9708c2ecf20Sopenharmony_ci smc_llc_is_empty_llc_message(llc)) 9718c2ecf20Sopenharmony_ci return true; 9728c2ecf20Sopenharmony_ci return false; 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic void smc_llc_process_cli_add_link(struct smc_link_group *lgr) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci mutex_lock(&lgr->llc_conf_mutex); 9828c2ecf20Sopenharmony_ci if (smc_llc_is_local_add_link(&qentry->msg)) 9838c2ecf20Sopenharmony_ci smc_llc_cli_add_link_invite(qentry->link, qentry); 9848c2ecf20Sopenharmony_ci else 9858c2ecf20Sopenharmony_ci smc_llc_cli_add_link(qentry->link, qentry); 9868c2ecf20Sopenharmony_ci mutex_unlock(&lgr->llc_conf_mutex); 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int smc_llc_active_link_count(struct smc_link_group *lgr) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci int i, link_count = 0; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 9948c2ecf20Sopenharmony_ci if (!smc_link_active(&lgr->lnk[i])) 9958c2ecf20Sopenharmony_ci continue; 9968c2ecf20Sopenharmony_ci link_count++; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci return link_count; 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/* find the asymmetric link when 3 links are established */ 10028c2ecf20Sopenharmony_cistatic struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci int asym_idx = -ENOENT; 10058c2ecf20Sopenharmony_ci int i, j, k; 10068c2ecf20Sopenharmony_ci bool found; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* determine asymmetric link */ 10098c2ecf20Sopenharmony_ci found = false; 10108c2ecf20Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 10118c2ecf20Sopenharmony_ci for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) { 10128c2ecf20Sopenharmony_ci if (!smc_link_usable(&lgr->lnk[i]) || 10138c2ecf20Sopenharmony_ci !smc_link_usable(&lgr->lnk[j])) 10148c2ecf20Sopenharmony_ci continue; 10158c2ecf20Sopenharmony_ci if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid, 10168c2ecf20Sopenharmony_ci SMC_GID_SIZE)) { 10178c2ecf20Sopenharmony_ci found = true; /* asym_lnk is i or j */ 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci if (found) 10228c2ecf20Sopenharmony_ci break; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci if (!found) 10258c2ecf20Sopenharmony_ci goto out; /* no asymmetric link */ 10268c2ecf20Sopenharmony_ci for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) { 10278c2ecf20Sopenharmony_ci if (!smc_link_usable(&lgr->lnk[k])) 10288c2ecf20Sopenharmony_ci continue; 10298c2ecf20Sopenharmony_ci if (k != i && 10308c2ecf20Sopenharmony_ci !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid, 10318c2ecf20Sopenharmony_ci SMC_GID_SIZE)) { 10328c2ecf20Sopenharmony_ci asym_idx = i; 10338c2ecf20Sopenharmony_ci break; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci if (k != j && 10368c2ecf20Sopenharmony_ci !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid, 10378c2ecf20Sopenharmony_ci SMC_GID_SIZE)) { 10388c2ecf20Sopenharmony_ci asym_idx = j; 10398c2ecf20Sopenharmony_ci break; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ciout: 10438c2ecf20Sopenharmony_ci return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx]; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic void smc_llc_delete_asym_link(struct smc_link_group *lgr) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci struct smc_link *lnk_new = NULL, *lnk_asym; 10498c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 10508c2ecf20Sopenharmony_ci int rc; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci lnk_asym = smc_llc_find_asym_link(lgr); 10538c2ecf20Sopenharmony_ci if (!lnk_asym) 10548c2ecf20Sopenharmony_ci return; /* no asymmetric link */ 10558c2ecf20Sopenharmony_ci if (!smc_link_downing(&lnk_asym->state)) 10568c2ecf20Sopenharmony_ci return; 10578c2ecf20Sopenharmony_ci lnk_new = smc_switch_conns(lgr, lnk_asym, false); 10588c2ecf20Sopenharmony_ci smc_wr_tx_wait_no_pending_sends(lnk_asym); 10598c2ecf20Sopenharmony_ci if (!lnk_new) 10608c2ecf20Sopenharmony_ci goto out_free; 10618c2ecf20Sopenharmony_ci /* change flow type from ADD_LINK into DEL_LINK */ 10628c2ecf20Sopenharmony_ci lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK; 10638c2ecf20Sopenharmony_ci rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ, 10648c2ecf20Sopenharmony_ci true, SMC_LLC_DEL_NO_ASYM_NEEDED); 10658c2ecf20Sopenharmony_ci if (rc) { 10668c2ecf20Sopenharmony_ci smcr_link_down_cond(lnk_new); 10678c2ecf20Sopenharmony_ci goto out_free; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME, 10708c2ecf20Sopenharmony_ci SMC_LLC_DELETE_LINK); 10718c2ecf20Sopenharmony_ci if (!qentry) { 10728c2ecf20Sopenharmony_ci smcr_link_down_cond(lnk_new); 10738c2ecf20Sopenharmony_ci goto out_free; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 10768c2ecf20Sopenharmony_ciout_free: 10778c2ecf20Sopenharmony_ci smcr_link_clear(lnk_asym, true); 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic int smc_llc_srv_rkey_exchange(struct smc_link *link, 10818c2ecf20Sopenharmony_ci struct smc_link *link_new) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link_cont *addc_llc; 10848c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 10858c2ecf20Sopenharmony_ci u8 max, num_rkeys_send, num_rkeys_recv; 10868c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 10878c2ecf20Sopenharmony_ci struct smc_buf_desc *buf_pos; 10888c2ecf20Sopenharmony_ci int buf_lst; 10898c2ecf20Sopenharmony_ci int rc = 0; 10908c2ecf20Sopenharmony_ci int i; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci mutex_lock(&lgr->rmbs_lock); 10938c2ecf20Sopenharmony_ci num_rkeys_send = lgr->conns_num; 10948c2ecf20Sopenharmony_ci buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 10958c2ecf20Sopenharmony_ci do { 10968c2ecf20Sopenharmony_ci smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 10978c2ecf20Sopenharmony_ci &buf_lst, &buf_pos); 10988c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, 10998c2ecf20Sopenharmony_ci SMC_LLC_ADD_LINK_CONT); 11008c2ecf20Sopenharmony_ci if (!qentry) { 11018c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 11028c2ecf20Sopenharmony_ci goto out; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci addc_llc = &qentry->msg.add_link_cont; 11058c2ecf20Sopenharmony_ci num_rkeys_recv = addc_llc->num_rkeys; 11068c2ecf20Sopenharmony_ci max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 11078c2ecf20Sopenharmony_ci for (i = 0; i < max; i++) { 11088c2ecf20Sopenharmony_ci smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 11098c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_key, 11108c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_vaddr_new, 11118c2ecf20Sopenharmony_ci addc_llc->rt[i].rmb_key_new); 11128c2ecf20Sopenharmony_ci num_rkeys_recv--; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 11158c2ecf20Sopenharmony_ci } while (num_rkeys_send || num_rkeys_recv); 11168c2ecf20Sopenharmony_ciout: 11178c2ecf20Sopenharmony_ci mutex_unlock(&lgr->rmbs_lock); 11188c2ecf20Sopenharmony_ci return rc; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic int smc_llc_srv_conf_link(struct smc_link *link, 11228c2ecf20Sopenharmony_ci struct smc_link *link_new, 11238c2ecf20Sopenharmony_ci enum smc_lgr_type lgr_new_t) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 11268c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 11278c2ecf20Sopenharmony_ci int rc; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci /* send CONFIRM LINK request over the RoCE fabric */ 11308c2ecf20Sopenharmony_ci rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ); 11318c2ecf20Sopenharmony_ci if (rc) 11328c2ecf20Sopenharmony_ci return -ENOLINK; 11338c2ecf20Sopenharmony_ci /* receive CONFIRM LINK response over the RoCE fabric */ 11348c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, 0); 11358c2ecf20Sopenharmony_ci if (!qentry || 11368c2ecf20Sopenharmony_ci qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) { 11378c2ecf20Sopenharmony_ci /* send DELETE LINK */ 11388c2ecf20Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 11398c2ecf20Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 11408c2ecf20Sopenharmony_ci if (qentry) 11418c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 11428c2ecf20Sopenharmony_ci return -ENOLINK; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci smc_llc_save_peer_uid(qentry); 11458c2ecf20Sopenharmony_ci smc_llc_link_active(link_new); 11468c2ecf20Sopenharmony_ci if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 11478c2ecf20Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) 11488c2ecf20Sopenharmony_ci smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); 11498c2ecf20Sopenharmony_ci else 11508c2ecf20Sopenharmony_ci smcr_lgr_set_type(lgr, lgr_new_t); 11518c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 11528c2ecf20Sopenharmony_ci return 0; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ciint smc_llc_srv_add_link(struct smc_link *link) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 11588c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 11598c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link *add_llc; 11608c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 11618c2ecf20Sopenharmony_ci struct smc_link *link_new; 11628c2ecf20Sopenharmony_ci struct smc_init_info ini; 11638c2ecf20Sopenharmony_ci int lnk_idx, rc = 0; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* ignore client add link recommendation, start new flow */ 11668c2ecf20Sopenharmony_ci ini.vlan_id = lgr->vlan_id; 11678c2ecf20Sopenharmony_ci smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev); 11688c2ecf20Sopenharmony_ci if (!ini.ib_dev) { 11698c2ecf20Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 11708c2ecf20Sopenharmony_ci ini.ib_dev = link->smcibdev; 11718c2ecf20Sopenharmony_ci ini.ib_port = link->ibport; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 11748c2ecf20Sopenharmony_ci if (lnk_idx < 0) 11758c2ecf20Sopenharmony_ci return 0; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, &ini); 11788c2ecf20Sopenharmony_ci if (rc) 11798c2ecf20Sopenharmony_ci return rc; 11808c2ecf20Sopenharmony_ci link_new = &lgr->lnk[lnk_idx]; 11818c2ecf20Sopenharmony_ci rc = smc_llc_send_add_link(link, 11828c2ecf20Sopenharmony_ci link_new->smcibdev->mac[ini.ib_port - 1], 11838c2ecf20Sopenharmony_ci link_new->gid, link_new, SMC_LLC_REQ); 11848c2ecf20Sopenharmony_ci if (rc) 11858c2ecf20Sopenharmony_ci goto out_err; 11868c2ecf20Sopenharmony_ci /* receive ADD LINK response over the RoCE fabric */ 11878c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK); 11888c2ecf20Sopenharmony_ci if (!qentry) { 11898c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 11908c2ecf20Sopenharmony_ci goto out_err; 11918c2ecf20Sopenharmony_ci } 11928c2ecf20Sopenharmony_ci add_llc = &qentry->msg.add_link; 11938c2ecf20Sopenharmony_ci if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) { 11948c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 11958c2ecf20Sopenharmony_ci rc = -ENOLINK; 11968c2ecf20Sopenharmony_ci goto out_err; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci if (lgr->type == SMC_LGR_SINGLE && 11998c2ecf20Sopenharmony_ci (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 12008c2ecf20Sopenharmony_ci !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN))) { 12018c2ecf20Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci smc_llc_save_add_link_info(link_new, add_llc); 12048c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci rc = smc_ib_ready_link(link_new); 12078c2ecf20Sopenharmony_ci if (rc) 12088c2ecf20Sopenharmony_ci goto out_err; 12098c2ecf20Sopenharmony_ci rc = smcr_buf_map_lgr(link_new); 12108c2ecf20Sopenharmony_ci if (rc) 12118c2ecf20Sopenharmony_ci goto out_err; 12128c2ecf20Sopenharmony_ci rc = smcr_buf_reg_lgr(link_new); 12138c2ecf20Sopenharmony_ci if (rc) 12148c2ecf20Sopenharmony_ci goto out_err; 12158c2ecf20Sopenharmony_ci rc = smc_llc_srv_rkey_exchange(link, link_new); 12168c2ecf20Sopenharmony_ci if (rc) 12178c2ecf20Sopenharmony_ci goto out_err; 12188c2ecf20Sopenharmony_ci rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t); 12198c2ecf20Sopenharmony_ci if (rc) 12208c2ecf20Sopenharmony_ci goto out_err; 12218c2ecf20Sopenharmony_ci return 0; 12228c2ecf20Sopenharmony_ciout_err: 12238c2ecf20Sopenharmony_ci link_new->state = SMC_LNK_INACTIVE; 12248c2ecf20Sopenharmony_ci smcr_link_clear(link_new, false); 12258c2ecf20Sopenharmony_ci return rc; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cistatic void smc_llc_process_srv_add_link(struct smc_link_group *lgr) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci struct smc_link *link = lgr->llc_flow_lcl.qentry->link; 12318c2ecf20Sopenharmony_ci int rc; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci mutex_lock(&lgr->llc_conf_mutex); 12368c2ecf20Sopenharmony_ci rc = smc_llc_srv_add_link(link); 12378c2ecf20Sopenharmony_ci if (!rc && lgr->type == SMC_LGR_SYMMETRIC) { 12388c2ecf20Sopenharmony_ci /* delete any asymmetric link */ 12398c2ecf20Sopenharmony_ci smc_llc_delete_asym_link(lgr); 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci mutex_unlock(&lgr->llc_conf_mutex); 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci/* enqueue a local add_link req to trigger a new add_link flow */ 12458c2ecf20Sopenharmony_civoid smc_llc_add_link_local(struct smc_link *link) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci struct smc_llc_msg_add_link add_llc = {}; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci add_llc.hd.length = sizeof(add_llc); 12508c2ecf20Sopenharmony_ci add_llc.hd.common.type = SMC_LLC_ADD_LINK; 12518c2ecf20Sopenharmony_ci /* no dev and port needed */ 12528c2ecf20Sopenharmony_ci smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc); 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci/* worker to process an add link message */ 12568c2ecf20Sopenharmony_cistatic void smc_llc_add_link_work(struct work_struct *work) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct smc_link_group *lgr = container_of(work, struct smc_link_group, 12598c2ecf20Sopenharmony_ci llc_add_link_work); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (list_empty(&lgr->list)) { 12628c2ecf20Sopenharmony_ci /* link group is terminating */ 12638c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 12648c2ecf20Sopenharmony_ci goto out; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci if (lgr->role == SMC_CLNT) 12688c2ecf20Sopenharmony_ci smc_llc_process_cli_add_link(lgr); 12698c2ecf20Sopenharmony_ci else 12708c2ecf20Sopenharmony_ci smc_llc_process_srv_add_link(lgr); 12718c2ecf20Sopenharmony_ciout: 12728c2ecf20Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci/* enqueue a local del_link msg to trigger a new del_link flow, 12768c2ecf20Sopenharmony_ci * called only for role SMC_SERV 12778c2ecf20Sopenharmony_ci */ 12788c2ecf20Sopenharmony_civoid smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci struct smc_llc_msg_del_link del_llc = {}; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci del_llc.hd.length = sizeof(del_llc); 12838c2ecf20Sopenharmony_ci del_llc.hd.common.type = SMC_LLC_DELETE_LINK; 12848c2ecf20Sopenharmony_ci del_llc.link_num = del_link_id; 12858c2ecf20Sopenharmony_ci del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH); 12868c2ecf20Sopenharmony_ci del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 12878c2ecf20Sopenharmony_ci smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc); 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic void smc_llc_process_cli_delete_link(struct smc_link_group *lgr) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct smc_link *lnk_del = NULL, *lnk_asym, *lnk; 12938c2ecf20Sopenharmony_ci struct smc_llc_msg_del_link *del_llc; 12948c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 12958c2ecf20Sopenharmony_ci int active_links; 12968c2ecf20Sopenharmony_ci int lnk_idx; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 12998c2ecf20Sopenharmony_ci lnk = qentry->link; 13008c2ecf20Sopenharmony_ci del_llc = &qentry->msg.delete_link; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { 13038c2ecf20Sopenharmony_ci smc_lgr_terminate_sched(lgr); 13048c2ecf20Sopenharmony_ci goto out; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci mutex_lock(&lgr->llc_conf_mutex); 13078c2ecf20Sopenharmony_ci /* delete single link */ 13088c2ecf20Sopenharmony_ci for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) { 13098c2ecf20Sopenharmony_ci if (lgr->lnk[lnk_idx].link_id != del_llc->link_num) 13108c2ecf20Sopenharmony_ci continue; 13118c2ecf20Sopenharmony_ci lnk_del = &lgr->lnk[lnk_idx]; 13128c2ecf20Sopenharmony_ci break; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci del_llc->hd.flags |= SMC_LLC_FLAG_RESP; 13158c2ecf20Sopenharmony_ci if (!lnk_del) { 13168c2ecf20Sopenharmony_ci /* link was not found */ 13178c2ecf20Sopenharmony_ci del_llc->reason = htonl(SMC_LLC_DEL_NOLNK); 13188c2ecf20Sopenharmony_ci smc_llc_send_message(lnk, &qentry->msg); 13198c2ecf20Sopenharmony_ci goto out_unlock; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci lnk_asym = smc_llc_find_asym_link(lgr); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci del_llc->reason = 0; 13248c2ecf20Sopenharmony_ci smc_llc_send_message(lnk, &qentry->msg); /* response */ 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci if (smc_link_downing(&lnk_del->state)) 13278c2ecf20Sopenharmony_ci smc_switch_conns(lgr, lnk_del, false); 13288c2ecf20Sopenharmony_ci smcr_link_clear(lnk_del, true); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci active_links = smc_llc_active_link_count(lgr); 13318c2ecf20Sopenharmony_ci if (lnk_del == lnk_asym) { 13328c2ecf20Sopenharmony_ci /* expected deletion of asym link, don't change lgr state */ 13338c2ecf20Sopenharmony_ci } else if (active_links == 1) { 13348c2ecf20Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); 13358c2ecf20Sopenharmony_ci } else if (!active_links) { 13368c2ecf20Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_NONE); 13378c2ecf20Sopenharmony_ci smc_lgr_terminate_sched(lgr); 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ciout_unlock: 13408c2ecf20Sopenharmony_ci mutex_unlock(&lgr->llc_conf_mutex); 13418c2ecf20Sopenharmony_ciout: 13428c2ecf20Sopenharmony_ci kfree(qentry); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci/* try to send a DELETE LINK ALL request on any active link, 13468c2ecf20Sopenharmony_ci * waiting for send completion 13478c2ecf20Sopenharmony_ci */ 13488c2ecf20Sopenharmony_civoid smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord, u32 rsn) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci struct smc_llc_msg_del_link delllc = {}; 13518c2ecf20Sopenharmony_ci int i; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci delllc.hd.common.type = SMC_LLC_DELETE_LINK; 13548c2ecf20Sopenharmony_ci delllc.hd.length = sizeof(delllc); 13558c2ecf20Sopenharmony_ci if (ord) 13568c2ecf20Sopenharmony_ci delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 13578c2ecf20Sopenharmony_ci delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 13588c2ecf20Sopenharmony_ci delllc.reason = htonl(rsn); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 13618c2ecf20Sopenharmony_ci if (!smc_link_sendable(&lgr->lnk[i])) 13628c2ecf20Sopenharmony_ci continue; 13638c2ecf20Sopenharmony_ci if (!smc_llc_send_message_wait(&lgr->lnk[i], &delllc)) 13648c2ecf20Sopenharmony_ci break; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic void smc_llc_process_srv_delete_link(struct smc_link_group *lgr) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct smc_llc_msg_del_link *del_llc; 13718c2ecf20Sopenharmony_ci struct smc_link *lnk, *lnk_del; 13728c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 13738c2ecf20Sopenharmony_ci int active_links; 13748c2ecf20Sopenharmony_ci int i; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci mutex_lock(&lgr->llc_conf_mutex); 13778c2ecf20Sopenharmony_ci qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 13788c2ecf20Sopenharmony_ci lnk = qentry->link; 13798c2ecf20Sopenharmony_ci del_llc = &qentry->msg.delete_link; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (qentry->msg.delete_link.hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { 13828c2ecf20Sopenharmony_ci /* delete entire lgr */ 13838c2ecf20Sopenharmony_ci smc_llc_send_link_delete_all(lgr, true, ntohl( 13848c2ecf20Sopenharmony_ci qentry->msg.delete_link.reason)); 13858c2ecf20Sopenharmony_ci smc_lgr_terminate_sched(lgr); 13868c2ecf20Sopenharmony_ci goto out; 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci /* delete single link */ 13898c2ecf20Sopenharmony_ci lnk_del = NULL; 13908c2ecf20Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 13918c2ecf20Sopenharmony_ci if (lgr->lnk[i].link_id == del_llc->link_num) { 13928c2ecf20Sopenharmony_ci lnk_del = &lgr->lnk[i]; 13938c2ecf20Sopenharmony_ci break; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci if (!lnk_del) 13978c2ecf20Sopenharmony_ci goto out; /* asymmetric link already deleted */ 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if (smc_link_downing(&lnk_del->state)) { 14008c2ecf20Sopenharmony_ci if (smc_switch_conns(lgr, lnk_del, false)) 14018c2ecf20Sopenharmony_ci smc_wr_tx_wait_no_pending_sends(lnk_del); 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci if (!list_empty(&lgr->list)) { 14048c2ecf20Sopenharmony_ci /* qentry is either a request from peer (send it back to 14058c2ecf20Sopenharmony_ci * initiate the DELETE_LINK processing), or a locally 14068c2ecf20Sopenharmony_ci * enqueued DELETE_LINK request (forward it) 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_ci if (!smc_llc_send_message(lnk, &qentry->msg)) { 14098c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry2; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci qentry2 = smc_llc_wait(lgr, lnk, SMC_LLC_WAIT_TIME, 14128c2ecf20Sopenharmony_ci SMC_LLC_DELETE_LINK); 14138c2ecf20Sopenharmony_ci if (qentry2) 14148c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci smcr_link_clear(lnk_del, true); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci active_links = smc_llc_active_link_count(lgr); 14208c2ecf20Sopenharmony_ci if (active_links == 1) { 14218c2ecf20Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); 14228c2ecf20Sopenharmony_ci } else if (!active_links) { 14238c2ecf20Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_NONE); 14248c2ecf20Sopenharmony_ci smc_lgr_terminate_sched(lgr); 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) { 14288c2ecf20Sopenharmony_ci /* trigger setup of asymm alt link */ 14298c2ecf20Sopenharmony_ci smc_llc_add_link_local(lnk); 14308c2ecf20Sopenharmony_ci } 14318c2ecf20Sopenharmony_ciout: 14328c2ecf20Sopenharmony_ci mutex_unlock(&lgr->llc_conf_mutex); 14338c2ecf20Sopenharmony_ci kfree(qentry); 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic void smc_llc_delete_link_work(struct work_struct *work) 14378c2ecf20Sopenharmony_ci{ 14388c2ecf20Sopenharmony_ci struct smc_link_group *lgr = container_of(work, struct smc_link_group, 14398c2ecf20Sopenharmony_ci llc_del_link_work); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (list_empty(&lgr->list)) { 14428c2ecf20Sopenharmony_ci /* link group is terminating */ 14438c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 14448c2ecf20Sopenharmony_ci goto out; 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (lgr->role == SMC_CLNT) 14488c2ecf20Sopenharmony_ci smc_llc_process_cli_delete_link(lgr); 14498c2ecf20Sopenharmony_ci else 14508c2ecf20Sopenharmony_ci smc_llc_process_srv_delete_link(lgr); 14518c2ecf20Sopenharmony_ciout: 14528c2ecf20Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci/* process a confirm_rkey request from peer, remote flow */ 14568c2ecf20Sopenharmony_cistatic void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci struct smc_llc_msg_confirm_rkey *llc; 14598c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 14608c2ecf20Sopenharmony_ci struct smc_link *link; 14618c2ecf20Sopenharmony_ci int num_entries; 14628c2ecf20Sopenharmony_ci int rk_idx; 14638c2ecf20Sopenharmony_ci int i; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci qentry = lgr->llc_flow_rmt.qentry; 14668c2ecf20Sopenharmony_ci llc = &qentry->msg.confirm_rkey; 14678c2ecf20Sopenharmony_ci link = qentry->link; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci num_entries = llc->rtoken[0].num_rkeys; 14708c2ecf20Sopenharmony_ci /* first rkey entry is for receiving link */ 14718c2ecf20Sopenharmony_ci rk_idx = smc_rtoken_add(link, 14728c2ecf20Sopenharmony_ci llc->rtoken[0].rmb_vaddr, 14738c2ecf20Sopenharmony_ci llc->rtoken[0].rmb_key); 14748c2ecf20Sopenharmony_ci if (rk_idx < 0) 14758c2ecf20Sopenharmony_ci goto out_err; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++) 14788c2ecf20Sopenharmony_ci smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id, 14798c2ecf20Sopenharmony_ci llc->rtoken[i].rmb_vaddr, 14808c2ecf20Sopenharmony_ci llc->rtoken[i].rmb_key); 14818c2ecf20Sopenharmony_ci /* max links is 3 so there is no need to support conf_rkey_cont msgs */ 14828c2ecf20Sopenharmony_ci goto out; 14838c2ecf20Sopenharmony_ciout_err: 14848c2ecf20Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 14858c2ecf20Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY; 14868c2ecf20Sopenharmony_ciout: 14878c2ecf20Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RESP; 14888c2ecf20Sopenharmony_ci smc_llc_send_message(link, &qentry->msg); 14898c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci/* process a delete_rkey request from peer, remote flow */ 14938c2ecf20Sopenharmony_cistatic void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr) 14948c2ecf20Sopenharmony_ci{ 14958c2ecf20Sopenharmony_ci struct smc_llc_msg_delete_rkey *llc; 14968c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 14978c2ecf20Sopenharmony_ci struct smc_link *link; 14988c2ecf20Sopenharmony_ci u8 err_mask = 0; 14998c2ecf20Sopenharmony_ci int i, max; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci qentry = lgr->llc_flow_rmt.qentry; 15028c2ecf20Sopenharmony_ci llc = &qentry->msg.delete_rkey; 15038c2ecf20Sopenharmony_ci link = qentry->link; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX); 15068c2ecf20Sopenharmony_ci for (i = 0; i < max; i++) { 15078c2ecf20Sopenharmony_ci if (smc_rtoken_delete(link, llc->rkey[i])) 15088c2ecf20Sopenharmony_ci err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i); 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci if (err_mask) { 15118c2ecf20Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 15128c2ecf20Sopenharmony_ci llc->err_mask = err_mask; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RESP; 15158c2ecf20Sopenharmony_ci smc_llc_send_message(link, &qentry->msg); 15168c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cistatic void smc_llc_protocol_violation(struct smc_link_group *lgr, u8 type) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci pr_warn_ratelimited("smc: SMC-R lg %*phN LLC protocol violation: " 15228c2ecf20Sopenharmony_ci "llc_type %d\n", SMC_LGR_ID_SIZE, &lgr->id, type); 15238c2ecf20Sopenharmony_ci smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_PROT_VIOL); 15248c2ecf20Sopenharmony_ci smc_lgr_terminate_sched(lgr); 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci/* flush the llc event queue */ 15288c2ecf20Sopenharmony_cistatic void smc_llc_event_flush(struct smc_link_group *lgr) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry, *q; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci spin_lock_bh(&lgr->llc_event_q_lock); 15338c2ecf20Sopenharmony_ci list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) { 15348c2ecf20Sopenharmony_ci list_del_init(&qentry->list); 15358c2ecf20Sopenharmony_ci kfree(qentry); 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_event_q_lock); 15388c2ecf20Sopenharmony_ci} 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_cistatic void smc_llc_event_handler(struct smc_llc_qentry *qentry) 15418c2ecf20Sopenharmony_ci{ 15428c2ecf20Sopenharmony_ci union smc_llc_msg *llc = &qentry->msg; 15438c2ecf20Sopenharmony_ci struct smc_link *link = qentry->link; 15448c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (!smc_link_usable(link)) 15478c2ecf20Sopenharmony_ci goto out; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci switch (llc->raw.hdr.common.type) { 15508c2ecf20Sopenharmony_ci case SMC_LLC_TEST_LINK: 15518c2ecf20Sopenharmony_ci llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP; 15528c2ecf20Sopenharmony_ci smc_llc_send_message(link, llc); 15538c2ecf20Sopenharmony_ci break; 15548c2ecf20Sopenharmony_ci case SMC_LLC_ADD_LINK: 15558c2ecf20Sopenharmony_ci if (list_empty(&lgr->list)) 15568c2ecf20Sopenharmony_ci goto out; /* lgr is terminating */ 15578c2ecf20Sopenharmony_ci if (lgr->role == SMC_CLNT) { 15588c2ecf20Sopenharmony_ci if (smc_llc_is_local_add_link(llc)) { 15598c2ecf20Sopenharmony_ci if (lgr->llc_flow_lcl.type == 15608c2ecf20Sopenharmony_ci SMC_LLC_FLOW_ADD_LINK) 15618c2ecf20Sopenharmony_ci break; /* add_link in progress */ 15628c2ecf20Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_lcl, 15638c2ecf20Sopenharmony_ci qentry)) { 15648c2ecf20Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci return; 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && 15698c2ecf20Sopenharmony_ci !lgr->llc_flow_lcl.qentry) { 15708c2ecf20Sopenharmony_ci /* a flow is waiting for this message */ 15718c2ecf20Sopenharmony_ci smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, 15728c2ecf20Sopenharmony_ci qentry); 15738c2ecf20Sopenharmony_ci wake_up(&lgr->llc_msg_waiter); 15748c2ecf20Sopenharmony_ci } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, 15758c2ecf20Sopenharmony_ci qentry)) { 15768c2ecf20Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 15798c2ecf20Sopenharmony_ci /* as smc server, handle client suggestion */ 15808c2ecf20Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci return; 15838c2ecf20Sopenharmony_ci case SMC_LLC_CONFIRM_LINK: 15848c2ecf20Sopenharmony_ci case SMC_LLC_ADD_LINK_CONT: 15858c2ecf20Sopenharmony_ci if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { 15868c2ecf20Sopenharmony_ci /* a flow is waiting for this message */ 15878c2ecf20Sopenharmony_ci smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); 15888c2ecf20Sopenharmony_ci wake_up(&lgr->llc_msg_waiter); 15898c2ecf20Sopenharmony_ci return; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci break; 15928c2ecf20Sopenharmony_ci case SMC_LLC_DELETE_LINK: 15938c2ecf20Sopenharmony_ci if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && 15948c2ecf20Sopenharmony_ci !lgr->llc_flow_lcl.qentry) { 15958c2ecf20Sopenharmony_ci /* DEL LINK REQ during ADD LINK SEQ */ 15968c2ecf20Sopenharmony_ci smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); 15978c2ecf20Sopenharmony_ci wake_up(&lgr->llc_msg_waiter); 15988c2ecf20Sopenharmony_ci } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 15998c2ecf20Sopenharmony_ci schedule_work(&lgr->llc_del_link_work); 16008c2ecf20Sopenharmony_ci } 16018c2ecf20Sopenharmony_ci return; 16028c2ecf20Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY: 16038c2ecf20Sopenharmony_ci /* new request from remote, assign to remote flow */ 16048c2ecf20Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 16058c2ecf20Sopenharmony_ci /* process here, does not wait for more llc msgs */ 16068c2ecf20Sopenharmony_ci smc_llc_rmt_conf_rkey(lgr); 16078c2ecf20Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci return; 16108c2ecf20Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY_CONT: 16118c2ecf20Sopenharmony_ci /* not used because max links is 3, and 3 rkeys fit into 16128c2ecf20Sopenharmony_ci * one CONFIRM_RKEY message 16138c2ecf20Sopenharmony_ci */ 16148c2ecf20Sopenharmony_ci break; 16158c2ecf20Sopenharmony_ci case SMC_LLC_DELETE_RKEY: 16168c2ecf20Sopenharmony_ci /* new request from remote, assign to remote flow */ 16178c2ecf20Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 16188c2ecf20Sopenharmony_ci /* process here, does not wait for more llc msgs */ 16198c2ecf20Sopenharmony_ci smc_llc_rmt_delete_rkey(lgr); 16208c2ecf20Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci return; 16238c2ecf20Sopenharmony_ci default: 16248c2ecf20Sopenharmony_ci smc_llc_protocol_violation(lgr, llc->raw.hdr.common.type); 16258c2ecf20Sopenharmony_ci break; 16268c2ecf20Sopenharmony_ci } 16278c2ecf20Sopenharmony_ciout: 16288c2ecf20Sopenharmony_ci kfree(qentry); 16298c2ecf20Sopenharmony_ci} 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci/* worker to process llc messages on the event queue */ 16328c2ecf20Sopenharmony_cistatic void smc_llc_event_work(struct work_struct *work) 16338c2ecf20Sopenharmony_ci{ 16348c2ecf20Sopenharmony_ci struct smc_link_group *lgr = container_of(work, struct smc_link_group, 16358c2ecf20Sopenharmony_ci llc_event_work); 16368c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if (!lgr->llc_flow_lcl.type && lgr->delayed_event) { 16398c2ecf20Sopenharmony_ci qentry = lgr->delayed_event; 16408c2ecf20Sopenharmony_ci lgr->delayed_event = NULL; 16418c2ecf20Sopenharmony_ci if (smc_link_usable(qentry->link)) 16428c2ecf20Sopenharmony_ci smc_llc_event_handler(qentry); 16438c2ecf20Sopenharmony_ci else 16448c2ecf20Sopenharmony_ci kfree(qentry); 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ciagain: 16488c2ecf20Sopenharmony_ci spin_lock_bh(&lgr->llc_event_q_lock); 16498c2ecf20Sopenharmony_ci if (!list_empty(&lgr->llc_event_q)) { 16508c2ecf20Sopenharmony_ci qentry = list_first_entry(&lgr->llc_event_q, 16518c2ecf20Sopenharmony_ci struct smc_llc_qentry, list); 16528c2ecf20Sopenharmony_ci list_del_init(&qentry->list); 16538c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_event_q_lock); 16548c2ecf20Sopenharmony_ci smc_llc_event_handler(qentry); 16558c2ecf20Sopenharmony_ci goto again; 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci spin_unlock_bh(&lgr->llc_event_q_lock); 16588c2ecf20Sopenharmony_ci} 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci/* process llc responses in tasklet context */ 16618c2ecf20Sopenharmony_cistatic void smc_llc_rx_response(struct smc_link *link, 16628c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci enum smc_llc_flowtype flowtype = link->lgr->llc_flow_lcl.type; 16658c2ecf20Sopenharmony_ci struct smc_llc_flow *flow = &link->lgr->llc_flow_lcl; 16668c2ecf20Sopenharmony_ci u8 llc_type = qentry->msg.raw.hdr.common.type; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci switch (llc_type) { 16698c2ecf20Sopenharmony_ci case SMC_LLC_TEST_LINK: 16708c2ecf20Sopenharmony_ci if (smc_link_active(link)) 16718c2ecf20Sopenharmony_ci complete(&link->llc_testlink_resp); 16728c2ecf20Sopenharmony_ci break; 16738c2ecf20Sopenharmony_ci case SMC_LLC_ADD_LINK: 16748c2ecf20Sopenharmony_ci case SMC_LLC_ADD_LINK_CONT: 16758c2ecf20Sopenharmony_ci case SMC_LLC_CONFIRM_LINK: 16768c2ecf20Sopenharmony_ci if (flowtype != SMC_LLC_FLOW_ADD_LINK || flow->qentry) 16778c2ecf20Sopenharmony_ci break; /* drop out-of-flow response */ 16788c2ecf20Sopenharmony_ci goto assign; 16798c2ecf20Sopenharmony_ci case SMC_LLC_DELETE_LINK: 16808c2ecf20Sopenharmony_ci if (flowtype != SMC_LLC_FLOW_DEL_LINK || flow->qentry) 16818c2ecf20Sopenharmony_ci break; /* drop out-of-flow response */ 16828c2ecf20Sopenharmony_ci goto assign; 16838c2ecf20Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY: 16848c2ecf20Sopenharmony_ci case SMC_LLC_DELETE_RKEY: 16858c2ecf20Sopenharmony_ci if (flowtype != SMC_LLC_FLOW_RKEY || flow->qentry) 16868c2ecf20Sopenharmony_ci break; /* drop out-of-flow response */ 16878c2ecf20Sopenharmony_ci goto assign; 16888c2ecf20Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY_CONT: 16898c2ecf20Sopenharmony_ci /* not used because max links is 3 */ 16908c2ecf20Sopenharmony_ci break; 16918c2ecf20Sopenharmony_ci default: 16928c2ecf20Sopenharmony_ci smc_llc_protocol_violation(link->lgr, llc_type); 16938c2ecf20Sopenharmony_ci break; 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci kfree(qentry); 16968c2ecf20Sopenharmony_ci return; 16978c2ecf20Sopenharmony_ciassign: 16988c2ecf20Sopenharmony_ci /* assign responses to the local flow, we requested them */ 16998c2ecf20Sopenharmony_ci smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry); 17008c2ecf20Sopenharmony_ci wake_up(&link->lgr->llc_msg_waiter); 17018c2ecf20Sopenharmony_ci} 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_cistatic void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc) 17048c2ecf20Sopenharmony_ci{ 17058c2ecf20Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 17068c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry; 17078c2ecf20Sopenharmony_ci unsigned long flags; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC); 17108c2ecf20Sopenharmony_ci if (!qentry) 17118c2ecf20Sopenharmony_ci return; 17128c2ecf20Sopenharmony_ci qentry->link = link; 17138c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&qentry->list); 17148c2ecf20Sopenharmony_ci memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg)); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci /* process responses immediately */ 17178c2ecf20Sopenharmony_ci if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) { 17188c2ecf20Sopenharmony_ci smc_llc_rx_response(link, qentry); 17198c2ecf20Sopenharmony_ci return; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci /* add requests to event queue */ 17238c2ecf20Sopenharmony_ci spin_lock_irqsave(&lgr->llc_event_q_lock, flags); 17248c2ecf20Sopenharmony_ci list_add_tail(&qentry->list, &lgr->llc_event_q); 17258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags); 17268c2ecf20Sopenharmony_ci queue_work(system_highpri_wq, &lgr->llc_event_work); 17278c2ecf20Sopenharmony_ci} 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci/* copy received msg and add it to the event queue */ 17308c2ecf20Sopenharmony_cistatic void smc_llc_rx_handler(struct ib_wc *wc, void *buf) 17318c2ecf20Sopenharmony_ci{ 17328c2ecf20Sopenharmony_ci struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 17338c2ecf20Sopenharmony_ci union smc_llc_msg *llc = buf; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci if (wc->byte_len < sizeof(*llc)) 17368c2ecf20Sopenharmony_ci return; /* short message */ 17378c2ecf20Sopenharmony_ci if (llc->raw.hdr.length != sizeof(*llc)) 17388c2ecf20Sopenharmony_ci return; /* invalid message */ 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci smc_llc_enqueue(link, llc); 17418c2ecf20Sopenharmony_ci} 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci/***************************** worker, utils *********************************/ 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic void smc_llc_testlink_work(struct work_struct *work) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci struct smc_link *link = container_of(to_delayed_work(work), 17488c2ecf20Sopenharmony_ci struct smc_link, llc_testlink_wrk); 17498c2ecf20Sopenharmony_ci unsigned long next_interval; 17508c2ecf20Sopenharmony_ci unsigned long expire_time; 17518c2ecf20Sopenharmony_ci u8 user_data[16] = { 0 }; 17528c2ecf20Sopenharmony_ci int rc; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci if (!smc_link_active(link)) 17558c2ecf20Sopenharmony_ci return; /* don't reschedule worker */ 17568c2ecf20Sopenharmony_ci expire_time = link->wr_rx_tstamp + link->llc_testlink_time; 17578c2ecf20Sopenharmony_ci if (time_is_after_jiffies(expire_time)) { 17588c2ecf20Sopenharmony_ci next_interval = expire_time - jiffies; 17598c2ecf20Sopenharmony_ci goto out; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci reinit_completion(&link->llc_testlink_resp); 17628c2ecf20Sopenharmony_ci smc_llc_send_test_link(link, user_data); 17638c2ecf20Sopenharmony_ci /* receive TEST LINK response over RoCE fabric */ 17648c2ecf20Sopenharmony_ci rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp, 17658c2ecf20Sopenharmony_ci SMC_LLC_WAIT_TIME); 17668c2ecf20Sopenharmony_ci if (!smc_link_active(link)) 17678c2ecf20Sopenharmony_ci return; /* link state changed */ 17688c2ecf20Sopenharmony_ci if (rc <= 0) { 17698c2ecf20Sopenharmony_ci smcr_link_down_cond_sched(link); 17708c2ecf20Sopenharmony_ci return; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci next_interval = link->llc_testlink_time; 17738c2ecf20Sopenharmony_ciout: 17748c2ecf20Sopenharmony_ci schedule_delayed_work(&link->llc_testlink_wrk, next_interval); 17758c2ecf20Sopenharmony_ci} 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_civoid smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc) 17788c2ecf20Sopenharmony_ci{ 17798c2ecf20Sopenharmony_ci struct net *net = sock_net(smc->clcsock->sk); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci INIT_WORK(&lgr->llc_event_work, smc_llc_event_work); 17828c2ecf20Sopenharmony_ci INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work); 17838c2ecf20Sopenharmony_ci INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work); 17848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lgr->llc_event_q); 17858c2ecf20Sopenharmony_ci spin_lock_init(&lgr->llc_event_q_lock); 17868c2ecf20Sopenharmony_ci spin_lock_init(&lgr->llc_flow_lock); 17878c2ecf20Sopenharmony_ci init_waitqueue_head(&lgr->llc_flow_waiter); 17888c2ecf20Sopenharmony_ci init_waitqueue_head(&lgr->llc_msg_waiter); 17898c2ecf20Sopenharmony_ci mutex_init(&lgr->llc_conf_mutex); 17908c2ecf20Sopenharmony_ci lgr->llc_testlink_time = READ_ONCE(net->ipv4.sysctl_tcp_keepalive_time); 17918c2ecf20Sopenharmony_ci} 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci/* called after lgr was removed from lgr_list */ 17948c2ecf20Sopenharmony_civoid smc_llc_lgr_clear(struct smc_link_group *lgr) 17958c2ecf20Sopenharmony_ci{ 17968c2ecf20Sopenharmony_ci smc_llc_event_flush(lgr); 17978c2ecf20Sopenharmony_ci wake_up_all(&lgr->llc_flow_waiter); 17988c2ecf20Sopenharmony_ci wake_up_all(&lgr->llc_msg_waiter); 17998c2ecf20Sopenharmony_ci cancel_work_sync(&lgr->llc_event_work); 18008c2ecf20Sopenharmony_ci cancel_work_sync(&lgr->llc_add_link_work); 18018c2ecf20Sopenharmony_ci cancel_work_sync(&lgr->llc_del_link_work); 18028c2ecf20Sopenharmony_ci if (lgr->delayed_event) { 18038c2ecf20Sopenharmony_ci kfree(lgr->delayed_event); 18048c2ecf20Sopenharmony_ci lgr->delayed_event = NULL; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci} 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ciint smc_llc_link_init(struct smc_link *link) 18098c2ecf20Sopenharmony_ci{ 18108c2ecf20Sopenharmony_ci init_completion(&link->llc_testlink_resp); 18118c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); 18128c2ecf20Sopenharmony_ci return 0; 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_civoid smc_llc_link_active(struct smc_link *link) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci pr_warn_ratelimited("smc: SMC-R lg %*phN link added: id %*phN, " 18188c2ecf20Sopenharmony_ci "peerid %*phN, ibdev %s, ibport %d\n", 18198c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &link->lgr->id, 18208c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &link->link_uid, 18218c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &link->peer_link_uid, 18228c2ecf20Sopenharmony_ci link->smcibdev->ibdev->name, link->ibport); 18238c2ecf20Sopenharmony_ci link->state = SMC_LNK_ACTIVE; 18248c2ecf20Sopenharmony_ci if (link->lgr->llc_testlink_time) { 18258c2ecf20Sopenharmony_ci link->llc_testlink_time = link->lgr->llc_testlink_time; 18268c2ecf20Sopenharmony_ci schedule_delayed_work(&link->llc_testlink_wrk, 18278c2ecf20Sopenharmony_ci link->llc_testlink_time); 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci/* called in worker context */ 18328c2ecf20Sopenharmony_civoid smc_llc_link_clear(struct smc_link *link, bool log) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci if (log) 18358c2ecf20Sopenharmony_ci pr_warn_ratelimited("smc: SMC-R lg %*phN link removed: id %*phN" 18368c2ecf20Sopenharmony_ci ", peerid %*phN, ibdev %s, ibport %d\n", 18378c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &link->lgr->id, 18388c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &link->link_uid, 18398c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE, &link->peer_link_uid, 18408c2ecf20Sopenharmony_ci link->smcibdev->ibdev->name, link->ibport); 18418c2ecf20Sopenharmony_ci complete(&link->llc_testlink_resp); 18428c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&link->llc_testlink_wrk); 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci/* register a new rtoken at the remote peer (for all links) */ 18468c2ecf20Sopenharmony_ciint smc_llc_do_confirm_rkey(struct smc_link *send_link, 18478c2ecf20Sopenharmony_ci struct smc_buf_desc *rmb_desc) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct smc_link_group *lgr = send_link->lgr; 18508c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 18518c2ecf20Sopenharmony_ci int rc = 0; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci rc = smc_llc_send_confirm_rkey(send_link, rmb_desc); 18548c2ecf20Sopenharmony_ci if (rc) 18558c2ecf20Sopenharmony_ci goto out; 18568c2ecf20Sopenharmony_ci /* receive CONFIRM RKEY response from server over RoCE fabric */ 18578c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 18588c2ecf20Sopenharmony_ci SMC_LLC_CONFIRM_RKEY); 18598c2ecf20Sopenharmony_ci if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 18608c2ecf20Sopenharmony_ci rc = -EFAULT; 18618c2ecf20Sopenharmony_ciout: 18628c2ecf20Sopenharmony_ci if (qentry) 18638c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 18648c2ecf20Sopenharmony_ci return rc; 18658c2ecf20Sopenharmony_ci} 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci/* unregister an rtoken at the remote peer */ 18688c2ecf20Sopenharmony_ciint smc_llc_do_delete_rkey(struct smc_link_group *lgr, 18698c2ecf20Sopenharmony_ci struct smc_buf_desc *rmb_desc) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 18728c2ecf20Sopenharmony_ci struct smc_link *send_link; 18738c2ecf20Sopenharmony_ci int rc = 0; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci send_link = smc_llc_usable_link(lgr); 18768c2ecf20Sopenharmony_ci if (!send_link) 18778c2ecf20Sopenharmony_ci return -ENOLINK; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci /* protected by llc_flow control */ 18808c2ecf20Sopenharmony_ci rc = smc_llc_send_delete_rkey(send_link, rmb_desc); 18818c2ecf20Sopenharmony_ci if (rc) 18828c2ecf20Sopenharmony_ci goto out; 18838c2ecf20Sopenharmony_ci /* receive DELETE RKEY response from server over RoCE fabric */ 18848c2ecf20Sopenharmony_ci qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 18858c2ecf20Sopenharmony_ci SMC_LLC_DELETE_RKEY); 18868c2ecf20Sopenharmony_ci if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 18878c2ecf20Sopenharmony_ci rc = -EFAULT; 18888c2ecf20Sopenharmony_ciout: 18898c2ecf20Sopenharmony_ci if (qentry) 18908c2ecf20Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 18918c2ecf20Sopenharmony_ci return rc; 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_civoid smc_llc_link_set_uid(struct smc_link *link) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci __be32 link_uid; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci link_uid = htonl(*((u32 *)link->lgr->id) + link->link_id); 18998c2ecf20Sopenharmony_ci memcpy(link->link_uid, &link_uid, SMC_LGR_ID_SIZE); 19008c2ecf20Sopenharmony_ci} 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci/* save peers link user id, used for debug purposes */ 19038c2ecf20Sopenharmony_civoid smc_llc_save_peer_uid(struct smc_llc_qentry *qentry) 19048c2ecf20Sopenharmony_ci{ 19058c2ecf20Sopenharmony_ci memcpy(qentry->link->peer_link_uid, qentry->msg.confirm_link.link_uid, 19068c2ecf20Sopenharmony_ci SMC_LGR_ID_SIZE); 19078c2ecf20Sopenharmony_ci} 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci/* evaluate confirm link request or response */ 19108c2ecf20Sopenharmony_ciint smc_llc_eval_conf_link(struct smc_llc_qentry *qentry, 19118c2ecf20Sopenharmony_ci enum smc_llc_reqresp type) 19128c2ecf20Sopenharmony_ci{ 19138c2ecf20Sopenharmony_ci if (type == SMC_LLC_REQ) { /* SMC server assigns link_id */ 19148c2ecf20Sopenharmony_ci qentry->link->link_id = qentry->msg.confirm_link.link_num; 19158c2ecf20Sopenharmony_ci smc_llc_link_set_uid(qentry->link); 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC)) 19188c2ecf20Sopenharmony_ci return -ENOTSUPP; 19198c2ecf20Sopenharmony_ci return 0; 19208c2ecf20Sopenharmony_ci} 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci/***************************** init, exit, misc ******************************/ 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic struct smc_wr_rx_handler smc_llc_rx_handlers[] = { 19258c2ecf20Sopenharmony_ci { 19268c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19278c2ecf20Sopenharmony_ci .type = SMC_LLC_CONFIRM_LINK 19288c2ecf20Sopenharmony_ci }, 19298c2ecf20Sopenharmony_ci { 19308c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19318c2ecf20Sopenharmony_ci .type = SMC_LLC_TEST_LINK 19328c2ecf20Sopenharmony_ci }, 19338c2ecf20Sopenharmony_ci { 19348c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19358c2ecf20Sopenharmony_ci .type = SMC_LLC_ADD_LINK 19368c2ecf20Sopenharmony_ci }, 19378c2ecf20Sopenharmony_ci { 19388c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19398c2ecf20Sopenharmony_ci .type = SMC_LLC_ADD_LINK_CONT 19408c2ecf20Sopenharmony_ci }, 19418c2ecf20Sopenharmony_ci { 19428c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19438c2ecf20Sopenharmony_ci .type = SMC_LLC_DELETE_LINK 19448c2ecf20Sopenharmony_ci }, 19458c2ecf20Sopenharmony_ci { 19468c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19478c2ecf20Sopenharmony_ci .type = SMC_LLC_CONFIRM_RKEY 19488c2ecf20Sopenharmony_ci }, 19498c2ecf20Sopenharmony_ci { 19508c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19518c2ecf20Sopenharmony_ci .type = SMC_LLC_CONFIRM_RKEY_CONT 19528c2ecf20Sopenharmony_ci }, 19538c2ecf20Sopenharmony_ci { 19548c2ecf20Sopenharmony_ci .handler = smc_llc_rx_handler, 19558c2ecf20Sopenharmony_ci .type = SMC_LLC_DELETE_RKEY 19568c2ecf20Sopenharmony_ci }, 19578c2ecf20Sopenharmony_ci { 19588c2ecf20Sopenharmony_ci .handler = NULL, 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci}; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ciint __init smc_llc_init(void) 19638c2ecf20Sopenharmony_ci{ 19648c2ecf20Sopenharmony_ci struct smc_wr_rx_handler *handler; 19658c2ecf20Sopenharmony_ci int rc = 0; 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci for (handler = smc_llc_rx_handlers; handler->handler; handler++) { 19688c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&handler->list); 19698c2ecf20Sopenharmony_ci rc = smc_wr_rx_register_handler(handler); 19708c2ecf20Sopenharmony_ci if (rc) 19718c2ecf20Sopenharmony_ci break; 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci return rc; 19748c2ecf20Sopenharmony_ci} 1975