summaryrefslogtreecommitdiffstats
path: root/src/kernel/xloop_file_fmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/xloop_file_fmt.c')
-rw-r--r--src/kernel/xloop_file_fmt.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/src/kernel/xloop_file_fmt.c b/src/kernel/xloop_file_fmt.c
new file mode 100644
index 0000000..6c4902f
--- /dev/null
+++ b/src/kernel/xloop_file_fmt.c
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * xloop_file_fmt.c
+ *
+ * File format subsystem for the xloop device module.
+ *
+ * Copyright (C) 2019 Manuel Bentele <development@manuel-bentele.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "xloop_file_fmt.h"
+#include "xloop_main.h"
+
+/* storage for all registered file format drivers */
+static struct xloop_file_fmt_driver *xloop_file_fmt_drivers[MAX_XLO_FILE_FMT] = {
+ NULL
+};
+
+int xloop_file_fmt_register_driver(struct xloop_file_fmt_driver *drv)
+{
+ int ret = 0;
+
+ if (drv == NULL)
+ return -EFAULT;
+
+ if (drv->file_fmt_type > MAX_XLO_FILE_FMT)
+ return -EINVAL;
+
+ if (xloop_file_fmt_drivers[drv->file_fmt_type] == NULL) {
+ xloop_file_fmt_drivers[drv->file_fmt_type] = drv;
+ pr_info("successfully registered file format driver %s\n", drv->name);
+ } else {
+ pr_warn("driver for file format already registered\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(xloop_file_fmt_register_driver);
+
+void xloop_file_fmt_unregister_driver(struct xloop_file_fmt_driver *drv)
+{
+ if (drv == NULL)
+ return;
+
+ if (drv->file_fmt_type > MAX_XLO_FILE_FMT)
+ return;
+
+ xloop_file_fmt_drivers[drv->file_fmt_type] = NULL;
+ pr_info("successfully unregistered file format driver %s\n", drv->name);
+}
+EXPORT_SYMBOL(xloop_file_fmt_unregister_driver);
+
+struct xloop_file_fmt *xloop_file_fmt_alloc(void)
+{
+ return kzalloc(sizeof(struct xloop_file_fmt), GFP_KERNEL);
+}
+
+void xloop_file_fmt_free(struct xloop_file_fmt *xlo_fmt)
+{
+ kfree(xlo_fmt);
+}
+
+int xloop_file_fmt_set_xlo(struct xloop_file_fmt *xlo_fmt, struct xloop_device *xlo)
+{
+ if (xlo_fmt == NULL)
+ return -EINVAL;
+
+ xlo_fmt->xlo = xlo;
+
+ return 0;
+}
+EXPORT_SYMBOL(xloop_file_fmt_set_xlo);
+
+struct xloop_device *xloop_file_fmt_get_xlo(struct xloop_file_fmt *xlo_fmt)
+{
+ return xlo_fmt->xlo;
+}
+EXPORT_SYMBOL(xloop_file_fmt_get_xlo);
+
+struct device *xloop_file_fmt_to_dev(struct xloop_file_fmt *xlo_fmt)
+{
+ struct xloop_device *xlo = xloop_file_fmt_get_xlo(xlo_fmt);
+ return xloop_device_to_dev(xlo);
+}
+EXPORT_SYMBOL(xloop_file_fmt_to_dev);
+
+int xloop_file_fmt_init(struct xloop_file_fmt *xlo_fmt,
+ u32 file_fmt_type)
+{
+ struct xloop_file_fmt_ops *ops;
+ struct module *drv;
+ int ret = 0;
+
+ if (file_fmt_type > MAX_XLO_FILE_FMT)
+ return -EINVAL;
+
+ xlo_fmt->file_fmt_type = file_fmt_type;
+
+ if (xlo_fmt->file_fmt_state != file_fmt_uninitialized) {
+ dev_warn(xloop_file_fmt_to_dev(xlo_fmt), "file format is "
+ "initialized already\n");
+ return -EINVAL;
+ }
+
+ /* check if new file format driver is registered */
+ if (xloop_file_fmt_drivers[xlo_fmt->file_fmt_type] == NULL) {
+ dev_err(xloop_file_fmt_to_dev(xlo_fmt), "file format driver is "
+ "not available\n");
+ return -ENODEV;
+ }
+
+ dev_info(xloop_file_fmt_to_dev(xlo_fmt), "use file format driver %s\n",
+ xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->name);
+
+ drv = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->owner;
+ if (!try_module_get(drv)) {
+ dev_err(xloop_file_fmt_to_dev(xlo_fmt), "file format driver %s can "
+ "not be accessed\n",
+ xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->name);
+ return -ENODEV;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->init)) {
+ ret = ops->init(xlo_fmt);
+ if (ret < 0)
+ goto free_drv;
+ }
+
+ /* after increasing the refcount of file format driver module and
+ * the successful initialization, the file format is initialized */
+ xlo_fmt->file_fmt_state = file_fmt_initialized;
+
+ return ret;
+
+free_drv:
+ module_put(drv);
+ xlo_fmt->file_fmt_state = file_fmt_uninitialized;
+ return ret;
+}
+
+void xloop_file_fmt_exit(struct xloop_file_fmt *xlo_fmt)
+{
+ struct xloop_file_fmt_ops *ops;
+ struct module *drv;
+
+ if (xlo_fmt->file_fmt_state != file_fmt_initialized) {
+ dev_warn(xloop_file_fmt_to_dev(xlo_fmt), "file format is "
+ "uninitialized already\n");
+ return;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->exit))
+ ops->exit(xlo_fmt);
+
+ drv = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->owner;
+ module_put(drv);
+
+ /* after decreasing the refcount of file format driver module,
+ * the file format is uninitialized */
+ xlo_fmt->file_fmt_state = file_fmt_uninitialized;
+}
+
+int xloop_file_fmt_read(struct xloop_file_fmt *xlo_fmt,
+ struct request *rq)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not read\n");
+ return -EINVAL;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->read))
+ return ops->read(xlo_fmt, rq);
+ else
+ return -EIO;
+}
+
+int xloop_file_fmt_read_aio(struct xloop_file_fmt *xlo_fmt,
+ struct request *rq)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not read aio\n");
+ return -EINVAL;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->read_aio))
+ return ops->read_aio(xlo_fmt, rq);
+ else
+ return -EIO;
+}
+
+int xloop_file_fmt_write(struct xloop_file_fmt *xlo_fmt,
+ struct request *rq)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not write\n");
+ return -EINVAL;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->write))
+ return ops->write(xlo_fmt, rq);
+ else
+ return -EIO;
+}
+
+int xloop_file_fmt_write_aio(struct xloop_file_fmt *xlo_fmt,
+ struct request *rq)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not write aio\n");
+ return -EINVAL;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->write_aio))
+ return ops->write_aio(xlo_fmt, rq);
+ else
+ return -EIO;
+}
+
+int xloop_file_fmt_write_zeros(struct xloop_file_fmt *xlo_fmt,
+ struct request *rq)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not write zeros\n");
+ return -EINVAL;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->write_zeros))
+ return ops->write_zeros(xlo_fmt, rq);
+ else
+ return -EIO;
+}
+
+int xloop_file_fmt_discard(struct xloop_file_fmt *xlo_fmt,
+ struct request *rq)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not discard\n");
+ return -EINVAL;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->discard))
+ return ops->discard(xlo_fmt, rq);
+ else
+ return -EIO;
+}
+
+int xloop_file_fmt_flush(struct xloop_file_fmt *xlo_fmt)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not flush\n");
+ return -EINVAL;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->flush))
+ return ops->flush(xlo_fmt);
+
+ return 0;
+}
+
+loff_t xloop_file_fmt_sector_size(struct xloop_file_fmt *xlo_fmt,
+ struct file *file, loff_t offset, loff_t sizelimit)
+{
+ struct xloop_file_fmt_ops *ops;
+
+ if (unlikely(xlo_fmt->file_fmt_state != file_fmt_initialized)) {
+ dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "file format "
+ "is not initialized, can not read sector size\n");
+ return 0;
+ }
+
+ ops = xloop_file_fmt_drivers[xlo_fmt->file_fmt_type]->ops;
+ if (likely(ops->sector_size))
+ return ops->sector_size(xlo_fmt, file, offset, sizelimit);
+ else
+ return 0;
+}
+
+int xloop_file_fmt_change(struct xloop_file_fmt *xlo_fmt,
+ u32 file_fmt_type_new)
+{
+ if (file_fmt_type_new > MAX_XLO_FILE_FMT)
+ return -EINVAL;
+
+ dev_info(xloop_file_fmt_to_dev(xlo_fmt), "change file format\n");
+
+ /* Unload the old file format driver if the file format is
+ * initialized */
+ if (xlo_fmt->file_fmt_state == file_fmt_initialized)
+ xloop_file_fmt_exit(xlo_fmt);
+
+ /* Load the new file format driver because the file format is
+ * uninitialized now */
+ return xloop_file_fmt_init(xlo_fmt, file_fmt_type_new);
+}
+
+ssize_t xloop_file_fmt_print_type(u32 file_fmt_type, char *file_fmt_name)
+{
+ ssize_t len = 0;
+
+ switch (file_fmt_type) {
+ case XLO_FILE_FMT_RAW:
+ len = sprintf(file_fmt_name, "%s", "RAW");
+ break;
+ case XLO_FILE_FMT_QCOW:
+ len = sprintf(file_fmt_name, "%s", "QCOW");
+ break;
+ case XLO_FILE_FMT_VDI:
+ len = sprintf(file_fmt_name, "%s", "VDI");
+ break;
+ case XLO_FILE_FMT_VMDK:
+ len = sprintf(file_fmt_name, "%s", "VMDK");
+ break;
+ default:
+ len = sprintf(file_fmt_name, "%s", "ERROR: Unsupported xloop "
+ "file format!");
+ break;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL(xloop_file_fmt_print_type);