xref: /openbmc/linux/net/smc/smc_wr.c (revision 79a22238)
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 
380*79a22238SKai 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);
384*79a22238SKai 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);
649*79a22238SKai Shen 	percpu_ref_kill(&lnk->wr_reg_refs);
650*79a22238SKai Shen 	wait_for_completion(&lnk->reg_ref_comp);
651*79a22238SKai Shen 	percpu_ref_kill(&lnk->wr_tx_refs);
652*79a22238SKai Shen 	wait_for_completion(&lnk->tx_ref_comp);
653f38ba179SUrsula Braun 
654f38ba179SUrsula Braun 	if (lnk->wr_rx_dma_addr) {
655f38ba179SUrsula Braun 		ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr,
656f38ba179SUrsula Braun 				    SMC_WR_BUF_SIZE * lnk->wr_rx_cnt,
657f38ba179SUrsula Braun 				    DMA_FROM_DEVICE);
658f38ba179SUrsula Braun 		lnk->wr_rx_dma_addr = 0;
659f38ba179SUrsula Braun 	}
6608799e310SKarsten Graul 	if (lnk->wr_rx_v2_dma_addr) {
6618799e310SKarsten Graul 		ib_dma_unmap_single(ibdev, lnk->wr_rx_v2_dma_addr,
6628799e310SKarsten Graul 				    SMC_WR_BUF_V2_SIZE,
6638799e310SKarsten Graul 				    DMA_FROM_DEVICE);
6648799e310SKarsten Graul 		lnk->wr_rx_v2_dma_addr = 0;
6658799e310SKarsten Graul 	}
666f38ba179SUrsula Braun 	if (lnk->wr_tx_dma_addr) {
667f38ba179SUrsula Braun 		ib_dma_unmap_single(ibdev, lnk->wr_tx_dma_addr,
668f38ba179SUrsula Braun 				    SMC_WR_BUF_SIZE * lnk->wr_tx_cnt,
669f38ba179SUrsula Braun 				    DMA_TO_DEVICE);
670f38ba179SUrsula Braun 		lnk->wr_tx_dma_addr = 0;
671f38ba179SUrsula Braun 	}
6728799e310SKarsten Graul 	if (lnk->wr_tx_v2_dma_addr) {
6738799e310SKarsten Graul 		ib_dma_unmap_single(ibdev, lnk->wr_tx_v2_dma_addr,
6748799e310SKarsten Graul 				    SMC_WR_BUF_V2_SIZE,
6758799e310SKarsten Graul 				    DMA_TO_DEVICE);
6768799e310SKarsten Graul 		lnk->wr_tx_v2_dma_addr = 0;
6778799e310SKarsten Graul 	}
6788799e310SKarsten Graul }
6798799e310SKarsten Graul 
smc_wr_free_lgr_mem(struct smc_link_group * lgr)6808799e310SKarsten Graul void smc_wr_free_lgr_mem(struct smc_link_group *lgr)
6818799e310SKarsten Graul {
6828799e310SKarsten Graul 	if (lgr->smc_version < SMC_V2)
6838799e310SKarsten Graul 		return;
6848799e310SKarsten Graul 
6858799e310SKarsten Graul 	kfree(lgr->wr_rx_buf_v2);
6868799e310SKarsten Graul 	lgr->wr_rx_buf_v2 = NULL;
6878799e310SKarsten Graul 	kfree(lgr->wr_tx_buf_v2);
6888799e310SKarsten Graul 	lgr->wr_tx_buf_v2 = NULL;
689f38ba179SUrsula Braun }
690f38ba179SUrsula Braun 
smc_wr_free_link_mem(struct smc_link * lnk)691f38ba179SUrsula Braun void smc_wr_free_link_mem(struct smc_link *lnk)
692f38ba179SUrsula Braun {
6938799e310SKarsten Graul 	kfree(lnk->wr_tx_v2_ib);
6948799e310SKarsten Graul 	lnk->wr_tx_v2_ib = NULL;
6958799e310SKarsten Graul 	kfree(lnk->wr_tx_v2_sge);
6968799e310SKarsten Graul 	lnk->wr_tx_v2_sge = NULL;
6978799e310SKarsten Graul 	kfree(lnk->wr_tx_v2_pend);
6988799e310SKarsten Graul 	lnk->wr_tx_v2_pend = NULL;
69909c61d24SKarsten Graul 	kfree(lnk->wr_tx_compl);
70009c61d24SKarsten Graul 	lnk->wr_tx_compl = NULL;
701f38ba179SUrsula Braun 	kfree(lnk->wr_tx_pends);
702f38ba179SUrsula Braun 	lnk->wr_tx_pends = NULL;
70349dc9013SChristophe JAILLET 	bitmap_free(lnk->wr_tx_mask);
704f38ba179SUrsula Braun 	lnk->wr_tx_mask = NULL;
705f38ba179SUrsula Braun 	kfree(lnk->wr_tx_sges);
706f38ba179SUrsula Braun 	lnk->wr_tx_sges = NULL;
707ad6f317fSUrsula Braun 	kfree(lnk->wr_tx_rdma_sges);
708ad6f317fSUrsula Braun 	lnk->wr_tx_rdma_sges = NULL;
709f38ba179SUrsula Braun 	kfree(lnk->wr_rx_sges);
710f38ba179SUrsula Braun 	lnk->wr_rx_sges = NULL;
711ad6f317fSUrsula Braun 	kfree(lnk->wr_tx_rdmas);
712ad6f317fSUrsula Braun 	lnk->wr_tx_rdmas = NULL;
713f38ba179SUrsula Braun 	kfree(lnk->wr_rx_ibs);
714f38ba179SUrsula Braun 	lnk->wr_rx_ibs = NULL;
715f38ba179SUrsula Braun 	kfree(lnk->wr_tx_ibs);
716f38ba179SUrsula Braun 	lnk->wr_tx_ibs = NULL;
717f38ba179SUrsula Braun 	kfree(lnk->wr_tx_bufs);
718f38ba179SUrsula Braun 	lnk->wr_tx_bufs = NULL;
719f38ba179SUrsula Braun 	kfree(lnk->wr_rx_bufs);
720f38ba179SUrsula Braun 	lnk->wr_rx_bufs = NULL;
721f38ba179SUrsula Braun }
722f38ba179SUrsula Braun 
smc_wr_alloc_lgr_mem(struct smc_link_group * lgr)7238799e310SKarsten Graul int smc_wr_alloc_lgr_mem(struct smc_link_group *lgr)
7248799e310SKarsten Graul {
7258799e310SKarsten Graul 	if (lgr->smc_version < SMC_V2)
7268799e310SKarsten Graul 		return 0;
7278799e310SKarsten Graul 
7288799e310SKarsten Graul 	lgr->wr_rx_buf_v2 = kzalloc(SMC_WR_BUF_V2_SIZE, GFP_KERNEL);
7298799e310SKarsten Graul 	if (!lgr->wr_rx_buf_v2)
7308799e310SKarsten Graul 		return -ENOMEM;
7318799e310SKarsten Graul 	lgr->wr_tx_buf_v2 = kzalloc(SMC_WR_BUF_V2_SIZE, GFP_KERNEL);
7328799e310SKarsten Graul 	if (!lgr->wr_tx_buf_v2) {
7338799e310SKarsten Graul 		kfree(lgr->wr_rx_buf_v2);
7348799e310SKarsten Graul 		return -ENOMEM;
7358799e310SKarsten Graul 	}
7368799e310SKarsten Graul 	return 0;
7378799e310SKarsten Graul }
7388799e310SKarsten Graul 
smc_wr_alloc_link_mem(struct smc_link * link)739f38ba179SUrsula Braun int smc_wr_alloc_link_mem(struct smc_link *link)
740f38ba179SUrsula Braun {
7418799e310SKarsten Graul 	int sges_per_buf = link->lgr->smc_version == SMC_V2 ? 2 : 1;
7428799e310SKarsten Graul 
743f38ba179SUrsula Braun 	/* allocate link related memory */
744f38ba179SUrsula Braun 	link->wr_tx_bufs = kcalloc(SMC_WR_BUF_CNT, SMC_WR_BUF_SIZE, GFP_KERNEL);
745f38ba179SUrsula Braun 	if (!link->wr_tx_bufs)
746f38ba179SUrsula Braun 		goto no_mem;
747f38ba179SUrsula Braun 	link->wr_rx_bufs = kcalloc(SMC_WR_BUF_CNT * 3, SMC_WR_BUF_SIZE,
748f38ba179SUrsula Braun 				   GFP_KERNEL);
749f38ba179SUrsula Braun 	if (!link->wr_rx_bufs)
750f38ba179SUrsula Braun 		goto no_mem_wr_tx_bufs;
751f38ba179SUrsula Braun 	link->wr_tx_ibs = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_ibs[0]),
752f38ba179SUrsula Braun 				  GFP_KERNEL);
753f38ba179SUrsula Braun 	if (!link->wr_tx_ibs)
754f38ba179SUrsula Braun 		goto no_mem_wr_rx_bufs;
755f38ba179SUrsula Braun 	link->wr_rx_ibs = kcalloc(SMC_WR_BUF_CNT * 3,
756f38ba179SUrsula Braun 				  sizeof(link->wr_rx_ibs[0]),
757f38ba179SUrsula Braun 				  GFP_KERNEL);
758f38ba179SUrsula Braun 	if (!link->wr_rx_ibs)
759f38ba179SUrsula Braun 		goto no_mem_wr_tx_ibs;
760ad6f317fSUrsula Braun 	link->wr_tx_rdmas = kcalloc(SMC_WR_BUF_CNT,
761ad6f317fSUrsula Braun 				    sizeof(link->wr_tx_rdmas[0]),
762ad6f317fSUrsula Braun 				    GFP_KERNEL);
763ad6f317fSUrsula Braun 	if (!link->wr_tx_rdmas)
764ad6f317fSUrsula Braun 		goto no_mem_wr_rx_ibs;
765ad6f317fSUrsula Braun 	link->wr_tx_rdma_sges = kcalloc(SMC_WR_BUF_CNT,
766ad6f317fSUrsula Braun 					sizeof(link->wr_tx_rdma_sges[0]),
767ad6f317fSUrsula Braun 					GFP_KERNEL);
768ad6f317fSUrsula Braun 	if (!link->wr_tx_rdma_sges)
769ad6f317fSUrsula Braun 		goto no_mem_wr_tx_rdmas;
770f38ba179SUrsula Braun 	link->wr_tx_sges = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_sges[0]),
771f38ba179SUrsula Braun 				   GFP_KERNEL);
772f38ba179SUrsula Braun 	if (!link->wr_tx_sges)
773ad6f317fSUrsula Braun 		goto no_mem_wr_tx_rdma_sges;
774f38ba179SUrsula Braun 	link->wr_rx_sges = kcalloc(SMC_WR_BUF_CNT * 3,
7758799e310SKarsten Graul 				   sizeof(link->wr_rx_sges[0]) * sges_per_buf,
776f38ba179SUrsula Braun 				   GFP_KERNEL);
777f38ba179SUrsula Braun 	if (!link->wr_rx_sges)
778f38ba179SUrsula Braun 		goto no_mem_wr_tx_sges;
77949dc9013SChristophe JAILLET 	link->wr_tx_mask = bitmap_zalloc(SMC_WR_BUF_CNT, GFP_KERNEL);
780f38ba179SUrsula Braun 	if (!link->wr_tx_mask)
781f38ba179SUrsula Braun 		goto no_mem_wr_rx_sges;
782f38ba179SUrsula Braun 	link->wr_tx_pends = kcalloc(SMC_WR_BUF_CNT,
783f38ba179SUrsula Braun 				    sizeof(link->wr_tx_pends[0]),
784f38ba179SUrsula Braun 				    GFP_KERNEL);
785f38ba179SUrsula Braun 	if (!link->wr_tx_pends)
786f38ba179SUrsula Braun 		goto no_mem_wr_tx_mask;
78709c61d24SKarsten Graul 	link->wr_tx_compl = kcalloc(SMC_WR_BUF_CNT,
78809c61d24SKarsten Graul 				    sizeof(link->wr_tx_compl[0]),
78909c61d24SKarsten Graul 				    GFP_KERNEL);
79009c61d24SKarsten Graul 	if (!link->wr_tx_compl)
79109c61d24SKarsten Graul 		goto no_mem_wr_tx_pends;
7928799e310SKarsten Graul 
7938799e310SKarsten Graul 	if (link->lgr->smc_version == SMC_V2) {
7948799e310SKarsten Graul 		link->wr_tx_v2_ib = kzalloc(sizeof(*link->wr_tx_v2_ib),
7958799e310SKarsten Graul 					    GFP_KERNEL);
7968799e310SKarsten Graul 		if (!link->wr_tx_v2_ib)
7978799e310SKarsten Graul 			goto no_mem_tx_compl;
7988799e310SKarsten Graul 		link->wr_tx_v2_sge = kzalloc(sizeof(*link->wr_tx_v2_sge),
7998799e310SKarsten Graul 					     GFP_KERNEL);
8008799e310SKarsten Graul 		if (!link->wr_tx_v2_sge)
8018799e310SKarsten Graul 			goto no_mem_v2_ib;
8028799e310SKarsten Graul 		link->wr_tx_v2_pend = kzalloc(sizeof(*link->wr_tx_v2_pend),
8038799e310SKarsten Graul 					      GFP_KERNEL);
8048799e310SKarsten Graul 		if (!link->wr_tx_v2_pend)
8058799e310SKarsten Graul 			goto no_mem_v2_sge;
8068799e310SKarsten Graul 	}
807f38ba179SUrsula Braun 	return 0;
808f38ba179SUrsula Braun 
8098799e310SKarsten Graul no_mem_v2_sge:
8108799e310SKarsten Graul 	kfree(link->wr_tx_v2_sge);
8118799e310SKarsten Graul no_mem_v2_ib:
8128799e310SKarsten Graul 	kfree(link->wr_tx_v2_ib);
8138799e310SKarsten Graul no_mem_tx_compl:
8148799e310SKarsten Graul 	kfree(link->wr_tx_compl);
81509c61d24SKarsten Graul no_mem_wr_tx_pends:
81609c61d24SKarsten Graul 	kfree(link->wr_tx_pends);
817f38ba179SUrsula Braun no_mem_wr_tx_mask:
818f38ba179SUrsula Braun 	kfree(link->wr_tx_mask);
819f38ba179SUrsula Braun no_mem_wr_rx_sges:
820f38ba179SUrsula Braun 	kfree(link->wr_rx_sges);
821f38ba179SUrsula Braun no_mem_wr_tx_sges:
822f38ba179SUrsula Braun 	kfree(link->wr_tx_sges);
823ad6f317fSUrsula Braun no_mem_wr_tx_rdma_sges:
824ad6f317fSUrsula Braun 	kfree(link->wr_tx_rdma_sges);
825ad6f317fSUrsula Braun no_mem_wr_tx_rdmas:
826ad6f317fSUrsula Braun 	kfree(link->wr_tx_rdmas);
827f38ba179SUrsula Braun no_mem_wr_rx_ibs:
828f38ba179SUrsula Braun 	kfree(link->wr_rx_ibs);
829f38ba179SUrsula Braun no_mem_wr_tx_ibs:
830f38ba179SUrsula Braun 	kfree(link->wr_tx_ibs);
831f38ba179SUrsula Braun no_mem_wr_rx_bufs:
832f38ba179SUrsula Braun 	kfree(link->wr_rx_bufs);
833f38ba179SUrsula Braun no_mem_wr_tx_bufs:
834f38ba179SUrsula Braun 	kfree(link->wr_tx_bufs);
835f38ba179SUrsula Braun no_mem:
836f38ba179SUrsula Braun 	return -ENOMEM;
837f38ba179SUrsula Braun }
838f38ba179SUrsula Braun 
smc_wr_remove_dev(struct smc_ib_device * smcibdev)839f38ba179SUrsula Braun void smc_wr_remove_dev(struct smc_ib_device *smcibdev)
840f38ba179SUrsula Braun {
841f38ba179SUrsula Braun 	tasklet_kill(&smcibdev->recv_tasklet);
842f38ba179SUrsula Braun 	tasklet_kill(&smcibdev->send_tasklet);
843f38ba179SUrsula Braun }
844f38ba179SUrsula Braun 
smc_wr_add_dev(struct smc_ib_device * smcibdev)845f38ba179SUrsula Braun void smc_wr_add_dev(struct smc_ib_device *smcibdev)
846f38ba179SUrsula Braun {
847fcb8e3a3SAllen Pais 	tasklet_setup(&smcibdev->recv_tasklet, smc_wr_rx_tasklet_fn);
848fcb8e3a3SAllen Pais 	tasklet_setup(&smcibdev->send_tasklet, smc_wr_tx_tasklet_fn);
849f38ba179SUrsula Braun }
850f38ba179SUrsula Braun 
smcr_wr_tx_refs_free(struct percpu_ref * ref)851*79a22238SKai Shen static void smcr_wr_tx_refs_free(struct percpu_ref *ref)
852*79a22238SKai Shen {
853*79a22238SKai Shen 	struct smc_link *lnk = container_of(ref, struct smc_link, wr_tx_refs);
854*79a22238SKai Shen 
855*79a22238SKai Shen 	complete(&lnk->tx_ref_comp);
856*79a22238SKai Shen }
857*79a22238SKai Shen 
smcr_wr_reg_refs_free(struct percpu_ref * ref)858*79a22238SKai Shen static void smcr_wr_reg_refs_free(struct percpu_ref *ref)
859*79a22238SKai Shen {
860*79a22238SKai Shen 	struct smc_link *lnk = container_of(ref, struct smc_link, wr_reg_refs);
861*79a22238SKai Shen 
862*79a22238SKai Shen 	complete(&lnk->reg_ref_comp);
863*79a22238SKai Shen }
864*79a22238SKai Shen 
smc_wr_create_link(struct smc_link * lnk)865f38ba179SUrsula Braun int smc_wr_create_link(struct smc_link *lnk)
866f38ba179SUrsula Braun {
867f38ba179SUrsula Braun 	struct ib_device *ibdev = lnk->smcibdev->ibdev;
868f38ba179SUrsula Braun 	int rc = 0;
869f38ba179SUrsula Braun 
870f38ba179SUrsula Braun 	smc_wr_tx_set_wr_id(&lnk->wr_tx_id, 0);
871f38ba179SUrsula Braun 	lnk->wr_rx_id = 0;
872f38ba179SUrsula Braun 	lnk->wr_rx_dma_addr = ib_dma_map_single(
873f38ba179SUrsula Braun 		ibdev, lnk->wr_rx_bufs,	SMC_WR_BUF_SIZE * lnk->wr_rx_cnt,
874f38ba179SUrsula Braun 		DMA_FROM_DEVICE);
875f38ba179SUrsula Braun 	if (ib_dma_mapping_error(ibdev, lnk->wr_rx_dma_addr)) {
876f38ba179SUrsula Braun 		lnk->wr_rx_dma_addr = 0;
877f38ba179SUrsula Braun 		rc = -EIO;
878f38ba179SUrsula Braun 		goto out;
879f38ba179SUrsula Braun 	}
8808799e310SKarsten Graul 	if (lnk->lgr->smc_version == SMC_V2) {
8818799e310SKarsten Graul 		lnk->wr_rx_v2_dma_addr = ib_dma_map_single(ibdev,
8828799e310SKarsten Graul 			lnk->lgr->wr_rx_buf_v2, SMC_WR_BUF_V2_SIZE,
8838799e310SKarsten Graul 			DMA_FROM_DEVICE);
8848799e310SKarsten Graul 		if (ib_dma_mapping_error(ibdev, lnk->wr_rx_v2_dma_addr)) {
8858799e310SKarsten Graul 			lnk->wr_rx_v2_dma_addr = 0;
8868799e310SKarsten Graul 			rc = -EIO;
8878799e310SKarsten Graul 			goto dma_unmap;
8888799e310SKarsten Graul 		}
8898799e310SKarsten Graul 		lnk->wr_tx_v2_dma_addr = ib_dma_map_single(ibdev,
8908799e310SKarsten Graul 			lnk->lgr->wr_tx_buf_v2, SMC_WR_BUF_V2_SIZE,
8918799e310SKarsten Graul 			DMA_TO_DEVICE);
8928799e310SKarsten Graul 		if (ib_dma_mapping_error(ibdev, lnk->wr_tx_v2_dma_addr)) {
8938799e310SKarsten Graul 			lnk->wr_tx_v2_dma_addr = 0;
8948799e310SKarsten Graul 			rc = -EIO;
8958799e310SKarsten Graul 			goto dma_unmap;
8968799e310SKarsten Graul 		}
8978799e310SKarsten Graul 	}
898f38ba179SUrsula Braun 	lnk->wr_tx_dma_addr = ib_dma_map_single(
899f38ba179SUrsula Braun 		ibdev, lnk->wr_tx_bufs,	SMC_WR_BUF_SIZE * lnk->wr_tx_cnt,
900f38ba179SUrsula Braun 		DMA_TO_DEVICE);
901f38ba179SUrsula Braun 	if (ib_dma_mapping_error(ibdev, lnk->wr_tx_dma_addr)) {
902f38ba179SUrsula Braun 		rc = -EIO;
903f38ba179SUrsula Braun 		goto dma_unmap;
904f38ba179SUrsula Braun 	}
905f38ba179SUrsula Braun 	smc_wr_init_sge(lnk);
90649dc9013SChristophe JAILLET 	bitmap_zero(lnk->wr_tx_mask, SMC_WR_BUF_CNT);
907652a1e41SUrsula Braun 	init_waitqueue_head(&lnk->wr_tx_wait);
908*79a22238SKai Shen 	rc = percpu_ref_init(&lnk->wr_tx_refs, smcr_wr_tx_refs_free, 0, GFP_KERNEL);
909*79a22238SKai Shen 	if (rc)
910*79a22238SKai Shen 		goto dma_unmap;
911*79a22238SKai Shen 	init_completion(&lnk->tx_ref_comp);
912652a1e41SUrsula Braun 	init_waitqueue_head(&lnk->wr_reg_wait);
913*79a22238SKai Shen 	rc = percpu_ref_init(&lnk->wr_reg_refs, smcr_wr_reg_refs_free, 0, GFP_KERNEL);
914*79a22238SKai Shen 	if (rc)
915*79a22238SKai Shen 		goto dma_unmap;
916*79a22238SKai Shen 	init_completion(&lnk->reg_ref_comp);
917e9b1a4f8SYacan Liu 	init_waitqueue_head(&lnk->wr_rx_empty_wait);
918f38ba179SUrsula Braun 	return rc;
919f38ba179SUrsula Braun 
920f38ba179SUrsula Braun dma_unmap:
9218799e310SKarsten Graul 	if (lnk->wr_rx_v2_dma_addr) {
9228799e310SKarsten Graul 		ib_dma_unmap_single(ibdev, lnk->wr_rx_v2_dma_addr,
9238799e310SKarsten Graul 				    SMC_WR_BUF_V2_SIZE,
9248799e310SKarsten Graul 				    DMA_FROM_DEVICE);
9258799e310SKarsten Graul 		lnk->wr_rx_v2_dma_addr = 0;
9268799e310SKarsten Graul 	}
9278799e310SKarsten Graul 	if (lnk->wr_tx_v2_dma_addr) {
9288799e310SKarsten Graul 		ib_dma_unmap_single(ibdev, lnk->wr_tx_v2_dma_addr,
9298799e310SKarsten Graul 				    SMC_WR_BUF_V2_SIZE,
9308799e310SKarsten Graul 				    DMA_TO_DEVICE);
9318799e310SKarsten Graul 		lnk->wr_tx_v2_dma_addr = 0;
9328799e310SKarsten Graul 	}
933f38ba179SUrsula Braun 	ib_dma_unmap_single(ibdev, lnk->wr_rx_dma_addr,
934f38ba179SUrsula Braun 			    SMC_WR_BUF_SIZE * lnk->wr_rx_cnt,
935f38ba179SUrsula Braun 			    DMA_FROM_DEVICE);
936f38ba179SUrsula Braun 	lnk->wr_rx_dma_addr = 0;
937f38ba179SUrsula Braun out:
938f38ba179SUrsula Braun 	return rc;
939f38ba179SUrsula Braun }
940