Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/subr_eventhandler.c
Show All 31 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/ktr.h> | #include <sys/ktr.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/rmlock.h> | |||||
#include <sys/eventhandler.h> | #include <sys/eventhandler.h> | ||||
static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records"); | static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records"); | ||||
/* List of all eventhandler lists */ | /* List of all eventhandler lists */ | ||||
static TAILQ_HEAD(, eventhandler_list) eventhandler_lists; | static TAILQ_HEAD(, eventhandler_list) eventhandler_lists; | ||||
static int eventhandler_lists_initted = 0; | static int eventhandler_lists_initted = 0; | ||||
static struct mtx eventhandler_mutex; | static struct mtx eventhandler_mutex; | ||||
▲ Show 20 Lines • Show All 263 Lines • ▼ Show 20 Lines | eventhandler_create_list(const char *name) | ||||
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); | ||||
mtx_unlock(&eventhandler_mutex); | mtx_unlock(&eventhandler_mutex); | ||||
return (list); | return (list); | ||||
} | |||||
/* | |||||
* Preemptible and sleepable event handler points. | |||||
*/ | |||||
static void | |||||
eventhandler_preemptible_lock(struct eventhandler_common *ehc) | |||||
{ | |||||
struct eventhandler_preemptible *ehp; | |||||
ehp = __containerof(ehc, struct eventhandler_preemptible, common); | |||||
rm_wlock(&ehp->lock); | |||||
} | |||||
static void | |||||
eventhandler_preemptible_unlock(struct eventhandler_common *ehc) | |||||
{ | |||||
struct eventhandler_preemptible *ehp; | |||||
ehp = __containerof(ehc, struct eventhandler_preemptible, common); | |||||
rm_wunlock(&ehp->lock); | |||||
} | |||||
static void | |||||
eventhandler_sleepable_lock(struct eventhandler_common *ehc) | |||||
{ | |||||
struct eventhandler_sleepable *ehs; | |||||
ehs = __containerof(ehc, struct eventhandler_sleepable, common); | |||||
rms_wlock(&ehs->lock); | |||||
} | |||||
static void | |||||
eventhandler_sleepable_unlock(struct eventhandler_common *ehc) | |||||
{ | |||||
struct eventhandler_sleepable *ehs; | |||||
ehs = __containerof(ehc, struct eventhandler_sleepable, common); | |||||
rms_wunlock(&ehs->lock); | |||||
} | |||||
static void | |||||
eventhandler_common_validate(struct eventhandler_common *ehc) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < ehc->count; i++) { | |||||
if (ehc->cb[i] != NULL) { | |||||
switch (ehc->prio[i]) { | |||||
case EVENTHANDLER_PRIORITY_FIRST: | |||||
case EVENTHANDLER_PRIORITY_ANY: | |||||
case EVENTHANDLER_PRIORITY_LAST: | |||||
break; | |||||
case EVENTHANDLER_PRIORITY_INVALID: | |||||
default: | |||||
rpokala: If this is the default case, why specify `EVENTHANDLER_PRIORITY_INVALID` as well? Also… | |||||
panic("%s bad priority %d at %d\n", ehc->ident, | |||||
ehc->prio[i], i); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
for (i = 1; i < ehc->count; i++) { | |||||
if (ehc->prio[i] < ehc->prio[i - 1]) { | |||||
panic("%s unsorted priorities (%d < %d) at %d\n", ehc->ident, | |||||
ehc->prio[i], ehc->prio[i - 1], i); | |||||
} | |||||
} | |||||
for (i = ehc->count; i < ehc->size; i++) { | |||||
if (ehc->cb[i] != NULL) { | |||||
panic("%s callback %p found past the end at %d\n", | |||||
ehc->ident, ehc->cb[i], i); | |||||
} | |||||
if (ehc->prio[i] != EVENTHANDLER_PRIORITY_INVALID) { | |||||
panic("%s prio %d found past the end at %d\n", | |||||
ehc->ident, ehc->prio[i], i); | |||||
} | |||||
} | |||||
} | |||||
static void | |||||
eventhandler_common_assert_prio(struct eventhandler_common *ehc, | |||||
enum eventhandler_priority prio) | |||||
{ | |||||
switch (prio) { | |||||
case EVENTHANDLER_PRIORITY_FIRST: | |||||
case EVENTHANDLER_PRIORITY_ANY: | |||||
case EVENTHANDLER_PRIORITY_LAST: | |||||
break; | |||||
default: | |||||
case EVENTHANDLER_PRIORITY_INVALID: | |||||
panic("invalid priority %d passed", prio); | |||||
break; | |||||
} | |||||
} | |||||
void | |||||
eventhandler_preemptible_init(struct eventhandler_preemptible *ehp) | |||||
{ | |||||
rm_init(&ehp->lock, ehp->common.ident); | |||||
} | |||||
void | |||||
eventhandler_sleepable_init(struct eventhandler_sleepable *ehs) | |||||
{ | |||||
rms_init(&ehs->lock, ehs->common.ident); | |||||
} | |||||
static void | |||||
eventhandler_common_realloc(struct eventhandler_common *ehc, | |||||
void (*lock)(struct eventhandler_common *), | |||||
void (*unlock)(struct eventhandler_common *)) | |||||
{ | |||||
eventhandler_cb **newcb, **tofree_cb; | |||||
size_t newsize; | |||||
enum eventhandler_priority *newprio, *tofree_prio; | |||||
MPASS(ehc->count == ehc->size); | |||||
for (;;) { | |||||
if (ehc->size == 0) | |||||
newsize = 8; | |||||
else | |||||
newsize = ehc->size * 2; | |||||
unlock(ehc); | |||||
newcb = malloc(sizeof(ehc->cb[0]) * newsize, | |||||
M_EVENTHANDLER, M_WAITOK | M_ZERO); | |||||
newprio = malloc(sizeof(ehc->prio[0]) * newsize, | |||||
M_EVENTHANDLER, M_WAITOK | M_ZERO); | |||||
lock(ehc); | |||||
if (ehc->count == newsize) { | |||||
free(newcb, M_EVENTHANDLER); | |||||
free(newprio, M_EVENTHANDLER); | |||||
continue; | |||||
} | |||||
if (ehc->size >= newsize) { | |||||
free(newcb, M_EVENTHANDLER); | |||||
free(newprio, M_EVENTHANDLER); | |||||
MPASS(ehc->count < ehc->size); | |||||
return; | |||||
} | |||||
break; | |||||
} | |||||
tofree_cb = ehc->cb; | |||||
tofree_prio = ehc->prio; | |||||
memcpy(newcb, ehc->cb, sizeof(ehc->cb[0]) * ehc->size); | |||||
memcpy(newprio, ehc->prio, sizeof(ehc->prio[0]) * ehc->size); | |||||
ehc->cb = newcb; | |||||
ehc->prio = newprio; | |||||
ehc->size = newsize; | |||||
free(tofree_cb, M_EVENTHANDLER); | |||||
free(tofree_prio, M_EVENTHANDLER); | |||||
} | |||||
static void | |||||
eventhandler_common_register(struct eventhandler_common *ehc, void *pcb, | |||||
enum eventhandler_priority prio, void (*lock)(struct eventhandler_common *), | |||||
void (*unlock)(struct eventhandler_common *)) | |||||
{ | |||||
eventhandler_cb *cb; | |||||
int i; | |||||
eventhandler_common_assert_prio(ehc, prio); | |||||
cb = pcb; | |||||
lock(ehc); | |||||
MPASS(ehc->count <= ehc->size); | |||||
if (ehc->count == ehc->size) { | |||||
eventhandler_common_realloc(ehc, lock, unlock); | |||||
MPASS(ehc->count < ehc->size); | |||||
} | |||||
eventhandler_common_validate(ehc); | |||||
for (i = 0; i < ehc->size; i++) { | |||||
if (ehc->cb[i] == cb) | |||||
panic("callback %p already registered", cb); | |||||
} | |||||
switch (prio) { | |||||
case EVENTHANDLER_PRIORITY_INVALID: | |||||
/* appease clang */ | |||||
__builtin_unreachable(); | |||||
break; | |||||
case EVENTHANDLER_PRIORITY_FIRST: | |||||
memmove(&ehc->cb[1], &ehc->cb[0], | |||||
sizeof(ehc->cb[0]) * ehc->count); | |||||
memmove(&ehc->prio[1], &ehc->prio[0], | |||||
sizeof(ehc->prio[0]) * ehc->count); | |||||
ehc->cb[0] = cb; | |||||
ehc->prio[0] = prio; | |||||
ehc->count++; | |||||
break; | |||||
case EVENTHANDLER_PRIORITY_ANY: | |||||
for (i = 0; i < ehc->count; i++) { | |||||
if (ehc->prio[i] != EVENTHANDLER_PRIORITY_FIRST) | |||||
break; | |||||
} | |||||
/* | |||||
* Move all entries to cover for EVENTHANDLER_PRIORITY_LAST. | |||||
*/ | |||||
memmove(&ehc->cb[i + 1], &ehc->cb[i], | |||||
sizeof(ehc->cb[0]) * (ehc->count - i)); | |||||
memmove(&ehc->prio[i + 1], &ehc->prio[i], | |||||
sizeof(ehc->prio[0]) * (ehc->count - i)); | |||||
ehc->cb[i] = cb; | |||||
ehc->prio[i] = prio; | |||||
ehc->count++; | |||||
break; | |||||
case EVENTHANDLER_PRIORITY_LAST: | |||||
ehc->cb[ehc->count] = cb; | |||||
ehc->prio[ehc->count] = prio; | |||||
ehc->count++; | |||||
break; | |||||
} | |||||
eventhandler_common_validate(ehc); | |||||
unlock(ehc); | |||||
} | |||||
void | |||||
eventhandler_preemptible_register(struct eventhandler_preemptible *ehp, void *pcb, | |||||
enum eventhandler_priority prio) | |||||
{ | |||||
struct eventhandler_common *ehc; | |||||
ehc = &ehp->common; | |||||
eventhandler_common_register(ehc, pcb, prio, eventhandler_preemptible_lock, | |||||
eventhandler_preemptible_unlock); | |||||
} | |||||
void | |||||
eventhandler_sleepable_register(struct eventhandler_sleepable *ehs, void *pcb, | |||||
enum eventhandler_priority prio) | |||||
{ | |||||
struct eventhandler_common *ehc; | |||||
ehc = &ehs->common; | |||||
eventhandler_common_register(ehc, pcb, prio, eventhandler_sleepable_lock, | |||||
eventhandler_sleepable_unlock); | |||||
} | |||||
static void | |||||
eventhandler_common_deregister(struct eventhandler_common *ehc, void *pcb, | |||||
void (*lock)(struct eventhandler_common *), | |||||
void (*unlock)(struct eventhandler_common *)) | |||||
{ | |||||
eventhandler_cb *cb; | |||||
int i; | |||||
cb = pcb; | |||||
lock(ehc); | |||||
MPASS(ehc->count <= ehc->size); | |||||
eventhandler_common_validate(ehc); | |||||
for (i = 0; i < ehc->count; i++) { | |||||
if (ehc->cb[i] == cb) | |||||
break; | |||||
} | |||||
if (i == ehc->count) | |||||
panic("callback %p not found", cb); | |||||
switch (ehc->prio[i]) { | |||||
case EVENTHANDLER_PRIORITY_INVALID: | |||||
/* appease clang */ | |||||
__builtin_unreachable(); | |||||
break; | |||||
case EVENTHANDLER_PRIORITY_FIRST: | |||||
case EVENTHANDLER_PRIORITY_ANY: | |||||
case EVENTHANDLER_PRIORITY_LAST: | |||||
memmove(&ehc->cb[i], &ehc->cb[i + 1], | |||||
sizeof(ehc->cb[0]) * (ehc->count - i - 1)); | |||||
memmove(&ehc->prio[i], &ehc->prio[i + 1], | |||||
sizeof(ehc->prio[0]) * (ehc->count - i - 1)); | |||||
break; | |||||
Not Done Inline ActionsIs this missing that __unreachable() builtin? Shouldn't eventhandler_common_validate() cover this? erj: Is this missing that __unreachable() builtin? Shouldn't eventhandler_common_validate() cover… | |||||
Done Inline Actionsit is coverd by the validate func, but clang warns anyway mjg: it is coverd by the validate func, but clang warns anyway | |||||
} | |||||
ehc->cb[ehc->count - 1] = NULL; | |||||
ehc->prio[ehc->count - 1] = EVENTHANDLER_PRIORITY_INVALID; | |||||
ehc->count--; | |||||
eventhandler_common_validate(ehc); | |||||
unlock(ehc); | |||||
} | |||||
void | |||||
eventhandler_preemptible_deregister(struct eventhandler_preemptible *ehp, void *pcb) | |||||
{ | |||||
struct eventhandler_common *ehc; | |||||
ehc = &ehp->common; | |||||
eventhandler_common_deregister(ehc, pcb, eventhandler_preemptible_lock, | |||||
eventhandler_preemptible_unlock); | |||||
} | |||||
void | |||||
eventhandler_sleepable_deregister(struct eventhandler_sleepable *ehs, void *pcb) | |||||
{ | |||||
struct eventhandler_common *ehc; | |||||
ehc = &ehs->common; | |||||
eventhandler_common_deregister(ehc, pcb, eventhandler_sleepable_lock, | |||||
eventhandler_sleepable_unlock); | |||||
} | |||||
static void | |||||
eventhandler_common_invoke(struct eventhandler_common *ehc, void *arg) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < ehc->count; i++) { | |||||
(ehc->cb[i])(arg); | |||||
} | |||||
} | |||||
void | |||||
eventhandler_preemptible_invoke(struct eventhandler_preemptible *ehs, void *arg) | |||||
{ | |||||
struct rm_priotracker tracker; | |||||
struct eventhandler_common *ehc; | |||||
ehc = &ehs->common; | |||||
if (atomic_load_int(&ehc->count) == 0) | |||||
return; | |||||
rm_rlock(&ehs->lock, &tracker); | |||||
eventhandler_common_invoke(ehc, arg); | |||||
rm_runlock(&ehs->lock, &tracker); | |||||
} | |||||
void | |||||
eventhandler_sleepable_invoke(struct eventhandler_sleepable *ehs, void *arg) | |||||
{ | |||||
struct eventhandler_common *ehc; | |||||
rms_assert_rlock_ok(ehs->lock); | |||||
ehc = &ehs->common; | |||||
if (atomic_load_int(&ehc->count) == 0) | |||||
return; | |||||
rms_rlock(&ehs->lock); | |||||
eventhandler_common_invoke(ehc, arg); | |||||
rms_runlock(&ehs->lock); | |||||
} | } |
If this is the default case, why specify EVENTHANDLER_PRIORITY_INVALID as well? Also, default should come at the end of the list, so if you keep the INVALID case, it should fall-thru to default.