xref: /openbmc/libpldm/tests/instance-id.cpp (revision 9c57ef5d371cec85075c7524a34c77bb8d8d884b)
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