From c3baf278d3bf8d628df0b28aa4921c5457d40211 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 15 Aug 2016 18:22:01 -0500 Subject: mtd: nand_bbt: Move BBT block selection logic out of write_bbt() This clarifies the write_bbt() function by removing the write label and simplifying the error/exit path. Signed-off-by: Boris Brezillon Tested-by: Kyle Roeschley --- drivers/mtd/nand/nand_bbt.c | 110 +++++++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 36 deletions(-) (limited to 'drivers/mtd/nand/nand_bbt.c') diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 2fbb523df066..9fbd66482403 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -604,6 +604,69 @@ static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, search_bbt(mtd, buf, md); } +/** + * get_bbt_block - Get the first valid eraseblock suitable to store a BBT + * @this: the NAND device + * @td: the BBT description + * @md: the mirror BBT descriptor + * @chip: the CHIP selector + * + * This functions returns a positive block number pointing a valid eraseblock + * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if + * all blocks are already used of marked bad. If td->pages[chip] was already + * pointing to a valid block we re-use it, otherwise we search for the next + * valid one. + */ +static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, + struct nand_bbt_descr *md, int chip) +{ + int startblock, dir, page, numblocks, i; + + /* + * There was already a version of the table, reuse the page. This + * applies for absolute placement too, as we have the page number in + * td->pages. + */ + if (td->pages[chip] != -1) + return td->pages[chip] >> + (this->bbt_erase_shift - this->page_shift); + + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); + if (!(td->options & NAND_BBT_PERCHIP)) + numblocks *= this->numchips; + + /* + * Automatic placement of the bad block table. Search direction + * top -> down? + */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + + /* Check, if the block is bad */ + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: + continue; + } + + page = block << (this->bbt_erase_shift - this->page_shift); + + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + return block; + } + + return -ENOSPC; +} + /** * write_bbt - [GENERIC] (Re)write the bad block table * @mtd: MTD device structure @@ -621,7 +684,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_chip *this = mtd_to_nand(mtd); struct erase_info einfo; int i, res, chip = 0; - int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int bits, page, offs, numblocks, sft, sftmsk; int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; @@ -653,45 +716,20 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, /* Loop through the chips */ for (; chip < nrchips; chip++) { - /* - * There was already a version of the table, reuse the page - * This applies for absolute placement too, as we have the - * page nr. in td->pages. - */ - if (td->pages[chip] != -1) { - page = td->pages[chip]; - goto write; + int block; + + block = get_bbt_block(this, td, md, chip); + if (block < 0) { + pr_err("No space left to write bad block table\n"); + res = block; + goto outerr; } /* - * Automatic placement of the bad block table. Search direction - * top -> down? + * get_bbt_block() returns a block number, shift the value to + * get a page number. */ - if (td->options & NAND_BBT_LASTBLOCK) { - startblock = numblocks * (chip + 1) - 1; - dir = -1; - } else { - startblock = chip * numblocks; - dir = 1; - } - - for (i = 0; i < td->maxblocks; i++) { - int block = startblock + dir * i; - /* Check, if the block is bad */ - switch (bbt_get_entry(this, block)) { - case BBT_BLOCK_WORN: - case BBT_BLOCK_FACTORY_BAD: - continue; - } - page = block << - (this->bbt_erase_shift - this->page_shift); - /* Check, if the block is used by the mirror table */ - if (!md || md->pages[chip] != page) - goto write; - } - pr_err("No space left to write bad block table\n"); - return -ENOSPC; - write: + page = block << (this->bbt_erase_shift - this->page_shift); /* Set up shift count and masks for the flash table */ bits = td->options & NAND_BBT_NRBITS_MSK; -- cgit v1.2.3-59-g8ed1b From 10ffd570f11701972aff2a6f91f3d253d6f0e7ee Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Mon, 15 Aug 2016 18:22:02 -0500 Subject: mtd: nand_bbt: scan for next free bbt block if writing bbt fails If erasing or writing the BBT fails, we should mark the current BBT block as bad and use the BBT descriptor to scan for the next available unused block in the BBT. We should only return a failure if there isn't any space left. Signed-off-by: Kyle Roeschley Suggested-by: Jeff Westfahl Tested-by: Kyle Roeschley Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_bbt.c | 51 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/nand/nand_bbt.c') diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 9fbd66482403..7695efea65f2 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -667,6 +667,37 @@ static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, return -ENOSPC; } +/** + * mark_bbt_block_bad - Mark one of the block reserved for BBT bad + * @this: the NAND device + * @td: the BBT description + * @chip: the CHIP selector + * @block: the BBT block to mark + * + * Blocks reserved for BBT can become bad. This functions is an helper to mark + * such blocks as bad. It takes care of updating the in-memory BBT, marking the + * block as bad using a bad block marker and invalidating the associated + * td->pages[] entry. + */ +static void mark_bbt_block_bad(struct nand_chip *this, + struct nand_bbt_descr *td, + int chip, int block) +{ + struct mtd_info *mtd = nand_to_mtd(this); + loff_t to; + int res; + + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + to = (loff_t)block << this->bbt_erase_shift; + res = this->block_markbad(mtd, to); + if (res) + pr_warn("nand_bbt: error %d while marking block %d bad\n", + res, block); + + td->pages[chip] = -1; +} + /** * write_bbt - [GENERIC] (Re)write the bad block table * @mtd: MTD device structure @@ -715,7 +746,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, } /* Loop through the chips */ - for (; chip < nrchips; chip++) { + while (chip < nrchips) { int block; block = get_bbt_block(this, td, md, chip); @@ -825,20 +856,28 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, einfo.addr = to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); - if (res < 0) - goto outerr; + if (res < 0) { + pr_warn("nand_bbt: error while erasing BBT block %d\n", + res); + mark_bbt_block_bad(this, td, chip, block); + continue; + } res = scan_write_bbt(mtd, to, len, buf, td->options & NAND_BBT_NO_OOB ? NULL : &buf[len]); - if (res < 0) - goto outerr; + if (res < 0) { + pr_warn("nand_bbt: error while writing BBT block %d\n", + res); + mark_bbt_block_bad(this, td, chip, block); + continue; + } pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", (unsigned long long)to, td->version[chip]); /* Mark it as used */ - td->pages[chip] = page; + td->pages[chip++] = page; } return 0; -- cgit v1.2.3-59-g8ed1b