Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions bitmap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#define _GNU_SOURCE
#include "bitmap.h"

#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>

#define CHUNK_SHIFT 21 /* 2MiB */
#define WORD_SHIFT 5
#define WORD_MASK ((1 << WORD_SHIFT) - 1)
#define BIT_TO_CHUNK(_b) ((_b) >> CHUNK_SHIFT)
#define BIT_TO_WORD(_b) ((_b) >> WORD_SHIFT)
#define BIT_TO_MASK(_b) (1 << ((_b) & WORD_MASK))
#define MAP_SIZE(_s) ((_s) * (1 << CHUNK_SHIFT))

static int resize(struct bitmap *b, size_t new_size)
{
if(new_size <= b->size)
return 0;

void *new_map = mremap(b->map, MAP_SIZE(b->size), MAP_SIZE(new_size),
MREMAP_MAYMOVE);

if(new_map == MAP_FAILED)
return -ENOMEM;

b->map = new_map;
b->size = new_size;
return 0;
}

int bitmap_init(struct bitmap *b)
{
b->size = 1;
b->map = mmap(NULL, MAP_SIZE(b->size), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return b->map == MAP_FAILED;
}

void bitmap_destroy(struct bitmap *b)
{
munmap(b->map, MAP_SIZE(b->size));
}

/**
* Mark and return previous value of a bit
*
* Resizes map if necessary. Returns -ENOMEM if resize fails.
*/
int bitmap_mark(struct bitmap *b, size_t bit)
{
int rv;
size_t chunk, word, mask;

chunk = BIT_TO_CHUNK(bit);
word = BIT_TO_WORD(bit);
mask = BIT_TO_MASK(bit);

rv = resize(b, chunk + 1);

if(rv)
return rv;

if(b->map[word] & mask)
return 1;

b->map[word] |= mask;
return 0;
}
28 changes: 28 additions & 0 deletions bitmap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef COMPSIZE__BITMAP_H__
#define COMPSIZE__BITMAP_H__

#include <stddef.h>
#include <stdint.h>

/**
* Simple pseudo-sparse bitmap
*
* Storage is mmaped to take advantage of lazy page allocation. Automatically
* resizes when accessing bits past the end of the current storage.
*
* Worst case memory usage 32MiB per TiB of 4K filesystem extents.
*
* @map: Word storage of bitmap
* @size: Size of map, as number of 2MiB chunks
*/
struct bitmap
{
uint32_t *map;
size_t size;
};

int bitmap_init(struct bitmap *b);
void bitmap_destroy(struct bitmap *b);
int bitmap_mark(struct bitmap *b, size_t bit);

#endif
34 changes: 26 additions & 8 deletions compsize.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#include <linux/limits.h>
#include <getopt.h>
#include <signal.h>
#include "radix-tree.h"
#include <errno.h>
#include <string.h>
#include "bitmap.h"
#include "endianness.h"

#if defined(DEBUG)
Expand Down Expand Up @@ -50,7 +52,7 @@ struct workspace
uint64_t nfiles;
uint64_t nextents, nrefs, ninline, nfrag;
uint64_t fragend;
struct radix_tree_root seen_extents;
struct bitmap seen_extents;
};

static const char *comp_types[MAX_ENTRIES] = { "none", "zlib", "lzo", "zstd" };
Expand Down Expand Up @@ -93,6 +95,11 @@ static void init_sv2_args(ino_t st_ino, struct btrfs_sv2_args *sv2_args)
sv2_args->buf_size = sizeof(sv2_args->buf);
}

static inline int IS_ALIGNED(uintptr_t ptr, size_t align)
{
return (ptr & (align - 1)) == 0;
}

static inline int is_hole(uint64_t disk_bytenr)
{
return disk_bytenr == 0;
Expand All @@ -105,6 +112,7 @@ static void parse_file_extent_item(uint8_t *bp, uint32_t hlen,
uint64_t disk_num_bytes, ram_bytes, disk_bytenr, num_bytes;
uint32_t inline_header_sz;
unsigned comp_type;
int rv;

DPRINTF("len=%u\n", hlen);

Expand Down Expand Up @@ -152,15 +160,20 @@ static void parse_file_extent_item(uint8_t *bp, uint32_t hlen,
if (!IS_ALIGNED(disk_bytenr, 1 << 12))
die("%s: Extent not 4K-aligned at %"PRIu64"?!?\n", filename, disk_bytenr);

unsigned long pageno = disk_bytenr >> 12;
radix_tree_preload(GFP_KERNEL);
if (radix_tree_insert(&ws->seen_extents, pageno, (void *)pageno) == 0)
switch((rv = bitmap_mark(&ws->seen_extents, disk_bytenr >> 12)))
{
case 0:
ws->disk[comp_type] += disk_num_bytes;
ws->uncomp[comp_type] += ram_bytes;
ws->nextents++;
break;
case 1:
break;
default:
errno = -rv;
die("bitmak_mark: %m\n");
}
radix_tree_preload_end();

ws->refd[comp_type] += num_bytes;
ws->nrefs++;

Expand Down Expand Up @@ -439,6 +452,7 @@ static int print_stats(struct workspace *ws)

int main(int argc, char **argv)
{
int rv;
struct workspace *ws;

ws = (struct workspace *) calloc(sizeof(*ws), 1);
Expand All @@ -451,15 +465,19 @@ int main(int argc, char **argv)
return 1;
}

radix_tree_init();
INIT_RADIX_TREE(&ws->seen_extents, 0);
if((rv = bitmap_init(&ws->seen_extents))) {
errno = -rv;
die("bitmap_init: %m\n");
}

signal(SIGUSR1, sigusr1);

for (; argv[optind]; optind++)
do_recursive_search(argv[optind], ws, NULL);

int ret = print_stats(ws);

bitmap_destroy(&ws->seen_extents);
free(ws);

return ret;
Expand Down
Loading