diff options
author | Tejun Heo <tj@kernel.org> | 2015-01-06 10:26:10 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2015-01-06 10:26:10 -0500 |
commit | 6810e4a394f9d781050107529b8d1465c00b7b13 (patch) | |
tree | bff56a1838d3c0d2e7d2e89a109ba3b739b9f666 | |
parent | 97bf6af1f928216fd6c5a66e8a57bfa95a659672 (diff) |
percpu_ref: remove unnecessary ACCESS_ONCE() in percpu_ref_tryget_live()
__ref_is_percpu() needs the implied ACCESS_ONCE() in
lockless_dereference() on @ref->percpu_count_ptr because the value is
tested for !__PERCPU_REF_ATOMIC, which may be set asynchronously, and
then used as a pointer. If the compiler generates a separate fetch
when using it as a pointer, __PERCPU_REF_ATOMIC may be set in between
contaminating the pointer value.
percpu_ref_tryget_live() also uses ACCESS_ONCE() to test
__PERCPU_REF_DEAD; however, there's no reason for this. I just copied
ACCESS_ONCE() usage blindly from __ref_is_percpu(). All it does is
confusing people trying to understand what's going on.
This patch removes the unnecessary ACCESS_ONCE() usage from
percpu_ref_tryget_live() and adds a comment explaining why
__ref_is_percpu() needs it.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Kent Overstreet <kmo@daterainc.com>
-rw-r--r-- | include/linux/percpu-refcount.h | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index b4337646388b..6a7a670366ab 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h | |||
@@ -128,8 +128,22 @@ static inline void percpu_ref_kill(struct percpu_ref *ref) | |||
128 | static inline bool __ref_is_percpu(struct percpu_ref *ref, | 128 | static inline bool __ref_is_percpu(struct percpu_ref *ref, |
129 | unsigned long __percpu **percpu_countp) | 129 | unsigned long __percpu **percpu_countp) |
130 | { | 130 | { |
131 | /* paired with smp_store_release() in percpu_ref_reinit() */ | 131 | unsigned long percpu_ptr; |
132 | unsigned long percpu_ptr = lockless_dereference(ref->percpu_count_ptr); | 132 | |
133 | /* | ||
134 | * The value of @ref->percpu_count_ptr is tested for | ||
135 | * !__PERCPU_REF_ATOMIC, which may be set asynchronously, and then | ||
136 | * used as a pointer. If the compiler generates a separate fetch | ||
137 | * when using it as a pointer, __PERCPU_REF_ATOMIC may be set in | ||
138 | * between contaminating the pointer value, meaning that | ||
139 | * ACCESS_ONCE() is required when fetching it. | ||
140 | * | ||
141 | * Also, we need a data dependency barrier to be paired with | ||
142 | * smp_store_release() in __percpu_ref_switch_to_percpu(). | ||
143 | * | ||
144 | * Use lockless deref which contains both. | ||
145 | */ | ||
146 | percpu_ptr = lockless_dereference(ref->percpu_count_ptr); | ||
133 | 147 | ||
134 | /* | 148 | /* |
135 | * Theoretically, the following could test just ATOMIC; however, | 149 | * Theoretically, the following could test just ATOMIC; however, |
@@ -233,7 +247,7 @@ static inline bool percpu_ref_tryget_live(struct percpu_ref *ref) | |||
233 | if (__ref_is_percpu(ref, &percpu_count)) { | 247 | if (__ref_is_percpu(ref, &percpu_count)) { |
234 | this_cpu_inc(*percpu_count); | 248 | this_cpu_inc(*percpu_count); |
235 | ret = true; | 249 | ret = true; |
236 | } else if (!(ACCESS_ONCE(ref->percpu_count_ptr) & __PERCPU_REF_DEAD)) { | 250 | } else if (!(ref->percpu_count_ptr & __PERCPU_REF_DEAD)) { |
237 | ret = atomic_long_inc_not_zero(&ref->count); | 251 | ret = atomic_long_inc_not_zero(&ref->count); |
238 | } | 252 | } |
239 | 253 | ||