blob: 7eb41ddec0dc21cd4b1059d212f34546a88904c1 [file] [log] [blame]
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +00001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +00003 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +00007 */
8
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +00009#include <gmock/gmock.h>
10
11extern "C" {
12#include "hf/mpool.h"
13}
14
15namespace
16{
Andrew Scull63d1f3f2018-12-06 13:29:10 +000017using ::testing::Eq;
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +000018using ::testing::IsNull;
19using ::testing::NotNull;
20
21/**
22 * Checks that the given allocations come from the given chunks.
23 */
24bool check_allocs(std::vector<std::unique_ptr<char[]>>& chunks,
25 std::vector<uintptr_t>& allocs, size_t entries_per_chunk,
26 size_t entry_size)
27{
28 size_t i, j;
29
30 if (allocs.size() != chunks.size() * entries_per_chunk) {
31 return false;
32 }
33
34 sort(allocs.begin(), allocs.end());
35 sort(chunks.begin(), chunks.end(),
36 [](const std::unique_ptr<char[]>& a,
37 const std::unique_ptr<char[]>& b) {
38 return a.get() < b.get();
39 });
40
41 for (i = 0; i < chunks.size(); i++) {
42 if ((uintptr_t)chunks[i].get() !=
43 allocs[i * entries_per_chunk]) {
44 return false;
45 }
46
47 for (j = 1; j < entries_per_chunk; j++) {
48 size_t k = i * entries_per_chunk + j;
49 if (allocs[k] != allocs[k - 1] + entry_size) {
50 return false;
51 }
52 }
53 }
54
55 return true;
56}
57
58/**
59 * Add chunks to the given mem pool and chunk vector.
60 */
61static void add_chunks(std::vector<std::unique_ptr<char[]>>& chunks,
62 struct mpool* p, size_t count, size_t size)
63{
64 size_t i;
65
66 for (i = 0; i < count; i++) {
67 chunks.emplace_back(std::make_unique<char[]>(size));
68 mpool_add_chunk(p, chunks.back().get(), size);
69 }
70}
71
72/**
73 * Validates allocations from a memory pool.
74 */
75TEST(mpool, allocation)
76{
77 struct mpool p;
78 constexpr size_t entry_size = 16;
79 constexpr size_t entries_per_chunk = 10;
80 constexpr size_t chunk_count = 10;
81 std::vector<std::unique_ptr<char[]>> chunks;
82 std::vector<uintptr_t> allocs;
83 void* ret;
84
85 mpool_init(&p, entry_size);
86
87 /* Allocate from an empty pool. */
88 EXPECT_THAT(mpool_alloc(&p), IsNull());
89
90 /*
91 * Add a chunk that is too small, it should be ignored, and allocation
92 * should return NULL.
93 */
94 mpool_add_chunk(&p, NULL, entry_size - 1);
95 EXPECT_THAT(mpool_alloc(&p), IsNull());
96
97 /* Allocate a number of chunks and add them to the pool. */
98 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
99
100 /* Allocate from the pool until we run out of memory. */
101 while ((ret = mpool_alloc(&p))) {
102 allocs.push_back((uintptr_t)ret);
103 }
104
105 /* Check that returned entries are within chunks that were added. */
106 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
107 true);
108}
109
110/**
111 * Validates frees into a memory pool.
112 */
113TEST(mpool, freeing)
114{
115 struct mpool p;
116 constexpr size_t entry_size = 16;
Andrew Scull63d1f3f2018-12-06 13:29:10 +0000117 constexpr size_t entries_per_chunk = 12;
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +0000118 constexpr size_t chunk_count = 10;
119 std::vector<std::unique_ptr<char[]>> chunks;
120 std::vector<uintptr_t> allocs;
121 size_t i;
122 alignas(entry_size) char entry[entry_size];
123 void* ret;
124
125 mpool_init(&p, entry_size);
126
127 /* Allocate from an empty pool. */
128 EXPECT_THAT(mpool_alloc(&p), IsNull());
129
130 /* Free an entry into the pool, then allocate it back. */
131 mpool_free(&p, &entry[0]);
132 EXPECT_THAT(mpool_alloc(&p), (void*)&entry[0]);
133 EXPECT_THAT(mpool_alloc(&p), IsNull());
134
135 /* Allocate a number of chunks and add them to the pool. */
136 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
137
138 /*
139 * Free again into the pool. Ensure that we get entry back on next
140 * allocation instead of something from the chunks.
141 */
142 mpool_free(&p, &entry[0]);
143 EXPECT_THAT(mpool_alloc(&p), (void*)&entry[0]);
144
145 /* Allocate from the pool until we run out of memory. */
146 while ((ret = mpool_alloc(&p))) {
147 allocs.push_back((uintptr_t)ret);
148 }
149
150 /*
151 * Free again into the pool. Ensure that we get entry back on next
152 * allocation instead of something from the chunks.
153 */
154 mpool_free(&p, &entry[0]);
155 EXPECT_THAT(mpool_alloc(&p), (void*)&entry[0]);
156
157 /* Add entries back to the pool by freeing them. */
158 for (i = 0; i < allocs.size(); i++) {
159 mpool_free(&p, (void*)allocs[i]);
160 }
161 allocs.clear();
162
163 /* Allocate from the pool until we run out of memory. */
164 while ((ret = mpool_alloc(&p))) {
165 allocs.push_back((uintptr_t)ret);
166 }
167
168 /* Check that returned entries are within chunks that were added. */
169 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
170 true);
171}
172
173/**
174 * Initialises a memory pool from an existing one.
175 */
176TEST(mpool, init_from)
177{
178 struct mpool p, q;
179 constexpr size_t entry_size = 16;
180 constexpr size_t entries_per_chunk = 10;
181 constexpr size_t chunk_count = 10;
182 std::vector<std::unique_ptr<char[]>> chunks;
183 std::vector<uintptr_t> allocs;
184 size_t i;
185 void* ret;
186
187 mpool_init(&p, entry_size);
188
189 /* Allocate a number of chunks and add them to the pool. */
190 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
191
192 /* Allocate half of the elements. */
193 for (i = 0; i < entries_per_chunk * chunk_count / 2; i++) {
194 void* ret = mpool_alloc(&p);
195 ASSERT_THAT(ret, NotNull());
196 allocs.push_back((uintptr_t)ret);
197 }
198
199 /* Add entries back to the pool by freeing them. */
200 for (i = 0; i < allocs.size(); i++) {
201 mpool_free(&p, (void*)allocs[i]);
202 }
203 allocs.clear();
204
205 /* Initialise q from p. */
206 mpool_init_from(&q, &p);
207
208 /* Allocation from p must now fail. */
209 EXPECT_THAT(mpool_alloc(&p), IsNull());
210
211 /* Allocate from q until we run out of memory. */
212 while ((ret = mpool_alloc(&q))) {
213 allocs.push_back((uintptr_t)ret);
214 }
215
216 /* Check that returned entries are within chunks that were added. */
217 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
218 true);
219}
220
221/**
222 * Initialises a memory pool from an existing one.
223 */
224TEST(mpool, alloc_contiguous)
225{
226 struct mpool p;
227 constexpr size_t entry_size = 16;
Andrew Scullf0f6be52018-12-21 14:44:33 +0000228 constexpr size_t entries_per_chunk = 12;
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +0000229 constexpr size_t chunk_count = 10;
230 std::vector<std::unique_ptr<char[]>> chunks;
231 std::vector<uintptr_t> allocs;
232 size_t i;
233 void* ret;
234 uintptr_t next;
235
236 mpool_init(&p, entry_size);
237
238 /* Allocate a number of chunks and add them to the pool. */
239 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
240
241 /*
242 * Allocate entries until the remaining chunk is aligned to 2 entries,
243 * but not aligned to 4 entries.
244 */
245 do {
246 ret = mpool_alloc(&p);
247 ASSERT_THAT(ret, NotNull());
248 allocs.push_back((uintptr_t)ret);
Andrew Scull63d1f3f2018-12-06 13:29:10 +0000249 next = ((uintptr_t)ret / entry_size) + 1;
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +0000250 } while ((next % 4) != 2);
251
252 /* Allocate 5 entries with an alignment of 4. So two must be skipped. */
253 ret = mpool_alloc_contiguous(&p, 5, 4);
254 ASSERT_THAT(ret, NotNull());
255 ASSERT_THAT((uintptr_t)ret, (next + 2) * entry_size);
256 for (i = 0; i < 5; i++) {
257 allocs.push_back((uintptr_t)ret + i * entry_size);
258 }
259
260 /* Allocate a whole chunk. */
261 ret = mpool_alloc_contiguous(&p, entries_per_chunk, 1);
262 ASSERT_THAT(ret, NotNull());
263 for (i = 0; i < entries_per_chunk; i++) {
264 allocs.push_back((uintptr_t)ret + i * entry_size);
265 }
266
267 /* Allocate 2 entries that are already aligned. */
268 ret = mpool_alloc_contiguous(&p, 2, 1);
269 ASSERT_THAT(ret, NotNull());
270 allocs.push_back((uintptr_t)ret);
271 allocs.push_back((uintptr_t)ret + entry_size);
272
273 /* Allocate from p until we run out of memory. */
274 while ((ret = mpool_alloc(&p))) {
275 allocs.push_back((uintptr_t)ret);
276 }
277
278 /* Check that returned entries are within chunks that were added. */
279 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
280 true);
281}
282
Andrew Scull63d1f3f2018-12-06 13:29:10 +0000283TEST(mpool, allocation_with_fallback)
284{
285 struct mpool fallback;
286 struct mpool p;
287 constexpr size_t entry_size = 16;
288 constexpr size_t entries_per_chunk = 10;
289 constexpr size_t chunk_count = 10;
290 std::vector<std::unique_ptr<char[]>> chunks;
291 std::vector<uintptr_t> allocs;
292 void* ret;
293
294 mpool_init(&fallback, entry_size);
295 mpool_init_with_fallback(&p, &fallback);
296
297 /* Allocate from an empty pool. */
298 EXPECT_THAT(mpool_alloc(&p), IsNull());
299
300 /* Allocate a number of chunks and add them to the fallback pool. */
301 add_chunks(chunks, &fallback, chunk_count,
302 entries_per_chunk * entry_size);
303
304 /* Allocate from the pool until we run out of memory. */
305 while ((ret = mpool_alloc(&p))) {
306 allocs.push_back((uintptr_t)ret);
307 }
308
309 /* Check that returned entries are within chunks that were added. */
310 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
311 true);
312}
313
314TEST(mpool, free_with_fallback)
315{
316 struct mpool fallback;
317 struct mpool p;
318 constexpr size_t entry_size = 16;
319 constexpr size_t entries_per_chunk = 1;
320 constexpr size_t chunk_count = 1;
321 std::vector<std::unique_ptr<char[]>> chunks;
322 std::vector<uintptr_t> allocs;
323 void* ret;
324
325 mpool_init(&fallback, entry_size);
326 mpool_init_with_fallback(&p, &fallback);
327
328 /* Allocate a number of chunks and add them to the fallback pool. */
329 add_chunks(chunks, &fallback, chunk_count,
330 entries_per_chunk * entry_size);
331
332 /* Allocate, making use of the fallback and free again. */
333 ret = mpool_alloc(&p);
334 mpool_free(&p, ret);
335
336 /* The entry is not available in the fallback. */
337 EXPECT_THAT(mpool_alloc(&fallback), IsNull());
338
339 /* The entry will be allocated by the local pool. */
340 EXPECT_THAT(mpool_alloc(&p), Eq(ret));
341
342 /* Return the memory to the local pool and then to the fallback. */
343 mpool_free(&p, ret);
344 mpool_fini(&p);
345
346 /* The fallback can now allocate the entry. */
347 EXPECT_THAT(mpool_alloc(&fallback), Eq(ret));
348}
349
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +0000350} /* namespace */