878 lines
24 KiB
C
878 lines
24 KiB
C
#include "fx.h"
|
|
#include <math.h> /* for random() */
|
|
|
|
int exercising; /* for callsub() and gread() */
|
|
|
|
static stop_on_err, showdiffs;
|
|
|
|
unchar *extrack;
|
|
unchar *chktrack;
|
|
|
|
struct err {
|
|
uint hits[4];
|
|
uint drive;
|
|
daddr_t bn;
|
|
};
|
|
|
|
#define MAX_ERRBLOCKS 100 /* max errors we keep in log */
|
|
#define TOOMANYCONSEC 10 /* max consecutive errors before complaining */
|
|
|
|
static struct exercise {
|
|
char modifier;
|
|
char aflag;
|
|
char iflag, lflag;
|
|
daddr_t firstbn, lastbn;
|
|
void (*stepper)(void);
|
|
int passes;
|
|
|
|
int rsize;
|
|
unchar *buf;
|
|
int bufsize;
|
|
daddr_t curbn;
|
|
int phase;
|
|
|
|
struct err eb[MAX_ERRBLOCKS];
|
|
uint neb;
|
|
} E;
|
|
|
|
static void showdiff(unchar *dbuf, unchar *cmpbuf, uint cnt);
|
|
static scanchunk(daddr_t bn, int sectors);
|
|
static errcompar(const void *a, const void *b);
|
|
static int exrun(void);
|
|
|
|
extern int nomenus; /* see fx.c, -c option */
|
|
|
|
# define MXR 001 /* read */
|
|
# define MXW 002 /* write */
|
|
# define MXS 004 /* seek */
|
|
# define MXC 010 /* compare */
|
|
# define MAX_TESTPAT MAX_BSIZE
|
|
static
|
|
int TestPhase = 0, TestCycle = 3;
|
|
|
|
/* a prime # is best, with a non-repeating pattern within the prime. Ideally,
|
|
* the pattern should be > than one sector in length, so that if the drive
|
|
* or controller returns the wrong sector sometimes, the error is likely
|
|
* to be detected; otherwise every sector contains the same pattern!
|
|
* We use 1031, since we may go to 1024 byte sectors; some drives already
|
|
* support that for raw accesses. This pattern was generated by a
|
|
* program using random(), with the first 3 bytes replaced by the old
|
|
* pattern.
|
|
*/
|
|
unchar defpat[1031] = {
|
|
0xdb, 0x6d, 0xb6, 0xbe, 0x7f, 0x51, 0x96, 0x0b, 0xcf, 0x9e, 0xdb, 0x2b,
|
|
0x61, 0xf0, 0x6f, 0x0f, 0xeb, 0x5a, 0x38, 0xb6, 0x48, 0x01, 0x02, 0xa4,
|
|
0xbf, 0x89, 0xa9, 0x9b, 0xa6, 0xbb, 0x5c, 0x63, 0xbd, 0xf8, 0x22, 0x3d,
|
|
0x49, 0xb8, 0x48, 0x18, 0x56, 0x23, 0x43, 0xb7, 0x13, 0xb2, 0xc7, 0xff,
|
|
0x0c, 0xff, 0xb5, 0x54, 0x00, 0xb8, 0xf8, 0xbf, 0x41, 0xa2, 0x5b, 0xe7,
|
|
0x5d, 0xb7, 0x4b, 0x1b, 0xaf, 0x6d, 0x58, 0xf8, 0x25, 0xa0, 0x11, 0x7c,
|
|
0xc4, 0x54, 0x33, 0xd7, 0x07, 0xfa, 0xd6, 0x13, 0xf9, 0x8c, 0x68, 0xfa,
|
|
0x44, 0x60, 0xb9, 0x85, 0x02, 0x14, 0x6d, 0x60, 0xcc, 0xb8, 0x7b, 0x7b,
|
|
0x25, 0xd3, 0x74, 0x4a, 0x73, 0x85, 0xc6, 0x37, 0xd9, 0xfa, 0x0f, 0xe0,
|
|
0xf4, 0xe5, 0xf4, 0xee, 0x71, 0x5c, 0xe8, 0xb5, 0xbc, 0xa1, 0x3b, 0xbf,
|
|
0xb6, 0xa8, 0x1f, 0x82, 0x60, 0x9a, 0xfd, 0x85, 0x6d, 0x71, 0xcf, 0xe0,
|
|
0xf6, 0x96, 0x18, 0xd0, 0x90, 0x27, 0xb0, 0x84, 0x0c, 0xa4, 0x72, 0x7e,
|
|
0x00, 0x5a, 0x33, 0xbd, 0xfc, 0x6e, 0x7c, 0xb2, 0x16, 0x9b, 0x34, 0x76,
|
|
0x35, 0x31, 0xfb, 0xa2, 0xa3, 0xcb, 0x82, 0x99, 0x61, 0x9a, 0x69, 0xf1,
|
|
0xc1, 0x1a, 0x75, 0xce, 0xbe, 0xe8, 0x4c, 0xbf, 0x42, 0x7f, 0x7c, 0x3e,
|
|
0xee, 0xf8, 0xf0, 0x04, 0x93, 0x24, 0x7b, 0xc8, 0x56, 0x76, 0x6a, 0xf9,
|
|
0x41, 0xec, 0x92, 0xa2, 0x87, 0xfc, 0x93, 0x48, 0x16, 0x09, 0x16, 0xd4,
|
|
0xf1, 0x62, 0x93, 0x33, 0xe2, 0x0f, 0x72, 0xd0, 0x07, 0x62, 0xd4, 0x9a,
|
|
0x87, 0x4f, 0x62, 0xdd, 0xc6, 0xcc, 0xd6, 0x07, 0xb9, 0x68, 0xaa, 0x40,
|
|
0x64, 0x3d, 0x88, 0x7a, 0x46, 0x9f, 0x4f, 0x37, 0x01, 0xe2, 0x6b, 0xe3,
|
|
0xf2, 0xdd, 0xb3, 0xf9, 0x3f, 0x88, 0x94, 0xc6, 0xd7, 0xf6, 0xa3, 0x9d,
|
|
0xc3, 0x79, 0xa5, 0x7c, 0xe2, 0x4f, 0xbc, 0x46, 0x8c, 0x44, 0xc1, 0xd3,
|
|
0xe3, 0x10, 0x0a, 0xe5, 0xf2, 0x75, 0xc8, 0xe4, 0x52, 0x7c, 0xde, 0x92,
|
|
0x04, 0x72, 0x58, 0xdb, 0x68, 0xfc, 0x79, 0x2b, 0x75, 0x1e, 0xa7, 0x57,
|
|
0x6d, 0x63, 0x9e, 0xf9, 0xa8, 0x5f, 0xcc, 0x8b, 0x6f, 0xd7, 0x70, 0x61,
|
|
0x4c, 0x39, 0x46, 0x9f, 0xb5, 0x24, 0x31, 0xb9, 0x96, 0x89, 0x94, 0xfe,
|
|
0x85, 0x0d, 0x2a, 0xfb, 0x2b, 0xd1, 0x52, 0x98, 0x35, 0xf0, 0x92, 0xdd,
|
|
0x4f, 0x5e, 0x68, 0xbe, 0x35, 0xd9, 0x20, 0x82, 0x12, 0x66, 0x21, 0xc7,
|
|
0x8a, 0x52, 0x80, 0x20, 0xdb, 0x14, 0x1e, 0x61, 0x22, 0x48, 0x5c, 0x4d,
|
|
0x1a, 0xae, 0xe6, 0x4f, 0x9f, 0x78, 0x2c, 0xee, 0xd6, 0x94, 0xad, 0x0c,
|
|
0x6d, 0xcd, 0x8e, 0x7f, 0x33, 0xaf, 0x46, 0xbd, 0x01, 0xc6, 0xdd, 0xdc,
|
|
0xdb, 0xfb, 0x3d, 0xfd, 0x44, 0x99, 0x4a, 0x5e, 0x48, 0x30, 0xad, 0xe7,
|
|
0xa8, 0xd9, 0xd5, 0x7f, 0x6d, 0x82, 0x8b, 0xdb, 0x4f, 0x19, 0x5a, 0x82,
|
|
0xc8, 0xa1, 0x3f, 0xc9, 0x67, 0x1c, 0xa5, 0x42, 0x18, 0xe3, 0x3f, 0x5c,
|
|
0x7c, 0x8a, 0xba, 0xc4, 0xba, 0x67, 0xab, 0x63, 0x40, 0x81, 0xe2, 0xad,
|
|
0x03, 0x6d, 0x88, 0x53, 0x86, 0xe3, 0xd5, 0x4e, 0x84, 0x15, 0x17, 0xeb,
|
|
0x31, 0xbc, 0x2e, 0x49, 0x9f, 0x6d, 0xa5, 0x1c, 0xf7, 0x5f, 0xe0, 0xb2,
|
|
0xc6, 0x8c, 0x15, 0x06, 0x0d, 0xf7, 0xb4, 0x10, 0x64, 0x3c, 0x63, 0xea,
|
|
0x1f, 0x39, 0x38, 0xa3, 0x4e, 0x4f, 0x8f, 0x7f, 0x0b, 0xbd, 0xc9, 0xab,
|
|
0x2a, 0x6e, 0xc7, 0x22, 0xce, 0xa7, 0xd4, 0x94, 0x33, 0xe9, 0x9b, 0x40,
|
|
0xe0, 0x4f, 0x51, 0x44, 0x8b, 0xb4, 0x2e, 0xab, 0xed, 0x66, 0x4e, 0x3b,
|
|
0xb5, 0xdd, 0xbb, 0xc0, 0x9a, 0x84, 0x6b, 0xc5, 0xf2, 0x32, 0xe7, 0xc0,
|
|
0xda, 0xbb, 0x55, 0x0d, 0xa4, 0xf0, 0x4e, 0x84, 0x3f, 0x9f, 0xc8, 0xca,
|
|
0x53, 0xf6, 0x75, 0x41, 0x5c, 0xc4, 0x7c, 0x11, 0xa1, 0x37, 0xd1, 0x3c,
|
|
0xbb, 0x3d, 0x01, 0xae, 0x6f, 0xe8, 0x6e, 0x49, 0xa3, 0xc3, 0x57, 0x47,
|
|
0xb3, 0xa5, 0xcb, 0xf2, 0x44, 0x93, 0xbd, 0x97, 0x89, 0x32, 0xd8, 0xe5,
|
|
0xf6, 0x55, 0xf6, 0x98, 0x8c, 0xc7, 0xd4, 0x48, 0x04, 0xd5, 0xf6, 0x74,
|
|
0xbd, 0x64, 0xbd, 0x60, 0x28, 0x14, 0xa7, 0xdb, 0xb9, 0x72, 0xce, 0xfd,
|
|
0x05, 0x8b, 0x95, 0x8e, 0xbd, 0x6d, 0x73, 0xb4, 0xc2, 0x69, 0x4c, 0x4f,
|
|
0x30, 0x20, 0x97, 0x35, 0xf5, 0x8d, 0xa9, 0xb2, 0xf1, 0x66, 0x12, 0x19,
|
|
0x7b, 0xb9, 0xf5, 0x34, 0x2b, 0xc3, 0x32, 0x30, 0x4e, 0xc7, 0xbe, 0x0b,
|
|
0x34, 0x31, 0xbf, 0xf7, 0x9a, 0x0b, 0x46, 0xca, 0x2b, 0xdd, 0xff, 0x20,
|
|
0x6a, 0xa8, 0xd2, 0x5b, 0x0f, 0xe4, 0x75, 0x8a, 0x9d, 0x6a, 0xbe, 0xc8,
|
|
0x2d, 0xf0, 0xf8, 0x7b, 0xb7, 0xb6, 0x86, 0xec, 0xe7, 0x46, 0xe3, 0x81,
|
|
0x51, 0x29, 0x4c, 0x7d, 0x06, 0x4b, 0x9d, 0x70, 0xf4, 0x70, 0xcb, 0x03,
|
|
0x54, 0x40, 0x8d, 0xf2, 0xaa, 0x4b, 0xba, 0xd7, 0x3c, 0xb3, 0x52, 0xf3,
|
|
0x69, 0xd9, 0xdf, 0x51, 0x1f, 0xc2, 0xd2, 0x70, 0xeb, 0x1e, 0xed, 0xf1,
|
|
0x6a, 0x8b, 0x61, 0x5e, 0xfb, 0x2d, 0x61, 0x4f, 0x6d, 0xee, 0x41, 0x18,
|
|
0x39, 0xfc, 0xef, 0x75, 0xaf, 0x42, 0x69, 0x18, 0x1b, 0x48, 0x69, 0x3a,
|
|
0x0b, 0x3c, 0xaa, 0xf6, 0x5a, 0x98, 0xe8, 0xc4, 0x23, 0x49, 0x22, 0x1e,
|
|
0x76, 0x83, 0x6d, 0xe4, 0x71, 0xaf, 0xfc, 0xab, 0xab, 0xeb, 0x20, 0x5a,
|
|
0x2d, 0x89, 0x72, 0x48, 0xd2, 0xdc, 0x82, 0xdd, 0x18, 0x2d, 0xd3, 0x72,
|
|
0xc5, 0xbb, 0x37, 0xe8, 0x05, 0x59, 0x06, 0x7b, 0xdd, 0x73, 0x5f, 0x4e,
|
|
0x22, 0x5b, 0xf9, 0xcd, 0x47, 0x1a, 0x27, 0x74, 0xa3, 0x9a, 0xbd, 0x75,
|
|
0x76, 0x3f, 0x52, 0x8e, 0x6c, 0x26, 0x00, 0x31, 0xe1, 0x37, 0x19, 0xe6,
|
|
0x91, 0x1f, 0x62, 0x6e, 0x93, 0xc1, 0xbc, 0xb5, 0x1d, 0xb6, 0x83, 0x64,
|
|
0xd0, 0xaa, 0xd8, 0x73, 0x44, 0x95, 0xe9, 0xba, 0xd5, 0x3b, 0x48, 0x41,
|
|
0x61, 0x49, 0x73, 0x43, 0x80, 0x8c, 0x29, 0x11, 0xac, 0x8b, 0x7f, 0x3f,
|
|
0x4d, 0x3c, 0xf4, 0x6a, 0xf2, 0x77, 0xce, 0xc2, 0x22, 0xa6, 0x35, 0x66,
|
|
0x3c, 0x1e, 0x21, 0x11, 0x5a, 0x69, 0x52, 0xbb, 0xb2, 0xc5, 0xfe, 0x33,
|
|
0x52, 0x28, 0x44, 0xfe, 0xb3, 0xc4, 0x3d, 0x00, 0x00, 0x31, 0x6a, 0xf2,
|
|
0xa9, 0x38, 0xb4, 0xcb, 0xdf, 0xe9, 0x31, 0x1b, 0x08, 0x52, 0x2c, 0x62,
|
|
0xbc, 0x7e, 0x1d, 0x6e, 0x44, 0x1c, 0xa1, 0x96, 0x44, 0xe6, 0x94, 0xf7,
|
|
0xaa, 0xd1, 0xf8, 0xaa, 0x02, 0x62, 0x9c, 0xab, 0x9b, 0x50, 0x76, 0x7a,
|
|
0x39, 0xa8, 0x95, 0x41, 0xfa, 0xc1, 0xa3, 0xb6, 0x3f, 0xc1, 0x25, 0x83,
|
|
0xdd, 0xc6, 0x19, 0x21, 0xac, 0xad, 0x18, 0x56, 0x7e, 0x10, 0x00, 0x81,
|
|
0x73, 0x9c, 0x2c, 0x0e, 0xec, 0xa3, 0x88, 0x26, 0x4b, 0x1d, 0x67, 0x45,
|
|
0xde, 0x0b, 0xfc, 0x1d, 0xcc, 0x21, 0xa1, 0xa9, 0xe7, 0xba, 0xca, 0x94,
|
|
0x68, 0xe2, 0xea, 0xe6, 0xf3, 0xeb, 0x67, 0x66, 0x87, 0x94, 0x74, 0x74,
|
|
0x37, 0xfc, 0x9a, 0x82, 0x19, 0x01, 0xc7, 0xf7, 0x0c, 0xc3, 0x14, 0xd8,
|
|
0xe4, 0xb5, 0x81, 0xcc, 0x70, 0x4b, 0x60, 0xd8, 0x2e, 0x4a, 0xbe, 0x21,
|
|
0x35, 0x26, 0x87, 0xbd, 0xba, 0xfb, 0x31, 0xf1, 0xf7, 0xcb, 0x73, 0x10,
|
|
0xcc, 0x3a, 0x07, 0xd9, 0xfe, 0x1b, 0xb1, 0xe2, 0xd1, 0x33, 0xae, 0x41,
|
|
0x7e, 0x0e, 0x19, 0xac, 0x59, 0xd7, 0xcd, 0x8e, 0xfd, 0x54, 0x4b, 0xb7,
|
|
0x4f, 0x7c, 0xa8, 0x46, 0x47, 0x1b, 0x56, 0x14, 0x56, 0x5d, 0xed, 0x54,
|
|
0x79, 0x9e, 0x36, 0x4a, 0xd1, 0xe5, 0x8b, 0x50, 0xf3, 0xa4, 0xfc
|
|
};
|
|
|
|
size_t defpatsz = sizeof(defpat);
|
|
|
|
/* TestPat is set to deftest during fx startup */
|
|
unsigned char DefTestPat[MAX_TESTPAT];
|
|
unsigned char *TestPat;
|
|
|
|
/*
|
|
* compute next block number for sequential exercise
|
|
*/
|
|
void
|
|
seqstepper(void)
|
|
{
|
|
E.curbn += E.rsize;
|
|
}
|
|
|
|
/*
|
|
* sequential exercise
|
|
*/
|
|
void
|
|
sequential_func(void)
|
|
{
|
|
exargs();
|
|
E.stepper = seqstepper;
|
|
(void)exrun();
|
|
}
|
|
|
|
/*
|
|
* compute next block number for butterfly exercise
|
|
*/
|
|
void
|
|
bflystepper(void)
|
|
{
|
|
E.curbn = E.lastbn - (E.curbn - E.firstbn);
|
|
if (E.curbn >= (E.lastbn + E.firstbn) / 2)
|
|
E.curbn -= E.rsize;
|
|
}
|
|
|
|
/*
|
|
* butterfly exercise
|
|
*/
|
|
void
|
|
butterfly_func(void)
|
|
{
|
|
exargs();
|
|
E.stepper = bflystepper;
|
|
(void)exrun();
|
|
}
|
|
|
|
/*
|
|
* random number generator
|
|
* The old one was *very* non-random with some values (a range of 500000
|
|
* passed to genrand 100000 times generated 38 unique numbers!)
|
|
* Now we use the random() function from BSD, with the defaults
|
|
*/
|
|
static int
|
|
genrand(long range)
|
|
{
|
|
if (range <= 0)
|
|
return 0;
|
|
return random() % range;
|
|
}
|
|
|
|
/*
|
|
* compute next block number for random exercise
|
|
*/
|
|
void
|
|
randstepper(void)
|
|
{
|
|
E.curbn = E.firstbn + genrand(E.lastbn - E.firstbn);
|
|
}
|
|
|
|
/*
|
|
* random exercise
|
|
*/
|
|
void
|
|
random_func(void)
|
|
{
|
|
exargs();
|
|
E.stepper = randstepper;
|
|
(void)exrun();
|
|
}
|
|
|
|
/*
|
|
* complete exercise
|
|
*/
|
|
void
|
|
complete_func(void)
|
|
{
|
|
completeargs();
|
|
E.stepper = seqstepper;
|
|
if(exrun()) {
|
|
E.stepper = bflystepper;
|
|
E.aflag = 1;
|
|
exrun();
|
|
}
|
|
/* else aborted */
|
|
}
|
|
|
|
/*
|
|
* get arguments for complete exercise
|
|
*/
|
|
void
|
|
completeargs(void)
|
|
{
|
|
E.modifier = MXC | MXW;
|
|
if(nomenus)
|
|
E.aflag = 1;
|
|
else
|
|
checkflags("a", &E.aflag);
|
|
E.firstbn = 0;
|
|
E.lastbn = E.firstbn + exarea(E.firstbn);
|
|
E.passes = 1;
|
|
|
|
switch (drivernum) {
|
|
#ifdef SMFD_NUMBER
|
|
case SMFD_NUMBER:
|
|
E.rsize = 16; /* arbitrary, but "small"; some "floppies"
|
|
aren't, but that's OK, they are still small */
|
|
break;
|
|
#endif /* SMFD_NUMBER */
|
|
case SCSI_NUMBER:
|
|
E.rsize = 256; /* arbitrary, but "large" */
|
|
break;
|
|
|
|
default:
|
|
printf("Don't know how to exercise disk for driver type %u\n",
|
|
drivernum);
|
|
return;
|
|
}
|
|
argcheck();
|
|
}
|
|
|
|
uint ebwidth = 35;
|
|
|
|
/* ix not used, but colprint passes it */
|
|
/*ARGSUSED*/
|
|
static void
|
|
ebsub(int ix, struct err *t, char *tgt)
|
|
{
|
|
{
|
|
sprintf(tgt, "%llu r%u(%u) w%u(%u)",
|
|
t->bn,
|
|
t->hits[0], t->hits[1],
|
|
t->hits[2], t->hits[3]);
|
|
}
|
|
}
|
|
|
|
/* print the error log */
|
|
void
|
|
printerr(void)
|
|
{
|
|
fxsort((char *) E.eb, E.neb, sizeof *E.eb, errcompar);
|
|
setoff("error summary");
|
|
printf("(bn) ");
|
|
printf(" rretries(rfail) wretries/wfail\n");
|
|
colprint(E.eb, E.neb, sizeof *E.eb, ebwidth, ebsub);
|
|
}
|
|
|
|
|
|
/*
|
|
* sort the error log
|
|
*/
|
|
static
|
|
errcompar(const void *a, const void *b)
|
|
{
|
|
return ((struct err *)a)->bn - ((struct err *)b)->bn;
|
|
}
|
|
/*
|
|
* menu item to manage the error log
|
|
*/
|
|
void
|
|
errlog_func(void)
|
|
{
|
|
checkflags("il", &E.iflag);
|
|
if (E.lflag)
|
|
printerr();
|
|
errlog_summary();
|
|
if (E.iflag)
|
|
E.neb = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* toggle between stopping on errors, or doing the 'normal'/old
|
|
* behavior of sparing and continuing.
|
|
*/
|
|
void
|
|
stop_on_error_func(void)
|
|
{
|
|
stop_on_err = !stop_on_err;
|
|
printf("Will %sstop on errors\n", stop_on_err ? "" : "not ");
|
|
}
|
|
|
|
|
|
/*
|
|
* toggle between showing diffs on miscompares, or just reporting it
|
|
* and retrying.
|
|
*/
|
|
void
|
|
miscompares_func(void)
|
|
{
|
|
showdiffs = !showdiffs;
|
|
printf("Will %sshow differences on miscompares\n", showdiffs ? "" : "not ");
|
|
}
|
|
|
|
|
|
/* ----- */
|
|
/* ----- testpat subroutines ----- */
|
|
/*
|
|
* menu item to interactively change the test pattern
|
|
*/
|
|
void
|
|
settestpat_func(void)
|
|
{
|
|
uint val;
|
|
register i;
|
|
printf("Enter .. to finish; CR keeps current value\n");
|
|
for(i = 0; i < MAX_TESTPAT;) {
|
|
argnum(&val, (uint)DefTestPat[i], 0x100, "value");
|
|
DefTestPat[i++] = val;
|
|
/* has to be inside loop, because loop is exitted via .. */
|
|
SetTestPat(DefTestPat, i);
|
|
}
|
|
}
|
|
/*
|
|
* menu item to print the current test pattern
|
|
*/
|
|
void
|
|
showtestpat_func(void)
|
|
{
|
|
register int n;
|
|
argcheck();
|
|
printf("test pattern:");
|
|
for (n = 0; n < TestCycle; n++) {
|
|
if((n%0x18) == 0)
|
|
printf("\n%3x: ", n);
|
|
printf(" %02x", TestPat[n]);
|
|
}
|
|
newline();
|
|
}
|
|
|
|
|
|
/*
|
|
* initialize exercise globals
|
|
*/
|
|
void
|
|
init_ex(void)
|
|
{
|
|
bzero((char *) &E, sizeof E);
|
|
SetTestPat(defpat, defpatsz);
|
|
bcopy(defpat, DefTestPat, sizeof defpat);
|
|
}
|
|
|
|
/*
|
|
* get arguments for sequential, random, and butterfly exercises
|
|
*/
|
|
ITEM exmod_items[] =
|
|
{
|
|
{"rd-only", 0, 0},
|
|
{"ro-cmp", 1, 0},
|
|
{"seek", 2, 0},
|
|
{"wr-only", 3, 1},
|
|
{"wr-cmp", 4, 1},
|
|
{0}
|
|
};
|
|
|
|
|
|
MENU exmod_menu = {
|
|
exmod_items, "exercise modifiers"};
|
|
char exmod_bits[] = {
|
|
MXR, MXC | MXR, MXS | MXR, MXW, MXC | MXW};
|
|
void
|
|
exargs(void)
|
|
{
|
|
daddr_t startbn, nblocks, lastbn;
|
|
uint nscans, defscans;
|
|
int n;
|
|
|
|
checkflags("a", &E.aflag);
|
|
argchoice(&n, 0, &exmod_menu, "modifier");
|
|
E.modifier = exmod_bits[n];
|
|
defscans = 1;
|
|
|
|
switch (drivernum) {
|
|
|
|
#ifdef SMFD_NUMBER
|
|
case SMFD_NUMBER:
|
|
E.rsize = 16; /* arbitrary, but "small"; some "floppies"
|
|
aren't, but that's OK, they are still small */
|
|
break;
|
|
#endif /* SMFD_NUMBER */
|
|
case SCSI_NUMBER:
|
|
E.rsize = 256; /* arbitrary, but "large" */
|
|
break;
|
|
|
|
default:
|
|
printf("Don't know how to exercise disk for driver type %u\n",
|
|
drivernum);
|
|
return;
|
|
}
|
|
|
|
/* used to calculate lastbn here, but that could be larger than
|
|
* what exarea returns, leading to case where we accept a larger
|
|
* max value for starting block than number of blocks. So just
|
|
* use exarea to get lastbn. Also, start block has to be at least
|
|
* 1 less than lastblock for there to be anything to do...
|
|
*/
|
|
lastbn = exarea(0);
|
|
argbn(&startbn, (daddr_t) 0, lastbn-1, "starting block#");
|
|
nblocks = lastbn - startbn;
|
|
argbn(&nblocks, nblocks, nblocks + 1, "nblocks");
|
|
argnum(&nscans, defscans, 1000000, "nscans");
|
|
argcheck();
|
|
E.firstbn = startbn;
|
|
E.lastbn = startbn + nblocks;
|
|
E.passes = nscans;
|
|
if (!(E.modifier & (MXR | MXW | MXS)))
|
|
E.modifier = MXR | MXW;
|
|
if (E.modifier & MXS)
|
|
E.modifier = (MXR | MXS), E.rsize = 1;
|
|
}
|
|
|
|
/*
|
|
* run an exercise, called from main exercise routines.
|
|
* return 1 if all OK, 0 if it was aborted.
|
|
*/
|
|
static int
|
|
exrun(void)
|
|
{
|
|
int passno;
|
|
|
|
(void) setintr(1);
|
|
if ((E.modifier & MXW) && !E.aflag)
|
|
lastchance();
|
|
|
|
/* allow for an interrupted exercise pass */
|
|
if(extrack) free(extrack);
|
|
if(chktrack) free(chktrack);
|
|
|
|
E.bufsize = E.rsize * DP(&vh)->dp_secbytes;
|
|
E.buf = extrack = malloc(E.bufsize);
|
|
if(!extrack) {
|
|
printf("Can't allocate memory for i/o buffer\n");
|
|
return 0;
|
|
}
|
|
if(E.modifier & MXC) {
|
|
chktrack = malloc(E.bufsize);
|
|
if(!chktrack) {
|
|
printf("Can't allocate memory for comparison buffer\n");
|
|
return 0;
|
|
}
|
|
}
|
|
exercising = 1;
|
|
|
|
for (passno = 0; passno < E.passes; passno++) {
|
|
printf("%s ", (E.stepper == seqstepper ? "sequential" :
|
|
(E.stepper == bflystepper ? "butterfly" : "random")));
|
|
/* casts necessary because 32 bit standalone has 32 bit
|
|
* daddr_t; See bug #524200. */
|
|
printf("pass %d: scanning [%llu, %llu]",
|
|
passno+1, (long long)E.firstbn, (long long)E.lastbn);
|
|
if(drivernum == SCSI_NUMBER)
|
|
printf(" (%lld blocks)", (long long)(E.lastbn-E.firstbn));
|
|
printf("\n");
|
|
SetTestPhase(passno);
|
|
TestPatFill(E.buf, E.bufsize);
|
|
E.phase = passno;
|
|
E.curbn = E.firstbn;
|
|
if (scanpass())
|
|
break; /* user abort, etc. */
|
|
if (E.neb > 0)
|
|
errlog_summary();
|
|
}
|
|
exercising = 0;
|
|
if(chktrack) free(chktrack);
|
|
free(extrack);
|
|
extrack = chktrack = 0;
|
|
return passno == E.passes; /* if not equal, we aborted */
|
|
}
|
|
|
|
|
|
/* progress report, for exercising. print a numeric value for every
|
|
* dotnumfreq dots, and a dot for every percent.
|
|
*
|
|
* For some i/o size vs total combos, we may be doing more than 1% / call
|
|
* (currently this happens for floppies, and disks of less than 12.8MB (yeah,
|
|
* right...), and also if only a small section of the disk is being exercised.
|
|
* For these cases, we simply print fewer than 100 dots, and possibly not
|
|
* all the 10% numerics. The "last10" hack helps this a bit.
|
|
*
|
|
* totsectors is only used on initialization call.
|
|
*/
|
|
|
|
static void
|
|
dot(uint sectors, uint totsectors)
|
|
{
|
|
static uint ncols, curcol, pctintvl, lastpct, runtotal;
|
|
uint last10;
|
|
|
|
if(!sectors) { /* initialize */
|
|
runtotal = lastpct = 0;
|
|
pctintvl = (99+totsectors)/100;
|
|
/* -1 because some terminals wrap at last column
|
|
* don't worry about it changing during the run. */
|
|
ncols = getscreenwidth() - 1;
|
|
printf("0%%");
|
|
flushoutp();
|
|
curcol = 2;
|
|
return;
|
|
}
|
|
else if( sectors == (uint)-1) { /* end of pass */
|
|
if(lastpct != 100) {
|
|
if((curcol+5) >= ncols)
|
|
newline();
|
|
printf(" 100%%");
|
|
}
|
|
newline();
|
|
return;
|
|
}
|
|
|
|
runtotal += sectors;
|
|
|
|
last10 = lastpct/10;
|
|
if((runtotal/pctintvl) > lastpct) {
|
|
lastpct = (runtotal/pctintvl);
|
|
if(++curcol >= ncols) {
|
|
newline();
|
|
curcol = 0;
|
|
}
|
|
printf(".");
|
|
if(last10 != (lastpct/10)) {
|
|
char nbuf[5];
|
|
size_t slen;
|
|
|
|
sprintf(nbuf, "%u%%", lastpct);
|
|
slen = strlen(nbuf);
|
|
curcol += slen;
|
|
/* if window is really small, don't do extra newline */
|
|
if(curcol >= ncols && slen < ncols) {
|
|
newline();
|
|
curcol = slen-1;
|
|
}
|
|
printf("%s", nbuf);
|
|
}
|
|
flushoutp();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* do one pass of an exercise Keep track of consecutive errors; if we
|
|
* see a number of sequential errors it's likely that there's a drive
|
|
* hardware problem. Used to check for more than a cyls worth, but that
|
|
* doesn't work well on SCSI where we do a cyl at a time, or on drives
|
|
* with a small number of heads.
|
|
* Warn the user & give a chance to abort. To that end, return 0
|
|
* normally, 1 if aborting.
|
|
*/
|
|
scanpass(void)
|
|
{
|
|
int steps, sectors, userabort;
|
|
int consec_errs=0;
|
|
|
|
dot(0, E.lastbn - E.firstbn);
|
|
sectors = E.rsize;
|
|
for (steps = (E.lastbn - E.firstbn + E.rsize - 1) / E.rsize; --steps >= 0;) {
|
|
dot(sectors,0);
|
|
if(drivernum == SCSI_NUMBER && E.stepper == randstepper) {
|
|
/* SCSI does cyl at a time i/o; which means it doesn't
|
|
* do as many random seeks as non-SCSI. To enhance the
|
|
* randomness, we therefore do a random number of
|
|
* sectors, and select a new base block each time.
|
|
* May not due quite as many blocks of i/o if many
|
|
* errors, but doesn't much matter in that case.
|
|
* Done here so instead of change E.rsize so that
|
|
* we still get one '.' per "cylinder" of i/o. */
|
|
int rsecs, secs;
|
|
for(rsecs=sectors; rsecs>0; rsecs-=secs) {
|
|
secs = genrand(sectors);
|
|
if(secs == 0)
|
|
secs = 1;
|
|
if(secs > rsecs)
|
|
secs = rsecs;
|
|
if((E.curbn+secs) > E.lastbn)
|
|
secs = E.lastbn-E.curbn;
|
|
if(secs && (userabort = scanchunk(E.curbn, secs)))
|
|
break;
|
|
consec_errs = 0;
|
|
randstepper();
|
|
}
|
|
}
|
|
else {
|
|
int secs = sectors;
|
|
/* can't change 'sectors', or dot() gets wrong arg
|
|
* when doing random i/o (or perhaps butterfly in some
|
|
* cases); that mess up the cylno we print */
|
|
if((E.curbn+secs) > E.lastbn)
|
|
secs = E.lastbn-E.curbn;
|
|
userabort = scanchunk(E.curbn, secs);
|
|
}
|
|
if(userabort) {
|
|
if (userabort == -1)
|
|
return 1;
|
|
if((E.modifier & MXW) && errno == EROFS)
|
|
return 1; /* don't retry! */
|
|
if(++consec_errs >= TOOMANYCONSEC) {
|
|
printf("\nNotice: %d consecutive errors detected.\n",
|
|
consec_errs);
|
|
printf("Suspect hardware problems with the drive.\n");
|
|
if (no("Continue exercising"))
|
|
return 1;
|
|
consec_errs = 0; /* restart count */
|
|
}
|
|
}
|
|
else
|
|
consec_errs = 0;
|
|
(*E.stepper) ();
|
|
}
|
|
dot(-1, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* do one chunk of an exercise, and find bad sectors if any.
|
|
* Return 0 if no error, 1 if any error. If sparing, retry
|
|
* the chunk to be sure it worked; return failure even if it
|
|
* worked on the retry for the consecutive error checks in scanpass().
|
|
*/
|
|
static
|
|
scanchunk(daddr_t bn, int sectors)
|
|
{
|
|
int i, gotone=0, savesectors, savebn;
|
|
int cmperr;
|
|
|
|
retry:
|
|
if((cmperr=rwcheck(bn, sectors)) == 0)
|
|
return gotone;
|
|
|
|
if((E.modifier & MXW) && errno == EROFS)
|
|
return 1; /* don't try to find error or spare */
|
|
|
|
/*
|
|
* If we got an error on the track, try up to ioretries
|
|
* times to find the bad sector. We do this because some
|
|
* errors are so 'soft', that we can get track errors, but not
|
|
* find an error on the individual sector with only one
|
|
* attempt. Don't stop after first one, in case multiple sectors
|
|
* are bad.
|
|
*/
|
|
savesectors = sectors;
|
|
savebn = bn;
|
|
scerrwarn("%s in chunk starting at block %d,\n"
|
|
" looking for failing block(s) sector by sector",
|
|
cmperr==1?"miscompare":"read error", bn);
|
|
|
|
for(i=0; i < ioretries; i++) {
|
|
for(sectors=savesectors, bn=savebn; --sectors >= 0; bn++) {
|
|
if (rwcheck(bn, 1) < 0) {
|
|
#ifdef SMFD_NUMBER /* don't even try on floppy */
|
|
if (drivernum != SMFD_NUMBER)
|
|
#endif /* SMFD_NUMBER */
|
|
{
|
|
if (stop_on_err) {
|
|
/* casts necessary because 32 bit standalone has 32 bit
|
|
* daddr_t; See bug #524200. */
|
|
printf("Error at block %lld, ",
|
|
(long long)bn);
|
|
if (no("Continue exercising"))
|
|
return -1;
|
|
if (no("Add to badblock list"))
|
|
return 1;
|
|
}
|
|
if(addibb(bn))
|
|
return -1; /* abort */
|
|
if(++gotone < TOOMANYCONSEC) {
|
|
bn = savebn;
|
|
goto retry; /* re-check after sparing */
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!gotone)
|
|
scerrwarn("Couldn't find bad block after %d tries,\n%s\n", i,
|
|
cmperr==1?"soft error or possibly a live filesystem" :
|
|
"probable soft error");
|
|
return gotone;
|
|
}
|
|
|
|
/*
|
|
* one chunk of an exercise, unadorned
|
|
*/
|
|
int
|
|
rwcheck(daddr_t bn, uint sectors)
|
|
{
|
|
if (E.modifier & MXW) {
|
|
if (gwrite(bn, E.buf, sectors) < 0)
|
|
return -1;
|
|
} else if (E.modifier & MXR) {
|
|
if (gread(bn, E.buf, sectors) < 0)
|
|
return -1;
|
|
}
|
|
if (E.modifier & MXC) {
|
|
if (gread(bn, chktrack, sectors) < 0)
|
|
return -1;
|
|
|
|
if (bcmp(chktrack, E.buf, stob(sectors)) != 0) {
|
|
errno = 0; /* make sure no bogus message printed */
|
|
errwarn("data mismatch, start block %d", bn);
|
|
if(showdiffs)
|
|
showdiff(E.buf, chktrack, sectors);
|
|
else
|
|
printf("\n");
|
|
logmsg("data mismatch, start block %d, %d sectors read\n",
|
|
bn, sectors);
|
|
return 1; /* so can tell difference from error */
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
showdiff(unchar *dbuf, unchar *cmpbuf, uint cnt)
|
|
{
|
|
int errs = 0, maxerrs=stob(1);
|
|
register i;
|
|
|
|
printf("\ncompared 0x%x sectors, differences are (in hex, as: byteoffset: expected, got):\n", cnt);
|
|
cnt = stob(cnt);
|
|
for (i = 0; i < cnt && errs < maxerrs; i++) {
|
|
/* print only first few lines of diffs */
|
|
if (*dbuf != *cmpbuf) {
|
|
printf("%x: %02x - %02x ", i, *dbuf, *cmpbuf);
|
|
if ((++errs % 5) == 0)
|
|
printf("\n");
|
|
}
|
|
dbuf++, cmpbuf++;
|
|
}
|
|
if (errs % 5)
|
|
printf("\n");
|
|
if(errs == maxerrs)
|
|
printf("only first %d errs were shown\n", maxerrs);
|
|
else
|
|
printf("%d bytes were different\n", errs);
|
|
}
|
|
|
|
/* ----- error logging routines ----- */
|
|
/* error flags */
|
|
# define EH 01 /* hard error (as opp to recovered error) */
|
|
# define EW 02 /* write error (as opp to read error) */
|
|
/*
|
|
* add to the error log
|
|
*/
|
|
void
|
|
adderr(int drive, daddr_t bn, char *s)
|
|
{
|
|
register int i;
|
|
register struct err *ep;
|
|
for (ep = E.eb, i = E.neb; --i >= 0; ep++)
|
|
if (ep->bn == bn && ep->drive == drive)
|
|
break;
|
|
if (i < 0) {
|
|
if (E.neb >= MAX_ERRBLOCKS)
|
|
return;
|
|
ep = E.eb + E.neb++;
|
|
ep->bn = bn;
|
|
ep->drive = drive;
|
|
ep->hits[0] = ep->hits[1]
|
|
= ep->hits[2] = ep->hits[3] = 0;
|
|
}
|
|
i = 0;
|
|
if (s[0] == 'h')
|
|
i |= EH;
|
|
if (s[1] == 'w')
|
|
i |= EW;
|
|
ep->hits[i]++;
|
|
}
|
|
|
|
void
|
|
errlog_summary(void)
|
|
{
|
|
register int i;
|
|
register struct err *ep;
|
|
struct err e;
|
|
bzero((char *) &e, sizeof e);
|
|
ep = E.eb;
|
|
for (i = E.neb; --i >= 0;) {
|
|
if (ep->hits[0])
|
|
e.hits[0]++;
|
|
if (ep->hits[1])
|
|
e.hits[1]++;
|
|
if (ep->hits[2])
|
|
e.hits[2]++;
|
|
if (ep->hits[3])
|
|
e.hits[3]++;
|
|
ep++;
|
|
}
|
|
printf("TOTAL ERR BLOCKS r%u(%u hard) w%u(%u hard)\n",
|
|
e.hits[0], e.hits[1],
|
|
e.hits[2], e.hits[3]);
|
|
}
|
|
|
|
void
|
|
SetTestPat(unchar *s, int n)
|
|
{
|
|
TestPat = s;
|
|
TestCycle = n;
|
|
}
|
|
|
|
int
|
|
SetTestPhase(int x)
|
|
{
|
|
return TestPhase = x;
|
|
}
|
|
|
|
|
|
# define BumpTestPat() ((TestPhase>=TestCycle?(TestPhase=0):0) \
|
|
, TestPat[TestPhase++])
|
|
|
|
void
|
|
TestPatFill(unchar *buf, int len)
|
|
{
|
|
while (--len >= 0)
|
|
*buf++ = BumpTestPat();
|
|
}
|
|
|