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