percpu: implement percpu_ref_reinit()
Subsystems (7)
Tejun Heo
maintainerPATCH 1/1 percpu: implement percpu_ref_reinit()
Hello,
Currently, percpu_ref destruction - freeing the percpu counter - happens when the percpu_ref is released. This while a bit more convenient restricts how percpu_ref can be used. It can't be initialized with static percpu area and it can't be reinitialized without going through percpu allocation.
There are use cases which can take advantage of cycling percpu_ref through init/release multiple times. This patchset separates out percpu_ref destruction into percpu_exit() which should be invoked explicitly and introduces percpu_ref_reinit() which can be used to recycle a released percpu_ref.
This patchset doesn't add any users. Patchset to use this will soon be posted.
This patchset contains the following six patches.
0001-percpu-refcount-aio-use-percpu_ref_cancel_init-in-io.patch 0002-percpu-refcount-one-bit-is-enough-for-REF_STATUS.patch 0003-percpu-refcount-add-helpers-for-percpu_count-accesse.patch 0004-percpu-refcount-use-unsigned-long-for-pcpu_count-poi.patch 0005-percpu-refcount-require-percpu_ref-to-be-exited-expl.patch 0006-percpu-refcount-implement-percpu_ref_reinit-and-perc.patch
0001-0004 are prep/cleanups.
0005 separates out percpu destruction into percpu_ref_exit() and 0006 implements percpu_ref_reinit().
This patchset is on top of percpu/for-3.17 6fbc07bbe2b5 ("percpu:
invoke __verify_pcpu_ptr() from the generic part of accessors and operations").
diffstat follows.
drivers/target/target_core_tpg.c | 4 + fs/aio.c | 6 +- include/linux/percpu-refcount.h | 64 +++++++++++++++++++---------- kernel/cgroup.c | 8 ++- lib/percpu-refcount.c | 86 ++++++++++++++++++++++++--------------- 5 files changed, 109 insertions(+), 59 deletions(-)
Thanks.
--
tejun
Tejun Heo
maintainerPATCH 1/6 percpu-refcount, aio: use percpu_ref_cancel_init() in ioctx_alloc()
ioctx_alloc() reaches inside percpu_ref and directly frees ->pcpu_count in its failure path, which is quite gross. percpu_ref has been providing a proper interface to do this, percpu_ref_cancel_init(), for quite some time now. Let's use that instead.
This patch doesn't introduce any behavior changes.
--- fs/aio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/aio.c +2 -2
@@ -715,8 +715,8 @@ err_ctx: err:mutex_unlock (&ctx->ring_lock);free_percpu (ctx->cpu); -free_percpu (ctx->reqs.pcpu_count); -free_percpu (ctx->users.pcpu_count); +percpu_ref_cancel_init (&ctx->reqs); +percpu_ref_cancel_init (&ctx->users);kmem_cache_free (kioctx_cachep, ctx);pr_debug ("error allocating ioctx %d\n" , err);return ERR_PTR (err);
Signature · 1 line
1.9.3
Benjamin LaHaise
maintainerRe: [PATCH 1/6] percpu-refcount, aio: use percpu_ref_cancel_init() in ioctx_alloc()
Quoted 5 lines · Tejun Heo
ioctx_alloc()reaches insidepercpu_refand directly frees ->pcpu_count in its failure path, which is quite gross.percpu_refhas been providing a proper interface to do this,percpu_ref_cancel_init(), for quite some time now. Let's use that instead.
I applied this to my aio-next tree at git://git.kvack.org/~bcrl/aio-next.git .
-ben
Quoted 26 lines
This patch doesn't introduce any behavior changes.
Signed-off-by Tejun Heo <[email protected]>Cc Benjamin LaHaise <[email protected]>Cc Kent Overstreet <[email protected]>--- fs/aio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)fs/aio.c +2
-2@@ -715,8 +715,8 @@ err_ctx: err:mutex_unlock (&ctx->ring_lock);free_percpu (ctx->cpu); -free_percpu (ctx->reqs.pcpu_count); -free_percpu (ctx->users.pcpu_count); +percpu_ref_cancel_init (&ctx->reqs); +percpu_ref_cancel_init (&ctx->users);kmem_cache_free (kioctx_cachep, ctx);pr_debug ("error allocating ioctx %d\n" , err);return ERR_PTR (err);Signature · 1 line
1.9.3
Signature · 1 line
"Thought is the essence of where you are now."
Tejun Heo
maintainerRe: [PATCH 1/6] percpu-refcount, aio: use percpu_ref_cancel_init() in ioctx_alloc()
Hello, Benjamin.
Quoted 8 lines · Benjamin LaHaise
Quoted 5 lines · Tejun Heo
ioctx_alloc()reaches insidepercpu_refand directly frees ->pcpu_count in its failure path, which is quite gross.percpu_refhas been providing a proper interface to do this,percpu_ref_cancel_init(), for quite some time now. Let's use that instead.I applied this to my aio-next tree at
git://git.kvack.org/~bcrl/aio-next.git.
It'd probably easier if I route this through percpu tree with the rest of changes as there are further changes on the aio side (trivial but still), so we'd end up having to do pulling dance back and forth. The changes on aio side are mostly trivial. Can I route them through percpu tree with your acks?
Thanks.
Signature · 1 line
tejun
Benjamin LaHaise
maintainerRe: [PATCH 1/6] percpu-refcount, aio: use percpu_ref_cancel_init() in ioctx_alloc()
Hi Tejun,
Quoted 1 line · Tejun Heo
Hello, Benjamin.
...
Quoted 5 lines
It'd probably easier if I route this through percpu tree with the rest of changes as there are further changes on the aio side (trivial but still), so we'd end up having to do pulling dance back and forth. The changes on aio side are mostly trivial. Can I route them through percpu tree with your acks?
Sure, no problem -- add my acks and proceed. I don't think there should be any conflicts. Cheers,
-ben
Quoted 4 lines
Thanks.
Signature · 1 line
tejun
Signature · 1 line
"Thought is the essence of where you are now."
Tejun Heo
maintainerRe: [PATCH 1/6] percpu-refcount, aio: use percpu_ref_cancel_init() in ioctx_alloc()
Quoted 5 lines · Benjamin LaHaise
Hi Tejun,
Quoted 1 line · Tejun Heo
Hello, Benjamin.
...
Quoted 8 lines
Quoted 5 lines
It'd probably easier if I route this through percpu tree with the rest of changes as there are further changes on the aio side (trivial but still), so we'd end up having to do pulling dance back and forth. The changes on aio side are mostly trivial. Can I route them through percpu tree with your acks?
Sure, no problem -- add my acks and proceed. I don't think there should be any conflicts. Cheers,
Thanks a lot!
Signature · 1 line
tejun
Tejun Heo
maintainerPATCH 2/6 percpu-refcount: one bit is enough for REF_STATUS
In reply to Tejun Heopercpu-refcount currently reserves two lowest bits of its percpu pointer to indicate its state; however, only one bit is used for PCPU_REF_DEAD.
Simplify it by removing PCPU_STATUS_BITS/MASK and testing PCPU_REF_DEAD directly. This also allows the compiler to choose a more efficient instruction depending on the architecture.
--- include/linux/percpu-refcount.h | 4 +--- lib/percpu-refcount.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-)
include/linux/percpu-refcount.h +1 -3
@@ -88,12 +88,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)return percpu_ref_kill_and_confirm (ref,NULL ); } -#define PCPU_STATUS_BITS 2 -#define PCPU_STATUS_MASK ((1 << PCPU_STATUS_BITS) - 1)#define PCPU_REF_PTR 0#define PCPU_REF_DEAD 1 -#define REF_STATUS (count) (((unsigned long) count) & PCPU_STATUS_MASK) +#define REF_STATUS (count) (((unsigned long) count) & PCPU_REF_DEAD)/** * percpu_ref_get - increment a percpu refcount
lib/percpu-refcount.c +1 -1
@@ -96,7 +96,7 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)/* Mask out PCPU_REF_DEAD */ pcpu_count = (unsigned __percpu *) - (((unsigned long ) pcpu_count) & ~PCPU_STATUS_MASK ); + (((unsigned long ) pcpu_count) & ~PCPU_REF_DEAD );for_each_possible_cpu (cpu) count += *per_cpu_ptr (pcpu_count, cpu);
Signature · 1 line
1.9.3
Re: [PATCH 2/6] percpu-refcount: one bit is enough for REF_STATUS
Quoted 21 lines · Tejun Heo
percpu-refcount currently reserves two lowest bits of its percpu pointer to indicate its state; however, only one bit is used for
PCPU_REF_DEAD.Simplify it by removing PCPU_STATUS_BITS/MASK and testing
PCPU_REF_DEADdirectly. This also allows the compiler to choose a more efficient instruction depending on the architecture.Signed-off-by Tejun Heo <[email protected]>Cc Kent Overstreet <[email protected]>Cc Christoph Lameter <[email protected]>--- include/linux/percpu-refcount.h | 4 +--- lib/percpu-refcount.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-)include/linux/percpu-refcount.h +0
-0
include/linux/percpu-refcount.h static inline void percpu_ref_kill(struct percpu_ref *ref) · line 91
Quoted 4 lines
include/linux/percpu-refcount.h +0
-1@@ -88,12 +88,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)return percpu_ref_kill_and_confirm (ref,NULL ); } -#define PCPU_STATUS_BITS 2
Just using "#define PCPU_STATUS_BITS 1" can achieve exactly the same
result as you need. And it is better for future extensions for the status.
Anyway,
Quoted 22 lines
include/linux/percpu-refcount.h +1
-2-#define PCPU_STATUS_MASK ((1 << PCPU_STATUS_BITS) - 1)#define PCPU_REF_PTR 0#define PCPU_REF_DEAD 1 -#define REF_STATUS (count) (((unsigned long) count) & PCPU_STATUS_MASK) +#define REF_STATUS (count) (((unsigned long) count) & PCPU_REF_DEAD)/** * percpu_ref_get - increment a percpu refcount lib/percpu-refcount.c +1
-1@@ -96,7 +96,7 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)/* Mask out PCPU_REF_DEAD */ pcpu_count = (unsigned __percpu *) - (((unsigned long ) pcpu_count) & ~PCPU_STATUS_MASK ); + (((unsigned long ) pcpu_count) & ~PCPU_REF_DEAD );for_each_possible_cpu (cpu) count += *per_cpu_ptr (pcpu_count, cpu);
Tejun Heo
maintainerPATCH 3/6 percpu-refcount: add helpers for ->percpu_count accesses
In reply to Tejun Heo* All four percpu_ref_*() operations implemented in the header file perform the same operation to determine whether the percpu_ref is alive and extract the percpu pointer. Factor out the common logic into __pcpu_ref_alive(). This doesn't change the generated code.
* There are a couple places in percpu-refcount.c which masks out PCPU_REF_DEAD to obtain the percpu pointer. Factor it out into pcpu_count_ptr().
* The above changes make the WARN_ON_ONCE() conditional at the top of percpu_ref_kill_and_confirm() the only user of REF_STATUS(). Test PCPU_REF_DEAD directly and remove REF_STATUS().
This patch doesn't introduce any functional change.
--- include/linux/percpu-refcount.h | 35 +++++++++++++++++++++-------------- lib/percpu-refcount.c | 17 +++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-)
include/linux/percpu-refcount.h +21 -14
@@ -88,10 +88,25 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)return percpu_ref_kill_and_confirm (ref,NULL ); } -#define PCPU_REF_PTR 0#define PCPU_REF_DEAD 1 -#define REF_STATUS (count) (((unsigned long) count) & PCPU_REF_DEAD) +/* +* Internal helper. Don't use outside percpu-refcount proper. The +* function doesn't return the pointer and let the caller test it for NULL +* because doing so forces the compiler to generate two conditional +* branches as it can't assume that @ref->pcpu_count is not NULL. +*/ +static inline bool __pcpu_ref_alive (struct percpu_ref *ref, +unsigned __percpu **pcpu_countp) +{ +unsigned long pcpu_ptr = (unsigned long )ACCESS_ONCE (ref->pcpu_count); + +if (unlikely (pcpu_ptr &PCPU_REF_DEAD )) +return false; + + *pcpu_countp = (unsigned __percpu *)pcpu_ptr; +return true; +}/** * percpu_ref_get - increment a percpu refcount
@@ -105,9 +120,7 @@ static inline void percpu_ref_get(struct percpu_ref *ref)rcu_read_lock_sched (); - pcpu_count =ACCESS_ONCE (ref->pcpu_count); - -if (likely (REF_STATUS (pcpu_count) ==PCPU_REF_PTR )) +if (__pcpu_ref_alive (ref, &pcpu_count))this_cpu_inc (*pcpu_count);else atomic_inc (&ref->count);
@@ -131,9 +144,7 @@ static inline bool percpu_ref_tryget(struct percpu_ref *ref)rcu_read_lock_sched (); - pcpu_count =ACCESS_ONCE (ref->pcpu_count); - -if (likely (REF_STATUS (pcpu_count) ==PCPU_REF_PTR )) { +if (__pcpu_ref_alive (ref, &pcpu_count)) {this_cpu_inc (*pcpu_count); ret = true; }else {
@@ -166,9 +177,7 @@ static inline bool percpu_ref_tryget_live(struct percpu_ref *ref)rcu_read_lock_sched (); - pcpu_count =ACCESS_ONCE (ref->pcpu_count); - -if (likely (REF_STATUS (pcpu_count) ==PCPU_REF_PTR )) { +if (__pcpu_ref_alive (ref, &pcpu_count)) {this_cpu_inc (*pcpu_count); ret = true; }
@@ -191,9 +200,7 @@ static inline void percpu_ref_put(struct percpu_ref *ref)rcu_read_lock_sched (); - pcpu_count =ACCESS_ONCE (ref->pcpu_count); - -if (likely (REF_STATUS (pcpu_count) ==PCPU_REF_PTR )) +if (__pcpu_ref_alive (ref, &pcpu_count))this_cpu_dec (*pcpu_count);else if (unlikely (atomic_dec_and_test (&ref->count))) ref->release (ref);
lib/percpu-refcount.c +9 -8
@@ -31,6 +31,11 @@#define PCPU_COUNT_BIAS (1U << 31) +static unsigned __percpu *pcpu_count_ptr (struct percpu_ref *ref) +{ +return (unsigned __percpu *)((unsigned long )ref->pcpu_count & ~PCPU_REF_DEAD ); +} +/** * percpu_ref_init - initialize a percpu refcount * @ref: percpu_ref to initialize
@@ -74,7 +79,7 @@ EXPORT_SYMBOL_GPL(percpu_ref_init);*/ void percpu_ref_cancel_init (struct percpu_ref *ref) { -unsigned __percpu *pcpu_count = ref->pcpu_count; +unsigned __percpu *pcpu_count =pcpu_count_ptr (ref);int cpu;WARN_ON_ONCE (atomic_read (&ref->count) !=1 +PCPU_COUNT_BIAS );
@@ -82,7 +87,7 @@ void percpu_ref_cancel_init(struct percpu_ref *ref)if (pcpu_count) {for_each_possible_cpu (cpu)WARN_ON_ONCE (*per_cpu_ptr (pcpu_count, cpu)); -free_percpu (ref->pcpu_count); +free_percpu (pcpu_count); } }EXPORT_SYMBOL_GPL (percpu_ref_cancel_init);
@@ -90,14 +95,10 @@ EXPORT_SYMBOL_GPL(percpu_ref_cancel_init);static void percpu_ref_kill_rcu (struct rcu_head *rcu) { struct percpu_ref *ref = container_of(rcu, struct percpu_ref, rcu); -unsigned __percpu *pcpu_count = ref->pcpu_count; +unsigned __percpu *pcpu_count =pcpu_count_ptr (ref);unsigned count =0 ;int cpu; -/* Mask out PCPU_REF_DEAD */ - pcpu_count = (unsigned __percpu *) - (((unsigned long ) pcpu_count) & ~PCPU_REF_DEAD ); -for_each_possible_cpu (cpu) count += *per_cpu_ptr (pcpu_count, cpu);
@@ -152,7 +153,7 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)void percpu_ref_kill_and_confirm (struct percpu_ref *ref,percpu_ref_func_t *confirm_kill) { -WARN_ONCE (REF_STATUS (ref->pcpu_count) ==PCPU_REF_DEAD , +WARN_ONCE ((unsigned long )ref->pcpu_count &PCPU_REF_DEAD ,"percpu_ref_kill() called more than once!\n" ); ref->pcpu_count = (unsigned __percpu *)
Signature · 1 line
1.9.3
Tejun Heo
maintainerPATCH 4/6 percpu-refcount: use unsigned long for pcpu_count pointer
In reply to Tejun Heopercpu_ref->pcpu_count is a percpu pointer with a status flag in its lowest bit. As such, it always goes through arithmetic operations which is very cumbersome to do on a pointer. It has to be first casted to unsigned long and then back.
Let's just make the field unsigned long so that we can skip the first casts. While at it, rename it to pcpu_counter_ptr to clarify that it's a pointer value.
--- include/linux/percpu-refcount.h | 4 ++-- lib/percpu-refcount.c | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-)
include/linux/percpu-refcount.h +2 -2
@@ -61,7 +61,7 @@ struct percpu_ref {* hack because we need to keep the pointer around for * percpu_ref_kill_rcu()) */ -unsigned __percpu *pcpu_count; +unsigned long pcpu_count_ptr;percpu_ref_func_t *release;percpu_ref_func_t *confirm_kill;struct rcu_head rcu;
@@ -99,7 +99,7 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)static inline bool __pcpu_ref_alive (struct percpu_ref *ref,unsigned __percpu **pcpu_countp) { -unsigned long pcpu_ptr = (unsigned long )ACCESS_ONCE (ref->pcpu_count); +unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr);if (unlikely (pcpu_ptr &PCPU_REF_DEAD ))return false;
lib/percpu-refcount.c +5 -6
@@ -33,7 +33,7 @@static unsigned __percpu *pcpu_count_ptr (struct percpu_ref *ref) { -return (unsigned __percpu *)((unsigned long )ref->pcpu_count & ~PCPU_REF_DEAD ); +return (unsigned __percpu *)(ref->pcpu_count_ptr & ~PCPU_REF_DEAD ); }/**
@@ -51,8 +51,8 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release) {atomic_set (&ref->count,1 +PCPU_COUNT_BIAS ); - ref->pcpu_count =alloc_percpu (unsigned); -if (!ref->pcpu_count) + ref->pcpu_count_ptr = (unsigned long )alloc_percpu (unsigned); +if (!ref->pcpu_count_ptr)return -ENOMEM ; ref->release = release;
@@ -153,11 +153,10 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)void percpu_ref_kill_and_confirm (struct percpu_ref *ref,percpu_ref_func_t *confirm_kill) { -WARN_ONCE ((unsigned long )ref->pcpu_count &PCPU_REF_DEAD , +WARN_ONCE (ref->pcpu_count_ptr &PCPU_REF_DEAD ,"percpu_ref_kill() called more than once!\n" ); - ref->pcpu_count = (unsigned __percpu *) - (((unsigned long ) ref->pcpu_count)|PCPU_REF_DEAD ); + ref->pcpu_count_ptr |=PCPU_REF_DEAD ; ref->confirm_kill = confirm_kill;call_rcu_sched (&ref->rcu, percpu_ref_kill_rcu);
Signature · 1 line
1.9.3
Tejun Heo
maintainerPATCH 5/6 percpu-refcount: require percpu_ref to be exited explicitly
In reply to Tejun HeoCurrently, a percpu_ref undoes percpu_ref_init() automatically by freeing the allocated percpu area when the percpu_ref is killed. While seemingly convenient, this has the following niggles.
* It's impossible to re-init a released reference counter without going through re-allocation.
* In the similar vein, it's impossible to initialize a percpu_ref count with static percpu variables.
* We need and have an explicit destructor anyway for failure paths - percpu_ref_cancel_init().
This patch removes the automatic percpu counter freeing in percpu_ref_kill_rcu() and repurposes percpu_ref_cancel_init() into a generic destructor now named percpu_ref_exit(). percpu_ref_destroy() is considered but it gets confusing with percpu_ref_kill() while "exit" clearly indicates that it's the counterpart of percpu_ref_init().
All percpu_ref_cancel_init() users are updated to invoke percpu_ref_exit() instead and explicit percpu_ref_exit() calls are added to the destruction path of all percpu_ref users.
--- drivers/target/target_core_tpg.c | 4 +++- fs/aio.c | 6 ++++-- include/linux/percpu-refcount.h | 6 ++---- kernel/cgroup.c | 8 +++++--- lib/percpu-refcount.c | 33 ++++++++++----------------------- 5 files changed, 24 insertions(+), 33 deletions(-)
drivers/target/target_core_tpg.c +3 -1
@@ -825,7 +825,7 @@ int core_tpg_add_lun( ret =core_dev_export (dev, tpg, lun);if (ret <0 ) { -percpu_ref_cancel_init (&lun->lun_ref); +percpu_ref_exit (&lun->lun_ref);return ret; }
@@ -880,5 +880,7 @@ int core_tpg_post_dellun( lun->lun_status =TRANSPORT_LUN_STATUS_FREE ;spin_unlock (&tpg->tpg_lun_lock); +percpu_ref_exit (&lun->lun_ref); +return 0 ; }
fs/aio.c +4 -2
@@ -506,6 +506,8 @@ static void free_ioctx(struct work_struct *work)aio_free_ring (ctx);free_percpu (ctx->cpu); +percpu_ref_exit (&ctx->reqs); +percpu_ref_exit (&ctx->users);kmem_cache_free (kioctx_cachep, ctx); }
@@ -715,8 +717,8 @@ err_ctx: err:mutex_unlock (&ctx->ring_lock);free_percpu (ctx->cpu); -percpu_ref_cancel_init (&ctx->reqs); -percpu_ref_cancel_init (&ctx->users); +percpu_ref_exit (&ctx->reqs); +percpu_ref_exit (&ctx->users);kmem_cache_free (kioctx_cachep, ctx);pr_debug ("error allocating ioctx %d\n" , err);return ERR_PTR (err);
include/linux/percpu-refcount.h +2 -4
@@ -57,9 +57,7 @@ struct percpu_ref {atomic_t count;/* * The low bit of the pointer indicates whether the ref is in percpu -* mode; if set, then get/put will manipulate the atomic_t (this is a -* hack because we need to keep the pointer around for -* percpu_ref_kill_rcu()) +* mode; if set, then get/put will manipulate the atomic_t. */ unsigned long pcpu_count_ptr;percpu_ref_func_t *release;
@@ -69,7 +67,7 @@ struct percpu_ref {int __must_check percpu_ref_init (struct percpu_ref *ref,percpu_ref_func_t *release); -void percpu_ref_cancel_init (struct percpu_ref *ref); +void percpu_ref_exit (struct percpu_ref *ref);void percpu_ref_kill_and_confirm (struct percpu_ref *ref,percpu_ref_func_t *confirm_kill);
kernel/cgroup.c +5 -3
@@ -1638,7 +1638,7 @@ destroy_root: exit_root_id:cgroup_exit_root_id (root); cancel_ref: -percpu_ref_cancel_init (&root_cgrp->self.refcnt); +percpu_ref_exit (&root_cgrp->self.refcnt); out:free_cgrp_cset_links (&tmp_links);return ret;
@@ -4133,6 +4133,8 @@ static void css_free_work_fn(struct work_struct *work) container_of(work, struct cgroup_subsys_state, destroy_work);struct cgroup *cgrp = css->cgroup; +percpu_ref_exit (&css->refcnt); +if (css->ss) {/* css free path */ if (css->parent)
@@ -4330,7 +4332,7 @@ err_list_del: err_free_id:cgroup_idr_remove (&ss->css_idr, css->id); err_free_percpu_ref: -percpu_ref_cancel_init (&css->refcnt); +percpu_ref_exit (&css->refcnt); err_free_css:call_rcu (&css->rcu_head, css_free_rcu_fn);return err;
@@ -4441,7 +4443,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, out_free_id:cgroup_idr_remove (&root->cgroup_idr, cgrp->id); out_cancel_ref: -percpu_ref_cancel_init (&cgrp->self.refcnt); +percpu_ref_exit (&cgrp->self.refcnt); out_free_cgrp:kfree (cgrp); out_unlock:
lib/percpu-refcount.c +10 -23
@@ -61,36 +61,25 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release)EXPORT_SYMBOL_GPL (percpu_ref_init);/** -* percpu_ref_cancel_init - cancel percpu_ref_init() -* @ref: percpu_ref to cancel init for +* percpu_ref_exit - undo percpu_ref_init() +* @ref: percpu_ref to exit * -* Once a percpu_ref is initialized, its destruction is initiated by -* percpu_ref_kill() and completes asynchronously, which can be painful to -* do when destroying a half-constructed object in init failure path. -* -* This function destroys @ref without invoking @ref->release and the -* memory area containing it can be freed immediately on return. To -* prevent accidental misuse, it's required that @ref has finished -* percpu_ref_init(), whether successful or not, but never used. -* -* The weird name and usage restriction are to prevent people from using -* this function by mistake for normal shutdown instead of -* percpu_ref_kill(). +* This function exits @ref. The caller is responsible for ensuring that +* @ref is no longer in active use. The usual places to invoke this +* function from are the @ref->release() callback or in init failure path +* where percpu_ref_init() succeeded but other parts of the initialization +* of the embedding object failed. */ -void percpu_ref_cancel_init (struct percpu_ref *ref) +void percpu_ref_exit (struct percpu_ref *ref) {unsigned __percpu *pcpu_count =pcpu_count_ptr (ref); -int cpu; - -WARN_ON_ONCE (atomic_read (&ref->count) !=1 +PCPU_COUNT_BIAS );if (pcpu_count) { -for_each_possible_cpu (cpu) -WARN_ON_ONCE (*per_cpu_ptr (pcpu_count, cpu));free_percpu (pcpu_count); + ref->pcpu_count_ptr =PCPU_REF_DEAD ; } } -EXPORT_SYMBOL_GPL (percpu_ref_cancel_init); +EXPORT_SYMBOL_GPL (percpu_ref_exit);static void percpu_ref_kill_rcu (struct rcu_head *rcu) {
@@ -102,8 +91,6 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)for_each_possible_cpu (cpu) count += *per_cpu_ptr (pcpu_count, cpu); -free_percpu (pcpu_count); -pr_debug ("global %i pcpu %i" ,atomic_read (&ref->count), (int ) count);/*
Signature · 1 line
1.9.3
Tejun Heo
maintainerPATCH 6/6 percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
In reply to Tejun HeoNow that explicit invocation of percpu_ref_exit() is necessary to free the percpu counter, we can implement percpu_ref_reinit() which reinitializes a released percpu_ref. This can be used implement scalable gating switch which can be drained and then re-opened without worrying about memory allocation failures.
percpu_ref_is_zero() is added to be used in a sanity check in percpu_ref_exit(). As this function will be useful for other purposes too, make it a public interface.
--- include/linux/percpu-refcount.h | 21 ++++++++++++++++++++- lib/percpu-refcount.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-)
include/linux/percpu-refcount.h +20 -1
@@ -67,6 +67,7 @@ struct percpu_ref {int __must_check percpu_ref_init (struct percpu_ref *ref,percpu_ref_func_t *release); +void percpu_ref_reinit (struct percpu_ref *ref);void percpu_ref_exit (struct percpu_ref *ref);void percpu_ref_kill_and_confirm (struct percpu_ref *ref,percpu_ref_func_t *confirm_kill);
@@ -97,7 +98,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)static inline bool __pcpu_ref_alive (struct percpu_ref *ref,unsigned __percpu **pcpu_countp) { -unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr); +unsigned long pcpu_ptr; + +/* paired with smp_store_release() in percpu_ref_reinit() */ + pcpu_ptr =smp_load_acquire (&ref->pcpu_count_ptr);if (unlikely (pcpu_ptr &PCPU_REF_DEAD ))return false;
@@ -206,4 +210,19 @@ static inline void percpu_ref_put(struct percpu_ref *ref) rcu_read_unlock_sched(); } +/** +* percpu_ref_is_zero - test whether a percpu refcount reached zero +* @ref: percpu_ref to test +* +* Returns %true if @ref reached zero. +*/ +static inline bool percpu_ref_is_zero (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count; + +if (__pcpu_ref_alive (ref, &pcpu_count)) +return false; +return !atomic_read (&ref->count); +} +#endif
lib/percpu-refcount.c +35 -0
@@ -61,6 +61,41 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release)EXPORT_SYMBOL_GPL (percpu_ref_init);/** +* percpu_ref_reinit - re-initialize a percpu refcount +* @ref: perpcu_ref to re-initialize +* +* Re-initialize @ref so that it's in the same state as when it finished +* percpu_ref_init(). @ref must have been initialized successfully, killed +* and reached 0 but not exited. +* +* Note that percpu_ref_tryget[_live]() are safe to perform on @ref while +* this function is in progress. +*/ +void percpu_ref_reinit (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count =pcpu_count_ptr (ref); +int cpu; + +BUG_ON (!pcpu_count); +WARN_ON (!percpu_ref_is_zero (ref)); + +atomic_set (&ref->count,1 +PCPU_COUNT_BIAS ); + +/* +* Restore per-cpu operation. smp_store_release() is paired with +* smp_load_acquire() in __pcpu_ref_alive() and guarantees that the +* zeroing is visible to all percpu accesses which can see the +* following PCPU_REF_DEAD clearing. +*/ +for_each_possible_cpu (cpu) + *per_cpu_ptr (pcpu_count, cpu) =0 ; + +smp_store_release (&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD ); +} +EXPORT_SYMBOL_GPL (percpu_ref_reinit); + +/** * percpu_ref_exit - undo percpu_ref_init() * @ref: percpu_ref to exit *
Signature · 1 line
1.9.3
Re: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 31 lines · Tejun Heo
Now that explicit invocation of
percpu_ref_exit()is necessary to free the percpu counter, we can implementpercpu_ref_reinit()which reinitializes a releasedpercpu_ref. This can be used implement scalable gating switch which can be drained and then re-opened without worrying about memory allocation failures.
percpu_ref_is_zero()is added to be used in a sanity check inpercpu_ref_exit(). As this function will be useful for other purposes too, make it a public interface.Signed-off-by Tejun Heo <[email protected]>Cc Kent Overstreet <[email protected]>Cc Christoph Lameter <[email protected]>--- include/linux/percpu-refcount.h | 21 ++++++++++++++++++++- lib/percpu-refcount.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-)include/linux/percpu-refcount.h +1
-0@@ -67,6 +67,7 @@ struct percpu_ref {int __must_check percpu_ref_init (struct percpu_ref *ref,percpu_ref_func_t *release); +void percpu_ref_reinit (struct percpu_ref *ref);void percpu_ref_exit (struct percpu_ref *ref);void percpu_ref_kill_and_confirm (struct percpu_ref *ref,percpu_ref_func_t *confirm_kill);
include/linux/percpu-refcount.h static inline void percpu_ref_kill(struct percpu_ref *ref) · line 104
Quoted 8 lines
include/linux/percpu-refcount.h +4
-1@@ -97,7 +98,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)static inline bool __pcpu_ref_alive (struct percpu_ref *ref,unsigned __percpu **pcpu_countp) { -unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr); +unsigned long pcpu_ptr; + +/* paired with smp_store_release() in percpu_ref_reinit() */ + pcpu_ptr =smp_load_acquire (&ref->pcpu_count_ptr);
Does "smp_load_acquire()" hurts the performance of percpu_ref_get/put() in non-x86 system?
Quoted 69 lines
include/linux/percpu-refcount.h +15
-0if (unlikely (pcpu_ptr &PCPU_REF_DEAD ))return false;@@ -206,4 +210,19 @@ static inline void percpu_ref_put(struct percpu_ref *ref) rcu_read_unlock_sched(); } +/** +* percpu_ref_is_zero - test whether a percpu refcount reached zero +* @ref: percpu_ref to test +* +* Returns %true if @ref reached zero. +*/ +static inline bool percpu_ref_is_zero (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count; + +if (__pcpu_ref_alive (ref, &pcpu_count)) +return false; +return !atomic_read (&ref->count); +} +#endif lib/percpu-refcount.c +35
-0@@ -61,6 +61,41 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release)EXPORT_SYMBOL_GPL (percpu_ref_init);/** +* percpu_ref_reinit - re-initialize a percpu refcount +* @ref: perpcu_ref to re-initialize +* +* Re-initialize @ref so that it's in the same state as when it finished +* percpu_ref_init(). @ref must have been initialized successfully, killed +* and reached 0 but not exited. +* +* Note that percpu_ref_tryget[_live]() are safe to perform on @ref while +* this function is in progress. +*/ +void percpu_ref_reinit (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count =pcpu_count_ptr (ref); +int cpu; + +BUG_ON (!pcpu_count); +WARN_ON (!percpu_ref_is_zero (ref)); + +atomic_set (&ref->count,1 +PCPU_COUNT_BIAS ); + +/* +* Restore per-cpu operation. smp_store_release() is paired with +* smp_load_acquire() in __pcpu_ref_alive() and guarantees that the +* zeroing is visible to all percpu accesses which can see the +* following PCPU_REF_DEAD clearing. +*/ +for_each_possible_cpu (cpu) + *per_cpu_ptr (pcpu_count, cpu) =0 ; + +smp_store_release (&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD ); +} +EXPORT_SYMBOL_GPL (percpu_ref_reinit); + +/** * percpu_ref_exit - undo percpu_ref_init() * @ref: percpu_ref to exit *
Tejun Heo
maintainerRe: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 5 lines · Lai Jiangshan
Quoted 5 lines
include/linux/percpu-refcount.h +0
-1@@ -97,7 +98,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)static inline bool __pcpu_ref_alive (struct percpu_ref *ref,unsigned __percpu **pcpu_countp) { -unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr);
Quoted 8 lines
include/linux/percpu-refcount.h static inline void percpu_ref_kill(struct percpu_ref *ref) · line 104
Quoted 4 lines
include/linux/percpu-refcount.h +4
-0+unsigned long pcpu_ptr; + +/* paired with smp_store_release() in percpu_ref_reinit() */ + pcpu_ptr =smp_load_acquire (&ref->pcpu_count_ptr);Does "
smp_load_acquire()" hurts the performance of percpu_ref_get/put() in non-x86 system?
It's equivalent to data dependency barrier. The only arch which needs something more than barrier() is alpha. It isn't an issue.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 15 lines · Tejun Heo
Quoted 5 lines · Lai Jiangshan
Quoted 5 lines
include/linux/percpu-refcount.h +0
-1@@ -97,7 +98,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)static inline bool __pcpu_ref_alive (struct percpu_ref *ref,unsigned __percpu **pcpu_countp) { -unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr);Quoted 8 lines
include/linux/percpu-refcount.h static inline void percpu_ref_kill(struct percpu_ref *ref) · line 104
Quoted 4 lines
include/linux/percpu-refcount.h +4
-0+unsigned long pcpu_ptr; + +/* paired with smp_store_release() in percpu_ref_reinit() */ + pcpu_ptr =smp_load_acquire (&ref->pcpu_count_ptr);Does "
smp_load_acquire()" hurts the performance of percpu_ref_get/put() in non-x86 system?
Quoted 3 lines
It's equivalent to data dependency barrier. The only arch which needs something more than
barrier()is alpha. It isn't an issue.
But I searched from the source, smp_load_acquire() is just barrier() in x86, arm64, ia64, s390, sparc, but it includes memory barrier instruction in other archs.
CC Paul. If smp_load_acquire() is sufficient lightweight, I would update the SRCU.
Thanks,
Lai
Tejun Heo
maintainerRe: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 16 lines · Lai Jiangshan
Quoted 15 lines · Tejun Heo
Quoted 13 lines · Lai Jiangshan
include/linux/percpu-refcount.h static inline void percpu_ref_kill(struct percpu_ref *ref) · line 104
Quoted 9 lines
include/linux/percpu-refcount.h +4
-1@@ -97,7 +98,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)static inline bool __pcpu_ref_alive (struct percpu_ref *ref,unsigned __percpu **pcpu_countp) { -unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr); +unsigned long pcpu_ptr; + +/* paired with smp_store_release() in percpu_ref_reinit() */ + pcpu_ptr =smp_load_acquire (&ref->pcpu_count_ptr);Does "
smp_load_acquire()" hurts the performance of percpu_ref_get/put() in non-x86 system?
Quoted 7 lines
Quoted 3 lines
It's equivalent to data dependency barrier. The only arch which needs something more than
barrier()is alpha. It isn't an issue.But I searched from the source,
smp_load_acquire()is justbarrier()in x86, arm64, ia64, s390, sparc, but it includes memory barrier instruction in other archs.
Hmmm, right, it's a stronger guarantee than the data dependency barrier. This should probably use smp_wmb() and smp_read_barrier_depends(). That's all it needs anyway.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 20 lines · Tejun Heo
Quoted 19 lines · Lai Jiangshan
Quoted 17 lines · Tejun Heo
Quoted 5 lines · Lai Jiangshan
Quoted 5 lines
include/linux/percpu-refcount.h +0
-1@@ -97,7 +98,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref)static inline bool __pcpu_ref_alive (struct percpu_ref *ref,unsigned __percpu **pcpu_countp) { -unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr);Quoted 8 lines
include/linux/percpu-refcount.h static inline void percpu_ref_kill(struct percpu_ref *ref) · line 104
Quoted 4 lines
include/linux/percpu-refcount.h +4
-0+unsigned long pcpu_ptr; + +/* paired with smp_store_release() in percpu_ref_reinit() */ + pcpu_ptr =smp_load_acquire (&ref->pcpu_count_ptr);Does "
smp_load_acquire()" hurts the performance of percpu_ref_get/put() in non-x86 system?It's equivalent to data dependency barrier. The only arch which needs something more than
barrier()is alpha. It isn't an issue.
Quoted 7 lines
Quoted 3 lines
But I searched from the source,
smp_load_acquire()is justbarrier()in x86, arm64, ia64, s390, sparc, but it includes memory barrier instruction in other archs.Hmmm, right, it's a stronger guarantee than the data dependency barrier. This should probably use
smp_wmb()andsmp_read_barrier_depends(). That's all it needs anyway.
Yep, smp_load_acquire() orders its load against later loads and stores, so it really does need a memory barrier on weakly ordered systems.
This is the "publish" operation for dynamically allocated per-CPU references? If so, agreed, you should be able to rely on dependency ordering. Make sure to comment the smp_read_barrier_depends(). ;-)
Thanx, Paul
Quoted 5 lines
Thanks.
Signature · 1 line
tejun
Tejun Heo
maintainerRe: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Hey, Paul.
Quoted 2 lines · Paul E. McKenney
Yep,
smp_load_acquire()orders its load against later loads and stores, so it really does need a memory barrier on weakly ordered systems.
Yeap.
Quoted 3 lines
This is the "publish" operation for dynamically allocated per-CPU references? If so, agreed, you should be able to rely on dependency ordering. Make sure to comment the
smp_read_barrier_depends(). ;-)
Definitely, there aren't many things which are more frustrating than barriers w/o comments explaining their pairing. I'm pairing store_release with read_barrier_depends as that's what RCU is doing. Is this the preferred way now? I like the new store_release and load_acquire as they document what's being barriered better but as Lai suggested in another reply it does seem a bit unbalanced. I wonder whether load_acquire_depends would make sense.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 12 lines · Tejun Heo
Hey, Paul.
Quoted 2 lines · Paul E. McKenney
Yep,
smp_load_acquire()orders its load against later loads and stores, so it really does need a memory barrier on weakly ordered systems.Yeap.
Quoted 3 lines
This is the "publish" operation for dynamically allocated per-CPU references? If so, agreed, you should be able to rely on dependency ordering. Make sure to comment the
smp_read_barrier_depends(). ;-)
Quoted 7 lines
Definitely, there aren't many things which are more frustrating than barriers w/o comments explaining their pairing. I'm pairing
store_releasewithread_barrier_dependsas that's what RCU is doing. Is this the preferred way now? I like the newstore_releaseandload_acquireas they document what's being barriered better but as Lai suggested in another reply it does seem a bit unbalanced. I wonder whetherload_acquire_dependswould make sense.
If you mean what I think you mean by load_acquire_depends(), it is spelled "rcu_dereference()" or, in this case, where you are never removing anything that has been added, "rcu_dereference_raw()". Because you are never removing anything, you don't need rcu_read_lock() or rcu_read_unlock(), thus you don't want lockdep yelling at you about not having RCU read-side critical sections, thus rcu_dereference_raw().
Thanx, Paul
Tejun Heo
maintainerRe: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Hello,
Quoted 6 lines · Paul E. McKenney
If you mean what I think you mean by
load_acquire_depends(), it is spelled "rcu_dereference()" or, in this case, where you are never removing anything that has been added, "rcu_dereference_raw()". Because you are never removing anything, you don't needrcu_read_lock()orrcu_read_unlock(), thus you don't want lockdep yelling at you about not having RCU read-side critical sections, thusrcu_dereference_raw().
Yeah, along that line but it's kinda weird to use rcu_dereference() when RCU isn't involved. It'd be clearer to have something like load_acquire_depends() and then define RCU deref in terms of it.
This is purely notational and clarifiying in the documentation is probably enough.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 10 lines · Tejun Heo
Hello,
Quoted 6 lines · Paul E. McKenney
If you mean what I think you mean by
load_acquire_depends(), it is spelled "rcu_dereference()" or, in this case, where you are never removing anything that has been added, "rcu_dereference_raw()". Because you are never removing anything, you don't needrcu_read_lock()orrcu_read_unlock(), thus you don't want lockdep yelling at you about not having RCU read-side critical sections, thusrcu_dereference_raw().
Quoted 6 lines
Yeah, along that line but it's kinda weird to use
rcu_dereference()when RCU isn't involved. It'd be clearer to have something likeload_acquire_depends()and then define RCU deref in terms of it.This is purely notational and clarifiying in the documentation is probably enough.
OK. If there end up being too many non-RCU uses of rcu_dereference_raw(), then it might make sense to create a new primitive. But it is not like we have any shortage of them just now. ;-)
Thanx, Paul
Tejun Heo
maintainerPATCH v2 6/6 percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
In reply to Tejun HeoNow that explicit invocation of percpu_ref_exit() is necessary to free the percpu counter, we can implement percpu_ref_reinit() which reinitializes a released percpu_ref. This can be used implement scalable gating switch which can be drained and then re-opened without worrying about memory allocation failures.
percpu_ref_is_zero() is added to be used in a sanity check in percpu_ref_exit(). As this function will be useful for other purposes too, make it a public interface.
v2: Use smp_read_barrier_depends() instead of smp_load_acquire(). We
only need data dep barrier and smp_load_acquire() is stronger and heavier on some archs. Spotted by Lai Jiangshan.
--- include/linux/percpu-refcount.h | 19 +++++++++++++++++++ lib/percpu-refcount.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
include/linux/percpu-refcount.h +19 -0
@@ -67,6 +67,7 @@ struct percpu_ref {int __must_check percpu_ref_init (struct percpu_ref *ref,percpu_ref_func_t *release); +void percpu_ref_reinit (struct percpu_ref *ref);void percpu_ref_exit (struct percpu_ref *ref);void percpu_ref_kill_and_confirm (struct percpu_ref *ref,percpu_ref_func_t *confirm_kill);
@@ -99,6 +100,9 @@ static inline bool __pcpu_ref_alive(stru {unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr); +/* paired with smp_store_release() in percpu_ref_reinit() */ +smp_read_barrier_depends (); +if (unlikely (pcpu_ptr &PCPU_REF_DEAD ))return false;
@@ -206,4 +210,19 @@ static inline void percpu_ref_put(struct rcu_read_unlock_sched(); } +/** +* percpu_ref_is_zero - test whether a percpu refcount reached zero +* @ref: percpu_ref to test +* +* Returns %true if @ref reached zero. +*/ +static inline bool percpu_ref_is_zero (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count; + +if (__pcpu_ref_alive (ref, &pcpu_count)) +return false; +return !atomic_read (&ref->count); +} +#endif
lib/percpu-refcount.c +35 -0
@@ -61,6 +61,41 @@ int percpu_ref_init(struct percpu_ref *rEXPORT_SYMBOL_GPL (percpu_ref_init);/** +* percpu_ref_reinit - re-initialize a percpu refcount +* @ref: perpcu_ref to re-initialize +* +* Re-initialize @ref so that it's in the same state as when it finished +* percpu_ref_init(). @ref must have been initialized successfully, killed +* and reached 0 but not exited. +* +* Note that percpu_ref_tryget[_live]() are safe to perform on @ref while +* this function is in progress. +*/ +void percpu_ref_reinit (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count =pcpu_count_ptr (ref); +int cpu; + +BUG_ON (!pcpu_count); +WARN_ON (!percpu_ref_is_zero (ref)); + +atomic_set (&ref->count,1 +PCPU_COUNT_BIAS ); + +/* +* Restore per-cpu operation. smp_store_release() is paired with +* smp_load_acquire() in __pcpu_ref_alive() and guarantees that the +* zeroing is visible to all percpu accesses which can see the +* following PCPU_REF_DEAD clearing. +*/ +for_each_possible_cpu (cpu) + *per_cpu_ptr (pcpu_count, cpu) =0 ; + +smp_store_release (&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD ); +} +EXPORT_SYMBOL_GPL (percpu_ref_reinit); + +/** * percpu_ref_exit - undo percpu_ref_init() * @ref: percpu_ref to exit *
Re: [PATCH v2 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 82 lines · Tejun Heo
Now that explicit invocation of
percpu_ref_exit()is necessary to free the percpu counter, we can implementpercpu_ref_reinit()which reinitializes a releasedpercpu_ref. This can be used implement scalable gating switch which can be drained and then re-opened without worrying about memory allocation failures.
percpu_ref_is_zero()is added to be used in a sanity check inpercpu_ref_exit(). As this function will be useful for other purposes too, make it a public interface.v2: Use
smp_read_barrier_depends()instead ofsmp_load_acquire(). We
only need data dep barrier andsmp_load_acquire()is stronger and heavier on some archs. Spotted by Lai Jiangshan.Signed-off-by Tejun Heo <[email protected]>Cc Kent Overstreet <[email protected]>Cc Christoph Lameter <[email protected]>Cc Lai Jiangshan <[email protected]>--- include/linux/percpu-refcount.h | 19 +++++++++++++++++++ lib/percpu-refcount.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)include/linux/percpu-refcount.h +19
-0@@ -67,6 +67,7 @@ struct percpu_ref {int __must_check percpu_ref_init (struct percpu_ref *ref,percpu_ref_func_t *release); +void percpu_ref_reinit (struct percpu_ref *ref);void percpu_ref_exit (struct percpu_ref *ref);void percpu_ref_kill_and_confirm (struct percpu_ref *ref,percpu_ref_func_t *confirm_kill);@@ -99,6 +100,9 @@ static inline bool __pcpu_ref_alive(stru {unsigned long pcpu_ptr =ACCESS_ONCE (ref->pcpu_count_ptr); +/* paired with smp_store_release() in percpu_ref_reinit() */ +smp_read_barrier_depends (); +if (unlikely (pcpu_ptr &PCPU_REF_DEAD ))return false;@@ -206,4 +210,19 @@ static inline void percpu_ref_put(struct rcu_read_unlock_sched(); } +/** +* percpu_ref_is_zero - test whether a percpu refcount reached zero +* @ref: percpu_ref to test +* +* Returns %true if @ref reached zero. +*/ +static inline bool percpu_ref_is_zero (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count; + +if (__pcpu_ref_alive (ref, &pcpu_count)) +return false; +return !atomic_read (&ref->count); +} +#endif lib/percpu-refcount.c +13
-0@@ -61,6 +61,41 @@ int percpu_ref_init(struct percpu_ref *rEXPORT_SYMBOL_GPL (percpu_ref_init);/** +* percpu_ref_reinit - re-initialize a percpu refcount +* @ref: perpcu_ref to re-initialize +* +* Re-initialize @ref so that it's in the same state as when it finished +* percpu_ref_init(). @ref must have been initialized successfully, killed +* and reached 0 but not exited. +* +* Note that percpu_ref_tryget[_live]() are safe to perform on @ref while +* this function is in progress. +*/ +void percpu_ref_reinit (struct percpu_ref *ref) +{ +unsigned __percpu *pcpu_count =pcpu_count_ptr (ref);
lib/percpu-refcount.c int percpu_ref_init(struct percpu_ref *r · line 86
Quoted 10 lines
lib/percpu-refcount.c +10
-0+int cpu; + +BUG_ON (!pcpu_count); +WARN_ON (!percpu_ref_is_zero (ref)); + +atomic_set (&ref->count,1 +PCPU_COUNT_BIAS ); + +/* +* Restore per-cpu operation. smp_store_release() is paired with +* smp_load_acquire() in __pcpu_ref_alive() and guarantees that the
s/smp_load_acquire()/smp_read_barrier_depends()/
s/smp_store_release()/smp_mb()/ if you accept my next comment.
lib/percpu-refcount.c int percpu_ref_init(struct percpu_ref *r · line 94
Quoted 8 lines
lib/percpu-refcount.c +8
-0+* zeroing is visible to all percpu accesses which can see the +* following PCPU_REF_DEAD clearing. +*/ +for_each_possible_cpu (cpu) + *per_cpu_ptr (pcpu_count, cpu) =0 ; + +smp_store_release (&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD );
I think it would be better if smp_mb() is used.
it is documented that smp_read_barrier_depends() and smp_mb() are paired. Not smp_read_barrier_depends() and smp_store_release().
Quoted 14 lines
lib/percpu-refcount.c +4
-1+} +EXPORT_SYMBOL_GPL (percpu_ref_reinit); + +/** * percpu_ref_exit - undo percpu_ref_init() * @ref: percpu_ref to exit * -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
.
Tejun Heo
maintainerRe: [PATCH v2 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 5 lines · Lai Jiangshan
lib/percpu-refcount.c int percpu_ref_init(struct percpu_ref *r · line 86
Quoted 3 lines
lib/percpu-refcount.c +3
-0+/* +* Restore per-cpu operation. smp_store_release() is paired with +* smp_load_acquire() in __pcpu_ref_alive() and guarantees that the s/smp_load_acquire()/smp_read_barrier_depends()/
Will update.
Quoted 12 lines
s/smp_store_release()/smp_mb()/ if you accept my next comment.
lib/percpu-refcount.c int percpu_ref_init(struct percpu_ref *r · line 94
Quoted 8 lines
lib/percpu-refcount.c +8
-0+* zeroing is visible to all percpu accesses which can see the +* following PCPU_REF_DEAD clearing. +*/ +for_each_possible_cpu (cpu) + *per_cpu_ptr (pcpu_count, cpu) =0 ; + +smp_store_release (&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD );I think it would be better if
smp_mb()is used.
smp_wmb() would be better here. We don't need the reader side.
Quoted 2 lines
it is documented that
smp_read_barrier_depends()andsmp_mb()are paired. Notsmp_read_barrier_depends()andsmp_store_release().
I don't know. I thought about doing that but the RCU accessors are pairing store_release with read_barrier_depends, so I don't think the particular paring is problematic and store_release is better at documenting what's being barriered.
Thanks.
Signature · 1 line
tejun
Re: [PATCH v2 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 22 lines · Tejun Heo
Quoted 5 lines · Lai Jiangshan
lib/percpu-refcount.c int percpu_ref_init(struct percpu_ref *r · line 86
Quoted 3 lines
lib/percpu-refcount.c +3
-0+/* +* Restore per-cpu operation. smp_store_release() is paired with +* smp_load_acquire() in __pcpu_ref_alive() and guarantees that the s/smp_load_acquire()/smp_read_barrier_depends()/
Will update.
Quoted 12 lines
s/smp_store_release()/smp_mb()/ if you accept my next comment.
lib/percpu-refcount.c int percpu_ref_init(struct percpu_ref *r · line 94
Quoted 8 lines
lib/percpu-refcount.c +8
-0+* zeroing is visible to all percpu accesses which can see the +* following PCPU_REF_DEAD clearing. +*/ +for_each_possible_cpu (cpu) + *per_cpu_ptr (pcpu_count, cpu) =0 ; + +smp_store_release (&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD );I think it would be better if
smp_mb()is used.
Quoted 4 lines
smp_wmb()would be better here. We don't need the reader side.Quoted 2 lines
it is documented that
smp_read_barrier_depends()andsmp_mb()are paired. Notsmp_read_barrier_depends()andsmp_store_release().
Well, sounds like the documentation needs an update, then. ;-)
For example, current rcu_assign_pointer() is a wrapper around smp_store_release().
Quoted 4 lines
I don't know. I thought about doing that but the RCU accessors are pairing
store_releasewithread_barrier_depends, so I don't think the particular paring is problematic andstore_releaseis better at documenting what's being barriered.
Which Tejun noted as well.
Thanx, Paul
Re: [PATCH v2 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 33 lines · Paul E. McKenney
Quoted 22 lines · Tejun Heo
Quoted 5 lines · Lai Jiangshan
Review
Quoted 3 lines
(quoted diff) +3
-0+ /* + * Restore per-cpu operation. smp_store_release() is paired with + * smp_load_acquire() in __pcpu_ref_alive() and guarantees that thes/smp_load_acquire()/smp_read_barrier_depends()/
Will update.
Quoted 12 lines
s/smp_store_release()/smp_mb()/ if you accept my next comment.
Review
Quoted 8 lines
(quoted diff) +8
-0+ * zeroing is visible to all percpu accesses which can see the + * following PCPU_REF_DEAD clearing. + */ + for_each_possible_cpu(cpu) + *per_cpu_ptr(pcpu_count, cpu) = 0; + + smp_store_release(&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD);I think it would be better if
smp_mb()is used.Quoted 4 lines
smp_wmb()would be better here. We don't need the reader side.Quoted 2 lines
it is documented that
smp_read_barrier_depends()andsmp_mb()are paired. Notsmp_read_barrier_depends()andsmp_store_release().Well, sounds like the documentation needs an update, then. ;-)
For example, current
rcu_assign_pointer()is a wrapper aroundsmp_store_release().
Quoted 6 lines
Quoted 4 lines
I don't know. I thought about doing that but the RCU accessors are pairing
store_releasewithread_barrier_depends, so I don't think the particular paring is problematic andstore_releaseis better at documenting what's being barriered.Which Tejun noted as well.
And here is a patch to update the documentation. Thoughts?
Thanx, Paul
------------------------------------------------------------------------
documentation: Add acquire/release barriers to pairing rules
It is possible to pair acquire and release barriers with other barriers, so this commit adds them to the list in the SMP barrier pairing section.
Documentation/memory-barriers.txt +6 -4
@@ -757,10 +757,12 @@ SMP BARRIER PAIRING When dealing with CPU-CPU interactions, certain types of memory barrier should always be paired. A lack of appropriate pairing is almost certainly an error. -A write barrier should always be paired with a data dependency barrier or read -barrier, though a general barrier would also be viable. Similarly a read -barrier or a data dependency barrier should always be paired with at least an -write barrier, though, again, a general barrier is viable: +A write barrier should always be paired with a data dependency barrier, +acquire barrier, release barrier, or read barrier, though a general +barrier would also be viable. Similarly a read barrier or a data +dependency barrier should always be paired with at least a write barrier, +an acquire barrier, or a release barrier, though, again, a general +barrier is viable: CPU 1 CPU 2 =============== ===============
Re: [PATCH v2 6/6] percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero()
Quoted 16 lines · Paul E. McKenney
documentation: Add acquire/release barriers to pairing rules
It is possible to pair acquire and release barriers with other barriers, so this commit adds them to the list in the SMP barrier pairing section.
Reported-by Lai Jiangshan <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Documentation/memory-barriers.txt +0
-0@@ -757,10 +757,12 @@ SMP BARRIER PAIRING When dealing with CPU-CPU interactions, certain types of memory barrier should always be paired. A lack of appropriate pairing is almost certainly an error.
Documentation/memory-barriers.txt SMP BARRIER PAIRING · line 765
Quoted 10 lines
Documentation/memory-barriers.txt +6
-4-A write barrier should always be paired with a data dependency barrier or read -barrier, though a general barrier would also be viable. Similarly a read -barrier or a data dependency barrier should always be paired with at least an -write barrier, though, again, a general barrier is viable: +A write barrier should always be paired with a data dependency barrier, +acquire barrier, release barrier, or read barrier, though a general +barrier would also be viable. Similarly a read barrier or a data +dependency barrier should always be paired with at least a write barrier, +an acquire barrier, or a release barrier, though, again, a general +barrier is viable:
FWIW,
Thanks.
Signature · 1 line
tejun
Quoted 17 lines · Tejun Heo
Hello,
Currently,
percpu_refdestruction - freeing the percpu counter - happens when thepercpu_refis released. This while a bit more convenient restricts howpercpu_refcan be used. It can't be initialized with static percpu area and it can't be reinitialized without going through percpu allocation.There are use cases which can take advantage of cycling
percpu_refthrough init/release multiple times. This patchset separates outpercpu_refdestruction intopercpu_exit()which should be invoked explicitly and introducespercpu_ref_reinit()which can be used to recycle a releasedpercpu_ref.This patchset doesn't add any users. Patchset to use this will soon be posted.
Quoted 8 lines
This patchset contains the following six patches.
0001-percpu-refcount-aio-use-percpu_ref_cancel_init-in-io.patch 0002-percpu-refcount-one-bit-is-enough-for-REF_STATUS.patch 0003-percpu-refcount-add-helpers-for-percpu_count-accesse.patch 0004-percpu-refcount-use-unsigned-long-for-pcpu_count-poi.patch 0005-percpu-refcount-require-percpu_ref-to-be-exited-expl.patch 0006-percpu-refcount-implement-percpu_ref_reinit-and-perc.patch
Applied to percpu/for-3.17.
Thanks.
Signature · 1 line
tejun