From: John Garry <john.garry@linux.dev>
To: sashiko-reviews@lists.linux.dev, John Garry <john.g.garry@oracle.com>
Cc: linux-scsi@vger.kernel.org
Subject: Re: [PATCH v8 03/27] libmultipath: Add path selection support
Date: Mon, 7 Sep 2026 13:00:13 +0100 [thread overview]
Message-ID: <a710c011-e85d-40f3-a6c5-d8344d608430@linux.dev> (raw)
In-Reply-To: <20260907110926.ED4271F00A3A@smtp.kernel.org>
On 9/7/26 12:09, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
>
> New issues:
> - [High] Infinite loop in `mpath_round_robin_path()` if the current round-robin path is concurrently removed from the device list, causing an SRCU deadlock.
> - [High] `mpath_round_robin_path` fast-path incorrectly returns an unlinked path, breaking failover.
> - [Medium] Calling `list_is_singular()` on an RCU-protected list without holding the update lock causes a data race on `head->prev`.
> - [Medium] Path selection functions bypass the `.is_optimized` callback, violating the API contract and causing inconsistent path evaluation.
> - [Low] Missing bounds check on iopolicy index in `mpath_get_iopolicy`.
>
> Pre-existing issues:
> - [Medium] `mpath_device_set_live()` silently swallows setup failures, leaving the driver unaware of the aborted initialization.
> --
>
> commit 59f56cbf3b60a1ec3a1336f453b4cd772a005e7b
> Author: John Garry <john.garry@linux.dev>
>
> libmultipath: Add path selection support
>
> This commit adds path selection support to libmultipath, providing
> round-robin, NUMA, and queue-depth selection modes similar to NVMe. It also
> abstracts NVMe ANA into enum mpath_access_state to allow SCSI ALUA usage
> and introduces iopolicy management functions.
>
>> diff --git a/lib/multipath.c b/lib/multipath.c
>> index 574868ce53381..3541357d07f8d 100644
>> --- a/lib/multipath.c
>> +++ b/lib/multipath.c
>> @@ -7,8 +7,245 @@
>
> [ ... ]
>
>> +int mpath_get_iopolicy(char *buf, int iopolicy)
>> +{
>> + return sprintf(buf, "%s\n", mpath_iopolicy_names[iopolicy]);
>> +}
>
> [Severity: Low]
> Does this code overflow mpath_iopolicy_names[] if a caller provides an
> uninitialized or out-of-bounds iopolicy value?
The caller should sanitize iopolicy
>
> [ ... ]
>
>> +static struct mpath_device *__mpath_find_path(struct mpath_head *mpath_head,
>> + int node)
>> +{
>> + int found_distance = INT_MAX, fallback_distance = INT_MAX, distance;
>> + struct mpath_device *found = NULL, *fallback = NULL, *mpath_device;
>> +
>> + list_for_each_entry_srcu(mpath_device, &mpath_head->dev_list, siblings,
>> + srcu_read_lock_held(&mpath_head->srcu)) {
>> + if (mpath_path_is_disabled(mpath_head, mpath_device))
>> + continue;
>
> [ ... ]
>
>> + switch(mpath_device->access_state) {
>> + case MPATH_STATE_OPTIMIZED:
>
> [Severity: Medium]
> Does this code bypass the driver's dynamic state evaluation by accessing
> access_state directly instead of invoking the provided
> mpath_path_is_optimized() callback wrapper?
>
> If a driver like SCSI ALUA dynamically changes state, this will read
> stale data.
ALUA support will be added later
>
>> + if (distance < found_distance) {
>> + found_distance = distance;
>> + found = mpath_device;
>> + }
>> + break;
>
> [ ... ]
>
>> +static struct mpath_device *mpath_round_robin_path(
>> + struct mpath_head *mpath_head)
>> +{
>> + struct mpath_device *mpath_device, *found = NULL;
>> + int node = numa_node_id();
>> + enum mpath_access_state access_state_old;
>> + struct mpath_device *old =
>> + srcu_dereference(mpath_head->current_path[node],
>> + &mpath_head->srcu);
>> +
>> + if (unlikely(!old))
>> + return __mpath_find_path(mpath_head, node);
>> +
>> + if (list_is_singular(&mpath_head->dev_list)) {
>
> [Severity: Medium]
> Calling list_is_singular() on an RCU-protected list without holding the
> update lock causes a data race on head->prev. In RCU-protected lists,
> only next pointers are safely updated for lockless readers.
locking will be added later
>
> [Severity: High]
> Does this fast-path blindly return old without verifying if it is actually
> the element present in the singular list?
as above
>
> If the previously cached old path was concurrently removed from dev_list
> and exactly one valid path remains, list_is_singular() evaluates to true,
> causing the function to incorrectly return the unlinked path instead of
> the remaining valid path.
>
>> + if (mpath_path_is_disabled(mpath_head, old))
>> + return NULL;
>> + return old;
>> + }
>> +
>> + for (mpath_device = mpath_next_dev(mpath_head, old);
>> + mpath_device && mpath_device != old;
>> + mpath_device = mpath_next_dev(mpath_head, mpath_device)) {
>
> [Severity: High]
> Can this loop spin infinitely if old is concurrently removed from the
> RCU-protected list?
as above
>
> The loop relies on reaching old again to terminate. If a concurrent writer
> unlinks old using list_del_rcu(), it is removed from the circular list but
> its next pointer remains intact. mpath_next_dev() follows old->next into
> the remaining list elements, wrapping around and circling the remaining
> elements indefinitely without ever evaluating mpath_device == old.
>
> If all remaining paths are non-optimized or disabled, the loop never hits
> the break condition, spinning forever under the SRCU read lock and stalling
> any writer.
>
>> +
>> + if (mpath_path_is_disabled(mpath_head, mpath_device))
>> + continue;
>> + if (mpath_device->access_state == MPATH_STATE_OPTIMIZED) {
>
> [Severity: Medium]
> Similar to __mpath_find_path(), does this bypass the driver's dynamic state
> evaluation by accessing access_state directly rather than calling the
> mpath_path_is_optimized() callback wrapper?
>
>> + found = mpath_device;
>> + goto out;
>> + }
>> + if (mpath_device->access_state == MPATH_STATE_NONOPTIMIZED)
>> + found = mpath_device;
>> + }
>
> [ ... ]
>
>> @@ -133,6 +371,19 @@ void mpath_device_set_live(struct mpath_device *mpath_device)
>> }
>> queue_work(mpath_wq, &mpath_head->partition_scan_work);
>> }
>
> [Severity: Medium]
> This is a pre-existing issue, but does mpath_device_set_live() silently
> swallow setup failures earlier in this function?
Yes, this just minics what NVMe does
>
> If device_add_disk() fails, for example due to memory exhaustion, the
> function clears the MPATH_HEAD_DISK_LIVE flag and returns early, masking
> the failure from the caller and leaving the driver unaware of the aborted
> initialization.
>
>> +
>> + mutex_lock(&mpath_head->lock);
>> + if (mpath_path_is_optimized(mpath_head, mpath_device)) {
>> + int node, srcu_idx;
>> +
>> + srcu_idx = srcu_read_lock(&mpath_head->srcu);
>> + for_each_online_node(node)
>> + __mpath_find_path(mpath_head, node);
>> + srcu_read_unlock(&mpath_head->srcu, srcu_idx);
>> + }
>> + mutex_unlock(&mpath_head->lock);
>> +
>> + mpath_synchronize(mpath_head);
>> }
>
next prev parent reply other threads:[~2026-09-07 12:00 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-07 10:49 [PATCH v8 00/27] Native SCSI Multipath support John Garry
2026-09-07 10:49 ` [PATCH v8 01/27] libmultipath: Add initial framework John Garry
2026-09-07 10:49 ` [PATCH v8 02/27] libmultipath: Add basic gendisk support John Garry
2026-09-07 10:49 ` [PATCH v8 03/27] libmultipath: Add path selection support John Garry
2026-09-07 11:09 ` sashiko-bot
2026-09-07 12:00 ` John Garry [this message]
2026-09-07 10:49 ` [PATCH v8 04/27] libmultipath: Add bio handling John Garry
2026-09-07 10:49 ` [PATCH v8 05/27] libmultipath: Add support for mpath_device management John Garry
2026-09-07 10:50 ` [PATCH v8 06/27] libmultipath: Add delayed removal support John Garry
2026-09-07 11:23 ` sashiko-bot
2026-09-07 12:01 ` John Garry
2026-09-07 10:50 ` [PATCH v8 07/27] libmultipath: Add sysfs helpers John Garry
2026-09-07 10:50 ` [PATCH v8 08/27] libmultipath: Add support for block device IOCTL John Garry
2026-09-07 10:50 ` [PATCH v8 09/27] libmultipath: Add mpath_bdev_getgeo() John Garry
2026-09-07 10:50 ` [PATCH v8 10/27] libmultipath: Add mpath_bdev_get_unique_id() John Garry
2026-09-07 10:50 ` [PATCH v8 11/27] scsi-multipath: introduce basic SCSI device support John Garry
2026-09-07 11:33 ` sashiko-bot
2026-09-07 12:06 ` John Garry
2026-09-07 10:50 ` [PATCH v8 12/27] scsi-multipath: introduce scsi_device head structure John Garry
2026-09-07 10:50 ` [PATCH v8 13/27] scsi-multipath: provide sysfs link from to scsi_device John Garry
2026-09-07 10:50 ` [PATCH v8 14/27] scsi-multipath: support iopolicy John Garry
2026-09-07 10:50 ` [PATCH v8 15/27] scsi-multipath: clone each bio John Garry
2026-09-07 11:45 ` sashiko-bot
2026-09-07 12:08 ` John Garry
2026-09-07 10:50 ` [PATCH v8 16/27] scsi-multipath: clear path when device is blocked John Garry
2026-09-07 11:49 ` sashiko-bot
2026-09-07 12:08 ` John Garry
2026-09-07 10:50 ` [PATCH v8 17/27] scsi-multipath: revalidate paths upon device unblock John Garry
2026-09-07 11:56 ` sashiko-bot
2026-09-07 12:10 ` John Garry
2026-09-07 10:50 ` [PATCH v8 18/27] scsi-multipath: failover handling John Garry
2026-09-07 12:02 ` sashiko-bot
2026-09-07 12:13 ` John Garry
2026-09-07 10:50 ` [PATCH v8 19/27] scsi-multipath: provide callbacks for path state John Garry
2026-09-07 12:03 ` sashiko-bot
2026-09-07 12:14 ` John Garry
2026-09-07 10:50 ` [PATCH v8 20/27] scsi-multipath: add scsi_mpath_{start,end}_request() John Garry
2026-09-07 12:09 ` sashiko-bot
2026-09-07 12:22 ` John Garry
2026-09-07 10:50 ` [PATCH v8 21/27] scsi-multipath: add delayed disk removal support John Garry
2026-09-07 10:50 ` [PATCH v8 22/27] scsi: sd: add multipath disk class John Garry
2026-09-07 10:50 ` [PATCH v8 23/27] scsi: sd: add multipath disk attr groups John Garry
2026-09-07 10:50 ` [PATCH v8 24/27] scsi: sd: support multipath disk John Garry
2026-09-07 12:25 ` sashiko-bot
2026-09-07 12:34 ` John Garry
2026-09-07 10:50 ` [PATCH v8 25/27] scsi: sd: add mpath_dev file John Garry
2026-09-07 10:50 ` [PATCH v8 26/27] scsi: sd: add mpath_numa_nodes dev attribute John Garry
2026-09-07 10:50 ` [PATCH v8 27/27] scsi: sd: add mpath_queue_depth " John Garry
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=a710c011-e85d-40f3-a6c5-d8344d608430@linux.dev \
--to=john.garry@linux.dev \
--cc=john.g.garry@oracle.com \
--cc=linux-scsi@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/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