From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 07C5C606A322 for ; Sun, 8 Nov 2026 20:48:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=kzZkBbB+; dkim=pass (1024-bit key, unprotected) header.d=linux.dev header.i=@linux.dev header.a=rsa-sha256 header.s=key1 header.b=CFygRJAl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From: Reply-To:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Unsubscribe-Post: List-Owner; bh=CvZz2HyxVQKF76MfuoDT0u/mT412IESznH8O8dXqMKc=; b=kzZkBbB+V97ACV DKXl1TZx2lh5gqkY1416SxVoSU9S3rgch0HOd9/zzlJv+AG9ayzstkvSlzNe3RqAKEbUUPnKtAgf6 QsBWMsunTJqi+EgdqsssX89NXWcEByBX9casCVAvq/6wCzJsc4rAq6GoblVv32G/mq+XlDlZ49/vx ZJWcfy13O7lE7g3JPVyGgClxtnn42nPoVAp+jxcwDRQ+mBGP7FfC5ryxddsiZdfc8Ue/LNGpzqpFM huxyLQozbKMwvYCrXvEUFEUJYlvPgrR4oUii0EukhAj0aLhtp8Bi5eqbQ4WAFBsXAAxnmUL94LdLK vl1a+mefsw4G2jrMeyeQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.100.1 #2 (Red Hat Linux)) id 1xQ9o5-0000000GboA-2ctO; Sun, 08 Nov 2026 20:48:05 +0000 Received: from out-44.mta1.migadu.com ([95.215.58.44] helo=mta1.migadu.com) by bombadil.infradead.org with esmtps (Exim 4.100.1 #2 (Red Hat Linux)) id 1xQ9o3-0000000Gbjg-1I1z for linux-nvme@lists.infradead.org; Sun, 08 Nov 2026 20:48:04 +0000 X-Envelope-To: linux-nvme@lists.infradead.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-nvme@lists.infradead.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-Archived-By: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20261108_124803_526852_A0F3B6FB X-CRM114-Status: GOOD ( 13.99 ) X-Spam-Score: -2.1 (--) X-BeenThere: linux-nvme@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-nvme" X-Spam-Status: No, score=-18.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 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