Merge branch 'fuzz-cleanup' of https://github.com/vincentkfu/fio
[fio.git] / t / dedupe.c
CommitLineData
bf481692
JA
1/*
2 * Small tool to check for dedupable blocks in a file or device. Basically
3 * just scans the filename for extents of the given size, checksums them,
4 * and orders them up.
5 */
3d2d14bc
SW
6#include <fcntl.h>
7#include <inttypes.h>
bf481692 8#include <stdio.h>
3d2d14bc 9#include <string.h>
bf481692 10#include <unistd.h>
bf481692 11#include <sys/stat.h>
bf481692 12
fd745bf3 13#include "../fio.h"
bf481692
JA
14#include "../flist.h"
15#include "../log.h"
971caeb1 16#include "../fio_sem.h"
bf481692
JA
17#include "../smalloc.h"
18#include "../minmax.h"
19#include "../crc/md5.h"
bf481692 20#include "../os/os.h"
5a155943
JA
21#include "../gettime.h"
22#include "../fio_time.h"
55660f42 23#include "../lib/rbtree.h"
bf481692 24
fa88cd09 25#include "../lib/bloom.h"
9077ee4a 26#include "debug.h"
df284fbd
BD
27#include "zlib.h"
28
29struct zlib_ctrl {
30 z_stream stream;
31 unsigned char *buf_in;
32 unsigned char *buf_out;
33};
bf481692
JA
34
35struct worker_thread {
df284fbd 36 struct zlib_ctrl zc;
bf481692 37 pthread_t thread;
bf481692
JA
38 uint64_t cur_offset;
39 uint64_t size;
df284fbd 40 unsigned long long unique_capacity;
bf481692 41 unsigned long items;
fa88cd09 42 unsigned long dupes;
bf481692 43 int err;
df284fbd
BD
44 int fd;
45 volatile int done;
bf481692
JA
46};
47
48struct extent {
49 struct flist_head list;
50 uint64_t offset;
51};
52
53struct chunk {
e96c0125 54 struct fio_rb_node rb_node;
bf481692
JA
55 uint64_t count;
56 uint32_t hash[MD5_HASH_WORDS];
5a155943 57 struct flist_head extent_list[0];
bf481692
JA
58};
59
60struct item {
61 uint64_t offset;
62 uint32_t hash[MD5_HASH_WORDS];
63};
64
65static struct rb_root rb_root;
fa88cd09 66static struct bloom *bloom;
971caeb1 67static struct fio_sem *rb_lock;
bf481692
JA
68
69static unsigned int blocksize = 4096;
70static unsigned int num_threads;
71static unsigned int chunk_size = 1048576;
72static unsigned int dump_output;
73static unsigned int odirect;
74static unsigned int collision_check;
4d53d6c0 75static unsigned int print_progress = 1;
fa88cd09 76static unsigned int use_bloom = 1;
df284fbd 77static unsigned int compression = 0;
bf481692
JA
78
79static uint64_t total_size;
80static uint64_t cur_offset;
971caeb1 81static struct fio_sem *size_lock;
bf481692 82
d08a6886 83static struct fio_file file;
bf481692 84
d08a6886 85static uint64_t get_size(struct fio_file *f, struct stat *sb)
bf481692
JA
86{
87 uint64_t ret;
88
89 if (S_ISBLK(sb->st_mode)) {
75adbcf5 90 unsigned long long bytes = 0;
d08a6886
JA
91
92 if (blockdev_size(f, &bytes)) {
93 log_err("dedupe: failed getting bdev size\n");
bf481692
JA
94 return 0;
95 }
d08a6886 96 ret = bytes;
1d08bfb0 97 } else {
bf481692 98 ret = sb->st_size;
1d08bfb0 99 }
bf481692
JA
100
101 return (ret & ~((uint64_t)blocksize - 1));
102}
103
104static int get_work(uint64_t *offset, uint64_t *size)
105{
106 uint64_t this_chunk;
107 int ret = 1;
108
971caeb1 109 fio_sem_down(size_lock);
bf481692
JA
110
111 if (cur_offset < total_size) {
112 *offset = cur_offset;
113 this_chunk = min((uint64_t)chunk_size, total_size - cur_offset);
114 *size = this_chunk;
115 cur_offset += this_chunk;
116 ret = 0;
117 }
118
971caeb1 119 fio_sem_up(size_lock);
bf481692
JA
120 return ret;
121}
122
13793d02 123static int __read_block(int fd, void *buf, off_t offset, size_t count)
bf481692
JA
124{
125 ssize_t ret;
126
13793d02 127 ret = pread(fd, buf, count, offset);
bf481692
JA
128 if (ret < 0) {
129 perror("pread");
130 return 1;
1d08bfb0 131 } else if (!ret) {
bf481692 132 return 1;
1d08bfb0 133 } else if (ret != count) {
bf481692
JA
134 log_err("dedupe: short read on block\n");
135 return 1;
136 }
137
138 return 0;
139}
140
13793d02
JA
141static int read_block(int fd, void *buf, off_t offset)
142{
143 return __read_block(fd, buf, offset, blocksize);
144}
145
1d08bfb0
JA
146static void account_unique_capacity(uint64_t offset, uint64_t *unique_capacity,
147 struct zlib_ctrl *zc)
df284fbd
BD
148{
149 z_stream *stream = &zc->stream;
150 unsigned int compressed_len;
151 int ret;
152
153 if (read_block(file.fd, zc->buf_in, offset))
154 return;
155
156 stream->next_in = zc->buf_in;
157 stream->avail_in = blocksize;
158 stream->avail_out = deflateBound(stream, blocksize);
159 stream->next_out = zc->buf_out;
160
161 ret = deflate(stream, Z_FINISH);
162 assert(ret != Z_STREAM_ERROR);
163 compressed_len = blocksize - stream->avail_out;
164
165 if (dump_output)
166 printf("offset 0x%lx compressed to %d blocksize %d ratio %.2f \n",
167 (unsigned long) offset, compressed_len, blocksize,
168 (float)compressed_len / (float)blocksize);
169
170 *unique_capacity += compressed_len;
df284fbd
BD
171 deflateReset(stream);
172}
173
bf481692
JA
174static void add_item(struct chunk *c, struct item *i)
175{
87818659
JA
176 /*
177 * Save some memory and don't add extent items, if we don't
178 * use them.
179 */
180 if (dump_output || collision_check) {
181 struct extent *e;
182
183 e = malloc(sizeof(*e));
184 e->offset = i->offset;
5a155943 185 flist_add_tail(&e->list, &c->extent_list[0]);
87818659 186 }
bf481692 187
bf481692
JA
188 c->count++;
189}
190
191static int col_check(struct chunk *c, struct item *i)
192{
193 struct extent *e;
194 char *cbuf, *ibuf;
195 int ret = 1;
196
3114b675
VF
197 cbuf = fio_memalign(blocksize, blocksize, false);
198 ibuf = fio_memalign(blocksize, blocksize, false);
bf481692 199
5a155943 200 e = flist_entry(c->extent_list[0].next, struct extent, list);
d08a6886 201 if (read_block(file.fd, cbuf, e->offset))
bf481692
JA
202 goto out;
203
d08a6886 204 if (read_block(file.fd, ibuf, i->offset))
bf481692
JA
205 goto out;
206
207 ret = memcmp(ibuf, cbuf, blocksize);
208out:
3114b675
VF
209 fio_memfree(cbuf, blocksize, false);
210 fio_memfree(ibuf, blocksize, false);
bf481692
JA
211 return ret;
212}
213
5a155943
JA
214static struct chunk *alloc_chunk(void)
215{
216 struct chunk *c;
217
218 if (collision_check || dump_output) {
219 c = malloc(sizeof(struct chunk) + sizeof(struct flist_head));
220 INIT_FLIST_HEAD(&c->extent_list[0]);
1d08bfb0 221 } else {
5a155943 222 c = malloc(sizeof(struct chunk));
1d08bfb0 223 }
5a155943
JA
224
225 return c;
226}
227
1d08bfb0
JA
228static void insert_chunk(struct item *i, uint64_t *unique_capacity,
229 struct zlib_ctrl *zc)
bf481692 230{
e96c0125 231 struct fio_rb_node **p, *parent;
bf481692
JA
232 struct chunk *c;
233 int diff;
234
235 p = &rb_root.rb_node;
236 parent = NULL;
237 while (*p) {
238 parent = *p;
239
240 c = rb_entry(parent, struct chunk, rb_node);
241 diff = memcmp(i->hash, c->hash, sizeof(i->hash));
1d08bfb0 242 if (diff < 0) {
bf481692 243 p = &(*p)->rb_left;
1d08bfb0 244 } else if (diff > 0) {
bf481692 245 p = &(*p)->rb_right;
1d08bfb0 246 } else {
bf481692
JA
247 int ret;
248
249 if (!collision_check)
250 goto add;
251
971caeb1 252 fio_sem_up(rb_lock);
bf481692 253 ret = col_check(c, i);
971caeb1 254 fio_sem_down(rb_lock);
bf481692
JA
255
256 if (!ret)
257 goto add;
258
259 p = &(*p)->rb_right;
260 }
261 }
262
5a155943 263 c = alloc_chunk();
bf481692 264 RB_CLEAR_NODE(&c->rb_node);
bf481692
JA
265 c->count = 0;
266 memcpy(c->hash, i->hash, sizeof(i->hash));
267 rb_link_node(&c->rb_node, parent, p);
268 rb_insert_color(&c->rb_node, &rb_root);
df284fbd
BD
269 if (compression)
270 account_unique_capacity(i->offset, unique_capacity, zc);
bf481692
JA
271add:
272 add_item(c, i);
273}
274
fa88cd09 275static void insert_chunks(struct item *items, unsigned int nitems,
1d08bfb0
JA
276 uint64_t *ndupes, uint64_t *unique_capacity,
277 struct zlib_ctrl *zc)
bf481692
JA
278{
279 int i;
280
971caeb1 281 fio_sem_down(rb_lock);
bf481692 282
fa88cd09
JA
283 for (i = 0; i < nitems; i++) {
284 if (bloom) {
285 unsigned int s;
286 int r;
287
288 s = sizeof(items[i].hash) / sizeof(uint32_t);
289 r = bloom_set(bloom, items[i].hash, s);
290 *ndupes += r;
291 } else
df284fbd 292 insert_chunk(&items[i], unique_capacity, zc);
fa88cd09 293 }
bf481692 294
971caeb1 295 fio_sem_up(rb_lock);
bf481692
JA
296}
297
298static void crc_buf(void *buf, uint32_t *hash)
299{
300 struct fio_md5_ctx ctx = { .hash = hash };
301
302 fio_md5_init(&ctx);
303 fio_md5_update(&ctx, buf, blocksize);
304 fio_md5_final(&ctx);
305}
306
13793d02
JA
307static unsigned int read_blocks(int fd, void *buf, off_t offset, size_t size)
308{
309 if (__read_block(fd, buf, offset, size))
310 return 0;
311
312 return size / blocksize;
313}
314
bf481692
JA
315static int do_work(struct worker_thread *thread, void *buf)
316{
317 unsigned int nblocks, i;
318 off_t offset;
13793d02 319 int nitems = 0;
fa88cd09 320 uint64_t ndupes = 0;
df284fbd 321 uint64_t unique_capacity = 0;
bf481692
JA
322 struct item *items;
323
bf481692 324 offset = thread->cur_offset;
13793d02 325
1d08bfb0
JA
326 nblocks = read_blocks(thread->fd, buf, offset,
327 min(thread->size, (uint64_t) chunk_size));
13793d02
JA
328 if (!nblocks)
329 return 1;
330
bf481692
JA
331 items = malloc(sizeof(*items) * nblocks);
332
333 for (i = 0; i < nblocks; i++) {
13793d02
JA
334 void *thisptr = buf + (i * blocksize);
335
356c552d 336 items[i].offset = offset;
13793d02 337 crc_buf(thisptr, items[i].hash);
bf481692
JA
338 offset += blocksize;
339 nitems++;
340 }
341
df284fbd 342 insert_chunks(items, nitems, &ndupes, &unique_capacity, &thread->zc);
fa88cd09 343
bf481692 344 free(items);
fa88cd09
JA
345 thread->items += nitems;
346 thread->dupes += ndupes;
df284fbd 347 thread->unique_capacity += unique_capacity;
13793d02 348 return 0;
bf481692
JA
349}
350
df284fbd
BD
351static void thread_init_zlib_control(struct worker_thread *thread)
352{
1d08bfb0
JA
353 size_t sz;
354
df284fbd
BD
355 z_stream *stream = &thread->zc.stream;
356 stream->zalloc = Z_NULL;
357 stream->zfree = Z_NULL;
358 stream->opaque = Z_NULL;
359
360 if (deflateInit(stream, Z_DEFAULT_COMPRESSION) != Z_OK)
361 return;
362
363 thread->zc.buf_in = fio_memalign(blocksize, blocksize, false);
1d08bfb0
JA
364 sz = deflateBound(stream, blocksize);
365 thread->zc.buf_out = fio_memalign(blocksize, sz, false);
df284fbd
BD
366}
367
bf481692
JA
368static void *thread_fn(void *data)
369{
370 struct worker_thread *thread = data;
371 void *buf;
372
3114b675 373 buf = fio_memalign(blocksize, chunk_size, false);
df284fbd
BD
374 thread_init_zlib_control(thread);
375
bf481692
JA
376 do {
377 if (get_work(&thread->cur_offset, &thread->size)) {
378 thread->err = 1;
379 break;
380 }
381 if (do_work(thread, buf)) {
382 thread->err = 1;
383 break;
384 }
385 } while (1);
386
4d53d6c0 387 thread->done = 1;
3114b675 388 fio_memfree(buf, chunk_size, false);
bf481692
JA
389 return NULL;
390}
391
5a155943
JA
392static void show_progress(struct worker_thread *threads, unsigned long total)
393{
394 unsigned long last_nitems = 0;
8b6a404c 395 struct timespec last_tv;
5a155943
JA
396
397 fio_gettime(&last_tv, NULL);
398
399 while (print_progress) {
400 unsigned long this_items;
401 unsigned long nitems = 0;
402 uint64_t tdiff;
403 float perc;
423adcc7 404 int some_done = 0;
5a155943
JA
405 int i;
406
407 for (i = 0; i < num_threads; i++) {
408 nitems += threads[i].items;
409 some_done = threads[i].done;
410 if (some_done)
411 break;
412 }
413
414 if (some_done)
415 break;
416
417 perc = (float) nitems / (float) total;
418 perc *= 100.0;
419 this_items = nitems - last_nitems;
420 this_items *= blocksize;
421 tdiff = mtime_since_now(&last_tv);
422 if (tdiff) {
45b56027 423 this_items = (this_items * 1000) / (tdiff * 1024);
4870138d 424 printf("%3.2f%% done (%luKiB/sec)\r", perc, this_items);
5a155943
JA
425 last_nitems = nitems;
426 fio_gettime(&last_tv, NULL);
1d08bfb0 427 } else {
5a155943 428 printf("%3.2f%% done\r", perc);
1d08bfb0 429 }
5a155943
JA
430 fflush(stdout);
431 usleep(250000);
432 };
433}
434
d08a6886 435static int run_dedupe_threads(struct fio_file *f, uint64_t dev_size,
1d08bfb0
JA
436 uint64_t *nextents, uint64_t *nchunks,
437 uint64_t *unique_capacity)
bf481692
JA
438{
439 struct worker_thread *threads;
4d53d6c0 440 unsigned long nitems, total_items;
bf481692
JA
441 int i, err = 0;
442
443 total_size = dev_size;
4d53d6c0 444 total_items = dev_size / blocksize;
bf481692 445 cur_offset = 0;
971caeb1 446 size_lock = fio_sem_init(FIO_SEM_UNLOCKED);
bf481692
JA
447
448 threads = malloc(num_threads * sizeof(struct worker_thread));
449 for (i = 0; i < num_threads; i++) {
60a5a18b 450 memset(&threads[i], 0, sizeof(struct worker_thread));
d08a6886 451 threads[i].fd = f->fd;
bf481692
JA
452
453 err = pthread_create(&threads[i].thread, NULL, thread_fn, &threads[i]);
454 if (err) {
455 log_err("fio: thread startup failed\n");
456 break;
457 }
458 }
459
5a155943 460 show_progress(threads, total_items);
4d53d6c0 461
bf481692 462 nitems = 0;
fa88cd09
JA
463 *nextents = 0;
464 *nchunks = 1;
df284fbd 465 *unique_capacity = 0;
bf481692
JA
466 for (i = 0; i < num_threads; i++) {
467 void *ret;
468 pthread_join(threads[i].thread, &ret);
469 nitems += threads[i].items;
fa88cd09 470 *nchunks += threads[i].dupes;
df284fbd 471 *unique_capacity += threads[i].unique_capacity;
bf481692
JA
472 }
473
474 printf("Threads(%u): %lu items processed\n", num_threads, nitems);
475
fa88cd09
JA
476 *nextents = nitems;
477 *nchunks = nitems - *nchunks;
478
971caeb1 479 fio_sem_remove(size_lock);
5a155943 480 free(threads);
bf481692
JA
481 return err;
482}
483
fa88cd09 484static int dedupe_check(const char *filename, uint64_t *nextents,
df284fbd 485 uint64_t *nchunks, uint64_t *unique_capacity)
bf481692
JA
486{
487 uint64_t dev_size;
488 struct stat sb;
489 int flags;
490
491 flags = O_RDONLY;
492 if (odirect)
a0ae50a6 493 flags |= OS_O_DIRECT;
bf481692 494
d08a6886
JA
495 memset(&file, 0, sizeof(file));
496 file.file_name = strdup(filename);
497
498 file.fd = open(filename, flags);
499 if (file.fd == -1) {
bf481692 500 perror("open");
d08a6886 501 goto err;
bf481692
JA
502 }
503
d08a6886 504 if (fstat(file.fd, &sb) < 0) {
bf481692 505 perror("fstat");
d08a6886 506 goto err;
bf481692
JA
507 }
508
d08a6886
JA
509 dev_size = get_size(&file, &sb);
510 if (!dev_size)
511 goto err;
bf481692 512
fa88cd09
JA
513 if (use_bloom) {
514 uint64_t bloom_entries;
515
bc095aab 516 bloom_entries = 8 * (dev_size / blocksize);
fa88cd09
JA
517 bloom = bloom_new(bloom_entries);
518 }
519
1d08bfb0
JA
520 printf("Will check <%s>, size <%llu>, using %u threads\n", filename,
521 (unsigned long long) dev_size, num_threads);
bf481692 522
1d08bfb0
JA
523 return run_dedupe_threads(&file, dev_size, nextents, nchunks,
524 unique_capacity);
d08a6886
JA
525err:
526 if (file.fd != -1)
527 close(file.fd);
528 free(file.file_name);
529 return 1;
bf481692
JA
530}
531
532static void show_chunk(struct chunk *c)
533{
534 struct flist_head *n;
535 struct extent *e;
536
1d08bfb0
JA
537 printf("c hash %8x %8x %8x %8x, count %lu\n", c->hash[0], c->hash[1],
538 c->hash[2], c->hash[3], (unsigned long) c->count);
5a155943 539 flist_for_each(n, &c->extent_list[0]) {
bf481692 540 e = flist_entry(n, struct extent, list);
761c2729 541 printf("\toffset %llu\n", (unsigned long long) e->offset);
bf481692
JA
542 }
543}
544
df284fbd
BD
545static const char *capacity_unit[] = {"b","KB", "MB", "GB", "TB", "PB", "EB"};
546
547static uint64_t bytes_to_human_readable_unit(uint64_t n, const char **unit_out)
548{
549 uint8_t i = 0;
550
551 while (n >= 1024) {
552 i++;
553 n /= 1024;
554 }
555
556 *unit_out = capacity_unit[i];
df284fbd
BD
557 return n;
558}
559
1d08bfb0
JA
560static void show_stat(uint64_t nextents, uint64_t nchunks, uint64_t ndupextents,
561 uint64_t unique_capacity)
bf481692 562{
9ca144fe 563 double perc, ratio;
df284fbd
BD
564 const char *unit;
565 uint64_t uc_human;
bf481692 566
1d08bfb0
JA
567 printf("Extents=%lu, Unique extents=%lu", (unsigned long) nextents,
568 (unsigned long) nchunks);
0d71aa98
BD
569 if (!bloom)
570 printf(" Duplicated extents=%lu", (unsigned long) ndupextents);
571 printf("\n");
15e3ca5b
JA
572
573 if (nchunks) {
574 ratio = (double) nextents / (double) nchunks;
575 printf("De-dupe ratio: 1:%3.2f\n", ratio - 1.0);
1d08bfb0 576 } else {
15e3ca5b 577 printf("De-dupe ratio: 1:infinite\n");
1d08bfb0 578 }
fa88cd09 579
1d08bfb0
JA
580 if (ndupextents) {
581 printf("De-dupe working set at least: %3.2f%%\n",
582 100.0 * (double) ndupextents / (double) nextents);
583 }
0d71aa98 584
fa88cd09
JA
585 perc = 1.00 - ((double) nchunks / (double) nextents);
586 perc *= 100.0;
587 printf("Fio setting: dedupe_percentage=%u\n", (int) (perc + 0.50));
588
df284fbd
BD
589
590 if (compression) {
591 uc_human = bytes_to_human_readable_unit(unique_capacity, &unit);
592 printf("Unique capacity %lu%s\n", (unsigned long) uc_human, unit);
593 }
fa88cd09
JA
594}
595
0d71aa98 596static void iter_rb_tree(uint64_t *nextents, uint64_t *nchunks, uint64_t *ndupextents)
fa88cd09 597{
e96c0125 598 struct fio_rb_node *n;
0d71aa98 599 *nchunks = *nextents = *ndupextents = 0;
bf481692
JA
600
601 n = rb_first(&rb_root);
602 if (!n)
603 return;
604
605 do {
606 struct chunk *c;
607
608 c = rb_entry(n, struct chunk, rb_node);
fa88cd09
JA
609 (*nchunks)++;
610 *nextents += c->count;
0d71aa98 611 *ndupextents += (c->count > 1);
bf481692
JA
612
613 if (dump_output)
614 show_chunk(c);
615
616 } while ((n = rb_next(n)) != NULL);
bf481692
JA
617}
618
619static int usage(char *argv[])
620{
621 log_err("Check for dedupable blocks on a device/file\n\n");
622 log_err("%s: [options] <device or file>\n", argv[0]);
623 log_err("\t-b\tChunk size to use\n");
624 log_err("\t-t\tNumber of threads to use\n");
625 log_err("\t-d\tFull extent/chunk debug output\n");
626 log_err("\t-o\tUse O_DIRECT\n");
627 log_err("\t-c\tFull collision check\n");
fa88cd09 628 log_err("\t-B\tUse probabilistic bloom filter\n");
4d53d6c0 629 log_err("\t-p\tPrint progress indicator\n");
df284fbd 630 log_err("\t-C\tCalculate compressible size\n");
bf481692
JA
631 return 1;
632}
633
634int main(int argc, char *argv[])
635{
df284fbd 636 uint64_t nextents = 0, nchunks = 0, ndupextents = 0, unique_capacity;
bf481692
JA
637 int c, ret;
638
71471cb1 639 arch_init(argv);
9077ee4a
JA
640 debug_init();
641
df284fbd 642 while ((c = getopt(argc, argv, "b:t:d:o:c:p:B:C:")) != -1) {
bf481692
JA
643 switch (c) {
644 case 'b':
645 blocksize = atoi(optarg);
646 break;
647 case 't':
648 num_threads = atoi(optarg);
649 break;
650 case 'd':
651 dump_output = atoi(optarg);
652 break;
653 case 'o':
654 odirect = atoi(optarg);
655 break;
656 case 'c':
657 collision_check = atoi(optarg);
658 break;
4d53d6c0
JA
659 case 'p':
660 print_progress = atoi(optarg);
661 break;
fa88cd09
JA
662 case 'B':
663 use_bloom = atoi(optarg);
664 break;
df284fbd
BD
665 case 'C':
666 compression = atoi(optarg);
667 break;
bf481692
JA
668 case '?':
669 default:
670 return usage(argv);
671 }
672 }
673
df284fbd 674 if (collision_check || dump_output || compression)
fa88cd09
JA
675 use_bloom = 0;
676
bf481692
JA
677 if (!num_threads)
678 num_threads = cpus_online();
679
680 if (argc == optind)
681 return usage(argv);
682
683 sinit();
684
685 rb_root = RB_ROOT;
971caeb1 686 rb_lock = fio_sem_init(FIO_SEM_UNLOCKED);
bf481692 687
df284fbd 688 ret = dedupe_check(argv[optind], &nextents, &nchunks, &unique_capacity);
fa88cd09 689
179b3ead
JA
690 if (!ret) {
691 if (!bloom)
0d71aa98 692 iter_rb_tree(&nextents, &nchunks, &ndupextents);
bf481692 693
df284fbd 694 show_stat(nextents, nchunks, ndupextents, unique_capacity);
179b3ead 695 }
bf481692 696
971caeb1 697 fio_sem_remove(rb_lock);
9b36c122
JA
698 if (bloom)
699 bloom_free(bloom);
bf481692
JA
700 scleanup();
701 return ret;
702}