| // 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/operation.h> |
| #include <blobfs/vmo-buffer.h> |
| #include <fbl/mutex.h> |
| #include <lib/fzl/owned-vmo-mapper.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(VmoidRegistry* vmoid_registry, 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 |