Add initial framework for libmultipath. libmultipath is a library for multipath-capable block drivers, such as NVMe. The main function is to support path management, path selection, and failover handling.
Basic support to add and remove the head structure - mpath_head - is included. This main purpose of this structure is to manage available paths and path selection. It is quite similar to the multipath functionality in nvme_ns_head. However a separate structure will introduced after to manage the multipath gendisk. Each path is represented by the mpath_device structure. It should hold a pointer to the per-path gendisk and also a list element for all siblings of paths. For NVMe, there would be a mpath_device per nvme_ns. All the libmultipath code is more or less taken from drivers/nvme/host/multipath.c, which was originally authored by Christoph Hellwig <[email protected]>. Signed-off-by: John Garry <[email protected]> --- include/linux/multipath.h | 28 +++++++++++++++ lib/Kconfig | 6 ++++ lib/Makefile | 2 ++ lib/multipath.c | 74 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 include/linux/multipath.h create mode 100644 lib/multipath.c diff --git a/include/linux/multipath.h b/include/linux/multipath.h new file mode 100644 index 0000000000000..18cd133b7ca21 --- /dev/null +++ b/include/linux/multipath.h @@ -0,0 +1,28 @@ + +#ifndef _LIBMULTIPATH_H +#define _LIBMULTIPATH_H + +#include <linux/blkdev.h> +#include <linux/srcu.h> + +struct mpath_device { + struct list_head siblings; + struct gendisk *disk; +}; + +struct mpath_head { + struct srcu_struct srcu; + struct list_head dev_list; /* list of all mpath_devs */ + struct mutex lock; + + struct kref ref; + + struct mpath_device __rcu *current_path[MAX_NUMNODES]; + void *drvdata; +}; + +int mpath_get_head(struct mpath_head *mpath_head); +void mpath_put_head(struct mpath_head *mpath_head); +struct mpath_head *mpath_alloc_head(void); + +#endif // _LIBMULTIPATH_H diff --git a/lib/Kconfig b/lib/Kconfig index 2923924bea78c..465aed2477d90 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -649,3 +649,9 @@ config UNION_FIND config MIN_HEAP bool + +config LIBMULTIPATH + bool "MULTIPATH BLOCK DRIVER LIBRARY" + depends on BLOCK + help + If you say yes here then you get a multipath lib for block drivers diff --git a/lib/Makefile b/lib/Makefile index aaf677cf4527e..b81002bc64d2e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -332,3 +332,5 @@ obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o obj-$(CONFIG_FIRMWARE_TABLE) += fw_table.o subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify + +obj-$(CONFIG_LIBMULTIPATH) += multipath.o diff --git a/lib/multipath.c b/lib/multipath.c new file mode 100644 index 0000000000000..15c495675d729 --- /dev/null +++ b/lib/multipath.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018 Christoph Hellwig. + * Copyright (c) 2026 Oracle and/or its affiliates. + */ +#include <linux/module.h> +#include <linux/multipath.h> + +static struct workqueue_struct *mpath_wq; + +static void mpath_free_head(struct kref *ref) +{ + struct mpath_head *mpath_head = + container_of(ref, struct mpath_head, ref); + + cleanup_srcu_struct(&mpath_head->srcu); + kfree(mpath_head); +} + +int mpath_get_head(struct mpath_head *mpath_head) +{ + if (!kref_get_unless_zero(&mpath_head->ref)) { + return -ENXIO; + } + return 0; +} +EXPORT_SYMBOL_GPL(mpath_get_head); + +void mpath_put_head(struct mpath_head *mpath_head) +{ + kref_put(&mpath_head->ref, mpath_free_head); +} +EXPORT_SYMBOL_GPL(mpath_put_head); + +struct mpath_head *mpath_alloc_head(void) +{ + struct mpath_head *mpath_head; + int ret; + + mpath_head = kzalloc(sizeof(*mpath_head), GFP_KERNEL); + if (!mpath_head) + return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&mpath_head->dev_list); + mutex_init(&mpath_head->lock); + kref_init(&mpath_head->ref); + + ret = init_srcu_struct(&mpath_head->srcu); + if (ret) { + kfree(mpath_head); + return ERR_PTR(ret); + } + + return mpath_head; +} +EXPORT_SYMBOL_GPL(mpath_alloc_head); + +static int __init mpath_init(void) +{ + mpath_wq = alloc_workqueue("mpath-wq", + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0); + if (!mpath_wq) + return -ENOMEM; + return 0; +} + +static void __exit mpath_exit(void) +{ + destroy_workqueue(mpath_wq); +} + +module_init(mpath_init); +module_exit(mpath_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("libmultipath"); -- 2.43.5

