]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/seastore/btree: introduce BtreeCursor
authorZhang Song <zhangsong02@qianxin.com>
Tue, 22 Apr 2025 08:16:12 +0000 (16:16 +0800)
committerMatan Breizman <mbreizma@redhat.com>
Sun, 8 Jun 2025 07:02:03 +0000 (10:02 +0300)
Signed-off-by: Zhang Song <zhangsong02@qianxin.com>
(cherry picked from commit 972c139e9c5c1405378495fa9d50b1a314dea618)

src/crimson/os/seastore/btree/btree_types.cc
src/crimson/os/seastore/btree/btree_types.h
src/crimson/os/seastore/linked_tree_node.h
src/crimson/os/seastore/seastore_types.h

index 61f50e574916a00e6ffc8a263994a51deea8f0d7..a8d6e883153f6ec9c21a9fefc98088729d9b29b9 100644 (file)
@@ -2,6 +2,8 @@
 // vim: ts=8 sw=2 smarttab
 
 #include "crimson/os/seastore/btree/btree_types.h"
+#include "crimson/os/seastore/lba_manager/btree/lba_btree_node.h"
+#include "crimson/os/seastore/backref/backref_tree_node.h"
 
 namespace crimson::os::seastore {
 
@@ -29,4 +31,40 @@ std::ostream& operator<<(std::ostream &out, const backref_map_val_t& val) {
 }
 
 } // namespace backref
+
+namespace {
+template <typename key_t, typename T>
+bool modified_since(T &&extent, uint64_t iter_modifications) {
+  using backref::BackrefLeafNode;
+  using lba_manager::btree::LBALeafNode;
+  if constexpr (std::is_same_v<key_t, laddr_t>) {
+    assert(extent->get_type() == extent_types_t::LADDR_LEAF);
+    auto leaf = extent->template cast<LBALeafNode>();
+    return leaf->modified_since(iter_modifications);
+  } else {
+    assert(extent->get_type() == extent_types_t::BACKREF_LEAF);
+    auto leaf = extent->template cast<BackrefLeafNode>();
+    return leaf->modified_since(iter_modifications);
+  }
+}
+}
+
+template <typename key_t, typename val_t>
+bool BtreeCursor<key_t, val_t>::is_viewable() const {
+  LOG_PREFIX(BtreeCursor::is_viewable());
+  if (!parent->is_valid() ||
+      modified_since<key_t>(parent, modifications)) {
+    return false;
+  }
+
+  auto [viewable, state] = parent->is_viewable_by_trans(ctx.trans);
+  assert(state != CachedExtent::viewable_state_t::invalid);
+  SUBTRACET(seastore_cache, "{} with viewable state {}",
+            ctx.trans, *parent, state);
+  return viewable;
+}
+
+template struct BtreeCursor<laddr_t, lba_manager::btree::lba_map_val_t>;
+template struct BtreeCursor<paddr_t, backref::backref_map_val_t>;
+
 } // namespace crimson::os::seastore
index 1c850a31cd0629c940011eb60315236ad7aa6430..cd616ee6e96cac2855e518be5d9c99dff089283a 100644 (file)
@@ -192,4 +192,142 @@ struct __attribute__((packed)) backref_map_val_le_t {
 
 } // namespace backerf
 
+/**
+ * BtreeCursor
+ *
+ * BtreeCursor is the type-erased wrapper for FixedKVBtree::iterator, stores
+ * a key-value mapping's location and the snapshot of its data at construction
+ * time.
+ */
+template <typename key_t, typename val_t>
+struct BtreeCursor {
+  BtreeCursor(
+    op_context_t &ctx,
+    CachedExtentRef parent,
+    uint64_t modifications,
+    key_t key,
+    std::optional<val_t> val,
+    btreenode_pos_t pos)
+      : ctx(ctx),
+       parent(std::move(parent)),
+       modifications(modifications),
+       key(key),
+       val(std::move(val)),
+       pos(pos)
+  {
+    if constexpr (std::is_same_v<key_t, laddr_t>) {
+      static_assert(std::is_same_v<val_t, lba_manager::btree::lba_map_val_t>,
+        "the value type of laddr_t for BtreeCursor should be lba_map_val_t");
+    } else {
+      static_assert(std::is_same_v<key_t, paddr_t>,
+        "the key type of BtreeCursor should be either laddr_t or paddr_t");
+      static_assert(std::is_same_v<val_t, backref::backref_map_val_t>,
+        "the value type should be either lba_map_val_t or backref_map_val_t");
+    }
+  }
+
+  op_context_t ctx;
+  CachedExtentRef parent;
+  uint64_t modifications;
+  key_t key;
+  std::optional<val_t> val;
+  btreenode_pos_t pos;
+
+  // NOTE: The overhead of calling is_viewable() might be not negligible in the
+  // case of the parent extent is stable and shared by multiple transactions.
+  // The best practice is to only hold cursors whose parent is pending in
+  // current transaction in the long term.
+  bool is_viewable() const;
+
+  bool is_end() const {
+    auto max_key = min_max_t<key_t>::max;
+    assert((key != max_key) == (bool)val);
+    return key == max_key;
+  }
+
+  extent_len_t get_length() const {
+    assert(!is_end());
+    return val->len;
+  }
+};
+
+struct LBACursor : BtreeCursor<laddr_t, lba_manager::btree::lba_map_val_t> {
+  using Base = BtreeCursor<laddr_t, lba_manager::btree::lba_map_val_t>;
+  using Base::BtreeCursor;
+  bool is_indirect() const {
+    assert(!is_end());
+    return val->pladdr.is_laddr();
+  }
+  laddr_t get_laddr() const {
+    return key;
+  }
+  paddr_t get_paddr() const {
+    assert(!is_indirect());
+    assert(!is_end());
+    return val->pladdr.get_paddr();
+  }
+  laddr_t get_intermediate_key() const {
+    assert(is_indirect());
+    assert(!is_end());
+    return val->pladdr.get_laddr();
+  }
+  checksum_t get_checksum() const {
+    assert(!is_end());
+    assert(!is_indirect());
+    return val->checksum;
+  }
+  extent_ref_count_t get_refcount() const {
+    assert(!is_end());
+    assert(!is_indirect());
+    return val->refcount;
+  }
+  std::unique_ptr<LBACursor> duplicate() const {
+    return std::make_unique<LBACursor>(*this);
+  }
+};
+using LBACursorRef = std::unique_ptr<LBACursor>;
+
+struct BackrefCursor : BtreeCursor<paddr_t, backref::backref_map_val_t> {
+  using Base = BtreeCursor<paddr_t, backref::backref_map_val_t>;
+  using Base::BtreeCursor;
+  paddr_t get_paddr() const {
+    return key;
+  }
+  laddr_t get_laddr() const {
+    assert(!is_end());
+    return val->laddr;
+  }
+  extent_types_t get_type() const {
+    assert(!is_end());
+    return val->type;
+  }
+};
+using BackrefCursorRef = std::unique_ptr<BackrefCursor>;
+
+template <typename key_t, typename val_t>
+std::ostream &operator<<(
+  std::ostream &out, const BtreeCursor<key_t, val_t> &cursor)
+{
+  if constexpr (std::is_same_v<key_t, laddr_t>) {
+    out << "LBACursor(";
+  } else {
+    out << "BackrefCursor(";
+  }
+  out << (void*)cursor.parent.get()
+      << "@" << cursor.pos
+      << "#" << cursor.modifications
+      << ",";
+  if (cursor.is_end()) {
+    return out << "END)";
+  }
+  return out << "," << cursor.key
+            << "~" << *cursor.val
+            << ")";
+}
+
 } // namespace crimson::os::seastore
+
+#if FMT_VERSION >= 90000
+template <> struct fmt::formatter<crimson::os::seastore::LBACursor> : fmt::ostream_formatter {};
+template <> struct fmt::formatter<crimson::os::seastore::BackrefCursor> : fmt::ostream_formatter {};
+#endif
index 154353776a2566b5588916390d5040ff8f138705..6a903df257145fc4d5776af087320adb338211c4 100644 (file)
@@ -9,10 +9,6 @@
 
 namespace crimson::os::seastore {
 
-// XXX: It happens to be true that the width of node
-//     index in lba and omap tree are the same.
-using btreenode_pos_t = uint16_t;
-
 template <typename ParentT>
 class child_pos_t {
 public:
index 74c2e2f404a211865f226774bb73a4f9c70da8a3..5a80c6ef720f5f0449051b059324ce11406cd4f2 100644 (file)
@@ -77,6 +77,12 @@ using checksum_t = uint32_t;
 using checksum_le_t = ceph_le32;
 constexpr checksum_t CRC_NULL = 0;
 
+// XXX: It happens to be true that the width of node
+//     index in lba and omap tree are the same.
+using btreenode_pos_t = uint16_t;
+constexpr auto BTREENODE_POS_MAX = std::numeric_limits<btreenode_pos_t>::max();
+constexpr auto BTREENODE_POS_NULL = BTREENODE_POS_MAX;
+
 // Immutable metadata for seastore to set at mkfs time
 struct seastore_meta_t {
   uuid_d seastore_id;