blob: d6a5ebe2af4d96a71dc411f215c788a3a63f5ee5 [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 <unordered_set>
#include <block-client/cpp/fake-device.h>
#include <gtest/gtest.h>
#include "third_party/f2fs/f2fs.h"
#include "unit_lib.h"
namespace f2fs {
namespace {
TEST(SegmentMgr, BlkChaining) {
std::unique_ptr<Bcache> bc;
unittest_lib::MkfsOnFakeDev(&bc);
// create f2fs and root dir
std::unique_ptr<F2fs> fs;
MountOptions options{};
ASSERT_EQ(options.SetValue(options.GetNameView(kOptInlineDentry), 0), ZX_OK);
unittest_lib::MountWithOptions(options, &bc, &fs);
fbl::RefPtr<VnodeF2fs> root;
unittest_lib::CreateRoot(fs.get(), &root);
Page *root_node_page = nullptr;
SbInfo &sbi = fs->GetSbInfo();
// read the node block where the root inode is stored
fs->Nodemgr().GetNodePage(RootIno(&sbi), &root_node_page);
ASSERT_TRUE(root_node_page);
// retrieve the lba for root inode
std::vector<block_t> blk_chain(0);
int nwritten = kDefaultBlocksPerSegment * 2;
NodeInfo ni;
fs->Nodemgr().GetNodeInfo(RootIno(&sbi), &ni);
ASSERT_NE(ni.blk_addr, kNullAddr);
ASSERT_NE(ni.blk_addr, kNewAddr);
block_t alloc_addr = ni.blk_addr;
blk_chain.push_back(alloc_addr);
// write the root inode, and read the block where the previous version of the root inode is stored
// to check if the block has a proper lba address to the next node block
for (int i = 0; i < nwritten; i++) {
block_t old_addr = alloc_addr;
Page *read_page = GrabCachePage(nullptr, 0, 0);
ASSERT_TRUE(read_page);
fs->Segmgr().WriteNodePage(root_node_page, RootIno(&sbi), old_addr, &alloc_addr);
blk_chain.push_back(alloc_addr);
ASSERT_NE(alloc_addr, kNullAddr);
ASSERT_EQ(fs->GetBc().Readblk(blk_chain[i], read_page->data), ZX_OK);
ASSERT_EQ(alloc_addr, fs->Nodemgr().NextBlkaddrOfNode(read_page));
F2fsPutPage(read_page, 0);
}
F2fsPutPage(root_node_page, 0);
ASSERT_EQ(root->Close(), ZX_OK);
root = nullptr;
unittest_lib::Unmount(std::move(fs), &bc);
}
TEST(SegmentMgr, DirtyToFree) {
std::unique_ptr<Bcache> bc;
unittest_lib::MkfsOnFakeDev(&bc);
// create f2fs and root dir
std::unique_ptr<F2fs> fs;
MountOptions options{};
ASSERT_EQ(options.SetValue(options.GetNameView(kOptInlineDentry), 0), ZX_OK);
unittest_lib::MountWithOptions(options, &bc, &fs);
fbl::RefPtr<VnodeF2fs> root;
unittest_lib::CreateRoot(fs.get(), &root);
// read the root inode block
Page *root_node_page = nullptr;
SbInfo &sbi = fs->GetSbInfo();
fs->Nodemgr().GetNodePage(RootIno(&sbi), &root_node_page);
ASSERT_TRUE(root_node_page);
// check the precond. before making dirty segments
std::vector<uint32_t> prefree_array(0);
int nwritten = kDefaultBlocksPerSegment * 2;
uint32_t nprefree = 0;
NodeInfo ni;
fs->Nodemgr().GetNodeInfo(RootIno(&sbi), &ni);
ASSERT_NE(ni.blk_addr, kNullAddr);
ASSERT_NE(ni.blk_addr, kNewAddr);
block_t alloc_addr = ni.blk_addr;
ASSERT_FALSE(fs->Segmgr().PrefreeSegments());
uint32_t nfree_segs = fs->Segmgr().FreeSegments();
FreeSegmapInfo *free_i = GetFreeInfo(&sbi);
DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi);
// write the root inode repeatedly as much as 2 segments
for (int i = 0; i < nwritten; i++) {
block_t old_addr = alloc_addr;
fs->Segmgr().WriteNodePage(root_node_page, RootIno(&sbi), old_addr, &alloc_addr);
if (fs->Segmgr().GetValidBlocks(GetSegNo(&sbi, old_addr), 0) == 0) {
prefree_array.push_back(GetSegNo(&sbi, old_addr));
ASSERT_EQ(fs->Segmgr().PrefreeSegments(), ++nprefree);
}
}
// check the bitmaps and the number of free/prefree segments
ASSERT_EQ(fs->Segmgr().FreeSegments(), nfree_segs - nprefree);
for (auto &pre : prefree_array) {
ASSERT_TRUE(test_bit(pre, dirty_i->dirty_segmap[static_cast<int>(DirtyType::kPre)]));
ASSERT_TRUE(test_bit(pre, free_i->free_segmap));
}
// triggers checkpoint to make prefree segments transit to free ones
fs->Segmgr().BalanceFs();
// check the bitmaps and the number of free/prefree segments
for (auto &pre : prefree_array) {
ASSERT_FALSE(test_bit(pre, dirty_i->dirty_segmap[static_cast<int>(DirtyType::kPre)]));
ASSERT_FALSE(test_bit(pre, free_i->free_segmap));
}
ASSERT_EQ(fs->Segmgr().FreeSegments(), nfree_segs);
ASSERT_FALSE(fs->Segmgr().PrefreeSegments());
F2fsPutPage(root_node_page, 0);
ASSERT_EQ(root->Close(), ZX_OK);
root = nullptr;
unittest_lib::Unmount(std::move(fs), &bc);
}
} // namespace
} // namespace f2fs