/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Domain Services Module System Specific Code. * * The Domain Services (DS) module is responsible for communication * with external service entities. It provides a kernel API for clients to * publish capabilities and handles the low level communication and * version negotiation required to export those capabilities to any * interested service entity. Once a capability has been successfully * registered with a service entity, the DS module facilitates all * data transfers between the service entity and the client providing * that particular capability. * * This file provides the system interfaces that are required for * the ds.c module, which is common to both Solaris and VBSC (linux). */ #include #include #include #include #include #include #include #include #include #include #include #include /* * All DS ports in the system * * The list of DS ports is read in from the MD when the DS module is * initialized and is never modified. This eliminates the need for * locking to access the port array itself. Access to the individual * ports are synchronized at the port level. */ ds_port_t ds_ports[DS_MAX_PORTS]; ds_portset_t ds_allports; /* all DS ports in the system */ /* * Table of registered services * * Locking: Accesses to the table of services are synchronized using * a mutex lock. The reader lock must be held when looking up service * information in the table. The writer lock must be held when any * service information is being modified. */ ds_svcs_t ds_svcs; /* * Taskq for internal task processing */ static taskq_t *ds_taskq; /* * The actual required number of parallel threads is not expected * to be very large. Use the maximum number of CPUs in the system * as a rough upper bound. */ #define DS_MAX_TASKQ_THR NCPU #define DS_DISPATCH(fn, arg) taskq_dispatch(ds_taskq, fn, arg, TQ_SLEEP) ds_domain_hdl_t ds_my_domain_hdl = DS_DHDL_INVALID; char *ds_my_domain_name = NULL; #ifdef DEBUG /* * Debug Flag */ uint_t ds_debug = 0; #endif /* DEBUG */ /* initialization functions */ static void ds_init(void); static void ds_fini(void); static int ds_ports_init(void); static int ds_ports_fini(void); /* port utilities */ static int ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan); /* log functions */ static void ds_log_init(void); static void ds_log_fini(void); static int ds_log_remove(void); static void ds_log_purge(void *arg); static struct modlmisc modlmisc = { &mod_miscops, "Domain Services 1.9" }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modlmisc, NULL }; int _init(void) { int rv; /* * Perform all internal setup before initializing * the DS ports. This ensures that events can be * processed as soon as the port comes up. */ ds_init(); /* force attach channel nexus */ (void) i_ddi_attach_hw_nodes("cnex"); if ((rv = ds_ports_init()) != 0) { cmn_err(CE_WARN, "Domain Services initialization failed"); ds_fini(); return (rv); } if ((rv = mod_install(&modlinkage)) != 0) { (void) ds_ports_fini(); ds_fini(); } return (rv); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } int _fini(void) { int rv; if ((rv = mod_remove(&modlinkage)) == 0) { (void) ds_ports_fini(); ds_fini(); } return (rv); } static void ds_fini(void) { /* * Flip the enabled switch to make sure that no * incoming events get dispatched while things * are being torn down. */ ds_enabled = B_FALSE; /* * Destroy the taskq. */ taskq_destroy(ds_taskq); /* * Destroy the message log. */ ds_log_fini(); /* * Deallocate the table of registered services */ /* clear out all entries */ mutex_enter(&ds_svcs.lock); (void) ds_walk_svcs(ds_svc_free, NULL); mutex_exit(&ds_svcs.lock); /* destroy the table itself */ DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *)); mutex_destroy(&ds_svcs.lock); bzero(&ds_svcs, sizeof (ds_svcs)); } /* * Initialize the list of ports based on the MD. */ static int ds_ports_init(void) { int idx; int rv = 0; md_t *mdp; int num_nodes; int listsz; mde_cookie_t rootnode; mde_cookie_t dsnode; mde_cookie_t *portp = NULL; mde_cookie_t *chanp = NULL; int nport; int nchan; if ((mdp = md_get_handle()) == NULL) { cmn_err(CE_WARN, "Unable to initialize machine description"); return (-1); } num_nodes = md_node_count(mdp); ASSERT(num_nodes > 0); listsz = num_nodes * sizeof (mde_cookie_t); /* allocate temporary storage for MD scans */ portp = kmem_zalloc(listsz, KM_SLEEP); chanp = kmem_zalloc(listsz, KM_SLEEP); rootnode = md_root_node(mdp); ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE); /* * The root of the search for DS port nodes is the * DS node. Perform a scan to find that node. */ nport = md_scan_dag(mdp, rootnode, md_find_name(mdp, DS_MD_ROOT_NAME), md_find_name(mdp, "fwd"), portp); if (nport <= 0) { DS_DBG_MD(CE_NOTE, "No '%s' node in MD", DS_MD_ROOT_NAME); goto done; } /* expecting only one DS node */ if (nport != 1) { DS_DBG_MD(CE_NOTE, "Expected one '%s' node in the MD, found %d", DS_MD_ROOT_NAME, nport); } dsnode = portp[0]; /* find all the DS ports in the MD */ nport = md_scan_dag(mdp, dsnode, md_find_name(mdp, DS_MD_PORT_NAME), md_find_name(mdp, "fwd"), portp); if (nport <= 0) { DS_DBG_MD(CE_NOTE, "No '%s' nodes in MD", DS_MD_PORT_NAME); goto done; } /* * Initialize all the ports found in the MD. */ for (idx = 0; idx < nport; idx++) { /* get the channels for this port */ nchan = md_scan_dag(mdp, portp[idx], md_find_name(mdp, DS_MD_CHAN_NAME), md_find_name(mdp, "fwd"), chanp); if (nchan <= 0) { cmn_err(CE_WARN, "No '%s' node for DS port", DS_MD_CHAN_NAME); rv = -1; goto done; } /* expecting only one channel */ if (nchan != 1) { DS_DBG_MD(CE_NOTE, "Expected one '%s' node for DS " " port, found %d", DS_MD_CHAN_NAME, nchan); } if (ds_port_add(mdp, portp[idx], chanp[0]) != 0) { rv = -1; goto done; } } done: if (rv != 0) (void) ds_ports_fini(); DS_FREE(portp, listsz); DS_FREE(chanp, listsz); (void) md_fini_handle(mdp); return (rv); } static int ds_ports_fini(void) { int idx; /* * Tear down each initialized port. */ for (idx = 0; idx < DS_MAX_PORTS; idx++) { if (DS_PORT_IN_SET(ds_allports, idx)) { (void) ds_remove_port(idx, 1); } } return (0); } static int ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan) { uint64_t port_id; uint64_t ldc_id; uint8_t *ldcidsp; int len; /* get the ID for this port */ if (md_get_prop_val(mdp, port, "id", &port_id) != 0) { cmn_err(CE_WARN, "%s: port 'id' property not found", __func__); return (-1); } /* sanity check the port id */ if (port_id > DS_MAX_PORT_ID) { cmn_err(CE_WARN, "%s: port ID %ld out of range", __func__, port_id); return (-1); } /* get the channel ID for this port */ if (md_get_prop_val(mdp, chan, "id", &ldc_id) != 0) { cmn_err(CE_WARN, "ds@%lx: %s: no channel 'id' property", port_id, __func__); return (-1); } if (ds_add_port(port_id, ldc_id, DS_DHDL_INVALID, NULL, 1) != 0) return (-1); /* * Identify the SP Port. The SP port is the only one with * the "ldc-ids" property, and is only on the primary domain. */ if (ds_sp_port_id == DS_PORTID_INVALID && md_get_prop_data(mdp, port, "ldc-ids", &ldcidsp, &len) == 0) { ds_sp_port_id = port_id; } return (0); } void ds_set_my_dom_hdl_name(ds_domain_hdl_t dhdl, char *name) { ds_my_domain_hdl = dhdl; if (ds_my_domain_name != NULL) { DS_FREE(ds_my_domain_name, strlen(ds_my_domain_name)+1); ds_my_domain_name = NULL; } if (name != NULL) { ds_my_domain_name = ds_strdup(name); } } void ds_init() { ds_common_init(); /* * Create taskq for internal processing threads. This * includes processing incoming request messages and * sending out of band registration messages. */ ds_taskq = taskq_create("ds_taskq", 1, minclsyspri, 1, DS_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC); /* * Initialize the message log. */ ds_log_init(); } int ds_sys_dispatch_func(void (func)(void *), void *arg) { return (DS_DISPATCH(func, arg) == TASKQID_INVALID); } /* * Drain event queue, if necessary. */ void ds_sys_drain_events(ds_port_t *port) { _NOTE(ARGUNUSED(port)) } /* * System specific port initalization. */ void ds_sys_port_init(ds_port_t *port) { _NOTE(ARGUNUSED(port)) } /* * System specific port teardown. */ void ds_sys_port_fini(ds_port_t *port) { _NOTE(ARGUNUSED(port)) } /* * System specific LDC channel initialization. */ void ds_sys_ldc_init(ds_port_t *port) { int rv; char ebuf[DS_EBUFSIZE]; ASSERT(MUTEX_HELD(&port->lock)); if ((rv = ldc_open(port->ldc.hdl)) != 0) { cmn_err(CE_WARN, "ds@%lx: %s: ldc_open: %s", PORTID(port), __func__, ds_errno_to_str(rv, ebuf)); return; } (void) ldc_up(port->ldc.hdl); (void) ldc_status(port->ldc.hdl, &port->ldc.state); DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: initial LDC state 0x%x", PORTID(port), __func__, port->ldc.state); port->state = DS_PORT_LDC_INIT; } /* * DS message log * * Locking: The message log is protected by a single mutex. This * protects all fields in the log structure itself as well as * everything in the entry structures on both the log and the * free list. */ static struct log { ds_log_entry_t *head; /* head of the log */ ds_log_entry_t *freelist; /* head of the free list */ size_t size; /* size of the log in bytes */ uint32_t nentry; /* number of entries */ kmutex_t lock; /* log lock */ } ds_log; /* log soft limit */ uint_t ds_log_sz = DS_LOG_DEFAULT_SZ; /* initial pool of log entry structures */ static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL]; /* * Logging Support */ static void ds_log_init(void) { ds_log_entry_t *new; /* initialize global lock */ mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL); mutex_enter(&ds_log.lock); /* initialize the log */ ds_log.head = NULL; ds_log.size = 0; ds_log.nentry = 0; /* initialize the free list */ for (new = ds_log_entry_pool; new < DS_LOG_POOL_END; new++) { new->next = ds_log.freelist; ds_log.freelist = new; } mutex_exit(&ds_log.lock); DS_DBG_LOG(CE_NOTE, "ds_log initialized: size=%d bytes, " " limit=%d bytes, ninit=%ld", ds_log_sz, DS_LOG_LIMIT, DS_LOG_NPOOL); } static void ds_log_fini(void) { ds_log_entry_t *next; mutex_enter(&ds_log.lock); /* clear out the log */ while (ds_log.nentry > 0) (void) ds_log_remove(); /* * Now all the entries are on the free list. * Clear out the free list, deallocating any * entry that was dynamically allocated. */ while (ds_log.freelist != NULL) { next = ds_log.freelist->next; if (!DS_IS_POOL_ENTRY(ds_log.freelist)) { kmem_free(ds_log.freelist, sizeof (ds_log_entry_t)); } ds_log.freelist = next; } mutex_exit(&ds_log.lock); mutex_destroy(&ds_log.lock); } static ds_log_entry_t * ds_log_entry_alloc(void) { ds_log_entry_t *new = NULL; ASSERT(MUTEX_HELD(&ds_log.lock)); if (ds_log.freelist != NULL) { new = ds_log.freelist; ds_log.freelist = ds_log.freelist->next; } if (new == NULL) { /* free list was empty */ new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP); } ASSERT(new); return (new); } static void ds_log_entry_free(ds_log_entry_t *entry) { ASSERT(MUTEX_HELD(&ds_log.lock)); if (entry == NULL) return; if (entry->data != NULL) { kmem_free(entry->data, entry->datasz); entry->data = NULL; } /* place entry on the free list */ entry->next = ds_log.freelist; ds_log.freelist = entry; } /* * Add a message to the end of the log */ static int ds_log_add(ds_log_entry_t *new) { ASSERT(MUTEX_HELD(&ds_log.lock)); if (ds_log.head == NULL) { new->prev = new; new->next = new; ds_log.head = new; } else { ds_log_entry_t *head = ds_log.head; ds_log_entry_t *tail = ds_log.head->prev; new->next = head; new->prev = tail; tail->next = new; head->prev = new; } /* increase the log size, including the metadata size */ ds_log.size += DS_LOG_ENTRY_SZ(new); ds_log.nentry++; DS_DBG_LOG(CE_NOTE, "ds_log: added %ld data bytes, %ld total bytes", new->datasz, DS_LOG_ENTRY_SZ(new)); return (0); } /* * Remove an entry from the head of the log */ static int ds_log_remove(void) { ds_log_entry_t *head; ASSERT(MUTEX_HELD(&ds_log.lock)); head = ds_log.head; /* empty list */ if (head == NULL) return (0); if (head->next == ds_log.head) { /* one element list */ ds_log.head = NULL; } else { head->next->prev = head->prev; head->prev->next = head->next; ds_log.head = head->next; } DS_DBG_LOG(CE_NOTE, "ds_log: removed %ld data bytes, %ld total bytes", head->datasz, DS_LOG_ENTRY_SZ(head)); ds_log.size -= DS_LOG_ENTRY_SZ(head); ds_log.nentry--; ds_log_entry_free(head); return (0); } /* * Replace the data in the entry at the front of the list with then * new data. This has the effect of removing the oldest entry and * adding the new entry. */ static int ds_log_replace(int32_t dest, uint8_t *msg, size_t sz) { ds_log_entry_t *head; ASSERT(MUTEX_HELD(&ds_log.lock)); head = ds_log.head; DS_DBG_LOG(CE_NOTE, "ds_log: replaced %ld data bytes (%ld total) with " " %ld data bytes (%ld total)", head->datasz, DS_LOG_ENTRY_SZ(head), sz, sz + sizeof (ds_log_entry_t)); ds_log.size -= DS_LOG_ENTRY_SZ(head); kmem_free(head->data, head->datasz); head->data = msg; head->datasz = sz; head->timestamp = ddi_get_time(); head->dest = dest; ds_log.size += DS_LOG_ENTRY_SZ(head); ds_log.head = head->next; return (0); } static void ds_log_purge(void *arg) { _NOTE(ARGUNUSED(arg)) mutex_enter(&ds_log.lock); DS_DBG_LOG(CE_NOTE, "ds_log: purging oldest log entries"); while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) { (void) ds_log_remove(); } mutex_exit(&ds_log.lock); } int ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz) { int rv = 0; void *data; mutex_enter(&ds_log.lock); /* allocate a local copy of the data */ data = kmem_alloc(sz, KM_SLEEP); bcopy(msg, data, sz); /* check if the log is larger than the soft limit */ if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) { /* * The log is larger than the soft limit. * Swap the oldest entry for the newest. */ DS_DBG_LOG(CE_NOTE, "%s: replacing oldest entry with new entry", __func__); (void) ds_log_replace(dest, data, sz); } else { /* * Still have headroom under the soft limit. * Add the new entry to the log. */ ds_log_entry_t *new; new = ds_log_entry_alloc(); /* fill in message data */ new->data = data; new->datasz = sz; new->timestamp = ddi_get_time(); new->dest = dest; rv = ds_log_add(new); } /* check if the log is larger than the hard limit */ if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) { /* * Wakeup the thread to remove entries * from the log until it is smaller than * the soft limit. */ DS_DBG_LOG(CE_NOTE, "%s: log exceeded %d bytes, scheduling" " a purge...", __func__, DS_LOG_LIMIT); if (DS_DISPATCH(ds_log_purge, NULL) == TASKQID_INVALID) { cmn_err(CE_NOTE, "%s: purge thread failed to start", __func__); } } mutex_exit(&ds_log.lock); return (rv); } int ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl, char *dom_name, int verbose) { ds_port_t *newport; /* sanity check the port id */ if (port_id > DS_MAX_PORT_ID) { cmn_err(CE_WARN, "%s: port ID %ld out of range", __func__, port_id); return (EINVAL); } DS_DBG_MD(CE_NOTE, "%s: adding port ds@%ld, LDC: 0x%lx, dhdl: 0x%lx " "name: '%s'", __func__, port_id, ldc_id, dhdl, dom_name == NULL ? "NULL" : dom_name); /* get the port structure from the array of ports */ newport = &ds_ports[port_id]; /* check for a duplicate port in the MD */ if (newport->state != DS_PORT_FREE) { if (verbose) { cmn_err(CE_WARN, "ds@%lx: %s: port already exists", port_id, __func__); } if (newport->domain_hdl == DS_DHDL_INVALID) { newport->domain_hdl = dhdl; } if (newport->domain_name == NULL && dom_name != NULL) { newport->domain_name = ds_strdup(dom_name); } return (EBUSY); } /* initialize the port */ newport->id = port_id; newport->ldc.id = ldc_id; newport->domain_hdl = dhdl; if (dom_name) { newport->domain_name = ds_strdup(dom_name); } else newport->domain_name = NULL; ds_port_common_init(newport); return (0); } /* ARGSUSED */ int ds_remove_port(uint64_t port_id, int is_fini) { ds_port_t *port; if (port_id >= DS_MAX_PORTS || !DS_PORT_IN_SET(ds_allports, port_id)) { DS_DBG_MD(CE_NOTE, "%s: invalid port %lx", __func__, port_id); return (EINVAL); } DS_DBG_MD(CE_NOTE, "%s: removing port ds@%lx", __func__, port_id); port = &ds_ports[port_id]; mutex_enter(&port->lock); if (port->state >= DS_PORT_LDC_INIT) { /* shut down the LDC for this port */ (void) ds_ldc_fini(port); } if (port->domain_name) { DS_FREE(port->domain_name, strlen(port->domain_name) + 1); port->domain_name = NULL; } port->domain_hdl = DS_DHDL_INVALID; /* clean up the port structure */ ds_port_common_fini(port); mutex_exit(&port->lock); return (0); } /* * Interface for ds_service_lookup in lds driver. */ int ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client) { ds_svc_t *svc; mutex_enter(&ds_svcs.lock); if ((svc = ds_get_svc(hdl)) == NULL) { mutex_exit(&ds_svcs.lock); DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, (u_longlong_t)hdl); return (ENXIO); } *servicep = svc->cap.svc_id; *is_client = svc->flags & DSSF_ISCLIENT; mutex_exit(&ds_svcs.lock); return (0); } /* * Interface for ds_domain_lookup in lds driver. */ int ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp) { ds_svc_t *svc; mutex_enter(&ds_svcs.lock); if ((svc = ds_get_svc(hdl)) == NULL) { mutex_exit(&ds_svcs.lock); DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, (u_longlong_t)hdl); return (ENXIO); } if (svc->port == NULL) *dhdlp = ds_my_domain_hdl; else *dhdlp = svc->port->domain_hdl; mutex_exit(&ds_svcs.lock); return (0); } /* * Interface for ds_hdl_isready in lds driver. */ int ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready) { ds_svc_t *svc; mutex_enter(&ds_svcs.lock); if ((svc = ds_get_svc(hdl)) == NULL) { mutex_exit(&ds_svcs.lock); DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__, (u_longlong_t)hdl); return (ENXIO); } *is_ready = (svc->state == DS_SVC_ACTIVE); mutex_exit(&ds_svcs.lock); return (0); } /* * Interface for ds_dom_name_to_hdl in lds driver. */ int ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp) { int i; ds_port_t *port; if (domain_name == NULL) { return (ENXIO); } if (ds_my_domain_name != NULL && strcmp(ds_my_domain_name, domain_name) == 0) { *dhdlp = ds_my_domain_hdl; return (0); } for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { if (port->state != DS_PORT_FREE && port->domain_name != NULL && strcmp(port->domain_name, domain_name) == 0) { *dhdlp = port->domain_hdl; return (0); } } return (ENXIO); } /* * Interface for ds_dom_hdl_to_name in lds driver. */ int ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep) { int i; ds_port_t *port; if (dhdl == ds_my_domain_hdl) { if (ds_my_domain_name != NULL) { *domain_namep = ds_my_domain_name; return (0); } return (ENXIO); } for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) { if (port->state != DS_PORT_FREE && port->domain_hdl == dhdl) { *domain_namep = port->domain_name; return (0); } } return (ENXIO); } /* * Unregister all handles related to device open instance. */ void ds_unreg_all(int instance) { int idx; ds_svc_t *svc; ds_svc_hdl_t hdl; DS_DBG_USR(CE_NOTE, "%s: entered", __func__); /* walk every table entry */ mutex_enter(&ds_svcs.lock); for (idx = 0; idx < ds_svcs.maxsvcs; idx++) { svc = ds_svcs.tbl[idx]; if (DS_SVC_ISFREE(svc)) continue; if ((svc->flags & DSSF_ISUSER) != 0 && svc->drvi == instance) { hdl = svc->hdl; mutex_exit(&ds_svcs.lock); (void) ds_unreg_hdl(hdl); mutex_enter(&ds_svcs.lock); DS_DBG_USR(CE_NOTE, "%s: ds_unreg_hdl(0x%llx):", __func__, (u_longlong_t)hdl); } } mutex_exit(&ds_svcs.lock); } /* * Special callbacks to allow the lds module revision-independent access * to service structure data in the callback routines. This assumes that * we put a special "cookie" in the arg argument passed to those * routines (for now, a ptr to the svc structure, but it could be a svc * table index or something that we could get back to the svc table entry). */ void ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp) { ds_svc_t *svc = (ds_svc_t *)arg; ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); *hdlp = svc->hdl; } void ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp) { ds_svc_t *svc = (ds_svc_t *)arg; ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); *flagsp = svc->flags; } void ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip) { ds_svc_t *svc = (ds_svc_t *)arg; ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); *drvip = svc->drvi; } void ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp) { ds_svc_t *svc = (ds_svc_t *)arg; ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); *dpspp = svc->drv_psp; } void ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp) { ds_svc_t *svc = (ds_svc_t *)arg; ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); if (svc->port == NULL) *dhdlp = ds_my_domain_hdl; else *dhdlp = svc->port->domain_hdl; } void ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep) { ds_svc_t *svc = (ds_svc_t *)arg; ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); *servicep = svc->cap.svc_id; } void ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp) { ds_svc_t *svc = (ds_svc_t *)arg; ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); svc->drv_psp = dpsp; } void ds_cbarg_set_cookie(ds_svc_t *svc) { svc->ops.cb_arg = (ds_cb_arg_t)(svc); } int ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp) { ds_svc_t *svc; mutex_enter(&ds_svcs.lock); if ((svc = ds_get_svc(hdl)) != NULL && (svc->flags & DSSF_ISUSER) != 0) { ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg); *cbargp = svc->ops.cb_arg; mutex_exit(&ds_svcs.lock); return (0); } mutex_exit(&ds_svcs.lock); return (ENXIO); } int ds_is_my_hdl(ds_svc_hdl_t hdl, int instance) { ds_svc_t *svc; int rv = 0; mutex_enter(&ds_svcs.lock); if ((svc = ds_get_svc(hdl)) == NULL) { DS_DBG_USR(CE_NOTE, "%s: invalid hdl: 0x%llx\n", __func__, (u_longlong_t)hdl); rv = ENXIO; } else if (instance == DS_INVALID_INSTANCE) { if ((svc->flags & DSSF_ISUSER) != 0) { DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__, (u_longlong_t)hdl); rv = EACCES; } } else if ((svc->flags & DSSF_ISUSER) == 0 || svc->drvi != instance) { DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__, (u_longlong_t)hdl); rv = EACCES; } mutex_exit(&ds_svcs.lock); return (rv); }