diff -r 1e6bd7b6001e sys/conf/files --- a/sys/conf/files Sun Dec 12 00:55:32 2010 +1100 +++ b/sys/conf/files Sun Dec 12 01:00:12 2010 +1100 @@ -2137,6 +2137,7 @@ 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 diff -r 1e6bd7b6001e sys/kern/kern_hhook.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/kern/kern_hhook.c Sun Dec 12 01:00:12 2010 +1100 @@ -0,0 +1,370 @@ +/*- + * 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); + +#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)) + +/* + * Remove a helper hook function from a helper hook point. + */ +int +hhook_remove_hook(struct hhook_head *hhh, hhook_func_t hook, void *udata) +{ + struct hhook *tmp; + + if (hhh == NULL) + return (ENOENT); + + HHH_WLOCK(hhh); + STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) { + if (tmp->hhk_func == hook && tmp->hhk_udata == 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(int hhook_type, int hhook_id, hhook_func_t hook, + void *udata) +{ + struct hhook_head *hhh; + + hhh = hhook_head_get(hhook_type, hhook_id); + + if (hhh == NULL) + return (ENOENT); + + hhook_remove_hook(hhh, hook, udata); + hhook_head_release(hhh); + + return (0); +} + +/* + * Remove a helper hook point. + */ +int +hhook_head_deregister(struct hhook_head *hhh) +{ + struct hhook *tmp, *tmp2; + int error; + + error = 0; + + HHHLIST_LOCK(); + if (hhh == NULL) + error = ENOENT; + else if (hhh->hhh_refcount > 1) + error = EBUSY; + else { + 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); + } + HHHLIST_UNLOCK(); + + return (error); +} + +/* + * Remove a helper hook point via a hhook_head lookup. + */ +int +hhook_head_deregister_lookup(int hhook_type, int 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(int hhook_type, int hhook_id) +{ + struct hhook_head *hhh; + + 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); +} + +/* + * Register a new helper hook function with a helper hook point. + */ +int +hhook_add_hook(struct hhook_head *hhh, struct helper *helper, + hhook_func_t hook, void *udata, 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 = helper; + hhk->hhk_func = hook; + hhk->hhk_udata = udata; + + HHH_WLOCK(hhh); + STAILQ_FOREACH(tmp, &hhh->hhh_hooks, hhk_next) { + if (tmp->hhk_func == hook && tmp->hhk_udata == 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(int hhook_type, int hhook_id, struct helper *helper, + hhook_func_t hook, void *udata, uint32_t flags) +{ + struct hhook_head *hhh; + int error; + + hhh = hhook_head_get(hhook_type, hhook_id); + + if (hhh == NULL) + return (ENOENT); + + error = hhook_add_hook(hhh, helper, hook, udata, flags); + hhook_head_release(hhh); + + return (error); +} + +/* + * Register a new helper hook point. + */ +int +hhook_head_register(int hhook_type, int 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); + } + + 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); + + HHHLIST_LOCK(); + LIST_INSERT_HEAD(&V_hhook_head_list, tmphhh, hhh_next); + HHHLIST_UNLOCK(); + + *hhh = tmphhh; + + return (0); +} + +void +hhook_head_release(struct hhook_head *hhh) +{ + + refcount_release(&hhh->hhh_refcount); +} + +/* + * 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; + + hhk->hhk_func(hhh->hhh_type, hhh->hhh_id, hhk->hhk_udata, + ctx_data, hdata, hosd); + } + HHH_RUNLOCK(hhh, &rmpt); +} + +/* + * Vnet coming online. + */ +static int +hhook_vnet_init(const void *unused) +{ + + LIST_INIT(&V_hhook_head_list); + + return (0); +} + +/* + * Vnet going offline. + */ +static int +hhook_vnet_uninit(const void *unused) +{ + + /* XXXLAS: Should we free all hhook related memory here? */ + return (0); +} + +VNET_SYSINIT(hhook_vnet_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, + hhook_vnet_init, NULL); + +VNET_SYSUNINIT(hhook_vnet_uninit, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, + hhook_vnet_uninit, NULL); diff -r 1e6bd7b6001e sys/sys/hhook.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/sys/hhook.h Sun Dec 12 01:00:12 2010 +1100 @@ -0,0 +1,127 @@ +/*- + * 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 + +/* Flags for register_* functions. */ +#define HHOOK_WAITOK 0x01 +#define HHOOK_NOWAIT 0x02 + +/* Helper hook types. */ +#define HHOOK_TYPE_TCP 1 + +struct helper; +struct osd; + +/* + * Ideally this would be private but we need access to the hhh_nhooks member + * variable in order to make the RUN_HHOOKS_IF() macro be useful. + */ +struct hhook_head { + STAILQ_HEAD(hhook_list, hhook) hhh_hooks; + struct rmlock hhh_lock; + int hhh_id; + int hhh_nhooks; + int hhh_type; + volatile uint32_t hhh_refcount; + LIST_ENTRY(hhook_head) hhh_next; +}; + +/* Signature for helper hook functions. */ +typedef void (*hhook_func_t)(int hhook_type, int hhook_id, void *udata, + void *ctx_data, void *hdata, struct osd *hosd); + +/* Public KPI functions. */ +int hhook_remove_hook(struct hhook_head *hhh, hhook_func_t hook, + void *udata); +int hhook_remove_hook_lookup(int hhook_type, int hhook_id, + hhook_func_t hook, void *udata); +int hhook_head_deregister(struct hhook_head *hhh); +int hhook_head_deregister_lookup(int hhook_type, int hhook_id); +struct hhook_head * hhook_head_get(int hhook_type, int hhook_id); +int hhook_add_hook(struct hhook_head *hhh, struct helper *helper, + hhook_func_t hook, void *udata, uint32_t flags); +int hhook_add_hook_lookup(int hhook_type, int hhook_id, + struct helper *helper, hhook_func_t hook, void *udata, uint32_t flags); +int hhook_head_register(int hhook_type, int hhook_id, + struct hhook_head **hhh, uint32_t flags); +void hhook_head_release(struct hhook_head *hhh); +void hhook_run_hooks(struct hhook_head *hhh, void *ctx_data, struct osd *hosd); + +/* + * 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 RUN_HHOOKS_IF(hhh, ctx_data, hosd) do { \ + if (hhh != NULL && hhh->hhh_nhooks > 0) \ + hhook_run_hooks(hhh, ctx_data, hosd); \ +} while (0) + +/* + * A similar wrapper to RUN_HHOOKS_IF() for situations where the caller prefers + * not to lookup and store the appropriate hhook_head pointer themselves. This + * macro should only be used in code paths that execute infrequently, otherwise + * the refcounting overhead would be excessive. + */ +#define RUN_HHOOKS_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_ */