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