summaryrefslogtreecommitdiffstats
path: root/drivers/block/loop/loop_file_fmt.c
diff options
context:
space:
mode:
authorManuel Bentele2019-07-07 23:14:46 +0200
committerManuel Bentele2019-08-21 22:01:19 +0200
commita2955811ca1c053fefa1dda8032532343e6e50ef (patch)
tree4336d1d6a333de997f7009fcba5a0c8efe8569ab /drivers/block/loop/loop_file_fmt.c
parentblock: loop: rename and move files for file format support integration (diff)
downloadkernel-qcow2-linux-a2955811ca1c053fefa1dda8032532343e6e50ef.tar.gz
kernel-qcow2-linux-a2955811ca1c053fefa1dda8032532343e6e50ef.tar.xz
kernel-qcow2-linux-a2955811ca1c053fefa1dda8032532343e6e50ef.zip
block: loop: add file format subsystem for loop devices
The loop device is extended by a file format subsystem to allow the implementation of various disk file formats. The file format drivers are implemented as own kernel modules and registered by the subsystem. The subsystem takes control over the specified file format at loop creation and calls the corresponding file format driver functions. At the moment, the file format subsystem can handle ... - read - write - discard - flush ... operations of the loop block device. To implement any disk file format in the future, the interface of the subsystem must be extended by a size and capacity functionality. This is especially necessary for sparse disk file formats. Signed-off-by: Manuel Bentele <development@manuel-bentele.de>
Diffstat (limited to 'drivers/block/loop/loop_file_fmt.c')
-rw-r--r--drivers/block/loop/loop_file_fmt.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/drivers/block/loop/loop_file_fmt.c b/drivers/block/loop/loop_file_fmt.c
new file mode 100644
index 000000000000..f29c15ed044b
--- /dev/null
+++ b/drivers/block/loop/loop_file_fmt.c
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * loop_file_fmt.c
+ *
+ * File format subsystem for the loop device module.
+ *
+ * Copyright (C) 2019 Manuel Bentele
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "loop_file_fmt.h"
+
+/* storage for all registered file format drivers */
+static struct loop_file_fmt_driver *loop_file_fmt_drivers[MAX_LO_FILE_FMT] = {
+ NULL
+};
+
+int loop_file_fmt_register_driver(struct loop_file_fmt_driver *drv)
+{
+ int ret = 0;
+
+ if (drv == NULL)
+ return -EFAULT;
+
+ if (drv->file_fmt_type > MAX_LO_FILE_FMT)
+ return -EINVAL;
+
+ if (loop_file_fmt_drivers[drv->file_fmt_type] == NULL) {
+ loop_file_fmt_drivers[drv->file_fmt_type] = drv;
+ printk(KERN_INFO "loop: successfully registered file format "
+ "driver %s\n", drv->name);
+ } else {
+ printk(KERN_WARNING "loop: driver for file format already "
+ "registered\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(loop_file_fmt_register_driver);
+
+void loop_file_fmt_unregister_driver(struct loop_file_fmt_driver *drv)
+{
+ if (drv == NULL)
+ return;
+
+ if (drv->file_fmt_type > MAX_LO_FILE_FMT)
+ return;
+
+ loop_file_fmt_drivers[drv->file_fmt_type] = NULL;
+ printk(KERN_INFO "loop: successfully unregistered file format driver "
+ "%s\n", drv->name);
+}
+EXPORT_SYMBOL(loop_file_fmt_unregister_driver);
+
+struct loop_file_fmt *loop_file_fmt_alloc(void)
+{
+ return kzalloc(sizeof(struct loop_file_fmt), GFP_KERNEL);
+}
+
+void loop_file_fmt_free(struct loop_file_fmt *lo_fmt)
+{
+ kfree(lo_fmt);
+}
+
+int loop_file_fmt_set_lo(struct loop_file_fmt *lo_fmt, struct loop_device *lo)
+{
+ if (lo_fmt == NULL)
+ return -EINVAL;
+
+ lo_fmt->lo = lo;
+
+ return 0;
+}
+EXPORT_SYMBOL(loop_file_fmt_set_lo);
+
+struct loop_device *loop_file_fmt_get_lo(struct loop_file_fmt *lo_fmt)
+{
+ return lo_fmt->lo;
+}
+EXPORT_SYMBOL(loop_file_fmt_get_lo);
+
+int loop_file_fmt_init(struct loop_file_fmt *lo_fmt)
+{
+ struct loop_file_fmt_ops* ops;
+ struct module *drv;
+
+ if (lo_fmt->file_fmt_type > MAX_LO_FILE_FMT)
+ return -EINVAL;
+
+ /* check if new file format driver is registered */
+ if (loop_file_fmt_drivers[lo_fmt->file_fmt_type] == NULL) {
+ printk(KERN_ERR "loop_file_fmt: file format driver is not "
+ "available\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "loop_file_fmt: use file format driver %s\n",
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->name);
+
+ drv = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->owner;
+ if (!try_module_get(drv)) {
+ printk(KERN_ERR "loop_file_fmt: file format driver %s can not "
+ "be accessed\n",
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->name);
+ return -ENODEV;
+ }
+
+ ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+ if (likely(ops->init))
+ return ops->init(lo_fmt);
+ else
+ return -ENOSYS;
+}
+
+void loop_file_fmt_exit(struct loop_file_fmt *lo_fmt)
+{
+ struct loop_file_fmt_ops* ops;
+ struct module *drv;
+
+ ops = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+ if (likely(ops->exit))
+ ops->exit(lo_fmt);
+
+ drv = loop_file_fmt_drivers[lo_fmt->file_fmt_type]->owner;
+ module_put(drv);
+}
+
+int loop_file_fmt_read(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->read))
+ return ops->read(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_read_aio(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->read_aio))
+ return ops->read_aio(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_write(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->write))
+ return ops->write(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_write_aio(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->write_aio))
+ return ops->write_aio(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_discard(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->discard))
+ return ops->discard(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_flush(struct loop_file_fmt *lo_fmt,
+ struct request *rq)
+{
+ struct loop_file_fmt_ops* ops =
+ loop_file_fmt_drivers[lo_fmt->file_fmt_type]->ops;
+
+ if (likely(ops->flush))
+ return ops->flush(lo_fmt, rq);
+ else
+ return -ENOSYS;
+}
+
+int loop_file_fmt_change(struct loop_file_fmt *lo_fmt,
+ __u32 file_fmt_type_new)
+{
+ if (file_fmt_type_new > MAX_LO_FILE_FMT)
+ return -EINVAL;
+
+ /*
+ * Unload the old file format driver and after that,
+ * load the new driver
+ */
+ loop_file_fmt_exit(lo_fmt);
+ lo_fmt->file_fmt_type = file_fmt_type_new;
+ printk(KERN_ERR "loop: change file format driver");
+ return loop_file_fmt_init(lo_fmt);
+}
+
+ssize_t loop_file_fmt_print_type(__u32 file_fmt_type, char *file_fmt_name)
+{
+ ssize_t len = 0;
+
+ switch(file_fmt_type) {
+ case LO_FILE_FMT_RAW:
+ len = sprintf(file_fmt_name, "%s", "RAW");
+ break;
+ case LO_FILE_FMT_QCOW:
+ len = sprintf(file_fmt_name, "%s", "QCOW");
+ break;
+ case LO_FILE_FMT_VDI:
+ len = sprintf(file_fmt_name, "%s", "VDI");
+ break;
+ case LO_FILE_FMT_VMDK:
+ len = sprintf(file_fmt_name, "%s", "VMDK");
+ break;
+ default:
+ len = sprintf(file_fmt_name, "%s", "ERROR: Unsupported loop "
+ "file format!");
+ break;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL(loop_file_fmt_print_type);