]> git.sesse.net Git - bcachefs-tools-debian/blob - bcache-test.c
ccc25bb298fb2d40bba25dc8f9c861964b4e1b0f
[bcachefs-tools-debian] / bcache-test.c
1 /*
2  * Author: Kent Overstreet <kent.overstreet@gmail.com>
3  *
4  * GPLv2
5  */
6
7 #define _FILE_OFFSET_BITS       64
8 #define _XOPEN_SOURCE 500
9 #define _GNU_SOURCE
10
11 #include <fcntl.h>
12 #include <limits.h>
13 #include <linux/fs.h>
14 #include <math.h>
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <sys/ioctl.h>
20 #include <sys/klog.h>
21 #include <sys/param.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <time.h>
27
28 #include <openssl/rc4.h>
29 #include <openssl/md4.h>
30
31 static const unsigned char bcache_magic[] = {
32         0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca,
33         0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81 };
34
35 unsigned char zero[4096];
36
37 bool klog = false;
38
39 #define Pread(fd, buf, size, offset) do {                               \
40         int _read = 0, _r;                                              \
41         while (_read < size) {                                          \
42                 _r = pread(fd, buf, (size) - _read, (offset) + _read);  \
43                 if (_r <= 0)                                            \
44                         goto err;                                       \
45                 _read += _r;                                            \
46         }                                                               \
47 } while (0)
48
49 #define Pwrite(fd, buf, size, offset) do {                              \
50         int _write = 0, _r;                                             \
51         while (_write < size) {                                         \
52                 _r = pwrite(fd, buf, (size) - _write, offset + _write); \
53                 if (_r < 0)                                             \
54                         goto err;                                       \
55                 _write += _r;                                           \
56         }                                                               \
57 } while (0)
58
59 /* Marsaglia polar method
60  */
61 double normal()
62 {
63         double x, y, s;
64         static double n = 0 / (double) 0;
65
66         if (n == n) {
67                 x = n;
68                 n = 0 / (double) 0;
69                 return x;
70         }
71
72         do {
73                 x = random() / (double) (RAND_MAX / 2) - 1;
74                 y = random() / (double) (RAND_MAX / 2) - 1;
75
76                 s = x * x + y * y;
77         } while (s >= 1);
78
79         s = sqrt(-2 * log(s) / s);
80         n =     y * s;
81         return  x * s;
82 }
83
84 long getblocks(int fd)
85 {
86         long ret;
87         struct stat statbuf;
88         if (fstat(fd, &statbuf)) {
89                 perror("stat error");
90                 exit(EXIT_FAILURE);
91         }
92         ret = statbuf.st_size / 512;
93         if (S_ISBLK(statbuf.st_mode))
94                 if (ioctl(fd, BLKGETSIZE, &ret)) {
95                         perror("ioctl error");
96                         exit(EXIT_FAILURE);
97                 }
98         return ret;
99 }
100
101 struct pagestuff {
102         unsigned char csum[16];
103         unsigned char oldcsum[16];
104         int readcount;
105         int writecount;
106 };
107
108 void flushlog(void)
109 {
110         char logbuf[1 << 21];
111         int w = 0, len;
112         static int fd;
113
114         if (!klog)
115                 return;
116
117         if (!fd) {
118                 klogctl(8, 0, 6);
119
120                 sprintf(logbuf, "log.%i", abs(random()) % 1000);
121                 fd = open(logbuf, O_WRONLY|O_CREAT|O_TRUNC, 0644);
122
123                 if (fd == -1) {
124                         perror("Error opening log file");
125                         exit(EXIT_FAILURE);
126                 }
127         }
128
129         len = klogctl(4, logbuf, 1 << 21);
130
131         if (len == -1) {
132                 perror("Error reading kernel log");
133                 exit(EXIT_FAILURE);
134         }
135
136         while (w < len) {
137                 int r = write(fd, logbuf + w, len - w);
138                 if (r == -1) {
139                         perror("Error writing log");
140                         exit(EXIT_FAILURE);
141                 }
142                 w += r;
143         }
144 }
145
146 void aio_loop(int nr)
147 {
148
149 }
150
151 void usage()
152 {
153         exit(EXIT_FAILURE);
154 }
155
156 int main(int argc, char **argv)
157 {
158         bool walk = false, randsize = false, verbose = false, csum = false, rtest = false, wtest = false;
159         int fd1, fd2 = 0, direct = 0, nbytes = 4096, j, o;
160         unsigned long size, i, offset = 0, done = 0, unique = 0, benchmark = 0;
161         void *buf1 = NULL, *buf2 = NULL;
162         struct pagestuff *pages, *p;
163         unsigned char c[16];
164         time_t last_printed = 0;
165         extern char *optarg;
166
167         RC4_KEY writedata;
168         RC4_set_key(&writedata, 16, bcache_magic);
169
170         while ((o = getopt(argc, argv, "dnwvscwlb:")) != EOF)
171                 switch (o) {
172                 case 'd':
173                         direct = O_DIRECT;
174                         break;
175                 case 'n':
176                         walk = true;
177                         break;
178                 case 'v':
179                         verbose = true;
180                         break;
181                 case 's':
182                         randsize = true;
183                         break;
184                 case 'c':
185                         csum = true;
186                         break;
187                 case 'w':
188                         wtest = true;
189                         break;
190                 case 'r':
191                         rtest = true;
192                         break;
193                 case 'l':
194                         klog = true;
195                         break;
196                 case 'b':
197                         benchmark = atol(optarg);
198                         break;
199                 default:
200                         usage();
201                 }
202
203         argv += optind;
204         argc -= optind;
205
206         if (!rtest && !wtest)
207                 rtest = true;
208
209         if (argc < 1) {
210                 printf("Please enter a device to test\n");
211                 exit(EXIT_FAILURE);
212         }
213
214         if (!csum && !benchmark && argc < 2) {
215                 printf("Please enter a device to compare against\n");
216                 exit(EXIT_FAILURE);
217         }
218
219         fd1 = open(argv[0], (wtest ? O_RDWR : O_RDONLY)|direct);
220         if (!csum && !benchmark)
221                 fd2 = open(argv[1], (wtest ? O_RDWR : O_RDONLY)|direct);
222
223         if (fd1 == -1 || fd2 == -1) {
224                 perror("Error opening device");
225                 exit(EXIT_FAILURE);
226         }
227
228         size = getblocks(fd1);
229         if (!csum && !benchmark)
230                 size = MIN(size, getblocks(fd2));
231
232         size = size / 8 - 16;
233         pages = calloc(size + 16, sizeof(*pages));
234         printf("size %li\n", size);
235
236         if (posix_memalign(&buf1, 4096, 4096 * 16) ||
237             posix_memalign(&buf2, 4096, 4096 * 16)) {
238                 printf("Could not allocate buffers\n");
239                 exit(EXIT_FAILURE);
240         }
241         //setvbuf(stdout, NULL, _IONBF, 0);
242
243         for (i = 0; !benchmark || i < benchmark; i++) {
244                 bool writing = (wtest && (i & 1)) || !rtest;
245                 nbytes = randsize ? drand48() * 16 + 1 : 1;
246                 nbytes <<= 12;
247
248                 offset >>= 12;
249                 offset += walk ? normal() * 20 : random();
250                 offset %= size;
251                 offset <<= 12;
252
253                 if (!(i % 200))
254                         flushlog();
255
256                 if (!verbose) {
257                         time_t now = time(NULL);
258                         if (now - last_printed >= 2) {
259                                 last_printed = now;
260                                 goto print;
261                         }
262                 } else
263 print:                  printf("Loop %6li offset %9li sectors %3i, %6lu mb done, %6lu mb unique\n",
264                                i, offset >> 9, nbytes >> 9, done >> 11, unique >> 11);
265
266                 done += nbytes >> 9;
267
268                 if (!writing)
269                         Pread(fd1, buf1, nbytes, offset);
270                 if (!writing && !csum && !benchmark)
271                         Pread(fd2, buf2, nbytes, offset);
272
273                 for (j = 0; j < nbytes; j += 4096) {
274                         p = &pages[(offset + j) / 4096];
275
276                         if (writing)
277                                 RC4(&writedata, 4096, zero, buf1 + j);
278
279                         if (csum) {
280                                 MD4(buf1 + j, 4096, &c[0]);
281
282                                 if (writing ||
283                                     (!p->readcount && !p->writecount)) {
284                                         memcpy(&p->oldcsum[0], &p->csum[0], 16);
285                                         memcpy(&p->csum[0], c, 16);
286                                 } else if (memcmp(&p->csum[0], c, 16))
287                                         goto bad;
288                         } else if (!writing && !benchmark &&
289                                    memcmp(buf1 + j,
290                                           buf2 + j,
291                                           4096))
292                                 goto bad;
293
294                         if (!p->writecount && !p->readcount)
295                                 unique += 8;
296
297                         writing ? p->writecount++ : p->readcount++;
298                 }
299                 if (writing)
300                         Pwrite(fd1, buf1, nbytes, offset);
301                 if (writing && !csum && !benchmark)
302                         Pwrite(fd2, buf2, nbytes, offset);
303         }
304         printf("Loop %6li offset %9li sectors %3i, %6lu mb done, %6lu mb unique\n",
305                i, offset >> 9, nbytes >> 9, done >> 11, unique >> 11);
306         exit(EXIT_SUCCESS);
307 err:
308         perror("IO error");
309         flushlog();
310         exit(EXIT_FAILURE);
311 bad:
312         printf("Bad read! loop %li offset %li readcount %i writecount %i\n",
313                i, (offset + j) >> 9, p->readcount, p->writecount);
314
315         if (!memcmp(&p->oldcsum[0], c, 16))
316                 printf("Matches previous csum\n");
317
318         flushlog();
319         exit(EXIT_FAILURE);
320 }