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