[RFC][PATCH] device mapper: Add builtin function dm_get_status()

Roberto Sassu
2021-12-01T16:37:57Z

Users of the device mapper driver might want to obtain a device status,
with status types defined in the status_type_t enumerator.

If a function to get the status is exported by the device mapper, when
compiled as a module, it is not suitable to use by callers compiled builtin
in the kernel.

Introduce the real function to get the status, _dm_get_status(), in the
device mapper module, and add the stub dm_get_status() in dm-builtin.c, so
that it can be invoked by builtin callers.

The stub calls the real function if the device mapper is compiled builtin
or the module has been loaded. Calls to the real function are safely
disabled if the module is unloaded. The race condition is avoided by
incrementing the reference count of the module.

_dm_get_status() invokes the status() method for each device mapper table,
which writes a string to the supplied buffer as output. The buffer might
contain multiple strings concatenated together. If there is not enough
space available, the string is truncated and a termination character is put
at the end.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
drivers/md/dm-builtin.c | 13 +++++++
drivers/md/dm-core.h | 5 +++
drivers/md/dm.c | 71 +++++++++++++++++++++++++++++++++++
include/linux/device-mapper.h | 3 ++
4 files changed, 92 insertions(+)

diff --git a/drivers/md/dm-builtin.c b/drivers/md/dm-builtin.c
index 8eb52e425141..cc1e9c27ab41 100644
--- a/drivers/md/dm-builtin.c
+++ b/drivers/md/dm-builtin.c
@@ -47,3 +47,16 @@ void dm_kobject_release(struct kobject *kobj)
}

EXPORT_SYMBOL(dm_kobject_release);
+
+dm_get_status_fn status_fn;
+EXPORT_SYMBOL(status_fn);
+
+ssize_t dm_get_status(dev_t dev, status_type_t type, const char *target_name,
+ u8 *buf, size_t buf_len)
+{
+ if (status_fn)
+ return status_fn(dev, type, target_name, buf, buf_len);
+
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(dm_get_status);
diff --git a/drivers/md/dm-core.h b/drivers/md/dm-core.h
index b855fef4f38a..6600ec260558 100644
--- a/drivers/md/dm-core.h
+++ b/drivers/md/dm-core.h
@@ -259,4 +259,9 @@ extern atomic_t dm_global_event_nr;
extern wait_queue_head_t dm_global_eventq;
void dm_issue_global_event(void);

+typedef ssize_t (*dm_get_status_fn)(dev_t dev, status_type_t type,
+ const char *target_name, u8 *buf,
+ size_t buf_len);
+
+extern dm_get_status_fn status_fn;
#endif
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 662742a310cb..55e59a4e3661 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -192,6 +192,74 @@ static unsigned dm_get_numa_node(void)
DM_NUMA_NODE, num_online_nodes() - 1);
}

+static ssize_t _dm_get_status(dev_t dev, status_type_t type,
+ const char *target_name, u8 *buf, size_t buf_len)
+{
+ struct mapped_device *md;
+ struct dm_table *table;
+ u8 *buf_ptr = buf;
+ ssize_t len, res = 0;
+ int srcu_idx, num_targets, i;
+
+ if (buf_len > INT_MAX)
+ return -EINVAL;
+
+ if (!buf_len)
+ return buf_len;
+
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+
+ md = dm_get_md(dev);
+ if (!md) {
+ res = -ENOENT;
+ goto out_module;
+ }
+
+ table = dm_get_live_table(md, &srcu_idx);
+ if (!table) {
+ res = -ENOENT;
+ goto out_md;
+ }
+
+ memset(buf, 0, buf_len);
+
+ num_targets = dm_table_get_num_targets(table);
+
+ for (i = 0; i < num_targets; i++) {
+ struct dm_target *ti = dm_table_get_target(table, i);
+
+ if (!ti)
+ continue;
+
+ if (target_name && strcmp(ti->type->name, target_name))
+ continue;
+
+ if (!ti->type->status)
+ continue;
+
+ ti->type->status(ti, type, 0, buf_ptr, buf + buf_len - buf_ptr);
+
+ if (!*buf_ptr)
+ continue;
+
+ len = strlen(buf_ptr);
+ buf_ptr += len + 1;
+
+ if (buf_ptr == buf + buf_len)
+ break;
+
+ res += len + 1;
+ }
+
+ dm_put_live_table(md, srcu_idx);
+out_md:
+ dm_put(md);
+out_module:
+ module_put(THIS_MODULE);
+ return res;
+}
+
static int __init local_init(void)
{
int r;
@@ -275,6 +343,7 @@ static int __init dm_init(void)
goto bad;
}

+ status_fn = _dm_get_status;
return 0;
bad:
while (i--)
@@ -287,6 +356,8 @@ static void __exit dm_exit(void)
{
int i = ARRAY_SIZE(_exits);

+ status_fn = NULL;
+
while (i--)
_exits[i]();

diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index a7df155ea49b..d97b296d3104 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -487,6 +487,9 @@ int dm_report_zones(struct block_device *bdev, sector_t start, sector_t sector,
struct dm_report_zones_args *args, unsigned int nr_zones);
#endif /* CONFIG_BLK_DEV_ZONED */

+ssize_t dm_get_status(dev_t dev, status_type_t type, const char *target_name,
+ u8 *buf, size_t buf_len);
+
/*
* Device mapper functions to parse and create devices specified by the
* parameter "dm-mod.create="
--
2.32.0