162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Shared Memory Communications over RDMA (SMC-R) and RoCE 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Link Layer Control (LLC) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright IBM Corp. 2016 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Author(s): Klaus Wacker <Klaus.Wacker@de.ibm.com> 1062306a36Sopenharmony_ci * Ursula Braun <ubraun@linux.vnet.ibm.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <net/tcp.h> 1462306a36Sopenharmony_ci#include <rdma/ib_verbs.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "smc.h" 1762306a36Sopenharmony_ci#include "smc_core.h" 1862306a36Sopenharmony_ci#include "smc_clc.h" 1962306a36Sopenharmony_ci#include "smc_llc.h" 2062306a36Sopenharmony_ci#include "smc_pnet.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define SMC_LLC_DATA_LEN 40 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct smc_llc_hdr { 2562306a36Sopenharmony_ci struct smc_wr_rx_hdr common; 2662306a36Sopenharmony_ci union { 2762306a36Sopenharmony_ci struct { 2862306a36Sopenharmony_ci u8 length; /* 44 */ 2962306a36Sopenharmony_ci #if defined(__BIG_ENDIAN_BITFIELD) 3062306a36Sopenharmony_ci u8 reserved:4, 3162306a36Sopenharmony_ci add_link_rej_rsn:4; 3262306a36Sopenharmony_ci#elif defined(__LITTLE_ENDIAN_BITFIELD) 3362306a36Sopenharmony_ci u8 add_link_rej_rsn:4, 3462306a36Sopenharmony_ci reserved:4; 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_ci }; 3762306a36Sopenharmony_ci u16 length_v2; /* 44 - 8192*/ 3862306a36Sopenharmony_ci }; 3962306a36Sopenharmony_ci u8 flags; 4062306a36Sopenharmony_ci} __packed; /* format defined in 4162306a36Sopenharmony_ci * IBM Shared Memory Communications Version 2 4262306a36Sopenharmony_ci * (https://www.ibm.com/support/pages/node/6326337) 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define SMC_LLC_FLAG_NO_RMBE_EYEC 0x03 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct smc_llc_msg_confirm_link { /* type 0x01 */ 4862306a36Sopenharmony_ci struct smc_llc_hdr hd; 4962306a36Sopenharmony_ci u8 sender_mac[ETH_ALEN]; 5062306a36Sopenharmony_ci u8 sender_gid[SMC_GID_SIZE]; 5162306a36Sopenharmony_ci u8 sender_qp_num[3]; 5262306a36Sopenharmony_ci u8 link_num; 5362306a36Sopenharmony_ci u8 link_uid[SMC_LGR_ID_SIZE]; 5462306a36Sopenharmony_ci u8 max_links; 5562306a36Sopenharmony_ci u8 max_conns; 5662306a36Sopenharmony_ci u8 reserved[8]; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define SMC_LLC_FLAG_ADD_LNK_REJ 0x40 6062306a36Sopenharmony_ci#define SMC_LLC_REJ_RSN_NO_ALT_PATH 1 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct smc_llc_msg_add_link { /* type 0x02 */ 6362306a36Sopenharmony_ci struct smc_llc_hdr hd; 6462306a36Sopenharmony_ci u8 sender_mac[ETH_ALEN]; 6562306a36Sopenharmony_ci u8 reserved2[2]; 6662306a36Sopenharmony_ci u8 sender_gid[SMC_GID_SIZE]; 6762306a36Sopenharmony_ci u8 sender_qp_num[3]; 6862306a36Sopenharmony_ci u8 link_num; 6962306a36Sopenharmony_ci#if defined(__BIG_ENDIAN_BITFIELD) 7062306a36Sopenharmony_ci u8 reserved3 : 4, 7162306a36Sopenharmony_ci qp_mtu : 4; 7262306a36Sopenharmony_ci#elif defined(__LITTLE_ENDIAN_BITFIELD) 7362306a36Sopenharmony_ci u8 qp_mtu : 4, 7462306a36Sopenharmony_ci reserved3 : 4; 7562306a36Sopenharmony_ci#endif 7662306a36Sopenharmony_ci u8 initial_psn[3]; 7762306a36Sopenharmony_ci u8 reserved[8]; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct smc_llc_msg_add_link_cont_rt { 8162306a36Sopenharmony_ci __be32 rmb_key; 8262306a36Sopenharmony_ci __be32 rmb_key_new; 8362306a36Sopenharmony_ci __be64 rmb_vaddr_new; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct smc_llc_msg_add_link_v2_ext { 8762306a36Sopenharmony_ci#if defined(__BIG_ENDIAN_BITFIELD) 8862306a36Sopenharmony_ci u8 v2_direct : 1, 8962306a36Sopenharmony_ci reserved : 7; 9062306a36Sopenharmony_ci#elif defined(__LITTLE_ENDIAN_BITFIELD) 9162306a36Sopenharmony_ci u8 reserved : 7, 9262306a36Sopenharmony_ci v2_direct : 1; 9362306a36Sopenharmony_ci#endif 9462306a36Sopenharmony_ci u8 reserved2; 9562306a36Sopenharmony_ci u8 client_target_gid[SMC_GID_SIZE]; 9662306a36Sopenharmony_ci u8 reserved3[8]; 9762306a36Sopenharmony_ci u16 num_rkeys; 9862306a36Sopenharmony_ci struct smc_llc_msg_add_link_cont_rt rt[]; 9962306a36Sopenharmony_ci} __packed; /* format defined in 10062306a36Sopenharmony_ci * IBM Shared Memory Communications Version 2 10162306a36Sopenharmony_ci * (https://www.ibm.com/support/pages/node/6326337) 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct smc_llc_msg_req_add_link_v2 { 10562306a36Sopenharmony_ci struct smc_llc_hdr hd; 10662306a36Sopenharmony_ci u8 reserved[20]; 10762306a36Sopenharmony_ci u8 gid_cnt; 10862306a36Sopenharmony_ci u8 reserved2[3]; 10962306a36Sopenharmony_ci u8 gid[][SMC_GID_SIZE]; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define SMC_LLC_RKEYS_PER_CONT_MSG 2 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct smc_llc_msg_add_link_cont { /* type 0x03 */ 11562306a36Sopenharmony_ci struct smc_llc_hdr hd; 11662306a36Sopenharmony_ci u8 link_num; 11762306a36Sopenharmony_ci u8 num_rkeys; 11862306a36Sopenharmony_ci u8 reserved2[2]; 11962306a36Sopenharmony_ci struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG]; 12062306a36Sopenharmony_ci u8 reserved[4]; 12162306a36Sopenharmony_ci} __packed; /* format defined in RFC7609 */ 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define SMC_LLC_FLAG_DEL_LINK_ALL 0x40 12462306a36Sopenharmony_ci#define SMC_LLC_FLAG_DEL_LINK_ORDERLY 0x20 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct smc_llc_msg_del_link { /* type 0x04 */ 12762306a36Sopenharmony_ci struct smc_llc_hdr hd; 12862306a36Sopenharmony_ci u8 link_num; 12962306a36Sopenharmony_ci __be32 reason; 13062306a36Sopenharmony_ci u8 reserved[35]; 13162306a36Sopenharmony_ci} __packed; /* format defined in RFC7609 */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistruct smc_llc_msg_test_link { /* type 0x07 */ 13462306a36Sopenharmony_ci struct smc_llc_hdr hd; 13562306a36Sopenharmony_ci u8 user_data[16]; 13662306a36Sopenharmony_ci u8 reserved[24]; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistruct smc_rmb_rtoken { 14062306a36Sopenharmony_ci union { 14162306a36Sopenharmony_ci u8 num_rkeys; /* first rtoken byte of CONFIRM LINK msg */ 14262306a36Sopenharmony_ci /* is actually the num of rtokens, first */ 14362306a36Sopenharmony_ci /* rtoken is always for the current link */ 14462306a36Sopenharmony_ci u8 link_id; /* link id of the rtoken */ 14562306a36Sopenharmony_ci }; 14662306a36Sopenharmony_ci __be32 rmb_key; 14762306a36Sopenharmony_ci __be64 rmb_vaddr; 14862306a36Sopenharmony_ci} __packed; /* format defined in RFC7609 */ 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#define SMC_LLC_RKEYS_PER_MSG 3 15162306a36Sopenharmony_ci#define SMC_LLC_RKEYS_PER_MSG_V2 255 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistruct smc_llc_msg_confirm_rkey { /* type 0x06 */ 15462306a36Sopenharmony_ci struct smc_llc_hdr hd; 15562306a36Sopenharmony_ci struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; 15662306a36Sopenharmony_ci u8 reserved; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define SMC_LLC_DEL_RKEY_MAX 8 16062306a36Sopenharmony_ci#define SMC_LLC_FLAG_RKEY_RETRY 0x10 16162306a36Sopenharmony_ci#define SMC_LLC_FLAG_RKEY_NEG 0x20 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistruct smc_llc_msg_delete_rkey { /* type 0x09 */ 16462306a36Sopenharmony_ci struct smc_llc_hdr hd; 16562306a36Sopenharmony_ci u8 num_rkeys; 16662306a36Sopenharmony_ci u8 err_mask; 16762306a36Sopenharmony_ci u8 reserved[2]; 16862306a36Sopenharmony_ci __be32 rkey[8]; 16962306a36Sopenharmony_ci u8 reserved2[4]; 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistruct smc_llc_msg_delete_rkey_v2 { /* type 0x29 */ 17362306a36Sopenharmony_ci struct smc_llc_hdr hd; 17462306a36Sopenharmony_ci u8 num_rkeys; 17562306a36Sopenharmony_ci u8 num_inval_rkeys; 17662306a36Sopenharmony_ci u8 reserved[2]; 17762306a36Sopenharmony_ci __be32 rkey[]; 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciunion smc_llc_msg { 18162306a36Sopenharmony_ci struct smc_llc_msg_confirm_link confirm_link; 18262306a36Sopenharmony_ci struct smc_llc_msg_add_link add_link; 18362306a36Sopenharmony_ci struct smc_llc_msg_req_add_link_v2 req_add_link; 18462306a36Sopenharmony_ci struct smc_llc_msg_add_link_cont add_link_cont; 18562306a36Sopenharmony_ci struct smc_llc_msg_del_link delete_link; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci struct smc_llc_msg_confirm_rkey confirm_rkey; 18862306a36Sopenharmony_ci struct smc_llc_msg_delete_rkey delete_rkey; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci struct smc_llc_msg_test_link test_link; 19162306a36Sopenharmony_ci struct { 19262306a36Sopenharmony_ci struct smc_llc_hdr hdr; 19362306a36Sopenharmony_ci u8 data[SMC_LLC_DATA_LEN]; 19462306a36Sopenharmony_ci } raw; 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#define SMC_LLC_FLAG_RESP 0x80 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistruct smc_llc_qentry { 20062306a36Sopenharmony_ci struct list_head list; 20162306a36Sopenharmony_ci struct smc_link *link; 20262306a36Sopenharmony_ci union smc_llc_msg msg; 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistruct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct smc_llc_qentry *qentry = flow->qentry; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci flow->qentry = NULL; 21262306a36Sopenharmony_ci return qentry; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_civoid smc_llc_flow_qentry_del(struct smc_llc_flow *flow) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (flow->qentry) { 22062306a36Sopenharmony_ci qentry = flow->qentry; 22162306a36Sopenharmony_ci flow->qentry = NULL; 22262306a36Sopenharmony_ci kfree(qentry); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow, 22762306a36Sopenharmony_ci struct smc_llc_qentry *qentry) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci flow->qentry = qentry; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void smc_llc_flow_parallel(struct smc_link_group *lgr, u8 flow_type, 23362306a36Sopenharmony_ci struct smc_llc_qentry *qentry) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci u8 msg_type = qentry->msg.raw.hdr.common.llc_type; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if ((msg_type == SMC_LLC_ADD_LINK || msg_type == SMC_LLC_DELETE_LINK) && 23862306a36Sopenharmony_ci flow_type != msg_type && !lgr->delayed_event) { 23962306a36Sopenharmony_ci lgr->delayed_event = qentry; 24062306a36Sopenharmony_ci return; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci /* drop parallel or already-in-progress llc requests */ 24362306a36Sopenharmony_ci if (flow_type != msg_type) 24462306a36Sopenharmony_ci pr_warn_once("smc: SMC-R lg %*phN net %llu dropped parallel " 24562306a36Sopenharmony_ci "LLC msg: msg %d flow %d role %d\n", 24662306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &lgr->id, 24762306a36Sopenharmony_ci lgr->net->net_cookie, 24862306a36Sopenharmony_ci qentry->msg.raw.hdr.common.type, 24962306a36Sopenharmony_ci flow_type, lgr->role); 25062306a36Sopenharmony_ci kfree(qentry); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* try to start a new llc flow, initiated by an incoming llc msg */ 25462306a36Sopenharmony_cistatic bool smc_llc_flow_start(struct smc_llc_flow *flow, 25562306a36Sopenharmony_ci struct smc_llc_qentry *qentry) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct smc_link_group *lgr = qentry->link->lgr; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_lock_bh(&lgr->llc_flow_lock); 26062306a36Sopenharmony_ci if (flow->type) { 26162306a36Sopenharmony_ci /* a flow is already active */ 26262306a36Sopenharmony_ci smc_llc_flow_parallel(lgr, flow->type, qentry); 26362306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 26462306a36Sopenharmony_ci return false; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci switch (qentry->msg.raw.hdr.common.llc_type) { 26762306a36Sopenharmony_ci case SMC_LLC_ADD_LINK: 26862306a36Sopenharmony_ci flow->type = SMC_LLC_FLOW_ADD_LINK; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case SMC_LLC_DELETE_LINK: 27162306a36Sopenharmony_ci flow->type = SMC_LLC_FLOW_DEL_LINK; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY: 27462306a36Sopenharmony_ci case SMC_LLC_DELETE_RKEY: 27562306a36Sopenharmony_ci flow->type = SMC_LLC_FLOW_RKEY; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci default: 27862306a36Sopenharmony_ci flow->type = SMC_LLC_FLOW_NONE; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci smc_llc_flow_qentry_set(flow, qentry); 28162306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 28262306a36Sopenharmony_ci return true; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* start a new local llc flow, wait till current flow finished */ 28662306a36Sopenharmony_ciint smc_llc_flow_initiate(struct smc_link_group *lgr, 28762306a36Sopenharmony_ci enum smc_llc_flowtype type) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE; 29062306a36Sopenharmony_ci int rc; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* all flows except confirm_rkey and delete_rkey are exclusive, 29362306a36Sopenharmony_ci * confirm/delete rkey flows can run concurrently (local and remote) 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (type == SMC_LLC_FLOW_RKEY) 29662306a36Sopenharmony_ci allowed_remote = SMC_LLC_FLOW_RKEY; 29762306a36Sopenharmony_ciagain: 29862306a36Sopenharmony_ci if (list_empty(&lgr->list)) 29962306a36Sopenharmony_ci return -ENODEV; 30062306a36Sopenharmony_ci spin_lock_bh(&lgr->llc_flow_lock); 30162306a36Sopenharmony_ci if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 30262306a36Sopenharmony_ci (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 30362306a36Sopenharmony_ci lgr->llc_flow_rmt.type == allowed_remote)) { 30462306a36Sopenharmony_ci lgr->llc_flow_lcl.type = type; 30562306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 30962306a36Sopenharmony_ci rc = wait_event_timeout(lgr->llc_flow_waiter, (list_empty(&lgr->list) || 31062306a36Sopenharmony_ci (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && 31162306a36Sopenharmony_ci (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || 31262306a36Sopenharmony_ci lgr->llc_flow_rmt.type == allowed_remote))), 31362306a36Sopenharmony_ci SMC_LLC_WAIT_TIME * 10); 31462306a36Sopenharmony_ci if (!rc) 31562306a36Sopenharmony_ci return -ETIMEDOUT; 31662306a36Sopenharmony_ci goto again; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* finish the current llc flow */ 32062306a36Sopenharmony_civoid smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci spin_lock_bh(&lgr->llc_flow_lock); 32362306a36Sopenharmony_ci memset(flow, 0, sizeof(*flow)); 32462306a36Sopenharmony_ci flow->type = SMC_LLC_FLOW_NONE; 32562306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_flow_lock); 32662306a36Sopenharmony_ci if (!list_empty(&lgr->list) && lgr->delayed_event && 32762306a36Sopenharmony_ci flow == &lgr->llc_flow_lcl) 32862306a36Sopenharmony_ci schedule_work(&lgr->llc_event_work); 32962306a36Sopenharmony_ci else 33062306a36Sopenharmony_ci wake_up(&lgr->llc_flow_waiter); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* lnk is optional and used for early wakeup when link goes down, useful in 33462306a36Sopenharmony_ci * cases where we wait for a response on the link after we sent a request 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistruct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr, 33762306a36Sopenharmony_ci struct smc_link *lnk, 33862306a36Sopenharmony_ci int time_out, u8 exp_msg) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct smc_llc_flow *flow = &lgr->llc_flow_lcl; 34162306a36Sopenharmony_ci u8 rcv_msg; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci wait_event_timeout(lgr->llc_msg_waiter, 34462306a36Sopenharmony_ci (flow->qentry || 34562306a36Sopenharmony_ci (lnk && !smc_link_usable(lnk)) || 34662306a36Sopenharmony_ci list_empty(&lgr->list)), 34762306a36Sopenharmony_ci time_out); 34862306a36Sopenharmony_ci if (!flow->qentry || 34962306a36Sopenharmony_ci (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) { 35062306a36Sopenharmony_ci smc_llc_flow_qentry_del(flow); 35162306a36Sopenharmony_ci goto out; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci rcv_msg = flow->qentry->msg.raw.hdr.common.llc_type; 35462306a36Sopenharmony_ci if (exp_msg && rcv_msg != exp_msg) { 35562306a36Sopenharmony_ci if (exp_msg == SMC_LLC_ADD_LINK && 35662306a36Sopenharmony_ci rcv_msg == SMC_LLC_DELETE_LINK) { 35762306a36Sopenharmony_ci /* flow_start will delay the unexpected msg */ 35862306a36Sopenharmony_ci smc_llc_flow_start(&lgr->llc_flow_lcl, 35962306a36Sopenharmony_ci smc_llc_flow_qentry_clr(flow)); 36062306a36Sopenharmony_ci return NULL; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci pr_warn_once("smc: SMC-R lg %*phN net %llu dropped unexpected LLC msg: " 36362306a36Sopenharmony_ci "msg %d exp %d flow %d role %d flags %x\n", 36462306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &lgr->id, lgr->net->net_cookie, 36562306a36Sopenharmony_ci rcv_msg, exp_msg, 36662306a36Sopenharmony_ci flow->type, lgr->role, 36762306a36Sopenharmony_ci flow->qentry->msg.raw.hdr.flags); 36862306a36Sopenharmony_ci smc_llc_flow_qentry_del(flow); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ciout: 37162306a36Sopenharmony_ci return flow->qentry; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/********************************** send *************************************/ 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistruct smc_llc_tx_pend { 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* handler for send/transmission completion of an LLC msg */ 38062306a36Sopenharmony_cistatic void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend, 38162306a36Sopenharmony_ci struct smc_link *link, 38262306a36Sopenharmony_ci enum ib_wc_status wc_status) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci /* future work: handle wc_status error for recovery and failover */ 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/** 38862306a36Sopenharmony_ci * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits 38962306a36Sopenharmony_ci * @link: Pointer to SMC link used for sending LLC control message. 39062306a36Sopenharmony_ci * @wr_buf: Out variable returning pointer to work request payload buffer. 39162306a36Sopenharmony_ci * @pend: Out variable returning pointer to private pending WR tracking. 39262306a36Sopenharmony_ci * It's the context the transmit complete handler will get. 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * Reserves and pre-fills an entry for a pending work request send/tx. 39562306a36Sopenharmony_ci * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx. 39662306a36Sopenharmony_ci * Can sleep due to smc_get_ctrl_buf (if not in softirq context). 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * Return: 0 on success, otherwise an error value. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_cistatic int smc_llc_add_pending_send(struct smc_link *link, 40162306a36Sopenharmony_ci struct smc_wr_buf **wr_buf, 40262306a36Sopenharmony_ci struct smc_wr_tx_pend_priv **pend) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int rc; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL, 40762306a36Sopenharmony_ci pend); 40862306a36Sopenharmony_ci if (rc < 0) 40962306a36Sopenharmony_ci return rc; 41062306a36Sopenharmony_ci BUILD_BUG_ON_MSG( 41162306a36Sopenharmony_ci sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE, 41262306a36Sopenharmony_ci "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)"); 41362306a36Sopenharmony_ci BUILD_BUG_ON_MSG( 41462306a36Sopenharmony_ci sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE, 41562306a36Sopenharmony_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()"); 41662306a36Sopenharmony_ci BUILD_BUG_ON_MSG( 41762306a36Sopenharmony_ci sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, 41862306a36Sopenharmony_ci "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)"); 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int smc_llc_add_pending_send_v2(struct smc_link *link, 42362306a36Sopenharmony_ci struct smc_wr_v2_buf **wr_buf, 42462306a36Sopenharmony_ci struct smc_wr_tx_pend_priv **pend) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int rc; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci rc = smc_wr_tx_get_v2_slot(link, smc_llc_tx_handler, wr_buf, pend); 42962306a36Sopenharmony_ci if (rc < 0) 43062306a36Sopenharmony_ci return rc; 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void smc_llc_init_msg_hdr(struct smc_llc_hdr *hdr, 43562306a36Sopenharmony_ci struct smc_link_group *lgr, size_t len) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2) { 43862306a36Sopenharmony_ci hdr->common.llc_version = SMC_V2; 43962306a36Sopenharmony_ci hdr->length_v2 = len; 44062306a36Sopenharmony_ci } else { 44162306a36Sopenharmony_ci hdr->common.llc_version = 0; 44262306a36Sopenharmony_ci hdr->length = len; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* high-level API to send LLC confirm link */ 44762306a36Sopenharmony_ciint smc_llc_send_confirm_link(struct smc_link *link, 44862306a36Sopenharmony_ci enum smc_llc_reqresp reqresp) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct smc_llc_msg_confirm_link *confllc; 45162306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 45262306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 45362306a36Sopenharmony_ci int rc; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 45662306a36Sopenharmony_ci return -ENOLINK; 45762306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 45862306a36Sopenharmony_ci if (rc) 45962306a36Sopenharmony_ci goto put_out; 46062306a36Sopenharmony_ci confllc = (struct smc_llc_msg_confirm_link *)wr_buf; 46162306a36Sopenharmony_ci memset(confllc, 0, sizeof(*confllc)); 46262306a36Sopenharmony_ci confllc->hd.common.llc_type = SMC_LLC_CONFIRM_LINK; 46362306a36Sopenharmony_ci smc_llc_init_msg_hdr(&confllc->hd, link->lgr, sizeof(*confllc)); 46462306a36Sopenharmony_ci confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC; 46562306a36Sopenharmony_ci if (reqresp == SMC_LLC_RESP) 46662306a36Sopenharmony_ci confllc->hd.flags |= SMC_LLC_FLAG_RESP; 46762306a36Sopenharmony_ci memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1], 46862306a36Sopenharmony_ci ETH_ALEN); 46962306a36Sopenharmony_ci memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE); 47062306a36Sopenharmony_ci hton24(confllc->sender_qp_num, link->roce_qp->qp_num); 47162306a36Sopenharmony_ci confllc->link_num = link->link_id; 47262306a36Sopenharmony_ci memcpy(confllc->link_uid, link->link_uid, SMC_LGR_ID_SIZE); 47362306a36Sopenharmony_ci confllc->max_links = SMC_LINKS_ADD_LNK_MAX; 47462306a36Sopenharmony_ci if (link->lgr->smc_version == SMC_V2 && 47562306a36Sopenharmony_ci link->lgr->peer_smc_release >= SMC_RELEASE_1) { 47662306a36Sopenharmony_ci confllc->max_conns = link->lgr->max_conns; 47762306a36Sopenharmony_ci confllc->max_links = link->lgr->max_links; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci /* send llc message */ 48062306a36Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 48162306a36Sopenharmony_ciput_out: 48262306a36Sopenharmony_ci smc_wr_tx_link_put(link); 48362306a36Sopenharmony_ci return rc; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/* send LLC confirm rkey request */ 48762306a36Sopenharmony_cistatic int smc_llc_send_confirm_rkey(struct smc_link *send_link, 48862306a36Sopenharmony_ci struct smc_buf_desc *rmb_desc) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct smc_llc_msg_confirm_rkey *rkeyllc; 49162306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 49262306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 49362306a36Sopenharmony_ci struct smc_link *link; 49462306a36Sopenharmony_ci int i, rc, rtok_ix; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(send_link)) 49762306a36Sopenharmony_ci return -ENOLINK; 49862306a36Sopenharmony_ci rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend); 49962306a36Sopenharmony_ci if (rc) 50062306a36Sopenharmony_ci goto put_out; 50162306a36Sopenharmony_ci rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf; 50262306a36Sopenharmony_ci memset(rkeyllc, 0, sizeof(*rkeyllc)); 50362306a36Sopenharmony_ci rkeyllc->hd.common.llc_type = SMC_LLC_CONFIRM_RKEY; 50462306a36Sopenharmony_ci smc_llc_init_msg_hdr(&rkeyllc->hd, send_link->lgr, sizeof(*rkeyllc)); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci rtok_ix = 1; 50762306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 50862306a36Sopenharmony_ci link = &send_link->lgr->lnk[i]; 50962306a36Sopenharmony_ci if (smc_link_active(link) && link != send_link) { 51062306a36Sopenharmony_ci rkeyllc->rtoken[rtok_ix].link_id = link->link_id; 51162306a36Sopenharmony_ci rkeyllc->rtoken[rtok_ix].rmb_key = 51262306a36Sopenharmony_ci htonl(rmb_desc->mr[link->link_idx]->rkey); 51362306a36Sopenharmony_ci rkeyllc->rtoken[rtok_ix].rmb_vaddr = rmb_desc->is_vm ? 51462306a36Sopenharmony_ci cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) : 51562306a36Sopenharmony_ci cpu_to_be64((u64)sg_dma_address 51662306a36Sopenharmony_ci (rmb_desc->sgt[link->link_idx].sgl)); 51762306a36Sopenharmony_ci rtok_ix++; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci /* rkey of send_link is in rtoken[0] */ 52162306a36Sopenharmony_ci rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1; 52262306a36Sopenharmony_ci rkeyllc->rtoken[0].rmb_key = 52362306a36Sopenharmony_ci htonl(rmb_desc->mr[send_link->link_idx]->rkey); 52462306a36Sopenharmony_ci rkeyllc->rtoken[0].rmb_vaddr = rmb_desc->is_vm ? 52562306a36Sopenharmony_ci cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) : 52662306a36Sopenharmony_ci cpu_to_be64((u64)sg_dma_address 52762306a36Sopenharmony_ci (rmb_desc->sgt[send_link->link_idx].sgl)); 52862306a36Sopenharmony_ci /* send llc message */ 52962306a36Sopenharmony_ci rc = smc_wr_tx_send(send_link, pend); 53062306a36Sopenharmony_ciput_out: 53162306a36Sopenharmony_ci smc_wr_tx_link_put(send_link); 53262306a36Sopenharmony_ci return rc; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* send LLC delete rkey request */ 53662306a36Sopenharmony_cistatic int smc_llc_send_delete_rkey(struct smc_link *link, 53762306a36Sopenharmony_ci struct smc_buf_desc *rmb_desc) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct smc_llc_msg_delete_rkey *rkeyllc; 54062306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 54162306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 54262306a36Sopenharmony_ci int rc; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 54562306a36Sopenharmony_ci return -ENOLINK; 54662306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 54762306a36Sopenharmony_ci if (rc) 54862306a36Sopenharmony_ci goto put_out; 54962306a36Sopenharmony_ci rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf; 55062306a36Sopenharmony_ci memset(rkeyllc, 0, sizeof(*rkeyllc)); 55162306a36Sopenharmony_ci rkeyllc->hd.common.llc_type = SMC_LLC_DELETE_RKEY; 55262306a36Sopenharmony_ci smc_llc_init_msg_hdr(&rkeyllc->hd, link->lgr, sizeof(*rkeyllc)); 55362306a36Sopenharmony_ci rkeyllc->num_rkeys = 1; 55462306a36Sopenharmony_ci rkeyllc->rkey[0] = htonl(rmb_desc->mr[link->link_idx]->rkey); 55562306a36Sopenharmony_ci /* send llc message */ 55662306a36Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 55762306a36Sopenharmony_ciput_out: 55862306a36Sopenharmony_ci smc_wr_tx_link_put(link); 55962306a36Sopenharmony_ci return rc; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/* return first buffer from any of the next buf lists */ 56362306a36Sopenharmony_cistatic struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr, 56462306a36Sopenharmony_ci int *buf_lst) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct smc_buf_desc *buf_pos; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci while (*buf_lst < SMC_RMBE_SIZES) { 56962306a36Sopenharmony_ci buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst], 57062306a36Sopenharmony_ci struct smc_buf_desc, list); 57162306a36Sopenharmony_ci if (buf_pos) 57262306a36Sopenharmony_ci return buf_pos; 57362306a36Sopenharmony_ci (*buf_lst)++; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci return NULL; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* return next rmb from buffer lists */ 57962306a36Sopenharmony_cistatic struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr, 58062306a36Sopenharmony_ci int *buf_lst, 58162306a36Sopenharmony_ci struct smc_buf_desc *buf_pos) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct smc_buf_desc *buf_next; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!buf_pos) 58662306a36Sopenharmony_ci return _smc_llc_get_next_rmb(lgr, buf_lst); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) { 58962306a36Sopenharmony_ci (*buf_lst)++; 59062306a36Sopenharmony_ci return _smc_llc_get_next_rmb(lgr, buf_lst); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci buf_next = list_next_entry(buf_pos, list); 59362306a36Sopenharmony_ci return buf_next; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr, 59762306a36Sopenharmony_ci int *buf_lst) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci *buf_lst = 0; 60062306a36Sopenharmony_ci return smc_llc_get_next_rmb(lgr, buf_lst, NULL); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int smc_llc_fill_ext_v2(struct smc_llc_msg_add_link_v2_ext *ext, 60462306a36Sopenharmony_ci struct smc_link *link, struct smc_link *link_new) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 60762306a36Sopenharmony_ci struct smc_buf_desc *buf_pos; 60862306a36Sopenharmony_ci int prim_lnk_idx, lnk_idx, i; 60962306a36Sopenharmony_ci struct smc_buf_desc *rmb; 61062306a36Sopenharmony_ci int len = sizeof(*ext); 61162306a36Sopenharmony_ci int buf_lst; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci ext->v2_direct = !lgr->uses_gateway; 61462306a36Sopenharmony_ci memcpy(ext->client_target_gid, link_new->gid, SMC_GID_SIZE); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci prim_lnk_idx = link->link_idx; 61762306a36Sopenharmony_ci lnk_idx = link_new->link_idx; 61862306a36Sopenharmony_ci down_write(&lgr->rmbs_lock); 61962306a36Sopenharmony_ci ext->num_rkeys = lgr->conns_num; 62062306a36Sopenharmony_ci if (!ext->num_rkeys) 62162306a36Sopenharmony_ci goto out; 62262306a36Sopenharmony_ci buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 62362306a36Sopenharmony_ci for (i = 0; i < ext->num_rkeys; i++) { 62462306a36Sopenharmony_ci while (buf_pos && !(buf_pos)->used) 62562306a36Sopenharmony_ci buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); 62662306a36Sopenharmony_ci if (!buf_pos) 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci rmb = buf_pos; 62962306a36Sopenharmony_ci ext->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey); 63062306a36Sopenharmony_ci ext->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey); 63162306a36Sopenharmony_ci ext->rt[i].rmb_vaddr_new = rmb->is_vm ? 63262306a36Sopenharmony_ci cpu_to_be64((uintptr_t)rmb->cpu_addr) : 63362306a36Sopenharmony_ci cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); 63462306a36Sopenharmony_ci buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci len += i * sizeof(ext->rt[0]); 63762306a36Sopenharmony_ciout: 63862306a36Sopenharmony_ci up_write(&lgr->rmbs_lock); 63962306a36Sopenharmony_ci return len; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* send ADD LINK request or response */ 64362306a36Sopenharmony_ciint smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[], 64462306a36Sopenharmony_ci struct smc_link *link_new, 64562306a36Sopenharmony_ci enum smc_llc_reqresp reqresp) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct smc_llc_msg_add_link_v2_ext *ext = NULL; 64862306a36Sopenharmony_ci struct smc_llc_msg_add_link *addllc; 64962306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 65062306a36Sopenharmony_ci int len = sizeof(*addllc); 65162306a36Sopenharmony_ci int rc; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 65462306a36Sopenharmony_ci return -ENOLINK; 65562306a36Sopenharmony_ci if (link->lgr->smc_version == SMC_V2) { 65662306a36Sopenharmony_ci struct smc_wr_v2_buf *wr_buf; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend); 65962306a36Sopenharmony_ci if (rc) 66062306a36Sopenharmony_ci goto put_out; 66162306a36Sopenharmony_ci addllc = (struct smc_llc_msg_add_link *)wr_buf; 66262306a36Sopenharmony_ci ext = (struct smc_llc_msg_add_link_v2_ext *) 66362306a36Sopenharmony_ci &wr_buf->raw[sizeof(*addllc)]; 66462306a36Sopenharmony_ci memset(ext, 0, SMC_WR_TX_SIZE); 66562306a36Sopenharmony_ci } else { 66662306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 66962306a36Sopenharmony_ci if (rc) 67062306a36Sopenharmony_ci goto put_out; 67162306a36Sopenharmony_ci addllc = (struct smc_llc_msg_add_link *)wr_buf; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci memset(addllc, 0, sizeof(*addllc)); 67562306a36Sopenharmony_ci addllc->hd.common.llc_type = SMC_LLC_ADD_LINK; 67662306a36Sopenharmony_ci if (reqresp == SMC_LLC_RESP) 67762306a36Sopenharmony_ci addllc->hd.flags |= SMC_LLC_FLAG_RESP; 67862306a36Sopenharmony_ci memcpy(addllc->sender_mac, mac, ETH_ALEN); 67962306a36Sopenharmony_ci memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); 68062306a36Sopenharmony_ci if (link_new) { 68162306a36Sopenharmony_ci addllc->link_num = link_new->link_id; 68262306a36Sopenharmony_ci hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num); 68362306a36Sopenharmony_ci hton24(addllc->initial_psn, link_new->psn_initial); 68462306a36Sopenharmony_ci if (reqresp == SMC_LLC_REQ) 68562306a36Sopenharmony_ci addllc->qp_mtu = link_new->path_mtu; 68662306a36Sopenharmony_ci else 68762306a36Sopenharmony_ci addllc->qp_mtu = min(link_new->path_mtu, 68862306a36Sopenharmony_ci link_new->peer_mtu); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci if (ext && link_new) 69162306a36Sopenharmony_ci len += smc_llc_fill_ext_v2(ext, link, link_new); 69262306a36Sopenharmony_ci smc_llc_init_msg_hdr(&addllc->hd, link->lgr, len); 69362306a36Sopenharmony_ci /* send llc message */ 69462306a36Sopenharmony_ci if (link->lgr->smc_version == SMC_V2) 69562306a36Sopenharmony_ci rc = smc_wr_tx_v2_send(link, pend, len); 69662306a36Sopenharmony_ci else 69762306a36Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 69862306a36Sopenharmony_ciput_out: 69962306a36Sopenharmony_ci smc_wr_tx_link_put(link); 70062306a36Sopenharmony_ci return rc; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci/* send DELETE LINK request or response */ 70462306a36Sopenharmony_ciint smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id, 70562306a36Sopenharmony_ci enum smc_llc_reqresp reqresp, bool orderly, 70662306a36Sopenharmony_ci u32 reason) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct smc_llc_msg_del_link *delllc; 70962306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 71062306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 71162306a36Sopenharmony_ci int rc; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 71462306a36Sopenharmony_ci return -ENOLINK; 71562306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 71662306a36Sopenharmony_ci if (rc) 71762306a36Sopenharmony_ci goto put_out; 71862306a36Sopenharmony_ci delllc = (struct smc_llc_msg_del_link *)wr_buf; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci memset(delllc, 0, sizeof(*delllc)); 72162306a36Sopenharmony_ci delllc->hd.common.llc_type = SMC_LLC_DELETE_LINK; 72262306a36Sopenharmony_ci smc_llc_init_msg_hdr(&delllc->hd, link->lgr, sizeof(*delllc)); 72362306a36Sopenharmony_ci if (reqresp == SMC_LLC_RESP) 72462306a36Sopenharmony_ci delllc->hd.flags |= SMC_LLC_FLAG_RESP; 72562306a36Sopenharmony_ci if (orderly) 72662306a36Sopenharmony_ci delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 72762306a36Sopenharmony_ci if (link_del_id) 72862306a36Sopenharmony_ci delllc->link_num = link_del_id; 72962306a36Sopenharmony_ci else 73062306a36Sopenharmony_ci delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 73162306a36Sopenharmony_ci delllc->reason = htonl(reason); 73262306a36Sopenharmony_ci /* send llc message */ 73362306a36Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 73462306a36Sopenharmony_ciput_out: 73562306a36Sopenharmony_ci smc_wr_tx_link_put(link); 73662306a36Sopenharmony_ci return rc; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/* send LLC test link request */ 74062306a36Sopenharmony_cistatic int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16]) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct smc_llc_msg_test_link *testllc; 74362306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 74462306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 74562306a36Sopenharmony_ci int rc; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 74862306a36Sopenharmony_ci return -ENOLINK; 74962306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 75062306a36Sopenharmony_ci if (rc) 75162306a36Sopenharmony_ci goto put_out; 75262306a36Sopenharmony_ci testllc = (struct smc_llc_msg_test_link *)wr_buf; 75362306a36Sopenharmony_ci memset(testllc, 0, sizeof(*testllc)); 75462306a36Sopenharmony_ci testllc->hd.common.llc_type = SMC_LLC_TEST_LINK; 75562306a36Sopenharmony_ci smc_llc_init_msg_hdr(&testllc->hd, link->lgr, sizeof(*testllc)); 75662306a36Sopenharmony_ci memcpy(testllc->user_data, user_data, sizeof(testllc->user_data)); 75762306a36Sopenharmony_ci /* send llc message */ 75862306a36Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 75962306a36Sopenharmony_ciput_out: 76062306a36Sopenharmony_ci smc_wr_tx_link_put(link); 76162306a36Sopenharmony_ci return rc; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci/* schedule an llc send on link, may wait for buffers */ 76562306a36Sopenharmony_cistatic int smc_llc_send_message(struct smc_link *link, void *llcbuf) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 76862306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 76962306a36Sopenharmony_ci int rc; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 77262306a36Sopenharmony_ci return -ENOLINK; 77362306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 77462306a36Sopenharmony_ci if (rc) 77562306a36Sopenharmony_ci goto put_out; 77662306a36Sopenharmony_ci memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); 77762306a36Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 77862306a36Sopenharmony_ciput_out: 77962306a36Sopenharmony_ci smc_wr_tx_link_put(link); 78062306a36Sopenharmony_ci return rc; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/* schedule an llc send on link, may wait for buffers, 78462306a36Sopenharmony_ci * and wait for send completion notification. 78562306a36Sopenharmony_ci * @return 0 on success 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_cistatic int smc_llc_send_message_wait(struct smc_link *link, void *llcbuf) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 79062306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 79162306a36Sopenharmony_ci int rc; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 79462306a36Sopenharmony_ci return -ENOLINK; 79562306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 79662306a36Sopenharmony_ci if (rc) 79762306a36Sopenharmony_ci goto put_out; 79862306a36Sopenharmony_ci memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); 79962306a36Sopenharmony_ci rc = smc_wr_tx_send_wait(link, pend, SMC_LLC_WAIT_TIME); 80062306a36Sopenharmony_ciput_out: 80162306a36Sopenharmony_ci smc_wr_tx_link_put(link); 80262306a36Sopenharmony_ci return rc; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/********************************* receive ***********************************/ 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic int smc_llc_alloc_alt_link(struct smc_link_group *lgr, 80862306a36Sopenharmony_ci enum smc_lgr_type lgr_new_t) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci int i; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (lgr->type == SMC_LGR_SYMMETRIC || 81362306a36Sopenharmony_ci (lgr->type != SMC_LGR_SINGLE && 81462306a36Sopenharmony_ci (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 81562306a36Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER))) 81662306a36Sopenharmony_ci return -EMLINK; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 81962306a36Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) { 82062306a36Sopenharmony_ci for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--) 82162306a36Sopenharmony_ci if (lgr->lnk[i].state == SMC_LNK_UNUSED) 82262306a36Sopenharmony_ci return i; 82362306a36Sopenharmony_ci } else { 82462306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) 82562306a36Sopenharmony_ci if (lgr->lnk[i].state == SMC_LNK_UNUSED) 82662306a36Sopenharmony_ci return i; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci return -EMLINK; 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci/* send one add_link_continue msg */ 83262306a36Sopenharmony_cistatic int smc_llc_add_link_cont(struct smc_link *link, 83362306a36Sopenharmony_ci struct smc_link *link_new, u8 *num_rkeys_todo, 83462306a36Sopenharmony_ci int *buf_lst, struct smc_buf_desc **buf_pos) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct smc_llc_msg_add_link_cont *addc_llc; 83762306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 83862306a36Sopenharmony_ci int prim_lnk_idx, lnk_idx, i, rc; 83962306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 84062306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 84162306a36Sopenharmony_ci struct smc_buf_desc *rmb; 84262306a36Sopenharmony_ci u8 n; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 84562306a36Sopenharmony_ci return -ENOLINK; 84662306a36Sopenharmony_ci rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 84762306a36Sopenharmony_ci if (rc) 84862306a36Sopenharmony_ci goto put_out; 84962306a36Sopenharmony_ci addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf; 85062306a36Sopenharmony_ci memset(addc_llc, 0, sizeof(*addc_llc)); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci prim_lnk_idx = link->link_idx; 85362306a36Sopenharmony_ci lnk_idx = link_new->link_idx; 85462306a36Sopenharmony_ci addc_llc->link_num = link_new->link_id; 85562306a36Sopenharmony_ci addc_llc->num_rkeys = *num_rkeys_todo; 85662306a36Sopenharmony_ci n = *num_rkeys_todo; 85762306a36Sopenharmony_ci for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) { 85862306a36Sopenharmony_ci while (*buf_pos && !(*buf_pos)->used) 85962306a36Sopenharmony_ci *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 86062306a36Sopenharmony_ci if (!*buf_pos) { 86162306a36Sopenharmony_ci addc_llc->num_rkeys = addc_llc->num_rkeys - 86262306a36Sopenharmony_ci *num_rkeys_todo; 86362306a36Sopenharmony_ci *num_rkeys_todo = 0; 86462306a36Sopenharmony_ci break; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci rmb = *buf_pos; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci addc_llc->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey); 86962306a36Sopenharmony_ci addc_llc->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey); 87062306a36Sopenharmony_ci addc_llc->rt[i].rmb_vaddr_new = rmb->is_vm ? 87162306a36Sopenharmony_ci cpu_to_be64((uintptr_t)rmb->cpu_addr) : 87262306a36Sopenharmony_ci cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci (*num_rkeys_todo)--; 87562306a36Sopenharmony_ci *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci addc_llc->hd.common.llc_type = SMC_LLC_ADD_LINK_CONT; 87862306a36Sopenharmony_ci addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont); 87962306a36Sopenharmony_ci if (lgr->role == SMC_CLNT) 88062306a36Sopenharmony_ci addc_llc->hd.flags |= SMC_LLC_FLAG_RESP; 88162306a36Sopenharmony_ci rc = smc_wr_tx_send(link, pend); 88262306a36Sopenharmony_ciput_out: 88362306a36Sopenharmony_ci smc_wr_tx_link_put(link); 88462306a36Sopenharmony_ci return rc; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic int smc_llc_cli_rkey_exchange(struct smc_link *link, 88862306a36Sopenharmony_ci struct smc_link *link_new) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct smc_llc_msg_add_link_cont *addc_llc; 89162306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 89262306a36Sopenharmony_ci u8 max, num_rkeys_send, num_rkeys_recv; 89362306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 89462306a36Sopenharmony_ci struct smc_buf_desc *buf_pos; 89562306a36Sopenharmony_ci int buf_lst; 89662306a36Sopenharmony_ci int rc = 0; 89762306a36Sopenharmony_ci int i; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci down_write(&lgr->rmbs_lock); 90062306a36Sopenharmony_ci num_rkeys_send = lgr->conns_num; 90162306a36Sopenharmony_ci buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 90262306a36Sopenharmony_ci do { 90362306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME, 90462306a36Sopenharmony_ci SMC_LLC_ADD_LINK_CONT); 90562306a36Sopenharmony_ci if (!qentry) { 90662306a36Sopenharmony_ci rc = -ETIMEDOUT; 90762306a36Sopenharmony_ci break; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci addc_llc = &qentry->msg.add_link_cont; 91062306a36Sopenharmony_ci num_rkeys_recv = addc_llc->num_rkeys; 91162306a36Sopenharmony_ci max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 91262306a36Sopenharmony_ci for (i = 0; i < max; i++) { 91362306a36Sopenharmony_ci smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 91462306a36Sopenharmony_ci addc_llc->rt[i].rmb_key, 91562306a36Sopenharmony_ci addc_llc->rt[i].rmb_vaddr_new, 91662306a36Sopenharmony_ci addc_llc->rt[i].rmb_key_new); 91762306a36Sopenharmony_ci num_rkeys_recv--; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 92062306a36Sopenharmony_ci rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 92162306a36Sopenharmony_ci &buf_lst, &buf_pos); 92262306a36Sopenharmony_ci if (rc) 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci } while (num_rkeys_send || num_rkeys_recv); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci up_write(&lgr->rmbs_lock); 92762306a36Sopenharmony_ci return rc; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci/* prepare and send an add link reject response */ 93162306a36Sopenharmony_cistatic int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 93462306a36Sopenharmony_ci qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; 93562306a36Sopenharmony_ci qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; 93662306a36Sopenharmony_ci smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr, 93762306a36Sopenharmony_ci sizeof(qentry->msg)); 93862306a36Sopenharmony_ci return smc_llc_send_message(qentry->link, &qentry->msg); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int smc_llc_cli_conf_link(struct smc_link *link, 94262306a36Sopenharmony_ci struct smc_init_info *ini, 94362306a36Sopenharmony_ci struct smc_link *link_new, 94462306a36Sopenharmony_ci enum smc_lgr_type lgr_new_t) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 94762306a36Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 94862306a36Sopenharmony_ci int rc = 0; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* receive CONFIRM LINK request over RoCE fabric */ 95162306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0); 95262306a36Sopenharmony_ci if (!qentry) { 95362306a36Sopenharmony_ci rc = smc_llc_send_delete_link(link, link_new->link_id, 95462306a36Sopenharmony_ci SMC_LLC_REQ, false, 95562306a36Sopenharmony_ci SMC_LLC_DEL_LOST_PATH); 95662306a36Sopenharmony_ci return -ENOLINK; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci if (qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) { 95962306a36Sopenharmony_ci /* received DELETE_LINK instead */ 96062306a36Sopenharmony_ci qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 96162306a36Sopenharmony_ci smc_llc_send_message(link, &qentry->msg); 96262306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 96362306a36Sopenharmony_ci return -ENOLINK; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci smc_llc_save_peer_uid(qentry); 96662306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci rc = smc_ib_modify_qp_rts(link_new); 96962306a36Sopenharmony_ci if (rc) { 97062306a36Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 97162306a36Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 97262306a36Sopenharmony_ci return -ENOLINK; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci smc_wr_remember_qp_attr(link_new); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci rc = smcr_buf_reg_lgr(link_new); 97762306a36Sopenharmony_ci if (rc) { 97862306a36Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 97962306a36Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 98062306a36Sopenharmony_ci return -ENOLINK; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* send CONFIRM LINK response over RoCE fabric */ 98462306a36Sopenharmony_ci rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP); 98562306a36Sopenharmony_ci if (rc) { 98662306a36Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 98762306a36Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 98862306a36Sopenharmony_ci return -ENOLINK; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci smc_llc_link_active(link_new); 99162306a36Sopenharmony_ci if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 99262306a36Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) 99362306a36Sopenharmony_ci smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); 99462306a36Sopenharmony_ci else 99562306a36Sopenharmony_ci smcr_lgr_set_type(lgr, lgr_new_t); 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic void smc_llc_save_add_link_rkeys(struct smc_link *link, 100062306a36Sopenharmony_ci struct smc_link *link_new) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct smc_llc_msg_add_link_v2_ext *ext; 100362306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 100462306a36Sopenharmony_ci int max, i; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci ext = (struct smc_llc_msg_add_link_v2_ext *)((u8 *)lgr->wr_rx_buf_v2 + 100762306a36Sopenharmony_ci SMC_WR_TX_SIZE); 100862306a36Sopenharmony_ci max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); 100962306a36Sopenharmony_ci down_write(&lgr->rmbs_lock); 101062306a36Sopenharmony_ci for (i = 0; i < max; i++) { 101162306a36Sopenharmony_ci smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 101262306a36Sopenharmony_ci ext->rt[i].rmb_key, 101362306a36Sopenharmony_ci ext->rt[i].rmb_vaddr_new, 101462306a36Sopenharmony_ci ext->rt[i].rmb_key_new); 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci up_write(&lgr->rmbs_lock); 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic void smc_llc_save_add_link_info(struct smc_link *link, 102062306a36Sopenharmony_ci struct smc_llc_msg_add_link *add_llc) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci link->peer_qpn = ntoh24(add_llc->sender_qp_num); 102362306a36Sopenharmony_ci memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE); 102462306a36Sopenharmony_ci memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN); 102562306a36Sopenharmony_ci link->peer_psn = ntoh24(add_llc->initial_psn); 102662306a36Sopenharmony_ci link->peer_mtu = add_llc->qp_mtu; 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci/* as an SMC client, process an add link request */ 103062306a36Sopenharmony_ciint smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct smc_llc_msg_add_link *llc = &qentry->msg.add_link; 103362306a36Sopenharmony_ci enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 103462306a36Sopenharmony_ci struct smc_link_group *lgr = smc_get_lgr(link); 103562306a36Sopenharmony_ci struct smc_init_info *ini = NULL; 103662306a36Sopenharmony_ci struct smc_link *lnk_new = NULL; 103762306a36Sopenharmony_ci int lnk_idx, rc = 0; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if (!llc->qp_mtu) 104062306a36Sopenharmony_ci goto out_reject; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci ini = kzalloc(sizeof(*ini), GFP_KERNEL); 104362306a36Sopenharmony_ci if (!ini) { 104462306a36Sopenharmony_ci rc = -ENOMEM; 104562306a36Sopenharmony_ci goto out_reject; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1) { 104962306a36Sopenharmony_ci rc = 0; 105062306a36Sopenharmony_ci goto out_reject; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci ini->vlan_id = lgr->vlan_id; 105462306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2) { 105562306a36Sopenharmony_ci ini->check_smcrv2 = true; 105662306a36Sopenharmony_ci ini->smcrv2.saddr = lgr->saddr; 105762306a36Sopenharmony_ci ini->smcrv2.daddr = smc_ib_gid_to_ipv4(llc->sender_gid); 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); 106062306a36Sopenharmony_ci if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 106162306a36Sopenharmony_ci (lgr->smc_version == SMC_V2 || 106262306a36Sopenharmony_ci !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN))) { 106362306a36Sopenharmony_ci if (!ini->ib_dev && !ini->smcrv2.ib_dev_v2) 106462306a36Sopenharmony_ci goto out_reject; 106562306a36Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) { 106862306a36Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 106962306a36Sopenharmony_ci ini->smcrv2.ib_dev_v2 = link->smcibdev; 107062306a36Sopenharmony_ci ini->smcrv2.ib_port_v2 = link->ibport; 107162306a36Sopenharmony_ci } else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) { 107262306a36Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 107362306a36Sopenharmony_ci ini->ib_dev = link->smcibdev; 107462306a36Sopenharmony_ci ini->ib_port = link->ibport; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 107762306a36Sopenharmony_ci if (lnk_idx < 0) 107862306a36Sopenharmony_ci goto out_reject; 107962306a36Sopenharmony_ci lnk_new = &lgr->lnk[lnk_idx]; 108062306a36Sopenharmony_ci rc = smcr_link_init(lgr, lnk_new, lnk_idx, ini); 108162306a36Sopenharmony_ci if (rc) 108262306a36Sopenharmony_ci goto out_reject; 108362306a36Sopenharmony_ci smc_llc_save_add_link_info(lnk_new, llc); 108462306a36Sopenharmony_ci lnk_new->link_id = llc->link_num; /* SMC server assigns link id */ 108562306a36Sopenharmony_ci smc_llc_link_set_uid(lnk_new); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci rc = smc_ib_ready_link(lnk_new); 108862306a36Sopenharmony_ci if (rc) 108962306a36Sopenharmony_ci goto out_clear_lnk; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci rc = smcr_buf_map_lgr(lnk_new); 109262306a36Sopenharmony_ci if (rc) 109362306a36Sopenharmony_ci goto out_clear_lnk; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci rc = smc_llc_send_add_link(link, 109662306a36Sopenharmony_ci lnk_new->smcibdev->mac[lnk_new->ibport - 1], 109762306a36Sopenharmony_ci lnk_new->gid, lnk_new, SMC_LLC_RESP); 109862306a36Sopenharmony_ci if (rc) 109962306a36Sopenharmony_ci goto out_clear_lnk; 110062306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2) { 110162306a36Sopenharmony_ci smc_llc_save_add_link_rkeys(link, lnk_new); 110262306a36Sopenharmony_ci } else { 110362306a36Sopenharmony_ci rc = smc_llc_cli_rkey_exchange(link, lnk_new); 110462306a36Sopenharmony_ci if (rc) { 110562306a36Sopenharmony_ci rc = 0; 110662306a36Sopenharmony_ci goto out_clear_lnk; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci rc = smc_llc_cli_conf_link(link, ini, lnk_new, lgr_new_t); 111062306a36Sopenharmony_ci if (!rc) 111162306a36Sopenharmony_ci goto out; 111262306a36Sopenharmony_ciout_clear_lnk: 111362306a36Sopenharmony_ci lnk_new->state = SMC_LNK_INACTIVE; 111462306a36Sopenharmony_ci smcr_link_clear(lnk_new, false); 111562306a36Sopenharmony_ciout_reject: 111662306a36Sopenharmony_ci smc_llc_cli_add_link_reject(qentry); 111762306a36Sopenharmony_ciout: 111862306a36Sopenharmony_ci kfree(ini); 111962306a36Sopenharmony_ci kfree(qentry); 112062306a36Sopenharmony_ci return rc; 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic void smc_llc_send_request_add_link(struct smc_link *link) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct smc_llc_msg_req_add_link_v2 *llc; 112662306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *pend; 112762306a36Sopenharmony_ci struct smc_wr_v2_buf *wr_buf; 112862306a36Sopenharmony_ci struct smc_gidlist gidlist; 112962306a36Sopenharmony_ci int rc, len, i; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 113262306a36Sopenharmony_ci return; 113362306a36Sopenharmony_ci if (link->lgr->type == SMC_LGR_SYMMETRIC || 113462306a36Sopenharmony_ci link->lgr->type == SMC_LGR_ASYMMETRIC_PEER) 113562306a36Sopenharmony_ci goto put_out; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci smc_fill_gid_list(link->lgr, &gidlist, link->smcibdev, link->gid); 113862306a36Sopenharmony_ci if (gidlist.len <= 1) 113962306a36Sopenharmony_ci goto put_out; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend); 114262306a36Sopenharmony_ci if (rc) 114362306a36Sopenharmony_ci goto put_out; 114462306a36Sopenharmony_ci llc = (struct smc_llc_msg_req_add_link_v2 *)wr_buf; 114562306a36Sopenharmony_ci memset(llc, 0, SMC_WR_TX_SIZE); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci llc->hd.common.llc_type = SMC_LLC_REQ_ADD_LINK; 114862306a36Sopenharmony_ci for (i = 0; i < gidlist.len; i++) 114962306a36Sopenharmony_ci memcpy(llc->gid[i], gidlist.list[i], sizeof(gidlist.list[0])); 115062306a36Sopenharmony_ci llc->gid_cnt = gidlist.len; 115162306a36Sopenharmony_ci len = sizeof(*llc) + (gidlist.len * sizeof(gidlist.list[0])); 115262306a36Sopenharmony_ci smc_llc_init_msg_hdr(&llc->hd, link->lgr, len); 115362306a36Sopenharmony_ci rc = smc_wr_tx_v2_send(link, pend, len); 115462306a36Sopenharmony_ci if (!rc) 115562306a36Sopenharmony_ci /* set REQ_ADD_LINK flow and wait for response from peer */ 115662306a36Sopenharmony_ci link->lgr->llc_flow_lcl.type = SMC_LLC_FLOW_REQ_ADD_LINK; 115762306a36Sopenharmony_ciput_out: 115862306a36Sopenharmony_ci smc_wr_tx_link_put(link); 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci/* as an SMC client, invite server to start the add_link processing */ 116262306a36Sopenharmony_cistatic void smc_llc_cli_add_link_invite(struct smc_link *link, 116362306a36Sopenharmony_ci struct smc_llc_qentry *qentry) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct smc_link_group *lgr = smc_get_lgr(link); 116662306a36Sopenharmony_ci struct smc_init_info *ini = NULL; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2) { 116962306a36Sopenharmony_ci smc_llc_send_request_add_link(link); 117062306a36Sopenharmony_ci goto out; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (lgr->type == SMC_LGR_SYMMETRIC || 117462306a36Sopenharmony_ci lgr->type == SMC_LGR_ASYMMETRIC_PEER) 117562306a36Sopenharmony_ci goto out; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1) 117862306a36Sopenharmony_ci goto out; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci ini = kzalloc(sizeof(*ini), GFP_KERNEL); 118162306a36Sopenharmony_ci if (!ini) 118262306a36Sopenharmony_ci goto out; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci ini->vlan_id = lgr->vlan_id; 118562306a36Sopenharmony_ci smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); 118662306a36Sopenharmony_ci if (!ini->ib_dev) 118762306a36Sopenharmony_ci goto out; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci smc_llc_send_add_link(link, ini->ib_dev->mac[ini->ib_port - 1], 119062306a36Sopenharmony_ci ini->ib_gid, NULL, SMC_LLC_REQ); 119162306a36Sopenharmony_ciout: 119262306a36Sopenharmony_ci kfree(ini); 119362306a36Sopenharmony_ci kfree(qentry); 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic bool smc_llc_is_empty_llc_message(union smc_llc_msg *llc) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci int i; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(llc->raw.data); i++) 120162306a36Sopenharmony_ci if (llc->raw.data[i]) 120262306a36Sopenharmony_ci return false; 120362306a36Sopenharmony_ci return true; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic bool smc_llc_is_local_add_link(union smc_llc_msg *llc) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci if (llc->raw.hdr.common.llc_type == SMC_LLC_ADD_LINK && 120962306a36Sopenharmony_ci smc_llc_is_empty_llc_message(llc)) 121062306a36Sopenharmony_ci return true; 121162306a36Sopenharmony_ci return false; 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic void smc_llc_process_cli_add_link(struct smc_link_group *lgr) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci down_write(&lgr->llc_conf_mutex); 122162306a36Sopenharmony_ci if (smc_llc_is_local_add_link(&qentry->msg)) 122262306a36Sopenharmony_ci smc_llc_cli_add_link_invite(qentry->link, qentry); 122362306a36Sopenharmony_ci else 122462306a36Sopenharmony_ci smc_llc_cli_add_link(qentry->link, qentry); 122562306a36Sopenharmony_ci up_write(&lgr->llc_conf_mutex); 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_cistatic int smc_llc_active_link_count(struct smc_link_group *lgr) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci int i, link_count = 0; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 123362306a36Sopenharmony_ci if (!smc_link_active(&lgr->lnk[i])) 123462306a36Sopenharmony_ci continue; 123562306a36Sopenharmony_ci link_count++; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci return link_count; 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci/* find the asymmetric link when 3 links are established */ 124162306a36Sopenharmony_cistatic struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci int asym_idx = -ENOENT; 124462306a36Sopenharmony_ci int i, j, k; 124562306a36Sopenharmony_ci bool found; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* determine asymmetric link */ 124862306a36Sopenharmony_ci found = false; 124962306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 125062306a36Sopenharmony_ci for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) { 125162306a36Sopenharmony_ci if (!smc_link_usable(&lgr->lnk[i]) || 125262306a36Sopenharmony_ci !smc_link_usable(&lgr->lnk[j])) 125362306a36Sopenharmony_ci continue; 125462306a36Sopenharmony_ci if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid, 125562306a36Sopenharmony_ci SMC_GID_SIZE)) { 125662306a36Sopenharmony_ci found = true; /* asym_lnk is i or j */ 125762306a36Sopenharmony_ci break; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci if (found) 126162306a36Sopenharmony_ci break; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci if (!found) 126462306a36Sopenharmony_ci goto out; /* no asymmetric link */ 126562306a36Sopenharmony_ci for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) { 126662306a36Sopenharmony_ci if (!smc_link_usable(&lgr->lnk[k])) 126762306a36Sopenharmony_ci continue; 126862306a36Sopenharmony_ci if (k != i && 126962306a36Sopenharmony_ci !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid, 127062306a36Sopenharmony_ci SMC_GID_SIZE)) { 127162306a36Sopenharmony_ci asym_idx = i; 127262306a36Sopenharmony_ci break; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci if (k != j && 127562306a36Sopenharmony_ci !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid, 127662306a36Sopenharmony_ci SMC_GID_SIZE)) { 127762306a36Sopenharmony_ci asym_idx = j; 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ciout: 128262306a36Sopenharmony_ci return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx]; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_cistatic void smc_llc_delete_asym_link(struct smc_link_group *lgr) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci struct smc_link *lnk_new = NULL, *lnk_asym; 128862306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 128962306a36Sopenharmony_ci int rc; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci lnk_asym = smc_llc_find_asym_link(lgr); 129262306a36Sopenharmony_ci if (!lnk_asym) 129362306a36Sopenharmony_ci return; /* no asymmetric link */ 129462306a36Sopenharmony_ci if (!smc_link_downing(&lnk_asym->state)) 129562306a36Sopenharmony_ci return; 129662306a36Sopenharmony_ci lnk_new = smc_switch_conns(lgr, lnk_asym, false); 129762306a36Sopenharmony_ci smc_wr_tx_wait_no_pending_sends(lnk_asym); 129862306a36Sopenharmony_ci if (!lnk_new) 129962306a36Sopenharmony_ci goto out_free; 130062306a36Sopenharmony_ci /* change flow type from ADD_LINK into DEL_LINK */ 130162306a36Sopenharmony_ci lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK; 130262306a36Sopenharmony_ci rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ, 130362306a36Sopenharmony_ci true, SMC_LLC_DEL_NO_ASYM_NEEDED); 130462306a36Sopenharmony_ci if (rc) { 130562306a36Sopenharmony_ci smcr_link_down_cond(lnk_new); 130662306a36Sopenharmony_ci goto out_free; 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME, 130962306a36Sopenharmony_ci SMC_LLC_DELETE_LINK); 131062306a36Sopenharmony_ci if (!qentry) { 131162306a36Sopenharmony_ci smcr_link_down_cond(lnk_new); 131262306a36Sopenharmony_ci goto out_free; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 131562306a36Sopenharmony_ciout_free: 131662306a36Sopenharmony_ci smcr_link_clear(lnk_asym, true); 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic int smc_llc_srv_rkey_exchange(struct smc_link *link, 132062306a36Sopenharmony_ci struct smc_link *link_new) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci struct smc_llc_msg_add_link_cont *addc_llc; 132362306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 132462306a36Sopenharmony_ci u8 max, num_rkeys_send, num_rkeys_recv; 132562306a36Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 132662306a36Sopenharmony_ci struct smc_buf_desc *buf_pos; 132762306a36Sopenharmony_ci int buf_lst; 132862306a36Sopenharmony_ci int rc = 0; 132962306a36Sopenharmony_ci int i; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci down_write(&lgr->rmbs_lock); 133262306a36Sopenharmony_ci num_rkeys_send = lgr->conns_num; 133362306a36Sopenharmony_ci buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); 133462306a36Sopenharmony_ci do { 133562306a36Sopenharmony_ci smc_llc_add_link_cont(link, link_new, &num_rkeys_send, 133662306a36Sopenharmony_ci &buf_lst, &buf_pos); 133762306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, 133862306a36Sopenharmony_ci SMC_LLC_ADD_LINK_CONT); 133962306a36Sopenharmony_ci if (!qentry) { 134062306a36Sopenharmony_ci rc = -ETIMEDOUT; 134162306a36Sopenharmony_ci goto out; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci addc_llc = &qentry->msg.add_link_cont; 134462306a36Sopenharmony_ci num_rkeys_recv = addc_llc->num_rkeys; 134562306a36Sopenharmony_ci max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); 134662306a36Sopenharmony_ci for (i = 0; i < max; i++) { 134762306a36Sopenharmony_ci smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, 134862306a36Sopenharmony_ci addc_llc->rt[i].rmb_key, 134962306a36Sopenharmony_ci addc_llc->rt[i].rmb_vaddr_new, 135062306a36Sopenharmony_ci addc_llc->rt[i].rmb_key_new); 135162306a36Sopenharmony_ci num_rkeys_recv--; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 135462306a36Sopenharmony_ci } while (num_rkeys_send || num_rkeys_recv); 135562306a36Sopenharmony_ciout: 135662306a36Sopenharmony_ci up_write(&lgr->rmbs_lock); 135762306a36Sopenharmony_ci return rc; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic int smc_llc_srv_conf_link(struct smc_link *link, 136162306a36Sopenharmony_ci struct smc_link *link_new, 136262306a36Sopenharmony_ci enum smc_lgr_type lgr_new_t) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 136562306a36Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 136662306a36Sopenharmony_ci int rc; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* send CONFIRM LINK request over the RoCE fabric */ 136962306a36Sopenharmony_ci rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ); 137062306a36Sopenharmony_ci if (rc) 137162306a36Sopenharmony_ci return -ENOLINK; 137262306a36Sopenharmony_ci /* receive CONFIRM LINK response over the RoCE fabric */ 137362306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, 0); 137462306a36Sopenharmony_ci if (!qentry || 137562306a36Sopenharmony_ci qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) { 137662306a36Sopenharmony_ci /* send DELETE LINK */ 137762306a36Sopenharmony_ci smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, 137862306a36Sopenharmony_ci false, SMC_LLC_DEL_LOST_PATH); 137962306a36Sopenharmony_ci if (qentry) 138062306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 138162306a36Sopenharmony_ci return -ENOLINK; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci smc_llc_save_peer_uid(qentry); 138462306a36Sopenharmony_ci smc_llc_link_active(link_new); 138562306a36Sopenharmony_ci if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || 138662306a36Sopenharmony_ci lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) 138762306a36Sopenharmony_ci smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); 138862306a36Sopenharmony_ci else 138962306a36Sopenharmony_ci smcr_lgr_set_type(lgr, lgr_new_t); 139062306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 139162306a36Sopenharmony_ci return 0; 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic void smc_llc_send_req_add_link_response(struct smc_llc_qentry *qentry) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; 139762306a36Sopenharmony_ci smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr, 139862306a36Sopenharmony_ci sizeof(qentry->msg)); 139962306a36Sopenharmony_ci memset(&qentry->msg.raw.data, 0, sizeof(qentry->msg.raw.data)); 140062306a36Sopenharmony_ci smc_llc_send_message(qentry->link, &qentry->msg); 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ciint smc_llc_srv_add_link(struct smc_link *link, 140462306a36Sopenharmony_ci struct smc_llc_qentry *req_qentry) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; 140762306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 140862306a36Sopenharmony_ci struct smc_llc_msg_add_link *add_llc; 140962306a36Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 141062306a36Sopenharmony_ci bool send_req_add_link_resp = false; 141162306a36Sopenharmony_ci struct smc_link *link_new = NULL; 141262306a36Sopenharmony_ci struct smc_init_info *ini = NULL; 141362306a36Sopenharmony_ci int lnk_idx, rc = 0; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (req_qentry && 141662306a36Sopenharmony_ci req_qentry->msg.raw.hdr.common.llc_type == SMC_LLC_REQ_ADD_LINK) 141762306a36Sopenharmony_ci send_req_add_link_resp = true; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci ini = kzalloc(sizeof(*ini), GFP_KERNEL); 142062306a36Sopenharmony_ci if (!ini) { 142162306a36Sopenharmony_ci rc = -ENOMEM; 142262306a36Sopenharmony_ci goto out; 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1) { 142662306a36Sopenharmony_ci rc = 0; 142762306a36Sopenharmony_ci goto out; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* ignore client add link recommendation, start new flow */ 143162306a36Sopenharmony_ci ini->vlan_id = lgr->vlan_id; 143262306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2) { 143362306a36Sopenharmony_ci ini->check_smcrv2 = true; 143462306a36Sopenharmony_ci ini->smcrv2.saddr = lgr->saddr; 143562306a36Sopenharmony_ci if (send_req_add_link_resp) { 143662306a36Sopenharmony_ci struct smc_llc_msg_req_add_link_v2 *req_add = 143762306a36Sopenharmony_ci &req_qentry->msg.req_add_link; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci ini->smcrv2.daddr = smc_ib_gid_to_ipv4(req_add->gid[0]); 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); 144362306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) { 144462306a36Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 144562306a36Sopenharmony_ci ini->smcrv2.ib_dev_v2 = link->smcibdev; 144662306a36Sopenharmony_ci ini->smcrv2.ib_port_v2 = link->ibport; 144762306a36Sopenharmony_ci } else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) { 144862306a36Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; 144962306a36Sopenharmony_ci ini->ib_dev = link->smcibdev; 145062306a36Sopenharmony_ci ini->ib_port = link->ibport; 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); 145362306a36Sopenharmony_ci if (lnk_idx < 0) { 145462306a36Sopenharmony_ci rc = 0; 145562306a36Sopenharmony_ci goto out; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, ini); 145962306a36Sopenharmony_ci if (rc) 146062306a36Sopenharmony_ci goto out; 146162306a36Sopenharmony_ci link_new = &lgr->lnk[lnk_idx]; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci rc = smcr_buf_map_lgr(link_new); 146462306a36Sopenharmony_ci if (rc) 146562306a36Sopenharmony_ci goto out_err; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci rc = smc_llc_send_add_link(link, 146862306a36Sopenharmony_ci link_new->smcibdev->mac[link_new->ibport-1], 146962306a36Sopenharmony_ci link_new->gid, link_new, SMC_LLC_REQ); 147062306a36Sopenharmony_ci if (rc) 147162306a36Sopenharmony_ci goto out_err; 147262306a36Sopenharmony_ci send_req_add_link_resp = false; 147362306a36Sopenharmony_ci /* receive ADD LINK response over the RoCE fabric */ 147462306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK); 147562306a36Sopenharmony_ci if (!qentry) { 147662306a36Sopenharmony_ci rc = -ETIMEDOUT; 147762306a36Sopenharmony_ci goto out_err; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci add_llc = &qentry->msg.add_link; 148062306a36Sopenharmony_ci if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) { 148162306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 148262306a36Sopenharmony_ci rc = -ENOLINK; 148362306a36Sopenharmony_ci goto out_err; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci if (lgr->type == SMC_LGR_SINGLE && 148662306a36Sopenharmony_ci (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && 148762306a36Sopenharmony_ci (lgr->smc_version == SMC_V2 || 148862306a36Sopenharmony_ci !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN)))) { 148962306a36Sopenharmony_ci lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci smc_llc_save_add_link_info(link_new, add_llc); 149262306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci rc = smc_ib_ready_link(link_new); 149562306a36Sopenharmony_ci if (rc) 149662306a36Sopenharmony_ci goto out_err; 149762306a36Sopenharmony_ci rc = smcr_buf_reg_lgr(link_new); 149862306a36Sopenharmony_ci if (rc) 149962306a36Sopenharmony_ci goto out_err; 150062306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2) { 150162306a36Sopenharmony_ci smc_llc_save_add_link_rkeys(link, link_new); 150262306a36Sopenharmony_ci } else { 150362306a36Sopenharmony_ci rc = smc_llc_srv_rkey_exchange(link, link_new); 150462306a36Sopenharmony_ci if (rc) 150562306a36Sopenharmony_ci goto out_err; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t); 150862306a36Sopenharmony_ci if (rc) 150962306a36Sopenharmony_ci goto out_err; 151062306a36Sopenharmony_ci kfree(ini); 151162306a36Sopenharmony_ci return 0; 151262306a36Sopenharmony_ciout_err: 151362306a36Sopenharmony_ci if (link_new) { 151462306a36Sopenharmony_ci link_new->state = SMC_LNK_INACTIVE; 151562306a36Sopenharmony_ci smcr_link_clear(link_new, false); 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ciout: 151862306a36Sopenharmony_ci kfree(ini); 151962306a36Sopenharmony_ci if (send_req_add_link_resp) 152062306a36Sopenharmony_ci smc_llc_send_req_add_link_response(req_qentry); 152162306a36Sopenharmony_ci return rc; 152262306a36Sopenharmony_ci} 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic void smc_llc_process_srv_add_link(struct smc_link_group *lgr) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci struct smc_link *link = lgr->llc_flow_lcl.qentry->link; 152762306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 152862306a36Sopenharmony_ci int rc; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci down_write(&lgr->llc_conf_mutex); 153362306a36Sopenharmony_ci rc = smc_llc_srv_add_link(link, qentry); 153462306a36Sopenharmony_ci if (!rc && lgr->type == SMC_LGR_SYMMETRIC) { 153562306a36Sopenharmony_ci /* delete any asymmetric link */ 153662306a36Sopenharmony_ci smc_llc_delete_asym_link(lgr); 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci up_write(&lgr->llc_conf_mutex); 153962306a36Sopenharmony_ci kfree(qentry); 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci/* enqueue a local add_link req to trigger a new add_link flow */ 154362306a36Sopenharmony_civoid smc_llc_add_link_local(struct smc_link *link) 154462306a36Sopenharmony_ci{ 154562306a36Sopenharmony_ci struct smc_llc_msg_add_link add_llc = {}; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK; 154862306a36Sopenharmony_ci smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc)); 154962306a36Sopenharmony_ci /* no dev and port needed */ 155062306a36Sopenharmony_ci smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc); 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci/* worker to process an add link message */ 155462306a36Sopenharmony_cistatic void smc_llc_add_link_work(struct work_struct *work) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci struct smc_link_group *lgr = container_of(work, struct smc_link_group, 155762306a36Sopenharmony_ci llc_add_link_work); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (list_empty(&lgr->list)) { 156062306a36Sopenharmony_ci /* link group is terminating */ 156162306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 156262306a36Sopenharmony_ci goto out; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (lgr->role == SMC_CLNT) 156662306a36Sopenharmony_ci smc_llc_process_cli_add_link(lgr); 156762306a36Sopenharmony_ci else 156862306a36Sopenharmony_ci smc_llc_process_srv_add_link(lgr); 156962306a36Sopenharmony_ciout: 157062306a36Sopenharmony_ci if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_REQ_ADD_LINK) 157162306a36Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci/* enqueue a local del_link msg to trigger a new del_link flow, 157562306a36Sopenharmony_ci * called only for role SMC_SERV 157662306a36Sopenharmony_ci */ 157762306a36Sopenharmony_civoid smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci struct smc_llc_msg_del_link del_llc = {}; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci del_llc.hd.common.llc_type = SMC_LLC_DELETE_LINK; 158262306a36Sopenharmony_ci smc_llc_init_msg_hdr(&del_llc.hd, link->lgr, sizeof(del_llc)); 158362306a36Sopenharmony_ci del_llc.link_num = del_link_id; 158462306a36Sopenharmony_ci del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH); 158562306a36Sopenharmony_ci del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 158662306a36Sopenharmony_ci smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc); 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_cistatic void smc_llc_process_cli_delete_link(struct smc_link_group *lgr) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci struct smc_link *lnk_del = NULL, *lnk_asym, *lnk; 159262306a36Sopenharmony_ci struct smc_llc_msg_del_link *del_llc; 159362306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 159462306a36Sopenharmony_ci int active_links; 159562306a36Sopenharmony_ci int lnk_idx; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 159862306a36Sopenharmony_ci lnk = qentry->link; 159962306a36Sopenharmony_ci del_llc = &qentry->msg.delete_link; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { 160262306a36Sopenharmony_ci smc_lgr_terminate_sched(lgr); 160362306a36Sopenharmony_ci goto out; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci down_write(&lgr->llc_conf_mutex); 160662306a36Sopenharmony_ci /* delete single link */ 160762306a36Sopenharmony_ci for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) { 160862306a36Sopenharmony_ci if (lgr->lnk[lnk_idx].link_id != del_llc->link_num) 160962306a36Sopenharmony_ci continue; 161062306a36Sopenharmony_ci lnk_del = &lgr->lnk[lnk_idx]; 161162306a36Sopenharmony_ci break; 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci del_llc->hd.flags |= SMC_LLC_FLAG_RESP; 161462306a36Sopenharmony_ci if (!lnk_del) { 161562306a36Sopenharmony_ci /* link was not found */ 161662306a36Sopenharmony_ci del_llc->reason = htonl(SMC_LLC_DEL_NOLNK); 161762306a36Sopenharmony_ci smc_llc_send_message(lnk, &qentry->msg); 161862306a36Sopenharmony_ci goto out_unlock; 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci lnk_asym = smc_llc_find_asym_link(lgr); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci del_llc->reason = 0; 162362306a36Sopenharmony_ci smc_llc_send_message(lnk, &qentry->msg); /* response */ 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (smc_link_downing(&lnk_del->state)) 162662306a36Sopenharmony_ci smc_switch_conns(lgr, lnk_del, false); 162762306a36Sopenharmony_ci smcr_link_clear(lnk_del, true); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci active_links = smc_llc_active_link_count(lgr); 163062306a36Sopenharmony_ci if (lnk_del == lnk_asym) { 163162306a36Sopenharmony_ci /* expected deletion of asym link, don't change lgr state */ 163262306a36Sopenharmony_ci } else if (active_links == 1) { 163362306a36Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); 163462306a36Sopenharmony_ci } else if (!active_links) { 163562306a36Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_NONE); 163662306a36Sopenharmony_ci smc_lgr_terminate_sched(lgr); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ciout_unlock: 163962306a36Sopenharmony_ci up_write(&lgr->llc_conf_mutex); 164062306a36Sopenharmony_ciout: 164162306a36Sopenharmony_ci kfree(qentry); 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci/* try to send a DELETE LINK ALL request on any active link, 164562306a36Sopenharmony_ci * waiting for send completion 164662306a36Sopenharmony_ci */ 164762306a36Sopenharmony_civoid smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord, u32 rsn) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci struct smc_llc_msg_del_link delllc = {}; 165062306a36Sopenharmony_ci int i; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci delllc.hd.common.llc_type = SMC_LLC_DELETE_LINK; 165362306a36Sopenharmony_ci smc_llc_init_msg_hdr(&delllc.hd, lgr, sizeof(delllc)); 165462306a36Sopenharmony_ci if (ord) 165562306a36Sopenharmony_ci delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 165662306a36Sopenharmony_ci delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 165762306a36Sopenharmony_ci delllc.reason = htonl(rsn); 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 166062306a36Sopenharmony_ci if (!smc_link_sendable(&lgr->lnk[i])) 166162306a36Sopenharmony_ci continue; 166262306a36Sopenharmony_ci if (!smc_llc_send_message_wait(&lgr->lnk[i], &delllc)) 166362306a36Sopenharmony_ci break; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_cistatic void smc_llc_process_srv_delete_link(struct smc_link_group *lgr) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci struct smc_llc_msg_del_link *del_llc; 167062306a36Sopenharmony_ci struct smc_link *lnk, *lnk_del; 167162306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 167262306a36Sopenharmony_ci int active_links; 167362306a36Sopenharmony_ci int i; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci down_write(&lgr->llc_conf_mutex); 167662306a36Sopenharmony_ci qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); 167762306a36Sopenharmony_ci lnk = qentry->link; 167862306a36Sopenharmony_ci del_llc = &qentry->msg.delete_link; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci if (qentry->msg.delete_link.hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { 168162306a36Sopenharmony_ci /* delete entire lgr */ 168262306a36Sopenharmony_ci smc_llc_send_link_delete_all(lgr, true, ntohl( 168362306a36Sopenharmony_ci qentry->msg.delete_link.reason)); 168462306a36Sopenharmony_ci smc_lgr_terminate_sched(lgr); 168562306a36Sopenharmony_ci goto out; 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci /* delete single link */ 168862306a36Sopenharmony_ci lnk_del = NULL; 168962306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 169062306a36Sopenharmony_ci if (lgr->lnk[i].link_id == del_llc->link_num) { 169162306a36Sopenharmony_ci lnk_del = &lgr->lnk[i]; 169262306a36Sopenharmony_ci break; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci if (!lnk_del) 169662306a36Sopenharmony_ci goto out; /* asymmetric link already deleted */ 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (smc_link_downing(&lnk_del->state)) { 169962306a36Sopenharmony_ci if (smc_switch_conns(lgr, lnk_del, false)) 170062306a36Sopenharmony_ci smc_wr_tx_wait_no_pending_sends(lnk_del); 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci if (!list_empty(&lgr->list)) { 170362306a36Sopenharmony_ci /* qentry is either a request from peer (send it back to 170462306a36Sopenharmony_ci * initiate the DELETE_LINK processing), or a locally 170562306a36Sopenharmony_ci * enqueued DELETE_LINK request (forward it) 170662306a36Sopenharmony_ci */ 170762306a36Sopenharmony_ci if (!smc_llc_send_message(lnk, &qentry->msg)) { 170862306a36Sopenharmony_ci struct smc_llc_qentry *qentry2; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci qentry2 = smc_llc_wait(lgr, lnk, SMC_LLC_WAIT_TIME, 171162306a36Sopenharmony_ci SMC_LLC_DELETE_LINK); 171262306a36Sopenharmony_ci if (qentry2) 171362306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci smcr_link_clear(lnk_del, true); 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci active_links = smc_llc_active_link_count(lgr); 171962306a36Sopenharmony_ci if (active_links == 1) { 172062306a36Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); 172162306a36Sopenharmony_ci } else if (!active_links) { 172262306a36Sopenharmony_ci smcr_lgr_set_type(lgr, SMC_LGR_NONE); 172362306a36Sopenharmony_ci smc_lgr_terminate_sched(lgr); 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) { 172762306a36Sopenharmony_ci /* trigger setup of asymm alt link */ 172862306a36Sopenharmony_ci smc_llc_add_link_local(lnk); 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ciout: 173162306a36Sopenharmony_ci up_write(&lgr->llc_conf_mutex); 173262306a36Sopenharmony_ci kfree(qentry); 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic void smc_llc_delete_link_work(struct work_struct *work) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci struct smc_link_group *lgr = container_of(work, struct smc_link_group, 173862306a36Sopenharmony_ci llc_del_link_work); 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (list_empty(&lgr->list)) { 174162306a36Sopenharmony_ci /* link group is terminating */ 174262306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 174362306a36Sopenharmony_ci goto out; 174462306a36Sopenharmony_ci } 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci if (lgr->role == SMC_CLNT) 174762306a36Sopenharmony_ci smc_llc_process_cli_delete_link(lgr); 174862306a36Sopenharmony_ci else 174962306a36Sopenharmony_ci smc_llc_process_srv_delete_link(lgr); 175062306a36Sopenharmony_ciout: 175162306a36Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 175262306a36Sopenharmony_ci} 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci/* process a confirm_rkey request from peer, remote flow */ 175562306a36Sopenharmony_cistatic void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr) 175662306a36Sopenharmony_ci{ 175762306a36Sopenharmony_ci struct smc_llc_msg_confirm_rkey *llc; 175862306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 175962306a36Sopenharmony_ci struct smc_link *link; 176062306a36Sopenharmony_ci int num_entries; 176162306a36Sopenharmony_ci int rk_idx; 176262306a36Sopenharmony_ci int i; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci qentry = lgr->llc_flow_rmt.qentry; 176562306a36Sopenharmony_ci llc = &qentry->msg.confirm_rkey; 176662306a36Sopenharmony_ci link = qentry->link; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci num_entries = llc->rtoken[0].num_rkeys; 176962306a36Sopenharmony_ci if (num_entries > SMC_LLC_RKEYS_PER_MSG) 177062306a36Sopenharmony_ci goto out_err; 177162306a36Sopenharmony_ci /* first rkey entry is for receiving link */ 177262306a36Sopenharmony_ci rk_idx = smc_rtoken_add(link, 177362306a36Sopenharmony_ci llc->rtoken[0].rmb_vaddr, 177462306a36Sopenharmony_ci llc->rtoken[0].rmb_key); 177562306a36Sopenharmony_ci if (rk_idx < 0) 177662306a36Sopenharmony_ci goto out_err; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++) 177962306a36Sopenharmony_ci smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id, 178062306a36Sopenharmony_ci llc->rtoken[i].rmb_vaddr, 178162306a36Sopenharmony_ci llc->rtoken[i].rmb_key); 178262306a36Sopenharmony_ci /* max links is 3 so there is no need to support conf_rkey_cont msgs */ 178362306a36Sopenharmony_ci goto out; 178462306a36Sopenharmony_ciout_err: 178562306a36Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 178662306a36Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY; 178762306a36Sopenharmony_ciout: 178862306a36Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RESP; 178962306a36Sopenharmony_ci smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc)); 179062306a36Sopenharmony_ci smc_llc_send_message(link, &qentry->msg); 179162306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 179262306a36Sopenharmony_ci} 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci/* process a delete_rkey request from peer, remote flow */ 179562306a36Sopenharmony_cistatic void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr) 179662306a36Sopenharmony_ci{ 179762306a36Sopenharmony_ci struct smc_llc_msg_delete_rkey *llc; 179862306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 179962306a36Sopenharmony_ci struct smc_link *link; 180062306a36Sopenharmony_ci u8 err_mask = 0; 180162306a36Sopenharmony_ci int i, max; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci qentry = lgr->llc_flow_rmt.qentry; 180462306a36Sopenharmony_ci llc = &qentry->msg.delete_rkey; 180562306a36Sopenharmony_ci link = qentry->link; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (lgr->smc_version == SMC_V2) { 180862306a36Sopenharmony_ci struct smc_llc_msg_delete_rkey_v2 *llcv2; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc)); 181162306a36Sopenharmony_ci llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2; 181262306a36Sopenharmony_ci llcv2->num_inval_rkeys = 0; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); 181562306a36Sopenharmony_ci for (i = 0; i < max; i++) { 181662306a36Sopenharmony_ci if (smc_rtoken_delete(link, llcv2->rkey[i])) 181762306a36Sopenharmony_ci llcv2->num_inval_rkeys++; 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci memset(&llc->rkey[0], 0, sizeof(llc->rkey)); 182062306a36Sopenharmony_ci memset(&llc->reserved2, 0, sizeof(llc->reserved2)); 182162306a36Sopenharmony_ci smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc)); 182262306a36Sopenharmony_ci if (llcv2->num_inval_rkeys) { 182362306a36Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 182462306a36Sopenharmony_ci llc->err_mask = llcv2->num_inval_rkeys; 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci goto finish; 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX); 183062306a36Sopenharmony_ci for (i = 0; i < max; i++) { 183162306a36Sopenharmony_ci if (smc_rtoken_delete(link, llc->rkey[i])) 183262306a36Sopenharmony_ci err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i); 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci if (err_mask) { 183562306a36Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 183662306a36Sopenharmony_ci llc->err_mask = err_mask; 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_cifinish: 183962306a36Sopenharmony_ci llc->hd.flags |= SMC_LLC_FLAG_RESP; 184062306a36Sopenharmony_ci smc_llc_send_message(link, &qentry->msg); 184162306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); 184262306a36Sopenharmony_ci} 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_cistatic void smc_llc_protocol_violation(struct smc_link_group *lgr, u8 type) 184562306a36Sopenharmony_ci{ 184662306a36Sopenharmony_ci pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu LLC protocol violation: " 184762306a36Sopenharmony_ci "llc_type %d\n", SMC_LGR_ID_SIZE, &lgr->id, 184862306a36Sopenharmony_ci lgr->net->net_cookie, type); 184962306a36Sopenharmony_ci smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_PROT_VIOL); 185062306a36Sopenharmony_ci smc_lgr_terminate_sched(lgr); 185162306a36Sopenharmony_ci} 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci/* flush the llc event queue */ 185462306a36Sopenharmony_cistatic void smc_llc_event_flush(struct smc_link_group *lgr) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci struct smc_llc_qentry *qentry, *q; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci spin_lock_bh(&lgr->llc_event_q_lock); 185962306a36Sopenharmony_ci list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) { 186062306a36Sopenharmony_ci list_del_init(&qentry->list); 186162306a36Sopenharmony_ci kfree(qentry); 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_event_q_lock); 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic void smc_llc_event_handler(struct smc_llc_qentry *qentry) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci union smc_llc_msg *llc = &qentry->msg; 186962306a36Sopenharmony_ci struct smc_link *link = qentry->link; 187062306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci if (!smc_link_usable(link)) 187362306a36Sopenharmony_ci goto out; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci switch (llc->raw.hdr.common.llc_type) { 187662306a36Sopenharmony_ci case SMC_LLC_TEST_LINK: 187762306a36Sopenharmony_ci llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP; 187862306a36Sopenharmony_ci smc_llc_send_message(link, llc); 187962306a36Sopenharmony_ci break; 188062306a36Sopenharmony_ci case SMC_LLC_ADD_LINK: 188162306a36Sopenharmony_ci if (list_empty(&lgr->list)) 188262306a36Sopenharmony_ci goto out; /* lgr is terminating */ 188362306a36Sopenharmony_ci if (lgr->role == SMC_CLNT) { 188462306a36Sopenharmony_ci if (smc_llc_is_local_add_link(llc)) { 188562306a36Sopenharmony_ci if (lgr->llc_flow_lcl.type == 188662306a36Sopenharmony_ci SMC_LLC_FLOW_ADD_LINK) 188762306a36Sopenharmony_ci break; /* add_link in progress */ 188862306a36Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_lcl, 188962306a36Sopenharmony_ci qentry)) { 189062306a36Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci return; 189362306a36Sopenharmony_ci } 189462306a36Sopenharmony_ci if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && 189562306a36Sopenharmony_ci !lgr->llc_flow_lcl.qentry) { 189662306a36Sopenharmony_ci /* a flow is waiting for this message */ 189762306a36Sopenharmony_ci smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, 189862306a36Sopenharmony_ci qentry); 189962306a36Sopenharmony_ci wake_up(&lgr->llc_msg_waiter); 190062306a36Sopenharmony_ci return; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci if (lgr->llc_flow_lcl.type == 190362306a36Sopenharmony_ci SMC_LLC_FLOW_REQ_ADD_LINK) { 190462306a36Sopenharmony_ci /* server started add_link processing */ 190562306a36Sopenharmony_ci lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK; 190662306a36Sopenharmony_ci smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, 190762306a36Sopenharmony_ci qentry); 190862306a36Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 190962306a36Sopenharmony_ci return; 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 191262306a36Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 191562306a36Sopenharmony_ci /* as smc server, handle client suggestion */ 191662306a36Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci return; 191962306a36Sopenharmony_ci case SMC_LLC_CONFIRM_LINK: 192062306a36Sopenharmony_ci case SMC_LLC_ADD_LINK_CONT: 192162306a36Sopenharmony_ci if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { 192262306a36Sopenharmony_ci /* a flow is waiting for this message */ 192362306a36Sopenharmony_ci smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); 192462306a36Sopenharmony_ci wake_up(&lgr->llc_msg_waiter); 192562306a36Sopenharmony_ci return; 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci break; 192862306a36Sopenharmony_ci case SMC_LLC_DELETE_LINK: 192962306a36Sopenharmony_ci if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && 193062306a36Sopenharmony_ci !lgr->llc_flow_lcl.qentry) { 193162306a36Sopenharmony_ci /* DEL LINK REQ during ADD LINK SEQ */ 193262306a36Sopenharmony_ci smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); 193362306a36Sopenharmony_ci wake_up(&lgr->llc_msg_waiter); 193462306a36Sopenharmony_ci } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 193562306a36Sopenharmony_ci schedule_work(&lgr->llc_del_link_work); 193662306a36Sopenharmony_ci } 193762306a36Sopenharmony_ci return; 193862306a36Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY: 193962306a36Sopenharmony_ci /* new request from remote, assign to remote flow */ 194062306a36Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 194162306a36Sopenharmony_ci /* process here, does not wait for more llc msgs */ 194262306a36Sopenharmony_ci smc_llc_rmt_conf_rkey(lgr); 194362306a36Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci return; 194662306a36Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY_CONT: 194762306a36Sopenharmony_ci /* not used because max links is 3, and 3 rkeys fit into 194862306a36Sopenharmony_ci * one CONFIRM_RKEY message 194962306a36Sopenharmony_ci */ 195062306a36Sopenharmony_ci break; 195162306a36Sopenharmony_ci case SMC_LLC_DELETE_RKEY: 195262306a36Sopenharmony_ci /* new request from remote, assign to remote flow */ 195362306a36Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { 195462306a36Sopenharmony_ci /* process here, does not wait for more llc msgs */ 195562306a36Sopenharmony_ci smc_llc_rmt_delete_rkey(lgr); 195662306a36Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci return; 195962306a36Sopenharmony_ci case SMC_LLC_REQ_ADD_LINK: 196062306a36Sopenharmony_ci /* handle response here, smc_llc_flow_stop() cannot be called 196162306a36Sopenharmony_ci * in tasklet context 196262306a36Sopenharmony_ci */ 196362306a36Sopenharmony_ci if (lgr->role == SMC_CLNT && 196462306a36Sopenharmony_ci lgr->llc_flow_lcl.type == SMC_LLC_FLOW_REQ_ADD_LINK && 196562306a36Sopenharmony_ci (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP)) { 196662306a36Sopenharmony_ci smc_llc_flow_stop(link->lgr, &lgr->llc_flow_lcl); 196762306a36Sopenharmony_ci } else if (lgr->role == SMC_SERV) { 196862306a36Sopenharmony_ci if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { 196962306a36Sopenharmony_ci /* as smc server, handle client suggestion */ 197062306a36Sopenharmony_ci lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK; 197162306a36Sopenharmony_ci schedule_work(&lgr->llc_add_link_work); 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci return; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci break; 197662306a36Sopenharmony_ci default: 197762306a36Sopenharmony_ci smc_llc_protocol_violation(lgr, llc->raw.hdr.common.type); 197862306a36Sopenharmony_ci break; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ciout: 198162306a36Sopenharmony_ci kfree(qentry); 198262306a36Sopenharmony_ci} 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci/* worker to process llc messages on the event queue */ 198562306a36Sopenharmony_cistatic void smc_llc_event_work(struct work_struct *work) 198662306a36Sopenharmony_ci{ 198762306a36Sopenharmony_ci struct smc_link_group *lgr = container_of(work, struct smc_link_group, 198862306a36Sopenharmony_ci llc_event_work); 198962306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci if (!lgr->llc_flow_lcl.type && lgr->delayed_event) { 199262306a36Sopenharmony_ci qentry = lgr->delayed_event; 199362306a36Sopenharmony_ci lgr->delayed_event = NULL; 199462306a36Sopenharmony_ci if (smc_link_usable(qentry->link)) 199562306a36Sopenharmony_ci smc_llc_event_handler(qentry); 199662306a36Sopenharmony_ci else 199762306a36Sopenharmony_ci kfree(qentry); 199862306a36Sopenharmony_ci } 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ciagain: 200162306a36Sopenharmony_ci spin_lock_bh(&lgr->llc_event_q_lock); 200262306a36Sopenharmony_ci if (!list_empty(&lgr->llc_event_q)) { 200362306a36Sopenharmony_ci qentry = list_first_entry(&lgr->llc_event_q, 200462306a36Sopenharmony_ci struct smc_llc_qentry, list); 200562306a36Sopenharmony_ci list_del_init(&qentry->list); 200662306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_event_q_lock); 200762306a36Sopenharmony_ci smc_llc_event_handler(qentry); 200862306a36Sopenharmony_ci goto again; 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci spin_unlock_bh(&lgr->llc_event_q_lock); 201162306a36Sopenharmony_ci} 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci/* process llc responses in tasklet context */ 201462306a36Sopenharmony_cistatic void smc_llc_rx_response(struct smc_link *link, 201562306a36Sopenharmony_ci struct smc_llc_qentry *qentry) 201662306a36Sopenharmony_ci{ 201762306a36Sopenharmony_ci enum smc_llc_flowtype flowtype = link->lgr->llc_flow_lcl.type; 201862306a36Sopenharmony_ci struct smc_llc_flow *flow = &link->lgr->llc_flow_lcl; 201962306a36Sopenharmony_ci u8 llc_type = qentry->msg.raw.hdr.common.llc_type; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci switch (llc_type) { 202262306a36Sopenharmony_ci case SMC_LLC_TEST_LINK: 202362306a36Sopenharmony_ci if (smc_link_active(link)) 202462306a36Sopenharmony_ci complete(&link->llc_testlink_resp); 202562306a36Sopenharmony_ci break; 202662306a36Sopenharmony_ci case SMC_LLC_ADD_LINK: 202762306a36Sopenharmony_ci case SMC_LLC_ADD_LINK_CONT: 202862306a36Sopenharmony_ci case SMC_LLC_CONFIRM_LINK: 202962306a36Sopenharmony_ci if (flowtype != SMC_LLC_FLOW_ADD_LINK || flow->qentry) 203062306a36Sopenharmony_ci break; /* drop out-of-flow response */ 203162306a36Sopenharmony_ci goto assign; 203262306a36Sopenharmony_ci case SMC_LLC_DELETE_LINK: 203362306a36Sopenharmony_ci if (flowtype != SMC_LLC_FLOW_DEL_LINK || flow->qentry) 203462306a36Sopenharmony_ci break; /* drop out-of-flow response */ 203562306a36Sopenharmony_ci goto assign; 203662306a36Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY: 203762306a36Sopenharmony_ci case SMC_LLC_DELETE_RKEY: 203862306a36Sopenharmony_ci if (flowtype != SMC_LLC_FLOW_RKEY || flow->qentry) 203962306a36Sopenharmony_ci break; /* drop out-of-flow response */ 204062306a36Sopenharmony_ci goto assign; 204162306a36Sopenharmony_ci case SMC_LLC_CONFIRM_RKEY_CONT: 204262306a36Sopenharmony_ci /* not used because max links is 3 */ 204362306a36Sopenharmony_ci break; 204462306a36Sopenharmony_ci default: 204562306a36Sopenharmony_ci smc_llc_protocol_violation(link->lgr, 204662306a36Sopenharmony_ci qentry->msg.raw.hdr.common.type); 204762306a36Sopenharmony_ci break; 204862306a36Sopenharmony_ci } 204962306a36Sopenharmony_ci kfree(qentry); 205062306a36Sopenharmony_ci return; 205162306a36Sopenharmony_ciassign: 205262306a36Sopenharmony_ci /* assign responses to the local flow, we requested them */ 205362306a36Sopenharmony_ci smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry); 205462306a36Sopenharmony_ci wake_up(&link->lgr->llc_msg_waiter); 205562306a36Sopenharmony_ci} 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_cistatic void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc) 205862306a36Sopenharmony_ci{ 205962306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 206062306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 206162306a36Sopenharmony_ci unsigned long flags; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC); 206462306a36Sopenharmony_ci if (!qentry) 206562306a36Sopenharmony_ci return; 206662306a36Sopenharmony_ci qentry->link = link; 206762306a36Sopenharmony_ci INIT_LIST_HEAD(&qentry->list); 206862306a36Sopenharmony_ci memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg)); 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci /* process responses immediately */ 207162306a36Sopenharmony_ci if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) && 207262306a36Sopenharmony_ci llc->raw.hdr.common.llc_type != SMC_LLC_REQ_ADD_LINK) { 207362306a36Sopenharmony_ci smc_llc_rx_response(link, qentry); 207462306a36Sopenharmony_ci return; 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci /* add requests to event queue */ 207862306a36Sopenharmony_ci spin_lock_irqsave(&lgr->llc_event_q_lock, flags); 207962306a36Sopenharmony_ci list_add_tail(&qentry->list, &lgr->llc_event_q); 208062306a36Sopenharmony_ci spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags); 208162306a36Sopenharmony_ci queue_work(system_highpri_wq, &lgr->llc_event_work); 208262306a36Sopenharmony_ci} 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci/* copy received msg and add it to the event queue */ 208562306a36Sopenharmony_cistatic void smc_llc_rx_handler(struct ib_wc *wc, void *buf) 208662306a36Sopenharmony_ci{ 208762306a36Sopenharmony_ci struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 208862306a36Sopenharmony_ci union smc_llc_msg *llc = buf; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci if (wc->byte_len < sizeof(*llc)) 209162306a36Sopenharmony_ci return; /* short message */ 209262306a36Sopenharmony_ci if (!llc->raw.hdr.common.llc_version) { 209362306a36Sopenharmony_ci if (llc->raw.hdr.length != sizeof(*llc)) 209462306a36Sopenharmony_ci return; /* invalid message */ 209562306a36Sopenharmony_ci } else { 209662306a36Sopenharmony_ci if (llc->raw.hdr.length_v2 < sizeof(*llc)) 209762306a36Sopenharmony_ci return; /* invalid message */ 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci smc_llc_enqueue(link, llc); 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci/***************************** worker, utils *********************************/ 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic void smc_llc_testlink_work(struct work_struct *work) 210662306a36Sopenharmony_ci{ 210762306a36Sopenharmony_ci struct smc_link *link = container_of(to_delayed_work(work), 210862306a36Sopenharmony_ci struct smc_link, llc_testlink_wrk); 210962306a36Sopenharmony_ci unsigned long next_interval; 211062306a36Sopenharmony_ci unsigned long expire_time; 211162306a36Sopenharmony_ci u8 user_data[16] = { 0 }; 211262306a36Sopenharmony_ci int rc; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci if (!smc_link_active(link)) 211562306a36Sopenharmony_ci return; /* don't reschedule worker */ 211662306a36Sopenharmony_ci expire_time = link->wr_rx_tstamp + link->llc_testlink_time; 211762306a36Sopenharmony_ci if (time_is_after_jiffies(expire_time)) { 211862306a36Sopenharmony_ci next_interval = expire_time - jiffies; 211962306a36Sopenharmony_ci goto out; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci reinit_completion(&link->llc_testlink_resp); 212262306a36Sopenharmony_ci smc_llc_send_test_link(link, user_data); 212362306a36Sopenharmony_ci /* receive TEST LINK response over RoCE fabric */ 212462306a36Sopenharmony_ci rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp, 212562306a36Sopenharmony_ci SMC_LLC_WAIT_TIME); 212662306a36Sopenharmony_ci if (!smc_link_active(link)) 212762306a36Sopenharmony_ci return; /* link state changed */ 212862306a36Sopenharmony_ci if (rc <= 0) { 212962306a36Sopenharmony_ci smcr_link_down_cond_sched(link); 213062306a36Sopenharmony_ci return; 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci next_interval = link->llc_testlink_time; 213362306a36Sopenharmony_ciout: 213462306a36Sopenharmony_ci schedule_delayed_work(&link->llc_testlink_wrk, next_interval); 213562306a36Sopenharmony_ci} 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_civoid smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc) 213862306a36Sopenharmony_ci{ 213962306a36Sopenharmony_ci struct net *net = sock_net(smc->clcsock->sk); 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci INIT_WORK(&lgr->llc_event_work, smc_llc_event_work); 214262306a36Sopenharmony_ci INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work); 214362306a36Sopenharmony_ci INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work); 214462306a36Sopenharmony_ci INIT_LIST_HEAD(&lgr->llc_event_q); 214562306a36Sopenharmony_ci spin_lock_init(&lgr->llc_event_q_lock); 214662306a36Sopenharmony_ci spin_lock_init(&lgr->llc_flow_lock); 214762306a36Sopenharmony_ci init_waitqueue_head(&lgr->llc_flow_waiter); 214862306a36Sopenharmony_ci init_waitqueue_head(&lgr->llc_msg_waiter); 214962306a36Sopenharmony_ci init_rwsem(&lgr->llc_conf_mutex); 215062306a36Sopenharmony_ci lgr->llc_testlink_time = READ_ONCE(net->smc.sysctl_smcr_testlink_time); 215162306a36Sopenharmony_ci} 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci/* called after lgr was removed from lgr_list */ 215462306a36Sopenharmony_civoid smc_llc_lgr_clear(struct smc_link_group *lgr) 215562306a36Sopenharmony_ci{ 215662306a36Sopenharmony_ci smc_llc_event_flush(lgr); 215762306a36Sopenharmony_ci wake_up_all(&lgr->llc_flow_waiter); 215862306a36Sopenharmony_ci wake_up_all(&lgr->llc_msg_waiter); 215962306a36Sopenharmony_ci cancel_work_sync(&lgr->llc_event_work); 216062306a36Sopenharmony_ci cancel_work_sync(&lgr->llc_add_link_work); 216162306a36Sopenharmony_ci cancel_work_sync(&lgr->llc_del_link_work); 216262306a36Sopenharmony_ci if (lgr->delayed_event) { 216362306a36Sopenharmony_ci kfree(lgr->delayed_event); 216462306a36Sopenharmony_ci lgr->delayed_event = NULL; 216562306a36Sopenharmony_ci } 216662306a36Sopenharmony_ci} 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ciint smc_llc_link_init(struct smc_link *link) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci init_completion(&link->llc_testlink_resp); 217162306a36Sopenharmony_ci INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); 217262306a36Sopenharmony_ci return 0; 217362306a36Sopenharmony_ci} 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_civoid smc_llc_link_active(struct smc_link *link) 217662306a36Sopenharmony_ci{ 217762306a36Sopenharmony_ci pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link added: id %*phN, " 217862306a36Sopenharmony_ci "peerid %*phN, ibdev %s, ibport %d\n", 217962306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &link->lgr->id, 218062306a36Sopenharmony_ci link->lgr->net->net_cookie, 218162306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &link->link_uid, 218262306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &link->peer_link_uid, 218362306a36Sopenharmony_ci link->smcibdev->ibdev->name, link->ibport); 218462306a36Sopenharmony_ci link->state = SMC_LNK_ACTIVE; 218562306a36Sopenharmony_ci if (link->lgr->llc_testlink_time) { 218662306a36Sopenharmony_ci link->llc_testlink_time = link->lgr->llc_testlink_time; 218762306a36Sopenharmony_ci schedule_delayed_work(&link->llc_testlink_wrk, 218862306a36Sopenharmony_ci link->llc_testlink_time); 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci} 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci/* called in worker context */ 219362306a36Sopenharmony_civoid smc_llc_link_clear(struct smc_link *link, bool log) 219462306a36Sopenharmony_ci{ 219562306a36Sopenharmony_ci if (log) 219662306a36Sopenharmony_ci pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link removed: id %*phN" 219762306a36Sopenharmony_ci ", peerid %*phN, ibdev %s, ibport %d\n", 219862306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &link->lgr->id, 219962306a36Sopenharmony_ci link->lgr->net->net_cookie, 220062306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &link->link_uid, 220162306a36Sopenharmony_ci SMC_LGR_ID_SIZE, &link->peer_link_uid, 220262306a36Sopenharmony_ci link->smcibdev->ibdev->name, link->ibport); 220362306a36Sopenharmony_ci complete(&link->llc_testlink_resp); 220462306a36Sopenharmony_ci cancel_delayed_work_sync(&link->llc_testlink_wrk); 220562306a36Sopenharmony_ci} 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci/* register a new rtoken at the remote peer (for all links) */ 220862306a36Sopenharmony_ciint smc_llc_do_confirm_rkey(struct smc_link *send_link, 220962306a36Sopenharmony_ci struct smc_buf_desc *rmb_desc) 221062306a36Sopenharmony_ci{ 221162306a36Sopenharmony_ci struct smc_link_group *lgr = send_link->lgr; 221262306a36Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 221362306a36Sopenharmony_ci int rc = 0; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci rc = smc_llc_send_confirm_rkey(send_link, rmb_desc); 221662306a36Sopenharmony_ci if (rc) 221762306a36Sopenharmony_ci goto out; 221862306a36Sopenharmony_ci /* receive CONFIRM RKEY response from server over RoCE fabric */ 221962306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 222062306a36Sopenharmony_ci SMC_LLC_CONFIRM_RKEY); 222162306a36Sopenharmony_ci if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 222262306a36Sopenharmony_ci rc = -EFAULT; 222362306a36Sopenharmony_ciout: 222462306a36Sopenharmony_ci if (qentry) 222562306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 222662306a36Sopenharmony_ci return rc; 222762306a36Sopenharmony_ci} 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci/* unregister an rtoken at the remote peer */ 223062306a36Sopenharmony_ciint smc_llc_do_delete_rkey(struct smc_link_group *lgr, 223162306a36Sopenharmony_ci struct smc_buf_desc *rmb_desc) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci struct smc_llc_qentry *qentry = NULL; 223462306a36Sopenharmony_ci struct smc_link *send_link; 223562306a36Sopenharmony_ci int rc = 0; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci send_link = smc_llc_usable_link(lgr); 223862306a36Sopenharmony_ci if (!send_link) 223962306a36Sopenharmony_ci return -ENOLINK; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci /* protected by llc_flow control */ 224262306a36Sopenharmony_ci rc = smc_llc_send_delete_rkey(send_link, rmb_desc); 224362306a36Sopenharmony_ci if (rc) 224462306a36Sopenharmony_ci goto out; 224562306a36Sopenharmony_ci /* receive DELETE RKEY response from server over RoCE fabric */ 224662306a36Sopenharmony_ci qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, 224762306a36Sopenharmony_ci SMC_LLC_DELETE_RKEY); 224862306a36Sopenharmony_ci if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) 224962306a36Sopenharmony_ci rc = -EFAULT; 225062306a36Sopenharmony_ciout: 225162306a36Sopenharmony_ci if (qentry) 225262306a36Sopenharmony_ci smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); 225362306a36Sopenharmony_ci return rc; 225462306a36Sopenharmony_ci} 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_civoid smc_llc_link_set_uid(struct smc_link *link) 225762306a36Sopenharmony_ci{ 225862306a36Sopenharmony_ci __be32 link_uid; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci link_uid = htonl(*((u32 *)link->lgr->id) + link->link_id); 226162306a36Sopenharmony_ci memcpy(link->link_uid, &link_uid, SMC_LGR_ID_SIZE); 226262306a36Sopenharmony_ci} 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci/* save peers link user id, used for debug purposes */ 226562306a36Sopenharmony_civoid smc_llc_save_peer_uid(struct smc_llc_qentry *qentry) 226662306a36Sopenharmony_ci{ 226762306a36Sopenharmony_ci memcpy(qentry->link->peer_link_uid, qentry->msg.confirm_link.link_uid, 226862306a36Sopenharmony_ci SMC_LGR_ID_SIZE); 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci/* evaluate confirm link request or response */ 227262306a36Sopenharmony_ciint smc_llc_eval_conf_link(struct smc_llc_qentry *qentry, 227362306a36Sopenharmony_ci enum smc_llc_reqresp type) 227462306a36Sopenharmony_ci{ 227562306a36Sopenharmony_ci if (type == SMC_LLC_REQ) { /* SMC server assigns link_id */ 227662306a36Sopenharmony_ci qentry->link->link_id = qentry->msg.confirm_link.link_num; 227762306a36Sopenharmony_ci smc_llc_link_set_uid(qentry->link); 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC)) 228062306a36Sopenharmony_ci return -ENOTSUPP; 228162306a36Sopenharmony_ci return 0; 228262306a36Sopenharmony_ci} 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci/***************************** init, exit, misc ******************************/ 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_cistatic struct smc_wr_rx_handler smc_llc_rx_handlers[] = { 228762306a36Sopenharmony_ci { 228862306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 228962306a36Sopenharmony_ci .type = SMC_LLC_CONFIRM_LINK 229062306a36Sopenharmony_ci }, 229162306a36Sopenharmony_ci { 229262306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 229362306a36Sopenharmony_ci .type = SMC_LLC_TEST_LINK 229462306a36Sopenharmony_ci }, 229562306a36Sopenharmony_ci { 229662306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 229762306a36Sopenharmony_ci .type = SMC_LLC_ADD_LINK 229862306a36Sopenharmony_ci }, 229962306a36Sopenharmony_ci { 230062306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 230162306a36Sopenharmony_ci .type = SMC_LLC_ADD_LINK_CONT 230262306a36Sopenharmony_ci }, 230362306a36Sopenharmony_ci { 230462306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 230562306a36Sopenharmony_ci .type = SMC_LLC_DELETE_LINK 230662306a36Sopenharmony_ci }, 230762306a36Sopenharmony_ci { 230862306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 230962306a36Sopenharmony_ci .type = SMC_LLC_CONFIRM_RKEY 231062306a36Sopenharmony_ci }, 231162306a36Sopenharmony_ci { 231262306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 231362306a36Sopenharmony_ci .type = SMC_LLC_CONFIRM_RKEY_CONT 231462306a36Sopenharmony_ci }, 231562306a36Sopenharmony_ci { 231662306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 231762306a36Sopenharmony_ci .type = SMC_LLC_DELETE_RKEY 231862306a36Sopenharmony_ci }, 231962306a36Sopenharmony_ci /* V2 types */ 232062306a36Sopenharmony_ci { 232162306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 232262306a36Sopenharmony_ci .type = SMC_LLC_CONFIRM_LINK_V2 232362306a36Sopenharmony_ci }, 232462306a36Sopenharmony_ci { 232562306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 232662306a36Sopenharmony_ci .type = SMC_LLC_TEST_LINK_V2 232762306a36Sopenharmony_ci }, 232862306a36Sopenharmony_ci { 232962306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 233062306a36Sopenharmony_ci .type = SMC_LLC_ADD_LINK_V2 233162306a36Sopenharmony_ci }, 233262306a36Sopenharmony_ci { 233362306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 233462306a36Sopenharmony_ci .type = SMC_LLC_DELETE_LINK_V2 233562306a36Sopenharmony_ci }, 233662306a36Sopenharmony_ci { 233762306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 233862306a36Sopenharmony_ci .type = SMC_LLC_REQ_ADD_LINK_V2 233962306a36Sopenharmony_ci }, 234062306a36Sopenharmony_ci { 234162306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 234262306a36Sopenharmony_ci .type = SMC_LLC_CONFIRM_RKEY_V2 234362306a36Sopenharmony_ci }, 234462306a36Sopenharmony_ci { 234562306a36Sopenharmony_ci .handler = smc_llc_rx_handler, 234662306a36Sopenharmony_ci .type = SMC_LLC_DELETE_RKEY_V2 234762306a36Sopenharmony_ci }, 234862306a36Sopenharmony_ci { 234962306a36Sopenharmony_ci .handler = NULL, 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci}; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ciint __init smc_llc_init(void) 235462306a36Sopenharmony_ci{ 235562306a36Sopenharmony_ci struct smc_wr_rx_handler *handler; 235662306a36Sopenharmony_ci int rc = 0; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci for (handler = smc_llc_rx_handlers; handler->handler; handler++) { 235962306a36Sopenharmony_ci INIT_HLIST_NODE(&handler->list); 236062306a36Sopenharmony_ci rc = smc_wr_rx_register_handler(handler); 236162306a36Sopenharmony_ci if (rc) 236262306a36Sopenharmony_ci break; 236362306a36Sopenharmony_ci } 236462306a36Sopenharmony_ci return rc; 236562306a36Sopenharmony_ci} 2366