blob: fd9beb49b8430ff1fa38def20f56984e47a581d0 [file] [log] [blame]
// Copyright Martin J. Bligh & Google. <mbligh@google.com>.
// New Year's Eve, 2006
// Released under the GPL v2.
//
// Compile with -D_FILE_OFFSET_BITS=64 -D _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <getopt.h>
#define SECTOR_SIZE 512
#define UINT_PER_SECTOR (SECTOR_SIZE / sizeof(unsigned int))
char *filename = "testfile";
unsigned int megabytes = 1;
unsigned int blocksize = 4096;
unsigned int seconds = 15;
unsigned int linear_tasks = 1;
unsigned int random_tasks = 4;
unsigned int blocks;
unsigned int sectors_per_block;
void die(char *error)
{
fprintf(stderr, error);
fprintf(stderr, "\n");
exit(1);
}
/*
* Fill a block with it's own sector number
* buf must be at least blocksize
*/
void write_block(int fd, unsigned int block, unsigned int *buf)
{
unsigned int i, sec_offset, sector;
off_t offset;
for (sec_offset = 0; sec_offset < sectors_per_block; sec_offset++) {
sector = (block * sectors_per_block) + sec_offset;
for (i = 0; i < SECTOR_SIZE / sizeof(unsigned int); i++)
buf[(sec_offset * UINT_PER_SECTOR) + i] = sector;
}
offset = block; offset *= blocksize; // careful of overflow
lseek(fd, offset, SEEK_SET);
if (write(fd, buf, blocksize) != blocksize)
die("write failed");
}
/*
* Verify a block contains it's own sector number
* buf must be at least blocksize
*
* We only check the first number - the rest is pretty pointless
*/
int verify_block(int fd, unsigned int block, unsigned int *buf)
{
unsigned int sec_offset, sector;
off_t offset;
int error = 0;
offset = block; offset *= blocksize; // careful of overflow
lseek(fd, offset, SEEK_SET);
if (read(fd, buf, blocksize) != blocksize) {
fprintf(stderr, "read failed: block %d\n", block);
exit(1);
}
// printf("Checking block %d, offset %llx, size %d\n", block, offset, blocksize);
for (sec_offset = 0; sec_offset < sectors_per_block; sec_offset++) {
sector = (block * sectors_per_block) + sec_offset;
if (buf[sec_offset * UINT_PER_SECTOR] != sector) {
printf("sector %08x says %08x\n", sector,
buf[sec_offset * UINT_PER_SECTOR]);
error = 1;
}
}
// printf("Checked block %d, offset %llx, size %d\n", block, offset, blocksize);
return error;
}
void write_file(char *filename, unsigned int end_time, int random_access)
{
int fd, pid;
unsigned int block;
void *buffer;
fflush(stdout); fflush(stderr);
pid = fork();
if (pid < 0)
die ("error forking child");
if (pid != 0) // parent
return;
fd = open(filename, O_RDWR, 0666);
buffer = malloc(blocksize);
if (random_access) {
srandom(time(NULL) - getpid());
while(time(NULL) < end_time) {
block = (unsigned int) (random() % blocks);
write_block(fd, block, buffer);
}
} else {
while(time(NULL) < end_time)
for (block = 0; block < blocks; block++)
write_block(fd, block, buffer);
}
free(buffer);
exit(0);
}
void verify_file(char *filename, unsigned int end_time, int random_access,
int direct)
{
int pid, error = 0;
fflush(stdout); fflush(stderr);
pid = fork();
if (pid < 0)
die ("error forking child");
if (pid != 0) // parent
return;
int fd;
unsigned int block;
void *buffer = malloc(blocksize);
if (direct)
fd = open(filename, O_RDONLY | O_DIRECT);
else
fd = open(filename, O_RDONLY);
if (random_access) {
srandom(time(NULL) - getpid());
while(time(NULL) < end_time) {
block = (unsigned int) (random() % blocks);
if (verify_block(fd, block, buffer))
error = 1;
}
} else {
while(time(NULL) < end_time)
for (block = 0; block < blocks; block++)
if (verify_block(fd, block, buffer))
error = 1;
}
free(buffer);
exit(error);
}
void usage(void)
{
printf("Usage: disktest\n");
printf(" [-f filename] filename to use (testfile)\n");
printf(" [-s seconds] seconds to run for (15)\n");
printf(" [-m megabytes] megabytes to use (1)\n");
printf(" [-b blocksize] blocksize (4096)\n");
printf(" [-l linear tasks] linear access tasks (4)\n");
printf(" [-r random tasks] random access tasks (4)\n");
printf("\n");
}
int main(int argc, char *argv[])
{
unsigned int block;
time_t start_time, end_time;
int tasks, opt, retcode, pid;
void *init_buffer;
/* Parse all input options */
while ((opt = getopt(argc, argv, "f:s:m:b:l:r:")) != -1) {
switch (opt) {
case 'f':
filename = optarg;
break;
case 's':
seconds = atoi(optarg);
break;
case 'm':
megabytes = atoi(optarg);
break;
case 'b':
blocksize = atoi(optarg);
break;
case 'l':
linear_tasks = atoi(optarg);
break;
case 'r':
random_tasks = atoi(optarg);
break;
default:
usage();
exit(1);
}
}
argc -= optind;
argv += optind;
/* blocksize must be < 1MB, and a divisor. Tough */
blocks = megabytes * (1024 * 1024 / blocksize);
sectors_per_block = blocksize / SECTOR_SIZE;
/* Initialise file */
int fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, 0666);
if (fd < 0)
die("open failed");
start_time = time(NULL);
/* Initialise all file data to correct blocks */
init_buffer = malloc(blocksize);
for (block = 0; block < blocks; block++)
write_block(fd, block, init_buffer);
if(fsync(fd) != 0)
die("fsync failed");
for (block = 0; block < blocks; block++)
if (verify_block(fd, block, init_buffer))
exit(1);
// free(init_buffer);
printf("Wrote %d MB to %s (%d seconds)\n", megabytes, filename, (int) (time(NULL) - start_time));
end_time = time(NULL) + seconds;
/* Fork off all linear access pattern tasks */
for (tasks = 0; tasks < linear_tasks; tasks++) {
write_file(filename, end_time, 0);
}
/* Fork off all random access pattern tasks */
for (tasks = 0; tasks < random_tasks; tasks++)
write_file(filename, end_time, 1);
/* Verify in all four possible ways */
verify_file(filename, end_time, 0, 0);
verify_file(filename, end_time, 0, 1);
verify_file(filename, end_time, 1, 0);
verify_file(filename, end_time, 1, 1);
for (tasks = 0; tasks < linear_tasks + random_tasks + 4; tasks++) {
pid = wait(&retcode);
if (retcode != 0) {
printf("pid %d exited with status %d\n", pid, retcode);
exit(retcode);
}
}
return 0;
}