phosphor-objmgr: remove pending associations by service If Service A has an association on Object X with Object Y (which comes from Service C), and Service B also has an association definition for Object X, there is a scenario where the X-Y association will not be created, even though both objects are present in the D-Bus tree. This change matches with the service while removing the pending association for an object. Only the pending association introduced by the service which removes the object/interface will be erased. The change is accepted in Upstream. Tested: Unit Tested. Also tested in a machine and checked all the associations were created correctly following the above steps. Fusion-Link: platform5: https://fusion2.corp.google.com/741e9e05-9d22-33b3-ac0f-17610fe1ddfd platform15: https://fusion2.corp.google.com/1c47d1b1-4b57-3df4-a172-acab8f30eb99 platform18: https://fusion2.corp.google.com/755b43f1-f9e8-32a5-880c-2d57bb0ad784 platform17: https://fusion2.corp.google.com/63ef3f9b-ad59-3528-8c54-4a986f05c649 platform11+ext: https://fusion2.corp.google.com/2fba6019-1d63-35b3-bb09-535621a27da4 platform11: https://fusion2.corp.google.com/86d39ef0-61aa-38bf-a34c-781793592b2b Platforms-Affected: All Google-Bug-Id: 488925115 Change-Id: I79bc7cfcf782a6aeee68370a3463ca84c124995e Signed-off-by: Munawar Hussain <munawarhussain@google.com> (cherry picked from commit 74ace72c37ecdbcfc11fd216754f08bef490fa9a)
diff --git a/recipes-phosphor/dbus/phosphor-objmgr/0001-remove-pending-associations-by-owner.patch b/recipes-phosphor/dbus/phosphor-objmgr/0001-remove-pending-associations-by-owner.patch new file mode 100644 index 0000000..8364b3b --- /dev/null +++ b/recipes-phosphor/dbus/phosphor-objmgr/0001-remove-pending-associations-by-owner.patch
@@ -0,0 +1,146 @@ +From fc757250682036146d706aeae1e18d4de4bd4813 Mon Sep 17 00:00:00 2001 +From: Munawar Hussain <munawarhussain@google.com> +Date: Mon, 9 Mar 2026 09:25:10 +0000 +Subject: [PATCH] remove pending associations by owner + +Changes: +Match with the service while removing the pending association for an +object. Only the pending association introduced by the service which +removes the object/interface will be erased. + +If Service A has an association on Object X with Object Y (which comes +from Service C), and Service B also has an association definition for +Object X, there is a scenario where the X-Y association will not be +created, even though both objects are present in the D-Bus tree. + +Steps to Reproduce: +1. Service A starts and creates Object X. It creates a pending +association Y-X. +2. Service B starts, registers properties for Object X, and creates +other associations. +3. Service B drops Object X and its associated definitions. As a +result, all pending associations for Object X are wiped out (including +X-Y which is from A). +4. Service B creates Object X again along with other associations +related to Service B (but not the ones from Service A). +5. Service C finally starts and creates Object Y, but no pending +association is processed to complete the X-Y link. + +Tested: +Unit tested. Also testing on a machine with the above steps and all +the associations were still created. + +Patch Tracking Bug: b/491287006 +Upstream info / review: https://gerrit.openbmc.org/c/openbmc/phosphor-objmgr/+/88131 +Upstream-Status: Accepted +Justification: Bugfix + - b/488925115 + +Google-Bug-Id: 488925115 +Signed-off-by: Munawar Hussain <munawarhussain@google.com> +--- + src/associations.cpp | 6 ++++-- + src/associations.hpp | 2 ++ + src/test/associations.cpp | 29 +++++++++++++++++++++++++++-- + 3 files changed, 33 insertions(+), 4 deletions(-) + +diff --git a/src/associations.cpp b/src/associations.cpp +index db911f9..1eceddd 100644 +--- a/src/associations.cpp ++++ b/src/associations.cpp +@@ -129,7 +129,7 @@ void removeAssociation(boost::asio::io_context& io, + + // If we were still waiting on the other side of this association to + // show up, cancel that wait. +- removeFromPendingAssociations(sourcePath, assocMaps); ++ removeFromPendingAssociations(sourcePath, owner, assocMaps); + } + + void removeAssociationEndpoints( +@@ -345,6 +345,7 @@ void addPendingAssociation(const std::string& objectPath, + } + + void removeFromPendingAssociations(const std::string& endpointPath, ++ const std::string& owner, + AssociationMaps& assocMaps) + { + auto assoc = assocMaps.pending.begin(); +@@ -354,7 +355,8 @@ void removeFromPendingAssociations(const std::string& endpointPath, + while (endpoint != assoc->second.end()) + { + auto& e = std::get<assocPos>(*endpoint); +- if (std::get<reversePathPos>(e) == endpointPath) ++ if (std::get<reversePathPos>(e) == endpointPath && ++ std::get<ownerPos>(*endpoint) == owner) + { + endpoint = assoc->second.erase(endpoint); + continue; +diff --git a/src/associations.hpp b/src/associations.hpp +index 1d7b778..d9bdbb1 100644 +--- a/src/associations.hpp ++++ b/src/associations.hpp +@@ -126,9 +126,11 @@ void addPendingAssociation(const std::string& objectPath, + * If the last endpoint is removed, removes the whole entry + * + * @param[in] endpointPath - the endpoint path to remove ++ * @param[in] owner - The service name that owns the association + * @param[in,out] assocMaps - The association maps + */ + void removeFromPendingAssociations(const std::string& endpointPath, ++ const std::string& owner, + AssociationMaps& assocMaps); + + /** @brief Adds a single association D-Bus object (<path>/<type>) +diff --git a/src/test/associations.cpp b/src/test/associations.cpp +index fe0a826..cfd7de9 100644 +--- a/src/test/associations.cpp ++++ b/src/test/associations.cpp +@@ -433,7 +433,8 @@ TEST_F(TestAssociations, testRemoveFromPendingAssociations) + + EXPECT_EQ(assocMaps.pending.size(), 1); + +- removeFromPendingAssociations("some/other/endpoint", assocMaps); ++ removeFromPendingAssociations("some/other/endpoint", defaultDbusSvc, ++ assocMaps); + + // Still 1 pending entry, but down to 1 endpoint + EXPECT_EQ(assocMaps.pending.size(), 1); +@@ -444,10 +445,34 @@ TEST_F(TestAssociations, testRemoveFromPendingAssociations) + EXPECT_EQ(endpoints.size(), 1); + + // Now nothing pending +- removeFromPendingAssociations(defaultEndpoint, assocMaps); ++ removeFromPendingAssociations(defaultEndpoint, defaultDbusSvc, assocMaps); + EXPECT_EQ(assocMaps.pending.size(), 0); + } + ++// Test removing pending associations by multiple owners ++TEST_F(TestAssociations, testRemoveFromPendingAssociationsMultipleOwners) ++{ ++ AssociationMaps assocMaps; ++ ++ addPendingAssociation(defaultSourcePath, "inventory", defaultEndpoint, ++ "error", "serviceA", assocMaps); ++ ++ addPendingAssociation(defaultSourcePath, "inventory", defaultEndpoint, ++ "error", "serviceB", assocMaps); ++ ++ EXPECT_EQ(assocMaps.pending.size(), 1); ++ ++ removeFromPendingAssociations(defaultEndpoint, "serviceB", assocMaps); ++ ++ EXPECT_EQ(assocMaps.pending.size(), 1); ++ ++ auto assoc = assocMaps.pending.find(defaultSourcePath); ++ EXPECT_NE(assoc, assocMaps.pending.end()); ++ auto& endpoints = assoc->second; ++ EXPECT_EQ(endpoints.size(), 1); ++ EXPECT_EQ(std::get<ownerPos>(endpoints[0]), "serviceA"); ++} ++ + // Test moving a pending association to a real one + TEST_F(TestAssociations, checkIfPending) + { +-- +2.53.0.473.g4a7958ca14-goog +
diff --git a/recipes-phosphor/dbus/phosphor-objmgr_%.bbappend b/recipes-phosphor/dbus/phosphor-objmgr_%.bbappend index 47840e3..a7daf8d 100644 --- a/recipes-phosphor/dbus/phosphor-objmgr_%.bbappend +++ b/recipes-phosphor/dbus/phosphor-objmgr_%.bbappend
@@ -2,4 +2,5 @@ SRC_URI:append:gbmc = "\ file://0001-associations-fix-leak-in-assocMaps.ifaces.patch \ + file://0001-remove-pending-associations-by-owner.patch \ "