1 #define pr_fmt(fmt) "%s: " fmt "\n", __func__
3 #include <linux/kernel.h>
4 #include <linux/jiffies.h>
5 #include <linux/percpu-refcount.h>
6 #include <linux/rcupdate.h>
8 #define PCPU_COUNT_BITS 50
9 #define PCPU_COUNT_MASK ((1LL << PCPU_COUNT_BITS) - 1)
11 #define PCPU_STATUS_BITS 2
12 #define PCPU_STATUS_MASK ((1 << PCPU_STATUS_BITS) - 1)
14 #define PCPU_REF_PTR 0
15 #define PCPU_REF_NONE 1
16 #define PCPU_REF_DYING 2
17 #define PCPU_REF_DEAD 3
19 #define REF_STATUS(count) ((unsigned long) count & PCPU_STATUS_MASK)
21 void percpu_ref_init(struct percpu_ref *ref)
23 unsigned long now = jiffies;
25 atomic64_set(&ref->count, 1);
27 now <<= PCPU_STATUS_BITS;
30 ref->pcpu_count = (void *) now;
33 static void percpu_ref_alloc(struct percpu_ref *ref, unsigned __user *pcpu_count)
35 unsigned __percpu *new;
36 unsigned long last = (unsigned long) pcpu_count;
37 unsigned long now = jiffies;
39 now <<= PCPU_STATUS_BITS;
42 if (now - last <= HZ << PCPU_STATUS_BITS) {
44 new = alloc_percpu(unsigned);
50 BUG_ON(((unsigned long) new) & PCPU_STATUS_MASK);
52 if (cmpxchg(&ref->pcpu_count, pcpu_count, new) != pcpu_count)
57 update_time: new = (void *) now;
58 cmpxchg(&ref->pcpu_count, pcpu_count, new);
62 void __percpu_ref_get(struct percpu_ref *ref, bool alloc)
64 unsigned __percpu *pcpu_count;
67 pcpu_count = rcu_dereference(ref->pcpu_count);
69 if (REF_STATUS(pcpu_count) == PCPU_REF_PTR) {
70 __this_cpu_inc(*pcpu_count);
72 v = atomic64_add_return(1 + (1ULL << PCPU_COUNT_BITS),
75 if (!(v >> PCPU_COUNT_BITS) &&
76 REF_STATUS(pcpu_count) == PCPU_REF_NONE && alloc)
77 percpu_ref_alloc(ref, pcpu_count);
81 int percpu_ref_put(struct percpu_ref *ref)
83 unsigned __percpu *pcpu_count;
89 pcpu_count = rcu_dereference(ref->pcpu_count);
91 switch (REF_STATUS(pcpu_count)) {
93 __this_cpu_dec(*pcpu_count);
97 atomic64_dec(&ref->count);
100 v = atomic64_dec_return(&ref->count);
101 v &= PCPU_COUNT_MASK;
112 int percpu_ref_kill(struct percpu_ref *ref)
114 unsigned __percpu *old, *new, *pcpu_count = ref->pcpu_count;
115 unsigned long status;
118 status = REF_STATUS(pcpu_count);
122 new = (void *) PCPU_REF_DYING;
125 new = (void *) PCPU_REF_DEAD;
133 pcpu_count = cmpxchg(&ref->pcpu_count, old, new);
134 } while (pcpu_count != old);
136 if (status == PCPU_REF_PTR) {
137 unsigned count = 0, cpu;
141 for_each_possible_cpu(cpu)
142 count += *per_cpu_ptr(pcpu_count, cpu);
144 pr_debug("global %lli pcpu %i",
145 atomic64_read(&ref->count) & PCPU_COUNT_MASK,
148 atomic64_add((int) count, &ref->count);
150 /* Between setting global count and setting PCPU_REF_DEAD */
151 ref->pcpu_count = (void *) PCPU_REF_DEAD;
153 free_percpu(pcpu_count);
159 int percpu_ref_dead(struct percpu_ref *ref)
161 unsigned status = REF_STATUS(ref->pcpu_count);
163 return status == PCPU_REF_DYING ||
164 status == PCPU_REF_DEAD;