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 <[email protected]> --- 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
