blob: f4d4ed05e43cc5cb219393eeb946ddfab5e38096 [file] [log] [blame]
Patrick Ventureca44b2f2019-10-31 11:02:26 -07001#include "Thresholds.hpp"
2
3#include "VariantVisitors.hpp"
4#include "sensor.hpp"
5
James Feist6714a252018-09-10 15:26:18 -07006#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -07007#include <boost/container/flat_map.hpp>
James Feist6714a252018-09-10 15:26:18 -07008#include <boost/lexical_cast.hpp>
James Feist38fb5982020-05-28 10:09:54 -07009
10#include <array>
James Feistdc6c55f2018-10-31 12:53:20 -070011#include <cmath>
James Feist6714a252018-09-10 15:26:18 -070012#include <fstream>
13#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070014#include <memory>
15#include <stdexcept>
16#include <string>
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +020017#include <tuple>
Patrick Venture96e97db2019-10-31 13:44:38 -070018#include <utility>
19#include <variant>
20#include <vector>
James Feist6714a252018-09-10 15:26:18 -070021
22static constexpr bool DEBUG = false;
James Feist6714a252018-09-10 15:26:18 -070023namespace thresholds
24{
James Feistd8705872019-02-08 13:26:09 -080025unsigned int toBusValue(const Level& level)
James Feist6714a252018-09-10 15:26:18 -070026{
27 switch (level)
28 {
29 case (Level::WARNING):
30 {
31 return 0;
32 }
33 case (Level::CRITICAL):
34 {
35 return 1;
36 }
37 default:
38 {
39 return -1;
40 }
41 }
42}
43
James Feistd8705872019-02-08 13:26:09 -080044std::string toBusValue(const Direction& direction)
James Feist6714a252018-09-10 15:26:18 -070045{
46 switch (direction)
47 {
48 case (Direction::LOW):
49 {
50 return "less than";
51 }
52 case (Direction::HIGH):
53 {
54 return "greater than";
55 }
56 default:
57 {
58 return "err";
59 }
60 }
61}
62
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070063bool parseThresholdsFromConfig(
James Feistd8705872019-02-08 13:26:09 -080064 const SensorData& sensorData,
65 std::vector<thresholds::Threshold>& thresholdVector,
66 const std::string* matchLabel)
James Feist6714a252018-09-10 15:26:18 -070067{
James Feistd8705872019-02-08 13:26:09 -080068 for (const auto& item : sensorData)
James Feist6714a252018-09-10 15:26:18 -070069 {
70 if (item.first.find("Thresholds") == std::string::npos)
71 {
72 continue;
73 }
74 if (matchLabel != nullptr)
75 {
76 auto labelFind = item.second.find("Label");
77 if (labelFind == item.second.end())
78 continue;
James Feist3eb82622019-02-08 13:10:22 -080079 if (std::visit(VariantToStringVisitor(), labelFind->second) !=
80 *matchLabel)
James Feist6714a252018-09-10 15:26:18 -070081 continue;
82 }
83 auto directionFind = item.second.find("Direction");
84 auto severityFind = item.second.find("Severity");
85 auto valueFind = item.second.find("Value");
86 if (valueFind == item.second.end() ||
87 severityFind == item.second.end() ||
88 directionFind == item.second.end())
89 {
90 std::cerr << "Malformed threshold in configuration\n";
91 return false;
92 }
93 Level level;
94 Direction direction;
James Feist3eb82622019-02-08 13:10:22 -080095 if (std::visit(VariantToUnsignedIntVisitor(), severityFind->second) ==
96 0)
James Feist6714a252018-09-10 15:26:18 -070097 {
98 level = Level::WARNING;
99 }
100 else
101 {
102 level = Level::CRITICAL;
103 }
James Feist3eb82622019-02-08 13:10:22 -0800104 if (std::visit(VariantToStringVisitor(), directionFind->second) ==
105 "less than")
James Feist6714a252018-09-10 15:26:18 -0700106 {
107 direction = Direction::LOW;
108 }
109 else
110 {
111 direction = Direction::HIGH;
112 }
James Feist13f340b2019-03-07 16:36:11 -0800113 double val = std::visit(VariantToDoubleVisitor(), valueFind->second);
James Feist6714a252018-09-10 15:26:18 -0700114
115 thresholdVector.emplace_back(level, direction, val);
116 }
117 return true;
118}
119
James Feistd8705872019-02-08 13:26:09 -0800120void persistThreshold(const std::string& path, const std::string& baseInterface,
121 const thresholds::Threshold& threshold,
James Feista222ba72019-03-01 15:57:51 -0800122 std::shared_ptr<sdbusplus::asio::connection>& conn,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800123 size_t thresholdCount, const std::string& labelMatch)
James Feist6714a252018-09-10 15:26:18 -0700124{
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500125 for (size_t ii = 0; ii < thresholdCount; ii++)
James Feist6714a252018-09-10 15:26:18 -0700126 {
127 std::string thresholdInterface =
128 baseInterface + ".Thresholds" + std::to_string(ii);
129 conn->async_method_call(
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800130 [&, path, threshold, thresholdInterface, labelMatch](
James Feistd8705872019-02-08 13:26:09 -0800131 const boost::system::error_code& ec,
132 const boost::container::flat_map<std::string, BasicVariantType>&
133 result) {
James Feist6714a252018-09-10 15:26:18 -0700134 if (ec)
135 {
136 return; // threshold not supported
137 }
138
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800139 if (!labelMatch.empty())
140 {
141 auto labelFind = result.find("Label");
142 if (labelFind == result.end())
143 {
144 std::cerr << "No label in threshold configuration\n";
145 return;
146 }
147 std::string label =
148 std::visit(VariantToStringVisitor(), labelFind->second);
149 if (label != labelMatch)
150 {
151 return;
152 }
153 }
154
James Feist6714a252018-09-10 15:26:18 -0700155 auto directionFind = result.find("Direction");
156 auto severityFind = result.find("Severity");
157 auto valueFind = result.find("Value");
158 if (valueFind == result.end() || severityFind == result.end() ||
159 directionFind == result.end())
160 {
161 std::cerr << "Malformed threshold in configuration\n";
162 return;
163 }
James Feist3eb82622019-02-08 13:10:22 -0800164 unsigned int level = std::visit(VariantToUnsignedIntVisitor(),
165 severityFind->second);
James Feist6714a252018-09-10 15:26:18 -0700166
James Feist3eb82622019-02-08 13:10:22 -0800167 std::string dir =
168 std::visit(VariantToStringVisitor(), directionFind->second);
James Feist6714a252018-09-10 15:26:18 -0700169 if ((toBusValue(threshold.level) != level) ||
170 (toBusValue(threshold.direction) != dir))
171 {
172 return; // not the droid we're looking for
173 }
174
James Feist3eb82622019-02-08 13:10:22 -0800175 std::variant<double> value(threshold.value);
James Feist6714a252018-09-10 15:26:18 -0700176 conn->async_method_call(
James Feistd8705872019-02-08 13:26:09 -0800177 [](const boost::system::error_code& ec) {
James Feist6714a252018-09-10 15:26:18 -0700178 if (ec)
179 {
180 std::cerr << "Error setting threshold " << ec
181 << "\n";
182 }
183 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700184 entityManagerName, path, "org.freedesktop.DBus.Properties",
185 "Set", thresholdInterface, "Value", value);
James Feist6714a252018-09-10 15:26:18 -0700186 },
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700187 entityManagerName, path, "org.freedesktop.DBus.Properties",
James Feist6714a252018-09-10 15:26:18 -0700188 "GetAll", thresholdInterface);
189 }
190}
191
Jae Hyun Yoo95b8a2d2019-02-25 20:15:09 -0800192void updateThresholds(Sensor* sensor)
193{
194 if (sensor->thresholds.empty())
195 {
196 return;
197 }
198
199 for (const auto& threshold : sensor->thresholds)
200 {
201 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
202 std::string property;
203 if (threshold.level == thresholds::Level::CRITICAL)
204 {
205 interface = sensor->thresholdInterfaceCritical;
206 if (threshold.direction == thresholds::Direction::HIGH)
207 {
208 property = "CriticalHigh";
209 }
210 else
211 {
212 property = "CriticalLow";
213 }
214 }
215 else if (threshold.level == thresholds::Level::WARNING)
216 {
217 interface = sensor->thresholdInterfaceWarning;
218 if (threshold.direction == thresholds::Direction::HIGH)
219 {
220 property = "WarningHigh";
221 }
222 else
223 {
224 property = "WarningLow";
225 }
226 }
227 else
228 {
229 continue;
230 }
231 if (!interface)
232 {
233 continue;
234 }
235 interface->set_property(property, threshold.value);
236 }
237}
238
Josh Lehan883fb3a2020-02-27 14:41:39 -0800239// Debugging counters
240static int cHiTrue = 0;
241static int cHiFalse = 0;
242static int cHiMidstate = 0;
243static int cLoTrue = 0;
244static int cLoFalse = 0;
245static int cLoMidstate = 0;
246static int cDebugThrottle = 0;
Zhikui Rend3da1282020-09-11 17:02:01 -0700247static constexpr int assertLogCount = 10;
Josh Lehan883fb3a2020-02-27 14:41:39 -0800248
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700249struct ChangeParam
James Feist251c7822018-09-12 12:54:15 -0700250{
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700251 ChangeParam(Threshold whichThreshold, bool status, double value) :
252 threshold(whichThreshold), asserted(status), assertValue(value)
253 {}
254
255 Threshold threshold;
256 bool asserted;
257 double assertValue;
258};
259
260static std::vector<ChangeParam> checkThresholds(Sensor* sensor, double value)
261{
262 std::vector<ChangeParam> thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700263 if (sensor->thresholds.empty())
264 {
James Feist46342ec2019-03-06 14:03:41 -0800265 return thresholdChanges;
James Feist251c7822018-09-12 12:54:15 -0700266 }
James Feist46342ec2019-03-06 14:03:41 -0800267
James Feistd8705872019-02-08 13:26:09 -0800268 for (auto& threshold : sensor->thresholds)
James Feist251c7822018-09-12 12:54:15 -0700269 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800270 // Use "Schmitt trigger" logic to avoid threshold trigger spam,
271 // if value is noisy while hovering very close to a threshold.
272 // When a threshold is crossed, indicate true immediately,
273 // but require more distance to be crossed the other direction,
274 // before resetting the indicator back to false.
James Feist46342ec2019-03-06 14:03:41 -0800275 if (threshold.direction == thresholds::Direction::HIGH)
James Feistdc6c55f2018-10-31 12:53:20 -0700276 {
James Feist551087a2019-12-09 11:17:12 -0800277 if (value >= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700278 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700279 thresholdChanges.emplace_back(threshold, true, value);
Zhikui Rend3da1282020-09-11 17:02:01 -0700280 if (++cHiTrue < assertLogCount)
281 {
282 std::cerr << "Sensor " << sensor->name << " high threshold "
283 << threshold.value << " assert: value " << value
284 << " raw data " << sensor->rawValue << "\n";
285 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800286 }
287 else if (value < (threshold.value - sensor->hysteresisTrigger))
288 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700289 thresholdChanges.emplace_back(threshold, false, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800290 ++cHiFalse;
James Feist251c7822018-09-12 12:54:15 -0700291 }
Patrick Venture66235d42019-10-11 08:31:27 -0700292 else
James Feist251c7822018-09-12 12:54:15 -0700293 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800294 ++cHiMidstate;
James Feist251c7822018-09-12 12:54:15 -0700295 }
296 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800297 else if (threshold.direction == thresholds::Direction::LOW)
James Feist251c7822018-09-12 12:54:15 -0700298 {
James Feist551087a2019-12-09 11:17:12 -0800299 if (value <= threshold.value)
James Feist251c7822018-09-12 12:54:15 -0700300 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700301 thresholdChanges.emplace_back(threshold, true, value);
Zhikui Rend3da1282020-09-11 17:02:01 -0700302 if (++cLoTrue < assertLogCount)
303 {
304 std::cerr << "Sensor " << sensor->name << " low threshold "
305 << threshold.value << " assert: value "
306 << sensor->value << " raw data "
307 << sensor->rawValue << "\n";
308 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800309 }
310 else if (value > (threshold.value + sensor->hysteresisTrigger))
311 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700312 thresholdChanges.emplace_back(threshold, false, value);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800313 ++cLoFalse;
James Feist251c7822018-09-12 12:54:15 -0700314 }
Patrick Venture66235d42019-10-11 08:31:27 -0700315 else
James Feist251c7822018-09-12 12:54:15 -0700316 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800317 ++cLoMidstate;
James Feist251c7822018-09-12 12:54:15 -0700318 }
319 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800320 else
321 {
322 std::cerr << "Error determining threshold direction\n";
323 }
James Feist251c7822018-09-12 12:54:15 -0700324 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800325
326 if constexpr (DEBUG)
327 {
328 // Throttle debug output, so that it does not continuously spam
329 ++cDebugThrottle;
330 if (cDebugThrottle >= 1000)
331 {
332 cDebugThrottle = 0;
333 std::cerr << "checkThresholds: High T=" << cHiTrue
334 << " F=" << cHiFalse << " M=" << cHiMidstate
335 << ", Low T=" << cLoTrue << " F=" << cLoFalse
336 << " M=" << cLoMidstate << "\n";
337 }
338 }
339
James Feist46342ec2019-03-06 14:03:41 -0800340 return thresholdChanges;
341}
342
343bool checkThresholds(Sensor* sensor)
344{
James Feist7b18b1e2019-05-14 13:42:09 -0700345 bool status = true;
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700346 std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value);
347 for (const auto& change : changes)
James Feist46342ec2019-03-06 14:03:41 -0800348 {
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700349 assertThresholds(sensor, change.assertValue, change.threshold.level,
350 change.threshold.direction, change.asserted);
351 if (change.threshold.level == thresholds::Level::CRITICAL &&
352 change.asserted)
James Feist46342ec2019-03-06 14:03:41 -0800353 {
James Feist7b18b1e2019-05-14 13:42:09 -0700354 status = false;
James Feist46342ec2019-03-06 14:03:41 -0800355 }
356 }
357
James Feistdc6c55f2018-10-31 12:53:20 -0700358 return status;
James Feist251c7822018-09-12 12:54:15 -0700359}
360
James Feist46342ec2019-03-06 14:03:41 -0800361void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
362{
363
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700364 std::vector<ChangeParam> changes = checkThresholds(sensor, sensor->value);
365 for (const auto& change : changes)
James Feist46342ec2019-03-06 14:03:41 -0800366 {
Zhikui Renbf7cbc82020-07-02 08:44:00 -0700367 // When CPU is powered off, some volatges are expected to
368 // go below low thresholds. Filter these events with thresholdTimer.
369 // 1. always delay the assertion of low events to see if they are
370 // caused by power off event.
371 // 2. conditional delay the de-assertion of low events if there is
372 // an existing timer for assertion.
373 // 3. no delays for de-assert of low events if there is an existing
374 // de-assert for low event. This means 2nd de-assert would happen
375 // first and when timer expires for the previous one, no additional
376 // signal will be logged.
377 // 4. no delays for all high events.
378 if (change.threshold.direction == thresholds::Direction::LOW)
James Feist46342ec2019-03-06 14:03:41 -0800379 {
Zhikui Renbf7cbc82020-07-02 08:44:00 -0700380 if (change.asserted || thresholdTimer.hasActiveTimer(
381 change.threshold, !change.asserted))
382 {
383 thresholdTimer.startTimer(change.threshold, change.asserted,
384 change.assertValue);
385 continue;
386 }
James Feist46342ec2019-03-06 14:03:41 -0800387 }
Zhikui Renbf7cbc82020-07-02 08:44:00 -0700388 assertThresholds(sensor, change.assertValue, change.threshold.level,
389 change.threshold.direction, change.asserted);
James Feist46342ec2019-03-06 14:03:41 -0800390 }
391}
392
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700393void assertThresholds(Sensor* sensor, double assertValue,
394 thresholds::Level level, thresholds::Direction direction,
395 bool assert)
James Feist251c7822018-09-12 12:54:15 -0700396{
397 std::string property;
398 std::shared_ptr<sdbusplus::asio::dbus_interface> interface;
399 if (level == thresholds::Level::WARNING &&
400 direction == thresholds::Direction::HIGH)
401 {
402 property = "WarningAlarmHigh";
403 interface = sensor->thresholdInterfaceWarning;
404 }
405 else if (level == thresholds::Level::WARNING &&
406 direction == thresholds::Direction::LOW)
407 {
408 property = "WarningAlarmLow";
409 interface = sensor->thresholdInterfaceWarning;
410 }
411 else if (level == thresholds::Level::CRITICAL &&
412 direction == thresholds::Direction::HIGH)
413 {
414 property = "CriticalAlarmHigh";
415 interface = sensor->thresholdInterfaceCritical;
416 }
417 else if (level == thresholds::Level::CRITICAL &&
418 direction == thresholds::Direction::LOW)
419 {
420 property = "CriticalAlarmLow";
421 interface = sensor->thresholdInterfaceCritical;
422 }
423 else
424 {
425 std::cerr << "Unknown threshold, level " << level << "direction "
426 << direction << "\n";
427 return;
428 }
429 if (!interface)
430 {
431 std::cout << "trying to set uninitialized interface\n";
432 return;
433 }
Zhikui Ren59b8b9e2020-06-26 18:34:22 -0700434
435 if (interface->set_property<bool, true>(property, assert))
436 {
437 try
438 {
439 // msg.get_path() is interface->get_object_path()
440 sdbusplus::message::message msg =
441 interface->new_signal("ThresholdAsserted");
442
443 msg.append(sensor->name, interface->get_interface_name(), property,
444 assert, assertValue);
445 msg.signal_send();
446 }
447 catch (const sdbusplus::exception::exception& e)
448 {
449 std::cerr
450 << "Failed to send thresholdAsserted signal with assertValue\n";
451 }
452 }
James Feist251c7822018-09-12 12:54:15 -0700453}
454
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700455bool parseThresholdsFromAttr(
James Feistd8705872019-02-08 13:26:09 -0800456 std::vector<thresholds::Threshold>& thresholdVector,
Vijay Khemka86dea2b2019-06-06 11:14:37 -0700457 const std::string& inputPath, const double& scaleFactor,
458 const double& offset)
James Feist6714a252018-09-10 15:26:18 -0700459{
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200460 const boost::container::flat_map<
461 std::string, std::vector<std::tuple<const char*, thresholds::Level,
462 thresholds::Direction, double>>>
463 map = {
464 {"average",
465 {
466 std::make_tuple("average_min", Level::WARNING, Direction::LOW,
467 0.0),
468 std::make_tuple("average_max", Level::WARNING, Direction::HIGH,
469 0.0),
470 }},
471 {"input",
472 {
473 std::make_tuple("min", Level::WARNING, Direction::LOW, 0.0),
474 std::make_tuple("max", Level::WARNING, Direction::HIGH, 0.0),
475 std::make_tuple("lcrit", Level::CRITICAL, Direction::LOW, 0.0),
476 std::make_tuple("crit", Level::CRITICAL, Direction::HIGH,
477 offset),
478 }},
479 };
480
481 if (auto fileParts = splitFileName(inputPath))
James Feist6714a252018-09-10 15:26:18 -0700482 {
Zbigniew Kurzynskidbfd4662020-09-28 18:06:00 +0200483 auto& [type, nr, item] = *fileParts;
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200484 if (map.count(item) != 0)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700485 {
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200486 for (const auto& t : map.at(item))
487 {
Zbigniew Kurzynskidbfd4662020-09-28 18:06:00 +0200488 auto& [suffix, level, direction, offset] = t;
Zbigniew Kurzynski0a4c4802020-04-01 11:22:27 +0200489 auto attrPath =
490 boost::replace_all_copy(inputPath, item, suffix);
491 if (auto val = readFile(attrPath, scaleFactor))
492 {
493 *val += offset;
494 if (DEBUG)
495 {
496 std::cout << "Threshold: " << attrPath << ": " << *val
497 << "\n";
498 }
499 thresholdVector.emplace_back(level, direction, *val);
500 }
501 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700502 }
James Feist6714a252018-09-10 15:26:18 -0700503 }
James Feist6714a252018-09-10 15:26:18 -0700504 return true;
505}
506
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700507bool hasCriticalInterface(
James Feistd8705872019-02-08 13:26:09 -0800508 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700509{
James Feistd8705872019-02-08 13:26:09 -0800510 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700511 {
512 if (threshold.level == Level::CRITICAL)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700513 {
James Feist6714a252018-09-10 15:26:18 -0700514 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700515 }
James Feist6714a252018-09-10 15:26:18 -0700516 }
517 return false;
518}
519
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700520bool hasWarningInterface(
James Feistd8705872019-02-08 13:26:09 -0800521 const std::vector<thresholds::Threshold>& thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700522{
James Feistd8705872019-02-08 13:26:09 -0800523 for (auto& threshold : thresholdVector)
James Feist6714a252018-09-10 15:26:18 -0700524 {
525 if (threshold.level == Level::WARNING)
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700526 {
James Feist6714a252018-09-10 15:26:18 -0700527 return true;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700528 }
James Feist6714a252018-09-10 15:26:18 -0700529 }
530 return false;
531}
532} // namespace thresholds