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