From: Masayoshi Mizuma <m.mizuma(a)jp.fujitsu.com>
Update for the "kmem -n" option to also dump memory block.
Currently, "kmem -n" shows the memory section only. This
patch gets available the memory block as well if 'memory_block'
structure and 'memory_subsys' symbol exist.
The memory block information is useful to investigate memory
hot-plug issue.
Signed-off-by: Masayoshi Mizuma <m.mizuma(a)jp.fujitsu.com>
---
defs.h | 9 ++
memory.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++---
symbols.c | 18 ++++
3 files changed, 299 insertions(+), 14 deletions(-)
diff --git a/defs.h b/defs.h
index 5b64bb7..20dd6a7 100644
--- a/defs.h
+++ b/defs.h
@@ -2049,6 +2049,15 @@ struct offset_table { /* stash of commonly-used
offsets */
long pci_bus_self;
long device_kobj;
long kobject_name;
+ long memory_block_dev;
+ long memory_block_start_section_nr;
+ long memory_block_end_section_nr;
+ long memory_block_state;
+ long memory_block_nid;
+ long mem_section_pageblock_flags;
+ long bus_type_p;
+ long device_private_device;
+ long device_private_knode_bus;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/memory.c b/memory.c
index ea25047..9657c28 100644
--- a/memory.c
+++ b/memory.c
@@ -255,13 +255,14 @@ static void PG_slab_flag_init(void);
static ulong nr_blockdev_pages(void);
void sparse_mem_init(void);
void dump_mem_sections(int);
+void dump_memory_blocks(int);
void list_mem_sections(void);
ulong sparse_decode_mem_map(ulong, ulong);
char *read_mem_section(ulong);
ulong nr_to_section(ulong);
int valid_section(ulong);
int section_has_mem_map(ulong);
-ulong section_mem_map_addr(ulong);
+ulong section_mem_map_addr(ulong, int);
ulong valid_section_nr(ulong);
ulong pfn_to_map(ulong);
static int get_nodes_online(void);
@@ -5528,7 +5529,7 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
pc->curcmd_flags |= HEADER_PRINTED;
}
- pp = section_mem_map_addr(section);
+ pp = section_mem_map_addr(section, 0);
pp = sparse_decode_mem_map(pp, section_nr);
phys = (physaddr_t) section_nr * PAGES_PER_SECTION() * PAGESIZE();
section_size = PAGES_PER_SECTION();
@@ -13389,7 +13390,7 @@ is_page_ptr(ulong addr, physaddr_t *phys)
nr_mem_sections = vt->max_mem_section_nr+1;
for (nr = 0; nr < nr_mem_sections ; nr++) {
if ((sec_addr = valid_section_nr(nr))) {
- coded_mem_map = section_mem_map_addr(sec_addr);
+ coded_mem_map = section_mem_map_addr(sec_addr, 0);
mem_map = sparse_decode_mem_map(coded_mem_map, nr);
end_mem_map = mem_map + (PAGES_PER_SECTION() * SIZE(page));
@@ -16354,8 +16355,10 @@ dump_memory_nodes(int initialize)
vt->numnodes = n;
}
- if (IS_SPARSEMEM())
+ if (IS_SPARSEMEM()) {
dump_mem_sections(initialize);
+ dump_memory_blocks(initialize);
+ }
}
/*
@@ -17140,7 +17143,7 @@ section_has_mem_map(ulong addr)
}
ulong
-section_mem_map_addr(ulong addr)
+section_mem_map_addr(ulong addr, int raw)
{
char *mem_section;
ulong map;
@@ -17148,7 +17151,8 @@ section_mem_map_addr(ulong addr)
if ((mem_section = read_mem_section(addr))) {
map = ULONG(mem_section +
OFFSET(mem_section_section_mem_map));
- map &= SECTION_MAP_MASK;
+ if (!raw)
+ map &= SECTION_MAP_MASK;
return map;
}
return 0;
@@ -17179,7 +17183,7 @@ pfn_to_map(ulong pfn)
if (section_has_mem_map(section)) {
page_offset = pfn - section_nr_to_pfn(section_nr);
- coded_mem_map = section_mem_map_addr(section);
+ coded_mem_map = section_mem_map_addr(section, 0);
mem_map = sparse_decode_mem_map(coded_mem_map, section_nr) +
(page_offset * SIZE(page));
return mem_map;
@@ -17188,16 +17192,33 @@ pfn_to_map(ulong pfn)
return 0;
}
+static void
+fill_mem_section_state(ulong state, char *buf)
+{
+ int bufidx = 0;
+
+ memset(buf, 0, sizeof(*buf) * BUFSIZE);
+
+ if (state & SECTION_MARKED_PRESENT)
+ bufidx += sprintf(buf + bufidx, "%s", "P");
+ if (state & SECTION_HAS_MEM_MAP)
+ bufidx += sprintf(buf + bufidx, "%s", "M");
+ if (state & SECTION_IS_ONLINE)
+ bufidx += sprintf(buf + bufidx, "%s", "O");
+}
+
void
dump_mem_sections(int initialize)
{
ulong nr, max, addr;
ulong nr_mem_sections;
ulong coded_mem_map, mem_map, pfn;
+ char statebuf[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char buf4[BUFSIZE];
+ char buf5[BUFSIZE];
nr_mem_sections = NR_MEM_SECTIONS();
@@ -17212,19 +17233,23 @@ dump_mem_sections(int initialize)
fprintf(fp, "\n");
pad_line(fp, BITS32() ? 59 : 67, '-');
- fprintf(fp, "\n\nNR %s %s %s PFN\n",
+ fprintf(fp, "\n\nNR %s %s %s %s PFN\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SECTION"),
mkstring(buf2, MAX(VADDR_PRLEN,strlen("CODED_MEM_MAP")),
CENTER|LJUST, "CODED_MEM_MAP"),
- mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "MEM_MAP"));
+ mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "MEM_MAP"),
+ mkstring(buf4, strlen("STATE"), CENTER, "STATE"));
for (nr = 0; nr < nr_mem_sections ; nr++) {
if ((addr = valid_section_nr(nr))) {
- coded_mem_map = section_mem_map_addr(addr);
+ coded_mem_map = section_mem_map_addr(addr, 0);
mem_map = sparse_decode_mem_map(coded_mem_map,nr);
pfn = section_nr_to_pfn(nr);
+ fill_mem_section_state(section_mem_map_addr(addr, 1),
+ statebuf);
- fprintf(fp, "%2ld %s %s %s %s\n",
+
+ fprintf(fp, "%2ld %s %s %s %s %s\n",
nr,
mkstring(buf1, VADDR_PRLEN,
CENTER|LONG_HEX, MKSTR(addr)),
@@ -17233,15 +17258,248 @@ dump_mem_sections(int initialize)
CENTER|LONG_HEX|RJUST, MKSTR(coded_mem_map)),
mkstring(buf3, VADDR_PRLEN,
CENTER|LONG_HEX|RJUST, MKSTR(mem_map)),
+ mkstring(buf4, strlen("STATE"), CENTER, statebuf),
pc->output_radix == 10 ?
- mkstring(buf4, VADDR_PRLEN,
+ mkstring(buf5, VADDR_PRLEN,
LONG_DEC|LJUST, MKSTR(pfn)) :
- mkstring(buf4, VADDR_PRLEN,
+ mkstring(buf5, VADDR_PRLEN,
LONG_HEX|LJUST, MKSTR(pfn)));
}
}
}
+#define MEM_ONLINE (1<<0)
+#define MEM_GOING_OFFLINE (1<<1)
+#define MEM_OFFLINE (1<<2)
+#define MEM_GOING_ONLINE (1<<3)
+#define MEM_CANCEL_ONLINE (1<<4)
+#define MEM_CANCEL_OFFLINE (1<<5)
+
+static void
+fill_memory_block_state(ulong memblock, char *buf)
+{
+ ulong state;
+
+ memset(buf, 0, sizeof(*buf) * BUFSIZE);
+
+ readmem(memblock + OFFSET(memory_block_state), KVADDR, &state,
+ sizeof(void *), "memory_block state", FAULT_ON_ERROR);
+
+ switch (state) {
+ case MEM_ONLINE:
+ sprintf(buf, "%s", "ONLINE");
+ break;
+ case MEM_GOING_OFFLINE:
+ sprintf(buf, "%s", "GOING_OFFLINE");
+ break;
+ case MEM_OFFLINE:
+ sprintf(buf, "%s", "OFFLINE");
+ break;
+ case MEM_GOING_ONLINE:
+ sprintf(buf, "%s", "GOING_ONLINE");
+ break;
+ case MEM_CANCEL_ONLINE:
+ sprintf(buf, "%s", "CANCEL_ONLINE");
+ break;
+ case MEM_CANCEL_OFFLINE:
+ sprintf(buf, "%s", "CANCEL_OFFLINE");
+ break;
+ default:
+ sprintf(buf, "%s", "UNKNOWN");
+ }
+}
+
+static ulong
+pfn_to_phys(ulong pfn)
+{
+ return pfn << PAGE_SHIFT;
+}
+
+static void
+fill_memory_block_name(ulong memblock, char *name)
+{
+ ulong kobj, value;
+
+ memset(name, 0, sizeof(*name) * BUFSIZE);
+
+ kobj = memblock + OFFSET(memory_block_dev) + OFFSET(device_kobj);
+
+ readmem(kobj + OFFSET(kobject_name),
+ KVADDR, &value, sizeof(void *), "kobject name",
+ FAULT_ON_ERROR);
+
+ read_string(value, name, BUFSIZE-1);
+}
+
+static void
+fill_memory_block_srange(ulong start_sec, ulong end_sec, char *srange)
+{
+ memset(srange, 0, sizeof(*srange) * BUFSIZE);
+
+ if (start_sec == end_sec)
+ sprintf(srange, "%lu", start_sec);
+ else
+ sprintf(srange, "%lu-%lu", start_sec, end_sec);
+}
+
+static void
+print_memory_block(ulong memory_block)
+{
+ ulong start_sec, end_sec, start_pfn, end_pfn, nid;
+ char statebuf[BUFSIZE];
+ char srangebuf[BUFSIZE];
+ char name[BUFSIZE];
+ char buf1[BUFSIZE];
+ char buf2[BUFSIZE];
+ char buf3[BUFSIZE];
+ char buf4[BUFSIZE];
+ char buf5[BUFSIZE];
+ char buf6[BUFSIZE];
+ char buf7[BUFSIZE];
+
+ readmem(memory_block + OFFSET(memory_block_start_section_nr), KVADDR,
+ &start_sec, sizeof(void *), "memory_block start_section_nr",
+ FAULT_ON_ERROR);
+ readmem(memory_block + OFFSET(memory_block_end_section_nr), KVADDR,
+ &end_sec, sizeof(void *), "memory_block end_section_nr",
+ FAULT_ON_ERROR);
+
+ start_pfn = section_nr_to_pfn(start_sec);
+ end_pfn = section_nr_to_pfn(end_sec + 1);
+ fill_memory_block_state(memory_block, statebuf);
+ fill_memory_block_name(memory_block, name);
+ fill_memory_block_srange(start_sec, end_sec, srangebuf);
+
+ if (MEMBER_EXISTS("memory_block", "nid")) {
+ readmem(memory_block + OFFSET(memory_block_nid), KVADDR, &nid,
+ sizeof(void *), "memory_block nid", FAULT_ON_ERROR);
+ fprintf(fp, " %s %s %s - %s %s %s %s\n",
+ mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX,
+ MKSTR(memory_block)),
+ mkstring(buf2, 12, CENTER, name),
+ mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX,
+ MKSTR(pfn_to_phys(start_pfn))),
+ mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX,
+ MKSTR(pfn_to_phys(end_pfn) - 1)),
+ mkstring(buf5, strlen("NODE"), CENTER|LONG_DEC,
+ MKSTR(nid)),
+ mkstring(buf6, strlen("CANCEL_OFFLINE"), LJUST,
+ statebuf),
+ mkstring(buf7, 12, LJUST, srangebuf));
+ } else
+ fprintf(fp, " %s %s %s - %s %s %s\n",
+ mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX,
+ MKSTR(memory_block)),
+ mkstring(buf2, 10, CENTER, name),
+ mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX,
+ MKSTR(pfn_to_phys(start_pfn))),
+ mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX,
+ MKSTR(pfn_to_phys(end_pfn) - 1)),
+ mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST,
+ statebuf),
+ mkstring(buf6, 12, LJUST, srangebuf));
+}
+
+static void
+init_memory_block_offset(void)
+{
+ MEMBER_OFFSET_INIT(bus_type_p, "bus_type", "p");
+ MEMBER_OFFSET_INIT(subsys_private_klist_devices,
+ "subsys_private", "klist_devices");
+ MEMBER_OFFSET_INIT(klist_k_list, "klist", "k_list");
+ MEMBER_OFFSET_INIT(klist_node_n_node, "klist_node", "n_node");
+ MEMBER_OFFSET_INIT(device_private_knode_bus,
+ "device_private", "knode_bus");
+ MEMBER_OFFSET_INIT(device_private_device, "device_private",
"device");
+ MEMBER_OFFSET_INIT(memory_block_dev, "memory_block", "dev");
+ MEMBER_OFFSET_INIT(memory_block_start_section_nr,
+ "memory_block", "start_section_nr");
+ MEMBER_OFFSET_INIT(memory_block_end_section_nr,
+ "memory_block", "end_section_nr");
+ MEMBER_OFFSET_INIT(memory_block_state, "memory_block", "state");
+ if (MEMBER_EXISTS("memory_block", "nid"))
+ MEMBER_OFFSET_INIT(memory_block_nid, "memory_block", "nid");
+}
+
+static void
+init_memory_block(struct list_data *ld, int *klistcnt, ulong **klistbuf)
+{
+ ulong memory_subsys = symbol_value("memory_subsys");
+ ulong private, klist, start;
+
+ init_memory_block_offset();
+
+ readmem(memory_subsys + OFFSET(bus_type_p), KVADDR, &private,
+ sizeof(void *), "memory_subsys.private", FAULT_ON_ERROR);
+ klist = private + OFFSET(subsys_private_klist_devices) +
+ OFFSET(klist_k_list);
+ BZERO(ld, sizeof(struct list_data));
+
+ readmem(klist, KVADDR, &start,
+ sizeof(void *), "klist klist", FAULT_ON_ERROR);
+
+ ld->start = start;
+ ld->end = klist;
+ ld->list_head_offset = OFFSET(klist_node_n_node) +
+ OFFSET(device_private_knode_bus);
+ hq_open();
+ *klistcnt = do_list(ld);
+ *klistbuf = (ulong *)GETBUF(*klistcnt * sizeof(ulong));
+ *klistcnt = retrieve_list(*klistbuf, *klistcnt);
+ hq_close();
+}
+
+void
+dump_memory_blocks(int initialize)
+{
+ ulong memory_block, device;
+ ulong *klistbuf;
+ int klistcnt, i;
+ struct list_data list_data;
+ char mb_hdr[BUFSIZE];
+ char buf1[BUFSIZE];
+ char buf2[BUFSIZE];
+ char buf3[BUFSIZE];
+ char buf4[BUFSIZE];
+ char buf5[BUFSIZE];
+ char buf6[BUFSIZE];
+
+ if ((!STRUCT_EXISTS("memory_block")) ||
+ (!symbol_exists("memory_subsys")))
+ return;
+
+ if (initialize)
+ return;
+
+ init_memory_block(&list_data, &klistcnt, &klistbuf);
+
+ if (MEMBER_EXISTS("memory_block", "nid"))
+ sprintf(mb_hdr, "\n%s %s %s %s %s %s\n",
+ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_BLOCK"),
+ mkstring(buf2, 10, CENTER, "NAME"),
+ mkstring(buf3, PADDR_PRLEN*2 + 2, CENTER, "PHYSICAL RANGE"),
+ mkstring(buf4, strlen("NODE"), CENTER, "NODE"),
+ mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST, "STATE"),
+ mkstring(buf6, 12, LJUST, "SECTIONS"));
+ else
+ sprintf(mb_hdr, "\n%s %s %s %s %s\n",
+ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_BLOCK"),
+ mkstring(buf2, 10, CENTER, "NAME"),
+ mkstring(buf3, PADDR_PRLEN*2, CENTER, "PHYSICAL RANGE"),
+ mkstring(buf4, strlen("CANCEL_OFFLINE"), LJUST, "STATE"),
+ mkstring(buf5, 12, LJUST, "SECTIONS"));
+ fprintf(fp, "%s", mb_hdr);
+
+ for (i = 0; i < klistcnt; i++) {
+ readmem(klistbuf[i] + OFFSET(device_private_device), KVADDR,
+ &device, sizeof(void *), "device_private device",
+ FAULT_ON_ERROR);
+ memory_block = device - OFFSET(memory_block_dev);
+ print_memory_block(memory_block);
+ }
+ FREEBUF(klistbuf);
+}
+
void
list_mem_sections(void)
{
@@ -17251,7 +17509,7 @@ list_mem_sections(void)
for (nr = 0; nr <= nr_mem_sections ; nr++) {
if ((addr = valid_section_nr(nr))) {
- coded_mem_map = section_mem_map_addr(addr);
+ coded_mem_map = section_mem_map_addr(addr, 0);
fprintf(fp,
"nr=%ld section = %lx coded_mem_map=%lx pfn=%ld mem_map=%lx\n",
nr,
diff --git a/symbols.c b/symbols.c
index cb2174b..c5aab14 100644
--- a/symbols.c
+++ b/symbols.c
@@ -9938,6 +9938,18 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(tss_struct_ist));
fprintf(fp, " mem_section_section_mem_map: %ld\n",
OFFSET(mem_section_section_mem_map));
+ fprintf(fp, " mem_section_pageblock_flags: %ld\n",
+ OFFSET(mem_section_pageblock_flags));
+ fprintf(fp, " memory_block_dev: %ld\n",
+ OFFSET(memory_block_dev));
+ fprintf(fp, " memory_block_nid: %ld\n",
+ OFFSET(memory_block_nid));
+ fprintf(fp, " memory_block_start_section_nr: %ld\n",
+ OFFSET(memory_block_start_section_nr));
+ fprintf(fp, " memory_block_end_section_nr: %ld\n",
+ OFFSET(memory_block_end_section_nr));
+ fprintf(fp, " memory_block_state: %ld\n",
+ OFFSET(memory_block_state));
fprintf(fp, " vcpu_guest_context_user_regs: %ld\n",
OFFSET(vcpu_guest_context_user_regs));
@@ -10031,6 +10043,8 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(unwind_idx_addr));
fprintf(fp, " unwind_idx_insn: %ld\n",
OFFSET(unwind_idx_insn));
+ fprintf(fp, " bus_type_p: %ld\n",
+ OFFSET(bus_type_p));
fprintf(fp, " class_devices: %ld\n",
OFFSET(class_devices));
fprintf(fp, " class_p: %ld\n",
@@ -10041,6 +10055,10 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(device_knode_class));
fprintf(fp, " device_node: %ld\n",
OFFSET(device_node));
+ fprintf(fp, " device_private_device: %ld\n",
+ OFFSET(device_private_device));
+ fprintf(fp, " device_private_knode_bus: %ld\n",
+ OFFSET(device_private_knode_bus));
fprintf(fp, " gendisk_dev: %ld\n",
OFFSET(gendisk_dev));
fprintf(fp, " gendisk_kobj: %ld\n",
--
2.19.0