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 }