diff options
Diffstat (limited to 'include/linux/percpu-refcount.h')
| -rw-r--r-- | include/linux/percpu-refcount.h | 64 |
1 files changed, 43 insertions, 21 deletions
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index 5d8920e23073..3dfbf237cd8f 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h | |||
| @@ -57,11 +57,9 @@ struct percpu_ref { | |||
| 57 | atomic_t count; | 57 | atomic_t count; |
| 58 | /* | 58 | /* |
| 59 | * The low bit of the pointer indicates whether the ref is in percpu | 59 | * The low bit of the pointer indicates whether the ref is in percpu |
| 60 | * mode; if set, then get/put will manipulate the atomic_t (this is a | 60 | * mode; if set, then get/put will manipulate the atomic_t. |
| 61 | * hack because we need to keep the pointer around for | ||
| 62 | * percpu_ref_kill_rcu()) | ||
| 63 | */ | 61 | */ |
| 64 | unsigned __percpu *pcpu_count; | 62 | unsigned long pcpu_count_ptr; |
| 65 | percpu_ref_func_t *release; | 63 | percpu_ref_func_t *release; |
| 66 | percpu_ref_func_t *confirm_kill; | 64 | percpu_ref_func_t *confirm_kill; |
| 67 | struct rcu_head rcu; | 65 | struct rcu_head rcu; |
| @@ -69,7 +67,8 @@ struct percpu_ref { | |||
| 69 | 67 | ||
| 70 | int __must_check percpu_ref_init(struct percpu_ref *ref, | 68 | int __must_check percpu_ref_init(struct percpu_ref *ref, |
| 71 | percpu_ref_func_t *release); | 69 | percpu_ref_func_t *release); |
| 72 | void percpu_ref_cancel_init(struct percpu_ref *ref); | 70 | void percpu_ref_reinit(struct percpu_ref *ref); |
| 71 | void percpu_ref_exit(struct percpu_ref *ref); | ||
| 73 | void percpu_ref_kill_and_confirm(struct percpu_ref *ref, | 72 | void percpu_ref_kill_and_confirm(struct percpu_ref *ref, |
| 74 | percpu_ref_func_t *confirm_kill); | 73 | percpu_ref_func_t *confirm_kill); |
| 75 | 74 | ||
| @@ -88,12 +87,28 @@ static inline void percpu_ref_kill(struct percpu_ref *ref) | |||
| 88 | return percpu_ref_kill_and_confirm(ref, NULL); | 87 | return percpu_ref_kill_and_confirm(ref, NULL); |
| 89 | } | 88 | } |
| 90 | 89 | ||
| 91 | #define PCPU_STATUS_BITS 2 | ||
| 92 | #define PCPU_STATUS_MASK ((1 << PCPU_STATUS_BITS) - 1) | ||
| 93 | #define PCPU_REF_PTR 0 | ||
| 94 | #define PCPU_REF_DEAD 1 | 90 | #define PCPU_REF_DEAD 1 |
| 95 | 91 | ||
| 96 | #define REF_STATUS(count) (((unsigned long) count) & PCPU_STATUS_MASK) | 92 | /* |
| 93 | * Internal helper. Don't use outside percpu-refcount proper. The | ||
| 94 | * function doesn't return the pointer and let the caller test it for NULL | ||
| 95 | * because doing so forces the compiler to generate two conditional | ||
| 96 | * branches as it can't assume that @ref->pcpu_count is not NULL. | ||
| 97 | */ | ||
| 98 | static inline bool __pcpu_ref_alive(struct percpu_ref *ref, | ||
| 99 | unsigned __percpu **pcpu_countp) | ||
| 100 | { | ||
| 101 | unsigned long pcpu_ptr = ACCESS_ONCE(ref->pcpu_count_ptr); | ||
| 102 | |||
| 103 | /* paired with smp_store_release() in percpu_ref_reinit() */ | ||
| 104 | smp_read_barrier_depends(); | ||
| 105 | |||
| 106 | if (unlikely(pcpu_ptr & PCPU_REF_DEAD)) | ||
| 107 | return false; | ||
| 108 | |||
| 109 | *pcpu_countp = (unsigned __percpu *)pcpu_ptr; | ||
| 110 | return true; | ||
| 111 | } | ||
| 97 | 112 | ||
| 98 | /** | 113 | /** |
| 99 | * percpu_ref_get - increment a percpu refcount | 114 | * percpu_ref_get - increment a percpu refcount |
| @@ -107,9 +122,7 @@ static inline void percpu_ref_get(struct percpu_ref *ref) | |||
| 107 | 122 | ||
| 108 | rcu_read_lock_sched(); | 123 | rcu_read_lock_sched(); |
| 109 | 124 | ||
| 110 | pcpu_count = ACCESS_ONCE(ref->pcpu_count); | 125 | if (__pcpu_ref_alive(ref, &pcpu_count)) |
| 111 | |||
| 112 | if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) | ||
| 113 | this_cpu_inc(*pcpu_count); | 126 | this_cpu_inc(*pcpu_count); |
| 114 | else | 127 | else |
| 115 | atomic_inc(&ref->count); | 128 | atomic_inc(&ref->count); |
| @@ -133,9 +146,7 @@ static inline bool percpu_ref_tryget(struct percpu_ref *ref) | |||
| 133 | 146 | ||
| 134 | rcu_read_lock_sched(); | 147 | rcu_read_lock_sched(); |
| 135 | 148 | ||
| 136 | pcpu_count = ACCESS_ONCE(ref->pcpu_count); | 149 | if (__pcpu_ref_alive(ref, &pcpu_count)) { |
| 137 | |||
| 138 | if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) { | ||
| 139 | this_cpu_inc(*pcpu_count); | 150 | this_cpu_inc(*pcpu_count); |
| 140 | ret = true; | 151 | ret = true; |
| 141 | } else { | 152 | } else { |
| @@ -168,9 +179,7 @@ static inline bool percpu_ref_tryget_live(struct percpu_ref *ref) | |||
| 168 | 179 | ||
| 169 | rcu_read_lock_sched(); | 180 | rcu_read_lock_sched(); |
| 170 | 181 | ||
| 171 | pcpu_count = ACCESS_ONCE(ref->pcpu_count); | 182 | if (__pcpu_ref_alive(ref, &pcpu_count)) { |
| 172 | |||
| 173 | if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) { | ||
| 174 | this_cpu_inc(*pcpu_count); | 183 | this_cpu_inc(*pcpu_count); |
| 175 | ret = true; | 184 | ret = true; |
| 176 | } | 185 | } |
| @@ -193,9 +202,7 @@ static inline void percpu_ref_put(struct percpu_ref *ref) | |||
| 193 | 202 | ||
| 194 | rcu_read_lock_sched(); | 203 | rcu_read_lock_sched(); |
| 195 | 204 | ||
| 196 | pcpu_count = ACCESS_ONCE(ref->pcpu_count); | 205 | if (__pcpu_ref_alive(ref, &pcpu_count)) |
| 197 | |||
| 198 | if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) | ||
| 199 | this_cpu_dec(*pcpu_count); | 206 | this_cpu_dec(*pcpu_count); |
| 200 | else if (unlikely(atomic_dec_and_test(&ref->count))) | 207 | else if (unlikely(atomic_dec_and_test(&ref->count))) |
| 201 | ref->release(ref); | 208 | ref->release(ref); |
| @@ -203,4 +210,19 @@ static inline void percpu_ref_put(struct percpu_ref *ref) | |||
| 203 | rcu_read_unlock_sched(); | 210 | rcu_read_unlock_sched(); |
| 204 | } | 211 | } |
| 205 | 212 | ||
| 213 | /** | ||
| 214 | * percpu_ref_is_zero - test whether a percpu refcount reached zero | ||
| 215 | * @ref: percpu_ref to test | ||
| 216 | * | ||
| 217 | * Returns %true if @ref reached zero. | ||
| 218 | */ | ||
| 219 | static inline bool percpu_ref_is_zero(struct percpu_ref *ref) | ||
| 220 | { | ||
| 221 | unsigned __percpu *pcpu_count; | ||
| 222 | |||
| 223 | if (__pcpu_ref_alive(ref, &pcpu_count)) | ||
| 224 | return false; | ||
| 225 | return !atomic_read(&ref->count); | ||
| 226 | } | ||
| 227 | |||
| 206 | #endif | 228 | #endif |
