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 /* 29 * Domain Services Module System Specific Code. 30 * 31 * The Domain Services (DS) module is responsible for communication 32 * with external service entities. It provides a kernel API for clients to 33 * publish capabilities and handles the low level communication and 34 * version negotiation required to export those capabilities to any 35 * interested service entity. Once a capability has been successfully 36 * registered with a service entity, the DS module facilitates all 37 * data transfers between the service entity and the client providing 38 * that particular capability. 39 * 40 * This file provides the system interfaces that are required for 41 * the ds.c module, which is common to both Solaris and VBSC (linux). 42 */ 43 44 #include <sys/modctl.h> 45 #include <sys/ksynch.h> 46 #include <sys/taskq.h> 47 #include <sys/disp.h> 48 #include <sys/cmn_err.h> 49 #include <sys/note.h> 50 #include <sys/mach_descrip.h> 51 #include <sys/mdesc.h> 52 #include <sys/mdeg.h> 53 #include <sys/ldc.h> 54 #include <sys/ds.h> 55 #include <sys/ds_impl.h> 56 57 /* 58 * Taskq for internal task processing 59 */ 60 static taskq_t *ds_taskq; 61 62 /* 63 * The actual required number of parallel threads is not expected 64 * to be very large. Use the maximum number of CPUs in the system 65 * as a rough upper bound. 66 */ 67 #define DS_MAX_TASKQ_THR NCPU 68 #define DS_DISPATCH(fn, arg) taskq_dispatch(ds_taskq, fn, arg, TQ_SLEEP) 69 70 ds_domain_hdl_t ds_my_domain_hdl = DS_DHDL_INVALID; 71 char *ds_my_domain_name = NULL; 72 73 #ifdef DEBUG 74 /* 75 * Debug Flag 76 */ 77 uint_t ds_debug = 0; 78 #endif /* DEBUG */ 79 80 /* initialization functions */ 81 static void ds_init(void); 82 static void ds_fini(void); 83 static int ds_ports_init(void); 84 static int ds_ports_fini(void); 85 86 /* port utilities */ 87 static int ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan); 88 89 /* log functions */ 90 static void ds_log_init(void); 91 static void ds_log_fini(void); 92 static int ds_log_remove(void); 93 static void ds_log_purge(void *arg); 94 95 static struct modlmisc modlmisc = { 96 &mod_miscops, 97 "Domain Services 1.9" 98 }; 99 100 static struct modlinkage modlinkage = { 101 MODREV_1, 102 (void *)&modlmisc, 103 NULL 104 }; 105 106 int 107 _init(void) 108 { 109 int rv; 110 111 /* 112 * Perform all internal setup before initializing 113 * the DS ports. This ensures that events can be 114 * processed as soon as the port comes up. 115 */ 116 ds_init(); 117 118 /* force attach channel nexus */ 119 (void) i_ddi_attach_hw_nodes("cnex"); 120 121 if ((rv = ds_ports_init()) != 0) { 122 cmn_err(CE_WARN, "Domain Services initialization failed"); 123 ds_fini(); 124 return (rv); 125 } 126 127 if ((rv = mod_install(&modlinkage)) != 0) { 128 (void) ds_ports_fini(); 129 ds_fini(); 130 } 131 132 return (rv); 133 } 134 135 int 136 _info(struct modinfo *modinfop) 137 { 138 return (mod_info(&modlinkage, modinfop)); 139 } 140 141 int 142 _fini(void) 143 { 144 int rv; 145 146 if ((rv = mod_remove(&modlinkage)) == 0) { 147 (void) ds_ports_fini(); 148 ds_fini(); 149 } 150 151 return (rv); 152 } 153 154 static void 155 ds_fini(void) 156 { 157 /* 158 * Flip the enabled switch to make sure that no 159 * incoming events get dispatched while things 160 * are being torn down. 161 */ 162 ds_enabled = B_FALSE; 163 164 /* 165 * Destroy the taskq. 166 */ 167 taskq_destroy(ds_taskq); 168 169 /* 170 * Destroy the message log. 171 */ 172 ds_log_fini(); 173 174 /* 175 * Deallocate the table of registered services 176 */ 177 178 /* clear out all entries */ 179 mutex_enter(&ds_svcs.lock); 180 (void) ds_walk_svcs(ds_svc_free, NULL); 181 mutex_exit(&ds_svcs.lock); 182 183 /* destroy the table itself */ 184 DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *)); 185 mutex_destroy(&ds_svcs.lock); 186 bzero(&ds_svcs, sizeof (ds_svcs)); 187 } 188 189 /* 190 * Initialize the list of ports based on the MD. 191 */ 192 static int 193 ds_ports_init(void) 194 { 195 int idx; 196 int rv = 0; 197 md_t *mdp; 198 int num_nodes; 199 int listsz; 200 mde_cookie_t rootnode; 201 mde_cookie_t dsnode; 202 mde_cookie_t *portp = NULL; 203 mde_cookie_t *chanp = NULL; 204 int nport; 205 int nchan; 206 207 if ((mdp = md_get_handle()) == NULL) { 208 cmn_err(CE_WARN, "Unable to initialize machine description"); 209 return (-1); 210 } 211 212 num_nodes = md_node_count(mdp); 213 ASSERT(num_nodes > 0); 214 215 listsz = num_nodes * sizeof (mde_cookie_t); 216 217 /* allocate temporary storage for MD scans */ 218 portp = kmem_zalloc(listsz, KM_SLEEP); 219 chanp = kmem_zalloc(listsz, KM_SLEEP); 220 221 rootnode = md_root_node(mdp); 222 ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE); 223 224 /* 225 * The root of the search for DS port nodes is the 226 * DS node. Perform a scan to find that node. 227 */ 228 nport = md_scan_dag(mdp, rootnode, md_find_name(mdp, DS_MD_ROOT_NAME), 229 md_find_name(mdp, "fwd"), portp); 230 231 if (nport <= 0) { 232 DS_DBG_MD(CE_NOTE, "No '%s' node in MD", DS_MD_ROOT_NAME); 233 goto done; 234 } 235 236 /* expecting only one DS node */ 237 if (nport != 1) { 238 DS_DBG_MD(CE_NOTE, "Expected one '%s' node in the MD, found %d", 239 DS_MD_ROOT_NAME, nport); 240 } 241 242 dsnode = portp[0]; 243 244 /* find all the DS ports in the MD */ 245 nport = md_scan_dag(mdp, dsnode, md_find_name(mdp, DS_MD_PORT_NAME), 246 md_find_name(mdp, "fwd"), portp); 247 248 if (nport <= 0) { 249 DS_DBG_MD(CE_NOTE, "No '%s' nodes in MD", DS_MD_PORT_NAME); 250 goto done; 251 } 252 253 /* 254 * Initialize all the ports found in the MD. 255 */ 256 for (idx = 0; idx < nport; idx++) { 257 258 /* get the channels for this port */ 259 nchan = md_scan_dag(mdp, portp[idx], 260 md_find_name(mdp, DS_MD_CHAN_NAME), 261 md_find_name(mdp, "fwd"), chanp); 262 263 if (nchan <= 0) { 264 cmn_err(CE_WARN, "No '%s' node for DS port", 265 DS_MD_CHAN_NAME); 266 rv = -1; 267 goto done; 268 } 269 270 /* expecting only one channel */ 271 if (nchan != 1) { 272 DS_DBG_MD(CE_NOTE, "Expected one '%s' node for DS " 273 " port, found %d", DS_MD_CHAN_NAME, nchan); 274 } 275 276 if (ds_port_add(mdp, portp[idx], chanp[0]) != 0) { 277 rv = -1; 278 goto done; 279 } 280 } 281 282 done: 283 if (rv != 0) 284 (void) ds_ports_fini(); 285 286 DS_FREE(portp, listsz); 287 DS_FREE(chanp, listsz); 288 289 (void) md_fini_handle(mdp); 290 291 return (rv); 292 } 293 294 static int 295 ds_ports_fini(void) 296 { 297 int idx; 298 299 /* 300 * Tear down each initialized port. 301 */ 302 for (idx = 0; idx < DS_MAX_PORTS; idx++) { 303 if (DS_PORT_IN_SET(ds_allports, idx)) { 304 (void) ds_remove_port(idx, 1); 305 } 306 } 307 308 return (0); 309 } 310 311 static int 312 ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan) 313 { 314 uint64_t port_id; 315 uint64_t ldc_id; 316 uint8_t *ldcidsp; 317 int len; 318 319 /* get the ID for this port */ 320 if (md_get_prop_val(mdp, port, "id", &port_id) != 0) { 321 cmn_err(CE_WARN, "%s: port 'id' property not found", 322 __func__); 323 return (-1); 324 } 325 326 /* sanity check the port id */ 327 if (port_id > DS_MAX_PORT_ID) { 328 cmn_err(CE_WARN, "%s: port ID %ld out of range", 329 __func__, port_id); 330 return (-1); 331 } 332 333 /* get the channel ID for this port */ 334 if (md_get_prop_val(mdp, chan, "id", &ldc_id) != 0) { 335 cmn_err(CE_WARN, "ds@%lx: %s: no channel 'id' property", 336 port_id, __func__); 337 return (-1); 338 } 339 340 if (ds_add_port(port_id, ldc_id, DS_DHDL_INVALID, NULL, 1) != 0) 341 return (-1); 342 343 /* 344 * Identify the SP Port. The SP port is the only one with 345 * the "ldc-ids" property, and is only on the primary domain. 346 */ 347 if (ds_sp_port_id == DS_PORTID_INVALID && 348 md_get_prop_data(mdp, port, "ldc-ids", &ldcidsp, &len) == 0) { 349 ds_sp_port_id = port_id; 350 } 351 352 return (0); 353 } 354 355 void 356 ds_set_my_dom_hdl_name(ds_domain_hdl_t dhdl, char *name) 357 { 358 ds_my_domain_hdl = dhdl; 359 if (ds_my_domain_name != NULL) { 360 DS_FREE(ds_my_domain_name, strlen(ds_my_domain_name)+1); 361 ds_my_domain_name = NULL; 362 } 363 if (name != NULL) { 364 ds_my_domain_name = ds_strdup(name); 365 } 366 } 367 368 void 369 ds_init() 370 { 371 ds_common_init(); 372 373 /* 374 * Create taskq for internal processing threads. This 375 * includes processing incoming request messages and 376 * sending out of band registration messages. 377 */ 378 ds_taskq = taskq_create("ds_taskq", 1, minclsyspri, 1, 379 DS_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC); 380 381 /* 382 * Initialize the message log. 383 */ 384 ds_log_init(); 385 } 386 387 int 388 ds_sys_dispatch_func(void (func)(void *), void *arg) 389 { 390 return (DS_DISPATCH(func, arg) == TASKQID_INVALID); 391 } 392 393 /* 394 * Drain event queue, if necessary. 395 */ 396 void 397 ds_sys_drain_events(ds_port_t *port) 398 { 399 _NOTE(ARGUNUSED(port)) 400 } 401 402 /* 403 * System specific port initalization. 404 */ 405 void 406 ds_sys_port_init(ds_port_t *port) 407 { 408 _NOTE(ARGUNUSED(port)) 409 } 410 411 /* 412 * System specific port teardown. 413 */ 414 void 415 ds_sys_port_fini(ds_port_t *port) 416 { 417 _NOTE(ARGUNUSED(port)) 418 } 419 420 /* 421 * System specific LDC channel initialization. 422 */ 423 void 424 ds_sys_ldc_init(ds_port_t *port) 425 { 426 int rv; 427 char ebuf[DS_EBUFSIZE]; 428 429 ASSERT(MUTEX_HELD(&port->lock)); 430 431 if ((rv = ldc_open(port->ldc.hdl)) != 0) { 432 cmn_err(CE_WARN, "ds@%lx: %s: ldc_open: %s", 433 PORTID(port), __func__, ds_errno_to_str(rv, ebuf)); 434 return; 435 } 436 437 (void) ldc_up(port->ldc.hdl); 438 439 (void) ldc_status(port->ldc.hdl, &port->ldc.state); 440 441 DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: initial LDC state 0x%x", 442 PORTID(port), __func__, port->ldc.state); 443 444 port->state = DS_PORT_LDC_INIT; 445 } 446 447 /* 448 * DS message log 449 * 450 * Locking: The message log is protected by a single mutex. This 451 * protects all fields in the log structure itself as well as 452 * everything in the entry structures on both the log and the 453 * free list. 454 */ 455 static struct log { 456 ds_log_entry_t *head; /* head of the log */ 457 ds_log_entry_t *freelist; /* head of the free list */ 458 size_t size; /* size of the log in bytes */ 459 uint32_t nentry; /* number of entries */ 460 kmutex_t lock; /* log lock */ 461 } ds_log; 462 463 /* log soft limit */ 464 uint_t ds_log_sz = DS_LOG_DEFAULT_SZ; 465 466 /* initial pool of log entry structures */ 467 static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL]; 468 469 /* 470 * Logging Support 471 */ 472 static void 473 ds_log_init(void) 474 { 475 /* initialize global lock */ 476 mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL); 477 478 mutex_enter(&ds_log.lock); 479 480 /* initialize the log */ 481 ds_log.head = NULL; 482 ds_log.size = 0; 483 ds_log.nentry = 0; 484 485 /* initialize the free list */ 486 for (int i = 0; i < DS_LOG_NPOOL; i++) { 487 ds_log_entry_pool[i].next = ds_log.freelist; 488 ds_log.freelist = &ds_log_entry_pool[i]; 489 } 490 491 mutex_exit(&ds_log.lock); 492 493 DS_DBG_LOG(CE_NOTE, "ds_log initialized: size=%d bytes, " 494 " limit=%d bytes, ninit=%ld", ds_log_sz, DS_LOG_LIMIT, 495 DS_LOG_NPOOL); 496 } 497 498 static void 499 ds_log_fini(void) 500 { 501 ds_log_entry_t *next; 502 503 mutex_enter(&ds_log.lock); 504 505 /* clear out the log */ 506 while (ds_log.nentry > 0) 507 (void) ds_log_remove(); 508 509 /* 510 * Now all the entries are on the free list. 511 * Clear out the free list, deallocating any 512 * entry that was dynamically allocated. 513 */ 514 while (ds_log.freelist != NULL) { 515 next = ds_log.freelist->next; 516 517 if (!DS_IS_POOL_ENTRY(ds_log.freelist)) { 518 kmem_free(ds_log.freelist, sizeof (ds_log_entry_t)); 519 } 520 521 ds_log.freelist = next; 522 } 523 524 mutex_exit(&ds_log.lock); 525 526 mutex_destroy(&ds_log.lock); 527 } 528 529 static ds_log_entry_t * 530 ds_log_entry_alloc(void) 531 { 532 ds_log_entry_t *new = NULL; 533 534 ASSERT(MUTEX_HELD(&ds_log.lock)); 535 536 if (ds_log.freelist != NULL) { 537 new = ds_log.freelist; 538 ds_log.freelist = ds_log.freelist->next; 539 } 540 541 if (new == NULL) { 542 /* free list was empty */ 543 new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP); 544 } 545 546 ASSERT(new); 547 548 return (new); 549 } 550 551 static void 552 ds_log_entry_free(ds_log_entry_t *entry) 553 { 554 ASSERT(MUTEX_HELD(&ds_log.lock)); 555 556 if (entry == NULL) 557 return; 558 559 if (entry->data != NULL) { 560 kmem_free(entry->data, entry->datasz); 561 entry->data = NULL; 562 } 563 564 /* place entry on the free list */ 565 entry->next = ds_log.freelist; 566 ds_log.freelist = entry; 567 } 568 569 /* 570 * Add a message to the end of the log 571 */ 572 static int 573 ds_log_add(ds_log_entry_t *new) 574 { 575 ASSERT(MUTEX_HELD(&ds_log.lock)); 576 577 if (ds_log.head == NULL) { 578 579 new->prev = new; 580 new->next = new; 581 582 ds_log.head = new; 583 } else { 584 ds_log_entry_t *head = ds_log.head; 585 ds_log_entry_t *tail = ds_log.head->prev; 586 587 new->next = head; 588 new->prev = tail; 589 tail->next = new; 590 head->prev = new; 591 } 592 593 /* increase the log size, including the metadata size */ 594 ds_log.size += DS_LOG_ENTRY_SZ(new); 595 ds_log.nentry++; 596 597 DS_DBG_LOG(CE_NOTE, "ds_log: added %ld data bytes, %ld total bytes", 598 new->datasz, DS_LOG_ENTRY_SZ(new)); 599 600 return (0); 601 } 602 603 /* 604 * Remove an entry from the head of the log 605 */ 606 static int 607 ds_log_remove(void) 608 { 609 ds_log_entry_t *head; 610 611 ASSERT(MUTEX_HELD(&ds_log.lock)); 612 613 head = ds_log.head; 614 615 /* empty list */ 616 if (head == NULL) 617 return (0); 618 619 if (head->next == ds_log.head) { 620 /* one element list */ 621 ds_log.head = NULL; 622 } else { 623 head->next->prev = head->prev; 624 head->prev->next = head->next; 625 ds_log.head = head->next; 626 } 627 628 DS_DBG_LOG(CE_NOTE, "ds_log: removed %ld data bytes, %ld total bytes", 629 head->datasz, DS_LOG_ENTRY_SZ(head)); 630 631 ds_log.size -= DS_LOG_ENTRY_SZ(head); 632 ds_log.nentry--; 633 634 ds_log_entry_free(head); 635 636 return (0); 637 } 638 639 /* 640 * Replace the data in the entry at the front of the list with then 641 * new data. This has the effect of removing the oldest entry and 642 * adding the new entry. 643 */ 644 static int 645 ds_log_replace(int32_t dest, uint8_t *msg, size_t sz) 646 { 647 ds_log_entry_t *head; 648 649 ASSERT(MUTEX_HELD(&ds_log.lock)); 650 651 head = ds_log.head; 652 653 DS_DBG_LOG(CE_NOTE, "ds_log: replaced %ld data bytes (%ld total) with " 654 " %ld data bytes (%ld total)", head->datasz, 655 DS_LOG_ENTRY_SZ(head), sz, sz + sizeof (ds_log_entry_t)); 656 657 ds_log.size -= DS_LOG_ENTRY_SZ(head); 658 659 kmem_free(head->data, head->datasz); 660 661 head->data = msg; 662 head->datasz = sz; 663 head->timestamp = ddi_get_time(); 664 head->dest = dest; 665 666 ds_log.size += DS_LOG_ENTRY_SZ(head); 667 668 ds_log.head = head->next; 669 670 return (0); 671 } 672 673 static void 674 ds_log_purge(void *arg) 675 { 676 _NOTE(ARGUNUSED(arg)) 677 678 mutex_enter(&ds_log.lock); 679 680 DS_DBG_LOG(CE_NOTE, "ds_log: purging oldest log entries"); 681 682 while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) { 683 (void) ds_log_remove(); 684 } 685 686 mutex_exit(&ds_log.lock); 687 } 688 689 int 690 ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz) 691 { 692 int rv = 0; 693 void *data; 694 695 mutex_enter(&ds_log.lock); 696 697 /* allocate a local copy of the data */ 698 data = kmem_alloc(sz, KM_SLEEP); 699 bcopy(msg, data, sz); 700 701 /* check if the log is larger than the soft limit */ 702 if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) { 703 /* 704 * The log is larger than the soft limit. 705 * Swap the oldest entry for the newest. 706 */ 707 DS_DBG_LOG(CE_NOTE, "%s: replacing oldest entry with new entry", 708 __func__); 709 (void) ds_log_replace(dest, data, sz); 710 } else { 711 /* 712 * Still have headroom under the soft limit. 713 * Add the new entry to the log. 714 */ 715 ds_log_entry_t *new; 716 717 new = ds_log_entry_alloc(); 718 719 /* fill in message data */ 720 new->data = data; 721 new->datasz = sz; 722 new->timestamp = ddi_get_time(); 723 new->dest = dest; 724 725 rv = ds_log_add(new); 726 } 727 728 /* check if the log is larger than the hard limit */ 729 if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) { 730 /* 731 * Wakeup the thread to remove entries 732 * from the log until it is smaller than 733 * the soft limit. 734 */ 735 DS_DBG_LOG(CE_NOTE, "%s: log exceeded %d bytes, scheduling" 736 " a purge...", __func__, DS_LOG_LIMIT); 737 738 if (DS_DISPATCH(ds_log_purge, NULL) == TASKQID_INVALID) { 739 cmn_err(CE_NOTE, "%s: purge thread failed to start", 740 __func__); 741 } 742 } 743 744 mutex_exit(&ds_log.lock); 745 746 return (rv); 747 } 748 749 int 750 ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl, 751 char *dom_name, int verbose) 752 { 753 ds_port_t *newport; 754 755 /* sanity check the port id */ 756 if (port_id > DS_MAX_PORT_ID) { 757 cmn_err(CE_WARN, "%s: port ID %ld out of range", 758 __func__, port_id); 759 return (EINVAL); 760 } 761 762 DS_DBG_MD(CE_NOTE, "%s: adding port ds@%ld, LDC: 0x%lx, dhdl: 0x%lx " 763 "name: '%s'", __func__, port_id, ldc_id, dhdl, 764 dom_name == NULL ? "NULL" : dom_name); 765 766 /* get the port structure from the array of ports */ 767 newport = &ds_ports[port_id]; 768 769 /* check for a duplicate port in the MD */ 770 if (newport->state != DS_PORT_FREE) { 771 if (verbose) { 772 cmn_err(CE_WARN, "ds@%lx: %s: port already exists", 773 port_id, __func__); 774 } 775 if (newport->domain_hdl == DS_DHDL_INVALID) { 776 newport->domain_hdl = dhdl; 777 } 778 if (newport->domain_name == NULL && dom_name != NULL) { 779 newport->domain_name = ds_strdup(dom_name); 780 } 781 return (EBUSY); 782 } 783 784 /* initialize the port */ 785 newport->id = port_id; 786 newport->ldc.id = ldc_id; 787 newport->domain_hdl = dhdl; 788 if (dom_name) { 789 newport->domain_name = ds_strdup(dom_name); 790 } else 791 newport->domain_name = NULL; 792 ds_port_common_init(newport); 793 794 return (0); 795 } 796 797 /* ARGSUSED */ 798 int 799 ds_remove_port(uint64_t port_id, int is_fini) 800 { 801 ds_port_t *port; 802 803 if (port_id >= DS_MAX_PORTS || !DS_PORT_IN_SET(ds_allports, port_id)) { 804 DS_DBG_MD(CE_NOTE, "%s: invalid port %lx", __func__, 805 port_id); 806 return (EINVAL); 807 } 808 809 DS_DBG_MD(CE_NOTE, "%s: removing port ds@%lx", __func__, port_id); 810 811 port = &ds_ports[port_id]; 812 813 mutex_enter(&port->lock); 814 815 if (port->state >= DS_PORT_LDC_INIT) { 816 /* shut down the LDC for this port */ 817 (void) ds_ldc_fini(port); 818 } 819 820 if (port->domain_name) { 821 DS_FREE(port->domain_name, strlen(port->domain_name) + 1); 822 port->domain_name = NULL; 823 } 824 port->domain_hdl = DS_DHDL_INVALID; 825 826 /* clean up the port structure */ 827 ds_port_common_fini(port); 828 829 mutex_exit(&port->lock); 830 return (0); 831 } 832 833 /* 834 * Interface for ds_service_lookup in lds driver. 835 */ 836 int 837 ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client) 838 { 839 ds_svc_t *svc; 840 841 mutex_enter(&ds_svcs.lock); 842 if ((svc = ds_get_svc(hdl)) == NULL) { 843 mutex_exit(&ds_svcs.lock); 844 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 845 (u_longlong_t)hdl); 846 return (ENXIO); 847 } 848 *servicep = svc->cap.svc_id; 849 *is_client = svc->flags & DSSF_ISCLIENT; 850 mutex_exit(&ds_svcs.lock); 851 return (0); 852 } 853 854 /* 855 * Interface for ds_domain_lookup in lds driver. 856 */ 857 int 858 ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp) 859 { 860 ds_svc_t *svc; 861 862 mutex_enter(&ds_svcs.lock); 863 if ((svc = ds_get_svc(hdl)) == NULL) { 864 mutex_exit(&ds_svcs.lock); 865 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 866 (u_longlong_t)hdl); 867 return (ENXIO); 868 } 869 if (svc->port == NULL) 870 *dhdlp = ds_my_domain_hdl; 871 else 872 *dhdlp = svc->port->domain_hdl; 873 mutex_exit(&ds_svcs.lock); 874 return (0); 875 } 876 877 /* 878 * Interface for ds_hdl_isready in lds driver. 879 */ 880 int 881 ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready) 882 { 883 ds_svc_t *svc; 884 885 mutex_enter(&ds_svcs.lock); 886 if ((svc = ds_get_svc(hdl)) == NULL) { 887 mutex_exit(&ds_svcs.lock); 888 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 889 (u_longlong_t)hdl); 890 return (ENXIO); 891 } 892 *is_ready = (svc->state == DS_SVC_ACTIVE); 893 mutex_exit(&ds_svcs.lock); 894 return (0); 895 } 896 897 /* 898 * Interface for ds_dom_name_to_hdl in lds driver. 899 */ 900 int 901 ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp) 902 { 903 int i; 904 ds_port_t *port; 905 906 if (domain_name == NULL) { 907 return (ENXIO); 908 } 909 if (ds_my_domain_name != NULL && 910 strcmp(ds_my_domain_name, domain_name) == 0) { 911 *dhdlp = ds_my_domain_hdl; 912 return (0); 913 } 914 for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { 915 if (port->state != DS_PORT_FREE && 916 port->domain_name != NULL && 917 strcmp(port->domain_name, domain_name) == 0) { 918 *dhdlp = port->domain_hdl; 919 return (0); 920 } 921 } 922 return (ENXIO); 923 } 924 925 /* 926 * Interface for ds_dom_hdl_to_name in lds driver. 927 */ 928 int 929 ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep) 930 { 931 int i; 932 ds_port_t *port; 933 934 if (dhdl == ds_my_domain_hdl) { 935 if (ds_my_domain_name != NULL) { 936 *domain_namep = ds_my_domain_name; 937 return (0); 938 } 939 return (ENXIO); 940 } 941 for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { 942 if (port->state != DS_PORT_FREE && 943 port->domain_hdl == dhdl) { 944 *domain_namep = port->domain_name; 945 return (0); 946 } 947 } 948 return (ENXIO); 949 } 950 951 /* 952 * Unregister all handles related to device open instance. 953 */ 954 void 955 ds_unreg_all(int instance) 956 { 957 int idx; 958 ds_svc_t *svc; 959 ds_svc_hdl_t hdl; 960 961 DS_DBG_USR(CE_NOTE, "%s: entered", __func__); 962 963 /* walk every table entry */ 964 mutex_enter(&ds_svcs.lock); 965 for (idx = 0; idx < ds_svcs.maxsvcs; idx++) { 966 svc = ds_svcs.tbl[idx]; 967 if (DS_SVC_ISFREE(svc)) 968 continue; 969 if ((svc->flags & DSSF_ISUSER) != 0 && svc->drvi == instance) { 970 hdl = svc->hdl; 971 mutex_exit(&ds_svcs.lock); 972 (void) ds_unreg_hdl(hdl); 973 mutex_enter(&ds_svcs.lock); 974 DS_DBG_USR(CE_NOTE, "%s: ds_unreg_hdl(0x%llx):", 975 __func__, (u_longlong_t)hdl); 976 } 977 } 978 mutex_exit(&ds_svcs.lock); 979 } 980 981 /* 982 * Special callbacks to allow the lds module revision-independent access 983 * to service structure data in the callback routines. This assumes that 984 * we put a special "cookie" in the arg argument passed to those 985 * routines (for now, a ptr to the svc structure, but it could be a svc 986 * table index or something that we could get back to the svc table entry). 987 */ 988 void 989 ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp) 990 { 991 ds_svc_t *svc = (ds_svc_t *)arg; 992 993 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 994 *hdlp = svc->hdl; 995 } 996 997 void 998 ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp) 999 { 1000 ds_svc_t *svc = (ds_svc_t *)arg; 1001 1002 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1003 *flagsp = svc->flags; 1004 } 1005 1006 void 1007 ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip) 1008 { 1009 ds_svc_t *svc = (ds_svc_t *)arg; 1010 1011 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1012 *drvip = svc->drvi; 1013 } 1014 1015 void 1016 ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp) 1017 { 1018 ds_svc_t *svc = (ds_svc_t *)arg; 1019 1020 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1021 *dpspp = svc->drv_psp; 1022 } 1023 1024 void 1025 ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp) 1026 { 1027 ds_svc_t *svc = (ds_svc_t *)arg; 1028 1029 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1030 if (svc->port == NULL) 1031 *dhdlp = ds_my_domain_hdl; 1032 else 1033 *dhdlp = svc->port->domain_hdl; 1034 } 1035 1036 void 1037 ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep) 1038 { 1039 ds_svc_t *svc = (ds_svc_t *)arg; 1040 1041 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1042 *servicep = svc->cap.svc_id; 1043 } 1044 1045 void 1046 ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp) 1047 { 1048 ds_svc_t *svc = (ds_svc_t *)arg; 1049 1050 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1051 svc->drv_psp = dpsp; 1052 } 1053 1054 void 1055 ds_cbarg_set_cookie(ds_svc_t *svc) 1056 { 1057 svc->ops.cb_arg = (ds_cb_arg_t)(svc); 1058 } 1059 1060 int 1061 ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp) 1062 { 1063 ds_svc_t *svc; 1064 1065 mutex_enter(&ds_svcs.lock); 1066 if ((svc = ds_get_svc(hdl)) != NULL && 1067 (svc->flags & DSSF_ISUSER) != 0) { 1068 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1069 *cbargp = svc->ops.cb_arg; 1070 mutex_exit(&ds_svcs.lock); 1071 return (0); 1072 } 1073 mutex_exit(&ds_svcs.lock); 1074 return (ENXIO); 1075 } 1076 1077 int 1078 ds_is_my_hdl(ds_svc_hdl_t hdl, int instance) 1079 { 1080 ds_svc_t *svc; 1081 int rv = 0; 1082 1083 mutex_enter(&ds_svcs.lock); 1084 if ((svc = ds_get_svc(hdl)) == NULL) { 1085 DS_DBG_USR(CE_NOTE, "%s: invalid hdl: 0x%llx\n", __func__, 1086 (u_longlong_t)hdl); 1087 rv = ENXIO; 1088 } else if (instance == DS_INVALID_INSTANCE) { 1089 if ((svc->flags & DSSF_ISUSER) != 0) { 1090 DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", 1091 __func__, (u_longlong_t)hdl); 1092 rv = EACCES; 1093 } 1094 } else if ((svc->flags & DSSF_ISUSER) == 0 || svc->drvi != instance) { 1095 DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__, 1096 (u_longlong_t)hdl); 1097 rv = EACCES; 1098 } 1099 mutex_exit(&ds_svcs.lock); 1100 return (rv); 1101 } 1102