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 }