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