Skip to content

Commit c439d5e

Browse files
mjguzikdennisszhou
authored andcommitted
pcpcntr: add group allocation/free
Allocations and frees are globally serialized on the pcpu lock (and the CPU hotplug lock if enabled, which is the case on Debian). At least one frequent consumer allocates 4 back-to-back counters (and frees them in the same manner), exacerbating the problem. While this does not fully remedy scalability issues, it is a step towards that goal and provides immediate relief. Signed-off-by: Mateusz Guzik <[email protected]> Reviewed-by: Dennis Zhou <[email protected]> Reviewed-by: Vegard Nossum <[email protected]> Link: https://lore.kernel.org/r/[email protected] [Dennis: reflowed a few lines] Signed-off-by: Dennis Zhou <[email protected]>
1 parent f7d77df commit c439d5e

File tree

2 files changed

+77
-26
lines changed

2 files changed

+77
-26
lines changed

include/linux/percpu_counter.h

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,28 @@ struct percpu_counter {
3030

3131
extern int percpu_counter_batch;
3232

33-
int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp,
34-
struct lock_class_key *key);
33+
int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount,
34+
gfp_t gfp, u32 nr_counters,
35+
struct lock_class_key *key);
3536

36-
#define percpu_counter_init(fbc, value, gfp) \
37+
#define percpu_counter_init_many(fbc, value, gfp, nr_counters) \
3738
({ \
3839
static struct lock_class_key __key; \
3940
\
40-
__percpu_counter_init(fbc, value, gfp, &__key); \
41+
__percpu_counter_init_many(fbc, value, gfp, nr_counters,\
42+
&__key); \
4143
})
4244

43-
void percpu_counter_destroy(struct percpu_counter *fbc);
45+
46+
#define percpu_counter_init(fbc, value, gfp) \
47+
percpu_counter_init_many(fbc, value, gfp, 1)
48+
49+
void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters);
50+
static inline void percpu_counter_destroy(struct percpu_counter *fbc)
51+
{
52+
percpu_counter_destroy_many(fbc, 1);
53+
}
54+
4455
void percpu_counter_set(struct percpu_counter *fbc, s64 amount);
4556
void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount,
4657
s32 batch);
@@ -116,11 +127,27 @@ struct percpu_counter {
116127
s64 count;
117128
};
118129

130+
static inline int percpu_counter_init_many(struct percpu_counter *fbc,
131+
s64 amount, gfp_t gfp,
132+
u32 nr_counters)
133+
{
134+
u32 i;
135+
136+
for (i = 0; i < nr_counters; i++)
137+
fbc[i].count = amount;
138+
139+
return 0;
140+
}
141+
119142
static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount,
120143
gfp_t gfp)
121144
{
122-
fbc->count = amount;
123-
return 0;
145+
return percpu_counter_init_many(fbc, amount, gfp, 1);
146+
}
147+
148+
static inline void percpu_counter_destroy_many(struct percpu_counter *fbc,
149+
u32 nr_counters)
150+
{
124151
}
125152

126153
static inline void percpu_counter_destroy(struct percpu_counter *fbc)

lib/percpu_counter.c

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -151,48 +151,72 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc)
151151
}
152152
EXPORT_SYMBOL(__percpu_counter_sum);
153153

154-
int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp,
155-
struct lock_class_key *key)
154+
int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount,
155+
gfp_t gfp, u32 nr_counters,
156+
struct lock_class_key *key)
156157
{
157158
unsigned long flags __maybe_unused;
158-
159-
raw_spin_lock_init(&fbc->lock);
160-
lockdep_set_class(&fbc->lock, key);
161-
fbc->count = amount;
162-
fbc->counters = alloc_percpu_gfp(s32, gfp);
163-
if (!fbc->counters)
159+
size_t counter_size;
160+
s32 __percpu *counters;
161+
u32 i;
162+
163+
counter_size = ALIGN(sizeof(*counters), __alignof__(*counters));
164+
counters = __alloc_percpu_gfp(nr_counters * counter_size,
165+
__alignof__(*counters), gfp);
166+
if (!counters) {
167+
fbc[0].counters = NULL;
164168
return -ENOMEM;
169+
}
165170

166-
debug_percpu_counter_activate(fbc);
171+
for (i = 0; i < nr_counters; i++) {
172+
raw_spin_lock_init(&fbc[i].lock);
173+
lockdep_set_class(&fbc[i].lock, key);
174+
#ifdef CONFIG_HOTPLUG_CPU
175+
INIT_LIST_HEAD(&fbc[i].list);
176+
#endif
177+
fbc[i].count = amount;
178+
fbc[i].counters = (void *)counters + (i * counter_size);
179+
180+
debug_percpu_counter_activate(&fbc[i]);
181+
}
167182

168183
#ifdef CONFIG_HOTPLUG_CPU
169-
INIT_LIST_HEAD(&fbc->list);
170184
spin_lock_irqsave(&percpu_counters_lock, flags);
171-
list_add(&fbc->list, &percpu_counters);
185+
for (i = 0; i < nr_counters; i++)
186+
list_add(&fbc[i].list, &percpu_counters);
172187
spin_unlock_irqrestore(&percpu_counters_lock, flags);
173188
#endif
174189
return 0;
175190
}
176-
EXPORT_SYMBOL(__percpu_counter_init);
191+
EXPORT_SYMBOL(__percpu_counter_init_many);
177192

178-
void percpu_counter_destroy(struct percpu_counter *fbc)
193+
void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters)
179194
{
180195
unsigned long flags __maybe_unused;
196+
u32 i;
197+
198+
if (WARN_ON_ONCE(!fbc))
199+
return;
181200

182-
if (!fbc->counters)
201+
if (!fbc[0].counters)
183202
return;
184203

185-
debug_percpu_counter_deactivate(fbc);
204+
for (i = 0; i < nr_counters; i++)
205+
debug_percpu_counter_deactivate(&fbc[i]);
186206

187207
#ifdef CONFIG_HOTPLUG_CPU
188208
spin_lock_irqsave(&percpu_counters_lock, flags);
189-
list_del(&fbc->list);
209+
for (i = 0; i < nr_counters; i++)
210+
list_del(&fbc[i].list);
190211
spin_unlock_irqrestore(&percpu_counters_lock, flags);
191212
#endif
192-
free_percpu(fbc->counters);
193-
fbc->counters = NULL;
213+
214+
free_percpu(fbc[0].counters);
215+
216+
for (i = 0; i < nr_counters; i++)
217+
fbc[i].counters = NULL;
194218
}
195-
EXPORT_SYMBOL(percpu_counter_destroy);
219+
EXPORT_SYMBOL(percpu_counter_destroy_many);
196220

197221
int percpu_counter_batch __read_mostly = 32;
198222
EXPORT_SYMBOL(percpu_counter_batch);

0 commit comments

Comments
 (0)