util-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sumanth Korikkar <sumanthk@linux.ibm.com>
To: Karel Zak <kzak@redhat.com>, util-linux@vger.kernel.org
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>,
	Heiko Carstens <hca@linux.ibm.com>,
	Vasily Gorbik <gor@linux.ibm.com>,
	Alexander Gordeev <agordeev@linux.ibm.com>,
	Sumanth Korikkar <sumanthk@linux.ibm.com>
Subject: [PATCH v2 2/6] lsmem: add support to display dynamic (de)configuration of memory
Date: Thu, 16 Oct 2025 17:38:02 +0200	[thread overview]
Message-ID: <20251016153808.3565873-3-sumanthk@linux.ibm.com> (raw)
In-Reply-To: <20251016153808.3565873-1-sumanthk@linux.ibm.com>

Extend lsmem to display (de)configured blocks and memmap_on_memory
state. With the new s390 kernel interface (linux-next) ff18dcb19aab
("s390/sclp: Add support for dynamic (de)configuration of memory"),
standby memory blocks are no longer pre-added at boot, but must be
explicitly configured before being eligible for online/offline
operations. At configuration time, users can also decide whether to use
memmap-on-memory per block.

Add CONFIGURED column : indicate if a memory block has been explicitly
configured.

Add MEMMAP-ON-MEMORY column : indicate if a memory block uses
memmap-on-memory.

memmap-on-memory reference:
https://docs.kernel.org/admin-guide/mm/memory-hotplug.html

Users can now inspect memory configuration state and retrieve
memmap-on-memory state per block.

lsmem -o RANGE,SIZE,STATE,BLOCK,CONFIGURED,MEMMAP-ON-MEMORY
RANGE                   SIZE   STATE   BLOCK CONFIGURED MEMMAP-ON-MEMORY
0x00000000-0x7fffffff   2G     online  0-15  yes        no
0x80000000-0xffffffff   2G     offline 16-31 no         yes

Memory block size:                128M
Total online memory:                2G
Total offline memory:               2G
Memmap on memory parameter:        yes

Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
 sys-utils/lsmem.c | 107 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 95 insertions(+), 12 deletions(-)

diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c
index 6151e269f..648c0bc27 100644
--- a/sys-utils/lsmem.c
+++ b/sys-utils/lsmem.c
@@ -33,6 +33,7 @@
 
 #define _PATH_SYS_MEMORY		"/sys/devices/system/memory"
 #define _PATH_SYS_MEMMAP_PARM		"/sys/module/memory_hotplug/parameters/memmap_on_memory"
+#define _PATH_SYS_MEMCONFIG		"/sys/firmware/memory"
 
 #define MEMORY_STATE_ONLINE		0
 #define MEMORY_STATE_OFFLINE		1
@@ -59,12 +60,17 @@ struct memory_block {
 	int		nr_zones;
 	int		zones[MAX_NR_ZONES];
 	bool		removable;
+	bool		config;
+	bool		memmap_on_memory;
 };
 
 struct lsmem {
 	struct path_cxt		*sysmem;		/* _PATH_SYS_MEMORY directory handler */
+	struct path_cxt		*sysmemconfig;		/* _PATH_SYS_MEMCONFIG directory handler */
 	struct dirent		**dirs;
+	struct dirent		**memconfig_dirs;
 	int			ndirs;
+	int			memconfig_ndirs;
 	struct memory_block	*blocks;
 	int			nblocks;
 	uint64_t		block_size;
@@ -86,7 +92,10 @@ struct lsmem {
 				split_by_state : 1,
 				split_by_removable : 1,
 				split_by_zones : 1,
-				have_zones : 1;
+				split_by_memmap_on_memory : 1,
+				split_by_config : 1,
+				have_zones : 1,
+				have_memconfig : 1;
 };
 
 
@@ -98,6 +107,8 @@ enum {
 	COL_BLOCK,
 	COL_NODE,
 	COL_ZONES,
+	COL_CONFIG,
+	COL_MEMMAP,
 };
 
 static const char *const zone_names[] = {
@@ -128,6 +139,10 @@ static struct coldesc coldescs[] = {
 	[COL_BLOCK]	= { "BLOCK", 0, SCOLS_FL_RIGHT, N_("memory block number or blocks range")},
 	[COL_NODE]	= { "NODE", 0, SCOLS_FL_RIGHT, N_("numa node of memory")},
 	[COL_ZONES]	= { "ZONES", 0, SCOLS_FL_RIGHT, N_("valid zones for the memory range")},
+	[COL_CONFIG]	= { "CONFIGURED", 0, SCOLS_FL_RIGHT,
+			    N_("configuration status of the memory range")},
+	[COL_MEMMAP]	= { "MEMMAP-ON-MEMORY", 0, SCOLS_FL_RIGHT,
+			    N_("memmap-on-memory status of the memory range")},
 };
 
 /* columns[] array specifies all currently wanted output column. The columns
@@ -197,6 +212,8 @@ static inline void reset_split_policy(struct lsmem *l, int enable)
 	l->split_by_node = enable;
 	l->split_by_removable = enable;
 	l->split_by_zones = enable;
+	l->split_by_memmap_on_memory = enable;
+	l->split_by_config = enable;
 }
 
 static void set_split_policy(struct lsmem *l, int cols[], size_t ncols)
@@ -219,12 +236,23 @@ static void set_split_policy(struct lsmem *l, int cols[], size_t ncols)
 		case COL_ZONES:
 			l->split_by_zones = 1;
 			break;
+		case COL_MEMMAP:
+			l->split_by_memmap_on_memory = 1;
+			break;
+		case COL_CONFIG:
+			l->split_by_config = 1;
+			break;
 		default:
 			break;
 		}
 	}
 }
 
+static bool skip_memconfig_column(struct lsmem *lsmem, int id)
+{
+	return (id == COL_MEMMAP || id == COL_CONFIG) && !lsmem->have_memconfig;
+}
+
 static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk)
 {
 	size_t i;
@@ -237,6 +265,8 @@ static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk)
 	for (i = 0; i < ncolumns; i++) {
 		char *str = NULL;
 
+		if (skip_memconfig_column(lsmem, get_column_id(i)))
+			continue;
 		switch (get_column_id(i)) {
 		case COL_RANGE:
 		{
@@ -292,6 +322,14 @@ static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk)
 				str = xstrdup(valid_zones);
 			}
 			break;
+		case COL_CONFIG:
+			if (lsmem->have_memconfig)
+				str = xstrdup(blk->config ? _("yes") : _("no"));
+			break;
+		case COL_MEMMAP:
+			if (lsmem->have_memconfig)
+				str = xstrdup(blk->memmap_on_memory ? _("yes") : _("no"));
+			break;
 		}
 
 		if (str && scols_line_refer_data(line, i, str) != 0)
@@ -400,6 +438,15 @@ static int memory_block_read_attrs(struct lsmem *lsmem, char *name,
 	if (errno)
 		rc = -errno;
 
+	if (lsmem->have_memconfig) {
+		if (ul_path_readf_s32(lsmem->sysmemconfig, &x, "%s/config", name) == 0)
+			blk->config = x == 1;
+		if (ul_path_readf_s32(lsmem->sysmemconfig, &x, "%s/memmap_on_memory", name) == 0)
+			blk->memmap_on_memory = x == 1;
+		blk->state = MEMORY_STATE_OFFLINE;
+		if (!blk->config)
+			return rc;
+	}
 	if (ul_path_readf_s32(lsmem->sysmem, &x, "%s/removable", name) == 0)
 		blk->removable = x == 1;
 
@@ -463,6 +510,10 @@ static int is_mergeable(struct lsmem *lsmem, struct memory_block *blk)
 				return 0;
 		}
 	}
+	if (lsmem->split_by_config && curr->config != blk->config)
+		return 0;
+	if (lsmem->split_by_memmap_on_memory && curr->memmap_on_memory != blk->memmap_on_memory)
+		return 0;
 	return 1;
 }
 
@@ -478,11 +529,33 @@ static void free_info(struct lsmem *lsmem)
 	free(lsmem->dirs);
 }
 
+static int memory_block_filter(const struct dirent *de)
+{
+	if (strncmp("memory", de->d_name, 6) != 0)
+		return 0;
+	return isdigit_string(de->d_name + 6);
+}
+
+static void read_memconfig(struct lsmem *lsmem)
+{
+	char dir[PATH_MAX];
+
+	if (!lsmem->have_memconfig) {
+		lsmem->memconfig_ndirs = 0;
+		return;
+	}
+	ul_path_get_abspath(lsmem->sysmemconfig, dir, sizeof(dir), NULL);
+	lsmem->memconfig_ndirs = scandir(dir, &lsmem->memconfig_dirs,
+					 memory_block_filter, versionsort);
+	if (lsmem->memconfig_ndirs <= 0)
+		err(EXIT_FAILURE, _("Failed to read %s"), dir);
+}
+
 static void read_info(struct lsmem *lsmem)
 {
 	struct memory_block blk;
-	char buf[128];
-	int i;
+	char buf[128], *name;
+	int i, num_dirs;
 
 	if (ul_path_read_buffer(lsmem->sysmem, buf, sizeof(buf), "block_size_bytes") <= 0)
 		err(EXIT_FAILURE, _("failed to read memory block size"));
@@ -492,8 +565,17 @@ static void read_info(struct lsmem *lsmem)
 	if (errno)
 		err(EXIT_FAILURE, _("failed to read memory block size"));
 
-	for (i = 0; i < lsmem->ndirs; i++) {
-		memory_block_read_attrs(lsmem, lsmem->dirs[i]->d_name, &blk);
+	read_memconfig(lsmem);
+	if (lsmem->have_memconfig)
+		num_dirs = lsmem->memconfig_ndirs;
+	else
+		num_dirs = lsmem->ndirs;
+	for (i = 0; i < num_dirs; i++) {
+		if (lsmem->have_memconfig)
+			name = lsmem->memconfig_dirs[i]->d_name;
+		else
+			name = lsmem->dirs[i]->d_name;
+		memory_block_read_attrs(lsmem, name, &blk);
 		if (blk.state == MEMORY_STATE_ONLINE)
 			lsmem->mem_online += lsmem->block_size;
 		else
@@ -508,13 +590,6 @@ static void read_info(struct lsmem *lsmem)
 	}
 }
 
-static int memory_block_filter(const struct dirent *de)
-{
-	if (strncmp("memory", de->d_name, 6) != 0)
-		return 0;
-	return isdigit_string(de->d_name + 6);
-}
-
 static void read_basic_info(struct lsmem *lsmem)
 {
 	char dir[PATH_MAX];
@@ -696,6 +771,12 @@ int main(int argc, char **argv)
 	lsmem->sysmem = ul_new_path(_PATH_SYS_MEMORY);
 	if (!lsmem->sysmem)
 		err(EXIT_FAILURE, _("failed to initialize %s handler"), _PATH_SYS_MEMORY);
+	lsmem->sysmemconfig = ul_new_path(_PATH_SYS_MEMCONFIG);
+	/* Always check for the existence of /sys/firmware/memory/memory0 first */
+	if (ul_path_access(lsmem->sysmemconfig, F_OK, "memory0") == 0)
+		lsmem->have_memconfig = 1;
+	if (!lsmem->sysmemconfig)
+		err(EXIT_FAILURE, _("failed to initialized %s handler"), _PATH_SYS_MEMCONFIG);
 	if (prefix && ul_path_set_prefix(lsmem->sysmem, prefix) != 0)
 		err(EXIT_FAILURE, _("invalid argument to --sysroot"));
 	if (!ul_path_is_accessible(lsmem->sysmem))
@@ -745,6 +826,8 @@ int main(int argc, char **argv)
 		struct coldesc *ci = get_column_desc(i);
 		struct libscols_column *cl;
 
+		if (skip_memconfig_column(lsmem, get_column_id(i)))
+			continue;
 		cl = scols_table_new_column(lsmem->table, ci->name, ci->whint, ci->flags);
 		if (!cl)
 			err(EXIT_FAILURE, _("failed to initialize output column"));
-- 
2.41.0


  parent reply	other threads:[~2025-10-16 15:38 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-16 15:38 [PATCH v2 0/6] chmem/lsmem: dynamic (de)configuration of memory Sumanth Korikkar
2025-10-16 15:38 ` [PATCH v2 1/6] lsmem: display global memmap on memory parameter Sumanth Korikkar
2025-10-16 15:38 ` Sumanth Korikkar [this message]
2025-10-16 15:38 ` [PATCH v2 3/6] chmem: add support for dynamic (de)configuration of hotplug memory Sumanth Korikkar
2025-10-16 15:38 ` [PATCH v2 4/6] chmem: add chmem documentation for dynamic (de)configuration of memory Sumanth Korikkar
2025-10-16 15:38 ` [PATCH v2 5/6] lsmem: add doc for dynamic (de)configuration and memmap-on-memory support Sumanth Korikkar
2025-10-16 15:38 ` [PATCH v2 6/6] lsmem,chmem: add configure/deconfigure bash completion options Sumanth Korikkar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251016153808.3565873-3-sumanthk@linux.ibm.com \
    --to=sumanthk@linux.ibm.com \
    --cc=agordeev@linux.ibm.com \
    --cc=gerald.schaefer@linux.ibm.com \
    --cc=gor@linux.ibm.com \
    --cc=hca@linux.ibm.com \
    --cc=kzak@redhat.com \
    --cc=util-linux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).