ctl_ha.c revision 288789
1184610Salfred/*-
2184610Salfred * Copyright (c) 2015 Alexander Motin <mav@FreeBSD.org>
3184610Salfred * All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer,
10184610Salfred *    without modification, immediately at the beginning of the file.
11184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
12184610Salfred *    notice, this list of conditions and the following disclaimer in the
13184610Salfred *    documentation and/or other materials provided with the distribution.
14184610Salfred *
15184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16184610Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17184610Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18184610Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19184610Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20184610Salfred * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21184610Salfred * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22184610Salfred * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23184610Salfred * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24184610Salfred * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25184610Salfred */
26184610Salfred
27194230Sthompsa#include <sys/cdefs.h>
28194230Sthompsa__FBSDID("$FreeBSD: stable/10/sys/cam/ctl/ctl_ha.c 288789 2015-10-05 10:53:13Z mav $");
29184610Salfred
30184610Salfred#include <sys/param.h>
31228056Shselasky#include <sys/systm.h>
32228056Shselasky#include <sys/kernel.h>
33228056Shselasky#include <sys/kthread.h>
34228056Shselasky#include <sys/types.h>
35228056Shselasky#include <sys/limits.h>
36228056Shselasky#include <sys/lock.h>
37228056Shselasky#include <sys/module.h>
38228056Shselasky#include <sys/mutex.h>
39228056Shselasky#include <sys/condvar.h>
40228056Shselasky#include <sys/malloc.h>
41228056Shselasky#include <sys/mbuf.h>
42228056Shselasky#include <sys/proc.h>
43228056Shselasky#include <sys/conf.h>
44228056Shselasky#include <sys/queue.h>
45228056Shselasky#include <sys/sysctl.h>
46228056Shselasky#include <sys/socket.h>
47228056Shselasky#include <sys/socketvar.h>
48228056Shselasky#include <sys/uio.h>
49228056Shselasky#include <netinet/in.h>
50228056Shselasky#include <netinet/tcp.h>
51228056Shselasky#include <vm/uma.h>
52228056Shselasky
53228056Shselasky#include <cam/cam.h>
54228056Shselasky#include <cam/scsi/scsi_all.h>
55228056Shselasky#include <cam/scsi/scsi_da.h>
56228056Shselasky#include <cam/ctl/ctl_io.h>
57228056Shselasky#include <cam/ctl/ctl.h>
58228056Shselasky#include <cam/ctl/ctl_frontend.h>
59228056Shselasky#include <cam/ctl/ctl_util.h>
60228056Shselasky#include <cam/ctl/ctl_backend.h>
61228056Shselasky#include <cam/ctl/ctl_ioctl.h>
62228056Shselasky#include <cam/ctl/ctl_ha.h>
63228056Shselasky#include <cam/ctl/ctl_private.h>
64228056Shselasky#include <cam/ctl/ctl_debug.h>
65228056Shselasky#include <cam/ctl/ctl_error.h>
66228056Shselasky
67228056Shselasky#if (__FreeBSD_version < 1100000)
68228056Shselaskystruct mbufq {
69228056Shselasky	struct mbuf *head;
70228056Shselasky	struct mbuf *tail;
71228056Shselasky};
72228056Shselasky
73228056Shselaskystatic void
74228056Shselaskymbufq_init(struct mbufq *q, int limit)
75228056Shselasky{
76228056Shselasky
77228056Shselasky	q->head = q->tail = NULL;
78228056Shselasky}
79228056Shselasky
80228056Shselaskystatic void
81228056Shselaskymbufq_drain(struct mbufq *q)
82228056Shselasky{
83228056Shselasky	struct mbuf *m;
84228056Shselasky
85228056Shselasky	while ((m = q->head) != NULL) {
86228056Shselasky		q->head = m->m_nextpkt;
87228056Shselasky		m_freem(m);
88228056Shselasky	}
89228056Shselasky	q->tail = NULL;
90228056Shselasky}
91228056Shselasky
92228056Shselaskystatic struct mbuf *
93228056Shselaskymbufq_dequeue(struct mbufq *q)
94228056Shselasky{
95228056Shselasky	struct mbuf *m;
96228056Shselasky
97228056Shselasky	m = q->head;
98228056Shselasky	if (m) {
99228056Shselasky		if (q->tail == m)
100228056Shselasky			q->tail = NULL;
101228056Shselasky		q->head = m->m_nextpkt;
102228056Shselasky		m->m_nextpkt = NULL;
103228056Shselasky	}
104228056Shselasky	return (m);
105228056Shselasky}
106228056Shselasky
107228056Shselaskystatic void
108228056Shselaskymbufq_enqueue(struct mbufq *q, struct mbuf *m)
109228056Shselasky{
110228056Shselasky
111228056Shselasky	m->m_nextpkt = NULL;
112228056Shselasky	if (q->tail)
113228056Shselasky		q->tail->m_nextpkt = m;
114228056Shselasky	else
115228056Shselasky		q->head = m;
116228056Shselasky	q->tail = m;
117228056Shselasky}
118228056Shselasky
119228056Shselaskystatic u_int
120228056Shselaskysbavail(struct sockbuf *sb)
121228056Shselasky{
122228056Shselasky	return (sb->sb_cc);
123228056Shselasky}
124228056Shselasky
125228056Shselasky#if (__FreeBSD_version < 1000000)
126228056Shselasky#define	mtodo(m, o)	((void *)(((m)->m_data) + (o)))
127228056Shselasky#endif
128228056Shselasky#endif
129228056Shselasky
130228056Shselaskystruct ha_msg_wire {
131228056Shselasky	uint32_t	 channel;
132228056Shselasky	uint32_t	 length;
133228056Shselasky};
134228056Shselasky
135228056Shselaskystruct ha_dt_msg_wire {
136228056Shselasky	ctl_ha_dt_cmd	command;
137228056Shselasky	uint32_t	size;
138228056Shselasky	uint8_t		*local;
139228056Shselasky	uint8_t		*remote;
140228056Shselasky};
141228056Shselasky
142228056Shselaskystruct ha_softc {
143228056Shselasky	struct ctl_softc *ha_ctl_softc;
144228056Shselasky	ctl_evt_handler	 ha_handler[CTL_HA_CHAN_MAX];
145184610Salfred	char		 ha_peer[128];
146184610Salfred	struct sockaddr_in  ha_peer_in;
147184610Salfred	struct socket	*ha_lso;
148192984Sthompsa	struct socket	*ha_so;
149192984Sthompsa	struct mbufq	 ha_sendq;
150192984Sthompsa	struct mbuf	*ha_sending;
151184610Salfred	struct mtx	 ha_lock;
152184610Salfred	int		 ha_connect;
153190180Sthompsa	int		 ha_listen;
154192984Sthompsa	int		 ha_connected;
155190180Sthompsa	int		 ha_receiving;
156192984Sthompsa	int		 ha_wakeup;
157190180Sthompsa	int		 ha_disconnect;
158184610Salfred	int		 ha_shutdown;
159184610Salfred	eventhandler_tag ha_shutdown_eh;
160184610Salfred	TAILQ_HEAD(, ctl_ha_dt_req) ha_dts;
161184610Salfred} ha_softc;
162184610Salfred
163192984Sthompsaextern struct ctl_softc *control_softc;
164192984Sthompsa
165190180Sthompsastatic void
166192984Sthompsactl_ha_conn_wake(struct ha_softc *softc)
167190180Sthompsa{
168192984Sthompsa
169192984Sthompsa	mtx_lock(&softc->ha_lock);
170184610Salfred	softc->ha_wakeup = 1;
171184610Salfred	mtx_unlock(&softc->ha_lock);
172192984Sthompsa	wakeup(&softc->ha_wakeup);
173184610Salfred}
174187173Sthompsa
175190180Sthompsastatic int
176192984Sthompsactl_ha_lupcall(struct socket *so, void *arg, int waitflag)
177192984Sthompsa{
178190180Sthompsa	struct ha_softc *softc = arg;
179192984Sthompsa
180192984Sthompsa	ctl_ha_conn_wake(softc);
181192984Sthompsa	return (SU_OK);
182192984Sthompsa}
183184610Salfred
184193074Sthompsastatic int
185193074Sthompsactl_ha_rupcall(struct socket *so, void *arg, int waitflag)
186190181Sthompsa{
187193045Sthompsa	struct ha_softc *softc = arg;
188193045Sthompsa
189193045Sthompsa	wakeup(&softc->ha_receiving);
190184610Salfred	return (SU_OK);
191184610Salfred}
192190181Sthompsa
193184610Salfredstatic int
194184610Salfredctl_ha_supcall(struct socket *so, void *arg, int waitflag)
195184610Salfred{
196184610Salfred	struct ha_softc *softc = arg;
197184610Salfred
198184610Salfred	ctl_ha_conn_wake(softc);
199184610Salfred	return (SU_OK);
200192984Sthompsa}
201192984Sthompsa
202192984Sthompsastatic void
203192984Sthompsactl_ha_evt(struct ha_softc *softc, ctl_ha_channel ch, ctl_ha_event evt,
204184610Salfred    int param)
205192984Sthompsa{
206184610Salfred	int i;
207192984Sthompsa
208192984Sthompsa	if (ch < CTL_HA_CHAN_MAX) {
209192984Sthompsa		if (softc->ha_handler[ch])
210192984Sthompsa			softc->ha_handler[ch](ch, evt, param);
211184610Salfred		return;
212193045Sthompsa	}
213184610Salfred	for (i = 0; i < CTL_HA_CHAN_MAX; i++) {
214193074Sthompsa		if (softc->ha_handler[i])
215193045Sthompsa			softc->ha_handler[i](i, evt, param);
216193045Sthompsa	}
217184610Salfred}
218213435Shselasky
219184610Salfredstatic void
220184610Salfredctl_ha_close(struct ha_softc *softc)
221192500Sthompsa{
222184610Salfred	struct socket *so = softc->ha_so;
223193045Sthompsa	int report = 0;
224184610Salfred
225184610Salfred	if (softc->ha_connected || softc->ha_disconnect) {
226184610Salfred		softc->ha_connected = 0;
227184610Salfred		mbufq_drain(&softc->ha_sendq);
228194228Sthompsa		m_freem(softc->ha_sending);
229193074Sthompsa		softc->ha_sending = NULL;
230193074Sthompsa		report = 1;
231213435Shselasky	}
232194228Sthompsa	if (so) {
233192984Sthompsa		SOCKBUF_LOCK(&so->so_rcv);
234194228Sthompsa		soupcall_clear(so, SO_RCV);
235194228Sthompsa		while (softc->ha_receiving) {
236194228Sthompsa			wakeup(&softc->ha_receiving);
237194228Sthompsa			msleep(&softc->ha_receiving, SOCKBUF_MTX(&so->so_rcv),
238194228Sthompsa			    0, "ha_rx exit", 0);
239192984Sthompsa		}
240194228Sthompsa		SOCKBUF_UNLOCK(&so->so_rcv);
241207080Sthompsa		SOCKBUF_LOCK(&so->so_snd);
242213435Shselasky		soupcall_clear(so, SO_SND);
243213435Shselasky		SOCKBUF_UNLOCK(&so->so_snd);
244194228Sthompsa		softc->ha_so = NULL;
245193644Sthompsa		if (softc->ha_connect)
246194228Sthompsa			pause("reconnect", hz / 2);
247194228Sthompsa		soclose(so);
248194228Sthompsa	}
249194228Sthompsa	if (report) {
250193045Sthompsa		ctl_ha_evt(softc, CTL_HA_CHAN_MAX, CTL_HA_EVT_LINK_CHANGE,
251212134Sthompsa		    (softc->ha_connect || softc->ha_listen) ?
252194228Sthompsa		    CTL_HA_LINK_UNKNOWN : CTL_HA_LINK_OFFLINE);
253184610Salfred	}
254194230Sthompsa}
255
256static void
257ctl_ha_lclose(struct ha_softc *softc)
258{
259
260	if (softc->ha_lso) {
261		SOCKBUF_LOCK(&softc->ha_lso->so_rcv);
262		soupcall_clear(softc->ha_lso, SO_RCV);
263		SOCKBUF_UNLOCK(&softc->ha_lso->so_rcv);
264		soclose(softc->ha_lso);
265		softc->ha_lso = NULL;
266	}
267}
268
269static void
270ctl_ha_rx_thread(void *arg)
271{
272	struct ha_softc *softc = arg;
273	struct socket *so = softc->ha_so;
274	struct ha_msg_wire wire_hdr;
275	struct uio uio;
276	struct iovec iov;
277	int error, flags, next;
278
279	bzero(&wire_hdr, sizeof(wire_hdr));
280	while (1) {
281		if (wire_hdr.length > 0)
282			next = wire_hdr.length;
283		else
284			next = sizeof(wire_hdr);
285		SOCKBUF_LOCK(&so->so_rcv);
286		while (sbavail(&so->so_rcv) < next || softc->ha_disconnect) {
287			if (softc->ha_connected == 0 || softc->ha_disconnect ||
288			    so->so_error ||
289			    (so->so_rcv.sb_state & SBS_CANTRCVMORE)) {
290				goto errout;
291			}
292			so->so_rcv.sb_lowat = next;
293			msleep(&softc->ha_receiving, SOCKBUF_MTX(&so->so_rcv),
294			    0, "-", 0);
295		}
296		SOCKBUF_UNLOCK(&so->so_rcv);
297
298		if (wire_hdr.length == 0) {
299			iov.iov_base = &wire_hdr;
300			iov.iov_len = sizeof(wire_hdr);
301			uio.uio_iov = &iov;
302			uio.uio_iovcnt = 1;
303			uio.uio_rw = UIO_READ;
304			uio.uio_segflg = UIO_SYSSPACE;
305			uio.uio_td = curthread;
306			uio.uio_resid = sizeof(wire_hdr);
307			flags = MSG_DONTWAIT;
308			error = soreceive(softc->ha_so, NULL, &uio, NULL,
309			    NULL, &flags);
310			if (error != 0) {
311				printf("%s: header receive error %d\n",
312				    __func__, error);
313				SOCKBUF_LOCK(&so->so_rcv);
314				goto errout;
315			}
316		} else {
317			ctl_ha_evt(softc, wire_hdr.channel,
318			    CTL_HA_EVT_MSG_RECV, wire_hdr.length);
319			wire_hdr.length = 0;
320		}
321	}
322
323errout:
324	softc->ha_receiving = 0;
325	wakeup(&softc->ha_receiving);
326	SOCKBUF_UNLOCK(&so->so_rcv);
327	ctl_ha_conn_wake(softc);
328	kthread_exit();
329}
330
331static void
332ctl_ha_send(struct ha_softc *softc)
333{
334	struct socket *so = softc->ha_so;
335	int error;
336
337	while (1) {
338		if (softc->ha_sending == NULL) {
339			mtx_lock(&softc->ha_lock);
340			softc->ha_sending = mbufq_dequeue(&softc->ha_sendq);
341			mtx_unlock(&softc->ha_lock);
342			if (softc->ha_sending == NULL) {
343				so->so_snd.sb_lowat = so->so_snd.sb_hiwat + 1;
344				break;
345			}
346		}
347		SOCKBUF_LOCK(&so->so_snd);
348		if (sbspace(&so->so_snd) < softc->ha_sending->m_pkthdr.len) {
349			so->so_snd.sb_lowat = softc->ha_sending->m_pkthdr.len;
350			SOCKBUF_UNLOCK(&so->so_snd);
351			break;
352		}
353		SOCKBUF_UNLOCK(&so->so_snd);
354		error = sosend(softc->ha_so, NULL, NULL, softc->ha_sending,
355		    NULL, MSG_DONTWAIT, curthread);
356		softc->ha_sending = NULL;
357		if (error != 0) {
358			printf("%s: sosend() error %d\n", __func__, error);
359			return;
360		}
361	};
362}
363
364static void
365ctl_ha_sock_setup(struct ha_softc *softc)
366{
367	struct sockopt opt;
368	struct socket *so = softc->ha_so;
369	int error, val;
370
371	val = 1024 * 1024;
372	error = soreserve(so, val, val);
373	if (error)
374		printf("%s: soreserve failed %d\n", __func__, error);
375
376	SOCKBUF_LOCK(&so->so_rcv);
377	so->so_rcv.sb_lowat = sizeof(struct ha_msg_wire);
378	soupcall_set(so, SO_RCV, ctl_ha_rupcall, softc);
379	SOCKBUF_UNLOCK(&so->so_rcv);
380	SOCKBUF_LOCK(&so->so_snd);
381	so->so_snd.sb_lowat = sizeof(struct ha_msg_wire);
382	soupcall_set(so, SO_SND, ctl_ha_supcall, softc);
383	SOCKBUF_UNLOCK(&so->so_snd);
384
385	bzero(&opt, sizeof(struct sockopt));
386	opt.sopt_dir = SOPT_SET;
387	opt.sopt_level = SOL_SOCKET;
388	opt.sopt_name = SO_KEEPALIVE;
389	opt.sopt_val = &val;
390	opt.sopt_valsize = sizeof(val);
391	val = 1;
392	error = sosetopt(so, &opt);
393	if (error)
394		printf("%s: KEEPALIVE setting failed %d\n", __func__, error);
395
396	opt.sopt_level = IPPROTO_TCP;
397	opt.sopt_name = TCP_NODELAY;
398	val = 1;
399	error = sosetopt(so, &opt);
400	if (error)
401		printf("%s: NODELAY setting failed %d\n", __func__, error);
402
403	opt.sopt_name = TCP_KEEPINIT;
404	val = 3;
405	error = sosetopt(so, &opt);
406	if (error)
407		printf("%s: KEEPINIT setting failed %d\n", __func__, error);
408
409	opt.sopt_name = TCP_KEEPIDLE;
410	val = 1;
411	error = sosetopt(so, &opt);
412	if (error)
413		printf("%s: KEEPIDLE setting failed %d\n", __func__, error);
414
415	opt.sopt_name = TCP_KEEPINTVL;
416	val = 1;
417	error = sosetopt(so, &opt);
418	if (error)
419		printf("%s: KEEPINTVL setting failed %d\n", __func__, error);
420
421	opt.sopt_name = TCP_KEEPCNT;
422	val = 5;
423	error = sosetopt(so, &opt);
424	if (error)
425		printf("%s: KEEPCNT setting failed %d\n", __func__, error);
426}
427
428static int
429ctl_ha_connect(struct ha_softc *softc)
430{
431	struct thread *td = curthread;
432	struct socket *so;
433	int error;
434
435	/* Create the socket */
436	error = socreate(PF_INET, &so, SOCK_STREAM,
437	    IPPROTO_TCP, td->td_ucred, td);
438	if (error != 0) {
439		printf("%s: socreate() error %d\n", __func__, error);
440		return (error);
441	}
442	softc->ha_so = so;
443	ctl_ha_sock_setup(softc);
444
445	error = soconnect(so, (struct sockaddr *)&softc->ha_peer_in, td);
446	if (error != 0) {
447		printf("%s: soconnect() error %d\n", __func__, error);
448		goto out;
449	}
450	return (0);
451
452out:
453	ctl_ha_close(softc);
454	return (error);
455}
456
457static int
458ctl_ha_accept(struct ha_softc *softc)
459{
460	struct socket *so;
461	struct sockaddr *sap;
462	int error;
463
464	ACCEPT_LOCK();
465	if (softc->ha_lso->so_rcv.sb_state & SBS_CANTRCVMORE)
466		softc->ha_lso->so_error = ECONNABORTED;
467	if (softc->ha_lso->so_error) {
468		error = softc->ha_lso->so_error;
469		softc->ha_lso->so_error = 0;
470		ACCEPT_UNLOCK();
471		printf("%s: socket error %d\n", __func__, error);
472		goto out;
473	}
474	so = TAILQ_FIRST(&softc->ha_lso->so_comp);
475	if (so == NULL) {
476		ACCEPT_UNLOCK();
477		return (EWOULDBLOCK);
478	}
479	KASSERT(!(so->so_qstate & SQ_INCOMP), ("accept1: so SQ_INCOMP"));
480	KASSERT(so->so_qstate & SQ_COMP, ("accept1: so not SQ_COMP"));
481
482	/*
483	 * Before changing the flags on the socket, we have to bump the
484	 * reference count.  Otherwise, if the protocol calls sofree(),
485	 * the socket will be released due to a zero refcount.
486	 */
487	SOCK_LOCK(so);			/* soref() and so_state update */
488	soref(so);			/* file descriptor reference */
489
490	TAILQ_REMOVE(&softc->ha_lso->so_comp, so, so_list);
491	softc->ha_lso->so_qlen--;
492	so->so_state |= SS_NBIO;
493	so->so_qstate &= ~SQ_COMP;
494	so->so_head = NULL;
495
496	SOCK_UNLOCK(so);
497	ACCEPT_UNLOCK();
498
499	sap = NULL;
500	error = soaccept(so, &sap);
501	if (error != 0) {
502		printf("%s: soaccept() error %d\n", __func__, error);
503		if (sap != NULL)
504			free(sap, M_SONAME);
505		goto out;
506	}
507	if (sap != NULL)
508		free(sap, M_SONAME);
509	softc->ha_so = so;
510	ctl_ha_sock_setup(softc);
511	return (0);
512
513out:
514	ctl_ha_lclose(softc);
515	return (error);
516}
517
518static int
519ctl_ha_listen(struct ha_softc *softc)
520{
521	struct thread *td = curthread;
522	struct sockopt opt;
523	int error, val;
524
525	/* Create the socket */
526	if (softc->ha_lso == NULL) {
527		error = socreate(PF_INET, &softc->ha_lso, SOCK_STREAM,
528		    IPPROTO_TCP, td->td_ucred, td);
529		if (error != 0) {
530			printf("%s: socreate() error %d\n", __func__, error);
531			return (error);
532		}
533		bzero(&opt, sizeof(struct sockopt));
534		opt.sopt_dir = SOPT_SET;
535		opt.sopt_level = SOL_SOCKET;
536		opt.sopt_name = SO_REUSEADDR;
537		opt.sopt_val = &val;
538		opt.sopt_valsize = sizeof(val);
539		val = 1;
540		error = sosetopt(softc->ha_lso, &opt);
541		if (error) {
542			printf("%s: REUSEADDR setting failed %d\n",
543			    __func__, error);
544		}
545		bzero(&opt, sizeof(struct sockopt));
546		opt.sopt_dir = SOPT_SET;
547		opt.sopt_level = SOL_SOCKET;
548		opt.sopt_name = SO_REUSEPORT;
549		opt.sopt_val = &val;
550		opt.sopt_valsize = sizeof(val);
551		val = 1;
552		error = sosetopt(softc->ha_lso, &opt);
553		if (error) {
554			printf("%s: REUSEPORT setting failed %d\n",
555			    __func__, error);
556		}
557		SOCKBUF_LOCK(&softc->ha_lso->so_rcv);
558		soupcall_set(softc->ha_lso, SO_RCV, ctl_ha_lupcall, softc);
559		SOCKBUF_UNLOCK(&softc->ha_lso->so_rcv);
560	}
561
562	error = sobind(softc->ha_lso, (struct sockaddr *)&softc->ha_peer_in, td);
563	if (error != 0) {
564		printf("%s: sobind() error %d\n", __func__, error);
565		goto out;
566	}
567	error = solisten(softc->ha_lso, 1, td);
568	if (error != 0) {
569		printf("%s: solisten() error %d\n", __func__, error);
570		goto out;
571	}
572	return (0);
573
574out:
575	ctl_ha_lclose(softc);
576	return (error);
577}
578
579static void
580ctl_ha_conn_thread(void *arg)
581{
582	struct ha_softc *softc = arg;
583	int error;
584
585	while (1) {
586		if (softc->ha_disconnect || softc->ha_shutdown) {
587			ctl_ha_close(softc);
588			if (softc->ha_disconnect == 2 || softc->ha_shutdown)
589				ctl_ha_lclose(softc);
590			softc->ha_disconnect = 0;
591			if (softc->ha_shutdown)
592				break;
593		} else if (softc->ha_so != NULL &&
594		    (softc->ha_so->so_error ||
595		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
596			ctl_ha_close(softc);
597		if (softc->ha_so == NULL) {
598			if (softc->ha_lso != NULL)
599				ctl_ha_accept(softc);
600			else if (softc->ha_listen)
601				ctl_ha_listen(softc);
602			else if (softc->ha_connect)
603				ctl_ha_connect(softc);
604		}
605		if (softc->ha_so != NULL) {
606			if (softc->ha_connected == 0 &&
607			    softc->ha_so->so_error == 0 &&
608			    (softc->ha_so->so_state & SS_ISCONNECTING) == 0) {
609				softc->ha_connected = 1;
610				ctl_ha_evt(softc, CTL_HA_CHAN_MAX,
611				    CTL_HA_EVT_LINK_CHANGE,
612				    CTL_HA_LINK_ONLINE);
613				softc->ha_receiving = 1;
614				error = kproc_kthread_add(ctl_ha_rx_thread,
615				    softc, &softc->ha_ctl_softc->ctl_proc,
616				    NULL, 0, 0, "ctl", "ha_rx");
617				if (error != 0) {
618					printf("Error creating CTL HA rx thread!\n");
619					softc->ha_receiving = 0;
620					softc->ha_disconnect = 1;
621				}
622			}
623			ctl_ha_send(softc);
624		}
625		mtx_lock(&softc->ha_lock);
626		if (softc->ha_so != NULL &&
627		    (softc->ha_so->so_error ||
628		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
629			;
630		else if (!softc->ha_wakeup)
631			msleep(&softc->ha_wakeup, &softc->ha_lock, 0, "-", hz);
632		softc->ha_wakeup = 0;
633		mtx_unlock(&softc->ha_lock);
634	}
635	mtx_lock(&softc->ha_lock);
636	softc->ha_shutdown = 2;
637	wakeup(&softc->ha_wakeup);
638	mtx_unlock(&softc->ha_lock);
639	kthread_exit();
640}
641
642static int
643ctl_ha_peer_sysctl(SYSCTL_HANDLER_ARGS)
644{
645	struct ha_softc *softc = (struct ha_softc *)arg1;
646	struct sockaddr_in *sa;
647	int error, b1, b2, b3, b4, p, num;
648	char buf[128];
649
650	strlcpy(buf, softc->ha_peer, sizeof(buf));
651	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
652	if ((error != 0) || (req->newptr == NULL) ||
653	    strncmp(buf, softc->ha_peer, sizeof(buf)) == 0)
654		return (error);
655
656	sa = &softc->ha_peer_in;
657	mtx_lock(&softc->ha_lock);
658	if ((num = sscanf(buf, "connect %d.%d.%d.%d:%d",
659	    &b1, &b2, &b3, &b4, &p)) >= 4) {
660		softc->ha_connect = 1;
661		softc->ha_listen = 0;
662	} else if ((num = sscanf(buf, "listen %d.%d.%d.%d:%d",
663	    &b1, &b2, &b3, &b4, &p)) >= 4) {
664		softc->ha_connect = 0;
665		softc->ha_listen = 1;
666	} else {
667		softc->ha_connect = 0;
668		softc->ha_listen = 0;
669		if (buf[0] != 0) {
670			buf[0] = 0;
671			error = EINVAL;
672		}
673	}
674	strlcpy(softc->ha_peer, buf, sizeof(softc->ha_peer));
675	if (softc->ha_connect || softc->ha_listen) {
676		memset(sa, 0, sizeof(*sa));
677		sa->sin_len = sizeof(struct sockaddr_in);
678		sa->sin_family = AF_INET;
679		sa->sin_port = htons((num >= 5) ? p : 999);
680		sa->sin_addr.s_addr =
681		    htonl((b1 << 24) + (b2 << 16) + (b3 << 8) + b4);
682	}
683	softc->ha_disconnect = 2;
684	softc->ha_wakeup = 1;
685	mtx_unlock(&softc->ha_lock);
686	wakeup(&softc->ha_wakeup);
687	return (error);
688}
689
690ctl_ha_status
691ctl_ha_msg_register(ctl_ha_channel channel, ctl_evt_handler handler)
692{
693	struct ha_softc *softc = &ha_softc;
694
695	KASSERT(channel < CTL_HA_CHAN_MAX,
696	    ("Wrong CTL HA channel %d", channel));
697	softc->ha_handler[channel] = handler;
698	return (CTL_HA_STATUS_SUCCESS);
699}
700
701ctl_ha_status
702ctl_ha_msg_deregister(ctl_ha_channel channel)
703{
704	struct ha_softc *softc = &ha_softc;
705
706	KASSERT(channel < CTL_HA_CHAN_MAX,
707	    ("Wrong CTL HA channel %d", channel));
708	softc->ha_handler[channel] = NULL;
709	return (CTL_HA_STATUS_SUCCESS);
710}
711
712/*
713 * Receive a message of the specified size.
714 */
715ctl_ha_status
716ctl_ha_msg_recv(ctl_ha_channel channel, void *addr, size_t len,
717		int wait)
718{
719	struct ha_softc *softc = &ha_softc;
720	struct uio uio;
721	struct iovec iov;
722	int error, flags;
723
724	if (!softc->ha_connected)
725		return (CTL_HA_STATUS_DISCONNECT);
726
727	iov.iov_base = addr;
728	iov.iov_len = len;
729	uio.uio_iov = &iov;
730	uio.uio_iovcnt = 1;
731	uio.uio_rw = UIO_READ;
732	uio.uio_segflg = UIO_SYSSPACE;
733	uio.uio_td = curthread;
734	uio.uio_resid = len;
735	flags = wait ? 0 : MSG_DONTWAIT;
736	error = soreceive(softc->ha_so, NULL, &uio, NULL, NULL, &flags);
737	if (error == 0)
738		return (CTL_HA_STATUS_SUCCESS);
739
740	/* Consider all errors fatal for HA sanity. */
741	mtx_lock(&softc->ha_lock);
742	if (softc->ha_connected) {
743		softc->ha_disconnect = 1;
744		softc->ha_wakeup = 1;
745		wakeup(&softc->ha_wakeup);
746	}
747	mtx_unlock(&softc->ha_lock);
748	return (CTL_HA_STATUS_ERROR);
749}
750
751/*
752 * Send a message of the specified size.
753 */
754ctl_ha_status
755ctl_ha_msg_send2(ctl_ha_channel channel, const void *addr, size_t len,
756    const void *addr2, size_t len2, int wait)
757{
758	struct ha_softc *softc = &ha_softc;
759	struct mbuf *mb, *newmb;
760	struct ha_msg_wire hdr;
761	size_t copylen, off;
762
763	if (!softc->ha_connected)
764		return (CTL_HA_STATUS_DISCONNECT);
765
766	newmb = m_getm2(NULL, sizeof(hdr) + len + len2, wait, MT_DATA,
767	    M_PKTHDR);
768	if (newmb == NULL) {
769		/* Consider all errors fatal for HA sanity. */
770		mtx_lock(&softc->ha_lock);
771		if (softc->ha_connected) {
772			softc->ha_disconnect = 1;
773			softc->ha_wakeup = 1;
774			wakeup(&softc->ha_wakeup);
775		}
776		mtx_unlock(&softc->ha_lock);
777		printf("%s: Can't allocate mbuf chain\n", __func__);
778		return (CTL_HA_STATUS_ERROR);
779	}
780	hdr.channel = channel;
781	hdr.length = len + len2;
782	mb = newmb;
783	memcpy(mtodo(mb, 0), &hdr, sizeof(hdr));
784	mb->m_len += sizeof(hdr);
785	off = 0;
786	for (; mb != NULL && off < len; mb = mb->m_next) {
787		copylen = min(M_TRAILINGSPACE(mb), len - off);
788		memcpy(mtodo(mb, mb->m_len), (const char *)addr + off, copylen);
789		mb->m_len += copylen;
790		off += copylen;
791		if (off == len)
792			break;
793	}
794	KASSERT(off == len, ("%s: off (%zu) != len (%zu)", __func__,
795	    off, len));
796	off = 0;
797	for (; mb != NULL && off < len2; mb = mb->m_next) {
798		copylen = min(M_TRAILINGSPACE(mb), len2 - off);
799		memcpy(mtodo(mb, mb->m_len), (const char *)addr2 + off, copylen);
800		mb->m_len += copylen;
801		off += copylen;
802	}
803	KASSERT(off == len2, ("%s: off (%zu) != len2 (%zu)", __func__,
804	    off, len2));
805	newmb->m_pkthdr.len = sizeof(hdr) + len + len2;
806
807	mtx_lock(&softc->ha_lock);
808	if (!softc->ha_connected) {
809		mtx_unlock(&softc->ha_lock);
810		m_freem(newmb);
811		return (CTL_HA_STATUS_DISCONNECT);
812	}
813	mbufq_enqueue(&softc->ha_sendq, newmb);
814	softc->ha_wakeup = 1;
815	mtx_unlock(&softc->ha_lock);
816	wakeup(&softc->ha_wakeup);
817	return (CTL_HA_STATUS_SUCCESS);
818}
819
820ctl_ha_status
821ctl_ha_msg_send(ctl_ha_channel channel, const void *addr, size_t len,
822    int wait)
823{
824
825	return (ctl_ha_msg_send2(channel, addr, len, NULL, 0, wait));
826}
827
828ctl_ha_status
829ctl_ha_msg_abort(ctl_ha_channel channel)
830{
831	struct ha_softc *softc = &ha_softc;
832
833	mtx_lock(&softc->ha_lock);
834	softc->ha_disconnect = 1;
835	softc->ha_wakeup = 1;
836	mtx_unlock(&softc->ha_lock);
837	wakeup(&softc->ha_wakeup);
838	return (CTL_HA_STATUS_SUCCESS);
839}
840
841/*
842 * Allocate a data transfer request structure.
843 */
844struct ctl_ha_dt_req *
845ctl_dt_req_alloc(void)
846{
847
848	return (malloc(sizeof(struct ctl_ha_dt_req), M_CTL, M_WAITOK | M_ZERO));
849}
850
851/*
852 * Free a data transfer request structure.
853 */
854void
855ctl_dt_req_free(struct ctl_ha_dt_req *req)
856{
857
858	free(req, M_CTL);
859}
860
861/*
862 * Issue a DMA request for a single buffer.
863 */
864ctl_ha_status
865ctl_dt_single(struct ctl_ha_dt_req *req)
866{
867	struct ha_softc *softc = &ha_softc;
868	struct ha_dt_msg_wire wire_dt;
869	ctl_ha_status status;
870
871	wire_dt.command = req->command;
872	wire_dt.size = req->size;
873	wire_dt.local = req->local;
874	wire_dt.remote = req->remote;
875	if (req->command == CTL_HA_DT_CMD_READ && req->callback != NULL) {
876		mtx_lock(&softc->ha_lock);
877		TAILQ_INSERT_TAIL(&softc->ha_dts, req, links);
878		mtx_unlock(&softc->ha_lock);
879		ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt, sizeof(wire_dt),
880		    M_WAITOK);
881		return (CTL_HA_STATUS_WAIT);
882	}
883	if (req->command == CTL_HA_DT_CMD_READ) {
884		status = ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt,
885		    sizeof(wire_dt), M_WAITOK);
886	} else {
887		status = ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
888		    sizeof(wire_dt), req->local, req->size, M_WAITOK);
889	}
890	return (status);
891}
892
893static void
894ctl_dt_event_handler(ctl_ha_channel channel, ctl_ha_event event, int param)
895{
896	struct ha_softc *softc = &ha_softc;
897	struct ctl_ha_dt_req *req;
898	ctl_ha_status isc_status;
899
900	if (event == CTL_HA_EVT_MSG_RECV) {
901		struct ha_dt_msg_wire wire_dt;
902		uint8_t *tmp;
903		int size;
904
905		size = min(sizeof(wire_dt), param);
906		isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA, &wire_dt,
907					     size, M_WAITOK);
908		if (isc_status != CTL_HA_STATUS_SUCCESS) {
909			printf("%s: Error receiving message: %d\n",
910			    __func__, isc_status);
911			return;
912		}
913
914		if (wire_dt.command == CTL_HA_DT_CMD_READ) {
915			wire_dt.command = CTL_HA_DT_CMD_WRITE;
916			tmp = wire_dt.local;
917			wire_dt.local = wire_dt.remote;
918			wire_dt.remote = tmp;
919			ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
920			    sizeof(wire_dt), wire_dt.local, wire_dt.size,
921			    M_WAITOK);
922		} else if (wire_dt.command == CTL_HA_DT_CMD_WRITE) {
923			isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA,
924			    wire_dt.remote, wire_dt.size, M_WAITOK);
925			mtx_lock(&softc->ha_lock);
926			TAILQ_FOREACH(req, &softc->ha_dts, links) {
927				if (req->local == wire_dt.remote) {
928					TAILQ_REMOVE(&softc->ha_dts, req, links);
929					break;
930				}
931			}
932			mtx_unlock(&softc->ha_lock);
933			if (req) {
934				req->ret = isc_status;
935				req->callback(req);
936			}
937		}
938	} else if (event == CTL_HA_EVT_LINK_CHANGE) {
939		CTL_DEBUG_PRINT(("%s: Link state change to %d\n", __func__,
940		    param));
941		if (param != CTL_HA_LINK_ONLINE) {
942			mtx_lock(&softc->ha_lock);
943			while ((req = TAILQ_FIRST(&softc->ha_dts)) != NULL) {
944				TAILQ_REMOVE(&softc->ha_dts, req, links);
945				mtx_unlock(&softc->ha_lock);
946				req->ret = CTL_HA_STATUS_DISCONNECT;
947				req->callback(req);
948				mtx_lock(&softc->ha_lock);
949			}
950			mtx_unlock(&softc->ha_lock);
951		}
952	} else {
953		printf("%s: Unknown event %d\n", __func__, event);
954	}
955}
956
957
958ctl_ha_status
959ctl_ha_msg_init(struct ctl_softc *ctl_softc)
960{
961	struct ha_softc *softc = &ha_softc;
962	int error;
963
964	softc->ha_ctl_softc = ctl_softc;
965	mtx_init(&softc->ha_lock, "CTL HA mutex", NULL, MTX_DEF);
966	mbufq_init(&softc->ha_sendq, INT_MAX);
967	TAILQ_INIT(&softc->ha_dts);
968	error = kproc_kthread_add(ctl_ha_conn_thread, softc,
969	    &ctl_softc->ctl_proc, NULL, 0, 0, "ctl", "ha_tx");
970	if (error != 0) {
971		printf("error creating CTL HA connection thread!\n");
972		mtx_destroy(&softc->ha_lock);
973		return (CTL_HA_STATUS_ERROR);
974	}
975	softc->ha_shutdown_eh = EVENTHANDLER_REGISTER(shutdown_pre_sync,
976	    ctl_ha_msg_shutdown, ctl_softc, SHUTDOWN_PRI_FIRST);
977	SYSCTL_ADD_PROC(&ctl_softc->sysctl_ctx,
978	    SYSCTL_CHILDREN(ctl_softc->sysctl_tree),
979	    OID_AUTO, "ha_peer", CTLTYPE_STRING | CTLFLAG_RWTUN,
980	    softc, 0, ctl_ha_peer_sysctl, "A", "HA peer connection method");
981
982	if (ctl_ha_msg_register(CTL_HA_CHAN_DATA, ctl_dt_event_handler)
983	    != CTL_HA_STATUS_SUCCESS) {
984		printf("%s: ctl_ha_msg_register failed.\n", __func__);
985	}
986
987	return (CTL_HA_STATUS_SUCCESS);
988};
989
990void
991ctl_ha_msg_shutdown(struct ctl_softc *ctl_softc)
992{
993	struct ha_softc *softc = &ha_softc;
994
995	/* Disconnect and shutdown threads. */
996	mtx_lock(&softc->ha_lock);
997	if (softc->ha_shutdown < 2) {
998		softc->ha_shutdown = 1;
999		softc->ha_wakeup = 1;
1000		wakeup(&softc->ha_wakeup);
1001		while (softc->ha_shutdown < 2) {
1002			msleep(&softc->ha_wakeup, &softc->ha_lock, 0,
1003			    "shutdown", hz);
1004		}
1005	}
1006	mtx_unlock(&softc->ha_lock);
1007};
1008
1009ctl_ha_status
1010ctl_ha_msg_destroy(struct ctl_softc *ctl_softc)
1011{
1012	struct ha_softc *softc = &ha_softc;
1013
1014	if (softc->ha_shutdown_eh != NULL) {
1015		EVENTHANDLER_DEREGISTER(shutdown_pre_sync,
1016		    softc->ha_shutdown_eh);
1017		softc->ha_shutdown_eh = NULL;
1018	}
1019
1020	ctl_ha_msg_shutdown(ctl_softc);	/* Just in case. */
1021
1022	if (ctl_ha_msg_deregister(CTL_HA_CHAN_DATA) != CTL_HA_STATUS_SUCCESS)
1023		printf("%s: ctl_ha_msg_deregister failed.\n", __func__);
1024
1025	mtx_destroy(&softc->ha_lock);
1026	return (CTL_HA_STATUS_SUCCESS);
1027};
1028