blob: 00c3b6bf052d6adb2ed300e76113a2c4b743c161 [file] [log] [blame]
Matthew Barthfd05d642019-11-14 15:01:57 -06001/**
2 * Copyright © 2019 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 */
Jolie Ku1a568652020-08-24 16:32:15 +080016#include "json_parser.hpp"
Matthew Barth2d2caa32020-05-26 11:07:24 -050017
18#include "anyof.hpp"
19#include "fallback.hpp"
20#include "gpio.hpp"
Jolie Ku1a568652020-08-24 16:32:15 +080021#include "json_config.hpp"
Matthew Barth2d2caa32020-05-26 11:07:24 -050022#include "sdbusplus.hpp"
23#include "tach.hpp"
24
Matthew Barth0961fae2019-11-15 09:00:27 -060025#include <nlohmann/json.hpp>
Anwaar Hadiebead9a2025-03-19 20:35:57 +000026#include <phosphor-logging/lg2.hpp>
Matthew Barth5060b102019-12-16 10:46:35 -060027#include <sdbusplus/bus.hpp>
Mike Cappsa35a8902021-06-10 10:20:14 -040028#include <xyz/openbmc_project/Logging/Create/server.hpp>
29#include <xyz/openbmc_project/Logging/Entry/server.hpp>
Matthew Barthfd05d642019-11-14 15:01:57 -060030
Matthew Barth2d2caa32020-05-26 11:07:24 -050031#include <filesystem>
32#include <fstream>
33#include <string>
Matthew Barthfd05d642019-11-14 15:01:57 -060034
35namespace phosphor
36{
37namespace fan
38{
39namespace presence
40{
41
Matthew Barth0961fae2019-11-15 09:00:27 -060042using json = nlohmann::json;
43namespace fs = std::filesystem;
Matthew Barth0961fae2019-11-15 09:00:27 -060044
Matthew Barthfd05d642019-11-14 15:01:57 -060045policies JsonConfig::_policies;
Matthew Barth2d2caa32020-05-26 11:07:24 -050046const std::map<std::string, methodHandler> JsonConfig::_methods = {
47 {"tach", method::getTach}, {"gpio", method::getGpio}};
48const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
49 {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
Matthew Barthfd05d642019-11-14 15:01:57 -060050
Mike Cappsa35a8902021-06-10 10:20:14 -040051const auto loggingPath = "/xyz/openbmc_project/logging";
52const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
53
Patrick Williams61b73292023-05-10 07:50:12 -050054JsonConfig::JsonConfig(sdbusplus::bus_t& bus) : _bus(bus) {}
Jolie Ku1a568652020-08-24 16:32:15 +080055
Matthew Barth5b839912021-06-21 14:46:34 -050056void JsonConfig::start()
Matt Spinler0daedd12021-01-25 14:46:03 -060057{
Matthew Barth5b839912021-06-21 14:46:34 -050058 using config = fan::JsonConfig;
59
Matt Spinlerdfc8c4d2022-06-22 16:52:27 -050060 if (!_loaded)
Matt Spinler0daedd12021-01-25 14:46:03 -060061 {
Mike Capps808d7fe2022-06-13 10:12:16 -040062 process(config::load(config::getConfFile(confAppName, confFileName)));
Matt Spinlerdfc8c4d2022-06-22 16:52:27 -050063
64 _loaded = true;
65
66 for (auto& p : _policies)
67 {
68 p->monitor();
69 }
Matt Spinler0daedd12021-01-25 14:46:03 -060070 }
Matthew Barthfd05d642019-11-14 15:01:57 -060071}
72
73const policies& JsonConfig::get()
74{
75 return _policies;
76}
77
Mike Capps808d7fe2022-06-13 10:12:16 -040078void JsonConfig::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
79 const struct signalfd_siginfo* /*sigInfo*/)
Matthew Barthf3e70472019-12-03 13:33:20 -060080{
81 try
82 {
Jolie Ku1a568652020-08-24 16:32:15 +080083 using config = fan::JsonConfig;
84
Matt Spinler9e9f5992020-09-30 08:29:24 -050085 _reporter.reset();
86
Matthew Barthf3e70472019-12-03 13:33:20 -060087 // Load and process the json configuration
Mike Capps808d7fe2022-06-13 10:12:16 -040088 process(config::load(config::getConfFile(confAppName, confFileName)));
Jolie Ku1a568652020-08-24 16:32:15 +080089
Matthew Barth2d2caa32020-05-26 11:07:24 -050090 for (auto& p : _policies)
Matthew Barthf3e70472019-12-03 13:33:20 -060091 {
92 p->monitor();
93 }
Anwaar Hadiebead9a2025-03-19 20:35:57 +000094 lg2::info("Configuration loaded successfully");
Matthew Barthf3e70472019-12-03 13:33:20 -060095 }
Patrick Williamsddb773b2021-10-06 11:24:49 -050096 catch (const std::runtime_error& re)
Matthew Barthf3e70472019-12-03 13:33:20 -060097 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +000098 lg2::error("Error loading config, no config changes made: {ERROR}",
99 "ERROR", re);
Matthew Barthf3e70472019-12-03 13:33:20 -0600100 }
101}
102
Matthew Barth4a94dec2019-11-15 10:40:47 -0600103void JsonConfig::process(const json& jsonConf)
104{
Matthew Barthf3e70472019-12-03 13:33:20 -0600105 policies policies;
106 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600107 // Set the expected number of fan entries
108 // to be size of the list of fan json config entries
109 // (Must be done to eliminate vector reallocation of fan references)
Matt Spinler9e9f5992020-09-30 08:29:24 -0500110 fans.reserve(jsonConf.size());
111 for (auto& member : jsonConf)
Matthew Barth4a94dec2019-11-15 10:40:47 -0600112 {
Matthew Barthe7566632019-11-18 16:13:04 -0600113 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600114 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600115 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000116 lg2::error(
117 "Missing one of the required fan presence properties, which are: 'name, path, methods, rpolicy'");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600118 throw std::runtime_error(
119 "Missing required fan presence properties");
120 }
Matthew Barthe7566632019-11-18 16:13:04 -0600121
122 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600123 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600124 for (auto& method : member["methods"].items())
125 {
126 if (!method.value().contains("type"))
127 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000128 lg2::error(
129 "Missing required fan presence method type for fan {FAN_NAME}",
130 "FAN_NAME", member["name"].get<std::string>());
Matthew Barthe7566632019-11-18 16:13:04 -0600131 throw std::runtime_error(
132 "Missing required fan presence method type");
133 }
134 // The method type of fan presence detection
135 // (Must have a supported function within the method namespace)
136 auto type = method.value()["type"].get<std::string>();
137 std::transform(type.begin(), type.end(), type.begin(), tolower);
138 auto func = _methods.find(type);
139 if (func != _methods.end())
140 {
141 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600142 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600143 if (sensor)
144 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600145 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600146 }
147 }
148 else
149 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000150 lg2::error(
151 "Invalid fan presence method type {METHOD_TYPE} for fan {FAN_NAME}",
152 "FAN_NAME", member["name"].get<std::string>(),
153 "METHOD_TYPE", type);
Matthew Barthe7566632019-11-18 16:13:04 -0600154 throw std::runtime_error("Invalid fan presence method type");
155 }
156 }
Matt Spinler9e9f5992020-09-30 08:29:24 -0500157
158 // Get the amount of time a fan must be not present before
159 // creating an error.
160 std::optional<size_t> timeUntilError;
161 if (member.contains("fan_missing_error_time"))
162 {
163 timeUntilError = member["fan_missing_error_time"].get<size_t>();
164 }
165
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500166 std::unique_ptr<EEPROMDevice> eepromDevice;
167 if (member.contains("eeprom"))
168 {
169 const auto& eeprom = member.at("eeprom");
170 if (!eeprom.contains("bus_address") ||
171 !eeprom.contains("driver_name") ||
172 !eeprom.contains("bind_delay_ms"))
173 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000174 lg2::error(
175 "Missing address, driver_name, or bind_delay_ms in eeprom section for fan {FAN_NAME}",
176 "FAN_NAME", member["name"].get<std::string>());
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500177
178 throw std::runtime_error("Missing address, driver_name, or "
179 "bind_delay_ms in eeprom section");
180 }
181 eepromDevice = std::make_unique<EEPROMDevice>(
182 eeprom["bus_address"].get<std::string>(),
183 eeprom["driver_name"].get<std::string>(),
184 eeprom["bind_delay_ms"].get<size_t>());
185 }
186
Patrick Williamsdfddd642024-08-16 15:21:51 -0400187 auto fan =
188 std::make_tuple(member["name"], member["path"], timeUntilError);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600189 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600190 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600191
192 // Add fan presence policy
Patrick Williamsdfddd642024-08-16 15:21:51 -0400193 auto policy =
194 getPolicy(member["rpolicy"], fans.back(), std::move(eepromDevice));
Matthew Barthf3e70472019-12-03 13:33:20 -0600195 if (policy)
196 {
197 policies.emplace_back(std::move(policy));
198 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600199 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600200
201 // Success, refresh fans and policies lists
202 _fans.clear();
203 _fans.swap(fans);
204
205 _policies.clear();
206 _policies.swap(policies);
Matt Spinlere8122392020-09-24 13:22:18 -0500207
208 // Create the error reporter class if necessary
Matt Spinler9e9f5992020-09-30 08:29:24 -0500209 if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400210 return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
211 std::nullopt;
212 }))
Matt Spinlere8122392020-09-24 13:22:18 -0500213 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500214 _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
Matt Spinlere8122392020-09-24 13:22:18 -0500215 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600216}
217
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500218std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
219 const json& rpolicy, const fanPolicy& fpolicy,
220 std::unique_ptr<EEPROMDevice> eepromDevice)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600221{
222 if (!rpolicy.contains("type"))
223 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000224 lg2::error(
225 "Missing required fan presence policy type for fan {FAN_NAME}",
226 "FAN_NAME", std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600227 throw std::runtime_error("Missing required fan presence policy type");
228 }
229
230 // The redundancy policy type for fan presence detection
231 // (Must have a supported function within the rpolicy namespace)
232 auto type = rpolicy["type"].get<std::string>();
233 std::transform(type.begin(), type.end(), type.begin(), tolower);
234 auto func = _rpolicies.find(type);
235 if (func != _rpolicies.end())
236 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600237 // Call function for redundancy policy type and return the policy
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500238 return func->second(fpolicy, std::move(eepromDevice));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600239 }
240 else
241 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000242 lg2::error(
243 "Invalid fan presence policy type {RPOLICY_TYPE} for fan {FAN_NAME}",
244 "FAN_NAME", std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)),
245 "RPOLICY_TYPE", type);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600246 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600247 }
248}
249
Matthew Barthe7566632019-11-18 16:13:04 -0600250/**
251 * Methods of fan presence detection function definitions
252 */
253namespace method
254{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500255// Get a constructed presence sensor for fan presence detection by tach
256std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
257{
258 if (!method.contains("sensors") || method["sensors"].size() == 0)
Matthew Barthe7566632019-11-18 16:13:04 -0600259 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000260 lg2::error(
261 "Missing required tach method property 'sensors' for fan index {FAN_ENTRY}",
262 "FAN_ENTRY", fanIndex);
Matthew Barth2d2caa32020-05-26 11:07:24 -0500263 throw std::runtime_error("Missing required tach method properties");
Matthew Barthe7566632019-11-18 16:13:04 -0600264 }
265
Matthew Barth2d2caa32020-05-26 11:07:24 -0500266 std::vector<std::string> sensors;
267 for (auto& sensor : method["sensors"])
Matthew Barthe7566632019-11-18 16:13:04 -0600268 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500269 sensors.emplace_back(sensor.get<std::string>());
Matthew Barthe7566632019-11-18 16:13:04 -0600270 }
271
Patrick Williamsdfddd642024-08-16 15:21:51 -0400272 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
273 fanIndex, std::move(sensors));
Matthew Barth2d2caa32020-05-26 11:07:24 -0500274}
275
276// Get a constructed presence sensor for fan presence detection by gpio
277std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
278{
279 if (!method.contains("physpath") || !method.contains("devpath") ||
280 !method.contains("key"))
281 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000282 lg2::error(
283 "Missing one of the required gpio method properties for fan index {FAN_ENTRY}, which are: 'physpath, devpath, key'",
284 "FAN_ENTRY", fanIndex);
Matthew Barth2d2caa32020-05-26 11:07:24 -0500285 throw std::runtime_error("Missing required gpio method properties");
286 }
287
288 auto physpath = method["physpath"].get<std::string>();
289 auto devpath = method["devpath"].get<std::string>();
290 auto key = method["key"].get<unsigned int>();
291
Mike Cappsa35a8902021-06-10 10:20:14 -0400292 try
293 {
294 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
295 fanIndex, physpath, devpath, key);
296 }
297 catch (const sdbusplus::exception_t& e)
298 {
299 namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server;
300
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000301 lg2::error(
302 "Error creating Gpio device bridge, hardware not detected: {ERROR}",
303 "ERROR", e);
Mike Cappsa35a8902021-06-10 10:20:14 -0400304
305 auto severity =
306 sdlogging::convertForMessage(sdlogging::Entry::Level::Error);
307
308 std::map<std::string, std::string> additionalData{
309 {"PHYSPATH", physpath},
310 {"DEVPATH", devpath},
311 {"FANINDEX", std::to_string(fanIndex)}};
312
313 try
314 {
Mike Cappsa35a8902021-06-10 10:20:14 -0400315 util::SDBusPlus::lookupAndCallMethod(
316 loggingPath, loggingCreateIface, "Create",
317 "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable",
318 severity, additionalData);
319 }
320 catch (const util::DBusError& e)
321 {
Anwaar Hadiebead9a2025-03-19 20:35:57 +0000322 lg2::error(
323 "Call to create an error log for presence-sensor failure failed: {ERROR}",
324 "ERROR", e);
Mike Cappsa35a8902021-06-10 10:20:14 -0400325 }
326
327 return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>();
328 }
Matthew Barth2d2caa32020-05-26 11:07:24 -0500329}
330
Matthew Barthe7566632019-11-18 16:13:04 -0600331} // namespace method
332
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600333/**
334 * Redundancy policies for fan presence detection function definitions
335 */
336namespace rpolicy
337{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500338// Get an `Anyof` redundancy policy for the fan
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500339std::unique_ptr<RedundancyPolicy> getAnyof(
340 const fanPolicy& fan, std::unique_ptr<EEPROMDevice> eepromDevice)
Matthew Barth2d2caa32020-05-26 11:07:24 -0500341{
342 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
343 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600344 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500345 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600346 }
347
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500348 return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors,
349 std::move(eepromDevice));
Matthew Barth2d2caa32020-05-26 11:07:24 -0500350}
Matthew Barth26ad44a2019-11-22 15:37:14 -0600351
Matthew Barth2d2caa32020-05-26 11:07:24 -0500352// Get a `Fallback` redundancy policy for the fan
Patrick Williamsdfddd642024-08-16 15:21:51 -0400353std::unique_ptr<RedundancyPolicy> getFallback(
354 const fanPolicy& fan, std::unique_ptr<EEPROMDevice> eepromDevice)
Matthew Barth2d2caa32020-05-26 11:07:24 -0500355{
356 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
357 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
358 {
359 // Place in the order given to fallback correctly
360 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600361 }
362
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500363 return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors,
364 std::move(eepromDevice));
Matthew Barth2d2caa32020-05-26 11:07:24 -0500365}
366
367} // namespace rpolicy
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600368
Matthew Barthfd05d642019-11-14 15:01:57 -0600369} // namespace presence
370} // namespace fan
371} // namespace phosphor