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; }
/* * INFTL_findwriteunit: Return the unit number into which we can write * for this block. Make it available if it isn't already. */ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) { unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); unsigned int thisEUN, writeEUN, prev_block, status; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); struct inftl_oob oob; struct inftl_bci bci; unsigned char anac, nacs, parity; size_t retlen; int silly, silly2 = 3; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p," "block=%d)\n", inftl, block); do { /* * Scan the media to find a unit in the VUC which has * a free space for the block in question. */ writeEUN = BLOCK_NIL; thisEUN = inftl->VUtable[thisVUC]; silly = MAX_LOOPS; while (thisEUN <= inftl->lastEUN) { MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci); status = bci.Status | bci.Status1; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " "EUN %d is %x\n", block , writeEUN, status); switch(status) { case SECTOR_FREE: writeEUN = thisEUN; break; case SECTOR_DELETED: case SECTOR_USED: /* Can't go any further */ goto hitused; case SECTOR_IGNORE: break; default: /* * Invalid block. Don't use it any more. * Must implement. */ break; } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in " "Virtual Unit Chain 0x%x\n", thisVUC); return 0xffff; } /* Skip to next block in chain */ thisEUN = inftl->PUtable[thisEUN]; } hitused: if (writeEUN != BLOCK_NIL) return writeEUN; /* * OK. We didn't find one in the existing chain, or there * is no existing chain. Allocate a new one. */ writeEUN = INFTL_findfreeblock(inftl, 0); if (writeEUN == BLOCK_NIL) { /* * That didn't work - there were no free blocks just * waiting to be picked up. We're going to have to fold * a chain to make room. */ thisEUN = INFTL_makefreeblock(inftl, 0xffff); /* * Hopefully we free something, lets try again. * This time we are desperate... */ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 " "to find free EUN to accommodate write to " "VUC %d\n", thisVUC); writeEUN = INFTL_findfreeblock(inftl, 1); if (writeEUN == BLOCK_NIL) { /* * Ouch. This should never happen - we should * always be able to make some room somehow. * If we get here, we've allocated more storage * space than actual media, or our makefreeblock * routine is missing something. */ printk(KERN_WARNING "INFTL: cannot make free " "space.\n"); #ifdef DEBUG INFTL_dumptables(inftl); INFTL_dumpVUchains(inftl); #endif return BLOCK_NIL; } } /* * Insert new block into virtual chain. Firstly update the * block headers in flash... */ anac = 0; nacs = 0; thisEUN = inftl->VUtable[thisVUC]; if (thisEUN != BLOCK_NIL) { MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); anac = oob.u.a.ANAC + 1; nacs = oob.u.a.NACs + 1; } prev_block = inftl->VUtable[thisVUC]; if (prev_block < inftl->nb_blocks) prev_block -= inftl->firstEUN; parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0; parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0; parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0; parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0; oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC); oob.u.a.prevUnitNo = cpu_to_le16(prev_block); oob.u.a.ANAC = anac; oob.u.a.NACs = nacs; oob.u.a.parityPerField = parity; oob.u.a.discarded = 0xaa; MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); /* Also back up header... */ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); oob.u.b.prevUnitNo = cpu_to_le16(prev_block); oob.u.b.ANAC = anac; oob.u.b.NACs = nacs; oob.u.b.parityPerField = parity; oob.u.b.discarded = 0xaa; MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; inftl->VUtable[thisVUC] = writeEUN; inftl->numfreeEUNs--; return writeEUN; } while (silly2--); printk(KERN_WARNING "INFTL: error folding to make room for Virtual " "Unit Chain 0x%x\n", thisVUC); return 0xffff; }
static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) { unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); unsigned int thisEUN, writeEUN, prev_block, status; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); struct mtd_info *mtd = inftl->mbd.mtd; struct inftl_oob oob; struct inftl_bci bci; unsigned char anac, nacs, parity; size_t retlen; int silly, silly2 = 3; pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)\n", inftl, block); do { writeEUN = BLOCK_NIL; thisEUN = inftl->VUtable[thisVUC]; silly = MAX_LOOPS; while (thisEUN <= inftl->lastEUN) { inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci); status = bci.Status | bci.Status1; pr_debug("INFTL: status of block %d in EUN %d is %x\n", block , writeEUN, status); switch(status) { case SECTOR_FREE: writeEUN = thisEUN; break; case SECTOR_DELETED: case SECTOR_USED: goto hitused; case SECTOR_IGNORE: break; default: break; } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in " "Virtual Unit Chain 0x%x\n", thisVUC); return BLOCK_NIL; } thisEUN = inftl->PUtable[thisEUN]; } hitused: if (writeEUN != BLOCK_NIL) return writeEUN; writeEUN = INFTL_findfreeblock(inftl, 0); if (writeEUN == BLOCK_NIL) { thisEUN = INFTL_makefreeblock(inftl, block); pr_debug("INFTL: using desperate==1 to find free EUN " "to accommodate write to VUC %d\n", thisVUC); writeEUN = INFTL_findfreeblock(inftl, 1); if (writeEUN == BLOCK_NIL) { printk(KERN_WARNING "INFTL: cannot make free " "space.\n"); #ifdef DEBUG INFTL_dumptables(inftl); INFTL_dumpVUchains(inftl); #endif return BLOCK_NIL; } } anac = 0; nacs = 0; thisEUN = inftl->VUtable[thisVUC]; if (thisEUN != BLOCK_NIL) { inftl_read_oob(mtd, thisEUN * inftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); anac = oob.u.a.ANAC + 1; nacs = oob.u.a.NACs + 1; } prev_block = inftl->VUtable[thisVUC]; if (prev_block < inftl->nb_blocks) prev_block -= inftl->firstEUN; parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0; parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0; parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0; parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0; oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC); oob.u.a.prevUnitNo = cpu_to_le16(prev_block); oob.u.a.ANAC = anac; oob.u.a.NACs = nacs; oob.u.a.parityPerField = parity; oob.u.a.discarded = 0xaa; inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); oob.u.b.prevUnitNo = cpu_to_le16(prev_block); oob.u.b.ANAC = anac; oob.u.b.NACs = nacs; oob.u.b.parityPerField = parity; oob.u.b.discarded = 0xaa; inftl_write_oob(mtd, writeEUN * inftl->EraseSize + SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; inftl->VUtable[thisVUC] = writeEUN; inftl->numfreeEUNs--; return writeEUN; } while (silly2--); printk(KERN_WARNING "INFTL: error folding to make room for Virtual " "Unit Chain 0x%x\n", thisVUC); return BLOCK_NIL; }