Add helpers for driver sysfs code for the following functionality: - get/set iopolicy with mpath_iopolicy_store() and mpath_iopolicy_show() - show device path per NUMA node - "multipath" attribute group, equivalent to nvme_ns_mpath_attr_group - device groups attribute array, similar to nvme_ns_attr_groups but not containing NVMe members.
Note that mpath_iopolicy_store() has a update callback to allow same functionality as nvme_subsys_iopolicy_update() be run for clearing paths. Signed-off-by: John Garry <[email protected]> --- include/linux/multipath.h | 9 ++++ lib/multipath.c | 110 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/include/linux/multipath.h b/include/linux/multipath.h index f7998de261899..9122560f71778 100644 --- a/include/linux/multipath.h +++ b/include/linux/multipath.h @@ -10,6 +10,8 @@ extern const struct file_operations mpath_generic_chr_fops; extern const struct block_device_operations mpath_ops; +extern const struct attribute_group mpath_attr_group; +extern const struct attribute_group *mpath_device_groups[]; enum mpath_iopolicy_e { MPATH_IOPOLICY_NUMA, @@ -145,6 +147,13 @@ struct mpath_disk *mpath_alloc_head_disk(struct queue_limits *lim, void mpath_device_set_live(struct mpath_disk *mpath_disk, struct mpath_device *mpath_device); void mpath_unregister_disk(struct mpath_disk *mpath_disk); +ssize_t mpath_numa_nodes_show(struct mpath_head *mpath_head, + struct mpath_device *mpath_device, + struct mpath_iopolicy *iopolicy, char *buf); +ssize_t mpath_iopolicy_show(struct mpath_iopolicy *mpath_iopolicy, char *buf); +ssize_t mpath_iopolicy_store(struct mpath_iopolicy *mpath_iopolicy, + const char *buf, size_t count, + void (*update)(void *data), void *); ssize_t mpath_delayed_removal_secs_show(struct mpath_head *mpath_head, char *buf); ssize_t mpath_delayed_removal_secs_store(struct mpath_head *mpath_head, diff --git a/lib/multipath.c b/lib/multipath.c index 1ce57b9b14d2e..c05b4d25ca223 100644 --- a/lib/multipath.c +++ b/lib/multipath.c @@ -745,6 +745,116 @@ void mpath_device_set_live(struct mpath_disk *mpath_disk, } EXPORT_SYMBOL_GPL(mpath_device_set_live); +static struct attribute dummy_attr = { + .name = "dummy", +}; + +static struct attribute *mpath_attrs[] = { + &dummy_attr, + NULL +}; + +static bool multipath_sysfs_group_visible(struct kobject *kobj) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gendisk *disk = dev_to_disk(dev); + + return is_mpath_head(disk); +} + +static bool multipath_sysfs_attr_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + return false; +} + +DEFINE_SYSFS_GROUP_VISIBLE(multipath_sysfs) + +const struct attribute_group mpath_attr_group = { + .name = "multipath", + .attrs = mpath_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(multipath_sysfs), +}; +EXPORT_SYMBOL_GPL(mpath_attr_group); + +const struct attribute_group *mpath_device_groups[] = { + &mpath_attr_group, + NULL +}; +EXPORT_SYMBOL_GPL(mpath_device_groups); + +ssize_t mpath_iopolicy_show(struct mpath_iopolicy *mpath_iopolicy, char *buf) +{ + return sysfs_emit(buf, "%s\n", + mpath_iopolicy_names[mpath_read_iopolicy(mpath_iopolicy)]); +} +EXPORT_SYMBOL_GPL(mpath_iopolicy_show); + +static void mpath_iopolicy_update(struct mpath_iopolicy *mpath_iopolicy, + int iopolicy, void (*update)(void *), void *data) +{ + int old_iopolicy = READ_ONCE(mpath_iopolicy->iopolicy); + + if (old_iopolicy == iopolicy) + return; + + WRITE_ONCE(mpath_iopolicy->iopolicy, iopolicy); + + /* + * iopolicy changes clear the mpath by design, which @update + * must do. + */ + update(data); + + pr_err("iopolicy changed from %s to %s\n", + mpath_iopolicy_names[old_iopolicy], + mpath_iopolicy_names[iopolicy]); +} + +ssize_t mpath_iopolicy_store(struct mpath_iopolicy *mpath_iopolicy, + const char *buf, size_t count, + void (*update)(void *), void *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mpath_iopolicy_names); i++) { + if (sysfs_streq(buf, mpath_iopolicy_names[i])) { + mpath_iopolicy_update(mpath_iopolicy, i, update, data); + return count; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(mpath_iopolicy_store); + +ssize_t mpath_numa_nodes_show(struct mpath_head *mpath_head, + struct mpath_device *mpath_device, + struct mpath_iopolicy *mpath_iopolicy, char *buf) +{ + int node, srcu_idx; + nodemask_t numa_nodes; + struct mpath_device *current_mpath_dev; + + if (mpath_read_iopolicy(mpath_iopolicy) != MPATH_IOPOLICY_NUMA) + return 0; + + nodes_clear(numa_nodes); + + srcu_idx = srcu_read_lock(&mpath_head->srcu); + for_each_node(node) { + current_mpath_dev = + srcu_dereference(mpath_head->current_path[node], + &mpath_head->srcu); + if (current_mpath_dev == mpath_device) + node_set(node, numa_nodes); + } + srcu_read_unlock(&mpath_head->srcu, srcu_idx); + + return sysfs_emit(buf, "%*pbl\n", nodemask_pr_args(&numa_nodes)); +} +EXPORT_SYMBOL_GPL(mpath_numa_nodes_show); + ssize_t mpath_delayed_removal_secs_show(struct mpath_head *mpath_head, char *buf) { -- 2.43.5

