Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/subr_eventhandler.c
Show First 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | eventhandler_init(void *dummy __unused) | ||||
TAILQ_INIT(&eventhandler_lists); | TAILQ_INIT(&eventhandler_lists); | ||||
mtx_init(&eventhandler_mutex, "eventhandler", NULL, MTX_DEF); | mtx_init(&eventhandler_mutex, "eventhandler", NULL, MTX_DEF); | ||||
atomic_store_rel_int(&eventhandler_lists_initted, 1); | atomic_store_rel_int(&eventhandler_lists_initted, 1); | ||||
} | } | ||||
SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init, | SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init, | ||||
NULL); | NULL); | ||||
static struct eventhandler_list * | static struct eventhandler_list * | ||||
eventhandler_find_or_create_list(const char *name) | eventhandler_find_or_create_list(const char *name, epoch_t *pepoch) | ||||
{ | { | ||||
struct eventhandler_list *list, *new_list; | struct eventhandler_list *list, *new_list; | ||||
/* look for a matching, existing list */ | /* look for a matching, existing list */ | ||||
list = _eventhandler_find_list(name); | list = _eventhandler_find_list(name); | ||||
/* Do we need to create the list? */ | /* Do we need to create the list? */ | ||||
if (list == NULL) { | if (list == NULL) { | ||||
mtx_unlock(&eventhandler_mutex); | mtx_unlock(&eventhandler_mutex); | ||||
new_list = malloc(sizeof(*new_list) + strlen(name) + 1, | new_list = malloc(sizeof(*new_list) + strlen(name) + 1, | ||||
M_EVENTHANDLER, M_WAITOK | M_ZERO); | M_EVENTHANDLER, M_WAITOK | M_ZERO); | ||||
/* If someone else created it already, then use that one. */ | /* If someone else created it already, then use that one. */ | ||||
mtx_lock(&eventhandler_mutex); | mtx_lock(&eventhandler_mutex); | ||||
list = _eventhandler_find_list(name); | list = _eventhandler_find_list(name); | ||||
if (list != NULL) { | if (list != NULL) { | ||||
/* Verify that both list definitions are the same */ | |||||
MPASS(list->el_pepoch == pepoch); | |||||
free(new_list, M_EVENTHANDLER); | free(new_list, M_EVENTHANDLER); | ||||
} else { | } else { | ||||
CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name); | CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name); | ||||
list = new_list; | list = new_list; | ||||
TAILQ_INIT(&list->el_entries); | CK_LIST_INIT(&list->el_entries); | ||||
list->el_name = (char *)(list + 1); | list->el_name = (char *)(list + 1); | ||||
list->el_pepoch = pepoch; | |||||
strcpy(list->el_name, name); | strcpy(list->el_name, name); | ||||
mtx_init(&list->el_lock, list->el_name, "eventhandler list", | mtx_init(&list->el_lock, list->el_name, "eventhandler list", | ||||
MTX_DEF); | MTX_DEF); | ||||
TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link); | TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link); | ||||
} | } | ||||
} | } | ||||
return (list); | return (list); | ||||
} | } | ||||
/* | /* | ||||
* Insertion is O(n) due to the priority scan, but optimises to O(1) | * Insertion is O(n) due to the priority scan, but optimises to O(1) | ||||
* if all priorities are identical. | * if all priorities are identical. | ||||
*/ | */ | ||||
static eventhandler_tag | static eventhandler_tag | ||||
eventhandler_register_internal(struct eventhandler_list *list, | eventhandler_register_internal(struct eventhandler_list *list, | ||||
const char *name, eventhandler_tag epn) | const char *name, epoch_t *pepoch, eventhandler_tag epn) | ||||
{ | { | ||||
struct eventhandler_entry *ep; | struct eventhandler_entry *ep, *ep_last; | ||||
KASSERT(eventhandler_lists_initted, ("eventhandler registered too early")); | KASSERT(eventhandler_lists_initted, ("eventhandler registered too early")); | ||||
KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__)); | KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__)); | ||||
/* Do we need to find/create the list? */ | /* Do we need to find/create the list? */ | ||||
if (list == NULL) { | if (list == NULL) { | ||||
mtx_lock(&eventhandler_mutex); | mtx_lock(&eventhandler_mutex); | ||||
list = eventhandler_find_or_create_list(name); | list = eventhandler_find_or_create_list(name, pepoch); | ||||
mtx_unlock(&eventhandler_mutex); | mtx_unlock(&eventhandler_mutex); | ||||
} | } | ||||
KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY, | KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY, | ||||
("%s: handler for %s registered with dead priority", __func__, name)); | ("%s: handler for %s registered with dead priority", __func__, name)); | ||||
/* sort it into the list */ | /* sort it into the list */ | ||||
CTR4(KTR_EVH, "%s: adding item %p (function %p) to \"%s\"", __func__, epn, | CTR4(KTR_EVH, "%s: adding item %p (function %p) to \"%s\"", __func__, epn, | ||||
((struct eventhandler_entry_generic *)epn)->func, name); | ((struct eventhandler_entry_generic *)epn)->func, name); | ||||
EHL_LOCK(list); | EHL_LOCK(list); | ||||
TAILQ_FOREACH(ep, &list->el_entries, ee_link) { | ep_last = NULL; | ||||
CK_LIST_FOREACH(ep, &list->el_entries, ee_link) { | |||||
if (ep->ee_priority != EHE_DEAD_PRIORITY && | if (ep->ee_priority != EHE_DEAD_PRIORITY && | ||||
epn->ee_priority < ep->ee_priority) { | epn->ee_priority < ep->ee_priority) { | ||||
TAILQ_INSERT_BEFORE(ep, epn, ee_link); | CK_LIST_INSERT_BEFORE(ep, epn, ee_link); | ||||
break; | break; | ||||
} | } | ||||
ep_last = ep; | |||||
} | } | ||||
if (ep == NULL) | if (ep == NULL) { | ||||
TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link); | if (ep_last != NULL) | ||||
CK_LIST_INSERT_AFTER(ep_last, epn, ee_link); | |||||
else | |||||
CK_LIST_INSERT_HEAD(&list->el_entries, epn, ee_link); | |||||
} | |||||
EHL_UNLOCK(list); | EHL_UNLOCK(list); | ||||
return(epn); | return(epn); | ||||
} | } | ||||
eventhandler_tag | eventhandler_tag | ||||
eventhandler_register(struct eventhandler_list *list, const char *name, | eventhandler_register(struct eventhandler_list *list, const char *name, | ||||
void *func, void *arg, int priority) | void *func, void *arg, int priority) | ||||
{ | { | ||||
return (eventhandler_register_epoch(list, name, NULL, func, arg, priority)); | |||||
} | |||||
eventhandler_tag | |||||
eventhandler_register_epoch(struct eventhandler_list *list, const char *name, | |||||
epoch_t *pepoch, void *func, void *arg, int priority) | |||||
{ | |||||
struct eventhandler_entry_generic *eg; | struct eventhandler_entry_generic *eg; | ||||
/* allocate an entry for this handler, populate it */ | /* allocate an entry for this handler, populate it */ | ||||
eg = malloc(sizeof(struct eventhandler_entry_generic), M_EVENTHANDLER, | eg = malloc(sizeof(struct eventhandler_entry_generic), M_EVENTHANDLER, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
eg->func = func; | eg->func = func; | ||||
eg->ee.ee_arg = arg; | eg->ee.ee_arg = arg; | ||||
eg->ee.ee_priority = priority; | eg->ee.ee_priority = priority; | ||||
return (eventhandler_register_internal(list, name, &eg->ee)); | return (eventhandler_register_internal(list, name, pepoch, &eg->ee)); | ||||
} | } | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
struct eventhandler_entry_generic_vimage | struct eventhandler_entry_generic_vimage | ||||
{ | { | ||||
struct eventhandler_entry ee; | struct eventhandler_entry ee; | ||||
vimage_iterator_func_t func; /* Vimage iterator function. */ | vimage_iterator_func_t func; /* Vimage iterator function. */ | ||||
struct eventhandler_entry_vimage v_ee; /* Original func, arg. */ | struct eventhandler_entry_vimage v_ee; /* Original func, arg. */ | ||||
Show All 9 Lines | vimage_eventhandler_register(struct eventhandler_list *list, const char *name, | ||||
eg = malloc(sizeof(struct eventhandler_entry_generic_vimage), | eg = malloc(sizeof(struct eventhandler_entry_generic_vimage), | ||||
M_EVENTHANDLER, M_WAITOK | M_ZERO); | M_EVENTHANDLER, M_WAITOK | M_ZERO); | ||||
eg->func = iterfunc; | eg->func = iterfunc; | ||||
eg->v_ee.func = func; | eg->v_ee.func = func; | ||||
eg->v_ee.ee_arg = arg; | eg->v_ee.ee_arg = arg; | ||||
eg->ee.ee_arg = &eg->v_ee; | eg->ee.ee_arg = &eg->v_ee; | ||||
eg->ee.ee_priority = priority; | eg->ee.ee_priority = priority; | ||||
return (eventhandler_register_internal(list, name, &eg->ee)); | return (eventhandler_register_internal(list, name, NULL, &eg->ee)); | ||||
} | } | ||||
#endif | #endif | ||||
static void | static void | ||||
destroy_ep_epoch(epoch_context_t ctx) | |||||
{ | |||||
struct eventhandler_entry *ep; | |||||
ep = __containerof(ctx, struct eventhandler_entry, ee_epoch_ctx); | |||||
free(ep, M_EVENTHANDLER); | |||||
} | |||||
static void | |||||
free_list_item(struct eventhandler_list *list, struct eventhandler_entry *ep) | |||||
{ | |||||
if (EHL_TYPE_EPOCH(list)) | |||||
epoch_call(*(list->el_pepoch), destroy_ep_epoch, &ep->ee_epoch_ctx); | |||||
else | |||||
free(ep, M_EVENTHANDLER); | |||||
} | |||||
static void | |||||
_eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag, | _eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag, | ||||
bool wait) | bool wait) | ||||
{ | { | ||||
struct eventhandler_entry *ep = tag; | struct eventhandler_entry *ep = tag; | ||||
EHL_LOCK_ASSERT(list, MA_OWNED); | EHL_LOCK_ASSERT(list, MA_OWNED); | ||||
if (ep != NULL) { | if (ep != NULL) { | ||||
/* remove just this entry */ | /* remove just this entry */ | ||||
if (list->el_runcount == 0) { | if ((list->el_runcount == 0) || EHL_TYPE_EPOCH(list)) { | ||||
CTR3(KTR_EVH, "%s: removing item %p from \"%s\"", __func__, ep, | CTR3(KTR_EVH, "%s: removing item %p from \"%s\"", __func__, ep, | ||||
list->el_name); | list->el_name); | ||||
TAILQ_REMOVE(&list->el_entries, ep, ee_link); | CK_LIST_REMOVE(ep, ee_link); | ||||
free(ep, M_EVENTHANDLER); | free_list_item(list, ep); | ||||
} else { | } else { | ||||
CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__, | CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__, | ||||
ep, list->el_name); | ep, list->el_name); | ||||
ep->ee_priority = EHE_DEAD_PRIORITY; | ep->ee_priority = EHE_DEAD_PRIORITY; | ||||
} | } | ||||
} else { | } else { | ||||
/* remove entire list */ | /* remove entire list */ | ||||
if (list->el_runcount == 0) { | if ((list->el_runcount == 0) || EHL_TYPE_EPOCH(list)) { | ||||
CTR2(KTR_EVH, "%s: removing all items from \"%s\"", __func__, | CTR2(KTR_EVH, "%s: removing all items from \"%s\"", __func__, | ||||
list->el_name); | list->el_name); | ||||
while (!TAILQ_EMPTY(&list->el_entries)) { | while (!CK_LIST_EMPTY(&list->el_entries)) { | ||||
ep = TAILQ_FIRST(&list->el_entries); | ep = CK_LIST_FIRST(&list->el_entries); | ||||
TAILQ_REMOVE(&list->el_entries, ep, ee_link); | CK_LIST_REMOVE(ep, ee_link); | ||||
free(ep, M_EVENTHANDLER); | free_list_item(list, ep); | ||||
} | } | ||||
} else { | } else { | ||||
CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead", | CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead", | ||||
__func__, list->el_name); | __func__, list->el_name); | ||||
TAILQ_FOREACH(ep, &list->el_entries, ee_link) | CK_LIST_FOREACH(ep, &list->el_entries, ee_link) | ||||
ep->ee_priority = EHE_DEAD_PRIORITY; | ep->ee_priority = EHE_DEAD_PRIORITY; | ||||
} | } | ||||
} | } | ||||
while (wait && list->el_runcount > 0) | while (wait && list->el_runcount > 0) | ||||
mtx_sleep(list, &list->el_lock, 0, "evhrm", 0); | mtx_sleep(list, &list->el_lock, 0, "evhrm", 0); | ||||
EHL_UNLOCK(list); | EHL_UNLOCK(list); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
* Prune "dead" entries from an eventhandler list. | * Prune "dead" entries from an eventhandler list. | ||||
*/ | */ | ||||
void | void | ||||
eventhandler_prune_list(struct eventhandler_list *list) | eventhandler_prune_list(struct eventhandler_list *list) | ||||
{ | { | ||||
struct eventhandler_entry *ep, *en; | struct eventhandler_entry *ep, *en; | ||||
int pruned = 0; | int pruned = 0; | ||||
if (EHL_TYPE_EPOCH(list)) | |||||
return; | |||||
CTR2(KTR_EVH, "%s: pruning list \"%s\"", __func__, list->el_name); | CTR2(KTR_EVH, "%s: pruning list \"%s\"", __func__, list->el_name); | ||||
EHL_LOCK_ASSERT(list, MA_OWNED); | EHL_LOCK_ASSERT(list, MA_OWNED); | ||||
TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) { | CK_LIST_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) { | ||||
if (ep->ee_priority == EHE_DEAD_PRIORITY) { | if (ep->ee_priority == EHE_DEAD_PRIORITY) { | ||||
TAILQ_REMOVE(&list->el_entries, ep, ee_link); | CK_LIST_REMOVE(ep, ee_link); | ||||
free(ep, M_EVENTHANDLER); | free(ep, M_EVENTHANDLER); | ||||
pruned++; | pruned++; | ||||
} | } | ||||
} | } | ||||
if (pruned > 0) | if (pruned > 0) | ||||
wakeup(list); | wakeup(list); | ||||
} | } | ||||
/* | /* | ||||
* Create (or get the existing) list so the pointer can be stored by | * Create (or get the existing) list so the pointer can be stored by | ||||
* EVENTHANDLER_LIST_DEFINE. | * EVENTHANDLER_LIST_DEFINE. | ||||
*/ | */ | ||||
struct eventhandler_list * | struct eventhandler_list * | ||||
eventhandler_create_list(const char *name) | eventhandler_create_list(const char *name) | ||||
{ | { | ||||
return (eventhandler_create_list_epoch(name, NULL)); | |||||
} | |||||
struct eventhandler_list * | |||||
eventhandler_create_list_epoch(const char *name, epoch_t *pepoch) | |||||
{ | |||||
struct eventhandler_list *list; | struct eventhandler_list *list; | ||||
KASSERT(eventhandler_lists_initted, | KASSERT(eventhandler_lists_initted, | ||||
("eventhandler list created too early")); | ("eventhandler list created too early")); | ||||
mtx_lock(&eventhandler_mutex); | mtx_lock(&eventhandler_mutex); | ||||
list = eventhandler_find_or_create_list(name); | list = eventhandler_find_or_create_list(name, pepoch); | ||||
mtx_unlock(&eventhandler_mutex); | mtx_unlock(&eventhandler_mutex); | ||||
return (list); | return (list); | ||||
} | } |