blob: e5aebae991121a6ad571bba1561184f332cd8a7e [file] [log] [blame]
// 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.
#pragma once
#ifndef __Fuchsia__
#error Fuchsia-only Header
#endif
#include <utility>
#include <blobfs/vmo-buffer.h>
#include <fbl/mutex.h>
#include <lib/fzl/owned-vmo-mapper.h>
#include "unbuffered-operations-builder.h"
namespace blobfs {
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(); }
// 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* MutableData(size_t index) {
return buffer_.MutableData(index);
}
// 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.
fbl::Mutex lock_;
// The units of all the following are "Blobfs blocks".
size_t reserved_start_ __TA_GUARDED(lock_) = 0;
size_t reserved_length_ __TA_GUARDED(lock_) = 0;
// TODO(ZX-4033): Replace fbl::Vector with a friendlier std container when possible.
fbl::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);
~RingBufferReservation();
// Unreserves the reservation. This will cause |Reserved()| to return
// false for the duration of the |RingBufferReservation|'s lifetime.
void Reset();
// 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.
//
// Preconditions:
// - The reservation must be large enough to copy |requests|:
// - offset + BlockCount(in_requests) <= length()
// - |Reserved()| must be true.
zx_status_t CopyRequests(const fbl::Vector<UnbufferedOperation>& requests, size_t offset,
fbl::Vector<BufferedOperation>* out);
// The first reservation block, relative to the start of |RingBuffer|.
size_t start() const { return start_; }
// The total length of this reservation, in blocks.
size_t length() const { return length_; }
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* MutableData(size_t index);
private:
// Returns true if the reservation holds blocks in a |RingBuffer|.
bool Reserved() const {
return buffer_ != nullptr;
}
internal::RingBufferState* buffer_ = nullptr;
size_t start_ = 0;
size_t length_ = 0;
};
// 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 kBlobfsBlockSize.
static zx_status_t Create(SpaceManager* space_manager, const size_t blocks,
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();
}
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(fbl::Vector<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 fbl::Vector<BufferedOperation>& Operations() const { return requests_; }
RingBufferReservation* Reservation() { return &reservation_; }
private:
fbl::Vector<BufferedOperation> requests_;
RingBufferReservation reservation_;
};
} // namespace blobfs