]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/seastore: leverage RetiredExtentPlaceholder to detect transaction conflicts
authorYingxin Cheng <yingxin.cheng@intel.com>
Mon, 5 Jul 2021 03:28:35 +0000 (11:28 +0800)
committerYingxin Cheng <yingxin.cheng@intel.com>
Tue, 6 Jul 2021 02:18:20 +0000 (10:18 +0800)
Signed-off-by: Yingxin Cheng <yingxin.cheng@intel.com>
src/crimson/os/seastore/cache.cc
src/crimson/os/seastore/cache.h
src/crimson/os/seastore/cached_extent.h
src/crimson/os/seastore/transaction.h

index 980ac2309106b46c87a793b213f4b308ceb3c71b..12d2215a3983e5da11533ebcd6c62bfc77b6f6b2 100644 (file)
@@ -43,34 +43,31 @@ Cache::retire_extent_ret Cache::retire_extent_addr(
   }
 
   // absent from transaction
-  result = query_cache_for_extent(addr, &ext);
+  result = query_cache_with_placeholders(addr, &ext);
   if (result == Transaction::get_extent_ret::PRESENT) {
-    t.add_to_read_set(ext);
-    return trans_intr::make_interruptible(
-      ext->wait_io()
-    ).then_interruptible([&t, ext=std::move(ext)]() mutable {
-      t.add_to_retired_set(ext);
-      return retire_extent_iertr::now();
-    });
+    if (ext->get_type() != extent_types_t::RETIRED_PLACEHOLDER) {
+      t.add_to_read_set(ext);
+      return trans_intr::make_interruptible(
+        ext->wait_io()
+      ).then_interruptible([&t, ext=std::move(ext)]() mutable {
+        t.add_to_retired_set(ext);
+        return retire_extent_iertr::now();
+      });
+    }
+    // the retired-placeholder exists
   } else { // result == get_extent_ret::ABSENT
-    // FIXME this will cause incorrect transaction invalidation because t
-    // will not be notified if other transactions that modify the extent at
-    // this addr are committed.
-    //
-    // Say that we have transaction A and B, conflicting on extent E at laddr
-    // L:
-    //   A: dec_ref(L) // cause uncached retirement
-    //   B: read(L) -> E
-    //   B: mutate(E)
-    //   B: submit_transaction() // assume successful
-    //   A: about to submit...
-    //
-    // A cannot be invalidated because E is not in A's read-set
-    //
-    // TODO: leverage RetiredExtentPlaceholder to fix the issue.
-    t.add_to_retired_uncached(addr, length);
-    return retire_extent_iertr::now();
+    // add a new placeholder to Cache
+    ext = CachedExtent::make_cached_extent_ref<
+      RetiredExtentPlaceholder>(length);
+    ext->set_paddr(addr);
+    ext->state = CachedExtent::extent_state_t::CLEAN;
+    add_extent(ext);
   }
+
+  // add the retired-placeholder to transaction
+  t.add_to_read_set(ext);
+  t.add_to_retired_set(ext);
+  return retire_extent_iertr::now();
 }
 
 void Cache::dump_contents()
@@ -319,21 +316,6 @@ record_t Cache::prepare_record(Transaction &t)
 
   // Transaction is now a go, set up in-memory cache state
   // invalidate now invalid blocks
-  for (auto &&i : t.retired_uncached) {
-    CachedExtentRef to_retire;
-    if (query_cache_for_extent(i.first, &to_retire) ==
-       Transaction::get_extent_ret::ABSENT) {
-      to_retire = CachedExtent::make_cached_extent_ref<
-       RetiredExtentPlaceholder
-       >(i.second);
-      to_retire->set_paddr(i.first);
-      to_retire->state = CachedExtent::extent_state_t::CLEAN;
-    }
-
-    t.retired_set.insert(to_retire);
-    extents.insert(*to_retire);
-  }
-
   for (auto &i: t.retired_set) {
     DEBUGT("retiring {}", t, *i);
     retire_extent(i);
@@ -485,8 +467,11 @@ Cache::replay_delta(
     auto _get_extent_if_cached = [this](paddr_t addr)
       -> get_extent_ertr::future<CachedExtentRef> {
       CachedExtentRef ret;
-      auto result = query_cache_for_extent(addr, &ret);
+      auto result = query_cache_with_placeholders(addr, &ret);
       if (result == Transaction::get_extent_ret::PRESENT) {
+        // no retired-placeholder should be exist yet because no transaction
+        // has been created.
+        assert(ret->get_type() != extent_types_t::RETIRED_PLACEHOLDER);
         return ret->wait_io().then([ret] {
           return ret;
         });
index 34d7ce64b4c0a21201e3b8ea99fa6996ac50131a..87605d59b91038295255facc102b2fc2e65ce55a 100644 (file)
@@ -181,43 +181,42 @@ public:
     paddr_t offset,       ///< [in] starting addr
     segment_off_t length  ///< [in] length
   ) {
-    CachedExtentRef _ret;
-    auto result = query_cache_for_extent(offset, &_ret);
-    if (result == Transaction::get_extent_ret::PRESENT) {
-      auto ret = TCachedExtentRef<T>(static_cast<T*>(_ret.get()));
+    CachedExtentRef cached;
+    auto result = query_cache_with_placeholders(offset, &cached);
+    if (result == Transaction::get_extent_ret::ABSENT) {
+      auto ret = CachedExtent::make_cached_extent_ref<T>(
+        alloc_cache_buf(length));
+      ret->set_paddr(offset);
+      ret->state = CachedExtent::extent_state_t::CLEAN;
+      add_extent(ret);
+      return read_extent<T>(std::move(ret));
+    }
+
+    // extent PRESENT in cache
+    if (cached->get_type() == extent_types_t::RETIRED_PLACEHOLDER) {
+      auto ret = CachedExtent::make_cached_extent_ref<T>(
+        alloc_cache_buf(length));
+      ret->set_paddr(offset);
+      ret->state = CachedExtent::extent_state_t::CLEAN;
+      extents.replace(*ret, *cached);
+
+      // replace placeholder in transactions
+      while (!cached->transactions.empty()) {
+        auto t = cached->transactions.begin()->t;
+        t->replace_placeholder(*cached, *ret);
+      }
+
+      cached->state = CachedExtent::extent_state_t::INVALID;
+      return read_extent<T>(std::move(ret));
+    } else {
+      auto ret = TCachedExtentRef<T>(static_cast<T*>(cached.get()));
       return ret->wait_io(
       ).then([ret=std::move(ret)]() mutable -> get_extent_ret<T> {
-       // ret may be invalid, caller must check
-       return get_extent_ret<T>(
-         get_extent_ertr::ready_future_marker{},
-         std::move(ret));
+        // ret may be invalid, caller must check
+        return get_extent_ret<T>(
+          get_extent_ertr::ready_future_marker{},
+          std::move(ret));
       });
-    } else { // get_extent_ret::ABSENT
-      auto ref = CachedExtent::make_cached_extent_ref<T>(
-       alloc_cache_buf(length));
-      ref->set_io_wait();
-      ref->set_paddr(offset);
-      ref->state = CachedExtent::extent_state_t::CLEAN;
-      add_extent(ref);
-
-      return segment_manager.read(
-       offset,
-       length,
-       ref->get_bptr()).safe_then(
-         [ref=std::move(ref)]() mutable {
-           /* TODO: crc should be checked against LBA manager */
-           ref->last_committed_crc = ref->get_crc32c();
-
-           ref->on_clean_read();
-           ref->complete_io();
-           return get_extent_ertr::make_ready_future<TCachedExtentRef<T>>(
-             std::move(ref));
-         },
-         get_extent_ertr::pass_further{},
-         crimson::ct_error::assert_all{
-           "Cache::get_extent: invalid error"
-         }
-       );
     }
   }
 
@@ -240,14 +239,16 @@ public:
         CachedExtentRef>(ret);
     }
 
-    // get_extent_ret::PRESENT from transaction
-    result = query_cache_for_extent(offset, &ret);
-    if (result == Transaction::get_extent_ret::ABSENT) {
+    // get_extent_ret::ABSENT from transaction
+    result = query_cache_with_placeholders(offset, &ret);
+    if (result == Transaction::get_extent_ret::ABSENT ||
+        // retired_placeholder is not really cached yet
+        ret->get_type() == extent_types_t::RETIRED_PLACEHOLDER) {
       return get_extent_if_cached_iertr::make_ready_future<
         CachedExtentRef>();
     }
 
-    // get_extent_ret::PRESENT from cache
+    // get_extent_ret::PRESENT from cache and is not placeholder
     t.add_to_read_set(ret);
     return ret->wait_io().then([ret] {
       return get_extent_if_cached_iertr::make_ready_future<
@@ -590,7 +591,33 @@ private:
   /// Invalidate extent and mark affected transactions
   void invalidate(CachedExtent &extent);
 
-  Transaction::get_extent_ret query_cache_for_extent(
+  template <typename T>
+  get_extent_ret<T> read_extent(
+    TCachedExtentRef<T>&& extent
+  ) {
+    extent->set_io_wait();
+    return segment_manager.read(
+      extent->get_paddr(),
+      extent->get_length(),
+      extent->get_bptr()
+    ).safe_then(
+      [extent=std::move(extent)]() mutable {
+        /* TODO: crc should be checked against LBA manager */
+        extent->last_committed_crc = extent->get_crc32c();
+
+        extent->on_clean_read();
+        extent->complete_io();
+        return get_extent_ertr::make_ready_future<TCachedExtentRef<T>>(
+          std::move(extent));
+      },
+      get_extent_ertr::pass_further{},
+      crimson::ct_error::assert_all{
+        "Cache::get_extent: invalid error"
+      }
+    );
+  }
+
+  Transaction::get_extent_ret query_cache_with_placeholders(
     paddr_t offset,
     CachedExtentRef *out) {
     if (auto iter = extents.find_offset(offset);
index ed696491c2d3fc298868970628d786ffc06850f6..4cfc76c0168305895a3f8f4182e42e73bfde8e98 100644 (file)
@@ -694,15 +694,13 @@ inline void retired_extent_gate_t::token_t::drop_self() {
 /**
  * RetiredExtentPlaceholder
  *
- * Cache::retire_extent(Transaction&, paddr_t, extent_len_t) can retire
- * an extent not currently in cache.  In that case, we need to add a
- * placeholder to the cache until transactions that might reference
- * the extent complete as in the case where the extent is already cached.
- * Cache::complete_commit thus creates a RetiredExtentPlaceholder to
- * serve that purpose.  ptr is not populated, and state is set to
- * RETIRED.  Cache interfaces check for RETIRED and return EAGAIN if
- * encountered, so references to these placeholder extents should not
- * escape the Cache interface boundary.
+ * Cache::retire_extent_addr(Transaction&, paddr_t, extent_len_t) can retire an
+ * extent not currently in cache. In that case, in order to detect transaction
+ * invalidation, we need to add a placeholder to the cache to create the
+ * mapping back to the transaction. And whenever there is a transaction tries
+ * to read the placeholder extent out, Cache is responsible to replace the
+ * placeholder by the real one. Anyway, No placeholder extents should escape
+ * the Cache interface boundary.
  */
 class RetiredExtentPlaceholder : public CachedExtent {
   extent_len_t length;
index 25b437f2770413abbc5245d5ef9f6d43017c9ccf..ef77e17924ab05269d9267e6837d05f60768139a 100644 (file)
@@ -42,6 +42,9 @@ public:
     } else if (
       auto iter = read_set.find(addr);
       iter != read_set.end()) {
+      // placeholder in read-set should be in the retired-set
+      // at the same time.
+      assert(iter->ref->get_type() != extent_types_t::RETIRED_PLACEHOLDER);
       if (out)
        *out = iter->ref;
       return get_extent_ret::PRESENT;
@@ -90,6 +93,29 @@ public:
     write_set.insert(*ref);
   }
 
+  void replace_placeholder(CachedExtent& placeholder, CachedExtent& extent) {
+    ceph_assert(!is_weak());
+
+    assert(placeholder.get_type() == extent_types_t::RETIRED_PLACEHOLDER);
+    assert(extent.get_type() != extent_types_t::RETIRED_PLACEHOLDER);
+    assert(extent.get_type() != extent_types_t::ROOT);
+    assert(extent.get_paddr() == placeholder.get_paddr());
+    {
+      auto where = read_set.find(placeholder.get_paddr());
+      assert(where != read_set.end());
+      assert(where->ref.get() == &placeholder);
+      where = read_set.erase(where);
+      read_set.emplace_hint(where, this, &extent);
+    }
+    {
+      auto where = retired_set.find(&placeholder);
+      assert(where != retired_set.end());
+      assert(where->get() == &placeholder);
+      where = retired_set.erase(where);
+      retired_set.emplace_hint(where, &extent);
+    }
+  }
+
   void mark_segment_to_release(segment_id_t segment) {
     assert(to_release == NULL_SEG_ID);
     to_release = segment;