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(&current_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(&current_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(&current_buffer), ZX_OK);
239     ASSERT_EQ(tester.pool_.BufferCompleted(&current_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