diff --git a/sys/conf/files b/sys/conf/files index 6e794a50b1a8..55778203872b 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2138,9 +2138,11 @@ kern/kern_exit.c standard kern/kern_fail.c standard kern/kern_fork.c standard kern/kern_gzio.c optional gzio +kern/kern_hhook.c standard kern/kern_idle.c standard kern/kern_intr.c standard kern/kern_jail.c standard +kern/kern_khelp.c standard kern/kern_kthread.c standard kern/kern_ktr.c optional ktr kern/kern_ktrace.c standard diff --git a/sys/kern/kern_hhook.c b/sys/kern/kern_hhook.c new file mode 100644 index 000000000000..946f76a5487c --- /dev/null +++ b/sys/kern/kern_hhook.c @@ -0,0 +1,457 @@ +/*- + * Copyright (c) 2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Lawrence Stewart while studying at the Centre + * for Advanced Internet Architectures, Swinburne University, made possible in + * part by grants from the FreeBSD Foundation and Cisco University Research + * Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct hhook { + hhook_func_t hhk_func; + struct helper *hhk_helper; + void *hhk_udata; + STAILQ_ENTRY(hhook) hhk_next; +}; + +MALLOC_DECLARE(M_HHOOK); +MALLOC_DEFINE(M_HHOOK, "hhook", "Helper hooks are linked off hhook_head lists"); + +LIST_HEAD(hhookheadhead, hhook_head); +VNET_DEFINE(struct hhookheadhead, hhook_head_list); +#define V_hhook_head_list VNET(hhook_head_list) + +static struct mtx hhook_head_list_lock; +MTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock", + MTX_DEF); + +/* Private function prototypes. */ +static void hhook_head_destroy(struct hhook_head *hhh); + +#define HHHLIST_LOCK() mtx_lock(&hhook_head_list_lock) +#define HHHLIST_UNLOCK() mtx_unlock(&hhook_head_list_lock) +#define HHHLIST_LOCK_ASSERT() mtx_assert(&hhook_head_list_lock, MA_OWNED) + +#define HHH_LOCK_INIT(hhh) rm_init(&(hhh)->hhh_lock, "hhook_head rm lock") +#define HHH_LOCK_DESTROY(hhh) rm_destroy(&(hhh)->hhh_lock) +#define HHH_WLOCK(hhh) rm_wlock(&(hhh)->hhh_lock) +#define HHH_WUNLOCK(hhh) rm_wunlock(&(hhh)->hhh_lock) +#define HHH_RLOCK(hhh, rmpt) rm_rlock(&(hhh)->hhh_lock, (rmpt)) +#define HHH_RUNLOCK(hhh, rmpt) rm_runlock(&(hhh)->hhh_lock, (rmpt)) + +/* + * Run all helper hook functions for a given hook point. + */ +void +hhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd) +{ + struct hhook *hhk; + void *hdata; + struct rm_priotracker rmpt; + + KASSERT(hhh->hhh_refcount > 0, ("hhook_head %p refcount is 0", hhh)); + + HHH_RLOCK(hhh, &rmpt); + STAILQ_FOREACH(hhk, &hhh->hhh_hooks, hhk_next) { + if (hhk->hhk_helper->h_flags & HELPER_NEEDS_OSD) { + hdata = osd_get(OSD_KHELP, hosd, hhk->hhk_helper->h_id); + if (hdata == NULL) + continue; + } else + hdata = NULL; + + /* + * XXXLAS: We currently ignore the int returned by the hook, + * but will likely want to handle it in future to allow hhook to + * be used like pfil and effect changes at the hhook calling + * site e.g. we could define a new hook type of HHOOK_TYPE_PFIL + * and standardise what particular return values mean and set + * the context data to pass exactly the same information as pfil + * hooks currently receive, thus replicating pfil with hhook. + */ + hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata, + ctx_data, hdata, hosd); + } + HHH_RUNLOCK(hhh, &rmpt); +} + +/* + * Register a new helper hook function with a helper hook point. + */ +int +hhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags) +{ + struct hhook *hhk, *tmp; + int error; + + error = 0; + + if (hhh == NULL) + return (ENOENT); + + hhk = malloc(sizeof(struct hhook), M_HHOOK, + M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT)); + + if (hhk == NULL) + return (ENOMEM); + + hhk->hhk_helper = hki->hook_helper; + hhk->hhk_func = hki->hook_func; + hhk->hhk_udata = hki->hook_udata; + + HHH_WLOCK(hhh); + STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) { + if (tmp->hhk_func == hki->hook_func && + tmp->hhk_udata == hki->hook_udata) { + /* The helper hook function is already registered. */ + error = EEXIST; + break; + } + } + + if (!error) { + STAILQ_INSERT_TAIL(&hhh->hhh_hooks, hhk, hhk_next); + hhh->hhh_nhooks++; + } + else + free(hhk, M_HHOOK); + + HHH_WUNLOCK(hhh); + + return (error); +} + +/* + * Lookup a helper hook point and register a new helper hook function with it. + */ +int +hhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags) +{ + struct hhook_head *hhh; + int error; + + hhh = hhook_head_get(hki->hook_type, hki->hook_id); + + if (hhh == NULL) + return (ENOENT); + + error = hhook_add_hook(hhh, hki, flags); + hhook_head_release(hhh); + + return (error); +} + +/* + * Remove a helper hook function from a helper hook point. + */ +int +hhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki) +{ + struct hhook *tmp; + + if (hhh == NULL) + return (ENOENT); + + HHH_WLOCK(hhh); + STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) { + if (tmp->hhk_func == hki->hook_func && + tmp->hhk_udata == hki->hook_udata) { + STAILQ_REMOVE(&hhh->hhh_hooks, tmp, hhook, hhk_next); + free(tmp, M_HHOOK); + hhh->hhh_nhooks--; + break; + } + } + HHH_WUNLOCK(hhh); + + return (0); +} + +/* + * Lookup a helper hook point and remove a helper hook function from it. + */ +int +hhook_remove_hook_lookup(struct hookinfo *hki) +{ + struct hhook_head *hhh; + + hhh = hhook_head_get(hki->hook_type, hki->hook_id); + + if (hhh == NULL) + return (ENOENT); + + hhook_remove_hook(hhh, hki); + hhook_head_release(hhh); + + return (0); +} + +/* + * Register a new helper hook point. + */ +int +hhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hhh, + uint32_t flags) +{ + struct hhook_head *tmphhh; + + tmphhh = hhook_head_get(hhook_type, hhook_id); + + if (tmphhh != NULL) { + /* Hook point previously registered. */ + hhook_head_release(tmphhh); + return (EEXIST); + } + + /* XXXLAS: Need to implement support for non-virtualised hooks. */ + if ((flags & HHOOK_HEADISINVNET) == 0) { + printf("%s: only vnet-style virtualised hooks can be used\n", + __func__); + return (EINVAL); + } + + tmphhh = malloc(sizeof(struct hhook_head), M_HHOOK, + M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT)); + + if (tmphhh == NULL) + return (ENOMEM); + + tmphhh->hhh_type = hhook_type; + tmphhh->hhh_id = hhook_id; + tmphhh->hhh_nhooks = 0; + STAILQ_INIT(&tmphhh->hhh_hooks); + HHH_LOCK_INIT(tmphhh); + + if (hhh != NULL) + refcount_init(&tmphhh->hhh_refcount, 1); + else + refcount_init(&tmphhh->hhh_refcount, 0); + + if (flags & HHOOK_HEADISINVNET) { + tmphhh->hhh_flags |= HHH_ISINVNET; + HHHLIST_LOCK(); + LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next); + HHHLIST_UNLOCK(); + } else { + /* XXXLAS: Add tmphhh to the non-virtualised list. */ + } + + *hhh = tmphhh; + + return (0); +} + +static void +hhook_head_destroy(struct hhook_head *hhh) +{ + struct hhook *tmp, *tmp2; + + HHHLIST_LOCK_ASSERT(); + + LIST_REMOVE(hhh, hhh_next); + HHH_WLOCK(hhh); + STAILQ_FOREACH_SAFE(tmp, &hhh->hhh_hooks, hhk_next, tmp2) + free(tmp, M_HHOOK); + HHH_WUNLOCK(hhh); + HHH_LOCK_DESTROY(hhh); + free(hhh, M_HHOOK); +} + +/* + * Remove a helper hook point. + */ +int +hhook_head_deregister(struct hhook_head *hhh) +{ + int error; + + error = 0; + + HHHLIST_LOCK(); + if (hhh == NULL) + error = ENOENT; + else if (hhh->hhh_refcount > 1) + error = EBUSY; + else + hhook_head_destroy(hhh); + HHHLIST_UNLOCK(); + + return (error); +} + +/* + * Remove a helper hook point via a hhook_head lookup. + */ +int +hhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id) +{ + struct hhook_head *hhh; + int error; + + error = 0; + hhh = hhook_head_get(hhook_type, hhook_id); + error = hhook_head_deregister(hhh); + + if (error == EBUSY) + hhook_head_release(hhh); + + return (error); +} + +/* + * Lookup and return the hhook_head struct associated with the specified type + * and id, or NULL if not found. If found, the hhook_head's refcount is bumped. + */ +struct hhook_head * +hhook_head_get(int32_t hhook_type, int32_t hhook_id) +{ + struct hhook_head *hhh; + + /* XXXLAS: Pick hhook_head_list based on hhook_head flags. */ + HHHLIST_LOCK(); + LIST_FOREACH(hhh, &V_hhook_head_list, hhh_next) { + if (hhh->hhh_type == hhook_type && hhh->hhh_id == hhook_id) { + refcount_acquire(&hhh->hhh_refcount); + HHHLIST_UNLOCK(); + return (hhh); + } + } + HHHLIST_UNLOCK(); + + return (NULL); +} + +void +hhook_head_release(struct hhook_head *hhh) +{ + + refcount_release(&hhh->hhh_refcount); +} + +/* + * Check the hhook_head private flags and return the appropriate public + * representation of the flag to the caller. The function is implemented in a + * way that allows us to cope with other subsystems becoming virtualised in the + * future. + */ +uint32_t +hhook_head_is_virtualised(struct hhook_head *hhh) +{ + uint32_t ret; + + if (hhh == NULL) + return (0); + + if (hhh->hhh_flags & HHH_ISINVNET) + ret = HHOOK_HEADISINVNET; + + return (ret); +} + +uint32_t +hhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id) +{ + struct hhook_head *hhh; + uint32_t ret; + + hhh = hhook_head_get(hook_type, hook_id); + + if (hhh == NULL) + return (0); + + ret = hhook_head_is_virtualised(hhh); + hhook_head_release(hhh); + + return (ret); +} + +/* + * Vnet created and being initialised. + */ +static void +hhook_vnet_init(const void *unused __unused) +{ + + LIST_INIT(&V_hhook_head_list); +} + +/* + * Vnet being torn down and destroyed. + */ +static void +hhook_vnet_uninit(const void *unused __unused) +{ + struct hhook_head *hhh, *tmphhh; + + /* + * If subsystems which export helper hook points use the hhook KPI + * correctly, the loop below should have no work to do because the + * subsystem should have already called hhook_head_deregister(). + */ + HHHLIST_LOCK(); + LIST_FOREACH_SAFE(hhh, &V_hhook_head_list, hhh_next, tmphhh) { + printf("%s: hhook_head type=%d, id=%d cleanup required\n", + __func__, hhh->hhh_type, hhh->hhh_id); + hhook_head_destroy(hhh); + } + HHHLIST_UNLOCK(); +} + + +/* + * When a vnet is created and being initialised, init the V_hhook_head_list. + */ +VNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, + hhook_vnet_init, NULL); + +/* + * The hhook KPI provides a mechanism for subsystems which export helper hook + * points to clean up on vnet tear down, but in case the KPI is misused, + * provide a function to clean up and free memory for a vnet being destroyed. + */ +VNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, + hhook_vnet_uninit, NULL); diff --git a/sys/kern/kern_khelp.c b/sys/kern/kern_khelp.c new file mode 100644 index 000000000000..f6ae3e215284 --- /dev/null +++ b/sys/kern/kern_khelp.c @@ -0,0 +1,471 @@ +/*- + * Copyright (c) 2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Lawrence Stewart while studying at the Centre + * for Advanced Internet Architectures, Swinburne University, made possible in + * part by grants from the FreeBSD Foundation and Cisco University Research + * Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct rwlock khelp_list_lock; +RW_SYSINIT(khelplistlock, &khelp_list_lock, "helper list lock"); + +static TAILQ_HEAD(helper_head, helper) helpers = TAILQ_HEAD_INITIALIZER(helpers); + +/* Private function prototypes. */ +static inline void khelp_remove_osd(struct helper *h, struct osd *hosd); + +#define KHELP_LIST_WLOCK() rw_wlock(&khelp_list_lock) +#define KHELP_LIST_WUNLOCK() rw_wunlock(&khelp_list_lock) +#define KHELP_LIST_RLOCK() rw_rlock(&khelp_list_lock) +#define KHELP_LIST_RUNLOCK() rw_runlock(&khelp_list_lock) +#define KHELP_LIST_LOCK_ASSERT() rw_assert(&khelp_list_lock, RA_LOCKED) + +int +khelp_register_helper(struct helper *h) +{ + struct helper *tmph; + int error, i, inserted; + + error = 0; + inserted = 0; + refcount_init(&h->h_refcount, 0); + h->h_id = osd_register(OSD_KHELP, NULL, NULL); + + /* It's only safe to add the hooks after osd_register(). */ + if (h->h_nhooks > 0) { + for (i = 0; i < h->h_nhooks && !error; i++) { + /* We don't require the module to assign hook_helper. */ + h->h_hooks[i].hook_helper = h; + error = khelp_add_hhook(&h->h_hooks[i], HHOOK_NOWAIT); + } + + if (error) { + for (i--; i >= 0; i--) + khelp_remove_hhook(&h->h_hooks[i]); + + osd_deregister(OSD_KHELP, h->h_id); + } + } + + if (!error) { + KHELP_LIST_WLOCK(); + /* + * Keep list of helpers sorted in descending h_id order. Due to + * the way osd_set() works, a sorted list ensures + * init_helper_osd() will operate with improved efficiency. + */ + TAILQ_FOREACH(tmph, &helpers, h_next) { + if (tmph->h_id < h->h_id) { + TAILQ_INSERT_BEFORE(tmph, h, h_next); + inserted = 1; + break; + } + } + + if (!inserted) + TAILQ_INSERT_TAIL(&helpers, h, h_next); + KHELP_LIST_WUNLOCK(); + } + + return (error); +} + +int +khelp_deregister_helper(struct helper *h) +{ + struct helper *tmph; + int error, i; + + error = 0; + + KHELP_LIST_WLOCK(); + if (h->h_refcount > 0) + error = EBUSY; + else { + error = ENOENT; + TAILQ_FOREACH(tmph, &helpers, h_next) { + if (tmph == h) { + TAILQ_REMOVE(&helpers, h, h_next); + error = 0; + break; + } + } + } + KHELP_LIST_WUNLOCK(); + + if (!error) { + if (h->h_nhooks > 0) { + for (i = 0; i < h->h_nhooks; i++) + khelp_remove_hhook(&h->h_hooks[i]); + } + osd_deregister(OSD_KHELP, h->h_id); + } + + return (error); +} + +int +khelp_init_osd(uint32_t classes, struct osd *hosd) +{ + struct helper *h; + void *hdata; + int error; + + KASSERT(hosd != NULL, ("struct osd not initialised!")); + + error = 0; + + KHELP_LIST_RLOCK(); + TAILQ_FOREACH(h, &helpers, h_next) { + /* If helper is correct class and needs to store OSD... */ + if (h->h_classes & classes && h->h_flags & HELPER_NEEDS_OSD) { + hdata = uma_zalloc(h->h_zone, M_NOWAIT); + if (hdata == NULL) { + error = ENOMEM; + break; + } + osd_set(OSD_KHELP, hosd, h->h_id, hdata); + refcount_acquire(&h->h_refcount); + } + } + + if (error) { + /* Delete OSD that was assigned prior to the error. */ + TAILQ_FOREACH(h, &helpers, h_next) { + if (h->h_classes & classes) + khelp_remove_osd(h, hosd); + } + } + KHELP_LIST_RUNLOCK(); + + return (error); +} + +int +khelp_destroy_osd(struct osd *hosd) +{ + struct helper *h; + int error; + + KASSERT(hosd != NULL, ("struct osd not initialised!")); + + error = 0; + + KHELP_LIST_RLOCK(); + /* + * Clean up all khelp related OSD. + * + * XXXLAS: Would be nice to use something like osd_exit() here but it + * doesn't have the right semantics for this purpose. + */ + TAILQ_FOREACH(h, &helpers, h_next) + khelp_remove_osd(h, hosd); + KHELP_LIST_RUNLOCK(); + + return (error); +} + +static inline void +khelp_remove_osd(struct helper *h, struct osd *hosd) +{ + void *hdata; + + if (h->h_flags & HELPER_NEEDS_OSD) { + /* + * If the current helper uses OSD and calling osd_get() + * on the helper's h_id returns non-NULL, the helper has + * OSD attached to 'hosd' which needs to be cleaned up. + */ + hdata = osd_get(OSD_KHELP, hosd, h->h_id); + if (hdata != NULL) { + uma_zfree(h->h_zone, hdata); + osd_del(OSD_KHELP, hosd, h->h_id); + refcount_release(&h->h_refcount); + } + } +} + +void * +khelp_get_osd(struct osd *hosd, int32_t id) +{ + + return (osd_get(OSD_KHELP, hosd, id)); +} + +int32_t +khelp_get_id(char *hname) +{ + struct helper *h; + int32_t id; + + id = -1; + + KHELP_LIST_RLOCK(); + TAILQ_FOREACH(h, &helpers, h_next) { + if (strncmp(h->h_name, hname, HELPER_NAME_MAXLEN) == 0) { + id = h->h_id; + break; + } + } + KHELP_LIST_RUNLOCK(); + + return (id); +} + +int +khelp_add_hhook(struct hookinfo *hki, uint32_t flags) +{ + VNET_ITERATOR_DECL(vnet_iter); + int error; + + error = 0; + + /* + * XXXLAS: If a helper is dynamically adding a helper hook function at + * runtime using this function, we should update the helper's h_hooks + * struct member to include the additional hookinfo struct. + */ + + VNET_LIST_RLOCK_NOSLEEP(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + error = hhook_add_hook_lookup(hki, flags); + CURVNET_RESTORE(); +#ifdef VIMAGE + if (error) + break; +#endif + } + VNET_LIST_RUNLOCK_NOSLEEP(); + + return (error); +} + +int +khelp_remove_hhook(struct hookinfo *hki) +{ + VNET_ITERATOR_DECL(vnet_iter); + int error; + + error = 0; + + /* + * XXXLAS: If a helper is dynamically removing a helper hook function at + * runtime using this function, we should update the helper's h_hooks + * struct member to remove the defunct hookinfo struct. + */ + + VNET_LIST_RLOCK_NOSLEEP(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + error = hhook_remove_hook_lookup(hki); + CURVNET_RESTORE(); +#ifdef VIMAGE + if (error) + break; +#endif + } + VNET_LIST_RUNLOCK_NOSLEEP(); + + return (error); +} + +int +khelp_modevent(module_t mod, int event_type, void *data) +{ + struct khelp_modevent_data *kmd; + int error; + + kmd = (struct khelp_modevent_data *)data; + error = 0; + + switch(event_type) { + case MOD_LOAD: + if (kmd->helper->h_flags & HELPER_NEEDS_OSD) { + if (kmd->uma_zsize <= 0) { + printf("Use KHELP_DECLARE_MOD_UMA() instead!\n"); + error = EDOOFUS; + break; + } + kmd->helper->h_zone = uma_zcreate(kmd->name, + kmd->uma_zsize, kmd->umactor, kmd->umadtor, NULL, + NULL, 0, 0); + if (kmd->helper->h_zone == NULL) { + error = ENOMEM; + break; + } + } + strlcpy(kmd->helper->h_name, kmd->name, HELPER_NAME_MAXLEN); + kmd->helper->h_hooks = kmd->hooks; + kmd->helper->h_nhooks = kmd->nhooks; + if (kmd->helper->mod_init != NULL) + error = kmd->helper->mod_init(); + if (!error) + error = khelp_register_helper(kmd->helper); + break; + + case MOD_QUIESCE: + case MOD_SHUTDOWN: + case MOD_UNLOAD: + error = khelp_deregister_helper(kmd->helper); + if (!error) { + if (kmd->helper->h_flags & HELPER_NEEDS_OSD) + uma_zdestroy(kmd->helper->h_zone); + if (kmd->helper->mod_destroy != NULL) + kmd->helper->mod_destroy(); + } else if (error == ENOENT) + /* Do nothing and allow unload if helper not in list. */ + error = 0; + else if (error == EBUSY) + printf("Khelp module \"%s\" can't unload until its " + "refcount drops from %d to 0.\n", kmd->name, + kmd->helper->h_refcount); + break; + + default: + error = EINVAL; + break; + } + + return (error); +} + +/* + * This function is called in two separate situations: + * + * - When the kernel is booting, it is called directly by the SYSINIT framework + * to allow Khelp modules which were compiled into the kernel or loaded by the + * boot loader to insert their non-virtualised hook functions into the kernel. + * + * - When the kernel is booting or a vnet is created, this function is also + * called indirectly through khelp_vnet_init() by the vnet initialisation code. + * In this situation, Khelp modules are able to insert their virtualised hook + * functions into the virtualised hook points in the vnet which is being + * initialised. In the case where the kernel is not compiled with "options + * VIMAGE", this step is still run once at boot, but the hook functions get + * transparently inserted into the standard unvirtualised network stack. + */ +static void +khelp_init(const void *vnet) +{ + struct helper *h; + int error, i, vinit; + int32_t htype, hid; + + error = 0; + vinit = vnet != NULL; + + KHELP_LIST_RLOCK(); + TAILQ_FOREACH(h, &helpers, h_next) { + for (i = 0; i < h->h_nhooks && !error; i++) { + htype = h->h_hooks[i].hook_type; + hid = h->h_hooks[i].hook_id; + + /* + * If we're doing a virtualised init (vinit != 0) and + * the hook point is virtualised, or we're doing a plain + * sysinit at boot and the hook point is not + * virtualised, insert the hook. + */ + if ((hhook_head_is_virtualised_lookup(htype, hid) == + HHOOK_HEADISINVNET && vinit) || + (!hhook_head_is_virtualised_lookup(htype, hid) && + !vinit)) { + error = hhook_add_hook_lookup(&h->h_hooks[i], + HHOOK_NOWAIT); + } + } + + if (error) { + /* Remove any helper's hooks we successfully added. */ + for (i--; i >= 0; i--) + hhook_remove_hook_lookup(&h->h_hooks[i]); + + printf("%s: Failed to add hooks for helper \"%s\" (%p)", + __func__, h->h_name, h); + if (vinit) + printf(" to vnet %p.\n", vnet); + else + printf(".\n"); + + error = 0; + } + } + KHELP_LIST_RUNLOCK(); +} + +/* + * Vnet created and being initialised. + */ +static void +khelp_vnet_init(const void *unused __unused) +{ + + khelp_init(TD_TO_VNET(curthread)); +} + + +/* + * As the kernel boots, allow Khelp modules which were compiled into the kernel + * or loaded by the boot loader to insert their non-virtualised hook functions + * into the kernel. + */ +SYSINIT(khelp_init, SI_SUB_PROTO_END, SI_ORDER_FIRST, khelp_init, NULL); + +/* + * When a vnet is created and being initialised, we need to insert the helper + * hook functions for all currently registered Khelp modules into the vnet's + * helper hook points. The hhook KPI provides a mechanism for subsystems which + * export helper hook points to clean up on vnet shutdown, so we don't need a + * VNET_SYSUNINIT for Khelp. + */ +VNET_SYSINIT(khelp_vnet_init, SI_SUB_PROTO_END, SI_ORDER_FIRST, + khelp_vnet_init, NULL); diff --git a/sys/sys/hhook.h b/sys/sys/hhook.h new file mode 100644 index 000000000000..8cca8a0bd627 --- /dev/null +++ b/sys/sys/hhook.h @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Lawrence Stewart while studying at the Centre + * for Advanced Internet Architectures, Swinburne University, made possible in + * part by grants from the FreeBSD Foundation and Cisco University Research + * Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * A KPI modelled on the pfil framework for instantiating helper hook points + * within the kernel for use by Khelp modules. Originally released as part of + * the NewTCP research project at Swinburne University's Centre for Advanced + * Internet Architectures, Melbourne, Australia, which was made possible in part + * by a grant from the Cisco University Research Program Fund at Community + * Foundation Silicon Valley. More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +#ifndef _SYS_HHOOK_H_ +#define _SYS_HHOOK_H_ + +/* XXXLAS: Is there a way around this? */ +#include +#include + +/* hhook_head flags. */ +#define HHH_ISINVNET 0x00000001 /* Is the hook point in a vnet? */ + +/* Flags common to all register functions. */ +#define HHOOK_WAITOK 0x00000001 /* Sleeping allowed. */ +#define HHOOK_NOWAIT 0x00000002 /* Sleeping disallowed. */ +/* Flags only relevant to hhook_head_register() and hhook_head_is_virtual(). */ +#define HHOOK_HEADISINVNET 0x00000100 /* Public proxy for HHH_ISINVNET. */ + +/* Helper hook types. */ +#define HHOOK_TYPE_TCP 1 + +struct helper; +struct osd; + +/* Signature for helper hook functions. */ +typedef int (*hhook_func_t)(int32_t hhook_type, int32_t hhook_id, void *udata, + void *ctx_data, void *hdata, struct osd *hosd); + +/* + * Information required to add/remove a helper hook function to/from a helper + * hook point. + */ +struct hookinfo { + hhook_func_t hook_func; + struct helper *hook_helper; + void *hook_udata; + int32_t hook_id; + int32_t hook_type; +}; + +/* + * Ideally this would be private but we need access to the hhh_nhooks member + * variable in order to make the HHOOKS_RUN_IF() macro low impact. + */ +struct hhook_head { + STAILQ_HEAD(hhook_list, hhook) hhh_hooks; + struct rmlock hhh_lock; + int32_t hhh_id; + int32_t hhh_nhooks; + int32_t hhh_type; + uint32_t hhh_flags; + volatile uint32_t hhh_refcount; + LIST_ENTRY(hhook_head) hhh_next; +}; + +/* Public KPI functions. */ +void hhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd); + +int hhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, + uint32_t flags); + +int hhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags); + +int hhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki); + +int hhook_remove_hook_lookup(struct hookinfo *hki); + +int hhook_head_register(int32_t hhook_type, int32_t hhook_id, + struct hhook_head **hhh, uint32_t flags); + +int hhook_head_deregister(struct hhook_head *hhh); + +int hhook_head_deregister_lookup(int32_t hhook_type, int32_t hhook_id); + +struct hhook_head * hhook_head_get(int32_t hhook_type, int32_t hhook_id); + +void hhook_head_release(struct hhook_head *hhh); + +uint32_t hhook_head_is_virtualised(struct hhook_head *hhh); + +uint32_t hhook_head_is_virtualised_lookup(int32_t hook_type, int32_t hook_id); + +/* + * A wrapper around hhook_run_hooks() that only calls the function if at least + * one helper hook function is registered for the specified helper hook point. + */ +#define HHOOKS_RUN_IF(hhh, ctx_data, hosd) do { \ + if (hhh != NULL && hhh->hhh_nhooks > 0) \ + hhook_run_hooks(hhh, ctx_data, hosd); \ +} while (0) + +/* + * WARNING: This macro should only be used in code paths that execute + * infrequently, otherwise the refcounting overhead would be excessive. + * + * A similar wrapper to HHOOKS_RUN_IF() for situations where the caller prefers + * not to lookup and store the appropriate hhook_head pointer themselves. + */ +#define HHOOKS_RUN_LOOKUP_IF(hhook_type, hhook_id, ctx_data, hosd) do { \ + struct hhook_head *_hhh; \ + \ + _hhh = hhook_head_get(hhook_type, hhook_id); \ + if (_hhh != NULL) { \ + if (_hhh->hhh_nhooks > 0) \ + hhook_run_hooks(_hhh, ctx_data, hosd); \ + hhook_head_release(_hhh); \ + } \ +} while (0) + +#endif /* _SYS_HHOOK_H_ */ diff --git a/sys/sys/khelp.h b/sys/sys/khelp.h new file mode 100644 index 000000000000..9b3ae1dc4b60 --- /dev/null +++ b/sys/sys/khelp.h @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Lawrence Stewart while studying at the Centre + * for Advanced Internet Architectures, Swinburne University, made possible in + * part by grants from the FreeBSD Foundation and Cisco University Research + * Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * A KPI for managing kernel helper modules which perform useful functionality + * within the kernel. Originally released as part of the NewTCP research + * project at Swinburne University's Centre for Advanced Internet Architectures, + * Melbourne, Australia, which was made possible in part by a grant from the + * Cisco University Research Program Fund at Community Foundation Silicon + * Valley. More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +#ifndef _SYS_KHELP_H_ +#define _SYS_KHELP_H_ + +struct helper; +struct hookinfo; +struct osd; + +/* Helper classes. */ +#define HELPER_CLASS_TCP 0x00000001 + +/* Public KPI functions. */ +int khelp_register_helper(struct helper *h); + +int khelp_deregister_helper(struct helper *h); + +int khelp_init_osd(uint32_t classes, struct osd *hosd); + +int khelp_destroy_osd(struct osd *hosd); + +void * khelp_get_osd(struct osd *hosd, int32_t id); + +int32_t khelp_get_id(char *hname); + +int khelp_add_hhook(struct hookinfo *hki, uint32_t flags); + +int khelp_remove_hhook(struct hookinfo *hki); + +#endif /* _SYS_KHELP_H_ */ diff --git a/sys/sys/module_khelp.h b/sys/sys/module_khelp.h new file mode 100644 index 000000000000..39e2591feec3 --- /dev/null +++ b/sys/sys/module_khelp.h @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Lawrence Stewart while studying at the Centre + * for Advanced Internet Architectures, Swinburne University, made possible in + * part by grants from the FreeBSD Foundation and Cisco University Research + * Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS_MODULE_KHELP_H_ +#define _SYS_MODULE_KHELP_H_ + +/* XXXLAS: Needed for uma related typedefs. */ +#include + +/* Helper flags. */ +#define HELPER_NEEDS_OSD 0x0001 + +struct helper { + int (*mod_init) (void); + int (*mod_destroy) (void); +#define HELPER_NAME_MAXLEN 16 + char h_name[HELPER_NAME_MAXLEN]; + uma_zone_t h_zone; + struct hookinfo *h_hooks; + uint32_t h_nhooks; + uint32_t h_classes; + int32_t h_id; + volatile uint32_t h_refcount; + uint16_t h_flags; + TAILQ_ENTRY(helper) h_next; +}; + +struct khelp_modevent_data { + char name[HELPER_NAME_MAXLEN]; + struct helper *helper; + struct hookinfo *hooks; + int nhooks; + int uma_zsize; + uma_ctor umactor; + uma_dtor umadtor; +}; + +#define KHELP_DECLARE_MOD(hname, hdata, hhooks, version) \ + static struct khelp_modevent_data kmd_##hname = { \ + .name = #hname, \ + .helper = hdata \ + .hooks = hhooks, \ + .nhooks = sizeof(hhooks) / sizeof(hhooks[0]), \ + }; \ + static moduledata_t h_##hname = { \ + .name = #hname, \ + .evhand = khelp_modevent, \ + .priv = &kmd_##hname \ + }; \ + DECLARE_MODULE(hname, h_##hname, SI_SUB_PROTO_IFATTACHDOMAIN, \ + SI_ORDER_ANY); \ + MODULE_VERSION(hname, version) + +#define KHELP_DECLARE_MOD_UMA(hname, hdata, hhooks, version, size, ctor, dtor) \ + static struct khelp_modevent_data kmd_##hname = { \ + .name = #hname, \ + .helper = hdata, \ + .hooks = hhooks, \ + .nhooks = sizeof(hhooks) / sizeof(hhooks[0]), \ + .uma_zsize = size, \ + .umactor = ctor, \ + .umadtor = dtor \ + }; \ + static moduledata_t h_##hname = { \ + .name = #hname, \ + .evhand = khelp_modevent, \ + .priv = &kmd_##hname \ + }; \ + DECLARE_MODULE(hname, h_##hname, SI_SUB_PROTO_IFATTACHDOMAIN, \ + SI_ORDER_ANY); \ + MODULE_VERSION(hname, version) + +int khelp_modevent(module_t mod, int type, void *data); + +#endif /* _SYS_MODULE_KHELP_H_ */ diff --git a/sys/sys/osd.h b/sys/sys/osd.h index 63d536fa495b..14316ae30cdb 100644 --- a/sys/sys/osd.h +++ b/sys/sys/osd.h @@ -46,9 +46,10 @@ struct osd { #define OSD_THREAD 0 #define OSD_JAIL 1 +#define OSD_KHELP 2 #define OSD_FIRST OSD_THREAD -#define OSD_LAST OSD_JAIL +#define OSD_LAST OSD_KHELP typedef void (*osd_destructor_t)(void *value); typedef int (*osd_method_t)(void *obj, void *data); diff --git a/sys/sys/param.h b/sys/sys/param.h index 3c796e232756..29114d301635 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 900027 /* Master, propagated to newvers */ +#define __FreeBSD_version 900028 /* Master, propagated to newvers */ #ifdef _KERNEL #define P_OSREL_SIGSEGV 700004