+ dev->gcDisable=0;
+ }
+
+ result = yaffs_ChangeObjectName(obj, newDir, newName, 1, 0);
+
+ yaffs_UpdateParent(oldDir);
+ if(newDir != oldDir)
+ yaffs_UpdateParent(newDir);
+
+ return result;
+ }
+ return YAFFS_FAIL;
+}
+
+/*------------------------- Block Management and Page Allocation ----------------*/
+
+static int yaffs_InitialiseBlocks(yaffs_Device *dev)
+{
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+
+ dev->blockInfo = NULL;
+ dev->chunkBits = NULL;
+
+ dev->allocationBlock = -1; /* force it to get a new one */
+
+ /* If the first allocation strategy fails, thry the alternate one */
+ dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
+ if (!dev->blockInfo) {
+ dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo));
+ dev->blockInfoAlt = 1;
+ } else
+ dev->blockInfoAlt = 0;
+
+ if (dev->blockInfo) {
+ /* Set up dynamic blockinfo stuff. */
+ dev->chunkBitmapStride = (dev->param.nChunksPerBlock + 7) / 8; /* round up bytes */
+ dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
+ if (!dev->chunkBits) {
+ dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks);
+ dev->chunkBitsAlt = 1;
+ } else
+ dev->chunkBitsAlt = 0;
+ }
+
+ if (dev->blockInfo && dev->chunkBits) {
+ memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo));
+ memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks);
+ return YAFFS_OK;
+ }
+
+ return YAFFS_FAIL;
+}
+
+static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
+{
+ if (dev->blockInfoAlt && dev->blockInfo)
+ YFREE_ALT(dev->blockInfo);
+ else if (dev->blockInfo)
+ YFREE(dev->blockInfo);
+
+ dev->blockInfoAlt = 0;
+
+ dev->blockInfo = NULL;
+
+ if (dev->chunkBitsAlt && dev->chunkBits)
+ YFREE_ALT(dev->chunkBits);
+ else if (dev->chunkBits)
+ YFREE(dev->chunkBits);
+ dev->chunkBitsAlt = 0;
+ dev->chunkBits = NULL;
+}
+
+static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev,
+ yaffs_BlockInfo *bi)
+{
+
+ if (!dev->param.isYaffs2)
+ return 1; /* disqualification only applies to yaffs2. */
+
+ if (!bi->hasShrinkHeader)
+ return 1; /* can gc */
+
+ yaffs_FindOldestDirtySequence(dev);
+
+ /* Can't do gc of this block if there are any blocks older than this one that have
+ * discarded pages.
+ */
+ return (bi->sequenceNumber <= dev->oldestDirtySequence);
+}
+
+/*
+ * yaffs_FindRefreshBlock()
+ * periodically finds the oldest full block by sequence number for refreshing.
+ * Only for yaffs2.
+ */
+static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
+{
+ __u32 b ;
+
+ __u32 oldest = 0;
+ __u32 oldestSequence = 0;
+
+ yaffs_BlockInfo *bi;
+
+ /*
+ * If refresh period < 10 then refreshing is disabled.
+ */
+ if(dev->param.refreshPeriod < 10 ||
+ !dev->param.isYaffs2)
+ return oldest;
+
+ /*
+ * Fix broken values.
+ */
+ if(dev->refreshSkip > dev->param.refreshPeriod)
+ dev->refreshSkip = dev->param.refreshPeriod;
+
+ if(dev->refreshSkip > 0)
+ return oldest;
+
+ /*
+ * Refresh skip is now zero.
+ * We'll do a refresh this time around....
+ * Update the refresh skip and find the oldest block.
+ */
+ dev->refreshSkip = dev->param.refreshPeriod;
+ dev->refreshCount++;
+ bi = dev->blockInfo;
+ for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){
+
+ if (bi->blockState == YAFFS_BLOCK_STATE_FULL){
+
+ if(oldest < 1 ||
+ bi->sequenceNumber < oldestSequence){
+ oldest = b;
+ oldestSequence = bi->sequenceNumber;
+ }
+ }
+ bi++;
+ }
+
+ if (oldest > 0) {
+ T(YAFFS_TRACE_GC,
+ (TSTR("GC refresh count %d selected block %d with sequenceNumber %d" TENDSTR),
+ dev->refreshCount, oldest, oldestSequence));
+ }
+
+ return oldest;
+}
+
+
+static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo)
+{
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo);
+
+ int erasedOk = 0;
+
+ /* If the block is still healthy erase it and mark as clean.
+ * If the block has had a data failure, then retire it.
+ */
+
+ T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
+ (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR),
+ blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : ""));
+
+ yaffs_ClearOldestDirtySequence(dev,bi);
+
+ bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
+
+ /* If this is the block being garbage collected then stop gc'ing this block */
+ if(blockNo == dev->gcBlock)
+ dev->gcBlock = 0;
+
+ /* If this block is currently the best candidate for gc then drop as a candidate */
+ if(blockNo == dev->gcDirtiest){
+ dev->gcDirtiest = 0;
+ dev->gcPagesInUse = 0;
+ }
+
+ if (!bi->needsRetiring) {
+ yaffs_InvalidateCheckpoint(dev);
+ erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
+ if (!erasedOk) {
+ dev->nErasureFailures++;
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ (TSTR("**>> Erasure failed %d" TENDSTR), blockNo));
+ }
+ }
+
+ if (erasedOk &&
+ ((yaffs_traceMask & YAFFS_TRACE_ERASE) || !yaffs_SkipVerification(dev))) {
+ int i;
+ for (i = 0; i < dev->param.nChunksPerBlock; i++) {
+ if (!yaffs_CheckChunkErased
+ (dev, blockNo * dev->param.nChunksPerBlock + i)) {
+ T(YAFFS_TRACE_ERROR,
+ (TSTR
+ (">>Block %d erasure supposedly OK, but chunk %d not erased"
+ TENDSTR), blockNo, i));
+ }
+ }
+ }
+
+ if (erasedOk) {
+ /* Clean it up... */
+ bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+ bi->sequenceNumber = 0;
+ dev->nErasedBlocks++;
+ bi->pagesInUse = 0;
+ bi->softDeletions = 0;
+ bi->hasShrinkHeader = 0;
+ bi->skipErasedCheck = 1; /* This is clean, so no need to check */
+ bi->gcPrioritise = 0;
+ yaffs_ClearChunkBits(dev, blockNo);
+
+ T(YAFFS_TRACE_ERASE,
+ (TSTR("Erased block %d" TENDSTR), blockNo));
+ } else {
+ dev->nFreeChunks -= dev->param.nChunksPerBlock; /* We lost a block of free space */
+
+ yaffs_RetireBlock(dev, blockNo);
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ (TSTR("**>> Block %d retired" TENDSTR), blockNo));
+ }
+}
+
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
+{
+ int i;
+
+ yaffs_BlockInfo *bi;
+
+ if (dev->nErasedBlocks < 1) {
+ /* Hoosterman we've got a problem.
+ * Can't get space to gc
+ */
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("yaffs tragedy: no more erased blocks" TENDSTR)));
+
+ return -1;
+ }
+
+ /* Find an empty block. */
+
+ for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
+ dev->allocationBlockFinder++;
+ if (dev->allocationBlockFinder < dev->internalStartBlock
+ || dev->allocationBlockFinder > dev->internalEndBlock) {
+ dev->allocationBlockFinder = dev->internalStartBlock;
+ }
+
+ bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder);
+
+ if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
+ bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->sequenceNumber++;
+ bi->sequenceNumber = dev->sequenceNumber;
+ dev->nErasedBlocks--;
+ T(YAFFS_TRACE_ALLOCATE,
+ (TSTR("Allocated block %d, seq %d, %d left" TENDSTR),
+ dev->allocationBlockFinder, dev->sequenceNumber,
+ dev->nErasedBlocks));
+ return dev->allocationBlockFinder;
+ }
+ }
+
+ T(YAFFS_TRACE_ALWAYS,
+ (TSTR
+ ("yaffs tragedy: no more erased blocks, but there should have been %d"
+ TENDSTR), dev->nErasedBlocks));
+
+ return -1;
+}
+
+
+
+static int yaffs_CalcCheckpointBlocksRequired(yaffs_Device *dev)
+{
+ if (!dev->nCheckpointBlocksRequired &&
+ dev->param.isYaffs2) {
+ /* Not a valid value so recalculate */
+ int nBytes = 0;
+ int nBlocks;
+ int devBlocks = (dev->param.endBlock - dev->param.startBlock + 1);
+ int tnodeSize = yaffs_CalcTnodeSize(dev);
+
+ nBytes += sizeof(yaffs_CheckpointValidity);
+ nBytes += sizeof(yaffs_CheckpointDevice);
+ nBytes += devBlocks * sizeof(yaffs_BlockInfo);
+ nBytes += devBlocks * dev->chunkBitmapStride;
+ nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjectsCreated - dev->nFreeObjects);
+ nBytes += (tnodeSize + sizeof(__u32)) * (dev->nTnodesCreated - dev->nFreeTnodes);
+ nBytes += sizeof(yaffs_CheckpointValidity);
+ nBytes += sizeof(__u32); /* checksum*/
+
+ /* Round up and add 2 blocks to allow for some bad blocks, so add 3 */
+
+ nBlocks = (nBytes/(dev->nDataBytesPerChunk * dev->param.nChunksPerBlock)) + 3;
+
+ dev->nCheckpointBlocksRequired = nBlocks;
+ }
+
+ return dev->nCheckpointBlocksRequired;
+}
+
+/*
+ * Check if there's space to allocate...
+ * Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()?
+ */
+static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev, int nChunks)
+{
+ int reservedChunks;
+ int reservedBlocks = dev->param.nReservedBlocks;
+ int checkpointBlocks;
+
+ if (dev->param.isYaffs2) {
+ checkpointBlocks = yaffs_CalcCheckpointBlocksRequired(dev) -
+ dev->blocksInCheckpoint;
+ if (checkpointBlocks < 0)
+ checkpointBlocks = 0;
+ } else {
+ checkpointBlocks = 0;
+ }
+
+ reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->param.nChunksPerBlock);
+
+ return (dev->nFreeChunks > (reservedChunks + nChunks));
+}
+
+static int yaffs_AllocateChunk(yaffs_Device *dev, int useReserve,
+ yaffs_BlockInfo **blockUsedPtr)
+{
+ int retVal;
+ yaffs_BlockInfo *bi;
+
+ if (dev->allocationBlock < 0) {
+ /* Get next block to allocate off */
+ dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
+ dev->allocationPage = 0;
+ }
+
+ if (!useReserve && !yaffs_CheckSpaceForAllocation(dev, 1)) {
+ /* Not enough space to allocate unless we're allowed to use the reserve. */
+ return -1;
+ }
+
+ if (dev->nErasedBlocks < dev->param.nReservedBlocks
+ && dev->allocationPage == 0) {
+ T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR)));
+ }
+
+ /* Next page please.... */
+ if (dev->allocationBlock >= 0) {
+ bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
+
+ retVal = (dev->allocationBlock * dev->param.nChunksPerBlock) +
+ dev->allocationPage;
+ bi->pagesInUse++;
+ yaffs_SetChunkBit(dev, dev->allocationBlock,
+ dev->allocationPage);
+
+ dev->allocationPage++;
+
+ dev->nFreeChunks--;
+
+ /* If the block is full set the state to full */
+ if (dev->allocationPage >= dev->param.nChunksPerBlock) {
+ bi->blockState = YAFFS_BLOCK_STATE_FULL;
+ dev->allocationBlock = -1;
+ }
+
+ if (blockUsedPtr)
+ *blockUsedPtr = bi;
+
+ return retVal;
+ }
+
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
+
+ return -1;
+}
+
+static int yaffs_GetErasedChunks(yaffs_Device *dev)
+{
+ int n;
+
+ n = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+
+ if (dev->allocationBlock > 0)
+ n += (dev->param.nChunksPerBlock - dev->allocationPage);
+
+ return n;
+
+}
+
+/*
+ * yaffs_SkipRestOfBlock() skips over the rest of the allocation block
+ * if we don't want to write to it.
+ */
+static void yaffs_SkipRestOfBlock(yaffs_Device *dev)
+{
+ if(dev->allocationBlock > 0){
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
+ if(bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING){
+ bi->blockState = YAFFS_BLOCK_STATE_FULL;
+ dev->allocationBlock = -1;
+ }
+ }
+}
+
+
+static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
+ int wholeBlock)
+{
+ int oldChunk;
+ int newChunk;
+ int markNAND;
+ int retVal = YAFFS_OK;
+ int cleanups = 0;
+ int i;
+ int isCheckpointBlock;
+ int matchingChunk;
+ int maxCopies;
+
+ 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);
+
+
+ T(YAFFS_TRACE_TRACING,
+ (TSTR("Collecting block %d, in use %d, shrink %d, wholeBlock %d" TENDSTR),
+ block,
+ bi->pagesInUse,
+ bi->hasShrinkHeader,
+ wholeBlock));
+
+ /*yaffs_VerifyFreeChunks(dev); */
+
+ if(bi->blockState == YAFFS_BLOCK_STATE_FULL)
+ bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
+
+ bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */
+
+ dev->gcDisable = 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__);
+
+ yaffs_VerifyBlock(dev, bi, block);
+
+ maxCopies = (wholeBlock) ? dev->param.nChunksPerBlock : 5;
+ oldChunk = block * dev->param.nChunksPerBlock + dev->gcChunk;
+
+ for (/* init already done */;
+ retVal == YAFFS_OK &&
+ dev->gcChunk < dev->param.nChunksPerBlock &&
+ (bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) &&
+ maxCopies > 0;
+ dev->gcChunk++, oldChunk++) {
+ if (yaffs_CheckChunkBit(dev, block, dev->gcChunk)) {
+
+ /* This page is in use and might need to be copied off */
+
+ maxCopies--;
+
+ markNAND = 1;
+
+ yaffs_InitialiseTags(&tags);
+
+ yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk,
+ buffer, &tags);
+
+ object =
+ yaffs_FindObjectByNumber(dev,
+ tags.objectId);
+
+ T(YAFFS_TRACE_GC_DETAIL,
+ (TSTR
+ ("Collecting chunk in block %d, %d %d %d " TENDSTR),
+ dev->gcChunk, tags.objectId, tags.chunkId,
+ tags.byteCount));
+
+ if (object && !yaffs_SkipVerification(dev)) {
+ if (tags.chunkId == 0)
+ matchingChunk = object->hdrChunk;
+ else if (object->softDeleted)
+ matchingChunk = oldChunk; /* Defeat the test */
+ else
+ matchingChunk = yaffs_FindChunkInFile(object, tags.chunkId, NULL);
+
+ if (oldChunk != matchingChunk)
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("gc: page in gc mismatch: %d %d %d %d"TENDSTR),
+ oldChunk, matchingChunk, tags.objectId, tags.chunkId));
+
+ }
+
+ if (!object) {
+ T(YAFFS_TRACE_ERROR,
+ (TSTR
+ ("page %d in gc has no object: %d %d %d "
+ TENDSTR), oldChunk,
+ tags.objectId, tags.chunkId, tags.byteCount));
+ }
+
+ if (object &&
+ object->deleted &&
+ object->softDeleted &&
+ tags.chunkId != 0) {
+ /* Data chunk in a soft 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.
+ */
+
+ /* Free chunks already includes softdeleted chunks.
+ * How ever this chunk is going to soon be really deleted
+ * which will increment free chunks.
+ * We have to decrement free chunks so this works out properly.
+ */
+ dev->nFreeChunks--;
+
+ 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->hdrChunk = 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
+ * Also need to clean up shadowing.
+ * We no longer want the shrinkHeader flag since its work is done
+ * and if it is left in place it will mess up scanning.
+ */
+
+ yaffs_ObjectHeader *oh;
+ oh = (yaffs_ObjectHeader *)buffer;
+
+ oh->isShrink = 0;
+ tags.extraIsShrinkHeader = 0;
+
+ oh->shadowsObject = 0;
+ oh->inbandShadowsObject = 0;
+ tags.extraShadows = 0;
+
+ /* Update file size */
+ if(object->variantType == YAFFS_OBJECT_TYPE_FILE){
+ oh->fileSize = object->variant.fileVariant.fileSize;
+ tags.extraFileLength = oh->fileSize;
+ }
+
+ yaffs_VerifyObjectHeader(object, oh, &tags, 1);
+ newChunk =
+ yaffs_WriteNewChunkWithTagsToNAND(dev,(__u8 *) oh, &tags, 1);
+ } else
+ 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->hdrChunk = newChunk;
+ object->serial = tags.serialNumber;
+ } else {
+ /* It's a data chunk */
+ int ok;
+ ok = yaffs_PutChunkIntoFile
+ (object,
+ tags.chunkId,
+ newChunk, 0);
+ }
+ }
+ }
+
+ if (retVal == YAFFS_OK)
+ 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--;
+ }
+