Util-Linux package development
 help / color / mirror / Atom feed
* Re: `losetup --remove` is confusing, and misuse silently fails
From: Karel Zak @ 2025-10-16 10:28 UTC (permalink / raw)
  To: Benno Schulenberg; +Cc: Util-Linux, wguanghao
In-Reply-To: <a5909aac-3f54-4fce-8785-410535dd4098@telfort.nl>

On Fri, Oct 10, 2025 at 11:41:08AM +0200, Benno Schulenberg wrote:
> After setting up a loop device with:
> 
>   # ./losetup loop4 some.img
> 
> I wanted to see what happens when trying to "remove" it
> (since the man page says it is "not recommended"):

https://github.com/util-linux/util-linux/pull/3803

A new PR, summary:

 - losetup: add error feedback for --remove command - Improves error
   reporting in the remove_loop() function so users see meaningful
   error messages when removal fails.

 - losetup: make --remove a long-only option with mutual exclusivity -
   Removes the -R short option (keeping only --remove) and adds mutual
   exclusivity with other major operations like -d, -D, -a, -c, -f,
   -j, -l, and -O. Updates man page, bash completion, and usage text.

 - losetup: improve --remove documentation - Rewrites the --remove
   description and adds a new "LOOP DEVICE LIFECYCLE" section
   explaining the difference between creation, detachment, and removal
   operations.

 Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* [PATCH 6/6] lsmem,chmem: add configure/deconfigure bash completion options
From: Sumanth Korikkar @ 2025-10-16 10:16 UTC (permalink / raw)
  To: Karel Zak, util-linux
  Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Sumanth Korikkar
In-Reply-To: <20251016101701.552597-1-sumanthk@linux.ibm.com>

Add bash completion for configure/deconfigure options in chmem and lsmem.

Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
 bash-completion/chmem | 3 +++
 bash-completion/lsmem | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/bash-completion/chmem b/bash-completion/chmem
index 3e3af87ac..f10646677 100644
--- a/bash-completion/chmem
+++ b/bash-completion/chmem
@@ -14,6 +14,9 @@ _chmem_module()
 			OPTS="
 				--enable
 				--disable
+				--configure
+				--deconfigure
+				--memmap-on-memory
 				--blocks
 				--verbose
 				--zone
diff --git a/bash-completion/lsmem b/bash-completion/lsmem
index 7d6e84247..185a15fd2 100644
--- a/bash-completion/lsmem
+++ b/bash-completion/lsmem
@@ -9,7 +9,7 @@ _lsmem_module()
 			local prefix realcur OUTPUT_ALL OUTPUT
 			realcur="${cur##*,}"
 			prefix="${cur%$realcur}"
-			OUTPUT_ALL='RANGE SIZE STATE REMOVABLE BLOCK NODE ZONES'
+			OUTPUT_ALL='RANGE SIZE STATE REMOVABLE BLOCK NODE ZONES CONFIGURED MEMMAP-ON-MEMORY'
 			for WORD in $OUTPUT_ALL; do
 				if ! [[ $prefix == *"$WORD"* ]]; then
 					OUTPUT="$WORD ${OUTPUT:-""}"
-- 
2.48.1


^ permalink raw reply related

* [PATCH 5/6] lsmem: add doc for dynamic (de)configuration and memmap-on-memory support
From: Sumanth Korikkar @ 2025-10-16 10:16 UTC (permalink / raw)
  To: Karel Zak, util-linux
  Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Sumanth Korikkar
In-Reply-To: <20251016101701.552597-1-sumanthk@linux.ibm.com>

lsmem --output-all now displays two new columns:

CONFIGURED : yes/no indicating if a memory block has been explicitly
configured.

MEMMAP-ON-MEMORY : yes/no indicating whether the block uses
memmap-on-memory.

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

Add documentation for new fields.

Reviewed-by: Maria Eisenhaendler <maria1@de.ibm.com>
Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
 sys-utils/lsmem.1.adoc | 44 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/sys-utils/lsmem.1.adoc b/sys-utils/lsmem.1.adoc
index 9c9397631..e7226725a 100644
--- a/sys-utils/lsmem.1.adoc
+++ b/sys-utils/lsmem.1.adoc
@@ -24,7 +24,45 @@ The *lsmem* command lists a new memory range always when the current memory bloc
 
 Note that some output columns may provide inaccurate information if a split policy forces *lsmem* to ignore differences in some attributes. For example if you merge removable and non-removable memory blocks to the one range than all the range will be marked as non-removable on *lsmem* output.
 
-Not all columns are supported on all systems. If an unsupported column is specified, *lsmem* prints the column but does not provide any data for it.
+The supported columns are RANGE, SIZE, STATE, REMOVABLE, BLOCK, NODE, ZONES, CONFIGURED, MEMMAP-ON-MEMORY.
+RANGE
+The start and end physical address of the memory range.
+
+SIZE
+The size of the memory range, representing the total amount of memory in that range.
+
+STATE
+The current online status of the memory range. Common states include online, offline or transitional states.
+
+BLOCK
+The specific memory block number.
+
+NODE
+The NUMA (Non-Uniform Memory Access) node to which the memory block belongs.
+
+ZONES
+The memory zones to which the blocks belongs, such as DMA, Normal, Movable.
+
+CONFIGURED
+The configuration state of a memory block. Refer to *chmem* for details on configuring or deconfiguring memory blocks.
+
+MEMMAP-ON-MEMORY
+The memmap-on-memory state of the memory block at configuration time. This setting indicates where memory hotplug stores its internal metadata (the struct pages array or memmap). If MEMMAP-ON-MEMORY is set to 1, the metadata is allocated directly from the newly added hotplugged memory, enabling hot-add operations even when the system is under high memory pressure. If set to 0, the memmap metadata is allocated from existing system memory.
+
+Possible BLOCK, CONFIGURED, STATE, MEMMAP-ON-MEMORY states::
+
+[cols="10,10,10,15,60", options="header"]
+|===
+| BLOCK | STATE   | CONFIGURED | MEMMAP-ON-MEMORY | Description
+
+| 0     | online  | yes        | yes/no           | The memory is configured with memmap-on-memory set to (1 or 0) and memory is currently online.
+
+| 1     | offline | yes        | yes/no           | The memory is configured, but memory is offline.
+
+| 2     | offline | no         | yes/no           | The memory is offline and deconfigured.
+|===
+
+Not all columns are supported on all systems. If an unsupported column is specified, *lsmem* prints the column but does not provide any data for it. Additionally, *lsmem* may skip columns like CONFIGURED or MEMMAP-ON-MEMORY if these states are not relevant to the system's architecture.
 
 Use the *--help* option to see the columns description.
 
@@ -45,7 +83,7 @@ Use JSON output format.
 Do not print a header line.
 
 *-o*, *--output* _list_::
-Specify which output columns to print. Use *--help* to get a list of all supported columns. The default list of columns may be extended if _list_ is specified in the format **+**__list__ (e.g., *lsmem -o +NODE*).
+Specify which output columns to print. Use *--help* to obtain a list of all supported columns. To extend the default list of columns specify _list_ in the format **+**__list__. For example, *lsmem -o +NODE*.
 
 *--output-all*::
 Output all available columns.
@@ -57,7 +95,7 @@ Produce output in the form of key="value" pairs. All potentially unsafe value ch
 Produce output in raw format. All potentially unsafe characters are hex-escaped (\x<code>).
 
 *-S*, *--split* _list_::
-Specify which columns (attributes) use to split memory blocks to ranges. The supported columns are STATE, REMOVABLE, NODE and ZONES, or "none". The other columns are silently ignored. For more details see *DESCRIPTION* above.
+Specify which columns are used to split memory blocks to ranges. The supported columns are STATE, REMOVABLE, NODE, ZONES, CONFIGURED, MEMMAP-ON-MEMORY or "none". The other columns are silently ignored. For more details see *DESCRIPTION* above.
 
 *-s*, *--sysroot* _directory_::
 Gather memory data for a Linux instance other than the instance from which the *lsmem* command is issued. The specified _directory_ is the system root of the Linux instance to be inspected.
-- 
2.48.1


^ permalink raw reply related

* [PATCH 3/6] chmem: add support for dynamic (de)configuration of hotplug memory
From: Sumanth Korikkar @ 2025-10-16 10:16 UTC (permalink / raw)
  To: Karel Zak, util-linux
  Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Sumanth Korikkar
In-Reply-To: <20251016101701.552597-1-sumanthk@linux.ibm.com>

Extend chmem to use the new s390 kernel interface for configuring and
deconfiguring hotpluggable memory blocks, with memmap-on-memory support.

Background:
On s390, memmap-on-memory was introduced to ensure that the struct page
array (metadata) for hotpluggable standby memory is allocated from the
memory block itself. This allowed hot-add operations even under memory
pressure, particularly in cases with a strong imbalance between
boot-time online memory and standby memory.

The original design, however, had few limitations:
* All hotpluggable standby memory was added at boot.
* The use of memmap-on-memory was global and static, decided at boot
  time. Either all standby blocks used it, or none of them did.
* memmap-on-memory choice could not be changed at runtime, limiting
  flexibility. For example, when continuous physical memory was required
  later across memory blocks.

The s390 kernel ff18dcb19aab ("s390/sclp: Add support for dynamic
(de)configuration of memory") no longer pre-adds all standby memory at
boot. Instead, users must explicitly configure a block before it can be
used for online/offline actions.  At configuration time, users can
dynamically decide whether to use optional memmap-on-memory for each
memory block, where value of 1 allocates metadata (such as struct pages
array) from the hotplug memory itself, enabling hot-add operations even
under memory pressure. A value of 0 stores metadata in regular system
memory and enables continuous physical memory across memory blocks.

s390 kernel sysfs interface to configure/deconfigure memory with
memmap-on-memory support looks as shown below:

1. Configure memory
echo 1 > /sys/firmware/memory/memoryX/config  

2. Deconfigure memory
echo 0 > /sys/firmware/memory/memoryX/config

3. Enable memmap-on-memory
echo 1 > /sys/firmware/memory/memoryX/memmap_on_memory

4. Disable memmap-on-memory
echo 0 > /sys/firmware/memory/memoryX/memmap_on_memory

* Initial memory layout:
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

* Configure memory with memmap-on-memory.
chmem -c 128M -m 1
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-0x87ffffff   128M   offline  16    yes        yes
0x88000000-0xffffffff   1.9G   offline  17-31 no         yes

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

* Deconfigure memory
chmem -g 128M
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

* Online memory.
If the memory is in deconfigured state, configure and online it.

chmem -e 128M -v
Memory Block 16 (0x0000000080000000-0x0000000087ffffff) configured
Memory Block 16 (0x0000000080000000-0x0000000087ffffff) enabled

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-0x87ffffff    128M  online   16    yes        yes
0x88000000-0xffffffff    1.9G  offline  17-31 no         yes

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

* Offline memory
If the memory is in online state, then offline it and deconfigure it.

chmem -d 128M -v
Memory Block 16 (0x0000000080000000-0x0000000087ffffff) disabled
Memory Block 16 (0x0000000080000000-0x0000000087ffffff) deconfigured

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

Just like online and offline actions, memory configuration and
deconfiguration can be controlled through similar options. Also,
memmap-on-memory setting can be changed, only when the memory block is
in deconfigured state. This means, it is usable only via --configure
option.

Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
 sys-utils/chmem.c | 372 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 343 insertions(+), 29 deletions(-)

diff --git a/sys-utils/chmem.c b/sys-utils/chmem.c
index bee2a90f8..9331925ff 100644
--- a/sys-utils/chmem.c
+++ b/sys-utils/chmem.c
@@ -37,11 +37,16 @@
 #define CHMEM_EXIT_SOMEOK		64
 
 #define _PATH_SYS_MEMORY		"/sys/devices/system/memory"
+#define _PATH_SYS_MEMCONFIG		"/sys/firmware/memory"
 
 struct chmem_desc {
 	struct path_cxt	*sysmem;	/* _PATH_SYS_MEMORY handler */
+	struct path_cxt *sysmemconfig;	/* _PATH_SYS_MEMCONFIG directory handler */
 	struct dirent	**dirs;
+	struct dirent   **memconfig_dirs;
 	int		ndirs;
+	int		memconfig_ndirs;
+	int		memmap_on_memory;
 	uint64_t	block_size;
 	uint64_t	start;
 	uint64_t	end;
@@ -50,11 +55,14 @@ struct chmem_desc {
 	unsigned int	is_size	   : 1;
 	unsigned int	verbose	   : 1;
 	unsigned int	have_zones : 1;
+	unsigned int	have_memconfig : 1;
 };
 
 enum {
 	CMD_MEMORY_ENABLE = 0,
 	CMD_MEMORY_DISABLE,
+	CMD_MEMORY_CONFIGURE,
+	CMD_MEMORY_DECONFIGURE,
 	CMD_NONE
 };
 
@@ -101,16 +109,130 @@ static void idxtostr(struct chmem_desc *desc, uint64_t idx, char *buf, size_t bu
 		 idx, start, end);
 }
 
-static int chmem_size(struct chmem_desc *desc, int enable, int zone_id)
+static bool chmem_memmap_on_memory_option_enabled(struct chmem_desc *desc)
+{
+	if (desc->memmap_on_memory == 0 || desc->memmap_on_memory == 1)
+		return true;
+	else
+		return false;
+}
+
+static int chmem_set_memmap_on_memory(struct chmem_desc *desc, char *name)
+{
+	char str[BUFSIZ];
+	int rc, index;
+
+	index = strtou64_or_err(name + 6, _("Failed to parse index"));
+	idxtostr(desc, index, str, sizeof(str));
+	rc = ul_path_writef_u64(desc->sysmemconfig, desc->memmap_on_memory,
+				"%s/memmap_on_memory", name);
+	if (rc)
+		warn(_("%s memmap-on-memory failed"), str);
+	return rc;
+}
+
+static int chmem_config(struct chmem_desc *desc, char *name, int configure)
+{
+	int mblock_configured, memmap, rc, index;
+	char str[BUFSIZ], state[BUFSIZ];
+
+	index = strtou64_or_err(name + 6, _("Failed to parse index"));
+	idxtostr(desc, index, str, sizeof(str));
+	rc = ul_path_readf_s32(desc->sysmemconfig, &mblock_configured, "%s/config", name);
+	if (rc)
+		goto out;
+	rc = ul_path_readf_s32(desc->sysmemconfig, &memmap, "%s/memmap_on_memory", name);
+	if (rc)
+		goto out;
+	if (mblock_configured) {
+		if (configure) {
+			if (chmem_memmap_on_memory_option_enabled(desc) &&
+					memmap != desc->memmap_on_memory) {
+				if (!desc->is_size || desc->verbose)
+					fprintf(stdout,
+						_("%s must be deconfigured before using -m option\n"), str);
+				rc = -1;
+			} else if (desc->is_size) {
+				/*
+				 * Allow chmem_onoff_size() to proceed with
+				 * configuring different memory blocks when the
+				 * current block is already configured.
+				 */
+				rc = -1;
+			} else if (desc->verbose) {
+				fprintf(stdout, _("%s already configured\n"), str);
+			}
+			goto out;
+		} else if (ul_path_readf_buffer(desc->sysmem, state,
+						sizeof(state), "%s/state", name) > 0 &&
+			   strncmp("online", state, 6) == 0) {
+			if (!desc->is_size || desc->verbose)
+				fprintf(stdout, "%s must be offline before deconfiguration\n", str);
+			rc = -1;
+			goto out;
+		}
+	} else {
+		/*
+		 * If memory block is currently in deconfigured state, set
+		 * memmap-on-memory if -m option is enabled.
+		 */
+		if (chmem_memmap_on_memory_option_enabled(desc)) {
+			rc = chmem_set_memmap_on_memory(desc, name);
+			if (rc)
+				goto out;
+		} else if (!configure) {
+			/*
+			 * Allow chmem_onoff_size() to proceed with
+			 * deconfiguring different memory blocks when the
+			 * current block is already deconfigured.
+			 */
+			if (desc->is_size)
+				rc = -1;
+			else if (desc->verbose)
+				fprintf(stdout, _("%s already deconfigured\n"), str);
+			goto out;
+		}
+	}
+	rc = ul_path_writef_u64(desc->sysmemconfig, configure ? 1 : 0, "%s/config", name);
+	if (rc) {
+		if (!desc->is_size) {
+			if (configure)
+				warn(_("%s configure failed\n"), str);
+			else
+				warn(_("%s deconfigure failed\n"), str);
+		} else if (desc->verbose) {
+			if (configure)
+				fprintf(stdout, _("%s configure failed\n"), str);
+			else
+				fprintf(stdout, _("%s deconfigure failed\n"), str);
+		}
+	} else if (desc->verbose) {
+		if (configure)
+			fprintf(stdout, _("%s configured\n"), str);
+		else
+			fprintf(stdout, _("%s deconfigured\n"), str);
+	}
+out:
+	return rc;
+}
+
+static int chmem_configured(struct chmem_desc *desc, char *name)
+{
+	int mblock_configured = 0;
+
+	ul_path_readf_s32(desc->sysmemconfig, &mblock_configured, "%s/config", name);
+	return mblock_configured;
+}
+
+static int chmem_onoff_size(struct chmem_desc *desc, int enable, int zone_id)
 {
 	char *name, *onoff, line[BUFSIZ], str[BUFSIZ];
 	uint64_t size, index;
+	int i, rc = 0, ndirs;
 	const char *zn;
-	int i, rc;
 
 	size = desc->size;
 	onoff = enable ? "online" : "offline";
-	i = enable ? 0 : desc->ndirs - 1;
 
 	if (enable && zone_id >= 0) {
 		if (zone_id == ZONE_MOVABLE)
@@ -118,15 +240,30 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id)
 		else
 			onoff = "online_kernel";
 	}
-
-	for (; i >= 0 && i < desc->ndirs && size; i += enable ? 1 : -1) {
-		name = desc->dirs[i]->d_name;
+	ndirs = desc->have_memconfig ? desc->memconfig_ndirs : desc->ndirs;
+	i = enable ? 0 : ndirs - 1;
+	for (; i >= 0 && i < ndirs && size; i += enable ? 1 : -1) {
+		if (desc->have_memconfig)
+			name = desc->memconfig_dirs[i]->d_name;
+		else
+			name = desc->dirs[i]->d_name;
 		index = strtou64_or_err(name + 6, _("Failed to parse index"));
-
-		if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0
-		    && strncmp(onoff, line, 6) == 0)
+		if (enable && desc->have_memconfig && !chmem_configured(desc, name)) {
+			/* Configure memory block */
+			rc = chmem_config(desc, name, enable);
+			if (rc)
+				continue;
+		} else if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0) {
+			if (strncmp(onoff, line, 6) == 0)
+				continue;
+		} else if (!enable) {
+			/*
+			 * If /sys/devices/system/memory/memoryX is
+			 * unavailable, memory block is offline and
+			 * deconfigured.
+			 */
 			continue;
-
+		}
 		if (desc->have_zones) {
 			ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/valid_zones", name);
 			if (zone_id >= 0) {
@@ -143,7 +280,6 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id)
 					onoff = "online";
 			}
 		}
-
 		idxtostr(desc, index, str, sizeof(str));
 		rc = ul_path_writef_string(desc->sysmem, onoff, "%s/state", name);
 		if (rc != 0 && desc->verbose) {
@@ -157,6 +293,12 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id)
 			else
 				fprintf(stdout, _("%s disabled\n"), str);
 		}
+		if (!rc && !enable && desc->have_memconfig) {
+			/* Deconfigure memory block */
+			rc = chmem_config(desc, name, enable);
+			if (rc)
+				continue;
+		}
 		if (rc == 0)
 			size--;
 	}
@@ -175,12 +317,80 @@ static int chmem_size(struct chmem_desc *desc, int enable, int zone_id)
 	return size == 0 ? 0 : size == desc->size ? -1 : 1;
 }
 
-static int chmem_range(struct chmem_desc *desc, int enable, int zone_id)
+static int chmem_config_size(struct chmem_desc *desc, int configure)
+{
+	uint64_t size;
+	char *name;
+	int i, rc;
+
+	if (!desc->have_memconfig) {
+		if (configure)
+			fprintf(stdout,
+				_("Skip configuration — use chmem -e instead\n"));
+		else
+			fprintf(stdout,
+				_("Skip deconfiguration - use chmem -d instead\n"));
+		return -1;
+	}
+	size = desc->size;
+	i = configure ? 0 : desc->memconfig_ndirs - 1;
+	for (; i >= 0 && i < desc->memconfig_ndirs && size; i += configure ? 1 : -1) {
+		name = desc->memconfig_dirs[i]->d_name;
+		rc = chmem_config(desc, name, configure);
+		if (rc == 0)
+			size--;
+	}
+	if (size) {
+		uint64_t bytes;
+		char *sizestr;
+
+		bytes = (desc->size - size) * desc->block_size;
+		sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, bytes);
+		if (configure)
+			fprintf(stdout, _("Could only configure %s of memory\n"), sizestr);
+		else
+			fprintf(stdout, _("Could only deconfigure %s of memory\n"), sizestr);
+		free(sizestr);
+	}
+	return size == 0 ? 0 : size == desc->size ? -1 : 1;
+}
+
+static int chmem_config_range(struct chmem_desc *desc, int configure)
+{
+	uint64_t index, todo;
+	char *name;
+	int rc, i;
+
+	if (!desc->have_memconfig) {
+		if (configure)
+			fprintf(stdout,
+				_("Skip configuration — use chmem -e instead\n"));
+		else
+			fprintf(stdout,
+				_("Skip deconfiguration - use chmem -d instead\n"));
+		return -1;
+	}
+	todo = desc->end - desc->start + 1;
+	for (i = 0; i < desc->memconfig_ndirs; i++) {
+		name = desc->memconfig_dirs[i]->d_name;
+		index = strtou64_or_err(name + 6, _("Failed to parse index"));
+		if (index < desc->start)
+			continue;
+		if (index > desc->end)
+			break;
+		rc = chmem_config(desc, name, configure);
+		if (rc == 0)
+			todo--;
+	}
+	return todo == 0 ? 0 : todo == desc->end - desc->start + 1 ? -1 : 1;
+}
+
+static int chmem_onoff_range(struct chmem_desc *desc, int enable, int zone_id)
 {
 	char *name, *onoff, line[BUFSIZ], str[BUFSIZ];
 	uint64_t index, todo;
+	int i, rc, ndirs;
 	const char *zn;
-	int i, rc;
 
 	todo = desc->end - desc->start + 1;
 	onoff = enable ? "online" : "offline";
@@ -192,22 +402,43 @@ static int chmem_range(struct chmem_desc *desc, int enable, int zone_id)
 			onoff = "online_kernel";
 	}
 
-	for (i = 0; i < desc->ndirs; i++) {
-		name = desc->dirs[i]->d_name;
+	ndirs = desc->have_memconfig ? desc->memconfig_ndirs : desc->ndirs;
+	for (i = 0; i < ndirs; i++) {
+		name = desc->have_memconfig ? desc->memconfig_dirs[i]->d_name :
+					      desc->dirs[i]->d_name;
 		index = strtou64_or_err(name + 6, _("Failed to parse index"));
 		if (index < desc->start)
 			continue;
 		if (index > desc->end)
 			break;
+		if (enable && desc->have_memconfig && !chmem_configured(desc, name)) {
+			/* Configure memory block */
+			rc = chmem_config(desc, name, enable);
+			if (rc)
+				continue;
+		}
 		idxtostr(desc, index, str, sizeof(str));
-		if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0
-		    && strncmp(onoff, line, 6) == 0) {
-			if (desc->verbose && enable)
-				fprintf(stdout, _("%s already enabled\n"), str);
-			else if (desc->verbose && !enable)
-				fprintf(stdout, _("%s already disabled\n"), str);
-			todo--;
-			continue;
+		if (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0) {
+			if (strncmp(onoff, line, 6) == 0) {
+				if (desc->verbose && enable)
+					fprintf(stdout, _("%s already enabled\n"), str);
+				else if (desc->verbose && !enable)
+					fprintf(stdout, _("%s already disabled\n"), str);
+				todo--;
+				continue;
+			}
+		} else {
+			/*
+			 * If /sys/devices/system/memory/memoryX is
+			 * unavailable, memory block is offline and
+			 * deconfigured.
+			 */
+			if (!enable) {
+				if (desc->verbose)
+					fprintf(stdout, _("%s already disabled\n"), str);
+				todo--;
+				continue;
+			}
 		}
 
 		if (desc->have_zones) {
@@ -243,6 +474,12 @@ static int chmem_range(struct chmem_desc *desc, int enable, int zone_id)
 			else
 				fprintf(stdout, _("%s disabled\n"), str);
 		}
+		if (!rc && !enable && desc->have_memconfig) {
+			/* Deconfigure memory block */
+			rc = chmem_config(desc, name, enable);
+			if (rc)
+				continue;
+		}
 		if (rc == 0)
 			todo--;
 	}
@@ -256,6 +493,16 @@ static int filter(const struct dirent *de)
 	return isdigit_string(de->d_name + 6);
 }
 
+static void read_conf(struct chmem_desc *desc)
+{
+	if (!desc->have_memconfig)
+		return;
+	desc->memconfig_ndirs = scandir(_PATH_SYS_MEMCONFIG, &desc->memconfig_dirs,
+					filter, versionsort);
+	if (desc->memconfig_ndirs <= 0)
+		err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMCONFIG);
+}
+
 static void read_info(struct chmem_desc *desc)
 {
 	char line[128];
@@ -269,6 +516,7 @@ static void read_info(struct chmem_desc *desc)
 	desc->block_size = strtoumax(line, NULL, 16);
 	if (errno)
 		goto fail;
+	read_conf(desc);
 	return;
 fail:
 	err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMORY);
@@ -347,6 +595,9 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" -d, --disable      disable memory\n"), out);
 	fputs(_(" -b, --blocks       use memory blocks\n"), out);
 	fputs(_(" -z, --zone <name>  select memory zone (see below)\n"), out);
+	fputs(_(" -c, --configure    configure range\n"), out);
+	fputs(_(" -g, --deconfigure  deconfigure range\n"), out);
+	fputs(_(" -m, --memmap-on-memory select memmap-on-memory\n"), out);
 	fputs(_(" -v, --verbose      verbose output\n"), out);
 	fprintf(out, USAGE_HELP_OPTIONS(20));
 
@@ -359,6 +610,52 @@ static void __attribute__((__noreturn__)) usage(void)
 	exit(EXIT_SUCCESS);
 }
 
+static int chmem_range(struct chmem_desc *desc, int cmd, int zone_id)
+{
+	int rc = -1;
+
+	switch (cmd) {
+	case CMD_MEMORY_ENABLE:
+		rc = chmem_onoff_range(desc, 1, zone_id);
+		break;
+	case CMD_MEMORY_DISABLE:
+		rc = chmem_onoff_range(desc, 0, zone_id);
+		break;
+	case CMD_MEMORY_CONFIGURE:
+		rc = chmem_config_range(desc, 1);
+		break;
+	case CMD_MEMORY_DECONFIGURE:
+		rc = chmem_config_range(desc, 0);
+		break;
+	default:
+		break;
+	}
+	return rc;
+}
+
+static int chmem_size(struct chmem_desc *desc, int cmd, int zone_id)
+{
+	int rc = -1;
+
+	switch (cmd) {
+	case CMD_MEMORY_ENABLE:
+		rc = chmem_onoff_size(desc, 1, zone_id);
+		break;
+	case CMD_MEMORY_DISABLE:
+		rc = chmem_onoff_size(desc, 0, zone_id);
+		break;
+	case CMD_MEMORY_CONFIGURE:
+		rc = chmem_config_size(desc, 1);
+		break;
+	case CMD_MEMORY_DECONFIGURE:
+		rc = chmem_config_size(desc, 0);
+		break;
+	default:
+		break;
+	}
+	return rc;
+}
+
 int main(int argc, char **argv)
 {
 	struct chmem_desc _desc = { 0 }, *desc = &_desc;
@@ -374,11 +671,15 @@ int main(int argc, char **argv)
 		{"verbose",	no_argument,		NULL, 'v'},
 		{"version",	no_argument,		NULL, 'V'},
 		{"zone",	required_argument,	NULL, 'z'},
+		{"configure",	no_argument,		NULL, 'c'},
+		{"deconfigure", no_argument,		NULL, 'g'},
+		{"memmap-on-memory", required_argument,	NULL, 'm'},
 		{NULL,		0,			NULL, 0}
 	};
 
 	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
-		{ 'd','e' },
+		{ 'd', 'e', 'g', 'm' },
+		{ 'c', 'd', 'e', 'g' },
 		{ 0 }
 	};
 	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
@@ -389,13 +690,18 @@ int main(int argc, char **argv)
 	close_stdout_atexit();
 
 	ul_path_init_debug();
+	desc->memmap_on_memory = -1;
 	desc->sysmem = ul_new_path(_PATH_SYS_MEMORY);
 	if (!desc->sysmem)
 		err(EXIT_FAILURE, _("failed to initialize %s handler"), _PATH_SYS_MEMORY);
-
+	desc->sysmemconfig = ul_new_path(_PATH_SYS_MEMCONFIG);
+	if (!desc->sysmemconfig)
+		err(EXIT_FAILURE, _("failed to initialize %s handler"), _PATH_SYS_MEMCONFIG);
+	if (ul_path_access(desc->sysmemconfig, F_OK, "memory0") == 0)
+		desc->have_memconfig = 1;
 	read_info(desc);
 
-	while ((c = getopt_long(argc, argv, "bdehvVz:", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "bcdeghm:vVz:", longopts, NULL)) != -1) {
 
 		err_exclusive_options(c, longopts, excl, excl_st);
 
@@ -415,7 +721,15 @@ int main(int argc, char **argv)
 		case 'z':
 			zone = xstrdup(optarg);
 			break;
-
+		case 'c':
+			cmd = CMD_MEMORY_CONFIGURE;
+			break;
+		case 'g':
+			cmd = CMD_MEMORY_DECONFIGURE;
+			break;
+		case 'm':
+			desc->memmap_on_memory = atoi(optarg);
+			break;
 		case 'h':
 			usage();
 		case 'V':
@@ -448,9 +762,9 @@ int main(int argc, char **argv)
 	}
 
 	if (desc->is_size)
-		rc = chmem_size(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id);
+		rc = chmem_size(desc, cmd, zone_id);
 	else
-		rc = chmem_range(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id);
+		rc = chmem_range(desc, cmd, zone_id);
 
 	ul_unref_path(desc->sysmem);
 
-- 
2.48.1


^ permalink raw reply related

* [PATCH 4/6] chmem: add chmem documentation for dynamic (de)configuration of memory
From: Sumanth Korikkar @ 2025-10-16 10:16 UTC (permalink / raw)
  To: Karel Zak, util-linux
  Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Sumanth Korikkar
In-Reply-To: <20251016101701.552597-1-sumanthk@linux.ibm.com>

Describe chmem configure, deconfigure and memmap-on-memory options:

ff18dcb19aab ("s390/sclp: Add support for dynamic (de)configuration of
memory") s390 kernel no longer pre-adds all standby memory at boot.
Instead, users must explicitly configure a block before it can be used
for online/offline actions. At configuration time, users can dynamically
decide whether to use optional memmap-on-memory for each memory block,
where value of 1 allocates metadata (such as struct pages array) from
the hotplug memory itself, enabling hot-add operations even under memory
pressure. A value of 0 stores metadata in regular system memory, which
may require additional free memory, but enables continuous physical
memory across memory blocks.

Add documentation to reflect the following options:
* chmem --configure 128M --memmap-on-memory 1
* chmem --deconfigure 128M
* chmem --enable 128M # implicitly configure memory if supported by
  architecture and online it
* chmem --disable 128M  # offline memory and implicitly deconfigure if
  supported by the architecture.

Just like online and offline actions, memory configuration and
deconfiguration can be controlled through similar options. Also,
memmap-on-memory setting can be changed, only when the memory block is
in deconfigured state. This means, it is usable only via --configure
option.

Reviewed-by: Maria Eisenhaendler <maria1@de.ibm.com>
Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
 sys-utils/chmem.8.adoc | 47 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/sys-utils/chmem.8.adoc b/sys-utils/chmem.8.adoc
index 5067a98b8..537beec63 100644
--- a/sys-utils/chmem.8.adoc
+++ b/sys-utils/chmem.8.adoc
@@ -12,7 +12,7 @@ chmem - configure memory
 
 == SYNOPSIS
 
-*chmem* [*-h*] [*-V*] [*-v*] [*-e*|*-d*] [_SIZE_|_RANGE_|*-b* _BLOCKRANGE_] [*-z* _ZONE_]
+*chmem* [*-h*] [*-V*] [*-v*] [*-c*|*-e*|*-d*|*-g*] [_SIZE_|_RANGE_|*-b* _BLOCKRANGE_] [*-z* _ZONE_] [*-m* _MEMMAP-ON-MEMORY_]
 
 == DESCRIPTION
 
@@ -26,23 +26,48 @@ The *chmem* command sets a particular size or range of memory online or offline.
 
 * Specify _ZONE_ as the name of a memory zone, as shown in the output of the *lsmem -o +ZONES* command. The output shows one or more valid memory zones for each memory range. If multiple zones are shown, then the memory range currently belongs to the first zone. By default, *chmem* will set memory online to the zone Movable, if this is among the valid zones. This default can be changed by specifying the *--zone* option with another valid zone. For memory ballooning, it is recommended to select the zone Movable for memory online and offline, if possible. Memory in this zone is much more likely to be able to be offlined again, but it cannot be used for arbitrary kernel allocations, only for migratable pages (e.g., anonymous and page cache pages). Use the *--help* option to see all available zones.
 
+* Specify _MEMMAP-ON-MEMORY_ as 1 or 0. A value of 1 allocates hotplug metadata (such as the struct pages array) from the hotplug memory itself, enabling hot-add operations even under memory pressure and without requiring additional system memory to do so. A value of 0 stores hotplugged memory metadata in regular system memory, which helps avoid issues related to fragmentation of continuous physical memory across memory blocks. The value can only be set when the memory block is in a deconfigured state, and *--memmap-on-memory* is valid only with *--configure*. If not specified, and if supported, *chmem* uses the default value shown in *lsmem* output.
+
+The *--enable* option configures the memory, if this is supported by the architecture. If configuring memory is not supported by the architecture, *--enable* still brings the memory online.
+
+The *--disable* option brings the memory offline and performs an optional deconfigure step if this is supported by the architecture.
+
+The *--configure* option requests memory from the hypervisor without bringing it online, when supported by the architecture, allowing explicit control and use of *--memmap-on-memory*.
+
+The *--deconfigure* option returns memory resources to the hypervisor if supported by the architecture.
+
 _SIZE_ and _RANGE_ must be aligned to the Linux memory block size, as shown in the output of the *lsmem*(1) command.
 
 Setting memory online can fail for various reasons. On virtualized systems it can fail if the hypervisor does not have enough memory left, for example because memory was overcommitted. Setting memory offline can fail if Linux cannot free the memory. If only part of the requested memory can be set online or offline, a message tells you how much memory was set online or offline instead of the requested amount.
 
-When setting memory online *chmem* starts with the lowest memory block numbers. When setting memory offline *chmem* starts with the highest memory block numbers.
+When setting memory online or when configuring memory, *chmem* starts with the lowest memory block numbers. When setting memory offline or deconfiguring memory, *chmem* starts with the highest memory block numbers.
+
+== ARCHITECTURE
+
+* s390 architecture:
+
+_MEMMAP-ON-MEMORY_: For memory blocks configured online at boot, the default value is 0 because they are added without memmap-on-memory support. Memory added dynamically at runtime uses the default value displayed in *lsmem* output.
 
 == OPTIONS
 
 *-b*, *--blocks*::
 Use a _BLOCKRANGE_ parameter instead of _RANGE_ or _SIZE_ for the *--enable* and *--disable* options.
 
+*-c*, *--configure*::
+Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory to be configured.
+
 *-d*, *--disable*::
 Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory offline.
 
 *-e*, *--enable*::
 Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory online.
 
+*-g*, *--deconfigure*::
+Set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory to be deconfigured.
+
+*-m*, *--memmap-on-memory*::
+Select memmap-on-memory for the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory. This option is valid only with *--configure*.
+
 *-z*, *--zone*::
 Select the memory _ZONE_ where to set the specified _RANGE_, _SIZE_, or _BLOCKRANGE_ of memory online or offline. By default, memory will be set online to the zone Movable, if possible.
 
@@ -70,13 +95,25 @@ partial success
 This command requests 1024 MiB of memory to be set online.
 
 *chmem -e 2g*::
-This command requests 2 GiB of memory to be set online.
+This command requests 2 GB of memory to be brought online and, if supported by the architecture, configures the memory beforehand.
 
 *chmem --disable 0x00000000e4000000-0x00000000f3ffffff*::
-This command requests the memory range starting with 0x00000000e4000000 and ending with 0x00000000f3ffffff to be set offline.
+This command takes the memory range from 0x00000000e4000000 to 0x00000000f3ffffff offline and deconfigures it if supported by the architecture.
 
 *chmem -b -d 10*::
-This command requests the memory block number 10 to be set offline.
+This command takes memory block number 10 offline.
+
+*chmem -b -c 10 -m 1*::
+This command configures memory block 10 with  _MEMMAP-ON-MEMORY_ set. The block must be in a deconfigured state.
+
+*chmem -b -c 10*::
+This command configures memory block 10 with the default _MEMMAP-ON-MEMORY_ setting. The default value is displayed in *lsmem --output-all*. The block must be in a deconfigured state.
+
+*chmem -b -g 10*::
+This command deconfigures memory block 10. The block must be offline.
+
+*chmem -d 5g*::
+This command takes 5 GB of memory offline and deconfigures it if supported by the architecture. Blocks that are already offline but still configured are skipped and must be explicitly deconfigured with *--deconfigure*.
 
 == SEE ALSO
 
-- 
2.48.1


^ permalink raw reply related

* [PATCH 2/6] lsmem: add support to display dynamic (de)configuration of memory
From: Sumanth Korikkar @ 2025-10-16 10:16 UTC (permalink / raw)
  To: Karel Zak, util-linux
  Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Sumanth Korikkar
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


^ permalink raw reply related

* [PATCH 0/6] chmem/lsmem: dynamic (de)configuration of memory
From: Sumanth Korikkar @ 2025-10-16 10:16 UTC (permalink / raw)
  To: Karel Zak, util-linux
  Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Sumanth Korikkar

Hi all,

Patchset extends chmem and lsmem with support for dynamically configuring and
deconfiguring hotpluggable memory blocks on s390, including per-block
memmap-on-memory handling.

On s390, the memmap-on-memory feature was
introduced to ensure that the struct page array (metadata) for hotpluggable
standby memory can be allocated from the memory block itself. This allowed
hot-add operations even under memory pressure, especially in systems with an
imbalance between boot-time online memory and standby memory.

The original implementation had few limitations:
* All hotpluggable standby memory was added at boot, making blocks
  visible for online/offline operations earlier.
* The use of memmap-on-memory was global and static, decided at boot
  time. Either all standby blocks used it, or none of them did.
* memmap-on-memory choice could not be changed at runtime, limiting
  flexibility.

The s390 kernel (linux-next) ff18dcb19aab ("s390/sclp: Add support for
dynamic (de)configuration of memory") no longer pre-adds all standby
memory at boot. Instead, users must explicitly configure a block before
it can be used for online/offline actions.  At configuration time, users
can dynamically decide whether to use optional memmap-on-memory for each
memory block, where value of 1 allocates metadata (such as struct pages
array) from the hotplug memory itself, enabling hot-add operations even
under memory pressure. A value of 0 stores metadata in regular system
memory and enables continuous physical memory across memory blocks.

Kernel changes for dynamic (de)configuration of memory (available on
linux-next):
https://lore.kernel.org/all/20251010085147.2175918-1-sumanthk@linux.ibm.com/
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/log/drivers/s390/char/sclp_mem.c

The kernel sysfs interface provides configuration and memmap-on-memory toggling:
echo 1 > /sys/firmware/memory/memoryX/config – configure block
echo 0 > /sys/firmware/memory/memoryX/config – deconfigure block
echo 1 > /sys/firmware/memory/memoryX/memmap_on_memory – enable memmap-on-memory
echo 0 > /sys/firmware/memory/memoryX/memmap_on_memory – disable memmap-on-memory

Patchset teaches chmem and lsmem to make use of these interfaces, mirroring
existing online/offline semantics:

chmem -c 128M -m 1 : configure memory with memmap-on-memory
chmem -g 128M : deconfigure memory
chmem -e 128M : configure (if supported by architecture) and online memory
chmem -d 128M : offline and deconfigure memory (if supported by
architecture)
lsmem -o RANGE,SIZE,STATE,BLOCK,CONFIGURED,MEMMAP-ON-MEMORY

memmap-on-memory can only be toggled when a block is in a deconfigured state,
and is supported via the --configure option.

Thank you

Sumanth Korikkar (6):
  lsmem: display global memmap on memory parameter
  lsmem: add support to display dynamic (de)configuration of memory
  chmem: add support for dynamic (de)configuration of hotplug memory
  chmem: add chmem documentation for dynamic (de)configuration of memory
  lsmem: add doc for dynamic (de)configuration and memmap-on-memory
    support
  lsmem,chmem: add configure/deconfigure bash completion options

 bash-completion/chmem  |   3 +
 bash-completion/lsmem  |   2 +-
 sys-utils/chmem.8.adoc |  47 +++++-
 sys-utils/chmem.c      | 372 +++++++++++++++++++++++++++++++++++++----
 sys-utils/lsmem.1.adoc |  46 ++++-
 sys-utils/lsmem.c      | 144 ++++++++++++++--
 6 files changed, 564 insertions(+), 50 deletions(-)

-- 
2.48.1


^ permalink raw reply

* [PATCH 1/6] lsmem: display global memmap on memory parameter
From: Sumanth Korikkar @ 2025-10-16 10:16 UTC (permalink / raw)
  To: Karel Zak, util-linux
  Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Sumanth Korikkar
In-Reply-To: <20251016101701.552597-1-sumanthk@linux.ibm.com>

Display the output of global memmap-on-memory parameter for memory
hotplug. Retrieve the details via
/sys/module/memory_hotplug/parameters/memmap_on_memory.

lsmem
RANGE                                 SIZE  STATE REMOVABLE BLOCK
0x0000000000000000-0x00000001ffffffff   8G online       yes  0-63

Memory block size:                128M
Total online memory:                8G
Total offline memory:               0B
Memmap on memory parameter:        yes

Signed-off-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
 sys-utils/lsmem.1.adoc |  2 ++
 sys-utils/lsmem.c      | 29 +++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/sys-utils/lsmem.1.adoc b/sys-utils/lsmem.1.adoc
index d588051a8..9c9397631 100644
--- a/sys-utils/lsmem.1.adoc
+++ b/sys-utils/lsmem.1.adoc
@@ -28,6 +28,8 @@ Not all columns are supported on all systems. If an unsupported column is specif
 
 Use the *--help* option to see the columns description.
 
+Memmap on memory parameter output displays the globally enabled memmap-on-memory setting for memory_hotplug. This is typically set on the kernel command line via memory_hotplug.memmap_on_memory.
+
 == OPTIONS
 
 *-a*, *--all*::
diff --git a/sys-utils/lsmem.c b/sys-utils/lsmem.c
index 39967bfc9..ea818d6dc 100644
--- a/sys-utils/lsmem.c
+++ b/sys-utils/lsmem.c
@@ -306,8 +306,25 @@ static void fill_scols_table(struct lsmem *lsmem)
 		add_scols_line(lsmem, &lsmem->blocks[i]);
 }
 
+static int get_memmap_mode(char *res, char *src, int len)
+{
+	if (!strncmp(src, "Y", 1))
+		strncpy(res, "yes", len);
+	else if (!strncmp(src, "N", 1))
+		strncpy(res, "no", len);
+	else if (!strncmp(src, "force", 5))
+		strncpy(res, "force", len);
+	else
+		return -1;
+	return 0;
+}
+
 static void print_summary(struct lsmem *lsmem)
 {
+	const char *path = "/sys/module/memory_hotplug/parameters/memmap_on_memory";
+	char buf[8], res[8];
+	FILE *memmap;
+
 	if (lsmem->bytes) {
 		printf("%-32s %15"PRId64"\n",_("Memory block size:"), lsmem->block_size);
 		printf("%-32s %15"PRId64"\n",_("Total online memory:"), lsmem->mem_online);
@@ -327,6 +344,18 @@ static void print_summary(struct lsmem *lsmem)
 			printf("%-32s %5s\n",_("Total offline memory:"), p);
 		free(p);
 	}
+	memmap = fopen(path, "r");
+	if (!memmap)
+		return;
+	if (fgets(buf, sizeof(buf), memmap)) {
+		if (!get_memmap_mode(res, buf, sizeof(res))) {
+			if (lsmem->bytes)
+				printf("%-32s %15s\n", _("Memmap on memory parameter:"), res);
+			else
+				printf("%-32s %5s\n", _("Memmap on memory parameter:"), res);
+		}
+	}
+	fclose(memmap);
 }
 
 static int memory_block_get_node(struct lsmem *lsmem, char *name)
-- 
2.48.1


^ permalink raw reply related

* Re: `losetup --remove` is confusing, and misuse silently fails
From: Karel Zak @ 2025-10-14 10:06 UTC (permalink / raw)
  To: Benno Schulenberg; +Cc: Util-Linux, wguanghao
In-Reply-To: <a5909aac-3f54-4fce-8785-410535dd4098@telfort.nl>

On Fri, Oct 10, 2025 at 11:41:08AM +0200, Benno Schulenberg wrote:
> 
> After setting up a loop device with:
> 
>   # ./losetup loop4 some.img
> 
> I wanted to see what happens when trying to "remove" it
> (since the man page says it is "not recommended"):
> 
>   # ./losetup -R loop4
> 
> There is no feedback, but it failed:
> 
>   # echo $?
>   1
> 
> When a command fails, shouldn't it print an error message?

Yes, we need to fix it.

> Then I tried detaching and removing at the same time:
> 
>   # ./losetup -R -d loop4
> 
> No feedback, but it didn't do anything:

The options need to be mutually exclusive.

>   # ./losetup loop4
>   /dev/loop4: [66306]:5939273 (/home/ben/Sources/util-linux/some.img)
> 
> Trying a different order of the options:
> 
>   # ./losetup -d -R loop4
>   losetup: /dev/-R: detach failed: No such file or directory
> 
> Huh?  Why does it try to interpret an option as a loop device name?

-d and -R requires argument.

>   # ./losetup -d loop4 -R
>   /home/ben/Sources/util-linux/.libs/losetup: option requires an argument -- 'R'
>   Try 'losetup --help' for more information.
> 
>   # ./losetup -d loop4 -R loop4
> 
> Oh.  Surprisingly, this last invocation worked.
> 
>   # ls -l /dev/loop? | grep loop4
>   # lsblk -a | grep loop4
> 
> 
> However, the loop4 device is not really gone, because one can attach
> a file to it again:
> 
>   # ./losetup --show loop4 some.img
>   /dev/loop4

This is expected. losetup asks kernel to create the node again.

>   # ./losetup -a
>   /dev/loop4: [66306]:5939273 (/home/ben/Sources/util-linux/some.img)
> 
> When the device is really gone, attaching doesn't work:
> 
>   # rm /dev/loop3
>   # ./losetup loop3 some.img
>   losetup: /dev/loop3: failed to set up loop device: No such file or directory
>   losetup: device node /dev/loop3 (7:3) is lost. You may use mknod(1) to
> recover it.

This is because according to kernel there is the node, but you have
manually removed the /dev/loop3 file. This is mostly udevd playground,
losetup is able to detect "lost node" situation, it's enough I think.
We can improve is losetup man page to explain it.

> In my opinion --remove is not a good name for the option, as it doesn't
> actually remove anything.

It asks kernel to remove the node (and it's processed by udevd I
guess). The ioctl name is LOOP_CTL_REMOVE.

> Also, its meaning is too similar to "detach":
> when the computer asks me to "Remove the USB stick", I detach this stick
> from the USB port.
> 
> What --remove accomplishes is that it prevents the specified loop device
> from getting listed.  So in my opinion a better name for the option would
> be: --unlist.

I think --remove is fine, but it needs to be implemented in more
verbose way and with more respect to the end users.

> But what is the purpose of --unlist?  Why would one want it? 

Well, we provide command line interface to kernel features :-)

> What harm is there in listing empty, fallow loop devices?

Maybe someone needs to optimize devices scanning, size of /sys
etc. The number of loopdevs can be large on some servers.

    Karel


-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [DKIM] Re: `losetup --remove` is confusing, and misuse silently fails
From: Benno Schulenberg @ 2025-10-11 12:47 UTC (permalink / raw)
  To: Nuno Silva, util-linux
In-Reply-To: <10cdaqe$vrk$1@ciao.gmane.io>


[-- Attachment #1.1: Type: text/plain, Size: 925 bytes --]


Op 11-10-2025 om 12:16 schreef Nuno Silva:
> For all commands after this one in your message, where there was no
> output, what was the exit status?

The answer is below.

>>    # ./losetup -d -R loop4
>>    losetup: /dev/-R: detach failed: No such file or directory
>>
>> Huh?  Why does it try to interpret an option as a loop device name?
> 
> The online manual here says that -d takes one argument, so my
> understanding is that that is expected?

Agreed.  (It's just that I expected the tool to be cleverer.)

>>    # ./losetup -d loop4 -R loop4

   # echo $?
   1

So it didn't actually work.  It only works (and has exit status 0)
when having a done a plain `./losetup -d loop4` beforehand.

Seeing that four options (-d, -D, -c, -R) are more like commands,
maybe those should be made mutually exclusive?  Meaning that
losetup` should barf when more than one of them is given?


Benno


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply

* Re: `losetup --remove` is confusing, and misuse silently fails
From: Nuno Silva @ 2025-10-11 10:16 UTC (permalink / raw)
  To: util-linux
In-Reply-To: <a5909aac-3f54-4fce-8785-410535dd4098@telfort.nl>

On 2025-10-10, Benno Schulenberg wrote:

> After setting up a loop device with:
>
>   # ./losetup loop4 some.img
>
> I wanted to see what happens when trying to "remove" it
> (since the man page says it is "not recommended"):
>
>   # ./losetup -R loop4
>
> There is no feedback, but it failed:
>
>   # echo $?
>   1
>
> When a command fails, shouldn't it print an error message?

For all commands after this one in your message, where there was no
output, what was the exit status?

[...]
> Trying a different order of the options:
>
>   # ./losetup -d -R loop4
>   losetup: /dev/-R: detach failed: No such file or directory
>
> Huh?  Why does it try to interpret an option as a loop device name?

The online manual here says that -d takes one argument, so my
understanding is that that is expected? (Just as for -R, apparently:)

>   # ./losetup -d loop4 -R
>   /home/ben/Sources/util-linux/.libs/losetup: option requires an argument -- 'R'
>   Try 'losetup --help' for more information.
>
>   # ./losetup -d loop4 -R loop4
>
> Oh.  Surprisingly, this last invocation worked.

[...]

-- 
Nuno Silva


^ permalink raw reply

* `losetup --remove` is confusing, and misuse silently fails
From: Benno Schulenberg @ 2025-10-10  9:41 UTC (permalink / raw)
  To: Util-Linux; +Cc: wguanghao


[-- Attachment #1.1: Type: text/plain, Size: 2520 bytes --]


After setting up a loop device with:

   # ./losetup loop4 some.img

I wanted to see what happens when trying to "remove" it
(since the man page says it is "not recommended"):

   # ./losetup -R loop4

There is no feedback, but it failed:

   # echo $?
   1

When a command fails, shouldn't it print an error message?


Then I tried detaching and removing at the same time:

   # ./losetup -R -d loop4

No feedback, but it didn't do anything:

   # ./losetup loop4
   /dev/loop4: [66306]:5939273 (/home/ben/Sources/util-linux/some.img)

Trying a different order of the options:

   # ./losetup -d -R loop4
   losetup: /dev/-R: detach failed: No such file or directory

Huh?  Why does it try to interpret an option as a loop device name?

   # ./losetup -d loop4 -R
   /home/ben/Sources/util-linux/.libs/losetup: option requires an argument -- 'R'
   Try 'losetup --help' for more information.

   # ./losetup -d loop4 -R loop4

Oh.  Surprisingly, this last invocation worked.

   # ls -l /dev/loop? | grep loop4
   # lsblk -a | grep loop4


However, the loop4 device is not really gone, because one can attach
a file to it again:

   # ./losetup --show loop4 some.img
   /dev/loop4
   # ./losetup -a
   /dev/loop4: [66306]:5939273 (/home/ben/Sources/util-linux/some.img)

When the device is really gone, attaching doesn't work:

   # rm /dev/loop3
   # ./losetup loop3 some.img
   losetup: /dev/loop3: failed to set up loop device: No such file or directory
   losetup: device node /dev/loop3 (7:3) is lost. You may use mknod(1) to 
recover it.

In my opinion --remove is not a good name for the option, as it doesn't
actually remove anything.  Also, its meaning is too similar to "detach":
when the computer asks me to "Remove the USB stick", I detach this stick
from the USB port.

What --remove accomplishes is that it prevents the specified loop device
from getting listed.  So in my opinion a better name for the option would
be: --unlist.

But what is the purpose of --unlist?  Why would one want it?  What harm
is there in listing empty, fallow loop devices?

   # ./lsblk -a | grep loop
   loop0         7:0    0     0B  0 loop
   loop1         7:1    0     0B  0 loop
   loop2         7:2    0     0B  0 loop
   loop3         7:3    0     0B  0 loop
   loop4         7:4    0   400M  0 loop
   loop5         7:5    0     0B  0 loop
   loop6         7:6    0     0B  0 loop
   loop7         7:7    0     0B  0 loop


Benno


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply

* Re: poor feedback when misusing `losetup`
From: Benno Schulenberg @ 2025-10-10  8:34 UTC (permalink / raw)
  To: Util-Linux
In-Reply-To: <9ab7cfb1-511c-4bb5-b630-d8ea51836bfc@telfort.nl>


[-- Attachment #1.1: Type: text/plain, Size: 900 bytes --]


Op 09-10-2025 om 11:44 schreef Benno Schulenberg:
>    # losetup loop2
>    losetup: loop2: failed to use device: No such device
> 
> It results that when setting up a loop device, one can use the short
> name, but when doing anything else, one needs to give the full path

Sorry, I was using the system-installed `losetup` -- the `./losetup`
from git accepts the plain name just fine.


>    # losetup -d /dev/loop2
>    # losetup -d /dev/loop2
>    losetup: /dev/loop2: detach failed: No such device or address
> 
> "No such device"?  So I request information about the device:
> 
>    # losetup /dev/loop2
>    losetup: /dev/loop2: No such file or directory

These inaccurate messages are still the case.  Are they seem even
more inaccurate when the user provides just the plain name (loop2)
and the error message mentions the full name (/dev/loop2).


Benno


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply

* poor feedback when misusing `losetup`
From: Benno Schulenberg @ 2025-10-09  9:44 UTC (permalink / raw)
  To: Util-Linux


[-- Attachment #1.1: Type: text/plain, Size: 1325 bytes --]


I've attached an image of some file system to a loop device with:

   # losetup loop2 some.img

Then I wanted to list information about this loop device:

   # losetup loop2
   losetup: loop2: failed to use device: No such device

It results that when setting up a loop device, one can use the short
name, but when doing anything else, one needs to give the full path:

   # losetup /dev/loop2
   /dev/loop2: [66306]:5792049 (/home/ben/some.img)

Wouldn't it be nicer if one can always use the short name -- when
the relevant loop device is located in /dev/?  Because the man page
always says 'loopdev'; it doesn't distinguish between setting up a
device and doing anything else with it.


Then I detach the device, forget that I have detached it, and detach
it again later:

   # losetup -d /dev/loop2
   # losetup -d /dev/loop2
   losetup: /dev/loop2: detach failed: No such device or address

"No such device"?  So I request information about the device:

   # losetup /dev/loop2
   losetup: /dev/loop2: No such file or directory

But it exists alright:

   # ls -l /dev/loop2
   brw-rw---- 1 root disk 7, 2 okt  9 11:29 /dev/loop2

Wouldn't it be better if `losetup` reported "Nothing is attached"
when trying to detach or get info from an unassociated loop device?


Benno


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2] swapon: rename the new --annotation option to --annotate
From: Karel Zak @ 2025-10-06 12:33 UTC (permalink / raw)
  To: Benno Schulenberg
  Cc: Christian Albrecht Goeschel Ndjomouo, util-linux@vger.kernel.org
In-Reply-To: <fed873ef-7480-4f91-a5ea-a10fe5e61654@telfort.nl>

On Mon, Oct 06, 2025 at 11:57:50AM +0200, Benno Schulenberg wrote:
> 
> Op 06-10-2025 om 04:02 schreef Christian Albrecht Goeschel Ndjomouo:
> > FYI, I added your patches to my pull request* on Github and for completeness
> > I renamed man-common/annotation.adoc to annotate.adoc and adapted the name
> > of some variables for clarity.

Applied all the patches, thanks guys!

    Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH 2/2] swapon: rename the new --annotation option to --annotate
From: Benno Schulenberg @ 2025-10-06  9:57 UTC (permalink / raw)
  To: Christian Albrecht Goeschel Ndjomouo; +Cc: util-linux@vger.kernel.org
In-Reply-To: <SJ0P220MB05411D798786BF09146DF3A8E9E3A@SJ0P220MB0541.NAMP220.PROD.OUTLOOK.COM>


[-- Attachment #1.1: Type: text/plain, Size: 286 bytes --]


Op 06-10-2025 om 04:02 schreef Christian Albrecht Goeschel Ndjomouo:
> FYI, I added your patches to my pull request* on Github and for completeness
> I renamed man-common/annotation.adoc to annotate.adoc and adapted the name
> of some variables for clarity.

Thanks.


Benno

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2] swapon: rename the new --annotation option to --annotate
From: Christian Albrecht Goeschel Ndjomouo @ 2025-10-06  2:02 UTC (permalink / raw)
  To: bensberg@telfort.nl; +Cc: util-linux@vger.kernel.org

> The form --annotation gave the impression that after the subsequent
> "=" comes the text that each column name should be annotated with.
> Furthermore, the other two options with optional arguments (--show
> and --discard) are in the imperative, so follow that style and use
> --annotate instead.

I prefer this name, it is more intuitive.

FYI, I added your patches to my pull request* on Github and for completeness I renamed
man-common/annotation.adoc to annotate.adoc and adapted the name of some
variables for clarity.

* https://github.com/util-linux/util-linux/pull/3782/

Christian Goeschel Ndjomouo
B.Sc. Software Engineering
Western Governors University


^ permalink raw reply

* [PATCH 2/2] swapon: rename the new --annotation option to --annotate
From: Benno Schulenberg @ 2025-10-05 13:20 UTC (permalink / raw)
  To: util-linux; +Cc: Christian Goeschel Ndjomouo
In-Reply-To: <20251005132048.78447-1-bensberg@telfort.nl>

The form --annotation gave the impression that after the subsequent
"=" comes the text that each column name should be annotated with.
Furthermore, the other two options with optional arguments (--show
and --discard) are in the imperative, so follow that style and use
--annotate instead.

(In the bargain, this allows aligning the descriptions in the help
text perfectly again.)

Also, improve the description of --annotate in the man page somewhat.

CC: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Benno Schulenberg <bensberg@telfort.nl>
---
 bash-completion/swapon     | 2 +-
 lib/strutils.c             | 2 +-
 man-common/annotation.adoc | 8 ++++----
 sys-utils/swapon.c         | 4 ++--
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/bash-completion/swapon b/bash-completion/swapon
index d94d2234d..5906ec844 100644
--- a/bash-completion/swapon
+++ b/bash-completion/swapon
@@ -56,7 +56,7 @@ _swapon_module()
 	case $cur in
 		-*)
 			OPTS="--all
-				--annotation
+				--annotate
 				--discard
 				--ifexists
 				--fixpgsz
diff --git a/lib/strutils.c b/lib/strutils.c
index e4dabf212..a3b1376e3 100644
--- a/lib/strutils.c
+++ b/lib/strutils.c
@@ -562,7 +562,7 @@ bool annotationwanted(const char *mode)
 	if (!mode || strcmp(mode, "auto") == 0)
 		return isatty(STDOUT_FILENO) ? true : false;
 
-	errx(EXIT_FAILURE, _("invalid argument of --annotation: %s"), mode);
+	errx(EXIT_FAILURE, _("invalid argument of --annotate: %s"), mode);
 }
 
 /*
diff --git a/man-common/annotation.adoc b/man-common/annotation.adoc
index 8f9d4c879..cbace162e 100644
--- a/man-common/annotation.adoc
+++ b/man-common/annotation.adoc
@@ -1,6 +1,6 @@
-*--annotation*[**=**_when_]::
-Adds an annotation to column header names which can be printed as a
-tooltip by supporting terminals. The optional _when_ argument can be
-*always*, *never*, or *auto*. If the argument is omitted, it defaults to
+*--annotate*[**=**_when_]::
+Adds an annotation to each column header name. Such an annotation can be shown
+as a tooltip by terminals that support this feature. The optional _when_ argument
+can be *always*, *never*, or *auto*. If the argument is omitted, it defaults to
 *auto*, which means that annotations will only be used when the output
 goes to a terminal.
diff --git a/sys-utils/swapon.c b/sys-utils/swapon.c
index 896728880..fc5351e21 100644
--- a/sys-utils/swapon.c
+++ b/sys-utils/swapon.c
@@ -840,7 +840,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" -T, --fstab <path>       alternative file to /etc/fstab\n"), out);
 	fputs(_("     --show[=<columns>]   display summary in definable table\n"), out);
 	fputs(_("     --output-all         output all available columns\n"), out);
-	fputs(_("     --annotation[=<when>]  annotate columns with a tooltip (always|never|auto)\n"), out);
+	fputs(_("     --annotate[=<when>]  annotate columns with a tooltip (always|never|auto)\n"), out);
 	fputs(_("     --noheadings         don't print table heading (with --show)\n"), out);
 	fputs(_("     --raw                use the raw output format (with --show)\n"), out);
 	fputs(_("     --bytes              display swap size in bytes in --show output\n"), out);
@@ -900,7 +900,7 @@ int main(int argc, char *argv[])
 		{ "version",      no_argument,       NULL, 'V'                 },
 		{ "show",         optional_argument, NULL, SHOW_OPTION         },
 		{ "output-all",   no_argument,       NULL, OPT_LIST_TYPES      },
-		{ "annotation",   optional_argument, NULL, ANNOTATION_OPTION   },
+		{ "annotate",     optional_argument, NULL, ANNOTATION_OPTION   },
 		{ "noheadings",   no_argument,       NULL, NOHEADINGS_OPTION   },
 		{ "raw",          no_argument,       NULL, RAW_OPTION          },
 		{ "bytes",        no_argument,       NULL, BYTES_OPTION        },
-- 
2.51.0


^ permalink raw reply related

* [PATCH 1/2] swapon: (usage) make the help text fit within 80 columns again
From: Benno Schulenberg @ 2025-10-05 13:20 UTC (permalink / raw)
  To: util-linux; +Cc: Christian Goeschel Ndjomouo

Recent commit 72829b08fe added four extra spaces to each description
in the usage text, causing some lines to be wider than 80 characters.
Undo that addition, to make the help text fit again in 80 columns.

Also, put the equals sign for the new --annotation option inside the
square brackets instead of mistakenly before them.

CC: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Benno Schulenberg <bensberg@telfort.nl>
---
 sys-utils/swapon.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/sys-utils/swapon.c b/sys-utils/swapon.c
index 1cf8fda2c..896728880 100644
--- a/sys-utils/swapon.c
+++ b/sys-utils/swapon.c
@@ -830,21 +830,21 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_("Enable devices and files for paging and swapping.\n"), out);
 
 	fputs(USAGE_OPTIONS, out);
-	fputs(_(" -a, --all                	enable all swaps from /etc/fstab\n"), out);
-	fputs(_(" -d, --discard[=<policy>] 	enable swap discards, if supported by device\n"), out);
-	fputs(_(" -e, --ifexists           	silently skip devices that do not exist\n"), out);
-	fputs(_(" -f, --fixpgsz            	reinitialize the swap space if necessary\n"), out);
-	fputs(_(" -o, --options <list>     	comma-separated list of swap options\n"), out);
-	fputs(_(" -p, --priority <prio>    	specify the priority of the swap device\n"), out);
-	fputs(_(" -s, --summary            	display summary about used swap devices (DEPRECATED)\n"), out);
-	fputs(_(" -T, --fstab <path>       	alternative file to /etc/fstab\n"), out);
-	fputs(_("     --show[=<columns>]   	display summary in definable table\n"), out);
-	fputs(_("     --output-all         	output all available columns\n"), out);
-	fputs(_("     --annotation=[<when>]     annotate columns with a tooltip (always|never|auto)\n"), out);
-	fputs(_("     --noheadings         	don't print table heading (with --show)\n"), out);
-	fputs(_("     --raw                	use the raw output format (with --show)\n"), out);
-	fputs(_("     --bytes              	display swap size in bytes in --show output\n"), out);
-	fputs(_(" -v, --verbose            	verbose mode\n"), out);
+	fputs(_(" -a, --all                enable all swaps from /etc/fstab\n"), out);
+	fputs(_(" -d, --discard[=<policy>] enable swap discards, if supported by device\n"), out);
+	fputs(_(" -e, --ifexists           silently skip devices that do not exist\n"), out);
+	fputs(_(" -f, --fixpgsz            reinitialize the swap space if necessary\n"), out);
+	fputs(_(" -o, --options <list>     comma-separated list of swap options\n"), out);
+	fputs(_(" -p, --priority <prio>    specify the priority of the swap device\n"), out);
+	fputs(_(" -s, --summary            display summary about used swap devices (DEPRECATED)\n"), out);
+	fputs(_(" -T, --fstab <path>       alternative file to /etc/fstab\n"), out);
+	fputs(_("     --show[=<columns>]   display summary in definable table\n"), out);
+	fputs(_("     --output-all         output all available columns\n"), out);
+	fputs(_("     --annotation[=<when>]  annotate columns with a tooltip (always|never|auto)\n"), out);
+	fputs(_("     --noheadings         don't print table heading (with --show)\n"), out);
+	fputs(_("     --raw                use the raw output format (with --show)\n"), out);
+	fputs(_("     --bytes              display swap size in bytes in --show output\n"), out);
+	fputs(_(" -v, --verbose            verbose mode\n"), out);
 
 	fputs(USAGE_SEPARATOR, out);
 	fprintf(out, USAGE_HELP_OPTIONS(26));
-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH] fallocate: allow O_CREATE if mode is FALLOC_FL_WRITE_ZEROES
From: Karel Zak @ 2025-10-01 13:06 UTC (permalink / raw)
  To: Lukas Herbolt; +Cc: util-linux
In-Reply-To: <20251001115448.1646647-3-lukas@herbolt.com>

On Wed, Oct 01, 2025 at 01:54:50PM +0200, Lukas Herbolt wrote:
> With the new flag we can create the ZEROED allcoated files directly and not 
> in the two steps. Removing FALLOC_FL_WRITE_ZEROES from the flags of not using
> O_CREATE on open().
> 
> Signed-off-by: Lukas Herbolt <lukas@herbolt.com>
> ---
>  sys-utils/fallocate.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Applied, thanks.

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* [PATCH] fallocate: allow O_CREATE if mode is FALLOC_FL_WRITE_ZEROES
From: Lukas Herbolt @ 2025-10-01 11:54 UTC (permalink / raw)
  To: kzak; +Cc: util-linux, Lukas Herbolt

With the new flag we can create the ZEROED allcoated files directly and not 
in the two steps. Removing FALLOC_FL_WRITE_ZEROES from the flags of not using
O_CREATE on open().

Signed-off-by: Lukas Herbolt <lukas@herbolt.com>
---
 sys-utils/fallocate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c
index f06009483..686e4925e 100644
--- a/sys-utils/fallocate.c
+++ b/sys-utils/fallocate.c
@@ -408,7 +408,7 @@ int main(int argc, char **argv)
 
 	/* O_CREAT makes sense only for the default fallocate(2) behavior
 	 * when mode is no specified and new space is allocated */
-	fd = open(filename, O_RDWR | (!dig && !mode ? O_CREAT : 0),
+	fd = open(filename, O_RDWR | (!dig && !(mode & ~FALLOC_FL_WRITE_ZEROES) ? O_CREAT : 0),
 		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
 	if (fd < 0)
 		err(EXIT_FAILURE, _("cannot open %s"), filename);
-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH 1/6] getopt: (usage) make the description of -U fit within 80 columns
From: Karel Zak @ 2025-09-24  8:09 UTC (permalink / raw)
  To: Benno Schulenberg; +Cc: util-linux
In-Reply-To: <20250918085707.41785-1-bensberg@telfort.nl>

On Thu, Sep 18, 2025 at 10:57:02AM +0200, Benno Schulenberg wrote:
>  misc-utils/getopt.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Applied (entire set). Thanks.
    
    Karel
    
-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* Re: [PATCH] blkid: correct an erroneous error message
From: Karel Zak @ 2025-09-24  8:06 UTC (permalink / raw)
  To: Benno Schulenberg; +Cc: util-linux
In-Reply-To: <20250916134441.51685-1-bensberg@telfort.nl>

On Tue, Sep 16, 2025 at 03:44:41PM +0200, Benno Schulenberg wrote:
>  misc-utils/blkid.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Applied, thanks.

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply

* [ANNOUNCE] util-linux stable v2.41.2
From: Karel Zak @ 2025-09-22 11:48 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, util-linux


The util-linux stable maintenance release v2.41.2 is now available at
      
  http://www.kernel.org/pub/linux/utils/util-linux/v2.41/
      
Feedback and bug reports, as always, are welcomed.
 
  Karel


util-linux 2.41.2 Release Notes
===============================

bash-completion:
    - fix function name of enosys completion (by Koichi Murase)
    - add choom and coresched (by Karel Zak)

blkid:
    - correct an erroneous error message (by Benno Schulenberg)

build-sys:
    - update release dates (by Karel Zak)

docs:
    - add v2.41.2-ReleaseNotes (by Karel Zak)

findmnt:
    - (usage) add a needed equals sign before an optional argument (by Benno Schulenberg)
    - add missing newline in --raw, --pair and --list output formats (by Christian Goeschel Ndjomouo)

fsck.cramfs:
    - check buffer size for memcpy() (by Karel Zak)

getopt:
    - document special symbols that should not be used as option characters (by cgoesche)

gitignore:
    - Ignore tests/diff/ and test/output/ (by Jesse Rosenstock)

hardlink:
    - (man) add note note about ULFILEEQ_DEBUG= (by Karel Zak)

include/mount-api-utils:
    - avoid using sys/mount.h (by Karel Zak)

libblkid:
    - Fix probe_ioctl_tp assigning BLKGETDISKSEQ as physical sector size (by Sam Fink)
    - (ext) reduce false positive (by 胡玮文)
    - improve UUID_SUB= description (by Karel Zak)

lib/color-names:
    - fix stupid bugs (by Karel Zak)
    - Fix color name canonicalization (by Karel Zak)

libfdisk:
    - (script) improve separator usage in named-fields dump (by Karel Zak)
    - (script) fix device name separator parsing (by Karel Zak)

liblastlog2:
    - markup fixes for man pages (by Mario Blättermann)

libmount:
    - don't report fsconfig errors with "nofail" (by Karel Zak)

lib/path:
    - avoid double free() for cpusets (by Karel Zak)

lib/strutils:
    - add ul_ prefix to strrep() and strrem() functions (by Karel Zak)
    - add ul_ prefix to split() function (by Karel Zak)
    - add ul_ prefix to strappend() functions (by Karel Zak)
    - add ul_ prefix to strconcat() functions (by Karel Zak)
    - add ul_ prefix to startswith() and endswith() (by Karel Zak)

lib/strv:
    - use ul_ prefix for strv functions (by Karel Zak)

logger:
    - fix buffer overflow when read stdin (by Karel Zak)
    - fix incorrect warning message when both --file and a message are specified (by Alexander Kappner)

lsblk:
    - fix possible use-after-free (by Karel Zak)
    - fix memory leak [coverity scan] (by Karel Zak)
    - use md as fallback TYPE when md/level empty (by codefiles)

lscpu:
    - New Arm C1 parts (by Jeremy Linton)
    - Add NVIDIA Olympus arm64 core (by Matthew R. Ochs)

man:
    - Fixed incorrect ipcrm options (by Prasanna Paithankar)
    - Replace RETURN VALUE with EXIT STATUS in section 1 (by Jesse Rosenstock)

mkfs.cramfs:
    - avoid uninitialized value [coverity scan] (by Karel Zak)

more:
    - temporarily ignore stdin when waiting for stderr (by Karel Zak)

po:
    - update uk.po (from translationproject.org) (by Yuri Chornoivan)
    - update ro.po (from translationproject.org) (by Remus-Gabriel Chelu)
    - update pl.po (from translationproject.org) (by Jakub Bogusz)
    - update nl.po (from translationproject.org) (by Benno Schulenberg)
    - update ko.po (from translationproject.org) (by Seong-ho Cho)
    - update ja.po (from translationproject.org) (by YOSHIDA Hideki)
    - update hr.po (from translationproject.org) (by Božidar Putanec)
    - update fr.po (from translationproject.org) (by Frédéric Marchal)
    - update es.po (from translationproject.org) (by Antonio Ceballos Roa)
    - update de.po (from translationproject.org) (by Mario Blättermann)
    - update cs.po (from translationproject.org) (by Petr Písař)

po-man:
    - merge changes (by Karel Zak)
    - update uk.po (from translationproject.org) (by Yuri Chornoivan)
    - update ro.po (from translationproject.org) (by Remus-Gabriel Chelu)
    - update pl.po (from translationproject.org) (by Michał Kułach)
    - update de.po (from translationproject.org) (by Mario Blättermann)
    - merge changes (by Karel Zak)
    - update es.po (from translationproject.org) (by Antonio Ceballos Roa)

rev:
    - add --zero option to --help output (by Christian Goeschel Ndjomouo)

setpriv:
    - Improve getgroups() Portability (by Karel Zak)

sfdisk:
    - reject spurious arguments for --reorder/--backup-pt-sectors (by Thomas Weißschuh)

tests:
    - add color names test (by Karel Zak)

tests/helpers/test_sigstate.c:
    - explicitly reset SIGINT to default action after trapping (by Hongxu Jia)

tools:
    - add git-version-next script release versioning (by Karel Zak)

zramctl:
    - ignore ENOENT when setting max_comp_streams (by Jiang XueQian)
    - fix MEM-USED column description (by Jérôme Poulin)

Misc:
    - Add missing ;; to -m case (#1) (by Nate Drake)


^ permalink raw reply

* [PATCH 4/6] fallocate: redo four tweaks that were accidentally undone
From: Benno Schulenberg @ 2025-09-18  8:57 UTC (permalink / raw)
  To: util-linux
In-Reply-To: <20250918085707.41785-1-bensberg@telfort.nl>

Recent commit c407a13fc1 undid some changes that were made by
commits 762f295a02 and 325a269995.  Redo these changes.

Signed-off-by: Benno Schulenberg <bensberg@telfort.nl>
---
 sys-utils/fallocate.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c
index 30c391406..afd615537 100644
--- a/sys-utils/fallocate.c
+++ b/sys-utils/fallocate.c
@@ -17,9 +17,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://gnu.org/licenses/>.
  */
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -396,16 +395,16 @@ int main(int argc, char **argv)
 		if (length == -2LL)
 			length = 0;
 		if (length < 0)
-			errx(EXIT_FAILURE, _("invalid length value specified"));
+			errx(EXIT_FAILURE, _("invalid length"));
 	} else {
 		/* it's safer to require the range specification (--length --offset) */
 		if (length == -2LL)
 			errx(EXIT_FAILURE, _("no length argument specified"));
 		if (length <= 0)
-			errx(EXIT_FAILURE, _("invalid length value specified"));
+			errx(EXIT_FAILURE, _("invalid length"));
 	}
 	if (offset < 0)
-		errx(EXIT_FAILURE, _("invalid offset value specified"));
+		errx(EXIT_FAILURE, _("invalid offset"));
 
 	/* O_CREAT makes sense only for the default fallocate(2) behavior
 	 * when mode is no specified and new space is allocated */
-- 
2.51.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox