Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287/* SPDX-License-Identifier: GPL-2.0 */#ifndef _LINUX_PERCPU_COUNTER_H#define _LINUX_PERCPU_COUNTER_H/* * A simple "approximate counter" for use in ext2 and ext3 superblocks. * * WARNING: these things are HUGE. 4 kbytes per counter on 32-way P4. */
#include <linux/spinlock.h>#include <linux/smp.h>#include <linux/list.h>#include <linux/threads.h>#include <linux/percpu.h>#include <linux/types.h>
/* percpu_counter batch for local add or sub */#define PERCPU_COUNTER_LOCAL_BATCH INT_MAX
#ifdef CONFIG_SMP
struct percpu_counter { raw_spinlock_t lock; s64 count;#ifdef CONFIG_HOTPLUG_CPU struct list_head list; /* All percpu_counters are on a list */#endif s32 __percpu *counters;};
extern int percpu_counter_batch;
int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount, gfp_t gfp, u32 nr_counters, struct lock_class_key *key);
#define percpu_counter_init_many(fbc, value, gfp, nr_counters) \ ({ \ static struct lock_class_key __key; \ \ __percpu_counter_init_many(fbc, value, gfp, nr_counters,\ &__key); \ })
#define percpu_counter_init(fbc, value, gfp) \ percpu_counter_init_many(fbc, value, gfp, 1)
void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters);static inline void percpu_counter_destroy(struct percpu_counter *fbc){ percpu_counter_destroy_many(fbc, 1);}
void percpu_counter_set(struct percpu_counter *fbc, s64 amount);void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch);s64 __percpu_counter_sum(struct percpu_counter *fbc);int __percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch);bool __percpu_counter_limited_add(struct percpu_counter *fbc, s64 limit, s64 amount, s32 batch);void percpu_counter_sync(struct percpu_counter *fbc);
static inline int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs){ return __percpu_counter_compare(fbc, rhs, percpu_counter_batch);}
static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount){ percpu_counter_add_batch(fbc, amount, percpu_counter_batch);}
static inline boolpercpu_counter_limited_add(struct percpu_counter *fbc, s64 limit, s64 amount){ return __percpu_counter_limited_add(fbc, limit, amount, percpu_counter_batch);}
/* * With percpu_counter_add_local() and percpu_counter_sub_local(), counts * are accumulated in local per cpu counter and not in fbc->count until * local count overflows PERCPU_COUNTER_LOCAL_BATCH. This makes counter * write efficient. * But percpu_counter_sum(), instead of percpu_counter_read(), needs to be * used to add up the counts from each CPU to account for all the local * counts. So percpu_counter_add_local() and percpu_counter_sub_local() * should be used when a counter is updated frequently and read rarely. */static inline voidpercpu_counter_add_local(struct percpu_counter *fbc, s64 amount){ percpu_counter_add_batch(fbc, amount, PERCPU_COUNTER_LOCAL_BATCH);}
static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc){ s64 ret = __percpu_counter_sum(fbc); return ret < 0 ? 0 : ret;}
static inline s64 percpu_counter_sum(struct percpu_counter *fbc){ return __percpu_counter_sum(fbc);}
static inline s64 percpu_counter_read(struct percpu_counter *fbc){ return fbc->count;}
/* * It is possible for the percpu_counter_read() to return a small negative * number for some counter which should never be negative. * */static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc){ /* Prevent reloads of fbc->count */ s64 ret = READ_ONCE(fbc->count);
if (ret >= 0) return ret; return 0;}
static inline bool percpu_counter_initialized(struct percpu_counter *fbc){ return (fbc->counters != NULL);}
#else /* !CONFIG_SMP */
struct percpu_counter { s64 count;};
static inline int percpu_counter_init_many(struct percpu_counter *fbc, s64 amount, gfp_t gfp, u32 nr_counters){ u32 i;
for (i = 0; i < nr_counters; i++) fbc[i].count = amount;
return 0;}
static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp){ return percpu_counter_init_many(fbc, amount, gfp, 1);}
static inline void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters){}
static inline void percpu_counter_destroy(struct percpu_counter *fbc){}
static inline void percpu_counter_set(struct percpu_counter *fbc, s64 amount){ fbc->count = amount;}
static inline int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs){ if (fbc->count > rhs) return 1; else if (fbc->count < rhs) return -1; else return 0;}
static inline int__percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch){ return percpu_counter_compare(fbc, rhs);}
static inline voidpercpu_counter_add(struct percpu_counter *fbc, s64 amount){ unsigned long flags;
local_irq_save(flags); fbc->count += amount; local_irq_restore(flags);}
static inline boolpercpu_counter_limited_add(struct percpu_counter *fbc, s64 limit, s64 amount){ unsigned long flags; bool good = false; s64 count;
if (amount == 0) return true;
local_irq_save(flags); count = fbc->count + amount; if ((amount > 0 && count <= limit) || (amount < 0 && count >= limit)) { fbc->count = count; good = true; } local_irq_restore(flags); return good;}
/* non-SMP percpu_counter_add_local is the same with percpu_counter_add */static inline voidpercpu_counter_add_local(struct percpu_counter *fbc, s64 amount){ percpu_counter_add(fbc, amount);}
static inline voidpercpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch){ percpu_counter_add(fbc, amount);}
static inline s64 percpu_counter_read(struct percpu_counter *fbc){ return fbc->count;}
/* * percpu_counter is intended to track positive numbers. In the UP case the * number should never be negative. */static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc){ return fbc->count;}
static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc){ return percpu_counter_read_positive(fbc);}
static inline s64 percpu_counter_sum(struct percpu_counter *fbc){ return percpu_counter_read(fbc);}
static inline bool percpu_counter_initialized(struct percpu_counter *fbc){ return true;}
static inline void percpu_counter_sync(struct percpu_counter *fbc){}#endif /* CONFIG_SMP */
static inline void percpu_counter_inc(struct percpu_counter *fbc){ percpu_counter_add(fbc, 1);}
static inline void percpu_counter_dec(struct percpu_counter *fbc){ percpu_counter_add(fbc, -1);}
static inline void percpu_counter_sub(struct percpu_counter *fbc, s64 amount){ percpu_counter_add(fbc, -amount);}
static inline voidpercpu_counter_sub_local(struct percpu_counter *fbc, s64 amount){ percpu_counter_add_local(fbc, -amount);}
#endif /* _LINUX_PERCPU_COUNTER_H */