blob: 6b20e778171b69d02a091b03a47061b9c41bf499 [file] [log] [blame]
Matt Spinler403d1f52021-02-01 15:35:25 -06001/**
2 * Copyright © 2021 IBM 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 */
Jerry C Chen35fb3a02024-08-30 14:54:30 +080016#include "config.h"
17
Matt Spinler403d1f52021-02-01 15:35:25 -060018#include "threshold_alarm_logger.hpp"
19
Matt Spinler50bf8162021-02-01 16:24:01 -060020#include "sdbusplus.hpp"
21
Matt Spinler3efec612021-05-11 15:26:17 -050022#include <unistd.h>
Matt Spinler50bf8162021-02-01 16:24:01 -060023
24#include <phosphor-logging/log.hpp>
25#include <xyz/openbmc_project/Logging/Entry/server.hpp>
26
Patrick Williamsfbf47032023-07-17 12:27:34 -050027#include <format>
28
Matt Spinler403d1f52021-02-01 15:35:25 -060029namespace sensor::monitor
30{
31
Matt Spinler50bf8162021-02-01 16:24:01 -060032using namespace sdbusplus::xyz::openbmc_project::Logging::server;
33using namespace phosphor::logging;
Matt Spinler66e75a72021-05-14 10:32:47 -050034using namespace phosphor::fan;
Matt Spinler50bf8162021-02-01 16:24:01 -060035using namespace phosphor::fan::util;
36
Matt Spinler403d1f52021-02-01 15:35:25 -060037const std::string warningInterface =
38 "xyz.openbmc_project.Sensor.Threshold.Warning";
39const std::string criticalInterface =
40 "xyz.openbmc_project.Sensor.Threshold.Critical";
41const std::string perfLossInterface =
42 "xyz.openbmc_project.Sensor.Threshold.PerformanceLoss";
Matt Spinler2f182672021-02-01 16:51:38 -060043constexpr auto loggingService = "xyz.openbmc_project.Logging";
44constexpr auto loggingPath = "/xyz/openbmc_project/logging";
45constexpr auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
46constexpr auto errorNameBase = "xyz.openbmc_project.Sensor.Threshold.Error.";
47constexpr auto valueInterface = "xyz.openbmc_project.Sensor.Value";
48constexpr auto assocInterface = "xyz.openbmc_project.Association";
Matt Spinler403d1f52021-02-01 15:35:25 -060049
Matt Spinler8ce65072022-11-03 15:15:55 -040050const std::vector<std::string> thresholdIfaceNames{
51 warningInterface, criticalInterface, perfLossInterface};
52
Delphine CC Chiu99914e52024-05-21 17:38:58 +080053using ErrorData = std::tuple<ErrorName, ErrorStatus, Entry::Level>;
Matt Spinler50bf8162021-02-01 16:24:01 -060054
55/**
56 * Map of threshold interfaces and alarm properties and values to error data.
57 */
58const std::map<InterfaceName, std::map<PropertyName, std::map<bool, ErrorData>>>
59 thresholdData{
60
61 {warningInterface,
62 {{"WarningAlarmHigh",
Delphine CC Chiu99914e52024-05-21 17:38:58 +080063 {{true, ErrorData{"WarningHigh", "", Entry::Level::Warning}},
Matt Spinler50bf8162021-02-01 16:24:01 -060064 {false,
Delphine CC Chiu99914e52024-05-21 17:38:58 +080065 ErrorData{"WarningHigh", "Clear", Entry::Level::Informational}}}},
Matt Spinler50bf8162021-02-01 16:24:01 -060066 {"WarningAlarmLow",
Delphine CC Chiu99914e52024-05-21 17:38:58 +080067 {{true, ErrorData{"WarningLow", "", Entry::Level::Warning}},
Matt Spinler50bf8162021-02-01 16:24:01 -060068 {false,
Delphine CC Chiu99914e52024-05-21 17:38:58 +080069 ErrorData{"WarningLow", "Clear", Entry::Level::Informational}}}}}},
Matt Spinler50bf8162021-02-01 16:24:01 -060070
71 {criticalInterface,
72 {{"CriticalAlarmHigh",
Delphine CC Chiu99914e52024-05-21 17:38:58 +080073 {{true, ErrorData{"CriticalHigh", "", Entry::Level::Critical}},
Matt Spinler50bf8162021-02-01 16:24:01 -060074 {false,
Delphine CC Chiu99914e52024-05-21 17:38:58 +080075 ErrorData{"CriticalHigh", "Clear", Entry::Level::Informational}}}},
Matt Spinler50bf8162021-02-01 16:24:01 -060076 {"CriticalAlarmLow",
Delphine CC Chiu99914e52024-05-21 17:38:58 +080077 {{true, ErrorData{"CriticalLow", "", Entry::Level::Critical}},
78 {false, ErrorData{"CriticalLow", "Clear",
79 Entry::Level::Informational}}}}}},
Matt Spinler50bf8162021-02-01 16:24:01 -060080
81 {perfLossInterface,
82 {{"PerfLossAlarmHigh",
Delphine CC Chiu99914e52024-05-21 17:38:58 +080083 {{true, ErrorData{"PerformanceLossHigh", "", Entry::Level::Warning}},
84 {false, ErrorData{"PerformanceLossHigh", "Clear",
85 Entry::Level::Informational}}}},
Matt Spinler50bf8162021-02-01 16:24:01 -060086 {"PerfLossAlarmLow",
Delphine CC Chiu99914e52024-05-21 17:38:58 +080087 {{true, ErrorData{"PerformanceLossLow", "", Entry::Level::Warning}},
88 {false, ErrorData{"PerformanceLossLow", "Clear",
89 Entry::Level::Informational}}}}}}};
Matt Spinler50bf8162021-02-01 16:24:01 -060090
Matt Spinler7f6946b2021-05-14 12:43:50 -050091ThresholdAlarmLogger::ThresholdAlarmLogger(
Patrick Williamscb356d42022-07-22 19:26:53 -050092 sdbusplus::bus_t& bus, sdeventplus::Event& event,
Matt Spinler7f6946b2021-05-14 12:43:50 -050093 std::shared_ptr<PowerState> powerState) :
Patrick Williamsdfddd642024-08-16 15:21:51 -040094 bus(bus), event(event), _powerState(std::move(powerState)),
Matt Spinler403d1f52021-02-01 15:35:25 -060095 warningMatch(bus,
96 "type='signal',member='PropertiesChanged',"
97 "path_namespace='/xyz/openbmc_project/sensors',"
98 "arg0='" +
99 warningInterface + "'",
100 std::bind(&ThresholdAlarmLogger::propertiesChanged, this,
101 std::placeholders::_1)),
102 criticalMatch(bus,
103 "type='signal',member='PropertiesChanged',"
104 "path_namespace='/xyz/openbmc_project/sensors',"
105 "arg0='" +
106 criticalInterface + "'",
107 std::bind(&ThresholdAlarmLogger::propertiesChanged, this,
108 std::placeholders::_1)),
109 perfLossMatch(bus,
110 "type='signal',member='PropertiesChanged',"
111 "path_namespace='/xyz/openbmc_project/sensors',"
112 "arg0='" +
113 perfLossInterface + "'",
114 std::bind(&ThresholdAlarmLogger::propertiesChanged, this,
Matt Spinlerb7a55402021-10-11 13:45:35 -0500115 std::placeholders::_1)),
116 ifacesRemovedMatch(bus,
117 "type='signal',member='InterfacesRemoved',arg0path="
118 "'/xyz/openbmc_project/sensors/'",
119 std::bind(&ThresholdAlarmLogger::interfacesRemoved, this,
Matt Spinler8ce65072022-11-03 15:15:55 -0400120 std::placeholders::_1)),
121 ifacesAddedMatch(bus,
122 "type='signal',member='InterfacesAdded',arg0path="
123 "'/xyz/openbmc_project/sensors/'",
124 std::bind(&ThresholdAlarmLogger::interfacesAdded, this,
125 std::placeholders::_1))
Matt Spinler50bf8162021-02-01 16:24:01 -0600126{
Matt Spinler7f6946b2021-05-14 12:43:50 -0500127 _powerState->addCallback("thresholdMon",
128 std::bind(&ThresholdAlarmLogger::powerStateChanged,
129 this, std::placeholders::_1));
130
Matt Spinler50bf8162021-02-01 16:24:01 -0600131 // check for any currently asserted threshold alarms
Patrick Williamsdfddd642024-08-16 15:21:51 -0400132 std::for_each(
133 thresholdData.begin(), thresholdData.end(),
134 [this](const auto& thresholdInterface) {
135 const auto& interface = thresholdInterface.first;
136 auto objects =
137 SDBusPlus::getSubTreeRaw(this->bus, "/", interface, 0);
138 std::for_each(objects.begin(), objects.end(),
139 [interface, this](const auto& object) {
140 const auto& path = object.first;
141 const auto& service =
142 object.second.begin()->first;
143 checkThresholds(interface, path, service);
144 });
Matt Spinler50bf8162021-02-01 16:24:01 -0600145 });
146}
Matt Spinler403d1f52021-02-01 15:35:25 -0600147
Patrick Williamscb356d42022-07-22 19:26:53 -0500148void ThresholdAlarmLogger::propertiesChanged(sdbusplus::message_t& msg)
Matt Spinler403d1f52021-02-01 15:35:25 -0600149{
Matt Spinlerf5d3be42021-02-01 16:38:01 -0600150 std::map<std::string, std::variant<bool>> properties;
151 std::string sensorPath = msg.get_path();
152 std::string interface;
153
154 msg.read(interface, properties);
155
Matt Spinler8ce65072022-11-03 15:15:55 -0400156 checkProperties(sensorPath, interface, properties);
157}
158
159void ThresholdAlarmLogger::interfacesRemoved(sdbusplus::message_t& msg)
160{
161 sdbusplus::message::object_path path;
162 std::vector<std::string> interfaces;
163
164 msg.read(path, interfaces);
165
166 for (const auto& interface : interfaces)
167 {
168 if (std::find(thresholdIfaceNames.begin(), thresholdIfaceNames.end(),
169 interface) != thresholdIfaceNames.end())
170 {
171 alarms.erase(InterfaceKey{path, interface});
172 }
173 }
174}
175
176void ThresholdAlarmLogger::interfacesAdded(sdbusplus::message_t& msg)
177{
178 sdbusplus::message::object_path path;
179 std::map<std::string, std::map<std::string, std::variant<bool>>> interfaces;
180
181 msg.read(path, interfaces);
182
183 for (const auto& [interface, properties] : interfaces)
184 {
185 if (std::find(thresholdIfaceNames.begin(), thresholdIfaceNames.end(),
186 interface) != thresholdIfaceNames.end())
187 {
188 checkProperties(path, interface, properties);
189 }
190 }
191}
192
193void ThresholdAlarmLogger::checkProperties(
194 const std::string& sensorPath, const std::string& interface,
195 const std::map<std::string, std::variant<bool>>& properties)
196{
Matt Spinlerf5d3be42021-02-01 16:38:01 -0600197 auto alarmProperties = thresholdData.find(interface);
198 if (alarmProperties == thresholdData.end())
199 {
200 return;
201 }
202
203 for (const auto& [propertyName, propertyValue] : properties)
204 {
205 if (alarmProperties->second.find(propertyName) !=
206 alarmProperties->second.end())
207 {
208 // If this is the first time we've seen this alarm, then
209 // assume it was off before so it doesn't create an event
210 // log for a value of false.
211
212 InterfaceKey key{sensorPath, interface};
213 if (alarms.find(key) == alarms.end())
214 {
215 alarms[key][propertyName] = false;
216 }
217
218 // Check if the value changed from what was there before.
219 auto alarmValue = std::get<bool>(propertyValue);
220 if (alarmValue != alarms[key][propertyName])
221 {
222 alarms[key][propertyName] = alarmValue;
Jerry C Chen35fb3a02024-08-30 14:54:30 +0800223#ifndef SKIP_POWER_CHECKING
Matt Spinler66e75a72021-05-14 10:32:47 -0500224 if (_powerState->isPowerOn())
Jerry C Chen35fb3a02024-08-30 14:54:30 +0800225#endif
Matt Spinler66e75a72021-05-14 10:32:47 -0500226 {
227 createEventLog(sensorPath, interface, propertyName,
228 alarmValue);
229 }
Matt Spinlerf5d3be42021-02-01 16:38:01 -0600230 }
231 }
232 }
Matt Spinler403d1f52021-02-01 15:35:25 -0600233}
234
Matt Spinler50bf8162021-02-01 16:24:01 -0600235void ThresholdAlarmLogger::checkThresholds(const std::string& interface,
236 const std::string& sensorPath,
237 const std::string& service)
238{
239 auto properties = thresholdData.find(interface);
240 if (properties == thresholdData.end())
241 {
242 return;
243 }
244
245 for (const auto& [property, unused] : properties->second)
246 {
247 try
248 {
249 auto alarmValue = SDBusPlus::getProperty<bool>(
250 bus, service, sensorPath, interface, property);
251 alarms[InterfaceKey(sensorPath, interface)][property] = alarmValue;
252
253 // This is just for checking alarms on startup,
254 // so only look for active alarms.
Jerry C Chen35fb3a02024-08-30 14:54:30 +0800255#ifdef SKIP_POWER_CHECKING
256 if (alarmValue)
257#else
Matt Spinler66e75a72021-05-14 10:32:47 -0500258 if (alarmValue && _powerState->isPowerOn())
Jerry C Chen35fb3a02024-08-30 14:54:30 +0800259#endif
Matt Spinler50bf8162021-02-01 16:24:01 -0600260 {
261 createEventLog(sensorPath, interface, property, alarmValue);
262 }
263 }
Patrick Williamscb356d42022-07-22 19:26:53 -0500264 catch (const sdbusplus::exception_t& e)
Matt Spinler50bf8162021-02-01 16:24:01 -0600265 {
Matt Spinler4b515922021-10-11 14:55:50 -0500266 // Sensor daemons that get their direction from entity manager
267 // may only be putting either the high alarm or low alarm on
268 // D-Bus, not both.
Matt Spinler50bf8162021-02-01 16:24:01 -0600269 continue;
270 }
271 }
272}
273
Patrick Williamsdfddd642024-08-16 15:21:51 -0400274void ThresholdAlarmLogger::createEventLog(
275 const std::string& sensorPath, const std::string& interface,
276 const std::string& alarmProperty, bool alarmValue)
Matt Spinler50bf8162021-02-01 16:24:01 -0600277{
Matt Spinler2f182672021-02-01 16:51:38 -0600278 std::map<std::string, std::string> ad;
279
280 auto type = getSensorType(sensorPath);
281 if (skipSensorType(type))
282 {
283 return;
284 }
285
286 auto it = thresholdData.find(interface);
287 if (it == thresholdData.end())
288 {
289 return;
290 }
291
292 auto properties = it->second.find(alarmProperty);
293 if (properties == it->second.end())
294 {
295 log<level::INFO>(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500296 std::format("Could not find {} in threshold alarms map",
Matt Spinler2f182672021-02-01 16:51:38 -0600297 alarmProperty)
298 .c_str());
299 return;
300 }
301
302 ad.emplace("SENSOR_NAME", sensorPath);
Matt Spinler3efec612021-05-11 15:26:17 -0500303 ad.emplace("_PID", std::to_string(getpid()));
Matt Spinler2f182672021-02-01 16:51:38 -0600304
305 try
306 {
307 auto sensorValue = SDBusPlus::getProperty<double>(
308 bus, sensorPath, valueInterface, "Value");
309
310 ad.emplace("SENSOR_VALUE", std::to_string(sensorValue));
311
312 log<level::INFO>(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500313 std::format("Threshold Event {} {} = {} (sensor value {})",
Matt Spinler2f182672021-02-01 16:51:38 -0600314 sensorPath, alarmProperty, alarmValue, sensorValue)
315 .c_str());
316 }
317 catch (const DBusServiceError& e)
318 {
319 // If the sensor was just added, the Value interface for it may
320 // not be in the mapper yet. This could only happen if the sensor
321 // application was started up after this one and the value exceeded the
322 // threshold immediately.
Patrick Williamsfbf47032023-07-17 12:27:34 -0500323 log<level::INFO>(std::format("Threshold Event {} {} = {}", sensorPath,
Matt Spinler2f182672021-02-01 16:51:38 -0600324 alarmProperty, alarmValue)
325 .c_str());
326 }
327
328 auto callout = getCallout(sensorPath);
329 if (!callout.empty())
330 {
331 ad.emplace("CALLOUT_INVENTORY_PATH", callout);
332 }
333
334 auto errorData = properties->second.find(alarmValue);
335
336 // Add the base error name and the sensor type (like Temperature) to the
337 // error name that's in the thresholdData name to get something like
338 // xyz.openbmc_project.Sensor.Threshold.Error.TemperatureWarningHigh
Delphine CC Chiu99914e52024-05-21 17:38:58 +0800339 const auto& [name, status, severity] = errorData->second;
340
341 try
342 {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400343 auto thresholdValue =
344 SDBusPlus::getProperty<double>(bus, sensorPath, interface, name);
Delphine CC Chiu99914e52024-05-21 17:38:58 +0800345
346 ad.emplace("THRESHOLD_VALUE", std::to_string(thresholdValue));
347
348 log<level::INFO>(
349 std::format("Threshold Event {} {} = {} (threshold value {})",
350 sensorPath, alarmProperty, alarmValue, thresholdValue)
351 .c_str());
352 }
353 catch (const DBusServiceError& e)
354 {
355 log<level::INFO>(std::format("Threshold Event {} {} = {}", sensorPath,
356 alarmProperty, alarmValue)
357 .c_str());
358 }
359
Matt Spinler2f182672021-02-01 16:51:38 -0600360 type.front() = toupper(type.front());
Delphine CC Chiu99914e52024-05-21 17:38:58 +0800361 std::string errorName = errorNameBase + type + name + status;
Matt Spinler2f182672021-02-01 16:51:38 -0600362
363 SDBusPlus::callMethod(loggingService, loggingPath, loggingCreateIface,
364 "Create", errorName, convertForMessage(severity), ad);
365}
366
367std::string ThresholdAlarmLogger::getSensorType(std::string sensorPath)
368{
369 auto pos = sensorPath.find_last_of('/');
370 if ((sensorPath.back() == '/') || (pos == std::string::npos))
371 {
372 log<level::ERR>(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500373 std::format("Cannot get sensor type from sensor path {}",
Matt Spinler2f182672021-02-01 16:51:38 -0600374 sensorPath)
375 .c_str());
376 throw std::runtime_error("Invalid sensor path");
377 }
378
379 sensorPath = sensorPath.substr(0, pos);
380 return sensorPath.substr(sensorPath.find_last_of('/') + 1);
381}
382
383bool ThresholdAlarmLogger::skipSensorType(const std::string& type)
384{
385 return (type == "utilization");
386}
387
388std::string ThresholdAlarmLogger::getCallout(const std::string& sensorPath)
389{
390 const std::array<std::string, 2> assocTypes{"inventory", "chassis"};
391
392 // Different implementations handle the association to the FRU
393 // differently:
394 // * phosphor-inventory-manager uses the 'inventory' association
395 // to point to the FRU.
396 // * dbus-sensors/entity-manager uses the 'chassis' association'.
397 // * For virtual sensors, no association.
398
399 for (const auto& assocType : assocTypes)
400 {
401 auto assocPath = sensorPath + "/" + assocType;
402
403 try
404 {
405 auto endpoints = SDBusPlus::getProperty<std::vector<std::string>>(
406 bus, assocPath, assocInterface, "endpoints");
407
408 if (!endpoints.empty())
409 {
410 return endpoints[0];
411 }
412 }
413 catch (const DBusServiceError& e)
414 {
415 // The association doesn't exist
416 continue;
417 }
418 }
419
420 return std::string{};
Matt Spinler50bf8162021-02-01 16:24:01 -0600421}
422
Matt Spinler66e75a72021-05-14 10:32:47 -0500423void ThresholdAlarmLogger::powerStateChanged(bool powerStateOn)
424{
425 if (powerStateOn)
426 {
427 checkThresholds();
428 }
429}
430
431void ThresholdAlarmLogger::checkThresholds()
432{
Matt Spinlereee25802022-11-03 15:20:13 -0400433 std::vector<InterfaceKey> toErase;
434
Matt Spinler66e75a72021-05-14 10:32:47 -0500435 for (const auto& [interfaceKey, alarmMap] : alarms)
436 {
437 for (const auto& [propertyName, alarmValue] : alarmMap)
438 {
439 if (alarmValue)
440 {
441 const auto& sensorPath = std::get<0>(interfaceKey);
442 const auto& interface = std::get<1>(interfaceKey);
Matt Spinlereee25802022-11-03 15:20:13 -0400443 std::string service;
Matt Spinler66e75a72021-05-14 10:32:47 -0500444
Matt Spinlereee25802022-11-03 15:20:13 -0400445 try
446 {
447 // Check that the service that provides the alarm is still
448 // running, because if it died when the alarm was active
449 // there would be no indication of it unless we listened
450 // for NameOwnerChanged and tracked services, and this is
451 // easier.
452 service = SDBusPlus::getService(bus, sensorPath, interface);
453 }
454 catch (const DBusServiceError& e)
455 {
456 // No longer on D-Bus delete the alarm entry
457 toErase.emplace_back(sensorPath, interface);
458 }
459
460 if (!service.empty())
461 {
462 createEventLog(sensorPath, interface, propertyName,
463 alarmValue);
464 }
Matt Spinler66e75a72021-05-14 10:32:47 -0500465 }
466 }
467 }
Matt Spinlereee25802022-11-03 15:20:13 -0400468
469 for (const auto& e : toErase)
470 {
471 alarms.erase(e);
472 }
Matt Spinler66e75a72021-05-14 10:32:47 -0500473}
474
Matt Spinler403d1f52021-02-01 15:35:25 -0600475} // namespace sensor::monitor