mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: dm-devel@lists.linux.dev
Cc: kwolf@redhat.com, hreitz@redhat.com, mpatocka@redhat.com,
	snitzer@kernel.org, bmarzins@redhat.com,
	linux-kernel@vger.kernel.org
Subject: [PATCH 2/2] dm mpath: Interface for explicit probing of active paths
Date: Tue, 29 Apr 2025 18:50:18 +0200	[thread overview]
Message-ID: <20250429165018.112999-3-kwolf@redhat.com> (raw)
In-Reply-To: <20250429165018.112999-1-kwolf@redhat.com>

Multipath cannot directly provide failover for ioctls in the kernel
because it doesn't know what each ioctl means and which result could
indicate a path error. Userspace generally knows what the ioctl it
issued means and if it might be a path error, but neither does it know
which path the ioctl took nor does it necessarily have the privileges to
fail a path using the control device.

In order to allow userspace to address this situation, implement a
DM_MPATH_PROBE_PATHS ioctl that prompts the dm-mpath driver to probe all
active paths in the current path group to see whether they still work,
and fail them if not. If this returns success, userspace can retry the
ioctl and expect that the previously hit bad path is now failed (or
working again).

The immediate motivation for this is the use of SG_IO in QEMU for SCSI
passthrough. Following a failed SG_IO ioctl, QEMU will trigger probing
to ensure that all active paths are actually alive, so that retrying
SG_IO at least has a lower chance of failing due to a path error.
However, the problem is broader than just SG_IO (it affects any ioctl),
and if applications need failover support for other ioctls, the same
probing can be used.

This is not implemented on the DM control device, but on the DM mpath
block devices, to allow all users who have access to such a block device
to make use of this interface, specifically to implement failover for
ioctls. For the same reason, it is also unprivileged. Its implementation
is effectively just a bunch of reads, which could already be issued by
userspace, just without any guarantee that all the rights paths are
selected.

The probing implemented here is done fully synchronously path by path;
probing all paths concurrently is left as an improvement for the future.

Co-developed-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 include/uapi/linux/dm-ioctl.h |  9 +++-
 drivers/md/dm-ioctl.c         |  1 +
 drivers/md/dm-mpath.c         | 91 ++++++++++++++++++++++++++++++++++-
 3 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h
index b08c7378164d..3225e025e30e 100644
--- a/include/uapi/linux/dm-ioctl.h
+++ b/include/uapi/linux/dm-ioctl.h
@@ -258,10 +258,12 @@ enum {
 	DM_DEV_SET_GEOMETRY_CMD,
 	DM_DEV_ARM_POLL_CMD,
 	DM_GET_TARGET_VERSION_CMD,
+	DM_MPATH_PROBE_PATHS_CMD,
 };
 
 #define DM_IOCTL 0xfd
 
+/* Control device ioctls */
 #define DM_VERSION       _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl)
 #define DM_REMOVE_ALL    _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl)
 #define DM_LIST_DEVICES  _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl)
@@ -285,10 +287,13 @@ enum {
 #define DM_TARGET_MSG	 _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl)
 #define DM_DEV_SET_GEOMETRY	_IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
 
+/* Block device ioctls */
+#define DM_MPATH_PROBE_PATHS _IO(DM_IOCTL, DM_MPATH_PROBE_PATHS_CMD)
+
 #define DM_VERSION_MAJOR	4
-#define DM_VERSION_MINOR	49
+#define DM_VERSION_MINOR	50
 #define DM_VERSION_PATCHLEVEL	0
-#define DM_VERSION_EXTRA	"-ioctl (2025-01-17)"
+#define DM_VERSION_EXTRA	"-ioctl (2025-04-28)"
 
 /* Status bits */
 #define DM_READONLY_FLAG	(1 << 0) /* In/Out */
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index d42eac944eb5..4165fef4c170 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1885,6 +1885,7 @@ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
 		{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry},
 		{DM_DEV_ARM_POLL_CMD, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll},
 		{DM_GET_TARGET_VERSION_CMD, 0, get_target_version},
+		{DM_MPATH_PROBE_PATHS_CMD, 0, NULL}, /* block device ioctl */
 	};
 
 	if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 909ed6890ba5..af17a35c6457 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -2021,6 +2021,85 @@ static int multipath_message(struct dm_target *ti, unsigned int argc, char **arg
 	return r;
 }
 
+/*
+ * Perform a minimal read from the given path to find out whether the
+ * path still works.  If a path error occurs, fail it.
+ */
+static int probe_path(struct pgpath *pgpath)
+{
+	struct block_device *bdev = pgpath->path.dev->bdev;
+	unsigned int read_size = bdev_logical_block_size(bdev);
+	struct page *page;
+	struct bio *bio;
+	blk_status_t status;
+	int r = 0;
+
+	if (WARN_ON_ONCE(read_size > PAGE_SIZE))
+		return -EINVAL;
+
+	page = alloc_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	/* Perform a minimal read: Sector 0, length read_size */
+	bio = bio_alloc(bdev, 1, REQ_OP_READ, GFP_KERNEL);
+	if (!bio) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	bio->bi_iter.bi_sector = 0;
+	__bio_add_page(bio, page, read_size, 0);
+	submit_bio_wait(bio);
+	status = bio->bi_status;
+	bio_put(bio);
+
+	if (status && blk_path_error(status))
+		fail_path(pgpath);
+
+out:
+	__free_page(page);
+	return r;
+}
+
+/*
+ * Probe all active paths in current_pg to find out whether they still work.
+ * Fail all paths that do not work.
+ *
+ * Return -ENOTCONN if no valid path is left (even outside of current_pg). We
+ * cannot probe paths in other pgs without switching current_pg, so if valid
+ * paths are only in different pgs, they may or may not work. Userspace can
+ * submit a request and we'll switch. If the request fails, it may need to
+ * probe again.
+ */
+static int probe_active_paths(struct multipath *m)
+{
+	struct pgpath *pgpath;
+	struct priority_group *pg;
+	int r = 0;
+
+	mutex_lock(&m->work_mutex);
+
+	pg = READ_ONCE(m->current_pg);
+	if (pg) {
+		list_for_each_entry(pgpath, &pg->pgpaths, list) {
+			if (!pgpath->is_active)
+				continue;
+
+			r = probe_path(pgpath);
+			if (r < 0)
+				goto out;
+		}
+	}
+
+	if (!atomic_read(&m->nr_valid_paths))
+		r = -ENOTCONN;
+
+out:
+	mutex_unlock(&m->work_mutex);
+	return r;
+}
+
 static int multipath_prepare_ioctl(struct dm_target *ti,
 				   struct block_device **bdev,
 				   unsigned int cmd, unsigned long arg,
@@ -2031,6 +2110,16 @@ static int multipath_prepare_ioctl(struct dm_target *ti,
 	unsigned long flags;
 	int r;
 
+	if (_IOC_TYPE(cmd) == DM_IOCTL) {
+		*forward = false;
+		switch (cmd) {
+		case DM_MPATH_PROBE_PATHS:
+			return probe_active_paths(m);
+		default:
+			return -ENOTTY;
+		}
+	}
+
 	pgpath = READ_ONCE(m->current_pgpath);
 	if (!pgpath || !mpath_double_check_test_bit(MPATHF_QUEUE_IO, m))
 		pgpath = choose_pgpath(m, 0);
@@ -2182,7 +2271,7 @@ static int multipath_busy(struct dm_target *ti)
  */
 static struct target_type multipath_target = {
 	.name = "multipath",
-	.version = {1, 14, 0},
+	.version = {1, 15, 0},
 	.features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE |
 		    DM_TARGET_PASSES_INTEGRITY,
 	.module = THIS_MODULE,
-- 
2.49.0


  parent reply	other threads:[~2025-04-29 16:52 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-29 16:50 [PATCH 0/2] " Kevin Wolf
2025-04-29 16:50 ` [PATCH 1/2] dm: Allow .prepare_ioctl to handle ioctls directly Kevin Wolf
2025-04-29 23:22   ` Benjamin Marzinski
2025-05-08 13:50   ` Martin Wilck
2025-04-29 16:50 ` Kevin Wolf [this message]
2025-04-29 23:22   ` [PATCH 2/2] dm mpath: Interface for explicit probing of active paths Benjamin Marzinski
2025-05-08 13:51 ` [PATCH 0/2] " Martin Wilck
2025-05-12 13:46   ` Mikulas Patocka
2025-05-13  7:06     ` Martin Wilck
2025-05-12 15:18   ` Kevin Wolf
2025-05-13  5:55     ` Christoph Hellwig
2025-05-13  6:09       ` Hannes Reinecke
2025-05-13  6:14         ` Christoph Hellwig
2025-05-13  6:32           ` Hannes Reinecke
2025-05-13  6:49             ` Christoph Hellwig
2025-05-13  8:17               ` Martin Wilck
2025-05-14  4:53                 ` Christoph Hellwig
2025-05-15 11:14                   ` Paolo Bonzini
2025-05-13 16:29               ` Benjamin Marzinski
2025-05-14  4:56                 ` Christoph Hellwig
2025-05-14  6:39                 ` Hannes Reinecke
2025-05-14 16:01                   ` Benjamin Marzinski
2025-05-16  5:52                 ` Christoph Hellwig
2025-05-13  9:29       ` Kevin Wolf
2025-05-13 15:43         ` Paolo Bonzini
2025-05-14  4:57         ` Christoph Hellwig
2025-05-14 16:23           ` Benjamin Marzinski
2025-05-14 17:37             ` Martin Wilck
2025-05-15  2:53               ` Paolo Bonzini
2025-05-15 10:34                 ` Martin Wilck
2025-05-15 10:51                   ` Paolo Bonzini
2025-05-15 14:50                     ` Martin Wilck
2025-05-15 14:29                   ` Benjamin Marzinski
2025-05-15 15:00                     ` Martin Wilck
2025-05-16  5:57                       ` Christoph Hellwig
2025-05-13  6:30     ` Hannes Reinecke
2025-05-13 18:09       ` Benjamin Marzinski
2025-05-13  8:00     ` Martin Wilck
2025-05-13 10:06       ` Martin Wilck
2025-05-14 21:21       ` Martin Wilck
2025-05-15 10:11         ` Kevin Wolf
2025-05-15 11:09           ` Paolo Bonzini
2025-05-15 15:18             ` Martin Wilck
2025-05-15 15:05           ` Martin Wilck
2025-05-16  6:00           ` Christoph Hellwig
2025-05-16 16:06             ` Benjamin Marzinski
2025-05-19  5:32               ` Christoph Hellwig
2025-05-19 18:24                 ` Benjamin Marzinski
2025-05-28 20:44                 ` Martin Wilck
2025-05-19 10:06             ` Kevin Wolf
2025-05-19 17:33             ` Martin Wilck
2025-05-20 13:46               ` Christoph Hellwig

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250429165018.112999-3-kwolf@redhat.com \
    --to=kwolf@redhat.com \
    --cc=bmarzins@redhat.com \
    --cc=dm-devel@lists.linux.dev \
    --cc=hreitz@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mpatocka@redhat.com \
    --cc=snitzer@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome