static int fsdirstat(char *path, int dev, Dir *d) { int fd; struct stat st; if(stat(path, &st) < 0 && lstat(path, &st) < 0) return -1; d->name = lastelem(path); d->uid = uidtoname(st.st_uid); d->gid = gidtoname(st.st_gid); d->muid = ""; d->qid = fsqid(&st); d->mode = (d->qid.type<<24) | (st.st_mode&0777); d->atime = st.st_atime; d->mtime = st.st_mtime; d->length = st.st_size; if(S_ISBLK(st.st_mode) && (fd = open(path, O_RDONLY)) >= 0){ d->length = disksize(fd, &st); close(fd); } // devmnt leaves 1-9 unused so that we can steal them. // it is easier for all involved if #Z shows M as the file type instead of Z. // dev is c->dev, either 1 (#Z) or 2 (#Zplan9). d->type = 'M'; d->dev = dev; return 0; }
int seek(char *disk, int oflag) { char *buf; int fd; off64_t size; off64_t begin, end; int usecs; int error; int tot_msec = 0, tot_io = 0; int stride; if ((fd = open(disk, oflag)) == -1) { perror(disk); return (-1); } #ifdef linux flushdisk(fd); #endif size = disksize(disk); buf = valloc(IOSIZE); bzero(buf, IOSIZE); /* * We flip back and forth, in strides of 1MB (typically). * If we have a 100MB fd, that means we do * 1, 99, 2, 98, etc. * * We want around SEEK POINTS data points * but the stride has to be at least 512 and a 512 multiple. */ stride = size / SEEKPOINTS; if (stride < 512) stride = 512; stride += 511; stride >>= 9; stride <<= 9; debug((stdout, "stride=%d size=%dM points=%d\n", stride, (int)(size >> 20), (int)(size/stride))); end = size; begin = 0; seekto(fd, begin); IO(fd, buf, IOSIZE); while (end >= begin + stride*2) { end -= stride; start(0); seekto(fd, end); IO(fd, buf, IOSIZE); usecs = stop(0, 0); if (usecs > TOOSMALL && usecs < TOOBIG) { tot_io++; tot_msec += usecs/1000; fprintf(stderr, "%.01f %.02f\n", (end - begin - stride) / 1000000., usecs/1000.); } begin += stride; start(0); seekto(fd, begin); IO(fd, buf, IOSIZE); usecs = stop(0, 0); if (usecs > TOOSMALL && usecs < TOOBIG) { tot_io++; tot_msec += usecs/1000; fprintf(stderr, "%.01f %.02f\n", (end + stride - begin) / 1000000., usecs/1000.); } } /* * This is wrong, it should take the 1/3 stroke seek average. avg_msec = (double)tot_msec/tot_io; fprintf(stderr, "Average time == %.04f\n", avg_msec); */ return (0); }
int zone(char *disk, int oflag, int bsize) { char *buf; int usecs; int error; int n; int fd; uint64 off; int stride; if ((fd = open(disk, oflag)) == -1) { perror(disk); exit(1); } buf = valloc(bsize); if (!buf) { perror("valloc"); exit(1); } bzero(buf, bsize); #ifdef linux flushdisk(fd); #endif /* * We want ZONEPOINTS data points * but the stride has to be at least 512 and a 512 multiple. * Weird code below for precision. */ off = disksize(disk); off /= ZONEPOINTS; stride = off; if (stride < 512) stride = 512; stride += 511; stride >>= 9; stride <<= 9; /* * Very small disks such as ZIP drives get a 256K blocksize. * As measured on my SCSI ZIP, there seems to be no * difference between 256K and 1MB for sequential reads. * XXX - there is a rotational delay difference but that's tough. */ if (bsize > stride) bsize = 256<<10; if (bsize > stride) stride = bsize; off *= ZONEPOINTS; debug((stdout, "stride=%d bs=%d size=%dM points=%d\n", stride, bsize, (int)(off >> 20), (int)(off/stride))); /* * Read buf's worth of data every stride and time it. * Don't include the rotational delay. * This first I/O outside the loop is to catch read/write permissions. */ #define IO(a,b,c) (oflag == 0 ? (n = read(a,b,c)) : (n = write(a,b,c))) error = IO(fd, buf, 512); if (error == -1) { perror(disk); exit(1); } off = 512; for ( ;; ) { if (IO(fd, buf, 1024) != 1024) { exit(0); } off += 1024; start(0); if (IO(fd, buf, bsize) != bsize) { exit(0); } usecs = stop(0, 0); off += bsize; fprintf(stderr, "%.01f %.2f\n", off/1000000.0, (double)bsize/usecs); off += stride; if (seekto(fd, off)) { exit(0); } } exit(0); }
int main(int argc, char* argv[]) { char* filename = argv[1]; off_t disk_size_in_bytes = disksize(filename); int i; double start_time, end_time; unsigned long sector_size = sectorsize(filename); long disk_size_in_sectors = disk_size_in_bytes / sector_size; int transfer_size_in_sectors = 1; // Probably doesn't make any sense to be anything other than 1. Except maybe 64 or 128. long transfer_size_in_bytes = transfer_size_in_sectors * sector_size; // TODO: initial random I/O test to determine number of repetitions based on random I/O rate. Or just stop after a certain elapsed time! int repetitions = 1000; struct timeval seed; // Scaling factor for the random numbers, to ensure that the sector chosen actually lies within the range of sectors on the disk. double block_scaling_factor = (double)MAX_RAND_INT / (double)disk_size_in_sectors; //fprintf(stderr, "block scaling factor = %f\n", block_scaling_factor); // Seed the RNG from the current time to avoid repetitive I/O patterns (and possible cache effects) across multiple runs: gettimeofday(&seed, NULL); srandom(seed.tv_usec); // char* buf = malloc(transfer_size_in_bytes); void* buf; int memalign_status = posix_memalign(&buf, 512, transfer_size_in_bytes); if (memalign_status != 0) { perror("Error allocating aligned buffer memory."); exit(EXIT_FAILURE); } int fd = diskbench_open(filename); off_t sector = 0; double elapsed_time_in_seconds = 0; fprintf(stderr, "\nAbout to perform random access test on %s\n\n", filename); fprintf(stderr, "Disk size: %lli GiB\n", disk_size_in_bytes / 1024 / 1024 / 1024); fprintf(stderr, " %lli MiB\n", disk_size_in_bytes / 1024 / 1024); fprintf(stderr, " %lli kiB\n", disk_size_in_bytes / 1024); fprintf(stderr, " %jd bytes\n", (intmax_t)disk_size_in_bytes); fprintf(stderr, " %li %lu-byte sectors\n\n", disk_size_in_sectors, sector_size); // Heading for output: fprintf(stderr, "sector\tbyte\tfraction\ttime(ms)\trate(MiB/s)\n"); for (i = 0; i < repetitions; i++) { sector = random() / block_scaling_factor; // Sector being sought: printf("%lli", sector); // Byte being sought: printf("\t%lli", sector * (long long int)sector_size); // Location as a fraction of the entire drive space: printf("\t%f", (double)sector / (double)disk_size_in_sectors); // Check the system time before doing the operation: start_time = seconds_since_epoch(); // Note: fseek() location is in bytes. lseek(fd, sector * (long)sector_size, SEEK_SET); // Does it make any difference if we only read one byte, rather than the whole block? read(fd, buf, transfer_size_in_bytes); // Record the time after finishing to determine elapsed time. end_time = seconds_since_epoch(); elapsed_time_in_seconds = end_time - start_time; // printf("; elapsed = %i s", time1.tv_sec - time0.tv_sec); printf("\t"); // Print elapsed time in milliseconds: printf("%f", elapsed_time_in_seconds * 1000.0); // printf("%f", 1000 * ((double)(time1.tv_sec - time0.tv_sec) + (double)(time1.tv_nsec - time0.tv_nsec) / 1000000000)); // Print throughput in MiB/s: printf("\t"); printf("%f", (double)transfer_size_in_bytes / elapsed_time_in_seconds / 1024.0 / 1024.0); printf("\n"); //printf("; time=%i, %i\n", (time1->tv_sec) - (time0->tv_sec), (time1->tv_nsec) - (time0->tv_nsec)); } free(buf); close(fd); return 0; }