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