[f2fs] Support ssr block allocation

It implements the ssr block allocation, and
it is triggered when # of freesections < # of OP sections.

For testing, it introduces a new gn variable 'f2fs_force_ssr'
that enforces every block allocation in a ssr manner if it is set to true.

Test: fx test fs-tests
Failed tests: RwFullDiskTest.PartialWriteSucceedsForFullDisk/F2fs

Test:
- linux host:
 fx emu -N --headless -hda third_party/f2fs/test_files/blk1g.bin
- fuchsia vm:
 mkdir /tmp/data;mount /dev/class/block/000 /tmp/data
 cp /tmp/data/100mb.bin /tmp/data/100mb.bin.2
 umount /tmp/data;dm shutdown
- linux host:
 fsck.f2fs third_party/f2fs/test_files/blk1g.bin
 mount third_party/f2fs/test_files/blk1g.bin ~/mnt
 diff ~/mnt/100mb.bin ~/mnt/100mb.bin.2
 umount ~/mnt

Prerequisite: set f2fs_force_ssr to true
Change-Id: I0dae89c935797eacba96b34fca56a7e0808d5aa2
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/f2fs/+/536745
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index d72c6b4..34ed37c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -7,8 +7,11 @@
 # moves out-of-tree.
 
 declare_args() {
-  # F2FS debug message for bringup product
+  # F2FS debug message
   f2fs_bu_debug = false
+
+  # Enforce every allocation in a ssr manner [for test]
+  f2fs_force_ssr = false
 }
 
 static_library("f2fs") {
@@ -20,6 +23,7 @@
     "dir_hash.cc",
     "f2fs.cc",
     "file.cc",
+    "gc.cc",
     "mkfs.cc",
     "namei.cc",
     "node.cc",
@@ -89,6 +93,10 @@
     defines = [ "F2FS_BU_DEBUG" ]
   }
 
+  if (f2fs_force_ssr) {
+    defines = [ "F2FS_FORCE_SSR" ]
+  }
+
   # TODO(fxbug.dev/69585): This target uses raw zx::channel with LLCPP which is deprecated.
   # Please migrate to typed channel APIs (fidl::ClientEnd<T>, fidl::ServerEnd<T>).
   # See linked bug for details.
diff --git a/gc.cc b/gc.cc
new file mode 100644
index 0000000..5f92168
--- /dev/null
+++ b/gc.cc
@@ -0,0 +1,126 @@
+// 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 {
+
+uint32_t SegMgr::GetGcCost(uint32_t segno, VictimSelPolicy *p) {
+  if (p->alloc_mode == AllocMode::kSSR)
+    return SegMgr::GetSegEntry(segno)->ckpt_valid_blocks;
+
+  ZX_ASSERT(0);
+#if 0  // porting needed
+  /* alloc_mode == kLFS */
+  if (p->gc_mode == kGcGreedy)
+    return get_valid_blocks(sbi, segno, sbi->segs_per_sec);
+  else
+    return get_cb_cost(sbi, segno);
+#endif
+}
+
+void SegMgr::SelectPolicy(GcType gc_type, CursegType type, VictimSelPolicy *p) {
+  SbInfo &sbi = fs_->GetSbInfo();
+  DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi);
+
+  if (p->alloc_mode == AllocMode::kSSR) {
+    p->gc_mode = GcMode::kGcGreedy;
+    p->dirty_segmap = dirty_i->dirty_segmap[static_cast<int>(type)];
+    p->ofs_unit = 1;
+  } else {
+    ZX_ASSERT(0);
+#if 0  // porting needed
+  p->gc_mode = select_gc_type(gc_type);
+  p->dirty_segmap = dirty_i->dirty_segmap[DIRTY];
+  p->ofs_unit = sbi->segs_per_sec;
+#endif
+  }
+
+  p->offset = sbi.last_victim[static_cast<int>(p->gc_mode)];
+}
+
+uint32_t SegMgr::GetMaxCost(VictimSelPolicy *p) {
+  SbInfo &sbi = fs_->GetSbInfo();
+
+  if (p->gc_mode == GcMode::kGcGreedy)
+    return (1 << sbi.log_blocks_per_seg) * p->ofs_unit;
+  else if (p->gc_mode == GcMode::kGcCb)
+    return kUint32Max;
+  return 0;
+}
+
+bool SegMgr::GetVictimByDefault(GcType gc_type, CursegType type, AllocMode alloc_mode,
+                                uint32_t *out) {
+  SbInfo &sbi = fs_->GetSbInfo();
+  DirtySeglistInfo *dirty_i = GetDirtyInfo(&sbi);
+  VictimSelPolicy p;
+
+  p.alloc_mode = alloc_mode;
+  SelectPolicy(gc_type, type, &p);
+
+  p.min_segno = kNullSegNo;
+  p.min_cost = GetMaxCost(&p);
+
+  fbl::AutoLock lock(&dirty_i->seglist_lock);
+
+#if 0  // porting needed
+	if (p.alloc_mode == AllocMode::kLFS && gc_type == GcType::kFgGC) {
+		p.min_segno = check_bg_victims(sbi);
+		if (p.min_segno != kNullSegNo)
+			goto got_it;
+	}
+#endif
+
+  uint32_t nsearched = 0;
+
+  while (1) {
+    uint32_t segno = find_next_bit_le(p.dirty_segmap, TotalSegs(&sbi), p.offset);
+    if (segno >= TotalSegs(&sbi)) {
+      if (sbi.last_victim[static_cast<int>(p.gc_mode)]) {
+        sbi.last_victim[static_cast<int>(p.gc_mode)] = 0;
+        p.offset = 0;
+        continue;
+      }
+      break;
+    }
+    p.offset = ((segno / p.ofs_unit) * p.ofs_unit) + p.ofs_unit;
+
+    if (test_bit(segno, dirty_i->victim_segmap[static_cast<int>(GcType::kFgGc)]))
+      continue;
+    if (gc_type == GcType::kBgGc &&
+        test_bit(segno, dirty_i->victim_segmap[static_cast<int>(GcType::kBgGc)]))
+      continue;
+    if (IsCurSec(&sbi, GetSecNo(&sbi, segno)))
+      continue;
+
+    uint32_t cost = GetGcCost(segno, &p);
+
+    if (p.min_cost > cost) {
+      p.min_segno = segno;
+      p.min_cost = cost;
+    }
+
+    if (cost == GetMaxCost(&p))
+      continue;
+
+    if (nsearched++ >= kMaxSearchLimit) {
+      sbi.last_victim[static_cast<int>(p.gc_mode)] = segno;
+      break;
+    }
+  }
+#if 0  // porting needed
+got_it:
+#endif
+  if (p.min_segno != kNullSegNo) {
+    *out = (p.min_segno / p.ofs_unit) * p.ofs_unit;
+    if (p.alloc_mode == AllocMode::kLFS) {
+      for (uint32_t i = 0; i < p.ofs_unit; i++)
+        set_bit(*out + i, dirty_i->victim_segmap[static_cast<int>(gc_type)]);
+    }
+  }
+
+  return (p.min_segno == kNullSegNo) ? false : true;
+}
+
+}  // namespace f2fs
diff --git a/segment.cc b/segment.cc
index 9ddddb4..9b5a917 100644
--- a/segment.cc
+++ b/segment.cc
@@ -200,15 +200,18 @@
 }
 
 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 GetDirtyInfo(&sbi)->v_ops->get_victim(
-      &sbi, &(curseg)->next_segno, static_cast<int>(GcType::kBgGc), static_cast<int>(type),
-      static_cast<uint8_t>(AllocMode::kSSR));
+  return GetVictimByDefault(GcType::kBgGc, type, AllocMode::kSSR, &(curseg->next_segno));
 }
 
 inline bool SegMgr::HasNotEnoughFreeSecs() {
@@ -931,17 +934,11 @@
   // ofs_unit = NeedSSR() ? 1 : sbi.segs_per_sec;
   // curseg->next_segno = CheckPrefreeSegments(ofs_unit, type);
 
-  // if (curseg->next_segno != kNullSegNo)
-  // 	ChangeCurseg(type, false);
-  // else
-
-  if (type == CursegType::kCursegWarmNode) {
+  if (curseg->next_segno != kNullSegNo) {
+    ChangeCurseg(type, false);
+  } else if (type == CursegType::kCursegWarmNode) {
     NewCurseg(type, false);
-  } else if (false) {
-#if 0  // porting needed
-    // TODO: IMPL (SSR)
-    //} else if (NeedSSR() && GetSsrSegment(type)) {
-#endif
+  } else if (NeedSSR() && GetSsrSegment(type)) {
     ChangeCurseg(type, true);
   } else {
     NewCurseg(type, false);
@@ -968,7 +965,7 @@
        i <= static_cast<int>(CursegType::kCursegColdData); i++) {
     curseg = CURSEG_I(&sbi, static_cast<CursegType>(i));
     old_curseg = curseg->segno;
-    GetSitInfo(&sbi)->s_ops->allocate_segment(&sbi, i, true);
+    AllocateSegmentByDefault(static_cast<CursegType>(i), true);
     LocateDirtySegment(old_curseg);
   }
 }
diff --git a/segment.h b/segment.h
index 7e58259..2050130 100644
--- a/segment.h
+++ b/segment.h
@@ -8,7 +8,9 @@
 namespace f2fs {
 
 /* constant macro */
-constexpr uint32_t kNullSegNo = (uint32_t)(~0);
+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 {
@@ -38,7 +40,7 @@
  * GC_CB is based on cost-benefit algorithm.
  * GC_GREEDY is based on greedy algorithm.
  */
-enum class GcAlgorithm { kGcCb = 0, kGcGreedy };
+enum class GcMode { kGcCb = 0, kGcGreedy };
 
 /*
  * BG_GC means the background cleaning job.
@@ -46,15 +48,15 @@
  */
 enum class GcType { kBgGc = 0, kFgGc };
 
-/* for a function parameter to select a victim segment */
+// for a function parameter to select a victim segment
 struct VictimSelPolicy {
-  int alloc_mode = 0;               /* LFS or SSR */
-  int gc_mode = 0;                  /* GC_CB or GC_GREEDY */
-  uint64_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 */
+  AllocMode alloc_mode = AllocMode::kLFS;  // LFS or SSR
+  GcMode gc_mode = GcMode::kGcCb;          // cost effective or greedy
+  uint64_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
 };
 
 struct SegEntry {
@@ -232,6 +234,37 @@
   void RewriteNodePage(Page *page, Summary *sum, block_t old_blkaddr, block_t new_blkaddr);
 
  private:
+  // 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_;
 
  public: