diff options
-rw-r--r-- | mm/oom_kill.c | 23 | ||||
-rw-r--r-- | mm/show_mem.c | 50 | ||||
-rw-r--r-- | mm/slab.h | 6 | ||||
-rw-r--r-- | mm/slab_common.c | 52 |
4 files changed, 97 insertions, 34 deletions
diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 25923cfec9c6..ad1ac9be0db4 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -169,27 +169,6 @@ static bool oom_unkillable_task(struct task_struct *p) return false; } -/* - * Check whether unreclaimable slab amount is greater than - * all user memory(LRU pages). - * dump_unreclaimable_slab() could help in the case that - * oom due to too much unreclaimable slab used by kernel. -*/ -static bool should_dump_unreclaim_slab(void) -{ - unsigned long nr_lru; - - nr_lru = global_node_page_state(NR_ACTIVE_ANON) + - global_node_page_state(NR_INACTIVE_ANON) + - global_node_page_state(NR_ACTIVE_FILE) + - global_node_page_state(NR_INACTIVE_FILE) + - global_node_page_state(NR_ISOLATED_ANON) + - global_node_page_state(NR_ISOLATED_FILE) + - global_node_page_state(NR_UNEVICTABLE); - - return (global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B) > nr_lru); -} - /** * oom_badness - heuristic function to determine which candidate task to kill * @p: task struct of which task we should calculate @@ -469,8 +448,6 @@ static void dump_header(struct oom_control *oc) mem_cgroup_print_oom_meminfo(oc->memcg); else { __show_mem(SHOW_MEM_FILTER_NODES, oc->nodemask, gfp_zone(oc->gfp_mask)); - if (should_dump_unreclaim_slab()) - dump_unreclaimable_slab(); } if (sysctl_oom_dump_tasks) dump_tasks(oc); diff --git a/mm/show_mem.c b/mm/show_mem.c index 0cf8bf5d832d..534848dd7209 100644 --- a/mm/show_mem.c +++ b/mm/show_mem.c @@ -7,15 +7,18 @@ #include <linux/blkdev.h> #include <linux/cma.h> +#include <linux/console.h> #include <linux/cpuset.h> #include <linux/highmem.h> #include <linux/hugetlb.h> #include <linux/mm.h> #include <linux/mmzone.h> +#include <linux/seq_buf.h> #include <linux/swap.h> #include <linux/vmstat.h> #include "internal.h" +#include "slab.h" #include "swap.h" atomic_long_t _totalram_pages __read_mostly; @@ -394,10 +397,31 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z show_swap_cache_info(); } +static void print_string_as_lines(const char *prefix, const char *lines) +{ + if (!lines) { + printk("%s (null)\n", prefix); + return; + } + + bool locked = console_trylock(); + + while (1) { + const char *p = strchrnul(lines, '\n'); + printk("%s%.*s\n", prefix, (int) (p - lines), lines); + if (!*p) + break; + lines = p + 1; + } + if (locked) + console_unlock(); +} + void __show_mem(unsigned int filter, nodemask_t *nodemask, int max_zone_idx) { unsigned long total = 0, reserved = 0, highmem = 0; struct zone *zone; + char *buf; printk("Mem-Info:\n"); show_free_areas(filter, nodemask, max_zone_idx); @@ -449,4 +473,30 @@ void __show_mem(unsigned int filter, nodemask_t *nodemask, int max_zone_idx) } } #endif + + const unsigned buf_size = 8192; + buf = kmalloc(buf_size, GFP_ATOMIC); + if (buf) { + struct seq_buf s; + + printk("Unreclaimable slab info:\n"); + seq_buf_init(&s, buf, buf_size); + dump_unreclaimable_slab(&s); + print_string_as_lines(KERN_NOTICE, seq_buf_str(&s)); + + static unsigned long shrinkers_last_print; + + /* Ratelimit to at most once every 30 seconds */ + if (!shrinkers_last_print || + time_after(jiffies, shrinkers_last_print + HZ * 30)) { + shrinkers_last_print = jiffies; + + printk("Shrinkers:\n"); + seq_buf_init(&s, buf, buf_size); + shrinkers_to_text(&s); + print_string_as_lines(KERN_NOTICE, seq_buf_str(&s)); + } + + kfree(buf); + } } diff --git a/mm/slab.h b/mm/slab.h index 05a21dc796e0..232f309d951b 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -611,10 +611,12 @@ static inline size_t slab_ksize(const struct kmem_cache *s) return s->size; } +struct seq_buf; + #ifdef CONFIG_SLUB_DEBUG -void dump_unreclaimable_slab(void); +void dump_unreclaimable_slab(struct seq_buf *); #else -static inline void dump_unreclaimable_slab(void) +static inline void dump_unreclaimable_slab(struct seq_buf *out) { } #endif diff --git a/mm/slab_common.c b/mm/slab_common.c index bfe7c40eeee1..a365f146e669 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -27,6 +27,7 @@ #include <asm/tlbflush.h> #include <asm/page.h> #include <linux/memcontrol.h> +#include <linux/seq_buf.h> #include <linux/stackdepot.h> #include <trace/events/rcu.h> @@ -1127,10 +1128,15 @@ static int slab_show(struct seq_file *m, void *p) return 0; } -void dump_unreclaimable_slab(void) +void dump_unreclaimable_slab(struct seq_buf *out) { struct kmem_cache *s; struct slabinfo sinfo; + struct slab_by_mem { + struct kmem_cache *s; + size_t total, active; + } slabs_by_mem[10], n; + int i, nr = 0; /* * Here acquiring slab_mutex is risky since we don't prefer to get @@ -1140,24 +1146,52 @@ void dump_unreclaimable_slab(void) * without acquiring the mutex. */ if (!mutex_trylock(&slab_mutex)) { - pr_warn("excessive unreclaimable slab but cannot dump stats\n"); + seq_buf_puts(out, "excessive unreclaimable slab but cannot dump stats\n"); return; } - pr_info("Unreclaimable slab info:\n"); - pr_info("Name Used Total\n"); - list_for_each_entry(s, &slab_caches, list) { if (s->flags & SLAB_RECLAIM_ACCOUNT) continue; get_slabinfo(s, &sinfo); - if (sinfo.num_objs > 0) - pr_info("%-17s %10luKB %10luKB\n", s->name, - (sinfo.active_objs * s->size) / 1024, - (sinfo.num_objs * s->size) / 1024); + if (!sinfo.num_objs) + continue; + + n.s = s; + n.total = sinfo.num_objs * s->size; + n.active = sinfo.active_objs * s->size; + + for (i = 0; i < nr; i++) + if (n.total < slabs_by_mem[i].total) + break; + + if (nr < ARRAY_SIZE(slabs_by_mem)) { + memmove(&slabs_by_mem[i + 1], + &slabs_by_mem[i], + sizeof(slabs_by_mem[0]) * (nr - i)); + nr++; + } else if (i) { + i--; + memmove(&slabs_by_mem[0], + &slabs_by_mem[1], + sizeof(slabs_by_mem[0]) * i); + } else { + continue; + } + + slabs_by_mem[i] = n; } + + for (i = nr - 1; i >= 0; --i) { + seq_buf_printf(out, "%-17s total: ", slabs_by_mem[i].s->name); + seq_buf_human_readable_u64(out, slabs_by_mem[i].total, STRING_UNITS_2); + seq_buf_printf(out, " active: "); + seq_buf_human_readable_u64(out, slabs_by_mem[i].active, STRING_UNITS_2); + seq_buf_putc(out, '\n'); + } + mutex_unlock(&slab_mutex); } |