📄 nftlcore.c
字号:
shouldn't actually lose data in this case. It's just that when we load up on a medium which has duplicate chains, we need to free one of the chains because it's not necessary any more. */ thisEUN = nftl->EUNtable[thisVUC]; DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n"); /* For each block in the old chain (except the targetEUN of course), free it and make it available for future use */ while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) { unsigned int EUNtmp; EUNtmp = nftl->ReplUnitTable[thisEUN]; if (NFTL_formatblock(nftl, thisEUN) < 0) { /* could not erase : mark block as reserved * FixMe: Update Bad Unit Table on disk */ nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; } else { /* correctly erased : mark it as free */ nftl->ReplUnitTable[thisEUN] = BLOCK_FREE; nftl->numfreeEUNs++; } thisEUN = EUNtmp; } /* Make this the new start of chain for thisVUC */ nftl->ReplUnitTable[targetEUN] = BLOCK_NIL; nftl->EUNtable[thisVUC] = targetEUN; return targetEUN;}u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock){ /* This is the part that needs some cleverness applied. For now, I'm doing the minimum applicable to actually get the thing to work. Wear-levelling and other clever stuff needs to be implemented and we also need to do some assessment of the results when the system loses power half-way through the routine. */ u16 LongestChain = 0; u16 ChainLength = 0, thislen; u16 chain, EUN; for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) { EUN = nftl->EUNtable[chain]; thislen = 0; while (EUN <= nftl->lastEUN) { thislen++; //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); EUN = nftl->ReplUnitTable[EUN] & 0x7fff; if (thislen > 0xff00) { printk("Endless loop in Virtual Chain %d: Unit %x\n", chain, EUN); } if (thislen > 0xff10) { /* Actually, don't return failure. Just ignore this chain and get on with it. */ thislen = 0; break; } } if (thislen > ChainLength) { //printk("New longest chain is %d with length %d\n", chain, thislen); ChainLength = thislen; LongestChain = chain; } } if (ChainLength < 2) { printk(KERN_WARNING "No Virtual Unit Chains available for folding. " "Failing request\n"); return 0xffff; } return NFTL_foldchain (nftl, LongestChain, pendingblock);}/* NFTL_findwriteunit: Return the unit number into which we can write for this block. Make it available if it isn't already*/static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block){ u16 lastEUN; u16 thisVUC = block / (nftl->EraseSize / 512); unsigned int writeEUN; unsigned long blockofs = (block * 512) & (nftl->EraseSize -1); size_t retlen; int silly, silly2 = 3; struct nftl_oob oob; do { /* Scan the media to find a unit in the VUC which has a free space for the block in question. */ /* This condition catches the 0x[7f]fff cases, as well as being a sanity check for past-end-of-media access */ lastEUN = BLOCK_NIL; writeEUN = nftl->EUNtable[thisVUC]; silly = MAX_LOOPS; while (writeEUN <= nftl->lastEUN) { struct nftl_bci bci; size_t retlen; unsigned int status; lastEUN = writeEUN; MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci); DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status)); status = bci.Status | bci.Status1; switch(status) { case SECTOR_FREE: return writeEUN; case SECTOR_DELETED: case SECTOR_USED: case SECTOR_IGNORE: break; default: // Invalid block. Don't use it any more. Must implement. break; } if (!silly--) { printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC); return 0xffff; } /* Skip to next block in chain */ writeEUN = nftl->ReplUnitTable[writeEUN]; } /* OK. We didn't find one in the existing chain, or there is no existing chain. */ /* Try to find an already-free block */ writeEUN = NFTL_findfreeblock(nftl, 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. */ /* First remember the start of this chain */ //u16 startEUN = nftl->EUNtable[thisVUC]; //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); writeEUN = NFTL_makefreeblock(nftl, 0xffff); if (writeEUN == BLOCK_NIL) { /* OK, we accept that the above comment is lying - there may have been free blocks last time we called NFTL_findfreeblock(), but they are reserved for when we're desperate. Well, now we're desperate. */ DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC); writeEUN = NFTL_findfreeblock(nftl, 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 "Cannot make free space.\n"); return BLOCK_NIL; } //printk("Restarting scan\n"); lastEUN = BLOCK_NIL; continue; } /* We've found a free block. Insert it into the chain. */ if (lastEUN != BLOCK_NIL) { thisVUC |= 0x8000; /* It's a replacement block */ } else { /* The first block in a new chain */ nftl->EUNtable[thisVUC] = writeEUN; } /* set up the actual EUN we're writing into */ /* Both in our cache... */ nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; /* ... and on the flash itself */ MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); /* we link the new block to the chain only after the block is ready. It avoids the case where the chain could point to a free block */ if (lastEUN != BLOCK_NIL) { /* Both in our cache... */ nftl->ReplUnitTable[lastEUN] = writeEUN; /* ... and on the flash itself */ MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u); oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN); MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u); } return writeEUN; } while (silly2--); printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", thisVUC); return 0xffff;}static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, char *buffer){ struct NFTLrecord *nftl = (void *)mbd; u16 writeEUN; unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); size_t retlen; u8 eccbuf[6]; writeEUN = NFTL_findwriteunit(nftl, block); if (writeEUN == BLOCK_NIL) { printk(KERN_WARNING "NFTL_writeblock(): Cannot find block to write to\n"); /* If we _still_ haven't got a block to use, we're screwed */ return 1; } MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP); /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ return 0;}#endif /* CONFIG_NFTL_RW */static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, char *buffer){ struct NFTLrecord *nftl = (void *)mbd; u16 lastgoodEUN; u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); unsigned int status; int silly = MAX_LOOPS; size_t retlen; struct nftl_bci bci; lastgoodEUN = BLOCK_NIL; if (thisEUN != BLOCK_NIL) { while (thisEUN < nftl->nb_blocks) { if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else status = bci.Status | bci.Status1; switch (status) { case SECTOR_FREE: /* no modification of a sector should follow a free sector */ goto the_end; case SECTOR_DELETED: lastgoodEUN = BLOCK_NIL; break; case SECTOR_USED: lastgoodEUN = thisEUN; break; case SECTOR_IGNORE: break; default: printk("Unknown status for block %ld in EUN %d: %x\n", block, thisEUN, status); break; } if (!silly--) { printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", block / (nftl->EraseSize / 512)); return 1; } thisEUN = nftl->ReplUnitTable[thisEUN]; } } the_end: if (lastgoodEUN == BLOCK_NIL) { /* the requested block is not on the media, return all 0x00 */ memset(buffer, 0, 512); } else { loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; size_t retlen; u_char eccbuf[6]; if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP)) return -EIO; } return 0;}static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo){ struct NFTLrecord *nftl = (void *)dev; geo->heads = nftl->heads; geo->sectors = nftl->sectors; geo->cylinders = nftl->cylinders; return 0;}/**************************************************************************** * * Module stuff * ****************************************************************************/struct mtd_blktrans_ops nftl_tr = { .name = "nftl", .major = NFTL_MAJOR, .part_bits = NFTL_PARTN_BITS, .getgeo = nftl_getgeo, .readsect = nftl_readblock,#ifdef CONFIG_NFTL_RW .writesect = nftl_writeblock,#endif .add_mtd = nftl_add_mtd, .remove_dev = nftl_remove_dev, .owner = THIS_MODULE,};extern char nftlmountrev[];int __init init_nftl(void){ printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev); return register_mtd_blktrans(&nftl_tr);}static void __exit cleanup_nftl(void){ deregister_mtd_blktrans(&nftl_tr);}module_init(init_nftl);module_exit(cleanup_nftl);MODULE_LICENSE("GPL");MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -