xref: /freebsd/sys/ofed/drivers/infiniband/ulp/sdp/sdp_tx.c (revision b633e08c705fe43180567eae26923d6f6f98c8d9)
1fe267a55SPedro F. Giffuni /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
3fe267a55SPedro F. Giffuni  *
4aa0a1e58SJeff Roberson  * Copyright (c) 2009 Mellanox Technologies Ltd.  All rights reserved.
5aa0a1e58SJeff Roberson  *
6aa0a1e58SJeff Roberson  * This software is available to you under a choice of one of two
7aa0a1e58SJeff Roberson  * licenses.  You may choose to be licensed under the terms of the GNU
8aa0a1e58SJeff Roberson  * General Public License (GPL) Version 2, available from the file
9aa0a1e58SJeff Roberson  * COPYING in the main directory of this source tree, or the
10aa0a1e58SJeff Roberson  * OpenIB.org BSD license below:
11aa0a1e58SJeff Roberson  *
12aa0a1e58SJeff Roberson  *     Redistribution and use in source and binary forms, with or
13aa0a1e58SJeff Roberson  *     without modification, are permitted provided that the following
14aa0a1e58SJeff Roberson  *     conditions are met:
15aa0a1e58SJeff Roberson  *
16aa0a1e58SJeff Roberson  *      - Redistributions of source code must retain the above
17aa0a1e58SJeff Roberson  *        copyright notice, this list of conditions and the following
18aa0a1e58SJeff Roberson  *        disclaimer.
19aa0a1e58SJeff Roberson  *
20aa0a1e58SJeff Roberson  *      - Redistributions in binary form must reproduce the above
21aa0a1e58SJeff Roberson  *        copyright notice, this list of conditions and the following
22aa0a1e58SJeff Roberson  *        disclaimer in the documentation and/or other materials
23aa0a1e58SJeff Roberson  *        provided with the distribution.
24aa0a1e58SJeff Roberson  *
25aa0a1e58SJeff Roberson  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26aa0a1e58SJeff Roberson  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27aa0a1e58SJeff Roberson  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28aa0a1e58SJeff Roberson  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29aa0a1e58SJeff Roberson  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30aa0a1e58SJeff Roberson  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31aa0a1e58SJeff Roberson  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32aa0a1e58SJeff Roberson  * SOFTWARE.
33aa0a1e58SJeff Roberson  */
34aa0a1e58SJeff Roberson #include "sdp.h"
35aa0a1e58SJeff Roberson 
36aa0a1e58SJeff Roberson #define sdp_cnt(var) do { (var)++; } while (0)
37aa0a1e58SJeff Roberson 
38aa0a1e58SJeff Roberson SDP_MODPARAM_SINT(sdp_keepalive_probes_sent, 0,
39aa0a1e58SJeff Roberson 		"Total number of keepalive probes sent.");
40aa0a1e58SJeff Roberson 
41aa0a1e58SJeff Roberson static int sdp_process_tx_cq(struct sdp_sock *ssk);
42aa0a1e58SJeff Roberson static void sdp_poll_tx_timeout(void *data);
43aa0a1e58SJeff Roberson 
44aa0a1e58SJeff Roberson int
sdp_xmit_poll(struct sdp_sock * ssk,int force)45aa0a1e58SJeff Roberson sdp_xmit_poll(struct sdp_sock *ssk, int force)
46aa0a1e58SJeff Roberson {
47aa0a1e58SJeff Roberson 	int wc_processed = 0;
48aa0a1e58SJeff Roberson 
49aa0a1e58SJeff Roberson 	SDP_WLOCK_ASSERT(ssk);
50aa0a1e58SJeff Roberson 	sdp_prf(ssk->socket, NULL, "%s", __func__);
51aa0a1e58SJeff Roberson 
52aa0a1e58SJeff Roberson 	/* If we don't have a pending timer, set one up to catch our recent
53aa0a1e58SJeff Roberson 	   post in case the interface becomes idle */
54aa0a1e58SJeff Roberson 	if (!callout_pending(&ssk->tx_ring.timer))
55aa0a1e58SJeff Roberson 		callout_reset(&ssk->tx_ring.timer, SDP_TX_POLL_TIMEOUT,
56aa0a1e58SJeff Roberson 		    sdp_poll_tx_timeout, ssk);
57aa0a1e58SJeff Roberson 
58aa0a1e58SJeff Roberson 	/* Poll the CQ every SDP_TX_POLL_MODER packets */
59aa0a1e58SJeff Roberson 	if (force || (++ssk->tx_ring.poll_cnt & (SDP_TX_POLL_MODER - 1)) == 0)
60aa0a1e58SJeff Roberson 		wc_processed = sdp_process_tx_cq(ssk);
61aa0a1e58SJeff Roberson 
62aa0a1e58SJeff Roberson 	return wc_processed;
63aa0a1e58SJeff Roberson }
64aa0a1e58SJeff Roberson 
65aa0a1e58SJeff Roberson void
sdp_post_send(struct sdp_sock * ssk,struct mbuf * mb)66aa0a1e58SJeff Roberson sdp_post_send(struct sdp_sock *ssk, struct mbuf *mb)
67aa0a1e58SJeff Roberson {
68aa0a1e58SJeff Roberson 	struct sdp_buf *tx_req;
69aa0a1e58SJeff Roberson 	struct sdp_bsdh *h;
70aa0a1e58SJeff Roberson 	unsigned long mseq;
71aa0a1e58SJeff Roberson 	struct ib_device *dev;
72693ddf4dSHans Petter Selasky 	const struct ib_send_wr *bad_wr;
73aa0a1e58SJeff Roberson 	struct ib_sge ibsge[SDP_MAX_SEND_SGES];
74aa0a1e58SJeff Roberson 	struct ib_sge *sge;
75aa0a1e58SJeff Roberson 	struct ib_send_wr tx_wr = { NULL };
76aa0a1e58SJeff Roberson 	int i, rc;
77aa0a1e58SJeff Roberson 	u64 addr;
78aa0a1e58SJeff Roberson 
79aa0a1e58SJeff Roberson 	SDPSTATS_COUNTER_MID_INC(post_send, h->mid);
80aa0a1e58SJeff Roberson 	SDPSTATS_HIST(send_size, mb->len);
81aa0a1e58SJeff Roberson 
82aa0a1e58SJeff Roberson 	if (!ssk->qp_active) {
83aa0a1e58SJeff Roberson 		m_freem(mb);
84aa0a1e58SJeff Roberson 		return;
85aa0a1e58SJeff Roberson 	}
86aa0a1e58SJeff Roberson 
87aa0a1e58SJeff Roberson 	mseq = ring_head(ssk->tx_ring);
88aa0a1e58SJeff Roberson 	h = mtod(mb, struct sdp_bsdh *);
89aa0a1e58SJeff Roberson 	ssk->tx_packets++;
90aa0a1e58SJeff Roberson 	ssk->tx_bytes += mb->m_pkthdr.len;
91aa0a1e58SJeff Roberson 
92aa0a1e58SJeff Roberson #ifdef SDP_ZCOPY
93aa0a1e58SJeff Roberson 	if (unlikely(h->mid == SDP_MID_SRCAVAIL)) {
94aa0a1e58SJeff Roberson 		struct tx_srcavail_state *tx_sa = TX_SRCAVAIL_STATE(mb);
95aa0a1e58SJeff Roberson 		if (ssk->tx_sa != tx_sa) {
96aa0a1e58SJeff Roberson 			sdp_dbg_data(ssk->socket, "SrcAvail cancelled "
97aa0a1e58SJeff Roberson 					"before being sent!\n");
98aa0a1e58SJeff Roberson 			WARN_ON(1);
99aa0a1e58SJeff Roberson 			m_freem(mb);
100aa0a1e58SJeff Roberson 			return;
101aa0a1e58SJeff Roberson 		}
102aa0a1e58SJeff Roberson 		TX_SRCAVAIL_STATE(mb)->mseq = mseq;
103aa0a1e58SJeff Roberson 	}
104aa0a1e58SJeff Roberson #endif
105aa0a1e58SJeff Roberson 
106aa0a1e58SJeff Roberson 	if (unlikely(mb->m_flags & M_URG))
107aa0a1e58SJeff Roberson 		h->flags = SDP_OOB_PRES | SDP_OOB_PEND;
108aa0a1e58SJeff Roberson 	else
109aa0a1e58SJeff Roberson 		h->flags = 0;
110aa0a1e58SJeff Roberson 
111aa0a1e58SJeff Roberson 	mb->m_flags |= M_RDONLY; /* Don't allow compression once sent. */
112aa0a1e58SJeff Roberson 	h->bufs = htons(rx_ring_posted(ssk));
113aa0a1e58SJeff Roberson 	h->len = htonl(mb->m_pkthdr.len);
114aa0a1e58SJeff Roberson 	h->mseq = htonl(mseq);
115aa0a1e58SJeff Roberson 	h->mseq_ack = htonl(mseq_ack(ssk));
116aa0a1e58SJeff Roberson 
117aa0a1e58SJeff Roberson 	sdp_prf1(ssk->socket, mb, "TX: %s bufs: %d mseq:%ld ack:%d",
118aa0a1e58SJeff Roberson 			mid2str(h->mid), rx_ring_posted(ssk), mseq,
119aa0a1e58SJeff Roberson 			ntohl(h->mseq_ack));
120aa0a1e58SJeff Roberson 
121aa0a1e58SJeff Roberson 	SDP_DUMP_PACKET(ssk->socket, "TX", mb, h);
122aa0a1e58SJeff Roberson 
123aa0a1e58SJeff Roberson 	tx_req = &ssk->tx_ring.buffer[mseq & (SDP_TX_SIZE - 1)];
124aa0a1e58SJeff Roberson 	tx_req->mb = mb;
125aa0a1e58SJeff Roberson 	dev = ssk->ib_device;
126aa0a1e58SJeff Roberson 	sge = &ibsge[0];
127aa0a1e58SJeff Roberson 	for (i = 0;  mb != NULL; i++, mb = mb->m_next, sge++) {
128aa0a1e58SJeff Roberson 		addr = ib_dma_map_single(dev, mb->m_data, mb->m_len,
129aa0a1e58SJeff Roberson 		    DMA_TO_DEVICE);
130aa0a1e58SJeff Roberson 		/* TODO: proper error handling */
131aa0a1e58SJeff Roberson 		BUG_ON(ib_dma_mapping_error(dev, addr));
132aa0a1e58SJeff Roberson 		BUG_ON(i >= SDP_MAX_SEND_SGES);
133aa0a1e58SJeff Roberson 		tx_req->mapping[i] = addr;
134aa0a1e58SJeff Roberson 		sge->addr = addr;
135aa0a1e58SJeff Roberson 		sge->length = mb->m_len;
136c69c74b8SHans Petter Selasky 		sge->lkey = ssk->sdp_dev->pd->local_dma_lkey;
137aa0a1e58SJeff Roberson 	}
138aa0a1e58SJeff Roberson 	tx_wr.next = NULL;
139aa0a1e58SJeff Roberson 	tx_wr.wr_id = mseq | SDP_OP_SEND;
140aa0a1e58SJeff Roberson 	tx_wr.sg_list = ibsge;
141aa0a1e58SJeff Roberson 	tx_wr.num_sge = i;
142aa0a1e58SJeff Roberson 	tx_wr.opcode = IB_WR_SEND;
143aa0a1e58SJeff Roberson 	tx_wr.send_flags = IB_SEND_SIGNALED;
144aa0a1e58SJeff Roberson 	if (unlikely(tx_req->mb->m_flags & M_URG))
145aa0a1e58SJeff Roberson 		tx_wr.send_flags |= IB_SEND_SOLICITED;
146aa0a1e58SJeff Roberson 
147aa0a1e58SJeff Roberson 	rc = ib_post_send(ssk->qp, &tx_wr, &bad_wr);
148aa0a1e58SJeff Roberson 	if (unlikely(rc)) {
149aa0a1e58SJeff Roberson 		sdp_dbg(ssk->socket,
150aa0a1e58SJeff Roberson 				"ib_post_send failed with status %d.\n", rc);
151aa0a1e58SJeff Roberson 
152aa0a1e58SJeff Roberson 		sdp_cleanup_sdp_buf(ssk, tx_req, DMA_TO_DEVICE);
153aa0a1e58SJeff Roberson 
154aa0a1e58SJeff Roberson 		sdp_notify(ssk, ECONNRESET);
155aa0a1e58SJeff Roberson 		m_freem(tx_req->mb);
156aa0a1e58SJeff Roberson 		return;
157aa0a1e58SJeff Roberson 	}
158aa0a1e58SJeff Roberson 
159aa0a1e58SJeff Roberson 	atomic_inc(&ssk->tx_ring.head);
160aa0a1e58SJeff Roberson 	atomic_dec(&ssk->tx_ring.credits);
161aa0a1e58SJeff Roberson 	atomic_set(&ssk->remote_credits, rx_ring_posted(ssk));
162aa0a1e58SJeff Roberson 
163aa0a1e58SJeff Roberson 	return;
164aa0a1e58SJeff Roberson }
165aa0a1e58SJeff Roberson 
166aa0a1e58SJeff Roberson static struct mbuf *
sdp_send_completion(struct sdp_sock * ssk,int mseq)167aa0a1e58SJeff Roberson sdp_send_completion(struct sdp_sock *ssk, int mseq)
168aa0a1e58SJeff Roberson {
169aa0a1e58SJeff Roberson 	struct ib_device *dev;
170aa0a1e58SJeff Roberson 	struct sdp_buf *tx_req;
171aa0a1e58SJeff Roberson 	struct mbuf *mb = NULL;
172aa0a1e58SJeff Roberson 	struct sdp_tx_ring *tx_ring = &ssk->tx_ring;
173aa0a1e58SJeff Roberson 
174aa0a1e58SJeff Roberson 	if (unlikely(mseq != ring_tail(*tx_ring))) {
175aa0a1e58SJeff Roberson 		printk(KERN_WARNING "Bogus send completion id %d tail %d\n",
176aa0a1e58SJeff Roberson 			mseq, ring_tail(*tx_ring));
177aa0a1e58SJeff Roberson 		goto out;
178aa0a1e58SJeff Roberson 	}
179aa0a1e58SJeff Roberson 
180aa0a1e58SJeff Roberson 	dev = ssk->ib_device;
181aa0a1e58SJeff Roberson 	tx_req = &tx_ring->buffer[mseq & (SDP_TX_SIZE - 1)];
182aa0a1e58SJeff Roberson 	mb = tx_req->mb;
183aa0a1e58SJeff Roberson 	sdp_cleanup_sdp_buf(ssk, tx_req, DMA_TO_DEVICE);
184aa0a1e58SJeff Roberson 
185aa0a1e58SJeff Roberson #ifdef SDP_ZCOPY
186aa0a1e58SJeff Roberson 	/* TODO: AIO and real zcopy code; add their context support here */
187aa0a1e58SJeff Roberson 	if (BZCOPY_STATE(mb))
188aa0a1e58SJeff Roberson 		BZCOPY_STATE(mb)->busy--;
189aa0a1e58SJeff Roberson #endif
190aa0a1e58SJeff Roberson 
191aa0a1e58SJeff Roberson 	atomic_inc(&tx_ring->tail);
192aa0a1e58SJeff Roberson 
193aa0a1e58SJeff Roberson out:
194aa0a1e58SJeff Roberson 	return mb;
195aa0a1e58SJeff Roberson }
196aa0a1e58SJeff Roberson 
197aa0a1e58SJeff Roberson static int
sdp_handle_send_comp(struct sdp_sock * ssk,struct ib_wc * wc)198aa0a1e58SJeff Roberson sdp_handle_send_comp(struct sdp_sock *ssk, struct ib_wc *wc)
199aa0a1e58SJeff Roberson {
200aa0a1e58SJeff Roberson 	struct mbuf *mb = NULL;
201aa0a1e58SJeff Roberson 	struct sdp_bsdh *h;
202aa0a1e58SJeff Roberson 
203aa0a1e58SJeff Roberson 	if (unlikely(wc->status)) {
204aa0a1e58SJeff Roberson 		if (wc->status != IB_WC_WR_FLUSH_ERR) {
205aa0a1e58SJeff Roberson 			sdp_prf(ssk->socket, mb, "Send completion with error. "
206aa0a1e58SJeff Roberson 				"Status %d", wc->status);
207aa0a1e58SJeff Roberson 			sdp_dbg_data(ssk->socket, "Send completion with error. "
208aa0a1e58SJeff Roberson 				"Status %d\n", wc->status);
209aa0a1e58SJeff Roberson 			sdp_notify(ssk, ECONNRESET);
210aa0a1e58SJeff Roberson 		}
211aa0a1e58SJeff Roberson 	}
212aa0a1e58SJeff Roberson 
213aa0a1e58SJeff Roberson 	mb = sdp_send_completion(ssk, wc->wr_id);
214aa0a1e58SJeff Roberson 	if (unlikely(!mb))
215aa0a1e58SJeff Roberson 		return -1;
216aa0a1e58SJeff Roberson 
217aa0a1e58SJeff Roberson 	h = mtod(mb, struct sdp_bsdh *);
218aa0a1e58SJeff Roberson 	sdp_prf1(ssk->socket, mb, "tx completion. mseq:%d", ntohl(h->mseq));
219aa0a1e58SJeff Roberson 	sdp_dbg(ssk->socket, "tx completion. %p %d mseq:%d",
220aa0a1e58SJeff Roberson 	    mb, mb->m_pkthdr.len, ntohl(h->mseq));
221aa0a1e58SJeff Roberson 	m_freem(mb);
222aa0a1e58SJeff Roberson 
223aa0a1e58SJeff Roberson 	return 0;
224aa0a1e58SJeff Roberson }
225aa0a1e58SJeff Roberson 
226aa0a1e58SJeff Roberson static inline void
sdp_process_tx_wc(struct sdp_sock * ssk,struct ib_wc * wc)227aa0a1e58SJeff Roberson sdp_process_tx_wc(struct sdp_sock *ssk, struct ib_wc *wc)
228aa0a1e58SJeff Roberson {
229aa0a1e58SJeff Roberson 
230aa0a1e58SJeff Roberson 	if (likely(wc->wr_id & SDP_OP_SEND)) {
231aa0a1e58SJeff Roberson 		sdp_handle_send_comp(ssk, wc);
232aa0a1e58SJeff Roberson 		return;
233aa0a1e58SJeff Roberson 	}
234aa0a1e58SJeff Roberson 
235aa0a1e58SJeff Roberson #ifdef SDP_ZCOPY
236aa0a1e58SJeff Roberson 	if (wc->wr_id & SDP_OP_RDMA) {
237aa0a1e58SJeff Roberson 		/* TODO: handle failed RDMA read cqe */
238aa0a1e58SJeff Roberson 
239aa0a1e58SJeff Roberson 		sdp_dbg_data(ssk->socket,
240aa0a1e58SJeff Roberson 	 	    "TX comp: RDMA read. status: %d\n", wc->status);
241aa0a1e58SJeff Roberson 		sdp_prf1(sk, NULL, "TX comp: RDMA read");
242aa0a1e58SJeff Roberson 
243aa0a1e58SJeff Roberson 		if (!ssk->tx_ring.rdma_inflight) {
244aa0a1e58SJeff Roberson 			sdp_warn(ssk->socket, "ERROR: unexpected RDMA read\n");
245aa0a1e58SJeff Roberson 			return;
246aa0a1e58SJeff Roberson 		}
247aa0a1e58SJeff Roberson 
248aa0a1e58SJeff Roberson 		if (!ssk->tx_ring.rdma_inflight->busy) {
249aa0a1e58SJeff Roberson 			sdp_warn(ssk->socket,
250aa0a1e58SJeff Roberson 			    "ERROR: too many RDMA read completions\n");
251aa0a1e58SJeff Roberson 			return;
252aa0a1e58SJeff Roberson 		}
253aa0a1e58SJeff Roberson 
254aa0a1e58SJeff Roberson 		/* Only last RDMA read WR is signalled. Order is guaranteed -
255aa0a1e58SJeff Roberson 		 * therefore if Last RDMA read WR is completed - all other
256aa0a1e58SJeff Roberson 		 * have, too */
257aa0a1e58SJeff Roberson 		ssk->tx_ring.rdma_inflight->busy = 0;
258aa0a1e58SJeff Roberson 		sowwakeup(ssk->socket);
259aa0a1e58SJeff Roberson 		sdp_dbg_data(ssk->socket, "woke up sleepers\n");
260aa0a1e58SJeff Roberson 		return;
261aa0a1e58SJeff Roberson 	}
262aa0a1e58SJeff Roberson #endif
263aa0a1e58SJeff Roberson 
264aa0a1e58SJeff Roberson 	/* Keepalive probe sent cleanup */
265aa0a1e58SJeff Roberson 	sdp_cnt(sdp_keepalive_probes_sent);
266aa0a1e58SJeff Roberson 
267aa0a1e58SJeff Roberson 	if (likely(!wc->status))
268aa0a1e58SJeff Roberson 		return;
269aa0a1e58SJeff Roberson 
270aa0a1e58SJeff Roberson 	sdp_dbg(ssk->socket, " %s consumes KEEPALIVE status %d\n",
271aa0a1e58SJeff Roberson 			__func__, wc->status);
272aa0a1e58SJeff Roberson 
273aa0a1e58SJeff Roberson 	if (wc->status == IB_WC_WR_FLUSH_ERR)
274aa0a1e58SJeff Roberson 		return;
275aa0a1e58SJeff Roberson 
276aa0a1e58SJeff Roberson 	sdp_notify(ssk, ECONNRESET);
277aa0a1e58SJeff Roberson }
278aa0a1e58SJeff Roberson 
279aa0a1e58SJeff Roberson static int
sdp_process_tx_cq(struct sdp_sock * ssk)280aa0a1e58SJeff Roberson sdp_process_tx_cq(struct sdp_sock *ssk)
281aa0a1e58SJeff Roberson {
282aa0a1e58SJeff Roberson 	struct ib_wc ibwc[SDP_NUM_WC];
283aa0a1e58SJeff Roberson 	int n, i;
284aa0a1e58SJeff Roberson 	int wc_processed = 0;
285aa0a1e58SJeff Roberson 
286aa0a1e58SJeff Roberson 	SDP_WLOCK_ASSERT(ssk);
287aa0a1e58SJeff Roberson 
288aa0a1e58SJeff Roberson 	if (!ssk->tx_ring.cq) {
289aa0a1e58SJeff Roberson 		sdp_dbg(ssk->socket, "tx irq on destroyed tx_cq\n");
290aa0a1e58SJeff Roberson 		return 0;
291aa0a1e58SJeff Roberson 	}
292aa0a1e58SJeff Roberson 
293aa0a1e58SJeff Roberson 	do {
294aa0a1e58SJeff Roberson 		n = ib_poll_cq(ssk->tx_ring.cq, SDP_NUM_WC, ibwc);
295aa0a1e58SJeff Roberson 		for (i = 0; i < n; ++i) {
296aa0a1e58SJeff Roberson 			sdp_process_tx_wc(ssk, ibwc + i);
297aa0a1e58SJeff Roberson 			wc_processed++;
298aa0a1e58SJeff Roberson 		}
299aa0a1e58SJeff Roberson 	} while (n == SDP_NUM_WC);
300aa0a1e58SJeff Roberson 
301aa0a1e58SJeff Roberson 	if (wc_processed) {
302eb1b1807SGleb Smirnoff 		sdp_post_sends(ssk, M_NOWAIT);
303aa0a1e58SJeff Roberson 		sdp_prf1(sk, NULL, "Waking sendmsg. inflight=%d",
304aa0a1e58SJeff Roberson 				(u32) tx_ring_posted(ssk));
305aa0a1e58SJeff Roberson 		sowwakeup(ssk->socket);
306aa0a1e58SJeff Roberson 	}
307aa0a1e58SJeff Roberson 
308aa0a1e58SJeff Roberson 	return wc_processed;
309aa0a1e58SJeff Roberson }
310aa0a1e58SJeff Roberson 
311aa0a1e58SJeff Roberson static void
sdp_poll_tx(struct sdp_sock * ssk)312aa0a1e58SJeff Roberson sdp_poll_tx(struct sdp_sock *ssk)
313aa0a1e58SJeff Roberson {
314aa0a1e58SJeff Roberson 	struct socket *sk = ssk->socket;
315aa0a1e58SJeff Roberson 	u32 inflight, wc_processed;
316aa0a1e58SJeff Roberson 
317aa0a1e58SJeff Roberson 	sdp_prf1(ssk->socket, NULL, "TX timeout: inflight=%d, head=%d tail=%d",
318aa0a1e58SJeff Roberson 		(u32) tx_ring_posted(ssk),
319aa0a1e58SJeff Roberson 		ring_head(ssk->tx_ring), ring_tail(ssk->tx_ring));
320aa0a1e58SJeff Roberson 
321aa0a1e58SJeff Roberson 	if (unlikely(ssk->state == TCPS_CLOSED)) {
322aa0a1e58SJeff Roberson 		sdp_warn(sk, "Socket is closed\n");
323aa0a1e58SJeff Roberson 		goto out;
324aa0a1e58SJeff Roberson 	}
325aa0a1e58SJeff Roberson 
326aa0a1e58SJeff Roberson 	wc_processed = sdp_process_tx_cq(ssk);
327aa0a1e58SJeff Roberson 	if (!wc_processed)
328aa0a1e58SJeff Roberson 		SDPSTATS_COUNTER_INC(tx_poll_miss);
329aa0a1e58SJeff Roberson 	else
330aa0a1e58SJeff Roberson 		SDPSTATS_COUNTER_INC(tx_poll_hit);
331aa0a1e58SJeff Roberson 
332aa0a1e58SJeff Roberson 	inflight = (u32) tx_ring_posted(ssk);
333bf5cba36SPedro F. Giffuni 	sdp_prf1(ssk->socket, NULL, "finished tx processing. inflight = %d",
334aa0a1e58SJeff Roberson 	    inflight);
335aa0a1e58SJeff Roberson 
336aa0a1e58SJeff Roberson 	/* If there are still packets in flight and the timer has not already
337aa0a1e58SJeff Roberson 	 * been scheduled by the Tx routine then schedule it here to guarantee
338aa0a1e58SJeff Roberson 	 * completion processing of these packets */
339aa0a1e58SJeff Roberson 	if (inflight)
340aa0a1e58SJeff Roberson 		callout_reset(&ssk->tx_ring.timer, SDP_TX_POLL_TIMEOUT,
341aa0a1e58SJeff Roberson 		    sdp_poll_tx_timeout, ssk);
342aa0a1e58SJeff Roberson out:
343aa0a1e58SJeff Roberson #ifdef SDP_ZCOPY
344aa0a1e58SJeff Roberson 	if (ssk->tx_ring.rdma_inflight && ssk->tx_ring.rdma_inflight->busy) {
345aa0a1e58SJeff Roberson 		sdp_prf1(sk, NULL, "RDMA is inflight - arming irq");
346aa0a1e58SJeff Roberson 		sdp_arm_tx_cq(ssk);
347aa0a1e58SJeff Roberson 	}
348aa0a1e58SJeff Roberson #endif
349aa0a1e58SJeff Roberson 	return;
350aa0a1e58SJeff Roberson }
351aa0a1e58SJeff Roberson 
352aa0a1e58SJeff Roberson static void
sdp_poll_tx_timeout(void * data)353aa0a1e58SJeff Roberson sdp_poll_tx_timeout(void *data)
354aa0a1e58SJeff Roberson {
355aa0a1e58SJeff Roberson 	struct sdp_sock *ssk = (struct sdp_sock *)data;
356aa0a1e58SJeff Roberson 
357aa0a1e58SJeff Roberson 	if (!callout_active(&ssk->tx_ring.timer))
358aa0a1e58SJeff Roberson 		return;
359aa0a1e58SJeff Roberson 	callout_deactivate(&ssk->tx_ring.timer);
360aa0a1e58SJeff Roberson 	sdp_poll_tx(ssk);
361aa0a1e58SJeff Roberson }
362aa0a1e58SJeff Roberson 
363aa0a1e58SJeff Roberson static void
sdp_tx_irq(struct ib_cq * cq,void * cq_context)364aa0a1e58SJeff Roberson sdp_tx_irq(struct ib_cq *cq, void *cq_context)
365aa0a1e58SJeff Roberson {
366aa0a1e58SJeff Roberson 	struct sdp_sock *ssk;
367aa0a1e58SJeff Roberson 
368aa0a1e58SJeff Roberson 	ssk = cq_context;
369aa0a1e58SJeff Roberson 	sdp_prf1(ssk->socket, NULL, "tx irq");
370aa0a1e58SJeff Roberson 	sdp_dbg_data(ssk->socket, "Got tx comp interrupt\n");
371aa0a1e58SJeff Roberson 	SDPSTATS_COUNTER_INC(tx_int_count);
372aa0a1e58SJeff Roberson 	SDP_WLOCK(ssk);
373aa0a1e58SJeff Roberson 	sdp_poll_tx(ssk);
374aa0a1e58SJeff Roberson 	SDP_WUNLOCK(ssk);
375aa0a1e58SJeff Roberson }
376aa0a1e58SJeff Roberson 
377aa0a1e58SJeff Roberson static
sdp_tx_ring_purge(struct sdp_sock * ssk)378aa0a1e58SJeff Roberson void sdp_tx_ring_purge(struct sdp_sock *ssk)
379aa0a1e58SJeff Roberson {
380aa0a1e58SJeff Roberson 	while (tx_ring_posted(ssk)) {
381aa0a1e58SJeff Roberson 		struct mbuf *mb;
382aa0a1e58SJeff Roberson 		mb = sdp_send_completion(ssk, ring_tail(ssk->tx_ring));
383aa0a1e58SJeff Roberson 		if (!mb)
384aa0a1e58SJeff Roberson 			break;
385aa0a1e58SJeff Roberson 		m_freem(mb);
386aa0a1e58SJeff Roberson 	}
387aa0a1e58SJeff Roberson }
388aa0a1e58SJeff Roberson 
389aa0a1e58SJeff Roberson void
sdp_post_keepalive(struct sdp_sock * ssk)390aa0a1e58SJeff Roberson sdp_post_keepalive(struct sdp_sock *ssk)
391aa0a1e58SJeff Roberson {
392aa0a1e58SJeff Roberson 	int rc;
393693ddf4dSHans Petter Selasky 	struct ib_send_wr wr;
394693ddf4dSHans Petter Selasky 	const struct ib_send_wr *bad_wr;
395aa0a1e58SJeff Roberson 
396aa0a1e58SJeff Roberson 	sdp_dbg(ssk->socket, "%s\n", __func__);
397aa0a1e58SJeff Roberson 
398aa0a1e58SJeff Roberson 	memset(&wr, 0, sizeof(wr));
399aa0a1e58SJeff Roberson 
400aa0a1e58SJeff Roberson 	wr.next    = NULL;
401aa0a1e58SJeff Roberson 	wr.wr_id   = 0;
402aa0a1e58SJeff Roberson 	wr.sg_list = NULL;
403aa0a1e58SJeff Roberson 	wr.num_sge = 0;
404aa0a1e58SJeff Roberson 	wr.opcode  = IB_WR_RDMA_WRITE;
405aa0a1e58SJeff Roberson 
406aa0a1e58SJeff Roberson 	rc = ib_post_send(ssk->qp, &wr, &bad_wr);
407aa0a1e58SJeff Roberson 	if (rc) {
408aa0a1e58SJeff Roberson 		sdp_dbg(ssk->socket,
409aa0a1e58SJeff Roberson 			"ib_post_keepalive failed with status %d.\n", rc);
410aa0a1e58SJeff Roberson 		sdp_notify(ssk, ECONNRESET);
411aa0a1e58SJeff Roberson 	}
412aa0a1e58SJeff Roberson 
413aa0a1e58SJeff Roberson 	sdp_cnt(sdp_keepalive_probes_sent);
414aa0a1e58SJeff Roberson }
415aa0a1e58SJeff Roberson 
416aa0a1e58SJeff Roberson static void
sdp_tx_cq_event_handler(struct ib_event * event,void * data)417aa0a1e58SJeff Roberson sdp_tx_cq_event_handler(struct ib_event *event, void *data)
418aa0a1e58SJeff Roberson {
419aa0a1e58SJeff Roberson }
420aa0a1e58SJeff Roberson 
421aa0a1e58SJeff Roberson int
sdp_tx_ring_create(struct sdp_sock * ssk,struct ib_device * device)422aa0a1e58SJeff Roberson sdp_tx_ring_create(struct sdp_sock *ssk, struct ib_device *device)
423aa0a1e58SJeff Roberson {
424c69c74b8SHans Petter Selasky 	struct ib_cq_init_attr tx_cq_attr = {
425c69c74b8SHans Petter Selasky 		.cqe = SDP_TX_SIZE,
426c69c74b8SHans Petter Selasky 		.comp_vector = 0,
427c69c74b8SHans Petter Selasky 		.flags = 0,
428c69c74b8SHans Petter Selasky 	};
429aa0a1e58SJeff Roberson 	struct ib_cq *tx_cq;
430aa0a1e58SJeff Roberson 	int rc = 0;
431aa0a1e58SJeff Roberson 
432aa0a1e58SJeff Roberson 	sdp_dbg(ssk->socket, "tx ring create\n");
433aa0a1e58SJeff Roberson 	callout_init_rw(&ssk->tx_ring.timer, &ssk->lock, 0);
434aa0a1e58SJeff Roberson 	callout_init_rw(&ssk->nagle_timer, &ssk->lock, 0);
435aa0a1e58SJeff Roberson 	atomic_set(&ssk->tx_ring.head, 1);
436aa0a1e58SJeff Roberson 	atomic_set(&ssk->tx_ring.tail, 1);
437aa0a1e58SJeff Roberson 
438d3461164SMark Johnston 	ssk->tx_ring.buffer = malloc(sizeof(*ssk->tx_ring.buffer) * SDP_TX_SIZE,
439d3461164SMark Johnston 	    M_SDP, M_WAITOK);
440aa0a1e58SJeff Roberson 
441aa0a1e58SJeff Roberson 	tx_cq = ib_create_cq(device, sdp_tx_irq, sdp_tx_cq_event_handler,
442c69c74b8SHans Petter Selasky 			  ssk, &tx_cq_attr);
443aa0a1e58SJeff Roberson 	if (IS_ERR(tx_cq)) {
444aa0a1e58SJeff Roberson 		rc = PTR_ERR(tx_cq);
445aa0a1e58SJeff Roberson 		sdp_warn(ssk->socket, "Unable to allocate TX CQ: %d.\n", rc);
446aa0a1e58SJeff Roberson 		goto err_cq;
447aa0a1e58SJeff Roberson 	}
448aa0a1e58SJeff Roberson 	ssk->tx_ring.cq = tx_cq;
449aa0a1e58SJeff Roberson 	ssk->tx_ring.poll_cnt = 0;
450aa0a1e58SJeff Roberson 	sdp_arm_tx_cq(ssk);
451aa0a1e58SJeff Roberson 
452aa0a1e58SJeff Roberson 	return 0;
453aa0a1e58SJeff Roberson 
454aa0a1e58SJeff Roberson err_cq:
455d3461164SMark Johnston 	free(ssk->tx_ring.buffer, M_SDP);
456aa0a1e58SJeff Roberson 	ssk->tx_ring.buffer = NULL;
457aa0a1e58SJeff Roberson 	return rc;
458aa0a1e58SJeff Roberson }
459aa0a1e58SJeff Roberson 
460aa0a1e58SJeff Roberson void
sdp_tx_ring_destroy(struct sdp_sock * ssk)461aa0a1e58SJeff Roberson sdp_tx_ring_destroy(struct sdp_sock *ssk)
462aa0a1e58SJeff Roberson {
463aa0a1e58SJeff Roberson 
464aa0a1e58SJeff Roberson 	sdp_dbg(ssk->socket, "tx ring destroy\n");
465aa0a1e58SJeff Roberson 	SDP_WLOCK(ssk);
466aa0a1e58SJeff Roberson 	callout_stop(&ssk->tx_ring.timer);
467aa0a1e58SJeff Roberson 	callout_stop(&ssk->nagle_timer);
468aa0a1e58SJeff Roberson 	SDP_WUNLOCK(ssk);
469aa0a1e58SJeff Roberson 	callout_drain(&ssk->tx_ring.timer);
470aa0a1e58SJeff Roberson 	callout_drain(&ssk->nagle_timer);
471aa0a1e58SJeff Roberson 
472aa0a1e58SJeff Roberson 	if (ssk->tx_ring.buffer) {
473aa0a1e58SJeff Roberson 		sdp_tx_ring_purge(ssk);
474d3461164SMark Johnston 		free(ssk->tx_ring.buffer, M_SDP);
475aa0a1e58SJeff Roberson 		ssk->tx_ring.buffer = NULL;
476aa0a1e58SJeff Roberson 	}
477aa0a1e58SJeff Roberson 
478aa0a1e58SJeff Roberson 	if (ssk->tx_ring.cq) {
479*b633e08cSHans Petter Selasky 		ib_destroy_cq(ssk->tx_ring.cq);
480aa0a1e58SJeff Roberson 		ssk->tx_ring.cq = NULL;
481aa0a1e58SJeff Roberson 	}
482aa0a1e58SJeff Roberson 
483aa0a1e58SJeff Roberson 	WARN_ON(ring_head(ssk->tx_ring) != ring_tail(ssk->tx_ring));
484aa0a1e58SJeff Roberson }
485