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 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 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 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: 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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