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 <sdbusplus/asio/connection.hpp>
33 #include <sdbusplus/asio/object_server.hpp>
34 
35 #include <algorithm>
36 #include <chrono>
37 #include <cstddef>
38 #include <cstdint>
39 #include <functional>
40 #include <iostream>
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), pollTime(IntelCPUSensor::sensorPollMs)
63 
64 {
65     if (show)
66     {
67         if (auto fileParts = splitFileName(path))
68         {
69             auto& [type, nr, item] = *fileParts;
70             std::string interfacePath;
71             const char* units = nullptr;
72             if (type == "power")
73             {
74                 interfacePath = "/xyz/openbmc_project/sensors/power/" + name;
75                 units = sensor_paths::unitWatts;
76                 minValue = 0;
77                 maxValue = 511;
78             }
79             else
80             {
81                 interfacePath = "/xyz/openbmc_project/sensors/temperature/" +
82                                 name;
83                 units = sensor_paths::unitDegreesC;
84                 minValue = -128;
85                 maxValue = 127;
86             }
87 
88             sensorInterface = objectServer.add_interface(
89                 interfacePath, "xyz.openbmc_project.Sensor.Value");
90             for (const auto& threshold : thresholds)
91             {
92                 std::string interface =
93                     thresholds::getInterface(threshold.level);
94                 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
95                     objectServer.add_interface(interfacePath, interface);
96             }
97             association = objectServer.add_interface(interfacePath,
98                                                      association::interface);
99 
100             setInitialProperties(units);
101         }
102     }
103 
104     // call setup always as not all sensors call setInitialProperties
105     setupPowerMatch(conn);
106 }
107 
~IntelCPUSensor()108 IntelCPUSensor::~IntelCPUSensor()
109 {
110     // close the input dev to cancel async operations
111     inputDev.close();
112     waitTimer.cancel();
113     if (show)
114     {
115         for (const auto& iface : thresholdInterfaces)
116         {
117             objServer.remove_interface(iface);
118         }
119         objServer.remove_interface(sensorInterface);
120         objServer.remove_interface(association);
121         objServer.remove_interface(availableInterface);
122         objServer.remove_interface(operationalInterface);
123     }
124 }
125 
restartRead()126 void IntelCPUSensor::restartRead()
127 {
128     std::weak_ptr<IntelCPUSensor> weakRef = weak_from_this();
129     waitTimer.expires_after(std::chrono::milliseconds(pollTime));
130     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
131         if (ec == boost::asio::error::operation_aborted)
132         {
133             std::cerr << "Failed to reschedule\n";
134             return;
135         }
136         std::shared_ptr<IntelCPUSensor> self = weakRef.lock();
137 
138         if (self)
139         {
140             self->setupRead();
141         }
142     });
143 }
144 
setupRead()145 void IntelCPUSensor::setupRead()
146 {
147     if (readingStateGood())
148     {
149         inputDev.close();
150 
151         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
152         fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
153         if (fd < 0)
154         {
155             std::cerr << name << " unable to open fd!\n";
156             return;
157         }
158 
159         inputDev.assign(fd);
160     }
161     else
162     {
163         markAvailable(false);
164         updateValue(std::numeric_limits<double>::quiet_NaN());
165         restartRead();
166         return;
167     }
168 
169     std::weak_ptr<IntelCPUSensor> weakRef = weak_from_this();
170     inputDev.async_wait(boost::asio::posix::descriptor_base::wait_read,
171                         [weakRef](const boost::system::error_code& ec) {
172         std::shared_ptr<IntelCPUSensor> self = weakRef.lock();
173 
174         if (self)
175         {
176             self->handleResponse(ec);
177         }
178     });
179 }
180 
updateMinMaxValues()181 void IntelCPUSensor::updateMinMaxValues()
182 {
183     const boost::container::flat_map<
184         std::string,
185         std::vector<std::tuple<const char*, std::reference_wrapper<double>,
186                                const char*>>>
187         map = {
188             {
189                 "cap",
190                 {
191                     std::make_tuple("cap_max", std::ref(maxValue), "MaxValue"),
192                     std::make_tuple("cap_min", std::ref(minValue), "MinValue"),
193                 },
194             },
195         };
196 
197     if (auto fileParts = splitFileName(path))
198     {
199         auto& [fileType, fileNr, fileItem] = *fileParts;
200         const auto mapIt = map.find(fileItem);
201         if (mapIt != map.cend())
202         {
203             for (const auto& vectorItem : mapIt->second)
204             {
205                 const auto& [suffix, oldValue, dbusName] = vectorItem;
206                 auto attrPath = boost::replace_all_copy(path, fileItem, suffix);
207                 if (auto newVal = readFile(attrPath,
208                                            IntelCPUSensor::sensorScaleFactor))
209                 {
210                     updateProperty(sensorInterface, oldValue, *newVal,
211                                    dbusName);
212                 }
213                 else
214                 {
215                     if (isPowerOn())
216                     {
217                         updateProperty(sensorInterface, oldValue, 0, dbusName);
218                     }
219                     else
220                     {
221                         updateProperty(sensorInterface, oldValue,
222                                        std::numeric_limits<double>::quiet_NaN(),
223                                        dbusName);
224                     }
225                 }
226             }
227         }
228     }
229 }
230 
handleResponse(const boost::system::error_code & err)231 void IntelCPUSensor::handleResponse(const boost::system::error_code& err)
232 {
233     if ((err == boost::system::errc::bad_file_descriptor) ||
234         (err == boost::asio::error::misc_errors::not_found))
235     {
236         return; // we're being destroyed
237     }
238     if (err == boost::system::errc::operation_canceled)
239     {
240         if (readingStateGood())
241         {
242             if (!loggedInterfaceDown)
243             {
244                 std::cerr << name << " interface down!\n";
245                 loggedInterfaceDown = true;
246             }
247             pollTime = static_cast<size_t>(IntelCPUSensor::sensorPollMs) * 10U;
248             markFunctional(false);
249         }
250         return;
251     }
252     loggedInterfaceDown = false;
253 
254     if (err)
255     {
256         pollTime = sensorFailedPollTimeMs;
257         incrementError();
258         return;
259     }
260 
261     static constexpr uint32_t bufLen = 128;
262     std::string response;
263     response.resize(bufLen);
264     int rdLen = 0;
265 
266     if (fd >= 0)
267     {
268         rdLen = pread(fd, response.data(), bufLen, 0);
269     }
270 
271     if (rdLen > 0)
272     {
273         try
274         {
275             rawValue = std::stod(response);
276             double nvalue = rawValue / IntelCPUSensor::sensorScaleFactor;
277 
278             if (show)
279             {
280                 updateValue(nvalue);
281             }
282             else
283             {
284                 value = nvalue;
285             }
286             if (minMaxReadCounter++ % 8 == 0)
287             {
288                 updateMinMaxValues();
289             }
290 
291             double gTcontrol = gCpuSensors[nameTcontrol]
292                                    ? gCpuSensors[nameTcontrol]->value
293                                    : std::numeric_limits<double>::quiet_NaN();
294             if (gTcontrol != privTcontrol)
295             {
296                 privTcontrol = gTcontrol;
297 
298                 if (!thresholds.empty())
299                 {
300                     std::vector<thresholds::Threshold> newThresholds;
301                     if (parseThresholdsFromAttr(
302                             newThresholds, path,
303                             IntelCPUSensor::sensorScaleFactor, dtsOffset, 0))
304                     {
305                         if (!std::equal(thresholds.begin(), thresholds.end(),
306                                         newThresholds.begin(),
307                                         newThresholds.end()))
308                         {
309                             thresholds = newThresholds;
310                             if (show)
311                             {
312                                 thresholds::updateThresholds(this);
313                             }
314                         }
315                     }
316                     else
317                     {
318                         std::cerr << "Failure to update thresholds for " << name
319                                   << "\n";
320                     }
321                 }
322             }
323         }
324         catch (const std::invalid_argument&)
325         {
326             incrementError();
327         }
328     }
329     else
330     {
331         pollTime = sensorFailedPollTimeMs;
332         incrementError();
333     }
334     restartRead();
335 }
336 
checkThresholds()337 void IntelCPUSensor::checkThresholds()
338 {
339     if (show)
340     {
341         thresholds::checkThresholds(this);
342     }
343 }
344