/* * Author: Kent Overstreet * * GPLv2 */ #define _FILE_OFFSET_BITS 64 #define _XOPEN_SOURCE 500 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const unsigned char bcache_magic[] = { 0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca, 0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81 }; unsigned char zero[4096]; bool klog = false; #define Pread(fd, buf, size, offset) do { \ int _read = 0, _r; \ while (_read < size) { \ _r = pread(fd, buf, (size) - _read, (offset) + _read); \ if (_r <= 0) \ goto err; \ _read += _r; \ } \ } while (0) #define Pwrite(fd, buf, size, offset) do { \ int _write = 0, _r; \ while (_write < size) { \ _r = pwrite(fd, buf, (size) - _write, offset + _write); \ if (_r < 0) \ goto err; \ _write += _r; \ } \ } while (0) /* Marsaglia polar method */ double normal() { double x, y, s; static double n = 0 / (double) 0; if (n == n) { x = n; n = 0 / (double) 0; return x; } do { x = random() / (double) (RAND_MAX / 2) - 1; y = random() / (double) (RAND_MAX / 2) - 1; s = x * x + y * y; } while (s >= 1); s = sqrt(-2 * log(s) / s); n = y * s; return x * s; } long getblocks(int fd) { long ret; struct stat statbuf; if (fstat(fd, &statbuf)) { perror("stat error"); exit(EXIT_FAILURE); } ret = statbuf.st_size / 512; if (S_ISBLK(statbuf.st_mode)) if (ioctl(fd, BLKGETSIZE, &ret)) { perror("ioctl error"); exit(EXIT_FAILURE); } return ret; } struct pagestuff { unsigned char csum[16]; unsigned char oldcsum[16]; int readcount; int writecount; }; void flushlog(void) { char logbuf[1 << 21]; int w = 0, len; static int fd; if (!klog) return; if (!fd) { klogctl(8, 0, 6); sprintf(logbuf, "log.%i", abs(random()) % 1000); fd = open(logbuf, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (fd == -1) { perror("Error opening log file"); exit(EXIT_FAILURE); } } len = klogctl(4, logbuf, 1 << 21); if (len == -1) { perror("Error reading kernel log"); exit(EXIT_FAILURE); } while (w < len) { int r = write(fd, logbuf + w, len - w); if (r == -1) { perror("Error writing log"); exit(EXIT_FAILURE); } w += r; } } void aio_loop(int nr) { } void usage() { exit(EXIT_FAILURE); } int main(int argc, char **argv) { bool walk = false, randsize = false, verbose = false, csum = false, rtest = false, wtest = false; int fd1, fd2 = 0, direct = 0, nbytes = 4096, j, o; unsigned long size, i, offset = 0, done = 0, unique = 0, benchmark = 0; void *buf1 = NULL, *buf2 = NULL; struct pagestuff *pages, *p; unsigned char c[16]; time_t last_printed = 0; extern char *optarg; RC4_KEY writedata; RC4_set_key(&writedata, 16, bcache_magic); while ((o = getopt(argc, argv, "dnwvscwlb:")) != EOF) switch (o) { case 'd': direct = O_DIRECT; break; case 'n': walk = true; break; case 'v': verbose = true; break; case 's': randsize = true; break; case 'c': csum = true; break; case 'w': wtest = true; break; case 'r': rtest = true; break; case 'l': klog = true; break; case 'b': benchmark = atol(optarg); break; default: usage(); } argv += optind; argc -= optind; if (!rtest && !wtest) rtest = true; if (argc < 1) { printf("Please enter a device to test\n"); exit(EXIT_FAILURE); } if (!csum && !benchmark && argc < 2) { printf("Please enter a device to compare against\n"); exit(EXIT_FAILURE); } fd1 = open(argv[0], (wtest ? O_RDWR : O_RDONLY)|direct); if (!csum && !benchmark) fd2 = open(argv[1], (wtest ? O_RDWR : O_RDONLY)|direct); if (fd1 == -1 || fd2 == -1) { perror("Error opening device"); exit(EXIT_FAILURE); } size = getblocks(fd1); if (!csum && !benchmark) size = MIN(size, getblocks(fd2)); size = size / 8 - 16; pages = calloc(size + 16, sizeof(*pages)); printf("size %li\n", size); if (posix_memalign(&buf1, 4096, 4096 * 16) || posix_memalign(&buf2, 4096, 4096 * 16)) { printf("Could not allocate buffers\n"); exit(EXIT_FAILURE); } //setvbuf(stdout, NULL, _IONBF, 0); for (i = 0; !benchmark || i < benchmark; i++) { bool writing = (wtest && (i & 1)) || !rtest; nbytes = randsize ? drand48() * 16 + 1 : 1; nbytes <<= 12; offset >>= 12; offset += walk ? normal() * 20 : random(); offset %= size; offset <<= 12; if (!(i % 200)) flushlog(); if (!verbose) { time_t now = time(NULL); if (now - last_printed >= 2) { last_printed = now; goto print; } } else print: printf("Loop %6li offset %9li sectors %3i, %6lu mb done, %6lu mb unique\n", i, offset >> 9, nbytes >> 9, done >> 11, unique >> 11); done += nbytes >> 9; if (!writing) Pread(fd1, buf1, nbytes, offset); if (!writing && !csum && !benchmark) Pread(fd2, buf2, nbytes, offset); for (j = 0; j < nbytes; j += 4096) { p = &pages[(offset + j) / 4096]; if (writing) RC4(&writedata, 4096, zero, buf1 + j); if (csum) { MD4(buf1 + j, 4096, &c[0]); if (writing || (!p->readcount && !p->writecount)) { memcpy(&p->oldcsum[0], &p->csum[0], 16); memcpy(&p->csum[0], c, 16); } else if (memcmp(&p->csum[0], c, 16)) goto bad; } else if (!writing && !benchmark && memcmp(buf1 + j, buf2 + j, 4096)) goto bad; if (!p->writecount && !p->readcount) unique += 8; writing ? p->writecount++ : p->readcount++; } if (writing) Pwrite(fd1, buf1, nbytes, offset); if (writing && !csum && !benchmark) Pwrite(fd2, buf2, nbytes, offset); } printf("Loop %6li offset %9li sectors %3i, %6lu mb done, %6lu mb unique\n", i, offset >> 9, nbytes >> 9, done >> 11, unique >> 11); exit(EXIT_SUCCESS); err: perror("IO error"); flushlog(); exit(EXIT_FAILURE); bad: printf("Bad read! loop %li offset %li readcount %i writecount %i\n", i, (offset + j) >> 9, p->readcount, p->writecount); if (!memcmp(&p->oldcsum[0], c, 16)) printf("Matches previous csum\n"); flushlog(); exit(EXIT_FAILURE); }