* [PATCH 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove
[not found] <20261005045726.71858-1-suresh.thelkar.ref@yahoo.com>
@ 2026-10-05 4:57 ` Suresh Thelkar
2026-10-05 4:57 ` [PATCH 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
` (2 more replies)
0 siblings, 3 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-05 4:57 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
Under heavy device-mapper ioctl load (e.g. LUN-number recycling combined
with ALUA transitions), dm_flush_map__() could leave a multipath map
suspended indefinitely after a failed DM_DEVICE_REMOVE. A suspended map
wedges any later task that touches it in uninterruptible D state, and
because sync() walks every block device, this can hang I/O node-wide
until reboot. The storage itself is healthy; the defect is entirely in
user space.
This series fixes two defects on the failed-remove path:
1. Query errors were misclassified as external removal. Anything that
was not DM_IS_MPATH_YES was treated as "removed externally" and
returned DM_FLUSH_OK without resuming. But dm_is_mpath() also
returns DM_IS_MPATH_ERR (-1) when the status query itself fails
transiently under ioctl pressure, so a still-suspended map was
abandoned. Only DM_IS_MPATH_NO now counts as external removal; a
query error is logged and falls through to the resume path.
2. The recovery resume was fire-and-forget. Even when recovery was
attempted, a single DM_DEVICE_RESUME was issued and its result
never checked. The resume is now retried once if the ioctl fails,
so the map is not left suspended on a transient failure.
Only libmultipath/devmapper.c changes. There is no change to the success
path or to behavior outside the failed-remove branch.
Validated with a deterministic LD_PRELOAD fault injector that forces
DM_DEV_REMOVE -> EBUSY followed by DM_TABLE_STATUS -> error (so
dm_is_mpath() returns -1) on a single scsi_debug-backed test map:
patched builds leave the map Active; unpatched builds leave it
Suspended.
GitHub PR for reference: https://github.com/opensvc/multipath-tools/pull/167
Suresh Thelkar (2):
libmultipath: dm_flush_map__: do not treat query error as external
removal
libmultipath: dm_flush_map__: retry the resume after a failed remove
libmultipath/devmapper.c | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal
2026-10-05 4:57 ` [PATCH 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove Suresh Thelkar
@ 2026-10-05 4:57 ` Suresh Thelkar
2026-10-05 17:33 ` Benjamin Marzinski
2026-10-05 4:57 ` [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2 siblings, 1 reply; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-05 4:57 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
On a failed DM_DEVICE_REMOVE, dm_flush_map__() classified anything that
was not DM_IS_MPATH_YES as "removed externally" and returned DM_FLUSH_OK
without resuming the map it had just suspended. dm_is_mpath() also
returns DM_IS_MPATH_ERR (-1) when the status query itself fails, which
happens transiently under a heavy ioctl load (e.g. LUN-number recycling
plus ALUA transitions). A transient error was thus misread as a
successful external removal, and the map was left suspended
indefinitely, wedging later I/O node-wide via sync().
Only DM_IS_MPATH_NO now counts as "removed externally". A query error is
logged and falls through to the resume path instead of being reported as
success.
Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
---
libmultipath/devmapper.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index e3870f59..a5570a85 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1042,13 +1042,26 @@ int dm_flush_map__ (const char *mapname, int flags, int retries)
}
condlog(4, "multipath map %s removed", mapname);
return DM_FLUSH_OK;
- } else if (dm_is_mpath(mapname) != DM_IS_MPATH_YES) {
- condlog(4, "multipath map %s removed externally",
- mapname);
- return DM_FLUSH_OK; /* raced. someone else removed it */
} else {
- condlog(2, "failed to remove multipath map %s",
- mapname);
+ int is_mpath = dm_is_mpath(mapname);
+
+ if (is_mpath == DM_IS_MPATH_NO) {
+ condlog(4, "multipath map %s removed externally",
+ mapname);
+ return DM_FLUSH_OK; /* raced. someone else removed it */
+ }
+ /*
+ * A query error (DM_IS_MPATH_ERR) must not be treated
+ * as "removed externally": under an ioctl storm the
+ * status query can transiently fail, and returning
+ * DM_FLUSH_OK here would leave the map suspended.
+ */
+ if (is_mpath == DM_IS_MPATH_ERR)
+ condlog(1, "%s: unable to verify map state after failed remove",
+ mapname);
+ else
+ condlog(2, "failed to remove multipath map %s",
+ mapname);
if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1) {
dm_simplecmd_noflush(DM_DEVICE_RESUME,
mapname, udev_flags);
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove
2026-10-05 4:57 ` [PATCH 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove Suresh Thelkar
2026-10-05 4:57 ` [PATCH 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
@ 2026-10-05 4:57 ` Suresh Thelkar
2026-10-05 17:59 ` Benjamin Marzinski
2026-10-06 21:07 ` Martin Wilck
2026-10-07 9:22 ` [PATCH v2 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2 siblings, 2 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-05 4:57 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
After a failed DM_DEVICE_REMOVE the map was suspended earlier in
dm_flush_map__(), and the recovery path issued a single fire-and-forget
DM_DEVICE_RESUME whose result was ignored. If that resume ioctl failed
(possible under an ioctl storm) the map stayed suspended, which wedges
any later task in uninterruptible D state and, because sync() walks
every block device, hangs I/O node-wide until reboot.
Since the map is known to have been suspended at the start of the loop,
the DM_DEVICE_RESUME return value is authoritative. Simply retry the
resume once if the ioctl fails, so a transient failure does not leave
the map suspended. The remove is still retried by the enclosing loop
when retries are requested.
Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
---
libmultipath/devmapper.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index a5570a85..bea7e804 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1062,10 +1062,12 @@ int dm_flush_map__ (const char *mapname, int flags, int retries)
else
condlog(2, "failed to remove multipath map %s",
mapname);
- if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1) {
- dm_simplecmd_noflush(DM_DEVICE_RESUME,
- mapname, udev_flags);
- }
+ /* The map was suspended above; resume it so a failed
+ * remove does not leave it suspended. Retry once if the
+ * resume ioctl itself fails. */
+ if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1 &&
+ !dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags))
+ dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags);
}
if (retries)
sleep(1);
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal
2026-10-05 4:57 ` [PATCH 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
@ 2026-10-05 17:33 ` Benjamin Marzinski
0 siblings, 0 replies; 19+ messages in thread
From: Benjamin Marzinski @ 2026-10-05 17:33 UTC (permalink / raw)
To: Suresh Thelkar; +Cc: dm-devel, Martin Wilck
On Mon, Oct 05, 2026 at 04:57:25AM +0000, Suresh Thelkar wrote:
> On a failed DM_DEVICE_REMOVE, dm_flush_map__() classified anything that
> was not DM_IS_MPATH_YES as "removed externally" and returned DM_FLUSH_OK
> without resuming the map it had just suspended. dm_is_mpath() also
> returns DM_IS_MPATH_ERR (-1) when the status query itself fails, which
> happens transiently under a heavy ioctl load (e.g. LUN-number recycling
> plus ALUA transitions). A transient error was thus misread as a
> successful external removal, and the map was left suspended
> indefinitely, wedging later I/O node-wide via sync().
>
> Only DM_IS_MPATH_NO now counts as "removed externally". A query error is
> logged and falls through to the resume path instead of being reported as
> success.
>
> Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove
2026-10-05 4:57 ` [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
@ 2026-10-05 17:59 ` Benjamin Marzinski
2026-10-06 21:07 ` Martin Wilck
1 sibling, 0 replies; 19+ messages in thread
From: Benjamin Marzinski @ 2026-10-05 17:59 UTC (permalink / raw)
To: Suresh Thelkar; +Cc: dm-devel, Martin Wilck
On Mon, Oct 05, 2026 at 04:57:26AM +0000, Suresh Thelkar wrote:
> After a failed DM_DEVICE_REMOVE the map was suspended earlier in
> dm_flush_map__(), and the recovery path issued a single fire-and-forget
> DM_DEVICE_RESUME whose result was ignored. If that resume ioctl failed
> (possible under an ioctl storm) the map stayed suspended, which wedges
> any later task in uninterruptible D state and, because sync() walks
> every block device, hangs I/O node-wide until reboot.
>
> Since the map is known to have been suspended at the start of the loop,
> the DM_DEVICE_RESUME return value is authoritative. Simply retry the
> resume once if the ioctl fails, so a transient failure does not leave
> the map suspended. The remove is still retried by the enclosing loop
> when retries are requested.
>
> Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove
2026-10-05 4:57 ` [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
2026-10-05 17:59 ` Benjamin Marzinski
@ 2026-10-06 21:07 ` Martin Wilck
2026-10-07 7:16 ` Suresh Thelkar
1 sibling, 1 reply; 19+ messages in thread
From: Martin Wilck @ 2026-10-06 21:07 UTC (permalink / raw)
To: Suresh Thelkar, dm-devel; +Cc: Benjamin Marzinski
On Mon, 2026-10-05 at 04:57 +0000, Suresh Thelkar wrote:
> After a failed DM_DEVICE_REMOVE the map was suspended earlier in
> dm_flush_map__(), and the recovery path issued a single fire-and-
> forget
> DM_DEVICE_RESUME whose result was ignored. If that resume ioctl
> failed
> (possible under an ioctl storm) the map stayed suspended, which
> wedges
> any later task in uninterruptible D state and, because sync() walks
> every block device, hangs I/O node-wide until reboot.
>
> Since the map is known to have been suspended at the start of the
> loop,
> the DM_DEVICE_RESUME return value is authoritative. Simply retry the
> resume once if the ioctl fails, so a transient failure does not leave
> the map suspended. The remove is still retried by the enclosing loop
> when retries are requested.
>
> Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
> ---
> libmultipath/devmapper.c | 10 ++++++----
> 1 file changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
> index a5570a85..bea7e804 100644
> --- a/libmultipath/devmapper.c
> +++ b/libmultipath/devmapper.c
> @@ -1062,10 +1062,12 @@ int dm_flush_map__ (const char *mapname, int
> flags, int retries)
> else
> condlog(2, "failed to remove
> multipath map %s",
> mapname);
> - if ((flags & DMFL_SUSPEND) &&
> queue_if_no_path != -1) {
> -
> dm_simplecmd_noflush(DM_DEVICE_RESUME,
> - mapname,
> udev_flags);
> - }
> + /* The map was suspended above; resume it so
> a failed
> + * remove does not leave it suspended. Retry
> once if the
> + * resume ioctl itself fails. */
> + if ((flags & DMFL_SUSPEND) &&
> queue_if_no_path != -1 &&
> + !dm_simplecmd_noflush(DM_DEVICE_RESUME,
> mapname, udev_flags))
> + dm_simplecmd_noflush(DM_DEVICE_RESUM
> E, mapname, udev_flags);
Have you tested this, and have you observed cases where it actually
helped to resume a device after the first DM_DEVICE_RESUME had failed?
Would it increase the chances to resume the device if we wait a few
fractions of a second before retrying?
Regards
Martin
--
Dr. Martin Wilck <mwilck@suse.com>
SUSE Software Solutions Germany GmbH, Frankenstr. 146, 90461 Nürnberg,
Germany
Geschäftsführer: Stefan Gaiser, Jochen Jaser, Abhinav Puri (HRB
36809,AG Nürnberg)
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove
2026-10-06 21:07 ` Martin Wilck
@ 2026-10-07 7:16 ` Suresh Thelkar
2026-10-07 7:22 ` Martin Wilck
0 siblings, 1 reply; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-07 7:16 UTC (permalink / raw)
To: Martin Wilck, dm-devel; +Cc: Benjamin Marzinski, Suresh Thelkar
On Tue, 2026-10-06 at 23:07 +0200, Martin Wilck wrote:
> Have you tested this, and have you observed cases where it actually
> helped to resume a device after the first DM_DEVICE_RESUME had failed?
>
> Would it increase the chances to resume the device if we wait a few
> fractions of a second before retrying?
Thanks Martin, you're right to push on this.
Our fault injector was written to reproduce the exact sequence we
observed in the field: DM_DEVICE_REMOVE returned -EBUSY, and the
following status query then failed. So the injector deliberately forces
just those two -- DM_DEV_REMOVE -> EBUSY and the next DM_TABLE_STATUS ->
error -- which is what drives dm_is_mpath() to return -1. It does not
force DM_DEVICE_RESUME to fail, so the retry branch added in 2/2 was
never actually exercised.
As a result I can't claim to have observed the second resume help. The
extra retry was purely defensive, in case the resume ioctl itself
failed, and I agree an immediate identical retry with no delay wouldn't
add much. On this no-table-swap resume the realistic failures (-EINVAL
"not suspended", -ENXIO "gone") already mean the map isn't left
suspended, so neither a retry nor a short delay buys much.
So I'll drop 2/2. Patch 1/2 is the real fix for the problem we hit in
our environment. If it's useful, I can add a condlog(1, ...) warning
when the resume ioctl fails, so a leaked suspend is at least visible in
the logs -- would that address your concern?
If you agree, should I resend patch 1/2 on its own as a v2 (with 2/2
dropped)? Please confirm.
Regards,
Suresh
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove
2026-10-07 7:16 ` Suresh Thelkar
@ 2026-10-07 7:22 ` Martin Wilck
2026-10-07 9:09 ` Suresh Thelkar
0 siblings, 1 reply; 19+ messages in thread
From: Martin Wilck @ 2026-10-07 7:22 UTC (permalink / raw)
To: Suresh Thelkar, dm-devel; +Cc: Benjamin Marzinski
On Wed, 2026-10-07 at 07:16 +0000, Suresh Thelkar wrote:
> On Tue, 2026-10-06 at 23:07 +0200, Martin Wilck wrote:
> > Have you tested this, and have you observed cases where it actually
> > helped to resume a device after the first DM_DEVICE_RESUME had
> > failed?
> >
> > Would it increase the chances to resume the device if we wait a few
> > fractions of a second before retrying?
>
> Thanks Martin, you're right to push on this.
>
> Our fault injector was written to reproduce the exact sequence we
> observed in the field: DM_DEVICE_REMOVE returned -EBUSY, and the
> following status query then failed. So the injector deliberately
> forces
> just those two -- DM_DEV_REMOVE -> EBUSY and the next DM_TABLE_STATUS
> ->
> error -- which is what drives dm_is_mpath() to return -1. It does not
> force DM_DEVICE_RESUME to fail, so the retry branch added in 2/2 was
> never actually exercised.
Have you ever found out what the exact reason for the DM_TABLE_STATUS
failure was? What was the return code of the kernel / device-mapper?
Can we perhaps make dm_is_mpath() smarter?
> As a result I can't claim to have observed the second resume help.
> The
> extra retry was purely defensive, in case the resume ioctl itself
> failed, and I agree an immediate identical retry with no delay
> wouldn't
> add much. On this no-table-swap resume the realistic failures (-
> EINVAL
> "not suspended", -ENXIO "gone") already mean the map isn't left
> suspended, so neither a retry nor a short delay buys much.
> 09:17 multip
> So I'll drop 2/2. Patch 1/2 is the real fix for the problem we hit in
> our environment. If it's useful, I can add a condlog(1, ...) warning
> when the resume ioctl fails, so a leaked suspend is at least visible
> in
> the logs -- would that address your concern?
Yes, that makes sense. We should see this in the logs.
> If you agree, should I resend patch 1/2 on its own as a v2 (with 2/2
> dropped)? Please confirm.
Yes, please resend a final version. It's up to you whether you keep two
commits or squash them into one.
Martin
--
Dr. Martin Wilck <mwilck@suse.com>
SUSE Software Solutions Germany GmbH, Frankenstr. 146, 90461 Nürnberg,
Germany
Geschäftsführer: Stefan Gaiser, Jochen Jaser, Abhinav Puri (HRB
36809,AG Nürnberg)
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove
2026-10-07 7:22 ` Martin Wilck
@ 2026-10-07 9:09 ` Suresh Thelkar
0 siblings, 0 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-07 9:09 UTC (permalink / raw)
To: Martin Wilck, dm-devel; +Cc: Benjamin Marzinski, Suresh Thelkar
On Wed, 2026-10-07 at 09:22 +0200, Martin Wilck wrote:
> Have you ever found out what the exact reason for the DM_TABLE_STATUS
> failure was? What was the return code of the kernel / device-mapper?
> Can we perhaps make dm_is_mpath() smarter?
Honestly, no -- we never captured the exact errno of the failing
DM_TABLE_STATUS ioctl in the field. The diagnosis came from the vmcore
and journal, which showed the leaked-suspend symptom together with the
LUN-recycling + ALUA storm, but not the return code of the status ioctl
itself. Our fault injector used EIO as a representative transient
failure rather than a measured one, so I can't give you the kernel's
actual value from the incident.
On making dm_is_mpath() smarter: on current master it already
distinguishes the cases via libmp_mapinfo__(). An ENXIO from the ioctl
(and a !dmi.exists result) maps to DMP_NOT_FOUND -> DM_IS_MPATH_NO
("genuinely gone"), and a target-type mismatch to DMP_NO_MATCH ->
DM_IS_MPATH_NO. Only a non-ENXIO ioctl failure becomes DMP_ERR ->
DM_IS_MPATH_ERR. So the "device is gone" case is already handled; what
reaches DM_IS_MPATH_ERR is a genuinely ambiguous failure where we can't
tell whether the map is still present and suspended. Without the real
errno I'd rather not reclassify any of those as "removed" -- that is
exactly the misclassification patch 1 removes. If we later capture a
specific, reliably-benign errno there, we can special-case it then.
I'll send v2 shortly (patch 1 unchanged + a condlog(1) on resume
failure).
Thanks,
Suresh
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v2 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove
2026-10-05 4:57 ` [PATCH 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove Suresh Thelkar
2026-10-05 4:57 ` [PATCH 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
2026-10-05 4:57 ` [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
@ 2026-10-07 9:22 ` Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
` (2 more replies)
2 siblings, 3 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-07 9:22 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
Under heavy device-mapper ioctl load (e.g. LUN-number recycling combined
with ALUA transitions), dm_flush_map__() could leave a multipath map
suspended indefinitely after a failed DM_DEVICE_REMOVE. A suspended map
wedges any later task that touches it in uninterruptible D state, and
because sync() walks every block device, this can hang I/O node-wide
until reboot. The storage itself is healthy; the defect is entirely in
user space.
Patch 1 is the actual fix. Query errors on the failed-remove path were
misclassified as external removal: anything that was not DM_IS_MPATH_YES
was treated as "removed externally" and returned DM_FLUSH_OK without
resuming. But dm_is_mpath() also returns DM_IS_MPATH_ERR (-1) when the
status query itself fails transiently under ioctl pressure, so a
still-suspended map was abandoned. Only DM_IS_MPATH_NO now counts as
external removal; a query error is logged and falls through to the
resume path.
Patch 2 checks the DM_DEVICE_RESUME return value on the failed-remove
path and logs at condlog(1) if it fails, so a leaked suspend is at least
visible in the logs instead of being silent.
Only libmultipath/devmapper.c changes. There is no change to the success
path or to behavior outside the failed-remove branch.
Changes since v1:
- Dropped the v1 "retry the resume" patch. As discussed on the list,
the fault injector never exercised a resume failure, and on this
no-table-swap resume the realistic failures (-EINVAL "not suspended",
-ENXIO "gone") already mean the map is not left suspended, so an
immediate identical retry (with or without a short delay) buys
little.
- Replaced it with a patch that simply logs at condlog(1) if the resume
ioctl fails, so a leaked suspend is visible in the logs.
- Patch 1 is unchanged from v1 and carries Benjamin Marzinski's
Reviewed-by.
v1: https://lore.kernel.org/dm-devel/20261005045726.71858-1-suresh.thelkar@yahoo.com/
Suresh Thelkar (2):
libmultipath: dm_flush_map__: do not treat query error as external
removal
libmultipath: dm_flush_map__: log if the resume fails after a failed
remove
libmultipath/devmapper.c | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
base-commit: d53932bdbee02f440518ea384e63d7c4d3f66685
--
2.43.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v2 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal
2026-10-07 9:22 ` [PATCH v2 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
@ 2026-10-07 9:22 ` Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2 siblings, 0 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-07 9:22 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
On a failed DM_DEVICE_REMOVE, dm_flush_map__() classified anything that
was not DM_IS_MPATH_YES as "removed externally" and returned DM_FLUSH_OK
without resuming the map it had just suspended. dm_is_mpath() also
returns DM_IS_MPATH_ERR (-1) when the status query itself fails, which
happens transiently under a heavy ioctl load (e.g. LUN-number recycling
plus ALUA transitions). A transient error was thus misread as a
successful external removal, and the map was left suspended
indefinitely, wedging later I/O node-wide via sync().
Only DM_IS_MPATH_NO now counts as "removed externally". A query error is
logged and falls through to the resume path instead of being reported as
success.
Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/devmapper.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index e3870f59..a5570a85 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1042,13 +1042,26 @@ int dm_flush_map__ (const char *mapname, int flags, int retries)
}
condlog(4, "multipath map %s removed", mapname);
return DM_FLUSH_OK;
- } else if (dm_is_mpath(mapname) != DM_IS_MPATH_YES) {
- condlog(4, "multipath map %s removed externally",
- mapname);
- return DM_FLUSH_OK; /* raced. someone else removed it */
} else {
- condlog(2, "failed to remove multipath map %s",
- mapname);
+ int is_mpath = dm_is_mpath(mapname);
+
+ if (is_mpath == DM_IS_MPATH_NO) {
+ condlog(4, "multipath map %s removed externally",
+ mapname);
+ return DM_FLUSH_OK; /* raced. someone else removed it */
+ }
+ /*
+ * A query error (DM_IS_MPATH_ERR) must not be treated
+ * as "removed externally": under an ioctl storm the
+ * status query can transiently fail, and returning
+ * DM_FLUSH_OK here would leave the map suspended.
+ */
+ if (is_mpath == DM_IS_MPATH_ERR)
+ condlog(1, "%s: unable to verify map state after failed remove",
+ mapname);
+ else
+ condlog(2, "failed to remove multipath map %s",
+ mapname);
if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1) {
dm_simplecmd_noflush(DM_DEVICE_RESUME,
mapname, udev_flags);
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove
2026-10-07 9:22 ` [PATCH v2 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
@ 2026-10-07 9:22 ` Suresh Thelkar
2026-10-07 16:42 ` Benjamin Marzinski
2026-10-08 4:37 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2 siblings, 1 reply; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-07 9:22 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
After a failed DM_DEVICE_REMOVE the map was suspended earlier in
dm_flush_map__(), and the recovery path issued a single fire-and-forget
DM_DEVICE_RESUME whose result was ignored. If that resume ioctl failed,
the map could be left suspended with no trace in the logs, which wedges
any later task in uninterruptible D state and, because sync() walks
every block device, hangs I/O node-wide until reboot.
Check the DM_DEVICE_RESUME return value and log at condlog(1) when it
fails, so a leaked suspend is at least visible in the logs. The map is
known to have been suspended at the start of the loop, so the ioctl
return value is authoritative.
Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
---
libmultipath/devmapper.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index a5570a85..85c552cf 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1062,10 +1062,12 @@ int dm_flush_map__ (const char *mapname, int flags, int retries)
else
condlog(2, "failed to remove multipath map %s",
mapname);
- if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1) {
- dm_simplecmd_noflush(DM_DEVICE_RESUME,
- mapname, udev_flags);
- }
+ /* Resume the map suspended above; log if the resume
+ * ioctl fails, so a leaked suspend is visible. */
+ if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1 &&
+ !dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags))
+ condlog(1, "%s: failed to resume map after failed remove",
+ mapname);
}
if (retries)
sleep(1);
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove
2026-10-07 9:22 ` [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove Suresh Thelkar
@ 2026-10-07 16:42 ` Benjamin Marzinski
2026-10-07 19:21 ` Martin Wilck
0 siblings, 1 reply; 19+ messages in thread
From: Benjamin Marzinski @ 2026-10-07 16:42 UTC (permalink / raw)
To: Suresh Thelkar; +Cc: dm-devel, Martin Wilck
On Wed, Oct 07, 2026 at 09:22:36AM +0000, Suresh Thelkar wrote:
> After a failed DM_DEVICE_REMOVE the map was suspended earlier in
> dm_flush_map__(), and the recovery path issued a single fire-and-forget
> DM_DEVICE_RESUME whose result was ignored. If that resume ioctl failed,
> the map could be left suspended with no trace in the logs, which wedges
> any later task in uninterruptible D state and, because sync() walks
> every block device, hangs I/O node-wide until reboot.
>
> Check the DM_DEVICE_RESUME return value and log at condlog(1) when it
> fails, so a leaked suspend is at least visible in the logs. The map is
> known to have been suspended at the start of the loop, so the ioctl
> return value is authoritative.
>
> Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
I want to discuss this change from v1 a bit. The issue that the second
resume is solving is the same one that lead us to add a second resume to
dm_addmap_reload(). If the device has an invalid inactive table, and you
do a resume, dm will attempt to swap the table, fail, and leave the
device suspended with the invalid table removed. Immediately issuing a
second resume will cause dm to actually resume with the old table. If we
don't manage to resume the device, it seems much less likely that
waiting a second and re-attempting the remove will succeed (I believe
the most common case is us racing with something that temporarily opens
the device, like udev. It may need the device to be unsuspended to
do a quick read and close it again).
Now, I admit that this is WAY more likely to happen when we are actually
reloading the table, since we just added an inactive table, and dm might
not be able to swap it correctly for any number of reasons. To hit this
issue when removing the device there would need to be an inactive table
(which is unlikely) and dm would have to fail when trying to swap tables
(which is also unlikely). Also, we resume the table in other places in
the code, without caring about this corner case. For consistency's sake,
we should probably either always do it, or only do it when we've loaded
a new table.
Thoughts? Also, I'm fine with this change going in as-is and dealing
with the resume question as a separate issue.
-Ben
> ---
> libmultipath/devmapper.c | 10 ++++++----
> 1 file changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
> index a5570a85..85c552cf 100644
> --- a/libmultipath/devmapper.c
> +++ b/libmultipath/devmapper.c
> @@ -1062,10 +1062,12 @@ int dm_flush_map__ (const char *mapname, int flags, int retries)
> else
> condlog(2, "failed to remove multipath map %s",
> mapname);
> - if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1) {
> - dm_simplecmd_noflush(DM_DEVICE_RESUME,
> - mapname, udev_flags);
> - }
> + /* Resume the map suspended above; log if the resume
> + * ioctl fails, so a leaked suspend is visible. */
> + if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1 &&
> + !dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags))
> + condlog(1, "%s: failed to resume map after failed remove",
> + mapname);
> }
> if (retries)
> sleep(1);
> --
> 2.43.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove
2026-10-07 16:42 ` Benjamin Marzinski
@ 2026-10-07 19:21 ` Martin Wilck
2026-10-08 4:36 ` Suresh Thelkar
0 siblings, 1 reply; 19+ messages in thread
From: Martin Wilck @ 2026-10-07 19:21 UTC (permalink / raw)
To: Benjamin Marzinski, Suresh Thelkar; +Cc: dm-devel
On Wed, 2026-10-07 at 12:42 -0400, Benjamin Marzinski wrote:
> On Wed, Oct 07, 2026 at 09:22:36AM +0000, Suresh Thelkar wrote:
> > After a failed DM_DEVICE_REMOVE the map was suspended earlier in
> > dm_flush_map__(), and the recovery path issued a single fire-and-
> > forget
> > DM_DEVICE_RESUME whose result was ignored. If that resume ioctl
> > failed,
> > the map could be left suspended with no trace in the logs, which
> > wedges
> > any later task in uninterruptible D state and, because sync() walks
> > every block device, hangs I/O node-wide until reboot.
> >
> > Check the DM_DEVICE_RESUME return value and log at condlog(1) when
> > it
> > fails, so a leaked suspend is at least visible in the logs. The map
> > is
> > known to have been suspended at the start of the loop, so the ioctl
> > return value is authoritative.
> >
> > Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
>
> I want to discuss this change from v1 a bit. The issue that the
> second
> resume is solving is the same one that lead us to add a second resume
> to
> dm_addmap_reload(). If the device has an invalid inactive table, and
> you
> do a resume, dm will attempt to swap the table, fail, and leave the
> device suspended with the invalid table removed. Immediately issuing
> a
> second resume will cause dm to actually resume with the old table. If
> we
> don't manage to resume the device, it seems much less likely that
> waiting a second and re-attempting the remove will succeed (I believe
> the most common case is us racing with something that temporarily
> opens
> the device, like udev. It may need the device to be unsuspended to
> do a quick read and close it again).
>
> Now, I admit that this is WAY more likely to happen when we are
> actually
> reloading the table, since we just added an inactive table, and dm
> might
> not be able to swap it correctly for any number of reasons. To hit
> this
> issue when removing the device there would need to be an inactive
> table
> (which is unlikely) and dm would have to fail when trying to swap
> tables
> (which is also unlikely). Also, we resume the table in other places
> in
> the code, without caring about this corner case. For consistency's
> sake,
> we should probably either always do it, or only do it when we've
> loaded
> a new table.
>
> Thoughts? Also, I'm fine with this change going in as-is and dealing
> with the resume question as a separate issue.
Right, thanks for explaining it. I'd forgotten about this failure
scenario. With this in mind, it actually makes sense to retry the
resume. You're right, there should be no inactive table, but retrying
won't hurt. [*]
I'd appreciate a short comment explaining the problem scenario that
the retry attempts to avoid (i.e. a short version of Ben's explanation
here).
Regards
Martin
[*] I wonder what would happen if there was a _valid_ inactive table.
In that case, instead of removing the map, we'd reload it with a
different table, which seems wrong. But again, there should be none.
--
Dr. Martin Wilck <mwilck@suse.com>
SUSE Software Solutions Germany GmbH, Frankenstr. 146, 90461 Nürnberg,
Germany
Geschäftsführer: Stefan Gaiser, Jochen Jaser, Abhinav Puri (HRB
36809,AG Nürnberg)
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove
2026-10-07 19:21 ` Martin Wilck
@ 2026-10-08 4:36 ` Suresh Thelkar
0 siblings, 0 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-08 4:36 UTC (permalink / raw)
To: Martin Wilck, Benjamin Marzinski, dm-devel; +Cc: Suresh Thelkar
On Wed, 2026-10-07 at 21:21 +0200, Martin Wilck wrote:
> > [Ben wrote:] The issue that the second resume is solving is the same
> > one that lead us to add a second resume to dm_addmap_reload(). If the
> > device has an invalid inactive table, and you do a resume, dm will
> > attempt to swap the table, fail, and leave the device suspended with
> > the invalid table removed. Immediately issuing a second resume will
> > cause dm to actually resume with the old table.
>
> Right, thanks for explaining it. [...] it actually makes sense to
> retry the resume. [...] I'd appreciate a short comment explaining the
> problem scenario that the retry attempts to avoid (i.e. a short
> version of Ben's explanation here).
Thanks both, that explains it well. I'll restore the retry in v3 (it is
what v1 did) and add a short comment describing the scenario:
/*
* Resume the map suspended above. If it has an inactive table,
* the resume makes dm try to swap to it; if that swap fails dm
* leaves the map suspended with the invalid table dropped, and a
* second resume reactivates it with the old table (as
* dm_addmap_reload() does). Retry the resume once, and log if it
* is still not resumed so a leaked suspend is visible.
*/
On Ben's consistency point (always retry vs only after loading a new
table): the flush/remove path never loads an inactive table, so in
practice there should be none to swap here. But the retry is cheap and
harmless and mirrors dm_addmap_reload(), so v3 simply retries on this
path too. I kept the condlog(1) from v2 so a map still suspended after
the retry remains visible in the logs.
Re Martin's valid-inactive-table footnote: agreed -- if a valid inactive
table somehow existed, the second resume would swap to it rather than
remove, which would be wrong. But dm_flush_map__() loads no inactive
table, so that state should not arise on this path; it would be a
pre-existing anomaly outside this fix, so I've left it out of scope here.
Sending v3 now with the above.
Thanks,
Suresh
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove
2026-10-07 9:22 ` [PATCH v2 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove Suresh Thelkar
@ 2026-10-08 4:37 ` Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
` (2 more replies)
2 siblings, 3 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-08 4:37 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
Under heavy device-mapper ioctl load (e.g. LUN-number recycling combined
with ALUA transitions), dm_flush_map__() could leave a multipath map
suspended indefinitely after a failed DM_DEVICE_REMOVE. A suspended map
wedges any later task that touches it in uninterruptible D state, and
because sync() walks every block device, this can hang I/O node-wide
until reboot. The storage itself is healthy; the defect is entirely in
user space.
Patch 1 is the primary fix. Query errors on the failed-remove path were
misclassified as external removal: anything that was not DM_IS_MPATH_YES
was treated as "removed externally" and returned DM_FLUSH_OK without
resuming. But dm_is_mpath() also returns DM_IS_MPATH_ERR (-1) when the
status query itself fails transiently under ioctl pressure, so a
still-suspended map was abandoned. Only DM_IS_MPATH_NO now counts as
external removal; a query error is logged and falls through to the
resume path.
Patch 2 makes the resume on the failed-remove path robust: it checks the
DM_DEVICE_RESUME result, retries once if it fails, and logs at condlog(1)
if the map is still not resumed. The retry handles the inactive-table
case (dm fails the table swap and leaves the map suspended with the
invalid table dropped; a second resume reactivates it with the old
table) - the same scenario dm_addmap_reload() already guards against.
Only libmultipath/devmapper.c changes. There is no change to the success
path or to behavior outside the failed-remove branch.
Changes since v2:
- Patch 2: restored the single resume retry (dropped in v2) and added a
comment explaining why, as requested on the list. Per Ben Marzinski's
explanation, a resume can fail the table swap on a map with an
inactive table and leave it suspended; a second resume recovers it
(as dm_addmap_reload() does). Martin Wilck agreed the retry is
worthwhile and asked for the explanatory comment. Kept the v2
condlog(1) so a still-unresumed map stays visible.
- Patch 1 unchanged; carries Benjamin Marzinski's Reviewed-by.
Changes since v1:
- v2 had replaced the retry with a log-only change; v3 restores the
retry together with the explanatory comment.
v1: https://lore.kernel.org/dm-devel/20261005045726.71858-1-suresh.thelkar@yahoo.com/
v2: https://lore.kernel.org/dm-devel/20261007092236.165908-1-suresh.thelkar@yahoo.com/
Suresh Thelkar (2):
libmultipath: dm_flush_map__: do not treat query error as external
removal
libmultipath: dm_flush_map__: retry the resume after a failed remove
libmultipath/devmapper.c | 41 +++++++++++++++++++++++++++++++---------
1 file changed, 32 insertions(+), 9 deletions(-)
base-commit: d53932bdbee02f440518ea384e63d7c4d3f66685
--
2.43.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v3 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal
2026-10-08 4:37 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
@ 2026-10-08 4:37 ` Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
2026-10-08 11:25 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " mwilck
2 siblings, 0 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-08 4:37 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
On a failed DM_DEVICE_REMOVE, dm_flush_map__() classified anything that
was not DM_IS_MPATH_YES as "removed externally" and returned DM_FLUSH_OK
without resuming the map it had just suspended. dm_is_mpath() also
returns DM_IS_MPATH_ERR (-1) when the status query itself fails, which
happens transiently under a heavy ioctl load (e.g. LUN-number recycling
plus ALUA transitions). A transient error was thus misread as a
successful external removal, and the map was left suspended
indefinitely, wedging later I/O node-wide via sync().
Only DM_IS_MPATH_NO now counts as "removed externally". A query error is
logged and falls through to the resume path instead of being reported as
success.
Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/devmapper.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index e3870f59..a5570a85 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1042,13 +1042,26 @@ int dm_flush_map__ (const char *mapname, int flags, int retries)
}
condlog(4, "multipath map %s removed", mapname);
return DM_FLUSH_OK;
- } else if (dm_is_mpath(mapname) != DM_IS_MPATH_YES) {
- condlog(4, "multipath map %s removed externally",
- mapname);
- return DM_FLUSH_OK; /* raced. someone else removed it */
} else {
- condlog(2, "failed to remove multipath map %s",
- mapname);
+ int is_mpath = dm_is_mpath(mapname);
+
+ if (is_mpath == DM_IS_MPATH_NO) {
+ condlog(4, "multipath map %s removed externally",
+ mapname);
+ return DM_FLUSH_OK; /* raced. someone else removed it */
+ }
+ /*
+ * A query error (DM_IS_MPATH_ERR) must not be treated
+ * as "removed externally": under an ioctl storm the
+ * status query can transiently fail, and returning
+ * DM_FLUSH_OK here would leave the map suspended.
+ */
+ if (is_mpath == DM_IS_MPATH_ERR)
+ condlog(1, "%s: unable to verify map state after failed remove",
+ mapname);
+ else
+ condlog(2, "failed to remove multipath map %s",
+ mapname);
if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1) {
dm_simplecmd_noflush(DM_DEVICE_RESUME,
mapname, udev_flags);
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove
2026-10-08 4:37 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
@ 2026-10-08 4:37 ` Suresh Thelkar
2026-10-08 11:25 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " mwilck
2 siblings, 0 replies; 19+ messages in thread
From: Suresh Thelkar @ 2026-10-08 4:37 UTC (permalink / raw)
To: dm-devel; +Cc: Benjamin Marzinski, Martin Wilck, Suresh Thelkar
After a failed DM_DEVICE_REMOVE the map was suspended earlier in
dm_flush_map__(), and the recovery path issued a single fire-and-forget
DM_DEVICE_RESUME whose result was ignored. If that resume failed, the
map could be left suspended with no trace in the logs, which wedges any
later task in uninterruptible D state and, because sync() walks every
block device, hangs I/O node-wide until reboot.
A resume can fail if the map has an inactive table: dm tries to swap to
it and, if the swap fails, leaves the map suspended with the invalid
table dropped. An immediate second resume then reactivates it with the
old table. This is the same scenario dm_addmap_reload() already guards
against. It is far less likely on the remove path (there should be no
inactive table here), but retrying is cheap and harmless.
Check the DM_DEVICE_RESUME return value, retry the resume once if it
fails, and log at condlog(1) if the map is still not resumed so a leaked
suspend is at least visible in the logs.
Signed-off-by: Suresh Thelkar <suresh.thelkar@yahoo.com>
---
libmultipath/devmapper.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index a5570a85..e3f71bc7 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1062,10 +1062,20 @@ int dm_flush_map__ (const char *mapname, int flags, int retries)
else
condlog(2, "failed to remove multipath map %s",
mapname);
- if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1) {
- dm_simplecmd_noflush(DM_DEVICE_RESUME,
- mapname, udev_flags);
- }
+ /*
+ * Resume the map suspended above. If it has an inactive
+ * table, the resume makes dm try to swap to it; if that
+ * swap fails dm leaves the map suspended with the
+ * invalid table dropped, and a second resume reactivates
+ * it with the old table (as dm_addmap_reload() does).
+ * Retry the resume once, and log if it is still not
+ * resumed so a leaked suspend is visible.
+ */
+ if ((flags & DMFL_SUSPEND) && queue_if_no_path != -1 &&
+ !dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags) &&
+ !dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags))
+ condlog(1, "%s: failed to resume map after failed remove",
+ mapname);
}
if (retries)
sleep(1);
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove
2026-10-08 4:37 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
@ 2026-10-08 11:25 ` mwilck
2 siblings, 0 replies; 19+ messages in thread
From: mwilck @ 2026-10-08 11:25 UTC (permalink / raw)
To: Suresh Thelkar; +Cc: dm-devel, Benjamin Marzinski, Martin Wilck
On Thu, 08 Oct 2026 04:37:47 +0000, Suresh Thelkar <suresh.thelkar@yahoo.com> wrote:
> libmultipath: fix leaked device-mapper suspend after a failed remove
>
> Under heavy device-mapper ioctl load (e.g. LUN-number recycling combined
> with ALUA transitions), dm_flush_map__() could leave a multipath map
> suspended indefinitely after a failed DM_DEVICE_REMOVE. A suspended map
> wedges any later task that touches it in uninterruptible D state, and
> because sync() walks every block device, this can hang I/O node-wide
> until reboot. The storage itself is healthy; the defect is entirely in
> user space.
>
> [...]
Reviewed-by: Martin Wilck <mwilck@suse.com>
--
Martin Wilck <mwilck@suse.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-10-08 11:25 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20261005045726.71858-1-suresh.thelkar.ref@yahoo.com>
2026-10-05 4:57 ` [PATCH 0/2] libmultipath: fix leaked device-mapper suspend after a failed remove Suresh Thelkar
2026-10-05 4:57 ` [PATCH 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
2026-10-05 17:33 ` Benjamin Marzinski
2026-10-05 4:57 ` [PATCH 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
2026-10-05 17:59 ` Benjamin Marzinski
2026-10-06 21:07 ` Martin Wilck
2026-10-07 7:16 ` Suresh Thelkar
2026-10-07 7:22 ` Martin Wilck
2026-10-07 9:09 ` Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
2026-10-07 9:22 ` [PATCH v2 2/2] libmultipath: dm_flush_map__: log if the resume fails after a failed remove Suresh Thelkar
2026-10-07 16:42 ` Benjamin Marzinski
2026-10-07 19:21 ` Martin Wilck
2026-10-08 4:36 ` Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 1/2] libmultipath: dm_flush_map__: do not treat query error as external removal Suresh Thelkar
2026-10-08 4:37 ` [PATCH v3 2/2] libmultipath: dm_flush_map__: retry the resume after a failed remove Suresh Thelkar
2026-10-08 11:25 ` [PATCH v3 0/2] libmultipath: fix leaked device-mapper suspend " mwilck
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.