Commit 1ad8d784 authored by chrfranke's avatar chrfranke
Browse files

smartctl: Add option '-l scterc[,READTIME,WRITETIME]' to get/set

the SCT Error Recovery Control time limit (ticket #50).

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@3065 4ea69e1a-61f1-4043-bf83-b5c94c648137
parent 75d58e71
......@@ -43,6 +43,14 @@ NOTES FOR FUTURE RELEASES: see TODO file.
<DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
[CF] smartctl: Add option '-l scterc[,READTIME,WRITETIME]' to get/set
the SCT Error Recovery Control time limit (ticket #50).
Patch was provided by Richard Gregory:
http://www.csc.liv.ac.uk/~greg/projects/erc/
Modified for new ata_pass_through() interface.
Linux HPT fixes ommitted for now.
[CF] Fix SCT temperature table commands on big endian CPUs.
[MS] drivedb.h updates:
......
......@@ -13,6 +13,8 @@ Summary: smartmontools release 5.40
- Drive database is in a separate source file 'drivedb.h'
which can be downloaded from SVN.
- smartd libcap-ng support, option '-C, --capabilities'.
- smartctl option '-l scterc[,...]' to get/set the
SCT Error Recovery Control time limit.
- Fix SCT temperature table commands on big endian CPUs.
Date 2010-01-28
......
......@@ -2310,6 +2310,96 @@ int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persisten
return 0;
}
// Get/Set SCT Error Recovery Control
static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned type,
bool set, unsigned short & time_limit)
{
// Check initial status
ata_sct_status_response sts;
if (ataReadSCTStatus(device, &sts))
return -1;
// Do nothing if other SCT command is executing
if (sts.ext_status_code == 0xffff) {
pout("Another SCT command is executing, abort Error Recovery Control\n"
"(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n",
sts.ext_status_code, sts.action_code, sts.function_code);
return -1;
}
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
cmd.selection_code = type; // 1=Read timer, 2=Write timer
if (set)
cmd.time_limit = time_limit;
// swap endian order if needed
if (isbigendian()) {
swapx(&cmd.action_code);
swapx(&cmd.function_code);
swapx(&cmd.selection_code);
swapx(&cmd.time_limit);
}
// write command via SMART log page 0xe0
// TODO: Debug output
ata_cmd_in in;
in.in_regs.command = ATA_SMART_CMD;
in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW;
in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR;
in.in_regs.lba_low = 0xe0;
in.set_data_out(&cmd, 1);
if (!set)
// Time limit returned in ATA registers
in.out_needed.sector_count = in.out_needed.lba_low = true;
ata_cmd_out out;
if (!device->ata_pass_through(in, out)) {
pout("Error Write SCT Error Recovery Control Command failed: %s\n", device->get_errmsg());
return -1;
}
// re-read and check SCT status
if (ataReadSCTStatus(device, &sts))
return -1;
if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == (set ? 1 : 2))) {
pout("Error unexcepted SCT status 0x%04x (action_code=%u, function_code=%u)\n",
sts.ext_status_code, sts.action_code, sts.function_code);
return -1;
}
if (!set) {
// Check whether registers are properly returned by ioctl()
if (!(out.out_regs.sector_count.is_set() && out.out_regs.lba_low.is_set())) {
// TODO: Output register support should be checked within each ata_pass_through()
// implementation before command is issued.
pout("Error SMART WRITE LOG does not return COUNT and LBA_LOW register\n");
return -1;
}
// Return value to caller
time_limit = out.out_regs.sector_count | (out.out_regs.lba_low << 8);
}
return 0;
}
// Get SCT Error Recovery Control
int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit)
{
return ataGetSetSCTErrorRecoveryControltime(device, type, false/*get*/, time_limit);
}
// Set SCT Error Recovery Control
int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit)
{
return ataGetSetSCTErrorRecoveryControltime(device, type, true/*set*/, time_limit);
}
// Print one self-test log entry.
// Returns true if self-test showed an error.
bool ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type,
......
......@@ -543,6 +543,20 @@ struct ata_sct_status_response
#pragma pack()
ASSERT_SIZEOF_STRUCT(ata_sct_status_response, 512);
// SCT Error Recovery Control command (send with SMART_WRITE_LOG page 0xe0)
// Table 88 of T13/1699-D Revision 6a
#pragma pack(1)
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 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
} ATTR_PACKED;
#pragma pack()
ASSERT_SIZEOF_STRUCT(ata_sct_error_recovery_control_command, 512);
// SCT Feature Control command (send with SMART_WRITE_LOG page 0xe0)
// Table 72 of T13/1699-D Revision 3f
#pragma pack(1)
......@@ -552,8 +566,8 @@ struct ata_sct_feature_control_command
unsigned short function_code; // 1 = Set, 2 = Return, 3 = Return options
unsigned short feature_code; // 3 = Temperature logging interval
unsigned short state; // Interval
unsigned short option_flags; // Bit 0: persistent, Bits 1-31: reserved
unsigned short words005_255[251]; // reserved
unsigned short option_flags; // Bit 0: persistent, Bits 1-15: reserved
unsigned short words005_255[251]; // reserved
} ATTR_PACKED;
#pragma pack()
ASSERT_SIZEOF_STRUCT(ata_sct_feature_control_command, 512);
......@@ -727,6 +741,10 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table *
// Set SCT temperature logging interval
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);
/* Enable/Disable SMART on device */
int ataEnableSmart (ata_device * device);
......@@ -789,6 +807,9 @@ int isSupportSelectiveSelfTest(const ata_smart_values * data);
inline bool isSCTCapable(const ata_identify_device *drive)
{ return !!(drive->words088_255[206-88] & 0x01); } // 0x01 = SCT support
inline bool isSCTErrorRecoveryControlCapable(const ata_identify_device *drive)
{ return ((drive->words088_255[206-88] & 0x09) == 0x09); } // 0x08 = SCT Error Recovery Control support
inline bool isSCTFeatureControlCapable(const ata_identify_device *drive)
{ return ((drive->words088_255[206-88] & 0x11) == 0x11); } // 0x10 = SCT Feature Control support
......
......@@ -883,6 +883,8 @@ static void ataPrintSCTCapability(const ata_identify_device *drive)
if (!(sctcaps & 0x01))
return;
pout("SCT capabilities: \t (0x%04x)\tSCT Status supported.\n", sctcaps);
if (sctcaps & 0x08)
pout("\t\t\t\t\tSCT Error Recovery Control supported.\n");
if (sctcaps & 0x10)
pout("\t\t\t\t\tSCT Feature Control supported.\n");
if (sctcaps & 0x20)
......@@ -1698,6 +1700,20 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh)
return 0;
}
// Print SCT Error Recovery Control timers
static void ataPrintSCTErrorRecoveryControl(unsigned short read_timer, unsigned short write_timer)
{
pout("SCT Error Recovery Control:\n");
if (!read_timer)
pout(" Read: Disabled\n");
else
pout(" Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0);
if (!write_timer)
pout(" Write: Disabled\n");
else
pout(" Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0);
}
// Compares failure type to policy in effect, and either exits or
// simply returns to the calling routine.
......@@ -2285,14 +2301,21 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
}
}
// SCT commands
bool sct_ok = false;
if ( options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int
|| options.sct_erc_get || options.sct_erc_set ) {
if (!isSCTCapable(&drive)) {
pout("Warning: device does not support SCT Commands\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}
else
sct_ok = true;
}
// Print SCT status and temperature history table
if (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int) {
if (sct_ok && (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int)) {
for (;;) {
if (!isSCTCapable(&drive)) {
pout("Warning: device does not support SCT Commands\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
break;
}
if (options.sct_temp_sts || options.sct_temp_hist) {
ata_sct_status_response sts;
ata_sct_temperature_history_table tmh;
......@@ -2340,6 +2363,44 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
}
}
// SCT Error Recovery Control
if (sct_ok && (options.sct_erc_get || options.sct_erc_set)) {
if (!isSCTErrorRecoveryControlCapable(&drive)) {
pout("Warning: device does not support SCT Error Recovery Control command\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}
else {
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)) {
pout("Warning: device does not support SCT (Set) Error Recovery Control command\n");
pout("Suggest common arguments: scterc,70,70 to enable ERC or sct,0,0 to disable\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
sct_erc_get = false;
}
else
sct_erc_get = true;
}
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)) {
pout("Warning: device does not support SCT (Get) Error Recovery Control command\n");
if (options.sct_erc_set)
pout("The previous SCT (Set) Error Recovery Control command succeeded\n");
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}
else
ataPrintSCTErrorRecoveryControl(read_timer, write_timer);
}
pout("\n");
}
}
// Print SATA Phy Event Counters
if (options.sataphy) {
unsigned nsectors = GetNumLogSectors(gplogdir, 0x11, true);
......
......@@ -63,6 +63,9 @@ struct ata_print_options
std::vector<ata_log_request> log_requests;
bool sct_temp_sts, sct_temp_hist;
bool sct_erc_get;
bool sct_erc_set;
unsigned sct_erc_readtime, sct_erc_writetime;
bool sataphy, sataphy_reset;
bool smart_disable, smart_enable;
......@@ -97,6 +100,9 @@ struct ata_print_options
smart_ext_selftest_log(0),
retry_error_log(false), retry_selftest_log(false),
sct_temp_sts(false), sct_temp_hist(false),
sct_erc_get(false),
sct_erc_set(false),
sct_erc_readtime(0), sct_erc_writetime(0),
sataphy(false), sataphy_reset(false),
smart_disable(false), smart_enable(false),
smart_auto_offl_disable(false), smart_auto_offl_enable(false),
......
......@@ -189,7 +189,7 @@ Prints all SMART and non-SMART information about the device. For ATA
devices this is equivalent to
.nf
\'\-H \-i \-c \-A \-l xerror,error \-l xselftest,selftest \-l selective
\-l directory \-l scttemp \-l sataphy\'.
\-l directory \-l scttemp \-l scterc \-l sataphy\'.
.fi
and for SCSI, this is equivalent to
.nf
......@@ -974,6 +974,16 @@ configured with the \'\-t scttempint,N[,p]\' option, see below.
The SCT commands are specified in the proposed ATA\-8 Command Set
(ACS), and are already implemented in some recent ATA\-7 disks.
.I scterc[,READTIME,WRITETIME]
\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values
and descriptions of the SCT Error Recovery Control settings. These
are equivalent to TLER (as used by Western Digital), CCTL (as used
by Samsung and Hitachi) and ERC (as used by Seagate. 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.
.I sataphy[,reset]
\- [SATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values
and descriptions of the SATA Phy Event Counters (General Purpose Log
......
......@@ -126,7 +126,7 @@ void Usage (void){
" -l TYPE, --log=TYPE\n"
" Show device log. TYPE: error, selftest, selective, directory[,g|s],\n"
" background, sasphy[,reset], sataphy[,reset],\n"
" scttemp[sts,hist],\n"
" scttemp[sts,hist], scterc[,N,M],\n"
" gplog,N[,RANGE], smartlog,N[,RANGE],\n"
" xerror[,N][,error], xselftest[,N][,selftest]\n\n"
" -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n"
......@@ -178,7 +178,7 @@ static std::string getvalidarglist(char opt)
case 'S':
return "on, off";
case 'l':
return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], "
return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], scterc[,N,M], "
"sasphy[,reset], sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], "
"xerror[,N][,error], xselftest[,N][,selftest]";
case 'P':
......@@ -430,6 +430,8 @@ const char * parse_options(int argc, char** argv,
ataopts.sataphy = ataopts.sataphy_reset = true;
} else if (!strcmp(optarg,"background")) {
scsiopts.smart_background_log = true;
} else if (!strcmp(optarg,"scterc")) {
ataopts.sct_erc_get = true;
} else if (!strcmp(optarg,"scttemp")) {
ataopts.sct_temp_sts = ataopts.sct_temp_hist = true;
} else if (!strcmp(optarg,"scttempsts")) {
......@@ -467,6 +469,18 @@ const char * parse_options(int argc, char** argv,
else
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_set = true;
ataopts.sct_erc_readtime = rt;
ataopts.sct_erc_writetime = wt;
}
else {
sprintf(extraerror, "Option -l scterc,[READTIME,WRITETIME] syntax error\n");
badarg = true;
}
} else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1)
|| !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) {
unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0;
......@@ -526,6 +540,7 @@ const char * parse_options(int argc, char** argv,
ataopts.smart_selective_selftest_log = true;
ataopts.smart_logdir = ataopts.gp_logdir = true;
ataopts.sct_temp_sts = ataopts.sct_temp_hist = true;
ataopts.sct_erc_get = true;
ataopts.sataphy = true;
scsiopts.smart_background_log = true;
scsiopts.sasphy = true;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment