uint8_t smart_data[512];
uint8_t smart_thresholds[512];
+ SkBool smart_initialized:1;
+
SkBool identify_valid:1;
SkBool smart_data_valid:1;
SkBool smart_thresholds_valid:1;
#define SK_MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL)
#define SK_MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL)
+static int init_smart(SkDisk *d);
+
static const char *disk_type_to_human_string(SkDiskType type) {
/* %STRINGPOOLSTART% */
int ret;
size_t len = 512;
+ if (init_smart(d) < 0)
+ return -1;
+
if (!disk_smart_is_available(d)) {
errno = ENOTSUP;
return -1;
uint16_t cmd[6];
int ret;
+ if (init_smart(d) < 0)
+ return -1;
+
if (!disk_smart_is_available(d)) {
errno = ENOTSUP;
return -1;
uint16_t cmd[6];
int ret;
+ if (init_smart(d) < 0)
+ return -1;
+
if (!disk_smart_is_available(d)) {
errno = ENOTSUP;
return -1;
a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150;
else if (!strcmp(a->name, "power-on-minutes"))
a->pretty_value = fourtyeight * 60 * 1000;
- else if (!strcmp(a->name, "power-on-seconds"))
+ else if (!strcmp(a->name, "power-on-seconds") ||
+ !strcmp(a->name, "power-on-seconds-2"))
a->pretty_value = fourtyeight * 1000;
else if (!strcmp(a->name, "power-on-half-minutes"))
a->pretty_value = fourtyeight * 30 * 1000;
else if (!strcmp(a->name, "reallocated-sector-count") ||
!strcmp(a->name, "current-pending-sector"))
a->pretty_value = fourtyeight & 0xFFFFFFFFU;
+ else if (!strcmp(a->name, "endurance-remaining") ||
+ !strcmp(a->name, "available-reserved-space"))
+ a->pretty_value = a->current_value;
+ else if (!strcmp(a->name, "total-lbas-written") ||
+ !strcmp(a->name, "total-lbas-read"))
+ a->pretty_value = fourtyeight * 65536LLU * 512LLU / 1000000LLU;
+ else if (!strcmp(a->name, "timed-workload-media-wear") ||
+ !strcmp(a->name, "timed-workload-host-reads"))
+ a->pretty_value = (double)fourtyeight / 1024LLU;
+ else if (!strcmp(a->name, "workload-timer"))
+ a->pretty_value = fourtyeight * 60 * 1000;
else
a->pretty_value = fourtyeight;
}
max_sectors = d->size / 512ULL;
- if (max_sectors > 0 && a->pretty_value > max_sectors) {
+ if (a->pretty_value == 0xffffffffULL ||
+ a->pretty_value == 0xffffffffffffffffULL ||
+ (max_sectors > 0 && a->pretty_value > max_sectors)) {
a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
d->attribute_verification_bad = TRUE;
} else {
[11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
[12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
[13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
+ [170] = { "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
+ [171] = { "program-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
+ [172] = { "erase-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
+ [184] = { "end-to-end-error", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
[187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors },
+ [188] = { "command-timeout", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
[189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
[190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
[191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
[228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL },
[230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
[231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature },
+
+ /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */
+ [232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL },
+ [233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
+ [234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL },
+ [235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL },
+
[240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time },
+ [241] = { "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
+ [242] = { "total-lbas-read", SK_SMART_ATTRIBUTE_UNIT_MB, NULL },
[250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }
};
/* %STRINGPOOLSTOP% */
typedef enum SkSmartQuirk {
- SK_SMART_QUIRK_9_POWERONMINUTES = 0x0001,
- SK_SMART_QUIRK_9_POWERONSECONDS = 0x0002,
- SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x0004,
- SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x0008,
- SK_SMART_QUIRK_193_LOADUNLOAD = 0x0010,
- SK_SMART_QUIRK_194_10XCELSIUS = 0x0020,
- SK_SMART_QUIRK_194_UNKNOWN = 0x0040,
- SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x0080,
- SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x0100,
- SK_SMART_QUIRK_5_UNKNOWN = 0x0200,
- SK_SMART_QUIRK_9_UNKNOWN = 0x0400,
- SK_SMART_QUIRK_197_UNKNOWN = 0x0800,
- SK_SMART_QUIRK_198_UNKNOWN = 0x1000,
+ SK_SMART_QUIRK_9_POWERONMINUTES = 0x000001,
+ SK_SMART_QUIRK_9_POWERONSECONDS = 0x000002,
+ SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004,
+ SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008,
+ SK_SMART_QUIRK_193_LOADUNLOAD = 0x000010,
+ SK_SMART_QUIRK_194_10XCELSIUS = 0x000020,
+ SK_SMART_QUIRK_194_UNKNOWN = 0x000040,
+ SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080,
+ SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100,
+ SK_SMART_QUIRK_5_UNKNOWN = 0x000200,
+ SK_SMART_QUIRK_9_UNKNOWN = 0x000400,
+ SK_SMART_QUIRK_197_UNKNOWN = 0x000800,
+ SK_SMART_QUIRK_198_UNKNOWN = 0x001000,
+ SK_SMART_QUIRK_190_UNKNOWN = 0x002000,
+ SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000,
+ SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000,
+ SK_SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000,
+ SK_SMART_QUIRK_4_UNUSED = 0x020000,
+ SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000,
+ SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000,
+ SK_SMART_QUIRK_228_WORKLOADTIMER = 0x100000,
+ SK_SMART_QUIRK_3_UNUSED = 0x200000
} SkSmartQuirk;
/* %STRINGPOOLSTART% */
"9_UNKNOWN",
"197_UNKNOWN",
"198_UNKNOWN",
+ "190_UNKNOWN",
+ "232_AVAILABLERESERVEDSPACE",
+ "233_MEDIAWEAROUTINDICATOR",
+ "225_TOTALLBASWRITTEN",
+ "4_UNUSED",
+ "226_TIMEWORKLOADMEDIAWEAR",
+ "227_TIMEWORKLOADHOSTREADS",
+ "228_WORKLOADTIMER",
+ "3_UNUSED",
NULL
};
/* %STRINGPOOLSTOP% */
"^MBZOC60P$",
SK_SMART_QUIRK_5_UNKNOWN
}, {
+
+ /*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700
+ https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */
+ "^MCCOE64GEMPP$",
+ "^2.9.0[3-9]$",
+ SK_SMART_QUIRK_5_UNKNOWN|
+ SK_SMART_QUIRK_190_UNKNOWN
+ }, {
+
+ /*** Intel */
+ "^INTEL SSDSA2CW[0-9]{3}G3$",
+ NULL,
+ SK_SMART_QUIRK_3_UNUSED|
+ SK_SMART_QUIRK_4_UNUSED|
+ SK_SMART_QUIRK_225_TOTALLBASWRITTEN|
+ SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR|
+ SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS|
+ SK_SMART_QUIRK_228_WORKLOADTIMER|
+ SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE|
+ SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR
+ }, {
NULL,
NULL,
0
if (quirk) {
switch (id) {
+ case 3:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_3_UNUSED) {
+ static const SkSmartAttributeInfo a = {
+ "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+
+ break;
+
+ case 4:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_4_UNUSED) {
+ static const SkSmartAttributeInfo a = {
+ "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+
+ break;
case 5:
if (quirk & SK_SMART_QUIRK_5_UNKNOWN)
break;
+ case 190:
+ if (quirk & SK_SMART_QUIRK_190_UNKNOWN)
+ return NULL;
+
+ break;
+
case 192:
/* %STRINGPOOLSTART% */
if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) {
/* %STRINGPOOLSTOP% */
break;
+
+ case 225:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) {
+ static const SkSmartAttributeInfo a = {
+ "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+
+ break;
+
+ case 226:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) {
+ static const SkSmartAttributeInfo a = {
+ "timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+
+ break;
+
+ case 227:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) {
+ static const SkSmartAttributeInfo a = {
+ "timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+
+ break;
+
+ case 228:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) {
+ static const SkSmartAttributeInfo a = {
+ "workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+
+ break;
+
+ case 232:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) {
+ static const SkSmartAttributeInfo a = {
+ "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+ break;
+
+ case 233:
+ /* %STRINGPOOLSTART% */
+ if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) {
+ static const SkSmartAttributeInfo a = {
+ "media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL
+ };
+ return &a;
+ }
+ /* %STRINGPOOLSTOP% */
+ break;
+
}
}
[SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
[SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
[SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
- [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK"
+ [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK",
+ [SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%",
+ [SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%",
+ [SK_SMART_ATTRIBUTE_UNIT_MB] = "MB"
};
/* %STRINGPOOLSTOP% */
if (!strcmp(a->name, "power-on-minutes") ||
!strcmp(a->name, "power-on-seconds") ||
+ !strcmp(a->name, "power-on-seconds-2") ||
!strcmp(a->name, "power-on-half-minutes") ||
!strcmp(a->name, "power-on-hours")) {
snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value);
break;
+ case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
+ snprintf(s, len, "%llu%%", (unsigned long long) pretty_value);
+ break;
+
+ case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
+ snprintf(s, len, "%0.3f%%", (double) pretty_value);
+ break;
+
+ case SK_SMART_ATTRIBUTE_UNIT_MB:
+ if (pretty_value >= 1000000LLU)
+ snprintf(s, len, "%0.3f TB", (double) pretty_value / 1000000LLU);
+ else if (pretty_value >= 1000LLU)
+ snprintf(s, len, "%0.3f GB", (double) pretty_value / 1000LLU);
+ else
+ snprintf(s, len, "%llu MB", (unsigned long long) pretty_value);
+ break;
+
case SK_SMART_ATTRIBUTE_UNIT_NONE:
snprintf(s, len, "%llu", (unsigned long long) pretty_value);
break;
goto finish;
}
- if ((vid == 0x0c0b && pid == 0xb159) ||
+ if ((vid == 0x0928 && pid == 0x0000))
+ /* This Oxford Semiconductor bridge seems to
+ * choke on SAT commands. Let's explicitly
+ * black list it here.
+ *
+ * http://bugs.freedesktop.org/show_bug.cgi?id=24951 */
+ d->type = SK_DISK_TYPE_NONE;
+ else if ((vid == 0x152d && pid == 0x2329) ||
+ (vid == 0x152d && pid == 0x2338) ||
+ (vid == 0x152d && pid == 0x2339))
+ /* Some JMicron bridges seem to choke on SMART
+ * commands, so let's explicitly black list
+ * them here.
+ *
+ * https://bugzilla.redhat.com/show_bug.cgi?id=515881
+ *
+ * At least some of the JMicron bridges with
+ * these vids/pids choke on the jmicron access
+ * mode. To make sure we don't break things
+ * for people we now disable this by
+ * default. */
+ d->type = SK_DISK_TYPE_NONE;
+ else if ((vid == 0x152d && pid == 0x2336))
+ /* This JMicron bridge seems to always work
+ * with SMART commands send with the jmicron
+ * access mode. */
+ d->type = SK_DISK_TYPE_JMICRON;
+ else if ((vid == 0x0c0b && pid == 0xb159) ||
(vid == 0x04fc && pid == 0x0c25) ||
(vid == 0x04fc && pid == 0x0c15))
d->type = SK_DISK_TYPE_SUNPLUS;
- else if ((vid == 0x152d && pid == 0x2329) ||
- (vid == 0x152d && pid == 0x2336) ||
- (vid == 0x152d && pid == 0x2338) ||
- (vid == 0x152d && pid == 0x2339))
- d->type = SK_DISK_TYPE_JMICRON;
else
d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12;
return r;
}
+static int init_smart(SkDisk *d) {
+ /* We don't do the SMART initialization right-away, since some
+ * drivers spin up when we do that */
+
+ int ret;
+
+ if (d->smart_initialized)
+ return 0;
+
+ d->smart_initialized = TRUE;
+
+ /* Check if driver can do SMART, and enable if necessary */
+ if (!disk_smart_is_available(d))
+ return 0;
+
+ if (!disk_smart_is_enabled(d)) {
+ if ((ret = disk_smart_enable(d, TRUE)) < 0)
+ goto fail;
+
+ if ((ret = disk_identify_device(d)) < 0)
+ goto fail;
+
+ if (!disk_smart_is_enabled(d)) {
+ errno = EIO;
+ ret = -1;
+ goto fail;
+ }
+ }
+
+ disk_smart_read_thresholds(d);
+ ret = 0;
+
+fail:
+ return ret;
+}
+
int sk_disk_open(const char *name, SkDisk **_d) {
SkDisk *d;
int ret = -1;
d->type = SK_DISK_TYPE_NONE;
} else
disk_identify_device(d);
-
- /* Check if driver can do SMART, and enable if necessary */
- if (disk_smart_is_available(d)) {
-
- if (!disk_smart_is_enabled(d)) {
- if ((ret = disk_smart_enable(d, TRUE)) < 0)
- goto fail;
-
- if ((ret = disk_identify_device(d)) < 0)
- goto fail;
-
- if (!disk_smart_is_enabled(d)) {
- errno = EIO;
- ret = -1;
- goto fail;
- }
- }
-
- disk_smart_read_thresholds(d);
- }
}
*_d = d;
size =
(d->identify_valid ? 8 + sizeof(d->identify) : 0) +
(d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) +
- (d->smart_thresholds ? 8 + sizeof(d->smart_thresholds) : 0);
+ (d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0);
if (sk_disk_smart_status(d, &good) >= 0) {
size += 12;