1/* @title: RCU */
2#pragma once
3#include <atomic.h>
4#include <compiler/core.h>
5#include <stdbool.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <structures/list.h>
9#include <tsa.h>
10
11struct thread;
12
13/* Forward declaration so we can put this pointer in threads */
14struct rcu_node;
15
16struct rcu_cb;
17typedef void (*rcu_fn)(struct rcu_cb *);
18
19struct rcu_cb {
20 struct list_node list;
21 rcu_fn fn;
22
23 /* TODO: as debug flags get better, move away from raw TEST_ENABLED */
24#ifdef TEST_ENABLED
25 size_t gen_when_called; /* Diagnostics */
26 size_t enqueued_waiting_on_gen;
27 size_t target_gen;
28#endif
29};
30#define rcu_cb_from_list_node(ln) (container_of(ln, struct rcu_cb, list))
31
32TSA_REENTRANT_CAPABILITY_DECLARE("rcu", RCU_READ_LOCKED);
33
34void rcu_init(void);
35
36void rcu_read_lock(void) TSA_ACQUIRES(RCU_READ_LOCKED);
37void rcu_read_unlock(void) TSA_RELEASES(RCU_READ_LOCKED);
38
39void rcu_synchronize(void);
40
41void rcu_defer(struct rcu_cb *cc_nonnull_ptr cb, rcu_fn cc_nonnull_ptr func);
42
43void rcu_note_context_switch(struct thread *outgoing);
44void rcu_note_irq_exit(void);
45
46void rcu_idle_enter(void);
47void rcu_idle_exit(void);
48
49#define rcu_plain_t(p) __typeof_unqual__(*(p)) *
50
51#define rcu_dereference(p) \
52 ((rcu_plain_t(p)) atomic_load_acq((_Atomic __typeof_unqual__(p) *) &(p)))
53
54#define rcu_assign_pointer(p, v) \
55 ({ \
56 rcu_plain_t(p) _v = (v); \
57 atomic_store_release((_Atomic __typeof_unqual__(p) *) &(p), \
58 (__typeof_unqual__(p)) _v); \
59 _v; \
60 })
61
62#define rcu_access_pointer(p) ((rcu_plain_t(p)) ca_read_once(p))
63#define RCU_INIT_POINTER(p, v) ((p) = (typeof(p)) (rcu_plain_t(p))(v))
64
65static inline cc_always_inline cc_unused void rcu_read_guard_exit(int *unused)
66 TSA_RELEASES(RCU_READ_LOCKED) TSA_NO_ANALYSIS {
67 cc_ignore(unused);
68 rcu_read_unlock();
69}
70
71static inline cc_always_inline cc_unused int rcu_read_guard_enter(void)
72 TSA_ACQUIRES(RCU_READ_LOCKED) TSA_NO_ANALYSIS {
73 rcu_read_lock();
74 return 0;
75}
76
77#define rcu_read_guard() \
78 cc_cleanup(rcu_read_guard_exit) int PP_CONCAT( \
79 rcu_read_guard_, __COUNTER__) = rcu_read_guard_enter()
80