Embedded Linux development
 help / color / mirror / Atom feed
* [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1308922482-14967-1-git-send-email-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 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

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1316678312-8913-1-git-send-email-david.wagner@free-electrons.com>

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

* Re: Timer interrupt on Linux 3.0.3
From: Vineeth @ 2011-09-26  9:11 UTC (permalink / raw)
  To: Scott Wood, linuxppc-dev, linux-embedded; +Cc: mohanreddykv
In-Reply-To: <CAEZ9+qnSXXi5w9BbwyaQWQybCtaCsJ0T5j5ySEjOkpnnYmr7aA@mail.gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 1575 bytes --]

Hi,

there's a good news which is pretty bad. Good news is that we identified why
our processor timer not working. its because the TBEN in our board is pulled
LOW. which means the decrementer and the timebase registers wont work.

The board design cannot be changed !!! but we have an MPC107 connected with
our processor. Our plan is to use the timer of MPC107 and register our
timer_interrupt function with this timer interrupt. I think that's the only
workaround left now.

Thanks
Vineeth

On Thu, Sep 22, 2011 at 11:52 AM, MohanReddy koppula <mohanreddykv@gmail.com
> wrote:

> I had the same issue with an MPC885 board. My kernel was 2.6.33. On
> that board decrementer exception was not working. I replaced the
> board, took new board (MPC885 only, just another board) and the same
> kernel worked fine. I don't know how the problem was solved.
>
> -Mohan
>
> On 9/22/11, Scott Wood <scottwood@freescale.com> wrote:
> > On 09/21/2011 01:56 AM, Vineeth wrote:
> >>>> What was the issue?  You really should try to make this work rather
> than
> >>>> hack around it.
> >>
> >> what we found was the decrementer is not generating an exception when it
> >> becomes 0. and the timebase registers are not getting incremented too.
> >
> > Does the decrementer actually tick until it reaches zero, or do it and
> > the timebase never tick?
> >
> > Is the TBEN input to the CPU asserted?
> >
> > -Scott
> >
> > _______________________________________________
> > Linuxppc-dev mailing list
> > Linuxppc-dev@lists.ozlabs.org
> > https://lists.ozlabs.org/listinfo/linuxppc-dev
> >
>

[-- Attachment #1.2: Type: text/html, Size: 2239 bytes --]

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

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

^ permalink raw reply

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1316678312-8913-1-git-send-email-david.wagner@free-electrons.com>


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

* [PATCHv7] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1308922482-14967-1-git-send-email-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 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

* Re: [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1316775538.19921.19.camel@sauron>

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

* Re: [PATCHv7] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1317040680-6199-1-git-send-email-david.wagner@free-electrons.com>

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

* [PATCHv8] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1308922482-14967-1-git-send-email-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 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

* Re: [PATCHv8] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1317047152-18809-1-git-send-email-david.wagner@free-electrons.com>

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

* [PATCHv9] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1308922482-14967-1-git-send-email-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 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

* Re: dma_unmap_single() lacking cache sync on some archs?
From: Arvid Brodin @ 2011-09-27 12:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-embedded, Hans-Christian Egtvedt, Haavard Skinnemoen, arnd

[Resending with CC to affected parties]

Hi,

I would expect cache synchronization for DMA_TO_DEVICE and DMA_BIDIRECTIONAL
when dma_map_single() is called, and for DMA_FROM_DEVICE and DMA_BIDIRECTIONAL
when dma_unmap_single() is called.

However, on some architechtures (at least avr32, blackfin, ...), cache
synchronization only happens when dma_map_single() is called (and then
irrespective of DMA direction). dma_unmap_single() is a no-op for these archs.

See e.g. http://lxr.linux.no/#linux+v3.0.4/arch/avr32/include/asm/dma-mapping.h#L117

Isn't this a bug?

(Please CC me in responses.)

-- 
Arvid Brodin
Enea Services Stockholm AB

^ permalink raw reply

* Re: dma_unmap_single() lacking cache sync on some archs?
From: Håvard Skinnemoen @ 2011-09-27 16:55 UTC (permalink / raw)
  To: Arvid Brodin; +Cc: linux-kernel, linux-embedded, Hans-Christian Egtvedt, arnd
In-Reply-To: <4E81BDEE.2080601@enea.com>

Hi,

On Tue, Sep 27, 2011 at 5:13 AM, Arvid Brodin <arvid.brodin@enea.com> wrote:
> [Resending with CC to affected parties]
>
> Hi,
>
> I would expect cache synchronization for DMA_TO_DEVICE and DMA_BIDIRECTIONAL
> when dma_map_single() is called, and for DMA_FROM_DEVICE and DMA_BIDIRECTIONAL
> when dma_unmap_single() is called.
>
> However, on some architechtures (at least avr32, blackfin, ...), cache
> synchronization only happens when dma_map_single() is called (and then
> irrespective of DMA direction). dma_unmap_single() is a no-op for these archs.
>
> See e.g. http://lxr.linux.no/#linux+v3.0.4/arch/avr32/include/asm/dma-mapping.h#L117
>
> Isn't this a bug?

I don't think so. What do other architectures do?

We always need to sync before the transfer because if there is dirty
data in the cache, it might get written to RAM during the transfer,
which would be bad. Then, since the relevant cache lines are already
clean and invalid, and the CPU is not allowed to access the buffer
during the transfer, there's no need to sync again when the transfer
is complete.

Havard

^ permalink raw reply

* Re: dma_unmap_single() lacking cache sync on some archs?
From: Arnd Bergmann @ 2011-09-27 18:27 UTC (permalink / raw)
  To: Håvard Skinnemoen
  Cc: Arvid Brodin, linux-kernel, linux-embedded,
	Hans-Christian Egtvedt
In-Reply-To: <CAF8ieYUPcer2ZwaOi6YbiZQPeDBFukT2_dPStH10G_15Rh1fpw@mail.gmail.com>

On Tuesday 27 September 2011 09:55:02 Håvard Skinnemoen wrote:
> 
> On Tue, Sep 27, 2011 at 5:13 AM, Arvid Brodin <arvid.brodin@enea.com> wrote:
> > [Resending with CC to affected parties]
> >
> > Hi,
> >
> > I would expect cache synchronization for DMA_TO_DEVICE and DMA_BIDIRECTIONAL
> > when dma_map_single() is called, and for DMA_FROM_DEVICE and DMA_BIDIRECTIONAL
> > when dma_unmap_single() is called.
> >
> > However, on some architechtures (at least avr32, blackfin, ...), cache
> > synchronization only happens when dma_map_single() is called (and then
> > irrespective of DMA direction). dma_unmap_single() is a no-op for these archs.
> >
> > See e.g. http://lxr.linux.no/#linux+v3.0.4/arch/avr32/include/asm/dma-mapping.h#L117
> >
> > Isn't this a bug?
> 
> I don't think so. What do other architectures do?
> 
> We always need to sync before the transfer because if there is dirty
> data in the cache, it might get written to RAM during the transfer,
> which would be bad. Then, since the relevant cache lines are already
> clean and invalid, and the CPU is not allowed to access the buffer
> during the transfer, there's no need to sync again when the transfer
> is complete.

On some architectures, e.g. ARMv6 and higher, a speculative prefetch might
cause cache lines to be read again while an inbound DMA is on its way.
On those architectures you need to discard cache lines before reading from
the buffer. In fact also for DMA_FROM_DEVICE you need to flush or invalidate
the cache for the buffer before the transfer and invalidate the cache again
after the transfer.

Most architectures however do not require this.

	Arnd

^ permalink raw reply

* Re: dma_unmap_single() lacking cache sync on some archs?
From: Arvid Brodin @ 2011-09-29 18:56 UTC (permalink / raw)
  To: Håvard Skinnemoen
  Cc: linux-kernel, linux-embedded, Hans-Christian Egtvedt, arnd
In-Reply-To: <CAF8ieYUPcer2ZwaOi6YbiZQPeDBFukT2_dPStH10G_15Rh1fpw@mail.gmail.com>

Håvard Skinnemoen wrote:
> Hi,
> 
> On Tue, Sep 27, 2011 at 5:13 AM, Arvid Brodin <arvid.brodin@enea.com> wrote:
>> [Resending with CC to affected parties]
>>
>> Hi,
>>
>> I would expect cache synchronization for DMA_TO_DEVICE and DMA_BIDIRECTIONAL
>> when dma_map_single() is called, and for DMA_FROM_DEVICE and DMA_BIDIRECTIONAL
>> when dma_unmap_single() is called.
>>
>> However, on some architechtures (at least avr32, blackfin, ...), cache
>> synchronization only happens when dma_map_single() is called (and then
>> irrespective of DMA direction). dma_unmap_single() is a no-op for these archs.
>>
>> See e.g. http://lxr.linux.no/#linux+v3.0.4/arch/avr32/include/asm/dma-mapping.h#L117
>>
>> Isn't this a bug?
> 
> I don't think so. What do other architectures do?
> 
> We always need to sync before the transfer because if there is dirty
> data in the cache, it might get written to RAM during the transfer,
> which would be bad. Then, since the relevant cache lines are already
> clean and invalid, and the CPU is not allowed to access the buffer
> during the transfer, there's no need to sync again when the transfer
> is complete.

I see. Thanks for the explanation!

> 
> Havard

-- 
Arvid Brodin
Enea Services Stockholm AB

^ permalink raw reply

* Re: [PATCHv9] UBI: new module ubiblk: block layer on top of UBI
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
In-Reply-To: <1317048049-19391-1-git-send-email-david.wagner@free-electrons.com>

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

* Re: debugging multi threaded applicatiosn on arm - status?
From: Peter Waechtler @ 2011-10-09  9:55 UTC (permalink / raw)
  To: linux-embedded
In-Reply-To: <20110617153930.GA28570@merkur.ravnborg.org>

Sam Ravnborg <sam <at> ravnborg.org> writes:
> 
> >> Is it possible to bebug multi-threaded applications using gdb on ARM these 
days?
> >>
> >
> > It works for me, using various ARM 926 and Cortex A8 chips and kernel  
> > versions from 2.6.27 to the present day. Which version of gbd do you  
> > have? gdb version 7.x works much better than 6.x in my experience.
> 
> We are using 6.8 - which may be the culprint.
> Does it work with NPTL too?
> 

Sam,

why don't you describe what's not working?

I guess you have a stripped libpthread and gdb isn't able to fetch
the thread infos via libthread_db.so.

On ARM you must not strip libpthread too much - see

http://sourceware.org/gdb/wiki/FAQ   question 6


  Peter




^ permalink raw reply

* Embedded Linux Conference Europe 2011 videos
From: Michael Opdenacker @ 2011-11-07  7:14 UTC (permalink / raw)
  To: CE Linux Developers List, linux-embedded mailing list

Greetings,

We are pleased to release the videos of the Embedded Linux Conference
Europe that took place one week ago in Prague:
http://free-electrons.com/blog/elce-2011-videos/

Happy downloads!

Michael.

-- 
Michael Opdenacker, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
+33 484 253 396

^ permalink raw reply

* Re: Embedded Linux Conference Europe 2011 videos
From: Wolfram Sang @ 2011-11-07  8:38 UTC (permalink / raw)
  To: Michael Opdenacker; +Cc: CE Linux Developers List, linux-embedded mailing list
In-Reply-To: <4EB7853E.3040503@free-electrons.com>

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

On Mon, Nov 07, 2011 at 08:14:06AM +0100, Michael Opdenacker wrote:
> Greetings,
> 
> We are pleased to release the videos of the Embedded Linux Conference
> Europe that took place one week ago in Prague:
> http://free-electrons.com/blog/elce-2011-videos/

Wow, that was _very_ fast! Congrats and thanks!

   Wolfram

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* A new Subsystem for Current Management
From: R, Durgadoss @ 2011-11-08 11:09 UTC (permalink / raw)
  To: linux-embedded@vger.kernel.org

Hi All,

[Very Sorry for the Big Email]

[I have posted this on lm-sensors and platform-drivers-x86
lists earlier. As per some recommendations there, posting it
here]

As we all know, Linux is increasingly being used in handhelds.
The Hardware that supports the handhelds is also becoming
Performance-centric. With this, we need a way to efficiently
monitor the current consumption of the platform and take actions
when the platform draws more current, than it should.

Where this can happen ?
-----------------------
In a handheld, there are many components that demand high
Current. For example, Camera Flash, Video Streaming, 3G Voice
Call etc. Typically, two or more of these components are used
simultaneously in a real-time scenario. When this happens, the
current draw of the platform surges. If this surge lasts for
more than a specific time, it could crash the platform irrecoverably.

How do we tackle this ?
-----------------------
In Intel Medfield (Atom based) platform we had a driver that
Configures the current limits. When the platform current draws
more current than the programmed limit, the hardware generates
interrupt. The driver receives this interrupt and notifies the
user space to take appropriate actions.
The patch and the subsequent discussions can be found here:
http://comments.gmane.org/gmane.linux.drivers.platform.x86.devel/1197

To Generalize:
--------------
With many more platforms to come, having a separate driver for each
results in heavy code duplication. Also, there arises a problem of
where to put these kind of drivers ? Hence I propose the idea of having
a Current Management subsystem.

This will provide a generic ABI, API, so that the platform specific
drivers can register with this framework (and thus avail the basic
needs) and handle the events in their own way.

In simple terms, this framework will offer something like this:
	Current[1-N]_limit - set of current limits
	Voltage[1-X]_limit - set of voltage limits
	Controllers[1-Y]_enable - These are the components by throttling/
      disabling which the current consumption can be brought down.

With the Controllers we can follow two approaches:
A) Each component driver registers with the current framework and gets
notified when the current surge happens. On receiving the notification,
it throttles its performance. There will be a follow-up notification,
indicating that 'we are out of the high current' situation; so that
the component resumes to operation at its full performance.

B) The Current framework forwards the notification to the upper
layers and lets them decide what to do.

I agree that A) bloats the size of all the component drivers a bit,
but considering the fact that the surge has to be brought down as
soon as possible (and the delay in reacting to the event if we
pass it to the upper layers) I am inclined towards A).

I would like to see the opinion of the community on this entire
stuff, before I start writing some code.

Please Help,
Durga

^ permalink raw reply

* Re: A new Subsystem for Current Management
From: Felipe Balbi @ 2011-11-08 11:15 UTC (permalink / raw)
  To: R, Durgadoss; +Cc: linux-embedded@vger.kernel.org
In-Reply-To: <D6D887BA8C9DFF48B5233887EF04654109F10AF2C7@bgsmsx502.gar.corp.intel.com>

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

Hi,

On Tue, Nov 08, 2011 at 04:39:17PM +0530, R, Durgadoss wrote:
> [Very Sorry for the Big Email]
> 
> [I have posted this on lm-sensors and platform-drivers-x86
> lists earlier. As per some recommendations there, posting it
> here]
> 
> As we all know, Linux is increasingly being used in handhelds.
> The Hardware that supports the handhelds is also becoming
> Performance-centric. With this, we need a way to efficiently
> monitor the current consumption of the platform and take actions
> when the platform draws more current, than it should.
> 
> Where this can happen ?
> -----------------------
> In a handheld, there are many components that demand high
> Current. For example, Camera Flash, Video Streaming, 3G Voice
> Call etc. Typically, two or more of these components are used
> simultaneously in a real-time scenario. When this happens, the
> current draw of the platform surges. If this surge lasts for
> more than a specific time, it could crash the platform irrecoverably.
> 
> How do we tackle this ?
> -----------------------
> In Intel Medfield (Atom based) platform we had a driver that
> Configures the current limits. When the platform current draws
> more current than the programmed limit, the hardware generates
> interrupt. The driver receives this interrupt and notifies the
> user space to take appropriate actions.
> The patch and the subsequent discussions can be found here:
> http://comments.gmane.org/gmane.linux.drivers.platform.x86.devel/1197
> 
> To Generalize:
> --------------
> With many more platforms to come, having a separate driver for each
> results in heavy code duplication. Also, there arises a problem of
> where to put these kind of drivers ? Hence I propose the idea of having
> a Current Management subsystem.
> 
> This will provide a generic ABI, API, so that the platform specific
> drivers can register with this framework (and thus avail the basic
> needs) and handle the events in their own way.
> 
> In simple terms, this framework will offer something like this:
> 	Current[1-N]_limit - set of current limits
> 	Voltage[1-X]_limit - set of voltage limits
> 	Controllers[1-Y]_enable - These are the components by throttling/
>       disabling which the current consumption can be brought down.
> 
> With the Controllers we can follow two approaches:
> A) Each component driver registers with the current framework and gets
> notified when the current surge happens. On receiving the notification,
> it throttles its performance. There will be a follow-up notification,
> indicating that 'we are out of the high current' situation; so that
> the component resumes to operation at its full performance.
> 
> B) The Current framework forwards the notification to the upper
> layers and lets them decide what to do.
> 
> I agree that A) bloats the size of all the component drivers a bit,
> but considering the fact that the surge has to be brought down as
> soon as possible (and the delay in reacting to the event if we
> pass it to the upper layers) I am inclined towards A).
> 
> I would like to see the opinion of the community on this entire
> stuff, before I start writing some code.

looks like this should be done on hwmon ?

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* RE: A new Subsystem for Current Management
From: R, Durgadoss @ 2011-11-08 11:25 UTC (permalink / raw)
  To: balbi@ti.com; +Cc: linux-embedded@vger.kernel.org
In-Reply-To: <20111108111525.GB6921@legolas.emea.dhcp.ti.com>

Hi,

[snip.]
> >
> > I would like to see the opinion of the community on this entire
> > stuff, before I start writing some code.
> 
> looks like this should be done on hwmon ?

We discussed it there. The link I sent in the previous mail,
has all the discussions. 

Thanks,
Durga

^ permalink raw reply

* Re: A new Subsystem for Current Management
From: Christian Gagneraud @ 2011-11-08 11:58 UTC (permalink / raw)
  To: linux-embedded@vger.kernel.org
In-Reply-To: <D6D887BA8C9DFF48B5233887EF04654109F10AF2C7@bgsmsx502.gar.corp.intel.com>

On 08/11/11 11:09, R, Durgadoss wrote:
> Hi All,

Hi Durga,

>
> [Very Sorry for the Big Email]
>
> [I have posted this on lm-sensors and platform-drivers-x86
> lists earlier. As per some recommendations there, posting it
> here]
>
> As we all know, Linux is increasingly being used in handhelds.
> The Hardware that supports the handhelds is also becoming
> Performance-centric. With this, we need a way to efficiently
> monitor the current consumption of the platform and take actions
> when the platform draws more current, than it should.
>
> Where this can happen ?
> -----------------------
> In a handheld, there are many components that demand high
> Current. For example, Camera Flash, Video Streaming, 3G Voice
> Call etc. Typically, two or more of these components are used
> simultaneously in a real-time scenario. When this happens, the
> current draw of the platform surges. If this surge lasts for
> more than a specific time, it could crash the platform irrecoverably.
>
> How do we tackle this ?
> -----------------------
> In Intel Medfield (Atom based) platform we had a driver that
> Configures the current limits. When the platform current draws
> more current than the programmed limit, the hardware generates
> interrupt. The driver receives this interrupt and notifies the
> user space to take appropriate actions.
> The patch and the subsequent discussions can be found here:
> http://comments.gmane.org/gmane.linux.drivers.platform.x86.devel/1197
>
> To Generalize:
> --------------
> With many more platforms to come, having a separate driver for each
> results in heavy code duplication. Also, there arises a problem of
> where to put these kind of drivers ? Hence I propose the idea of having
> a Current Management subsystem.
>
> This will provide a generic ABI, API, so that the platform specific
> drivers can register with this framework (and thus avail the basic
> needs) and handle the events in their own way.
>
> In simple terms, this framework will offer something like this:
> 	Current[1-N]_limit - set of current limits
> 	Voltage[1-X]_limit - set of voltage limits
> 	Controllers[1-Y]_enable - These are the components by throttling/
>        disabling which the current consumption can be brought down.

Could you elaborate a bit more on this, perhaps by taking the Medfield 
platform as an example. I don't see above any reference to timers or 
maybe it's part of {Current|Voltage}[1-X]_limit?

As well, it would be nice if this system could work nicely beside 
linux-iio. For example to provide the ability to plot currents and 
voltages, and see when/why the actions have been taken.

Chris

>
> With the Controllers we can follow two approaches:
> A) Each component driver registers with the current framework and gets
> notified when the current surge happens. On receiving the notification,
> it throttles its performance. There will be a follow-up notification,
> indicating that 'we are out of the high current' situation; so that
> the component resumes to operation at its full performance.
>
> B) The Current framework forwards the notification to the upper
> layers and lets them decide what to do.
>
> I agree that A) bloats the size of all the component drivers a bit,
> but considering the fact that the surge has to be brought down as
> soon as possible (and the delay in reacting to the event if we
> pass it to the upper layers) I am inclined towards A).
>
> I would like to see the opinion of the community on this entire
> stuff, before I start writing some code.
>
> Please Help,
> Durga
> --
> To unsubscribe from this list: send the line "unsubscribe linux-embedded" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Christian Gagneraud,
Embedded systems engineer.
Techworks Marine
1 Harbour road
Dun Laoghaire
Co. Dublin
Ireland
Tel: + 353 (0) 1 236 5990
Web: http://www.techworks.ie/

^ permalink raw reply

* Re: A new Subsystem for Current Management
From: Mark Brown @ 2011-11-08 13:56 UTC (permalink / raw)
  To: R, Durgadoss; +Cc: linux-embedded@vger.kernel.org
In-Reply-To: <D6D887BA8C9DFF48B5233887EF04654109F10AF2C7@bgsmsx502.gar.corp.intel.com>

On Tue, Nov 08, 2011 at 04:39:17PM +0530, R, Durgadoss wrote:

> [I have posted this on lm-sensors and platform-drivers-x86
> lists earlier. As per some recommendations there, posting it
> here]

lkml would probably be useful.  It'd also help if you could publish code
along with your mail, in general people are much more likely to review
concrete code.

> In simple terms, this framework will offer something like this:
> 	Current[1-N]_limit - set of current limits
> 	Voltage[1-X]_limit - set of voltage limits

What would the voltage limits be?  Whatever is going on here there
should be some integration with the regulator framework, modern
regulators are often able to report when they go out of regulator and
able to impose current limits.

^ permalink raw reply

* Remote IP setup of devices in a network
From: Sam Ravnborg @ 2011-11-08 18:54 UTC (permalink / raw)
  To: Linux-embedded

I am faced with a challenge that we ship devices which are
all preconfigured to a fixed IP address, and we want to provide
an easy solution to do remote IP configuration of all devices
connected to the same network.
In other words - all devices are reachable by a broadcast package.

The network may or may not include a DHCP server - which
we in any case do not have control over.

In the typical situation we will assign static IP
addresses to the devices - but sometimes we may also
configure them to DHCP.

So we are looking for a (de facto?) way to do this.
Any hints?

I haved tried to ask google without luck.

The idea is to use an UDP broadcast to discover all devices,
and a similar UDP broadcast to configure the devices.
In the latter the MAC will be the key to address individual devices.

As we are talking less than 100 devices this looks quite doable.
But I do not want to invent something again if it already exists.

Thanks in advance for any inputs,

	Sam

^ permalink raw reply

* Re: Remote IP setup of devices in a network
From: David Woodhouse @ 2011-11-08 19:11 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: Linux-embedded
In-Reply-To: <20111108185421.GA27727@merkur.ravnborg.org>

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

On Tue, 2011-11-08 at 19:54 +0100, Sam Ravnborg wrote:
> The idea is to use an UDP broadcast to discover all devices,
> and a similar UDP broadcast to configure the devices.
> In the latter the MAC will be the key to address individual devices.

You could almost be describing link-local IPv6. Each device
automatically gets an IPv6 address based on its MAC address, which you
can use for unicast addressing. The broadcast (or multicast) bit is easy
enough too.

-- 
dwmw2

[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 5818 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox