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