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