Skip to content

Commit

Permalink
[LIB]: Introduce struct pcounter
Browse files Browse the repository at this point in the history
This just generalises what was introduced by Eric Dumazet for the struct proto
inuse field in 286ab3d:

    [NET]: Define infrastructure to keep 'inuse' changes in an efficent SMP/NUMA way.

Please look at the comment in there to see the rationale.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Arnaldo Carvalho de Melo authored and David S. Miller committed Jan 28, 2008
1 parent 0c88443 commit de4d1db
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
102 changes: 102 additions & 0 deletions include/linux/pcounter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#ifndef __LINUX_PCOUNTER_H
#define __LINUX_PCOUNTER_H

struct pcounter {
#ifdef CONFIG_SMP
void (*add)(struct pcounter *self, int inc);
int (*getval)(const struct pcounter *self);
int *per_cpu_values;
#else
int val;
#endif
};

/*
* Special macros to let pcounters use a fast version of {getvalue|add}
* using a static percpu variable per pcounter instead of an allocated one,
* saving one dereference.
* This might be changed if/when dynamic percpu vars become fast.
*/
#ifdef CONFIG_SMP
#include <linux/cpumask.h>
#include <linux/percpu.h>

#define DEFINE_PCOUNTER(NAME) \
static DEFINE_PER_CPU(int, NAME##_pcounter_values); \
static void NAME##_pcounter_add(struct pcounter *self, int inc) \
{ \
__get_cpu_var(NAME##_pcounter_values) += inc; \
} \
\
static int NAME##_pcounter_getval(const struct pcounter *self) \
{ \
int res = 0, cpu; \
\
for_each_possible_cpu(cpu) \
res += per_cpu(NAME##_pcounter_values, cpu); \
return res; \
}

#define PCOUNTER_MEMBER_INITIALIZER(NAME, MEMBER) \
MEMBER = { \
.add = NAME##_pcounter_add, \
.getval = NAME##_pcounter_getval, \
}

extern void pcounter_def_add(struct pcounter *self, int inc);
extern int pcounter_def_getval(const struct pcounter *self);

static inline int pcounter_alloc(struct pcounter *self)
{
int rc = 0;
if (self->add == NULL) {
self->per_cpu_values = alloc_percpu(int);
if (self->per_cpu_values != NULL) {
self->add = pcounter_def_add;
self->getval = pcounter_def_getval;
} else
rc = 1;
}
return rc;
}

static inline void pcounter_free(struct pcounter *self)
{
if (self->per_cpu_values != NULL) {
free_percpu(self->per_cpu_values);
self->per_cpu_values = NULL;
self->getval = NULL;
self->add = NULL;
}
}

static inline void pcounter_add(struct pcounter *self, int inc)
{
self->add(self, inc);
}

static inline int pcounter_getval(const struct pcounter *self)
{
return self->getval(self);
}

#else /* CONFIG_SMP */

static inline void pcounter_add(struct pcounter *self, int inc)
{
self->value += inc;
}

static inline int pcounter_getval(const struct pcounter *self)
{
return self->val;
}

#define DEFINE_PCOUNTER(NAME)
#define PCOUNTER_MEMBER_INITIALIZER(NAME, MEMBER)
#define pcounter_alloc(self) 0
#define pcounter_free(self)

#endif /* CONFIG_SMP */

#endif /* __LINUX_PCOUNTER_H */
1 change: 1 addition & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o
obj-$(CONFIG_TEXTSEARCH_BM) += ts_bm.o
obj-$(CONFIG_TEXTSEARCH_FSM) += ts_fsm.o
obj-$(CONFIG_SMP) += percpu_counter.o
obj-$(CONFIG_SMP) += pcounter.o
obj-$(CONFIG_AUDIT_GENERIC) += audit.o

obj-$(CONFIG_SWIOTLB) += swiotlb.o
Expand Down
26 changes: 26 additions & 0 deletions lib/pcounter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Define default pcounter functions
* Note that often used pcounters use dedicated functions to get a speed increase.
* (see DEFINE_PCOUNTER/REF_PCOUNTER_MEMBER)
*/

#include <linux/module.h>
#include <linux/pcounter.h>
#include <linux/smp.h>

void pcounter_def_add(struct pcounter *self, int inc)
{
per_cpu_ptr(self->per_cpu_values, smp_processor_id())[0] += inc;
}

EXPORT_SYMBOL_GPL(pcounter_def_add);

int pcounter_def_getval(const struct pcounter *self)
{
int res = 0, cpu;
for_each_possible_cpu(cpu)
res += per_cpu_ptr(self->per_cpu_values, cpu)[0];
return res;
}

EXPORT_SYMBOL_GPL(pcounter_def_getval);

0 comments on commit de4d1db

Please sign in to comment.