/* * Maximum number of loops while examining next block, to have a * chance to detect consistency problems (they should never happen * because of the checks done in the mounting.
*/ #define MAX_LOOPS 10000
if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX) return; /* OK, this is moderately ugly. But probably safe. Alternatives? */ if (memcmp(mtd->name, "DiskOnChip", 10)) return;
if (!mtd->_block_isbad) {
printk(KERN_ERR "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" "Please use the new diskonchip driver under the NAND subsystem.\n"); return;
}
pr_debug("INFTL: add_mtd for %s\n", mtd->name);
inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
if (!inftl) return;
inftl->mbd.mtd = mtd;
inftl->mbd.devnum = -1;
inftl->mbd.tr = tr;
if (INFTL_mount(inftl) < 0) {
printk(KERN_WARNING "INFTL: could not mount device\n");
kfree(inftl); return;
}
/* OK, it's a new one. Set up all the data structures. */
/* * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. * This function is used when the give Virtual Unit Chain.
*/ static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
{
u16 pot = inftl->LastFreeEUN; int silly = inftl->nb_blocks;
/* * Normally, we force a fold to happen before we run out of free * blocks completely.
*/ if (!desperate && inftl->numfreeEUNs < 2) {
pr_debug("INFTL: there are too few free EUNs (%d)\n",
inftl->numfreeEUNs); return BLOCK_NIL;
}
/* Scan for a free block */ do { if (inftl->PUtable[pot] == BLOCK_FREE) {
inftl->LastFreeEUN = pot; return pot;
}
if (++pot > inftl->lastEUN)
pot = 0;
if (!silly--) {
printk(KERN_WARNING "INFTL: no free blocks found! " "EUN range = %d - %d\n", 0, inftl->LastFreeEUN); return BLOCK_NIL;
}
} while (pot != inftl->LastFreeEUN);
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.
*/
pr_debug("INFTL: folding chain %d into unit %d\n", thisVUC, targetEUN);
for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) { unsignedchar 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 && !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;
/* * 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 * is relatively simple to clean up the mess).
*/
pr_debug("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;
}
static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, 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;
while (EUN <= inftl->lastEUN) {
thislen++;
EUN = inftl->PUtable[EUN]; if (thislen > 0xff00) {
printk(KERN_WARNING "INFTL: endless loop in " "Virtual Chain %d: Unit %x\n",
chain, EUN); /* * Actually, don't return failure. * Just ignore this chain and get on with it.
*/
thislen = 0; break;
}
}
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) { if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
blockofs, 8, &retlen, (char *)&bci) < 0)
status = SECTOR_IGNORE; else
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: /* 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);
/* * Hopefully we free something, lets try again. * This time we are desperate...
*/
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) { /* * 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;
}
}
printk(KERN_WARNING "INFTL: error folding to make room for Virtual " "Unit Chain 0x%x\n", thisVUC); return BLOCK_NIL;
}
/* * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
*/ staticvoid INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
{ struct mtd_info *mtd = inftl->mbd.mtd; unsignedchar BlockUsed[MAX_SECTORS_PER_UNIT]; unsignedchar BlockDeleted[MAX_SECTORS_PER_UNIT]; unsignedint thisEUN, status; int block, silly; struct inftl_bci bci;
size_t retlen;
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.
*/
pr_debug("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) {
pr_debug("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);
pr_debug("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_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);
}
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 && !mtd_is_bitflip(ret)) return -EIO;
} return 0;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Ungerer , David Woodhouse , Fabrice Bellard et al.");
MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.