From: Masayoshi Mizuma <m.mizuma@xxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxx> --- defs.h | 8 ++ memory.c | 412 +++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 379 insertions(+), 41 deletions(-) diff --git a/defs.h b/defs.h index 5b64bb7..f707c64 100644 --- a/defs.h +++ b/defs.h @@ -2049,6 +2049,14 @@ 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 mem_section_pageblock_flags; + long memory_block_state; + long memory_block_nid; + 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..c7a4787 100644 --- a/memory.c +++ b/memory.c @@ -254,14 +254,16 @@ static void PG_reserved_flag_init(void); 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_mem_block_and_sections(int); +void _dump_mem_block_and_sections(void); +void dump_mem_sections(void); 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 +5530,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 +13391,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)); @@ -16355,7 +16357,7 @@ dump_memory_nodes(int initialize) } if (IS_SPARSEMEM()) - dump_mem_sections(initialize); + dump_mem_block_and_sections(initialize); } /* @@ -17140,7 +17142,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 +17150,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 +17182,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 +17191,365 @@ pfn_to_map(ulong pfn) return 0; } -void -dump_mem_sections(int initialize) +struct memory_block_info { + ulong memory_block; + ulong start_sec; + ulong start_pfn; + ulong nid; + char state[24]; + char name[32]; +}; + +#define MIN_MEMORY_BLOCK_SIZE (1UL << _SECTION_SIZE_BITS) + +#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 nr, max, addr; - ulong nr_mem_sections; + 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 void +fill_mem_section_state(ulong state, char *buf) +{ + int bufidx = 0, others = 0; + + memset(buf, 0, sizeof(*buf) * BUFSIZE); + +#define printflag(X) sprintf(buf + bufidx, X, others++ ? "|" : "") + + if (state & SECTION_MARKED_PRESENT) + bufidx += printflag("%sPRESENT"); + if (state & SECTION_HAS_MEM_MAP) + bufidx += printflag("%sHAS_MEMMAP"); + if (state & SECTION_IS_ONLINE) + bufidx += printflag("%sONLINE"); +} + +static ulong +pfn_to_phys(ulong pfn) +{ + return pfn << PAGE_SHIFT; +} + +static ulong +get_memory_block_size(void) +{ + static ulong blksz; + + if (blksz) + return blksz; + + if (symbol_exists("memory_block_size_probed")) + get_symbol_data("memory_block_size_probed", sizeof(ulong), + &blksz); + else + blksz = MIN_MEMORY_BLOCK_SIZE; + + return blksz; +} + +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_info(ulong mb, struct memory_block_info *mbi) +{ + ulong start, start_pfn, nid; + char statebuf[BUFSIZE]; + char name[BUFSIZE]; + + readmem(mb + OFFSET(memory_block_start_section_nr), KVADDR, + &start, sizeof(void *), "memory_block start_section_nr", + FAULT_ON_ERROR); + + start_pfn = section_nr_to_pfn(start); + fill_memory_block_state(mb, statebuf); + fill_memory_block_name(mb, name); + + mbi->memory_block = mb; + mbi->start_sec = start; + mbi->start_pfn = start_pfn; + strncpy(mbi->state, statebuf, sizeof(mbi->state)); + strncpy(mbi->name, name, sizeof(mbi->name)); + if (MEMBER_EXISTS("memory_block", "nid")) { + readmem(mb + OFFSET(memory_block_nid), KVADDR, &nid, + sizeof(void *), "memory_block nid", FAULT_ON_ERROR); + mbi->nid = nid; + } +} + +static void +init_memory_block(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_state, "memory_block", "state"); + if (MEMBER_EXISTS("memory_block", "nid")) + MEMBER_OFFSET_INIT(memory_block_nid, "memory_block", "nid"); +} + +static struct memory_block_info* +parse_memory_block(void) +{ + ulong memory_subsys = symbol_value("memory_subsys"); + ulong private, klist, memory_block, device; + ulong *klistbuf; + int klistcnt, i; + struct list_data list_data, *ld; + struct memory_block_info *memory_block_info; + + init_memory_block(); + + 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); + ld = &list_data; + BZERO(ld, sizeof(struct list_data)); + + ld->start = klist; + 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(); + + memory_block_info = calloc(klistcnt + 1, + sizeof(struct memory_block_info)); + if (!memory_block_info) + error(FATAL, "cannot allocate memory for memory_block_info\n"); + + 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); + fill_memory_block_info(memory_block, &memory_block_info[i]); + } + FREEBUF(klistbuf); + + return memory_block_info; +} + +static struct memory_block_info* +get_memory_block_info(ulong pfn, struct memory_block_info *mbi_tbl) +{ + int i; + + for (i = 0; mbi_tbl[i].memory_block != 0; i++) + if (pfn == mbi_tbl[i].start_pfn) + return &mbi_tbl[i]; + + return NULL; +} + + +static int +is_memory_block_head(ulong pfn) +{ + ulong nr_pages = get_memory_block_size() / PAGE_SIZE; + + return (pfn % nr_pages) == 0; +} + +static void +print_memory_block(ulong pfn, ulong nr_mb_pages, struct memory_block_info *mbi) +{ + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + char buf4[BUFSIZE]; + char buf5[BUFSIZE]; + char buf6[BUFSIZE]; + + if (MEMBER_EXISTS("memory_block", "nid")) + fprintf(fp, " %s %s %s - %s %s %s\n", + mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX, + MKSTR(mbi->memory_block)), + mkstring(buf2, 12, CENTER, mbi->name), + mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX, + MKSTR(pfn_to_phys(pfn))), + mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX, + MKSTR(pfn_to_phys(pfn + nr_mb_pages) - 1)), + mkstring(buf5, strlen("NODE"), CENTER|LONG_DEC, + MKSTR(mbi->nid)), + mkstring(buf6, strlen("CANCEL_OFFLINE"), LJUST, + mbi->state)); + else + fprintf(fp, " %s %s %s - %s %s\n", + mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX, + MKSTR(mbi->memory_block)), + mkstring(buf2, 10, CENTER, mbi->name), + mkstring(buf3, PADDR_PRLEN, RJUST|LONG_HEX, + MKSTR(pfn_to_phys(pfn))), + mkstring(buf4, PADDR_PRLEN, LJUST|LONG_HEX, + MKSTR(pfn_to_phys(pfn + nr_mb_pages) - 1)), + mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST, + mbi->state)); +} + +static void +do_mem_block_and_sections(int print_memblk, struct memory_block_info *mbi_tbl) +{ + ulong nr, addr; + ulong nr_mem_sections, nr_mb_pages; ulong coded_mem_map, mem_map, pfn; + struct memory_block_info *mbi; + char mb_hdr[BUFSIZE]; + char ms_hdr[BUFSIZE]; + char statebuf[BUFSIZE]; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; + char buf5[BUFSIZE]; + + nr_mb_pages = PAGES_PER_SECTION() * + get_memory_block_size() / MIN_MEMORY_BLOCK_SIZE; + + if (MEMBER_EXISTS("memory_block", "nid")) + 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 + 2, CENTER, "PHYSICAL RANGE"), + mkstring(buf4, strlen("NODE"), CENTER, "NODE"), + mkstring(buf5, strlen("CANCEL_OFFLINE"), LJUST, "STATE")); + else + sprintf(mb_hdr, "\n%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")); + + sprintf(ms_hdr, "NR %s %s %s %s %s\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(buf4, 12, CENTER, "PFN"), + mkstring(buf5, 12, LJUST, "STATE")); + + if (!print_memblk) + fprintf(fp, "%s", ms_hdr); + + nr_mem_sections = NR_MEM_SECTIONS(); + for (nr = 0; nr < nr_mem_sections ; nr++) { + addr = valid_section_nr(nr); + if (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); + + if ((print_memblk) && (is_memory_block_head(pfn))) { + mbi = get_memory_block_info(pfn, mbi_tbl); + fprintf(fp, "%s", mb_hdr); + print_memory_block(pfn, nr_mb_pages, mbi); + fprintf(fp, "%s", ms_hdr); + } + + fprintf(fp, "%5ld %s %s %s %s %s\n", + nr, + mkstring(buf1, VADDR_PRLEN, + CENTER|LONG_HEX, MKSTR(addr)), + mkstring(buf2, MAX(VADDR_PRLEN, + strlen("CODED_MEM_MAP")), + CENTER|LONG_HEX|RJUST, MKSTR(coded_mem_map)), + mkstring(buf3, VADDR_PRLEN, + CENTER|LONG_HEX|RJUST, MKSTR(mem_map)), + pc->output_radix == 10 ? + mkstring(buf4, 12, + LONG_DEC|LJUST, MKSTR(pfn)) : + mkstring(buf4, 12, + LONG_HEX|LJUST, MKSTR(pfn)), + mkstring(buf5, 12, LJUST, statebuf)); + } + } +} + + +void +dump_mem_sections(void) +{ + do_mem_block_and_sections(0, NULL); +} + +void +_dump_mem_block_and_sections(void) +{ + struct memory_block_info *mbi_tbl; + + mbi_tbl = parse_memory_block(); + do_mem_block_and_sections(1, mbi_tbl); + free(mbi_tbl); +} + +void +dump_mem_block_and_sections(int initialize) +{ + ulong nr, max; + ulong nr_mem_sections; nr_mem_sections = NR_MEM_SECTIONS(); @@ -17212,34 +17564,12 @@ dump_mem_sections(int initialize) fprintf(fp, "\n"); pad_line(fp, BITS32() ? 59 : 67, '-'); - fprintf(fp, "\n\nNR %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")); - - for (nr = 0; nr < nr_mem_sections ; nr++) { - if ((addr = valid_section_nr(nr))) { - coded_mem_map = section_mem_map_addr(addr); - mem_map = sparse_decode_mem_map(coded_mem_map,nr); - pfn = section_nr_to_pfn(nr); - - fprintf(fp, "%2ld %s %s %s %s\n", - nr, - mkstring(buf1, VADDR_PRLEN, - CENTER|LONG_HEX, MKSTR(addr)), - mkstring(buf2, MAX(VADDR_PRLEN, - strlen("CODED_MEM_MAP")), - CENTER|LONG_HEX|RJUST, MKSTR(coded_mem_map)), - mkstring(buf3, VADDR_PRLEN, - CENTER|LONG_HEX|RJUST, MKSTR(mem_map)), - pc->output_radix == 10 ? - mkstring(buf4, VADDR_PRLEN, - LONG_DEC|LJUST, MKSTR(pfn)) : - mkstring(buf4, VADDR_PRLEN, - LONG_HEX|LJUST, MKSTR(pfn))); - } - } + fprintf(fp, "\n\n"); + if ((!STRUCT_EXISTS("memory_block")) || + (!symbol_exists("memory_subsys"))) + dump_mem_sections(); + else + _dump_mem_block_and_sections(); } void @@ -17251,7 +17581,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, -- 2.19.0 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility