- int isCheckpointBlock;
-
- int chunksBefore = yaffs_GetErasedChunks(dev);
- int chunksAfter;
-
- yaffs_ExtendedTags tags;
-
- yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block);
-
- yaffs_Object *object;
-
- isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT);
-
- bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
-
- T(YAFFS_TRACE_TRACING,
- (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block,
- bi->pagesInUse, bi->hasShrinkHeader));
-
- /*yaffs_VerifyFreeChunks(dev); */
-
- bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */
-
- /* Take off the number of soft deleted entries because
- * they're going to get really deleted during GC.
- */
- dev->nFreeChunks -= bi->softDeletions;
-
- dev->isDoingGC = 1;
-
- if (isCheckpointBlock ||
- !yaffs_StillSomeChunkBits(dev, block)) {
- T(YAFFS_TRACE_TRACING,
- (TSTR
- ("Collecting block %d that has no chunks in use" TENDSTR),
- block));
- yaffs_BlockBecameDirty(dev, block);
- } else {
-
- __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__);
-
- for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock;
- chunkInBlock < dev->nChunksPerBlock
- && yaffs_StillSomeChunkBits(dev, block);
- chunkInBlock++, oldChunk++) {
- if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) {
-
- /* This page is in use and might need to be copied off */
-
- markNAND = 1;
-
- yaffs_InitialiseTags(&tags);
-
- yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk,
- buffer, &tags);
-
- object =
- yaffs_FindObjectByNumber(dev,
- tags.objectId);
-
- T(YAFFS_TRACE_GC_DETAIL,
- (TSTR
- ("Collecting page %d, %d %d %d " TENDSTR),
- chunkInBlock, tags.objectId, tags.chunkId,
- tags.byteCount));
-
- if (!object) {
- T(YAFFS_TRACE_ERROR,
- (TSTR
- ("page %d in gc has no object "
- TENDSTR), oldChunk));
- }
-
- if (object && object->deleted
- && tags.chunkId != 0) {
- /* Data chunk in a deleted file, throw it away
- * It's a soft deleted data chunk,
- * No need to copy this, just forget about it and
- * fix up the object.
- */
-
- object->nDataChunks--;
-
- if (object->nDataChunks <= 0) {
- /* remeber to clean up the object */
- dev->gcCleanupList[cleanups] =
- tags.objectId;
- cleanups++;
- }
- markNAND = 0;
- } else if (0
- /* Todo object && object->deleted && object->nDataChunks == 0 */
- ) {
- /* Deleted object header with no data chunks.
- * Can be discarded and the file deleted.
- */
- object->chunkId = 0;
- yaffs_FreeTnode(object->myDev,
- object->variant.
- fileVariant.top);
- object->variant.fileVariant.top = NULL;
- yaffs_DoGenericObjectDeletion(object);
-
- } else if (object) {
- /* It's either a data chunk in a live file or
- * an ObjectHeader, so we're interested in it.
- * NB Need to keep the ObjectHeaders of deleted files
- * until the whole file has been deleted off
- */
- tags.serialNumber++;
-
- dev->nGCCopies++;
-
- if (tags.chunkId == 0) {
- /* It is an object Id,
- * We need to nuke the shrinkheader flags first
- * We no longer want the shrinkHeader flag since its work is done
- * and if it is left in place it will mess up scanning.
- * Also, clear out any shadowing stuff
- */
-
- yaffs_ObjectHeader *oh;
- oh = (yaffs_ObjectHeader *)buffer;
- oh->isShrink = 0;
- oh->shadowsObject = -1;
- tags.extraShadows = 0;
- tags.extraIsShrinkHeader = 0;
- }
-
- newChunk =
- yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1);
-
- if (newChunk < 0) {
- retVal = YAFFS_FAIL;
- } else {
-
- /* Ok, now fix up the Tnodes etc. */
-
- if (tags.chunkId == 0) {
- /* It's a header */
- object->chunkId = newChunk;
- object->serial = tags.serialNumber;
- } else {
- /* It's a data chunk */
- yaffs_PutChunkIntoFile
- (object,
- tags.chunkId,
- newChunk, 0);
- }
- }
- }
-
- yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__);
-
- }
- }
-
- yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
-
-
- /* Do any required cleanups */
- for (i = 0; i < cleanups; i++) {
- /* Time to delete the file too */
- object =
- yaffs_FindObjectByNumber(dev,
- dev->gcCleanupList[i]);
- if (object) {
- yaffs_FreeTnode(dev,
- object->variant.fileVariant.
- top);
- object->variant.fileVariant.top = NULL;
- T(YAFFS_TRACE_GC,
- (TSTR
- ("yaffs: About to finally delete object %d"
- TENDSTR), object->objectId));
- yaffs_DoGenericObjectDeletion(object);
- object->myDev->nDeletedFiles--;
- }
-
- }
-
- }
-
- if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) {
- T(YAFFS_TRACE_GC,
- (TSTR
- ("gc did not increase free chunks before %d after %d"
- TENDSTR), chunksBefore, chunksAfter));
- }
-
- dev->isDoingGC = 0;
-
- return YAFFS_OK;
-}
-
-/* New garbage collector
- * If we're very low on erased blocks then we do aggressive garbage collection
- * otherwise we do "leasurely" garbage collection.
- * Aggressive gc looks further (whole array) and will accept less dirty blocks.
- * Passive gc only inspects smaller areas and will only accept more dirty blocks.
- *
- * The idea is to help clear out space in a more spread-out manner.
- * Dunno if it really does anything useful.
- */
-static int yaffs_CheckGarbageCollection(yaffs_Device * dev)
-{
- int block;
- int aggressive;
- int gcOk = YAFFS_OK;
- int maxTries = 0;
-
- int checkpointBlockAdjust;
-
- if (dev->isDoingGC) {
- /* Bail out so we don't get recursive gc */
- return YAFFS_OK;
- }
-
- /* This loop should pass the first time.
- * We'll only see looping here if the erase of the collected block fails.
- */
-
- do {
- maxTries++;
-
- checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint);
- if(checkpointBlockAdjust < 0)
- checkpointBlockAdjust = 0;
-
- if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust)) {
- /* We need a block soon...*/
- aggressive = 1;
- } else {
- /* We're in no hurry */
- aggressive = 0;
- }
-
- block = yaffs_FindBlockForGarbageCollection(dev, aggressive);
-
- if (block > 0) {
- dev->garbageCollections++;
- if (!aggressive) {
- dev->passiveGarbageCollections++;
- }
-
- T(YAFFS_TRACE_GC,
- (TSTR
- ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),
- dev->nErasedBlocks, aggressive));
-
- gcOk = yaffs_GarbageCollectBlock(dev, block);
- }
-
- if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) {
- T(YAFFS_TRACE_GC,
- (TSTR
- ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d"
- TENDSTR), dev->nErasedBlocks, maxTries, block));
- }
- } while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0)
- && (maxTries < 2));
-
- return aggressive ? gcOk : YAFFS_OK;
-}
-
-/*------------------------- TAGS --------------------------------*/
-
-static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
- int chunkInObject)
-{
- return (tags->chunkId == chunkInObject &&
- tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0;
-
-}
-
-
-/*-------------------- Data file manipulation -----------------*/
-
-static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode,
- yaffs_ExtendedTags * tags)
-{
- /*Get the Tnode, then get the level 0 offset chunk offset */
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_ExtendedTags localTags;
- int retVal = -1;
-
- yaffs_Device *dev = in->myDev;
-
- if (!tags) {
- /* Passed a NULL, so use our own tags space */
- tags = &localTags;
- }
-
- tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode);
-
- if (tn) {
- theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
- retVal =
- yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
- chunkInInode);
- }
- return retVal;
-}
-
-static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode,
- yaffs_ExtendedTags * tags)
-{
- /* Get the Tnode, then get the level 0 offset chunk offset */
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_ExtendedTags localTags;
-
- yaffs_Device *dev = in->myDev;
- int retVal = -1;
-
- if (!tags) {
- /* Passed a NULL, so use our own tags space */
- tags = &localTags;
- }
-
- tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode);
-
- if (tn) {
-
- theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
- retVal =
- yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
- chunkInInode);
-
- /* Delete the entry in the filestructure (if found) */
- if (retVal != -1) {
- yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0);
- }
- } else {
- /*T(("No level 0 found for %d\n", chunkInInode)); */
- }
-
- if (retVal == -1) {
- /* T(("Could not find %d to delete\n",chunkInInode)); */
- }
- return retVal;
-}
-
-#ifdef YAFFS_PARANOID
-
-static int yaffs_CheckFileSanity(yaffs_Object * in)
-{
- int chunk;
- int nChunks;
- int fSize;
- int failed = 0;
- int objId;
- yaffs_Tnode *tn;
- yaffs_Tags localTags;
- yaffs_Tags *tags = &localTags;
- int theChunk;
- int chunkDeleted;
-
- if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
- /* T(("Object not a file\n")); */
- return YAFFS_FAIL;
- }
-
- objId = in->objectId;
- fSize = in->variant.fileVariant.fileSize;
- nChunks =
- (fSize + in->myDev->nBytesPerChunk - 1) / in->myDev->nBytesPerChunk;
-
- for (chunk = 1; chunk <= nChunks; chunk++) {
- tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant,
- chunk);
-
- if (tn) {
-
- theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk);
-
- if (yaffs_CheckChunkBits
- (dev, theChunk / dev->nChunksPerBlock,
- theChunk % dev->nChunksPerBlock)) {
-
- yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk,
- tags,
- &chunkDeleted);
- if (yaffs_TagsMatch
- (tags, in->objectId, chunk, chunkDeleted)) {
- /* found it; */
-
- }
- } else {
-
- failed = 1;
- }
-
- } else {
- /* T(("No level 0 found for %d\n", chunk)); */
- }
- }
-
- return failed ? YAFFS_FAIL : YAFFS_OK;
-}
-
-#endif
-
-static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode,
- int chunkInNAND, int inScan)
-{
- /* NB inScan is zero unless scanning.
- * For forward scanning, inScan is > 0;
- * for backward scanning inScan is < 0
- */
-
- yaffs_Tnode *tn;
- yaffs_Device *dev = in->myDev;
- int existingChunk;
- yaffs_ExtendedTags existingTags;
- yaffs_ExtendedTags newTags;
- unsigned existingSerial, newSerial;
-
- if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
- /* Just ignore an attempt at putting a chunk into a non-file during scanning
- * If it is not during Scanning then something went wrong!
- */
- if (!inScan) {
- T(YAFFS_TRACE_ERROR,
- (TSTR
- ("yaffs tragedy:attempt to put data chunk into a non-file"
- TENDSTR)));
- YBUG();
- }
-
- yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
- return YAFFS_OK;
- }
-
- tn = yaffs_AddOrFindLevel0Tnode(dev,
- &in->variant.fileVariant,
- chunkInInode,
- NULL);
- if (!tn) {
- return YAFFS_FAIL;
- }
-
- existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
- if (inScan != 0) {
- /* If we're scanning then we need to test for duplicates
- * NB This does not need to be efficient since it should only ever
- * happen when the power fails during a write, then only one
- * chunk should ever be affected.
- *
- * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
- * Update: For backward scanning we don't need to re-read tags so this is quite cheap.
- */
-
- if (existingChunk != 0) {
- /* NB Right now existing chunk will not be real chunkId if the device >= 32MB
- * thus we have to do a FindChunkInFile to get the real chunk id.
- *
- * We have a duplicate now we need to decide which one to use:
- *
- * Backwards scanning YAFFS2: The old one is what we use, dump the new one.
- * Forward scanning YAFFS2: The new one is what we use, dump the old one.
- * YAFFS1: Get both sets of tags and compare serial numbers.
- */
-
- if (inScan > 0) {
- /* Only do this for forward scanning */
- yaffs_ReadChunkWithTagsFromNAND(dev,
- chunkInNAND,
- NULL, &newTags);
-
- /* Do a proper find */
- existingChunk =
- yaffs_FindChunkInFile(in, chunkInInode,
- &existingTags);
- }
-
- if (existingChunk <= 0) {
- /*Hoosterman - how did this happen? */
-
- T(YAFFS_TRACE_ERROR,
- (TSTR
- ("yaffs tragedy: existing chunk < 0 in scan"
- TENDSTR)));
-
- }
-
- /* NB The deleted flags should be false, otherwise the chunks will
- * not be loaded during a scan
- */
-
- newSerial = newTags.serialNumber;
- existingSerial = existingTags.serialNumber;
-
- if ((inScan > 0) &&
- (in->myDev->isYaffs2 ||
- existingChunk <= 0 ||
- ((existingSerial + 1) & 3) == newSerial)) {
- /* Forward scanning.
- * Use new
- * Delete the old one and drop through to update the tnode
- */
- yaffs_DeleteChunk(dev, existingChunk, 1,
- __LINE__);
- } else {
- /* Backward scanning or we want to use the existing one
- * Use existing.
- * Delete the new one and return early so that the tnode isn't changed
- */
- yaffs_DeleteChunk(dev, chunkInNAND, 1,
- __LINE__);
- return YAFFS_OK;
- }
- }
-
- }
-
- if (existingChunk == 0) {
- in->nDataChunks++;
- }
-
- yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND);
-
- return YAFFS_OK;
-}
-
-static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode,
- __u8 * buffer)
-{
- int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL);
-
- if (chunkInNAND >= 0) {
- return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND,
- buffer, NULL);
- } else {
- T(YAFFS_TRACE_NANDACCESS,
- (TSTR("Chunk %d not found zero instead" TENDSTR),
- chunkInNAND));
- /* get sane (zero) data if you read a hole */
- memset(buffer, 0, in->myDev->nBytesPerChunk);
- return 0;
- }
-
-}
-
-void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn)
-{
- int block;
- int page;
- yaffs_ExtendedTags tags;
- yaffs_BlockInfo *bi;
-
- if (chunkId <= 0)
- return;
-
- dev->nDeletions++;
- block = chunkId / dev->nChunksPerBlock;
- page = chunkId % dev->nChunksPerBlock;
-
- bi = yaffs_GetBlockInfo(dev, block);
-
- T(YAFFS_TRACE_DELETION,
- (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
-
- if (markNAND &&
- bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) {
-
- yaffs_InitialiseTags(&tags);
-
- tags.chunkDeleted = 1;