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
_init(void)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
_info(struct modinfo * modinfop)157 _info(struct modinfo *modinfop)
158 {
159 return (mod_info(&modlinkage, modinfop));
160 }
161
162 int
_fini(void)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
ds_fini(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
ds_ports_init(void)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
ds_ports_fini(void)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
ds_port_add(md_t * mdp,mde_cookie_t port,mde_cookie_t chan)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
ds_set_my_dom_hdl_name(ds_domain_hdl_t dhdl,char * name)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
ds_init()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
ds_sys_dispatch_func(void (func)(void *),void * arg)409 ds_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 */
417 void
ds_sys_drain_events(ds_port_t * port)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
ds_sys_port_init(ds_port_t * port)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
ds_sys_port_fini(ds_port_t * port)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
ds_sys_ldc_init(ds_port_t * port)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
ds_log_init(void)494 ds_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
521 static void
ds_log_fini(void)522 ds_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
552 static ds_log_entry_t *
ds_log_entry_alloc(void)553 ds_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
574 static void
ds_log_entry_free(ds_log_entry_t * entry)575 ds_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 */
595 static int
ds_log_add(ds_log_entry_t * new)596 ds_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 */
629 static int
ds_log_remove(void)630 ds_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 */
667 static int
ds_log_replace(int32_t dest,uint8_t * msg,size_t sz)668 ds_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
696 static void
ds_log_purge(void * arg)697 ds_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
712 int
ds_log_add_msg(int32_t dest,uint8_t * msg,size_t sz)713 ds_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
772 int
ds_add_port(uint64_t port_id,uint64_t ldc_id,ds_domain_hdl_t dhdl,char * dom_name,int verbose)773 ds_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 */
821 int
ds_remove_port(uint64_t port_id,int is_fini)822 ds_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 */
859 int
ds_service_lookup(ds_svc_hdl_t hdl,char ** servicep,uint_t * is_client)860 ds_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 */
880 int
ds_domain_lookup(ds_svc_hdl_t hdl,ds_domain_hdl_t * dhdlp)881 ds_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 */
903 int
ds_hdl_isready(ds_svc_hdl_t hdl,uint_t * is_ready)904 ds_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 */
923 int
ds_dom_name_to_hdl(char * domain_name,ds_domain_hdl_t * dhdlp)924 ds_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 */
951 int
ds_dom_hdl_to_name(ds_domain_hdl_t dhdl,char ** domain_namep)952 ds_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 */
977 void
ds_unreg_all(int instance)978 ds_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 */
1011 void
ds_cbarg_get_hdl(ds_cb_arg_t arg,ds_svc_hdl_t * hdlp)1012 ds_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
1020 void
ds_cbarg_get_flags(ds_cb_arg_t arg,uint32_t * flagsp)1021 ds_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
1029 void
ds_cbarg_get_drv_info(ds_cb_arg_t arg,int * drvip)1030 ds_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
1038 void
ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg,void ** dpspp)1039 ds_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
1047 void
ds_cbarg_get_domain(ds_cb_arg_t arg,ds_domain_hdl_t * dhdlp)1048 ds_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
1059 void
ds_cbarg_get_service_id(ds_cb_arg_t arg,char ** servicep)1060 ds_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
1068 void
ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg,void * dpsp)1069 ds_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
1077 void
ds_cbarg_set_cookie(ds_svc_t * svc)1078 ds_cbarg_set_cookie(ds_svc_t *svc)
1079 {
1080 svc->ops.cb_arg = (ds_cb_arg_t)(svc);
1081 }
1082
1083 int
ds_hdl_get_cbarg(ds_svc_hdl_t hdl,ds_cb_arg_t * cbargp)1084 ds_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
1100 int
ds_is_my_hdl(ds_svc_hdl_t hdl,int instance)1101 ds_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