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