blob: 022d0b0582b5d2f956da8fd7ced1239284fb0189 [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.
#ifndef SRC_STORAGE_F2FS_SEGMENT_H_
#define SRC_STORAGE_F2FS_SEGMENT_H_
namespace f2fs {
// constant macro
constexpr uint32_t kNullSegNo = std::numeric_limits<uint32_t>::max();
constexpr uint32_t kUint32Max = std::numeric_limits<uint32_t>::max();
constexpr uint32_t kMaxSearchLimit = 20;
// during checkpoint, BioPrivate is used to synchronize the last bio
struct BioPrivate {
bool is_sync = false;
void *wait = nullptr;
};
// Indicate a block allocation direction:
// kAllocRight means allocating new sections towards the end of volume.
// kAllocLeft means the opposite direction.
enum class AllocDirection {
kAllocRight = 0,
kAllocLeft,
};
// In the VictimSelPolicy->alloc_mode, there are two block allocation modes.
// LFS writes data sequentially with cleaning operations.
// SSR (Slack Space Recycle) reuses obsolete space without cleaning operations.
enum class AllocMode { kLFS = 0, kSSR };
// In the VictimSelPolicy->gc_mode, there are two gc, aka cleaning, modes.
// GC_CB is based on cost-benefit algorithm.
// GC_GREEDY is based on greedy algorithm.
enum class GcMode { kGcCb = 0, kGcGreedy };
// BG_GC means the background cleaning job.
// FG_GC means the on-demand cleaning job.
enum class GcType { kBgGc = 0, kFgGc };
// for a function parameter to select a victim segment
struct VictimSelPolicy {
AllocMode alloc_mode = AllocMode::kLFS; // LFS or SSR
GcMode gc_mode = GcMode::kGcCb; // cost effective or greedy
uint8_t *dirty_segmap = nullptr; // dirty segment bitmap
uint32_t offset = 0; // last scanned bitmap offset
uint32_t ofs_unit = 0; // bitmap search unit
uint32_t min_cost = 0; // minimum cost
uint32_t min_segno = 0; // segment # having min. cost
};
// SSR mode uses these field to determine which blocks are allocatable.
struct SegmentEntry {
std::unique_ptr<uint8_t[]> cur_valid_map; // validity bitmap of blocks
std::unique_ptr<uint8_t[]> ckpt_valid_map; // validity bitmap in the last CP
uint64_t mtime = 0; // modification time of the segment
uint16_t valid_blocks = 0; // # of valid blocks
uint16_t ckpt_valid_blocks = 0; // # of valid blocks in the last CP
uint8_t type = 0; // segment type like CURSEG_XXX_TYPE
};
struct SectionEntry {
uint32_t valid_blocks = 0; // # of valid blocks in a section
};
struct SitInfo {
std::unique_ptr<uint8_t[]> sit_bitmap; // SIT bitmap pointer
std::unique_ptr<uint8_t[]> dirty_sentries_bitmap; // bitmap for dirty sentries
SegmentEntry *sentries = nullptr; // SIT segment-level cache
SectionEntry *sec_entries = nullptr; // SIT section-level cache
block_t sit_base_addr = 0; // start block address of SIT area
block_t sit_blocks = 0; // # of blocks used by SIT area
block_t written_valid_blocks = 0; // # of valid blocks in main area
uint32_t bitmap_size = 0; // SIT bitmap size
uint32_t dirty_sentries = 0; // # of dirty sentries
uint32_t sents_per_block = 0; // # of SIT entries per block
// for cost-benefit algorithm in cleaning procedure
uint64_t elapsed_time = 0; // elapsed time after mount
uint64_t mounted_time = 0; // mount time
uint64_t min_mtime = 0; // min. modification time
uint64_t max_mtime = 0; // max. modification time
fs::SharedMutex sentry_lock; // to protect SIT cache
};
struct FreeSegmapInfo {
std::unique_ptr<uint8_t[]> free_segmap; // free segment bitmap
std::unique_ptr<uint8_t[]> free_secmap; // free section bitmap
uint32_t start_segno = 0; // start segment number logically
uint32_t free_segments = 0; // # of free segments
uint32_t free_sections = 0; // # of free sections
fs::SharedMutex segmap_lock; // free segmap lock
};
// Notice: The order of dirty type is same with CURSEG_XXX in f2fs.h
enum class DirtyType {
kDirtyHotData = 0, // dirty segments assigned as hot data logs
kDirtyWarmData, // dirty segments assigned as warm data logs
kDirtyColdData, // dirty segments assigned as cold data logs
kDirtyHotNode, // dirty segments assigned as hot node logs
kDirtyWarmNode, // dirty segments assigned as warm node logs
kDirtyColdNode, // dirty segments assigned as cold node logs
kDirty, // to count # of dirty segments
kPre, // to count # of entirely obsolete segments
kNrDirtytype
};
struct DirtySeglistInfo {
std::unique_ptr<uint8_t[]> dirty_segmap[static_cast<int>(DirtyType::kNrDirtytype)];
std::mutex seglist_lock; // lock for segment bitmaps
int nr_dirty[static_cast<int>(DirtyType::kNrDirtytype)] = {}; // # of dirty segments
std::unique_ptr<uint8_t[]> victim_segmap[2]; // kBgGc, kFgGc
};
// for active log information
struct CursegInfo {
union {
SummaryBlock *sum_blk = nullptr; // cached summary block
FsBlock *raw_blk;
};
uint32_t segno = 0; // current segment number
uint32_t zone = 0; // current zone number
uint32_t next_segno = 0; // preallocated segment
std::mutex curseg_mutex; // lock for consistency
uint16_t next_blkoff = 0; // next block offset to write
uint8_t alloc_type = 0; // current allocation type
};
// For SIT manager
//
// By default, there are 6 active log areas across the whole main area.
// When considering hot and cold data separation to reduce cleaning overhead,
// we split 3 for data logs and 3 for node logs as hot, warm, and cold types,
// respectively.
// In the current design, you should not change the numbers intentionally.
// Instead, as a mount option such as active_logs=x, you can use 2, 4, and 6
// logs individually according to the underlying devices. (default: 6)
// Just in case, on-disk layout covers maximum 16 logs that consist of 8 for
// data and 8 for node logs.
constexpr int kNrCursegDataType = 3;
constexpr int kNrCursegNodeType = 3;
constexpr int kNrCursegType = kNrCursegDataType + kNrCursegNodeType;
enum class CursegType {
kCursegHotData = 0, // directory entry blocks
kCursegWarmData, // data blocks
kCursegColdData, // multimedia or GCed data blocks
kCursegHotNode, // direct node blocks of directory files
kCursegWarmNode, // direct node blocks of normal files
kCursegColdNode, // indirect node blocks
kNoCheckType
};
int LookupJournalInCursum(SummaryBlock *sum, JournalType type, uint32_t val, int alloc);
class SegmentManager {
public:
// Not copyable or moveable
SegmentManager(const SegmentManager &) = delete;
SegmentManager &operator=(const SegmentManager &) = delete;
SegmentManager(SegmentManager &&) = delete;
SegmentManager &operator=(SegmentManager &&) = delete;
SegmentManager() = delete;
SegmentManager(F2fs *fs);
SegmentManager(SuperblockInfo *info) { superblock_info_ = info; }
zx_status_t BuildSegmentManager();
void DestroySegmentManager();
SegmentEntry &GetSegmentEntry(uint32_t segno);
SectionEntry *GetSectionEntry(uint32_t segno);
uint32_t GetValidBlocks(uint32_t segno, int section);
void SegInfoFromRawSit(SegmentEntry &segment_entry, SitEntry &raw_sit);
void SegInfoToRawSit(SegmentEntry &segment_entry, SitEntry &raw_sit);
uint32_t FindNextInuse(uint32_t max, uint32_t segno);
void SetFree(uint32_t segno);
void SetInuse(uint32_t segno);
void SetTestAndFree(uint32_t segno);
void SetTestAndInuse(uint32_t segno);
void GetSitBitmap(void *dst_addr);
uint32_t FreeSegments();
uint32_t FreeSections();
uint32_t PrefreeSegments();
block_t DirtySegments();
block_t OverprovisionSegments();
block_t OverprovisionSections();
block_t ReservedSections();
bool NeedSSR();
int GetSsrSegment(CursegType type);
bool HasNotEnoughFreeSecs();
uint32_t Utilization();
bool NeedInplaceUpdate(VnodeF2fs *vnode);
uint32_t CursegSegno(int type);
uint8_t CursegAllocType(int type);
uint16_t CursegBlkoff(int type);
void CheckSegRange(uint32_t segno);
void CheckBlockCount(int segno, SitEntry &raw_sit);
pgoff_t CurrentSitAddr(uint32_t start);
pgoff_t NextSitAddr(pgoff_t block_addr);
void SetToNextSit(uint32_t start);
uint64_t GetMtime();
void SetSummary(Summary *sum, nid_t nid, uint32_t ofs_in_node, uint8_t version);
block_t StartSumBlock();
block_t SumBlkAddr(int base, int type);
bool NeedToCheckpoint();
void BalanceFs();
void LocateDirtySegment(uint32_t segno, enum DirtyType dirty_type);
void RemoveDirtySegment(uint32_t segno, enum DirtyType dirty_type);
void LocateDirtySegment(uint32_t segno);
void SetPrefreeAsFreeSegments();
void ClearPrefreeSegments();
void MarkSitEntryDirty(uint32_t segno);
void SetSitEntryType(CursegType type, uint32_t segno, int modified);
void UpdateSitEntry(block_t blkaddr, int del);
void RefreshSitEntry(block_t old_blkaddr, block_t new_blkaddr);
void InvalidateBlocks(block_t addr);
void AddSumEntry(CursegType type, Summary *sum, uint16_t offset);
int NpagesForSummaryFlush();
void GetSumPage(uint32_t segno, LockedPage *page);
void WriteSumPage(SummaryBlock *sum_blk, block_t blk_addr);
uint32_t CheckPrefreeSegments(int ofs_unit, CursegType type);
void GetNewSegment(uint32_t *newseg, bool new_sec, int dir);
void ResetCurseg(CursegType type, int modified);
void NewCurseg(CursegType type, bool new_sec);
void NextFreeBlkoff(CursegInfo *seg, block_t start);
void RefreshNextBlkoff(CursegInfo *seg);
void ChangeCurseg(CursegType type, bool reuse);
void AllocateSegmentByDefault(CursegType type, bool force);
void AllocateNewSegments();
block_t GetWrittenBlockCount();
#if 0 // porting needed
void VerifyBlockAddr(block_t blk_addr) = 0;
#endif
bool HasCursegSpace(CursegType type);
CursegType GetSegmentType2(Page &page, PageType p_type);
CursegType GetSegmentType4(Page &page, PageType p_type);
CursegType GetSegmentType6(Page &page, PageType p_type);
CursegType GetSegmentType(Page &page, PageType p_type);
zx_status_t DoWritePage(LockedPage &page, block_t old_blkaddr, block_t *new_blkaddr, Summary *sum,
PageType p_type);
zx_status_t WriteMetaPage(LockedPage &page, bool is_reclaim = false);
zx_status_t WriteNodePage(LockedPage &page, uint32_t nid, block_t old_blkaddr,
block_t *new_blkaddr);
zx_status_t WriteDataPage(VnodeF2fs *vnode, LockedPage &page, nid_t nid, uint32_t ofs_in_node,
block_t old_blkaddr, block_t *new_blkaddr);
zx_status_t RewriteDataPage(LockedPage &page, block_t old_blk_addr);
void RecoverDataPage(Summary &sum, block_t old_blkaddr, block_t new_blkaddr);
zx_status_t ReadCompactedSummaries();
zx_status_t ReadNormalSummaries(int type);
int RestoreCursegSummaries();
void WriteCompactedSummaries(block_t blkaddr);
void WriteNormalSummaries(block_t blkaddr, CursegType type);
void WriteDataSummaries(block_t start_blk);
void WriteNodeSummaries(block_t start_blk);
void GetCurrentSitPage(uint32_t segno, LockedPage *out);
void GetNextSitPage(uint32_t start, LockedPage *out);
bool FlushSitsInJournal();
void FlushSitEntries();
zx_status_t BuildSitInfo();
zx_status_t BuildFreeSegmap();
zx_status_t BuildCurseg();
void BuildSitEntries();
void InitFreeSegmap();
void InitDirtySegmap();
zx_status_t InitVictimSegmap();
zx_status_t BuildDirtySegmap();
void InitMinMaxMtime();
void DiscardDirtySegmap(enum DirtyType dirty_type);
void ResetVictimSegmap();
void DestroyVictimSegmap();
void DestroyDirtySegmap();
void DestroyCurseg();
void DestroyFreeSegmap();
void DestroySitInfo();
block_t GetSegment0StartBlock() const { return seg0_blkaddr_; }
block_t GetMainAreaStartBlock() const { return main_blkaddr_; }
block_t GetSSAreaStartBlock() const { return ssa_blkaddr_; }
block_t GetSegmentsCount() const { return segment_count_; }
block_t GetMainSegmentsCount() const { return main_segments_; }
block_t GetReservedSegmentsCount() const { return reserved_segments_; }
block_t GetOPSegmentsCount() const { return ovp_segments_; }
SitInfo &GetSitInfo() const { return *sit_info_; }
FreeSegmapInfo &GetFreeSegmentInfo() const { return *free_info_; }
DirtySeglistInfo &GetDirtySegmentInfo() const { return *dirty_info_; }
void SetSegment0StartBlock(const block_t addr) { seg0_blkaddr_ = addr; }
void SetMainAreaStartBlock(const block_t addr) { main_blkaddr_ = addr; }
void SetSSAreaStartBlock(const block_t addr) { ssa_blkaddr_ = addr; }
void SetSegmentsCount(const block_t count) { segment_count_ = count; }
void SetMainSegmentsCount(const block_t count) { main_segments_ = count; }
void SetReservedSegmentsCount(const block_t count) { reserved_segments_ = count; }
void SetOPSegmentsCount(const block_t count) { ovp_segments_ = count; }
void SetSitInfo(std::unique_ptr<SitInfo> &&info) { sit_info_ = std::move(info); }
void SetFreeSegmentInfo(std::unique_ptr<FreeSegmapInfo> &&info) { free_info_ = std::move(info); }
void SetDirtySegmentInfo(std::unique_ptr<DirtySeglistInfo> &&info) {
dirty_info_ = std::move(info);
}
CursegInfo *CURSEG_I(CursegType type) { return &curseg_array_[static_cast<int>(type)]; }
bool IsCurSeg(uint32_t segno) {
return ((segno == CURSEG_I(CursegType::kCursegHotData)->segno) ||
(segno == CURSEG_I(CursegType::kCursegWarmData)->segno) ||
(segno == CURSEG_I(CursegType::kCursegColdData)->segno) ||
(segno == CURSEG_I(CursegType::kCursegHotNode)->segno) ||
(segno == CURSEG_I(CursegType::kCursegWarmNode)->segno) ||
(segno == CURSEG_I(CursegType::kCursegColdNode)->segno));
}
bool IsCurSec(uint32_t secno) {
return ((secno ==
CURSEG_I(CursegType::kCursegHotData)->segno / superblock_info_->GetSegsPerSec()) ||
(secno ==
CURSEG_I(CursegType::kCursegWarmData)->segno / superblock_info_->GetSegsPerSec()) ||
(secno ==
CURSEG_I(CursegType::kCursegColdData)->segno / superblock_info_->GetSegsPerSec()) ||
(secno ==
CURSEG_I(CursegType::kCursegHotNode)->segno / superblock_info_->GetSegsPerSec()) ||
(secno ==
CURSEG_I(CursegType::kCursegWarmNode)->segno / superblock_info_->GetSegsPerSec()) ||
(secno ==
CURSEG_I(CursegType::kCursegColdNode)->segno / superblock_info_->GetSegsPerSec()));
}
// L: Logical segment number in volume, R: Relative segment number in main area
uint32_t GetL2RSegNo(uint32_t segno) { return (segno - free_info_->start_segno); }
uint32_t GetR2LSegNo(uint32_t segno) { return (segno + free_info_->start_segno); }
bool IsDataSeg(CursegType t) {
return ((t == CursegType::kCursegHotData) || (t == CursegType::kCursegColdData) ||
(t == CursegType::kCursegWarmData));
}
bool IsNodeSeg(CursegType t) {
return ((t == CursegType::kCursegHotNode) || (t == CursegType::kCursegColdNode) ||
(t == CursegType::kCursegWarmNode));
}
block_t StartBlock(uint32_t segno) {
return (seg0_blkaddr_ + (GetR2LSegNo(segno) << superblock_info_->GetLogBlocksPerSeg()));
}
block_t NextFreeBlkAddr(CursegType type) {
CursegInfo *curseg = CURSEG_I(type);
return (StartBlock(curseg->segno) + curseg->next_blkoff);
}
block_t GetSegOffFromSeg0(block_t blk_addr) { return blk_addr - GetSegment0StartBlock(); }
uint32_t GetSegNoFromSeg0(block_t blk_addr) {
return GetSegOffFromSeg0(blk_addr) >> superblock_info_->GetLogBlocksPerSeg();
}
uint32_t GetSegmentNumber(block_t blk_addr) {
return ((blk_addr == kNullAddr) || (blk_addr == kNewAddr))
? kNullSegNo
: GetL2RSegNo(GetSegNoFromSeg0(blk_addr));
}
uint32_t GetSecNo(uint32_t segno) { return segno / superblock_info_->GetSegsPerSec(); }
uint32_t GetZoneNoFromSegNo(uint32_t segno) {
return segno / superblock_info_->GetSegsPerSec() / superblock_info_->GetSecsPerZone();
}
block_t GetSumBlock(uint32_t segno) { return ssa_blkaddr_ + segno; }
uint32_t SitEntryOffset(uint32_t segno) { return segno % sit_info_->sents_per_block; }
block_t SitBlockOffset(uint32_t segno) { return segno / kSitEntryPerBlock; }
block_t StartSegNo(uint32_t segno) { return SitBlockOffset(segno) * kSitEntryPerBlock; }
uint32_t BitmapSize(uint32_t nr) {
return static_cast<uint32_t>(BitsToLongs(nr) * sizeof(uint64_t));
}
block_t TotalSegs() { return main_segments_; }
// GetVictimByDefault() is called for two purposes:
// 1) One is to select a victim segment for garbage collection, and
// 2) the other is to find a dirty segment used for SSR.
// For GC, it tries to find a victim segment that might require less cost
// to secure free segments among all types of dirty segments.
// The gc cost can be calucalted in two ways according to GcType.
// In case of GcType::kFgGc, it is typically triggered in the middle of user IO path,
// and thus it selects a victim with a less valid block count (i.e., GcMode::kGcGreedy)
// as it hopes the migration completes more quickly.
// In case of GcType::kBgGc, it is triggered at a idle time,
// so it uses a cost-benefit method (i.e., GcMode:: kGcCb) rather than kGcGreedy for the victim
// selection. kGcCb tries to find a cold segment as a victim as it hopes to mitigate a block
// thrashing problem.
// Meanwhile, SSR is to reuse invalid blocks for new block allocation, and thus
// it uses kGcGreedy to select a dirty segment with more invalid blocks
// among the same type of dirty segments as that of the current segment.
// |out| contains the segment number of the seleceted victim, and
// it returns true when it finds a victim segment.
bool GetVictimByDefault(GcType gc_type, CursegType type, AllocMode alloc_mode, uint32_t *out);
// This function calculates the maximum cost for a victim in each GcType
// Any segment with a less cost value becomes a victim candidate.
uint32_t GetMaxCost(VictimSelPolicy *p);
// This method determines GcMode for GetVictimByDefault
void SelectPolicy(GcType gc_type, CursegType type, VictimSelPolicy *p);
// This method calculates the gc cost for each dirty segment
uint32_t GetGcCost(uint32_t segno, VictimSelPolicy *p);
private:
F2fs *fs_ = nullptr;
SuperblockInfo *superblock_info_ = nullptr;
std::unique_ptr<SitInfo> sit_info_; // whole segment information
std::unique_ptr<FreeSegmapInfo> free_info_; // free segment information
std::unique_ptr<DirtySeglistInfo> dirty_info_; // dirty segment information
CursegInfo curseg_array_[kNrCursegType]; // active segment information
block_t seg0_blkaddr_ = 0; // block address of 0'th segment
block_t main_blkaddr_ = 0; // start block address of main area
block_t ssa_blkaddr_ = 0; // start block address of SSA area
block_t segment_count_ = 0; // total # of segments
block_t main_segments_ = 0; // # of segments in main area
block_t reserved_segments_ = 0; // # of reserved segments
block_t ovp_segments_ = 0; // # of overprovision segments
};
} // namespace f2fs
#endif // SRC_STORAGE_F2FS_SEGMENT_H_