blob: c24076551d82e361780edc56814bf1e70e2fb1ce [file] [log] [blame]
Tony Lee84d430c2019-06-13 15:26:15 +08001#include "nvme_manager.hpp"
2
Tony Lee6c595012019-06-19 10:54:59 +08003#include "smbus.hpp"
4
5#include <filesystem>
6#include <map>
7#include <nlohmann/json.hpp>
Tony Lee84d430c2019-06-13 15:26:15 +08008#include <phosphor-logging/elog-errors.hpp>
9#include <phosphor-logging/log.hpp>
Tony Lee89659212019-06-21 17:34:14 +080010#include <sdbusplus/message.hpp>
Tony Lee6c595012019-06-19 10:54:59 +080011#include <sstream>
12#include <string>
Tony Lee89659212019-06-21 17:34:14 +080013#include <xyz/openbmc_project/Led/Physical/server.hpp>
Tony Lee84d430c2019-06-13 15:26:15 +080014
Tony Lee6c595012019-06-19 10:54:59 +080015#include "i2c.h"
Tony Lee84d430c2019-06-13 15:26:15 +080016#define MONITOR_INTERVAL_SECONDS 1
Tony Lee6c595012019-06-19 10:54:59 +080017#define NVME_SSD_SLAVE_ADDRESS 0x6a
18#define GPIO_BASE_PATH "/sys/class/gpio/gpio"
19#define IS_PRESENT "0"
20#define POWERGD "1"
Tony Lee89659212019-06-21 17:34:14 +080021#define NOWARNING_STRING "ff"
Tony Lee6c595012019-06-19 10:54:59 +080022
23static constexpr auto configFile = "/etc/nvme/nvme_config.json";
24static constexpr auto delay = std::chrono::milliseconds{100};
25using Json = nlohmann::json;
26
27static constexpr const uint8_t COMMAND_CODE_0 = 0;
28static constexpr const uint8_t COMMAND_CODE_8 = 8;
29
Tony Lee89659212019-06-21 17:34:14 +080030static constexpr int CapacityFaultMask = 1;
31static constexpr int temperatureFaultMask = 1 << 1;
32static constexpr int DegradesFaultMask = 1 << 2;
33static constexpr int MediaFaultMask = 1 << 3;
34static constexpr int BackupDeviceFaultMask = 1 << 4;
35static constexpr int NOWARNING = 255;
36
Tony Lee6c595012019-06-19 10:54:59 +080037static constexpr int SERIALNUMBER_START_INDEX = 3;
38static constexpr int SERIALNUMBER_END_INDEX = 23;
39
40static constexpr const int TEMPERATURE_SENSOR_FAILURE = 0x81;
41
42namespace fs = std::filesystem;
43
Tony Lee84d430c2019-06-13 15:26:15 +080044namespace phosphor
45{
46namespace nvme
47{
48
49using namespace std;
50using namespace phosphor::logging;
51
Tony Lee89659212019-06-21 17:34:14 +080052void Nvme::setNvmeInventoryProperties(
53 bool present, const phosphor::nvme::Nvme::NVMeData& nvmeData,
54 const std::string& inventoryPath)
55{
56 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
57 ITEM_IFACE, "Present", present);
58 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
59 ASSET_IFACE, "Manufacturer", nvmeData.vendor);
60 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
61 ASSET_IFACE, "SerialNumber",
62 nvmeData.serialNumber);
63 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
64 NVME_STATUS_IFACE, "SmartWarnings",
65 nvmeData.smartWarnings);
66 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
67 NVME_STATUS_IFACE, "StatusFlags",
68 nvmeData.statusFlags);
69 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
70 NVME_STATUS_IFACE, "DriveLifeUsed",
71 nvmeData.driveLifeUsed);
72
73 auto smartWarning = (!nvmeData.smartWarnings.empty())
74 ? std::stoi(nvmeData.smartWarnings, 0, 16)
75 : NOWARNING;
76
77 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
78 NVME_STATUS_IFACE, "CapacityFault",
79 !(smartWarning & CapacityFaultMask));
80
81 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
82 NVME_STATUS_IFACE, "TemperatureFault",
83 !(smartWarning & temperatureFaultMask));
84
85 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
86 NVME_STATUS_IFACE, "DegradesFault",
87 !(smartWarning & DegradesFaultMask));
88
89 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
90 NVME_STATUS_IFACE, "MediaFault",
91 !(smartWarning & MediaFaultMask));
92
93 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
94 NVME_STATUS_IFACE, "BackupDeviceFault",
95 !(smartWarning & BackupDeviceFaultMask));
96}
97
98void Nvme::setFaultLED(const std::string& locateLedGroupPath,
99 const std::string& faultLedGroupPath, bool request)
100{
101 if (locateLedGroupPath.empty() || faultLedGroupPath.empty())
102 {
103 return;
104 }
105
106 // Before toggle LED, check whether is Identify or not.
107 if (!getLEDGroupState(locateLedGroupPath))
108 {
109 util::SDBusPlus::setProperty(bus, LED_GROUP_BUSNAME, faultLedGroupPath,
110 LED_GROUP_IFACE, "Asserted", request);
111 }
112}
113
114void Nvme::setLocateLED(const std::string& locateLedGroupPath,
115 const std::string& locateLedBusName,
116 const std::string& locateLedPath, bool isPresent)
117{
118 if (locateLedGroupPath.empty() || locateLedBusName.empty() ||
119 locateLedPath.empty())
120 {
121 return;
122 }
123
124 namespace server = sdbusplus::xyz::openbmc_project::Led::server;
125
126 if (!getLEDGroupState(locateLedGroupPath))
127 {
128 if (isPresent)
129 util::SDBusPlus::setProperty(
130 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
131 "State",
132 server::convertForMessage(server::Physical::Action::On));
133 else
134 util::SDBusPlus::setProperty(
135 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
136 "State",
137 server::convertForMessage(server::Physical::Action::Off));
138 }
139}
140
141bool Nvme::getLEDGroupState(const std::string& ledPath)
142{
143 auto asserted = util::SDBusPlus::getProperty<bool>(
144 bus, LED_GROUP_BUSNAME, ledPath, LED_GROUP_IFACE, "Asserted");
145
146 return asserted;
147}
148
149void Nvme::setLEDsStatus(const phosphor::nvme::Nvme::NVMeConfig& config,
150 bool success,
151 const phosphor::nvme::Nvme::NVMeData& nvmeData)
152{
153 static std::unordered_map<std::string, bool> isError;
154
155 if (success)
156 {
157 if (!nvmeData.smartWarnings.empty())
158 {
159 auto request =
160 (strcmp(nvmeData.smartWarnings.c_str(), NOWARNING_STRING) == 0)
161 ? false
162 : true;
163
164 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
165 request);
166 setLocateLED(config.locateLedGroupPath,
167 config.locateLedControllerBusName,
168 config.locateLedControllerPath, !request);
169 }
170 isError[config.index] = false;
171 }
172 else
173 {
174 if (isError[config.index] != true)
175 {
176 // Drive is present but can not get data, turn on fault LED.
177 log<level::ERR>("Drive status is good but can not get data.",
178 entry("objPath = %s", config.index.c_str()));
179 isError[config.index] = true;
180 }
181
182 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath, true);
183 setLocateLED(config.locateLedGroupPath,
184 config.locateLedControllerBusName,
185 config.locateLedControllerPath, false);
186 }
187}
188
Tony Lee6c595012019-06-19 10:54:59 +0800189std::string intToHex(int input)
190{
191 std::stringstream tmp;
192 tmp << std::hex << input;
193
194 return tmp.str();
195}
196
197/** @brief Get NVMe info over smbus */
198bool getNVMeInfobyBusID(int busID, phosphor::nvme::Nvme::NVMeData& nvmeData)
199{
200 nvmeData.present = true;
201 nvmeData.vendor = "";
202 nvmeData.serialNumber = "";
203 nvmeData.smartWarnings = "";
204 nvmeData.statusFlags = "";
205 nvmeData.driveLifeUsed = "";
206 nvmeData.sensorValue = (int8_t)TEMPERATURE_SENSOR_FAILURE;
207
208 phosphor::smbus::Smbus smbus;
209
210 unsigned char rsp_data_command_0[I2C_DATA_MAX] = {0};
211 unsigned char rsp_data_command_8[I2C_DATA_MAX] = {0};
212
213 uint8_t tx_data = COMMAND_CODE_0;
214
215 auto init = smbus.smbusInit(busID);
216
217 static std::unordered_map<int, bool> isErrorSmbus;
218
219 if (init == -1)
220 {
221 if (isErrorSmbus[busID] != true)
222 {
223 log<level::ERR>("smbusInit fail!");
224 isErrorSmbus[busID] = true;
225 }
226
227 nvmeData.present = false;
228
229 return nvmeData.present;
230 }
231
232 auto res_int =
233 smbus.SendSmbusRWBlockCmdRAW(busID, NVME_SSD_SLAVE_ADDRESS, &tx_data,
234 sizeof(tx_data), rsp_data_command_0);
235
236 if (res_int < 0)
237 {
238 if (isErrorSmbus[busID] != true)
239 {
240 log<level::ERR>("Send command code 0 fail!");
241 isErrorSmbus[busID] = true;
242 }
243
244 smbus.smbusClose(busID);
245 nvmeData.present = false;
246 return nvmeData.present;
247 }
248
249 tx_data = COMMAND_CODE_8;
250
251 res_int =
252 smbus.SendSmbusRWBlockCmdRAW(busID, NVME_SSD_SLAVE_ADDRESS, &tx_data,
253 sizeof(tx_data), rsp_data_command_8);
254
255 if (res_int < 0)
256 {
257 if (isErrorSmbus[busID] != true)
258 {
259 log<level::ERR>("Send command code 8 fail!");
260 isErrorSmbus[busID] = true;
261 }
262
263 smbus.smbusClose(busID);
264 nvmeData.present = false;
265 return nvmeData.present;
266 }
267
268 nvmeData.vendor =
269 intToHex(rsp_data_command_8[1]) + " " + intToHex(rsp_data_command_8[2]);
270
271 for (int offset = SERIALNUMBER_START_INDEX; offset < SERIALNUMBER_END_INDEX;
272 offset++)
273 {
274 nvmeData.serialNumber += static_cast<char>(rsp_data_command_8[offset]);
275 }
276
277 nvmeData.statusFlags = intToHex(rsp_data_command_0[1]);
278 nvmeData.smartWarnings = intToHex(rsp_data_command_0[2]);
279 nvmeData.driveLifeUsed = intToHex(rsp_data_command_0[4]);
280 nvmeData.sensorValue = (int8_t)rsp_data_command_0[3];
281
282 smbus.smbusClose(busID);
283
284 isErrorSmbus[busID] = false;
285
286 return nvmeData.present;
287}
288
Tony Lee84d430c2019-06-13 15:26:15 +0800289void Nvme::run()
290{
Tony Lee89659212019-06-21 17:34:14 +0800291 init();
292
Tony Lee84d430c2019-06-13 15:26:15 +0800293 std::function<void()> callback(std::bind(&Nvme::read, this));
294 try
295 {
296 u_int64_t interval = MONITOR_INTERVAL_SECONDS * 1000000;
297 _timer.restart(std::chrono::microseconds(interval));
298 }
299 catch (const std::exception& e)
300 {
301 log<level::ERR>("Error in polling loop. "),
302 entry("ERROR = %s", e.what());
303 }
304}
305
Tony Lee6c595012019-06-19 10:54:59 +0800306/** @brief Parsing NVMe config JSON file */
307Json parseSensorConfig()
Tony Lee84d430c2019-06-13 15:26:15 +0800308{
Tony Lee6c595012019-06-19 10:54:59 +0800309 std::ifstream jsonFile(configFile);
310 if (!jsonFile.is_open())
311 {
312 log<level::ERR>("NVMe config JSON file not found");
313 }
314
315 auto data = Json::parse(jsonFile, nullptr, false);
316 if (data.is_discarded())
317 {
318 log<level::ERR>("NVMe config readings JSON parser failure");
319 }
320
321 return data;
Tony Lee84d430c2019-06-13 15:26:15 +0800322}
323
Tony Lee6c595012019-06-19 10:54:59 +0800324/** @brief Obtain the initial configuration value of NVMe */
325std::vector<phosphor::nvme::Nvme::NVMeConfig> Nvme::getNvmeConfig()
326{
327
328 phosphor::nvme::Nvme::NVMeConfig nvmeConfig;
329 std::vector<phosphor::nvme::Nvme::NVMeConfig> nvmeConfigs;
330 int8_t criticalHigh = 0;
331 int8_t criticalLow = 0;
332 int8_t maxValue = 0;
333 int8_t minValue = 0;
334 int8_t warningHigh = 0;
335 int8_t warningLow = 0;
336
337 try
338 {
339 auto data = parseSensorConfig();
340 static const std::vector<Json> empty{};
341 std::vector<Json> readings = data.value("config", empty);
342 std::vector<Json> thresholds = data.value("threshold", empty);
343 if (!thresholds.empty())
344 {
345 for (const auto& instance : thresholds)
346 {
347 criticalHigh = instance.value("criticalHigh", 0);
348 criticalLow = instance.value("criticalLow", 0);
349 maxValue = instance.value("maxValue", 0);
350 minValue = instance.value("minValue", 0);
351 warningHigh = instance.value("warningHigh", 0);
352 warningLow = instance.value("warningLow", 0);
353 }
354 }
355 else
356 {
357 log<level::ERR>(
358 "Invalid NVMe config file, thresholds dosen't exist");
359 }
360
361 if (!readings.empty())
362 {
363 for (const auto& instance : readings)
364 {
365 uint8_t index = instance.value("NVMeDriveIndex", 0);
366 uint8_t busID = instance.value("NVMeDriveBusID", 0);
Tony Lee89659212019-06-21 17:34:14 +0800367 std::string faultLedGroupPath =
368 instance.value("NVMeDriveFaultLEDGroupPath", "");
369 std::string locateLedGroupPath =
370 instance.value("NVMeDriveLocateLEDGroupPath", "");
George Hung873b5b32020-06-20 14:56:15 +0800371 uint16_t presentPin = instance.value("NVMeDrivePresentPin", 0);
372 uint16_t pwrGoodPin = instance.value("NVMeDrivePwrGoodPin", 0);
Tony Lee89659212019-06-21 17:34:14 +0800373 std::string locateLedControllerBusName =
374 instance.value("NVMeDriveLocateLEDControllerBusName", "");
375 std::string locateLedControllerPath =
376 instance.value("NVMeDriveLocateLEDControllerPath", "");
Tony Lee6c595012019-06-19 10:54:59 +0800377
378 nvmeConfig.index = std::to_string(index);
379 nvmeConfig.busID = busID;
Tony Lee89659212019-06-21 17:34:14 +0800380 nvmeConfig.faultLedGroupPath = faultLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800381 nvmeConfig.presentPin = presentPin;
382 nvmeConfig.pwrGoodPin = pwrGoodPin;
Tony Lee89659212019-06-21 17:34:14 +0800383 nvmeConfig.locateLedControllerBusName =
384 locateLedControllerBusName;
385 nvmeConfig.locateLedControllerPath = locateLedControllerPath;
386 nvmeConfig.locateLedGroupPath = locateLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800387 nvmeConfig.criticalHigh = criticalHigh;
388 nvmeConfig.criticalLow = criticalLow;
389 nvmeConfig.warningHigh = warningHigh;
390 nvmeConfig.warningLow = warningLow;
391 nvmeConfig.maxValue = maxValue;
392 nvmeConfig.minValue = minValue;
393 nvmeConfigs.push_back(nvmeConfig);
394 }
395 }
396 else
397 {
398 log<level::ERR>("Invalid NVMe config file, config dosen't exist");
399 }
400 }
401 catch (const Json::exception& e)
402 {
403 log<level::ERR>("Json Exception caught."), entry("MSG: %s", e.what());
404 }
405
406 return nvmeConfigs;
407}
408
409std::string Nvme::getGPIOValueOfNvme(const std::string& fullPath)
410{
411 std::string val;
412 std::ifstream ifs;
413 auto retries = 3;
414
415 while (retries != 0)
416 {
417 try
418 {
419 if (!ifs.is_open())
420 ifs.open(fullPath);
421 ifs.clear();
422 ifs.seekg(0);
423 ifs >> val;
424 }
425 catch (const std::exception& e)
426 {
427 --retries;
428 std::this_thread::sleep_for(delay);
429 log<level::ERR>("Can not open gpio path.",
430 entry("MSG: %s", e.what()));
431 continue;
432 }
433 break;
434 }
435
436 ifs.close();
437 return val;
438}
439
Tony Lee89659212019-06-21 17:34:14 +0800440void Nvme::createNVMeInventory()
441{
Patrick Williams05eedaa2020-05-13 17:58:42 -0500442 using Properties = std::map<std::string, std::variant<std::string, bool>>;
Tony Lee89659212019-06-21 17:34:14 +0800443 using Interfaces = std::map<std::string, Properties>;
444
445 std::string inventoryPath;
446 std::map<sdbusplus::message::object_path, Interfaces> obj;
447
448 for (const auto config : configs)
449 {
450 inventoryPath = "/system/chassis/motherboard/nvme" + config.index;
451
452 obj = {{
453 inventoryPath,
454 {{ITEM_IFACE, {}}, {NVME_STATUS_IFACE, {}}, {ASSET_IFACE, {}}},
455 }};
456 util::SDBusPlus::CallMethod(bus, INVENTORY_BUSNAME, INVENTORY_NAMESPACE,
457 INVENTORY_MANAGER_IFACE, "Notify", obj);
458 }
459}
460
461void Nvme::init()
462{
463 createNVMeInventory();
464}
465
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700466void Nvme::readNvmeData(NVMeConfig& config)
467{
468 std::string inventoryPath = NVME_INVENTORY_PATH + config.index;
469 NVMeData nvmeData;
470
471 // get NVMe information through i2c by busID.
472 auto success = getNVMeInfobyBusID(config.busID, nvmeData);
473 auto iter = nvmes.find(config.index);
474
475 // can not find. create dbus
476 if (iter == nvmes.end())
477 {
478 log<level::INFO>("SSD plug.",
479 entry("index = %s", config.index.c_str()));
480
481 std::string objPath = NVME_OBJ_PATH + config.index;
482 auto nvmeSSD =
483 std::make_shared<phosphor::nvme::NvmeSSD>(bus, objPath.c_str());
484 nvmes.emplace(config.index, nvmeSSD);
485
486 setNvmeInventoryProperties(true, nvmeData, inventoryPath);
487 nvmeSSD->setSensorValueToDbus(nvmeData.sensorValue);
488 nvmeSSD->setSensorThreshold(config.criticalHigh, config.criticalLow,
489 config.maxValue, config.minValue,
490 config.warningHigh, config.warningLow);
491
492 nvmeSSD->checkSensorThreshold();
493 setLEDsStatus(config, success, nvmeData);
494 }
495 else
496 {
497 setNvmeInventoryProperties(true, nvmeData, inventoryPath);
498 iter->second->setSensorValueToDbus(nvmeData.sensorValue);
499 iter->second->checkSensorThreshold();
500 setLEDsStatus(config, success, nvmeData);
501 }
502}
503
Tony Lee6c595012019-06-19 10:54:59 +0800504/** @brief Monitor NVMe drives every one second */
Tony Lee84d430c2019-06-13 15:26:15 +0800505void Nvme::read()
506{
Tony Lee6c595012019-06-19 10:54:59 +0800507 std::string devPresentPath;
508 std::string devPwrGoodPath;
Tony Lee89659212019-06-21 17:34:14 +0800509 std::string inventoryPath;
Tony Lee6c595012019-06-19 10:54:59 +0800510
511 static std::unordered_map<std::string, bool> isErrorPower;
512
513 for (auto config : configs)
514 {
515 NVMeData nvmeData;
Tony Lee6c595012019-06-19 10:54:59 +0800516
Tony Lee89659212019-06-21 17:34:14 +0800517 inventoryPath = NVME_INVENTORY_PATH + config.index;
518
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700519 if (config.presentPin)
Tony Lee6c595012019-06-19 10:54:59 +0800520 {
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700521 devPresentPath =
522 GPIO_BASE_PATH + std::to_string(config.presentPin) + "/value";
523
524 if (getGPIOValueOfNvme(devPresentPath) != IS_PRESENT)
Tony Lee6c595012019-06-19 10:54:59 +0800525 {
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700526 // Drive not present, remove nvme d-bus path ,
527 // clean all properties in inventory
528 // and turn off fault and locate LED
Tony Lee89659212019-06-21 17:34:14 +0800529
530 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700531 false);
Tony Lee89659212019-06-21 17:34:14 +0800532 setLocateLED(config.locateLedGroupPath,
533 config.locateLedControllerBusName,
534 config.locateLedControllerPath, false);
535
Tony Lee6c595012019-06-19 10:54:59 +0800536 nvmeData = NVMeData();
Tony Lee89659212019-06-21 17:34:14 +0800537 setNvmeInventoryProperties(false, nvmeData, inventoryPath);
Tony Lee6c595012019-06-19 10:54:59 +0800538 nvmes.erase(config.index);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700539 return;
540 }
541 else if (config.pwrGoodPin)
542 {
543 devPwrGoodPath = GPIO_BASE_PATH +
544 std::to_string(config.pwrGoodPin) + "/value";
Tony Lee6c595012019-06-19 10:54:59 +0800545
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700546 if (getGPIOValueOfNvme(devPwrGoodPath) != POWERGD)
Tony Lee6c595012019-06-19 10:54:59 +0800547 {
Tony Lee6c595012019-06-19 10:54:59 +0800548
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700549 // Present pin is true but power good pin is false
550 // remove nvme d-bus path, clean all properties in inventory
551 // and turn on fault LED
552
553 setFaultLED(config.locateLedGroupPath,
554 config.faultLedGroupPath, true);
555 setLocateLED(config.locateLedGroupPath,
556 config.locateLedControllerBusName,
557 config.locateLedControllerPath, false);
558
559 nvmeData = NVMeData();
560 setNvmeInventoryProperties(false, nvmeData, inventoryPath);
561 nvmes.erase(config.index);
562
563 if (isErrorPower[config.index] != true)
564 {
565 log<level::ERR>(
566 "Present pin is true but power good pin is false.",
567 entry("index = %s", config.index.c_str()));
568 log<level::ERR>(
569 "Erase SSD from map and d-bus.",
570 entry("index = %s", config.index.c_str()));
571
572 isErrorPower[config.index] = true;
573 }
574 return;
Tony Lee6c595012019-06-19 10:54:59 +0800575 }
576 }
577 }
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700578 // Drive status is good, update value or create d-bus and update
579 // value.
580 readNvmeData(config);
Tony Lee89659212019-06-21 17:34:14 +0800581
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700582 isErrorPower[config.index] = false;
Tony Lee6c595012019-06-19 10:54:59 +0800583 }
Tony Lee84d430c2019-06-13 15:26:15 +0800584}
585} // namespace nvme
586} // namespace phosphor