[MTD] UBI: Per volume update marker

Alexander Schmidt alexs at linux.vnet.ibm.com
Mon Jan 29 11:36:00 EST 2007


Hi Artem,

> May you please instead add something like
> 
> + * Jan 2006: Alexander Schmidt, implemented per-volume update.
> 
> which is what people usually do in these cases.

okay

> In the code, in comments you still call this flag update marker. I am
> not sure, but may be it makes sense to call this field 'upd_marker'
> instead? It looks more coherent to the comments, but on the other hand
> it does not look very self-documenting. Hmm... We need ask Andreas, he
> is a naming-expert :-)

Andreas agrees with you :-)

> AFAIU, in your implementation only one update at a time is possible, so
> I think it is better not to remove this attribute but export the ID of
> the volume which is currently being updated.

That's right, I re-added the sysfs entry.

> Please, do not utilize vmt unit from upd unit at all. Utilize vtbl unit
> directly.

I removed the function from vmt unit and moved the code for checking if the
update marker already has the given value to the vtbl unit.

Signed-off-by: Alexander Schmidt, <alexs at linux.vnet.ibm.com>
---
 drivers/mtd/ubi/sysfs.c   |   26 ++++++
 drivers/mtd/ubi/upd.c     |  179 ++++------------------------------------------
 drivers/mtd/ubi/upd.h     |   22 ++---
 drivers/mtd/ubi/volmgmt.c |    6 +
 drivers/mtd/ubi/vtbl.c    |   71 ++++++++++++++----
 drivers/mtd/ubi/vtbl.h    |   29 +++++--
 include/mtd/ubi-header.h  |   22 +++--
 7 files changed, 153 insertions(+), 202 deletions(-)

--- ubi-2.6.orig/drivers/mtd/ubi/vtbl.h
+++ ubi-2.6/drivers/mtd/ubi/vtbl.h
@@ -17,6 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  * Author: Artem B. Bityutskiy
+ * Jan 2007: Alexander Schmidt, implemented per-volume update
  */
 
 /*
@@ -59,11 +60,12 @@
  * eraseblocks went bad. So we cannot alarm the user about this sever
  * corruption.
  *
- * Also, in this UBI implementation we make use of so called update marker when
- * updating volumes. The update marker is global. This may cause quite
- * unpleasant UBI usability problems. What we could do is to implement
- * many-updates-at-a-time support by adding a per-volume "corrupted" flag to the
- * volume table. This flag would be set before update and cleared after update.
+ * The volume table record also contains a so called update marker, which
+ * indicates whether a volume is under update or not. The update marker is
+ * set and stored on flash on the beginning of an update and deleted afterwards.
+ * This makes UBI recognize aborted updates, which may happen because of
+ * power-offs during updates. Read operations on volumes with pending update
+ * markers get rejected.
  */
 
 #ifndef __UBI_VTBL_H__
@@ -133,6 +135,19 @@ int ubi_vtbl_set_data_len(const struct u
 			  long long bytes);
 
 /**
+ * ubi_vtbl_updvol - set update marker of the volume.
+ *
+ * @ubi: the UBI device description object
+ * @vol_id: ID of the volume to set the update marker
+ * @upd_marker: new value for the upd_marker
+ *
+ * This function sets the update marker for a volume. The previously existing
+ * value is simply overwritten. This function returns zero in case of success
+ * and a negative error code in case of failure.
+ */
+int ubi_vtbl_updvol(const struct ubi_info *ubi, int vol_id, int upd_marker);
+
+/**
  * ubi_vtbl_set_corrupted - mark a volume as 'corrupted'.
  *
  * @ubi: the UBI device description object
@@ -193,7 +208,7 @@ static inline int ubi_is_ivol(int vol_id
  */
 static inline int ubi_ivol_is_known(int vol_id)
 {
-	return vol_id == UBI_LAYOUT_VOL_ID || vol_id == UBI_UPDATE_VOL_ID;
+	return vol_id == UBI_LAYOUT_VOL_ID;
 }
 
 /**
@@ -230,6 +245,7 @@ void ubi_vtbl_close(const struct ubi_inf
  * @last_eb_bytes: how many bytes are stored in the last logical eraseblock
  * @used_bytes: how many bytes of data this volume contains
  * @corrupted: non-zero if the data is corrupted
+ * @upd_marker: non-zero if volume is under update
  *
  * Note, the @usable_leb_size field is not stored on flash, as it is easily
  * calculated with help of the @data_pad field. But it is just very handy, so
@@ -250,6 +266,7 @@ struct ubi_vtbl_vtr {
 	int last_eb_bytes;
 	long long used_bytes;
 	int corrupted;
+	int upd_marker;
 };
 
 /**
--- ubi-2.6.orig/include/mtd/ubi-header.h
+++ ubi-2.6/include/mtd/ubi-header.h
@@ -64,6 +64,17 @@ enum {
 };
 
 /*
+ * Volume updating constants used in volume table records.
+ *
+ * @UBI_VOL_NOUPD: volume is not being updated
+ * @UBI_VOL_UPD: volume is being updated
+ */
+enum {
+	UBI_VOL_NOUPD = 0,
+	UBI_VOL_UPD = 1
+};
+
+/*
  * Compatibility constants used by internal volumes.
  *
  * @UBI_COMPAT_DELETE: delete this internal volume before anything is written
@@ -275,7 +286,7 @@ struct ubi_vid_hdr_upd_vol {
 } __attribute__ ((packed));
 
 /* Count of internal UBI volumes */
-#define UBI_INT_VOL_COUNT 2
+#define UBI_INT_VOL_COUNT 1
 
 /*
  * Internal volume IDs start from this digit. There is a reserved room for 4096
@@ -287,24 +298,19 @@ struct ubi_vid_hdr_upd_vol {
  * enum ubi_internal_volume_numbers - volume IDs of internal UBI volumes.
  *
  * %UBI_LAYOUT_VOL_ID: volume ID of the layout volume
- * %UBI_UPDATE_VOL_ID: volume ID of the update volume
  */
 enum {
 	UBI_LAYOUT_VOL_ID = UBI_INTERNAL_VOL_START,
-	UBI_UPDATE_VOL_ID = UBI_INTERNAL_VOL_START + 1
 };
 
 /* Number of logical eraseblocks reserved for internal volumes */
 #define UBI_LAYOUT_VOLUME_EBS 2
-#define UBI_UPDATE_VOLUME_EBS 1
 
 /* Names of internal volumes */
 #define UBI_LAYOUT_VOLUME_NAME "The layout volume"
-#define UBI_UPDATE_VOLUME_NAME "The update volume"
 
 /* Compatibility flags of internal volumes */
 #define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
-#define UBI_UPDATE_VOLUME_COMPAT UBI_COMPAT_REJECT
 
 /* The maximum number of volumes per one UBI device */
 #define UBI_MAX_VOLUMES 128
@@ -326,7 +332,7 @@ enum {
  * @data_pad: how many bytes are not used at the end of the eraseblocks to
  * satisfy the requested alignment
  * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
- * @padding1: reserved, zeroes
+ * @upd_marker: the volume update marker
  * @name_len: the volume name length
  * @name: the volume name
  * @padding2: reserved, zeroes
@@ -355,7 +361,7 @@ struct ubi_vol_tbl_record {
 	ubi32_t alignment;
 	ubi32_t data_pad;
 	uint8_t vol_type;
-	uint8_t padding1;
+	uint8_t upd_marker;
 	ubi16_t name_len;
 	uint8_t name[UBI_VOL_NAME_MAX+1];
 	uint8_t padding2[24];
--- ubi-2.6.orig/drivers/mtd/ubi/sysfs.c
+++ ubi-2.6/drivers/mtd/ubi/sysfs.c
@@ -205,6 +205,7 @@ static ssize_t vol_corrupted_show(struct
 static ssize_t vol_alignment_show(struct class_device *dev, char *buf);
 static ssize_t vol_usable_eb_size_show(struct class_device *dev, char *buf);
 static ssize_t vol_data_bytes_show(struct class_device *dev, char *buf);
+static ssize_t vol_updating_show(struct class_device *dev, char *buf);
 
 /*
  * Class device attributes corresponding to files in
@@ -224,6 +225,8 @@ static struct class_device_attribute vol
 	__ATTR(usable_eb_size, S_IRUGO, vol_usable_eb_size_show, NULL);
 static struct class_device_attribute vol_data_bytes =
 	__ATTR(data_bytes, S_IRUGO, vol_data_bytes_show, NULL);
+static struct class_device_attribute vol_updating =
+	__ATTR(updating, S_IRUGO, vol_updating_show, NULL);
 
 /*
  * Note, this function does not free allocated resources in case of failure -
@@ -264,11 +267,15 @@ int ubi_sysfs_vol_init(const struct ubi_
 	err = class_device_create_file(&vol->dev, &vol_data_bytes);
 	if (err)
 		return err;
+	err = class_device_create_file(&vol->dev, &vol_updating);
+	if (err)
+		return err;
 	return 0;
 }
 
 void ubi_sysfs_vol_close(struct ubi_uif_volume *vol)
 {
+	class_device_remove_file(&vol->dev, &vol_updating);
 	class_device_remove_file(&vol->dev, &vol_data_bytes);
 	class_device_remove_file(&vol->dev, &vol_usable_eb_size);
 	class_device_remove_file(&vol->dev, &vol_alignment);
@@ -549,3 +556,22 @@ static ssize_t vol_data_bytes_show(struc
 	spin_unlock(&vol->vol_lock);
 	return ret;
 }
+
+static ssize_t vol_updating_show(struct class_device *dev, char *buf)
+{
+	int ret;
+	const struct ubi_vtbl_vtr *vtr;
+	struct ubi_uif_volume *vol = dev2vol(dev);
+
+	spin_lock(&vol->vol_lock);
+	if (vol->removed) {
+		spin_unlock(&vol->vol_lock);
+		dbg_uif("volume %d was removed", vol->vol_id);
+		return -EIO;
+	}
+	vtr = ubi_vtbl_get_vtr(vol->ubi, vol->vol_id);
+	ret = sprintf(buf, "%d\n", vtr->upd_marker);
+	spin_unlock(&vol->vol_lock);
+	return ret;
+}
+
--- ubi-2.6.orig/drivers/mtd/ubi/upd.c
+++ ubi-2.6/drivers/mtd/ubi/upd.c
@@ -38,12 +38,11 @@
 #include "scan.h"
 #include "debug.h"
 
-static int put_marker(const struct ubi_info *ubi, int vol_id);
 static int finish_update(const struct ubi_info *ubi);
 
 int ubi_upd_start(const struct ubi_info *ubi, int vol_id, long long bytes)
 {
-	int err, rem, marker_present = 0;
+	int err, rem;
 	uint64_t tmp;
 	const struct ubi_vtbl_vtr *vtr;
 	struct ubi_upd_info *upd = ubi->upd;
@@ -57,35 +56,19 @@ int ubi_upd_start(const struct ubi_info 
 		   bytes <= vtr->usable_leb_size * vtr->reserved_pebs);
 
 	mutex_lock(&upd->mutex);
-	if (upd->vol_id != -1) {
-		/* Hmm, the update marker is busy */
-		err = -EBUSY;
-		if (upd->updating) {
-			dbg_upd("volume %d is being updated, update marker "
+	if (upd->updating && upd->vol_id != vol_id) {
+		dbg_upd("volume %d is being updated, update marker "
 				"busy", upd->vol_id);
-			goto out_unlock;
-		} else if (upd->vol_id != vol_id) {
-			dbg_upd("update marker is held by corrupted volume %d",
-				upd->vol_id);
-			goto out_unlock;
-		}
-
-		/*
-		 * The update marker on the flash media corresponds to our
-		 * volume. Proceed with the update operation.
-		 */
-		marker_present = 1;
+		goto out_unlock;
 	}
 
 	upd->updating = 1;
 	upd->vol_id = vol_id;
 	ubi_vtbl_set_corrupted(ubi, vol_id);
 
-	if (!marker_present) {
-		err = put_marker(ubi, vol_id);
-		if (err)
-			goto out_unlock;
-	}
+	err = ubi_vtbl_updvol(ubi, vol_id, UBI_VOL_UPD);
+	if (err)
+		goto out_unlock;
 
 	/* Before updating, we wipe out volume */
 	err = ubi_wipe_out_volume(ubi, vol_id);
@@ -276,11 +259,8 @@ int ubi_upd_abort(const struct ubi_info 
 	return 0;
 }
 
-static int remove_marker(const struct ubi_info *ubi);
-
 int ubi_upd_clean(const struct ubi_info *ubi, int vol_id)
 {
-	int err = 0;
 	struct ubi_upd_info *upd = ubi->upd;
 
 	mutex_lock(&upd->mutex);
@@ -290,10 +270,7 @@ int ubi_upd_clean(const struct ubi_info 
 
 	dbg_upd("clean update marker for volume %d", vol_id);
 
-	err = remove_marker(ubi);
-	if (err)
-		goto out_unlock;
-
+	upd->updating = 0;
 	upd->vol_id = -1;
 
 out_unlock:
@@ -304,11 +281,7 @@ out_unlock:
 int ubi_upd_init_scan(struct ubi_info *ubi, struct ubi_scan_info *si)
 {
 	int err;
-	struct ubi_scan_volume *sv;
-	struct ubi_vid_hdr *vid_hdr;
 	struct ubi_upd_info *upd;
-	const struct ubi_vtbl_vtr *vtr;
-	const struct ubi_scan_leb *seb;
 
 	dbg_upd("initialize the update unit");
 
@@ -320,92 +293,17 @@ int ubi_upd_init_scan(struct ubi_info *u
 	upd->upd_buf = ubi_kmalloc(ubi->io->leb_size);
 	if (!upd->upd_buf) {
 		err = -ENOMEM;
-		goto out_free_upd;
+		goto out;
 	}
 
 	mutex_init(&upd->mutex);
 	upd->vol_id = -1;
-
-	sv = ubi_scan_find_sv(si, UBI_UPDATE_VOL_ID);
-	if (!sv) {
-		dbg_upd("the update unit is initialized");
-		return 0;
-	}
-
-
-	/*
-	 * The update marker was found - a volume update operation was
-	 * interrupted. We have to mark the corresponding volume as corrupted.
-	 */
-
-	err = -EINVAL;
-	if (sv->leb_count > 1) {
-		/* There may be only one update marker */
-		dbg_err("too many update markers %d", sv->leb_count);
-		goto out_free_upd_buf;
-	}
-
-	seb = ubi_scan_find_seb(sv, 0);
-	if (!seb) {
-		dbg_err("bad update marker");
-		goto out_free_upd_buf;
-	}
-
-	vid_hdr = ubi_zalloc_vid_hdr(ubi);
-	if (!vid_hdr) {
-		err = -ENOMEM;
-		goto out_free_upd_buf;
-	}
-
-	err = ubi_io_read_vid_hdr(ubi, seb->pnum, vid_hdr, 1);
-	if (unlikely(err < 0))
-		goto out_vid_hdr;
-	else if (unlikely(err > 0) && err != UBI_IO_BITFLIPS) {
-		/*
-		 * Cannot read the update marker. But we read it earlier,
-		 * during scanning. No idea what happened. Don't erase this
-		 * physical eraseblock because some corrupted volume will then
-		 * be treated as good.
-		 */
-		err = -EIO;
-		goto out_vid_hdr;
-	}
-
-	memcpy(&upd->hdr_data, &vid_hdr->ivol_data[0],
-	       UBI_VID_HDR_IVOL_DATA_SIZE);
-	upd->vol_id = ubi32_to_cpu(upd->hdr_data.vol_id);
-	ubi_free_vid_hdr(ubi, vid_hdr);
-
-	/* Check sanity */
-	if (upd->vol_id < 0 || upd->vol_id >= ubi->acc->max_volumes) {
-		ubi_err("bad volume ID %d in update marker",
-			upd->vol_id);
-		goto out_free_upd_buf;
-	}
-
-	ubi_warn("volume %d update was interrupted", upd->vol_id);
-	vtr = ubi_vtbl_get_vtr(ubi, upd->vol_id);
-	if (IS_ERR(vtr)) {
-		/*
-		 * Update marker belongs to an non-existing volume. This may
-		 * happen if an unclean reboot happened during volume deletion.
-		 */
-
-		err = remove_marker(ubi);
-		if (err)
-			goto out_free_upd_buf;
-		upd->vol_id = -1;
-	} else
-		ubi_vtbl_set_corrupted(ubi, upd->vol_id);
+	upd->updating = 0;
 
 	dbg_upd("the update unit is initialized");
 	return 0;
 
-out_vid_hdr:
-	ubi_free_vid_hdr(ubi, vid_hdr);
-out_free_upd_buf:
-	ubi_kfree(upd->upd_buf);
-out_free_upd:
+out:
 	ubi_kfree(upd);
 	return err;
 }
@@ -418,52 +316,6 @@ void ubi_upd_close(const struct ubi_info
 }
 
 /**
- * put_marker - put update marker.
- *
- * @ubi: the UBI device description object
- * @vol_id: for which volume to put the update marker.
- *
- * This function returns zero in case of success, and a negative error code in
- * case of failure.
- */
-static int put_marker(const struct ubi_info *ubi, int vol_id)
-{
-	int err;
-	size_t written;
-	struct ubi_upd_info *upd = ubi->upd;
-
-	dbg_upd("put update marker for volume %d", vol_id);
-	upd->hdr_data.vol_id = cpu_to_ubi32(vol_id);
-	err = ubi_eba_write_leb(ubi, UBI_UPDATE_VOL_ID, 0,  NULL, 0, 0,
-				UBI_DATA_SHORTTERM, &written, &upd->hdr_data);
-
-	return err;
-}
-
-/**
- * remove_marker - remove update marker.
- *
- * @ubi: the UBI device description object
- *
- * This function returns zero in case of success, and a negative error code in
- * case of failure.
- */
-static int remove_marker(const struct ubi_info *ubi)
-{
-	int err;
-	struct ubi_upd_info *upd = ubi->upd;
-
-	dbg_upd("remove update marker for volume %d", upd->vol_id);
-
-	err = ubi_eba_erase_leb(ubi, UBI_UPDATE_VOL_ID, 0);
-	if (err)
-		return err;
-
-	err = ubi_wl_erase_flush(ubi);
-	return err;
-}
-
-/**
  * finish_update - finish volume update.
  *
  * @ubi: the UBI device description object
@@ -476,16 +328,23 @@ static int finish_update(const struct ub
 {
 	int err;
 	struct ubi_upd_info *upd = ubi->upd;
+	struct ubi_vtbl_info *vtbl = ubi->vtbl;
+	struct ubi_vtbl_vtr *vtr = &vtbl->vt[upd->vol_id];
 
 	dbg_upd("finish volume %d update", upd->vol_id);
 
 	upd->updating = 0;
 
-	err = remove_marker(ubi);
+	err = ubi_vtbl_updvol(ubi, upd->vol_id, UBI_VOL_NOUPD);
 	if (err)
 		return err;
 
 	ubi_vtbl_set_data_len(ubi, upd->vol_id, upd->upd_bytes);
+
+	mutex_lock(&vtbl->mutex);
+	vtr->corrupted = 0;
+	mutex_unlock(&vtbl->mutex);
+
 	upd->vol_id = -1;
 	return 0;
 }
--- ubi-2.6.orig/drivers/mtd/ubi/vtbl.c
+++ ubi-2.6/drivers/mtd/ubi/vtbl.c
@@ -160,6 +160,44 @@ int ubi_vtbl_set_data_len(const struct u
 	return 0;
 }
 
+int ubi_vtbl_updvol(const struct ubi_info *ubi, int vol_id, int upd_marker)
+{
+	int err;
+	struct ubi_vtbl_vtr tmp_vtr;
+	struct ubi_vtbl_vtr *vtr;
+	struct ubi_vtbl_info *vtbl = ubi->vtbl;
+
+	ubi_assert(vol_id >= 0 && vol_id < vtbl->vt_slots);
+	ubi_assert(upd_marker == UBI_VOL_NOUPD || upd_marker == UBI_VOL_UPD);
+	ubi_assert(!ubi_is_ivol(vol_id));
+
+	dbg_vtbl("set update marker for volume %d to %d", vol_id, upd_marker);
+
+	/* Ensure that this volume exists */
+	vtr = ubi_vtbl_get_vtr(ubi, vol_id);
+	if (IS_ERR(vtr)) {
+		return PTR_ERR(vtr);
+	}
+
+	/* If the marker already has the given value, there is nothing to do */
+	if (upd_marker == vtr->upd_marker)
+		return 0;
+
+	memcpy(&tmp_vtr, &vtbl->vt[vol_id], sizeof(struct ubi_vtbl_vtr));
+
+	tmp_vtr.name = strdup_len(vtbl->vt[vol_id].name,
+			vtbl->vt[vol_id].name_len);
+	if (!tmp_vtr.name)
+		return -ENOMEM;
+
+	tmp_vtr.upd_marker = upd_marker;
+
+	err = change_volume(ubi, vol_id, &tmp_vtr);
+
+	ubi_kfree(tmp_vtr.name);
+	return err;
+}
+
 int ubi_vtbl_set_corrupted(const struct ubi_info *ubi, int vol_id)
 {
 	struct ubi_vtbl_info *vtbl = ubi->vtbl;
@@ -212,8 +250,6 @@ int ubi_vtbl_get_compat(const struct ubi
 	switch (vol_id) {
 		case UBI_LAYOUT_VOL_ID:
 			return UBI_LAYOUT_VOLUME_COMPAT;
-		case UBI_UPDATE_VOL_ID:
-			return UBI_UPDATE_VOLUME_COMPAT;
 		default:
 			BUG();
 	}
@@ -414,6 +450,7 @@ static int change_volume(const struct ub
 			vol_tbl[i].vol_type = UBI_VID_DYNAMIC;
 		else
 			vol_tbl[i].vol_type = UBI_VID_STATIC;
+		vol_tbl[i].upd_marker = tmp_vtr->upd_marker;
 		vol_tbl[i].name_len = cpu_to_ubi16((uint16_t)tmp_vtr->name_len);
 
 		memcpy(&vol_tbl[i].name, tmp_vtr->name, tmp_vtr->name_len);
@@ -793,18 +830,6 @@ static void init_ivols(struct ubi_info *
 	vtr->used_ebs = vtr->reserved_pebs;
 	vtr->last_eb_bytes = vtr->reserved_pebs;
 	vtr->used_bytes = vtr->used_ebs * (io->leb_size - vtr->data_pad);
-
-	/* The update volume */
-	vtr = &vtbl->ivol_vtrs[1];
-	vtr->reserved_pebs = UBI_UPDATE_VOLUME_EBS;
-	vtr->alignment = 1;
-	vtr->vol_type = UBI_DYNAMIC_VOLUME;
-	vtr->name_len = sizeof(UBI_UPDATE_VOLUME_NAME) - 1;
-	vtr->name = UBI_UPDATE_VOLUME_NAME;
-	vtr->usable_leb_size = io->leb_size;
-	vtr->used_ebs = vtr->reserved_pebs;
-	vtr->last_eb_bytes = vtr->reserved_pebs;
-	vtr->used_bytes = vtr->used_ebs * (io->leb_size - vtr->data_pad);
 }
 
 /**
@@ -867,6 +892,7 @@ static int init_ram_vt(const struct ubi_
 		name_len = ubi16_to_cpu(vol_tbl[i].name_len);
 		vtr->name_len = name_len;
 		vtr->usable_leb_size = ubi->io->leb_size - vtr->data_pad;
+		vtr->upd_marker = vol_tbl[i].upd_marker;
 
 		vtr->name = ubi_kmalloc(name_len + 1);
 		if (unlikely(!vtr->name)) {
@@ -880,7 +906,12 @@ static int init_ram_vt(const struct ubi_
 
 		/* Initialize the data-related fields */
 
-		vtr->corrupted = 0;
+		if (vtr->upd_marker) {
+			ubi_warn("volume %d update was interrupted", i);
+			vtr->corrupted = 1;
+		}
+		else
+			vtr->corrupted = 0;
 
 		/*
 		 * In case of dynamic volume UBI knows nothing about how many
@@ -954,7 +985,8 @@ static void free_volume_info(const struc
 static int vol_tbl_check(const struct ubi_info *ubi,
 			 const struct ubi_vol_tbl_record *vol_tbl)
 {
-	int i, reserved_pebs, alignment, data_pad, vol_type, name_len;
+	int i, reserved_pebs, alignment, data_pad, vol_type, name_len,
+	    upd_marker;
 	const char *name;
 	const struct ubi_vtbl_info *vtbl = ubi->vtbl;
 	const struct ubi_io_info *io = ubi->io;
@@ -969,6 +1001,7 @@ static int vol_tbl_check(const struct ub
 		alignment = ubi32_to_cpu(vol_tbl[i].alignment);
 		data_pad = ubi32_to_cpu(vol_tbl[i].data_pad);
 		vol_type = vol_tbl[i].vol_type;
+		upd_marker = vol_tbl[i].upd_marker;
 		name_len = ubi16_to_cpu(vol_tbl[i].name_len);
 		name = &vol_tbl[i].name[0];
 
@@ -1029,6 +1062,12 @@ static int vol_tbl_check(const struct ub
 			goto bad;
 		}
 
+		if (unlikely(upd_marker != UBI_VOL_NOUPD &&
+			     upd_marker != UBI_VOL_UPD)) {
+			dbg_err("bad update marker");
+			goto bad;
+		}
+
 		if (unlikely(reserved_pebs > io->good_peb_count)) {
 			dbg_err("too large reserved_pebs");
 			goto bad;
--- ubi-2.6.orig/drivers/mtd/ubi/volmgmt.c
+++ ubi-2.6/drivers/mtd/ubi/volmgmt.c
@@ -366,6 +366,12 @@ static int paranoid_check_vtr(const stru
 		goto bad;
 	}
 
+	if (unlikely(vtr->updating != UBI_VOL_NOUPD &&
+		     vtr->updating != UBI_VOL_UPD)) {
+		ubi_err("bad update marker");
+		goto bad;
+	}
+
 	if (unlikely(vtr->name_len > UBI_VOL_NAME_MAX)) {
 		ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX);
 		goto bad;
--- ubi-2.6.orig/drivers/mtd/ubi/upd.h
+++ ubi-2.6/drivers/mtd/ubi/upd.h
@@ -17,6 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  * Author: Artem B. Bityutskiy
+ * Jan 2007: Alexander Schmidt, implemented per-volume update
  */
 
 /*
@@ -24,25 +25,22 @@
  *
  * This unit implements the volume update operation. In the current
  * implementation we use an update marker for this. The update marker is
- * per-UBI device, not per-volume, so only one volume update at a time is
- * possible. The update marker is written to flash before the update starts,
+ * stored in the volume table. It is written to flash before the update starts,
  * and removed from flash after the update has been finished. So, if the update
  * was interrupted by an unclean re-boot, the update marker will be hit on and
  * we'll know that the volume is corrupted.
  *
- * The update marker is implemented as follows. We maintain an internal volume,
- * called "the update volume", which has only one logical eraseblock.  And this
- * logical eraseblock is effectively the update marker. Thus, to put the update
- * marker means to write to the only eraseblock of the update volume, and to
- * remove the update marker is to erase that eraseblock.
+ * The update marker is implemented as follows. When starting an update
+ * procedure, the update marker is set in the volume table record containing
+ * the volume to be updated. Removing the update marker after a successfull
+ * update is done by removing the update marker from the respective volume
+ * table record.
  *
  * Note, in general it is possible to implement the update operation as a
  * transaction with a possibility to roll-back. But this is far more complex.
- *
- * Note, if a volume update was interrupted, the update marker stays on flash,
- * and no other updates are possible. This may cause different usability
- * problems so it would make sense to implement per-volume update marker or
- * just use the volume table for these reasons (introduce an "updating" flag).
+ * In addition, it is not possible to perform concurrent updates, but it is
+ * possible to update volumes when updates on other volumes were interrupted
+ * previously.
  */
 
 #ifndef __UBI_UPD_H__





More information about the linux-mtd mailing list