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