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"
32247434Snp#include "opt_inet6.h"
33237263Snp
34237263Snp#include <sys/param.h>
35237263Snp#include <sys/types.h>
36237263Snp#include <sys/systm.h>
37237263Snp#include <sys/kernel.h>
38237263Snp#include <sys/ktr.h>
39237263Snp#include <sys/module.h>
40237263Snp#include <sys/protosw.h>
41237263Snp#include <sys/domain.h>
42237263Snp#include <sys/socket.h>
43237263Snp#include <sys/socketvar.h>
44252495Snp#include <sys/taskqueue.h>
45247434Snp#include <net/if.h>
46237263Snp#include <netinet/in.h>
47237263Snp#include <netinet/in_pcb.h>
48247434Snp#include <netinet/in_var.h>
49237263Snp#include <netinet/ip.h>
50247434Snp#include <netinet/ip6.h>
51237263Snp#include <netinet/tcp_var.h>
52247434Snp#include <netinet6/scope6_var.h>
53237263Snp#define TCPSTATES
54237263Snp#include <netinet/tcp_fsm.h>
55237263Snp#include <netinet/toecore.h>
56237263Snp
57237263Snp#ifdef TCP_OFFLOAD
58237263Snp#include "common/common.h"
59237263Snp#include "common/t4_msg.h"
60237263Snp#include "common/t4_regs.h"
61252814Snp#include "common/t4_regs_values.h"
62252814Snp#include "common/t4_tcb.h"
63237263Snp#include "tom/t4_tom_l2t.h"
64237263Snp#include "tom/t4_tom.h"
65237263Snp
66240169Snpstatic struct protosw ddp_protosw;
67240169Snpstatic struct pr_usrreqs ddp_usrreqs;
68240169Snp
69247434Snpstatic struct protosw ddp6_protosw;
70247434Snpstatic struct pr_usrreqs ddp6_usrreqs;
71247434Snp
72237263Snp/* Module ops */
73237263Snpstatic int t4_tom_mod_load(void);
74237263Snpstatic int t4_tom_mod_unload(void);
75237263Snpstatic int t4_tom_modevent(module_t, int, void *);
76237263Snp
77237263Snp/* ULD ops and helpers */
78237263Snpstatic int t4_tom_activate(struct adapter *);
79237263Snpstatic int t4_tom_deactivate(struct adapter *);
80237263Snp
81237263Snpstatic struct uld_info tom_uld_info = {
82237263Snp	.uld_id = ULD_TOM,
83237263Snp	.activate = t4_tom_activate,
84237263Snp	.deactivate = t4_tom_deactivate,
85237263Snp};
86237263Snp
87237263Snpstatic void queue_tid_release(struct adapter *, int);
88237263Snpstatic void release_offload_resources(struct toepcb *);
89237263Snpstatic int alloc_tid_tabs(struct tid_info *);
90237263Snpstatic void free_tid_tabs(struct tid_info *);
91247434Snpstatic int add_lip(struct adapter *, struct in6_addr *);
92247434Snpstatic int delete_lip(struct adapter *, struct in6_addr *);
93247434Snpstatic struct clip_entry *search_lip(struct tom_data *, struct in6_addr *);
94247434Snpstatic void init_clip_table(struct adapter *, struct tom_data *);
95252495Snpstatic void update_clip(struct adapter *, void *);
96252495Snpstatic void t4_clip_task(void *, int);
97252495Snpstatic void update_clip_table(struct adapter *, struct tom_data *);
98247434Snpstatic void destroy_clip_table(struct adapter *, struct tom_data *);
99237263Snpstatic void free_tom_data(struct adapter *, struct tom_data *);
100237263Snp
101252495Snpstatic int in6_ifaddr_gen;
102252495Snpstatic eventhandler_tag ifaddr_evhandler;
103252495Snpstatic struct timeout_task clip_task;
104252495Snp
105237263Snpstruct toepcb *
106237263Snpalloc_toepcb(struct port_info *pi, int txqid, int rxqid, int flags)
107237263Snp{
108237263Snp	struct adapter *sc = pi->adapter;
109237263Snp	struct toepcb *toep;
110237263Snp	int tx_credits, txsd_total, len;
111237263Snp
112237263Snp	/*
113237263Snp	 * The firmware counts tx work request credits in units of 16 bytes
114237263Snp	 * each.  Reserve room for an ABORT_REQ so the driver never has to worry
115237263Snp	 * about tx credits if it wants to abort a connection.
116237263Snp	 */
117237263Snp	tx_credits = sc->params.ofldq_wr_cred;
118237263Snp	tx_credits -= howmany(sizeof(struct cpl_abort_req), 16);
119237263Snp
120237263Snp	/*
121237263Snp	 * Shortest possible tx work request is a fw_ofld_tx_data_wr + 1 byte
122237263Snp	 * immediate payload, and firmware counts tx work request credits in
123237263Snp	 * units of 16 byte.  Calculate the maximum work requests possible.
124237263Snp	 */
125237263Snp	txsd_total = tx_credits /
126237263Snp	    howmany((sizeof(struct fw_ofld_tx_data_wr) + 1), 16);
127237263Snp
128237263Snp	if (txqid < 0)
129237263Snp		txqid = (arc4random() % pi->nofldtxq) + pi->first_ofld_txq;
130237263Snp	KASSERT(txqid >= pi->first_ofld_txq &&
131237263Snp	    txqid < pi->first_ofld_txq + pi->nofldtxq,
132237263Snp	    ("%s: txqid %d for port %p (first %d, n %d)", __func__, txqid, pi,
133237263Snp		pi->first_ofld_txq, pi->nofldtxq));
134237263Snp
135237263Snp	if (rxqid < 0)
136237263Snp		rxqid = (arc4random() % pi->nofldrxq) + pi->first_ofld_rxq;
137237263Snp	KASSERT(rxqid >= pi->first_ofld_rxq &&
138237263Snp	    rxqid < pi->first_ofld_rxq + pi->nofldrxq,
139237263Snp	    ("%s: rxqid %d for port %p (first %d, n %d)", __func__, rxqid, pi,
140237263Snp		pi->first_ofld_rxq, pi->nofldrxq));
141237263Snp
142237263Snp	len = offsetof(struct toepcb, txsd) +
143237263Snp	    txsd_total * sizeof(struct ofld_tx_sdesc);
144237263Snp
145237263Snp	toep = malloc(len, M_CXGBE, M_ZERO | flags);
146237263Snp	if (toep == NULL)
147237263Snp		return (NULL);
148237263Snp
149237263Snp	toep->td = sc->tom_softc;
150237263Snp	toep->port = pi;
151265482Snp	toep->tx_total = tx_credits;
152237263Snp	toep->tx_credits = tx_credits;
153237263Snp	toep->ofld_txq = &sc->sge.ofld_txq[txqid];
154237263Snp	toep->ofld_rxq = &sc->sge.ofld_rxq[rxqid];
155237263Snp	toep->ctrlq = &sc->sge.ctrlq[pi->port_id];
156237263Snp	toep->txsd_total = txsd_total;
157237263Snp	toep->txsd_avail = txsd_total;
158237263Snp	toep->txsd_pidx = 0;
159237263Snp	toep->txsd_cidx = 0;
160237263Snp
161237263Snp	return (toep);
162237263Snp}
163237263Snp
164237263Snpvoid
165237263Snpfree_toepcb(struct toepcb *toep)
166237263Snp{
167237263Snp
168240169Snp	KASSERT(!(toep->flags & TPF_ATTACHED),
169237263Snp	    ("%s: attached to an inpcb", __func__));
170240169Snp	KASSERT(!(toep->flags & TPF_CPL_PENDING),
171237263Snp	    ("%s: CPL pending", __func__));
172237263Snp
173237263Snp	free(toep, M_CXGBE);
174237263Snp}
175237263Snp
176237263Snp/*
177237263Snp * Set up the socket for TCP offload.
178237263Snp */
179237263Snpvoid
180237263Snpoffload_socket(struct socket *so, struct toepcb *toep)
181237263Snp{
182237263Snp	struct tom_data *td = toep->td;
183237263Snp	struct inpcb *inp = sotoinpcb(so);
184237263Snp	struct tcpcb *tp = intotcpcb(inp);
185237263Snp	struct sockbuf *sb;
186237263Snp
187237263Snp	INP_WLOCK_ASSERT(inp);
188237263Snp
189237263Snp	/* Update socket */
190237263Snp	sb = &so->so_snd;
191237263Snp	SOCKBUF_LOCK(sb);
192237263Snp	sb->sb_flags |= SB_NOCOALESCE;
193237263Snp	SOCKBUF_UNLOCK(sb);
194237263Snp	sb = &so->so_rcv;
195237263Snp	SOCKBUF_LOCK(sb);
196237263Snp	sb->sb_flags |= SB_NOCOALESCE;
197247434Snp	if (toep->ulp_mode == ULP_MODE_TCPDDP) {
198247434Snp		if (inp->inp_vflag & INP_IPV6)
199247434Snp			so->so_proto = &ddp6_protosw;
200247434Snp		else
201247434Snp			so->so_proto = &ddp_protosw;
202247434Snp	}
203237263Snp	SOCKBUF_UNLOCK(sb);
204237263Snp
205237263Snp	/* Update TCP PCB */
206237263Snp	tp->tod = &td->tod;
207237263Snp	tp->t_toe = toep;
208237263Snp	tp->t_flags |= TF_TOE;
209237263Snp
210237263Snp	/* Install an extra hold on inp */
211237263Snp	toep->inp = inp;
212240169Snp	toep->flags |= TPF_ATTACHED;
213237263Snp	in_pcbref(inp);
214237263Snp
215237263Snp	/* Add the TOE PCB to the active list */
216237263Snp	mtx_lock(&td->toep_list_lock);
217237263Snp	TAILQ_INSERT_HEAD(&td->toep_list, toep, link);
218237263Snp	mtx_unlock(&td->toep_list_lock);
219237263Snp}
220237263Snp
221237263Snp/* This is _not_ the normal way to "unoffload" a socket. */
222237263Snpvoid
223237263Snpundo_offload_socket(struct socket *so)
224237263Snp{
225237263Snp	struct inpcb *inp = sotoinpcb(so);
226237263Snp	struct tcpcb *tp = intotcpcb(inp);
227237263Snp	struct toepcb *toep = tp->t_toe;
228237263Snp	struct tom_data *td = toep->td;
229237263Snp	struct sockbuf *sb;
230237263Snp
231237263Snp	INP_WLOCK_ASSERT(inp);
232237263Snp
233237263Snp	sb = &so->so_snd;
234237263Snp	SOCKBUF_LOCK(sb);
235237263Snp	sb->sb_flags &= ~SB_NOCOALESCE;
236237263Snp	SOCKBUF_UNLOCK(sb);
237237263Snp	sb = &so->so_rcv;
238237263Snp	SOCKBUF_LOCK(sb);
239237263Snp	sb->sb_flags &= ~SB_NOCOALESCE;
240237263Snp	SOCKBUF_UNLOCK(sb);
241237263Snp
242237263Snp	tp->tod = NULL;
243237263Snp	tp->t_toe = NULL;
244237263Snp	tp->t_flags &= ~TF_TOE;
245237263Snp
246237263Snp	toep->inp = NULL;
247240169Snp	toep->flags &= ~TPF_ATTACHED;
248237263Snp	if (in_pcbrele_wlocked(inp))
249237263Snp		panic("%s: inp freed.", __func__);
250237263Snp
251237263Snp	mtx_lock(&td->toep_list_lock);
252237263Snp	TAILQ_REMOVE(&td->toep_list, toep, link);
253237263Snp	mtx_unlock(&td->toep_list_lock);
254237263Snp}
255237263Snp
256237263Snpstatic void
257237263Snprelease_offload_resources(struct toepcb *toep)
258237263Snp{
259237263Snp	struct tom_data *td = toep->td;
260237263Snp	struct adapter *sc = td_adapter(td);
261237263Snp	int tid = toep->tid;
262237263Snp
263240169Snp	KASSERT(!(toep->flags & TPF_CPL_PENDING),
264237263Snp	    ("%s: %p has CPL pending.", __func__, toep));
265240169Snp	KASSERT(!(toep->flags & TPF_ATTACHED),
266237263Snp	    ("%s: %p is still attached.", __func__, toep));
267237263Snp
268247434Snp	CTR5(KTR_CXGBE, "%s: toep %p (tid %d, l2te %p, ce %p)",
269247434Snp	    __func__, toep, tid, toep->l2te, toep->ce);
270237263Snp
271240169Snp	if (toep->ulp_mode == ULP_MODE_TCPDDP)
272240169Snp		release_ddp_resources(toep);
273240169Snp
274237263Snp	if (toep->l2te)
275237263Snp		t4_l2t_release(toep->l2te);
276237263Snp
277237263Snp	if (tid >= 0) {
278237263Snp		remove_tid(sc, tid);
279237263Snp		release_tid(sc, tid, toep->ctrlq);
280237263Snp	}
281237263Snp
282247434Snp	if (toep->ce)
283247434Snp		release_lip(td, toep->ce);
284247434Snp
285237263Snp	mtx_lock(&td->toep_list_lock);
286237263Snp	TAILQ_REMOVE(&td->toep_list, toep, link);
287237263Snp	mtx_unlock(&td->toep_list_lock);
288237263Snp
289237263Snp	free_toepcb(toep);
290237263Snp}
291237263Snp
292237263Snp/*
293237263Snp * The kernel is done with the TCP PCB and this is our opportunity to unhook the
294237263Snp * toepcb hanging off of it.  If the TOE driver is also done with the toepcb (no
295237263Snp * pending CPL) then it is time to release all resources tied to the toepcb.
296237263Snp *
297237263Snp * Also gets called when an offloaded active open fails and the TOM wants the
298237263Snp * kernel to take the TCP PCB back.
299237263Snp */
300237263Snpstatic void
301237263Snpt4_pcb_detach(struct toedev *tod __unused, struct tcpcb *tp)
302237263Snp{
303237263Snp#if defined(KTR) || defined(INVARIANTS)
304237263Snp	struct inpcb *inp = tp->t_inpcb;
305237263Snp#endif
306237263Snp	struct toepcb *toep = tp->t_toe;
307237263Snp
308237263Snp	INP_WLOCK_ASSERT(inp);
309237263Snp
310237263Snp	KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
311240169Snp	KASSERT(toep->flags & TPF_ATTACHED,
312237263Snp	    ("%s: not attached", __func__));
313237263Snp
314237263Snp#ifdef KTR
315237263Snp	if (tp->t_state == TCPS_SYN_SENT) {
316237263Snp		CTR6(KTR_CXGBE, "%s: atid %d, toep %p (0x%x), inp %p (0x%x)",
317237263Snp		    __func__, toep->tid, toep, toep->flags, inp,
318237263Snp		    inp->inp_flags);
319237263Snp	} else {
320237263Snp		CTR6(KTR_CXGBE,
321237263Snp		    "t4_pcb_detach: tid %d (%s), toep %p (0x%x), inp %p (0x%x)",
322237263Snp		    toep->tid, tcpstates[tp->t_state], toep, toep->flags, inp,
323237263Snp		    inp->inp_flags);
324237263Snp	}
325237263Snp#endif
326237263Snp
327237263Snp	tp->t_toe = NULL;
328237263Snp	tp->t_flags &= ~TF_TOE;
329240169Snp	toep->flags &= ~TPF_ATTACHED;
330237263Snp
331240169Snp	if (!(toep->flags & TPF_CPL_PENDING))
332237263Snp		release_offload_resources(toep);
333237263Snp}
334237263Snp
335237263Snp/*
336252814Snp * setsockopt handler.
337252814Snp */
338252814Snpstatic void
339252814Snpt4_ctloutput(struct toedev *tod, struct tcpcb *tp, int dir, int name)
340252814Snp{
341252814Snp	struct adapter *sc = tod->tod_softc;
342252814Snp	struct toepcb *toep = tp->t_toe;
343252814Snp
344252814Snp	if (dir == SOPT_GET)
345252814Snp		return;
346252814Snp
347252814Snp	CTR4(KTR_CXGBE, "%s: tp %p, dir %u, name %u", __func__, tp, dir, name);
348252814Snp
349252814Snp	switch (name) {
350252814Snp	case TCP_NODELAY:
351252814Snp		t4_set_tcb_field(sc, toep, 1, W_TCB_T_FLAGS, V_TF_NAGLE(1),
352252814Snp		    V_TF_NAGLE(tp->t_flags & TF_NODELAY ? 0 : 1));
353252814Snp		break;
354252814Snp	default:
355252814Snp		break;
356252814Snp	}
357252814Snp}
358252814Snp
359252814Snp/*
360237263Snp * The TOE driver will not receive any more CPLs for the tid associated with the
361237263Snp * toepcb; release the hold on the inpcb.
362237263Snp */
363237263Snpvoid
364237263Snpfinal_cpl_received(struct toepcb *toep)
365237263Snp{
366237263Snp	struct inpcb *inp = toep->inp;
367237263Snp
368237263Snp	KASSERT(inp != NULL, ("%s: inp is NULL", __func__));
369237263Snp	INP_WLOCK_ASSERT(inp);
370240169Snp	KASSERT(toep->flags & TPF_CPL_PENDING,
371237263Snp	    ("%s: CPL not pending already?", __func__));
372237263Snp
373237263Snp	CTR6(KTR_CXGBE, "%s: tid %d, toep %p (0x%x), inp %p (0x%x)",
374237263Snp	    __func__, toep->tid, toep, toep->flags, inp, inp->inp_flags);
375237263Snp
376237263Snp	toep->inp = NULL;
377240169Snp	toep->flags &= ~TPF_CPL_PENDING;
378237263Snp
379240169Snp	if (!(toep->flags & TPF_ATTACHED))
380237263Snp		release_offload_resources(toep);
381237263Snp
382237263Snp	if (!in_pcbrele_wlocked(inp))
383237263Snp		INP_WUNLOCK(inp);
384237263Snp}
385237263Snp
386237263Snpvoid
387237263Snpinsert_tid(struct adapter *sc, int tid, void *ctx)
388237263Snp{
389237263Snp	struct tid_info *t = &sc->tids;
390237263Snp
391237263Snp	t->tid_tab[tid] = ctx;
392237263Snp	atomic_add_int(&t->tids_in_use, 1);
393237263Snp}
394237263Snp
395237263Snpvoid *
396237263Snplookup_tid(struct adapter *sc, int tid)
397237263Snp{
398237263Snp	struct tid_info *t = &sc->tids;
399237263Snp
400237263Snp	return (t->tid_tab[tid]);
401237263Snp}
402237263Snp
403237263Snpvoid
404237263Snpupdate_tid(struct adapter *sc, int tid, void *ctx)
405237263Snp{
406237263Snp	struct tid_info *t = &sc->tids;
407237263Snp
408237263Snp	t->tid_tab[tid] = ctx;
409237263Snp}
410237263Snp
411237263Snpvoid
412237263Snpremove_tid(struct adapter *sc, int tid)
413237263Snp{
414237263Snp	struct tid_info *t = &sc->tids;
415237263Snp
416237263Snp	t->tid_tab[tid] = NULL;
417237263Snp	atomic_subtract_int(&t->tids_in_use, 1);
418237263Snp}
419237263Snp
420237263Snpvoid
421237263Snprelease_tid(struct adapter *sc, int tid, struct sge_wrq *ctrlq)
422237263Snp{
423237263Snp	struct wrqe *wr;
424237263Snp	struct cpl_tid_release *req;
425237263Snp
426237263Snp	wr = alloc_wrqe(sizeof(*req), ctrlq);
427237263Snp	if (wr == NULL) {
428237263Snp		queue_tid_release(sc, tid);	/* defer */
429237263Snp		return;
430237263Snp	}
431237263Snp	req = wrtod(wr);
432237263Snp
433237263Snp	INIT_TP_WR_MIT_CPL(req, CPL_TID_RELEASE, tid);
434237263Snp
435237263Snp	t4_wrq_tx(sc, wr);
436237263Snp}
437237263Snp
438237263Snpstatic void
439237263Snpqueue_tid_release(struct adapter *sc, int tid)
440237263Snp{
441237263Snp
442237263Snp	CXGBE_UNIMPLEMENTED("deferred tid release");
443237263Snp}
444237263Snp
445237263Snp/*
446237263Snp * What mtu_idx to use, given a 4-tuple and/or an MSS cap
447237263Snp */
448237263Snpint
449237263Snpfind_best_mtu_idx(struct adapter *sc, struct in_conninfo *inc, int pmss)
450237263Snp{
451237263Snp	unsigned short *mtus = &sc->params.mtus[0];
452247434Snp	int i, mss, n;
453237263Snp
454237263Snp	KASSERT(inc != NULL || pmss > 0,
455237263Snp	    ("%s: at least one of inc/pmss must be specified", __func__));
456237263Snp
457237263Snp	mss = inc ? tcp_mssopt(inc) : pmss;
458237263Snp	if (pmss > 0 && mss > pmss)
459237263Snp		mss = pmss;
460237263Snp
461247434Snp	if (inc->inc_flags & INC_ISIPV6)
462247434Snp		n = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
463247434Snp	else
464247434Snp		n = sizeof(struct ip) + sizeof(struct tcphdr);
465237263Snp
466247434Snp	for (i = 0; i < NMTUS - 1 && mtus[i + 1] <= mss + n; i++)
467247434Snp		continue;
468247434Snp
469237263Snp	return (i);
470237263Snp}
471237263Snp
472237263Snp/*
473237263Snp * Determine the receive window size for a socket.
474237263Snp */
475237263Snpu_long
476237263Snpselect_rcv_wnd(struct socket *so)
477237263Snp{
478237263Snp	unsigned long wnd;
479237263Snp
480237263Snp	SOCKBUF_LOCK_ASSERT(&so->so_rcv);
481237263Snp
482237263Snp	wnd = sbspace(&so->so_rcv);
483237263Snp	if (wnd < MIN_RCV_WND)
484237263Snp		wnd = MIN_RCV_WND;
485237263Snp
486237263Snp	return min(wnd, MAX_RCV_WND);
487237263Snp}
488237263Snp
489237263Snpint
490237263Snpselect_rcv_wscale(void)
491237263Snp{
492237263Snp	int wscale = 0;
493237263Snp	unsigned long space = sb_max;
494237263Snp
495237263Snp	if (space > MAX_RCV_WND)
496237263Snp		space = MAX_RCV_WND;
497237263Snp
498237263Snp	while (wscale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << wscale) < space)
499237263Snp		wscale++;
500237263Snp
501237263Snp	return (wscale);
502237263Snp}
503237263Snp
504237263Snpextern int always_keepalive;
505237263Snp#define VIID_SMACIDX(v)	(((unsigned int)(v) & 0x7f) << 1)
506237263Snp
507237263Snp/*
508237263Snp * socket so could be a listening socket too.
509237263Snp */
510237263Snpuint64_t
511237263Snpcalc_opt0(struct socket *so, struct port_info *pi, struct l2t_entry *e,
512237263Snp    int mtu_idx, int rscale, int rx_credits, int ulp_mode)
513237263Snp{
514237263Snp	uint64_t opt0;
515237263Snp
516237263Snp	KASSERT(rx_credits <= M_RCV_BUFSIZ,
517237263Snp	    ("%s: rcv_bufsiz too high", __func__));
518237263Snp
519237263Snp	opt0 = F_TCAM_BYPASS | V_WND_SCALE(rscale) | V_MSS_IDX(mtu_idx) |
520237263Snp	    V_ULP_MODE(ulp_mode) | V_RCV_BUFSIZ(rx_credits);
521237263Snp
522237263Snp	if (so != NULL) {
523237263Snp		struct inpcb *inp = sotoinpcb(so);
524237263Snp		struct tcpcb *tp = intotcpcb(inp);
525237263Snp		int keepalive = always_keepalive ||
526237263Snp		    so_options_get(so) & SO_KEEPALIVE;
527237263Snp
528237263Snp		opt0 |= V_NAGLE((tp->t_flags & TF_NODELAY) == 0);
529237263Snp		opt0 |= V_KEEP_ALIVE(keepalive != 0);
530237263Snp	}
531237263Snp
532237263Snp	if (e != NULL)
533237263Snp		opt0 |= V_L2T_IDX(e->idx);
534237263Snp
535237263Snp	if (pi != NULL) {
536237263Snp		opt0 |= V_SMAC_SEL(VIID_SMACIDX(pi->viid));
537237263Snp		opt0 |= V_TX_CHAN(pi->tx_chan);
538237263Snp	}
539237263Snp
540237263Snp	return htobe64(opt0);
541237263Snp}
542237263Snp
543252495Snpuint64_t
544252814Snpselect_ntuple(struct port_info *pi, struct l2t_entry *e)
545237263Snp{
546252814Snp	struct adapter *sc = pi->adapter;
547252814Snp	struct tp_params *tp = &sc->params.tp;
548237263Snp	uint16_t viid = pi->viid;
549252814Snp	uint64_t ntuple = 0;
550237263Snp
551252814Snp	/*
552252814Snp	 * Initialize each of the fields which we care about which are present
553252814Snp	 * in the Compressed Filter Tuple.
554252814Snp	 */
555252814Snp	if (tp->vlan_shift >= 0 && e->vlan != CPL_L2T_VLAN_NONE)
556252814Snp		ntuple |= (uint64_t)(F_FT_VLAN_VLD | e->vlan) << tp->vlan_shift;
557237263Snp
558252814Snp	if (tp->port_shift >= 0)
559252814Snp		ntuple |= (uint64_t)e->lport << tp->port_shift;
560252814Snp
561252814Snp	if (tp->protocol_shift >= 0)
562252814Snp		ntuple |= (uint64_t)IPPROTO_TCP << tp->protocol_shift;
563252814Snp
564252814Snp	if (tp->vnic_shift >= 0) {
565252814Snp		uint32_t vf = G_FW_VIID_VIN(viid);
566252814Snp		uint32_t pf = G_FW_VIID_PFN(viid);
567252814Snp		uint32_t vld = G_FW_VIID_VIVLD(viid);
568252814Snp
569252814Snp		ntuple |= (uint64_t)(V_FT_VNID_ID_VF(vf) | V_FT_VNID_ID_PF(pf) |
570252814Snp		    V_FT_VNID_ID_VLD(vld)) << tp->vnic_shift;
571252814Snp	}
572252814Snp
573252814Snp	if (is_t4(sc))
574252814Snp		return (htobe32((uint32_t)ntuple));
575252495Snp	else
576252495Snp		return (htobe64(V_FILTER_TUPLE(ntuple)));
577237263Snp}
578237263Snp
579247434Snpvoid
580247434Snpset_tcpddp_ulp_mode(struct toepcb *toep)
581247434Snp{
582247434Snp
583247434Snp	toep->ulp_mode = ULP_MODE_TCPDDP;
584247434Snp	toep->ddp_flags = DDP_OK;
585247434Snp	toep->ddp_score = DDP_LOW_SCORE;
586247434Snp}
587247434Snp
588247434Snpint
589247434Snpnegative_advice(int status)
590247434Snp{
591247434Snp
592247434Snp	return (status == CPL_ERR_RTX_NEG_ADVICE ||
593247434Snp	    status == CPL_ERR_PERSIST_NEG_ADVICE ||
594247434Snp	    status == CPL_ERR_KEEPALV_NEG_ADVICE);
595247434Snp}
596247434Snp
597237263Snpstatic int
598237263Snpalloc_tid_tabs(struct tid_info *t)
599237263Snp{
600237263Snp	size_t size;
601237263Snp	unsigned int i;
602237263Snp
603237263Snp	size = t->ntids * sizeof(*t->tid_tab) +
604237263Snp	    t->natids * sizeof(*t->atid_tab) +
605237263Snp	    t->nstids * sizeof(*t->stid_tab);
606237263Snp
607237263Snp	t->tid_tab = malloc(size, M_CXGBE, M_ZERO | M_NOWAIT);
608237263Snp	if (t->tid_tab == NULL)
609237263Snp		return (ENOMEM);
610237263Snp
611237263Snp	mtx_init(&t->atid_lock, "atid lock", NULL, MTX_DEF);
612237263Snp	t->atid_tab = (union aopen_entry *)&t->tid_tab[t->ntids];
613237263Snp	t->afree = t->atid_tab;
614237263Snp	t->atids_in_use = 0;
615237263Snp	for (i = 1; i < t->natids; i++)
616237263Snp		t->atid_tab[i - 1].next = &t->atid_tab[i];
617237263Snp	t->atid_tab[t->natids - 1].next = NULL;
618237263Snp
619237263Snp	mtx_init(&t->stid_lock, "stid lock", NULL, MTX_DEF);
620247434Snp	t->stid_tab = (struct listen_ctx **)&t->atid_tab[t->natids];
621237263Snp	t->stids_in_use = 0;
622247434Snp	TAILQ_INIT(&t->stids);
623247434Snp	t->nstids_free_head = t->nstids;
624237263Snp
625237263Snp	atomic_store_rel_int(&t->tids_in_use, 0);
626237263Snp
627237263Snp	return (0);
628237263Snp}
629237263Snp
630237263Snpstatic void
631237263Snpfree_tid_tabs(struct tid_info *t)
632237263Snp{
633237263Snp	KASSERT(t->tids_in_use == 0,
634237263Snp	    ("%s: %d tids still in use.", __func__, t->tids_in_use));
635237263Snp	KASSERT(t->atids_in_use == 0,
636237263Snp	    ("%s: %d atids still in use.", __func__, t->atids_in_use));
637237263Snp	KASSERT(t->stids_in_use == 0,
638237263Snp	    ("%s: %d tids still in use.", __func__, t->stids_in_use));
639237263Snp
640237263Snp	free(t->tid_tab, M_CXGBE);
641237263Snp	t->tid_tab = NULL;
642237263Snp
643237263Snp	if (mtx_initialized(&t->atid_lock))
644237263Snp		mtx_destroy(&t->atid_lock);
645237263Snp	if (mtx_initialized(&t->stid_lock))
646237263Snp		mtx_destroy(&t->stid_lock);
647237263Snp}
648237263Snp
649247434Snpstatic int
650247434Snpadd_lip(struct adapter *sc, struct in6_addr *lip)
651247434Snp{
652247434Snp        struct fw_clip_cmd c;
653247434Snp
654247434Snp	ASSERT_SYNCHRONIZED_OP(sc);
655247434Snp	/* mtx_assert(&td->clip_table_lock, MA_OWNED); */
656247434Snp
657247434Snp        memset(&c, 0, sizeof(c));
658247434Snp	c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
659247434Snp	    F_FW_CMD_WRITE);
660247434Snp        c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
661247434Snp        c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
662247434Snp        c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
663247434Snp
664252495Snp	return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
665247434Snp}
666247434Snp
667247434Snpstatic int
668247434Snpdelete_lip(struct adapter *sc, struct in6_addr *lip)
669247434Snp{
670247434Snp	struct fw_clip_cmd c;
671247434Snp
672247434Snp	ASSERT_SYNCHRONIZED_OP(sc);
673247434Snp	/* mtx_assert(&td->clip_table_lock, MA_OWNED); */
674247434Snp
675247434Snp	memset(&c, 0, sizeof(c));
676247434Snp	c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
677247434Snp	    F_FW_CMD_READ);
678247434Snp        c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
679247434Snp        c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
680247434Snp        c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
681247434Snp
682252495Snp	return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
683247434Snp}
684247434Snp
685247434Snpstatic struct clip_entry *
686247434Snpsearch_lip(struct tom_data *td, struct in6_addr *lip)
687247434Snp{
688247434Snp	struct clip_entry *ce;
689247434Snp
690247434Snp	mtx_assert(&td->clip_table_lock, MA_OWNED);
691247434Snp
692247434Snp	TAILQ_FOREACH(ce, &td->clip_table, link) {
693247434Snp		if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
694247434Snp			return (ce);
695247434Snp	}
696247434Snp
697247434Snp	return (NULL);
698247434Snp}
699247434Snp
700247434Snpstruct clip_entry *
701247434Snphold_lip(struct tom_data *td, struct in6_addr *lip)
702247434Snp{
703247434Snp	struct clip_entry *ce;
704247434Snp
705247434Snp	mtx_lock(&td->clip_table_lock);
706247434Snp	ce = search_lip(td, lip);
707247434Snp	if (ce != NULL)
708247434Snp		ce->refcount++;
709247434Snp	mtx_unlock(&td->clip_table_lock);
710247434Snp
711247434Snp	return (ce);
712247434Snp}
713247434Snp
714247434Snpvoid
715247434Snprelease_lip(struct tom_data *td, struct clip_entry *ce)
716247434Snp{
717247434Snp
718247434Snp	mtx_lock(&td->clip_table_lock);
719247434Snp	KASSERT(search_lip(td, &ce->lip) == ce,
720247434Snp	    ("%s: CLIP entry %p p not in CLIP table.", __func__, ce));
721247434Snp	KASSERT(ce->refcount > 0,
722247434Snp	    ("%s: CLIP entry %p has refcount 0", __func__, ce));
723247434Snp	--ce->refcount;
724247434Snp	mtx_unlock(&td->clip_table_lock);
725247434Snp}
726247434Snp
727237263Snpstatic void
728247434Snpinit_clip_table(struct adapter *sc, struct tom_data *td)
729247434Snp{
730247434Snp
731247434Snp	ASSERT_SYNCHRONIZED_OP(sc);
732247434Snp
733247434Snp	mtx_init(&td->clip_table_lock, "CLIP table lock", NULL, MTX_DEF);
734247434Snp	TAILQ_INIT(&td->clip_table);
735252495Snp	td->clip_gen = -1;
736247434Snp
737252495Snp	update_clip_table(sc, td);
738252495Snp}
739252495Snp
740252495Snpstatic void
741252495Snpupdate_clip(struct adapter *sc, void *arg __unused)
742252495Snp{
743252495Snp
744252495Snp	if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4tomuc"))
745252495Snp		return;
746252495Snp
747252495Snp	if (sc->flags & TOM_INIT_DONE)
748252495Snp		update_clip_table(sc, sc->tom_softc);
749252495Snp
750252495Snp	end_synchronized_op(sc, LOCK_HELD);
751252495Snp}
752252495Snp
753252495Snpstatic void
754252495Snpt4_clip_task(void *arg, int count)
755252495Snp{
756252495Snp
757252495Snp	t4_iterate(update_clip, NULL);
758252495Snp}
759252495Snp
760252495Snpstatic void
761252495Snpupdate_clip_table(struct adapter *sc, struct tom_data *td)
762252495Snp{
763252495Snp	struct in6_ifaddr *ia;
764252495Snp	struct in6_addr *lip, tlip;
765252495Snp	struct clip_head stale;
766252495Snp	struct clip_entry *ce, *ce_temp;
767252495Snp	int rc, gen = atomic_load_acq_int(&in6_ifaddr_gen);
768252495Snp
769252495Snp	ASSERT_SYNCHRONIZED_OP(sc);
770252495Snp
771247434Snp	IN6_IFADDR_RLOCK();
772252495Snp	mtx_lock(&td->clip_table_lock);
773252495Snp
774252495Snp	if (gen == td->clip_gen)
775252495Snp		goto done;
776252495Snp
777252495Snp	TAILQ_INIT(&stale);
778252495Snp	TAILQ_CONCAT(&stale, &td->clip_table, link);
779252495Snp
780247434Snp	TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
781247434Snp		lip = &ia->ia_addr.sin6_addr;
782247434Snp
783247434Snp		KASSERT(!IN6_IS_ADDR_MULTICAST(lip),
784247434Snp		    ("%s: mcast address in in6_ifaddr list", __func__));
785247434Snp
786247434Snp		if (IN6_IS_ADDR_LOOPBACK(lip))
787247434Snp			continue;
788247434Snp		if (IN6_IS_SCOPE_EMBED(lip)) {
789247434Snp			/* Remove the embedded scope */
790247434Snp			tlip = *lip;
791247434Snp			lip = &tlip;
792247434Snp			in6_clearscope(lip);
793247434Snp		}
794247434Snp		/*
795247434Snp		 * XXX: how to weed out the link local address for the loopback
796247434Snp		 * interface?  It's fe80::1 usually (always?).
797247434Snp		 */
798247434Snp
799252495Snp		/*
800252495Snp		 * If it's in the main list then we already know it's not stale.
801252495Snp		 */
802252495Snp		TAILQ_FOREACH(ce, &td->clip_table, link) {
803252495Snp			if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
804252495Snp				goto next;
805252495Snp		}
806252495Snp
807252495Snp		/*
808252495Snp		 * If it's in the stale list we should move it to the main list.
809252495Snp		 */
810252495Snp		TAILQ_FOREACH(ce, &stale, link) {
811252495Snp			if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) {
812252495Snp				TAILQ_REMOVE(&stale, ce, link);
813247434Snp				TAILQ_INSERT_TAIL(&td->clip_table, ce, link);
814252495Snp				goto next;
815252495Snp			}
816252495Snp		}
817252495Snp
818252495Snp		/* A new IP6 address; add it to the CLIP table */
819252495Snp		ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT);
820252495Snp		memcpy(&ce->lip, lip, sizeof(ce->lip));
821252495Snp		ce->refcount = 0;
822252495Snp		rc = add_lip(sc, lip);
823252495Snp		if (rc == 0)
824252495Snp			TAILQ_INSERT_TAIL(&td->clip_table, ce, link);
825252495Snp		else {
826252495Snp			char ip[INET6_ADDRSTRLEN];
827252495Snp
828252495Snp			inet_ntop(AF_INET6, &ce->lip, &ip[0], sizeof(ip));
829252495Snp			log(LOG_ERR, "%s: could not add %s (%d)\n",
830252495Snp			    __func__, ip, rc);
831252495Snp			free(ce, M_CXGBE);
832252495Snp		}
833252495Snpnext:
834252495Snp		continue;
835252495Snp	}
836252495Snp
837252495Snp	/*
838252495Snp	 * Remove stale addresses (those no longer in V_in6_ifaddrhead) that are
839252495Snp	 * no longer referenced by the driver.
840252495Snp	 */
841252495Snp	TAILQ_FOREACH_SAFE(ce, &stale, link, ce_temp) {
842252495Snp		if (ce->refcount == 0) {
843252495Snp			rc = delete_lip(sc, &ce->lip);
844252495Snp			if (rc == 0) {
845252495Snp				TAILQ_REMOVE(&stale, ce, link);
846247434Snp				free(ce, M_CXGBE);
847252495Snp			} else {
848252495Snp				char ip[INET6_ADDRSTRLEN];
849252495Snp
850252495Snp				inet_ntop(AF_INET6, &ce->lip, &ip[0],
851252495Snp				    sizeof(ip));
852252495Snp				log(LOG_ERR, "%s: could not delete %s (%d)\n",
853252495Snp				    __func__, ip, rc);
854252495Snp			}
855247434Snp		}
856247434Snp	}
857252495Snp	/* The ones that are still referenced need to stay in the CLIP table */
858252495Snp	TAILQ_CONCAT(&td->clip_table, &stale, link);
859252495Snp
860252495Snp	td->clip_gen = gen;
861252495Snpdone:
862252495Snp	mtx_unlock(&td->clip_table_lock);
863247434Snp	IN6_IFADDR_RUNLOCK();
864247434Snp}
865247434Snp
866247434Snpstatic void
867247434Snpdestroy_clip_table(struct adapter *sc, struct tom_data *td)
868247434Snp{
869247434Snp	struct clip_entry *ce, *ce_temp;
870247434Snp
871247434Snp	if (mtx_initialized(&td->clip_table_lock)) {
872247434Snp		mtx_lock(&td->clip_table_lock);
873247434Snp		TAILQ_FOREACH_SAFE(ce, &td->clip_table, link, ce_temp) {
874247434Snp			KASSERT(ce->refcount == 0,
875247434Snp			    ("%s: CLIP entry %p still in use (%d)", __func__,
876247434Snp			    ce, ce->refcount));
877247434Snp			TAILQ_REMOVE(&td->clip_table, ce, link);
878247434Snp			delete_lip(sc, &ce->lip);
879247434Snp			free(ce, M_CXGBE);
880247434Snp		}
881247434Snp		mtx_unlock(&td->clip_table_lock);
882247434Snp		mtx_destroy(&td->clip_table_lock);
883247434Snp	}
884247434Snp}
885247434Snp
886247434Snpstatic void
887237263Snpfree_tom_data(struct adapter *sc, struct tom_data *td)
888237263Snp{
889247434Snp
890247434Snp	ASSERT_SYNCHRONIZED_OP(sc);
891247434Snp
892237263Snp	KASSERT(TAILQ_EMPTY(&td->toep_list),
893237263Snp	    ("%s: TOE PCB list is not empty.", __func__));
894237263Snp	KASSERT(td->lctx_count == 0,
895237263Snp	    ("%s: lctx hash table is not empty.", __func__));
896237263Snp
897237263Snp	t4_uninit_l2t_cpl_handlers(sc);
898240169Snp	t4_uninit_cpl_io_handlers(sc);
899240169Snp	t4_uninit_ddp(sc, td);
900247434Snp	destroy_clip_table(sc, td);
901237263Snp
902237263Snp	if (td->listen_mask != 0)
903237263Snp		hashdestroy(td->listen_hash, M_CXGBE, td->listen_mask);
904237263Snp
905237263Snp	if (mtx_initialized(&td->lctx_hash_lock))
906237263Snp		mtx_destroy(&td->lctx_hash_lock);
907237263Snp	if (mtx_initialized(&td->toep_list_lock))
908237263Snp		mtx_destroy(&td->toep_list_lock);
909237263Snp
910237263Snp	free_tid_tabs(&sc->tids);
911237263Snp	free(td, M_CXGBE);
912237263Snp}
913237263Snp
914237263Snp/*
915237263Snp * Ground control to Major TOM
916237263Snp * Commencing countdown, engines on
917237263Snp */
918237263Snpstatic int
919237263Snpt4_tom_activate(struct adapter *sc)
920237263Snp{
921237263Snp	struct tom_data *td;
922237263Snp	struct toedev *tod;
923237263Snp	int i, rc;
924237263Snp
925247434Snp	ASSERT_SYNCHRONIZED_OP(sc);
926237263Snp
927237263Snp	/* per-adapter softc for TOM */
928237263Snp	td = malloc(sizeof(*td), M_CXGBE, M_ZERO | M_NOWAIT);
929237263Snp	if (td == NULL)
930237263Snp		return (ENOMEM);
931237263Snp
932237263Snp	/* List of TOE PCBs and associated lock */
933237263Snp	mtx_init(&td->toep_list_lock, "PCB list lock", NULL, MTX_DEF);
934237263Snp	TAILQ_INIT(&td->toep_list);
935237263Snp
936237263Snp	/* Listen context */
937237263Snp	mtx_init(&td->lctx_hash_lock, "lctx hash lock", NULL, MTX_DEF);
938237263Snp	td->listen_hash = hashinit_flags(LISTEN_HASH_SIZE, M_CXGBE,
939237263Snp	    &td->listen_mask, HASH_NOWAIT);
940237263Snp
941237263Snp	/* TID tables */
942237263Snp	rc = alloc_tid_tabs(&sc->tids);
943237263Snp	if (rc != 0)
944237263Snp		goto done;
945237263Snp
946247434Snp	/* DDP page pods and CPL handlers */
947240169Snp	t4_init_ddp(sc, td);
948240169Snp
949247434Snp	/* CLIP table for IPv6 offload */
950247434Snp	init_clip_table(sc, td);
951247434Snp
952237263Snp	/* CPL handlers */
953237263Snp	t4_init_connect_cpl_handlers(sc);
954237263Snp	t4_init_l2t_cpl_handlers(sc);
955237263Snp	t4_init_listen_cpl_handlers(sc);
956237263Snp	t4_init_cpl_io_handlers(sc);
957237263Snp
958237263Snp	/* toedev ops */
959237263Snp	tod = &td->tod;
960237263Snp	init_toedev(tod);
961237263Snp	tod->tod_softc = sc;
962237263Snp	tod->tod_connect = t4_connect;
963237263Snp	tod->tod_listen_start = t4_listen_start;
964237263Snp	tod->tod_listen_stop = t4_listen_stop;
965237263Snp	tod->tod_rcvd = t4_rcvd;
966237263Snp	tod->tod_output = t4_tod_output;
967237263Snp	tod->tod_send_rst = t4_send_rst;
968237263Snp	tod->tod_send_fin = t4_send_fin;
969237263Snp	tod->tod_pcb_detach = t4_pcb_detach;
970237263Snp	tod->tod_l2_update = t4_l2_update;
971237263Snp	tod->tod_syncache_added = t4_syncache_added;
972237263Snp	tod->tod_syncache_removed = t4_syncache_removed;
973237263Snp	tod->tod_syncache_respond = t4_syncache_respond;
974237263Snp	tod->tod_offload_socket = t4_offload_socket;
975252814Snp	tod->tod_ctloutput = t4_ctloutput;
976237263Snp
977237263Snp	for_each_port(sc, i)
978237263Snp		TOEDEV(sc->port[i]->ifp) = &td->tod;
979237263Snp
980237263Snp	sc->tom_softc = td;
981237263Snp	sc->flags |= TOM_INIT_DONE;
982237263Snp	register_toedev(sc->tom_softc);
983237263Snp
984237263Snpdone:
985237263Snp	if (rc != 0)
986237263Snp		free_tom_data(sc, td);
987237263Snp	return (rc);
988237263Snp}
989237263Snp
990237263Snpstatic int
991237263Snpt4_tom_deactivate(struct adapter *sc)
992237263Snp{
993237263Snp	int rc = 0;
994237263Snp	struct tom_data *td = sc->tom_softc;
995237263Snp
996247434Snp	ASSERT_SYNCHRONIZED_OP(sc);
997237263Snp
998237263Snp	if (td == NULL)
999237263Snp		return (0);	/* XXX. KASSERT? */
1000237263Snp
1001237263Snp	if (sc->offload_map != 0)
1002237263Snp		return (EBUSY);	/* at least one port has IFCAP_TOE enabled */
1003237263Snp
1004237263Snp	mtx_lock(&td->toep_list_lock);
1005237263Snp	if (!TAILQ_EMPTY(&td->toep_list))
1006237263Snp		rc = EBUSY;
1007237263Snp	mtx_unlock(&td->toep_list_lock);
1008237263Snp
1009237263Snp	mtx_lock(&td->lctx_hash_lock);
1010237263Snp	if (td->lctx_count > 0)
1011237263Snp		rc = EBUSY;
1012237263Snp	mtx_unlock(&td->lctx_hash_lock);
1013237263Snp
1014237263Snp	if (rc == 0) {
1015237263Snp		unregister_toedev(sc->tom_softc);
1016237263Snp		free_tom_data(sc, td);
1017237263Snp		sc->tom_softc = NULL;
1018237263Snp		sc->flags &= ~TOM_INIT_DONE;
1019237263Snp	}
1020237263Snp
1021237263Snp	return (rc);
1022237263Snp}
1023237263Snp
1024252495Snpstatic void
1025252495Snpt4_tom_ifaddr_event(void *arg __unused, struct ifnet *ifp)
1026252495Snp{
1027252495Snp
1028252495Snp	atomic_add_rel_int(&in6_ifaddr_gen, 1);
1029252495Snp	taskqueue_enqueue_timeout(taskqueue_thread, &clip_task, -hz / 4);
1030252495Snp}
1031252495Snp
1032237263Snpstatic int
1033237263Snpt4_tom_mod_load(void)
1034237263Snp{
1035237263Snp	int rc;
1036247434Snp	struct protosw *tcp_protosw, *tcp6_protosw;
1037237263Snp
1038240169Snp	tcp_protosw = pffindproto(PF_INET, IPPROTO_TCP, SOCK_STREAM);
1039240169Snp	if (tcp_protosw == NULL)
1040240169Snp		return (ENOPROTOOPT);
1041240169Snp	bcopy(tcp_protosw, &ddp_protosw, sizeof(ddp_protosw));
1042240169Snp	bcopy(tcp_protosw->pr_usrreqs, &ddp_usrreqs, sizeof(ddp_usrreqs));
1043240169Snp	ddp_usrreqs.pru_soreceive = t4_soreceive_ddp;
1044240169Snp	ddp_protosw.pr_usrreqs = &ddp_usrreqs;
1045240169Snp
1046247434Snp	tcp6_protosw = pffindproto(PF_INET6, IPPROTO_TCP, SOCK_STREAM);
1047247434Snp	if (tcp6_protosw == NULL)
1048247434Snp		return (ENOPROTOOPT);
1049247434Snp	bcopy(tcp6_protosw, &ddp6_protosw, sizeof(ddp6_protosw));
1050247434Snp	bcopy(tcp6_protosw->pr_usrreqs, &ddp6_usrreqs, sizeof(ddp6_usrreqs));
1051247434Snp	ddp6_usrreqs.pru_soreceive = t4_soreceive_ddp;
1052247434Snp	ddp6_protosw.pr_usrreqs = &ddp6_usrreqs;
1053247434Snp
1054252495Snp	TIMEOUT_TASK_INIT(taskqueue_thread, &clip_task, 0, t4_clip_task, NULL);
1055252495Snp	ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event,
1056252495Snp	    t4_tom_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
1057252495Snp
1058237263Snp	rc = t4_register_uld(&tom_uld_info);
1059237263Snp	if (rc != 0)
1060237263Snp		t4_tom_mod_unload();
1061237263Snp
1062237263Snp	return (rc);
1063237263Snp}
1064237263Snp
1065237263Snpstatic void
1066237263Snptom_uninit(struct adapter *sc, void *arg __unused)
1067237263Snp{
1068247434Snp	if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4tomun"))
1069247434Snp		return;
1070247434Snp
1071237263Snp	/* Try to free resources (works only if no port has IFCAP_TOE) */
1072237263Snp	if (sc->flags & TOM_INIT_DONE)
1073237263Snp		t4_deactivate_uld(sc, ULD_TOM);
1074247434Snp
1075247434Snp	end_synchronized_op(sc, LOCK_HELD);
1076237263Snp}
1077237263Snp
1078237263Snpstatic int
1079237263Snpt4_tom_mod_unload(void)
1080237263Snp{
1081237263Snp	t4_iterate(tom_uninit, NULL);
1082237263Snp
1083237263Snp	if (t4_unregister_uld(&tom_uld_info) == EBUSY)
1084237263Snp		return (EBUSY);
1085237263Snp
1086252495Snp	if (ifaddr_evhandler) {
1087252495Snp		EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_evhandler);
1088252495Snp		taskqueue_cancel_timeout(taskqueue_thread, &clip_task, NULL);
1089252495Snp	}
1090252495Snp
1091237263Snp	return (0);
1092237263Snp}
1093237263Snp#endif	/* TCP_OFFLOAD */
1094237263Snp
1095237263Snpstatic int
1096237263Snpt4_tom_modevent(module_t mod, int cmd, void *arg)
1097237263Snp{
1098237263Snp	int rc = 0;
1099237263Snp
1100237263Snp#ifdef TCP_OFFLOAD
1101237263Snp	switch (cmd) {
1102237263Snp	case MOD_LOAD:
1103237263Snp		rc = t4_tom_mod_load();
1104237263Snp		break;
1105237263Snp
1106237263Snp	case MOD_UNLOAD:
1107237263Snp		rc = t4_tom_mod_unload();
1108237263Snp		break;
1109237263Snp
1110237263Snp	default:
1111237263Snp		rc = EINVAL;
1112237263Snp	}
1113237263Snp#else
1114237263Snp	printf("t4_tom: compiled without TCP_OFFLOAD support.\n");
1115237263Snp	rc = EOPNOTSUPP;
1116237263Snp#endif
1117237263Snp	return (rc);
1118237263Snp}
1119237263Snp
1120237263Snpstatic moduledata_t t4_tom_moddata= {
1121237263Snp	"t4_tom",
1122237263Snp	t4_tom_modevent,
1123237263Snp	0
1124237263Snp};
1125237263Snp
1126237263SnpMODULE_VERSION(t4_tom, 1);
1127237263SnpMODULE_DEPEND(t4_tom, toecore, 1, 1, 1);
1128237263SnpMODULE_DEPEND(t4_tom, t4nex, 1, 1, 1);
1129237263SnpDECLARE_MODULE(t4_tom, t4_tom_moddata, SI_SUB_EXEC, SI_ORDER_ANY);
1130