Find the bad blocks for each partition.
During kernel init, the BBT is scanned and the bad block is mapped to
corresponding MTD partitions. This information is posted in
/proc/sys/dev/repartition/bbinfo. The information can be used later for
purposes like non-bb check of kernel partition during manufacturing,
diagnostics and statistics.
Change-Id: I3a7e0129b1b48bb611647f08b9baf68c7cf26480
diff --git a/arch/mips/brcmstb/partitionmap.c b/arch/mips/brcmstb/partitionmap.c
index 6dd6374..2136073 100644
--- a/arch/mips/brcmstb/partitionmap.c
+++ b/arch/mips/brcmstb/partitionmap.c
@@ -20,7 +20,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include "partitionmap.h"
+#include <mtd/partitionmap.h>
#define CFE_NAME "cfe"
#define HNVRAM_NAME "hnvram"
@@ -131,13 +131,18 @@
/* By default, use partition map v2. */
struct mtd_partition *fixed_nor_partition_map = fixed_nor_partition_map_v1;
int fixed_nor_partition_map_size = ARRAY_SIZE(fixed_nor_partition_map_v1);
+EXPORT_SYMBOL(fixed_nor_partition_map_size);
struct mtd_partition *fixed_nand_partition_map = fixed_nand_partition_map_v2;
int fixed_nand_partition_map_size = ARRAY_SIZE(fixed_nand_partition_map_v2);
+EXPORT_SYMBOL(fixed_nand_partition_map_size);
static DEFINE_MUTEX(partitionmap_mutex);
static struct list_head mtd_dev_list = LIST_HEAD_INIT(mtd_dev_list);
+static DEFINE_MUTEX(bb_mutex);
+static struct list_head bb_list = LIST_HEAD_INIT(bb_list);
+
int partitionmap_version = 2; /* partition map version */
EXPORT_SYMBOL(partitionmap_version);
@@ -146,21 +151,84 @@
struct platform_device *pdev;
};
+struct bb_entry {
+ struct list_head list;
+ loff_t offset;
+};
+
+size_t partitionmap_print_bbinfo(char *buffer, size_t size)
+{
+ size_t ret;
+ size_t pos = 0;
+ int i;
+ struct mtd_partition *mtd;
+ struct bb_entry *bb;
+ size_t *bb_map = kzalloc(fixed_nand_partition_map_size*sizeof(size_t),
+ GFP_KERNEL);
+ if (!bb_map)
+ return 0;
+
+ ret = scnprintf(buffer + pos, size - pos, "partition: badblocks\n");
+ if (!ret) {
+ kfree(bb_map);
+ return 0;
+ }
+
+ pos += ret;
+
+ mutex_lock(&bb_mutex);
+ list_for_each_entry(bb, &bb_list, list) {
+ for (i= 0, mtd = &fixed_nand_partition_map[0];
+ i < fixed_nand_partition_map_size; ++i, ++mtd) {
+ if ((bb->offset >= mtd->offset) &&
+ (bb->offset < (mtd->offset + mtd->size))) {
+ ++bb_map[i];
+ }
+ }
+ }
+ mutex_unlock(&bb_mutex);
+
+ for (i = 0, mtd = &fixed_nand_partition_map[0];
+ i < fixed_nand_partition_map_size; ++i, ++mtd) {
+ if (pos < size) {
+ ret = (size_t) scnprintf(
+ buffer + pos, size - pos,
+ "%s: %u\n", mtd->name,
+ bb_map[i]);
+ } else {
+ ret = 0;
+ }
+ if (!ret) {
+ kfree(bb_map);
+ return ret;
+ }
+ pos += ret;
+ }
+
+ if (pos && buffer[pos - 1] == '\n') {
+ buffer[pos - 1] = '\0';
+ }
+
+ kfree(bb_map);
+
+ return pos;
+}
+EXPORT_SYMBOL(partitionmap_print_bbinfo);
+
size_t partitionmap_print_info(char *buffer, size_t size)
{
size_t ret;
- size_t pos = 0;
+ size_t pos = 0;
struct mtd_dev_entry *mtd;
ret = scnprintf(buffer + pos, size - pos,
"Partition map version: %d\n",
- partitionmap_version);
+ partitionmap_version);
if (!ret)
return 0;
pos += ret;
mutex_lock(&partitionmap_mutex);
-
list_for_each_entry(mtd, &mtd_dev_list, list) {
if (pos < size) {
ret = (size_t)scnprintf(buffer + pos, size - pos,
@@ -175,9 +243,12 @@
}
pos += ret;
}
-
mutex_unlock(&partitionmap_mutex);
+ if (pos && buffer[pos - 1] == '\n') {
+ buffer[pos - 1] = '\0';
+ }
+
return pos;
}
EXPORT_SYMBOL(partitionmap_print_info);
@@ -210,12 +281,28 @@
return 2;
}
pr_info("Switched partition from version %d to version %d.\n",
- partitionmap_version, pver);
- partitionmap_version = pver;
+ partitionmap_version, pver);
+ partitionmap_version = pver;
return 0;
}
EXPORT_SYMBOL(switch_partition);
+void register_badblock(loff_t offset)
+{
+ struct bb_entry* obj = (struct bb_entry *)
+ kmalloc(sizeof(struct bb_entry), GFP_KERNEL);
+
+ if (!obj)
+ panic("Insufficient memory to allocate MTD device entry\n");
+
+ obj->offset = offset;
+
+ mutex_lock(&bb_mutex);
+ list_add(&obj->list, &bb_list);
+ mutex_unlock(&bb_mutex);
+}
+EXPORT_SYMBOL(register_badblock);
+
void register_nand(struct platform_device *pdev)
{
struct mtd_dev_entry* obj = (struct mtd_dev_entry *)
@@ -252,7 +339,7 @@
static int __init partitionver_setup(char *options)
{
int pver;
- char* endp;
+ char* endp;
if (*options == 0)
return 0;
pver = simple_strtol(options, &endp, 10);
diff --git a/arch/mips/brcmstb/repartition.c b/arch/mips/brcmstb/repartition.c
index cfb9d13..70f5503 100644
--- a/arch/mips/brcmstb/repartition.c
+++ b/arch/mips/brcmstb/repartition.c
@@ -19,15 +19,17 @@
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
-#include "partitionmap.h"
+#include <mtd/partitionmap.h>
#include "repartition.h"
#define PARTITIONMAP_STR_SIZE 512
+#define BADBLOCK_STR_SIZE 512
static const char repartition_proc_name[] = "repartition";
static struct repartition_sysctl_setting {
char info[PARTITIONMAP_STR_SIZE]; /* partition map info */
+ char bbinfo[BADBLOCK_STR_SIZE]; /* bad block info */
int version;
int disable;
int disable_min;
@@ -49,15 +51,42 @@
return 0;
}
+static int repartition_print_bbinfo(void)
+{
+ int ret = partitionmap_print_bbinfo(setting.bbinfo, sizeof(setting.bbinfo));
+ if (!ret)
+ return 1;
+
+ return 0;
+}
+
static void reinit_nand(void)
{
flush_nand();
init_nand();
}
+static int repartition_sysctl_bbinfo(ctl_table *ctl, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ if (!*lenp || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if(repartition_print_bbinfo()) {
+ *lenp = 0;
+ pr_err("insufficient bad block info buffer\n");
+ return -ENOMEM;
+ }
+ proc_dostring(ctl, write, buffer, lenp, ppos);
+ return 0;
+}
+
static int repartition_sysctl_info(ctl_table *ctl, int write,
- void __user *buffer, size_t *lenp,
- loff_t *ppos)
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
{
if (!*lenp || (*ppos && !write)) {
*lenp = 0;
@@ -70,7 +99,7 @@
return -ENOMEM;
}
proc_dostring(ctl, write, buffer, lenp, ppos);
- return 0;
+ return 0;
}
static int repartition_sysctl_version(ctl_table *ctl, int write,
@@ -109,6 +138,13 @@
static ctl_table repartition_data_table[] = {
{
+ .procname = "bbinfo",
+ .data = setting.bbinfo,
+ .maxlen = BADBLOCK_STR_SIZE,
+ .mode = 0444,
+ .proc_handler = repartition_sysctl_bbinfo,
+ },
+ {
.procname = "info",
.data = setting.info,
.maxlen = PARTITIONMAP_STR_SIZE,
@@ -170,7 +206,7 @@
static int __init partitionver_setup(char *options)
{
int pver;
- char* endp;
+ char* endp;
if (*options == 0)
return 0;
pver = simple_strtol(options, &endp, 10);
@@ -184,7 +220,7 @@
{
if (repartition_sysctl_header)
unregister_sysctl_table(repartition_sysctl_header);
- return 0;
+ return 0;
}
module_exit(repartition_exit);
#endif /* MODULE */
diff --git a/arch/mips/brcmstb/setup.c b/arch/mips/brcmstb/setup.c
index f3c70ea..ca042a0 100644
--- a/arch/mips/brcmstb/setup.c
+++ b/arch/mips/brcmstb/setup.c
@@ -56,7 +56,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/map.h>
-#include "partitionmap.h"
+#include <mtd/partitionmap.h>
/* Default SPI flash chip selects to scan at boot time
Can be overriden with spics=N kernel boot argument
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index f5be986..c660b94 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -67,6 +67,7 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
+#include <mtd/partitionmap.h>
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
{
@@ -236,6 +237,9 @@
*/
pr_info("nand_read_bbt: bad block at 0x%012llx\n",
(loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+#ifdef CONFIG_BRUNO
+ register_badblock((loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+#endif
/* Factory marked bad or worn out? */
if (tmp == 0)
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
diff --git a/arch/mips/brcmstb/partitionmap.h b/include/mtd/partitionmap.h
similarity index 89%
rename from arch/mips/brcmstb/partitionmap.h
rename to include/mtd/partitionmap.h
index 4c1ecab..ebc52a9 100644
--- a/arch/mips/brcmstb/partitionmap.h
+++ b/include/mtd/partitionmap.h
@@ -19,9 +19,11 @@
extern int fixed_nand_partition_map_size;
extern struct mtd_partition *fixed_nor_partition_map;
extern struct mtd_partition *fixed_nand_partition_map;
+extern size_t partitionmap_print_bbinfo(char *buffer, size_t size);
extern size_t partitionmap_print_info(char *buffer, size_t size);
extern int switch_partition(int pver);
extern void register_nand(struct platform_device *pdev);
+extern void register_badblock(loff_t offset);
extern void flush_nand(void);
#endif /* __PARTITIONMAP_H__ */