shithub: riscv

Download patch

ref: 2a9d7025069d9bc472cd3883ce5bf675e78d5948
parent: fa7fb8b66b9ff50029532d09315f03896f2ac4c4
author: google <[email protected]>
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 *, ...);