Skip to content

Per-CPU Reference Counter

include/sync/percpu_rc.h View source View on GitHub

Per-CPU Reference Counter

Fast, no contention reference counting by distributing RC inc/dec across per-CPU counters in fast path, using large bias and RCU GP to transition to atomic counter without lost updates

struct percpu_rc (5 fields) source
§ atomic_uintptr_t percpu_count_ptr
§ bool allow_reinit
§ struct rcu_cb rcu
enum percpu_rc_flags (2 variants) source
§ PERCPU_RC_INIT_ATOMIC = BIT(0)
§ PERCPU_RC_ALLOW_REINIT = BIT(1)
typedef void (*percpu_rc_release_fn)(struct percpu_rc * *)
source
int percpu_rc_init(
    struct percpu_rc *ref,
    percpu_rc_release_fn release,
    enum percpu_rc_flags flags
)
source
void percpu_rc_destroy(struct percpu_rc *ref)
source
void percpu_rc_kill(struct percpu_rc *ref)
source
void percpu_rc_reinit(struct percpu_rc *ref)
source
void percpu_rc_resurrect(struct percpu_rc *ref)
source
int64_t percpu_rc_read(struct percpu_rc *ref)
source
bool percpu_rc_is_percpu(uintptr_t pcpu)
source
void percpu_rc_getref(struct percpu_rc *ref)
source

Notes on why TOPOC_NONE is fine here:

percpu_rc is designed with the premise of “getting migrated, dec’ing the wrong counter, and all that other business will cause no problem”.

the eventual kill will deal with all the +‘s and -‘s, meaning that if you end up with

CPU0: rc == 9 CPU1: rc == -9

the eventual cleanup goes “9 - 9 = 0” and handles it perfectly ok

void percpu_rc_putref(struct percpu_rc *ref)
source
bool percpu_rc_try_getref(struct percpu_rc *ref)
source
bool percpu_rc_try_getref_live(struct percpu_rc *ref)
source
bool percpu_rc_is_zero(struct percpu_rc *ref)
source
bool percpu_rc_is_dying(struct percpu_rc *ref)
source
#define PERCPU_COUNT_BIAS (1LL << 30)
source
#define PERCPU_RC_DEAD BIT(0)
source
#define PERCPU_RC_ATOMIC BIT(1)
source
#define PERCPU_RC_PTR_MASK (~(PERCPU_RC_DEAD | PERCPU_RC_ATOMIC))
source
#define PERCPU_RC_PTR(m)
source
Expansion
#define PERCPU_RC_PTR(m) (atomic_int64_t *) (m & PERCPU_RC_PTR_MASK)