percpu_counter.c 3.1 KB
Newer Older
1 2 3 4 5
/*
 * Fast batching percpu counters.
 */

#include <linux/percpu_counter.h>
6 7 8 9
#include <linux/notifier.h>
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/cpu.h>
10 11
#include <linux/module.h>

12 13 14 15 16
#ifdef CONFIG_HOTPLUG_CPU
static LIST_HEAD(percpu_counters);
static DEFINE_MUTEX(percpu_counters_lock);
#endif

P
Peter Zijlstra 已提交
17 18 19 20 21 22 23 24 25 26 27 28 29 30
void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
{
	int cpu;

	spin_lock(&fbc->lock);
	for_each_possible_cpu(cpu) {
		s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
		*pcount = 0;
	}
	fbc->count = amount;
	spin_unlock(&fbc->lock);
}
EXPORT_SYMBOL(percpu_counter_set);

31
void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch)
32
{
33
	s64 count;
34
	s32 *pcount;
35 36 37 38
	int cpu = get_cpu();

	pcount = per_cpu_ptr(fbc->counters, cpu);
	count = *pcount + amount;
39
	if (count >= batch || count <= -batch) {
40 41 42 43 44 45 46 47 48
		spin_lock(&fbc->lock);
		fbc->count += count;
		*pcount = 0;
		spin_unlock(&fbc->lock);
	} else {
		*pcount = count;
	}
	put_cpu();
}
49
EXPORT_SYMBOL(__percpu_counter_add);
50 51 52 53 54

/*
 * Add up all the per-cpu counts, return the result.  This is a more accurate
 * but much slower version of percpu_counter_read_positive()
 */
P
Peter Zijlstra 已提交
55
s64 __percpu_counter_sum(struct percpu_counter *fbc)
56
{
57
	s64 ret;
58 59 60 61
	int cpu;

	spin_lock(&fbc->lock);
	ret = fbc->count;
62
	for_each_online_cpu(cpu) {
63
		s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
64 65 66
		ret += *pcount;
	}
	spin_unlock(&fbc->lock);
P
Peter Zijlstra 已提交
67
	return ret;
68
}
P
Peter Zijlstra 已提交
69
EXPORT_SYMBOL(__percpu_counter_sum);
70

P
Peter Zijlstra 已提交
71 72
static struct lock_class_key percpu_counter_irqsafe;

73
int percpu_counter_init(struct percpu_counter *fbc, s64 amount)
74 75 76 77
{
	spin_lock_init(&fbc->lock);
	fbc->count = amount;
	fbc->counters = alloc_percpu(s32);
78 79
	if (!fbc->counters)
		return -ENOMEM;
80 81 82 83 84
#ifdef CONFIG_HOTPLUG_CPU
	mutex_lock(&percpu_counters_lock);
	list_add(&fbc->list, &percpu_counters);
	mutex_unlock(&percpu_counters_lock);
#endif
85
	return 0;
86 87
}
EXPORT_SYMBOL(percpu_counter_init);
P
Peter Zijlstra 已提交
88 89 90 91 92 93 94 95 96 97

int percpu_counter_init_irq(struct percpu_counter *fbc, s64 amount)
{
	int err;

	err = percpu_counter_init(fbc, amount);
	if (!err)
		lockdep_set_class(&fbc->lock, &percpu_counter_irqsafe);
	return err;
}
98 99 100

void percpu_counter_destroy(struct percpu_counter *fbc)
{
101 102 103
	if (!fbc->counters)
		return;

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	free_percpu(fbc->counters);
#ifdef CONFIG_HOTPLUG_CPU
	mutex_lock(&percpu_counters_lock);
	list_del(&fbc->list);
	mutex_unlock(&percpu_counters_lock);
#endif
}
EXPORT_SYMBOL(percpu_counter_destroy);

#ifdef CONFIG_HOTPLUG_CPU
static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb,
					unsigned long action, void *hcpu)
{
	unsigned int cpu;
	struct percpu_counter *fbc;

	if (action != CPU_DEAD)
		return NOTIFY_OK;

	cpu = (unsigned long)hcpu;
	mutex_lock(&percpu_counters_lock);
	list_for_each_entry(fbc, &percpu_counters, list) {
		s32 *pcount;

		spin_lock(&fbc->lock);
		pcount = per_cpu_ptr(fbc->counters, cpu);
		fbc->count += *pcount;
		*pcount = 0;
		spin_unlock(&fbc->lock);
	}
	mutex_unlock(&percpu_counters_lock);
	return NOTIFY_OK;
}

static int __init percpu_counter_startup(void)
{
	hotcpu_notifier(percpu_counter_hotcpu_callback, 0);
	return 0;
}
module_init(percpu_counter_startup);
#endif