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