| // 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 { |
| |
| /* |
| * inline functions |
| */ |
| inline SegEntry *SegMgr::GetSegEntry(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| return &sit_i->sentries[segno]; |
| } |
| |
| inline SecEntry *SegMgr::GetSecEntry(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| return &sit_i->sec_entries[GetSecNo(&sbi, segno)]; |
| } |
| |
| inline uint32_t SegMgr::GetValidBlocks(uint32_t segno, int section) { |
| /* |
| * In order to get # of valid blocks in a section instantly from many |
| * segments, f2fs manages two counting structures separately. |
| */ |
| if (section > 1) { |
| return GetSecEntry(segno)->valid_blocks; |
| } |
| return GetSegEntry(segno)->valid_blocks; |
| } |
| |
| inline void SegMgr::SegInfoFromRawSit(SegEntry *se, SitEntry *rs) { |
| se->valid_blocks = GetSitVblocks(rs); |
| se->ckpt_valid_blocks = GetSitVblocks(rs); |
| memcpy(se->cur_valid_map, rs->valid_map, kSitVBlockMapSize); |
| memcpy(se->ckpt_valid_map, rs->valid_map, kSitVBlockMapSize); |
| se->type = GetSitType(rs); |
| se->mtime = LeToCpu(uint64_t{rs->mtime}); |
| } |
| |
| inline void SegMgr::SegInfoToRawSit(SegEntry *se, SitEntry *rs) { |
| uint16_t raw_vblocks = (se->type << kSitVblocksShift) | se->valid_blocks; |
| rs->vblocks = CpuToLe(raw_vblocks); |
| memcpy(rs->valid_map, se->cur_valid_map, kSitVBlockMapSize); |
| memcpy(se->ckpt_valid_map, rs->valid_map, kSitVBlockMapSize); |
| se->ckpt_valid_blocks = se->valid_blocks; |
| rs->mtime = CpuToLe(static_cast<uint64_t>(se->mtime)); |
| } |
| |
| inline uint32_t SegMgr::FindNextInuse(FreeSegmapInfo *free_i, uint32_t max, uint32_t segno) { |
| uint32_t ret; |
| ReadLock(&free_i->segmap_lock); |
| ret = find_next_bit_le(free_i->free_segmap, max, segno); |
| ReadUnlock(&free_i->segmap_lock); |
| return ret; |
| } |
| |
| inline void SegMgr::SetFree(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t secno = segno / sbi.segs_per_sec; |
| uint32_t start_segno = secno * sbi.segs_per_sec; |
| uint32_t next; |
| |
| WriteLock(&free_i->segmap_lock); |
| clear_bit(segno, free_i->free_segmap); |
| free_i->free_segments++; |
| |
| next = find_next_bit_le(free_i->free_segmap, TotalSegs(&sbi), start_segno); |
| if (next >= start_segno + sbi.segs_per_sec) { |
| clear_bit(secno, free_i->free_secmap); |
| free_i->free_sections++; |
| } |
| WriteUnlock(&free_i->segmap_lock); |
| } |
| |
| inline void SegMgr::SetInuse(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t secno = segno / sbi.segs_per_sec; |
| set_bit(segno, free_i->free_segmap); |
| free_i->free_segments--; |
| if (!test_and_set_bit(secno, free_i->free_secmap)) |
| free_i->free_sections--; |
| } |
| |
| inline void SegMgr::SetTestAndFree(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t secno = segno / sbi.segs_per_sec; |
| uint32_t start_segno = secno * sbi.segs_per_sec; |
| uint32_t next; |
| |
| WriteLock(&free_i->segmap_lock); |
| if (test_and_clear_bit(segno, free_i->free_segmap)) { |
| free_i->free_segments++; |
| |
| next = find_next_bit_le(free_i->free_segmap, TotalSegs(&sbi), start_segno); |
| if (next >= start_segno + sbi.segs_per_sec) { |
| if (test_and_clear_bit(secno, free_i->free_secmap)) |
| free_i->free_sections++; |
| } |
| } |
| WriteUnlock(&free_i->segmap_lock); |
| } |
| |
| inline void SegMgr::SetTestAndInuse(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t secno = segno / sbi.segs_per_sec; |
| WriteLock(&free_i->segmap_lock); |
| if (!test_and_set_bit(segno, free_i->free_segmap)) { |
| free_i->free_segments--; |
| if (!test_and_set_bit(secno, free_i->free_secmap)) |
| free_i->free_sections--; |
| } |
| WriteUnlock(&free_i->segmap_lock); |
| } |
| |
| void SegMgr::GetSitBitmap(void *dst_addr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size); |
| } |
| |
| #if 0 // porting needed |
| inline block_t SegMgr::WrittenBlockCount() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| block_t vblocks; |
| |
| mtx_lock(&sit_i->sentry_lock); |
| vblocks = sit_i->written_valid_blocks; |
| mtx_unlock(&sit_i->sentry_lock); |
| |
| return vblocks; |
| } |
| #endif |
| |
| uint32_t SegMgr::FreeSegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t free_segs; |
| |
| ReadLock(&free_i->segmap_lock); |
| free_segs = free_i->free_segments; |
| ReadUnlock(&free_i->segmap_lock); |
| |
| return free_segs; |
| } |
| |
| inline int SegMgr::ReservedSegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return GetSmInfo(&sbi)->reserved_segments; |
| } |
| |
| inline uint32_t SegMgr::FreeSections() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t free_secs; |
| |
| ReadLock(&free_i->segmap_lock); |
| free_secs = free_i->free_sections; |
| ReadUnlock(&free_i->segmap_lock); |
| |
| return free_secs; |
| } |
| |
| inline uint32_t SegMgr::PrefreeSegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return GetDirtyInfo(&sbi)->nr_dirty[static_cast<int>(DirtyType::kPre)]; |
| } |
| |
| inline uint32_t SegMgr::DirtySegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return GetDirtyInfo(&sbi)->nr_dirty[static_cast<int>(DirtyType::kDirtyHotData)] + |
| GetDirtyInfo(&sbi)->nr_dirty[static_cast<int>(DirtyType::kDirtyWarmData)] + |
| GetDirtyInfo(&sbi)->nr_dirty[static_cast<int>(DirtyType::kDirtyColdData)] + |
| GetDirtyInfo(&sbi)->nr_dirty[static_cast<int>(DirtyType::kDirtyHotNode)] + |
| GetDirtyInfo(&sbi)->nr_dirty[static_cast<int>(DirtyType::kDirtyWarmNode)] + |
| GetDirtyInfo(&sbi)->nr_dirty[static_cast<int>(DirtyType::kDirtyColdNode)]; |
| } |
| |
| inline int SegMgr::OverprovisionSegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return GetSmInfo(&sbi)->ovp_segments; |
| } |
| |
| inline int SegMgr::OverprovisionSections() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return (static_cast<uint32_t>(OverprovisionSegments())) / sbi.segs_per_sec; |
| } |
| |
| inline int SegMgr::ReservedSections() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return (static_cast<uint32_t>(ReservedSegments())) / sbi.segs_per_sec; |
| } |
| |
| inline bool SegMgr::NeedSSR() { |
| #ifdef F2FS_FORCE_SSR |
| return true; |
| #else |
| // TODO: need to consider allocation mode and gc mode |
| return (FreeSections() < static_cast<uint32_t>(OverprovisionSections())); |
| #endif |
| } |
| |
| inline int SegMgr::GetSsrSegment(CursegType type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, type); |
| return GetVictimByDefault(GcType::kBgGc, type, AllocMode::kSSR, &(curseg->next_segno)); |
| } |
| |
| inline bool SegMgr::HasNotEnoughFreeSecs() { |
| return FreeSections() <= static_cast<uint32_t>(ReservedSections()); |
| } |
| |
| inline uint32_t SegMgr::Utilization() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return static_cast<uint32_t>(static_cast<int64_t>(ValidUserBlocks(&sbi)) * 100 / |
| static_cast<int64_t>(sbi.user_block_count)); |
| } |
| |
| /* |
| * Sometimes f2fs may be better to drop out-of-place update policy. |
| * So, if fs utilization is over kMinIpuUtil, then f2fs tries to write |
| * data in the original place likewise other traditional file systems. |
| * But, currently set 100 in percentage, which means it is disabled. |
| * See below need_inplace_update(). |
| */ |
| constexpr uint32_t kMinIpuUtil = 0; |
| bool SegMgr::NeedInplaceUpdate(VnodeF2fs *vnode) { |
| if (vnode->IsDir()) |
| return false; |
| if (/*NeedSSR() &&*/ Utilization() > kMinIpuUtil) |
| return true; |
| return false; |
| } |
| |
| uint32_t SegMgr::CursegSegno(int type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, static_cast<CursegType>(type)); |
| return curseg->segno; |
| } |
| |
| uint8_t SegMgr::CursegAllocType(int type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, static_cast<CursegType>(type)); |
| return curseg->alloc_type; |
| } |
| |
| inline uint16_t SegMgr::CursegBlkoff(int type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, static_cast<CursegType>(type)); |
| return curseg->next_blkoff; |
| } |
| |
| inline void SegMgr::CheckSegRange(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| [[maybe_unused]] uint32_t end_segno = GetSmInfo(&sbi)->segment_count - 1; |
| ZX_ASSERT(!(segno > end_segno)); |
| } |
| |
| #if 0 // porting needed |
| /* |
| * This function is used for only debugging. |
| * NOTE: In future, we have to remove this function. |
| */ |
| inline void SegMgr::VerifyBlockAddr(block_t blk_addr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SmInfo *sm_info = GetSmInfo(&sbi); |
| block_t total_blks = sm_info->segment_count << sbi.log_blocks_per_seg; |
| [[maybe_unused]] block_t start_addr = sm_info->seg0_blkaddr; |
| [[maybe_unused]] block_t end_addr = start_addr + total_blks - 1; |
| ZX_ASSERT(!(blk_addr < start_addr)); |
| ZX_ASSERT(!(blk_addr > end_addr)); |
| } |
| #endif |
| |
| /* |
| * Summary block is always treated as invalid block |
| */ |
| inline void SegMgr::CheckBlockCount(int segno, SitEntry *raw_sit) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SmInfo *sm_info = GetSmInfo(&sbi); |
| uint32_t end_segno = sm_info->segment_count - 1; |
| int valid_blocks = 0; |
| uint32_t i; |
| |
| /* check segment usage */ |
| ZX_ASSERT(!(GetSitVblocks(raw_sit) > sbi.blocks_per_seg)); |
| |
| /* check boundary of a given segment number */ |
| ZX_ASSERT(!(segno > (int)end_segno)); |
| |
| /* check bitmap with valid block count */ |
| for (i = 0; i < sbi.blocks_per_seg; i++) { |
| if (f2fs_test_bit(i, reinterpret_cast<char *>(raw_sit->valid_map))) |
| valid_blocks++; |
| } |
| ZX_ASSERT(GetSitVblocks(raw_sit) == valid_blocks); |
| } |
| |
| inline pgoff_t SegMgr::CurrentSitAddr(uint32_t start) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| uint32_t offset = SitBlockOffset(sit_i, start); |
| block_t blk_addr = sit_i->sit_base_addr + offset; |
| |
| CheckSegRange(start); |
| |
| /* calculate sit block address */ |
| if (f2fs_test_bit(offset, sit_i->sit_bitmap)) |
| blk_addr += sit_i->sit_blocks; |
| |
| return blk_addr; |
| } |
| |
| inline pgoff_t SegMgr::NextSitAddr(pgoff_t block_addr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| block_addr -= sit_i->sit_base_addr; |
| if (block_addr < sit_i->sit_blocks) |
| block_addr += sit_i->sit_blocks; |
| else |
| block_addr -= sit_i->sit_blocks; |
| |
| return block_addr + sit_i->sit_base_addr; |
| } |
| |
| inline void SegMgr::SetToNextSit(SitInfo *sit_i, uint32_t start) { |
| uint32_t block_off = SitBlockOffset(sit_i, start); |
| |
| if (f2fs_test_bit(block_off, sit_i->sit_bitmap)) { |
| f2fs_clear_bit(block_off, sit_i->sit_bitmap); |
| } else { |
| f2fs_set_bit(block_off, sit_i->sit_bitmap); |
| } |
| } |
| |
| inline uint64_t SegMgr::GetMtime() { |
| auto cur_time = time(nullptr); |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| |
| return sit_i->elapsed_time + cur_time - sit_i->mounted_time; |
| return 0; |
| } |
| |
| inline void SegMgr::SetSummary(Summary *sum, nid_t nid, uint32_t ofs_in_node, uint8_t version) { |
| sum->nid = CpuToLe(nid); |
| sum->ofs_in_node = CpuToLe(ofs_in_node); |
| sum->version = version; |
| } |
| |
| inline block_t SegMgr::StartSumBlock() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return StartCpAddr(&sbi) + LeToCpu(GetCheckpoint(&sbi)->cp_pack_start_sum); |
| } |
| |
| inline block_t SegMgr::SumBlkAddr(int base, int type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return StartCpAddr(&sbi) + LeToCpu(GetCheckpoint(&sbi)->cp_pack_total_block_count) - (base + 1) + |
| type; |
| } |
| |
| /* |
| * functions |
| */ |
| SegMgr::SegMgr(F2fs *fs) : fs_(fs){}; |
| |
| int SegMgr::NeedToFlush() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| |
| uint32_t pages_per_sec = (1 << sbi.log_blocks_per_seg) * sbi.segs_per_sec; |
| int node_secs = |
| ((GetPages(&sbi, CountType::kDirtyNodes) + pages_per_sec - 1) >> sbi.log_blocks_per_seg) / |
| sbi.segs_per_sec; |
| int dent_secs = |
| ((GetPages(&sbi, CountType::kDirtyDents) + pages_per_sec - 1) >> sbi.log_blocks_per_seg) / |
| sbi.segs_per_sec; |
| |
| if (sbi.por_doing) |
| return 0; |
| |
| if (FreeSections() <= static_cast<uint32_t>(node_secs + 2 * dent_secs + ReservedSections())) |
| return 1; |
| return 0; |
| } |
| |
| /** |
| * This function balances dirty node and dentry pages. |
| * In addition, it controls garbage collection. |
| */ |
| void SegMgr::BalanceFs() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| WritebackControl wbc = { |
| #if 0 // porting needed |
| // .nr_to_write = LONG_MAX, |
| // .sync_mode = WB_SYNC_ALL, |
| // .for_reclaim = 0, |
| #endif |
| }; |
| |
| if (sbi.por_doing) |
| return; |
| |
| /* |
| * We should do checkpoint when there are so many dirty node pages |
| * with enough free segments. After then, we should do GC. |
| */ |
| if (NeedToFlush()) { |
| fs_->SyncDirtyDirInodes(); |
| fs_->Nodemgr().SyncNodePages(0, &wbc); |
| } |
| |
| // TODO: need to change after gc IMPL |
| // Without GC, f2fs needs to secure free segments aggressively. |
| if (/*HasNotEnoughFreeSecs() &&*/ PrefreeSegments()) { |
| #if 0 // porting needed |
| // mtx_lock(&sbi.gc_mutex); |
| // F2fsGc(&sbi, 1); |
| #endif |
| fs_->WriteCheckpoint(false, false); |
| } |
| } |
| |
| void SegMgr::LocateDirtySegment(uint32_t segno, DirtyType dirty_type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| |
| /* need not be added */ |
| if (IsCurSeg(&sbi, segno)) |
| return; |
| |
| if (!test_and_set_bit(segno, dirty_i->dirty_segmap[static_cast<int>(dirty_type)])) |
| dirty_i->nr_dirty[static_cast<int>(dirty_type)]++; |
| |
| if (dirty_type == DirtyType::kDirty) { |
| SegEntry *sentry = GetSegEntry(segno); |
| dirty_type = static_cast<DirtyType>(sentry->type); |
| if (!test_and_set_bit(segno, dirty_i->dirty_segmap[static_cast<int>(dirty_type)])) |
| dirty_i->nr_dirty[static_cast<int>(dirty_type)]++; |
| } |
| } |
| |
| void SegMgr::RemoveDirtySegment(uint32_t segno, DirtyType dirty_type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| |
| if (test_and_clear_bit(segno, dirty_i->dirty_segmap[static_cast<int>(dirty_type)])) |
| dirty_i->nr_dirty[static_cast<int>(dirty_type)]--; |
| |
| if (dirty_type == DirtyType::kDirty) { |
| SegEntry *sentry = GetSegEntry(segno); |
| dirty_type = static_cast<DirtyType>(sentry->type); |
| if (test_and_clear_bit(segno, dirty_i->dirty_segmap[static_cast<int>(dirty_type)])) |
| dirty_i->nr_dirty[static_cast<int>(dirty_type)]--; |
| clear_bit(segno, dirty_i->victim_segmap[static_cast<int>(GcType::kFgGc)]); |
| clear_bit(segno, dirty_i->victim_segmap[static_cast<int>(GcType::kBgGc)]); |
| } |
| } |
| |
| /** |
| * Should not occur error such as -ENOMEM. |
| * Adding dirty entry into seglist is not critical operation. |
| * If a given segment is one of current working segments, it won't be added. |
| */ |
| void SegMgr::LocateDirtySegment(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| uint16_t valid_blocks; |
| |
| if (segno == kNullSegNo || IsCurSeg(&sbi, segno)) |
| return; |
| |
| mtx_lock(&dirty_i->seglist_lock); |
| |
| valid_blocks = GetValidBlocks(segno, 0); |
| |
| if (valid_blocks == 0) { |
| LocateDirtySegment(segno, DirtyType::kPre); |
| RemoveDirtySegment(segno, DirtyType::kDirty); |
| } else if (valid_blocks < sbi.blocks_per_seg) { |
| LocateDirtySegment(segno, DirtyType::kDirty); |
| } else { |
| /* Recovery routine with SSR needs this */ |
| RemoveDirtySegment(segno, DirtyType::kDirty); |
| } |
| |
| mtx_unlock(&dirty_i->seglist_lock); |
| } |
| |
| /** |
| * Should call clear_prefree_segments after checkpoint is done. |
| */ |
| void SegMgr::SetPrefreeAsFreeSegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| uint32_t segno, offset = 0; |
| uint32_t total_segs = TotalSegs(&sbi); |
| |
| mtx_lock(&dirty_i->seglist_lock); |
| while (true) { |
| segno = find_next_bit_le(dirty_i->dirty_segmap[static_cast<int>(DirtyType::kPre)], total_segs, |
| offset); |
| if (segno >= total_segs) |
| break; |
| SetTestAndFree(segno); |
| offset = segno + 1; |
| } |
| mtx_unlock(&dirty_i->seglist_lock); |
| } |
| |
| void SegMgr::ClearPrefreeSegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| uint32_t segno, offset = 0; |
| uint32_t total_segs = TotalSegs(&sbi); |
| |
| mtx_lock(&dirty_i->seglist_lock); |
| while (true) { |
| segno = find_next_bit_le(dirty_i->dirty_segmap[static_cast<int>(DirtyType::kPre)], total_segs, |
| offset); |
| if (segno >= total_segs) |
| break; |
| |
| offset = segno + 1; |
| if (test_and_clear_bit(segno, dirty_i->dirty_segmap[static_cast<int>(DirtyType::kPre)])) |
| dirty_i->nr_dirty[static_cast<int>(DirtyType::kPre)]--; |
| |
| if (TestOpt(&sbi, kMountDiscard)) |
| fs_->bc_->Trim(StartBlock(&sbi, segno), (1 << sbi.log_blocks_per_seg)); |
| } |
| mtx_unlock(&dirty_i->seglist_lock); |
| } |
| |
| void SegMgr::MarkSitEntryDirty(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| if (!test_and_set_bit_le(segno, sit_i->dirty_sentries_bitmap)) |
| sit_i->dirty_sentries++; |
| } |
| |
| void SegMgr::SetSitEntryType(CursegType type, uint32_t segno, int modified) { |
| SegEntry *se = GetSegEntry(segno); |
| se->type = static_cast<uint8_t>(type); |
| if (modified) |
| MarkSitEntryDirty(segno); |
| } |
| |
| void SegMgr::UpdateSitEntry(block_t blkaddr, int del) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SegEntry *se; |
| uint32_t segno, offset; |
| uint64_t new_vblocks; |
| |
| segno = GetSegNo(&sbi, blkaddr); |
| |
| se = GetSegEntry(segno); |
| new_vblocks = se->valid_blocks + del; |
| offset = GetSegOffFromSeg0(&sbi, blkaddr) & (sbi.blocks_per_seg - 1); |
| |
| ZX_ASSERT(!((new_vblocks >> (sizeof(uint16_t) << 3) || (new_vblocks > sbi.blocks_per_seg)))); |
| |
| se->valid_blocks = new_vblocks; |
| se->mtime = GetMtime(); |
| GetSitInfo(&sbi)->max_mtime = se->mtime; |
| |
| /* Update valid block bitmap */ |
| if (del > 0) { |
| if (f2fs_set_bit(offset, reinterpret_cast<char *>(se->cur_valid_map))) |
| ZX_ASSERT(0); |
| } else { |
| if (!f2fs_clear_bit(offset, reinterpret_cast<char *>(se->cur_valid_map))) |
| ZX_ASSERT(0); |
| } |
| if (!f2fs_test_bit(offset, reinterpret_cast<char *>(se->ckpt_valid_map))) |
| se->ckpt_valid_blocks += del; |
| |
| MarkSitEntryDirty(segno); |
| |
| /* update total number of valid blocks to be written in ckpt area */ |
| GetSitInfo(&sbi)->written_valid_blocks += del; |
| |
| if (sbi.segs_per_sec > 1) |
| GetSecEntry(segno)->valid_blocks += del; |
| } |
| |
| void SegMgr::RefreshSitEntry(block_t old_blkaddr, block_t new_blkaddr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| UpdateSitEntry(new_blkaddr, 1); |
| if (GetSegNo(&sbi, old_blkaddr) != kNullSegNo) |
| UpdateSitEntry(old_blkaddr, -1); |
| } |
| |
| void SegMgr::InvalidateBlocks(block_t addr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| uint32_t segno = GetSegNo(&sbi, addr); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| |
| ZX_ASSERT(addr != kNullAddr); |
| if (addr == kNewAddr) |
| return; |
| |
| /* add it into sit main buffer */ |
| mtx_lock(&sit_i->sentry_lock); |
| |
| UpdateSitEntry(addr, -1); |
| |
| /* add it into dirty seglist */ |
| LocateDirtySegment(segno); |
| |
| mtx_unlock(&sit_i->sentry_lock); |
| } |
| |
| /** |
| * This function should be resided under the curseg_mutex lock |
| */ |
| void SegMgr::AddSumEntry(CursegType type, Summary *sum, uint16_t offset) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, type); |
| char *addr = reinterpret_cast<char *>(curseg->sum_blk); |
| (addr) += offset * sizeof(Summary); |
| memcpy(addr, sum, sizeof(Summary)); |
| } |
| |
| /** |
| * Calculate the number of current summary pages for writing |
| */ |
| int SegMgr::NpagesForSummaryFlush() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| int total_size_bytes = 0; |
| int valid_sum_count = 0; |
| int i, sum_space; |
| |
| for (i = static_cast<int>(CursegType::kCursegHotData); |
| i <= static_cast<int>(CursegType::kCursegColdData); i++) { |
| if (sbi.ckpt->alloc_type[i] == static_cast<uint8_t>(AllocMode::kSSR)) { |
| valid_sum_count += sbi.blocks_per_seg; |
| } else { |
| valid_sum_count += CursegBlkoff(i); |
| } |
| } |
| |
| total_size_bytes = |
| valid_sum_count * (kSummarySize + 1) + sizeof(NatJournal) + 2 + sizeof(SitJournal) + 2; |
| sum_space = kPageCacheSize - kSumFooterSize; |
| if (total_size_bytes < sum_space) { |
| return 1; |
| } else if (total_size_bytes < 2 * sum_space) { |
| return 2; |
| } |
| return 3; |
| } |
| |
| /** |
| * Caller should put this summary page |
| */ |
| Page *SegMgr::GetSumPage(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| return fs_->GetMetaPage(GetSumBlock(&sbi, segno)); |
| } |
| |
| void SegMgr::WriteSumPage(SummaryBlock *sum_blk, block_t blk_addr) { |
| Page *page = fs_->GrabMetaPage(blk_addr); |
| void *kaddr = PageAddress(page); |
| memcpy(kaddr, sum_blk, kPageCacheSize); |
| #if 0 // porting needed |
| // set_page_dirty(page); |
| #endif |
| FlushDirtyMetaPage(fs_, page); |
| F2fsPutPage(page, 1); |
| } |
| |
| uint32_t SegMgr::CheckPrefreeSegments(int ofs_unit, CursegType type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| uint64_t *prefree_segmap = dirty_i->dirty_segmap[static_cast<int>(DirtyType::kPre)]; |
| uint32_t segno, next_segno, i; |
| int ofs = 0; |
| |
| /* |
| * If there is not enough reserved sections, |
| * we should not reuse prefree segments. |
| */ |
| if (HasNotEnoughFreeSecs()) |
| return kNullSegNo; |
| |
| /* |
| * NODE page should not reuse prefree segment, |
| * since those information is used for SPOR. |
| */ |
| if (IsNodeSeg(type)) |
| return kNullSegNo; |
| next: |
| segno = find_next_bit_le(prefree_segmap, TotalSegs(&sbi), ofs++); |
| ofs = ((segno / ofs_unit) * ofs_unit) + ofs_unit; |
| if (segno < TotalSegs(&sbi)) { |
| /* skip intermediate segments in a section */ |
| if (segno % ofs_unit) |
| goto next; |
| |
| /* skip if whole section is not prefree */ |
| next_segno = find_next_zero_bit(prefree_segmap, TotalSegs(&sbi), segno + 1); |
| if (next_segno - segno < static_cast<uint32_t>(ofs_unit)) |
| goto next; |
| |
| /* skip if whole section was not free at the last checkpoint */ |
| for (i = 0; i < static_cast<uint32_t>(ofs_unit); i++) { |
| if (GetSegEntry(segno)->ckpt_valid_blocks) |
| goto next; |
| } |
| return segno; |
| } |
| return kNullSegNo; |
| } |
| |
| /** |
| * Find a new segment from the free segments bitmap to right order |
| * This function should be returned with success, otherwise BUG |
| */ |
| void SegMgr::GetNewSegment(uint32_t *newseg, bool new_sec, int dir) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t total_secs = sbi.total_sections; |
| uint32_t segno, secno, zoneno; |
| uint32_t total_zones = sbi.total_sections / sbi.secs_per_zone; |
| uint32_t hint = *newseg / sbi.segs_per_sec; |
| uint32_t old_zoneno = GetZoneNoFromSegNo(&sbi, *newseg); |
| uint32_t left_start = hint; |
| bool init = true; |
| int go_left = 0; |
| int i; |
| |
| WriteLock(&free_i->segmap_lock); |
| |
| if (!new_sec && ((*newseg + 1) % sbi.segs_per_sec)) { |
| segno = find_next_zero_bit(free_i->free_segmap, TotalSegs(&sbi), *newseg + 1); |
| if (segno < TotalSegs(&sbi)) |
| goto got_it; |
| } |
| find_other_zone: |
| secno = find_next_zero_bit(free_i->free_secmap, total_secs, hint); |
| if (secno >= total_secs) { |
| if (dir == static_cast<int>(AllocDirection::kAllocRight)) { |
| secno = find_next_zero_bit(free_i->free_secmap, total_secs, 0); |
| ZX_ASSERT(!(secno >= total_secs)); |
| } else { |
| go_left = 1; |
| left_start = hint - 1; |
| } |
| } |
| if (go_left == 0) |
| goto skip_left; |
| |
| while (test_bit(left_start, free_i->free_secmap)) { |
| if (left_start > 0) { |
| left_start--; |
| continue; |
| } |
| left_start = find_next_zero_bit(free_i->free_secmap, total_secs, 0); |
| ZX_ASSERT(!(left_start >= total_secs)); |
| break; |
| } |
| secno = left_start; |
| skip_left: |
| hint = secno; |
| segno = secno * sbi.segs_per_sec; |
| zoneno = secno / sbi.secs_per_zone; |
| |
| /* give up on finding another zone */ |
| if (!init) |
| goto got_it; |
| if (sbi.secs_per_zone == 1) |
| goto got_it; |
| if (zoneno == old_zoneno) |
| goto got_it; |
| if (dir == static_cast<int>(AllocDirection::kAllocLeft)) { |
| if (!go_left && zoneno + 1 >= total_zones) |
| goto got_it; |
| if (go_left && zoneno == 0) |
| goto got_it; |
| } |
| for (i = 0; i < kNrCursegType; i++) { |
| if (CURSEG_I(&sbi, static_cast<CursegType>(i))->zone == zoneno) |
| break; |
| } |
| |
| if (i < kNrCursegType) { |
| /* zone is in user, try another */ |
| if (go_left) { |
| hint = zoneno * sbi.secs_per_zone - 1; |
| } else if (zoneno + 1 >= total_zones) { |
| hint = 0; |
| } else { |
| hint = (zoneno + 1) * sbi.secs_per_zone; |
| } |
| init = false; |
| goto find_other_zone; |
| } |
| got_it: |
| /* set it as dirty segment in free segmap */ |
| ZX_ASSERT(!test_bit(segno, free_i->free_segmap)); |
| SetInuse(segno); |
| *newseg = segno; |
| WriteUnlock(&free_i->segmap_lock); |
| } |
| |
| void SegMgr::ResetCurseg(CursegType type, int modified) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, type); |
| SummaryFooter *sum_footer; |
| |
| curseg->segno = curseg->next_segno; |
| curseg->zone = GetZoneNoFromSegNo(&sbi, curseg->segno); |
| curseg->next_blkoff = 0; |
| curseg->next_segno = kNullSegNo; |
| |
| sum_footer = &(curseg->sum_blk->footer); |
| memset(sum_footer, 0, sizeof(SummaryFooter)); |
| if (IsDataSeg(type)) |
| SetSumType(sum_footer, kSumTypeData); |
| if (IsNodeSeg(type)) |
| SetSumType(sum_footer, kSumTypeNode); |
| SetSitEntryType(type, curseg->segno, modified); |
| } |
| |
| /** |
| * Allocate a current working segment. |
| * This function always allocates a free segment in LFS manner. |
| */ |
| void SegMgr::NewCurseg(CursegType type, bool new_sec) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, type); |
| uint32_t segno = curseg->segno; |
| int dir = static_cast<int>(AllocDirection::kAllocLeft); |
| |
| WriteSumPage(curseg->sum_blk, GetSumBlock(&sbi, curseg->segno)); |
| if (type == CursegType::kCursegWarmData || type == CursegType::kCursegColdData) |
| dir = static_cast<int>(AllocDirection::kAllocRight); |
| |
| if (TestOpt(&sbi, kMountNoheap)) |
| dir = static_cast<int>(AllocDirection::kAllocRight); |
| |
| GetNewSegment(&segno, new_sec, dir); |
| curseg->next_segno = segno; |
| ResetCurseg(type, 1); |
| curseg->alloc_type = static_cast<uint8_t>(AllocMode::kLFS); |
| } |
| |
| void SegMgr::NextFreeBlkoff(CursegInfo *seg, block_t start) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SegEntry *se = GetSegEntry(seg->segno); |
| block_t ofs; |
| for (ofs = start; ofs < sbi.blocks_per_seg; ofs++) { |
| if (!f2fs_test_bit(ofs, reinterpret_cast<char *>(se->ckpt_valid_map)) && |
| !f2fs_test_bit(ofs, reinterpret_cast<char *>(se->cur_valid_map))) |
| break; |
| } |
| seg->next_blkoff = ofs; |
| } |
| |
| /** |
| * If a segment is written by LFS manner, next block offset is just obtained |
| * by increasing the current block offset. However, if a segment is written by |
| * SSR manner, next block offset obtained by calling __next_free_blkoff |
| */ |
| void SegMgr::RefreshNextBlkoff(CursegInfo *seg) { |
| if (seg->alloc_type == static_cast<uint8_t>(AllocMode::kSSR)) { |
| NextFreeBlkoff(seg, seg->next_blkoff + 1); |
| } else { |
| seg->next_blkoff++; |
| } |
| } |
| |
| /** |
| * This function always allocates a used segment (from dirty seglist) by SSR |
| * manner, so it should recover the existing segment information of valid blocks |
| */ |
| void SegMgr::ChangeCurseg(CursegType type, bool reuse) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| CursegInfo *curseg = CURSEG_I(&sbi, type); |
| uint32_t new_segno = curseg->next_segno; |
| SummaryBlock *sum_node; |
| Page *sum_page; |
| |
| WriteSumPage(curseg->sum_blk, GetSumBlock(&sbi, curseg->segno)); |
| SetTestAndInuse(new_segno); |
| |
| mtx_lock(&dirty_i->seglist_lock); |
| RemoveDirtySegment(new_segno, DirtyType::kPre); |
| RemoveDirtySegment(new_segno, DirtyType::kDirty); |
| mtx_unlock(&dirty_i->seglist_lock); |
| |
| ResetCurseg(type, 1); |
| curseg->alloc_type = static_cast<uint8_t>(AllocMode::kSSR); |
| NextFreeBlkoff(curseg, 0); |
| |
| if (reuse) { |
| sum_page = GetSumPage(new_segno); |
| sum_node = static_cast<SummaryBlock *>(PageAddress(sum_page)); |
| memcpy(curseg->sum_blk, sum_node, kSumEntrySize); |
| F2fsPutPage(sum_page, 1); |
| } |
| } |
| |
| /* |
| * flush out current segment and replace it with new segment |
| * This function should be returned with success, otherwise BUG |
| */ |
| void SegMgr::AllocateSegmentByDefault(CursegType type, bool force) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, type); |
| // uint32_t ofs_unit; |
| |
| if (force) { |
| NewCurseg(type, true); |
| goto out; |
| } |
| |
| // TODO: BUG (we can get next_segno from prefree_segment only after checkpoint) |
| // ofs_unit = NeedSSR() ? 1 : sbi.segs_per_sec; |
| // curseg->next_segno = CheckPrefreeSegments(ofs_unit, type); |
| |
| // TODO: Temporarily enable ssr for warm node segments |
| // when the kMountDisableRollForward bit is clear. |
| // It is very helpful not to waste node segments in the current sync io impl. |
| // Need to remove it after gc IMPL or cache. |
| if (curseg->next_segno != kNullSegNo) { |
| ChangeCurseg(type, false); |
| } else if (!TestOpt(&sbi, kMountDisableRollForward) && type == CursegType::kCursegWarmNode) { |
| NewCurseg(type, false); |
| } else if (NeedSSR() && GetSsrSegment(type)) { |
| ChangeCurseg(type, true); |
| } else { |
| NewCurseg(type, false); |
| } |
| out: |
| sbi.segment_count[curseg->alloc_type]++; |
| |
| #ifdef F2FS_BU_DEBUG |
| std::cout << "SegMgr::AllocateSegmentByDefault, type=" << type |
| << ", curseg->segno =" << curseg->segno << ", FreeSections()=" << FreeSections() |
| << ", PrefreeSegments()=" << PrefreeSegments() |
| << ", DirtySegments()=" << DirtySegments() << ", TotalSegs=" << TotalSegs(&sbi) |
| << ", Utilization()=" << Utilization() << std::endl; |
| #endif |
| } |
| |
| void SegMgr::AllocateNewSegments() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg; |
| uint32_t old_curseg; |
| int i; |
| |
| for (i = static_cast<int>(CursegType::kCursegHotData); |
| i <= static_cast<int>(CursegType::kCursegColdData); i++) { |
| curseg = CURSEG_I(&sbi, static_cast<CursegType>(i)); |
| old_curseg = curseg->segno; |
| AllocateSegmentByDefault(static_cast<CursegType>(i), true); |
| LocateDirtySegment(old_curseg); |
| } |
| } |
| |
| #if 0 // porting needed |
| /* |
| const segment_allocation default_salloc_ops = { |
| .allocate_segment = AllocateSegmentByDefault, |
| }; |
| */ |
| #endif |
| |
| #if 0 // porting needed (bio) |
| void SegMgr::EndIoWrite(bio *bio, int err) { |
| // const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); |
| // bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; |
| // BioPrivate *p = bio->bi_private; |
| |
| // do { |
| // page *page = bvec->bv_page; |
| |
| // if (--bvec >= bio->bi_io_vec) |
| // prefetchw(&bvec->bv_page->flags); |
| // if (!uptodate) { |
| // SetPageError(page); |
| // if (page->mapping) |
| // set_bit(AS_EIO, &page->mapping->flags); |
| // p->sbi->ckpt->ckpt_flags |= kCpErrorFlag; |
| // set_page_dirty(page); |
| // } |
| // end_page_writeback(page); |
| // dec_page_count(p->sbi, CountType::kWriteback); |
| // } while (bvec >= bio->bi_io_vec); |
| |
| // if (p->is_sync) |
| // complete(p->wait); |
| // kfree(p); |
| // bio_put(bio); |
| } |
| #endif |
| |
| #if 0 // porting needed (bio) |
| bio *SegMgr::BioAlloc(block_device *bdev, sector_t first_sector, int nr_vecs, |
| gfp_t gfp_flags) { |
| // bio *bio; |
| // repeat: |
| // /* allocate new bio */ |
| // bio = bio_alloc(gfp_flags, nr_vecs); |
| |
| // if (bio == NULL && (current->flags & PF_MEMALLOC)) { |
| // while (!bio && (nr_vecs /= 2)) |
| // bio = bio_alloc(gfp_flags, nr_vecs); |
| // } |
| // if (bio) { |
| // bio->bi_bdev = bdev; |
| // bio->bi_sector = first_sector; |
| // retry: |
| // bio->bi_private = kmalloc(sizeof(BioPrivate), |
| // GFP_NOFS | __GFP_HIGH); |
| // if (!bio->bi_private) { |
| // cond_resched(); |
| // goto retry; |
| // } |
| // } |
| // if (bio == NULL) { |
| // cond_resched(); |
| // goto repeat; |
| // } |
| // return bio; |
| return nullptr; |
| } |
| #endif |
| |
| #if 0 // porting needed (bio) |
| void SegMgr::DoSubmitBio(PageType type, bool sync) { |
| // int rw = sync ? kWriteSync : kWrite; |
| // PageType btype = type > META ? META : type; |
| |
| // if (type >= PageType::kMetaFlush) |
| // rw = kWriteFlushFua; |
| |
| // if (sbi->bio[btype]) { |
| // BioPrivate *p = sbi->bio[btype]->bi_private; |
| // p->sbi = sbi; |
| // sbi->bio[btype]->bi_end_io = f2fs_end_io_write; |
| // if (type == PageType::kMetaFlush) { |
| // DECLARE_COMPLETION_ONSTACK(wait); |
| // p->is_sync = true; |
| // p->wait = &wait; |
| // submit_bio(rw, sbi->bio[btype]); |
| // wait_for_completion(&wait); |
| // } else { |
| // p->is_sync = false; |
| // submit_bio(rw, sbi->bio[btype]); |
| // } |
| // sbi->bio[btype] = NULL; |
| // } |
| } |
| #endif |
| |
| #if 0 // porting needed (bio) |
| void SegMgr::SubmitBio(PageType type, bool sync) { |
| // down_write(&sbi->bio_sem); |
| // DoSubmitBio(type, sync); |
| // up_write(&sbi->bio_sem); |
| } |
| #endif |
| |
| void SegMgr::SubmitWritePage(Page *page, block_t blk_addr, PageType type) { |
| zx_status_t ret = fs_->bc_->Writeblk(blk_addr, page->data); |
| if (ret) { |
| std::cout << "SubmitWritePage error " << ret << std::endl; |
| } |
| #if 0 // porting needed (bio) |
| // fs_->bc_->Flush(); |
| |
| // block_device *bdev = sbi->sb->s_bdev; |
| |
| // verify_block_addr(sbi, blk_addr); |
| |
| // down_write(&sbi->bio_sem); |
| |
| // IncPageCount(sbi, CountType::kWriteback); |
| |
| // if (sbi->bio[type] && sbi->last_block_in_bio[type] != blk_addr - 1) |
| // do_submit_bio(sbi, type, false); |
| // alloc_new: |
| // if (sbi->bio[type] == NULL) |
| // sbi->bio[type] = f2fs_bio_alloc(bdev, |
| // blk_addr << (sbi->log_blocksize - 9), |
| // bio_get_nr_vecs(bdev), GFP_NOFS | __GFP_HIGH); |
| |
| // if (bio_add_page(sbi->bio[type], page, kPageCacheSize, 0) < |
| // kPageCacheSize) { |
| // do_submit_bio(sbi, type, false); |
| // goto alloc_new; |
| // } |
| |
| // sbi->last_block_in_bio[type] = blk_addr; |
| |
| // up_write(&sbi->bio_sem); |
| #endif |
| } |
| |
| bool SegMgr::HasCursegSpace(CursegType type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *curseg = CURSEG_I(&sbi, type); |
| if (curseg->next_blkoff < sbi.blocks_per_seg) { |
| return true; |
| } |
| return false; |
| } |
| |
| CursegType SegMgr::GetSegmentType2(Page *page, PageType p_type) { |
| if (p_type == PageType::kData) { |
| return CursegType::kCursegHotData; |
| } else { |
| return CursegType::kCursegHotNode; |
| } |
| } |
| |
| CursegType SegMgr::GetSegmentType4(Page *page, PageType p_type) { |
| if (p_type == PageType::kData) { |
| VnodeF2fs *vnode = static_cast<f2fs::VnodeF2fs *>(page->host); |
| |
| if (vnode->IsDir()) { |
| return CursegType::kCursegHotData; |
| } else { |
| return CursegType::kCursegColdData; |
| } |
| } else { |
| if (fs_->Nodemgr().IS_DNODE(page) && !NodeMgr::IsColdNode(page)) { |
| return CursegType::kCursegHotNode; |
| } else { |
| return CursegType::kCursegColdNode; |
| } |
| return static_cast<CursegType>(0); |
| } |
| return static_cast<CursegType>(0); |
| } |
| |
| CursegType SegMgr::GetSegmentType6(Page *page, PageType p_type) { |
| if (p_type == PageType::kData) { |
| VnodeF2fs *vnode = static_cast<f2fs::VnodeF2fs *>(page->host); |
| |
| if (vnode->IsDir()) { |
| return CursegType::kCursegHotData; |
| } else if (NodeMgr::IsColdData(page) || NodeMgr::IsColdFile(vnode)) { |
| return CursegType::kCursegColdData; |
| } else { |
| return CursegType::kCursegWarmData; |
| } |
| } else { |
| if (fs_->Nodemgr().IS_DNODE(page)) { |
| return NodeMgr::IsColdNode(page) ? CursegType::kCursegWarmNode : CursegType::kCursegHotNode; |
| } else { |
| return CursegType::kCursegColdNode; |
| } |
| } |
| return static_cast<CursegType>(0); |
| } |
| |
| CursegType SegMgr::GetSegmentType(Page *page, PageType p_type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| switch (sbi.active_logs) { |
| case 2: |
| return GetSegmentType2(page, p_type); |
| case 4: |
| return GetSegmentType4(page, p_type); |
| case 6: |
| return GetSegmentType6(page, p_type); |
| default: |
| ZX_ASSERT(0); |
| } |
| } |
| |
| void SegMgr::DoWritePage(Page *page, block_t old_blkaddr, block_t *new_blkaddr, Summary *sum, |
| PageType p_type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| CursegInfo *curseg; |
| uint32_t old_cursegno; |
| CursegType type; |
| |
| type = GetSegmentType(page, p_type); |
| curseg = CURSEG_I(&sbi, type); |
| |
| mtx_lock(&curseg->curseg_mutex); |
| *new_blkaddr = NextFreeBlkAddr(&sbi, curseg); |
| old_cursegno = curseg->segno; |
| |
| /* |
| * __add_sum_entry should be resided under the curseg_mutex |
| * because, this function updates a summary entry in the |
| * current summary block. |
| */ |
| AddSumEntry(type, sum, curseg->next_blkoff); |
| |
| mtx_lock(&sit_i->sentry_lock); |
| RefreshNextBlkoff(curseg); |
| sbi.block_count[curseg->alloc_type]++; |
| |
| /* |
| * SIT information should be updated before segment allocation, |
| * since SSR needs latest valid block information. |
| */ |
| RefreshSitEntry(old_blkaddr, *new_blkaddr); |
| |
| if (!HasCursegSpace(type)) { |
| #if 0 // porting needed |
| // sit_i->s_ops->allocate_segment(&sbi, type, false); |
| #endif |
| AllocateSegmentByDefault(type, false); |
| } |
| |
| LocateDirtySegment(old_cursegno); |
| LocateDirtySegment(GetSegNo(&sbi, old_blkaddr)); |
| mtx_unlock(&sit_i->sentry_lock); |
| |
| if (p_type == PageType::kNode) |
| fs_->Nodemgr().FillNodeFooterBlkaddr(page, NextFreeBlkAddr(&sbi, curseg)); |
| |
| /* writeout dirty page into bdev */ |
| SubmitWritePage(page, *new_blkaddr, p_type); |
| |
| mtx_unlock(&curseg->curseg_mutex); |
| } |
| |
| zx_status_t SegMgr::WriteMetaPage(Page *page, WritebackControl *wbc) { |
| #if 0 // porting needed |
| // if (wbc && wbc->for_reclaim) |
| // return kAopWritepageActivate; |
| #endif |
| SetPageWriteback(page); |
| SubmitWritePage(page, page->index, PageType::kMeta); |
| return ZX_OK; |
| } |
| |
| void SegMgr::WriteNodePage(Page *page, uint32_t nid, block_t old_blkaddr, block_t *new_blkaddr) { |
| Summary sum; |
| SetSummary(&sum, nid, 0, 0); |
| DoWritePage(page, old_blkaddr, new_blkaddr, &sum, PageType::kNode); |
| } |
| |
| void SegMgr::WriteDataPage(VnodeF2fs *vnode, Page *page, DnodeOfData *dn, block_t old_blkaddr, |
| block_t *new_blkaddr) { |
| Summary sum; |
| NodeInfo ni; |
| |
| ZX_ASSERT(old_blkaddr != kNullAddr); |
| fs_->Nodemgr().GetNodeInfo(dn->nid, &ni); |
| SetSummary(&sum, dn->nid, dn->ofs_in_node, ni.version); |
| |
| DoWritePage(page, old_blkaddr, new_blkaddr, &sum, PageType::kData); |
| } |
| |
| void SegMgr::RewriteDataPage(Page *page, block_t old_blk_addr) { |
| SubmitWritePage(page, old_blk_addr, PageType::kData); |
| } |
| |
| void SegMgr::RecoverDataPage(Page *page, Summary *sum, block_t old_blkaddr, block_t new_blkaddr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| CursegInfo *curseg; |
| uint32_t segno, old_cursegno; |
| SegEntry *se; |
| CursegType type; |
| |
| segno = GetSegNo(&sbi, new_blkaddr); |
| se = GetSegEntry(segno); |
| type = static_cast<CursegType>(se->type); |
| |
| if (se->valid_blocks == 0 && !IsCurSeg(&sbi, segno)) { |
| if (old_blkaddr == kNullAddr) { |
| type = CursegType::kCursegColdData; |
| } else { |
| type = CursegType::kCursegWarmData; |
| } |
| } |
| curseg = CURSEG_I(&sbi, type); |
| |
| mtx_lock(&curseg->curseg_mutex); |
| mtx_lock(&sit_i->sentry_lock); |
| |
| old_cursegno = curseg->segno; |
| |
| /* change the current segment */ |
| if (segno != curseg->segno) { |
| curseg->next_segno = segno; |
| ChangeCurseg(type, true); |
| } |
| |
| curseg->next_blkoff = GetSegOffFromSeg0(&sbi, new_blkaddr) & (sbi.blocks_per_seg - 1); |
| AddSumEntry(type, sum, curseg->next_blkoff); |
| |
| RefreshSitEntry(old_blkaddr, new_blkaddr); |
| |
| LocateDirtySegment(old_cursegno); |
| LocateDirtySegment(GetSegNo(&sbi, old_blkaddr)); |
| |
| mtx_unlock(&sit_i->sentry_lock); |
| mtx_unlock(&curseg->curseg_mutex); |
| } |
| |
| void SegMgr::RewriteNodePage(Page *page, Summary *sum, block_t old_blkaddr, block_t new_blkaddr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| CursegType type = CursegType::kCursegWarmNode; |
| CursegInfo *curseg; |
| uint32_t segno, old_cursegno; |
| block_t next_blkaddr = NodeMgr::NextBlkaddrOfNode(page); |
| uint32_t next_segno = GetSegNo(&sbi, next_blkaddr); |
| |
| curseg = CURSEG_I(&sbi, type); |
| |
| mtx_lock(&curseg->curseg_mutex); |
| mtx_lock(&sit_i->sentry_lock); |
| |
| segno = GetSegNo(&sbi, new_blkaddr); |
| old_cursegno = curseg->segno; |
| |
| /* change the current segment */ |
| if (segno != curseg->segno) { |
| curseg->next_segno = segno; |
| ChangeCurseg(type, true); |
| } |
| curseg->next_blkoff = GetSegOffFromSeg0(&sbi, new_blkaddr) & (sbi.blocks_per_seg - 1); |
| AddSumEntry(type, sum, curseg->next_blkoff); |
| |
| /* change the current log to the next block addr in advance */ |
| if (next_segno != segno) { |
| curseg->next_segno = next_segno; |
| ChangeCurseg(type, true); |
| } |
| curseg->next_blkoff = GetSegOffFromSeg0(&sbi, next_blkaddr) & (sbi.blocks_per_seg - 1); |
| |
| /* rewrite node page */ |
| SetPageWriteback(page); |
| SubmitWritePage(page, new_blkaddr, PageType::kNode); |
| #if 0 // porting needed |
| SubmitBio(NODE, true); |
| #endif |
| RefreshSitEntry(old_blkaddr, new_blkaddr); |
| |
| LocateDirtySegment(old_cursegno); |
| LocateDirtySegment(GetSegNo(&sbi, old_blkaddr)); |
| |
| mtx_unlock(&sit_i->sentry_lock); |
| mtx_unlock(&curseg->curseg_mutex); |
| } |
| |
| int SegMgr::ReadCompactedSummaries() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| Checkpoint *ckpt = GetCheckpoint(&sbi); |
| CursegInfo *seg_i; |
| uint8_t *kaddr; |
| Page *page; |
| block_t start; |
| int i, j, offset; |
| |
| start = StartSumBlock(); |
| |
| page = fs_->GetMetaPage(start++); |
| kaddr = static_cast<uint8_t *>(PageAddress(page)); |
| |
| /* Step 1: restore nat cache */ |
| seg_i = CURSEG_I(&sbi, CursegType::kCursegHotData); |
| memcpy(&seg_i->sum_blk->n_nats, kaddr, kSumJournalSize); |
| |
| /* Step 2: restore sit cache */ |
| seg_i = CURSEG_I(&sbi, CursegType::kCursegColdData); |
| memcpy(&seg_i->sum_blk->n_sits, kaddr + kSumJournalSize, kSumJournalSize); |
| offset = 2 * kSumJournalSize; |
| |
| /* Step 3: restore summary entries */ |
| for (i = static_cast<int>(CursegType::kCursegHotData); |
| i <= static_cast<int>(CursegType::kCursegColdData); i++) { |
| uint16_t blk_off; |
| uint32_t segno; |
| |
| seg_i = CURSEG_I(&sbi, static_cast<CursegType>(i)); |
| segno = LeToCpu(ckpt->cur_data_segno[i]); |
| blk_off = LeToCpu(ckpt->cur_data_blkoff[i]); |
| seg_i->next_segno = segno; |
| ResetCurseg(static_cast<CursegType>(i), 0); |
| seg_i->alloc_type = ckpt->alloc_type[i]; |
| seg_i->next_blkoff = blk_off; |
| |
| if (seg_i->alloc_type == static_cast<uint8_t>(AllocMode::kSSR)) |
| blk_off = sbi.blocks_per_seg; |
| |
| for (j = 0; j < blk_off; j++) { |
| Summary *s; |
| s = reinterpret_cast<Summary *>(kaddr + offset); |
| seg_i->sum_blk->entries[j] = *s; |
| offset += kSummarySize; |
| if (offset + kSummarySize <= kPageCacheSize - kSumFooterSize) |
| continue; |
| |
| F2fsPutPage(page, 1); |
| page = nullptr; |
| |
| page = fs_->GetMetaPage(start++); |
| kaddr = static_cast<uint8_t *>(PageAddress(page)); |
| offset = 0; |
| } |
| } |
| F2fsPutPage(page, 1); |
| return 0; |
| } |
| |
| int SegMgr::ReadNormalSummaries(int type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| Checkpoint *ckpt = GetCheckpoint(&sbi); |
| SummaryBlock *sum; |
| CursegInfo *curseg; |
| Page *new_page; |
| uint16_t blk_off; |
| uint32_t segno = 0; |
| block_t blk_addr = 0; |
| |
| /* get segment number and block addr */ |
| if (IsDataSeg(static_cast<CursegType>(type))) { |
| segno = LeToCpu(ckpt->cur_data_segno[type]); |
| blk_off = LeToCpu(ckpt->cur_data_blkoff[type - static_cast<int>(CursegType::kCursegHotData)]); |
| if (ckpt->ckpt_flags & kCpUmountFlag) { |
| blk_addr = SumBlkAddr(kNrCursegType, type); |
| } else |
| blk_addr = SumBlkAddr(kNrCursegDataType, type); |
| } else { |
| segno = LeToCpu(ckpt->cur_node_segno[type - static_cast<int>(CursegType::kCursegHotNode)]); |
| blk_off = LeToCpu(ckpt->cur_node_blkoff[type - static_cast<int>(CursegType::kCursegHotNode)]); |
| if (ckpt->ckpt_flags & kCpUmountFlag) { |
| blk_addr = SumBlkAddr(kNrCursegNodeType, type - static_cast<int>(CursegType::kCursegHotNode)); |
| } else |
| blk_addr = GetSumBlock(&sbi, segno); |
| } |
| |
| new_page = fs_->GetMetaPage(blk_addr); |
| sum = static_cast<SummaryBlock *>(PageAddress(new_page)); |
| |
| if (IsNodeSeg(static_cast<CursegType>(type))) { |
| if (ckpt->ckpt_flags & kCpUmountFlag) { |
| Summary *ns = &sum->entries[0]; |
| uint32_t i; |
| for (i = 0; i < sbi.blocks_per_seg; i++, ns++) { |
| ns->version = 0; |
| ns->ofs_in_node = 0; |
| } |
| } else { |
| if (NodeMgr::RestoreNodeSummary(fs_, segno, sum)) { |
| F2fsPutPage(new_page, 1); |
| return -EINVAL; |
| } |
| } |
| } |
| |
| /* set uncompleted segment to curseg */ |
| curseg = CURSEG_I(&sbi, static_cast<CursegType>(type)); |
| mtx_lock(&curseg->curseg_mutex); |
| memcpy(curseg->sum_blk, sum, kPageCacheSize); |
| curseg->next_segno = segno; |
| ResetCurseg(static_cast<CursegType>(type), 0); |
| curseg->alloc_type = ckpt->alloc_type[type]; |
| curseg->next_blkoff = blk_off; |
| |
| mtx_unlock(&curseg->curseg_mutex); |
| F2fsPutPage(new_page, 1); |
| return 0; |
| } |
| |
| zx_status_t SegMgr::RestoreCursegSummaries() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| int type = static_cast<int>(CursegType::kCursegHotData); |
| |
| if (sbi.ckpt->ckpt_flags & kCpCompactSumFlag) { |
| /* restore for compacted data summary */ |
| if (ReadCompactedSummaries()) |
| return ZX_ERR_INVALID_ARGS; |
| type = static_cast<int>(CursegType::kCursegHotNode); |
| } |
| |
| for (; type <= static_cast<int>(CursegType::kCursegColdNode); type++) { |
| if (ReadNormalSummaries(type)) |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return ZX_OK; |
| } |
| |
| void SegMgr::WriteCompactedSummaries(block_t blkaddr) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| Page *page; |
| uint8_t *kaddr; |
| Summary *summary; |
| CursegInfo *seg_i; |
| int written_size = 0; |
| int i, j; |
| |
| page = fs_->GrabMetaPage(blkaddr++); |
| kaddr = static_cast<uint8_t *>(PageAddress(page)); |
| |
| /* Step 1: write nat cache */ |
| seg_i = CURSEG_I(&sbi, CursegType::kCursegHotData); |
| memcpy(kaddr, &seg_i->sum_blk->n_nats, kSumJournalSize); |
| written_size += kSumJournalSize; |
| |
| /* Step 2: write sit cache */ |
| seg_i = CURSEG_I(&sbi, CursegType::kCursegColdData); |
| memcpy(kaddr + written_size, &seg_i->sum_blk->n_sits, kSumJournalSize); |
| written_size += kSumJournalSize; |
| |
| // set_page_dirty(page); |
| FlushDirtyMetaPage(fs_, page); |
| |
| /* Step 3: write summary entries */ |
| for (i = static_cast<int>(CursegType::kCursegHotData); |
| i <= static_cast<int>(CursegType::kCursegColdData); i++) { |
| uint16_t blkoff; |
| seg_i = CURSEG_I(&sbi, static_cast<CursegType>(i)); |
| if (sbi.ckpt->alloc_type[i] == static_cast<uint8_t>(AllocMode::kSSR)) { |
| blkoff = sbi.blocks_per_seg; |
| } else { |
| blkoff = CursegBlkoff(i); |
| } |
| |
| for (j = 0; j < blkoff; j++) { |
| if (!page) { |
| page = fs_->GrabMetaPage(blkaddr++); |
| kaddr = static_cast<uint8_t *>(PageAddress(page)); |
| written_size = 0; |
| } |
| summary = reinterpret_cast<Summary *>(kaddr + written_size); |
| *summary = seg_i->sum_blk->entries[j]; |
| written_size += kSummarySize; |
| #if 0 // porting needed |
| // set_page_dirty(page); |
| #endif |
| FlushDirtyMetaPage(fs_, page); |
| |
| if (written_size + kSummarySize <= kPageCacheSize - kSumFooterSize) |
| continue; |
| |
| F2fsPutPage(page, 1); |
| page = nullptr; |
| } |
| } |
| if (page) |
| F2fsPutPage(page, 1); |
| } |
| |
| void SegMgr::WriteNormalSummaries(block_t blkaddr, CursegType type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| int i, end; |
| |
| if (IsDataSeg(type)) { |
| end = static_cast<int>(type) + kNrCursegDataType; |
| } else { |
| end = static_cast<int>(type) + kNrCursegNodeType; |
| } |
| |
| for (i = static_cast<int>(type); i < end; i++) { |
| CursegInfo *sum = CURSEG_I(&sbi, static_cast<CursegType>(i)); |
| mtx_lock(&sum->curseg_mutex); |
| WriteSumPage(sum->sum_blk, blkaddr + (i - static_cast<int>(type))); |
| mtx_unlock(&sum->curseg_mutex); |
| } |
| } |
| |
| void SegMgr::WriteDataSummaries(block_t start_blk) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| if (sbi.ckpt->ckpt_flags & kCpCompactSumFlag) { |
| WriteCompactedSummaries(start_blk); |
| } else { |
| WriteNormalSummaries(start_blk, CursegType::kCursegHotData); |
| } |
| } |
| |
| void SegMgr::WriteNodeSummaries(block_t start_blk) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| if (sbi.ckpt->ckpt_flags & kCpUmountFlag) |
| WriteNormalSummaries(start_blk, CursegType::kCursegHotNode); |
| } |
| |
| int SegMgr::LookupJournalInCursum(SummaryBlock *sum, JournalType type, uint32_t val, int alloc) { |
| int i; |
| |
| if (type == JournalType::kNatJournal) { |
| for (i = 0; i < NatsInCursum(sum); i++) { |
| if (LeToCpu(NidInJournal(sum, i)) == val) |
| return i; |
| } |
| if (alloc && NatsInCursum(sum) < static_cast<int>(kNatJournalEntries)) |
| return UpdateNatsInCursum(sum, 1); |
| } else if (type == JournalType::kSitJournal) { |
| for (i = 0; i < SitsInCursum(sum); i++) { |
| if (LeToCpu(SegnoInJournal(sum, i)) == val) |
| return i; |
| } |
| if (alloc && SitsInCursum(sum) < static_cast<int>(kSitJournalEntries)) |
| return UpdateSitsInCursum(sum, 1); |
| } |
| return -1; |
| } |
| |
| Page *SegMgr::GetCurrentSitPage(uint32_t segno) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| uint32_t offset = SitBlockOffset(sit_i, segno); |
| block_t blk_addr = sit_i->sit_base_addr + offset; |
| |
| CheckSegRange(segno); |
| |
| /* calculate sit block address */ |
| if (f2fs_test_bit(offset, sit_i->sit_bitmap)) |
| blk_addr += sit_i->sit_blocks; |
| |
| return fs_->GetMetaPage(blk_addr); |
| } |
| |
| Page *SegMgr::GetNextSitPage(uint32_t start) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| Page *src_page, *dst_page; |
| pgoff_t src_off, dst_off; |
| void *src_addr, *dst_addr; |
| |
| src_off = CurrentSitAddr(start); |
| dst_off = NextSitAddr(src_off); |
| |
| /* get current sit block page without lock */ |
| src_page = fs_->GetMetaPage(src_off); |
| dst_page = fs_->GrabMetaPage(dst_off); |
| ZX_ASSERT(!PageDirty(src_page)); |
| |
| src_addr = PageAddress(src_page); |
| dst_addr = PageAddress(dst_page); |
| memcpy(dst_addr, src_addr, kPageCacheSize); |
| |
| #if 0 // porting needed |
| // set_page_dirty(dst_page); |
| #endif |
| F2fsPutPage(src_page, 1); |
| |
| SetToNextSit(sit_i, start); |
| |
| return dst_page; |
| } |
| |
| bool SegMgr::FlushSitsInJournal() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| CursegInfo *curseg = CURSEG_I(&sbi, CursegType::kCursegColdData); |
| SummaryBlock *sum = curseg->sum_blk; |
| int i; |
| |
| // If the journal area in the current summary is full of sit entries, |
| // all the sit entries will be flushed. Otherwise the sit entries |
| // are not able to replace with newly hot sit entries. |
| if ((SitsInCursum(sum) + sit_i->dirty_sentries) > static_cast<int>(kSitJournalEntries)) { |
| for (i = SitsInCursum(sum) - 1; i >= 0; i--) { |
| uint32_t segno; |
| segno = LeToCpu(SegnoInJournal(sum, i)); |
| MarkSitEntryDirty(segno); |
| } |
| UpdateSitsInCursum(sum, -SitsInCursum(sum)); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * CP calls this function, which flushes SIT entries including SitJournal, |
| * and moves prefree segs to free segs. |
| */ |
| void SegMgr::FlushSitEntries() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| uint64_t *bitmap = sit_i->dirty_sentries_bitmap; |
| CursegInfo *curseg = CURSEG_I(&sbi, CursegType::kCursegColdData); |
| SummaryBlock *sum = curseg->sum_blk; |
| uint64_t nsegs = TotalSegs(&sbi); |
| Page *page = nullptr; |
| SitBlock *raw_sit = nullptr; |
| uint32_t start = 0, end = 0; |
| uint32_t segno = -1; |
| bool flushed; |
| |
| mtx_lock(&curseg->curseg_mutex); |
| mtx_lock(&sit_i->sentry_lock); |
| |
| /* |
| * "flushed" indicates whether sit entries in journal are flushed |
| * to the SIT area or not. |
| */ |
| flushed = FlushSitsInJournal(); |
| |
| while ((segno = find_next_bit_le(bitmap, nsegs, segno + 1)) < nsegs) { |
| SegEntry *se = GetSegEntry(segno); |
| int sit_offset, offset = -1; |
| |
| sit_offset = SitEntryOffset(sit_i, segno); |
| |
| if (!flushed) { |
| offset = LookupJournalInCursum(sum, JournalType::kSitJournal, segno, 1); |
| } |
| |
| if (offset >= 0) { |
| SetSegnoInJournal(sum, offset, CpuToLe(segno)); |
| SegInfoToRawSit(se, SitInJournal(sum, offset)); |
| } else { |
| if (!page || (start > segno) || (segno > end)) { |
| if (page) { |
| // set_page_dirty(page, fs_); |
| FlushDirtyMetaPage(fs_, page); |
| F2fsPutPage(page, 1); |
| page = nullptr; |
| } |
| |
| start = StartSegNo(sit_i, segno); |
| end = start + kSitEntryPerBlock - 1; |
| |
| /* read sit block that will be updated */ |
| page = GetNextSitPage(start); |
| raw_sit = static_cast<SitBlock *>(PageAddress(page)); |
| } |
| |
| /* udpate entry in SIT block */ |
| SegInfoToRawSit(se, &raw_sit->entries[sit_offset]); |
| } |
| __clear_bit(segno, bitmap); |
| sit_i->dirty_sentries--; |
| } |
| mtx_unlock(&sit_i->sentry_lock); |
| mtx_unlock(&curseg->curseg_mutex); |
| |
| /* writeout last modified SIT block */ |
| #if 0 // porting needed |
| // set_page_dirty(page, fs_); |
| #endif |
| FlushDirtyMetaPage(fs_, page); |
| F2fsPutPage(page, 1); |
| |
| SetPrefreeAsFreeSegments(); |
| } |
| |
| /* |
| * Build |
| */ |
| zx_status_t SegMgr::BuildSitInfo() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| const SuperBlock *raw_super = RawSuper(&sbi); |
| Checkpoint *ckpt = GetCheckpoint(&sbi); |
| SitInfo *sit_i; |
| uint32_t sit_segs, start; |
| char *src_bitmap, *dst_bitmap; |
| uint32_t bitmap_size; |
| |
| /* allocate memory for SIT information */ |
| sit_i = static_cast<SitInfo *>(malloc(sizeof(SitInfo))); |
| memset(sit_i, 0, sizeof(SitInfo)); |
| if (!sit_i) |
| return ZX_ERR_NO_MEMORY; |
| |
| GetSmInfo(&sbi)->SitInfo = sit_i; |
| |
| sit_i->sentries = static_cast<SegEntry *>(calloc(TotalSegs(&sbi), sizeof(SegEntry))); |
| if (!sit_i->sentries) |
| return ZX_ERR_NO_MEMORY; |
| |
| bitmap_size = BitmapSize(TotalSegs(&sbi)); |
| sit_i->dirty_sentries_bitmap = static_cast<uint64_t *>(malloc(bitmap_size)); |
| memset(sit_i->dirty_sentries_bitmap, 0, bitmap_size); |
| if (!sit_i->dirty_sentries_bitmap) |
| return ZX_ERR_NO_MEMORY; |
| |
| for (start = 0; start < TotalSegs(&sbi); start++) { |
| sit_i->sentries[start].cur_valid_map = static_cast<uint8_t *>(malloc(kSitVBlockMapSize)); |
| memset(sit_i->sentries[start].cur_valid_map, 0, kSitVBlockMapSize); |
| sit_i->sentries[start].ckpt_valid_map = static_cast<uint8_t *>(malloc(kSitVBlockMapSize)); |
| memset(sit_i->sentries[start].ckpt_valid_map, 0, kSitVBlockMapSize); |
| if (!sit_i->sentries[start].cur_valid_map || !sit_i->sentries[start].ckpt_valid_map) |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| if (sbi.segs_per_sec > 1) { |
| sit_i->sec_entries = static_cast<SecEntry *>(calloc(sbi.total_sections, sizeof(SecEntry))); |
| if (!sit_i->sec_entries) |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| /* get information related with SIT */ |
| sit_segs = LeToCpu(raw_super->segment_count_sit) >> 1; |
| |
| /* setup SIT bitmap from ckeckpoint pack */ |
| bitmap_size = BitmapSize(&sbi, MetaBitmap::kSitBitmap); |
| src_bitmap = static_cast<char *>(BitmapPrt(&sbi, MetaBitmap::kSitBitmap)); |
| |
| dst_bitmap = static_cast<char *>(malloc(bitmap_size)); |
| memset(dst_bitmap, 0, bitmap_size); |
| if (!dst_bitmap) |
| return ZX_ERR_NO_MEMORY; |
| memcpy(dst_bitmap, src_bitmap, bitmap_size); |
| |
| #if 0 // porting needed |
| /* init SIT information */ |
| // sit_i->s_ops = &default_salloc_ops; |
| #endif |
| auto cur_time = time(nullptr); |
| |
| sit_i->sit_base_addr = LeToCpu(raw_super->sit_blkaddr); |
| sit_i->sit_blocks = sit_segs << sbi.log_blocks_per_seg; |
| sit_i->written_valid_blocks = LeToCpu(ckpt->valid_block_count); |
| sit_i->sit_bitmap = dst_bitmap; |
| sit_i->bitmap_size = bitmap_size; |
| sit_i->dirty_sentries = 0; |
| sit_i->sents_per_block = kSitEntryPerBlock; |
| sit_i->elapsed_time = LeToCpu(sbi.ckpt->elapsed_time); |
| sit_i->mounted_time = cur_time; |
| mtx_init(&sit_i->sentry_lock, mtx_plain); |
| return ZX_OK; |
| } |
| |
| zx_status_t SegMgr::BuildFreeSegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SmInfo *sm_info = GetSmInfo(&sbi); |
| FreeSegmapInfo *free_i; |
| uint32_t bitmap_size, sec_bitmap_size; |
| |
| /* allocate memory for free segmap information */ |
| free_i = static_cast<FreeSegmapInfo *>(malloc(sizeof(FreeSegmapInfo))); |
| memset(free_i, 0, sizeof(FreeSegmapInfo)); |
| |
| if (!free_i) |
| return ZX_ERR_NO_MEMORY; |
| |
| GetSmInfo(&sbi)->free_info = free_i; |
| |
| bitmap_size = BitmapSize(TotalSegs(&sbi)); |
| free_i->free_segmap = static_cast<uint64_t *>(malloc(bitmap_size)); |
| if (!free_i->free_segmap) |
| return ZX_ERR_NO_MEMORY; |
| |
| sec_bitmap_size = BitmapSize(sbi.total_sections); |
| free_i->free_secmap = static_cast<uint64_t *>(malloc(sec_bitmap_size)); |
| if (!free_i->free_secmap) |
| return ZX_ERR_NO_MEMORY; |
| |
| /* set all segments as dirty temporarily */ |
| memset(free_i->free_segmap, 0xff, bitmap_size); |
| memset(free_i->free_secmap, 0xff, sec_bitmap_size); |
| |
| /* init free segmap information */ |
| free_i->start_segno = GetSegNoFromSeg0(&sbi, sm_info->main_blkaddr); |
| free_i->free_segments = 0; |
| free_i->free_sections = 0; |
| |
| RwlockInit(&free_i->segmap_lock); |
| return ZX_OK; |
| } |
| |
| zx_status_t SegMgr::BuildCurseg() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *array = nullptr; |
| int i; |
| array = static_cast<CursegInfo *>(calloc(kNrCursegType, sizeof(*array))); |
| |
| if (!array) |
| return ZX_ERR_NO_MEMORY; |
| |
| GetSmInfo(&sbi)->curseg_array = array; |
| |
| for (i = 0; i < kNrCursegType; i++) { |
| mtx_init(&array[i].curseg_mutex, mtx_plain); |
| array[i].sum_blk = static_cast<SummaryBlock *>(malloc(kPageCacheSize)); |
| memset(array[i].sum_blk, 0, kPageCacheSize); |
| if (!array[i].sum_blk) |
| return ZX_ERR_NO_MEMORY; |
| array[i].segno = kNullSegNo; |
| array[i].next_blkoff = 0; |
| } |
| return RestoreCursegSummaries(); |
| } |
| |
| void SegMgr::BuildSitEntries() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| CursegInfo *curseg = CURSEG_I(&sbi, CursegType::kCursegColdData); |
| SummaryBlock *sum = curseg->sum_blk; |
| uint32_t start; |
| |
| for (start = 0; start < TotalSegs(&sbi); start++) { |
| SegEntry *se = &sit_i->sentries[start]; |
| SitBlock *sit_blk; |
| SitEntry sit; |
| Page *page; |
| int i; |
| |
| mtx_lock(&curseg->curseg_mutex); |
| for (i = 0; i < SitsInCursum(sum); i++) { |
| if (LeToCpu(SegnoInJournal(sum, i)) == start) { |
| sit = *SitInJournal(sum, i); |
| mtx_unlock(&curseg->curseg_mutex); |
| goto got_it; |
| } |
| } |
| mtx_unlock(&curseg->curseg_mutex); |
| page = GetCurrentSitPage(start); |
| sit_blk = static_cast<SitBlock *>(PageAddress(page)); |
| sit = sit_blk->entries[SitEntryOffset(sit_i, start)]; |
| F2fsPutPage(page, 1); |
| got_it: |
| CheckBlockCount(start, &sit); |
| SegInfoFromRawSit(se, &sit); |
| if (sbi.segs_per_sec > 1) { |
| SecEntry *e = GetSecEntry(start); |
| e->valid_blocks += se->valid_blocks; |
| } |
| } |
| } |
| |
| void SegMgr::InitFreeSegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| uint32_t start; |
| int type; |
| |
| for (start = 0; start < TotalSegs(&sbi); start++) { |
| SegEntry *sentry = GetSegEntry(start); |
| if (!sentry->valid_blocks) |
| SetFree(start); |
| } |
| |
| /* set use the current segments */ |
| for (type = static_cast<int>(CursegType::kCursegHotData); |
| type <= static_cast<int>(CursegType::kCursegColdNode); type++) { |
| CursegInfo *curseg_t = CURSEG_I(&sbi, static_cast<CursegType>(type)); |
| SetTestAndInuse(curseg_t->segno); |
| } |
| } |
| |
| void SegMgr::InitDirtySegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| FreeSegmapInfo *free_i = GetFreeInfo(&sbi); |
| uint32_t segno = 0, offset = 0; |
| uint16_t valid_blocks; |
| int full_block_cnt = 0, dirty_block_cnt = 0; |
| |
| while (segno < TotalSegs(&sbi)) { |
| /* find dirty segment based on free segmap */ |
| segno = FindNextInuse(free_i, TotalSegs(&sbi), offset); |
| if (segno >= TotalSegs(&sbi)) |
| break; |
| offset = segno + 1; |
| valid_blocks = GetValidBlocks(segno, 0); |
| if (valid_blocks >= sbi.blocks_per_seg || !valid_blocks) { |
| full_block_cnt++; |
| continue; |
| } |
| mtx_lock(&dirty_i->seglist_lock); |
| LocateDirtySegment(segno, DirtyType::kDirty); |
| dirty_block_cnt++; |
| mtx_unlock(&dirty_i->seglist_lock); |
| } |
| |
| #ifdef F2FS_BU_DEBUG |
| std::cout << "SegMgr::InitDirtySegmap, full_block_cnt=" << full_block_cnt |
| << ", dirty_block_cnt=" << dirty_block_cnt << std::endl; |
| #endif |
| } |
| |
| zx_status_t SegMgr::InitVictimSegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| uint32_t bitmap_size = BitmapSize(TotalSegs(&sbi)); |
| |
| dirty_i->victim_segmap[static_cast<int>(GcType::kFgGc)] = |
| static_cast<uint64_t *>(malloc(bitmap_size)); |
| memset(dirty_i->victim_segmap[static_cast<int>(GcType::kFgGc)], 0, bitmap_size); |
| dirty_i->victim_segmap[static_cast<int>(GcType::kBgGc)] = |
| static_cast<uint64_t *>(malloc(bitmap_size)); |
| memset(dirty_i->victim_segmap[static_cast<int>(GcType::kBgGc)], 0, bitmap_size); |
| |
| if (!dirty_i->victim_segmap[static_cast<int>(GcType::kFgGc)] || |
| !dirty_i->victim_segmap[static_cast<int>(GcType::kBgGc)]) |
| return ZX_ERR_NO_MEMORY; |
| return ZX_OK; |
| } |
| |
| zx_status_t SegMgr::BuildDirtySegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i; |
| uint32_t bitmap_size, i; |
| |
| dirty_i = static_cast<DirtySeglistInfo *>(malloc(sizeof(DirtySeglistInfo))); |
| memset(dirty_i, 0, sizeof(DirtySeglistInfo)); |
| if (!dirty_i) |
| return ZX_ERR_NO_MEMORY; |
| |
| GetSmInfo(&sbi)->dirty_info = dirty_i; |
| mtx_init(&dirty_i->seglist_lock, mtx_plain); |
| |
| bitmap_size = BitmapSize(TotalSegs(&sbi)); |
| |
| for (i = 0; i < static_cast<int>(DirtyType::kNrDirtytype); i++) { |
| dirty_i->dirty_segmap[i] = static_cast<uint64_t *>(malloc(bitmap_size)); |
| memset(dirty_i->dirty_segmap[i], 0, bitmap_size); |
| dirty_i->nr_dirty[i] = 0; |
| if (!dirty_i->dirty_segmap[i]) |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| InitDirtySegmap(); |
| return InitVictimSegmap(); |
| } |
| |
| /** |
| * Update min, max modified time for cost-benefit GC algorithm |
| */ |
| void SegMgr::InitMinMaxMtime() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| uint32_t segno; |
| |
| mtx_lock(&sit_i->sentry_lock); |
| |
| sit_i->min_mtime = LLONG_MAX; |
| |
| for (segno = 0; segno < TotalSegs(&sbi); segno += sbi.segs_per_sec) { |
| uint32_t i; |
| uint64_t mtime = 0; |
| |
| for (i = 0; i < sbi.segs_per_sec; i++) |
| mtime += GetSegEntry(segno + i)->mtime; |
| |
| mtime = DivU64(mtime, sbi.segs_per_sec); |
| |
| if (sit_i->min_mtime > mtime) |
| sit_i->min_mtime = mtime; |
| } |
| sit_i->max_mtime = GetMtime(); |
| mtx_unlock(&sit_i->sentry_lock); |
| } |
| |
| zx_status_t SegMgr::BuildSegmentManager() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| const SuperBlock *raw_super = RawSuper(&sbi); |
| Checkpoint *ckpt = GetCheckpoint(&sbi); |
| SmInfo *sm_info = nullptr; |
| zx_status_t err = 0; |
| |
| sm_info = new SmInfo; |
| if (!sm_info) |
| return ZX_ERR_NO_MEMORY; |
| |
| /* init sm info */ |
| sbi.sm_info = sm_info; |
| |
| list_initialize(&sm_info->wblist_head); |
| SpinLockInit(&sm_info->wblist_lock); |
| sm_info->seg0_blkaddr = LeToCpu(raw_super->segment0_blkaddr); |
| sm_info->main_blkaddr = LeToCpu(raw_super->main_blkaddr); |
| sm_info->segment_count = LeToCpu(raw_super->segment_count); |
| sm_info->reserved_segments = LeToCpu(ckpt->rsvd_segment_count); |
| sm_info->ovp_segments = LeToCpu(ckpt->overprov_segment_count); |
| sm_info->main_segments = LeToCpu(raw_super->segment_count_main); |
| sm_info->ssa_blkaddr = LeToCpu(raw_super->ssa_blkaddr); |
| |
| err = BuildSitInfo(); |
| if (err) |
| return err; |
| |
| err = BuildFreeSegmap(); |
| if (err) |
| return err; |
| |
| err = BuildCurseg(); |
| if (err) |
| return err; |
| |
| /* reinit free segmap based on SIT */ |
| BuildSitEntries(); |
| |
| InitFreeSegmap(); |
| err = BuildDirtySegmap(); |
| if (err) |
| return err; |
| |
| #ifdef F2FS_BU_DEBUG |
| std::cout << "SegMgr::BuildSegmentManager(), TotalSegs(&sbi)=" << TotalSegs(&sbi) << std::endl; |
| std::cout << "SegMgr::BuildSegmentManager(), ReservedSections()=" << ReservedSections() |
| << std::endl; |
| std::cout << "SegMgr::BuildSegmentManager(), OverprovisionSections()=" << OverprovisionSections() |
| << std::endl; |
| #endif |
| |
| InitMinMaxMtime(); |
| return ZX_OK; |
| } |
| |
| void SegMgr::DiscardDirtySegmap(DirtyType dirty_type) { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| |
| mtx_lock(&dirty_i->seglist_lock); |
| free(dirty_i->dirty_segmap[static_cast<int>(dirty_type)]); |
| dirty_i->nr_dirty[static_cast<int>(dirty_type)] = 0; |
| mtx_unlock(&dirty_i->seglist_lock); |
| } |
| |
| void SegMgr::ResetVictimSegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| uint32_t bitmap_size = BitmapSize(TotalSegs(&sbi)); |
| memset(GetDirtyInfo(&sbi)->victim_segmap[static_cast<int>(GcType::kFgGc)], 0, bitmap_size); |
| } |
| |
| void SegMgr::DestroyVictimSegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| |
| free(dirty_i->victim_segmap[static_cast<int>(GcType::kFgGc)]); |
| free(dirty_i->victim_segmap[static_cast<int>(GcType::kBgGc)]); |
| } |
| |
| void SegMgr::DestroyDirtySegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi); |
| int i; |
| |
| if (!dirty_i) |
| return; |
| |
| /* discard pre-free/dirty segments list */ |
| for (i = 0; i < static_cast<int>(DirtyType::kNrDirtytype); i++) |
| DiscardDirtySegmap(static_cast<DirtyType>(i)); |
| |
| DestroyVictimSegmap(); |
| GetSmInfo(&sbi)->dirty_info = nullptr; |
| free(dirty_i); |
| } |
| |
| // TODO: destroy_curseg |
| void SegMgr::DestroyCurseg() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| CursegInfo *array = GetSmInfo(&sbi)->curseg_array; |
| int i; |
| |
| if (!array) |
| return; |
| GetSmInfo(&sbi)->curseg_array = nullptr; |
| for (i = 0; i < kNrCursegType; i++) |
| free(array[i].sum_blk); |
| free(array); |
| } |
| |
| void SegMgr::DestroyFreeSegmap() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| FreeSegmapInfo *free_i = GetSmInfo(&sbi)->free_info; |
| if (!free_i) |
| return; |
| GetSmInfo(&sbi)->free_info = nullptr; |
| free(free_i->free_segmap); |
| free(free_i->free_secmap); |
| free(free_i); |
| } |
| |
| void SegMgr::DestroySitInfo() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SitInfo *sit_i = GetSitInfo(&sbi); |
| uint32_t start; |
| |
| if (!sit_i) |
| return; |
| |
| if (sit_i->sentries) { |
| for (start = 0; start < TotalSegs(&sbi); start++) { |
| free(sit_i->sentries[start].cur_valid_map); |
| free(sit_i->sentries[start].ckpt_valid_map); |
| } |
| } |
| free(sit_i->sentries); |
| free(sit_i->sec_entries); |
| free(sit_i->dirty_sentries_bitmap); |
| |
| GetSmInfo(&sbi)->SitInfo = nullptr; |
| free(sit_i->sit_bitmap); |
| free(sit_i); |
| } |
| |
| void SegMgr::DestroySegmentManager() { |
| SbInfo &sbi = fs_->GetSbInfo(); |
| SmInfo *sm_info = GetSmInfo(&sbi); |
| |
| DestroyDirtySegmap(); |
| DestroyCurseg(); |
| DestroyFreeSegmap(); |
| DestroySitInfo(); |
| sbi.sm_info = nullptr; |
| free(sm_info); |
| } |
| |
| } // namespace f2fs |