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/vmo.h>
7 #include <unittest/unittest.h>
8 #include <zircon/rights.h>
9
10 #include <utility>
11
12 #include "vmo-probe.h"
13
14 // Things to test:
15 // 1) Init with vmos, init with non-initialized vmos
16 // 2) memset at address, for size()
17 // 3) Get a bunch of buffers, make sure it runs out
18 // 4) Call GetNewBuffer twice, assert fail
19 // 5) pass bad buffer index to BufferRelease
20 // 6) try to release twice
21 // 7) Check GetNewBuffer and BufferCompleted return the same
22
23 namespace {
24
25 static constexpr size_t kVmoTestSize = 512 << 10; // 512KB
26 static constexpr size_t kNumVmos = 20; // 512KB
27
28 // Create vmos for each handle in an array of vmo handles:
AssignVmos(size_t num_vmos,size_t vmo_size,zx::vmo * vmos)29 bool AssignVmos(size_t num_vmos, size_t vmo_size, zx::vmo* vmos) {
30 BEGIN_TEST;
31 zx_status_t status;
32 for (size_t i = 0; i < num_vmos; ++i) {
33 status = zx::vmo::create(vmo_size, 0, &vmos[i]);
34 ASSERT_EQ(status, ZX_OK);
35 }
36 END_TEST;
37 }
38
39 // A helper class to initialize the VmoPool, and to check the state.
40 // Since we cannot access the VmoPool's free buffer list, we check the
41 // state of the VmoPool by filling it up and emptying it out.
42 class VmoPoolTester {
43 public:
44 zx::vmo vmo_handles_[kNumVmos];
45 fzl::VmoPool pool_;
46
Init()47 bool Init() {
48 BEGIN_TEST;
49 ASSERT_TRUE(AssignVmos(kNumVmos, kVmoTestSize, vmo_handles_));
50 ASSERT_EQ(pool_.Init(vmo_handles_, kNumVmos), ZX_OK);
51 END_TEST;
52 }
53
FillBuffers(size_t num_buffers)54 bool FillBuffers(size_t num_buffers) {
55 BEGIN_TEST;
56 for (size_t i = 0; i < kNumVmos && i < num_buffers; ++i) {
57 ASSERT_EQ(pool_.GetNewBuffer(nullptr), ZX_OK);
58 ASSERT_EQ(pool_.BufferCompleted(nullptr), ZX_OK);
59 }
60 END_TEST;
61 }
62
63 // Fills the pool, to make sure all accounting
64 // is done correctly.
65 // filled_count is the number of buffers that are already reserved.
CheckFillingPool(size_t filled_count)66 bool CheckFillingPool(size_t filled_count) {
67 BEGIN_TEST;
68 // Test that the pool gives indexes from 0 to kNumVmos-1
69 // It is not required to give the indexes in any particular
70 // order.
71 bool gave_index[kNumVmos]; // initialized to false
72 for (size_t i = 0; i < kNumVmos; ++i) {
73 gave_index[i] = false;
74 }
75 for (size_t i = 0; i < kNumVmos - filled_count; ++i) {
76 uint32_t new_buffer_index, buffer_completed_index;
77 ASSERT_EQ(pool_.GetNewBuffer(&new_buffer_index), ZX_OK);
78 ASSERT_LT(new_buffer_index, kNumVmos);
79 ASSERT_EQ(gave_index[new_buffer_index], false);
80 gave_index[new_buffer_index] = true;
81 ASSERT_TRUE(CheckHasBuffer());
82 // Now mark as complete:
83 ASSERT_EQ(pool_.BufferCompleted(&buffer_completed_index), ZX_OK);
84 // Make sure the index passed on BufferComplete is still the same:
85 ASSERT_EQ(new_buffer_index, buffer_completed_index);
86 ASSERT_TRUE(CheckHasNoBuffer());
87 }
88 // Now check that requesting any further buffers fails:
89 ASSERT_EQ(pool_.GetNewBuffer(nullptr), ZX_ERR_NOT_FOUND);
90 END_TEST;
91 }
92
93 // Empties the pool, to make sure all accounting
94 // is done correctly.
95 // unfilled_count is the number of buffers that are already reserved.
CheckEmptyPool(size_t unfilled_count)96 bool CheckEmptyPool(size_t unfilled_count) {
97 BEGIN_TEST;
98 size_t release_errors = 0;
99 for (uint32_t i = 0; i < kNumVmos; ++i) {
100 if (pool_.BufferRelease(i) == ZX_ERR_NOT_FOUND) {
101 release_errors++;
102 ASSERT_LE(release_errors, unfilled_count);
103 }
104 }
105 // Make sure we had exactly filled_count buffers already released.
106 ASSERT_EQ(unfilled_count, release_errors);
107 // Now, make sure all buffers are now released.
108 for (uint32_t i = 0; i < kNumVmos; ++i) {
109 ASSERT_EQ(pool_.BufferRelease(i), ZX_ERR_NOT_FOUND);
110 }
111 END_TEST;
112 }
113
CheckHasBuffer()114 bool CheckHasBuffer() {
115 BEGIN_TEST;
116 ASSERT_TRUE(pool_.HasBufferInProgress());
117 void* addr = pool_.CurrentBufferAddress();
118 ASSERT_NONNULL(addr);
119 size_t mem_size = pool_.CurrentBufferSize();
120 ASSERT_EQ(mem_size, kVmoTestSize);
121 uint32_t rw_access = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
122 ASSERT_TRUE(vmo_probe::probe_verify_region(addr, mem_size, rw_access));
123 END_TEST;
124 }
125
CheckHasNoBuffer()126 bool CheckHasNoBuffer() {
127 BEGIN_TEST;
128 ASSERT_FALSE(pool_.HasBufferInProgress());
129 void* addr = pool_.CurrentBufferAddress();
130 ASSERT_NULL(addr);
131 size_t mem_size = pool_.CurrentBufferSize();
132 ASSERT_EQ(mem_size, 0);
133 END_TEST;
134 }
135
CheckAccounting(bool buffer_in_progress,size_t filled_count)136 bool CheckAccounting(bool buffer_in_progress, size_t filled_count) {
137 BEGIN_TEST;
138 if (buffer_in_progress) {
139 ASSERT_TRUE(CheckHasBuffer());
140 ASSERT_EQ(pool_.BufferCompleted(), ZX_OK);
141 filled_count++;
142 }
143 ASSERT_TRUE(CheckHasNoBuffer());
144 ASSERT_TRUE(CheckFillingPool(filled_count));
145 ASSERT_TRUE(CheckEmptyPool(0));
146 END_TEST;
147 }
148
149 // Shuffles the free list, psuedo-randomly.
150 // Assumes that the pool is empty.
151 // This shuffle function relies on the fact that if you have a prime
152 // number p and a number (n) that does not have that prime number
153 // as a factor, the set of (x*p)%n, where x := {0,n-1} will cover the
154 // range of {0,n-1} exactly.
ShufflePool()155 bool ShufflePool() {
156 BEGIN_TEST;
157 ASSERT_TRUE(FillBuffers(kNumVmos));
158 static constexpr uint32_t kHashingSeed = 7;
159 static_assert(kNumVmos % kHashingSeed != 0, "Bad Hashing seed");
160 uint32_t hashing_index = 0;
161 for (size_t i = 0; i < kNumVmos; ++i) {
162 hashing_index = (hashing_index + kHashingSeed) % kNumVmos;
163 ASSERT_EQ(pool_.BufferRelease(hashing_index), ZX_OK);
164 }
165 END_TEST;
166 }
167 };
168
169 // Initialize the pool with a vector.
170 // (All the other tests initialize with an array)
171 // First tries vector of invalid handles
172 // then assigns the handles, and tries again.
173 // This test also verifies that you can re-initialize if a previous call to
174 // Init fails.
vmo_pool_init_vector_test()175 bool vmo_pool_init_vector_test() {
176 BEGIN_TEST;
177 VmoPoolTester tester;
178 fbl::Vector<zx::vmo> vmo_vector;
179 fbl::AllocChecker ac;
180 // Move the tester's vmos into a vector:
181 for (size_t i = 0; i < kNumVmos; ++i) {
182 vmo_vector.push_back(std::move(tester.vmo_handles_[i]), &ac);
183 ASSERT_TRUE(ac.check());
184 }
185 ASSERT_NE(tester.pool_.Init(vmo_vector), ZX_OK);
186 // Now assign the vmos:
187 ASSERT_TRUE(AssignVmos(kNumVmos, kVmoTestSize, vmo_vector.get()));
188 ASSERT_EQ(tester.pool_.Init(vmo_vector), ZX_OK);
189
190 ASSERT_TRUE(tester.CheckAccounting(false, 0));
191 END_TEST;
192 }
193
vmo_pool_fill_and_empty_pool_test()194 bool vmo_pool_fill_and_empty_pool_test() {
195 BEGIN_TEST;
196 VmoPoolTester tester;
197 ASSERT_TRUE(tester.Init());
198 ASSERT_TRUE(tester.CheckAccounting(false, 0));
199 END_TEST;
200 }
201
vmo_pool_double_get_buffer_test()202 bool vmo_pool_double_get_buffer_test() {
203 BEGIN_TEST;
204 VmoPoolTester tester;
205 ASSERT_TRUE(tester.Init());
206 ASSERT_EQ(tester.pool_.GetNewBuffer(), ZX_OK);
207 ASSERT_EQ(tester.pool_.GetNewBuffer(), ZX_ERR_BAD_STATE);
208
209 // Now check accounting:
210 ASSERT_TRUE(tester.CheckAccounting(true, 0));
211 END_TEST;
212 }
213
214 // Checks that you can cancel a buffer, which will put it back into the pool.
vmo_pool_release_before_complete_test()215 bool vmo_pool_release_before_complete_test() {
216 BEGIN_TEST;
217 VmoPoolTester tester;
218 ASSERT_TRUE(tester.Init());
219 uint32_t current_buffer;
220 ASSERT_EQ(tester.pool_.GetNewBuffer(¤t_buffer), ZX_OK);
221 ASSERT_EQ(tester.pool_.BufferRelease(current_buffer), ZX_OK);
222 ASSERT_TRUE(tester.CheckHasNoBuffer());
223 // Running BufferCompleted should now fail, because we did not have an
224 // in-progress buffer.
225 ASSERT_EQ(tester.pool_.BufferCompleted(¤t_buffer), ZX_ERR_BAD_STATE);
226
227 // Now check accounting:
228 ASSERT_TRUE(tester.CheckAccounting(false, 0));
229 END_TEST;
230 }
231
vmo_pool_release_wrong_buffer_test()232 bool vmo_pool_release_wrong_buffer_test() {
233 BEGIN_TEST;
234 VmoPoolTester tester;
235 ASSERT_TRUE(tester.Init());
236
237 uint32_t current_buffer;
238 ASSERT_EQ(tester.pool_.GetNewBuffer(¤t_buffer), ZX_OK);
239 ASSERT_EQ(tester.pool_.BufferCompleted(¤t_buffer), ZX_OK);
240 // Test an out-of-bounds index:
241 ASSERT_EQ(tester.pool_.BufferRelease(kNumVmos), ZX_ERR_INVALID_ARGS);
242 // Test all of the buffer indices that are not locked:
243 for (uint32_t i = 0; i < kNumVmos; ++i) {
244 if (i == current_buffer) {
245 continue;
246 }
247 ASSERT_EQ(tester.pool_.BufferRelease(i), ZX_ERR_NOT_FOUND);
248 }
249 // Now check accounting:
250 ASSERT_TRUE(tester.CheckAccounting(false, 1));
251 END_TEST;
252 }
253
254 // Checks that the pool does not need to be amptied or filled in any particular
255 // order.
vmo_pool_out_of_order_test()256 bool vmo_pool_out_of_order_test() {
257 BEGIN_TEST;
258 VmoPoolTester tester;
259 ASSERT_TRUE(tester.Init());
260 ASSERT_TRUE(tester.ShufflePool());
261 // Now check accounting:
262 ASSERT_TRUE(tester.CheckAccounting(false, 0));
263 END_TEST;
264 }
265
vmo_pool_reset_test()266 bool vmo_pool_reset_test() {
267 BEGIN_TEST;
268 VmoPoolTester tester;
269 ASSERT_TRUE(tester.Init());
270 size_t test_cases[]{0, 1, kNumVmos / 2, kNumVmos};
271 for (size_t buffer_count : test_cases) {
272 // With no buffer in progress
273 ASSERT_TRUE(tester.FillBuffers(buffer_count));
274 tester.pool_.Reset();
275 ASSERT_TRUE(tester.CheckAccounting(false, 0));
276 // With buffer in progress:
277 if (buffer_count != kNumVmos) {
278 ASSERT_TRUE(tester.FillBuffers(buffer_count));
279 ASSERT_EQ(tester.pool_.GetNewBuffer(), ZX_OK);
280 tester.pool_.Reset();
281 ASSERT_TRUE(tester.CheckAccounting(false, 0));
282 }
283 }
284 END_TEST;
285 }
286
vmo_pool_reinit()287 bool vmo_pool_reinit() {
288 BEGIN_TEST;
289 VmoPoolTester tester;
290 ASSERT_TRUE(tester.Init());
291 ASSERT_TRUE(tester.CheckAccounting(false, 0));
292
293 ASSERT_TRUE(tester.Init());
294 ASSERT_TRUE(tester.CheckAccounting(false, 0));
295 END_TEST;
296 }
297
298 } // namespace
299
300 BEGIN_TEST_CASE(vmo_pool_tests)
301 RUN_NAMED_TEST("vmo_pool_reset", vmo_pool_reset_test)
302 RUN_NAMED_TEST("vmo_pool_init_vector", vmo_pool_init_vector_test)
303 RUN_NAMED_TEST("vmo_pool_double_get_buffer", vmo_pool_double_get_buffer_test)
304 RUN_NAMED_TEST("vmo_pool_release_wrong_buffer", vmo_pool_release_wrong_buffer_test)
305 RUN_NAMED_TEST("vmo_pool_release_before_complete", vmo_pool_release_before_complete_test)
306 RUN_NAMED_TEST("vmo_pool_fill_and_empty_pool", vmo_pool_fill_and_empty_pool_test)
307 RUN_NAMED_TEST("vmo_pool_out_of_order", vmo_pool_out_of_order_test)
308 RUN_NAMED_TEST("vmo_pool_reinit", vmo_pool_reinit)
309 END_TEST_CASE(vmo_pool_tests)
310