diff options
author | Kent Overstreet <koverstreet@google.com> | 2013-05-31 18:26:45 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2013-06-03 18:36:41 -0400 |
commit | 215e262f2aeba378aa192da07c30770f9925a4bf (patch) | |
tree | c854461e40f3ce9dde45f7128679b20a362643f8 /include/linux/percpu-refcount.h | |
parent | 042dd60ca6dec9a02cefa8edd67de386e35755d6 (diff) |
percpu: implement generic percpu refcounting
This implements a refcount with similar semantics to
atomic_get()/atomic_dec_and_test() - but percpu.
It also implements two stage shutdown, as we need it to tear down the
percpu counts. Before dropping the initial refcount, you must call
percpu_ref_kill(); this puts the refcount in "shutting down mode" and
switches back to a single atomic refcount with the appropriate
barriers (synchronize_rcu()).
It's also legal to call percpu_ref_kill() multiple times - it only
returns true once, so callers don't have to reimplement shutdown
synchronization.
[akpm@linux-foundation.org: fix build]
[akpm@linux-foundation.org: coding-style tweak]
Signed-off-by: Kent Overstreet <koverstreet@google.com>
Cc: Zach Brown <zab@redhat.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Asai Thambi S P <asamymuthupa@micron.com>
Cc: Selvan Mani <smani@micron.com>
Cc: Sam Bradshaw <sbradshaw@micron.com>
Cc: Jeff Moyer <jmoyer@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Benjamin LaHaise <bcrl@kvack.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Reviewed-by: "Theodore Ts'o" <tytso@mit.edu>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'include/linux/percpu-refcount.h')
-rw-r--r-- | include/linux/percpu-refcount.h | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h new file mode 100644 index 000000000000..24b31ef15932 --- /dev/null +++ b/include/linux/percpu-refcount.h | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | * Percpu refcounts: | ||
3 | * (C) 2012 Google, Inc. | ||
4 | * Author: Kent Overstreet <koverstreet@google.com> | ||
5 | * | ||
6 | * This implements a refcount with similar semantics to atomic_t - atomic_inc(), | ||
7 | * atomic_dec_and_test() - but percpu. | ||
8 | * | ||
9 | * There's one important difference between percpu refs and normal atomic_t | ||
10 | * refcounts; you have to keep track of your initial refcount, and then when you | ||
11 | * start shutting down you call percpu_ref_kill() _before_ dropping the initial | ||
12 | * refcount. | ||
13 | * | ||
14 | * The refcount will have a range of 0 to ((1U << 31) - 1), i.e. one bit less | ||
15 | * than an atomic_t - this is because of the way shutdown works, see | ||
16 | * percpu_ref_kill()/PCPU_COUNT_BIAS. | ||
17 | * | ||
18 | * Before you call percpu_ref_kill(), percpu_ref_put() does not check for the | ||
19 | * refcount hitting 0 - it can't, if it was in percpu mode. percpu_ref_kill() | ||
20 | * puts the ref back in single atomic_t mode, collecting the per cpu refs and | ||
21 | * issuing the appropriate barriers, and then marks the ref as shutting down so | ||
22 | * that percpu_ref_put() will check for the ref hitting 0. After it returns, | ||
23 | * it's safe to drop the initial ref. | ||
24 | * | ||
25 | * USAGE: | ||
26 | * | ||
27 | * See fs/aio.c for some example usage; it's used there for struct kioctx, which | ||
28 | * is created when userspaces calls io_setup(), and destroyed when userspace | ||
29 | * calls io_destroy() or the process exits. | ||
30 | * | ||
31 | * In the aio code, kill_ioctx() is called when we wish to destroy a kioctx; it | ||
32 | * calls percpu_ref_kill(), then hlist_del_rcu() and sychronize_rcu() to remove | ||
33 | * the kioctx from the proccess's list of kioctxs - after that, there can't be | ||
34 | * any new users of the kioctx (from lookup_ioctx()) and it's then safe to drop | ||
35 | * the initial ref with percpu_ref_put(). | ||
36 | * | ||
37 | * Code that does a two stage shutdown like this often needs some kind of | ||
38 | * explicit synchronization to ensure the initial refcount can only be dropped | ||
39 | * once - percpu_ref_kill() does this for you, it returns true once and false if | ||
40 | * someone else already called it. The aio code uses it this way, but it's not | ||
41 | * necessary if the code has some other mechanism to synchronize teardown. | ||
42 | * around. | ||
43 | */ | ||
44 | |||
45 | #ifndef _LINUX_PERCPU_REFCOUNT_H | ||
46 | #define _LINUX_PERCPU_REFCOUNT_H | ||
47 | |||
48 | #include <linux/atomic.h> | ||
49 | #include <linux/kernel.h> | ||
50 | #include <linux/percpu.h> | ||
51 | #include <linux/rcupdate.h> | ||
52 | |||
53 | struct percpu_ref; | ||
54 | typedef void (percpu_ref_release)(struct percpu_ref *); | ||
55 | |||
56 | struct percpu_ref { | ||
57 | atomic_t count; | ||
58 | /* | ||
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 | ||
61 | * hack because we need to keep the pointer around for | ||
62 | * percpu_ref_kill_rcu()) | ||
63 | */ | ||
64 | unsigned __percpu *pcpu_count; | ||
65 | percpu_ref_release *release; | ||
66 | struct rcu_head rcu; | ||
67 | }; | ||
68 | |||
69 | int percpu_ref_init(struct percpu_ref *, percpu_ref_release *); | ||
70 | void percpu_ref_kill(struct percpu_ref *ref); | ||
71 | |||
72 | #define PCPU_STATUS_BITS 2 | ||
73 | #define PCPU_STATUS_MASK ((1 << PCPU_STATUS_BITS) - 1) | ||
74 | #define PCPU_REF_PTR 0 | ||
75 | #define PCPU_REF_DEAD 1 | ||
76 | |||
77 | #define REF_STATUS(count) (((unsigned long) count) & PCPU_STATUS_MASK) | ||
78 | |||
79 | /** | ||
80 | * percpu_ref_get - increment a percpu refcount | ||
81 | * | ||
82 | * Analagous to atomic_inc(). | ||
83 | */ | ||
84 | static inline void percpu_ref_get(struct percpu_ref *ref) | ||
85 | { | ||
86 | unsigned __percpu *pcpu_count; | ||
87 | |||
88 | preempt_disable(); | ||
89 | |||
90 | pcpu_count = ACCESS_ONCE(ref->pcpu_count); | ||
91 | |||
92 | if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) | ||
93 | __this_cpu_inc(*pcpu_count); | ||
94 | else | ||
95 | atomic_inc(&ref->count); | ||
96 | |||
97 | preempt_enable(); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * percpu_ref_put - decrement a percpu refcount | ||
102 | * | ||
103 | * Decrement the refcount, and if 0, call the release function (which was passed | ||
104 | * to percpu_ref_init()) | ||
105 | */ | ||
106 | static inline void percpu_ref_put(struct percpu_ref *ref) | ||
107 | { | ||
108 | unsigned __percpu *pcpu_count; | ||
109 | |||
110 | preempt_disable(); | ||
111 | |||
112 | pcpu_count = ACCESS_ONCE(ref->pcpu_count); | ||
113 | |||
114 | if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) | ||
115 | __this_cpu_dec(*pcpu_count); | ||
116 | else if (unlikely(atomic_dec_and_test(&ref->count))) | ||
117 | ref->release(ref); | ||
118 | |||
119 | preempt_enable(); | ||
120 | } | ||
121 | |||
122 | #endif | ||