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 }