xref: /openbmc/dbus-sensors/src/intel-cpu/IntelCPUSensor.cpp (revision 9b992939d7ba8ed69ace8cf7ed65d0c7b416818f)
1d7be555eSGeorge Liu /*
2d7be555eSGeorge Liu // Copyright (c) 2018 Intel Corporation
3d7be555eSGeorge Liu //
4d7be555eSGeorge Liu // Licensed under the Apache License, Version 2.0 (the "License");
5d7be555eSGeorge Liu // you may not use this file except in compliance with the License.
6d7be555eSGeorge Liu // You may obtain a copy of the License at
7d7be555eSGeorge Liu //
8d7be555eSGeorge Liu //      http://www.apache.org/licenses/LICENSE-2.0
9d7be555eSGeorge Liu //
10d7be555eSGeorge Liu // Unless required by applicable law or agreed to in writing, software
11d7be555eSGeorge Liu // distributed under the License is distributed on an "AS IS" BASIS,
12d7be555eSGeorge Liu // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7be555eSGeorge Liu // See the License for the specific language governing permissions and
14d7be555eSGeorge Liu // limitations under the License.
15d7be555eSGeorge Liu */
16d7be555eSGeorge Liu 
17d7be555eSGeorge Liu #include "IntelCPUSensor.hpp"
18d7be555eSGeorge Liu 
19d7be555eSGeorge Liu #include "SensorPaths.hpp"
20d7be555eSGeorge Liu #include "Thresholds.hpp"
21d7be555eSGeorge Liu #include "Utils.hpp"
22d7be555eSGeorge Liu #include "sensor.hpp"
23d7be555eSGeorge Liu 
24d7be555eSGeorge Liu #include <fcntl.h>
25d7be555eSGeorge Liu #include <unistd.h>
26d7be555eSGeorge Liu 
27d7be555eSGeorge Liu #include <boost/algorithm/string/replace.hpp>
28d7be555eSGeorge Liu #include <boost/asio/error.hpp>
29d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
30d7be555eSGeorge Liu #include <boost/asio/posix/descriptor_base.hpp>
31d7be555eSGeorge Liu #include <boost/container/flat_map.hpp>
324ab892a4SGeorge Liu #include <phosphor-logging/lg2.hpp>
33d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
34d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
35d7be555eSGeorge Liu 
36d7be555eSGeorge Liu #include <algorithm>
37d7be555eSGeorge Liu #include <chrono>
38d7be555eSGeorge Liu #include <cstddef>
39d7be555eSGeorge Liu #include <cstdint>
40d7be555eSGeorge Liu #include <functional>
41d7be555eSGeorge Liu #include <limits>
42d7be555eSGeorge Liu #include <memory>
43d7be555eSGeorge Liu #include <stdexcept>
44d7be555eSGeorge Liu #include <string>
45d7be555eSGeorge Liu #include <tuple>
46d7be555eSGeorge Liu #include <utility>
47d7be555eSGeorge Liu #include <vector>
48d7be555eSGeorge Liu 
IntelCPUSensor(const std::string & path,const std::string & objectType,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & conn,boost::asio::io_context & io,const std::string & sensorName,std::vector<thresholds::Threshold> && thresholdsIn,const std::string & sensorConfiguration,int cpuId,bool show,double dtsOffset)49d7be555eSGeorge Liu IntelCPUSensor::IntelCPUSensor(
50d7be555eSGeorge Liu     const std::string& path, const std::string& objectType,
51d7be555eSGeorge Liu     sdbusplus::asio::object_server& objectServer,
52d7be555eSGeorge Liu     std::shared_ptr<sdbusplus::asio::connection>& conn,
53d7be555eSGeorge Liu     boost::asio::io_context& io, const std::string& sensorName,
54d7be555eSGeorge Liu     std::vector<thresholds::Threshold>&& thresholdsIn,
55d7be555eSGeorge Liu     const std::string& sensorConfiguration, int cpuId, bool show,
56d7be555eSGeorge Liu     double dtsOffset) :
57d7be555eSGeorge Liu     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
58d7be555eSGeorge Liu            objectType, false, false, 0, 0, conn, PowerState::on),
59d7be555eSGeorge Liu     objServer(objectServer), inputDev(io), waitTimer(io),
60d7be555eSGeorge Liu     nameTcontrol("Tcontrol CPU" + std::to_string(cpuId)), path(path),
61d7be555eSGeorge Liu     privTcontrol(std::numeric_limits<double>::quiet_NaN()),
62aba6fcacSEd Tanous     dtsOffset(dtsOffset), show(show)
63d7be555eSGeorge Liu {
64d7be555eSGeorge Liu     if (show)
65d7be555eSGeorge Liu     {
66d7be555eSGeorge Liu         if (auto fileParts = splitFileName(path))
67d7be555eSGeorge Liu         {
68d7be555eSGeorge Liu             auto& [type, nr, item] = *fileParts;
69d7be555eSGeorge Liu             std::string interfacePath;
70d7be555eSGeorge Liu             const char* units = nullptr;
71d7be555eSGeorge Liu             if (type == "power")
72d7be555eSGeorge Liu             {
73d7be555eSGeorge Liu                 interfacePath = "/xyz/openbmc_project/sensors/power/" + name;
74d7be555eSGeorge Liu                 units = sensor_paths::unitWatts;
75d7be555eSGeorge Liu                 minValue = 0;
76d7be555eSGeorge Liu                 maxValue = 511;
77d7be555eSGeorge Liu             }
78d7be555eSGeorge Liu             else
79d7be555eSGeorge Liu             {
80d7be555eSGeorge Liu                 interfacePath = "/xyz/openbmc_project/sensors/temperature/" +
81d7be555eSGeorge Liu                                 name;
82d7be555eSGeorge Liu                 units = sensor_paths::unitDegreesC;
83d7be555eSGeorge Liu                 minValue = -128;
84d7be555eSGeorge Liu                 maxValue = 127;
85d7be555eSGeorge Liu             }
86d7be555eSGeorge Liu 
87d7be555eSGeorge Liu             sensorInterface = objectServer.add_interface(
88d7be555eSGeorge Liu                 interfacePath, "xyz.openbmc_project.Sensor.Value");
89d7be555eSGeorge Liu             for (const auto& threshold : thresholds)
90d7be555eSGeorge Liu             {
91d7be555eSGeorge Liu                 std::string interface =
92d7be555eSGeorge Liu                     thresholds::getInterface(threshold.level);
93d7be555eSGeorge Liu                 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
94d7be555eSGeorge Liu                     objectServer.add_interface(interfacePath, interface);
95d7be555eSGeorge Liu             }
96d7be555eSGeorge Liu             association = objectServer.add_interface(interfacePath,
97d7be555eSGeorge Liu                                                      association::interface);
98d7be555eSGeorge Liu 
99d7be555eSGeorge Liu             setInitialProperties(units);
100d7be555eSGeorge Liu         }
101d7be555eSGeorge Liu     }
102d7be555eSGeorge Liu 
103d7be555eSGeorge Liu     // call setup always as not all sensors call setInitialProperties
104d7be555eSGeorge Liu     setupPowerMatch(conn);
105d7be555eSGeorge Liu }
106d7be555eSGeorge Liu 
~IntelCPUSensor()107d7be555eSGeorge Liu IntelCPUSensor::~IntelCPUSensor()
108d7be555eSGeorge Liu {
109d7be555eSGeorge Liu     // close the input dev to cancel async operations
110d7be555eSGeorge Liu     inputDev.close();
111d7be555eSGeorge Liu     waitTimer.cancel();
112d7be555eSGeorge Liu     if (show)
113d7be555eSGeorge Liu     {
114d7be555eSGeorge Liu         for (const auto& iface : thresholdInterfaces)
115d7be555eSGeorge Liu         {
116d7be555eSGeorge Liu             objServer.remove_interface(iface);
117d7be555eSGeorge Liu         }
118d7be555eSGeorge Liu         objServer.remove_interface(sensorInterface);
119d7be555eSGeorge Liu         objServer.remove_interface(association);
120d7be555eSGeorge Liu         objServer.remove_interface(availableInterface);
121d7be555eSGeorge Liu         objServer.remove_interface(operationalInterface);
122d7be555eSGeorge Liu     }
123d7be555eSGeorge Liu }
124d7be555eSGeorge Liu 
restartRead()125d7be555eSGeorge Liu void IntelCPUSensor::restartRead()
126d7be555eSGeorge Liu {
127d7be555eSGeorge Liu     std::weak_ptr<IntelCPUSensor> weakRef = weak_from_this();
128d7be555eSGeorge Liu     waitTimer.expires_after(std::chrono::milliseconds(pollTime));
129d7be555eSGeorge Liu     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
130d7be555eSGeorge Liu         if (ec == boost::asio::error::operation_aborted)
131d7be555eSGeorge Liu         {
1324ab892a4SGeorge Liu             lg2::error("Failed to reschedule");
133d7be555eSGeorge Liu             return;
134d7be555eSGeorge Liu         }
135d7be555eSGeorge Liu         std::shared_ptr<IntelCPUSensor> self = weakRef.lock();
136d7be555eSGeorge Liu 
137d7be555eSGeorge Liu         if (self)
138d7be555eSGeorge Liu         {
139d7be555eSGeorge Liu             self->setupRead();
140d7be555eSGeorge Liu         }
141d7be555eSGeorge Liu     });
142d7be555eSGeorge Liu }
143d7be555eSGeorge Liu 
setupRead()144d7be555eSGeorge Liu void IntelCPUSensor::setupRead()
145d7be555eSGeorge Liu {
146d7be555eSGeorge Liu     if (readingStateGood())
147d7be555eSGeorge Liu     {
148d7be555eSGeorge Liu         inputDev.close();
149d7be555eSGeorge Liu 
150d7be555eSGeorge Liu         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
151d7be555eSGeorge Liu         fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
152d7be555eSGeorge Liu         if (fd < 0)
153d7be555eSGeorge Liu         {
1544ab892a4SGeorge Liu             lg2::error("'{NAME}' unable to open fd!", "NAME", name);
155d7be555eSGeorge Liu             return;
156d7be555eSGeorge Liu         }
157d7be555eSGeorge Liu 
158d7be555eSGeorge Liu         inputDev.assign(fd);
159d7be555eSGeorge Liu     }
160d7be555eSGeorge Liu     else
161d7be555eSGeorge Liu     {
162d7be555eSGeorge Liu         markAvailable(false);
163*9b992939SKonstantin Aladyshev         if (show)
164*9b992939SKonstantin Aladyshev         {
165d7be555eSGeorge Liu             updateValue(std::numeric_limits<double>::quiet_NaN());
166*9b992939SKonstantin Aladyshev         }
167*9b992939SKonstantin Aladyshev         else
168*9b992939SKonstantin Aladyshev         {
169*9b992939SKonstantin Aladyshev             value = std::numeric_limits<double>::quiet_NaN();
170*9b992939SKonstantin Aladyshev         }
171d7be555eSGeorge Liu         restartRead();
172d7be555eSGeorge Liu         return;
173d7be555eSGeorge Liu     }
174d7be555eSGeorge Liu 
175d7be555eSGeorge Liu     std::weak_ptr<IntelCPUSensor> weakRef = weak_from_this();
176d7be555eSGeorge Liu     inputDev.async_wait(boost::asio::posix::descriptor_base::wait_read,
177d7be555eSGeorge Liu                         [weakRef](const boost::system::error_code& ec) {
178d7be555eSGeorge Liu                             std::shared_ptr<IntelCPUSensor> self =
179d7be555eSGeorge Liu                                 weakRef.lock();
180d7be555eSGeorge Liu 
181d7be555eSGeorge Liu                             if (self)
182d7be555eSGeorge Liu                             {
183d7be555eSGeorge Liu                                 self->handleResponse(ec);
184d7be555eSGeorge Liu                             }
185d7be555eSGeorge Liu                         });
186d7be555eSGeorge Liu }
187d7be555eSGeorge Liu 
updateMinMaxValues()188d7be555eSGeorge Liu void IntelCPUSensor::updateMinMaxValues()
189d7be555eSGeorge Liu {
190d7be555eSGeorge Liu     const boost::container::flat_map<
191d7be555eSGeorge Liu         std::string,
192d7be555eSGeorge Liu         std::vector<std::tuple<const char*, std::reference_wrapper<double>,
193d7be555eSGeorge Liu                                const char*>>>
194d7be555eSGeorge Liu         map = {
195d7be555eSGeorge Liu             {
196d7be555eSGeorge Liu                 "cap",
197d7be555eSGeorge Liu                 {
198d7be555eSGeorge Liu                     std::make_tuple("cap_max", std::ref(maxValue), "MaxValue"),
199d7be555eSGeorge Liu                     std::make_tuple("cap_min", std::ref(minValue), "MinValue"),
200d7be555eSGeorge Liu                 },
201d7be555eSGeorge Liu             },
202d7be555eSGeorge Liu         };
203d7be555eSGeorge Liu 
204d7be555eSGeorge Liu     if (auto fileParts = splitFileName(path))
205d7be555eSGeorge Liu     {
206d7be555eSGeorge Liu         auto& [fileType, fileNr, fileItem] = *fileParts;
207d7be555eSGeorge Liu         const auto mapIt = map.find(fileItem);
208d7be555eSGeorge Liu         if (mapIt != map.cend())
209d7be555eSGeorge Liu         {
210d7be555eSGeorge Liu             for (const auto& vectorItem : mapIt->second)
211d7be555eSGeorge Liu             {
212d7be555eSGeorge Liu                 const auto& [suffix, oldValue, dbusName] = vectorItem;
213d7be555eSGeorge Liu                 auto attrPath = boost::replace_all_copy(path, fileItem, suffix);
214d7be555eSGeorge Liu                 if (auto newVal =
215d7be555eSGeorge Liu                         readFile(attrPath, IntelCPUSensor::sensorScaleFactor))
216d7be555eSGeorge Liu                 {
217d7be555eSGeorge Liu                     updateProperty(sensorInterface, oldValue, *newVal,
218d7be555eSGeorge Liu                                    dbusName);
219d7be555eSGeorge Liu                 }
220d7be555eSGeorge Liu                 else
221d7be555eSGeorge Liu                 {
222d7be555eSGeorge Liu                     if (isPowerOn())
223d7be555eSGeorge Liu                     {
224d7be555eSGeorge Liu                         updateProperty(sensorInterface, oldValue, 0, dbusName);
225d7be555eSGeorge Liu                     }
226d7be555eSGeorge Liu                     else
227d7be555eSGeorge Liu                     {
228d7be555eSGeorge Liu                         updateProperty(sensorInterface, oldValue,
229d7be555eSGeorge Liu                                        std::numeric_limits<double>::quiet_NaN(),
230d7be555eSGeorge Liu                                        dbusName);
231d7be555eSGeorge Liu                     }
232d7be555eSGeorge Liu                 }
233d7be555eSGeorge Liu             }
234d7be555eSGeorge Liu         }
235d7be555eSGeorge Liu     }
236d7be555eSGeorge Liu }
237d7be555eSGeorge Liu 
handleResponse(const boost::system::error_code & err)238d7be555eSGeorge Liu void IntelCPUSensor::handleResponse(const boost::system::error_code& err)
239d7be555eSGeorge Liu {
240d7be555eSGeorge Liu     if ((err == boost::system::errc::bad_file_descriptor) ||
241d7be555eSGeorge Liu         (err == boost::asio::error::misc_errors::not_found))
242d7be555eSGeorge Liu     {
243d7be555eSGeorge Liu         return; // we're being destroyed
244d7be555eSGeorge Liu     }
245d7be555eSGeorge Liu     if (err == boost::system::errc::operation_canceled)
246d7be555eSGeorge Liu     {
247d7be555eSGeorge Liu         if (readingStateGood())
248d7be555eSGeorge Liu         {
249d7be555eSGeorge Liu             if (!loggedInterfaceDown)
250d7be555eSGeorge Liu             {
2514ab892a4SGeorge Liu                 lg2::error("'{NAME}' interface down!", "NAME", name);
252d7be555eSGeorge Liu                 loggedInterfaceDown = true;
253d7be555eSGeorge Liu             }
254d7be555eSGeorge Liu             pollTime = static_cast<size_t>(IntelCPUSensor::sensorPollMs) * 10U;
255d7be555eSGeorge Liu             markFunctional(false);
256d7be555eSGeorge Liu         }
257d7be555eSGeorge Liu         return;
258d7be555eSGeorge Liu     }
259d7be555eSGeorge Liu     loggedInterfaceDown = false;
260d7be555eSGeorge Liu 
261d7be555eSGeorge Liu     if (err)
262d7be555eSGeorge Liu     {
263d7be555eSGeorge Liu         pollTime = sensorFailedPollTimeMs;
264d7be555eSGeorge Liu         incrementError();
265d7be555eSGeorge Liu         return;
266d7be555eSGeorge Liu     }
267d7be555eSGeorge Liu 
268d7be555eSGeorge Liu     static constexpr uint32_t bufLen = 128;
269d7be555eSGeorge Liu     std::string response;
270d7be555eSGeorge Liu     response.resize(bufLen);
271d7be555eSGeorge Liu     int rdLen = 0;
272d7be555eSGeorge Liu 
273d7be555eSGeorge Liu     if (fd >= 0)
274d7be555eSGeorge Liu     {
275d7be555eSGeorge Liu         rdLen = pread(fd, response.data(), bufLen, 0);
276d7be555eSGeorge Liu     }
277d7be555eSGeorge Liu 
278d7be555eSGeorge Liu     if (rdLen > 0)
279d7be555eSGeorge Liu     {
280d7be555eSGeorge Liu         try
281d7be555eSGeorge Liu         {
282d7be555eSGeorge Liu             rawValue = std::stod(response);
283d7be555eSGeorge Liu             double nvalue = rawValue / IntelCPUSensor::sensorScaleFactor;
284d7be555eSGeorge Liu 
285d7be555eSGeorge Liu             if (show)
286d7be555eSGeorge Liu             {
287d7be555eSGeorge Liu                 updateValue(nvalue);
288d7be555eSGeorge Liu             }
289d7be555eSGeorge Liu             else
290d7be555eSGeorge Liu             {
291d7be555eSGeorge Liu                 value = nvalue;
292d7be555eSGeorge Liu             }
293d7be555eSGeorge Liu             if (minMaxReadCounter++ % 8 == 0)
294d7be555eSGeorge Liu             {
295d7be555eSGeorge Liu                 updateMinMaxValues();
296d7be555eSGeorge Liu             }
297d7be555eSGeorge Liu 
298d7be555eSGeorge Liu             double gTcontrol = gCpuSensors[nameTcontrol]
299d7be555eSGeorge Liu                                    ? gCpuSensors[nameTcontrol]->value
300d7be555eSGeorge Liu                                    : std::numeric_limits<double>::quiet_NaN();
301d7be555eSGeorge Liu             if (gTcontrol != privTcontrol)
302d7be555eSGeorge Liu             {
303d7be555eSGeorge Liu                 privTcontrol = gTcontrol;
304d7be555eSGeorge Liu 
305d7be555eSGeorge Liu                 if (!thresholds.empty())
306d7be555eSGeorge Liu                 {
307d7be555eSGeorge Liu                     std::vector<thresholds::Threshold> newThresholds;
308d7be555eSGeorge Liu                     if (parseThresholdsFromAttr(
309d7be555eSGeorge Liu                             newThresholds, path,
310d7be555eSGeorge Liu                             IntelCPUSensor::sensorScaleFactor, dtsOffset, 0))
311d7be555eSGeorge Liu                     {
312d7be555eSGeorge Liu                         if (!std::equal(thresholds.begin(), thresholds.end(),
313d7be555eSGeorge Liu                                         newThresholds.begin(),
314d7be555eSGeorge Liu                                         newThresholds.end()))
315d7be555eSGeorge Liu                         {
316d7be555eSGeorge Liu                             thresholds = newThresholds;
317d7be555eSGeorge Liu                             if (show)
318d7be555eSGeorge Liu                             {
319d7be555eSGeorge Liu                                 thresholds::updateThresholds(this);
320d7be555eSGeorge Liu                             }
321d7be555eSGeorge Liu                         }
322d7be555eSGeorge Liu                     }
323d7be555eSGeorge Liu                     else
324d7be555eSGeorge Liu                     {
3254ab892a4SGeorge Liu                         lg2::error("Failure to update thresholds for '{NAME}'",
3264ab892a4SGeorge Liu                                    "NAME", name);
327d7be555eSGeorge Liu                     }
328d7be555eSGeorge Liu                 }
329d7be555eSGeorge Liu             }
330d7be555eSGeorge Liu         }
331d7be555eSGeorge Liu         catch (const std::invalid_argument&)
332d7be555eSGeorge Liu         {
333d7be555eSGeorge Liu             incrementError();
334d7be555eSGeorge Liu         }
335d7be555eSGeorge Liu     }
336d7be555eSGeorge Liu     else
337d7be555eSGeorge Liu     {
338d7be555eSGeorge Liu         pollTime = sensorFailedPollTimeMs;
339d7be555eSGeorge Liu         incrementError();
340d7be555eSGeorge Liu     }
341d7be555eSGeorge Liu     restartRead();
342d7be555eSGeorge Liu }
343d7be555eSGeorge Liu 
checkThresholds()344d7be555eSGeorge Liu void IntelCPUSensor::checkThresholds()
345d7be555eSGeorge Liu {
346d7be555eSGeorge Liu     if (show)
347d7be555eSGeorge Liu     {
348d7be555eSGeorge Liu         thresholds::checkThresholds(this);
349d7be555eSGeorge Liu     }
350d7be555eSGeorge Liu }
351