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