Home | History | Annotate | Download | only in io
      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) == NULL);
    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 	ds_log_entry_t	*new;
    497 
    498 	/* initialize global lock */
    499 	mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL);
    500 
    501 	mutex_enter(&ds_log.lock);
    502 
    503 	/* initialize the log */
    504 	ds_log.head = NULL;
    505 	ds_log.size = 0;
    506 	ds_log.nentry = 0;
    507 
    508 	/* initialize the free list */
    509 	for (new = ds_log_entry_pool; new < DS_LOG_POOL_END; new++) {
    510 		new->next = ds_log.freelist;
    511 		ds_log.freelist = new;
    512 	}
    513 
    514 	mutex_exit(&ds_log.lock);
    515 
    516 	DS_DBG_LOG(CE_NOTE, "ds_log initialized: size=%d bytes, "
    517 	    " limit=%d bytes, ninit=%ld", ds_log_sz, DS_LOG_LIMIT,
    518 	    DS_LOG_NPOOL);
    519 }
    520 
    521 static void
    522 ds_log_fini(void)
    523 {
    524 	ds_log_entry_t	*next;
    525 
    526 	mutex_enter(&ds_log.lock);
    527 
    528 	/* clear out the log */
    529 	while (ds_log.nentry > 0)
    530 		(void) ds_log_remove();
    531 
    532 	/*
    533 	 * Now all the entries are on the free list.
    534 	 * Clear out the free list, deallocating any
    535 	 * entry that was dynamically allocated.
    536 	 */
    537 	while (ds_log.freelist != NULL) {
    538 		next = ds_log.freelist->next;
    539 
    540 		if (!DS_IS_POOL_ENTRY(ds_log.freelist)) {
    541 			kmem_free(ds_log.freelist, sizeof (ds_log_entry_t));
    542 		}
    543 
    544 		ds_log.freelist = next;
    545 	}
    546 
    547 	mutex_exit(&ds_log.lock);
    548 
    549 	mutex_destroy(&ds_log.lock);
    550 }
    551 
    552 static ds_log_entry_t *
    553 ds_log_entry_alloc(void)
    554 {
    555 	ds_log_entry_t	*new = NULL;
    556 
    557 	ASSERT(MUTEX_HELD(&ds_log.lock));
    558 
    559 	if (ds_log.freelist != NULL) {
    560 		new = ds_log.freelist;
    561 		ds_log.freelist = ds_log.freelist->next;
    562 	}
    563 
    564 	if (new == NULL) {
    565 		/* free list was empty */
    566 		new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP);
    567 	}
    568 
    569 	ASSERT(new);
    570 
    571 	return (new);
    572 }
    573 
    574 static void
    575 ds_log_entry_free(ds_log_entry_t *entry)
    576 {
    577 	ASSERT(MUTEX_HELD(&ds_log.lock));
    578 
    579 	if (entry == NULL)
    580 		return;
    581 
    582 	if (entry->data != NULL) {
    583 		kmem_free(entry->data, entry->datasz);
    584 		entry->data = NULL;
    585 	}
    586 
    587 	/* place entry on the free list */
    588 	entry->next = ds_log.freelist;
    589 	ds_log.freelist = entry;
    590 }
    591 
    592 /*
    593  * Add a message to the end of the log
    594  */
    595 static int
    596 ds_log_add(ds_log_entry_t *new)
    597 {
    598 	ASSERT(MUTEX_HELD(&ds_log.lock));
    599 
    600 	if (ds_log.head == NULL) {
    601 
    602 		new->prev = new;
    603 		new->next = new;
    604 
    605 		ds_log.head = new;
    606 	} else {
    607 		ds_log_entry_t	*head = ds_log.head;
    608 		ds_log_entry_t	*tail = ds_log.head->prev;
    609 
    610 		new->next = head;
    611 		new->prev = tail;
    612 		tail->next = new;
    613 		head->prev = new;
    614 	}
    615 
    616 	/* increase the log size, including the metadata size */
    617 	ds_log.size += DS_LOG_ENTRY_SZ(new);
    618 	ds_log.nentry++;
    619 
    620 	DS_DBG_LOG(CE_NOTE, "ds_log: added %ld data bytes, %ld total bytes",
    621 	    new->datasz, DS_LOG_ENTRY_SZ(new));
    622 
    623 	return (0);
    624 }
    625 
    626 /*
    627  * Remove an entry from the head of the log
    628  */
    629 static int
    630 ds_log_remove(void)
    631 {
    632 	ds_log_entry_t	*head;
    633 
    634 	ASSERT(MUTEX_HELD(&ds_log.lock));
    635 
    636 	head = ds_log.head;
    637 
    638 	/* empty list */
    639 	if (head == NULL)
    640 		return (0);
    641 
    642 	if (head->next == ds_log.head) {
    643 		/* one element list */
    644 		ds_log.head = NULL;
    645 	} else {
    646 		head->next->prev = head->prev;
    647 		head->prev->next = head->next;
    648 		ds_log.head = head->next;
    649 	}
    650 
    651 	DS_DBG_LOG(CE_NOTE, "ds_log: removed %ld data bytes, %ld total bytes",
    652 	    head->datasz, DS_LOG_ENTRY_SZ(head));
    653 
    654 	ds_log.size -= DS_LOG_ENTRY_SZ(head);
    655 	ds_log.nentry--;
    656 
    657 	ds_log_entry_free(head);
    658 
    659 	return (0);
    660 }
    661 
    662 /*
    663  * Replace the data in the entry at the front of the list with then
    664  * new data. This has the effect of removing the oldest entry and
    665  * adding the new entry.
    666  */
    667 static int
    668 ds_log_replace(int32_t dest, uint8_t *msg, size_t sz)
    669 {
    670 	ds_log_entry_t	*head;
    671 
    672 	ASSERT(MUTEX_HELD(&ds_log.lock));
    673 
    674 	head = ds_log.head;
    675 
    676 	DS_DBG_LOG(CE_NOTE, "ds_log: replaced %ld data bytes (%ld total) with "
    677 	    " %ld data bytes (%ld total)", head->datasz,
    678 	    DS_LOG_ENTRY_SZ(head), sz, sz + sizeof (ds_log_entry_t));
    679 
    680 	ds_log.size -= DS_LOG_ENTRY_SZ(head);
    681 
    682 	kmem_free(head->data, head->datasz);
    683 
    684 	head->data = msg;
    685 	head->datasz = sz;
    686 	head->timestamp = ddi_get_time();
    687 	head->dest = dest;
    688 
    689 	ds_log.size += DS_LOG_ENTRY_SZ(head);
    690 
    691 	ds_log.head = head->next;
    692 
    693 	return (0);
    694 }
    695 
    696 static void
    697 ds_log_purge(void *arg)
    698 {
    699 	_NOTE(ARGUNUSED(arg))
    700 
    701 	mutex_enter(&ds_log.lock);
    702 
    703 	DS_DBG_LOG(CE_NOTE, "ds_log: purging oldest log entries");
    704 
    705 	while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) {
    706 		(void) ds_log_remove();
    707 	}
    708 
    709 	mutex_exit(&ds_log.lock);
    710 }
    711 
    712 int
    713 ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz)
    714 {
    715 	int	rv = 0;
    716 	void	*data;
    717 
    718 	mutex_enter(&ds_log.lock);
    719 
    720 	/* allocate a local copy of the data */
    721 	data = kmem_alloc(sz, KM_SLEEP);
    722 	bcopy(msg, data, sz);
    723 
    724 	/* check if the log is larger than the soft limit */
    725 	if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) {
    726 		/*
    727 		 * The log is larger than the soft limit.
    728 		 * Swap the oldest entry for the newest.
    729 		 */
    730 		DS_DBG_LOG(CE_NOTE, "%s: replacing oldest entry with new entry",
    731 		    __func__);
    732 		(void) ds_log_replace(dest, data, sz);
    733 	} else {
    734 		/*
    735 		 * Still have headroom under the soft limit.
    736 		 * Add the new entry to the log.
    737 		 */
    738 		ds_log_entry_t	*new;
    739 
    740 		new = ds_log_entry_alloc();
    741 
    742 		/* fill in message data */
    743 		new->data = data;
    744 		new->datasz = sz;
    745 		new->timestamp = ddi_get_time();
    746 		new->dest = dest;
    747 
    748 		rv = ds_log_add(new);
    749 	}
    750 
    751 	/* check if the log is larger than the hard limit */
    752 	if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) {
    753 		/*
    754 		 * Wakeup the thread to remove entries
    755 		 * from the log until it is smaller than
    756 		 * the soft limit.
    757 		 */
    758 		DS_DBG_LOG(CE_NOTE, "%s: log exceeded %d bytes, scheduling"
    759 		    " a purge...", __func__, DS_LOG_LIMIT);
    760 
    761 		if (DS_DISPATCH(ds_log_purge, NULL) == NULL) {
    762 			cmn_err(CE_NOTE, "%s: purge thread failed to start",
    763 			    __func__);
    764 		}
    765 	}
    766 
    767 	mutex_exit(&ds_log.lock);
    768 
    769 	return (rv);
    770 }
    771 
    772 int
    773 ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl,
    774     char *dom_name, int verbose)
    775 {
    776 	ds_port_t	*newport;
    777 
    778 	/* sanity check the port id */
    779 	if (port_id > DS_MAX_PORT_ID) {
    780 		cmn_err(CE_WARN, "%s: port ID %ld out of range",
    781 		    __func__, port_id);
    782 		return (EINVAL);
    783 	}
    784 
    785 	DS_DBG_MD(CE_NOTE, "%s: adding port ds@%ld, LDC: 0x%lx, dhdl: 0x%lx "
    786 	    "name: '%s'", __func__, port_id, ldc_id, dhdl,
    787 	    dom_name == NULL ? "NULL" : dom_name);
    788 
    789 	/* get the port structure from the array of ports */
    790 	newport = &ds_ports[port_id];
    791 
    792 	/* check for a duplicate port in the MD */
    793 	if (newport->state != DS_PORT_FREE) {
    794 		if (verbose) {
    795 			cmn_err(CE_WARN, "ds@%lx: %s: port already exists",
    796 			    port_id, __func__);
    797 		}
    798 		if (newport->domain_hdl == DS_DHDL_INVALID) {
    799 			newport->domain_hdl = dhdl;
    800 		}
    801 		if (newport->domain_name == NULL && dom_name != NULL) {
    802 			newport->domain_name = ds_strdup(dom_name);
    803 		}
    804 		return (EBUSY);
    805 	}
    806 
    807 	/* initialize the port */
    808 	newport->id = port_id;
    809 	newport->ldc.id = ldc_id;
    810 	newport->domain_hdl = dhdl;
    811 	if (dom_name) {
    812 		newport->domain_name = ds_strdup(dom_name);
    813 	} else
    814 		newport->domain_name = NULL;
    815 	ds_port_common_init(newport);
    816 
    817 	return (0);
    818 }
    819 
    820 /* ARGSUSED */
    821 int
    822 ds_remove_port(uint64_t port_id, int is_fini)
    823 {
    824 	ds_port_t *port;
    825 
    826 	if (port_id >= DS_MAX_PORTS || !DS_PORT_IN_SET(ds_allports, port_id)) {
    827 		DS_DBG_MD(CE_NOTE, "%s: invalid port %lx", __func__,
    828 		    port_id);
    829 		return (EINVAL);
    830 	}
    831 
    832 	DS_DBG_MD(CE_NOTE, "%s: removing port ds@%lx", __func__, port_id);
    833 
    834 	port = &ds_ports[port_id];
    835 
    836 	mutex_enter(&port->lock);
    837 
    838 	if (port->state >= DS_PORT_LDC_INIT) {
    839 		/* shut down the LDC for this port */
    840 		(void) ds_ldc_fini(port);
    841 	}
    842 
    843 	if (port->domain_name) {
    844 		DS_FREE(port->domain_name, strlen(port->domain_name) + 1);
    845 		port->domain_name = NULL;
    846 	}
    847 	port->domain_hdl = DS_DHDL_INVALID;
    848 
    849 	/* clean up the port structure */
    850 	ds_port_common_fini(port);
    851 
    852 	mutex_exit(&port->lock);
    853 	return (0);
    854 }
    855 
    856 /*
    857  * Interface for ds_service_lookup in lds driver.
    858  */
    859 int
    860 ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client)
    861 {
    862 	ds_svc_t	*svc;
    863 
    864 	mutex_enter(&ds_svcs.lock);
    865 	if ((svc = ds_get_svc(hdl)) == NULL) {
    866 		mutex_exit(&ds_svcs.lock);
    867 		DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
    868 		    (u_longlong_t)hdl);
    869 		return (ENXIO);
    870 	}
    871 	*servicep = svc->cap.svc_id;
    872 	*is_client = svc->flags & DSSF_ISCLIENT;
    873 	mutex_exit(&ds_svcs.lock);
    874 	return (0);
    875 }
    876 
    877 /*
    878  * Interface for ds_domain_lookup in lds driver.
    879  */
    880 int
    881 ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp)
    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 	if (svc->port == NULL)
    893 		*dhdlp = ds_my_domain_hdl;
    894 	else
    895 		*dhdlp = svc->port->domain_hdl;
    896 	mutex_exit(&ds_svcs.lock);
    897 	return (0);
    898 }
    899 
    900 /*
    901  * Interface for ds_hdl_isready in lds driver.
    902  */
    903 int
    904 ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready)
    905 {
    906 	ds_svc_t	*svc;
    907 
    908 	mutex_enter(&ds_svcs.lock);
    909 	if ((svc = ds_get_svc(hdl)) == NULL) {
    910 		mutex_exit(&ds_svcs.lock);
    911 		DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
    912 		    (u_longlong_t)hdl);
    913 		return (ENXIO);
    914 	}
    915 	*is_ready = (svc->state == DS_SVC_ACTIVE);
    916 	mutex_exit(&ds_svcs.lock);
    917 	return (0);
    918 }
    919 
    920 /*
    921  * Interface for ds_dom_name_to_hdl in lds driver.
    922  */
    923 int
    924 ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp)
    925 {
    926 	int i;
    927 	ds_port_t *port;
    928 
    929 	if (domain_name == NULL) {
    930 		return (ENXIO);
    931 	}
    932 	if (ds_my_domain_name != NULL &&
    933 	    strcmp(ds_my_domain_name, domain_name) == 0) {
    934 		*dhdlp = ds_my_domain_hdl;
    935 		return (0);
    936 	}
    937 	for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) {
    938 		if (port->state != DS_PORT_FREE &&
    939 		    port->domain_name != NULL &&
    940 		    strcmp(port->domain_name, domain_name) == 0) {
    941 			*dhdlp = port->domain_hdl;
    942 			return (0);
    943 		}
    944 	}
    945 	return (ENXIO);
    946 }
    947 
    948 /*
    949  * Interface for ds_dom_hdl_to_name in lds driver.
    950  */
    951 int
    952 ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep)
    953 {
    954 	int i;
    955 	ds_port_t *port;
    956 
    957 	if (dhdl == ds_my_domain_hdl) {
    958 		if (ds_my_domain_name != NULL) {
    959 			*domain_namep = ds_my_domain_name;
    960 			return (0);
    961 		}
    962 		return (ENXIO);
    963 	}
    964 	for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) {
    965 		if (port->state != DS_PORT_FREE &&
    966 		    port->domain_hdl == dhdl) {
    967 			*domain_namep = port->domain_name;
    968 			return (0);
    969 		}
    970 	}
    971 	return (ENXIO);
    972 }
    973 
    974 /*
    975  * Unregister all handles related to device open instance.
    976  */
    977 void
    978 ds_unreg_all(int instance)
    979 {
    980 	int		idx;
    981 	ds_svc_t	*svc;
    982 	ds_svc_hdl_t	hdl;
    983 
    984 	DS_DBG_USR(CE_NOTE, "%s: entered", __func__);
    985 
    986 	/* walk every table entry */
    987 	mutex_enter(&ds_svcs.lock);
    988 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
    989 		svc = ds_svcs.tbl[idx];
    990 		if (DS_SVC_ISFREE(svc))
    991 			continue;
    992 		if ((svc->flags & DSSF_ISUSER) != 0 && svc->drvi == instance) {
    993 			hdl = svc->hdl;
    994 			mutex_exit(&ds_svcs.lock);
    995 			(void) ds_unreg_hdl(hdl);
    996 			mutex_enter(&ds_svcs.lock);
    997 			DS_DBG_USR(CE_NOTE, "%s: ds_unreg_hdl(0x%llx):",
    998 			    __func__, (u_longlong_t)hdl);
    999 		}
   1000 	}
   1001 	mutex_exit(&ds_svcs.lock);
   1002 }
   1003 
   1004 /*
   1005  * Special callbacks to allow the lds module revision-independent access
   1006  * to service structure data in the callback routines.  This assumes that
   1007  * we put a special "cookie" in the arg argument passed to those
   1008  * routines (for now, a ptr to the svc structure, but it could be a svc
   1009  * table index or something that we could get back to the svc table entry).
   1010  */
   1011 void
   1012 ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp)
   1013 {
   1014 	ds_svc_t *svc = (ds_svc_t *)arg;
   1015 
   1016 	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1017 	*hdlp = svc->hdl;
   1018 }
   1019 
   1020 void
   1021 ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp)
   1022 {
   1023 	ds_svc_t *svc = (ds_svc_t *)arg;
   1024 
   1025 	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1026 	*flagsp = svc->flags;
   1027 }
   1028 
   1029 void
   1030 ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip)
   1031 {
   1032 	ds_svc_t *svc = (ds_svc_t *)arg;
   1033 
   1034 	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1035 	*drvip = svc->drvi;
   1036 }
   1037 
   1038 void
   1039 ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp)
   1040 {
   1041 	ds_svc_t *svc = (ds_svc_t *)arg;
   1042 
   1043 	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1044 	*dpspp = svc->drv_psp;
   1045 }
   1046 
   1047 void
   1048 ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp)
   1049 {
   1050 	ds_svc_t *svc = (ds_svc_t *)arg;
   1051 
   1052 	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1053 	if (svc->port == NULL)
   1054 		*dhdlp = ds_my_domain_hdl;
   1055 	else
   1056 		*dhdlp = svc->port->domain_hdl;
   1057 }
   1058 
   1059 void
   1060 ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep)
   1061 {
   1062 	ds_svc_t *svc = (ds_svc_t *)arg;
   1063 
   1064 	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1065 	*servicep = svc->cap.svc_id;
   1066 }
   1067 
   1068 void
   1069 ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp)
   1070 {
   1071 	ds_svc_t *svc = (ds_svc_t *)arg;
   1072 
   1073 	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1074 	svc->drv_psp = dpsp;
   1075 }
   1076 
   1077 void
   1078 ds_cbarg_set_cookie(ds_svc_t *svc)
   1079 {
   1080 	svc->ops.cb_arg = (ds_cb_arg_t)(svc);
   1081 }
   1082 
   1083 int
   1084 ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp)
   1085 {
   1086 	ds_svc_t *svc;
   1087 
   1088 	mutex_enter(&ds_svcs.lock);
   1089 	if ((svc = ds_get_svc(hdl)) != NULL &&
   1090 	    (svc->flags & DSSF_ISUSER) != 0) {
   1091 		ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
   1092 		*cbargp = svc->ops.cb_arg;
   1093 		mutex_exit(&ds_svcs.lock);
   1094 		return (0);
   1095 	}
   1096 	mutex_exit(&ds_svcs.lock);
   1097 	return (ENXIO);
   1098 }
   1099 
   1100 int
   1101 ds_is_my_hdl(ds_svc_hdl_t hdl, int instance)
   1102 {
   1103 	ds_svc_t *svc;
   1104 	int rv = 0;
   1105 
   1106 	mutex_enter(&ds_svcs.lock);
   1107 	if ((svc = ds_get_svc(hdl)) == NULL) {
   1108 		DS_DBG_USR(CE_NOTE, "%s: invalid hdl: 0x%llx\n", __func__,
   1109 		    (u_longlong_t)hdl);
   1110 		rv = ENXIO;
   1111 	} else if (instance == DS_INVALID_INSTANCE) {
   1112 		if ((svc->flags & DSSF_ISUSER) != 0) {
   1113 			DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n",
   1114 			    __func__, (u_longlong_t)hdl);
   1115 			rv = EACCES;
   1116 		}
   1117 	} else if ((svc->flags & DSSF_ISUSER) == 0 || svc->drvi != instance) {
   1118 		DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__,
   1119 		    (u_longlong_t)hdl);
   1120 		rv = EACCES;
   1121 	}
   1122 	mutex_exit(&ds_svcs.lock);
   1123 	return (rv);
   1124 }
   1125