subr_eventhandler.c [plain text]
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <kern/queue.h>
#include <kern/locks.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/mcache.h>
#include <sys/eventhandler.h>
#include <sys/sysctl.h>
int evh_debug = 0;
MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records");
SYSCTL_NODE(_kern, OID_AUTO, eventhandler, CTLFLAG_RW | CTLFLAG_LOCKED,
0, "Eventhandler");
SYSCTL_INT(_kern_eventhandler, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
&evh_debug, 0, "Eventhandler debug mode");
struct eventhandler_entry_arg eventhandler_entry_dummy_arg = { .ee_fm_uuid = { 0 }, .ee_fr_uuid = { 0 } };
static struct eventhandler_lists_ctxt evthdlr_lists_ctxt_glb;
static lck_grp_attr_t *eventhandler_mutex_grp_attr;
static lck_grp_t *eventhandler_mutex_grp;
static lck_attr_t *eventhandler_mutex_attr;
static unsigned int eg_size;
static struct mcache *eg_cache;
static unsigned int el_size;
static struct mcache *el_cache;
static lck_grp_attr_t *el_lock_grp_attr;
lck_grp_t *el_lock_grp;
lck_attr_t *el_lock_attr;
struct eventhandler_entry_generic {
struct eventhandler_entry ee;
void (* func)(void);
};
static struct eventhandler_list *_eventhandler_find_list(
struct eventhandler_lists_ctxt *evthdlr_lists_ctxt, const char *name);
void
eventhandler_lists_ctxt_init(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt)
{
VERIFY(evthdlr_lists_ctxt != NULL);
TAILQ_INIT(&evthdlr_lists_ctxt->eventhandler_lists);
evthdlr_lists_ctxt->eventhandler_lists_initted = 1;
lck_mtx_init(&evthdlr_lists_ctxt->eventhandler_mutex,
eventhandler_mutex_grp, eventhandler_mutex_attr);
}
void
eventhandler_init(void)
{
eventhandler_mutex_grp_attr = lck_grp_attr_alloc_init();
eventhandler_mutex_grp = lck_grp_alloc_init("eventhandler",
eventhandler_mutex_grp_attr);
eventhandler_mutex_attr = lck_attr_alloc_init();
el_lock_grp_attr = lck_grp_attr_alloc_init();
el_lock_grp = lck_grp_alloc_init("eventhandler list",
el_lock_grp_attr);
el_lock_attr = lck_attr_alloc_init();
eventhandler_lists_ctxt_init(&evthdlr_lists_ctxt_glb);
eg_size = sizeof(struct eventhandler_entry_generic);
eg_cache = mcache_create("eventhdlr_generic", eg_size,
sizeof(uint64_t), 0, MCR_SLEEP);
el_size = sizeof(struct eventhandler_list);
el_cache = mcache_create("eventhdlr_list", el_size,
sizeof(uint64_t), 0, MCR_SLEEP);
}
void
eventhandler_reap_caches(boolean_t purge)
{
mcache_reap_now(eg_cache, purge);
mcache_reap_now(el_cache, purge);
}
static eventhandler_tag
eventhandler_register_internal(
struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
struct eventhandler_list *list,
const char *name, eventhandler_tag epn)
{
struct eventhandler_list *new_list;
struct eventhandler_entry *ep;
VERIFY(strlen(name) <= (sizeof(new_list->el_name) - 1));
if (evthdlr_lists_ctxt == NULL) {
evthdlr_lists_ctxt = &evthdlr_lists_ctxt_glb;
}
VERIFY(evthdlr_lists_ctxt->eventhandler_lists_initted);
VERIFY(epn != NULL);
lck_mtx_lock_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
if (list == NULL) {
list = _eventhandler_find_list(evthdlr_lists_ctxt, name);
if (list == NULL) {
lck_mtx_convert_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
new_list = mcache_alloc(el_cache, MCR_SLEEP);
if (new_list == NULL) {
evhlog((LOG_DEBUG, "%s: Can't allocate list \"%s\"", __func__, name));
lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
return NULL;
}
bzero(new_list, el_size);
evhlog((LOG_DEBUG, "%s: creating list \"%s\"", __func__, name));
list = new_list;
list->el_flags = 0;
list->el_runcount = 0;
bzero(&list->el_lock, sizeof(list->el_lock));
(void) snprintf(list->el_name, sizeof(list->el_name), "%s", name);
TAILQ_INSERT_HEAD(&evthdlr_lists_ctxt->eventhandler_lists, list, el_link);
}
}
if (!(list->el_flags & EHL_INITTED)) {
TAILQ_INIT(&list->el_entries);
EHL_LOCK_INIT(list);
list->el_flags |= EHL_INITTED;
}
lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
("%s: handler for %s registered with dead priority", __func__, name));
evhlog((LOG_DEBUG, "%s: adding item %p (function %p to \"%s\"", __func__, VM_KERNEL_ADDRPERM(epn),
VM_KERNEL_UNSLIDE(((struct eventhandler_entry_generic *)epn)->func), name));
EHL_LOCK(list);
TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
if (ep->ee_priority != EHE_DEAD_PRIORITY &&
epn->ee_priority < ep->ee_priority) {
TAILQ_INSERT_BEFORE(ep, epn, ee_link);
break;
}
}
if (ep == NULL) {
TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link);
}
EHL_UNLOCK(list);
return epn;
}
eventhandler_tag
eventhandler_register(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
struct eventhandler_list *list, const char *name,
void *func, struct eventhandler_entry_arg arg, int priority)
{
struct eventhandler_entry_generic *eg;
eg = mcache_alloc(eg_cache, MCR_SLEEP);
if (eg == NULL) {
evhlog((LOG_DEBUG, "%s: Can't allocate entry to register for event list "
"\"%s\"", __func__, name));
return NULL;
}
bzero(eg, eg_size);
eg->func = func;
eg->ee.ee_arg = arg;
eg->ee.ee_priority = priority;
return eventhandler_register_internal(evthdlr_lists_ctxt, list, name, &eg->ee);
}
void
eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
{
struct eventhandler_entry *ep = tag;
EHL_LOCK_ASSERT(list, LCK_MTX_ASSERT_OWNED);
if (ep != NULL) {
if (list->el_runcount == 0) {
evhlog((LOG_DEBUG, "%s: removing item %p from \"%s\"", __func__, VM_KERNEL_ADDRPERM(ep),
list->el_name));
if (!TAILQ_EMPTY(&list->el_entries)) {
TAILQ_REMOVE(&list->el_entries, ep, ee_link);
}
EHL_LOCK_CONVERT(list);
mcache_free(eg_cache, ep);
} else {
evhlog((LOG_DEBUG, "%s: marking item %p from \"%s\" as dead", __func__,
VM_KERNEL_ADDRPERM(ep), list->el_name));
ep->ee_priority = EHE_DEAD_PRIORITY;
}
} else {
if (list->el_runcount == 0) {
evhlog((LOG_DEBUG, "%s: removing all items from \"%s\"", __func__,
list->el_name));
EHL_LOCK_CONVERT(list);
while (!TAILQ_EMPTY(&list->el_entries)) {
ep = TAILQ_FIRST(&list->el_entries);
TAILQ_REMOVE(&list->el_entries, ep, ee_link);
mcache_free(eg_cache, ep);
}
} else {
evhlog((LOG_DEBUG, "%s: marking all items from \"%s\" as dead",
__func__, list->el_name));
TAILQ_FOREACH(ep, &list->el_entries, ee_link)
ep->ee_priority = EHE_DEAD_PRIORITY;
}
}
while (list->el_runcount > 0) {
msleep((caddr_t)list, &list->el_lock, PSPIN, "evhrm", 0);
}
EHL_UNLOCK(list);
}
static struct eventhandler_list *
_eventhandler_find_list(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
const char *name)
{
struct eventhandler_list *list;
VERIFY(evthdlr_lists_ctxt != NULL);
LCK_MTX_ASSERT(&evthdlr_lists_ctxt->eventhandler_mutex, LCK_MTX_ASSERT_OWNED);
TAILQ_FOREACH(list, &evthdlr_lists_ctxt->eventhandler_lists, el_link) {
if (!strcmp(name, list->el_name)) {
break;
}
}
return list;
}
struct eventhandler_list *
eventhandler_find_list(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
const char *name)
{
struct eventhandler_list *list;
if (evthdlr_lists_ctxt == NULL) {
evthdlr_lists_ctxt = &evthdlr_lists_ctxt_glb;
}
if (!evthdlr_lists_ctxt->eventhandler_lists_initted) {
return NULL;
}
lck_mtx_lock_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
list = _eventhandler_find_list(evthdlr_lists_ctxt, name);
if (list != NULL) {
lck_mtx_convert_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
EHL_LOCK_SPIN(list);
}
lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
return list;
}
void
eventhandler_prune_list(struct eventhandler_list *list)
{
struct eventhandler_entry *ep, *en;
int pruned = 0;
evhlog((LOG_DEBUG, "%s: pruning list \"%s\"", __func__, list->el_name));
EHL_LOCK_ASSERT(list, LCK_MTX_ASSERT_OWNED);
TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) {
if (ep->ee_priority == EHE_DEAD_PRIORITY) {
TAILQ_REMOVE(&list->el_entries, ep, ee_link);
mcache_free(eg_cache, ep);
pruned++;
}
}
if (pruned > 0) {
wakeup(list);
}
}
void
eventhandler_lists_ctxt_destroy(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt)
{
struct eventhandler_list *list = NULL;
struct eventhandler_list *list_next = NULL;
lck_mtx_lock(&evthdlr_lists_ctxt->eventhandler_mutex);
TAILQ_FOREACH_SAFE(list, &evthdlr_lists_ctxt->eventhandler_lists,
el_link, list_next) {
VERIFY(TAILQ_EMPTY(&list->el_entries));
EHL_LOCK_DESTROY(list);
mcache_free(el_cache, list);
}
lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
lck_mtx_destroy(&evthdlr_lists_ctxt->eventhandler_mutex,
eventhandler_mutex_grp);
return;
}