blob: 67c86f653ec7806d92712ff80bc41b53688e38a7 [file]
// Copyright 2019 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_LIB_BUFFER_RING_BUFFER_H_
#define SRC_STORAGE_LIB_BUFFER_RING_BUFFER_H_
#ifndef __Fuchsia__
#error Fuchsia-only Header
#endif
#include <lib/stdcompat/span.h>
#include <lib/zx/result.h>
#include <mutex>
#include <vector>
#include "src/storage/lib/buffer/block_buffer_view.h"
#include "src/storage/lib/buffer/vmo_buffer.h"
#include "src/storage/lib/operation/unbuffered_operation.h"
namespace storage {
class RingBufferReservation;
namespace internal {
// Internal state backing RingBuffer. Refer to that class for the public API.
//
// This class is not movable or copyable.
// This class is thread-safe.
class RingBufferState {
public:
explicit RingBufferState(VmoBuffer buffer)
: buffer_(std::move(buffer)), reserved_start_(0), reserved_length_(0) {}
RingBufferState(const RingBufferState&) = delete;
RingBufferState& operator=(const RingBufferState&) = delete;
RingBufferState(RingBufferState&& other) = delete;
RingBufferState& operator=(RingBufferState&& other) = delete;
~RingBufferState() = default;
// Reserves space for |blocks| contiguous blocks in the circular buffer.
//
// To perform optimally, these reservations should be destroyed in the same order
// they are reserved.
//
// |blocks| must be greater than zero.
// Returns ZX_ERR_NO_SPACE if there is not enough room.
zx_status_t Reserve(uint64_t blocks, RingBufferReservation* out);
// Returns the total amount of pending blocks which may be buffered.
size_t capacity() const { return buffer_.capacity(); }
uint32_t BlockSize() const { return buffer_.BlockSize(); }
// Frees |reservation| from the buffer.
//
// Only callable by |RingBufferReservation|, since this frees the previously used
// reservation.
void Free(const RingBufferReservation& reservation);
// Returns data starting at block |index| in the buffer.
//
// Only callable by |RingBufferReservation|, since this uses the previously created
// reservation.
void* Data(size_t index) { return buffer_.Data(index); }
// Returns a pointer to the underlying buffer. Should only be accessible to the
// |RingBufferReservation|, which should take caution to only reference reserved portions
// of the buffer itself.
VmoBuffer* buffer() { return &buffer_; }
const VmoBuffer* buffer() const { return &buffer_; }
// Returns the vmoid of the underlying RingBuffer.
//
// Only callable by |RingBufferReservation|, since this uses the previously created
// reservation.
vmoid_t vmoid() const { return buffer_.vmoid(); }
private:
struct Range {
size_t start;
size_t length;
};
// Returns true if there is space available for |blocks| blocks within the buffer.
bool IsSpaceAvailableLocked(size_t blocks) const __TA_REQUIRES(lock_);
// Actually frees |blocks| in the buffer at |start| in the buffer.
void CompleteFreeLocked(size_t start, size_t blocks) __TA_REQUIRES(lock_);
VmoBuffer buffer_;
// Although this lock guards some fields of |RingBuffer| explicitly, access to the
// buffer data ("who can access the region at [start, start + length)?") is implicit
// via the RingBufferReservation objects.
std::mutex lock_;
// The units of all the following are "filesystem blocks".
size_t reserved_start_ __TA_GUARDED(lock_) = 0;
size_t reserved_length_ __TA_GUARDED(lock_) = 0;
std::vector<Range> pending_free_ __TA_GUARDED(lock_);
};
} // namespace internal
// A reservation of space within |RingBuffer|. Allows clients to safely access a portion
// of the circular buffer for either reading or writing.
//
// Releases the space when going out of scope (or reset).
//
// This class is movable, but not copyable.
// This class is thread-compatible.
class RingBufferReservation {
public:
RingBufferReservation() = default;
// Creates a RingBufferReservation within a buffer, at |start| blocks within
// the buffer, of |length| bytes long. [start, start + length) may wrap around the
// RingBuffer.
RingBufferReservation(internal::RingBufferState* buffer, size_t start, size_t length);
RingBufferReservation(const RingBufferReservation&) = delete;
RingBufferReservation& operator=(const RingBufferReservation&) = delete;
RingBufferReservation(RingBufferReservation&& other);
RingBufferReservation& operator=(RingBufferReservation&& other);
virtual ~RingBufferReservation();
// Copies from |in_requests|, at the provided |offset| into this reservation.
//
// Updates the in-memory offsets of |requests| so they point to the correct offsets in the
// in-memory buffer instead of their original VMOs, outputting these updated requests.
//
// Returns an error if a VMO from |requests| cannot be accessed to write into
// the buffer, but otherwise returns the number of blocks copied.
//
// Preconditions:
// - The reservation must be large enough to copy |requests|:
// - offset + BlockCount(in_requests) <= length()
// - |Reserved()| must be true.
zx::result<uint64_t> CopyRequests(cpp20::span<const storage::UnbufferedOperation> in_operations,
size_t offset,
std::vector<storage::BufferedOperation>* out_operations);
BlockBufferView buffer_view() { return view_; }
// The first reservation block, relative to the start of |RingBuffer|.
size_t start() const { return view_.start(); }
// The total length of this reservation, in blocks.
size_t length() const { return view_.length(); }
// The capacity of the underlying buffer.
size_t capacity() const { return buffer_->capacity(); }
vmoid_t vmoid() const;
// Returns one block of data starting at block |index| within this reservation.
// Since this data has been reserved, |RingBuffer| will not attempt to access it concurrently.
//
// Preconditions:
// - |Reserved()| must be true.
// - |index| < |length()|
void* Data(size_t index);
const void* Data(size_t index) const;
protected:
// Returns true if the reservation holds blocks in a |RingBuffer|.
bool Reserved() const { return buffer_ != nullptr; }
// Unreserves the reservation. This will cause |Reserved()| to return
// false for the duration of the |RingBufferReservation|'s lifetime.
void Reset();
private:
internal::RingBufferState* buffer_ = nullptr;
BlockBufferView view_;
};
// In-memory circular buffer.
//
// This class is not movable or copyable.
// This class is thread-safe.
class RingBuffer {
public:
explicit RingBuffer(VmoBuffer buffer) : state_(std::move(buffer)) {}
RingBuffer(const RingBuffer&) = delete;
RingBuffer& operator=(const RingBuffer&) = delete;
RingBuffer(RingBuffer&& other) = delete;
RingBuffer& operator=(RingBuffer&& other) = delete;
~RingBuffer() = default;
// Initializes the buffer with |blocks| blocks of size |block_size|.
static zx_status_t Create(VmoidRegistry* vmoid_registry, size_t blocks, uint32_t block_size,
const char* label, std::unique_ptr<RingBuffer>* out);
// Reserves space for |blocks| contiguous blocks in the circular buffer.
//
// To perform optimally, these reservations should be destroyed in the same order
// they are reserved.
//
// |blocks| must be greater than zero.
// Returns ZX_ERR_NO_SPACE if there is not enough room.
zx_status_t Reserve(uint64_t blocks, RingBufferReservation* out) {
return state_.Reserve(blocks, out);
}
// Returns the total amount of pending blocks which may be buffered.
size_t capacity() const { return state_.capacity(); }
uint32_t BlockSize() const { return state_.buffer()->BlockSize(); }
private:
internal::RingBufferState state_;
};
// A utility class, holding a collection of write requests associated with a portion of a single
// RingBuffer, ready to be transmitted to persistent storage.
//
// This class is movable, but not copyable.
// This class is thread-safe.
class RingBufferRequests {
public:
RingBufferRequests() = default;
RingBufferRequests(std::vector<storage::BufferedOperation> requests,
RingBufferReservation reservation);
RingBufferRequests(const RingBufferRequests&) = delete;
RingBufferRequests& operator=(const RingBufferRequests&) = delete;
RingBufferRequests(RingBufferRequests&& other) = default;
RingBufferRequests& operator=(RingBufferRequests&& other) = default;
~RingBufferRequests() = default;
const std::vector<storage::BufferedOperation>& Operations() const { return requests_; }
RingBufferReservation* Reservation() { return &reservation_; }
private:
std::vector<storage::BufferedOperation> requests_;
RingBufferReservation reservation_;
};
} // namespace storage
#endif // SRC_STORAGE_LIB_BUFFER_RING_BUFFER_H_