1*9c5db199SXin Li // Copyright Martin J. Bligh & Google. <[email protected]>.
2*9c5db199SXin Li // New Year's Eve, 2006
3*9c5db199SXin Li // Released under the GPL v2.
4*9c5db199SXin Li //
5*9c5db199SXin Li // Compile with -D_FILE_OFFSET_BITS=64 -D _GNU_SOURCE
6*9c5db199SXin Li
7*9c5db199SXin Li #include <stdio.h>
8*9c5db199SXin Li #include <stdlib.h>
9*9c5db199SXin Li #include <unistd.h>
10*9c5db199SXin Li #include <sys/types.h>
11*9c5db199SXin Li #include <sys/wait.h>
12*9c5db199SXin Li #include <sys/stat.h>
13*9c5db199SXin Li #include <fcntl.h>
14*9c5db199SXin Li #include <time.h>
15*9c5db199SXin Li #include <getopt.h>
16*9c5db199SXin Li #include <errno.h>
17*9c5db199SXin Li #include <malloc.h>
18*9c5db199SXin Li #include <string.h>
19*9c5db199SXin Li
20*9c5db199SXin Li struct pattern {
21*9c5db199SXin Li unsigned int sector;
22*9c5db199SXin Li unsigned int signature;
23*9c5db199SXin Li };
24*9c5db199SXin Li
25*9c5db199SXin Li #define SECTOR_SIZE 512
26*9c5db199SXin Li #define PATTERN_PER_SECTOR (SECTOR_SIZE / sizeof(struct pattern))
27*9c5db199SXin Li
28*9c5db199SXin Li char *filename = "testfile";
29*9c5db199SXin Li volatile int stop = 0;
30*9c5db199SXin Li int init_only = 0;
31*9c5db199SXin Li int verify_only = 0;
32*9c5db199SXin Li unsigned int megabytes = 1;
33*9c5db199SXin Li unsigned int skip_mb = 0;
34*9c5db199SXin Li unsigned int start_block = 0;
35*9c5db199SXin Li unsigned int blocksize = 4096;
36*9c5db199SXin Li unsigned int seconds = 15;
37*9c5db199SXin Li unsigned int linear_tasks = 1;
38*9c5db199SXin Li unsigned int random_tasks = 4;
39*9c5db199SXin Li unsigned int blocks;
40*9c5db199SXin Li unsigned int sectors_per_block;
41*9c5db199SXin Li unsigned int signature = 0;
42*9c5db199SXin Li unsigned int stop_on_error = 0;
43*9c5db199SXin Li
die(char * error)44*9c5db199SXin Li void die(char *error)
45*9c5db199SXin Li {
46*9c5db199SXin Li fprintf(stderr, "%s\n", error);
47*9c5db199SXin Li exit(1);
48*9c5db199SXin Li }
49*9c5db199SXin Li
50*9c5db199SXin Li /*
51*9c5db199SXin Li * Fill a block with it's own sector number
52*9c5db199SXin Li * buf must be at least blocksize
53*9c5db199SXin Li */
write_block(int fd,unsigned int block,struct pattern * buffer)54*9c5db199SXin Li void write_block(int fd, unsigned int block, struct pattern *buffer)
55*9c5db199SXin Li {
56*9c5db199SXin Li unsigned int i, sec_offset, sector;
57*9c5db199SXin Li off_t offset;
58*9c5db199SXin Li struct pattern *sector_buffer;
59*9c5db199SXin Li
60*9c5db199SXin Li for (sec_offset = 0; sec_offset < sectors_per_block; sec_offset++) {
61*9c5db199SXin Li sector = (block * sectors_per_block) + sec_offset;
62*9c5db199SXin Li sector_buffer = &buffer[sec_offset * PATTERN_PER_SECTOR];
63*9c5db199SXin Li
64*9c5db199SXin Li for (i = 0; i < PATTERN_PER_SECTOR; i++) {
65*9c5db199SXin Li sector_buffer[i].sector = sector;
66*9c5db199SXin Li sector_buffer[i].signature = signature;
67*9c5db199SXin Li }
68*9c5db199SXin Li }
69*9c5db199SXin Li
70*9c5db199SXin Li offset = block; offset *= blocksize; // careful of overflow
71*9c5db199SXin Li lseek(fd, offset, SEEK_SET);
72*9c5db199SXin Li if (write(fd, buffer, blocksize) != blocksize) {
73*9c5db199SXin Li fprintf(stderr, "Write failed : file %s : block %d\n", filename, block);
74*9c5db199SXin Li exit(1);
75*9c5db199SXin Li }
76*9c5db199SXin Li }
77*9c5db199SXin Li
78*9c5db199SXin Li /*
79*9c5db199SXin Li * Verify a block contains the correct signature and sector numbers for
80*9c5db199SXin Li * each sector within that block. We check every copy within the sector
81*9c5db199SXin Li * and count how many were wrong.
82*9c5db199SXin Li *
83*9c5db199SXin Li * buf must be at least blocksize
84*9c5db199SXin Li */
verify_block(int fd,unsigned int block,struct pattern * buffer,char * err)85*9c5db199SXin Li int verify_block(int fd, unsigned int block, struct pattern *buffer, char *err)
86*9c5db199SXin Li {
87*9c5db199SXin Li unsigned int sec_offset, sector;
88*9c5db199SXin Li off_t offset;
89*9c5db199SXin Li int i, errors = 0;
90*9c5db199SXin Li struct pattern *sector_buffer;
91*9c5db199SXin Li
92*9c5db199SXin Li offset = block; offset *= blocksize; // careful of overflow
93*9c5db199SXin Li lseek(fd, offset, SEEK_SET);
94*9c5db199SXin Li if (read(fd, buffer, blocksize) != blocksize) {
95*9c5db199SXin Li fprintf(stderr, "read failed: block %d (errno: %d) filename %s %s\n", block, errno, filename, err);
96*9c5db199SXin Li exit(1);
97*9c5db199SXin Li }
98*9c5db199SXin Li
99*9c5db199SXin Li for (sec_offset = 0; sec_offset < sectors_per_block; sec_offset++) {
100*9c5db199SXin Li unsigned int read_sector = 0, read_signature = 0;
101*9c5db199SXin Li unsigned int sector_errors = 0, signature_errors = 0;
102*9c5db199SXin Li
103*9c5db199SXin Li sector = (block * sectors_per_block) + sec_offset;
104*9c5db199SXin Li sector_buffer = &buffer[sec_offset * PATTERN_PER_SECTOR];
105*9c5db199SXin Li
106*9c5db199SXin Li for (i = 0; i < PATTERN_PER_SECTOR; i++) {
107*9c5db199SXin Li if (sector_buffer[i].sector != sector) {
108*9c5db199SXin Li read_sector = sector_buffer[i].sector;
109*9c5db199SXin Li sector_errors++;
110*9c5db199SXin Li errors++;
111*9c5db199SXin Li }
112*9c5db199SXin Li if (sector_buffer[i].signature != signature) {
113*9c5db199SXin Li read_signature = sector_buffer[i].signature;
114*9c5db199SXin Li signature_errors++;
115*9c5db199SXin Li errors++;
116*9c5db199SXin Li }
117*9c5db199SXin Li }
118*9c5db199SXin Li if (sector_errors)
119*9c5db199SXin Li printf("Block %d (from %d to %d) sector %08x has wrong sector number %08x (%d/%lu) filename %s %s\n",
120*9c5db199SXin Li block, start_block, start_block+blocks,
121*9c5db199SXin Li sector, read_sector,
122*9c5db199SXin Li sector_errors, PATTERN_PER_SECTOR,
123*9c5db199SXin Li filename, err);
124*9c5db199SXin Li if (signature_errors)
125*9c5db199SXin Li printf("Block %d (from %d to %d) sector %08x signature is %08x should be %08x (%d/%lu) filename %s %s\n",
126*9c5db199SXin Li block, start_block, start_block+blocks,
127*9c5db199SXin Li sector, read_signature, signature,
128*9c5db199SXin Li signature_errors, PATTERN_PER_SECTOR,
129*9c5db199SXin Li filename, err);
130*9c5db199SXin Li
131*9c5db199SXin Li }
132*9c5db199SXin Li return errors;
133*9c5db199SXin Li }
134*9c5db199SXin Li
write_file(unsigned int end_time,int random_access)135*9c5db199SXin Li void write_file(unsigned int end_time, int random_access)
136*9c5db199SXin Li {
137*9c5db199SXin Li int fd, pid;
138*9c5db199SXin Li unsigned int block;
139*9c5db199SXin Li void *buffer;
140*9c5db199SXin Li
141*9c5db199SXin Li fflush(stdout); fflush(stderr);
142*9c5db199SXin Li pid = fork();
143*9c5db199SXin Li
144*9c5db199SXin Li if (pid < 0)
145*9c5db199SXin Li die ("error forking child");
146*9c5db199SXin Li if (pid != 0) // parent
147*9c5db199SXin Li return;
148*9c5db199SXin Li
149*9c5db199SXin Li fd = open(filename, O_RDWR, 0666);
150*9c5db199SXin Li buffer = malloc(blocksize);
151*9c5db199SXin Li
152*9c5db199SXin Li if (random_access) {
153*9c5db199SXin Li srandom(time(NULL) - getpid());
154*9c5db199SXin Li while(time(NULL) < end_time) {
155*9c5db199SXin Li block = start_block + (unsigned int)(random() % blocks);
156*9c5db199SXin Li write_block(fd, block, buffer);
157*9c5db199SXin Li }
158*9c5db199SXin Li } else {
159*9c5db199SXin Li while(time(NULL) < end_time)
160*9c5db199SXin Li for (block = start_block; block < start_block + blocks; block++)
161*9c5db199SXin Li write_block(fd, block, buffer);
162*9c5db199SXin Li }
163*9c5db199SXin Li free(buffer);
164*9c5db199SXin Li exit(0);
165*9c5db199SXin Li }
166*9c5db199SXin Li
verify_file(unsigned int end_time,int random_access,int direct)167*9c5db199SXin Li void verify_file(unsigned int end_time, int random_access, int direct)
168*9c5db199SXin Li {
169*9c5db199SXin Li int pid, error = 0;
170*9c5db199SXin Li char err_msg[40];
171*9c5db199SXin Li char *err = err_msg;
172*9c5db199SXin Li fflush(stdout); fflush(stderr);
173*9c5db199SXin Li pid = fork();
174*9c5db199SXin Li
175*9c5db199SXin Li if (pid < 0)
176*9c5db199SXin Li die ("error forking child");
177*9c5db199SXin Li if (pid != 0) // parent
178*9c5db199SXin Li return;
179*9c5db199SXin Li
180*9c5db199SXin Li int fd;
181*9c5db199SXin Li unsigned int block;
182*9c5db199SXin Li unsigned int align = (blocksize > 4096) ? blocksize : 4096;
183*9c5db199SXin Li void *buffer = memalign(align, blocksize);
184*9c5db199SXin Li
185*9c5db199SXin Li if (direct) {
186*9c5db199SXin Li fd = open(filename, O_RDONLY | O_DIRECT);
187*9c5db199SXin Li strcpy(err, "direct");
188*9c5db199SXin Li err += 6;
189*9c5db199SXin Li } else {
190*9c5db199SXin Li fd = open(filename, O_RDONLY);
191*9c5db199SXin Li strcpy(err, "cached");
192*9c5db199SXin Li err += 6;
193*9c5db199SXin Li }
194*9c5db199SXin Li
195*9c5db199SXin Li if (random_access) {
196*9c5db199SXin Li strcpy(err, ",random");
197*9c5db199SXin Li srandom(time(NULL) - getpid());
198*9c5db199SXin Li while(time(NULL) < end_time) {
199*9c5db199SXin Li block = start_block + (unsigned int)(random() % blocks);
200*9c5db199SXin Li if (verify_block(fd, block, buffer, err_msg))
201*9c5db199SXin Li error = 1;
202*9c5db199SXin Li }
203*9c5db199SXin Li } else {
204*9c5db199SXin Li strcpy(err, ",linear");
205*9c5db199SXin Li while(time(NULL) < end_time)
206*9c5db199SXin Li for (block = start_block; block < start_block + blocks; block++)
207*9c5db199SXin Li if (verify_block(fd, block, buffer, err_msg))
208*9c5db199SXin Li error = 1;
209*9c5db199SXin Li }
210*9c5db199SXin Li free(buffer);
211*9c5db199SXin Li exit(error);
212*9c5db199SXin Li }
213*9c5db199SXin Li
usage(void)214*9c5db199SXin Li void usage(void)
215*9c5db199SXin Li {
216*9c5db199SXin Li printf("Usage: disktest\n");
217*9c5db199SXin Li printf(" [-f filename] filename to use (testfile)\n");
218*9c5db199SXin Li printf(" [-s seconds] seconds to run for (15)\n");
219*9c5db199SXin Li printf(" [-m megabytes] megabytes to use (1)\n");
220*9c5db199SXin Li printf(" [-M megabytes] megabytes to skip (0)\n");
221*9c5db199SXin Li printf(" [-b blocksize] blocksize (4096)\n");
222*9c5db199SXin Li printf(" [-l linear tasks] linear access tasks (4)\n");
223*9c5db199SXin Li printf(" [-r random tasks] random access tasks (4)\n");
224*9c5db199SXin Li printf(" [-v] verify pre-existing file\n");
225*9c5db199SXin Li printf(" [-i] only do init phase\n");
226*9c5db199SXin Li printf(" [-S] stop immediately on error\n");
227*9c5db199SXin Li printf("\n");
228*9c5db199SXin Li }
229*9c5db199SXin Li
double_verify(int fd,void * buffer,char * err)230*9c5db199SXin Li unsigned int double_verify(int fd, void *buffer, char *err)
231*9c5db199SXin Li {
232*9c5db199SXin Li unsigned int block, errors = 0;
233*9c5db199SXin Li
234*9c5db199SXin Li for (block = start_block; block < start_block + blocks; block++) {
235*9c5db199SXin Li if (verify_block(fd, block, buffer, err)) {
236*9c5db199SXin Li if (stop_on_error)
237*9c5db199SXin Li return 1;
238*9c5db199SXin Li errors++;
239*9c5db199SXin Li }
240*9c5db199SXin Li }
241*9c5db199SXin Li return errors;
242*9c5db199SXin Li }
243*9c5db199SXin Li
main(int argc,char * argv[])244*9c5db199SXin Li int main(int argc, char *argv[])
245*9c5db199SXin Li {
246*9c5db199SXin Li unsigned int block;
247*9c5db199SXin Li time_t start_time, end_time;
248*9c5db199SXin Li int tasks, opt, retcode, pid;
249*9c5db199SXin Li void *init_buffer;
250*9c5db199SXin Li
251*9c5db199SXin Li /* Parse all input options */
252*9c5db199SXin Li while ((opt = getopt(argc, argv, "vf:s:m:M:b:l:r:iS")) != -1) {
253*9c5db199SXin Li switch (opt) {
254*9c5db199SXin Li case 'v':
255*9c5db199SXin Li verify_only = 1;
256*9c5db199SXin Li break;
257*9c5db199SXin Li case 'f':
258*9c5db199SXin Li filename = optarg;
259*9c5db199SXin Li break;
260*9c5db199SXin Li case 's':
261*9c5db199SXin Li seconds = atoi(optarg);
262*9c5db199SXin Li break;
263*9c5db199SXin Li case 'm':
264*9c5db199SXin Li megabytes = atoi(optarg);
265*9c5db199SXin Li break;
266*9c5db199SXin Li case 'M':
267*9c5db199SXin Li skip_mb = atoi(optarg);
268*9c5db199SXin Li break;
269*9c5db199SXin Li case 'b':
270*9c5db199SXin Li blocksize = atoi(optarg);
271*9c5db199SXin Li break;
272*9c5db199SXin Li case 'l':
273*9c5db199SXin Li linear_tasks = atoi(optarg);
274*9c5db199SXin Li break;
275*9c5db199SXin Li case 'r':
276*9c5db199SXin Li random_tasks = atoi(optarg);
277*9c5db199SXin Li break;
278*9c5db199SXin Li case 'i':
279*9c5db199SXin Li init_only = 1;
280*9c5db199SXin Li break;
281*9c5db199SXin Li case 'S':
282*9c5db199SXin Li stop_on_error = 1;
283*9c5db199SXin Li break;
284*9c5db199SXin Li default:
285*9c5db199SXin Li usage();
286*9c5db199SXin Li exit(1);
287*9c5db199SXin Li }
288*9c5db199SXin Li }
289*9c5db199SXin Li argc -= optind;
290*9c5db199SXin Li argv += optind;
291*9c5db199SXin Li
292*9c5db199SXin Li /* blocksize must be < 1MB, and a divisor. Tough */
293*9c5db199SXin Li blocks = megabytes * (1024 * 1024 / blocksize);
294*9c5db199SXin Li start_block = skip_mb * (1024 * 1024 / blocksize);
295*9c5db199SXin Li sectors_per_block = blocksize / SECTOR_SIZE;
296*9c5db199SXin Li init_buffer = malloc(blocksize);
297*9c5db199SXin Li
298*9c5db199SXin Li if (verify_only) {
299*9c5db199SXin Li struct stat stat_buf;
300*9c5db199SXin Li
301*9c5db199SXin Li printf("Verifying %s\n", filename);
302*9c5db199SXin Li int fd = open(filename, O_RDONLY);
303*9c5db199SXin Li if (fd < 0)
304*9c5db199SXin Li die("open failed");
305*9c5db199SXin Li
306*9c5db199SXin Li if (fstat(fd, &stat_buf) != 0)
307*9c5db199SXin Li die("fstat failed");
308*9c5db199SXin Li megabytes = stat_buf.st_size / (1024 * 1024);
309*9c5db199SXin Li blocks = megabytes * (1024 * 1024 / blocksize);
310*9c5db199SXin Li if (read(fd, init_buffer, SECTOR_SIZE) != SECTOR_SIZE) {
311*9c5db199SXin Li fprintf(stderr, "read failed of initial sector (errno: %d) filename %s\n", errno, filename);
312*9c5db199SXin Li exit(1);
313*9c5db199SXin Li }
314*9c5db199SXin Li lseek(fd, 0, SEEK_SET);
315*9c5db199SXin Li signature = ((struct pattern *)init_buffer)->signature;
316*9c5db199SXin Li
317*9c5db199SXin Li printf("Checking %d megabytes using signature %08x\n",
318*9c5db199SXin Li megabytes, signature);
319*9c5db199SXin Li if (double_verify(fd, init_buffer, "init1"))
320*9c5db199SXin Li exit(1);
321*9c5db199SXin Li else
322*9c5db199SXin Li exit(0);
323*9c5db199SXin Li }
324*9c5db199SXin Li
325*9c5db199SXin Li signature = (getpid() << 16) + ((unsigned int) time(NULL) & 0xffff);
326*9c5db199SXin Li
327*9c5db199SXin Li /* Initialise file */
328*9c5db199SXin Li int fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, 0666);
329*9c5db199SXin Li if (fd < 0)
330*9c5db199SXin Li die("open failed");
331*9c5db199SXin Li
332*9c5db199SXin Li start_time = time(NULL);
333*9c5db199SXin Li
334*9c5db199SXin Li printf("Ininitializing block %d to %d in file %s (signature %08x)\n", start_block, start_block+blocks, filename, signature);
335*9c5db199SXin Li /* Initialise all file data to correct blocks */
336*9c5db199SXin Li for (block = start_block; block < start_block+blocks; block++)
337*9c5db199SXin Li write_block(fd, block, init_buffer);
338*9c5db199SXin Li if(fsync(fd) != 0)
339*9c5db199SXin Li die("fsync failed");
340*9c5db199SXin Li if (double_verify(fd, init_buffer, "init1")) {
341*9c5db199SXin Li if (!stop_on_error) {
342*9c5db199SXin Li printf("First verify failed. Repeating for posterity\n");
343*9c5db199SXin Li double_verify(fd, init_buffer, "init2");
344*9c5db199SXin Li }
345*9c5db199SXin Li exit(1);
346*9c5db199SXin Li }
347*9c5db199SXin Li
348*9c5db199SXin Li printf("Wrote %d MB to %s (%d seconds)\n", megabytes, filename, (int) (time(NULL) - start_time));
349*9c5db199SXin Li
350*9c5db199SXin Li free(init_buffer);
351*9c5db199SXin Li if (init_only)
352*9c5db199SXin Li exit(0);
353*9c5db199SXin Li
354*9c5db199SXin Li end_time = time(NULL) + seconds;
355*9c5db199SXin Li
356*9c5db199SXin Li /* Fork off all linear access pattern tasks */
357*9c5db199SXin Li for (tasks = 0; tasks < linear_tasks; tasks++)
358*9c5db199SXin Li write_file(end_time, 0);
359*9c5db199SXin Li
360*9c5db199SXin Li /* Fork off all random access pattern tasks */
361*9c5db199SXin Li for (tasks = 0; tasks < random_tasks; tasks++)
362*9c5db199SXin Li write_file(end_time, 1);
363*9c5db199SXin Li
364*9c5db199SXin Li /* Verify in all four possible ways */
365*9c5db199SXin Li verify_file(end_time, 0, 0);
366*9c5db199SXin Li verify_file(end_time, 0, 1);
367*9c5db199SXin Li verify_file(end_time, 1, 0);
368*9c5db199SXin Li verify_file(end_time, 1, 1);
369*9c5db199SXin Li
370*9c5db199SXin Li for (tasks = 0; tasks < linear_tasks + random_tasks + 4; tasks++) {
371*9c5db199SXin Li pid = wait(&retcode);
372*9c5db199SXin Li if (retcode != 0) {
373*9c5db199SXin Li printf("pid %d exited with status %d\n", pid, retcode);
374*9c5db199SXin Li exit(1);
375*9c5db199SXin Li }
376*9c5db199SXin Li }
377*9c5db199SXin Li return 0;
378*9c5db199SXin Li }
379