xref: /illumos-gate/usr/src/uts/sun4v/io/ds_drv.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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