#include <linux/stat.h>
#include <linux/miscdevice.h>
#include <linux/log2.h>
+#include <linux/kthread.h>
#include "ubi.h"
/* Maximum length of the 'mtd=' parameter */
/* 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);
* @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)
* 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.
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.
}
/**
- * 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;
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);
}
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);
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);
#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;
}
/**
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;
/* 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;
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);
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);
misc_deregister(&ubi_ctrl_cdev);