blob: 7ce2f7a32b5a8cc144cd4e2d39e10c6925454409 [file]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "MctpMockTestBase.hpp"
#include "MessagePackUnpackUtils.hpp"
#include "MockMctpRequester.hpp"
#include "NvidiaGpuMctpVdm.hpp"
#include "NvidiaGpuMemoryClockFrequency.hpp"
#include "OcpMctpVdm.hpp"
#include "TestUtils.hpp"
#include <sdbusplus/asio/object_server.hpp>
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <system_error>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace
{
const std::string dramIface = "xyz.openbmc_project.Inventory.Item.Dimm";
std::vector<uint8_t> buildClockFreqResponse(uint32_t mhz)
{
return test_utils::buildPlatformEnvSuccessResponse(
gpu::PlatformEnvironmentalCommands::GET_CURRENT_CLOCK_FREQUENCY, mhz);
}
std::vector<uint8_t> buildErrorResponse()
{
return test_utils::buildPlatformEnvErrorResponse(
gpu::PlatformEnvironmentalCommands::GET_CURRENT_CLOCK_FREQUENCY,
static_cast<uint8_t>(
ocp::accelerator_management::CompletionCode::ERROR),
0x1234);
}
class NvidiaGpuMemoryClockFrequencyTest : public MctpMockTestBase
{
protected:
static std::shared_ptr<sdbusplus::asio::dbus_interface> createDramInterface(
const std::string& name)
{
const std::string path =
"/xyz/openbmc_project/inventory/" + name + "_DRAM_0";
auto iface = objects().add_interface(path, dramIface);
iface->register_property("MemoryConfiguredSpeedInMhz", uint16_t{0});
iface->initialize();
return iface;
}
static std::shared_ptr<NvidiaGpuMemoryClockFrequency> createMemFreq(
const std::string& name = "GPU_MEM",
uint8_t eid = test_utils::defaultEid)
{
auto iface = createDramInterface(name);
return std::make_shared<NvidiaGpuMemoryClockFrequency>(
requester(), name, eid, iface);
}
static std::string dramPath(const std::string& name)
{
return "/xyz/openbmc_project/inventory/" + name + "_DRAM_0";
}
};
TEST_F(NvidiaGpuMemoryClockFrequencyTest, ConstructorDoesNotCrash)
{
auto metric = createMemFreq("mem_ctor");
ASSERT_NE(metric, nullptr);
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateSuccessSetsMemorySpeed)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith({}, buildClockFreqResponse(2619)));
auto metric = createMemFreq("mem_succ");
metric->update();
EXPECT_EQ(getProperty<uint16_t>(dramPath("mem_succ"), dramIface,
"MemoryConfiguredSpeedInMhz"),
2619);
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateTruncatesTo16Bits)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith({}, buildClockFreqResponse(65537)));
auto metric = createMemFreq("mem_trunc");
metric->update();
EXPECT_EQ(getProperty<uint16_t>(dramPath("mem_trunc"), dramIface,
"MemoryConfiguredSpeedInMhz"),
1);
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateSendsRequest)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.Times(testing::AtLeast(1))
.WillRepeatedly(mock_mctp::respondWith({}, {}));
auto metric = createMemFreq("mem_sends");
metric->update();
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateRequestContainsCorrectEid)
{
constexpr uint8_t testEid = 42;
EXPECT_CALL(mctpMock, sendRecvMsg(testEid, testing::_, testing::_))
.WillOnce(mock_mctp::respondWith({}, {}));
auto metric = createMemFreq("mem_eid", testEid);
metric->update();
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateVerifiesRequestEncoding)
{
// Copy the request bytes before completing the call: the reqMsg span is
// a view into caller-owned memory, valid only during the call.
std::vector<uint8_t> lastRequest;
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce([&](uint8_t /*eid*/, std::span<const uint8_t> reqMsg,
auto callback) {
lastRequest.assign(reqMsg.begin(), reqMsg.end());
callback(std::error_code{}, std::span<const uint8_t>{});
});
auto metric = createMemFreq("mem_enc");
metric->update();
ASSERT_FALSE(lastRequest.empty());
UnpackBuffer unpack(lastRequest);
ocp::accelerator_management::MessageType ocpMsgType{};
uint8_t instanceId = 0;
uint8_t msgType = 0;
EXPECT_EQ(ocp::accelerator_management::unpackHeader(
unpack, gpu::nvidiaPciVendorId, ocpMsgType, instanceId,
msgType),
0);
uint8_t command = 0;
uint8_t dataSize = 0;
uint8_t clockType = 0;
unpack.unpack(command);
unpack.unpack(dataSize);
unpack.unpack(clockType);
EXPECT_EQ(
command,
static_cast<uint8_t>(
gpu::PlatformEnvironmentalCommands::GET_CURRENT_CLOCK_FREQUENCY));
EXPECT_EQ(clockType, static_cast<uint8_t>(gpu::ClockType::MEMORY_CLOCK));
EXPECT_EQ(unpack.getError(), 0);
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateDecodeErrorNoCrash)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith({}, buildErrorResponse()));
auto metric = createMemFreq("mem_dec_err");
EXPECT_NO_THROW(metric->update());
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateEmptyBufferNoCrash)
{
EXPECT_CALL(mctpMock, sendRecvMsg).WillOnce(mock_mctp::respondWith({}, {}));
auto metric = createMemFreq("mem_empty");
EXPECT_NO_THROW(metric->update());
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateTinyBufferNoCrash)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith({}, {0x00, 0x01}));
auto metric = createMemFreq("mem_tiny");
EXPECT_NO_THROW(metric->update());
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest, UpdateMctpTransportError)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith(
std::make_error_code(std::errc::timed_out), {}));
auto metric = createMemFreq("mem_mctp_err_val");
metric->update();
// A transport error must leave the speed at its default (0).
EXPECT_EQ(getProperty<uint16_t>(dramPath("mem_mctp_err_val"), dramIface,
"MemoryConfiguredSpeedInMhz"),
0);
}
TEST_F(NvidiaGpuMemoryClockFrequencyTest,
UpdateSuccessThenErrorKeepsPreviousValue)
{
EXPECT_CALL(mctpMock, sendRecvMsg)
.WillOnce(mock_mctp::respondWith({}, buildClockFreqResponse(2619)))
.WillOnce(mock_mctp::respondWith({}, buildErrorResponse()));
auto metric = createMemFreq("mem_keep");
metric->update();
EXPECT_EQ(getProperty<uint16_t>(dramPath("mem_keep"), dramIface,
"MemoryConfiguredSpeedInMhz"),
2619);
// A subsequent error response must not overwrite the last good value.
metric->update();
EXPECT_EQ(getProperty<uint16_t>(dramPath("mem_keep"), dramIface,
"MemoryConfiguredSpeedInMhz"),
2619);
}
} // namespace