1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2f38ba179SUrsula Braun /*
3f38ba179SUrsula Braun * Shared Memory Communications over RDMA (SMC-R) and RoCE
4f38ba179SUrsula Braun *
5f38ba179SUrsula Braun * Work Requests exploiting Infiniband API
6f38ba179SUrsula Braun *
7f38ba179SUrsula Braun * Work requests (WR) of type ib_post_send or ib_post_recv respectively
8f38ba179SUrsula Braun * are submitted to either RC SQ or RC RQ respectively
9f38ba179SUrsula Braun * (reliably connected send/receive queue)
10f38ba179SUrsula Braun * and become work queue entries (WQEs).
11f38ba179SUrsula Braun * While an SQ WR/WQE is pending, we track it until transmission completion.
12f38ba179SUrsula Braun * Through a send or receive completion queue (CQ) respectively,
13f38ba179SUrsula Braun * we get completion queue entries (CQEs) [aka work completions (WCs)].
14f38ba179SUrsula Braun * Since the CQ callback is called from IRQ context, we split work by using
15f38ba179SUrsula Braun * bottom halves implemented by tasklets.
16f38ba179SUrsula Braun *
17f38ba179SUrsula Braun * SMC uses this to exchange LLC (link layer control)
18f38ba179SUrsula Braun * and CDC (connection data control) messages.
19f38ba179SUrsula Braun *
20f38ba179SUrsula Braun * Copyright IBM Corp. 2016
21f38ba179SUrsula Braun *
22f38ba179SUrsula Braun * Author(s): Steffen Maier <maier@linux.vnet.ibm.com>
23f38ba179SUrsula Braun */
24f38ba179SUrsula Braun
25f38ba179SUrsula Braun #include <linux/atomic.h>
26f38ba179SUrsula Braun #include <linux/hashtable.h>
27f38ba179SUrsula Braun #include <linux/wait.h>
28f38ba179SUrsula Braun #include <rdma/ib_verbs.h>
29f38ba179SUrsula Braun #include <asm/div64.h>
30f38ba179SUrsula Braun
31f38ba179SUrsula Braun #include "smc.h"
32f38ba179SUrsula Braun #include "smc_wr.h"
33f38ba179SUrsula Braun
34f38ba179SUrsula Braun #define SMC_WR_MAX_POLL_CQE 10 /* max. # of compl. queue elements in 1 poll */
35f38ba179SUrsula Braun
36f38ba179SUrsula Braun #define SMC_WR_RX_HASH_BITS 4
37f38ba179SUrsula Braun static DEFINE_HASHTABLE(smc_wr_rx_hash, SMC_WR_RX_HASH_BITS);
38f38ba179SUrsula Braun static DEFINE_SPINLOCK(smc_wr_rx_hash_lock);
39f38ba179SUrsula Braun
40f38ba179SUrsula Braun struct smc_wr_tx_pend { /* control data for a pending send request */
41f38ba179SUrsula Braun u64 wr_id; /* work request id sent */
42f38ba179SUrsula Braun smc_wr_tx_handler handler;
43f38ba179SUrsula Braun enum ib_wc_status wc_status; /* CQE status */
44f38ba179SUrsula Braun struct smc_link *link;
45f38ba179SUrsula Braun u32 idx;
46f38ba179SUrsula Braun struct smc_wr_tx_pend_priv priv;
4709c61d24SKarsten Graul u8 compl_requested;
48f38ba179SUrsula Braun };
49f38ba179SUrsula Braun
50f38ba179SUrsula Braun /******************************** send queue *********************************/
51f38ba179SUrsula Braun
52f38ba179SUrsula Braun /*------------------------------- completion --------------------------------*/
53f38ba179SUrsula Braun
546a37ad3dSUrsula Braun /* returns true if at least one tx work request is pending on the given link */
smc_wr_is_tx_pend(struct smc_link * link)556a37ad3dSUrsula Braun static inline bool smc_wr_is_tx_pend(struct smc_link *link)
566a37ad3dSUrsula Braun {
5749dc9013SChristophe JAILLET return !bitmap_empty(link->wr_tx_mask, link->wr_tx_cnt);
586a37ad3dSUrsula Braun }
596a37ad3dSUrsula Braun
606a37ad3dSUrsula Braun /* wait till all pending tx work requests on the given link are completed */
smc_wr_tx_wait_no_pending_sends(struct smc_link * link)61349d4312SDust Li void smc_wr_tx_wait_no_pending_sends(struct smc_link *link)
626a37ad3dSUrsula Braun {
63349d4312SDust Li wait_event(link->wr_tx_wait, !smc_wr_is_tx_pend(link));
646a37ad3dSUrsula Braun }
656a37ad3dSUrsula Braun
smc_wr_tx_find_pending_index(struct smc_link * link,u64 wr_id)66f38ba179SUrsula Braun static inline int smc_wr_tx_find_pending_index(struct smc_link *link, u64 wr_id)
67f38ba179SUrsula Braun {
68f38ba179SUrsula Braun u32 i;
69f38ba179SUrsula Braun
70f38ba179SUrsula Braun for (i = 0; i < link->wr_tx_cnt; i++) {
71f38ba179SUrsula Braun if (link->wr_tx_pends[i].wr_id == wr_id)
72f38ba179SUrsula Braun return i;
73f38ba179SUrsula Braun }
74f38ba179SUrsula Braun return link->wr_tx_cnt;
75f38ba179SUrsula Braun }
76f38ba179SUrsula Braun
smc_wr_tx_process_cqe(struct ib_wc * wc)77f38ba179SUrsula Braun static inline void smc_wr_tx_process_cqe(struct ib_wc *wc)
78f38ba179SUrsula Braun {
79f38ba179SUrsula Braun struct smc_wr_tx_pend pnd_snd;
80f38ba179SUrsula Braun struct smc_link *link;
81f38ba179SUrsula Braun u32 pnd_snd_idx;
82f38ba179SUrsula Braun
83f38ba179SUrsula Braun link = wc->qp->qp_context;
84652a1e41SUrsula Braun
85652a1e41SUrsula Braun if (wc->opcode == IB_WC_REG_MR) {
86652a1e41SUrsula Braun if (wc->status)
87652a1e41SUrsula Braun link->wr_reg_state = FAILED;
88652a1e41SUrsula Braun else
89652a1e41SUrsula Braun link->wr_reg_state = CONFIRMED;
9015e1b99aSUrsula Braun smc_wr_wakeup_reg_wait(link);
91652a1e41SUrsula Braun return;
92652a1e41SUrsula Braun }
93652a1e41SUrsula Braun
94f38ba179SUrsula Braun pnd_snd_idx = smc_wr_tx_find_pending_index(link, wc->wr_id);
958799e310SKarsten Graul if (pnd_snd_idx == link->wr_tx_cnt) {
968799e310SKarsten Graul if (link->lgr->smc_version != SMC_V2 ||
978799e310SKarsten Graul link->wr_tx_v2_pend->wr_id != wc->wr_id)
98f38ba179SUrsula Braun return;
998799e310SKarsten Graul link->wr_tx_v2_pend->wc_status = wc->status;
1008799e310SKarsten Graul memcpy(&pnd_snd, link->wr_tx_v2_pend, sizeof(pnd_snd));
1018799e310SKarsten Graul /* clear the full struct smc_wr_tx_pend including .priv */
1028799e310SKarsten Graul memset(link->wr_tx_v2_pend, 0,
1038799e310SKarsten Graul sizeof(*link->wr_tx_v2_pend));
1048799e310SKarsten Graul memset(link->lgr->wr_tx_buf_v2, 0,
1058799e310SKarsten Graul sizeof(*link->lgr->wr_tx_buf_v2));
1068799e310SKarsten Graul } else {
107f38ba179SUrsula Braun link->wr_tx_pends[pnd_snd_idx].wc_status = wc->status;
10809c61d24SKarsten Graul if (link->wr_tx_pends[pnd_snd_idx].compl_requested)
10909c61d24SKarsten Graul complete(&link->wr_tx_compl[pnd_snd_idx]);
1108799e310SKarsten Graul memcpy(&pnd_snd, &link->wr_tx_pends[pnd_snd_idx],
1118799e310SKarsten Graul sizeof(pnd_snd));
112f38ba179SUrsula Braun /* clear the full struct smc_wr_tx_pend including .priv */
113f38ba179SUrsula Braun memset(&link->wr_tx_pends[pnd_snd_idx], 0,
114f38ba179SUrsula Braun sizeof(link->wr_tx_pends[pnd_snd_idx]));
115f38ba179SUrsula Braun memset(&link->wr_tx_bufs[pnd_snd_idx], 0,
116f38ba179SUrsula Braun sizeof(link->wr_tx_bufs[pnd_snd_idx]));
117f38ba179SUrsula Braun if (!test_and_clear_bit(pnd_snd_idx, link->wr_tx_mask))
118f38ba179SUrsula Braun return;
1198799e310SKarsten Graul }
1208799e310SKarsten Graul
121f38ba179SUrsula Braun if (wc->status) {
1228799e310SKarsten Graul if (link->lgr->smc_version == SMC_V2) {
1238799e310SKarsten Graul memset(link->wr_tx_v2_pend, 0,
1248799e310SKarsten Graul sizeof(*link->wr_tx_v2_pend));
1258799e310SKarsten Graul memset(link->lgr->wr_tx_buf_v2, 0,
1268799e310SKarsten Graul sizeof(*link->lgr->wr_tx_buf_v2));
1278799e310SKarsten Graul }
12887523930SKarsten Graul /* terminate link */
12987523930SKarsten Graul smcr_link_down_cond_sched(link);
130f38ba179SUrsula Braun }
131f38ba179SUrsula Braun if (pnd_snd.handler)
132f38ba179SUrsula Braun pnd_snd.handler(&pnd_snd.priv, link, wc->status);
133f38ba179SUrsula Braun wake_up(&link->wr_tx_wait);
134f38ba179SUrsula Braun }
135f38ba179SUrsula Braun
smc_wr_tx_tasklet_fn(struct tasklet_struct * t)136fcb8e3a3SAllen Pais static void smc_wr_tx_tasklet_fn(struct tasklet_struct *t)
137f38ba179SUrsula Braun {
138fcb8e3a3SAllen Pais struct smc_ib_device *dev = from_tasklet(dev, t, send_tasklet);
139f38ba179SUrsula Braun struct ib_wc wc[SMC_WR_MAX_POLL_CQE];
140f38ba179SUrsula Braun int i = 0, rc;
141f38ba179SUrsula Braun int polled = 0;
142f38ba179SUrsula Braun
143f38ba179SUrsula Braun again:
144f38ba179SUrsula Braun polled++;
145f38ba179SUrsula Braun do {
14686e780d3SUrsula Braun memset(&wc, 0, sizeof(wc));
147f38ba179SUrsula Braun rc = ib_poll_cq(dev->roce_cq_send, SMC_WR_MAX_POLL_CQE, wc);
148f38ba179SUrsula Braun if (polled == 1) {
149f38ba179SUrsula Braun ib_req_notify_cq(dev->roce_cq_send,
150f38ba179SUrsula Braun IB_CQ_NEXT_COMP |
151f38ba179SUrsula Braun IB_CQ_REPORT_MISSED_EVENTS);
152f38ba179SUrsula Braun }
153f38ba179SUrsula Braun if (!rc)
154f38ba179SUrsula Braun break;
155f38ba179SUrsula Braun for (i = 0; i < rc; i++)
156f38ba179SUrsula Braun smc_wr_tx_process_cqe(&wc[i]);
157f38ba179SUrsula Braun } while (rc > 0);
158f38ba179SUrsula Braun if (polled == 1)
159f38ba179SUrsula Braun goto again;
160f38ba179SUrsula Braun }
161f38ba179SUrsula Braun
smc_wr_tx_cq_handler(struct ib_cq * ib_cq,void * cq_context)162f38ba179SUrsula Braun void smc_wr_tx_cq_handler(struct ib_cq *ib_cq, void *cq_context)
163f38ba179SUrsula Braun {
164f38ba179SUrsula Braun struct smc_ib_device *dev = (struct smc_ib_device *)cq_context;
165f38ba179SUrsula Braun
166f38ba179SUrsula Braun tasklet_schedule(&dev->send_tasklet);
167f38ba179SUrsula Braun }
168f38ba179SUrsula Braun
169f38ba179SUrsula Braun /*---------------------------- request submission ---------------------------*/
170f38ba179SUrsula Braun
smc_wr_tx_get_free_slot_index(struct smc_link * link,u32 * idx)171f38ba179SUrsula Braun static inline int smc_wr_tx_get_free_slot_index(struct smc_link *link, u32 *idx)
172f38ba179SUrsula Braun {
173f38ba179SUrsula Braun *idx = link->wr_tx_cnt;
17490cee52fSDust Li if (!smc_link_sendable(link))
175b7eede75SKarsten Graul return -ENOLINK;
176f38ba179SUrsula Braun for_each_clear_bit(*idx, link->wr_tx_mask, link->wr_tx_cnt) {
177f38ba179SUrsula Braun if (!test_and_set_bit(*idx, link->wr_tx_mask))
178f38ba179SUrsula Braun return 0;
179f38ba179SUrsula Braun }
180f38ba179SUrsula Braun *idx = link->wr_tx_cnt;
181f38ba179SUrsula Braun return -EBUSY;
182f38ba179SUrsula Braun }
183f38ba179SUrsula Braun
184f38ba179SUrsula Braun /**
185f38ba179SUrsula Braun * smc_wr_tx_get_free_slot() - returns buffer for message assembly,
186f38ba179SUrsula Braun * and sets info for pending transmit tracking
187f38ba179SUrsula Braun * @link: Pointer to smc_link used to later send the message.
188f38ba179SUrsula Braun * @handler: Send completion handler function pointer.
189f38ba179SUrsula Braun * @wr_buf: Out value returns pointer to message buffer.
190ad6f317fSUrsula Braun * @wr_rdma_buf: Out value returns pointer to rdma work request.
191f38ba179SUrsula Braun * @wr_pend_priv: Out value returns pointer serving as handler context.
192f38ba179SUrsula Braun *
193f38ba179SUrsula Braun * Return: 0 on success, or -errno on error.
194f38ba179SUrsula Braun */
smc_wr_tx_get_free_slot(struct smc_link * link,smc_wr_tx_handler handler,struct smc_wr_buf ** wr_buf,struct smc_rdma_wr ** wr_rdma_buf,struct smc_wr_tx_pend_priv ** wr_pend_priv)195f38ba179SUrsula Braun int smc_wr_tx_get_free_slot(struct smc_link *link,
196f38ba179SUrsula Braun smc_wr_tx_handler handler,
197f38ba179SUrsula Braun struct smc_wr_buf **wr_buf,
198ad6f317fSUrsula Braun struct smc_rdma_wr **wr_rdma_buf,
199f38ba179SUrsula Braun struct smc_wr_tx_pend_priv **wr_pend_priv)
200f38ba179SUrsula Braun {
20115e1b99aSUrsula Braun struct smc_link_group *lgr = smc_get_lgr(link);
202f38ba179SUrsula Braun struct smc_wr_tx_pend *wr_pend;
2031a0a04c7SUrsula Braun u32 idx = link->wr_tx_cnt;
204f38ba179SUrsula Braun struct ib_send_wr *wr_ib;
205f38ba179SUrsula Braun u64 wr_id;
206f38ba179SUrsula Braun int rc;
207f38ba179SUrsula Braun
208f38ba179SUrsula Braun *wr_buf = NULL;
209f38ba179SUrsula Braun *wr_pend_priv = NULL;
21015e1b99aSUrsula Braun if (in_softirq() || lgr->terminating) {
211f38ba179SUrsula Braun rc = smc_wr_tx_get_free_slot_index(link, &idx);
212f38ba179SUrsula Braun if (rc)
213f38ba179SUrsula Braun return rc;
214f38ba179SUrsula Braun } else {
21515e1b99aSUrsula Braun rc = wait_event_interruptible_timeout(
2161a0a04c7SUrsula Braun link->wr_tx_wait,
21790cee52fSDust Li !smc_link_sendable(link) ||
21815e1b99aSUrsula Braun lgr->terminating ||
2191a0a04c7SUrsula Braun (smc_wr_tx_get_free_slot_index(link, &idx) != -EBUSY),
2201a0a04c7SUrsula Braun SMC_WR_TX_WAIT_FREE_SLOT_TIME);
2211a0a04c7SUrsula Braun if (!rc) {
22287523930SKarsten Graul /* timeout - terminate link */
22387523930SKarsten Graul smcr_link_down_cond_sched(link);
224f38ba179SUrsula Braun return -EPIPE;
225f38ba179SUrsula Braun }
226f38ba179SUrsula Braun if (idx == link->wr_tx_cnt)
227f38ba179SUrsula Braun return -EPIPE;
228f38ba179SUrsula Braun }
229f38ba179SUrsula Braun wr_id = smc_wr_tx_get_next_wr_id(link);
230f38ba179SUrsula Braun wr_pend = &link->wr_tx_pends[idx];
231f38ba179SUrsula Braun wr_pend->wr_id = wr_id;
232f38ba179SUrsula Braun wr_pend->handler = handler;
233f38ba179SUrsula Braun wr_pend->link = link;
234f38ba179SUrsula Braun wr_pend->idx = idx;
235f38ba179SUrsula Braun wr_ib = &link->wr_tx_ibs[idx];
236f38ba179SUrsula Braun wr_ib->wr_id = wr_id;
237f38ba179SUrsula Braun *wr_buf = &link->wr_tx_bufs[idx];
238ad6f317fSUrsula Braun if (wr_rdma_buf)
239ad6f317fSUrsula Braun *wr_rdma_buf = &link->wr_tx_rdmas[idx];
240f38ba179SUrsula Braun *wr_pend_priv = &wr_pend->priv;
241f38ba179SUrsula Braun return 0;
242f38ba179SUrsula Braun }
243f38ba179SUrsula Braun
smc_wr_tx_get_v2_slot(struct smc_link * link,smc_wr_tx_handler handler,struct smc_wr_v2_buf ** wr_buf,struct smc_wr_tx_pend_priv ** wr_pend_priv)244b4ba4652SKarsten Graul int smc_wr_tx_get_v2_slot(struct smc_link *link,
245b4ba4652SKarsten Graul smc_wr_tx_handler handler,
246b4ba4652SKarsten Graul struct smc_wr_v2_buf **wr_buf,
247b4ba4652SKarsten Graul struct smc_wr_tx_pend_priv **wr_pend_priv)
248b4ba4652SKarsten Graul {
249b4ba4652SKarsten Graul struct smc_wr_tx_pend *wr_pend;
250b4ba4652SKarsten Graul struct ib_send_wr *wr_ib;
251b4ba4652SKarsten Graul u64 wr_id;
252b4ba4652SKarsten Graul
253b4ba4652SKarsten Graul if (link->wr_tx_v2_pend->idx == link->wr_tx_cnt)
254b4ba4652SKarsten Graul return -EBUSY;
255b4ba4652SKarsten Graul
256b4ba4652SKarsten Graul *wr_buf = NULL;
257b4ba4652SKarsten Graul *wr_pend_priv = NULL;
258b4ba4652SKarsten Graul wr_id = smc_wr_tx_get_next_wr_id(link);
259b4ba4652SKarsten Graul wr_pend = link->wr_tx_v2_pend;
260b4ba4652SKarsten Graul wr_pend->wr_id = wr_id;
261b4ba4652SKarsten Graul wr_pend->handler = handler;
262b4ba4652SKarsten Graul wr_pend->link = link;
263b4ba4652SKarsten Graul wr_pend->idx = link->wr_tx_cnt;
264b4ba4652SKarsten Graul wr_ib = link->wr_tx_v2_ib;
265b4ba4652SKarsten Graul wr_ib->wr_id = wr_id;
266b4ba4652SKarsten Graul *wr_buf = link->lgr->wr_tx_buf_v2;
267b4ba4652SKarsten Graul *wr_pend_priv = &wr_pend->priv;
268b4ba4652SKarsten Graul return 0;
269b4ba4652SKarsten Graul }
270b4ba4652SKarsten Graul
smc_wr_tx_put_slot(struct smc_link * link,struct smc_wr_tx_pend_priv * wr_pend_priv)271f38ba179SUrsula Braun int smc_wr_tx_put_slot(struct smc_link *link,
272f38ba179SUrsula Braun struct smc_wr_tx_pend_priv *wr_pend_priv)
273f38ba179SUrsula Braun {
274f38ba179SUrsula Braun struct smc_wr_tx_pend *pend;
275f38ba179SUrsula Braun
276f38ba179SUrsula Braun pend = container_of(wr_pend_priv, struct smc_wr_tx_pend, priv);
277f38ba179SUrsula Braun if (pend->idx < link->wr_tx_cnt) {
278e438bae4SUrsula Braun u32 idx = pend->idx;
279e438bae4SUrsula Braun
280f38ba179SUrsula Braun /* clear the full struct smc_wr_tx_pend including .priv */
28146ad0222SKarsten Graul memset(&link->wr_tx_pends[idx], 0,
28246ad0222SKarsten Graul sizeof(link->wr_tx_pends[idx]));
28346ad0222SKarsten Graul memset(&link->wr_tx_bufs[idx], 0,
28446ad0222SKarsten Graul sizeof(link->wr_tx_bufs[idx]));
285e438bae4SUrsula Braun test_and_clear_bit(idx, link->wr_tx_mask);
2866a37ad3dSUrsula Braun wake_up(&link->wr_tx_wait);
287f38ba179SUrsula Braun return 1;
2888799e310SKarsten Graul } else if (link->lgr->smc_version == SMC_V2 &&
2898799e310SKarsten Graul pend->idx == link->wr_tx_cnt) {
2908799e310SKarsten Graul /* Large v2 buffer */
2918799e310SKarsten Graul memset(&link->wr_tx_v2_pend, 0,
2928799e310SKarsten Graul sizeof(link->wr_tx_v2_pend));
2938799e310SKarsten Graul memset(&link->lgr->wr_tx_buf_v2, 0,
2948799e310SKarsten Graul sizeof(link->lgr->wr_tx_buf_v2));
2958799e310SKarsten Graul return 1;
296f38ba179SUrsula Braun }
297f38ba179SUrsula Braun
298f38ba179SUrsula Braun return 0;
299f38ba179SUrsula Braun }
300f38ba179SUrsula Braun
301f38ba179SUrsula Braun /* Send prepared WR slot via ib_post_send.
302f38ba179SUrsula Braun * @priv: pointer to smc_wr_tx_pend_priv identifying prepared message buffer
303f38ba179SUrsula Braun */
smc_wr_tx_send(struct smc_link * link,struct smc_wr_tx_pend_priv * priv)304f38ba179SUrsula Braun int smc_wr_tx_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv)
305f38ba179SUrsula Braun {
306f38ba179SUrsula Braun struct smc_wr_tx_pend *pend;
307f38ba179SUrsula Braun int rc;
308f38ba179SUrsula Braun
309f38ba179SUrsula Braun ib_req_notify_cq(link->smcibdev->roce_cq_send,
3108301fa44SUrsula Braun IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS);
311f38ba179SUrsula Braun pend = container_of(priv, struct smc_wr_tx_pend, priv);
3122e3bbe46SBart Van Assche rc = ib_post_send(link->roce_qp, &link->wr_tx_ibs[pend->idx], NULL);
313b4772b3aSUrsula Braun if (rc) {
314f38ba179SUrsula Braun smc_wr_tx_put_slot(link, priv);
31587523930SKarsten Graul smcr_link_down_cond_sched(link);
316b4772b3aSUrsula Braun }
317f38ba179SUrsula Braun return rc;
318f38ba179SUrsula Braun }
319f38ba179SUrsula Braun
smc_wr_tx_v2_send(struct smc_link * link,struct smc_wr_tx_pend_priv * priv,int len)320b4ba4652SKarsten Graul int smc_wr_tx_v2_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv,
321b4ba4652SKarsten Graul int len)
322b4ba4652SKarsten Graul {
323b4ba4652SKarsten Graul int rc;
324b4ba4652SKarsten Graul
325b4ba4652SKarsten Graul link->wr_tx_v2_ib->sg_list[0].length = len;
326b4ba4652SKarsten Graul ib_req_notify_cq(link->smcibdev->roce_cq_send,
327b4ba4652SKarsten Graul IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS);
328b4ba4652SKarsten Graul rc = ib_post_send(link->roce_qp, link->wr_tx_v2_ib, NULL);
329b4ba4652SKarsten Graul if (rc) {
330b4ba4652SKarsten Graul smc_wr_tx_put_slot(link, priv);
331b4ba4652SKarsten Graul smcr_link_down_cond_sched(link);
332b4ba4652SKarsten Graul }
333b4ba4652SKarsten Graul return rc;
334b4ba4652SKarsten Graul }
335b4ba4652SKarsten Graul
33609c61d24SKarsten Graul /* Send prepared WR slot via ib_post_send and wait for send completion
33709c61d24SKarsten Graul * notification.
33809c61d24SKarsten Graul * @priv: pointer to smc_wr_tx_pend_priv identifying prepared message buffer
33909c61d24SKarsten Graul */
smc_wr_tx_send_wait(struct smc_link * link,struct smc_wr_tx_pend_priv * priv,unsigned long timeout)34009c61d24SKarsten Graul int smc_wr_tx_send_wait(struct smc_link *link, struct smc_wr_tx_pend_priv *priv,
34109c61d24SKarsten Graul unsigned long timeout)
34209c61d24SKarsten Graul {
34309c61d24SKarsten Graul struct smc_wr_tx_pend *pend;
3446d7373daSKarsten Graul u32 pnd_idx;
34509c61d24SKarsten Graul int rc;
34609c61d24SKarsten Graul
34709c61d24SKarsten Graul pend = container_of(priv, struct smc_wr_tx_pend, priv);
34809c61d24SKarsten Graul pend->compl_requested = 1;
3496d7373daSKarsten Graul pnd_idx = pend->idx;
3506d7373daSKarsten Graul init_completion(&link->wr_tx_compl[pnd_idx]);
35109c61d24SKarsten Graul
35209c61d24SKarsten Graul rc = smc_wr_tx_send(link, priv);
35309c61d24SKarsten Graul if (rc)
35409c61d24SKarsten Graul return rc;
35509c61d24SKarsten Graul /* wait for completion by smc_wr_tx_process_cqe() */
35609c61d24SKarsten Graul rc = wait_for_completion_interruptible_timeout(
3576d7373daSKarsten Graul &link->wr_tx_compl[pnd_idx], timeout);
35809c61d24SKarsten Graul if (rc <= 0)
35909c61d24SKarsten Graul rc = -ENODATA;
36009c61d24SKarsten Graul if (rc > 0)
36109c61d24SKarsten Graul rc = 0;
36209c61d24SKarsten Graul return rc;
36309c61d24SKarsten Graul }
36409c61d24SKarsten Graul
365652a1e41SUrsula Braun /* Register a memory region and wait for result. */
smc_wr_reg_send(struct smc_link * link,struct ib_mr * mr)366652a1e41SUrsula Braun int smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr)
367652a1e41SUrsula Braun {
368652a1e41SUrsula Braun int rc;
369652a1e41SUrsula Braun
370652a1e41SUrsula Braun ib_req_notify_cq(link->smcibdev->roce_cq_send,
371652a1e41SUrsula Braun IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS);
372652a1e41SUrsula Braun link->wr_reg_state = POSTED;
373652a1e41SUrsula Braun link->wr_reg.wr.wr_id = (u64)(uintptr_t)mr;
374652a1e41SUrsula Braun link->wr_reg.mr = mr;
375652a1e41SUrsula Braun link->wr_reg.key = mr->rkey;
3762e3bbe46SBart Van Assche rc = ib_post_send(link->roce_qp, &link->wr_reg.wr, NULL);
377652a1e41SUrsula Braun if (rc)
378652a1e41SUrsula Braun return rc;
379652a1e41SUrsula Braun
38079a22238SKai Shen percpu_ref_get(&link->wr_reg_refs);
381652a1e41SUrsula Braun rc = wait_event_interruptible_timeout(link->wr_reg_wait,
382652a1e41SUrsula Braun (link->wr_reg_state != POSTED),
383652a1e41SUrsula Braun SMC_WR_REG_MR_WAIT_TIME);
38479a22238SKai Shen percpu_ref_put(&link->wr_reg_refs);
385652a1e41SUrsula Braun if (!rc) {
38687523930SKarsten Graul /* timeout - terminate link */
38787523930SKarsten Graul smcr_link_down_cond_sched(link);
388652a1e41SUrsula Braun return -EPIPE;
389652a1e41SUrsula Braun }
390652a1e41SUrsula Braun if (rc == -ERESTARTSYS)
391652a1e41SUrsula Braun return -EINTR;
392652a1e41SUrsula Braun switch (link->wr_reg_state) {
393652a1e41SUrsula Braun case CONFIRMED:
394652a1e41SUrsula Braun rc = 0;
395652a1e41SUrsula Braun break;
396652a1e41SUrsula Braun case FAILED:
397652a1e41SUrsula Braun rc = -EIO;
398652a1e41SUrsula Braun break;
399652a1e41SUrsula Braun case POSTED:
400652a1e41SUrsula Braun rc = -EPIPE;
401652a1e41SUrsula Braun break;
402652a1e41SUrsula Braun }
403652a1e41SUrsula Braun return rc;
404652a1e41SUrsula Braun }
405652a1e41SUrsula Braun
406f38ba179SUrsula Braun /****************************** receive queue ********************************/
407f38ba179SUrsula Braun
smc_wr_rx_register_handler(struct smc_wr_rx_handler * handler)408f38ba179SUrsula Braun int smc_wr_rx_register_handler(struct smc_wr_rx_handler *handler)
409f38ba179SUrsula Braun {
410f38ba179SUrsula Braun struct smc_wr_rx_handler *h_iter;
411f38ba179SUrsula Braun int rc = 0;
412f38ba179SUrsula Braun
413f38ba179SUrsula Braun spin_lock(&smc_wr_rx_hash_lock);
414f38ba179SUrsula Braun hash_for_each_possible(smc_wr_rx_hash, h_iter, list, handler->type) {
415f38ba179SUrsula Braun if (h_iter->type == handler->type) {
416f38ba179SUrsula Braun rc = -EEXIST;
417f38ba179SUrsula Braun goto out_unlock;
418f38ba179SUrsula Braun }
419f38ba179SUrsula Braun }
420f38ba179SUrsula Braun hash_add(smc_wr_rx_hash, &handler->list, handler->type);
421f38ba179SUrsula Braun out_unlock:
422f38ba179SUrsula Braun spin_unlock(&smc_wr_rx_hash_lock);
423f38ba179SUrsula Braun return rc;
424f38ba179SUrsula Braun }
425f38ba179SUrsula Braun
426f38ba179SUrsula Braun /* Demultiplex a received work request based on the message type to its handler.
427f38ba179SUrsula Braun * Relies on smc_wr_rx_hash having been completely filled before any IB WRs,
428f38ba179SUrsula Braun * and not being modified any more afterwards so we don't need to lock it.
429f38ba179SUrsula Braun */
smc_wr_rx_demultiplex(struct ib_wc * wc)430f38ba179SUrsula Braun static inline void smc_wr_rx_demultiplex(struct ib_wc *wc)
431f38ba179SUrsula Braun {
432f38ba179SUrsula Braun struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
433f38ba179SUrsula Braun struct smc_wr_rx_handler *handler;
434f38ba179SUrsula Braun struct smc_wr_rx_hdr *wr_rx;
435f38ba179SUrsula Braun u64 temp_wr_id;
436f38ba179SUrsula Braun u32 index;
437f38ba179SUrsula Braun
438f38ba179SUrsula Braun if (wc->byte_len < sizeof(*wr_rx))
439f38ba179SUrsula Braun return; /* short message */
440f38ba179SUrsula Braun temp_wr_id = wc->wr_id;
441f38ba179SUrsula Braun index = do_div(temp_wr_id, link->wr_rx_cnt);
442f38ba179SUrsula Braun wr_rx = (struct smc_wr_rx_hdr *)&link->wr_rx_bufs[index];
443f38ba179SUrsula Braun hash_for_each_possible(smc_wr_rx_hash, handler, list, wr_rx->type) {
444f38ba179SUrsula Braun if (handler->type == wr_rx->type)
445f38ba179SUrsula Braun handler->handler(wc, wr_rx);
446f38ba179SUrsula Braun }
447f38ba179SUrsula Braun }
448f38ba179SUrsula Braun
smc_wr_rx_process_cqes(struct ib_wc wc[],int num)449f38ba179SUrsula Braun static inline void smc_wr_rx_process_cqes(struct ib_wc wc[], int num)
450f38ba179SUrsula Braun {
451f38ba179SUrsula Braun struct smc_link *link;
452f38ba179SUrsula Braun int i;
453f38ba179SUrsula Braun
454f38ba179SUrsula Braun for (i = 0; i < num; i++) {
455f38ba179SUrsula Braun link = wc[i].qp->qp_context;
456e9b1a4f8SYacan Liu link->wr_rx_id_compl = wc[i].wr_id;
457f38ba179SUrsula Braun if (wc[i].status == IB_WC_SUCCESS) {
458877ae5beSKarsten Graul link->wr_rx_tstamp = jiffies;
459f38ba179SUrsula Braun smc_wr_rx_demultiplex(&wc[i]);
460f38ba179SUrsula Braun smc_wr_rx_post(link); /* refill WR RX */
461f38ba179SUrsula Braun } else {
462f38ba179SUrsula Braun /* handle status errors */
463f38ba179SUrsula Braun switch (wc[i].status) {
464f38ba179SUrsula Braun case IB_WC_RETRY_EXC_ERR:
465f38ba179SUrsula Braun case IB_WC_RNR_RETRY_EXC_ERR:
466f38ba179SUrsula Braun case IB_WC_WR_FLUSH_ERR:
46787523930SKarsten Graul smcr_link_down_cond_sched(link);
468e9b1a4f8SYacan Liu if (link->wr_rx_id_compl == link->wr_rx_id)
469e9b1a4f8SYacan Liu wake_up(&link->wr_rx_empty_wait);
470f38ba179SUrsula Braun break;
471f38ba179SUrsula Braun default:
472f38ba179SUrsula Braun smc_wr_rx_post(link); /* refill WR RX */
473f38ba179SUrsula Braun break;
474f38ba179SUrsula Braun }
475f38ba179SUrsula Braun }
476f38ba179SUrsula Braun }
477f38ba179SUrsula Braun }
478f38ba179SUrsula Braun
smc_wr_rx_tasklet_fn(struct tasklet_struct * t)479fcb8e3a3SAllen Pais static void smc_wr_rx_tasklet_fn(struct tasklet_struct *t)
480f38ba179SUrsula Braun {
481fcb8e3a3SAllen Pais struct smc_ib_device *dev = from_tasklet(dev, t, recv_tasklet);
482f38ba179SUrsula Braun struct ib_wc wc[SMC_WR_MAX_POLL_CQE];
483f38ba179SUrsula Braun int polled = 0;
484f38ba179SUrsula Braun int rc;
485f38ba179SUrsula Braun
486f38ba179SUrsula Braun again:
487f38ba179SUrsula Braun polled++;
488f38ba179SUrsula Braun do {
489f38ba179SUrsula Braun memset(&wc, 0, sizeof(wc));
490f38ba179SUrsula Braun rc = ib_poll_cq(dev->roce_cq_recv, SMC_WR_MAX_POLL_CQE, wc);
491f38ba179SUrsula Braun if (polled == 1) {
492f38ba179SUrsula Braun ib_req_notify_cq(dev->roce_cq_recv,
493f38ba179SUrsula Braun IB_CQ_SOLICITED_MASK
494f38ba179SUrsula Braun | IB_CQ_REPORT_MISSED_EVENTS);
495f38ba179SUrsula Braun }
496f38ba179SUrsula Braun if (!rc)
497f38ba179SUrsula Braun break;
498f38ba179SUrsula Braun smc_wr_rx_process_cqes(&wc[0], rc);
499f38ba179SUrsula Braun } while (rc > 0);
500f38ba179SUrsula Braun if (polled == 1)
501f38ba179SUrsula Braun goto again;
502f38ba179SUrsula Braun }
503f38ba179SUrsula Braun
smc_wr_rx_cq_handler(struct ib_cq * ib_cq,void * cq_context)504f38ba179SUrsula Braun void smc_wr_rx_cq_handler(struct ib_cq *ib_cq, void *cq_context)
505f38ba179SUrsula Braun {
506f38ba179SUrsula Braun struct smc_ib_device *dev = (struct smc_ib_device *)cq_context;
507f38ba179SUrsula Braun
508f38ba179SUrsula Braun tasklet_schedule(&dev->recv_tasklet);
509f38ba179SUrsula Braun }
510f38ba179SUrsula Braun
smc_wr_rx_post_init(struct smc_link * link)511f38ba179SUrsula Braun int smc_wr_rx_post_init(struct smc_link *link)
512f38ba179SUrsula Braun {
513f38ba179SUrsula Braun u32 i;
514f38ba179SUrsula Braun int rc = 0;
515f38ba179SUrsula Braun
516f38ba179SUrsula Braun for (i = 0; i < link->wr_rx_cnt; i++)
517f38ba179SUrsula Braun rc = smc_wr_rx_post(link);
518f38ba179SUrsula Braun return rc;
519f38ba179SUrsula Braun }
520f38ba179SUrsula Braun
521f38ba179SUrsula Braun /***************************** init, exit, misc ******************************/
522f38ba179SUrsula Braun
smc_wr_remember_qp_attr(struct smc_link * lnk)523f38ba179SUrsula Braun void smc_wr_remember_qp_attr(struct smc_link *lnk)
524f38ba179SUrsula Braun {
525f38ba179SUrsula Braun struct ib_qp_attr *attr = &lnk->qp_attr;
526f38ba179SUrsula Braun struct ib_qp_init_attr init_attr;
527f38ba179SUrsula Braun
528f38ba179SUrsula Braun memset(attr, 0, sizeof(*attr));
529f38ba179SUrsula Braun memset(&init_attr, 0, sizeof(init_attr));
530f38ba179SUrsula Braun ib_query_qp(lnk->roce_qp, attr,
531f38ba179SUrsula Braun IB_QP_STATE |
532f38ba179SUrsula Braun IB_QP_CUR_STATE |
533f38ba179SUrsula Braun IB_QP_PKEY_INDEX |
534f38ba179SUrsula Braun IB_QP_PORT |
535f38ba179SUrsula Braun IB_QP_QKEY |
536f38ba179SUrsula Braun IB_QP_AV |
537f38ba179SUrsula Braun IB_QP_PATH_MTU |
538f38ba179SUrsula Braun IB_QP_TIMEOUT |
539f38ba179SUrsula Braun IB_QP_RETRY_CNT |
540f38ba179SUrsula Braun IB_QP_RNR_RETRY |
541f38ba179SUrsula Braun IB_QP_RQ_PSN |
542f38ba179SUrsula Braun IB_QP_ALT_PATH |
543f38ba179SUrsula Braun IB_QP_MIN_RNR_TIMER |
544f38ba179SUrsula Braun IB_QP_SQ_PSN |
545f38ba179SUrsula Braun IB_QP_PATH_MIG_STATE |
546f38ba179SUrsula Braun IB_QP_CAP |
547f38ba179SUrsula Braun IB_QP_DEST_QPN,
548f38ba179SUrsula Braun &init_attr);
549f38ba179SUrsula Braun
550f38ba179SUrsula Braun lnk->wr_tx_cnt = min_t(size_t, SMC_WR_BUF_CNT,
551f38ba179SUrsula Braun lnk->qp_attr.cap.max_send_wr);
552f38ba179SUrsula Braun lnk->wr_rx_cnt = min_t(size_t, SMC_WR_BUF_CNT * 3,
553f38ba179SUrsula Braun lnk->qp_attr.cap.max_recv_wr);
554f38ba179SUrsula Braun }
555f38ba179SUrsula Braun
smc_wr_init_sge(struct smc_link * lnk)556f38ba179SUrsula Braun static void smc_wr_init_sge(struct smc_link *lnk)
557f38ba179SUrsula Braun {
5588799e310SKarsten Graul int sges_per_buf = (lnk->lgr->smc_version == SMC_V2) ? 2 : 1;
559b632eb06SGuangguan Wang bool send_inline = (lnk->qp_attr.cap.max_inline_data > SMC_WR_TX_SIZE);
560f38ba179SUrsula Braun u32 i;
561f38ba179SUrsula Braun
562f38ba179SUrsula Braun for (i = 0; i < lnk->wr_tx_cnt; i++) {
563b632eb06SGuangguan Wang lnk->wr_tx_sges[i].addr = send_inline ? (uintptr_t)(&lnk->wr_tx_bufs[i]) :
564f38ba179SUrsula Braun lnk->wr_tx_dma_addr + i * SMC_WR_BUF_SIZE;
565f38ba179SUrsula Braun lnk->wr_tx_sges[i].length = SMC_WR_TX_SIZE;
566bd4ad577SUrsula Braun lnk->wr_tx_sges[i].lkey = lnk->roce_pd->local_dma_lkey;
567ad6f317fSUrsula Braun lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[0].lkey =
568ad6f317fSUrsula Braun lnk->roce_pd->local_dma_lkey;
569ad6f317fSUrsula Braun lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[1].lkey =
570ad6f317fSUrsula Braun lnk->roce_pd->local_dma_lkey;
571ad6f317fSUrsula Braun lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[0].lkey =
572ad6f317fSUrsula Braun lnk->roce_pd->local_dma_lkey;
573ad6f317fSUrsula Braun lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[1].lkey =
574ad6f317fSUrsula Braun lnk->roce_pd->local_dma_lkey;
575f38ba179SUrsula Braun lnk->wr_tx_ibs[i].next = NULL;
576f38ba179SUrsula Braun lnk->wr_tx_ibs[i].sg_list = &lnk->wr_tx_sges[i];
577f38ba179SUrsula Braun lnk->wr_tx_ibs[i].num_sge = 1;
578f38ba179SUrsula Braun lnk->wr_tx_ibs[i].opcode = IB_WR_SEND;
579f38ba179SUrsula Braun lnk->wr_tx_ibs[i].send_flags =
5802c9c1682SUrsula Braun IB_SEND_SIGNALED | IB_SEND_SOLICITED;
581b632eb06SGuangguan Wang if (send_inline)
582b632eb06SGuangguan Wang lnk->wr_tx_ibs[i].send_flags |= IB_SEND_INLINE;
583ad6f317fSUrsula Braun lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.opcode = IB_WR_RDMA_WRITE;
584ad6f317fSUrsula Braun lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.opcode = IB_WR_RDMA_WRITE;
585ad6f317fSUrsula Braun lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.sg_list =
586ad6f317fSUrsula Braun lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge;
587ad6f317fSUrsula Braun lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.sg_list =
588ad6f317fSUrsula Braun lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge;
589f38ba179SUrsula Braun }
5908799e310SKarsten Graul
5918799e310SKarsten Graul if (lnk->lgr->smc_version == SMC_V2) {
5928799e310SKarsten Graul lnk->wr_tx_v2_sge->addr = lnk->wr_tx_v2_dma_addr;
5938799e310SKarsten Graul lnk->wr_tx_v2_sge->length = SMC_WR_BUF_V2_SIZE;
5948799e310SKarsten Graul lnk->wr_tx_v2_sge->lkey = lnk->roce_pd->local_dma_lkey;
5958799e310SKarsten Graul
5968799e310SKarsten Graul lnk->wr_tx_v2_ib->next = NULL;
5978799e310SKarsten Graul lnk->wr_tx_v2_ib->sg_list = lnk->wr_tx_v2_sge;
5988799e310SKarsten Graul lnk->wr_tx_v2_ib->num_sge = 1;
5998799e310SKarsten Graul lnk->wr_tx_v2_ib->opcode = IB_WR_SEND;
6008799e310SKarsten Graul lnk->wr_tx_v2_ib->send_flags =
6018799e310SKarsten Graul IB_SEND_SIGNALED | IB_SEND_SOLICITED;
6028799e310SKarsten Graul }
6038799e310SKarsten Graul
6048799e310SKarsten Graul /* With SMC-Rv2 there can be messages larger than SMC_WR_TX_SIZE.
6058799e310SKarsten Graul * Each ib_recv_wr gets 2 sges, the second one is a spillover buffer
6068799e310SKarsten Graul * and the same buffer for all sges. When a larger message arrived then
6078799e310SKarsten Graul * the content of the first small sge is copied to the beginning of
6088799e310SKarsten Graul * the larger spillover buffer, allowing easy data mapping.
6098799e310SKarsten Graul */
610f38ba179SUrsula Braun for (i = 0; i < lnk->wr_rx_cnt; i++) {
6118799e310SKarsten Graul int x = i * sges_per_buf;
6128799e310SKarsten Graul
6138799e310SKarsten Graul lnk->wr_rx_sges[x].addr =
614f38ba179SUrsula Braun lnk->wr_rx_dma_addr + i * SMC_WR_BUF_SIZE;
6158799e310SKarsten Graul lnk->wr_rx_sges[x].length = SMC_WR_TX_SIZE;
6168799e310SKarsten Graul lnk->wr_rx_sges[x].lkey = lnk->roce_pd->local_dma_lkey;
6178799e310SKarsten Graul if (lnk->lgr->smc_version == SMC_V2) {
6188799e310SKarsten Graul lnk->wr_rx_sges[x + 1].addr =
6198799e310SKarsten Graul lnk->wr_rx_v2_dma_addr + SMC_WR_TX_SIZE;
6208799e310SKarsten Graul lnk->wr_rx_sges[x + 1].length =
6218799e310SKarsten Graul SMC_WR_BUF_V2_SIZE - SMC_WR_TX_SIZE;
6228799e310SKarsten Graul lnk->wr_rx_sges[x + 1].lkey =
6238799e310SKarsten Graul lnk->roce_pd->local_dma_lkey;
6248799e310SKarsten Graul }
625f38ba179SUrsula Braun lnk->wr_rx_ibs[i].next = NULL;
6268799e310SKarsten Graul lnk->wr_rx_ibs[i].sg_list = &lnk->wr_rx_sges[x];
6278799e310SKarsten Graul lnk->wr_rx_ibs[i].num_sge = sges_per_buf;
628f38ba179SUrsula Braun }
629652a1e41SUrsula Braun lnk->wr_reg.wr.next = NULL;
630652a1e41SUrsula Braun lnk->wr_reg.wr.num_sge = 0;
631652a1e41SUrsula Braun lnk->wr_reg.wr.send_flags = IB_SEND_SIGNALED;
632652a1e41SUrsula Braun lnk->wr_reg.wr.opcode = IB_WR_REG_MR;
633652a1e41SUrsula Braun lnk->wr_reg.access = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE;
634f38ba179SUrsula Braun }
635f38ba179SUrsula Braun
smc_wr_free_link(struct smc_link * lnk)636f38ba179SUrsula Braun void smc_wr_free_link(struct smc_link *lnk)
637f38ba179SUrsula Braun {
638f38ba179SUrsula Braun struct ib_device *ibdev;
639f38ba179SUrsula Braun
640b7eede75SKarsten Graul if (!lnk->smcibdev)
641b7eede75SKarsten Graul return;
642b7eede75SKarsten Graul ibdev = lnk->smcibdev->ibdev;
643b7eede75SKarsten Graul
644e9b1a4f8SYacan Liu smc_wr_drain_cq(lnk);
6458f3d65c1SKarsten Graul smc_wr_wakeup_reg_wait(lnk);
6468f3d65c1SKarsten Graul smc_wr_wakeup_tx_wait(lnk);
6478f3d65c1SKarsten Graul
648349d4312SDust Li smc_wr_tx_wait_no_pending_sends(lnk);
64979a22238SKai Shen percpu_ref_kill(&lnk->wr_reg_refs);
65079a22238SKai Shen wait_for_completion(&lnk->reg_ref_comp);
651*a17874a3SKai Shen percpu_ref_exit(&lnk->wr_reg_refs);
65279a22238SKai Shen percpu_ref_kill(&lnk->wr_tx_refs);
65379a22238SKai Shen wait_for_completion(&lnk->tx_ref_comp);
654*a17874a3SKai Shen percpu_ref_exit(&lnk->wr_tx_refs);
655f38ba179SUrsula Braun
656f38ba179SUrsula Braun if (lnk->wr_rx_dma_addr) {
657f38ba179SUrsula Braun ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr,
658f38ba179SUrsula Braun SMC_WR_BUF_SIZE * lnk->wr_rx_cnt,
659f38ba179SUrsula Braun DMA_FROM_DEVICE);
660f38ba179SUrsula Braun lnk->wr_rx_dma_addr = 0;
661f38ba179SUrsula Braun }
6628799e310SKarsten Graul if (lnk->wr_rx_v2_dma_addr) {
6638799e310SKarsten Graul ib_dma_unmap_single(ibdev, lnk->wr_rx_v2_dma_addr,
6648799e310SKarsten Graul SMC_WR_BUF_V2_SIZE,
6658799e310SKarsten Graul DMA_FROM_DEVICE);
6668799e310SKarsten Graul lnk->wr_rx_v2_dma_addr = 0;
6678799e310SKarsten Graul }
668f38ba179SUrsula Braun if (lnk->wr_tx_dma_addr) {
669f38ba179SUrsula Braun ib_dma_unmap_single(ibdev, lnk->wr_tx_dma_addr,
670f38ba179SUrsula Braun SMC_WR_BUF_SIZE * lnk->wr_tx_cnt,
671f38ba179SUrsula Braun DMA_TO_DEVICE);
672f38ba179SUrsula Braun lnk->wr_tx_dma_addr = 0;
673f38ba179SUrsula Braun }
6748799e310SKarsten Graul if (lnk->wr_tx_v2_dma_addr) {
6758799e310SKarsten Graul ib_dma_unmap_single(ibdev, lnk->wr_tx_v2_dma_addr,
6768799e310SKarsten Graul SMC_WR_BUF_V2_SIZE,
6778799e310SKarsten Graul DMA_TO_DEVICE);
6788799e310SKarsten Graul lnk->wr_tx_v2_dma_addr = 0;
6798799e310SKarsten Graul }
6808799e310SKarsten Graul }
6818799e310SKarsten Graul
smc_wr_free_lgr_mem(struct smc_link_group * lgr)6828799e310SKarsten Graul void smc_wr_free_lgr_mem(struct smc_link_group *lgr)
6838799e310SKarsten Graul {
6848799e310SKarsten Graul if (lgr->smc_version < SMC_V2)
6858799e310SKarsten Graul return;
6868799e310SKarsten Graul
6878799e310SKarsten Graul kfree(lgr->wr_rx_buf_v2);
6888799e310SKarsten Graul lgr->wr_rx_buf_v2 = NULL;
6898799e310SKarsten Graul kfree(lgr->wr_tx_buf_v2);
6908799e310SKarsten Graul lgr->wr_tx_buf_v2 = NULL;
691f38ba179SUrsula Braun }
692f38ba179SUrsula Braun
smc_wr_free_link_mem(struct smc_link * lnk)693f38ba179SUrsula Braun void smc_wr_free_link_mem(struct smc_link *lnk)
694f38ba179SUrsula Braun {
6958799e310SKarsten Graul kfree(lnk->wr_tx_v2_ib);
6968799e310SKarsten Graul lnk->wr_tx_v2_ib = NULL;
6978799e310SKarsten Graul kfree(lnk->wr_tx_v2_sge);
6988799e310SKarsten Graul lnk->wr_tx_v2_sge = NULL;
6998799e310SKarsten Graul kfree(lnk->wr_tx_v2_pend);
7008799e310SKarsten Graul lnk->wr_tx_v2_pend = NULL;
70109c61d24SKarsten Graul kfree(lnk->wr_tx_compl);
70209c61d24SKarsten Graul lnk->wr_tx_compl = NULL;
703f38ba179SUrsula Braun kfree(lnk->wr_tx_pends);
704f38ba179SUrsula Braun lnk->wr_tx_pends = NULL;
70549dc9013SChristophe JAILLET bitmap_free(lnk->wr_tx_mask);
706f38ba179SUrsula Braun lnk->wr_tx_mask = NULL;
707f38ba179SUrsula Braun kfree(lnk->wr_tx_sges);
708f38ba179SUrsula Braun lnk->wr_tx_sges = NULL;
709ad6f317fSUrsula Braun kfree(lnk->wr_tx_rdma_sges);
710ad6f317fSUrsula Braun lnk->wr_tx_rdma_sges = NULL;
711f38ba179SUrsula Braun kfree(lnk->wr_rx_sges);
712f38ba179SUrsula Braun lnk->wr_rx_sges = NULL;
713ad6f317fSUrsula Braun kfree(lnk->wr_tx_rdmas);
714ad6f317fSUrsula Braun lnk->wr_tx_rdmas = NULL;
715f38ba179SUrsula Braun kfree(lnk->wr_rx_ibs);
716f38ba179SUrsula Braun lnk->wr_rx_ibs = NULL;
717f38ba179SUrsula Braun kfree(lnk->wr_tx_ibs);
718f38ba179SUrsula Braun lnk->wr_tx_ibs = NULL;
719f38ba179SUrsula Braun kfree(lnk->wr_tx_bufs);
720f38ba179SUrsula Braun lnk->wr_tx_bufs = NULL;
721f38ba179SUrsula Braun kfree(lnk->wr_rx_bufs);
722f38ba179SUrsula Braun lnk->wr_rx_bufs = NULL;
723f38ba179SUrsula Braun }
724f38ba179SUrsula Braun
smc_wr_alloc_lgr_mem(struct smc_link_group * lgr)7258799e310SKarsten Graul int smc_wr_alloc_lgr_mem(struct smc_link_group *lgr)
7268799e310SKarsten Graul {
7278799e310SKarsten Graul if (lgr->smc_version < SMC_V2)
7288799e310SKarsten Graul return 0;
7298799e310SKarsten Graul
7308799e310SKarsten Graul lgr->wr_rx_buf_v2 = kzalloc(SMC_WR_BUF_V2_SIZE, GFP_KERNEL);
7318799e310SKarsten Graul if (!lgr->wr_rx_buf_v2)
7328799e310SKarsten Graul return -ENOMEM;
7338799e310SKarsten Graul lgr->wr_tx_buf_v2 = kzalloc(SMC_WR_BUF_V2_SIZE, GFP_KERNEL);
7348799e310SKarsten Graul if (!lgr->wr_tx_buf_v2) {
7358799e310SKarsten Graul kfree(lgr->wr_rx_buf_v2);
7368799e310SKarsten Graul return -ENOMEM;
7378799e310SKarsten Graul }
7388799e310SKarsten Graul return 0;
7398799e310SKarsten Graul }
7408799e310SKarsten Graul
smc_wr_alloc_link_mem(struct smc_link * link)741f38ba179SUrsula Braun int smc_wr_alloc_link_mem(struct smc_link *link)
742f38ba179SUrsula Braun {
7438799e310SKarsten Graul int sges_per_buf = link->lgr->smc_version == SMC_V2 ? 2 : 1;
7448799e310SKarsten Graul
745f38ba179SUrsula Braun /* allocate link related memory */
746f38ba179SUrsula Braun link->wr_tx_bufs = kcalloc(SMC_WR_BUF_CNT, SMC_WR_BUF_SIZE, GFP_KERNEL);
747f38ba179SUrsula Braun if (!link->wr_tx_bufs)
748f38ba179SUrsula Braun goto no_mem;
749f38ba179SUrsula Braun link->wr_rx_bufs = kcalloc(SMC_WR_BUF_CNT * 3, SMC_WR_BUF_SIZE,
750f38ba179SUrsula Braun GFP_KERNEL);
751f38ba179SUrsula Braun if (!link->wr_rx_bufs)
752f38ba179SUrsula Braun goto no_mem_wr_tx_bufs;
753f38ba179SUrsula Braun link->wr_tx_ibs = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_ibs[0]),
754f38ba179SUrsula Braun GFP_KERNEL);
755f38ba179SUrsula Braun if (!link->wr_tx_ibs)
756f38ba179SUrsula Braun goto no_mem_wr_rx_bufs;
757f38ba179SUrsula Braun link->wr_rx_ibs = kcalloc(SMC_WR_BUF_CNT * 3,
758f38ba179SUrsula Braun sizeof(link->wr_rx_ibs[0]),
759f38ba179SUrsula Braun GFP_KERNEL);
760f38ba179SUrsula Braun if (!link->wr_rx_ibs)
761f38ba179SUrsula Braun goto no_mem_wr_tx_ibs;
762ad6f317fSUrsula Braun link->wr_tx_rdmas = kcalloc(SMC_WR_BUF_CNT,
763ad6f317fSUrsula Braun sizeof(link->wr_tx_rdmas[0]),
764ad6f317fSUrsula Braun GFP_KERNEL);
765ad6f317fSUrsula Braun if (!link->wr_tx_rdmas)
766ad6f317fSUrsula Braun goto no_mem_wr_rx_ibs;
767ad6f317fSUrsula Braun link->wr_tx_rdma_sges = kcalloc(SMC_WR_BUF_CNT,
768ad6f317fSUrsula Braun sizeof(link->wr_tx_rdma_sges[0]),
769ad6f317fSUrsula Braun GFP_KERNEL);
770ad6f317fSUrsula Braun if (!link->wr_tx_rdma_sges)
771ad6f317fSUrsula Braun goto no_mem_wr_tx_rdmas;
772f38ba179SUrsula Braun link->wr_tx_sges = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_sges[0]),
773f38ba179SUrsula Braun GFP_KERNEL);
774f38ba179SUrsula Braun if (!link->wr_tx_sges)
775ad6f317fSUrsula Braun goto no_mem_wr_tx_rdma_sges;
776f38ba179SUrsula Braun link->wr_rx_sges = kcalloc(SMC_WR_BUF_CNT * 3,
7778799e310SKarsten Graul sizeof(link->wr_rx_sges[0]) * sges_per_buf,
778f38ba179SUrsula Braun GFP_KERNEL);
779f38ba179SUrsula Braun if (!link->wr_rx_sges)
780f38ba179SUrsula Braun goto no_mem_wr_tx_sges;
78149dc9013SChristophe JAILLET link->wr_tx_mask = bitmap_zalloc(SMC_WR_BUF_CNT, GFP_KERNEL);
782f38ba179SUrsula Braun if (!link->wr_tx_mask)
783f38ba179SUrsula Braun goto no_mem_wr_rx_sges;
784f38ba179SUrsula Braun link->wr_tx_pends = kcalloc(SMC_WR_BUF_CNT,
785f38ba179SUrsula Braun sizeof(link->wr_tx_pends[0]),
786f38ba179SUrsula Braun GFP_KERNEL);
787f38ba179SUrsula Braun if (!link->wr_tx_pends)
788f38ba179SUrsula Braun goto no_mem_wr_tx_mask;
78909c61d24SKarsten Graul link->wr_tx_compl = kcalloc(SMC_WR_BUF_CNT,
79009c61d24SKarsten Graul sizeof(link->wr_tx_compl[0]),
79109c61d24SKarsten Graul GFP_KERNEL);
79209c61d24SKarsten Graul if (!link->wr_tx_compl)
79309c61d24SKarsten Graul goto no_mem_wr_tx_pends;
7948799e310SKarsten Graul
7958799e310SKarsten Graul if (link->lgr->smc_version == SMC_V2) {
7968799e310SKarsten Graul link->wr_tx_v2_ib = kzalloc(sizeof(*link->wr_tx_v2_ib),
7978799e310SKarsten Graul GFP_KERNEL);
7988799e310SKarsten Graul if (!link->wr_tx_v2_ib)
7998799e310SKarsten Graul goto no_mem_tx_compl;
8008799e310SKarsten Graul link->wr_tx_v2_sge = kzalloc(sizeof(*link->wr_tx_v2_sge),
8018799e310SKarsten Graul GFP_KERNEL);
8028799e310SKarsten Graul if (!link->wr_tx_v2_sge)
8038799e310SKarsten Graul goto no_mem_v2_ib;
8048799e310SKarsten Graul link->wr_tx_v2_pend = kzalloc(sizeof(*link->wr_tx_v2_pend),
8058799e310SKarsten Graul GFP_KERNEL);
8068799e310SKarsten Graul if (!link->wr_tx_v2_pend)
8078799e310SKarsten Graul goto no_mem_v2_sge;
8088799e310SKarsten Graul }
809f38ba179SUrsula Braun return 0;
810f38ba179SUrsula Braun
8118799e310SKarsten Graul no_mem_v2_sge:
8128799e310SKarsten Graul kfree(link->wr_tx_v2_sge);
8138799e310SKarsten Graul no_mem_v2_ib:
8148799e310SKarsten Graul kfree(link->wr_tx_v2_ib);
8158799e310SKarsten Graul no_mem_tx_compl:
8168799e310SKarsten Graul kfree(link->wr_tx_compl);
81709c61d24SKarsten Graul no_mem_wr_tx_pends:
81809c61d24SKarsten Graul kfree(link->wr_tx_pends);
819f38ba179SUrsula Braun no_mem_wr_tx_mask:
820f38ba179SUrsula Braun kfree(link->wr_tx_mask);
821f38ba179SUrsula Braun no_mem_wr_rx_sges:
822f38ba179SUrsula Braun kfree(link->wr_rx_sges);
823f38ba179SUrsula Braun no_mem_wr_tx_sges:
824f38ba179SUrsula Braun kfree(link->wr_tx_sges);
825ad6f317fSUrsula Braun no_mem_wr_tx_rdma_sges:
826ad6f317fSUrsula Braun kfree(link->wr_tx_rdma_sges);
827ad6f317fSUrsula Braun no_mem_wr_tx_rdmas:
828ad6f317fSUrsula Braun kfree(link->wr_tx_rdmas);
829f38ba179SUrsula Braun no_mem_wr_rx_ibs:
830f38ba179SUrsula Braun kfree(link->wr_rx_ibs);
831f38ba179SUrsula Braun no_mem_wr_tx_ibs:
832f38ba179SUrsula Braun kfree(link->wr_tx_ibs);
833f38ba179SUrsula Braun no_mem_wr_rx_bufs:
834f38ba179SUrsula Braun kfree(link->wr_rx_bufs);
835f38ba179SUrsula Braun no_mem_wr_tx_bufs:
836f38ba179SUrsula Braun kfree(link->wr_tx_bufs);
837f38ba179SUrsula Braun no_mem:
838f38ba179SUrsula Braun return -ENOMEM;
839f38ba179SUrsula Braun }
840f38ba179SUrsula Braun
smc_wr_remove_dev(struct smc_ib_device * smcibdev)841f38ba179SUrsula Braun void smc_wr_remove_dev(struct smc_ib_device *smcibdev)
842f38ba179SUrsula Braun {
843f38ba179SUrsula Braun tasklet_kill(&smcibdev->recv_tasklet);
844f38ba179SUrsula Braun tasklet_kill(&smcibdev->send_tasklet);
845f38ba179SUrsula Braun }
846f38ba179SUrsula Braun
smc_wr_add_dev(struct smc_ib_device * smcibdev)847f38ba179SUrsula Braun void smc_wr_add_dev(struct smc_ib_device *smcibdev)
848f38ba179SUrsula Braun {
849fcb8e3a3SAllen Pais tasklet_setup(&smcibdev->recv_tasklet, smc_wr_rx_tasklet_fn);
850fcb8e3a3SAllen Pais tasklet_setup(&smcibdev->send_tasklet, smc_wr_tx_tasklet_fn);
851f38ba179SUrsula Braun }
852f38ba179SUrsula Braun
smcr_wr_tx_refs_free(struct percpu_ref * ref)85379a22238SKai Shen static void smcr_wr_tx_refs_free(struct percpu_ref *ref)
85479a22238SKai Shen {
85579a22238SKai Shen struct smc_link *lnk = container_of(ref, struct smc_link, wr_tx_refs);
85679a22238SKai Shen
85779a22238SKai Shen complete(&lnk->tx_ref_comp);
85879a22238SKai Shen }
85979a22238SKai Shen
smcr_wr_reg_refs_free(struct percpu_ref * ref)86079a22238SKai Shen static void smcr_wr_reg_refs_free(struct percpu_ref *ref)
86179a22238SKai Shen {
86279a22238SKai Shen struct smc_link *lnk = container_of(ref, struct smc_link, wr_reg_refs);
86379a22238SKai Shen
86479a22238SKai Shen complete(&lnk->reg_ref_comp);
86579a22238SKai Shen }
86679a22238SKai Shen
smc_wr_create_link(struct smc_link * lnk)867f38ba179SUrsula Braun int smc_wr_create_link(struct smc_link *lnk)
868f38ba179SUrsula Braun {
869f38ba179SUrsula Braun struct ib_device *ibdev = lnk->smcibdev->ibdev;
870f38ba179SUrsula Braun int rc = 0;
871f38ba179SUrsula Braun
872f38ba179SUrsula Braun smc_wr_tx_set_wr_id(&lnk->wr_tx_id, 0);
873f38ba179SUrsula Braun lnk->wr_rx_id = 0;
874f38ba179SUrsula Braun lnk->wr_rx_dma_addr = ib_dma_map_single(
875f38ba179SUrsula Braun ibdev, lnk->wr_rx_bufs, SMC_WR_BUF_SIZE * lnk->wr_rx_cnt,
876f38ba179SUrsula Braun DMA_FROM_DEVICE);
877f38ba179SUrsula Braun if (ib_dma_mapping_error(ibdev, lnk->wr_rx_dma_addr)) {
878f38ba179SUrsula Braun lnk->wr_rx_dma_addr = 0;
879f38ba179SUrsula Braun rc = -EIO;
880f38ba179SUrsula Braun goto out;
881f38ba179SUrsula Braun }
8828799e310SKarsten Graul if (lnk->lgr->smc_version == SMC_V2) {
8838799e310SKarsten Graul lnk->wr_rx_v2_dma_addr = ib_dma_map_single(ibdev,
8848799e310SKarsten Graul lnk->lgr->wr_rx_buf_v2, SMC_WR_BUF_V2_SIZE,
8858799e310SKarsten Graul DMA_FROM_DEVICE);
8868799e310SKarsten Graul if (ib_dma_mapping_error(ibdev, lnk->wr_rx_v2_dma_addr)) {
8878799e310SKarsten Graul lnk->wr_rx_v2_dma_addr = 0;
8888799e310SKarsten Graul rc = -EIO;
8898799e310SKarsten Graul goto dma_unmap;
8908799e310SKarsten Graul }
8918799e310SKarsten Graul lnk->wr_tx_v2_dma_addr = ib_dma_map_single(ibdev,
8928799e310SKarsten Graul lnk->lgr->wr_tx_buf_v2, SMC_WR_BUF_V2_SIZE,
8938799e310SKarsten Graul DMA_TO_DEVICE);
8948799e310SKarsten Graul if (ib_dma_mapping_error(ibdev, lnk->wr_tx_v2_dma_addr)) {
8958799e310SKarsten Graul lnk->wr_tx_v2_dma_addr = 0;
8968799e310SKarsten Graul rc = -EIO;
8978799e310SKarsten Graul goto dma_unmap;
8988799e310SKarsten Graul }
8998799e310SKarsten Graul }
900f38ba179SUrsula Braun lnk->wr_tx_dma_addr = ib_dma_map_single(
901f38ba179SUrsula Braun ibdev, lnk->wr_tx_bufs, SMC_WR_BUF_SIZE * lnk->wr_tx_cnt,
902f38ba179SUrsula Braun DMA_TO_DEVICE);
903f38ba179SUrsula Braun if (ib_dma_mapping_error(ibdev, lnk->wr_tx_dma_addr)) {
904f38ba179SUrsula Braun rc = -EIO;
905f38ba179SUrsula Braun goto dma_unmap;
906f38ba179SUrsula Braun }
907f38ba179SUrsula Braun smc_wr_init_sge(lnk);
90849dc9013SChristophe JAILLET bitmap_zero(lnk->wr_tx_mask, SMC_WR_BUF_CNT);
909652a1e41SUrsula Braun init_waitqueue_head(&lnk->wr_tx_wait);
91079a22238SKai Shen rc = percpu_ref_init(&lnk->wr_tx_refs, smcr_wr_tx_refs_free, 0, GFP_KERNEL);
91179a22238SKai Shen if (rc)
91279a22238SKai Shen goto dma_unmap;
91379a22238SKai Shen init_completion(&lnk->tx_ref_comp);
914652a1e41SUrsula Braun init_waitqueue_head(&lnk->wr_reg_wait);
91579a22238SKai Shen rc = percpu_ref_init(&lnk->wr_reg_refs, smcr_wr_reg_refs_free, 0, GFP_KERNEL);
91679a22238SKai Shen if (rc)
917*a17874a3SKai Shen goto cancel_ref;
91879a22238SKai Shen init_completion(&lnk->reg_ref_comp);
919e9b1a4f8SYacan Liu init_waitqueue_head(&lnk->wr_rx_empty_wait);
920f38ba179SUrsula Braun return rc;
921f38ba179SUrsula Braun
922*a17874a3SKai Shen cancel_ref:
923*a17874a3SKai Shen percpu_ref_exit(&lnk->wr_tx_refs);
924f38ba179SUrsula Braun dma_unmap:
9258799e310SKarsten Graul if (lnk->wr_rx_v2_dma_addr) {
9268799e310SKarsten Graul ib_dma_unmap_single(ibdev, lnk->wr_rx_v2_dma_addr,
9278799e310SKarsten Graul SMC_WR_BUF_V2_SIZE,
9288799e310SKarsten Graul DMA_FROM_DEVICE);
9298799e310SKarsten Graul lnk->wr_rx_v2_dma_addr = 0;
9308799e310SKarsten Graul }
9318799e310SKarsten Graul if (lnk->wr_tx_v2_dma_addr) {
9328799e310SKarsten Graul ib_dma_unmap_single(ibdev, lnk->wr_tx_v2_dma_addr,
9338799e310SKarsten Graul SMC_WR_BUF_V2_SIZE,
9348799e310SKarsten Graul DMA_TO_DEVICE);
9358799e310SKarsten Graul lnk->wr_tx_v2_dma_addr = 0;
9368799e310SKarsten Graul }
937f38ba179SUrsula Braun ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr,
938f38ba179SUrsula Braun SMC_WR_BUF_SIZE * lnk->wr_rx_cnt,
939f38ba179SUrsula Braun DMA_FROM_DEVICE);
940f38ba179SUrsula Braun lnk->wr_rx_dma_addr = 0;
941f38ba179SUrsula Braun out:
942f38ba179SUrsula Braun return rc;
943f38ba179SUrsula Braun }
944