/* * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase * Units in a Virtual Unit Chain, i.e. all the units are disconnected. * * Since the chain is invalid then we will have to erase it from its * head (normally for INFTL we go from the oldest). But if it has a * loop then there is no oldest... */ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) { unsigned int block = first_block, block1; printk(KERN_WARNING "INFTL: formatting chain at block %d\n", first_block); for (;;) { block1 = inftl->PUtable[block]; printk(KERN_WARNING "INFTL: formatting block %d\n", block); if (INFTL_formatblock(inftl, block) < 0) { /* * Cannot format !!!! Mark it as Bad Unit, * FixMe: update the BadUnitTable on disk. */ inftl->PUtable[block] = BLOCK_RESERVED; } else { inftl->PUtable[block] = BLOCK_FREE; } /* Goto next block on the chain */ block = block1; if (block == BLOCK_NIL || block >= inftl->lastEUN) break; } }
/* * Given a Virtual Unit Chain, see if it can be deleted, and if so do it. */ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) { struct mtd_info *mtd = inftl->mbd.mtd; unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, status; int block, silly; struct inftl_bci bci; size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p," "thisVUC=%d)\n", inftl, thisVUC); memset(BlockUsed, 0, sizeof(BlockUsed)); memset(BlockDeleted, 0, sizeof(BlockDeleted)); thisEUN = inftl->VUtable[thisVUC]; if (thisEUN == BLOCK_NIL) { printk(KERN_WARNING "INFTL: trying to delete non-existent " "Virtual Unit Chain %d!\n", thisVUC); return; } /* * Scan through the Erase Units to determine whether any data is in * each of the 512-byte blocks within the Chain. */ silly = MAX_LOOPS; while (thisEUN < inftl->nb_blocks) { for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) { if (BlockUsed[block] || BlockDeleted[block]) continue; if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + (block * SECTORSIZE), 8 , &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; switch(status) { case SECTOR_FREE: case SECTOR_IGNORE: break; case SECTOR_USED: BlockUsed[block] = 1; continue; case SECTOR_DELETED: BlockDeleted[block] = 1; continue; default: printk(KERN_WARNING "INFTL: unknown status " "for block %d in EUN %d: 0x%x\n", block, thisEUN, status); } } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in Virtual " "Unit Chain 0x%x\n", thisVUC); return; } thisEUN = inftl->PUtable[thisEUN]; } for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) if (BlockUsed[block]) return; /* * For each block in the chain free it and make it available * for future use. Erase from the oldest unit first. */ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC); for (;;) { u16 *prevEUN = &inftl->VUtable[thisVUC]; thisEUN = *prevEUN; /* If the chain is all gone already, we're done */ if (thisEUN == BLOCK_NIL) { DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN); return; } /* Find oldest unit in chain. */ while (inftl->PUtable[thisEUN] != BLOCK_NIL) { BUG_ON(thisEUN >= inftl->nb_blocks); prevEUN = &inftl->PUtable[thisEUN]; thisEUN = *prevEUN; } DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n", thisEUN, thisVUC); if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { /* Correctly erased : mark it as free */ inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; } /* Now sort out whatever was pointing to it... */ *prevEUN = BLOCK_NIL; /* Ideally we'd actually be responsive to new requests while we're doing this -- if there's free space why should others be made to wait? */ cond_resched(); } inftl->VUtable[thisVUC] = BLOCK_NIL; }
static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock) { u16 BlockMap[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, prevEUN, status; struct mtd_info *mtd = inftl->mbd.mtd; int block, silly; unsigned int targetEUN; struct inftl_oob oob; size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d," "pending=%d)\n", inftl, thisVUC, pendingblock); memset(BlockMap, 0xff, sizeof(BlockMap)); memset(BlockDeleted, 0, sizeof(BlockDeleted)); thisEUN = targetEUN = inftl->VUtable[thisVUC]; if (thisEUN == BLOCK_NIL) { printk(KERN_WARNING "INFTL: trying to fold non-existent " "Virtual Unit Chain %d!\n", thisVUC); return BLOCK_NIL; } /* * Scan to find the Erase Unit which holds the actual data for each * 512-byte block within the Chain. */ silly = MAX_LOOPS; while (thisEUN < inftl->nb_blocks) { for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { if ((BlockMap[block] != BLOCK_NIL) || BlockDeleted[block]) continue; if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + (block * SECTORSIZE), 16, &retlen, (char *)&oob) < 0) status = SECTOR_IGNORE; else status = oob.b.Status | oob.b.Status1; switch(status) { case SECTOR_FREE: case SECTOR_IGNORE: break; case SECTOR_USED: BlockMap[block] = thisEUN; continue; case SECTOR_DELETED: BlockDeleted[block] = 1; continue; default: printk(KERN_WARNING "INFTL: unknown status " "for block %d in EUN %d: %x\n", block, thisEUN, status); break; } } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in Virtual " "Unit Chain 0x%x\n", thisVUC); return BLOCK_NIL; } thisEUN = inftl->PUtable[thisEUN]; } /* * OK. We now know the location of every block in the Virtual Unit * Chain, and the Erase Unit into which we are supposed to be copying. * Go for it. */ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n", thisVUC, targetEUN); for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) { unsigned char movebuf[SECTORSIZE]; int ret; /* * If it's in the target EUN already, or if it's pending write, * do nothing. */ if (BlockMap[block] == targetEUN || (pendingblock == (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) { continue; } /* * Copy only in non free block (free blocks can only * happen in case of media errors or deleted blocks). */ if (BlockMap[block] == BLOCK_NIL) continue; ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret < 0 && ret != -EUCLEAN) { ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret != -EIO) DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " "away on retry?\n"); } memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf, (char *)&oob); } /* * Newest unit in chain now contains data from _all_ older units. * So go through and erase each unit in chain, oldest first. (This * is important, by doing oldest first if we crash/reboot then it * it is relatively simple to clean up the mess). */ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n", thisVUC); for (;;) { /* Find oldest unit in chain. */ thisEUN = inftl->VUtable[thisVUC]; prevEUN = BLOCK_NIL; while (inftl->PUtable[thisEUN] != BLOCK_NIL) { prevEUN = thisEUN; thisEUN = inftl->PUtable[thisEUN]; } /* Check if we are all done */ if (thisEUN == targetEUN) break; /* Unlink the last block from the chain. */ inftl->PUtable[prevEUN] = BLOCK_NIL; /* Now try to erase it. */ if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { /* Correctly erased : mark it as free */ inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; } } return targetEUN; }
int INFTL_mount(struct INFTLrecord *s) { unsigned int block, first_block, prev_block, last_block; unsigned int first_logical_block, logical_block, erase_mark; int chain_length, do_format_chain; struct inftl_unithead1 h0; struct inftl_unittail h1; int i, retlen; u8 *ANACtable, ANAC; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s); /* Search for INFTL MediaHeader and Spare INFTL Media Header */ if (find_boot_record(s) < 0) { printk(KERN_WARNING "INFTL: could not find valid boot record?\n"); return -1; } /* Init the logical to physical table */ for (i = 0; i < s->nb_blocks; i++) s->VUtable[i] = BLOCK_NIL; logical_block = block = BLOCK_NIL; /* Temporary buffer to store ANAC numbers. */ ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); memset(ANACtable, 0, s->nb_blocks); /* * First pass is to explore each physical unit, and construct the * virtual chains that exist (newest physical unit goes into VUtable). * Any block that is in any way invalid will be left in the * NOTEXPLORED state. Then at the end we will try to format it and * mark it as free. */ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n"); for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) { if (s->PUtable[first_block] != BLOCK_NOTEXPLORED) continue; do_format_chain = 0; first_logical_block = BLOCK_NIL; last_block = BLOCK_NIL; block = first_block; for (chain_length = 0; ; chain_length++) { if ((chain_length == 0) && (s->PUtable[block] != BLOCK_NOTEXPLORED)) { /* Nothing to do here, onto next block */ break; } if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8, &retlen, (char *)&h0) < 0 || MTD_READOOB(s->mtd, block * s->EraseSize + 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { /* Should never happen? */ do_format_chain++; break; } logical_block = le16_to_cpu(h0.virtualUnitNo); prev_block = le16_to_cpu(h0.prevUnitNo); erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1)); ANACtable[block] = h0.ANAC; /* Previous block is relative to start of Partition */ if (prev_block < s->nb_blocks) prev_block += s->firstEUN; /* Already explored partial chain? */ if (s->PUtable[block] != BLOCK_NOTEXPLORED) { /* Check if chain for this logical */ if (logical_block == first_logical_block) { if (last_block != BLOCK_NIL) s->PUtable[last_block] = block; } break; } /* Check for invalid block */ if (erase_mark != ERASE_MARK) { printk(KERN_WARNING "INFTL: corrupt block %d " "in chain %d, chain length %d, erase " "mark 0x%x?\n", block, first_block, chain_length, erase_mark); /* * Assume end of chain, probably incomplete * fold/erase... */ if (chain_length == 0) do_format_chain++; break; } /* Check for it being free already then... */ if ((logical_block == BLOCK_FREE) || (logical_block == BLOCK_NIL)) { s->PUtable[block] = BLOCK_FREE; break; } /* Sanity checks on block numbers */ if ((logical_block >= s->nb_blocks) || ((prev_block >= s->nb_blocks) && (prev_block != BLOCK_NIL))) { if (chain_length > 0) { printk(KERN_WARNING "INFTL: corrupt " "block %d in chain %d?\n", block, first_block); do_format_chain++; } break; } if (first_logical_block == BLOCK_NIL) { first_logical_block = logical_block; } else { if (first_logical_block != logical_block) { /* Normal for folded chain... */ break; } } /* * Current block is valid, so if we followed a virtual * chain to get here then we can set the previous * block pointer in our PUtable now. Then move onto * the previous block in the chain. */ s->PUtable[block] = BLOCK_NIL; if (last_block != BLOCK_NIL) s->PUtable[last_block] = block; last_block = block; block = prev_block; /* Check for end of chain */ if (block == BLOCK_NIL) break; /* Validate next block before following it... */ if (block > s->lastEUN) { printk(KERN_WARNING "INFTL: invalid previous " "block %d in chain %d?\n", block, first_block); do_format_chain++; break; } } if (do_format_chain) { format_chain(s, first_block); continue; } /* * Looks like a valid chain then. It may not really be the * newest block in the chain, but it is the newest we have * found so far. We might update it in later iterations of * this loop if we find something newer. */ s->VUtable[first_logical_block] = first_block; logical_block = BLOCK_NIL; } #ifdef CONFIG_MTD_DEBUG_VERBOSE if (CONFIG_MTD_DEBUG_VERBOSE >= 2) INFTL_dumptables(s); #endif /* * Second pass, check for infinite loops in chains. These are * possible because we don't update the previous pointers when * we fold chains. No big deal, just fix them up in PUtable. */ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n"); for (logical_block = 0; logical_block < s->numvunits; logical_block++) { block = s->VUtable[logical_block]; last_block = BLOCK_NIL; /* Check for free/reserved/nil */ if (block >= BLOCK_RESERVED) continue; ANAC = ANACtable[block]; for (i = 0; i < s->numvunits; i++) { if (s->PUtable[block] == BLOCK_NIL) break; if (s->PUtable[block] > s->lastEUN) { printk(KERN_WARNING "INFTL: invalid prev %d, " "in virtual chain %d\n", s->PUtable[block], logical_block); s->PUtable[block] = BLOCK_NIL; } if (ANACtable[block] != ANAC) { /* * Chain must point back to itself. This is ok, * but we will need adjust the tables with this * newest block and oldest block. */ s->VUtable[logical_block] = block; s->PUtable[last_block] = BLOCK_NIL; break; } ANAC--; last_block = block; block = s->PUtable[block]; } if (i >= s->nb_blocks) { /* * Uhoo, infinite chain with valid ANACS! * Format whole chain... */ format_chain(s, first_block); } } #ifdef CONFIG_MTD_DEBUG_VERBOSE if (CONFIG_MTD_DEBUG_VERBOSE >= 2) INFTL_dumptables(s); if (CONFIG_MTD_DEBUG_VERBOSE >= 2) INFTL_dumpVUchains(s); #endif /* * Third pass, format unreferenced blocks and init free block count. */ s->numfreeEUNs = 0; s->LastFreeEUN = BLOCK_NIL; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n"); for (block = s->firstEUN; block <= s->lastEUN; block++) { if (s->PUtable[block] == BLOCK_NOTEXPLORED) { printk("INFTL: unreferenced block %d, formatting it\n", block); if (INFTL_formatblock(s, block) < 0) s->PUtable[block] = BLOCK_RESERVED; else s->PUtable[block] = BLOCK_FREE; } if (s->PUtable[block] == BLOCK_FREE) { s->numfreeEUNs++; if (s->LastFreeEUN == BLOCK_NIL) s->LastFreeEUN = block; } } kfree(ANACtable); return 0; }
static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) { struct mtd_info *mtd = inftl->mbd.mtd; unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, status; int block, silly; struct inftl_bci bci; size_t retlen; pr_debug("INFTL: INFTL_trydeletechain(inftl=%p," "thisVUC=%d)\n", inftl, thisVUC); memset(BlockUsed, 0, sizeof(BlockUsed)); memset(BlockDeleted, 0, sizeof(BlockDeleted)); thisEUN = inftl->VUtable[thisVUC]; if (thisEUN == BLOCK_NIL) { printk(KERN_WARNING "INFTL: trying to delete non-existent " "Virtual Unit Chain %d!\n", thisVUC); return; } silly = MAX_LOOPS; while (thisEUN < inftl->nb_blocks) { for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) { if (BlockUsed[block] || BlockDeleted[block]) continue; if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + (block * SECTORSIZE), 8 , &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; switch(status) { case SECTOR_FREE: case SECTOR_IGNORE: break; case SECTOR_USED: BlockUsed[block] = 1; continue; case SECTOR_DELETED: BlockDeleted[block] = 1; continue; default: printk(KERN_WARNING "INFTL: unknown status " "for block %d in EUN %d: 0x%x\n", block, thisEUN, status); } } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in Virtual " "Unit Chain 0x%x\n", thisVUC); return; } thisEUN = inftl->PUtable[thisEUN]; } for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) if (BlockUsed[block]) return; pr_debug("INFTL: deleting empty VUC %d\n", thisVUC); for (;;) { u16 *prevEUN = &inftl->VUtable[thisVUC]; thisEUN = *prevEUN; if (thisEUN == BLOCK_NIL) { pr_debug("INFTL: Empty VUC %d for deletion was already absent\n", thisEUN); return; } while (inftl->PUtable[thisEUN] != BLOCK_NIL) { BUG_ON(thisEUN >= inftl->nb_blocks); prevEUN = &inftl->PUtable[thisEUN]; thisEUN = *prevEUN; } pr_debug("Deleting EUN %d from VUC %d\n", thisEUN, thisVUC); if (INFTL_formatblock(inftl, thisEUN) < 0) { inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; } *prevEUN = BLOCK_NIL; cond_resched(); } inftl->VUtable[thisVUC] = BLOCK_NIL; }
static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock) { u16 BlockMap[MAX_SECTORS_PER_UNIT]; unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; unsigned int thisEUN, prevEUN, status; struct mtd_info *mtd = inftl->mbd.mtd; int block, silly; unsigned int targetEUN; struct inftl_oob oob; size_t retlen; pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)\n", inftl, thisVUC, pendingblock); memset(BlockMap, 0xff, sizeof(BlockMap)); memset(BlockDeleted, 0, sizeof(BlockDeleted)); thisEUN = targetEUN = inftl->VUtable[thisVUC]; if (thisEUN == BLOCK_NIL) { printk(KERN_WARNING "INFTL: trying to fold non-existent " "Virtual Unit Chain %d!\n", thisVUC); return BLOCK_NIL; } silly = MAX_LOOPS; while (thisEUN < inftl->nb_blocks) { for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { if ((BlockMap[block] != BLOCK_NIL) || BlockDeleted[block]) continue; if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + (block * SECTORSIZE), 16, &retlen, (char *)&oob) < 0) status = SECTOR_IGNORE; else status = oob.b.Status | oob.b.Status1; switch(status) { case SECTOR_FREE: case SECTOR_IGNORE: break; case SECTOR_USED: BlockMap[block] = thisEUN; continue; case SECTOR_DELETED: BlockDeleted[block] = 1; continue; default: printk(KERN_WARNING "INFTL: unknown status " "for block %d in EUN %d: %x\n", block, thisEUN, status); break; } } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in Virtual " "Unit Chain 0x%x\n", thisVUC); return BLOCK_NIL; } thisEUN = inftl->PUtable[thisEUN]; } pr_debug("INFTL: folding chain %d into unit %d\n", thisVUC, targetEUN); for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) { unsigned char movebuf[SECTORSIZE]; int ret; if (BlockMap[block] == targetEUN || (pendingblock == (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) { continue; } if (BlockMap[block] == BLOCK_NIL) continue; ret = mtd_read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret < 0 && !mtd_is_bitflip(ret)) { ret = mtd_read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret != -EIO) pr_debug("INFTL: error went away on retry?\n"); } memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf, (char *)&oob); } pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC); for (;;) { thisEUN = inftl->VUtable[thisVUC]; prevEUN = BLOCK_NIL; while (inftl->PUtable[thisEUN] != BLOCK_NIL) { prevEUN = thisEUN; thisEUN = inftl->PUtable[thisEUN]; } if (thisEUN == targetEUN) break; inftl->PUtable[prevEUN] = BLOCK_NIL; if (INFTL_formatblock(inftl, thisEUN) < 0) { inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; } } return targetEUN; }