From: Tom Zhu Date: Mon, 21 Sep 2009 21:36:05 +0000 (-0500) Subject: misc: apanic: bad block handling X-Git-Tag: firefly_0821_release~11659 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=03d778875b93d95217990e3e420252a2ed7fa3a8;p=firefly-linux-kernel-4.4.55.git misc: apanic: bad block handling Add bad block handling in apanic Signed-off-by: Tom Zhu Signed-off-by: San Mehat 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 Signed-off-by: San Mehat --- diff --git a/drivers/misc/apanic.c b/drivers/misc/apanic.c index 2f858be7ef59..f50881ba917d 100644 --- a/drivers/misc/apanic.c +++ b/drivers/misc/apanic.c @@ -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)<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;