1fe267a55SPedro F. Giffuni /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
3fe267a55SPedro F. Giffuni  *
4aa0a1e58SJeff Roberson  * Copyright (c) 2006 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  * $Id$
35aa0a1e58SJeff Roberson  */
36aa0a1e58SJeff Roberson #include "sdp.h"
37aa0a1e58SJeff Roberson 
38aa0a1e58SJeff Roberson static void sdp_nagle_timeout(void *data);
39aa0a1e58SJeff Roberson 
40aa0a1e58SJeff Roberson #ifdef CONFIG_INFINIBAND_SDP_DEBUG_DATA
_dump_packet(const char * func,int line,struct socket * sk,char * str,struct mbuf * mb,const struct sdp_bsdh * h)41aa0a1e58SJeff Roberson void _dump_packet(const char *func, int line, struct socket *sk, char *str,
42aa0a1e58SJeff Roberson 		struct mbuf *mb, const struct sdp_bsdh *h)
43aa0a1e58SJeff Roberson {
44aa0a1e58SJeff Roberson 	struct sdp_hh *hh;
45aa0a1e58SJeff Roberson 	struct sdp_hah *hah;
46aa0a1e58SJeff Roberson 	struct sdp_chrecvbuf *req_size;
47aa0a1e58SJeff Roberson 	struct sdp_rrch *rrch;
48aa0a1e58SJeff Roberson 	struct sdp_srcah *srcah;
49aa0a1e58SJeff Roberson 	int len = 0;
50aa0a1e58SJeff Roberson 	char buf[256];
51aa0a1e58SJeff Roberson 	len += snprintf(buf, 255-len, "%s mb: %p mid: %2x:%-20s flags: 0x%x "
52aa0a1e58SJeff Roberson 			"bufs: 0x%x len: 0x%x mseq: 0x%x mseq_ack: 0x%x | ",
53aa0a1e58SJeff Roberson 			str, mb, h->mid, mid2str(h->mid), h->flags,
54aa0a1e58SJeff Roberson 			ntohs(h->bufs), ntohl(h->len), ntohl(h->mseq),
55aa0a1e58SJeff Roberson 			ntohl(h->mseq_ack));
56aa0a1e58SJeff Roberson 
57aa0a1e58SJeff Roberson 	switch (h->mid) {
58aa0a1e58SJeff Roberson 	case SDP_MID_HELLO:
59aa0a1e58SJeff Roberson 		hh = (struct sdp_hh *)h;
60aa0a1e58SJeff Roberson 		len += snprintf(buf + len, 255-len,
61aa0a1e58SJeff Roberson 				"max_adverts: %d  majv_minv: 0x%x "
62aa0a1e58SJeff Roberson 				"localrcvsz: 0x%x desremrcvsz: 0x%x |",
63aa0a1e58SJeff Roberson 				hh->max_adverts, hh->majv_minv,
64aa0a1e58SJeff Roberson 				ntohl(hh->localrcvsz),
65aa0a1e58SJeff Roberson 				ntohl(hh->desremrcvsz));
66aa0a1e58SJeff Roberson 		break;
67aa0a1e58SJeff Roberson 	case SDP_MID_HELLO_ACK:
68aa0a1e58SJeff Roberson 		hah = (struct sdp_hah *)h;
69aa0a1e58SJeff Roberson 		len += snprintf(buf + len, 255-len, "actrcvz: 0x%x |",
70aa0a1e58SJeff Roberson 				ntohl(hah->actrcvsz));
71aa0a1e58SJeff Roberson 		break;
72aa0a1e58SJeff Roberson 	case SDP_MID_CHRCVBUF:
73aa0a1e58SJeff Roberson 	case SDP_MID_CHRCVBUF_ACK:
74aa0a1e58SJeff Roberson 		req_size = (struct sdp_chrecvbuf *)(h+1);
75aa0a1e58SJeff Roberson 		len += snprintf(buf + len, 255-len, "req_size: 0x%x |",
76aa0a1e58SJeff Roberson 				ntohl(req_size->size));
77aa0a1e58SJeff Roberson 		break;
78aa0a1e58SJeff Roberson 	case SDP_MID_DATA:
79aa0a1e58SJeff Roberson 		len += snprintf(buf + len, 255-len, "data_len: 0x%lx |",
80aa0a1e58SJeff Roberson 			ntohl(h->len) - sizeof(struct sdp_bsdh));
81aa0a1e58SJeff Roberson 		break;
82aa0a1e58SJeff Roberson 	case SDP_MID_RDMARDCOMPL:
83aa0a1e58SJeff Roberson 		rrch = (struct sdp_rrch *)(h+1);
84aa0a1e58SJeff Roberson 
85aa0a1e58SJeff Roberson 		len += snprintf(buf + len, 255-len, " | len: 0x%x |",
86aa0a1e58SJeff Roberson 				ntohl(rrch->len));
87aa0a1e58SJeff Roberson 		break;
88aa0a1e58SJeff Roberson 	case SDP_MID_SRCAVAIL:
89aa0a1e58SJeff Roberson 		srcah = (struct sdp_srcah *)(h+1);
90aa0a1e58SJeff Roberson 
91aa0a1e58SJeff Roberson 		len += snprintf(buf + len, 255-len, " | payload: 0x%lx, "
92aa0a1e58SJeff Roberson 				"len: 0x%x, rkey: 0x%x, vaddr: 0x%jx |",
93aa0a1e58SJeff Roberson 				ntohl(h->len) - sizeof(struct sdp_bsdh) -
94aa0a1e58SJeff Roberson 				sizeof(struct sdp_srcah),
95aa0a1e58SJeff Roberson 				ntohl(srcah->len), ntohl(srcah->rkey),
96aa0a1e58SJeff Roberson 				be64_to_cpu(srcah->vaddr));
97aa0a1e58SJeff Roberson 		break;
98aa0a1e58SJeff Roberson 	default:
99aa0a1e58SJeff Roberson 		break;
100aa0a1e58SJeff Roberson 	}
101aa0a1e58SJeff Roberson 	buf[len] = 0;
102aa0a1e58SJeff Roberson 	_sdp_printk(func, line, KERN_WARNING, sk, "%s: %s\n", str, buf);
103aa0a1e58SJeff Roberson }
104aa0a1e58SJeff Roberson #endif
105aa0a1e58SJeff Roberson 
106aa0a1e58SJeff Roberson static inline int
sdp_nagle_off(struct sdp_sock * ssk,struct mbuf * mb)107aa0a1e58SJeff Roberson sdp_nagle_off(struct sdp_sock *ssk, struct mbuf *mb)
108aa0a1e58SJeff Roberson {
109aa0a1e58SJeff Roberson 
110aa0a1e58SJeff Roberson 	struct sdp_bsdh *h;
111aa0a1e58SJeff Roberson 
112aa0a1e58SJeff Roberson 	h = mtod(mb, struct sdp_bsdh *);
113aa0a1e58SJeff Roberson 	int send_now =
114aa0a1e58SJeff Roberson #ifdef SDP_ZCOPY
115aa0a1e58SJeff Roberson 		BZCOPY_STATE(mb) ||
116aa0a1e58SJeff Roberson #endif
117aa0a1e58SJeff Roberson 		unlikely(h->mid != SDP_MID_DATA) ||
118aa0a1e58SJeff Roberson 		(ssk->flags & SDP_NODELAY) ||
119aa0a1e58SJeff Roberson 		!ssk->nagle_last_unacked ||
120aa0a1e58SJeff Roberson 		mb->m_pkthdr.len >= ssk->xmit_size_goal / 4 ||
121aa0a1e58SJeff Roberson 		(mb->m_flags & M_PUSH);
122aa0a1e58SJeff Roberson 
123aa0a1e58SJeff Roberson 	if (send_now) {
124aa0a1e58SJeff Roberson 		unsigned long mseq = ring_head(ssk->tx_ring);
125aa0a1e58SJeff Roberson 		ssk->nagle_last_unacked = mseq;
126aa0a1e58SJeff Roberson 	} else {
127aa0a1e58SJeff Roberson 		if (!callout_pending(&ssk->nagle_timer)) {
128aa0a1e58SJeff Roberson 			callout_reset(&ssk->nagle_timer, SDP_NAGLE_TIMEOUT,
129aa0a1e58SJeff Roberson 			    sdp_nagle_timeout, ssk);
130aa0a1e58SJeff Roberson 			sdp_dbg_data(ssk->socket, "Starting nagle timer\n");
131aa0a1e58SJeff Roberson 		}
132aa0a1e58SJeff Roberson 	}
133aa0a1e58SJeff Roberson 	sdp_dbg_data(ssk->socket, "send_now = %d last_unacked = %ld\n",
134aa0a1e58SJeff Roberson 		send_now, ssk->nagle_last_unacked);
135aa0a1e58SJeff Roberson 
136aa0a1e58SJeff Roberson 	return send_now;
137aa0a1e58SJeff Roberson }
138aa0a1e58SJeff Roberson 
139aa0a1e58SJeff Roberson static void
sdp_nagle_timeout(void * data)140aa0a1e58SJeff Roberson sdp_nagle_timeout(void *data)
141aa0a1e58SJeff Roberson {
142aa0a1e58SJeff Roberson 	struct sdp_sock *ssk = (struct sdp_sock *)data;
143aa0a1e58SJeff Roberson 	struct socket *sk = ssk->socket;
144aa0a1e58SJeff Roberson 
145aa0a1e58SJeff Roberson 	sdp_dbg_data(sk, "last_unacked = %ld\n", ssk->nagle_last_unacked);
146aa0a1e58SJeff Roberson 
147aa0a1e58SJeff Roberson 	if (!callout_active(&ssk->nagle_timer))
148aa0a1e58SJeff Roberson 		return;
149aa0a1e58SJeff Roberson 	callout_deactivate(&ssk->nagle_timer);
150aa0a1e58SJeff Roberson 
151aa0a1e58SJeff Roberson 	if (!ssk->nagle_last_unacked)
152aa0a1e58SJeff Roberson 		goto out;
153aa0a1e58SJeff Roberson 	if (ssk->state == TCPS_CLOSED)
154aa0a1e58SJeff Roberson 		return;
155aa0a1e58SJeff Roberson 	ssk->nagle_last_unacked = 0;
156eb1b1807SGleb Smirnoff 	sdp_post_sends(ssk, M_NOWAIT);
157aa0a1e58SJeff Roberson 
158aa0a1e58SJeff Roberson 	sowwakeup(ssk->socket);
159aa0a1e58SJeff Roberson out:
160aa0a1e58SJeff Roberson 	if (sk->so_snd.sb_sndptr)
161aa0a1e58SJeff Roberson 		callout_reset(&ssk->nagle_timer, SDP_NAGLE_TIMEOUT,
162aa0a1e58SJeff Roberson 		    sdp_nagle_timeout, ssk);
163aa0a1e58SJeff Roberson }
164aa0a1e58SJeff Roberson 
165aa0a1e58SJeff Roberson void
sdp_post_sends(struct sdp_sock * ssk,int wait)166aa0a1e58SJeff Roberson sdp_post_sends(struct sdp_sock *ssk, int wait)
167aa0a1e58SJeff Roberson {
168aa0a1e58SJeff Roberson 	struct mbuf *mb;
169aa0a1e58SJeff Roberson 	int post_count = 0;
170aa0a1e58SJeff Roberson 	struct socket *sk;
171aa0a1e58SJeff Roberson 	int low;
172aa0a1e58SJeff Roberson 
173aa0a1e58SJeff Roberson 	sk = ssk->socket;
174aa0a1e58SJeff Roberson 	if (unlikely(!ssk->id)) {
175aa0a1e58SJeff Roberson 		if (sk->so_snd.sb_sndptr) {
176aa0a1e58SJeff Roberson 			sdp_dbg(ssk->socket,
177aa0a1e58SJeff Roberson 				"Send on socket without cmid ECONNRESET.\n");
178aa0a1e58SJeff Roberson 			sdp_notify(ssk, ECONNRESET);
179aa0a1e58SJeff Roberson 		}
180aa0a1e58SJeff Roberson 		return;
181aa0a1e58SJeff Roberson 	}
182aa0a1e58SJeff Roberson again:
183aa0a1e58SJeff Roberson 	if (sdp_tx_ring_slots_left(ssk) < SDP_TX_SIZE / 2)
184aa0a1e58SJeff Roberson 		sdp_xmit_poll(ssk,  1);
185aa0a1e58SJeff Roberson 
186aa0a1e58SJeff Roberson 	if (ssk->recv_request &&
187aa0a1e58SJeff Roberson 	    ring_tail(ssk->rx_ring) >= ssk->recv_request_head &&
188aa0a1e58SJeff Roberson 	    tx_credits(ssk) >= SDP_MIN_TX_CREDITS &&
189aa0a1e58SJeff Roberson 	    sdp_tx_ring_slots_left(ssk)) {
190aa0a1e58SJeff Roberson 		mb = sdp_alloc_mb_chrcvbuf_ack(sk,
191aa0a1e58SJeff Roberson 		    ssk->recv_bytes - SDP_HEAD_SIZE, wait);
192aa0a1e58SJeff Roberson 		if (mb == NULL)
193aa0a1e58SJeff Roberson 			goto allocfail;
194aa0a1e58SJeff Roberson 		ssk->recv_request = 0;
195aa0a1e58SJeff Roberson 		sdp_post_send(ssk, mb);
196aa0a1e58SJeff Roberson 		post_count++;
197aa0a1e58SJeff Roberson 	}
198aa0a1e58SJeff Roberson 
199aa0a1e58SJeff Roberson 	if (tx_credits(ssk) <= SDP_MIN_TX_CREDITS &&
200aa0a1e58SJeff Roberson 	    sdp_tx_ring_slots_left(ssk) && sk->so_snd.sb_sndptr &&
201aa0a1e58SJeff Roberson 	    sdp_nagle_off(ssk, sk->so_snd.sb_sndptr)) {
202aa0a1e58SJeff Roberson 		SDPSTATS_COUNTER_INC(send_miss_no_credits);
203aa0a1e58SJeff Roberson 	}
204aa0a1e58SJeff Roberson 
205aa0a1e58SJeff Roberson 	while (tx_credits(ssk) > SDP_MIN_TX_CREDITS &&
206aa0a1e58SJeff Roberson 	    sdp_tx_ring_slots_left(ssk) && (mb = sk->so_snd.sb_sndptr) &&
207aa0a1e58SJeff Roberson 	    sdp_nagle_off(ssk, mb)) {
208aa0a1e58SJeff Roberson 		struct mbuf *n;
209aa0a1e58SJeff Roberson 
210aa0a1e58SJeff Roberson 		SOCKBUF_LOCK(&sk->so_snd);
211aa0a1e58SJeff Roberson 		sk->so_snd.sb_sndptr = mb->m_nextpkt;
212aa0a1e58SJeff Roberson 		sk->so_snd.sb_mb = mb->m_nextpkt;
213aa0a1e58SJeff Roberson 		mb->m_nextpkt = NULL;
214aa0a1e58SJeff Roberson 		SB_EMPTY_FIXUP(&sk->so_snd);
215aa0a1e58SJeff Roberson 		for (n = mb; n != NULL; n = n->m_next)
216aa0a1e58SJeff Roberson 			sbfree(&sk->so_snd, n);
217aa0a1e58SJeff Roberson 		SOCKBUF_UNLOCK(&sk->so_snd);
218aa0a1e58SJeff Roberson 		sdp_post_send(ssk, mb);
219aa0a1e58SJeff Roberson 		post_count++;
220aa0a1e58SJeff Roberson 	}
221aa0a1e58SJeff Roberson 
222aa0a1e58SJeff Roberson 	if (credit_update_needed(ssk) && ssk->state >= TCPS_ESTABLISHED &&
223aa0a1e58SJeff Roberson 	    ssk->state < TCPS_FIN_WAIT_2) {
224aa0a1e58SJeff Roberson 		mb = sdp_alloc_mb_data(ssk->socket, wait);
225aa0a1e58SJeff Roberson 		if (mb == NULL)
226aa0a1e58SJeff Roberson 			goto allocfail;
227aa0a1e58SJeff Roberson 		sdp_post_send(ssk, mb);
228aa0a1e58SJeff Roberson 
229aa0a1e58SJeff Roberson 		SDPSTATS_COUNTER_INC(post_send_credits);
230aa0a1e58SJeff Roberson 		post_count++;
231aa0a1e58SJeff Roberson 	}
232aa0a1e58SJeff Roberson 
233aa0a1e58SJeff Roberson 	/* send DisConn if needed
234aa0a1e58SJeff Roberson 	 * Do not send DisConn if there is only 1 credit. Compliance with CA4-82
235aa0a1e58SJeff Roberson 	 * If one credit is available, an implementation shall only send SDP
236aa0a1e58SJeff Roberson 	 * messages that provide additional credits and also do not contain ULP
237aa0a1e58SJeff Roberson 	 * payload. */
238aa0a1e58SJeff Roberson 	if ((ssk->flags & SDP_NEEDFIN) && !sk->so_snd.sb_sndptr &&
239aa0a1e58SJeff Roberson 	    tx_credits(ssk) > 1) {
240aa0a1e58SJeff Roberson 		mb = sdp_alloc_mb_disconnect(sk, wait);
241aa0a1e58SJeff Roberson 		if (mb == NULL)
242aa0a1e58SJeff Roberson 			goto allocfail;
243aa0a1e58SJeff Roberson 		ssk->flags &= ~SDP_NEEDFIN;
244aa0a1e58SJeff Roberson 		sdp_post_send(ssk, mb);
245aa0a1e58SJeff Roberson 		post_count++;
246aa0a1e58SJeff Roberson 	}
247aa0a1e58SJeff Roberson 	low = (sdp_tx_ring_slots_left(ssk) <= SDP_MIN_TX_CREDITS);
248aa0a1e58SJeff Roberson 	if (post_count || low) {
249aa0a1e58SJeff Roberson 		if (low)
250aa0a1e58SJeff Roberson 			sdp_arm_tx_cq(ssk);
251aa0a1e58SJeff Roberson 		if (sdp_xmit_poll(ssk, low))
252aa0a1e58SJeff Roberson 			goto again;
253aa0a1e58SJeff Roberson 	}
254aa0a1e58SJeff Roberson 	return;
255aa0a1e58SJeff Roberson 
256aa0a1e58SJeff Roberson allocfail:
257aa0a1e58SJeff Roberson 	ssk->nagle_last_unacked = -1;
258aa0a1e58SJeff Roberson 	callout_reset(&ssk->nagle_timer, 1, sdp_nagle_timeout, ssk);
259aa0a1e58SJeff Roberson 	return;
260aa0a1e58SJeff Roberson }
261