|
| 1 | +#ifndef __THRD_RCU_H__ |
| 2 | +#define __THRD_RCU_H__ |
| 3 | + |
| 4 | +/* lock primitives from pthread and compiler primitives */ |
| 5 | + |
| 6 | +#include <pthread.h> |
| 7 | + |
| 8 | +typedef pthread_mutex_t spinlock_t; |
| 9 | + |
| 10 | +#define SPINLOCK_INIT PTHREAD_MUTEX_INITIALIZER |
| 11 | + |
| 12 | +static __inline__ void spin_lock(spinlock_t *sp) |
| 13 | +{ |
| 14 | + int ret; |
| 15 | + |
| 16 | + ret = pthread_mutex_lock(sp); |
| 17 | + if (ret != 0) { |
| 18 | + fprintf(stderr, "spin_lock:pthread_mutex_lock %d\n", ret); |
| 19 | + abort(); |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +static __inline__ void spin_unlock(spinlock_t *sp) |
| 24 | +{ |
| 25 | + int ret; |
| 26 | + |
| 27 | + ret = pthread_mutex_unlock(sp); |
| 28 | + if (ret != 0) { |
| 29 | + fprintf(stderr, "spin_unlock:pthread_mutex_unlock %d\n", ret); |
| 30 | + abort(); |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +#define current_tid() (unsigned int) pthread_self() |
| 35 | + |
| 36 | +#define ACCESS_ONCE(x) (*(volatile __typeof__(x) *) &(x)) |
| 37 | +#define READ_ONCE(x) \ |
| 38 | + ({ \ |
| 39 | + __typeof__(x) ___x = ACCESS_ONCE(x); \ |
| 40 | + ___x; \ |
| 41 | + }) |
| 42 | +#define WRITE_ONCE(x, val) \ |
| 43 | + do { \ |
| 44 | + ACCESS_ONCE(x) = (val); \ |
| 45 | + } while (0) |
| 46 | +#define barrier() __asm__ __volatile__("" : : : "memory") |
| 47 | +#define __allow_unused __attribute__((unused)) |
| 48 | +#define smp_mb() __asm__ __volatile__("mfence" : : : "memory") |
| 49 | + |
| 50 | +#include <errno.h> |
| 51 | +#include <stdio.h> |
| 52 | +#include <stdlib.h> |
| 53 | +#include <unistd.h> |
| 54 | + |
| 55 | +#ifdef __CHECKER__ |
| 56 | +#define __rcu __attribute__((noderef, address_space(__rcu))) |
| 57 | +#define rcu_check_sparse(p, space) ((void) (((typeof(*p) space *) p) == p)) |
| 58 | +#define __force __attribute__((force)) |
| 59 | +#define rcu_uncheck(p) ((__typeof__(*p) __force *) p) |
| 60 | +#define rcu_check(p) ((__typeof__(*p) __force __rcu *) p) |
| 61 | +#else |
| 62 | +#define __rcu |
| 63 | +#define rcu_check_sparse(p, space) |
| 64 | +#define __force |
| 65 | +#define rcu_uncheck(p) p |
| 66 | +#define rcu_check(p) p |
| 67 | +#endif /* __CHECKER__ */ |
| 68 | + |
| 69 | +/* Avoid false sharing */ |
| 70 | +#define __rcu_aligned __attribute__((aligned(128))) |
| 71 | + |
| 72 | +struct rcu_node { |
| 73 | + unsigned int tid; |
| 74 | + int rcu_nesting[2]; |
| 75 | + struct rcu_node *next; |
| 76 | +} __rcu_aligned; |
| 77 | + |
| 78 | +struct rcu_data { |
| 79 | + unsigned int nr_thread; |
| 80 | + struct rcu_node *head; |
| 81 | + unsigned int rcu_thrd_nesting_idx; |
| 82 | + spinlock_t sp; |
| 83 | +}; |
| 84 | + |
| 85 | +/* Easy to use */ |
| 86 | +#define __rcu_thrd_idx rcu_data.rcu_thrd_nesting_idx |
| 87 | +#define __rcu_thrd_nesting(ptr) \ |
| 88 | + ptr->rcu_nesting[READ_ONCE(__rcu_thrd_idx) & 0x01] |
| 89 | +#define rcu_thrd_nesting __rcu_thrd_nesting(__rcu_per_thrd_ptr) |
| 90 | + |
| 91 | +static struct rcu_data rcu_data = {.nr_thread = 0, |
| 92 | + .head = NULL, |
| 93 | + .rcu_thrd_nesting_idx = 0, |
| 94 | + .sp = SPINLOCK_INIT}; |
| 95 | +static __thread struct rcu_node *__rcu_per_thrd_ptr; |
| 96 | + |
| 97 | +static __inline__ struct rcu_node *__rcu_node_add(unsigned int tid) |
| 98 | +{ |
| 99 | + struct rcu_node **indirect = &rcu_data.head; |
| 100 | + struct rcu_node *node; |
| 101 | + |
| 102 | + node = (struct rcu_node *) malloc(sizeof(struct rcu_node)); |
| 103 | + if (!node) { |
| 104 | + fprintf(stderr, "__rcu_node_add: malloc failed\n"); |
| 105 | + abort(); |
| 106 | + } |
| 107 | + |
| 108 | + node->tid = tid; |
| 109 | + node->rcu_nesting[0] = 0; |
| 110 | + node->rcu_nesting[1] = 0; |
| 111 | + node->next = NULL; |
| 112 | + |
| 113 | + spin_lock(&rcu_data.sp); |
| 114 | + |
| 115 | + while (*indirect) { |
| 116 | + if ((*indirect)->tid == node->tid) { |
| 117 | + spin_unlock(&rcu_data.sp); |
| 118 | + free(node); |
| 119 | + return NULL; |
| 120 | + } |
| 121 | + indirect = &(*indirect)->next; |
| 122 | + } |
| 123 | + |
| 124 | + *indirect = node; |
| 125 | + rcu_data.nr_thread++; |
| 126 | + |
| 127 | + spin_unlock(&rcu_data.sp); |
| 128 | + |
| 129 | + smp_mb(); |
| 130 | + |
| 131 | + return node; |
| 132 | +} |
| 133 | + |
| 134 | +static __inline__ int rcu_init(void) |
| 135 | +{ |
| 136 | + unsigned int tid = current_tid(); |
| 137 | + |
| 138 | + __rcu_per_thrd_ptr = __rcu_node_add(tid); |
| 139 | + |
| 140 | + return (__rcu_per_thrd_ptr == NULL) ? -ENOMEM : 0; |
| 141 | +} |
| 142 | + |
| 143 | +static __inline__ void rcu_clean(void) |
| 144 | +{ |
| 145 | + struct rcu_node *node, *tmp; |
| 146 | + |
| 147 | + spin_lock(&rcu_data.sp); |
| 148 | + |
| 149 | + for (node = rcu_data.head; node != NULL; node = tmp) { |
| 150 | + tmp = node->next; |
| 151 | + free(node); |
| 152 | + } |
| 153 | + |
| 154 | + rcu_data.head = NULL; |
| 155 | + rcu_data.nr_thread = 0; |
| 156 | + |
| 157 | + spin_unlock(&rcu_data.sp); |
| 158 | +} |
| 159 | + |
| 160 | +/* The per-thread reference count will only modified by their owner |
| 161 | + * thread but will read by other threads. So here we use WRITE_ONCE(). |
| 162 | + */ |
| 163 | +static __inline__ void rcu_read_lock(void) |
| 164 | +{ |
| 165 | + WRITE_ONCE(rcu_thrd_nesting, 1); |
| 166 | +} |
| 167 | + |
| 168 | +static __inline__ void rcu_read_unlock(void) |
| 169 | +{ |
| 170 | + WRITE_ONCE(rcu_thrd_nesting, 0); |
| 171 | +} |
| 172 | + |
| 173 | +static __inline__ void synchronize_rcu(void) |
| 174 | +{ |
| 175 | + struct rcu_node *node; |
| 176 | + |
| 177 | + smp_mb(); |
| 178 | + |
| 179 | + spin_lock(&rcu_data.sp); |
| 180 | + |
| 181 | + /* When the rcu_thrd_nesting is odd, which means that the LSB set 1, |
| 182 | + * that thread is in the read-side critical section. Also, we need |
| 183 | + * to skip the read side when it is in the new grace period. |
| 184 | + */ |
| 185 | + for (node = rcu_data.head; node != NULL; node = node->next) { |
| 186 | + while (READ_ONCE(__rcu_thrd_nesting(node)) & 0x1) { |
| 187 | + barrier(); |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + /* Going to next grace period */ |
| 192 | + __atomic_fetch_add(&__rcu_thrd_idx, 1, __ATOMIC_RELEASE); |
| 193 | + |
| 194 | + spin_unlock(&rcu_data.sp); |
| 195 | + |
| 196 | + smp_mb(); |
| 197 | +} |
| 198 | + |
| 199 | +#define rcu_dereference(p) \ |
| 200 | + ({ \ |
| 201 | + __typeof__(*p) *__r_d_p = (__typeof__(*p) __force *) READ_ONCE(p); \ |
| 202 | + rcu_check_sparse(p, __rcu); \ |
| 203 | + __r_d_p; \ |
| 204 | + }) |
| 205 | + |
| 206 | +#define rcu_assign_pointer(p, v) \ |
| 207 | + ({ \ |
| 208 | + __typeof__(*p) *__r_a_p = \ |
| 209 | + (__typeof__(*p) __force *) __atomic_exchange_n( \ |
| 210 | + &(p), (__typeof__(*(p)) __force __rcu *) v, __ATOMIC_RELEASE); \ |
| 211 | + rcu_check_sparse(p, __rcu); \ |
| 212 | + __r_a_p; \ |
| 213 | + }) |
| 214 | + |
| 215 | +#endif /* __THRD_RCU_H__ */ |
0 commit comments