1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 */
26
27 /*
28 * Media independent RPC-like comms
29 */
30
31 #include <sys/types.h>
32 #include <sys/conf.h>
33 #include <sys/stat.h>
34 #include <sys/errno.h>
35 #include <sys/cmn_err.h>
36 #include <sys/ksynch.h>
37 #include <sys/kmem.h>
38 #include <sys/modctl.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41
42 #include <sys/varargs.h>
43 #ifdef DS_DDICT
44 #include <sys/nsctl/contract.h>
45 #endif
46 #include "ncall.h"
47 #include "ncall_module.h"
48
49 #include <sys/nsctl/nsvers.h>
50
51 /*
52 * cb_ops functions.
53 */
54
55 static int ncallioctl(dev_t, int, intptr_t, int, cred_t *, int *);
56 static int ncallprint(dev_t, char *);
57
58
59 static struct cb_ops ncall_cb_ops = {
60 nulldev, /* open */
61 nulldev, /* close */
62 nulldev, /* strategy */
63 ncallprint,
64 nodev, /* dump */
65 nodev, /* read */
66 nodev, /* write */
67 ncallioctl,
68 nodev, /* devmap */
69 nodev, /* mmap */
70 nodev, /* segmap */
71 nochpoll, /* poll */
72 ddi_prop_op,
73 NULL, /* NOT a stream */
74 D_NEW | D_MP | D_64BIT,
75 CB_REV,
76 nodev, /* aread */
77 nodev, /* awrite */
78 };
79
80
81 /*
82 * dev_ops functions.
83 */
84
85 static int ncall_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
86 static int ncall_attach(dev_info_t *, ddi_attach_cmd_t);
87 static int ncall_detach(dev_info_t *, ddi_detach_cmd_t);
88
89 static struct dev_ops ncall_ops = {
90 DEVO_REV,
91 0,
92 ncall_getinfo,
93 nulldev, /* identify */
94 nulldev, /* probe */
95 ncall_attach,
96 ncall_detach,
97 nodev, /* reset */
98 &ncall_cb_ops,
99 (struct bus_ops *)0,
100 NULL /* power */
101 };
102
103 /*
104 * Module linkage.
105 */
106
107 extern struct mod_ops mod_driverops;
108
109 static struct modldrv modldrv = {
110 &mod_driverops,
111 "nws:Kernel Call:" ISS_VERSION_STR,
112 &ncall_ops
113 };
114
115 static struct modlinkage modlinkage = {
116 MODREV_1,
117 &modldrv,
118 0
119 };
120
121 typedef struct ncall_modinfo_s {
122 struct ncall_modinfo_s *next;
123 ncall_module_t *module;
124 } ncall_modinfo_t;
125
126 static dev_info_t *ncall_dip; /* Single DIP for driver */
127 static kmutex_t ncall_mutex;
128
129 static ncall_modinfo_t *ncall_modules;
130 static int ncall_active;
131
132 static ncall_node_t ncall_nodeinfo;
133
134 static int ncallgetnodes(intptr_t, int, int *);
135 extern void ncall_init_stub(void);
136
137 int
138 _init(void)
139 {
140 int error;
141
142 mutex_init(&ncall_mutex, NULL, MUTEX_DRIVER, NULL);
143
144 if ((error = mod_install(&modlinkage)) != 0) {
145 mutex_destroy(&ncall_mutex);
146 return (error);
147 }
148
149 return (0);
150 }
151
152
153 int
154 _fini(void)
155 {
156 int error;
157
158 if ((error = mod_remove(&modlinkage)) != 0)
159 return (error);
160
161 mutex_destroy(&ncall_mutex);
162 return (error);
163 }
164
165
166 int
167 _info(struct modinfo *modinfop)
168 {
169 return (mod_info(&modlinkage, modinfop));
170 }
171
172 static int
173 ncall_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
174 {
175 switch (cmd) {
176
177 case DDI_ATTACH:
178 ncall_dip = dip;
179
180 if (ddi_create_minor_node(dip, "c,ncall", S_IFCHR,
181 0, DDI_PSEUDO, 0) != DDI_SUCCESS)
182 goto failed;
183
184 ddi_report_dev(dip);
185
186 return (DDI_SUCCESS);
187
188 default:
189 return (DDI_FAILURE);
190 }
191
192 failed:
193 (void) ncall_detach(dip, DDI_DETACH);
194 return (DDI_FAILURE);
195 }
196
197
198 static int
199 ncall_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
200 {
201 switch (cmd) {
202
203 case DDI_DETACH:
204
205 /*
206 * If still active, then refuse to detach.
207 */
208
209 if (ncall_modules != NULL || ncall_active)
210 return (DDI_FAILURE);
211
212 /*
213 * Remove all minor nodes.
214 */
215
216 ddi_remove_minor_node(dip, NULL);
217 ncall_dip = NULL;
218
219 return (DDI_SUCCESS);
220
221 default:
222 return (DDI_FAILURE);
223 }
224 }
225
226
227 /* ARGSUSED */
228
229 static int
230 ncall_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
231 {
232 int rc = DDI_FAILURE;
233
234 switch (infocmd) {
235
236 case DDI_INFO_DEVT2DEVINFO:
237 *result = ncall_dip;
238 rc = DDI_SUCCESS;
239 break;
240
241 case DDI_INFO_DEVT2INSTANCE:
242 /*
243 * We only have a single instance.
244 */
245 *result = 0;
246 rc = DDI_SUCCESS;
247 break;
248
249 default:
250 break;
251 }
252
253 return (rc);
254 }
255
256
257 /* ARGSUSED */
258 static int
259 ncallprint(dev_t dev, char *str)
260 {
261 cmn_err(CE_WARN, "%s%d: %s", ddi_get_name(ncall_dip),
262 ddi_get_instance(ncall_dip), str);
263
264 return (0);
265 }
266
267
268 int
269 ncall_register_module(ncall_module_t *mp, ncall_node_t *nodep)
270 {
271 ncall_modinfo_t *new;
272 int rc = 0;
273
274 if (mp == NULL || mp->ncall_version != NCALL_MODULE_VER)
275 return (EINVAL);
276
277 new = kmem_alloc(sizeof (*new), KM_SLEEP);
278
279 if (new != NULL) {
280 new->module = mp;
281
282 mutex_enter(&ncall_mutex);
283
284 new->next = ncall_modules;
285 ncall_modules = new;
286
287 mutex_exit(&ncall_mutex);
288 } else {
289 rc = ENOMEM;
290 }
291
292 *nodep = ncall_nodeinfo; /* structure copy */
293 return (rc);
294 }
295
296
297 int
298 ncall_unregister_module(ncall_module_t *mod)
299 {
300 ncall_modinfo_t **mpp;
301 int rc = ESRCH;
302
303 mutex_enter(&ncall_mutex);
304
305 for (mpp = &ncall_modules; *mpp != NULL; mpp = &((*mpp)->next)) {
306 if ((*mpp)->module == mod) {
307 *mpp = (*mpp)->next;
308 rc = 0;
309 break;
310 }
311 }
312
313 mutex_exit(&ncall_mutex);
314
315 return (rc);
316 }
317
318
319 static int
320 ncall_stop(void)
321 {
322 ncall_modinfo_t *mod;
323 int rc = 0;
324
325 mutex_enter(&ncall_mutex);
326
327 while ((rc == 0) && ((mod = ncall_modules) != NULL)) {
328 mutex_exit(&ncall_mutex);
329
330 rc = (*mod->module->ncall_stop)();
331
332 mutex_enter(&ncall_mutex);
333 }
334
335 mutex_exit(&ncall_mutex);
336
337 return (rc);
338 }
339
340
341 /* ARGSUSED */
342 static int ncallioctl(dev_t dev, int cmd, intptr_t arg, int mode,
343 cred_t *crp, int *rvalp)
344 {
345 ncall_node_t node = { 0, };
346 int mirror;
347 int rc = 0;
348
349 *rvalp = 0;
350
351 if ((rc = drv_priv(crp)) != 0)
352 return (rc);
353
354 switch (cmd) {
355
356 case NC_IOC_START:
357 if (ncall_active) {
358 rc = EALREADY;
359 break;
360 }
361
362 if (ddi_copyin((void *)arg, &node, sizeof (node), mode) < 0)
363 return (EFAULT);
364
365 bcopy(&node, &ncall_nodeinfo, sizeof (ncall_nodeinfo));
366 ncall_init_stub();
367 ncall_active = 1;
368 break;
369
370 case NC_IOC_STOP:
371 ncall_active = 0;
372 rc = ncall_stop();
373 break;
374
375 case NC_IOC_GETNODE:
376 if (!ncall_active) {
377 rc = ENONET;
378 break;
379 }
380 if (ddi_copyout(&ncall_nodeinfo, (void *)arg,
381 sizeof (ncall_nodeinfo), mode) < 0) {
382 rc = EFAULT;
383 break;
384 }
385 mirror = ncall_mirror(ncall_nodeinfo.nc_nodeid);
386 /*
387 * can't return -1, as this will mask the ioctl
388 * failure, so return 0.
389 */
390 if (mirror == -1)
391 mirror = 0;
392 *rvalp = mirror;
393 break;
394
395 case NC_IOC_GETNETNODES:
396 rc = ncallgetnodes(arg, mode, rvalp);
397 break;
398
399 case NC_IOC_PING:
400 if (!ncall_active) {
401 rc = ENONET;
402 break;
403 }
404
405 if (ddi_copyin((void *)arg, &node, sizeof (node), mode) < 0) {
406 rc = EFAULT;
407 break;
408 }
409
410 node.nc_nodename[sizeof (node.nc_nodename)-1] = '\0';
411 rc = ncall_ping(node.nc_nodename, rvalp);
412 break;
413
414 default:
415 rc = EINVAL;
416 break;
417 }
418
419 return (rc);
420 }
421
422
423 void
424 ncall_register_svc(int svc_id, void (*func)(ncall_t *, int *))
425 {
426 if (ncall_modules)
427 (*ncall_modules->module->ncall_register_svc)(svc_id, func);
428 }
429
430
431 void
432 ncall_unregister_svc(int svc_id)
433 {
434 if (ncall_modules)
435 (*ncall_modules->module->ncall_unregister_svc)(svc_id);
436 }
437
438
439 int
440 ncall_nodeid(char *nodename)
441 {
442 if (ncall_modules)
443 return ((ncall_modules->module->ncall_nodeid)(nodename));
444 else
445 return (0);
446 }
447
448
449 char *
450 ncall_nodename(int nodeid)
451 {
452 if (ncall_modules)
453 return ((*ncall_modules->module->ncall_nodename)(nodeid));
454 else
455 return ("unknown");
456 }
457
458
459 int
460 ncall_mirror(int nodeid)
461 {
462 if (ncall_modules)
463 return ((*ncall_modules->module->ncall_mirror)(nodeid));
464 else
465 return (-1);
466 }
467
468
469 int
470 ncall_self(void)
471 {
472 if (ncall_modules)
473 return ((*ncall_modules->module->ncall_self)());
474 else
475 return (-1);
476 }
477
478
479 int
480 ncall_alloc(int host_id, int flags, int net, ncall_t **ncall_p)
481 {
482 int rc = ENOLINK;
483
484 if (ncall_modules)
485 rc = (*ncall_modules->module->ncall_alloc)(host_id,
486 flags, net, ncall_p);
487
488 return (rc);
489 }
490
491
492 int
493 ncall_timedsend(ncall_t *ncall, int flags, int svc_id,
494 struct timeval *t, ...)
495 {
496 va_list ap;
497 int rc = ENOLINK;
498
499 va_start(ap, t);
500
501 if (ncall_modules)
502 rc = (*ncall_modules->module->ncall_timedsend)(ncall, flags,
503 svc_id, t, ap);
504
505 va_end(ap);
506
507 return (rc);
508 }
509
510 int
511 ncall_timedsendnotify(ncall_t *ncall, int flags, int svc_id,
512 struct timeval *t, void (*ncall_callback)(ncall_t *, void *),
513 void *vptr, ...)
514 {
515 va_list ap;
516 int rc = ENOLINK;
517
518 va_start(ap, vptr);
519
520 if (ncall_modules)
521 rc = (*ncall_modules->module->ncall_timedsendnotify)(ncall,
522 flags, svc_id, t, ncall_callback, vptr, ap);
523 va_end(ap);
524
525 return (rc);
526 }
527
528 int
529 ncall_broadcast(ncall_t *ncall, int flags, int svc_id,
530 struct timeval *t, ...)
531 {
532 va_list ap;
533 int rc = ENOLINK;
534
535 va_start(ap, t);
536
537 if (ncall_modules)
538 rc = (*ncall_modules->module->ncall_broadcast)(ncall, flags,
539 svc_id, t, ap);
540 va_end(ap);
541
542 return (rc);
543 }
544
545
546 int
547 ncall_send(ncall_t *ncall, int flags, int svc_id, ...)
548 {
549 va_list ap;
550 int rc = ENOLINK;
551
552 va_start(ap, svc_id);
553
554 if (ncall_modules)
555 rc = (*ncall_modules->module->ncall_timedsend)(ncall, flags,
556 svc_id, NULL, ap);
557
558 va_end(ap);
559
560 return (rc);
561 }
562
563
564 int
565 ncall_read_reply(ncall_t *ncall, int n, ...)
566 {
567 va_list ap;
568 int rc = ENOLINK;
569
570 va_start(ap, n);
571
572 if (ncall_modules)
573 rc = (*ncall_modules->module->ncall_read_reply)(ncall, n, ap);
574
575 va_end(ap);
576
577 return (rc);
578 }
579
580
581 void
582 ncall_reset(ncall_t *ncall)
583 {
584 if (ncall_modules)
585 (*ncall_modules->module->ncall_reset)(ncall);
586 }
587
588
589 void
590 ncall_free(ncall_t *ncall)
591 {
592 if (ncall_modules)
593 (*ncall_modules->module->ncall_free)(ncall);
594 }
595
596
597 int
598 ncall_put_data(ncall_t *ncall, void *data, int len)
599 {
600 int rc = ENOLINK;
601
602 if (ncall_modules)
603 rc = (*ncall_modules->module->ncall_put_data)(ncall, data, len);
604
605 return (rc);
606 }
607
608
609 int
610 ncall_get_data(ncall_t *ncall, void *data, int len)
611 {
612 int rc = ENOLINK;
613
614 if (ncall_modules)
615 rc = (*ncall_modules->module->ncall_get_data)(ncall, data, len);
616
617 return (rc);
618 }
619
620
621 int
622 ncall_sender(ncall_t *ncall)
623 {
624 int rc = -1;
625
626 if (ncall_modules)
627 rc = (*ncall_modules->module->ncall_sender)(ncall);
628
629 return (rc);
630 }
631
632
633 void
634 ncall_reply(ncall_t *ncall, ...)
635 {
636 va_list ap;
637
638 if (ncall_modules) {
639 va_start(ap, ncall);
640
641 (*ncall_modules->module->ncall_reply)(ncall, ap);
642
643 va_end(ap);
644 }
645 }
646
647
648 void
649 ncall_pend(ncall_t *ncall)
650 {
651 if (ncall_modules)
652 (*ncall_modules->module->ncall_pend)(ncall);
653 }
654
655
656 void
657 ncall_done(ncall_t *ncall)
658 {
659 if (ncall_modules)
660 (*ncall_modules->module->ncall_done)(ncall);
661 }
662
663 int
664 ncall_ping(char *nodename, int *up)
665 {
666 int rc = ENOLINK;
667 if (ncall_modules)
668 rc = (*ncall_modules->module->ncall_ping)(nodename, up);
669 return (rc);
670 }
671
672 int
673 ncall_maxnodes()
674 {
675 int rc = 0;
676
677 if (ncall_modules)
678 rc = (*ncall_modules->module->ncall_maxnodes)();
679
680 return (rc);
681 }
682
683 int
684 ncall_nextnode(void **vptr)
685 {
686 int rc = 0;
687
688 if (ncall_modules)
689 rc = (*ncall_modules->module->ncall_nextnode)(vptr);
690
691 return (rc);
692 }
693
694 int
695 ncall_errcode(ncall_t *ncall, int *result)
696 {
697 int rc = ENOLINK;
698 if (ncall_modules)
699 rc = (*ncall_modules->module->ncall_errcode)(ncall, result);
700
701 return (rc);
702 }
703
704 static int
705 ncallgetnodes(intptr_t uaddr, int mode, int *rvalp)
706 {
707 ncall_node_t *nodelist;
708 int slot;
709 int rc;
710 int nodecnt;
711 int nodeid;
712 void *sequence;
713 char *nodename;
714
715 rc = 0;
716
717 nodecnt = ncall_maxnodes();
718 if (nodecnt <= 0) {
719 return (ENONET);
720 }
721
722 /*
723 * If the user passes up a null address argument, then
724 * they don't want the actual nodes, but the configured
725 * maximum, so space can be correctly allocated.
726 */
727
728 if (uaddr == NULL) {
729 *rvalp = nodecnt;
730 return (0);
731 }
732 nodelist = kmem_zalloc(sizeof (*nodelist) * nodecnt, KM_SLEEP);
733
734 slot = 0;
735 sequence = NULL;
736 while ((nodeid = ncall_nextnode(&sequence)) > 0) {
737 nodename = ncall_nodename(nodeid);
738 /*
739 * There is a small window where nextnode can
740 * return a valid nodeid, and it being disabled
741 * which will get nodename to return "".
742 * Discard the nodeid if this happens.
743 */
744 if (strlen(nodename) > 0) {
745 int size = sizeof (nodelist[slot].nc_nodename) - 1;
746 ASSERT(slot < nodecnt);
747 /*
748 * make sure its null terminated when it
749 * gets to userland.
750 */
751 nodelist[slot].nc_nodename[size] = 0;
752 (void) strncpy(nodelist[slot].nc_nodename, nodename,
753 size);
754 nodelist[slot].nc_nodeid = nodeid;
755 slot++;
756 }
757 }
758 if (ddi_copyout(nodelist, (void *)uaddr, sizeof (*nodelist) * slot,
759 mode) < 0) {
760 rc = EFAULT;
761 } else {
762 /*
763 * tell them how many have come back.
764 */
765 *rvalp = slot;
766 }
767 kmem_free(nodelist, sizeof (*nodelist) * nodecnt);
768 return (rc);
769 }