--- //depot/vendor/freebsd/src/share/man/man9/EVENTHANDLER.9 +++ //depot/projects/smpng/share/man/man9/EVENTHANDLER.9 @@ -33,9 +33,13 @@ .In sys/eventhandler.h .Fn EVENTHANDLER_DECLARE name type .Fn EVENTHANDLER_INVOKE name ... +.Fn EVENTHANDLER_FAST_DECLARE name type +.Fn EVENTHANDLER_FAST_DEFINE name type +.Fn EVENTHANDLER_FAST_INVOKE name ... .Ft eventhandler_tag .Fn EVENTHANDLER_REGISTER name func arg priority .Fn EVENTHANDLER_DEREGISTER name tag +.Fn EVENTHANDLER_DEFINE name func arg priority .Ft eventhandler_tag .Fo eventhandler_register .Fa "struct eventhandler_list *list" @@ -60,15 +64,63 @@ kernel events and have their callback functions invoked when these events occur. .Pp -The normal way to use this subsystem is via the macro interface. +Each event is associated with a named event handler list which contains +the associated callback functions. +There are two types of event handler lists: +normal lists and fast lists. +.Pp +Normal lists are more flexible at the expense of additional overhead. +These lists are allocated dynamically when the first callback +function is registered. +Invoking a normal event list always requires a linear lookup of the +list by name, +even if there are no callback functions registered for the event. +.Pp +Fast lists use static allocation rather than dynamic allocation. +This allows fast list invocations to avoid the lookup operation +and to minimize overhead if no callback functions are registered for +an event. +However, +the storage for fast lists must always be valid, +so they cannot be used in a kernel module that supports unloading. +In addition, +fast lists must be defined in exactly one place. +.Pp +This subsystem should be used via the macro interface. The macros that can be used for working with event handlers and callback function lists are: .Bl -tag -width indent .It Fn EVENTHANDLER_DECLARE -This macro declares an event handler named by argument +This macro declares a normal event handler named by argument +.Fa name +with callback functions of type +.Fa type . +.It Fn EVENTHANDLER_INVOKE +This macro is used to invoke all the callbacks associated with the +event handler +.Fa name . +This macro is a variadic one. +Additional arguments to the macro after the +.Fa name +parameter are passed as the second and subsequent arguments to each +registered callback function. +.It Fn EVENTHANDLER_FAST_DECLARE +This macro declares a fast event handler named by argument .Fa name with callback functions of type .Fa type . +.It Fn EVENTHANDLER_FAST_DEFINE +This macro defines the storage for a fast event handler list and +registers the list with +.Fa name . +.It Fn EVENTHANDLER_FAST_INVOKE +This macro is used to invoke all the callbacks associated with the +fast event handler +.Fa name . +As with +.Fn EVENTHANDLER_INVOKE , +this macro is a variadic macro that passes additional arguments to +each registered callback function. .It Fn EVENTHANDLER_REGISTER This macro registers a callback function .Fa func @@ -78,10 +130,12 @@ .Fa func will be invoked with argument .Fa arg -as its first parameter along with any additional parameters passed in -via macro +as its first parameter along with any additional parameters passed to +the .Fn EVENTHANDLER_INVOKE -(see below). +or +.Fn EVENTHANDLER_FAST_INVOKE +macro. Callback functions are invoked in order of priority. The relative priority of each callback among other callbacks associated with an event is given by argument @@ -104,15 +158,17 @@ .Fa tag from the event handler named by argument .Fa name . -.It Fn EVENTHANDLER_INVOKE -This macro is used to invoke all the callbacks associated with event -handler -.Fa name . -This macro is a variadic one. -Additional arguments to the macro after the -.Fa name -parameter are passed as the second and subsequent arguments to each -registered callback function. +.It Fn EVENTHANDLER_DEFINE +This macro is used to manage a static callback. +It registers a callback function via the +.Fn EVENTHANDLER_REGISTER +macro at system boot or module load. +Its arguments have the same meaning as for the +.Fn EVENTHANDLER_REGISTER +macro. +Additionally, +the callback function will be removed from the named event handler +list on module unload. .El .Pp The macros are implemented using the following functions: --- //depot/vendor/freebsd/src/share/man/man9/Makefile +++ //depot/projects/smpng/share/man/man9/Makefile @@ -661,8 +661,12 @@ DRIVER_MODULE.9 EARLY_DRIVER_MODULE.9 \ DRIVER_MODULE.9 EARLY_DRIVER_MODULE_ORDERED.9 MLINKS+=EVENTHANDLER.9 EVENTHANDLER_DECLARE.9 \ + EVENTHANDLER.9 EVENTHANDLER_DEFINE.9 \ EVENTHANDLER.9 EVENTHANDLER_DEREGISTER.9 \ EVENTHANDLER.9 eventhandler_deregister.9 \ + EVENTHANDLER.9 EVENTHANDLER_FAST_DECLARE.9 \ + EVENTHANDLER.9 EVENTHANDLER_FAST_DEFINE.9 \ + EVENTHANDLER.9 EVENTHANDLER_FAST_INVOKE.9 \ EVENTHANDLER.9 eventhandler_find_list.9 \ EVENTHANDLER.9 EVENTHANDLER_INVOKE.9 \ EVENTHANDLER.9 eventhandler_prune_list.9 \ --- //depot/vendor/freebsd/src/sys/kern/subr_eventhandler.c +++ //depot/projects/smpng/sys/kern/subr_eventhandler.c @@ -50,6 +50,8 @@ }; static struct eventhandler_list *_eventhandler_find_list(const char *name); +static struct eventhandler_list *eventhandler_list_alloc(const char *name); +static void eventhandler_list_free(struct eventhandler_list *el); /* * Initialize the eventhandler mutex and list. @@ -64,6 +66,56 @@ SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init, NULL); +/* + * Locate an existing eventhandler list with the given name. If an + * existing list is not found, create a new list. + */ +static struct eventhandler_list * +eventhandler_list_alloc(const char *name) +{ + struct eventhandler_list *list, *new_list; + + mtx_lock(&eventhandler_mutex); + list = _eventhandler_find_list(name); + if (list == NULL) { + mtx_unlock(&eventhandler_mutex); + + new_list = malloc(sizeof(struct eventhandler_list) + strlen(name) + 1, + M_EVENTHANDLER, M_WAITOK | M_ZERO); + + mtx_lock(&eventhandler_mutex); + list = _eventhandler_find_list(name); + if (list != NULL) { + free(new_list, M_EVENTHANDLER); + } else { + CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name); + list = new_list; + list->el_name = (char *)list + sizeof(struct eventhandler_list); + strcpy(list->el_name, name); + TAILQ_INIT(&list->el_entries); + mtx_init(&list->el_lock, name, "eventhandler list", MTX_DEF); + TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link); + atomic_store_rel_int(&list->el_flags, EHL_INITTED); + } + } + mtx_unlock(&eventhandler_mutex); + return (list); +} + +/* + * Initialize a reference to a eventhandler list used for "fast" + * invocations. + */ +void +eventhandler_fast_list_init(void *arg) +{ + struct eventhandler_list_init *eli; + + eli = arg; + CTR2(KTR_EVH, "%s: looking up list \"%s\"", __func__, eli->eli_name); + eli->eli_list = eventhandler_list_alloc(eli->eli_name); +} + /* * Insertion is O(n) due to the priority scan, but optimises to O(1) * if all priorities are identical. @@ -72,50 +124,13 @@ eventhandler_register_internal(struct eventhandler_list *list, const char *name, eventhandler_tag epn) { - struct eventhandler_list *new_list; struct eventhandler_entry *ep; KASSERT(eventhandler_lists_initted, ("eventhandler registered too early")); KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__)); - /* lock the eventhandler lists */ - mtx_lock(&eventhandler_mutex); - - /* Do we need to find/create the (slow) list? */ - if (list == NULL) { - /* look for a matching, existing list */ - list = _eventhandler_find_list(name); - - /* Do we need to create the list? */ - if (list == NULL) { - mtx_unlock(&eventhandler_mutex); - - new_list = malloc(sizeof(struct eventhandler_list) + - strlen(name) + 1, M_EVENTHANDLER, M_WAITOK); - - /* If someone else created it already, then use that one. */ - mtx_lock(&eventhandler_mutex); - list = _eventhandler_find_list(name); - if (list != NULL) { - free(new_list, M_EVENTHANDLER); - } else { - CTR2(KTR_EVH, "%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)); - list->el_name = (char *)list + sizeof(struct eventhandler_list); - strcpy(list->el_name, name); - TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link); - } - } - } - if (!(list->el_flags & EHL_INITTED)) { - TAILQ_INIT(&list->el_entries); - mtx_init(&list->el_lock, name, "eventhandler list", MTX_DEF); - atomic_store_rel_int(&list->el_flags, EHL_INITTED); - } - mtx_unlock(&eventhandler_mutex); + if (list == NULL) + list = eventhandler_list_alloc(name); KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY, ("%s: handler for %s registered with dead priority", __func__, name)); @@ -244,8 +259,8 @@ { struct eventhandler_list *list; - if (!eventhandler_lists_initted) - return(NULL); + KASSERT(eventhandler_lists_initted, + ("eventhandler_find_list called too early")); /* scan looking for the requested list */ mtx_lock(&eventhandler_mutex); --- //depot/vendor/freebsd/src/sys/sys/eventhandler.h +++ //depot/projects/smpng/sys/sys/eventhandler.h @@ -59,6 +59,11 @@ TAILQ_HEAD(,eventhandler_entry) el_entries; }; +struct eventhandler_list_init { + char *eli_name; + struct eventhandler_list *eli_list; +}; + typedef struct eventhandler_entry *eventhandler_tag; #define EHL_LOCK(p) mtx_lock(&(p)->el_lock) @@ -98,12 +103,31 @@ } while (0) /* - * Slow handlers are entirely dynamic; lists are created - * when entries are added to them, and thus have no concept of "owner", + * Event handlers provide named lists of hooks to be invoked when a + * specific event occurs. Hooks are added and removed to lists that + * are identified by name. Lists are allocated when the first hook + * is registered for an event. + * + * Invoking an event handler calls all of the hooks associated with + * the event. There are two ways to invoke an event handler: + * + * 1) The default invocation operation accepts a list name and uses an + * O(n^2) lookup to locate the list to operate on. This is + * backwards compatible with the old API, but does trigger the + * overhead of the lookup even if no hooks are registered. + * + * 2) "Fast" invocations store a local reference to the named list + * when the containing module is loaded. This avoids the need for + * the O(n^2) lookup on each invocation. This does require + * EVENTHANDLER_FAST_DEFINE() to be invoked in the same code module + * at global scope to perform the lookup during module load. + * + * Note that both invocation methods may be used on the same list. * - * Slow handlers need to be declared, but do not need to be defined. The - * declaration must be in scope wherever the handler is to be invoked. + * Registering a hook for an event or invoking an event requires the + * event handler to be declared via EVENTHANDLER_DECLARE(). */ + #define EVENTHANDLER_DECLARE(name, type) \ struct eventhandler_entry_ ## name \ { \ @@ -112,17 +136,6 @@ }; \ struct __hack -#define EVENTHANDLER_DEFINE(name, func, arg, priority) \ - static eventhandler_tag name ## _tag; \ - static void name ## _evh_init(void *ctx) \ - { \ - name ## _tag = EVENTHANDLER_REGISTER(name, func, ctx, \ - priority); \ - } \ - SYSINIT(name ## _evh_init, SI_SUB_CONFIGURE, SI_ORDER_ANY, \ - name ## _evh_init, arg); \ - struct __hack - #define EVENTHANDLER_INVOKE(name, ...) \ do { \ struct eventhandler_list *_el; \ @@ -131,6 +144,40 @@ _EVENTHANDLER_INVOKE(name, _el , ## __VA_ARGS__); \ } while (0) +#define EVENTHANDLER_FAST_DEFINE(name) \ +struct eventhandler_list_init Xeventhandler_list_ ## name = \ + { #name, NULL }; \ +SYSINIT(eventhandler_list_ ##name, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, \ + eventhandler_fast_list_init, &Xeventhandler_list_ ## name) + +#define EVENTHANDLER_FAST_INVOKE(name, ...) do { \ + struct eventhandler_list *_el = Xeventhandler_list_ ## name.eli_list; \ + \ + KASSERT(_el->el_flags & EHL_INITTED, \ + ("eventhandler_fast_invoke: too early")); \ + if (!TAILQ_EMPTY(&_el->el_entries)) { \ + EHL_LOCK(_el); \ + _EVENTHANDLER_INVOKE(name, _el , ## __VA_ARGS__); \ + } \ +} while (0) + +#define EVENTHANDLER_DEFINE(name, func, arg, priority) \ +static eventhandler_tag name ## _tag; \ + \ +static void name ## _evh_init(void *ctx) \ +{ \ + name ## _tag = EVENTHANDLER_REGISTER(name, func, ctx, priority); \ +} \ +SYSINIT(name ## _evh_init, SI_SUB_CONFIGURE, SI_ORDER_ANY, \ + name ## _evh_init, arg); \ + \ +static void name ## _evh_fini(void *ctx __unused) \ +{ \ + EVENTHANDLER_DEREGISTER(name, name ## _tag); \ +} \ +SYSUNINIT(name ## _evh_fini, SI_SUB_CONFIGURE, SI_ORDER_ANY, \ + name ## _evh_fini, NULL) + #define EVENTHANDLER_REGISTER(name, func, arg, priority) \ eventhandler_register(NULL, #name, func, arg, priority) @@ -141,12 +188,12 @@ if ((_el = eventhandler_find_list(#name)) != NULL) \ eventhandler_deregister(_el, tag); \ } while(0) - -eventhandler_tag eventhandler_register(struct eventhandler_list *list, +eventhandler_tag eventhandler_register(struct eventhandler_list *list, const char *name, void *func, void *arg, int priority); void eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag); +void eventhandler_fast_list_init(void *arg); struct eventhandler_list *eventhandler_find_list(const char *name); void eventhandler_prune_list(struct eventhandler_list *list);