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