static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) { unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); struct mtd_info *mtd = inftl->mbd.mtd; unsigned int status; int silly = MAX_LOOPS; size_t retlen; struct inftl_bci bci; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p," "block=%d)\n", inftl, block); while (thisEUN < inftl->nb_blocks) { if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + blockofs, 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_DELETED: thisEUN = BLOCK_NIL; goto foundit; case SECTOR_USED: goto foundit; default: printk(KERN_WARNING "INFTL: unknown status for " "block %d in EUN %d: 0x%x\n", block, thisEUN, status); break; } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in Virtual " "Unit Chain 0x%x\n", block / (inftl->EraseSize / SECTORSIZE)); return 1; } thisEUN = inftl->PUtable[thisEUN]; } foundit: if (thisEUN != BLOCK_NIL) { loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; bci.Status = bci.Status1 = SECTOR_DELETED; if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) return -EIO; INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); } 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 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; 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) { inftl_read_oob(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 BLOCK_NIL; } /* 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, BLOCK_NIL); /* * 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) { 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); /* 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; 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; }
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; }