All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] ubiblk: read-only block layer on top of UBI
@ 2011-06-24 13:34 ` david.wagner
  0 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-06-24 13:34 UTC (permalink / raw)
  To: linux-mtd; +Cc: dedekind1, dwmw2, linux-kernel, David Wagner

From: David Wagner <david.wagner@free-electrons.com>

It creates one device for each UBI volume and dynamically add/remove one when a
UBI volume is created or deleted : it registers to UBI notifications.  The
devices are names "ubiblkX_Y" where X is the UBI device number and Y is the
volume ID.

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.

It is only known to work well with SquashFS.  Tests reported that with other
filesystems (ext2/3 and vfat), some bytes in some files (I couldn't find any
pattern), when read through ubiblk, don't match the original content (the
content of a file becomes random on several "lines" and goes back to normal).
The reason for that is still unknown.

Some other known issues are listed in the commit message.

 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

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [RFC] ubiblk: read-only block layer on top of UBI
@ 2011-06-24 13:34 ` david.wagner
  0 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-06-24 13:34 UTC (permalink / raw)
  To: linux-mtd; +Cc: David Wagner, dwmw2, linux-kernel, dedekind1

From: David Wagner <david.wagner@free-electrons.com>

It creates one device for each UBI volume and dynamically add/remove one when a
UBI volume is created or deleted : it registers to UBI notifications.  The
devices are names "ubiblkX_Y" where X is the UBI device number and Y is the
volume ID.

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.

It is only known to work well with SquashFS.  Tests reported that with other
filesystems (ext2/3 and vfat), some bytes in some files (I couldn't find any
pattern), when read through ubiblk, don't match the original content (the
content of a file becomes random on several "lines" and goes back to normal).
The reason for that is still unknown.

Some other known issues are listed in the commit message.

 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

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-06-24 13:34   ` david.wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-06-24 13:34 UTC (permalink / raw)
  To: linux-mtd; +Cc: dedekind1, dwmw2, linux-kernel, David Wagner

From: David Wagner <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 <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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-24 13:34   ` david.wagner
  0 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-06-24 13:34 UTC (permalink / raw)
  To: linux-mtd; +Cc: David Wagner, dwmw2, linux-kernel, dedekind1

From: David Wagner <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 <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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [Addendum][RFC] ubiblk: read-only block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
  (?)
  (?)
@ 2011-06-24 13:45 ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-06-24 13:45 UTC (permalink / raw)
  To: david.wagner; +Cc: linux-mtd, dedekind1, dwmw2, linux-kernel

On 06/24/2011 03:34 PM, david.wagner@free-electrons.com wrote:

>  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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [RFC] ubiblk: read-only block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-06-27 19:14   ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-27 19:14 UTC (permalink / raw)
  To: david.wagner; +Cc: linux-mtd, dwmw2, linux-kernel

Hi,

thanks, for the driver.

On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
wrote:
> 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 (Битюцкий Артём)


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [RFC] ubiblk: read-only block layer on top of UBI
@ 2011-06-27 19:14   ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-27 19:14 UTC (permalink / raw)
  To: david.wagner; +Cc: dwmw2, linux-mtd, linux-kernel

Hi,

thanks, for the driver.

On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
wrote:
> 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 (Битюцкий Артём)

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34   ` david.wagner
@ 2011-06-27 19:26     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-27 19:26 UTC (permalink / raw)
  To: david.wagner; +Cc: linux-mtd, dwmw2, linux-kernel

On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
wrote:
> +	/* 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?

> +	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 (Битюцкий Артём)


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-27 19:26     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-27 19:26 UTC (permalink / raw)
  To: david.wagner; +Cc: dwmw2, linux-mtd, linux-kernel

On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
wrote:
> +	/* 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?

> +	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 (Битюцкий Артём)

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-27 19:26     ` Artem Bityutskiy
@ 2011-06-28 11:35       ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-06-28 11:35 UTC (permalink / raw)
  To: dedekind1; +Cc: dwmw2, linux-mtd, linux-kernel

Hi,

On 06/27/2011 09:26 PM, Artem Bityutskiy wrote:
> On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
> wrote:
>> +	/* 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-28 11:35       ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-06-28 11:35 UTC (permalink / raw)
  To: dedekind1; +Cc: linux-mtd, dwmw2, linux-kernel

Hi,

On 06/27/2011 09:26 PM, Artem Bityutskiy wrote:
> On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
> wrote:
>> +	/* 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-27 19:26     ` Artem Bityutskiy
@ 2011-06-28 14:50       ` Matthieu CASTET
  -1 siblings, 0 replies; 118+ messages in thread
From: Matthieu CASTET @ 2011-06-28 14:50 UTC (permalink / raw)
  To: dedekind1; +Cc: david.wagner, dwmw2, linux-mtd, linux-kernel

Hi,

Artem Bityutskiy a écrit :
> On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
> wrote:
>> +	/* 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 ?

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-28 14:50       ` Matthieu CASTET
  0 siblings, 0 replies; 118+ messages in thread
From: Matthieu CASTET @ 2011-06-28 14:50 UTC (permalink / raw)
  To: dedekind1; +Cc: dwmw2, david.wagner, linux-kernel, linux-mtd

Hi,

Artem Bityutskiy a écrit :
> On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
> wrote:
>> +	/* 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 ?

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [RFC PATCHv2] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
  (?)
@ 2011-06-28 15:24   ` david.wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-06-28 15:24 UTC (permalink / raw)
  To: linux-mtd
  Cc: dedekind1, dwmw2, linux-kernel, linux-embedded, tim.bird, David Wagner

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 16600 bytes --]

From: David Wagner <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 <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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [RFC PATCHv2] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-28 15:24   ` david.wagner
  0 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-06-28 15:24 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, dedekind1, David Wagner, linux-kernel, tim.bird, dwmw2

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 16599 bytes --]

From: David Wagner <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 <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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [RFC PATCHv2] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-28 15:24   ` david.wagner
  0 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-06-28 15:24 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, dedekind1, David Wagner, linux-kernel, tim.bird, dwmw2

[-- Attachment #1: Type: text/plain, Size: 16809 bytes --]

From: David Wagner <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 <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



[-- Attachment #2: Type: text/plain, Size: 144 bytes --]

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-28 14:50       ` Matthieu CASTET
@ 2011-06-28 15:32         ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-06-28 15:32 UTC (permalink / raw)
  To: Matthieu CASTET; +Cc: dedekind1, dwmw2, linux-kernel, linux-mtd

On 06/28/2011 04:50 PM, Matthieu CASTET wrote:
> Hi,
>
[...]
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-28 15:32         ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-06-28 15:32 UTC (permalink / raw)
  To: Matthieu CASTET; +Cc: linux-mtd, dwmw2, linux-kernel, dedekind1

On 06/28/2011 04:50 PM, Matthieu CASTET wrote:
> Hi,
>
[...]
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-28 15:32         ` David Wagner
@ 2011-06-29  6:25           ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  6:25 UTC (permalink / raw)
  To: David Wagner; +Cc: Matthieu CASTET, dwmw2, linux-kernel, linux-mtd

On Tue, 2011-06-28 at 17:32 +0200, David Wagner wrote:
> On 06/28/2011 04:50 PM, Matthieu CASTET wrote:
> > Hi,
> >
> [...]
> > 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-29  6:25           ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  6:25 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-mtd, dwmw2, linux-kernel, Matthieu CASTET

On Tue, 2011-06-28 at 17:32 +0200, David Wagner wrote:
> On 06/28/2011 04:50 PM, Matthieu CASTET wrote:
> > Hi,
> >
> [...]
> > 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-28 11:35       ` David Wagner
@ 2011-06-29  6:52         ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  6:52 UTC (permalink / raw)
  To: David Wagner; +Cc: dwmw2, linux-mtd, linux-kernel

On Tue, 2011-06-28 at 13:35 +0200, David Wagner wrote:
> Hi,
> 
> On 06/27/2011 09:26 PM, Artem Bityutskiy wrote:
> > On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
> > wrote:
> >> +	/* 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-29  6:52         ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  6:52 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-mtd, dwmw2, linux-kernel

On Tue, 2011-06-28 at 13:35 +0200, David Wagner wrote:
> Hi,
> 
> On 06/27/2011 09:26 PM, Artem Bityutskiy wrote:
> > On Fri, 2011-06-24 at 15:34 +0200, david.wagner@free-electrons.com
> > wrote:
> >> +	/* 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [RFC PATCHv2] UBI: new module ubiblk: block layer on top of UBI
  2011-06-28 15:24   ` david.wagner
@ 2011-06-29  6:54     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  6:54 UTC (permalink / raw)
  To: david.wagner; +Cc: linux-mtd, dwmw2, linux-kernel, linux-embedded, tim.bird

On Tue, 2011-06-28 at 17:24 +0200, david.wagner@free-electrons.com
wrote:
> + * 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [RFC PATCHv2] UBI: new module ubiblk: block layer on top of UBI
@ 2011-06-29  6:54     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-06-29  6:54 UTC (permalink / raw)
  To: david.wagner; +Cc: dwmw2, linux-mtd, linux-kernel, linux-embedded, tim.bird

On Tue, 2011-06-28 at 17:24 +0200, david.wagner@free-electrons.com
wrote:
> + * 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-07-26 12:27   ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-07-26 12:27 UTC (permalink / raw)
  To: linux-mtd
  Cc: dedekind1, dwmw2, linux-kernel, linux-embedded, tim.bird, David Wagner

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 17064 bytes --]

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 <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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-07-26 12:27   ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-07-26 12:27 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, dedekind1, David Wagner, linux-kernel, tim.bird, dwmw2

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 17063 bytes --]

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 <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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-07-26 12:27   ` David Wagner
  (?)
@ 2011-07-26 12:34     ` Christoph Hellwig
  -1 siblings, 0 replies; 118+ messages in thread
From: Christoph Hellwig @ 2011-07-26 12:34 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-mtd, dedekind1, dwmw2, linux-kernel, linux-embedded, tim.bird

> 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.

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.

> Advantages of ubiblk over gluebi+mtdblock_ro:

This section should be in the main commit log, and not that part with
the changelog which gets dropped.

> +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.


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-07-26 12:34     ` Christoph Hellwig
  0 siblings, 0 replies; 118+ messages in thread
From: Christoph Hellwig @ 2011-07-26 12:34 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-embedded, dedekind1, linux-kernel, linux-mtd, tim.bird, dwmw2

> 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.

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.

> Advantages of ubiblk over gluebi+mtdblock_ro:

This section should be in the main commit log, and not that part with
the changelog which gets dropped.

> +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.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-07-26 12:34     ` Christoph Hellwig
  0 siblings, 0 replies; 118+ messages in thread
From: Christoph Hellwig @ 2011-07-26 12:34 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-embedded, dedekind1, linux-kernel, linux-mtd, tim.bird, dwmw2

> 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.

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.

> Advantages of ubiblk over gluebi+mtdblock_ro:

This section should be in the main commit log, and not that part with
the changelog which gets dropped.

> +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.


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-07-26 12:34     ` Christoph Hellwig
@ 2011-07-26 12:58       ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-07-26 12:58 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-mtd, dedekind1, dwmw2, linux-kernel, linux-embedded, tim.bird

On 07/26/2011 02:34 PM, Christoph Hellwig wrote:
>> 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.
>
> 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).

>> Advantages of ubiblk over gluebi+mtdblock_ro:
>
> This section should be in the main commit log, and not that part with
> the changelog which gets dropped.

Will do.

>> +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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-07-26 12:58       ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-07-26 12:58 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-embedded, dedekind1, linux-kernel, linux-mtd, tim.bird, dwmw2

On 07/26/2011 02:34 PM, Christoph Hellwig wrote:
>> 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.
>
> 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).

>> Advantages of ubiblk over gluebi+mtdblock_ro:
>
> This section should be in the main commit log, and not that part with
> the changelog which gets dropped.

Will do.

>> +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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-07-26 12:27   ` David Wagner
@ 2011-07-28  6:14     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-07-28  6:14 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-mtd, dwmw2, linux-kernel, linux-embedded, tim.bird

On Tue, 2011-07-26 at 14:27 +0200, David Wagner wrote:
>  * 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 (Битюцкий Артём)


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-07-28  6:14     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-07-28  6:14 UTC (permalink / raw)
  To: David Wagner; +Cc: dwmw2, linux-mtd, linux-kernel, linux-embedded, tim.bird

On Tue, 2011-07-26 at 14:27 +0200, David Wagner wrote:
>  * 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 (Битюцкий Артём)

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
  2011-07-26 12:27   ` David Wagner
@ 2011-08-15 11:56     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-15 11:56 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-mtd, dwmw2, linux-kernel, linux-embedded, tim.bird

On Tue, 2011-07-26 at 14:27 +0200, David Wagner wrote:
> 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)

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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-15 11:56     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-15 11:56 UTC (permalink / raw)
  To: David Wagner; +Cc: dwmw2, linux-mtd, linux-kernel, linux-embedded, tim.bird

On Tue, 2011-07-26 at 14:27 +0200, David Wagner wrote:
> 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)

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-08-17 13:17   ` david.wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-08-17 13:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, linux-embedded, lkml, Tim Bird,
	David Woodhouse, David Wagner

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 23966 bytes --]

From: David Wagner <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 <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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-17 13:17   ` david.wagner
  0 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-08-17 13:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, Artem Bityutskiy, David Wagner, lkml, Tim Bird,
	David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 23965 bytes --]

From: David Wagner <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 <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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH] Tools for controling ubiblk
  2011-08-17 13:17   ` david.wagner
@ 2011-08-17 14:20     ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-08-17 14:20 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, linux-embedded, lkml, Tim Bird,
	David Woodhouse, David Wagner

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 5958 bytes --]

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 <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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH] Tools for controling ubiblk
@ 2011-08-17 14:20     ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-08-17 14:20 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, Artem Bityutskiy, David Wagner, lkml, Tim Bird,
	David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 5957 bytes --]

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 <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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-08-17 13:17   ` david.wagner
@ 2011-08-22  7:39     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-22  7:39 UTC (permalink / raw)
  To: david.wagner; +Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

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.

On Wed, 2011-08-17 at 15:17 +0200, david.wagner@free-electrons.com
wrote:
> 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)).

Wold be nice to do the same change for UBI, BTW :-)

> 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]

> +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/
> +
> +	   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]

> +/*
> + * 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".

> +	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.

> +	/* Protects the access to the UBI volume */
> +	struct mutex lock;

Lets call all locks <something_meaningful>_lock, e.g., volumes_lock or
vol_lock.

> +
> +	/* 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]

> +	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]

> +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;

Please, remove cases which do nothing and let them end up in "default:".

[snip]

> +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.

> +		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);

Please, return the error code!

> +		break;
> +	case UBIBLK_IOCDEL:
> +		pr_info("Removing ubiblk from ubi%d:%d\n",
> +			req.ubi_num, req.vol_id);
> +		ubiblk_remove(&vol_info);

And here!

[snip]

> +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".

> +
> +	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]

> +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]

> +/* 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.

> +
> +#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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-22  7:39     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-22  7:39 UTC (permalink / raw)
  To: david.wagner; +Cc: David Woodhouse, linux-mtd, lkml, linux-embedded, Tim Bird

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.

On Wed, 2011-08-17 at 15:17 +0200, david.wagner@free-electrons.com
wrote:
> 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)).

Wold be nice to do the same change for UBI, BTW :-)

> 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]

> +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/
> +
> +	   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]

> +/*
> + * 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".

> +	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.

> +	/* Protects the access to the UBI volume */
> +	struct mutex lock;

Lets call all locks <something_meaningful>_lock, e.g., volumes_lock or
vol_lock.

> +
> +	/* 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]

> +	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]

> +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;

Please, remove cases which do nothing and let them end up in "default:".

[snip]

> +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.

> +		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);

Please, return the error code!

> +		break;
> +	case UBIBLK_IOCDEL:
> +		pr_info("Removing ubiblk from ubi%d:%d\n",
> +			req.ubi_num, req.vol_id);
> +		ubiblk_remove(&vol_info);

And here!

[snip]

> +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".

> +
> +	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]

> +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]

> +/* 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.

> +
> +#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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-08-17 13:17   ` david.wagner
@ 2011-08-22  7:42     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-22  7:42 UTC (permalink / raw)
  To: david.wagner
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Arnd Bergmann

On Wed, 2011-08-17 at 15:17 +0200, david.wagner@free-electrons.com
wrote:
> 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 ?

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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-22  7:42     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-22  7:42 UTC (permalink / raw)
  To: david.wagner
  Cc: linux-embedded, Arnd Bergmann, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On Wed, 2011-08-17 at 15:17 +0200, david.wagner@free-electrons.com
wrote:
> 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 ?

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] Tools for controling ubiblk
  2011-08-17 14:20     ` David Wagner
@ 2011-08-22  8:17       ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-22  8:17 UTC (permalink / raw)
  To: David Wagner; +Cc: David Woodhouse, linux-mtd, Tim Bird, linux-embedded

I do not think LKML is interested in mtd-utils user-space project
patches, please, do not add lkml to spam it less.

On Wed, 2011-08-17 at 16:20 +0200, David Wagner wrote:
> +#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

Please, do not copy kernel headers verbatim, copy the result of "make
headers_install" invoked in the kernel tree.

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] Tools for controling ubiblk
@ 2011-08-22  8:17       ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-22  8:17 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-mtd, linux-embedded, Tim Bird, David Woodhouse

I do not think LKML is interested in mtd-utils user-space project
patches, please, do not add lkml to spam it less.

On Wed, 2011-08-17 at 16:20 +0200, David Wagner wrote:
> +#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

Please, do not copy kernel headers verbatim, copy the result of "make
headers_install" invoked in the kernel tree.

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCHv4] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-08-24 16:15   ` david.wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-08-24 16:15 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, linux-embedded, lkml, Tim Bird,
	David Woodhouse, David Wagner

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 25555 bytes --]

From: David Wagner <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 <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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv4] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-24 16:15   ` david.wagner
  0 siblings, 0 replies; 118+ messages in thread
From: david.wagner @ 2011-08-24 16:15 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, Artem Bityutskiy, David Wagner, lkml, Tim Bird,
	David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 25554 bytes --]

From: David Wagner <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 <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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH] document ubiblk's usage of the same ioctl magic as a part of UBI
  2011-08-24 16:15   ` david.wagner
@ 2011-08-24 16:21     ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-08-24 16:21 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, linux-embedded, lkml, Tim Bird,
	David Woodhouse, 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
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCH] document ubiblk's usage of the same ioctl magic as a part of UBI
@ 2011-08-24 16:21     ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-08-24 16:21 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, Artem Bityutskiy, David Wagner, lkml, Tim Bird,
	David Woodhouse

---
 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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-08-22  7:42     ` Artem Bityutskiy
@ 2011-08-24 16:23       ` Arnd Bergmann
  -1 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-08-24 16:23 UTC (permalink / raw)
  To: dedekind1
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Monday 22 August 2011, Artem Bityutskiy wrote:
> 
> On Wed, 2011-08-17 at 15:17 +0200, david.wagner@free-electrons.com
> wrote:
> > 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 ?
> 
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-24 16:23       ` Arnd Bergmann
  0 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-08-24 16:23 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Monday 22 August 2011, Artem Bityutskiy wrote:
> 
> On Wed, 2011-08-17 at 15:17 +0200, david.wagner@free-electrons.com
> wrote:
> > 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 ?
> 
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-08-24 16:23       ` Arnd Bergmann
@ 2011-08-25  7:06         ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-25  7:06 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

Hi Arnd,

On Wed, 2011-08-24 at 18:23 +0200, Arnd Bergmann wrote:
> 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.

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?

> * 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-25  7:06         ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-08-25  7:06 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

Hi Arnd,

On Wed, 2011-08-24 at 18:23 +0200, Arnd Bergmann wrote:
> 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.

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?

> * 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-08-25  7:06         ` Artem Bityutskiy
  (?)
@ 2011-08-25 15:12           ` Arnd Bergmann
  -1 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-08-25 15:12 UTC (permalink / raw)
  To: dedekind1
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Thursday 25 August 2011, Artem Bityutskiy wrote:
> On Wed, 2011-08-24 at 18:23 +0200, Arnd Bergmann wrote:
> > 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.
> 
> 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.

> > * 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-25 15:12           ` Arnd Bergmann
  0 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-08-25 15:12 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Thursday 25 August 2011, Artem Bityutskiy wrote:
> On Wed, 2011-08-24 at 18:23 +0200, Arnd Bergmann wrote:
> > 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.
> 
> 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.

> > * 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-08-25 15:12           ` Arnd Bergmann
  0 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-08-25 15:12 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Thursday 25 August 2011, Artem Bityutskiy wrote:
> On Wed, 2011-08-24 at 18:23 +0200, Arnd Bergmann wrote:
> > 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.
> 
> 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.

> > * 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

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-08-25 15:12           ` Arnd Bergmann
  (?)
@ 2011-09-01 12:55             ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-01 12:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: dedekind1, linux-embedded, lkml, linux-mtd, Tim Bird, David Woodhouse

On 08/25/2011 05:12 PM, Arnd Bergmann wrote:
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-01 12:55             ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-01 12:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, dedekind1, lkml, linux-mtd, Tim Bird, David Woodhouse

On 08/25/2011 05:12 PM, Arnd Bergmann wrote:
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-01 12:55             ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-01 12:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, dedekind1, lkml, linux-mtd, Tim Bird, David Woodhouse

On 08/25/2011 05:12 PM, Arnd Bergmann wrote:
> 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

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-08-25 15:12           ` Arnd Bergmann
@ 2011-09-06  3:44             ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  3:44 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

Hi, sorry for long delay, did not have time to read my mail.

On Thu, 2011-08-25 at 17:12 +0200, Arnd Bergmann wrote:
> > 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.

> 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.

> 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-06  3:44             ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  3:44 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

Hi, sorry for long delay, did not have time to read my mail.

On Thu, 2011-08-25 at 17:12 +0200, Arnd Bergmann wrote:
> > 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.

> 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.

> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-06  3:44             ` Artem Bityutskiy
  (?)
@ 2011-09-06  4:10               ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:10 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Tue, 2011-09-06 at 06:44 +0300, Artem Bityutskiy wrote:
> > 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-06  4:10               ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:10 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Tue, 2011-09-06 at 06:44 +0300, Artem Bityutskiy wrote:
> > 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-06  4:10               ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:10 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Tue, 2011-09-06 at 06:44 +0300, Artem Bityutskiy wrote:
> > 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


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-06  4:10               ` Artem Bityutskiy
@ 2011-09-06  4:29                 ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:29 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Tue, 2011-09-06 at 07:10 +0300, Artem Bityutskiy wrote:
> On Tue, 2011-09-06 at 06:44 +0300, Artem Bityutskiy wrote:
> > > 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-06  4:29                 ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:29 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Tue, 2011-09-06 at 07:10 +0300, Artem Bityutskiy wrote:
> On Tue, 2011-09-06 at 06:44 +0300, Artem Bityutskiy wrote:
> > > 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv4] UBI: new module ubiblk: block layer on top of UBI
  2011-08-24 16:15   ` david.wagner
@ 2011-09-06  4:55     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:55 UTC (permalink / raw)
  To: david.wagner; +Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Wed, 2011-08-24 at 18:15 +0200, david.wagner@free-electrons.com
wrote:
>  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.

> 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).

> (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) ?

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.

> +/**
> + * 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.

> +		return -ENODEV;

Why do you override the error code returned by 'register_blkdev()'?

> +	}
> +	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?

> +
> +	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"

> +
> +	return ret;
> +
> +out_unreg_ctrl:
> +	misc_deregister(&ctrl_dev);
> +out_unreg_blk:
> +	unregister_blkdev(ubiblk_major, "ubiblk");
> +
> +	return ret;
> +}

-- 
Best Regards,
Artem Bityutskiy


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv4] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-06  4:55     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:55 UTC (permalink / raw)
  To: david.wagner; +Cc: David Woodhouse, linux-mtd, lkml, linux-embedded, Tim Bird

On Wed, 2011-08-24 at 18:15 +0200, david.wagner@free-electrons.com
wrote:
>  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.

> 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).

> (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) ?

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.

> +/**
> + * 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.

> +		return -ENODEV;

Why do you override the error code returned by 'register_blkdev()'?

> +	}
> +	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?

> +
> +	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"

> +
> +	return ret;
> +
> +out_unreg_ctrl:
> +	misc_deregister(&ctrl_dev);
> +out_unreg_blk:
> +	unregister_blkdev(ubiblk_major, "ubiblk");
> +
> +	return ret;
> +}

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] document ubiblk's usage of the same ioctl magic as a part of UBI
  2011-08-24 16:21     ` David Wagner
@ 2011-09-06  4:58       ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:58 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Wed, 2011-08-24 at 18:21 +0200, David Wagner wrote:
> ---
>  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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCH] document ubiblk's usage of the same ioctl magic as a part of UBI
@ 2011-09-06  4:58       ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-06  4:58 UTC (permalink / raw)
  To: David Wagner; +Cc: David Woodhouse, linux-mtd, lkml, linux-embedded, Tim Bird

On Wed, 2011-08-24 at 18:21 +0200, David Wagner wrote:
> ---
>  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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-06  4:10               ` Artem Bityutskiy
  (?)
@ 2011-09-08 15:26                 ` Arnd Bergmann
  -1 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-09-08 15:26 UTC (permalink / raw)
  To: dedekind1
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-08 15:26                 ` Arnd Bergmann
  0 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-09-08 15:26 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-08 15:26                 ` Arnd Bergmann
  0 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-09-08 15:26 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> 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

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-08 15:26                 ` Arnd Bergmann
@ 2011-09-09 11:53                   ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-09 11:53 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-09 11:53                   ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-09 11:53 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-09 11:53                   ` Artem Bityutskiy
@ 2011-09-09 12:02                     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-09 12:02 UTC (permalink / raw)
  To: david.wagner
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Arnd Bergmann

On Fri, 2011-09-09 at 14:53 +0300, Artem Bityutskiy wrote:
> 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

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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-09 12:02                     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-09 12:02 UTC (permalink / raw)
  To: david.wagner
  Cc: linux-embedded, Arnd Bergmann, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On Fri, 2011-09-09 at 14:53 +0300, Artem Bityutskiy wrote:
> 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

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-09 11:53                   ` Artem Bityutskiy
@ 2011-09-09 14:25                     ` Arnd Bergmann
  -1 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-09-09 14:25 UTC (permalink / raw)
  To: dedekind1
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Friday 09 September 2011, Artem Bityutskiy wrote:
> On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> > On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > > 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.

> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-09 14:25                     ` Arnd Bergmann
  0 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-09-09 14:25 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Friday 09 September 2011, Artem Bityutskiy wrote:
> On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> > On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > > 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.

> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-09 11:53                   ` Artem Bityutskiy
  (?)
@ 2011-09-09 14:41                     ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-09 14:41 UTC (permalink / raw)
  To: dedekind1
  Cc: Arnd Bergmann, linux-mtd, linux-embedded, lkml, Tim Bird,
	David Woodhouse

On 09/09/2011 01:53 PM, Artem Bityutskiy wrote:
> On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
>> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
>>> 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.
>>

@Arnd:
> * 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).


@Artem:
> 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() ?


> Anyway, I suggest the following algorithm:
> 
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-09 14:41                     ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-09 14:41 UTC (permalink / raw)
  To: dedekind1
  Cc: Arnd Bergmann, linux-embedded, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On 09/09/2011 01:53 PM, Artem Bityutskiy wrote:
> On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
>> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
>>> 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.
>>

@Arnd:
> * 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).


@Artem:
> 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() ?


> Anyway, I suggest the following algorithm:
> 
> 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-09 14:41                     ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-09 14:41 UTC (permalink / raw)
  To: dedekind1
  Cc: Arnd Bergmann, linux-embedded, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On 09/09/2011 01:53 PM, Artem Bityutskiy wrote:
> On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
>> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
>>> 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.
>>

@Arnd:
> * 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).


@Artem:
> 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() ?


> Anyway, I suggest the following algorithm:
> 
> 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

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-09 14:41                     ` David Wagner
@ 2011-09-09 14:51                       ` Arnd Bergmann
  -1 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-09-09 14:51 UTC (permalink / raw)
  To: David Wagner
  Cc: dedekind1, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Friday 09 September 2011, David Wagner wrote:
> On 09/09/2011 01:53 PM, Artem Bityutskiy wrote:
> > On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> >> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> >>> 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.
> >>
> 
> @Arnd:
> > * 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.

> @Artem:
> > 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-09 14:51                       ` Arnd Bergmann
  0 siblings, 0 replies; 118+ messages in thread
From: Arnd Bergmann @ 2011-09-09 14:51 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-embedded, dedekind1, lkml, linux-mtd, Tim Bird, David Woodhouse

On Friday 09 September 2011, David Wagner wrote:
> On 09/09/2011 01:53 PM, Artem Bityutskiy wrote:
> > On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> >> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> >>> 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.
> >>
> 
> @Arnd:
> > * 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.

> @Artem:
> > 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-09 14:25                     ` Arnd Bergmann
@ 2011-09-09 15:27                       ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-09 15:27 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse

On Fri, 2011-09-09 at 16:25 +0200, Arnd Bergmann wrote:
> On Friday 09 September 2011, Artem Bityutskiy wrote:
> > On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> > > On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > > > 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.

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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-09 15:27                       ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-09 15:27 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird, David Woodhouse

On Fri, 2011-09-09 at 16:25 +0200, Arnd Bergmann wrote:
> On Friday 09 September 2011, Artem Bityutskiy wrote:
> > On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> > > On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > > > 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.

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-09 14:41                     ` David Wagner
  (?)
@ 2011-09-11 10:18                       ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-11 10:18 UTC (permalink / raw)
  To: David Wagner
  Cc: Arnd Bergmann, linux-mtd, linux-embedded, lkml, Tim Bird,
	David Woodhouse

On Fri, 2011-09-09 at 16:41 +0200, David Wagner wrote:
> > 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-11 10:18                       ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-11 10:18 UTC (permalink / raw)
  To: David Wagner
  Cc: Arnd Bergmann, linux-embedded, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On Fri, 2011-09-09 at 16:41 +0200, David Wagner wrote:
> > 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-11 10:18                       ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-11 10:18 UTC (permalink / raw)
  To: David Wagner
  Cc: Arnd Bergmann, linux-embedded, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On Fri, 2011-09-09 at 16:41 +0200, David Wagner wrote:
> > 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


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
  2011-09-11 10:18                       ` Artem Bityutskiy
@ 2011-09-11 10:35                         ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-11 10:35 UTC (permalink / raw)
  To: dedekind1
  Cc: Arnd Bergmann, linux-mtd, linux-embedded, lkml, Tim Bird,
	David Woodhouse

On 11/09/2011 12:18, Artem Bityutskiy wrote:
> On Fri, 2011-09-09 at 16:41 +0200, David Wagner wrote:
>>> 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.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-11 10:35                         ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-11 10:35 UTC (permalink / raw)
  To: dedekind1
  Cc: Arnd Bergmann, linux-embedded, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On 11/09/2011 12:18, Artem Bityutskiy wrote:
> On Fri, 2011-09-09 at 16:41 +0200, David Wagner wrote:
>>> 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.

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCHv5] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
  (?)
@ 2011-09-12  9:51   ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-12  9:51 UTC (permalink / raw)
  To: linux-mtd
  Cc: Artem Bityutskiy, linux-embedded, lkml, Tim Bird,
	David Woodhouse, Arnd Bergmann, David Wagner

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 24756 bytes --]

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 <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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv5] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-12  9:51   ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-12  9:51 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, Arnd Bergmann, Artem Bityutskiy, David Wagner,
	lkml, Tim Bird, David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 24755 bytes --]

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 <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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv5] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-12  9:51   ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-12  9:51 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, Arnd Bergmann, Artem Bityutskiy, David Wagner,
	lkml, Tim Bird, David Woodhouse

[-- Attachment #1: Type: text/plain, Size: 25221 bytes --]

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 <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 <dwmw2@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



[-- Attachment #2: Type: text/plain, Size: 144 bytes --]

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv5] UBI: new module ubiblk: block layer on top of UBI
  2011-09-12  9:51   ` David Wagner
@ 2011-09-19  4:50     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-19  4:50 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Arnd Bergmann

Hi David,

some review below.

> +/**
> + * 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;
> +}

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...


...

> +/**
> + * 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);

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. 
...

> +	set_capacity(gd,
> +		     (dev->vi->size *
> +		      dev->vi->usable_leb_size) >> 9);

A temporary variable would be neater.

> +	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/ ?
...


> +/**
> + * 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;
> +	}

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)".

> +
> +	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;
> +}

...

> +#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. 

...

> +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.

...

> +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.

> +
> +		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.

> +		kfree(dev);
> +	}
> +
> +	unregister_blkdev(ubiblk_major, "ubiblk");
> +	pr_info("end of life\n");

This funny but let's remove it please.

> +}
> +
> +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");

....

> +/**
> + * 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[8] padding;
> +} __packed;

Please, add paddings as suggested above.

-- 
Best Regards,
Artem Bityutskiy


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv5] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-19  4:50     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-19  4:50 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-embedded, Arnd Bergmann, lkml, linux-mtd, Tim Bird,
	David Woodhouse

Hi David,

some review below.

> +/**
> + * 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;
> +}

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...


...

> +/**
> + * 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);

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. 
...

> +	set_capacity(gd,
> +		     (dev->vi->size *
> +		      dev->vi->usable_leb_size) >> 9);

A temporary variable would be neater.

> +	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/ ?
...


> +/**
> + * 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;
> +	}

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)".

> +
> +	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;
> +}

...

> +#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. 

...

> +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.

...

> +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.

> +
> +		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.

> +		kfree(dev);
> +	}
> +
> +	unregister_blkdev(ubiblk_major, "ubiblk");
> +	pr_info("end of life\n");

This funny but let's remove it please.

> +}
> +
> +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");

....

> +/**
> + * 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[8] padding;
> +} __packed;

Please, add paddings as suggested above.

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-09-22  7:58   ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-22  7:58 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, lkml, Tim Bird, David Woodhouse, David Wagner,
	Artem Bityutskiy, Arnd Bergmann

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 26536 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-22  7:58   ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-22  7:58 UTC (permalink / raw)
  To: linux-mtd
  Cc: Arnd Bergmann, linux-embedded, Artem Bityutskiy, David Wagner,
	lkml, Tim Bird, David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 26535 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
  2011-09-22  7:58   ` David Wagner
@ 2011-09-23 10:58     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-23 10:58 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Arnd Bergmann

On Thu, 2011-09-22 at 09:58 +0200, David Wagner wrote:

> +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").


> +/**
> + * 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.


> +/**
> + * 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.

> + */
> +static int __init ubi_ubiblk_init(void)

Name it ubiblk_init() please.

> +{
> +	int ret = 0;

The initialization is not needed.

> +
> +	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.

> +
> +	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.

> +
> +	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:

if (volumes)
	ubiblk_destroy(xxx);

or something like that is needed here.

> +	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.

> + * 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.

> +{
> +	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?

> +/*
> + * 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.

> +/**
> + * 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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-23 10:58     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-23 10:58 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-embedded, Arnd Bergmann, lkml, linux-mtd, Tim Bird,
	David Woodhouse

On Thu, 2011-09-22 at 09:58 +0200, David Wagner wrote:

> +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").


> +/**
> + * 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.


> +/**
> + * 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.

> + */
> +static int __init ubi_ubiblk_init(void)

Name it ubiblk_init() please.

> +{
> +	int ret = 0;

The initialization is not needed.

> +
> +	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.

> +
> +	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.

> +
> +	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:

if (volumes)
	ubiblk_destroy(xxx);

or something like that is needed here.

> +	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.

> + * 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.

> +{
> +	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?

> +/*
> + * 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.

> +/**
> + * 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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
  2011-09-22  7:58   ` David Wagner
  (?)
@ 2011-09-26  9:17     ` Ricard Wanderlof
  -1 siblings, 0 replies; 118+ messages in thread
From: Ricard Wanderlof @ 2011-09-26  9:17 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-mtd, Arnd Bergmann, linux-embedded, Artem Bityutskiy, lkml,
	Tim Bird, David Woodhouse


On Thu, 22 Sep 2011, David Wagner wrote:

> 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?

A couple of comments below:

> +/**
> + * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
> + *
> + * @file: the file on which the ioctl was invoked (usunsed)
                                                     ^^^^^^^ typo: unused
> + * @cmd: the ioctl type
> + * @arg: additional command informations
> + */
> +static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
> +                             unsigned long arg)


> +/**
> + * 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);
                            ^^^  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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26  9:17     ` Ricard Wanderlof
  0 siblings, 0 replies; 118+ messages in thread
From: Ricard Wanderlof @ 2011-09-26  9:17 UTC (permalink / raw)
  To: David Wagner
  Cc: Arnd Bergmann, linux-embedded, Artem Bityutskiy, lkml, linux-mtd,
	Tim Bird, David Woodhouse


On Thu, 22 Sep 2011, David Wagner wrote:

> 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?

A couple of comments below:

> +/**
> + * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
> + *
> + * @file: the file on which the ioctl was invoked (usunsed)
                                                     ^^^^^^^ typo: unused
> + * @cmd: the ioctl type
> + * @arg: additional command informations
> + */
> +static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
> +                             unsigned long arg)


> +/**
> + * 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);
                            ^^^  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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26  9:17     ` Ricard Wanderlof
  0 siblings, 0 replies; 118+ messages in thread
From: Ricard Wanderlof @ 2011-09-26  9:17 UTC (permalink / raw)
  To: David Wagner
  Cc: Arnd Bergmann, linux-embedded, Artem Bityutskiy, lkml, linux-mtd,
	Tim Bird, David Woodhouse


On Thu, 22 Sep 2011, David Wagner wrote:

> 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?

A couple of comments below:

> +/**
> + * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
> + *
> + * @file: the file on which the ioctl was invoked (usunsed)
                                                     ^^^^^^^ typo: unused
> + * @cmd: the ioctl type
> + * @arg: additional command informations
> + */
> +static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
> +                             unsigned long arg)


> +/**
> + * 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);
                            ^^^  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

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
  2011-09-22  7:58   ` David Wagner
                     ` (2 preceding siblings ...)
  (?)
@ 2011-09-26 12:11   ` Ricard Wanderlof
  -1 siblings, 0 replies; 118+ messages in thread
From: Ricard Wanderlof @ 2011-09-26 12:11 UTC (permalink / raw)
  To: David Wagner; +Cc: David Woodhouse, linux-mtd


On Thu, 22 Sep 2011, David Wagner wrote:

> 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).

One thing I'm missing is the ability to specify a volume name on the 
kernel command line (or when loading the module), i.e. similar to 
specifying for example root=ubi0:rootfs one could be able to specify e.g. 
ubiblk.volume=0:rootfs, with rootfs being a volume name.

/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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCHv7] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-09-26 12:38   ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 12:38 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, lkml, Tim Bird, David Woodhouse,
	Ricard Wanderlof, David Wagner, Artem Bityutskiy, Arnd Bergmann

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 26271 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv7] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26 12:38   ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 12:38 UTC (permalink / raw)
  To: linux-mtd
  Cc: Ricard Wanderlof, Arnd Bergmann, linux-embedded,
	Artem Bityutskiy, David Wagner, lkml, Tim Bird, David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 26270 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
  2011-09-23 10:58     ` Artem Bityutskiy
@ 2011-09-26 12:58       ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 12:58 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Arnd Bergmann

Hi,

On 09/23/2011 12:58 PM, Artem Bityutskiy wrote:
[...]
> 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?

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26 12:58       ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 12:58 UTC (permalink / raw)
  To: dedekind1
  Cc: linux-embedded, Arnd Bergmann, lkml, linux-mtd, Tim Bird,
	David Woodhouse

Hi,

On 09/23/2011 12:58 PM, Artem Bityutskiy wrote:
[...]
> 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?

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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv7] UBI: new module ubiblk: block layer on top of UBI
  2011-09-26 12:38   ` David Wagner
@ 2011-09-26 13:20     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-26 13:20 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Ricard Wanderlof, Arnd Bergmann

On Mon, 2011-09-26 at 14:38 +0200, David Wagner wrote:
> 	* "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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv7] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26 13:20     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-26 13:20 UTC (permalink / raw)
  To: David Wagner
  Cc: Ricard Wanderlof, Arnd Bergmann, linux-embedded, lkml, linux-mtd,
	Tim Bird, David Woodhouse

On Mon, 2011-09-26 at 14:38 +0200, David Wagner wrote:
> 	* "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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCHv8] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-09-26 14:25   ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 14:25 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, lkml, Tim Bird, David Woodhouse,
	Ricard Wanderlof, David Wagner, Artem Bityutskiy, Arnd Bergmann

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 25655 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv8] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26 14:25   ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 14:25 UTC (permalink / raw)
  To: linux-mtd
  Cc: Ricard Wanderlof, Arnd Bergmann, linux-embedded,
	Artem Bityutskiy, David Wagner, lkml, Tim Bird, David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 25654 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv8] UBI: new module ubiblk: block layer on top of UBI
  2011-09-26 14:25   ` David Wagner
@ 2011-09-26 14:36     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-26 14:36 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Ricard Wanderlof, Arnd Bergmann

On Mon, 2011-09-26 at 16:25 +0200, David Wagner wrote:
> +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


^ permalink raw reply	[flat|nested] 118+ messages in thread

* Re: [PATCHv8] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26 14:36     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-09-26 14:36 UTC (permalink / raw)
  To: David Wagner
  Cc: Ricard Wanderlof, Arnd Bergmann, linux-embedded, lkml, linux-mtd,
	Tim Bird, David Woodhouse

On Mon, 2011-09-26 at 16:25 +0200, David Wagner wrote:
> +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

^ permalink raw reply	[flat|nested] 118+ messages in thread

* [PATCHv9] UBI: new module ubiblk: block layer on top of UBI
  2011-06-24 13:34 ` david.wagner
@ 2011-09-26 14:40   ` David Wagner
  -1 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 14:40 UTC (permalink / raw)
  To: linux-mtd
  Cc: linux-embedded, lkml, Tim Bird, David Woodhouse,
	Ricard Wanderlof, David Wagner, Artem Bityutskiy, Arnd Bergmann

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 25552 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* [PATCHv9] UBI: new module ubiblk: block layer on top of UBI
@ 2011-09-26 14:40   ` David Wagner
  0 siblings, 0 replies; 118+ messages in thread
From: David Wagner @ 2011-09-26 14:40 UTC (permalink / raw)
  To: linux-mtd
  Cc: Ricard Wanderlof, Arnd Bergmann, linux-embedded,
	Artem Bityutskiy, David Wagner, lkml, Tim Bird, David Woodhouse

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 25551 bytes --]

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 <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@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 <dwmw2@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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv9] UBI: new module ubiblk: block layer on top of UBI
  2011-09-26 14:40   ` David Wagner
@ 2011-10-01 14:08     ` Artem Bityutskiy
  -1 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-10-01 14:08 UTC (permalink / raw)
  To: David Wagner
  Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
	Ricard Wanderlof, Arnd Bergmann

On Mon, 2011-09-26 at 16:40 +0200, David Wagner wrote:
> 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:

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


^ permalink raw reply related	[flat|nested] 118+ messages in thread

* Re: [PATCHv9] UBI: new module ubiblk: block layer on top of UBI
@ 2011-10-01 14:08     ` Artem Bityutskiy
  0 siblings, 0 replies; 118+ messages in thread
From: Artem Bityutskiy @ 2011-10-01 14:08 UTC (permalink / raw)
  To: David Wagner
  Cc: Ricard Wanderlof, Arnd Bergmann, linux-embedded, lkml, linux-mtd,
	Tim Bird, David Woodhouse

On Mon, 2011-09-26 at 16:40 +0200, David Wagner wrote:
> 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:

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

^ permalink raw reply related	[flat|nested] 118+ messages in thread

end of thread, other threads:[~2011-10-01 14:05 UTC | newest]

Thread overview: 118+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-06-24 13:34 [RFC] ubiblk: read-only block layer on top of UBI david.wagner
2011-06-24 13:34 ` david.wagner
2011-06-24 13:34 ` [PATCH] UBI: new module ubiblk: " david.wagner
2011-06-24 13:34   ` david.wagner
2011-06-27 19:26   ` Artem Bityutskiy
2011-06-27 19:26     ` Artem Bityutskiy
2011-06-28 11:35     ` David Wagner
2011-06-28 11:35       ` David Wagner
2011-06-29  6:52       ` Artem Bityutskiy
2011-06-29  6:52         ` Artem Bityutskiy
2011-06-28 14:50     ` Matthieu CASTET
2011-06-28 14:50       ` Matthieu CASTET
2011-06-28 15:32       ` David Wagner
2011-06-28 15:32         ` David Wagner
2011-06-29  6:25         ` Artem Bityutskiy
2011-06-29  6:25           ` Artem Bityutskiy
2011-06-24 13:45 ` [Addendum][RFC] ubiblk: read-only " David Wagner
2011-06-27 19:14 ` [RFC] " Artem Bityutskiy
2011-06-27 19:14   ` Artem Bityutskiy
2011-06-28 15:24 ` [RFC PATCHv2] UBI: new module ubiblk: " david.wagner
2011-06-28 15:24   ` david.wagner
2011-06-28 15:24   ` david.wagner
2011-06-29  6:54   ` Artem Bityutskiy
2011-06-29  6:54     ` Artem Bityutskiy
2011-07-26 12:27 ` [PATCH] " David Wagner
2011-07-26 12:27   ` David Wagner
2011-07-26 12:34   ` Christoph Hellwig
2011-07-26 12:34     ` Christoph Hellwig
2011-07-26 12:34     ` Christoph Hellwig
2011-07-26 12:58     ` David Wagner
2011-07-26 12:58       ` David Wagner
2011-07-28  6:14   ` Artem Bityutskiy
2011-07-28  6:14     ` Artem Bityutskiy
2011-08-15 11:56   ` Artem Bityutskiy
2011-08-15 11:56     ` Artem Bityutskiy
2011-08-17 13:17 ` [PATCHv3] " david.wagner
2011-08-17 13:17   ` david.wagner
2011-08-17 14:20   ` [PATCH] Tools for controling ubiblk David Wagner
2011-08-17 14:20     ` David Wagner
2011-08-22  8:17     ` Artem Bityutskiy
2011-08-22  8:17       ` Artem Bityutskiy
2011-08-22  7:39   ` [PATCHv3] UBI: new module ubiblk: block layer on top of UBI Artem Bityutskiy
2011-08-22  7:39     ` Artem Bityutskiy
2011-08-22  7:42   ` Artem Bityutskiy
2011-08-22  7:42     ` Artem Bityutskiy
2011-08-24 16:23     ` Arnd Bergmann
2011-08-24 16:23       ` Arnd Bergmann
2011-08-25  7:06       ` Artem Bityutskiy
2011-08-25  7:06         ` Artem Bityutskiy
2011-08-25 15:12         ` Arnd Bergmann
2011-08-25 15:12           ` Arnd Bergmann
2011-08-25 15:12           ` Arnd Bergmann
2011-09-01 12:55           ` David Wagner
2011-09-01 12:55             ` David Wagner
2011-09-01 12:55             ` David Wagner
2011-09-06  3:44           ` Artem Bityutskiy
2011-09-06  3:44             ` Artem Bityutskiy
2011-09-06  4:10             ` Artem Bityutskiy
2011-09-06  4:10               ` Artem Bityutskiy
2011-09-06  4:10               ` Artem Bityutskiy
2011-09-06  4:29               ` Artem Bityutskiy
2011-09-06  4:29                 ` Artem Bityutskiy
2011-09-08 15:26               ` Arnd Bergmann
2011-09-08 15:26                 ` Arnd Bergmann
2011-09-08 15:26                 ` Arnd Bergmann
2011-09-09 11:53                 ` Artem Bityutskiy
2011-09-09 11:53                   ` Artem Bityutskiy
2011-09-09 12:02                   ` Artem Bityutskiy
2011-09-09 12:02                     ` Artem Bityutskiy
2011-09-09 14:25                   ` Arnd Bergmann
2011-09-09 14:25                     ` Arnd Bergmann
2011-09-09 15:27                     ` Artem Bityutskiy
2011-09-09 15:27                       ` Artem Bityutskiy
2011-09-09 14:41                   ` David Wagner
2011-09-09 14:41                     ` David Wagner
2011-09-09 14:41                     ` David Wagner
2011-09-09 14:51                     ` Arnd Bergmann
2011-09-09 14:51                       ` Arnd Bergmann
2011-09-11 10:18                     ` Artem Bityutskiy
2011-09-11 10:18                       ` Artem Bityutskiy
2011-09-11 10:18                       ` Artem Bityutskiy
2011-09-11 10:35                       ` David Wagner
2011-09-11 10:35                         ` David Wagner
2011-08-24 16:15 ` [PATCHv4] " david.wagner
2011-08-24 16:15   ` david.wagner
2011-08-24 16:21   ` [PATCH] document ubiblk's usage of the same ioctl magic as a part " David Wagner
2011-08-24 16:21     ` David Wagner
2011-09-06  4:58     ` Artem Bityutskiy
2011-09-06  4:58       ` Artem Bityutskiy
2011-09-06  4:55   ` [PATCHv4] UBI: new module ubiblk: block layer on top " Artem Bityutskiy
2011-09-06  4:55     ` Artem Bityutskiy
2011-09-12  9:51 ` [PATCHv5] " David Wagner
2011-09-12  9:51   ` David Wagner
2011-09-12  9:51   ` David Wagner
2011-09-19  4:50   ` Artem Bityutskiy
2011-09-19  4:50     ` Artem Bityutskiy
2011-09-22  7:58 ` [PATCHv6] " David Wagner
2011-09-22  7:58   ` David Wagner
2011-09-23 10:58   ` Artem Bityutskiy
2011-09-23 10:58     ` Artem Bityutskiy
2011-09-26 12:58     ` David Wagner
2011-09-26 12:58       ` David Wagner
2011-09-26  9:17   ` Ricard Wanderlof
2011-09-26  9:17     ` Ricard Wanderlof
2011-09-26  9:17     ` Ricard Wanderlof
2011-09-26 12:11   ` Ricard Wanderlof
2011-09-26 12:38 ` [PATCHv7] " David Wagner
2011-09-26 12:38   ` David Wagner
2011-09-26 13:20   ` Artem Bityutskiy
2011-09-26 13:20     ` Artem Bityutskiy
2011-09-26 14:25 ` [PATCHv8] " David Wagner
2011-09-26 14:25   ` David Wagner
2011-09-26 14:36   ` Artem Bityutskiy
2011-09-26 14:36     ` Artem Bityutskiy
2011-09-26 14:40 ` [PATCHv9] " David Wagner
2011-09-26 14:40   ` David Wagner
2011-10-01 14:08   ` Artem Bityutskiy
2011-10-01 14:08     ` Artem Bityutskiy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.