📄 yaffs_guts.c
字号:
/* * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. * * Copyright (C) 2002-2007 Aleph One Ltd. * for Toby Churchill Ltd and Brightstar Engineering * * Created by Charles Manning <charles@aleph1.co.uk> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */const char *yaffs_guts_c_version = "$Id: yaffs_guts.c,v 1.1.1.1 2008/03/28 04:29:21 jlwei Exp $";#include "yportenv.h"#include "yaffsinterface.h"#include "yaffs_guts.h"#include "yaffs_tagsvalidity.h"#include "yaffs_tagscompat.h"#ifndef CONFIG_YAFFS_OWN_SORT#include "yaffs_qsort.h"#endif#include "yaffs_nand.h"#include "yaffs_checkptrw.h"#include "yaffs_nand.h"#include "yaffs_packedtags2.h"#ifdef CONFIG_YAFFS_WINCEvoid yfsd_LockYAFFS(BOOL fsLockOnly);void yfsd_UnlockYAFFS(BOOL fsLockOnly);#endif#define YAFFS_PASSIVE_GC_CHUNKS 2#include "yaffs_ecc.h"/* Robustification (if it ever comes about...) */static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND);static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk);static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * tags);static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, const yaffs_ExtendedTags * tags);/* Other local prototypes */static int yaffs_UnlinkObject( yaffs_Object *obj);static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj);static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList);static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev, const __u8 * buffer, yaffs_ExtendedTags * tags, int useReserve);static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, int chunkInNAND, int inScan);static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, yaffs_ObjectType type);static void yaffs_AddObjectToDirectory(yaffs_Object * directory, yaffs_Object * obj);static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, int isShrink, int shadows);static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj);static int yaffs_CheckStructures(void);static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, int chunkOffset, int *limit);static int yaffs_DoGenericObjectDeletion(yaffs_Object * in);static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo);static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo);static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, int lineNo);static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, int chunkInNAND);static int yaffs_UnlinkWorker(yaffs_Object * obj);static void yaffs_DestroyObject(yaffs_Object * obj);static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, int chunkInObject);loff_t yaffs_GetFileSize(yaffs_Object * obj);static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr);static void yaffs_VerifyFreeChunks(yaffs_Device * dev);#ifdef YAFFS_PARANOIDstatic int yaffs_CheckFileSanity(yaffs_Object * in);#else#define yaffs_CheckFileSanity(in)#endifstatic void yaffs_InvalidateWholeChunkCache(yaffs_Object * in);static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId);static void yaffs_InvalidateCheckpoint(yaffs_Device *dev);/* Function to calculate chunk and offset */static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, __u32 *chunk, __u32 *offset){ if(dev->chunkShift){ /* Easy-peasy power of 2 case */ *chunk = (__u32)(addr >> dev->chunkShift); *offset = (__u32)(addr & dev->chunkMask); } else if(dev->crumbsPerChunk) { /* Case where we're using "crumbs" */ *offset = (__u32)(addr & dev->crumbMask); addr >>= dev->crumbShift; *chunk = ((__u32)addr)/dev->crumbsPerChunk; *offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift); } else YBUG();}/* Function to return the number of shifts for a power of 2 greater than or equal * to the given number * Note we don't try to cater for all possible numbers and this does not have to * be hellishly efficient. */ static __u32 ShiftsGE(__u32 x){ int extraBits; int nShifts; nShifts = extraBits = 0; while(x>1){ if(x & 1) extraBits++; x>>=1; nShifts++; } if(extraBits) nShifts++; return nShifts;}/* Function to return the number of shifts to get a 1 in bit 0 */ static __u32 ShiftDiv(__u32 x){ int nShifts; nShifts = 0; if(!x) return 0; while( !(x&1)){ x>>=1; nShifts++; } return nShifts;}/* * Temporary buffer manipulations. */static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo){ int i, j; for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { if (dev->tempBuffer[i].line == 0) { dev->tempBuffer[i].line = lineNo; if ((i + 1) > dev->maxTemp) { dev->maxTemp = i + 1; for (j = 0; j <= i; j++) dev->tempBuffer[j].maxLine = dev->tempBuffer[j].line; } return dev->tempBuffer[i].buffer; } } T(YAFFS_TRACE_BUFFERS, (TSTR("Out of temp buffers at line %d, other held by lines:"), lineNo)); for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line)); } T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR))); /* * If we got here then we have to allocate an unmanaged one * This is not good. */ dev->unmanagedTempAllocations++; return YMALLOC(dev->nDataBytesPerChunk);}static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, int lineNo){ int i; for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { if (dev->tempBuffer[i].buffer == buffer) { dev->tempBuffer[i].line = 0; return; } } if (buffer) { /* assume it is an unmanaged one. */ T(YAFFS_TRACE_BUFFERS, (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR), lineNo)); YFREE(buffer); dev->unmanagedTempDeallocations++; }}/* * Determine if we have a managed buffer. */int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer){ int i; for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { if (dev->tempBuffer[i].buffer == buffer) return 1; } for (i = 0; i < dev->nShortOpCaches; i++) { if( dev->srCache[i].data == buffer ) return 1; } if (buffer == dev->checkpointBuffer) return 1; T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: unmaged buffer detected.\n" TENDSTR))); return 0;}/* * Chunk bitmap manipulations */static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk){ if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { T(YAFFS_TRACE_ERROR, (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), blk)); YBUG(); } return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->internalStartBlock));}static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk){ __u8 *blkBits = yaffs_BlockBits(dev, blk); memset(blkBits, 0, dev->chunkBitmapStride);}static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk){ __u8 *blkBits = yaffs_BlockBits(dev, blk); blkBits[chunk / 8] &= ~(1 << (chunk & 7));}static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk){ __u8 *blkBits = yaffs_BlockBits(dev, blk); blkBits[chunk / 8] |= (1 << (chunk & 7));}static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk){ __u8 *blkBits = yaffs_BlockBits(dev, blk); return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;}static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk){ __u8 *blkBits = yaffs_BlockBits(dev, blk); int i; for (i = 0; i < dev->chunkBitmapStride; i++) { if (*blkBits) return 1; blkBits++; } return 0;}/* * Simple hash function. Needs to have a reasonable spread */ static Y_INLINE int yaffs_HashFunction(int n){ n = abs(n); return (n % YAFFS_NOBJECT_BUCKETS);}/* * Access functions to useful fake objects */ yaffs_Object *yaffs_Root(yaffs_Device * dev){ return dev->rootDir;}yaffs_Object *yaffs_LostNFound(yaffs_Device * dev){ return dev->lostNFoundDir;}/* * Erased NAND checking functions */ int yaffs_CheckFF(__u8 * buffer, int nBytes){ /* Horrible, slow implementation */ while (nBytes--) { if (*buffer != 0xFF) return 0; buffer++; } return 1;}static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, int chunkInNAND){ int retval = YAFFS_OK; __u8 *data = yaffs_GetTempBuffer(dev, __LINE__); yaffs_ExtendedTags tags; int result; result = yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags); if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR) retval = YAFFS_FAIL; if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) { T(YAFFS_TRACE_NANDACCESS, (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND)); retval = YAFFS_FAIL; } yaffs_ReleaseTempBuffer(dev, data, __LINE__); return retval;}static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, const __u8 * data, yaffs_ExtendedTags * tags, int useReserve){ int chunk; int writeOk = 0; int erasedOk = 1; int attempts = 0; yaffs_BlockInfo *bi; yaffs_InvalidateCheckpoint(dev); do { chunk = yaffs_AllocateChunk(dev, useReserve,&bi); if (chunk >= 0) { /* First check this chunk is erased, if it needs checking. * The checking policy (unless forced always on) is as follows: * Check the first page we try to write in a block. * - If the check passes then we don't need to check any more. * - If the check fails, we check again... * If the block has been erased, we don't need to check. * * However, if the block has been prioritised for gc, then * we think there might be something odd about this block * and stop using it. * * Rationale: * We should only ever see chunks that have not been erased * if there was a partially written chunk due to power loss * This checking policy should catch that case with very * few checks and thus save a lot of checks that are most likely not * needed. */ if(bi->gcPrioritise){ yaffs_DeleteChunk(dev, chunk, 1, __LINE__); } else {#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED bi->skipErasedCheck = 0;#endif if(!bi->skipErasedCheck){ erasedOk = yaffs_CheckChunkErased(dev, chunk); if(erasedOk && !bi->gcPrioritise) bi->skipErasedCheck = 1; } if (!erasedOk) { T(YAFFS_TRACE_ERROR, (TSTR ("**>> yaffs chunk %d was not erased" TENDSTR), chunk)); } else { writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, data, tags); } attempts++; if (writeOk) { /* * Copy the data into the robustification buffer. * NB We do this at the end to prevent duplicates in the case of a write error. * Todo */ yaffs_HandleWriteChunkOk(dev, chunk, data, tags); } else { /* The erased check or write failed */ yaffs_HandleWriteChunkError(dev, chunk, erasedOk); } } } } while (chunk >= 0 && !writeOk); if (attempts > 1) { T(YAFFS_TRACE_ERROR, (TSTR("**>> yaffs write required %d attempts" TENDSTR), attempts)); dev->nRetriedWrites += (attempts - 1); } return chunk;}/* * Block retiring for handling a broken block. */ static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND){ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); yaffs_InvalidateCheckpoint(dev); yaffs_MarkBlockBad(dev, blockInNAND); bi->blockState = YAFFS_BLOCK_STATE_DEAD; bi->gcPrioritise = 0; bi->needsRetiring = 0; dev->nRetiredBlocks++;}/* * Functions for robustisizing TODO * */ static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * tags){}static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, const yaffs_ExtendedTags * tags){}void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi){ if(!bi->gcPrioritise){ bi->gcPrioritise = 1; dev->hasPendingPrioritisedGCs = 1; bi->chunkErrorStrikes ++; if(bi->chunkErrorStrikes > 3){ bi->needsRetiring = 1; /* Too many stikes, so retire this */ T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR))); } }}static void yaffs_ReportOddballBlocks(yaffs_Device *dev){ int i; for(i = dev->internalStartBlock; i <= dev->internalEndBlock && (yaffs_traceMask & YAFFS_TRACE_BAD_BLOCKS); i++){ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); if(bi->needsRetiring || bi->gcPrioritise) T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("yaffs block %d%s%s" TENDSTR), i, bi->needsRetiring ? " needs retiring" : "", bi->gcPrioritise ? " gc prioritised" : "")); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -