From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- src/crimson/os/seastore/segment_manager/block.cc | 402 +++++++++++++++++++++ src/crimson/os/seastore/segment_manager/block.h | 222 ++++++++++++ .../os/seastore/segment_manager/ephemeral.cc | 226 ++++++++++++ .../os/seastore/segment_manager/ephemeral.h | 111 ++++++ 4 files changed, 961 insertions(+) create mode 100644 src/crimson/os/seastore/segment_manager/block.cc create mode 100644 src/crimson/os/seastore/segment_manager/block.h create mode 100644 src/crimson/os/seastore/segment_manager/ephemeral.cc create mode 100644 src/crimson/os/seastore/segment_manager/ephemeral.h (limited to 'src/crimson/os/seastore/segment_manager') diff --git a/src/crimson/os/seastore/segment_manager/block.cc b/src/crimson/os/seastore/segment_manager/block.cc new file mode 100644 index 000000000..6a4991d42 --- /dev/null +++ b/src/crimson/os/seastore/segment_manager/block.cc @@ -0,0 +1,402 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include +#include + +#include "crimson/common/log.h" + +#include "include/buffer.h" +#include "crimson/os/seastore/segment_manager/block.h" + +namespace { + seastar::logger& logger() { + return crimson::get_logger(ceph_subsys_filestore); + } +} + + +namespace crimson::os::seastore::segment_manager::block { + +static write_ertr::future<> do_write( + seastar::file &device, + uint64_t offset, + bufferptr &bptr) +{ + logger().debug( + "block: do_write offset {} len {}", + offset, + bptr.length()); + return device.dma_write( + offset, + bptr.c_str(), + bptr.length() + ).handle_exception([](auto e) -> write_ertr::future { + logger().error( + "do_write: dma_write got error {}", + e); + return crimson::ct_error::input_output_error::make(); + }).then([length=bptr.length()](auto result) + -> write_ertr::future<> { + if (result != length) { + return crimson::ct_error::input_output_error::make(); + } + return write_ertr::now(); + }); +} + +static read_ertr::future<> do_read( + seastar::file &device, + uint64_t offset, + bufferptr &bptr) +{ + logger().debug( + "block: do_read offset {} len {}", + offset, + bptr.length()); + return device.dma_read( + offset, + bptr.c_str(), + bptr.length() + ).handle_exception([](auto e) -> read_ertr::future { + logger().error( + "do_read: dma_read got error {}", + e); + return crimson::ct_error::input_output_error::make(); + }).then([length=bptr.length()](auto result) -> read_ertr::future<> { + if (result != length) { + return crimson::ct_error::input_output_error::make(); + } + return read_ertr::now(); + }); +} + +write_ertr::future<> +SegmentStateTracker::write_out( + seastar::file &device, + uint64_t offset) +{ + return do_write(device, offset, bptr); +} + +write_ertr::future<> +SegmentStateTracker::read_in( + seastar::file &device, + uint64_t offset) +{ + return do_read( + device, + offset, + bptr); +} + +static +block_sm_superblock_t make_superblock( + const BlockSegmentManager::mkfs_config_t &config, + const seastar::stat_data &data) +{ + logger().debug( + "{}: size {}, block_size {}, allocated_size {}, configured_size {}", + __func__, + data.size, + data.block_size, + data.allocated_size, + config.total_size); + size_t size = (data.size == 0) ? config.total_size : data.size; + size_t raw_segments = size / config.segment_size; + size_t tracker_size = SegmentStateTracker::get_raw_size( + raw_segments, + data.block_size); + size_t segments = (size - tracker_size - data.block_size) + / config.segment_size; + return block_sm_superblock_t{ + size, + config.segment_size, + data.block_size, + segments, + data.block_size, + tracker_size + data.block_size, + config.meta + }; +} + +using open_device_ret = + BlockSegmentManager::access_ertr::future< + std::pair + >; +static +open_device_ret open_device(const std::string &in_path, seastar::open_flags mode) +{ + return seastar::do_with( + in_path, + [mode](auto &path) { + return seastar::file_stat(path, seastar::follow_symlink::yes + ).then([mode, &path](auto stat) mutable { + return seastar::open_file_dma(path, mode).then([=](auto file) { + logger().debug("open_device: open successful"); + return std::make_pair(file, stat); + }); + }).handle_exception([](auto e) -> open_device_ret { + logger().error( + "open_device: got error {}", + e); + return crimson::ct_error::input_output_error::make(); + }); + }); +} + + +static +BlockSegmentManager::access_ertr::future<> +write_superblock(seastar::file &device, block_sm_superblock_t sb) +{ + assert(ceph::encoded_sizeof_bounded() < + sb.block_size); + return seastar::do_with( + bufferptr(ceph::buffer::create_page_aligned(sb.block_size)), + [=, &device](auto &bp) { + bufferlist bl; + encode(sb, bl); + auto iter = bl.begin(); + assert(bl.length() < sb.block_size); + iter.copy(bl.length(), bp.c_str()); + logger().debug("write_superblock: doing writeout"); + return do_write(device, 0, bp); + }); +} + +static +BlockSegmentManager::access_ertr::future +read_superblock(seastar::file &device, seastar::stat_data sd) +{ + assert(ceph::encoded_sizeof_bounded() < + sd.block_size); + return seastar::do_with( + bufferptr(ceph::buffer::create_page_aligned(sd.block_size)), + [=, &device](auto &bp) { + return do_read( + device, + 0, + bp + ).safe_then([=, &bp] { + bufferlist bl; + bl.push_back(bp); + block_sm_superblock_t ret; + auto bliter = bl.cbegin(); + decode(ret, bliter); + return BlockSegmentManager::access_ertr::future( + BlockSegmentManager::access_ertr::ready_future_marker{}, + ret); + }); + }); +} + +BlockSegment::BlockSegment( + BlockSegmentManager &manager, segment_id_t id) + : manager(manager), id(id) {} + +segment_off_t BlockSegment::get_write_capacity() const +{ + return manager.get_segment_size(); +} + +Segment::close_ertr::future<> BlockSegment::close() +{ + manager.segment_close(id); + return close_ertr::now(); +} + +Segment::write_ertr::future<> BlockSegment::write( + segment_off_t offset, ceph::bufferlist bl) +{ + if (offset < write_pointer || offset % manager.superblock.block_size != 0) + return crimson::ct_error::invarg::make(); + + if (offset + bl.length() > manager.superblock.segment_size) + return crimson::ct_error::enospc::make(); + + write_pointer = offset + bl.length(); + return manager.segment_write({id, offset}, bl); +} + +Segment::close_ertr::future<> BlockSegmentManager::segment_close(segment_id_t id) +{ + assert(tracker); + tracker->set(id, segment_state_t::CLOSED); + return tracker->write_out(device, superblock.tracker_offset); +} + +Segment::write_ertr::future<> BlockSegmentManager::segment_write( + paddr_t addr, + ceph::bufferlist bl, + bool ignore_check) +{ + assert((bl.length() % superblock.block_size) == 0); + logger().debug( + "segment_write to segment {} at offset {}, physical offset {}, len {}", + addr.segment, + addr.offset, + get_offset(addr), + bl.length()); + + + // TODO send an iovec and avoid the copy -- bl should have aligned + // constituent buffers and they will remain unmodified until the write + // completes + return seastar::do_with( + bufferptr(ceph::buffer::create_page_aligned(bl.length())), + [&](auto &bp) { + auto iter = bl.cbegin(); + iter.copy(bl.length(), bp.c_str()); + return do_write(device, get_offset(addr), bp); + }); +} + +BlockSegmentManager::~BlockSegmentManager() +{ +} + +BlockSegmentManager::mount_ret BlockSegmentManager::mount(mount_config_t config) +{ + return open_device( + config.path, seastar::open_flags::rw | seastar::open_flags::dsync + ).safe_then([=](auto p) { + device = std::move(p.first); + auto sd = p.second; + return read_superblock(device, sd); + }).safe_then([=](auto sb) { + superblock = sb; + tracker = std::make_unique( + superblock.segments, + superblock.block_size); + return tracker->read_in( + device, + superblock.tracker_offset + ).safe_then([this] { + for (segment_id_t i = 0; i < tracker->get_capacity(); ++i) { + if (tracker->get(i) == segment_state_t::OPEN) { + tracker->set(i, segment_state_t::CLOSED); + } + } + return tracker->write_out(device, superblock.tracker_offset); + }); + }); +} + +BlockSegmentManager::mkfs_ret BlockSegmentManager::mkfs(mkfs_config_t config) +{ + return seastar::do_with( + seastar::file{}, + seastar::stat_data{}, + block_sm_superblock_t{}, + std::unique_ptr(), + [=](auto &device, auto &stat, auto &sb, auto &tracker) { + return open_device( + config.path, seastar::open_flags::rw + ).safe_then([&, config](auto p) { + device = p.first; + stat = p.second; + sb = make_superblock(config, stat); + return write_superblock(device, sb); + }).safe_then([&] { + logger().debug("BlockSegmentManager::mkfs: superblock written"); + tracker.reset(new SegmentStateTracker(sb.segments, sb.block_size)); + return tracker->write_out(device, sb.tracker_offset); + }).finally([&] { + return device.close(); + }).safe_then([] { + logger().debug("BlockSegmentManager::mkfs: complete"); + return mkfs_ertr::now(); + }); + }); +} + +BlockSegmentManager::close_ertr::future<> BlockSegmentManager::close() +{ + return device.close(); +} + +SegmentManager::open_ertr::future BlockSegmentManager::open( + segment_id_t id) +{ + if (id >= get_num_segments()) { + logger().error("BlockSegmentManager::open: invalid segment {}", id); + return crimson::ct_error::invarg::make(); + } + + if (tracker->get(id) != segment_state_t::EMPTY) { + logger().error( + "BlockSegmentManager::open: invalid segment {} state {}", + id, + tracker->get(id)); + return crimson::ct_error::invarg::make(); + } + + tracker->set(id, segment_state_t::OPEN); + return tracker->write_out(device, superblock.tracker_offset + ).safe_then([this, id] { + return open_ertr::future( + open_ertr::ready_future_marker{}, + SegmentRef(new BlockSegment(*this, id))); + }); +} + +SegmentManager::release_ertr::future<> BlockSegmentManager::release( + segment_id_t id) +{ + logger().debug("BlockSegmentManager::release: {}", id); + + if (id >= get_num_segments()) { + logger().error( + "BlockSegmentManager::release: invalid segment {}", + id); + return crimson::ct_error::invarg::make(); + } + + if (tracker->get(id) != segment_state_t::CLOSED) { + logger().error( + "BlockSegmentManager::release: invalid segment {} state {}", + id, + tracker->get(id)); + return crimson::ct_error::invarg::make(); + } + + tracker->set(id, segment_state_t::EMPTY); + return tracker->write_out(device, superblock.tracker_offset); +} + +SegmentManager::read_ertr::future<> BlockSegmentManager::read( + paddr_t addr, + size_t len, + ceph::bufferptr &out) +{ + if (addr.segment >= get_num_segments()) { + logger().error( + "BlockSegmentManager::read: invalid segment {}", + addr); + return crimson::ct_error::invarg::make(); + } + + if (addr.offset + len > superblock.segment_size) { + logger().error( + "BlockSegmentManager::read: invalid offset {}~{}!", + addr, + len); + return crimson::ct_error::invarg::make(); + } + + if (tracker->get(addr.segment) == segment_state_t::EMPTY) { + logger().error( + "BlockSegmentManager::read: read on invalid segment {} state {}", + addr.segment, + tracker->get(addr.segment)); + return crimson::ct_error::enoent::make(); + } + + return do_read( + device, + get_offset(addr), + out); +} + +} diff --git a/src/crimson/os/seastore/segment_manager/block.h b/src/crimson/os/seastore/segment_manager/block.h new file mode 100644 index 000000000..927b13e4e --- /dev/null +++ b/src/crimson/os/seastore/segment_manager/block.h @@ -0,0 +1,222 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#pragma once + +#include +#include + +#include +#include +#include + +#include "crimson/common/layout.h" + +#include "crimson/os/seastore/segment_manager.h" + +namespace crimson::os::seastore::segment_manager::block { + +struct block_sm_superblock_t { + size_t size = 0; + size_t segment_size = 0; + size_t block_size = 0; + + size_t segments = 0; + uint64_t tracker_offset = 0; + uint64_t first_segment_offset = 0; + + seastore_meta_t meta; + + DENC(block_sm_superblock_t, v, p) { + DENC_START(1, 1, p); + denc(v.size, p); + denc(v.segment_size, p); + denc(v.block_size, p); + denc(v.segments, p); + denc(v.tracker_offset, p); + denc(v.first_segment_offset, p); + denc(v.meta, p); + DENC_FINISH(p); + } +}; + +using write_ertr = crimson::errorator< + crimson::ct_error::input_output_error>; +using read_ertr = crimson::errorator< + crimson::ct_error::input_output_error>; + +/** + * SegmentStateTracker + * + * Tracks lifecycle state of each segment using space at the beginning + * of the drive. + */ +class SegmentStateTracker { + using segment_state_t = Segment::segment_state_t; + + bufferptr bptr; + + using L = absl::container_internal::Layout; + const L layout; + +public: + static size_t get_raw_size(size_t segments, size_t block_size) { + return p2roundup(segments, block_size); + } + + SegmentStateTracker(size_t segments, size_t block_size) + : bptr(ceph::buffer::create_page_aligned( + get_raw_size(segments, block_size))), + layout(bptr.length()) + { + ::memset( + bptr.c_str(), + static_cast(segment_state_t::EMPTY), + bptr.length()); + } + + size_t get_size() const { + return bptr.length(); + } + + size_t get_capacity() const { + return bptr.length(); + } + + segment_state_t get(segment_id_t offset) const { + assert(offset < get_capacity()); + return static_cast( + layout.template Pointer<0>( + bptr.c_str())[offset]); + } + + void set(segment_id_t offset, segment_state_t state) { + assert(offset < get_capacity()); + layout.template Pointer<0>(bptr.c_str())[offset] = + static_cast(state); + } + + write_ertr::future<> write_out( + seastar::file &device, + uint64_t offset); + + read_ertr::future<> read_in( + seastar::file &device, + uint64_t offset); +}; + +class BlockSegmentManager; +class BlockSegment final : public Segment { + friend class BlockSegmentManager; + BlockSegmentManager &manager; + const segment_id_t id; + segment_off_t write_pointer = 0; +public: + BlockSegment(BlockSegmentManager &manager, segment_id_t id); + + segment_id_t get_segment_id() const final { return id; } + segment_off_t get_write_capacity() const final; + segment_off_t get_write_ptr() const final { return write_pointer; } + close_ertr::future<> close() final; + write_ertr::future<> write(segment_off_t offset, ceph::bufferlist bl) final; + + ~BlockSegment() {} +}; + +/** + * BlockSegmentManager + * + * Implements SegmentManager on a conventional block device. + * SegmentStateTracker uses space at the start of the device to store + * state analagous to that of the segments of a zns device. + */ +class BlockSegmentManager final : public SegmentManager { +public: + using access_ertr = crimson::errorator< + crimson::ct_error::input_output_error, + crimson::ct_error::permission_denied, + crimson::ct_error::enoent>; + + + struct mount_config_t { + std::string path; + }; + using mount_ertr = access_ertr; + using mount_ret = access_ertr::future<>; + mount_ret mount(mount_config_t); + + struct mkfs_config_t { + std::string path; + size_t segment_size = 0; + size_t total_size = 0; + seastore_meta_t meta; + }; + using mkfs_ertr = access_ertr; + using mkfs_ret = mkfs_ertr::future<>; + static mkfs_ret mkfs(mkfs_config_t); + + using close_ertr = crimson::errorator< + crimson::ct_error::input_output_error + >; + close_ertr::future<> close(); + + BlockSegmentManager() = default; + ~BlockSegmentManager(); + + open_ertr::future open(segment_id_t id) final; + + release_ertr::future<> release(segment_id_t id) final; + + read_ertr::future<> read( + paddr_t addr, + size_t len, + ceph::bufferptr &out) final; + + size_t get_size() const final { + return superblock.size; + } + segment_off_t get_block_size() const { + return superblock.block_size; + } + segment_off_t get_segment_size() const { + return superblock.segment_size; + } + + // public so tests can bypass segment interface when simpler + Segment::write_ertr::future<> segment_write( + paddr_t addr, + ceph::bufferlist bl, + bool ignore_check=false); + +private: + friend class BlockSegment; + using segment_state_t = Segment::segment_state_t; + + + std::unique_ptr tracker; + block_sm_superblock_t superblock; + seastar::file device; + + size_t get_offset(paddr_t addr) { + return superblock.first_segment_offset + + (addr.segment * superblock.segment_size) + + addr.offset; + } + + const seastore_meta_t &get_meta() const { + return superblock.meta; + } + + std::vector segment_state; + + char *buffer = nullptr; + + Segment::close_ertr::future<> segment_close(segment_id_t id); +}; + +} + +WRITE_CLASS_DENC_BOUNDED( + crimson::os::seastore::segment_manager::block::block_sm_superblock_t +) + diff --git a/src/crimson/os/seastore/segment_manager/ephemeral.cc b/src/crimson/os/seastore/segment_manager/ephemeral.cc new file mode 100644 index 000000000..3250303ad --- /dev/null +++ b/src/crimson/os/seastore/segment_manager/ephemeral.cc @@ -0,0 +1,226 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include +#include + +#include "seastar/core/sleep.hh" + +#include "crimson/common/log.h" + +#include "include/buffer.h" +#include "crimson/os/seastore/segment_manager/ephemeral.h" + +namespace { + seastar::logger& logger() { + return crimson::get_logger(ceph_subsys_filestore); + } +} + +namespace crimson::os::seastore::segment_manager { + +std::ostream &operator<<(std::ostream &lhs, const ephemeral_config_t &c) { + return lhs << "ephemeral_config_t(size=" << c.size << ", block_size=" << c.block_size + << ", segment_size=" << c.segment_size << ")"; +} + +EphemeralSegmentManagerRef create_test_ephemeral() { + return EphemeralSegmentManagerRef( + new EphemeralSegmentManager(DEFAULT_TEST_EPHEMERAL)); +} + +EphemeralSegment::EphemeralSegment( + EphemeralSegmentManager &manager, segment_id_t id) + : manager(manager), id(id) {} + +segment_off_t EphemeralSegment::get_write_capacity() const +{ + return manager.get_segment_size(); +} + +Segment::close_ertr::future<> EphemeralSegment::close() +{ + manager.segment_close(id); + return close_ertr::now().safe_then([] { + return seastar::sleep(std::chrono::milliseconds(1)); + }); +} + +Segment::write_ertr::future<> EphemeralSegment::write( + segment_off_t offset, ceph::bufferlist bl) +{ + if (offset < write_pointer || offset % manager.config.block_size != 0) + return crimson::ct_error::invarg::make(); + + if (offset + bl.length() > (size_t)manager.get_segment_size()) + return crimson::ct_error::enospc::make(); + + return manager.segment_write({id, offset}, bl); +} + +Segment::close_ertr::future<> EphemeralSegmentManager::segment_close(segment_id_t id) +{ + if (segment_state[id] != segment_state_t::OPEN) + return crimson::ct_error::invarg::make(); + + segment_state[id] = segment_state_t::CLOSED; + return Segment::close_ertr::now().safe_then([] { + return seastar::sleep(std::chrono::milliseconds(1)); + }); +} + +Segment::write_ertr::future<> EphemeralSegmentManager::segment_write( + paddr_t addr, + ceph::bufferlist bl, + bool ignore_check) +{ + logger().debug( + "segment_write to segment {} at offset {}, physical offset {}, len {}, crc {}", + addr.segment, + addr.offset, + get_offset(addr), + bl.length(), + bl.crc32c(1)); + if (!ignore_check && segment_state[addr.segment] != segment_state_t::OPEN) + return crimson::ct_error::invarg::make(); + + bl.begin().copy(bl.length(), buffer + get_offset(addr)); + return Segment::write_ertr::now().safe_then([] { + return seastar::sleep(std::chrono::milliseconds(1)); + }); +} + +EphemeralSegmentManager::init_ertr::future<> EphemeralSegmentManager::init() +{ + logger().debug( + "Initing ephemeral segment manager with config {}", + config); + + meta = seastore_meta_t{}; + + if (config.block_size % (4<<10) != 0) { + return crimson::ct_error::invarg::make(); + } + if (config.segment_size % config.block_size != 0) { + return crimson::ct_error::invarg::make(); + } + if (config.size % config.segment_size != 0) { + return crimson::ct_error::invarg::make(); + } + + auto addr = ::mmap( + nullptr, + config.size, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, + -1, + 0); + + segment_state.resize(config.size / config.segment_size, segment_state_t::EMPTY); + + if (addr == MAP_FAILED) + return crimson::ct_error::enospc::make(); + + buffer = (char*)addr; + + ::memset(buffer, 0, config.size); + return init_ertr::now().safe_then([] { + return seastar::sleep(std::chrono::milliseconds(1)); + }); +} + +EphemeralSegmentManager::~EphemeralSegmentManager() +{ + if (buffer) { + ::munmap(buffer, config.size); + } +} + +void EphemeralSegmentManager::remount() +{ + for (auto &i : segment_state) { + if (i == Segment::segment_state_t::OPEN) + i = Segment::segment_state_t::CLOSED; + } +} + +SegmentManager::open_ertr::future EphemeralSegmentManager::open( + segment_id_t id) +{ + if (id >= get_num_segments()) { + logger().error("EphemeralSegmentManager::open: invalid segment {}", id); + return crimson::ct_error::invarg::make(); + } + + if (segment_state[id] != segment_state_t::EMPTY) { + logger().error("EphemeralSegmentManager::open: segment {} not empty", id); + return crimson::ct_error::invarg::make(); + } + + segment_state[id] = segment_state_t::OPEN; + return open_ertr::make_ready_future(new EphemeralSegment(*this, id)); +} + +SegmentManager::release_ertr::future<> EphemeralSegmentManager::release( + segment_id_t id) +{ + logger().debug("EphemeralSegmentManager::release: {}", id); + + if (id >= get_num_segments()) { + logger().error( + "EphemeralSegmentManager::release: invalid segment {}", + id); + return crimson::ct_error::invarg::make(); + } + + if (segment_state[id] != segment_state_t::CLOSED) { + logger().error( + "EphemeralSegmentManager::release: segment id {} not closed", + id); + return crimson::ct_error::invarg::make(); + } + + ::memset(buffer + get_offset({id, 0}), 0, config.segment_size); + segment_state[id] = segment_state_t::EMPTY; + return release_ertr::now().safe_then([] { + return seastar::sleep(std::chrono::milliseconds(1)); + }); +} + +SegmentManager::read_ertr::future<> EphemeralSegmentManager::read( + paddr_t addr, + size_t len, + ceph::bufferptr &out) +{ + if (addr.segment >= get_num_segments()) { + logger().error( + "EphemeralSegmentManager::read: invalid segment {}", + addr); + return crimson::ct_error::invarg::make(); + } + + if (addr.offset + len > config.segment_size) { + logger().error( + "EphemeralSegmentManager::read: invalid offset {}~{}!", + addr, + len); + return crimson::ct_error::invarg::make(); + } + + out.copy_in(0, len, buffer + get_offset(addr)); + + bufferlist bl; + bl.push_back(out); + logger().debug( + "segment_read to segment {} at offset {}, physical offset {}, length {}, crc {}", + addr.segment, + addr.offset, + get_offset(addr), + len, + bl.begin().crc32c(len, 1)); + + return read_ertr::now().safe_then([] { + return seastar::sleep(std::chrono::milliseconds(1)); + }); +} + +} diff --git a/src/crimson/os/seastore/segment_manager/ephemeral.h b/src/crimson/os/seastore/segment_manager/ephemeral.h new file mode 100644 index 000000000..9f19cb4d0 --- /dev/null +++ b/src/crimson/os/seastore/segment_manager/ephemeral.h @@ -0,0 +1,111 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#pragma once + +#include +#include +#include + +#include "crimson/os/seastore/segment_manager.h" + +#include "crimson/os/seastore/segment_manager/ephemeral.h" + +namespace crimson::os::seastore::segment_manager { + +class EphemeralSegmentManager; +using EphemeralSegmentManagerRef = std::unique_ptr; + +struct ephemeral_config_t { + size_t size = 0; + size_t block_size = 0; + size_t segment_size = 0; +}; + +constexpr ephemeral_config_t DEFAULT_TEST_EPHEMERAL = { + 1 << 30, + 4 << 10, + 8 << 20 +}; + +std::ostream &operator<<(std::ostream &, const ephemeral_config_t &); +EphemeralSegmentManagerRef create_test_ephemeral(); + +class EphemeralSegment final : public Segment { + friend class EphemeralSegmentManager; + EphemeralSegmentManager &manager; + const segment_id_t id; + segment_off_t write_pointer = 0; +public: + EphemeralSegment(EphemeralSegmentManager &manager, segment_id_t id); + + segment_id_t get_segment_id() const final { return id; } + segment_off_t get_write_capacity() const final; + segment_off_t get_write_ptr() const final { return write_pointer; } + close_ertr::future<> close() final; + write_ertr::future<> write(segment_off_t offset, ceph::bufferlist bl) final; + + ~EphemeralSegment() {} +}; + +class EphemeralSegmentManager final : public SegmentManager { + friend class EphemeralSegment; + using segment_state_t = Segment::segment_state_t; + + const ephemeral_config_t config; + std::optional meta; + + size_t get_offset(paddr_t addr) { + return (addr.segment * config.segment_size) + addr.offset; + } + + std::vector segment_state; + + char *buffer = nullptr; + + Segment::close_ertr::future<> segment_close(segment_id_t id); + +public: + EphemeralSegmentManager(ephemeral_config_t config) : config(config) {} + ~EphemeralSegmentManager(); + + using init_ertr = crimson::errorator< + crimson::ct_error::enospc, + crimson::ct_error::invarg, + crimson::ct_error::erange>; + init_ertr::future<> init(); + + open_ertr::future open(segment_id_t id) final; + + release_ertr::future<> release(segment_id_t id) final; + + read_ertr::future<> read( + paddr_t addr, + size_t len, + ceph::bufferptr &out) final; + + size_t get_size() const final { + return config.size; + } + segment_off_t get_block_size() const final { + return config.block_size; + } + segment_off_t get_segment_size() const final { + return config.segment_size; + } + + const seastore_meta_t &get_meta() const final { + assert(meta); + return *meta; + } + + void remount(); + + // public so tests can bypass segment interface when simpler + Segment::write_ertr::future<> segment_write( + paddr_t addr, + ceph::bufferlist bl, + bool ignore_check=false); +}; + +} -- cgit v1.2.3