xref: /freebsd/sys/cam/ctl/ctl_ha.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2015 Alexander Motin <mav@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer,
12  *    without modification, immediately at the beginning of the file.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/condvar.h>
32 #include <sys/conf.h>
33 #include <sys/eventhandler.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/proc.h>
43 #include <sys/queue.h>
44 #include <sys/socket.h>
45 #include <sys/socketvar.h>
46 #include <sys/sysctl.h>
47 #include <sys/systm.h>
48 #include <sys/uio.h>
49 #include <netinet/in.h>
50 #include <netinet/tcp.h>
51 #include <vm/uma.h>
52 
53 #include <cam/cam.h>
54 #include <cam/scsi/scsi_all.h>
55 #include <cam/scsi/scsi_da.h>
56 #include <cam/ctl/ctl_io.h>
57 #include <cam/ctl/ctl.h>
58 #include <cam/ctl/ctl_frontend.h>
59 #include <cam/ctl/ctl_util.h>
60 #include <cam/ctl/ctl_backend.h>
61 #include <cam/ctl/ctl_ioctl.h>
62 #include <cam/ctl/ctl_ha.h>
63 #include <cam/ctl/ctl_private.h>
64 #include <cam/ctl/ctl_debug.h>
65 #include <cam/ctl/ctl_error.h>
66 
67 struct ha_msg_wire {
68 	uint32_t	 channel;
69 	uint32_t	 length;
70 };
71 
72 struct ha_dt_msg_wire {
73 	ctl_ha_dt_cmd	command;
74 	uint32_t	size;
75 	uint8_t		*local;
76 	uint8_t		*remote;
77 };
78 
79 struct ha_softc {
80 	struct ctl_softc *ha_ctl_softc;
81 	ctl_evt_handler	 ha_handler[CTL_HA_CHAN_MAX];
82 	char		 ha_peer[128];
83 	struct sockaddr_in  ha_peer_in;
84 	struct socket	*ha_lso;
85 	struct socket	*ha_so;
86 	struct mbufq	 ha_sendq;
87 	struct mbuf	*ha_sending;
88 	struct mtx	 ha_lock;
89 	int		 ha_connect;
90 	int		 ha_listen;
91 	int		 ha_connected;
92 	int		 ha_receiving;
93 	int		 ha_wakeup;
94 	int		 ha_disconnect;
95 	int		 ha_shutdown;
96 	eventhandler_tag ha_shutdown_eh;
97 	TAILQ_HEAD(, ctl_ha_dt_req) ha_dts;
98 } ha_softc;
99 
100 static void
101 ctl_ha_conn_wake(struct ha_softc *softc)
102 {
103 
104 	mtx_lock(&softc->ha_lock);
105 	softc->ha_wakeup = 1;
106 	mtx_unlock(&softc->ha_lock);
107 	wakeup(&softc->ha_wakeup);
108 }
109 
110 static int
111 ctl_ha_lupcall(struct socket *so, void *arg, int waitflag)
112 {
113 	struct ha_softc *softc = arg;
114 
115 	ctl_ha_conn_wake(softc);
116 	return (SU_OK);
117 }
118 
119 static int
120 ctl_ha_rupcall(struct socket *so, void *arg, int waitflag)
121 {
122 	struct ha_softc *softc = arg;
123 
124 	wakeup(&softc->ha_receiving);
125 	return (SU_OK);
126 }
127 
128 static int
129 ctl_ha_supcall(struct socket *so, void *arg, int waitflag)
130 {
131 	struct ha_softc *softc = arg;
132 
133 	ctl_ha_conn_wake(softc);
134 	return (SU_OK);
135 }
136 
137 static void
138 ctl_ha_evt(struct ha_softc *softc, ctl_ha_channel ch, ctl_ha_event evt,
139     int param)
140 {
141 	int i;
142 
143 	if (ch < CTL_HA_CHAN_MAX) {
144 		if (softc->ha_handler[ch])
145 			softc->ha_handler[ch](ch, evt, param);
146 		return;
147 	}
148 	for (i = 0; i < CTL_HA_CHAN_MAX; i++) {
149 		if (softc->ha_handler[i])
150 			softc->ha_handler[i](i, evt, param);
151 	}
152 }
153 
154 static void
155 ctl_ha_close(struct ha_softc *softc)
156 {
157 	struct socket *so = softc->ha_so;
158 	int report = 0;
159 
160 	if (softc->ha_connected || softc->ha_disconnect) {
161 		softc->ha_connected = 0;
162 		mbufq_drain(&softc->ha_sendq);
163 		m_freem(softc->ha_sending);
164 		softc->ha_sending = NULL;
165 		report = 1;
166 	}
167 	if (so) {
168 		SOCKBUF_LOCK(&so->so_rcv);
169 		soupcall_clear(so, SO_RCV);
170 		while (softc->ha_receiving) {
171 			wakeup(&softc->ha_receiving);
172 			msleep(&softc->ha_receiving, SOCKBUF_MTX(&so->so_rcv),
173 			    0, "ha_rx exit", 0);
174 		}
175 		SOCKBUF_UNLOCK(&so->so_rcv);
176 		SOCKBUF_LOCK(&so->so_snd);
177 		soupcall_clear(so, SO_SND);
178 		SOCKBUF_UNLOCK(&so->so_snd);
179 		softc->ha_so = NULL;
180 		if (softc->ha_connect)
181 			pause("reconnect", hz / 2);
182 		soclose(so);
183 	}
184 	if (report) {
185 		ctl_ha_evt(softc, CTL_HA_CHAN_MAX, CTL_HA_EVT_LINK_CHANGE,
186 		    (softc->ha_connect || softc->ha_listen) ?
187 		    CTL_HA_LINK_UNKNOWN : CTL_HA_LINK_OFFLINE);
188 	}
189 }
190 
191 static void
192 ctl_ha_lclose(struct ha_softc *softc)
193 {
194 
195 	if (softc->ha_lso) {
196 		if (SOLISTENING(softc->ha_lso)) {
197 			SOLISTEN_LOCK(softc->ha_lso);
198 			solisten_upcall_set(softc->ha_lso, NULL, NULL);
199 			SOLISTEN_UNLOCK(softc->ha_lso);
200 		}
201 		soclose(softc->ha_lso);
202 		softc->ha_lso = NULL;
203 	}
204 }
205 
206 static void
207 ctl_ha_rx_thread(void *arg)
208 {
209 	struct ha_softc *softc = arg;
210 	struct socket *so = softc->ha_so;
211 	struct ha_msg_wire wire_hdr;
212 	struct uio uio;
213 	struct iovec iov;
214 	int error, flags, next;
215 
216 	bzero(&wire_hdr, sizeof(wire_hdr));
217 	while (1) {
218 		if (wire_hdr.length > 0)
219 			next = wire_hdr.length;
220 		else
221 			next = sizeof(wire_hdr);
222 		SOCKBUF_LOCK(&so->so_rcv);
223 		while (sbavail(&so->so_rcv) < next || softc->ha_disconnect) {
224 			if (softc->ha_connected == 0 || softc->ha_disconnect ||
225 			    so->so_error ||
226 			    (so->so_rcv.sb_state & SBS_CANTRCVMORE)) {
227 				goto errout;
228 			}
229 			so->so_rcv.sb_lowat = next;
230 			msleep(&softc->ha_receiving, SOCKBUF_MTX(&so->so_rcv),
231 			    0, "-", 0);
232 		}
233 		SOCKBUF_UNLOCK(&so->so_rcv);
234 
235 		if (wire_hdr.length == 0) {
236 			iov.iov_base = &wire_hdr;
237 			iov.iov_len = sizeof(wire_hdr);
238 			uio.uio_iov = &iov;
239 			uio.uio_iovcnt = 1;
240 			uio.uio_rw = UIO_READ;
241 			uio.uio_segflg = UIO_SYSSPACE;
242 			uio.uio_td = curthread;
243 			uio.uio_resid = sizeof(wire_hdr);
244 			flags = MSG_DONTWAIT;
245 			error = soreceive(softc->ha_so, NULL, &uio, NULL,
246 			    NULL, &flags);
247 			if (error != 0) {
248 				printf("%s: header receive error %d\n",
249 				    __func__, error);
250 				SOCKBUF_LOCK(&so->so_rcv);
251 				goto errout;
252 			}
253 		} else {
254 			ctl_ha_evt(softc, wire_hdr.channel,
255 			    CTL_HA_EVT_MSG_RECV, wire_hdr.length);
256 			wire_hdr.length = 0;
257 		}
258 	}
259 
260 errout:
261 	softc->ha_receiving = 0;
262 	wakeup(&softc->ha_receiving);
263 	SOCKBUF_UNLOCK(&so->so_rcv);
264 	ctl_ha_conn_wake(softc);
265 	kthread_exit();
266 }
267 
268 static void
269 ctl_ha_send(struct ha_softc *softc)
270 {
271 	struct socket *so = softc->ha_so;
272 	int error;
273 
274 	while (1) {
275 		if (softc->ha_sending == NULL) {
276 			mtx_lock(&softc->ha_lock);
277 			softc->ha_sending = mbufq_dequeue(&softc->ha_sendq);
278 			mtx_unlock(&softc->ha_lock);
279 			if (softc->ha_sending == NULL) {
280 				so->so_snd.sb_lowat = so->so_snd.sb_hiwat + 1;
281 				break;
282 			}
283 		}
284 		SOCKBUF_LOCK(&so->so_snd);
285 		if (sbspace(&so->so_snd) < softc->ha_sending->m_pkthdr.len) {
286 			so->so_snd.sb_lowat = softc->ha_sending->m_pkthdr.len;
287 			SOCKBUF_UNLOCK(&so->so_snd);
288 			break;
289 		}
290 		SOCKBUF_UNLOCK(&so->so_snd);
291 		error = sosend(softc->ha_so, NULL, NULL, softc->ha_sending,
292 		    NULL, MSG_DONTWAIT, curthread);
293 		softc->ha_sending = NULL;
294 		if (error != 0) {
295 			printf("%s: sosend() error %d\n", __func__, error);
296 			return;
297 		}
298 	}
299 }
300 
301 static void
302 ctl_ha_sock_setup(struct ha_softc *softc)
303 {
304 	struct sockopt opt;
305 	struct socket *so = softc->ha_so;
306 	int error, val;
307 
308 	val = 1024 * 1024;
309 	error = soreserve(so, val, val);
310 	if (error)
311 		printf("%s: soreserve failed %d\n", __func__, error);
312 
313 	SOCKBUF_LOCK(&so->so_rcv);
314 	so->so_rcv.sb_lowat = sizeof(struct ha_msg_wire);
315 	soupcall_set(so, SO_RCV, ctl_ha_rupcall, softc);
316 	SOCKBUF_UNLOCK(&so->so_rcv);
317 	SOCKBUF_LOCK(&so->so_snd);
318 	so->so_snd.sb_lowat = sizeof(struct ha_msg_wire);
319 	soupcall_set(so, SO_SND, ctl_ha_supcall, softc);
320 	SOCKBUF_UNLOCK(&so->so_snd);
321 
322 	bzero(&opt, sizeof(struct sockopt));
323 	opt.sopt_dir = SOPT_SET;
324 	opt.sopt_level = SOL_SOCKET;
325 	opt.sopt_name = SO_KEEPALIVE;
326 	opt.sopt_val = &val;
327 	opt.sopt_valsize = sizeof(val);
328 	val = 1;
329 	error = sosetopt(so, &opt);
330 	if (error)
331 		printf("%s: KEEPALIVE setting failed %d\n", __func__, error);
332 
333 	opt.sopt_level = IPPROTO_TCP;
334 	opt.sopt_name = TCP_NODELAY;
335 	val = 1;
336 	error = sosetopt(so, &opt);
337 	if (error)
338 		printf("%s: NODELAY setting failed %d\n", __func__, error);
339 
340 	opt.sopt_name = TCP_KEEPINIT;
341 	val = 3;
342 	error = sosetopt(so, &opt);
343 	if (error)
344 		printf("%s: KEEPINIT setting failed %d\n", __func__, error);
345 
346 	opt.sopt_name = TCP_KEEPIDLE;
347 	val = 1;
348 	error = sosetopt(so, &opt);
349 	if (error)
350 		printf("%s: KEEPIDLE setting failed %d\n", __func__, error);
351 
352 	opt.sopt_name = TCP_KEEPINTVL;
353 	val = 1;
354 	error = sosetopt(so, &opt);
355 	if (error)
356 		printf("%s: KEEPINTVL setting failed %d\n", __func__, error);
357 
358 	opt.sopt_name = TCP_KEEPCNT;
359 	val = 5;
360 	error = sosetopt(so, &opt);
361 	if (error)
362 		printf("%s: KEEPCNT setting failed %d\n", __func__, error);
363 }
364 
365 static int
366 ctl_ha_connect(struct ha_softc *softc)
367 {
368 	struct thread *td = curthread;
369 	struct sockaddr_in sa;
370 	struct socket *so;
371 	int error;
372 
373 	/* Create the socket */
374 	error = socreate(PF_INET, &so, SOCK_STREAM,
375 	    IPPROTO_TCP, td->td_ucred, td);
376 	if (error != 0) {
377 		printf("%s: socreate() error %d\n", __func__, error);
378 		return (error);
379 	}
380 	softc->ha_so = so;
381 	ctl_ha_sock_setup(softc);
382 
383 	memcpy(&sa, &softc->ha_peer_in, sizeof(sa));
384 	error = soconnect(so, (struct sockaddr *)&sa, td);
385 	if (error != 0) {
386 		if (bootverbose)
387 			printf("%s: soconnect() error %d\n", __func__, error);
388 		goto out;
389 	}
390 	return (0);
391 
392 out:
393 	ctl_ha_close(softc);
394 	return (error);
395 }
396 
397 static int
398 ctl_ha_accept(struct ha_softc *softc)
399 {
400 	struct socket *lso, *so;
401 	struct sockaddr *sap;
402 	int error;
403 
404 	lso = softc->ha_lso;
405 	SOLISTEN_LOCK(lso);
406 	error = solisten_dequeue(lso, &so, 0);
407 	if (error == EWOULDBLOCK)
408 		return (error);
409 	if (error) {
410 		printf("%s: socket error %d\n", __func__, error);
411 		goto out;
412 	}
413 
414 	sap = NULL;
415 	error = soaccept(so, &sap);
416 	if (error != 0) {
417 		printf("%s: soaccept() error %d\n", __func__, error);
418 		if (sap != NULL)
419 			free(sap, M_SONAME);
420 		goto out;
421 	}
422 	if (sap != NULL)
423 		free(sap, M_SONAME);
424 	softc->ha_so = so;
425 	ctl_ha_sock_setup(softc);
426 	return (0);
427 
428 out:
429 	ctl_ha_lclose(softc);
430 	return (error);
431 }
432 
433 static int
434 ctl_ha_listen(struct ha_softc *softc)
435 {
436 	struct thread *td = curthread;
437 	struct sockaddr_in sa;
438 	struct sockopt opt;
439 	int error, val;
440 
441 	/* Create the socket */
442 	if (softc->ha_lso == NULL) {
443 		error = socreate(PF_INET, &softc->ha_lso, SOCK_STREAM,
444 		    IPPROTO_TCP, td->td_ucred, td);
445 		if (error != 0) {
446 			printf("%s: socreate() error %d\n", __func__, error);
447 			return (error);
448 		}
449 		bzero(&opt, sizeof(struct sockopt));
450 		opt.sopt_dir = SOPT_SET;
451 		opt.sopt_level = SOL_SOCKET;
452 		opt.sopt_name = SO_REUSEADDR;
453 		opt.sopt_val = &val;
454 		opt.sopt_valsize = sizeof(val);
455 		val = 1;
456 		error = sosetopt(softc->ha_lso, &opt);
457 		if (error) {
458 			printf("%s: REUSEADDR setting failed %d\n",
459 			    __func__, error);
460 		}
461 		bzero(&opt, sizeof(struct sockopt));
462 		opt.sopt_dir = SOPT_SET;
463 		opt.sopt_level = SOL_SOCKET;
464 		opt.sopt_name = SO_REUSEPORT;
465 		opt.sopt_val = &val;
466 		opt.sopt_valsize = sizeof(val);
467 		val = 1;
468 		error = sosetopt(softc->ha_lso, &opt);
469 		if (error) {
470 			printf("%s: REUSEPORT setting failed %d\n",
471 			    __func__, error);
472 		}
473 	}
474 
475 	memcpy(&sa, &softc->ha_peer_in, sizeof(sa));
476 	error = sobind(softc->ha_lso, (struct sockaddr *)&sa, td);
477 	if (error != 0) {
478 		printf("%s: sobind() error %d\n", __func__, error);
479 		goto out;
480 	}
481 	error = solisten(softc->ha_lso, 1, td);
482 	if (error != 0) {
483 		printf("%s: solisten() error %d\n", __func__, error);
484 		goto out;
485 	}
486 	SOLISTEN_LOCK(softc->ha_lso);
487 	softc->ha_lso->so_state |= SS_NBIO;
488 	solisten_upcall_set(softc->ha_lso, ctl_ha_lupcall, softc);
489 	SOLISTEN_UNLOCK(softc->ha_lso);
490 	return (0);
491 
492 out:
493 	ctl_ha_lclose(softc);
494 	return (error);
495 }
496 
497 static void
498 ctl_ha_conn_thread(void *arg)
499 {
500 	struct ha_softc *softc = arg;
501 	int error;
502 
503 	while (1) {
504 		if (softc->ha_disconnect || softc->ha_shutdown) {
505 			ctl_ha_close(softc);
506 			if (softc->ha_disconnect == 2 || softc->ha_shutdown)
507 				ctl_ha_lclose(softc);
508 			softc->ha_disconnect = 0;
509 			if (softc->ha_shutdown)
510 				break;
511 		} else if (softc->ha_so != NULL &&
512 		    (softc->ha_so->so_error ||
513 		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
514 			ctl_ha_close(softc);
515 		if (softc->ha_so == NULL) {
516 			if (softc->ha_lso != NULL)
517 				ctl_ha_accept(softc);
518 			else if (softc->ha_listen)
519 				ctl_ha_listen(softc);
520 			else if (softc->ha_connect)
521 				ctl_ha_connect(softc);
522 		}
523 		if (softc->ha_so != NULL) {
524 			if (softc->ha_connected == 0 &&
525 			    softc->ha_so->so_error == 0 &&
526 			    (softc->ha_so->so_state & SS_ISCONNECTING) == 0) {
527 				softc->ha_connected = 1;
528 				ctl_ha_evt(softc, CTL_HA_CHAN_MAX,
529 				    CTL_HA_EVT_LINK_CHANGE,
530 				    CTL_HA_LINK_ONLINE);
531 				softc->ha_receiving = 1;
532 				error = kproc_kthread_add(ctl_ha_rx_thread,
533 				    softc, &softc->ha_ctl_softc->ctl_proc,
534 				    NULL, 0, 0, "ctl", "ha_rx");
535 				if (error != 0) {
536 					printf("Error creating CTL HA rx thread!\n");
537 					softc->ha_receiving = 0;
538 					softc->ha_disconnect = 1;
539 				}
540 			}
541 			ctl_ha_send(softc);
542 		}
543 		mtx_lock(&softc->ha_lock);
544 		if (softc->ha_so != NULL &&
545 		    (softc->ha_so->so_error ||
546 		     softc->ha_so->so_rcv.sb_state & SBS_CANTRCVMORE))
547 			;
548 		else if (!softc->ha_wakeup)
549 			msleep(&softc->ha_wakeup, &softc->ha_lock, 0, "-", hz);
550 		softc->ha_wakeup = 0;
551 		mtx_unlock(&softc->ha_lock);
552 	}
553 	mtx_lock(&softc->ha_lock);
554 	softc->ha_shutdown = 2;
555 	wakeup(&softc->ha_wakeup);
556 	mtx_unlock(&softc->ha_lock);
557 	kthread_exit();
558 }
559 
560 static int
561 ctl_ha_peer_sysctl(SYSCTL_HANDLER_ARGS)
562 {
563 	struct ha_softc *softc = (struct ha_softc *)arg1;
564 	struct sockaddr_in *sa;
565 	int error, b1, b2, b3, b4, p, num;
566 	char buf[128];
567 
568 	strlcpy(buf, softc->ha_peer, sizeof(buf));
569 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
570 	if ((error != 0) || (req->newptr == NULL) ||
571 	    strncmp(buf, softc->ha_peer, sizeof(buf)) == 0)
572 		return (error);
573 
574 	sa = &softc->ha_peer_in;
575 	mtx_lock(&softc->ha_lock);
576 	if ((num = sscanf(buf, "connect %d.%d.%d.%d:%d",
577 	    &b1, &b2, &b3, &b4, &p)) >= 4) {
578 		softc->ha_connect = 1;
579 		softc->ha_listen = 0;
580 	} else if ((num = sscanf(buf, "listen %d.%d.%d.%d:%d",
581 	    &b1, &b2, &b3, &b4, &p)) >= 4) {
582 		softc->ha_connect = 0;
583 		softc->ha_listen = 1;
584 	} else {
585 		softc->ha_connect = 0;
586 		softc->ha_listen = 0;
587 		if (buf[0] != 0) {
588 			buf[0] = 0;
589 			error = EINVAL;
590 		}
591 	}
592 	strlcpy(softc->ha_peer, buf, sizeof(softc->ha_peer));
593 	if (softc->ha_connect || softc->ha_listen) {
594 		memset(sa, 0, sizeof(*sa));
595 		sa->sin_len = sizeof(struct sockaddr_in);
596 		sa->sin_family = AF_INET;
597 		sa->sin_port = htons((num >= 5) ? p : 999);
598 		sa->sin_addr.s_addr =
599 		    htonl((b1 << 24) + (b2 << 16) + (b3 << 8) + b4);
600 	}
601 	softc->ha_disconnect = 2;
602 	softc->ha_wakeup = 1;
603 	mtx_unlock(&softc->ha_lock);
604 	wakeup(&softc->ha_wakeup);
605 	return (error);
606 }
607 
608 ctl_ha_status
609 ctl_ha_msg_register(ctl_ha_channel channel, ctl_evt_handler handler)
610 {
611 	struct ha_softc *softc = &ha_softc;
612 
613 	KASSERT(channel < CTL_HA_CHAN_MAX,
614 	    ("Wrong CTL HA channel %d", channel));
615 	softc->ha_handler[channel] = handler;
616 	return (CTL_HA_STATUS_SUCCESS);
617 }
618 
619 ctl_ha_status
620 ctl_ha_msg_deregister(ctl_ha_channel channel)
621 {
622 	struct ha_softc *softc = &ha_softc;
623 
624 	KASSERT(channel < CTL_HA_CHAN_MAX,
625 	    ("Wrong CTL HA channel %d", channel));
626 	softc->ha_handler[channel] = NULL;
627 	return (CTL_HA_STATUS_SUCCESS);
628 }
629 
630 /*
631  * Receive a message of the specified size.
632  */
633 ctl_ha_status
634 ctl_ha_msg_recv(ctl_ha_channel channel, void *addr, size_t len,
635 		int wait)
636 {
637 	struct ha_softc *softc = &ha_softc;
638 	struct uio uio;
639 	struct iovec iov;
640 	int error, flags;
641 
642 	if (!softc->ha_connected)
643 		return (CTL_HA_STATUS_DISCONNECT);
644 
645 	iov.iov_base = addr;
646 	iov.iov_len = len;
647 	uio.uio_iov = &iov;
648 	uio.uio_iovcnt = 1;
649 	uio.uio_rw = UIO_READ;
650 	uio.uio_segflg = UIO_SYSSPACE;
651 	uio.uio_td = curthread;
652 	uio.uio_resid = len;
653 	flags = wait ? 0 : MSG_DONTWAIT;
654 	error = soreceive(softc->ha_so, NULL, &uio, NULL, NULL, &flags);
655 	if (error == 0)
656 		return (CTL_HA_STATUS_SUCCESS);
657 
658 	/* Consider all errors fatal for HA sanity. */
659 	mtx_lock(&softc->ha_lock);
660 	if (softc->ha_connected) {
661 		softc->ha_disconnect = 1;
662 		softc->ha_wakeup = 1;
663 		wakeup(&softc->ha_wakeup);
664 	}
665 	mtx_unlock(&softc->ha_lock);
666 	return (CTL_HA_STATUS_ERROR);
667 }
668 
669 /*
670  * Send a message of the specified size.
671  */
672 ctl_ha_status
673 ctl_ha_msg_send2(ctl_ha_channel channel, const void *addr, size_t len,
674     const void *addr2, size_t len2, int wait)
675 {
676 	struct ha_softc *softc = &ha_softc;
677 	struct mbuf *mb, *newmb;
678 	struct ha_msg_wire hdr;
679 	size_t copylen, off;
680 
681 	if (!softc->ha_connected)
682 		return (CTL_HA_STATUS_DISCONNECT);
683 
684 	newmb = m_getm2(NULL, sizeof(hdr) + len + len2, wait, MT_DATA,
685 	    M_PKTHDR);
686 	if (newmb == NULL) {
687 		/* Consider all errors fatal for HA sanity. */
688 		mtx_lock(&softc->ha_lock);
689 		if (softc->ha_connected) {
690 			softc->ha_disconnect = 1;
691 			softc->ha_wakeup = 1;
692 			wakeup(&softc->ha_wakeup);
693 		}
694 		mtx_unlock(&softc->ha_lock);
695 		printf("%s: Can't allocate mbuf chain\n", __func__);
696 		return (CTL_HA_STATUS_ERROR);
697 	}
698 	hdr.channel = channel;
699 	hdr.length = len + len2;
700 	mb = newmb;
701 	memcpy(mtodo(mb, 0), &hdr, sizeof(hdr));
702 	mb->m_len += sizeof(hdr);
703 	off = 0;
704 	for (; mb != NULL && off < len; mb = mb->m_next) {
705 		copylen = min(M_TRAILINGSPACE(mb), len - off);
706 		memcpy(mtodo(mb, mb->m_len), (const char *)addr + off, copylen);
707 		mb->m_len += copylen;
708 		off += copylen;
709 		if (off == len)
710 			break;
711 	}
712 	KASSERT(off == len, ("%s: off (%zu) != len (%zu)", __func__,
713 	    off, len));
714 	off = 0;
715 	for (; mb != NULL && off < len2; mb = mb->m_next) {
716 		copylen = min(M_TRAILINGSPACE(mb), len2 - off);
717 		memcpy(mtodo(mb, mb->m_len), (const char *)addr2 + off, copylen);
718 		mb->m_len += copylen;
719 		off += copylen;
720 	}
721 	KASSERT(off == len2, ("%s: off (%zu) != len2 (%zu)", __func__,
722 	    off, len2));
723 	newmb->m_pkthdr.len = sizeof(hdr) + len + len2;
724 
725 	mtx_lock(&softc->ha_lock);
726 	if (!softc->ha_connected) {
727 		mtx_unlock(&softc->ha_lock);
728 		m_freem(newmb);
729 		return (CTL_HA_STATUS_DISCONNECT);
730 	}
731 	mbufq_enqueue(&softc->ha_sendq, newmb);
732 	softc->ha_wakeup = 1;
733 	mtx_unlock(&softc->ha_lock);
734 	wakeup(&softc->ha_wakeup);
735 	return (CTL_HA_STATUS_SUCCESS);
736 }
737 
738 ctl_ha_status
739 ctl_ha_msg_send(ctl_ha_channel channel, const void *addr, size_t len,
740     int wait)
741 {
742 
743 	return (ctl_ha_msg_send2(channel, addr, len, NULL, 0, wait));
744 }
745 
746 ctl_ha_status
747 ctl_ha_msg_abort(ctl_ha_channel channel)
748 {
749 	struct ha_softc *softc = &ha_softc;
750 
751 	mtx_lock(&softc->ha_lock);
752 	softc->ha_disconnect = 1;
753 	softc->ha_wakeup = 1;
754 	mtx_unlock(&softc->ha_lock);
755 	wakeup(&softc->ha_wakeup);
756 	return (CTL_HA_STATUS_SUCCESS);
757 }
758 
759 /*
760  * Allocate a data transfer request structure.
761  */
762 struct ctl_ha_dt_req *
763 ctl_dt_req_alloc(void)
764 {
765 
766 	return (malloc(sizeof(struct ctl_ha_dt_req), M_CTL, M_WAITOK | M_ZERO));
767 }
768 
769 /*
770  * Free a data transfer request structure.
771  */
772 void
773 ctl_dt_req_free(struct ctl_ha_dt_req *req)
774 {
775 
776 	free(req, M_CTL);
777 }
778 
779 /*
780  * Issue a DMA request for a single buffer.
781  */
782 ctl_ha_status
783 ctl_dt_single(struct ctl_ha_dt_req *req)
784 {
785 	struct ha_softc *softc = &ha_softc;
786 	struct ha_dt_msg_wire wire_dt;
787 	ctl_ha_status status;
788 
789 	wire_dt.command = req->command;
790 	wire_dt.size = req->size;
791 	wire_dt.local = req->local;
792 	wire_dt.remote = req->remote;
793 	if (req->command == CTL_HA_DT_CMD_READ && req->callback != NULL) {
794 		mtx_lock(&softc->ha_lock);
795 		TAILQ_INSERT_TAIL(&softc->ha_dts, req, links);
796 		mtx_unlock(&softc->ha_lock);
797 		ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt, sizeof(wire_dt),
798 		    M_WAITOK);
799 		return (CTL_HA_STATUS_WAIT);
800 	}
801 	if (req->command == CTL_HA_DT_CMD_READ) {
802 		status = ctl_ha_msg_send(CTL_HA_CHAN_DATA, &wire_dt,
803 		    sizeof(wire_dt), M_WAITOK);
804 	} else {
805 		status = ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
806 		    sizeof(wire_dt), req->local, req->size, M_WAITOK);
807 	}
808 	return (status);
809 }
810 
811 static void
812 ctl_dt_event_handler(ctl_ha_channel channel, ctl_ha_event event, int param)
813 {
814 	struct ha_softc *softc = &ha_softc;
815 	struct ctl_ha_dt_req *req;
816 	ctl_ha_status isc_status;
817 
818 	if (event == CTL_HA_EVT_MSG_RECV) {
819 		struct ha_dt_msg_wire wire_dt;
820 		uint8_t *tmp;
821 		int size;
822 
823 		size = min(sizeof(wire_dt), param);
824 		isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA, &wire_dt,
825 					     size, M_WAITOK);
826 		if (isc_status != CTL_HA_STATUS_SUCCESS) {
827 			printf("%s: Error receiving message: %d\n",
828 			    __func__, isc_status);
829 			return;
830 		}
831 
832 		if (wire_dt.command == CTL_HA_DT_CMD_READ) {
833 			wire_dt.command = CTL_HA_DT_CMD_WRITE;
834 			tmp = wire_dt.local;
835 			wire_dt.local = wire_dt.remote;
836 			wire_dt.remote = tmp;
837 			ctl_ha_msg_send2(CTL_HA_CHAN_DATA, &wire_dt,
838 			    sizeof(wire_dt), wire_dt.local, wire_dt.size,
839 			    M_WAITOK);
840 		} else if (wire_dt.command == CTL_HA_DT_CMD_WRITE) {
841 			isc_status = ctl_ha_msg_recv(CTL_HA_CHAN_DATA,
842 			    wire_dt.remote, wire_dt.size, M_WAITOK);
843 			mtx_lock(&softc->ha_lock);
844 			TAILQ_FOREACH(req, &softc->ha_dts, links) {
845 				if (req->local == wire_dt.remote) {
846 					TAILQ_REMOVE(&softc->ha_dts, req, links);
847 					break;
848 				}
849 			}
850 			mtx_unlock(&softc->ha_lock);
851 			if (req) {
852 				req->ret = isc_status;
853 				req->callback(req);
854 			}
855 		}
856 	} else if (event == CTL_HA_EVT_LINK_CHANGE) {
857 		CTL_DEBUG_PRINT(("%s: Link state change to %d\n", __func__,
858 		    param));
859 		if (param != CTL_HA_LINK_ONLINE) {
860 			mtx_lock(&softc->ha_lock);
861 			while ((req = TAILQ_FIRST(&softc->ha_dts)) != NULL) {
862 				TAILQ_REMOVE(&softc->ha_dts, req, links);
863 				mtx_unlock(&softc->ha_lock);
864 				req->ret = CTL_HA_STATUS_DISCONNECT;
865 				req->callback(req);
866 				mtx_lock(&softc->ha_lock);
867 			}
868 			mtx_unlock(&softc->ha_lock);
869 		}
870 	} else {
871 		printf("%s: Unknown event %d\n", __func__, event);
872 	}
873 }
874 
875 ctl_ha_status
876 ctl_ha_msg_init(struct ctl_softc *ctl_softc)
877 {
878 	struct ha_softc *softc = &ha_softc;
879 	int error;
880 
881 	softc->ha_ctl_softc = ctl_softc;
882 	mtx_init(&softc->ha_lock, "CTL HA mutex", NULL, MTX_DEF);
883 	mbufq_init(&softc->ha_sendq, INT_MAX);
884 	TAILQ_INIT(&softc->ha_dts);
885 	error = kproc_kthread_add(ctl_ha_conn_thread, softc,
886 	    &ctl_softc->ctl_proc, NULL, 0, 0, "ctl", "ha_tx");
887 	if (error != 0) {
888 		printf("error creating CTL HA connection thread!\n");
889 		mtx_destroy(&softc->ha_lock);
890 		return (CTL_HA_STATUS_ERROR);
891 	}
892 	softc->ha_shutdown_eh = EVENTHANDLER_REGISTER(shutdown_pre_sync,
893 	    ctl_ha_msg_shutdown, ctl_softc, SHUTDOWN_PRI_FIRST);
894 	SYSCTL_ADD_PROC(&ctl_softc->sysctl_ctx,
895 	    SYSCTL_CHILDREN(ctl_softc->sysctl_tree),
896 	    OID_AUTO, "ha_peer",
897 	    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
898 	    softc, 0, ctl_ha_peer_sysctl, "A", "HA peer connection method");
899 
900 	if (ctl_ha_msg_register(CTL_HA_CHAN_DATA, ctl_dt_event_handler)
901 	    != CTL_HA_STATUS_SUCCESS) {
902 		printf("%s: ctl_ha_msg_register failed.\n", __func__);
903 	}
904 
905 	return (CTL_HA_STATUS_SUCCESS);
906 };
907 
908 void
909 ctl_ha_msg_shutdown(struct ctl_softc *ctl_softc)
910 {
911 	struct ha_softc *softc = &ha_softc;
912 
913 	/* Disconnect and shutdown threads. */
914 	mtx_lock(&softc->ha_lock);
915 	if (softc->ha_shutdown < 2) {
916 		softc->ha_shutdown = 1;
917 		softc->ha_wakeup = 1;
918 		wakeup(&softc->ha_wakeup);
919 		while (softc->ha_shutdown < 2 && !SCHEDULER_STOPPED()) {
920 			msleep(&softc->ha_wakeup, &softc->ha_lock, 0,
921 			    "shutdown", hz);
922 		}
923 	}
924 	mtx_unlock(&softc->ha_lock);
925 };
926 
927 ctl_ha_status
928 ctl_ha_msg_destroy(struct ctl_softc *ctl_softc)
929 {
930 	struct ha_softc *softc = &ha_softc;
931 
932 	if (softc->ha_shutdown_eh != NULL) {
933 		EVENTHANDLER_DEREGISTER(shutdown_pre_sync,
934 		    softc->ha_shutdown_eh);
935 		softc->ha_shutdown_eh = NULL;
936 	}
937 
938 	ctl_ha_msg_shutdown(ctl_softc);	/* Just in case. */
939 
940 	if (ctl_ha_msg_deregister(CTL_HA_CHAN_DATA) != CTL_HA_STATUS_SUCCESS)
941 		printf("%s: ctl_ha_msg_deregister failed.\n", __func__);
942 
943 	mtx_destroy(&softc->ha_lock);
944 	return (CTL_HA_STATUS_SUCCESS);
945 };
946