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 }