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 */
65ds_port_t	ds_ports[DS_MAX_PORTS];
66ds_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 */
76ds_svcs_t	ds_svcs;
77
78/*
79 * Taskq for internal task processing
80 */
81static 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
91ds_domain_hdl_t ds_my_domain_hdl = DS_DHDL_INVALID;
92char *ds_my_domain_name = NULL;
93
94#ifdef DEBUG
95/*
96 * Debug Flag
97 */
98uint_t ds_debug = 0;
99#endif	/* DEBUG */
100
101/* initialization functions */
102static void ds_init(void);
103static void ds_fini(void);
104static int ds_ports_init(void);
105static int ds_ports_fini(void);
106
107/* port utilities */
108static int ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan);
109
110/* log functions */
111static void ds_log_init(void);
112static void ds_log_fini(void);
113static int ds_log_remove(void);
114static void ds_log_purge(void *arg);
115
116static struct modlmisc modlmisc = {
117	&mod_miscops,
118	"Domain Services 1.9"
119};
120
121static struct modlinkage modlinkage = {
122	MODREV_1,
123	(void *)&modlmisc,
124	NULL
125};
126
127int
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
156int
157_info(struct modinfo *modinfop)
158{
159	return (mod_info(&modlinkage, modinfop));
160}
161
162int
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
175static void
176ds_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 */
213static int
214ds_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
303done:
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
315static int
316ds_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
332static int
333ds_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
376void
377ds_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
389void
390ds_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
408int
409ds_sys_dispatch_func(void (func)(void *), void *arg)
410{
411	return (DS_DISPATCH(func, arg) == NULL);
412}
413
414/*
415 * Drain event queue, if necessary.
416 */
417void
418ds_sys_drain_events(ds_port_t *port)
419{
420	_NOTE(ARGUNUSED(port))
421}
422
423/*
424 * System specific port initalization.
425 */
426void
427ds_sys_port_init(ds_port_t *port)
428{
429	_NOTE(ARGUNUSED(port))
430}
431
432/*
433 * System specific port teardown.
434 */
435void
436ds_sys_port_fini(ds_port_t *port)
437{
438	_NOTE(ARGUNUSED(port))
439}
440
441/*
442 * System specific LDC channel initialization.
443 */
444void
445ds_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 */
476static 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 */
485uint_t ds_log_sz = DS_LOG_DEFAULT_SZ;
486
487/* initial pool of log entry structures */
488static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL];
489
490/*
491 * Logging Support
492 */
493static void
494ds_log_init(void)
495{
496	ds_log_entry_t	*new;
497
498	/* initialize global lock */
499	mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL);
500
501	mutex_enter(&ds_log.lock);
502
503	/* initialize the log */
504	ds_log.head = NULL;
505	ds_log.size = 0;
506	ds_log.nentry = 0;
507
508	/* initialize the free list */
509	for (new = ds_log_entry_pool; new < DS_LOG_POOL_END; new++) {
510		new->next = ds_log.freelist;
511		ds_log.freelist = new;
512	}
513
514	mutex_exit(&ds_log.lock);
515
516	DS_DBG_LOG(CE_NOTE, "ds_log initialized: size=%d bytes, "
517	    " limit=%d bytes, ninit=%ld", ds_log_sz, DS_LOG_LIMIT,
518	    DS_LOG_NPOOL);
519}
520
521static void
522ds_log_fini(void)
523{
524	ds_log_entry_t	*next;
525
526	mutex_enter(&ds_log.lock);
527
528	/* clear out the log */
529	while (ds_log.nentry > 0)
530		(void) ds_log_remove();
531
532	/*
533	 * Now all the entries are on the free list.
534	 * Clear out the free list, deallocating any
535	 * entry that was dynamically allocated.
536	 */
537	while (ds_log.freelist != NULL) {
538		next = ds_log.freelist->next;
539
540		if (!DS_IS_POOL_ENTRY(ds_log.freelist)) {
541			kmem_free(ds_log.freelist, sizeof (ds_log_entry_t));
542		}
543
544		ds_log.freelist = next;
545	}
546
547	mutex_exit(&ds_log.lock);
548
549	mutex_destroy(&ds_log.lock);
550}
551
552static ds_log_entry_t *
553ds_log_entry_alloc(void)
554{
555	ds_log_entry_t	*new = NULL;
556
557	ASSERT(MUTEX_HELD(&ds_log.lock));
558
559	if (ds_log.freelist != NULL) {
560		new = ds_log.freelist;
561		ds_log.freelist = ds_log.freelist->next;
562	}
563
564	if (new == NULL) {
565		/* free list was empty */
566		new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP);
567	}
568
569	ASSERT(new);
570
571	return (new);
572}
573
574static void
575ds_log_entry_free(ds_log_entry_t *entry)
576{
577	ASSERT(MUTEX_HELD(&ds_log.lock));
578
579	if (entry == NULL)
580		return;
581
582	if (entry->data != NULL) {
583		kmem_free(entry->data, entry->datasz);
584		entry->data = NULL;
585	}
586
587	/* place entry on the free list */
588	entry->next = ds_log.freelist;
589	ds_log.freelist = entry;
590}
591
592/*
593 * Add a message to the end of the log
594 */
595static int
596ds_log_add(ds_log_entry_t *new)
597{
598	ASSERT(MUTEX_HELD(&ds_log.lock));
599
600	if (ds_log.head == NULL) {
601
602		new->prev = new;
603		new->next = new;
604
605		ds_log.head = new;
606	} else {
607		ds_log_entry_t	*head = ds_log.head;
608		ds_log_entry_t	*tail = ds_log.head->prev;
609
610		new->next = head;
611		new->prev = tail;
612		tail->next = new;
613		head->prev = new;
614	}
615
616	/* increase the log size, including the metadata size */
617	ds_log.size += DS_LOG_ENTRY_SZ(new);
618	ds_log.nentry++;
619
620	DS_DBG_LOG(CE_NOTE, "ds_log: added %ld data bytes, %ld total bytes",
621	    new->datasz, DS_LOG_ENTRY_SZ(new));
622
623	return (0);
624}
625
626/*
627 * Remove an entry from the head of the log
628 */
629static int
630ds_log_remove(void)
631{
632	ds_log_entry_t	*head;
633
634	ASSERT(MUTEX_HELD(&ds_log.lock));
635
636	head = ds_log.head;
637
638	/* empty list */
639	if (head == NULL)
640		return (0);
641
642	if (head->next == ds_log.head) {
643		/* one element list */
644		ds_log.head = NULL;
645	} else {
646		head->next->prev = head->prev;
647		head->prev->next = head->next;
648		ds_log.head = head->next;
649	}
650
651	DS_DBG_LOG(CE_NOTE, "ds_log: removed %ld data bytes, %ld total bytes",
652	    head->datasz, DS_LOG_ENTRY_SZ(head));
653
654	ds_log.size -= DS_LOG_ENTRY_SZ(head);
655	ds_log.nentry--;
656
657	ds_log_entry_free(head);
658
659	return (0);
660}
661
662/*
663 * Replace the data in the entry at the front of the list with then
664 * new data. This has the effect of removing the oldest entry and
665 * adding the new entry.
666 */
667static int
668ds_log_replace(int32_t dest, uint8_t *msg, size_t sz)
669{
670	ds_log_entry_t	*head;
671
672	ASSERT(MUTEX_HELD(&ds_log.lock));
673
674	head = ds_log.head;
675
676	DS_DBG_LOG(CE_NOTE, "ds_log: replaced %ld data bytes (%ld total) with "
677	    " %ld data bytes (%ld total)", head->datasz,
678	    DS_LOG_ENTRY_SZ(head), sz, sz + sizeof (ds_log_entry_t));
679
680	ds_log.size -= DS_LOG_ENTRY_SZ(head);
681
682	kmem_free(head->data, head->datasz);
683
684	head->data = msg;
685	head->datasz = sz;
686	head->timestamp = ddi_get_time();
687	head->dest = dest;
688
689	ds_log.size += DS_LOG_ENTRY_SZ(head);
690
691	ds_log.head = head->next;
692
693	return (0);
694}
695
696static void
697ds_log_purge(void *arg)
698{
699	_NOTE(ARGUNUSED(arg))
700
701	mutex_enter(&ds_log.lock);
702
703	DS_DBG_LOG(CE_NOTE, "ds_log: purging oldest log entries");
704
705	while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) {
706		(void) ds_log_remove();
707	}
708
709	mutex_exit(&ds_log.lock);
710}
711
712int
713ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz)
714{
715	int	rv = 0;
716	void	*data;
717
718	mutex_enter(&ds_log.lock);
719
720	/* allocate a local copy of the data */
721	data = kmem_alloc(sz, KM_SLEEP);
722	bcopy(msg, data, sz);
723
724	/* check if the log is larger than the soft limit */
725	if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) {
726		/*
727		 * The log is larger than the soft limit.
728		 * Swap the oldest entry for the newest.
729		 */
730		DS_DBG_LOG(CE_NOTE, "%s: replacing oldest entry with new entry",
731		    __func__);
732		(void) ds_log_replace(dest, data, sz);
733	} else {
734		/*
735		 * Still have headroom under the soft limit.
736		 * Add the new entry to the log.
737		 */
738		ds_log_entry_t	*new;
739
740		new = ds_log_entry_alloc();
741
742		/* fill in message data */
743		new->data = data;
744		new->datasz = sz;
745		new->timestamp = ddi_get_time();
746		new->dest = dest;
747
748		rv = ds_log_add(new);
749	}
750
751	/* check if the log is larger than the hard limit */
752	if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) {
753		/*
754		 * Wakeup the thread to remove entries
755		 * from the log until it is smaller than
756		 * the soft limit.
757		 */
758		DS_DBG_LOG(CE_NOTE, "%s: log exceeded %d bytes, scheduling"
759		    " a purge...", __func__, DS_LOG_LIMIT);
760
761		if (DS_DISPATCH(ds_log_purge, NULL) == NULL) {
762			cmn_err(CE_NOTE, "%s: purge thread failed to start",
763			    __func__);
764		}
765	}
766
767	mutex_exit(&ds_log.lock);
768
769	return (rv);
770}
771
772int
773ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl,
774    char *dom_name, int verbose)
775{
776	ds_port_t	*newport;
777
778	/* sanity check the port id */
779	if (port_id > DS_MAX_PORT_ID) {
780		cmn_err(CE_WARN, "%s: port ID %ld out of range",
781		    __func__, port_id);
782		return (EINVAL);
783	}
784
785	DS_DBG_MD(CE_NOTE, "%s: adding port ds@%ld, LDC: 0x%lx, dhdl: 0x%lx "
786	    "name: '%s'", __func__, port_id, ldc_id, dhdl,
787	    dom_name == NULL ? "NULL" : dom_name);
788
789	/* get the port structure from the array of ports */
790	newport = &ds_ports[port_id];
791
792	/* check for a duplicate port in the MD */
793	if (newport->state != DS_PORT_FREE) {
794		if (verbose) {
795			cmn_err(CE_WARN, "ds@%lx: %s: port already exists",
796			    port_id, __func__);
797		}
798		if (newport->domain_hdl == DS_DHDL_INVALID) {
799			newport->domain_hdl = dhdl;
800		}
801		if (newport->domain_name == NULL && dom_name != NULL) {
802			newport->domain_name = ds_strdup(dom_name);
803		}
804		return (EBUSY);
805	}
806
807	/* initialize the port */
808	newport->id = port_id;
809	newport->ldc.id = ldc_id;
810	newport->domain_hdl = dhdl;
811	if (dom_name) {
812		newport->domain_name = ds_strdup(dom_name);
813	} else
814		newport->domain_name = NULL;
815	ds_port_common_init(newport);
816
817	return (0);
818}
819
820/* ARGSUSED */
821int
822ds_remove_port(uint64_t port_id, int is_fini)
823{
824	ds_port_t *port;
825
826	if (port_id >= DS_MAX_PORTS || !DS_PORT_IN_SET(ds_allports, port_id)) {
827		DS_DBG_MD(CE_NOTE, "%s: invalid port %lx", __func__,
828		    port_id);
829		return (EINVAL);
830	}
831
832	DS_DBG_MD(CE_NOTE, "%s: removing port ds@%lx", __func__, port_id);
833
834	port = &ds_ports[port_id];
835
836	mutex_enter(&port->lock);
837
838	if (port->state >= DS_PORT_LDC_INIT) {
839		/* shut down the LDC for this port */
840		(void) ds_ldc_fini(port);
841	}
842
843	if (port->domain_name) {
844		DS_FREE(port->domain_name, strlen(port->domain_name) + 1);
845		port->domain_name = NULL;
846	}
847	port->domain_hdl = DS_DHDL_INVALID;
848
849	/* clean up the port structure */
850	ds_port_common_fini(port);
851
852	mutex_exit(&port->lock);
853	return (0);
854}
855
856/*
857 * Interface for ds_service_lookup in lds driver.
858 */
859int
860ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client)
861{
862	ds_svc_t	*svc;
863
864	mutex_enter(&ds_svcs.lock);
865	if ((svc = ds_get_svc(hdl)) == NULL) {
866		mutex_exit(&ds_svcs.lock);
867		DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
868		    (u_longlong_t)hdl);
869		return (ENXIO);
870	}
871	*servicep = svc->cap.svc_id;
872	*is_client = svc->flags & DSSF_ISCLIENT;
873	mutex_exit(&ds_svcs.lock);
874	return (0);
875}
876
877/*
878 * Interface for ds_domain_lookup in lds driver.
879 */
880int
881ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp)
882{
883	ds_svc_t	*svc;
884
885	mutex_enter(&ds_svcs.lock);
886	if ((svc = ds_get_svc(hdl)) == NULL) {
887		mutex_exit(&ds_svcs.lock);
888		DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
889		    (u_longlong_t)hdl);
890		return (ENXIO);
891	}
892	if (svc->port == NULL)
893		*dhdlp = ds_my_domain_hdl;
894	else
895		*dhdlp = svc->port->domain_hdl;
896	mutex_exit(&ds_svcs.lock);
897	return (0);
898}
899
900/*
901 * Interface for ds_hdl_isready in lds driver.
902 */
903int
904ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready)
905{
906	ds_svc_t	*svc;
907
908	mutex_enter(&ds_svcs.lock);
909	if ((svc = ds_get_svc(hdl)) == NULL) {
910		mutex_exit(&ds_svcs.lock);
911		DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
912		    (u_longlong_t)hdl);
913		return (ENXIO);
914	}
915	*is_ready = (svc->state == DS_SVC_ACTIVE);
916	mutex_exit(&ds_svcs.lock);
917	return (0);
918}
919
920/*
921 * Interface for ds_dom_name_to_hdl in lds driver.
922 */
923int
924ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp)
925{
926	int i;
927	ds_port_t *port;
928
929	if (domain_name == NULL) {
930		return (ENXIO);
931	}
932	if (ds_my_domain_name != NULL &&
933	    strcmp(ds_my_domain_name, domain_name) == 0) {
934		*dhdlp = ds_my_domain_hdl;
935		return (0);
936	}
937	for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) {
938		if (port->state != DS_PORT_FREE &&
939		    port->domain_name != NULL &&
940		    strcmp(port->domain_name, domain_name) == 0) {
941			*dhdlp = port->domain_hdl;
942			return (0);
943		}
944	}
945	return (ENXIO);
946}
947
948/*
949 * Interface for ds_dom_hdl_to_name in lds driver.
950 */
951int
952ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep)
953{
954	int i;
955	ds_port_t *port;
956
957	if (dhdl == ds_my_domain_hdl) {
958		if (ds_my_domain_name != NULL) {
959			*domain_namep = ds_my_domain_name;
960			return (0);
961		}
962		return (ENXIO);
963	}
964	for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) {
965		if (port->state != DS_PORT_FREE &&
966		    port->domain_hdl == dhdl) {
967			*domain_namep = port->domain_name;
968			return (0);
969		}
970	}
971	return (ENXIO);
972}
973
974/*
975 * Unregister all handles related to device open instance.
976 */
977void
978ds_unreg_all(int instance)
979{
980	int		idx;
981	ds_svc_t	*svc;
982	ds_svc_hdl_t	hdl;
983
984	DS_DBG_USR(CE_NOTE, "%s: entered", __func__);
985
986	/* walk every table entry */
987	mutex_enter(&ds_svcs.lock);
988	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
989		svc = ds_svcs.tbl[idx];
990		if (DS_SVC_ISFREE(svc))
991			continue;
992		if ((svc->flags & DSSF_ISUSER) != 0 && svc->drvi == instance) {
993			hdl = svc->hdl;
994			mutex_exit(&ds_svcs.lock);
995			(void) ds_unreg_hdl(hdl);
996			mutex_enter(&ds_svcs.lock);
997			DS_DBG_USR(CE_NOTE, "%s: ds_unreg_hdl(0x%llx):",
998			    __func__, (u_longlong_t)hdl);
999		}
1000	}
1001	mutex_exit(&ds_svcs.lock);
1002}
1003
1004/*
1005 * Special callbacks to allow the lds module revision-independent access
1006 * to service structure data in the callback routines.  This assumes that
1007 * we put a special "cookie" in the arg argument passed to those
1008 * routines (for now, a ptr to the svc structure, but it could be a svc
1009 * table index or something that we could get back to the svc table entry).
1010 */
1011void
1012ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp)
1013{
1014	ds_svc_t *svc = (ds_svc_t *)arg;
1015
1016	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1017	*hdlp = svc->hdl;
1018}
1019
1020void
1021ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp)
1022{
1023	ds_svc_t *svc = (ds_svc_t *)arg;
1024
1025	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1026	*flagsp = svc->flags;
1027}
1028
1029void
1030ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip)
1031{
1032	ds_svc_t *svc = (ds_svc_t *)arg;
1033
1034	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1035	*drvip = svc->drvi;
1036}
1037
1038void
1039ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp)
1040{
1041	ds_svc_t *svc = (ds_svc_t *)arg;
1042
1043	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1044	*dpspp = svc->drv_psp;
1045}
1046
1047void
1048ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp)
1049{
1050	ds_svc_t *svc = (ds_svc_t *)arg;
1051
1052	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1053	if (svc->port == NULL)
1054		*dhdlp = ds_my_domain_hdl;
1055	else
1056		*dhdlp = svc->port->domain_hdl;
1057}
1058
1059void
1060ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep)
1061{
1062	ds_svc_t *svc = (ds_svc_t *)arg;
1063
1064	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1065	*servicep = svc->cap.svc_id;
1066}
1067
1068void
1069ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp)
1070{
1071	ds_svc_t *svc = (ds_svc_t *)arg;
1072
1073	ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1074	svc->drv_psp = dpsp;
1075}
1076
1077void
1078ds_cbarg_set_cookie(ds_svc_t *svc)
1079{
1080	svc->ops.cb_arg = (ds_cb_arg_t)(svc);
1081}
1082
1083int
1084ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp)
1085{
1086	ds_svc_t *svc;
1087
1088	mutex_enter(&ds_svcs.lock);
1089	if ((svc = ds_get_svc(hdl)) != NULL &&
1090	    (svc->flags & DSSF_ISUSER) != 0) {
1091		ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
1092		*cbargp = svc->ops.cb_arg;
1093		mutex_exit(&ds_svcs.lock);
1094		return (0);
1095	}
1096	mutex_exit(&ds_svcs.lock);
1097	return (ENXIO);
1098}
1099
1100int
1101ds_is_my_hdl(ds_svc_hdl_t hdl, int instance)
1102{
1103	ds_svc_t *svc;
1104	int rv = 0;
1105
1106	mutex_enter(&ds_svcs.lock);
1107	if ((svc = ds_get_svc(hdl)) == NULL) {
1108		DS_DBG_USR(CE_NOTE, "%s: invalid hdl: 0x%llx\n", __func__,
1109		    (u_longlong_t)hdl);
1110		rv = ENXIO;
1111	} else if (instance == DS_INVALID_INSTANCE) {
1112		if ((svc->flags & DSSF_ISUSER) != 0) {
1113			DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n",
1114			    __func__, (u_longlong_t)hdl);
1115			rv = EACCES;
1116		}
1117	} else if ((svc->flags & DSSF_ISUSER) == 0 || svc->drvi != instance) {
1118		DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__,
1119		    (u_longlong_t)hdl);
1120		rv = EACCES;
1121	}
1122	mutex_exit(&ds_svcs.lock);
1123	return (rv);
1124}
1125