blob: f8f02dfbd6eb4e81ef7fcec8debf43480ce9abc3 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/storage/f2fs/f2fs.h"
namespace f2fs {
bool F2fs::SpaceForRollForward() {
SuperblockInfo &superblock_info = GetSuperblockInfo();
if (superblock_info.GetLastValidBlockCount() + superblock_info.GetAllocValidBlockCount() >
superblock_info.GetUserBlockCount())
return false;
return true;
}
FsyncInodeEntry *F2fs::GetFsyncInode(list_node_t *head, nid_t ino) {
list_node_t *this_node;
FsyncInodeEntry *entry;
list_for_every(head, this_node) {
entry = containerof(this_node, FsyncInodeEntry, list);
if (entry->vnode->Ino() == ino)
return entry;
}
return nullptr;
}
zx_status_t F2fs::RecoverDentry(NodePage *ipage, VnodeF2fs *vnode) {
Node *raw_node = ipage->GetAddress<Node>();
Inode *raw_inode = &(raw_node->i);
fbl::RefPtr<VnodeF2fs> dir_refptr;
Dir *dir;
zx_status_t err = ZX_OK;
if (!ipage->IsDentDnode())
goto out;
err = VnodeF2fs::Vget(this, LeToCpu(raw_inode->i_pino), &dir_refptr);
if (err != ZX_OK) {
goto out;
}
dir = static_cast<Dir *>(dir_refptr.get());
#if 0 // porting needed
// parent.d_inode = dir;
// dent.d_parent = &parent;
// dent.d_name.len = LeToCpu(raw_inode->i_namelen);
// dent.d_name.name = raw_inode->i_name;
#endif
if (auto dir_entry = dir->FindEntry(vnode->GetNameView()); dir_entry.is_error()) {
dir->AddLink(vnode->GetNameView(), vnode);
}
out:
#if 0 // porting needed
// kunmap(ipage);
#endif
return err;
}
zx_status_t F2fs::RecoverInode(VnodeF2fs *vnode, NodePage *node_page) {
struct Node *raw_node = node_page->GetAddress<Node>();
struct Inode *raw_inode = &(raw_node->i);
vnode->SetMode(LeToCpu(raw_inode->i_mode));
vnode->SetSize(LeToCpu(raw_inode->i_size));
vnode->SetATime(LeToCpu(raw_inode->i_atime), LeToCpu(raw_inode->i_atime_nsec));
vnode->SetCTime(LeToCpu(raw_inode->i_ctime), LeToCpu(raw_inode->i_ctime_nsec));
vnode->SetMTime(LeToCpu(raw_inode->i_mtime), LeToCpu(raw_inode->i_mtime_nsec));
return RecoverDentry(node_page, vnode);
}
zx_status_t F2fs::FindFsyncDnodes(list_node_t *head) {
SuperblockInfo &superblock_info = GetSuperblockInfo();
uint64_t cp_ver = LeToCpu(superblock_info.GetCheckpoint().checkpoint_ver);
fbl::RefPtr<VnodeF2fs> vnode_refptr;
zx_status_t err = ZX_OK;
// retrieve the curseg information of kCursegWarmNode
CursegInfo *curseg = segment_manager_->CURSEG_I(CursegType::kCursegWarmNode);
// get blkaddr from which it starts finding fsyncd dnode block
block_t blkaddr = segment_manager_->StartBlock(curseg->segno) + curseg->next_blkoff;
// alloc a temporal page to read node blocks.
LockedPage page;
if (zx_status_t ret = GetNodeVnode().GrabCachePage(-1, &page); ret != ZX_OK) {
return ret;
}
#if 0 // porting needed
// lock_page(page);
#endif
while (true) {
if (MakeOperation(storage::OperationType::kRead, page, blkaddr, PageType::kNode)) {
break;
}
if (cp_ver != page.GetPage<NodePage>().CpverOfNode()) {
break;
}
if (!page.GetPage<NodePage>().IsFsyncDnode()) {
/* check next segment */
blkaddr = page.GetPage<NodePage>().NextBlkaddrOfNode();
page->ClearUptodate();
continue;
}
FsyncInodeEntry *entry = GetFsyncInode(head, page.GetPage<NodePage>().InoOfNode());
if (entry) {
entry->blkaddr = blkaddr;
if (IsInode(*page) && page.GetPage<NodePage>().IsDentDnode()) {
entry->vnode->SetFlag(InodeInfoFlag::kIncLink);
}
} else {
if (IsInode(*page) && page.GetPage<NodePage>().IsDentDnode()) {
if (GetNodeManager().RecoverInodePage(page.GetPage<NodePage>())) {
err = ZX_ERR_NO_MEMORY;
break;
}
}
// TODO: Without cache, Vget cannot retrieve the node page for the fsyncd file
// that was created after the last checkpoint before spo. (i.e., IsDentDnode)
// It expects RecoverInodePage() creates a cached page for the inode.
if (err = VnodeF2fs::Vget(this, page.GetPage<NodePage>().InoOfNode(), &vnode_refptr);
err != ZX_OK) {
break;
}
// add this fsync inode to the list
entry = new FsyncInodeEntry;
if (!entry) {
err = ZX_ERR_NO_MEMORY;
vnode_refptr.reset();
break;
}
list_initialize(&entry->list);
entry->vnode = std::move(vnode_refptr);
entry->blkaddr = blkaddr;
list_add_tail(&entry->list, head);
}
if (IsInode(*page)) {
err = RecoverInode(entry->vnode.get(), &page.GetPage<NodePage>());
if (err) {
break;
}
}
// get the next block informatio from
blkaddr = page.GetPage<NodePage>().NextBlkaddrOfNode();
page->ClearUptodate();
}
#if 0 // porting needed
out:
// unlock_page(page);
//__free_pages(page, 0);
#endif
return err;
}
void F2fs::DestroyFsyncDnodes(list_node_t *head) {
list_node_t *this_node;
FsyncInodeEntry *entry;
list_for_every(head, this_node) {
entry = containerof(this_node, FsyncInodeEntry, list);
entry->vnode.reset();
list_delete(&entry->list);
delete entry;
}
}
void F2fs::CheckIndexInPrevNodes(block_t blkaddr) {
SuperblockInfo &superblock_info = GetSuperblockInfo();
uint32_t segno = segment_manager_->GetSegmentNumber(blkaddr);
uint16_t blkoff = static_cast<uint16_t>(segment_manager_->GetSegOffFromSeg0(blkaddr) &
(superblock_info.GetBlocksPerSeg() - 1));
Summary sum;
nid_t ino;
fbl::RefPtr<VnodeF2fs> vnode_refptr;
VnodeF2fs *vnode;
block_t bidx;
int i;
SegmentEntry &sentry = GetSegmentManager().GetSegmentEntry(segno);
if (!TestValidBitmap(blkoff, sentry.cur_valid_map.get())) {
return;
}
// Get the previous summary
for (i = static_cast<int>(CursegType::kCursegWarmData);
i <= static_cast<int>(CursegType::kCursegColdData); ++i) {
CursegInfo *curseg = segment_manager_->CURSEG_I(static_cast<CursegType>(i));
if (curseg->segno == segno) {
sum = curseg->sum_blk->entries[blkoff];
break;
}
}
if (i > static_cast<int>(CursegType::kCursegColdData)) {
LockedPage sum_page;
GetSegmentManager().GetSumPage(segno, &sum_page);
SummaryBlock *sum_node;
sum_node = sum_page->GetAddress<SummaryBlock>();
sum = sum_node->entries[blkoff];
}
// Get the node page
{
LockedPage node_page;
if (zx_status_t err = GetNodeManager().GetNodePage(LeToCpu(sum.nid), &node_page);
err != ZX_OK) {
FX_LOGS(ERROR) << "F2fs::CheckIndexInPrevNodes, GetNodePage Error!!!";
return;
}
bidx = node_page.GetPage<NodePage>().StartBidxOfNode() + LeToCpu(sum.ofs_in_node);
ino = node_page.GetPage<NodePage>().InoOfNode();
}
// Deallocate previous index in the node page
VnodeF2fs::Vget(this, ino, &vnode_refptr);
vnode = vnode_refptr.get();
vnode->TruncateHole(bidx, bidx + 1);
}
void F2fs::DoRecoverData(VnodeF2fs *vnode, NodePage *page, block_t blkaddr) {
uint32_t start, end;
Summary sum;
NodeInfo ni;
start = page->StartBidxOfNode();
if (IsInode(*page)) {
end = start + kAddrsPerInode;
} else {
end = start + kAddrsPerBlock;
}
LockedPage dnode_page;
if (GetNodeManager().GetLockedDnodePage(*vnode, start, &dnode_page) != ZX_OK) {
return;
}
dnode_page->WaitOnWriteback();
GetNodeManager().GetNodeInfo(dnode_page.GetPage<NodePage>().NidOfNode(), ni);
ZX_ASSERT(ni.ino == page->InoOfNode());
ZX_ASSERT(dnode_page.GetPage<NodePage>().OfsOfNode() == page->OfsOfNode());
uint32_t ofs_in_dnode;
if (auto result = GetNodeManager().GetOfsInDnode(*vnode, start); result.is_error()) {
return;
} else {
ofs_in_dnode = result.value();
}
for (; start < end; ++start) {
block_t src, dest;
src = DatablockAddr(&dnode_page.GetPage<NodePage>(), ofs_in_dnode);
dest = DatablockAddr(page, ofs_in_dnode);
if (src != dest && dest != kNewAddr && dest != kNullAddr) {
if (src == kNullAddr) {
zx_status_t err = vnode->ReserveNewBlock(dnode_page.GetPage<NodePage>(), ofs_in_dnode);
ZX_ASSERT(err == ZX_OK);
}
// Check the previous node page having this index
CheckIndexInPrevNodes(dest);
GetSegmentManager().SetSummary(&sum, dnode_page.GetPage<NodePage>().NidOfNode(), ofs_in_dnode,
ni.version);
// write dummy data page
GetSegmentManager().RecoverDataPage(nullptr, &sum, src, dest);
vnode->SetDataBlkaddr(dnode_page.GetPage<NodePage>(), ofs_in_dnode, dest);
vnode->UpdateExtentCache(dest, page->StartBidxOfNode());
}
++ofs_in_dnode;
}
// write node page in place
GetSegmentManager().SetSummary(&sum, dnode_page.GetPage<NodePage>().NidOfNode(), 0, 0);
if (IsInode(*dnode_page)) {
vnode->MarkInodeDirty();
}
dnode_page.GetPage<NodePage>().CopyNodeFooterFrom(*page);
dnode_page.GetPage<NodePage>().FillNodeFooter(ni.nid, ni.ino, page->OfsOfNode(), false);
dnode_page->SetDirty();
GetNodeManager().RecoverNodePage(dnode_page, sum, ni, blkaddr);
}
void F2fs::RecoverData(list_node_t *head, CursegType type) {
SuperblockInfo &superblock_info = GetSuperblockInfo();
uint64_t cp_ver = LeToCpu(superblock_info.GetCheckpoint().checkpoint_ver);
block_t blkaddr;
blkaddr = segment_manager_->NextFreeBlkAddr(type);
// Alloc a tempotal page to read a chain of node blocks
// TODO: need to request read IOs w/ uncached pages
LockedPage page;
if (zx_status_t ret = GetNodeVnode().GrabCachePage(-1, &page); ret != ZX_OK) {
return;
}
while (true) {
if (MakeOperation(storage::OperationType::kRead, page, blkaddr, PageType::kNode)) {
break;
}
if (cp_ver != page.GetPage<NodePage>().CpverOfNode()) {
break;
}
if (FsyncInodeEntry *entry = GetFsyncInode(head, page.GetPage<NodePage>().InoOfNode());
entry != nullptr) {
DoRecoverData(entry->vnode.get(), &page.GetPage<NodePage>(), blkaddr);
if (entry->blkaddr == blkaddr) {
list_delete(&entry->list);
entry->vnode.reset();
delete entry;
}
}
// check next segment
blkaddr = page.GetPage<NodePage>().NextBlkaddrOfNode();
page->ClearUptodate();
}
#if 0 // porting needed
//__free_pages(page, 0);
#endif
GetSegmentManager().AllocateNewSegments();
}
void F2fs::RecoverFsyncData() {
SuperblockInfo &superblock_info = GetSuperblockInfo();
list_node_t inode_list;
list_initialize(&inode_list);
// step #1: find fsynced inode numbers
if (FindFsyncDnodes(&inode_list)) {
goto out;
}
if (list_is_empty(&inode_list)) {
goto out;
}
// step #2: recover data
superblock_info.SetOnRecovery();
RecoverData(&inode_list, CursegType::kCursegWarmNode);
superblock_info.ClearOnRecovery();
ZX_ASSERT(list_is_empty(&inode_list));
out:
DestroyFsyncDnodes(&inode_list);
WriteCheckpoint(false, false);
}
} // namespace f2fs