linux/lib/percpu_counter.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Fast batching percpu counters.
 */

#include <linux/percpu_counter.h>
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/debugobjects.h>

#ifdef CONFIG_HOTPLUG_CPU
static LIST_HEAD(percpu_counters);
static DEFINE_SPINLOCK(percpu_counters_lock);
#endif

#ifdef CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER

static const struct debug_obj_descr percpu_counter_debug_descr;

static bool percpu_counter_fixup_free(void *addr, enum debug_obj_state state)
{}

static const struct debug_obj_descr percpu_counter_debug_descr =;

static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
{}

static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
{}

#else	/* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */
static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
{ }
static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
{ }
#endif	/* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */

void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
{}
EXPORT_SYMBOL();

/*
 * Add to a counter while respecting batch size.
 *
 * There are 2 implementations, both dealing with the following problem:
 *
 * The decision slow path/fast path and the actual update must be atomic.
 * Otherwise a call in process context could check the current values and
 * decide that the fast path can be used. If now an interrupt occurs before
 * the this_cpu_add(), and the interrupt updates this_cpu(*fbc->counters),
 * then the this_cpu_add() that is executed after the interrupt has completed
 * can produce values larger than "batch" or even overflows.
 */
#ifdef CONFIG_HAVE_CMPXCHG_LOCAL
/*
 * Safety against interrupts is achieved in 2 ways:
 * 1. the fast path uses local cmpxchg (note: no lock prefix)
 * 2. the slow path operates with interrupts disabled
 */
void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
{}
#else
/*
 * local_irq_save() is used to make the function irq safe:
 * - The slow path would be ok as protected by an irq-safe spinlock.
 * - this_cpu_add would be ok as it is irq-safe by definition.
 */
void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
{
	s64 count;
	unsigned long flags;

	local_irq_save(flags);
	count = __this_cpu_read(*fbc->counters) + amount;
	if (abs(count) >= batch) {
		raw_spin_lock(&fbc->lock);
		fbc->count += count;
		__this_cpu_sub(*fbc->counters, count - amount);
		raw_spin_unlock(&fbc->lock);
	} else {
		this_cpu_add(*fbc->counters, amount);
	}
	local_irq_restore(flags);
}
#endif
EXPORT_SYMBOL();

/*
 * For percpu_counter with a big batch, the devication of its count could
 * be big, and there is requirement to reduce the deviation, like when the
 * counter's batch could be runtime decreased to get a better accuracy,
 * which can be achieved by running this sync function on each CPU.
 */
void percpu_counter_sync(struct percpu_counter *fbc)
{}
EXPORT_SYMBOL();

/*
 * Add up all the per-cpu counts, return the result.  This is a more accurate
 * but much slower version of percpu_counter_read_positive().
 *
 * We use the cpu mask of (cpu_online_mask | cpu_dying_mask) to capture sums
 * from CPUs that are in the process of being taken offline. Dying cpus have
 * been removed from the online mask, but may not have had the hotplug dead
 * notifier called to fold the percpu count back into the global counter sum.
 * By including dying CPUs in the iteration mask, we avoid this race condition
 * so __percpu_counter_sum() just does the right thing when CPUs are being taken
 * offline.
 */
s64 __percpu_counter_sum(struct percpu_counter *fbc)
{}
EXPORT_SYMBOL();

int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount,
			       gfp_t gfp, u32 nr_counters,
			       struct lock_class_key *key)
{}
EXPORT_SYMBOL();

void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters)
{}
EXPORT_SYMBOL();

int percpu_counter_batch __read_mostly =;
EXPORT_SYMBOL();

static int compute_batch_value(unsigned int cpu)
{}

static int percpu_counter_cpu_dead(unsigned int cpu)
{}

/*
 * Compare counter against given value.
 * Return 1 if greater, 0 if equal and -1 if less
 */
int __percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch)
{}
EXPORT_SYMBOL();

/*
 * Compare counter, and add amount if total is: less than or equal to limit if
 * amount is positive, or greater than or equal to limit if amount is negative.
 * Return true if amount is added, or false if total would be beyond the limit.
 *
 * Negative limit is allowed, but unusual.
 * When negative amounts (subs) are given to percpu_counter_limited_add(),
 * the limit would most naturally be 0 - but other limits are also allowed.
 *
 * Overflow beyond S64_MAX is not allowed for: counter, limit and amount
 * are all assumed to be sane (far from S64_MIN and S64_MAX).
 */
bool __percpu_counter_limited_add(struct percpu_counter *fbc,
				  s64 limit, s64 amount, s32 batch)
{}

static int __init percpu_counter_startup(void)
{}
module_init();