1/*-
2 * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
3 *
4 * Copyright (c) 2006 Mellanox Technologies Ltd.  All rights reserved.
5 *
6 * This software is available to you under a choice of one of two
7 * licenses.  You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
11 *
12 *     Redistribution and use in source and binary forms, with or
13 *     without modification, are permitted provided that the following
14 *     conditions are met:
15 *
16 *      - Redistributions of source code must retain the above
17 *        copyright notice, this list of conditions and the following
18 *        disclaimer.
19 *
20 *      - Redistributions in binary form must reproduce the above
21 *        copyright notice, this list of conditions and the following
22 *        disclaimer in the documentation and/or other materials
23 *        provided with the distribution.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 * $Id$
35 */
36#include "sdp.h"
37
38static void sdp_nagle_timeout(void *data);
39
40#ifdef CONFIG_INFINIBAND_SDP_DEBUG_DATA
41void _dump_packet(const char *func, int line, struct socket *sk, char *str,
42		struct mbuf *mb, const struct sdp_bsdh *h)
43{
44	struct sdp_hh *hh;
45	struct sdp_hah *hah;
46	struct sdp_chrecvbuf *req_size;
47	struct sdp_rrch *rrch;
48	struct sdp_srcah *srcah;
49	int len = 0;
50	char buf[256];
51	len += snprintf(buf, 255-len, "%s mb: %p mid: %2x:%-20s flags: 0x%x "
52			"bufs: 0x%x len: 0x%x mseq: 0x%x mseq_ack: 0x%x | ",
53			str, mb, h->mid, mid2str(h->mid), h->flags,
54			ntohs(h->bufs), ntohl(h->len), ntohl(h->mseq),
55			ntohl(h->mseq_ack));
56
57	switch (h->mid) {
58	case SDP_MID_HELLO:
59		hh = (struct sdp_hh *)h;
60		len += snprintf(buf + len, 255-len,
61				"max_adverts: %d  majv_minv: 0x%x "
62				"localrcvsz: 0x%x desremrcvsz: 0x%x |",
63				hh->max_adverts, hh->majv_minv,
64				ntohl(hh->localrcvsz),
65				ntohl(hh->desremrcvsz));
66		break;
67	case SDP_MID_HELLO_ACK:
68		hah = (struct sdp_hah *)h;
69		len += snprintf(buf + len, 255-len, "actrcvz: 0x%x |",
70				ntohl(hah->actrcvsz));
71		break;
72	case SDP_MID_CHRCVBUF:
73	case SDP_MID_CHRCVBUF_ACK:
74		req_size = (struct sdp_chrecvbuf *)(h+1);
75		len += snprintf(buf + len, 255-len, "req_size: 0x%x |",
76				ntohl(req_size->size));
77		break;
78	case SDP_MID_DATA:
79		len += snprintf(buf + len, 255-len, "data_len: 0x%lx |",
80			ntohl(h->len) - sizeof(struct sdp_bsdh));
81		break;
82	case SDP_MID_RDMARDCOMPL:
83		rrch = (struct sdp_rrch *)(h+1);
84
85		len += snprintf(buf + len, 255-len, " | len: 0x%x |",
86				ntohl(rrch->len));
87		break;
88	case SDP_MID_SRCAVAIL:
89		srcah = (struct sdp_srcah *)(h+1);
90
91		len += snprintf(buf + len, 255-len, " | payload: 0x%lx, "
92				"len: 0x%x, rkey: 0x%x, vaddr: 0x%jx |",
93				ntohl(h->len) - sizeof(struct sdp_bsdh) -
94				sizeof(struct sdp_srcah),
95				ntohl(srcah->len), ntohl(srcah->rkey),
96				be64_to_cpu(srcah->vaddr));
97		break;
98	default:
99		break;
100	}
101	buf[len] = 0;
102	_sdp_printk(func, line, KERN_WARNING, sk, "%s: %s\n", str, buf);
103}
104#endif
105
106static inline int
107sdp_nagle_off(struct sdp_sock *ssk, struct mbuf *mb)
108{
109
110	struct sdp_bsdh *h;
111
112	h = mtod(mb, struct sdp_bsdh *);
113	int send_now =
114#ifdef SDP_ZCOPY
115		BZCOPY_STATE(mb) ||
116#endif
117		unlikely(h->mid != SDP_MID_DATA) ||
118		(ssk->flags & SDP_NODELAY) ||
119		!ssk->nagle_last_unacked ||
120		mb->m_pkthdr.len >= ssk->xmit_size_goal / 4 ||
121		(mb->m_flags & M_PUSH);
122
123	if (send_now) {
124		unsigned long mseq = ring_head(ssk->tx_ring);
125		ssk->nagle_last_unacked = mseq;
126	} else {
127		if (!callout_pending(&ssk->nagle_timer)) {
128			callout_reset(&ssk->nagle_timer, SDP_NAGLE_TIMEOUT,
129			    sdp_nagle_timeout, ssk);
130			sdp_dbg_data(ssk->socket, "Starting nagle timer\n");
131		}
132	}
133	sdp_dbg_data(ssk->socket, "send_now = %d last_unacked = %ld\n",
134		send_now, ssk->nagle_last_unacked);
135
136	return send_now;
137}
138
139static void
140sdp_nagle_timeout(void *data)
141{
142	struct sdp_sock *ssk = (struct sdp_sock *)data;
143	struct socket *sk = ssk->socket;
144
145	sdp_dbg_data(sk, "last_unacked = %ld\n", ssk->nagle_last_unacked);
146
147	if (!callout_active(&ssk->nagle_timer))
148		return;
149	callout_deactivate(&ssk->nagle_timer);
150
151	if (!ssk->nagle_last_unacked)
152		goto out;
153	if (ssk->state == TCPS_CLOSED)
154		return;
155	ssk->nagle_last_unacked = 0;
156	sdp_post_sends(ssk, M_NOWAIT);
157
158	sowwakeup(ssk->socket);
159out:
160	if (sk->so_snd.sb_sndptr)
161		callout_reset(&ssk->nagle_timer, SDP_NAGLE_TIMEOUT,
162		    sdp_nagle_timeout, ssk);
163}
164
165void
166sdp_post_sends(struct sdp_sock *ssk, int wait)
167{
168	struct mbuf *mb;
169	int post_count = 0;
170	struct socket *sk;
171	int low;
172
173	sk = ssk->socket;
174	if (unlikely(!ssk->id)) {
175		if (sk->so_snd.sb_sndptr) {
176			sdp_dbg(ssk->socket,
177				"Send on socket without cmid ECONNRESET.\n");
178			sdp_notify(ssk, ECONNRESET);
179		}
180		return;
181	}
182again:
183	if (sdp_tx_ring_slots_left(ssk) < SDP_TX_SIZE / 2)
184		sdp_xmit_poll(ssk,  1);
185
186	if (ssk->recv_request &&
187	    ring_tail(ssk->rx_ring) >= ssk->recv_request_head &&
188	    tx_credits(ssk) >= SDP_MIN_TX_CREDITS &&
189	    sdp_tx_ring_slots_left(ssk)) {
190		mb = sdp_alloc_mb_chrcvbuf_ack(sk,
191		    ssk->recv_bytes - SDP_HEAD_SIZE, wait);
192		if (mb == NULL)
193			goto allocfail;
194		ssk->recv_request = 0;
195		sdp_post_send(ssk, mb);
196		post_count++;
197	}
198
199	if (tx_credits(ssk) <= SDP_MIN_TX_CREDITS &&
200	    sdp_tx_ring_slots_left(ssk) && sk->so_snd.sb_sndptr &&
201	    sdp_nagle_off(ssk, sk->so_snd.sb_sndptr)) {
202		SDPSTATS_COUNTER_INC(send_miss_no_credits);
203	}
204
205	while (tx_credits(ssk) > SDP_MIN_TX_CREDITS &&
206	    sdp_tx_ring_slots_left(ssk) && (mb = sk->so_snd.sb_sndptr) &&
207	    sdp_nagle_off(ssk, mb)) {
208		struct mbuf *n;
209
210		SOCKBUF_LOCK(&sk->so_snd);
211		sk->so_snd.sb_sndptr = mb->m_nextpkt;
212		sk->so_snd.sb_mb = mb->m_nextpkt;
213		mb->m_nextpkt = NULL;
214		SB_EMPTY_FIXUP(&sk->so_snd);
215		for (n = mb; n != NULL; n = n->m_next)
216			sbfree(&sk->so_snd, n);
217		SOCKBUF_UNLOCK(&sk->so_snd);
218		sdp_post_send(ssk, mb);
219		post_count++;
220	}
221
222	if (credit_update_needed(ssk) && ssk->state >= TCPS_ESTABLISHED &&
223	    ssk->state < TCPS_FIN_WAIT_2) {
224		mb = sdp_alloc_mb_data(ssk->socket, wait);
225		if (mb == NULL)
226			goto allocfail;
227		sdp_post_send(ssk, mb);
228
229		SDPSTATS_COUNTER_INC(post_send_credits);
230		post_count++;
231	}
232
233	/* send DisConn if needed
234	 * Do not send DisConn if there is only 1 credit. Compliance with CA4-82
235	 * If one credit is available, an implementation shall only send SDP
236	 * messages that provide additional credits and also do not contain ULP
237	 * payload. */
238	if ((ssk->flags & SDP_NEEDFIN) && !sk->so_snd.sb_sndptr &&
239	    tx_credits(ssk) > 1) {
240		mb = sdp_alloc_mb_disconnect(sk, wait);
241		if (mb == NULL)
242			goto allocfail;
243		ssk->flags &= ~SDP_NEEDFIN;
244		sdp_post_send(ssk, mb);
245		post_count++;
246	}
247	low = (sdp_tx_ring_slots_left(ssk) <= SDP_MIN_TX_CREDITS);
248	if (post_count || low) {
249		if (low)
250			sdp_arm_tx_cq(ssk);
251		if (sdp_xmit_poll(ssk, low))
252			goto again;
253	}
254	return;
255
256allocfail:
257	ssk->nagle_last_unacked = -1;
258	callout_reset(&ssk->nagle_timer, 1, sdp_nagle_timeout, ssk);
259	return;
260}
261