Skip to contentSkip to conversation

UBI: implement atomic LEB change

PATCH New Artem Bityutskiy 1 participant 1 message Feb 1, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 UBI: implement atomic LEB change

Quoted 1 line

From 1abfdcbb9426d9c47709bb70e3a4a8974535be2c Mon Sep 17 00:00:00 2001

From: Artem Bityutskiy <[email protected]>
Date: Thu, 1 Feb 2007 19:25:31 +0200
Subject: [PATCH] [MTD] UBI: implement atomic LEB change

This patch implements the atomic LEB change feature which allows to change the contents of a logical eraseblock atomically.

This patch makes UBI reserve one more PEB which is needed to implement this feature.

---
 drivers/mtd/ubi/eba.c   |  192 ++++++++++++++++++++++++++++++++++++++++++++++-
 drivers/mtd/ubi/eba.h   |   47 +++++++++--
 drivers/mtd/ubi/uif.c   |   62 +++++++++++++++
 include/linux/mtd/ubi.h |   54 +++++++++++++-
 4 files changed, 342 insertions(+), 13 deletions(-)
drivers/mtd/ubi/eba.c +190 -2
Index: ubi-2.6.git/drivers/mtd/ubi/eba.c
===================================================================
@@ -22,7 +22,7 @@
 #include <linux/sched.h>
 #include <linux/crc32.h>
 #include <linux/spinlock.h>
-#include <linux/rwsem.h>
+#include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/types.h>
 #include <mtd/ubi-header.h>
@@ -41,6 +41,12 @@
 #include "debug.h"
 
 /*
+ * The EBA unit reserves on PEB to implement the atomic eraseblock movement
+ * feature.
+ */
+#define EBA_RESERVED_PEBS 1
+
+/*
  * The highest bit in logical-to-physical eraseblock mappings is used to
  * indicate that the logical eraseblock is not mapped.
  */
@@ -164,6 +170,34 @@ static inline void leb_unmap(const struc
 }
 
 /**
+ * leb_remap - re-map a logical eraseblock to another physical eraseblock.
+ *
+ * @ubi: the UBI device description object
+ * @vol_id: the volume ID
+ * @lnum: the logical eraseblock number
+ * @new_pnum: new physical eraseblock
+ *
+ * This function re-maps a logical eraseblock from one physical eraseblock to
+ * another physical eraseblock.
+ * The logical eraseblock has to be locked.
+ */
+static inline void leb_remap(const struct ubi_info *ubi, int vol_id, int lnum,
+			     int new_pnum)
+{
+	int idx;
+	struct ubi_eba_info *eba = ubi->eba;
+
+	idx = vol_id2idx(ubi, vol_id);
+
+	spin_lock(&eba->eba_tbl_lock);
+	ubi_assert(eba->eba_tbl[idx].recs);
+	ubi_assert(eba->eba_tbl[idx].recs[lnum].pnum >= 0);
+	eba->eba_tbl[idx].recs[lnum].pnum = new_pnum;
+	eba->eba_tbl[idx].recs[lnum].leb_ver += 1;
+	spin_unlock(&eba->eba_tbl_lock);
+}
+
+/**
  * leb2peb - get physical eraseblock number the logical eraseblock is mapped
  * to.
  *
@@ -413,6 +447,7 @@ int ubi_eba_read_leb(const struct ubi_in
 		*read = len;
 		return 0;
 	}
+
 	dbg_eba("read %zd bytes from offset %d of LEB %d:%d, PEB %d",
 		len, offset, vol_id, lnum, pnum);
 
@@ -576,8 +611,10 @@ retry:
 	pnum = ubi_wl_get_peb(ubi, dtype);
 	if (unlikely(pnum < 0)) {
 		err = pnum;
+		dbg_err("cannot get free PEB - error %d", err);
 		goto out_vid_hdr;
 	}
+
 	dbg_eba("write VID hdr and %zd bytes at offset %d of LEB %d:%d, PEB %d",
 		len, offset, vol_id, lnum, pnum);
 
@@ -725,8 +762,10 @@ retry:
 	pnum = ubi_wl_get_peb(ubi, dtype);
 	if (unlikely(pnum < 0)) {
 		err = pnum;
+		dbg_err("cannot get free PEB - error %d", err);
 		goto out_vid_hdr;
 	}
+
 	dbg_eba("write VID hdr and %zd bytes at of LEB %d:%d, PEB %d",
 		len, vol_id, lnum, pnum);
 
@@ -757,7 +796,6 @@ out_unlock:
 	/* Write failure */
 write_error:
 	ubi_free_vid_hdr(ubi, vid_hdr);
-	ubi_free_vid_hdr(ubi, vid_hdr);
 	if (err != -EIO || !io->bad_allowed)
 		goto no_bad_eraseblocks;
 
@@ -783,6 +821,151 @@ no_bad_eraseblocks:
 	return err;
 }
 
+int ubi_eba_atomic_leb_change(const struct ubi_info *ubi, int vol_id, int lnum,
+			      const void *buf, size_t len,
+			      enum ubi_data_type dtype)
+{
+	int err, err1, old_pnum, new_pnum, tries = 0;
+	uint32_t leb_ver;
+	struct ubi_vid_hdr *vid_hdr;
+	const struct ubi_vtbl_vtr *vtr;
+	struct ubi_eba_info *eba = ubi->eba;
+	const struct ubi_io_info *io = ubi->io;
+
+retry:
+	/* Input arguments sanity check */
+	ubi_assert(vol_id >= 0);
+	ubi_assert(vol_id < ubi->acc->max_volumes || ubi_is_ivol(vol_id));
+	ubi_assert(lnum >= 0);
+	ubi_assert(len >= 0);
+	ubi_assert(dtype == UBI_DATA_LONGTERM || dtype == UBI_DATA_SHORTTERM ||
+		   dtype == UBI_DATA_UNKNOWN);
+
+	vtr = ubi_vtbl_get_vtr(ubi, vol_id);
+	ubi_assert(!IS_ERR(vtr));
+	ubi_assert(len <= io->leb_size - vtr->data_pad);
+	ubi_assert(lnum < ubi->eba->eba_tbl[vol_id2idx(ubi, vol_id)].leb_count);
+	ubi_assert(len % io->min_io_size == 0);
+	ubi_assert(vtr->vol_type == UBI_DYNAMIC_VOLUME);
+
+	if (unlikely(ubi->io->ro_mode)) {
+		dbg_err("read-only mode");
+		return -EROFS;
+	}
+
+	mutex_lock(&eba->change_mutex);
+
+	new_pnum = ubi_wl_get_peb(ubi, dtype);
+	if (unlikely(new_pnum < 0)) {
+		err = new_pnum;
+		dbg_err("cannot get free PEB - error %d", err);
+		goto out_unlock_mutex;
+	}
+
+	err = ubi_eba_leb_write_lock(ubi, vol_id, lnum);
+	if (unlikely(err))
+		goto out_put_unlock_mutex;
+
+	old_pnum = leb2peb(ubi, vol_id, lnum);
+	leb_ver = leb_get_ver(ubi, vol_id, lnum);
+
+	dbg_eba("change LEB %d:%d with %zd bytes of data, old PEB %d, "
+		"new PEB is %d", vol_id, lnum, len, old_pnum, new_pnum);
+
+	/*
+	 * We are ready to write new data to the new physical eraseblock. Write
+	 * the VID header first, then data.
+	 */
+	vid_hdr = ubi_zalloc_vid_hdr(ubi);
+	if (unlikely(!vid_hdr)) {
+		err = -ENOMEM;
+		goto out_unlock_leb_put;
+	}
+
+	leb_ver += 1; /* we have to increase the version */
+	vid_hdr->leb_ver = cpu_to_ubi32(leb_ver);
+	vid_hdr->vol_type = UBI_VID_DYNAMIC;
+	vid_hdr->vol_id = cpu_to_ubi32(vol_id);
+	vid_hdr->lnum = cpu_to_ubi32(lnum);
+	vid_hdr->compat = ubi_vtbl_get_compat(ubi, vol_id);
+	vid_hdr->data_pad = cpu_to_ubi32(vtr->data_pad);
+
+	err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr);
+	if (unlikely(err))
+		goto write_error;
+
+	if (len != 0) {
+		size_t written;
+
+		err = ubi_io_write_data(ubi, buf, new_pnum, 0, len, &written);
+		if (unlikely(err))
+			goto write_error;
+	}
+
+	/*
+	 * The data is there, we need to re-map our LEB from @old_pnum to
+	 * @new_pnum.
+	 */
+	leb_remap(ubi, vol_id, lnum, new_pnum);
+
+	/* And drop the old physical eraseblock */
+	err = ubi_wl_put_peb(ubi, old_pnum, 0);
+	if (unlikely(err))
+		/*
+		 * No idea what is this. But the best we can do is to switch to
+		 * R/O mode.
+		 */
+		ubi_eba_ro_mode(ubi);
+
+
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	ubi_eba_leb_write_unlock(ubi, vol_id, lnum);
+	mutex_unlock(&eba->change_mutex);
+	return err;
+
+out_unlock_leb_put:
+	ubi_eba_leb_write_unlock(ubi, vol_id, lnum);
+out_put_unlock_mutex:
+	err1 = ubi_wl_put_peb(ubi, new_pnum, 0);
+	if (err1)
+		/*
+		 * Something really bad is going on, switch to R/O mode just in
+		 * case.
+		 */
+		ubi_eba_ro_mode(ubi);
+out_unlock_mutex:
+	mutex_unlock(&eba->change_mutex);
+	return err;
+
+write_error:
+	ubi_free_vid_hdr(ubi, vid_hdr);
+	if (err != -EIO || !io->bad_allowed)
+		goto no_bad_eraseblocks;
+
+	/*
+	 * We assume that if this physical eraseblock went bad - the erase code
+	 * will handle that.
+	 */
+	ubi_msg("try to recover form the error");
+	err = ubi_wl_put_peb(ubi, new_pnum, 1);
+	ubi_eba_leb_write_unlock(ubi, vol_id, lnum);
+	mutex_unlock(&eba->change_mutex);
+	if (err || ++tries > 5)
+		return err;
+	goto retry;
+
+	/*
+	 * This flash device does not admit of bad eraseblocks or something
+	 * nasty and unexpected happened. Switch to read-only mode just in
+	 * case.
+	 */
+no_bad_eraseblocks:
+	ubi_eba_ro_mode(ubi);
+	ubi_eba_leb_write_unlock(ubi, vol_id, lnum);
+	mutex_unlock(&eba->change_mutex);
+	return err;
+}
+
 int ubi_eba_leb_is_mapped(const struct ubi_info *ubi, int vol_id, int lnum)
 {
 	dbg_eba("check LEB %d:%d PEBs", vol_id, lnum);
@@ -942,6 +1125,7 @@ int ubi_eba_init_scan(struct ubi_info *u
 
 	spin_lock_init(&eba->eba_tbl_lock);
 	spin_lock_init(&eba->ltree_lock);
+	mutex_init(&eba->change_mutex);
 	eba->ltree = RB_ROOT;
 
 	eba->num_volumes = acc->max_volumes + acc->ivol_count;
@@ -952,6 +1136,10 @@ int ubi_eba_init_scan(struct ubi_info *u
 		goto out;
 	}
 
+	err = ubi_acc_reserve(ubi, EBA_RESERVED_PEBS);
+	if (err)
+		goto out;
+
 	err = build_eba_tbl(ubi, si);
 	if (err)
 		goto out;
drivers/mtd/ubi/eba.h +38 -9

101 lines not on this page · raw message

drivers/mtd/ubi/uif.c +62 -0

68 lines not on this page · raw message

include/linux/mtd/ubi.h +52 -2

72 lines not on this page · raw message