aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_hhook.c91
-rw-r--r--sys/kern/kern_khelp.c51
-rw-r--r--sys/sys/hhook.h2
3 files changed, 86 insertions, 58 deletions
diff --git a/sys/kern/kern_hhook.c b/sys/kern/kern_hhook.c
index 63f09b22a6ff..9e3919ec987d 100644
--- a/sys/kern/kern_hhook.c
+++ b/sys/kern/kern_hhook.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010,2013 Lawrence Stewart <lstewart@freebsd.org>
* Copyright (c) 2010 The FreeBSD Foundation
* All rights reserved.
*
@@ -69,6 +69,9 @@ static struct mtx hhook_head_list_lock;
MTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock",
MTX_DEF);
+/* Protected by hhook_head_list_lock. */
+static uint32_t n_hhookheads;
+
/* Private function prototypes. */
static void hhook_head_destroy(struct hhook_head *hhh);
@@ -165,21 +168,71 @@ hhook_add_hook(struct hhook_head *hhh, struct hookinfo *hki, uint32_t flags)
}
/*
- * Lookup a helper hook point and register a new helper hook function with it.
+ * Register a helper hook function with a helper hook point (including all
+ * virtual instances of the hook point if it is virtualised).
+ *
+ * The logic is unfortunately far more complex than for
+ * hhook_remove_hook_lookup() because hhook_add_hook() can call malloc() with
+ * M_WAITOK and thus we cannot call hhook_add_hook() with the
+ * hhook_head_list_lock held.
+ *
+ * The logic assembles an array of hhook_head structs that correspond to the
+ * helper hook point being hooked and bumps the refcount on each (all done with
+ * the hhook_head_list_lock held). The hhook_head_list_lock is then dropped, and
+ * hhook_add_hook() is called and the refcount dropped for each hhook_head
+ * struct in the array.
*/
int
hhook_add_hook_lookup(struct hookinfo *hki, uint32_t flags)
{
- struct hhook_head *hhh;
- int error;
+ struct hhook_head **heads_to_hook, *hhh;
+ int error, i, n_heads_to_hook;
- hhh = hhook_head_get(hki->hook_type, hki->hook_id);
+tryagain:
+ error = i = 0;
+ /*
+ * Accessing n_hhookheads without hhook_head_list_lock held opens up a
+ * race with hhook_head_register() which we are unlikely to lose, but
+ * nonetheless have to cope with - hence the complex goto logic.
+ */
+ n_heads_to_hook = n_hhookheads;
+ heads_to_hook = malloc(n_heads_to_hook * sizeof(struct hhook_head *),
+ M_HHOOK, flags & HHOOK_WAITOK ? M_WAITOK : M_NOWAIT);
+ if (heads_to_hook == NULL)
+ return (ENOMEM);
- if (hhh == NULL)
- return (ENOENT);
+ HHHLIST_LOCK();
+ LIST_FOREACH(hhh, &hhook_head_list, hhh_next) {
+ if (hhh->hhh_type == hki->hook_type &&
+ hhh->hhh_id == hki->hook_id) {
+ if (i < n_heads_to_hook) {
+ heads_to_hook[i] = hhh;
+ refcount_acquire(&heads_to_hook[i]->hhh_refcount);
+ i++;
+ } else {
+ /*
+ * We raced with hhook_head_register() which
+ * inserted a hhook_head that we need to hook
+ * but did not malloc space for. Abort this run
+ * and try again.
+ */
+ for (i--; i >= 0; i--)
+ refcount_release(&heads_to_hook[i]->hhh_refcount);
+ free(heads_to_hook, M_HHOOK);
+ HHHLIST_UNLOCK();
+ goto tryagain;
+ }
+ }
+ }
+ HHHLIST_UNLOCK();
- error = hhook_add_hook(hhh, hki, flags);
- hhook_head_release(hhh);
+ for (i--; i >= 0; i--) {
+ if (!error)
+ error = hhook_add_hook(heads_to_hook[i], hki, flags);
+ refcount_release(&heads_to_hook[i]->hhh_refcount);
+ }
+
+ free(heads_to_hook, M_HHOOK);
return (error);
}
@@ -211,20 +264,21 @@ hhook_remove_hook(struct hhook_head *hhh, struct hookinfo *hki)
}
/*
- * Lookup a helper hook point and remove a helper hook function from it.
+ * Remove a helper hook function from a helper hook point (including all
+ * virtual instances of the hook point if it is virtualised).
*/
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);
+ HHHLIST_LOCK();
+ LIST_FOREACH(hhh, &hhook_head_list, hhh_next) {
+ if (hhh->hhh_type == hki->hook_type &&
+ hhh->hhh_id == hki->hook_id)
+ hhook_remove_hook(hhh, hki);
+ }
+ HHHLIST_UNLOCK();
return (0);
}
@@ -274,6 +328,7 @@ hhook_head_register(int32_t hhook_type, int32_t hhook_id, struct hhook_head **hh
#endif
}
LIST_INSERT_HEAD(&hhook_head_list, tmphhh, hhh_next);
+ n_hhookheads++;
HHHLIST_UNLOCK();
return (0);
@@ -285,6 +340,7 @@ hhook_head_destroy(struct hhook_head *hhh)
struct hhook *tmp, *tmp2;
HHHLIST_LOCK_ASSERT();
+ KASSERT(n_hhookheads > 0, ("n_hhookheads should be > 0"));
LIST_REMOVE(hhh, hhh_next);
#ifdef VIMAGE
@@ -297,6 +353,7 @@ hhook_head_destroy(struct hhook_head *hhh)
HHH_WUNLOCK(hhh);
HHH_LOCK_DESTROY(hhh);
free(hhh, M_HHOOK);
+ n_hhookheads--;
}
/*
diff --git a/sys/kern/kern_khelp.c b/sys/kern/kern_khelp.c
index 1d335b383cc1..fe7687e8bf24 100644
--- a/sys/kern/kern_khelp.c
+++ b/sys/kern/kern_khelp.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010,2013 Lawrence Stewart <lstewart@freebsd.org>
* Copyright (c) 2010 The FreeBSD Foundation
* All rights reserved.
*
@@ -84,12 +84,13 @@ khelp_register_helper(struct helper *h)
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);
+ error = hhook_add_hook_lookup(&h->h_hooks[i],
+ HHOOK_WAITOK);
}
if (error) {
for (i--; i >= 0; i--)
- khelp_remove_hhook(&h->h_hooks[i]);
+ hhook_remove_hook_lookup(&h->h_hooks[i]);
osd_deregister(OSD_KHELP, h->h_id);
}
@@ -144,7 +145,7 @@ khelp_deregister_helper(struct helper *h)
if (!error) {
if (h->h_nhooks > 0) {
for (i = 0; i < h->h_nhooks; i++)
- khelp_remove_hhook(&h->h_hooks[i]);
+ hhook_remove_hook_lookup(&h->h_hooks[i]);
}
osd_deregister(OSD_KHELP, h->h_id);
}
@@ -263,28 +264,13 @@ khelp_get_id(char *hname)
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.
+ * XXXLAS: Should probably include the functionality to update the
+ * helper's h_hooks struct member.
*/
-
- 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();
+ error = hhook_add_hook_lookup(hki, flags);
return (error);
}
@@ -292,28 +278,13 @@ khelp_add_hhook(struct hookinfo *hki, uint32_t flags)
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.
+ * XXXLAS: Should probably include the functionality to update the
+ * helper's h_hooks struct member.
*/
-
- 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();
+ error = hhook_remove_hook_lookup(hki);
return (error);
}
diff --git a/sys/sys/hhook.h b/sys/sys/hhook.h
index 096a5a0458f8..a54e980e1118 100644
--- a/sys/sys/hhook.h
+++ b/sys/sys/hhook.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * Copyright (c) 2010,2013 Lawrence Stewart <lstewart@freebsd.org>
* Copyright (c) 2010 The FreeBSD Foundation
* All rights reserved.
*