blob: 628274ec6db48ae597cfd539e6db7ac5de8944a5 [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 "f2fs.h"
namespace f2fs {
/**
* We guarantee no failure on the returned page.
*/
Page *F2fs::GrabMetaPage(pgoff_t index) {
Page *page;
while (!(page = GrabCachePage(nullptr, MetaIno(sbi_.get()), index))) {
#if 0 // porting needed
// cond_resched();
#endif
}
/* We wait writeback only inside grab_meta_page() */
WaitOnPageWriteback(page);
SetPageUptodate(page);
return page;
}
Page *F2fs::GetMetaPage(pgoff_t index) {
Page *page;
repeat:
page = GrabCachePage(nullptr, MetaIno(sbi_.get()), index);
if (!page) {
#if 0 // porting needed
// cond_resched();
#endif
goto repeat;
}
if (VnodeF2fs::Readpage(this, static_cast<Page *>(PageAddress(page)), index, kReadSync)) {
F2fsPutPage(page, 1);
goto repeat;
}
#if 0 // porting needed
// mark_page_accessed(page);
#endif
/* We do not allow returning an errorneous page */
return page;
}
zx_status_t F2fs::F2fsWriteMetaPage(Page *page, WritebackControl *wbc) {
zx_status_t err;
WaitOnPageWriteback(page);
err = this->Segmgr().WriteMetaPage(page, wbc);
if (err) {
#if 0 // porting needed
// wbc->pages_skipped++;
// set_page_dirty(page, this);
#else
FlushDirtyMetaPage(this, page);
#endif
}
DecPageCount(&GetSbInfo(), CountType::kDirtyMeta);
/* In this case, we should not unlock this page */
#if 0 // porting needed
// if (err != kAopWritepageActivate)
// unlock_page(page);
#endif
return err;
}
#if 0 // porting needed
// int F2fs::F2fsWriteMetaPages(address_space *mapping, WritebackControl *wbc) {
// struct block_device *bdev = sbi_->sb->s_bdev;
// long written;
// if (wbc->for_kupdate)
// return 0;
// if (GetPages(sbi_, CountType::kDirtyMeta) == 0)
// return 0;
// /* if mounting is failed, skip writing node pages */
// mtx_lock(&sbi_->cp_mutex);
// written = sync_meta_pages(sbi_.get(), META, bio_get_nr_vecs(bdev));
// mtx_unlock(&sbi_->cp_mutex);
// wbc->nr_to_write -= written;
// return 0;
// }
#endif
int64_t F2fs::SyncMetaPages(PageType type, int64_t nr_to_write) {
#if 0 // porting needed
// address_space *mapping = sbi->meta_inode->i_mapping;
// pgoff_t index = 0, end = LONG_MAX;
// pagevec pvec;
// int64_t nwritten = 0;
// struct WritebackControl wbc = {
// .for_reclaim = 0,
// };
// pagevec_init(&pvec, 0);
// while (index <= end) {
// int i, nr_pages;
// nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
// PAGECACHE_TAG_DIRTY,
// min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1);
// if (nr_pages == 0)
// break;
// for (i = 0; i < nr_pages; i++) {
// page *page = pvec.pages[i];
// lock_page(page);
// BUG_ON(page->mapping != mapping);
// BUG_ON(!PageDirty(page));
// ClearPageDirtyForIo(page);
// f2fs_write_meta_page(page, &wbc);
// if (nwritten++ >= nr_to_write)
// break;
// }
// pagevec_release(&pvec);
// cond_resched();
// }
// if (nwritten)
// f2fs_submit_bio(sbi, type, nr_to_write == LONG_MAX);
// return nwritten;
#else
return 0;
#endif
}
#if 0 // porting needed
// int F2fs::F2fsSetMetaPageDirty(Page *page) {
// SetPageUptodate(page);
// if (!PageDirty(page)) {
// // __set_page_dirty_nobuffers(page);
// FlushDirtyMetaPage(this, page);
// IncPageCount(&GetSbInfo(), CountType::kDirtyMeta);
// SetSbDirt(&GetSbInfo());
// return 1;
// }
// return 0;
// }
#endif
zx_status_t F2fs::CheckOrphanSpace() {
SbInfo &sbi = GetSbInfo();
uint32_t max_orphans;
zx_status_t err = 0;
/*
* considering 512 blocks in a segment 5 blocks are needed for cp
* and log segment summaries. Remaining blocks are used to keep
* orphan entries with the limitation one reserved segment
* for cp pack we can have max 1020*507 orphan entries
*/
max_orphans = (sbi.blocks_per_seg - 5) * kOrphansPerBlock;
mtx_lock(&sbi.orphan_inode_mutex);
if (sbi.n_orphans >= max_orphans)
err = ZX_ERR_NO_SPACE;
mtx_unlock(&sbi.orphan_inode_mutex);
return err;
}
void F2fs::AddOrphanInode(nid_t ino) {
SbInfo &sbi = GetSbInfo();
list_node_t *head, *this_node;
OrphanInodeEntry *new_entry = nullptr, *orphan = nullptr;
mtx_lock(&sbi.orphan_inode_mutex);
head = &sbi.orphan_inode_list;
list_for_every(head, this_node) {
orphan = containerof(this_node, OrphanInodeEntry, list);
if (orphan->ino == ino)
goto out;
if (orphan->ino > ino)
break;
orphan = nullptr;
}
retry:
#if 0 // porting needed
// new_entry = kmem_cache_alloc(orphan_entry_slab, GFP_ATOMIC);
#else
new_entry = new OrphanInodeEntry;
#endif
if (!new_entry) {
#if 0 // porting needed
// cond_resched();
#endif
goto retry;
}
new_entry->ino = ino;
list_initialize(&new_entry->list);
/* add new_oentry into list which is sorted by inode number */
if (orphan) {
OrphanInodeEntry *prev;
/* get previous entry */
prev = containerof(orphan->list.prev, OrphanInodeEntry, list);
if (&prev->list != head) {
/* insert new orphan inode entry */
list_add(&prev->list, &new_entry->list);
} else {
list_add(head, &new_entry->list);
}
} else {
list_add_tail(head, &new_entry->list);
}
sbi.n_orphans++;
out:
mtx_unlock(&sbi.orphan_inode_mutex);
}
void F2fs::RemoveOrphanInode(nid_t ino) {
SbInfo &sbi = GetSbInfo();
list_node_t *this_node, *next, *head;
OrphanInodeEntry *orphan;
mtx_lock(&sbi.orphan_inode_mutex);
head = &sbi.orphan_inode_list;
list_for_every_safe(head, this_node, next) {
orphan = containerof(this_node, OrphanInodeEntry, list);
if (orphan->ino == ino) {
list_delete(&orphan->list);
#if 0 // porting needed
// kmem_cache_free(orphan_entry_slab, orphan);
#endif
delete orphan;
sbi.n_orphans--;
break;
}
}
mtx_unlock(&sbi.orphan_inode_mutex);
}
void F2fs::RecoverOrphanInode(nid_t ino) {
fbl::RefPtr<VnodeF2fs> vnode;
zx_status_t ret;
ret = VnodeF2fs::Vget(this, ino, &vnode);
ZX_ASSERT(ret == ZX_OK);
vnode->ClearNlink();
/* truncate all the data during Iput */
Iput(vnode.get());
vnode.reset();
}
int F2fs::RecoverOrphanInodes() {
SbInfo &sbi = GetSbInfo();
block_t start_blk, orphan_blkaddr, i, j;
if (!(GetCheckpoint(&sbi)->ckpt_flags & kCpOrphanPresentFlag))
return 0;
sbi.por_doing = 1;
start_blk = StartCpAddr(&sbi) + 1;
orphan_blkaddr = StartSumAddr(&sbi) - 1;
for (i = 0; i < orphan_blkaddr; i++) {
Page *page = GetMetaPage(start_blk + i);
OrphanBlock *orphan_blk;
orphan_blk = static_cast<OrphanBlock *>(PageAddress(page));
for (j = 0; j < LeToCpu(orphan_blk->entry_count); j++) {
nid_t ino = LeToCpu(orphan_blk->ino[j]);
RecoverOrphanInode(ino);
}
F2fsPutPage(page, 1);
}
/* clear Orphan Flag */
GetCheckpoint(&sbi)->ckpt_flags &= (~kCpOrphanPresentFlag);
sbi.por_doing = 0;
return 0;
}
void F2fs::WriteOrphanInodes(block_t start_blk) {
SbInfo &sbi = GetSbInfo();
list_node_t *head, *this_node, *next;
OrphanBlock *orphan_blk = nullptr;
Page *page = nullptr;
uint32_t nentries = 0;
uint16_t index = 1;
uint16_t orphan_blocks;
orphan_blocks =
static_cast<uint16_t>((sbi.n_orphans + (kOrphansPerBlock - 1)) / kOrphansPerBlock);
mtx_lock(&sbi.orphan_inode_mutex);
head = &sbi.orphan_inode_list;
/* loop for each orphan inode entry and write them in Jornal block */
list_for_every_safe(head, this_node, next) {
OrphanInodeEntry *orphan;
orphan = containerof(this_node, OrphanInodeEntry, list);
if (nentries == kOrphansPerBlock) {
/*
* an orphan block is full of 1020 entries,
* then we need to flush current orphan blocks
* and bring another one in memory
*/
orphan_blk->blk_addr = CpuToLe(index);
orphan_blk->blk_count = CpuToLe(orphan_blocks);
orphan_blk->entry_count = CpuToLe(nentries);
#if 0 // porting needed
// set_page_dirty(page, this);
#else
FlushDirtyMetaPage(this, page);
#endif
F2fsPutPage(page, 1);
index++;
start_blk++;
nentries = 0;
page = nullptr;
}
if (page)
goto page_exist;
page = GrabMetaPage(start_blk);
orphan_blk = static_cast<OrphanBlock *>(PageAddress(page));
memset(orphan_blk, 0, sizeof(*orphan_blk));
page_exist:
orphan_blk->ino[nentries++] = CpuToLe(orphan->ino);
}
if (!page)
goto end;
orphan_blk->blk_addr = CpuToLe(index);
orphan_blk->blk_count = CpuToLe(orphan_blocks);
orphan_blk->entry_count = CpuToLe(nentries);
#if 0 // porting needed
// set_page_dirty(page, this);
#else
FlushDirtyMetaPage(this, page);
#endif
F2fsPutPage(page, 1);
end:
mtx_unlock(&sbi.orphan_inode_mutex);
}
Page *F2fs::ValidateCheckpoint(block_t cp_addr, uint64_t *version) {
Page *cp_page_1, *cp_page_2;
uint64_t blk_size = sbi_->blocksize;
Checkpoint *cp_block;
uint64_t cur_version = 0, pre_version = 0;
uint32_t crc = 0;
size_t crc_offset;
/* Read the 1st cp block in this CP pack */
cp_page_1 = GetMetaPage(cp_addr);
/* get the version number */
cp_block = static_cast<Checkpoint *>(PageAddress(cp_page_1));
crc_offset = LeToCpu(cp_block->checksum_offset);
if (crc_offset >= blk_size)
goto invalid_cp1;
crc = *reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(cp_block) + crc_offset);
if (!F2fsCrcValid(crc, cp_block, crc_offset))
goto invalid_cp1;
pre_version = LeToCpu(cp_block->checkpoint_ver);
/* Read the 2nd cp block in this CP pack */
cp_addr += LeToCpu(cp_block->cp_pack_total_block_count) - 1;
cp_page_2 = GetMetaPage(cp_addr);
cp_block = static_cast<Checkpoint *>(PageAddress(cp_page_2));
crc_offset = LeToCpu(cp_block->checksum_offset);
if (crc_offset >= blk_size)
goto invalid_cp2;
crc = *reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(cp_block) + crc_offset);
if (!F2fsCrcValid(crc, cp_block, crc_offset))
goto invalid_cp2;
cur_version = LeToCpu(cp_block->checkpoint_ver);
if (cur_version == pre_version) {
*version = cur_version;
F2fsPutPage(cp_page_2, 1);
return cp_page_1;
}
invalid_cp2:
F2fsPutPage(cp_page_2, 1);
invalid_cp1:
F2fsPutPage(cp_page_1, 1);
return nullptr;
}
zx_status_t F2fs::GetValidCheckpoint() {
Checkpoint *cp_block;
SuperBlock &fsb = RawSb();
Page *cp1, *cp2, *cur_page;
uint64_t blk_size = sbi_->blocksize;
uint64_t cp1_version = 0, cp2_version = 0;
uint64_t cp_start_blk_no;
sbi_->ckpt = static_cast<Checkpoint *>(malloc(blk_size));
if (!sbi_->ckpt)
return -ENOMEM;
/*
* Finding out valid cp block involves read both
* sets( cp pack1 and cp pack 2)
*/
cp_start_blk_no = LeToCpu(fsb.cp_blkaddr);
cp1 = ValidateCheckpoint(cp_start_blk_no, &cp1_version);
/* The second checkpoint pack should start at the next segment */
cp_start_blk_no += 1 << LeToCpu(fsb.log_blocks_per_seg);
cp2 = ValidateCheckpoint(cp_start_blk_no, &cp2_version);
if (cp1 && cp2) {
if (VerAfter(cp2_version, cp1_version)) {
cur_page = cp2;
} else {
cur_page = cp1;
}
} else if (cp1) {
cur_page = cp1;
} else if (cp2) {
cur_page = cp2;
} else {
goto fail_no_cp;
}
cp_block = static_cast<Checkpoint *>(PageAddress(cur_page));
memcpy(sbi_->ckpt, cp_block, blk_size);
#ifdef F2FS_BU_DEBUG
int i;
std::cout << std::endl << "F2fs::GetValidCheckpoint" << std::endl;
for (i = 0; i < kMaxActiveNodeLogs; i++) {
std::cout << "[" << i << "] cur_node_segno " << cp_block->cur_node_segno[i]
<< ", cur_node_blkoff=" << cp_block->cur_node_blkoff[i] << std::endl;
}
for (i = 0; i < kMaxActiveNodeLogs; i++) {
std::cout << "[" << i << "] cur_data_segno " << cp_block->cur_data_segno[i]
<< ", cur_data_blkoff=" << cp_block->cur_data_blkoff[i] << std::endl;
}
#endif
F2fsPutPage(cp1, 1);
F2fsPutPage(cp2, 1);
return 0;
fail_no_cp:
free(sbi_->ckpt);
return -EINVAL;
}
#if 0 // porting needed
// void F2fs::SetDirtyDirPage(VnodeF2fs *vnode, Page *page) {
// SbInfo &sbi = GetSbInfo();
// list_node_t *head = &sbi.dir_inode_list;
// DirInodeEntry *new_entry;
// list_node_t *this_node;
// if (!S_ISDIR(vnode->i_mode_))
// return;
// retry:
// // new = kmem_cache_alloc(inode_entry_slab, GFP_NOFS);
// new_entry = new DirInodeEntry;
// if (!new_entry) {
// // cond_resched();
// goto retry;
// }
// new_entry->vnode = vnode;
// list_initialize(&new_entry->list);
// SpinLock(&sbi.dir_inode_lock);
// list_for_every(head, this_node) {
// DirInodeEntry *entry;
// entry = containerof(this_node, DirInodeEntry, list);
// if (entry->vnode == vnode) {
// // kmem_cache_free(inode_entry_slab, new_entry);
// delete new_entry;
// goto out;
// }
// }
// list_add_tail(&new_entry->list, head);
// sbi.n_dirty_dirs++;
// BUG_ON(!S_ISDIR(inode->i_mode));
// out:
// IncPageCount(&sbi, CountType::kDirtyDents);
// InodeIncDirtyDents(vnode);
// // SetPagePrivate(page);
// SpinUnlock(&sbi.dir_inode_lock);
// }
// void F2fs::RemoveDirtyDirInode(VnodeF2fs *vnode) {
// SbInfo &sbi = GetSbInfo();
// list_node_t *head = &sbi.dir_inode_list;
// list_node_t *this_node;
// if (!S_ISDIR(vnode->i_mode_))
// return;
// SpinLock(&sbi.dir_inode_lock);
// // if (AtomicRead(&F2FS_I(vnode)->dirty_dents))
// if (vnode->fi_.dirty_dents)
// goto out;
// list_for_every(head, this_node) {
// DirInodeEntry *entry;
// entry = containerof(this_node, DirInodeEntry, list);
// if (entry->vnode == vnode) {
// list_delete(&entry->list);
// // kmem_cache_free(inode_entry_slab, entry);
// delete entry;
// sbi.n_dirty_dirs--;
// break;
// }
// }
// out:
// SpinUnlock(&sbi.dir_inode_lock);
// }
#endif
void F2fs::SyncDirtyDirInodes() {
SbInfo &sbi = GetSbInfo();
list_node_t *head = &sbi.dir_inode_list;
DirInodeEntry *entry;
fbl::RefPtr<VnodeF2fs> vnode;
retry:
SpinLock(&sbi.dir_inode_lock);
if (list_is_empty(head)) {
SpinUnlock(&sbi.dir_inode_lock);
return;
}
entry = containerof(head->next, DirInodeEntry, list);
vnode.reset(static_cast<VnodeF2fs *>(Igrab(entry->vnode)));
SpinUnlock(&sbi.dir_inode_lock);
if (vnode) {
#if 0 // porting needed
// filemap_flush(vnode->i_mapping);
#endif
Iput(vnode.get());
vnode.reset();
} else {
/*
* We should submit bio, since it exists several
* wribacking dentry pages in the freeing inode.
*/
// TODO(unknown): bio[type] is empty
// Segmgr().SubmitBio(DATA, true);
}
goto retry;
}
/**
* Freeze all the FS-operations for checkpoint.
*/
void F2fs::BlockOperations() TA_NO_THREAD_SAFETY_ANALYSIS {
SbInfo &sbi = GetSbInfo();
int t;
struct WritebackControl wbc = {
#if 0 // porting needed
// .nr_to_write = LONG_MAX,
// .sync_mode = WB_SYNC_ALL,
// .for_reclaim = 0,
#endif
};
/* Stop renaming operation */
mutex_lock_op(&sbi, LockType::kRename);
mutex_lock_op(&sbi, LockType::kDentryOps);
retry_dents:
/* write all the dirty dentry pages */
SyncDirtyDirInodes();
mutex_lock_op(&sbi, LockType::kDataWrtie);
if (GetPages(&sbi, CountType::kDirtyDents)) {
mutex_unlock_op(&sbi, LockType::kDataWrtie);
goto retry_dents;
}
/* block all the operations */
for (t = static_cast<int>(LockType::kDataNew); t <= static_cast<int>(LockType::kNodeTrunc); t++)
mutex_lock_op(&sbi, static_cast<LockType>(t));
mtx_lock(&sbi.write_inode);
/*
* POR: we should ensure that there is no dirty node pages
* until finishing nat/sit flush.
*/
retry:
Nodemgr().SyncNodePages(0, &wbc);
mutex_lock_op(&sbi, LockType::kNodeWrite);
if (GetPages(&sbi, CountType::kDirtyNodes)) {
mutex_unlock_op(&sbi, LockType::kNodeWrite);
goto retry;
}
mtx_unlock(&sbi.write_inode);
}
void F2fs::UnblockOperations() TA_NO_THREAD_SAFETY_ANALYSIS {
SbInfo &sbi = GetSbInfo();
int t;
for (t = static_cast<int>(LockType::kNodeWrite); t >= static_cast<int>(LockType::kRename); t--)
mutex_unlock_op(&sbi, static_cast<LockType>(t));
}
void F2fs::DoCheckpoint(bool is_umount) {
SbInfo &sbi = GetSbInfo();
Checkpoint *ckpt = GetCheckpoint(&sbi);
nid_t last_nid = 0;
block_t start_blk;
Page *cp_page;
uint32_t data_sum_blocks, orphan_blocks;
void *kaddr;
uint32_t crc32 = 0;
int i;
/* Flush all the NAT/SIT pages */
while (GetPages(&sbi, CountType::kDirtyMeta))
SyncMetaPages(PageType::kMeta, LONG_MAX);
Nodemgr().NextFreeNid(&last_nid);
/*
* modify checkpoint
* version number is already updated
*/
ckpt->elapsed_time = CpuToLe(static_cast<uint64_t>(Segmgr().GetMtime()));
ckpt->valid_block_count = CpuToLe(ValidUserBlocks(&sbi));
ckpt->free_segment_count = CpuToLe(Segmgr().FreeSegments());
for (i = 0; i < 3; i++) {
ckpt->cur_node_segno[i] =
CpuToLe(Segmgr().CursegSegno(i + static_cast<int>(CursegType::kCursegHotNode)));
ckpt->cur_node_blkoff[i] =
CpuToLe(Segmgr().CursegBlkoff(i + static_cast<int>(CursegType::kCursegHotNode)));
ckpt->alloc_type[i + static_cast<int>(CursegType::kCursegHotNode)] =
Segmgr().CursegAllocType(i + static_cast<int>(CursegType::kCursegHotNode));
}
for (i = 0; i < 3; i++) {
ckpt->cur_data_segno[i] =
CpuToLe(Segmgr().CursegSegno(i + static_cast<int>(CursegType::kCursegHotData)));
ckpt->cur_data_blkoff[i] =
CpuToLe(Segmgr().CursegBlkoff(i + static_cast<int>(CursegType::kCursegHotData)));
ckpt->alloc_type[i + static_cast<int>(CursegType::kCursegHotData)] =
Segmgr().CursegAllocType(i + static_cast<int>(CursegType::kCursegHotData));
#ifdef F2FS_BU_DEBUG
std::cout << std::endl << "F2fs::DoCheckpoint " << std::endl;
std::cout << "[" << i << "] cur_data_segno " << ckpt->cur_data_segno[i]
<< ", cur_data_blkoff=" << ckpt->cur_data_blkoff[i] << std::endl;
std::cout << "[" << i << "] cur_node_segno " << ckpt->cur_node_segno[i]
<< ", cur_node_blkoff=" << ckpt->cur_node_blkoff[i] << std::endl;
#endif
}
ckpt->valid_node_count = CpuToLe(ValidNodeCount(&sbi));
ckpt->valid_inode_count = CpuToLe(ValidInodeCount(&sbi));
ckpt->next_free_nid = CpuToLe(last_nid);
/* 2 cp + n data seg summary + orphan inode blocks */
data_sum_blocks = Segmgr().NpagesForSummaryFlush();
if (data_sum_blocks < 3) {
ckpt->ckpt_flags |= kCpCompactSumFlag;
} else {
ckpt->ckpt_flags &= (~kCpCompactSumFlag);
}
orphan_blocks = (sbi.n_orphans + kOrphansPerBlock - 1) / kOrphansPerBlock;
ckpt->cp_pack_start_sum = 1 + orphan_blocks;
ckpt->cp_pack_total_block_count = 2 + data_sum_blocks + orphan_blocks;
if (is_umount) {
ckpt->ckpt_flags |= kCpUmountFlag;
ckpt->cp_pack_total_block_count += kNrCursegNodeType;
} else {
ckpt->ckpt_flags &= (~kCpUmountFlag);
}
if (sbi.n_orphans) {
ckpt->ckpt_flags |= kCpOrphanPresentFlag;
} else {
ckpt->ckpt_flags &= (~kCpOrphanPresentFlag);
}
/* update SIT/NAT bitmap */
Segmgr().GetSitBitmap(BitmapPrt(&sbi, MetaBitmap::kSitBitmap));
Nodemgr().GetNatBitmap(BitmapPrt(&sbi, MetaBitmap::kNatBitmap));
crc32 = F2fsCrc32(ckpt, LeToCpu(ckpt->checksum_offset));
*reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(ckpt) +
LeToCpu(ckpt->checksum_offset)) = CpuToLe(crc32);
start_blk = StartCpAddr(&sbi);
/* write out checkpoint buffer at block 0 */
cp_page = GrabMetaPage(start_blk++);
kaddr = PageAddress(cp_page);
memcpy(kaddr, ckpt, (1 << sbi.log_blocksize));
#if 0 // porting needed
// set_page_dirty(cp_page, this);
#else
FlushDirtyMetaPage(this, cp_page);
#endif
F2fsPutPage(cp_page, 1);
if (sbi.n_orphans) {
WriteOrphanInodes(start_blk);
start_blk += orphan_blocks;
}
Segmgr().WriteDataSummaries(start_blk);
start_blk += data_sum_blocks;
if (is_umount) {
Segmgr().WriteNodeSummaries(start_blk);
start_blk += kNrCursegNodeType;
}
/* writeout checkpoint block */
cp_page = GrabMetaPage(start_blk);
kaddr = PageAddress(cp_page);
memcpy(kaddr, ckpt, (1 << sbi.log_blocksize));
#if 0 // porting needed
// set_page_dirty(cp_page, this);
#else
FlushDirtyMetaPage(this, cp_page);
#endif
F2fsPutPage(cp_page, 1);
/* wait for previous submitted node/meta pages writeback */
#if 0 // porting needed
// while (GetPages(&sbi, kWriteback))
// congestion_wait(BLK_RW_ASYNC, HZ / 50);
// filemap_fdatawait_range(sbi.node_inode->i_mapping, 0, LONG_MAX);
// filemap_fdatawait_range(sbi.meta_inode->i_mapping, 0, LONG_MAX);
#endif
/* update user_block_counts */
sbi.last_valid_block_count = sbi.total_valid_block_count;
sbi.alloc_valid_block_count = 0;
/* Here, we only have one bio having CP pack */
#if 0 // porting needed
// if (sbi.ckpt->ckpt_flags & kCpErrorFlag)
// sbi->sb->s_flags |= MS_RDONLY;
// else
#endif
SyncMetaPages(PageType::kMetaFlush, LONG_MAX);
Segmgr().ClearPrefreeSegments();
ResetSbDirt(&sbi);
}
/**
* We guarantee that this checkpoint procedure should not fail.
*/
void F2fs::WriteCheckpoint(bool blocked, bool is_umount) {
SbInfo &sbi = GetSbInfo();
Checkpoint *ckpt = GetCheckpoint(&sbi);
uint64_t ckpt_ver;
// TODO(unknown): Need to confirm if blocked is true
// if (!blocked) {
mtx_lock(&sbi.cp_mutex);
BlockOperations();
//}
#if 0 // porting needed (bio[type] is empty)
// Segmgr().SubmitBio(PageType::kData, true);
// Segmgr().SubmitBio(PageType::kNode, true);
// Segmgr().SubmitBio(PageType::kMeta, true);
#endif
/*
* update checkpoint pack index
* Increase the version number so that
* SIT entries and seg summaries are written at correct place
*/
ckpt_ver = LeToCpu(ckpt->checkpoint_ver);
ckpt->checkpoint_ver = CpuToLe(static_cast<uint64_t>(++ckpt_ver));
/* write cached NAT/SIT entries to NAT/SIT area */
Nodemgr().FlushNatEntries();
Segmgr().FlushSitEntries();
Segmgr().ResetVictimSegmap();
/* unlock all the fs_lock[] in do_checkpoint() */
DoCheckpoint(is_umount);
UnblockOperations();
mtx_unlock(&sbi.cp_mutex);
}
void F2fs::InitOrphanInfo() {
SbInfo &sbi = GetSbInfo();
mtx_init(&sbi.orphan_inode_mutex, mtx_plain);
list_initialize(&sbi.orphan_inode_list);
sbi.n_orphans = 0;
}
#if 0 // porting needed
// int F2fs::CreateCheckpointCaches() {
// orphan_entry_slab = KmemCacheCreate("f2fs_orphan_entry",
// sizeof(OrphanInodeEntry), nullptr);
// if (unlikely(!orphan_entry_slab))
// return -ENOMEM;
// inode_entry_slab = KmemCacheCreate("f2fs_dirty_dir_entry",
// sizeof(DirInodeEntry), nullptr);
// if (unlikely(!inode_entry_slab)) {
// kmem_cache_destroy(orphan_entry_slab);
// return -ENOMEM;
// }
// return 0;
// }
// void F2fs::DestroyCheckpointCaches(void) {
// // kmem_cache_destroy(orphan_entry_slab);
// // kmem_cache_destroy(inode_entry_slab);
// }
#endif
} // namespace f2fs