Subject: bin/29165: new scsictl(8) {get,set}errrecov commands
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: Greg A. Woods <woods@weird.com>
List: netbsd-bugs
Date: 01/31/2005 01:28:00
>Number:         29165
>Category:       bin
>Synopsis:       new scsictl(8) {get,set}errrecov commands
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Jan 31 01:28:00 +0000 2005
>Originator:     Greg A. Woods
>Release:        NetBSD-current 2005/01/29
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Environment:
System: NetBSD
>Description:

	scsictl(8) should have commands to view and change the automatic
	read and write recovery bits in mode page 0x01 of block devices.

>How-To-Repeat:

	Try to avoid having to use the ancient scsi(8) tool I ported
	from FreeBSD oh so long ago just to check/enable ARRE & AWRE.

>Fix:

(note these changes are merged to -current from my netbsd-1-6 branch and
have only been tested fully on the latter)

(note also there seems to have been a missing byte in struct page_disk_format)

(note this code doesn't honour the SCSI spec. w.r.t. how the page length
is supposed to be handled and as a result some values could be missed or
clobbered on some devices)

(note too the hints about generic mode page handling :-)

Index: sys/dev/scsipi/scsi_disk.h
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/dev/scsipi/scsi_disk.h,v
retrieving revision 1.28
diff -u -r1.28 scsi_disk.h
--- sys/dev/scsipi/scsi_disk.h	7 Jan 2005 02:08:34 -0000	1.28
+++ sys/dev/scsipi/scsi_disk.h	31 Jan 2005 00:31:21 -0000
@@ -236,6 +236,27 @@
 
 union scsi_disk_pages {
 #define	DISK_PGCODE	0x3F	/* only 6 bits valid */
+	struct page_err_recov {
+		u_int8_t pg_code;	/* page code (should be 1) */
+		u_int8_t pg_length;	/* page length (should be 0x09) */
+		u_int8_t flags;
+#define	ERR_RECOV_DCR	0x01		/* disable correction */
+#define	ERR_RECOV_DTE	0x02		/* disable transfer on error */
+#define	ERR_RECOV_PER	0x04		/* post error */
+#define	ERR_RECOV_EEC	0x08		/* enable early correction */
+#define	ERR_RECOV_RC	0x10		/* read continuous */
+#define	ERR_RECOV_TB	0x20		/* transfer block */
+#define	ERR_RECOV_ARRE	0x40		/* automatic read recovery enable */
+#define	ERR_RECOV_AWRE	0x80		/* automatic write recovery enable */
+		u_int8_t rd_retry_ct;	/* read retry count */
+		u_int8_t corr_span;	/* correction span */
+		u_int8_t hd_off_ct;	/* head offset count */
+		u_int8_t dat_strb_off_ct; /* data strobe offset count */
+		u_int8_t reserved1;
+		u_int8_t wr_retry_ct;	/* write retry count */
+		u_int8_t reserved2;
+		u_int8_t recov_tm_lim[2]; /* recovery time limit x10 ms */
+	} disk_format;
 	struct page_disk_format {
 		u_int8_t pg_code;	/* page code (should be 3) */
 		u_int8_t pg_length;	/* page length (should be 0x16) */
@@ -253,6 +274,7 @@
 #define	DISK_FMT_RMB	0x20
 #define	DISK_FMT_HSEC	0x40
 #define	DISK_FMT_SSEC	0x80
+		u_int8_t reserved1;
 		u_int8_t reserved2;
 		u_int8_t reserved3;
 	} disk_format;
Index: sbin/scsictl/scsictl.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sbin/scsictl/scsictl.c,v
retrieving revision 1.25
diff -u -r1.25 scsictl.c
--- sbin/scsictl/scsictl.c	7 Jan 2005 02:08:34 -0000	1.25
+++ sbin/scsictl/scsictl.c	31 Jan 2005 00:44:17 -0000
@@ -99,6 +99,11 @@
 void	device_getcache __P((int, char *[]));
 void	device_setcache __P((int, char *[]));
 void	device_flushcache __P((int, char *[]));
+void	device_geterrrecov __P((int, char *[]));
+void	device_seterrrecov __P((int, char *[]));
+#if 0
+void	device_modesense __P((int, char *[]));
+#endif
 
 struct command device_commands[] = {
 	{ "defects",	"[primary] [grown] [block|byte|physical]",
@@ -118,6 +123,12 @@
 	{ "getcache",	"",			device_getcache },
 	{ "setcache",	"none|r|w|rw [save]",	device_setcache },
 	{ "flushcache",	"",			device_flushcache },
+	{ "geterrrecov","",			device_geterrrecov },
+	{ "seterrrecov","none|r|w|rw [save]",	device_seterrrecov },
+#if 0
+	{ "modesense",  "pagenum [cur|chg|def|svd]",	device_modesense },
+	{ "modeedit",  "pagenum",		device_modeedit },
+#endif
 	{ NULL,		NULL,			NULL },
 };
 
@@ -517,6 +528,7 @@
 	 * Get the DISK FORMAT mode page.  SCSI-2 recommends specifying the
 	 * interleave read from this page in the FORMAT UNIT command.
 	 */
+	/* XXX 0x03 == Mode-page-03; 0x00 == current-values */
 	scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page));
 
 	j = (mode_page.format_page.bytes_s[0] << 8) |
@@ -872,6 +884,10 @@
 	if (argc != 0)
 		usage();
 
+	/*
+	 * Get the CACHING mode page.
+	 */
+	/* XXX 0x08 == Mode-page-08; 0x00 == current-values */
 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
 
 	if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) ==
@@ -926,6 +942,10 @@
 			usage();
 	}
 
+	/*
+	 * Get the CACHING mode page.
+	 */
+	/* XXX 0x08 == Mode-page-08; 0x00 == current-values */
 	scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data));
 
 	data.caching_params.pg_code &= PGCODE_MASK;
@@ -972,6 +992,114 @@
 }
 
 /*
+ * device_geterrrecov:
+ *
+ *	Get the automatic R/W Error Recovery parameters for a SCSI disk.
+ */
+void
+device_geterrrecov(argc, argv)
+	int argc;
+	char *argv[];
+{
+	struct {
+		struct scsipi_mode_header header;
+		struct scsi_blk_desc blk_desc;
+		struct page_err_recov err_recov_params;
+	} data;
+
+	/* No arguments. */
+	if (argc != 0)
+		usage();
+
+	/*
+	 * Get the READ-WRITE ERROR RECOVERY mode page.
+	 */
+	/* XXX 0x01 == Mode-page-01; 0x00 == current-values */
+	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
+
+	printf("%s: automatic write reallocation %senabled\n", dvname,
+	       (data.err_recov_params.flags & ERR_RECOV_AWRE) ? "" : "not ");
+	printf("%s: automatic read reallocation %senabled\n", dvname,
+	       (data.err_recov_params.flags & ERR_RECOV_ARRE) ? "" : "not ");
+	printf("%s: read retry count: %u\n", dvname,
+	    (unsigned int) data.err_recov_params.rd_retry_ct);
+	printf("%s: correction span: %u\n", dvname,
+	    (unsigned int) data.err_recov_params.corr_span);
+	printf("%s: head offset count: %u\n", dvname,
+	    (unsigned int) data.err_recov_params.hd_off_ct);
+	printf("%s: data strobe offset count: %u\n", dvname,
+	    (unsigned int) data.err_recov_params.dat_strb_off_ct);
+	printf("%s: write retry count: %u\n", dvname,
+	    (unsigned int) data.err_recov_params.wr_retry_ct);
+	printf("%s: recovery time limit: %lu milliseconds\n", dvname,
+	    (unsigned long) (_2btol(data.err_recov_params.recov_tm_lim) * 10));
+	printf("%s: error recovery parameters are %ssavable\n", dvname,
+	    (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not ");
+}
+
+/*
+ * device_seterrrecov:
+ *
+ *	Set the automatic R/W Error Recovery parameters for a SCSI disk.
+ */
+void
+device_seterrrecov(argc, argv)
+	int argc;
+	char *argv[];
+{
+	struct {
+		struct scsipi_mode_header header;
+		struct scsi_blk_desc blk_desc;
+		struct page_err_recov err_recov_params;
+	} data;
+	int dlen;
+	u_int8_t flags, byte2;
+
+	if (argc > 2 || argc == 0)
+		usage();
+
+	if (strcmp(argv[0], "none") == 0)
+		flags = 0;
+	else if (strcmp(argv[0], "r") == 0)
+		flags = ERR_RECOV_ARRE;
+	else if (strcmp(argv[0], "w") == 0)
+		flags = ERR_RECOV_AWRE;
+	else if (strcmp(argv[0], "rw") == 0)
+		flags = ERR_RECOV_ARRE|ERR_RECOV_AWRE;
+	else
+		usage();
+
+	if (argc == 2) {
+		/* XXX could allow specification of retry count(s) and time limit too? */
+		if (strcmp(argv[1], "save") == 0)
+			byte2 = SMS_SP;
+		else
+			usage();
+	}
+
+	/*
+	 * Get the ERROR RECOVERY mode page.
+	 */
+	/* XXX 0x08 == Mode-page-01; 0x00 == current-values */
+	scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data));
+
+	data.err_recov_params.pg_code &= PGCODE_MASK;
+	data.err_recov_params.flags =
+	    (data.err_recov_params.flags & ~(ERR_RECOV_AWRE|ERR_RECOV_ARRE)) | flags;
+
+	data.header.data_length = 0;
+
+	/* XXX what if sizeof(data) does not match dlen? */
+	dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 +
+	    data.err_recov_params.pg_length;
+
+	/*
+	 * save the (possibly modified) ERROR RECOVERY mode page.
+	 */
+	scsi_mode_select(fd, byte2, &data, dlen);
+}
+
+/*
  * device_prevent:
  *
  *      Issue a prevent to a SCSI device.
Index: sbin/scsictl/scsictl.8
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sbin/scsictl/scsictl.8,v
retrieving revision 1.22
diff -u -r1.22 scsictl.8
--- sbin/scsictl/scsictl.8	7 Jan 2005 02:13:13 -0000	1.22
+++ sbin/scsictl/scsictl.8	31 Jan 2005 00:45:05 -0000
@@ -203,6 +203,28 @@
 .Nm flushcache
 .Pp
 Explicitly flushes the write cache.
+.Pp
+.Nm geterrrecov
+.Pp
+Returns basic error recovery parameters for the device.
+.Pp
+.Nm seterrrecov
+.Ar none|r|w|rw
+.Op Ar save
+.Pp
+Set automatic error recovery parameters for the device.  Automatic error
+recovery may be disabled
+.Pq none ,
+just the automatic read recovery enabled
+.Pq r ,
+or just the automatic write recovery enabled
+.Pq w ,
+or both read and write automatic recovery can be enabled
+.Pq rw .
+If the drive's error recovery parameters are savable, specifying
+.Ar save
+after the error recovery enable state will cause the parameters to be
+saved in non-volatile storage.
 .Sh BUS COMMANDS
 The following commands are supported for SCSI busses:
 .Pp