feature: Add support for multi-fan table on windows

Related-To: NEO-16563

Signed-off-by: shubham kumar <shubham.kumar@intel.com>
This commit is contained in:
shubham kumar
2025-11-17 09:23:05 +00:00
committed by Compute-Runtime-Automation
parent 7660b29bbb
commit d8bb0a0410
14 changed files with 1282 additions and 253 deletions

View File

@@ -8,15 +8,23 @@
#include "level_zero/tools/source/sysman/fan/fan.h"
#include "level_zero/tools/source/sysman/fan/fan_imp.h"
#include "level_zero/tools/source/sysman/fan/os_fan.h"
namespace L0 {
FanHandleContext::~FanHandleContext() = default;
void FanHandleContext::createHandle(uint32_t fanIndex, bool multipleFansSupported) {
std::unique_ptr<Fan> pFan = std::make_unique<FanImp>(pOsSysman, fanIndex, multipleFansSupported);
handleList.push_back(std::move(pFan));
}
void FanHandleContext::init() {
std::unique_ptr<Fan> pFan = std::make_unique<FanImp>(pOsSysman);
if (pFan->initSuccess == true) {
handleList.push_back(std::move(pFan));
auto supportedFanCount = OsFan::getSupportedFanCount(pOsSysman);
bool multipleFansSupported = (supportedFanCount > 1);
for (uint32_t fanIndex = 0; fanIndex < supportedFanCount; fanIndex++) {
createHandle(fanIndex, multipleFansSupported);
}
}

View File

@@ -30,13 +30,13 @@ class Fan : _zes_fan_handle_t {
return static_cast<Fan *>(handle);
}
inline zes_fan_handle_t toHandle() { return this; }
bool initSuccess = false;
};
struct FanHandleContext {
FanHandleContext(OsSysman *pOsSysman) : pOsSysman(pOsSysman){};
~FanHandleContext();
void init();
void createHandle(uint32_t fanIndex, bool multipleFansSupported);
ze_result_t fanGet(uint32_t *pCount, zes_fan_handle_t *phFan);

View File

@@ -13,11 +13,9 @@
namespace L0 {
FanImp::FanImp(OsSysman *pOsSysman) {
pOsFan = OsFan::create(pOsSysman);
FanImp::FanImp(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported) {
pOsFan = OsFan::create(pOsSysman, fanIndex, multipleFansSupported);
UNRECOVERABLE_IF(nullptr == pOsFan);
init();
}
ze_result_t FanImp::fanGetProperties(zes_fan_properties_t *pProperties) {
@@ -44,12 +42,6 @@ ze_result_t FanImp::fanGetState(zes_fan_speed_units_t units, int32_t *pSpeed) {
return pOsFan->getState(units, pSpeed);
}
void FanImp::init() {
if (pOsFan->isFanModuleSupported()) {
this->initSuccess = true;
}
}
FanImp::~FanImp() = default;
} // namespace L0

View File

@@ -24,10 +24,11 @@ class FanImp : public Fan, NEO::NonCopyableAndNonMovableClass {
ze_result_t fanSetSpeedTableMode(const zes_fan_speed_table_t *pSpeedTable) override;
ze_result_t fanGetState(zes_fan_speed_units_t units, int32_t *pSpeed) override;
FanImp() = default;
FanImp(OsSysman *pOsSysman);
FanImp(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported);
~FanImp() override;
std::unique_ptr<OsFan> pOsFan;
void init();
protected:
};
} // namespace L0

View File

@@ -32,16 +32,16 @@ ze_result_t LinuxFanImp::getState(zes_fan_speed_units_t units, int32_t *pSpeed)
return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
}
bool LinuxFanImp::isFanModuleSupported() {
return false;
LinuxFanImp::LinuxFanImp(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported) {
}
LinuxFanImp::LinuxFanImp(OsSysman *pOsSysman) {
}
std::unique_ptr<OsFan> OsFan::create(OsSysman *pOsSysman) {
std::unique_ptr<LinuxFanImp> pLinuxFanImp = std::make_unique<LinuxFanImp>(pOsSysman);
std::unique_ptr<OsFan> OsFan::create(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported) {
std::unique_ptr<LinuxFanImp> pLinuxFanImp = std::make_unique<LinuxFanImp>(pOsSysman, fanIndex, multipleFansSupported);
return pLinuxFanImp;
}
uint32_t OsFan::getSupportedFanCount(OsSysman *pOsSysman) {
return 0;
}
} // namespace L0

View File

@@ -24,9 +24,10 @@ class LinuxFanImp : public OsFan, NEO::NonCopyableAndNonMovableClass {
ze_result_t setFixedSpeedMode(const zes_fan_speed_t *pSpeed) override;
ze_result_t setSpeedTableMode(const zes_fan_speed_table_t *pSpeedTable) override;
ze_result_t getState(zes_fan_speed_units_t units, int32_t *pSpeed) override;
bool isFanModuleSupported() override;
LinuxFanImp(OsSysman *pOsSysman);
LinuxFanImp(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported);
LinuxFanImp() = default;
~LinuxFanImp() override = default;
protected:
};
} // namespace L0

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Intel Corporation
* Copyright (C) 2020-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -22,8 +22,8 @@ class OsFan {
virtual ze_result_t setFixedSpeedMode(const zes_fan_speed_t *pSpeed) = 0;
virtual ze_result_t setSpeedTableMode(const zes_fan_speed_table_t *pSpeedTable) = 0;
virtual ze_result_t getState(zes_fan_speed_units_t units, int32_t *pSpeed) = 0;
virtual bool isFanModuleSupported() = 0;
static std::unique_ptr<OsFan> create(OsSysman *pOsSysman);
static uint32_t getSupportedFanCount(OsSysman *pOsSysman);
static std::unique_ptr<OsFan> create(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported);
virtual ~OsFan() = default;
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2024 Intel Corporation
* Copyright (C) 2020-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -21,6 +21,16 @@ struct FanPoint {
};
};
union FanCapability {
struct
{
int32_t allSupported : 1; /// Allows a single usermode fan table to be applied to all fan devices.
int32_t singleSupported : 1; /// Allows different usermode fan tables to be configured.
int32_t reserved : 30; /// Reserved Bits
};
int32_t data;
};
ze_result_t WddmFanImp::getProperties(zes_fan_properties_t *pProperties) {
pProperties->onSubdevice = false;
pProperties->subdeviceId = 0;
@@ -29,6 +39,9 @@ ze_result_t WddmFanImp::getProperties(zes_fan_properties_t *pProperties) {
std::vector<KmdSysman::ResponseProperty> vResponses = {};
KmdSysman::RequestProperty request = {};
// Set fan index only if multiple fans are supported
setFanIndexForMultipleFans(vRequests);
request.commandId = KmdSysman::Command::Set;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentNumOfControlPoints;
@@ -46,19 +59,37 @@ ze_result_t WddmFanImp::getProperties(zes_fan_properties_t *pProperties) {
vRequests.push_back(request);
// Add request to get max fan speed (RPM)
request.requestId = KmdSysman::Requests::Fans::MaxFanSpeedSupported;
vRequests.push_back(request);
ze_result_t status = pKmdSysManager->requestMultiple(vRequests, vResponses);
if ((status != ZE_RESULT_SUCCESS) || (vResponses.size() != vRequests.size())) {
if (status != ZE_RESULT_SUCCESS) {
return status;
}
pProperties->canControl = (vResponses[0].returnCode == KmdSysman::Success);
if (vResponses.size() != vRequests.size()) {
return ZE_RESULT_ERROR_UNKNOWN;
}
if (vResponses[1].returnCode == KmdSysman::Success) {
memcpy_s(&fanPoints, sizeof(uint32_t), vResponses[1].dataBuffer, sizeof(uint32_t));
// Calculate response offset based on whether fan index requests were added
uint32_t responseOffset = getResponseOffset();
pProperties->canControl = (vResponses[responseOffset].returnCode == KmdSysman::Success);
if (vResponses[responseOffset + 1].returnCode == KmdSysman::Success) {
memcpy_s(&fanPoints, sizeof(uint32_t), vResponses[responseOffset + 1].dataBuffer, sizeof(uint32_t));
pProperties->maxPoints = maxPoints = static_cast<int32_t>(fanPoints);
}
pProperties->maxRPM = -1;
// Get max fan speed (RPM)
pProperties->maxRPM = -1; // Default to unsupported
if (vResponses[responseOffset + 2].returnCode == KmdSysman::Success) {
uint32_t maxRPS = 0;
memcpy_s(&maxRPS, sizeof(uint32_t), vResponses[responseOffset + 2].dataBuffer, sizeof(uint32_t));
pProperties->maxRPM = static_cast<int32_t>(maxRPS) * 60;
}
pProperties->supportedModes = zes_fan_speed_mode_t::ZES_FAN_SPEED_MODE_TABLE;
pProperties->supportedUnits = zes_fan_speed_units_t::ZES_FAN_SPEED_UNITS_PERCENT;
@@ -66,81 +97,133 @@ ze_result_t WddmFanImp::getProperties(zes_fan_properties_t *pProperties) {
}
ze_result_t WddmFanImp::getConfig(zes_fan_config_t *pConfig) {
KmdSysman::RequestProperty request;
KmdSysman::ResponseProperty response;
std::vector<KmdSysman::RequestProperty> vRequests = {};
std::vector<KmdSysman::ResponseProperty> vResponses = {};
setFanIndexForMultipleFans(vRequests);
KmdSysman::RequestProperty request = {};
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentNumOfControlPoints;
vRequests.push_back(request);
ze_result_t status = pKmdSysManager->requestSingle(request, response);
ze_result_t status = pKmdSysManager->requestMultiple(vRequests, vResponses);
if (status != ZE_RESULT_SUCCESS) {
return status;
}
if (vResponses.size() != vRequests.size()) {
return ZE_RESULT_ERROR_UNKNOWN;
}
uint32_t responseOffset = getResponseOffset();
uint32_t value = 0;
if (vResponses[responseOffset].returnCode == KmdSysman::Success) {
memcpy_s(&value, sizeof(uint32_t), vResponses[responseOffset].dataBuffer, sizeof(uint32_t));
}
if (value == 0) {
return handleDefaultMode(pConfig);
} else {
uint32_t value = 0;
memcpy_s(&value, sizeof(uint32_t), response.dataBuffer, sizeof(uint32_t));
if (value == 0) {
pConfig->mode = ZES_FAN_SPEED_MODE_DEFAULT;
pConfig->speedTable.numPoints = 0;
return handleTableMode(pConfig, value);
}
}
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::MaxFanControlPointsSupported;
ze_result_t WddmFanImp::handleDefaultMode(zes_fan_config_t *pConfig) {
// Default mode - need to get max fan points
pConfig->mode = ZES_FAN_SPEED_MODE_DEFAULT;
pConfig->speedTable.numPoints = 0;
status = pKmdSysManager->requestSingle(request, response);
if (status == ZE_RESULT_SUCCESS) {
uint32_t maxFanPoints = 0;
memcpy_s(&maxFanPoints, sizeof(uint32_t), response.dataBuffer, sizeof(uint32_t));
pConfig->speedTable.numPoints = static_cast<int32_t>(maxFanPoints);
std::vector<KmdSysman::RequestProperty> vRequests = {};
std::vector<KmdSysman::ResponseProperty> vResponses = {};
setFanIndexForMultipleFans(vRequests);
if (maxFanPoints != 0) {
request.requestId = KmdSysman::Requests::Fans::CurrentFanPoint;
// Try reading Default Fan table if the platform supports
// If platform doesn't support reading default fan table we still return valid FanConfig.Mode
// but not the table filled with default fan table entries
for (int32_t i = 0; i < pConfig->speedTable.numPoints; i++) {
if (pKmdSysManager->requestSingle(request, response) == ZE_RESULT_SUCCESS) {
KmdSysman::RequestProperty request = {};
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::MaxFanControlPointsSupported;
vRequests.push_back(request);
FanPoint point = {};
memcpy_s(&point.data, sizeof(uint32_t), response.dataBuffer, sizeof(uint32_t));
pConfig->speedTable.table[i].speed.speed = point.fanSpeedPercent;
pConfig->speedTable.table[i].speed.units = ZES_FAN_SPEED_UNITS_PERCENT;
pConfig->speedTable.table[i].temperature = point.temperatureDegreesCelsius;
} else {
pConfig->speedTable.numPoints = i;
break;
}
}
}
} // else, return Success. We still return valid FanConfig.mode
} else {
pConfig->mode = ZES_FAN_SPEED_MODE_TABLE;
pConfig->speedTable.numPoints = value;
request.requestId = KmdSysman::Requests::Fans::CurrentFanPoint;
uint32_t responseOffset = getResponseOffset();
ze_result_t status = pKmdSysManager->requestMultiple(vRequests, vResponses);
if ((status == ZE_RESULT_SUCCESS) && (vResponses.size() == vRequests.size()) && (vResponses[responseOffset].returnCode == KmdSysman::Success)) {
uint32_t maxFanPoints = 0;
memcpy_s(&maxFanPoints, sizeof(uint32_t), vResponses[responseOffset].dataBuffer, sizeof(uint32_t));
pConfig->speedTable.numPoints = static_cast<int32_t>(maxFanPoints);
// Try reading Default Fan table if the platform supports
if (maxFanPoints != 0) {
for (int32_t i = 0; i < pConfig->speedTable.numPoints; i++) {
if (pKmdSysManager->requestSingle(request, response) == ZE_RESULT_SUCCESS) {
vRequests.clear();
vResponses.clear();
setFanIndexForMultipleFans(vRequests);
request = {};
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentFanPoint;
vRequests.push_back(request);
status = pKmdSysManager->requestMultiple(vRequests, vResponses);
if ((status == ZE_RESULT_SUCCESS) && (vResponses.size() == vRequests.size()) && (vResponses[responseOffset].returnCode == KmdSysman::Success)) {
FanPoint point = {};
memcpy_s(&point.data, sizeof(uint32_t), response.dataBuffer, sizeof(uint32_t));
memcpy_s(&point.data, sizeof(uint32_t), vResponses[responseOffset].dataBuffer, sizeof(uint32_t));
pConfig->speedTable.table[i].speed.speed = point.fanSpeedPercent;
pConfig->speedTable.table[i].speed.units = ZES_FAN_SPEED_UNITS_PERCENT;
pConfig->speedTable.table[i].temperature = point.temperatureDegreesCelsius;
} else {
pConfig->speedTable.numPoints = i;
break;
}
}
}
} // else, return Success. We still return valid FanConfig.mode
return ZE_RESULT_SUCCESS;
}
ze_result_t WddmFanImp::handleTableMode(zes_fan_config_t *pConfig, uint32_t numPoints) {
// Table mode - need to read existing fan points
pConfig->mode = ZES_FAN_SPEED_MODE_TABLE;
pConfig->speedTable.numPoints = numPoints;
std::vector<KmdSysman::RequestProperty> vRequests = {};
std::vector<KmdSysman::ResponseProperty> vResponses = {};
uint32_t responseOffset = getResponseOffset();
for (int32_t i = 0; i < pConfig->speedTable.numPoints; i++) {
vRequests.clear();
vResponses.clear();
setFanIndexForMultipleFans(vRequests);
KmdSysman::RequestProperty request = {};
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentFanPoint;
vRequests.push_back(request);
ze_result_t status = pKmdSysManager->requestMultiple(vRequests, vResponses);
if ((status == ZE_RESULT_SUCCESS) && (vResponses.size() == vRequests.size()) && (vResponses[responseOffset].returnCode == KmdSysman::Success)) {
FanPoint point = {};
memcpy_s(&point.data, sizeof(uint32_t), vResponses[responseOffset].dataBuffer, sizeof(uint32_t));
pConfig->speedTable.table[i].speed.speed = point.fanSpeedPercent;
pConfig->speedTable.table[i].speed.units = ZES_FAN_SPEED_UNITS_PERCENT;
pConfig->speedTable.table[i].temperature = point.temperatureDegreesCelsius;
}
}
return ZE_RESULT_SUCCESS;
}
ze_result_t WddmFanImp::setDefaultMode() {
KmdSysman::RequestProperty request;
KmdSysman::ResponseProperty response;
std::vector<KmdSysman::RequestProperty> vRequests = {};
std::vector<KmdSysman::ResponseProperty> vResponses = {};
// Set fan index only if multiple fans are supported
setFanIndexForMultipleFans(vRequests);
KmdSysman::RequestProperty request = {};
// Passing current number of control points as zero will reset pcode to default fan curve
uint32_t value = 0; // 0 to reset to default
@@ -149,8 +232,9 @@ ze_result_t WddmFanImp::setDefaultMode() {
request.requestId = KmdSysman::Requests::Fans::CurrentNumOfControlPoints;
request.dataSize = sizeof(uint32_t);
memcpy_s(request.dataBuffer, sizeof(uint32_t), &value, sizeof(uint32_t));
vRequests.push_back(request);
return pKmdSysManager->requestSingle(request, response);
return pKmdSysManager->requestMultiple(vRequests, vResponses);
}
ze_result_t WddmFanImp::setFixedSpeedMode(const zes_fan_speed_t *pSpeed) {
@@ -158,19 +242,31 @@ ze_result_t WddmFanImp::setFixedSpeedMode(const zes_fan_speed_t *pSpeed) {
}
ze_result_t WddmFanImp::setSpeedTableMode(const zes_fan_speed_table_t *pSpeedTable) {
std::vector<KmdSysman::RequestProperty> vRequests = {};
std::vector<KmdSysman::ResponseProperty> vResponses = {};
// Set fan index first only if multiple fans are supported
setFanIndexForMultipleFans(vRequests);
KmdSysman::RequestProperty request = {};
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::MaxFanControlPointsSupported;
vRequests.push_back(request);
KmdSysman::RequestProperty singleRequest;
KmdSysman::ResponseProperty singleResponse;
singleRequest.commandId = KmdSysman::Command::Get;
singleRequest.componentId = KmdSysman::Component::FanComponent;
singleRequest.requestId = KmdSysman::Requests::Fans::MaxFanControlPointsSupported;
uint32_t fanPoints = 2;
if (pKmdSysManager->requestSingle(singleRequest, singleResponse) == ZE_RESULT_SUCCESS) {
if (singleResponse.returnCode == KmdSysman::Success) {
memcpy_s(&fanPoints, sizeof(uint32_t), singleResponse.dataBuffer, sizeof(uint32_t));
}
uint32_t responseOffset = getResponseOffset();
ze_result_t status = pKmdSysManager->requestMultiple(vRequests, vResponses);
if (status != ZE_RESULT_SUCCESS) {
return status;
}
if ((vResponses.size() == vRequests.size()) && (vResponses[responseOffset].returnCode == KmdSysman::Success)) {
memcpy_s(&fanPoints, sizeof(uint32_t), vResponses[responseOffset].dataBuffer, sizeof(uint32_t));
maxPoints = static_cast<int32_t>(fanPoints);
} else {
return ZE_RESULT_ERROR_UNKNOWN;
}
if (pSpeedTable->numPoints == 0 || pSpeedTable->numPoints > maxPoints) {
@@ -179,16 +275,20 @@ ze_result_t WddmFanImp::setSpeedTableMode(const zes_fan_speed_table_t *pSpeedTab
for (int32_t i = 0; i < pSpeedTable->numPoints; i++) {
if (pSpeedTable->table[i].speed.units == zes_fan_speed_units_t::ZES_FAN_SPEED_UNITS_RPM) {
return ze_result_t::ZE_RESULT_ERROR_INVALID_ARGUMENT;
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
}
}
std::vector<KmdSysman::RequestProperty> vRequests = {};
std::vector<KmdSysman::ResponseProperty> vResponses = {};
KmdSysman::RequestProperty request = {};
// Clear the vectors and prepare for the actual speed table setting
vRequests.clear();
vResponses.clear();
// Set fan index first only if multiple fans are supported
setFanIndexForMultipleFans(vRequests);
uint32_t value = pSpeedTable->numPoints;
request = {};
request.commandId = KmdSysman::Command::Set;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentNumOfControlPoints;
@@ -214,46 +314,119 @@ ze_result_t WddmFanImp::getState(zes_fan_speed_units_t units, int32_t *pSpeed) {
return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
}
KmdSysman::RequestProperty request;
KmdSysman::ResponseProperty response;
std::vector<KmdSysman::RequestProperty> vRequests = {};
std::vector<KmdSysman::ResponseProperty> vResponses = {};
setFanIndexForMultipleFans(vRequests);
KmdSysman::RequestProperty request = {};
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentFanSpeed;
vRequests.push_back(request);
ze_result_t status = pKmdSysManager->requestSingle(request, response);
uint32_t responseOffset = getResponseOffset();
ze_result_t status = pKmdSysManager->requestMultiple(vRequests, vResponses);
if (status != ZE_RESULT_SUCCESS) {
return status;
}
uint32_t value = 0;
memcpy_s(&value, sizeof(uint32_t), response.dataBuffer, sizeof(uint32_t));
if ((vResponses.size() != vRequests.size()) || (vResponses[responseOffset].returnCode != KmdSysman::Success)) {
return ZE_RESULT_ERROR_UNKNOWN;
}
uint32_t value = 0;
memcpy_s(&value, sizeof(uint32_t), vResponses[responseOffset].dataBuffer, sizeof(uint32_t));
*pSpeed = static_cast<int32_t>(value);
return status;
return ZE_RESULT_SUCCESS;
}
bool WddmFanImp::isFanModuleSupported() {
KmdSysman::RequestProperty request = {};
KmdSysman::ResponseProperty response = {};
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentFanSpeed;
return (pKmdSysManager->requestSingle(request, response) == ZE_RESULT_SUCCESS);
}
WddmFanImp::WddmFanImp(OsSysman *pOsSysman) {
WddmFanImp::WddmFanImp(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported)
: fanIndex(fanIndex), multipleFansSupported(multipleFansSupported) {
WddmSysmanImp *pWddmSysmanImp = static_cast<WddmSysmanImp *>(pOsSysman);
pKmdSysManager = &pWddmSysmanImp->getKmdSysManager();
}
std::unique_ptr<OsFan> OsFan::create(OsSysman *pOsSysman) {
std::unique_ptr<WddmFanImp> pWddmFanImp = std::make_unique<WddmFanImp>(pOsSysman);
std::unique_ptr<OsFan> OsFan::create(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported) {
std::unique_ptr<WddmFanImp> pWddmFanImp = std::make_unique<WddmFanImp>(pOsSysman, fanIndex, multipleFansSupported);
return pWddmFanImp;
}
uint32_t OsFan::getSupportedFanCount(OsSysman *pOsSysman) {
WddmSysmanImp *pWddmSysmanImp = static_cast<WddmSysmanImp *>(pOsSysman);
KmdSysManager *pKmdSysManager = &pWddmSysmanImp->getKmdSysManager();
KmdSysman::RequestProperty request;
KmdSysman::ResponseProperty response;
// Get the number of fan domains
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::NumFanDomains;
ze_result_t status = pKmdSysManager->requestSingle(request, response);
if (status != ZE_RESULT_SUCCESS) {
return 0;
}
uint32_t fanCount = 0;
memcpy_s(&fanCount, sizeof(uint32_t), response.dataBuffer, sizeof(uint32_t));
// If fanCount > 1, check if SingleSupported mode is available
if (fanCount > 1) {
request.commandId = KmdSysman::Command::Get;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::SupportedFanModeCapabilities;
status = pKmdSysManager->requestSingle(request, response);
if (status == ZE_RESULT_SUCCESS) {
FanCapability fanCaps = {};
memcpy_s(&fanCaps.data, sizeof(uint32_t), response.dataBuffer, sizeof(uint32_t));
// Check SingleSupported capability
bool singleSupported = (fanCaps.singleSupported != 0);
// If SingleSupported is not available, return fanCount as 1
if (!singleSupported) {
fanCount = 1;
}
}
}
return fanCount;
}
void WddmFanImp::setFanIndexForMultipleFans(std::vector<KmdSysman::RequestProperty> &vRequests) {
if (!multipleFansSupported) {
return;
}
KmdSysman::RequestProperty request = {};
// First, set fan mode to single
request.commandId = KmdSysman::Command::Set;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentFanMode;
request.dataSize = sizeof(uint32_t);
uint32_t fanMode = KmdSysman::FanUserMode::FanModeSingle;
memcpy_s(request.dataBuffer, sizeof(uint32_t), &fanMode, sizeof(uint32_t));
vRequests.push_back(request);
// Then, set the fan index
request.commandId = KmdSysman::Command::Set;
request.componentId = KmdSysman::Component::FanComponent;
request.requestId = KmdSysman::Requests::Fans::CurrentFanIndex;
request.dataSize = sizeof(uint32_t);
memcpy_s(request.dataBuffer, sizeof(uint32_t), &fanIndex, sizeof(uint32_t));
vRequests.push_back(request);
}
uint32_t WddmFanImp::getResponseOffset() const {
return multipleFansSupported ? 2 : 0;
}
} // namespace L0

View File

@@ -9,6 +9,7 @@
#include "shared/source/helpers/non_copyable_or_moveable.h"
#include "level_zero/tools/source/sysman/fan/os_fan.h"
#include "level_zero/tools/source/sysman/windows/kmd_sys.h"
#include "level_zero/tools/source/sysman/windows/os_sysman_imp.h"
namespace L0 {
@@ -22,16 +23,22 @@ class WddmFanImp : public OsFan, NEO::NonCopyableAndNonMovableClass {
ze_result_t setFixedSpeedMode(const zes_fan_speed_t *pSpeed) override;
ze_result_t setSpeedTableMode(const zes_fan_speed_table_t *pSpeedTable) override;
ze_result_t getState(zes_fan_speed_units_t units, int32_t *pSpeed) override;
bool isFanModuleSupported() override;
WddmFanImp(OsSysman *pOsSysman);
WddmFanImp(OsSysman *pOsSysman, uint32_t fanIndex, bool multipleFansSupported);
WddmFanImp() = default;
~WddmFanImp() override = default;
protected:
KmdSysManager *pKmdSysManager = nullptr;
uint32_t fanIndex = 0;
bool multipleFansSupported = false;
private:
void setFanIndexForMultipleFans(std::vector<KmdSysman::RequestProperty> &vRequests);
uint32_t getResponseOffset() const;
ze_result_t handleDefaultMode(zes_fan_config_t *pConfig);
ze_result_t handleTableMode(zes_fan_config_t *pConfig, uint32_t numPoints);
int32_t maxPoints = 0;
};

View File

@@ -240,6 +240,9 @@ enum Fans {
CurrentNumOfControlPoints,
CurrentFanPoint,
CurrentFanSpeed,
SupportedFanModeCapabilities,
CurrentFanMode,
CurrentFanIndex,
MaxFanRequests,
};
@@ -492,6 +495,13 @@ enum PciLinkWidthType {
MaxPciLinkWidthTypes,
};
enum FanUserMode {
FanModeAll = 0, /// Means 1 user fan table is applied to all fan devices
FanModeSingle, /// Means configuration is on a per fan basis
FanModeMax
};
struct KmdSysmanVersion {
KmdSysmanVersion() : data(0) {}
union {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Intel Corporation
* Copyright (C) 2020-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -24,7 +24,7 @@ class SysmanDeviceFanFixture : public SysmanDeviceFixture {
GTEST_SKIP();
}
SysmanDeviceFixture::SetUp();
pFan = std::make_unique<L0::FanImp>(pOsSysman);
pFan = std::make_unique<L0::FanImp>(pOsSysman, 0, false);
}
void TearDown() override {
@@ -112,5 +112,29 @@ TEST_F(SysmanDeviceFanFixture, GivenValidFanHandleWhenGettingFanSpeedWithPercent
EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, zesFanGetState(fanHandle, unit, &fanSpeed));
}
TEST_F(SysmanDeviceFanFixture, GivenFanHandleContextWhenCallingCreateHandleThenHandleIsCreatedSuccessfully) {
// Create a FanHandleContext to directly test the createHandle functionality
auto fanContext = std::make_unique<L0::FanHandleContext>(pOsSysman);
// Test that createHandle works correctly by calling it directly
uint32_t fanIndex = 0;
bool multipleFansSupported = false;
// Verify initial handle count is 0
EXPECT_EQ(fanContext->handleList.size(), 0u);
// Call createHandle directly to test the function
fanContext->createHandle(fanIndex, multipleFansSupported);
// Verify handle was created
EXPECT_EQ(fanContext->handleList.size(), 1u);
EXPECT_NE(fanContext->handleList[0], nullptr);
// Test that the created handle works as expected for Linux (should return unsupported)
auto handle = fanContext->handleList[0]->toHandle();
zes_fan_properties_t properties;
EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, zesFanGetProperties(handle, &properties));
}
} // namespace ult
} // namespace L0

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2024 Intel Corporation
* Copyright (C) 2020-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -25,81 +25,120 @@ struct Mock<FanKmdSysManager> : public FanKmdSysManager {
};
uint32_t Data;
} mockFanTempSpeed[10], mockStockFanTable[10];
uint32_t mockFanMaxPoints = 10;
uint32_t mockFanCurrentPulses = 523436;
uint32_t mockFanCurrentFanPoints = 0;
uint32_t mockCurrentReadIndex = 0;
uint32_t mockCurrentWriteIndex = 0;
bool isFanTableSet = false;
bool isStockFanTableAvailable = false;
bool failMaxPointsGet = false;
bool retZeroMaxPoints = false;
bool smallStockTable = false;
uint32_t mockFanMaxPoints = 10; // Maximum number of fan control points
uint32_t mockFanCurrentPulses = 523436; // Current fan speed in pulses/RPM
uint32_t mockFanCurrentFanPoints = 0; // Default: no fan points set
uint32_t mockCurrentReadIndex = 0; // Default: start reading from beginning
uint32_t mockCurrentWriteIndex = 0; // Default: start writing from beginning
uint32_t mockSupportedFanCount = 1; // Number of supported fans
uint32_t mockMaxFanSpeed = 3000; // Mock max RPM value
uint32_t mockSupportedFanModeCapabilities = 0x02; // Default: singleSupported = 1
bool isFanTableSet = false; // Default: no custom fan table set
bool mockReturnNoStockTable = false; // Default: don't force no stock table scenario
bool mockReturnSmallStockTable = false; // Default: don't force small stock table scenario
void getFanProperty(KmdSysman::GfxSysmanReqHeaderIn *pRequest, KmdSysman::GfxSysmanReqHeaderOut *pResponse) override {
// Controllable failure flags for each fan request type (similar to power mock pattern)
uint32_t mockFanFailure[KmdSysman::Requests::Fans::MaxFanRequests] = {0};
uint32_t getReturnCode(uint32_t fanRequestCode) {
return mockFanFailure[fanRequestCode] ? KmdSysman::KmdSysmanFail : KmdSysman::KmdSysmanSuccess;
}
void getFanProperty(KmdSysman::GfxSysmanReqHeaderIn *pRequest, KmdSysman::GfxSysmanReqHeaderOut *pResponse) {
uint8_t *pBuffer = reinterpret_cast<uint8_t *>(pResponse);
pBuffer += sizeof(KmdSysman::GfxSysmanReqHeaderOut);
switch (pRequest->inRequestId) {
case KmdSysman::Requests::Fans::NumFanDomains: {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
*pValue = mockSupportedFanCount;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
pResponse->outDataSize = sizeof(uint32_t);
} break;
case KmdSysman::Requests::Fans::SupportedFanModeCapabilities: {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
// Set configurable fan mode capabilities
*pValue = mockSupportedFanModeCapabilities;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
pResponse->outDataSize = sizeof(uint32_t);
} break;
case KmdSysman::Requests::Fans::MaxFanSpeedSupported: {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
*pValue = mockMaxFanSpeed;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
pResponse->outDataSize = sizeof(uint32_t);
} break;
case KmdSysman::Requests::Fans::MaxFanControlPointsSupported: {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
*pValue = mockFanMaxPoints;
if (retZeroMaxPoints) {
*pValue = 0;
}
if (!failMaxPointsGet) {
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
pResponse->outDataSize = sizeof(uint32_t);
// Use explicit scenario flags for clearer test control
if (mockReturnNoStockTable) {
*pValue = 0; // No stock table available
} else if (mockReturnSmallStockTable) {
*pValue = 5; // Small stock table
} else {
pResponse->outReturnCode = KmdSysman::KmdSysmanFail;
pResponse->outDataSize = 0;
// Default behavior: normal stock table with full points
*pValue = mockFanMaxPoints;
}
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
pResponse->outDataSize = sizeof(uint32_t);
} break;
case KmdSysman::Requests::Fans::CurrentFanSpeed: {
if (fanSupported) {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
*pValue = mockFanCurrentPulses;
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
pResponse->outDataSize = sizeof(uint32_t);
} else {
pResponse->outDataSize = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanFail;
}
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
*pValue = mockFanCurrentPulses;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
pResponse->outDataSize = sizeof(uint32_t);
} break;
case KmdSysman::Requests::Fans::CurrentNumOfControlPoints: {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
*pValue = mockFanCurrentFanPoints;
mockCurrentReadIndex = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
pResponse->outDataSize = sizeof(uint32_t);
} break;
case KmdSysman::Requests::Fans::CurrentFanPoint: {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
// Check controllable failure first
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
if (pResponse->outReturnCode != KmdSysman::KmdSysmanSuccess) {
pResponse->outDataSize = 0;
break;
}
if (isFanTableSet) {
// Return custom fan table points set by test
mockFanTempSpeed[mockCurrentReadIndex].FanSpeedPercent = mockCurrentReadIndex * 10;
mockFanTempSpeed[mockCurrentReadIndex].TemperatureDegreesCelsius = mockCurrentReadIndex * 10 + 20;
*pValue = mockFanTempSpeed[mockCurrentReadIndex].Data;
mockCurrentReadIndex++;
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
pResponse->outDataSize = sizeof(uint32_t);
} else if (isStockFanTableAvailable) {
} else if (mockReturnNoStockTable) {
// No stock table available - fail immediately
pResponse->outDataSize = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanFail;
} else if (mockReturnSmallStockTable) {
// Small stock table - return 5 points then fail
if (mockCurrentReadIndex < 5) {
mockStockFanTable[mockCurrentReadIndex].FanSpeedPercent = mockCurrentReadIndex * 10 - 5;
mockStockFanTable[mockCurrentReadIndex].TemperatureDegreesCelsius = mockCurrentReadIndex * 10 + 15;
*pValue = mockStockFanTable[mockCurrentReadIndex].Data;
mockCurrentReadIndex++;
pResponse->outDataSize = sizeof(uint32_t);
} else {
// End of small table
*pValue = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanFail;
pResponse->outDataSize = 0;
}
} else {
// Default: full stock table available
mockStockFanTable[mockCurrentReadIndex].FanSpeedPercent = mockCurrentReadIndex * 10 - 5;
mockStockFanTable[mockCurrentReadIndex].TemperatureDegreesCelsius = mockCurrentReadIndex * 10 + 15;
*pValue = mockStockFanTable[mockCurrentReadIndex].Data;
mockCurrentReadIndex++;
if (smallStockTable && mockCurrentReadIndex == 6) {
// only return 5 points for smallStockTable
*pValue = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanFail;
pResponse->outDataSize = 0;
} else {
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
pResponse->outDataSize = sizeof(uint32_t);
}
} else {
pResponse->outDataSize = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanFail;
pResponse->outDataSize = sizeof(uint32_t);
}
} break;
default: {
@@ -109,23 +148,39 @@ struct Mock<FanKmdSysManager> : public FanKmdSysManager {
}
}
void setFanProperty(KmdSysman::GfxSysmanReqHeaderIn *pRequest, KmdSysman::GfxSysmanReqHeaderOut *pResponse) override {
void setFanProperty(KmdSysman::GfxSysmanReqHeaderIn *pRequest, KmdSysman::GfxSysmanReqHeaderOut *pResponse) {
uint8_t *pBuffer = reinterpret_cast<uint8_t *>(pRequest);
pBuffer += sizeof(KmdSysman::GfxSysmanReqHeaderIn);
switch (pRequest->inRequestId) {
case KmdSysman::Requests::Fans::CurrentFanMode: {
// Just acknowledge the fan mode setting
pResponse->outDataSize = 0;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
} break;
case KmdSysman::Requests::Fans::CurrentFanIndex: {
// Just acknowledge the fan index setting
pResponse->outDataSize = 0;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
} break;
case KmdSysman::Requests::Fans::CurrentNumOfControlPoints: {
uint32_t *pValue = reinterpret_cast<uint32_t *>(pBuffer);
mockFanCurrentFanPoints = *pValue;
pResponse->outDataSize = 0;
// Check controllable failure first
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
if (pResponse->outReturnCode != KmdSysman::KmdSysmanSuccess) {
break;
}
if ((mockFanCurrentFanPoints % 2 == 0) && (mockFanCurrentFanPoints > 0) && (mockFanCurrentFanPoints <= mockFanMaxPoints)) {
mockCurrentWriteIndex = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
} else if (mockFanCurrentFanPoints == 0) {
isFanTableSet = false;
mockCurrentReadIndex = 0;
mockCurrentWriteIndex = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
} else {
pResponse->outReturnCode = KmdSysman::KmdSysmanFail;
}
@@ -135,7 +190,7 @@ struct Mock<FanKmdSysManager> : public FanKmdSysManager {
mockFanTempSpeed[mockCurrentWriteIndex++].Data = *pValue;
isFanTableSet = true;
pResponse->outDataSize = 0;
pResponse->outReturnCode = KmdSysman::KmdSysmanSuccess;
pResponse->outReturnCode = getReturnCode(pRequest->inRequestId);
} break;
default: {
pResponse->outDataSize = 0;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Intel Corporation
* Copyright (C) 2020-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -38,6 +38,8 @@ struct Mock<MockKmdSysManager> : public MockKmdSysManager {
bool requestMultipleSizeDiff = false;
ze_result_t mockRequestSingleResult = ZE_RESULT_ERROR_NOT_AVAILABLE;
ze_result_t mockRequestMultipleResult = ZE_RESULT_ERROR_NOT_AVAILABLE;
uint32_t failSelectiveRequestMultipleCount = 0;
uint32_t requestMultipleCallCount = 0;
MockEventHandle handles[KmdSysman::Events::MaxEvents][mockKmdMaxHandlesPerEvent];
@@ -309,10 +311,14 @@ struct Mock<MockKmdSysManager> : public MockKmdSysManager {
}
ze_result_t requestMultiple(std::vector<KmdSysman::RequestProperty> &vIn, std::vector<KmdSysman::ResponseProperty> &vOut) override {
requestMultipleCallCount++;
if (mockRequestMultiple == false) {
return KmdSysManager::requestMultiple(vIn, vOut);
} else {
if (requestMultipleSizeDiff == true) {
if ((failSelectiveRequestMultipleCount && failSelectiveRequestMultipleCount > requestMultipleCallCount)) {
return KmdSysManager::requestMultiple(vIn, vOut);
}
if (requestMultipleSizeDiff == true && vOut.size() == vIn.size()) {
KmdSysman::ResponseProperty temp;
vOut.push_back(temp);
}