Commit e267992f authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/dennis/percpu

Pull percpu updates from Dennis Zhou:

 - percpu chunk depopulation - depopulate backing pages for chunks with
   empty pages when we exceed a global threshold without those pages.
   This lets us reclaim a portion of memory that would previously be
   lost until the full chunk would be freed (possibly never).

 - memcg accounting cleanup - previously separate chunks were managed
   for normal allocations and __GFP_ACCOUNT allocations. These are now
   consolidated which cleans up the code quite a bit.

 - a few misc clean ups for clang warnings

* 'for-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/dennis/percpu:
  percpu: optimize locking in pcpu_balance_workfn()
  percpu: initialize best_upa variable
  percpu: rework memcg accounting
  mm, memcg: introduce mem_cgroup_kmem_disabled()
  mm, memcg: mark cgroup_memory_nosocket, nokmem and noswap as __ro_after_init
  percpu: make symbol 'pcpu_free_slot' static
  percpu: implement partial chunk depopulation
  percpu: use pcpu_free_slot instead of pcpu_nr_slots - 1
  percpu: factor out pcpu_check_block_hint()
  percpu: split __pcpu_balance_workfn()
  percpu: fix a comment about the chunks ordering
parents 19b43859 e4d77700
......@@ -1619,6 +1619,7 @@ static inline void set_shrinker_bit(struct mem_cgroup *memcg,
#endif
#ifdef CONFIG_MEMCG_KMEM
bool mem_cgroup_kmem_disabled(void);
int __memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order);
void __memcg_kmem_uncharge_page(struct page *page, int order);
......@@ -1672,6 +1673,10 @@ static inline int memcg_cache_id(struct mem_cgroup *memcg)
struct mem_cgroup *mem_cgroup_from_obj(void *p);
#else
static inline bool mem_cgroup_kmem_disabled(void)
{
return true;
}
static inline int memcg_kmem_charge_page(struct page *page, gfp_t gfp,
int order)
......
......@@ -81,14 +81,14 @@ DEFINE_PER_CPU(struct mem_cgroup *, int_active_memcg);
EXPORT_PER_CPU_SYMBOL_GPL(int_active_memcg);
/* Socket memory accounting disabled? */
static bool cgroup_memory_nosocket;
static bool cgroup_memory_nosocket __ro_after_init;
/* Kernel memory accounting disabled? */
bool cgroup_memory_nokmem;
bool cgroup_memory_nokmem __ro_after_init;
/* Whether the swap controller is active */
#ifdef CONFIG_MEMCG_SWAP
bool cgroup_memory_noswap __read_mostly;
bool cgroup_memory_noswap __ro_after_init;
#else
#define cgroup_memory_noswap 1
#endif
......@@ -256,6 +256,11 @@ struct cgroup_subsys_state *vmpressure_to_css(struct vmpressure *vmpr)
#ifdef CONFIG_MEMCG_KMEM
extern spinlock_t css_set_lock;
bool mem_cgroup_kmem_disabled(void)
{
return cgroup_memory_nokmem;
}
static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,
unsigned int nr_pages);
......
......@@ -5,25 +5,6 @@
#include <linux/types.h>
#include <linux/percpu.h>
/*
* There are two chunk types: root and memcg-aware.
* Chunks of each type have separate slots list.
*
* Memcg-aware chunks have an attached vector of obj_cgroup pointers, which is
* used to store memcg membership data of a percpu object. Obj_cgroups are
* ref-counted pointers to a memory cgroup with an ability to switch dynamically
* to the parent memory cgroup. This allows to reclaim a deleted memory cgroup
* without reclaiming of all outstanding objects, which hold a reference at it.
*/
enum pcpu_chunk_type {
PCPU_CHUNK_ROOT,
#ifdef CONFIG_MEMCG_KMEM
PCPU_CHUNK_MEMCG,
#endif
PCPU_NR_CHUNK_TYPES,
PCPU_FAIL_ALLOC = PCPU_NR_CHUNK_TYPES
};
/*
* pcpu_block_md is the metadata block struct.
* Each chunk's bitmap is split into a number of full blocks.
......@@ -67,6 +48,8 @@ struct pcpu_chunk {
void *data; /* chunk data */
bool immutable; /* no [de]population allowed */
bool isolated; /* isolated from active chunk
slots */
int start_offset; /* the overlap with the previous
region to have a page aligned
base_addr */
......@@ -87,7 +70,9 @@ extern spinlock_t pcpu_lock;
extern struct list_head *pcpu_chunk_lists;
extern int pcpu_nr_slots;
extern int pcpu_nr_empty_pop_pages[];
extern int pcpu_sidelined_slot;
extern int pcpu_to_depopulate_slot;
extern int pcpu_nr_empty_pop_pages;
extern struct pcpu_chunk *pcpu_first_chunk;
extern struct pcpu_chunk *pcpu_reserved_chunk;
......@@ -128,37 +113,6 @@ static inline int pcpu_chunk_map_bits(struct pcpu_chunk *chunk)
return pcpu_nr_pages_to_map_bits(chunk->nr_pages);
}
#ifdef CONFIG_MEMCG_KMEM
static inline enum pcpu_chunk_type pcpu_chunk_type(struct pcpu_chunk *chunk)
{
if (chunk->obj_cgroups)
return PCPU_CHUNK_MEMCG;
return PCPU_CHUNK_ROOT;
}
static inline bool pcpu_is_memcg_chunk(enum pcpu_chunk_type chunk_type)
{
return chunk_type == PCPU_CHUNK_MEMCG;
}
#else
static inline enum pcpu_chunk_type pcpu_chunk_type(struct pcpu_chunk *chunk)
{
return PCPU_CHUNK_ROOT;
}
static inline bool pcpu_is_memcg_chunk(enum pcpu_chunk_type chunk_type)
{
return false;
}
#endif
static inline struct list_head *pcpu_chunk_list(enum pcpu_chunk_type chunk_type)
{
return &pcpu_chunk_lists[pcpu_nr_slots *
pcpu_is_memcg_chunk(chunk_type)];
}
#ifdef CONFIG_PERCPU_STATS
#include <linux/spinlock.h>
......
......@@ -44,8 +44,7 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk,
/* nada */
}
static struct pcpu_chunk *pcpu_create_chunk(enum pcpu_chunk_type type,
gfp_t gfp)
static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp)
{
const int nr_pages = pcpu_group_sizes[0] >> PAGE_SHIFT;
struct pcpu_chunk *chunk;
......@@ -53,7 +52,7 @@ static struct pcpu_chunk *pcpu_create_chunk(enum pcpu_chunk_type type,
unsigned long flags;
int i;
chunk = pcpu_alloc_chunk(type, gfp);
chunk = pcpu_alloc_chunk(gfp);
if (!chunk)
return NULL;
......@@ -118,3 +117,8 @@ static int __init pcpu_verify_alloc_info(const struct pcpu_alloc_info *ai)
return 0;
}
static bool pcpu_should_reclaim_chunk(struct pcpu_chunk *chunk)
{
return false;
}
......@@ -34,15 +34,11 @@ static int find_max_nr_alloc(void)
{
struct pcpu_chunk *chunk;
int slot, max_nr_alloc;
enum pcpu_chunk_type type;
max_nr_alloc = 0;
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++)
for (slot = 0; slot < pcpu_nr_slots; slot++)
list_for_each_entry(chunk, &pcpu_chunk_list(type)[slot],
list)
max_nr_alloc = max(max_nr_alloc,
chunk->nr_alloc);
for (slot = 0; slot < pcpu_nr_slots; slot++)
list_for_each_entry(chunk, &pcpu_chunk_lists[slot], list)
max_nr_alloc = max(max_nr_alloc, chunk->nr_alloc);
return max_nr_alloc;
}
......@@ -133,9 +129,6 @@ static void chunk_map_stats(struct seq_file *m, struct pcpu_chunk *chunk,
P("cur_min_alloc", cur_min_alloc);
P("cur_med_alloc", cur_med_alloc);
P("cur_max_alloc", cur_max_alloc);
#ifdef CONFIG_MEMCG_KMEM
P("memcg_aware", pcpu_is_memcg_chunk(pcpu_chunk_type(chunk)));
#endif
seq_putc(m, '\n');
}
......@@ -144,8 +137,6 @@ static int percpu_stats_show(struct seq_file *m, void *v)
struct pcpu_chunk *chunk;
int slot, max_nr_alloc;
int *buffer;
enum pcpu_chunk_type type;
int nr_empty_pop_pages;
alloc_buffer:
spin_lock_irq(&pcpu_lock);
......@@ -166,10 +157,6 @@ static int percpu_stats_show(struct seq_file *m, void *v)
goto alloc_buffer;
}
nr_empty_pop_pages = 0;
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++)
nr_empty_pop_pages += pcpu_nr_empty_pop_pages[type];
#define PL(X) \
seq_printf(m, " %-20s: %12lld\n", #X, (long long int)pcpu_stats_ai.X)
......@@ -201,7 +188,7 @@ static int percpu_stats_show(struct seq_file *m, void *v)
PU(nr_max_chunks);
PU(min_alloc_size);
PU(max_alloc_size);
P("empty_pop_pages", nr_empty_pop_pages);
P("empty_pop_pages", pcpu_nr_empty_pop_pages);
seq_putc(m, '\n');
#undef PU
......@@ -215,18 +202,17 @@ static int percpu_stats_show(struct seq_file *m, void *v)
chunk_map_stats(m, pcpu_reserved_chunk, buffer);
}
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++) {
for (slot = 0; slot < pcpu_nr_slots; slot++) {
list_for_each_entry(chunk, &pcpu_chunk_list(type)[slot],
list) {
if (chunk == pcpu_first_chunk) {
seq_puts(m, "Chunk: <- First Chunk\n");
chunk_map_stats(m, chunk, buffer);
} else {
seq_puts(m, "Chunk:\n");
chunk_map_stats(m, chunk, buffer);
}
}
for (slot = 0; slot < pcpu_nr_slots; slot++) {
list_for_each_entry(chunk, &pcpu_chunk_lists[slot], list) {
if (chunk == pcpu_first_chunk)
seq_puts(m, "Chunk: <- First Chunk\n");
else if (slot == pcpu_to_depopulate_slot)
seq_puts(m, "Chunk (to_depopulate)\n");
else if (slot == pcpu_sidelined_slot)
seq_puts(m, "Chunk (sidelined):\n");
else
seq_puts(m, "Chunk:\n");
chunk_map_stats(m, chunk, buffer);
}
}
......
......@@ -329,13 +329,12 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk,
pcpu_free_pages(chunk, pages, page_start, page_end);
}
static struct pcpu_chunk *pcpu_create_chunk(enum pcpu_chunk_type type,
gfp_t gfp)
static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp)
{
struct pcpu_chunk *chunk;
struct vm_struct **vms;
chunk = pcpu_alloc_chunk(type, gfp);
chunk = pcpu_alloc_chunk(gfp);
if (!chunk)
return NULL;
......@@ -378,3 +377,33 @@ static int __init pcpu_verify_alloc_info(const struct pcpu_alloc_info *ai)
/* no extra restriction */
return 0;
}
/**
* pcpu_should_reclaim_chunk - determine if a chunk should go into reclaim
* @chunk: chunk of interest
*
* This is the entry point for percpu reclaim. If a chunk qualifies, it is then
* isolated and managed in separate lists at the back of pcpu_slot: sidelined
* and to_depopulate respectively. The to_depopulate list holds chunks slated
* for depopulation. They no longer contribute to pcpu_nr_empty_pop_pages once
* they are on this list. Once depopulated, they are moved onto the sidelined
* list which enables them to be pulled back in for allocation if no other chunk
* can suffice the allocation.
*/
static bool pcpu_should_reclaim_chunk(struct pcpu_chunk *chunk)
{
/* do not reclaim either the first chunk or reserved chunk */
if (chunk == pcpu_first_chunk || chunk == pcpu_reserved_chunk)
return false;
/*
* If it is isolated, it may be on the sidelined list so move it back to
* the to_depopulate list. If we hit at least 1/4 pages empty pages AND
* there is no system-wide shortage of empty pages aside from this
* chunk, move it to the to_depopulate list.
*/
return ((chunk->isolated && chunk->nr_empty_pop_pages) ||
(pcpu_nr_empty_pop_pages >
(PCPU_EMPTY_POP_PAGES_HIGH + chunk->nr_empty_pop_pages) &&
chunk->nr_empty_pop_pages >= chunk->nr_pages / 4));
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment