1237263Snp/*-
2237263Snp * Copyright (c) 2012 Chelsio Communications, Inc.
3237263Snp * All rights reserved.
4237263Snp * Written by: Navdeep Parhar <np@FreeBSD.org>
5237263Snp *
6237263Snp * Redistribution and use in source and binary forms, with or without
7237263Snp * modification, are permitted provided that the following conditions
8237263Snp * are met:
9237263Snp * 1. Redistributions of source code must retain the above copyright
10237263Snp *    notice, this list of conditions and the following disclaimer.
11237263Snp * 2. Redistributions in binary form must reproduce the above copyright
12237263Snp *    notice, this list of conditions and the following disclaimer in the
13237263Snp *    documentation and/or other materials provided with the distribution.
14237263Snp *
15237263Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16237263Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17237263Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18237263Snp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19237263Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20237263Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21237263Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22237263Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23237263Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24237263Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25237263Snp * SUCH DAMAGE.
26237263Snp */
27237263Snp
28237263Snp#include <sys/cdefs.h>
29237263Snp__FBSDID("$FreeBSD$");
30237263Snp
31237263Snp#include "opt_inet.h"
32237263Snp
33237263Snp#ifdef TCP_OFFLOAD
34237263Snp#include <sys/param.h>
35237263Snp#include <sys/types.h>
36237263Snp#include <sys/kernel.h>
37237263Snp#include <sys/ktr.h>
38237263Snp#include <sys/module.h>
39237263Snp#include <sys/protosw.h>
40237263Snp#include <sys/domain.h>
41237263Snp#include <sys/socket.h>
42237263Snp#include <sys/socketvar.h>
43237263Snp#include <sys/sglist.h>
44237263Snp#include <netinet/in.h>
45237263Snp#include <netinet/in_pcb.h>
46237263Snp#include <netinet/ip.h>
47237263Snp#include <netinet/tcp_var.h>
48237263Snp#define TCPSTATES
49237263Snp#include <netinet/tcp_fsm.h>
50237263Snp#include <netinet/tcp_seq.h>
51237263Snp#include <netinet/toecore.h>
52237263Snp
53237263Snp#include "common/common.h"
54237263Snp#include "common/t4_msg.h"
55237263Snp#include "common/t4_regs.h"
56239344Snp#include "common/t4_tcb.h"
57237263Snp#include "tom/t4_tom_l2t.h"
58237263Snp#include "tom/t4_tom.h"
59237263Snp
60237263SnpVNET_DECLARE(int, tcp_do_autosndbuf);
61237263Snp#define V_tcp_do_autosndbuf VNET(tcp_do_autosndbuf)
62237263SnpVNET_DECLARE(int, tcp_autosndbuf_inc);
63237263Snp#define V_tcp_autosndbuf_inc VNET(tcp_autosndbuf_inc)
64237263SnpVNET_DECLARE(int, tcp_autosndbuf_max);
65237263Snp#define V_tcp_autosndbuf_max VNET(tcp_autosndbuf_max)
66237263SnpVNET_DECLARE(int, tcp_do_autorcvbuf);
67237263Snp#define V_tcp_do_autorcvbuf VNET(tcp_do_autorcvbuf)
68237263SnpVNET_DECLARE(int, tcp_autorcvbuf_inc);
69237263Snp#define V_tcp_autorcvbuf_inc VNET(tcp_autorcvbuf_inc)
70237263SnpVNET_DECLARE(int, tcp_autorcvbuf_max);
71237263Snp#define V_tcp_autorcvbuf_max VNET(tcp_autorcvbuf_max)
72237263Snp
73237263Snpvoid
74237263Snpsend_flowc_wr(struct toepcb *toep, struct flowc_tx_params *ftxp)
75237263Snp{
76241626Snp	struct wrqe *wr;
77241626Snp	struct fw_flowc_wr *flowc;
78241642Snp	unsigned int nparams = ftxp ? 8 : 6, flowclen;
79237263Snp	struct port_info *pi = toep->port;
80237263Snp	struct adapter *sc = pi->adapter;
81237263Snp	unsigned int pfvf = G_FW_VIID_PFN(pi->viid) << S_FW_VIID_PFN;
82237263Snp	struct ofld_tx_sdesc *txsd = &toep->txsd[toep->txsd_pidx];
83237263Snp
84239514Snp	KASSERT(!(toep->flags & TPF_FLOWC_WR_SENT),
85237263Snp	    ("%s: flowc for tid %u sent already", __func__, toep->tid));
86237263Snp
87237263Snp	CTR2(KTR_CXGBE, "%s: tid %u", __func__, toep->tid);
88237263Snp
89237263Snp	flowclen = sizeof(*flowc) + nparams * sizeof(struct fw_flowc_mnemval);
90237263Snp
91248925Snp	wr = alloc_wrqe(roundup2(flowclen, 16), toep->ofld_txq);
92237263Snp	if (wr == NULL) {
93237263Snp		/* XXX */
94237263Snp		panic("%s: allocation failure.", __func__);
95237263Snp	}
96237263Snp	flowc = wrtod(wr);
97237263Snp	memset(flowc, 0, wr->wr_len);
98237263Snp
99237263Snp	flowc->op_to_nparams = htobe32(V_FW_WR_OP(FW_FLOWC_WR) |
100237263Snp	    V_FW_FLOWC_WR_NPARAMS(nparams));
101237263Snp	flowc->flowid_len16 = htonl(V_FW_WR_LEN16(howmany(flowclen, 16)) |
102237263Snp	    V_FW_WR_FLOWID(toep->tid));
103237263Snp
104237263Snp	flowc->mnemval[0].mnemonic = FW_FLOWC_MNEM_PFNVFN;
105241626Snp	flowc->mnemval[0].val = htobe32(pfvf);
106241626Snp	flowc->mnemval[1].mnemonic = FW_FLOWC_MNEM_CH;
107241626Snp	flowc->mnemval[1].val = htobe32(pi->tx_chan);
108241626Snp	flowc->mnemval[2].mnemonic = FW_FLOWC_MNEM_PORT;
109241626Snp	flowc->mnemval[2].val = htobe32(pi->tx_chan);
110241626Snp	flowc->mnemval[3].mnemonic = FW_FLOWC_MNEM_IQID;
111241626Snp	flowc->mnemval[3].val = htobe32(toep->ofld_rxq->iq.abs_id);
112237263Snp	if (ftxp) {
113237263Snp		uint32_t sndbuf = min(ftxp->snd_space, sc->tt.sndbuf);
114237263Snp
115237263Snp		flowc->mnemval[4].mnemonic = FW_FLOWC_MNEM_SNDNXT;
116237263Snp		flowc->mnemval[4].val = htobe32(ftxp->snd_nxt);
117237263Snp		flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT;
118237263Snp		flowc->mnemval[5].val = htobe32(ftxp->rcv_nxt);
119237263Snp		flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF;
120237263Snp		flowc->mnemval[6].val = htobe32(sndbuf);
121237263Snp		flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS;
122237263Snp		flowc->mnemval[7].val = htobe32(ftxp->mss);
123241642Snp	} else {
124241642Snp		flowc->mnemval[4].mnemonic = FW_FLOWC_MNEM_SNDBUF;
125241642Snp		flowc->mnemval[4].val = htobe32(512);
126241642Snp		flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_MSS;
127241642Snp		flowc->mnemval[5].val = htobe32(512);
128237263Snp	}
129237263Snp
130237263Snp	txsd->tx_credits = howmany(flowclen, 16);
131237263Snp	txsd->plen = 0;
132237263Snp	KASSERT(toep->tx_credits >= txsd->tx_credits && toep->txsd_avail > 0,
133237263Snp	    ("%s: not enough credits (%d)", __func__, toep->tx_credits));
134237263Snp	toep->tx_credits -= txsd->tx_credits;
135237263Snp	if (__predict_false(++toep->txsd_pidx == toep->txsd_total))
136237263Snp		toep->txsd_pidx = 0;
137237263Snp	toep->txsd_avail--;
138237263Snp
139239514Snp	toep->flags |= TPF_FLOWC_WR_SENT;
140237263Snp        t4_wrq_tx(sc, wr);
141237263Snp}
142237263Snp
143237263Snpvoid
144237263Snpsend_reset(struct adapter *sc, struct toepcb *toep, uint32_t snd_nxt)
145237263Snp{
146237263Snp	struct wrqe *wr;
147237263Snp	struct cpl_abort_req *req;
148237263Snp	int tid = toep->tid;
149237263Snp	struct inpcb *inp = toep->inp;
150237263Snp	struct tcpcb *tp = intotcpcb(inp);	/* don't use if INP_DROPPED */
151237263Snp
152237263Snp	INP_WLOCK_ASSERT(inp);
153237263Snp
154237263Snp	CTR6(KTR_CXGBE, "%s: tid %d (%s), toep_flags 0x%x, inp_flags 0x%x%s",
155237263Snp	    __func__, toep->tid,
156237263Snp	    inp->inp_flags & INP_DROPPED ? "inp dropped" :
157237263Snp	    tcpstates[tp->t_state],
158237263Snp	    toep->flags, inp->inp_flags,
159239514Snp	    toep->flags & TPF_ABORT_SHUTDOWN ?
160237263Snp	    " (abort already in progress)" : "");
161237263Snp
162239514Snp	if (toep->flags & TPF_ABORT_SHUTDOWN)
163237263Snp		return;	/* abort already in progress */
164237263Snp
165239514Snp	toep->flags |= TPF_ABORT_SHUTDOWN;
166237263Snp
167239514Snp	KASSERT(toep->flags & TPF_FLOWC_WR_SENT,
168237263Snp	    ("%s: flowc_wr not sent for tid %d.", __func__, tid));
169237263Snp
170237263Snp	wr = alloc_wrqe(sizeof(*req), toep->ofld_txq);
171237263Snp	if (wr == NULL) {
172237263Snp		/* XXX */
173237263Snp		panic("%s: allocation failure.", __func__);
174237263Snp	}
175237263Snp	req = wrtod(wr);
176237263Snp
177237263Snp	INIT_TP_WR_MIT_CPL(req, CPL_ABORT_REQ, tid);
178237263Snp	if (inp->inp_flags & INP_DROPPED)
179237263Snp		req->rsvd0 = htobe32(snd_nxt);
180237263Snp	else
181237263Snp		req->rsvd0 = htobe32(tp->snd_nxt);
182239514Snp	req->rsvd1 = !(toep->flags & TPF_TX_DATA_SENT);
183237263Snp	req->cmd = CPL_ABORT_SEND_RST;
184237263Snp
185237263Snp	/*
186237263Snp	 * XXX: What's the correct way to tell that the inp hasn't been detached
187237263Snp	 * from its socket?  Should I even be flushing the snd buffer here?
188237263Snp	 */
189237263Snp	if ((inp->inp_flags & (INP_DROPPED | INP_TIMEWAIT)) == 0) {
190237263Snp		struct socket *so = inp->inp_socket;
191237263Snp
192237263Snp		if (so != NULL)	/* because I'm not sure.  See comment above */
193237263Snp			sbflush(&so->so_snd);
194237263Snp	}
195237263Snp
196237263Snp	t4_l2t_send(sc, wr, toep->l2te);
197237263Snp}
198237263Snp
199237263Snp/*
200237263Snp * Called when a connection is established to translate the TCP options
201237263Snp * reported by HW to FreeBSD's native format.
202237263Snp */
203237263Snpstatic void
204237263Snpassign_rxopt(struct tcpcb *tp, unsigned int opt)
205237263Snp{
206237263Snp	struct toepcb *toep = tp->t_toe;
207237263Snp	struct adapter *sc = td_adapter(toep->td);
208237263Snp
209237263Snp	INP_LOCK_ASSERT(tp->t_inpcb);
210237263Snp
211237263Snp	tp->t_maxseg = tp->t_maxopd = sc->params.mtus[G_TCPOPT_MSS(opt)] - 40;
212237263Snp
213237263Snp	if (G_TCPOPT_TSTAMP(opt)) {
214237263Snp		tp->t_flags |= TF_RCVD_TSTMP;	/* timestamps ok */
215237263Snp		tp->ts_recent = 0;		/* hmmm */
216237263Snp		tp->ts_recent_age = tcp_ts_getticks();
217237263Snp		tp->t_maxseg -= TCPOLEN_TSTAMP_APPA;
218237263Snp	}
219237263Snp
220237263Snp	if (G_TCPOPT_SACK(opt))
221237263Snp		tp->t_flags |= TF_SACK_PERMIT;	/* should already be set */
222237263Snp	else
223237263Snp		tp->t_flags &= ~TF_SACK_PERMIT;	/* sack disallowed by peer */
224237263Snp
225237263Snp	if (G_TCPOPT_WSCALE_OK(opt))
226237263Snp		tp->t_flags |= TF_RCVD_SCALE;
227237263Snp
228237263Snp	/* Doing window scaling? */
229237263Snp	if ((tp->t_flags & (TF_RCVD_SCALE | TF_REQ_SCALE)) ==
230237263Snp	    (TF_RCVD_SCALE | TF_REQ_SCALE)) {
231237263Snp		tp->rcv_scale = tp->request_r_scale;
232237263Snp		tp->snd_scale = G_TCPOPT_SND_WSCALE(opt);
233237263Snp	}
234237263Snp}
235237263Snp
236237263Snp/*
237237263Snp * Completes some final bits of initialization for just established connections
238237263Snp * and changes their state to TCPS_ESTABLISHED.
239237263Snp *
240237263Snp * The ISNs are from after the exchange of SYNs.  i.e., the true ISN + 1.
241237263Snp */
242237263Snpvoid
243237263Snpmake_established(struct toepcb *toep, uint32_t snd_isn, uint32_t rcv_isn,
244237263Snp    uint16_t opt)
245237263Snp{
246237263Snp	struct inpcb *inp = toep->inp;
247237263Snp	struct socket *so = inp->inp_socket;
248237263Snp	struct tcpcb *tp = intotcpcb(inp);
249237263Snp	long bufsize;
250237263Snp	uint32_t iss = be32toh(snd_isn) - 1;	/* true ISS */
251237263Snp	uint32_t irs = be32toh(rcv_isn) - 1;	/* true IRS */
252237263Snp	uint16_t tcpopt = be16toh(opt);
253237263Snp	struct flowc_tx_params ftxp;
254237263Snp
255237263Snp	INP_WLOCK_ASSERT(inp);
256237263Snp	KASSERT(tp->t_state == TCPS_SYN_SENT ||
257237263Snp	    tp->t_state == TCPS_SYN_RECEIVED,
258237263Snp	    ("%s: TCP state %s", __func__, tcpstates[tp->t_state]));
259237263Snp
260237263Snp	CTR4(KTR_CXGBE, "%s: tid %d, toep %p, inp %p",
261237263Snp	    __func__, toep->tid, toep, inp);
262237263Snp
263237263Snp	tp->t_state = TCPS_ESTABLISHED;
264237263Snp	tp->t_starttime = ticks;
265237263Snp	TCPSTAT_INC(tcps_connects);
266237263Snp
267237263Snp	tp->irs = irs;
268237263Snp	tcp_rcvseqinit(tp);
269237263Snp	tp->rcv_wnd = toep->rx_credits << 10;
270237263Snp	tp->rcv_adv += tp->rcv_wnd;
271237263Snp	tp->last_ack_sent = tp->rcv_nxt;
272237263Snp
273237263Snp	/*
274237263Snp	 * If we were unable to send all rx credits via opt0, save the remainder
275237263Snp	 * in rx_credits so that they can be handed over with the next credit
276237263Snp	 * update.
277237263Snp	 */
278237263Snp	SOCKBUF_LOCK(&so->so_rcv);
279237263Snp	bufsize = select_rcv_wnd(so);
280237263Snp	SOCKBUF_UNLOCK(&so->so_rcv);
281237263Snp	toep->rx_credits = bufsize - tp->rcv_wnd;
282237263Snp
283237263Snp	tp->iss = iss;
284237263Snp	tcp_sendseqinit(tp);
285237263Snp	tp->snd_una = iss + 1;
286237263Snp	tp->snd_nxt = iss + 1;
287237263Snp	tp->snd_max = iss + 1;
288237263Snp
289237263Snp	assign_rxopt(tp, tcpopt);
290237263Snp
291237263Snp	SOCKBUF_LOCK(&so->so_snd);
292237263Snp	if (so->so_snd.sb_flags & SB_AUTOSIZE && V_tcp_do_autosndbuf)
293237263Snp		bufsize = V_tcp_autosndbuf_max;
294237263Snp	else
295237263Snp		bufsize = sbspace(&so->so_snd);
296237263Snp	SOCKBUF_UNLOCK(&so->so_snd);
297237263Snp
298237263Snp	ftxp.snd_nxt = tp->snd_nxt;
299237263Snp	ftxp.rcv_nxt = tp->rcv_nxt;
300237263Snp	ftxp.snd_space = bufsize;
301237263Snp	ftxp.mss = tp->t_maxseg;
302237263Snp	send_flowc_wr(toep, &ftxp);
303237263Snp
304237263Snp	soisconnected(so);
305237263Snp}
306237263Snp
307237263Snpstatic int
308239344Snpsend_rx_credits(struct adapter *sc, struct toepcb *toep, int credits)
309237263Snp{
310237263Snp	struct wrqe *wr;
311237263Snp	struct cpl_rx_data_ack *req;
312237263Snp	uint32_t dack = F_RX_DACK_CHANGE | V_RX_DACK_MODE(1);
313237263Snp
314239344Snp	KASSERT(credits >= 0, ("%s: %d credits", __func__, credits));
315239344Snp
316237263Snp	wr = alloc_wrqe(sizeof(*req), toep->ctrlq);
317237263Snp	if (wr == NULL)
318237263Snp		return (0);
319237263Snp	req = wrtod(wr);
320237263Snp
321237263Snp	INIT_TP_WR_MIT_CPL(req, CPL_RX_DATA_ACK, toep->tid);
322237263Snp	req->credit_dack = htobe32(dack | V_RX_CREDITS(credits));
323237263Snp
324237263Snp	t4_wrq_tx(sc, wr);
325237263Snp	return (credits);
326237263Snp}
327237263Snp
328237263Snpvoid
329237263Snpt4_rcvd(struct toedev *tod, struct tcpcb *tp)
330237263Snp{
331237263Snp	struct adapter *sc = tod->tod_softc;
332237263Snp	struct inpcb *inp = tp->t_inpcb;
333237263Snp	struct socket *so = inp->inp_socket;
334239344Snp	struct sockbuf *sb = &so->so_rcv;
335237263Snp	struct toepcb *toep = tp->t_toe;
336239344Snp	int credits;
337237263Snp
338237263Snp	INP_WLOCK_ASSERT(inp);
339237263Snp
340239344Snp	SOCKBUF_LOCK(sb);
341239344Snp	KASSERT(toep->sb_cc >= sb->sb_cc,
342239344Snp	    ("%s: sb %p has more data (%d) than last time (%d).",
343239344Snp	    __func__, sb, sb->sb_cc, toep->sb_cc));
344239344Snp	toep->rx_credits += toep->sb_cc - sb->sb_cc;
345239344Snp	toep->sb_cc = sb->sb_cc;
346239344Snp	credits = toep->rx_credits;
347239344Snp	SOCKBUF_UNLOCK(sb);
348237263Snp
349239344Snp	if (credits > 0 &&
350239344Snp	    (credits + 16384 >= tp->rcv_wnd || credits >= 15 * 1024)) {
351237263Snp
352239344Snp		credits = send_rx_credits(sc, toep, credits);
353239344Snp		SOCKBUF_LOCK(sb);
354237263Snp		toep->rx_credits -= credits;
355239344Snp		SOCKBUF_UNLOCK(sb);
356237263Snp		tp->rcv_wnd += credits;
357237263Snp		tp->rcv_adv += credits;
358237263Snp	}
359237263Snp}
360237263Snp
361237263Snp/*
362237263Snp * Close a connection by sending a CPL_CLOSE_CON_REQ message.
363237263Snp */
364237263Snpstatic int
365237263Snpclose_conn(struct adapter *sc, struct toepcb *toep)
366237263Snp{
367237263Snp	struct wrqe *wr;
368237263Snp	struct cpl_close_con_req *req;
369237263Snp	unsigned int tid = toep->tid;
370237263Snp
371237263Snp	CTR3(KTR_CXGBE, "%s: tid %u%s", __func__, toep->tid,
372239514Snp	    toep->flags & TPF_FIN_SENT ? ", IGNORED" : "");
373237263Snp
374239514Snp	if (toep->flags & TPF_FIN_SENT)
375237263Snp		return (0);
376237263Snp
377239514Snp	KASSERT(toep->flags & TPF_FLOWC_WR_SENT,
378237263Snp	    ("%s: flowc_wr not sent for tid %u.", __func__, tid));
379237263Snp
380237263Snp	wr = alloc_wrqe(sizeof(*req), toep->ofld_txq);
381237263Snp	if (wr == NULL) {
382237263Snp		/* XXX */
383237263Snp		panic("%s: allocation failure.", __func__);
384237263Snp	}
385237263Snp	req = wrtod(wr);
386237263Snp
387237263Snp        req->wr.wr_hi = htonl(V_FW_WR_OP(FW_TP_WR) |
388237263Snp	    V_FW_WR_IMMDLEN(sizeof(*req) - sizeof(req->wr)));
389237263Snp	req->wr.wr_mid = htonl(V_FW_WR_LEN16(howmany(sizeof(*req), 16)) |
390237263Snp	    V_FW_WR_FLOWID(tid));
391237263Snp        req->wr.wr_lo = cpu_to_be64(0);
392237263Snp        OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, tid));
393237263Snp	req->rsvd = 0;
394237263Snp
395239514Snp	toep->flags |= TPF_FIN_SENT;
396239514Snp	toep->flags &= ~TPF_SEND_FIN;
397237263Snp	t4_l2t_send(sc, wr, toep->l2te);
398237263Snp
399237263Snp	return (0);
400237263Snp}
401237263Snp
402237263Snp#define MAX_OFLD_TX_CREDITS (SGE_MAX_WR_LEN / 16)
403237263Snp#define MIN_OFLD_TX_CREDITS (howmany(sizeof(struct fw_ofld_tx_data_wr) + 1, 16))
404237263Snp
405237263Snp/* Maximum amount of immediate data we could stuff in a WR */
406237263Snpstatic inline int
407237263Snpmax_imm_payload(int tx_credits)
408237263Snp{
409237263Snp	const int n = 2;	/* Use only up to 2 desc for imm. data WR */
410237263Snp
411237263Snp	KASSERT(tx_credits >= 0 &&
412237263Snp		tx_credits <= MAX_OFLD_TX_CREDITS,
413237263Snp		("%s: %d credits", __func__, tx_credits));
414237263Snp
415237263Snp	if (tx_credits < MIN_OFLD_TX_CREDITS)
416237263Snp		return (0);
417237263Snp
418237263Snp	if (tx_credits >= (n * EQ_ESIZE) / 16)
419237263Snp		return ((n * EQ_ESIZE) - sizeof(struct fw_ofld_tx_data_wr));
420237263Snp	else
421237263Snp		return (tx_credits * 16 - sizeof(struct fw_ofld_tx_data_wr));
422237263Snp}
423237263Snp
424237263Snp/* Maximum number of SGL entries we could stuff in a WR */
425237263Snpstatic inline int
426237263Snpmax_dsgl_nsegs(int tx_credits)
427237263Snp{
428237263Snp	int nseg = 1;	/* ulptx_sgl has room for 1, rest ulp_tx_sge_pair */
429237263Snp	int sge_pair_credits = tx_credits - MIN_OFLD_TX_CREDITS;
430237263Snp
431237263Snp	KASSERT(tx_credits >= 0 &&
432237263Snp		tx_credits <= MAX_OFLD_TX_CREDITS,
433237263Snp		("%s: %d credits", __func__, tx_credits));
434237263Snp
435237263Snp	if (tx_credits < MIN_OFLD_TX_CREDITS)
436237263Snp		return (0);
437237263Snp
438237263Snp	nseg += 2 * (sge_pair_credits * 16 / 24);
439237263Snp	if ((sge_pair_credits * 16) % 24 == 16)
440237263Snp		nseg++;
441237263Snp
442237263Snp	return (nseg);
443237263Snp}
444237263Snp
445237263Snpstatic inline void
446237263Snpwrite_tx_wr(void *dst, struct toepcb *toep, unsigned int immdlen,
447255411Snp    unsigned int plen, uint8_t credits, int shove)
448237263Snp{
449237263Snp	struct fw_ofld_tx_data_wr *txwr = dst;
450237263Snp
451237263Snp	txwr->op_to_immdlen = htobe32(V_WR_OP(FW_OFLD_TX_DATA_WR) |
452255411Snp	    V_FW_WR_IMMDLEN(immdlen));
453237263Snp	txwr->flowid_len16 = htobe32(V_FW_WR_FLOWID(toep->tid) |
454237263Snp	    V_FW_WR_LEN16(credits));
455256791Snp	txwr->lsodisable_to_proxy =
456237263Snp	    htobe32(V_FW_OFLD_TX_DATA_WR_ULPMODE(toep->ulp_mode) |
457237263Snp		V_FW_OFLD_TX_DATA_WR_URGENT(0) |	/* XXX */
458237263Snp		V_FW_OFLD_TX_DATA_WR_SHOVE(shove));
459237263Snp	txwr->plen = htobe32(plen);
460237263Snp}
461237263Snp
462237263Snp/*
463237263Snp * Generate a DSGL from a starting mbuf.  The total number of segments and the
464237263Snp * maximum segments in any one mbuf are provided.
465237263Snp */
466237263Snpstatic void
467237263Snpwrite_tx_sgl(void *dst, struct mbuf *start, struct mbuf *stop, int nsegs, int n)
468237263Snp{
469237263Snp	struct mbuf *m;
470237263Snp	struct ulptx_sgl *usgl = dst;
471237263Snp	int i, j, rc;
472237263Snp	struct sglist sg;
473237263Snp	struct sglist_seg segs[n];
474237263Snp
475237263Snp	KASSERT(nsegs > 0, ("%s: nsegs 0", __func__));
476237263Snp
477237263Snp	sglist_init(&sg, n, segs);
478237263Snp	usgl->cmd_nsge = htobe32(V_ULPTX_CMD(ULP_TX_SC_DSGL) |
479237263Snp	    V_ULPTX_NSGE(nsegs));
480237263Snp
481237263Snp	i = -1;
482237263Snp	for (m = start; m != stop; m = m->m_next) {
483237263Snp		rc = sglist_append(&sg, mtod(m, void *), m->m_len);
484237263Snp		if (__predict_false(rc != 0))
485237263Snp			panic("%s: sglist_append %d", __func__, rc);
486237263Snp
487237263Snp		for (j = 0; j < sg.sg_nseg; i++, j++) {
488237263Snp			if (i < 0) {
489237263Snp				usgl->len0 = htobe32(segs[j].ss_len);
490237263Snp				usgl->addr0 = htobe64(segs[j].ss_paddr);
491237263Snp			} else {
492237263Snp				usgl->sge[i / 2].len[i & 1] =
493237263Snp				    htobe32(segs[j].ss_len);
494237263Snp				usgl->sge[i / 2].addr[i & 1] =
495237263Snp				    htobe64(segs[j].ss_paddr);
496237263Snp			}
497237263Snp#ifdef INVARIANTS
498237263Snp			nsegs--;
499237263Snp#endif
500237263Snp		}
501237263Snp		sglist_reset(&sg);
502237263Snp	}
503237263Snp	if (i & 1)
504237263Snp		usgl->sge[i / 2].len[1] = htobe32(0);
505237263Snp	KASSERT(nsegs == 0, ("%s: nsegs %d, start %p, stop %p",
506237263Snp	    __func__, nsegs, start, stop));
507237263Snp}
508237263Snp
509237263Snp/*
510237263Snp * Max number of SGL entries an offload tx work request can have.  This is 41
511237263Snp * (1 + 40) for a full 512B work request.
512237263Snp * fw_ofld_tx_data_wr(16B) + ulptx_sgl(16B, 1) + ulptx_sge_pair(480B, 40)
513237263Snp */
514237263Snp#define OFLD_SGL_LEN (41)
515237263Snp
516237263Snp/*
517237263Snp * Send data and/or a FIN to the peer.
518237263Snp *
519237263Snp * The socket's so_snd buffer consists of a stream of data starting with sb_mb
520237263Snp * and linked together with m_next.  sb_sndptr, if set, is the last mbuf that
521237263Snp * was transmitted.
522255411Snp *
523255411Snp * drop indicates the number of bytes that should be dropped from the head of
524255411Snp * the send buffer.  It is an optimization that lets do_fw4_ack avoid creating
525255411Snp * contention on the send buffer lock (before this change it used to do
526255411Snp * sowwakeup and then t4_push_frames right after that when recovering from tx
527255411Snp * stalls).  When drop is set this function MUST drop the bytes and wake up any
528255411Snp * writers.
529237263Snp */
530237263Snpstatic void
531255411Snpt4_push_frames(struct adapter *sc, struct toepcb *toep, int drop)
532237263Snp{
533237263Snp	struct mbuf *sndptr, *m, *sb_sndptr;
534237263Snp	struct fw_ofld_tx_data_wr *txwr;
535237263Snp	struct wrqe *wr;
536255411Snp	u_int plen, nsegs, credits, max_imm, max_nsegs, max_nsegs_1mbuf;
537237263Snp	struct inpcb *inp = toep->inp;
538237263Snp	struct tcpcb *tp = intotcpcb(inp);
539237263Snp	struct socket *so = inp->inp_socket;
540237263Snp	struct sockbuf *sb = &so->so_snd;
541255411Snp	int tx_credits, shove, compl, space, sowwakeup;
542237263Snp	struct ofld_tx_sdesc *txsd = &toep->txsd[toep->txsd_pidx];
543237263Snp
544237263Snp	INP_WLOCK_ASSERT(inp);
545239514Snp	KASSERT(toep->flags & TPF_FLOWC_WR_SENT,
546237263Snp	    ("%s: flowc_wr not sent for tid %u.", __func__, toep->tid));
547237263Snp
548255005Snp	KASSERT(toep->ulp_mode == ULP_MODE_NONE ||
549255005Snp	    toep->ulp_mode == ULP_MODE_TCPDDP ||
550255005Snp	    toep->ulp_mode == ULP_MODE_RDMA,
551255005Snp	    ("%s: ulp_mode %u for toep %p", __func__, toep->ulp_mode, toep));
552237263Snp
553237263Snp	/*
554237263Snp	 * This function doesn't resume by itself.  Someone else must clear the
555237263Snp	 * flag and call this function.
556237263Snp	 */
557255411Snp	if (__predict_false(toep->flags & TPF_TX_SUSPENDED)) {
558255411Snp		KASSERT(drop == 0,
559255411Snp		    ("%s: drop (%d) != 0 but tx is suspended", __func__, drop));
560237263Snp		return;
561255411Snp	}
562237263Snp
563237263Snp	do {
564237263Snp		tx_credits = min(toep->tx_credits, MAX_OFLD_TX_CREDITS);
565237263Snp		max_imm = max_imm_payload(tx_credits);
566237263Snp		max_nsegs = max_dsgl_nsegs(tx_credits);
567237263Snp
568237263Snp		SOCKBUF_LOCK(sb);
569255411Snp		sowwakeup = drop;
570255411Snp		if (drop) {
571255411Snp			sbdrop_locked(sb, drop);
572255411Snp			drop = 0;
573255411Snp		}
574237263Snp		sb_sndptr = sb->sb_sndptr;
575237263Snp		sndptr = sb_sndptr ? sb_sndptr->m_next : sb->sb_mb;
576237263Snp		plen = 0;
577237263Snp		nsegs = 0;
578237263Snp		max_nsegs_1mbuf = 0; /* max # of SGL segments in any one mbuf */
579237263Snp		for (m = sndptr; m != NULL; m = m->m_next) {
580237263Snp			int n = sglist_count(mtod(m, void *), m->m_len);
581237263Snp
582237263Snp			nsegs += n;
583237263Snp			plen += m->m_len;
584237263Snp
585237263Snp			/* This mbuf sent us _over_ the nsegs limit, back out */
586237263Snp			if (plen > max_imm && nsegs > max_nsegs) {
587237263Snp				nsegs -= n;
588237263Snp				plen -= m->m_len;
589237263Snp				if (plen == 0) {
590237263Snp					/* Too few credits */
591239514Snp					toep->flags |= TPF_TX_SUSPENDED;
592255411Snp					if (sowwakeup)
593255411Snp						sowwakeup_locked(so);
594255411Snp					else
595255411Snp						SOCKBUF_UNLOCK(sb);
596255411Snp					SOCKBUF_UNLOCK_ASSERT(sb);
597237263Snp					return;
598237263Snp				}
599237263Snp				break;
600237263Snp			}
601237263Snp
602237263Snp			if (max_nsegs_1mbuf < n)
603237263Snp				max_nsegs_1mbuf = n;
604237263Snp			sb_sndptr = m;	/* new sb->sb_sndptr if all goes well */
605237263Snp
606237263Snp			/* This mbuf put us right at the max_nsegs limit */
607237263Snp			if (plen > max_imm && nsegs == max_nsegs) {
608237263Snp				m = m->m_next;
609237263Snp				break;
610237263Snp			}
611237263Snp		}
612237263Snp
613255411Snp		shove = m == NULL && !(tp->t_flags & TF_MORETOCOME);
614255411Snp		space = sbspace(sb);
615255411Snp
616255411Snp		if (space <= sb->sb_hiwat * 3 / 8 &&
617255411Snp		    toep->plen_nocompl + plen >= sb->sb_hiwat / 4)
618255411Snp			compl = 1;
619255411Snp		else
620255411Snp			compl = 0;
621255411Snp
622237263Snp		if (sb->sb_flags & SB_AUTOSIZE &&
623237263Snp		    V_tcp_do_autosndbuf &&
624237263Snp		    sb->sb_hiwat < V_tcp_autosndbuf_max &&
625255411Snp		    space < sb->sb_hiwat / 8) {
626237263Snp			int newsize = min(sb->sb_hiwat + V_tcp_autosndbuf_inc,
627237263Snp			    V_tcp_autosndbuf_max);
628237263Snp
629237263Snp			if (!sbreserve_locked(sb, newsize, so, NULL))
630237263Snp				sb->sb_flags &= ~SB_AUTOSIZE;
631255411Snp			else
632255411Snp				sowwakeup = 1;	/* room available */
633237263Snp		}
634255411Snp		if (sowwakeup)
635255411Snp			sowwakeup_locked(so);
636255411Snp		else
637255411Snp			SOCKBUF_UNLOCK(sb);
638255411Snp		SOCKBUF_UNLOCK_ASSERT(sb);
639237263Snp
640237263Snp		/* nothing to send */
641237263Snp		if (plen == 0) {
642237263Snp			KASSERT(m == NULL,
643237263Snp			    ("%s: nothing to send, but m != NULL", __func__));
644237263Snp			break;
645237263Snp		}
646237263Snp
647239514Snp		if (__predict_false(toep->flags & TPF_FIN_SENT))
648237263Snp			panic("%s: excess tx.", __func__);
649237263Snp
650237263Snp		if (plen <= max_imm) {
651237263Snp
652237263Snp			/* Immediate data tx */
653237263Snp
654248925Snp			wr = alloc_wrqe(roundup2(sizeof(*txwr) + plen, 16),
655237263Snp					toep->ofld_txq);
656237263Snp			if (wr == NULL) {
657237263Snp				/* XXX: how will we recover from this? */
658239514Snp				toep->flags |= TPF_TX_SUSPENDED;
659237263Snp				return;
660237263Snp			}
661237263Snp			txwr = wrtod(wr);
662237263Snp			credits = howmany(wr->wr_len, 16);
663255411Snp			write_tx_wr(txwr, toep, plen, plen, credits, shove);
664237263Snp			m_copydata(sndptr, 0, plen, (void *)(txwr + 1));
665255411Snp			nsegs = 0;
666237263Snp		} else {
667237263Snp			int wr_len;
668237263Snp
669237263Snp			/* DSGL tx */
670237263Snp
671237263Snp			wr_len = sizeof(*txwr) + sizeof(struct ulptx_sgl) +
672237263Snp			    ((3 * (nsegs - 1)) / 2 + ((nsegs - 1) & 1)) * 8;
673248925Snp			wr = alloc_wrqe(roundup2(wr_len, 16), toep->ofld_txq);
674237263Snp			if (wr == NULL) {
675237263Snp				/* XXX: how will we recover from this? */
676239514Snp				toep->flags |= TPF_TX_SUSPENDED;
677237263Snp				return;
678237263Snp			}
679237263Snp			txwr = wrtod(wr);
680237263Snp			credits = howmany(wr_len, 16);
681255411Snp			write_tx_wr(txwr, toep, 0, plen, credits, shove);
682237263Snp			write_tx_sgl(txwr + 1, sndptr, m, nsegs,
683237263Snp			    max_nsegs_1mbuf);
684237263Snp			if (wr_len & 0xf) {
685237263Snp				uint64_t *pad = (uint64_t *)
686237263Snp				    ((uintptr_t)txwr + wr_len);
687237263Snp				*pad = 0;
688237263Snp			}
689237263Snp		}
690237263Snp
691237263Snp		KASSERT(toep->tx_credits >= credits,
692237263Snp			("%s: not enough credits", __func__));
693237263Snp
694237263Snp		toep->tx_credits -= credits;
695255411Snp		toep->tx_nocompl += credits;
696255411Snp		toep->plen_nocompl += plen;
697255411Snp		if (toep->tx_credits <= toep->tx_total * 3 / 8 &&
698255411Snp		    toep->tx_nocompl >= toep->tx_total / 4)
699255411Snp			compl = 1;
700237263Snp
701255411Snp		if (compl) {
702255411Snp			txwr->op_to_immdlen |= htobe32(F_FW_WR_COMPL);
703255411Snp			toep->tx_nocompl = 0;
704255411Snp			toep->plen_nocompl = 0;
705255411Snp		}
706255411Snp
707237263Snp		tp->snd_nxt += plen;
708237263Snp		tp->snd_max += plen;
709237263Snp
710237263Snp		SOCKBUF_LOCK(sb);
711237263Snp		KASSERT(sb_sndptr, ("%s: sb_sndptr is NULL", __func__));
712237263Snp		sb->sb_sndptr = sb_sndptr;
713237263Snp		SOCKBUF_UNLOCK(sb);
714237263Snp
715239514Snp		toep->flags |= TPF_TX_DATA_SENT;
716255411Snp		if (toep->tx_credits < MIN_OFLD_TX_CREDITS)
717255411Snp			toep->flags |= TPF_TX_SUSPENDED;
718237263Snp
719237263Snp		KASSERT(toep->txsd_avail > 0, ("%s: no txsd", __func__));
720237263Snp		txsd->plen = plen;
721237263Snp		txsd->tx_credits = credits;
722237263Snp		txsd++;
723237263Snp		if (__predict_false(++toep->txsd_pidx == toep->txsd_total)) {
724237263Snp			toep->txsd_pidx = 0;
725237263Snp			txsd = &toep->txsd[0];
726237263Snp		}
727237263Snp		toep->txsd_avail--;
728237263Snp
729237263Snp		t4_l2t_send(sc, wr, toep->l2te);
730237263Snp	} while (m != NULL);
731237263Snp
732237263Snp	/* Send a FIN if requested, but only if there's no more data to send */
733239514Snp	if (m == NULL && toep->flags & TPF_SEND_FIN)
734237263Snp		close_conn(sc, toep);
735237263Snp}
736237263Snp
737237263Snpint
738237263Snpt4_tod_output(struct toedev *tod, struct tcpcb *tp)
739237263Snp{
740237263Snp	struct adapter *sc = tod->tod_softc;
741237263Snp#ifdef INVARIANTS
742237263Snp	struct inpcb *inp = tp->t_inpcb;
743237263Snp#endif
744237263Snp	struct toepcb *toep = tp->t_toe;
745237263Snp
746237263Snp	INP_WLOCK_ASSERT(inp);
747237263Snp	KASSERT((inp->inp_flags & INP_DROPPED) == 0,
748237263Snp	    ("%s: inp %p dropped.", __func__, inp));
749237263Snp	KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
750237263Snp
751255411Snp	t4_push_frames(sc, toep, 0);
752237263Snp
753237263Snp	return (0);
754237263Snp}
755237263Snp
756237263Snpint
757237263Snpt4_send_fin(struct toedev *tod, struct tcpcb *tp)
758237263Snp{
759237263Snp	struct adapter *sc = tod->tod_softc;
760237263Snp#ifdef INVARIANTS
761237263Snp	struct inpcb *inp = tp->t_inpcb;
762237263Snp#endif
763237263Snp	struct toepcb *toep = tp->t_toe;
764237263Snp
765237263Snp	INP_WLOCK_ASSERT(inp);
766237263Snp	KASSERT((inp->inp_flags & INP_DROPPED) == 0,
767237263Snp	    ("%s: inp %p dropped.", __func__, inp));
768237263Snp	KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
769237263Snp
770239514Snp	toep->flags |= TPF_SEND_FIN;
771255411Snp	if (tp->t_state >= TCPS_ESTABLISHED)
772255411Snp		t4_push_frames(sc, toep, 0);
773237263Snp
774237263Snp	return (0);
775237263Snp}
776237263Snp
777237263Snpint
778237263Snpt4_send_rst(struct toedev *tod, struct tcpcb *tp)
779237263Snp{
780237263Snp	struct adapter *sc = tod->tod_softc;
781237263Snp#if defined(INVARIANTS)
782237263Snp	struct inpcb *inp = tp->t_inpcb;
783237263Snp#endif
784237263Snp	struct toepcb *toep = tp->t_toe;
785237263Snp
786237263Snp	INP_WLOCK_ASSERT(inp);
787237263Snp	KASSERT((inp->inp_flags & INP_DROPPED) == 0,
788237263Snp	    ("%s: inp %p dropped.", __func__, inp));
789237263Snp	KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
790237263Snp
791237263Snp	/* hmmmm */
792239514Snp	KASSERT(toep->flags & TPF_FLOWC_WR_SENT,
793237263Snp	    ("%s: flowc for tid %u [%s] not sent already",
794237263Snp	    __func__, toep->tid, tcpstates[tp->t_state]));
795237263Snp
796237263Snp	send_reset(sc, toep, 0);
797237263Snp	return (0);
798237263Snp}
799237263Snp
800237263Snp/*
801237263Snp * Peer has sent us a FIN.
802237263Snp */
803237263Snpstatic int
804237263Snpdo_peer_close(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
805237263Snp{
806237263Snp	struct adapter *sc = iq->adapter;
807237263Snp	const struct cpl_peer_close *cpl = (const void *)(rss + 1);
808237263Snp	unsigned int tid = GET_TID(cpl);
809237263Snp	struct toepcb *toep = lookup_tid(sc, tid);
810237263Snp	struct inpcb *inp = toep->inp;
811237263Snp	struct tcpcb *tp = NULL;
812239344Snp	struct socket *so;
813239344Snp	struct sockbuf *sb;
814237263Snp#ifdef INVARIANTS
815237263Snp	unsigned int opcode = G_CPL_OPCODE(be32toh(OPCODE_TID(cpl)));
816237263Snp#endif
817237263Snp
818237263Snp	KASSERT(opcode == CPL_PEER_CLOSE,
819237263Snp	    ("%s: unexpected opcode 0x%x", __func__, opcode));
820237263Snp	KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
821243680Snp
822243680Snp	if (__predict_false(toep->flags & TPF_SYNQE)) {
823243680Snp#ifdef INVARIANTS
824243680Snp		struct synq_entry *synqe = (void *)toep;
825243680Snp
826243680Snp		INP_WLOCK(synqe->lctx->inp);
827243680Snp		if (synqe->flags & TPF_SYNQE_HAS_L2TE) {
828243680Snp			KASSERT(synqe->flags & TPF_ABORT_SHUTDOWN,
829243680Snp			    ("%s: listen socket closed but tid %u not aborted.",
830243680Snp			    __func__, tid));
831243680Snp		} else {
832243680Snp			/*
833243680Snp			 * do_pass_accept_req is still running and will
834243680Snp			 * eventually take care of this tid.
835243680Snp			 */
836243680Snp		}
837243680Snp		INP_WUNLOCK(synqe->lctx->inp);
838243680Snp#endif
839243680Snp		CTR4(KTR_CXGBE, "%s: tid %u, synqe %p (0x%x)", __func__, tid,
840243680Snp		    toep, toep->flags);
841243680Snp		return (0);
842243680Snp	}
843243680Snp
844237263Snp	KASSERT(toep->tid == tid, ("%s: toep tid mismatch", __func__));
845237263Snp
846237263Snp	INP_INFO_WLOCK(&V_tcbinfo);
847237263Snp	INP_WLOCK(inp);
848237263Snp	tp = intotcpcb(inp);
849237263Snp
850237263Snp	CTR5(KTR_CXGBE, "%s: tid %u (%s), toep_flags 0x%x, inp %p", __func__,
851237263Snp	    tid, tp ? tcpstates[tp->t_state] : "no tp", toep->flags, inp);
852237263Snp
853239514Snp	if (toep->flags & TPF_ABORT_SHUTDOWN)
854237263Snp		goto done;
855237263Snp
856239344Snp	tp->rcv_nxt++;	/* FIN */
857239344Snp
858237263Snp	so = inp->inp_socket;
859239344Snp	sb = &so->so_rcv;
860239344Snp	SOCKBUF_LOCK(sb);
861239344Snp	if (__predict_false(toep->ddp_flags & (DDP_BUF0_ACTIVE | DDP_BUF1_ACTIVE))) {
862250218Snp		m = get_ddp_mbuf(be32toh(cpl->rcv_nxt) - tp->rcv_nxt);
863239344Snp		tp->rcv_nxt = be32toh(cpl->rcv_nxt);
864239344Snp		toep->ddp_flags &= ~(DDP_BUF0_ACTIVE | DDP_BUF1_ACTIVE);
865239344Snp
866239344Snp		KASSERT(toep->sb_cc >= sb->sb_cc,
867239344Snp		    ("%s: sb %p has more data (%d) than last time (%d).",
868239344Snp		    __func__, sb, sb->sb_cc, toep->sb_cc));
869239344Snp		toep->rx_credits += toep->sb_cc - sb->sb_cc;
870239344Snp#ifdef USE_DDP_RX_FLOW_CONTROL
871239344Snp		toep->rx_credits -= m->m_len;	/* adjust for F_RX_FC_DDP */
872239344Snp#endif
873239344Snp		sbappendstream_locked(sb, m);
874239344Snp		toep->sb_cc = sb->sb_cc;
875239344Snp	}
876239344Snp	socantrcvmore_locked(so);	/* unlocks the sockbuf */
877239344Snp
878255005Snp	if (toep->ulp_mode != ULP_MODE_RDMA) {
879255005Snp		KASSERT(tp->rcv_nxt == be32toh(cpl->rcv_nxt),
880255005Snp	    		("%s: rcv_nxt mismatch: %u %u", __func__, tp->rcv_nxt,
881255005Snp	    		be32toh(cpl->rcv_nxt)));
882255005Snp	}
883237263Snp
884237263Snp	switch (tp->t_state) {
885237263Snp	case TCPS_SYN_RECEIVED:
886237263Snp		tp->t_starttime = ticks;
887237263Snp		/* FALLTHROUGH */
888237263Snp
889237263Snp	case TCPS_ESTABLISHED:
890237263Snp		tp->t_state = TCPS_CLOSE_WAIT;
891237263Snp		break;
892237263Snp
893237263Snp	case TCPS_FIN_WAIT_1:
894237263Snp		tp->t_state = TCPS_CLOSING;
895237263Snp		break;
896237263Snp
897237263Snp	case TCPS_FIN_WAIT_2:
898237263Snp		tcp_twstart(tp);
899237263Snp		INP_UNLOCK_ASSERT(inp);	 /* safe, we have a ref on the inp */
900237263Snp		INP_INFO_WUNLOCK(&V_tcbinfo);
901237263Snp
902237263Snp		INP_WLOCK(inp);
903237263Snp		final_cpl_received(toep);
904237263Snp		return (0);
905237263Snp
906237263Snp	default:
907237263Snp		log(LOG_ERR, "%s: TID %u received CPL_PEER_CLOSE in state %d\n",
908237263Snp		    __func__, tid, tp->t_state);
909237263Snp	}
910237263Snpdone:
911237263Snp	INP_WUNLOCK(inp);
912237263Snp	INP_INFO_WUNLOCK(&V_tcbinfo);
913237263Snp	return (0);
914237263Snp}
915237263Snp
916237263Snp/*
917237263Snp * Peer has ACK'd our FIN.
918237263Snp */
919237263Snpstatic int
920237263Snpdo_close_con_rpl(struct sge_iq *iq, const struct rss_header *rss,
921237263Snp    struct mbuf *m)
922237263Snp{
923237263Snp	struct adapter *sc = iq->adapter;
924237263Snp	const struct cpl_close_con_rpl *cpl = (const void *)(rss + 1);
925237263Snp	unsigned int tid = GET_TID(cpl);
926237263Snp	struct toepcb *toep = lookup_tid(sc, tid);
927237263Snp	struct inpcb *inp = toep->inp;
928237263Snp	struct tcpcb *tp = NULL;
929237263Snp	struct socket *so = NULL;
930237263Snp#ifdef INVARIANTS
931237263Snp	unsigned int opcode = G_CPL_OPCODE(be32toh(OPCODE_TID(cpl)));
932237263Snp#endif
933237263Snp
934237263Snp	KASSERT(opcode == CPL_CLOSE_CON_RPL,
935237263Snp	    ("%s: unexpected opcode 0x%x", __func__, opcode));
936237263Snp	KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
937237263Snp	KASSERT(toep->tid == tid, ("%s: toep tid mismatch", __func__));
938237263Snp
939237263Snp	INP_INFO_WLOCK(&V_tcbinfo);
940237263Snp	INP_WLOCK(inp);
941237263Snp	tp = intotcpcb(inp);
942237263Snp
943237263Snp	CTR4(KTR_CXGBE, "%s: tid %u (%s), toep_flags 0x%x",
944237263Snp	    __func__, tid, tp ? tcpstates[tp->t_state] : "no tp", toep->flags);
945237263Snp
946239514Snp	if (toep->flags & TPF_ABORT_SHUTDOWN)
947237263Snp		goto done;
948237263Snp
949237263Snp	so = inp->inp_socket;
950237263Snp	tp->snd_una = be32toh(cpl->snd_nxt) - 1;	/* exclude FIN */
951237263Snp
952237263Snp	switch (tp->t_state) {
953237263Snp	case TCPS_CLOSING:	/* see TCPS_FIN_WAIT_2 in do_peer_close too */
954237263Snp		tcp_twstart(tp);
955237263Snprelease:
956237263Snp		INP_UNLOCK_ASSERT(inp);	/* safe, we have a ref on the  inp */
957237263Snp		INP_INFO_WUNLOCK(&V_tcbinfo);
958237263Snp
959237263Snp		INP_WLOCK(inp);
960237263Snp		final_cpl_received(toep);	/* no more CPLs expected */
961237263Snp
962237263Snp		return (0);
963237263Snp	case TCPS_LAST_ACK:
964237263Snp		if (tcp_close(tp))
965237263Snp			INP_WUNLOCK(inp);
966237263Snp		goto release;
967237263Snp
968237263Snp	case TCPS_FIN_WAIT_1:
969237263Snp		if (so->so_rcv.sb_state & SBS_CANTRCVMORE)
970237263Snp			soisdisconnected(so);
971237263Snp		tp->t_state = TCPS_FIN_WAIT_2;
972237263Snp		break;
973237263Snp
974237263Snp	default:
975237263Snp		log(LOG_ERR,
976237263Snp		    "%s: TID %u received CPL_CLOSE_CON_RPL in state %s\n",
977237263Snp		    __func__, tid, tcpstates[tp->t_state]);
978237263Snp	}
979237263Snpdone:
980237263Snp	INP_WUNLOCK(inp);
981237263Snp	INP_INFO_WUNLOCK(&V_tcbinfo);
982237263Snp	return (0);
983237263Snp}
984237263Snp
985237263Snpvoid
986237263Snpsend_abort_rpl(struct adapter *sc, struct sge_wrq *ofld_txq, int tid,
987237263Snp    int rst_status)
988237263Snp{
989237263Snp	struct wrqe *wr;
990237263Snp	struct cpl_abort_rpl *cpl;
991237263Snp
992237263Snp	wr = alloc_wrqe(sizeof(*cpl), ofld_txq);
993237263Snp	if (wr == NULL) {
994237263Snp		/* XXX */
995237263Snp		panic("%s: allocation failure.", __func__);
996237263Snp	}
997237263Snp	cpl = wrtod(wr);
998237263Snp
999237263Snp	INIT_TP_WR_MIT_CPL(cpl, CPL_ABORT_RPL, tid);
1000237263Snp	cpl->cmd = rst_status;
1001237263Snp
1002237263Snp	t4_wrq_tx(sc, wr);
1003237263Snp}
1004237263Snp
1005237263Snpstatic int
1006237263Snpabort_status_to_errno(struct tcpcb *tp, unsigned int abort_reason)
1007237263Snp{
1008237263Snp	switch (abort_reason) {
1009237263Snp	case CPL_ERR_BAD_SYN:
1010237263Snp	case CPL_ERR_CONN_RESET:
1011237263Snp		return (tp->t_state == TCPS_CLOSE_WAIT ? EPIPE : ECONNRESET);
1012237263Snp	case CPL_ERR_XMIT_TIMEDOUT:
1013237263Snp	case CPL_ERR_PERSIST_TIMEDOUT:
1014237263Snp	case CPL_ERR_FINWAIT2_TIMEDOUT:
1015237263Snp	case CPL_ERR_KEEPALIVE_TIMEDOUT:
1016237263Snp		return (ETIMEDOUT);
1017237263Snp	default:
1018237263Snp		return (EIO);
1019237263Snp	}
1020237263Snp}
1021237263Snp
1022237263Snp/*
1023237263Snp * TCP RST from the peer, timeout, or some other such critical error.
1024237263Snp */
1025237263Snpstatic int
1026237263Snpdo_abort_req(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
1027237263Snp{
1028237263Snp	struct adapter *sc = iq->adapter;
1029237263Snp	const struct cpl_abort_req_rss *cpl = (const void *)(rss + 1);
1030237263Snp	unsigned int tid = GET_TID(cpl);
1031237263Snp	struct toepcb *toep = lookup_tid(sc, tid);
1032237263Snp	struct sge_wrq *ofld_txq = toep->ofld_txq;
1033237263Snp	struct inpcb *inp;
1034237263Snp	struct tcpcb *tp;
1035237263Snp#ifdef INVARIANTS
1036237263Snp	unsigned int opcode = G_CPL_OPCODE(be32toh(OPCODE_TID(cpl)));
1037237263Snp#endif
1038237263Snp
1039237263Snp	KASSERT(opcode == CPL_ABORT_REQ_RSS,
1040237263Snp	    ("%s: unexpected opcode 0x%x", __func__, opcode));
1041237263Snp	KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
1042237263Snp
1043239514Snp	if (toep->flags & TPF_SYNQE)
1044237263Snp		return (do_abort_req_synqe(iq, rss, m));
1045237263Snp
1046237263Snp	KASSERT(toep->tid == tid, ("%s: toep tid mismatch", __func__));
1047237263Snp
1048245935Snp	if (negative_advice(cpl->status)) {
1049237263Snp		CTR4(KTR_CXGBE, "%s: negative advice %d for tid %d (0x%x)",
1050237263Snp		    __func__, cpl->status, tid, toep->flags);
1051237263Snp		return (0);	/* Ignore negative advice */
1052237263Snp	}
1053237263Snp
1054237263Snp	inp = toep->inp;
1055237263Snp	INP_INFO_WLOCK(&V_tcbinfo);	/* for tcp_close */
1056237263Snp	INP_WLOCK(inp);
1057237263Snp
1058237263Snp	tp = intotcpcb(inp);
1059237263Snp
1060237263Snp	CTR6(KTR_CXGBE,
1061237263Snp	    "%s: tid %d (%s), toep_flags 0x%x, inp_flags 0x%x, status %d",
1062239528Snp	    __func__, tid, tp ? tcpstates[tp->t_state] : "no tp", toep->flags,
1063239528Snp	    inp->inp_flags, cpl->status);
1064237263Snp
1065237263Snp	/*
1066237263Snp	 * If we'd initiated an abort earlier the reply to it is responsible for
1067237263Snp	 * cleaning up resources.  Otherwise we tear everything down right here
1068237263Snp	 * right now.  We owe the T4 a CPL_ABORT_RPL no matter what.
1069237263Snp	 */
1070239514Snp	if (toep->flags & TPF_ABORT_SHUTDOWN) {
1071237263Snp		INP_WUNLOCK(inp);
1072237263Snp		goto done;
1073237263Snp	}
1074239514Snp	toep->flags |= TPF_ABORT_SHUTDOWN;
1075237263Snp
1076242671Snp	if ((inp->inp_flags & (INP_DROPPED | INP_TIMEWAIT)) == 0) {
1077242671Snp		struct socket *so = inp->inp_socket;
1078237263Snp
1079242671Snp		if (so != NULL)
1080242671Snp			so_error_set(so, abort_status_to_errno(tp,
1081242671Snp			    cpl->status));
1082242671Snp		tp = tcp_close(tp);
1083242671Snp		if (tp == NULL)
1084242671Snp			INP_WLOCK(inp);	/* re-acquire */
1085242671Snp	}
1086242671Snp
1087237263Snp	final_cpl_received(toep);
1088237263Snpdone:
1089237263Snp	INP_INFO_WUNLOCK(&V_tcbinfo);
1090237263Snp	send_abort_rpl(sc, ofld_txq, tid, CPL_ABORT_NO_RST);
1091237263Snp	return (0);
1092237263Snp}
1093237263Snp
1094237263Snp/*
1095237263Snp * Reply to the CPL_ABORT_REQ (send_reset)
1096237263Snp */
1097237263Snpstatic int
1098237263Snpdo_abort_rpl(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
1099237263Snp{
1100237263Snp	struct adapter *sc = iq->adapter;
1101237263Snp	const struct cpl_abort_rpl_rss *cpl = (const void *)(rss + 1);
1102237263Snp	unsigned int tid = GET_TID(cpl);
1103237263Snp	struct toepcb *toep = lookup_tid(sc, tid);
1104237263Snp	struct inpcb *inp = toep->inp;
1105237263Snp#ifdef INVARIANTS
1106237263Snp	unsigned int opcode = G_CPL_OPCODE(be32toh(OPCODE_TID(cpl)));
1107237263Snp#endif
1108237263Snp
1109237263Snp	KASSERT(opcode == CPL_ABORT_RPL_RSS,
1110237263Snp	    ("%s: unexpected opcode 0x%x", __func__, opcode));
1111237263Snp	KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
1112237263Snp
1113239514Snp	if (toep->flags & TPF_SYNQE)
1114237263Snp		return (do_abort_rpl_synqe(iq, rss, m));
1115237263Snp
1116237263Snp	KASSERT(toep->tid == tid, ("%s: toep tid mismatch", __func__));
1117237263Snp
1118237263Snp	CTR5(KTR_CXGBE, "%s: tid %u, toep %p, inp %p, status %d",
1119237263Snp	    __func__, tid, toep, inp, cpl->status);
1120237263Snp
1121239514Snp	KASSERT(toep->flags & TPF_ABORT_SHUTDOWN,
1122237263Snp	    ("%s: wasn't expecting abort reply", __func__));
1123237263Snp
1124237263Snp	INP_WLOCK(inp);
1125237263Snp	final_cpl_received(toep);
1126237263Snp
1127237263Snp	return (0);
1128237263Snp}
1129237263Snp
1130237263Snpstatic int
1131237263Snpdo_rx_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
1132237263Snp{
1133237263Snp	struct adapter *sc = iq->adapter;
1134237263Snp	const struct cpl_rx_data *cpl = mtod(m, const void *);
1135237263Snp	unsigned int tid = GET_TID(cpl);
1136237263Snp	struct toepcb *toep = lookup_tid(sc, tid);
1137237263Snp	struct inpcb *inp = toep->inp;
1138237263Snp	struct tcpcb *tp;
1139237263Snp	struct socket *so;
1140239344Snp	struct sockbuf *sb;
1141239344Snp	int len;
1142243681Snp	uint32_t ddp_placed = 0;
1143237263Snp
1144239514Snp	if (__predict_false(toep->flags & TPF_SYNQE)) {
1145243680Snp#ifdef INVARIANTS
1146243680Snp		struct synq_entry *synqe = (void *)toep;
1147243680Snp
1148243680Snp		INP_WLOCK(synqe->lctx->inp);
1149243680Snp		if (synqe->flags & TPF_SYNQE_HAS_L2TE) {
1150243680Snp			KASSERT(synqe->flags & TPF_ABORT_SHUTDOWN,
1151243680Snp			    ("%s: listen socket closed but tid %u not aborted.",
1152243680Snp			    __func__, tid));
1153243680Snp		} else {
1154243680Snp			/*
1155243680Snp			 * do_pass_accept_req is still running and will
1156243680Snp			 * eventually take care of this tid.
1157243680Snp			 */
1158243680Snp		}
1159243680Snp		INP_WUNLOCK(synqe->lctx->inp);
1160243680Snp#endif
1161243680Snp		CTR4(KTR_CXGBE, "%s: tid %u, synqe %p (0x%x)", __func__, tid,
1162243680Snp		    toep, toep->flags);
1163237263Snp		m_freem(m);
1164237263Snp		return (0);
1165237263Snp	}
1166237263Snp
1167237263Snp	KASSERT(toep->tid == tid, ("%s: toep tid mismatch", __func__));
1168237263Snp
1169237263Snp	/* strip off CPL header */
1170237263Snp	m_adj(m, sizeof(*cpl));
1171239344Snp	len = m->m_pkthdr.len;
1172237263Snp
1173237263Snp	INP_WLOCK(inp);
1174237263Snp	if (inp->inp_flags & (INP_DROPPED | INP_TIMEWAIT)) {
1175237263Snp		CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x",
1176239344Snp		    __func__, tid, len, inp->inp_flags);
1177237263Snp		INP_WUNLOCK(inp);
1178237263Snp		m_freem(m);
1179237263Snp		return (0);
1180237263Snp	}
1181237263Snp
1182237263Snp	tp = intotcpcb(inp);
1183237263Snp
1184243681Snp	if (__predict_false(tp->rcv_nxt != be32toh(cpl->seq)))
1185243681Snp		ddp_placed = be32toh(cpl->seq) - tp->rcv_nxt;
1186237263Snp
1187239344Snp	tp->rcv_nxt += len;
1188239344Snp	KASSERT(tp->rcv_wnd >= len, ("%s: negative window size", __func__));
1189239344Snp	tp->rcv_wnd -= len;
1190237263Snp	tp->t_rcvtime = ticks;
1191237263Snp
1192237263Snp	so = inp_inpcbtosocket(inp);
1193239344Snp	sb = &so->so_rcv;
1194239344Snp	SOCKBUF_LOCK(sb);
1195237263Snp
1196239344Snp	if (__predict_false(sb->sb_state & SBS_CANTRCVMORE)) {
1197237263Snp		CTR3(KTR_CXGBE, "%s: tid %u, excess rx (%d bytes)",
1198239344Snp		    __func__, tid, len);
1199237263Snp		m_freem(m);
1200239344Snp		SOCKBUF_UNLOCK(sb);
1201237263Snp		INP_WUNLOCK(inp);
1202237263Snp
1203237263Snp		INP_INFO_WLOCK(&V_tcbinfo);
1204237263Snp		INP_WLOCK(inp);
1205237263Snp		tp = tcp_drop(tp, ECONNRESET);
1206237263Snp		if (tp)
1207237263Snp			INP_WUNLOCK(inp);
1208237263Snp		INP_INFO_WUNLOCK(&V_tcbinfo);
1209237263Snp
1210237263Snp		return (0);
1211237263Snp	}
1212237263Snp
1213237263Snp	/* receive buffer autosize */
1214239344Snp	if (sb->sb_flags & SB_AUTOSIZE &&
1215237263Snp	    V_tcp_do_autorcvbuf &&
1216239344Snp	    sb->sb_hiwat < V_tcp_autorcvbuf_max &&
1217239344Snp	    len > (sbspace(sb) / 8 * 7)) {
1218239344Snp		unsigned int hiwat = sb->sb_hiwat;
1219237263Snp		unsigned int newsize = min(hiwat + V_tcp_autorcvbuf_inc,
1220237263Snp		    V_tcp_autorcvbuf_max);
1221237263Snp
1222239344Snp		if (!sbreserve_locked(sb, newsize, so, NULL))
1223239344Snp			sb->sb_flags &= ~SB_AUTOSIZE;
1224237263Snp		else
1225237263Snp			toep->rx_credits += newsize - hiwat;
1226237263Snp	}
1227239344Snp
1228239344Snp	if (toep->ulp_mode == ULP_MODE_TCPDDP) {
1229239344Snp		int changed = !(toep->ddp_flags & DDP_ON) ^ cpl->ddp_off;
1230239344Snp
1231239344Snp		if (changed) {
1232243681Snp			if (toep->ddp_flags & DDP_SC_REQ)
1233243681Snp				toep->ddp_flags ^= DDP_ON | DDP_SC_REQ;
1234243681Snp			else {
1235243681Snp				KASSERT(cpl->ddp_off == 1,
1236243681Snp				    ("%s: DDP switched on by itself.",
1237243681Snp				    __func__));
1238243681Snp
1239243681Snp				/* Fell out of DDP mode */
1240243681Snp				toep->ddp_flags &= ~(DDP_ON | DDP_BUF0_ACTIVE |
1241243681Snp				    DDP_BUF1_ACTIVE);
1242243681Snp
1243243681Snp				if (ddp_placed)
1244243681Snp					insert_ddp_data(toep, ddp_placed);
1245239344Snp			}
1246239344Snp		}
1247239344Snp
1248239344Snp		if ((toep->ddp_flags & DDP_OK) == 0 &&
1249239344Snp		    time_uptime >= toep->ddp_disabled + DDP_RETRY_WAIT) {
1250239344Snp			toep->ddp_score = DDP_LOW_SCORE;
1251239344Snp			toep->ddp_flags |= DDP_OK;
1252239344Snp			CTR3(KTR_CXGBE, "%s: tid %u DDP_OK @ %u",
1253239344Snp			    __func__, tid, time_uptime);
1254239344Snp		}
1255239344Snp
1256239344Snp		if (toep->ddp_flags & DDP_ON) {
1257239344Snp
1258239344Snp			/*
1259239344Snp			 * CPL_RX_DATA with DDP on can only be an indicate.  Ask
1260239344Snp			 * soreceive to post a buffer or disable DDP.  The
1261239344Snp			 * payload that arrived in this indicate is appended to
1262239344Snp			 * the socket buffer as usual.
1263239344Snp			 */
1264239344Snp
1265239344Snp#if 0
1266239344Snp			CTR5(KTR_CXGBE,
1267239344Snp			    "%s: tid %u (0x%x) DDP indicate (seq 0x%x, len %d)",
1268239344Snp			    __func__, tid, toep->flags, be32toh(cpl->seq), len);
1269239344Snp#endif
1270239344Snp			sb->sb_flags |= SB_DDP_INDICATE;
1271239344Snp		} else if ((toep->ddp_flags & (DDP_OK|DDP_SC_REQ)) == DDP_OK &&
1272239344Snp		    tp->rcv_wnd > DDP_RSVD_WIN && len >= sc->tt.ddp_thres) {
1273239344Snp
1274239344Snp			/*
1275239344Snp			 * DDP allowed but isn't on (and a request to switch it
1276239344Snp			 * on isn't pending either), and conditions are ripe for
1277239344Snp			 * it to work.  Switch it on.
1278239344Snp			 */
1279239344Snp
1280239344Snp			enable_ddp(sc, toep);
1281239344Snp		}
1282239344Snp	}
1283239344Snp
1284239344Snp	KASSERT(toep->sb_cc >= sb->sb_cc,
1285239344Snp	    ("%s: sb %p has more data (%d) than last time (%d).",
1286239344Snp	    __func__, sb, sb->sb_cc, toep->sb_cc));
1287239344Snp	toep->rx_credits += toep->sb_cc - sb->sb_cc;
1288239344Snp	sbappendstream_locked(sb, m);
1289239344Snp	toep->sb_cc = sb->sb_cc;
1290237263Snp	sorwakeup_locked(so);
1291239344Snp	SOCKBUF_UNLOCK_ASSERT(sb);
1292237263Snp
1293237263Snp	INP_WUNLOCK(inp);
1294237263Snp	return (0);
1295237263Snp}
1296237263Snp
1297237263Snp#define S_CPL_FW4_ACK_OPCODE    24
1298237263Snp#define M_CPL_FW4_ACK_OPCODE    0xff
1299237263Snp#define V_CPL_FW4_ACK_OPCODE(x) ((x) << S_CPL_FW4_ACK_OPCODE)
1300237263Snp#define G_CPL_FW4_ACK_OPCODE(x) \
1301237263Snp    (((x) >> S_CPL_FW4_ACK_OPCODE) & M_CPL_FW4_ACK_OPCODE)
1302237263Snp
1303237263Snp#define S_CPL_FW4_ACK_FLOWID    0
1304237263Snp#define M_CPL_FW4_ACK_FLOWID    0xffffff
1305237263Snp#define V_CPL_FW4_ACK_FLOWID(x) ((x) << S_CPL_FW4_ACK_FLOWID)
1306237263Snp#define G_CPL_FW4_ACK_FLOWID(x) \
1307237263Snp    (((x) >> S_CPL_FW4_ACK_FLOWID) & M_CPL_FW4_ACK_FLOWID)
1308237263Snp
1309237263Snp#define S_CPL_FW4_ACK_CR        24
1310237263Snp#define M_CPL_FW4_ACK_CR        0xff
1311237263Snp#define V_CPL_FW4_ACK_CR(x)     ((x) << S_CPL_FW4_ACK_CR)
1312237263Snp#define G_CPL_FW4_ACK_CR(x)     (((x) >> S_CPL_FW4_ACK_CR) & M_CPL_FW4_ACK_CR)
1313237263Snp
1314237263Snp#define S_CPL_FW4_ACK_SEQVAL    0
1315237263Snp#define M_CPL_FW4_ACK_SEQVAL    0x1
1316237263Snp#define V_CPL_FW4_ACK_SEQVAL(x) ((x) << S_CPL_FW4_ACK_SEQVAL)
1317237263Snp#define G_CPL_FW4_ACK_SEQVAL(x) \
1318237263Snp    (((x) >> S_CPL_FW4_ACK_SEQVAL) & M_CPL_FW4_ACK_SEQVAL)
1319237263Snp#define F_CPL_FW4_ACK_SEQVAL    V_CPL_FW4_ACK_SEQVAL(1U)
1320237263Snp
1321237263Snpstatic int
1322237263Snpdo_fw4_ack(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
1323237263Snp{
1324237263Snp	struct adapter *sc = iq->adapter;
1325237263Snp	const struct cpl_fw4_ack *cpl = (const void *)(rss + 1);
1326237263Snp	unsigned int tid = G_CPL_FW4_ACK_FLOWID(be32toh(OPCODE_TID(cpl)));
1327237263Snp	struct toepcb *toep = lookup_tid(sc, tid);
1328237263Snp	struct inpcb *inp;
1329237263Snp	struct tcpcb *tp;
1330237263Snp	struct socket *so;
1331237263Snp	uint8_t credits = cpl->credits;
1332237263Snp	struct ofld_tx_sdesc *txsd;
1333237263Snp	int plen;
1334237263Snp#ifdef INVARIANTS
1335237263Snp	unsigned int opcode = G_CPL_FW4_ACK_OPCODE(be32toh(OPCODE_TID(cpl)));
1336237263Snp#endif
1337237263Snp
1338237263Snp	/*
1339237263Snp	 * Very unusual case: we'd sent a flowc + abort_req for a synq entry and
1340237263Snp	 * now this comes back carrying the credits for the flowc.
1341237263Snp	 */
1342239514Snp	if (__predict_false(toep->flags & TPF_SYNQE)) {
1343239514Snp		KASSERT(toep->flags & TPF_ABORT_SHUTDOWN,
1344237263Snp		    ("%s: credits for a synq entry %p", __func__, toep));
1345237263Snp		return (0);
1346237263Snp	}
1347237263Snp
1348237263Snp	inp = toep->inp;
1349237263Snp
1350237263Snp	KASSERT(opcode == CPL_FW4_ACK,
1351237263Snp	    ("%s: unexpected opcode 0x%x", __func__, opcode));
1352237263Snp	KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
1353237263Snp	KASSERT(toep->tid == tid, ("%s: toep tid mismatch", __func__));
1354237263Snp
1355237263Snp	INP_WLOCK(inp);
1356237263Snp
1357239514Snp	if (__predict_false(toep->flags & TPF_ABORT_SHUTDOWN)) {
1358237263Snp		INP_WUNLOCK(inp);
1359237263Snp		return (0);
1360237263Snp	}
1361237263Snp
1362237263Snp	KASSERT((inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) == 0,
1363237263Snp	    ("%s: inp_flags 0x%x", __func__, inp->inp_flags));
1364237263Snp
1365237263Snp	tp = intotcpcb(inp);
1366237263Snp
1367237436Snp	if (cpl->flags & CPL_FW4_ACK_FLAGS_SEQVAL) {
1368237263Snp		tcp_seq snd_una = be32toh(cpl->snd_una);
1369237263Snp
1370237263Snp#ifdef INVARIANTS
1371237263Snp		if (__predict_false(SEQ_LT(snd_una, tp->snd_una))) {
1372237263Snp			log(LOG_ERR,
1373237263Snp			    "%s: unexpected seq# %x for TID %u, snd_una %x\n",
1374237263Snp			    __func__, snd_una, toep->tid, tp->snd_una);
1375237263Snp		}
1376237263Snp#endif
1377237263Snp
1378237263Snp		if (tp->snd_una != snd_una) {
1379237263Snp			tp->snd_una = snd_una;
1380237263Snp			tp->ts_recent_age = tcp_ts_getticks();
1381237263Snp		}
1382237263Snp	}
1383237263Snp
1384237263Snp	so = inp->inp_socket;
1385237263Snp	txsd = &toep->txsd[toep->txsd_cidx];
1386237263Snp	plen = 0;
1387237263Snp	while (credits) {
1388237263Snp		KASSERT(credits >= txsd->tx_credits,
1389237263Snp		    ("%s: too many (or partial) credits", __func__));
1390237263Snp		credits -= txsd->tx_credits;
1391237263Snp		toep->tx_credits += txsd->tx_credits;
1392237263Snp		plen += txsd->plen;
1393237263Snp		txsd++;
1394237263Snp		toep->txsd_avail++;
1395237263Snp		KASSERT(toep->txsd_avail <= toep->txsd_total,
1396237263Snp		    ("%s: txsd avail > total", __func__));
1397237263Snp		if (__predict_false(++toep->txsd_cidx == toep->txsd_total)) {
1398237263Snp			txsd = &toep->txsd[0];
1399237263Snp			toep->txsd_cidx = 0;
1400237263Snp		}
1401237263Snp	}
1402237263Snp
1403255411Snp	if (toep->tx_credits == toep->tx_total) {
1404255411Snp		toep->tx_nocompl = 0;
1405255411Snp		toep->plen_nocompl = 0;
1406255411Snp	}
1407255411Snp
1408255411Snp	if (toep->flags & TPF_TX_SUSPENDED &&
1409255411Snp	    toep->tx_credits >= toep->tx_total / 4) {
1410255411Snp		toep->flags &= ~TPF_TX_SUSPENDED;
1411255411Snp		t4_push_frames(sc, toep, plen);
1412255411Snp	} else if (plen > 0) {
1413237263Snp		struct sockbuf *sb = &so->so_snd;
1414237263Snp
1415237263Snp		SOCKBUF_LOCK(sb);
1416237263Snp		sbdrop_locked(sb, plen);
1417237263Snp		sowwakeup_locked(so);
1418237263Snp		SOCKBUF_UNLOCK_ASSERT(sb);
1419237263Snp	}
1420237263Snp
1421237263Snp	INP_WUNLOCK(inp);
1422237263Snp
1423237263Snp	return (0);
1424237263Snp}
1425237263Snp
1426239338Snpstatic int
1427239338Snpdo_set_tcb_rpl(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
1428239338Snp{
1429239338Snp	struct adapter *sc = iq->adapter;
1430239338Snp	const struct cpl_set_tcb_rpl *cpl = (const void *)(rss + 1);
1431239338Snp	unsigned int tid = GET_TID(cpl);
1432239338Snp#ifdef INVARIANTS
1433239338Snp	unsigned int opcode = G_CPL_OPCODE(be32toh(OPCODE_TID(cpl)));
1434239338Snp#endif
1435239338Snp
1436239338Snp	KASSERT(opcode == CPL_SET_TCB_RPL,
1437239338Snp	    ("%s: unexpected opcode 0x%x", __func__, opcode));
1438239338Snp	KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
1439239338Snp
1440239338Snp	if (tid >= sc->tids.ftid_base &&
1441239338Snp	    tid < sc->tids.ftid_base + sc->tids.nftids)
1442239338Snp		return (t4_filter_rpl(iq, rss, m)); /* TCB is a filter */
1443239338Snp
1444239338Snp	CXGBE_UNIMPLEMENTED(__func__);
1445239338Snp}
1446239338Snp
1447237263Snpvoid
1448251638Snpt4_set_tcb_field(struct adapter *sc, struct toepcb *toep, int ctrl,
1449251638Snp    uint16_t word, uint64_t mask, uint64_t val)
1450239338Snp{
1451239338Snp	struct wrqe *wr;
1452239338Snp	struct cpl_set_tcb_field *req;
1453239338Snp
1454251638Snp	wr = alloc_wrqe(sizeof(*req), ctrl ? toep->ctrlq : toep->ofld_txq);
1455239338Snp	if (wr == NULL) {
1456239338Snp		/* XXX */
1457239338Snp		panic("%s: allocation failure.", __func__);
1458239338Snp	}
1459239338Snp	req = wrtod(wr);
1460239338Snp
1461239338Snp	INIT_TP_WR_MIT_CPL(req, CPL_SET_TCB_FIELD, toep->tid);
1462239338Snp	req->reply_ctrl = htobe16(V_NO_REPLY(1) |
1463239338Snp	    V_QUEUENO(toep->ofld_rxq->iq.abs_id));
1464239338Snp	req->word_cookie = htobe16(V_WORD(word) | V_COOKIE(0));
1465239338Snp	req->mask = htobe64(mask);
1466239338Snp	req->val = htobe64(val);
1467239338Snp
1468239338Snp	t4_wrq_tx(sc, wr);
1469239338Snp}
1470239338Snp
1471239338Snpvoid
1472237263Snpt4_init_cpl_io_handlers(struct adapter *sc)
1473237263Snp{
1474237263Snp
1475237263Snp	t4_register_cpl_handler(sc, CPL_PEER_CLOSE, do_peer_close);
1476237263Snp	t4_register_cpl_handler(sc, CPL_CLOSE_CON_RPL, do_close_con_rpl);
1477237263Snp	t4_register_cpl_handler(sc, CPL_ABORT_REQ_RSS, do_abort_req);
1478237263Snp	t4_register_cpl_handler(sc, CPL_ABORT_RPL_RSS, do_abort_rpl);
1479237263Snp	t4_register_cpl_handler(sc, CPL_RX_DATA, do_rx_data);
1480237263Snp	t4_register_cpl_handler(sc, CPL_FW4_ACK, do_fw4_ack);
1481239338Snp	t4_register_cpl_handler(sc, CPL_SET_TCB_RPL, do_set_tcb_rpl);
1482237263Snp}
1483239338Snp
1484239338Snpvoid
1485239338Snpt4_uninit_cpl_io_handlers(struct adapter *sc)
1486239338Snp{
1487239338Snp
1488239338Snp	t4_register_cpl_handler(sc, CPL_SET_TCB_RPL, t4_filter_rpl);
1489239338Snp}
1490237263Snp#endif
1491