ref: 59a3427361d807610a258d7712ff15e44957922d
parent: 78a18e43b40535b3409ef5bca5bcbcd7307f3d58
author: google <google@daverabbitz.ath.cx>
date: Thu Sep 20 18:42:06 EDT 2012
Add Erik Quanstrom's smart tool for ATA SMART.
--- /dev/null
+++ b/sys/src/cmd/disk/smart/ata.c
@@ -1,0 +1,192 @@
+#include <u.h>
+#include <libc.h>
+#include <fis.h>
+#include "smart.h"
+
+enum{+ Nop,
+ Idall,
+ Idpkt,
+ Smart,
+ Id,
+ Sig,
+
+ Cmdsz = 18,
+ Replysz = 18,
+
+};
+
+typedef struct Atatab Atatab;
+struct Atatab {+ ushort cc;
+ uchar protocol;
+ char *name;
+};
+
+Atatab atatab[] = {+[Nop] 0x00, Pnd|P28, "nop",
+[Idall] 0xff, Pin|Ppio|P28, "identify * device",
+[Idpkt] 0xa1, Pin|Ppio|P28, "identify packet device",
+[Smart] 0xb0, Pnd|P28, "smart",
+[Id] 0xec, Pin|Ppio|P28, "identify device",
+[Sig] 0xf000, Pnd|P28, "signature",
+};
+
+typedef struct Rcmd Rcmd;
+struct Rcmd{+ uchar sdcmd; /* sd command; 0xff means ata passthrough */
+ uchar ataproto; /* ata protocol. non-data, pio, reset, dd, etc. */
+ uchar fis[Fissize];
+};
+
+typedef struct Req Req;
+struct Req {+ char haverfis;
+ Rcmd cmd;
+ Rcmd reply;
+ uchar data[0x200];
+ uint count;
+};
+
+static int
+issueata(Req *r, Sdisk *d, int errok)
+{+ char buf[ERRMAX];
+ int ok, rv;
+
+ if((rv = write(d->fd, &r->cmd, Cmdsz)) != Cmdsz){+ /* handle non-atazz compatable kernels */
+ rerrstr(buf, sizeof buf);
+ if(rv != -1 || strstr(buf, "bad arg in system call") != 0)
+ eprint(d, "fis write error: %r\n");
+ return -1;
+ }
+
+ werrstr("");+ switch(r->cmd.ataproto & Pdatam){+ default:
+ ok = read(d->fd, "", 0) == 0;
+ break;
+ case Pin:
+ ok = read(d->fd, r->data, r->count) == r->count;
+ break;
+ case Pout:
+ ok = write(d->fd, r->data, r->count) == r->count;
+ break;
+ }
+ rv = 0;
+ if(ok == 0){+ rerrstr(buf, sizeof buf);
+ if(!errok && strstr(buf, "not sata") == 0)
+ eprint(d, "xfer error: %.2ux%.2ux: %r\n", r->cmd.fis[0], r->cmd.fis[2]);
+ rv = -1;
+ }
+ if(read(d->fd, &r->reply, Replysz) != Replysz){+ if(!errok)
+ eprint(d, "status fis read error: %r\n");
+ return -1;
+ }
+ r->haverfis = 1;
+ return rv;
+}
+
+int
+issueatat(Req *r, int i, Sdisk *d, int e)
+{+ uchar *fis;
+ Atatab *a;
+
+ a = atatab + i;
+ r->haverfis = 0;
+ r->cmd.sdcmd = 0xff;
+ r->cmd.ataproto = a->protocol;
+ fis = r->cmd.fis;
+ fis[0] = H2dev;
+ if(a->cc & 0xff00)
+ fis[0] = a->cc >> 8;
+ fis[1] = Fiscmd;
+ if(a->cc != 0xff)
+ fis[2] = a->cc;
+ return issueata(r, d, e);
+}
+
+int
+ataprobe(Sdisk *d)
+{+ int rv;
+ Req r;
+
+ memset(&r, 0, sizeof r);
+ if(issueatat(&r, Sig, d, 1) == -1)
+ return -1;
+ setfissig(d, fistosig(r.reply.fis));
+ memset(&r, 0, sizeof r);
+ r.count = 0x200;
+ identifyfis(d, r.cmd.fis);
+ if((rv = issueatat(&r, Idall, d, 1)) != -1){+ idfeat(d, (ushort*)r.data);
+ if((d->feat & Dsmart) == 0)
+ rv = -1;
+ }
+ return rv;
+}
+
+int
+smartfis(Sfis *f, uchar *c, int n)
+{+ if((f->feat & Dsmart) == 0)
+ return -1;
+ skelfis(c);
+ c[2] = 0xb0;
+ c[3] = 0xd8 + n; /* able smart */
+ c[5] = 0x4f;
+ c[6] = 0xc2;
+ return 0;
+}
+
+int
+ataenable(Sdisk *d)
+{+ int rv;
+ Req r;
+
+ memset(&r, 0, sizeof r);
+ smartfis(d, r.cmd.fis, 0);
+ rv = issueatat(&r, Smart, d, 0);
+ return rv;
+}
+
+void
+smartrsfis(Sfis*, uchar *c)
+{+ skelfis(c);
+ c[2] = 0xb0;
+ c[3] = 0xda; /* return smart status */
+ c[5] = 0x4f;
+ c[6] = 0xc2;
+}
+
+int
+atastatus(Sdisk *d, char *s, int l)
+{+ uchar *fis;
+ int rv;
+ Req r;
+
+ memset(&r, 0, sizeof r);
+ smartrsfis(d, r.cmd.fis);
+ rv = issueatat(&r, Smart, d, 0);
+ *s = 0;
+ if(rv != -1){+ fis = r.reply.fis;
+ if(fis[5] == 0x4f &&
+ fis[6] == 0xc2)
+ snprint(s, l, "normal");
+ else{+ snprint(s, l, "threshold exceeded");
+ rv = -1;
+ }
+ } else
+ snprint(s, l, "smart error");
+ return rv;
+}
--- /dev/null
+++ b/sys/src/cmd/disk/smart/mkfile
@@ -1,0 +1,15 @@
+</$objtype/mkfile
+
+TARG = disk/smart
+
+HFILES = smart.h
+OFILES = ata.$O scsi.$O smart.$O
+
+BIN=/$objtype/bin
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\+ ${TARG:%=/386/bin/%}\+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/disk/smart/scsi.c
@@ -1,0 +1,223 @@
+#include <u.h>
+#include <libc.h>
+#include <disk.h>
+#include <fis.h>
+#include </sys/src/cmd/scuzz/scsireq.h>
+#include "smart.h"
+
+enum{+ Replysz = 16,
+};
+
+typedef struct Rcmd Rcmd;
+struct Rcmd{+ uchar proto;
+ uchar cdbsz;
+ uchar cdb[16];
+};
+
+typedef struct Req Req;
+struct Req {+ char haverfis;
+ Rcmd cmd;
+ char sdstat[16];
+ uchar sense[0x100];
+ uchar data[0x200];
+ uint count;
+};
+
+void
+turcdb(Req *r)
+{+ uchar *cmd;
+
+ cmd = r->cmd.cdb;
+ r->cmd.cdbsz = 6;
+ r->cmd.proto = Pin;
+ memset(cmd, 0, 6);
+ r->count = 0;
+}
+
+void
+reqsensecdb(Req *r)
+{+ uchar *cmd;
+
+ cmd = r->cmd.cdb;
+ r->cmd.cdbsz = 6;
+ r->cmd.proto = Pin;
+ memset(cmd, 0, 6);
+ cmd[0] = ScmdRsense;
+ cmd[4] = 128;
+ r->count = 128;
+}
+
+static void
+sensetrace(uchar *cdb, uchar *u)
+{+ char *e;
+
+ if(1)
+ return;
+ e = scsierror(u[12], u[13]);
+ fprint(2, "sense %.2ux: %.2ux%.2ux%.2ux %s\n", cdb[0], u[2], u[12], u[13], e);
+}
+
+static int
+issuescsi(Req *r, Sdisk *d)
+{+ uchar *u;
+ int ok, rv, n;
+ Req sense;
+
+ if(write(d->fd, r->cmd.cdb, r->cmd.cdbsz) != r->cmd.cdbsz){+ eprint(d, "cdb write error: %r\n");
+ return -1;
+ }
+ werrstr("");+ switch(r->cmd.proto){+ default:
+ case Pin:
+ n = read(d->fd, r->data, r->count);
+ ok = n >= 0;
+ r->count = 0;
+ if(ok)
+ r->count = n;
+ break;
+ case Pout:
+ n = write(d->fd, r->data, r->count);
+ ok = n == r->count;
+ break;
+ }
+ rv = 0;
+ memset(r->sdstat, 0, sizeof r->sdstat);
+ if(read(d->fd, r->sdstat, Replysz) < 1){+ eprint(d, "status reply read error: %r\n");
+ return -1;
+ }
+ if(n == -1)
+ rv = -1; /* scsi not supported; don't whine */
+ else if(rv == 0 && (rv = atoi(r->sdstat)) != 0){+ memset(&sense, 0, sizeof sense);
+ reqsensecdb(&sense);
+ if(issuescsi(&sense, d) == 0){+ memmove(r->sense, sense.data, sense.count);
+ u = r->sense;
+ rv = u[2];
+ sensetrace(r->cmd.cdb, u);
+ }else
+ rv = -1;
+ }
+ return ok? rv: -1;
+}
+
+void
+modesensecdb(Req *r, uchar page, uint n)
+{+ uchar *cmd;
+
+ cmd = r->cmd.cdb;
+ r->cmd.cdbsz = 10;
+ r->cmd.proto = Pin;
+ memset(cmd, 0, 10);
+ cmd[0] = ScmdMsense10;
+ cmd[2] = page;
+ cmd[7] = n>>8;
+ cmd[8] = n;
+ r->count = n;
+}
+
+void
+modeselectcdb(Req *r, uint n)
+{+ uchar *cmd;
+
+ cmd = r->cmd.cdb;
+ r->cmd.proto = Pout;
+ r->cmd.cdbsz = 10;
+ memset(cmd, 0, 10);
+ cmd[0] = ScmdMselect10;
+ cmd[1] = 0x10; /* assume scsi2 ! */
+ cmd[7] = n>>8;
+ cmd[8] = n;
+ r->count = n;
+}
+
+int
+scsiprobe(Sdisk *d)
+{+ Req r;
+
+ memset(&r, 0, sizeof r);
+ turcdb(&r);
+ if(issuescsi(&r, d) == -1)
+ return -1;
+ memset(&r, 0, sizeof r);
+ modesensecdb(&r, 0x1c, sizeof r.data);
+ if(issuescsi(&r, d) != 0 || r.count < 8)
+ return -1;
+ return 0;
+}
+
+enum{+ /* mrie bits */
+ Mnone = 0,
+ Masync = 1, /* obs */
+ Mattn = 2, /* generate unit attention */
+ Mcrerror = 3, /* conditionally generate recovered error */
+ Mrerror = 4, /* unconditionally " */
+ Mnosense = 5, /* generate no sense */
+ Mreqonly = 6, /* report only in response to req sense */
+
+ /* byte 2 bits */
+ Perf = 1<<7, /* smart may not cause delays */
+ Ebf = 1<<5, /* enable bacground functions */
+ Ewasc = 1<<4, /* enable warnings */
+ Dexcpt = 1<<3, /* disable smart */
+ Smarttst = 1<<4, /* generate spurious smart error 5dff */
+ Logerr = 1<<0, /* enable reporting */
+};
+
+int
+scsienable(Sdisk *d)
+{+ Req r;
+
+ memset(&r, 0, sizeof r);
+ r.data[8 + 0] = 0x1c;
+ r.data[8 + 1] = 0xa;
+ r.data[8 + 2] = Ebf | Ewasc | Logerr;
+ r.data[8 + 3] = Mreqonly;
+ r.data[8 +11] = 1;
+ modeselectcdb(&r, 12 + 8);
+ if(issuescsi(&r, d) != 0)
+ return -1;
+ return 0;
+}
+
+int
+scsistatus(Sdisk *d, char *s, int l)
+{+ char *err;
+ uchar *u;
+ int rv;
+ Req r;
+
+ memset(&r, 0, sizeof r);
+ reqsensecdb(&r);
+ rv = issuescsi(&r, d);
+ if(rv == 0 && r.count > 12){+ u = r.data;
+ if(u[12] + u[13] == 0)
+ err = "normal";
+ else{+ err = scsierror(u[12], u[13]);
+ rv = -1;
+ }
+ if(err == nil)
+ err = "unknown";
+ snprint(s, l, "%s", err);
+ }else
+ snprint(s, l, "smart error");
+ return rv;
+}
--- /dev/null
+++ b/sys/src/cmd/disk/smart/smart.c
@@ -1,0 +1,236 @@
+/*
+ * smart monitoring for scsi and ata
+ * copyright © 2009 erik quanstrom
+ */
+#include <u.h>
+#include <libc.h>
+#include <fis.h>
+#include "smart.h"
+
+enum{+ Checksec = 600,
+ Opensec = 60 * 60,
+ Relogsec = 38400 / 4,
+};
+
+static Sdisk *disks;
+static Dtype dtab[] = {+ Tata, "ata", ataprobe, ataenable, atastatus,
+ Tscsi, "scsi", scsiprobe, scsienable, scsistatus,
+};
+static char *logfile = "smart";
+static int aflag;
+static int tflag;
+static int vflag;
+
+void
+eprint(Sdisk *d, char *s, ...)
+{+ char buf[256];
+ va_list arg;
+
+ va_start(arg, s);
+ vseprint(buf, buf + sizeof buf, s, arg);
+ va_end(arg);
+// syslog(0, logfile, "%s: %s", d->name, buf);
+ if(vflag)
+ fprint(2, "%s: %s", d->name, buf);
+}
+
+void
+smartlog(Sdisk *d, char *s, ...)
+{+ char buf[256];
+ va_list arg;
+
+ va_start(arg, s);
+ vseprint(buf, buf + sizeof buf, s, arg);
+ va_end(arg);
+ if(!tflag)
+ syslog(0, logfile, "%s: %s", d->name, buf);
+ if(tflag || vflag)
+ fprint(2, "%s: %s\n", d->name, buf);
+}
+
+static void
+diskclose(Sdisk *d)
+{+ close(d->fd);
+ d->fd = -1;
+}
+
+static int
+diskopen(Sdisk *d)
+{+ char buf[128];
+
+ snprint(buf, sizeof buf, "%s/raw", d->path);
+ werrstr("");+ return d->fd = open(buf, ORDWR);
+}
+
+static int
+noexist(void)
+{+ char buf[ERRMAX];
+
+ errstr(buf, sizeof buf);
+ if(strstr(buf, "exist"))
+ return -1;
+ return 0;
+}
+
+static void
+lognew(Sdisk *d)
+{+ if(aflag && !tflag)
+ smartlog(d, d->t->tname);
+}
+
+static int
+newdisk(char *s)
+{+ char buf[128], *p;
+ int i;
+ Sdisk d;
+
+ memset(&d, 0, sizeof d);
+ snprint(d.path, sizeof d.path, "%s", s);
+ if(p = strrchr(s, '/'))
+ p++;
+ else
+ p = s;
+ snprint(d.name, sizeof d.name, "%s", p);
+ snprint(buf, sizeof buf, "%s/raw", s);
+ if(diskopen(&d) == -1)
+ return noexist();
+ for(i = 0; i < nelem(dtab); i++)
+ if(dtab[i].probe(&d) == 0)
+ if(dtab[i].enable(&d) == 0){+ d.t = dtab + i;
+ lognew(&d);
+ break;
+ }
+ diskclose(&d);
+ if(d.t != 0){+ d.next = disks;
+ disks = malloc(sizeof d);
+ memmove(disks, &d, sizeof d);
+ }
+ return 0;
+}
+
+static int
+probe0(char *s, int l)
+{+ char *p, *f[3], buf[16];
+ int i;
+
+ s[l] = 0;
+ for(; p = strchr(s, '\n'); s = p + 1){+ if(tokenize(s, f, nelem(f)) < 1)
+ continue;
+ for(i = 0; i < 0x10; i++){+ snprint(buf, sizeof buf, "/dev/%s%ux", f[0], i);
+ if(newdisk(buf) == -1 && i > 2)
+ break;
+ }
+ }
+ return -1;
+}
+
+int
+probe(void)
+{+ char *s;
+ int fd, l, r;
+
+ fd = open("/dev/sdctl", OREAD);+ if(fd == -1)
+ return -1;
+ r = -1;
+ l = 1024; /* #S/sdctl has 0 size; guess */
+ if(s = malloc(l + 1))
+ if((l = read(fd, s, l)) > 0)
+ r = probe0(s, l);
+ free(s);
+ close(fd);
+ return r;
+}
+
+void
+run(void)
+{+ char buf[1024];
+ int e, s0;
+ uvlong t, t0;
+ Sdisk *d;
+
+ e = 0;
+ t = time(0);
+ for(d = disks; d; d = d->next){+ t0 = d->lastcheck;
+ if(t0 != 0 && t - t0 < Checksec)
+ continue;
+ if(diskopen(d) == -1){+ if(t - t0 > Opensec)
+ smartlog(d, "can't open in %ullds\n", t - t0);
+ continue;
+ }
+ s0 = d->status;
+ d->status = d->t->status(d, buf, sizeof buf);
+ diskclose(d);
+ if(d->status == -1)
+ e++;
+ if((aflag || d->status != s0 || d->status != 0) && !d->silent){+ t0 = d->lastlog;
+ if(t0 == 0 || t - t0 >= Relogsec){+ smartlog(d, buf);
+ d->lastlog = t;
+ }
+ }else
+ d->lastlog = 0;
+ d->lastcheck = t;
+ }
+ if(tflag)
+ exits(e? "smart errors": "");
+}
+
+void
+usage(void)
+{+ fprint(2, "usage: disk/smart [-aptv] [/dev/sdXX] ...\n");
+ exits("usage");+}
+
+void
+main(int argc, char **argv)
+{+ int pflag;
+
+ pflag = 0;
+ ARGBEGIN{+ case 'a':
+ aflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 't':
+ tflag = 1;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ for(; *argv; argv++)
+ newdisk(*argv);
+ if(argc == 0 || pflag)
+ probe();
+ if(disks == nil)
+ sysfatal("no disks");+ for(;; sleep(30*1000))
+ run();
+}
--- /dev/null
+++ b/sys/src/cmd/disk/smart/smart.h
@@ -1,0 +1,44 @@
+enum {+ Tscsi = 1,
+ Tata = 2,
+
+ Sok = 0,
+ Ssoon = 1,
+ Sfail = 2,
+
+ Nrb = 32,
+ Pathlen = 256,
+};
+
+typedef struct Dtype Dtype;
+typedef struct Sdisk Sdisk;
+
+struct Dtype {+ int type;
+ char *tname;
+ int (*probe)(Sdisk*);
+ int (*enable)(Sdisk*);
+ int (*status)(Sdisk*, char*, int);
+};
+
+struct Sdisk {+ Sdisk *next;
+ Dtype *t;
+ int fd;
+ Sfis;
+ char path[Pathlen];
+ char name[28];
+ char status;
+ uchar silent;
+ uvlong lastcheck;
+ uvlong lastlog;
+};
+
+int scsiprobe(Sdisk*);
+int scsienable(Sdisk*);
+int scsistatus(Sdisk*, char*, int);
+int ataprobe(Sdisk*);
+int ataenable(Sdisk*);
+int atastatus(Sdisk*, char*, int);
+
+void eprint(Sdisk*, char *, ...);
--
⑨