+ if (state.WriteECounterPageSupported && (0 == scsiLogSense(scsidev,
+ WRITE_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
+ scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[1].errCounter);
+ state.scsi_error_counters[1].found=1;
+ }
+ if (state.VerifyECounterPageSupported && (0 == scsiLogSense(scsidev,
+ VERIFY_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
+ scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[2].errCounter);
+ state.scsi_error_counters[2].found=1;
+ }
+ if (state.NonMediumErrorPageSupported && (0 == scsiLogSense(scsidev,
+ NON_MEDIUM_ERROR_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
+ scsiDecodeNonMediumErrPage(tBuf, &state.scsi_nonmedium_error.nme);
+ state.scsi_nonmedium_error.found=1;
+ }
+ // store temperature if not done by CheckTemperature() above
+ if (!(cfg.tempdiff || cfg.tempinfo || cfg.tempcrit))
+ state.temperature = currenttemp;
+ }
+ CloseDevice(scsidev, name);
+ return 0;
+}
+
+static int NVMeCheckDevice(const dev_config & cfg, dev_state & state, nvme_device * nvmedev)
+{
+ if (!open_device(cfg, state, nvmedev, "NVMe"))
+ return 1;
+
+ const char * name = cfg.name.c_str();
+
+ // Read SMART/Health log
+ nvme_smart_log smart_log;
+ if (!nvme_read_smart_log(nvmedev, smart_log)) {
+ PrintOut(LOG_INFO, "Device: %s, failed to read NVMe SMART/Health Information\n", name);
+ MailWarning(cfg, state, 6, "Device: %s, failed to read NVMe SMART/Health Information", name);
+ state.must_write = true;
+ return 0;
+ }
+
+ // Check Critical Warning bits
+ if (cfg.smartcheck && smart_log.critical_warning) {
+ unsigned char w = smart_log.critical_warning;
+ std::string msg;
+ static const char * const wnames[] =
+ {"LowSpare", "Temperature", "Reliability", "R/O", "VolMemBackup"};
+
+ for (unsigned b = 0, cnt = 0; b < 8 ; b++) {
+ if (!(w & (1 << b)))
+ continue;
+ if (cnt)
+ msg += ", ";
+ if (++cnt > 3) {
+ msg += "..."; break;
+ }
+ if (b >= sizeof(wnames)/sizeof(wnames[0])) {
+ msg += "*Unknown*"; break;
+ }
+ msg += wnames[b];
+ }
+
+ PrintOut(LOG_CRIT, "Device: %s, Critical Warning (0x%02x): %s\n", name, w, msg.c_str());
+ MailWarning(cfg, state, 1, "Device: %s, Critical Warning (0x%02x): %s", name, w, msg.c_str());
+ state.must_write = true;
+ }
+
+ // Check temperature limits
+ if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) {
+ int k = nvme_get_max_temp_kelvin(smart_log);
+ // Convert Kelvin to positive Celsius (TODO: Allow negative temperatures)
+ int c = k - 273;
+ if (c < 1)
+ c = 1;
+ else if (c > 0xff)
+ c = 0xff;
+ CheckTemperature(cfg, state, c, 0);
+ }
+
+ // Check if number of errors has increased
+ if (cfg.errorlog || cfg.xerrorlog) {
+ uint64_t oldcnt = state.nvme_err_log_entries;
+ uint64_t newcnt = le128_to_uint64(smart_log.num_err_log_entries);
+ if (newcnt > oldcnt) {
+ PrintOut(LOG_CRIT, "Device: %s, number of Error Log entries increased from %" PRIu64 " to %" PRIu64 "\n",
+ name, oldcnt, newcnt);
+ MailWarning(cfg, state, 4, "Device: %s, number of Error Log entries increased from %" PRIu64 " to %" PRIu64,
+ name, oldcnt, newcnt);
+ state.must_write = true;
+ }
+ state.nvme_err_log_entries = newcnt;
+ }
+
+ CloseDevice(nvmedev, name);
+ return 0;
+}
+
+// 0=not used, 1=not disabled, 2=disable rejected by OS, 3=disabled
+static int standby_disable_state = 0;
+
+static void init_disable_standby_check(dev_config_vector & configs)
+{
+ // Check for '-l offlinests,ns' or '-l selfteststs,ns' directives
+ bool sts1 = false, sts2 = false;
+ for (unsigned i = 0; i < configs.size() && !(sts1 || sts2); i++) {
+ const dev_config & cfg = configs.at(i);
+ if (cfg.offlinests_ns)
+ sts1 = true;
+ if (cfg.selfteststs_ns)
+ sts2 = true;
+ }
+
+ // Check for support of disable auto standby
+ // Reenable standby if smartd.conf was reread
+ if (sts1 || sts2 || standby_disable_state == 3) {
+ if (!smi()->disable_system_auto_standby(false)) {
+ if (standby_disable_state == 3)
+ PrintOut(LOG_CRIT, "System auto standby enable failed: %s\n", smi()->get_errmsg());
+ if (sts1 || sts2) {
+ PrintOut(LOG_INFO, "Disable auto standby not supported, ignoring ',ns' from %s%s%s\n",
+ (sts1 ? "-l offlinests,ns" : ""), (sts1 && sts2 ? " and " : ""), (sts2 ? "-l selfteststs,ns" : ""));
+ sts1 = sts2 = false;
+ }
+ }
+ }
+
+ standby_disable_state = (sts1 || sts2 ? 1 : 0);
+}
+
+static void do_disable_standby_check(const dev_config_vector & configs, const dev_state_vector & states)
+{
+ if (!standby_disable_state)
+ return;
+
+ // Check for just started or still running self-tests
+ bool running = false;
+ for (unsigned i = 0; i < configs.size() && !running; i++) {
+ const dev_config & cfg = configs.at(i); const dev_state & state = states.at(i);
+
+ if ( ( cfg.offlinests_ns
+ && (state.offline_started ||
+ is_offl_coll_in_progress(state.smartval.offline_data_collection_status)))
+ || ( cfg.selfteststs_ns
+ && (state.selftest_started ||
+ is_self_test_in_progress(state.smartval.self_test_exec_status))) )
+ running = true;
+ // state.offline/selftest_started will be reset after next logging of test status
+ }
+
+ // Disable/enable auto standby and log state changes
+ if (!running) {
+ if (standby_disable_state != 1) {
+ if (!smi()->disable_system_auto_standby(false))
+ PrintOut(LOG_CRIT, "Self-test(s) completed, system auto standby enable failed: %s\n",
+ smi()->get_errmsg());
+ else
+ PrintOut(LOG_INFO, "Self-test(s) completed, system auto standby enabled\n");
+ standby_disable_state = 1;
+ }
+ }
+ else if (!smi()->disable_system_auto_standby(true)) {
+ if (standby_disable_state != 2) {
+ PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disable rejected: %s\n",
+ smi()->get_errmsg());
+ standby_disable_state = 2;
+ }
+ }
+ else {
+ if (standby_disable_state != 3) {
+ PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disabled\n");
+ standby_disable_state = 3;
+ }
+ }