📄 inftlcore.c
字号:
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 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) { 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 0xffff;}/* * 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 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;}static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, char *buffer){ struct INFTLrecord *inftl = (void *)mbd; unsigned int writeEUN; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); size_t retlen; struct inftl_oob oob; char *p, *pend; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld," "buffer=%p)\n", inftl, block, buffer); /* Is block all zero? */ pend = buffer + SECTORSIZE; for (p = buffer; p < pend && !*p; p++) ; if (p < pend) { writeEUN = INFTL_findwriteunit(inftl, block); if (writeEUN == BLOCK_NIL) { printk(KERN_WARNING "inftl_writeblock(): cannot find " "block to write to\n"); /* * If we _still_ haven't got a block to use, * we're screwed. */ return 1; } memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + blockofs, SECTORSIZE, &retlen, (char *)buffer, (char *)&oob); /* * need to write SECTOR_USED flags since they are not written * in mtd_writeecc */ } else { INFTL_deleteblock(inftl, block); } return 0;}static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, char *buffer){ struct INFTLrecord *inftl = (void *)mbd; 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; struct inftl_bci bci; size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld," "buffer=%p)\n", inftl, block, buffer); 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_DELETED: thisEUN = BLOCK_NIL; goto foundit; case SECTOR_USED: goto foundit; case SECTOR_FREE: case SECTOR_IGNORE: break; default: printk(KERN_WARNING "INFTL: unknown status for " "block %ld in EUN %d: 0x%04x\n", block, thisEUN, status); break; } if (!silly--) { printk(KERN_WARNING "INFTL: infinite loop in " "Virtual Unit Chain 0x%lx\n", block / (inftl->EraseSize / SECTORSIZE)); return 1; } thisEUN = inftl->PUtable[thisEUN]; }foundit: if (thisEUN == BLOCK_NIL) { /* The requested block is not on the media, return all 0x00 */ memset(buffer, 0, SECTORSIZE); } else { size_t retlen; loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer); /* Handle corrected bit flips gracefully */ if (ret < 0 && ret != -EUCLEAN) return -EIO; } return 0;}static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo){ struct INFTLrecord *inftl = (void *)dev; geo->heads = inftl->heads; geo->sectors = inftl->sectors; geo->cylinders = inftl->cylinders; return 0;}static struct mtd_blktrans_ops inftl_tr = { .name = "inftl", .major = INFTL_MAJOR, .part_bits = INFTL_PARTN_BITS, .blksize = 512, .getgeo = inftl_getgeo, .readsect = inftl_readblock, .writesect = inftl_writeblock, .add_mtd = inftl_add_mtd, .remove_dev = inftl_remove_dev, .owner = THIS_MODULE,};static int __init init_inftl(void){ return register_mtd_blktrans(&inftl_tr);}static void __exit cleanup_inftl(void){ deregister_mtd_blktrans(&inftl_tr);}module_init(init_inftl);module_exit(cleanup_inftl);MODULE_LICENSE("GPL");MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -