From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=91.218.175.245 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794170884; b=lHr+jaHmkJ5HlgZvJPCC4zs6QXYPzNl/qjVtGcZOVZUbsJTuMr2tk66ISiXXsq8b4qWN kT7WeAyhe3AMJqbkOyFrlv/bsjjMHtkF23/U1g07o/8sP9ATyNt423LMJh7s3FgDSKMHr vdzK0s9wwC/HEkmeZp/wyCTB7HyvGlCGHIaD3CU7viLcjCMbi4DdaUzvFkpkTllxjuftu cynjF9OKmvKYl8ZYuBF3fGk8Ua10mU8dcEi4voaZBMvBh/ddj6jsNNbJl7GIgy9eQP7Al 6GwKmLzOEIwMUVkSlHglvp5FWXQ421rggQ5C6jXtQO6wxCFZk2MHlz1k5rfvVlTFB3g== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794170884; h=DMARC-Filter:X-Envelope-To:DKIM-Signature:X-Envelope-To:Received: X-Mizu-Trace-ID:X-Migadu-Flow:From:To:Cc:Subject:Date:Message-ID: X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=CvZz2HyxVQKF76MfuoDT0u/mT412IESznH8O8dXqMKc=; b=SyyWN5LaHm8CFFiwNpFi5QLuSb+GqfTyAHW0UhQOqJ/nF6FWgH42k6jhXUXk+4DakBCL zFU9J2bQo/JVx/8j07AKru+hRdV0wvQ37ERFr6calDCHacAhvopLzIa6oW9n0G7zG7Ltp oEi6ZDg82R/EwBuqPs3bNXTfoIMBFQahkdH4g5uOYz9Tg/uEjVNrA3EepYIXa/7OCTCF1 2Rg3E5xPL+zkwdScMjJ9Uc93khCAqLq58N3mf2MMcc8z3QWh2tyQYpJAjalJhBvO0lxUP BLAYjn5S+0X4SqdgnIX9HZIoT7qwbW7n36/Q+IEaAjxN0ZsVMk1NCCYbrufdJGL3eTQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass header.d=linux.dev header.i=@linux.dev header.a=rsa-sha256 header.s=key1 header.b=CFygRJAl; arc=none smtp.remote-ip=91.218.175.245 Received: from mta0.migadu.com (out-245.mta0.migadu.com [91.218.175.245]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EE73606A33B for ; Sun, 8 Nov 2026 20:48:03 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 7EE73606A33B Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.dev header.i=@linux.dev header.a=rsa-sha256 header.s=key1 header.b=CFygRJAl X-Envelope-To: linux-scsi@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=fzImnFBnluTdCmwFmXEhzLQOgXmU5tVUO3WAUAEYd6Q=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1794170881; v=1; x=1794775681; b=CFygRJAl3Gju7P0ZiHdceR8gTxxrGwRAWJXAvqnO2IWGc+vIOLJvGZf2tY1DHQO8K5vQJ9Wt EqpO+cnVg+VgB7hfe9o/AHdXCRO5jax3Vz9jwm1Pguq3Jwxeo5bGihGdfvZOf/dXwTZUf1smrLU 18nCOytcE/OaYaks+NThK1qc= X-Envelope-To: linux-scsi@vger.kernel.org Received: by mta10.migadu.com with ESMTPS id 74a264a40e9488a0; Sun, 08 Nov 2026 20:48:01 +0000 X-Mizu-Trace-ID: 74a264a40e9488a0 X-Migadu-Flow: FLOW_OUT From: John Garry To: james.bottomley@hansenpartnership.com, mkp@kernel.org Cc: hare@suse.com, linux-nvme@lists.infradead.org, linux-scsi@vger.kernel.org, snitzer@kernel.org, jmeneghi@redhat.com, bmarzins@redhat.com, dm-devel@lists.linux.dev, hch@lst.de, michael.christie@oracle.com, John Garry Subject: [PATCH v9 06/29] libmultipath: Add support for mpath_device management Date: Sun, 8 Nov 2026 20:47:10 +0000 Message-ID: <20261108204733.557995-7-john.garry@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261108204733.557995-1-john.garry@linux.dev> References: <20261108204733.557995-1-john.garry@linux.dev> X-Mailing-List: linux-scsi@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add support to add or remove a mpath_device as a path. NVMe has almost like-for-like equivalents here: - nvme_mpath_clear_current_path() -> mpath_clear_current_path() - nvme_mpath_add_sysfs_link() -> mpath_add_sysfs_link() - nvme_mpath_remove_sysfs_link() -> mpath_remove_sysfs_link() - nvme_mpath_revalidate_paths() -> mpath_revalidate_paths() The functionality in mpath_clear_paths() and mpath_synchronize() have the same pattern which is frequently used in the NVMe code. Signed-off-by: John Garry --- include/linux/multipath.h | 12 +++ lib/multipath.c | 154 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/include/linux/multipath.h b/include/linux/multipath.h index 374f0531f7e0..033ee8d54bbe 100644 --- a/include/linux/multipath.h +++ b/include/linux/multipath.h @@ -20,10 +20,13 @@ enum mpath_access_state { MPATH_STATE_OTHER }; +#define MPATH_DEVICE_SYSFS_ATTR_LINK 0 + struct mpath_device { struct mpath_head *mpath_head; struct list_head siblings; struct gendisk *disk; + unsigned long flags; int numa_node; atomic_t *nr_active; enum mpath_access_state access_state; @@ -89,6 +92,15 @@ static inline enum mpath_iopolicy_e mpath_read_iopolicy( void mpath_synchronize(struct mpath_head *mpath_head); int mpath_set_iopolicy(const char *str, enum mpath_iopolicy_e *iopolicy); int mpath_get_iopolicy(char *buf, int iopolicy); +bool mpath_clear_current_path(struct mpath_device *mpath_device); +int mpath_add_device(struct mpath_device *mpath_device, struct gendisk *disk, + int numa_node, atomic_t *nr_active); +bool mpath_delete_device(struct mpath_device *mpath_device); +bool mpath_head_devices_empty(struct mpath_head *mpath_head); +void mpath_clear_paths(struct mpath_head *mpath_head); +void mpath_revalidate_paths(struct mpath_head *mpath_head); +void mpath_add_sysfs_link(struct mpath_head *mpath_head); +void mpath_remove_sysfs_link(struct mpath_device *mpath_device); int mpath_get_head(struct mpath_head *mpath_head); void mpath_put_head(struct mpath_head *mpath_head); int mpath_head_init(struct mpath_head *mpath_head); diff --git a/lib/multipath.c b/lib/multipath.c index 5dc7994f8095..fd7821824447 100644 --- a/lib/multipath.c +++ b/lib/multipath.c @@ -52,6 +52,87 @@ void mpath_synchronize(struct mpath_head *mpath_head) } EXPORT_SYMBOL_GPL(mpath_synchronize); +int mpath_add_device(struct mpath_device *mpath_device, struct gendisk *disk, + int numa_node, atomic_t *nr_active) +{ + struct mpath_head *mpath_head = mpath_device->mpath_head; + + if (!disk || !nr_active) + return -EINVAL; + + mpath_device->disk = disk; + mpath_device->numa_node = numa_node; + mpath_device->nr_active = nr_active; + mutex_lock(&mpath_head->lock); + list_add_tail_rcu(&mpath_device->siblings, &mpath_head->dev_list); + mutex_unlock(&mpath_head->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(mpath_add_device); + +bool mpath_delete_device(struct mpath_device *mpath_device) +{ + bool empty; + + mutex_lock(&mpath_device->mpath_head->lock); + list_del_rcu(&mpath_device->siblings); + empty = list_empty(&mpath_device->mpath_head->dev_list); + mutex_unlock(&mpath_device->mpath_head->lock); + + mpath_synchronize(mpath_device->mpath_head); + + return empty; +} +EXPORT_SYMBOL_GPL(mpath_delete_device); + +bool mpath_head_devices_empty(struct mpath_head *mpath_head) +{ + bool empty; + + mutex_lock(&mpath_head->lock); + empty = list_empty(&mpath_head->dev_list); + mutex_unlock(&mpath_head->lock); + + return empty; +} +EXPORT_SYMBOL_GPL(mpath_head_devices_empty); + +bool mpath_clear_current_path(struct mpath_device *mpath_device) +{ + struct mpath_head *mpath_head = mpath_device->mpath_head; + bool changed = false; + int node; + + for_each_node(node) { + if (mpath_device == + rcu_access_pointer(mpath_head->current_path[node])) { + rcu_assign_pointer(mpath_head->current_path[node], + NULL); + changed = true; + } + } + + return changed; +} +EXPORT_SYMBOL_GPL(mpath_clear_current_path); + +void mpath_clear_paths(struct mpath_head *mpath_head) +{ + int node; + + for_each_node(node) + rcu_assign_pointer(mpath_head->current_path[node], NULL); +} +EXPORT_SYMBOL_GPL(mpath_clear_paths); + +void mpath_revalidate_paths(struct mpath_head *mpath_head) +{ + mpath_clear_paths(mpath_head); + mpath_schedule_requeue_work(mpath_head); +} +EXPORT_SYMBOL_GPL(mpath_revalidate_paths); + static bool mpath_path_is_disabled(struct mpath_head *mpath_head, struct mpath_device *mpath_device) { @@ -473,6 +554,8 @@ void mpath_device_set_live(struct mpath_device *mpath_device) queue_work(mpath_wq, &mpath_head->partition_scan_work); } + mpath_add_sysfs_link(mpath_head); + mutex_lock(&mpath_head->lock); if (mpath_path_is_optimized(mpath_head, mpath_device)) { int node, srcu_idx; @@ -489,6 +572,77 @@ void mpath_device_set_live(struct mpath_device *mpath_device) } EXPORT_SYMBOL_GPL(mpath_device_set_live); +void mpath_add_sysfs_link(struct mpath_head *mpath_head) +{ + struct device *target; + struct device *source; + int rc, srcu_idx; + struct kobject *mpath_gd_kobj; + struct mpath_device *mpath_device; + + /* + * Ensure head disk node is already added otherwise we may get invalid + * kobj for head disk node + */ + if (!test_bit(GD_ADDED, &mpath_head->disk->state)) + return; + + mpath_gd_kobj = &disk_to_dev(mpath_head->disk)->kobj; + srcu_idx = srcu_read_lock(&mpath_head->srcu); + + list_for_each_entry_srcu(mpath_device, &mpath_head->dev_list, siblings, + srcu_read_lock_held(&mpath_head->srcu)) { + if (!test_bit(GD_ADDED, &mpath_device->disk->state)) + continue; + + if (test_and_set_bit(MPATH_DEVICE_SYSFS_ATTR_LINK, + &mpath_device->flags)) + continue; + + target = disk_to_dev(mpath_device->disk); + source = disk_to_dev(mpath_head->disk); + /* + * Create sysfs link from head gendisk kobject @kobj to the + * ns path gendisk kobject @target->kobj. + */ + rc = sysfs_add_link_to_group(mpath_gd_kobj, "multipath", + &target->kobj, dev_name(target)); + + if (unlikely(rc)) { + dev_err(disk_to_dev(mpath_head->disk), + "failed to create link to %s rc=%d\n", + dev_name(target), rc); + clear_bit(MPATH_DEVICE_SYSFS_ATTR_LINK, + &mpath_device->flags); + } else { + dev_info(source, "Created multipath sysfs link to %s\n", + mpath_device->disk->disk_name); + } + } + + srcu_read_unlock(&mpath_head->srcu, srcu_idx); +} +EXPORT_SYMBOL_GPL(mpath_add_sysfs_link); + +void mpath_remove_sysfs_link(struct mpath_device *mpath_device) +{ + struct device *target; + struct kobject *mpath_gd_kobj; + struct mpath_head *mpath_head = mpath_device->mpath_head; + + if (!test_bit(MPATH_DEVICE_SYSFS_ATTR_LINK, &mpath_device->flags)) + return; + + target = disk_to_dev(mpath_device->disk); + mpath_gd_kobj = &disk_to_dev(mpath_head->disk)->kobj; + + sysfs_remove_link_from_group(mpath_gd_kobj, "multipath", + dev_name(target)); + + clear_bit(MPATH_DEVICE_SYSFS_ATTR_LINK, &mpath_device->flags); +} +EXPORT_SYMBOL_GPL(mpath_remove_sysfs_link); + int mpath_head_init(struct mpath_head *mpath_head) { memset(mpath_head, 0, sizeof(*mpath_head)); -- 2.43.0