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 }