/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include "IntelCPUSensor.hpp" #include "SensorPaths.hpp" #include "Thresholds.hpp" #include "Utils.hpp" #include "sensor.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include IntelCPUSensor::IntelCPUSensor( const std::string& path, const std::string& objectType, sdbusplus::asio::object_server& objectServer, std::shared_ptr& conn, boost::asio::io_context& io, const std::string& sensorName, std::vector&& thresholdsIn, const std::string& sensorConfiguration, int cpuId, bool show, double dtsOffset) : Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, objectType, false, false, 0, 0, conn, PowerState::on), objServer(objectServer), inputDev(io), waitTimer(io), nameTcontrol("Tcontrol CPU" + std::to_string(cpuId)), path(path), privTcontrol(std::numeric_limits::quiet_NaN()), dtsOffset(dtsOffset), show(show), pollTime(IntelCPUSensor::sensorPollMs) { if (show) { if (auto fileParts = splitFileName(path)) { auto& [type, nr, item] = *fileParts; std::string interfacePath; const char* units = nullptr; if (type == "power") { interfacePath = "/xyz/openbmc_project/sensors/power/" + name; units = sensor_paths::unitWatts; minValue = 0; maxValue = 511; } else { interfacePath = "/xyz/openbmc_project/sensors/temperature/" + name; units = sensor_paths::unitDegreesC; minValue = -128; maxValue = 127; } sensorInterface = objectServer.add_interface( interfacePath, "xyz.openbmc_project.Sensor.Value"); for (const auto& threshold : thresholds) { std::string interface = thresholds::getInterface(threshold.level); thresholdInterfaces[static_cast(threshold.level)] = objectServer.add_interface(interfacePath, interface); } association = objectServer.add_interface(interfacePath, association::interface); setInitialProperties(units); } } // call setup always as not all sensors call setInitialProperties setupPowerMatch(conn); } IntelCPUSensor::~IntelCPUSensor() { // close the input dev to cancel async operations inputDev.close(); waitTimer.cancel(); if (show) { for (const auto& iface : thresholdInterfaces) { objServer.remove_interface(iface); } objServer.remove_interface(sensorInterface); objServer.remove_interface(association); objServer.remove_interface(availableInterface); objServer.remove_interface(operationalInterface); } } void IntelCPUSensor::restartRead() { std::weak_ptr weakRef = weak_from_this(); waitTimer.expires_after(std::chrono::milliseconds(pollTime)); waitTimer.async_wait([weakRef](const boost::system::error_code& ec) { if (ec == boost::asio::error::operation_aborted) { std::cerr << "Failed to reschedule\n"; return; } std::shared_ptr self = weakRef.lock(); if (self) { self->setupRead(); } }); } void IntelCPUSensor::setupRead() { if (readingStateGood()) { inputDev.close(); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) fd = open(path.c_str(), O_RDONLY | O_NONBLOCK); if (fd < 0) { std::cerr << name << " unable to open fd!\n"; return; } inputDev.assign(fd); } else { markAvailable(false); updateValue(std::numeric_limits::quiet_NaN()); restartRead(); return; } std::weak_ptr weakRef = weak_from_this(); inputDev.async_wait(boost::asio::posix::descriptor_base::wait_read, [weakRef](const boost::system::error_code& ec) { std::shared_ptr self = weakRef.lock(); if (self) { self->handleResponse(ec); } }); } void IntelCPUSensor::updateMinMaxValues() { const boost::container::flat_map< std::string, std::vector, const char*>>> map = { { "cap", { std::make_tuple("cap_max", std::ref(maxValue), "MaxValue"), std::make_tuple("cap_min", std::ref(minValue), "MinValue"), }, }, }; if (auto fileParts = splitFileName(path)) { auto& [fileType, fileNr, fileItem] = *fileParts; const auto mapIt = map.find(fileItem); if (mapIt != map.cend()) { for (const auto& vectorItem : mapIt->second) { const auto& [suffix, oldValue, dbusName] = vectorItem; auto attrPath = boost::replace_all_copy(path, fileItem, suffix); if (auto newVal = readFile(attrPath, IntelCPUSensor::sensorScaleFactor)) { updateProperty(sensorInterface, oldValue, *newVal, dbusName); } else { if (isPowerOn()) { updateProperty(sensorInterface, oldValue, 0, dbusName); } else { updateProperty(sensorInterface, oldValue, std::numeric_limits::quiet_NaN(), dbusName); } } } } } } void IntelCPUSensor::handleResponse(const boost::system::error_code& err) { if ((err == boost::system::errc::bad_file_descriptor) || (err == boost::asio::error::misc_errors::not_found)) { return; // we're being destroyed } if (err == boost::system::errc::operation_canceled) { if (readingStateGood()) { if (!loggedInterfaceDown) { std::cerr << name << " interface down!\n"; loggedInterfaceDown = true; } pollTime = static_cast(IntelCPUSensor::sensorPollMs) * 10U; markFunctional(false); } return; } loggedInterfaceDown = false; if (err) { pollTime = sensorFailedPollTimeMs; incrementError(); return; } static constexpr uint32_t bufLen = 128; std::string response; response.resize(bufLen); int rdLen = 0; if (fd >= 0) { rdLen = pread(fd, response.data(), bufLen, 0); } if (rdLen > 0) { try { rawValue = std::stod(response); double nvalue = rawValue / IntelCPUSensor::sensorScaleFactor; if (show) { updateValue(nvalue); } else { value = nvalue; } if (minMaxReadCounter++ % 8 == 0) { updateMinMaxValues(); } double gTcontrol = gCpuSensors[nameTcontrol] ? gCpuSensors[nameTcontrol]->value : std::numeric_limits::quiet_NaN(); if (gTcontrol != privTcontrol) { privTcontrol = gTcontrol; if (!thresholds.empty()) { std::vector newThresholds; if (parseThresholdsFromAttr( newThresholds, path, IntelCPUSensor::sensorScaleFactor, dtsOffset, 0)) { if (!std::equal(thresholds.begin(), thresholds.end(), newThresholds.begin(), newThresholds.end())) { thresholds = newThresholds; if (show) { thresholds::updateThresholds(this); } } } else { std::cerr << "Failure to update thresholds for " << name << "\n"; } } } } catch (const std::invalid_argument&) { incrementError(); } } else { pollTime = sensorFailedPollTimeMs; incrementError(); } restartRead(); } void IntelCPUSensor::checkThresholds() { if (show) { thresholds::checkThresholds(this); } }