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 }