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 =
173 weakRef.lock();
174
175 if (self)
176 {
177 self->handleResponse(ec);
178 }
179 });
180 }
181
updateMinMaxValues()182 void IntelCPUSensor::updateMinMaxValues()
183 {
184 const boost::container::flat_map<
185 std::string,
186 std::vector<std::tuple<const char*, std::reference_wrapper<double>,
187 const char*>>>
188 map = {
189 {
190 "cap",
191 {
192 std::make_tuple("cap_max", std::ref(maxValue), "MaxValue"),
193 std::make_tuple("cap_min", std::ref(minValue), "MinValue"),
194 },
195 },
196 };
197
198 if (auto fileParts = splitFileName(path))
199 {
200 auto& [fileType, fileNr, fileItem] = *fileParts;
201 const auto mapIt = map.find(fileItem);
202 if (mapIt != map.cend())
203 {
204 for (const auto& vectorItem : mapIt->second)
205 {
206 const auto& [suffix, oldValue, dbusName] = vectorItem;
207 auto attrPath = boost::replace_all_copy(path, fileItem, suffix);
208 if (auto newVal =
209 readFile(attrPath, IntelCPUSensor::sensorScaleFactor))
210 {
211 updateProperty(sensorInterface, oldValue, *newVal,
212 dbusName);
213 }
214 else
215 {
216 if (isPowerOn())
217 {
218 updateProperty(sensorInterface, oldValue, 0, dbusName);
219 }
220 else
221 {
222 updateProperty(sensorInterface, oldValue,
223 std::numeric_limits<double>::quiet_NaN(),
224 dbusName);
225 }
226 }
227 }
228 }
229 }
230 }
231
handleResponse(const boost::system::error_code & err)232 void IntelCPUSensor::handleResponse(const boost::system::error_code& err)
233 {
234 if ((err == boost::system::errc::bad_file_descriptor) ||
235 (err == boost::asio::error::misc_errors::not_found))
236 {
237 return; // we're being destroyed
238 }
239 if (err == boost::system::errc::operation_canceled)
240 {
241 if (readingStateGood())
242 {
243 if (!loggedInterfaceDown)
244 {
245 std::cerr << name << " interface down!\n";
246 loggedInterfaceDown = true;
247 }
248 pollTime = static_cast<size_t>(IntelCPUSensor::sensorPollMs) * 10U;
249 markFunctional(false);
250 }
251 return;
252 }
253 loggedInterfaceDown = false;
254
255 if (err)
256 {
257 pollTime = sensorFailedPollTimeMs;
258 incrementError();
259 return;
260 }
261
262 static constexpr uint32_t bufLen = 128;
263 std::string response;
264 response.resize(bufLen);
265 int rdLen = 0;
266
267 if (fd >= 0)
268 {
269 rdLen = pread(fd, response.data(), bufLen, 0);
270 }
271
272 if (rdLen > 0)
273 {
274 try
275 {
276 rawValue = std::stod(response);
277 double nvalue = rawValue / IntelCPUSensor::sensorScaleFactor;
278
279 if (show)
280 {
281 updateValue(nvalue);
282 }
283 else
284 {
285 value = nvalue;
286 }
287 if (minMaxReadCounter++ % 8 == 0)
288 {
289 updateMinMaxValues();
290 }
291
292 double gTcontrol = gCpuSensors[nameTcontrol]
293 ? gCpuSensors[nameTcontrol]->value
294 : std::numeric_limits<double>::quiet_NaN();
295 if (gTcontrol != privTcontrol)
296 {
297 privTcontrol = gTcontrol;
298
299 if (!thresholds.empty())
300 {
301 std::vector<thresholds::Threshold> newThresholds;
302 if (parseThresholdsFromAttr(
303 newThresholds, path,
304 IntelCPUSensor::sensorScaleFactor, dtsOffset, 0))
305 {
306 if (!std::equal(thresholds.begin(), thresholds.end(),
307 newThresholds.begin(),
308 newThresholds.end()))
309 {
310 thresholds = newThresholds;
311 if (show)
312 {
313 thresholds::updateThresholds(this);
314 }
315 }
316 }
317 else
318 {
319 std::cerr << "Failure to update thresholds for " << name
320 << "\n";
321 }
322 }
323 }
324 }
325 catch (const std::invalid_argument&)
326 {
327 incrementError();
328 }
329 }
330 else
331 {
332 pollTime = sensorFailedPollTimeMs;
333 incrementError();
334 }
335 restartRead();
336 }
337
checkThresholds()338 void IntelCPUSensor::checkThresholds()
339 {
340 if (show)
341 {
342 thresholds::checkThresholds(this);
343 }
344 }
345