aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoqun Feng <boqun.feng@gmail.com>2024-04-03 07:28:26 -0700
committerBoqun Feng <boqun.feng@gmail.com>2024-04-04 09:30:36 -0700
commit809c307dd8a074d866ad97209209e7b8a4249861 (patch)
tree101ed9751cf571523c49a0a733f40f76b803065f
parent4cece764965020c22cff7665b18a012006359095 (diff)
downloadlinux-dev/hazptr.tar.gz
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
-rw-r--r--include/linux/hazptr.h48
-rw-r--r--kernel/rcu/Makefile1
-rw-r--r--kernel/rcu/hazptr.c49
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
+}