aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumanth Korikkar <sumanthk@linux.ibm.com>2025-10-16 17:38:02 +0200
committerKarel Zak <kzak@redhat.com>2025-11-07 10:25:59 +0100
commit92d018a105e0b2ee6b7bafe25681de919247373d (patch)
treef57df5644c6a679e898a7891d2a34bca391c1d23
parent52491bde50e0afadc6e8d6f840ccd17bf4df62e0 (diff)
downloadutil-linux-92d018a105e0b2ee6b7bafe25681de919247373d.tar.gz
lsmem: add support to display dynamic (de)configuration of memory
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>
-rw-r--r--sys-utils/lsmem.c107
1 files changed, 95 insertions, 12 deletions
diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c
index 6151e269ff..648c0bc274 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"));