#107164 scsiinfo should grok log pages

Package:
scsitools
Source:
scsitools
Description:
Collection of tools for SCSI hardware management
Submitter:
Adam Goldman
Date:
2005-07-18 03:45:49 UTC
Severity:
wishlist
#107164#5
Date:
2001-07-31 01:08:46 UTC
From:
To:
Currently scsiinfo can print information from mode pages, but not log
pages. I have modified it to print log pages, but it's sort of a hack and
I only implemented the pages available on the Exabyte Eliant 820. I've
included the patch below anyway. It'd be nice if this patch or something
like it could end up in the standard .deb.

You need to uncompress scsiinfo/man8/scsiinfo.8.gz before applying the
patch. BTW, the patch also fixes some typos in the man page
("Miscanellous" and "pages pages").

diff -u -r scsitools-0.2/scsiinfo/man8/scsiinfo.8 scsitools-0.2rtg/scsiinfo/man8/scsiinfo.8
--- scsitools-0.2/scsiinfo/man8/scsiinfo.8	Sun Aug 24 09:35:14 1997
+++ scsitools-0.2rtg/scsiinfo/man8/scsiinfo.8	Mon Jul 30 16:13:58 2001
@@ -7,7 +7,7 @@
 .B scsiinfo
 queries information from an scsi target. This means generally using the
 .B INQUIRY
-scsi command or reading out SCSI-II mode pages (the number of the mode pages and corresponding
+scsi command or reading out SCSI-II mode and log pages (the number of the mode pages and corresponding
 sections of the SCSI-II sections is given below). It allows also to modify some of these
 settings on the scsi device (if it supports it).

@@ -153,7 +153,30 @@
 .B -m
 displays modifiable fields instead of current values (All bits set in modifiable fields).

-.SS Miscanellous
+.SS SCSI-II and vendor log pages
+.B Note that log page reading seems to work well with tape drives, but some hard drives seem to get very unhappy when asked for a log page and may cause a bus reset.
+.TP
+.B -Gw
+displays information from Error Counter Write Page.
+.TP
+.B -Gr
+displays information from Error Counter Read Page.
+.TP
+.B -Gc
+displays information from Data Compression Page.
+.TP
+.B -Gu
+displays information from Drive Usage Information Page.
+.TP
+.B -Ga
+displays information from all supported log pages.
+.TP
+.B -GL
+lists log pages supported by this
+.B scsiinfo
+version and target.
+
+.SS Miscellaneous
 .TP
 .B -v
 Show
@@ -167,7 +190,7 @@
 List scsi devices known to the system.
 .TP
 .B -L
-List mode pages pages supported by this
+List mode pages supported by this
 .B scsiinfo
 version and target (notched pages and active notch are also returned).
 .TP
diff -u -r scsitools-0.2/scsiinfo/scsiinfo.c scsitools-0.2rtg/scsiinfo/scsiinfo.c
--- scsitools-0.2/scsiinfo/scsiinfo.c	Mon Jul 30 16:15:15 2001
+++ scsitools-0.2rtg/scsiinfo/scsiinfo.c	Mon Jul 30 16:14:32 2001
@@ -20,8 +20,14 @@
  * -V    Display information from Verify Error Recovery Page.
  * -v    Show version number
  * -a    All of the above.
+ * -Gw   Display information from Error Counter Write Page
+ * -Gr   Display information from Error Counter Read Page
+ * -Gc   Display information from Data Compression Page
+ * -Gu   Display information from Drive Usage Information Page
+ * -Ga   All of the above.
  * -l    List known scsi devices on the system
- * -L    List pages supported by program and target
+ * -L    List mode pages supported by program and target
+ * -GL   List log pages supported by program and target
  *
  * Only one of the following three options can be specified.
  * None of these three implies the current values are returned.
@@ -120,20 +126,37 @@

 #define MAX_PAGENO (sizeof(page_names)/sizeof(char *))

-#if 0
 char *log_names[] =
 {
-    "Supported Log Pages ",
-    "Buffer Over-Run/Under-Run ",
-    "Error Counter Write ",
-    "Error Counter Read ",
-    "Error Counter Read Reverse ",
-    "Error Counter Verify ",
-    "Non-Medium Error ",
-    "Last n Error Events "};
+    "Supported Log Pages ",		/* SCSI-2 */
+    "Buffer Over-Run/Under-Run ",	/* SCSI-2 */
+    "Error Counter Write ",		/* SCSI-2 */
+    "Error Counter Read ",		/* SCSI-2 */
+    "Error Counter Read Reverse ",	/* SCSI-2 */
+    "Error Counter Verify ",		/* SCSI-2 */
+    "Non-Medium Error ",		/* SCSI-2 */
+    "Last n Error Events ",		/* SCSI-2 */
+    NULL, NULL, NULL, NULL,		/* 0x08-0x0b reserved */
+    NULL, NULL, NULL, NULL,		/* 0x0c-0x0f reserved */
+    NULL, NULL, NULL, NULL,		/* 0x10-0x13 reserved */
+    NULL, NULL, NULL, NULL,		/* 0x14-0x17 reserved */
+    NULL, NULL, NULL, NULL,		/* 0x18-0x1b reserved */
+    NULL, NULL, NULL, NULL,		/* 0x1c-0x1f reserved */
+    NULL, NULL, NULL, NULL,		/* 0x20-0x23 reserved */
+    NULL, NULL, NULL, NULL,		/* 0x24-0x27 reserved */
+    NULL, NULL, NULL, NULL,		/* 0x28-0x2b reserved */
+    NULL, NULL, NULL, NULL,		/* 0x2c-0x2f reserved */
+    NULL, NULL, NULL, NULL,		/* 0x30-0x33 vendor-specific */
+    NULL, NULL, NULL, NULL,		/* 0x34-0x37 vendor-specific */
+    NULL, 				/* 0x38 vendor-specific */
+    "Data Compression ",		/* Exabyte */
+    NULL, NULL,				/* 0x3a-0x3b vendor-specific */
+    "Drive Usage Information "		/* Exabyte */
+/* if we ever run into conflicting vendor pages, we'll have to add multiple
+   tables and switch on the inquiry data... */
+};

 #define MAX_LOGNO (sizeof(log_names)/sizeof(char *))
-#endif

 int hexdata_ptr = -1;		/* We allow for one hexdata (@) field.. this is where we
 				   parsed it */
@@ -194,14 +217,12 @@
     return page_names[pageno];
 }

-#if 0
 char *get_log_name(int pageno)
 {
     if ((pageno >= MAX_LOGNO) || (!log_names[pageno]))
 	return "Unknown ";
     return log_names[pageno];
 }
-#endif

 void dump(void *buffer, unsigned int length)
 {
@@ -412,7 +433,6 @@
     return status;
 }

-#if 0
 int get_log_page(int page, int page_code, char save_values, unsigned short parameter_ptr)
 {
     int status, quiet;
@@ -439,14 +459,11 @@
     cmd[8] = 0xff;		/* allocation length lo */
     cmd[9] = 0x00;		/* control */

-    dump(buffer, 200);
     status = ioctl(fileno(infile), 1 /* SCSI_IOCTL_SEND_COMMAND */ , buffer);
     if (status && (!quiet))
 	fprintf(stderr, "Unable to read %sLog Page %02xh\n", get_log_name(page), page);
-    dump(buffer, 400);
     return status;
 }
-#endif

 /* Contents should point to the mode parameter header that we obtained
    in a prior read operation.  This way we do not have to work out the
@@ -1234,9 +1251,15 @@
 	  "\t-V    Display information from Verify Error Recovery Page.\n"
 	  "\t-v    Show version number\n"
 	  "\t-a    All of the above.\n\n"
+	  "\t-Gw   Display information from Error Counter Write Page.\n"
+	  "\t-Gr   Display information from Error Counter Read Page.\n"
+	  "\t-Gc   Display information from Data Compression Page.\n"
+	  "\t-Gu   Display information from Drive Usage Information Page.\n"
+	  "\t-Ga   All of the above.\n\n"
 	  "\t-l    List known scsi devices on the system\n"
-	  "\t-L    List pages supported notched by program and target\n"
-	  "\t        (notched and active notch are also returned)\n\n"
+	  "\t-L    List mode pages supported notched by program and target\n"
+	  "\t        (notched and active notch are also returned)\n"
+	  "\t-GL   List log pages supported by program and target\n\n"
 	  "\tOnly one of the following three options can be specified.\n"
        "\tNone of these three implies the current values are returned.\n"
 	  "\t-m    Display modifiable fields instead of current values\n"
@@ -1282,6 +1305,177 @@
     return 0;
 }

+struct logparmlist {
+    int code;
+    char *name;
+};
+
+int print_log_page(int page_code, char *pagename, struct logparmlist parms[])
+{
+    int i;
+    int offset, length;
+    int flags, parmlen, parmcode;
+
+    if(get_log_page(0x3c, page_code, 0, 0) != 0)
+	return 2;
+
+    if (!x_interface && !replace) {
+	printf("Data from %sPage\n", pagename);
+	for(i = 0; i < strlen("Data from Page") + strlen(pagename); i++) {
+	     putchar('-');
+	}
+	putchar('\n');
+    }
+
+    offset = 12;
+    length = 12 + (buffer[10] * 256) + buffer[11];
+
+    while ( offset < length ) {
+	parmcode = buffer[offset] * 256 + buffer[offset+1];
+	flags = buffer[offset+2];
+	parmlen = buffer[offset+3];
+	offset += 4;
+	for (i=0; ; i++) {
+	    if(parms[i].name == NULL)
+		break;
+	    if(parms[i].code == parmcode) {
+		intfield(buffer + offset, parmlen, parms[i].name);
+		break;
+	    }
+	}
+	offset += parmlen;
+    }
+
+    if (!x_interface && !replace)
+	putchar('\n');
+
+    return 0;
+}
+
+int log_usage(int page_code)
+{
+    struct logparmlist parms[]= {
+	{ 0x6, "Total Load Count" },
+	{ 0x7, "Minutes Since Last Clean" },
+	{ 0xa, "Cleaning Count" },
+	{ 0x11, "Time to Clean" },
+	{ 0, NULL}
+    };
+
+    return print_log_page(page_code, "Drive Usage Information ", parms);
+}
+
+int log_error_counter_w(int page_code)
+{
+    struct logparmlist parms[]= {
+	{ 0x0, "Errors corrected without delay" },
+	{ 0x1, "Errors corrected with delays" },
+	{ 0x2, "Total rewrites" },
+	{ 0x3, "Total errors corrected" },
+	{ 0x4, "Total times errors processed" },
+	{ 0x5, "Total bytes processed" },
+	{ 0x6, "Total uncorrected errors" },
+	{ 0, NULL }
+    };
+    return print_log_page(page_code, "Error Counter Write ", parms);
+}
+
+int log_error_counter_r(int page_code)
+{
+    struct logparmlist parms[]= {
+	{ 0x0, "Errors corrected without delay" },
+	{ 0x1, "Errors corrected with delays" },
+	{ 0x2, "Total rereads" },
+	{ 0x3, "Total errors corrected" },
+	{ 0x4, "Total times errors processed" },
+	{ 0x5, "Total bytes processed" },
+	{ 0x6, "Total uncorrected errors" },
+	{ 0, NULL }
+    };
+    return print_log_page(page_code, "Error Counter Read ", parms);
+}
+
+int log_compression(int page_code)
+{
+    struct logparmlist parms[]= {
+	{ 0x5, "KB Transferred to Data Compressor" },
+	{ 0x7, "KB Transferred to Tape" },
+	{ 0, NULL }
+    };
+    return print_log_page(page_code, "Data Compression ", parms);
+}
+
+/* Ask the target what log pages it supports. */
+unsigned long long target_log_pages(int page_code) {
+    unsigned long long pages = 0;
+    int offset, length;
+
+    if (get_log_page(0, page_code, 0, 0) != 0)
+	return 0;
+
+    offset = 12;
+    length = 12 + (buffer[10] * 256) + buffer[11];
+
+    while ( offset < length ) {
+	pages |= (1LL << (buffer[offset] & 0x3f));
+	offset++;
+    }
+
+    return pages;
+}
+
+int all_log_pages(int page_code) {
+    unsigned long long pages_mask=0;
+    unsigned long long pages_sup;
+    int i;
+    int status = 0;
+
+    pages_sup = target_log_pages(page_code);
+    if (pages_sup == 0)
+	return 2;
+
+    for (i = 0; i < MAX_LOGNO; i++)
+	if (log_names[i])
+	    pages_mask |= (1LL << i);
+
+    pages_sup &= pages_mask;
+
+    if (pages_sup & (1 << 2))
+	status |= log_error_counter_w(page_code);
+    if (pages_sup & (1 << 3))
+	status |= log_error_counter_r(page_code);
+    if (pages_sup & (1LL << 0x39))
+	status |= log_compression(page_code);
+    if (pages_sup & (1LL << 0x3c))
+	status |= log_usage(page_code);
+
+    return status;
+}
+
+/* Display a list of pages the target supports */
+int show_log_pages(int page_code) {
+    unsigned long long pages_sup;
+    int i;
+    unsigned long long l;
+
+    pages_sup=target_log_pages(page_code);
+    if (pages_sup == 0)
+	return 2;
+
+    puts("Log Pages supported by this target:");
+    puts("-----------------------------------");
+
+    l = 1;
+    for (i = 0; i < 0x40; i++) {
+	if (pages_sup & l) {
+	     printf("%02xh: %sPage\n", i, get_log_name(i));
+	}
+	l <<= 1;
+    }
+
+    return 0;
+}
+
 int main(int argc, char *argv[])
 {
     char c;
@@ -1290,10 +1484,12 @@
     char all = 0;
     int i;
     long tmp;
+    char *log = NULL;
+    char *p;

     if (argc < 2)
 	usage("too few arguments");
-    while ((c = getopt(argc, argv, "agdcfisDeCXmMSRvlnLpVF:")) != EOF) {
+    while ((c = getopt(argc, argv, "agdcfisDeCXmMSRvlnLpVF:G:")) != EOF) {
 	switch (c) {
 	case 'F':
 	    if (!strcasecmp(optarg, "logical"))
@@ -1380,6 +1576,9 @@
 	case 'v':
 	    fprintf(stderr, " Scsiinfo version 1.7(eowmob)\n");
 	    break;
+	case 'G':
+	    log = optarg;
+	    break;
 	default:
 	    fprintf(stderr, "Unknown option '-%c' (ascii %02xh)\n", c, c);
 	    usage("bad option");
@@ -1446,6 +1645,43 @@
 	exit(0);
     };

+    if (!x_interface)
+	printf("\n");
+
+    page_code = 1; /* cumulative values for log pages */
+
+    if (log) {
+	for (p=log; *p != '\0'; p++) {
+	    switch (*p) {
+	    case 'L':
+		status |= show_log_pages(page_code);
+		break;
+	    case 'w':
+		status |= log_error_counter_w(page_code);
+		break;
+	    case 'r':
+		status |= log_error_counter_r(page_code);
+		break;
+	    case 'c':
+		status |= log_compression(page_code);
+		break;
+	    case 'u':
+		status |= log_usage(page_code);
+		break;
+	    case 'a':
+		status |= all_log_pages(page_code);
+		break;
+#if 0
+	    case '0':
+		status |= zero_logs(page_code);
+		break;
+#endif
+	    default:
+		usage("Illegal -G parameter"); /* XXX */
+	    }
+	}
+    }
+
     page_code = 0;
     if (modifiable)
 	page_code = 1;
@@ -1453,9 +1689,6 @@
 	page_code = 2;
     if (saved)
 	page_code = 3;
-
-    if (!x_interface)
-	printf("\n");

     if (inquiry)
 	status |= do_inquiry(page_code);

#107164#10
Date:
2001-07-31 02:19:29 UTC
From:
To:
The patch I sent worked for the Drive Usage Information page but the
other pages were broken. The below changes would need to be applied after
that patch. Oops.
--- scsiinfo.c.old Mon Jul 30 19:07:54 2001 +++ scsiinfo.c Mon Jul 30 19:09:21 2001 @@ -1310,13 +1310,13 @@ char *name; };