mctp: filter endpoints by message type Add a filter to MctpUtil to only care about endpoints that support a specific message type (e.g., type 4 for NVMe MI). We assume the mctpd service name is au.com.codeconstruct.MCTP1 and use it directly to query the supported message types of an endpoint. A new unit test is added to validate that unsupported endpoints are correctly filtered. Misc: - Define MctpMessageType enum for readable message type passing. - Update mock server to support D-Bus properties and custom message types. Tested: All 10 tests passed in Docker environment. Change-Id: I452cab692640d55efa71c2ee81058c7a51fb33f3 Google-Bug-Id: 490106522 Signed-off-by: Hao Jiang <jianghao@google.com>
diff --git a/src/MctpUtil.cpp b/src/MctpUtil.cpp index 8d1e82c..b02f844 100644 --- a/src/MctpUtil.cpp +++ b/src/MctpUtil.cpp
@@ -2,21 +2,18 @@ #include "absl/strings/match.h" -#include "Utils.hpp" -#include "VariantVisitors.hpp" - #include <boost/asio/steady_timer.hpp> #include <boost/container/flat_map.hpp> #include <phosphor-logging/lg2.hpp> #include <sdbusplus/bus/match.hpp> #include <chrono> +#include <cstdint> #include <filesystem> -#include <string> std::map<std::string, SensorData> mctpEndpointConfigMap; -enum class State : uint8_t +enum class State : std::uint8_t { None, Add, @@ -33,6 +30,7 @@ }; static std::map<std::string, EndpointState> endpointStates; +static uint8_t filterMsgType = 0; static std::unique_ptr<sdbusplus::bus::match_t> associationMatch = nullptr; static std::unique_ptr<sdbusplus::bus::match_t> associationRemoveMatch = @@ -41,7 +39,7 @@ static void performEmConfigQuery( const std::shared_ptr<sdbusplus::asio::connection>& conn, const std::string& endpointPath, const std::string& emConfigPath, - bool bypassCooldown = false) + bool bypassCooldown = false, std::function<void()> cleanup = nullptr) { auto& state = endpointStates[endpointPath]; auto now = std::chrono::steady_clock::now(); @@ -63,77 +61,159 @@ timer->expires_after(delay); timer->async_wait([conn, endpointPath, emConfigPath, - timer](const boost::system::error_code& ec) { + cleanup = std::move(cleanup), + timer](const boost::system::error_code& ec) mutable { if (ec) { return; } - performEmConfigQuery(conn, endpointPath, emConfigPath); + performEmConfigQuery(conn, endpointPath, emConfigPath, false, + std::move(cleanup)); }); return; } - + // Step 1: Query SupportedMessageTypes directly from mctpd conn->async_method_call( - [conn, endpointPath, emConfigPath](const boost::system::error_code& ec, - const ManagedObjectType& objects) { + [conn, endpointPath, emConfigPath, cleanup = std::move(cleanup)]( + const boost::system::error_code& ec, + const std::variant<std::vector<uint8_t>>& value) mutable { auto& state = endpointStates[endpointPath]; - state.querying = false; - - state.lastQueryTime = std::chrono::steady_clock::now(); // Update time! - - State lastState = state.state; - state.state = State::None; // Reset for next query - - if (lastState == State::Remove) + if (ec) { - lg2::info("Ignoring query reply for removed endpoint {ENDPOINT}", - "ENDPOINT", endpointPath); - auto it = mctpEndpointConfigMap.find(endpointPath); - if (it != mctpEndpointConfigMap.end()) + lg2::error( + "Failed to get SupportedMessageTypes for {PATH}: {ERROR}", + "PATH", endpointPath, "ERROR", ec.message()); + if (cleanup) { - mctpEndpointConfigMap.erase(it); + cleanup(); + } + else + { + state.querying = false; } return; } - if (ec) + const auto* typesPtr = std::get_if<std::vector<uint8_t>>(&value); + if (!typesPtr) { - lg2::error("Failed to get managed objects for {PATH}: {ERROR}", - "PATH", emConfigPath, "ERROR", ec.message()); + lg2::error("Invalid SupportedMessageTypes type for {PATH}", "PATH", + endpointPath); + if (cleanup) + { + cleanup(); + } + else + { + state.querying = false; + } return; } - auto objIt = - objects.find(sdbusplus::message::object_path(emConfigPath)); - - if (objIt == objects.end()) + // Check if it contains the requested message type + if (std::find(typesPtr->begin(), typesPtr->end(), filterMsgType) == + typesPtr->end()) { - lg2::warning("EM config path {PATH} not found in managed objects", - "PATH", emConfigPath); + lg2::info( + "Endpoint {PATH} does not support message type {TYPE}, ignoring", + "PATH", endpointPath, "TYPE", filterMsgType); + if (cleanup) + { + cleanup(); + } + else + { + state.querying = false; // Clear protection! + } return; } - if (!objIt->second.empty()) - { - lg2::info( - "Recorded MCTP endpoint {ENDPOINT} with config from {PATH}", - "ENDPOINT", endpointPath, "PATH", emConfigPath); - mctpEndpointConfigMap[endpointPath] = objIt->second; - lg2::info("DEBUG: Map populated, new size: {SIZE}", "SIZE", - mctpEndpointConfigMap.size()); - } + // Step 2: Proceed to EM query + conn->async_method_call( + [conn, endpointPath, emConfigPath, cleanup = std::move(cleanup)]( + const boost::system::error_code& ec, + const ManagedObjectType& objects) mutable { + auto& state = endpointStates[endpointPath]; + state.querying = false; + state.lastQueryTime = + std::chrono::steady_clock::now(); // Update time! - if (lastState == State::Add) - { - lg2::info( - "DEBUG: Triggering deferred query for endpoint {ENDPOINT}", - "ENDPOINT", endpointPath); - state.querying = true; - performEmConfigQuery(conn, endpointPath, emConfigPath); - } + State lastState = state.state; + state.state = State::None; // Reset for next query + + if (lastState == State::Remove) + { + lg2::info( + "Ignoring query reply for removed endpoint {ENDPOINT}", + "ENDPOINT", endpointPath); + auto it = mctpEndpointConfigMap.find(endpointPath); + if (it != mctpEndpointConfigMap.end()) + { + mctpEndpointConfigMap.erase(it); + } + if (cleanup) + { + cleanup(); + } + return; + } + + if (ec) + { + lg2::error("Failed to get managed objects for {PATH}: {ERROR}", + "PATH", emConfigPath, "ERROR", ec.message()); + if (cleanup) + { + cleanup(); + } + return; + } + + auto objIt = + objects.find(sdbusplus::message::object_path(emConfigPath)); + + if (objIt == objects.end()) + { + lg2::warning( + "EM config path {PATH} not found in managed objects", + "PATH", emConfigPath); + if (cleanup) + { + cleanup(); + } + return; + } + + if (!objIt->second.empty()) + { + lg2::info( + "Recorded MCTP endpoint {ENDPOINT} with config from {PATH}", + "ENDPOINT", endpointPath, "PATH", emConfigPath); + mctpEndpointConfigMap[endpointPath] = objIt->second; + lg2::info("DEBUG: Map populated, new size: {SIZE}", "SIZE", + mctpEndpointConfigMap.size()); + } + + if (lastState == State::Add) + { + lg2::info( + "DEBUG: Triggering deferred query for endpoint {ENDPOINT}", + "ENDPOINT", endpointPath); + performEmConfigQuery(conn, endpointPath, emConfigPath); + } + + if (cleanup) + { + cleanup(); + } + }, + "xyz.openbmc_project.EntityManager", + "/xyz/openbmc_project/inventory", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); }, - "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + "au.com.codeconstruct.MCTP1", endpointPath, + "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.MCTP.Endpoint", "SupportedMessageTypes"); } static void triggerDeferredQueries( @@ -165,8 +245,10 @@ } void setupMctpEndpointListener( - const std::shared_ptr<sdbusplus::asio::connection>& conn) + const std::shared_ptr<sdbusplus::asio::connection>& conn, + MctpMessageType msgType) { + filterMsgType = static_cast<uint8_t>(msgType); // Match 1: Listen for Associations from mctp-reactor const std::string associationMatchSpec = "type='signal',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded',arg0path='/au/com/codeconstruct/mctp1/'"; @@ -195,14 +277,14 @@ return; } - const auto* endpoints = + const auto* endpointsPtr = std::get_if<std::vector<std::string>>(&propIt->second); - if (!endpoints || endpoints->empty()) + if (!endpointsPtr) { return; } - std::string emConfigPath = endpoints->front(); + std::string emConfigPath = endpointsPtr->front(); std::string endpointPath = path.str; constexpr std::string_view suffix = "/configured_by"; @@ -215,14 +297,19 @@ auto& state = endpointStates[endpointPath]; state.emConfigPath = emConfigPath; - if (endpointStates["__global__"].querying || state.querying) - { - state.state = State::Add; - return; - } + bool wasQuerying = endpointStates["__global__"].querying || + state.querying; - state.querying = true; - performEmConfigQuery(conn, endpointPath, emConfigPath); + if (wasQuerying) + { + lg2::info("Deferring Add for endpoint {PATH}", "PATH", + endpointPath); + state.state = State::Add; + } + else + { + performEmConfigQuery(conn, endpointPath, emConfigPath); + } }); const std::string associationRemoveMatchSpec = @@ -313,7 +400,13 @@ [conn, barrier, endpointPath]( const boost::system::error_code& ec, const std::variant<std::vector<std::string>>& value) { - auto cleanup = [&]() { + std::weak_ptr<sdbusplus::asio::connection> weakConn = conn; + auto cleanup = [barrier, weakConn]() { + auto conn = weakConn.lock(); + if (!conn) + { + return; + } (*barrier)--; if (*barrier == 0) { @@ -344,10 +437,8 @@ auto& state = endpointStates[endpointPath]; state.emConfigPath = emConfigPath; - state.querying = true; - performEmConfigQuery(conn, endpointPath, emConfigPath); - - cleanup(); + performEmConfigQuery(conn, endpointPath, emConfigPath, true, + std::move(cleanup)); }, "xyz.openbmc_project.ObjectMapper", path, "org.freedesktop.DBus.Properties", "Get",
diff --git a/src/MctpUtil.hpp b/src/MctpUtil.hpp index 282e7e2..8f6d25c 100644 --- a/src/MctpUtil.hpp +++ b/src/MctpUtil.hpp
@@ -10,6 +10,14 @@ using BusInfo = std::map<std::string, std::string>; +enum class MctpMessageType : uint8_t +{ + CONTROL = 0, + PLDM = 2, + NVME_MI = 4, + SPDM = 5 +}; + // Global maps (declared extern) #include "Utils.hpp" @@ -17,7 +25,8 @@ extern std::map<std::string, SensorData> mctpEndpointConfigMap; void setupMctpEndpointListener( - const std::shared_ptr<sdbusplus::asio::connection>& conn); + const std::shared_ptr<sdbusplus::asio::connection>& conn, + MctpMessageType msgType); void cleanupMctpEndpointListener(); BusInfo extractBusInfo(const SensorData& configData);
diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp index 584fd89..76a584c 100644 --- a/src/NVMeSensorMain.cpp +++ b/src/NVMeSensorMain.cpp
@@ -580,7 +580,7 @@ objectServer.add_manager("/xyz/openbmc_project/sensors"); objectServer.add_manager("/xyz/openbmc_project/inventory"); - setupMctpEndpointListener(systemBus); + setupMctpEndpointListener(systemBus, MctpMessageType::NVME_MI); boost::asio::post( io, [&]() { createNVMeSubsystems(io, objectServer, systemBus); });
diff --git a/tests/mock_mctpd.py b/tests/mock_mctpd.py index 1d99a03..65801e7 100644 --- a/tests/mock_mctpd.py +++ b/tests/mock_mctpd.py
@@ -29,22 +29,30 @@ class MockEndpoint(dbus.service.Object): - def __init__(self, bus, path, em_config_path): + def __init__(self, bus, path, em_config_path, msg_types=None): dbus.service.Object.__init__(self, bus, path) - self.associations = dbus.Array([ + self._associations = dbus.Array([ dbus.Struct(('configured_by', 'endpoint', em_config_path), signature='(sss)') ], signature='(sss)') + if msg_types is not None: + self._message_types = dbus.ByteArray(msg_types) + else: + self._message_types = dbus.ByteArray(b'\x02\x00\x04') @dbus.service.method('org.freedesktop.DBus.Properties', in_signature='ss', out_signature='v') def Get(self, interface_name, property_name): if interface_name == 'xyz.openbmc_project.Association.Definitions' and property_name == 'Associations': - return dbus.LowLevel.Variant(self.associations, 'a(sss)') + return self._associations + if interface_name == 'xyz.openbmc_project.MCTP.Endpoint' and property_name == 'SupportedMessageTypes': + return self._message_types raise dbus.exceptions.DBusException('org.freedesktop.DBus.Error.InvalidArgs') @dbus.service.method('org.freedesktop.DBus.Properties', in_signature='s', out_signature='a{sv}') def GetAll(self, interface_name): if interface_name == 'xyz.openbmc_project.Association.Definitions': - return {'Associations': dbus.LowLevel.Variant(self.associations, 'a(sss)')} + return {'Associations': self._associations} + if interface_name == 'xyz.openbmc_project.MCTP.Endpoint': + return {'SupportedMessageTypes': self._message_types} return {} @@ -85,9 +93,25 @@ self.InterfacesAdded(dbus.ObjectPath(endpoint_path), { 'xyz.openbmc_project.Association.Definitions': { - 'Associations': dbus.Array([ - dbus.Struct(('configured_by', 'endpoint', em_config_path), signature='(sss)') - ], signature='(sss)') + 'Associations': self.endpoints[endpoint_path]._associations + }, + 'xyz.openbmc_project.MCTP.Endpoint': { + 'SupportedMessageTypes': self.endpoints[endpoint_path]._message_types + } + }) + return "OK" + + @dbus.service.method('com.example.Control', in_signature='ssay') + def TriggerAddCustom(self, endpoint_path, em_config_path, msg_types): + if endpoint_path not in self.endpoints: + self.endpoints[endpoint_path] = MockEndpoint(self.bus, endpoint_path, em_config_path, msg_types) + + self.InterfacesAdded(dbus.ObjectPath(endpoint_path), { + 'xyz.openbmc_project.Association.Definitions': { + 'Associations': self.endpoints[endpoint_path]._associations + }, + 'xyz.openbmc_project.MCTP.Endpoint': { + 'SupportedMessageTypes': self.endpoints[endpoint_path]._message_types } }) return "OK" @@ -154,7 +178,7 @@ _em = MockEM(bus) # noqa: F841 _name_em = dbus.service.BusName("xyz.openbmc_project.EntityManager", bus) # noqa: F841 - _name_mctp = dbus.service.BusName("xyz.openbmc_project.Mctp", bus) # noqa: F841 + _name_mctp = dbus.service.BusName("au.com.codeconstruct.MCTP1", bus) # noqa: F841 print("Mock mctpd and EM running...") sys.stdout.flush() loop.run()
diff --git a/tests/test_MctpUtil.cpp b/tests/test_MctpUtil.cpp index e939779..a743d07 100644 --- a/tests/test_MctpUtil.cpp +++ b/tests/test_MctpUtil.cpp
@@ -135,7 +135,7 @@ "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; // 2.a setupMctpEndpointListener() first in the test. - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); // 2.b setup the busctl event listener to all O-M event. // We use a C++ match listener for all InterfacesAdded signals! @@ -154,7 +154,7 @@ // Trigger Add event via D-Bus! std::string addCmd = - "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" + + "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" + endpointPath + "\" \"" + emConfigPath + "\""; runCmd(addCmd); @@ -192,7 +192,7 @@ std::string emConfigPath = "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -216,7 +216,7 @@ // Trigger Add event std::string addCmd = - "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" + + "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" + endpointPath + "\" \"" + emConfigPath + "\""; runCmd(addCmd); @@ -229,7 +229,7 @@ // IMMEDIATELY trigger Remove event (while query is in progress!) std::string removeCmd = - "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" + + "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" + endpointPath + "\""; runCmd(removeCmd); @@ -263,7 +263,7 @@ std::string emConfigPath = "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -287,7 +287,7 @@ // Trigger Add event std::string addCmd = - "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" + + "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" + endpointPath + "\" \"" + emConfigPath + "\""; runCmd(addCmd); @@ -300,7 +300,7 @@ // IMMEDIATELY trigger Remove event std::string removeCmd = - "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" + + "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" + endpointPath + "\""; runCmd(removeCmd); @@ -339,7 +339,7 @@ "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; // Pre-populate association by triggering it BEFORE listener starts! - auto m = conn->new_method_call("xyz.openbmc_project.Mctp", + auto m = conn->new_method_call("au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd"); m.append(endpointPath, emConfigPath); @@ -359,7 +359,7 @@ ASSERT_TRUE(found); // Now setup listener (this starts priming!) - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -400,7 +400,7 @@ { std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n"; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd", endpointPath5, emConfigPath); // Wait for queries to complete (we expect 2!) @@ -442,7 +442,7 @@ "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; // Pre-populate association by triggering it BEFORE listener starts! - auto m = conn->new_method_call("xyz.openbmc_project.Mctp", + auto m = conn->new_method_call("au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd"); m.append(endpointPath, emConfigPath); @@ -462,7 +462,7 @@ ASSERT_TRUE(found); // Now setup listener (this starts priming!) - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -501,7 +501,7 @@ std::cout << "[TEST] TriggerRemove failed: " << ec.message() << "\n"; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerRemove", endpointPath); // Wait for queries to complete (we expect 1!) @@ -534,7 +534,7 @@ "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; // Pre-populate association by triggering it BEFORE listener starts! - auto m = conn->new_method_call("xyz.openbmc_project.Mctp", + auto m = conn->new_method_call("au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd"); m.append(endpointPath, emConfigPath); @@ -554,7 +554,7 @@ ASSERT_TRUE(found); // Now setup listener (this starts priming!) - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -593,7 +593,7 @@ std::cout << "[TEST] TriggerRemove failed: " << ec.message() << "\n"; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerRemove", endpointPath); std::cout << "[DEBUG] Triggering Add again (racing!)\n"; @@ -604,7 +604,7 @@ { std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n"; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd", endpointPath, emConfigPath); // Wait for queries to complete (we expect 2!) @@ -638,7 +638,7 @@ std::string emConfigPath = "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -648,7 +648,7 @@ auto queryStartedMatch = std::make_unique<sdbusplus::bus::match_t>( static_cast<sdbusplus::bus_t&>(*conn), queryStartedSpec, [&](sdbusplus::message_t&) { - std::cout << "[TEST] QueryStarted received\n"; + std::cout << "[TEST] QueryStarted received" << '\n'; queryStarted = true; }); @@ -657,7 +657,7 @@ auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>( static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec, [&](sdbusplus::message_t&) { - std::cout << "[TEST] QueryCompleted received\n"; + std::cout << "[TEST] QueryCompleted received" << '\n'; queryCompletedCount++; }); @@ -666,9 +666,9 @@ [](const boost::system::error_code& ec, const std::string&) { if (ec) { - std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n"; + std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd", endpointPath, emConfigPath); // Wait for FIRST query to complete! @@ -684,16 +684,16 @@ } ASSERT_EQ(queryCompletedCount, 1); - std::cout << "[DEBUG] Triggering Remove and Add (racing!)\n"; + std::cout << "[DEBUG] Triggering Remove and Add (racing!)" << '\n'; // Trigger Remove! conn->async_method_call( [](const boost::system::error_code& ec, const std::string&) { if (ec) { std::cout << "[TEST] TriggerRemove failed: " << ec.message() - << "\n"; + << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerRemove", endpointPath); // Trigger SECOND Add! (Immediately after!). @@ -701,9 +701,9 @@ [](const boost::system::error_code& ec, const std::string&) { if (ec) { - std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n"; + std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd", endpointPath, emConfigPath); // Wait for SECOND query to complete! @@ -742,7 +742,7 @@ "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; // Pre-populate association by triggering it BEFORE listener starts! - auto m = conn->new_method_call("xyz.openbmc_project.Mctp", + auto m = conn->new_method_call("au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd"); m.append(endpointPath, emConfigPath); @@ -762,7 +762,7 @@ ASSERT_TRUE(found); // Now setup listener (this starts priming!) - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -772,7 +772,7 @@ auto queryStartedMatch = std::make_unique<sdbusplus::bus::match_t>( static_cast<sdbusplus::bus_t&>(*conn), queryStartedSpec, [&](sdbusplus::message_t&) { - std::cout << "[TEST] QueryStarted received\n"; + std::cout << "[TEST] QueryStarted received" << '\n'; queryStarted = true; }); @@ -781,7 +781,7 @@ auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>( static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec, [&](sdbusplus::message_t&) { - std::cout << "[TEST] QueryCompleted received\n"; + std::cout << "[TEST] QueryCompleted received" << '\n'; queryCompletedCount++; }); @@ -792,16 +792,17 @@ usleep(10000); // 10ms } - std::cout << "[DEBUG] Triggering Remove and Add (racing with priming!)\n"; + std::cout << "[DEBUG] Triggering Remove and Add (racing with priming!)" + << '\n'; // Trigger Remove event! conn->async_method_call( [](const boost::system::error_code& ec, const std::string&) { if (ec) { std::cout << "[TEST] TriggerRemove failed: " << ec.message() - << "\n"; + << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerRemove", endpointPath); // Trigger Add event! @@ -809,9 +810,9 @@ [](const boost::system::error_code& ec, const std::string&) { if (ec) { - std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n"; + std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd", endpointPath, emConfigPath); // Wait for queries to complete! @@ -848,7 +849,7 @@ std::string emConfigPath = "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; - setupMctpEndpointListener(conn); + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); bool queryStarted = false; int queryCompletedCount = 0; @@ -858,7 +859,7 @@ auto queryStartedMatch = std::make_unique<sdbusplus::bus::match_t>( static_cast<sdbusplus::bus_t&>(*conn), queryStartedSpec, [&](sdbusplus::message_t&) { - std::cout << "[TEST] QueryStarted received\n"; + std::cout << "[TEST] QueryStarted received" << '\n'; queryStarted = true; }); @@ -867,7 +868,7 @@ auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>( static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec, [&](sdbusplus::message_t&) { - std::cout << "[TEST] QueryCompleted received\n"; + std::cout << "[TEST] QueryCompleted received" << '\n'; queryCompletedCount++; }); @@ -876,9 +877,9 @@ [](const boost::system::error_code& ec, const std::string&) { if (ec) { - std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n"; + std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd", endpointPath, emConfigPath); // Wait for FIRST query to complete! @@ -894,16 +895,16 @@ } ASSERT_EQ(queryCompletedCount, 1); - std::cout << "[DEBUG] Triggering Remove -> Add -> Remove\n"; + std::cout << "[DEBUG] Triggering Remove -> Add -> Remove" << '\n'; // Trigger Remove! conn->async_method_call( [](const boost::system::error_code& ec, const std::string&) { if (ec) { std::cout << "[TEST] TriggerRemove failed: " << ec.message() - << "\n"; + << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerRemove", endpointPath); // Trigger Add! @@ -911,9 +912,9 @@ [](const boost::system::error_code& ec, const std::string&) { if (ec) { - std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n"; + std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerAdd", endpointPath, emConfigPath); // Trigger Remove! @@ -922,9 +923,9 @@ if (ec) { std::cout << "[TEST] TriggerRemove failed: " << ec.message() - << "\n"; + << '\n'; } - }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1", + }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1", "com.example.Control", "TriggerRemove", endpointPath); // Wait for queries to complete! @@ -933,17 +934,75 @@ while (io.poll() > 0) {} // Drain all ready handlers! usleep(100000); // 100ms - if (queryCompletedCount >= 2) + } + + EXPECT_EQ(queryCompletedCount, 1); + + // Verify map is EMPTY! + auto it = mctpEndpointConfigMap.find(endpointPath); + EXPECT_EQ(it, mctpEndpointConfigMap.end()); +} + +// 10. Filter unsupported message types +TEST_F(MctpUtilTest, Filter_UnsupportedMessageType_IsIgnored) +{ + std::string endpointPathSupported = + "/au/com/codeconstruct/mctp1/networks/1/endpoints/test10_supported"; + std::string endpointPathUnsupported = + "/au/com/codeconstruct/mctp1/networks/1/endpoints/" + "test10_unsupported"; + std::string emConfigPath = + "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device"; + + setupMctpEndpointListener(conn, MctpMessageType::NVME_MI); + + int queryCompletedCount = 0; + const std::string queryCompletedSpec = + "type='signal',interface='com.example.Control',member='QueryCompleted'"; + auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>( + static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec, + [&](sdbusplus::message_t&) { queryCompletedCount++; }); + + // 1. Trigger Add for SUPPORTED endpoint (type 4 included) + std::vector<uint8_t> msgTypesSupported = {2, 0, 4}; + auto m1 = conn->new_method_call("au.com.codeconstruct.MCTP1", + "/au/com/codeconstruct/mctp1", + "com.example.Control", "TriggerAddCustom"); + m1.append(endpointPathSupported, emConfigPath, msgTypesSupported); + conn->call(m1); + + // 2. Trigger Add for UNSUPPORTED endpoint (no type 4) + std::vector<uint8_t> msgTypesUnsupported = {2}; + auto m2 = conn->new_method_call("au.com.codeconstruct.MCTP1", + "/au/com/codeconstruct/mctp1", + "com.example.Control", "TriggerAddCustom"); + m2.append(endpointPathUnsupported, emConfigPath, msgTypesUnsupported); + conn->call(m2); + + // Wait for queries to complete (we expect exactly 1 to complete!) + for (int i = 0; i < 50; i++) + { + while (io.poll() > 0) + {} // Drain all ready handlers! + usleep(100000); // 100ms + if (queryCompletedCount >= 1) { break; } } - EXPECT_EQ(queryCompletedCount, 2); + EXPECT_EQ(queryCompletedCount, 1); - // Verify map is EMPTY! - auto it = mctpEndpointConfigMap.find(endpointPath); - EXPECT_EQ(it, mctpEndpointConfigMap.end()); + // Verify map size is 1 + EXPECT_EQ(mctpEndpointConfigMap.size(), 1); + + // Verify supported endpoint is present + auto itSupported = mctpEndpointConfigMap.find(endpointPathSupported); + EXPECT_NE(itSupported, mctpEndpointConfigMap.end()); + + // Verify unsupported endpoint is NOT present + auto itUnsupported = mctpEndpointConfigMap.find(endpointPathUnsupported); + EXPECT_EQ(itUnsupported, mctpEndpointConfigMap.end()); } int main(int argc, char** argv)