]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/test/crimson/seastore/test_cbjournal.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / test / crimson / seastore / test_cbjournal.cc
diff --git a/ceph/src/test/crimson/seastore/test_cbjournal.cc b/ceph/src/test/crimson/seastore/test_cbjournal.cc
new file mode 100644 (file)
index 0000000..05cd542
--- /dev/null
@@ -0,0 +1,515 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/crimson/gtest_seastar.h"
+
+#include <random>
+
+#include "crimson/common/log.h"
+#include "crimson/os/seastore/async_cleaner.h"
+#include "crimson/os/seastore/journal.h"
+#include "crimson/os/seastore/journal/circular_bounded_journal.h"
+#include "crimson/os/seastore/random_block_manager.h"
+#include "crimson/os/seastore/random_block_manager/rbm_device.h"
+#include "crimson/os/seastore/seastore_types.h"
+#include "test/crimson/seastore/transaction_manager_test_state.h"
+#include "crimson/os/seastore/random_block_manager/block_rb_manager.h"
+
+using namespace crimson;
+using namespace crimson::os;
+using namespace crimson::os::seastore;
+using namespace crimson::os::seastore::journal;
+
+namespace {
+  [[maybe_unused]] seastar::logger& logger() {
+    return crimson::get_logger(ceph_subsys_test);
+  }
+}
+
+std::optional<record_t> decode_record(
+  bufferlist& bl)
+{
+  record_t record;
+  record_group_header_t r_header;
+  auto bliter = bl.cbegin();
+  decode(r_header, bliter);
+  logger().debug(" decode_record mdlength {} records {}",
+                 r_header.mdlength, r_header.records);
+  device_id_t d_id = 1 << (std::numeric_limits<device_id_t>::digits - 1);
+
+  auto del_infos = try_decode_deltas(r_header, bl,
+    paddr_t::make_blk_paddr(d_id, 0));
+  for (auto &iter : *del_infos) {
+    for (auto r : iter.deltas) {
+      record.deltas.push_back(r.second);
+    }
+  }
+  auto ex_infos = try_decode_extent_infos(r_header, bl);
+  auto bliter_ex = bl.cbegin();
+  bliter_ex += r_header.mdlength;
+  for (auto &iter: *ex_infos) {
+    for (auto e : iter.extent_infos) {
+      extent_t ex;
+      auto bptr = bufferptr(ceph::buffer::create_page_aligned(e.len));
+      logger().debug(" exten len {} remaining {} ", e.len, bliter_ex.get_remaining());
+      bliter_ex.copy(e.len, bptr.c_str());
+      ex.bl.append(bptr);
+      record.extents.push_back(ex);
+    }
+  }
+  return record;
+}
+
+struct entry_validator_t {
+  bufferlist bl;
+  int entries;
+  journal_seq_t last_seq;
+  record_t record;
+  rbm_abs_addr addr = 0;
+
+  template <typename... T>
+  entry_validator_t(T&&... entry) : record(std::forward<T>(entry)...) {}
+
+  void validate(record_t read) {
+    auto iter = read.extents.begin();
+    for (auto &&block : record.extents) {
+      ASSERT_EQ(
+       iter->bl.length(),
+       block.bl.length());
+      ASSERT_EQ(
+       iter->bl.begin().crc32c(iter->bl.length(), 1),
+       block.bl.begin().crc32c(block.bl.length(), 1));
+      ++iter;
+    }
+    auto iter_delta = read.deltas.begin();
+    for (auto &&block : record.deltas) {
+      ASSERT_EQ(
+       iter_delta->bl.length(),
+       block.bl.length());
+      ASSERT_EQ(
+       iter_delta->bl.begin().crc32c(iter_delta->bl.length(), 1),
+       block.bl.begin().crc32c(block.bl.length(), 1));
+      ++iter_delta;
+    }
+  }
+
+  void validate(CircularBoundedJournal &cbj) {
+    rbm_abs_addr offset = 0;
+    for (int i = 0; i < entries; i++) {
+      paddr_t paddr = convert_abs_addr_to_paddr(
+       addr + offset,
+       cbj.get_device_id());
+      auto [header, buf] = *(cbj.read_record(paddr, NULL_SEG_SEQ).unsafe_get0());
+      auto record = decode_record(buf);
+      validate(*record);
+      offset += header.mdlength + header.dlength;
+    }
+  }
+
+  bool validate_delta(bufferlist bl) {
+    for (auto &&block : record.deltas) {
+      if (bl.begin().crc32c(bl.length(), 1) ==
+         block.bl.begin().crc32c(block.bl.length(), 1)) {
+       return true;
+      }
+    }
+    return false;
+  }
+};
+
+struct cbjournal_test_t : public seastar_test_suite_t, JournalTrimmer
+{
+  std::vector<entry_validator_t> entries;
+  std::unique_ptr<CircularBoundedJournal> cbj;
+  random_block_device::EphemeralRBMDeviceRef device;
+
+  std::default_random_engine generator;
+  uint64_t block_size;
+  WritePipeline pipeline;
+
+  cbjournal_test_t() = default;
+
+  /*
+   * JournalTrimmer interfaces
+   */
+  journal_seq_t get_journal_head() const {
+    return JOURNAL_SEQ_NULL;
+  }
+
+  journal_seq_t get_dirty_tail() const final {
+    return JOURNAL_SEQ_NULL;
+  }
+
+  journal_seq_t get_alloc_tail() const final {
+    return JOURNAL_SEQ_NULL;
+  }
+
+  void set_journal_head(journal_seq_t head) final {}
+
+  void update_journal_tails(
+    journal_seq_t dirty_tail,
+    journal_seq_t alloc_tail) final {}
+
+  bool try_reserve_inline_usage(std::size_t) final { return true; }
+
+  void release_inline_usage(std::size_t) final {}
+
+  std::size_t get_trim_size_per_cycle() const final {
+    return 0;
+  }
+
+  auto submit_record(record_t&& record) {
+    entries.push_back(record);
+    OrderingHandle handle = get_dummy_ordering_handle();
+    auto [addr, w_result] = cbj->submit_record(
+         std::move(record),
+         handle).unsafe_get0();
+    entries.back().addr = 
+      convert_paddr_to_abs_addr(w_result.start_seq.offset);
+    entries.back().entries = 1;
+    logger().debug("submit entry to addr {}", entries.back().addr);
+    return entries.back().addr;
+  }
+
+  seastar::future<> tear_down_fut() final {
+    return close();
+  }
+
+  extent_t generate_extent(size_t blocks) {
+    std::uniform_int_distribution<char> distribution(
+      std::numeric_limits<char>::min(),
+      std::numeric_limits<char>::max()
+    );
+    char contents = distribution(generator);
+    bufferlist bl;
+    bl.append(buffer::ptr(buffer::create(blocks * block_size, contents)));
+    return extent_t{extent_types_t::TEST_BLOCK, L_ADDR_NULL, bl};
+  }
+
+  delta_info_t generate_delta(size_t bytes) {
+    std::uniform_int_distribution<char> distribution(
+       std::numeric_limits<char>::min(),
+       std::numeric_limits<char>::max()
+       );
+    char contents = distribution(generator);
+    bufferlist bl;
+    bl.append(buffer::ptr(buffer::create(bytes, contents)));
+    return delta_info_t{
+      extent_types_t::TEST_BLOCK,
+       paddr_t{},
+       L_ADDR_NULL,
+       0, 0,
+       device->get_block_size(),
+       1,
+       0,
+       segment_type_t::JOURNAL,
+       bl
+    };
+  }
+
+  auto replay_and_check() {
+    for (auto &i : entries) {
+      i.validate(*(cbj.get()));
+    }
+  }
+
+  auto replay() {
+    return cbj->replay(
+      [this](const auto &offsets,
+            const auto &e,
+            auto &dirty_seq,
+            auto &alloc_seq,
+            auto last_modified) {
+      bool found = false;
+      for (auto &i : entries) {
+       paddr_t base = offsets.write_result.start_seq.offset; 
+       rbm_abs_addr addr = convert_paddr_to_abs_addr(base);
+       if (addr == i.addr) {
+         logger().debug(" compare addr: {} and i.addr {} ", base, i.addr);
+         found = i.validate_delta(e.bl);
+         break;
+       }
+      }
+      assert(found == true);
+      return Journal::replay_ertr::make_ready_future<bool>(true);
+    });
+  }
+
+  auto mkfs() {
+    device_config_t config = get_rbm_ephemeral_device_config(0, 1);
+    return device->mkfs(config
+    ).safe_then([this]() {
+      return device->mount(
+      ).safe_then([this]() {
+       return cbj->open_for_mkfs(
+       ).safe_then([](auto q) {
+         return seastar::now();
+       });
+      });
+    }).safe_then([this] {
+      return cbj->close();
+    });
+  }
+  auto open() {
+    return cbj->open_for_mount(
+    ).safe_then([](auto q) {
+      return seastar::now();
+    });
+  }
+  seastar::future<> close() {
+    return cbj->close().handle_error(crimson::ct_error::assert_all{});
+  }
+  auto get_records_available_size() {
+    return cbj->get_cjs().get_records_available_size();
+  }
+  auto get_records_total_size() {
+    return cbj->get_cjs().get_records_total_size();
+  }
+  auto get_block_size() {
+    return device->get_block_size();
+  }
+  auto get_written_to_rbm_addr() {
+    return cbj->get_rbm_addr(cbj->get_cjs().get_written_to());
+  }
+  auto get_written_to() {
+    return cbj->get_cjs().get_written_to();
+  }
+  auto get_journal_tail() {
+    return cbj->get_dirty_tail();
+  }
+  auto get_records_used_size() {
+    return cbj->get_cjs().get_records_used_size();
+  }
+  bool is_available_size(uint64_t size) {
+    return cbj->get_cjs().is_available_size(size);
+  }
+  void update_journal_tail(rbm_abs_addr addr, uint32_t len) {
+    paddr_t paddr =
+      convert_abs_addr_to_paddr(
+         addr + len,
+         cbj->get_device_id());
+    journal_seq_t seq = {0, paddr};
+    cbj->update_journal_tail(
+      seq,
+      seq
+    ).get0();
+  }
+  void set_written_to(journal_seq_t seq) {
+    cbj->set_written_to(seq);
+  }
+
+  seastar::future<> set_up_fut() final {
+    device = random_block_device::create_test_ephemeral(
+     random_block_device::DEFAULT_TEST_CBJOURNAL_SIZE, 0);
+    cbj.reset(new CircularBoundedJournal(*this, device.get(), std::string()));
+    block_size = device->get_block_size();
+    cbj->set_write_pipeline(&pipeline);
+    return mkfs(
+    ).safe_then([this] {
+      return replay(
+      ).safe_then([this] {
+       return open();
+      });
+    }).handle_error(crimson::ct_error::assert_all{});
+  }
+};
+
+TEST_F(cbjournal_test_t, submit_one_record)
+{
+  run_async([this] {
+    submit_record(
+     record_t{
+      { generate_extent(1), generate_extent(2) },
+      { generate_delta(3), generate_delta(4) }
+      });
+    replay_and_check();
+  });
+}
+
+TEST_F(cbjournal_test_t, submit_three_records)
+{
+  run_async([this] {
+    submit_record(
+     record_t{
+      { generate_extent(1), generate_extent(2) },
+      { generate_delta(3), generate_delta(4) }
+      });
+    submit_record(
+     record_t{
+      { generate_extent(8), generate_extent(9) },
+      { generate_delta(20), generate_delta(21) }
+      });
+    submit_record(
+     record_t{
+      { generate_extent(5), generate_extent(6) },
+      { generate_delta(200), generate_delta(210) }
+      });
+    replay_and_check();
+  });
+}
+
+TEST_F(cbjournal_test_t, submit_full_records)
+{
+  run_async([this] {
+    record_t rec {
+     { generate_extent(1), generate_extent(2) },
+     { generate_delta(20), generate_delta(21) }
+     };
+    auto r_size = record_group_size_t(rec.size, block_size);
+    auto record_total_size = r_size.get_encoded_length();
+
+    submit_record(std::move(rec));
+    while (is_available_size(record_total_size)) {
+     submit_record(
+       record_t {
+       { generate_extent(1), generate_extent(2) },
+       { generate_delta(20), generate_delta(21) }
+       });
+    }
+
+    update_journal_tail(entries.back().addr, record_total_size);
+    ASSERT_EQ(get_records_total_size(),
+            get_records_available_size());
+
+    // will be appended at the begining of log
+    submit_record(
+     record_t {
+      { generate_extent(1), generate_extent(2) },
+      { generate_delta(20), generate_delta(21) }
+      });
+
+    while (is_available_size(record_total_size)) {
+     submit_record(
+       record_t {
+       { generate_extent(1), generate_extent(2) },
+       { generate_delta(20), generate_delta(21) }
+       });
+    }
+    ASSERT_TRUE(record_total_size > get_records_available_size());
+  });
+}
+
+TEST_F(cbjournal_test_t, boudary_check_verify)
+{
+  run_async([this] {
+    record_t rec {
+     { generate_extent(1), generate_extent(2) },
+     { generate_delta(20), generate_delta(21) }
+     };
+    auto r_size = record_group_size_t(rec.size, block_size);
+    auto record_total_size = r_size.get_encoded_length();
+    submit_record(std::move(rec));
+    while (is_available_size(record_total_size)) {
+     submit_record(
+       record_t {
+       { generate_extent(1), generate_extent(2) },
+       { generate_delta(20), generate_delta(21) }
+       });
+    }
+
+    uint64_t avail = get_records_available_size();
+    // forward 2 recod size here because 1 block is reserved between head and tail
+    update_journal_tail(entries.front().addr, record_total_size * 2);
+    entries.erase(entries.begin());
+    entries.erase(entries.begin());
+    ASSERT_EQ(avail + (record_total_size * 2), get_records_available_size());
+    avail = get_records_available_size();
+    // will be appended at the begining of WAL
+    submit_record(
+     record_t {
+      { generate_extent(1), generate_extent(2) },
+      { generate_delta(20), generate_delta(21) }
+      });
+    ASSERT_TRUE(avail - record_total_size >= get_records_available_size());
+    replay_and_check();
+  });
+}
+
+TEST_F(cbjournal_test_t, update_header)
+{
+  run_async([this] {
+    auto [header, _buf] = *(cbj->get_cjs().read_header().unsafe_get0());
+    record_t rec {
+     { generate_extent(1), generate_extent(2) },
+     { generate_delta(20), generate_delta(21) }
+     };
+    auto r_size = record_group_size_t(rec.size, block_size);
+    auto record_total_size = r_size.get_encoded_length();
+    submit_record(std::move(rec));
+
+    update_journal_tail(entries.front().addr, record_total_size);
+    cbj->get_cjs().write_header().unsafe_get0();
+    auto [update_header, update_buf2] = *(cbj->get_cjs().read_header().unsafe_get0());
+    cbj->close().unsafe_get0();
+    replay().unsafe_get0();
+
+    ASSERT_EQ(update_header.dirty_tail.offset, update_header.dirty_tail.offset);
+  });
+}
+
+TEST_F(cbjournal_test_t, replay)
+{
+  run_async([this] {
+    record_t rec {
+     { generate_extent(1), generate_extent(2) },
+     { generate_delta(20), generate_delta(21) }
+     };
+    auto r_size = record_group_size_t(rec.size, block_size);
+    auto record_total_size = r_size.get_encoded_length();
+    submit_record(std::move(rec));
+    while (is_available_size(record_total_size)) {
+    submit_record(
+      record_t {
+       { generate_extent(1), generate_extent(2) },
+       { generate_delta(20), generate_delta(21) }
+       });
+    }
+    // will be appended at the begining of WAL
+    uint64_t avail = get_records_available_size();
+    update_journal_tail(entries.front().addr, record_total_size * 2);
+    entries.erase(entries.begin());
+    entries.erase(entries.begin());
+    ASSERT_EQ(avail + (record_total_size * 2), get_records_available_size());
+    avail = get_records_available_size();
+    submit_record(
+      record_t {
+       { generate_extent(1), generate_extent(2) },
+       { generate_delta(20), generate_delta(21) }
+       });
+    ASSERT_TRUE(avail - record_total_size >= get_records_available_size());
+    cbj->close().unsafe_get0();
+    replay().unsafe_get0();
+  });
+}
+
+TEST_F(cbjournal_test_t, replay_after_reset)
+{
+  run_async([this] {
+    record_t rec {
+     { generate_extent(1), generate_extent(2) },
+     { generate_delta(20), generate_delta(21) }
+     };
+    auto r_size = record_group_size_t(rec.size, block_size);
+    auto record_total_size = r_size.get_encoded_length();
+    submit_record(std::move(rec));
+    while (is_available_size(record_total_size)) {
+    submit_record(
+      record_t {
+       { generate_extent(1), generate_extent(2) },
+       { generate_delta(20), generate_delta(21) }
+       });
+    }
+    auto old_written_to = get_written_to();
+    auto old_used_size = get_records_used_size();
+    set_written_to(
+      journal_seq_t{0,
+       convert_abs_addr_to_paddr(
+         cbj->get_records_start(),
+         cbj->get_device_id())});
+    cbj->close().unsafe_get0();
+    replay().unsafe_get0();
+    ASSERT_EQ(old_written_to, get_written_to());
+    ASSERT_EQ(old_used_size,
+      get_records_used_size());
+  });
+}