18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Shared Memory Communications over RDMA (SMC-R) and RoCE 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Work Requests exploiting Infiniband API 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Work requests (WR) of type ib_post_send or ib_post_recv respectively 88c2ecf20Sopenharmony_ci * are submitted to either RC SQ or RC RQ respectively 98c2ecf20Sopenharmony_ci * (reliably connected send/receive queue) 108c2ecf20Sopenharmony_ci * and become work queue entries (WQEs). 118c2ecf20Sopenharmony_ci * While an SQ WR/WQE is pending, we track it until transmission completion. 128c2ecf20Sopenharmony_ci * Through a send or receive completion queue (CQ) respectively, 138c2ecf20Sopenharmony_ci * we get completion queue entries (CQEs) [aka work completions (WCs)]. 148c2ecf20Sopenharmony_ci * Since the CQ callback is called from IRQ context, we split work by using 158c2ecf20Sopenharmony_ci * bottom halves implemented by tasklets. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * SMC uses this to exchange LLC (link layer control) 188c2ecf20Sopenharmony_ci * and CDC (connection data control) messages. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2016 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Author(s): Steffen Maier <maier@linux.vnet.ibm.com> 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/atomic.h> 268c2ecf20Sopenharmony_ci#include <linux/hashtable.h> 278c2ecf20Sopenharmony_ci#include <linux/wait.h> 288c2ecf20Sopenharmony_ci#include <rdma/ib_verbs.h> 298c2ecf20Sopenharmony_ci#include <asm/div64.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "smc.h" 328c2ecf20Sopenharmony_ci#include "smc_wr.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define SMC_WR_MAX_POLL_CQE 10 /* max. # of compl. queue elements in 1 poll */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define SMC_WR_RX_HASH_BITS 4 378c2ecf20Sopenharmony_cistatic DEFINE_HASHTABLE(smc_wr_rx_hash, SMC_WR_RX_HASH_BITS); 388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(smc_wr_rx_hash_lock); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct smc_wr_tx_pend { /* control data for a pending send request */ 418c2ecf20Sopenharmony_ci u64 wr_id; /* work request id sent */ 428c2ecf20Sopenharmony_ci smc_wr_tx_handler handler; 438c2ecf20Sopenharmony_ci enum ib_wc_status wc_status; /* CQE status */ 448c2ecf20Sopenharmony_ci struct smc_link *link; 458c2ecf20Sopenharmony_ci u32 idx; 468c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv priv; 478c2ecf20Sopenharmony_ci u8 compl_requested; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/******************************** send queue *********************************/ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/*------------------------------- completion --------------------------------*/ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* returns true if at least one tx work request is pending on the given link */ 558c2ecf20Sopenharmony_cistatic inline bool smc_wr_is_tx_pend(struct smc_link *link) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci if (find_first_bit(link->wr_tx_mask, link->wr_tx_cnt) != 588c2ecf20Sopenharmony_ci link->wr_tx_cnt) { 598c2ecf20Sopenharmony_ci return true; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci return false; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* wait till all pending tx work requests on the given link are completed */ 658c2ecf20Sopenharmony_civoid smc_wr_tx_wait_no_pending_sends(struct smc_link *link) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci wait_event(link->wr_tx_wait, !smc_wr_is_tx_pend(link)); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic inline int smc_wr_tx_find_pending_index(struct smc_link *link, u64 wr_id) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u32 i; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci for (i = 0; i < link->wr_tx_cnt; i++) { 758c2ecf20Sopenharmony_ci if (link->wr_tx_pends[i].wr_id == wr_id) 768c2ecf20Sopenharmony_ci return i; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci return link->wr_tx_cnt; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic inline void smc_wr_tx_process_cqe(struct ib_wc *wc) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct smc_wr_tx_pend pnd_snd; 848c2ecf20Sopenharmony_ci struct smc_link *link; 858c2ecf20Sopenharmony_ci u32 pnd_snd_idx; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci link = wc->qp->qp_context; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (wc->opcode == IB_WC_REG_MR) { 908c2ecf20Sopenharmony_ci if (wc->status) 918c2ecf20Sopenharmony_ci link->wr_reg_state = FAILED; 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci link->wr_reg_state = CONFIRMED; 948c2ecf20Sopenharmony_ci smc_wr_wakeup_reg_wait(link); 958c2ecf20Sopenharmony_ci return; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci pnd_snd_idx = smc_wr_tx_find_pending_index(link, wc->wr_id); 998c2ecf20Sopenharmony_ci if (pnd_snd_idx == link->wr_tx_cnt) 1008c2ecf20Sopenharmony_ci return; 1018c2ecf20Sopenharmony_ci link->wr_tx_pends[pnd_snd_idx].wc_status = wc->status; 1028c2ecf20Sopenharmony_ci if (link->wr_tx_pends[pnd_snd_idx].compl_requested) 1038c2ecf20Sopenharmony_ci complete(&link->wr_tx_compl[pnd_snd_idx]); 1048c2ecf20Sopenharmony_ci memcpy(&pnd_snd, &link->wr_tx_pends[pnd_snd_idx], sizeof(pnd_snd)); 1058c2ecf20Sopenharmony_ci /* clear the full struct smc_wr_tx_pend including .priv */ 1068c2ecf20Sopenharmony_ci memset(&link->wr_tx_pends[pnd_snd_idx], 0, 1078c2ecf20Sopenharmony_ci sizeof(link->wr_tx_pends[pnd_snd_idx])); 1088c2ecf20Sopenharmony_ci memset(&link->wr_tx_bufs[pnd_snd_idx], 0, 1098c2ecf20Sopenharmony_ci sizeof(link->wr_tx_bufs[pnd_snd_idx])); 1108c2ecf20Sopenharmony_ci if (!test_and_clear_bit(pnd_snd_idx, link->wr_tx_mask)) 1118c2ecf20Sopenharmony_ci return; 1128c2ecf20Sopenharmony_ci if (wc->status) { 1138c2ecf20Sopenharmony_ci /* terminate link */ 1148c2ecf20Sopenharmony_ci smcr_link_down_cond_sched(link); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci if (pnd_snd.handler) 1178c2ecf20Sopenharmony_ci pnd_snd.handler(&pnd_snd.priv, link, wc->status); 1188c2ecf20Sopenharmony_ci wake_up(&link->wr_tx_wait); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void smc_wr_tx_tasklet_fn(unsigned long data) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct smc_ib_device *dev = (struct smc_ib_device *)data; 1248c2ecf20Sopenharmony_ci struct ib_wc wc[SMC_WR_MAX_POLL_CQE]; 1258c2ecf20Sopenharmony_ci int i = 0, rc; 1268c2ecf20Sopenharmony_ci int polled = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciagain: 1298c2ecf20Sopenharmony_ci polled++; 1308c2ecf20Sopenharmony_ci do { 1318c2ecf20Sopenharmony_ci memset(&wc, 0, sizeof(wc)); 1328c2ecf20Sopenharmony_ci rc = ib_poll_cq(dev->roce_cq_send, SMC_WR_MAX_POLL_CQE, wc); 1338c2ecf20Sopenharmony_ci if (polled == 1) { 1348c2ecf20Sopenharmony_ci ib_req_notify_cq(dev->roce_cq_send, 1358c2ecf20Sopenharmony_ci IB_CQ_NEXT_COMP | 1368c2ecf20Sopenharmony_ci IB_CQ_REPORT_MISSED_EVENTS); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci if (!rc) 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci for (i = 0; i < rc; i++) 1418c2ecf20Sopenharmony_ci smc_wr_tx_process_cqe(&wc[i]); 1428c2ecf20Sopenharmony_ci } while (rc > 0); 1438c2ecf20Sopenharmony_ci if (polled == 1) 1448c2ecf20Sopenharmony_ci goto again; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_civoid smc_wr_tx_cq_handler(struct ib_cq *ib_cq, void *cq_context) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct smc_ib_device *dev = (struct smc_ib_device *)cq_context; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci tasklet_schedule(&dev->send_tasklet); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/*---------------------------- request submission ---------------------------*/ 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic inline int smc_wr_tx_get_free_slot_index(struct smc_link *link, u32 *idx) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci *idx = link->wr_tx_cnt; 1598c2ecf20Sopenharmony_ci if (!smc_link_sendable(link)) 1608c2ecf20Sopenharmony_ci return -ENOLINK; 1618c2ecf20Sopenharmony_ci for_each_clear_bit(*idx, link->wr_tx_mask, link->wr_tx_cnt) { 1628c2ecf20Sopenharmony_ci if (!test_and_set_bit(*idx, link->wr_tx_mask)) 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci *idx = link->wr_tx_cnt; 1668c2ecf20Sopenharmony_ci return -EBUSY; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/** 1708c2ecf20Sopenharmony_ci * smc_wr_tx_get_free_slot() - returns buffer for message assembly, 1718c2ecf20Sopenharmony_ci * and sets info for pending transmit tracking 1728c2ecf20Sopenharmony_ci * @link: Pointer to smc_link used to later send the message. 1738c2ecf20Sopenharmony_ci * @handler: Send completion handler function pointer. 1748c2ecf20Sopenharmony_ci * @wr_buf: Out value returns pointer to message buffer. 1758c2ecf20Sopenharmony_ci * @wr_rdma_buf: Out value returns pointer to rdma work request. 1768c2ecf20Sopenharmony_ci * @wr_pend_priv: Out value returns pointer serving as handler context. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Return: 0 on success, or -errno on error. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ciint smc_wr_tx_get_free_slot(struct smc_link *link, 1818c2ecf20Sopenharmony_ci smc_wr_tx_handler handler, 1828c2ecf20Sopenharmony_ci struct smc_wr_buf **wr_buf, 1838c2ecf20Sopenharmony_ci struct smc_rdma_wr **wr_rdma_buf, 1848c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv **wr_pend_priv) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct smc_link_group *lgr = smc_get_lgr(link); 1878c2ecf20Sopenharmony_ci struct smc_wr_tx_pend *wr_pend; 1888c2ecf20Sopenharmony_ci u32 idx = link->wr_tx_cnt; 1898c2ecf20Sopenharmony_ci struct ib_send_wr *wr_ib; 1908c2ecf20Sopenharmony_ci u64 wr_id; 1918c2ecf20Sopenharmony_ci int rc; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci *wr_buf = NULL; 1948c2ecf20Sopenharmony_ci *wr_pend_priv = NULL; 1958c2ecf20Sopenharmony_ci if (in_softirq() || lgr->terminating) { 1968c2ecf20Sopenharmony_ci rc = smc_wr_tx_get_free_slot_index(link, &idx); 1978c2ecf20Sopenharmony_ci if (rc) 1988c2ecf20Sopenharmony_ci return rc; 1998c2ecf20Sopenharmony_ci } else { 2008c2ecf20Sopenharmony_ci rc = wait_event_interruptible_timeout( 2018c2ecf20Sopenharmony_ci link->wr_tx_wait, 2028c2ecf20Sopenharmony_ci !smc_link_sendable(link) || 2038c2ecf20Sopenharmony_ci lgr->terminating || 2048c2ecf20Sopenharmony_ci (smc_wr_tx_get_free_slot_index(link, &idx) != -EBUSY), 2058c2ecf20Sopenharmony_ci SMC_WR_TX_WAIT_FREE_SLOT_TIME); 2068c2ecf20Sopenharmony_ci if (!rc) { 2078c2ecf20Sopenharmony_ci /* timeout - terminate link */ 2088c2ecf20Sopenharmony_ci smcr_link_down_cond_sched(link); 2098c2ecf20Sopenharmony_ci return -EPIPE; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci if (idx == link->wr_tx_cnt) 2128c2ecf20Sopenharmony_ci return -EPIPE; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci wr_id = smc_wr_tx_get_next_wr_id(link); 2158c2ecf20Sopenharmony_ci wr_pend = &link->wr_tx_pends[idx]; 2168c2ecf20Sopenharmony_ci wr_pend->wr_id = wr_id; 2178c2ecf20Sopenharmony_ci wr_pend->handler = handler; 2188c2ecf20Sopenharmony_ci wr_pend->link = link; 2198c2ecf20Sopenharmony_ci wr_pend->idx = idx; 2208c2ecf20Sopenharmony_ci wr_ib = &link->wr_tx_ibs[idx]; 2218c2ecf20Sopenharmony_ci wr_ib->wr_id = wr_id; 2228c2ecf20Sopenharmony_ci *wr_buf = &link->wr_tx_bufs[idx]; 2238c2ecf20Sopenharmony_ci if (wr_rdma_buf) 2248c2ecf20Sopenharmony_ci *wr_rdma_buf = &link->wr_tx_rdmas[idx]; 2258c2ecf20Sopenharmony_ci *wr_pend_priv = &wr_pend->priv; 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ciint smc_wr_tx_put_slot(struct smc_link *link, 2308c2ecf20Sopenharmony_ci struct smc_wr_tx_pend_priv *wr_pend_priv) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct smc_wr_tx_pend *pend; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pend = container_of(wr_pend_priv, struct smc_wr_tx_pend, priv); 2358c2ecf20Sopenharmony_ci if (pend->idx < link->wr_tx_cnt) { 2368c2ecf20Sopenharmony_ci u32 idx = pend->idx; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* clear the full struct smc_wr_tx_pend including .priv */ 2398c2ecf20Sopenharmony_ci memset(&link->wr_tx_pends[idx], 0, 2408c2ecf20Sopenharmony_ci sizeof(link->wr_tx_pends[idx])); 2418c2ecf20Sopenharmony_ci memset(&link->wr_tx_bufs[idx], 0, 2428c2ecf20Sopenharmony_ci sizeof(link->wr_tx_bufs[idx])); 2438c2ecf20Sopenharmony_ci test_and_clear_bit(idx, link->wr_tx_mask); 2448c2ecf20Sopenharmony_ci wake_up(&link->wr_tx_wait); 2458c2ecf20Sopenharmony_ci return 1; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* Send prepared WR slot via ib_post_send. 2528c2ecf20Sopenharmony_ci * @priv: pointer to smc_wr_tx_pend_priv identifying prepared message buffer 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ciint smc_wr_tx_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct smc_wr_tx_pend *pend; 2578c2ecf20Sopenharmony_ci int rc; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ib_req_notify_cq(link->smcibdev->roce_cq_send, 2608c2ecf20Sopenharmony_ci IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); 2618c2ecf20Sopenharmony_ci pend = container_of(priv, struct smc_wr_tx_pend, priv); 2628c2ecf20Sopenharmony_ci rc = ib_post_send(link->roce_qp, &link->wr_tx_ibs[pend->idx], NULL); 2638c2ecf20Sopenharmony_ci if (rc) { 2648c2ecf20Sopenharmony_ci smc_wr_tx_put_slot(link, priv); 2658c2ecf20Sopenharmony_ci smcr_link_down_cond_sched(link); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci return rc; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* Send prepared WR slot via ib_post_send and wait for send completion 2718c2ecf20Sopenharmony_ci * notification. 2728c2ecf20Sopenharmony_ci * @priv: pointer to smc_wr_tx_pend_priv identifying prepared message buffer 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ciint smc_wr_tx_send_wait(struct smc_link *link, struct smc_wr_tx_pend_priv *priv, 2758c2ecf20Sopenharmony_ci unsigned long timeout) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct smc_wr_tx_pend *pend; 2788c2ecf20Sopenharmony_ci u32 pnd_idx; 2798c2ecf20Sopenharmony_ci int rc; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci pend = container_of(priv, struct smc_wr_tx_pend, priv); 2828c2ecf20Sopenharmony_ci pend->compl_requested = 1; 2838c2ecf20Sopenharmony_ci pnd_idx = pend->idx; 2848c2ecf20Sopenharmony_ci init_completion(&link->wr_tx_compl[pnd_idx]); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci rc = smc_wr_tx_send(link, priv); 2878c2ecf20Sopenharmony_ci if (rc) 2888c2ecf20Sopenharmony_ci return rc; 2898c2ecf20Sopenharmony_ci /* wait for completion by smc_wr_tx_process_cqe() */ 2908c2ecf20Sopenharmony_ci rc = wait_for_completion_interruptible_timeout( 2918c2ecf20Sopenharmony_ci &link->wr_tx_compl[pnd_idx], timeout); 2928c2ecf20Sopenharmony_ci if (rc <= 0) 2938c2ecf20Sopenharmony_ci rc = -ENODATA; 2948c2ecf20Sopenharmony_ci if (rc > 0) 2958c2ecf20Sopenharmony_ci rc = 0; 2968c2ecf20Sopenharmony_ci return rc; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* Register a memory region and wait for result. */ 3008c2ecf20Sopenharmony_ciint smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci int rc; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci ib_req_notify_cq(link->smcibdev->roce_cq_send, 3058c2ecf20Sopenharmony_ci IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); 3068c2ecf20Sopenharmony_ci link->wr_reg_state = POSTED; 3078c2ecf20Sopenharmony_ci link->wr_reg.wr.wr_id = (u64)(uintptr_t)mr; 3088c2ecf20Sopenharmony_ci link->wr_reg.mr = mr; 3098c2ecf20Sopenharmony_ci link->wr_reg.key = mr->rkey; 3108c2ecf20Sopenharmony_ci rc = ib_post_send(link->roce_qp, &link->wr_reg.wr, NULL); 3118c2ecf20Sopenharmony_ci if (rc) 3128c2ecf20Sopenharmony_ci return rc; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci atomic_inc(&link->wr_reg_refcnt); 3158c2ecf20Sopenharmony_ci rc = wait_event_interruptible_timeout(link->wr_reg_wait, 3168c2ecf20Sopenharmony_ci (link->wr_reg_state != POSTED), 3178c2ecf20Sopenharmony_ci SMC_WR_REG_MR_WAIT_TIME); 3188c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&link->wr_reg_refcnt)) 3198c2ecf20Sopenharmony_ci wake_up_all(&link->wr_reg_wait); 3208c2ecf20Sopenharmony_ci if (!rc) { 3218c2ecf20Sopenharmony_ci /* timeout - terminate link */ 3228c2ecf20Sopenharmony_ci smcr_link_down_cond_sched(link); 3238c2ecf20Sopenharmony_ci return -EPIPE; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci if (rc == -ERESTARTSYS) 3268c2ecf20Sopenharmony_ci return -EINTR; 3278c2ecf20Sopenharmony_ci switch (link->wr_reg_state) { 3288c2ecf20Sopenharmony_ci case CONFIRMED: 3298c2ecf20Sopenharmony_ci rc = 0; 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci case FAILED: 3328c2ecf20Sopenharmony_ci rc = -EIO; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case POSTED: 3358c2ecf20Sopenharmony_ci rc = -EPIPE; 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci return rc; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/****************************** receive queue ********************************/ 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciint smc_wr_rx_register_handler(struct smc_wr_rx_handler *handler) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct smc_wr_rx_handler *h_iter; 3468c2ecf20Sopenharmony_ci int rc = 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci spin_lock(&smc_wr_rx_hash_lock); 3498c2ecf20Sopenharmony_ci hash_for_each_possible(smc_wr_rx_hash, h_iter, list, handler->type) { 3508c2ecf20Sopenharmony_ci if (h_iter->type == handler->type) { 3518c2ecf20Sopenharmony_ci rc = -EEXIST; 3528c2ecf20Sopenharmony_ci goto out_unlock; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci hash_add(smc_wr_rx_hash, &handler->list, handler->type); 3568c2ecf20Sopenharmony_ciout_unlock: 3578c2ecf20Sopenharmony_ci spin_unlock(&smc_wr_rx_hash_lock); 3588c2ecf20Sopenharmony_ci return rc; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* Demultiplex a received work request based on the message type to its handler. 3628c2ecf20Sopenharmony_ci * Relies on smc_wr_rx_hash having been completely filled before any IB WRs, 3638c2ecf20Sopenharmony_ci * and not being modified any more afterwards so we don't need to lock it. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic inline void smc_wr_rx_demultiplex(struct ib_wc *wc) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 3688c2ecf20Sopenharmony_ci struct smc_wr_rx_handler *handler; 3698c2ecf20Sopenharmony_ci struct smc_wr_rx_hdr *wr_rx; 3708c2ecf20Sopenharmony_ci u64 temp_wr_id; 3718c2ecf20Sopenharmony_ci u32 index; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (wc->byte_len < sizeof(*wr_rx)) 3748c2ecf20Sopenharmony_ci return; /* short message */ 3758c2ecf20Sopenharmony_ci temp_wr_id = wc->wr_id; 3768c2ecf20Sopenharmony_ci index = do_div(temp_wr_id, link->wr_rx_cnt); 3778c2ecf20Sopenharmony_ci wr_rx = (struct smc_wr_rx_hdr *)&link->wr_rx_bufs[index]; 3788c2ecf20Sopenharmony_ci hash_for_each_possible(smc_wr_rx_hash, handler, list, wr_rx->type) { 3798c2ecf20Sopenharmony_ci if (handler->type == wr_rx->type) 3808c2ecf20Sopenharmony_ci handler->handler(wc, wr_rx); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic inline void smc_wr_rx_process_cqes(struct ib_wc wc[], int num) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct smc_link *link; 3878c2ecf20Sopenharmony_ci int i; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 3908c2ecf20Sopenharmony_ci link = wc[i].qp->qp_context; 3918c2ecf20Sopenharmony_ci if (wc[i].status == IB_WC_SUCCESS) { 3928c2ecf20Sopenharmony_ci link->wr_rx_tstamp = jiffies; 3938c2ecf20Sopenharmony_ci smc_wr_rx_demultiplex(&wc[i]); 3948c2ecf20Sopenharmony_ci smc_wr_rx_post(link); /* refill WR RX */ 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci /* handle status errors */ 3978c2ecf20Sopenharmony_ci switch (wc[i].status) { 3988c2ecf20Sopenharmony_ci case IB_WC_RETRY_EXC_ERR: 3998c2ecf20Sopenharmony_ci case IB_WC_RNR_RETRY_EXC_ERR: 4008c2ecf20Sopenharmony_ci case IB_WC_WR_FLUSH_ERR: 4018c2ecf20Sopenharmony_ci smcr_link_down_cond_sched(link); 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci default: 4048c2ecf20Sopenharmony_ci smc_wr_rx_post(link); /* refill WR RX */ 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic void smc_wr_rx_tasklet_fn(unsigned long data) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct smc_ib_device *dev = (struct smc_ib_device *)data; 4148c2ecf20Sopenharmony_ci struct ib_wc wc[SMC_WR_MAX_POLL_CQE]; 4158c2ecf20Sopenharmony_ci int polled = 0; 4168c2ecf20Sopenharmony_ci int rc; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ciagain: 4198c2ecf20Sopenharmony_ci polled++; 4208c2ecf20Sopenharmony_ci do { 4218c2ecf20Sopenharmony_ci memset(&wc, 0, sizeof(wc)); 4228c2ecf20Sopenharmony_ci rc = ib_poll_cq(dev->roce_cq_recv, SMC_WR_MAX_POLL_CQE, wc); 4238c2ecf20Sopenharmony_ci if (polled == 1) { 4248c2ecf20Sopenharmony_ci ib_req_notify_cq(dev->roce_cq_recv, 4258c2ecf20Sopenharmony_ci IB_CQ_SOLICITED_MASK 4268c2ecf20Sopenharmony_ci | IB_CQ_REPORT_MISSED_EVENTS); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci if (!rc) 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci smc_wr_rx_process_cqes(&wc[0], rc); 4318c2ecf20Sopenharmony_ci } while (rc > 0); 4328c2ecf20Sopenharmony_ci if (polled == 1) 4338c2ecf20Sopenharmony_ci goto again; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_civoid smc_wr_rx_cq_handler(struct ib_cq *ib_cq, void *cq_context) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct smc_ib_device *dev = (struct smc_ib_device *)cq_context; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci tasklet_schedule(&dev->recv_tasklet); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ciint smc_wr_rx_post_init(struct smc_link *link) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci u32 i; 4468c2ecf20Sopenharmony_ci int rc = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci for (i = 0; i < link->wr_rx_cnt; i++) 4498c2ecf20Sopenharmony_ci rc = smc_wr_rx_post(link); 4508c2ecf20Sopenharmony_ci return rc; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/***************************** init, exit, misc ******************************/ 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_civoid smc_wr_remember_qp_attr(struct smc_link *lnk) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct ib_qp_attr *attr = &lnk->qp_attr; 4588c2ecf20Sopenharmony_ci struct ib_qp_init_attr init_attr; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci memset(attr, 0, sizeof(*attr)); 4618c2ecf20Sopenharmony_ci memset(&init_attr, 0, sizeof(init_attr)); 4628c2ecf20Sopenharmony_ci ib_query_qp(lnk->roce_qp, attr, 4638c2ecf20Sopenharmony_ci IB_QP_STATE | 4648c2ecf20Sopenharmony_ci IB_QP_CUR_STATE | 4658c2ecf20Sopenharmony_ci IB_QP_PKEY_INDEX | 4668c2ecf20Sopenharmony_ci IB_QP_PORT | 4678c2ecf20Sopenharmony_ci IB_QP_QKEY | 4688c2ecf20Sopenharmony_ci IB_QP_AV | 4698c2ecf20Sopenharmony_ci IB_QP_PATH_MTU | 4708c2ecf20Sopenharmony_ci IB_QP_TIMEOUT | 4718c2ecf20Sopenharmony_ci IB_QP_RETRY_CNT | 4728c2ecf20Sopenharmony_ci IB_QP_RNR_RETRY | 4738c2ecf20Sopenharmony_ci IB_QP_RQ_PSN | 4748c2ecf20Sopenharmony_ci IB_QP_ALT_PATH | 4758c2ecf20Sopenharmony_ci IB_QP_MIN_RNR_TIMER | 4768c2ecf20Sopenharmony_ci IB_QP_SQ_PSN | 4778c2ecf20Sopenharmony_ci IB_QP_PATH_MIG_STATE | 4788c2ecf20Sopenharmony_ci IB_QP_CAP | 4798c2ecf20Sopenharmony_ci IB_QP_DEST_QPN, 4808c2ecf20Sopenharmony_ci &init_attr); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci lnk->wr_tx_cnt = min_t(size_t, SMC_WR_BUF_CNT, 4838c2ecf20Sopenharmony_ci lnk->qp_attr.cap.max_send_wr); 4848c2ecf20Sopenharmony_ci lnk->wr_rx_cnt = min_t(size_t, SMC_WR_BUF_CNT * 3, 4858c2ecf20Sopenharmony_ci lnk->qp_attr.cap.max_recv_wr); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic void smc_wr_init_sge(struct smc_link *lnk) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci u32 i; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci for (i = 0; i < lnk->wr_tx_cnt; i++) { 4938c2ecf20Sopenharmony_ci lnk->wr_tx_sges[i].addr = 4948c2ecf20Sopenharmony_ci lnk->wr_tx_dma_addr + i * SMC_WR_BUF_SIZE; 4958c2ecf20Sopenharmony_ci lnk->wr_tx_sges[i].length = SMC_WR_TX_SIZE; 4968c2ecf20Sopenharmony_ci lnk->wr_tx_sges[i].lkey = lnk->roce_pd->local_dma_lkey; 4978c2ecf20Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[0].lkey = 4988c2ecf20Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 4998c2ecf20Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[1].lkey = 5008c2ecf20Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 5018c2ecf20Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[0].lkey = 5028c2ecf20Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 5038c2ecf20Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[1].lkey = 5048c2ecf20Sopenharmony_ci lnk->roce_pd->local_dma_lkey; 5058c2ecf20Sopenharmony_ci lnk->wr_tx_ibs[i].next = NULL; 5068c2ecf20Sopenharmony_ci lnk->wr_tx_ibs[i].sg_list = &lnk->wr_tx_sges[i]; 5078c2ecf20Sopenharmony_ci lnk->wr_tx_ibs[i].num_sge = 1; 5088c2ecf20Sopenharmony_ci lnk->wr_tx_ibs[i].opcode = IB_WR_SEND; 5098c2ecf20Sopenharmony_ci lnk->wr_tx_ibs[i].send_flags = 5108c2ecf20Sopenharmony_ci IB_SEND_SIGNALED | IB_SEND_SOLICITED; 5118c2ecf20Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.opcode = IB_WR_RDMA_WRITE; 5128c2ecf20Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.opcode = IB_WR_RDMA_WRITE; 5138c2ecf20Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.sg_list = 5148c2ecf20Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge; 5158c2ecf20Sopenharmony_ci lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.sg_list = 5168c2ecf20Sopenharmony_ci lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci for (i = 0; i < lnk->wr_rx_cnt; i++) { 5198c2ecf20Sopenharmony_ci lnk->wr_rx_sges[i].addr = 5208c2ecf20Sopenharmony_ci lnk->wr_rx_dma_addr + i * SMC_WR_BUF_SIZE; 5218c2ecf20Sopenharmony_ci lnk->wr_rx_sges[i].length = SMC_WR_BUF_SIZE; 5228c2ecf20Sopenharmony_ci lnk->wr_rx_sges[i].lkey = lnk->roce_pd->local_dma_lkey; 5238c2ecf20Sopenharmony_ci lnk->wr_rx_ibs[i].next = NULL; 5248c2ecf20Sopenharmony_ci lnk->wr_rx_ibs[i].sg_list = &lnk->wr_rx_sges[i]; 5258c2ecf20Sopenharmony_ci lnk->wr_rx_ibs[i].num_sge = 1; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci lnk->wr_reg.wr.next = NULL; 5288c2ecf20Sopenharmony_ci lnk->wr_reg.wr.num_sge = 0; 5298c2ecf20Sopenharmony_ci lnk->wr_reg.wr.send_flags = IB_SEND_SIGNALED; 5308c2ecf20Sopenharmony_ci lnk->wr_reg.wr.opcode = IB_WR_REG_MR; 5318c2ecf20Sopenharmony_ci lnk->wr_reg.access = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_civoid smc_wr_free_link(struct smc_link *lnk) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct ib_device *ibdev; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (!lnk->smcibdev) 5398c2ecf20Sopenharmony_ci return; 5408c2ecf20Sopenharmony_ci ibdev = lnk->smcibdev->ibdev; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci smc_wr_wakeup_reg_wait(lnk); 5438c2ecf20Sopenharmony_ci smc_wr_wakeup_tx_wait(lnk); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci smc_wr_tx_wait_no_pending_sends(lnk); 5468c2ecf20Sopenharmony_ci wait_event(lnk->wr_reg_wait, (!atomic_read(&lnk->wr_reg_refcnt))); 5478c2ecf20Sopenharmony_ci wait_event(lnk->wr_tx_wait, (!atomic_read(&lnk->wr_tx_refcnt))); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (lnk->wr_rx_dma_addr) { 5508c2ecf20Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr, 5518c2ecf20Sopenharmony_ci SMC_WR_BUF_SIZE * lnk->wr_rx_cnt, 5528c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5538c2ecf20Sopenharmony_ci lnk->wr_rx_dma_addr = 0; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci if (lnk->wr_tx_dma_addr) { 5568c2ecf20Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_tx_dma_addr, 5578c2ecf20Sopenharmony_ci SMC_WR_BUF_SIZE * lnk->wr_tx_cnt, 5588c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 5598c2ecf20Sopenharmony_ci lnk->wr_tx_dma_addr = 0; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_civoid smc_wr_free_link_mem(struct smc_link *lnk) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_compl); 5668c2ecf20Sopenharmony_ci lnk->wr_tx_compl = NULL; 5678c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_pends); 5688c2ecf20Sopenharmony_ci lnk->wr_tx_pends = NULL; 5698c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_mask); 5708c2ecf20Sopenharmony_ci lnk->wr_tx_mask = NULL; 5718c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_sges); 5728c2ecf20Sopenharmony_ci lnk->wr_tx_sges = NULL; 5738c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_rdma_sges); 5748c2ecf20Sopenharmony_ci lnk->wr_tx_rdma_sges = NULL; 5758c2ecf20Sopenharmony_ci kfree(lnk->wr_rx_sges); 5768c2ecf20Sopenharmony_ci lnk->wr_rx_sges = NULL; 5778c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_rdmas); 5788c2ecf20Sopenharmony_ci lnk->wr_tx_rdmas = NULL; 5798c2ecf20Sopenharmony_ci kfree(lnk->wr_rx_ibs); 5808c2ecf20Sopenharmony_ci lnk->wr_rx_ibs = NULL; 5818c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_ibs); 5828c2ecf20Sopenharmony_ci lnk->wr_tx_ibs = NULL; 5838c2ecf20Sopenharmony_ci kfree(lnk->wr_tx_bufs); 5848c2ecf20Sopenharmony_ci lnk->wr_tx_bufs = NULL; 5858c2ecf20Sopenharmony_ci kfree(lnk->wr_rx_bufs); 5868c2ecf20Sopenharmony_ci lnk->wr_rx_bufs = NULL; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ciint smc_wr_alloc_link_mem(struct smc_link *link) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci /* allocate link related memory */ 5928c2ecf20Sopenharmony_ci link->wr_tx_bufs = kcalloc(SMC_WR_BUF_CNT, SMC_WR_BUF_SIZE, GFP_KERNEL); 5938c2ecf20Sopenharmony_ci if (!link->wr_tx_bufs) 5948c2ecf20Sopenharmony_ci goto no_mem; 5958c2ecf20Sopenharmony_ci link->wr_rx_bufs = kcalloc(SMC_WR_BUF_CNT * 3, SMC_WR_BUF_SIZE, 5968c2ecf20Sopenharmony_ci GFP_KERNEL); 5978c2ecf20Sopenharmony_ci if (!link->wr_rx_bufs) 5988c2ecf20Sopenharmony_ci goto no_mem_wr_tx_bufs; 5998c2ecf20Sopenharmony_ci link->wr_tx_ibs = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_ibs[0]), 6008c2ecf20Sopenharmony_ci GFP_KERNEL); 6018c2ecf20Sopenharmony_ci if (!link->wr_tx_ibs) 6028c2ecf20Sopenharmony_ci goto no_mem_wr_rx_bufs; 6038c2ecf20Sopenharmony_ci link->wr_rx_ibs = kcalloc(SMC_WR_BUF_CNT * 3, 6048c2ecf20Sopenharmony_ci sizeof(link->wr_rx_ibs[0]), 6058c2ecf20Sopenharmony_ci GFP_KERNEL); 6068c2ecf20Sopenharmony_ci if (!link->wr_rx_ibs) 6078c2ecf20Sopenharmony_ci goto no_mem_wr_tx_ibs; 6088c2ecf20Sopenharmony_ci link->wr_tx_rdmas = kcalloc(SMC_WR_BUF_CNT, 6098c2ecf20Sopenharmony_ci sizeof(link->wr_tx_rdmas[0]), 6108c2ecf20Sopenharmony_ci GFP_KERNEL); 6118c2ecf20Sopenharmony_ci if (!link->wr_tx_rdmas) 6128c2ecf20Sopenharmony_ci goto no_mem_wr_rx_ibs; 6138c2ecf20Sopenharmony_ci link->wr_tx_rdma_sges = kcalloc(SMC_WR_BUF_CNT, 6148c2ecf20Sopenharmony_ci sizeof(link->wr_tx_rdma_sges[0]), 6158c2ecf20Sopenharmony_ci GFP_KERNEL); 6168c2ecf20Sopenharmony_ci if (!link->wr_tx_rdma_sges) 6178c2ecf20Sopenharmony_ci goto no_mem_wr_tx_rdmas; 6188c2ecf20Sopenharmony_ci link->wr_tx_sges = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_sges[0]), 6198c2ecf20Sopenharmony_ci GFP_KERNEL); 6208c2ecf20Sopenharmony_ci if (!link->wr_tx_sges) 6218c2ecf20Sopenharmony_ci goto no_mem_wr_tx_rdma_sges; 6228c2ecf20Sopenharmony_ci link->wr_rx_sges = kcalloc(SMC_WR_BUF_CNT * 3, 6238c2ecf20Sopenharmony_ci sizeof(link->wr_rx_sges[0]), 6248c2ecf20Sopenharmony_ci GFP_KERNEL); 6258c2ecf20Sopenharmony_ci if (!link->wr_rx_sges) 6268c2ecf20Sopenharmony_ci goto no_mem_wr_tx_sges; 6278c2ecf20Sopenharmony_ci link->wr_tx_mask = kcalloc(BITS_TO_LONGS(SMC_WR_BUF_CNT), 6288c2ecf20Sopenharmony_ci sizeof(*link->wr_tx_mask), 6298c2ecf20Sopenharmony_ci GFP_KERNEL); 6308c2ecf20Sopenharmony_ci if (!link->wr_tx_mask) 6318c2ecf20Sopenharmony_ci goto no_mem_wr_rx_sges; 6328c2ecf20Sopenharmony_ci link->wr_tx_pends = kcalloc(SMC_WR_BUF_CNT, 6338c2ecf20Sopenharmony_ci sizeof(link->wr_tx_pends[0]), 6348c2ecf20Sopenharmony_ci GFP_KERNEL); 6358c2ecf20Sopenharmony_ci if (!link->wr_tx_pends) 6368c2ecf20Sopenharmony_ci goto no_mem_wr_tx_mask; 6378c2ecf20Sopenharmony_ci link->wr_tx_compl = kcalloc(SMC_WR_BUF_CNT, 6388c2ecf20Sopenharmony_ci sizeof(link->wr_tx_compl[0]), 6398c2ecf20Sopenharmony_ci GFP_KERNEL); 6408c2ecf20Sopenharmony_ci if (!link->wr_tx_compl) 6418c2ecf20Sopenharmony_ci goto no_mem_wr_tx_pends; 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cino_mem_wr_tx_pends: 6458c2ecf20Sopenharmony_ci kfree(link->wr_tx_pends); 6468c2ecf20Sopenharmony_cino_mem_wr_tx_mask: 6478c2ecf20Sopenharmony_ci kfree(link->wr_tx_mask); 6488c2ecf20Sopenharmony_cino_mem_wr_rx_sges: 6498c2ecf20Sopenharmony_ci kfree(link->wr_rx_sges); 6508c2ecf20Sopenharmony_cino_mem_wr_tx_sges: 6518c2ecf20Sopenharmony_ci kfree(link->wr_tx_sges); 6528c2ecf20Sopenharmony_cino_mem_wr_tx_rdma_sges: 6538c2ecf20Sopenharmony_ci kfree(link->wr_tx_rdma_sges); 6548c2ecf20Sopenharmony_cino_mem_wr_tx_rdmas: 6558c2ecf20Sopenharmony_ci kfree(link->wr_tx_rdmas); 6568c2ecf20Sopenharmony_cino_mem_wr_rx_ibs: 6578c2ecf20Sopenharmony_ci kfree(link->wr_rx_ibs); 6588c2ecf20Sopenharmony_cino_mem_wr_tx_ibs: 6598c2ecf20Sopenharmony_ci kfree(link->wr_tx_ibs); 6608c2ecf20Sopenharmony_cino_mem_wr_rx_bufs: 6618c2ecf20Sopenharmony_ci kfree(link->wr_rx_bufs); 6628c2ecf20Sopenharmony_cino_mem_wr_tx_bufs: 6638c2ecf20Sopenharmony_ci kfree(link->wr_tx_bufs); 6648c2ecf20Sopenharmony_cino_mem: 6658c2ecf20Sopenharmony_ci return -ENOMEM; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_civoid smc_wr_remove_dev(struct smc_ib_device *smcibdev) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci tasklet_kill(&smcibdev->recv_tasklet); 6718c2ecf20Sopenharmony_ci tasklet_kill(&smcibdev->send_tasklet); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_civoid smc_wr_add_dev(struct smc_ib_device *smcibdev) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci tasklet_init(&smcibdev->recv_tasklet, smc_wr_rx_tasklet_fn, 6778c2ecf20Sopenharmony_ci (unsigned long)smcibdev); 6788c2ecf20Sopenharmony_ci tasklet_init(&smcibdev->send_tasklet, smc_wr_tx_tasklet_fn, 6798c2ecf20Sopenharmony_ci (unsigned long)smcibdev); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciint smc_wr_create_link(struct smc_link *lnk) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct ib_device *ibdev = lnk->smcibdev->ibdev; 6858c2ecf20Sopenharmony_ci int rc = 0; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci smc_wr_tx_set_wr_id(&lnk->wr_tx_id, 0); 6888c2ecf20Sopenharmony_ci lnk->wr_rx_id = 0; 6898c2ecf20Sopenharmony_ci lnk->wr_rx_dma_addr = ib_dma_map_single( 6908c2ecf20Sopenharmony_ci ibdev, lnk->wr_rx_bufs, SMC_WR_BUF_SIZE * lnk->wr_rx_cnt, 6918c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6928c2ecf20Sopenharmony_ci if (ib_dma_mapping_error(ibdev, lnk->wr_rx_dma_addr)) { 6938c2ecf20Sopenharmony_ci lnk->wr_rx_dma_addr = 0; 6948c2ecf20Sopenharmony_ci rc = -EIO; 6958c2ecf20Sopenharmony_ci goto out; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci lnk->wr_tx_dma_addr = ib_dma_map_single( 6988c2ecf20Sopenharmony_ci ibdev, lnk->wr_tx_bufs, SMC_WR_BUF_SIZE * lnk->wr_tx_cnt, 6998c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 7008c2ecf20Sopenharmony_ci if (ib_dma_mapping_error(ibdev, lnk->wr_tx_dma_addr)) { 7018c2ecf20Sopenharmony_ci rc = -EIO; 7028c2ecf20Sopenharmony_ci goto dma_unmap; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci smc_wr_init_sge(lnk); 7058c2ecf20Sopenharmony_ci memset(lnk->wr_tx_mask, 0, 7068c2ecf20Sopenharmony_ci BITS_TO_LONGS(SMC_WR_BUF_CNT) * sizeof(*lnk->wr_tx_mask)); 7078c2ecf20Sopenharmony_ci init_waitqueue_head(&lnk->wr_tx_wait); 7088c2ecf20Sopenharmony_ci atomic_set(&lnk->wr_tx_refcnt, 0); 7098c2ecf20Sopenharmony_ci init_waitqueue_head(&lnk->wr_reg_wait); 7108c2ecf20Sopenharmony_ci atomic_set(&lnk->wr_reg_refcnt, 0); 7118c2ecf20Sopenharmony_ci return rc; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cidma_unmap: 7148c2ecf20Sopenharmony_ci ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr, 7158c2ecf20Sopenharmony_ci SMC_WR_BUF_SIZE * lnk->wr_rx_cnt, 7168c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7178c2ecf20Sopenharmony_ci lnk->wr_rx_dma_addr = 0; 7188c2ecf20Sopenharmony_ciout: 7198c2ecf20Sopenharmony_ci return rc; 7208c2ecf20Sopenharmony_ci} 721