diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 025bdfce5165..a62dba9f2310 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -481,6 +481,8 @@ static void __init fdt_reserve_elfcorehdr(void) } memblock_reserve(elfcorehdr_addr, elfcorehdr_size); + memblock_memsize_record("elfcorehdr", elfcorehdr_addr, elfcorehdr_size, + false, false); pr_info("Reserving %llu KiB of memory at 0x%llx for elfcorehdr\n", elfcorehdr_size >> 10, elfcorehdr_addr); @@ -510,6 +512,7 @@ void __init early_init_fdt_scan_reserved_mem(void) if (!size) break; memblock_reserve(base, size); + memblock_memsize_record("memreserve", base, size, false, false); } } diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 45445a1600a9..4ea756ac1a69 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -539,6 +539,9 @@ static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem) nomap ? "nomap" : "map", reusable ? "reusable" : "non-reusable", rmem->name ? rmem->name : "unknown"); + + memblock_memsize_record(rmem->name, rmem->base, + rmem->size, nomap, reusable); } } diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 673d5cae7c81..4e3706832fcf 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -593,5 +593,12 @@ static inline void early_memtest(phys_addr_t start, phys_addr_t end) { } static inline void memtest_report_meminfo(struct seq_file *m) { } #endif +#ifdef CONFIG_MEMBLOCK_MEMSIZE +extern void __init_memblock memblock_memsize_record(const char *name, + phys_addr_t base, phys_addr_t size, bool nomap, bool reusable); +#else +static inline void __init_memblock memblock_memsize_record(const char *name, + phys_addr_t base, phys_addr_t size, bool nomap, bool reusable) { } +#endif #endif /* _LINUX_MEMBLOCK_H */ diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index 4c14c7853d0c..83ab8b629eff 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -287,6 +287,8 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, dma_contiguous_early_fixup(cma_get_base(*res_cma), cma_get_size(*res_cma)); + memblock_memsize_record("dma_cma", cma_get_base(*res_cma), + cma_get_size(*res_cma), false, true); return 0; } diff --git a/mm/Kconfig b/mm/Kconfig index 6e187ffd607d..ff2ee890dd47 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -506,6 +506,22 @@ config HAVE_GUP_FAST depends on MMU bool +config MAX_MEMBLOCK_MEMSIZE + int "Maximum number of tracking regions" + depends on MEMBLOCK_MEMSIZE + default 100 + range 0 200 + help + This number sets maximum number of tracking regions. If this is set to + 0, nothing will be saved. + +config MEMBLOCK_MEMSIZE + bool "memblock based reserved memory profiling" + default y + help + This patch introduce a node, memblock/memsize, to see reserved memory + easily. + # Don't discard allocated memory used to track "memory" and "reserved" memblocks # after early boot, so it can still be used to test for validity of memory. # Also, memblocks are updated with memory hot(un)plug. diff --git a/mm/memblock.c b/mm/memblock.c index d3395f008eb9..4f27c3f5bd16 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -19,6 +19,7 @@ #include #include +#include #include "internal.h" @@ -2035,6 +2036,65 @@ static int __init early_memblock(char *p) } early_param("memblock", early_memblock); +#ifdef CONFIG_MEMBLOCK_MEMSIZE + +#define NAME_SIZE 100 +struct memsize_rgn_struct { + phys_addr_t base; + long size; + bool nomap; /* 1/32 byte */ + bool reusable; /* 1/32 byte */ + char name[NAME_SIZE]; /* 30/32 byte */ +}; + +static struct memsize_rgn_struct memsize_rgn[CONFIG_MAX_MEMBLOCK_MEMSIZE] __initdata_memblock; +static int memsize_rgn_count __initdata_memblock; + +static void __init_memblock memsize_get_valid_name(char *valid_name, const char *name) +{ + char *head, *tail, *found; + int valid_size; + + head = (char *)name; + tail = head + strlen(name); + + /* get tail position after valid char */ + found = strchr(name, '@'); + if (found) + tail = found; + + valid_size = tail - head + 1; + if (valid_size > NAME_SIZE) + valid_size = NAME_SIZE; + strscpy(valid_name, head, valid_size); +} + +void __init_memblock memblock_memsize_record(const char *name, phys_addr_t base, + phys_addr_t size, bool nomap, bool reusable) +{ + struct memsize_rgn_struct *rgn; + phys_addr_t end; + + if (memsize_rgn_count == CONFIG_MAX_MEMBLOCK_MEMSIZE) { + pr_err("not enough space on memsize_rgn\n"); + return; + } + rgn = &memsize_rgn[memsize_rgn_count++]; + rgn->base = base; + rgn->size = size; + rgn->nomap = nomap; + rgn->reusable = reusable; + + if (!name) + strscpy(rgn->name, "unknown", sizeof(rgn->name)); + else + memsize_get_valid_name(rgn->name, name); + end = base + size - 1; + memblock_dbg("%s %pa..%pa nomap:%d reusable:%d\n", + __func__, &base, &end, nomap, reusable); +} +#endif /* MEMBLOCK_MEMSIZE */ + static void __init free_memmap(unsigned long start_pfn, unsigned long end_pfn) { struct page *start_pg, *end_pg; @@ -2416,6 +2476,61 @@ static int memblock_debug_show(struct seq_file *m, void *private) } DEFINE_SHOW_ATTRIBUTE(memblock_debug); +#ifdef CONFIG_MEMBLOCK_MEMSIZE + +static int memsize_rgn_cmp(const void *a, const void *b) +{ + const struct memsize_rgn_struct *ra = a, *rb = b; + + if (ra->base > rb->base) + return -1; + + if (ra->base < rb->base) + return 1; + + return 0; +} + +static int memblock_memsize_show(struct seq_file *m, void *private) +{ + int i; + struct memsize_rgn_struct *rgn; + unsigned long reserved = 0, reusable = 0; + + sort(memsize_rgn, memsize_rgn_count, + sizeof(memsize_rgn[0]), memsize_rgn_cmp, NULL); + for (i = 0; i < memsize_rgn_count; i++) { + phys_addr_t base, end; + long size; + + rgn = &memsize_rgn[i]; + base = rgn->base; + size = rgn->size; + end = base + size; + + seq_printf(m, "0x%pK-0x%pK 0x%08lx ( %7lu KB ) %s %s %s\n", + (void *)base, (void *)end, + size, DIV_ROUND_UP(size, SZ_1K), + rgn->nomap ? "nomap" : " map", + rgn->reusable ? "reusable" : "unusable", + rgn->name); + if (rgn->reusable) + reusable += (unsigned long)rgn->size; + else + reserved += (unsigned long)rgn->size; + } + + seq_puts(m, "\n"); + seq_printf(m, " .unusable : %7lu KB\n", + DIV_ROUND_UP(reserved, SZ_1K)); + seq_printf(m, " .reusable : %7lu KB\n", + DIV_ROUND_UP(reusable, SZ_1K)); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(memblock_memsize); +#endif + static int __init memblock_init_debugfs(void) { struct dentry *root = debugfs_create_dir("memblock", NULL); @@ -2428,6 +2543,10 @@ static int __init memblock_init_debugfs(void) debugfs_create_file("physmem", 0444, root, &physmem, &memblock_debug_fops); #endif +#ifdef CONFIG_MEMBLOCK_MEMSIZE + debugfs_create_file("memsize", 0444, root, + NULL, &memblock_memsize_fops); +#endif return 0; }