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