]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/cam/ctl/ctl_ha.c
zfs: merge openzfs/zfs@797f55ef1
[FreeBSD/FreeBSD.git] / sys / cam / ctl / ctl_ha.c
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 };