blob: d10d1d008838725b96579430dd7ef385896a6b26 [file] [log] [blame]
Tony Lee84d430c2019-06-13 15:26:15 +08001#include "nvme_manager.hpp"
2
Patrick Williams7da98582023-05-10 07:50:46 -05003#include "i2c.h"
4
Tony Lee6c595012019-06-19 10:54:59 +08005#include "smbus.hpp"
6
Tony Lee6c595012019-06-19 10:54:59 +08007#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 Lee89659212019-06-21 17:34:14 +080011#include <xyz/openbmc_project/Led/Physical/server.hpp>
Tony Lee84d430c2019-06-13 15:26:15 +080012
Patrick Williams7da98582023-05-10 07:50:46 -050013#include <filesystem>
14#include <map>
15#include <sstream>
16#include <string>
Tony Lee84d430c2019-06-13 15:26:15 +080017#define MONITOR_INTERVAL_SECONDS 1
Potin Lai6e149e92022-11-23 10:04:05 +080018#define MAX_SMBUS_ERROR_RETRY 0
Tony Lee6c595012019-06-19 10:54:59 +080019#define NVME_SSD_SLAVE_ADDRESS 0x6a
George Hung92a15ba2020-09-14 17:14:52 +080020#define NVME_SSD_VPD_SLAVE_ADDRESS 0x53
Tony Lee6c595012019-06-19 10:54:59 +080021#define GPIO_BASE_PATH "/sys/class/gpio/gpio"
22#define IS_PRESENT "0"
23#define POWERGD "1"
Tony Lee89659212019-06-21 17:34:14 +080024#define NOWARNING_STRING "ff"
Tony Lee6c595012019-06-19 10:54:59 +080025
26static constexpr auto configFile = "/etc/nvme/nvme_config.json";
27static constexpr auto delay = std::chrono::milliseconds{100};
28using Json = nlohmann::json;
29
30static constexpr const uint8_t COMMAND_CODE_0 = 0;
31static constexpr const uint8_t COMMAND_CODE_8 = 8;
George Hung92a15ba2020-09-14 17:14:52 +080032static constexpr const uint8_t CODE_0_LENGTH = 8;
33static constexpr const uint8_t CODE_8_LENGTH = 23;
Tony Lee6c595012019-06-19 10:54:59 +080034
Tony Lee89659212019-06-21 17:34:14 +080035static constexpr int CapacityFaultMask = 1;
36static constexpr int temperatureFaultMask = 1 << 1;
37static constexpr int DegradesFaultMask = 1 << 2;
38static constexpr int MediaFaultMask = 1 << 3;
39static constexpr int BackupDeviceFaultMask = 1 << 4;
40static constexpr int NOWARNING = 255;
41
Tony Lee6c595012019-06-19 10:54:59 +080042static constexpr int SERIALNUMBER_START_INDEX = 3;
43static constexpr int SERIALNUMBER_END_INDEX = 23;
George Hung92a15ba2020-09-14 17:14:52 +080044static constexpr int MODELNUMBER_START_INDEX = 46;
45static constexpr int MODELNUMBER_END_INDEX = 85;
Tony Lee6c595012019-06-19 10:54:59 +080046
47static constexpr const int TEMPERATURE_SENSOR_FAILURE = 0x81;
48
Brandon Kim8353c5f2022-07-08 17:09:23 -070049static std::map<std::string, std::string> map_vendor = {
50 {"80 86", "Intel"}, {"1e f", "Kioxia"}, {"14 4d", "Samsung"}};
George Hung69b96182020-08-20 17:19:56 +080051
Tony Lee6c595012019-06-19 10:54:59 +080052namespace fs = std::filesystem;
53
Tony Lee84d430c2019-06-13 15:26:15 +080054namespace phosphor
55{
56namespace nvme
57{
58
59using namespace std;
60using namespace phosphor::logging;
61
Tony Lee89659212019-06-21 17:34:14 +080062void Nvme::setNvmeInventoryProperties(
Chanh Nguyene528b0a2021-07-14 16:57:57 +070063 NVMeConfig& config, bool present,
64 const phosphor::nvme::Nvme::NVMeData& nvmeData,
Tony Lee89659212019-06-21 17:34:14 +080065 const std::string& inventoryPath)
66{
Chanh Nguyene528b0a2021-07-14 16:57:57 +070067 static std::unordered_map<int, std::string> preSerial;
68 static std::unordered_map<int, std::string> preSmartWarning;
69 static std::unordered_map<int, std::string> preStatusFlags;
Tony Lee89659212019-06-21 17:34:14 +080070
Chanh Nguyene528b0a2021-07-14 16:57:57 +070071 if (preSerial[config.busID].compare(nvmeData.serialNumber) != 0)
72 {
73 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
74 ITEM_IFACE, "Present", present);
75 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
76 ASSET_IFACE, "Manufacturer",
77 nvmeData.vendor);
78 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
79 ASSET_IFACE, "SerialNumber",
80 nvmeData.serialNumber);
81 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
82 ASSET_IFACE, "Model",
83 nvmeData.modelNumber);
84 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
85 NVME_STATUS_IFACE, "DriveLifeUsed",
86 nvmeData.driveLifeUsed);
87 preSerial[config.busID] = nvmeData.serialNumber;
88 }
Tony Lee89659212019-06-21 17:34:14 +080089
Chanh Nguyene528b0a2021-07-14 16:57:57 +070090 if (preStatusFlags[config.busID].compare(nvmeData.statusFlags) != 0)
91 {
92 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
93 NVME_STATUS_IFACE, "StatusFlags",
94 nvmeData.statusFlags);
95 preStatusFlags[config.busID] = nvmeData.statusFlags;
96 }
Tony Lee89659212019-06-21 17:34:14 +080097
Chanh Nguyene528b0a2021-07-14 16:57:57 +070098 if (preSmartWarning[config.busID].compare(nvmeData.smartWarnings) != 0)
99 {
100 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
101 NVME_STATUS_IFACE, "SmartWarnings",
102 nvmeData.smartWarnings);
103 auto smartWarning = (!nvmeData.smartWarnings.empty())
104 ? std::stoi(nvmeData.smartWarnings, 0, 16)
105 : NOWARNING;
Tony Lee89659212019-06-21 17:34:14 +0800106
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700107 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
108 NVME_STATUS_IFACE, "CapacityFault",
109 !(smartWarning & CapacityFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800110
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700111 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
112 NVME_STATUS_IFACE, "TemperatureFault",
113 !(smartWarning & temperatureFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800114
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700115 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
116 NVME_STATUS_IFACE, "DegradesFault",
117 !(smartWarning & DegradesFaultMask));
118
119 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
120 NVME_STATUS_IFACE, "MediaFault",
121 !(smartWarning & MediaFaultMask));
122
123 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
124 NVME_STATUS_IFACE, "BackupDeviceFault",
125 !(smartWarning & BackupDeviceFaultMask));
126 preSmartWarning[config.busID] = nvmeData.smartWarnings;
127 }
Tony Lee89659212019-06-21 17:34:14 +0800128}
129
130void Nvme::setFaultLED(const std::string& locateLedGroupPath,
131 const std::string& faultLedGroupPath, bool request)
132{
133 if (locateLedGroupPath.empty() || faultLedGroupPath.empty())
134 {
135 return;
136 }
137
138 // Before toggle LED, check whether is Identify or not.
139 if (!getLEDGroupState(locateLedGroupPath))
140 {
Brandon Kimfdffe5c2021-03-30 15:07:59 -0700141 if (getLEDGroupState(faultLedGroupPath) != request)
142 {
143 util::SDBusPlus::setProperty(bus, LED_GROUP_BUSNAME,
144 faultLedGroupPath, LED_GROUP_IFACE,
145 "Asserted", request);
146 }
Tony Lee89659212019-06-21 17:34:14 +0800147 }
148}
149
150void Nvme::setLocateLED(const std::string& locateLedGroupPath,
151 const std::string& locateLedBusName,
152 const std::string& locateLedPath, bool isPresent)
153{
154 if (locateLedGroupPath.empty() || locateLedBusName.empty() ||
155 locateLedPath.empty())
156 {
157 return;
158 }
159
160 namespace server = sdbusplus::xyz::openbmc_project::Led::server;
161
162 if (!getLEDGroupState(locateLedGroupPath))
163 {
164 if (isPresent)
165 util::SDBusPlus::setProperty(
166 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
167 "State",
168 server::convertForMessage(server::Physical::Action::On));
169 else
170 util::SDBusPlus::setProperty(
171 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
172 "State",
173 server::convertForMessage(server::Physical::Action::Off));
174 }
175}
176
177bool Nvme::getLEDGroupState(const std::string& ledPath)
178{
179 auto asserted = util::SDBusPlus::getProperty<bool>(
180 bus, LED_GROUP_BUSNAME, ledPath, LED_GROUP_IFACE, "Asserted");
181
182 return asserted;
183}
184
185void Nvme::setLEDsStatus(const phosphor::nvme::Nvme::NVMeConfig& config,
186 bool success,
187 const phosphor::nvme::Nvme::NVMeData& nvmeData)
188{
Tony Lee89659212019-06-21 17:34:14 +0800189 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
Patrick Williams7da98582023-05-10 07:50:46 -0500309 nvmeData.vendor = intToHex(rsp_data_command_8[1]) + " " +
310 intToHex(rsp_data_command_8[2]);
Tony Lee6c595012019-06-19 10:54:59 +0800311
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{
Tony Lee6c595012019-06-19 10:54:59 +0800409 phosphor::nvme::Nvme::NVMeConfig nvmeConfig;
410 std::vector<phosphor::nvme::Nvme::NVMeConfig> nvmeConfigs;
411 int8_t criticalHigh = 0;
412 int8_t criticalLow = 0;
413 int8_t maxValue = 0;
414 int8_t minValue = 0;
415 int8_t warningHigh = 0;
416 int8_t warningLow = 0;
417
418 try
419 {
420 auto data = parseSensorConfig();
421 static const std::vector<Json> empty{};
422 std::vector<Json> readings = data.value("config", empty);
423 std::vector<Json> thresholds = data.value("threshold", empty);
Patrick Williams7da98582023-05-10 07:50:46 -0500424 monitorIntervalSec = data.value("monitorIntervalSec",
425 MONITOR_INTERVAL_SECONDS);
426 maxSmbusErrorRetry = data.value("maxSmbusErrorRetry",
427 MAX_SMBUS_ERROR_RETRY);
Potin Laibcae0872022-10-20 15:29:17 +0800428
Tony Lee6c595012019-06-19 10:54:59 +0800429 if (!thresholds.empty())
430 {
431 for (const auto& instance : thresholds)
432 {
433 criticalHigh = instance.value("criticalHigh", 0);
434 criticalLow = instance.value("criticalLow", 0);
435 maxValue = instance.value("maxValue", 0);
436 minValue = instance.value("minValue", 0);
437 warningHigh = instance.value("warningHigh", 0);
438 warningLow = instance.value("warningLow", 0);
439 }
440 }
441 else
442 {
443 log<level::ERR>(
444 "Invalid NVMe config file, thresholds dosen't exist");
445 }
446
447 if (!readings.empty())
448 {
449 for (const auto& instance : readings)
450 {
451 uint8_t index = instance.value("NVMeDriveIndex", 0);
452 uint8_t busID = instance.value("NVMeDriveBusID", 0);
Tony Lee89659212019-06-21 17:34:14 +0800453 std::string faultLedGroupPath =
454 instance.value("NVMeDriveFaultLEDGroupPath", "");
455 std::string locateLedGroupPath =
456 instance.value("NVMeDriveLocateLEDGroupPath", "");
George Hung873b5b32020-06-20 14:56:15 +0800457 uint16_t presentPin = instance.value("NVMeDrivePresentPin", 0);
458 uint16_t pwrGoodPin = instance.value("NVMeDrivePwrGoodPin", 0);
Tony Lee89659212019-06-21 17:34:14 +0800459 std::string locateLedControllerBusName =
460 instance.value("NVMeDriveLocateLEDControllerBusName", "");
461 std::string locateLedControllerPath =
462 instance.value("NVMeDriveLocateLEDControllerPath", "");
Tony Lee6c595012019-06-19 10:54:59 +0800463
464 nvmeConfig.index = std::to_string(index);
465 nvmeConfig.busID = busID;
Tony Lee89659212019-06-21 17:34:14 +0800466 nvmeConfig.faultLedGroupPath = faultLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800467 nvmeConfig.presentPin = presentPin;
468 nvmeConfig.pwrGoodPin = pwrGoodPin;
Tony Lee89659212019-06-21 17:34:14 +0800469 nvmeConfig.locateLedControllerBusName =
470 locateLedControllerBusName;
471 nvmeConfig.locateLedControllerPath = locateLedControllerPath;
472 nvmeConfig.locateLedGroupPath = locateLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800473 nvmeConfig.criticalHigh = criticalHigh;
474 nvmeConfig.criticalLow = criticalLow;
475 nvmeConfig.warningHigh = warningHigh;
476 nvmeConfig.warningLow = warningLow;
477 nvmeConfig.maxValue = maxValue;
478 nvmeConfig.minValue = minValue;
479 nvmeConfigs.push_back(nvmeConfig);
480 }
481 }
482 else
483 {
484 log<level::ERR>("Invalid NVMe config file, config dosen't exist");
485 }
486 }
487 catch (const Json::exception& e)
488 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700489 log<level::ERR>("Json Exception caught."), entry("MSG=%s", e.what());
Tony Lee6c595012019-06-19 10:54:59 +0800490 }
491
492 return nvmeConfigs;
493}
494
495std::string Nvme::getGPIOValueOfNvme(const std::string& fullPath)
496{
497 std::string val;
498 std::ifstream ifs;
499 auto retries = 3;
500
501 while (retries != 0)
502 {
503 try
504 {
505 if (!ifs.is_open())
506 ifs.open(fullPath);
507 ifs.clear();
508 ifs.seekg(0);
509 ifs >> val;
510 }
511 catch (const std::exception& e)
512 {
513 --retries;
514 std::this_thread::sleep_for(delay);
515 log<level::ERR>("Can not open gpio path.",
Brandon Kim9b771e22020-10-05 12:02:01 -0700516 entry("MSG=%s", e.what()));
Tony Lee6c595012019-06-19 10:54:59 +0800517 continue;
518 }
519 break;
520 }
521
522 ifs.close();
523 return val;
524}
525
Tony Lee89659212019-06-21 17:34:14 +0800526void Nvme::createNVMeInventory()
527{
Patrick Williams05eedaa2020-05-13 17:58:42 -0500528 using Properties = std::map<std::string, std::variant<std::string, bool>>;
Tony Lee89659212019-06-21 17:34:14 +0800529 using Interfaces = std::map<std::string, Properties>;
530
531 std::string inventoryPath;
532 std::map<sdbusplus::message::object_path, Interfaces> obj;
533
George Hung831f2042021-05-19 17:02:29 +0800534 for (const auto& config : configs)
Tony Lee89659212019-06-21 17:34:14 +0800535 {
536 inventoryPath = "/system/chassis/motherboard/nvme" + config.index;
537
538 obj = {{
539 inventoryPath,
540 {{ITEM_IFACE, {}}, {NVME_STATUS_IFACE, {}}, {ASSET_IFACE, {}}},
541 }};
542 util::SDBusPlus::CallMethod(bus, INVENTORY_BUSNAME, INVENTORY_NAMESPACE,
543 INVENTORY_MANAGER_IFACE, "Notify", obj);
544 }
545}
546
547void Nvme::init()
548{
549 createNVMeInventory();
550}
551
Potin Lai03144732023-02-14 22:20:38 +0800552void Nvme::readNvmeData(NVMeConfig& config, bool isPwrGood)
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700553{
554 std::string inventoryPath = NVME_INVENTORY_PATH + config.index;
555 NVMeData nvmeData;
556
557 // get NVMe information through i2c by busID.
Potin Lai03144732023-02-14 22:20:38 +0800558 bool success;
559
560 // skip reading nvme data when power good is false
561 if (isPwrGood)
562 {
563 success = getNVMeInfobyBusID(config.busID, nvmeData);
564 }
565 else
566 {
567 nvmeData.present = false;
568 nvmeData.sensorValue = static_cast<int8_t>(TEMPERATURE_SENSOR_FAILURE);
569 success = false;
570 // Skip retry below when isPwrGood is false because smbus is going to
571 // fail
572 nvmeSmbusErrCnt[config.busID] = maxSmbusErrorRetry;
573 }
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700574
Potin Lai6e149e92022-11-23 10:04:05 +0800575 if (success)
576 {
577 nvmeSmbusErrCnt[config.busID] = 0;
578 }
579 else
580 {
581 if (nvmeSmbusErrCnt[config.busID] < maxSmbusErrorRetry)
582 {
Potin Lai03144732023-02-14 22:20:38 +0800583 // Return early so that we retry
Potin Lai6e149e92022-11-23 10:04:05 +0800584 nvmeSmbusErrCnt[config.busID]++;
585 log<level::INFO>("getNVMeInfobyBusID failed, retry...",
586 entry("INDEX=%s", config.index.c_str()),
587 entry("ERRCNT=%u", nvmeSmbusErrCnt[config.busID]));
588 return;
589 }
590 }
591
Potin Lai03144732023-02-14 22:20:38 +0800592 // find NvmeSSD object by index
593 auto iter = nvmes.find(config.index);
594
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700595 // can not find. create dbus
596 if (iter == nvmes.end())
597 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700598 log<level::INFO>("SSD plug.", entry("INDEX=%s", config.index.c_str()));
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700599
600 std::string objPath = NVME_OBJ_PATH + config.index;
601 auto nvmeSSD =
602 std::make_shared<phosphor::nvme::NvmeSSD>(bus, objPath.c_str());
603 nvmes.emplace(config.index, nvmeSSD);
604
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700605 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700606 nvmeSSD->setSensorValueToDbus(nvmeData.sensorValue);
George Hung93455332021-01-06 20:57:04 +0800607 if (nvmeData.wcTemp != 0)
608 {
609 config.criticalHigh = nvmeData.wcTemp;
610 config.warningHigh = nvmeData.wcTemp;
611 }
George Hung831f2042021-05-19 17:02:29 +0800612 nvmeSSD->setSensorMaxMin(config.maxValue, config.minValue);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700613 nvmeSSD->setSensorThreshold(config.criticalHigh, config.criticalLow,
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700614 config.warningHigh, config.warningLow);
615
616 nvmeSSD->checkSensorThreshold();
617 setLEDsStatus(config, success, nvmeData);
618 }
619 else
620 {
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700621 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700622 iter->second->setSensorValueToDbus(nvmeData.sensorValue);
George Hung831f2042021-05-19 17:02:29 +0800623 if (nvmeData.wcTemp != 0)
624 {
George Hungbe343992021-07-02 09:15:54 +0800625 config.criticalHigh = nvmeData.wcTemp;
626 config.warningHigh = nvmeData.wcTemp;
627
George Hung831f2042021-05-19 17:02:29 +0800628 iter->second->setSensorThreshold(
629 config.criticalHigh, config.criticalLow, config.warningHigh,
630 config.warningLow);
631 }
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700632 iter->second->checkSensorThreshold();
633 setLEDsStatus(config, success, nvmeData);
634 }
635}
636
Tony Lee6c595012019-06-19 10:54:59 +0800637/** @brief Monitor NVMe drives every one second */
Tony Lee84d430c2019-06-13 15:26:15 +0800638void Nvme::read()
639{
Tony Lee6c595012019-06-19 10:54:59 +0800640 std::string devPresentPath;
641 std::string devPwrGoodPath;
Tony Lee89659212019-06-21 17:34:14 +0800642 std::string inventoryPath;
Tony Lee6c595012019-06-19 10:54:59 +0800643
Tony Lee6c595012019-06-19 10:54:59 +0800644 for (auto config : configs)
645 {
646 NVMeData nvmeData;
Tony Lee6c595012019-06-19 10:54:59 +0800647
Tony Lee89659212019-06-21 17:34:14 +0800648 inventoryPath = NVME_INVENTORY_PATH + config.index;
Patrick Williams7da98582023-05-10 07:50:46 -0500649 devPresentPath = GPIO_BASE_PATH + std::to_string(config.presentPin) +
650 "/value";
651 devPwrGoodPath = GPIO_BASE_PATH + std::to_string(config.pwrGoodPin) +
652 "/value";
Tony Lee89659212019-06-21 17:34:14 +0800653
Potin Laiaef8fa02022-09-21 19:10:30 +0800654 auto presentPinValStr = (config.presentPin)
655 ? getGPIOValueOfNvme(devPresentPath)
656 : IS_PRESENT;
657 auto pwrGoodPinValStr =
658 (config.pwrGoodPin) ? getGPIOValueOfNvme(devPwrGoodPath) : POWERGD;
Potin Lai76f455e2022-09-22 00:11:26 +0800659 const bool isPwrGood = (pwrGoodPinValStr == POWERGD);
Potin Laiaef8fa02022-09-21 19:10:30 +0800660
661 if (presentPinValStr != IS_PRESENT)
Tony Lee6c595012019-06-19 10:54:59 +0800662 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800663 // Drive not present, remove nvme d-bus path ,
664 // clean all properties in inventory
665 // and turn off fault and locate LED
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700666
Potin Laiaef8fa02022-09-21 19:10:30 +0800667 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
668 false);
669 setLocateLED(config.locateLedGroupPath,
670 config.locateLedControllerBusName,
671 config.locateLedControllerPath, false);
672
673 nvmeData = NVMeData();
674 setNvmeInventoryProperties(config, false, nvmeData, inventoryPath);
675 nvmes.erase(config.index);
676 continue;
677 }
678
Potin Lai76f455e2022-09-22 00:11:26 +0800679 if (!isPwrGood)
Potin Laiaef8fa02022-09-21 19:10:30 +0800680 {
681 // IFDET should be used to provide the final say
682 // in SSD's presence - IFDET showing SSD is present
683 // but the power is off (if the drive is plugged in)
684 // is a valid state.
685
686 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
687 true);
688 setLocateLED(config.locateLedGroupPath,
689 config.locateLedControllerBusName,
690 config.locateLedControllerPath, false);
691
692 nvmeData = NVMeData();
693 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
694
695 if (isErrorPower[config.index] != true)
Tony Lee6c595012019-06-19 10:54:59 +0800696 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800697 log<level::ERR>(
698 "Present pin is true but power good pin is false.",
699 entry("INDEX=%s", config.index.c_str()));
700 log<level::ERR>("Erase SSD from map and d-bus.",
701 entry("INDEX=%s", config.index.c_str()));
Tony Lee89659212019-06-21 17:34:14 +0800702
Potin Laiaef8fa02022-09-21 19:10:30 +0800703 isErrorPower[config.index] = true;
Tony Lee6c595012019-06-19 10:54:59 +0800704 }
705 }
Potin Laiaef8fa02022-09-21 19:10:30 +0800706 else
707 {
708 isErrorPower[config.index] = false;
709 }
Tony Lee89659212019-06-21 17:34:14 +0800710
Potin Laiaef8fa02022-09-21 19:10:30 +0800711 // Keep reading to report the invalid temperature
712 // (To make thermal loop know that the sensor reading
713 // is invalid).
Potin Lai03144732023-02-14 22:20:38 +0800714 readNvmeData(config, isPwrGood);
Potin Lai6e149e92022-11-23 10:04:05 +0800715 if (nvmes.find(config.index) != nvmes.end())
716 {
717 nvmes.find(config.index)->second->setSensorAvailability(isPwrGood);
718 }
Tony Lee6c595012019-06-19 10:54:59 +0800719 }
Tony Lee84d430c2019-06-13 15:26:15 +0800720}
721} // namespace nvme
722} // namespace phosphor