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 | ||
