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 2/6] lsmem: add support to display dynamic (de)configuration of memory
Date: Thu, 16 Oct 2025 12:16:49 +0200 [thread overview]
Message-ID: <20251016101701.552597-3-sumanthk@linux.ibm.com> (raw)
In-Reply-To: <20251016101701.552597-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 | 115 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 103 insertions(+), 12 deletions(-)
diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c
index ea818d6dc..7b456492e 100644
--- a/sys-utils/lsmem.c
+++ b/sys-utils/lsmem.c
@@ -32,6 +32,7 @@
#include "optutils.h"
#define _PATH_SYS_MEMORY "/sys/devices/system/memory"
+#define _PATH_SYS_MEMCONFIG "/sys/firmware/memory"
#define MEMORY_STATE_ONLINE 0
#define MEMORY_STATE_OFFLINE 1
@@ -58,12 +59,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;
@@ -85,7 +91,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;
};
@@ -97,6 +106,8 @@ enum {
COL_BLOCK,
COL_NODE,
COL_ZONES,
+ COL_CONFIG,
+ COL_MEMMAP,
};
static const char *const zone_names[] = {
@@ -127,6 +138,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
@@ -196,6 +211,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)
@@ -218,12 +235,32 @@ 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 i)
+{
+ struct coldesc *ci = get_column_desc(i);
+
+ if (!strcmp(ci->name, "MEMMAP-ON-MEMORY") || !strcmp(ci->name, "CONFIGURED")) {
+ if (ul_path_access(lsmem->sysmemconfig, F_OK, "memory0") == 0)
+ return false;
+ else
+ return true;
+ } else {
+ return false;
+ }
+}
+
static void add_scols_line(struct lsmem *lsmem, struct memory_block *blk)
{
size_t i;
@@ -236,6 +273,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, i))
+ continue;
switch (get_column_id(i)) {
case COL_RANGE:
{
@@ -291,6 +330,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 +447,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 +519,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 +538,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 (ul_path_access(lsmem->sysmemconfig, F_OK, "memory0") != 0) {
+ 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 +574,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 +599,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];
@@ -539,6 +623,8 @@ static void read_basic_info(struct lsmem *lsmem)
/* The valid_zones sysmem attribute was introduced with kernel 3.18 */
if (ul_path_access(lsmem->sysmem, F_OK, "memory0/valid_zones") == 0)
lsmem->have_zones = 1;
+ if (ul_path_access(lsmem->sysmemconfig, F_OK, "memory0") == 0)
+ lsmem->have_memconfig = 1;
}
static void __attribute__((__noreturn__)) usage(void)
@@ -696,6 +782,9 @@ 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);
+ 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 +834,8 @@ int main(int argc, char **argv)
struct coldesc *ci = get_column_desc(i);
struct libscols_column *cl;
+ if (skip_memconfig_column(lsmem, 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.48.1
next prev parent reply other threads:[~2025-10-16 10:17 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-16 10:16 [PATCH 0/6] chmem/lsmem: dynamic (de)configuration of memory Sumanth Korikkar
2025-10-16 10:16 ` [PATCH 1/6] lsmem: display global memmap on memory parameter Sumanth Korikkar
2025-10-16 10:51 ` Karel Zak
2025-10-16 12:02 ` Sumanth Korikkar
2025-10-16 10:16 ` Sumanth Korikkar [this message]
2025-10-16 11:11 ` [PATCH 2/6] lsmem: add support to display dynamic (de)configuration of memory Karel Zak
2025-10-16 12:04 ` Sumanth Korikkar
2025-10-16 10:16 ` [PATCH 3/6] chmem: add support for dynamic (de)configuration of hotplug memory Sumanth Korikkar
2025-10-16 11:39 ` Karel Zak
2025-10-16 12:08 ` Sumanth Korikkar
2025-10-16 10:16 ` [PATCH 4/6] chmem: add chmem documentation for dynamic (de)configuration of memory Sumanth Korikkar
2025-10-16 10:16 ` [PATCH 5/6] lsmem: add doc for dynamic (de)configuration and memmap-on-memory support Sumanth Korikkar
2025-10-16 10:16 ` [PATCH 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=20251016101701.552597-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).