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