summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2025-01-18 12:45:46 -0500
committerTavian Barnes <tavianator@tavianator.com>2025-01-18 12:54:49 -0500
commit41f3283ec38e0cc27e350569bef3651e16447ce0 (patch)
treedef9ec554ad0bf9bd50d71c86bd31b6688f02a44 /src
parentd267ed57314a8bfa7efaf46e444c593f9d45bbc6 (diff)
downloadbfs-41f3283ec38e0cc27e350569bef3651e16447ce0.tar.xz
sighook: Convert siglist to a more general rcu_list type
Diffstat (limited to 'src')
-rw-r--r--src/sighook.c118
1 files changed, 66 insertions, 52 deletions
diff --git a/src/sighook.c b/src/sighook.c
index b3ac711..a1fc30d 100644
--- a/src/sighook.c
+++ b/src/sighook.c
@@ -249,64 +249,86 @@ static void *rcu_update(struct rcu *rcu, void *ptr) {
return rcu_decode(arc_wait(prev));
}
-struct sighook {
- /** The signal being hooked, or 0 for atsigexit(). */
- int sig;
- /** Signal hook flags. */
- enum sigflags flags;
- /** The function to call. */
- sighook_fn *fn;
- /** An argument to pass to the function. */
- void *arg;
- /** Flag for SH_ONESHOT. */
- atomic bool armed;
-
- /** The RCU pointer to this hook. */
- struct rcu *self;
- /** The next hook in the list. */
- struct rcu next;
-};
-
/**
- * An RCU-protected linked list of signal hooks.
+ * An RCU-protected linked list.
*/
-struct siglist {
- /** The first hook in the list. */
+struct rcu_list {
+ /** The first node in the list. */
struct rcu head;
/** &last->next */
struct rcu *tail;
};
-/** Initialize a siglist. */
-static void siglist_init(struct siglist *list) {
+/**
+ * An rcu_list node.
+ */
+struct rcu_node {
+ /** The RCU pointer to this node. */
+ struct rcu *self;
+ /** The next node in the list. */
+ struct rcu next;
+};
+
+/** Initialize an rcu_list. */
+static void rcu_list_init(struct rcu_list *list) {
rcu_init(&list->head, NULL);
list->tail = &list->head;
}
-/** Append a hook to a linked list. */
-static void sigpush(struct siglist *list, struct sighook *hook) {
- hook->self = list->tail;
- list->tail = &hook->next;
- rcu_init(&hook->next, NULL);
- rcu_update(hook->self, hook);
+/** Append to an rcu_list. */
+static void rcu_list_append(struct rcu_list *list, struct rcu_node *node) {
+ node->self = list->tail;
+ list->tail = &node->next;
+ rcu_init(&node->next, NULL);
+ rcu_update(node->self, node);
}
-/** Remove a hook from the linked list. */
-static void sigpop(struct siglist *list, struct sighook *hook) {
- struct sighook *next = rcu_peek(&hook->next);
- rcu_update(hook->self, next);
+/** Remove from an rcu_list. */
+static void rcu_list_remove(struct rcu_list *list, struct rcu_node *node) {
+ struct rcu_node *next = rcu_peek(&node->next);
+ rcu_update(node->self, next);
if (next) {
- next->self = hook->self;
+ next->self = node->self;
} else {
list->tail = &list->head;
}
+ rcu_destroy(&node->next);
}
+/** Iterate over an rcu_list. */
+#define for_rcu(type, node, list) \
+ for_rcu_(type, node, (list), node##_slot_, node##_prev_, node##_done_)
+
+#define for_rcu_(type, node, list, slot, prev, done) \
+ /* This outer loop is just for declaring variables; it iterates once. */ \
+ for (struct arc *slot, *prev, **done = NULL; !done && (done = &slot); ) \
+ for (type *node = rcu_read(&list->head, &slot); \
+ node || (arc_put(slot), false); \
+ (prev = slot, \
+ node = rcu_read(&((struct rcu_node *)node)->next, &slot), \
+ arc_put(prev)))
+
+struct sighook {
+ /** The RCU list node (must be the first field). */
+ struct rcu_node node;
+
+ /** The signal being hooked, or 0 for atsigexit(). */
+ int sig;
+ /** Signal hook flags. */
+ enum sigflags flags;
+ /** The function to call. */
+ sighook_fn *fn;
+ /** An argument to pass to the function. */
+ void *arg;
+ /** Flag for SH_ONESHOT. */
+ atomic bool armed;
+};
+
/** The lists of signal hooks. */
-static struct siglist sighooks[64];
+static struct rcu_list sighooks[64];
/** Get the hook list for a particular signal. */
-static struct siglist *siglist(int sig) {
+static struct rcu_list *siglist(int sig) {
return &sighooks[sig % countof(sighooks)];
}
@@ -442,23 +464,16 @@ static bool should_run(int sig, struct sighook *hook) {
}
/** Find any matching hooks and run them. */
-static enum sigflags run_hooks(struct siglist *list, int sig, siginfo_t *info) {
+static enum sigflags run_hooks(struct rcu_list *list, int sig, siginfo_t *info) {
enum sigflags ret = 0;
- struct arc *slot = NULL;
- struct sighook *hook = rcu_read(&list->head, &slot);
- while (hook) {
+ for_rcu (struct sighook, hook, list) {
if (should_run(sig, hook)) {
hook->fn(sig, info, hook->arg);
ret |= hook->flags;
}
-
- struct arc *prev = slot;
- hook = rcu_read(&hook->next, &slot);
- arc_put(prev);
}
- arc_put(slot);
return ret;
}
@@ -497,7 +512,7 @@ static void sigdispatch(int sig, siginfo_t *info, void *context) {
int error = errno;
// Run the normal hooks
- struct siglist *list = siglist(sig);
+ struct rcu_list *list = siglist(sig);
enum sigflags flags = run_hooks(list, sig, info);
// Run the atsigexit() hooks, if we're exiting
@@ -533,7 +548,7 @@ static int siginit(int sig) {
}
for (size_t i = 0; i < countof(sighooks); ++i) {
- siglist_init(&sighooks[i]);
+ rcu_list_init(&sighooks[i]);
}
initialized = true;
@@ -570,8 +585,8 @@ static struct sighook *sighook_impl(int sig, sighook_fn *fn, void *arg, enum sig
hook->arg = arg;
atomic_init(&hook->armed, true);
- struct siglist *list = siglist(sig);
- sigpush(list, hook);
+ struct rcu_list *list = siglist(sig);
+ rcu_list_append(list, &hook->node);
return hook;
}
@@ -617,11 +632,10 @@ void sigunhook(struct sighook *hook) {
mutex_lock(&sigmutex);
- struct siglist *list = siglist(hook->sig);
- sigpop(list, hook);
+ struct rcu_list *list = siglist(hook->sig);
+ rcu_list_remove(list, &hook->node);
mutex_unlock(&sigmutex);
- rcu_destroy(&hook->next);
free(hook);
}