Discussion:
[PATCH] UBI: new module ubiblk: block layer on top of UBI
(too old to reply)
d***@free-electrons.com
2011-06-24 13:40:02 UTC
Permalink
From: David Wagner <***@free-electrons.com>

ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices.

It is used by putting a block filesystem image on a UBI volume and then mounting
the corresponding device.

It uses the UBI API to register to UBI notifications (to dynamically create and
delete devices as volumes are added or removed) and to read from the volumes.

Known issues:
* UBI_VOLUME_RESIZED notification hook isn't implemented yet ;

* only squashfs is known to work. With ext2/3 and vfat, errors appear randomly
in the middle of some files. The reason for that is still unknown ;

* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. It should make use of a linked list.

A lot of code is taken from mtd_blkdevs and gluebi

Signed-off-by: David Wagner <***@free-electrons.com>
---
drivers/mtd/ubi/Kconfig | 9 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 462 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 472 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c

diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..389a996 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,13 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ Creates block devices that can be used to mount read-only block
+ filesystems. Caches reads on a raw UBI volume. Filesystems images
+ can be flashed using ubiupdatevol or put in a UBI image with
+ ubinize
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..aab940a
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+#define UBIBLK_MAX_DEVS (UBI_MAX_DEVICES * UBI_MAX_VOLUMES)
+
+/*
+ * Structure representing a ubiblk device, proxying a UBI volume
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *vol_desc;
+ struct ubi_volume_info *vol_info;
+ int ubi_num;
+ int vol_id;
+
+ /* Block stuff */
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *thread;
+
+ /* Protects the access to the UBI volume */
+ struct mutex lock;
+
+ /* Avoids concurrent accesses to the request queue */
+ spinlock_t queue_lock;
+};
+
+/*
+ * Contains the pointers to all ubiblk_dev instances
+ * TODO: use a linked list
+ */
+static struct ubiblk_dev *ubiblk_devs[UBIBLK_MAX_DEVS];
+static struct mutex devtable_lock;
+
+int major;
+static const struct block_device_operations ubiblk_ops;
+
+/*
+ * Read a LEB and fill the request buffer with the requested sector
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vol_info->usable_leb_size;
+ offset = start % dev->vol_info->usable_leb_size;
+
+ if (offset + len > dev->vol_info->usable_leb_size)
+ len = dev->vol_info->usable_leb_size - offset;
+
+ pr_debug("%s(%s) of sector %llu (LEB %d). start=%lu, len=%lu\n",
+ __func__, rq_data_dir(req) ? "Write" : "Read",
+ blk_rq_pos(req), leb, start, len);
+
+ ret = ubi_read(dev->vol_desc, leb, req->buffer, offset, len);
+
+ if (ret) {
+ pr_err("ubi_read error\n");
+ return ret;
+ }
+
+ pr_debug("ubi_read done.\n");
+
+ return 0;
+}
+
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->thread);
+}
+
+/*
+ * Open a UBI volume (get the volume descriptor)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__,
+ bdev->bd_disk->disk_name, mode);
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (!dev->vol_desc) {
+ pr_err("open_volume failed");
+ return -EINVAL;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ return -ENOMEM;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ return 0;
+}
+
+/*
+ * Close a UBI volume (close the volume descriptor)
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__, gd->disk_name,
+ mode);
+
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ if (dev->vol_desc) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Loop on the block request queue and wait for new requests ; run them with
+ * do_ubiblk_request()
+ *
+ * Mostly stolen from mtd_blkdevs.c
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req && !(req = blk_fetch_request(rq))) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->lock);
+ res = do_ubiblk_request(req, dev);
+ pr_debug("return from request: %d\n", res);
+ mutex_unlock(&dev->lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+static int ubiblk_create(struct ubi_device_info *dev_info,
+ struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int i;
+ int ret = 0;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++)
+ if (!ubiblk_devs[i])
+ break;
+
+ if (i == UBIBLK_MAX_DEVS) {
+ /* Shouldn't happen: UBI can't make more volumes than that */
+ pr_err("no slot left for a new ubiblk device.\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("UBIBLK: ENOMEM when trying to create a new"
+ "ubiblk dev\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+ ubiblk_devs[i] = dev;
+ mutex_unlock(&devtable_lock);
+
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+
+ dev->ubi_num = vol_info->ubi_num;
+ dev->vol_id = vol_info->vol_id;
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->vol_desc)) {
+ pr_err("open_volume failed\n");
+ ret = PTR_ERR(dev->vol_desc);
+ goto out_vol;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ pr_info("Got volume %s: device %d/volume %d of size %d\n",
+ dev->vol_info->name, dev->ubi_num, dev->vol_id,
+ dev->vol_info->size);
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_disk;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ pr_debug("creating a gd '%s'\n", gd->disk_name);
+ set_capacity(gd,
+ (dev->vol_info->size *
+ dev->vol_info->usable_leb_size) >> 9);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblkd", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
+
+ add_disk(dev->gd);
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_disk:
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+out_info:
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+out_vol:
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+static int ubiblk_remove(struct ubi_volume_info *vol_info)
+{
+ int i;
+ struct ubiblk_dev *dev;
+
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ dev = ubiblk_devs[i];
+ if (dev && dev->ubi_num == vol_info->ubi_num &&
+ dev->vol_id == vol_info->vol_id)
+ break;
+ }
+ if (i == UBIBLK_MAX_DEVS) {
+ pr_warn("Trying to remove %s, which is unknown from ubiblk\n",
+ vol_info->name);
+ return -ENODEV;
+ }
+
+ pr_info("Removing %s\n", vol_info->name);
+
+ if (dev->vol_desc)
+ ubi_close_volume(dev->vol_desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+
+ kfree(ubiblk_devs[i]);
+ ubiblk_devs[i] = NULL;
+
+ return 0;
+}
+
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_ADDED:
+ ubiblk_create(&nt->di, &nt->vi);
+ break;
+ case UBI_VOLUME_REMOVED:
+ /* TODO */
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ /* TODO. needed ? */
+ /* gluebi_resized(&nt->vi); */
+ break;
+ case UBI_VOLUME_UPDATED:
+ break;
+ case UBI_VOLUME_RENAMED:
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+/*
+ * Initialize the module
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret;
+
+ pr_info("UBIBLK starting\n");
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("UBIBLK: could not register_blkdev\n");
+ return -ENODEV;
+ }
+ major = ret;
+ pr_info("UBIBLK: device's major: %d\n", major);
+
+ mutex_init(&devtable_lock);
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void __exit ubi_ubiblk_exit(void)
+{
+ int i;
+
+ pr_info("UBIBLK: going to exit\n");
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ struct ubiblk_dev *dev = ubiblk_devs[i];
+ if (!dev)
+ continue;
+
+ if (dev->vol_desc)
+ ubi_close_volume(dev->vol_desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+ kfree(dev);
+ }
+
+ unregister_blkdev(major, "ubiblk");
+ pr_info("UBIBLK: The End\n");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-06-24 13:50:02 UTC
Permalink
Post by d***@free-electrons.com
drivers/mtd/ubi/Kconfig | 9 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 462 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 472 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
The patch was made against tag v2.6.39

The Kconfig description states that the module caches the reads from UBI
volume ; it isn't true.
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-06-27 19:20:02 UTC
Permalink
Hi,

thanks, for the driver.
I'm submitting it for review, comments (on the concept as well as on the
implementation) and advice. It is my first kernel module - a lot of code was
taken from mtd_blkdevs and some from gluebi.
If you borrow code from anywhere, you should preserve Copyrights.
--
Best Regards,
Artem Bityutskiy (Битюцкий Артём)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-06-27 19:30:01 UTC
Permalink
Post by d***@free-electrons.com
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblkd", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
Why we need a kernel thread? Could you please describe when exactly it
is needed and why we cannot avoid having it?
Post by d***@free-electrons.com
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("UBIBLK: could not register_blkdev\n");
+ return -ENODEV;
+ }
+ major = ret;
+ pr_info("UBIBLK: device's major: %d\n", major);
+
+ mutex_init(&devtable_lock);
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 0);
+ if (ret < 0)
+ return ret;
You should probably de-register the blkdev when you fail here.
--
Best Regards,
Artem Bityutskiy (Битюцкий Артём)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-06-28 11:50:01 UTC
Permalink
Hi,
Post by Artem Bityutskiy
Post by d***@free-electrons.com
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblkd", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
Why we need a kernel thread? Could you please describe when exactly it
is needed and why we cannot avoid having it?
Do you mean that there could be another/better way ?
I read that workqueues could be used for that but since they seem to
internally use kthreads, I don't see the advantage yet. Simpler API ?

I also tried without a kthread altogether (and call do_ubiblk_request
directly within the callback registered with blk_init_queue) but got
lost in locks/context debugging ...

It seems that do_ubiblk_request needs to be in process context because
there are thousands causes for blocking (locking, page fault, for
instance, are the one I encountered). And on the other hand,
blk_run_queue must not block ; So we need to wake the thread up and
return (what ubi_ubiblk_request does).
So, would this be a sufficient justification ?


It's probably possible, however, to have only one thread for the whole
module instead of having one for each volume ; but that seemed good
enough on first approach.


I fixed the read errors issue with filesystems != SquashFS, so they
should all work, now.
I'll send the next iteration, probably later today.
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-06-29 07:00:02 UTC
Permalink
Post by David Wagner
Hi,
Post by Artem Bityutskiy
Post by d***@free-electrons.com
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblkd", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
Why we need a kernel thread? Could you please describe when exactly it
is needed and why we cannot avoid having it?
Do you mean that there could be another/better way ?
No, I just do not understand why it is there. I think this is juts block
layer's design, but I wanted you to explain this - the design, how block
requests are handled, and where exactly the thread is needed. I expected
you just have the explanation.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Matthieu CASTET
2011-06-28 15:10:02 UTC
Permalink
Hi,
Post by Artem Bityutskiy
Post by d***@free-electrons.com
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblkd", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
Why we need a kernel thread? Could you please describe when exactly it
is needed and why we cannot avoid having it?
Also what are the advantage against gluebi + mtdblock_ro ?
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-06-28 16:00:03 UTC
Permalink
Post by Matthieu CASTET
Hi,
[...]
Post by Matthieu CASTET
Also what are the advantage against gluebi + mtdblock_ro ?
The main advantage is a reduced number of layers ; I must say I cannot
see much more for now. I could add that the Kconfig help of gluebi
advises not to use it except when needed by legacy software.
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-06-29 06:30:02 UTC
Permalink
Post by David Wagner
Post by Matthieu CASTET
Hi,
[...]
Post by Matthieu CASTET
Also what are the advantage against gluebi + mtdblock_ro ?
The main advantage is a reduced number of layers ; I must say I cannot
see much more for now. I could add that the Kconfig help of gluebi
advises not to use it except when needed by legacy software.
Well, I think Matthieu has a valid poit, you should try to come up with
a set of advantages, otherwise why would this drivers be needed? Why
would people spend time reviewing it? May be less memcpy's? Do we do an
extra memcpy in gluebi? If yes, can we avoid doing this. Anyway, please,
try to sell this driver a bit better.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
d***@free-electrons.com
2011-06-28 16:00:02 UTC
Permalink
From: David Wagner <***@free-electrons.com>

Hi,

This is next iteration ; the read errors issue with FS that do large requests
has been fixed (it means that all block filesystems should now work) ; the
'resized' callback is implemented.

The reuse of code from mtd_blkdevs and gluebi is now mentionned is the file
header.
The use of a kernel thread is also justified (line 353).
The Kconfig has also been fixed.

I also tested compiling ubiblk statically into the kernel ; it worked and I was
able to mount a squashfs ubiblk device as root with, for instance:
"ubi.mtd=1 root=/dev/ubiblk0_0"

Performances still haven't been tested, though.


Thanks to those for review it !
David.

PS: I included the commit message+diff in the same mail as the introduction for
convenience ; tell me if it annoys you - I'll split the next iterations as I did
for the first one.


ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices.

It is used by putting a block filesystem image on a UBI volume and then mounting
the corresponding device.

It uses the UBI API to register to UBI notifications (to dynamically create and
delete devices as volumes are added or removed) and to read from the volumes.

Known issues:
* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. It should make use of a linked list.

Some code is taken from mtd_blkdevs and gluebi

Signed-off-by: David Wagner <***@free-electrons.com>
---
drivers/mtd/ubi/Kconfig | 12 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 554 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 567 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c

diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..d19508b 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,16 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device for
+ each UBI volume. It makes it possible to use block filesystems on
+ top of UBI (and thus, on top of MTDs while avoiding bad blocks).
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..53e6d4e
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c (Artem Bityutskiy (БОтюцкОй АртёЌ),
+ * Joern Engel)
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+#define UBIBLK_MAX_DEVS (UBI_MAX_DEVICES * UBI_MAX_VOLUMES)
+
+/*
+ * Structure representing a ubiblk device, proxying a UBI volume
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *vol_desc;
+ struct ubi_volume_info *vol_info;
+ int ubi_num;
+ int vol_id;
+
+ /* Block stuff */
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *thread;
+
+ /* Protects the access to the UBI volume */
+ struct mutex lock;
+
+ /* Avoids concurrent accesses to the request queue */
+ spinlock_t queue_lock;
+};
+
+/*
+ * Contains the pointers to all ubiblk_dev instances
+ * TODO: use a linked list
+ */
+static struct ubiblk_dev *ubiblk_devs[UBIBLK_MAX_DEVS];
+static struct mutex devtable_lock;
+
+int major;
+static const struct block_device_operations ubiblk_ops;
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vol_info)
+{
+ int i;
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ dev = ubiblk_devs[i];
+ if (dev && dev->ubi_num == vol_info->ubi_num &&
+ dev->vol_id == vol_info->vol_id)
+ break;
+ }
+ mutex_unlock(&devtable_lock);
+ if (i == UBIBLK_MAX_DEVS)
+ return NULL;
+ return dev;
+}
+
+/*
+ * Read a LEB and fill the request buffer with the requested sector
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vol_info->usable_leb_size;
+ offset = start % dev->vol_info->usable_leb_size;
+
+ do {
+ int overlap = 0;
+
+ if (offset + len > dev->vol_info->usable_leb_size) {
+ len = dev->vol_info->usable_leb_size - offset;
+ overlap = 1;
+ }
+
+ if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk)) {
+ pr_err("UBIBLK: attempting to read too far\n");
+ return -EIO;
+ }
+
+ pr_debug("%s(%s) of sector %llu (LEB %d). offset=%d, len=%lu\n",
+ __func__, rq_data_dir(req) ? "Write" : "Read",
+ blk_rq_pos(req), leb, offset, len);
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request ; if the request
+ * is overlapping on several lebs, (read) will be > 0 and the
+ * data will be put in the buffer at offset (read) */
+ ret = ubi_read(dev->vol_desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ pr_err("ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+ len = blk_rq_cur_bytes(req) - read_bytes;
+
+ /* If we needed to cap the length of ubi_read, the next
+ * ubi_read will be done on the beginning of the next LEB */
+ if (overlap) {
+ leb++;
+ offset = 0;
+ overlap = 0;
+ }
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ pr_debug("ubi_read done.\n");
+
+ return 0;
+}
+
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->thread);
+}
+
+/*
+ * Open a UBI volume (get the volume descriptor)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__,
+ bdev->bd_disk->disk_name, mode);
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (!dev->vol_desc) {
+ pr_err("open_volume failed");
+ return -EINVAL;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ return -ENOMEM;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ return 0;
+}
+
+/*
+ * Close a UBI volume (close the volume descriptor)
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__, gd->disk_name, mode);
+
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ if (dev->vol_desc) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Loop on the block request queue and wait for new requests ; run them with
+ * do_ubiblk_request()
+ *
+ * Mostly stolen from mtd_blkdevs.c
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req && !(req = blk_fetch_request(rq))) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->lock);
+ res = do_ubiblk_request(req, dev);
+ pr_debug("return from request: %d\n", res);
+ mutex_unlock(&dev->lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/*
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * thread.
+ */
+static int ubiblk_create(struct ubi_device_info *dev_info,
+ struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int i;
+ int ret = 0;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++)
+ if (!ubiblk_devs[i])
+ break;
+
+ if (i == UBIBLK_MAX_DEVS) {
+ /* Shouldn't happen: UBI can't make more volumes than that */
+ pr_err("no slot left for a new ubiblk device.\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("UBIBLK: ENOMEM when trying to create a new"
+ "ubiblk dev\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+ ubiblk_devs[i] = dev;
+ mutex_unlock(&devtable_lock);
+
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+
+ dev->ubi_num = vol_info->ubi_num;
+ dev->vol_id = vol_info->vol_id;
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->vol_desc)) {
+ pr_err("open_volume failed\n");
+ ret = PTR_ERR(dev->vol_desc);
+ goto out_vol;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ pr_info("Got volume %s: device %d/volume %d of size %d\n",
+ dev->vol_info->name, dev->ubi_num, dev->vol_id,
+ dev->vol_info->size);
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_disk;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ pr_debug("creating a gd '%s'\n", gd->disk_name);
+ set_capacity(gd,
+ (dev->vol_info->size *
+ dev->vol_info->usable_leb_size) >> 9);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
+
+ add_disk(dev->gd);
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_disk:
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+out_info:
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+out_vol:
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+/*
+ * A UBI has been removed ; destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+
+ dev = ubiblk_find_dev(vol_info);
+
+ if (!dev) {
+ pr_warn("Trying to remove %s, which is unknown from ubiblk\n",
+ vol_info->name);
+ return -ENODEV;
+ }
+
+ pr_info("Removing %s\n", vol_info->name);
+
+ if (dev->vol_desc)
+ ubi_close_volume(dev->vol_desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+
+ mutex_lock(&devtable_lock);
+ kfree(dev);
+ dev = NULL;
+ mutex_unlock(&devtable_lock);
+
+ return 0;
+}
+
+static int ubiblk_resized(struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+
+ dev = ubiblk_find_dev(vol_info);
+ if (!dev) {
+ pr_warn("Trying to resize %s, which is unknown from ubiblk\n",
+ vol_info->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->lock);
+ set_capacity(dev->gd,
+ (vol_info->size * vol_info->usable_leb_size) >> 9);
+ mutex_unlock(&dev->lock);
+ pr_debug("Resized ubiblk%d_%d to %d LEBs\n", vol_info->ubi_num,
+ vol_info->vol_id, vol_info->size);
+ return 0;
+}
+
+/*
+ * Dispatches the UBI notifications
+ * copied from gluebi.c
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_ADDED:
+ ubiblk_create(&nt->di, &nt->vi);
+ break;
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resized(&nt->vi);
+ break;
+ case UBI_VOLUME_UPDATED:
+ break;
+ case UBI_VOLUME_RENAMED:
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+/*
+ * Initialize the module
+ * (Get a major number and register to UBI notifications)
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ pr_info("UBIBLK starting\n");
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("UBIBLK: could not register_blkdev\n");
+ return -ENODEV;
+ }
+ major = ret;
+ pr_info("UBIBLK: device's major: %d\n", major);
+
+ mutex_init(&devtable_lock);
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 0);
+ if (ret < 0)
+ unregister_blkdev(major, "ubiblk");
+
+ return ret;
+}
+
+/*
+ * End of life
+ * unregister the block device major, unregister from UBI notifications,
+ * stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ int i;
+
+ pr_info("UBIBLK: going to exit\n");
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ struct ubiblk_dev *dev = ubiblk_devs[i];
+ if (!dev)
+ continue;
+
+ if (dev->vol_desc)
+ ubi_close_volume(dev->vol_desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+ kfree(dev);
+ }
+
+ unregister_blkdev(major, "ubiblk");
+ pr_info("UBIBLK: The End\n");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-06-29 07:00:02 UTC
Permalink
Post by d***@free-electrons.com
+ * Author: David Wagner
+ * Some code taken from gluebi.c (Artem Bityutskiy (Битюцкий Артём),
+ * Joern Engel)
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
It is important to preserve Copyrights, because it has to do with legal
stuff.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-07-26 12:30:01 UTC
Permalink
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices.

It is used by putting a block filesystem image on a UBI volume and then mounting
the corresponding device (/dev/ubiblkX_Y where X is the UBI device number and Y
the volume ID).

It uses the UBI API to register to UBI notifications (to dynamically create and
delete devices as volumes are added or removed) and to read from the volumes.

Some code is taken from mtd_blkdevs and gluebi

TODO:
* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. It should make use of a linked list.

Signed-off-by: David Wagner <***@free-electrons.com>
---

updates from v2:

* Added copyright information
* Simplify the do_ubiblk_request loop
* Fix a crash at module exit

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional
MTD device for each UBI volume, so the number of MTD devices grows
quite a lot and is a bit difficult to understand. For example,
mtdblock[0-4] might be your real MTD partitions, while mtdblock[5-9]
might be your UBI volumes.
It also means that if a new real MTD
partition is added, the number of all the MTD devices exposing UBI
volumes will be incremented by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are
emulated on top of volumes that come after this new one will have
their ID incremented.

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)



drivers/mtd/ubi/Kconfig | 12 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 556 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 569 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c

diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..d19508b 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,16 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device for
+ each UBI volume. It makes it possible to use block filesystems on
+ top of UBI (and thus, on top of MTDs while avoiding bad blocks).
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..bbf5b84
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c (Artem Bityutskiy (БОтюцкОй АртёЌ),
+ * Joern Engel)
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+#define UBIBLK_MAX_DEVS (UBI_MAX_DEVICES * UBI_MAX_VOLUMES)
+
+/*
+ * Structure representing a ubiblk device, proxying a UBI volume
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *vol_desc;
+ struct ubi_volume_info *vol_info;
+ int ubi_num;
+ int vol_id;
+
+ /* Block stuff */
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *thread;
+
+ /* Protects the access to the UBI volume */
+ struct mutex lock;
+
+ /* Avoids concurrent accesses to the request queue */
+ spinlock_t queue_lock;
+};
+
+/*
+ * Contains the pointers to all ubiblk_dev instances
+ * TODO: use a linked list
+ */
+static struct ubiblk_dev *ubiblk_devs[UBIBLK_MAX_DEVS];
+static struct mutex devtable_lock;
+
+int major;
+static const struct block_device_operations ubiblk_ops;
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vol_info)
+{
+ int i;
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ dev = ubiblk_devs[i];
+ if (dev && dev->ubi_num == vol_info->ubi_num &&
+ dev->vol_id == vol_info->vol_id)
+ break;
+ }
+ mutex_unlock(&devtable_lock);
+ if (i == UBIBLK_MAX_DEVS)
+ return NULL;
+ return dev;
+}
+
+/*
+ * Read a LEB and fill the request buffer with the requested sector
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vol_info->usable_leb_size;
+ offset = start % dev->vol_info->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vol_info->usable_leb_size)
+ len = dev->vol_info->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ pr_err("UBIBLK: attempting to read too far\n");
+ return -EIO;
+ }
+
+ pr_debug("%s(%s) of sector %llu (LEB %d). offset=%d, len=%lu\n",
+ __func__, rq_data_dir(req) ? "Write" : "Read",
+ blk_rq_pos(req), leb, offset, len);
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->vol_desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ pr_err("ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ pr_debug("ubi_read done.\n");
+
+ return 0;
+}
+
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->thread);
+}
+
+/*
+ * Open a UBI volume (get the volume descriptor)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__,
+ bdev->bd_disk->disk_name, mode);
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (!dev->vol_desc) {
+ pr_err("open_volume failed");
+ return -EINVAL;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ return -ENOMEM;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ return 0;
+}
+
+/*
+ * Close a UBI volume (close the volume descriptor)
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__, gd->disk_name, mode);
+
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ if (dev->vol_desc) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Loop on the block request queue and wait for new requests ; run them with
+ * do_ubiblk_request()
+ *
+ * Mostly copied from mtd_blkdevs.c
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req && !(req = blk_fetch_request(rq))) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->lock);
+ res = do_ubiblk_request(req, dev);
+ pr_debug("return from request: %d\n", res);
+ mutex_unlock(&dev->lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/*
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * thread.
+ */
+static int ubiblk_create(struct ubi_device_info *dev_info,
+ struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int i;
+ int ret = 0;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++)
+ if (!ubiblk_devs[i])
+ break;
+
+ if (i == UBIBLK_MAX_DEVS) {
+ /* Shouldn't happen: UBI can't make more volumes than that */
+ pr_err("no slot left for a new ubiblk device.\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("UBIBLK: ENOMEM when trying to create a new"
+ "ubiblk dev\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+ ubiblk_devs[i] = dev;
+ mutex_unlock(&devtable_lock);
+
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+
+ dev->ubi_num = vol_info->ubi_num;
+ dev->vol_id = vol_info->vol_id;
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->vol_desc)) {
+ pr_err("open_volume failed\n");
+ ret = PTR_ERR(dev->vol_desc);
+ goto out_vol;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ pr_info("Got volume %s: device %d/volume %d of size %d\n",
+ dev->vol_info->name, dev->ubi_num, dev->vol_id,
+ dev->vol_info->size);
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_disk;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ pr_debug("creating a gd '%s'\n", gd->disk_name);
+ set_capacity(gd,
+ (dev->vol_info->size *
+ dev->vol_info->usable_leb_size) >> 9);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
+
+ add_disk(dev->gd);
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_disk:
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+out_info:
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+out_vol:
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+/*
+ * A UBI has been removed ; destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vol_info)
+{
+ int i;
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ dev = ubiblk_devs[i];
+ if (dev && dev->ubi_num == vol_info->ubi_num &&
+ dev->vol_id == vol_info->vol_id)
+ break;
+ }
+ if (i == UBIBLK_MAX_DEVS) {
+ pr_warn("Trying to remove %s, which is unknown from ubiblk\n",
+ vol_info->name);
+ return -ENODEV;
+ }
+
+ pr_info("ubiblk: Removing %s\n", vol_info->name);
+
+ if (dev->vol_desc) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+
+ kfree(ubiblk_devs[i]);
+ ubiblk_devs[i] = NULL;
+
+ mutex_unlock(&devtable_lock);
+ return 0;
+}
+
+static int ubiblk_resized(struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+
+ dev = ubiblk_find_dev(vol_info);
+ if (!dev) {
+ pr_warn("Trying to resize %s, which is unknown from ubiblk\n",
+ vol_info->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->lock);
+ set_capacity(dev->gd,
+ (vol_info->size * vol_info->usable_leb_size) >> 9);
+ mutex_unlock(&dev->lock);
+ pr_debug("Resized ubiblk%d_%d to %d LEBs\n", vol_info->ubi_num,
+ vol_info->vol_id, vol_info->size);
+ return 0;
+}
+
+/*
+ * Dispatches the UBI notifications
+ * copied from gluebi.c
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_ADDED:
+ ubiblk_create(&nt->di, &nt->vi);
+ break;
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resized(&nt->vi);
+ break;
+ case UBI_VOLUME_UPDATED:
+ break;
+ case UBI_VOLUME_RENAMED:
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+/*
+ * Initialize the module
+ * (Get a major number and register to UBI notifications)
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ pr_info("UBIBLK starting\n");
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("UBIBLK: could not register_blkdev\n");
+ return -ENODEV;
+ }
+ major = ret;
+ pr_info("UBIBLK: device's major: %d\n", major);
+
+ mutex_init(&devtable_lock);
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 0);
+ if (ret < 0)
+ unregister_blkdev(major, "ubiblk");
+
+ return ret;
+}
+
+/*
+ * End of life
+ * unregister the block device major, unregister from UBI notifications,
+ * stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ int i;
+
+ pr_info("UBIBLK: going to exit\n");
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ struct ubiblk_dev *dev = ubiblk_devs[i];
+ if (!dev)
+ continue;
+
+ if (dev->vol_desc)
+ ubi_close_volume(dev->vol_desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+ kfree(ubiblk_devs[i]);
+ }
+
+ unregister_blkdev(major, "ubiblk");
+ pr_info("UBIBLK: The End\n");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Christoph Hellwig
2011-07-26 12:40:01 UTC
Permalink
Post by d***@free-electrons.com
* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. It should make use of a linked list.
A linked list isn't very nice either. Try using idr, which gives you
both an allocator for the minor number space, and a way to look up
the structure by that index.
This section should be in the main commit log, and not that part with
the changelog which gets dropped.
Post by d***@free-electrons.com
+int major;
Please don't use global variables with generic names. In this case you
should neither make it global nor give it a generic name.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-07-26 13:00:02 UTC
Permalink
Post by Christoph Hellwig
Post by d***@free-electrons.com
* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. It should make use of a linked list.
A linked list isn't very nice either. Try using idr, which gives you
both an allocator for the minor number space, and a way to look up
the structure by that index.
I'll take a look ; is it really worth it, though ? I look in the table
only when a volume is created, resized, or removed (that is, rarely) and
there are really few items (I bet, between 1 and 8 ; max possible is 4096).
Post by Christoph Hellwig
This section should be in the main commit log, and not that part with
the changelog which gets dropped.
Will do.
Post by Christoph Hellwig
Post by d***@free-electrons.com
+int major;
Please don't use global variables with generic names. In this case you
should neither make it global nor give it a generic name.
fixed.


Thank you for your feedback :)
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-07-28 06:20:01 UTC
Permalink
Post by David Wagner
* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional
MTD device for each UBI volume, so the number of MTD devices grows
quite a lot and is a bit difficult to understand. For example,
mtdblock[0-4] might be your real MTD partitions, while mtdblock[5-9]
might be your UBI volumes.
It also means that if a new real MTD
partition is added, the number of all the MTD devices exposing UBI
volumes will be incremented by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are
emulated on top of volumes that come after this new one will have
their ID incremented.
* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)
Hi, I am having vacation till the middle of August and I do not have a
possibility to look at UBI/UBIFS/MTD stuff now, sorry. When I come back,
I'll go through my e-mails and respond. Apologies again.
--
Best Regards,
Artem Bityutskiy (Битюцкий Артём)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-08-15 12:00:01 UTC
Permalink
Post by David Wagner
* Simpler architecture
* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional
MTD device for each UBI volume, so the number of MTD devices grows
quite a lot and is a bit difficult to understand. For example,
mtdblock[0-4] might be your real MTD partitions, while mtdblock[5-9]
might be your UBI volumes.
It also means that if a new real MTD
partition is added, the number of all the MTD devices exposing UBI
volumes will be incremented by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are
emulated on top of volumes that come after this new one will have
their ID incremented.
* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)
Hi, sounds good.

However, what I think is wrong is to follow the MTD + mtdblock approach:
mtdblock creates a block device for every MTD device - this is wasteful
and confusing. Indeed, it is wasteful because we end up with potentially
many unneeded block devices which are not used but consume resources.
Confusing is because people get impression that /dev/mtdblock* are the
right interfaces to access the MTD devices, which is wrong.

You can say that gluebi is also doing something like that - yes, this is
because gluebi is kind of temporary solution for software which cannot
work with UBI. We wanted to keep it very simple.

My suggestion is to do something similar to UBI:

* make ubiblk register its own character device (/dev/ubiblk_ctl,
similar to /dev/ubi_ctl).
* add a couple of ioclts to create / delete ubiblk devices (similar to
UBI attach/detach)
* add a couple of user-space tools to mtd-utils to create / delete
ubiblk devices (similar to ubiattach / ubidetach).

The other reason why I think this more complex approach is justified is
that when / if you decide to add R/W support to ubiblk, you'd have
troubles with the current approach (e.g., what if the UBI volume you are
looking at is not ubiblk-formatted?)

I think it should be rather easy to implement /dev/ubiblk_ctl.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
d***@free-electrons.com
2011-08-17 13:20:01 UTC
Permalink
From: David Wagner <***@free-electrons.com>

ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblk_X_Y, where X is the UBI device number
and Y the Volume ID).

It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.

It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.

Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the number of all
the MTD devices exposing UBI volumes will be incremented by one, which is a
bit confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.

* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)

TODO:
* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. There should be a better solution (linked list or, as
Christoph Hellwig suggests, a radix tree (idr)).

Signed-off-by: David Wagner <***@free-electrons.com>
---

Hi Artem,

Here is the v3 of ubiblk implementing ioctls for on-demand creation/removal of
ubiblk device ; is it what you were thinking of ?

I also wrote a minimal (and ugly w.r.t argument parsing) tool in mtd-utils'
git ; I'll send it as a reply to this email.

known issue: attempts to create the same ubiblk device twice aren't catched and,
obviously, make things go wrong (see TODO).

Questions:
==========
I wasn't sure what magic ioctl number to use, so I settled to use the same one
as a part of UBI: 'O', which was so far only used by UBI but on a higher range
and leaving some room for UBI to add ioctls (for nw, it uses 'O'/0x00-0x06 and
ubiblk uses 'O'/0x10-0x11). Is it ok or should ubiblk use a different
number/range ?


updates from v3:
===============
- Removed unused parameter "struct ubi_device_info" from ubiblk_create

- Synchronisation: forgot to release a mutex on the error path of ubiblk_create

- New header ubiblk-user.h, contains ioctl numbers ; patch
Documentation/ioctl/ioctl-numbers.txt (use the same magic as UBI but on
another range, leaving expansion margin for UBI)

- Add a ioctl handler and make it use the already-existing
ubiblk_{create,remove} functions (inspired from ubi/cdev.c).

- Register a miscdevice (inspired from ubi/build.c) that will only receive ioctls

- Notifications: don't receive a notifications for already-existing volumes
upon registration ; don't create a ubiblk device when a new UBI volume
appears

- Amend commit message and Kconfig according to these previous points


TODO:
=====
- Use a dynamic structure for storing the ubiblk_devs (linked list or idr ?)
- After this task is done, check that using ubiblkadd can't create an
already-existing ubiblk device (see warning/todo in the code)


Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 15 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 653 ++++++++++++++++++++++++++++++++++
include/mtd/ubiblk-user.h | 43 +++
5 files changed, 713 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 3a46e36..240cf0f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..3eedf9a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,19 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use block filesystems on top of
+ UBI (and thus, on top of MTDs while avoiding bad blocks).
+
+ ubiblk devices are created by sending appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..20c0896
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c (Artem Bityutskiy (БОтюцкОй АртёЌ),
+ * Joern Engel)
+ * Some code taken from cdev.c (Artem Bityutskiy (БОтюцкОй АртёЌ))
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+#define UBIBLK_MAX_DEVS (UBI_MAX_DEVICES * UBI_MAX_VOLUMES)
+
+/*
+ * Structure representing a ubiblk device, proxying a UBI volume
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *vol_desc;
+ struct ubi_volume_info *vol_info;
+ int ubi_num;
+ int vol_id;
+
+ /* Block stuff */
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *thread;
+
+ /* Protects the access to the UBI volume */
+ struct mutex lock;
+
+ /* Avoids concurrent accesses to the request queue */
+ spinlock_t queue_lock;
+};
+
+/*
+ * Contains the pointers to all ubiblk_dev instances
+ * TODO: use a linked list
+ */
+static struct ubiblk_dev *ubiblk_devs[UBIBLK_MAX_DEVS];
+static struct mutex devtable_lock;
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vol_info)
+{
+ int i;
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ dev = ubiblk_devs[i];
+ if (dev && dev->ubi_num == vol_info->ubi_num &&
+ dev->vol_id == vol_info->vol_id)
+ break;
+ }
+ mutex_unlock(&devtable_lock);
+ if (i == UBIBLK_MAX_DEVS)
+ return NULL;
+ return dev;
+}
+
+/*
+ * Read a LEB and fill the request buffer with the requested sector
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vol_info->usable_leb_size;
+ offset = start % dev->vol_info->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vol_info->usable_leb_size)
+ len = dev->vol_info->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ pr_err("UBIBLK: attempting to read too far\n");
+ return -EIO;
+ }
+
+ pr_debug("%s(%s) of sector %llu (LEB %d). offset=%d, len=%lu\n",
+ __func__, rq_data_dir(req) ? "Write" : "Read",
+ blk_rq_pos(req), leb, offset, len);
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->vol_desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ pr_err("ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ pr_debug("ubi_read done.\n");
+
+ return 0;
+}
+
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->thread);
+}
+
+/*
+ * Open a UBI volume (get the volume descriptor)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__,
+ bdev->bd_disk->disk_name, mode);
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (!dev->vol_desc) {
+ pr_err("open_volume failed");
+ return -EINVAL;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ return -ENOMEM;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ return 0;
+}
+
+/*
+ * Close a UBI volume (close the volume descriptor)
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+ pr_debug("%s() disk_name=%s, mode=%d\n", __func__, gd->disk_name, mode);
+
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ if (dev->vol_desc) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Loop on the block request queue and wait for new requests ; run them with
+ * do_ubiblk_request()
+ *
+ * Mostly copied from mtd_blkdevs.c
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req && !(req = blk_fetch_request(rq))) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->lock);
+ res = do_ubiblk_request(req, dev);
+ pr_debug("return from request: %d\n", res);
+ mutex_unlock(&dev->lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/*
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * thread.
+ */
+static int ubiblk_create(struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int i;
+ int ret = 0;
+
+ mutex_lock(&devtable_lock);
+#warning "TODO: Make sure the ubiblk device doesn't already exist (to be done after introducing the use of a dynamic structure for storing the ubiblk_devs)"
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++)
+ if (!ubiblk_devs[i])
+ break;
+
+ if (i == UBIBLK_MAX_DEVS) {
+ /* Shouldn't happen: UBI can't make more volumes than that */
+ pr_err("no slot left for a new ubiblk device.\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("UBIBLK: ENOMEM when trying to create a new"
+ "ubiblk dev\n");
+ mutex_unlock(&devtable_lock);
+ return -ENOMEM;
+ }
+ ubiblk_devs[i] = dev;
+ mutex_unlock(&devtable_lock);
+
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+
+ dev->ubi_num = vol_info->ubi_num;
+ dev->vol_id = vol_info->vol_id;
+
+ dev->vol_desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->vol_desc)) {
+ pr_err("open_volume failed\n");
+ ret = PTR_ERR(dev->vol_desc);
+ goto out_vol;
+ }
+
+ dev->vol_info = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vol_info) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+ ubi_get_volume_info(dev->vol_desc, dev->vol_info);
+
+ pr_info("Got volume %s: device %d/volume %d of size %d\n",
+ dev->vol_info->name, dev->ubi_num, dev->vol_id,
+ dev->vol_info->size);
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_disk;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ pr_debug("creating a gd '%s'\n", gd->disk_name);
+ set_capacity(gd,
+ (dev->vol_info->size *
+ dev->vol_info->usable_leb_size) >> 9);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing thread
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->thread = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->thread)) {
+ ret = PTR_ERR(dev->thread);
+ goto out_thread;
+ }
+
+ add_disk(dev->gd);
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_disk:
+ kfree(dev->vol_info);
+ dev->vol_info = NULL;
+out_info:
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+out_vol:
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+/*
+ * A UBI has been removed ; destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vol_info)
+{
+ int i;
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devtable_lock);
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ dev = ubiblk_devs[i];
+ if (dev && dev->ubi_num == vol_info->ubi_num &&
+ dev->vol_id == vol_info->vol_id)
+ break;
+ }
+ if (i == UBIBLK_MAX_DEVS) {
+ mutex_unlock(&devtable_lock);
+ pr_warn("Trying to remove %s, but it isn't handled by ubiblk\n",
+ vol_info->name);
+ return -ENODEV;
+ }
+
+ pr_info("ubiblk: Removing %s\n", vol_info->name);
+
+ if (dev->vol_desc) {
+ ubi_close_volume(dev->vol_desc);
+ dev->vol_desc = NULL;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+
+ kfree(ubiblk_devs[i]);
+ ubiblk_devs[i] = NULL;
+
+ mutex_unlock(&devtable_lock);
+ return 0;
+}
+
+static int ubiblk_resized(struct ubi_volume_info *vol_info)
+{
+ struct ubiblk_dev *dev;
+
+ dev = ubiblk_find_dev(vol_info);
+ if (!dev) {
+ pr_warn("Trying to resize %s, which is unknown from ubiblk\n",
+ vol_info->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->lock);
+ set_capacity(dev->gd,
+ (vol_info->size * vol_info->usable_leb_size) >> 9);
+ mutex_unlock(&dev->lock);
+ pr_debug("Resized ubiblk%d_%d to %d LEBs\n", vol_info->ubi_num,
+ vol_info->vol_id, vol_info->size);
+ return 0;
+}
+
+/*
+ * Dispatches the UBI notifications
+ * copied from gluebi.c
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_ADDED:
+ break;
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resized(&nt->vi);
+ break;
+ case UBI_VOLUME_UPDATED:
+ break;
+ case UBI_VOLUME_RENAMED:
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/*
+ * ioctl handling for managing/unmanaging a UBI volume
+ */
+
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *vol_desc;
+ struct ubi_volume_info vol_info;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ vol_desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(vol_desc)) {
+ pr_err("Opening volume %d on device %d failed\n",
+ req.vol_id, req.ubi_num);
+ return PTR_ERR(vol_desc);
+ }
+
+ ubi_get_volume_info(vol_desc, &vol_info);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ pr_info("Creating a ubiblk device proxing ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ ubiblk_create(&vol_info);
+ break;
+ case UBIBLK_IOCDEL:
+ pr_info("Removing ubiblk from ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ ubiblk_remove(&vol_info);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long ubiblk_ctrl_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned long translated_arg = (unsigned long)compat_ptr(arg);
+
+ return ubiblk_ctrl_ioctl(file, cmd, translated_arg);
+}
+#endif
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ubiblk_ctrl_compat_ioctl,
+#endif
+ .llseek = no_llseek,
+};
+static struct miscdevice ubiblk_ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/*
+ * Initialize the module
+ * (Get a major number and register to UBI notifications)
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ pr_info("UBIBLK starting\n");
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("UBIBLK: could not register_blkdev\n");
+ return -ENODEV;
+ }
+ ubiblk_major = ret;
+ pr_info("UBIBLK: device's major: %d\n", ubiblk_major);
+
+ mutex_init(&devtable_lock);
+
+ ret = misc_register(&ubiblk_ctrl_dev);
+ if (ret) {
+ pr_err("Cannot register ubiblk_ctrl\n");
+ goto out_unreg_blk;
+ }
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_ctrl;
+
+ return ret;
+
+out_unreg_ctrl:
+ misc_deregister(&ubiblk_ctrl_dev);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+
+/*
+ * End of life
+ * unregister the block device major, unregister from UBI notifications,
+ * stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ int i;
+
+ pr_info("UBIBLK: going to exit\n");
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ubiblk_ctrl_dev);
+
+ for (i = 0; i < UBIBLK_MAX_DEVS; i++) {
+ struct ubiblk_dev *dev = ubiblk_devs[i];
+ if (!dev)
+ continue;
+
+ if (dev->vol_desc)
+ ubi_close_volume(dev->vol_desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->thread);
+ put_disk(dev->gd);
+
+ kfree(dev->vol_info);
+ kfree(ubiblk_devs[i]);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+ pr_info("UBIBLK: The End\n");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..fa0d007
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/* Structure to be passed to UBIBLK_IOCADD or IOCDEL ioctl */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+};
+
+/* ioctl commands of the UBI control character device */
+
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+
+#endif
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-08-17 14:20:02 UTC
Permalink
ubiblk_ctrl sends an appropriate ioctl to ubiblk control node.

ubiblkadd and ubiblkdel are wrappers around ubiblk_ctrl.
The syntax is:
ubiblk{add,del} x y
where x is the UBI device number and y the volume ID.

Signed-off-by: David Wagner <***@free-electrons.com>
---

This is a preliminary version: it has the control device node hardcoded which is
probably not nice.

Makefile | 6 ++-
include/mtd/ubiblk-user.h | 43 ++++++++++++++++++++
ubi-utils/ubiblk_ctrl.c | 95 +++++++++++++++++++++++++++++++++++++++++++++
ubi-utils/ubiblkadd | 2 +
ubi-utils/ubiblkdel | 2 +
5 files changed, 147 insertions(+), 1 deletions(-)
create mode 100644 include/mtd/ubiblk-user.h
create mode 100644 ubi-utils/ubiblk_ctrl.c
create mode 100755 ubi-utils/ubiblkadd
create mode 100755 ubi-utils/ubiblkdel

diff --git a/Makefile b/Makefile
index 67e42ce..2859a8c 100644
--- a/Makefile
+++ b/Makefile
@@ -27,12 +27,16 @@ MTD_BINS = \
sumtool #jffs2reader
UBI_BINS = \
ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
- ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol
+ ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol \
+ ubiblk_ctrl
+UBI_SCRIPTS = \
+ ubiblkadd ubiblkdel

BINS = $(MTD_BINS)
BINS += mkfs.ubifs/mkfs.ubifs
BINS += $(addprefix ubi-utils/,$(UBI_BINS))
SCRIPTS = flash_eraseall
+SCRIPTS += $(addprefix ubi-utils/,$(UBI_SCRIPTS))

TARGETS = $(BINS)
TARGETS += lib/libmtd.a
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..fa0d007
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/* Structure to be passed to UBIBLK_IOCADD or IOCDEL ioctl */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+};
+
+/* ioctl commands of the UBI control character device */
+
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+
+#endif
diff --git a/ubi-utils/ubiblk_ctrl.c b/ubi-utils/ubiblk_ctrl.c
new file mode 100644
index 0000000..1132f35
--- /dev/null
+++ b/ubi-utils/ubiblk_ctrl.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: David Wagner
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <mtd/ubiblk-user.h>
+
+#define CONTROL_NODE "/dev/ubiblk_ctrl"
+
+void usage(char *exec_path)
+{
+ fprintf(stderr, "%s <-a|-d> <-u UBI_DEVICE_NUMBER> <-v VOLUME_ID>\n",
+ basename(exec_path));
+}
+
+int main(int argc, char *argv[])
+{
+ int ubi_num = -2, vol_id = -1;
+ int fd;
+ struct ubiblk_ctrl_req req;
+ int ret;
+ int command;
+
+ int option;
+
+ while ((option = getopt(argc, argv, "adu:v:")) != -1) {
+ switch (option) {
+ case 'a':
+ command = UBIBLK_IOCADD;
+ break;
+ case 'd':
+ command = UBIBLK_IOCDEL;
+ break;
+ case 'u':
+ ubi_num = atoi(optarg);
+ break;
+ case 'v':
+ vol_id = atoi(optarg);
+ break;
+ }
+ }
+
+ if (command != UBIBLK_IOCDEL && command != UBIBLK_IOCADD) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ if (vol_id == -1 || vol_id == -1) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ req.ubi_num = ubi_num;
+ req.vol_id = vol_id;
+
+ fd = open(CONTROL_NODE, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Error while opening the control node: %s\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ ret = ioctl(fd, command, &req);
+ if (ret == -1) {
+ fprintf(stderr, "Error while ioctl: %s\n", strerror(errno));
+ close(fd);
+ return EXIT_FAILURE;
+ }
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/ubi-utils/ubiblkadd b/ubi-utils/ubiblkadd
new file mode 100755
index 0000000..fd8fbdd
--- /dev/null
+++ b/ubi-utils/ubiblkadd
@@ -0,0 +1,2 @@
+#! /bin/sh
+ubiblk_ctrl -a -u $0 -v $1
diff --git a/ubi-utils/ubiblkdel b/ubi-utils/ubiblkdel
new file mode 100755
index 0000000..40a1fd1
--- /dev/null
+++ b/ubi-utils/ubiblkdel
@@ -0,0 +1,2 @@
+#! /bin/sh
+ubiblk_ctrl -d -u $0 -v $1
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-08-22 07:40:02 UTC
Permalink
Hi,

thanks for the patch, it is quite good I think, but I still have few
comments. I did not review very carefully because of limited amount of
free time.

There are few checkpatch.pl complaints, could you please take a look?

Note, often I give a comment for one place, but there are many other
similar places with the same stuff.
Post by d***@free-electrons.com
* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. There should be a better solution (linked list or, as
Christoph Hellwig suggests, a radix tree (idr)).
Wold be nice to do the same change for UBI, BTW :-)
Post by d***@free-electrons.com
Here is the v3 of ubiblk implementing ioctls for on-demand creation/removal of
ubiblk device ; is it what you were thinking of ?
Looks like.

[snip]
Post by d***@free-electrons.com
+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use block filesystems on top of
+ UBI (and thus, on top of MTDs while avoiding bad blocks).
s/block filesystems/R/O block filesystems/
s/on top of UBI/on top of UBI volumes/
s/and thus, on top of MTDs/and hence, on top of MTD devices/
Post by d***@free-electrons.com
+
+ ubiblk devices are created by sending appropriate ioctl to the
+ ubiblk_ctrl device node.
s/sending/invoking/

[snip]

Would be good to add a section to the UBI Documentation describing
ubiblk by sending a patch against mtd-www:

http://git.infradead.org/mtd-www.git

[snip]
Post by d***@free-electrons.com
+/*
+ * Structure representing a ubiblk device, proxying a UBI volume
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *vol_desc;
+ struct ubi_volume_info *vol_info;
Since this piece of code is part of drivers/mtd/ubi/, I think that it
makes sense to make it very consistent with the rest of the code. It is
then better to use consistent names for variables of this type: "desc"
and "vi".
Post by d***@free-electrons.com
+ int ubi_num;
+ int vol_id;
+
+ /* Block stuff */
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *thread;
Here is one comment I got from hch once which I also saved in the git
history: 6f904ff0e39ea88f81eb77e8dfb4e1238492f0a8

hch: a convention I tend to use and I've seen in various places
is to always use _task for the storage of the task_struct pointer,
and thread everywhere else. This especially helps with having
foo_thread for the actual thread and foo_task for a global
variable keeping the task_struct pointer

Let's follow it and call this field "<something_meaningful>_task", e.g.,
"req_task" (request queue/dispatcher/etc task) ? Keep using "_thread"
for other stuff.

You can also change UBI correspondingly.
Post by d***@free-electrons.com
+ /* Protects the access to the UBI volume */
+ struct mutex lock;
Lets call all locks <something_meaningful>_lock, e.g., volumes_lock or
vol_lock.
Post by d***@free-electrons.com
+
+ /* Avoids concurrent accesses to the request queue */
+ spinlock_t queue_lock;
+};
And for consistency, it is better to use kerneldoc comments, like the
rest of UBI, but if you hate them, I will not insist.

[snip]
Post by d***@free-electrons.com
+ mutex_lock(&dev->lock);
+ set_capacity(dev->gd,
+ (vol_info->size * vol_info->usable_leb_size) >> 9);
+ mutex_unlock(&dev->lock);
+ pr_debug("Resized ubiblk%d_%d to %d LEBs\n", vol_info->ubi_num,
+ vol_info->vol_id, vol_info->size);
If you find a way to properly use dev_dbg() instead of pr_debug(), your
messages will be automatically prefixed with "ubiblk%d_%d", and your
messages will be shorter - so less code, smaller binary size. See below
my other comment.

[snip]
Post by d***@free-electrons.com
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ break;
+ ubiblk_remove(&nt->vi);
+ break;
+ ubiblk_resized(&nt->vi);
+ break;
+ break;
+ break;
+ break;
Please, remove cases which do nothing and let them end up in "default:".

[snip]
Post by d***@free-electrons.com
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *vol_desc;
+ struct ubi_volume_info vol_info;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ vol_desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(vol_desc)) {
+ pr_err("Opening volume %d on device %d failed\n",
+ req.vol_id, req.ubi_num);
Because dynamic_debug usually adds prefixes to messages, or pr_fmt is
usually used to add a prefix, I suggest to make all pr_* / dev_*
messages to start with small letter.

Also, should we use dev_* macros instead? I have always thought that
pr_* is used only if you do not have "struct device", no? But you do
have it, AFAICS:

1. For messages which are not related to a specific ubiblk device,
ubi_blk_ctrl_dev.this_device (may be ubi_blk_ctrk_dev then can be
shortened to ctrl_dev?)

2. For messages that are about a specific device - not exactly sure, but
I bet there is a struct device for your ubiblk_%d_%d device. Probably
disk_to_dev(<struct ubiblk_dev>->gd)

Try to find this out and test. We should not use pr_* unless there is a
good reason why dev_* does not work.
Post by d***@free-electrons.com
+ return PTR_ERR(vol_desc);
+ }
+
+ ubi_get_volume_info(vol_desc, &vol_info);
+
+ switch (cmd) {
+ pr_info("Creating a ubiblk device proxing ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ ubiblk_create(&vol_info);
Please, return the error code!
Post by d***@free-electrons.com
+ break;
+ pr_info("Removing ubiblk from ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ ubiblk_remove(&vol_info);
And here!

[snip]
Post by d***@free-electrons.com
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ pr_info("UBIBLK starting\n");
Please, kill this message, it looks more like tracing, not info. I
suggest you to add one single message at the end like "blah blah
initialized, major number %d".
Post by d***@free-electrons.com
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("UBIBLK: could not register_blkdev\n");
+ return -ENODEV;
+ }
+ ubiblk_major = ret;
+ pr_info("UBIBLK: device's major: %d\n", ubiblk_major);
Please, kill this message as well, or turn it into pr_debug()/dev_dbg().

Talking about messages:

1. Remove this UBIBLK: prefix from all of them. Rationale: if dynamic
debug is enabled, the dynamic debug infrastructure will add it for you,
see "Documentation/dynamic-debug-howto.txt". Otherwise you can always
define "pr_fmt" and add the prefix you wish.

[snip]
Post by d***@free-electrons.com
+static void __exit ubi_ubiblk_exit(void)
+{
+ int i;
+
+ pr_info("UBIBLK: going to exit\n");
Please, kill these. Again, if you really feel like it - add one single
message like "blah exited" at the end.

[snip]
Post by d***@free-electrons.com
+/* Structure to be passed to UBIBLK_IOCADD or IOCDEL ioctl */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+};
+
+/* ioctl commands of the UBI control character device */
Please, document here that you share "O" with UBI. Also document this in
UBI in a separate patch.
Post by d***@free-electrons.com
+
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+
+#endif
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-08-22 07:50:01 UTC
Permalink
Post by d***@free-electrons.com
==========
I wasn't sure what magic ioctl number to use, so I settled to use the same one
as a part of UBI: 'O', which was so far only used by UBI but on a higher range
and leaving some room for UBI to add ioctls (for nw, it uses 'O'/0x00-0x06 and
ubiblk uses 'O'/0x10-0x11). Is it ok or should ubiblk use a different
number/range ?
I think this is OK to share them between UBI and ubiblk, as long as this
is documented. But I always CC Arnd when it comes to ioctl-related
questions.

P.S. Arnd, you can always find the initial post in lkml, if needed.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Arnd Bergmann
2011-08-24 16:30:02 UTC
Permalink
Post by Artem Bityutskiy
Post by d***@free-electrons.com
==========
I wasn't sure what magic ioctl number to use, so I settled to use the same one
as a part of UBI: 'O', which was so far only used by UBI but on a higher range
and leaving some room for UBI to add ioctls (for nw, it uses 'O'/0x00-0x06 and
ubiblk uses 'O'/0x10-0x11). Is it ok or should ubiblk use a different
number/range ?
I think this is OK to share them between UBI and ubiblk, as long as this
is documented.
That should be fine, yes. I would probably put them into the same
header file though if they are in the same number space even
when you use them on distinct devices.

It does feel a little clumsy to have yet another character device
to manage the block devices though. What do you think about one
of these alternative approaches:

* When the ubi block device driver gets loaded, create one block
device per volume and let the user deal with permissions for
the devices instead of having to first create them as well.
* Use the existing UBI control device for the block devices as
well and just add two more ioctls to create the devices.
You can add a logical bus_type for this so that the ubi block
driver gets automatically loaded matched with the device when
one is created using the control device.

Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-08-25 07:10:02 UTC
Permalink
Hi Arnd,
Post by Arnd Bergmann
That should be fine, yes. I would probably put them into the same
header file though if they are in the same number space even
when you use them on distinct devices.
It does feel a little clumsy to have yet another character device
to manage the block devices though. What do you think about one
* When the ubi block device driver gets loaded, create one block
device per volume and let the user deal with permissions for
the devices instead of having to first create them as well.
I think this wasteful. Why should I have block devices which I do not
need? If I have 4 UBI volumes, and need only one ubiblk, why should I
waste my resources for 3 more of them (e.g., I do not want to waste
memory for struct inode for each sysfs entry which these useless block
devices will add). Also, will this mean 3 more block devices registered?

I think it is much uglier to have 3 "dummy" block devices and confuse
users than have one nice control character device. For the sake of not
having a separate control chardev?
Post by Arnd Bergmann
* Use the existing UBI control device for the block devices as
well and just add two more ioctls to create the devices.
You can add a logical bus_type for this so that the ubi block
driver gets automatically loaded matched with the device when
one is created using the control device.
This sounds better IMHO, but I am still not sure that adding another
dummy bus and exposing it in sysfs and more complexity in the ubiblk
code is more elegant and less wasteful than just creating a separate
chardev...
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Arnd Bergmann
2011-08-25 15:20:04 UTC
Permalink
Post by Artem Bityutskiy
Post by Arnd Bergmann
That should be fine, yes. I would probably put them into the same
header file though if they are in the same number space even
when you use them on distinct devices.
It does feel a little clumsy to have yet another character device
to manage the block devices though. What do you think about one
* When the ubi block device driver gets loaded, create one block
device per volume and let the user deal with permissions for
the devices instead of having to first create them as well.
I think this wasteful. Why should I have block devices which I do not
need? If I have 4 UBI volumes, and need only one ubiblk, why should I
waste my resources for 3 more of them (e.g., I do not want to waste
memory for struct inode for each sysfs entry which these useless block
devices will add). Also, will this mean 3 more block devices registered?
I think it is much uglier to have 3 "dummy" block devices and confuse
users than have one nice control character device. For the sake of not
having a separate control chardev?
The cost of a block device node in the kernel is rather low. Nowadays,
sysfs does not even permanently use inodes for entries, it has a much
more compact internal representation IIRC.

The main advantage of this approach is not having to set up the
block device at all, it would just be there, which e.g. makes it
possible to put a root file system on it or do something else without
requiring a user space tool to issue an ioctl.

Evidently you can do everything you need even with that user space
tool, but IMHO the complexity of doing that is way bigger than
just creating the block devices right away.
Post by Artem Bityutskiy
Post by Arnd Bergmann
* Use the existing UBI control device for the block devices as
well and just add two more ioctls to create the devices.
You can add a logical bus_type for this so that the ubi block
driver gets automatically loaded matched with the device when
one is created using the control device.
This sounds better IMHO, but I am still not sure that adding another
dummy bus and exposing it in sysfs and more complexity in the ubiblk
code is more elegant and less wasteful than just creating a separate
chardev...
It's not a dummy bus, in this approach it would be a the bus that gets
used by all ubiblk devices, which is a very common concept by itself.
It's more like the classic understanding of a 'device class' that Greg
wants to see get replaced by bus_types in the kernel.

Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-01 13:00:02 UTC
Permalink
Post by Arnd Bergmann
The cost of a block device node in the kernel is rather low. Nowadays,
sysfs does not even permanently use inodes for entries, it has a much
more compact internal representation IIRC.
The main advantage of this approach is not having to set up the
block device at all, it would just be there, which e.g. makes it
possible to put a root file system on it or do something else without
requiring a user space tool to issue an ioctl.
Before patch v3, every existing and new UBI volumes were "proxyfied" by
ubiblk and it was, indeed, one of our goal to be able to have a rootfs
on it. Patch v3 hinders that goal but it could still be achievable by
adding a module parameter that would explicitly create a ubiblk device
for a UBI volume at boot time.

I for one am fine with both solutions (keep the ioctl + add a kernel
parameter and throwing the ioctl away and go back "automatically create
a ubiblk for each UBI volume"). However, I agree that it doesn't make
sense to create a ubiblk device on top of UBI volumes containing, for
instance, a ubifs.

Best Regards,
David.
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-06 03:50:01 UTC
Permalink
Hi, sorry for long delay, did not have time to read my mail.
Post by Arnd Bergmann
Post by Artem Bityutskiy
I think this wasteful. Why should I have block devices which I do not
need? If I have 4 UBI volumes, and need only one ubiblk, why should I
waste my resources for 3 more of them (e.g., I do not want to waste
memory for struct inode for each sysfs entry which these useless block
devices will add). Also, will this mean 3 more block devices registered?
I think it is much uglier to have 3 "dummy" block devices and confuse
users than have one nice control character device. For the sake of not
having a separate control chardev?
The cost of a block device node in the kernel is rather low. Nowadays,
sysfs does not even permanently use inodes for entries, it has a much
more compact internal representation IIRC.
The main advantage of this approach is not having to set up the
block device at all, it would just be there, which e.g. makes it
possible to put a root file system on it or do something else without
requiring a user space tool to issue an ioctl.
Yes, I understand, but the cost of each block device is not zero, I did
not measure it, but I believe it would be in Kilobytes. Also, littering
sysfs and /dev with useless block devices is not beautiful - why would
a user want to have or see fake and useless block devices? And as David
said, for some UBI volumes we do not want to have block devices at all.

And BTW, mtdblock is uses exactly this technique, and I had to write
many times to different confused people that they should not look at
those "/dev/mtdblockX" devices, as if they did not exist...

IOW, I think automatic creation for all UBI volumes has more drawbacks
than advantages.
Post by Arnd Bergmann
Evidently you can do everything you need even with that user space
tool, but IMHO the complexity of doing that is way bigger than
just creating the block devices right away.
Well, it is jut another step to a set of steps one usually needs to do
to attach an MTD device, create an UBI volume, etc.
Post by Arnd Bergmann
It's not a dummy bus, in this approach it would be a the bus that gets
used by all ubiblk devices, which is a very common concept by itself.
It's more like the classic understanding of a 'device class' that Greg
wants to see get replaced by bus_types in the kernel.
Yes, this sounds OK. Although UBI already has notifiers, so we could
just add 2 more events.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-06 04:20:01 UTC
Permalink
Post by Artem Bityutskiy
Post by Arnd Bergmann
It's not a dummy bus, in this approach it would be a the bus that gets
used by all ubiblk devices, which is a very common concept by itself.
It's more like the classic understanding of a 'device class' that Greg
wants to see get replaced by bus_types in the kernel.
Yes, this sounds OK. Although UBI already has notifiers, so we could
just add 2 more events.
Hmm, with notifications the error handling becomes a problem - we want
the ioctls for creating/removing the block device to be synchronous,
and, should an error occur, we want to return the error code to the
user-space. So the existing notifications mechanism does not work well.

Not sure about the bus approach - David, could you take a look at it
please? If we can handle errors there - then we could indeed re-use the
UBI control device. We could even re-use the ioctl data structures for
UBI volumes creation/removal - we have plenty of space there reserved
for future extensions.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-06 04:30:02 UTC
Permalink
Post by Artem Bityutskiy
Post by Artem Bityutskiy
Post by Arnd Bergmann
It's not a dummy bus, in this approach it would be a the bus that gets
used by all ubiblk devices, which is a very common concept by itself.
It's more like the classic understanding of a 'device class' that Greg
wants to see get replaced by bus_types in the kernel.
Yes, this sounds OK. Although UBI already has notifiers, so we could
just add 2 more events.
Hmm, with notifications the error handling becomes a problem - we want
the ioctls for creating/removing the block device to be synchronous,
and, should an error occur, we want to return the error code to the
user-space. So the existing notifications mechanism does not work well.
... but we can change it a little and have error codes handling, this is
just UB implementation issues.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Arnd Bergmann
2011-09-08 15:30:02 UTC
Permalink
Post by Artem Bityutskiy
Not sure about the bus approach - David, could you take a look at it
please? If we can handle errors there - then we could indeed re-use the
UBI control device. We could even re-use the ioctl data structures for
UBI volumes creation/removal - we have plenty of space there reserved
for future extensions.
I would generally recommend using new ioctl commands. ioctl numbers
are cheap, but complexity in data structures is not, because every
user who wants to deal with the data structures has to understand
them. Also, changing the ABI is always tricky since you have to
provide backward and forwards compatibility with existing kernels
and with existing user space.

Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-09 12:00:03 UTC
Permalink
Post by Arnd Bergmann
Post by Artem Bityutskiy
Not sure about the bus approach - David, could you take a look at it
please? If we can handle errors there - then we could indeed re-use the
UBI control device. We could even re-use the ioctl data structures for
UBI volumes creation/removal - we have plenty of space there reserved
for future extensions.
I would generally recommend using new ioctl commands. ioctl numbers
are cheap, but complexity in data structures is not, because every
user who wants to deal with the data structures has to understand
them. Also, changing the ABI is always tricky since you have to
provide backward and forwards compatibility with existing kernels
and with existing user space.
Hmm, what do we do if ubiblk module is not loaded, and UBI would have
to return an error (because the block device cannot be created), how
will UBI know that ubiblk is not there? Any direct call to ubiblk from
UBI would be a direct dependency and would require ubiblk to be always
loaded, which is bad.

IOW, we need a blocking mechanism to call the upper layer's function
(ubiblk) from the lower layer (UBI) which can return an error, and which
allows to check if a ubiblk exists at all. Do we have such mechanism?

Actually the fact of invoking upper layers from lower makes me worry.

And yes, the "bus"<->"devices" model does not look suitable in our case.
We have more like hierarchy: ubiblk uses ubi which usese mtd device.

David, I am really busy and now, I suggest you to think about this. I'd
so far stick to the own ubiblk cdev approach, and would
analyse/prototype the approach of using UBI cdev for this. I provided
some concerns above. Also, think about race conditions like:

1. Someone
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-09 12:10:01 UTC
Permalink
Post by Artem Bityutskiy
David, I am really busy and now, I suggest you to think about this. I'd
so far stick to the own ubiblk cdev approach, and would
analyse/prototype the approach of using UBI cdev for this. I provided
1. Someone
Sorry, I wonted to talk about situations when someone opens an ubiblk
device while the underlying UBI volume is being removed, but then though
this is trivial and forgot to erase the last sentence.

Anyway, I suggest the following algorithm:

1. Stick with the own cdev approach - the driver becomes very simple
in this case - we review it.
2. Once it is ready, or in parallel, you can play with the second
approach and if you find it solid/nice, you can then provide a
new version and/or the delta.

The idea is that on step 1 we review/deal with other things, without
being blocked by the ioctl stuff.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Arnd Bergmann
2011-09-09 14:30:01 UTC
Permalink
Post by Artem Bityutskiy
Post by Arnd Bergmann
Post by Artem Bityutskiy
Not sure about the bus approach - David, could you take a look at it
please? If we can handle errors there - then we could indeed re-use the
UBI control device. We could even re-use the ioctl data structures for
UBI volumes creation/removal - we have plenty of space there reserved
for future extensions.
I would generally recommend using new ioctl commands. ioctl numbers
are cheap, but complexity in data structures is not, because every
user who wants to deal with the data structures has to understand
them. Also, changing the ABI is always tricky since you have to
provide backward and forwards compatibility with existing kernels
and with existing user space.
Hmm, what do we do if ubiblk module is not loaded, and UBI would have
to return an error (because the block device cannot be created), how
will UBI know that ubiblk is not there? Any direct call to ubiblk from
UBI would be a direct dependency and would require ubiblk to be always
loaded, which is bad.
No, the idea of this approach is that the main ubi driver creates
a device, which can always succeed. It's just that there won't
be a block device node created, because that is part of what
the ubiblk driver does.

Compare this to how scsi works:

A scsi host driver scans the host controller and adds scsi devices
internal to the kernel, each of them have a specific type (disk,
tape, ...). If the scsi disk driver is loaded, it will create
a blockdev for each disk device. It doesn't matter in which order
the drivers are loaded though.

In case of ubiblk, it's similar, except that there is no way for
the ubi layer to know if some partition should be a block device or
not, so it relies on user space to tell it.

Well, actually, you /could/ encode this somewhere so that the main
ubi layer creates different kinds of devices based on what it finds:
a ubiblk_device when it finds a partition that was created as a
block device or gluebi_device for gluebi or a ubifs volume.
Post by Artem Bityutskiy
IOW, we need a blocking mechanism to call the upper layer's function
(ubiblk) from the lower layer (UBI) which can return an error, and which
allows to check if a ubiblk exists at all. Do we have such mechanism?
Actually the fact of invoking upper layers from lower makes me worry.
Yes, you should not do that.

Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-09 15:30:01 UTC
Permalink
Post by Arnd Bergmann
Post by Artem Bityutskiy
Post by Arnd Bergmann
Post by Artem Bityutskiy
Not sure about the bus approach - David, could you take a look at it
please? If we can handle errors there - then we could indeed re-use the
UBI control device. We could even re-use the ioctl data structures for
UBI volumes creation/removal - we have plenty of space there reserved
for future extensions.
I would generally recommend using new ioctl commands. ioctl numbers
are cheap, but complexity in data structures is not, because every
user who wants to deal with the data structures has to understand
them. Also, changing the ABI is always tricky since you have to
provide backward and forwards compatibility with existing kernels
and with existing user space.
Hmm, what do we do if ubiblk module is not loaded, and UBI would have
to return an error (because the block device cannot be created), how
will UBI know that ubiblk is not there? Any direct call to ubiblk from
UBI would be a direct dependency and would require ubiblk to be always
loaded, which is bad.
No, the idea of this approach is that the main ubi driver creates
a device, which can always succeed. It's just that there won't
be a block device node created, because that is part of what
the ubiblk driver does.
A scsi host driver scans the host controller and adds scsi devices
internal to the kernel, each of them have a specific type (disk,
tape, ...). If the scsi disk driver is loaded, it will create
a blockdev for each disk device. It doesn't matter in which order
the drivers are loaded though.
In case of ubiblk, it's similar, except that there is no way for
the ubi layer to know if some partition should be a block device or
not, so it relies on user space to tell it.
There is another difference, though. Disk types are persistent. UBI
volumes types are not necessarily persistent:

I may create a "just UBI volume", flash something there, may-be re-size,
do other things.

Then I want to "turn" it into a "ubiblk" volume without destroying it.
So I call the corresponding ioctl. And my requirements are:

1) if ubiblk is not loaded, I want to get -ENODEV or something
2) if ubiblk fails, I want to get -ESOMETHING as well.
3) I want to be blocked while ubiblk is creating the block device.

Similarly, I may want to ask ubiblk to "release" the volume and turn it
to "just a volume" again.

So I am not sure the analogy is very close, but thanks a lot for
providing it - it makes things clearer.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-09 14:40:02 UTC
Permalink
Post by Arnd Bergmann
Post by Artem Bityutskiy
Not sure about the bus approach - David, could you take a look at it
please? If we can handle errors there - then we could indeed re-use the
UBI control device. We could even re-use the ioctl data structures for
UBI volumes creation/removal - we have plenty of space there reserved
for future extensions.
* Use the existing UBI control device for the block devices as
well and just add two more ioctls to create the devices.
You can add a logical bus_type for this so that the ubi block
driver gets automatically loaded matched with the device when
one is created using the control device.
I certainly miss some background, I'm not sure I understand how this
works: bus_type seems suitable for pluggable devices that possess a
device ID which matches against a driver that will then get loaded. But
ubiblk devices are created by ubiblk.
So, are you suggesting to move ubiblk_create() to UBI and add a
MODULE_ALIAS to ubiblk (actually, I don't know what it would contain) ?

(I just saw that you sent an email while I was writing this one ;
however, I still understand. I'll try and read how scsi does that).
Post by Arnd Bergmann
Sorry, I wonted to talk about situations when someone opens an ubiblk
device while the underlying UBI volume is being removed, but then though
this is trivial and forgot to erase the last sentence.
Ah, yes, I guess we need to hold a vol_lock in ubiblk_remove() ?
Post by Arnd Bergmann
1. Stick with the own cdev approach - the driver becomes very simple
in this case - we review it.
This is the way it's implemented in v4, right ?

BTW, those are the changes made so far since v4:
* Add missing headers (they are included by other headers but it seems
to be good practice not to rely on that).

* Remove an macro rendered useless with the linked lists

* correct some formatting in kerneldoc comments

* introduce refcounting to avoid multiple opens or closing a UBI
volume while still in use

* make checkpatch happy about assignation inside a condition

* use DEFINE_MUTEX for devlist_lock


Best Regards,
David
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Arnd Bergmann
2011-09-09 15:00:01 UTC
Permalink
Post by David Wagner
Post by Arnd Bergmann
Post by Artem Bityutskiy
Not sure about the bus approach - David, could you take a look at it
please? If we can handle errors there - then we could indeed re-use the
UBI control device. We could even re-use the ioctl data structures for
UBI volumes creation/removal - we have plenty of space there reserved
for future extensions.
* Use the existing UBI control device for the block devices as
well and just add two more ioctls to create the devices.
You can add a logical bus_type for this so that the ubi block
driver gets automatically loaded matched with the device when
one is created using the control device.
I certainly miss some background, I'm not sure I understand how this
works: bus_type seems suitable for pluggable devices that possess a
device ID which matches against a driver that will then get loaded. But
ubiblk devices are created by ubiblk.
So, are you suggesting to move ubiblk_create() to UBI and add a
MODULE_ALIAS to ubiblk (actually, I don't know what it would contain) ?
Sort of: You definitely move some part of ubiblk into the control
driver, but that part can be relatively small. All devices on
the ubiblk bus would have the same type, so the same probe function
would always get called.
Post by David Wagner
Post by Arnd Bergmann
Sorry, I wonted to talk about situations when someone opens an ubiblk
device while the underlying UBI volume is being removed, but then though
this is trivial and forgot to erase the last sentence.
Ah, yes, I guess we need to hold a vol_lock in ubiblk_remove() ?
Most importantly, you need reference counting.

Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-11 10:20:02 UTC
Permalink
Post by David Wagner
Post by Artem Bityutskiy
1. Stick with the own cdev approach - the driver becomes very simple
in this case - we review it.
This is the way it's implemented in v4, right ?
Yes, but I though I sent you some feed back with minor things as well as
with major issue related to race condigions: you should keep the UBI
volume open as long as your block device is referenced, to prevent the
UBI volume from being removed.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-11 10:40:01 UTC
Permalink
Post by Artem Bityutskiy
Post by David Wagner
Post by Artem Bityutskiy
1. Stick with the own cdev approach - the driver becomes very simple
in this case - we review it.
This is the way it's implemented in v4, right ?
Yes, but I though I sent you some feed back with minor things as well as
with major issue related to race condigions: you should keep the UBI
volume open as long as your block device is referenced, to prevent the
UBI volume from being removed.
Ah, yes, of course. I made these changes and will send them monday.

Regards,
David.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
d***@free-electrons.com
2011-08-24 16:20:02 UTC
Permalink
From: David Wagner <***@free-electrons.com>

ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblk_X_Y, where X is the UBI device number
and Y the Volume ID).

It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.

It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.

Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the number of all
the MTD devices exposing UBI volumes will be incremented by one, which is a
bit confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.

* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)

TODO:
* the modules keeps a table of the devices which length is the maximum number
of UBI volumes. There should be a better solution (linked list or, as
Christoph Hellwig suggests, a radix tree (idr)).

Signed-off-by: David Wagner <***@free-electrons.com>
---

Hi,

changes since v3
~~~~~~~~~~~~~~~~

a renamed *thread to *req_task
b kerneldoc comments
c fix ubiblk-user.h integration in Kbuild
d use dev_* instead of pr_*
e fix a bug: it was possible to remove a opened device
f use linked lists instead of static table
even if idr provides an address space for minor numbers, we found no way to
use that feature: when we need to get the device from the idr structure, we
only have a ubi_volume_info at hand, so we don't know the minor number. A
solution might be, instead of getting a minor number from idr, to decide of
one (for instance, ubi_num * UBI_MAX_VOLUMES + vol_id, but if they were more
than one UBI device, that would make the idr structure grow needlessly.
Plus, there are few chances that the number of ubiblk devices be high, so
it's probably not worth the pain.
However, if we have time, we may be interessed to see how it could benefit
UBI.
g checkpatch'd (it still complains about an assignment in an if ()), is it
really bad ?


about e), I have a question: it was possible to unmount a mounted filesystem
because ot this. The fix we found was to return -EBUSY if the ubiblk device is
opened. But is it correct to assume that a mounted filesystem will always hold
the device opened ? If not, what is the correct way to impeach the removal of a
ubiblk device when it is "mounted" ?
(see ubiblk_remove(), if (dev->desc))

other questions:
is it ok not to return the error value from the notify function ? I found now
way to return an error value w/o stopping the notifications from being emitted.

there are error messages printed when a ubiblk_remove() is attempted on a
non-existing device - that happens a lot when a UBI device is detached ; should
the messages be debug-only (or even removed) ?


Best regards,
David.

Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 697 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 47 +++
6 files changed, 763 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..6758f00
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c
+ * (Artem Bityutskiy (БОтюцкОй АртёЌ), Joern Engel)
+ * Some code taken from cdev.c (Artem Bityutskiy (БОтюцкОй АртёЌ))
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+#define UBIBLK_MAX_DEVS (UBI_MAX_DEVICES * UBI_MAX_VOLUMES)
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static struct mutex devlist_lock;
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct list_head *list_ptr;
+
+ /* TODO: use list_for_each_entry ? */
+ list_for_each(list_ptr, &ubiblk_devs) {
+ dev = list_entry(list_ptr, struct ubiblk_dev, list);
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ break;
+ dev = NULL;
+ }
+ return dev;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector
+ *
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_request - wakes the processing thread
+ *
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor)
+ *
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err = 0;
+
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_err;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_err:
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor)
+ *
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+
+ kfree(dev->vi);
+ dev->vi = NULL;
+ if (dev->desc) {
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c
+ *
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req && !(req = blk_fetch_request(rq))) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int ret = 0;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (ubiblk_find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_devlist;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_devlist;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+
+ mutex_init(&dev->vol_lock);
+ mutex_lock(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ pr_err("ubi_open_volume failed\n");
+ ret = PTR_ERR(dev->desc);
+ goto out_vol;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_disk;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ set_capacity(gd,
+ (dev->vi->size *
+ dev->vi->usable_leb_size) >> 9);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_thread;
+ }
+
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ dev->vi->name);
+
+ kfree(dev->vi);
+ dev->vi = NULL;
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_disk:
+ kfree(dev->vi);
+ dev->vi = NULL;
+out_info:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_vol:
+ mutex_unlock(&dev->vol_lock);
+out_devlist:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev = NULL;
+
+ mutex_lock(&devlist_lock);
+
+ dev = ubiblk_find_dev(vi);
+
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ if (dev->desc) {
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+
+ list_del(&dev->list);
+ kfree(dev);
+
+ mutex_unlock(&devlist_lock);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = ubiblk_find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ set_capacity(dev->gd,
+ (vi->size * vi->usable_leb_size) >> 9);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications
+ * copied from gluebi.c
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
+ * @file: the file on which the ioctl was invoked (usunsed)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long ubiblk_ctrl_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned long translated_arg = (unsigned long)compat_ptr(arg);
+
+ return ubiblk_ctrl_ioctl(file, cmd, translated_arg);
+}
+#endif
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ubiblk_ctrl_compat_ioctl,
+#endif
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * ubi_ubiblk_init - initialize the module
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("can't register the major\n");
+ return -ENODEV;
+ }
+ ubiblk_major = ret;
+
+ mutex_init(&devlist_lock);
+
+ ret = misc_register(&ctrl_dev);
+ if (ret) {
+ pr_err("can't register control device\n");
+ goto out_unreg_blk;
+ }
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_ctrl;
+
+ pr_info("major=%d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_ctrl:
+ misc_deregister(&ctrl_dev);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+
+/*
+ * ubi_ubiblk_exit - end of life
+ *
+ * unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ struct list_head *list_ptr, *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_safe(list_ptr, next, &ubiblk_devs) {
+ dev = list_entry(list_ptr, struct ubiblk_dev, list);
+
+ /* TODO: it shouldn't happen, right ? */
+ if (dev->desc)
+ ubi_close_volume(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+ list_del(&dev->list); /* really needed ? */
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+ pr_info("end of life\n");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..61df415
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-08-24 16:20:02 UTC
Permalink
---
include/mtd/ubi-user.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
index 3c41097..7c8bfe3 100644
--- a/include/mtd/ubi-user.h
+++ b/include/mtd/ubi-user.h
@@ -188,6 +188,7 @@
/* Set an UBI volume property */
#define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \
struct ubi_set_vol_prop_req)
+/* If you add ioctls here, please note that ubiblk uses 'O'/0x10-0x11 */

/* Maximum MTD device name length supported by UBI */
#define MAX_UBI_MTD_NAME_LEN 127
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-06 05:00:01 UTC
Permalink
Post by David Wagner
---
include/mtd/ubi-user.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
index 3c41097..7c8bfe3 100644
--- a/include/mtd/ubi-user.h
+++ b/include/mtd/ubi-user.h
@@ -188,6 +188,7 @@
/* Set an UBI volume property */
#define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \
struct ubi_set_vol_prop_req)
+/* If you add ioctls here, please note that ubiblk uses 'O'/0x10-0x11 */
/* Maximum MTD device name length supported by UBI */
#define MAX_UBI_MTD_NAME_LEN 127
Let's postpone this patch and the user-space tool until ubiblk is ready
for inclusion. I sent you some comments - the main one is the block
device reference counting and making sure that the underlying UBI volume
does not disappear when you use it.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-06 05:00:01 UTC
Permalink
Post by d***@free-electrons.com
g checkpatch'd (it still complains about an assignment in an if ()), is it
really bad ?
Well, UBI does not do this, so for the sake of consistent style you
could fix those warnings.
Post by d***@free-electrons.com
about e), I have a question: it was possible to unmount a mounted filesystem
because ot this. The fix we found was to return -EBUSY if the ubiblk device is
opened. But is it correct to assume that a mounted filesystem will always hold
the device opened ? If not, what is the correct way to impeach the removal of a
ubiblk device when it is "mounted" ?
Once the block device is referenced (refcound is not zero), you should
open the underlying UBI volume (in write mode). And you close it only
when the block device's reference count becomes zero, just like gluebi
does this. So you need to enhance your ubiblk_open() and release
functions.

This should make sure that no one can remove the underlying UBI volume
when the corresponding ubiblk device is being used (e.g., mounted).
Post by d***@free-electrons.com
(see ubiblk_remove(), if (dev->desc))
is it ok not to return the error value from the notify function ? I found now
way to return an error value w/o stopping the notifications from being emitted.
there are error messages printed when a ubiblk_remove() is attempted on a
non-existing device - that happens a lot when a UBI device is detached ; should
the messages be debug-only (or even removed) ?
Not sure I got the question, is this an error situation? I see the
following possibilities:

1. UBI volume ubiX_Y is not used by ubiblk at all -> no issues
2. UBI volume ubiX_Y is has corresponding device ubiblkA, which is
unused, so UBI volume can be removed, which causes a notifier,
which removes ubiblkA.
3. ubiX_Y has corresponding ubiblkA, which is used, which means ubiX_Y
is still open, which means it cannot be removed, no issues.
Post by d***@free-electrons.com
+/**
+ * ubi_ubiblk_init - initialize the module
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0) {
+ pr_err("can't register the major\n");
Not very user-friendly message - I do not think it is very readable and
easy to understand. Saying "cannot register block device" would be
readable. But I'd better just not print anything. And the function
internally has some error-path prints.
Post by d***@free-electrons.com
+ return -ENODEV;
Why do you override the error code returned by 'register_blkdev()'?
Post by d***@free-electrons.com
+ }
+ ubiblk_major = ret;
+
+ mutex_init(&devlist_lock);
Isn't it cleaner to initialize the global mutex when declaring it -
there is a macros to do this?
Post by d***@free-electrons.com
+
+ ret = misc_register(&ctrl_dev);
+ if (ret) {
+ pr_err("can't register control device\n");
+ goto out_unreg_blk;
+ }
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_ctrl;
+
+ pr_info("major=%d\n", ubiblk_major);
Please, print something nicer, e.g.,

"control device major number is %d"
Post by d***@free-electrons.com
+
+ return ret;
+
+ misc_deregister(&ctrl_dev);
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-12 09:50:02 UTC
Permalink
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).

It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.

It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.

Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the index of all the
MTD devices exposing UBI volumes will be shifted by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.

* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)

Signed-off-by: David Wagner <***@free-electrons.com>
---

changes since v4:
~~~~~~~~~~~~~~~~~

* Add missing headers (they are included by other headers but it seems to be
good practice not to rely on that).

* Remove an macro rendered useless with the linked lists

* correct some formatting in kerneldoc comments

* introduce refcounting to avoid multiple opens or closing a UBI volume while
still in use

* make checkpatch happy about assignation inside a condition

* use DEFINE_MUTEX for devlist_lock

plan for v6:
~~~~~~~~~~~~

* Use list_for_each_entry

* Dig into Arnd's suggestions, understand it and see if/how it could be done

* If it isn't rendered obsolete by the previous point, add a kernel parameter
for creating ubiblk devices at boot time (in order to have a rootfs on a
ubiblk device)

Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 724 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 47 +++
6 files changed, 790 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..6abf76b
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,724 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c
+ * (Artem Bityutskiy (БОтюцкОй АртёЌ), Joern Engel)
+ * Some code taken from cdev.c (Artem Bityutskiy (БОтюцкОй АртёЌ))
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @refcnt: reference counter (increases with open(), decreases with release())
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static DEFINE_MUTEX(devlist_lock);
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct list_head *list_ptr;
+
+ /* TODO: use list_for_each_entry ? */
+ list_for_each(list_ptr, &ubiblk_devs) {
+ dev = list_entry(list_ptr, struct ubiblk_dev, list);
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ break;
+ dev = NULL;
+ }
+ return dev;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector
+ *
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_request - wakes the processing thread
+ *
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor)
+ *
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err = 0;
+
+ mutex_lock(&dev->vol_lock);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already opened ; just increase the reference
+ * counter.
+ */
+ dev->refcnt++;
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_lock;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ dev->refcnt++;
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_lock:
+ mutex_unlock(&dev->vol_lock);
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor)
+ *
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ }
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c
+ *
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req)
+ req = blk_fetch_request(rq);
+ if (!req) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int ret = 0;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (ubiblk_find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_devlist;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_devlist;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+
+ mutex_init(&dev->vol_lock);
+ mutex_lock(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ pr_err("ubi_open_volume failed\n");
+ ret = PTR_ERR(dev->desc);
+ goto out_vol;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_disk;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ set_capacity(gd,
+ (dev->vi->size *
+ dev->vi->usable_leb_size) >> 9);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_thread;
+ }
+
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ dev->vi->name);
+
+ kfree(dev->vi);
+ dev->vi = NULL;
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_disk:
+ kfree(dev->vi);
+ dev->vi = NULL;
+out_info:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_vol:
+ mutex_unlock(&dev->vol_lock);
+out_devlist:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev = NULL;
+
+ mutex_lock(&devlist_lock);
+
+ dev = ubiblk_find_dev(vi);
+
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ if (dev->desc) {
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+
+ list_del(&dev->list);
+ kfree(dev);
+
+ mutex_unlock(&devlist_lock);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = ubiblk_find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ set_capacity(dev->gd,
+ (vi->size * vi->usable_leb_size) >> 9);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications
+ * copied from gluebi.c
+ *
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
+ *
+ * @file: the file on which the ioctl was invoked (usunsed)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long ubiblk_ctrl_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned long translated_arg = (unsigned long)compat_ptr(arg);
+
+ return ubiblk_ctrl_ioctl(file, cmd, translated_arg);
+}
+#endif
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ubiblk_ctrl_compat_ioctl,
+#endif
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * ubi_ubiblk_init - initialize the module
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0)
+ return ret;
+ ubiblk_major = ret;
+
+ mutex_lock(&devlist_lock);
+
+ ret = misc_register(&ctrl_dev);
+ if (ret) {
+ pr_err("can't register control device\n");
+ goto out_unreg_blk;
+ }
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_ctrl;
+
+ mutex_unlock(&devlist_lock);
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_ctrl:
+ misc_deregister(&ctrl_dev);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ mutex_unlock(&devlist_lock);
+ return ret;
+}
+
+/**
+ * ubi_ubiblk_exit - end of life
+ *
+ * unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ struct list_head *list_ptr, *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_safe(list_ptr, next, &ubiblk_devs) {
+ dev = list_entry(list_ptr, struct ubiblk_dev, list);
+
+ /* TODO: it shouldn't happen, right ? */
+ if (dev->desc)
+ ubi_close_volume(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+ list_del(&dev->list); /* really needed ? */
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+ pr_info("end of life\n");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..61df415
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-19 04:50:01 UTC
Permalink
Hi David,

some review below.
Post by d***@free-electrons.com
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor)
+ *
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ }
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
Looks buggy. I'd expect this function to remove the device from the
list as well as free it, as well as hold the devlist_mutex...


...
Post by d***@free-electrons.com
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume
+ *
+ *
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int ret = 0;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (ubiblk_find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_devlist;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_devlist;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+
+ mutex_init(&dev->vol_lock);
+ mutex_lock(&dev->vol_lock);
I think upi do not need to take vol_lock. You are creating the volume,
and no one can open it anyway because you have 'devlist_lock' now.
...
Post by d***@free-electrons.com
+ set_capacity(gd,
+ (dev->vi->size *
+ dev->vi->usable_leb_size) >> 9);
A temporary variable would be neater.
Post by d***@free-electrons.com
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
s/Stolen/borrowed/ ?
...
Post by d***@free-electrons.com
+/**
+ * ubiblk_remove - destroy a ubiblk device
+ *
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev = NULL;
+
+ mutex_lock(&devlist_lock);
+
+ dev = ubiblk_find_dev(vi);
+
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ if (dev->desc) {
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
Racy. Your dev->desc is protected by the "vol_lock", here you did not
take it, but you should. And then you can release it after
"list_del(&dev->list)".
Post by d***@free-electrons.com
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+
+ list_del(&dev->list);
+ kfree(dev);
+
+ mutex_unlock(&devlist_lock);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
...
Post by d***@free-electrons.com
+#ifdef CONFIG_COMPAT
+static long ubiblk_ctrl_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned long translated_arg = (unsigned long)compat_ptr(arg);
+
+ return ubiblk_ctrl_ioctl(file, cmd, translated_arg);
+}
+#endif
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ubiblk_ctrl_compat_ioctl,
+#endif
+ .llseek = no_llseek,
+};
You do not need compat ioctl. This is needed only for old code which
does nasty things like using pointers in ioctl data structures. The idea
is that new code uses proper ioctl data structures instead. Please, kill
this.

...
Post by d***@free-electrons.com
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0)
+ return ret;
Error code "0" means success, so if you return 0 here, you'll confuse
the upper layers.

...
Post by d***@free-electrons.com
+static void __exit ubi_ubiblk_exit(void)
+{
+ struct list_head *list_ptr, *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_safe(list_ptr, next, &ubiblk_devs) {
+ dev = list_entry(list_ptr, struct ubiblk_dev, list);
+
+ /* TODO: it shouldn't happen, right ? */
+ if (dev->desc)
+ ubi_close_volume(dev->desc);
Since you are using mutexes now, can you remove the TODO line? If you
think "if (dev->desc)" is still needed, then please, add WARN_ON() in
the "else" part, or just kill the "if" part.
Post by d***@free-electrons.com
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+ list_del(&dev->list); /* really needed ? */
Good question, I think we should not have comments like this in the
final driver.
Post by d***@free-electrons.com
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+ pr_info("end of life\n");
This funny but let's remove it please.
Post by d***@free-electrons.com
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
....
Post by d***@free-electrons.com
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
__u8[8] padding;
Post by d***@free-electrons.com
+} __packed;
Please, add paddings as suggested above.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-22 08:00:02 UTC
Permalink
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).

It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.

It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.

Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the index of all the
MTD devices exposing UBI volumes will be shifted by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.

* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.

* It is also possible to specify a "volume" parameter in order to create a
ubiblk device at init time. This makes possible to put a rootfs on a ubiblk
device.

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)

Signed-off-by: David Wagner <***@free-electrons.com>
Cc: Artem Bityutskiy <***@gmail.com>
Cc: Arnd Bergmann <***@arndb.de>
---

changelog since v5:
~~~~~~~~~~~~~~~~~~~

* Use list_for_each_entry instead of list_for_each

* Add a "volume" parameter for creating a device at init time. It allows to
put a rootfs on the volume. Format: volume=<ubi_num>:<vol_id>

* Locking: remove useless locks and add necessary new locks

* Remove the CONFIG_COMPAT code

* Don't walk the error path if register_blkdev returns 0

* WARN when a ubiblk device still has an open descriptor at exit time (happens
with rmmod -f).

* Use temporary variables for readability

* Add dynamic debug regarding refcounting

* Remove a useless check in ubiblk_create (the test consisted in opening the
proxified volume)

* In ubiblk_create(), directly use vi (passed as argument) instead of dev->vi

* No need to remove the devices from the linked list at exit time: it doesn't
free any memory

* Add padding in the ioctl data structure

* remove a useless kfree(dev->vi) in ubiblk_remove: the refcounting assures
that it has been freed before (in ubiblk_release). Same in exit.


@Artem regarding ubiblk_release:
it is only called when the device is closed (eg, unmounted), not when it is
removed (eg the volume disappears or the user send an ioctl to remove it). The
function responsible for removing a device (and thus removing it from the linked
list) is ubiblk_remove.

Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 751 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 49 +++
6 files changed, 819 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..d22ae4f
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,751 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c
+ * (Artem Bityutskiy (БОтюцкОй АртёЌ), Joern Engel)
+ * Some code taken from cdev.c (Artem Bityutskiy (БОтюцкОй АртёЌ))
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @refcnt: reference counter (increases with open(), decreases with release())
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static DEFINE_MUTEX(devlist_lock);
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+#define VOL_PARAM_MAXLEN 8
+static char *volume;
+module_param(volume, charp, 0000);
+MODULE_PARM_DESC(volume,
+ "Format: volume=<ubi_num>:<vol_id>\n"
+ "Create a ubiblk device at init time. Useful for mounting it as root "
+ "device.");
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ list_for_each_entry(dev, &ubiblk_devs, list) {
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ return dev;
+ }
+ return NULL;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector
+ *
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_request - wakes the processing thread
+ *
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor)
+ *
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err = 0;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "open(); refcnt = %d\n", dev->refcnt);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already opened ; just increase the reference
+ * counter.
+ */
+ dev->refcnt++;
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_lock;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ dev->refcnt++;
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_lock:
+ mutex_unlock(&dev->vol_lock);
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor)
+ *
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "release(); refcnt = %d\n", dev->refcnt);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+ }
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c
+ *
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req)
+ req = blk_fetch_request(rq);
+ if (!req) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int disk_capacity;
+ int ret = 0;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (ubiblk_find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_lock;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_lock;
+ }
+
+ mutex_init(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_lock;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(gd, disk_capacity);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Borrowed from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_thread;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ vi->name);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_lock:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev = NULL;
+
+ mutex_lock(&devlist_lock);
+
+ dev = ubiblk_find_dev(vi);
+
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ if (dev->desc) {
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ list_del(&dev->list);
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+
+ kfree(dev);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ int disk_capacity;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = ubiblk_find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(dev->gd, disk_capacity);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications
+ * copied from gluebi.c
+ *
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
+ *
+ * @file: the file on which the ioctl was invoked (usunsed)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * ubiblk_parse_volume_param - parse the "volume" module parameter
+ *
+ * @ubi_num: where to store the UBI device number
+ * @vol_id: where to store the volume ID
+ */
+static int __init ubiblk_parse_volume_param(int *ubi_num, int *vol_id)
+{
+ char *tokens[2] = {NULL, NULL};
+ char buf[VOL_PARAM_MAXLEN];
+ char *pbuf = buf;
+ int len = strlen(buf);
+ int err;
+
+ if (len > VOL_PARAM_MAXLEN || len == 0)
+ return -1;
+
+ strcpy(buf, volume);
+ tokens[0] = strsep(&pbuf, ":");
+ tokens[1] = strsep(&pbuf, ":");
+
+ if (pbuf)
+ return -1; /* There are surnumerous parameters */
+
+ err = kstrtoint(tokens[0], 10, ubi_num);
+ if (err < 0 || *ubi_num < 0)
+ return -1;
+
+ err = kstrtoint(tokens[1], 10, vol_id);
+ if (err < 0 || *vol_id < 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * ubiblk_inittime_volume - create a volume at init time
+ *
+ * If the user passed a "ubiblk.volume" argument, check it and create the said
+ * volume.
+ */
+static void __init ubiblk_inittime_volume(void)
+{
+ int ubi_num, vol_id;
+ int err;
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+
+ if (ubiblk_parse_volume_param(&ubi_num, &vol_id) < 0) {
+ pr_err("cannot parse the volume parameter");
+ return;
+ }
+
+ desc = ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ pr_err("opening ubi%d:%d failed: %ld\n", ubi_num, vol_id,
+ PTR_ERR(desc));
+ return;
+ }
+ ubi_get_volume_info(desc, &vi);
+
+ err = ubiblk_create(&vi);
+ if (err < 0) {
+ pr_err("can't create the initial device "
+ "ubiblk%d_%d: %d\n", ubi_num, vol_id, err);
+ }
+ ubi_close_volume(desc);
+}
+
+/**
+ * ubi_ubiblk_init - initialize the module
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret < 0)
+ return ret;
+ ubiblk_major = ret;
+
+ /* Check if the user wants a volume to be proxified at init time */
+ if (volume)
+ ubiblk_inittime_volume();
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_blk;
+
+ ret = misc_register(&ctrl_dev);
+ if (ret) {
+ pr_err("can't register control device\n");
+ goto out_unreg_notifier;
+ }
+
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_notifier:
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+
+/**
+ * ubi_ubiblk_exit - end of life
+ *
+ * unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ struct ubiblk_dev *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_entry_safe(dev, next, &ubiblk_devs, list) {
+ /* The module is being forcefully removed */
+ WARN_ON(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..96858c7
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @padding: reserved for future, must contain zeroes
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+ __u8 padding[8];
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-23 11:00:01 UTC
Permalink
Post by David Wagner
+MODULE_PARM_DESC(volume,
+ "Format: volume=<ubi_num>:<vol_id>\n"
+ "Create a ubiblk device at init time. Useful for mounting it as root "
+ "device.");
I encourage people to use names, not IDs, because names are persistent,
while IDs may change when the configuration changes.

Take a look at UBI and how it handles the "mtd=" parameter. Do something
similar when you are parsing vol_id. IOW, I, as a user, would want to be
able to do:

ubimkvol -N "kuku" ...
modprobe ubiblk volume=0:kuku

(specify volume name "kuku").
Post by David Wagner
+/**
+ * ubiblk_inittime_volume - create a volume at init time
Please, stick to the same rule as UBI is using:
1. If the function is non-static, always add an ubiblk prefix
2. If the function is static, but it just makes sense to start with
ubiblk_ prefix, fine. For example, ubiblk_init().
3. Otherwise, no need to add ubiblk_ prefix for a static function.

IOW, please, do not automatically prefix all your functions with ubiblk_
prefix, especially when it brings no value and makes names longer.

I think this function is one of those which does not need that prefix.
Post by David Wagner
+/**
+ * ubi_ubiblk_init - initialize the module
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device
Nitpick: please, for consistency, put dots at the end of description.
Post by David Wagner
+ */
+static int __init ubi_ubiblk_init(void)
Name it ubiblk_init() please.
Post by David Wagner
+{
+ int ret = 0;
The initialization is not needed.
Post by David Wagner
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret < 0)
+ return ret;
+ ubiblk_major = ret;
+
+ /* Check if the user wants a volume to be proxified at init time */
+ if (volume)
+ ubiblk_inittime_volume();
If you fail to create the block device the user requested using the
module parameters, you should exit with error code. IOW,
'ubiblk_inittime_volume()' has to return an error on failure.
Post by David Wagner
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_blk;
If you previously created a block device in 'ubiblk_inittime_volume()',
and fail here, the error path will not destroy the block device, but it
should. IOW, you have a problem in your error path here.
Post by David Wagner
+
+ ret = misc_register(&ctrl_dev);
+ if (ret) {
+ pr_err("can't register control device\n");
+ goto out_unreg_notifier;
+ }
+
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
if (volumes)
ubiblk_destroy(xxx);

or something like that is needed here.
Post by David Wagner
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+/**
+ * ubi_ubiblk_exit - end of life
+ *
+ * unregister the block device major, unregister from UBI notifications,
Nitpick: please, start description with capital letter, for consistency.
Post by David Wagner
+ * unregister the ioctl handler device stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
Please, name it simply 'ubiblk_exit()' instead.
Post by David Wagner
+{
+ struct ubiblk_dev *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_entry_safe(dev, next, &ubiblk_devs, list) {
+ /* The module is being forcefully removed */
+ WARN_ON(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev);
1. I think you should have an "ubiblk_destroy()" function, symmetric to
the "ubiblk_create()". And you'll also use it in the error path of the
init function, see above.

2. Please, could you explain what prevents the following crash/issue:

modprobe ubiblk volumes=0:0
mkfs.ext3 /dev/ubiblk0
mount -t ext3 /dev/ubiblk0 /mnt/fs
rmmod ubiblk

Not that I think this is a problem, I just do not realize what would
prevent ubiblk from being unloaded when it is mounted. Did you test this
scenario?
Post by David Wagner
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
Since this will be living in the UBI directory and be kind of part of
UBI sources, this comment is probably not needed. The same for the
ubiblk.c file.
Post by David Wagner
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
Nitpick: please, let's be consistent with the rest of the UBI code and
put dot at the end of the "heading" line of the kernel-doc comments for
structures, and functions as well.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-26 13:00:02 UTC
Permalink
Hi,

On 09/23/2011 12:58 PM, Artem Bityutskiy wrote:
[...]
Post by Artem Bityutskiy
modprobe ubiblk volumes=0:0
mkfs.ext3 /dev/ubiblk0
mount -t ext3 /dev/ubiblk0 /mnt/fs
rmmod ubiblk
Not that I think this is a problem, I just do not realize what would
prevent ubiblk from being unloaded when it is mounted. Did you test this
scenario?
I forgot to address this in the v7, so:

The kernel has an internal refcounter for each module. It increases
with each module that uses it and for each open device owned by it.

In the case of ubiblk, we have:
static const struct block_device_operations ubiblk_ops = {
.owner = THIS_MODULE,
.open = ubiblk_open,
.release = ubiblk_release,
};

The "owner" field makes ubiblk devices owned by ubiblk, so each open
device increases the refcounting and if the user tries to rmmod ubiblk
when there are still open ubiblk devices, it will get an EBUSY error.


Regards,
David.
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Ricard Wanderlof
2011-09-26 09:30:02 UTC
Permalink
Post by d***@free-electrons.com
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
...
I decided to try this out, as I've been looking for a flexible
block-device-on-ubi scheme for using a cramfs or squashfs root file system
over UBI without having to resort to gluebi (which in its current
implementation is too inflexible for my needs).

This can't be considered a complete test, as I basically just booted the
system a couple of times, using a cramfs file system residing in an UBI
volume. I compiled ubiblk built in to the kernel, not as a module;
furthermore, I'm running 2.6.35 for various reasons, so had to backport
kstrtoul from 2.6.39 in order to get it to build.

But for what it's worth, it does confirm that it in fact works to set up a
ubiblk device at boot time for the root filesystem.

There doesn't seem to be a userspace application for attaching/detaching
ubiblk devices. I hacked together something based on ubinfo, I could post
it, but perhaps something more thorough is forthcoming?
Post by d***@free-electrons.com
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
+ *
^^^^^^^ typo: unused
Post by d***@free-electrons.com
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+/**
+ * ubiblk_parse_volume_param - parse the "volume" module parameter
+ *
+ */
+static int __init ubiblk_parse_volume_param(int *ubi_num, int *vol_id)
+{
+ char *tokens[2] = {NULL, NULL};
+ char buf[VOL_PARAM_MAXLEN];
+ char *pbuf = buf;
+ int len = strlen(buf);
^^^ this needs to be volume, not buf.

/Ricard
--
Ricard Wolf Wanderlöf ricardw(at)axis.com
Axis Communications AB, Lund, Sweden www.axis.com
Phone +46 46 272 2016 Fax +46 46 13 61 30
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-26 12:40:01 UTC
Permalink
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).

It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.

It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.

Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the index of all the
MTD devices exposing UBI volumes will be shifted by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.

* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.

* It is also possible to specify a "volume" parameter in order to create a
ubiblk device at init time. This makes possible to put a rootfs on a ubiblk
device. Format: "<ubi device number>:<volume name>"

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)

Signed-off-by: David Wagner <***@free-electrons.com>
Cc: Artem Bityutskiy <***@gmail.com>
Cc: Arnd Bergmann <***@arndb.de>
---

changes since v6:
~~~~~~~~~~~~~~~~~
For convenience the v6/v7 diff can be seen at
http://code.bulix.org/isds0w-80618?raw

* "volume" parameter: pass the volume name instead of the volume ID

* Change the initialization order: now, the inittime volume is created
at the very end of the init: it avoids creating an error path.

* Add error management in the parsing

* Homogenize the name of the error path labels

* Remove the "ubiblk_" prefix of some functions and rename some

* Correct the format of the kerneldoc comments and homogenize with the
rest of UBI

* Remove useless initializations and comments


@Ricard Wanderlof:
Thanks for testing and for your feedback. I just saw that you were missing the
ability to use the volume name instead of the volume ID ; that also was a
request from Artem, so here it is. Thanks for spotting the typo as well.
As for the userland tools, there is a separate patch against linux-mtd. The
latest version isn't in any mailing-list yet but I pasted it for you at
http://code.bulix.org/4iz8u3-80619?raw


Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 748 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 48 +++
6 files changed, 815 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..41f47b2
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+#include "ubi-media.h"
+
+#define BLK_SIZE 512
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @refcnt: reference counter (increases with open(), decreases with release())
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static DEFINE_MUTEX(devlist_lock);
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+/* +3 is for the separator and the UBI device num */
+#define VOL_PARAM_MAXLEN (UBI_VOL_NAME_MAX + 3)
+static char *volume;
+module_param(volume, charp, 0000);
+MODULE_PARM_DESC(volume,
+ "Format: volume=<ubi_num>:<vol_id>\n"
+ "Create a ubiblk device at init time. Useful for mounting it as root "
+ "device.");
+
+static struct ubiblk_dev *find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ list_for_each_entry(dev, &ubiblk_devs, list) {
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ return dev;
+ }
+ return NULL;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector.
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubiblk_request - wakes the processing thread
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor).
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "open(); refcnt = %d\n", dev->refcnt);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already opened ; just increase the reference
+ * counter.
+ */
+ dev->refcnt++;
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_unlock;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ dev->refcnt++;
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_unlock:
+ mutex_unlock(&dev->vol_lock);
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor).
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "release(); refcnt = %d\n", dev->refcnt);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+ }
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c.
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req)
+ req = blk_fetch_request(rq);
+ if (!req) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume.
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int disk_capacity;
+ int ret;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_unlock;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ mutex_init(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_free_dev;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(gd, disk_capacity);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_put_disk;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Borrowed from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_cleanup_queue;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ vi->name);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_cleanup_queue:
+ blk_cleanup_queue(dev->rq);
+out_put_disk:
+ put_disk(dev->gd);
+out_free_dev:
+ kfree(dev);
+out_unlock:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device.
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device.
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devlist_lock);
+
+ dev = find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ if (dev->desc) {
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ list_del(&dev->list);
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+
+ kfree(dev);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device.
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly.
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ int disk_capacity;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(dev->gd, disk_capacity);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications.
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume.
+ * @file: the file on which the ioctl was invoked (unused)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * volume_param_parse - parse the "volume" module parameter.
+ * @ubi_num: where to store the UBI device number
+ * @vol_name: where to store the volume name (fixed lenght, at least
+ * UBI_VOL_NAME_MAX)
+ */
+static int __init volume_param_parse(int *ubi_num, char *vol_name)
+{
+ char *tokens[2] = {NULL, NULL};
+ char buf[VOL_PARAM_MAXLEN + 1];
+ char *pbuf = buf;
+ int len = strlen(volume);
+ int err;
+
+ if (len > VOL_PARAM_MAXLEN || len == 0)
+ return -EINVAL;
+
+ strcpy(buf, volume);
+ tokens[0] = strsep(&pbuf, ":");
+ tokens[1] = strsep(&pbuf, ":");
+
+ if (pbuf)
+ return -EINVAL; /* There are surnumerous parameters */
+
+ err = kstrtoint(tokens[0], 10, ubi_num);
+ if (err < 0 || *ubi_num < 0)
+ return -EINVAL;
+
+ len = strlen(tokens[1]);
+ if (len > UBI_VOL_NAME_MAX || len == 0)
+ return -EINVAL;
+ strcpy(vol_name, tokens[1]);
+
+ return 0;
+}
+
+/**
+ * inittime_volume - create a volume at init time.
+ *
+ * If the user passed a "ubiblk.volume" argument, check it and create the said
+ * volume.
+ */
+static int __init inittime_volume(void)
+{
+ int ubi_num;
+ char vol_name[UBI_VOL_NAME_MAX + 1];
+ int err;
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+
+ err = volume_param_parse(&ubi_num, vol_name);
+ if (err < 0) {
+ pr_err("cannot parse the volume parameter");
+ return err;
+ }
+
+ desc = ubi_open_volume_nm(ubi_num, vol_name, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ pr_err("opening ubi%d:%s failed: %ld\n", ubi_num, vol_name,
+ PTR_ERR(desc));
+ return PTR_ERR(desc);
+ }
+ ubi_get_volume_info(desc, &vi);
+
+ err = ubiblk_create(&vi);
+ if (err < 0)
+ pr_err("can't create the initial device "
+ "ubiblk%d_%d: %d\n", ubi_num, vi.vol_id, err);
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/**
+ * ubiblk_init - initialize the module.
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device.
+ */
+static int __init ubiblk_init(void)
+{
+ int ret;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret < 0)
+ return ret;
+ ubiblk_major = ret;
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_blk;
+
+ ret = misc_register(&ctrl_dev);
+ if (ret < 0) {
+ pr_err("can't register control device\n");
+ goto out_unreg_notifier;
+ }
+
+ /* Check if the user wants a volume to be proxified at init time */
+ if (volume) {
+ ret = inittime_volume();
+ if (ret < 0)
+ goto out_unreg_misc;
+ }
+
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_misc:
+ misc_deregister(&ctrl_dev);
+out_unreg_notifier:
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+
+/**
+ * ubiblk_exit - end of life.
+ *
+ * Unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device, stop the threads and free the memory.
+ */
+static void __exit ubiblk_exit(void)
+{
+ struct ubiblk_dev *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_entry_safe(dev, next, &ubiblk_devs, list) {
+ /* The module is being forcefully removed */
+ WARN_ON(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+}
+
+module_init(ubiblk_init);
+module_exit(ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..61692d5
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @padding: reserved for future, must contain zeroes
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+ __u8 padding[8];
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-26 13:20:02 UTC
Permalink
Post by David Wagner
* "volume" parameter: pass the volume name instead of the volume ID
Sorry if I was not clear, but it should accept both name and number.
Take a look at UBI - it first checks if the string is a number, if yes -
tries to open by number, if this fails, tries to treat this as a name
and open by name.

And AFAICS, you did not change the documentation for the "volume" module
parameter.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-26 14:30:02 UTC
Permalink
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).

It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.

It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.

Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the index of all the
MTD devices exposing UBI volumes will be shifted by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.

* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.

* It is also possible to specify a "volume" parameter in order to create a
ubiblk device at init time. This makes possible to put a rootfs on a ubiblk
device. Format: "<ubi device number>:<volume name|volume ID>"

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)

Signed-off-by: David Wagner <***@free-electrons.com>
Cc: Artem Bityutskiy <***@gmail.com>
Cc: Arnd Bergmann <***@arndb.de>
---

changes since v7:
~~~~~~~~~~~~~~~~~

* It is now possible to pass the volume id OR the volume name in the "volume"
parameter. The two functions responsible for that have been reorganized and
renamed.

Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 755 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 48 +++
6 files changed, 822 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..32fc9d1
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+#include "ubi-media.h"
+
+#define BLK_SIZE 512
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @refcnt: reference counter (increases with open(), decreases with release())
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static DEFINE_MUTEX(devlist_lock);
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+/* +3 is for the separator and the UBI device num */
+#define VOL_PARAM_MAXLEN (UBI_VOL_NAME_MAX + 3)
+static char *volume;
+module_param(volume, charp, 0000);
+MODULE_PARM_DESC(volume,
+ "Format: volume=<ubi_num>:<vol_id>\n"
+ "Create a ubiblk device at init time. Useful for mounting it as root "
+ "device.");
+
+static struct ubiblk_dev *find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ list_for_each_entry(dev, &ubiblk_devs, list) {
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ return dev;
+ }
+ return NULL;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector.
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubiblk_request - wakes the processing thread
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor).
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "open(); refcnt = %d\n", dev->refcnt);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already opened ; just increase the reference
+ * counter.
+ */
+ dev->refcnt++;
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_unlock;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ dev->refcnt++;
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_unlock:
+ mutex_unlock(&dev->vol_lock);
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor).
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "release(); refcnt = %d\n", dev->refcnt);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+ }
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c.
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req)
+ req = blk_fetch_request(rq);
+ if (!req) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume.
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int disk_capacity;
+ int ret;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_unlock;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ mutex_init(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_free_dev;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(gd, disk_capacity);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_put_disk;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Borrowed from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_cleanup_queue;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ vi->name);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_cleanup_queue:
+ blk_cleanup_queue(dev->rq);
+out_put_disk:
+ put_disk(dev->gd);
+out_free_dev:
+ kfree(dev);
+out_unlock:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device.
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device.
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devlist_lock);
+
+ dev = find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ if (dev->desc) {
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ list_del(&dev->list);
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+
+ kfree(dev);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device.
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly.
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ int disk_capacity;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(dev->gd, disk_capacity);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications.
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume.
+ * @file: the file on which the ioctl was invoked (unused)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * volume_param_parse - parse the "volume" module parameter.
+ * @ubi_num: where to store the UBI device number
+ * @vol_name: where to store the volume name (fixed lenght, at least
+ * UBI_VOL_NAME_MAX)
+ */
+static struct ubi_volume_desc __init *inittime_volume_open(void)
+{
+ char *tokens[2] = {NULL, NULL};
+ char buf[VOL_PARAM_MAXLEN + 1];
+ char *pbuf = buf;
+
+ int len = strlen(volume);
+
+ int ubi_num, vol_id;
+ char vol_name[UBI_VOL_NAME_MAX + 1];
+ struct ubi_volume_desc *desc;
+
+ int err;
+
+ if (len > VOL_PARAM_MAXLEN || len == 0)
+ return ERR_PTR(-EINVAL);
+
+ strcpy(buf, volume);
+ tokens[0] = strsep(&pbuf, ":");
+ tokens[1] = strsep(&pbuf, ":");
+
+ if (pbuf)
+ return ERR_PTR(-EINVAL); /* There are surnumerous parameters */
+
+ err = kstrtoint(tokens[0], 10, &ubi_num);
+ if (err < 0 || ubi_num < 0)
+ return ERR_PTR(err);
+
+ len = strlen(tokens[1]);
+ if (len > UBI_VOL_NAME_MAX || len == 0)
+ return ERR_PTR(-EINVAL);
+ strcpy(vol_name, tokens[1]);
+
+ /* Try to open it by its name */
+ desc = ubi_open_volume_nm(ubi_num, vol_name, UBI_READONLY);
+ if (!IS_ERR(desc))
+ return desc;
+
+ /* Convert the vol_name string to int and try to open it by its ID */
+ err = kstrtoint(tokens[1], 10, &vol_id);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
+}
+
+/**
+ * inittime_volume - create a volume at init time.
+ *
+ * If the user passed a "ubiblk.volume" argument, check it and create the said
+ * volume.
+ */
+static int __init inittime_device(void)
+{
+ int err;
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+
+ desc = inittime_volume_open();
+ if (IS_ERR(desc)) {
+ pr_err("failed to open ubi%s: %ld\n", volume, PTR_ERR(desc));
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+ err = ubiblk_create(&vi);
+ if (err < 0)
+ pr_err("can't create the initial device "
+ "ubiblk%d_%d: %d\n", vi.ubi_num, vi.vol_id, err);
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/**
+ * ubiblk_init - initialize the module.
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device.
+ */
+static int __init ubiblk_init(void)
+{
+ int ret;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret < 0)
+ return ret;
+ ubiblk_major = ret;
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_blk;
+
+ ret = misc_register(&ctrl_dev);
+ if (ret < 0) {
+ pr_err("can't register control device\n");
+ goto out_unreg_notifier;
+ }
+
+ /* Check if the user wants a volume to be proxified at init time */
+ if (volume) {
+ ret = inittime_device();
+ if (ret < 0)
+ goto out_unreg_misc;
+ }
+
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_misc:
+ misc_deregister(&ctrl_dev);
+out_unreg_notifier:
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+
+/**
+ * ubiblk_exit - end of life.
+ *
+ * Unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device, stop the threads and free the memory.
+ */
+static void __exit ubiblk_exit(void)
+{
+ struct ubiblk_dev *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_entry_safe(dev, next, &ubiblk_devs, list) {
+ /* The module is being forcefully removed */
+ WARN_ON(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+}
+
+module_init(ubiblk_init);
+module_exit(ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..61692d5
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @padding: reserved for future, must contain zeroes
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+ __u8 padding[8];
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-09-26 14:40:01 UTC
Permalink
Post by David Wagner
+MODULE_PARM_DESC(volume,
+ "Format: volume=<ubi_num>:<vol_id>\n"
+ "Create a ubiblk device at init time. Useful for mounting it as root "
+ "device.");
Sorry, you still did not change the documentation. It should describe
what this parameter is. Now I see that it requires volume ID.
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Wagner
2011-09-26 14:50:02 UTC
Permalink
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).

It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.

It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.

Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.

Advantages of ubiblk over gluebi+mtdblock_ro:

* Simpler architecture

* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the index of all the
MTD devices exposing UBI volumes will be shifted by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.

* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.

* It is also possible to specify a "volume" parameter in order to create a
ubiblk device at init time. This makes possible to put a rootfs on a ubiblk
device. Format: "<ubi device number>:<volume name|volume ID>"

* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)

Signed-off-by: David Wagner <***@free-electrons.com>
Cc: Artem Bityutskiy <***@gmail.com>
Cc: Arnd Bergmann <***@arndb.de>
---

changes since v8:
~~~~~~~~~~~~~~~~~

* Update the module parameter description

Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 755 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 48 +++
6 files changed, 822 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.

+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o

ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..ccb22de
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <***@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+#include "ubi-media.h"
+
+#define BLK_SIZE 512
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @refcnt: reference counter (increases with open(), decreases with release())
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static DEFINE_MUTEX(devlist_lock);
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+/* +3 is for the separator and the UBI device num */
+#define VOL_PARAM_MAXLEN (UBI_VOL_NAME_MAX + 3)
+static char *volume;
+module_param(volume, charp, 0000);
+MODULE_PARM_DESC(volume,
+ "Format: volume=<ubi device number>:<volume name|volume id>\n"
+ "Create a ubiblk device at init time. Useful for mounting it as root "
+ "device.");
+
+static struct ubiblk_dev *find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ list_for_each_entry(dev, &ubiblk_devs, list) {
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ return dev;
+ }
+ return NULL;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector.
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubiblk_request - wakes the processing thread
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor).
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "open(); refcnt = %d\n", dev->refcnt);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already opened ; just increase the reference
+ * counter.
+ */
+ dev->refcnt++;
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_unlock;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ dev->refcnt++;
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_unlock:
+ mutex_unlock(&dev->vol_lock);
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor).
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "release(); refcnt = %d\n", dev->refcnt);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+ }
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c.
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req)
+ req = blk_fetch_request(rq);
+ if (!req) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume.
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int disk_capacity;
+ int ret;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_unlock;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ mutex_init(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_free_dev;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(gd, disk_capacity);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_put_disk;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Borrowed from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_cleanup_queue;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ vi->name);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_cleanup_queue:
+ blk_cleanup_queue(dev->rq);
+out_put_disk:
+ put_disk(dev->gd);
+out_free_dev:
+ kfree(dev);
+out_unlock:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device.
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device.
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ mutex_lock(&devlist_lock);
+
+ dev = find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ if (dev->desc) {
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ list_del(&dev->list);
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+
+ kfree(dev);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device.
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly.
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ int disk_capacity;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(dev->gd, disk_capacity);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications.
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume.
+ * @file: the file on which the ioctl was invoked (unused)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * volume_param_parse - parse the "volume" module parameter.
+ * @ubi_num: where to store the UBI device number
+ * @vol_name: where to store the volume name (fixed lenght, at least
+ * UBI_VOL_NAME_MAX)
+ */
+static struct ubi_volume_desc __init *inittime_volume_open(void)
+{
+ char *tokens[2] = {NULL, NULL};
+ char buf[VOL_PARAM_MAXLEN + 1];
+ char *pbuf = buf;
+
+ int len = strlen(volume);
+
+ int ubi_num, vol_id;
+ char vol_name[UBI_VOL_NAME_MAX + 1];
+ struct ubi_volume_desc *desc;
+
+ int err;
+
+ if (len > VOL_PARAM_MAXLEN || len == 0)
+ return ERR_PTR(-EINVAL);
+
+ strcpy(buf, volume);
+ tokens[0] = strsep(&pbuf, ":");
+ tokens[1] = strsep(&pbuf, ":");
+
+ if (pbuf)
+ return ERR_PTR(-EINVAL); /* There are surnumerous parameters */
+
+ err = kstrtoint(tokens[0], 10, &ubi_num);
+ if (err < 0 || ubi_num < 0)
+ return ERR_PTR(err);
+
+ len = strlen(tokens[1]);
+ if (len > UBI_VOL_NAME_MAX || len == 0)
+ return ERR_PTR(-EINVAL);
+ strcpy(vol_name, tokens[1]);
+
+ /* Try to open it by its name */
+ desc = ubi_open_volume_nm(ubi_num, vol_name, UBI_READONLY);
+ if (!IS_ERR(desc))
+ return desc;
+
+ /* Convert the vol_name string to int and try to open it by its ID */
+ err = kstrtoint(tokens[1], 10, &vol_id);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
+}
+
+/**
+ * inittime_volume - create a volume at init time.
+ *
+ * If the user passed a "ubiblk.volume" argument, check it and create the said
+ * volume.
+ */
+static int __init inittime_device(void)
+{
+ int err;
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+
+ desc = inittime_volume_open();
+ if (IS_ERR(desc)) {
+ pr_err("failed to open ubi%s: %ld\n", volume, PTR_ERR(desc));
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+ err = ubiblk_create(&vi);
+ if (err < 0)
+ pr_err("can't create the initial device "
+ "ubiblk%d_%d: %d\n", vi.ubi_num, vi.vol_id, err);
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/**
+ * ubiblk_init - initialize the module.
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device.
+ */
+static int __init ubiblk_init(void)
+{
+ int ret;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret < 0)
+ return ret;
+ ubiblk_major = ret;
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_blk;
+
+ ret = misc_register(&ctrl_dev);
+ if (ret < 0) {
+ pr_err("can't register control device\n");
+ goto out_unreg_notifier;
+ }
+
+ /* Check if the user wants a volume to be proxified at init time */
+ if (volume) {
+ ret = inittime_device();
+ if (ret < 0)
+ goto out_unreg_misc;
+ }
+
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_misc:
+ misc_deregister(&ctrl_dev);
+out_unreg_notifier:
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+
+/**
+ * ubiblk_exit - end of life.
+ *
+ * Unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device, stop the threads and free the memory.
+ */
+static void __exit ubiblk_exit(void)
+{
+ struct ubiblk_dev *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_entry_safe(dev, next, &ubiblk_devs, list) {
+ /* The module is being forcefully removed */
+ WARN_ON(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+}
+
+module_init(ubiblk_init);
+module_exit(ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..61692d5
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @padding: reserved for future, must contain zeroes
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+ __u8 padding[8];
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Artem Bityutskiy
2011-10-01 14:10:02 UTC
Permalink
Post by d***@free-electrons.com
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).
It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.
It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.
Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.
I do not have enough time to nicely answer with comments, so here is
just some patch with my cosmetic changes plus I added "TODO:" items here
and there. Please, apply it and resolve the TODO items, if you can, ok?

diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
index ccb22de..2da46fe 100644
--- a/drivers/mtd/ubi/ubiblk.c
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -40,7 +40,7 @@
#define BLK_SIZE 512

/**
- * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume.
* @desc: open UBI volume descriptor
* @vi: UBI volume information
* @ubi_num: UBI device number
@@ -49,25 +49,25 @@
* @gd: the disk (block device) created by ubiblk
* @rq: the request queue to @gd
* @req_task: the thread processing @rq requests
+TODO: vol_lock is bad name, not clean what it protects, the below comment is
+also vague
* @vol_lock: protects write access to the elements of this structure
- * @queue_lock: avoids concurrent accesses to the request queue
- * @list: linked list structure
+ * @queue_lock: protects the request queue
+ * @list: links &struct ubiblk_dev objects
*/
struct ubiblk_dev {
+/* TODO: let's name this structure ubiblk_info, to be consistent with UBI's
+ * naming conventions. */
struct ubi_volume_desc *desc;
struct ubi_volume_info *vi;
int ubi_num;
int vol_id;
int refcnt;
-
struct gendisk *gd;
struct request_queue *rq;
struct task_struct *req_task;
-
struct mutex vol_lock;
-
spinlock_t queue_lock;
-
struct list_head list;
};

@@ -105,24 +105,23 @@ static struct ubiblk_dev *find_dev(struct ubi_volume_info *vi)
}

/**
- * do_ubiblk_request - Read a LEB and fill the request buffer with the
- * requested sector.
+ * do_request - fill the request buffer by reading the UBI volume.
* @req: the request data structure
* @dev: the ubiblk device on which the request is issued
+ *
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
*/
-static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+static int do_request(struct request *req, struct ubiblk_dev *dev)
+/* TODO: if struct ubiblk_dev becomes struct ubiblk_info, how about to
+ * name all variables of this type "inf"? */
{
unsigned long start, len, read_bytes;
- int offset;
- int leb;
- int ret;
+ int offset, leb, ret;

start = blk_rq_pos(req) << 9;
len = blk_rq_cur_bytes(req);
read_bytes = 0;
-
- /* We are always reading. No need to handle writing for now */
-
leb = start / dev->vi->usable_leb_size;
offset = start % dev->vi->usable_leb_size;

@@ -130,31 +129,34 @@ static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
if (offset + len > dev->vi->usable_leb_size)
len = dev->vi->usable_leb_size - offset;

- if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
- get_capacity(req->rq_disk))) {
+ if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk)) {
+ /*
+ * TODO: snitize the error message, e.g.,
+ * "cannot read sector %llu beyond device size %llu"
+ */
dev_err(disk_to_dev(dev->gd),
"attempting to read too far\n");
+ /*
+ * TODO: hmm, is -EIO the right error? What other block
+ * devices return in this case? Any specific pointer
+ * please?
+ */
return -EIO;
}

- /* Read (len) bytes of LEB (leb) from (offset) and put the
- * result in the buffer given by the request.
- * If the request is overlapping on several lebs, (read_bytes)
- * will be > 0 and the data will be put in the buffer at
- * offset (read_bytes)
- */
- ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
- offset, len);
-
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes, offset,
+ len);
if (ret) {
- dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ dev_err(disk_to_dev(dev->gd),
+ "can't read %d bytes from LEB %d:%d, error %d\n",
+ len, leb, offset, ret);
return ret;
}

read_bytes += len;
-
len = blk_rq_cur_bytes(req) - read_bytes;
- leb++;
+ leb += 1;
offset = 0;
} while (read_bytes < blk_rq_cur_bytes(req));

@@ -162,38 +164,34 @@ static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
}

/**
- * ubiblk_request - wakes the processing thread
- * @rq: the request queue which device is to be awaken
+ * ubiblk_request - wakes the processing thread.
+ * @rq: the request queue which requires processing
*/
+/* TODO: bad name, may be wakeup_req_thread() would be better? */
static void ubiblk_request(struct request_queue *rq)
{
struct ubiblk_dev *dev;
struct request *req;

dev = rq->queuedata;
-
- if (!dev)
+ if (dev)
+ wake_up_process(dev->req_task);
+ else {
+ /* TODO: an error message or WARN here ? */
while ((req = blk_fetch_request(rq)) != NULL)
__blk_end_request_all(req, -ENODEV);
- else
- wake_up_process(dev->req_task);
+ }
}

-/**
- * ubiblk_open - open a UBI volume (get the volume descriptor).
- * @bdev: the corresponding block device
- * @mode: opening mode (don't care as long as ubiblk is read-only)
- */
static int ubiblk_open(struct block_device *bdev, fmode_t mode)
{
struct ubiblk_dev *dev = bdev->bd_disk->private_data;
int err;

mutex_lock(&dev->vol_lock);
- dev_dbg(disk_to_dev(dev->gd), "open(); refcnt = %d\n", dev->refcnt);
if (dev->refcnt > 0) {
/*
- * The volume is already opened ; just increase the reference
+ * The volume is already opened, just increase the reference
* counter.
*/
dev->refcnt++;
@@ -201,11 +199,12 @@ static int ubiblk_open(struct block_device *bdev, fmode_t mode)
return 0;
}

- dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
- UBI_READONLY);
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id, UBI_READONLY);
if (IS_ERR(dev->desc)) {
+ /* TODO: Failed to open what? which volume? Why not to print
+ * full information? Could you please go through _all_ error
+ * message and assess them WRT niceness to the user? */
dev_err(disk_to_dev(dev->gd), "failed to open");
-
err = PTR_ERR(dev->desc);
dev->desc = NULL;
goto out_unlock;
@@ -219,7 +218,6 @@ static int ubiblk_open(struct block_device *bdev, fmode_t mode)
ubi_get_volume_info(dev->desc, dev->vi);

dev->refcnt++;
- dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
mutex_unlock(&dev->vol_lock);
return 0;

@@ -231,38 +229,30 @@ out_unlock:
return err;
}

-/**
- * ubiblk_release - close a UBI volume (close the volume descriptor).
- * @gd: the disk that was previously opened
- * @mode: don't care
- */
static int ubiblk_release(struct gendisk *gd, fmode_t mode)
{
struct ubiblk_dev *dev = gd->private_data;

mutex_lock(&dev->vol_lock);
- dev_dbg(disk_to_dev(dev->gd), "release(); refcnt = %d\n", dev->refcnt);
-
dev->refcnt--;
if (dev->refcnt == 0) {
kfree(dev->vi);
dev->vi = NULL;
-
ubi_close_volume(dev->desc);
dev->desc = NULL;
-
- dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
}
-
mutex_unlock(&dev->vol_lock);
+
return 0;
}

/**
- * ubiblk_thread - loop on the block request queue and wait for new
- * requests ; run them with do_ubiblk_request(). Mostly copied from
- * mtd_blkdevs.c.
+ * ubiblk_thread - dispatch UBI requests.
* @arg: the ubiblk device which request queue to process
+ *
+ * This function loops on the block request queue and waits for new requests.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
*/
static int ubiblk_thread(void *arg)
{
@@ -270,8 +260,9 @@ static int ubiblk_thread(void *arg)
struct request_queue *rq = dev->rq;
struct request *req = NULL;

+ /* TODO: I doubt you need to disable IRQs because you do not have any
+ * of them! Please, investigate this. */
spin_lock_irq(rq->queue_lock);
-
while (!kthread_should_stop()) {
int res;

@@ -282,40 +273,37 @@ static int ubiblk_thread(void *arg)

if (kthread_should_stop())
set_current_state(TASK_RUNNING);
-
spin_unlock_irq(rq->queue_lock);
+
schedule();
+
spin_lock_irq(rq->queue_lock);
continue;
}
-
spin_unlock_irq(rq->queue_lock);

mutex_lock(&dev->vol_lock);
- res = do_ubiblk_request(req, dev);
+ res = do_request(req, dev);
mutex_unlock(&dev->vol_lock);

spin_lock_irq(rq->queue_lock);
-
if (!__blk_end_request_cur(req, res))
- req = NULL;
+ req = NULL;
}

if (req)
__blk_end_request_all(req, -EIO);
-
spin_unlock_irq(rq->queue_lock);

return 0;
}

/**
- * ubiblk_create - create a ubiblk device proxying a UBI volume.
+ * ubiblk_create - create a ubiblk device.
* @vi: the UBI volume information data structure
*
- * An UBI volume has been created ; create a corresponding ubiblk device:
- * Initialize the locks, the structure, the block layer infos and start a
- * req_task.
+ * Creates a ubiblk device for UBI volume described by @vi. Returns zero in
+ * case of success and a negative error code in case of failure.
*/
static int ubiblk_create(struct ubi_volume_info *vi)
{
@@ -371,16 +359,14 @@ static int ubiblk_create(struct ubi_volume_info *vi)
blk_queue_logical_block_size(dev->rq, BLK_SIZE);
dev->gd->queue = dev->rq;

- /* Borrowed from mtd_blkdevs.c */
- /* Create processing req_task
- *
+ /*
* The processing of the request has to be done in process context (it
- * might sleep) but blk_run_queue can't block ; so we need to separate
+ * might sleep) but blk_run_queue can't block; so we need to separate
* the event of a request being added to the queue (which triggers the
* callback ubiblk_request - that is set with blk_init_queue())
* and the processing of that request.
*
- * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * Thus, the sole purpose of ubiblk_request is to wake the kthread
* up so that it will process the request queue
*/
dev->req_task = kthread_run(ubiblk_thread, dev, "%s%d_%d",
@@ -396,7 +382,6 @@ static int ubiblk_create(struct ubi_volume_info *vi)
dev_info(disk_to_dev(dev->gd),
"created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
vi->name);
-
mutex_unlock(&devlist_lock);

return 0;
@@ -417,15 +402,14 @@ out_unlock:
* ubiblk_remove - destroy a ubiblk device.
* @vi: the UBI volume information data structure
*
- * A UBI volume has been removed or we are requested to unproxify a volume ;
- * destroy the corresponding ubiblk device.
+ * Destroys the ubiblk device for UBI volume described by @vi. Returns zero in
+ * case of success and a negative error code in case of failure.
*/
static int ubiblk_remove(struct ubi_volume_info *vi)
{
struct ubiblk_dev *dev;

mutex_lock(&devlist_lock);
-
dev = find_dev(vi);
if (!dev) {
mutex_unlock(&devlist_lock);
@@ -445,7 +429,6 @@ static int ubiblk_remove(struct ubi_volume_info *vi)
blk_cleanup_queue(dev->rq);
kthread_stop(dev->req_task);
put_disk(dev->gd);
-
list_del(&dev->list);
mutex_unlock(&dev->vol_lock);
mutex_unlock(&devlist_lock);
@@ -459,17 +442,15 @@ static int ubiblk_remove(struct ubi_volume_info *vi)
* ubiblk_resize - resize a ubiblk device.
* @vi: the UBI volume information data structure
*
- * A UBI volume has been resized, change the ubiblk device geometry accordingly.
+ * A UBI volume has been resized, change the ubiblk device geometry
+ * accordingly. Returns zero in case of success and a negative error code in
+ * case of failure.
*/
static int ubiblk_resize(struct ubi_volume_info *vi)
{
struct ubiblk_dev *dev;
int disk_capacity;

- /* We don't touch the list, but we better lock it: it could be that the
- * device gets removed between the time the device has been found and
- * the time we access dev->gd
- */
mutex_lock(&devlist_lock);
dev = find_dev(vi);
if (!dev) {
@@ -482,10 +463,9 @@ static int ubiblk_resize(struct ubi_volume_info *vi)
mutex_lock(&dev->vol_lock);
disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
set_capacity(dev->gd, disk_capacity);
- dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
mutex_unlock(&dev->vol_lock);
-
mutex_unlock(&devlist_lock);
+
return 0;
}
--
Best Regards,
Artem Bityutskiy

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Continue reading on narkive:
Loading...