1 #include <libpldm/base.h>
2 #include <libpldm/instance-id.h>
3
4 #include <cerrno>
5 #include <cstdlib>
6 #include <cstring>
7 #include <filesystem>
8
9 #include <gtest/gtest.h>
10
11 static constexpr auto pldmMaxInstanceIds = 32;
12 static const std::filesystem::path nonexistentDb = {"remove-this-file"};
13
TEST(InstanceId,dbInstanceNullDb)14 TEST(InstanceId, dbInstanceNullDb)
15 {
16 ASSERT_FALSE(std::filesystem::exists(nonexistentDb));
17 EXPECT_EQ(::pldm_instance_db_init(nullptr, nonexistentDb.c_str()), -EINVAL);
18 }
19
TEST(InstanceId,dbInstanceNonNullDerefDb)20 TEST(InstanceId, dbInstanceNonNullDerefDb)
21 {
22 struct pldm_instance_db* db = (struct pldm_instance_db*)8;
23
24 ASSERT_FALSE(std::filesystem::exists(nonexistentDb));
25 EXPECT_EQ(::pldm_instance_db_init(&db, nonexistentDb.c_str()), -EINVAL);
26 }
27
TEST(InstanceId,dbInstanceInvalidPath)28 TEST(InstanceId, dbInstanceInvalidPath)
29 {
30 struct pldm_instance_db* db = nullptr;
31
32 EXPECT_NE(::pldm_instance_db_init(&db, ""), 0);
33 }
34
35 class PldmInstanceDbTest : public ::testing::Test
36 {
37 protected:
SetUp()38 void SetUp() override
39 {
40 static const char dbTmpl[] = "db.XXXXXX";
41 char dbName[sizeof(dbTmpl)] = {};
42
43 ::strncpy(dbName, dbTmpl, sizeof(dbName));
44 fd = ::mkstemp(dbName);
45 ASSERT_NE(fd, -1);
46
47 dbPath = std::filesystem::path(dbName);
48 std::filesystem::resize_file(
49 dbPath, (uintmax_t)(PLDM_MAX_TIDS)*pldmMaxInstanceIds);
50 }
51
TearDown()52 void TearDown() override
53 {
54 std::filesystem::remove(dbPath);
55 ::close(fd);
56 }
57
58 std::filesystem::path dbPath;
59
60 private:
61 int fd;
62 };
63
TEST_F(PldmInstanceDbTest,dbLengthZero)64 TEST_F(PldmInstanceDbTest, dbLengthZero)
65 {
66 struct pldm_instance_db* db = nullptr;
67
68 std::filesystem::resize_file(dbPath, 0);
69 EXPECT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), -EINVAL);
70 }
71
TEST_F(PldmInstanceDbTest,dbLengthShort)72 TEST_F(PldmInstanceDbTest, dbLengthShort)
73 {
74 struct pldm_instance_db* db = nullptr;
75
76 std::filesystem::resize_file(dbPath,
77 PLDM_MAX_TIDS * pldmMaxInstanceIds - 1);
78 EXPECT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), -EINVAL);
79 }
80
TEST_F(PldmInstanceDbTest,dbInstance)81 TEST_F(PldmInstanceDbTest, dbInstance)
82 {
83 struct pldm_instance_db* db = nullptr;
84
85 EXPECT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
86 EXPECT_EQ(pldm_instance_db_destroy(db), 0);
87 }
88
TEST_F(PldmInstanceDbTest,allocOnNulldb)89 TEST_F(PldmInstanceDbTest, allocOnNulldb)
90 {
91 struct pldm_instance_db* db = nullptr;
92 const pldm_tid_t tid = 1;
93 pldm_instance_id_t iid;
94 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &iid), -EINVAL);
95 }
96
TEST_F(PldmInstanceDbTest,freeOnNulldb)97 TEST_F(PldmInstanceDbTest, freeOnNulldb)
98 {
99 struct pldm_instance_db* db = nullptr;
100 const pldm_tid_t tid = 1;
101 pldm_instance_id_t iid = 1;
102
103 EXPECT_EQ(pldm_instance_id_free(db, tid, iid), -EINVAL);
104 }
105
TEST_F(PldmInstanceDbTest,allocFreeOne)106 TEST_F(PldmInstanceDbTest, allocFreeOne)
107 {
108 struct pldm_instance_db* db = nullptr;
109 const pldm_tid_t tid = 1;
110 pldm_instance_id_t iid;
111
112 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
113 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &iid), 0);
114 EXPECT_EQ(pldm_instance_id_free(db, tid, iid), 0);
115 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
116 }
117
TEST_F(PldmInstanceDbTest,allocFreeTwoSerialSameTid)118 TEST_F(PldmInstanceDbTest, allocFreeTwoSerialSameTid)
119 {
120 static constexpr pldm_tid_t tid = 1;
121
122 struct pldm_instance_db* db = nullptr;
123 pldm_instance_id_t first;
124 pldm_instance_id_t second;
125
126 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
127 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &first), 0);
128 EXPECT_EQ(pldm_instance_id_free(db, tid, first), 0);
129 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &second), 0);
130 EXPECT_EQ(pldm_instance_id_free(db, tid, second), 0);
131 EXPECT_NE(first, second);
132 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
133 }
134
TEST_F(PldmInstanceDbTest,allocFreeTwoSerialDifferentTid)135 TEST_F(PldmInstanceDbTest, allocFreeTwoSerialDifferentTid)
136 {
137 struct
138 {
139 pldm_tid_t tid;
140 pldm_instance_id_t iid;
141 } instances[] = {
142 {1, 0},
143 {2, 0},
144 };
145
146 struct pldm_instance_db* db = nullptr;
147
148 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
149
150 EXPECT_EQ(pldm_instance_id_alloc(db, instances[0].tid, &instances[0].iid),
151 0);
152 EXPECT_EQ(pldm_instance_id_alloc(db, instances[1].tid, &instances[1].iid),
153 0);
154
155 EXPECT_EQ(instances[0].iid, instances[1].iid);
156
157 EXPECT_EQ(pldm_instance_id_free(db, instances[1].tid, instances[1].iid), 0);
158 EXPECT_EQ(pldm_instance_id_free(db, instances[0].tid, instances[0].iid), 0);
159
160 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
161 }
162
TEST_F(PldmInstanceDbTest,allocFreeTwoConcurrentSameTid)163 TEST_F(PldmInstanceDbTest, allocFreeTwoConcurrentSameTid)
164 {
165 static constexpr pldm_tid_t tid = 1;
166
167 struct
168 {
169 struct pldm_instance_db* db;
170 pldm_instance_id_t iid;
171 } connections[] = {
172 {nullptr, 0},
173 {nullptr, 0},
174 };
175
176 ASSERT_EQ(pldm_instance_db_init(&connections[0].db, dbPath.c_str()), 0);
177 EXPECT_EQ(
178 pldm_instance_id_alloc(connections[0].db, tid, &connections[0].iid), 0);
179
180 ASSERT_EQ(pldm_instance_db_init(&connections[1].db, dbPath.c_str()), 0);
181 EXPECT_EQ(
182 pldm_instance_id_alloc(connections[1].db, tid, &connections[1].iid), 0);
183
184 EXPECT_NE(connections[0].iid, connections[1].iid);
185
186 EXPECT_EQ(pldm_instance_id_free(connections[1].db, tid, connections[1].iid),
187 0);
188 ASSERT_EQ(pldm_instance_db_destroy(connections[1].db), 0);
189
190 EXPECT_EQ(pldm_instance_id_free(connections[0].db, tid, connections[0].iid),
191 0);
192 ASSERT_EQ(pldm_instance_db_destroy(connections[0].db), 0);
193 }
194
TEST_F(PldmInstanceDbTest,allocFreeTwoConcurrentDifferentTid)195 TEST_F(PldmInstanceDbTest, allocFreeTwoConcurrentDifferentTid)
196 {
197 struct
198 {
199 struct pldm_instance_db* db;
200 pldm_tid_t tid;
201 pldm_instance_id_t iid;
202 } connections[] = {
203 {nullptr, 1, 0},
204 {nullptr, 2, 0},
205 };
206
207 ASSERT_EQ(pldm_instance_db_init(&connections[0].db, dbPath.c_str()), 0);
208 EXPECT_EQ(pldm_instance_id_alloc(connections[0].db, connections[0].tid,
209 &connections[0].iid),
210 0);
211
212 ASSERT_EQ(pldm_instance_db_init(&connections[1].db, dbPath.c_str()), 0);
213 EXPECT_EQ(pldm_instance_id_alloc(connections[1].db, connections[1].tid,
214 &connections[1].iid),
215 0);
216
217 EXPECT_EQ(connections[0].iid, connections[1].iid);
218
219 EXPECT_EQ(pldm_instance_id_free(connections[1].db, connections[1].tid,
220 connections[1].iid),
221 0);
222 ASSERT_EQ(pldm_instance_db_destroy(connections[1].db), 0);
223
224 EXPECT_EQ(pldm_instance_id_free(connections[0].db, connections[0].tid,
225 connections[0].iid),
226 0);
227 ASSERT_EQ(pldm_instance_db_destroy(connections[0].db), 0);
228 }
229
TEST_F(PldmInstanceDbTest,allocAllInstanceIds)230 TEST_F(PldmInstanceDbTest, allocAllInstanceIds)
231 {
232 static constexpr pldm_tid_t tid = 1;
233
234 struct pldm_instance_db* db = nullptr;
235 std::array<pldm_instance_id_t, pldmMaxInstanceIds> iids = {};
236 pldm_instance_id_t extra;
237
238 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
239
240 for (auto& iid : iids)
241 {
242 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &iid), 0);
243 }
244
245 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &extra), -EAGAIN);
246
247 for (auto& iid : iids)
248 {
249 EXPECT_EQ(pldm_instance_id_free(db, tid, iid), 0);
250 }
251
252 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &extra), 0);
253
254 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
255 }
256
TEST_F(PldmInstanceDbTest,releaseConflictedSameTid)257 TEST_F(PldmInstanceDbTest, releaseConflictedSameTid)
258 {
259 static constexpr pldm_tid_t tid = 1;
260 struct
261 {
262 struct pldm_instance_db* db;
263 pldm_instance_id_t iid;
264 } connections[] = {
265 {nullptr, 0},
266 {nullptr, 0},
267 };
268 pldm_instance_id_t iid;
269
270 /* Allocate IID 0 for the TID to the first connection */
271 ASSERT_EQ(pldm_instance_db_init(&connections[0].db, dbPath.c_str()), 0);
272 EXPECT_EQ(
273 pldm_instance_id_alloc(connections[0].db, tid, &connections[0].iid), 0);
274
275 /*
276 * On the second connection, allocate the first available IID for the TID.
277 * This should generate a conflict on IID 0 (allocated to the first
278 * connection), and result in IID 1 being provided.
279 *
280 * There should now be one read lock held on each of IID 0 and IID 1 for TID
281 * 1 (by the first and second connections respectively).
282 */
283 ASSERT_EQ(pldm_instance_db_init(&connections[1].db, dbPath.c_str()), 0);
284 EXPECT_EQ(
285 pldm_instance_id_alloc(connections[1].db, tid, &connections[1].iid), 0);
286
287 /*
288 * Make sure the implementation hasn't allocated the connections a
289 * conflicting IID for the TID.
290 */
291 EXPECT_NE(connections[0].iid, connections[1].iid);
292
293 /*
294 * Now free the IID allocated to the first connection.
295 *
296 * We should be able to re-acquire this later.
297 */
298 EXPECT_EQ(pldm_instance_id_free(connections[0].db, tid, connections[0].iid),
299 0);
300
301 /*
302 * Iterate through the IID space on the first connection to wrap it back
303 * around to IID 0.
304 *
305 * Note that:
306 *
307 * 1. The first connection has already allocated (and released) IID 0,
308 * eliminating one iteration
309 *
310 * 2. IID 1 is held by the second connection. This eliminates a second
311 * iteration as it must be skipped to avoid a conflict.
312 */
313 for (int i = 0; i < (pldmMaxInstanceIds - 1 - 1); i++)
314 {
315 EXPECT_EQ(pldm_instance_id_alloc(connections[0].db, tid, &iid), 0);
316 EXPECT_EQ(pldm_instance_id_free(connections[0].db, tid, iid), 0);
317 }
318
319 /*
320 * The next IID allocated to the first connection should be the IID it
321 * allocated initially (which should be 0).
322 */
323 EXPECT_EQ(pldm_instance_id_alloc(connections[0].db, tid, &iid), 0);
324 EXPECT_EQ(iid, connections[0].iid);
325
326 /* Now tidy up */
327 ASSERT_EQ(pldm_instance_id_free(connections[0].db, tid, iid), 0);
328
329 EXPECT_EQ(pldm_instance_id_free(connections[1].db, tid, connections[1].iid),
330 0);
331 ASSERT_EQ(pldm_instance_db_destroy(connections[1].db), 0);
332 ASSERT_EQ(pldm_instance_db_destroy(connections[0].db), 0);
333 }
334
TEST_F(PldmInstanceDbTest,freeUnallocatedInstanceId)335 TEST_F(PldmInstanceDbTest, freeUnallocatedInstanceId)
336 {
337 struct pldm_instance_db* db = nullptr;
338 const pldm_tid_t tid = 1;
339
340 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
341 EXPECT_NE(pldm_instance_id_free(db, tid, 0), 0);
342 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
343 }
344