t4_tom.c revision 255006
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: head/sys/dev/cxgbe/tom/t4_tom.c 255006 2013-08-28 20:59:22Z np $");
30237263Snp
31237263Snp#include "opt_inet.h"
32245441Snp#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>
44249627Snp#include <sys/taskqueue.h>
45245448Snp#include <net/if.h>
46237263Snp#include <netinet/in.h>
47237263Snp#include <netinet/in_pcb.h>
48245448Snp#include <netinet/in_var.h>
49237263Snp#include <netinet/ip.h>
50245441Snp#include <netinet/ip6.h>
51237263Snp#include <netinet/tcp_var.h>
52245448Snp#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"
61252705Snp#include "common/t4_regs_values.h"
62252716Snp#include "common/t4_tcb.h"
63237263Snp#include "tom/t4_tom_l2t.h"
64237263Snp#include "tom/t4_tom.h"
65237263Snp
66239344Snpstatic struct protosw ddp_protosw;
67239344Snpstatic struct pr_usrreqs ddp_usrreqs;
68239344Snp
69245441Snpstatic struct protosw ddp6_protosw;
70245441Snpstatic struct pr_usrreqs ddp6_usrreqs;
71245441Snp
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 *);
91245448Snpstatic int add_lip(struct adapter *, struct in6_addr *);
92245448Snpstatic int delete_lip(struct adapter *, struct in6_addr *);
93245448Snpstatic struct clip_entry *search_lip(struct tom_data *, struct in6_addr *);
94245448Snpstatic void init_clip_table(struct adapter *, struct tom_data *);
95249627Snpstatic void update_clip(struct adapter *, void *);
96249627Snpstatic void t4_clip_task(void *, int);
97249627Snpstatic void update_clip_table(struct adapter *, struct tom_data *);
98245448Snpstatic void destroy_clip_table(struct adapter *, struct tom_data *);
99237263Snpstatic void free_tom_data(struct adapter *, struct tom_data *);
100237263Snp
101249627Snpstatic int in6_ifaddr_gen;
102249627Snpstatic eventhandler_tag ifaddr_evhandler;
103249627Snpstatic struct timeout_task clip_task;
104249627Snp
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;
151237263Snp	toep->tx_credits = tx_credits;
152237263Snp	toep->ofld_txq = &sc->sge.ofld_txq[txqid];
153237263Snp	toep->ofld_rxq = &sc->sge.ofld_rxq[rxqid];
154237263Snp	toep->ctrlq = &sc->sge.ctrlq[pi->port_id];
155237263Snp	toep->txsd_total = txsd_total;
156237263Snp	toep->txsd_avail = txsd_total;
157237263Snp	toep->txsd_pidx = 0;
158237263Snp	toep->txsd_cidx = 0;
159237263Snp
160237263Snp	return (toep);
161237263Snp}
162237263Snp
163237263Snpvoid
164237263Snpfree_toepcb(struct toepcb *toep)
165237263Snp{
166237263Snp
167239514Snp	KASSERT(!(toep->flags & TPF_ATTACHED),
168237263Snp	    ("%s: attached to an inpcb", __func__));
169239514Snp	KASSERT(!(toep->flags & TPF_CPL_PENDING),
170237263Snp	    ("%s: CPL pending", __func__));
171237263Snp
172237263Snp	free(toep, M_CXGBE);
173237263Snp}
174237263Snp
175237263Snp/*
176237263Snp * Set up the socket for TCP offload.
177237263Snp */
178237263Snpvoid
179237263Snpoffload_socket(struct socket *so, struct toepcb *toep)
180237263Snp{
181237263Snp	struct tom_data *td = toep->td;
182237263Snp	struct inpcb *inp = sotoinpcb(so);
183237263Snp	struct tcpcb *tp = intotcpcb(inp);
184237263Snp	struct sockbuf *sb;
185237263Snp
186237263Snp	INP_WLOCK_ASSERT(inp);
187237263Snp
188237263Snp	/* Update socket */
189237263Snp	sb = &so->so_snd;
190237263Snp	SOCKBUF_LOCK(sb);
191237263Snp	sb->sb_flags |= SB_NOCOALESCE;
192237263Snp	SOCKBUF_UNLOCK(sb);
193237263Snp	sb = &so->so_rcv;
194237263Snp	SOCKBUF_LOCK(sb);
195237263Snp	sb->sb_flags |= SB_NOCOALESCE;
196245441Snp	if (toep->ulp_mode == ULP_MODE_TCPDDP) {
197245441Snp		if (inp->inp_vflag & INP_IPV6)
198245441Snp			so->so_proto = &ddp6_protosw;
199245441Snp		else
200245441Snp			so->so_proto = &ddp_protosw;
201245441Snp	}
202237263Snp	SOCKBUF_UNLOCK(sb);
203237263Snp
204237263Snp	/* Update TCP PCB */
205237263Snp	tp->tod = &td->tod;
206237263Snp	tp->t_toe = toep;
207237263Snp	tp->t_flags |= TF_TOE;
208237263Snp
209237263Snp	/* Install an extra hold on inp */
210237263Snp	toep->inp = inp;
211239514Snp	toep->flags |= TPF_ATTACHED;
212237263Snp	in_pcbref(inp);
213237263Snp
214237263Snp	/* Add the TOE PCB to the active list */
215237263Snp	mtx_lock(&td->toep_list_lock);
216237263Snp	TAILQ_INSERT_HEAD(&td->toep_list, toep, link);
217237263Snp	mtx_unlock(&td->toep_list_lock);
218237263Snp}
219237263Snp
220237263Snp/* This is _not_ the normal way to "unoffload" a socket. */
221237263Snpvoid
222237263Snpundo_offload_socket(struct socket *so)
223237263Snp{
224237263Snp	struct inpcb *inp = sotoinpcb(so);
225237263Snp	struct tcpcb *tp = intotcpcb(inp);
226237263Snp	struct toepcb *toep = tp->t_toe;
227237263Snp	struct tom_data *td = toep->td;
228237263Snp	struct sockbuf *sb;
229237263Snp
230237263Snp	INP_WLOCK_ASSERT(inp);
231237263Snp
232237263Snp	sb = &so->so_snd;
233237263Snp	SOCKBUF_LOCK(sb);
234237263Snp	sb->sb_flags &= ~SB_NOCOALESCE;
235237263Snp	SOCKBUF_UNLOCK(sb);
236237263Snp	sb = &so->so_rcv;
237237263Snp	SOCKBUF_LOCK(sb);
238237263Snp	sb->sb_flags &= ~SB_NOCOALESCE;
239237263Snp	SOCKBUF_UNLOCK(sb);
240237263Snp
241237263Snp	tp->tod = NULL;
242237263Snp	tp->t_toe = NULL;
243237263Snp	tp->t_flags &= ~TF_TOE;
244237263Snp
245237263Snp	toep->inp = NULL;
246239514Snp	toep->flags &= ~TPF_ATTACHED;
247237263Snp	if (in_pcbrele_wlocked(inp))
248237263Snp		panic("%s: inp freed.", __func__);
249237263Snp
250237263Snp	mtx_lock(&td->toep_list_lock);
251237263Snp	TAILQ_REMOVE(&td->toep_list, toep, link);
252237263Snp	mtx_unlock(&td->toep_list_lock);
253237263Snp}
254237263Snp
255237263Snpstatic void
256237263Snprelease_offload_resources(struct toepcb *toep)
257237263Snp{
258237263Snp	struct tom_data *td = toep->td;
259237263Snp	struct adapter *sc = td_adapter(td);
260237263Snp	int tid = toep->tid;
261237263Snp
262239514Snp	KASSERT(!(toep->flags & TPF_CPL_PENDING),
263237263Snp	    ("%s: %p has CPL pending.", __func__, toep));
264239514Snp	KASSERT(!(toep->flags & TPF_ATTACHED),
265237263Snp	    ("%s: %p is still attached.", __func__, toep));
266237263Snp
267245448Snp	CTR5(KTR_CXGBE, "%s: toep %p (tid %d, l2te %p, ce %p)",
268245448Snp	    __func__, toep, tid, toep->l2te, toep->ce);
269237263Snp
270239344Snp	if (toep->ulp_mode == ULP_MODE_TCPDDP)
271239344Snp		release_ddp_resources(toep);
272239344Snp
273237263Snp	if (toep->l2te)
274237263Snp		t4_l2t_release(toep->l2te);
275237263Snp
276237263Snp	if (tid >= 0) {
277237263Snp		remove_tid(sc, tid);
278237263Snp		release_tid(sc, tid, toep->ctrlq);
279237263Snp	}
280237263Snp
281245448Snp	if (toep->ce)
282245448Snp		release_lip(td, toep->ce);
283245448Snp
284237263Snp	mtx_lock(&td->toep_list_lock);
285237263Snp	TAILQ_REMOVE(&td->toep_list, toep, link);
286237263Snp	mtx_unlock(&td->toep_list_lock);
287237263Snp
288237263Snp	free_toepcb(toep);
289237263Snp}
290237263Snp
291237263Snp/*
292237263Snp * The kernel is done with the TCP PCB and this is our opportunity to unhook the
293237263Snp * toepcb hanging off of it.  If the TOE driver is also done with the toepcb (no
294237263Snp * pending CPL) then it is time to release all resources tied to the toepcb.
295237263Snp *
296237263Snp * Also gets called when an offloaded active open fails and the TOM wants the
297237263Snp * kernel to take the TCP PCB back.
298237263Snp */
299237263Snpstatic void
300237263Snpt4_pcb_detach(struct toedev *tod __unused, struct tcpcb *tp)
301237263Snp{
302237263Snp#if defined(KTR) || defined(INVARIANTS)
303237263Snp	struct inpcb *inp = tp->t_inpcb;
304237263Snp#endif
305237263Snp	struct toepcb *toep = tp->t_toe;
306237263Snp
307237263Snp	INP_WLOCK_ASSERT(inp);
308237263Snp
309237263Snp	KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
310239514Snp	KASSERT(toep->flags & TPF_ATTACHED,
311237263Snp	    ("%s: not attached", __func__));
312237263Snp
313237263Snp#ifdef KTR
314237263Snp	if (tp->t_state == TCPS_SYN_SENT) {
315237263Snp		CTR6(KTR_CXGBE, "%s: atid %d, toep %p (0x%x), inp %p (0x%x)",
316237263Snp		    __func__, toep->tid, toep, toep->flags, inp,
317237263Snp		    inp->inp_flags);
318237263Snp	} else {
319237263Snp		CTR6(KTR_CXGBE,
320237263Snp		    "t4_pcb_detach: tid %d (%s), toep %p (0x%x), inp %p (0x%x)",
321237263Snp		    toep->tid, tcpstates[tp->t_state], toep, toep->flags, inp,
322237263Snp		    inp->inp_flags);
323237263Snp	}
324237263Snp#endif
325237263Snp
326237263Snp	tp->t_toe = NULL;
327237263Snp	tp->t_flags &= ~TF_TOE;
328239514Snp	toep->flags &= ~TPF_ATTACHED;
329237263Snp
330239514Snp	if (!(toep->flags & TPF_CPL_PENDING))
331237263Snp		release_offload_resources(toep);
332237263Snp}
333237263Snp
334237263Snp/*
335252716Snp * setsockopt handler.
336252716Snp */
337252716Snpstatic void
338252716Snpt4_ctloutput(struct toedev *tod, struct tcpcb *tp, int dir, int name)
339252716Snp{
340252716Snp	struct adapter *sc = tod->tod_softc;
341252716Snp	struct toepcb *toep = tp->t_toe;
342252716Snp
343252716Snp	if (dir == SOPT_GET)
344252716Snp		return;
345252716Snp
346252716Snp	CTR4(KTR_CXGBE, "%s: tp %p, dir %u, name %u", __func__, tp, dir, name);
347252716Snp
348252716Snp	switch (name) {
349252716Snp	case TCP_NODELAY:
350252716Snp		t4_set_tcb_field(sc, toep, 1, W_TCB_T_FLAGS, V_TF_NAGLE(1),
351252716Snp		    V_TF_NAGLE(tp->t_flags & TF_NODELAY ? 0 : 1));
352252716Snp		break;
353252716Snp	default:
354252716Snp		break;
355252716Snp	}
356252716Snp}
357252716Snp
358252716Snp/*
359237263Snp * The TOE driver will not receive any more CPLs for the tid associated with the
360237263Snp * toepcb; release the hold on the inpcb.
361237263Snp */
362237263Snpvoid
363237263Snpfinal_cpl_received(struct toepcb *toep)
364237263Snp{
365237263Snp	struct inpcb *inp = toep->inp;
366237263Snp
367237263Snp	KASSERT(inp != NULL, ("%s: inp is NULL", __func__));
368237263Snp	INP_WLOCK_ASSERT(inp);
369239514Snp	KASSERT(toep->flags & TPF_CPL_PENDING,
370237263Snp	    ("%s: CPL not pending already?", __func__));
371237263Snp
372237263Snp	CTR6(KTR_CXGBE, "%s: tid %d, toep %p (0x%x), inp %p (0x%x)",
373237263Snp	    __func__, toep->tid, toep, toep->flags, inp, inp->inp_flags);
374237263Snp
375237263Snp	toep->inp = NULL;
376239514Snp	toep->flags &= ~TPF_CPL_PENDING;
377237263Snp
378239514Snp	if (!(toep->flags & TPF_ATTACHED))
379237263Snp		release_offload_resources(toep);
380237263Snp
381237263Snp	if (!in_pcbrele_wlocked(inp))
382237263Snp		INP_WUNLOCK(inp);
383237263Snp}
384237263Snp
385237263Snpvoid
386237263Snpinsert_tid(struct adapter *sc, int tid, void *ctx)
387237263Snp{
388237263Snp	struct tid_info *t = &sc->tids;
389237263Snp
390237263Snp	t->tid_tab[tid] = ctx;
391237263Snp	atomic_add_int(&t->tids_in_use, 1);
392237263Snp}
393237263Snp
394237263Snpvoid *
395237263Snplookup_tid(struct adapter *sc, int tid)
396237263Snp{
397237263Snp	struct tid_info *t = &sc->tids;
398237263Snp
399237263Snp	return (t->tid_tab[tid]);
400237263Snp}
401237263Snp
402237263Snpvoid
403237263Snpupdate_tid(struct adapter *sc, int tid, void *ctx)
404237263Snp{
405237263Snp	struct tid_info *t = &sc->tids;
406237263Snp
407237263Snp	t->tid_tab[tid] = ctx;
408237263Snp}
409237263Snp
410237263Snpvoid
411237263Snpremove_tid(struct adapter *sc, int tid)
412237263Snp{
413237263Snp	struct tid_info *t = &sc->tids;
414237263Snp
415237263Snp	t->tid_tab[tid] = NULL;
416237263Snp	atomic_subtract_int(&t->tids_in_use, 1);
417237263Snp}
418237263Snp
419237263Snpvoid
420237263Snprelease_tid(struct adapter *sc, int tid, struct sge_wrq *ctrlq)
421237263Snp{
422237263Snp	struct wrqe *wr;
423237263Snp	struct cpl_tid_release *req;
424237263Snp
425237263Snp	wr = alloc_wrqe(sizeof(*req), ctrlq);
426237263Snp	if (wr == NULL) {
427237263Snp		queue_tid_release(sc, tid);	/* defer */
428237263Snp		return;
429237263Snp	}
430237263Snp	req = wrtod(wr);
431237263Snp
432237263Snp	INIT_TP_WR_MIT_CPL(req, CPL_TID_RELEASE, tid);
433237263Snp
434237263Snp	t4_wrq_tx(sc, wr);
435237263Snp}
436237263Snp
437237263Snpstatic void
438237263Snpqueue_tid_release(struct adapter *sc, int tid)
439237263Snp{
440237263Snp
441237263Snp	CXGBE_UNIMPLEMENTED("deferred tid release");
442237263Snp}
443237263Snp
444237263Snp/*
445237263Snp * What mtu_idx to use, given a 4-tuple and/or an MSS cap
446237263Snp */
447237263Snpint
448237263Snpfind_best_mtu_idx(struct adapter *sc, struct in_conninfo *inc, int pmss)
449237263Snp{
450237263Snp	unsigned short *mtus = &sc->params.mtus[0];
451245441Snp	int i, mss, n;
452237263Snp
453237263Snp	KASSERT(inc != NULL || pmss > 0,
454237263Snp	    ("%s: at least one of inc/pmss must be specified", __func__));
455237263Snp
456237263Snp	mss = inc ? tcp_mssopt(inc) : pmss;
457237263Snp	if (pmss > 0 && mss > pmss)
458237263Snp		mss = pmss;
459237263Snp
460245441Snp	if (inc->inc_flags & INC_ISIPV6)
461245441Snp		n = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
462245441Snp	else
463245441Snp		n = sizeof(struct ip) + sizeof(struct tcphdr);
464237263Snp
465245441Snp	for (i = 0; i < NMTUS - 1 && mtus[i + 1] <= mss + n; i++)
466245441Snp		continue;
467245441Snp
468237263Snp	return (i);
469237263Snp}
470237263Snp
471237263Snp/*
472237263Snp * Determine the receive window size for a socket.
473237263Snp */
474237263Snpu_long
475237263Snpselect_rcv_wnd(struct socket *so)
476237263Snp{
477237263Snp	unsigned long wnd;
478237263Snp
479237263Snp	SOCKBUF_LOCK_ASSERT(&so->so_rcv);
480237263Snp
481237263Snp	wnd = sbspace(&so->so_rcv);
482237263Snp	if (wnd < MIN_RCV_WND)
483237263Snp		wnd = MIN_RCV_WND;
484237263Snp
485237263Snp	return min(wnd, MAX_RCV_WND);
486237263Snp}
487237263Snp
488237263Snpint
489237263Snpselect_rcv_wscale(void)
490237263Snp{
491237263Snp	int wscale = 0;
492237263Snp	unsigned long space = sb_max;
493237263Snp
494237263Snp	if (space > MAX_RCV_WND)
495237263Snp		space = MAX_RCV_WND;
496237263Snp
497237263Snp	while (wscale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << wscale) < space)
498237263Snp		wscale++;
499237263Snp
500237263Snp	return (wscale);
501237263Snp}
502237263Snp
503237263Snpextern int always_keepalive;
504237263Snp#define VIID_SMACIDX(v)	(((unsigned int)(v) & 0x7f) << 1)
505237263Snp
506237263Snp/*
507237263Snp * socket so could be a listening socket too.
508237263Snp */
509237263Snpuint64_t
510237263Snpcalc_opt0(struct socket *so, struct port_info *pi, struct l2t_entry *e,
511237263Snp    int mtu_idx, int rscale, int rx_credits, int ulp_mode)
512237263Snp{
513237263Snp	uint64_t opt0;
514237263Snp
515237263Snp	KASSERT(rx_credits <= M_RCV_BUFSIZ,
516237263Snp	    ("%s: rcv_bufsiz too high", __func__));
517237263Snp
518237263Snp	opt0 = F_TCAM_BYPASS | V_WND_SCALE(rscale) | V_MSS_IDX(mtu_idx) |
519237263Snp	    V_ULP_MODE(ulp_mode) | V_RCV_BUFSIZ(rx_credits);
520237263Snp
521237263Snp	if (so != NULL) {
522237263Snp		struct inpcb *inp = sotoinpcb(so);
523237263Snp		struct tcpcb *tp = intotcpcb(inp);
524237263Snp		int keepalive = always_keepalive ||
525237263Snp		    so_options_get(so) & SO_KEEPALIVE;
526237263Snp
527237263Snp		opt0 |= V_NAGLE((tp->t_flags & TF_NODELAY) == 0);
528237263Snp		opt0 |= V_KEEP_ALIVE(keepalive != 0);
529237263Snp	}
530237263Snp
531237263Snp	if (e != NULL)
532237263Snp		opt0 |= V_L2T_IDX(e->idx);
533237263Snp
534237263Snp	if (pi != NULL) {
535237263Snp		opt0 |= V_SMAC_SEL(VIID_SMACIDX(pi->viid));
536237263Snp		opt0 |= V_TX_CHAN(pi->tx_chan);
537237263Snp	}
538237263Snp
539237263Snp	return htobe64(opt0);
540237263Snp}
541237263Snp
542248925Snpuint64_t
543252705Snpselect_ntuple(struct port_info *pi, struct l2t_entry *e)
544237263Snp{
545252705Snp	struct adapter *sc = pi->adapter;
546252705Snp	struct tp_params *tp = &sc->params.tp;
547237263Snp	uint16_t viid = pi->viid;
548252705Snp	uint64_t ntuple = 0;
549237263Snp
550252705Snp	/*
551252705Snp	 * Initialize each of the fields which we care about which are present
552252705Snp	 * in the Compressed Filter Tuple.
553252705Snp	 */
554252705Snp	if (tp->vlan_shift >= 0 && e->vlan != CPL_L2T_VLAN_NONE)
555252705Snp		ntuple |= (uint64_t)(F_FT_VLAN_VLD | e->vlan) << tp->vlan_shift;
556237263Snp
557252705Snp	if (tp->port_shift >= 0)
558252705Snp		ntuple |= (uint64_t)e->lport << tp->port_shift;
559252705Snp
560252705Snp	if (tp->protocol_shift >= 0)
561252705Snp		ntuple |= (uint64_t)IPPROTO_TCP << tp->protocol_shift;
562252705Snp
563252705Snp	if (tp->vnic_shift >= 0) {
564252705Snp		uint32_t vf = G_FW_VIID_VIN(viid);
565252705Snp		uint32_t pf = G_FW_VIID_PFN(viid);
566252705Snp		uint32_t vld = G_FW_VIID_VIVLD(viid);
567252705Snp
568252705Snp		ntuple |= (uint64_t)(V_FT_VNID_ID_VF(vf) | V_FT_VNID_ID_PF(pf) |
569252705Snp		    V_FT_VNID_ID_VLD(vld)) << tp->vnic_shift;
570252705Snp	}
571252705Snp
572252705Snp	if (is_t4(sc))
573252705Snp		return (htobe32((uint32_t)ntuple));
574248925Snp	else
575248925Snp		return (htobe64(V_FILTER_TUPLE(ntuple)));
576237263Snp}
577237263Snp
578245441Snpvoid
579245441Snpset_tcpddp_ulp_mode(struct toepcb *toep)
580245441Snp{
581245441Snp
582245441Snp	toep->ulp_mode = ULP_MODE_TCPDDP;
583245441Snp	toep->ddp_flags = DDP_OK;
584245441Snp	toep->ddp_score = DDP_LOW_SCORE;
585245441Snp}
586245441Snp
587245935Snpint
588245935Snpnegative_advice(int status)
589245935Snp{
590245935Snp
591245935Snp	return (status == CPL_ERR_RTX_NEG_ADVICE ||
592245935Snp	    status == CPL_ERR_PERSIST_NEG_ADVICE ||
593245935Snp	    status == CPL_ERR_KEEPALV_NEG_ADVICE);
594245935Snp}
595245935Snp
596237263Snpstatic int
597237263Snpalloc_tid_tabs(struct tid_info *t)
598237263Snp{
599237263Snp	size_t size;
600237263Snp	unsigned int i;
601237263Snp
602237263Snp	size = t->ntids * sizeof(*t->tid_tab) +
603237263Snp	    t->natids * sizeof(*t->atid_tab) +
604237263Snp	    t->nstids * sizeof(*t->stid_tab);
605237263Snp
606237263Snp	t->tid_tab = malloc(size, M_CXGBE, M_ZERO | M_NOWAIT);
607237263Snp	if (t->tid_tab == NULL)
608237263Snp		return (ENOMEM);
609237263Snp
610237263Snp	mtx_init(&t->atid_lock, "atid lock", NULL, MTX_DEF);
611237263Snp	t->atid_tab = (union aopen_entry *)&t->tid_tab[t->ntids];
612237263Snp	t->afree = t->atid_tab;
613237263Snp	t->atids_in_use = 0;
614237263Snp	for (i = 1; i < t->natids; i++)
615237263Snp		t->atid_tab[i - 1].next = &t->atid_tab[i];
616237263Snp	t->atid_tab[t->natids - 1].next = NULL;
617237263Snp
618237263Snp	mtx_init(&t->stid_lock, "stid lock", NULL, MTX_DEF);
619245276Snp	t->stid_tab = (struct listen_ctx **)&t->atid_tab[t->natids];
620237263Snp	t->stids_in_use = 0;
621245276Snp	TAILQ_INIT(&t->stids);
622245276Snp	t->nstids_free_head = t->nstids;
623237263Snp
624237263Snp	atomic_store_rel_int(&t->tids_in_use, 0);
625237263Snp
626237263Snp	return (0);
627237263Snp}
628237263Snp
629237263Snpstatic void
630237263Snpfree_tid_tabs(struct tid_info *t)
631237263Snp{
632237263Snp	KASSERT(t->tids_in_use == 0,
633237263Snp	    ("%s: %d tids still in use.", __func__, t->tids_in_use));
634237263Snp	KASSERT(t->atids_in_use == 0,
635237263Snp	    ("%s: %d atids still in use.", __func__, t->atids_in_use));
636237263Snp	KASSERT(t->stids_in_use == 0,
637237263Snp	    ("%s: %d tids still in use.", __func__, t->stids_in_use));
638237263Snp
639237263Snp	free(t->tid_tab, M_CXGBE);
640237263Snp	t->tid_tab = NULL;
641237263Snp
642237263Snp	if (mtx_initialized(&t->atid_lock))
643237263Snp		mtx_destroy(&t->atid_lock);
644237263Snp	if (mtx_initialized(&t->stid_lock))
645237263Snp		mtx_destroy(&t->stid_lock);
646237263Snp}
647237263Snp
648245448Snpstatic int
649245448Snpadd_lip(struct adapter *sc, struct in6_addr *lip)
650245448Snp{
651245448Snp        struct fw_clip_cmd c;
652245448Snp
653245448Snp	ASSERT_SYNCHRONIZED_OP(sc);
654245448Snp	/* mtx_assert(&td->clip_table_lock, MA_OWNED); */
655245448Snp
656245448Snp        memset(&c, 0, sizeof(c));
657245448Snp	c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
658245448Snp	    F_FW_CMD_WRITE);
659245448Snp        c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
660245448Snp        c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
661245448Snp        c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
662245448Snp
663249627Snp	return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
664245448Snp}
665245448Snp
666245448Snpstatic int
667245448Snpdelete_lip(struct adapter *sc, struct in6_addr *lip)
668245448Snp{
669245448Snp	struct fw_clip_cmd c;
670245448Snp
671245448Snp	ASSERT_SYNCHRONIZED_OP(sc);
672245448Snp	/* mtx_assert(&td->clip_table_lock, MA_OWNED); */
673245448Snp
674245448Snp	memset(&c, 0, sizeof(c));
675245448Snp	c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
676245448Snp	    F_FW_CMD_READ);
677245448Snp        c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
678245448Snp        c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
679245448Snp        c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
680245448Snp
681249627Snp	return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
682245448Snp}
683245448Snp
684245448Snpstatic struct clip_entry *
685245448Snpsearch_lip(struct tom_data *td, struct in6_addr *lip)
686245448Snp{
687245448Snp	struct clip_entry *ce;
688245448Snp
689245448Snp	mtx_assert(&td->clip_table_lock, MA_OWNED);
690245448Snp
691245448Snp	TAILQ_FOREACH(ce, &td->clip_table, link) {
692245448Snp		if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
693245448Snp			return (ce);
694245448Snp	}
695245448Snp
696245448Snp	return (NULL);
697245448Snp}
698245448Snp
699245448Snpstruct clip_entry *
700245448Snphold_lip(struct tom_data *td, struct in6_addr *lip)
701245448Snp{
702245448Snp	struct clip_entry *ce;
703245448Snp
704245448Snp	mtx_lock(&td->clip_table_lock);
705245448Snp	ce = search_lip(td, lip);
706245448Snp	if (ce != NULL)
707245448Snp		ce->refcount++;
708245448Snp	mtx_unlock(&td->clip_table_lock);
709245448Snp
710245448Snp	return (ce);
711245448Snp}
712245448Snp
713245448Snpvoid
714245448Snprelease_lip(struct tom_data *td, struct clip_entry *ce)
715245448Snp{
716245448Snp
717245448Snp	mtx_lock(&td->clip_table_lock);
718245448Snp	KASSERT(search_lip(td, &ce->lip) == ce,
719245448Snp	    ("%s: CLIP entry %p p not in CLIP table.", __func__, ce));
720245448Snp	KASSERT(ce->refcount > 0,
721245448Snp	    ("%s: CLIP entry %p has refcount 0", __func__, ce));
722245448Snp	--ce->refcount;
723245448Snp	mtx_unlock(&td->clip_table_lock);
724245448Snp}
725245448Snp
726237263Snpstatic void
727245448Snpinit_clip_table(struct adapter *sc, struct tom_data *td)
728245448Snp{
729245448Snp
730245448Snp	ASSERT_SYNCHRONIZED_OP(sc);
731245448Snp
732245448Snp	mtx_init(&td->clip_table_lock, "CLIP table lock", NULL, MTX_DEF);
733245448Snp	TAILQ_INIT(&td->clip_table);
734249627Snp	td->clip_gen = -1;
735245448Snp
736249627Snp	update_clip_table(sc, td);
737249627Snp}
738249627Snp
739249627Snpstatic void
740249627Snpupdate_clip(struct adapter *sc, void *arg __unused)
741249627Snp{
742249627Snp
743249627Snp	if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4tomuc"))
744249627Snp		return;
745249627Snp
746249627Snp	if (sc->flags & TOM_INIT_DONE)
747249627Snp		update_clip_table(sc, sc->tom_softc);
748249627Snp
749249627Snp	end_synchronized_op(sc, LOCK_HELD);
750249627Snp}
751249627Snp
752249627Snpstatic void
753249627Snpt4_clip_task(void *arg, int count)
754249627Snp{
755249627Snp
756249627Snp	t4_iterate(update_clip, NULL);
757249627Snp}
758249627Snp
759249627Snpstatic void
760249627Snpupdate_clip_table(struct adapter *sc, struct tom_data *td)
761249627Snp{
762249627Snp	struct in6_ifaddr *ia;
763249627Snp	struct in6_addr *lip, tlip;
764249627Snp	struct clip_head stale;
765249627Snp	struct clip_entry *ce, *ce_temp;
766249627Snp	int rc, gen = atomic_load_acq_int(&in6_ifaddr_gen);
767249627Snp
768249627Snp	ASSERT_SYNCHRONIZED_OP(sc);
769249627Snp
770245448Snp	IN6_IFADDR_RLOCK();
771249627Snp	mtx_lock(&td->clip_table_lock);
772249627Snp
773249627Snp	if (gen == td->clip_gen)
774249627Snp		goto done;
775249627Snp
776249627Snp	TAILQ_INIT(&stale);
777249627Snp	TAILQ_CONCAT(&stale, &td->clip_table, link);
778249627Snp
779245448Snp	TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
780245448Snp		lip = &ia->ia_addr.sin6_addr;
781245448Snp
782245448Snp		KASSERT(!IN6_IS_ADDR_MULTICAST(lip),
783245448Snp		    ("%s: mcast address in in6_ifaddr list", __func__));
784245448Snp
785245448Snp		if (IN6_IS_ADDR_LOOPBACK(lip))
786245448Snp			continue;
787245448Snp		if (IN6_IS_SCOPE_EMBED(lip)) {
788245448Snp			/* Remove the embedded scope */
789245448Snp			tlip = *lip;
790245448Snp			lip = &tlip;
791245448Snp			in6_clearscope(lip);
792245448Snp		}
793245448Snp		/*
794245448Snp		 * XXX: how to weed out the link local address for the loopback
795245448Snp		 * interface?  It's fe80::1 usually (always?).
796245448Snp		 */
797245448Snp
798249627Snp		/*
799249627Snp		 * If it's in the main list then we already know it's not stale.
800249627Snp		 */
801249627Snp		TAILQ_FOREACH(ce, &td->clip_table, link) {
802249627Snp			if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
803249627Snp				goto next;
804249627Snp		}
805249627Snp
806249627Snp		/*
807249627Snp		 * If it's in the stale list we should move it to the main list.
808249627Snp		 */
809249627Snp		TAILQ_FOREACH(ce, &stale, link) {
810249627Snp			if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) {
811249627Snp				TAILQ_REMOVE(&stale, ce, link);
812245448Snp				TAILQ_INSERT_TAIL(&td->clip_table, ce, link);
813249627Snp				goto next;
814249627Snp			}
815249627Snp		}
816249627Snp
817249627Snp		/* A new IP6 address; add it to the CLIP table */
818249627Snp		ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT);
819249627Snp		memcpy(&ce->lip, lip, sizeof(ce->lip));
820249627Snp		ce->refcount = 0;
821249627Snp		rc = add_lip(sc, lip);
822249627Snp		if (rc == 0)
823249627Snp			TAILQ_INSERT_TAIL(&td->clip_table, ce, link);
824249627Snp		else {
825249627Snp			char ip[INET6_ADDRSTRLEN];
826249627Snp
827249627Snp			inet_ntop(AF_INET6, &ce->lip, &ip[0], sizeof(ip));
828249627Snp			log(LOG_ERR, "%s: could not add %s (%d)\n",
829249627Snp			    __func__, ip, rc);
830249627Snp			free(ce, M_CXGBE);
831249627Snp		}
832249627Snpnext:
833249627Snp		continue;
834249627Snp	}
835249627Snp
836249627Snp	/*
837249627Snp	 * Remove stale addresses (those no longer in V_in6_ifaddrhead) that are
838249627Snp	 * no longer referenced by the driver.
839249627Snp	 */
840249627Snp	TAILQ_FOREACH_SAFE(ce, &stale, link, ce_temp) {
841249627Snp		if (ce->refcount == 0) {
842249627Snp			rc = delete_lip(sc, &ce->lip);
843249627Snp			if (rc == 0) {
844249627Snp				TAILQ_REMOVE(&stale, ce, link);
845245448Snp				free(ce, M_CXGBE);
846249627Snp			} else {
847249627Snp				char ip[INET6_ADDRSTRLEN];
848249627Snp
849249627Snp				inet_ntop(AF_INET6, &ce->lip, &ip[0],
850249627Snp				    sizeof(ip));
851249627Snp				log(LOG_ERR, "%s: could not delete %s (%d)\n",
852249627Snp				    __func__, ip, rc);
853249627Snp			}
854245448Snp		}
855245448Snp	}
856249627Snp	/* The ones that are still referenced need to stay in the CLIP table */
857249627Snp	TAILQ_CONCAT(&td->clip_table, &stale, link);
858249627Snp
859249627Snp	td->clip_gen = gen;
860249627Snpdone:
861249627Snp	mtx_unlock(&td->clip_table_lock);
862245448Snp	IN6_IFADDR_RUNLOCK();
863245448Snp}
864245448Snp
865245448Snpstatic void
866245448Snpdestroy_clip_table(struct adapter *sc, struct tom_data *td)
867245448Snp{
868245448Snp	struct clip_entry *ce, *ce_temp;
869245448Snp
870245448Snp	if (mtx_initialized(&td->clip_table_lock)) {
871245448Snp		mtx_lock(&td->clip_table_lock);
872245448Snp		TAILQ_FOREACH_SAFE(ce, &td->clip_table, link, ce_temp) {
873245448Snp			KASSERT(ce->refcount == 0,
874245448Snp			    ("%s: CLIP entry %p still in use (%d)", __func__,
875245448Snp			    ce, ce->refcount));
876245448Snp			TAILQ_REMOVE(&td->clip_table, ce, link);
877245448Snp			delete_lip(sc, &ce->lip);
878245448Snp			free(ce, M_CXGBE);
879245448Snp		}
880245448Snp		mtx_unlock(&td->clip_table_lock);
881245448Snp		mtx_destroy(&td->clip_table_lock);
882245448Snp	}
883245448Snp}
884245448Snp
885245448Snpstatic void
886237263Snpfree_tom_data(struct adapter *sc, struct tom_data *td)
887237263Snp{
888245448Snp
889245448Snp	ASSERT_SYNCHRONIZED_OP(sc);
890245448Snp
891237263Snp	KASSERT(TAILQ_EMPTY(&td->toep_list),
892237263Snp	    ("%s: TOE PCB list is not empty.", __func__));
893237263Snp	KASSERT(td->lctx_count == 0,
894237263Snp	    ("%s: lctx hash table is not empty.", __func__));
895237263Snp
896237263Snp	t4_uninit_l2t_cpl_handlers(sc);
897239344Snp	t4_uninit_cpl_io_handlers(sc);
898239344Snp	t4_uninit_ddp(sc, td);
899245448Snp	destroy_clip_table(sc, td);
900237263Snp
901237263Snp	if (td->listen_mask != 0)
902237263Snp		hashdestroy(td->listen_hash, M_CXGBE, td->listen_mask);
903237263Snp
904237263Snp	if (mtx_initialized(&td->lctx_hash_lock))
905237263Snp		mtx_destroy(&td->lctx_hash_lock);
906237263Snp	if (mtx_initialized(&td->toep_list_lock))
907237263Snp		mtx_destroy(&td->toep_list_lock);
908237263Snp
909237263Snp	free_tid_tabs(&sc->tids);
910237263Snp	free(td, M_CXGBE);
911237263Snp}
912237263Snp
913237263Snp/*
914237263Snp * Ground control to Major TOM
915237263Snp * Commencing countdown, engines on
916237263Snp */
917237263Snpstatic int
918237263Snpt4_tom_activate(struct adapter *sc)
919237263Snp{
920237263Snp	struct tom_data *td;
921237263Snp	struct toedev *tod;
922237263Snp	int i, rc;
923237263Snp
924245274Snp	ASSERT_SYNCHRONIZED_OP(sc);
925237263Snp
926237263Snp	/* per-adapter softc for TOM */
927237263Snp	td = malloc(sizeof(*td), M_CXGBE, M_ZERO | M_NOWAIT);
928237263Snp	if (td == NULL)
929237263Snp		return (ENOMEM);
930237263Snp
931237263Snp	/* List of TOE PCBs and associated lock */
932237263Snp	mtx_init(&td->toep_list_lock, "PCB list lock", NULL, MTX_DEF);
933237263Snp	TAILQ_INIT(&td->toep_list);
934237263Snp
935237263Snp	/* Listen context */
936237263Snp	mtx_init(&td->lctx_hash_lock, "lctx hash lock", NULL, MTX_DEF);
937237263Snp	td->listen_hash = hashinit_flags(LISTEN_HASH_SIZE, M_CXGBE,
938237263Snp	    &td->listen_mask, HASH_NOWAIT);
939237263Snp
940237263Snp	/* TID tables */
941237263Snp	rc = alloc_tid_tabs(&sc->tids);
942237263Snp	if (rc != 0)
943237263Snp		goto done;
944237263Snp
945245448Snp	/* DDP page pods and CPL handlers */
946239344Snp	t4_init_ddp(sc, td);
947239344Snp
948245448Snp	/* CLIP table for IPv6 offload */
949245448Snp	init_clip_table(sc, td);
950245448Snp
951237263Snp	/* CPL handlers */
952237263Snp	t4_init_connect_cpl_handlers(sc);
953237263Snp	t4_init_l2t_cpl_handlers(sc);
954237263Snp	t4_init_listen_cpl_handlers(sc);
955237263Snp	t4_init_cpl_io_handlers(sc);
956237263Snp
957237263Snp	/* toedev ops */
958237263Snp	tod = &td->tod;
959237263Snp	init_toedev(tod);
960237263Snp	tod->tod_softc = sc;
961237263Snp	tod->tod_connect = t4_connect;
962237263Snp	tod->tod_listen_start = t4_listen_start;
963237263Snp	tod->tod_listen_stop = t4_listen_stop;
964237263Snp	tod->tod_rcvd = t4_rcvd;
965237263Snp	tod->tod_output = t4_tod_output;
966237263Snp	tod->tod_send_rst = t4_send_rst;
967237263Snp	tod->tod_send_fin = t4_send_fin;
968237263Snp	tod->tod_pcb_detach = t4_pcb_detach;
969237263Snp	tod->tod_l2_update = t4_l2_update;
970237263Snp	tod->tod_syncache_added = t4_syncache_added;
971237263Snp	tod->tod_syncache_removed = t4_syncache_removed;
972237263Snp	tod->tod_syncache_respond = t4_syncache_respond;
973237263Snp	tod->tod_offload_socket = t4_offload_socket;
974252716Snp	tod->tod_ctloutput = t4_ctloutput;
975237263Snp
976237263Snp	for_each_port(sc, i)
977237263Snp		TOEDEV(sc->port[i]->ifp) = &td->tod;
978237263Snp
979237263Snp	sc->tom_softc = td;
980237263Snp	sc->flags |= TOM_INIT_DONE;
981237263Snp	register_toedev(sc->tom_softc);
982237263Snp
983237263Snpdone:
984237263Snp	if (rc != 0)
985237263Snp		free_tom_data(sc, td);
986237263Snp	return (rc);
987237263Snp}
988237263Snp
989237263Snpstatic int
990237263Snpt4_tom_deactivate(struct adapter *sc)
991237263Snp{
992237263Snp	int rc = 0;
993237263Snp	struct tom_data *td = sc->tom_softc;
994237263Snp
995245274Snp	ASSERT_SYNCHRONIZED_OP(sc);
996237263Snp
997237263Snp	if (td == NULL)
998237263Snp		return (0);	/* XXX. KASSERT? */
999237263Snp
1000237263Snp	if (sc->offload_map != 0)
1001237263Snp		return (EBUSY);	/* at least one port has IFCAP_TOE enabled */
1002237263Snp
1003237263Snp	mtx_lock(&td->toep_list_lock);
1004237263Snp	if (!TAILQ_EMPTY(&td->toep_list))
1005237263Snp		rc = EBUSY;
1006237263Snp	mtx_unlock(&td->toep_list_lock);
1007237263Snp
1008237263Snp	mtx_lock(&td->lctx_hash_lock);
1009237263Snp	if (td->lctx_count > 0)
1010237263Snp		rc = EBUSY;
1011237263Snp	mtx_unlock(&td->lctx_hash_lock);
1012237263Snp
1013237263Snp	if (rc == 0) {
1014237263Snp		unregister_toedev(sc->tom_softc);
1015237263Snp		free_tom_data(sc, td);
1016237263Snp		sc->tom_softc = NULL;
1017237263Snp		sc->flags &= ~TOM_INIT_DONE;
1018237263Snp	}
1019237263Snp
1020237263Snp	return (rc);
1021237263Snp}
1022237263Snp
1023249627Snpstatic void
1024249627Snpt4_tom_ifaddr_event(void *arg __unused, struct ifnet *ifp)
1025249627Snp{
1026249627Snp
1027249627Snp	atomic_add_rel_int(&in6_ifaddr_gen, 1);
1028249627Snp	taskqueue_enqueue_timeout(taskqueue_thread, &clip_task, -hz / 4);
1029249627Snp}
1030249627Snp
1031237263Snpstatic int
1032237263Snpt4_tom_mod_load(void)
1033237263Snp{
1034237263Snp	int rc;
1035245441Snp	struct protosw *tcp_protosw, *tcp6_protosw;
1036237263Snp
1037239344Snp	tcp_protosw = pffindproto(PF_INET, IPPROTO_TCP, SOCK_STREAM);
1038239344Snp	if (tcp_protosw == NULL)
1039239344Snp		return (ENOPROTOOPT);
1040239344Snp	bcopy(tcp_protosw, &ddp_protosw, sizeof(ddp_protosw));
1041239344Snp	bcopy(tcp_protosw->pr_usrreqs, &ddp_usrreqs, sizeof(ddp_usrreqs));
1042239344Snp	ddp_usrreqs.pru_soreceive = t4_soreceive_ddp;
1043239344Snp	ddp_protosw.pr_usrreqs = &ddp_usrreqs;
1044239344Snp
1045245441Snp	tcp6_protosw = pffindproto(PF_INET6, IPPROTO_TCP, SOCK_STREAM);
1046245441Snp	if (tcp6_protosw == NULL)
1047245441Snp		return (ENOPROTOOPT);
1048245441Snp	bcopy(tcp6_protosw, &ddp6_protosw, sizeof(ddp6_protosw));
1049245441Snp	bcopy(tcp6_protosw->pr_usrreqs, &ddp6_usrreqs, sizeof(ddp6_usrreqs));
1050245441Snp	ddp6_usrreqs.pru_soreceive = t4_soreceive_ddp;
1051245441Snp	ddp6_protosw.pr_usrreqs = &ddp6_usrreqs;
1052245441Snp
1053249627Snp	TIMEOUT_TASK_INIT(taskqueue_thread, &clip_task, 0, t4_clip_task, NULL);
1054249627Snp	ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event,
1055249627Snp	    t4_tom_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
1056249627Snp
1057237263Snp	rc = t4_register_uld(&tom_uld_info);
1058237263Snp	if (rc != 0)
1059237263Snp		t4_tom_mod_unload();
1060237263Snp
1061237263Snp	return (rc);
1062237263Snp}
1063237263Snp
1064237263Snpstatic void
1065237263Snptom_uninit(struct adapter *sc, void *arg __unused)
1066237263Snp{
1067255006Snp	if (begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4tomun"))
1068245274Snp		return;
1069245274Snp
1070237263Snp	/* Try to free resources (works only if no port has IFCAP_TOE) */
1071237263Snp	if (sc->flags & TOM_INIT_DONE)
1072237263Snp		t4_deactivate_uld(sc, ULD_TOM);
1073245274Snp
1074255006Snp	end_synchronized_op(sc, 0);
1075237263Snp}
1076237263Snp
1077237263Snpstatic int
1078237263Snpt4_tom_mod_unload(void)
1079237263Snp{
1080237263Snp	t4_iterate(tom_uninit, NULL);
1081237263Snp
1082237263Snp	if (t4_unregister_uld(&tom_uld_info) == EBUSY)
1083237263Snp		return (EBUSY);
1084237263Snp
1085249627Snp	if (ifaddr_evhandler) {
1086249627Snp		EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_evhandler);
1087249627Snp		taskqueue_cancel_timeout(taskqueue_thread, &clip_task, NULL);
1088249627Snp	}
1089249627Snp
1090237263Snp	return (0);
1091237263Snp}
1092237263Snp#endif	/* TCP_OFFLOAD */
1093237263Snp
1094237263Snpstatic int
1095237263Snpt4_tom_modevent(module_t mod, int cmd, void *arg)
1096237263Snp{
1097237263Snp	int rc = 0;
1098237263Snp
1099237263Snp#ifdef TCP_OFFLOAD
1100237263Snp	switch (cmd) {
1101237263Snp	case MOD_LOAD:
1102237263Snp		rc = t4_tom_mod_load();
1103237263Snp		break;
1104237263Snp
1105237263Snp	case MOD_UNLOAD:
1106237263Snp		rc = t4_tom_mod_unload();
1107237263Snp		break;
1108237263Snp
1109237263Snp	default:
1110237263Snp		rc = EINVAL;
1111237263Snp	}
1112237263Snp#else
1113237263Snp	printf("t4_tom: compiled without TCP_OFFLOAD support.\n");
1114237263Snp	rc = EOPNOTSUPP;
1115237263Snp#endif
1116237263Snp	return (rc);
1117237263Snp}
1118237263Snp
1119237263Snpstatic moduledata_t t4_tom_moddata= {
1120237263Snp	"t4_tom",
1121237263Snp	t4_tom_modevent,
1122241394Skevlo	0
1123237263Snp};
1124237263Snp
1125237263SnpMODULE_VERSION(t4_tom, 1);
1126237263SnpMODULE_DEPEND(t4_tom, toecore, 1, 1, 1);
1127237263SnpMODULE_DEPEND(t4_tom, t4nex, 1, 1, 1);
1128237263SnpDECLARE_MODULE(t4_tom, t4_tom_moddata, SI_SUB_EXEC, SI_ORDER_ANY);
1129