#include "hammer.h"
#include <sys/tree.h>
#include <libutil.h>
#include <openssl/sha.h>
#define DEDUP_BUF (64 * 1024)
struct sim_dedup_entry_rb_tree;
static RB_HEAD(sim_dedup_entry_rb_tree, sim_dedup_entry) sim_dedup_tree =
RB_INITIALIZER(&sim_dedup_tree);
RB_PROTOTYPE2(sim_dedup_entry_rb_tree, sim_dedup_entry, rb_entry,
rb_sim_dedup_entry_compare, hammer_crc_t);
struct sim_dedup_entry {
hammer_crc_t crc;
uint64_t ref_blks;
uint64_t ref_size;
RB_ENTRY(sim_dedup_entry) rb_entry;
};
struct dedup_entry {
struct hammer_btree_leaf_elm leaf;
union {
struct {
uint64_t ref_blks;
uint64_t ref_size;
} de;
RB_HEAD(sha_dedup_entry_rb_tree, sha_dedup_entry) fict_root;
} u;
uint8_t flags;
RB_ENTRY(dedup_entry) rb_entry;
};
#define HAMMER_DEDUP_ENTRY_FICTITIOUS 0x0001
struct sha_dedup_entry {
struct hammer_btree_leaf_elm leaf;
uint64_t ref_blks;
uint64_t ref_size;
uint8_t sha_hash[SHA256_DIGEST_LENGTH];
RB_ENTRY(sha_dedup_entry) fict_entry;
};
struct dedup_entry_rb_tree;
struct sha_dedup_entry_rb_tree;
static RB_HEAD(dedup_entry_rb_tree, dedup_entry) dedup_tree =
RB_INITIALIZER(&dedup_tree);
RB_PROTOTYPE2(dedup_entry_rb_tree, dedup_entry, rb_entry,
rb_dedup_entry_compare, hammer_crc_t);
RB_PROTOTYPE(sha_dedup_entry_rb_tree, sha_dedup_entry, fict_entry,
rb_sha_dedup_entry_compare);
static STAILQ_HEAD(, pass2_dedup_entry) pass2_dedup_queue =
STAILQ_HEAD_INITIALIZER(pass2_dedup_queue);
struct pass2_dedup_entry {
struct hammer_btree_leaf_elm leaf;
STAILQ_ENTRY(pass2_dedup_entry) sq_entry;
};
#define DEDUP_PASS2 0x0001
static int SigInfoFlag;
static int SigAlrmFlag;
static int64_t DedupDataReads;
static int64_t DedupCurrentRecords;
static int64_t DedupTotalRecords;
static uint32_t DedupCrcStart;
static uint32_t DedupCrcEnd;
static uint64_t MemoryUse;
static int glob_fd;
static struct hammer_ioc_pseudofs_rw glob_pfs;
static uint64_t dedup_alloc_size;
static uint64_t dedup_ref_size;
static uint64_t dedup_skipped_size;
static uint64_t dedup_crc_failures;
static uint64_t dedup_sha_failures;
static uint64_t dedup_underflows;
static uint64_t dedup_successes_count;
static uint64_t dedup_successes_bytes;
static int rb_sim_dedup_entry_compare(struct sim_dedup_entry *sim_de1,
struct sim_dedup_entry *sim_de2);
static int rb_dedup_entry_compare(struct dedup_entry *de1,
struct dedup_entry *de2);
static int rb_sha_dedup_entry_compare(struct sha_dedup_entry *sha_de1,
struct sha_dedup_entry *sha_de2);
typedef int (*scan_pfs_cb_t)(hammer_btree_leaf_elm_t scan_leaf, int flags);
static void scan_pfs(char *filesystem, scan_pfs_cb_t func, const char *id);
static int collect_btree_elm(hammer_btree_leaf_elm_t scan_leaf, int flags);
static int count_btree_elm(hammer_btree_leaf_elm_t scan_leaf, int flags);
static int process_btree_elm(hammer_btree_leaf_elm_t scan_leaf, int flags);
static int upgrade_chksum(hammer_btree_leaf_elm_t leaf, uint8_t *sha_hash);
static void dump_simulated_dedup(void);
static void dump_real_dedup(void);
static void dedup_usage(int code);
RB_GENERATE2(sim_dedup_entry_rb_tree, sim_dedup_entry, rb_entry,
rb_sim_dedup_entry_compare, hammer_crc_t, crc);
RB_GENERATE2(dedup_entry_rb_tree, dedup_entry, rb_entry,
rb_dedup_entry_compare, hammer_crc_t, leaf.data_crc);
RB_GENERATE(sha_dedup_entry_rb_tree, sha_dedup_entry, fict_entry,
rb_sha_dedup_entry_compare);
static
int
rb_sim_dedup_entry_compare(struct sim_dedup_entry *sim_de1,
struct sim_dedup_entry *sim_de2)
{
if (sim_de1->crc < sim_de2->crc)
return (-1);
if (sim_de1->crc > sim_de2->crc)
return (1);
return (0);
}
static
int
rb_dedup_entry_compare(struct dedup_entry *de1, struct dedup_entry *de2)
{
if (de1->leaf.data_crc < de2->leaf.data_crc)
return (-1);
if (de1->leaf.data_crc > de2->leaf.data_crc)
return (1);
return (0);
}
static
int
rb_sha_dedup_entry_compare(struct sha_dedup_entry *sha_de1,
struct sha_dedup_entry *sha_de2)
{
unsigned long *h1 = (unsigned long *)&sha_de1->sha_hash;
unsigned long *h2 = (unsigned long *)&sha_de2->sha_hash;
int i;
for (i = 0; i < SHA256_DIGEST_LENGTH / (int)sizeof(unsigned long); ++i) {
if (h1[i] < h2[i])
return (-1);
if (h1[i] > h2[i])
return (1);
}
return (0);
}
void
hammer_cmd_dedup_simulate(char **av, int ac)
{
struct sim_dedup_entry *sim_de;
if (ac != 1) {
dedup_usage(1);
}
glob_fd = getpfs(&glob_pfs, av[0]);
printf("Dedup-simulate running\n");
do {
DedupCrcStart = DedupCrcEnd;
DedupCrcEnd = 0;
MemoryUse = 0;
if (VerboseOpt) {
printf("B-Tree pass crc-range %08x-max\n",
DedupCrcStart);
fflush(stdout);
}
scan_pfs(av[0], collect_btree_elm, "simu-pass");
if (VerboseOpt >= 2)
dump_simulated_dedup();
while ((sim_de = RB_ROOT(&sim_dedup_tree)) != NULL) {
assert(sim_de->ref_blks != 0);
dedup_ref_size += sim_de->ref_size;
dedup_alloc_size += sim_de->ref_size / sim_de->ref_blks;
RB_REMOVE(sim_dedup_entry_rb_tree, &sim_dedup_tree, sim_de);
free(sim_de);
}
if (DedupCrcEnd && VerboseOpt == 0)
printf(".");
} while (DedupCrcEnd);
printf("Dedup-simulate %s succeeded\n", av[0]);
relpfs(glob_fd, &glob_pfs);
printf("Simulated dedup ratio = %.2f\n",
(dedup_alloc_size != 0) ?
(double)dedup_ref_size / dedup_alloc_size : 0);
}
void
hammer_cmd_dedup(char **av, int ac)
{
struct dedup_entry *de;
struct sha_dedup_entry *sha_de;
struct pass2_dedup_entry *pass2_de;
char *tmp;
char buf[8];
int needfree = 0;
if (TimeoutOpt > 0)
alarm(TimeoutOpt);
if (ac != 1) {
dedup_usage(1);
}
STAILQ_INIT(&pass2_dedup_queue);
glob_fd = getpfs(&glob_pfs, av[0]);
if (!CyclePath) {
if (glob_pfs.ondisk->snapshots[0] != '/') {
asprintf(&tmp, "%s/%s/.dedup.cycle",
SNAPSHOTS_BASE, av[0]);
} else {
asprintf(&tmp, "%s/.dedup.cycle",
glob_pfs.ondisk->snapshots);
}
CyclePath = tmp;
needfree = 1;
}
scan_pfs(av[0], count_btree_elm, "pre-pass ");
DedupTotalRecords = DedupCurrentRecords;
printf("Dedup running\n");
do {
DedupCrcStart = DedupCrcEnd;
DedupCrcEnd = 0;
MemoryUse = 0;
if (VerboseOpt) {
printf("B-Tree pass crc-range %08x-max\n",
DedupCrcStart);
fflush(stdout);
}
scan_pfs(av[0], process_btree_elm, "main-pass");
while ((pass2_de = STAILQ_FIRST(&pass2_dedup_queue)) != NULL) {
if (process_btree_elm(&pass2_de->leaf, DEDUP_PASS2))
dedup_skipped_size -= pass2_de->leaf.data_len;
STAILQ_REMOVE_HEAD(&pass2_dedup_queue, sq_entry);
free(pass2_de);
}
assert(STAILQ_EMPTY(&pass2_dedup_queue));
if (VerboseOpt >= 2)
dump_real_dedup();
while ((de = RB_ROOT(&dedup_tree)) != NULL) {
if (de->flags & HAMMER_DEDUP_ENTRY_FICTITIOUS) {
while ((sha_de = RB_ROOT(&de->u.fict_root)) != NULL) {
assert(sha_de->ref_blks != 0);
dedup_ref_size += sha_de->ref_size;
dedup_alloc_size += sha_de->ref_size / sha_de->ref_blks;
RB_REMOVE(sha_dedup_entry_rb_tree,
&de->u.fict_root, sha_de);
free(sha_de);
}
assert(RB_EMPTY(&de->u.fict_root));
} else {
assert(de->u.de.ref_blks != 0);
dedup_ref_size += de->u.de.ref_size;
dedup_alloc_size += de->u.de.ref_size / de->u.de.ref_blks;
}
RB_REMOVE(dedup_entry_rb_tree, &dedup_tree, de);
free(de);
}
assert(RB_EMPTY(&dedup_tree));
if (DedupCrcEnd && VerboseOpt == 0)
printf(".");
} while (DedupCrcEnd);
printf("Dedup %s succeeded\n", av[0]);
relpfs(glob_fd, &glob_pfs);
humanize_unsigned(buf, sizeof(buf), dedup_ref_size, "B", 1024);
printf("Dedup ratio = %.2f (in this run)\n"
" %8s referenced\n",
((dedup_alloc_size != 0) ?
(double)dedup_ref_size / dedup_alloc_size : 0),
buf
);
humanize_unsigned(buf, sizeof(buf), dedup_alloc_size, "B", 1024);
printf(" %8s allocated\n", buf);
humanize_unsigned(buf, sizeof(buf), dedup_skipped_size, "B", 1024);
printf(" %8s skipped\n", buf);
printf(" %8jd CRC collisions\n"
" %8jd SHA collisions\n"
" %8jd big-block underflows\n"
" %8jd new dedup records\n"
" %8jd new dedup bytes\n",
(intmax_t)dedup_crc_failures,
(intmax_t)dedup_sha_failures,
(intmax_t)dedup_underflows,
(intmax_t)dedup_successes_count,
(intmax_t)dedup_successes_bytes
);
hammer_reset_cycle();
if (needfree) {
free(tmp);
CyclePath = NULL;
}
}
static
int
count_btree_elm(hammer_btree_leaf_elm_t scan_leaf __unused, int flags __unused)
{
return(1);
}
static
int
collect_btree_elm(hammer_btree_leaf_elm_t scan_leaf, int flags __unused)
{
struct sim_dedup_entry *sim_de;
if (MemoryUse > MemoryLimit) {
DedupCrcEnd = DedupCrcStart +
(uint32_t)(DedupCrcEnd - DedupCrcStart - 1) / 2;
if (VerboseOpt) {
printf("memory limit crc-range %08x-%08x\n",
DedupCrcStart, DedupCrcEnd);
fflush(stdout);
}
for (;;) {
sim_de = RB_MAX(sim_dedup_entry_rb_tree,
&sim_dedup_tree);
if (sim_de == NULL || sim_de->crc < DedupCrcEnd)
break;
RB_REMOVE(sim_dedup_entry_rb_tree,
&sim_dedup_tree, sim_de);
MemoryUse -= sizeof(*sim_de);
free(sim_de);
}
}
sim_de = RB_LOOKUP(sim_dedup_entry_rb_tree, &sim_dedup_tree,
scan_leaf->data_crc);
if (sim_de == NULL) {
sim_de = calloc(1, sizeof(*sim_de));
sim_de->crc = scan_leaf->data_crc;
RB_INSERT(sim_dedup_entry_rb_tree, &sim_dedup_tree, sim_de);
MemoryUse += sizeof(*sim_de);
}
sim_de->ref_blks += 1;
sim_de->ref_size += scan_leaf->data_len;
return (1);
}
static __inline
int
validate_dedup_pair(hammer_btree_leaf_elm_t p, hammer_btree_leaf_elm_t q)
{
if (HAMMER_ZONE(p->data_offset) != HAMMER_ZONE(q->data_offset))
return (1);
if (p->data_len != q->data_len)
return (1);
return (0);
}
#define DEDUP_TECH_FAILURE 1
#define DEDUP_CMP_FAILURE 2
#define DEDUP_INVALID_ZONE 3
#define DEDUP_UNDERFLOW 4
#define DEDUP_VERS_FAILURE 5
static __inline
int
deduplicate(hammer_btree_leaf_elm_t p, hammer_btree_leaf_elm_t q)
{
struct hammer_ioc_dedup dedup;
bzero(&dedup, sizeof(dedup));
if (p->data_offset == q->data_offset)
return (0);
dedup.elm1 = p->base;
dedup.elm2 = q->base;
RunningIoctl = 1;
if (ioctl(glob_fd, HAMMERIOC_DEDUP, &dedup) < 0) {
if (errno == EOPNOTSUPP)
return (DEDUP_VERS_FAILURE);
return (DEDUP_TECH_FAILURE);
}
if (dedup.head.flags & HAMMER_IOC_DEDUP_CMP_FAILURE)
return (DEDUP_CMP_FAILURE);
if (dedup.head.flags & HAMMER_IOC_DEDUP_INVALID_ZONE)
return (DEDUP_INVALID_ZONE);
if (dedup.head.flags & HAMMER_IOC_DEDUP_UNDERFLOW)
return (DEDUP_UNDERFLOW);
RunningIoctl = 0;
++dedup_successes_count;
dedup_successes_bytes += p->data_len;
return (0);
}
static
int
process_btree_elm(hammer_btree_leaf_elm_t scan_leaf, int flags)
{
struct dedup_entry *de;
struct sha_dedup_entry *sha_de, temp;
struct pass2_dedup_entry *pass2_de;
int error;
while (MemoryUse > MemoryLimit) {
DedupCrcEnd = DedupCrcStart +
(uint32_t)(DedupCrcEnd - DedupCrcStart - 1) / 2;
if (VerboseOpt) {
printf("memory limit crc-range %08x-%08x\n",
DedupCrcStart, DedupCrcEnd);
fflush(stdout);
}
for (;;) {
de = RB_MAX(dedup_entry_rb_tree, &dedup_tree);
if (de == NULL || de->leaf.data_crc < DedupCrcEnd)
break;
if (de->flags & HAMMER_DEDUP_ENTRY_FICTITIOUS) {
while ((sha_de = RB_ROOT(&de->u.fict_root)) !=
NULL) {
RB_REMOVE(sha_dedup_entry_rb_tree,
&de->u.fict_root, sha_de);
MemoryUse -= sizeof(*sha_de);
free(sha_de);
}
}
RB_REMOVE(dedup_entry_rb_tree, &dedup_tree, de);
MemoryUse -= sizeof(*de);
free(de);
}
}
de = RB_LOOKUP(dedup_entry_rb_tree, &dedup_tree, scan_leaf->data_crc);
if (de == NULL) {
de = calloc(1, sizeof(*de));
de->leaf = *scan_leaf;
RB_INSERT(dedup_entry_rb_tree, &dedup_tree, de);
MemoryUse += sizeof(*de);
goto upgrade_stats;
}
if (de->flags & HAMMER_DEDUP_ENTRY_FICTITIOUS) {
RB_FOREACH(sha_de, sha_dedup_entry_rb_tree, &de->u.fict_root) {
if (sha_de->leaf.data_offset ==
scan_leaf->data_offset &&
sha_de->leaf.data_len == scan_leaf->data_len) {
memcpy(temp.sha_hash, sha_de->sha_hash,
SHA256_DIGEST_LENGTH);
break;
}
}
if (sha_de == NULL) {
if (upgrade_chksum(scan_leaf, temp.sha_hash))
goto pass2_insert;
sha_de = RB_FIND(sha_dedup_entry_rb_tree,
&de->u.fict_root, &temp);
}
if (sha_de == NULL) {
sha_de = calloc(1, sizeof(*sha_de));
sha_de->leaf = *scan_leaf;
memcpy(sha_de->sha_hash, temp.sha_hash,
SHA256_DIGEST_LENGTH);
RB_INSERT(sha_dedup_entry_rb_tree, &de->u.fict_root,
sha_de);
MemoryUse += sizeof(*sha_de);
goto upgrade_stats_sha;
}
if (validate_dedup_pair(&sha_de->leaf, scan_leaf))
goto sha256_failure;
error = deduplicate(&sha_de->leaf, scan_leaf);
switch(error) {
case 0:
goto upgrade_stats_sha;
case DEDUP_TECH_FAILURE:
goto pass2_insert;
case DEDUP_CMP_FAILURE:
goto sha256_failure;
case DEDUP_INVALID_ZONE:
goto terminate_early;
case DEDUP_UNDERFLOW:
++dedup_underflows;
sha_de->leaf = *scan_leaf;
memcpy(sha_de->sha_hash, temp.sha_hash,
SHA256_DIGEST_LENGTH);
goto upgrade_stats_sha;
case DEDUP_VERS_FAILURE:
errx(1, "HAMMER filesystem must be at least "
"version 5 to dedup");
default:
fprintf(stderr, "Unknown error\n");
goto terminate_early;
}
sha256_failure:
++dedup_sha_failures;
goto terminate_early;
} else {
if (validate_dedup_pair(&de->leaf, scan_leaf))
goto crc_failure;
error = deduplicate(&de->leaf, scan_leaf);
switch(error) {
case 0:
goto upgrade_stats;
case DEDUP_TECH_FAILURE:
goto pass2_insert;
case DEDUP_CMP_FAILURE:
goto crc_failure;
case DEDUP_INVALID_ZONE:
goto terminate_early;
case DEDUP_UNDERFLOW:
++dedup_underflows;
de->leaf = *scan_leaf;
goto upgrade_stats;
case DEDUP_VERS_FAILURE:
errx(1, "HAMMER filesystem must be at least "
"version 5 to dedup");
default:
fprintf(stderr, "Unknown error\n");
goto terminate_early;
}
crc_failure:
++dedup_crc_failures;
sha_de = calloc(1, sizeof(*sha_de));
sha_de->leaf = de->leaf;
sha_de->ref_blks = de->u.de.ref_blks;
sha_de->ref_size = de->u.de.ref_size;
if (upgrade_chksum(&sha_de->leaf, sha_de->sha_hash)) {
free(sha_de);
goto pass2_insert;
}
MemoryUse += sizeof(*sha_de);
RB_INIT(&de->u.fict_root);
RB_INSERT(sha_dedup_entry_rb_tree, &de->u.fict_root, sha_de);
de->flags |= HAMMER_DEDUP_ENTRY_FICTITIOUS;
if (upgrade_chksum(scan_leaf, temp.sha_hash))
goto pass2_insert;
sha_de = RB_FIND(sha_dedup_entry_rb_tree, &de->u.fict_root,
&temp);
if (sha_de != NULL) {
goto sha256_failure;
}
sha_de = calloc(1, sizeof(*sha_de));
sha_de->leaf = *scan_leaf;
memcpy(sha_de->sha_hash, temp.sha_hash, SHA256_DIGEST_LENGTH);
RB_INSERT(sha_dedup_entry_rb_tree, &de->u.fict_root, sha_de);
MemoryUse += sizeof(*sha_de);
goto upgrade_stats_sha;
}
upgrade_stats:
de->u.de.ref_blks += 1;
de->u.de.ref_size += scan_leaf->data_len;
return (1);
upgrade_stats_sha:
sha_de->ref_blks += 1;
sha_de->ref_size += scan_leaf->data_len;
return (1);
pass2_insert:
if ((flags & DEDUP_PASS2) == 0) {
pass2_de = calloc(1, sizeof(*pass2_de));
pass2_de->leaf = *scan_leaf;
STAILQ_INSERT_TAIL(&pass2_dedup_queue, pass2_de, sq_entry);
dedup_skipped_size += scan_leaf->data_len;
return (1);
}
terminate_early:
dedup_alloc_size += scan_leaf->data_len;
dedup_ref_size += scan_leaf->data_len;
return (0);
}
static
int
upgrade_chksum(hammer_btree_leaf_elm_t leaf, uint8_t *sha_hash)
{
struct hammer_ioc_data data;
char *buf = malloc(DEDUP_BUF);
SHA256_CTX ctx;
int error;
bzero(&data, sizeof(data));
data.elm = leaf->base;
data.ubuf = buf;
data.size = DEDUP_BUF;
error = 0;
if (ioctl(glob_fd, HAMMERIOC_GET_DATA, &data) < 0) {
fprintf(stderr, "Get-data failed: %s\n", strerror(errno));
error = 1;
goto done;
}
DedupDataReads += leaf->data_len;
if (data.leaf.data_len != leaf->data_len) {
error = 1;
goto done;
}
if (data.leaf.base.btype == HAMMER_BTREE_TYPE_RECORD &&
data.leaf.base.rec_type == HAMMER_RECTYPE_DATA) {
SHA256_Init(&ctx);
SHA256_Update(&ctx, (void *)buf, data.leaf.data_len);
SHA256_Final(sha_hash, &ctx);
}
done:
free(buf);
return (error);
}
static
void
sigAlrm(int signo __unused)
{
SigAlrmFlag = 1;
}
static
void
sigInfo(int signo __unused)
{
SigInfoFlag = 1;
}
static
void
scan_pfs(char *filesystem, scan_pfs_cb_t func, const char *id)
{
struct hammer_ioc_mirror_rw mirror;
hammer_ioc_mrecord_any_t mrec;
struct hammer_btree_leaf_elm elm;
char *buf = malloc(DEDUP_BUF);
char buf1[8];
char buf2[8];
int offset, bytes;
SigInfoFlag = 0;
DedupDataReads = 0;
DedupCurrentRecords = 0;
signal(SIGINFO, sigInfo);
signal(SIGALRM, sigAlrm);
bzero(&mirror, sizeof(mirror));
hammer_key_beg_init(&mirror.key_beg);
hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
if (VerboseOpt) {
fprintf(stderr, "%s: mirror-read: Resuming at object %016jx\n",
id, (uintmax_t)mirror.key_beg.obj_id);
}
}
hammer_key_end_init(&mirror.key_end);
mirror.tid_beg = glob_pfs.ondisk->sync_beg_tid;
mirror.tid_end = glob_pfs.ondisk->sync_end_tid;
mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA;
mirror.ubuf = buf;
mirror.size = DEDUP_BUF;
mirror.pfs_id = glob_pfs.pfs_id;
mirror.shared_uuid = glob_pfs.ondisk->shared_uuid;
if (VerboseOpt && DedupCrcStart == 0) {
printf("%s %s: objspace %016jx:%04x %016jx:%04x\n",
id, filesystem,
(uintmax_t)mirror.key_beg.obj_id,
mirror.key_beg.localization,
(uintmax_t)mirror.key_end.obj_id,
mirror.key_end.localization);
printf("%s %s: pfs_id %d\n",
id, filesystem, glob_pfs.pfs_id);
}
fflush(stdout);
fflush(stderr);
do {
mirror.count = 0;
mirror.pfs_id = glob_pfs.pfs_id;
mirror.shared_uuid = glob_pfs.ondisk->shared_uuid;
if (ioctl(glob_fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
err(1, "Mirror-read %s failed", filesystem);
}
if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
errx(1, "Mirror-read %s fatal error %d",
filesystem, mirror.head.error);
}
if (mirror.count) {
offset = 0;
while (offset < mirror.count) {
mrec = (void *)((char *)buf + offset);
bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
if (offset + bytes > mirror.count) {
errx(1, "Misaligned record");
}
assert((mrec->head.type &
HAMMER_MRECF_TYPE_MASK) ==
HAMMER_MREC_TYPE_REC);
offset += bytes;
elm = mrec->rec.leaf;
if (elm.base.btype != HAMMER_BTREE_TYPE_RECORD)
continue;
if (elm.base.rec_type != HAMMER_RECTYPE_DATA)
continue;
++DedupCurrentRecords;
if (DedupCrcStart != DedupCrcEnd) {
if (elm.data_crc < DedupCrcStart)
continue;
if (DedupCrcEnd &&
elm.data_crc >= DedupCrcEnd) {
continue;
}
}
func(&elm, 0);
}
}
mirror.key_beg = mirror.key_cur;
if (DidInterrupt || SigAlrmFlag) {
if (VerboseOpt) {
fprintf(stderr, "%s\n",
(DidInterrupt ? "Interrupted" : "Timeout"));
}
hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
if (VerboseOpt) {
fprintf(stderr, "Cyclefile %s updated for "
"continuation\n", CyclePath);
}
exit(1);
}
if (SigInfoFlag) {
if (DedupTotalRecords) {
humanize_unsigned(buf1, sizeof(buf1),
DedupDataReads,
"B", 1024);
humanize_unsigned(buf2, sizeof(buf2),
dedup_successes_bytes,
"B", 1024);
fprintf(stderr, "%s count %7jd/%jd "
"(%02d.%02d%%) "
"ioread %s newddup %s\n",
id,
(intmax_t)DedupCurrentRecords,
(intmax_t)DedupTotalRecords,
(int)(DedupCurrentRecords * 100 /
DedupTotalRecords),
(int)(DedupCurrentRecords * 10000 /
DedupTotalRecords % 100),
buf1, buf2);
} else {
fprintf(stderr, "%s count %-7jd\n",
id,
(intmax_t)DedupCurrentRecords);
}
SigInfoFlag = 0;
}
} while (mirror.count != 0);
signal(SIGINFO, SIG_IGN);
signal(SIGALRM, SIG_IGN);
free(buf);
}
static
void
dump_simulated_dedup(void)
{
struct sim_dedup_entry *sim_de;
printf("=== Dumping simulated dedup entries:\n");
RB_FOREACH(sim_de, sim_dedup_entry_rb_tree, &sim_dedup_tree) {
printf("\tcrc=%08x cnt=%ju size=%ju\n",
sim_de->crc,
(uintmax_t)sim_de->ref_blks,
(uintmax_t)sim_de->ref_size);
}
printf("end of dump ===\n");
}
static
void
dump_real_dedup(void)
{
struct dedup_entry *de;
struct sha_dedup_entry *sha_de;
int i;
printf("=== Dumping dedup entries:\n");
RB_FOREACH(de, dedup_entry_rb_tree, &dedup_tree) {
if (de->flags & HAMMER_DEDUP_ENTRY_FICTITIOUS) {
printf("\tcrc=%08x fictitious\n", de->leaf.data_crc);
RB_FOREACH(sha_de, sha_dedup_entry_rb_tree, &de->u.fict_root) {
printf("\t\tcrc=%08x cnt=%ju size=%ju\n\t"
"\t\tsha=",
sha_de->leaf.data_crc,
(uintmax_t)sha_de->ref_blks,
(uintmax_t)sha_de->ref_size);
for (i = 0; i < SHA256_DIGEST_LENGTH; ++i)
printf("%02x", sha_de->sha_hash[i]);
printf("\n");
}
} else {
printf("\tcrc=%08x cnt=%ju size=%ju\n",
de->leaf.data_crc,
(uintmax_t)de->u.de.ref_blks,
(uintmax_t)de->u.de.ref_size);
}
}
printf("end of dump ===\n");
}
static
void
dedup_usage(int code)
{
fprintf(stderr,
"hammer dedup-simulate <filesystem>\n"
"hammer dedup <filesystem>\n"
);
exit(code);
}