Skip to content

Commit

Permalink
Merge branch 'yk_repl_long' of github.com:yamingk/HomeStore into yk_r…
Browse files Browse the repository at this point in the history
…epl_long
  • Loading branch information
yamingk committed May 13, 2024
2 parents 65eb749 + 1729bc7 commit 55f5a3d
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 338 deletions.
1 change: 0 additions & 1 deletion src/include/homestore/vchunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class VChunk {
const uint8_t* get_user_private() const;
blk_num_t get_total_blks() const;
blk_num_t available_blks() const;
blk_num_t get_freeable_nblks() const;
blk_num_t get_defrag_nblks() const;
uint32_t get_pdev_id() const;
uint16_t get_chunk_id() const;
Expand Down
162 changes: 78 additions & 84 deletions src/lib/blkalloc/append_blk_allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,66 +30,43 @@ AppendBlkAllocator::AppendBlkAllocator(const BlkAllocConfig& cfg, bool need_form
nullptr);

if (need_format) {
m_freeable_nblks = available_blks();
m_freeable_nblks = 0;
m_last_append_offset = 0;
m_commit_offset = 0;
}

// for both fresh start and recovery, firstly init m_sb fields;
m_sb.create(sizeof(append_blk_sb_t));
m_sb.set_name(get_name());
m_sb->allocator_id = id;
m_sb->last_append_offset = m_last_append_offset;
m_sb->commit_offset = m_last_append_offset;
m_sb->freeable_nblks = m_freeable_nblks;

for (auto i = 0ul; i < m_dirty_sb.size(); ++i) {
m_dirty_sb[i].is_dirty = false;
}

// for recovery boot, fields will also be recovered from metablks;
}

void AppendBlkAllocator::on_meta_blk_found(const sisl::byte_view& buf, void* meta_cookie) {
m_sb.load(buf, meta_cookie);

// recover in-memory counter/offset from metablk;
m_last_append_offset = m_sb->last_append_offset;
m_freeable_nblks = m_sb->freeable_nblks;

HS_REL_ASSERT_EQ(m_sb->magic, append_blkalloc_sb_magic, "Invalid AppendBlkAlloc metablk, magic mismatch");
HS_REL_ASSERT_EQ(m_sb->version, append_blkalloc_sb_version, "Invalid version of AppendBlkAllocator metablk");

// recover in-memory counter/offset from metablk;
m_last_append_offset.store(m_sb->commit_offset);
m_commit_offset.store(m_sb->commit_offset);
m_freeable_nblks.store(m_sb->freeable_nblks);
}

//
// Every time buffer is being dirtied, it needs to be within CPGuard().
// It garunteens either this dirty buffer is flushed in current cp or next cp as a whole;
//
// alloc a single block;
//
BlkAllocStatus AppendBlkAllocator::alloc_contiguous(BlkId& bid) {
std::unique_lock lk(m_mtx);
if (available_blks() < 1) {
COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1);
LOGERROR("No space left to serve request nblks: 1, available_blks: {}", available_blks());
return BlkAllocStatus::SPACE_FULL;
}

bid = BlkId{m_last_append_offset, 1, m_chunk_id};

auto cur_cp = hs()->cp_mgr().cp_guard();
++m_last_append_offset;
--m_freeable_nblks;
set_dirty_offset(cur_cp->id() % MAX_CP_COUNT);

COUNTER_INCREMENT(m_metrics, num_alloc, 1);
return BlkAllocStatus::SUCCESS;
}
BlkAllocStatus AppendBlkAllocator::alloc_contiguous(BlkId& bid) { return alloc(1, blk_alloc_hints{}, bid); }

//
// For append blk allocator, the assumption is only one writer will append data on one chunk.
// If we want to change above design, we can open this api for vector allocation;
//
BlkAllocStatus AppendBlkAllocator::alloc(blk_count_t nblks, const blk_alloc_hints& hint, BlkId& out_bid) {
std::unique_lock lk(m_mtx);
if (available_blks() < nblks) {
COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1);
LOGERROR("No space left to serve request nblks: {}, available_blks: {}", nblks, available_blks());
Expand All @@ -102,95 +79,112 @@ BlkAllocStatus AppendBlkAllocator::alloc(blk_count_t nblks, const blk_alloc_hint
}

// Push 1 blk to the vector which has all the requested nblks;
out_bid = BlkId{m_last_append_offset, nblks, m_chunk_id};

auto cur_cp = hs()->cp_mgr().cp_guard();
m_last_append_offset += nblks;
m_freeable_nblks -= nblks;

// it is guaranteed that dirty buffer always contains updates of current_cp or next_cp, it will
// never get dirty buffer from across updates;
set_dirty_offset(cur_cp->id() % MAX_CP_COUNT);
out_bid = BlkId{m_last_append_offset.fetch_add(nblks), nblks, m_chunk_id};

COUNTER_INCREMENT(m_metrics, num_alloc, 1);

return BlkAllocStatus::SUCCESS;
}

BlkAllocStatus AppendBlkAllocator::alloc_on_disk(BlkId const&) { return BlkAllocStatus::SUCCESS; }

BlkAllocStatus AppendBlkAllocator::mark_blk_allocated(BlkId const&) { return BlkAllocStatus::SUCCESS; }

void AppendBlkAllocator::free_on_disk(BlkId const&) {}
// Reserve on disk will update the commit_offset with the new_offset, if its above the current commit_offset.
BlkAllocStatus AppendBlkAllocator::reserve_on_disk(BlkId const& blkid) {
HS_DBG_ASSERT(is_blk_alloced(blkid), "Trying to reserve on disk for unallocated blkid={}", blkid);

auto new_offset = blkid.blk_num() + blkid.blk_count();
auto cur_offset = m_commit_offset.load();
bool modified{true};
do {
if (cur_offset >= new_offset) {
// Already allocated
modified = false;
break;
}
} while (!m_commit_offset.compare_exchange_weak(cur_offset, new_offset));

if (modified) { m_is_dirty.store(true); }
return BlkAllocStatus::SUCCESS;
}

bool AppendBlkAllocator::is_blk_alloced_on_disk(BlkId const&, bool) const { return false; }
// Given that AppendBlkAllocator uses highest of all offsets during the commit during recovery, we don't need to
// individually recover each block.
BlkAllocStatus AppendBlkAllocator::reserve_on_cache(BlkId const& blkid) {
auto new_offset = blkid.blk_num() + blkid.blk_count();
auto cur_offset = m_last_append_offset.load();
do {
if (cur_offset >= new_offset) { break; }
} while (!m_last_append_offset.compare_exchange_weak(cur_offset, new_offset));
return BlkAllocStatus::SUCCESS;
}

//
// cp_flush doesn't need CPGuard as it is triggered by CPMgr which already handles the reference check;
//
// cp_flush should not block alloc/free;
//
void AppendBlkAllocator::cp_flush(CP* cp) {
const auto idx = cp->id() % MAX_CP_COUNT;
// check if current cp's context has dirty buffer already
if (m_dirty_sb[idx].is_dirty) {
m_sb->last_append_offset = m_dirty_sb[idx].last_append_offset;
m_sb->freeable_nblks = m_dirty_sb[idx].freeable_nblks;
if (m_is_dirty.exchange(false)) {
m_sb->commit_offset = m_commit_offset.load();
m_sb->freeable_nblks = m_freeable_nblks.load();

// write to metablk;
m_sb.write();

// clear this dirty buff's dirty flag;
clear_dirty_offset(idx);
}
}

// updating current cp's dirty buffer context;
void AppendBlkAllocator::set_dirty_offset(const uint8_t idx) {
m_dirty_sb[idx].is_dirty = true;

m_dirty_sb[idx].last_append_offset = m_last_append_offset;
m_dirty_sb[idx].freeable_nblks = m_freeable_nblks;
}

// clearing current cp context's dirty flag;
void AppendBlkAllocator::clear_dirty_offset(const uint8_t idx) { m_dirty_sb[idx].is_dirty = false; }

//
// free operation does:
// 1. book keeping "total freeable" space
// 2. if the blk being freed happens to be last block, move last_append_offset backwards accordingly;
//
void AppendBlkAllocator::free(const BlkId& bid) {
std::unique_lock lk(m_mtx);
auto cur_cp = hs()->cp_mgr().cp_guard();
const auto n = bid.blk_count();
m_freeable_nblks += n;
if (bid.blk_num() + n == m_last_append_offset) {
// we are freeing the the last blk id, let's rewind.
m_last_append_offset -= n;
// If we are freeing the last block, just move the offset back
blk_num_t cur_last_offset = m_last_append_offset.load();
auto const input_last_offset = bid.blk_num() + bid.blk_count();
blk_num_t new_last_offset;
bool freeing_in_middle{false};
do {
if (input_last_offset == cur_last_offset) {
new_last_offset = bid.blk_num();
freeing_in_middle = false;
} else {
new_last_offset = cur_last_offset;
freeing_in_middle = true;
}
} while (!m_last_append_offset.compare_exchange_weak(cur_last_offset, new_last_offset));

if (freeing_in_middle) {
// Freeing something in the middle, increment the count
m_freeable_nblks.fetch_add(bid.blk_count());
} else {
m_commit_offset.store(m_last_append_offset.load());
}
set_dirty_offset(cur_cp->id() % MAX_CP_COUNT);
m_is_dirty.store(true);
}

bool AppendBlkAllocator::is_blk_alloced(const BlkId& in_bid, bool) const {
// blk_num starts from 0;
return in_bid.blk_num() < get_used_blks();
}

bool AppendBlkAllocator::is_blk_alloced_on_disk(BlkId const& bid, bool) const {
return bid.blk_num() < m_sb->commit_offset;
}

std::string AppendBlkAllocator::get_name() const { return "AppendBlkAlloc_chunk_" + std::to_string(m_chunk_id); }

std::string AppendBlkAllocator::to_string() const {
return fmt::format("{}, last_append_offset: {}", get_name(), m_last_append_offset);
return fmt::format("{}, last_append_offset: {} fragmented_nblks={}", get_name(),
m_last_append_offset.load(std::memory_order_relaxed), get_defrag_nblks());
}

blk_num_t AppendBlkAllocator::available_blks() const { return get_total_blks() - get_used_blks(); }

blk_num_t AppendBlkAllocator::get_used_blks() const { return m_last_append_offset; }
blk_num_t AppendBlkAllocator::get_used_blks() const { return m_last_append_offset.load(std::memory_order_relaxed); }

blk_num_t AppendBlkAllocator::get_freeable_nblks() const { return m_freeable_nblks; }
blk_num_t AppendBlkAllocator::get_defrag_nblks() const { return m_freeable_nblks.load(std::memory_order_relaxed); }

blk_num_t AppendBlkAllocator::get_defrag_nblks() const { return get_freeable_nblks() - available_blks(); }

nlohmann::json AppendBlkAllocator::get_status(int log_level) const { return nlohmann::json{}; }
nlohmann::json AppendBlkAllocator::get_status(int log_level) const {
nlohmann::json j;
j["total_blks"] = get_total_blks();
j["next_append_blk_num"] = m_last_append_offset.load(std::memory_order_relaxed);
j["commit_offset"] = m_commit_offset.load(std::memory_order_relaxed);
j["freeable_nblks"] = m_freeable_nblks.load(std::memory_order_relaxed);
return j;
}
} // namespace homestore
40 changes: 10 additions & 30 deletions src/lib/blkalloc/append_blk_allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,7 @@ struct append_blk_sb_t {
uint32_t version{append_blkalloc_sb_version};
allocator_id_t allocator_id; // doesn't expect this to be changed once initialized;
blk_num_t freeable_nblks;
blk_num_t last_append_offset;
};

struct append_blk_dirty_buf_t {
bool is_dirty; // this field is needed for cp_flush, but not necessarily needed for persistence;
blk_num_t freeable_nblks;
blk_num_t last_append_offset;
blk_num_t commit_offset;
};
#pragma pack()

Expand Down Expand Up @@ -83,10 +77,9 @@ class AppendBlkAllocator : public BlkAllocator {
BlkAllocStatus alloc_contiguous(BlkId& bid) override;
BlkAllocStatus alloc(blk_count_t nblks, blk_alloc_hints const& hints, BlkId& out_blkid) override;
void free(BlkId const& b) override;
BlkAllocStatus alloc_on_disk(BlkId const& in_bid) override;
BlkAllocStatus mark_blk_allocated(BlkId const& b) override;
BlkAllocStatus reserve_on_disk(BlkId const& in_bid) override;
BlkAllocStatus reserve_on_cache(BlkId const& b) override;

void free_on_disk(BlkId const& b) override;
bool is_blk_alloced_on_disk(BlkId const& b, bool use_lock = false) const override;

/**
Expand All @@ -102,14 +95,8 @@ class AppendBlkAllocator : public BlkAllocator {
blk_num_t get_used_blks() const override;

/**
* @brief : the number of freeable blocks by the AppendBlkAllocator.
* @return : the number of freeable blocks.
*/
blk_num_t get_freeable_nblks() const;

/**
* @brief : the number of blocks that have been allocated by the AppendBlkAllocator.
* @return : the number of allocated blocks.
* @brief : the number of blocks that have been fragmented by the free
* @return : the number of fragmented blks
*/
blk_num_t get_defrag_nblks() const;

Expand All @@ -121,13 +108,6 @@ class AppendBlkAllocator : public BlkAllocator {

std::string to_string() const override;

/// @brief : needs to be called with cp_guard();
void set_dirty_offset(const uint8_t idx);

/// @brief : clear dirty is best effort;
/// offset flush is idempotent;
void clear_dirty_offset(const uint8_t idx);

void cp_flush(CP* cp) override;

nlohmann::json get_status(int log_level) const override;
Expand All @@ -137,12 +117,12 @@ class AppendBlkAllocator : public BlkAllocator {
void on_meta_blk_found(const sisl::byte_view& buf, void* meta_cookie);

private:
std::mutex m_mtx; // thread_safe, TODO: open option for consumer to choose to go lockless;
blk_num_t m_last_append_offset{0}; // last appended offset in blocks;
blk_num_t m_freeable_nblks{0};
std::atomic< blk_num_t > m_last_append_offset{0}; // last appended offset in blocks in memory
std::atomic< blk_num_t > m_freeable_nblks{0}; // count of blks fragmentedly freed (both on-disk and in-memory)
std::atomic< blk_num_t > m_commit_offset{0}; // offset in on-disk version
std::atomic< bool > m_is_dirty{false};
AppendBlkAllocMetrics m_metrics;
superblk< append_blk_sb_t > m_sb; // only cp will be writing to this disk
std::array< append_blk_dirty_buf_t, MAX_CP_COUNT > m_dirty_sb; // keep track of dirty sb;
superblk< append_blk_sb_t > m_sb; // only cp will be writing to this disk
};

} // namespace homestore
4 changes: 2 additions & 2 deletions src/lib/blkalloc/bitmap_blk_allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ bool BitmapBlkAllocator::is_blk_alloced_on_disk(const BlkId& b, bool use_lock) c
}
}

BlkAllocStatus BitmapBlkAllocator::alloc_on_disk(BlkId const& bid) {
BlkAllocStatus BitmapBlkAllocator::reserve_on_disk(BlkId const& bid) {
if (!is_persistent()) {
// for non-persistent bitmap nothing is needed to do. So always return success
return BlkAllocStatus::SUCCESS;
Expand Down Expand Up @@ -170,7 +170,7 @@ void BitmapBlkAllocator::release_underlying_buffer() {
auto it = old_alloc_list_ptr->begin(true /* latest */);
const BlkId* bid{nullptr};
while ((bid = old_alloc_list_ptr->next(it)) != nullptr) {
alloc_on_disk(*bid);
reserve_on_disk(*bid);
}
old_alloc_list_ptr->clear();
delete (old_alloc_list_ptr);
Expand Down
6 changes: 4 additions & 2 deletions src/lib/blkalloc/bitmap_blk_allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ class BitmapBlkAllocator : public BlkAllocator {
virtual ~BitmapBlkAllocator() = default;

virtual void load() = 0;
BlkAllocStatus alloc_on_disk(BlkId const& in_bid) override;
void free_on_disk(BlkId const& b) override;
BlkAllocStatus reserve_on_disk(BlkId const& in_bid) override;
bool is_blk_alloced_on_disk(BlkId const& b, bool use_lock = false) const override;
void cp_flush(CP* cp) override;

Expand All @@ -100,6 +99,9 @@ class BitmapBlkAllocator : public BlkAllocator {
void decr_alloced_blk_count(blk_count_t nblks) { m_alloced_blk_count.fetch_sub(nblks, std::memory_order_relaxed); }
int64_t get_alloced_blk_count() const { return m_alloced_blk_count.load(std::memory_order_acquire); }

protected:
void free_on_disk(BlkId const& b);

private:
void do_init();
sisl::ThreadVector< MultiBlkId >* get_alloc_blk_list();
Expand Down
Loading

0 comments on commit 55f5a3d

Please sign in to comment.