blob: 2a90b6fff9296b9289b8d52de6660fc0f4043f07 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_STORAGE_F2FS_WRITEBACK_H_
#define SRC_STORAGE_F2FS_WRITEBACK_H_
#ifdef __Fuchsia__
#include "src/lib/storage/vfs/cpp/journal/background_executor.h"
#else // __Fuchsia__
#include <lib/fpromise/promise.h>
#include <lib/fpromise/single_threaded_executor.h>
#include <storage/buffer/array_buffer.h>
#endif // __Fuchsia__
namespace f2fs {
class F2fs;
class PageOperations;
constexpr auto kWriteTimeOut = std::chrono::seconds(60);
class SegmentWriteBuffer {
public:
#ifdef __Fuchsia__
SegmentWriteBuffer(storage::VmoidRegistry *vmoid_registry, size_t blocks, uint32_t block_size,
PageType type);
#else // __Fuchsia__
SegmentWriteBuffer(Bcache *bc, size_t blocks, uint32_t block_size, PageType type);
#endif // __Fuchsia__
SegmentWriteBuffer() = delete;
SegmentWriteBuffer(const SegmentWriteBuffer &) = delete;
SegmentWriteBuffer &operator=(const SegmentWriteBuffer &) = delete;
SegmentWriteBuffer(const SegmentWriteBuffer &&) = delete;
SegmentWriteBuffer &operator=(const SegmentWriteBuffer &&) = delete;
~SegmentWriteBuffer();
PageOperations TakeOperations() __TA_EXCLUDES(mutex_);
// It tries to reserve |buffer_| space for |page| subject to writeback. If successful,
// it unlocks |page| and moves its ownership to |pages_| after copying it to the space,
// and then PageOperaions will transfers the part of |buffer_| related to |pages_| to disk
// when a certain condition is met. To allow users to access |page| during writeback,
// |page| gets unlocked in |pages_| with kWriteback flag set. So, any writers who want to access
// |page| wait for its writeback by calling Page::WaitOnWriteback(), but readers are free to
// access to it. If successful, it returns the number of Pages in |pages_|, and a caller
// must not access |page|.
zx::status<size_t> ReserveOperation(storage::Operation &operation, LockedPage &page)
__TA_EXCLUDES(mutex_);
void ReleaseBuffers(const PageOperations &operation) __TA_EXCLUDES(mutex_);
private:
static constexpr std::string_view kVmoBufferLabels[static_cast<uint32_t>(PageType::kNrPageType)] =
{"DataSegment", "NodeSegment", "MetaArea"};
fs::BufferedOperationsBuilder builder_ __TA_GUARDED(mutex_);
std::vector<fbl::RefPtr<Page>> pages_ __TA_GUARDED(mutex_);
#ifdef __Fuchsia__
storage::VmoBuffer buffer_;
#else
storage::ArrayBuffer buffer_;
#endif // __Fuchsia__
size_t start_index_ __TA_GUARDED(mutex_) = 0;
size_t count_ __TA_GUARDED(mutex_) = 0;
std::condition_variable_any cvar_;
std::mutex mutex_;
};
using PageOperationCallback = fit::function<void(const PageOperations &)>;
// A utility class, holding a collection of write requests associated with a portion of a single
// SegmentWriteBuffer, ready to be transmitted to persistent storage.
class PageOperations {
public:
PageOperations() = delete;
PageOperations(std::vector<storage::BufferedOperation> operations,
std::vector<fbl::RefPtr<Page>> pages, PageOperationCallback release_buffers)
: operations_(std::move(operations)),
pages_(std::move(pages)),
release_buffers_(std::move(release_buffers)) {}
PageOperations(const PageOperations &operations) = delete;
PageOperations &operator=(const PageOperations &) = delete;
PageOperations(const PageOperations &&op) = delete;
PageOperations &operator=(const PageOperations &&) = delete;
PageOperations(PageOperations &&op) = default;
PageOperations &operator=(PageOperations &&) = default;
~PageOperations() {
ZX_DEBUG_ASSERT(pages_.empty());
ZX_DEBUG_ASSERT(operations_.empty());
}
std::vector<storage::BufferedOperation> TakeOperations() { return std::move(operations_); }
void Completion(PageCallback put_page) {
release_buffers_(*this);
for (auto &page : pages_) {
put_page(std::move(page));
}
pages_.clear();
pages_.shrink_to_fit();
}
bool Empty() const { return pages_.empty(); }
size_t GetLength() const { return pages_.size(); }
private:
std::vector<storage::BufferedOperation> operations_;
std::vector<fbl::RefPtr<Page>> pages_;
PageOperationCallback release_buffers_;
};
class Writer {
public:
Writer(Bcache *bc);
Writer() = delete;
Writer(const Writer &) = delete;
Writer &operator=(const Writer &) = delete;
Writer(const Writer &&) = delete;
Writer &operator=(const Writer &&) = delete;
~Writer();
void ScheduleTask(fpromise::pending_task task);
// It schedules SubmitPages().
// If |completion| is set, it notifies the caller of the operation completion.
void ScheduleSubmitPages(sync_completion_t *completion = nullptr,
PageType type = PageType::kNrPageType);
// It merges Pages to be written.
void EnqueuePage(storage::Operation &operation, LockedPage &page, PageType type);
private:
// It takes writeback operations from |builder_| and passes them to RunReqeusts().
// When the operations are complete, it groups Pages by the vnode id and passes each group to the
// regarding FileCache for releasing mappings and committed pages.
fpromise::promise<> SubmitPages(sync_completion_t *completion, PageType type);
std::array<std::unique_ptr<SegmentWriteBuffer>, static_cast<uint32_t>(PageType::kNrPageType)>
write_buffer_;
fs::TransactionHandler *transaction_handler_ = nullptr;
#ifdef __Fuchsia__
fs::BackgroundExecutor executor_;
#endif // __Fuchsia__
};
} // namespace f2fs
#endif // SRC_STORAGE_F2FS_WRITEBACK_H_