Skip to content

Commit faf65dd

Browse files
rgushchindennisszhou
authored andcommitted
percpu: rework memcg accounting
The current implementation of the memcg accounting of the percpu memory is based on the idea of having two separate sets of chunks for accounted and non-accounted memory. This approach has an advantage of not wasting any extra memory for memcg data for non-accounted chunks, however it complicates the code and leads to a higher chunks number due to a lower chunk utilization. Instead of having two chunk types it's possible to declare all* chunks memcg-aware unless the kernel memory accounting is disabled globally by a boot option. The size of objcg_array is usually small in comparison to chunks themselves (it obviously depends on the number of CPUs), so even if some chunk will have no accounted allocations, the memory waste isn't significant and will likely be compensated by a higher chunk utilization. Also, with time more and more percpu allocations will likely become accounted. * The first chunk is initialized before the memory cgroup subsystem, so we don't know for sure whether we need to allocate obj_cgroups. Because it's small, let's make it free for use. Then we don't need to allocate obj_cgroups for it. Signed-off-by: Roman Gushchin <[email protected]> Signed-off-by: Dennis Zhou <[email protected]>
1 parent 4d5c8ae commit faf65dd

File tree

5 files changed

+76
-172
lines changed

5 files changed

+76
-172
lines changed

mm/percpu-internal.h

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,6 @@
55
#include <linux/types.h>
66
#include <linux/percpu.h>
77

8-
/*
9-
* There are two chunk types: root and memcg-aware.
10-
* Chunks of each type have separate slots list.
11-
*
12-
* Memcg-aware chunks have an attached vector of obj_cgroup pointers, which is
13-
* used to store memcg membership data of a percpu object. Obj_cgroups are
14-
* ref-counted pointers to a memory cgroup with an ability to switch dynamically
15-
* to the parent memory cgroup. This allows to reclaim a deleted memory cgroup
16-
* without reclaiming of all outstanding objects, which hold a reference at it.
17-
*/
18-
enum pcpu_chunk_type {
19-
PCPU_CHUNK_ROOT,
20-
#ifdef CONFIG_MEMCG_KMEM
21-
PCPU_CHUNK_MEMCG,
22-
#endif
23-
PCPU_NR_CHUNK_TYPES,
24-
PCPU_FAIL_ALLOC = PCPU_NR_CHUNK_TYPES
25-
};
26-
278
/*
289
* pcpu_block_md is the metadata block struct.
2910
* Each chunk's bitmap is split into a number of full blocks.
@@ -91,7 +72,7 @@ extern struct list_head *pcpu_chunk_lists;
9172
extern int pcpu_nr_slots;
9273
extern int pcpu_sidelined_slot;
9374
extern int pcpu_to_depopulate_slot;
94-
extern int pcpu_nr_empty_pop_pages[];
75+
extern int pcpu_nr_empty_pop_pages;
9576

9677
extern struct pcpu_chunk *pcpu_first_chunk;
9778
extern struct pcpu_chunk *pcpu_reserved_chunk;
@@ -132,37 +113,6 @@ static inline int pcpu_chunk_map_bits(struct pcpu_chunk *chunk)
132113
return pcpu_nr_pages_to_map_bits(chunk->nr_pages);
133114
}
134115

135-
#ifdef CONFIG_MEMCG_KMEM
136-
static inline enum pcpu_chunk_type pcpu_chunk_type(struct pcpu_chunk *chunk)
137-
{
138-
if (chunk->obj_cgroups)
139-
return PCPU_CHUNK_MEMCG;
140-
return PCPU_CHUNK_ROOT;
141-
}
142-
143-
static inline bool pcpu_is_memcg_chunk(enum pcpu_chunk_type chunk_type)
144-
{
145-
return chunk_type == PCPU_CHUNK_MEMCG;
146-
}
147-
148-
#else
149-
static inline enum pcpu_chunk_type pcpu_chunk_type(struct pcpu_chunk *chunk)
150-
{
151-
return PCPU_CHUNK_ROOT;
152-
}
153-
154-
static inline bool pcpu_is_memcg_chunk(enum pcpu_chunk_type chunk_type)
155-
{
156-
return false;
157-
}
158-
#endif
159-
160-
static inline struct list_head *pcpu_chunk_list(enum pcpu_chunk_type chunk_type)
161-
{
162-
return &pcpu_chunk_lists[pcpu_nr_slots *
163-
pcpu_is_memcg_chunk(chunk_type)];
164-
}
165-
166116
#ifdef CONFIG_PERCPU_STATS
167117

168118
#include <linux/spinlock.h>

mm/percpu-km.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,15 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk,
4444
/* nada */
4545
}
4646

47-
static struct pcpu_chunk *pcpu_create_chunk(enum pcpu_chunk_type type,
48-
gfp_t gfp)
47+
static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp)
4948
{
5049
const int nr_pages = pcpu_group_sizes[0] >> PAGE_SHIFT;
5150
struct pcpu_chunk *chunk;
5251
struct page *pages;
5352
unsigned long flags;
5453
int i;
5554

56-
chunk = pcpu_alloc_chunk(type, gfp);
55+
chunk = pcpu_alloc_chunk(gfp);
5756
if (!chunk)
5857
return NULL;
5958

mm/percpu-stats.c

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,11 @@ static int find_max_nr_alloc(void)
3434
{
3535
struct pcpu_chunk *chunk;
3636
int slot, max_nr_alloc;
37-
enum pcpu_chunk_type type;
3837

3938
max_nr_alloc = 0;
40-
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++)
41-
for (slot = 0; slot < pcpu_nr_slots; slot++)
42-
list_for_each_entry(chunk, &pcpu_chunk_list(type)[slot],
43-
list)
44-
max_nr_alloc = max(max_nr_alloc,
45-
chunk->nr_alloc);
39+
for (slot = 0; slot < pcpu_nr_slots; slot++)
40+
list_for_each_entry(chunk, &pcpu_chunk_lists[slot], list)
41+
max_nr_alloc = max(max_nr_alloc, chunk->nr_alloc);
4642

4743
return max_nr_alloc;
4844
}
@@ -133,9 +129,6 @@ static void chunk_map_stats(struct seq_file *m, struct pcpu_chunk *chunk,
133129
P("cur_min_alloc", cur_min_alloc);
134130
P("cur_med_alloc", cur_med_alloc);
135131
P("cur_max_alloc", cur_max_alloc);
136-
#ifdef CONFIG_MEMCG_KMEM
137-
P("memcg_aware", pcpu_is_memcg_chunk(pcpu_chunk_type(chunk)));
138-
#endif
139132
seq_putc(m, '\n');
140133
}
141134

@@ -144,8 +137,6 @@ static int percpu_stats_show(struct seq_file *m, void *v)
144137
struct pcpu_chunk *chunk;
145138
int slot, max_nr_alloc;
146139
int *buffer;
147-
enum pcpu_chunk_type type;
148-
int nr_empty_pop_pages;
149140

150141
alloc_buffer:
151142
spin_lock_irq(&pcpu_lock);
@@ -166,10 +157,6 @@ static int percpu_stats_show(struct seq_file *m, void *v)
166157
goto alloc_buffer;
167158
}
168159

169-
nr_empty_pop_pages = 0;
170-
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++)
171-
nr_empty_pop_pages += pcpu_nr_empty_pop_pages[type];
172-
173160
#define PL(X) \
174161
seq_printf(m, " %-20s: %12lld\n", #X, (long long int)pcpu_stats_ai.X)
175162

@@ -201,7 +188,7 @@ static int percpu_stats_show(struct seq_file *m, void *v)
201188
PU(nr_max_chunks);
202189
PU(min_alloc_size);
203190
PU(max_alloc_size);
204-
P("empty_pop_pages", nr_empty_pop_pages);
191+
P("empty_pop_pages", pcpu_nr_empty_pop_pages);
205192
seq_putc(m, '\n');
206193

207194
#undef PU
@@ -215,20 +202,17 @@ static int percpu_stats_show(struct seq_file *m, void *v)
215202
chunk_map_stats(m, pcpu_reserved_chunk, buffer);
216203
}
217204

218-
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++) {
219-
for (slot = 0; slot < pcpu_nr_slots; slot++) {
220-
list_for_each_entry(chunk, &pcpu_chunk_list(type)[slot],
221-
list) {
222-
if (chunk == pcpu_first_chunk)
223-
seq_puts(m, "Chunk: <- First Chunk\n");
224-
else if (slot == pcpu_to_depopulate_slot)
225-
seq_puts(m, "Chunk (to_depopulate)\n");
226-
else if (slot == pcpu_sidelined_slot)
227-
seq_puts(m, "Chunk (sidelined):\n");
228-
else
229-
seq_puts(m, "Chunk:\n");
230-
chunk_map_stats(m, chunk, buffer);
231-
}
205+
for (slot = 0; slot < pcpu_nr_slots; slot++) {
206+
list_for_each_entry(chunk, &pcpu_chunk_lists[slot], list) {
207+
if (chunk == pcpu_first_chunk)
208+
seq_puts(m, "Chunk: <- First Chunk\n");
209+
else if (slot == pcpu_to_depopulate_slot)
210+
seq_puts(m, "Chunk (to_depopulate)\n");
211+
else if (slot == pcpu_sidelined_slot)
212+
seq_puts(m, "Chunk (sidelined):\n");
213+
else
214+
seq_puts(m, "Chunk:\n");
215+
chunk_map_stats(m, chunk, buffer);
232216
}
233217
}
234218

mm/percpu-vm.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,12 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk,
328328
pcpu_free_pages(chunk, pages, page_start, page_end);
329329
}
330330

331-
static struct pcpu_chunk *pcpu_create_chunk(enum pcpu_chunk_type type,
332-
gfp_t gfp)
331+
static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp)
333332
{
334333
struct pcpu_chunk *chunk;
335334
struct vm_struct **vms;
336335

337-
chunk = pcpu_alloc_chunk(type, gfp);
336+
chunk = pcpu_alloc_chunk(gfp);
338337
if (!chunk)
339338
return NULL;
340339

@@ -403,7 +402,7 @@ static bool pcpu_should_reclaim_chunk(struct pcpu_chunk *chunk)
403402
* chunk, move it to the to_depopulate list.
404403
*/
405404
return ((chunk->isolated && chunk->nr_empty_pop_pages) ||
406-
(pcpu_nr_empty_pop_pages[pcpu_chunk_type(chunk)] >
407-
PCPU_EMPTY_POP_PAGES_HIGH + chunk->nr_empty_pop_pages &&
408-
chunk->nr_empty_pop_pages >= chunk->nr_pages / 4));
405+
(pcpu_nr_empty_pop_pages >
406+
(PCPU_EMPTY_POP_PAGES_HIGH + chunk->nr_empty_pop_pages) &&
407+
chunk->nr_empty_pop_pages >= chunk->nr_pages / 4));
409408
}

0 commit comments

Comments
 (0)