[Crash-utility] [PATCH] Fix "kmem -v" option on Linux 6.9 and later kernels

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The following kernel commits removed vmap_area_list and vmap_area_root
rb-tree, and introduced vmap_nodes.

  55c49fee57af mm/vmalloc: remove vmap_area_list
  d093602919ad mm: vmalloc: remove global vmap_area_root rb-tree

Without the patch, the "kmem -v" option and functions that use
dump_vmlist() fail with or without an error:

  crash> kmem -v
     VM_STRUCT                 ADDRESS RANGE               SIZE
  kmem: invalid kernel virtual address: ccccccccccccccd4  type: "vmlist addr"

  crash> kmem -v
  crash>

Signed-off-by: Kazuhito Hagio <k-hagio-ab@xxxxxxx>
---
 defs.h    |   4 ++
 memory.c  | 135 +++++++++++++++++++++++++++++++++++++++++++++---------
 symbols.c |   3 ++
 3 files changed, 120 insertions(+), 22 deletions(-)

diff --git a/defs.h b/defs.h
index 01f316e67dde..95de33188070 100644
--- a/defs.h
+++ b/defs.h
@@ -2240,6 +2240,8 @@ struct offset_table {                    /* stash of commonly-used offsets */
 	long mnt_namespace_nr_mounts;
 	long mount_mnt_node;
 	long log_caller_id;
+	long vmap_node_busy;
+	long rb_list_head;
 };
 
 struct size_table {         /* stash of commonly-used sizes */
@@ -2414,6 +2416,7 @@ struct size_table {         /* stash of commonly-used sizes */
 	long maple_tree;
 	long maple_node;
 	long module_memory;
+	long vmap_node;
 };
 
 struct array_table {
@@ -2678,6 +2681,7 @@ struct vm_table {                /* kernel VM-related data */
 #define SLAB_OVERLOAD_PAGE    (0x8000000)
 #define SLAB_CPU_CACHE       (0x10000000)
 #define SLAB_ROOT_CACHES     (0x20000000)
+#define USE_VMAP_NODES       (0x40000000)
 
 #define IS_FLATMEM()		(vt->flags & FLATMEM)
 #define IS_DISCONTIGMEM()	(vt->flags & DISCONTIGMEM)
diff --git a/memory.c b/memory.c
index 34ed646b5d1e..acb8507cfb75 100644
--- a/memory.c
+++ b/memory.c
@@ -235,6 +235,7 @@ static void dump_slab_objects(struct meminfo *);
 static void dump_slab_objects_percpu(struct meminfo *);
 static void dump_vmlist(struct meminfo *);
 static void dump_vmap_area(struct meminfo *);
+static int get_vmap_area_list_from_nodes(ulong **);
 static int dump_page_lists(struct meminfo *);
 static void dump_kmeminfo(void);
 static int page_to_phys(ulong, physaddr_t *); 
@@ -433,9 +434,15 @@ vm_init(void)
 	if (VALID_MEMBER(vmap_area_va_start) &&
 	    VALID_MEMBER(vmap_area_va_end) &&
 	    VALID_MEMBER(vmap_area_list) &&
-	    VALID_MEMBER(vmap_area_vm) &&
-	    kernel_symbol_exists("vmap_area_list"))
-		vt->flags |= USE_VMAP_AREA;
+	    VALID_MEMBER(vmap_area_vm)) {
+		if (kernel_symbol_exists("vmap_nodes")) {
+			STRUCT_SIZE_INIT(vmap_node, "vmap_node");
+			MEMBER_OFFSET_INIT(vmap_node_busy, "vmap_node", "busy");
+			MEMBER_OFFSET_INIT(rb_list_head, "rb_list", "head");
+			vt->flags |= USE_VMAP_NODES;
+		} else if (kernel_symbol_exists("vmap_area_list"))
+			vt->flags |= USE_VMAP_AREA;
+	}
 
 	if (kernel_symbol_exists("hstates")) {
 		STRUCT_SIZE_INIT(hstate, "hstate");
@@ -8957,7 +8964,7 @@ dump_vmlist(struct meminfo *vi)
 	physaddr_t paddr;
 	int mod_vmlist;
 
-	if (vt->flags & USE_VMAP_AREA) {
+	if (vt->flags & (USE_VMAP_AREA|USE_VMAP_NODES)) {
 		dump_vmap_area(vi);
 		return;
 	}
@@ -9067,6 +9074,77 @@ next_entry:
 		vi->retval = verified;
 }
 
+static int
+sort_by_va_start(const void *arg1, const void *arg2)
+{
+	ulong va_start1, va_start2;
+
+	readmem(*(ulong *)arg1 + OFFSET(vmap_area_va_start), KVADDR, &va_start1,
+		sizeof(void *), "vmap_area.va_start", FAULT_ON_ERROR);
+	readmem(*(ulong *)arg2 + OFFSET(vmap_area_va_start), KVADDR, &va_start2,
+		sizeof(void *), "vmap_area.va_start", FAULT_ON_ERROR);
+
+	return va_start1 < va_start2 ? -1 : (va_start1 == va_start2 ? 0 : 1);
+}
+
+/* Linux 6.9 and later kernels use "vmap_nodes". */
+static int
+get_vmap_area_list_from_nodes(ulong **list_ptr)
+{
+	int i, cnt, c;
+	struct list_data list_data, *ld = &list_data;
+	uint nr_vmap_nodes;
+	ulong vmap_nodes, list_head;
+	ulong *list, *ptr;
+
+	get_symbol_data("nr_vmap_nodes", sizeof(uint), &nr_vmap_nodes);
+	get_symbol_data("vmap_nodes", sizeof(ulong), &vmap_nodes);
+
+	/* count up all vmap_areas. */
+	cnt = 0;
+	for (i = 0; i < nr_vmap_nodes; i++) {
+		BZERO(ld, sizeof(struct list_data));
+		list_head = vmap_nodes + SIZE(vmap_node) * i +
+				OFFSET(vmap_node_busy) + OFFSET(rb_list_head);
+		readmem(list_head, KVADDR, &ld->start, sizeof(void *),
+				"rb_list.head", FAULT_ON_ERROR);
+		ld->list_head_offset = OFFSET(vmap_area_list);
+		ld->end = list_head;
+		c = do_list(ld);
+		if (c < 0)
+			return -1;
+
+		cnt += c;
+	}
+
+	list = ptr = (ulong *)GETBUF(sizeof(void *) * cnt);
+
+	/* gather all vmap_areas into a list. */
+	for (i = 0; i < nr_vmap_nodes; i++) {
+		BZERO(ld, sizeof(struct list_data));
+		ld->flags = LIST_ALLOCATE;
+		list_head = vmap_nodes + SIZE(vmap_node) * i +
+				OFFSET(vmap_node_busy) + OFFSET(rb_list_head);
+		readmem(list_head, KVADDR, &ld->start, sizeof(void *),
+				"rb_list.head", FAULT_ON_ERROR);
+		ld->list_head_offset = OFFSET(vmap_area_list);
+		ld->end = list_head;
+		c = do_list(ld);
+		if (c < 0)
+			return -1;
+
+		memcpy(ptr, ld->list_ptr, sizeof(void *) * c);
+		ptr += c;
+
+		FREEBUF(ld->list_ptr);
+	}
+
+	qsort(list, cnt, sizeof(void *), sort_by_va_start);
+
+	*list_ptr = list;
+	return cnt;
+}
+
 static void
 dump_vmap_area(struct meminfo *vi)
 {
@@ -9080,26 +9158,37 @@ dump_vmap_area(struct meminfo *vi)
 	char buf2[BUFSIZE];
 	char buf3[BUFSIZE];
 	char buf4[BUFSIZE];
+	ulong *list_ptr;
 
 #define VM_VM_AREA 0x4   /* mm/vmalloc.c */
 
-	vmap_area_buf = GETBUF(SIZE(vmap_area));
 	start = count = verified = size = 0;
 
-	ld = &list_data;
-	BZERO(ld, sizeof(struct list_data));
-	ld->flags = LIST_HEAD_FORMAT|LIST_HEAD_POINTER|LIST_ALLOCATE;
-	get_symbol_data("vmap_area_list", sizeof(void *), &ld->start);
-	ld->list_head_offset = OFFSET(vmap_area_list);
-	ld->end = symbol_value("vmap_area_list");
-	cnt = do_list(ld);
-	if (cnt < 0) {
-		FREEBUF(vmap_area_buf);
-		error(WARNING, "invalid/corrupt vmap_area_list\n"); 
-		vi->retval = 0;
-		return;
+	if (vt->flags & USE_VMAP_NODES) {
+		cnt = get_vmap_area_list_from_nodes(&list_ptr);
+		if (cnt < 0) {
+			error(WARNING, "invalid/corrupt vmap_nodes.busy list\n");
+			vi->retval = 0;
+			return;
+		}
+	} else {
+		ld = &list_data;
+		BZERO(ld, sizeof(struct list_data));
+		ld->flags = LIST_HEAD_FORMAT|LIST_HEAD_POINTER|LIST_ALLOCATE;
+		get_symbol_data("vmap_area_list", sizeof(void *), &ld->start);
+		ld->list_head_offset = OFFSET(vmap_area_list);
+		ld->end = symbol_value("vmap_area_list");
+		cnt = do_list(ld);
+		if (cnt < 0) {
+			error(WARNING, "invalid/corrupt vmap_area_list\n");
+			vi->retval = 0;
+			return;
+		}
+		list_ptr = ld->list_ptr;
 	}
 
+	vmap_area_buf = GETBUF(SIZE(vmap_area));
+
 	for (i = 0; i < cnt; i++) {
 		if (!(pc->curcmd_flags & HEADER_PRINTED) && (i == 0) && 
 		    !(vi->flags & (GET_HIGHEST|GET_PHYS_TO_VMALLOC|
@@ -9116,7 +9205,7 @@ dump_vmap_area(struct meminfo *vi)
 			pc->curcmd_flags |= HEADER_PRINTED;
 		}
 
-		readmem(ld->list_ptr[i], KVADDR, vmap_area_buf,
+		readmem(list_ptr[i], KVADDR, vmap_area_buf,
                         SIZE(vmap_area), "vmap_area struct", FAULT_ON_ERROR); 
 
 		if (VALID_MEMBER(vmap_area_flags) &&
@@ -9158,7 +9247,7 @@ dump_vmap_area(struct meminfo *vi)
 			} 	
 			fprintf(fp, "%s%s  %s%s  %s - %s  %7ld\n",
 				mkstring(buf1,VADDR_PRLEN, LONG_HEX|CENTER|LJUST,
-				MKSTR(ld->list_ptr[i])), space(MINSPACE-1),
+				MKSTR(list_ptr[i])), space(MINSPACE-1),
 				mkstring(buf2,VADDR_PRLEN, LONG_HEX|CENTER|LJUST,
 				MKSTR(vm_struct)), space(MINSPACE-1),
 				mkstring(buf3, VADDR_PRLEN, LONG_HEX|RJUST,
@@ -9179,14 +9268,14 @@ dump_vmap_area(struct meminfo *vi)
 					if (vi->flags & GET_PHYS_TO_VMALLOC) {
 						vi->retval = pcheck +
 						    PAGEOFFSET(vi->spec_addr);
-						FREEBUF(ld->list_ptr);
+						FREEBUF(list_ptr);
 						return;
 				        } else
 						fprintf(fp,
 						"%s%s  %s%s  %s - %s  %7ld\n",
 						mkstring(buf1,VADDR_PRLEN, 
 						LONG_HEX|CENTER|LJUST,
-						MKSTR(ld->list_ptr[i])), 
+						MKSTR(list_ptr[i])),
 						space(MINSPACE-1),
 						mkstring(buf2, VADDR_PRLEN,
 						LONG_HEX|CENTER|LJUST,
@@ -9204,7 +9293,7 @@ dump_vmap_area(struct meminfo *vi)
 	}
 
 	FREEBUF(vmap_area_buf);
-	FREEBUF(ld->list_ptr);
+	FREEBUF(list_ptr);
 
 	if (vi->flags & GET_HIGHEST)
 		vi->retval = start+size;
@@ -14001,6 +14090,8 @@ dump_vm_table(int verbose)
 		fprintf(fp, "%sSLAB_ROOT_CACHES", others++ ? "|" : "");\
 	if (vt->flags & USE_VMAP_AREA)
 		fprintf(fp, "%sUSE_VMAP_AREA", others++ ? "|" : "");\
+	if (vt->flags & USE_VMAP_NODES)
+		fprintf(fp, "%sUSE_VMAP_NODES", others++ ? "|" : "");\
 	if (vt->flags & CONFIG_NUMA)
 		fprintf(fp, "%sCONFIG_NUMA", others++ ? "|" : "");\
 	if (vt->flags & VM_EVENT)
diff --git a/symbols.c b/symbols.c
index b7627a83587a..ded34412ff41 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10167,6 +10167,8 @@ dump_offset_table(char *spec, ulong makestruct)
 	fprintf(fp, "               vmap_area_flags: %ld\n", 
 		OFFSET(vmap_area_flags));
 	fprintf(fp, "          vmap_area_purge_list: %ld\n", OFFSET(vmap_area_purge_list));
+	fprintf(fp, "                vmap_node_busy: %ld\n", OFFSET(vmap_node_busy));
+	fprintf(fp, "                  rb_list_head: %ld\n", OFFSET(rb_list_head));
 
 	fprintf(fp, "         module_size_of_struct: %ld\n", 
 		OFFSET(module_size_of_struct));
@@ -12040,6 +12042,7 @@ dump_offset_table(char *spec, ulong makestruct)
 		SIZE(task_group));
 	fprintf(fp, "                     vmap_area: %ld\n",
 		SIZE(vmap_area));
+	fprintf(fp, "                     vmap_node: %ld\n", SIZE(vmap_node));
 	fprintf(fp, "            hrtimer_clock_base: %ld\n",
 		SIZE(hrtimer_clock_base));
 	fprintf(fp, "                  hrtimer_base: %ld\n",
-- 
2.31.1
--
Crash-utility mailing list -- devel@xxxxxxxxxxxxxxxxxxxxxxxxxxx
To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxxxxxxxxxxxx
https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/
Contribution Guidelines: https://github.com/crash-utility/crash/wiki




[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux