| /* Copyright (C) 1991-2017, the Linux Kernel authors */ | 
 |  | 
 | /* | 
 |  * Fast batching percpu counters. | 
 |  */ | 
 |  | 
 | #include <percpu_counter.h> | 
 | #include <linux_compat.h> | 
 |  | 
 | void percpu_counter_set(struct percpu_counter *fbc, int64_t amount) | 
 | { | 
 | 	int cpu; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&fbc->lock); | 
 | 	for_each_possible_cpu(cpu) { | 
 | 		int32_t *pcount = _PERCPU_VARPTR(*fbc->counters, cpu); | 
 | 		*pcount = 0; | 
 | 	} | 
 | 	fbc->count = amount; | 
 | 	spin_unlock_irqsave(&fbc->lock); | 
 | } | 
 |  | 
 | /** | 
 |  * This function is both preempt and irq safe. The former is due to explicit | 
 |  * preemption disable. The latter is guaranteed by the fact that the slow path | 
 |  * is explicitly protected by an irq-safe spinlock whereas the fast patch uses | 
 |  * this_cpu_add which is irq-safe by definition. Hence there is no need muck | 
 |  * with irq state before calling this one | 
 |  */ | 
 | void percpu_counter_add_batch(struct percpu_counter *fbc, int64_t amount, | 
 | 			      int32_t batch) | 
 | { | 
 | 	int64_t count; | 
 |  | 
 | 	preempt_disable(); | 
 | 	count = __this_cpu_read(*fbc->counters) + amount; | 
 | 	if (count >= batch || count <= -batch) { | 
 | 		unsigned long flags; | 
 | 		spin_lock_irqsave(&fbc->lock); | 
 | 		fbc->count += count; | 
 | 		__this_cpu_sub(*fbc->counters, count - amount); | 
 | 		spin_unlock_irqsave(&fbc->lock); | 
 | 	} else { | 
 | 		this_cpu_add(*fbc->counters, amount); | 
 | 	} | 
 | 	preempt_enable(); | 
 | } | 
 |  | 
 | /* | 
 |  * Add up all the per-cpu counts, return the result.  This is a more accurate | 
 |  * but much slower version of percpu_counter_read_positive() | 
 |  */ | 
 | int64_t __percpu_counter_sum(struct percpu_counter *fbc) | 
 | { | 
 | 	int64_t ret; | 
 | 	int cpu; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&fbc->lock); | 
 | 	ret = fbc->count; | 
 | 	for_each_online_cpu(cpu) { | 
 | 		int32_t *pcount = _PERCPU_VARPTR(*fbc->counters, cpu); | 
 | 		ret += *pcount; | 
 | 	} | 
 | 	spin_unlock_irqsave(&fbc->lock); | 
 | 	return ret; | 
 | } | 
 |  | 
 | int percpu_counter_init(struct percpu_counter *fbc, int64_t amount, gfp_t gfp) | 
 | { | 
 | 	unsigned long flags __maybe_unused; | 
 |  | 
 | 	spinlock_init_irqsave(&fbc->lock); | 
 | 	fbc->count = amount; | 
 | 	fbc->counters = alloc_percpu_gfp(int32_t, gfp); | 
 | 	if (!fbc->counters) | 
 | 		return -ENOMEM; | 
 | 	return 0; | 
 | } | 
 |  | 
 | void percpu_counter_destroy(struct percpu_counter *fbc) | 
 | { | 
 | 	unsigned long flags __maybe_unused; | 
 |  | 
 | 	if (!fbc->counters) | 
 | 		return; | 
 | 	free_percpu(fbc->counters); | 
 | 	fbc->counters = NULL; | 
 | } | 
 |  | 
 | int percpu_counter_batch __read_mostly = 32; | 
 |  | 
 | /* | 
 |  * Compare counter against given value. | 
 |  * Return 1 if greater, 0 if equal and -1 if less | 
 |  */ | 
 | int __percpu_counter_compare(struct percpu_counter *fbc, int64_t rhs, | 
 | 		             int32_t batch) | 
 | { | 
 | 	int64_t	count; | 
 |  | 
 | 	count = percpu_counter_read(fbc); | 
 | 	/* Check to see if rough count will be sufficient for comparison */ | 
 | 	if (abs(count - rhs) > (batch * num_online_cpus())) { | 
 | 		if (count > rhs) | 
 | 			return 1; | 
 | 		else | 
 | 			return -1; | 
 | 	} | 
 | 	/* Need to use precise count */ | 
 | 	count = percpu_counter_sum(fbc); | 
 | 	if (count > rhs) | 
 | 		return 1; | 
 | 	else if (count < rhs) | 
 | 		return -1; | 
 | 	else | 
 | 		return 0; | 
 | } |