Modify the "kmem" command to query and display the migration type of
pages and page blocks:
- Appending the migration type string (e.g., Unmovable, Movable,
Reclaimable, HighAtomic, CMA, Isolate) to the output of page flags
in kmem -p
- Showing the migration type for each free area sublist in kmem -f
and kmem -F when multiple lists are present per area
To support this, offsets and sizes for mem_section_usage,
mem_section_usage_pageblock_flags, and zone_pageblock_flags are
retrieved and cached. The pageblock flags bitmap is then parsed to
extract the migration type of each page block, matching the logic in the
Linux kernel buddy allocator.
An example excerpt of kmem -f is provided below:
ZONE NAME SIZE FREE MEM_MAP START_PADDR START_MAPNR
2 Normal 3680256 2752702 ffffedd284000000 100000000 1048575
AREA SIZE TYPE FREE_AREA_STRUCT BLOCKS PAGES
0 4k Unmovable ffff8a0b827d5f00 232 232
0 4k Movable ffff8a0b827d5f10 44 44
0 4k Reclaimable ffff8a0b827d5f20 0 0
0 4k HighAtomic ffff8a0b827d5f30 0 0
0 4k CMA ffff8a0b827d5f40 0 0
0 4k Isolate ffff8a0b827d5f50 0 0
1 8k Unmovable ffff8a0b827d5f68 382 764
1 8k Movable ffff8a0b827d5f78 104 208
1 8k Reclaimable ffff8a0b827d5f88 1 2
1 8k HighAtomic ffff8a0b827d5f98 0 0
Signed-off-by: Aaron Tomlin <atomlin(a)atomlin.com>
---
defs.h | 4 +
memory.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++---
symbols.c | 8 ++
3 files changed, 230 insertions(+), 10 deletions(-)
diff --git a/defs.h b/defs.h
index 1ee8744..ec8cc37 100644
--- a/defs.h
+++ b/defs.h
@@ -2108,6 +2108,9 @@ struct offset_table { /* stash of commonly-used
offsets */
long memory_block_state;
long memory_block_nid;
long mem_section_pageblock_flags;
+ long mem_section_usage;
+ long mem_section_usage_pageblock_flags;
+ long zone_pageblock_flags;
long bus_type_p;
long device_private_device;
long device_private_knode_bus;
@@ -2389,6 +2392,7 @@ struct size_table { /* stash of commonly-used sizes */
long task_struct_start_time;
long cputime_t;
long mem_section;
+ long mem_section_usage;
long pid_link;
long unwind_table;
long rlimit;
diff --git a/memory.c b/memory.c
index 3f3aa27..7ed59a4 100644
--- a/memory.c
+++ b/memory.c
@@ -171,6 +171,10 @@ static int get_bitfield_data(struct integer_data *);
static int show_page_member_data(char *, ulong, struct meminfo *, char *);
static void dump_mem_map(struct meminfo *);
static void dump_mem_map_SPARSEMEM(struct meminfo *);
+static ulong find_pfn_zone(ulong);
+static ulong get_pageblock_bitmap(ulong);
+static char *get_migratetype_name(int);
+static char *get_page_migration_type_str(physaddr_t);
static void fill_mem_map_cache(ulong, ulong, char *);
static void page_flags_init(void);
static int page_flags_init_from_pageflag_names(void);
@@ -1255,6 +1259,8 @@ vm_init(void)
"zone", "zone_start_pfn");
MEMBER_OFFSET_INIT(zone_spanned_pages,
"zone", "spanned_pages");
+ MEMBER_OFFSET_INIT(zone_pageblock_flags,
+ "zone", "pageblock_flags");
MEMBER_OFFSET_INIT(zone_present_pages,
"zone", "present_pages");
MEMBER_OFFSET_INIT(zone_pages_min,
@@ -6115,10 +6121,19 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
bufferindex += sprintflag("%sreserved");
bufferindex += sprintf(outputbuffer+bufferindex, "\n");
} else if (THIS_KERNEL_VERSION > LINUX(2,4,9)) {
- if (vt->flags & PAGEFLAGS)
+ char *mig_type = get_page_migration_type_str(phys);
+ if (vt->flags & PAGEFLAGS) {
bufferindex += translate_page_flags(outputbuffer+bufferindex, flags);
- else
- bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ if (strlen(mig_type) > 0 && bufferindex > 0 &&
outputbuffer[bufferindex-1] == '\n') {
+ bufferindex--;
+ bufferindex += sprintf(outputbuffer+bufferindex, " (%s)\n", mig_type);
+ }
+ } else {
+ if (strlen(mig_type) > 0)
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx (%s)\n", flags,
mig_type);
+ else
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ }
} else {
if ((flags >> v24_PG_locked) & 1)
@@ -6215,6 +6230,170 @@ display_members:
FREEBUF(page_cache);
}
+static ulong
+find_pfn_zone(ulong pfn)
+{
+ int n, i;
+ struct node_table *nt;
+ ulong node_zones;
+ ulong zone_start_pfn = 0, spanned_pages = 0;
+
+ if (INVALID_MEMBER(pglist_data_node_zones) ||
+ INVALID_MEMBER(zone_zone_start_pfn) ||
+ INVALID_MEMBER(zone_spanned_pages))
+ return 0;
+
+ for (n = 0; n < vt->numnodes; n++) {
+ nt = &vt->node_table[n];
+ node_zones = nt->pgdat + OFFSET(pglist_data_node_zones);
+
+ for (i = 0; i < vt->nr_zones; i++) {
+ if (!readmem(node_zones + OFFSET(zone_zone_start_pfn),
+ KVADDR, &zone_start_pfn, sizeof(ulong),
+ "zone zone_start_pfn", RETURN_ON_ERROR|QUIET))
+ goto next_zone;
+ if (!readmem(node_zones + OFFSET(zone_spanned_pages),
+ KVADDR, &spanned_pages, sizeof(ulong),
+ "zone spanned_pages", RETURN_ON_ERROR|QUIET))
+ goto next_zone;
+
+ if (pfn >= zone_start_pfn && pfn < zone_start_pfn + spanned_pages)
+ return node_zones;
+
+next_zone:
+ node_zones += SIZE(zone);
+ }
+ }
+ return 0;
+}
+
+static ulong
+get_pageblock_bitmap(ulong pfn)
+{
+ if (IS_SPARSEMEM()) {
+ ulong section_nr = pfn / PAGES_PER_SECTION();
+ ulong mem_section_addr = nr_to_section(section_nr);
+ if (!mem_section_addr)
+ return 0;
+
+ char *mem_section = read_mem_section(mem_section_addr);
+ if (!mem_section)
+ return 0;
+
+ if (VALID_MEMBER(mem_section_pageblock_flags)) {
+ return ULONG(mem_section + OFFSET(mem_section_pageblock_flags));
+ } else if (VALID_MEMBER(mem_section_usage)) {
+ ulong usage_addr = ULONG(mem_section + OFFSET(mem_section_usage));
+ if (!usage_addr)
+ return 0;
+ return usage_addr + OFFSET(mem_section_usage_pageblock_flags);
+ }
+ } else {
+ ulong zone_addr = find_pfn_zone(pfn);
+ if (zone_addr && VALID_MEMBER(zone_pageblock_flags)) {
+ ulong pageblock_flags_addr = 0;
+ if (readmem(zone_addr + OFFSET(zone_pageblock_flags),
+ KVADDR, &pageblock_flags_addr, sizeof(ulong),
+ "zone pageblock_flags", RETURN_ON_ERROR|QUIET)) {
+ return pageblock_flags_addr;
+ }
+ }
+ }
+ return 0;
+}
+
+static char *
+get_migratetype_name(int migratetype)
+{
+ static char name_buf[64];
+ ulong array_addr, string_addr;
+
+ if (kernel_symbol_exists("migratetype_names")) {
+ array_addr = symbol_value("migratetype_names") + migratetype * sizeof(void
*);
+ if (readmem(array_addr, KVADDR, &string_addr, sizeof(void *),
"migratetype_names entry", RETURN_ON_ERROR|QUIET)) {
+ if (read_string(string_addr, name_buf, sizeof(name_buf) - 1)) {
+ return name_buf;
+ }
+ }
+ }
+
+ switch (migratetype) {
+ case 0:
+ return "Unmovable";
+ case 1:
+ return "Movable";
+ case 2:
+ return "Reclaimable";
+ case 3:
+ return "HighAtomic";
+ case 4:
+ return "CMA";
+ case 5:
+ return "Isolate";
+ default:
+ sprintf(name_buf, "unknown_%d", migratetype);
+ return name_buf;
+ }
+}
+
+static char *
+get_page_migration_type_str(physaddr_t phys)
+{
+ ulong pfn = phys / PAGESIZE();
+ ulong pageblock_flags_addr = get_pageblock_bitmap(pfn);
+ if (!pageblock_flags_addr)
+ return "";
+
+ int nr_pageblock_bits = 4;
+ long pb_migrate_isolate = 0;
+ if (enumerator_value("PB_migrate_isolate", &pb_migrate_isolate)) {
+ nr_pageblock_bits = 8;
+ }
+
+ unsigned int pageblock_order = 10;
+ if (kernel_symbol_exists("pageblock_order")) {
+ get_symbol_data("pageblock_order", sizeof(unsigned int),
&pageblock_order);
+ }
+
+ int bitidx;
+ if (IS_SPARSEMEM()) {
+ bitidx = ((pfn & (PAGES_PER_SECTION() - 1)) >> pageblock_order) *
nr_pageblock_bits;
+ } else {
+ ulong zone_addr = find_pfn_zone(pfn);
+ ulong zone_start_pfn = 0;
+ if (zone_addr) {
+ if (!readmem(zone_addr + OFFSET(zone_zone_start_pfn),
+ KVADDR, &zone_start_pfn, sizeof(ulong),
+ "zone zone_start_pfn", RETURN_ON_ERROR|QUIET))
+ zone_start_pfn = 0;
+ }
+ bitidx = ((pfn - zone_start_pfn) >> pageblock_order) * nr_pageblock_bits;
+ }
+
+ ulong word_bitidx = bitidx / (sizeof(ulong) * 8);
+ bitidx &= ((sizeof(ulong) * 8) - 1);
+
+ ulong word_addr = pageblock_flags_addr + word_bitidx * sizeof(ulong);
+ ulong word = 0;
+ if (!readmem(word_addr, KVADDR, &word, sizeof(ulong), "pageblock_flags
word", RETURN_ON_ERROR|QUIET)) {
+ return "";
+ }
+
+ int migratetype = (word >> bitidx) & 7;
+ if (nr_pageblock_bits == 8) {
+ int isolate_bit = 4;
+ if ((word >> (bitidx + isolate_bit)) & 1) {
+ long migrate_isolate = 0;
+ if (enumerator_value("MIGRATE_ISOLATE", &migrate_isolate))
+ migratetype = migrate_isolate;
+ else
+ migratetype = 5;
+ }
+ }
+
+ return get_migratetype_name(migratetype);
+}
+
static void
dump_mem_map(struct meminfo *mi)
{
@@ -6566,10 +6745,19 @@ dump_mem_map(struct meminfo *mi)
bufferindex += sprintflag("%sreserved");
bufferindex += sprintf(outputbuffer+bufferindex, "\n");
} else if (THIS_KERNEL_VERSION > LINUX(2,4,9)) {
- if (vt->flags & PAGEFLAGS)
+ char *mig_type = get_page_migration_type_str(phys);
+ if (vt->flags & PAGEFLAGS) {
bufferindex += translate_page_flags(outputbuffer+bufferindex, flags);
- else
- bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ if (strlen(mig_type) > 0 && bufferindex > 0 &&
outputbuffer[bufferindex-1] == '\n') {
+ bufferindex--;
+ bufferindex += sprintf(outputbuffer+bufferindex, " (%s)\n", mig_type);
+ }
+ } else {
+ if (strlen(mig_type) > 0)
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx (%s)\n", flags,
mig_type);
+ else
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ }
} else {
if ((flags >> v24_PG_locked) & 1)
@@ -8412,8 +8600,12 @@ dump_zone_free_area(ulong free_area, int num, ulong verbose,
ld = &list_data;
- if (!verbose)
- fprintf(fp, "%s", free_area_hdr4);
+ if (!verbose) {
+ if (list_count > 1)
+ fprintf(fp, "AREA SIZE TYPE FREE_AREA_STRUCT BLOCKS
PAGES\n");
+ else
+ fprintf(fp, "%s", free_area_hdr4);
+ }
total_free = 0;
flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT"));
@@ -8487,14 +8679,21 @@ multiple_lists:
for (j = 0, free_list = free_area; j < list_count;
j++, free_list += SIZE(list_head)) {
- if (verbose)
- fprintf(fp, "%s", free_area_hdr3);
+ if (verbose) {
+ if (list_count > 1)
+ fprintf(fp, "AREA SIZE TYPE FREE_AREA_STRUCT\n");
+ else
+ fprintf(fp, "%s", free_area_hdr3);
+ }
fprintf(fp, "%3d ", i);
chunk_size = power(2, i);
sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024);
fprintf(fp, " %7s ", buf);
+ if (list_count > 1)
+ fprintf(fp, "%-12s ", get_migratetype_name(j));
+
readmem(free_list, KVADDR, free_list_buf,
SIZE(list_head), "free_area free_list",
FAULT_ON_ERROR);
@@ -17722,6 +17921,15 @@ sparse_mem_init(void)
MEMBER_OFFSET_INIT(mem_section_section_mem_map, "mem_section",
"section_mem_map");
+ if (MEMBER_EXISTS("mem_section", "pageblock_flags")) {
+ MEMBER_OFFSET_INIT(mem_section_pageblock_flags, "mem_section",
+ "pageblock_flags");
+ }
+ if (MEMBER_EXISTS("mem_section", "usage")) {
+ MEMBER_OFFSET_INIT(mem_section_usage, "mem_section", "usage");
+ STRUCT_SIZE_INIT(mem_section_usage, "mem_section_usage");
+ MEMBER_OFFSET_INIT(mem_section_usage_pageblock_flags, "mem_section_usage",
"pageblock_flags");
+ }
if (!MAX_PHYSMEM_BITS())
error(FATAL,
diff --git a/symbols.c b/symbols.c
index 78e400b..2e3a797 100644
--- a/symbols.c
+++ b/symbols.c
@@ -11416,6 +11416,12 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(mem_section_section_mem_map));
fprintf(fp, " mem_section_pageblock_flags: %ld\n",
OFFSET(mem_section_pageblock_flags));
+ fprintf(fp, " mem_section_usage: %ld\n",
+ OFFSET(mem_section_usage));
+ fprintf(fp, "mem_section_usage_pageblock_flags: %ld\n",
+ OFFSET(mem_section_usage_pageblock_flags));
+ fprintf(fp, " zone_pageblock_flags: %ld\n",
+ OFFSET(zone_pageblock_flags));
fprintf(fp, " memory_block_dev: %ld\n",
OFFSET(memory_block_dev));
fprintf(fp, " memory_block_nid: %ld\n",
@@ -12158,6 +12164,8 @@ dump_offset_table(char *spec, ulong makestruct)
SIZE(cputime_t));
fprintf(fp, " mem_section: %ld\n",
SIZE(mem_section));
+ fprintf(fp, " mem_section_usage: %ld\n",
+ SIZE(mem_section_usage));
fprintf(fp, " pid_link: %ld\n",
SIZE(pid_link));
fprintf(fp, " upid: %ld\n",
--
2.55.0