1287621Smav/*-
2287621Smav * Copyright (c) 2015 Alexander Motin <mav@FreeBSD.org>
3287621Smav * All rights reserved.
4287621Smav *
5287621Smav * Redistribution and use in source and binary forms, with or without
6287621Smav * modification, are permitted provided that the following conditions
7287621Smav * are met:
8287621Smav * 1. Redistributions of source code must retain the above copyright
9287621Smav *    notice, this list of conditions and the following disclaimer,
10287621Smav *    without modification, immediately at the beginning of the file.
11287621Smav * 2. Redistributions in binary form must reproduce the above copyright
12287621Smav *    notice, this list of conditions and the following disclaimer in the
13287621Smav *    documentation and/or other materials provided with the distribution.
14287621Smav *
15287621Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16287621Smav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17287621Smav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18287621Smav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19287621Smav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20287621Smav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21287621Smav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22287621Smav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23287621Smav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24287621Smav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25287621Smav */
26287621Smav
27287621Smav#include <sys/cdefs.h>
28287621Smav__FBSDID("$FreeBSD: releng/11.0/sys/cam/ctl/ctl_ha.c 297793 2016-04-10 23:07:00Z pfg $");
29287621Smav
30287621Smav#include <sys/param.h>
31287621Smav#include <sys/systm.h>
32287621Smav#include <sys/kernel.h>
33287621Smav#include <sys/kthread.h>
34287621Smav#include <sys/types.h>
35287621Smav#include <sys/limits.h>
36287621Smav#include <sys/lock.h>
37287621Smav#include <sys/module.h>
38287621Smav#include <sys/mutex.h>
39287621Smav#include <sys/condvar.h>
40287621Smav#include <sys/malloc.h>
41287621Smav#include <sys/mbuf.h>
42287621Smav#include <sys/proc.h>
43287621Smav#include <sys/conf.h>
44287621Smav#include <sys/queue.h>
45287621Smav#include <sys/sysctl.h>
46287621Smav#include <sys/socket.h>
47287621Smav#include <sys/socketvar.h>
48287621Smav#include <sys/uio.h>
49287621Smav#include <netinet/in.h>
50287621Smav#include <netinet/tcp.h>
51287621Smav#include <vm/uma.h>
52287621Smav
53287621Smav#include <cam/cam.h>
54287621Smav#include <cam/scsi/scsi_all.h>
55287621Smav#include <cam/scsi/scsi_da.h>
56287621Smav#include <cam/ctl/ctl_io.h>
57287621Smav#include <cam/ctl/ctl.h>
58287621Smav#include <cam/ctl/ctl_frontend.h>
59287621Smav#include <cam/ctl/ctl_util.h>
60287621Smav#include <cam/ctl/ctl_backend.h>
61287621Smav#include <cam/ctl/ctl_ioctl.h>
62287621Smav#include <cam/ctl/ctl_ha.h>
63287621Smav#include <cam/ctl/ctl_private.h>
64287621Smav#include <cam/ctl/ctl_debug.h>
65287621Smav#include <cam/ctl/ctl_error.h>
66287621Smav
67287621Smav#if (__FreeBSD_version < 1100000)
68287621Smavstruct mbufq {
69287621Smav	struct mbuf *head;
70287621Smav	struct mbuf *tail;
71287621Smav};
72287621Smav
73287621Smavstatic void
74287621Smavmbufq_init(struct mbufq *q, int limit)
75287621Smav{
76287621Smav
77287621Smav	q->head = q->tail = NULL;
78287621Smav}
79287621Smav
80287621Smavstatic void
81287621Smavmbufq_drain(struct mbufq *q)
82287621Smav{
83287621Smav	struct mbuf *m;
84287621Smav
85287621Smav	while ((m = q->head) != NULL) {
86287621Smav		q->head = m->m_nextpkt;
87287621Smav		m_freem(m);
88287621Smav	}
89287621Smav	q->tail = NULL;
90287621Smav}
91287621Smav
92287621Smavstatic struct mbuf *
93287621Smavmbufq_dequeue(struct mbufq *q)
94287621Smav{
95287621Smav	struct mbuf *m;
96287621Smav
97287621Smav	m = q->head;
98287621Smav	if (m) {
99287621Smav		if (q->tail == m)
100287621Smav			q->tail = NULL;
101287621Smav		q->head = m->m_nextpkt;
102287621Smav		m->m_nextpkt = NULL;
103287621Smav	}
104287621Smav	return (m);
105287621Smav}
106287621Smav
107287621Smavstatic void
108287621Smavmbufq_enqueue(struct mbufq *q, struct mbuf *m)
109287621Smav{
110287621Smav
111287621Smav	m->m_nextpkt = NULL;
112287621Smav	if (q->tail)
113287621Smav		q->tail->m_nextpkt = m;
114287621Smav	else
115287621Smav		q->head = m;
116287621Smav	q->tail = m;
117287621Smav}
118287621Smav
119287621Smavstatic u_int
120287621Smavsbavail(struct sockbuf *sb)
121287621Smav{
122287621Smav	return (sb->sb_cc);
123287621Smav}
124287621Smav
125287621Smav#if (__FreeBSD_version < 1000000)
126287621Smav#define	mtodo(m, o)	((void *)(((m)->m_data) + (o)))
127287621Smav#endif
128287621Smav#endif
129287621Smav
130287621Smavstruct ha_msg_wire {
131287621Smav	uint32_t	 channel;
132287621Smav	uint32_t	 length;
133287621Smav};
134287621Smav
135287621Smavstruct ha_dt_msg_wire {
136287621Smav	ctl_ha_dt_cmd	command;
137287621Smav	uint32_t	size;
138287621Smav	uint8_t		*local;
139287621Smav	uint8_t		*remote;
140287621Smav};
141287621Smav
142287621Smavstruct ha_softc {
143287621Smav	struct ctl_softc *ha_ctl_softc;
144287621Smav	ctl_evt_handler	 ha_handler[CTL_HA_CHAN_MAX];
145287621Smav	char		 ha_peer[128];
146287621Smav	struct sockaddr_in  ha_peer_in;
147287621Smav	struct socket	*ha_lso;
148287621Smav	struct socket	*ha_so;
149287621Smav	struct mbufq	 ha_sendq;
150287621Smav	struct mbuf	*ha_sending;
151287621Smav	struct mtx	 ha_lock;
152287621Smav	int		 ha_connect;
153287621Smav	int		 ha_listen;
154287621Smav	int		 ha_connected;
155287621Smav	int		 ha_receiving;
156287621Smav	int		 ha_wakeup;
157287621Smav	int		 ha_disconnect;
158287957Smav	int		 ha_shutdown;
159287957Smav	eventhandler_tag ha_shutdown_eh;
160287621Smav	TAILQ_HEAD(, ctl_ha_dt_req) ha_dts;
161287621Smav} ha_softc;
162287621Smav
163287621Smavstatic void
164287621Smavctl_ha_conn_wake(struct ha_softc *softc)
165287621Smav{
166287621Smav
167287621Smav	mtx_lock(&softc->ha_lock);
168287621Smav	softc->ha_wakeup = 1;
169287621Smav	mtx_unlock(&softc->ha_lock);
170287621Smav	wakeup(&softc->ha_wakeup);
171287621Smav}
172287621Smav
173287621Smavstatic int
174287621Smavctl_ha_lupcall(struct socket *so, void *arg, int waitflag)
175287621Smav{
176287621Smav	struct ha_softc *softc = arg;
177287621Smav
178287621Smav	ctl_ha_conn_wake(softc);
179287621Smav	return (SU_OK);
180287621Smav}
181287621Smav
182287621Smavstatic int
183287621Smavctl_ha_rupcall(struct socket *so, void *arg, int waitflag)
184287621Smav{
185287621Smav	struct ha_softc *softc = arg;
186287621Smav
187287621Smav	wakeup(&softc->ha_receiving);
188287621Smav	return (SU_OK);
189287621Smav}
190287621Smav
191287621Smavstatic int
192287621Smavctl_ha_supcall(struct socket *so, void *arg, int waitflag)
193287621Smav{
194287621Smav	struct ha_softc *softc = arg;
195287621Smav
196287621Smav	ctl_ha_conn_wake(softc);
197287621Smav	return (SU_OK);
198287621Smav}
199287621Smav
200287621Smavstatic void
201287621Smavctl_ha_evt(struct ha_softc *softc, ctl_ha_channel ch, ctl_ha_event evt,
202287621Smav    int param)
203287621Smav{
204287621Smav	int i;
205287621Smav
206287621Smav	if (ch < CTL_HA_CHAN_MAX) {
207287621Smav		if (softc->ha_handler[ch])
208287621Smav			softc->ha_handler[ch](ch, evt, param);
209287621Smav		return;
210287621Smav	}
211287621Smav	for (i = 0; i < CTL_HA_CHAN_MAX; i++) {
212287621Smav		if (softc->ha_handler[i])
213287621Smav			softc->ha_handler[i](i, evt, param);
214287621Smav	}
215287621Smav}
216287621Smav
217287621Smavstatic void
218287621Smavctl_ha_close(struct ha_softc *softc)
219287621Smav{
220287621Smav	struct socket *so = softc->ha_so;
221287621Smav	int report = 0;
222287621Smav
223287621Smav	if (softc->ha_connected || softc->ha_disconnect) {
224287621Smav		softc->ha_connected = 0;
225287621Smav		mbufq_drain(&softc->ha_sendq);
226287621Smav		m_freem(softc->ha_sending);
227287621Smav		softc->ha_sending = NULL;
228287621Smav		report = 1;
229287621Smav	}
230287621Smav	if (so) {
231287621Smav		SOCKBUF_LOCK(&so->so_rcv);
232287621Smav		soupcall_clear(so, SO_RCV);
233287621Smav		while (softc->ha_receiving) {
234287621Smav			wakeup(&softc->ha_receiving);
235287621Smav			msleep(&softc->ha_receiving, SOCKBUF_MTX(&so->so_rcv),
236287621Smav			    0, "ha_rx exit", 0);
237287621Smav		}
238287621Smav		SOCKBUF_UNLOCK(&so->so_rcv);
239287621Smav		SOCKBUF_LOCK(&so->so_snd);
240287621Smav		soupcall_clear(so, SO_SND);
241287621Smav		SOCKBUF_UNLOCK(&so->so_snd);
242287621Smav		softc->ha_so = NULL;
243287621Smav		if (softc->ha_connect)
244287621Smav			pause("reconnect", hz / 2);
245287621Smav		soclose(so);
246287621Smav	}
247287621Smav	if (report) {
248287621Smav		ctl_ha_evt(softc, CTL_HA_CHAN_MAX, CTL_HA_EVT_LINK_CHANGE,
249287621Smav		    (softc->ha_connect || softc->ha_listen) ?
250287621Smav		    CTL_HA_LINK_UNKNOWN : CTL_HA_LINK_OFFLINE);
251287621Smav	}
252287621Smav}
253287621Smav
254287621Smavstatic void
255287621Smavctl_ha_lclose(struct ha_softc *softc)
256287621Smav{
257287621Smav
258287621Smav	if (softc->ha_lso) {
259287621Smav		SOCKBUF_LOCK(&softc->ha_lso->so_rcv);
260287621Smav		soupcall_clear(softc->ha_lso, SO_RCV);
261287621Smav		SOCKBUF_UNLOCK(&softc->ha_lso->so_rcv);
262287621Smav		soclose(softc->ha_lso);
263287621Smav		softc->ha_lso = NULL;
264287621Smav	}
265287621Smav}
266287621Smav
267287621Smavstatic void
268287621Smavctl_ha_rx_thread(void *arg)
269287621Smav{
270287621Smav	struct ha_softc *softc = arg;
271287621Smav	struct socket *so = softc->ha_so;
272287621Smav	struct ha_msg_wire wire_hdr;
273287621Smav	struct uio uio;
274287621Smav	struct iovec iov;
275287621Smav	int error, flags, next;
276287621Smav
277287621Smav	bzero(&wire_hdr, sizeof(wire_hdr));
278287621Smav	while (1) {
279287621Smav		if (wire_hdr.length > 0)
280287621Smav			next = wire_hdr.length;
281287621Smav		else
282287621Smav			next = sizeof(wire_hdr);
283287621Smav		SOCKBUF_LOCK(&so->so_rcv);
284288146Smav		while (sbavail(&so->so_rcv) < next || softc->ha_disconnect) {
285288146Smav			if (softc->ha_connected == 0 || softc->ha_disconnect ||
286288146Smav			    so->so_error ||
287287621Smav			    (so->so_rcv.sb_state & SBS_CANTRCVMORE)) {
288287621Smav				goto errout;
289287621Smav			}
290287621Smav			so->so_rcv.sb_lowat = next;
291287621Smav			msleep(&softc->ha_receiving, SOCKBUF_MTX(&so->so_rcv),
292287621Smav			    0, "-", 0);
293287621Smav		}
294287621Smav		SOCKBUF_UNLOCK(&so->so_rcv);
295287621Smav
296287621Smav		if (wire_hdr.length == 0) {
297287621Smav			iov.iov_base = &wire_hdr;
298287621Smav			iov.iov_len = sizeof(wire_hdr);
299287621Smav			uio.uio_iov = &iov;
300287621Smav			uio.uio_iovcnt = 1;
301287621Smav			uio.uio_rw = UIO_READ;
302287621Smav			uio.uio_segflg = UIO_SYSSPACE;
303287621Smav			uio.uio_td = curthread;
304287621Smav			uio.uio_resid = sizeof(wire_hdr);
305287621Smav			flags = MSG_DONTWAIT;
306287621Smav			error = soreceive(softc->ha_so, NULL, &uio, NULL,
307287621Smav			    NULL, &flags);
308287621Smav			if (error != 0) {
309287621Smav				printf("%s: header receive error %d\n",
310287621Smav				    __func__, error);
311287621Smav				SOCKBUF_LOCK(&so->so_rcv);
312287621Smav				goto errout;
313287621Smav			}
314287621Smav		} else {
315287621Smav			ctl_ha_evt(softc, wire_hdr.channel,
316287621Smav			    CTL_HA_EVT_MSG_RECV, wire_hdr.length);
317287621Smav			wire_hdr.length = 0;
318287621Smav		}
319287621Smav	}
320287621Smav
321287621Smaverrout:
322287621Smav	softc->ha_receiving = 0;
323287621Smav	wakeup(&softc->ha_receiving);
324287621Smav	SOCKBUF_UNLOCK(&so->so_rcv);
325287621Smav	ctl_ha_conn_wake(softc);
326287621Smav	kthread_exit();
327287621Smav}
328287621Smav
329287621Smavstatic void
330287621Smavctl_ha_send(struct ha_softc *softc)
331287621Smav{
332287621Smav	struct socket *so = softc->ha_so;
333287621Smav	int error;
334287621Smav
335287621Smav	while (1) {
336287621Smav		if (softc->ha_sending == NULL) {
337287621Smav			mtx_lock(&softc->ha_lock);
338287621Smav			softc->ha_sending = mbufq_dequeue(&softc->ha_sendq);
339287621Smav			mtx_unlock(&softc->ha_lock);
340287621Smav			if (softc->ha_sending == NULL) {
341287621Smav				so->so_snd.sb_lowat = so->so_snd.sb_hiwat + 1;
342287621Smav				break;
343287621Smav			}
344287621Smav		}
345287621Smav		SOCKBUF_LOCK(&so->so_snd);
346287621Smav		if (sbspace(&so->so_snd) < softc->ha_sending->m_pkthdr.len) {
347287621Smav			so->so_snd.sb_lowat = softc->ha_sending->m_pkthdr.len;
348287621Smav			SOCKBUF_UNLOCK(&so->so_snd);
349287621Smav			break;
350287621Smav		}
351287621Smav		SOCKBUF_UNLOCK(&so->so_snd);
352287621Smav		error = sosend(softc->ha_so, NULL, NULL, softc->ha_sending,
353287621Smav		    NULL, MSG_DONTWAIT, curthread);
354287621Smav		softc->ha_sending = NULL;
355287621Smav		if (error != 0) {
356287621Smav			printf("%s: sosend() error %d\n", __func__, error);
357287621Smav			return;
358287621Smav		}
359297793Spfg	}
360287621Smav}
361287621Smav
362287621Smavstatic void
363287621Smavctl_ha_sock_setup(struct ha_softc *softc)
364287621Smav{
365287621Smav	struct sockopt opt;
366287621Smav	struct socket *so = softc->ha_so;
367287621Smav	int error, val;
368287621Smav
369287621Smav	val = 1024 * 1024;
370287621Smav	error = soreserve(so, val, val);
371287621Smav	if (error)
372287621Smav		printf("%s: soreserve failed %d\n", __func__, error);
373287621Smav
374287621Smav	SOCKBUF_LOCK(&so->so_rcv);
375287621Smav	so->so_rcv.sb_lowat = sizeof(struct ha_msg_wire);
376287621Smav	soupcall_set(so, SO_RCV, ctl_ha_rupcall, softc);
377287621Smav	SOCKBUF_UNLOCK(&so->so_rcv);
378287621Smav	SOCKBUF_LOCK(&so->so_snd);
379287621Smav	so->so_snd.sb_lowat = sizeof(struct ha_msg_wire);
380287621Smav	soupcall_set(so, SO_SND, ctl_ha_supcall, softc);
381287621Smav	SOCKBUF_UNLOCK(&so->so_snd);
382287621Smav
383287621Smav	bzero(&opt, sizeof(struct sockopt));
384287621Smav	opt.sopt_dir = SOPT_SET;
385287621Smav	opt.sopt_level = SOL_SOCKET;
386287621Smav	opt.sopt_name = SO_KEEPALIVE;
387287621Smav	opt.sopt_val = &val;
388287621Smav	opt.sopt_valsize = sizeof(val);
389287621Smav	val = 1;
390287621Smav	error = sosetopt(so, &opt);
391287621Smav	if (error)
392287621Smav		printf("%s: KEEPALIVE setting failed %d\n", __func__, error);
393287621Smav
394287621Smav	opt.sopt_level = IPPROTO_TCP;
395287621Smav	opt.sopt_name = TCP_NODELAY;
396287621Smav	val = 1;
397287621Smav	error = sosetopt(so, &opt);
398287621Smav	if (error)
399287621Smav		printf("%s: NODELAY setting failed %d\n", __func__, error);
400287621Smav
401287621Smav	opt.sopt_name = TCP_KEEPINIT;
402287621Smav	val = 3;
403287621Smav	error = sosetopt(so, &opt);
404287621Smav	if (error)
405287621Smav		printf("%s: KEEPINIT setting failed %d\n", __func__, error);
406287621Smav
407287621Smav	opt.sopt_name = TCP_KEEPIDLE;
408287621Smav	val = 1;
409287621Smav	error = sosetopt(so, &opt);
410287621Smav	if (error)
411287621Smav		printf("%s: KEEPIDLE setting failed %d\n", __func__, error);
412287621Smav
413287621Smav	opt.sopt_name = TCP_KEEPINTVL;
414287621Smav	val = 1;
415287621Smav	error = sosetopt(so, &opt);
416287621Smav	if (error)
417287621Smav		printf("%s: KEEPINTVL setting failed %d\n", __func__, error);
418287621Smav
419287621Smav	opt.sopt_name = TCP_KEEPCNT;
420287621Smav	val = 5;
421287621Smav	error = sosetopt(so, &opt);
422287621Smav	if (error)
423287621Smav		printf("%s: KEEPCNT setting failed %d\n", __func__, error);
424287621Smav}
425287621Smav
426287621Smavstatic int
427287621Smavctl_ha_connect(struct ha_softc *softc)
428287621Smav{
429287621Smav	struct thread *td = curthread;
430288247Smav	struct sockaddr_in sa;
431287621Smav	struct socket *so;
432287621Smav	int error;
433287621Smav
434287621Smav	/* Create the socket */
435287621Smav	error = socreate(PF_INET, &so, SOCK_STREAM,
436287621Smav	    IPPROTO_TCP, td->td_ucred, td);
437287621Smav	if (error != 0) {
438287621Smav		printf("%s: socreate() error %d\n", __func__, error);
439287621Smav		return (error);
440287621Smav	}
441287621Smav	softc->ha_so = so;
442287621Smav	ctl_ha_sock_setup(softc);
443287621Smav
444288247Smav	memcpy(&sa, &softc->ha_peer_in, sizeof(sa));
445288247Smav	error = soconnect(so, (struct sockaddr *)&sa, td);
446294558Smav	if (error != 0 && bootverbose) {
447287621Smav		printf("%s: soconnect() error %d\n", __func__, error);
448287621Smav		goto out;
449287621Smav	}
450287621Smav	return (0);
451287621Smav
452287621Smavout:
453287621Smav	ctl_ha_close(softc);
454287621Smav	return (error);
455287621Smav}
456287621Smav
457287621Smavstatic int
458287621Smavctl_ha_accept(struct ha_softc *softc)
459287621Smav{
460287621Smav	struct socket *so;
461287621Smav	struct sockaddr *sap;
462287621Smav	int error;
463287621Smav
464287621Smav	ACCEPT_LOCK();
465287621Smav	if (softc->ha_lso->so_rcv.sb_state & SBS_CANTRCVMORE)
466287621Smav		softc->ha_lso->so_error = ECONNABORTED;
467287621Smav	if (softc->ha_lso->so_error) {
468287621Smav		error = softc->ha_lso->so_error;
469287621Smav		softc->ha_lso->so_error = 0;
470287621Smav		ACCEPT_UNLOCK();
471287621Smav		printf("%s: socket error %d\n", __func__, error);
472287621Smav		goto out;
473287621Smav	}
474287621Smav	so = TAILQ_FIRST(&softc->ha_lso->so_comp);
475287621Smav	if (so == NULL) {
476287621Smav		ACCEPT_UNLOCK();
477287621Smav		return (EWOULDBLOCK);
478287621Smav	}
479287621Smav	KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP"));
480287621Smav	KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP"));
481287621Smav
482287621Smav	/*
483287621Smav	 * Before changing the flags on the socket, we have to bump the
484287621Smav	 * reference count.  Otherwise, if the protocol calls sofree(),
485287621Smav	 * the socket will be released due to a zero refcount.
486287621Smav	 */
487287621Smav	SOCK_LOCK(so);			/* soref() and so_state update */
488287621Smav	soref(so);			/* file descriptor reference */
489287621Smav
490287621Smav	TAILQ_REMOVE(&softc->ha_lso->so_comp, so, so_list);
491287621Smav	softc->ha_lso->so_qlen--;
492287621Smav	so->so_state |= SS_NBIO;
493287621Smav	so->so_qstate &= ~SQ_COMP;
494287621Smav	so->so_head = NULL;
495287621Smav
496287621Smav	SOCK_UNLOCK(so);
497287621Smav	ACCEPT_UNLOCK();
498287621Smav
499287621Smav	sap = NULL;
500287621Smav	error = soaccept(so, &sap);
501287621Smav	if (error != 0) {
502287621Smav		printf("%s: soaccept() error %d\n", __func__, error);
503287621Smav		if (sap != NULL)
504287621Smav			free(sap, M_SONAME);
505287621Smav		goto out;
506287621Smav	}
507287621Smav	if (sap != NULL)
508287621Smav		free(sap, M_SONAME);
509287621Smav	softc->ha_so = so;
510287621Smav	ctl_ha_sock_setup(softc);
511287621Smav	return (0);
512287621Smav
513287621Smavout:
514287621Smav	ctl_ha_lclose(softc);
515287621Smav	return (error);
516287621Smav}
517287621Smav
518287621Smavstatic int
519287621Smavctl_ha_listen(struct ha_softc *softc)
520287621Smav{
521287621Smav	struct thread *td = curthread;
522288247Smav	struct sockaddr_in sa;
523287621Smav	struct sockopt opt;
524287621Smav	int error, val;
525287621Smav
526287621Smav	/* Create the socket */
527287621Smav	if (softc->ha_lso == NULL) {
528287621Smav		error = socreate(PF_INET, &softc->ha_lso, SOCK_STREAM,
529287621Smav		    IPPROTO_TCP, td->td_ucred, td);
530287621Smav		if (error != 0) {
531287621Smav			printf("%s: socreate() error %d\n", __func__, error);
532287621Smav			return (error);
533287621Smav		}
534287621Smav		bzero(&opt, sizeof(struct sockopt));
535287621Smav		opt.sopt_dir = SOPT_SET;
536287621Smav		opt.sopt_level = SOL_SOCKET;
537287621Smav		opt.sopt_name = SO_REUSEADDR;
538287621Smav		opt.sopt_val = &val;
539287621Smav		opt.sopt_valsize = sizeof(val);
540287621Smav		val = 1;
541287621Smav		error = sosetopt(softc->ha_lso, &opt);
542287621Smav		if (error) {
543287621Smav			printf("%s: REUSEADDR setting failed %d\n",
544287621Smav			    __func__, error);
545287621Smav		}
546288146Smav		bzero(&opt, sizeof(struct sockopt));
547288146Smav		opt.sopt_dir = SOPT_SET;
548288146Smav		opt.sopt_level = SOL_SOCKET;
549288146Smav		opt.sopt_name = SO_REUSEPORT;
550288146Smav		opt.sopt_val = &val;
551288146Smav		opt.sopt_valsize = sizeof(val);
552288146Smav		val = 1;
553288146Smav		error = sosetopt(softc->ha_lso, &opt);
554288146Smav		if (error) {
555288146Smav			printf("%s: REUSEPORT setting failed %d\n",
556288146Smav			    __func__, error);
557288146Smav		}
558287621Smav		SOCKBUF_LOCK(&softc->ha_lso->so_rcv);
559287621Smav		soupcall_set(softc->ha_lso, SO_RCV, ctl_ha_lupcall, softc);
560287621Smav		SOCKBUF_UNLOCK(&softc->ha_lso->so_rcv);
561287621Smav	}
562287621Smav
563288247Smav	memcpy(&sa, &softc->ha_peer_in, sizeof(sa));
564288247Smav	error = sobind(softc->ha_lso, (struct sockaddr *)&sa, td);
565287621Smav	if (error != 0) {
566287621Smav		printf("%s: sobind() error %d\n", __func__, error);
567287621Smav		goto out;
568287621Smav	}
569287621Smav	error = solisten(softc->ha_lso, 1, td);
570287621Smav	if (error != 0) {
571287621Smav		printf("%s: solisten() error %d\n", __func__, error);
572287621Smav		goto out;
573287621Smav	}
574287621Smav	return (0);
575287621Smav
576287621Smavout:
577287621Smav	ctl_ha_lclose(softc);
578287621Smav	return (error);
579287621Smav}
580287621Smav
581287621Smavstatic void
582287621Smavctl_ha_conn_thread(void *arg)
583287621Smav{
584287621Smav	struct ha_softc *softc = arg;
585287621Smav	int error;
586287621Smav
587287621Smav	while (1) {
588287957Smav		if (softc->ha_disconnect || softc->ha_shutdown) {
589287621Smav			ctl_ha_close(softc);
590288146Smav			if (softc->ha_disconnect == 2 || softc->ha_shutdown)
591288146Smav				ctl_ha_lclose(softc);
592287621Smav			softc->ha_disconnect = 0;
593287957Smav			if (softc->ha_shutdown)
594287957Smav				break;
595287621Smav		} else if (softc->ha_so != NULL &&
596287621Smav		    (softc->ha_so->so_error ||
597287621Smav		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
598287621Smav			ctl_ha_close(softc);
599287621Smav		if (softc->ha_so == NULL) {
600287621Smav			if (softc->ha_lso != NULL)
601287621Smav				ctl_ha_accept(softc);
602287621Smav			else if (softc->ha_listen)
603287621Smav				ctl_ha_listen(softc);
604287621Smav			else if (softc->ha_connect)
605287621Smav				ctl_ha_connect(softc);
606287621Smav		}
607287621Smav		if (softc->ha_so != NULL) {
608287621Smav			if (softc->ha_connected == 0 &&
609287621Smav			    softc->ha_so->so_error == 0 &&
610287621Smav			    (softc->ha_so->so_state & SS_ISCONNECTING) == 0) {
611287621Smav				softc->ha_connected = 1;
612287621Smav				ctl_ha_evt(softc, CTL_HA_CHAN_MAX,
613287621Smav				    CTL_HA_EVT_LINK_CHANGE,
614287621Smav				    CTL_HA_LINK_ONLINE);
615287621Smav				softc->ha_receiving = 1;
616287621Smav				error = kproc_kthread_add(ctl_ha_rx_thread,
617287621Smav				    softc, &softc->ha_ctl_softc->ctl_proc,
618287621Smav				    NULL, 0, 0, "ctl", "ha_rx");
619287621Smav				if (error != 0) {
620287621Smav					printf("Error creating CTL HA rx thread!\n");
621287621Smav					softc->ha_receiving = 0;
622287621Smav					softc->ha_disconnect = 1;
623287621Smav				}
624287621Smav			}
625287621Smav			ctl_ha_send(softc);
626287621Smav		}
627287621Smav		mtx_lock(&softc->ha_lock);
628287621Smav		if (softc->ha_so != NULL &&
629287621Smav		    (softc->ha_so->so_error ||
630287621Smav		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
631287621Smav			;
632287621Smav		else if (!softc->ha_wakeup)
633287621Smav			msleep(&softc->ha_wakeup, &softc->ha_lock, 0, "-", hz);
634287621Smav		softc->ha_wakeup = 0;
635287621Smav		mtx_unlock(&softc->ha_lock);
636287621Smav	}
637287957Smav	mtx_lock(&softc->ha_lock);
638287957Smav	softc->ha_shutdown = 2;
639287957Smav	wakeup(&softc->ha_wakeup);
640287957Smav	mtx_unlock(&softc->ha_lock);
641287957Smav	kthread_exit();
642287621Smav}
643287621Smav
644287621Smavstatic int
645287621Smavctl_ha_peer_sysctl(SYSCTL_HANDLER_ARGS)
646287621Smav{
647287621Smav	struct ha_softc *softc = (struct ha_softc *)arg1;
648287621Smav	struct sockaddr_in *sa;
649287621Smav	int error, b1, b2, b3, b4, p, num;
650287855Smav	char buf[128];
651287621Smav
652287855Smav	strlcpy(buf, softc->ha_peer, sizeof(buf));
653287855Smav	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
654287855Smav	if ((error != 0) || (req->newptr == NULL) ||
655287855Smav	    strncmp(buf, softc->ha_peer, sizeof(buf)) == 0)
656287621Smav		return (error);
657287621Smav
658287621Smav	sa = &softc->ha_peer_in;
659287621Smav	mtx_lock(&softc->ha_lock);
660287855Smav	if ((num = sscanf(buf, "connect %d.%d.%d.%d:%d",
661287621Smav	    &b1, &b2, &b3, &b4, &p)) >= 4) {
662287621Smav		softc->ha_connect = 1;
663287621Smav		softc->ha_listen = 0;
664287855Smav	} else if ((num = sscanf(buf, "listen %d.%d.%d.%d:%d",
665287621Smav	    &b1, &b2, &b3, &b4, &p)) >= 4) {
666287621Smav		softc->ha_connect = 0;
667287621Smav		softc->ha_listen = 1;
668287621Smav	} else {
669287621Smav		softc->ha_connect = 0;
670287621Smav		softc->ha_listen = 0;
671287855Smav		if (buf[0] != 0) {
672287855Smav			buf[0] = 0;
673287621Smav			error = EINVAL;
674287855Smav		}
675287621Smav	}
676287855Smav	strlcpy(softc->ha_peer, buf, sizeof(softc->ha_peer));
677287621Smav	if (softc->ha_connect || softc->ha_listen) {
678287621Smav		memset(sa, 0, sizeof(*sa));
679287621Smav		sa->sin_len = sizeof(struct sockaddr_in);
680287621Smav		sa->sin_family = AF_INET;
681287621Smav		sa->sin_port = htons((num >= 5) ? p : 999);
682287621Smav		sa->sin_addr.s_addr =
683287621Smav		    htonl((b1 << 24) + (b2 << 16) + (b3 << 8) + b4);
684287621Smav	}
685288146Smav	softc->ha_disconnect = 2;
686287621Smav	softc->ha_wakeup = 1;
687287621Smav	mtx_unlock(&softc->ha_lock);
688287621Smav	wakeup(&softc->ha_wakeup);
689287621Smav	return (error);
690287621Smav}
691287621Smav
692287621Smavctl_ha_status
693287621Smavctl_ha_msg_register(ctl_ha_channel channel, ctl_evt_handler handler)
694287621Smav{
695287621Smav	struct ha_softc *softc = &ha_softc;
696287621Smav
697287621Smav	KASSERT(channel < CTL_HA_CHAN_MAX,
698287621Smav	    ("Wrong CTL HA channel %d", channel));
699287621Smav	softc->ha_handler[channel] = handler;
700287621Smav	return (CTL_HA_STATUS_SUCCESS);
701287621Smav}
702287621Smav
703287621Smavctl_ha_status
704287621Smavctl_ha_msg_deregister(ctl_ha_channel channel)
705287621Smav{
706287621Smav	struct ha_softc *softc = &ha_softc;
707287621Smav
708287621Smav	KASSERT(channel < CTL_HA_CHAN_MAX,
709287621Smav	    ("Wrong CTL HA channel %d", channel));
710287621Smav	softc->ha_handler[channel] = NULL;
711287621Smav	return (CTL_HA_STATUS_SUCCESS);
712287621Smav}
713287621Smav
714287621Smav/*
715287621Smav * Receive a message of the specified size.
716287621Smav */
717287621Smavctl_ha_status
718287621Smavctl_ha_msg_recv(ctl_ha_channel channel, void *addr, size_t len,
719287621Smav		int wait)
720287621Smav{
721287621Smav	struct ha_softc *softc = &ha_softc;
722287621Smav	struct uio uio;
723287621Smav	struct iovec iov;
724287621Smav	int error, flags;
725287621Smav
726287621Smav	if (!softc->ha_connected)
727287621Smav		return (CTL_HA_STATUS_DISCONNECT);
728287621Smav
729287621Smav	iov.iov_base = addr;
730287621Smav	iov.iov_len = len;
731287621Smav	uio.uio_iov = &iov;
732287621Smav	uio.uio_iovcnt = 1;
733287621Smav	uio.uio_rw = UIO_READ;
734287621Smav	uio.uio_segflg = UIO_SYSSPACE;
735287621Smav	uio.uio_td = curthread;
736287621Smav	uio.uio_resid = len;
737287621Smav	flags = wait ? 0 : MSG_DONTWAIT;
738287621Smav	error = soreceive(softc->ha_so, NULL, &uio, NULL, NULL, &flags);
739287621Smav	if (error == 0)
740287621Smav		return (CTL_HA_STATUS_SUCCESS);
741287621Smav
742287621Smav	/* Consider all errors fatal for HA sanity. */
743287621Smav	mtx_lock(&softc->ha_lock);
744287621Smav	if (softc->ha_connected) {
745287621Smav		softc->ha_disconnect = 1;
746287621Smav		softc->ha_wakeup = 1;
747287621Smav		wakeup(&softc->ha_wakeup);
748287621Smav	}
749287621Smav	mtx_unlock(&softc->ha_lock);
750287621Smav	return (CTL_HA_STATUS_ERROR);
751287621Smav}
752287621Smav
753287621Smav/*
754287621Smav * Send a message of the specified size.
755287621Smav */
756287621Smavctl_ha_status
757287621Smavctl_ha_msg_send2(ctl_ha_channel channel, const void *addr, size_t len,
758287621Smav    const void *addr2, size_t len2, int wait)
759287621Smav{
760287621Smav	struct ha_softc *softc = &ha_softc;
761287621Smav	struct mbuf *mb, *newmb;
762287621Smav	struct ha_msg_wire hdr;
763287621Smav	size_t copylen, off;
764287621Smav
765287621Smav	if (!softc->ha_connected)
766287621Smav		return (CTL_HA_STATUS_DISCONNECT);
767287621Smav
768287621Smav	newmb = m_getm2(NULL, sizeof(hdr) + len + len2, wait, MT_DATA,
769287621Smav	    M_PKTHDR);
770287621Smav	if (newmb == NULL) {
771287621Smav		/* Consider all errors fatal for HA sanity. */
772287621Smav		mtx_lock(&softc->ha_lock);
773287621Smav		if (softc->ha_connected) {
774287621Smav			softc->ha_disconnect = 1;
775287621Smav			softc->ha_wakeup = 1;
776287621Smav			wakeup(&softc->ha_wakeup);
777287621Smav		}
778287621Smav		mtx_unlock(&softc->ha_lock);
779287621Smav		printf("%s: Can't allocate mbuf chain\n", __func__);
780287621Smav		return (CTL_HA_STATUS_ERROR);
781287621Smav	}
782287621Smav	hdr.channel = channel;
783287621Smav	hdr.length = len + len2;
784287621Smav	mb = newmb;
785287621Smav	memcpy(mtodo(mb, 0), &hdr, sizeof(hdr));
786287621Smav	mb->m_len += sizeof(hdr);
787287621Smav	off = 0;
788287621Smav	for (; mb != NULL && off < len; mb = mb->m_next) {
789287621Smav		copylen = min(M_TRAILINGSPACE(mb), len - off);
790287621Smav		memcpy(mtodo(mb, mb->m_len), (const char *)addr + off, copylen);
791287621Smav		mb->m_len += copylen;
792287621Smav		off += copylen;
793287621Smav		if (off == len)
794287621Smav			break;
795287621Smav	}
796287621Smav	KASSERT(off == len, ("%s: off (%zu) != len (%zu)", __func__,
797287621Smav	    off, len));
798287621Smav	off = 0;
799287621Smav	for (; mb != NULL && off < len2; mb = mb->m_next) {
800287621Smav		copylen = min(M_TRAILINGSPACE(mb), len2 - off);
801287621Smav		memcpy(mtodo(mb, mb->m_len), (const char *)addr2 + off, copylen);
802287621Smav		mb->m_len += copylen;
803287621Smav		off += copylen;
804287621Smav	}
805287621Smav	KASSERT(off == len2, ("%s: off (%zu) != len2 (%zu)", __func__,
806287621Smav	    off, len2));
807287621Smav	newmb->m_pkthdr.len = sizeof(hdr) + len + len2;
808287621Smav
809287621Smav	mtx_lock(&softc->ha_lock);
810287621Smav	if (!softc->ha_connected) {
811287621Smav		mtx_unlock(&softc->ha_lock);
812287621Smav		m_freem(newmb);
813287621Smav		return (CTL_HA_STATUS_DISCONNECT);
814287621Smav	}
815287621Smav	mbufq_enqueue(&softc->ha_sendq, newmb);
816287621Smav	softc->ha_wakeup = 1;
817287621Smav	mtx_unlock(&softc->ha_lock);
818287621Smav	wakeup(&softc->ha_wakeup);
819287621Smav	return (CTL_HA_STATUS_SUCCESS);
820287621Smav}
821287621Smav
822287621Smavctl_ha_status
823287621Smavctl_ha_msg_send(ctl_ha_channel channel, const void *addr, size_t len,
824287621Smav    int wait)
825287621Smav{
826287621Smav
827287621Smav	return (ctl_ha_msg_send2(channel, addr, len, NULL, 0, wait));
828287621Smav}
829287621Smav
830288146Smavctl_ha_status
831288146Smavctl_ha_msg_abort(ctl_ha_channel channel)
832288146Smav{
833288146Smav	struct ha_softc *softc = &ha_softc;
834288146Smav
835288146Smav	mtx_lock(&softc->ha_lock);
836288146Smav	softc->ha_disconnect = 1;
837288146Smav	softc->ha_wakeup = 1;
838288146Smav	mtx_unlock(&softc->ha_lock);
839288146Smav	wakeup(&softc->ha_wakeup);
840288146Smav	return (CTL_HA_STATUS_SUCCESS);
841288146Smav}
842288146Smav
843287621Smav/*
844287621Smav * Allocate a data transfer request structure.
845287621Smav */
846287621Smavstruct ctl_ha_dt_req *
847287621Smavctl_dt_req_alloc(void)
848287621Smav{
849287621Smav
850287621Smav	return (malloc(sizeof(struct ctl_ha_dt_req), M_CTL, M_WAITOK | M_ZERO));
851287621Smav}
852287621Smav
853287621Smav/*
854287621Smav * Free a data transfer request structure.
855287621Smav */
856287621Smavvoid
857287621Smavctl_dt_req_free(struct ctl_ha_dt_req *req)
858287621Smav{
859287621Smav
860287621Smav	free(req, M_CTL);
861287621Smav}
862287621Smav
863287621Smav/*
864287621Smav * Issue a DMA request for a single buffer.
865287621Smav */
866287621Smavctl_ha_status
867287621Smavctl_dt_single(struct ctl_ha_dt_req *req)
868287621Smav{
869287621Smav	struct ha_softc *softc = &ha_softc;
870287621Smav	struct ha_dt_msg_wire wire_dt;
871287621Smav	ctl_ha_status status;
872287621Smav
873287621Smav	wire_dt.command = req->command;
874287621Smav	wire_dt.size = req->size;
875287621Smav	wire_dt.local = req->local;
876287621Smav	wire_dt.remote = req->remote;
877287621Smav	if (req->command == CTL_HA_DT_CMD_READ && req->callback != NULL) {
878287621Smav		mtx_lock(&softc->ha_lock);
879287621Smav		TAILQ_INSERT_TAIL(&softc->ha_dts, req, links);
880287621Smav		mtx_unlock(&softc->ha_lock);
881287621Smav		ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt, sizeof(wire_dt),
882287621Smav		    M_WAITOK);
883287621Smav		return (CTL_HA_STATUS_WAIT);
884287621Smav	}
885287621Smav	if (req->command == CTL_HA_DT_CMD_READ) {
886287621Smav		status = ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt,
887287621Smav		    sizeof(wire_dt), M_WAITOK);
888287621Smav	} else {
889287621Smav		status = ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
890287621Smav		    sizeof(wire_dt), req->local, req->size, M_WAITOK);
891287621Smav	}
892287621Smav	return (status);
893287621Smav}
894287621Smav
895287621Smavstatic void
896287621Smavctl_dt_event_handler(ctl_ha_channel channel, ctl_ha_event event, int param)
897287621Smav{
898287621Smav	struct ha_softc *softc = &ha_softc;
899287621Smav	struct ctl_ha_dt_req *req;
900287621Smav	ctl_ha_status isc_status;
901287621Smav
902287621Smav	if (event == CTL_HA_EVT_MSG_RECV) {
903287621Smav		struct ha_dt_msg_wire wire_dt;
904287621Smav		uint8_t *tmp;
905287621Smav		int size;
906287621Smav
907287621Smav		size = min(sizeof(wire_dt), param);
908287621Smav		isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA, &wire_dt,
909287621Smav					     size, M_WAITOK);
910287621Smav		if (isc_status != CTL_HA_STATUS_SUCCESS) {
911287621Smav			printf("%s: Error receiving message: %d\n",
912287621Smav			    __func__, isc_status);
913287621Smav			return;
914287621Smav		}
915287621Smav
916287621Smav		if (wire_dt.command == CTL_HA_DT_CMD_READ) {
917287621Smav			wire_dt.command = CTL_HA_DT_CMD_WRITE;
918287621Smav			tmp = wire_dt.local;
919287621Smav			wire_dt.local = wire_dt.remote;
920287621Smav			wire_dt.remote = tmp;
921287621Smav			ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
922287621Smav			    sizeof(wire_dt), wire_dt.local, wire_dt.size,
923287621Smav			    M_WAITOK);
924287621Smav		} else if (wire_dt.command == CTL_HA_DT_CMD_WRITE) {
925287621Smav			isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA,
926287621Smav			    wire_dt.remote, wire_dt.size, M_WAITOK);
927287621Smav			mtx_lock(&softc->ha_lock);
928287621Smav			TAILQ_FOREACH(req, &softc->ha_dts, links) {
929287621Smav				if (req->local == wire_dt.remote) {
930287621Smav					TAILQ_REMOVE(&softc->ha_dts, req, links);
931287621Smav					break;
932287621Smav				}
933287621Smav			}
934287621Smav			mtx_unlock(&softc->ha_lock);
935287621Smav			if (req) {
936287621Smav				req->ret = isc_status;
937287621Smav				req->callback(req);
938287621Smav			}
939287621Smav		}
940287621Smav	} else if (event == CTL_HA_EVT_LINK_CHANGE) {
941287621Smav		CTL_DEBUG_PRINT(("%s: Link state change to %d\n", __func__,
942287621Smav		    param));
943287621Smav		if (param != CTL_HA_LINK_ONLINE) {
944287621Smav			mtx_lock(&softc->ha_lock);
945287621Smav			while ((req = TAILQ_FIRST(&softc->ha_dts)) != NULL) {
946287621Smav				TAILQ_REMOVE(&softc->ha_dts, req, links);
947287621Smav				mtx_unlock(&softc->ha_lock);
948287621Smav				req->ret = CTL_HA_STATUS_DISCONNECT;
949287621Smav				req->callback(req);
950287621Smav				mtx_lock(&softc->ha_lock);
951287621Smav			}
952287621Smav			mtx_unlock(&softc->ha_lock);
953287621Smav		}
954287621Smav	} else {
955287621Smav		printf("%s: Unknown event %d\n", __func__, event);
956287621Smav	}
957287621Smav}
958287621Smav
959287621Smav
960287621Smavctl_ha_status
961287621Smavctl_ha_msg_init(struct ctl_softc *ctl_softc)
962287621Smav{
963287621Smav	struct ha_softc *softc = &ha_softc;
964287621Smav	int error;
965287621Smav
966287621Smav	softc->ha_ctl_softc = ctl_softc;
967287621Smav	mtx_init(&softc->ha_lock, "CTL HA mutex", NULL, MTX_DEF);
968287621Smav	mbufq_init(&softc->ha_sendq, INT_MAX);
969287621Smav	TAILQ_INIT(&softc->ha_dts);
970287621Smav	error = kproc_kthread_add(ctl_ha_conn_thread, softc,
971287621Smav	    &ctl_softc->ctl_proc, NULL, 0, 0, "ctl", "ha_tx");
972287621Smav	if (error != 0) {
973287621Smav		printf("error creating CTL HA connection thread!\n");
974287621Smav		mtx_destroy(&softc->ha_lock);
975287621Smav		return (CTL_HA_STATUS_ERROR);
976287621Smav	}
977287957Smav	softc->ha_shutdown_eh = EVENTHANDLER_REGISTER(shutdown_pre_sync,
978287957Smav	    ctl_ha_msg_shutdown, ctl_softc, SHUTDOWN_PRI_FIRST);
979287621Smav	SYSCTL_ADD_PROC(&ctl_softc->sysctl_ctx,
980287621Smav	    SYSCTL_CHILDREN(ctl_softc->sysctl_tree),
981287621Smav	    OID_AUTO, "ha_peer", CTLTYPE_STRING | CTLFLAG_RWTUN,
982287621Smav	    softc, 0, ctl_ha_peer_sysctl, "A", "HA peer connection method");
983287621Smav
984287621Smav	if (ctl_ha_msg_register(CTL_HA_CHAN_DATA, ctl_dt_event_handler)
985287621Smav	    != CTL_HA_STATUS_SUCCESS) {
986287621Smav		printf("%s: ctl_ha_msg_register failed.\n", __func__);
987287621Smav	}
988287621Smav
989287621Smav	return (CTL_HA_STATUS_SUCCESS);
990287621Smav};
991287621Smav
992287957Smavvoid
993287621Smavctl_ha_msg_shutdown(struct ctl_softc *ctl_softc)
994287621Smav{
995287621Smav	struct ha_softc *softc = &ha_softc;
996287621Smav
997287957Smav	/* Disconnect and shutdown threads. */
998287957Smav	mtx_lock(&softc->ha_lock);
999287957Smav	if (softc->ha_shutdown < 2) {
1000287957Smav		softc->ha_shutdown = 1;
1001287957Smav		softc->ha_wakeup = 1;
1002287957Smav		wakeup(&softc->ha_wakeup);
1003287957Smav		while (softc->ha_shutdown < 2) {
1004287957Smav			msleep(&softc->ha_wakeup, &softc->ha_lock, 0,
1005287957Smav			    "shutdown", hz);
1006287957Smav		}
1007287621Smav	}
1008287957Smav	mtx_unlock(&softc->ha_lock);
1009287957Smav};
1010287621Smav
1011287957Smavctl_ha_status
1012287957Smavctl_ha_msg_destroy(struct ctl_softc *ctl_softc)
1013287957Smav{
1014287957Smav	struct ha_softc *softc = &ha_softc;
1015287957Smav
1016287957Smav	if (softc->ha_shutdown_eh != NULL) {
1017287957Smav		EVENTHANDLER_DEREGISTER(shutdown_pre_sync,
1018287957Smav		    softc->ha_shutdown_eh);
1019287957Smav		softc->ha_shutdown_eh = NULL;
1020287957Smav	}
1021287957Smav
1022287957Smav	ctl_ha_msg_shutdown(ctl_softc);	/* Just in case. */
1023287957Smav
1024287957Smav	if (ctl_ha_msg_deregister(CTL_HA_CHAN_DATA) != CTL_HA_STATUS_SUCCESS)
1025287957Smav		printf("%s: ctl_ha_msg_deregister failed.\n", __func__);
1026287957Smav
1027287621Smav	mtx_destroy(&softc->ha_lock);
1028287621Smav	return (CTL_HA_STATUS_SUCCESS);
1029287621Smav};
1030