diff options
author | Boqun Feng <boqun.feng@gmail.com> | 2024-04-03 07:28:26 -0700 |
---|---|---|
committer | Boqun Feng <boqun.feng@gmail.com> | 2024-04-04 09:30:36 -0700 |
commit | 809c307dd8a074d866ad97209209e7b8a4249861 (patch) | |
tree | 101ed9751cf571523c49a0a733f40f76b803065f | |
parent | 4cece764965020c22cff7665b18a012006359095 (diff) | |
download | linux-dev/hazptr.tar.gz |
WIIPdev/hazptr
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
-rw-r--r-- | include/linux/hazptr.h | 48 | ||||
-rw-r--r-- | kernel/rcu/Makefile | 1 | ||||
-rw-r--r-- | kernel/rcu/hazptr.c | 49 |
3 files changed, 98 insertions, 0 deletions
diff --git a/include/linux/hazptr.h b/include/linux/hazptr.h new file mode 100644 index 00000000000000..935eee97c95e6a --- /dev/null +++ b/include/linux/hazptr.h @@ -0,0 +1,48 @@ +#include <linux/list.h> + +typedef void* hazptr_t; + +#define HAZPTR_UNUSED (1ul) + +struct hazptr_context; + +struct hazptr_head { + struct rcu_head head; +}; + +void init_hazptr_context(struct hazptr_context *hzcp); +hazptr_t *hazptr_alloc(struct hazptr_context *hzcp); +void hazptr_free(struct hazptr_context *hzcp, hazptr_t *hzp); + +#define hazptr_tryprotect(hzp, p, field) __hazptr_tryprotect(hzp, p, offset_of(__typeof__(*p), field) + +static inline bool __hazptr_tryprotect(hazptr_t *hzp, void **p, unsigned long head_offset) +{ + void *ptr; + struct hazptr_head *head; + + ptr = READ_ONCE(*p); + + if (ptr == NULL) + return false; + + head = (struct hazptr_head *)ptr + head_offset; + + WRITE_ONCE(*hzp, head); + smp_mb(); + + ptr = READ_ONCE(*p); // read again + + if (ptr + head_offset != *hzp) { // pointer changed + WRITE_ONCE(*hzp, NULL); // reset hazard pointer + return false; + } else + return true; +} + +static inline void hazptr_clear(hazptr_t *hzp) +{ + WRITE_ONCE(*hzp, NULL); +} + +void call_hazptr(struct hazptr_head *head, rcu_callback_t func); diff --git a/kernel/rcu/Makefile b/kernel/rcu/Makefile index 0cfb009a99b9f7..53553aa4622f21 100644 --- a/kernel/rcu/Makefile +++ b/kernel/rcu/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_RCU_REF_SCALE_TEST) += refscale.o obj-$(CONFIG_TREE_RCU) += tree.o obj-$(CONFIG_TINY_RCU) += tiny.o obj-$(CONFIG_RCU_NEED_SEGCBLIST) += rcu_segcblist.o +obj-y += hazptr.o diff --git a/kernel/rcu/hazptr.c b/kernel/rcu/hazptr.c new file mode 100644 index 00000000000000..9b0e429aebddec --- /dev/null +++ b/kernel/rcu/hazptr.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/spinlock.h> +#include <linux/cleanup.h> +#include <linux/hazptr.h> + +struct hazptr_context { + struct list_head list; + hazptr_t start_with_one; +}; + +struct hazptr_context_list { + struct list_head list; + spinlock_t lock; +}; + +DEFINE_PER_CPU(struct hazptr_context_list, hzc_list); + +void init_hazptr_context(struct hazptr_context *hzcp) +{ + struct hazptr_context_list *this_hzc_list = this_cpu_ptr(&hzc_list); + + guard(spinlock)(&this_hzc_list->lock); + list_add(&hzcp->list, &this_hzc_list->list); +} + +hazptr_t *hazptr_alloc(struct hazptr_context *hzcp) +{ + if (((unsigned long)hzcp->start_with_one) != HAZPTR_UNUSED) { + WRITE_ONCE(hzcp->start_with_one, NULL); + return &hzcp->start_with_one; + } + + return NULL; +} + +void hazptr_free(struct hazptr_context *hzcp, hazptr_t *hzp) +{ + WARN_ON(((unsigned long)*hzp) == HAZPTR_UNUSED); + WARN_ON(&hzcp->start_with_one != hzp); + + WRITE_ONCE(*hzp, (void *)HAZPTR_UNUSED); +} + +void call_hazptr(struct hazptr_head *head, rcu_callback_t func) +{ + head->head.func = func; + // TODO +} |