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 * Work Requests exploiting Infiniband API 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Work requests (WR) of type ib_post_send or ib_post_recv respectively 862306a36Sopenharmony_ci * are submitted to either RC SQ or RC RQ respectively 962306a36Sopenharmony_ci * (reliably connected send/receive queue) 1062306a36Sopenharmony_ci * and become work queue entries (WQEs). 1162306a36Sopenharmony_ci * While an SQ WR/WQE is pending, we track it until transmission completion. 1262306a36Sopenharmony_ci * Through a send or receive completion queue (CQ) respectively, 1362306a36Sopenharmony_ci * we get completion queue entries (CQEs) [aka work completions (WCs)]. 1462306a36Sopenharmony_ci * Since the CQ callback is called from IRQ context, we split work by using 1562306a36Sopenharmony_ci * bottom halves implemented by tasklets. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * SMC uses this to exchange LLC (link layer control) 1862306a36Sopenharmony_ci * and CDC (connection data control) messages. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Copyright IBM Corp. 2016 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Author(s): Steffen Maier <maier@linux.vnet.ibm.com> 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/atomic.h> 2662306a36Sopenharmony_ci#include <linux/hashtable.h> 2762306a36Sopenharmony_ci#include <linux/wait.h> 2862306a36Sopenharmony_ci#include <rdma/ib_verbs.h> 2962306a36Sopenharmony_ci#include <asm/div64.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "smc.h" 3262306a36Sopenharmony_ci#include "smc_wr.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define SMC_WR_MAX_POLL_CQE 10 /* max. # of compl. queue elements in 1 poll */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define SMC_WR_RX_HASH_BITS 4 3762306a36Sopenharmony_cistatic DEFINE_HASHTABLE(smc_wr_rx_hash, SMC_WR_RX_HASH_BITS); 3862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(smc_wr_rx_hash_lock); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct smc_wr_tx_pend { /* control data for a pending send request */ 4162306a36Sopenharmony_ci u64 wr_id; /* work request id sent */ 4262306a36Sopenharmony_ci smc_wr_tx_handler handler; 4362306a36Sopenharmony_ci enum ib_wc_status wc_status; /* CQE status */ 4462306a36Sopenharmony_ci struct smc_link *link; 4562306a36Sopenharmony_ci u32 idx; 4662306a36Sopenharmony_ci struct smc_wr_tx_pend_priv priv; 4762306a36Sopenharmony_ci u8 compl_requested; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/******************************** send queue *********************************/ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/*------------------------------- completion --------------------------------*/ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* returns true if at least one tx work request is pending on the given link */ 5562306a36Sopenharmony_cistatic inline bool smc_wr_is_tx_pend(struct smc_link *link) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return !bitmap_empty(link->wr_tx_mask, link->wr_tx_cnt); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* wait till all pending tx work requests on the given link are completed */ 6162306a36Sopenharmony_civoid smc_wr_tx_wait_no_pending_sends(struct smc_link *link) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci wait_event(link->wr_tx_wait, !smc_wr_is_tx_pend(link)); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic inline int smc_wr_tx_find_pending_index(struct smc_link *link, u64 wr_id) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u32 i; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (i = 0; i < link->wr_tx_cnt; i++) { 7162306a36Sopenharmony_ci if (link->wr_tx_pends[i].wr_id == wr_id) 7262306a36Sopenharmony_ci return i; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci return link->wr_tx_cnt; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic inline void smc_wr_tx_process_cqe(struct ib_wc *wc) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct smc_wr_tx_pend pnd_snd; 8062306a36Sopenharmony_ci struct smc_link *link; 8162306a36Sopenharmony_ci u32 pnd_snd_idx; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci link = wc->qp->qp_context; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (wc->opcode == IB_WC_REG_MR) { 8662306a36Sopenharmony_ci if (wc->status) 8762306a36Sopenharmony_ci link->wr_reg_state = FAILED; 8862306a36Sopenharmony_ci else 8962306a36Sopenharmony_ci link->wr_reg_state = CONFIRMED; 9062306a36Sopenharmony_ci smc_wr_wakeup_reg_wait(link); 9162306a36Sopenharmony_ci return; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci pnd_snd_idx = smc_wr_tx_find_pending_index(link, wc->wr_id); 9562306a36Sopenharmony_ci if (pnd_snd_idx == link->wr_tx_cnt) { 9662306a36Sopenharmony_ci if (link->lgr->smc_version != SMC_V2 || 9762306a36Sopenharmony_ci link->wr_tx_v2_pend->wr_id != wc->wr_id) 9862306a36Sopenharmony_ci return; 9962306a36Sopenharmony_ci link->wr_tx_v2_pend->wc_status = wc->status; 10062306a36Sopenharmony_ci memcpy(&pnd_snd, link->wr_tx_v2_pend, sizeof(pnd_snd)); 10162306a36Sopenharmony_ci /* clear the full struct smc_wr_tx_pend including .priv */ 10262306a36Sopenharmony_ci memset(link->wr_tx_v2_pend, 0, 10362306a36Sopenharmony_ci sizeof(*link->wr_tx_v2_pend)); 10462306a36Sopenharmony_ci memset(link->lgr->wr_tx_buf_v2, 0, 10562306a36Sopenharmony_ci sizeof(*link->lgr->wr_tx_buf_v2)); 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci link->wr_tx_pends[pnd_snd_idx].wc_status = wc->status; 10862306a36Sopenharmony_ci if (link->wr_tx_pends[pnd_snd_idx].compl_requested) 10962306a36Sopenharmony_ci complete(&link->wr_tx_compl[pnd_snd_idx]); 11062306a36Sopenharmony_ci memcpy(&pnd_snd, &link->wr_tx_pends[pnd_snd_idx], 11162306a36Sopenharmony_ci sizeof(pnd_snd)); 11262306a36Sopenharmony_ci /* clear the full struct smc_wr_tx_pend including .priv */ 11362306a36Sopenharmony_ci memset(&link->wr_tx_pends[pnd_snd_idx], 0, 11462306a36Sopenharmony_ci sizeof(link->wr_tx_pends[pnd_snd_idx])); 11562306a36Sopenharmony_ci memset(&link->wr_tx_bufs[pnd_snd_idx], 0, 11662306a36Sopenharmony_ci sizeof(link->wr_tx_bufs[pnd_snd_idx])); 11762306a36Sopenharmony_ci if (!test_and_clear_bit(pnd_snd_idx, link->wr_tx_mask)) 11862306a36Sopenharmony_ci return; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (wc->status) { 12262306a36Sopenharmony_ci if (link->lgr->smc_version == SMC_V2) { 12362306a36Sopenharmony_ci memset(link->wr_tx_v2_pend, 0, 12462306a36Sopenharmony_ci sizeof(*link->wr_tx_v2_pend)); 12562306a36Sopenharmony_ci memset(link->lgr->wr_tx_buf_v2, 0, 12662306a36Sopenharmony_ci sizeof(*link->lgr->wr_tx_buf_v2)); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci /* terminate link */ 12962306a36Sopenharmony_ci smcr_link_down_cond_sched(link); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci if (pnd_snd.handler) 13262306a36Sopenharmony_ci pnd_snd.handler(&pnd_snd.priv, link, wc->status); 13362306a36Sopenharmony_ci wake_up(&link->wr_tx_wait); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void smc_wr_tx_tasklet_fn(struct tasklet_struct *t) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct smc_ib_device *dev = from_tasklet(dev, t, send_tasklet); 13962306a36Sopenharmony_ci struct ib_wc wc[SMC_WR_MAX_POLL_CQE]; 14062306a36Sopenharmony_ci int i = 0, rc; 14162306a36Sopenharmony_ci int polled = 0; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciagain: 14462306a36Sopenharmony_ci polled++; 14562306a36Sopenharmony_ci do { 14662306a36Sopenharmony_ci memset(&wc, 0, sizeof(wc)); 14762306a36Sopenharmony_ci rc = ib_poll_cq(dev->roce_cq_send, SMC_WR_MAX_POLL_CQE, wc); 14862306a36Sopenharmony_ci if (polled == 1) { 14962306a36Sopenharmony_ci ib_req_notify_cq(dev->roce_cq_send, 15062306a36Sopenharmony_ci IB_CQ_NEXT_COMP | 15162306a36Sopenharmony_ci IB_CQ_REPORT_MISSED_EVENTS); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci if (!rc) 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci for (i = 0; i < rc; i++) 15662306a36Sopenharmony_ci smc_wr_tx_process_cqe(&wc[i]); 15762306a36Sopenharmony_ci } while (rc > 0); 15862306a36Sopenharmony_ci if (polled == 1) 15962306a36Sopenharmony_ci goto again; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_civoid smc_wr_tx_cq_handler(struct ib_cq *ib_cq, void *cq_context) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct smc_ib_device *dev = (struct smc_ib_device *)cq_context; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci tasklet_schedule(&dev->send_tasklet); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/*---------------------------- request submission ---------------------------*/ 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic inline int smc_wr_tx_get_free_slot_index(struct smc_link *link, u32 *idx) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci *idx = link->wr_tx_cnt; 17462306a36Sopenharmony_ci if (!smc_link_sendable(link)) 17562306a36Sopenharmony_ci return -ENOLINK; 17662306a36Sopenharmony_ci for_each_clear_bit(*idx, link->wr_tx_mask, link->wr_tx_cnt) { 17762306a36Sopenharmony_ci if (!test_and_set_bit(*idx, link->wr_tx_mask)) 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci *idx = link->wr_tx_cnt; 18162306a36Sopenharmony_ci return -EBUSY; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/** 18562306a36Sopenharmony_ci * smc_wr_tx_get_free_slot() - returns buffer for message assembly, 18662306a36Sopenharmony_ci * and sets info for pending transmit tracking 18762306a36Sopenharmony_ci * @link: Pointer to smc_link used to later send the message. 18862306a36Sopenharmony_ci * @handler: Send completion handler function pointer. 18962306a36Sopenharmony_ci * @wr_buf: Out value returns pointer to message buffer. 19062306a36Sopenharmony_ci * @wr_rdma_buf: Out value returns pointer to rdma work request. 19162306a36Sopenharmony_ci * @wr_pend_priv: Out value returns pointer serving as handler context. 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Return: 0 on success, or -errno on error. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ciint smc_wr_tx_get_free_slot(struct smc_link *link, 19662306a36Sopenharmony_ci smc_wr_tx_handler handler, 19762306a36Sopenharmony_ci struct smc_wr_buf **wr_buf, 19862306a36Sopenharmony_ci struct smc_rdma_wr **wr_rdma_buf, 19962306a36Sopenharmony_ci struct smc_wr_tx_pend_priv **wr_pend_priv) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct smc_link_group *lgr = smc_get_lgr(link); 20262306a36Sopenharmony_ci struct smc_wr_tx_pend *wr_pend; 20362306a36Sopenharmony_ci u32 idx = link->wr_tx_cnt; 20462306a36Sopenharmony_ci struct ib_send_wr *wr_ib; 20562306a36Sopenharmony_ci u64 wr_id; 20662306a36Sopenharmony_ci int rc; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci *wr_buf = NULL; 20962306a36Sopenharmony_ci *wr_pend_priv = NULL; 21062306a36Sopenharmony_ci if (in_softirq() || lgr->terminating) { 21162306a36Sopenharmony_ci rc = smc_wr_tx_get_free_slot_index(link, &idx); 21262306a36Sopenharmony_ci if (rc) 21362306a36Sopenharmony_ci return rc; 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci rc = wait_event_interruptible_timeout( 21662306a36Sopenharmony_ci link->wr_tx_wait, 21762306a36Sopenharmony_ci !smc_link_sendable(link) || 21862306a36Sopenharmony_ci lgr->terminating || 21962306a36Sopenharmony_ci (smc_wr_tx_get_free_slot_index(link, &idx) != -EBUSY), 22062306a36Sopenharmony_ci SMC_WR_TX_WAIT_FREE_SLOT_TIME); 22162306a36Sopenharmony_ci if (!rc) { 22262306a36Sopenharmony_ci /* timeout - terminate link */ 22362306a36Sopenharmony_ci smcr_link_down_cond_sched(link); 22462306a36Sopenharmony_ci return -EPIPE; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci if (idx == link->wr_tx_cnt) 22762306a36Sopenharmony_ci return -EPIPE; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci wr_id = smc_wr_tx_get_next_wr_id(link); 23062306a36Sopenharmony_ci wr_pend = &link->wr_tx_pends[idx]; 23162306a36Sopenharmony_ci wr_pend->wr_id = wr_id; 23262306a36Sopenharmony_ci wr_pend->handler = handler; 23362306a36Sopenharmony_ci wr_pend->link = link; 23462306a36Sopenharmony_ci wr_pend->idx = idx; 23562306a36Sopenharmony_ci wr_ib = &link->wr_tx_ibs[idx]; 23662306a36Sopenharmony_ci wr_ib->wr_id = wr_id; 23762306a36Sopenharmony_ci *wr_buf = &link->wr_tx_bufs[idx]; 23862306a36Sopenharmony_ci if (wr_rdma_buf) 23962306a36Sopenharmony_ci *wr_rdma_buf = &link->wr_tx_rdmas[idx]; 24062306a36Sopenharmony_ci *wr_pend_priv = &wr_pend->priv; 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ciint smc_wr_tx_get_v2_slot(struct smc_link *link, 24562306a36Sopenharmony_ci smc_wr_tx_handler handler, 24662306a36Sopenharmony_ci struct smc_wr_v2_buf **wr_buf, 24762306a36Sopenharmony_ci struct smc_wr_tx_pend_priv **wr_pend_priv) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct smc_wr_tx_pend *wr_pend; 25062306a36Sopenharmony_ci struct ib_send_wr *wr_ib; 25162306a36Sopenharmony_ci u64 wr_id; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (link->wr_tx_v2_pend->idx == link->wr_tx_cnt) 25462306a36Sopenharmony_ci return -EBUSY; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci *wr_buf = NULL; 25762306a36Sopenharmony_ci *wr_pend_priv = NULL; 25862306a36Sopenharmony_ci wr_id = smc_wr_tx_get_next_wr_id(link); 25962306a36Sopenharmony_ci wr_pend = link->wr_tx_v2_pend; 26062306a36Sopenharmony_ci wr_pend->wr_id = wr_id; 26162306a36Sopenharmony_ci wr_pend->handler = handler; 26262306a36Sopenharmony_ci wr_pend->link = link; 26362306a36Sopenharmony_ci wr_pend->idx = link->wr_tx_cnt; 26462306a36Sopenharmony_ci wr_ib = link->wr_tx_v2_ib; 26562306a36Sopenharmony_ci wr_ib->wr_id = wr_id; 26662306a36Sopenharmony_ci *wr_buf = link->lgr->wr_tx_buf_v2; 26762306a36Sopenharmony_ci *wr_pend_priv = &wr_pend->priv; 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciint smc_wr_tx_put_slot(struct smc_link *link, 27262306a36Sopenharmony_ci struct smc_wr_tx_pend_priv *wr_pend_priv) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct smc_wr_tx_pend *pend; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci pend = container_of(wr_pend_priv, struct smc_wr_tx_pend, priv); 27762306a36Sopenharmony_ci if (pend->idx < link->wr_tx_cnt) { 27862306a36Sopenharmony_ci u32 idx = pend->idx; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* clear the full struct smc_wr_tx_pend including .priv */ 28162306a36Sopenharmony_ci memset(&link->wr_tx_pends[idx], 0, 28262306a36Sopenharmony_ci sizeof(link->wr_tx_pends[idx])); 28362306a36Sopenharmony_ci memset(&link->wr_tx_bufs[idx], 0, 28462306a36Sopenharmony_ci sizeof(link->wr_tx_bufs[idx])); 28562306a36Sopenharmony_ci test_and_clear_bit(idx, link->wr_tx_mask); 28662306a36Sopenharmony_ci wake_up(&link->wr_tx_wait); 28762306a36Sopenharmony_ci return 1; 28862306a36Sopenharmony_ci } else if (link->lgr->smc_version == SMC_V2 && 28962306a36Sopenharmony_ci pend->idx == link->wr_tx_cnt) { 29062306a36Sopenharmony_ci /* Large v2 buffer */ 29162306a36Sopenharmony_ci memset(&link->wr_tx_v2_pend, 0, 29262306a36Sopenharmony_ci sizeof(link->wr_tx_v2_pend)); 29362306a36Sopenharmony_ci memset(&link->lgr->wr_tx_buf_v2, 0, 29462306a36Sopenharmony_ci sizeof(link->lgr->wr_tx_buf_v2)); 29562306a36Sopenharmony_ci return 1; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* Send prepared WR slot via ib_post_send. 30262306a36Sopenharmony_ci * @priv: pointer to smc_wr_tx_pend_priv identifying prepared message buffer 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ciint smc_wr_tx_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct smc_wr_tx_pend *pend; 30762306a36Sopenharmony_ci int rc; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ib_req_notify_cq(link->smcibdev->roce_cq_send, 31062306a36Sopenharmony_ci IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); 31162306a36Sopenharmony_ci pend = container_of(priv, struct smc_wr_tx_pend, priv); 31262306a36Sopenharmony_ci rc = ib_post_send(link->roce_qp, &link->wr_tx_ibs[pend->idx], NULL); 31362306a36Sopenharmony_ci if (rc) { 31462306a36Sopenharmony_ci smc_wr_tx_put_slot(link, priv); 31562306a36Sopenharmony_ci smcr_link_down_cond_sched(link); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci return rc; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciint smc_wr_tx_v2_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv, 32162306a36Sopenharmony_ci int len) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int rc; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci link->wr_tx_v2_ib->sg_list[0].length = len; 32662306a36Sopenharmony_ci ib_req_notify_cq(link->smcibdev->roce_cq_send, 32762306a36Sopenharmony_ci IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); 32862306a36Sopenharmony_ci rc = ib_post_send(link->roce_qp, link->wr_tx_v2_ib, NULL); 32962306a36Sopenharmony_ci if (rc) { 33062306a36Sopenharmony_ci smc_wr_tx_put_slot(link, priv); 33162306a36Sopenharmony_ci smcr_link_down_cond_sched(link); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci return rc; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* Send prepared WR slot via ib_post_send and wait for send completion 33762306a36Sopenharmony_ci * notification. 33862306a36Sopenharmony_ci * @priv: pointer to smc_wr_tx_pend_priv identifying prepared message buffer 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ciint smc_wr_tx_send_wait(struct smc_link *link, struct smc_wr_tx_pend_priv *priv, 34162306a36Sopenharmony_ci unsigned long timeout) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct smc_wr_tx_pend *pend; 34462306a36Sopenharmony_ci u32 pnd_idx; 34562306a36Sopenharmony_ci int rc; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci pend = container_of(priv, struct smc_wr_tx_pend, priv); 34862306a36Sopenharmony_ci pend->compl_requested = 1; 34962306a36Sopenharmony_ci pnd_idx = pend->idx; 35062306a36Sopenharmony_ci init_completion(&link->wr_tx_compl[pnd_idx]); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci rc = smc_wr_tx_send(link, priv); 35362306a36Sopenharmony_ci if (rc) 35462306a36Sopenharmony_ci return rc; 35562306a36Sopenharmony_ci /* wait for completion by smc_wr_tx_process_cqe() */ 35662306a36Sopenharmony_ci rc = wait_for_completion_interruptible_timeout( 35762306a36Sopenharmony_ci &link->wr_tx_compl[pnd_idx], timeout); 35862306a36Sopenharmony_ci if (rc <= 0) 35962306a36Sopenharmony_ci rc = -ENODATA; 36062306a36Sopenharmony_ci if (rc > 0) 36162306a36Sopenharmony_ci rc = 0; 36262306a36Sopenharmony_ci return rc; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* Register a memory region and wait for result. */ 36662306a36Sopenharmony_ciint smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int rc; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ib_req_notify_cq(link->smcibdev->roce_cq_send, 37162306a36Sopenharmony_ci IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); 37262306a36Sopenharmony_ci link->wr_reg_state = POSTED; 37362306a36Sopenharmony_ci link->wr_reg.wr.wr_id = (u64)(uintptr_t)mr; 37462306a36Sopenharmony_ci link->wr_reg.mr = mr; 37562306a36Sopenharmony_ci link->wr_reg.key = mr->rkey; 37662306a36Sopenharmony_ci rc = ib_post_send(link->roce_qp, &link->wr_reg.wr, NULL); 37762306a36Sopenharmony_ci if (rc) 37862306a36Sopenharmony_ci return rc; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci percpu_ref_get(&link->wr_reg_refs); 38162306a36Sopenharmony_ci rc = wait_event_interruptible_timeout(link->wr_reg_wait, 38262306a36Sopenharmony_ci (link->wr_reg_state != POSTED), 38362306a36Sopenharmony_ci SMC_WR_REG_MR_WAIT_TIME); 38462306a36Sopenharmony_ci percpu_ref_put(&link->wr_reg_refs); 38562306a36Sopenharmony_ci if (!rc) { 38662306a36Sopenharmony_ci /* timeout - terminate link */ 38762306a36Sopenharmony_ci smcr_link_down_cond_sched(link); 38862306a36Sopenharmony_ci return -EPIPE; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci if (rc == -ERESTARTSYS) 39162306a36Sopenharmony_ci return -EINTR; 39262306a36Sopenharmony_ci switch (link->wr_reg_state) { 39362306a36Sopenharmony_ci case CONFIRMED: 39462306a36Sopenharmony_ci rc = 0; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case FAILED: 39762306a36Sopenharmony_ci rc = -EIO; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci case POSTED: 40062306a36Sopenharmony_ci rc = -EPIPE; 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci return rc; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/****************************** receive queue ********************************/ 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciint smc_wr_rx_register_handler(struct smc_wr_rx_handler *handler) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct smc_wr_rx_handler *h_iter; 41162306a36Sopenharmony_ci int rc = 0; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci spin_lock(&smc_wr_rx_hash_lock); 41462306a36Sopenharmony_ci hash_for_each_possible(smc_wr_rx_hash, h_iter, list, handler->type) { 41562306a36Sopenharmony_ci if (h_iter->type == handler->type) { 41662306a36Sopenharmony_ci rc = -EEXIST; 41762306a36Sopenharmony_ci goto out_unlock; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci hash_add(smc_wr_rx_hash, &handler->list, handler->type); 42162306a36Sopenharmony_ciout_unlock: 42262306a36Sopenharmony_ci spin_unlock(&smc_wr_rx_hash_lock); 42362306a36Sopenharmony_ci return rc; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/* Demultiplex a received work request based on the message type to its handler. 42762306a36Sopenharmony_ci * Relies on smc_wr_rx_hash having been completely filled before any IB WRs, 42862306a36Sopenharmony_ci * and not being modified any more afterwards so we don't need to lock it. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic inline void smc_wr_rx_demultiplex(struct ib_wc *wc) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 43362306a36Sopenharmony_ci struct smc_wr_rx_handler *handler; 43462306a36Sopenharmony_ci struct smc_wr_rx_hdr *wr_rx; 43562306a36Sopenharmony_ci u64 temp_wr_id; 43662306a36Sopenharmony_ci u32 index; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (wc->byte_len < sizeof(*wr_rx)) 43962306a36Sopenharmony_ci return; /* short message */ 44062306a36Sopenharmony_ci temp_wr_id = wc->wr_id; 44162306a36Sopenharmony_ci index = do_div(temp_wr_id, link->wr_rx_cnt); 44262306a36Sopenharmony_ci wr_rx = (struct smc_wr_rx_hdr *)&link->wr_rx_bufs[index]; 44362306a36Sopenharmony_ci hash_for_each_possible(smc_wr_rx_hash, handler, list, wr_rx->type) { 44462306a36Sopenharmony_ci if (handler->type == wr_rx->type) 44562306a36Sopenharmony_ci handler->handler(wc, wr_rx); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic inline void smc_wr_rx_process_cqes(struct ib_wc wc[], int num) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct smc_link *link; 45262306a36Sopenharmony_ci int i; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci for (i = 0; i < num; i++) { 45562306a36Sopenharmony_ci link = wc[i].qp->qp_context; 45662306a36Sopenharmony_ci link->wr_rx_id_compl = wc[i].wr_id; 45762306a36Sopenharmony_ci if (wc[i].status == IB_WC_SUCCESS) { 45862306a36Sopenharmony_ci link->wr_rx_tstamp = jiffies; 45962306a36Sopenharmony_ci smc_wr_rx_demultiplex(&wc[i]); 46062306a36Sopenharmony_ci smc_wr_rx_post(link); /* refill WR RX */ 46162306a36Sopenharmony_ci } else { 46262306a36Sopenharmony_ci /* handle status errors */ 46362306a36Sopenharmony_ci switch (wc[i].status) { 46462306a36Sopenharmony_ci case IB_WC_RETRY_EXC_ERR: 46562306a36Sopenharmony_ci case IB_WC_RNR_RETRY_EXC_ERR: 46662306a36Sopenharmony_ci case IB_WC_WR_FLUSH_ERR: 46762306a36Sopenharmony_ci smcr_link_down_cond_sched(link); 46862306a36Sopenharmony_ci if (link->wr_rx_id_compl == link->wr_rx_id) 46962306a36Sopenharmony_ci wake_up(&link->wr_rx_empty_wait); 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci default: 47262306a36Sopenharmony_ci smc_wr_rx_post(link); /* refill WR RX */ 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void smc_wr_rx_tasklet_fn(struct tasklet_struct *t) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct smc_ib_device *dev = from_tasklet(dev, t, recv_tasklet); 48262306a36Sopenharmony_ci struct ib_wc wc[SMC_WR_MAX_POLL_CQE]; 48362306a36Sopenharmony_ci int polled = 0; 48462306a36Sopenharmony_ci int rc; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ciagain: 48762306a36Sopenharmony_ci polled++; 48862306a36Sopenharmony_ci do { 48962306a36Sopenharmony_ci memset(&wc, 0, sizeof(wc)); 49062306a36Sopenharmony_ci rc = ib_poll_cq(dev->roce_cq_recv, SMC_WR_MAX_POLL_CQE, wc); 49162306a36Sopenharmony_ci if (polled == 1) { 49262306a36Sopenharmony_ci ib_req_notify_cq(dev->roce_cq_recv, 49362306a36Sopenharmony_ci IB_CQ_SOLICITED_MASK 49462306a36Sopenharmony_ci | IB_CQ_REPORT_MISSED_EVENTS); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci if (!rc) 49762306a36Sopenharmony_ci break; 49862306a36Sopenharmony_ci smc_wr_rx_process_cqes(&wc[0], rc); 49962306a36Sopenharmony_ci } while (rc > 0); 50062306a36Sopenharmony_ci if (polled == 1) 50162306a36Sopenharmony_ci goto again; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_civoid smc_wr_rx_cq_handler(struct ib_cq *ib_cq, void *cq_context) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct smc_ib_device *dev = (struct smc_ib_device *)cq_context; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci tasklet_schedule(&dev->recv_tasklet); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciint smc_wr_rx_post_init(struct smc_link *link) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci u32 i; 51462306a36Sopenharmony_ci int rc = 0; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci for (i = 0; i < link->wr_rx_cnt; i++) 51762306a36Sopenharmony_ci rc = smc_wr_rx_post(link); 51862306a36Sopenharmony_ci return rc; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/***************************** init, exit, misc ******************************/ 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_civoid smc_wr_remember_qp_attr(struct smc_link *lnk) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct ib_qp_attr *attr = &lnk->qp_attr; 52662306a36Sopenharmony_ci struct ib_qp_init_attr init_attr; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci memset(attr, 0, sizeof(*attr)); 52962306a36Sopenharmony_ci memset(&init_attr, 0, sizeof(init_attr)); 53062306a36Sopenharmony_ci ib_query_qp(lnk->roce_qp, attr, 53162306a36Sopenharmony_ci IB_QP_STATE | 53262306a36Sopenharmony_ci IB_QP_CUR_STATE | 53362306a36Sopenharmony_ci IB_QP_PKEY_INDEX | 53462306a36Sopenharmony_ci IB_QP_PORT | 53562306a36Sopenharmony_ci IB_QP_QKEY | 53662306a36Sopenharmony_ci IB_QP_AV | 53762306a36Sopenharmony_ci IB_QP_PATH_MTU | 53862306a36Sopenharmony_ci IB_QP_TIMEOUT | 53962306a36Sopenharmony_ci IB_QP_RETRY_CNT | 54062306a36Sopenharmony_ci IB_QP_RNR_RETRY | 54162306a36Sopenharmony_ci IB_QP_RQ_PSN | 54262306a36Sopenharmony_ci IB_QP_ALT_PATH | 54362306a36Sopenharmony_ci IB_QP_MIN_RNR_TIMER | 54462306a36Sopenharmony_ci IB_QP_SQ_PSN | 54562306a36Sopenharmony_ci IB_QP_PATH_MIG_STATE | 54662306a36Sopenharmony_ci IB_QP_CAP | 54762306a36Sopenharmony_ci IB_QP_DEST_QPN, 54862306a36Sopenharmony_ci &init_attr); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci lnk->wr_tx_cnt = min_t(size_t, SMC_WR_BUF_CNT, 55162306a36Sopenharmony_ci lnk->qp_attr.cap.max_send_wr); 55262306a36Sopenharmony_ci lnk->wr_rx_cnt = min_t(size_t, SMC_WR_BUF_CNT * 3, 55362306a36Sopenharmony_ci lnk->qp_attr.cap.max_recv_wr); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void smc_wr_init_sge(struct smc_link *lnk) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci int sges_per_buf = (lnk->lgr->smc_version == SMC_V2) ? 2 : 1; 55962306a36Sopenharmony_ci bool send_inline = (lnk->qp_attr.cap.max_inline_data > SMC_WR_TX_SIZE); 56062306a36Sopenharmony_ci u32 i; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci for (i = 0; i < lnk->wr_tx_cnt; i++) { 56362306a36Sopenharmony_ci lnk->wr_tx_sges[i].addr = send_inline ? (uintptr_t)(&lnk->wr_tx_bufs[i]) : 56462306a36Sopenharmony_ci lnk->wr_tx_dma_addr + i * SMC_WR_BUF_SIZE; 56562306a36Sopenharmony_ci lnk->wr_tx_sges[i].length = SMC_WR_TX_SIZE; 56662306a36Sopenharmony_ci lnk->wr_tx_sges[i].lkey = lnk->roce_pd->local_dma_lkey; 56762306a36Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[0].lkey = 56862306a36Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 56962306a36Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[1].lkey = 57062306a36Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 57162306a36Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[0].lkey = 57262306a36Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 57362306a36Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[1].lkey = 57462306a36Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 57562306a36Sopenharmony_ci lnk->wr_tx_ibs[i].next = NULL; 57662306a36Sopenharmony_ci lnk->wr_tx_ibs[i].sg_list = &lnk->wr_tx_sges[i]; 57762306a36Sopenharmony_ci lnk->wr_tx_ibs[i].num_sge = 1; 57862306a36Sopenharmony_ci lnk->wr_tx_ibs[i].opcode = IB_WR_SEND; 57962306a36Sopenharmony_ci lnk->wr_tx_ibs[i].send_flags = 58062306a36Sopenharmony_ci IB_SEND_SIGNALED | IB_SEND_SOLICITED; 58162306a36Sopenharmony_ci if (send_inline) 58262306a36Sopenharmony_ci lnk->wr_tx_ibs[i].send_flags |= IB_SEND_INLINE; 58362306a36Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.opcode = IB_WR_RDMA_WRITE; 58462306a36Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.opcode = IB_WR_RDMA_WRITE; 58562306a36Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.sg_list = 58662306a36Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge; 58762306a36Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.sg_list = 58862306a36Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (lnk->lgr->smc_version == SMC_V2) { 59262306a36Sopenharmony_ci lnk->wr_tx_v2_sge->addr = lnk->wr_tx_v2_dma_addr; 59362306a36Sopenharmony_ci lnk->wr_tx_v2_sge->length = SMC_WR_BUF_V2_SIZE; 59462306a36Sopenharmony_ci lnk->wr_tx_v2_sge->lkey = lnk->roce_pd->local_dma_lkey; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci lnk->wr_tx_v2_ib->next = NULL; 59762306a36Sopenharmony_ci lnk->wr_tx_v2_ib->sg_list = lnk->wr_tx_v2_sge; 59862306a36Sopenharmony_ci lnk->wr_tx_v2_ib->num_sge = 1; 59962306a36Sopenharmony_ci lnk->wr_tx_v2_ib->opcode = IB_WR_SEND; 60062306a36Sopenharmony_ci lnk->wr_tx_v2_ib->send_flags = 60162306a36Sopenharmony_ci IB_SEND_SIGNALED | IB_SEND_SOLICITED; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* With SMC-Rv2 there can be messages larger than SMC_WR_TX_SIZE. 60562306a36Sopenharmony_ci * Each ib_recv_wr gets 2 sges, the second one is a spillover buffer 60662306a36Sopenharmony_ci * and the same buffer for all sges. When a larger message arrived then 60762306a36Sopenharmony_ci * the content of the first small sge is copied to the beginning of 60862306a36Sopenharmony_ci * the larger spillover buffer, allowing easy data mapping. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci for (i = 0; i < lnk->wr_rx_cnt; i++) { 61162306a36Sopenharmony_ci int x = i * sges_per_buf; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci lnk->wr_rx_sges[x].addr = 61462306a36Sopenharmony_ci lnk->wr_rx_dma_addr + i * SMC_WR_BUF_SIZE; 61562306a36Sopenharmony_ci lnk->wr_rx_sges[x].length = SMC_WR_TX_SIZE; 61662306a36Sopenharmony_ci lnk->wr_rx_sges[x].lkey = lnk->roce_pd->local_dma_lkey; 61762306a36Sopenharmony_ci if (lnk->lgr->smc_version == SMC_V2) { 61862306a36Sopenharmony_ci lnk->wr_rx_sges[x + 1].addr = 61962306a36Sopenharmony_ci lnk->wr_rx_v2_dma_addr + SMC_WR_TX_SIZE; 62062306a36Sopenharmony_ci lnk->wr_rx_sges[x + 1].length = 62162306a36Sopenharmony_ci SMC_WR_BUF_V2_SIZE - SMC_WR_TX_SIZE; 62262306a36Sopenharmony_ci lnk->wr_rx_sges[x + 1].lkey = 62362306a36Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci lnk->wr_rx_ibs[i].next = NULL; 62662306a36Sopenharmony_ci lnk->wr_rx_ibs[i].sg_list = &lnk->wr_rx_sges[x]; 62762306a36Sopenharmony_ci lnk->wr_rx_ibs[i].num_sge = sges_per_buf; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci lnk->wr_reg.wr.next = NULL; 63062306a36Sopenharmony_ci lnk->wr_reg.wr.num_sge = 0; 63162306a36Sopenharmony_ci lnk->wr_reg.wr.send_flags = IB_SEND_SIGNALED; 63262306a36Sopenharmony_ci lnk->wr_reg.wr.opcode = IB_WR_REG_MR; 63362306a36Sopenharmony_ci lnk->wr_reg.access = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_civoid smc_wr_free_link(struct smc_link *lnk) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct ib_device *ibdev; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!lnk->smcibdev) 64162306a36Sopenharmony_ci return; 64262306a36Sopenharmony_ci ibdev = lnk->smcibdev->ibdev; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci smc_wr_drain_cq(lnk); 64562306a36Sopenharmony_ci smc_wr_wakeup_reg_wait(lnk); 64662306a36Sopenharmony_ci smc_wr_wakeup_tx_wait(lnk); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci smc_wr_tx_wait_no_pending_sends(lnk); 64962306a36Sopenharmony_ci percpu_ref_kill(&lnk->wr_reg_refs); 65062306a36Sopenharmony_ci wait_for_completion(&lnk->reg_ref_comp); 65162306a36Sopenharmony_ci percpu_ref_kill(&lnk->wr_tx_refs); 65262306a36Sopenharmony_ci wait_for_completion(&lnk->tx_ref_comp); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (lnk->wr_rx_dma_addr) { 65562306a36Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr, 65662306a36Sopenharmony_ci SMC_WR_BUF_SIZE * lnk->wr_rx_cnt, 65762306a36Sopenharmony_ci DMA_FROM_DEVICE); 65862306a36Sopenharmony_ci lnk->wr_rx_dma_addr = 0; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci if (lnk->wr_rx_v2_dma_addr) { 66162306a36Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_rx_v2_dma_addr, 66262306a36Sopenharmony_ci SMC_WR_BUF_V2_SIZE, 66362306a36Sopenharmony_ci DMA_FROM_DEVICE); 66462306a36Sopenharmony_ci lnk->wr_rx_v2_dma_addr = 0; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci if (lnk->wr_tx_dma_addr) { 66762306a36Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_tx_dma_addr, 66862306a36Sopenharmony_ci SMC_WR_BUF_SIZE * lnk->wr_tx_cnt, 66962306a36Sopenharmony_ci DMA_TO_DEVICE); 67062306a36Sopenharmony_ci lnk->wr_tx_dma_addr = 0; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci if (lnk->wr_tx_v2_dma_addr) { 67362306a36Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_tx_v2_dma_addr, 67462306a36Sopenharmony_ci SMC_WR_BUF_V2_SIZE, 67562306a36Sopenharmony_ci DMA_TO_DEVICE); 67662306a36Sopenharmony_ci lnk->wr_tx_v2_dma_addr = 0; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_civoid smc_wr_free_lgr_mem(struct smc_link_group *lgr) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci if (lgr->smc_version < SMC_V2) 68362306a36Sopenharmony_ci return; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci kfree(lgr->wr_rx_buf_v2); 68662306a36Sopenharmony_ci lgr->wr_rx_buf_v2 = NULL; 68762306a36Sopenharmony_ci kfree(lgr->wr_tx_buf_v2); 68862306a36Sopenharmony_ci lgr->wr_tx_buf_v2 = NULL; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_civoid smc_wr_free_link_mem(struct smc_link *lnk) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci kfree(lnk->wr_tx_v2_ib); 69462306a36Sopenharmony_ci lnk->wr_tx_v2_ib = NULL; 69562306a36Sopenharmony_ci kfree(lnk->wr_tx_v2_sge); 69662306a36Sopenharmony_ci lnk->wr_tx_v2_sge = NULL; 69762306a36Sopenharmony_ci kfree(lnk->wr_tx_v2_pend); 69862306a36Sopenharmony_ci lnk->wr_tx_v2_pend = NULL; 69962306a36Sopenharmony_ci kfree(lnk->wr_tx_compl); 70062306a36Sopenharmony_ci lnk->wr_tx_compl = NULL; 70162306a36Sopenharmony_ci kfree(lnk->wr_tx_pends); 70262306a36Sopenharmony_ci lnk->wr_tx_pends = NULL; 70362306a36Sopenharmony_ci bitmap_free(lnk->wr_tx_mask); 70462306a36Sopenharmony_ci lnk->wr_tx_mask = NULL; 70562306a36Sopenharmony_ci kfree(lnk->wr_tx_sges); 70662306a36Sopenharmony_ci lnk->wr_tx_sges = NULL; 70762306a36Sopenharmony_ci kfree(lnk->wr_tx_rdma_sges); 70862306a36Sopenharmony_ci lnk->wr_tx_rdma_sges = NULL; 70962306a36Sopenharmony_ci kfree(lnk->wr_rx_sges); 71062306a36Sopenharmony_ci lnk->wr_rx_sges = NULL; 71162306a36Sopenharmony_ci kfree(lnk->wr_tx_rdmas); 71262306a36Sopenharmony_ci lnk->wr_tx_rdmas = NULL; 71362306a36Sopenharmony_ci kfree(lnk->wr_rx_ibs); 71462306a36Sopenharmony_ci lnk->wr_rx_ibs = NULL; 71562306a36Sopenharmony_ci kfree(lnk->wr_tx_ibs); 71662306a36Sopenharmony_ci lnk->wr_tx_ibs = NULL; 71762306a36Sopenharmony_ci kfree(lnk->wr_tx_bufs); 71862306a36Sopenharmony_ci lnk->wr_tx_bufs = NULL; 71962306a36Sopenharmony_ci kfree(lnk->wr_rx_bufs); 72062306a36Sopenharmony_ci lnk->wr_rx_bufs = NULL; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ciint smc_wr_alloc_lgr_mem(struct smc_link_group *lgr) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci if (lgr->smc_version < SMC_V2) 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci lgr->wr_rx_buf_v2 = kzalloc(SMC_WR_BUF_V2_SIZE, GFP_KERNEL); 72962306a36Sopenharmony_ci if (!lgr->wr_rx_buf_v2) 73062306a36Sopenharmony_ci return -ENOMEM; 73162306a36Sopenharmony_ci lgr->wr_tx_buf_v2 = kzalloc(SMC_WR_BUF_V2_SIZE, GFP_KERNEL); 73262306a36Sopenharmony_ci if (!lgr->wr_tx_buf_v2) { 73362306a36Sopenharmony_ci kfree(lgr->wr_rx_buf_v2); 73462306a36Sopenharmony_ci return -ENOMEM; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ciint smc_wr_alloc_link_mem(struct smc_link *link) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int sges_per_buf = link->lgr->smc_version == SMC_V2 ? 2 : 1; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* allocate link related memory */ 74462306a36Sopenharmony_ci link->wr_tx_bufs = kcalloc(SMC_WR_BUF_CNT, SMC_WR_BUF_SIZE, GFP_KERNEL); 74562306a36Sopenharmony_ci if (!link->wr_tx_bufs) 74662306a36Sopenharmony_ci goto no_mem; 74762306a36Sopenharmony_ci link->wr_rx_bufs = kcalloc(SMC_WR_BUF_CNT * 3, SMC_WR_BUF_SIZE, 74862306a36Sopenharmony_ci GFP_KERNEL); 74962306a36Sopenharmony_ci if (!link->wr_rx_bufs) 75062306a36Sopenharmony_ci goto no_mem_wr_tx_bufs; 75162306a36Sopenharmony_ci link->wr_tx_ibs = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_ibs[0]), 75262306a36Sopenharmony_ci GFP_KERNEL); 75362306a36Sopenharmony_ci if (!link->wr_tx_ibs) 75462306a36Sopenharmony_ci goto no_mem_wr_rx_bufs; 75562306a36Sopenharmony_ci link->wr_rx_ibs = kcalloc(SMC_WR_BUF_CNT * 3, 75662306a36Sopenharmony_ci sizeof(link->wr_rx_ibs[0]), 75762306a36Sopenharmony_ci GFP_KERNEL); 75862306a36Sopenharmony_ci if (!link->wr_rx_ibs) 75962306a36Sopenharmony_ci goto no_mem_wr_tx_ibs; 76062306a36Sopenharmony_ci link->wr_tx_rdmas = kcalloc(SMC_WR_BUF_CNT, 76162306a36Sopenharmony_ci sizeof(link->wr_tx_rdmas[0]), 76262306a36Sopenharmony_ci GFP_KERNEL); 76362306a36Sopenharmony_ci if (!link->wr_tx_rdmas) 76462306a36Sopenharmony_ci goto no_mem_wr_rx_ibs; 76562306a36Sopenharmony_ci link->wr_tx_rdma_sges = kcalloc(SMC_WR_BUF_CNT, 76662306a36Sopenharmony_ci sizeof(link->wr_tx_rdma_sges[0]), 76762306a36Sopenharmony_ci GFP_KERNEL); 76862306a36Sopenharmony_ci if (!link->wr_tx_rdma_sges) 76962306a36Sopenharmony_ci goto no_mem_wr_tx_rdmas; 77062306a36Sopenharmony_ci link->wr_tx_sges = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_sges[0]), 77162306a36Sopenharmony_ci GFP_KERNEL); 77262306a36Sopenharmony_ci if (!link->wr_tx_sges) 77362306a36Sopenharmony_ci goto no_mem_wr_tx_rdma_sges; 77462306a36Sopenharmony_ci link->wr_rx_sges = kcalloc(SMC_WR_BUF_CNT * 3, 77562306a36Sopenharmony_ci sizeof(link->wr_rx_sges[0]) * sges_per_buf, 77662306a36Sopenharmony_ci GFP_KERNEL); 77762306a36Sopenharmony_ci if (!link->wr_rx_sges) 77862306a36Sopenharmony_ci goto no_mem_wr_tx_sges; 77962306a36Sopenharmony_ci link->wr_tx_mask = bitmap_zalloc(SMC_WR_BUF_CNT, GFP_KERNEL); 78062306a36Sopenharmony_ci if (!link->wr_tx_mask) 78162306a36Sopenharmony_ci goto no_mem_wr_rx_sges; 78262306a36Sopenharmony_ci link->wr_tx_pends = kcalloc(SMC_WR_BUF_CNT, 78362306a36Sopenharmony_ci sizeof(link->wr_tx_pends[0]), 78462306a36Sopenharmony_ci GFP_KERNEL); 78562306a36Sopenharmony_ci if (!link->wr_tx_pends) 78662306a36Sopenharmony_ci goto no_mem_wr_tx_mask; 78762306a36Sopenharmony_ci link->wr_tx_compl = kcalloc(SMC_WR_BUF_CNT, 78862306a36Sopenharmony_ci sizeof(link->wr_tx_compl[0]), 78962306a36Sopenharmony_ci GFP_KERNEL); 79062306a36Sopenharmony_ci if (!link->wr_tx_compl) 79162306a36Sopenharmony_ci goto no_mem_wr_tx_pends; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (link->lgr->smc_version == SMC_V2) { 79462306a36Sopenharmony_ci link->wr_tx_v2_ib = kzalloc(sizeof(*link->wr_tx_v2_ib), 79562306a36Sopenharmony_ci GFP_KERNEL); 79662306a36Sopenharmony_ci if (!link->wr_tx_v2_ib) 79762306a36Sopenharmony_ci goto no_mem_tx_compl; 79862306a36Sopenharmony_ci link->wr_tx_v2_sge = kzalloc(sizeof(*link->wr_tx_v2_sge), 79962306a36Sopenharmony_ci GFP_KERNEL); 80062306a36Sopenharmony_ci if (!link->wr_tx_v2_sge) 80162306a36Sopenharmony_ci goto no_mem_v2_ib; 80262306a36Sopenharmony_ci link->wr_tx_v2_pend = kzalloc(sizeof(*link->wr_tx_v2_pend), 80362306a36Sopenharmony_ci GFP_KERNEL); 80462306a36Sopenharmony_ci if (!link->wr_tx_v2_pend) 80562306a36Sopenharmony_ci goto no_mem_v2_sge; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci return 0; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cino_mem_v2_sge: 81062306a36Sopenharmony_ci kfree(link->wr_tx_v2_sge); 81162306a36Sopenharmony_cino_mem_v2_ib: 81262306a36Sopenharmony_ci kfree(link->wr_tx_v2_ib); 81362306a36Sopenharmony_cino_mem_tx_compl: 81462306a36Sopenharmony_ci kfree(link->wr_tx_compl); 81562306a36Sopenharmony_cino_mem_wr_tx_pends: 81662306a36Sopenharmony_ci kfree(link->wr_tx_pends); 81762306a36Sopenharmony_cino_mem_wr_tx_mask: 81862306a36Sopenharmony_ci kfree(link->wr_tx_mask); 81962306a36Sopenharmony_cino_mem_wr_rx_sges: 82062306a36Sopenharmony_ci kfree(link->wr_rx_sges); 82162306a36Sopenharmony_cino_mem_wr_tx_sges: 82262306a36Sopenharmony_ci kfree(link->wr_tx_sges); 82362306a36Sopenharmony_cino_mem_wr_tx_rdma_sges: 82462306a36Sopenharmony_ci kfree(link->wr_tx_rdma_sges); 82562306a36Sopenharmony_cino_mem_wr_tx_rdmas: 82662306a36Sopenharmony_ci kfree(link->wr_tx_rdmas); 82762306a36Sopenharmony_cino_mem_wr_rx_ibs: 82862306a36Sopenharmony_ci kfree(link->wr_rx_ibs); 82962306a36Sopenharmony_cino_mem_wr_tx_ibs: 83062306a36Sopenharmony_ci kfree(link->wr_tx_ibs); 83162306a36Sopenharmony_cino_mem_wr_rx_bufs: 83262306a36Sopenharmony_ci kfree(link->wr_rx_bufs); 83362306a36Sopenharmony_cino_mem_wr_tx_bufs: 83462306a36Sopenharmony_ci kfree(link->wr_tx_bufs); 83562306a36Sopenharmony_cino_mem: 83662306a36Sopenharmony_ci return -ENOMEM; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_civoid smc_wr_remove_dev(struct smc_ib_device *smcibdev) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci tasklet_kill(&smcibdev->recv_tasklet); 84262306a36Sopenharmony_ci tasklet_kill(&smcibdev->send_tasklet); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_civoid smc_wr_add_dev(struct smc_ib_device *smcibdev) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci tasklet_setup(&smcibdev->recv_tasklet, smc_wr_rx_tasklet_fn); 84862306a36Sopenharmony_ci tasklet_setup(&smcibdev->send_tasklet, smc_wr_tx_tasklet_fn); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic void smcr_wr_tx_refs_free(struct percpu_ref *ref) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct smc_link *lnk = container_of(ref, struct smc_link, wr_tx_refs); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci complete(&lnk->tx_ref_comp); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic void smcr_wr_reg_refs_free(struct percpu_ref *ref) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct smc_link *lnk = container_of(ref, struct smc_link, wr_reg_refs); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci complete(&lnk->reg_ref_comp); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ciint smc_wr_create_link(struct smc_link *lnk) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct ib_device *ibdev = lnk->smcibdev->ibdev; 86862306a36Sopenharmony_ci int rc = 0; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci smc_wr_tx_set_wr_id(&lnk->wr_tx_id, 0); 87162306a36Sopenharmony_ci lnk->wr_rx_id = 0; 87262306a36Sopenharmony_ci lnk->wr_rx_dma_addr = ib_dma_map_single( 87362306a36Sopenharmony_ci ibdev, lnk->wr_rx_bufs, SMC_WR_BUF_SIZE * lnk->wr_rx_cnt, 87462306a36Sopenharmony_ci DMA_FROM_DEVICE); 87562306a36Sopenharmony_ci if (ib_dma_mapping_error(ibdev, lnk->wr_rx_dma_addr)) { 87662306a36Sopenharmony_ci lnk->wr_rx_dma_addr = 0; 87762306a36Sopenharmony_ci rc = -EIO; 87862306a36Sopenharmony_ci goto out; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci if (lnk->lgr->smc_version == SMC_V2) { 88162306a36Sopenharmony_ci lnk->wr_rx_v2_dma_addr = ib_dma_map_single(ibdev, 88262306a36Sopenharmony_ci lnk->lgr->wr_rx_buf_v2, SMC_WR_BUF_V2_SIZE, 88362306a36Sopenharmony_ci DMA_FROM_DEVICE); 88462306a36Sopenharmony_ci if (ib_dma_mapping_error(ibdev, lnk->wr_rx_v2_dma_addr)) { 88562306a36Sopenharmony_ci lnk->wr_rx_v2_dma_addr = 0; 88662306a36Sopenharmony_ci rc = -EIO; 88762306a36Sopenharmony_ci goto dma_unmap; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci lnk->wr_tx_v2_dma_addr = ib_dma_map_single(ibdev, 89062306a36Sopenharmony_ci lnk->lgr->wr_tx_buf_v2, SMC_WR_BUF_V2_SIZE, 89162306a36Sopenharmony_ci DMA_TO_DEVICE); 89262306a36Sopenharmony_ci if (ib_dma_mapping_error(ibdev, lnk->wr_tx_v2_dma_addr)) { 89362306a36Sopenharmony_ci lnk->wr_tx_v2_dma_addr = 0; 89462306a36Sopenharmony_ci rc = -EIO; 89562306a36Sopenharmony_ci goto dma_unmap; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci lnk->wr_tx_dma_addr = ib_dma_map_single( 89962306a36Sopenharmony_ci ibdev, lnk->wr_tx_bufs, SMC_WR_BUF_SIZE * lnk->wr_tx_cnt, 90062306a36Sopenharmony_ci DMA_TO_DEVICE); 90162306a36Sopenharmony_ci if (ib_dma_mapping_error(ibdev, lnk->wr_tx_dma_addr)) { 90262306a36Sopenharmony_ci rc = -EIO; 90362306a36Sopenharmony_ci goto dma_unmap; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci smc_wr_init_sge(lnk); 90662306a36Sopenharmony_ci bitmap_zero(lnk->wr_tx_mask, SMC_WR_BUF_CNT); 90762306a36Sopenharmony_ci init_waitqueue_head(&lnk->wr_tx_wait); 90862306a36Sopenharmony_ci rc = percpu_ref_init(&lnk->wr_tx_refs, smcr_wr_tx_refs_free, 0, GFP_KERNEL); 90962306a36Sopenharmony_ci if (rc) 91062306a36Sopenharmony_ci goto dma_unmap; 91162306a36Sopenharmony_ci init_completion(&lnk->tx_ref_comp); 91262306a36Sopenharmony_ci init_waitqueue_head(&lnk->wr_reg_wait); 91362306a36Sopenharmony_ci rc = percpu_ref_init(&lnk->wr_reg_refs, smcr_wr_reg_refs_free, 0, GFP_KERNEL); 91462306a36Sopenharmony_ci if (rc) 91562306a36Sopenharmony_ci goto dma_unmap; 91662306a36Sopenharmony_ci init_completion(&lnk->reg_ref_comp); 91762306a36Sopenharmony_ci init_waitqueue_head(&lnk->wr_rx_empty_wait); 91862306a36Sopenharmony_ci return rc; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cidma_unmap: 92162306a36Sopenharmony_ci if (lnk->wr_rx_v2_dma_addr) { 92262306a36Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_rx_v2_dma_addr, 92362306a36Sopenharmony_ci SMC_WR_BUF_V2_SIZE, 92462306a36Sopenharmony_ci DMA_FROM_DEVICE); 92562306a36Sopenharmony_ci lnk->wr_rx_v2_dma_addr = 0; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci if (lnk->wr_tx_v2_dma_addr) { 92862306a36Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_tx_v2_dma_addr, 92962306a36Sopenharmony_ci SMC_WR_BUF_V2_SIZE, 93062306a36Sopenharmony_ci DMA_TO_DEVICE); 93162306a36Sopenharmony_ci lnk->wr_tx_v2_dma_addr = 0; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr, 93462306a36Sopenharmony_ci SMC_WR_BUF_SIZE * lnk->wr_rx_cnt, 93562306a36Sopenharmony_ci DMA_FROM_DEVICE); 93662306a36Sopenharmony_ci lnk->wr_rx_dma_addr = 0; 93762306a36Sopenharmony_ciout: 93862306a36Sopenharmony_ci return rc; 93962306a36Sopenharmony_ci} 940