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