mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
misc: apanic: bad block handling
Add bad block handling in apanic Signed-off-by: Tom Zhu <ling.zhu@motorola.com> Signed-off-by: San Mehat <san@google.com> misc: apanic: Improved bad-block / watchdog handling 1. handle cases that there is no more good blocks 2. touch softlockup watchdog at the start of apanic 3. change unsigned char get_bb() to unsigned int get_bb() 4. return idx instead of rc2, to keep the previous written pages. Signed-off-by: Tom Zhu <ling.zhu@motorola.com> Signed-off-by: San Mehat <san@google.com>
This commit is contained in:
@@ -54,8 +54,6 @@ struct panic_header {
|
||||
u32 threads_length;
|
||||
};
|
||||
|
||||
#define CHECK_BB 0
|
||||
|
||||
struct apanic_data {
|
||||
struct mtd_info *mtd;
|
||||
struct panic_header curr;
|
||||
@@ -68,6 +66,72 @@ static struct apanic_data drv_ctx;
|
||||
static struct work_struct proc_removal_work;
|
||||
static DEFINE_MUTEX(drv_mutex);
|
||||
|
||||
static unsigned int *apanic_bbt;
|
||||
static unsigned int apanic_erase_blocks;
|
||||
static unsigned int apanic_good_blocks;
|
||||
|
||||
static void set_bb(unsigned int block, unsigned int *bbt)
|
||||
{
|
||||
unsigned int flag = 1;
|
||||
|
||||
BUG_ON(block >= apanic_erase_blocks);
|
||||
|
||||
flag = flag << (block%32);
|
||||
apanic_bbt[block/32] |= flag;
|
||||
apanic_good_blocks--;
|
||||
}
|
||||
|
||||
static unsigned int get_bb(unsigned int block, unsigned int *bbt)
|
||||
{
|
||||
unsigned int flag;
|
||||
|
||||
BUG_ON(block >= apanic_erase_blocks);
|
||||
|
||||
flag = 1 << (block%32);
|
||||
return apanic_bbt[block/32] & flag;
|
||||
}
|
||||
|
||||
static void alloc_bbt(struct mtd_info *mtd, unsigned int *bbt)
|
||||
{
|
||||
int bbt_size;
|
||||
apanic_erase_blocks = (mtd->size)>>(mtd->erasesize_shift);
|
||||
bbt_size = (apanic_erase_blocks+32)/32;
|
||||
|
||||
apanic_bbt = kmalloc(bbt_size*4, GFP_KERNEL);
|
||||
memset(apanic_bbt, 0, bbt_size*4);
|
||||
apanic_good_blocks = apanic_erase_blocks;
|
||||
}
|
||||
static void scan_bbt(struct mtd_info *mtd, unsigned int *bbt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < apanic_erase_blocks; i++) {
|
||||
if (mtd->block_isbad(mtd, i*mtd->erasesize))
|
||||
set_bb(i, apanic_bbt);
|
||||
}
|
||||
}
|
||||
|
||||
#define APANIC_INVALID_OFFSET 0xFFFFFFFF
|
||||
|
||||
static unsigned int phy_offset(struct mtd_info *mtd, unsigned int offset)
|
||||
{
|
||||
unsigned int logic_block = offset>>(mtd->erasesize_shift);
|
||||
unsigned int phy_block;
|
||||
unsigned good_block = 0;
|
||||
|
||||
for (phy_block = 0; phy_block < apanic_erase_blocks; phy_block++) {
|
||||
if (!get_bb(phy_block, apanic_bbt))
|
||||
good_block++;
|
||||
if (good_block == (logic_block + 1))
|
||||
break;
|
||||
}
|
||||
|
||||
if (good_block != (logic_block + 1))
|
||||
return APANIC_INVALID_OFFSET;
|
||||
|
||||
return offset + ((phy_block-logic_block)<<mtd->erasesize_shift);
|
||||
}
|
||||
|
||||
static void apanic_erase_callback(struct erase_info *done)
|
||||
{
|
||||
wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
|
||||
@@ -117,10 +181,17 @@ static int apanic_proc_read(char *buffer, char **start, off_t offset,
|
||||
page_no = (file_offset + offset) / ctx->mtd->writesize;
|
||||
page_offset = (file_offset + offset) % ctx->mtd->writesize;
|
||||
|
||||
|
||||
if (phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize))
|
||||
== APANIC_INVALID_OFFSET) {
|
||||
pr_err("apanic: reading an invalid address\n");
|
||||
mutex_unlock(&drv_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = ctx->mtd->read(ctx->mtd,
|
||||
(page_no * ctx->mtd->writesize),
|
||||
ctx->mtd->writesize,
|
||||
&len, ctx->bounce);
|
||||
phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize)),
|
||||
ctx->mtd->writesize,
|
||||
&len, ctx->bounce);
|
||||
|
||||
if (page_offset)
|
||||
count -= page_offset;
|
||||
@@ -153,14 +224,7 @@ static void mtd_panic_erase(void)
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&wait_q, &wait);
|
||||
|
||||
rc = ctx->mtd->block_isbad(ctx->mtd, erase.addr);
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR
|
||||
"apanic: Bad block check "
|
||||
"failed (%d)\n", rc);
|
||||
goto out;
|
||||
}
|
||||
if (rc) {
|
||||
if (get_bb(erase.addr>>ctx->mtd->erasesize_shift, apanic_bbt)) {
|
||||
printk(KERN_WARNING
|
||||
"apanic: Skipping erase of bad "
|
||||
"block @%llx\n", erase.addr);
|
||||
@@ -187,6 +251,8 @@ static void mtd_panic_erase(void)
|
||||
printk(KERN_INFO
|
||||
"apanic: Marked a bad block"
|
||||
" @%llx\n", erase.addr);
|
||||
set_bb(erase.addr>>ctx->mtd->erasesize_shift,
|
||||
apanic_bbt);
|
||||
continue;
|
||||
}
|
||||
goto out;
|
||||
@@ -237,12 +303,16 @@ static void mtd_panic_notify_add(struct mtd_info *mtd)
|
||||
|
||||
ctx->mtd = mtd;
|
||||
|
||||
if (mtd->block_isbad(mtd, 0)) {
|
||||
printk(KERN_ERR "apanic: Offset 0 bad block. Boourns!\n");
|
||||
alloc_bbt(mtd, apanic_bbt);
|
||||
scan_bbt(mtd, apanic_bbt);
|
||||
|
||||
if (apanic_good_blocks == 0) {
|
||||
printk(KERN_ERR "apanic: no any good blocks?!\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
rc = mtd->read(mtd, 0, mtd->writesize, &len, ctx->bounce);
|
||||
rc = mtd->read(mtd, phy_offset(mtd, 0), mtd->writesize,
|
||||
&len, ctx->bounce);
|
||||
if (rc && rc == -EBADMSG) {
|
||||
printk(KERN_WARNING
|
||||
"apanic: Bad ECC on block 0 (ignored)\n");
|
||||
@@ -341,6 +411,12 @@ static int apanic_writeflashpage(struct mtd_info *mtd, loff_t to,
|
||||
return 0;
|
||||
}
|
||||
|
||||
to = phy_offset(mtd, to);
|
||||
if (to == APANIC_INVALID_OFFSET) {
|
||||
printk(KERN_EMERG "apanic: write to invalid address\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (panic)
|
||||
rc = mtd->panic_write(mtd, to, mtd->writesize, &wlen, buf);
|
||||
else
|
||||
@@ -386,34 +462,12 @@ static int apanic_write_console(struct mtd_info *mtd, unsigned int off)
|
||||
break;
|
||||
if (rc != mtd->writesize)
|
||||
memset(ctx->bounce + rc, 0, mtd->writesize - rc);
|
||||
#if CHECK_BB
|
||||
check_badblock:
|
||||
rc = mtd->block_isbad(mtd, off);
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR
|
||||
"apanic: Bad block check "
|
||||
"failed (%d)\n", rc);
|
||||
}
|
||||
if (rc) {
|
||||
printk(KERN_WARNING
|
||||
"apanic: Skipping over bad "
|
||||
"block @%x\n", off);
|
||||
off += mtd->erasesize;
|
||||
printk("chk %u %llu\n", off, mtd->size);
|
||||
if (off >= mtd->size) {
|
||||
printk(KERN_EMERG
|
||||
"apanic: Too many bad blocks!\n");
|
||||
return -EIO;
|
||||
}
|
||||
goto check_badblock;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc2 = apanic_writeflashpage(mtd, off, ctx->bounce);
|
||||
if (rc2 <= 0) {
|
||||
printk(KERN_EMERG
|
||||
"apanic: Flash write failed (%d)\n", rc2);
|
||||
return rc2;
|
||||
return idx;
|
||||
}
|
||||
if (!last_chunk)
|
||||
idx += rc2;
|
||||
@@ -442,6 +496,7 @@ static int apanic(struct notifier_block *this, unsigned long event,
|
||||
/* Ensure that cond_resched() won't try to preempt anybody */
|
||||
add_preempt_count(PREEMPT_ACTIVE);
|
||||
#endif
|
||||
touch_softlockup_watchdog();
|
||||
|
||||
if (!ctx->mtd)
|
||||
goto out;
|
||||
|
||||
Reference in New Issue
Block a user