/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * hot-plug services module */ #include #include #include #include #include #include #include #include /* * debug macros: */ #if defined(DEBUG) int hpcsvc_debug = 0; static void debug(char *, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); #define DEBUG0(fmt) \ debug(fmt, 0, 0, 0, 0, 0); #define DEBUG1(fmt, a1) \ debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); #define DEBUG2(fmt, a1, a2) \ debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); #define DEBUG3(fmt, a1, a2, a3) \ debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0); #else #define DEBUG0(fmt) #define DEBUG1(fmt, a1) #define DEBUG2(fmt, a1, a2) #define DEBUG3(fmt, a1, a2, a3) #endif /* * Definitions for the bus node registration list: * * The hot-plug service module maintains a linked list of items * representing the device bus nodes that have been registered via * hpc_nexus_register, or identified as candidates for registration * by the bus argument to hpc_slot_register. * * The head of the linked listed is stored in hpc_bus_list_head. Insertions * and removals from the list should be locked with mutex hpc_bus_mutex. * * Items in the list are allocated/freed with the macros hpc_alloc_bus_entry() * and hpc_free_bus_entry(). * * Each item in the list contains the following fields: * * bus_dip - pointer to devinfo node of the registering bus * * bus_name - device path name of the bus (ie /pci@1f,4000) * * bus_callback - bus nexus driver callback function registered * with the bus * * bus_registered - a boolean value which is true if the bus has * been registered with hpc_nexus_register, false otherwise * * bus_mutex - mutex lock to be held while updating this list entry * * bus_slot_list - linked list of the slots registered for this * bus node (see slot list details below) * * bus_thread - kernel thread for running slot event handlers for * slots associated with this bus * * bus_thread_cv - condition variable for synchronization between * the service routines and the thread for running slot * event handlers * * bus_thread_exit - a boolean value used to instruct the thread * for invoking the slot event handlers to exit * * bus_slot_event_list_head - the head of the linked list of instances * of slot event handlers to be run * handlers to be invoked * * bus_next - pointer to next list entry */ typedef struct hpc_bus_entry hpc_bus_entry_t; typedef struct hpc_slot_entry hpc_slot_entry_t; typedef struct hpc_event_entry hpc_event_entry_t; struct hpc_event_entry { hpc_slot_entry_t *slotp; int event; hpc_event_entry_t *next; }; #define hpc_alloc_event_entry() \ (hpc_event_entry_t *)kmem_zalloc(sizeof (hpc_event_entry_t), KM_SLEEP) #define hpc_free_event_entry(a) \ kmem_free((a), sizeof (hpc_event_entry_t)) struct hpc_bus_entry { dev_info_t *bus_dip; char bus_name[MAXPATHLEN + 1]; boolean_t bus_registered; kmutex_t bus_mutex; int (* bus_callback)(dev_info_t *dip, hpc_slot_t hdl, hpc_slot_info_t *slot_info, int slot_state); hpc_slot_entry_t *bus_slot_list; kthread_t *bus_thread; kcondvar_t bus_thread_cv; boolean_t bus_thread_exit; hpc_event_entry_t *bus_slot_event_list_head; hpc_bus_entry_t *bus_next; }; #define hpc_alloc_bus_entry() \ (hpc_bus_entry_t *)kmem_zalloc(sizeof (hpc_bus_entry_t), KM_SLEEP) #define hpc_free_bus_entry(a) \ kmem_free((a), sizeof (hpc_bus_entry_t)) /* * Definitions for the per-bus node slot registration list: * * For each bus node in the bus list, the hot-plug service module maintains * a doubly linked link list of items representing the slots that have been * registered (by hot-plug controllers) for that bus. * * The head of the linked listed is stored in bus_slot_list field of the bus * node. Insertions and removals from this list should locked with the mutex * in the bus_mutex field of the bus node. * * Items in the list are allocated/freed with the macros hpc_alloc_slot_entry() * and hpc_free_slot_entry(). * * Each item in the list contains the following fields: * * slot_handle - handle for slot (hpc_slot_t) * * slot_info - information registered with the slot (hpc_slot_info_t) * * slot_ops - ops vector registered with the slot (hpc_slot_ops_t) * * slot_ops_arg - argument to be passed to ops routines (caddr_t) * * slot_event_handler - handler registered for slot events * * slot_event_handler_arg - argument to be passed to event handler * * slot_event_mask - the set of events for which the event handler * gets invoked * * slot_bus - pointer to bus node for the slot * * slot_hpc_dip - devinfo node pointer to the HPC driver instance * that controls this slot * * slot_{prev,next} - point to {previous,next} node in the list */ struct hpc_slot_entry { hpc_slot_t slot_handle; hpc_slot_info_t slot_info; /* should be static & copied */ hpc_slot_ops_t slot_ops; caddr_t slot_ops_arg; int (* slot_event_handler)(caddr_t, uint_t); caddr_t slot_event_handler_arg; uint_t slot_event_mask; hpc_bus_entry_t *slot_bus; dev_info_t *slot_hpc_dip; hpc_slot_entry_t *slot_next, *slot_prev; }; #define hpc_alloc_slot_entry() \ (hpc_slot_entry_t *)kmem_zalloc(sizeof (hpc_slot_entry_t), KM_SLEEP) #define hpc_free_slot_entry(a) \ kmem_free((a), sizeof (hpc_slot_entry_t)) /* * Definitions for slot registration callback table. */ typedef struct hpc_callback_entry hpc_callback_entry_t; struct hpc_callback_entry { int (* callback)(dev_info_t *dip, hpc_slot_t hdl, hpc_slot_info_t *slot_info, int slot_state); dev_info_t *dip; hpc_slot_t hdl; hpc_slot_info_t *slot_info; int slot_state; hpc_callback_entry_t *next; }; #define hpc_alloc_callback_entry() \ (hpc_callback_entry_t *) \ kmem_zalloc(sizeof (hpc_callback_entry_t), KM_SLEEP) #define hpc_free_callback_entry(a) \ kmem_free((a), sizeof (hpc_callback_entry_t)) /* * Mutex lock for bus registration table and table head. */ static kmutex_t hpc_bus_mutex; static hpc_bus_entry_t *hpc_bus_list_head; /* * Forward function declarations. */ static hpc_bus_entry_t *hpc_find_bus_by_name(char *name); static void hpc_slot_event_dispatcher(hpc_bus_entry_t *busp); /* * loadable module definitions: */ extern struct mod_ops mod_miscops; static struct modlmisc modlmisc = { &mod_miscops, /* Type of module */ "hot-plug controller services v%I%" }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modlmisc, NULL }; int _init(void) { int e; mutex_init(&hpc_bus_mutex, NULL, MUTEX_DRIVER, NULL); /* * Install the module. */ e = mod_install(&modlinkage); if (e != 0) { mutex_destroy(&hpc_bus_mutex); } return (e); } int _fini(void) { int e; e = mod_remove(&modlinkage); if (e == 0) { mutex_destroy(&hpc_bus_mutex); } return (e); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } hpc_slot_ops_t * hpc_alloc_slot_ops(int flag) { hpc_slot_ops_t *ops; ops = (hpc_slot_ops_t *)kmem_zalloc(sizeof (hpc_slot_ops_t), flag); return (ops); } void hpc_free_slot_ops(hpc_slot_ops_t *ops) { kmem_free((void *)ops, sizeof (hpc_slot_ops_t)); } /*ARGSUSED2*/ int hpc_nexus_register_bus(dev_info_t *dip, int (* callback)(dev_info_t *dip, hpc_slot_t hdl, hpc_slot_info_t *slot_info, int slot_state), uint_t flags) { hpc_bus_entry_t *busp; hpc_slot_entry_t *slotp; char bus_path[MAXPATHLEN + 1]; DEBUG2("hpc_nexus_register_bus: %s%d", ddi_node_name(dip), ddi_get_instance(dip)); mutex_enter(&hpc_bus_mutex); (void) ddi_pathname(dip, bus_path); busp = hpc_find_bus_by_name(bus_path); if (busp == NULL) { /* * Initialize the new bus node and link it at the head * of the bus list. */ DEBUG0("hpc_nexus_register_bus: not in bus list"); busp = hpc_alloc_bus_entry(); busp->bus_dip = dip; busp->bus_registered = B_TRUE; (void) strcpy(busp->bus_name, bus_path); mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL); busp->bus_callback = callback; busp->bus_slot_list = NULL; busp->bus_next = hpc_bus_list_head; hpc_bus_list_head = busp; } else { /* * The bus is in the bus list but isn't registered yet. * Mark it as registered, and run the registration callbacks * for it slots. */ DEBUG0("hpc_nexus_register_bus: in list, but not registered"); mutex_enter(&busp->bus_mutex); if (busp->bus_registered == B_TRUE) { mutex_exit(&busp->bus_mutex); mutex_exit(&hpc_bus_mutex); return (HPC_ERR_BUS_DUPLICATE); } busp->bus_dip = dip; busp->bus_callback = callback; busp->bus_registered = B_TRUE; mutex_exit(&busp->bus_mutex); mutex_exit(&hpc_bus_mutex); if (callback) { DEBUG0("hpc_nexus_register_bus: running callbacks"); for (slotp = busp->bus_slot_list; slotp; slotp = slotp->slot_next) { (void) callback(dip, slotp, &slotp->slot_info, HPC_SLOT_ONLINE); } } return (HPC_SUCCESS); } mutex_exit(&hpc_bus_mutex); return (HPC_SUCCESS); } int hpc_nexus_unregister_bus(dev_info_t *dip) { hpc_bus_entry_t *busp, *busp_prev; hpc_slot_entry_t *slotp; /* * Search the list for the bus node and remove it. */ DEBUG2("hpc_nexus_unregister_bus: %s%d", ddi_node_name(dip), ddi_get_instance(dip)); mutex_enter(&hpc_bus_mutex); for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp, busp = busp->bus_next) { if (busp->bus_dip == dip) break; } if (busp == NULL) { mutex_exit(&hpc_bus_mutex); return (HPC_ERR_BUS_NOTREGISTERED); } /* * If the bus has slots, mark the bus as unregistered, otherwise * remove the bus entry from the list. */ mutex_enter(&busp->bus_mutex); if (busp->bus_slot_list == NULL) { if (busp == hpc_bus_list_head) hpc_bus_list_head = busp->bus_next; else busp_prev->bus_next = busp->bus_next; mutex_exit(&busp->bus_mutex); mutex_destroy(&busp->bus_mutex); hpc_free_bus_entry(busp); mutex_exit(&hpc_bus_mutex); return (HPC_SUCCESS); } /* * unregister event handlers for all the slots on this bus. */ for (slotp = busp->bus_slot_list; slotp != NULL; slotp = slotp->slot_next) { slotp->slot_event_handler = NULL; slotp->slot_event_handler_arg = NULL; } busp->bus_registered = B_FALSE; mutex_exit(&busp->bus_mutex); mutex_exit(&hpc_bus_mutex); return (HPC_SUCCESS); } /*ARGSUSED5*/ int hpc_slot_register(dev_info_t *hpc_dip, char *bus, hpc_slot_info_t *infop, hpc_slot_t *handlep, hpc_slot_ops_t *opsp, caddr_t ops_arg, uint_t flags) { hpc_bus_entry_t *busp; hpc_slot_entry_t *slotp, *slot_list_head; boolean_t run_callback = B_FALSE; int (* callback)(dev_info_t *dip, hpc_slot_t hdl, hpc_slot_info_t *slot_info, int slot_state); dev_info_t *dip; kthread_t *t; /* * Validate the arguments. */ DEBUG1("hpc_slot_register: %s", bus); if (handlep == NULL || infop == NULL || opsp == NULL || hpc_dip == NULL) return (HPC_ERR_INVALID); /* * The bus for the slot may or may not be in the bus list. If it's * not, we create a node for the bus in the bus list and mark it as * not registered. */ mutex_enter(&hpc_bus_mutex); busp = hpc_find_bus_by_name(bus); if (busp == NULL) { /* * Initialize the new bus node and link it at the * head of the bus list. */ DEBUG1("hpc_slot_register: %s not in bus list", bus); busp = hpc_alloc_bus_entry(); busp->bus_registered = B_FALSE; (void) strcpy(busp->bus_name, bus); mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL); busp->bus_slot_list = NULL; busp->bus_next = hpc_bus_list_head; hpc_bus_list_head = busp; } else { if (busp->bus_registered == B_TRUE) { run_callback = B_TRUE; callback = busp->bus_callback; dip = busp->bus_dip; } } mutex_enter(&busp->bus_mutex); slot_list_head = busp->bus_slot_list; if (slot_list_head == NULL) { /* * The slot list was empty, so this is the first slot * registered for the bus. Create a per-bus thread * for running the slot event handlers. */ DEBUG0("hpc_slot_register: creating event callback thread"); cv_init(&busp->bus_thread_cv, NULL, CV_DRIVER, NULL); busp->bus_thread_exit = B_FALSE; t = thread_create(NULL, 0, hpc_slot_event_dispatcher, (caddr_t)busp, 0, &p0, TS_RUN, minclsyspri); busp->bus_thread = t; } /* * Create and initialize a new entry in the slot list for the bus. */ slotp = hpc_alloc_slot_entry(); slotp->slot_handle = (hpc_slot_t)slotp; slotp->slot_info = *infop; slotp->slot_ops = *opsp; slotp->slot_ops_arg = ops_arg; slotp->slot_bus = busp; slotp->slot_hpc_dip = hpc_dip; slotp->slot_prev = NULL; busp->bus_slot_list = slotp; slotp->slot_next = slot_list_head; if (slot_list_head != NULL) slot_list_head->slot_prev = slotp; mutex_exit(&busp->bus_mutex); mutex_exit(&hpc_bus_mutex); /* * Return the handle to the caller prior to return to avoid recursion. */ *handlep = (hpc_slot_t)slotp; /* * If the bus was registered, we run the callback registered by * the bus node. */ if (run_callback) { DEBUG0("hpc_slot_register: running callback"); (void) callback(dip, slotp, infop, HPC_SLOT_ONLINE); } /* * Keep hpc driver in memory */ (void) ndi_hold_driver(hpc_dip); return (HPC_SUCCESS); } int hpc_slot_unregister(hpc_slot_t *handlep) { hpc_slot_entry_t *slotp; hpc_bus_entry_t *busp, *busp_prev; boolean_t run_callback; int (* callback)(dev_info_t *dip, hpc_slot_t hdl, hpc_slot_info_t *slot_info, int slot_state); int r; dev_info_t *dip; char *bus_name; DEBUG0("hpc_slot_unregister:"); ASSERT(handlep != NULL); /* validate the handle */ slotp = (hpc_slot_entry_t *)*handlep; if ((slotp == NULL) || slotp->slot_handle != *handlep) return (HPC_ERR_INVALID); /* * Get the bus list entry from the slot to grap the mutex for * the slot list of the bus. */ mutex_enter(&hpc_bus_mutex); busp = slotp->slot_bus; DEBUG2("hpc_slot_unregister: handlep=%x, slotp=%x", handlep, slotp); if (busp == NULL) { mutex_exit(&hpc_bus_mutex); return (HPC_ERR_SLOT_NOTREGISTERED); } /* * Determine if we need to run the slot offline callback and * save the data necessary to do so. */ callback = busp->bus_callback; run_callback = (busp->bus_registered == B_TRUE) && (callback != NULL); dip = busp->bus_dip; bus_name = busp->bus_name; /* * Run the slot offline callback if necessary. */ if (run_callback) { mutex_exit(&hpc_bus_mutex); DEBUG0("hpc_slot_unregister: running callback"); r = callback(dip, (hpc_slot_t)slotp, &slotp->slot_info, HPC_SLOT_OFFLINE); DEBUG1("hpc_slot_unregister: callback returned %x", r); if (r != HPC_SUCCESS) return (HPC_ERR_FAILED); mutex_enter(&hpc_bus_mutex); } /* * Remove the slot from list and free the memory associated with it. */ mutex_enter(&busp->bus_mutex); DEBUG1("hpc_slot_unregister: freeing slot, bus_slot_list=%x", busp->bus_slot_list); if (slotp->slot_prev != NULL) slotp->slot_prev->slot_next = slotp->slot_next; if (slotp->slot_next != NULL) slotp->slot_next->slot_prev = slotp->slot_prev; if (slotp == busp->bus_slot_list) busp->bus_slot_list = slotp->slot_next; /* * Release hold from slot registration */ ndi_rele_driver(slotp->slot_hpc_dip); /* Free the memory associated with the slot entry structure */ hpc_free_slot_entry(slotp); /* * If the slot list is empty then stop the event handler thread. */ if (busp->bus_slot_list == NULL) { DEBUG0("hpc_slot_unregister: stopping thread"); busp->bus_thread_exit = B_TRUE; cv_signal(&busp->bus_thread_cv); DEBUG0("hpc_slot_unregister: waiting for thread to exit"); cv_wait(&busp->bus_thread_cv, &busp->bus_mutex); DEBUG0("hpc_slot_unregister: thread exit"); cv_destroy(&busp->bus_thread_cv); } /* * If the bus is unregistered and this is the last slot for this bus * then remove the entry from the bus list. */ if (busp->bus_registered == B_FALSE && busp->bus_slot_list == NULL) { /* locate the previous entry in the bus list */ for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp, busp = busp->bus_next) if (strcmp(bus_name, busp->bus_name) == 0) break; if (busp == hpc_bus_list_head) hpc_bus_list_head = busp->bus_next; else busp_prev->bus_next = busp->bus_next; mutex_exit(&busp->bus_mutex); mutex_destroy(&busp->bus_mutex); hpc_free_bus_entry(busp); } else mutex_exit(&busp->bus_mutex); mutex_exit(&hpc_bus_mutex); /* * reset the slot handle. */ *handlep = NULL; return (HPC_SUCCESS); } int hpc_install_event_handler(hpc_slot_t handle, uint_t event_mask, int (*event_handler)(caddr_t, uint_t), caddr_t arg) { hpc_slot_entry_t *slotp; hpc_bus_entry_t *busp; DEBUG3("hpc_install_event_handler: handle=%x, mask=%x, arg=%x", handle, event_mask, arg); ASSERT((handle != NULL) && (event_handler != NULL)); slotp = (hpc_slot_entry_t *)handle; busp = slotp->slot_bus; ASSERT(slotp == slotp->slot_handle); mutex_enter(&busp->bus_mutex); slotp->slot_event_mask = event_mask; slotp->slot_event_handler = event_handler; slotp->slot_event_handler_arg = arg; mutex_exit(&busp->bus_mutex); return (HPC_SUCCESS); } int hpc_remove_event_handler(hpc_slot_t handle) { hpc_slot_entry_t *slotp; hpc_bus_entry_t *busp; DEBUG1("hpc_remove_event_handler: handle=%x", handle); ASSERT(handle != NULL); slotp = (hpc_slot_entry_t *)handle; ASSERT(slotp == slotp->slot_handle); busp = slotp->slot_bus; mutex_enter(&busp->bus_mutex); slotp->slot_event_mask = 0; slotp->slot_event_handler = NULL; slotp->slot_event_handler_arg = NULL; mutex_exit(&busp->bus_mutex); return (HPC_SUCCESS); } /*ARGSUSED2*/ int hpc_slot_event_notify(hpc_slot_t handle, uint_t event, uint_t flags) { hpc_slot_entry_t *slotp; hpc_bus_entry_t *busp; hpc_event_entry_t *eventp; DEBUG2("hpc_slot_event_notify: handle=%x event=%x", handle, event); ASSERT(handle != NULL); slotp = (hpc_slot_entry_t *)handle; ASSERT(slotp == slotp->slot_handle); if (slotp->slot_event_handler == NULL) return (HPC_EVENT_UNCLAIMED); /* * If the request is to handle the event synchronously, then call * the event handler without queuing the event. */ if (flags == HPC_EVENT_SYNCHRONOUS) { caddr_t arg; int (* func)(caddr_t, uint_t); func = slotp->slot_event_handler; arg = slotp->slot_event_handler_arg; return (func(arg, event)); } /* * Insert the event into the bus slot event handler list and * signal the bus slot event handler dispatch thread. */ busp = slotp->slot_bus; mutex_enter(&busp->bus_mutex); if (busp->bus_slot_event_list_head == NULL) { eventp = busp->bus_slot_event_list_head = hpc_alloc_event_entry(); } else { for (eventp = busp->bus_slot_event_list_head; eventp->next != NULL; eventp = eventp->next) ; eventp->next = hpc_alloc_event_entry(); eventp = eventp->next; } eventp->slotp = slotp; eventp->event = event; eventp->next = NULL; DEBUG2("hpc_slot_event_notify: busp=%x event=%x", busp, event); cv_signal(&busp->bus_thread_cv); mutex_exit(&busp->bus_mutex); return (HPC_EVENT_CLAIMED); } int hpc_nexus_connect(hpc_slot_t handle, void *data, uint_t flags) { hpc_slot_entry_t *slotp; ASSERT(handle != NULL); slotp = (hpc_slot_entry_t *)handle; if (slotp->slot_ops.hpc_op_connect) return (slotp->slot_ops.hpc_op_connect(slotp->slot_ops_arg, handle, data, flags)); return (HPC_ERR_FAILED); } int hpc_nexus_disconnect(hpc_slot_t handle, void *data, uint_t flags) { hpc_slot_entry_t *slotp; ASSERT(handle != NULL); slotp = (hpc_slot_entry_t *)handle; if (slotp->slot_ops.hpc_op_disconnect) return (slotp->slot_ops.hpc_op_disconnect(slotp->slot_ops_arg, handle, data, flags)); return (HPC_ERR_FAILED); } int hpc_nexus_insert(hpc_slot_t handle, void *data, uint_t flags) { hpc_slot_entry_t *slotp; ASSERT(handle != NULL); slotp = (hpc_slot_entry_t *)handle; if (slotp->slot_ops.hpc_op_insert) return (slotp->slot_ops.hpc_op_insert(slotp->slot_ops_arg, handle, data, flags)); return (HPC_ERR_FAILED); } int hpc_nexus_remove(hpc_slot_t handle, void *data, uint_t flags) { hpc_slot_entry_t *slotp; ASSERT(handle != NULL); slotp = (hpc_slot_entry_t *)handle; if (slotp->slot_ops.hpc_op_remove) return (slotp->slot_ops.hpc_op_remove(slotp->slot_ops_arg, handle, data, flags)); return (HPC_ERR_FAILED); } int hpc_nexus_control(hpc_slot_t handle, int request, caddr_t arg) { hpc_slot_entry_t *slotp; ASSERT(handle != NULL); slotp = (hpc_slot_entry_t *)handle; if (slotp->slot_ops.hpc_op_control) return (slotp->slot_ops.hpc_op_control(slotp->slot_ops_arg, handle, request, arg)); return (HPC_ERR_FAILED); } /* * The following function is run from the bus entries slot event handling * thread. */ static void hpc_slot_event_dispatcher(hpc_bus_entry_t *busp) { hpc_event_entry_t *eventp; hpc_slot_entry_t *slotp; int event; caddr_t arg; int (* func)(caddr_t, uint_t); callb_cpr_t cprinfo; /* * The creator of this thread is waiting to be signaled that * the thread has been started. */ DEBUG1("hpc_slot_event_dispatcher: busp=%x", busp); CALLB_CPR_INIT(&cprinfo, &busp->bus_mutex, callb_generic_cpr, "hpc_slot_event_dispatcher"); mutex_enter(&busp->bus_mutex); /* * Wait for events to queue and then process them. */ for (;;) { /* * Note we only hold the mutex while determining * the number of entries that have been added to * the event list, while updating the event list * after processing the event list entries. */ if (busp->bus_slot_event_list_head == NULL) { CALLB_CPR_SAFE_BEGIN(&cprinfo); cv_wait(&busp->bus_thread_cv, &busp->bus_mutex); CALLB_CPR_SAFE_END(&cprinfo, &busp->bus_mutex); if (busp->bus_thread_exit) break; continue; } /* * We have an event handler instance in the list to * process. Remove the head of the list, saving the * information required to run the event handler. * Then run the event handler while the bus mutex * is released. */ eventp = busp->bus_slot_event_list_head; slotp = eventp->slotp; event = eventp->event; func = slotp->slot_event_handler; arg = slotp->slot_event_handler_arg; busp->bus_slot_event_list_head = eventp->next; hpc_free_event_entry(eventp); mutex_exit(&busp->bus_mutex); func(arg, event); mutex_enter(&busp->bus_mutex); if (busp->bus_thread_exit) break; } DEBUG0("hpc_slot_event_dispatcher: thread_exit"); cv_signal(&busp->bus_thread_cv); CALLB_CPR_EXIT(&cprinfo); thread_exit(); } static hpc_bus_entry_t * hpc_find_bus_by_name(char *path) { hpc_bus_entry_t *busp; for (busp = hpc_bus_list_head; busp != NULL; busp = busp->bus_next) { if (strcmp(path, busp->bus_name) == 0) break; } return (busp); } boolean_t hpc_bus_registered(hpc_slot_t slot_hdl) { hpc_slot_entry_t *slotp; hpc_bus_entry_t *busp; slotp = (hpc_slot_entry_t *)slot_hdl; busp = slotp->slot_bus; return (busp->bus_registered); } #ifdef DEBUG extern void prom_printf(const char *, ...); static void debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) { if (hpcsvc_debug != 0) { cmn_err(CE_CONT, "hpcsvc: "); cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); cmn_err(CE_CONT, "\n"); } } #endif