Module Name:    src
Committed By:   mrg
Date:           Wed Dec  5 06:49:54 UTC 2018

Modified Files:
        src/sbin/atactl: atactl.8 atactl.c

Log Message:
"smart status" already obtains the ata parameters to check if smart
is actually supported, so we can attempt to guess a vendor smart
table from the model name.  add basic support for all the micron /
crucial disk names i could find, and add a couple more micron
specific values.

XXX: probably should add regex support for matching, and probably
     should be more restrictive with the current matches.


To generate a diff of this commit:
cvs rdiff -u -r1.26 -r1.27 src/sbin/atactl/atactl.8
cvs rdiff -u -r1.78 -r1.79 src/sbin/atactl/atactl.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sbin/atactl/atactl.8
diff -u src/sbin/atactl/atactl.8:1.26 src/sbin/atactl/atactl.8:1.27
--- src/sbin/atactl/atactl.8:1.26	Sat Nov  3 10:51:14 2018
+++ src/sbin/atactl/atactl.8	Wed Dec  5 06:49:54 2018
@@ -1,4 +1,4 @@
-.\"	$NetBSD: atactl.8,v 1.26 2018/11/03 10:51:14 wiz Exp $
+.\"	$NetBSD: atactl.8,v 1.27 2018/12/05 06:49:54 mrg Exp $
 .\"
 .\" Copyright (c) 1998 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd October 4, 2018
+.Dd December 5, 2018
 .Dt ATACTL 8
 .Os
 .Sh NAME
@@ -162,6 +162,8 @@ information if known to
 Currently, only
 .Dq micron
 has a vendor-specific table.
+If the vendor is not supplied, it may be guessed from devices' model
+or other data available.
 .It Ar offline #
 Runs the numbered offline self-test on the drive.
 .It Ar error-log
@@ -273,6 +275,10 @@ It was based heavily on the
 .Xr scsictl 8
 command written by
 .An Jason R. Thorpe .
+.An Matthew R. Green
+significantly enhanced the 
+.Cm smart status
+support.
 .Sh BUGS
 The output from the
 .Cm identify

Index: src/sbin/atactl/atactl.c
diff -u src/sbin/atactl/atactl.c:1.78 src/sbin/atactl/atactl.c:1.79
--- src/sbin/atactl/atactl.c:1.78	Wed Oct 31 20:00:56 2018
+++ src/sbin/atactl/atactl.c	Wed Dec  5 06:49:54 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: atactl.c,v 1.78 2018/10/31 20:00:56 mrg Exp $	*/
+/*	$NetBSD: atactl.c,v 1.79 2018/12/05 06:49:54 mrg Exp $	*/
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: atactl.c,v 1.78 2018/10/31 20:00:56 mrg Exp $");
+__RCSID("$NetBSD: atactl.c,v 1.79 2018/12/05 06:49:54 mrg Exp $");
 #endif
 
 
@@ -114,7 +114,7 @@ static void	print_selftest_entry(int, co
 static void	print_error(const void *);
 static void	print_selftest(const void *);
 
-static const struct ataparams *getataparams(void);
+static void	fillataparams(void);
 
 static int	is_smart(void);
 
@@ -122,6 +122,10 @@ static int	fd;				/* file descriptor for
 static const	char *dvname;			/* device name */
 static char	dvname_store[MAXPATHLEN];	/* for opendisk(3) */
 static const	char *cmdname;			/* command user issued */
+static const	struct ataparams *inqbuf;	/* inquiry buffer */
+static char	model[sizeof(inqbuf->atap_model)+1];
+static char	revision[sizeof(inqbuf->atap_revision)+1];
+static char	serial[sizeof(inqbuf->atap_serial)+1];
 
 static void	device_identify(int, char *[]);
 static void	device_setidle(int, char *[]);
@@ -355,7 +359,9 @@ static const struct attr_table {
 static const struct attr_table micron_smart_names[] = {
 	{   5,		"Reallocated NAND block count", NULL },
 	{ 173,          "Average block erase count", NULL },
+	{ 181,          "Non 4K aligned access count", NULL },
 	{ 184,          "Error correction count", NULL },
+	{ 189,          "Factory bad block count", NULL },
 	{ 197,		"Current pending ECC count", NULL },
 	{ 198,		"SMART offline scan uncorrectable error count", NULL },
 	{ 202,		"Percent lifetime remaining", NULL },
@@ -855,14 +861,19 @@ print_selftest(const void *buf)
 		print_selftest_entry(i, &stlog->log_entries[i]);
 }
 
-static const struct ataparams *
-getataparams(void)
+static void
+fillataparams(void)
 {
 	struct atareq req;
 	static union {
 		unsigned char inbuf[DEV_BSIZE];
 		struct ataparams inqbuf;
 	} inbuf;
+	static int first = 1;
+
+	if (!first)
+		return;
+	first = 0;
 
 	memset(&inbuf, 0, sizeof(inbuf));
 	memset(&req, 0, sizeof(req));
@@ -875,7 +886,7 @@ getataparams(void)
 
 	ata_command(&req);
 
-	return (&inbuf.inqbuf);
+	inqbuf = &inbuf.inqbuf;
 }
 
 /*
@@ -888,10 +899,9 @@ static int
 is_smart(void)
 {
 	int retval = 0;
-	const struct ataparams *inqbuf;
 	const char *status;
 
-	inqbuf = getataparams();
+	fillataparams();
 
 	if (inqbuf->atap_cmd_def != 0 && inqbuf->atap_cmd_def != 0xffff) {
 		if (!(inqbuf->atap_cmd_set1 & WDC_CMD1_SMART)) {
@@ -951,8 +961,7 @@ extract_string(char *buf, size_t bufmax,
 }
 
 static void
-compute_capacity(const struct ataparams *inqbuf, uint64_t *capacityp,
-    uint64_t *sectorsp, uint32_t *secsizep)
+compute_capacity(uint64_t *capacityp, uint64_t *sectorsp, uint32_t *secsizep)
 {
 	uint64_t capacity;
 	uint64_t sectors;
@@ -994,38 +1003,51 @@ compute_capacity(const struct ataparams 
 }
 
 /*
- * DEVICE COMMANDS
+ * Inspect the inqbuf and guess what vendor to use.  This list is fairly
+ * basic, and probably should be converted into a regexp scheme.
  */
+static const char *
+guess_vendor(void)
+{
+	struct {
+		const char *model;
+		const char *vendor;
+	} model_to_vendor[] = {
+		{ "Crucial", "Micron" },
+		{ "Micron", "Micron" },
+		{ "C300-CT", "Micron" },
+		{ "C400-MT", "Micron" },
+		{ "M4-CT", "Micron" },
+		{ "M500", "Micron" },
+		{ "M510", "Micron" },
+		{ "M550", "Micron" },
+		{ "MTFDDA", "Micron" },
+		{ "EEFDDA", "Micron" },
+	};
+	unsigned i;
+
+	for (i = 0; i < __arraycount(model_to_vendor); i++)
+		if (strncasecmp(model, model_to_vendor[i].model,
+				strlen(model_to_vendor[i].model)) == 0)
+			return model_to_vendor[i].vendor;
+
+	return NULL;
+}
 
 /*
- * device_identify:
- *
- *	Display the identity of the device
+ * identify_fixup() - Given an obtained ataparams, fix up the endian and
+ * other issues before using them.
  */
 static void
-device_identify(int argc, char *argv[])
+identify_fixup(void)
 {
-	const struct ataparams *inqbuf;
-	char model[sizeof(inqbuf->atap_model)+1];
-	char revision[sizeof(inqbuf->atap_revision)+1];
-	char serial[sizeof(inqbuf->atap_serial)+1];
-	char hnum[12];
-	uint64_t capacity;
-	uint64_t sectors;
-	uint32_t secsize;
-	int lb_per_pb;
 	int needswap = 0;
-	int i;
-	uint8_t checksum;
-
-	/* No arguments. */
-	if (argc != 0)
-		usage();
-
-	inqbuf = getataparams();
 
 	if ((inqbuf->atap_integrity & WDC_INTEGRITY_MAGIC_MASK) ==
 	    WDC_INTEGRITY_MAGIC) {
+		int i;
+		uint8_t checksum;
+
 		for (i = checksum = 0; i < 512; i++)
 			checksum += ((const uint8_t *)inqbuf)[i];
 		if (checksum != 0)
@@ -1062,6 +1084,33 @@ device_identify(int argc, char *argv[])
 		inqbuf->atap_serial, sizeof(inqbuf->atap_serial),
 		needswap);
 
+}
+
+/*
+ * DEVICE COMMANDS
+ */
+
+/*
+ * device_identify:
+ *
+ *	Display the identity of the device
+ */
+static void
+device_identify(int argc, char *argv[])
+{
+	char hnum[12];
+	uint64_t capacity;
+	uint64_t sectors;
+	uint32_t secsize;
+	int lb_per_pb;
+
+	/* No arguments. */
+	if (argc != 0)
+		usage();
+
+	fillataparams();
+	identify_fixup();
+
 	printf("Model: %s, Rev: %s, Serial #: %s\n",
 		model, revision, serial);
 
@@ -1081,7 +1130,7 @@ device_identify(int argc, char *argv[])
 		 inqbuf->atap_config & ATA_CFG_FIXED ? "fixed" : "removable");
 	printf("\n");
 
-	compute_capacity(inqbuf, &capacity, &sectors, &secsize);
+	compute_capacity(&capacity, &sectors, &secsize);
 
 	humanize_number(hnum, sizeof(hnum), capacity, "bytes",
 		HN_AUTOSCALE, HN_DIVISOR_1000);
@@ -1361,7 +1410,7 @@ device_smart(int argc, char *argv[])
 		is_smart();
 	} else if (strcmp(argv[0], "status") == 0) {
 		int rv;
-		char *vendor = argc > 1 ? argv[1] : NULL;
+		const char *vendor = argc > 1 ? argv[1] : NULL;
 
 		rv = is_smart();
 
@@ -1416,6 +1465,11 @@ device_smart(int argc, char *argv[])
 
 		ata_command(&req);
 
+		if (!vendor || strcmp(vendor, "noauto") == 0) {
+			fillataparams();
+			identify_fixup();
+			vendor = guess_vendor();
+		}
 		print_smart_status(inbuf, inbuf2, vendor);
 
 	} else if (strcmp(argv[0], "offline") == 0) {
@@ -1490,7 +1544,6 @@ static void
 device_security(int argc, char *argv[])
 {
 	struct atareq req;
-	const struct ataparams *inqbuf;
 	unsigned char data[DEV_BSIZE];
 	char *pass;
 
@@ -1500,7 +1553,7 @@ device_security(int argc, char *argv[])
 
 	memset(&req, 0, sizeof(req));
 	if (strcmp(argv[0], "status") == 0) {
-		inqbuf = getataparams();
+		fillataparams();
 		print_bitinfo("\t", "\n", inqbuf->atap_sec_st, ata_sec_st);
 	} else if (strcmp(argv[0], "freeze") == 0) {
 		req.command = WDCC_SECURITY_FREEZE;
@@ -1551,7 +1604,7 @@ device_security(int argc, char *argv[])
 		} else if (strcmp(argv[0], "erase") == 0) {
 			struct atareq prepare;
 
-			inqbuf = getataparams();
+			fillataparams();
 
 			/*
 			 * XXX Any way to lock the device to make sure
@@ -1589,7 +1642,7 @@ device_security(int argc, char *argv[])
 			 */
 			if (req.timeout == 30600000) {
 				uint64_t bytes, timeout;
-				compute_capacity(inqbuf, &bytes, NULL, NULL);
+				compute_capacity(&bytes, NULL, NULL);
 				timeout = (bytes / (16 * 1024 * 1024)) * 1000;
 				if (timeout > (uint64_t)INT_MAX)
 					req.timeout = INT_MAX;

Reply via email to