UBI: do not panic if volume check fails
[pandora-kernel.git] / drivers / mtd / ubi / vmt.c
index 2cd886a..1628e1f 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include <linux/err.h>
-#include <asm/div64.h>
+#include <linux/math64.h>
 #include "ubi.h"
 
 #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
@@ -198,14 +198,13 @@ static void volume_sysfs_close(struct ubi_volume *vol)
  * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume
  * and saves it in @req->vol_id. Returns zero in case of success and a negative
  * error code in case of failure. Note, the caller has to have the
- * @ubi->volumes_mutex locked.
+ * @ubi->device_mutex locked.
  */
 int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
 {
        int i, err, vol_id = req->vol_id, do_free = 1;
        struct ubi_volume *vol;
        struct ubi_vtbl_record vtbl_rec;
-       uint64_t bytes;
        dev_t dev;
 
        if (ubi->ro_mode)
@@ -233,8 +232,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
                req->vol_id = vol_id;
        }
 
-       dbg_gen("volume ID %d, %llu bytes, type %d, name %s",
-               vol_id, (unsigned long long)req->bytes,
+       dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s",
+               ubi->ubi_num, vol_id, (unsigned long long)req->bytes,
                (int)req->vol_type, req->name);
 
        /* Ensure that this volume does not exist */
@@ -253,12 +252,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
                        goto out_unlock;
                }
 
-        /* Calculate how many eraseblocks are requested */
+       /* Calculate how many eraseblocks are requested */
        vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
-       bytes = req->bytes;
-       if (do_div(bytes, vol->usable_leb_size))
-               vol->reserved_pebs = 1;
-       vol->reserved_pebs += bytes;
+       vol->reserved_pebs += div_u64(req->bytes + vol->usable_leb_size - 1,
+                                     vol->usable_leb_size);
 
        /* Reserve physical eraseblocks */
        if (vol->reserved_pebs > ubi->avail_pebs) {
@@ -301,10 +298,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
                vol->used_bytes =
                        (long long)vol->used_ebs * vol->usable_leb_size;
        } else {
-               bytes = vol->used_bytes;
-               vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size);
-               vol->used_ebs = bytes;
-               if (vol->last_eb_bytes)
+               vol->used_ebs = div_u64_rem(vol->used_bytes,
+                                           vol->usable_leb_size,
+                                           &vol->last_eb_bytes);
+               if (vol->last_eb_bytes != 0)
                        vol->used_ebs += 1;
                else
                        vol->last_eb_bytes = vol->usable_leb_size;
@@ -361,7 +358,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
        ubi->vol_count += 1;
        spin_unlock(&ubi->volumes_lock);
 
-       err = paranoid_check_volumes(ubi);
+       if (paranoid_check_volumes(ubi))
+               dbg_err("check failed while creating volume %d", vol_id);
        return err;
 
 out_sysfs:
@@ -402,19 +400,20 @@ out_unlock:
 /**
  * ubi_remove_volume - remove volume.
  * @desc: volume descriptor
+ * @no_vtbl: do not change volume table if not zero
  *
  * This function removes volume described by @desc. The volume has to be opened
  * in "exclusive" mode. Returns zero in case of success and a negative error
- * code in case of failure. The caller has to have the @ubi->volumes_mutex
+ * code in case of failure. The caller has to have the @ubi->device_mutex
  * locked.
  */
-int ubi_remove_volume(struct ubi_volume_desc *desc)
+int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
 {
        struct ubi_volume *vol = desc->vol;
        struct ubi_device *ubi = vol->ubi;
        int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
 
-       dbg_gen("remove UBI volume %d", vol_id);
+       dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id);
        ubi_assert(desc->mode == UBI_EXCLUSIVE);
        ubi_assert(vol == ubi->volumes[vol_id]);
 
@@ -437,9 +436,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
        if (err)
                goto out_err;
 
-       err = ubi_change_vtbl_record(ubi, vol_id, NULL);
-       if (err)
-               goto out_err;
+       if (!no_vtbl) {
+               err = ubi_change_vtbl_record(ubi, vol_id, NULL);
+               if (err)
+                       goto out_err;
+       }
 
        for (i = 0; i < vol->reserved_pebs; i++) {
                err = ubi_eba_unmap_leb(ubi, vol, i);
@@ -465,7 +466,9 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
        ubi->vol_count -= 1;
        spin_unlock(&ubi->volumes_lock);
 
-       err = paranoid_check_volumes(ubi);
+       if (!no_vtbl && paranoid_check_volumes(ubi))
+               dbg_err("check failed while removing volume %d", vol_id);
+
        return err;
 
 out_err:
@@ -484,7 +487,7 @@ out_unlock:
  *
  * This function re-sizes the volume and returns zero in case of success, and a
  * negative error code in case of failure. The caller has to have the
- * @ubi->volumes_mutex locked.
+ * @ubi->device_mutex locked.
  */
 int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
 {
@@ -497,8 +500,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
        if (ubi->ro_mode)
                return -EROFS;
 
-       dbg_gen("re-size volume %d to from %d to %d PEBs",
-               vol_id, vol->reserved_pebs, reserved_pebs);
+       dbg_gen("re-size device %d, volume %d to from %d to %d PEBs",
+               ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs);
 
        if (vol->vol_type == UBI_STATIC_VOLUME &&
            reserved_pebs < vol->used_ebs) {
@@ -586,7 +589,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
                        (long long)vol->used_ebs * vol->usable_leb_size;
        }
 
-       err = paranoid_check_volumes(ubi);
+       if (paranoid_check_volumes(ubi))
+               dbg_err("check failed while re-sizing volume %d", vol_id);
        return err;
 
 out_acc:
@@ -601,6 +605,44 @@ out_free:
        return err;
 }
 
+/**
+ * ubi_rename_volumes - re-name UBI volumes.
+ * @ubi: UBI device description object
+ * @rename_list: list of &struct ubi_rename_entry objects
+ *
+ * This function re-names or removes volumes specified in the re-name list.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
+{
+       int err;
+       struct ubi_rename_entry *re;
+
+       err = ubi_vtbl_rename_volumes(ubi, rename_list);
+       if (err)
+               return err;
+
+       list_for_each_entry(re, rename_list, list) {
+               if (re->remove) {
+                       err = ubi_remove_volume(re->desc, 1);
+                       if (err)
+                               break;
+               } else {
+                       struct ubi_volume *vol = re->desc->vol;
+
+                       spin_lock(&ubi->volumes_lock);
+                       vol->name_len = re->new_name_len;
+                       memcpy(vol->name, re->new_name, re->new_name_len + 1);
+                       spin_unlock(&ubi->volumes_lock);
+               }
+       }
+
+       if (!err && paranoid_check_volumes(ubi))
+               ;
+       return err;
+}
+
 /**
  * ubi_add_volume - add volume.
  * @ubi: UBI device description object
@@ -649,7 +691,8 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
                return err;
        }
 
-       err = paranoid_check_volumes(ubi);
+       if (paranoid_check_volumes(ubi))
+               dbg_err("check failed while adding volume %d", vol_id);
        return err;
 
 out_gluebi:
@@ -816,7 +859,7 @@ static int paranoid_check_volume(struct ubi_device *ubi, int vol_id)
 
        if (alignment != vol->alignment || data_pad != vol->data_pad ||
            upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
-           name_len!= vol->name_len || strncmp(name, vol->name, name_len)) {
+           name_len != vol->name_len || strncmp(name, vol->name, name_len)) {
                ubi_err("volume info is different");
                goto fail;
        }
@@ -826,10 +869,10 @@ static int paranoid_check_volume(struct ubi_device *ubi, int vol_id)
 
 fail:
        ubi_err("paranoid check failed for volume %d", vol_id);
-       if (vol) {
+       if (vol)
                ubi_dbg_dump_vol_info(vol);
-               ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
-       }
+       ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
+       dump_stack();
        spin_unlock(&ubi->volumes_lock);
        return -EINVAL;
 }