blob: 54ca99287d90419ccd7824c6bfeec0f4f416d0d4 [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
Potin Lai6e149e92022-11-23 10:04:05 +080017#define MAX_SMBUS_ERROR_RETRY 0
Tony Lee6c595012019-06-19 10:54:59 +080018#define NVME_SSD_SLAVE_ADDRESS 0x6a
George Hung92a15ba2020-09-14 17:14:52 +080019#define NVME_SSD_VPD_SLAVE_ADDRESS 0x53
Tony Lee6c595012019-06-19 10:54:59 +080020#define GPIO_BASE_PATH "/sys/class/gpio/gpio"
21#define IS_PRESENT "0"
22#define POWERGD "1"
Tony Lee89659212019-06-21 17:34:14 +080023#define NOWARNING_STRING "ff"
Tony Lee6c595012019-06-19 10:54:59 +080024
25static constexpr auto configFile = "/etc/nvme/nvme_config.json";
26static constexpr auto delay = std::chrono::milliseconds{100};
27using Json = nlohmann::json;
28
29static constexpr const uint8_t COMMAND_CODE_0 = 0;
30static constexpr const uint8_t COMMAND_CODE_8 = 8;
George Hung92a15ba2020-09-14 17:14:52 +080031static constexpr const uint8_t CODE_0_LENGTH = 8;
32static constexpr const uint8_t CODE_8_LENGTH = 23;
Tony Lee6c595012019-06-19 10:54:59 +080033
Tony Lee89659212019-06-21 17:34:14 +080034static constexpr int CapacityFaultMask = 1;
35static constexpr int temperatureFaultMask = 1 << 1;
36static constexpr int DegradesFaultMask = 1 << 2;
37static constexpr int MediaFaultMask = 1 << 3;
38static constexpr int BackupDeviceFaultMask = 1 << 4;
39static constexpr int NOWARNING = 255;
40
Tony Lee6c595012019-06-19 10:54:59 +080041static constexpr int SERIALNUMBER_START_INDEX = 3;
42static constexpr int SERIALNUMBER_END_INDEX = 23;
George Hung92a15ba2020-09-14 17:14:52 +080043static constexpr int MODELNUMBER_START_INDEX = 46;
44static constexpr int MODELNUMBER_END_INDEX = 85;
Tony Lee6c595012019-06-19 10:54:59 +080045
46static constexpr const int TEMPERATURE_SENSOR_FAILURE = 0x81;
47
Brandon Kim8353c5f2022-07-08 17:09:23 -070048static std::map<std::string, std::string> map_vendor = {
49 {"80 86", "Intel"}, {"1e f", "Kioxia"}, {"14 4d", "Samsung"}};
George Hung69b96182020-08-20 17:19:56 +080050
Tony Lee6c595012019-06-19 10:54:59 +080051namespace fs = std::filesystem;
52
Tony Lee84d430c2019-06-13 15:26:15 +080053namespace phosphor
54{
55namespace nvme
56{
57
58using namespace std;
59using namespace phosphor::logging;
60
Tony Lee89659212019-06-21 17:34:14 +080061void Nvme::setNvmeInventoryProperties(
Chanh Nguyene528b0a2021-07-14 16:57:57 +070062 NVMeConfig& config, bool present,
63 const phosphor::nvme::Nvme::NVMeData& nvmeData,
Tony Lee89659212019-06-21 17:34:14 +080064 const std::string& inventoryPath)
65{
Chanh Nguyene528b0a2021-07-14 16:57:57 +070066 static std::unordered_map<int, std::string> preSerial;
67 static std::unordered_map<int, std::string> preSmartWarning;
68 static std::unordered_map<int, std::string> preStatusFlags;
Tony Lee89659212019-06-21 17:34:14 +080069
Chanh Nguyene528b0a2021-07-14 16:57:57 +070070 if (preSerial[config.busID].compare(nvmeData.serialNumber) != 0)
71 {
72 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
73 ITEM_IFACE, "Present", present);
74 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
75 ASSET_IFACE, "Manufacturer",
76 nvmeData.vendor);
77 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
78 ASSET_IFACE, "SerialNumber",
79 nvmeData.serialNumber);
80 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
81 ASSET_IFACE, "Model",
82 nvmeData.modelNumber);
83 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
84 NVME_STATUS_IFACE, "DriveLifeUsed",
85 nvmeData.driveLifeUsed);
86 preSerial[config.busID] = nvmeData.serialNumber;
87 }
Tony Lee89659212019-06-21 17:34:14 +080088
Chanh Nguyene528b0a2021-07-14 16:57:57 +070089 if (preStatusFlags[config.busID].compare(nvmeData.statusFlags) != 0)
90 {
91 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
92 NVME_STATUS_IFACE, "StatusFlags",
93 nvmeData.statusFlags);
94 preStatusFlags[config.busID] = nvmeData.statusFlags;
95 }
Tony Lee89659212019-06-21 17:34:14 +080096
Chanh Nguyene528b0a2021-07-14 16:57:57 +070097 if (preSmartWarning[config.busID].compare(nvmeData.smartWarnings) != 0)
98 {
99 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
100 NVME_STATUS_IFACE, "SmartWarnings",
101 nvmeData.smartWarnings);
102 auto smartWarning = (!nvmeData.smartWarnings.empty())
103 ? std::stoi(nvmeData.smartWarnings, 0, 16)
104 : NOWARNING;
Tony Lee89659212019-06-21 17:34:14 +0800105
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700106 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
107 NVME_STATUS_IFACE, "CapacityFault",
108 !(smartWarning & CapacityFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800109
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700110 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
111 NVME_STATUS_IFACE, "TemperatureFault",
112 !(smartWarning & temperatureFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800113
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700114 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
115 NVME_STATUS_IFACE, "DegradesFault",
116 !(smartWarning & DegradesFaultMask));
117
118 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
119 NVME_STATUS_IFACE, "MediaFault",
120 !(smartWarning & MediaFaultMask));
121
122 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
123 NVME_STATUS_IFACE, "BackupDeviceFault",
124 !(smartWarning & BackupDeviceFaultMask));
125 preSmartWarning[config.busID] = nvmeData.smartWarnings;
126 }
Tony Lee89659212019-06-21 17:34:14 +0800127}
128
129void Nvme::setFaultLED(const std::string& locateLedGroupPath,
130 const std::string& faultLedGroupPath, bool request)
131{
132 if (locateLedGroupPath.empty() || faultLedGroupPath.empty())
133 {
134 return;
135 }
136
137 // Before toggle LED, check whether is Identify or not.
138 if (!getLEDGroupState(locateLedGroupPath))
139 {
Brandon Kimfdffe5c2021-03-30 15:07:59 -0700140 if (getLEDGroupState(faultLedGroupPath) != request)
141 {
142 util::SDBusPlus::setProperty(bus, LED_GROUP_BUSNAME,
143 faultLedGroupPath, LED_GROUP_IFACE,
144 "Asserted", request);
145 }
Tony Lee89659212019-06-21 17:34:14 +0800146 }
147}
148
149void Nvme::setLocateLED(const std::string& locateLedGroupPath,
150 const std::string& locateLedBusName,
151 const std::string& locateLedPath, bool isPresent)
152{
153 if (locateLedGroupPath.empty() || locateLedBusName.empty() ||
154 locateLedPath.empty())
155 {
156 return;
157 }
158
159 namespace server = sdbusplus::xyz::openbmc_project::Led::server;
160
161 if (!getLEDGroupState(locateLedGroupPath))
162 {
163 if (isPresent)
164 util::SDBusPlus::setProperty(
165 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
166 "State",
167 server::convertForMessage(server::Physical::Action::On));
168 else
169 util::SDBusPlus::setProperty(
170 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
171 "State",
172 server::convertForMessage(server::Physical::Action::Off));
173 }
174}
175
176bool Nvme::getLEDGroupState(const std::string& ledPath)
177{
178 auto asserted = util::SDBusPlus::getProperty<bool>(
179 bus, LED_GROUP_BUSNAME, ledPath, LED_GROUP_IFACE, "Asserted");
180
181 return asserted;
182}
183
184void Nvme::setLEDsStatus(const phosphor::nvme::Nvme::NVMeConfig& config,
185 bool success,
186 const phosphor::nvme::Nvme::NVMeData& nvmeData)
187{
Tony Lee89659212019-06-21 17:34:14 +0800188
189 if (success)
190 {
191 if (!nvmeData.smartWarnings.empty())
192 {
193 auto request =
194 (strcmp(nvmeData.smartWarnings.c_str(), NOWARNING_STRING) == 0)
195 ? false
196 : true;
197
198 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
199 request);
200 setLocateLED(config.locateLedGroupPath,
201 config.locateLedControllerBusName,
202 config.locateLedControllerPath, !request);
203 }
204 isError[config.index] = false;
205 }
206 else
207 {
208 if (isError[config.index] != true)
209 {
210 // Drive is present but can not get data, turn on fault LED.
211 log<level::ERR>("Drive status is good but can not get data.",
Brandon Kim9b771e22020-10-05 12:02:01 -0700212 entry("OBJ_PATH=%s", config.index.c_str()));
Tony Lee89659212019-06-21 17:34:14 +0800213 isError[config.index] = true;
214 }
215
216 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath, true);
217 setLocateLED(config.locateLedGroupPath,
218 config.locateLedControllerBusName,
219 config.locateLedControllerPath, false);
220 }
221}
222
Tony Lee6c595012019-06-19 10:54:59 +0800223std::string intToHex(int input)
224{
225 std::stringstream tmp;
226 tmp << std::hex << input;
227
228 return tmp.str();
229}
230
231/** @brief Get NVMe info over smbus */
George Hung5e23bcd2021-07-01 12:16:11 +0800232bool Nvme::getNVMeInfobyBusID(int busID,
233 phosphor::nvme::Nvme::NVMeData& nvmeData)
Tony Lee6c595012019-06-19 10:54:59 +0800234{
235 nvmeData.present = true;
236 nvmeData.vendor = "";
237 nvmeData.serialNumber = "";
George Hung831f2042021-05-19 17:02:29 +0800238 nvmeData.modelNumber = "";
Tony Lee6c595012019-06-19 10:54:59 +0800239 nvmeData.smartWarnings = "";
240 nvmeData.statusFlags = "";
241 nvmeData.driveLifeUsed = "";
Brandon Kimd5838d12021-05-19 12:51:55 -0700242 nvmeData.sensorValue = static_cast<int8_t>(TEMPERATURE_SENSOR_FAILURE);
George Hung831f2042021-05-19 17:02:29 +0800243 nvmeData.wcTemp = 0;
Tony Lee6c595012019-06-19 10:54:59 +0800244
245 phosphor::smbus::Smbus smbus;
246
247 unsigned char rsp_data_command_0[I2C_DATA_MAX] = {0};
248 unsigned char rsp_data_command_8[I2C_DATA_MAX] = {0};
249
250 uint8_t tx_data = COMMAND_CODE_0;
251
252 auto init = smbus.smbusInit(busID);
253
Tony Lee6c595012019-06-19 10:54:59 +0800254 if (init == -1)
255 {
256 if (isErrorSmbus[busID] != true)
257 {
258 log<level::ERR>("smbusInit fail!");
259 isErrorSmbus[busID] = true;
260 }
261
262 nvmeData.present = false;
263
264 return nvmeData.present;
265 }
266
George Hung92a15ba2020-09-14 17:14:52 +0800267 auto res_int = smbus.SendSmbusRWCmdRAW(busID, NVME_SSD_SLAVE_ADDRESS,
268 &tx_data, sizeof(tx_data),
269 rsp_data_command_0, CODE_0_LENGTH);
Tony Lee6c595012019-06-19 10:54:59 +0800270
271 if (res_int < 0)
272 {
273 if (isErrorSmbus[busID] != true)
274 {
275 log<level::ERR>("Send command code 0 fail!");
276 isErrorSmbus[busID] = true;
277 }
278
279 smbus.smbusClose(busID);
280 nvmeData.present = false;
281 return nvmeData.present;
282 }
283
George Hung92a15ba2020-09-14 17:14:52 +0800284 nvmeData.statusFlags = intToHex(rsp_data_command_0[1]);
285 nvmeData.smartWarnings = intToHex(rsp_data_command_0[2]);
286 nvmeData.driveLifeUsed = intToHex(rsp_data_command_0[4]);
George Hung93455332021-01-06 20:57:04 +0800287 nvmeData.sensorValue = static_cast<int8_t>(rsp_data_command_0[3]);
288 nvmeData.wcTemp = static_cast<int8_t>(rsp_data_command_0[5]);
George Hung92a15ba2020-09-14 17:14:52 +0800289
Tony Lee6c595012019-06-19 10:54:59 +0800290 tx_data = COMMAND_CODE_8;
291
George Hung92a15ba2020-09-14 17:14:52 +0800292 res_int = smbus.SendSmbusRWCmdRAW(busID, NVME_SSD_SLAVE_ADDRESS, &tx_data,
293 sizeof(tx_data), rsp_data_command_8,
294 CODE_8_LENGTH);
Tony Lee6c595012019-06-19 10:54:59 +0800295
296 if (res_int < 0)
297 {
298 if (isErrorSmbus[busID] != true)
299 {
300 log<level::ERR>("Send command code 8 fail!");
301 isErrorSmbus[busID] = true;
302 }
303
304 smbus.smbusClose(busID);
305 nvmeData.present = false;
306 return nvmeData.present;
307 }
308
309 nvmeData.vendor =
310 intToHex(rsp_data_command_8[1]) + " " + intToHex(rsp_data_command_8[2]);
311
George Hung69b96182020-08-20 17:19:56 +0800312 for (auto iter = map_vendor.begin(); iter != map_vendor.end(); iter++)
313 {
314 if (iter->first == nvmeData.vendor)
315 {
316 nvmeData.vendor = iter->second;
317 break;
318 }
319 }
320
Tony Lee6c595012019-06-19 10:54:59 +0800321 for (int offset = SERIALNUMBER_START_INDEX; offset < SERIALNUMBER_END_INDEX;
322 offset++)
323 {
George Hung31c3a2f2021-07-29 19:15:14 +0800324 // Only accept digits/letters/punctuation characters.
325 if (rsp_data_command_8[offset] >= '!' &&
326 rsp_data_command_8[offset] <= '~')
George Hung69b96182020-08-20 17:19:56 +0800327 nvmeData.serialNumber +=
328 static_cast<char>(rsp_data_command_8[offset]);
Tony Lee6c595012019-06-19 10:54:59 +0800329 }
330
Brandon Kim89a24e12022-11-18 21:05:30 +0000331 if ((nvmeData.vendor == "Samsung") || (nvmeData.vendor == "Kioxia"))
George Hung92a15ba2020-09-14 17:14:52 +0800332 {
333 unsigned char rsp_data_vpd[I2C_DATA_MAX] = {0};
334 const int rx_len = (MODELNUMBER_END_INDEX - MODELNUMBER_START_INDEX);
335 tx_data = MODELNUMBER_START_INDEX;
336
337 auto res_int =
338 smbus.SendSmbusRWCmdRAW(busID, NVME_SSD_VPD_SLAVE_ADDRESS, &tx_data,
339 sizeof(tx_data), rsp_data_vpd, rx_len);
340
341 if (res_int < 0)
342 {
343 if (isErrorSmbus[busID] != true)
344 {
345 log<level::ERR>("Send command read VPD fail!");
346 isErrorSmbus[busID] = true;
347 }
348
349 smbus.smbusClose(busID);
350 nvmeData.present = false;
351 return nvmeData.present;
352 }
353
354 for (int i = 0; i < rx_len; i++)
355 {
George Hung31c3a2f2021-07-29 19:15:14 +0800356 // Only accept digits/letters/punctuation characters.
357 if ((rsp_data_vpd[i] >= '!' && rsp_data_vpd[i] <= '~'))
George Hung92a15ba2020-09-14 17:14:52 +0800358 nvmeData.modelNumber += static_cast<char>(rsp_data_vpd[i]);
359 }
360
361 if (nvmeData.modelNumber.substr(0, nvmeData.vendor.size()) == "SAMSUNG")
362 nvmeData.modelNumber.erase(0, nvmeData.vendor.size());
363 }
Tony Lee6c595012019-06-19 10:54:59 +0800364
365 smbus.smbusClose(busID);
366
367 isErrorSmbus[busID] = false;
368
369 return nvmeData.present;
370}
371
Tony Lee84d430c2019-06-13 15:26:15 +0800372void Nvme::run()
373{
Tony Lee89659212019-06-21 17:34:14 +0800374 init();
375
Tony Lee84d430c2019-06-13 15:26:15 +0800376 std::function<void()> callback(std::bind(&Nvme::read, this));
377 try
378 {
Potin Laibcae0872022-10-20 15:29:17 +0800379 u_int64_t interval = monitorIntervalSec * 1000000;
Tony Lee84d430c2019-06-13 15:26:15 +0800380 _timer.restart(std::chrono::microseconds(interval));
381 }
382 catch (const std::exception& e)
383 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700384 log<level::ERR>("Error in polling loop. "), entry("ERROR=%s", e.what());
Tony Lee84d430c2019-06-13 15:26:15 +0800385 }
386}
387
Tony Lee6c595012019-06-19 10:54:59 +0800388/** @brief Parsing NVMe config JSON file */
389Json parseSensorConfig()
Tony Lee84d430c2019-06-13 15:26:15 +0800390{
Tony Lee6c595012019-06-19 10:54:59 +0800391 std::ifstream jsonFile(configFile);
392 if (!jsonFile.is_open())
393 {
394 log<level::ERR>("NVMe config JSON file not found");
395 }
396
397 auto data = Json::parse(jsonFile, nullptr, false);
398 if (data.is_discarded())
399 {
400 log<level::ERR>("NVMe config readings JSON parser failure");
401 }
402
403 return data;
Tony Lee84d430c2019-06-13 15:26:15 +0800404}
405
Tony Lee6c595012019-06-19 10:54:59 +0800406/** @brief Obtain the initial configuration value of NVMe */
407std::vector<phosphor::nvme::Nvme::NVMeConfig> Nvme::getNvmeConfig()
408{
409
410 phosphor::nvme::Nvme::NVMeConfig nvmeConfig;
411 std::vector<phosphor::nvme::Nvme::NVMeConfig> nvmeConfigs;
412 int8_t criticalHigh = 0;
413 int8_t criticalLow = 0;
414 int8_t maxValue = 0;
415 int8_t minValue = 0;
416 int8_t warningHigh = 0;
417 int8_t warningLow = 0;
418
419 try
420 {
421 auto data = parseSensorConfig();
422 static const std::vector<Json> empty{};
423 std::vector<Json> readings = data.value("config", empty);
424 std::vector<Json> thresholds = data.value("threshold", empty);
Potin Laibcae0872022-10-20 15:29:17 +0800425 monitorIntervalSec =
426 data.value("monitorIntervalSec", MONITOR_INTERVAL_SECONDS);
Potin Lai6e149e92022-11-23 10:04:05 +0800427 maxSmbusErrorRetry =
428 data.value("maxSmbusErrorRetry", MAX_SMBUS_ERROR_RETRY);
Potin Laibcae0872022-10-20 15:29:17 +0800429
Tony Lee6c595012019-06-19 10:54:59 +0800430 if (!thresholds.empty())
431 {
432 for (const auto& instance : thresholds)
433 {
434 criticalHigh = instance.value("criticalHigh", 0);
435 criticalLow = instance.value("criticalLow", 0);
436 maxValue = instance.value("maxValue", 0);
437 minValue = instance.value("minValue", 0);
438 warningHigh = instance.value("warningHigh", 0);
439 warningLow = instance.value("warningLow", 0);
440 }
441 }
442 else
443 {
444 log<level::ERR>(
445 "Invalid NVMe config file, thresholds dosen't exist");
446 }
447
448 if (!readings.empty())
449 {
450 for (const auto& instance : readings)
451 {
452 uint8_t index = instance.value("NVMeDriveIndex", 0);
453 uint8_t busID = instance.value("NVMeDriveBusID", 0);
Tony Lee89659212019-06-21 17:34:14 +0800454 std::string faultLedGroupPath =
455 instance.value("NVMeDriveFaultLEDGroupPath", "");
456 std::string locateLedGroupPath =
457 instance.value("NVMeDriveLocateLEDGroupPath", "");
George Hung873b5b32020-06-20 14:56:15 +0800458 uint16_t presentPin = instance.value("NVMeDrivePresentPin", 0);
459 uint16_t pwrGoodPin = instance.value("NVMeDrivePwrGoodPin", 0);
Tony Lee89659212019-06-21 17:34:14 +0800460 std::string locateLedControllerBusName =
461 instance.value("NVMeDriveLocateLEDControllerBusName", "");
462 std::string locateLedControllerPath =
463 instance.value("NVMeDriveLocateLEDControllerPath", "");
Tony Lee6c595012019-06-19 10:54:59 +0800464
465 nvmeConfig.index = std::to_string(index);
466 nvmeConfig.busID = busID;
Tony Lee89659212019-06-21 17:34:14 +0800467 nvmeConfig.faultLedGroupPath = faultLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800468 nvmeConfig.presentPin = presentPin;
469 nvmeConfig.pwrGoodPin = pwrGoodPin;
Tony Lee89659212019-06-21 17:34:14 +0800470 nvmeConfig.locateLedControllerBusName =
471 locateLedControllerBusName;
472 nvmeConfig.locateLedControllerPath = locateLedControllerPath;
473 nvmeConfig.locateLedGroupPath = locateLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800474 nvmeConfig.criticalHigh = criticalHigh;
475 nvmeConfig.criticalLow = criticalLow;
476 nvmeConfig.warningHigh = warningHigh;
477 nvmeConfig.warningLow = warningLow;
478 nvmeConfig.maxValue = maxValue;
479 nvmeConfig.minValue = minValue;
480 nvmeConfigs.push_back(nvmeConfig);
481 }
482 }
483 else
484 {
485 log<level::ERR>("Invalid NVMe config file, config dosen't exist");
486 }
487 }
488 catch (const Json::exception& e)
489 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700490 log<level::ERR>("Json Exception caught."), entry("MSG=%s", e.what());
Tony Lee6c595012019-06-19 10:54:59 +0800491 }
492
493 return nvmeConfigs;
494}
495
496std::string Nvme::getGPIOValueOfNvme(const std::string& fullPath)
497{
498 std::string val;
499 std::ifstream ifs;
500 auto retries = 3;
501
502 while (retries != 0)
503 {
504 try
505 {
506 if (!ifs.is_open())
507 ifs.open(fullPath);
508 ifs.clear();
509 ifs.seekg(0);
510 ifs >> val;
511 }
512 catch (const std::exception& e)
513 {
514 --retries;
515 std::this_thread::sleep_for(delay);
516 log<level::ERR>("Can not open gpio path.",
Brandon Kim9b771e22020-10-05 12:02:01 -0700517 entry("MSG=%s", e.what()));
Tony Lee6c595012019-06-19 10:54:59 +0800518 continue;
519 }
520 break;
521 }
522
523 ifs.close();
524 return val;
525}
526
Tony Lee89659212019-06-21 17:34:14 +0800527void Nvme::createNVMeInventory()
528{
Patrick Williams05eedaa2020-05-13 17:58:42 -0500529 using Properties = std::map<std::string, std::variant<std::string, bool>>;
Tony Lee89659212019-06-21 17:34:14 +0800530 using Interfaces = std::map<std::string, Properties>;
531
532 std::string inventoryPath;
533 std::map<sdbusplus::message::object_path, Interfaces> obj;
534
George Hung831f2042021-05-19 17:02:29 +0800535 for (const auto& config : configs)
Tony Lee89659212019-06-21 17:34:14 +0800536 {
537 inventoryPath = "/system/chassis/motherboard/nvme" + config.index;
538
539 obj = {{
540 inventoryPath,
541 {{ITEM_IFACE, {}}, {NVME_STATUS_IFACE, {}}, {ASSET_IFACE, {}}},
542 }};
543 util::SDBusPlus::CallMethod(bus, INVENTORY_BUSNAME, INVENTORY_NAMESPACE,
544 INVENTORY_MANAGER_IFACE, "Notify", obj);
545 }
546}
547
548void Nvme::init()
549{
550 createNVMeInventory();
551}
552
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700553void Nvme::readNvmeData(NVMeConfig& config)
554{
555 std::string inventoryPath = NVME_INVENTORY_PATH + config.index;
556 NVMeData nvmeData;
557
558 // get NVMe information through i2c by busID.
559 auto success = getNVMeInfobyBusID(config.busID, nvmeData);
560 auto iter = nvmes.find(config.index);
561
Potin Lai6e149e92022-11-23 10:04:05 +0800562 if (success)
563 {
564 nvmeSmbusErrCnt[config.busID] = 0;
565 }
566 else
567 {
568 if (nvmeSmbusErrCnt[config.busID] < maxSmbusErrorRetry)
569 {
570 // skip this time if error count less than maxSmbusErrorRetry
571 nvmeSmbusErrCnt[config.busID]++;
572 log<level::INFO>("getNVMeInfobyBusID failed, retry...",
573 entry("INDEX=%s", config.index.c_str()),
574 entry("ERRCNT=%u", nvmeSmbusErrCnt[config.busID]));
575 return;
576 }
577 }
578
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700579 // can not find. create dbus
580 if (iter == nvmes.end())
581 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700582 log<level::INFO>("SSD plug.", entry("INDEX=%s", config.index.c_str()));
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700583
584 std::string objPath = NVME_OBJ_PATH + config.index;
585 auto nvmeSSD =
586 std::make_shared<phosphor::nvme::NvmeSSD>(bus, objPath.c_str());
587 nvmes.emplace(config.index, nvmeSSD);
588
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700589 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700590 nvmeSSD->setSensorValueToDbus(nvmeData.sensorValue);
George Hung93455332021-01-06 20:57:04 +0800591 if (nvmeData.wcTemp != 0)
592 {
593 config.criticalHigh = nvmeData.wcTemp;
594 config.warningHigh = nvmeData.wcTemp;
595 }
George Hung831f2042021-05-19 17:02:29 +0800596 nvmeSSD->setSensorMaxMin(config.maxValue, config.minValue);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700597 nvmeSSD->setSensorThreshold(config.criticalHigh, config.criticalLow,
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700598 config.warningHigh, config.warningLow);
599
600 nvmeSSD->checkSensorThreshold();
601 setLEDsStatus(config, success, nvmeData);
602 }
603 else
604 {
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700605 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700606 iter->second->setSensorValueToDbus(nvmeData.sensorValue);
George Hung831f2042021-05-19 17:02:29 +0800607 if (nvmeData.wcTemp != 0)
608 {
George Hungbe343992021-07-02 09:15:54 +0800609 config.criticalHigh = nvmeData.wcTemp;
610 config.warningHigh = nvmeData.wcTemp;
611
George Hung831f2042021-05-19 17:02:29 +0800612 iter->second->setSensorThreshold(
613 config.criticalHigh, config.criticalLow, config.warningHigh,
614 config.warningLow);
615 }
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700616 iter->second->checkSensorThreshold();
617 setLEDsStatus(config, success, nvmeData);
618 }
619}
620
Tony Lee6c595012019-06-19 10:54:59 +0800621/** @brief Monitor NVMe drives every one second */
Tony Lee84d430c2019-06-13 15:26:15 +0800622void Nvme::read()
623{
Tony Lee6c595012019-06-19 10:54:59 +0800624 std::string devPresentPath;
625 std::string devPwrGoodPath;
Tony Lee89659212019-06-21 17:34:14 +0800626 std::string inventoryPath;
Tony Lee6c595012019-06-19 10:54:59 +0800627
Tony Lee6c595012019-06-19 10:54:59 +0800628 for (auto config : configs)
629 {
630 NVMeData nvmeData;
Tony Lee6c595012019-06-19 10:54:59 +0800631
Tony Lee89659212019-06-21 17:34:14 +0800632 inventoryPath = NVME_INVENTORY_PATH + config.index;
Potin Laiaef8fa02022-09-21 19:10:30 +0800633 devPresentPath =
634 GPIO_BASE_PATH + std::to_string(config.presentPin) + "/value";
635 devPwrGoodPath =
636 GPIO_BASE_PATH + std::to_string(config.pwrGoodPin) + "/value";
Tony Lee89659212019-06-21 17:34:14 +0800637
Potin Laiaef8fa02022-09-21 19:10:30 +0800638 auto presentPinValStr = (config.presentPin)
639 ? getGPIOValueOfNvme(devPresentPath)
640 : IS_PRESENT;
641 auto pwrGoodPinValStr =
642 (config.pwrGoodPin) ? getGPIOValueOfNvme(devPwrGoodPath) : POWERGD;
Potin Lai76f455e2022-09-22 00:11:26 +0800643 const bool isPwrGood = (pwrGoodPinValStr == POWERGD);
Potin Laiaef8fa02022-09-21 19:10:30 +0800644
645 if (presentPinValStr != IS_PRESENT)
Tony Lee6c595012019-06-19 10:54:59 +0800646 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800647 // Drive not present, remove nvme d-bus path ,
648 // clean all properties in inventory
649 // and turn off fault and locate LED
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700650
Potin Laiaef8fa02022-09-21 19:10:30 +0800651 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
652 false);
653 setLocateLED(config.locateLedGroupPath,
654 config.locateLedControllerBusName,
655 config.locateLedControllerPath, false);
656
657 nvmeData = NVMeData();
658 setNvmeInventoryProperties(config, false, nvmeData, inventoryPath);
659 nvmes.erase(config.index);
660 continue;
661 }
662
Potin Lai76f455e2022-09-22 00:11:26 +0800663 if (!isPwrGood)
Potin Laiaef8fa02022-09-21 19:10:30 +0800664 {
665 // IFDET should be used to provide the final say
666 // in SSD's presence - IFDET showing SSD is present
667 // but the power is off (if the drive is plugged in)
668 // is a valid state.
669
670 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
671 true);
672 setLocateLED(config.locateLedGroupPath,
673 config.locateLedControllerBusName,
674 config.locateLedControllerPath, false);
675
676 nvmeData = NVMeData();
677 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
678
679 if (isErrorPower[config.index] != true)
Tony Lee6c595012019-06-19 10:54:59 +0800680 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800681 log<level::ERR>(
682 "Present pin is true but power good pin is false.",
683 entry("INDEX=%s", config.index.c_str()));
684 log<level::ERR>("Erase SSD from map and d-bus.",
685 entry("INDEX=%s", config.index.c_str()));
Tony Lee89659212019-06-21 17:34:14 +0800686
Potin Laiaef8fa02022-09-21 19:10:30 +0800687 isErrorPower[config.index] = true;
Tony Lee6c595012019-06-19 10:54:59 +0800688 }
689 }
Potin Laiaef8fa02022-09-21 19:10:30 +0800690 else
691 {
692 isErrorPower[config.index] = false;
693 }
Tony Lee89659212019-06-21 17:34:14 +0800694
Potin Laiaef8fa02022-09-21 19:10:30 +0800695 // Keep reading to report the invalid temperature
696 // (To make thermal loop know that the sensor reading
697 // is invalid).
698 readNvmeData(config);
Potin Lai6e149e92022-11-23 10:04:05 +0800699 if (nvmes.find(config.index) != nvmes.end())
700 {
701 nvmes.find(config.index)->second->setSensorAvailability(isPwrGood);
702 }
Tony Lee6c595012019-06-19 10:54:59 +0800703 }
Tony Lee84d430c2019-06-13 15:26:15 +0800704}
705} // namespace nvme
706} // namespace phosphor