/* maximum number of loops while examining next block, to have a chancetodetectconsistencyproblems(theyshouldneverhappen
because of the checks done in the mounting */
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;
/* Actual NFTL access routines */ /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used *whenthegiveVirtualUnitChain
*/ static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
{ /* For a given Virtual Unit Chain: find or create a free block and
add it to the chain */ /* We're passed the number of the last EUN in the chain, to save us from
having to look it up again */
u16 pot = nftl->LastFreeEUN; int silly = nftl->nb_blocks;
/* Normally, we force a fold to happen before we run out of free blocks completely */ if (!desperate && nftl->numfreeEUNs < 2) {
pr_debug("NFTL_findfreeblock: there are too few free EUNs\n"); return BLOCK_NIL;
}
/* Scan for a free block */ do { if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
nftl->LastFreeEUN = pot;
nftl->numfreeEUNs--; return pot;
}
/* This will probably point to the MediaHdr unit itself, rightatthebeginningofthepartition.Butthatunit (andthebackupunittoo)shouldhavetheUCIset
up so that it's not selected for overwriting */ if (++pot > nftl->lastEUN)
pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
if (!silly--) {
printk("Argh! No free blocks found! LastFreeEUN = %d, " "FirstEUN = %d\n", nftl->LastFreeEUN,
le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN)); return BLOCK_NIL;
}
} while (pot != nftl->LastFreeEUN);
if (thisEUN == BLOCK_NIL) {
printk(KERN_WARNING "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-byteblockwithintheChain.
*/
silly = MAX_LOOPS;
targetEUN = BLOCK_NIL; while (thisEUN <= nftl->lastEUN ) { unsignedint status, foldmark;
targetEUN = thisEUN; for (block = 0; block < nftl->EraseSize / 512; block ++) {
nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
(block * 512), 16 , &retlen,
(char *)&oob); if (block == 2) {
foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; if (foldmark == FOLD_MARK_IN_PROGRESS) {
pr_debug("Write Inhibited on EUN %d\n", thisEUN);
inplace = 0;
} else { /* There's no other reason not to do inplace, exceptonesthatcomelater.Sowedon'tneed
to preserve inplace */
inplace = 1;
}
}
status = oob.b.Status | oob.b.Status1;
BlockLastState[block] = status;
switch(status) { case SECTOR_FREE:
BlockFreeFound[block] = 1; break;
case SECTOR_USED: if (!BlockFreeFound[block])
BlockMap[block] = thisEUN; else
printk(KERN_WARNING "SECTOR_USED found after SECTOR_FREE " "in Virtual Unit Chain %d for block %d\n",
thisVUC, block); break; case SECTOR_DELETED: if (!BlockFreeFound[block])
BlockMap[block] = BLOCK_NIL; else
printk(KERN_WARNING "SECTOR_DELETED found after SECTOR_FREE " "in Virtual Unit Chain %d for block %d\n",
thisVUC, block); break;
case SECTOR_IGNORE: break; default:
printk("Unknown status for block %d in EUN %d: %x\n",
block, thisEUN, status);
}
}
if (!silly--) {
printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
thisVUC); return BLOCK_NIL;
}
thisEUN = nftl->ReplUnitTable[thisEUN];
}
if (inplace) { /* We're being asked to be a fold-in-place. Check thatallblockswhichactuallyhavedataassociated withthem(i.e.BlockMap[block]!=BLOCK_NIL)are eitheralreadypresentorSECTOR_FREEinthetarget block.Ifnot,we'regoingtohavetofoldout-of-place anyway.
*/ for (block = 0; block < nftl->EraseSize / 512 ; block++) { if (BlockLastState[block] != SECTOR_FREE &&
BlockMap[block] != BLOCK_NIL &&
BlockMap[block] != targetEUN) {
pr_debug("Setting inplace to 0. VUC %d, " "block %d was %x lastEUN, " "and is in EUN %d (%s) %d\n",
thisVUC, block, BlockLastState[block],
BlockMap[block],
BlockMap[block]== targetEUN ? "==" : "!=",
targetEUN);
inplace = 0; break;
}
}
if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
SECTOR_FREE) {
pr_debug("Pending write not free in EUN %d. " "Folding out of place.\n", targetEUN);
inplace = 0;
}
}
if (!inplace) {
pr_debug("Cannot fold Virtual Unit Chain %d in place. " "Trying out-of-place\n", thisVUC); /* We need to find a targetEUN to fold into. */
targetEUN = NFTL_findfreeblock(nftl, 1); if (targetEUN == BLOCK_NIL) { /* Ouch. Now we're screwed. We need to do a fold-in-placeofanotherchaintomakeroom forthisone.Weneedabetterwayofselecting whichchaintofold,becausemakefreeblockwill onlyaskustofoldthesameoneagain.
*/
printk(KERN_WARNING "NFTL_findfreeblock(desperate) returns 0xffff.\n"); return BLOCK_NIL;
}
} else { /* We put a fold mark in the chain we are folding only if we foldinplacetohelpthemountcheckcode.Ifwedonotfoldin place,itispossibletofindthevalidchainbyselectingthe
longer one */
oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
oob.u.c.unused = 0xffffffff;
nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 8, &retlen, (char *)&oob.u);
}
/* OK. We now know the location of every block in the Virtual Unit Chain, andtheEraseUnitintowhichwearesupposedtobecopying. Goforit.
*/
pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN); for (block = 0; block < nftl->EraseSize / 512 ; block++) { /* If it's in the target EUN already, or if it's pending write, do nothing */ if (BlockMap[block] == targetEUN ||
(pendingblock == (thisVUC * (nftl->EraseSize / 512) + 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;
/* add the header so that it is now a valid chain */
oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
/* At this point, we have two different chains for this Virtual Unit, and no way to tell themapart.Ifwecrashnow,wegetconfused.However,bothcontainthesamedata,sowe shouldn'tactuallylosedatainthiscase.It'sjustthatwhenweloaduponamediumwhich hasduplicatechains,weneedtofreeoneofthechainsbecauseit'snotnecessaryanymore.
*/
thisEUN = nftl->EUNtable[thisVUC];
pr_debug("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) { unsignedint EUNtmp;
EUNtmp = nftl->ReplUnitTable[thisEUN];
if (NFTL_formatblock(nftl, thisEUN) < 0) { /* could not erase : mark block as reserved
*/
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;
}
static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
{ /* This is the part that needs some cleverness applied. Fornow,I'mdoingtheminimumapplicabletoactually getthethingtowork. Wear-levellingandothercleverstuffneedstobeimplemented andwealsoneedtodosomeassessmentoftheresultswhen thesystemlosespowerhalf-waythroughtheroutine.
*/
u16 LongestChain = 0;
u16 ChainLength = 0, thislen;
u16 chain, EUN;
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 BLOCK_NIL;
}
if (writeEUN == BLOCK_NIL) { /* OK, we accept that the above comment is lying-theremayhavebeenfreeblocks lasttimewecalledNFTL_findfreeblock(), buttheyarereservedforwhenwe're desperate.Well,nowwe'redesperate.
*/
pr_debug("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 alwaysbeabletomakesomeroomsomehow. Ifwegethere,we'veallocatedmorestorage spacethanactualmedia,orourmakefreeblock routineismissingsomething.
*/
printk(KERN_WARNING "Cannot make free space.\n"); return BLOCK_NIL;
} //printk("Restarting scan\n"); 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 */
nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
&retlen, (char *)&oob.u);
/* we link the new block to the chain only after the blockisready.Itavoidsthecasewherethechain
could point to a free block */ if (lastEUN != BLOCK_NIL) { /* Both in our cache... */
nftl->ReplUnitTable[lastEUN] = writeEUN; /* ... and on the flash itself */
nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u);
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 */ return1;
}
if (thisEUN != BLOCK_NIL) { while (thisEUN < nftl->nb_blocks) { if (nftl_read_oob(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)); return1;
}
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; int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
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");
MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.22Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
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.