Home | History | Annotate | Download | only in src
      1 // Copyright Martin J. Bligh & Google. <mbligh (at) 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 
     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  */
     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  */
     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 
    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 
    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 
    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 
    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 
    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