| // 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. |
| |
| #include <lib/fzl/vmo-pool.h> |
| #include <lib/zx/vmar.h> |
| #include <string.h> |
| |
| #include <utility> |
| |
| namespace fzl { |
| |
| VmoPool::Buffer::Buffer(VmoPool::Buffer&& other) : VmoPool::Buffer(other.pool_, other.index_) { |
| other.pool_ = nullptr; |
| } |
| |
| VmoPool::Buffer& VmoPool::Buffer::operator=(VmoPool::Buffer&& other) { |
| if (valid()) { |
| Release(); |
| } |
| pool_ = other.pool_; |
| index_ = other.index_; |
| other.pool_ = nullptr; |
| return *this; |
| } |
| |
| size_t VmoPool::Buffer::size() const { |
| ZX_ASSERT(valid()); |
| return pool_->buffers_[index_].buffer_size; |
| } |
| |
| void* VmoPool::Buffer::virtual_address() const { |
| ZX_ASSERT(valid()); |
| return pool_->buffers_[index_].virtual_address(); |
| } |
| |
| void* VmoPool::ListableBuffer::virtual_address() const { |
| ZX_ASSERT_MSG(is_mapped, "Querying virtual address of unmapped Buffer."); |
| return mapped_buffer.start(); |
| } |
| |
| zx_paddr_t VmoPool::Buffer::physical_address() const { |
| ZX_ASSERT(valid()); |
| return pool_->buffers_[index_].physical_address(); |
| } |
| |
| zx_handle_t VmoPool::Buffer::vmo_handle() const { |
| ZX_ASSERT(valid()); |
| return pool_->buffers_[index_].vmo.get(); |
| } |
| |
| zx_paddr_t VmoPool::ListableBuffer::physical_address() const { |
| ZX_ASSERT_MSG(is_pinned, "Querying physical address of unpinned Buffer."); |
| return pinned_buffer.region(0).phys_addr; |
| } |
| |
| // Release the buffer from its write lock. Returns the index |
| // of the buffer for use when signaling. |
| // This will also be called on destruction. |
| uint32_t VmoPool::Buffer::ReleaseWriteLockAndGetIndex() { |
| ZX_ASSERT(valid()); |
| pool_ = nullptr; |
| return index_; |
| } |
| |
| // Releases buffer back to free pool. This can be done regardless of |
| // write lock. |
| zx_status_t VmoPool::Buffer::Release() { |
| ZX_ASSERT(valid()); |
| zx_status_t status = pool_->ReleaseBuffer(index_); |
| pool_ = nullptr; |
| return status; |
| } |
| |
| VmoPool::Buffer::~Buffer() { |
| if (valid()) { |
| Release(); |
| } |
| } |
| |
| VmoPool::~VmoPool() { |
| // Clear out the free_buffers_, since the intrusive container |
| // will throw an assert if it contains unmanaged pointers on |
| // destruction. |
| free_buffers_.clear_unsafe(); |
| for (auto& buffer : buffers_) { |
| buffer.vmo.reset(); |
| } |
| } |
| |
| zx_status_t VmoPool::ListableBuffer::PinVmo(const zx::bti& bti, |
| VmoPool::RequireContig require_contiguous, |
| VmoPool::RequireLowMem require_low_memory) { |
| zx_status_t status; |
| status = pinned_buffer.Pin(vmo, bti, ZX_BTI_CONTIGUOUS | ZX_VM_PERM_READ | ZX_VM_PERM_WRITE); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (static_cast<bool>(require_contiguous) && pinned_buffer.region_count() != 1) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| if (static_cast<bool>(require_low_memory) && pinned_buffer.region(0).phys_addr > UINT32_MAX) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| is_pinned = true; |
| return ZX_OK; |
| } |
| |
| zx_status_t VmoPool::ListableBuffer::MapVmo() { |
| zx_status_t status = mapped_buffer.Map(vmo, 0, 0, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE); |
| if (status == ZX_OK) { |
| is_mapped = true; |
| } |
| return status; |
| } |
| |
| zx_status_t VmoPool::Init(const zx::vmo* vmos, size_t num_vmos) { |
| fbl::AllocChecker ac; |
| fbl::Array<ListableBuffer> buffers(new (&ac) ListableBuffer[num_vmos], num_vmos); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| buffers_ = std::move(buffers); |
| free_buffers_.clear_unsafe(); |
| |
| for (size_t i = 0; i < num_vmos; ++i) { |
| free_buffers_.push_front(&buffers_[i]); |
| zx_status_t status = vmos[i].get_size(&buffers_[i].buffer_size); |
| if (status == ZX_OK) { |
| status = vmos[i].duplicate(ZX_RIGHT_SAME_RIGHTS, &buffers_[i].vmo); |
| } |
| if (status != ZX_OK) { |
| free_buffers_.clear_unsafe(); |
| buffers_.reset(); |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VmoPool::PinVmos(const zx::bti& bti, VmoPool::RequireContig req_contiguous, |
| VmoPool::RequireLowMem req_low_memory) { |
| for (auto& buffer : buffers_) { |
| zx_status_t status = buffer.PinVmo(bti, req_contiguous, req_low_memory); |
| if (status != ZX_OK) { |
| free_buffers_.clear_unsafe(); |
| buffers_.reset(); |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VmoPool::MapVmos() { |
| for (auto& buffer : buffers_) { |
| zx_status_t status = buffer.MapVmo(); |
| if (status != ZX_OK) { |
| free_buffers_.clear_unsafe(); |
| buffers_.reset(); |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| void VmoPool::Reset() { |
| for (auto& buffer : buffers_) { |
| if (!buffer.InContainer()) { |
| free_buffers_.push_front(&buffer); |
| } |
| } |
| } |
| |
| std::optional<VmoPool::Buffer> VmoPool::LockBufferForWrite() { |
| if (free_buffers_.is_empty()) { // No available buffers! |
| return std::nullopt; |
| } |
| ListableBuffer* buf = free_buffers_.pop_front(); |
| ZX_DEBUG_ASSERT(buf >= &buffers_[0]); |
| uint32_t buffer_offset = static_cast<uint32_t>(buf - &buffers_[0]); |
| ZX_DEBUG_ASSERT(buffer_offset < buffers_.size()); |
| return Buffer(this, buffer_offset); |
| } |
| |
| zx_status_t VmoPool::ReleaseBuffer(uint32_t buffer_index) { |
| if (buffer_index >= buffers_.size()) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (buffers_[buffer_index].InContainer()) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| free_buffers_.push_front(&buffers_[buffer_index]); |
| return ZX_OK; |
| } |
| } // namespace fzl |