pdr: Remove PDR record by record set identifier
API to remove a record from a PLDM PDR repository if it is
of type FRU record set identifier and if it matches given
record set identifier. Record handle of the PDR where this
entity is found is saved and will be required to update PDR
repo by removing the entry corresponding to this entity
in the PDR table. Also the Node associated with this pldm
entity is deleted from PDR tree.
Change-Id: I24606c4d75ff64864f4aa95d8b2341b8911a7ff8
Signed-off-by: Varsha Kaverappa <vkaverap@in.ibm.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2892e7b..55a18e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
2. oem: meta: Add decode_oem_meta_file_io_read_req()
3. oem: meta: Add encode_oem_meta_file_io_read_resp()
4. pdr: Add pldm_entity_association_pdr_remove_contained_entity()
+5. pdr: Add pldm_pdr_remove_fru_record_set_by_rsi()
### Deprecated
diff --git a/include/libpldm/pdr.h b/include/libpldm/pdr.h
index 089f2bb..a028e7b 100644
--- a/include/libpldm/pdr.h
+++ b/include/libpldm/pdr.h
@@ -644,6 +644,19 @@
pldm_pdr *repo, pldm_entity *entity, bool is_remote,
uint32_t *pdr_record_handle);
+/** @brief removes a PLDM PDR record if it matches given record set identifier
+ * @param[in] repo - opaque pointer acting as a PDR repo handle
+ * @param[in] fru_rsi - FRU record set identifier
+ * @param[in] is_remote - indicates which PDR to remove, local or remote
+ * @param[out] record_handle - record handle of the fru record to be removed
+ *
+ * @return 0 on success, -EINVAL if the arguments are invalid or -EOVERFLOW if value is too
+ * large for defined type
+ */
+int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi,
+ bool is_remote,
+ uint32_t *record_handle);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/dsp/pdr.c b/src/dsp/pdr.c
index 5610205..407e04a 100644
--- a/src/dsp/pdr.c
+++ b/src/dsp/pdr.c
@@ -14,6 +14,9 @@
(sizeof(struct pldm_pdr_hdr) + \
sizeof(struct pldm_pdr_entity_association))
+#define PDR_FRU_RECORD_SET_MIN_SIZE \
+ (sizeof(struct pldm_pdr_hdr) + sizeof(struct pldm_pdr_fru_record_set))
+
typedef struct pldm_pdr_record {
uint32_t record_handle;
uint32_t size;
@@ -47,7 +50,7 @@
bool is_remote, uint16_t terminus_handle,
uint32_t *record_handle)
{
- uint32_t curr;
+ uint32_t curr = 0;
if (!repo || !data || !size) {
return -EINVAL;
@@ -1877,3 +1880,155 @@
free(new_record);
return rc;
}
+
+/* API to find the PDR record that is previous to a given PLDM PDR
+ * record in a given PLDM PDR repository
+ */
+LIBPLDM_CC_NONNULL
+static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo,
+ pldm_pdr_record *record)
+{
+ pldm_pdr_record *prev = NULL;
+ pldm_pdr_record *curr = repo->first;
+
+ while (curr != NULL) {
+ if (curr->record_handle == record->record_handle) {
+ break;
+ }
+ prev = curr;
+ curr = curr->next;
+ }
+ return prev;
+}
+
+/* API to check if a PLDM PDR record is present in a PLDM PDR repository
+ */
+LIBPLDM_CC_NONNULL
+static bool is_prev_record_present(pldm_pdr *repo, pldm_pdr_record *record)
+{
+ if (repo->first == record) {
+ return true;
+ }
+
+ return pldm_pdr_get_prev_record(repo, record) != NULL;
+}
+
+/* API to check if FRU RSI of record matches the given record set identifier.
+ * Returns 1 if the provided FRU record matches the provided record set identifier,
+ * 0 if it does not, otherwise -EINVAL if the arguments are invalid.
+ */
+LIBPLDM_CC_NONNULL
+static int pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record *record,
+ uint16_t rsi)
+{
+ uint16_t record_fru_rsi = 0;
+ uint8_t *skip_data = NULL;
+ uint8_t skip_data_size = 0;
+ struct pldm_msgbuf _dst;
+ struct pldm_msgbuf *dst = &_dst;
+ int rc = 0;
+
+ rc = pldm_msgbuf_init_errno(dst, PDR_FRU_RECORD_SET_MIN_SIZE,
+ record->data, record->size);
+ if (rc) {
+ return rc;
+ }
+ skip_data_size = sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t);
+ pldm_msgbuf_span_required(dst, skip_data_size, (void **)&skip_data);
+ pldm_msgbuf_extract(dst, record_fru_rsi);
+
+ rc = pldm_msgbuf_destroy(dst);
+ if (rc) {
+ return rc;
+ }
+ return record_fru_rsi == rsi;
+}
+
+/* API to remove PLDM PDR record from a PLDM PDR repository
+ */
+LIBPLDM_CC_NONNULL_ARGS(1, 2)
+static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record,
+ pldm_pdr_record *prev)
+{
+ if (!is_prev_record_present(repo, record)) {
+ return -EINVAL;
+ }
+
+ assert(repo->size >= record->size);
+ if (repo->size < record->size) {
+ return -EOVERFLOW;
+ }
+
+ if (repo->first == record) {
+ repo->first = record->next;
+ } else {
+ if (prev != NULL) {
+ prev->next = record->next;
+ }
+ }
+
+ if (repo->last == record) {
+ repo->last = prev;
+ if (prev != NULL) {
+ prev->next = NULL;
+ }
+ }
+ repo->record_count -= 1;
+ repo->size -= record->size;
+ free(record->data);
+ free(record);
+
+ return 0;
+}
+
+LIBPLDM_ABI_TESTING
+int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi,
+ bool is_remote,
+ uint32_t *record_handle)
+{
+ pldm_pdr_record *record;
+ pldm_pdr_record *prev = NULL;
+ size_t skip_data_size = sizeof(uint32_t) + sizeof(uint8_t);
+ uint8_t hdr_type = 0;
+ int rc = 0;
+ int match;
+
+ if (!repo || !record_handle) {
+ return -EINVAL;
+ }
+ record = repo->first;
+
+ while (record != NULL) {
+ struct pldm_msgbuf _buf;
+ struct pldm_msgbuf *buf = &_buf;
+ rc = pldm_msgbuf_init_errno(buf, PDR_FRU_RECORD_SET_MIN_SIZE,
+ record->data, record->size);
+ if (rc) {
+ return rc;
+ }
+ pldm_msgbuf_span_required(buf, skip_data_size, NULL);
+ if ((rc = pldm_msgbuf_extract(buf, hdr_type))) {
+ return rc;
+ }
+ if (record->is_remote != is_remote ||
+ hdr_type != PLDM_PDR_FRU_RECORD_SET) {
+ goto cleanup;
+ }
+ match = pldm_pdr_record_matches_fru_rsi(record, fru_rsi);
+ if (match < 0) {
+ return match;
+ }
+ if (match) {
+ *record_handle = record->record_handle;
+ prev = pldm_pdr_get_prev_record(repo, record);
+ return pldm_pdr_remove_record(repo, record, prev);
+ }
+ cleanup:
+ rc = pldm_msgbuf_destroy(buf);
+ if (rc) {
+ return rc;
+ }
+ record = record->next;
+ }
+ return rc;
+}
diff --git a/tests/dsp/pdr.cpp b/tests/dsp/pdr.cpp
index e6a43f0..726bdfb 100644
--- a/tests/dsp/pdr.cpp
+++ b/tests/dsp/pdr.cpp
@@ -2060,3 +2060,70 @@
pldm_entity_association_tree_destroy(tree);
}
#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PDRUpdate, testRemoveFruRecord)
+{
+ auto repo = pldm_pdr_init();
+
+ uint32_t record_handle = 1;
+ int rc = pldm_pdr_add_fru_record_set(repo, 1, 1, 1, 0, 100, &record_handle);
+ EXPECT_EQ(rc, 0);
+ record_handle = 2;
+ rc = pldm_pdr_add_fru_record_set(repo, 1, 2, 1, 1, 100, &record_handle);
+ EXPECT_EQ(rc, 0);
+ record_handle = 3;
+ rc = pldm_pdr_add_fru_record_set(repo, 1, 3, 1, 2, 100, &record_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(pldm_pdr_get_record_count(repo), 3);
+
+ uint32_t removed_record_handle{};
+ rc = pldm_pdr_remove_fru_record_set_by_rsi(repo, 2, false,
+ &removed_record_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(removed_record_handle, 2);
+ EXPECT_EQ(pldm_pdr_get_record_count(repo), 2);
+
+ uint16_t terminusHdl{};
+ uint16_t entityType{};
+ uint16_t entityInstanceNum{};
+ uint16_t containerId{};
+ auto record = pldm_pdr_fru_record_set_find_by_rsi(
+ repo, 1, &terminusHdl, &entityType, &entityInstanceNum, &containerId);
+ EXPECT_NE(record, nullptr);
+ record_handle = pldm_pdr_get_record_handle(repo, record);
+ EXPECT_EQ(record_handle, 1);
+
+ record = pldm_pdr_fru_record_set_find_by_rsi(
+ repo, 3, &terminusHdl, &entityType, &entityInstanceNum, &containerId);
+ EXPECT_NE(record, nullptr);
+ record_handle = pldm_pdr_get_record_handle(repo, record);
+ EXPECT_EQ(record_handle, 3);
+
+ record = pldm_pdr_fru_record_set_find_by_rsi(
+ repo, 2, &terminusHdl, &entityType, &entityInstanceNum, &containerId);
+ EXPECT_EQ(record, nullptr);
+
+ rc = pldm_pdr_remove_fru_record_set_by_rsi(repo, 1, false,
+ &removed_record_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(removed_record_handle, 1);
+
+ // remove the same record again
+ removed_record_handle = 5;
+ rc = pldm_pdr_remove_fru_record_set_by_rsi(repo, 1, false,
+ &removed_record_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_NE(removed_record_handle, 1);
+ EXPECT_EQ(removed_record_handle, 5);
+
+ rc = pldm_pdr_remove_fru_record_set_by_rsi(repo, 3, false,
+ &removed_record_handle);
+ EXPECT_EQ(rc, 0);
+ EXPECT_EQ(removed_record_handle, 3);
+
+ EXPECT_EQ(pldm_pdr_get_record_count(repo), 0);
+
+ pldm_pdr_destroy(repo);
+}
+#endif