📄 uffs_buf.c~
字号:
father = dirty->father;
serial = dirty->serial;
dirty = dirty->nextDirty;
while(dirty) {
if(father != dirty->father ||
serial != dirty->serial) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"father or serial in dirty pages buffer are not the same ?\n");
return U_FAIL;
}
if(dirty->mark != UFFS_BUF_DIRTY) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"non-dirty page buffer in dirty buffer list ?\n");
return U_FAIL;
}
dirty = dirty->nextDirty;
}
return U_SUCC;
}
/** find a page in dirty list, which has minimum pageID */
uffs_Buf * _FindMinimunPageIdFromDirtyList(uffs_Buf *dirtyList)
{
uffs_Buf * work = dirtyList;
uffs_Buf * buf = dirtyList;
work = work->nextDirty;
while(work) {
if(work->pageID < buf->pageID) buf = work;
work = work->nextDirty;
}
return buf;
}
/**
* \brief flush buffer to a block with enough free pages
*
* pages in dirty list must be sorted by pageID to write to flash
*/
static
URET
_BufFlush_Exist_With_Enough_FreePage(
uffs_Device *dev,
uffs_blockInfo *bc, //!< block info (Source, also destination)
u16 freePages //!< free pages number on destination block
)
{
u16 page;
uffs_Buf *buf;
uffs_Tags *tag;
URET ret;
// uffs_Perror(UFFS_ERR_NOISY, PFX"Flush buffers with Enough Free Page, in block %d\n",
// bc->blockNum);
for(page = dev->attr->pages_per_block - freePages; //page: free page num
dev->buf.dirtyCount > 0; //still has dirty pages?
page++) {
buf = _FindMinimunPageIdFromDirtyList(dev->buf.dirty);
if(buf == NULL) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"dirtyCount > 0, but no dirty pages in list ?\n");
return U_FAIL;
}
//now a dirty page which has pageID(id) been found
//write to page i
uffs_LoadBlockInfo(dev, bc, page);
tag = &(bc->spares[page].tag);
tag->blockTimeStamp = uffs_GetBlockTimeStamp(dev, bc);
tag->dataLength = buf->dataLen;
tag->type = buf->type;
tag->dataSum = _GetDataSum(dev, buf);
tag->father = buf->father;
tag->serial = buf->serial;
tag->pageID = (u8)(buf->pageID); //FIX ME!! if page more than 256 in a block
ret = uffs_WriteDataToNewPage(dev, bc->blockNum, page, tag, buf);
if(ret == U_SUCC) {
if(_BreakFromDirty(dev, buf) == U_SUCC) {
buf->mark = UFFS_BUF_VALID;
_MoveNodeToHead(dev, buf);
}
}
else{
uffs_Perror(UFFS_ERR_NORMAL, PFX"I/O error <1>?\n");
return U_FAIL;
}
} //end of for
if(dev->buf.dirty != NULL || dev->buf.dirtyCount != 0) {
uffs_Perror(UFFS_ERR_NORMAL, PFX"still has dirty buffer ?\n");
}
return U_SUCC;
}
/**
* \brief flush buffer to a new block which is not registered in tree
*
* Scenario:
* 1. get a new block
* 2. write pages in dirty list to new block, sorted by pageID
* 3. insert new block to tree
*/
static URET _BufFlush_NewBlock(uffs_Device *dev)
{
u8 type;
u16 father, serial;
uffs_Buf *dirty, *buf;
TreeNode *node;
u16 i;
uffs_blockInfo *bc;
uffs_Tags *tag;
URET ret;
dirty = dev->buf.dirty;
type = dirty->type;
father = dirty->father; //all pages in dirty list have the same type, father and serial
serial = dirty->serial;
node = uffs_GetErased(dev);
if(node == NULL) {
uffs_Perror(UFFS_ERR_NOISY, PFX"no erased block!\n");
return U_FAIL;
}
bc = uffs_GetBlockInfo(dev, node->u.list.block);
if(bc == NULL) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"get block info fail!\n");
uffs_InsertToErasedListHead(dev, node); //put node back to erased list
return U_FAIL;
}
// uffs_Perror(UFFS_ERR_NOISY, PFX"Flush buffers with NewBlock, block %d\n",
// bc->blockNum);
for(i = 0; i < dev->attr->pages_per_block; i++) {
buf = _FindBufInDirtyList(dev->buf.dirty, i);
if(buf == NULL) break;
uffs_LoadBlockInfo(dev, bc, i);
tag = &(bc->spares[i].tag);
tag->blockTimeStamp = uffs_GetFirstBlockTimeStamp();
tag->dataLength = buf->dataLen;
tag->dataSum = _GetDataSum(dev, buf);
tag->type = type;
tag->father = buf->father;
tag->serial = buf->serial;
tag->pageID = (u8)(buf->pageID); //FIX ME!! if page more than 256 in a block
ret = uffs_WriteDataToNewPage(dev, node->u.list.block, i, tag, buf);
if(ret == U_SUCC) {
if(_BreakFromDirty(dev, buf) == U_SUCC) {
buf->mark = UFFS_BUF_VALID;
_MoveNodeToHead(dev, buf);
}
}
else{
uffs_Perror(UFFS_ERR_NORMAL, PFX"I/O error <2>?\n");
uffs_PutBlockInfo(dev, bc);
return U_FAIL;
}
}
//now, all pages write successful, fix the tree...
switch(type) {
case UFFS_TYPE_DIR:
node->u.dir.block = bc->blockNum;
node->u.dir.father = father;
node->u.dir.serial = serial;
//node->u.dir.pagID = 0; //dir stored in page 0, ??
//node->u.dir.ofs = 0; //TODO!!, for dir, the ofs should be ... ?
node->u.dir.checkSum = bc->spares[0].tag.dataSum; //for dir, the checkSum should be the same as file
//FIXME: if we support more than one dir in one block
break;
case UFFS_TYPE_FILE:
node->u.file.block = bc->blockNum;
node->u.file.father = father;
node->u.file.serial = serial;
node->u.file.checkSum = bc->spares[0].tag.dataSum; //for file, the page0 is where fileinfo ...
break;
case UFFS_TYPE_DATA:
node->u.data.block = bc->blockNum;
node->u.data.father = father;
node->u.data.serial = serial;
break;
default:
uffs_Perror(UFFS_ERR_NOISY, PFX"Unknown type %d\n", type);
break;
}
uffs_InsertNodeToTree(dev, type, node);
uffs_PutBlockInfo(dev, bc);
return U_SUCC;
}
/**
* \brief flush buffer with block recover
*
* Scenario:
* 1. get a free (erased) block --> newNode <br>
* 2. copy from old block ---> oldNode, or copy from dirty list, <br>
* sorted by pageID, to new block. Skips the invalid pages when copy pages.<br>
* 3. erased old block. set new info to oldNode, set newNode->block = old block,<br>
* and put newNode to erased list.<br>
* \note IT'S IMPORTANT TO KEEP OLD NODE IN THE LIST, so you don't need to update the obj->node :-)
*/
static URET _BufFlush_Exist_With_BlockCover(
uffs_Device *dev,
TreeNode *node, //!< old data node on tree
uffs_blockInfo *bc //!< old data block info
)
{
u16 i;
u8 type, timeStamp;
u16 page, father, serial;
uffs_Buf *buf;
TreeNode *newNode;
uffs_blockInfo *newBc;
uffs_Tags *tag, *oldTag;
URET ret = U_SUCC;
u16 newBlock;
UBOOL succRecover = U_TRUE; //TRUE: recover successful, erase old block,
//FALSE: fail to recover, erase new block
type = dev->buf.dirty->type;
father = dev->buf.dirty->father;
serial = dev->buf.dirty->serial;
newNode = uffs_GetErased(dev);
if(newNode == NULL) {
uffs_Perror(UFFS_ERR_NOISY, PFX"no enough erased block!\n");
return U_FAIL;
}
newBlock = newNode->u.list.block;
newBc = uffs_GetBlockInfo(dev, newBlock);
if(newBc == NULL) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"get block info fail!\n");
uffs_InsertToErasedListHead(dev, newNode); //put node back to erased list
//because it doesn't use, so put to head
return U_FAIL;
}
uffs_LoadBlockInfo(dev, newBc, UFFS_ALL_PAGES);
timeStamp = uffs_GetNextBlockTimeStamp(uffs_GetBlockTimeStamp(dev, bc));
// uffs_Perror(UFFS_ERR_NOISY, PFX"Flush buffers with Block Recover, from %d to %d\n",
// bc->blockNum, newBc->blockNum);
for(i = 0; i < dev->attr->pages_per_block; i++) {
tag = &(newBc->spares[i].tag);
tag->blockTimeStamp = timeStamp;
tag->father = father;
tag->serial = serial;
tag->type = type;
tag->pageID = (u8)i; //now, pageID = page, FIX ME!! if more than 256 pages in a block
buf = _FindBufInDirtyList(dev->buf.dirty, i);
if(buf != NULL) {
tag->dataLength = buf->dataLen;
tag->dataSum = _GetDataSum(dev, buf);
ret = uffs_WriteDataToNewPage(dev, newBlock, i, tag, buf);
if(ret == U_SUCC) {
if(_BreakFromDirty(dev, buf) == U_SUCC) {
buf->mark = UFFS_BUF_VALID;
_MoveNodeToHead(dev, buf);
}
}
else{
uffs_Perror(UFFS_ERR_NORMAL, PFX"I/O error <3>?\n");
succRecover = U_FALSE;
break;
}
}
else {
page = uffs_FindPageInBlockWithPageId(dev, bc, i);
if(page == UFFS_INVALID_PAGE) {
uffs_ExpireBlockInfo(dev, newBc, i);
break; //end of last page, normal break
}
page = uffs_FindBestPageInBlock(dev, bc, page);
oldTag = &(bc->spares[page].tag);
buf = uffs_BufClone(dev, NULL);
if(buf == NULL) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"Can't clone a new buf!\n");
succRecover = U_FALSE;
break;
}
ret = uffs_LoadPhiDataToBuf(dev, buf, bc->blockNum, page);
if(ret == U_FAIL) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"I/O error ?\n");
uffs_BufFreeClone(dev, buf);
break;
}
buf->dataLen = oldTag->dataLength;
if(buf->dataLen > dev->com.pgDataSize) {
uffs_Perror(UFFS_ERR_NOISY, PFX"data length over flow!!!\n");
buf->dataLen = dev->com.pgDataSize;
}
buf->type = type;
buf->father = father;
buf->serial = serial;
buf->dataLen = oldTag->dataLength;
buf->pageID = oldTag->pageID;
tag->dataLength = buf->dataLen;
tag->dataSum = _GetDataSum(dev, buf);
ret = uffs_WriteDataToNewPage(dev, newBlock, i, tag, buf);
uffs_BufFreeClone(dev, buf);
if(ret != U_SUCC) {
uffs_Perror(UFFS_ERR_NORMAL, PFX"I/O error <4>?\n");
succRecover = U_FALSE;
break;
}
}
} //end of for
if(succRecover == U_TRUE) {
switch(type) {
case UFFS_TYPE_DIR:
node->u.dir.block = newBlock;
node->u.dir.checkSum = newBc->spares[0].tag.dataSum;
//node->u.dir.ofs = 0; //TODO!! fix me!
//node->u.dir.pagID = 0; //TODO!! fix me!
break;
case UFFS_TYPE_FILE:
node->u.file.block = newBlock;
node->u.file.checkSum = newBc->spares[0].tag.dataSum;
break;
case UFFS_TYPE_DATA:
node->u.data.block = newBlock;
break;
default:
uffs_Perror(UFFS_ERR_SERIOUS, PFX"UNKNOW TYPE\n");
break;
}
newNode->u.list.block = bc->blockNum;
dev->ops->EraseBlock(dev, bc->blockNum);
uffs_InsertToErasedListTail(dev, newNode);
uffs_ExpireBlockInfo(dev, bc, UFFS_ALL_PAGES);
}
else {
uffs_ExpireBlockInfo(dev, newBc, UFFS_ALL_PAGES);
dev->ops->EraseBlock(dev, newBlock);
newNode->u.list.block = newBlock;
uffs_InsertToErasedListTail(dev, newNode);
}
if(dev->buf.dirty != NULL || dev->buf.dirtyCount != 0) {
uffs_Perror(UFFS_ERR_NORMAL, PFX"still has dirty buffer ?\n");
}
uffs_PutBlockInfo(dev, newBc);
return U_SUCC;
}
URET _BufFlush(struct uffs_DeviceSt *dev, UBOOL force_block_recover)
{
uffs_Buf *dirty;
TreeNode *node;
uffs_blockInfo *bc;
u16 n;
URET ret;
u8 type;
u16 father;
u16 serial;
int block;
dirty = dev->buf.dirty;
if(dirty == NULL || dev->buf.dirtyCount == 0) {
return U_SUCC;
}
if(_CheckDirtyList(dev) == U_FAIL) return U_FAIL;
type = dirty->type;
father = dirty->father;
serial = dirty->serial;
switch(type) {
case UFFS_TYPE_DIR:
node = uffs_FindDirNodeFromTree(dev, serial);
break;
case UFFS_TYPE_FILE:
node = uffs_FindFileNodeFromTree(dev, serial);
break;
case UFFS_TYPE_DATA:
node = uffs_FindDataNode(dev, father, serial);
break;
default:
uffs_Perror(UFFS_ERR_SERIOUS, PFX"unknown type\n");
return U_FAIL;
}
if(node == NULL) {
//not found in the tree, need to generate a new block
ret = _BufFlush_NewBlock(dev);
}
else {
switch(type) {
case UFFS_TYPE_DIR:
block = node->u.dir.block;
break;
case UFFS_TYPE_FILE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -