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