Add a private KPI between hhook and khelp that allows khelp modules to insert

hook functions into hhook points which register after the modules were loaded -
potentially useful during boot or if hhook points are dynamically registered.

MFC after:	1 week
This commit is contained in:
Lawrence Stewart 2013-06-15 05:57:29 +00:00
parent 127a9483ed
commit 58261d30e1
2 changed files with 40 additions and 101 deletions

View File

@ -74,6 +74,7 @@ static uint32_t n_hhookheads;
/* Private function prototypes. */
static void hhook_head_destroy(struct hhook_head *hhh);
void khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags);
#define HHHLIST_LOCK() mtx_lock(&hhook_head_list_lock)
#define HHHLIST_UNLOCK() mtx_unlock(&hhook_head_list_lock)
@ -311,12 +312,7 @@ hhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hh
tmphhh->hhh_nhooks = 0;
STAILQ_INIT(&tmphhh->hhh_hooks);
HHH_LOCK_INIT(tmphhh);
if (hhh != NULL) {
refcount_init(&tmphhh->hhh_refcount, 1);
*hhh = tmphhh;
} else
refcount_init(&tmphhh->hhh_refcount, 0);
refcount_init(&tmphhh->hhh_refcount, 1);
HHHLIST_LOCK();
if (flags & HHOOK_HEADISINVNET) {
@ -331,6 +327,13 @@ hhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hh
n_hhookheads++;
HHHLIST_UNLOCK();
khelp_new_hhook_registered(tmphhh, flags);
if (hhh != NULL)
*hhh = tmphhh;
else
refcount_release(&tmphhh->hhh_refcount);
return (0);
}

View File

@ -40,7 +40,6 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/hhook.h>
#include <sys/jail.h>
#include <sys/khelp.h>
#include <sys/lock.h>
#include <sys/malloc.h>
@ -52,8 +51,6 @@ __FBSDID("$FreeBSD$");
#include <sys/rwlock.h>
#include <sys/systm.h>
#include <net/vnet.h>
static struct rwlock khelp_list_lock;
RW_SYSINIT(khelplistlock, &khelp_list_lock, "helper list lock");
@ -61,6 +58,7 @@ 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);
void khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags);
#define KHELP_LIST_WLOCK() rw_wlock(&khelp_list_lock)
#define KHELP_LIST_WUNLOCK() rw_wunlock(&khelp_list_lock)
@ -289,6 +287,36 @@ khelp_remove_hhook(struct hookinfo *hki)
return (error);
}
/*
* Private KPI between hhook and khelp that allows khelp modules to insert hook
* functions into hhook points which register after the modules were loaded.
*/
void
khelp_new_hhook_registered(struct hhook_head *hhh, uint32_t flags)
{
struct helper *h;
int error, i;
KHELP_LIST_RLOCK();
TAILQ_FOREACH(h, &helpers, h_next) {
for (i = 0; i < h->h_nhooks; i++) {
if (hhh->hhh_type != h->h_hooks[i].hook_type ||
hhh->hhh_id != h->h_hooks[i].hook_id)
continue;
error = hhook_add_hook(hhh, &h->h_hooks[i], flags);
if (error) {
printf("%s: \"%s\" khelp module unable to "
"hook type %d id %d due to error %d\n",
__func__, h->h_name,
h->h_hooks[i].hook_type,
h->h_hooks[i].hook_id, error);
error = 0;
}
}
}
KHELP_LIST_RUNLOCK();
}
int
khelp_modevent(module_t mod, int event_type, void *data)
{
@ -348,95 +376,3 @@ khelp_modevent(module_t mod, int event_type, void *data)
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);