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 <[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..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


Reply via email to