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