blob: 8b9f461aca423f06a0cd56853c56d73e55fa5210 [file] [log] [blame]
// Copyright 2018 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 LIB_FZL_VMO_POOL_H_
#define LIB_FZL_VMO_POOL_H_
#include <lib/fzl/pinned-vmo.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/zx/vmo.h>
#include <zircon/types.h>
#include <limits>
#include <optional>
#include <fbl/array.h>
#include <fbl/intrusive_single_list.h>
#include <fbl/vector.h>
#include "lib/stdcompat/span.h"
namespace fzl {
// This class is not thread safe.
// VmoPool is intended to be used by content producers who have the following
// usage pattern regarding a collection of VMOs:
// Setup: A producer and at least one consumer establish a connection and share a
// homogenous set of VMOs. A common way to do this is through BufferCollections
// and the Sysmem library.
// During normal operation:
// 1) The producer obtains a write lock on a free vmo.
// 2) The producer writes into the VMO. Multiple write-locked VMOs may be held
// simultaneously.
// 3) When the producer is finished writing to the VMO, it signals the consumer
// that the VMO is ready to be consumed. The VMO is now read-locked.
// 4) When the VMO is finished being consumed, the consumer signals the producer
// that it is done with the vmo. The producer then marks that VMO as free.
//
// VmoPool maintains the bookkeeping for the above interaction, as follows:
// 1) The producer calls LockBufferForWrite(), which returns a Buffer object.
// 2) The valid Buffer object represents a write lock.
// 3) When the producer is done writing, it calls ReleaseWriteLockAndGetIndex
// which returns the index of the buffer. This index can be sent to the
// consumer to signal that a buffer is ready to be consumed. Calling
// ReleaseWriteLockAndGetIndex invalidates the Buffer object and constitutes
// a read lock on the VMO.
// 4) When the VMO is finished being consumed, the consumer send the index back
// to the producer, who then calls ReleaseBuffer on that index, marking the
// VMO as free.
//
// VmoPool additionally handles the mapping and pinning of VMOs through the
// MapVmos and PinVmos functions. After the buffers have been mapped/pinned,
// the virtual/physical address can be accessed through the Buffer instances.
class VmoPool {
public:
// Options for pinning VMOs:
// Require contiguous memory:
enum class RequireContig : bool { No = false, Yes };
// Require that the physical memory address be expressable as a 32bit
// unsigned integer:
enum class RequireLowMem : bool { No = false, Yes };
// Initializes the VmoPool with a set of vmos.
zx_status_t Init(cpp20::span<zx::unowned_vmo> vmos);
// Pin all the vmos to physical memory. This must be called prior to
// requesting a physical address from any Buffer instance.
zx_status_t PinVmos(const zx::bti& bti, RequireContig req_contiguous,
RequireLowMem req_low_memory);
// Map the vmos to virtual memory. This must be called prior to
// requesting a virtual address from any Buffer instance.
zx_status_t MapVmos();
// Resets the buffer read and write locks.
void Reset();
// Finds the next available buffer, locks that buffer for writing, and
// returns a Buffer instance to allow access to that buffer.
// If no buffers are available, returns an empty std::optional.
class Buffer;
std::optional<Buffer> LockBufferForWrite();
// Unlocks the buffer with the specified index and sets it as ready to be
// reused. Calling ReleaseBuffer with the index from
// buffer.ReleaseWriteLockAndGetIndex() is equivalent to calling
// buffer.Release().
// Returns ZX_OK if successful, or ZX_ERR_NOT_FOUND if no locked buffer
// was found with the given index. If the index is out of bounds,
// ZX_ERROR_INVALID_ARGS will be returned.
zx_status_t ReleaseBuffer(uint32_t buffer_index);
// Returns the total number of buffers in this pool.
size_t total_buffers() const { return buffers_.size(); }
// Returns the number of free buffers in this pool.
size_t free_buffers() const { return free_buffers_.size(); }
// Returns the size (in bytes) of the buffer at a given index in this pool.
size_t buffer_size(uint32_t buffer_index = 0) const {
ZX_DEBUG_ASSERT(buffer_index < buffers_.size());
return buffers_[buffer_index].buffer_size;
}
~VmoPool();
// The Buffer class offers an object-oriented way to accessing the buffers
// within VmoPool. Retaining ownership of a valid Buffer corresponds to
// a write lock. To release the write lock, you can:
// - Call ReleaseWriteLockAndGetIndex(). This releases the write
// lock, and returns the index of the buffer, for use with fidl interfaces with
// shared arrays of VMOs, such as ImagePipe, or camera::Stream::FrameAvailable.
// The buffer is now read locked.
// - Call Release(). This releases the buffer completely.
// - Allow the Buffer object to go out of scope. This is equivalent to calling
// Release().
class Buffer {
public:
// Default constructor creates an invalid Buffer:
Buffer() : Buffer(nullptr, 0) {}
~Buffer();
// Only allow move constructors.
Buffer(Buffer&& other) noexcept;
Buffer& operator=(Buffer&& other) noexcept;
// Release the buffer from its write lock, which puts the Buffer instance
// in the invalid state. If the Buffer was in a valid state before the call,
// this will return the index of the Buffer. Calling ReleaseBuffer with
// this index is now the only way to release the buffer.
// Asserts that the Buffer is valid.
uint32_t ReleaseWriteLockAndGetIndex();
// Releases buffer back to free pool.
zx_status_t Release();
// Returns the size of the buffer. Asserts that the Buffer instance is
// valid.
size_t size() const;
// Return the virtual address to the start of the buffer.
// Asserts that the buffer is mapped, and that the Buffer instance
// is valid.
void* virtual_address() const;
// Return the physical address of the start of the buffer.a
//
// Asserts that the buffer is pinned, and that the Buffer instance is
// valid.
zx_paddr_t physical_address() const;
// Return the vmo handle.
// Asserts that the Buffer instance is valid.
zx_handle_t vmo_handle() const;
bool valid() const { return pool_ != nullptr; }
private:
Buffer(VmoPool* pool, uint32_t index) : pool_(pool), index_(index) {}
// Remove the copy and assignment constructor.
Buffer(const Buffer&) = delete;
void operator=(const Buffer&) = delete;
// The only function which should construct a Buffer from an index and
// a pool is VmoPool::LockBufferForWrite.
friend std::optional<Buffer> VmoPool::LockBufferForWrite();
// The validity of the pool pointer indicates whether the buffer
// instance itself is valid.
VmoPool* pool_ = nullptr;
uint32_t index_ = 0;
};
// Buffer is allowed to access the internals of VmoPool to access
// ListableBuffers.
friend class Buffer;
private:
struct ListableBuffer
: public fbl::SinglyLinkedListable<ListableBuffer*, fbl::NodeOptions::AllowClearUnsafe> {
// Get the start of the virtual memory address.
void* virtual_address() const;
// Get the start of the physical address.
zx_paddr_t physical_address() const;
zx_status_t PinVmo(const zx::bti& bti, RequireContig require_contiguous,
RequireLowMem require_low_memory);
zx_status_t MapVmo();
VmoMapper mapped_buffer;
PinnedVmo pinned_buffer;
zx::vmo vmo;
uint64_t buffer_size = 0;
bool is_mapped = false;
bool is_pinned = false;
};
// VMO / mapping / pinning backing each buffer.
fbl::Array<ListableBuffer> buffers_;
// The list of free buffers.
fbl::SizedSinglyLinkedList<ListableBuffer*> free_buffers_;
};
} // namespace fzl
#endif // LIB_FZL_VMO_POOL_H_