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