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 * 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 uint8_t *ldcidsp; 338 int len; 339 340 /* get the ID for this port */ 341 if (md_get_prop_val(mdp, port, "id", &port_id) != 0) { 342 cmn_err(CE_WARN, "%s: port 'id' property not found", 343 __func__); 344 return (-1); 345 } 346 347 /* sanity check the port id */ 348 if (port_id > DS_MAX_PORT_ID) { 349 cmn_err(CE_WARN, "%s: port ID %ld out of range", 350 __func__, port_id); 351 return (-1); 352 } 353 354 /* get the channel ID for this port */ 355 if (md_get_prop_val(mdp, chan, "id", &ldc_id) != 0) { 356 cmn_err(CE_WARN, "ds@%lx: %s: no channel 'id' property", 357 port_id, __func__); 358 return (-1); 359 } 360 361 if (ds_add_port(port_id, ldc_id, DS_DHDL_INVALID, NULL, 1) != 0) 362 return (-1); 363 364 /* 365 * Identify the SP Port. The SP port is the only one with 366 * the "ldc-ids" property, and is only on the primary domain. 367 */ 368 if (ds_sp_port_id == DS_PORTID_INVALID && 369 md_get_prop_data(mdp, port, "ldc-ids", &ldcidsp, &len) == 0) { 370 ds_sp_port_id = port_id; 371 } 372 373 return (0); 374 } 375 376 void 377 ds_set_my_dom_hdl_name(ds_domain_hdl_t dhdl, char *name) 378 { 379 ds_my_domain_hdl = dhdl; 380 if (ds_my_domain_name != NULL) { 381 DS_FREE(ds_my_domain_name, strlen(ds_my_domain_name)+1); 382 ds_my_domain_name = NULL; 383 } 384 if (name != NULL) { 385 ds_my_domain_name = ds_strdup(name); 386 } 387 } 388 389 void 390 ds_init() 391 { 392 ds_common_init(); 393 394 /* 395 * Create taskq for internal processing threads. This 396 * includes processing incoming request messages and 397 * sending out of band registration messages. 398 */ 399 ds_taskq = taskq_create("ds_taskq", 1, minclsyspri, 1, 400 DS_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC); 401 402 /* 403 * Initialize the message log. 404 */ 405 ds_log_init(); 406 } 407 408 int 409 ds_sys_dispatch_func(void (func)(void *), void *arg) 410 { 411 return (DS_DISPATCH(func, arg) == TASKQID_INVALID); 412 } 413 414 /* 415 * Drain event queue, if necessary. 416 */ 417 void 418 ds_sys_drain_events(ds_port_t *port) 419 { 420 _NOTE(ARGUNUSED(port)) 421 } 422 423 /* 424 * System specific port initalization. 425 */ 426 void 427 ds_sys_port_init(ds_port_t *port) 428 { 429 _NOTE(ARGUNUSED(port)) 430 } 431 432 /* 433 * System specific port teardown. 434 */ 435 void 436 ds_sys_port_fini(ds_port_t *port) 437 { 438 _NOTE(ARGUNUSED(port)) 439 } 440 441 /* 442 * System specific LDC channel initialization. 443 */ 444 void 445 ds_sys_ldc_init(ds_port_t *port) 446 { 447 int rv; 448 char ebuf[DS_EBUFSIZE]; 449 450 ASSERT(MUTEX_HELD(&port->lock)); 451 452 if ((rv = ldc_open(port->ldc.hdl)) != 0) { 453 cmn_err(CE_WARN, "ds@%lx: %s: ldc_open: %s", 454 PORTID(port), __func__, ds_errno_to_str(rv, ebuf)); 455 return; 456 } 457 458 (void) ldc_up(port->ldc.hdl); 459 460 (void) ldc_status(port->ldc.hdl, &port->ldc.state); 461 462 DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: initial LDC state 0x%x", 463 PORTID(port), __func__, port->ldc.state); 464 465 port->state = DS_PORT_LDC_INIT; 466 } 467 468 /* 469 * DS message log 470 * 471 * Locking: The message log is protected by a single mutex. This 472 * protects all fields in the log structure itself as well as 473 * everything in the entry structures on both the log and the 474 * free list. 475 */ 476 static struct log { 477 ds_log_entry_t *head; /* head of the log */ 478 ds_log_entry_t *freelist; /* head of the free list */ 479 size_t size; /* size of the log in bytes */ 480 uint32_t nentry; /* number of entries */ 481 kmutex_t lock; /* log lock */ 482 } ds_log; 483 484 /* log soft limit */ 485 uint_t ds_log_sz = DS_LOG_DEFAULT_SZ; 486 487 /* initial pool of log entry structures */ 488 static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL]; 489 490 /* 491 * Logging Support 492 */ 493 static void 494 ds_log_init(void) 495 { 496 /* initialize global lock */ 497 mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL); 498 499 mutex_enter(&ds_log.lock); 500 501 /* initialize the log */ 502 ds_log.head = NULL; 503 ds_log.size = 0; 504 ds_log.nentry = 0; 505 506 /* initialize the free list */ 507 for (int i = 0; i < DS_LOG_NPOOL; i++) { 508 ds_log_entry_pool[i].next = ds_log.freelist; 509 ds_log.freelist = &ds_log_entry_pool[i]; 510 } 511 512 mutex_exit(&ds_log.lock); 513 514 DS_DBG_LOG(CE_NOTE, "ds_log initialized: size=%d bytes, " 515 " limit=%d bytes, ninit=%ld", ds_log_sz, DS_LOG_LIMIT, 516 DS_LOG_NPOOL); 517 } 518 519 static void 520 ds_log_fini(void) 521 { 522 ds_log_entry_t *next; 523 524 mutex_enter(&ds_log.lock); 525 526 /* clear out the log */ 527 while (ds_log.nentry > 0) 528 (void) ds_log_remove(); 529 530 /* 531 * Now all the entries are on the free list. 532 * Clear out the free list, deallocating any 533 * entry that was dynamically allocated. 534 */ 535 while (ds_log.freelist != NULL) { 536 next = ds_log.freelist->next; 537 538 if (!DS_IS_POOL_ENTRY(ds_log.freelist)) { 539 kmem_free(ds_log.freelist, sizeof (ds_log_entry_t)); 540 } 541 542 ds_log.freelist = next; 543 } 544 545 mutex_exit(&ds_log.lock); 546 547 mutex_destroy(&ds_log.lock); 548 } 549 550 static ds_log_entry_t * 551 ds_log_entry_alloc(void) 552 { 553 ds_log_entry_t *new = NULL; 554 555 ASSERT(MUTEX_HELD(&ds_log.lock)); 556 557 if (ds_log.freelist != NULL) { 558 new = ds_log.freelist; 559 ds_log.freelist = ds_log.freelist->next; 560 } 561 562 if (new == NULL) { 563 /* free list was empty */ 564 new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP); 565 } 566 567 ASSERT(new); 568 569 return (new); 570 } 571 572 static void 573 ds_log_entry_free(ds_log_entry_t *entry) 574 { 575 ASSERT(MUTEX_HELD(&ds_log.lock)); 576 577 if (entry == NULL) 578 return; 579 580 if (entry->data != NULL) { 581 kmem_free(entry->data, entry->datasz); 582 entry->data = NULL; 583 } 584 585 /* place entry on the free list */ 586 entry->next = ds_log.freelist; 587 ds_log.freelist = entry; 588 } 589 590 /* 591 * Add a message to the end of the log 592 */ 593 static int 594 ds_log_add(ds_log_entry_t *new) 595 { 596 ASSERT(MUTEX_HELD(&ds_log.lock)); 597 598 if (ds_log.head == NULL) { 599 600 new->prev = new; 601 new->next = new; 602 603 ds_log.head = new; 604 } else { 605 ds_log_entry_t *head = ds_log.head; 606 ds_log_entry_t *tail = ds_log.head->prev; 607 608 new->next = head; 609 new->prev = tail; 610 tail->next = new; 611 head->prev = new; 612 } 613 614 /* increase the log size, including the metadata size */ 615 ds_log.size += DS_LOG_ENTRY_SZ(new); 616 ds_log.nentry++; 617 618 DS_DBG_LOG(CE_NOTE, "ds_log: added %ld data bytes, %ld total bytes", 619 new->datasz, DS_LOG_ENTRY_SZ(new)); 620 621 return (0); 622 } 623 624 /* 625 * Remove an entry from the head of the log 626 */ 627 static int 628 ds_log_remove(void) 629 { 630 ds_log_entry_t *head; 631 632 ASSERT(MUTEX_HELD(&ds_log.lock)); 633 634 head = ds_log.head; 635 636 /* empty list */ 637 if (head == NULL) 638 return (0); 639 640 if (head->next == ds_log.head) { 641 /* one element list */ 642 ds_log.head = NULL; 643 } else { 644 head->next->prev = head->prev; 645 head->prev->next = head->next; 646 ds_log.head = head->next; 647 } 648 649 DS_DBG_LOG(CE_NOTE, "ds_log: removed %ld data bytes, %ld total bytes", 650 head->datasz, DS_LOG_ENTRY_SZ(head)); 651 652 ds_log.size -= DS_LOG_ENTRY_SZ(head); 653 ds_log.nentry--; 654 655 ds_log_entry_free(head); 656 657 return (0); 658 } 659 660 /* 661 * Replace the data in the entry at the front of the list with then 662 * new data. This has the effect of removing the oldest entry and 663 * adding the new entry. 664 */ 665 static int 666 ds_log_replace(int32_t dest, uint8_t *msg, size_t sz) 667 { 668 ds_log_entry_t *head; 669 670 ASSERT(MUTEX_HELD(&ds_log.lock)); 671 672 head = ds_log.head; 673 674 DS_DBG_LOG(CE_NOTE, "ds_log: replaced %ld data bytes (%ld total) with " 675 " %ld data bytes (%ld total)", head->datasz, 676 DS_LOG_ENTRY_SZ(head), sz, sz + sizeof (ds_log_entry_t)); 677 678 ds_log.size -= DS_LOG_ENTRY_SZ(head); 679 680 kmem_free(head->data, head->datasz); 681 682 head->data = msg; 683 head->datasz = sz; 684 head->timestamp = ddi_get_time(); 685 head->dest = dest; 686 687 ds_log.size += DS_LOG_ENTRY_SZ(head); 688 689 ds_log.head = head->next; 690 691 return (0); 692 } 693 694 static void 695 ds_log_purge(void *arg) 696 { 697 _NOTE(ARGUNUSED(arg)) 698 699 mutex_enter(&ds_log.lock); 700 701 DS_DBG_LOG(CE_NOTE, "ds_log: purging oldest log entries"); 702 703 while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) { 704 (void) ds_log_remove(); 705 } 706 707 mutex_exit(&ds_log.lock); 708 } 709 710 int 711 ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz) 712 { 713 int rv = 0; 714 void *data; 715 716 mutex_enter(&ds_log.lock); 717 718 /* allocate a local copy of the data */ 719 data = kmem_alloc(sz, KM_SLEEP); 720 bcopy(msg, data, sz); 721 722 /* check if the log is larger than the soft limit */ 723 if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) { 724 /* 725 * The log is larger than the soft limit. 726 * Swap the oldest entry for the newest. 727 */ 728 DS_DBG_LOG(CE_NOTE, "%s: replacing oldest entry with new entry", 729 __func__); 730 (void) ds_log_replace(dest, data, sz); 731 } else { 732 /* 733 * Still have headroom under the soft limit. 734 * Add the new entry to the log. 735 */ 736 ds_log_entry_t *new; 737 738 new = ds_log_entry_alloc(); 739 740 /* fill in message data */ 741 new->data = data; 742 new->datasz = sz; 743 new->timestamp = ddi_get_time(); 744 new->dest = dest; 745 746 rv = ds_log_add(new); 747 } 748 749 /* check if the log is larger than the hard limit */ 750 if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) { 751 /* 752 * Wakeup the thread to remove entries 753 * from the log until it is smaller than 754 * the soft limit. 755 */ 756 DS_DBG_LOG(CE_NOTE, "%s: log exceeded %d bytes, scheduling" 757 " a purge...", __func__, DS_LOG_LIMIT); 758 759 if (DS_DISPATCH(ds_log_purge, NULL) == TASKQID_INVALID) { 760 cmn_err(CE_NOTE, "%s: purge thread failed to start", 761 __func__); 762 } 763 } 764 765 mutex_exit(&ds_log.lock); 766 767 return (rv); 768 } 769 770 int 771 ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl, 772 char *dom_name, int verbose) 773 { 774 ds_port_t *newport; 775 776 /* sanity check the port id */ 777 if (port_id > DS_MAX_PORT_ID) { 778 cmn_err(CE_WARN, "%s: port ID %ld out of range", 779 __func__, port_id); 780 return (EINVAL); 781 } 782 783 DS_DBG_MD(CE_NOTE, "%s: adding port ds@%ld, LDC: 0x%lx, dhdl: 0x%lx " 784 "name: '%s'", __func__, port_id, ldc_id, dhdl, 785 dom_name == NULL ? "NULL" : dom_name); 786 787 /* get the port structure from the array of ports */ 788 newport = &ds_ports[port_id]; 789 790 /* check for a duplicate port in the MD */ 791 if (newport->state != DS_PORT_FREE) { 792 if (verbose) { 793 cmn_err(CE_WARN, "ds@%lx: %s: port already exists", 794 port_id, __func__); 795 } 796 if (newport->domain_hdl == DS_DHDL_INVALID) { 797 newport->domain_hdl = dhdl; 798 } 799 if (newport->domain_name == NULL && dom_name != NULL) { 800 newport->domain_name = ds_strdup(dom_name); 801 } 802 return (EBUSY); 803 } 804 805 /* initialize the port */ 806 newport->id = port_id; 807 newport->ldc.id = ldc_id; 808 newport->domain_hdl = dhdl; 809 if (dom_name) { 810 newport->domain_name = ds_strdup(dom_name); 811 } else 812 newport->domain_name = NULL; 813 ds_port_common_init(newport); 814 815 return (0); 816 } 817 818 /* ARGSUSED */ 819 int 820 ds_remove_port(uint64_t port_id, int is_fini) 821 { 822 ds_port_t *port; 823 824 if (port_id >= DS_MAX_PORTS || !DS_PORT_IN_SET(ds_allports, port_id)) { 825 DS_DBG_MD(CE_NOTE, "%s: invalid port %lx", __func__, 826 port_id); 827 return (EINVAL); 828 } 829 830 DS_DBG_MD(CE_NOTE, "%s: removing port ds@%lx", __func__, port_id); 831 832 port = &ds_ports[port_id]; 833 834 mutex_enter(&port->lock); 835 836 if (port->state >= DS_PORT_LDC_INIT) { 837 /* shut down the LDC for this port */ 838 (void) ds_ldc_fini(port); 839 } 840 841 if (port->domain_name) { 842 DS_FREE(port->domain_name, strlen(port->domain_name) + 1); 843 port->domain_name = NULL; 844 } 845 port->domain_hdl = DS_DHDL_INVALID; 846 847 /* clean up the port structure */ 848 ds_port_common_fini(port); 849 850 mutex_exit(&port->lock); 851 return (0); 852 } 853 854 /* 855 * Interface for ds_service_lookup in lds driver. 856 */ 857 int 858 ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client) 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 *servicep = svc->cap.svc_id; 870 *is_client = svc->flags & DSSF_ISCLIENT; 871 mutex_exit(&ds_svcs.lock); 872 return (0); 873 } 874 875 /* 876 * Interface for ds_domain_lookup in lds driver. 877 */ 878 int 879 ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp) 880 { 881 ds_svc_t *svc; 882 883 mutex_enter(&ds_svcs.lock); 884 if ((svc = ds_get_svc(hdl)) == NULL) { 885 mutex_exit(&ds_svcs.lock); 886 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 887 (u_longlong_t)hdl); 888 return (ENXIO); 889 } 890 if (svc->port == NULL) 891 *dhdlp = ds_my_domain_hdl; 892 else 893 *dhdlp = svc->port->domain_hdl; 894 mutex_exit(&ds_svcs.lock); 895 return (0); 896 } 897 898 /* 899 * Interface for ds_hdl_isready in lds driver. 900 */ 901 int 902 ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready) 903 { 904 ds_svc_t *svc; 905 906 mutex_enter(&ds_svcs.lock); 907 if ((svc = ds_get_svc(hdl)) == NULL) { 908 mutex_exit(&ds_svcs.lock); 909 DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, 910 (u_longlong_t)hdl); 911 return (ENXIO); 912 } 913 *is_ready = (svc->state == DS_SVC_ACTIVE); 914 mutex_exit(&ds_svcs.lock); 915 return (0); 916 } 917 918 /* 919 * Interface for ds_dom_name_to_hdl in lds driver. 920 */ 921 int 922 ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp) 923 { 924 int i; 925 ds_port_t *port; 926 927 if (domain_name == NULL) { 928 return (ENXIO); 929 } 930 if (ds_my_domain_name != NULL && 931 strcmp(ds_my_domain_name, domain_name) == 0) { 932 *dhdlp = ds_my_domain_hdl; 933 return (0); 934 } 935 for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { 936 if (port->state != DS_PORT_FREE && 937 port->domain_name != NULL && 938 strcmp(port->domain_name, domain_name) == 0) { 939 *dhdlp = port->domain_hdl; 940 return (0); 941 } 942 } 943 return (ENXIO); 944 } 945 946 /* 947 * Interface for ds_dom_hdl_to_name in lds driver. 948 */ 949 int 950 ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep) 951 { 952 int i; 953 ds_port_t *port; 954 955 if (dhdl == ds_my_domain_hdl) { 956 if (ds_my_domain_name != NULL) { 957 *domain_namep = ds_my_domain_name; 958 return (0); 959 } 960 return (ENXIO); 961 } 962 for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { 963 if (port->state != DS_PORT_FREE && 964 port->domain_hdl == dhdl) { 965 *domain_namep = port->domain_name; 966 return (0); 967 } 968 } 969 return (ENXIO); 970 } 971 972 /* 973 * Unregister all handles related to device open instance. 974 */ 975 void 976 ds_unreg_all(int instance) 977 { 978 int idx; 979 ds_svc_t *svc; 980 ds_svc_hdl_t hdl; 981 982 DS_DBG_USR(CE_NOTE, "%s: entered", __func__); 983 984 /* walk every table entry */ 985 mutex_enter(&ds_svcs.lock); 986 for (idx = 0; idx < ds_svcs.maxsvcs; idx++) { 987 svc = ds_svcs.tbl[idx]; 988 if (DS_SVC_ISFREE(svc)) 989 continue; 990 if ((svc->flags & DSSF_ISUSER) != 0 && svc->drvi == instance) { 991 hdl = svc->hdl; 992 mutex_exit(&ds_svcs.lock); 993 (void) ds_unreg_hdl(hdl); 994 mutex_enter(&ds_svcs.lock); 995 DS_DBG_USR(CE_NOTE, "%s: ds_unreg_hdl(0x%llx):", 996 __func__, (u_longlong_t)hdl); 997 } 998 } 999 mutex_exit(&ds_svcs.lock); 1000 } 1001 1002 /* 1003 * Special callbacks to allow the lds module revision-independent access 1004 * to service structure data in the callback routines. This assumes that 1005 * we put a special "cookie" in the arg argument passed to those 1006 * routines (for now, a ptr to the svc structure, but it could be a svc 1007 * table index or something that we could get back to the svc table entry). 1008 */ 1009 void 1010 ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp) 1011 { 1012 ds_svc_t *svc = (ds_svc_t *)arg; 1013 1014 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1015 *hdlp = svc->hdl; 1016 } 1017 1018 void 1019 ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp) 1020 { 1021 ds_svc_t *svc = (ds_svc_t *)arg; 1022 1023 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1024 *flagsp = svc->flags; 1025 } 1026 1027 void 1028 ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip) 1029 { 1030 ds_svc_t *svc = (ds_svc_t *)arg; 1031 1032 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1033 *drvip = svc->drvi; 1034 } 1035 1036 void 1037 ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp) 1038 { 1039 ds_svc_t *svc = (ds_svc_t *)arg; 1040 1041 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1042 *dpspp = svc->drv_psp; 1043 } 1044 1045 void 1046 ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp) 1047 { 1048 ds_svc_t *svc = (ds_svc_t *)arg; 1049 1050 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1051 if (svc->port == NULL) 1052 *dhdlp = ds_my_domain_hdl; 1053 else 1054 *dhdlp = svc->port->domain_hdl; 1055 } 1056 1057 void 1058 ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep) 1059 { 1060 ds_svc_t *svc = (ds_svc_t *)arg; 1061 1062 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1063 *servicep = svc->cap.svc_id; 1064 } 1065 1066 void 1067 ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp) 1068 { 1069 ds_svc_t *svc = (ds_svc_t *)arg; 1070 1071 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1072 svc->drv_psp = dpsp; 1073 } 1074 1075 void 1076 ds_cbarg_set_cookie(ds_svc_t *svc) 1077 { 1078 svc->ops.cb_arg = (ds_cb_arg_t)(svc); 1079 } 1080 1081 int 1082 ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp) 1083 { 1084 ds_svc_t *svc; 1085 1086 mutex_enter(&ds_svcs.lock); 1087 if ((svc = ds_get_svc(hdl)) != NULL && 1088 (svc->flags & DSSF_ISUSER) != 0) { 1089 ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); 1090 *cbargp = svc->ops.cb_arg; 1091 mutex_exit(&ds_svcs.lock); 1092 return (0); 1093 } 1094 mutex_exit(&ds_svcs.lock); 1095 return (ENXIO); 1096 } 1097 1098 int 1099 ds_is_my_hdl(ds_svc_hdl_t hdl, int instance) 1100 { 1101 ds_svc_t *svc; 1102 int rv = 0; 1103 1104 mutex_enter(&ds_svcs.lock); 1105 if ((svc = ds_get_svc(hdl)) == NULL) { 1106 DS_DBG_USR(CE_NOTE, "%s: invalid hdl: 0x%llx\n", __func__, 1107 (u_longlong_t)hdl); 1108 rv = ENXIO; 1109 } else if (instance == DS_INVALID_INSTANCE) { 1110 if ((svc->flags & DSSF_ISUSER) != 0) { 1111 DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", 1112 __func__, (u_longlong_t)hdl); 1113 rv = EACCES; 1114 } 1115 } else if ((svc->flags & DSSF_ISUSER) == 0 || svc->drvi != instance) { 1116 DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__, 1117 (u_longlong_t)hdl); 1118 rv = EACCES; 1119 } 1120 mutex_exit(&ds_svcs.lock); 1121 return (rv); 1122 } 1123