Commit b2dffa1e authored by dpgilbert's avatar dpgilbert
Browse files

[SCSI]: rework scsiGetIEString(), this should address ticket #1614

git-svn-id: https://svn.code.sf.net/p/smartmontools/code/trunk@5394 4ea69e1a-61f1-4043-bf83-b5c94c648137
parent 8cb0f6e4
$Id$
2022-05-30 Douglas Gilbert <dgilbert@interlog.com>
[SCSI]: rework scsiGetIEString() so it should now output
all asc=0xb||0x5d strings defined in spc6r06.pdf . These
are the strings associated with "Informational Exceptions".
This should address ticket #1614 .
2022-05-28 Douglas Gilbert <dgilbert@interlog.com>
[SCSI]: more work for calling REPORT SUPPORTED OPERATION
......
......@@ -1737,6 +1737,11 @@ scsiGetTemp(scsi_device * device, uint8_t *currenttemp, uint8_t *triptemp)
return 0;
}
/* Informational Exception conditions specified by spc6r06.pdf seem to be
* associated with ASC values 0xb (warnings) and 0x5d (impending failures).
* The asc/accq value 0x5d,0xff is reported in response to setting the TEST
* bit in the Informationl Exception Control mode page. */
/* Read informational exception log page or Request Sense response.
* Fetching asc/ascq code potentially flagging an exception or warning.
* Returns 0 if ok, else error number. A current temperature of 255
......@@ -2200,162 +2205,6 @@ scsiTapeAlertsChangerDevice(unsigned short code)
"Unknown Alert";
}
/* this is a subset of the SCSI additional sense code strings indexed
* by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d)
*/
static const char * strs_for_asc_5d[] = {
/* 0x00 */ "FAILURE PREDICTION THRESHOLD EXCEEDED",
"MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED",
"LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED",
"SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
/* 0x10 */ "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
"HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
"HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
"HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
"HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
"HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
"HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
"HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
"HARDWARE IMPENDING FAILURE CONTROLLER DETECTED",
"HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
"HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
"HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
"HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
"",
"",
"",
/* 0x20 */ "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
"CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
"CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
"CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
"CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
"CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH",
"CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH",
"CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS",
"CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED",
"CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE",
"CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE",
"CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT",
"CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
"",
"",
"",
/* 0x30 */ "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
"DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
"DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
"DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
"DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
"DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH",
"DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH",
"DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS",
"DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED",
"DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE",
"DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE",
"DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT",
"DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
"",
"",
"",
/* 0x40 */ "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
"SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
"SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
"SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
"SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
"SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH",
"SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH",
"SERVO IMPENDING FAILURE CHANNEL PARAMETRICS",
"SERVO IMPENDING FAILURE CONTROLLER DETECTED",
"SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE",
"SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE",
"SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT",
"SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
"",
"",
"",
/* 0x50 */ "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
"SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
"SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
"SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
"SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
"SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
"SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
"SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS",
"SPINDLE IMPENDING FAILURE CONTROLLER DETECTED",
"SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
"SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE",
"SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT",
"SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
"",
"",
"",
/* 0x60 */ "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
"FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
"FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
"FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
"FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
"FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
"FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
"FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
"FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED",
"FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
"FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
"FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
/* 0x6c */ "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"};
/* this is a subset of the SCSI additional sense code strings indexed
* * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb)
* */
static const char * strs_for_asc_b[] = {
/* 0x00 */ "WARNING",
"WARNING - SPECIFIED TEMPERATURE EXCEEDED",
"WARNING - ENCLOSURE DEGRADED"};
static char spare_buff[128];
const char *
scsiGetIEString(uint8_t asc, uint8_t ascq)
{
const char * rp;
if (SCSI_ASC_IMPENDING_FAILURE == asc) {
if (ascq == 0xff)
return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
else if (ascq <
(sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
rp = strs_for_asc_5d[ascq];
if (strlen(rp) > 0)
return rp;
}
snprintf(spare_buff, sizeof(spare_buff),
"FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq);
return spare_buff;
} else if (SCSI_ASC_WARNING == asc) {
if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) {
rp = strs_for_asc_b[ascq];
if (strlen(rp) > 0)
return rp;
}
snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq);
return spare_buff;
}
return NULL; /* not a IE additional sense code */
}
int
scsiSmartDefaultSelfTest(scsi_device * device)
{
......@@ -3002,3 +2851,136 @@ scsi_format_id_string(char * out, const uint8_t * in, int n)
strncpy(out, tmp+first, last-first+1);
out[last-first+1] = '\0';
}
static const char * wn = "Warning";
static const char * wn1_9[] = {
"specified temperature exceeded",
"enclosure degraded",
"background self-test failed",
"background pre-scan detected medium error",
"background medium scan detected medium error",
"non-volatile cache now volatile",
"degraded power to non-volatile cache",
"power loss expected",
"device statistics notification active",
};
static const char * five_d_t[] = {
"Hardware",
"Controller",
"Data channel",
"Servo",
"Spindle",
"Firmware",
};
static const char * impfail = "impending failure";
static const char * impending0_c[] = {
"general hard drive failure",
"drive error rate too high",
"data error rate too high",
"seek error rate too high",
"too many block reassigns",
"access times too high",
"start unit times too high",
"channel parametrics",
"controller detected",
"throughput performance",
"seek time performance",
"spin-up retry count",
"drive calibration retry count",
};
static const char * pred = "prediction threshold exceeded";
/* The SCSI Informational Exceptions log page and various other mechanisms
* yield an additional sense code (and its qualifier) [asc and ascq] when
* triggered. It seems only two asc values are involved: 0xb and 0xd.
* If asc,ascq strings are known (in spc6r06.pdf) for asc 0xb and 0x5d
* then a pointer to that string is returned, else NULL is returned. The
* caller provides a buffer (b) and its length (blen) that a string (if
* found) is placed in. So if a match is found b is returned. */
char *
scsiGetIEString(uint8_t asc, uint8_t ascq, char * b, int blen)
{
if (asc == 0xb) {
switch (ascq) {
case 0:
snprintf(b, blen, "%s", wn);
return b;
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7:
case 0x8:
case 0x9:
snprintf(b, blen, "%s - %s", wn, wn1_9[ascq - 1]);
return b;
case 0x12:
snprintf(b, blen, "%s - microcode security at risk", wn);
return b;
case 0x13:
snprintf(b, blen, "%s - microcode digital signature validation "
"failure", wn);
return b;
case 0x14:
snprintf(b, blen, "%s - physical element status change", wn);
return b;
default:
if ((ascq >= 0xa) && (ascq <= 0x11)) {
uint8_t q = ascq - 0xa;
snprintf(b, blen, "%s - %s %s %s limit exceeded", wn,
(((q % 2) == 0) ? "high" : "low"),
((((q / 2) % 2) == 0) ? "critical" : "operating"),
((((q / 4) % 2) == 0) ? "temperature" : "humidity"));
return b;
} else
return NULL;
}
} else if (asc == 0x5d) {
switch (ascq) {
case 0:
snprintf(b, blen, "Failure %s", pred);
return b;
case 1:
snprintf(b, blen, "Media failure %s", pred);
return b;
case 2:
snprintf(b, blen, "Logical unit failure %s", pred);
return b;
case 3:
snprintf(b, blen, "spare area exhaustion failure %s", pred);
return b;
case 0x1d:
snprintf(b, blen, "%s %s power loss protection circuit area "
"exhaustion failure", five_d_t[0], impfail);
return b;
case 0x73:
snprintf(b, blen, "Media %s endurance limit met", impfail);
return b;
case 0xff:
snprintf(b, blen, "Failure %s (false)", pred);
return b;
default:
if ((ascq >= 0x10) && (ascq <= 0x6c)) {
uint8_t q = ascq - 0x10;
uint8_t rem = q % 0x10;
if (rem <= 0xc) {
snprintf(b, blen, "%s %s %s", five_d_t[q / 0x10], impfail,
impending0_c[rem]);
return b;
} else
return NULL;
} else
return NULL;
}
} else
return NULL;
}
......@@ -187,9 +187,12 @@ struct scsi_supp_log_pages {
/* SCSI Peripheral types (of interest) */
#define SCSI_PT_DIRECT_ACCESS 0x0
#define SCSI_PT_SEQUENTIAL_ACCESS 0x1
#define SCSI_PT_WO 0x4 /* write once device */
#define SCSI_PT_CDROM 0x5
#define SCSI_PT_OPTICAL 0x7
#define SCSI_PT_MEDIUM_CHANGER 0x8
#define SCSI_PT_ENCLOSURE 0xd
#define SCSI_PT_RBC 0xe
#define SCSI_PT_HOST_MANAGED 0x14 /* Zoned disk */
/* Transport protocol identifiers or just Protocol identifiers */
......@@ -512,7 +515,7 @@ uint64_t scsiGetSize(scsi_device * device, bool avoid_rcap16,
struct scsi_readcap_resp * srrp);
/* T10 Standard IE Additional Sense Code strings taken from t10.org */
const char* scsiGetIEString(uint8_t asc, uint8_t ascq);
char * scsiGetIEString(uint8_t asc, uint8_t ascq, char * b, int blen);
int scsiGetTemp(scsi_device * device, uint8_t *currenttemp, uint8_t *triptemp);
......@@ -538,6 +541,7 @@ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
int sense_len, int desc_type);
/* SCSI command transmission interface function declaration. Its
* definition is target OS specific (see os_<OS>.c file).
* Returns 0 if SCSI command successfully launched and response
......
......@@ -343,6 +343,8 @@ scsiGetSmartData(scsi_device * device, bool attribs)
uint8_t triptemp = 255;
const char * cp;
int err = 0;
char b[128];
print_on();
if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
&currenttemp, &triptemp)) {
......@@ -351,7 +353,7 @@ scsiGetSmartData(scsi_device * device, bool attribs)
return -1;
}
print_off();
cp = scsiGetIEString(asc, ascq);
cp = scsiGetIEString(asc, ascq, b, sizeof(b));
if (cp) {
err = -2;
print_on();
......@@ -1187,7 +1189,7 @@ scsiPrintSelfTest(scsi_device * device)
jout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
u = ucp[16] & 0xf;
jglb[st]["sense_key"]["value"] = u;
jglb[st]["sense_key"]["string"] =
jglb[st]["sense_key"]["string"] =
scsi_get_sense_key_str(u, sizeof(b), b);
jglb[st]["asc"] = ucp[17];
jglb[st]["ascq"] = ucp[18];
......@@ -3376,7 +3378,7 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
}
if (gEnviroReportingLPage && options.smart_env_rep) {
scsiPrintEnviroReporting(device);
envRepDone = true;
envRepDone = true;
} else if (gTempLPage)
scsiPrintTemp(device);
// in the 'smartctl -A' case only want: "Accumulated power on time"
......@@ -3576,11 +3578,11 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
scsiGetSupportedLogPages(device);
checkedSupportedLogPages = true;
}
if (gProtocolSpecificLPage) {
if (gProtocolSpecificLPage) {
if (scsiPrintSasPhy(device, options.sasphy_reset))
return returnval | FAILSMART;
any_output = true;
}
}
}
if (options.smart_env_rep && ! envRepDone) {
if (! checkedSupportedLogPages) {
......
......@@ -2403,8 +2403,15 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
int pdt = inqBuf[0] & 0x1f;
if (! ((0 == pdt) || (4 == pdt) || (5 == pdt) || (7 == pdt) ||
(0xe == pdt))) {
switch (pdt) {
case SCSI_PT_DIRECT_ACCESS:
case SCSI_PT_WO:
case SCSI_PT_CDROM:
case SCSI_PT_OPTICAL:
case SCSI_PT_RBC: /* Reduced Block commands */
case SCSI_PT_HOST_MANAGED: /* Zoned disk */
break;
default:
PrintOut(LOG_INFO, "Device: %s, not a disk like device [PDT=0x%x], "
"skip\n", device, pdt);
return 2;
......@@ -2417,8 +2424,8 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
supported_vpd_pages_p = new supported_vpd_pages(scsidev);
lu_id[0] = '\0';
if ((version >= 0x3) && (version < 0x8)) {
/* SPC to SPC-5 */
if (version >= 0x3) {
/* SPC to SPC-5, assume SPC-6 is version==8 or higher */
if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_DEVICE_IDENTIFICATION,
vpdBuf, sizeof(vpdBuf))) {
len = vpdBuf[3];
......@@ -3703,7 +3710,9 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic
}
}
if (asc > 0) {
const char * cp = scsiGetIEString(asc, ascq);
char b[128];
const char * cp = scsiGetIEString(asc, ascq, b, sizeof(b));
if (cp) {
PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp);
......
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