diff --git a/tests/core_hotunplug.c b/tests/core_hotunplug.c
index 4f7e89c95..2884c3f77 100644
--- a/tests/core_hotunplug.c
+++ b/tests/core_hotunplug.c
@@ -43,7 +43,7 @@ struct hotunplug {
 		int sysfs_dev;
 		int sysfs_bus;
 		int sysfs_drv;
-	} fd;
+	} fd;	/* >= 0: valid fd, == -1: closed, < -1: close failed */
 	const char *dev_bus_addr;
 	const char *failure;
 };
@@ -67,6 +67,25 @@ static int local_drm_open_driver(const char *prefix, const char *suffix)
 	return fd_drm;
 }
 
+static int local_close(int fd, const char *message)
+{
+	errno = 0;
+	if (igt_warn_on_f(close(fd), "%s\n", message))
+		return -errno;	/* (never -1) */
+
+	return -1;	/* success - return 'closed' */
+}
+
+static int close_device(int fd_drm)
+{
+	return local_close(fd_drm, "Device close failed");
+}
+
+static int close_sysfs(int fd_sysfs_dev)
+{
+	return local_close(fd_sysfs_dev, "Device sysfs node close failed");
+}
+
 static void prepare_for_unbind(struct hotunplug *priv, char *buf, int buflen)
 {
 	int len;
@@ -83,11 +102,16 @@ static void prepare_for_unbind(struct hotunplug *priv, char *buf, int buflen)
 	igt_assert(priv->dev_bus_addr++);
 
 	/* sysfs_dev no longer needed */
-	close(priv->fd.sysfs_dev);
+	priv->fd.sysfs_dev = close_sysfs(priv->fd.sysfs_dev);
+	igt_assert_eq(priv->fd.sysfs_dev, -1);
 }
 
 static void prepare(struct hotunplug *priv, char *buf, int buflen)
 {
+	/* assert device file descriptors closed cleanly on subtest start */
+	igt_assert_eq(priv->fd.drm, -1);
+	igt_assert_eq(priv->fd.sysfs_dev, -1);
+
 	priv->fd.drm = local_drm_open_driver("", " for subtest");
 
 	priv->fd.sysfs_dev = igt_sysfs_open(priv->fd.drm);
@@ -142,7 +166,7 @@ static void device_unplug(struct hotunplug *priv, const char *prefix)
 	igt_reset_timeout();
 	priv->failure = NULL;
 
-	close(priv->fd.sysfs_dev);
+	priv->fd.sysfs_dev = close_sysfs(priv->fd.sysfs_dev);
 }
 
 /* Re-discover the device by rescanning its bus */
@@ -161,6 +185,7 @@ static void bus_rescan(struct hotunplug *priv)
 
 static void healthcheck(struct hotunplug *priv)
 {
+	/* preserve error code potentially stored before in priv->fd.drm */
 	int fd_drm;
 
 	/* device name may have changed, rebuild IGT device list */
@@ -176,7 +201,17 @@ static void healthcheck(struct hotunplug *priv)
 		priv->failure = NULL;
 	}
 
-	close(fd_drm);
+	fd_drm = close_device(fd_drm);
+	if (priv->fd.drm == -1)	/* store result if no error code to preserve */
+		priv->fd.drm = fd_drm;
+}
+
+static void post_healthcheck(struct hotunplug *priv)
+{
+	igt_abort_on_f(priv->failure, "%s\n", priv->failure);
+
+	igt_require(priv->fd.drm == -1);
+	igt_require(priv->fd.sysfs_dev == -1);
 }
 
 static void set_filter_from_device(int fd)
@@ -203,7 +238,8 @@ static void unbind_rebind(struct hotunplug *priv)
 	prepare(priv, buf, sizeof(buf));
 
 	igt_debug("closing the device\n");
-	close(priv->fd.drm);
+	priv->fd.drm = close_device(priv->fd.drm);
+	igt_assert_eq(priv->fd.drm, -1);
 
 	driver_unbind(priv, "");
 
@@ -217,7 +253,8 @@ static void unplug_rescan(struct hotunplug *priv)
 	prepare(priv, NULL, 0);
 
 	igt_debug("closing the device\n");
-	close(priv->fd.drm);
+	priv->fd.drm = close_device(priv->fd.drm);
+	igt_assert_eq(priv->fd.drm, -1);
 
 	device_unplug(priv, "");
 
@@ -237,7 +274,7 @@ static void hotunbind_lateclose(struct hotunplug *priv)
 	driver_bind(priv);
 
 	igt_debug("late closing the unbound device instance\n");
-	close(priv->fd.drm);
+	priv->fd.drm = close_device(priv->fd.drm);
 
 	healthcheck(priv);
 }
@@ -251,7 +288,7 @@ static void hotunplug_lateclose(struct hotunplug *priv)
 	bus_rescan(priv);
 
 	igt_debug("late closing the removed device instance\n");
-	close(priv->fd.drm);
+	priv->fd.drm = close_device(priv->fd.drm);
 
 	healthcheck(priv);
 }
@@ -260,7 +297,10 @@ static void hotunplug_lateclose(struct hotunplug *priv)
 
 igt_main
 {
-	struct hotunplug priv = { .failure = NULL, };
+	struct hotunplug priv = {
+		.fd		= { .drm = -1, .sysfs_dev = -1, },
+		.failure	= NULL,
+	};
 
 	igt_fixture {
 		int fd_drm;
@@ -276,7 +316,7 @@ igt_main
 		/* Make sure subtests always reopen the same device */
 		set_filter_from_device(fd_drm);
 
-		close(fd_drm);
+		igt_assert_eq(close_device(fd_drm), -1);
 	}
 
 	igt_describe("Check if the driver can be cleanly unbound from a device believed to be closed");
@@ -284,26 +324,26 @@ igt_main
 		unbind_rebind(&priv);
 
 	igt_fixture
-		igt_abort_on_f(priv.failure, "%s\n", priv.failure);
+		post_healthcheck(&priv);
 
 	igt_describe("Check if a device believed to be closed can be cleanly unplugged");
 	igt_subtest("unplug-rescan")
 		unplug_rescan(&priv);
 
 	igt_fixture
-		igt_abort_on_f(priv.failure, "%s\n", priv.failure);
+		post_healthcheck(&priv);
 
 	igt_describe("Check if the driver can be cleanly unbound from a still open device, then released");
 	igt_subtest("hotunbind-lateclose")
 		hotunbind_lateclose(&priv);
 
 	igt_fixture
-		igt_abort_on_f(priv.failure, "%s\n", priv.failure);
+		post_healthcheck(&priv);
 
 	igt_describe("Check if a still open device can be cleanly unplugged, then released");
 	igt_subtest("hotunplug-lateclose")
 		hotunplug_lateclose(&priv);
 
 	igt_fixture
-		igt_abort_on_f(priv.failure, "%s\n", priv.failure);
+		post_healthcheck(&priv);
 }
