diff --git a/smartmontools/AUTHORS b/smartmontools/AUTHORS index d789bb0255b5d975c1ca8a9cb1a89f1f54d7780e..016e3c99e4edb683a98227fc28efbcf40e6067d7 100644 --- a/smartmontools/AUTHORS +++ b/smartmontools/AUTHORS @@ -4,6 +4,7 @@ Developers / Maintainers / Contributors: Raghava Aditya <...> Bruce Allen <...> +Jeremy Bauer <jeremy.bauer@wdc.com> Casey Biemiller <cbiemiller@intelliprop.com> Erik Inge Bolsø <...> Stanislav Brabec <sbrabec@suse.cz> diff --git a/smartmontools/ChangeLog b/smartmontools/ChangeLog index 951a6650c58cac44ac8467b0e214d4eb7499a91f..bcf4d5ad0ea7f1aaced8ed7586ea952e51b9125e 100644 --- a/smartmontools/ChangeLog +++ b/smartmontools/ChangeLog @@ -1,5 +1,12 @@ $Id$ +2021-01-15 Jeremy Bauer <jeremy.bauer@wdc.com> + + Add support for SCT Error Recovery Timer features added in ACS-4 + (#1427). + '-l scterc[,R,W],p' option gets/sets the persistent power-on values. + '-l scterc,r' option restores to the manufacturer's default values. + 2021-01-15 Zhenwei Pi <pizhenwei@bytedance.com> nvmeprint.cpp: Add bit 5 of SMART/Health 'Critical Warning' byte diff --git a/smartmontools/atacmds.cpp b/smartmontools/atacmds.cpp index b0b1d2b1399e4877c27db74fda3affdeebb1f418..fdb33cbf776dd0b24929d9b264eb53f9702917e3 100644 --- a/smartmontools/atacmds.cpp +++ b/smartmontools/atacmds.cpp @@ -2414,7 +2414,8 @@ int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persisten // Get/Set SCT Error Recovery Control static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, - bool set, unsigned short & time_limit) + bool set, unsigned short & time_limit, + bool power_on, bool mfg_default) { // Check initial status ata_sct_status_response sts; @@ -2432,7 +2433,17 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned ty ata_sct_error_recovery_control_command cmd; memset(&cmd, 0, sizeof(cmd)); // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) cmd.action_code = 3; // Error Recovery Control command - cmd.function_code = (set ? 1 : 2); // 1=Set timer, 2=Get timer + + // 1=Set timer, 2=Get timer, 3=Set Power-on timer, 4=Get Power-on timer, 5=Restore mfg default + if (mfg_default) { + cmd.function_code = 5; + } else if (power_on) { + cmd.function_code = (set ? 3 : 4); + } else { + cmd.function_code = (set ? 1 : 2); + } + unsigned short saved_function_code = cmd.function_code; + cmd.selection_code = type; // 1=Read timer, 2=Write timer if (set) cmd.time_limit = time_limit; @@ -2469,7 +2480,7 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned ty if (ataReadSCTStatus(device, &sts)) return -1; - if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == (set ? 1 : 2))) { + if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == saved_function_code)) { pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; @@ -2498,15 +2509,16 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned ty } // Get SCT Error Recovery Control -int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit) +int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit, bool power_on) { - return ataGetSetSCTErrorRecoveryControltime(device, type, false/*get*/, time_limit); + return ataGetSetSCTErrorRecoveryControltime(device, type, false/*get*/, time_limit, power_on, false); } // Set SCT Error Recovery Control -int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit) +int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit, + bool power_on, bool mfg_default) { - return ataGetSetSCTErrorRecoveryControltime(device, type, true/*set*/, time_limit); + return ataGetSetSCTErrorRecoveryControltime(device, type, true/*set*/, time_limit, power_on, mfg_default); } diff --git a/smartmontools/atacmds.h b/smartmontools/atacmds.h index 94c9ae9ba22c3dcde9810f313b18bfee717c6898..9e81377632a6c3dd0ae79f177a397c67afe2e1f2 100644 --- a/smartmontools/atacmds.h +++ b/smartmontools/atacmds.h @@ -545,7 +545,7 @@ STATIC_ASSERT(sizeof(ata_sct_status_response) == 512); struct ata_sct_error_recovery_control_command { unsigned short action_code; // 3 = Error Recovery Control - unsigned short function_code; // 1 = Set, 2 = Return + unsigned short function_code; // 1 = Set Current, 2 = Return Current, 3 = Set Power-on, 4 = Return Power-on, 5 = Restore Default unsigned short selection_code; // 1 = Read Timer, 2 = Write Timer unsigned short time_limit; // If set: Recovery time limit in 100ms units unsigned short words004_255[252]; // reserved @@ -792,8 +792,8 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent); // Get/Set SCT Error Recovery Control -int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit); -int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit); +int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit, bool power_on); +int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit, bool power_on, bool mfg_default); /* Enable/Disable SMART on device */ diff --git a/smartmontools/ataprint.cpp b/smartmontools/ataprint.cpp index 335ef646ce5fcf21273ef6dd67b27dbfc35dcb8f..bcc435d16dd3d4a4e9e00649715b773274c30c25 100644 --- a/smartmontools/ataprint.cpp +++ b/smartmontools/ataprint.cpp @@ -3128,25 +3128,28 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh) } // Print SCT Error Recovery Control timers -static void ataPrintSCTErrorRecoveryControl(bool set, unsigned short read_timer, unsigned short write_timer) +static void ataPrintSCTErrorRecoveryControl(bool set, unsigned short read_timer, unsigned short write_timer, bool power_on, bool mfg_default = false) { + const char* power_on_str = (power_on ? "Power-on " : ""); json::ref jref = jglb["ata_sct_erc"]; - jout("SCT Error Recovery Control%s:\n", (set ? " set to" : "")); + jout("SCT Error Recovery Control%s:%s\n", (set ? " set to" : ""), (mfg_default ? " default values." : "")); - jref["read"]["enabled"] = !!read_timer; - if (!read_timer) - jout(" Read: Disabled\n"); - else { - jout(" Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0); - jref["read"]["deciseconds"] = read_timer; - } + if (!mfg_default) { + jref["read"]["enabled"] = !!read_timer; + if (!read_timer) + jout(" %sRead: Disabled\n", power_on_str); + else { + jout(" %sRead: %6d (%0.1f seconds)\n", power_on_str, read_timer, read_timer/10.0); + jref["read"]["deciseconds"] = read_timer; + } - jref["write"]["enabled"] = !!write_timer; - if (!write_timer) - jout(" Write: Disabled\n"); - else { - jout(" Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0); - jref["write"]["deciseconds"] = write_timer; + jref["write"]["enabled"] = !!write_timer; + if (!write_timer) + jout(" %sWrite: Disabled\n", power_on_str); + else { + jout(" %sWrite: %6d (%0.1f seconds)\n", power_on_str, write_timer, write_timer/10.0); + jref["write"]["deciseconds"] = write_timer; + } } } @@ -4348,8 +4351,8 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) bool sct_erc_get = options.sct_erc_get; if (options.sct_erc_set) { // Set SCT Error Recovery Control - if ( ataSetSCTErrorRecoveryControltime(device, 1, options.sct_erc_readtime ) - || ataSetSCTErrorRecoveryControltime(device, 2, options.sct_erc_writetime)) { + if ( ataSetSCTErrorRecoveryControltime(device, 1, options.sct_erc_readtime, options.sct_erc_power_on, options.sct_erc_mfg_default ) + || ataSetSCTErrorRecoveryControltime(device, 2, options.sct_erc_writetime, options.sct_erc_power_on, options.sct_erc_mfg_default)) { pout("SCT (Set) Error Recovery Control command failed\n"); if (!( (options.sct_erc_readtime == 70 && options.sct_erc_writetime == 70) || (options.sct_erc_readtime == 0 && options.sct_erc_writetime == 0))) @@ -4359,24 +4362,24 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) } else if (!sct_erc_get) ataPrintSCTErrorRecoveryControl(true, options.sct_erc_readtime, - options.sct_erc_writetime); + options.sct_erc_writetime, options.sct_erc_power_on, options.sct_erc_mfg_default); } if (sct_erc_get) { // Print SCT Error Recovery Control unsigned short read_timer, write_timer; - if ( ataGetSCTErrorRecoveryControltime(device, 1, read_timer ) - || ataGetSCTErrorRecoveryControltime(device, 2, write_timer)) { + if ( ataGetSCTErrorRecoveryControltime(device, 1, read_timer, options.sct_erc_power_on ) + || ataGetSCTErrorRecoveryControltime(device, 2, write_timer, options.sct_erc_power_on)) { pout("SCT (Get) Error Recovery Control command failed\n"); if (options.sct_erc_set) { pout("The previous SCT (Set) Error Recovery Control command succeeded\n"); ataPrintSCTErrorRecoveryControl(true, options.sct_erc_readtime, - options.sct_erc_writetime); + options.sct_erc_writetime, options.sct_erc_power_on); } failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } else - ataPrintSCTErrorRecoveryControl(false, read_timer, write_timer); + ataPrintSCTErrorRecoveryControl(false, read_timer, write_timer, options.sct_erc_power_on); } pout("\n"); } diff --git a/smartmontools/ataprint.h b/smartmontools/ataprint.h index 638db1ef6702a8ec53aee51fcf7ed13ad92ba583..2e007e406339fc365d2c0721d46c5ff065365749 100644 --- a/smartmontools/ataprint.h +++ b/smartmontools/ataprint.h @@ -58,6 +58,8 @@ struct ata_print_options bool sct_erc_get; bool sct_erc_set; unsigned sct_erc_readtime, sct_erc_writetime; + bool sct_erc_power_on; + bool sct_erc_mfg_default; bool sataphy, sataphy_reset; bool smart_disable, smart_enable; @@ -125,6 +127,8 @@ struct ata_print_options sct_erc_get(false), sct_erc_set(false), sct_erc_readtime(0), sct_erc_writetime(0), + sct_erc_power_on(false), + sct_erc_mfg_default(false), sataphy(false), sataphy_reset(false), smart_disable(false), smart_enable(false), smart_auto_offl_disable(false), smart_auto_offl_enable(false), diff --git a/smartmontools/smartctl.8.in b/smartmontools/smartctl.8.in index dad017a9d404d9ce7abb443db7f19e098add52bf..a511bc49115f4ed2e2dc4924d45742c127b0f9a1 100644 --- a/smartmontools/smartctl.8.in +++ b/smartmontools/smartctl.8.in @@ -1491,7 +1491,7 @@ Otherwise, the setting is volatile and will be reverted to the last non-volatile setting by the next hard reset. The default interval is vendor specific, typical values are 1, 2, or 5 minutes. .Sp -.I scterc[,READTIME,WRITETIME] +.I scterc[,READTIME,WRITETIME][,p|r] \- [ATA only] prints values and descriptions of the SCT Error Recovery Control settings. These are equivalent to TLER (as used by Western Digital), CCTL (as used @@ -1500,6 +1500,14 @@ READTIME and WRITETIME arguments (deciseconds) set the specified values. Values of 0 disable the feature, other values less than 65 are probably not supported. For RAID configurations, this is typically set to 70,70 deciseconds. +If \*(Aq,p\*(Aq is specified with read and write time values, these +time values will be persistent over a power-on reset. If \*(Aq,p\*(Aq is +specified without read and write time values, the persistent over power-on +values will be returned. +If \*(Aq,r\*(Aq is specified, all SCT timer settings are restored to the +manufacturer's default value. +The \*(Aq,p\*(Aq and \*(Aq,r\*(Aq options require the device to support ACS-4 +or higher. .Sp .I devstat[,PAGE] \- [ATA only] prints values and descriptions of the ATA Device Statistics diff --git a/smartmontools/smartctl.cpp b/smartmontools/smartctl.cpp index d4db32b5927d044439242e755c881641bcde9849..b0bf682b3dea5df64351e42428c0e9bdad900ad3 100644 --- a/smartmontools/smartctl.cpp +++ b/smartmontools/smartctl.cpp @@ -176,7 +176,7 @@ static void Usage() " Show device log. TYPE: error, selftest, selective, directory[,g|s],\n" " xerror[,N][,error], xselftest[,N][,selftest], background,\n" " sasphy[,reset], sataphy[,reset], scttemp[sts,hist],\n" -" scttempint,N[,p], scterc[,N,M], devstat[,N], defects[,N], ssd,\n" +" scttempint,N[,p], scterc[,N,M][,p|r], devstat[,N], defects[,N], ssd,\n" " gplog,N[,RANGE], smartlog,N[,RANGE], nvmelog,N,SIZE\n\n" " -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" " Set display OPTION for vendor Attribute N (see man page)\n\n" @@ -243,7 +243,7 @@ static std::string getvalidarglist(int opt) "xerror[,N][,error], xselftest[,N][,selftest], " "background, sasphy[,reset], sataphy[,reset], " "scttemp[sts,hist], scttempint,N[,p], " - "scterc[,N,M], devstat[,N], defects[,N], ssd, " + "scterc[,N,M][,p|r], devstat[,N], defects[,N], ssd, " "gplog,N[,RANGE], smartlog,N[,RANGE], " "nvmelog,N,SIZE"; case 'P': @@ -615,16 +615,37 @@ static int parse_options(int argc, char** argv, const char * & type, badarg = true; } else if (!strncmp(optarg, "scterc,", sizeof("scterc,")-1)) { - unsigned rt = ~0, wt = ~0; int n = -1; - sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n); - if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) { + ataopts.sct_erc_power_on = false; + ataopts.sct_erc_mfg_default = false; + unsigned rt = ~0, wt = ~0; char opt = 0; int n = -1; + sscanf(optarg,"scterc,%u,%u,%c%n", &rt, &wt, &opt, &n); + if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999 && toupper(opt) == 'P') { ataopts.sct_erc_set = true; ataopts.sct_erc_readtime = rt; ataopts.sct_erc_writetime = wt; + ataopts.sct_erc_power_on = true; } else { - snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); - badarg = true; + sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n); + if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) { + ataopts.sct_erc_set = true; + ataopts.sct_erc_readtime = rt; + ataopts.sct_erc_writetime = wt; + } else { + sscanf(optarg,"scterc,%c%n", &opt, &n); + if (n == (int)strlen(optarg) && (toupper(opt) == 'P' || toupper(opt) == 'R')) { + if (toupper(opt) == 'R') { + ataopts.sct_erc_set = true; + ataopts.sct_erc_mfg_default = true; + } else { /* P */ + ataopts.sct_erc_get = true; + ataopts.sct_erc_power_on = true; + } + } else { + snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME][,P|R] syntax error\n"); + badarg = true; + } + } } } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) { diff --git a/smartmontools/smartd.cpp b/smartmontools/smartd.cpp index c352dde98b429fb1b2bf7a58a30b11ceb2f52fa8..45b2fe96a67ff7c343e319be6d3f0f9930e39d6e 100644 --- a/smartmontools/smartd.cpp +++ b/smartmontools/smartd.cpp @@ -2313,8 +2313,8 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade else if (locked) PrintOut(LOG_INFO, "Device: %s, no SCT support if ATA Security is LOCKED, ignoring -l scterc\n", name); - else if ( ataSetSCTErrorRecoveryControltime(atadev, 1, cfg.sct_erc_readtime ) - || ataSetSCTErrorRecoveryControltime(atadev, 2, cfg.sct_erc_writetime)) + else if ( ataSetSCTErrorRecoveryControltime(atadev, 1, cfg.sct_erc_readtime, false, false ) + || ataSetSCTErrorRecoveryControltime(atadev, 2, cfg.sct_erc_writetime, false, false)) PrintOut(LOG_INFO, "Device: %s, set of SCT Error Recovery Control failed\n", name); else PrintOut(LOG_INFO, "Device: %s, SCT Error Recovery Control set to: Read: %u, Write: %u\n",