1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <lib/fzl/vmo-pool.h>
6 #include <lib/zx/vmar.h>
7 #include <string.h>
8 
9 #include <utility>
10 
11 namespace fzl {
12 
~VmoPool()13 VmoPool::~VmoPool() {
14     // Clear out the free_buffers_, since the intrusive container
15     // will throw an assert if it contains unmanaged pointers on
16     // destruction.
17     free_buffers_.clear_unsafe();
18 }
19 
Init(const fbl::Vector<zx::vmo> & vmos)20 zx_status_t VmoPool::Init(const fbl::Vector<zx::vmo>& vmos) {
21     return Init(vmos.begin(), vmos.size());
22 }
23 
Init(const zx::vmo * vmos,size_t num_vmos)24 zx_status_t VmoPool::Init(const zx::vmo* vmos, size_t num_vmos) {
25     fbl::AllocChecker ac;
26     fbl::Array<ListableBuffer> buffers(new (&ac) ListableBuffer[num_vmos], num_vmos);
27     if (!ac.check()) {
28         return ZX_ERR_NO_MEMORY;
29     }
30     buffers_ = std::move(buffers);
31     free_buffers_.clear_unsafe();
32 
33     zx_status_t status;
34     for (size_t i = 0; i < num_vmos; ++i) {
35         free_buffers_.push_front(&buffers_[i]);
36         status = buffers_[i].buffer.Map(vmos[i], 0, 0, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE);
37         if (status != ZX_OK) {
38             free_buffers_.clear_unsafe();
39             buffers_.reset();
40             return status;
41         }
42     }
43     current_buffer_ = kInvalidCurBuffer;
44     return ZX_OK;
45 }
46 
Reset()47 void VmoPool::Reset() {
48     current_buffer_ = kInvalidCurBuffer;
49     for (size_t i = 0; i < buffers_.size(); ++i) {
50         if (!buffers_[i].InContainer()) {
51             free_buffers_.push_front(&buffers_[i]);
52         }
53     }
54 }
55 
GetNewBuffer(uint32_t * buffer_index)56 zx_status_t VmoPool::GetNewBuffer(uint32_t* buffer_index) {
57     if (HasBufferInProgress()) {
58         return ZX_ERR_BAD_STATE;
59     }
60     if (free_buffers_.is_empty()) { // No available buffers!
61         return ZX_ERR_NOT_FOUND;
62     }
63     ListableBuffer* buf = free_buffers_.pop_front();
64     ZX_DEBUG_ASSERT(buf >= &buffers_[0]);
65     uint32_t buffer_offset = static_cast<uint32_t>(buf - &buffers_[0]);
66     ZX_DEBUG_ASSERT(buffer_offset < buffers_.size());
67     current_buffer_ = buffer_offset;
68     if (buffer_index != nullptr) {
69         *buffer_index = current_buffer_;
70     }
71     return ZX_OK;
72 }
73 
BufferCompleted(uint32_t * buffer_index)74 zx_status_t VmoPool::BufferCompleted(uint32_t* buffer_index) {
75     if (!HasBufferInProgress()) {
76         return ZX_ERR_BAD_STATE;
77     }
78     if (buffer_index != nullptr) {
79         *buffer_index = current_buffer_;
80     }
81     current_buffer_ = kInvalidCurBuffer;
82     return ZX_OK;
83 }
84 
BufferRelease(uint32_t buffer_index)85 zx_status_t VmoPool::BufferRelease(uint32_t buffer_index) {
86     if (buffer_index >= buffers_.size()) {
87         return ZX_ERR_INVALID_ARGS;
88     }
89     if (buffers_[buffer_index].InContainer()) {
90         return ZX_ERR_NOT_FOUND;
91     }
92     // If we are cancelling the in-progress buffer:
93     if (current_buffer_ == buffer_index) {
94         current_buffer_ = kInvalidCurBuffer;
95     }
96 
97     free_buffers_.push_front(&buffers_[buffer_index]);
98     return ZX_OK;
99 }
100 
CurrentBufferSize() const101 uint64_t VmoPool::CurrentBufferSize() const {
102     if (HasBufferInProgress()) {
103         return buffers_[current_buffer_].buffer.size();
104     }
105     return 0;
106 }
CurrentBufferAddress() const107 void* VmoPool::CurrentBufferAddress() const {
108     if (HasBufferInProgress()) {
109         return buffers_[current_buffer_].buffer.start();
110     }
111     return nullptr;
112 }
113 } // namespace fzl
114