blob: a73d08be7bdcf3a01787434b70671d8f6708a613 [file] [log] [blame]
Matt Spinlere8122392020-09-24 13:22:18 -05001/**
2 * Copyright © 2020 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 */
16#include "error_reporter.hpp"
17
Matt Spinler0dc85ee2020-09-24 14:07:02 -050018#include "logging.hpp"
19#include "psensor.hpp"
20#include "utility.hpp"
21
22#include <fmt/format.h>
23#include <unistd.h>
24
Matt Spinlere8122392020-09-24 13:22:18 -050025#include <phosphor-logging/log.hpp>
26
27namespace phosphor::fan::presence
28{
29
30using json = nlohmann::json;
31using namespace phosphor::logging;
Matt Spinler0dc85ee2020-09-24 14:07:02 -050032using namespace sdbusplus::bus::match;
33using namespace std::literals::string_literals;
34namespace fs = std::filesystem;
35
36const auto itemIface = "xyz.openbmc_project.Inventory.Item"s;
37const auto invPrefix = "/xyz/openbmc_project/inventory"s;
Matt Spinlere8122392020-09-24 13:22:18 -050038
39ErrorReporter::ErrorReporter(
40 sdbusplus::bus::bus& bus, const json& jsonConf,
41 const std::vector<
42 std::tuple<Fan, std::vector<std::unique_ptr<PresenceSensor>>>>& fans) :
Matt Spinler0dc85ee2020-09-24 14:07:02 -050043 _bus(bus),
44 _event(sdeventplus::Event::get_default())
Matt Spinlere8122392020-09-24 13:22:18 -050045{
46 loadConfig(jsonConf);
Matt Spinler0dc85ee2020-09-24 14:07:02 -050047
48 // If different methods to check the power state are needed across the
49 // various platforms, the method/class to use could be read out of JSON
50 // or set with a compilation flag.
51 _powerState = std::make_unique<PGoodState>(
52 bus, std::bind(std::mem_fn(&ErrorReporter::powerStateChanged), this,
53 std::placeholders::_1));
54
55 for (const auto& fan : fans)
56 {
57 auto path = invPrefix + std::get<1>(std::get<0>(fan));
58
59 // Register for fan presence changes, get their initial states,
60 // and create the fan missing timers for each fan.
61
62 _matches.emplace_back(
63 _bus, rules::propertiesChanged(path, itemIface),
64 std::bind(std::mem_fn(&ErrorReporter::presenceChanged), this,
65 std::placeholders::_1));
66
67 _fanStates.emplace(path, getPresence(std::get<0>(fan)));
68
69 auto timer = std::make_unique<
70 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
71 _event,
72 std::bind(std::mem_fn(&ErrorReporter::fanMissingTimerExpired), this,
73 path));
74
75 _fanMissingTimers.emplace(path, std::move(timer));
76 }
77
78 // If power is already on, check for currently missing fans.
79 if (_powerState->isPowerOn())
80 {
81 powerStateChanged(true);
82 }
Matt Spinlere8122392020-09-24 13:22:18 -050083}
84
85void ErrorReporter::loadConfig(const json& jsonConf)
86{
87 if (!jsonConf.contains("fan_missing_error_time"))
88 {
89 log<level::ERR>("Missing 'fan_missing_error_time' entry in JSON "
90 "'reporting' section");
91
92 throw std::runtime_error("Missing fan missing time entry in JSON");
93 }
94
95 _fanMissingErrorTime = std::chrono::seconds{
96 jsonConf.at("fan_missing_error_time").get<std::size_t>()};
97}
98
Matt Spinler0dc85ee2020-09-24 14:07:02 -050099void ErrorReporter::presenceChanged(sdbusplus::message::message& msg)
100{
101 bool present;
102 auto fanPath = msg.get_path();
103 std::string interface;
104 std::map<std::string, std::variant<bool>> properties;
105
106 msg.read(interface, properties);
107
108 auto presentProp = properties.find("Present");
109 if (presentProp != properties.end())
110 {
111 present = std::get<bool>(presentProp->second);
112 if (_fanStates[fanPath] != present)
113 {
114 getLogger().log(fmt::format("Fan {} presence state change to {}",
115 fanPath, present));
116
117 _fanStates[fanPath] = present;
118 checkFan(fanPath);
119 }
120 }
121}
122
123void ErrorReporter::checkFan(const std::string& fanPath)
124{
125 if (!_fanStates[fanPath])
126 {
127 // Fan is missing. If power is on, start the timer.
128 // If power is off, stop a running timer.
129 if (_powerState->isPowerOn())
130 {
131 _fanMissingTimers[fanPath]->restartOnce(_fanMissingErrorTime);
132 }
133 else if (_fanMissingTimers[fanPath]->isEnabled())
134 {
135 _fanMissingTimers[fanPath]->setEnabled(false);
136 }
137 }
138 else
139 {
140 // Fan is present. Stop a running timer.
141 if (_fanMissingTimers[fanPath]->isEnabled())
142 {
143 _fanMissingTimers[fanPath]->setEnabled(false);
144 }
145 }
146}
147
148void ErrorReporter::fanMissingTimerExpired(const std::string& fanPath)
149{}
150
151void ErrorReporter::powerStateChanged(bool powerState)
152{
153 if (powerState)
154 {
155 // If there are fans already missing, log it.
156 auto missing = std::count_if(
157 _fanStates.begin(), _fanStates.end(),
158 [](const auto& fanState) { return fanState.second == false; });
159
160 if (missing)
161 {
162 getLogger().log(
163 fmt::format("At power on, there are {} missing fans", missing));
164 }
165 }
166
167 std::for_each(
168 _fanStates.begin(), _fanStates.end(),
169 [this](const auto& fanState) { this->checkFan(fanState.first); });
170}
171
Matt Spinlere8122392020-09-24 13:22:18 -0500172} // namespace phosphor::fan::presence