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