ctl_ha.c revision 287855
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: head/sys/cam/ctl/ctl_ha.c 287855 2015-09-16 09:59:05Z mav $");
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;
158287621Smav	TAILQ_HEAD(, ctl_ha_dt_req) ha_dts;
159287621Smav} ha_softc;
160287621Smav
161287621Smavextern struct ctl_softc *control_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);
284287621Smav		while (sbavail(&so->so_rcv) < next) {
285287621Smav			if (softc->ha_connected == 0 || so->so_error ||
286287621Smav			    (so->so_rcv.sb_state & SBS_CANTRCVMORE)) {
287287621Smav				goto errout;
288287621Smav			}
289287621Smav			so->so_rcv.sb_lowat = next;
290287621Smav			msleep(&softc->ha_receiving, SOCKBUF_MTX(&so->so_rcv),
291287621Smav			    0, "-", 0);
292287621Smav		}
293287621Smav		SOCKBUF_UNLOCK(&so->so_rcv);
294287621Smav
295287621Smav		if (wire_hdr.length == 0) {
296287621Smav			iov.iov_base = &wire_hdr;
297287621Smav			iov.iov_len = sizeof(wire_hdr);
298287621Smav			uio.uio_iov = &iov;
299287621Smav			uio.uio_iovcnt = 1;
300287621Smav			uio.uio_rw = UIO_READ;
301287621Smav			uio.uio_segflg = UIO_SYSSPACE;
302287621Smav			uio.uio_td = curthread;
303287621Smav			uio.uio_resid = sizeof(wire_hdr);
304287621Smav			flags = MSG_DONTWAIT;
305287621Smav			error = soreceive(softc->ha_so, NULL, &uio, NULL,
306287621Smav			    NULL, &flags);
307287621Smav			if (error != 0) {
308287621Smav				printf("%s: header receive error %d\n",
309287621Smav				    __func__, error);
310287621Smav				SOCKBUF_LOCK(&so->so_rcv);
311287621Smav				goto errout;
312287621Smav			}
313287621Smav		} else {
314287621Smav			ctl_ha_evt(softc, wire_hdr.channel,
315287621Smav			    CTL_HA_EVT_MSG_RECV, wire_hdr.length);
316287621Smav			wire_hdr.length = 0;
317287621Smav		}
318287621Smav	}
319287621Smav
320287621Smaverrout:
321287621Smav	softc->ha_receiving = 0;
322287621Smav	wakeup(&softc->ha_receiving);
323287621Smav	SOCKBUF_UNLOCK(&so->so_rcv);
324287621Smav	ctl_ha_conn_wake(softc);
325287621Smav	kthread_exit();
326287621Smav}
327287621Smav
328287621Smavstatic void
329287621Smavctl_ha_send(struct ha_softc *softc)
330287621Smav{
331287621Smav	struct socket *so = softc->ha_so;
332287621Smav	int error;
333287621Smav
334287621Smav	while (1) {
335287621Smav		if (softc->ha_sending == NULL) {
336287621Smav			mtx_lock(&softc->ha_lock);
337287621Smav			softc->ha_sending = mbufq_dequeue(&softc->ha_sendq);
338287621Smav			mtx_unlock(&softc->ha_lock);
339287621Smav			if (softc->ha_sending == NULL) {
340287621Smav				so->so_snd.sb_lowat = so->so_snd.sb_hiwat + 1;
341287621Smav				break;
342287621Smav			}
343287621Smav		}
344287621Smav		SOCKBUF_LOCK(&so->so_snd);
345287621Smav		if (sbspace(&so->so_snd) < softc->ha_sending->m_pkthdr.len) {
346287621Smav			so->so_snd.sb_lowat = softc->ha_sending->m_pkthdr.len;
347287621Smav			SOCKBUF_UNLOCK(&so->so_snd);
348287621Smav			break;
349287621Smav		}
350287621Smav		SOCKBUF_UNLOCK(&so->so_snd);
351287621Smav		error = sosend(softc->ha_so, NULL, NULL, softc->ha_sending,
352287621Smav		    NULL, MSG_DONTWAIT, curthread);
353287621Smav		softc->ha_sending = NULL;
354287621Smav		if (error != 0) {
355287621Smav			printf("%s: sosend() error %d\n", __func__, error);
356287621Smav			return;
357287621Smav		}
358287621Smav	};
359287621Smav}
360287621Smav
361287621Smavstatic void
362287621Smavctl_ha_sock_setup(struct ha_softc *softc)
363287621Smav{
364287621Smav	struct sockopt opt;
365287621Smav	struct socket *so = softc->ha_so;
366287621Smav	int error, val;
367287621Smav
368287621Smav	val = 1024 * 1024;
369287621Smav	error = soreserve(so, val, val);
370287621Smav	if (error)
371287621Smav		printf("%s: soreserve failed %d\n", __func__, error);
372287621Smav
373287621Smav	SOCKBUF_LOCK(&so->so_rcv);
374287621Smav	so->so_rcv.sb_lowat = sizeof(struct ha_msg_wire);
375287621Smav	soupcall_set(so, SO_RCV, ctl_ha_rupcall, softc);
376287621Smav	SOCKBUF_UNLOCK(&so->so_rcv);
377287621Smav	SOCKBUF_LOCK(&so->so_snd);
378287621Smav	so->so_snd.sb_lowat = sizeof(struct ha_msg_wire);
379287621Smav	soupcall_set(so, SO_SND, ctl_ha_supcall, softc);
380287621Smav	SOCKBUF_UNLOCK(&so->so_snd);
381287621Smav
382287621Smav	bzero(&opt, sizeof(struct sockopt));
383287621Smav	opt.sopt_dir = SOPT_SET;
384287621Smav	opt.sopt_level = SOL_SOCKET;
385287621Smav	opt.sopt_name = SO_KEEPALIVE;
386287621Smav	opt.sopt_val = &val;
387287621Smav	opt.sopt_valsize = sizeof(val);
388287621Smav	val = 1;
389287621Smav	error = sosetopt(so, &opt);
390287621Smav	if (error)
391287621Smav		printf("%s: KEEPALIVE setting failed %d\n", __func__, error);
392287621Smav
393287621Smav	opt.sopt_level = IPPROTO_TCP;
394287621Smav	opt.sopt_name = TCP_NODELAY;
395287621Smav	val = 1;
396287621Smav	error = sosetopt(so, &opt);
397287621Smav	if (error)
398287621Smav		printf("%s: NODELAY setting failed %d\n", __func__, error);
399287621Smav
400287621Smav	opt.sopt_name = TCP_KEEPINIT;
401287621Smav	val = 3;
402287621Smav	error = sosetopt(so, &opt);
403287621Smav	if (error)
404287621Smav		printf("%s: KEEPINIT setting failed %d\n", __func__, error);
405287621Smav
406287621Smav	opt.sopt_name = TCP_KEEPIDLE;
407287621Smav	val = 1;
408287621Smav	error = sosetopt(so, &opt);
409287621Smav	if (error)
410287621Smav		printf("%s: KEEPIDLE setting failed %d\n", __func__, error);
411287621Smav
412287621Smav	opt.sopt_name = TCP_KEEPINTVL;
413287621Smav	val = 1;
414287621Smav	error = sosetopt(so, &opt);
415287621Smav	if (error)
416287621Smav		printf("%s: KEEPINTVL setting failed %d\n", __func__, error);
417287621Smav
418287621Smav	opt.sopt_name = TCP_KEEPCNT;
419287621Smav	val = 5;
420287621Smav	error = sosetopt(so, &opt);
421287621Smav	if (error)
422287621Smav		printf("%s: KEEPCNT setting failed %d\n", __func__, error);
423287621Smav}
424287621Smav
425287621Smavstatic int
426287621Smavctl_ha_connect(struct ha_softc *softc)
427287621Smav{
428287621Smav	struct thread *td = curthread;
429287621Smav	struct socket *so;
430287621Smav	int error;
431287621Smav
432287621Smav	/* Create the socket */
433287621Smav	error = socreate(PF_INET, &so, SOCK_STREAM,
434287621Smav	    IPPROTO_TCP, td->td_ucred, td);
435287621Smav	if (error != 0) {
436287621Smav		printf("%s: socreate() error %d\n", __func__, error);
437287621Smav		return (error);
438287621Smav	}
439287621Smav	softc->ha_so = so;
440287621Smav	ctl_ha_sock_setup(softc);
441287621Smav
442287621Smav	error = soconnect(so, (struct sockaddr *)&softc->ha_peer_in, td);
443287621Smav	if (error != 0) {
444287621Smav		printf("%s: soconnect() error %d\n", __func__, error);
445287621Smav		goto out;
446287621Smav	}
447287621Smav	return (0);
448287621Smav
449287621Smavout:
450287621Smav	ctl_ha_close(softc);
451287621Smav	return (error);
452287621Smav}
453287621Smav
454287621Smavstatic int
455287621Smavctl_ha_accept(struct ha_softc *softc)
456287621Smav{
457287621Smav	struct socket *so;
458287621Smav	struct sockaddr *sap;
459287621Smav	int error;
460287621Smav
461287621Smav	ACCEPT_LOCK();
462287621Smav	if (softc->ha_lso->so_rcv.sb_state & SBS_CANTRCVMORE)
463287621Smav		softc->ha_lso->so_error = ECONNABORTED;
464287621Smav	if (softc->ha_lso->so_error) {
465287621Smav		error = softc->ha_lso->so_error;
466287621Smav		softc->ha_lso->so_error = 0;
467287621Smav		ACCEPT_UNLOCK();
468287621Smav		printf("%s: socket error %d\n", __func__, error);
469287621Smav		goto out;
470287621Smav	}
471287621Smav	so = TAILQ_FIRST(&softc->ha_lso->so_comp);
472287621Smav	if (so == NULL) {
473287621Smav		ACCEPT_UNLOCK();
474287621Smav		return (EWOULDBLOCK);
475287621Smav	}
476287621Smav	KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP"));
477287621Smav	KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP"));
478287621Smav
479287621Smav	/*
480287621Smav	 * Before changing the flags on the socket, we have to bump the
481287621Smav	 * reference count.  Otherwise, if the protocol calls sofree(),
482287621Smav	 * the socket will be released due to a zero refcount.
483287621Smav	 */
484287621Smav	SOCK_LOCK(so);			/* soref() and so_state update */
485287621Smav	soref(so);			/* file descriptor reference */
486287621Smav
487287621Smav	TAILQ_REMOVE(&softc->ha_lso->so_comp, so, so_list);
488287621Smav	softc->ha_lso->so_qlen--;
489287621Smav	so->so_state |= SS_NBIO;
490287621Smav	so->so_qstate &= ~SQ_COMP;
491287621Smav	so->so_head = NULL;
492287621Smav
493287621Smav	SOCK_UNLOCK(so);
494287621Smav	ACCEPT_UNLOCK();
495287621Smav
496287621Smav	sap = NULL;
497287621Smav	error = soaccept(so, &sap);
498287621Smav	if (error != 0) {
499287621Smav		printf("%s: soaccept() error %d\n", __func__, error);
500287621Smav		if (sap != NULL)
501287621Smav			free(sap, M_SONAME);
502287621Smav		goto out;
503287621Smav	}
504287621Smav	if (sap != NULL)
505287621Smav		free(sap, M_SONAME);
506287621Smav	softc->ha_so = so;
507287621Smav	ctl_ha_sock_setup(softc);
508287621Smav	return (0);
509287621Smav
510287621Smavout:
511287621Smav	ctl_ha_lclose(softc);
512287621Smav	return (error);
513287621Smav}
514287621Smav
515287621Smavstatic int
516287621Smavctl_ha_listen(struct ha_softc *softc)
517287621Smav{
518287621Smav	struct thread *td = curthread;
519287621Smav	struct sockopt opt;
520287621Smav	int error, val;
521287621Smav
522287621Smav	/* Create the socket */
523287621Smav	if (softc->ha_lso == NULL) {
524287621Smav		error = socreate(PF_INET, &softc->ha_lso, SOCK_STREAM,
525287621Smav		    IPPROTO_TCP, td->td_ucred, td);
526287621Smav		if (error != 0) {
527287621Smav			printf("%s: socreate() error %d\n", __func__, error);
528287621Smav			return (error);
529287621Smav		}
530287621Smav		bzero(&opt, sizeof(struct sockopt));
531287621Smav		opt.sopt_dir = SOPT_SET;
532287621Smav		opt.sopt_level = SOL_SOCKET;
533287621Smav		opt.sopt_name = SO_REUSEADDR;
534287621Smav		opt.sopt_val = &val;
535287621Smav		opt.sopt_valsize = sizeof(val);
536287621Smav		val = 1;
537287621Smav		error = sosetopt(softc->ha_lso, &opt);
538287621Smav		if (error) {
539287621Smav			printf("%s: REUSEADDR setting failed %d\n",
540287621Smav			    __func__, error);
541287621Smav		}
542287621Smav		SOCKBUF_LOCK(&softc->ha_lso->so_rcv);
543287621Smav		soupcall_set(softc->ha_lso, SO_RCV, ctl_ha_lupcall, softc);
544287621Smav		SOCKBUF_UNLOCK(&softc->ha_lso->so_rcv);
545287621Smav	}
546287621Smav
547287621Smav	error = sobind(softc->ha_lso, (struct sockaddr *)&softc->ha_peer_in, td);
548287621Smav	if (error != 0) {
549287621Smav		printf("%s: sobind() error %d\n", __func__, error);
550287621Smav		goto out;
551287621Smav	}
552287621Smav	error = solisten(softc->ha_lso, 1, td);
553287621Smav	if (error != 0) {
554287621Smav		printf("%s: solisten() error %d\n", __func__, error);
555287621Smav		goto out;
556287621Smav	}
557287621Smav	return (0);
558287621Smav
559287621Smavout:
560287621Smav	ctl_ha_lclose(softc);
561287621Smav	return (error);
562287621Smav}
563287621Smav
564287621Smavstatic void
565287621Smavctl_ha_conn_thread(void *arg)
566287621Smav{
567287621Smav	struct ha_softc *softc = arg;
568287621Smav	int error;
569287621Smav
570287621Smav	while (1) {
571287621Smav		if (softc->ha_disconnect) {
572287621Smav			ctl_ha_close(softc);
573287621Smav			ctl_ha_lclose(softc);
574287621Smav			softc->ha_disconnect = 0;
575287621Smav		} else if (softc->ha_so != NULL &&
576287621Smav		    (softc->ha_so->so_error ||
577287621Smav		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
578287621Smav			ctl_ha_close(softc);
579287621Smav		if (softc->ha_so == NULL) {
580287621Smav			if (softc->ha_lso != NULL)
581287621Smav				ctl_ha_accept(softc);
582287621Smav			else if (softc->ha_listen)
583287621Smav				ctl_ha_listen(softc);
584287621Smav			else if (softc->ha_connect)
585287621Smav				ctl_ha_connect(softc);
586287621Smav		}
587287621Smav		if (softc->ha_so != NULL) {
588287621Smav			if (softc->ha_connected == 0 &&
589287621Smav			    softc->ha_so->so_error == 0 &&
590287621Smav			    (softc->ha_so->so_state & SS_ISCONNECTING) == 0) {
591287621Smav				softc->ha_connected = 1;
592287621Smav				ctl_ha_evt(softc, CTL_HA_CHAN_MAX,
593287621Smav				    CTL_HA_EVT_LINK_CHANGE,
594287621Smav				    CTL_HA_LINK_ONLINE);
595287621Smav				softc->ha_receiving = 1;
596287621Smav				error = kproc_kthread_add(ctl_ha_rx_thread,
597287621Smav				    softc, &softc->ha_ctl_softc->ctl_proc,
598287621Smav				    NULL, 0, 0, "ctl", "ha_rx");
599287621Smav				if (error != 0) {
600287621Smav					printf("Error creating CTL HA rx thread!\n");
601287621Smav					softc->ha_receiving = 0;
602287621Smav					softc->ha_disconnect = 1;
603287621Smav				}
604287621Smav			}
605287621Smav			ctl_ha_send(softc);
606287621Smav		}
607287621Smav		mtx_lock(&softc->ha_lock);
608287621Smav		if (softc->ha_so != NULL &&
609287621Smav		    (softc->ha_so->so_error ||
610287621Smav		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
611287621Smav			;
612287621Smav		else if (!softc->ha_wakeup)
613287621Smav			msleep(&softc->ha_wakeup, &softc->ha_lock, 0, "-", hz);
614287621Smav		softc->ha_wakeup = 0;
615287621Smav		mtx_unlock(&softc->ha_lock);
616287621Smav	}
617287621Smav}
618287621Smav
619287621Smavstatic int
620287621Smavctl_ha_peer_sysctl(SYSCTL_HANDLER_ARGS)
621287621Smav{
622287621Smav	struct ha_softc *softc = (struct ha_softc *)arg1;
623287621Smav	struct sockaddr_in *sa;
624287621Smav	int error, b1, b2, b3, b4, p, num;
625287855Smav	char buf[128];
626287621Smav
627287855Smav	strlcpy(buf, softc->ha_peer, sizeof(buf));
628287855Smav	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
629287855Smav	if ((error != 0) || (req->newptr == NULL) ||
630287855Smav	    strncmp(buf, softc->ha_peer, sizeof(buf)) == 0)
631287621Smav		return (error);
632287621Smav
633287621Smav	sa = &softc->ha_peer_in;
634287621Smav	mtx_lock(&softc->ha_lock);
635287855Smav	if ((num = sscanf(buf, "connect %d.%d.%d.%d:%d",
636287621Smav	    &b1, &b2, &b3, &b4, &p)) >= 4) {
637287621Smav		softc->ha_connect = 1;
638287621Smav		softc->ha_listen = 0;
639287855Smav	} else if ((num = sscanf(buf, "listen %d.%d.%d.%d:%d",
640287621Smav	    &b1, &b2, &b3, &b4, &p)) >= 4) {
641287621Smav		softc->ha_connect = 0;
642287621Smav		softc->ha_listen = 1;
643287621Smav	} else {
644287621Smav		softc->ha_connect = 0;
645287621Smav		softc->ha_listen = 0;
646287855Smav		if (buf[0] != 0) {
647287855Smav			buf[0] = 0;
648287621Smav			error = EINVAL;
649287855Smav		}
650287621Smav	}
651287855Smav	strlcpy(softc->ha_peer, buf, sizeof(softc->ha_peer));
652287621Smav	if (softc->ha_connect || softc->ha_listen) {
653287621Smav		memset(sa, 0, sizeof(*sa));
654287621Smav		sa->sin_len = sizeof(struct sockaddr_in);
655287621Smav		sa->sin_family = AF_INET;
656287621Smav		sa->sin_port = htons((num >= 5) ? p : 999);
657287621Smav		sa->sin_addr.s_addr =
658287621Smav		    htonl((b1 << 24) + (b2 << 16) + (b3 << 8) + b4);
659287621Smav	}
660287621Smav	softc->ha_disconnect = 1;
661287621Smav	softc->ha_wakeup = 1;
662287621Smav	mtx_unlock(&softc->ha_lock);
663287621Smav	wakeup(&softc->ha_wakeup);
664287621Smav	return (error);
665287621Smav}
666287621Smav
667287621Smavctl_ha_status
668287621Smavctl_ha_msg_register(ctl_ha_channel channel, ctl_evt_handler handler)
669287621Smav{
670287621Smav	struct ha_softc *softc = &ha_softc;
671287621Smav
672287621Smav	KASSERT(channel < CTL_HA_CHAN_MAX,
673287621Smav	    ("Wrong CTL HA channel %d", channel));
674287621Smav	softc->ha_handler[channel] = handler;
675287621Smav	return (CTL_HA_STATUS_SUCCESS);
676287621Smav}
677287621Smav
678287621Smavctl_ha_status
679287621Smavctl_ha_msg_deregister(ctl_ha_channel channel)
680287621Smav{
681287621Smav	struct ha_softc *softc = &ha_softc;
682287621Smav
683287621Smav	KASSERT(channel < CTL_HA_CHAN_MAX,
684287621Smav	    ("Wrong CTL HA channel %d", channel));
685287621Smav	softc->ha_handler[channel] = NULL;
686287621Smav	return (CTL_HA_STATUS_SUCCESS);
687287621Smav}
688287621Smav
689287621Smav/*
690287621Smav * Receive a message of the specified size.
691287621Smav */
692287621Smavctl_ha_status
693287621Smavctl_ha_msg_recv(ctl_ha_channel channel, void *addr, size_t len,
694287621Smav		int wait)
695287621Smav{
696287621Smav	struct ha_softc *softc = &ha_softc;
697287621Smav	struct uio uio;
698287621Smav	struct iovec iov;
699287621Smav	int error, flags;
700287621Smav
701287621Smav	if (!softc->ha_connected)
702287621Smav		return (CTL_HA_STATUS_DISCONNECT);
703287621Smav
704287621Smav	iov.iov_base = addr;
705287621Smav	iov.iov_len = len;
706287621Smav	uio.uio_iov = &iov;
707287621Smav	uio.uio_iovcnt = 1;
708287621Smav	uio.uio_rw = UIO_READ;
709287621Smav	uio.uio_segflg = UIO_SYSSPACE;
710287621Smav	uio.uio_td = curthread;
711287621Smav	uio.uio_resid = len;
712287621Smav	flags = wait ? 0 : MSG_DONTWAIT;
713287621Smav	error = soreceive(softc->ha_so, NULL, &uio, NULL, NULL, &flags);
714287621Smav	if (error == 0)
715287621Smav		return (CTL_HA_STATUS_SUCCESS);
716287621Smav
717287621Smav	/* Consider all errors fatal for HA sanity. */
718287621Smav	mtx_lock(&softc->ha_lock);
719287621Smav	if (softc->ha_connected) {
720287621Smav		softc->ha_disconnect = 1;
721287621Smav		softc->ha_wakeup = 1;
722287621Smav		wakeup(&softc->ha_wakeup);
723287621Smav	}
724287621Smav	mtx_unlock(&softc->ha_lock);
725287621Smav	return (CTL_HA_STATUS_ERROR);
726287621Smav}
727287621Smav
728287621Smav/*
729287621Smav * Send a message of the specified size.
730287621Smav */
731287621Smavctl_ha_status
732287621Smavctl_ha_msg_send2(ctl_ha_channel channel, const void *addr, size_t len,
733287621Smav    const void *addr2, size_t len2, int wait)
734287621Smav{
735287621Smav	struct ha_softc *softc = &ha_softc;
736287621Smav	struct mbuf *mb, *newmb;
737287621Smav	struct ha_msg_wire hdr;
738287621Smav	size_t copylen, off;
739287621Smav
740287621Smav	if (!softc->ha_connected)
741287621Smav		return (CTL_HA_STATUS_DISCONNECT);
742287621Smav
743287621Smav	newmb = m_getm2(NULL, sizeof(hdr) + len + len2, wait, MT_DATA,
744287621Smav	    M_PKTHDR);
745287621Smav	if (newmb == NULL) {
746287621Smav		/* Consider all errors fatal for HA sanity. */
747287621Smav		mtx_lock(&softc->ha_lock);
748287621Smav		if (softc->ha_connected) {
749287621Smav			softc->ha_disconnect = 1;
750287621Smav			softc->ha_wakeup = 1;
751287621Smav			wakeup(&softc->ha_wakeup);
752287621Smav		}
753287621Smav		mtx_unlock(&softc->ha_lock);
754287621Smav		printf("%s: Can't allocate mbuf chain\n", __func__);
755287621Smav		return (CTL_HA_STATUS_ERROR);
756287621Smav	}
757287621Smav	hdr.channel = channel;
758287621Smav	hdr.length = len + len2;
759287621Smav	mb = newmb;
760287621Smav	memcpy(mtodo(mb, 0), &hdr, sizeof(hdr));
761287621Smav	mb->m_len += sizeof(hdr);
762287621Smav	off = 0;
763287621Smav	for (; mb != NULL && off < len; mb = mb->m_next) {
764287621Smav		copylen = min(M_TRAILINGSPACE(mb), len - off);
765287621Smav		memcpy(mtodo(mb, mb->m_len), (const char *)addr + off, copylen);
766287621Smav		mb->m_len += copylen;
767287621Smav		off += copylen;
768287621Smav		if (off == len)
769287621Smav			break;
770287621Smav	}
771287621Smav	KASSERT(off == len, ("%s: off (%zu) != len (%zu)", __func__,
772287621Smav	    off, len));
773287621Smav	off = 0;
774287621Smav	for (; mb != NULL && off < len2; mb = mb->m_next) {
775287621Smav		copylen = min(M_TRAILINGSPACE(mb), len2 - off);
776287621Smav		memcpy(mtodo(mb, mb->m_len), (const char *)addr2 + off, copylen);
777287621Smav		mb->m_len += copylen;
778287621Smav		off += copylen;
779287621Smav	}
780287621Smav	KASSERT(off == len2, ("%s: off (%zu) != len2 (%zu)", __func__,
781287621Smav	    off, len2));
782287621Smav	newmb->m_pkthdr.len = sizeof(hdr) + len + len2;
783287621Smav
784287621Smav	mtx_lock(&softc->ha_lock);
785287621Smav	if (!softc->ha_connected) {
786287621Smav		mtx_unlock(&softc->ha_lock);
787287621Smav		m_freem(newmb);
788287621Smav		return (CTL_HA_STATUS_DISCONNECT);
789287621Smav	}
790287621Smav	mbufq_enqueue(&softc->ha_sendq, newmb);
791287621Smav	softc->ha_wakeup = 1;
792287621Smav	mtx_unlock(&softc->ha_lock);
793287621Smav	wakeup(&softc->ha_wakeup);
794287621Smav	return (CTL_HA_STATUS_SUCCESS);
795287621Smav}
796287621Smav
797287621Smavctl_ha_status
798287621Smavctl_ha_msg_send(ctl_ha_channel channel, const void *addr, size_t len,
799287621Smav    int wait)
800287621Smav{
801287621Smav
802287621Smav	return (ctl_ha_msg_send2(channel, addr, len, NULL, 0, wait));
803287621Smav}
804287621Smav
805287621Smav/*
806287621Smav * Allocate a data transfer request structure.
807287621Smav */
808287621Smavstruct ctl_ha_dt_req *
809287621Smavctl_dt_req_alloc(void)
810287621Smav{
811287621Smav
812287621Smav	return (malloc(sizeof(struct ctl_ha_dt_req), M_CTL, M_WAITOK | M_ZERO));
813287621Smav}
814287621Smav
815287621Smav/*
816287621Smav * Free a data transfer request structure.
817287621Smav */
818287621Smavvoid
819287621Smavctl_dt_req_free(struct ctl_ha_dt_req *req)
820287621Smav{
821287621Smav
822287621Smav	free(req, M_CTL);
823287621Smav}
824287621Smav
825287621Smav/*
826287621Smav * Issue a DMA request for a single buffer.
827287621Smav */
828287621Smavctl_ha_status
829287621Smavctl_dt_single(struct ctl_ha_dt_req *req)
830287621Smav{
831287621Smav	struct ha_softc *softc = &ha_softc;
832287621Smav	struct ha_dt_msg_wire wire_dt;
833287621Smav	ctl_ha_status status;
834287621Smav
835287621Smav	wire_dt.command = req->command;
836287621Smav	wire_dt.size = req->size;
837287621Smav	wire_dt.local = req->local;
838287621Smav	wire_dt.remote = req->remote;
839287621Smav	if (req->command == CTL_HA_DT_CMD_READ && req->callback != NULL) {
840287621Smav		mtx_lock(&softc->ha_lock);
841287621Smav		TAILQ_INSERT_TAIL(&softc->ha_dts, req, links);
842287621Smav		mtx_unlock(&softc->ha_lock);
843287621Smav		ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt, sizeof(wire_dt),
844287621Smav		    M_WAITOK);
845287621Smav		return (CTL_HA_STATUS_WAIT);
846287621Smav	}
847287621Smav	if (req->command == CTL_HA_DT_CMD_READ) {
848287621Smav		status = ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt,
849287621Smav		    sizeof(wire_dt), M_WAITOK);
850287621Smav	} else {
851287621Smav		status = ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
852287621Smav		    sizeof(wire_dt), req->local, req->size, M_WAITOK);
853287621Smav	}
854287621Smav	return (status);
855287621Smav}
856287621Smav
857287621Smavstatic void
858287621Smavctl_dt_event_handler(ctl_ha_channel channel, ctl_ha_event event, int param)
859287621Smav{
860287621Smav	struct ha_softc *softc = &ha_softc;
861287621Smav	struct ctl_ha_dt_req *req;
862287621Smav	ctl_ha_status isc_status;
863287621Smav
864287621Smav	if (event == CTL_HA_EVT_MSG_RECV) {
865287621Smav		struct ha_dt_msg_wire wire_dt;
866287621Smav		uint8_t *tmp;
867287621Smav		int size;
868287621Smav
869287621Smav		size = min(sizeof(wire_dt), param);
870287621Smav		isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA, &wire_dt,
871287621Smav					     size, M_WAITOK);
872287621Smav		if (isc_status != CTL_HA_STATUS_SUCCESS) {
873287621Smav			printf("%s: Error receiving message: %d\n",
874287621Smav			    __func__, isc_status);
875287621Smav			return;
876287621Smav		}
877287621Smav
878287621Smav		if (wire_dt.command == CTL_HA_DT_CMD_READ) {
879287621Smav			wire_dt.command = CTL_HA_DT_CMD_WRITE;
880287621Smav			tmp = wire_dt.local;
881287621Smav			wire_dt.local = wire_dt.remote;
882287621Smav			wire_dt.remote = tmp;
883287621Smav			ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
884287621Smav			    sizeof(wire_dt), wire_dt.local, wire_dt.size,
885287621Smav			    M_WAITOK);
886287621Smav		} else if (wire_dt.command == CTL_HA_DT_CMD_WRITE) {
887287621Smav			isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA,
888287621Smav			    wire_dt.remote, wire_dt.size, M_WAITOK);
889287621Smav			mtx_lock(&softc->ha_lock);
890287621Smav			TAILQ_FOREACH(req, &softc->ha_dts, links) {
891287621Smav				if (req->local == wire_dt.remote) {
892287621Smav					TAILQ_REMOVE(&softc->ha_dts, req, links);
893287621Smav					break;
894287621Smav				}
895287621Smav			}
896287621Smav			mtx_unlock(&softc->ha_lock);
897287621Smav			if (req) {
898287621Smav				req->ret = isc_status;
899287621Smav				req->callback(req);
900287621Smav			}
901287621Smav		}
902287621Smav	} else if (event == CTL_HA_EVT_LINK_CHANGE) {
903287621Smav		CTL_DEBUG_PRINT(("%s: Link state change to %d\n", __func__,
904287621Smav		    param));
905287621Smav		if (param != CTL_HA_LINK_ONLINE) {
906287621Smav			mtx_lock(&softc->ha_lock);
907287621Smav			while ((req = TAILQ_FIRST(&softc->ha_dts)) != NULL) {
908287621Smav				TAILQ_REMOVE(&softc->ha_dts, req, links);
909287621Smav				mtx_unlock(&softc->ha_lock);
910287621Smav				req->ret = CTL_HA_STATUS_DISCONNECT;
911287621Smav				req->callback(req);
912287621Smav				mtx_lock(&softc->ha_lock);
913287621Smav			}
914287621Smav			mtx_unlock(&softc->ha_lock);
915287621Smav		}
916287621Smav	} else {
917287621Smav		printf("%s: Unknown event %d\n", __func__, event);
918287621Smav	}
919287621Smav}
920287621Smav
921287621Smav
922287621Smavctl_ha_status
923287621Smavctl_ha_msg_init(struct ctl_softc *ctl_softc)
924287621Smav{
925287621Smav	struct ha_softc *softc = &ha_softc;
926287621Smav	int error;
927287621Smav
928287621Smav	softc->ha_ctl_softc = ctl_softc;
929287621Smav	mtx_init(&softc->ha_lock, "CTL HA mutex", NULL, MTX_DEF);
930287621Smav	mbufq_init(&softc->ha_sendq, INT_MAX);
931287621Smav	TAILQ_INIT(&softc->ha_dts);
932287621Smav	error = kproc_kthread_add(ctl_ha_conn_thread, softc,
933287621Smav	    &ctl_softc->ctl_proc, NULL, 0, 0, "ctl", "ha_tx");
934287621Smav	if (error != 0) {
935287621Smav		printf("error creating CTL HA connection thread!\n");
936287621Smav		mtx_destroy(&softc->ha_lock);
937287621Smav		return (CTL_HA_STATUS_ERROR);
938287621Smav	}
939287621Smav	SYSCTL_ADD_PROC(&ctl_softc->sysctl_ctx,
940287621Smav	    SYSCTL_CHILDREN(ctl_softc->sysctl_tree),
941287621Smav	    OID_AUTO, "ha_peer", CTLTYPE_STRING | CTLFLAG_RWTUN,
942287621Smav	    softc, 0, ctl_ha_peer_sysctl, "A", "HA peer connection method");
943287621Smav
944287621Smav	if (ctl_ha_msg_register(CTL_HA_CHAN_DATA, ctl_dt_event_handler)
945287621Smav	    != CTL_HA_STATUS_SUCCESS) {
946287621Smav		printf("%s: ctl_ha_msg_register failed.\n", __func__);
947287621Smav	}
948287621Smav
949287621Smav	return (CTL_HA_STATUS_SUCCESS);
950287621Smav};
951287621Smav
952287621Smavctl_ha_status
953287621Smavctl_ha_msg_shutdown(struct ctl_softc *ctl_softc)
954287621Smav{
955287621Smav	struct ha_softc *softc = &ha_softc;
956287621Smav
957287621Smav	if (ctl_ha_msg_deregister(CTL_HA_CHAN_DATA) != CTL_HA_STATUS_SUCCESS) {
958287621Smav		printf("%s: ctl_ha_msg_deregister failed.\n", __func__);
959287621Smav	}
960287621Smav
961287621Smav	mtx_destroy(&softc->ha_lock);
962287621Smav	return (CTL_HA_STATUS_SUCCESS);
963287621Smav};
964