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,allocFreeOne)89 TEST_F(PldmInstanceDbTest, allocFreeOne)
90 {
91 struct pldm_instance_db* db = nullptr;
92 const pldm_tid_t tid = 1;
93 pldm_instance_id_t iid;
94
95 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
96 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &iid), 0);
97 EXPECT_EQ(pldm_instance_id_free(db, tid, iid), 0);
98 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
99 }
100
TEST_F(PldmInstanceDbTest,allocFreeTwoSerialSameTid)101 TEST_F(PldmInstanceDbTest, allocFreeTwoSerialSameTid)
102 {
103 static constexpr pldm_tid_t tid = 1;
104
105 struct pldm_instance_db* db = nullptr;
106 pldm_instance_id_t first;
107 pldm_instance_id_t second;
108
109 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
110 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &first), 0);
111 EXPECT_EQ(pldm_instance_id_free(db, tid, first), 0);
112 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &second), 0);
113 EXPECT_EQ(pldm_instance_id_free(db, tid, second), 0);
114 EXPECT_NE(first, second);
115 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
116 }
117
TEST_F(PldmInstanceDbTest,allocFreeTwoSerialDifferentTid)118 TEST_F(PldmInstanceDbTest, allocFreeTwoSerialDifferentTid)
119 {
120 struct
121 {
122 pldm_tid_t tid;
123 pldm_instance_id_t iid;
124 } instances[] = {
125 {1, 0},
126 {2, 0},
127 };
128
129 struct pldm_instance_db* db = nullptr;
130
131 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
132
133 EXPECT_EQ(pldm_instance_id_alloc(db, instances[0].tid, &instances[0].iid),
134 0);
135 EXPECT_EQ(pldm_instance_id_alloc(db, instances[1].tid, &instances[1].iid),
136 0);
137
138 EXPECT_EQ(instances[0].iid, instances[1].iid);
139
140 EXPECT_EQ(pldm_instance_id_free(db, instances[1].tid, instances[1].iid), 0);
141 EXPECT_EQ(pldm_instance_id_free(db, instances[0].tid, instances[0].iid), 0);
142
143 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
144 }
145
TEST_F(PldmInstanceDbTest,allocFreeTwoConcurrentSameTid)146 TEST_F(PldmInstanceDbTest, allocFreeTwoConcurrentSameTid)
147 {
148 static constexpr pldm_tid_t tid = 1;
149
150 struct
151 {
152 struct pldm_instance_db* db;
153 pldm_instance_id_t iid;
154 } connections[] = {
155 {nullptr, 0},
156 {nullptr, 0},
157 };
158
159 ASSERT_EQ(pldm_instance_db_init(&connections[0].db, dbPath.c_str()), 0);
160 EXPECT_EQ(
161 pldm_instance_id_alloc(connections[0].db, tid, &connections[0].iid), 0);
162
163 ASSERT_EQ(pldm_instance_db_init(&connections[1].db, dbPath.c_str()), 0);
164 EXPECT_EQ(
165 pldm_instance_id_alloc(connections[1].db, tid, &connections[1].iid), 0);
166
167 EXPECT_NE(connections[0].iid, connections[1].iid);
168
169 EXPECT_EQ(pldm_instance_id_free(connections[1].db, tid, connections[1].iid),
170 0);
171 ASSERT_EQ(pldm_instance_db_destroy(connections[1].db), 0);
172
173 EXPECT_EQ(pldm_instance_id_free(connections[0].db, tid, connections[0].iid),
174 0);
175 ASSERT_EQ(pldm_instance_db_destroy(connections[0].db), 0);
176 }
177
TEST_F(PldmInstanceDbTest,allocFreeTwoConcurrentDifferentTid)178 TEST_F(PldmInstanceDbTest, allocFreeTwoConcurrentDifferentTid)
179 {
180 struct
181 {
182 struct pldm_instance_db* db;
183 pldm_tid_t tid;
184 pldm_instance_id_t iid;
185 } connections[] = {
186 {nullptr, 1, 0},
187 {nullptr, 2, 0},
188 };
189
190 ASSERT_EQ(pldm_instance_db_init(&connections[0].db, dbPath.c_str()), 0);
191 EXPECT_EQ(pldm_instance_id_alloc(connections[0].db, connections[0].tid,
192 &connections[0].iid),
193 0);
194
195 ASSERT_EQ(pldm_instance_db_init(&connections[1].db, dbPath.c_str()), 0);
196 EXPECT_EQ(pldm_instance_id_alloc(connections[1].db, connections[1].tid,
197 &connections[1].iid),
198 0);
199
200 EXPECT_EQ(connections[0].iid, connections[1].iid);
201
202 EXPECT_EQ(pldm_instance_id_free(connections[1].db, connections[1].tid,
203 connections[1].iid),
204 0);
205 ASSERT_EQ(pldm_instance_db_destroy(connections[1].db), 0);
206
207 EXPECT_EQ(pldm_instance_id_free(connections[0].db, connections[0].tid,
208 connections[0].iid),
209 0);
210 ASSERT_EQ(pldm_instance_db_destroy(connections[0].db), 0);
211 }
212
TEST_F(PldmInstanceDbTest,allocAllInstanceIds)213 TEST_F(PldmInstanceDbTest, allocAllInstanceIds)
214 {
215 static constexpr pldm_tid_t tid = 1;
216
217 struct pldm_instance_db* db = nullptr;
218 std::array<pldm_instance_id_t, pldmMaxInstanceIds> iids = {};
219 pldm_instance_id_t extra;
220
221 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
222
223 for (auto& iid : iids)
224 {
225 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &iid), 0);
226 }
227
228 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &extra), -EAGAIN);
229
230 for (auto& iid : iids)
231 {
232 EXPECT_EQ(pldm_instance_id_free(db, tid, iid), 0);
233 }
234
235 EXPECT_EQ(pldm_instance_id_alloc(db, tid, &extra), 0);
236
237 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
238 }
239
TEST_F(PldmInstanceDbTest,releaseConflictedSameTid)240 TEST_F(PldmInstanceDbTest, releaseConflictedSameTid)
241 {
242 static constexpr pldm_tid_t tid = 1;
243 struct
244 {
245 struct pldm_instance_db* db;
246 pldm_instance_id_t iid;
247 } connections[] = {
248 {nullptr, 0},
249 {nullptr, 0},
250 };
251 pldm_instance_id_t iid;
252
253 /* Allocate IID 0 for the TID to the first connection */
254 ASSERT_EQ(pldm_instance_db_init(&connections[0].db, dbPath.c_str()), 0);
255 EXPECT_EQ(
256 pldm_instance_id_alloc(connections[0].db, tid, &connections[0].iid), 0);
257
258 /*
259 * On the second connection, allocate the first available IID for the TID.
260 * This should generate a conflict on IID 0 (allocated to the first
261 * connection), and result in IID 1 being provided.
262 *
263 * There should now be one read lock held on each of IID 0 and IID 1 for TID
264 * 1 (by the first and second connections respectively).
265 */
266 ASSERT_EQ(pldm_instance_db_init(&connections[1].db, dbPath.c_str()), 0);
267 EXPECT_EQ(
268 pldm_instance_id_alloc(connections[1].db, tid, &connections[1].iid), 0);
269
270 /*
271 * Make sure the implementation hasn't allocated the connections a
272 * conflicting IID for the TID.
273 */
274 EXPECT_NE(connections[0].iid, connections[1].iid);
275
276 /*
277 * Now free the IID allocated to the first connection.
278 *
279 * We should be able to re-acquire this later.
280 */
281 EXPECT_EQ(pldm_instance_id_free(connections[0].db, tid, connections[0].iid),
282 0);
283
284 /*
285 * Iterate through the IID space on the first connection to wrap it back
286 * around to IID 0.
287 *
288 * Note that:
289 *
290 * 1. The first connection has already allocated (and released) IID 0,
291 * eliminating one iteration
292 *
293 * 2. IID 1 is held by the second connection. This eliminates a second
294 * iteration as it must be skipped to avoid a conflict.
295 */
296 for (int i = 0; i < (pldmMaxInstanceIds - 1 - 1); i++)
297 {
298 EXPECT_EQ(pldm_instance_id_alloc(connections[0].db, tid, &iid), 0);
299 EXPECT_EQ(pldm_instance_id_free(connections[0].db, tid, iid), 0);
300 }
301
302 /*
303 * The next IID allocated to the first connection should be the IID it
304 * allocated initially (which should be 0).
305 */
306 EXPECT_EQ(pldm_instance_id_alloc(connections[0].db, tid, &iid), 0);
307 EXPECT_EQ(iid, connections[0].iid);
308
309 /* Now tidy up */
310 ASSERT_EQ(pldm_instance_id_free(connections[0].db, tid, iid), 0);
311
312 EXPECT_EQ(pldm_instance_id_free(connections[1].db, tid, connections[1].iid),
313 0);
314 ASSERT_EQ(pldm_instance_db_destroy(connections[1].db), 0);
315 ASSERT_EQ(pldm_instance_db_destroy(connections[0].db), 0);
316 }
317
TEST_F(PldmInstanceDbTest,freeUnallocatedInstanceId)318 TEST_F(PldmInstanceDbTest, freeUnallocatedInstanceId)
319 {
320 struct pldm_instance_db* db = nullptr;
321 const pldm_tid_t tid = 1;
322
323 ASSERT_EQ(pldm_instance_db_init(&db, dbPath.c_str()), 0);
324 EXPECT_NE(pldm_instance_id_free(db, tid, 0), 0);
325 ASSERT_EQ(pldm_instance_db_destroy(db), 0);
326 }
327