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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * LDOMs Domain Services Device Driver 29 */ 30 #include <sys/types.h> 31 #include <sys/file.h> 32 #include <sys/errno.h> 33 #include <sys/open.h> 34 #include <sys/cred.h> 35 #include <sys/uio.h> 36 #include <sys/stat.h> 37 #include <sys/ksynch.h> 38 #include <sys/modctl.h> 39 #include <sys/conf.h> 40 #include <sys/devops.h> 41 #include <sys/debug.h> 42 #include <sys/cmn_err.h> 43 #include <sys/ddi.h> 44 #include <sys/sunddi.h> 45 #include <sys/taskq.h> 46 #include <sys/disp.h> 47 #include <sys/note.h> 48 #include <sys/mach_descrip.h> 49 #include <sys/mdesc.h> 50 #include <sys/mdeg.h> 51 #include <sys/ldc.h> 52 #include <sys/ds.h> 53 #include <sys/ds_impl.h> 54 #include <sys/vlds.h> 55 #include <sys/bitmap.h> 56 #include <sys/sysevent.h> 57 58 static dev_info_t *vlds_devi; 59 60 61 typedef struct vlds_state { 62 dev_info_t *dip; 63 int instance; 64 evchan_t *evchan; 65 } vlds_state_t; 66 67 static void *vlds_statep; 68 69 typedef struct vlds_recv_hdr { 70 struct vlds_recv_hdr *next; /* next in recv list */ 71 void *data; /* the data itself */ 72 size_t datasz; /* size of the data */ 73 } vlds_recv_hdr_t; 74 75 typedef struct vlds_svc_info { 76 int state; /* driver svc info state VLDS_RECV* */ 77 vlds_recv_hdr_t *recv_headp; /* ptr to head of recv queue */ 78 vlds_recv_hdr_t *recv_tailp; /* ptr to tail of recv queue */ 79 size_t recv_size; /* no. of bytes in recv queue */ 80 uint_t recv_cnt; /* no of messages in recv queue */ 81 kmutex_t recv_lock; /* lock for recv queue */ 82 kcondvar_t recv_cv; /* condition variable for recv queue */ 83 int recv_nreaders; /* no of currently waiting readers */ 84 } vlds_svc_info_t; 85 86 #define VLDS_RECV_OK 1 87 #define VLDS_RECV_UNREG_PENDING 2 88 #define VLDS_RECV_OVERFLOW 3 89 90 static int vlds_ports_inited = 0; 91 92 static uint_t vlds_flags_to_svc(uint64_t flags); 93 94 95 #define VLDS_NAME "vlds" 96 static int vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp); 97 static int vlds_close(dev_t dev, int flag, int otyp, cred_t *credp); 98 static int vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 99 int *rvalp); 100 static int vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, 101 void **resultp); 102 static int vlds_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 103 static int vlds_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 104 105 /* mdeg register functions */ 106 static void vlds_mdeg_init(void); 107 static int vlds_mdeg_cb(void *cb_argp, mdeg_result_t *resp); 108 static int vlds_mdeg_register(void); 109 static int vlds_mdeg_unregister(void); 110 static int vlds_add_mdeg_port(md_t *mdp, mde_cookie_t node); 111 112 /* driver utilities */ 113 static void vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl); 114 static void vlds_user_unreg_cb(ds_cb_arg_t arg); 115 static void vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen); 116 static void vlds_recvq_init(vlds_svc_info_t *dpsp); 117 static void vlds_recvq_destroy(vlds_svc_info_t *dpsp); 118 static int vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen, 119 size_t *msglenp, int mode); 120 static void vlds_recvq_drain(vlds_svc_info_t *dpsp); 121 static int vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen); 122 static int vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen, 123 size_t *msglenp, int mode); 124 125 /* 126 * DS driver Ops Vector 127 */ 128 static struct cb_ops vlds_cb_ops = { 129 vlds_open, /* cb_open */ 130 vlds_close, /* cb_close */ 131 nodev, /* cb_strategy */ 132 nodev, /* cb_print */ 133 nodev, /* cb_dump */ 134 nodev, /* cb_read */ 135 nodev, /* cb_write */ 136 vlds_ioctl, /* cb_ioctl */ 137 nodev, /* cb_devmap */ 138 nodev, /* cb_mmap */ 139 nodev, /* cb_segmap */ 140 nochpoll, /* cb_chpoll */ 141 ddi_prop_op, /* cb_prop_op */ 142 (struct streamtab *)NULL, /* cb_str */ 143 D_MP | D_64BIT, /* cb_flag */ 144 CB_REV, /* cb_rev */ 145 nodev, /* cb_aread */ 146 nodev /* cb_awrite */ 147 }; 148 149 static struct dev_ops vlds_dev_ops = { 150 DEVO_REV, /* devo_rev */ 151 0, /* devo_refcnt */ 152 vlds_getinfo, /* devo_getinfo */ 153 nulldev, /* devo_identify */ 154 nulldev, /* devo_probe */ 155 vlds_attach, /* devo_attach */ 156 vlds_detach, /* devo_detach */ 157 nodev, /* devo_reset */ 158 &vlds_cb_ops, /* devo_cb_ops */ 159 (struct bus_ops *)NULL, /* devo_bus_ops */ 160 nulldev /* devo_power */ 161 }; 162 163 static struct modldrv modldrv = { 164 &mod_driverops, 165 "Domain Services Driver 1.0", 166 &vlds_dev_ops 167 }; 168 169 static struct modlinkage modlinkage = { 170 MODREV_1, 171 (void *)&modldrv, 172 NULL 173 }; 174 175 /* 176 * Callback ops for user-land services. 177 */ 178 static ds_clnt_ops_t ds_user_ops = { 179 vlds_user_reg_cb, /* register */ 180 vlds_user_unreg_cb, /* unregister */ 181 vlds_user_data_cb, /* data */ 182 NULL /* ds_ucap_init will fill in */ 183 }; 184 185 static size_t vlds_recvq_maxsize = DS_STREAM_MTU * 8; 186 static uint_t vlds_recvq_maxmsg = 16; 187 188 #define VLDS_MINOR_MAX SHRT_MAX 189 190 /* Definitions for binding handle array */ 191 static ulong_t vlds_bitmap_initial = 1; /* index 0 indicates error */ 192 static ulong_t *vlds_minor_bitmap = &vlds_bitmap_initial; 193 static size_t vlds_minor_bits = BT_NBIPUL; 194 static kmutex_t vlds_minor_mutex; 195 196 /* 197 * Following vlds_minor_* routines map a binding handle to a minor number. 198 * Has to be called w/ locks held. 199 */ 200 static ulong_t * 201 vlds_minor_alloc(void) 202 { 203 ulong_t *bhst = vlds_minor_bitmap; 204 205 /* Increase bitmap by one BT_NBIPUL */ 206 if (vlds_minor_bits + BT_NBIPUL > VLDS_MINOR_MAX) { 207 return ((ulong_t *)NULL); 208 } 209 vlds_minor_bitmap = kmem_zalloc( 210 BT_SIZEOFMAP(vlds_minor_bits + BT_NBIPUL), KM_SLEEP); 211 bcopy(bhst, vlds_minor_bitmap, BT_SIZEOFMAP(vlds_minor_bits)); 212 if (bhst != &vlds_bitmap_initial) 213 kmem_free(bhst, BT_SIZEOFMAP(vlds_minor_bits)); 214 vlds_minor_bits += BT_NBIPUL; 215 216 return (vlds_minor_bitmap); 217 } 218 219 static void 220 vlds_minor_free(ulong_t *bitmap) 221 { 222 if (bitmap != &vlds_bitmap_initial) 223 kmem_free(bitmap, BT_SIZEOFMAP(vlds_minor_bits)); 224 } 225 226 static index_t 227 vlds_minor_get(void) 228 { 229 index_t idx; 230 ulong_t *bhst; 231 232 /* Search for an available index */ 233 mutex_enter(&vlds_minor_mutex); 234 if ((idx = bt_availbit(vlds_minor_bitmap, 235 vlds_minor_bits)) == -1) { 236 /* All busy - allocate additional binding handle bitmap space */ 237 if ((bhst = vlds_minor_alloc()) == NULL) { 238 /* Reached our maximum of id's == SHRT_MAX */ 239 mutex_exit(&vlds_minor_mutex); 240 return (0); 241 } else { 242 vlds_minor_bitmap = bhst; 243 } 244 idx = bt_availbit(vlds_minor_bitmap, vlds_minor_bits); 245 } 246 BT_SET(vlds_minor_bitmap, idx); 247 mutex_exit(&vlds_minor_mutex); 248 return (idx); 249 } 250 251 static void 252 vlds_minor_rele(index_t idx) 253 { 254 mutex_enter(&vlds_minor_mutex); 255 ASSERT(BT_TEST(vlds_minor_bitmap, idx) == 1); 256 BT_CLEAR(vlds_minor_bitmap, idx); 257 mutex_exit(&vlds_minor_mutex); 258 } 259 260 static void 261 vlds_minor_init(void) 262 { 263 mutex_init(&vlds_minor_mutex, NULL, MUTEX_DEFAULT, NULL); 264 } 265 266 int 267 _init(void) 268 { 269 int s; 270 271 if ((s = ddi_soft_state_init(&vlds_statep, sizeof (vlds_state_t), 0)) 272 != 0) 273 return (s); 274 275 if ((s = mod_install(&modlinkage)) != 0) { 276 ddi_soft_state_fini(&vlds_statep); 277 return (s); 278 } 279 280 vlds_mdeg_init(); 281 282 return (s); 283 } 284 285 int 286 _fini(void) 287 { 288 int s; 289 290 if ((s = mod_remove(&modlinkage)) != 0) 291 return (s); 292 293 ddi_soft_state_fini(&vlds_statep); 294 295 return (s); 296 } 297 298 int 299 _info(struct modinfo *modinfop) 300 { 301 return (mod_info(&modlinkage, modinfop)); 302 } 303 304 305 306 /*ARGSUSED*/ 307 static int 308 vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 309 { 310 switch (cmd) { 311 case DDI_INFO_DEVT2DEVINFO: 312 *resultp = vlds_devi; 313 return (DDI_SUCCESS); 314 case DDI_INFO_DEVT2INSTANCE: 315 *resultp = 0; 316 return (DDI_SUCCESS); 317 } 318 return (DDI_FAILURE); 319 } 320 321 322 static int 323 vlds_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 324 { 325 if (cmd != DDI_ATTACH) { 326 return (DDI_FAILURE); 327 } 328 329 if (ddi_create_minor_node(devi, VLDS_NAME, S_IFCHR, 330 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 331 ddi_remove_minor_node(devi, NULL); 332 return (DDI_FAILURE); 333 } 334 vlds_devi = devi; 335 336 vlds_minor_init(); 337 338 (void) vlds_mdeg_register(); 339 340 return (DDI_SUCCESS); 341 } 342 343 344 /*ARGSUSED*/ 345 static int 346 vlds_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 347 { 348 if (cmd != DDI_DETACH) { 349 return (DDI_FAILURE); 350 } 351 352 vlds_minor_free(vlds_minor_bitmap); 353 ddi_remove_minor_node(devi, NULL); 354 (void) vlds_mdeg_unregister(); 355 return (DDI_SUCCESS); 356 } 357 358 359 /*ARGSUSED*/ 360 static int 361 vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp) 362 { 363 int minor; 364 365 if (otyp != OTYP_CHR) 366 return (EINVAL); 367 368 if (getminor(*devp) != 0) 369 return (ENXIO); 370 371 minor = vlds_minor_get(); 372 if (minor == 0) 373 /* All minors are busy */ 374 return (EBUSY); 375 376 if (ddi_soft_state_zalloc(vlds_statep, minor) != DDI_SUCCESS) { 377 vlds_minor_rele(minor); 378 return (ENOMEM); 379 } 380 381 *devp = makedevice(getmajor(*devp), minor); 382 383 return (0); 384 } 385 386 387 /*ARGSUSED*/ 388 static int 389 vlds_close(dev_t dev, int flag, int otyp, cred_t *credp) 390 { 391 int minor = (int)getminor(dev); 392 vlds_state_t *sp; 393 394 DS_DBG_VLDS(CE_NOTE, "vlds_close"); 395 396 /* 397 * Unregister all handles associated with this process. 398 */ 399 ds_unreg_all(minor); 400 401 if (otyp != OTYP_CHR) 402 return (EINVAL); 403 404 sp = ddi_get_soft_state(vlds_statep, minor); 405 if (sp == NULL) { 406 return (ENXIO); 407 } 408 409 if (sp->evchan) { 410 (void) sysevent_evc_unbind(sp->evchan); 411 sp->evchan = NULL; 412 } 413 414 ddi_soft_state_free(vlds_statep, minor); 415 vlds_minor_rele(minor); 416 417 return (0); 418 } 419 420 int 421 vlds_init_sysevent(vlds_state_t *sp, uint32_t flags) 422 { 423 char evchan_name[MAX_CHNAME_LEN]; 424 int rv; 425 426 if (flags & DSSF_ANYCB_VALID) { 427 if (sp->evchan) { 428 DS_DBG_VLDS(CE_NOTE, "%s: sysevent already bound", 429 __func__); 430 return (0); 431 } 432 (void) sprintf(evchan_name, VLDS_SYSEV_CHAN_FMT, ddi_get_pid()); 433 if ((rv = sysevent_evc_bind(evchan_name, &sp->evchan, 434 EVCH_CREAT|EVCH_HOLD_PEND)) != 0) { 435 cmn_err(CE_WARN, "%s: can't bind to '%s' (%d)", 436 __func__, evchan_name, rv); 437 return (rv); 438 } 439 440 DS_DBG_VLDS(CE_NOTE, "%s: sysevent bind to '%s' successful", 441 __func__, evchan_name); 442 } 443 return (0); 444 } 445 446 #define ARGTOPTR(x) ((void *)((uintptr_t)(x))) 447 #define ARGTOUINT(x) ((uint_t)(x)) 448 #define ARGTOINT(x) ((int)(x)) 449 450 static int 451 vlds_get_string(vlds_string_t *strp, char **rstrp, int mode) 452 { 453 char *str; 454 uint_t len = strp->vlds_strlen; 455 uint_t slen; 456 457 if (len == 0) { 458 *rstrp = NULL; 459 return (0); 460 } 461 if (len > MAXNAMELEN) { 462 DS_DBG_VLDS(CE_NOTE, "%s: invalid string length: %d", __func__, 463 len); 464 return (EINVAL); 465 } 466 str = DS_MALLOC(len); 467 if (ddi_copyin(ARGTOPTR(strp->vlds_strp), str, len, mode) != 0) { 468 DS_DBG_VLDS(CE_NOTE, "%s: ddi copyin failed (%p)", __func__, 469 ARGTOPTR(strp->vlds_strp)); 470 DS_FREE(str, len); 471 return (EFAULT); 472 } 473 slen = strlen(str) + 1; 474 if (slen != len) { 475 DS_DBG_VLDS(CE_NOTE, "%s: invalid string len: %d != len: %d", 476 __func__, slen, len); 477 DS_FREE(str, len); 478 return (EINVAL); 479 } 480 *rstrp = str; 481 return (0); 482 } 483 484 static int 485 vlds_put_string(char *str, vlds_string_t *strp, int mode) 486 { 487 uint_t len; 488 char *tstr = NULL; 489 int rv; 490 491 if (str == NULL) { 492 str = ""; 493 } 494 len = strlen(str) + 1; 495 496 /* 497 * If string is longer than user buffer, return a 498 * truncated, null-terminated string. 499 */ 500 if (len > strp->vlds_strlen) { 501 len = strp->vlds_strlen; 502 if (len > 0) { 503 tstr = DS_MALLOC(len); 504 (void) memcpy(tstr, str, len - 1); 505 tstr[len - 1] = '\0'; 506 str = tstr; 507 } 508 } 509 rv = ddi_copyout(str, ARGTOPTR(strp->vlds_strp), len, mode); 510 if (tstr) { 511 DS_FREE(tstr, len); 512 } 513 if (rv) { 514 DS_DBG_VLDS(CE_NOTE, "%s: copyout (%p) failed", __func__, 515 ARGTOPTR(strp->vlds_strp)); 516 return (EFAULT); 517 } 518 return (0); 519 } 520 521 static int 522 vlds_get_ucap(vlds_cap_t *capp, ds_capability_t *ucap, int mode) 523 { 524 char *servp; 525 vlds_ver_t *dsvp; 526 vlds_cap_t vlds_cap; 527 uint_t n; 528 uint_t nver; 529 int i; 530 int rv; 531 532 if (ddi_copyin(capp, &vlds_cap, sizeof (vlds_cap), mode) != 0) { 533 DS_DBG_VLDS(CE_NOTE, "%s: cap copyin failed (%p)", __func__, 534 (void *)capp); 535 return (EFAULT); 536 } 537 538 nver = ARGTOUINT(vlds_cap.vlds_nver); 539 540 if (nver > VLDS_MAX_VERS) { 541 DS_DBG_VLDS(CE_NOTE, "%s: vlds_nver (%d) invalid", __func__, 542 nver); 543 return (EINVAL); 544 } 545 546 if ((rv = vlds_get_string(&vlds_cap.vlds_service, &servp, mode)) != 0) { 547 DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service failed " 548 "(%d)", __func__, rv); 549 return (rv); 550 } else if (servp == NULL) { 551 DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service is NULL", 552 __func__); 553 return (EINVAL); 554 } 555 556 n = nver * sizeof (vlds_ver_t); 557 dsvp = DS_MALLOC(n); 558 559 if (ddi_copyin(ARGTOPTR(vlds_cap.vlds_versp), dsvp, n, mode) != 0) { 560 DS_DBG_VLDS(CE_NOTE, "%s: copyin of vers (%p, %d) failed", 561 __func__, ARGTOPTR(vlds_cap.vlds_versp), n); 562 DS_FREE(servp, strlen(servp) + 1); 563 DS_FREE(dsvp, n); 564 return (EFAULT); 565 } 566 567 ucap->svc_id = servp; 568 ucap->vers = DS_MALLOC(nver * sizeof (ds_ver_t)); 569 for (i = 0; i < nver; i++) { 570 ucap->vers[i].major = dsvp[i].vlds_major; 571 ucap->vers[i].minor = dsvp[i].vlds_minor; 572 } 573 ucap->nvers = nver; 574 DS_FREE(dsvp, n); 575 return (0); 576 } 577 578 static void 579 vlds_free_ucap(ds_capability_t *ucap) 580 { 581 kmem_free(ucap->svc_id, strlen(ucap->svc_id) + 1); 582 kmem_free(ucap->vers, ucap->nvers * sizeof (ds_ver_t)); 583 } 584 585 /*ARGSUSED*/ 586 static int 587 vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 588 int *rvalp) 589 { 590 vlds_state_t *sp; 591 ds_svc_hdl_t hdl; 592 ds_domain_hdl_t dhdl; 593 char *servicep; 594 int rv; 595 int minor = (int)getminor(dev); 596 597 if ((sp = ddi_get_soft_state(vlds_statep, minor)) == NULL) 598 return (ENXIO); 599 600 switch (cmd) { 601 602 case VLDS_SVC_REG: 603 { 604 vlds_svc_reg_arg_t vlds_arg; 605 ds_capability_t ucap; 606 uint64_t hdl_arg; 607 uint_t flags; 608 609 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 610 mode) != 0) { 611 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG arg copyin failed", 612 __func__); 613 return (EFAULT); 614 } 615 616 if ((rv = vlds_get_ucap(ARGTOPTR(vlds_arg.vlds_capp), &ucap, 617 mode)) != 0) { 618 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG get_ucap failed (%d)", 619 __func__, rv); 620 return (rv); 621 } 622 623 flags = vlds_flags_to_svc(vlds_arg.vlds_reg_flags); 624 if ((rv = vlds_init_sysevent(sp, flags)) != 0) { 625 vlds_free_ucap(&ucap); 626 return (rv); 627 } 628 629 rv = ds_ucap_init(&ucap, &ds_user_ops, 630 vlds_flags_to_svc(vlds_arg.vlds_reg_flags) | DSSF_ISUSER, 631 minor, &hdl); 632 633 vlds_free_ucap(&ucap); 634 635 if (rv) { 636 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG ds_ucap_init failed " 637 "(%d)", __func__, rv); 638 return (rv); 639 } 640 641 hdl_arg = hdl; 642 if (ddi_copyout(&hdl_arg, ARGTOPTR(vlds_arg.vlds_hdlp), 643 sizeof (hdl_arg), mode) != 0) { 644 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG copyout failed", 645 __func__); 646 return (EFAULT); 647 } 648 DS_DBG_VLDS(CE_NOTE, "%s: SVC REG succeeded: hdl: %lx", 649 __func__, hdl); 650 break; 651 } 652 653 case VLDS_UNREG_HDL: 654 { 655 vlds_unreg_hdl_arg_t vlds_arg; 656 657 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 658 mode) != 0) { 659 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL arg copyin failed", 660 __func__); 661 return (EFAULT); 662 } 663 664 hdl = vlds_arg.vlds_hdl; 665 666 if ((rv = ds_is_my_hdl(hdl, minor)) != 0) { 667 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_is_my_hdl " 668 " hdl: %lx inst: %d failed (%d)", __func__, 669 hdl, rv, minor); 670 return (rv); 671 } 672 673 if ((rv = ds_unreg_hdl(hdl)) != 0) { 674 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_cap_unreg " 675 " hdl: %lx failed (%d)", __func__, hdl, rv); 676 return (rv); 677 } 678 DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL hdl: %lx succeeded", 679 __func__, hdl); 680 break; 681 } 682 683 case VLDS_HDL_LOOKUP: 684 { 685 vlds_hdl_lookup_arg_t vlds_arg; 686 ds_svc_hdl_t *hdlsp; 687 uint_t is_client, maxhdls, nhdls; 688 uint64_t nhdls_arg; 689 690 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 691 mode) != 0) { 692 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP arg copyin failed", 693 __func__); 694 return (EFAULT); 695 } 696 697 is_client = ARGTOUINT(vlds_arg.vlds_isclient); 698 maxhdls = ARGTOUINT(vlds_arg.vlds_maxhdls); 699 if (maxhdls == 0) { 700 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP invalid maxhdls " 701 "%d", __func__, maxhdls); 702 return (EINVAL); 703 } 704 705 if ((rv = vlds_get_string(&vlds_arg.vlds_service, &servicep, 706 mode)) != 0) { 707 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string " 708 "(service) failed (%d)", __func__, rv); 709 return (EFAULT); 710 } else if (servicep == NULL) { 711 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string " 712 " service is NULL", __func__); 713 return (EINVAL); 714 } 715 716 if (ARGTOPTR(vlds_arg.vlds_hdlsp) == 0) { 717 hdlsp = NULL; 718 } else { 719 hdlsp = DS_MALLOC(maxhdls * sizeof (*hdlsp)); 720 } 721 722 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP (%s, %d) entered", 723 __func__, servicep, is_client); 724 rv = ds_hdl_lookup(servicep, is_client, hdlsp, maxhdls, &nhdls); 725 726 DS_FREE(servicep, strlen(servicep) + 1); 727 if (rv) { 728 if (hdlsp) { 729 DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp)); 730 } 731 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP failed: (%d)", 732 __func__, rv); 733 return (rv); 734 } 735 736 if (hdlsp != NULL && nhdls > 0 && 737 ddi_copyout(hdlsp, ARGTOPTR(vlds_arg.vlds_hdlsp), 738 nhdls * sizeof (ds_svc_hdl_t), mode) != 0) { 739 if (hdlsp) { 740 DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp)); 741 } 742 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of hdls " 743 " failed", __func__); 744 return (EFAULT); 745 } 746 if (hdlsp) { 747 DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp)); 748 } 749 750 nhdls_arg = nhdls; 751 if (ddi_copyout(&nhdls_arg, ARGTOPTR(vlds_arg.vlds_nhdlsp), 752 sizeof (nhdls_arg), mode) != 0) { 753 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of nhdls " 754 " failed", __func__); 755 return (EFAULT); 756 } 757 DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP succeeded: nhdls: %d", 758 __func__, nhdls); 759 break; 760 } 761 762 case VLDS_DMN_LOOKUP: 763 { 764 vlds_dmn_lookup_arg_t vlds_arg; 765 uint64_t dhdl_arg; 766 767 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 768 mode) != 0) { 769 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP arg copyin failed", 770 __func__); 771 return (EFAULT); 772 } 773 774 hdl = vlds_arg.vlds_hdl; 775 776 if ((rv = ds_domain_lookup(hdl, &dhdl)) != 0) { 777 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP lookup hdl: 0x%lx " 778 "failed (%d)", __func__, hdl, rv); 779 return (rv); 780 } 781 782 dhdl_arg = dhdl; 783 784 if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp), 785 sizeof (dhdl_arg), mode) != 0) { 786 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP copyout " 787 "failed (%d)", __func__, rv); 788 return (rv); 789 } 790 791 DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP hdl: 0x%lx, dhdl: 0x%lx " 792 "succeeded", __func__, hdl, dhdl); 793 break; 794 } 795 796 case VLDS_SEND_MSG: 797 { 798 vlds_send_msg_arg_t vlds_arg; 799 size_t buflen; 800 char *bufp; 801 802 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 803 mode) != 0) { 804 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG arg copyin failed", 805 __func__); 806 return (EFAULT); 807 } 808 809 hdl = vlds_arg.vlds_hdl; 810 if ((rv = ds_is_my_hdl(hdl, minor)) != 0) { 811 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_is_my_hdl " 812 " hdl: %lx inst: %d failed (%d)", __func__, 813 hdl, rv, minor); 814 return (rv); 815 } 816 817 buflen = ARGTOUINT(vlds_arg.vlds_buflen); 818 bufp = DS_MALLOC(buflen); 819 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG (hdl: %lx, bufp: %p, " 820 "buflen: %ld", __func__, hdl, ARGTOPTR(vlds_arg.vlds_bufp), 821 buflen); 822 823 if (ddi_copyin(ARGTOPTR(vlds_arg.vlds_bufp), bufp, buflen, 824 mode) != 0) { 825 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG buf (%p, %ld) " 826 "copyin failed", __func__, 827 ARGTOPTR(vlds_arg.vlds_bufp), buflen); 828 DS_FREE(bufp, buflen); 829 return (EFAULT); 830 } 831 832 if ((rv = ds_cap_send(hdl, bufp, buflen)) != 0) { 833 DS_FREE(bufp, buflen); 834 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_cap_send failed " 835 "(%d)", __func__, rv); 836 return (rv); 837 } 838 DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG hdl: %lx, bufp: %p, " 839 "buflen: %ld succeeded", __func__, hdl, (void *)bufp, 840 buflen); 841 DS_DUMP_MSG(DS_DBG_FLAG_VLDS, bufp, buflen); 842 DS_FREE(bufp, buflen); 843 break; 844 } 845 846 case VLDS_RECV_MSG: 847 { 848 vlds_recv_msg_arg_t vlds_arg; 849 size_t buflen, msglen; 850 uint64_t msglen_arg; 851 852 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 853 mode) != 0) { 854 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG arg copyin failed", 855 __func__); 856 return (EFAULT); 857 } 858 859 hdl = vlds_arg.vlds_hdl; 860 if ((rv = ds_is_my_hdl(hdl, minor)) != 0) { 861 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG ds_is_my_hdl " 862 " hdl: %lx inst: %d failed (%d)", __func__, 863 hdl, rv, minor); 864 return (rv); 865 } 866 867 buflen = ARGTOUINT(vlds_arg.vlds_buflen); 868 869 if ((rv = vlds_recv_msg(hdl, ARGTOPTR(vlds_arg.vlds_bufp), 870 buflen, &msglen, mode)) != 0 && rv != EFBIG) { 871 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG vlds_recv_msg " 872 " failed (%d)", __func__, rv); 873 return (rv); 874 } 875 876 msglen_arg = msglen; 877 if (ddi_copyout(&msglen_arg, ARGTOPTR(vlds_arg.vlds_msglenp), 878 sizeof (msglen_arg), mode) != 0) { 879 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG copyout of msglen " 880 "failed", __func__); 881 return (EFAULT); 882 } 883 884 if (rv == EFBIG) { 885 return (EFBIG); 886 } 887 888 DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG hdl: %lx, " 889 "msglen: %ld succeeded", __func__, hdl, buflen); 890 break; 891 } 892 893 case VLDS_HDL_ISREADY: 894 { 895 vlds_hdl_isready_arg_t vlds_arg; 896 ds_svc_hdl_t hdl; 897 uint64_t is_ready_arg; 898 uint_t is_ready; 899 900 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 901 mode) != 0) { 902 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY arg copyin " 903 "failed", __func__); 904 return (EFAULT); 905 } 906 907 hdl = vlds_arg.vlds_hdl; 908 if ((rv = ds_hdl_isready(hdl, &is_ready)) != 0) { 909 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY ds_hdl_isready " 910 "error (%d)", __func__, rv); 911 return (rv); 912 } 913 914 is_ready_arg = is_ready; 915 if (ddi_copyout(&is_ready_arg, ARGTOPTR(vlds_arg.vlds_isreadyp), 916 sizeof (is_ready_arg), mode) != 0) { 917 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY copyout of " 918 "vlds_isready failed", __func__); 919 return (EFAULT); 920 } 921 DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY succeeded hdl: %lx, " 922 "is_ready: %d", __func__, hdl, is_ready); 923 break; 924 } 925 926 case VLDS_DOM_NAM2HDL: 927 { 928 vlds_dom_nam2hdl_arg_t vlds_arg; 929 char *domain_name; 930 uint64_t dhdl_arg; 931 ds_domain_hdl_t dhdl; 932 933 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 934 mode) != 0) { 935 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL arg copyin " 936 "failed", __func__); 937 return (EFAULT); 938 } 939 940 if ((rv = vlds_get_string(&vlds_arg.vlds_domain_name, 941 &domain_name, mode)) != 0) { 942 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string " 943 "domain_name failed (%d)", __func__, rv); 944 return (EFAULT); 945 } else if (servicep == NULL) { 946 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string " 947 " domain_name is NULL", __func__); 948 return (EINVAL); 949 } 950 951 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL (%s) entered", __func__, 952 domain_name); 953 954 if ((rv = ds_dom_name_to_hdl(domain_name, &dhdl)) != 0) { 955 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL name: '%s' " 956 "failed: (%d)", __func__, domain_name, rv); 957 DS_FREE(domain_name, strlen(domain_name) + 1); 958 return (rv); 959 } 960 961 dhdl_arg = dhdl; 962 if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp), 963 sizeof (dhdl_arg), mode) != 0) { 964 DS_FREE(domain_name, strlen(domain_name) + 1); 965 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL copyout of dhdl " 966 " failed", __func__); 967 return (EFAULT); 968 } 969 970 DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL succeeded: name: '%s', " 971 "dhdl: 0x%lx", __func__, domain_name, dhdl); 972 DS_FREE(domain_name, strlen(domain_name) + 1); 973 break; 974 } 975 976 case VLDS_DOM_HDL2NAM: 977 { 978 vlds_dom_hdl2nam_arg_t vlds_arg; 979 ds_domain_hdl_t dhdl; 980 char *domain_name; 981 982 if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg), 983 mode) != 0) { 984 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM arg copyin " 985 "failed", __func__); 986 return (EFAULT); 987 } 988 989 dhdl = vlds_arg.vlds_dhdl; 990 if ((rv = ds_dom_hdl_to_name(dhdl, &domain_name)) != 0) { 991 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM lookup dhdl: %lx " 992 "failed (%d)", __func__, dhdl, rv); 993 return (rv); 994 } 995 996 if ((rv = vlds_put_string(domain_name, 997 &vlds_arg.vlds_domain_name, mode)) != 0) { 998 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM vlds_put_string " 999 "'%s' failed (%d)", __func__, domain_name, rv); 1000 return (rv); 1001 } 1002 1003 DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM dhdl: 0x%lx name: '%s'", 1004 __func__, dhdl, domain_name); 1005 break; 1006 } 1007 1008 default: 1009 return (EINVAL); 1010 } 1011 return (0); 1012 } 1013 1014 static uint_t 1015 vlds_flags_to_svc(uint64_t flags) 1016 { 1017 uint_t sflags = 0; 1018 1019 if (flags & VLDS_REG_CLIENT) 1020 sflags |= DSSF_ISCLIENT; 1021 if (flags & VLDS_REGCB_VALID) 1022 sflags |= DSSF_REGCB_VALID; 1023 if (flags & VLDS_UNREGCB_VALID) 1024 sflags |= DSSF_UNREGCB_VALID; 1025 if (flags & VLDS_DATACB_VALID) 1026 sflags |= DSSF_DATACB_VALID; 1027 return (sflags); 1028 } 1029 1030 /* 1031 * MD registration code. 1032 * Placed in vlds rather than ds module due to cirular dependency of 1033 * platsvc module which contains the mdeg code. 1034 */ 1035 mdeg_handle_t vlds_mdeg_hdl; 1036 1037 /* 1038 * Look for "virtual-device-service" node among the 1039 * "virtual-device" nodes. 1040 */ 1041 static mdeg_prop_spec_t vlds_prop_template[] = { 1042 { MDET_PROP_STR, "name", VLDS_MD_VIRT_ROOT_NAME }, 1043 { MDET_LIST_END, NULL, NULL } 1044 }; 1045 1046 static mdeg_node_spec_t vlds_node_template = 1047 { VLDS_MD_VIRT_DEV_NAME, vlds_prop_template }; 1048 1049 /* 1050 * Matching criteria passed to the MDEG to register interest 1051 * in changes to domain services port nodes identified by their 1052 * 'id' property. 1053 */ 1054 static md_prop_match_t vlds_port_prop_match[] = { 1055 { MDET_PROP_VAL, "id" }, 1056 { MDET_LIST_END, NULL } 1057 }; 1058 1059 static mdeg_node_match_t vlds_port_match = { VLDS_MD_VIRT_PORT_NAME, 1060 vlds_port_prop_match }; 1061 1062 /* mdeg callback */ 1063 static int 1064 vlds_mdeg_cb(void *cb_argp, mdeg_result_t *resp) 1065 { 1066 _NOTE(ARGUNUSED(cb_argp)) 1067 int idx; 1068 uint64_t portno; 1069 int rv; 1070 md_t *mdp; 1071 mde_cookie_t node; 1072 1073 if (resp == NULL) { 1074 DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_cb: no result returned"); 1075 return (MDEG_FAILURE); 1076 } 1077 1078 DS_DBG_VLDS(CE_NOTE, "%s: added=%d, removed=%d, matched=%d", __func__, 1079 resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem); 1080 1081 /* process added ports */ 1082 for (idx = 0; idx < resp->added.nelem; idx++) { 1083 mdp = resp->added.mdp; 1084 node = resp->added.mdep[idx]; 1085 1086 DS_DBG_VLDS(CE_NOTE, "%s: processing added node 0x%lx", 1087 __func__, node); 1088 1089 /* attempt to add a port */ 1090 if ((rv = vlds_add_mdeg_port(mdp, node)) != MDEG_SUCCESS) { 1091 if (vlds_ports_inited) { 1092 cmn_err(CE_NOTE, "%s: unable to add port, " 1093 "err = %d", __func__, rv); 1094 } 1095 } 1096 } 1097 1098 /* process removed ports */ 1099 for (idx = 0; idx < resp->removed.nelem; idx++) { 1100 mdp = resp->removed.mdp; 1101 node = resp->removed.mdep[idx]; 1102 1103 DS_DBG_VLDS(CE_NOTE, "%s: processing removed node 0x%lx", 1104 __func__, node); 1105 1106 /* read in the port's id property */ 1107 if (md_get_prop_val(mdp, node, "id", &portno)) { 1108 cmn_err(CE_NOTE, "%s: node 0x%lx of removed list " 1109 "has no 'id' property", __func__, node); 1110 continue; 1111 } 1112 1113 /* attempt to remove a port */ 1114 if ((rv = ds_remove_port(portno, 0)) != 0) { 1115 cmn_err(CE_NOTE, "%s: unable to remove port %lu, " 1116 " err %d", __func__, portno, rv); 1117 } 1118 } 1119 1120 vlds_ports_inited = 1; 1121 1122 return (MDEG_SUCCESS); 1123 } 1124 1125 /* register callback to mdeg */ 1126 static int 1127 vlds_mdeg_register(void) 1128 { 1129 int rv; 1130 1131 DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_register: entered"); 1132 1133 /* perform the registration */ 1134 rv = mdeg_register(&vlds_node_template, &vlds_port_match, vlds_mdeg_cb, 1135 NULL, &vlds_mdeg_hdl); 1136 1137 if (rv != MDEG_SUCCESS) { 1138 cmn_err(CE_NOTE, "vlds_mdeg_register: mdeg_register " 1139 "failed, err = %d", rv); 1140 return (DDI_FAILURE); 1141 } 1142 1143 return (DDI_SUCCESS); 1144 } 1145 1146 /* unregister callback from mdeg */ 1147 static int 1148 vlds_mdeg_unregister(void) 1149 { 1150 DS_DBG_VLDS(CE_NOTE, "vlds_mdeg_unregister: hdl=0x%lx", vlds_mdeg_hdl); 1151 1152 return (mdeg_unregister(vlds_mdeg_hdl)); 1153 } 1154 1155 static int 1156 vlds_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id) 1157 { 1158 int num_nodes, nchan; 1159 size_t listsz; 1160 mde_cookie_t *listp; 1161 1162 /* 1163 * Find the channel-endpoint node(s) (which should be under this 1164 * port node) which contain the channel id(s). 1165 */ 1166 if ((num_nodes = md_node_count(mdp)) <= 0) { 1167 cmn_err(CE_NOTE, "%s: invalid number of channel-endpoint nodes " 1168 "found (%d)", __func__, num_nodes); 1169 return (-1); 1170 } 1171 1172 /* allocate space for node list */ 1173 listsz = num_nodes * sizeof (mde_cookie_t); 1174 listp = kmem_alloc(listsz, KM_SLEEP); 1175 1176 nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"), 1177 md_find_name(mdp, "fwd"), listp); 1178 1179 if (nchan <= 0) { 1180 cmn_err(CE_NOTE, "%s: no channel-endpoint nodes found", 1181 __func__); 1182 kmem_free(listp, listsz); 1183 return (-1); 1184 } 1185 1186 DS_DBG_VLDS(CE_NOTE, "%s: %d channel-endpoint nodes found", __func__, 1187 nchan); 1188 1189 /* use property from first node found */ 1190 if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) { 1191 cmn_err(CE_NOTE, "%s: channel-endpoint has no 'id' property", 1192 __func__); 1193 kmem_free(listp, listsz); 1194 return (-1); 1195 } 1196 1197 kmem_free(listp, listsz); 1198 1199 return (0); 1200 } 1201 1202 /* add a DS services port */ 1203 static int 1204 vlds_add_mdeg_port(md_t *mdp, mde_cookie_t node) 1205 { 1206 uint64_t portno; 1207 uint64_t ldc_id; 1208 int rv; 1209 uint64_t dhdl; 1210 char *dom_name; 1211 1212 /* read in the port's id property */ 1213 if (md_get_prop_val(mdp, node, "id", &portno)) { 1214 cmn_err(CE_NOTE, "%s: node 0x%lx of added list has no " 1215 "'id' property", __func__, node); 1216 return (MDEG_FAILURE); 1217 } 1218 1219 if (portno >= DS_MAX_PORTS) { 1220 cmn_err(CE_NOTE, "%s: found port number (%lu) " 1221 "larger than maximum supported number of ports", __func__, 1222 portno); 1223 return (MDEG_FAILURE); 1224 } 1225 1226 /* get all channels for this device (currently only one) */ 1227 if (vlds_get_port_channel(mdp, node, &ldc_id) == -1) { 1228 return (MDEG_FAILURE); 1229 } 1230 1231 if (md_get_prop_val(mdp, node, VLDS_MD_REM_DOMAIN_HDL, &dhdl) != 0) { 1232 cmn_err(CE_NOTE, "!ds%lx: %s no %s property", portno, __func__, 1233 VLDS_MD_REM_DOMAIN_HDL); 1234 dhdl = DS_DHDL_INVALID; 1235 } 1236 1237 if (md_get_prop_str(mdp, node, VLDS_MD_REM_DOMAIN_NAME, &dom_name) 1238 != 0) { 1239 cmn_err(CE_NOTE, "!ds%lx: %s no %s property", portno, __func__, 1240 VLDS_MD_REM_DOMAIN_NAME); 1241 dom_name = NULL; 1242 } 1243 1244 rv = ds_add_port(portno, ldc_id, dhdl, dom_name, vlds_ports_inited); 1245 1246 if (rv != 0) { 1247 if (vlds_ports_inited) { 1248 DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx " 1249 "failed err = %d", portno, __func__, ldc_id, rv); 1250 } 1251 return (MDEG_FAILURE); 1252 } 1253 1254 DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx inited", portno, 1255 __func__, ldc_id); 1256 1257 return (MDEG_SUCCESS); 1258 } 1259 1260 static void 1261 vlds_mdeg_init(void) 1262 { 1263 md_t *mdp; 1264 int num_nodes; 1265 int listsz; 1266 mde_cookie_t rootnode; 1267 mde_cookie_t vldsnode; 1268 mde_cookie_t *vlds_nodes = NULL; 1269 int nvlds; 1270 int i; 1271 ds_domain_hdl_t dhdl; 1272 char *dom_name; 1273 char *svc_name; 1274 1275 if ((mdp = md_get_handle()) == NULL) { 1276 cmn_err(CE_NOTE, "Unable to initialize machine description"); 1277 return; 1278 } 1279 1280 num_nodes = md_node_count(mdp); 1281 ASSERT(num_nodes > 0); 1282 1283 listsz = num_nodes * sizeof (mde_cookie_t); 1284 1285 /* allocate temporary storage for MD scans */ 1286 vlds_nodes = kmem_zalloc(listsz, KM_SLEEP); 1287 1288 rootnode = md_root_node(mdp); 1289 ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE); 1290 1291 /* 1292 * Search for Virtual Domain Service node. 1293 */ 1294 nvlds = md_scan_dag(mdp, rootnode, md_find_name(mdp, 1295 VLDS_MD_VIRT_DEV_NAME), md_find_name(mdp, "fwd"), vlds_nodes); 1296 1297 if (nvlds <= 0) { 1298 DS_DBG_MD(CE_NOTE, "No '%s' nodes in MD", 1299 VLDS_MD_VIRT_DEV_NAME); 1300 goto done; 1301 } 1302 1303 for (i = 0; i < nvlds; i++) { 1304 if (md_get_prop_str(mdp, vlds_nodes[i], "name", &svc_name)) { 1305 DS_DBG_MD(CE_NOTE, "%s: missing 'name' property for" 1306 " IO node %d\n", __func__, i); 1307 continue; 1308 } 1309 1310 if (strcmp(svc_name, VLDS_MD_VIRT_ROOT_NAME) == 0) { 1311 vldsnode = vlds_nodes[i]; 1312 break; 1313 } 1314 } 1315 1316 if (i >= nvlds) { 1317 DS_DBG_MD(CE_NOTE, "No '%s' node in MD", 1318 VLDS_MD_VIRT_ROOT_NAME); 1319 goto done; 1320 } 1321 1322 if (md_get_prop_val(mdp, vldsnode, VLDS_MD_DOMAIN_HDL, &dhdl) != 0) { 1323 DS_DBG_MD(CE_NOTE, "No '%s' property for '%s' node in MD", 1324 VLDS_MD_DOMAIN_HDL, VLDS_MD_VIRT_ROOT_NAME); 1325 dhdl = DS_DHDL_INVALID; 1326 } 1327 if (md_get_prop_str(mdp, vldsnode, VLDS_MD_DOMAIN_NAME, &dom_name) 1328 != 0) { 1329 DS_DBG_MD(CE_NOTE, "No '%s' property for '%s' node in MD", 1330 VLDS_MD_DOMAIN_NAME, VLDS_MD_VIRT_ROOT_NAME); 1331 dom_name = NULL; 1332 } 1333 DS_DBG_MD(CE_NOTE, "My Domain Hdl: 0x%lx, Name: '%s'", dhdl, 1334 dom_name == NULL ? "NULL" : dom_name); 1335 ds_set_my_dom_hdl_name(dhdl, dom_name); 1336 1337 done: 1338 DS_FREE(vlds_nodes, listsz); 1339 1340 (void) md_fini_handle(mdp); 1341 } 1342 1343 static void 1344 vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl) 1345 { 1346 nvlist_t *nvl = NULL; 1347 ds_domain_hdl_t dhdl; 1348 char *servicep; 1349 uint32_t flags; 1350 int minor; 1351 vlds_state_t *sp; 1352 vlds_svc_info_t *dpsp; 1353 1354 ds_cbarg_get_flags(arg, &flags); 1355 ASSERT((flags & DSSF_ISUSER) != 0); 1356 1357 if ((flags & DSSF_DATACB_VALID) == 0) { 1358 /* 1359 * must allocate and init the svc read queue. 1360 */ 1361 DS_DBG_VLDS(CE_NOTE, "%s: hdl: 0x%lx initing recvq", __func__, 1362 hdl); 1363 dpsp = DS_MALLOC(sizeof (vlds_svc_info_t)); 1364 vlds_recvq_init(dpsp); 1365 ds_cbarg_set_drv_per_svc_ptr(arg, dpsp); 1366 } 1367 1368 if ((flags & DSSF_REGCB_VALID) != 0) { 1369 ds_cbarg_get_drv_info(arg, &minor); 1370 sp = ddi_get_soft_state(vlds_statep, minor); 1371 ASSERT(sp != NULL); 1372 ASSERT(sp->evchan != NULL); 1373 ds_cbarg_get_domain(arg, &dhdl); 1374 ds_cbarg_get_service_id(arg, &servicep); 1375 DS_DBG_VLDS(CE_NOTE, "%s: regcb: hdl: 0x%lx, ver%d.%d, " 1376 " dhdl: 0x%lx", __func__, hdl, ver->major, 1377 ver->minor, dhdl); 1378 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) || 1379 nvlist_add_uint64(nvl, VLDS_HDL, hdl) || 1380 nvlist_add_uint16(nvl, VLDS_VER_MAJOR, ver->major) || 1381 nvlist_add_uint16(nvl, VLDS_VER_MINOR, ver->minor) || 1382 nvlist_add_uint64(nvl, VLDS_DOMAIN_HDL, dhdl) || 1383 nvlist_add_string(nvl, VLDS_SERVICE_ID, servicep) || 1384 nvlist_add_boolean_value(nvl, VLDS_ISCLIENT, 1385 (flags & DSSF_ISCLIENT) != 0) || 1386 sysevent_evc_publish(sp->evchan, EC_VLDS, 1387 ESC_VLDS_REGISTER, "sun.com", "kernel", nvl, EVCH_SLEEP)) { 1388 cmn_err(CE_WARN, "Failed to send REG Callback"); 1389 } else { 1390 DS_DBG_VLDS(CE_NOTE, "%s: sysevent_evc_publish " 1391 "succeeded", __func__); 1392 } 1393 nvlist_free(nvl); 1394 } 1395 } 1396 1397 static void 1398 vlds_user_unreg_cb(ds_cb_arg_t arg) 1399 { 1400 nvlist_t *nvl = NULL; 1401 int minor; 1402 ds_svc_hdl_t hdl; 1403 vlds_state_t *sp; 1404 void *dpsp; 1405 uint32_t flags; 1406 1407 ds_cbarg_get_flags(arg, &flags); 1408 ASSERT((flags & DSSF_ISUSER) != 0); 1409 1410 if ((flags & DSSF_DATACB_VALID) == 0) { 1411 ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp); 1412 if (dpsp) { 1413 DS_DBG_VLDS(CE_NOTE, "%s: unregcb draining recvq", 1414 __func__); 1415 vlds_recvq_drain(dpsp); 1416 vlds_recvq_destroy(dpsp); 1417 ds_cbarg_set_drv_per_svc_ptr(arg, NULL); 1418 } 1419 } 1420 1421 if ((flags & DSSF_UNREGCB_VALID) != 0) { 1422 ds_cbarg_get_hdl(arg, &hdl); 1423 DS_DBG_VLDS(CE_NOTE, "%s: unregcb hdl: 0x%lx", __func__, 1424 hdl); 1425 ds_cbarg_get_drv_info(arg, &minor); 1426 sp = ddi_get_soft_state(vlds_statep, minor); 1427 ASSERT(sp != NULL); 1428 ASSERT(sp->evchan != NULL); 1429 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) || 1430 nvlist_add_uint64(nvl, VLDS_HDL, hdl) || 1431 sysevent_evc_publish(sp->evchan, EC_VLDS, 1432 ESC_VLDS_UNREGISTER, "sun.com", "kernel", nvl, 1433 EVCH_SLEEP)) { 1434 cmn_err(CE_WARN, "Failed to send UNREG Callback"); 1435 } 1436 nvlist_free(nvl); 1437 } 1438 } 1439 1440 static void 1441 vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen) 1442 { 1443 nvlist_t *nvl = NULL; 1444 ds_svc_hdl_t hdl; 1445 int minor; 1446 void *dpsp; 1447 vlds_state_t *sp; 1448 uint32_t flags; 1449 1450 ds_cbarg_get_flags(arg, &flags); 1451 ASSERT((flags & DSSF_ISUSER) != 0); 1452 1453 if ((flags & DSSF_DATACB_VALID) == 0) { 1454 ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp); 1455 ASSERT(dpsp != NULL); 1456 DS_DBG_VLDS(CE_NOTE, "%s: datacb: to recvq: buflen: %ld", 1457 __func__, buflen); 1458 (void) vlds_recvq_put_data(dpsp, buf, buflen); 1459 } else { 1460 ds_cbarg_get_hdl(arg, &hdl); 1461 DS_DBG_VLDS(CE_NOTE, "%s: datacb: usercb: hdl: 0x%lx, " 1462 " buflen: %ld", __func__, hdl, buflen); 1463 ds_cbarg_get_drv_info(arg, &minor); 1464 sp = ddi_get_soft_state(vlds_statep, minor); 1465 ASSERT(sp != NULL); 1466 ASSERT(sp->evchan != NULL); 1467 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) || 1468 nvlist_add_uint64(nvl, VLDS_HDL, hdl) || 1469 nvlist_add_byte_array(nvl, VLDS_DATA, buf, buflen) || 1470 sysevent_evc_publish(sp->evchan, EC_VLDS, 1471 ESC_VLDS_DATA, "sun.com", "kernel", nvl, EVCH_SLEEP)) { 1472 cmn_err(CE_WARN, "Failed to send DATA Callback"); 1473 } 1474 } 1475 nvlist_free(nvl); 1476 } 1477 1478 /* 1479 * Initialize receive queue if request is from user land but 1480 * data callback is null (implying user will be using ds_recv_msg). 1481 */ 1482 static void 1483 vlds_recvq_init(vlds_svc_info_t *dpsp) 1484 { 1485 dpsp->state = VLDS_RECV_OK; 1486 mutex_init(&dpsp->recv_lock, NULL, MUTEX_DRIVER, NULL); 1487 cv_init(&dpsp->recv_cv, NULL, CV_DRIVER, NULL); 1488 dpsp->recv_headp = NULL; 1489 dpsp->recv_tailp = NULL; 1490 dpsp->recv_size = 0; 1491 dpsp->recv_cnt = 0; 1492 } 1493 1494 static void 1495 vlds_recvq_destroy(vlds_svc_info_t *dpsp) 1496 { 1497 ASSERT(dpsp->state == VLDS_RECV_UNREG_PENDING); 1498 ASSERT(dpsp->recv_size == 0); 1499 ASSERT(dpsp->recv_cnt == 0); 1500 ASSERT(dpsp->recv_headp == NULL); 1501 ASSERT(dpsp->recv_tailp == NULL); 1502 1503 mutex_destroy(&dpsp->recv_lock); 1504 cv_destroy(&dpsp->recv_cv); 1505 DS_FREE(dpsp, sizeof (vlds_svc_info_t)); 1506 } 1507 1508 static int 1509 vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen, 1510 size_t *msglenp, int mode) 1511 { 1512 vlds_recv_hdr_t *rhp; 1513 int rv; 1514 size_t msglen; 1515 1516 mutex_enter(&dpsp->recv_lock); 1517 while (dpsp->recv_size == 0) { 1518 ASSERT(dpsp->recv_cnt == 0); 1519 if (dpsp->state == VLDS_RECV_UNREG_PENDING) 1520 break; 1521 1522 if (dpsp->state == VLDS_RECV_OVERFLOW) { 1523 DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow", 1524 __func__); 1525 dpsp->state = VLDS_RECV_OK; 1526 mutex_exit(&dpsp->recv_lock); 1527 return (ENOBUFS); 1528 } 1529 /* 1530 * Passing in a buflen of 0 allows user to poll for msgs. 1531 */ 1532 if (buflen == 0) { 1533 mutex_exit(&dpsp->recv_lock); 1534 *msglenp = 0; 1535 return (EFBIG); 1536 } 1537 dpsp->recv_nreaders += 1; 1538 rv = cv_wait_sig(&dpsp->recv_cv, &dpsp->recv_lock); 1539 dpsp->recv_nreaders -= 1; 1540 if (rv == 0) { 1541 DS_DBG_RCVQ(CE_NOTE, "%s: signal EINTR", __func__); 1542 mutex_exit(&dpsp->recv_lock); 1543 return (EINTR); 1544 } 1545 } 1546 if (dpsp->state == VLDS_RECV_UNREG_PENDING) { 1547 DS_DBG_RCVQ(CE_NOTE, "%s: unreg pending", __func__); 1548 cv_broadcast(&dpsp->recv_cv); 1549 mutex_exit(&dpsp->recv_lock); 1550 return (EINVAL); 1551 } 1552 ASSERT(dpsp->recv_headp != NULL); 1553 rhp = dpsp->recv_headp; 1554 1555 /* 1556 * Don't transfer truncated data, return EFBIG error if user-supplied 1557 * buffer is too small. 1558 */ 1559 if (rhp->datasz > buflen) { 1560 *msglenp = rhp->datasz; 1561 mutex_exit(&dpsp->recv_lock); 1562 return (EFBIG); 1563 } 1564 if (rhp == dpsp->recv_tailp) { 1565 dpsp->recv_headp = NULL; 1566 dpsp->recv_tailp = NULL; 1567 } else { 1568 dpsp->recv_headp = rhp->next; 1569 ASSERT(dpsp->recv_headp != NULL); 1570 } 1571 ASSERT(dpsp->recv_cnt > 0); 1572 dpsp->recv_size -= rhp->datasz; 1573 dpsp->recv_cnt -= 1; 1574 mutex_exit(&dpsp->recv_lock); 1575 1576 msglen = rhp->datasz; 1577 rv = ddi_copyout(rhp->data, buf, msglen, mode); 1578 1579 if (rv == 0) { 1580 DS_DBG_VLDS(CE_NOTE, "%s: user data dequeued msglen: %ld", 1581 __func__, rhp->datasz); 1582 DS_DUMP_MSG(DS_DBG_FLAG_VLDS, rhp->data, rhp->datasz); 1583 } 1584 1585 DS_FREE(rhp->data, rhp->datasz); 1586 DS_FREE(rhp, sizeof (vlds_recv_hdr_t)); 1587 1588 if (rv != 0) { 1589 DS_DBG_VLDS(CE_NOTE, "%s: copyout failed", __func__); 1590 return (EFAULT); 1591 } 1592 1593 *msglenp = msglen; 1594 return (0); 1595 } 1596 1597 uint64_t vlds_recv_drain_delay_time = 1 * MILLISEC; 1598 1599 static void 1600 vlds_recvq_drain(vlds_svc_info_t *dpsp) 1601 { 1602 vlds_recv_hdr_t *rhp, *nextp; 1603 1604 mutex_enter(&dpsp->recv_lock); 1605 dpsp->state = VLDS_RECV_UNREG_PENDING; 1606 for (rhp = dpsp->recv_tailp; rhp != NULL; rhp = nextp) { 1607 nextp = rhp->next; 1608 DS_FREE(rhp->data, rhp->datasz); 1609 DS_FREE(rhp, sizeof (vlds_recv_hdr_t)); 1610 } 1611 dpsp->recv_headp = NULL; 1612 dpsp->recv_tailp = NULL; 1613 dpsp->recv_size = 0; 1614 dpsp->recv_cnt = 0; 1615 1616 /* 1617 * Make sure other readers have exited. 1618 */ 1619 while (dpsp->recv_nreaders > 0) { 1620 cv_broadcast(&dpsp->recv_cv); 1621 mutex_exit(&dpsp->recv_lock); 1622 delay(vlds_recv_drain_delay_time); 1623 mutex_enter(&dpsp->recv_lock); 1624 } 1625 1626 mutex_exit(&dpsp->recv_lock); 1627 } 1628 1629 static int 1630 vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen) 1631 { 1632 vlds_recv_hdr_t *rhp; 1633 1634 mutex_enter(&dpsp->recv_lock); 1635 if (dpsp->state != VLDS_RECV_UNREG_PENDING) { 1636 /* 1637 * If we've already encountered an overflow, or there 1638 * are pending messages and either queue size and 1639 * message limits will be exceeded with this message, 1640 * we mark the recvq as overflowed and return an ENOBUFS 1641 * error. This allows the enqueuing of one big message 1642 * or several little messages. 1643 */ 1644 if ((dpsp->state == VLDS_RECV_OVERFLOW) || 1645 ((dpsp->recv_cnt != 0) && 1646 ((dpsp->recv_size + buflen) > vlds_recvq_maxsize) || 1647 ((dpsp->recv_cnt + 1) > vlds_recvq_maxmsg))) { 1648 DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow", 1649 __func__); 1650 dpsp->state = VLDS_RECV_OVERFLOW; 1651 cv_broadcast(&dpsp->recv_cv); 1652 mutex_exit(&dpsp->recv_lock); 1653 return (ENOBUFS); 1654 } 1655 1656 DS_DBG_RCVQ(CE_NOTE, "%s: user data enqueued msglen: %ld", 1657 __func__, buflen); 1658 DS_DUMP_MSG(DS_DBG_FLAG_RCVQ, buf, buflen); 1659 rhp = DS_MALLOC(sizeof (vlds_recv_hdr_t)); 1660 rhp->data = DS_MALLOC(buflen); 1661 (void) memcpy(rhp->data, buf, buflen); 1662 rhp->datasz = buflen; 1663 rhp->next = NULL; 1664 if (dpsp->recv_headp == NULL) { 1665 dpsp->recv_headp = rhp; 1666 dpsp->recv_tailp = rhp; 1667 } else { 1668 dpsp->recv_tailp->next = rhp; 1669 dpsp->recv_tailp = rhp; 1670 } 1671 dpsp->recv_size += rhp->datasz; 1672 dpsp->recv_cnt += 1; 1673 cv_broadcast(&dpsp->recv_cv); 1674 } 1675 mutex_exit(&dpsp->recv_lock); 1676 return (0); 1677 } 1678 1679 static int 1680 vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen, size_t *msglenp, 1681 int mode) 1682 { 1683 void *dpsp; 1684 ds_cb_arg_t cbarg; 1685 uint32_t flags; 1686 int rv; 1687 1688 if ((rv = ds_hdl_get_cbarg(hdl, &cbarg)) != 0) { 1689 DS_DBG_VLDS(CE_NOTE, "%s: handle %lx not found (%d)", __func__, 1690 hdl, rv); 1691 return (rv); 1692 } 1693 ds_cbarg_get_flags(cbarg, &flags); 1694 if ((flags & DSSF_ISUSER) == 0 || (flags & DSSF_DATACB_VALID) != 0) { 1695 DS_DBG_VLDS(CE_NOTE, "%s: invalid flags: %x", __func__, flags); 1696 return (EINVAL); 1697 } 1698 ds_cbarg_get_drv_per_svc_ptr(cbarg, &dpsp); 1699 if (dpsp == NULL) { 1700 DS_DBG_VLDS(CE_NOTE, "%s: recv on non-ready handle: %x", 1701 __func__, flags); 1702 return (ENXIO); 1703 } 1704 rv = vlds_recvq_get_data(dpsp, buf, buflen, msglenp, mode); 1705 return (rv); 1706 } 1707