[PATCH 03/10] util_lib: Add maple tree walker for VMA enumeration

Pnina Feder <[email protected]>
Newsgroups org.infradead.lists.kexec
Message-ID <[email protected]>
Port the maple tree traversal logic from crash-utility to support
walking the kernel's VMA maple tree structure. Supports count,
search, gather, and dump operations.

This is needed for enumerating process virtual memory areas (VMAs)
when building per-task memory snapshots.
Needed for Linux 6.x and up.

Signed-off-by: Pnina Feder <[email protected]>
---
 util_lib/include/maple_tree.h | 137 +++++++
 util_lib/maple_tree.c         | 715 ++++++++++++++++++++++++++++++++++
 2 files changed, 852 insertions(+)
 create mode 100644 util_lib/include/maple_tree.h
 create mode 100644 util_lib/maple_tree.c

diff --git a/util_lib/include/maple_tree.h b/util_lib/include/maple_tree.h
new file mode 100644
index 00000000..c17c528c
--- /dev/null
+++ b/util_lib/include/maple_tree.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef _MAPLE_TREE_H
+#define _MAPLE_TREE_H
+/*
+ * Maple Tree - An RCU-safe adaptive tree for storing ranges
+ * Copyright (c) 2018-2022 Oracle
+ * Authors:     Liam R. Howlett <[email protected]>
+ *              Matthew Wilcox <[email protected]>
+ *
+ * eXtensible Arrays
+ * Copyright (c) 2017 Microsoft Corporation
+ * Author: Matthew Wilcox <[email protected]>
+ *
+ * See Documentation/core-api/xarray.rst for how to use the XArray.
+ */
+#include <stdbool.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#include "vmcore_tasks_util.h"
+
+#define MAPLE_TREE_COUNT   (1)
+#define MAPLE_TREE_SEARCH  (2)
+#define MAPLE_TREE_DUMP    (3)
+#define MAPLE_TREE_GATHER  (4)
+#define MAPLE_TREE_DUMP_CB (5)
+
+/*
+ * The following are copied and modified from include/linux/maple_tree.h
+ */
+
+enum maple_type {
+	maple_dense,
+	maple_leaf_64,
+	maple_range_64,
+	maple_arange_64,
+};
+
+#define MAPLE_NODE_MASK		255UL
+
+#define MT_FLAGS_HEIGHT_OFFSET	0x02
+#define MT_FLAGS_HEIGHT_MASK	0x7C
+
+#define MAPLE_NODE_TYPE_MASK	0x0F
+#define MAPLE_NODE_TYPE_SHIFT	0x03
+
+#define MAPLE_RESERVED_RANGE	4096
+
+#define VERBOSE (0x1)
+#define TREE_ROOT_OFFSET_ENTERED  (VERBOSE << 1)
+#define TREE_NODE_OFFSET_ENTERED  (VERBOSE << 2)
+#define TREE_NODE_POINTER         (VERBOSE << 3)
+#define TREE_POSITION_DISPLAY     (VERBOSE << 4)
+#define TREE_STRUCT_RADIX_10      (VERBOSE << 5)
+#define TREE_STRUCT_RADIX_16      (VERBOSE << 6)
+#define TREE_PARSE_MEMBER         (VERBOSE << 7)
+#define TREE_READ_MEMBER          (VERBOSE << 8)
+#define TREE_LINEAR_ORDER         (VERBOSE << 9)
+#define TREE_STRUCT_VERBOSE       (VERBOSE << 10)
+
+/*Copied from linux/maple_tree.h*/
+/* 64bit sizes */
+#define MAPLE_NODE_SLOTS	31	/* 256 bytes including ->parent */
+#define MAPLE_RANGE64_SLOTS	16	/* 256 bytes */
+#define MAPLE_ARANGE64_SLOTS	10	/* 240 bytes */
+#define MAPLE_ALLOC_SLOTS	(MAPLE_NODE_SLOTS - 1)
+
+/*
+ * The following are copied and modified from include/linux/xarray.h
+ */
+
+#define XA_ZERO_ENTRY		xa_mk_internal(257)
+
+static inline ulong xa_mk_internal(ulong v)
+{
+	return (v << 2) | 2;
+}
+
+static inline bool xa_is_internal(ulong entry)
+{
+	return (entry & 3) == 2;
+}
+
+static inline bool xa_is_node(ulong entry)
+{
+	return xa_is_internal(entry) && entry > 4096;
+}
+
+static inline bool xa_is_value(ulong entry)
+{
+	return entry & 1;
+}
+
+static inline bool xa_is_zero(ulong entry)
+{
+	return entry == XA_ZERO_ENTRY;
+}
+
+static inline unsigned long xa_to_internal(ulong entry)
+{
+	return entry >> 2;
+}
+
+static inline unsigned long xa_to_value(ulong entry)
+{
+	return entry >> 1;
+}
+
+struct tree_data {
+	ulong flags;
+	ulong start;
+	long node_member_offset;
+	char **structname;
+	int structname_args;
+	int count;
+};
+
+struct list_pair {
+	ulong index;
+	void *value;
+};
+
+struct req_entry {
+	char *arg, *name, **member;
+	int *is_str, *is_ptr;
+	ulong *width, *offset;
+	int count;
+};
+
+extern const unsigned char mt_slots[];
+extern const unsigned char mt_pivots[];
+
+void maple_init(void);
+int do_mptree(struct tree_data *);
+ulong do_maple_tree(ulong, int, struct list_pair *);
+
+#endif /* _MAPLE_TREE_H */
diff --git a/util_lib/maple_tree.c b/util_lib/maple_tree.c
new file mode 100644
index 00000000..909dfdbf
--- /dev/null
+++ b/util_lib/maple_tree.c
@@ -0,0 +1,715 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Maple Tree implementation
+ * Copyright (c) 2018-2022 Oracle Corporation
+ * Authors: Liam R. Howlett <[email protected]>
+ * 	    Matthew Wilcox <[email protected]>
+ *
+ * The following are copied and modified from lib/maple_tree.c
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "maple_tree.h"
+#include "memory.h"
+#include "vmcore_info.h"
+
+const unsigned char mt_slots[] = {
+	[maple_dense]		= MAPLE_NODE_SLOTS,
+	[maple_leaf_64]		= MAPLE_RANGE64_SLOTS,
+	[maple_range_64]	= MAPLE_RANGE64_SLOTS,
+	[maple_arange_64]	= MAPLE_ARANGE64_SLOTS,
+};
+
+const unsigned char mt_pivots[] = {
+	[maple_dense]		= 0,
+	[maple_leaf_64]		= MAPLE_RANGE64_SLOTS - 1,
+	[maple_range_64]	= MAPLE_RANGE64_SLOTS - 1,
+	[maple_arange_64]	= MAPLE_ARANGE64_SLOTS - 1,
+};
+
+ulong mt_max[4] = {0};
+static FILE* fp;
+static bool maple_initialized = false;
+
+static uint64_t size_maple_tree;
+static uint64_t offset_maple_tree_ma_root;
+static uint64_t offset_maple_tree_ma_flags;
+
+/* maple_node offsets */
+static uint64_t size_maple_node;
+static uint64_t offset_maple_node_slot;
+static uint64_t offset_maple_node_parent;
+static uint64_t offset_maple_node_ma64;
+static uint64_t offset_maple_node_mr64;
+
+/* maple_range_64 size + offsets */
+static uint64_t offset_maple_range_64_pivot;
+static uint64_t offset_maple_range_64_slot;
+
+/* maple_metadata offsets */
+static uint64_t offset_maple_metadata_end;
+static uint64_t offset_maple_metadata_gap;
+
+/* maple_arange_64 size + offsets */
+static uint64_t offset_maple_arange_64_pivot;
+static uint64_t offset_maple_arange_64_slot;
+static uint64_t offset_maple_arange_64_gap;
+static uint64_t offset_maple_arange_64_meta;
+
+
+#define MAPLE_BUFSIZE			512
+#define BUFSIZE (1500)
+
+static inline ulong mte_to_node(ulong maple_enode_entry)
+{
+	return maple_enode_entry & ~MAPLE_NODE_MASK;
+}
+
+static inline enum maple_type mte_node_type(ulong maple_enode_entry)
+{
+	return (maple_enode_entry >> MAPLE_NODE_TYPE_SHIFT) &
+		MAPLE_NODE_TYPE_MASK;
+}
+
+static inline ulong mt_slot(void **slots, unsigned char offset)
+{
+	return (ulong)slots[offset];
+}
+
+static inline bool ma_is_leaf(const enum maple_type type)
+{
+	return type < maple_range_64;
+}
+
+/*************** For cmd_tree ********************/
+
+struct do_maple_tree_info {
+	ulong maxcount;
+	ulong count;
+	void *data;
+};
+
+struct maple_tree_ops {
+	void (*entry)(ulong node, ulong slot, const char *path,
+		      ulong index, void *private);
+	void *private;
+	bool is_td;
+};
+
+static const char spaces[] = "                                ";
+
+static void do_mt_range64(ulong, ulong, ulong, uint, char *, ulong *,
+			  struct maple_tree_ops *);
+static void do_mt_arange64(ulong, ulong, ulong, uint, char *, ulong *,
+			   struct maple_tree_ops *);
+static void do_mt_entry(ulong, ulong, ulong, uint, uint, char *, ulong *,
+			struct maple_tree_ops *);
+static void do_mt_node(ulong, ulong, ulong, uint, char *, ulong *,
+		       struct maple_tree_ops *);
+
+static int count_chars(const char *s, char c) {
+	int count = 0;
+	while (*s) if (*s++ == c) count++;
+	return count;
+}
+
+static void dump_struct(const char *name, ulong addr, int radix) {
+	printf("dump_struct not implemented\n");
+}
+
+struct req_entry *fill_member_offsets(char *name){
+	printf("fill_member_offsets - Not inmplemented\n");
+	return NULL;
+}
+
+void dump_struct_members_fast(struct req_entry *e, int radix , ulong addr){
+	printf("dump_struct_members_fast - Not inmplemented");
+}
+
+void dump_struct_members_for_tree(struct tree_data *td, int index, ulong addr){
+	printf("dump_struct_members_for_tree - Not inmplemented");
+}
+
+static void mt_dump_range(ulong min, ulong max, uint depth)
+{
+	if (min == max)
+		fprintf(fp, "%.*s%lu: ", depth * 2, spaces, min);
+	else
+		fprintf(fp, "%.*s%lu-%lu: ", depth * 2, spaces, min, max);
+}
+
+static inline bool mt_is_reserved(ulong entry)
+{
+       return (entry < MAPLE_RESERVED_RANGE) && xa_is_internal(entry);
+}
+
+static inline bool mte_is_leaf(ulong maple_enode_entry)
+{
+       return ma_is_leaf(mte_node_type(maple_enode_entry));
+}
+
+static uint mt_height(char *mt_buf)
+{
+	return (UINT(mt_buf + offset_maple_tree_ma_flags) &
+		MT_FLAGS_HEIGHT_MASK)
+	       >> MT_FLAGS_HEIGHT_OFFSET;
+}
+
+static void dump_mt_range64(char *mr64_buf)
+{
+	int i;
+
+	fprintf(fp, " contents: ");
+	for (i = 0; i < mt_slots[maple_range_64] - 1; i++)
+		fprintf(fp, "%p %lu ",
+			VOID_PTR(mr64_buf + offset_maple_range_64_slot
+				 + sizeof(void *) * i),
+			ULONG(mr64_buf + offset_maple_range_64_pivot
+			      + sizeof(ulong) * i));
+	fprintf(fp, "%p\n", VOID_PTR(mr64_buf + offset_maple_range_64_slot
+				     + sizeof(void *) * i));
+}
+
+static void dump_mt_arange64(char *ma64_buf)
+{
+	int i;
+
+	fprintf(fp, " contents: ");
+	for (i = 0; i < mt_slots[maple_arange_64]; i++)
+		fprintf(fp, "%lu ", ULONG(ma64_buf + offset_maple_arange_64_gap
+					  + sizeof(ulong) * i));
+
+	fprintf(fp, "| %02X %02X| ",
+		UCHAR(ma64_buf + offset_maple_arange_64_meta +
+		      offset_maple_metadata_end),
+		UCHAR(ma64_buf + offset_maple_arange_64_meta +
+		      offset_maple_metadata_gap));
+
+	for (i = 0; i < mt_slots[maple_arange_64] - 1; i++)
+		fprintf(fp, "%p %lu ",
+			VOID_PTR(ma64_buf + offset_maple_arange_64_slot +
+				 sizeof(void *) * i),
+			ULONG(ma64_buf + offset_maple_arange_64_pivot +
+			      sizeof(ulong) * i));
+	fprintf(fp, "%p\n", VOID_PTR(ma64_buf + offset_maple_arange_64_slot +
+				     sizeof(void *) * i));
+}
+
+static void dump_mt_entry(ulong entry, ulong min, ulong max, uint depth)
+{
+	mt_dump_range(min, max, depth);
+
+	if (xa_is_value(entry))
+		fprintf(fp, "value %ld (0x%lx) [0x%lx]\n", xa_to_value(entry),
+			xa_to_value(entry), entry);
+	else if (xa_is_zero(entry))
+		fprintf(fp, "zero (%ld)\n", xa_to_internal(entry));
+	else if (mt_is_reserved(entry))
+		fprintf(fp, "UNKNOWN ENTRY (0x%lx)\n", entry);
+	else
+		fprintf(fp, "0x%lx\n", entry);
+}
+
+static void dump_mt_node(ulong maple_node, char *node_data, uint type,
+			 ulong min, ulong max, uint depth)
+{
+	mt_dump_range(min, max, depth);
+
+	fprintf(fp, "node 0x%lx depth %d type %d parent %p",
+		maple_node, depth, type,
+		maple_node ? VOID_PTR(node_data + offset_maple_node_parent) :
+			     NULL);
+}
+
+static void do_mt_range64(ulong entry, ulong min, ulong max,
+			  uint depth, char *path, ulong *global_index,
+			  struct maple_tree_ops *ops)
+{
+	ulong maple_node_m_node = mte_to_node(entry);
+	char node_buf[MAPLE_BUFSIZE];
+	bool leaf = mte_is_leaf(entry);
+	ulong first = min, last;
+	int i;
+	int len = strlen(path);
+	struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+	char *mr64_buf;
+
+	if (size_maple_node > MAPLE_BUFSIZE) {
+		fprintf(fp, "MAPLE_BUFSIZE should be larger than maple_node struct");
+		return;
+	}
+
+	if (readmem(maple_node_m_node, node_buf, size_maple_node, "mt_dump_range64 read maple_node", KVADDR) < 0) {
+		fprintf(stderr, "do_mt_range64: failed to read maple_node at 0x%lx\n", maple_node_m_node);
+		return;
+	}
+
+	mr64_buf = node_buf + offset_maple_node_mr64;
+
+	if (td && td->flags & TREE_STRUCT_VERBOSE) {
+		dump_mt_range64(mr64_buf);
+	}
+
+	for (i = 0; i < mt_slots[maple_range_64]; i++) {
+		last = max;
+
+		if (i < (mt_slots[maple_range_64] - 1))
+			last = ULONG(mr64_buf + offset_maple_range_64_pivot +
+				     sizeof(ulong) * i);
+
+		else if (!VOID_PTR(mr64_buf + offset_maple_range_64_slot +
+			  sizeof(void *) * i) &&
+			 max != mt_max[mte_node_type(entry)])
+			break;
+		if (last == 0 && i > 0)
+			break;
+		if (leaf)
+			do_mt_entry(mt_slot((void **)(mr64_buf +
+						      offset_maple_range_64_slot), i),
+				    first, last, depth + 1, i, path, global_index, ops);
+		else if (VOID_PTR(mr64_buf + offset_maple_range_64_slot +
+				  sizeof(void *) * i)) {
+			sprintf(path + len, "/%d", i);
+			do_mt_node(mt_slot((void **)(mr64_buf +
+						     offset_maple_range_64_slot), i),
+				   first, last, depth + 1, path, global_index, ops);
+		}
+
+		if (last == max)
+			break;
+		if (last > max) {
+			fprintf(fp, "node %p last (%lu) > max (%lu) at pivot %d!\n",
+				mr64_buf, last, max, i);
+			break;
+		}
+		first = last + 1;
+	}
+}
+
+static void do_mt_arange64(ulong entry, ulong min, ulong max,
+			   uint depth, char *path, ulong *global_index,
+			   struct maple_tree_ops *ops)
+{
+	ulong maple_node_m_node = mte_to_node(entry);
+	char node_buf[MAPLE_BUFSIZE];
+	bool leaf = mte_is_leaf(entry);
+	ulong first = min, last;
+	int i;
+	int len = strlen(path);
+	struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+	char *ma64_buf;
+
+	if (size_maple_node > MAPLE_BUFSIZE) {
+		fprintf(fp, "MAPLE_BUFSIZE should be larger than maple_node struct");
+		return;
+	}
+
+	if (readmem(maple_node_m_node, node_buf, size_maple_node, "mt_dump_arange64 read maple_node", KVADDR) < 0) {
+		fprintf(stderr, "do_mt_arange64: failed to read maple_node at 0x%lx\n", maple_node_m_node);
+		return;
+	}
+
+	ma64_buf = node_buf + offset_maple_node_ma64;
+
+	if (td && td->flags & TREE_STRUCT_VERBOSE) {
+		dump_mt_arange64(ma64_buf);
+	}
+
+	for (i = 0; i < mt_slots[maple_arange_64]; i++) {
+		last = max;
+
+		if (i < (mt_slots[maple_arange_64] - 1))
+			last = ULONG(ma64_buf + offset_maple_arange_64_pivot +
+				     sizeof(ulong) * i);
+		else if (!VOID_PTR(ma64_buf + offset_maple_arange_64_slot +
+				   sizeof(void *) * i))
+			break;
+		if (last == 0 && i > 0)
+			break;
+
+		if (leaf)
+			do_mt_entry(mt_slot((void **)(ma64_buf +
+						      offset_maple_arange_64_slot), i),
+				    first, last, depth + 1, i, path, global_index, ops);
+		else if (VOID_PTR(ma64_buf + offset_maple_arange_64_slot +
+				  sizeof(void *) * i)) {
+			sprintf(path + len, "/%d", i);
+			do_mt_node(mt_slot((void **)(ma64_buf +
+						     offset_maple_arange_64_slot), i),
+				   first, last, depth + 1, path, global_index, ops);
+		}
+
+		if (last == max)
+			break;
+		if (last > max) {
+			fprintf(fp, "node %p last (%lu) > max (%lu) at pivot %d!\n",
+				ma64_buf, last, max, i);
+			break;
+		}
+		first = last + 1;
+	}
+}
+
+static void do_mt_entry(ulong entry, ulong min, ulong max, uint depth,
+			uint index, char *path, ulong *global_index,
+			struct maple_tree_ops *ops)
+{
+	int print_radix = 0, i;
+	static struct req_entry **e = NULL;
+	struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+
+	if (ops->entry && entry)
+		ops->entry(entry, entry, path, max, ops->private);
+
+	if (!td)
+		return;
+
+	if (!td->count && td->structname_args) {
+		/*
+		 * Retrieve all members' info only once (count == 0)
+		 * After last iteration all memory will be freed up
+		 */
+		e = (struct req_entry **)malloc(sizeof(*e) * td->structname_args);
+		for (i = 0; i < td->structname_args; i++)
+			e[i] = fill_member_offsets(td->structname[i]);
+	}
+
+	td->count++;
+
+	if (td->flags & TREE_STRUCT_VERBOSE) {
+		dump_mt_entry(entry, min, max, depth);
+	} else if (td->flags & VERBOSE && entry)
+		fprintf(fp, "%lx\n", entry);
+	if (td->flags & TREE_POSITION_DISPLAY && entry)
+		fprintf(fp, "  index: %ld  position: %s/%u\n",
+			++(*global_index), path, index);
+
+	if (td->structname && entry) {
+		if (td->flags & TREE_STRUCT_RADIX_10)
+			print_radix = 10;
+		else if (td->flags & TREE_STRUCT_RADIX_16)
+			print_radix = 16;
+		else
+			print_radix = 0;
+
+		for (i = 0; i < td->structname_args; i++) {
+			switch (count_chars(td->structname[i], '.')) {
+			case 0:
+				dump_struct(td->structname[i], entry, print_radix);
+				break;
+			default:
+				if (td->flags & TREE_PARSE_MEMBER)
+					dump_struct_members_for_tree(td, i, entry);
+				else if (td->flags & TREE_READ_MEMBER)
+					dump_struct_members_fast(e[i], print_radix, entry);
+			}
+		}
+	}
+
+	if (e) {
+		for (i = 0; i < td->structname_args; i++)
+			free(e[i]);
+		free(e);
+		e = NULL;
+	}
+}
+
+static void do_mt_node(ulong entry, ulong min, ulong max,
+		       uint depth, char *path, ulong *global_index,
+		       struct maple_tree_ops *ops)
+{
+	ulong maple_node = mte_to_node(entry);
+	uint type = mte_node_type(entry);
+	uint i;
+	char node_buf[MAPLE_BUFSIZE];
+	struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+
+	if (size_maple_node > MAPLE_BUFSIZE) {
+		fprintf(fp, "MAPLE_BUFSIZE should be larger than maple_node struct");
+		return;
+	}
+
+	if (readmem(maple_node, node_buf, size_maple_node, "mt_dump_node read maple_node", KVADDR) < 0) {
+		fprintf(stderr, "do_mt_node: failed to read maple_node at 0x%lx\n", maple_node);
+		return;
+	}
+
+	if (td && td->flags & TREE_STRUCT_VERBOSE) {
+		dump_mt_node(maple_node, node_buf, type, min, max, depth);
+	}
+
+	switch (type) {
+	case maple_dense:
+		for (i = 0; i < mt_slots[maple_dense]; i++) {
+			if (min + i > max)
+				fprintf(fp, "OUT OF RANGE: ");
+			do_mt_entry(mt_slot((void **)(node_buf + offset_maple_node_slot), i),
+				    min + i, min + i, depth, i, path, global_index, ops);
+		}
+		break;
+	case maple_leaf_64:
+	case maple_range_64:
+		do_mt_range64(entry, min, max, depth, path, global_index, ops);
+		break;
+	case maple_arange_64:
+		do_mt_arange64(entry, min, max, depth, path, global_index, ops);
+		break;
+	default:
+		fprintf(fp, " UNKNOWN TYPE\n");
+	}
+}
+
+static int do_maple_tree_traverse(ulong ptr, int is_root,
+				  struct maple_tree_ops *ops)
+{
+	char path[BUFSIZE] = {0};
+	char tree_buf[MAPLE_BUFSIZE];
+	ulong entry;
+	struct tree_data *td = ops->is_td ? (struct tree_data *)ops->private : NULL;
+	ulong global_index = 0;
+
+	if (size_maple_tree > MAPLE_BUFSIZE) {
+		fprintf(fp, "MAPLE_BUFSIZE should be larger than maple_tree struct");
+		return -1;
+	}
+
+	if (!is_root) {
+		strcpy(path, "direct");
+		do_mt_node(ptr, 0, mt_max[mte_node_type(ptr)],
+			   0, path, &global_index, ops);
+	} else {
+		if (readmem(ptr, tree_buf, size_maple_tree, "mt_dump read maple_tree", KVADDR) < 0) {
+			fprintf(stderr, "do_maple_tree_traverse: failed to read maple_tree at 0x%lx\n", ptr);
+			return -1;
+		}
+
+		entry = ULONG(tree_buf + offset_maple_tree_ma_root);
+
+		if (td && td->flags & TREE_STRUCT_VERBOSE) {
+			fprintf(fp, "maple_tree(%lx) flags %X, height %u root 0x%lx\n\n",
+				ptr, UINT(tree_buf + offset_maple_tree_ma_flags),
+				mt_height(tree_buf), entry);
+		}
+
+		if (!xa_is_node(entry))
+			do_mt_entry(entry, 0, 0, 0, 0, path, &global_index, ops);
+		else if (entry) {
+			strcpy(path, "root");
+			do_mt_node(entry, 0, mt_max[mte_node_type(entry)], 0,
+				   path, &global_index, ops);
+		}
+	}
+	return 0;
+}
+
+int do_mptree(struct tree_data *td)
+{
+	maple_init();
+
+	if (!fp) {
+		fprintf(stderr, "maple_tree: not initialized, call maple_init() first\n");
+		return -1;
+	}
+
+	struct maple_tree_ops ops = {
+		.entry		= NULL,
+		.private	= td,
+		.is_td		= true,
+	};
+
+	int is_root = !(td->flags & TREE_NODE_POINTER);
+
+	do_maple_tree_traverse(td->start, is_root, &ops);
+
+	return 0;
+}
+
+/************* For do_maple_tree *****************/
+static void do_maple_tree_count(ulong node, ulong slot, const char *path,
+				ulong index, void *private)
+{
+	struct do_maple_tree_info *info = private;
+	info->count++;
+}
+
+static void do_maple_tree_search(ulong node, ulong slot, const char *path,
+				 ulong index, void *private)
+{
+	struct do_maple_tree_info *info = private;
+	struct list_pair *lp = info->data;
+
+	if (lp->index == index) {
+		lp->value = (void *)slot;
+		info->count = 1;
+	}
+}
+
+static void do_maple_tree_dump(ulong node, ulong slot, const char *path,
+			       ulong index, void *private)
+{
+	struct do_maple_tree_info *info = private;
+	fprintf(fp, "[%lu] %lx\n", index, slot);
+	info->count++;
+}
+
+static void do_maple_tree_gather(ulong node, ulong slot, const char *path,
+				 ulong index, void *private)
+{
+	struct do_maple_tree_info *info = private;
+	struct list_pair *lp = info->data;
+
+	if (info->maxcount) {
+		lp[info->count].index = index;
+		lp[info->count].value = (void *)slot;
+
+		info->count++;
+		info->maxcount--;
+	}
+}
+
+static void do_maple_tree_dump_cb(ulong node, ulong slot, const char *path,
+				  ulong index, void *private)
+{
+	struct do_maple_tree_info *info = private;
+	struct list_pair *lp = info->data;
+	int (*cb)(ulong) = lp->value;
+
+	/* Caller defined operation */
+	if (!cb(slot)) {
+		fprintf(fp, "do_maple_tree: callback "
+			"operation failed: entry: %ld  item: %lx\n",
+			info->count, slot);
+		return;
+	}
+	info->count++;
+}
+
+/*
+ *  do_maple_tree argument usage:
+ *
+ *    root: Address of a maple_tree_root structure
+ *
+ *    flag: MAPLE_TREE_COUNT - Return the number of entries in the tree.
+ *          MAPLE_TREE_SEARCH - Search for an entry at lp->index; if found,
+ *            store the entry in lp->value and return a count of 1; otherwise
+ *            return a count of 0.
+ *          MAPLE_TREE_DUMP - Dump all existing index/value pairs.
+ *          MAPLE_TREE_GATHER - Store all existing index/value pairs in the
+ *            passed-in array of list_pair structs starting at lp,
+ *            returning the count of entries stored; the caller can/should
+ *            limit the number of returned entries by putting the array size
+ *            (max count) in the lp->index field of the first structure
+ *            in the passed-in array.
+ *          MAPLE_TREE_DUMP_CB - Similar with MAPLE_TREE_DUMP, but for each
+ *            maple tree entry, a user defined callback at lp->value will
+ *            be invoked.
+ *
+ *     lp: Unused by MAPLE_TREE_COUNT and MAPLE_TREE_DUMP.
+ *          A pointer to a list_pair structure for MAPLE_TREE_SEARCH.
+ *          A pointer to an array of list_pair structures for
+ *          MAPLE_TREE_GATHER; the dimension (max count) of the array may
+ *          be stored in the index field of the first structure to avoid
+ *          any chance of an overrun.
+ *          For MAPLE_TREE_DUMP_CB, the lp->value must be initialized as a
+ *          callback function.  The callback prototype must be: int (*)(ulong);
+ */
+ulong
+do_maple_tree(ulong root, int flag, struct list_pair *lp)
+{
+	maple_init();
+
+	if (!fp) {
+		fprintf(stderr, "maple_tree: not initialized, call maple_init() first\n");
+		return 0;
+	}
+
+	struct do_maple_tree_info info = {
+		.count		= 0,
+		.data		= lp,
+	};
+	struct maple_tree_ops ops = {
+		.private	= &info,
+		.is_td		= false,
+	};
+
+	switch (flag)
+	{
+	case MAPLE_TREE_COUNT:
+		ops.entry = do_maple_tree_count;
+		break;
+
+	case MAPLE_TREE_SEARCH:
+		ops.entry = do_maple_tree_search;
+		break;
+
+	case MAPLE_TREE_DUMP:
+		ops.entry = do_maple_tree_dump;
+		break;
+
+	case MAPLE_TREE_GATHER:
+		if (!(info.maxcount = lp->index))
+			info.maxcount = (ulong)(-1);   /* caller beware */
+
+		ops.entry = do_maple_tree_gather;
+		break;
+
+	case MAPLE_TREE_DUMP_CB:
+		if (lp->value == NULL) {
+			fprintf(fp, "do_maple_tree: need set callback function");
+			return 0;
+		}
+		ops.entry = do_maple_tree_dump_cb;
+		break;
+
+	default:
+		fprintf(fp, "do_maple_tree: invalid flag: %d\n", flag);
+		return 0;
+	}
+
+	do_maple_tree_traverse(root, true, &ops);
+	return info.count;
+}
+
+/***********************************************/
+void maple_init(void)
+{
+	if (maple_initialized)
+		return;
+	maple_initialized = true;
+
+	if (SIZE_EXISTS("maple_tree")) {
+		fp = stdout;
+
+		size_maple_tree = SIZE("maple_tree");
+		size_maple_node = SIZE("maple_node");
+
+		offset_maple_tree_ma_root = OFFSET("maple_tree.ma_root");
+		offset_maple_tree_ma_flags = OFFSET("maple_tree.ma_flags");
+
+		offset_maple_node_parent = OFFSET("maple_node.parent");
+		offset_maple_node_ma64 = OFFSET("maple_node.ma64");
+		offset_maple_node_mr64 = OFFSET("maple_node.mr64");
+		offset_maple_node_slot = OFFSET("maple_node.slot");
+
+		offset_maple_arange_64_pivot = OFFSET("maple_arange_64.pivot");
+		offset_maple_arange_64_slot = OFFSET("maple_arange_64.slot");
+		offset_maple_arange_64_gap = OFFSET("maple_arange_64.gap");
+		offset_maple_arange_64_meta = OFFSET("maple_arange_64.meta");
+
+		offset_maple_range_64_pivot = OFFSET("maple_range_64.pivot");
+		offset_maple_range_64_slot = OFFSET("maple_range_64.slot");
+
+		offset_maple_metadata_end = OFFSET("maple_metadata.end");
+		offset_maple_metadata_gap = OFFSET("maple_metadata.gap");
+
+		mt_max[maple_dense]           = mt_slots[maple_dense];
+		mt_max[maple_leaf_64]         = ULONG_MAX;
+		mt_max[maple_range_64]        = ULONG_MAX;
+		mt_max[maple_arange_64]       = ULONG_MAX;
+
+	} else {
+		pr_info("maple_tree not found in vmcoreinfo.\n");
+	}
+}
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.