blob: 7270e9fb974dd2b3a97049425c2fdb6d27296e72 [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;
Joseph.Fu097562e2024-04-16 14:40:49 +080040static constexpr int DriveNotReadyMask = 1 << 6;
Tony Lee89659212019-06-21 17:34:14 +080041static constexpr int NOWARNING = 255;
42
Tony Lee6c595012019-06-19 10:54:59 +080043static constexpr int SERIALNUMBER_START_INDEX = 3;
44static constexpr int SERIALNUMBER_END_INDEX = 23;
George Hung92a15ba2020-09-14 17:14:52 +080045static constexpr int MODELNUMBER_START_INDEX = 46;
46static constexpr int MODELNUMBER_END_INDEX = 85;
Tony Lee6c595012019-06-19 10:54:59 +080047
48static constexpr const int TEMPERATURE_SENSOR_FAILURE = 0x81;
49
Brandon Kim8353c5f2022-07-08 17:09:23 -070050static std::map<std::string, std::string> map_vendor = {
51 {"80 86", "Intel"}, {"1e f", "Kioxia"}, {"14 4d", "Samsung"}};
George Hung69b96182020-08-20 17:19:56 +080052
Tony Lee6c595012019-06-19 10:54:59 +080053namespace fs = std::filesystem;
54
Tony Lee84d430c2019-06-13 15:26:15 +080055namespace phosphor
56{
57namespace nvme
58{
59
60using namespace std;
61using namespace phosphor::logging;
62
Tony Lee89659212019-06-21 17:34:14 +080063void Nvme::setNvmeInventoryProperties(
Chanh Nguyene528b0a2021-07-14 16:57:57 +070064 NVMeConfig& config, bool present,
65 const phosphor::nvme::Nvme::NVMeData& nvmeData,
Tony Lee89659212019-06-21 17:34:14 +080066 const std::string& inventoryPath)
67{
Chanh Nguyene528b0a2021-07-14 16:57:57 +070068 static std::unordered_map<int, std::string> preSerial;
69 static std::unordered_map<int, std::string> preSmartWarning;
70 static std::unordered_map<int, std::string> preStatusFlags;
Tony Lee89659212019-06-21 17:34:14 +080071
Chanh Nguyene528b0a2021-07-14 16:57:57 +070072 if (preSerial[config.busID].compare(nvmeData.serialNumber) != 0)
73 {
74 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
75 ITEM_IFACE, "Present", present);
76 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
77 ASSET_IFACE, "Manufacturer",
78 nvmeData.vendor);
79 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
80 ASSET_IFACE, "SerialNumber",
81 nvmeData.serialNumber);
82 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
83 ASSET_IFACE, "Model",
84 nvmeData.modelNumber);
85 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
86 NVME_STATUS_IFACE, "DriveLifeUsed",
87 nvmeData.driveLifeUsed);
88 preSerial[config.busID] = nvmeData.serialNumber;
89 }
Tony Lee89659212019-06-21 17:34:14 +080090
Chanh Nguyene528b0a2021-07-14 16:57:57 +070091 if (preStatusFlags[config.busID].compare(nvmeData.statusFlags) != 0)
92 {
93 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
94 NVME_STATUS_IFACE, "StatusFlags",
95 nvmeData.statusFlags);
96 preStatusFlags[config.busID] = nvmeData.statusFlags;
97 }
Tony Lee89659212019-06-21 17:34:14 +080098
Chanh Nguyene528b0a2021-07-14 16:57:57 +070099 if (preSmartWarning[config.busID].compare(nvmeData.smartWarnings) != 0)
100 {
101 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
102 NVME_STATUS_IFACE, "SmartWarnings",
103 nvmeData.smartWarnings);
104 auto smartWarning = (!nvmeData.smartWarnings.empty())
105 ? std::stoi(nvmeData.smartWarnings, 0, 16)
106 : NOWARNING;
Tony Lee89659212019-06-21 17:34:14 +0800107
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700108 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
109 NVME_STATUS_IFACE, "CapacityFault",
110 !(smartWarning & CapacityFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800111
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700112 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
113 NVME_STATUS_IFACE, "TemperatureFault",
114 !(smartWarning & temperatureFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800115
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700116 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
117 NVME_STATUS_IFACE, "DegradesFault",
118 !(smartWarning & DegradesFaultMask));
119
120 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
121 NVME_STATUS_IFACE, "MediaFault",
122 !(smartWarning & MediaFaultMask));
123
124 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
125 NVME_STATUS_IFACE, "BackupDeviceFault",
126 !(smartWarning & BackupDeviceFaultMask));
127 preSmartWarning[config.busID] = nvmeData.smartWarnings;
128 }
Tony Lee89659212019-06-21 17:34:14 +0800129}
130
131void Nvme::setFaultLED(const std::string& locateLedGroupPath,
132 const std::string& faultLedGroupPath, bool request)
133{
134 if (locateLedGroupPath.empty() || faultLedGroupPath.empty())
135 {
136 return;
137 }
138
139 // Before toggle LED, check whether is Identify or not.
140 if (!getLEDGroupState(locateLedGroupPath))
141 {
Brandon Kimfdffe5c2021-03-30 15:07:59 -0700142 if (getLEDGroupState(faultLedGroupPath) != request)
143 {
144 util::SDBusPlus::setProperty(bus, LED_GROUP_BUSNAME,
145 faultLedGroupPath, LED_GROUP_IFACE,
146 "Asserted", request);
147 }
Tony Lee89659212019-06-21 17:34:14 +0800148 }
149}
150
151void Nvme::setLocateLED(const std::string& locateLedGroupPath,
152 const std::string& locateLedBusName,
153 const std::string& locateLedPath, bool isPresent)
154{
155 if (locateLedGroupPath.empty() || locateLedBusName.empty() ||
156 locateLedPath.empty())
157 {
158 return;
159 }
160
161 namespace server = sdbusplus::xyz::openbmc_project::Led::server;
162
163 if (!getLEDGroupState(locateLedGroupPath))
164 {
165 if (isPresent)
166 util::SDBusPlus::setProperty(
167 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
168 "State",
169 server::convertForMessage(server::Physical::Action::On));
170 else
171 util::SDBusPlus::setProperty(
172 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
173 "State",
174 server::convertForMessage(server::Physical::Action::Off));
175 }
176}
177
178bool Nvme::getLEDGroupState(const std::string& ledPath)
179{
180 auto asserted = util::SDBusPlus::getProperty<bool>(
181 bus, LED_GROUP_BUSNAME, ledPath, LED_GROUP_IFACE, "Asserted");
182
183 return asserted;
184}
185
186void Nvme::setLEDsStatus(const phosphor::nvme::Nvme::NVMeConfig& config,
187 bool success,
188 const phosphor::nvme::Nvme::NVMeData& nvmeData)
189{
Tony Lee89659212019-06-21 17:34:14 +0800190 if (success)
191 {
192 if (!nvmeData.smartWarnings.empty())
193 {
194 auto request =
195 (strcmp(nvmeData.smartWarnings.c_str(), NOWARNING_STRING) == 0)
196 ? false
197 : true;
198
199 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
200 request);
201 setLocateLED(config.locateLedGroupPath,
202 config.locateLedControllerBusName,
203 config.locateLedControllerPath, !request);
204 }
205 isError[config.index] = false;
206 }
207 else
208 {
209 if (isError[config.index] != true)
210 {
211 // Drive is present but can not get data, turn on fault LED.
212 log<level::ERR>("Drive status is good but can not get data.",
Brandon Kim9b771e22020-10-05 12:02:01 -0700213 entry("OBJ_PATH=%s", config.index.c_str()));
Tony Lee89659212019-06-21 17:34:14 +0800214 isError[config.index] = true;
215 }
216
217 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath, true);
218 setLocateLED(config.locateLedGroupPath,
219 config.locateLedControllerBusName,
220 config.locateLedControllerPath, false);
221 }
222}
223
Tony Lee6c595012019-06-19 10:54:59 +0800224std::string intToHex(int input)
225{
226 std::stringstream tmp;
227 tmp << std::hex << input;
228
229 return tmp.str();
230}
231
232/** @brief Get NVMe info over smbus */
George Hung5e23bcd2021-07-01 12:16:11 +0800233bool Nvme::getNVMeInfobyBusID(int busID,
234 phosphor::nvme::Nvme::NVMeData& nvmeData)
Tony Lee6c595012019-06-19 10:54:59 +0800235{
236 nvmeData.present = true;
237 nvmeData.vendor = "";
238 nvmeData.serialNumber = "";
George Hung831f2042021-05-19 17:02:29 +0800239 nvmeData.modelNumber = "";
Tony Lee6c595012019-06-19 10:54:59 +0800240 nvmeData.smartWarnings = "";
241 nvmeData.statusFlags = "";
242 nvmeData.driveLifeUsed = "";
Brandon Kimd5838d12021-05-19 12:51:55 -0700243 nvmeData.sensorValue = static_cast<int8_t>(TEMPERATURE_SENSOR_FAILURE);
George Hung831f2042021-05-19 17:02:29 +0800244 nvmeData.wcTemp = 0;
Tony Lee6c595012019-06-19 10:54:59 +0800245
246 phosphor::smbus::Smbus smbus;
247
248 unsigned char rsp_data_command_0[I2C_DATA_MAX] = {0};
249 unsigned char rsp_data_command_8[I2C_DATA_MAX] = {0};
250
251 uint8_t tx_data = COMMAND_CODE_0;
252
253 auto init = smbus.smbusInit(busID);
254
Tony Lee6c595012019-06-19 10:54:59 +0800255 if (init == -1)
256 {
257 if (isErrorSmbus[busID] != true)
258 {
259 log<level::ERR>("smbusInit fail!");
260 isErrorSmbus[busID] = true;
261 }
262
263 nvmeData.present = false;
264
265 return nvmeData.present;
266 }
267
Patrick Williamsf89f1532024-08-16 15:20:48 -0400268 auto res_int = smbus.SendSmbusRWCmdRAW(
269 busID, NVME_SSD_SLAVE_ADDRESS, &tx_data, sizeof(tx_data),
270 rsp_data_command_0, CODE_0_LENGTH);
Tony Lee6c595012019-06-19 10:54:59 +0800271
272 if (res_int < 0)
273 {
274 if (isErrorSmbus[busID] != true)
275 {
276 log<level::ERR>("Send command code 0 fail!");
277 isErrorSmbus[busID] = true;
278 }
279
280 smbus.smbusClose(busID);
281 nvmeData.present = false;
282 return nvmeData.present;
283 }
284
Joseph.Fu097562e2024-04-16 14:40:49 +0800285 if (rsp_data_command_0[1] & DriveNotReadyMask)
286 {
287 if (isErrorSmbus[busID] != true)
288 {
289 log<level::ERR>("Drive not ready!");
290 isErrorSmbus[busID] = true;
291 }
292 smbus.smbusClose(busID);
293 return false;
294 }
295
George Hung92a15ba2020-09-14 17:14:52 +0800296 nvmeData.statusFlags = intToHex(rsp_data_command_0[1]);
297 nvmeData.smartWarnings = intToHex(rsp_data_command_0[2]);
298 nvmeData.driveLifeUsed = intToHex(rsp_data_command_0[4]);
George Hung93455332021-01-06 20:57:04 +0800299 nvmeData.sensorValue = static_cast<int8_t>(rsp_data_command_0[3]);
300 nvmeData.wcTemp = static_cast<int8_t>(rsp_data_command_0[5]);
George Hung92a15ba2020-09-14 17:14:52 +0800301
Tony Lee6c595012019-06-19 10:54:59 +0800302 tx_data = COMMAND_CODE_8;
303
George Hung92a15ba2020-09-14 17:14:52 +0800304 res_int = smbus.SendSmbusRWCmdRAW(busID, NVME_SSD_SLAVE_ADDRESS, &tx_data,
305 sizeof(tx_data), rsp_data_command_8,
306 CODE_8_LENGTH);
Tony Lee6c595012019-06-19 10:54:59 +0800307
308 if (res_int < 0)
309 {
310 if (isErrorSmbus[busID] != true)
311 {
312 log<level::ERR>("Send command code 8 fail!");
313 isErrorSmbus[busID] = true;
314 }
315
316 smbus.smbusClose(busID);
317 nvmeData.present = false;
318 return nvmeData.present;
319 }
320
Patrick Williamsf89f1532024-08-16 15:20:48 -0400321 nvmeData.vendor =
322 intToHex(rsp_data_command_8[1]) + " " + intToHex(rsp_data_command_8[2]);
Tony Lee6c595012019-06-19 10:54:59 +0800323
George Hung69b96182020-08-20 17:19:56 +0800324 for (auto iter = map_vendor.begin(); iter != map_vendor.end(); iter++)
325 {
326 if (iter->first == nvmeData.vendor)
327 {
328 nvmeData.vendor = iter->second;
329 break;
330 }
331 }
332
Tony Lee6c595012019-06-19 10:54:59 +0800333 for (int offset = SERIALNUMBER_START_INDEX; offset < SERIALNUMBER_END_INDEX;
334 offset++)
335 {
George Hung31c3a2f2021-07-29 19:15:14 +0800336 // Only accept digits/letters/punctuation characters.
337 if (rsp_data_command_8[offset] >= '!' &&
338 rsp_data_command_8[offset] <= '~')
George Hung69b96182020-08-20 17:19:56 +0800339 nvmeData.serialNumber +=
340 static_cast<char>(rsp_data_command_8[offset]);
Tony Lee6c595012019-06-19 10:54:59 +0800341 }
342
Brandon Kim89a24e12022-11-18 21:05:30 +0000343 if ((nvmeData.vendor == "Samsung") || (nvmeData.vendor == "Kioxia"))
George Hung92a15ba2020-09-14 17:14:52 +0800344 {
345 unsigned char rsp_data_vpd[I2C_DATA_MAX] = {0};
346 const int rx_len = (MODELNUMBER_END_INDEX - MODELNUMBER_START_INDEX);
347 tx_data = MODELNUMBER_START_INDEX;
348
349 auto res_int =
350 smbus.SendSmbusRWCmdRAW(busID, NVME_SSD_VPD_SLAVE_ADDRESS, &tx_data,
351 sizeof(tx_data), rsp_data_vpd, rx_len);
352
353 if (res_int < 0)
354 {
355 if (isErrorSmbus[busID] != true)
356 {
357 log<level::ERR>("Send command read VPD fail!");
358 isErrorSmbus[busID] = true;
359 }
360
361 smbus.smbusClose(busID);
362 nvmeData.present = false;
363 return nvmeData.present;
364 }
365
366 for (int i = 0; i < rx_len; i++)
367 {
George Hung31c3a2f2021-07-29 19:15:14 +0800368 // Only accept digits/letters/punctuation characters.
369 if ((rsp_data_vpd[i] >= '!' && rsp_data_vpd[i] <= '~'))
George Hung92a15ba2020-09-14 17:14:52 +0800370 nvmeData.modelNumber += static_cast<char>(rsp_data_vpd[i]);
371 }
372
373 if (nvmeData.modelNumber.substr(0, nvmeData.vendor.size()) == "SAMSUNG")
374 nvmeData.modelNumber.erase(0, nvmeData.vendor.size());
375 }
Tony Lee6c595012019-06-19 10:54:59 +0800376
377 smbus.smbusClose(busID);
378
379 isErrorSmbus[busID] = false;
380
381 return nvmeData.present;
382}
383
Tony Lee84d430c2019-06-13 15:26:15 +0800384void Nvme::run()
385{
Tony Lee89659212019-06-21 17:34:14 +0800386 init();
387
Tony Lee84d430c2019-06-13 15:26:15 +0800388 std::function<void()> callback(std::bind(&Nvme::read, this));
389 try
390 {
Potin Laibcae0872022-10-20 15:29:17 +0800391 u_int64_t interval = monitorIntervalSec * 1000000;
Tony Lee84d430c2019-06-13 15:26:15 +0800392 _timer.restart(std::chrono::microseconds(interval));
393 }
394 catch (const std::exception& e)
395 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700396 log<level::ERR>("Error in polling loop. "), entry("ERROR=%s", e.what());
Tony Lee84d430c2019-06-13 15:26:15 +0800397 }
398}
399
Tony Lee6c595012019-06-19 10:54:59 +0800400/** @brief Parsing NVMe config JSON file */
401Json parseSensorConfig()
Tony Lee84d430c2019-06-13 15:26:15 +0800402{
Tony Lee6c595012019-06-19 10:54:59 +0800403 std::ifstream jsonFile(configFile);
404 if (!jsonFile.is_open())
405 {
406 log<level::ERR>("NVMe config JSON file not found");
407 }
408
409 auto data = Json::parse(jsonFile, nullptr, false);
410 if (data.is_discarded())
411 {
412 log<level::ERR>("NVMe config readings JSON parser failure");
413 }
414
415 return data;
Tony Lee84d430c2019-06-13 15:26:15 +0800416}
417
Tony Lee6c595012019-06-19 10:54:59 +0800418/** @brief Obtain the initial configuration value of NVMe */
419std::vector<phosphor::nvme::Nvme::NVMeConfig> Nvme::getNvmeConfig()
420{
Tony Lee6c595012019-06-19 10:54:59 +0800421 phosphor::nvme::Nvme::NVMeConfig nvmeConfig;
422 std::vector<phosphor::nvme::Nvme::NVMeConfig> nvmeConfigs;
423 int8_t criticalHigh = 0;
424 int8_t criticalLow = 0;
425 int8_t maxValue = 0;
426 int8_t minValue = 0;
427 int8_t warningHigh = 0;
428 int8_t warningLow = 0;
429
430 try
431 {
432 auto data = parseSensorConfig();
433 static const std::vector<Json> empty{};
434 std::vector<Json> readings = data.value("config", empty);
435 std::vector<Json> thresholds = data.value("threshold", empty);
Patrick Williamsf89f1532024-08-16 15:20:48 -0400436 monitorIntervalSec =
437 data.value("monitorIntervalSec", MONITOR_INTERVAL_SECONDS);
438 maxSmbusErrorRetry =
439 data.value("maxSmbusErrorRetry", MAX_SMBUS_ERROR_RETRY);
Potin Laibcae0872022-10-20 15:29:17 +0800440
Tony Lee6c595012019-06-19 10:54:59 +0800441 if (!thresholds.empty())
442 {
443 for (const auto& instance : thresholds)
444 {
445 criticalHigh = instance.value("criticalHigh", 0);
446 criticalLow = instance.value("criticalLow", 0);
447 maxValue = instance.value("maxValue", 0);
448 minValue = instance.value("minValue", 0);
449 warningHigh = instance.value("warningHigh", 0);
450 warningLow = instance.value("warningLow", 0);
451 }
452 }
453 else
454 {
455 log<level::ERR>(
Manojkiran Eda3b2e5a92024-06-17 14:47:39 +0530456 "Invalid NVMe config file, thresholds doesn't exist");
Tony Lee6c595012019-06-19 10:54:59 +0800457 }
458
459 if (!readings.empty())
460 {
461 for (const auto& instance : readings)
462 {
463 uint8_t index = instance.value("NVMeDriveIndex", 0);
464 uint8_t busID = instance.value("NVMeDriveBusID", 0);
Tony Lee89659212019-06-21 17:34:14 +0800465 std::string faultLedGroupPath =
466 instance.value("NVMeDriveFaultLEDGroupPath", "");
467 std::string locateLedGroupPath =
468 instance.value("NVMeDriveLocateLEDGroupPath", "");
George Hung873b5b32020-06-20 14:56:15 +0800469 uint16_t presentPin = instance.value("NVMeDrivePresentPin", 0);
470 uint16_t pwrGoodPin = instance.value("NVMeDrivePwrGoodPin", 0);
Tony Lee89659212019-06-21 17:34:14 +0800471 std::string locateLedControllerBusName =
472 instance.value("NVMeDriveLocateLEDControllerBusName", "");
473 std::string locateLedControllerPath =
474 instance.value("NVMeDriveLocateLEDControllerPath", "");
Tony Lee6c595012019-06-19 10:54:59 +0800475
476 nvmeConfig.index = std::to_string(index);
477 nvmeConfig.busID = busID;
Tony Lee89659212019-06-21 17:34:14 +0800478 nvmeConfig.faultLedGroupPath = faultLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800479 nvmeConfig.presentPin = presentPin;
480 nvmeConfig.pwrGoodPin = pwrGoodPin;
Tony Lee89659212019-06-21 17:34:14 +0800481 nvmeConfig.locateLedControllerBusName =
482 locateLedControllerBusName;
483 nvmeConfig.locateLedControllerPath = locateLedControllerPath;
484 nvmeConfig.locateLedGroupPath = locateLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800485 nvmeConfig.criticalHigh = criticalHigh;
486 nvmeConfig.criticalLow = criticalLow;
487 nvmeConfig.warningHigh = warningHigh;
488 nvmeConfig.warningLow = warningLow;
489 nvmeConfig.maxValue = maxValue;
490 nvmeConfig.minValue = minValue;
491 nvmeConfigs.push_back(nvmeConfig);
492 }
493 }
494 else
495 {
Manojkiran Eda3b2e5a92024-06-17 14:47:39 +0530496 log<level::ERR>("Invalid NVMe config file, config doesn't exist");
Tony Lee6c595012019-06-19 10:54:59 +0800497 }
498 }
499 catch (const Json::exception& e)
500 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700501 log<level::ERR>("Json Exception caught."), entry("MSG=%s", e.what());
Tony Lee6c595012019-06-19 10:54:59 +0800502 }
503
504 return nvmeConfigs;
505}
506
507std::string Nvme::getGPIOValueOfNvme(const std::string& fullPath)
508{
509 std::string val;
510 std::ifstream ifs;
511 auto retries = 3;
512
513 while (retries != 0)
514 {
515 try
516 {
517 if (!ifs.is_open())
518 ifs.open(fullPath);
519 ifs.clear();
520 ifs.seekg(0);
521 ifs >> val;
522 }
523 catch (const std::exception& e)
524 {
525 --retries;
526 std::this_thread::sleep_for(delay);
527 log<level::ERR>("Can not open gpio path.",
Brandon Kim9b771e22020-10-05 12:02:01 -0700528 entry("MSG=%s", e.what()));
Tony Lee6c595012019-06-19 10:54:59 +0800529 continue;
530 }
531 break;
532 }
533
534 ifs.close();
535 return val;
536}
537
Tony Lee89659212019-06-21 17:34:14 +0800538void Nvme::createNVMeInventory()
539{
Patrick Williams05eedaa2020-05-13 17:58:42 -0500540 using Properties = std::map<std::string, std::variant<std::string, bool>>;
Tony Lee89659212019-06-21 17:34:14 +0800541 using Interfaces = std::map<std::string, Properties>;
542
543 std::string inventoryPath;
544 std::map<sdbusplus::message::object_path, Interfaces> obj;
545
George Hung831f2042021-05-19 17:02:29 +0800546 for (const auto& config : configs)
Tony Lee89659212019-06-21 17:34:14 +0800547 {
548 inventoryPath = "/system/chassis/motherboard/nvme" + config.index;
549
550 obj = {{
551 inventoryPath,
552 {{ITEM_IFACE, {}}, {NVME_STATUS_IFACE, {}}, {ASSET_IFACE, {}}},
553 }};
554 util::SDBusPlus::CallMethod(bus, INVENTORY_BUSNAME, INVENTORY_NAMESPACE,
555 INVENTORY_MANAGER_IFACE, "Notify", obj);
556 }
557}
558
559void Nvme::init()
560{
561 createNVMeInventory();
562}
563
Potin Lai03144732023-02-14 22:20:38 +0800564void Nvme::readNvmeData(NVMeConfig& config, bool isPwrGood)
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700565{
566 std::string inventoryPath = NVME_INVENTORY_PATH + config.index;
567 NVMeData nvmeData;
568
569 // get NVMe information through i2c by busID.
Potin Lai03144732023-02-14 22:20:38 +0800570 bool success;
571
572 // skip reading nvme data when power good is false
573 if (isPwrGood)
574 {
575 success = getNVMeInfobyBusID(config.busID, nvmeData);
576 }
577 else
578 {
579 nvmeData.present = false;
580 nvmeData.sensorValue = static_cast<int8_t>(TEMPERATURE_SENSOR_FAILURE);
581 success = false;
582 // Skip retry below when isPwrGood is false because smbus is going to
583 // fail
584 nvmeSmbusErrCnt[config.busID] = maxSmbusErrorRetry;
585 }
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700586
Potin Lai6e149e92022-11-23 10:04:05 +0800587 if (success)
588 {
589 nvmeSmbusErrCnt[config.busID] = 0;
590 }
591 else
592 {
593 if (nvmeSmbusErrCnt[config.busID] < maxSmbusErrorRetry)
594 {
Potin Lai03144732023-02-14 22:20:38 +0800595 // Return early so that we retry
Potin Lai6e149e92022-11-23 10:04:05 +0800596 nvmeSmbusErrCnt[config.busID]++;
597 log<level::INFO>("getNVMeInfobyBusID failed, retry...",
598 entry("INDEX=%s", config.index.c_str()),
599 entry("ERRCNT=%u", nvmeSmbusErrCnt[config.busID]));
600 return;
601 }
602 }
603
Potin Lai03144732023-02-14 22:20:38 +0800604 // find NvmeSSD object by index
605 auto iter = nvmes.find(config.index);
606
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700607 // can not find. create dbus
608 if (iter == nvmes.end())
609 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700610 log<level::INFO>("SSD plug.", entry("INDEX=%s", config.index.c_str()));
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700611
612 std::string objPath = NVME_OBJ_PATH + config.index;
613 auto nvmeSSD =
614 std::make_shared<phosphor::nvme::NvmeSSD>(bus, objPath.c_str());
615 nvmes.emplace(config.index, nvmeSSD);
616
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700617 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700618 nvmeSSD->setSensorValueToDbus(nvmeData.sensorValue);
George Hung93455332021-01-06 20:57:04 +0800619 if (nvmeData.wcTemp != 0)
620 {
621 config.criticalHigh = nvmeData.wcTemp;
622 config.warningHigh = nvmeData.wcTemp;
623 }
George Hung831f2042021-05-19 17:02:29 +0800624 nvmeSSD->setSensorMaxMin(config.maxValue, config.minValue);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700625 nvmeSSD->setSensorThreshold(config.criticalHigh, config.criticalLow,
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700626 config.warningHigh, config.warningLow);
627
Joseph.Fua7782722024-04-19 12:18:07 +0800628 if (success)
629 nvmeSSD->checkSensorThreshold();
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700630 setLEDsStatus(config, success, nvmeData);
631 }
632 else
633 {
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700634 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700635 iter->second->setSensorValueToDbus(nvmeData.sensorValue);
George Hung831f2042021-05-19 17:02:29 +0800636 if (nvmeData.wcTemp != 0)
637 {
George Hungbe343992021-07-02 09:15:54 +0800638 config.criticalHigh = nvmeData.wcTemp;
639 config.warningHigh = nvmeData.wcTemp;
640
George Hung831f2042021-05-19 17:02:29 +0800641 iter->second->setSensorThreshold(
642 config.criticalHigh, config.criticalLow, config.warningHigh,
643 config.warningLow);
644 }
Joseph.Fua7782722024-04-19 12:18:07 +0800645 if (success)
646 iter->second->checkSensorThreshold();
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700647 setLEDsStatus(config, success, nvmeData);
648 }
649}
650
Tony Lee6c595012019-06-19 10:54:59 +0800651/** @brief Monitor NVMe drives every one second */
Tony Lee84d430c2019-06-13 15:26:15 +0800652void Nvme::read()
653{
Tony Lee6c595012019-06-19 10:54:59 +0800654 std::string devPresentPath;
655 std::string devPwrGoodPath;
Tony Lee89659212019-06-21 17:34:14 +0800656 std::string inventoryPath;
Tony Lee6c595012019-06-19 10:54:59 +0800657
Tony Lee6c595012019-06-19 10:54:59 +0800658 for (auto config : configs)
659 {
660 NVMeData nvmeData;
Tony Lee6c595012019-06-19 10:54:59 +0800661
Tony Lee89659212019-06-21 17:34:14 +0800662 inventoryPath = NVME_INVENTORY_PATH + config.index;
Patrick Williamsf89f1532024-08-16 15:20:48 -0400663 devPresentPath =
664 GPIO_BASE_PATH + std::to_string(config.presentPin) + "/value";
665 devPwrGoodPath =
666 GPIO_BASE_PATH + std::to_string(config.pwrGoodPin) + "/value";
Tony Lee89659212019-06-21 17:34:14 +0800667
Potin Laiaef8fa02022-09-21 19:10:30 +0800668 auto presentPinValStr = (config.presentPin)
669 ? getGPIOValueOfNvme(devPresentPath)
670 : IS_PRESENT;
671 auto pwrGoodPinValStr =
672 (config.pwrGoodPin) ? getGPIOValueOfNvme(devPwrGoodPath) : POWERGD;
Potin Lai76f455e2022-09-22 00:11:26 +0800673 const bool isPwrGood = (pwrGoodPinValStr == POWERGD);
Potin Laiaef8fa02022-09-21 19:10:30 +0800674
675 if (presentPinValStr != IS_PRESENT)
Tony Lee6c595012019-06-19 10:54:59 +0800676 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800677 // Drive not present, remove nvme d-bus path ,
678 // clean all properties in inventory
679 // and turn off fault and locate LED
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700680
Potin Laiaef8fa02022-09-21 19:10:30 +0800681 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
682 false);
683 setLocateLED(config.locateLedGroupPath,
684 config.locateLedControllerBusName,
685 config.locateLedControllerPath, false);
686
687 nvmeData = NVMeData();
688 setNvmeInventoryProperties(config, false, nvmeData, inventoryPath);
689 nvmes.erase(config.index);
690 continue;
691 }
692
Potin Lai76f455e2022-09-22 00:11:26 +0800693 if (!isPwrGood)
Potin Laiaef8fa02022-09-21 19:10:30 +0800694 {
695 // IFDET should be used to provide the final say
696 // in SSD's presence - IFDET showing SSD is present
697 // but the power is off (if the drive is plugged in)
698 // is a valid state.
699
700 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
701 true);
702 setLocateLED(config.locateLedGroupPath,
703 config.locateLedControllerBusName,
704 config.locateLedControllerPath, false);
705
706 nvmeData = NVMeData();
707 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
708
709 if (isErrorPower[config.index] != true)
Tony Lee6c595012019-06-19 10:54:59 +0800710 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800711 log<level::ERR>(
712 "Present pin is true but power good pin is false.",
713 entry("INDEX=%s", config.index.c_str()));
714 log<level::ERR>("Erase SSD from map and d-bus.",
715 entry("INDEX=%s", config.index.c_str()));
Tony Lee89659212019-06-21 17:34:14 +0800716
Potin Laiaef8fa02022-09-21 19:10:30 +0800717 isErrorPower[config.index] = true;
Tony Lee6c595012019-06-19 10:54:59 +0800718 }
719 }
Potin Laiaef8fa02022-09-21 19:10:30 +0800720 else
721 {
722 isErrorPower[config.index] = false;
723 }
Tony Lee89659212019-06-21 17:34:14 +0800724
Potin Laiaef8fa02022-09-21 19:10:30 +0800725 // Keep reading to report the invalid temperature
726 // (To make thermal loop know that the sensor reading
727 // is invalid).
Potin Lai03144732023-02-14 22:20:38 +0800728 readNvmeData(config, isPwrGood);
Potin Lai6e149e92022-11-23 10:04:05 +0800729 if (nvmes.find(config.index) != nvmes.end())
730 {
731 nvmes.find(config.index)->second->setSensorAvailability(isPwrGood);
732 }
Tony Lee6c595012019-06-19 10:54:59 +0800733 }
Tony Lee84d430c2019-06-13 15:26:15 +0800734}
735} // namespace nvme
736} // namespace phosphor