1237263Snp/*-
2237263Snp * Copyright (c) 2012 Chelsio Communications, Inc.
3237263Snp * All rights reserved.
4237263Snp *
5237263Snp * Redistribution and use in source and binary forms, with or without
6237263Snp * modification, are permitted provided that the following conditions
7237263Snp * are met:
8237263Snp * 1. Redistributions of source code must retain the above copyright
9237263Snp *    notice, this list of conditions and the following disclaimer.
10237263Snp * 2. Redistributions in binary form must reproduce the above copyright
11237263Snp *    notice, this list of conditions and the following disclaimer in the
12237263Snp *    documentation and/or other materials provided with the distribution.
13237263Snp *
14237263Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15237263Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16237263Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17237263Snp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18237263Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19237263Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20237263Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21237263Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22237263Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23237263Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24237263Snp * SUCH DAMAGE.
25237263Snp */
26174641Skmacy
27237263Snp#include <sys/cdefs.h>
28237263Snp__FBSDID("$FreeBSD$");
29174641Skmacy
30237263Snp#include "opt_inet.h"
31174641Skmacy
32237263Snp#ifdef TCP_OFFLOAD
33237263Snp#include <sys/param.h>
34237263Snp#include <sys/refcount.h>
35237263Snp#include <sys/socket.h>
36237263Snp#include <sys/socketvar.h>
37237263Snp#include <sys/sysctl.h>
38237263Snp#include <net/if.h>
39257241Sglebius#include <net/if_var.h>
40237263Snp#include <net/route.h>
41237263Snp#include <netinet/in.h>
42237263Snp#include <netinet/ip.h>
43293309Smelifaro#include <netinet/in_fib.h>
44237263Snp#include <netinet/in_pcb.h>
45237263Snp#include <netinet/in_var.h>
46239544Snp#include <netinet/tcp_timer.h>
47237263Snp#define TCPSTATES
48237263Snp#include <netinet/tcp_fsm.h>
49294889Sglebius#include <netinet/tcp_var.h>
50237263Snp#include <netinet/toecore.h>
51174641Skmacy
52237263Snp#include "cxgb_include.h"
53237263Snp#include "ulp/tom/cxgb_tom.h"
54237263Snp#include "ulp/tom/cxgb_l2t.h"
55237263Snp#include "ulp/tom/cxgb_toepcb.h"
56174641Skmacy
57237263Snpstatic void t3_send_reset_synqe(struct toedev *, struct synq_entry *);
58174641Skmacy
59237263Snpstatic int
60237263Snpalloc_stid(struct tid_info *t, void *ctx)
61237263Snp{
62237263Snp	int stid = -1;
63174641Skmacy
64237263Snp	mtx_lock(&t->stid_lock);
65237263Snp	if (t->sfree) {
66237263Snp		union listen_entry *p = t->sfree;
67174641Skmacy
68237263Snp		stid = (p - t->stid_tab) + t->stid_base;
69237263Snp		t->sfree = p->next;
70237263Snp		p->ctx = ctx;
71237263Snp		t->stids_in_use++;
72237263Snp	}
73237263Snp	mtx_unlock(&t->stid_lock);
74237263Snp	return (stid);
75237263Snp}
76181067Skmacy
77237263Snpstatic void
78237263Snpfree_stid(struct tid_info *t, int stid)
79237263Snp{
80237263Snp	union listen_entry *p = stid2entry(t, stid);
81181067Skmacy
82237263Snp	mtx_lock(&t->stid_lock);
83237263Snp	p->next = t->sfree;
84237263Snp	t->sfree = p;
85237263Snp	t->stids_in_use--;
86237263Snp	mtx_unlock(&t->stid_lock);
87237263Snp}
88174641Skmacy
89237263Snpstatic struct listen_ctx *
90237263Snpalloc_lctx(struct tom_data *td, struct inpcb *inp, int qset)
91237263Snp{
92237263Snp	struct listen_ctx *lctx;
93174641Skmacy
94237263Snp	INP_WLOCK_ASSERT(inp);
95174641Skmacy
96237263Snp	lctx = malloc(sizeof(struct listen_ctx), M_CXGB, M_NOWAIT | M_ZERO);
97237263Snp	if (lctx == NULL)
98237263Snp		return (NULL);
99174641Skmacy
100237263Snp	lctx->stid = alloc_stid(&td->tid_maps, lctx);
101237263Snp	if (lctx->stid < 0) {
102237263Snp		free(lctx, M_CXGB);
103237263Snp		return (NULL);
104237263Snp	}
105174641Skmacy
106237263Snp	lctx->inp = inp;
107237263Snp	in_pcbref(inp);
108174641Skmacy
109237263Snp	lctx->qset = qset;
110237263Snp	refcount_init(&lctx->refcnt, 1);
111237263Snp	TAILQ_INIT(&lctx->synq);
112174641Skmacy
113237263Snp	return (lctx);
114237263Snp}
115174641Skmacy
116237263Snp/* Don't call this directly, use release_lctx instead */
117237263Snpstatic int
118237263Snpfree_lctx(struct tom_data *td, struct listen_ctx *lctx)
119237263Snp{
120237263Snp	struct inpcb *inp = lctx->inp;
121174641Skmacy
122237263Snp	INP_WLOCK_ASSERT(inp);
123237263Snp	KASSERT(lctx->refcnt == 0,
124237263Snp	    ("%s: refcnt %d", __func__, lctx->refcnt));
125237263Snp	KASSERT(TAILQ_EMPTY(&lctx->synq),
126237263Snp	    ("%s: synq not empty.", __func__));
127237263Snp	KASSERT(lctx->stid >= 0, ("%s: bad stid %d.", __func__, lctx->stid));
128174641Skmacy
129237263Snp	CTR4(KTR_CXGB, "%s: stid %u, lctx %p, inp %p",
130237263Snp	    __func__, lctx->stid, lctx, lctx->inp);
131237263Snp
132237263Snp	free_stid(&td->tid_maps, lctx->stid);
133237263Snp	free(lctx, M_CXGB);
134237263Snp
135237263Snp	return in_pcbrele_wlocked(inp);
136237263Snp}
137237263Snp
138237263Snpstatic void
139237263Snphold_lctx(struct listen_ctx *lctx)
140237263Snp{
141237263Snp
142237263Snp	refcount_acquire(&lctx->refcnt);
143237263Snp}
144237263Snp
145237263Snpstatic inline uint32_t
146237263Snplisten_hashfn(void *key, u_long mask)
147237263Snp{
148237263Snp
149237263Snp	return (fnv_32_buf(&key, sizeof(key), FNV1_32_INIT) & mask);
150237263Snp}
151237263Snp
152174641Skmacy/*
153237263Snp * Add a listen_ctx entry to the listen hash table.
154237263Snp */
155237263Snpstatic void
156237263Snplisten_hash_add(struct tom_data *td, struct listen_ctx *lctx)
157237263Snp{
158237263Snp	int bucket = listen_hashfn(lctx->inp, td->listen_mask);
159237263Snp
160237263Snp	mtx_lock(&td->lctx_hash_lock);
161237263Snp	LIST_INSERT_HEAD(&td->listen_hash[bucket], lctx, link);
162237263Snp	td->lctx_count++;
163237263Snp	mtx_unlock(&td->lctx_hash_lock);
164237263Snp}
165237263Snp
166237263Snp/*
167237263Snp * Look for the listening socket's context entry in the hash and return it.
168237263Snp */
169237263Snpstatic struct listen_ctx *
170237263Snplisten_hash_find(struct tom_data *td, struct inpcb *inp)
171237263Snp{
172237263Snp	int bucket = listen_hashfn(inp, td->listen_mask);
173237263Snp	struct listen_ctx *lctx;
174237263Snp
175237263Snp	mtx_lock(&td->lctx_hash_lock);
176237263Snp	LIST_FOREACH(lctx, &td->listen_hash[bucket], link) {
177237263Snp		if (lctx->inp == inp)
178237263Snp			break;
179237263Snp	}
180237263Snp	mtx_unlock(&td->lctx_hash_lock);
181237263Snp
182237263Snp	return (lctx);
183237263Snp}
184237263Snp
185237263Snp/*
186237263Snp * Removes the listen_ctx structure for inp from the hash and returns it.
187237263Snp */
188237263Snpstatic struct listen_ctx *
189237263Snplisten_hash_del(struct tom_data *td, struct inpcb *inp)
190237263Snp{
191237263Snp	int bucket = listen_hashfn(inp, td->listen_mask);
192237263Snp	struct listen_ctx *lctx, *l;
193237263Snp
194237263Snp	mtx_lock(&td->lctx_hash_lock);
195237263Snp	LIST_FOREACH_SAFE(lctx, &td->listen_hash[bucket], link, l) {
196237263Snp		if (lctx->inp == inp) {
197237263Snp			LIST_REMOVE(lctx, link);
198237263Snp			td->lctx_count--;
199237263Snp			break;
200237263Snp		}
201237263Snp	}
202237263Snp	mtx_unlock(&td->lctx_hash_lock);
203237263Snp
204237263Snp	return (lctx);
205237263Snp}
206237263Snp
207237263Snp/*
208237263Snp * Releases a hold on the lctx.  Must be called with the listening socket's inp
209237263Snp * locked.  The inp may be freed by this function and it returns NULL to
210237263Snp * indicate this.
211237263Snp */
212237263Snpstatic struct inpcb *
213237263Snprelease_lctx(struct tom_data *td, struct listen_ctx *lctx)
214237263Snp{
215237263Snp	struct inpcb *inp = lctx->inp;
216237263Snp	int inp_freed = 0;
217237263Snp
218237263Snp	INP_WLOCK_ASSERT(inp);
219237263Snp	if (refcount_release(&lctx->refcnt))
220237263Snp		inp_freed = free_lctx(td, lctx);
221237263Snp
222237263Snp	return (inp_freed ? NULL : inp);
223237263Snp}
224237263Snp
225237263Snpstatic int
226237263Snpcreate_server(struct adapter *sc, struct listen_ctx *lctx)
227237263Snp{
228237263Snp	struct mbuf *m;
229237263Snp	struct cpl_pass_open_req *req;
230237263Snp	struct inpcb *inp = lctx->inp;
231237263Snp
232237263Snp	m = M_GETHDR_OFLD(lctx->qset, CPL_PRIORITY_CONTROL, req);
233237263Snp	if (m == NULL)
234237263Snp		return (ENOMEM);
235237263Snp
236237263Snp	req->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
237237263Snp	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, lctx->stid));
238237263Snp	req->local_port = inp->inp_lport;
239237263Snp	memcpy(&req->local_ip, &inp->inp_laddr, 4);
240237263Snp	req->peer_port = 0;
241237263Snp	req->peer_ip = 0;
242237263Snp	req->peer_netmask = 0;
243237263Snp	req->opt0h = htonl(F_DELACK | F_TCAM_BYPASS);
244237263Snp	req->opt0l = htonl(V_RCV_BUFSIZ(16));
245237263Snp	req->opt1 = htonl(V_CONN_POLICY(CPL_CONN_POLICY_ASK));
246237263Snp
247237263Snp	t3_offload_tx(sc, m);
248237263Snp
249237263Snp	return (0);
250237263Snp}
251237263Snp
252237263Snpstatic int
253237263Snpdestroy_server(struct adapter *sc, struct listen_ctx *lctx)
254237263Snp{
255237263Snp	struct mbuf *m;
256237263Snp	struct cpl_close_listserv_req *req;
257237263Snp
258237263Snp	m = M_GETHDR_OFLD(lctx->qset, CPL_PRIORITY_CONTROL, req);
259237263Snp	if (m == NULL)
260237263Snp		return (ENOMEM);
261237263Snp
262237263Snp	req->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
263237263Snp	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ,
264237263Snp	    lctx->stid));
265237263Snp	req->cpu_idx = 0;
266237263Snp
267237263Snp	t3_offload_tx(sc, m);
268237263Snp
269237263Snp	return (0);
270237263Snp}
271237263Snp
272237263Snp/*
273174641Skmacy * Process a CPL_CLOSE_LISTSRV_RPL message.  If the status is good we release
274174641Skmacy * the STID.
275174641Skmacy */
276174641Skmacystatic int
277237263Snpdo_close_server_rpl(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m)
278174641Skmacy{
279237263Snp	struct adapter *sc = qs->adap;
280237263Snp	struct tom_data *td = sc->tom_softc;
281237263Snp	struct cpl_close_listserv_rpl *rpl = mtod(m, void *);
282174641Skmacy	unsigned int stid = GET_TID(rpl);
283237263Snp	struct listen_ctx *lctx = lookup_stid(&td->tid_maps, stid);
284237263Snp	struct inpcb *inp = lctx->inp;
285174641Skmacy
286237263Snp	CTR3(KTR_CXGB, "%s: stid %u, status %u", __func__, stid, rpl->status);
287174641Skmacy
288237263Snp	if (rpl->status != CPL_ERR_NONE) {
289237263Snp		log(LOG_ERR, "%s: failed (%u) to close listener for stid %u",
290237263Snp		    __func__, rpl->status, stid);
291237263Snp	} else {
292237263Snp		INP_WLOCK(inp);
293237263Snp		KASSERT(listen_hash_del(td, lctx->inp) == NULL,
294237263Snp		    ("%s: inp %p still in listen hash", __func__, inp));
295237263Snp		if (release_lctx(td, lctx) != NULL)
296237263Snp			INP_WUNLOCK(inp);
297174641Skmacy	}
298174641Skmacy
299237263Snp	m_freem(m);
300237263Snp	return (0);
301174641Skmacy}
302174641Skmacy
303174641Skmacy/*
304237263Snp * Process a CPL_PASS_OPEN_RPL message.  Remove the lctx from the listen hash
305237263Snp * table and free it if there was any error, otherwise nothing to do.
306174641Skmacy */
307174641Skmacystatic int
308237263Snpdo_pass_open_rpl(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m)
309174641Skmacy{
310237263Snp	struct adapter *sc = qs->adap;
311237263Snp	struct tom_data *td = sc->tom_softc;
312237263Snp       	struct cpl_pass_open_rpl *rpl = mtod(m, void *);
313237263Snp	int stid = GET_TID(rpl);
314237263Snp	struct listen_ctx *lctx;
315237263Snp	struct inpcb *inp;
316174641Skmacy
317237263Snp	/*
318237263Snp	 * We get these replies also when setting up HW filters.  Just throw
319237263Snp	 * those away.
320237263Snp	 */
321237263Snp	if (stid >= td->tid_maps.stid_base + td->tid_maps.nstids)
322237263Snp		goto done;
323237263Snp
324237263Snp	lctx = lookup_stid(&td->tid_maps, stid);
325237263Snp	inp = lctx->inp;
326237263Snp
327237263Snp	INP_WLOCK(inp);
328237263Snp
329237263Snp	CTR4(KTR_CXGB, "%s: stid %u, status %u, flags 0x%x",
330237263Snp	    __func__, stid, rpl->status, lctx->flags);
331237263Snp
332237263Snp	lctx->flags &= ~LCTX_RPL_PENDING;
333237263Snp
334174641Skmacy	if (rpl->status != CPL_ERR_NONE) {
335237263Snp		log(LOG_ERR, "%s: %s: hw listen (stid %d) failed: %d\n",
336237263Snp		    __func__, device_get_nameunit(sc->dev), stid, rpl->status);
337237263Snp	}
338174641Skmacy
339237263Snp#ifdef INVARIANTS
340237263Snp	/*
341237263Snp	 * If the inp has been dropped (listening socket closed) then
342237263Snp	 * listen_stop must have run and taken the inp out of the hash.
343237263Snp	 */
344237263Snp	if (inp->inp_flags & INP_DROPPED) {
345237263Snp		KASSERT(listen_hash_del(td, inp) == NULL,
346237263Snp		    ("%s: inp %p still in listen hash", __func__, inp));
347237263Snp	}
348174641Skmacy#endif
349237263Snp
350237263Snp	if (inp->inp_flags & INP_DROPPED && rpl->status != CPL_ERR_NONE) {
351237263Snp		if (release_lctx(td, lctx) != NULL)
352237263Snp			INP_WUNLOCK(inp);
353237263Snp		goto done;
354174641Skmacy	}
355237263Snp
356237263Snp	/*
357237263Snp	 * Listening socket stopped listening earlier and now the chip tells us
358237263Snp	 * it has started the hardware listener.  Stop it; the lctx will be
359237263Snp	 * released in do_close_server_rpl.
360237263Snp	 */
361237263Snp	if (inp->inp_flags & INP_DROPPED) {
362237263Snp		destroy_server(sc, lctx);
363237263Snp		INP_WUNLOCK(inp);
364237263Snp		goto done;
365237263Snp	}
366237263Snp
367237263Snp	/*
368237263Snp	 * Failed to start hardware listener.  Take inp out of the hash and
369237263Snp	 * release our reference on it.  An error message has been logged
370237263Snp	 * already.
371237263Snp	 */
372237263Snp	if (rpl->status != CPL_ERR_NONE) {
373237263Snp		listen_hash_del(td, inp);
374237263Snp		if (release_lctx(td, lctx) != NULL)
375237263Snp			INP_WUNLOCK(inp);
376237263Snp		goto done;
377237263Snp	}
378237263Snp
379237263Snp	/* hardware listener open for business */
380237263Snp
381237263Snp	INP_WUNLOCK(inp);
382237263Snpdone:
383237263Snp	m_freem(m);
384237263Snp	return (0);
385174641Skmacy}
386174641Skmacy
387237263Snpstatic void
388237263Snppass_accept_req_to_protohdrs(const struct cpl_pass_accept_req *cpl,
389237263Snp    struct in_conninfo *inc, struct tcphdr *th, struct tcpopt *to)
390174641Skmacy{
391237263Snp	const struct tcp_options *t3opt = &cpl->tcp_options;
392237263Snp
393237263Snp	bzero(inc, sizeof(*inc));
394237263Snp	inc->inc_faddr.s_addr = cpl->peer_ip;
395237263Snp	inc->inc_laddr.s_addr = cpl->local_ip;
396237263Snp	inc->inc_fport = cpl->peer_port;
397237263Snp	inc->inc_lport = cpl->local_port;
398237263Snp
399237263Snp	bzero(th, sizeof(*th));
400237263Snp	th->th_sport = cpl->peer_port;
401237263Snp	th->th_dport = cpl->local_port;
402237263Snp	th->th_seq = be32toh(cpl->rcv_isn); /* as in tcp_fields_to_host */
403237263Snp	th->th_flags = TH_SYN;
404237263Snp
405237263Snp	bzero(to, sizeof(*to));
406237263Snp	if (t3opt->mss) {
407237263Snp		to->to_flags |= TOF_MSS;
408237263Snp		to->to_mss = be16toh(t3opt->mss);
409237263Snp	}
410237263Snp	if (t3opt->wsf) {
411237263Snp		to->to_flags |= TOF_SCALE;
412237263Snp		to->to_wscale = t3opt->wsf;
413237263Snp	}
414237263Snp	if (t3opt->tstamp)
415237263Snp		to->to_flags |= TOF_TS;
416237263Snp	if (t3opt->sack)
417237263Snp		to->to_flags |= TOF_SACKPERM;
418174641Skmacy}
419174641Skmacy
420237263Snpstatic inline void
421237263Snphold_synqe(struct synq_entry *synqe)
422174641Skmacy{
423237263Snp
424237263Snp	refcount_acquire(&synqe->refcnt);
425174641Skmacy}
426174641Skmacy
427237263Snpstatic inline void
428237263Snprelease_synqe(struct synq_entry *synqe)
429237263Snp{
430237263Snp
431237263Snp	if (refcount_release(&synqe->refcnt))
432237263Snp		m_freem(synqe->m);
433237263Snp}
434237263Snp
435174641Skmacy/*
436237263Snp * Use the trailing space in the mbuf in which the PASS_ACCEPT_REQ arrived to
437237263Snp * store some state temporarily.  There will be enough room in the mbuf's
438237263Snp * trailing space as the CPL is not that large.
439237263Snp *
440237263Snp * XXX: bad hack.
441174641Skmacy */
442237263Snpstatic struct synq_entry *
443237263Snpmbuf_to_synq_entry(struct mbuf *m)
444174641Skmacy{
445237263Snp	int len = roundup(sizeof (struct synq_entry), 8);
446174641Skmacy
447237263Snp	if (__predict_false(M_TRAILINGSPACE(m) < len)) {
448237263Snp	    panic("%s: no room for synq_entry (%td, %d)\n", __func__,
449237263Snp	    M_TRAILINGSPACE(m), len);
450237263Snp	}
451174641Skmacy
452276563Srwatson	return ((void *)(M_START(m) + M_SIZE(m) - len));
453174641Skmacy}
454174641Skmacy
455237263Snp#ifdef KTR
456237263Snp#define REJECT_PASS_ACCEPT()	do { \
457237263Snp	reject_reason = __LINE__; \
458237263Snp	goto reject; \
459237263Snp} while (0)
460237263Snp#else
461237263Snp#define REJECT_PASS_ACCEPT()	do { goto reject; } while (0)
462237263Snp#endif
463237263Snp
464174641Skmacy/*
465237263Snp * The context associated with a tid entry via insert_tid could be a synq_entry
466237263Snp * or a toepcb.  The only way CPL handlers can tell is via a bit in these flags.
467174641Skmacy */
468237263SnpCTASSERT(offsetof(struct toepcb, tp_flags) == offsetof(struct synq_entry, flags));
469237263Snp
470237263Snp/*
471237263Snp * Handle a CPL_PASS_ACCEPT_REQ message.
472237263Snp */
473174641Skmacystatic int
474237263Snpdo_pass_accept_req(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m)
475174641Skmacy{
476237263Snp	struct adapter *sc = qs->adap;
477237263Snp	struct tom_data *td = sc->tom_softc;
478237263Snp	struct toedev *tod = &td->tod;
479237263Snp	const struct cpl_pass_accept_req *req = mtod(m, void *);
480237263Snp	unsigned int stid = G_PASS_OPEN_TID(ntohl(req->tos_tid));
481237263Snp	unsigned int tid = GET_TID(req);
482237263Snp	struct listen_ctx *lctx = lookup_stid(&td->tid_maps, stid);
483237263Snp	struct l2t_entry *e = NULL;
484293309Smelifaro	struct nhop4_basic nh4;
485237263Snp	struct sockaddr_in nam;
486237263Snp	struct inpcb *inp;
487237263Snp	struct socket *so;
488237263Snp	struct port_info *pi;
489237263Snp	struct ifnet *ifp;
490237263Snp	struct in_conninfo inc;
491237263Snp	struct tcphdr th;
492237263Snp	struct tcpopt to;
493237263Snp	struct synq_entry *synqe = NULL;
494237263Snp	int i;
495237263Snp#ifdef KTR
496237263Snp	int reject_reason;
497237263Snp#endif
498174641Skmacy
499237263Snp	CTR4(KTR_CXGB, "%s: stid %u, tid %u, lctx %p", __func__, stid, tid,
500237263Snp	    lctx);
501237263Snp
502237263Snp	pass_accept_req_to_protohdrs(req, &inc, &th, &to);
503237263Snp
504237263Snp	/*
505237263Snp	 * Don't offload if the interface that received the SYN doesn't have
506237263Snp	 * IFCAP_TOE enabled.
507237263Snp	 */
508237263Snp	pi = NULL;
509237263Snp	for_each_port(sc, i) {
510237263Snp		if (memcmp(sc->port[i].hw_addr, req->dst_mac, ETHER_ADDR_LEN))
511237263Snp			continue;
512237263Snp		pi = &sc->port[i];
513237263Snp		break;
514237263Snp	}
515237263Snp	if (pi == NULL)
516237263Snp		REJECT_PASS_ACCEPT();
517237263Snp	ifp = pi->ifp;
518237263Snp	if ((ifp->if_capenable & IFCAP_TOE4) == 0)
519237263Snp		REJECT_PASS_ACCEPT();
520237263Snp
521237263Snp	/*
522237263Snp	 * Don't offload if the outgoing interface for the route back to the
523237263Snp	 * peer is not the same as the interface that received the SYN.
524237263Snp	 */
525237263Snp	bzero(&nam, sizeof(nam));
526237263Snp	nam.sin_len = sizeof(nam);
527237263Snp	nam.sin_family = AF_INET;
528237263Snp	nam.sin_addr = inc.inc_faddr;
529293309Smelifaro	if (fib4_lookup_nh_basic(RT_DEFAULT_FIB, nam.sin_addr, 0, 0, &nh4) != 0)
530237263Snp		REJECT_PASS_ACCEPT();
531237263Snp	else {
532293309Smelifaro		nam.sin_addr = nh4.nh_addr;
533293309Smelifaro		if (nh4.nh_ifp == ifp)
534293309Smelifaro			e = t3_l2t_get(pi, ifp, (struct sockaddr *)&nam);
535237263Snp		if (e == NULL)
536237263Snp			REJECT_PASS_ACCEPT();	/* no l2te, or ifp mismatch */
537237263Snp	}
538237263Snp
539286227Sjch	INP_INFO_RLOCK(&V_tcbinfo);
540237263Snp
541237263Snp	/* Don't offload if the 4-tuple is already in use */
542237263Snp	if (toe_4tuple_check(&inc, &th, ifp) != 0) {
543286227Sjch		INP_INFO_RUNLOCK(&V_tcbinfo);
544237263Snp		REJECT_PASS_ACCEPT();
545237263Snp	}
546237263Snp
547237263Snp	inp = lctx->inp;	/* listening socket (not owned by the TOE) */
548237263Snp	INP_WLOCK(inp);
549237263Snp	if (__predict_false(inp->inp_flags & INP_DROPPED)) {
550237263Snp		/*
551237263Snp		 * The listening socket has closed.  The reply from the TOE to
552237263Snp		 * our CPL_CLOSE_LISTSRV_REQ will ultimately release all
553237263Snp		 * resources tied to this listen context.
554237263Snp		 */
555237263Snp		INP_WUNLOCK(inp);
556286227Sjch		INP_INFO_RUNLOCK(&V_tcbinfo);
557237263Snp		REJECT_PASS_ACCEPT();
558237263Snp	}
559237263Snp	so = inp->inp_socket;
560237263Snp
561237263Snp	/* Reuse the mbuf that delivered the CPL to us */
562237263Snp	synqe = mbuf_to_synq_entry(m);
563237263Snp	synqe->flags = TP_IS_A_SYNQ_ENTRY;
564237263Snp	synqe->m = m;
565237263Snp	synqe->lctx = lctx;
566237263Snp	synqe->tid = tid;
567237263Snp	synqe->e = e;
568237263Snp	synqe->opt0h = calc_opt0h(so, 0, 0, e);
569237263Snp	synqe->qset = pi->first_qset + (arc4random() % pi->nqsets);
570237263Snp	SOCKBUF_LOCK(&so->so_rcv);
571237263Snp	synqe->rx_credits = min(select_rcv_wnd(so) >> 10, M_RCV_BUFSIZ);
572237263Snp	SOCKBUF_UNLOCK(&so->so_rcv);
573237263Snp	refcount_init(&synqe->refcnt, 1);
574237263Snp	atomic_store_rel_int(&synqe->reply, RPL_OK);
575237263Snp
576237263Snp	insert_tid(td, synqe, tid);
577237263Snp	TAILQ_INSERT_TAIL(&lctx->synq, synqe, link);
578237263Snp	hold_synqe(synqe);
579237263Snp	hold_lctx(lctx);
580237263Snp
581237263Snp	/* syncache_add releases both pcbinfo and pcb locks */
582237263Snp	toe_syncache_add(&inc, &to, &th, inp, tod, synqe);
583237263Snp	INP_UNLOCK_ASSERT(inp);
584237263Snp	INP_INFO_UNLOCK_ASSERT(&V_tcbinfo);
585237263Snp
586237263Snp	/*
587237263Snp	 * If we replied during syncache_add (reply is RPL_DONE), good.
588237263Snp	 * Otherwise (reply is unchanged - RPL_OK) it's no longer ok to reply.
589237263Snp	 * The mbuf will stick around as long as the entry is in the syncache.
590237263Snp	 * The kernel is free to retry syncache_respond but we'll ignore it due
591237263Snp	 * to RPL_DONT.
592237263Snp	 */
593237263Snp	if (atomic_cmpset_int(&synqe->reply, RPL_OK, RPL_DONT)) {
594237263Snp
595237263Snp		INP_WLOCK(inp);
596237263Snp		if (__predict_false(inp->inp_flags & INP_DROPPED)) {
597237263Snp			/* listener closed.  synqe must have been aborted. */
598237263Snp			KASSERT(synqe->flags & TP_ABORT_SHUTDOWN,
599237263Snp			    ("%s: listener %p closed but synqe %p not aborted",
600237263Snp			    __func__, inp, synqe));
601237263Snp
602237263Snp			CTR5(KTR_CXGB,
603237263Snp			    "%s: stid %u, tid %u, lctx %p, synqe %p, ABORTED",
604237263Snp			    __func__, stid, tid, lctx, synqe);
605237263Snp			INP_WUNLOCK(inp);
606237263Snp			release_synqe(synqe);
607237263Snp			return (__LINE__);
608174641Skmacy		}
609237263Snp
610237263Snp		KASSERT(!(synqe->flags & TP_ABORT_SHUTDOWN),
611237263Snp		    ("%s: synqe %p aborted, but listener %p not dropped.",
612237263Snp		    __func__, synqe, inp));
613237263Snp
614237263Snp		TAILQ_REMOVE(&lctx->synq, synqe, link);
615237263Snp		release_synqe(synqe);	/* removed from synq list */
616237263Snp		inp = release_lctx(td, lctx);
617237263Snp		if (inp)
618237263Snp			INP_WUNLOCK(inp);
619237263Snp
620237263Snp		release_synqe(synqe);	/* about to exit function */
621237263Snp		REJECT_PASS_ACCEPT();
622237263Snp	}
623237263Snp
624237263Snp	KASSERT(synqe->reply == RPL_DONE,
625237263Snp	    ("%s: reply %d", __func__, synqe->reply));
626237263Snp
627237263Snp	CTR3(KTR_CXGB, "%s: stid %u, tid %u, OK", __func__, stid, tid);
628237263Snp	release_synqe(synqe);
629237263Snp	return (0);
630237263Snp
631237263Snpreject:
632237263Snp	CTR4(KTR_CXGB, "%s: stid %u, tid %u, REJECT (%d)", __func__, stid, tid,
633237263Snp	    reject_reason);
634237263Snp
635237263Snp	if (synqe == NULL)
636237263Snp		m_freem(m);
637237263Snp	if (e)
638237263Snp		l2t_release(td->l2t, e);
639237263Snp	queue_tid_release(tod, tid);
640237263Snp
641237263Snp	return (0);
642174641Skmacy}
643174641Skmacy
644237263Snpstatic void
645237263Snppass_establish_to_protohdrs(const struct cpl_pass_establish *cpl,
646237263Snp    struct in_conninfo *inc, struct tcphdr *th, struct tcpopt *to)
647237263Snp{
648237263Snp	uint16_t tcp_opt = be16toh(cpl->tcp_opt);
649237263Snp
650237263Snp	bzero(inc, sizeof(*inc));
651237263Snp	inc->inc_faddr.s_addr = cpl->peer_ip;
652237263Snp	inc->inc_laddr.s_addr = cpl->local_ip;
653237263Snp	inc->inc_fport = cpl->peer_port;
654237263Snp	inc->inc_lport = cpl->local_port;
655237263Snp
656237263Snp	bzero(th, sizeof(*th));
657237263Snp	th->th_sport = cpl->peer_port;
658237263Snp	th->th_dport = cpl->local_port;
659237263Snp	th->th_flags = TH_ACK;
660237263Snp	th->th_seq = be32toh(cpl->rcv_isn); /* as in tcp_fields_to_host */
661237263Snp	th->th_ack = be32toh(cpl->snd_isn); /* ditto */
662237263Snp
663237263Snp	bzero(to, sizeof(*to));
664237263Snp	if (G_TCPOPT_TSTAMP(tcp_opt))
665237263Snp		to->to_flags |= TOF_TS;
666237263Snp}
667237263Snp
668174641Skmacy/*
669237263Snp * Process a CPL_PASS_ESTABLISH message.  The T3 has already established a
670237263Snp * connection and we need to do the software side setup.
671174641Skmacy */
672174641Skmacystatic int
673237263Snpdo_pass_establish(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m)
674174641Skmacy{
675237263Snp	struct adapter *sc = qs->adap;
676237263Snp	struct tom_data *td = sc->tom_softc;
677237263Snp	struct cpl_pass_establish *cpl = mtod(m, void *);
678237263Snp	struct toedev *tod = &td->tod;
679237263Snp	unsigned int tid = GET_TID(cpl);
680237263Snp	struct synq_entry *synqe = lookup_tid(&td->tid_maps, tid);
681237263Snp	struct toepcb *toep;
682237263Snp	struct socket *so;
683237263Snp	struct listen_ctx *lctx = synqe->lctx;
684286227Sjch	struct inpcb *inp = lctx->inp, *new_inp;
685237263Snp	struct tcpopt to;
686237263Snp	struct tcphdr th;
687237263Snp	struct in_conninfo inc;
688237263Snp#ifdef KTR
689237263Snp	int stid = G_PASS_OPEN_TID(ntohl(cpl->tos_tid));
690237263Snp#endif
691174641Skmacy
692237263Snp	CTR5(KTR_CXGB, "%s: stid %u, tid %u, lctx %p, inp_flags 0x%x",
693237263Snp	    __func__, stid, tid, lctx, inp->inp_flags);
694174641Skmacy
695237263Snp	KASSERT(qs->idx == synqe->qset,
696237263Snp	    ("%s qset mismatch %d %d", __func__, qs->idx, synqe->qset));
697237263Snp
698286227Sjch	INP_INFO_RLOCK(&V_tcbinfo);	/* for syncache_expand */
699237263Snp	INP_WLOCK(inp);
700237263Snp
701237263Snp	if (__predict_false(inp->inp_flags & INP_DROPPED)) {
702237263Snp		/*
703237263Snp		 * The listening socket has closed.  The TOM must have aborted
704237263Snp		 * all the embryonic connections (including this one) that were
705237263Snp		 * on the lctx's synq.  do_abort_rpl for the tid is responsible
706237263Snp		 * for cleaning up.
707237263Snp		 */
708237263Snp		KASSERT(synqe->flags & TP_ABORT_SHUTDOWN,
709237263Snp		    ("%s: listen socket dropped but tid %u not aborted.",
710237263Snp		    __func__, tid));
711237263Snp		INP_WUNLOCK(inp);
712286227Sjch		INP_INFO_RUNLOCK(&V_tcbinfo);
713237263Snp		m_freem(m);
714237263Snp		return (0);
715237263Snp	}
716237263Snp
717237263Snp	pass_establish_to_protohdrs(cpl, &inc, &th, &to);
718237263Snp
719237263Snp	/* Lie in order to pass the checks in syncache_expand */
720237263Snp	to.to_tsecr = synqe->ts;
721237263Snp	th.th_ack = synqe->iss + 1;
722237263Snp
723237263Snp	toep = toepcb_alloc(tod);
724237263Snp	if (toep == NULL) {
725237263Snpreset:
726237263Snp		t3_send_reset_synqe(tod, synqe);
727237263Snp		INP_WUNLOCK(inp);
728286227Sjch		INP_INFO_RUNLOCK(&V_tcbinfo);
729237263Snp		m_freem(m);
730237263Snp		return (0);
731237263Snp	}
732237263Snp	toep->tp_qset = qs->idx;
733237263Snp	toep->tp_l2t = synqe->e;
734237263Snp	toep->tp_tid = tid;
735237263Snp	toep->tp_rx_credits = synqe->rx_credits;
736237263Snp
737237263Snp	synqe->toep = toep;
738237263Snp	synqe->cpl = cpl;
739237263Snp
740237263Snp	so = inp->inp_socket;
741237263Snp	if (!toe_syncache_expand(&inc, &to, &th, &so) || so == NULL) {
742237263Snp		toepcb_free(toep);
743237263Snp		goto reset;
744237263Snp	}
745237263Snp
746286227Sjch	/* New connection inpcb is already locked by syncache_expand(). */
747286227Sjch	new_inp = sotoinpcb(so);
748286227Sjch	INP_WLOCK_ASSERT(new_inp);
749286227Sjch
750239544Snp	if (__predict_false(!(synqe->flags & TP_SYNQE_EXPANDED))) {
751239544Snp		tcp_timer_activate(intotcpcb(new_inp), TT_KEEP, 0);
752239544Snp		t3_offload_socket(tod, synqe, so);
753239544Snp	}
754239544Snp
755286227Sjch	INP_WUNLOCK(new_inp);
756286227Sjch
757237263Snp	/* Remove the synq entry and release its reference on the lctx */
758237263Snp	TAILQ_REMOVE(&lctx->synq, synqe, link);
759237263Snp	inp = release_lctx(td, lctx);
760237263Snp	if (inp)
761237263Snp		INP_WUNLOCK(inp);
762286227Sjch	INP_INFO_RUNLOCK(&V_tcbinfo);
763237263Snp	release_synqe(synqe);
764237263Snp
765237263Snp	m_freem(m);
766237263Snp	return (0);
767174641Skmacy}
768174641Skmacy
769237263Snpvoid
770237263Snpt3_init_listen_cpl_handlers(struct adapter *sc)
771237263Snp{
772237263Snp	t3_register_cpl_handler(sc, CPL_PASS_OPEN_RPL, do_pass_open_rpl);
773237263Snp	t3_register_cpl_handler(sc, CPL_CLOSE_LISTSRV_RPL, do_close_server_rpl);
774237263Snp	t3_register_cpl_handler(sc, CPL_PASS_ACCEPT_REQ, do_pass_accept_req);
775237263Snp	t3_register_cpl_handler(sc, CPL_PASS_ESTABLISH, do_pass_establish);
776237263Snp}
777237263Snp
778174641Skmacy/*
779174641Skmacy * Start a listening server by sending a passive open request to HW.
780237263Snp *
781237263Snp * Can't take adapter lock here and access to sc->flags, sc->open_device_map,
782237263Snp * sc->offload_map, if_capenable are all race prone.
783174641Skmacy */
784237263Snpint
785237263Snpt3_listen_start(struct toedev *tod, struct tcpcb *tp)
786174641Skmacy{
787237263Snp	struct tom_data *td = t3_tomdata(tod);
788237263Snp	struct adapter *sc = tod->tod_softc;
789237263Snp	struct port_info *pi;
790237263Snp	struct inpcb *inp = tp->t_inpcb;
791237263Snp	struct listen_ctx *lctx;
792237263Snp	int i;
793174641Skmacy
794237263Snp	INP_WLOCK_ASSERT(inp);
795174641Skmacy
796237263Snp	if ((inp->inp_vflag & INP_IPV4) == 0)
797237263Snp		return (0);
798174641Skmacy
799237263Snp#ifdef notyet
800237263Snp	ADAPTER_LOCK(sc);
801237263Snp	if (IS_BUSY(sc)) {
802237263Snp		log(LOG_ERR, "%s: listen request ignored, %s is busy",
803237263Snp		    __func__, device_get_nameunit(sc->dev));
804237263Snp		goto done;
805237263Snp	}
806174641Skmacy
807237263Snp	KASSERT(sc->flags & TOM_INIT_DONE,
808237263Snp	    ("%s: TOM not initialized", __func__));
809237263Snp#endif
810174641Skmacy
811237263Snp	if ((sc->open_device_map & sc->offload_map) == 0)
812237263Snp		goto done;	/* no port that's UP with IFCAP_TOE enabled */
813174641Skmacy
814237263Snp	/*
815237263Snp	 * Find a running port with IFCAP_TOE4.  We'll use the first such port's
816237263Snp	 * queues to send the passive open and receive the reply to it.
817237263Snp	 *
818237263Snp	 * XXX: need a way to mark an port in use by offload.  if_cxgbe should
819237263Snp	 * then reject any attempt to bring down such a port (and maybe reject
820237263Snp	 * attempts to disable IFCAP_TOE on that port too?).
821237263Snp	 */
822237263Snp	for_each_port(sc, i) {
823237263Snp		if (isset(&sc->open_device_map, i) &&
824237263Snp		    sc->port[i].ifp->if_capenable & IFCAP_TOE4)
825237263Snp				break;
826237263Snp	}
827237263Snp	KASSERT(i < sc->params.nports,
828237263Snp	    ("%s: no running port with TOE capability enabled.", __func__));
829237263Snp	pi = &sc->port[i];
830174641Skmacy
831237263Snp	if (listen_hash_find(td, inp) != NULL)
832237263Snp		goto done;	/* already setup */
833174641Skmacy
834237263Snp	lctx = alloc_lctx(td, inp, pi->first_qset);
835237263Snp	if (lctx == NULL) {
836237263Snp		log(LOG_ERR,
837237263Snp		    "%s: listen request ignored, %s couldn't allocate lctx\n",
838237263Snp		    __func__, device_get_nameunit(sc->dev));
839237263Snp		goto done;
840237263Snp	}
841237263Snp	listen_hash_add(td, lctx);
842237263Snp
843237263Snp	CTR5(KTR_CXGB, "%s: stid %u (%s), lctx %p, inp %p", __func__,
844237263Snp	    lctx->stid, tcpstates[tp->t_state], lctx, inp);
845237263Snp
846237263Snp	if (create_server(sc, lctx) != 0) {
847237263Snp		log(LOG_ERR, "%s: %s failed to create hw listener.\n", __func__,
848237263Snp		    device_get_nameunit(sc->dev));
849237263Snp		(void) listen_hash_del(td, inp);
850237263Snp		inp = release_lctx(td, lctx);
851237263Snp		/* can't be freed, host stack has a reference */
852237263Snp		KASSERT(inp != NULL, ("%s: inp freed", __func__));
853237263Snp		goto done;
854237263Snp	}
855237263Snp	lctx->flags |= LCTX_RPL_PENDING;
856237263Snpdone:
857237263Snp#ifdef notyet
858237263Snp	ADAPTER_UNLOCK(sc);
859237263Snp#endif
860237263Snp	return (0);
861174641Skmacy}
862174641Skmacy
863174641Skmacy/*
864174641Skmacy * Stop a listening server by sending a close_listsvr request to HW.
865174641Skmacy * The server TID is freed when we get the reply.
866174641Skmacy */
867237263Snpint
868237263Snpt3_listen_stop(struct toedev *tod, struct tcpcb *tp)
869174641Skmacy{
870174641Skmacy	struct listen_ctx *lctx;
871237263Snp	struct adapter *sc = tod->tod_softc;
872237263Snp	struct tom_data *td = t3_tomdata(tod);
873237263Snp	struct inpcb *inp = tp->t_inpcb;
874237263Snp	struct synq_entry *synqe;
875174641Skmacy
876237263Snp	INP_WLOCK_ASSERT(inp);
877237263Snp
878237263Snp	lctx = listen_hash_del(td, inp);
879237263Snp	if (lctx == NULL)
880237263Snp		return (ENOENT);	/* no hardware listener for this inp */
881237263Snp
882237263Snp	CTR4(KTR_CXGB, "%s: stid %u, lctx %p, flags %x", __func__, lctx->stid,
883237263Snp	    lctx, lctx->flags);
884237263Snp
885174641Skmacy	/*
886237263Snp	 * If the reply to the PASS_OPEN is still pending we'll wait for it to
887237263Snp	 * arrive and clean up when it does.
888174641Skmacy	 */
889237263Snp	if (lctx->flags & LCTX_RPL_PENDING) {
890237263Snp		KASSERT(TAILQ_EMPTY(&lctx->synq),
891237263Snp		    ("%s: synq not empty.", __func__));
892237263Snp		return (EINPROGRESS);
893237263Snp	}
894174641Skmacy
895237263Snp	/*
896237263Snp	 * The host stack will abort all the connections on the listening
897237263Snp	 * socket's so_comp.  It doesn't know about the connections on the synq
898237263Snp	 * so we need to take care of those.
899237263Snp	 */
900237263Snp	TAILQ_FOREACH(synqe, &lctx->synq, link) {
901237263Snp		KASSERT(synqe->lctx == lctx, ("%s: synq corrupt", __func__));
902237263Snp		t3_send_reset_synqe(tod, synqe);
903174641Skmacy	}
904174641Skmacy
905237263Snp	destroy_server(sc, lctx);
906237263Snp	return (0);
907237263Snp}
908174641Skmacy
909237263Snpvoid
910237263Snpt3_syncache_added(struct toedev *tod __unused, void *arg)
911237263Snp{
912237263Snp	struct synq_entry *synqe = arg;
913237263Snp
914237263Snp	hold_synqe(synqe);
915174641Skmacy}
916237263Snp
917237263Snpvoid
918237263Snpt3_syncache_removed(struct toedev *tod __unused, void *arg)
919237263Snp{
920237263Snp	struct synq_entry *synqe = arg;
921237263Snp
922237263Snp	release_synqe(synqe);
923237263Snp}
924237263Snp
925237263Snpint
926237263Snpt3_syncache_respond(struct toedev *tod, void *arg, struct mbuf *m)
927237263Snp{
928237263Snp	struct adapter *sc = tod->tod_softc;
929237263Snp	struct synq_entry *synqe = arg;
930237263Snp	struct l2t_entry *e = synqe->e;
931237263Snp	struct ip *ip = mtod(m, struct ip *);
932237263Snp	struct tcphdr *th = (void *)(ip + 1);
933237263Snp	struct cpl_pass_accept_rpl *rpl;
934237263Snp	struct mbuf *r;
935237263Snp	struct listen_ctx *lctx = synqe->lctx;
936237263Snp	struct tcpopt to;
937237263Snp	int mtu_idx, cpu_idx;
938237263Snp
939237263Snp	/*
940237263Snp	 * The first time we run it's during the call to syncache_add.  That's
941237263Snp	 * the only one we care about.
942237263Snp	 */
943237263Snp	if (atomic_cmpset_int(&synqe->reply, RPL_OK, RPL_DONE) == 0)
944237263Snp		goto done;	/* reply to the CPL only if it's ok to do so */
945237263Snp
946237263Snp	r = M_GETHDR_OFLD(lctx->qset, CPL_PRIORITY_CONTROL, rpl);
947237263Snp	if (r == NULL)
948237263Snp		goto done;
949237263Snp
950237263Snp	/*
951237263Snp	 * Use only the provided mbuf (with ip and tcp headers) and what's in
952237263Snp	 * synqe.  Avoid looking at the listening socket (lctx->inp) here.
953237263Snp	 *
954237263Snp	 * XXX: if the incoming SYN had the TCP timestamp option but the kernel
955237263Snp	 * decides it doesn't want to use TCP timestamps we have no way of
956237263Snp	 * relaying this info to the chip on a per-tid basis (all we have is a
957237263Snp	 * global knob).
958237263Snp	 */
959237263Snp	bzero(&to, sizeof(to));
960237263Snp	tcp_dooptions(&to, (void *)(th + 1), (th->th_off << 2) - sizeof(*th),
961237263Snp	    TO_SYN);
962237263Snp
963237263Snp	/* stash them for later */
964237263Snp	synqe->iss = be32toh(th->th_seq);
965237263Snp	synqe->ts = to.to_tsval;
966237263Snp
967237263Snp	mtu_idx = find_best_mtu_idx(sc, NULL, to.to_mss);
968237263Snp	cpu_idx = sc->rrss_map[synqe->qset];
969237263Snp
970237263Snp	rpl->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
971237263Snp	rpl->wr.wrh_lo = 0;
972237263Snp	OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL, synqe->tid));
973237263Snp	rpl->opt2 = calc_opt2(cpu_idx);
974237263Snp	rpl->rsvd = rpl->opt2;		/* workaround for HW bug */
975237263Snp	rpl->peer_ip = ip->ip_dst.s_addr;
976237263Snp	rpl->opt0h = synqe->opt0h |
977237263Snp	    calc_opt0h(NULL, mtu_idx, to.to_wscale, NULL);
978237263Snp	rpl->opt0l_status = htobe32(CPL_PASS_OPEN_ACCEPT) |
979237263Snp	    calc_opt0l(NULL, synqe->rx_credits);
980237263Snp
981237263Snp	l2t_send(sc, r, e);
982237263Snpdone:
983237263Snp	m_freem(m);
984237263Snp	return (0);
985237263Snp}
986237263Snp
987237263Snpint
988237263Snpdo_abort_req_synqe(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m)
989237263Snp{
990237263Snp	struct adapter *sc = qs->adap;
991237263Snp	struct tom_data *td = sc->tom_softc;
992237263Snp	struct toedev *tod = &td->tod;
993237263Snp	const struct cpl_abort_req_rss *req = mtod(m, void *);
994237263Snp	unsigned int tid = GET_TID(req);
995237263Snp	struct synq_entry *synqe = lookup_tid(&td->tid_maps, tid);
996237263Snp	struct listen_ctx *lctx = synqe->lctx;
997237263Snp	struct inpcb *inp = lctx->inp;
998237263Snp
999237263Snp	KASSERT(synqe->flags & TP_IS_A_SYNQ_ENTRY,
1000237263Snp	    ("%s: !SYNQ_ENTRY", __func__));
1001237263Snp
1002237263Snp	CTR6(KTR_CXGB, "%s: tid %u, synqe %p (%x), lctx %p, status %d",
1003237263Snp	    __func__, tid, synqe, synqe->flags, synqe->lctx, req->status);
1004237263Snp
1005237263Snp	INP_WLOCK(inp);
1006237263Snp
1007237263Snp	if (!(synqe->flags & TP_ABORT_REQ_RCVD)) {
1008237263Snp		synqe->flags |= TP_ABORT_REQ_RCVD;
1009237263Snp		synqe->flags |= TP_ABORT_SHUTDOWN;
1010237263Snp		INP_WUNLOCK(inp);
1011237263Snp		m_freem(m);
1012237263Snp		return (0);
1013237263Snp	}
1014237263Snp	synqe->flags &= ~TP_ABORT_REQ_RCVD;
1015237263Snp
1016237263Snp	/*
1017237263Snp	 * If we'd sent a reset on this synqe, we'll ignore this and clean up in
1018237263Snp	 * the T3's reply to our reset instead.
1019237263Snp	 */
1020237263Snp	if (synqe->flags & TP_ABORT_RPL_PENDING) {
1021237263Snp		synqe->flags |= TP_ABORT_RPL_SENT;
1022237263Snp		INP_WUNLOCK(inp);
1023237263Snp	} else {
1024237263Snp		TAILQ_REMOVE(&lctx->synq, synqe, link);
1025237263Snp		inp = release_lctx(td, lctx);
1026237263Snp		if (inp)
1027237263Snp			INP_WUNLOCK(inp);
1028237263Snp		release_tid(tod, tid, qs->idx);
1029237263Snp		l2t_release(td->l2t, synqe->e);
1030237263Snp		release_synqe(synqe);
1031237263Snp	}
1032237263Snp
1033237263Snp	send_abort_rpl(tod, tid, qs->idx);
1034237263Snp	m_freem(m);
1035237263Snp	return (0);
1036237263Snp}
1037237263Snp
1038237263Snpint
1039237263Snpdo_abort_rpl_synqe(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m)
1040237263Snp{
1041237263Snp	struct adapter *sc = qs->adap;
1042237263Snp	struct tom_data *td = sc->tom_softc;
1043237263Snp	struct toedev *tod = &td->tod;
1044237263Snp	const struct cpl_abort_rpl_rss *rpl = mtod(m, void *);
1045237263Snp	unsigned int tid = GET_TID(rpl);
1046237263Snp	struct synq_entry *synqe = lookup_tid(&td->tid_maps, tid);
1047237263Snp	struct listen_ctx *lctx = synqe->lctx;
1048237263Snp	struct inpcb *inp = lctx->inp;
1049237263Snp
1050237263Snp	CTR3(KTR_CXGB, "%s: tid %d, synqe %p, status %d", tid, synqe,
1051237263Snp	    rpl->status);
1052237263Snp
1053237263Snp	INP_WLOCK(inp);
1054237263Snp
1055237263Snp	if (synqe->flags & TP_ABORT_RPL_PENDING) {
1056237263Snp		if (!(synqe->flags & TP_ABORT_RPL_RCVD)) {
1057237263Snp			synqe->flags |= TP_ABORT_RPL_RCVD;
1058237263Snp			INP_WUNLOCK(inp);
1059237263Snp		} else {
1060237263Snp			synqe->flags &= ~TP_ABORT_RPL_RCVD;
1061237263Snp			synqe->flags &= TP_ABORT_RPL_PENDING;
1062237263Snp
1063237263Snp			TAILQ_REMOVE(&lctx->synq, synqe, link);
1064237263Snp			inp = release_lctx(td, lctx);
1065237263Snp			if (inp)
1066237263Snp				INP_WUNLOCK(inp);
1067237263Snp			release_tid(tod, tid, qs->idx);
1068237263Snp			l2t_release(td->l2t, synqe->e);
1069237263Snp			release_synqe(synqe);
1070237263Snp		}
1071237263Snp	}
1072237263Snp
1073237263Snp	m_freem(m);
1074237263Snp	return (0);
1075237263Snp}
1076237263Snp
1077237263Snpstatic void
1078237263Snpt3_send_reset_synqe(struct toedev *tod, struct synq_entry *synqe)
1079237263Snp{
1080237263Snp	struct cpl_abort_req *req;
1081237263Snp	unsigned int tid = synqe->tid;
1082237263Snp	struct adapter *sc = tod->tod_softc;
1083237263Snp	struct mbuf *m;
1084237263Snp#ifdef INVARIANTS
1085237263Snp	struct listen_ctx *lctx = synqe->lctx;
1086237263Snp	struct inpcb *inp = lctx->inp;
1087237263Snp#endif
1088237263Snp
1089237263Snp	INP_WLOCK_ASSERT(inp);
1090237263Snp
1091237263Snp	CTR4(KTR_CXGB, "%s: tid %d, synqe %p (%x)", __func__, tid, synqe,
1092237263Snp	    synqe->flags);
1093237263Snp
1094237263Snp	if (synqe->flags & TP_ABORT_SHUTDOWN)
1095237263Snp		return;
1096237263Snp
1097237263Snp	synqe->flags |= (TP_ABORT_RPL_PENDING | TP_ABORT_SHUTDOWN);
1098237263Snp
1099237263Snp	m = M_GETHDR_OFLD(synqe->qset, CPL_PRIORITY_DATA, req);
1100237263Snp	if (m == NULL)
1101237263Snp		CXGB_UNIMPLEMENTED();
1102237263Snp
1103237263Snp	req->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_REQ));
1104237263Snp	req->wr.wrh_lo = htonl(V_WR_TID(tid));
1105237263Snp	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ABORT_REQ, tid));
1106237263Snp	req->rsvd0 = 0;
1107237263Snp	req->rsvd1 = !(synqe->flags & TP_DATASENT);
1108237263Snp	req->cmd = CPL_ABORT_SEND_RST;
1109237263Snp
1110237263Snp	l2t_send(sc, m, synqe->e);
1111237263Snp}
1112237263Snp
1113237263Snpvoid
1114237263Snpt3_offload_socket(struct toedev *tod, void *arg, struct socket *so)
1115237263Snp{
1116237263Snp	struct adapter *sc = tod->tod_softc;
1117237263Snp	struct tom_data *td = sc->tom_softc;
1118237263Snp	struct synq_entry *synqe = arg;
1119237263Snp#ifdef INVARIANTS
1120237263Snp	struct inpcb *inp = sotoinpcb(so);
1121237263Snp#endif
1122237263Snp	struct cpl_pass_establish *cpl = synqe->cpl;
1123237263Snp	struct toepcb *toep = synqe->toep;
1124237263Snp
1125286227Sjch	INP_INFO_RLOCK_ASSERT(&V_tcbinfo); /* prevents bad race with accept() */
1126237263Snp	INP_WLOCK_ASSERT(inp);
1127237263Snp
1128237263Snp	offload_socket(so, toep);
1129237263Snp	make_established(so, cpl->snd_isn, cpl->rcv_isn, cpl->tcp_opt);
1130237263Snp	update_tid(td, toep, synqe->tid);
1131239544Snp	synqe->flags |= TP_SYNQE_EXPANDED;
1132237263Snp}
1133237263Snp#endif
1134