blob: 4b6d73f9f107b8e0f8fa5b5e19bc8351728ad065 [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaGpuMctpVdm.hpp"
#include "NvidiaLongRunningHandler.hpp"
#include "OcpMctpVdm.hpp"
#include "TestUtils.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/system/error_code.hpp>
#include <cerrno>
#include <chrono>
#include <cstdint>
#include <memory>
#include <span>
#include <vector>
#include <gtest/gtest.h>
namespace
{
constexpr uint8_t msgTypePlatformEnv =
static_cast<uint8_t>(gpu::MessageType::PLATFORM_ENVIRONMENTAL);
constexpr uint8_t commandCode = static_cast<uint8_t>(
gpu::PlatformEnvironmentalCommands::GET_CURRENT_UTILIZATION);
class NvidiaLongRunningHandlerTest : public ::testing::Test
{
protected:
// Shared across the fixture's tests so the binary creates a single
// io_uring instance. A per-test io_context exhausts the io_uring / locked
// memory budget in constrained CI containers (io_uring_queue_init ENOMEM).
inline static boost::asio::io_context io;
std::shared_ptr<NvidiaLongRunningResponseHandler> handler =
std::make_shared<NvidiaLongRunningResponseHandler>(io);
};
TEST_F(NvidiaLongRunningHandlerTest, RegisterReturnsZeroFirstTime)
{
const int rc = handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[](boost::system::error_code,
ocp::accelerator_management::CompletionCode, uint16_t,
std::span<const uint8_t>) {});
EXPECT_EQ(rc, 0);
}
TEST_F(NvidiaLongRunningHandlerTest, RegisterDuplicateReturnsEEXIST)
{
EXPECT_EQ(handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[](boost::system::error_code,
ocp::accelerator_management::CompletionCode, uint16_t,
std::span<const uint8_t>) {}),
0);
EXPECT_EQ(handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[](boost::system::error_code,
ocp::accelerator_management::CompletionCode, uint16_t,
std::span<const uint8_t>) {}),
EEXIST);
}
TEST_F(NvidiaLongRunningHandlerTest, HandlerWrongEventClassIgnored)
{
bool called = false;
handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[&called](boost::system::error_code,
ocp::accelerator_management::CompletionCode, uint16_t,
std::span<const uint8_t>) { called = true; });
const auto data =
test_utils::buildLongRunningEventData(5, 0, 0, {0x01, 0x02});
handler->handler(
test_utils::makeEventInfo(5, msgTypePlatformEnv, commandCode), data);
EXPECT_FALSE(called);
}
TEST_F(NvidiaLongRunningHandlerTest, HandlerDispatchesToRegisteredHandler)
{
bool called = false;
ocp::accelerator_management::CompletionCode capturedCc{
ocp::accelerator_management::CompletionCode::ERROR};
std::vector<uint8_t> capturedData;
handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[&](boost::system::error_code ec,
ocp::accelerator_management::CompletionCode cc, uint16_t,
std::span<const uint8_t> responseData) {
called = true;
EXPECT_FALSE(ec);
capturedCc = cc;
capturedData.assign(responseData.begin(), responseData.end());
});
const std::vector<uint8_t> payload{0xAA, 0xBB, 0xCC};
const auto data = test_utils::buildLongRunningEventData(
5,
static_cast<uint8_t>(
ocp::accelerator_management::CompletionCode::SUCCESS),
0, payload);
handler->handler(
test_utils::makeEventInfo(test_utils::longRunningResponseEventClass,
msgTypePlatformEnv, commandCode),
data);
EXPECT_TRUE(called);
EXPECT_EQ(capturedCc, ocp::accelerator_management::CompletionCode::SUCCESS);
EXPECT_EQ(capturedData, payload);
}
TEST_F(NvidiaLongRunningHandlerTest, HandlerNoMatchingKeyIgnored)
{
bool called = false;
handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[&called](boost::system::error_code,
ocp::accelerator_management::CompletionCode, uint16_t,
std::span<const uint8_t>) { called = true; });
// Event for a different command code -> no matching handler.
const uint8_t otherCommand = commandCode + 1;
const auto data = test_utils::buildLongRunningEventData(5, 0, 0, {0x01});
handler->handler(
test_utils::makeEventInfo(test_utils::longRunningResponseEventClass,
msgTypePlatformEnv, otherCommand),
data);
EXPECT_FALSE(called);
}
TEST_F(NvidiaLongRunningHandlerTest, HandlerDecodeErrorTooShort)
{
bool called = false;
handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[&called](boost::system::error_code,
ocp::accelerator_management::CompletionCode, uint16_t,
std::span<const uint8_t>) { called = true; });
// Fewer than longRunningResponseEventSize (4) bytes -> decode fails.
const std::vector<uint8_t> data{0x05, 0x00};
handler->handler(
test_utils::makeEventInfo(test_utils::longRunningResponseEventClass,
msgTypePlatformEnv, commandCode),
data);
EXPECT_FALSE(called);
}
TEST_F(NvidiaLongRunningHandlerTest, HandlerConsumesHandlerOnce)
{
int callCount = 0;
handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[&callCount](boost::system::error_code,
ocp::accelerator_management::CompletionCode, uint16_t,
std::span<const uint8_t>) { callCount++; });
const auto data = test_utils::buildLongRunningEventData(5, 0, 0, {0x01});
const auto info =
test_utils::makeEventInfo(test_utils::longRunningResponseEventClass,
msgTypePlatformEnv, commandCode);
handler->handler(info, data);
handler->handler(info, data); // entry already erased
EXPECT_EQ(callCount, 1);
}
TEST_F(NvidiaLongRunningHandlerTest, HandlerTimesOutWhenNoEventArrives)
{
int callCount = 0;
boost::system::error_code capturedEc;
ocp::accelerator_management::CompletionCode capturedCc{
ocp::accelerator_management::CompletionCode::SUCCESS};
handler->registerResponseHandler(
gpu::MessageType::PLATFORM_ENVIRONMENTAL, commandCode, 5,
[&](boost::system::error_code ec,
ocp::accelerator_management::CompletionCode cc, uint16_t,
std::span<const uint8_t> responseData) {
callCount++;
capturedEc = ec;
capturedCc = cc;
EXPECT_TRUE(responseData.empty());
});
// No response event is injected, so the registration's own timer must
// fire. The wait is bounded well above the handler's 2s timeout;
// run_for() returns as soon as the timer completes and no work is left.
io.restart();
io.run_for(std::chrono::seconds(10));
ASSERT_EQ(callCount, 1);
EXPECT_EQ(capturedEc, boost::system::errc::timed_out);
EXPECT_EQ(capturedCc, ocp::accelerator_management::CompletionCode::ERROR);
// Timing out erased the entry, so a late event finds no handler.
handler->handler(
test_utils::makeEventInfo(test_utils::longRunningResponseEventClass,
msgTypePlatformEnv, commandCode),
test_utils::buildLongRunningEventData(5, 0, 0, {0x01}));
EXPECT_EQ(callCount, 1);
}
} // namespace