summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRichard Weinberger <richard@nod.at>2014-09-22 15:36:40 +0200
committerRichard Weinberger <richard@nod.at>2015-03-26 22:46:05 +0100
commitdaef3dd1f0ae71bf04a32af4ef643baf040bb73d (patch)
tree74ce5537d58c2489653b3a9643a63f8c0eec84d7 /drivers
parentee59ba8b064f692a1dee99b6f3ba30b0e904b2c1 (diff)
UBI: Fastmap: Add self check to detect absent PEBs
This self check allows Fastmap to detect absent PEBs while writing a new fastmap to the MTD device. It will help to find implementation issues in Fastmap. Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/ubi/fastmap.c86
1 files changed, 84 insertions, 2 deletions
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index db2625d3ecd9..13492fef6471 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012 Linutronix GmbH
+ * Copyright (c) 2014 sigma star gmbh
* Author: Richard Weinberger <richard@nod.at>
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +18,69 @@
#include "ubi.h"
/**
+ * init_seen - allocate memory for used for debugging.
+ * @ubi: UBI device description object
+ */
+static inline int *init_seen(struct ubi_device *ubi)
+{
+ int *ret;
+
+ if (!ubi_dbg_chk_fastmap(ubi))
+ return NULL;
+
+ ret = kcalloc(ubi->peb_count, sizeof(int), GFP_KERNEL);
+ if (!ret)
+ return ERR_PTR(-ENOMEM);
+
+ return ret;
+}
+
+/**
+ * free_seen - free the seen logic integer array.
+ * @seen: integer array of @ubi->peb_count size
+ */
+static inline void free_seen(int *seen)
+{
+ kfree(seen);
+}
+
+/**
+ * set_seen - mark a PEB as seen.
+ * @ubi: UBI device description object
+ * @pnum: The PEB to be makred as seen
+ * @seen: integer array of @ubi->peb_count size
+ */
+static inline void set_seen(struct ubi_device *ubi, int pnum, int *seen)
+{
+ if (!ubi_dbg_chk_fastmap(ubi) || !seen)
+ return;
+
+ seen[pnum] = 1;
+}
+
+/**
+ * self_check_seen - check whether all PEB have been seen by fastmap.
+ * @ubi: UBI device description object
+ * @seen: integer array of @ubi->peb_count size
+ */
+static int self_check_seen(struct ubi_device *ubi, int *seen)
+{
+ int pnum, ret = 0;
+
+ if (!ubi_dbg_chk_fastmap(ubi) || !seen)
+ return 0;
+
+ for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+ if (!seen[pnum] && ubi->lookuptbl[pnum]) {
+ ubi_err(ubi, "self-check failed for PEB %d, fastmap didn't see it", pnum);
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+/**
* ubi_calc_fm_size - calculates the fastmap size in bytes for an UBI device.
* @ubi: UBI device description object
*/
@@ -1030,6 +1094,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
struct ubi_work *ubi_wrk;
int ret, i, j, free_peb_count, used_peb_count, vol_count;
int scrub_peb_count, erase_peb_count;
+ int *seen_pebs = NULL;
fm_raw = ubi->fm_buf;
memset(ubi->fm_buf, 0, ubi->fm_size);
@@ -1046,6 +1111,12 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
goto out_kfree;
}
+ seen_pebs = init_seen(ubi);
+ if (IS_ERR(seen_pebs)) {
+ ret = PTR_ERR(seen_pebs);
+ goto out_kfree;
+ }
+
spin_lock(&ubi->volumes_lock);
spin_lock(&ubi->wl_lock);
@@ -1076,8 +1147,10 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fmpl1->size = cpu_to_be16(ubi->fm_pool.size);
fmpl1->max_size = cpu_to_be16(ubi->fm_pool.max_size);
- for (i = 0; i < ubi->fm_pool.size; i++)
+ for (i = 0; i < ubi->fm_pool.size; i++) {
fmpl1->pebs[i] = cpu_to_be32(ubi->fm_pool.pebs[i]);
+ set_seen(ubi, ubi->fm_pool.pebs[i], seen_pebs);
+ }
fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
fm_pos += sizeof(*fmpl2);
@@ -1085,14 +1158,17 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fmpl2->size = cpu_to_be16(ubi->fm_wl_pool.size);
fmpl2->max_size = cpu_to_be16(ubi->fm_wl_pool.max_size);
- for (i = 0; i < ubi->fm_wl_pool.size; i++)
+ for (i = 0; i < ubi->fm_wl_pool.size; i++) {
fmpl2->pebs[i] = cpu_to_be32(ubi->fm_wl_pool.pebs[i]);
+ set_seen(ubi, ubi->fm_wl_pool.pebs[i], seen_pebs);
+ }
for (node = rb_first(&ubi->free); node; node = rb_next(node)) {
wl_e = rb_entry(node, struct ubi_wl_entry, u.rb);
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
fec->pnum = cpu_to_be32(wl_e->pnum);
+ set_seen(ubi, wl_e->pnum, seen_pebs);
fec->ec = cpu_to_be32(wl_e->ec);
free_peb_count++;
@@ -1106,6 +1182,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
fec->pnum = cpu_to_be32(wl_e->pnum);
+ set_seen(ubi, wl_e->pnum, seen_pebs);
fec->ec = cpu_to_be32(wl_e->ec);
used_peb_count++;
@@ -1132,6 +1209,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
fec->pnum = cpu_to_be32(wl_e->pnum);
+ set_seen(ubi, wl_e->pnum, seen_pebs);
fec->ec = cpu_to_be32(wl_e->ec);
scrub_peb_count++;
@@ -1149,6 +1227,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
fec->pnum = cpu_to_be32(wl_e->pnum);
+ set_seen(ubi, wl_e->pnum, seen_pebs);
fec->ec = cpu_to_be32(wl_e->ec);
erase_peb_count++;
@@ -1208,6 +1287,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
for (i = 0; i < new_fm->used_blocks; i++) {
fmsb->block_loc[i] = cpu_to_be32(new_fm->e[i]->pnum);
+ set_seen(ubi, new_fm->e[i]->pnum, seen_pebs);
fmsb->block_ec[i] = cpu_to_be32(new_fm->e[i]->ec);
}
@@ -1241,11 +1321,13 @@ static int ubi_write_fastmap(struct ubi_device *ubi,
ubi_assert(new_fm);
ubi->fm = new_fm;
+ ret = self_check_seen(ubi, seen_pebs);
dbg_bld("fastmap written!");
out_kfree:
ubi_free_vid_hdr(ubi, avhdr);
ubi_free_vid_hdr(ubi, dvhdr);
+ free_seen(seen_pebs);
out:
return ret;
}