.. | .. |
---|
1 | | -#define pr_fmt(fmt) "%s: " fmt "\n", __func__ |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
| 2 | +#define pr_fmt(fmt) "%s: " fmt, __func__ |
---|
2 | 3 | |
---|
3 | 4 | #include <linux/kernel.h> |
---|
4 | 5 | #include <linux/sched.h> |
---|
5 | 6 | #include <linux/wait.h> |
---|
| 7 | +#include <linux/slab.h> |
---|
6 | 8 | #include <linux/percpu-refcount.h> |
---|
7 | 9 | |
---|
8 | 10 | /* |
---|
.. | .. |
---|
49 | 51 | * @flags: PERCPU_REF_INIT_* flags |
---|
50 | 52 | * @gfp: allocation mask to use |
---|
51 | 53 | * |
---|
52 | | - * Initializes @ref. If @flags is zero, @ref starts in percpu mode with a |
---|
53 | | - * refcount of 1; analagous to atomic_long_set(ref, 1). See the |
---|
54 | | - * definitions of PERCPU_REF_INIT_* flags for flag behaviors. |
---|
| 54 | + * Initializes @ref. @ref starts out in percpu mode with a refcount of 1 unless |
---|
| 55 | + * @flags contains PERCPU_REF_INIT_ATOMIC or PERCPU_REF_INIT_DEAD. These flags |
---|
| 56 | + * change the start state to atomic with the latter setting the initial refcount |
---|
| 57 | + * to 0. See the definitions of PERCPU_REF_INIT_* flags for flag behaviors. |
---|
55 | 58 | * |
---|
56 | 59 | * Note that @release must not sleep - it may potentially be called from RCU |
---|
57 | 60 | * callback context by percpu_ref_kill(). |
---|
.. | .. |
---|
62 | 65 | size_t align = max_t(size_t, 1 << __PERCPU_REF_FLAG_BITS, |
---|
63 | 66 | __alignof__(unsigned long)); |
---|
64 | 67 | unsigned long start_count = 0; |
---|
| 68 | + struct percpu_ref_data *data; |
---|
65 | 69 | |
---|
66 | 70 | ref->percpu_count_ptr = (unsigned long) |
---|
67 | 71 | __alloc_percpu_gfp(sizeof(unsigned long), align, gfp); |
---|
68 | 72 | if (!ref->percpu_count_ptr) |
---|
69 | 73 | return -ENOMEM; |
---|
70 | 74 | |
---|
71 | | - ref->force_atomic = flags & PERCPU_REF_INIT_ATOMIC; |
---|
| 75 | + data = kzalloc(sizeof(*ref->data), gfp); |
---|
| 76 | + if (!data) { |
---|
| 77 | + free_percpu((void __percpu *)ref->percpu_count_ptr); |
---|
| 78 | + ref->percpu_count_ptr = 0; |
---|
| 79 | + return -ENOMEM; |
---|
| 80 | + } |
---|
72 | 81 | |
---|
73 | | - if (flags & (PERCPU_REF_INIT_ATOMIC | PERCPU_REF_INIT_DEAD)) |
---|
| 82 | + data->force_atomic = flags & PERCPU_REF_INIT_ATOMIC; |
---|
| 83 | + data->allow_reinit = flags & PERCPU_REF_ALLOW_REINIT; |
---|
| 84 | + |
---|
| 85 | + if (flags & (PERCPU_REF_INIT_ATOMIC | PERCPU_REF_INIT_DEAD)) { |
---|
74 | 86 | ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC; |
---|
75 | | - else |
---|
| 87 | + data->allow_reinit = true; |
---|
| 88 | + } else { |
---|
76 | 89 | start_count += PERCPU_COUNT_BIAS; |
---|
| 90 | + } |
---|
77 | 91 | |
---|
78 | 92 | if (flags & PERCPU_REF_INIT_DEAD) |
---|
79 | 93 | ref->percpu_count_ptr |= __PERCPU_REF_DEAD; |
---|
80 | 94 | else |
---|
81 | 95 | start_count++; |
---|
82 | 96 | |
---|
83 | | - atomic_long_set(&ref->count, start_count); |
---|
| 97 | + atomic_long_set(&data->count, start_count); |
---|
84 | 98 | |
---|
85 | | - ref->release = release; |
---|
86 | | - ref->confirm_switch = NULL; |
---|
| 99 | + data->release = release; |
---|
| 100 | + data->confirm_switch = NULL; |
---|
| 101 | + data->ref = ref; |
---|
| 102 | + ref->data = data; |
---|
87 | 103 | return 0; |
---|
88 | 104 | } |
---|
89 | 105 | EXPORT_SYMBOL_GPL(percpu_ref_init); |
---|
| 106 | + |
---|
| 107 | +static void __percpu_ref_exit(struct percpu_ref *ref) |
---|
| 108 | +{ |
---|
| 109 | + unsigned long __percpu *percpu_count = percpu_count_ptr(ref); |
---|
| 110 | + |
---|
| 111 | + if (percpu_count) { |
---|
| 112 | + /* non-NULL confirm_switch indicates switching in progress */ |
---|
| 113 | + WARN_ON_ONCE(ref->data && ref->data->confirm_switch); |
---|
| 114 | + free_percpu(percpu_count); |
---|
| 115 | + ref->percpu_count_ptr = __PERCPU_REF_ATOMIC_DEAD; |
---|
| 116 | + } |
---|
| 117 | +} |
---|
90 | 118 | |
---|
91 | 119 | /** |
---|
92 | 120 | * percpu_ref_exit - undo percpu_ref_init() |
---|
.. | .. |
---|
100 | 128 | */ |
---|
101 | 129 | void percpu_ref_exit(struct percpu_ref *ref) |
---|
102 | 130 | { |
---|
103 | | - unsigned long __percpu *percpu_count = percpu_count_ptr(ref); |
---|
| 131 | + struct percpu_ref_data *data = ref->data; |
---|
| 132 | + unsigned long flags; |
---|
104 | 133 | |
---|
105 | | - if (percpu_count) { |
---|
106 | | - /* non-NULL confirm_switch indicates switching in progress */ |
---|
107 | | - WARN_ON_ONCE(ref->confirm_switch); |
---|
108 | | - free_percpu(percpu_count); |
---|
109 | | - ref->percpu_count_ptr = __PERCPU_REF_ATOMIC_DEAD; |
---|
110 | | - } |
---|
| 134 | + __percpu_ref_exit(ref); |
---|
| 135 | + |
---|
| 136 | + if (!data) |
---|
| 137 | + return; |
---|
| 138 | + |
---|
| 139 | + spin_lock_irqsave(&percpu_ref_switch_lock, flags); |
---|
| 140 | + ref->percpu_count_ptr |= atomic_long_read(&ref->data->count) << |
---|
| 141 | + __PERCPU_REF_FLAG_BITS; |
---|
| 142 | + ref->data = NULL; |
---|
| 143 | + spin_unlock_irqrestore(&percpu_ref_switch_lock, flags); |
---|
| 144 | + |
---|
| 145 | + kfree(data); |
---|
111 | 146 | } |
---|
112 | 147 | EXPORT_SYMBOL_GPL(percpu_ref_exit); |
---|
113 | 148 | |
---|
114 | 149 | static void percpu_ref_call_confirm_rcu(struct rcu_head *rcu) |
---|
115 | 150 | { |
---|
116 | | - struct percpu_ref *ref = container_of(rcu, struct percpu_ref, rcu); |
---|
| 151 | + struct percpu_ref_data *data = container_of(rcu, |
---|
| 152 | + struct percpu_ref_data, rcu); |
---|
| 153 | + struct percpu_ref *ref = data->ref; |
---|
117 | 154 | |
---|
118 | | - ref->confirm_switch(ref); |
---|
119 | | - ref->confirm_switch = NULL; |
---|
| 155 | + data->confirm_switch(ref); |
---|
| 156 | + data->confirm_switch = NULL; |
---|
120 | 157 | wake_up_all(&percpu_ref_switch_waitq); |
---|
| 158 | + |
---|
| 159 | + if (!data->allow_reinit) |
---|
| 160 | + __percpu_ref_exit(ref); |
---|
121 | 161 | |
---|
122 | 162 | /* drop ref from percpu_ref_switch_to_atomic() */ |
---|
123 | 163 | percpu_ref_put(ref); |
---|
.. | .. |
---|
125 | 165 | |
---|
126 | 166 | static void percpu_ref_switch_to_atomic_rcu(struct rcu_head *rcu) |
---|
127 | 167 | { |
---|
128 | | - struct percpu_ref *ref = container_of(rcu, struct percpu_ref, rcu); |
---|
| 168 | + struct percpu_ref_data *data = container_of(rcu, |
---|
| 169 | + struct percpu_ref_data, rcu); |
---|
| 170 | + struct percpu_ref *ref = data->ref; |
---|
129 | 171 | unsigned long __percpu *percpu_count = percpu_count_ptr(ref); |
---|
130 | 172 | unsigned long count = 0; |
---|
131 | 173 | int cpu; |
---|
.. | .. |
---|
133 | 175 | for_each_possible_cpu(cpu) |
---|
134 | 176 | count += *per_cpu_ptr(percpu_count, cpu); |
---|
135 | 177 | |
---|
136 | | - pr_debug("global %ld percpu %ld", |
---|
137 | | - atomic_long_read(&ref->count), (long)count); |
---|
| 178 | + pr_debug("global %lu percpu %lu\n", |
---|
| 179 | + atomic_long_read(&data->count), count); |
---|
138 | 180 | |
---|
139 | 181 | /* |
---|
140 | 182 | * It's crucial that we sum the percpu counters _before_ adding the sum |
---|
.. | .. |
---|
148 | 190 | * reaching 0 before we add the percpu counts. But doing it at the same |
---|
149 | 191 | * time is equivalent and saves us atomic operations: |
---|
150 | 192 | */ |
---|
151 | | - atomic_long_add((long)count - PERCPU_COUNT_BIAS, &ref->count); |
---|
| 193 | + atomic_long_add((long)count - PERCPU_COUNT_BIAS, &data->count); |
---|
152 | 194 | |
---|
153 | | - WARN_ONCE(atomic_long_read(&ref->count) <= 0, |
---|
154 | | - "percpu ref (%pf) <= 0 (%ld) after switching to atomic", |
---|
155 | | - ref->release, atomic_long_read(&ref->count)); |
---|
| 195 | + WARN_ONCE(atomic_long_read(&data->count) <= 0, |
---|
| 196 | + "percpu ref (%ps) <= 0 (%ld) after switching to atomic", |
---|
| 197 | + data->release, atomic_long_read(&data->count)); |
---|
156 | 198 | |
---|
157 | 199 | /* @ref is viewed as dead on all CPUs, send out switch confirmation */ |
---|
158 | 200 | percpu_ref_call_confirm_rcu(rcu); |
---|
.. | .. |
---|
178 | 220 | * Non-NULL ->confirm_switch is used to indicate that switching is |
---|
179 | 221 | * in progress. Use noop one if unspecified. |
---|
180 | 222 | */ |
---|
181 | | - ref->confirm_switch = confirm_switch ?: percpu_ref_noop_confirm_switch; |
---|
| 223 | + ref->data->confirm_switch = confirm_switch ?: |
---|
| 224 | + percpu_ref_noop_confirm_switch; |
---|
182 | 225 | |
---|
183 | 226 | percpu_ref_get(ref); /* put after confirmation */ |
---|
184 | | - call_rcu_sched(&ref->rcu, percpu_ref_switch_to_atomic_rcu); |
---|
| 227 | + call_rcu(&ref->data->rcu, percpu_ref_switch_to_atomic_rcu); |
---|
185 | 228 | } |
---|
186 | 229 | |
---|
187 | 230 | static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref) |
---|
.. | .. |
---|
194 | 237 | if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC)) |
---|
195 | 238 | return; |
---|
196 | 239 | |
---|
197 | | - atomic_long_add(PERCPU_COUNT_BIAS, &ref->count); |
---|
| 240 | + if (WARN_ON_ONCE(!ref->data->allow_reinit)) |
---|
| 241 | + return; |
---|
| 242 | + |
---|
| 243 | + atomic_long_add(PERCPU_COUNT_BIAS, &ref->data->count); |
---|
198 | 244 | |
---|
199 | 245 | /* |
---|
200 | 246 | * Restore per-cpu operation. smp_store_release() is paired |
---|
.. | .. |
---|
212 | 258 | static void __percpu_ref_switch_mode(struct percpu_ref *ref, |
---|
213 | 259 | percpu_ref_func_t *confirm_switch) |
---|
214 | 260 | { |
---|
| 261 | + struct percpu_ref_data *data = ref->data; |
---|
| 262 | + |
---|
215 | 263 | lockdep_assert_held(&percpu_ref_switch_lock); |
---|
216 | 264 | |
---|
217 | 265 | /* |
---|
.. | .. |
---|
219 | 267 | * its completion. If the caller ensures that ATOMIC switching |
---|
220 | 268 | * isn't in progress, this function can be called from any context. |
---|
221 | 269 | */ |
---|
222 | | - wait_event_lock_irq(percpu_ref_switch_waitq, !ref->confirm_switch, |
---|
| 270 | + wait_event_lock_irq(percpu_ref_switch_waitq, !data->confirm_switch, |
---|
223 | 271 | percpu_ref_switch_lock); |
---|
224 | 272 | |
---|
225 | | - if (ref->force_atomic || (ref->percpu_count_ptr & __PERCPU_REF_DEAD)) |
---|
| 273 | + if (data->force_atomic || (ref->percpu_count_ptr & __PERCPU_REF_DEAD)) |
---|
226 | 274 | __percpu_ref_switch_to_atomic(ref, confirm_switch); |
---|
227 | 275 | else |
---|
228 | 276 | __percpu_ref_switch_to_percpu(ref); |
---|
.. | .. |
---|
255 | 303 | |
---|
256 | 304 | spin_lock_irqsave(&percpu_ref_switch_lock, flags); |
---|
257 | 305 | |
---|
258 | | - ref->force_atomic = true; |
---|
| 306 | + ref->data->force_atomic = true; |
---|
259 | 307 | __percpu_ref_switch_mode(ref, confirm_switch); |
---|
260 | 308 | |
---|
261 | 309 | spin_unlock_irqrestore(&percpu_ref_switch_lock, flags); |
---|
.. | .. |
---|
273 | 321 | void percpu_ref_switch_to_atomic_sync(struct percpu_ref *ref) |
---|
274 | 322 | { |
---|
275 | 323 | percpu_ref_switch_to_atomic(ref, NULL); |
---|
276 | | - wait_event(percpu_ref_switch_waitq, !ref->confirm_switch); |
---|
| 324 | + wait_event(percpu_ref_switch_waitq, !ref->data->confirm_switch); |
---|
277 | 325 | } |
---|
278 | 326 | EXPORT_SYMBOL_GPL(percpu_ref_switch_to_atomic_sync); |
---|
279 | 327 | |
---|
.. | .. |
---|
301 | 349 | |
---|
302 | 350 | spin_lock_irqsave(&percpu_ref_switch_lock, flags); |
---|
303 | 351 | |
---|
304 | | - ref->force_atomic = false; |
---|
| 352 | + ref->data->force_atomic = false; |
---|
305 | 353 | __percpu_ref_switch_mode(ref, NULL); |
---|
306 | 354 | |
---|
307 | 355 | spin_unlock_irqrestore(&percpu_ref_switch_lock, flags); |
---|
.. | .. |
---|
333 | 381 | spin_lock_irqsave(&percpu_ref_switch_lock, flags); |
---|
334 | 382 | |
---|
335 | 383 | WARN_ONCE(ref->percpu_count_ptr & __PERCPU_REF_DEAD, |
---|
336 | | - "%s called more than once on %pf!", __func__, ref->release); |
---|
| 384 | + "%s called more than once on %ps!", __func__, |
---|
| 385 | + ref->data->release); |
---|
337 | 386 | |
---|
338 | 387 | ref->percpu_count_ptr |= __PERCPU_REF_DEAD; |
---|
339 | 388 | __percpu_ref_switch_mode(ref, confirm_kill); |
---|
.. | .. |
---|
342 | 391 | spin_unlock_irqrestore(&percpu_ref_switch_lock, flags); |
---|
343 | 392 | } |
---|
344 | 393 | EXPORT_SYMBOL_GPL(percpu_ref_kill_and_confirm); |
---|
| 394 | + |
---|
| 395 | +/** |
---|
| 396 | + * percpu_ref_is_zero - test whether a percpu refcount reached zero |
---|
| 397 | + * @ref: percpu_ref to test |
---|
| 398 | + * |
---|
| 399 | + * Returns %true if @ref reached zero. |
---|
| 400 | + * |
---|
| 401 | + * This function is safe to call as long as @ref is between init and exit. |
---|
| 402 | + */ |
---|
| 403 | +bool percpu_ref_is_zero(struct percpu_ref *ref) |
---|
| 404 | +{ |
---|
| 405 | + unsigned long __percpu *percpu_count; |
---|
| 406 | + unsigned long count, flags; |
---|
| 407 | + |
---|
| 408 | + if (__ref_is_percpu(ref, &percpu_count)) |
---|
| 409 | + return false; |
---|
| 410 | + |
---|
| 411 | + /* protect us from being destroyed */ |
---|
| 412 | + spin_lock_irqsave(&percpu_ref_switch_lock, flags); |
---|
| 413 | + if (ref->data) |
---|
| 414 | + count = atomic_long_read(&ref->data->count); |
---|
| 415 | + else |
---|
| 416 | + count = ref->percpu_count_ptr >> __PERCPU_REF_FLAG_BITS; |
---|
| 417 | + spin_unlock_irqrestore(&percpu_ref_switch_lock, flags); |
---|
| 418 | + |
---|
| 419 | + return count == 0; |
---|
| 420 | +} |
---|
| 421 | +EXPORT_SYMBOL_GPL(percpu_ref_is_zero); |
---|
345 | 422 | |
---|
346 | 423 | /** |
---|
347 | 424 | * percpu_ref_reinit - re-initialize a percpu refcount |
---|
.. | .. |
---|
356 | 433 | */ |
---|
357 | 434 | void percpu_ref_reinit(struct percpu_ref *ref) |
---|
358 | 435 | { |
---|
| 436 | + WARN_ON_ONCE(!percpu_ref_is_zero(ref)); |
---|
| 437 | + |
---|
| 438 | + percpu_ref_resurrect(ref); |
---|
| 439 | +} |
---|
| 440 | +EXPORT_SYMBOL_GPL(percpu_ref_reinit); |
---|
| 441 | + |
---|
| 442 | +/** |
---|
| 443 | + * percpu_ref_resurrect - modify a percpu refcount from dead to live |
---|
| 444 | + * @ref: perpcu_ref to resurrect |
---|
| 445 | + * |
---|
| 446 | + * Modify @ref so that it's in the same state as before percpu_ref_kill() was |
---|
| 447 | + * called. @ref must be dead but must not yet have exited. |
---|
| 448 | + * |
---|
| 449 | + * If @ref->release() frees @ref then the caller is responsible for |
---|
| 450 | + * guaranteeing that @ref->release() does not get called while this |
---|
| 451 | + * function is in progress. |
---|
| 452 | + * |
---|
| 453 | + * Note that percpu_ref_tryget[_live]() are safe to perform on @ref while |
---|
| 454 | + * this function is in progress. |
---|
| 455 | + */ |
---|
| 456 | +void percpu_ref_resurrect(struct percpu_ref *ref) |
---|
| 457 | +{ |
---|
| 458 | + unsigned long __percpu *percpu_count; |
---|
359 | 459 | unsigned long flags; |
---|
360 | 460 | |
---|
361 | 461 | spin_lock_irqsave(&percpu_ref_switch_lock, flags); |
---|
362 | 462 | |
---|
363 | | - WARN_ON_ONCE(!percpu_ref_is_zero(ref)); |
---|
| 463 | + WARN_ON_ONCE(!(ref->percpu_count_ptr & __PERCPU_REF_DEAD)); |
---|
| 464 | + WARN_ON_ONCE(__ref_is_percpu(ref, &percpu_count)); |
---|
364 | 465 | |
---|
365 | 466 | ref->percpu_count_ptr &= ~__PERCPU_REF_DEAD; |
---|
366 | 467 | percpu_ref_get(ref); |
---|
.. | .. |
---|
368 | 469 | |
---|
369 | 470 | spin_unlock_irqrestore(&percpu_ref_switch_lock, flags); |
---|
370 | 471 | } |
---|
371 | | -EXPORT_SYMBOL_GPL(percpu_ref_reinit); |
---|
| 472 | +EXPORT_SYMBOL_GPL(percpu_ref_resurrect); |
---|