* [PATCH v2 1/6] UBI: add UBI control device
2007-12-20 14:22 [PATCH v2 0/6] UBI: make UBI devices dynamic Artem Bityutskiy
@ 2007-12-20 14:22 ` Artem Bityutskiy
2007-12-20 14:23 ` [PATCH v2 2/6] UBI: add UBI devices reference counting Artem Bityutskiy
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Artem Bityutskiy @ 2007-12-20 14:22 UTC (permalink / raw)
To: linux-mtd; +Cc: Frank Haverkamp, Arnd Bergmann, Andreas Arnez
>From 028997ff1dca743d538187962133ea77aefbc510 Mon Sep 17 00:00:00 2001
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Date: Sun, 16 Dec 2007 16:59:31 +0200
Subject: [PATCH] UBI: add UBI control device
This patch is a preparation to make UBI devices dynamic. It
adds an UBI control device which has dynamically allocated
major number and registers itself as "ubi_ctrl". It does not
do anything so far. The idea is that this device will allow
to attach/detach MTD devices from userspace.
This is symilar to what the Linux device mapper has.
The next things to do are:
* Fix UBI, because it now assumes UBI devices cannot go away
* Implement control device ioctls which will attach/detach MTD
devices
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
---
drivers/mtd/ubi/build.c | 69 ++++++++++++++++++++++++++++++++++++++++-------
drivers/mtd/ubi/cdev.c | 10 +++++++
drivers/mtd/ubi/ubi.h | 3 +-
3 files changed, 71 insertions(+), 11 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index b3efb2f..8d05d96 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -21,11 +21,16 @@
*/
/*
- * This file includes UBI initialization and building of UBI devices. At the
- * moment UBI devices may only be added while UBI is initialized, but dynamic
- * device add/remove functionality is planned. Also, at the moment we only
- * attach UBI devices by scanning, which will become a bottleneck when flashes
- * reach certain large size. Then one may improve UBI and add other methods.
+ * This file includes UBI initialization and building of UBI devices.
+ *
+ * When UBI is initialized, it attaches all the MTD devices specified as the
+ * module load parameters or the kernel boot parameters. If MTD devices were
+ * specified, UBI does not attach any MTD device, but it is possible to do
+ * later using the "UBI control device".
+ *
+ * At the moment we only attach UBI devices by scanning, which will become a
+ * bottleneck when flashes reach certain large size. Then one may improve UBI
+ * and add other methods, although it does not seem to be easy to do.
*/
#include <linux/err.h>
@@ -70,6 +75,11 @@ struct kmem_cache *ubi_ltree_slab;
/* Slab cache for wear-leveling entries */
struct kmem_cache *ubi_wl_entry_slab;
+/* UBI control character device major number */
+static int ubi_ctrl_major;
+
+/* Linux device model object corresponding to the control UBI device */
+static struct device ubi_ctrl_dev;
/* "Show" method for files in '/<sysfs>/class/ubi/' */
static ssize_t ubi_version_show(struct class *class, char *buf)
@@ -142,6 +152,9 @@ static ssize_t dev_attribute_show(struct device *dev,
/* Fake "release" method for UBI devices */
static void dev_release(struct device *dev) { }
+/* Fake "release" method for UBI control device */
+static void ctrl_dev_release(struct device *dev) { }
+
/**
* ubi_sysfs_init - initialize sysfs for an UBI device.
* @ubi: UBI device description object
@@ -701,19 +714,45 @@ static int __init ubi_init(void)
return -EINVAL;
}
+ /* Allocate major number for the UBI control device and register it */
+ ubi_ctrl_major = register_chrdev(0, "ubi_ctrl",
+ &ubi_ctrl_cdev_operations);
+ if (ubi_ctrl_major < 0) {
+ err = ubi_ctrl_major;
+ printk(KERN_ERR "UBI error: cannot register control device\n");
+ return err;
+ }
+
+ /* Create base sysfs directory and sysfs files */
ubi_class = class_create(THIS_MODULE, UBI_NAME_STR);
- if (IS_ERR(ubi_class))
- return PTR_ERR(ubi_class);
+ if (IS_ERR(ubi_class)) {
+ err = PTR_ERR(ubi_class);
+ printk(KERN_ERR "UBI error: cannot create UBI class\n");
+ goto out_cdev_unreg;
+ }
err = class_create_file(ubi_class, &ubi_version);
- if (err)
+ if (err) {
+ printk(KERN_ERR "UBI error: cannot create sysfs file\n");
goto out_class;
+ }
+
+ /* Register the control device in the Linux device system */
+ ubi_ctrl_dev.release = ctrl_dev_release;
+ ubi_ctrl_dev.devt = MKDEV(ubi_ctrl_major, 0);
+ ubi_ctrl_dev.class = ubi_class;
+ sprintf(&ubi_ctrl_dev.bus_id[0], "ubi_ctrl");
+ err = device_register(&ubi_ctrl_dev);
+ if (err) {
+ printk(KERN_ERR "UBI error: cannot register device\n");
+ goto out_version;
+ }
ubi_ltree_slab = kmem_cache_create("ubi_ltree_slab",
sizeof(struct ubi_ltree_entry), 0,
0, <ree_entry_ctor);
if (!ubi_ltree_slab)
- goto out_version;
+ goto out_dev_unreg;
ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
sizeof(struct ubi_wl_entry),
@@ -727,8 +766,11 @@ static int __init ubi_init(void)
cond_resched();
err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs);
- if (err)
+ if (err) {
+ printk(KERN_ERR "UBI error: cannot attach %s\n",
+ p->name);
goto out_detach;
+ }
}
return 0;
@@ -739,10 +781,15 @@ out_detach:
kmem_cache_destroy(ubi_wl_entry_slab);
out_ltree:
kmem_cache_destroy(ubi_ltree_slab);
+out_dev_unreg:
+ device_unregister(&ubi_ctrl_dev);
out_version:
class_remove_file(ubi_class, &ubi_version);
out_class:
class_destroy(ubi_class);
+out_cdev_unreg:
+ unregister_chrdev(ubi_ctrl_major, "ubi_ctrl");
+ printk(KERN_ERR "UBI error: cannot initialize UBI, error %d\n", err);
return err;
}
module_init(ubi_init);
@@ -756,8 +803,10 @@ static void __exit ubi_exit(void)
detach_mtd_dev(ubi_devices[i]);
kmem_cache_destroy(ubi_wl_entry_slab);
kmem_cache_destroy(ubi_ltree_slab);
+ device_unregister(&ubi_ctrl_dev);
class_remove_file(ubi_class, &ubi_version);
class_destroy(ubi_class);
+ unregister_chrdev(ubi_ctrl_major, "ubi_ctrl");
}
module_exit(ubi_exit);
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 22c15a3..bc900d2 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -28,6 +28,11 @@
*
* Major and minor numbers are assigned dynamically to both UBI and volume
* character devices.
+ *
+ * Well, there is the third kind of character devices - the UBI control
+ * character device, which allows to manipulate by UBI devices - create and
+ * delete them. In other words, it is used for attaching and detaching MTD
+ * devices.
*/
#include <linux/module.h>
@@ -693,6 +698,11 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
return err;
}
+/* UBI control character device operations */
+struct file_operations ubi_ctrl_cdev_operations = {
+ .owner = THIS_MODULE,
+};
+
/* UBI character device operations */
struct file_operations ubi_cdev_operations = {
.owner = THIS_MODULE,
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 994233d..21c0283 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -235,7 +235,7 @@ struct ubi_wl_entry;
/**
* struct ubi_device - UBI device description structure
- * @dev: class device object to use the the Linux device model
+ * @dev: UBI device object to use the the Linux device model
* @cdev: character device object to create character device
* @ubi_num: UBI device number
* @ubi_name: UBI device name
@@ -398,6 +398,7 @@ struct ubi_device {
extern struct kmem_cache *ubi_ltree_slab;
extern struct kmem_cache *ubi_wl_entry_slab;
+extern struct file_operations ubi_ctrl_cdev_operations;
extern struct file_operations ubi_cdev_operations;
extern struct file_operations ubi_vol_cdev_operations;
extern struct ubi_device *ubi_devices[];
--
1.5.3.4
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 2/6] UBI: add UBI devices reference counting
2007-12-20 14:22 [PATCH v2 0/6] UBI: make UBI devices dynamic Artem Bityutskiy
2007-12-20 14:22 ` [PATCH v2 1/6] UBI: add UBI control device Artem Bityutskiy
@ 2007-12-20 14:23 ` Artem Bityutskiy
2007-12-20 14:23 ` [PATCH v2 3/6] UBI: prepare attach and detach functions Artem Bityutskiy
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Artem Bityutskiy @ 2007-12-20 14:23 UTC (permalink / raw)
To: linux-mtd; +Cc: Frank Haverkamp, Arnd Bergmann, Andreas Arnez
>From 502c3f72aacff2056497bbbdb219459914a28a7f Mon Sep 17 00:00:00 2001
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Date: Mon, 17 Dec 2007 17:37:26 +0200
Subject: [PATCH] UBI: add UBI devices reference counting
This is one more step on the way to "removable" UBI devices. It
adds reference counting for UBI devices. Every time a volume on
this device is opened - the device's refcount is increased. It
is also increased if someone is reading any sysfs file of this
UBI device or of one of its volumes.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
---
drivers/mtd/ubi/build.c | 142 ++++++++++++++++++++++++++++++++++++++++++-----
drivers/mtd/ubi/cdev.c | 34 +++--------
drivers/mtd/ubi/eba.c | 5 ++
drivers/mtd/ubi/kapi.c | 59 ++++++++++++++------
drivers/mtd/ubi/ubi.h | 9 +++-
drivers/mtd/ubi/vmt.c | 14 +++--
drivers/mtd/ubi/wl.c | 1 +
7 files changed, 201 insertions(+), 63 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 8d05d96..e60a646 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -63,9 +63,6 @@ static int mtd_devs = 0;
/* MTD devices specification parameters */
static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
-/* All UBI devices in system */
-struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
-
/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
struct class *ubi_class;
@@ -81,6 +78,12 @@ static int ubi_ctrl_major;
/* Linux device model object corresponding to the control UBI device */
static struct device ubi_ctrl_dev;
+/* All UBI devices in system */
+static struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
+
+/* Protects @ubi_devices and @ubi->ref_count */
+static DEFINE_SPINLOCK(ubi_devices_lock);
+
/* "Show" method for files in '/<sysfs>/class/ubi/' */
static ssize_t ubi_version_show(struct class *class, char *buf)
{
@@ -116,37 +119,145 @@ static struct device_attribute dev_min_io_size =
static struct device_attribute dev_bgt_enabled =
__ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL);
+/**
+ * ubi_get_device - get UBI device.
+ * @ubi_num: UBI device number
+ *
+ * This function returns UBI device description object for UBI device number
+ * @ubi_num, or %NULL if the device does not exist. This function increases the
+ * device reference count to prevent removal of the device. In other words, the
+ * device cannot be removed if its reference count is not zero.
+ */
+struct ubi_device *ubi_get_device(int ubi_num)
+{
+ struct ubi_device *ubi;
+
+ spin_lock(&ubi_devices_lock);
+ ubi = ubi_devices[ubi_num];
+ if (ubi) {
+ ubi_assert(ubi->ref_count >= 0);
+ ubi->ref_count += 1;
+ get_device(&ubi->dev);
+ }
+ spin_unlock(&ubi_devices_lock);
+
+ return ubi;
+}
+
+/**
+ * ubi_put_device - drop an UBI device reference.
+ * @ubi: UBI device description object
+ */
+void ubi_put_device(struct ubi_device *ubi)
+{
+ spin_lock(&ubi_devices_lock);
+ ubi->ref_count -= 1;
+ put_device(&ubi->dev);
+ spin_unlock(&ubi_devices_lock);
+}
+
+/**
+ * ubi_get_by_major - get UBI device description object by character device
+ * major number.
+ * @major: major number
+ *
+ * This function is similar to 'ubi_get_device()', but it searches the device
+ * by its major number.
+ */
+struct ubi_device *ubi_get_by_major(int major)
+{
+ int i;
+ struct ubi_device *ubi;
+
+ spin_lock(&ubi_devices_lock);
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
+ ubi = ubi_devices[i];
+ if (ubi && MAJOR(ubi->cdev.dev) == major) {
+ ubi_assert(ubi->ref_count >= 0);
+ ubi->ref_count += 1;
+ get_device(&ubi->dev);
+ spin_unlock(&ubi_devices_lock);
+ return ubi;
+ }
+ }
+ spin_unlock(&ubi_devices_lock);
+
+ return NULL;
+}
+
+/**
+ * ubi_major2num - get UBI device number by character device major number.
+ * @major: major number
+ *
+ * This function searches UBI device number object by its major number. If UBI
+ * device was not found, this function returns -ENODEV, othewise the UBI device
+ * number is returned.
+ */
+int ubi_major2num(int major)
+{
+ int i, ubi_num = -ENODEV;
+
+ spin_lock(&ubi_devices_lock);
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
+ struct ubi_device *ubi = ubi_devices[i];
+
+ if (ubi && MAJOR(ubi->cdev.dev) == major) {
+ ubi_num = ubi->ubi_num;
+ break;
+ }
+ }
+ spin_unlock(&ubi_devices_lock);
+
+ return ubi_num;
+}
+
/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
static ssize_t dev_attribute_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- const struct ubi_device *ubi;
+ ssize_t ret;
+ struct ubi_device *ubi;
+ /*
+ * The below code looks weird, but it actually makes sense. We get the
+ * UBI device reference from the contained 'struct ubi_device'. But it
+ * is unclear if the device was removed or not yet. Indeed, if the
+ * device was removed before we increased its reference count,
+ * 'ubi_get_device()' will return -ENODEV and we fail.
+ *
+ * Remember, 'struct ubi_device' is freed in the release function, so
+ * we still can use 'ubi->ubi_num'.
+ */
ubi = container_of(dev, struct ubi_device, dev);
+ ubi = ubi_get_device(ubi->ubi_num);
+ if (!ubi)
+ return -ENODEV;
+
if (attr == &dev_eraseblock_size)
- return sprintf(buf, "%d\n", ubi->leb_size);
+ ret = sprintf(buf, "%d\n", ubi->leb_size);
else if (attr == &dev_avail_eraseblocks)
- return sprintf(buf, "%d\n", ubi->avail_pebs);
+ ret = sprintf(buf, "%d\n", ubi->avail_pebs);
else if (attr == &dev_total_eraseblocks)
- return sprintf(buf, "%d\n", ubi->good_peb_count);
+ ret = sprintf(buf, "%d\n", ubi->good_peb_count);
else if (attr == &dev_volumes_count)
- return sprintf(buf, "%d\n", ubi->vol_count);
+ ret = sprintf(buf, "%d\n", ubi->vol_count);
else if (attr == &dev_max_ec)
- return sprintf(buf, "%d\n", ubi->max_ec);
+ ret = sprintf(buf, "%d\n", ubi->max_ec);
else if (attr == &dev_reserved_for_bad)
- return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
+ ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
else if (attr == &dev_bad_peb_count)
- return sprintf(buf, "%d\n", ubi->bad_peb_count);
+ ret = sprintf(buf, "%d\n", ubi->bad_peb_count);
else if (attr == &dev_max_vol_count)
- return sprintf(buf, "%d\n", ubi->vtbl_slots);
+ ret = sprintf(buf, "%d\n", ubi->vtbl_slots);
else if (attr == &dev_min_io_size)
- return sprintf(buf, "%d\n", ubi->min_io_size);
+ ret = sprintf(buf, "%d\n", ubi->min_io_size);
else if (attr == &dev_bgt_enabled)
- return sprintf(buf, "%d\n", ubi->thread_enabled);
+ ret = sprintf(buf, "%d\n", ubi->thread_enabled);
else
BUG();
- return 0;
+ ubi_put_device(ubi);
+ return ret;
}
/* Fake "release" method for UBI devices */
@@ -671,6 +782,7 @@ static void detach_mtd_dev(struct ubi_device *ubi)
int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index;
dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
+ ubi_assert(ubi->ref_count == 0);
uif_close(ubi);
ubi_eba_close(ubi);
ubi_wl_close(ubi);
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index bc900d2..01978b5 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -56,23 +56,6 @@
#endif
/**
- * major_to_device - get UBI device object by character device major number.
- * @major: major number
- *
- * This function returns a pointer to the UBI device object.
- */
-static struct ubi_device *major_to_device(int major)
-{
- int i;
-
- for (i = 0; i < UBI_MAX_DEVICES; i++)
- if (ubi_devices[i] && MAJOR(ubi_devices[i]->cdev.dev) == major)
- return ubi_devices[i];
- BUG();
- return NULL;
-}
-
-/**
* get_exclusive - get exclusive access to an UBI volume.
* @desc: volume descriptor
*
@@ -129,9 +112,11 @@ static void revoke_exclusive(struct ubi_volume_desc *desc, int mode)
static int vol_cdev_open(struct inode *inode, struct file *file)
{
struct ubi_volume_desc *desc;
- const struct ubi_device *ubi = major_to_device(imajor(inode));
- int vol_id = iminor(inode) - 1;
- int mode;
+ int vol_id = iminor(inode) - 1, mode, ubi_num;
+
+ ubi_num = ubi_major2num(imajor(inode));
+ if (ubi_num < 0)
+ return ubi_num;
if (file->f_mode & FMODE_WRITE)
mode = UBI_READWRITE;
@@ -140,7 +125,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file)
dbg_msg("open volume %d, mode %d", vol_id, mode);
- desc = ubi_open_volume(ubi->ubi_num, vol_id, mode);
+ desc = ubi_open_volume(ubi_num, vol_id, mode);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -586,9 +571,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
if (!capable(CAP_SYS_RESOURCE))
return -EPERM;
- ubi = major_to_device(imajor(inode));
- if (IS_ERR(ubi))
- return PTR_ERR(ubi);
+ ubi = ubi_get_by_major(imajor(inode));
+ if (!ubi)
+ return -ENODEV;
switch (cmd) {
/* Create volume command */
@@ -695,6 +680,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
break;
}
+ ubi_put_device(ubi);
return err;
}
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index c94f475..85297cd 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -339,6 +339,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
{
int err, pnum, vol_id = vol->vol_id;
+ ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode)
@@ -389,6 +390,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
struct ubi_vid_hdr *vid_hdr;
uint32_t uninitialized_var(crc);
+ ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
err = leb_read_lock(ubi, vol_id, lnum);
@@ -614,6 +616,7 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int err, pnum, tries = 0, vol_id = vol->vol_id;
struct ubi_vid_hdr *vid_hdr;
+ ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode)
@@ -749,6 +752,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
struct ubi_vid_hdr *vid_hdr;
uint32_t crc;
+ ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode)
@@ -865,6 +869,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
struct ubi_vid_hdr *vid_hdr;
uint32_t crc;
+ ubi_assert(ubi->ref_count > 0);
ubi_assert(vol->ref_count > 0);
if (ubi->ro_mode)
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index 780c273..4ec3a33 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -30,23 +30,27 @@
* @ubi_num: UBI device number
* @di: the information is stored here
*
- * This function returns %0 in case of success and a %-ENODEV if there is no
- * such UBI device.
+ * This function returns %0 in case of success, %-EINVAL if the UBI device
+ * number is invalid, and %-ENODEV if there is no such UBI device.
*/
int ubi_get_device_info(int ubi_num, struct ubi_device_info *di)
{
- const struct ubi_device *ubi;
+ struct ubi_device *ubi;
- if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES ||
- !ubi_devices[ubi_num])
+ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
+ return -EINVAL;
+
+ ubi = ubi_get_device(ubi_num);
+ if (!ubi)
return -ENODEV;
- ubi = ubi_devices[ubi_num];
di->ubi_num = ubi->ubi_num;
di->leb_size = ubi->leb_size;
di->min_io_size = ubi->min_io_size;
di->ro_mode = ubi->ro_mode;
di->cdev = ubi->cdev.dev;
+
+ ubi_put_device(ubi);
return 0;
}
EXPORT_SYMBOL_GPL(ubi_get_device_info);
@@ -111,16 +115,23 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
mode != UBI_EXCLUSIVE)
return ERR_PTR(-EINVAL);
- ubi = ubi_devices[ubi_num];
+ /*
+ * First of all, we have to get the UBI device to prevent its removal.
+ */
+ ubi = ubi_get_device(ubi_num);
if (!ubi)
return ERR_PTR(-ENODEV);
- if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
- return ERR_PTR(-EINVAL);
+ if (vol_id < 0 || vol_id >= ubi->vtbl_slots) {
+ err = -EINVAL;
+ goto out_put_ubi;
+ }
desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL);
- if (!desc)
- return ERR_PTR(-ENOMEM);
+ if (!desc) {
+ err = -ENOMEM;
+ goto out_put_ubi;
+ }
err = -ENODEV;
if (!try_module_get(THIS_MODULE))
@@ -188,6 +199,8 @@ out_unlock:
module_put(THIS_MODULE);
out_free:
kfree(desc);
+out_put_ubi:
+ ubi_put_device(ubi);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(ubi_open_volume);
@@ -205,6 +218,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
{
int i, vol_id = -1, len;
struct ubi_device *ubi;
+ struct ubi_volume_desc *ret;
dbg_msg("open volume %s, mode %d", name, mode);
@@ -218,7 +232,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
return ERR_PTR(-EINVAL);
- ubi = ubi_devices[ubi_num];
+ ubi = ubi_get_device(ubi_num);
if (!ubi)
return ERR_PTR(-ENODEV);
@@ -234,10 +248,17 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
}
spin_unlock(&ubi->volumes_lock);
- if (vol_id < 0)
- return ERR_PTR(-ENODEV);
+ if (vol_id >= 0)
+ ret = ubi_open_volume(ubi_num, vol_id, mode);
+ else
+ ret = ERR_PTR(-ENODEV);
- return ubi_open_volume(ubi_num, vol_id, mode);
+ /*
+ * We should put the UBI device even in case of success, because
+ * 'ubi_open_volume()' took a reference as well.
+ */
+ ubi_put_device(ubi);
+ return ret;
}
EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
@@ -248,10 +269,11 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
void ubi_close_volume(struct ubi_volume_desc *desc)
{
struct ubi_volume *vol = desc->vol;
+ struct ubi_device *ubi = vol->ubi;
dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode);
- spin_lock(&vol->ubi->volumes_lock);
+ spin_lock(&ubi->volumes_lock);
switch (desc->mode) {
case UBI_READONLY:
vol->readers -= 1;
@@ -263,10 +285,11 @@ void ubi_close_volume(struct ubi_volume_desc *desc)
vol->exclusive = 0;
}
vol->ref_count -= 1;
- spin_unlock(&vol->ubi->volumes_lock);
+ spin_unlock(&ubi->volumes_lock);
- put_device(&vol->dev);
kfree(desc);
+ put_device(&vol->dev);
+ ubi_put_device(ubi);
module_put(THIS_MODULE);
}
EXPORT_SYMBOL_GPL(ubi_close_volume);
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 21c0283..91fde0e 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -245,6 +245,7 @@ struct ubi_wl_entry;
* @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count,
* @vol->readers, @vol->writers, @vol->exclusive,
* @vol->ref_count, @vol->mapping and @vol->eba_tbl.
+ * @ref_count: count of references on the UBI device
*
* @rsvd_pebs: count of reserved physical eraseblocks
* @avail_pebs: count of available physical eraseblocks
@@ -325,6 +326,7 @@ struct ubi_device {
int vol_count;
struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT];
spinlock_t volumes_lock;
+ int ref_count;
int rsvd_pebs;
int avail_pebs;
@@ -401,7 +403,6 @@ extern struct kmem_cache *ubi_wl_entry_slab;
extern struct file_operations ubi_ctrl_cdev_operations;
extern struct file_operations ubi_cdev_operations;
extern struct file_operations ubi_vol_cdev_operations;
-extern struct ubi_device *ubi_devices[];
extern struct class *ubi_class;
/* vtbl.c */
@@ -479,6 +480,12 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
struct ubi_vid_hdr *vid_hdr);
+/* build.c */
+struct ubi_device *ubi_get_device(int ubi_num);
+void ubi_put_device(struct ubi_device *ubi);
+struct ubi_device *ubi_get_by_major(int major);
+int ubi_major2num(int major);
+
/*
* ubi_rb_for_each_entry - walk an RB-tree.
* @rb: a pointer to type 'struct rb_node' to to use as a loop counter
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 3ed63dc..42d3dd7 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -71,11 +71,16 @@ static ssize_t vol_attribute_show(struct device *dev,
{
int ret;
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
- struct ubi_device *ubi = vol->ubi;
+ struct ubi_device *ubi;
+
+ ubi = ubi_get_device(vol->ubi->ubi_num);
+ if (!ubi)
+ return -ENODEV;
spin_lock(&ubi->volumes_lock);
if (!ubi->volumes[vol->vol_id]) {
spin_unlock(&ubi->volumes_lock);
+ ubi_put_device(ubi);
return -ENODEV;
}
/* Take a reference to prevent volume removal */
@@ -108,10 +113,12 @@ static ssize_t vol_attribute_show(struct device *dev,
/* This must be a bug */
ret = -EINVAL;
+ /* We've done the operation, drop volume and UBI device references */
spin_lock(&ubi->volumes_lock);
vol->ref_count -= 1;
ubi_assert(vol->ref_count >= 0);
spin_unlock(&ubi->volumes_lock);
+ ubi_put_device(ubi);
return ret;
}
@@ -260,6 +267,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
}
ubi->avail_pebs -= vol->reserved_pebs;
ubi->rsvd_pebs += vol->reserved_pebs;
+ spin_unlock(&ubi->volumes_lock);
vol->vol_id = vol_id;
vol->alignment = req->alignment;
@@ -267,9 +275,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
vol->vol_type = req->vol_type;
vol->name_len = req->name_len;
memcpy(vol->name, req->name, vol->name_len + 1);
- vol->exclusive = 1;
vol->ubi = ubi;
- spin_unlock(&ubi->volumes_lock);
/*
* Finish all pending erases because there may be some LEBs belonging
@@ -350,8 +356,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
goto out_sysfs;
spin_lock(&ubi->volumes_lock);
- ubi->vol_count += 1;
- vol->exclusive = 0;
ubi->volumes[vol_id] = vol;
spin_unlock(&ubi->volumes_lock);
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 7d32f71..bfc64c8 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1303,6 +1303,7 @@ int ubi_wl_flush(struct ubi_device *ubi)
* Make sure all the works which have been done in parallel are
* finished.
*/
+ ubi_assert(ubi->ref_count > 0);
down_write(&ubi->work_sem);
up_write(&ubi->work_sem);
--
1.5.3.4
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 3/6] UBI: prepare attach and detach functions
2007-12-20 14:22 [PATCH v2 0/6] UBI: make UBI devices dynamic Artem Bityutskiy
2007-12-20 14:22 ` [PATCH v2 1/6] UBI: add UBI control device Artem Bityutskiy
2007-12-20 14:23 ` [PATCH v2 2/6] UBI: add UBI devices reference counting Artem Bityutskiy
@ 2007-12-20 14:23 ` Artem Bityutskiy
2007-12-20 14:23 ` [PATCH v2 4/6] UBI: UBI: remove data_offset Artem Bityutskiy
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Artem Bityutskiy @ 2007-12-20 14:23 UTC (permalink / raw)
To: linux-mtd; +Cc: Frank Haverkamp, Arnd Bergmann, Andreas Arnez
>From 9fd405794615345e2089a6cfe719988f8bf4ff9a Mon Sep 17 00:00:00 2001
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Date: Mon, 17 Dec 2007 20:33:20 +0200
Subject: [PATCH] UBI: prepare attach and detach functions
Prepare the attach and detach functions to by used outside of
module initialization:
* detach function checks reference count before detaching
* it kills the background thread as well
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
---
drivers/mtd/ubi/build.c | 235 ++++++++++++++++++++++++++++++++---------------
drivers/mtd/ubi/ubi.h | 5 +
drivers/mtd/ubi/wl.c | 16 +---
3 files changed, 169 insertions(+), 87 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index e60a646..8f6aef6 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -39,6 +39,7 @@
#include <linux/stringify.h>
#include <linux/stat.h>
#include <linux/log2.h>
+#include <linux/kthread.h>
#include "ubi.h"
/* Maximum length of the 'mtd=' parameter */
@@ -81,6 +82,9 @@ static struct device ubi_ctrl_dev;
/* All UBI devices in system */
static struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
+/* Serializes UBI devices creations and removals */
+DEFINE_MUTEX(ubi_devices_mutex);
+
/* Protects @ubi_devices and @ubi->ref_count */
static DEFINE_SPINLOCK(ubi_devices_lock);
@@ -190,7 +194,7 @@ struct ubi_device *ubi_get_by_major(int major)
* @major: major number
*
* This function searches UBI device number object by its major number. If UBI
- * device was not found, this function returns -ENODEV, othewise the UBI device
+ * device was not found, this function returns -ENODEV, otherwise the UBI device
* number is returned.
*/
int ubi_major2num(int major)
@@ -486,9 +490,9 @@ out_si:
* assumed:
* o EC header is always at offset zero - this cannot be changed;
* o VID header starts just after the EC header at the closest address
- * aligned to @io->@hdrs_min_io_size;
+ * aligned to @io->hdrs_min_io_size;
* o data starts just after the VID header at the closest address aligned to
- * @io->@min_io_size
+ * @io->min_io_size
*
* This function returns zero in case of success and a negative error code in
* case of failure.
@@ -509,6 +513,9 @@ static int io_init(struct ubi_device *ubi)
return -EINVAL;
}
+ if (ubi->vid_hdr_offset < 0 || ubi->leb_start < ubi->vid_hdr_offset)
+ return -EINVAL;
+
/*
* Note, in this implementation we support MTD devices with 0x7FFFFFFF
* physical eraseblocks maximum.
@@ -617,84 +624,62 @@ static int io_init(struct ubi_device *ubi)
}
/**
- * attach_mtd_dev - attach an MTD device.
- * @mtd_dev: MTD device name or number string
+ * ubi_attach_mtd_dev - attach an MTD device.
+ * @mtd_dev: MTD device description object
* @vid_hdr_offset: VID header offset
* @data_offset: data offset
*
* This function attaches an MTD device to UBI. It first treats @mtd_dev as the
* MTD device name, and tries to open it by this name. If it is unable to open,
* it tries to convert @mtd_dev to an integer and open the MTD device by its
- * number. Returns zero in case of success and a negative error code in case of
- * failure.
+ * number. Returns new UBI device's number in case of success and a negative
+ * error code in case of failure.
+ *
+ * Note, the invocations of this function has to be serialized by the
+ * @ubi_devices_mutex.
*/
-static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset,
- int data_offset)
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset,
+ int data_offset)
{
struct ubi_device *ubi;
- struct mtd_info *mtd;
int i, err;
- mtd = get_mtd_device_nm(mtd_dev);
- if (IS_ERR(mtd)) {
- int mtd_num;
- char *endp;
-
- if (PTR_ERR(mtd) != -ENODEV)
- return PTR_ERR(mtd);
-
- /*
- * Probably this is not MTD device name but MTD device number -
- * check this out.
- */
- mtd_num = simple_strtoul(mtd_dev, &endp, 0);
- if (*endp != '\0' || mtd_dev == endp) {
- ubi_err("incorrect MTD device: \"%s\"", mtd_dev);
- return -ENODEV;
- }
-
- mtd = get_mtd_device(NULL, mtd_num);
- if (IS_ERR(mtd))
- return PTR_ERR(mtd);
- }
-
- /* Check if we already have the same MTD device attached */
+ /*
+ * Check if we already have the same MTD device attached.
+ *
+ * Note, this function assumes that UBI devices creations and deletions
+ * are serialized, so it does not take the &ubi_devices_lock.
+ */
for (i = 0; i < UBI_MAX_DEVICES; i++)
ubi = ubi_devices[i];
- if (ubi && ubi->mtd->index == mtd->index) {
+ if (ubi && mtd->index == ubi->mtd->index) {
ubi_err("mtd%d is already attached to ubi%d",
mtd->index, i);
- err = -EINVAL;
- goto out_mtd;
+ return -EINVAL;
}
- ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL);
- if (!ubi) {
- err = -ENOMEM;
- goto out_mtd;
- }
-
- ubi->mtd = mtd;
-
/* Search for an empty slot in the @ubi_devices array */
- ubi->ubi_num = -1;
for (i = 0; i < UBI_MAX_DEVICES; i++)
- if (!ubi_devices[i]) {
- ubi->ubi_num = i;
+ if (!ubi_devices[i])
break;
- }
- if (ubi->ubi_num == -1) {
+ if (i == UBI_MAX_DEVICES) {
ubi_err("only %d UBI devices may be created", UBI_MAX_DEVICES);
- err = -ENFILE;
- goto out_free;
+ return -ENFILE;
}
- dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d",
- ubi->mtd->index, ubi->ubi_num, vid_hdr_offset, data_offset);
+ ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL);
+ if (!ubi)
+ return -ENOMEM;
+ ubi->mtd = mtd;
+ ubi->ubi_num = i;
ubi->vid_hdr_offset = vid_hdr_offset;
ubi->leb_start = data_offset;
+
+ dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d",
+ mtd->index, ubi->ubi_num, vid_hdr_offset, data_offset);
+
err = io_init(ubi);
if (err)
goto out_free;
@@ -725,8 +710,16 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset,
if (err)
goto out_detach;
- ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi->ubi_num);
- ubi_msg("MTD device name: \"%s\"", ubi->mtd->name);
+ ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
+ if (IS_ERR(ubi->bgt_thread)) {
+ err = PTR_ERR(ubi->bgt_thread);
+ ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
+ err);
+ goto out_uif;
+ }
+
+ ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi->ubi_num);
+ ubi_msg("MTD device name: \"%s\"", mtd->name);
ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
ubi->peb_size, ubi->peb_size >> 10);
@@ -755,8 +748,10 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset,
}
ubi_devices[ubi->ubi_num] = ubi;
- return 0;
+ return ubi->ubi_num;
+out_uif:
+ uif_close(ubi);
out_detach:
ubi_eba_close(ubi);
ubi_wl_close(ubi);
@@ -768,21 +763,57 @@ out_free:
vfree(ubi->dbg_peb_buf);
#endif
kfree(ubi);
-out_mtd:
- put_mtd_device(mtd);
return err;
}
/**
- * detach_mtd_dev - detach an MTD device.
- * @ubi: UBI device description object
+ * ubi_detach_mtd_dev - detach an MTD device.
+ * @ubi_num: UBI device number to detach from
+ * @anyway: detach MTD even if device reference count is not zero
+ *
+ * This function destroys an UBI device number @ubi_num and detaches the
+ * underlying MTD device. Returns zero in case of success and %-EBUSY if the
+ * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not
+ * exist.
+ *
+ * Note, the invocations of this function has to be serialized by the
+ * @ubi_devices_mutex.
*/
-static void detach_mtd_dev(struct ubi_device *ubi)
+int ubi_detach_mtd_dev(int ubi_num, int anyway)
{
- int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index;
+ struct ubi_device *ubi;
+
+ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
+ return -EINVAL;
+
+ spin_lock(&ubi_devices_lock);
+ ubi = ubi_devices[ubi_num];
+ if (!ubi) {
+ spin_lock(&ubi_devices_lock);
+ return -EINVAL;
+ }
+
+ if (ubi->ref_count) {
+ if (!anyway) {
+ spin_lock(&ubi_devices_lock);
+ return -EBUSY;
+ }
+ /* This may only happen if there is a bug */
+ ubi_err("%s reference count %d, destroy anyway",
+ ubi->ubi_name, ubi->ref_count);
+ }
+ ubi_devices[ubi->ubi_num] = NULL;
+ spin_unlock(&ubi_devices_lock);
+
+ dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi->ubi_num);
+
+ /*
+ * Before freeing anything, we have to stop the background thread to
+ * prevent it from doing anything on this device while we are freeing.
+ */
+ if (ubi->bgt_thread)
+ kthread_stop(ubi->bgt_thread);
- dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
- ubi_assert(ubi->ref_count == 0);
uif_close(ubi);
ubi_eba_close(ubi);
ubi_wl_close(ubi);
@@ -793,9 +824,9 @@ static void detach_mtd_dev(struct ubi_device *ubi)
#ifdef CONFIG_MTD_UBI_DEBUG
vfree(ubi->dbg_peb_buf);
#endif
- kfree(ubi_devices[ubi_num]);
- ubi_devices[ubi_num] = NULL;
- ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num);
+ ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
+ kfree(ubi);
+ return 0;
}
/**
@@ -812,6 +843,46 @@ static void ltree_entry_ctor(struct kmem_cache *cache, void *obj)
init_rwsem(&le->mutex);
}
+/**
+ * find_mtd_device - open an MTD device by its name or number.
+ * @mtd_dev: name or number of the device
+ *
+ * This function tries to open and MTD device with name @mtd_dev, and if it
+ * fails, then it tries to interpret the @mtd_dev string as an ASCII-coded
+ * integer and open an MTD device with this number. Returns MTD device
+ * description object in case of success and a negative error code in case of
+ * failure.
+ */
+static struct mtd_info * __init open_mtd_device(const char *mtd_dev)
+{
+ struct mtd_info *mtd;
+
+ mtd = get_mtd_device_nm(mtd_dev);
+ if (IS_ERR(mtd)) {
+ int mtd_num;
+ char *endp;
+
+ if (PTR_ERR(mtd) != -ENODEV)
+ return mtd;
+
+ /*
+ * Probably this is not MTD device name but MTD device number -
+ * check this out.
+ */
+ mtd_num = simple_strtoul(mtd_dev, &endp, 0);
+ if (*endp != '\0' || mtd_dev == endp) {
+ ubi_err("incorrect MTD device: \"%s\"", mtd_dev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ mtd = get_mtd_device(NULL, mtd_num);
+ if (IS_ERR(mtd))
+ return mtd;
+ }
+
+ return mtd;
+}
+
static int __init ubi_init(void)
{
int err, i, k;
@@ -875,10 +946,21 @@ static int __init ubi_init(void)
/* Attach MTD devices */
for (i = 0; i < mtd_devs; i++) {
struct mtd_dev_param *p = &mtd_dev_param[i];
+ struct mtd_info *mtd;
cond_resched();
- err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs);
- if (err) {
+
+ mtd = open_mtd_device(p->name);
+ if (IS_ERR(mtd)) {
+ err = PTR_ERR(mtd);
+ goto out_detach;
+ }
+
+ mutex_lock(&ubi_devices_mutex);
+ err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs, p->data_offs);
+ mutex_unlock(&ubi_devices_mutex);
+ if (err < 0) {
+ put_mtd_device(mtd);
printk(KERN_ERR "UBI error: cannot attach %s\n",
p->name);
goto out_detach;
@@ -889,7 +971,11 @@ static int __init ubi_init(void)
out_detach:
for (k = 0; k < i; k++)
- detach_mtd_dev(ubi_devices[k]);
+ if (ubi_devices[k]) {
+ mutex_lock(&ubi_devices_mutex);
+ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
+ mutex_unlock(&ubi_devices_mutex);
+ }
kmem_cache_destroy(ubi_wl_entry_slab);
out_ltree:
kmem_cache_destroy(ubi_ltree_slab);
@@ -911,8 +997,11 @@ static void __exit ubi_exit(void)
int i;
for (i = 0; i < UBI_MAX_DEVICES; i++)
- if (ubi_devices[i])
- detach_mtd_dev(ubi_devices[i]);
+ if (ubi_devices[i]) {
+ mutex_lock(&ubi_devices_mutex);
+ ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1);
+ mutex_unlock(&ubi_devices_mutex);
+ }
kmem_cache_destroy(ubi_wl_entry_slab);
kmem_cache_destroy(ubi_ltree_slab);
device_unregister(&ubi_ctrl_dev);
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 91fde0e..25ff15a 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -404,6 +404,7 @@ extern struct file_operations ubi_ctrl_cdev_operations;
extern struct file_operations ubi_cdev_operations;
extern struct file_operations ubi_vol_cdev_operations;
extern struct class *ubi_class;
+extern struct mutex ubi_devices_mutex;
/* vtbl.c */
int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
@@ -462,6 +463,7 @@ int ubi_wl_flush(struct ubi_device *ubi);
int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum);
int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
void ubi_wl_close(struct ubi_device *ubi);
+int ubi_thread(void *u);
/* io.c */
int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
@@ -481,6 +483,9 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
struct ubi_vid_hdr *vid_hdr);
/* build.c */
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset,
+ int data_offset);
+int ubi_detach_mtd_dev(int ubi_num, int anyway);
struct ubi_device *ubi_get_device(int ubi_num);
void ubi_put_device(struct ubi_device *ubi);
struct ubi_device *ubi_get_by_major(int major);
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index bfc64c8..1142aab 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1356,7 +1356,7 @@ static void tree_destroy(struct rb_root *root)
* ubi_thread - UBI background thread.
* @u: the UBI device description object pointer
*/
-static int ubi_thread(void *u)
+int ubi_thread(void *u)
{
int failures = 0;
struct ubi_device *ubi = u;
@@ -1454,18 +1454,10 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
- ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
- if (IS_ERR(ubi->bgt_thread)) {
- err = PTR_ERR(ubi->bgt_thread);
- ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
- err);
- return err;
- }
-
err = -ENOMEM;
ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL);
if (!ubi->lookuptbl)
- goto out_free;
+ return err;
list_for_each_entry_safe(seb, tmp, &si->erase, u.list) {
cond_resched();
@@ -1598,10 +1590,6 @@ static void protection_trees_destroy(struct ubi_device *ubi)
*/
void ubi_wl_close(struct ubi_device *ubi)
{
- dbg_wl("disable \"%s\"", ubi->bgt_name);
- if (ubi->bgt_thread)
- kthread_stop(ubi->bgt_thread);
-
dbg_wl("close the UBI wear-leveling unit");
cancel_pending(ubi);
--
1.5.3.4
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 4/6] UBI: UBI: remove data_offset
2007-12-20 14:22 [PATCH v2 0/6] UBI: make UBI devices dynamic Artem Bityutskiy
` (2 preceding siblings ...)
2007-12-20 14:23 ` [PATCH v2 3/6] UBI: prepare attach and detach functions Artem Bityutskiy
@ 2007-12-20 14:23 ` Artem Bityutskiy
2007-12-20 14:23 ` [PATCH v2 5/6] UBI: introduce attach ioctls Artem Bityutskiy
2007-12-20 14:23 ` [PATCH v2 5/6] UBI: handle attach ioctl Artem Bityutskiy
5 siblings, 0 replies; 7+ messages in thread
From: Artem Bityutskiy @ 2007-12-20 14:23 UTC (permalink / raw)
To: linux-mtd; +Cc: Frank Haverkamp, Arnd Bergmann, Andreas Arnez
>From 09610a4ca122838a1292ec919ac2d05cf499a140 Mon Sep 17 00:00:00 2001
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Date: Wed, 19 Dec 2007 21:43:32 +0200
Subject: [PATCH] UBI: remove data_offset
'data_offset' parameter does not really make sense and it is not
needed. Get rid of it.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
---
drivers/mtd/ubi/build.c | 42 +++++++++++++++---------------------------
drivers/mtd/ubi/ubi.h | 3 +--
2 files changed, 16 insertions(+), 29 deletions(-)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 8f6aef6..5045588 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -49,13 +49,11 @@
* struct mtd_dev_param - MTD device parameter description data structure.
* @name: MTD device name or number string
* @vid_hdr_offs: VID header offset
- * @data_offs: data offset
*/
struct mtd_dev_param
{
char name[MTD_PARAM_LEN_MAX];
int vid_hdr_offs;
- int data_offs;
};
/* Numbers of elements set in the @mtd_dev_param array */
@@ -513,7 +511,7 @@ static int io_init(struct ubi_device *ubi)
return -EINVAL;
}
- if (ubi->vid_hdr_offset < 0 || ubi->leb_start < ubi->vid_hdr_offset)
+ if (ubi->vid_hdr_offset < 0)
return -EINVAL;
/*
@@ -563,10 +561,8 @@ static int io_init(struct ubi_device *ubi)
}
/* Similar for the data offset */
- if (ubi->leb_start == 0) {
- ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize;
- ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
- }
+ ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize;
+ ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset);
dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset);
@@ -627,7 +623,6 @@ static int io_init(struct ubi_device *ubi)
* ubi_attach_mtd_dev - attach an MTD device.
* @mtd_dev: MTD device description object
* @vid_hdr_offset: VID header offset
- * @data_offset: data offset
*
* This function attaches an MTD device to UBI. It first treats @mtd_dev as the
* MTD device name, and tries to open it by this name. If it is unable to open,
@@ -638,8 +633,7 @@ static int io_init(struct ubi_device *ubi)
* Note, the invocations of this function has to be serialized by the
* @ubi_devices_mutex.
*/
-int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset,
- int data_offset)
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset)
{
struct ubi_device *ubi;
int i, err;
@@ -675,10 +669,9 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset,
ubi->mtd = mtd;
ubi->ubi_num = i;
ubi->vid_hdr_offset = vid_hdr_offset;
- ubi->leb_start = data_offset;
- dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d",
- mtd->index, ubi->ubi_num, vid_hdr_offset, data_offset);
+ dbg_msg("attaching mtd%d to ubi%d: VID header offset %d",
+ mtd->index, ubi->ubi_num, vid_hdr_offset);
err = io_init(ubi);
if (err)
@@ -957,7 +950,7 @@ static int __init ubi_init(void)
}
mutex_lock(&ubi_devices_mutex);
- err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs, p->data_offs);
+ err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs);
mutex_unlock(&ubi_devices_mutex);
if (err < 0) {
put_mtd_device(mtd);
@@ -1111,13 +1104,9 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
if (tokens[1])
p->vid_hdr_offs = bytes_str_to_int(tokens[1]);
- if (tokens[2])
- p->data_offs = bytes_str_to_int(tokens[2]);
if (p->vid_hdr_offs < 0)
return p->vid_hdr_offs;
- if (p->data_offs < 0)
- return p->data_offs;
mtd_devs += 1;
return 0;
@@ -1125,16 +1114,15 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000);
MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: "
- "mtd=<name|num>[,<vid_hdr_offs>,<data_offs>]. "
+ "mtd=<name|num>[,<vid_hdr_offs>].\n"
"Multiple \"mtd\" parameters may be specified.\n"
- "MTD devices may be specified by their number or name. "
- "Optional \"vid_hdr_offs\" and \"data_offs\" parameters "
- "specify UBI VID header position and data starting "
- "position to be used by UBI.\n"
- "Example: mtd=content,1984,2048 mtd=4 - attach MTD device"
- "with name content using VID header offset 1984 and data "
- "start 2048, and MTD device number 4 using default "
- "offsets");
+ "MTD devices may be specified by their number or name.\n"
+ "Optional \"vid_hdr_offs\" parameter specifies UBI VID "
+ "header position and data starting position to be used "
+ "by UBI.\n"
+ "Example: mtd=content,1984 mtd=4 - attach MTD device"
+ "with name \"content\" using VID header offset 1984, and "
+ "MTD device number 4 with default VID header offset.");
MODULE_VERSION(__stringify(UBI_VERSION));
MODULE_DESCRIPTION("UBI - Unsorted Block Images");
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 25ff15a..4c3607e 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -483,8 +483,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
struct ubi_vid_hdr *vid_hdr);
/* build.c */
-int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset,
- int data_offset);
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset);
int ubi_detach_mtd_dev(int ubi_num, int anyway);
struct ubi_device *ubi_get_device(int ubi_num);
void ubi_put_device(struct ubi_device *ubi);
--
1.5.3.4
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 5/6] UBI: introduce attach ioctls
2007-12-20 14:22 [PATCH v2 0/6] UBI: make UBI devices dynamic Artem Bityutskiy
` (3 preceding siblings ...)
2007-12-20 14:23 ` [PATCH v2 4/6] UBI: UBI: remove data_offset Artem Bityutskiy
@ 2007-12-20 14:23 ` Artem Bityutskiy
2007-12-20 14:23 ` [PATCH v2 5/6] UBI: handle attach ioctl Artem Bityutskiy
5 siblings, 0 replies; 7+ messages in thread
From: Artem Bityutskiy @ 2007-12-20 14:23 UTC (permalink / raw)
To: linux-mtd; +Cc: Frank Haverkamp, Arnd Bergmann, Andreas Arnez
>From 9143611137b01d56137c3b21b2b360e7e38cee9a Mon Sep 17 00:00:00 2001
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Date: Tue, 18 Dec 2007 18:22:16 +0200
Subject: [PATCH] UBI: introduce attach ioctls
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
---
include/mtd/ubi-user.h | 69 ++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
index fe06ded..c3a1331 100644
--- a/include/mtd/ubi-user.h
+++ b/include/mtd/ubi-user.h
@@ -22,6 +22,21 @@
#define __UBI_USER_H__
/*
+ * UBI device creation (the same as MTD device attachment)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI
+ * control device. The caller has to properly fill and pass
+ * &struct ubi_attach_req object - UBI will attach the MTD device specified in
+ * the request and return the newly created UBI device number as the ioctl
+ * return value.
+ *
+ * UBI device deletion (the same as MTD device detachment)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI
+ * control device.
+ *
* UBI volume creation
* ~~~~~~~~~~~~~~~~~~~
*
@@ -80,6 +95,15 @@
/* Re-size an UBI volume */
#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req)
+/* IOCTL commands of the UBI control character device */
+
+#define UBI_CTRL_IOC_MAGIC 'o'
+
+/* Attach an MTD device */
+#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req)
+/* Detach an MTD device */
+#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t)
+
/* IOCTL commands of UBI volume character devices */
#define UBI_VOL_IOC_MAGIC 'O'
@@ -89,6 +113,9 @@
/* An eraseblock erasure command, used for debugging, disabled by default */
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
+/* Maximum MTD device name length supported by UBI */
+#define MAX_UBI_MTD_NAME_LEN 127
+
/*
* UBI volume type constants.
*
@@ -97,19 +124,51 @@
*/
enum {
UBI_DYNAMIC_VOLUME = 3,
- UBI_STATIC_VOLUME = 4
+ UBI_STATIC_VOLUME = 4,
+};
+
+/**
+ * struct ubi_attach_req - attach MTD device request.
+ * @vid_hdr_offset: VID header offset (use defaults if %0)
+ * @mtd_num: MTD device number to attach
+ * @padding: reserved for future, not used, has to be zeroed
+ *
+ * This data structure is used to specify MTD device UBI has to attach and the
+ * parameters it has to use. The "attach MTD device" ioctl returns the number
+ * of the newly created UBI device as the return value.
+ *
+ * Most applications should pass %0 in @vid_hdr_offset to make UBI use default
+ * offset of the VID header within physical eraseblocks. The default offset is
+ * the next min. I/O unit after the EC header. For example, it will be offset
+ * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or
+ * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages.
+ *
+ * But in rare cases, if this optimizes things, the VID header may be placed to
+ * a different offset. For example, the boot-loader might do things faster if the
+ * VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As
+ * the boot-loader would not normally need to read EC headers (unless it needs
+ * UBI in RW mode), it might be faster to calculate ECC. This is weird example,
+ * but it real-life example. So, in this example, @vid_hdr_offer would be
+ * 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes
+ * aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page
+ * of the first page and add needed padding.
+ */
+struct ubi_attach_req {
+ int32_t vid_hdr_offset;
+ int32_t mtd_num;
+ uint8_t padding[12];
};
/**
* struct ubi_mkvol_req - volume description data structure used in
- * volume creation requests.
+ * volume creation requests.
* @vol_id: volume number
* @alignment: volume alignment
* @bytes: volume size in bytes
* @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
- * @padding1: reserved for future, not used
+ * @padding1: reserved for future, not used, has to be zeroed
* @name_len: volume name length
- * @padding2: reserved for future, not used
+ * @padding2: reserved for future, not used, has to be zeroed
* @name: volume name
*
* This structure is used by userspace programs when creating new volumes. The
@@ -139,7 +198,7 @@ struct ubi_mkvol_req {
int8_t padding1;
int16_t name_len;
int8_t padding2[4];
- char name[UBI_MAX_VOLUME_NAME+1];
+ char name[UBI_MAX_VOLUME_NAME + 1];
} __attribute__ ((packed));
/**
--
1.5.3.4
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 5/6] UBI: handle attach ioctl
2007-12-20 14:22 [PATCH v2 0/6] UBI: make UBI devices dynamic Artem Bityutskiy
` (4 preceding siblings ...)
2007-12-20 14:23 ` [PATCH v2 5/6] UBI: introduce attach ioctls Artem Bityutskiy
@ 2007-12-20 14:23 ` Artem Bityutskiy
5 siblings, 0 replies; 7+ messages in thread
From: Artem Bityutskiy @ 2007-12-20 14:23 UTC (permalink / raw)
To: linux-mtd; +Cc: Frank Haverkamp, Arnd Bergmann, Andreas Arnez
>From f3a2178d8b632f712992d5a62f5d4b78440df527 Mon Sep 17 00:00:00 2001
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Date: Tue, 18 Dec 2007 18:23:39 +0200
Subject: [PATCH] UBI: handle attach ioctl
Actually implement the MTD device attach/detach handlers.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
---
drivers/mtd/ubi/cdev.c | 84 +++++++++++++++++++++++++++++++++++++++--------
1 files changed, 69 insertions(+), 15 deletions(-)
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 01978b5..493bfb9 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -44,17 +44,6 @@
#include <asm/div64.h>
#include "ubi.h"
-/*
- * Maximum sequence numbers of UBI and volume character device IOCTLs (direct
- * logical eraseblock erase is a debug-only feature).
- */
-#define UBI_CDEV_IOC_MAX_SEQ 2
-#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
-#define VOL_CDEV_IOC_MAX_SEQ 1
-#else
-#define VOL_CDEV_IOC_MAX_SEQ 2
-#endif
-
/**
* get_exclusive - get exclusive access to an UBI volume.
* @desc: volume descriptor
@@ -582,8 +571,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
struct ubi_mkvol_req req;
dbg_msg("create volume");
- err = copy_from_user(&req, argp,
- sizeof(struct ubi_mkvol_req));
+ err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req));
if (err) {
err = -EFAULT;
break;
@@ -647,8 +635,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
struct ubi_rsvol_req req;
dbg_msg("re-size volume");
- err = copy_from_user(&req, argp,
- sizeof(struct ubi_rsvol_req));
+ err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req));
if (err) {
err = -EFAULT;
break;
@@ -684,8 +671,75 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
return err;
}
+static int ctrl_cdev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ switch (cmd) {
+ /* Attach an MTD device command */
+ case UBI_IOCATT:
+ {
+ struct ubi_attach_req req;
+ struct mtd_info *mtd;
+
+ dbg_msg("attach MTD device");
+ err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req));
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (req.mtd_num < 0)
+ return -EINVAL;
+
+ mtd = get_mtd_device(NULL, req.mtd_num);
+ if (IS_ERR(mtd))
+ return PTR_ERR(mtd);
+
+ /* Note, request verification is done by the attach function */
+ mutex_lock(&ubi_devices_mutex);
+ err = ubi_attach_mtd_dev(mtd, req.vid_hdr_offset,
+ req.data_offset);
+ mutex_unlock(&ubi_devices_mutex);
+ if (err < 0)
+ put_mtd_device(mtd);
+ break;
+ }
+
+ /* Detach an MTD device command */
+ case UBI_IOCDET:
+ {
+ int ubi_num;
+
+ dbg_msg("dettach MTD device");
+ err = get_user(ubi_num, (__user int32_t *)argp);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+
+ mutex_lock(&ubi_devices_mutex);
+ err = ubi_detach_mtd_dev(ubi_num, 0);
+ mutex_unlock(&ubi_devices_mutex);
+ break;
+ }
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
/* UBI control character device operations */
struct file_operations ubi_ctrl_cdev_operations = {
+ .ioctl = ctrl_cdev_ioctl,
.owner = THIS_MODULE,
};
--
1.5.3.4
^ permalink raw reply related [flat|nested] 7+ messages in thread