1237263Snp/*-
2237263Snp * Copyright (c) 2012 Chelsio Communications, Inc.
3237263Snp * All rights reserved.
4237263Snp * Written by: Navdeep Parhar <np@FreeBSD.org>
5237263Snp *
6237263Snp * Redistribution and use in source and binary forms, with or without
7237263Snp * modification, are permitted provided that the following conditions
8237263Snp * are met:
9237263Snp * 1. Redistributions of source code must retain the above copyright
10237263Snp *    notice, this list of conditions and the following disclaimer.
11237263Snp * 2. Redistributions in binary form must reproduce the above copyright
12237263Snp *    notice, this list of conditions and the following disclaimer in the
13237263Snp *    documentation and/or other materials provided with the distribution.
14237263Snp *
15237263Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16237263Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17237263Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18237263Snp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19237263Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20237263Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21237263Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22237263Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23237263Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24237263Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25237263Snp * SUCH DAMAGE.
26237263Snp */
27237263Snp
28237263Snp#include <sys/cdefs.h>
29237263Snp__FBSDID("$FreeBSD$");
30237263Snp
31237263Snp#include "opt_inet.h"
32237263Snp#include "opt_inet6.h"
33237263Snp
34237263Snp#include <sys/param.h>
35237263Snp#include <sys/kernel.h>
36237263Snp#include <sys/systm.h>
37295126Sglebius#include <sys/malloc.h>
38237263Snp#include <sys/mbuf.h>
39237263Snp#include <sys/module.h>
40237263Snp#include <sys/types.h>
41237263Snp#include <sys/sockopt.h>
42237263Snp#include <sys/sysctl.h>
43237263Snp#include <sys/socket.h>
44237263Snp
45237263Snp#include <net/ethernet.h>
46237263Snp#include <net/if.h>
47257241Sglebius#include <net/if_var.h>
48237263Snp#include <net/if_types.h>
49237263Snp#include <net/if_vlan_var.h>
50237263Snp#include <net/if_llatbl.h>
51237263Snp#include <net/route.h>
52237263Snp
53237263Snp#include <netinet/if_ether.h>
54237263Snp#include <netinet/in.h>
55237263Snp#include <netinet/in_pcb.h>
56237263Snp#include <netinet/in_var.h>
57245932Snp#include <netinet6/in6_var.h>
58245916Snp#include <netinet6/in6_pcb.h>
59237263Snp#include <netinet6/nd6.h>
60237263Snp#define TCPSTATES
61237263Snp#include <netinet/tcp.h>
62237263Snp#include <netinet/tcp_fsm.h>
63237263Snp#include <netinet/tcp_timer.h>
64237263Snp#include <netinet/tcp_var.h>
65237263Snp#include <netinet/tcp_syncache.h>
66237263Snp#include <netinet/tcp_offload.h>
67237263Snp#include <netinet/toecore.h>
68237263Snp
69237263Snpstatic struct mtx toedev_lock;
70237263Snpstatic TAILQ_HEAD(, toedev) toedev_list;
71237263Snpstatic eventhandler_tag listen_start_eh;
72237263Snpstatic eventhandler_tag listen_stop_eh;
73237263Snpstatic eventhandler_tag lle_event_eh;
74237263Snp
75237263Snpstatic int
76237263Snptoedev_connect(struct toedev *tod __unused, struct socket *so __unused,
77237263Snp    struct rtentry *rt __unused, struct sockaddr *nam __unused)
78237263Snp{
79237263Snp
80237263Snp	return (ENOTSUP);
81237263Snp}
82237263Snp
83237263Snpstatic int
84237263Snptoedev_listen_start(struct toedev *tod __unused, struct tcpcb *tp __unused)
85237263Snp{
86237263Snp
87237263Snp	return (ENOTSUP);
88237263Snp}
89237263Snp
90237263Snpstatic int
91237263Snptoedev_listen_stop(struct toedev *tod __unused, struct tcpcb *tp __unused)
92237263Snp{
93237263Snp
94237263Snp	return (ENOTSUP);
95237263Snp}
96237263Snp
97237263Snpstatic void
98237263Snptoedev_input(struct toedev *tod __unused, struct tcpcb *tp __unused,
99237263Snp    struct mbuf *m)
100237263Snp{
101237263Snp
102237263Snp	m_freem(m);
103237263Snp	return;
104237263Snp}
105237263Snp
106237263Snpstatic void
107237263Snptoedev_rcvd(struct toedev *tod __unused, struct tcpcb *tp __unused)
108237263Snp{
109237263Snp
110237263Snp	return;
111237263Snp}
112237263Snp
113237263Snpstatic int
114237263Snptoedev_output(struct toedev *tod __unused, struct tcpcb *tp __unused)
115237263Snp{
116237263Snp
117237263Snp	return (ENOTSUP);
118237263Snp}
119237263Snp
120237263Snpstatic void
121237263Snptoedev_pcb_detach(struct toedev *tod __unused, struct tcpcb *tp __unused)
122237263Snp{
123237263Snp
124237263Snp	return;
125237263Snp}
126237263Snp
127237263Snpstatic void
128237263Snptoedev_l2_update(struct toedev *tod __unused, struct ifnet *ifp __unused,
129237263Snp    struct sockaddr *sa __unused, uint8_t *lladdr __unused,
130237263Snp    uint16_t vtag __unused)
131237263Snp{
132237263Snp
133237263Snp	return;
134237263Snp}
135237263Snp
136237263Snpstatic void
137237263Snptoedev_route_redirect(struct toedev *tod __unused, struct ifnet *ifp __unused,
138237263Snp    struct rtentry *rt0 __unused, struct rtentry *rt1 __unused)
139237263Snp{
140237263Snp
141237263Snp	return;
142237263Snp}
143237263Snp
144237263Snpstatic void
145237263Snptoedev_syncache_added(struct toedev *tod __unused, void *ctx __unused)
146237263Snp{
147237263Snp
148237263Snp	return;
149237263Snp}
150237263Snp
151237263Snpstatic void
152237263Snptoedev_syncache_removed(struct toedev *tod __unused, void *ctx __unused)
153237263Snp{
154237263Snp
155237263Snp	return;
156237263Snp}
157237263Snp
158237263Snpstatic int
159237263Snptoedev_syncache_respond(struct toedev *tod __unused, void *ctx __unused,
160237263Snp    struct mbuf *m)
161237263Snp{
162237263Snp
163237263Snp	m_freem(m);
164237263Snp	return (0);
165237263Snp}
166237263Snp
167237263Snpstatic void
168237263Snptoedev_offload_socket(struct toedev *tod __unused, void *ctx __unused,
169237263Snp    struct socket *so __unused)
170237263Snp{
171237263Snp
172237263Snp	return;
173237263Snp}
174237263Snp
175237263Snpstatic void
176237263Snptoedev_ctloutput(struct toedev *tod __unused, struct tcpcb *tp __unused,
177237263Snp    int sopt_dir __unused, int sopt_name __unused)
178237263Snp{
179237263Snp
180237263Snp	return;
181237263Snp}
182237263Snp
183237263Snp/*
184237263Snp * Inform one or more TOE devices about a listening socket.
185237263Snp */
186237263Snpstatic void
187237263Snptoe_listen_start(struct inpcb *inp, void *arg)
188237263Snp{
189237263Snp	struct toedev *t, *tod;
190237263Snp	struct tcpcb *tp;
191237263Snp
192237263Snp	INP_WLOCK_ASSERT(inp);
193237263Snp	KASSERT(inp->inp_pcbinfo == &V_tcbinfo,
194237263Snp	    ("%s: inp is not a TCP inp", __func__));
195237263Snp
196237263Snp	if (inp->inp_flags & (INP_DROPPED | INP_TIMEWAIT))
197237263Snp		return;
198237263Snp
199237263Snp	tp = intotcpcb(inp);
200237263Snp	if (tp->t_state != TCPS_LISTEN)
201237263Snp		return;
202237263Snp
203237263Snp	t = arg;
204237263Snp	mtx_lock(&toedev_lock);
205237263Snp	TAILQ_FOREACH(tod, &toedev_list, link) {
206237263Snp		if (t == NULL || t == tod)
207237263Snp			tod->tod_listen_start(tod, tp);
208237263Snp	}
209237263Snp	mtx_unlock(&toedev_lock);
210237263Snp}
211237263Snp
212237263Snpstatic void
213237263Snptoe_listen_start_event(void *arg __unused, struct tcpcb *tp)
214237263Snp{
215237263Snp	struct inpcb *inp = tp->t_inpcb;
216237263Snp
217237263Snp	INP_WLOCK_ASSERT(inp);
218237263Snp	KASSERT(tp->t_state == TCPS_LISTEN,
219237263Snp	    ("%s: t_state %s", __func__, tcpstates[tp->t_state]));
220237263Snp
221237263Snp	toe_listen_start(inp, NULL);
222237263Snp}
223237263Snp
224237263Snpstatic void
225237263Snptoe_listen_stop_event(void *arg __unused, struct tcpcb *tp)
226237263Snp{
227237263Snp	struct toedev *tod;
228237263Snp#ifdef INVARIANTS
229237263Snp	struct inpcb *inp = tp->t_inpcb;
230237263Snp#endif
231237263Snp
232237263Snp	INP_WLOCK_ASSERT(inp);
233237263Snp	KASSERT(tp->t_state == TCPS_LISTEN,
234237263Snp	    ("%s: t_state %s", __func__, tcpstates[tp->t_state]));
235237263Snp
236237263Snp	mtx_lock(&toedev_lock);
237237263Snp	TAILQ_FOREACH(tod, &toedev_list, link)
238237263Snp	    tod->tod_listen_stop(tod, tp);
239237263Snp	mtx_unlock(&toedev_lock);
240237263Snp}
241237263Snp
242237263Snp/*
243237263Snp * Fill up a freshly allocated toedev struct with reasonable defaults.
244237263Snp */
245237263Snpvoid
246237263Snpinit_toedev(struct toedev *tod)
247237263Snp{
248237263Snp
249237263Snp	tod->tod_softc = NULL;
250237263Snp
251237263Snp	/*
252237263Snp	 * Provide no-op defaults so that the kernel can call any toedev
253237263Snp	 * function without having to check whether the TOE driver supplied one
254237263Snp	 * or not.
255237263Snp	 */
256237263Snp	tod->tod_connect = toedev_connect;
257237263Snp	tod->tod_listen_start = toedev_listen_start;
258237263Snp	tod->tod_listen_stop = toedev_listen_stop;
259237263Snp	tod->tod_input = toedev_input;
260237263Snp	tod->tod_rcvd = toedev_rcvd;
261237263Snp	tod->tod_output = toedev_output;
262237263Snp	tod->tod_send_rst = toedev_output;
263237263Snp	tod->tod_send_fin = toedev_output;
264237263Snp	tod->tod_pcb_detach = toedev_pcb_detach;
265237263Snp	tod->tod_l2_update = toedev_l2_update;
266237263Snp	tod->tod_route_redirect = toedev_route_redirect;
267237263Snp	tod->tod_syncache_added = toedev_syncache_added;
268237263Snp	tod->tod_syncache_removed = toedev_syncache_removed;
269237263Snp	tod->tod_syncache_respond = toedev_syncache_respond;
270237263Snp	tod->tod_offload_socket = toedev_offload_socket;
271237263Snp	tod->tod_ctloutput = toedev_ctloutput;
272237263Snp}
273237263Snp
274237263Snp/*
275237263Snp * Register an active TOE device with the system.  This allows it to receive
276237263Snp * notifications from the kernel.
277237263Snp */
278237263Snpint
279237263Snpregister_toedev(struct toedev *tod)
280237263Snp{
281237263Snp	struct toedev *t;
282237263Snp
283237263Snp	mtx_lock(&toedev_lock);
284237263Snp	TAILQ_FOREACH(t, &toedev_list, link) {
285237263Snp		if (t == tod) {
286237263Snp			mtx_unlock(&toedev_lock);
287237263Snp			return (EEXIST);
288237263Snp		}
289237263Snp	}
290237263Snp
291237263Snp	TAILQ_INSERT_TAIL(&toedev_list, tod, link);
292237263Snp	registered_toedevs++;
293237263Snp	mtx_unlock(&toedev_lock);
294237263Snp
295237263Snp	inp_apply_all(toe_listen_start, tod);
296237263Snp
297237263Snp	return (0);
298237263Snp}
299237263Snp
300237263Snp/*
301237263Snp * Remove the TOE device from the global list of active TOE devices.  It is the
302237263Snp * caller's responsibility to ensure that the TOE device is quiesced prior to
303237263Snp * this call.
304237263Snp */
305237263Snpint
306237263Snpunregister_toedev(struct toedev *tod)
307237263Snp{
308237263Snp	struct toedev *t, *t2;
309237263Snp	int rc = ENODEV;
310237263Snp
311237263Snp	mtx_lock(&toedev_lock);
312237263Snp	TAILQ_FOREACH_SAFE(t, &toedev_list, link, t2) {
313237263Snp		if (t == tod) {
314237263Snp			TAILQ_REMOVE(&toedev_list, tod, link);
315237263Snp			registered_toedevs--;
316237263Snp			rc = 0;
317237263Snp			break;
318237263Snp		}
319237263Snp	}
320237263Snp	KASSERT(registered_toedevs >= 0,
321237263Snp	    ("%s: registered_toedevs (%d) < 0", __func__, registered_toedevs));
322237263Snp	mtx_unlock(&toedev_lock);
323237263Snp	return (rc);
324237263Snp}
325237263Snp
326237263Snpvoid
327237263Snptoe_syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
328237263Snp    struct inpcb *inp, void *tod, void *todctx)
329237263Snp{
330237263Snp	struct socket *lso = inp->inp_socket;
331237263Snp
332237263Snp	INP_WLOCK_ASSERT(inp);
333237263Snp
334237263Snp	syncache_add(inc, to, th, inp, &lso, NULL, tod, todctx);
335237263Snp}
336237263Snp
337237263Snpint
338237263Snptoe_syncache_expand(struct in_conninfo *inc, struct tcpopt *to,
339237263Snp    struct tcphdr *th, struct socket **lsop)
340237263Snp{
341237263Snp
342286227Sjch	INP_INFO_RLOCK_ASSERT(&V_tcbinfo);
343237263Snp
344237263Snp	return (syncache_expand(inc, to, th, lsop, NULL));
345237263Snp}
346237263Snp
347237263Snp/*
348237263Snp * General purpose check to see if a 4-tuple is in use by the kernel.  If a TCP
349237263Snp * header (presumably for an incoming SYN) is also provided, an existing 4-tuple
350237263Snp * in TIME_WAIT may be assassinated freeing it up for re-use.
351237263Snp *
352237263Snp * Note that the TCP header must have been run through tcp_fields_to_host() or
353237263Snp * equivalent.
354237263Snp */
355237263Snpint
356237263Snptoe_4tuple_check(struct in_conninfo *inc, struct tcphdr *th, struct ifnet *ifp)
357237263Snp{
358237263Snp	struct inpcb *inp;
359237263Snp
360245916Snp	if (inc->inc_flags & INC_ISIPV6) {
361245916Snp		inp = in6_pcblookup(&V_tcbinfo, &inc->inc6_faddr,
362245916Snp		    inc->inc_fport, &inc->inc6_laddr, inc->inc_lport,
363245916Snp		    INPLOOKUP_WLOCKPCB, ifp);
364245916Snp	} else {
365245916Snp		inp = in_pcblookup(&V_tcbinfo, inc->inc_faddr, inc->inc_fport,
366245916Snp		    inc->inc_laddr, inc->inc_lport, INPLOOKUP_WLOCKPCB, ifp);
367245916Snp	}
368237263Snp	if (inp != NULL) {
369237263Snp		INP_WLOCK_ASSERT(inp);
370237263Snp
371237263Snp		if ((inp->inp_flags & INP_TIMEWAIT) && th != NULL) {
372237263Snp
373286227Sjch			INP_INFO_RLOCK_ASSERT(&V_tcbinfo); /* for twcheck */
374237263Snp			if (!tcp_twcheck(inp, NULL, th, NULL, 0))
375237263Snp				return (EADDRINUSE);
376237263Snp		} else {
377237263Snp			INP_WUNLOCK(inp);
378237263Snp			return (EADDRINUSE);
379237263Snp		}
380237263Snp	}
381237263Snp
382237263Snp	return (0);
383237263Snp}
384237263Snp
385237263Snpstatic void
386237263Snptoe_lle_event(void *arg __unused, struct llentry *lle, int evt)
387237263Snp{
388237263Snp	struct toedev *tod;
389237263Snp	struct ifnet *ifp;
390237263Snp	struct sockaddr *sa;
391237263Snp	uint8_t *lladdr;
392237263Snp	uint16_t vtag;
393286624Smelifaro	int family;
394286624Smelifaro	struct sockaddr_in6 sin6;
395237263Snp
396237263Snp	LLE_WLOCK_ASSERT(lle);
397237263Snp
398286624Smelifaro	ifp = lltable_get_ifp(lle->lle_tbl);
399286624Smelifaro	family = lltable_get_af(lle->lle_tbl);
400237263Snp
401286624Smelifaro	if (family != AF_INET && family != AF_INET6)
402286624Smelifaro		return;
403237263Snp	/*
404237263Snp	 * Not interested if the interface's TOE capability is not enabled.
405237263Snp	 */
406286624Smelifaro	if ((family == AF_INET && !(ifp->if_capenable & IFCAP_TOE4)) ||
407286624Smelifaro	    (family == AF_INET6 && !(ifp->if_capenable & IFCAP_TOE6)))
408237263Snp		return;
409237263Snp
410237263Snp	tod = TOEDEV(ifp);
411237263Snp	if (tod == NULL)
412237263Snp		return;
413237263Snp
414286624Smelifaro	sa = (struct sockaddr *)&sin6;
415286624Smelifaro	lltable_fill_sa_entry(lle, sa);
416286624Smelifaro
417237263Snp	vtag = 0xfff;
418237263Snp	if (evt != LLENTRY_RESOLVED) {
419237263Snp
420237263Snp		/*
421237263Snp		 * LLENTRY_TIMEDOUT, LLENTRY_DELETED, LLENTRY_EXPIRED all mean
422237263Snp		 * this entry is going to be deleted.
423237263Snp		 */
424237263Snp
425237263Snp		lladdr = NULL;
426237263Snp	} else {
427237263Snp
428237263Snp		KASSERT(lle->la_flags & LLE_VALID,
429237263Snp		    ("%s: %p resolved but not valid?", __func__, lle));
430237263Snp
431292978Smelifaro		lladdr = (uint8_t *)lle->ll_addr;
432237263Snp#ifdef VLAN_TAG
433237263Snp		VLAN_TAG(ifp, &vtag);
434237263Snp#endif
435237263Snp	}
436237263Snp
437237263Snp	tod->tod_l2_update(tod, ifp, sa, lladdr, vtag);
438237263Snp}
439237263Snp
440237263Snp/*
441237263Snp * Returns 0 or EWOULDBLOCK on success (any other value is an error).  0 means
442237263Snp * lladdr and vtag are valid on return, EWOULDBLOCK means the TOE driver's
443237263Snp * tod_l2_update will be called later, when the entry is resolved or times out.
444237263Snp */
445237263Snpint
446237263Snptoe_l2_resolve(struct toedev *tod, struct ifnet *ifp, struct sockaddr *sa,
447237263Snp    uint8_t *lladdr, uint16_t *vtag)
448237263Snp{
449237263Snp	int rc;
450237263Snp
451237263Snp	switch (sa->sa_family) {
452237263Snp#ifdef INET
453237263Snp	case AF_INET:
454301217Sgnn		rc = arpresolve(ifp, 0, NULL, sa, lladdr, NULL, NULL);
455237263Snp		break;
456237263Snp#endif
457237263Snp#ifdef INET6
458237263Snp	case AF_INET6:
459301217Sgnn		rc = nd6_resolve(ifp, 0, NULL, sa, lladdr, NULL, NULL);
460237263Snp		break;
461237263Snp#endif
462237263Snp	default:
463237263Snp		return (EPROTONOSUPPORT);
464237263Snp	}
465237263Snp
466237263Snp	if (rc == 0) {
467237263Snp#ifdef VLAN_TAG
468237263Snp		if (VLAN_TAG(ifp, vtag) != 0)
469237263Snp#endif
470237263Snp			*vtag = 0xfff;
471237263Snp	}
472237263Snp
473237263Snp	return (rc);
474237263Snp}
475237263Snp
476237263Snpvoid
477239511Snptoe_connect_failed(struct toedev *tod, struct inpcb *inp, int err)
478237263Snp{
479237263Snp
480237263Snp	INP_WLOCK_ASSERT(inp);
481237263Snp
482237263Snp	if (!(inp->inp_flags & INP_DROPPED)) {
483239511Snp		struct tcpcb *tp = intotcpcb(inp);
484239511Snp
485239511Snp		KASSERT(tp->t_flags & TF_TOE,
486239511Snp		    ("%s: tp %p not offloaded.", __func__, tp));
487239511Snp
488237263Snp		if (err == EAGAIN) {
489237263Snp
490237263Snp			/*
491237263Snp			 * Temporary failure during offload, take this PCB back.
492237263Snp			 * Detach from the TOE driver and do the rest of what
493237263Snp			 * TCP's pru_connect would have done if the connection
494237263Snp			 * wasn't offloaded.
495237263Snp			 */
496237263Snp
497237263Snp			tod->tod_pcb_detach(tod, tp);
498237263Snp			KASSERT(!(tp->t_flags & TF_TOE),
499237263Snp			    ("%s: tp %p still offloaded.", __func__, tp));
500237263Snp			tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp));
501292309Srrs			(void) tp->t_fb->tfb_tcp_output(tp);
502237263Snp		} else {
503237263Snp
504286227Sjch			INP_INFO_RLOCK_ASSERT(&V_tcbinfo);
505237263Snp			tp = tcp_drop(tp, err);
506237263Snp			if (tp == NULL)
507237263Snp				INP_WLOCK(inp);	/* re-acquire */
508237263Snp		}
509237263Snp	}
510237263Snp	INP_WLOCK_ASSERT(inp);
511237263Snp}
512237263Snp
513237263Snpstatic int
514237263Snptoecore_load(void)
515237263Snp{
516237263Snp
517237263Snp	mtx_init(&toedev_lock, "toedev lock", NULL, MTX_DEF);
518237263Snp	TAILQ_INIT(&toedev_list);
519237263Snp
520237263Snp	listen_start_eh = EVENTHANDLER_REGISTER(tcp_offload_listen_start,
521237263Snp	    toe_listen_start_event, NULL, EVENTHANDLER_PRI_ANY);
522237263Snp	listen_stop_eh = EVENTHANDLER_REGISTER(tcp_offload_listen_stop,
523237263Snp	    toe_listen_stop_event, NULL, EVENTHANDLER_PRI_ANY);
524237263Snp	lle_event_eh = EVENTHANDLER_REGISTER(lle_event, toe_lle_event, NULL,
525237263Snp	    EVENTHANDLER_PRI_ANY);
526237263Snp
527237263Snp	return (0);
528237263Snp}
529237263Snp
530237263Snpstatic int
531237263Snptoecore_unload(void)
532237263Snp{
533237263Snp
534237263Snp	mtx_lock(&toedev_lock);
535237263Snp	if (!TAILQ_EMPTY(&toedev_list)) {
536237263Snp		mtx_unlock(&toedev_lock);
537237263Snp		return (EBUSY);
538237263Snp	}
539237263Snp
540237263Snp	EVENTHANDLER_DEREGISTER(tcp_offload_listen_start, listen_start_eh);
541237263Snp	EVENTHANDLER_DEREGISTER(tcp_offload_listen_stop, listen_stop_eh);
542237263Snp	EVENTHANDLER_DEREGISTER(lle_event, lle_event_eh);
543237263Snp
544237263Snp	mtx_unlock(&toedev_lock);
545237263Snp	mtx_destroy(&toedev_lock);
546237263Snp
547237263Snp	return (0);
548237263Snp}
549237263Snp
550237263Snpstatic int
551237263Snptoecore_mod_handler(module_t mod, int cmd, void *arg)
552237263Snp{
553237263Snp
554237263Snp	if (cmd == MOD_LOAD)
555237263Snp		return (toecore_load());
556237263Snp
557237263Snp	if (cmd == MOD_UNLOAD)
558237263Snp		return (toecore_unload());
559237263Snp
560237263Snp	return (EOPNOTSUPP);
561237263Snp}
562237263Snp
563237263Snpstatic moduledata_t mod_data= {
564237263Snp	"toecore",
565237263Snp	toecore_mod_handler,
566241394Skevlo	0
567237263Snp};
568237263Snp
569237263SnpMODULE_VERSION(toecore, 1);
570237263SnpDECLARE_MODULE(toecore, mod_data, SI_SUB_EXEC, SI_ORDER_ANY);
571