mirror of
https://github.com/Zygo/bees.git
synced 2025-08-03 06:13:29 +02:00
Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
88b1e4ca6e | ||
|
c1d7fa13a5 | ||
|
aa39bddb2d | ||
|
1aea2d2f96 | ||
|
673b450671 | ||
|
183b6a5361 | ||
|
b6446d7316 | ||
|
d32f31f411 | ||
|
dd08f6379f | ||
|
58ee297cde | ||
|
a3c0ba0d69 | ||
|
75040789c6 | ||
|
f9a697518d | ||
|
c4ba6ec269 | ||
|
440740201a | ||
|
f6908420ad | ||
|
925b12823e | ||
|
561e604edc | ||
|
30cd375d03 | ||
|
48b7fbda9c | ||
|
85aba7b695 | ||
|
de38b46dd8 | ||
|
0abf6ebb3d | ||
|
360ce7e125 | ||
|
ad11db2ee1 | ||
|
874832dc58 | ||
|
5fe89d85c3 | ||
|
a2b3e1e0c2 | ||
|
aaec931081 |
@@ -120,13 +120,14 @@ The `crawl` event group consists of operations related to scanning btrfs trees t
|
|||||||
|
|
||||||
* `crawl_again`: An inode crawl was restarted because the extent was already locked by another running crawl.
|
* `crawl_again`: An inode crawl was restarted because the extent was already locked by another running crawl.
|
||||||
* `crawl_blacklisted`: An extent was not scanned because it belongs to a blacklisted file.
|
* `crawl_blacklisted`: An extent was not scanned because it belongs to a blacklisted file.
|
||||||
* `crawl_create`: A new subvol or extent crawler was created.
|
|
||||||
* `crawl_deferred_inode`: Two tasks attempted to scan the same inode at the same time, so one was deferred.
|
* `crawl_deferred_inode`: Two tasks attempted to scan the same inode at the same time, so one was deferred.
|
||||||
* `crawl_done`: One pass over a subvol was completed.
|
* `crawl_done`: One pass over a subvol was completed.
|
||||||
* `crawl_discard`: An extent that didn't match the crawler's size tier was discarded.
|
* `crawl_discard_high`: An extent that was too large for the crawler's size tier was discarded.
|
||||||
|
* `crawl_discard_low`: An extent that was too small for the crawler's size tier was discarded.
|
||||||
* `crawl_empty`: A `TREE_SEARCH_V2` ioctl call failed or returned an empty set (usually because all data in the subvol was scanned).
|
* `crawl_empty`: A `TREE_SEARCH_V2` ioctl call failed or returned an empty set (usually because all data in the subvol was scanned).
|
||||||
* `crawl_extent`: The extent crawler queued all references to an extent for processing.
|
* `crawl_extent`: The extent crawler queued all references to an extent for processing.
|
||||||
* `crawl_fail`: A `TREE_SEARCH_V2` ioctl call failed.
|
* `crawl_fail`: A `TREE_SEARCH_V2` ioctl call failed.
|
||||||
|
* `crawl_flop`: Small extent items were not skipped because the next extent started at or before the end of the previous extent.
|
||||||
* `crawl_gen_high`: An extent item in the search results refers to an extent that is newer than the current crawl's `max_transid` allows.
|
* `crawl_gen_high`: An extent item in the search results refers to an extent that is newer than the current crawl's `max_transid` allows.
|
||||||
* `crawl_gen_low`: An extent item in the search results refers to an extent that is older than the current crawl's `min_transid` allows.
|
* `crawl_gen_low`: An extent item in the search results refers to an extent that is older than the current crawl's `min_transid` allows.
|
||||||
* `crawl_hole`: An extent item in the search results refers to a hole.
|
* `crawl_hole`: An extent item in the search results refers to a hole.
|
||||||
@@ -138,6 +139,8 @@ The `crawl` event group consists of operations related to scanning btrfs trees t
|
|||||||
* `crawl_prealloc`: An extent item in the search results refers to a `PREALLOC` extent.
|
* `crawl_prealloc`: An extent item in the search results refers to a `PREALLOC` extent.
|
||||||
* `crawl_push`: An extent item in the search results is suitable for scanning and deduplication.
|
* `crawl_push`: An extent item in the search results is suitable for scanning and deduplication.
|
||||||
* `crawl_scan`: An extent item in the search results is submitted to `BeesContext::scan_forward` for scanning and deduplication.
|
* `crawl_scan`: An extent item in the search results is submitted to `BeesContext::scan_forward` for scanning and deduplication.
|
||||||
|
* `crawl_skip`: Small extent items were skipped because no extent of sufficient size was found within the minimum search distance.
|
||||||
|
* `crawl_skip_ms`: Time spent skipping small extent items.
|
||||||
* `crawl_search`: A `TREE_SEARCH_V2` ioctl call was successful.
|
* `crawl_search`: A `TREE_SEARCH_V2` ioctl call was successful.
|
||||||
* `crawl_throttled`: Extent scan created too many work queue items and was prevented from creating any more.
|
* `crawl_throttled`: Extent scan created too many work queue items and was prevented from creating any more.
|
||||||
* `crawl_tree_block`: Extent scan found and skipped a metadata tree block.
|
* `crawl_tree_block`: Extent scan found and skipped a metadata tree block.
|
||||||
@@ -281,11 +284,14 @@ The `progress` event group consists of events related to progress estimation.
|
|||||||
readahead
|
readahead
|
||||||
---------
|
---------
|
||||||
|
|
||||||
The `readahead` event group consists of events related to calls to `posix_fadvise`.
|
The `readahead` event group consists of events related to data prefetching (formerly calls to `posix_fadvise` or `readahead`, but now emulated in userspace).
|
||||||
|
|
||||||
|
* `readahead_bytes`: Number of bytes prefetched.
|
||||||
|
* `readahead_count`: Number of read calls.
|
||||||
* `readahead_clear`: Number of times the duplicate read cache was cleared.
|
* `readahead_clear`: Number of times the duplicate read cache was cleared.
|
||||||
* `readahead_skip`: Number of times a duplicate read was identified in the cache and skipped.
|
* `readahead_fail`: Number of read errors during prefetch.
|
||||||
* `readahead_ms`: Total time spent emulating readahead in user-space (kernel readahead is not measured).
|
* `readahead_ms`: Total time spent emulating readahead in user-space (kernel readahead is not measured).
|
||||||
|
* `readahead_skip`: Number of times a duplicate read was identified in the cache and skipped.
|
||||||
* `readahead_unread_ms`: Total time spent running `posix_fadvise(..., POSIX_FADV_DONTNEED)`.
|
* `readahead_unread_ms`: Total time spent running `posix_fadvise(..., POSIX_FADV_DONTNEED)`.
|
||||||
|
|
||||||
replacedst
|
replacedst
|
||||||
|
@@ -173,34 +173,42 @@ namespace crucible {
|
|||||||
void get_sums(uint64_t logical, size_t count, function<void(uint64_t logical, const uint8_t *buf, size_t count)> output);
|
void get_sums(uint64_t logical, size_t count, function<void(uint64_t logical, const uint8_t *buf, size_t count)> output);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Fetch extent items from extent tree
|
/// Fetch extent items from extent tree.
|
||||||
|
/// Does not filter out metadata! See BtrfsDataExtentTreeFetcher for that.
|
||||||
class BtrfsExtentItemFetcher : public BtrfsTreeObjectFetcher {
|
class BtrfsExtentItemFetcher : public BtrfsTreeObjectFetcher {
|
||||||
public:
|
public:
|
||||||
BtrfsExtentItemFetcher(const Fd &fd);
|
BtrfsExtentItemFetcher(const Fd &fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Fetch extent refs from an inode
|
/// Fetch extent refs from an inode. Caller must set the tree and objectid.
|
||||||
class BtrfsExtentDataFetcher : public BtrfsTreeOffsetFetcher {
|
class BtrfsExtentDataFetcher : public BtrfsTreeOffsetFetcher {
|
||||||
public:
|
public:
|
||||||
BtrfsExtentDataFetcher(const Fd &fd);
|
BtrfsExtentDataFetcher(const Fd &fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Fetch inodes from a subvol
|
/// Fetch raw inode items
|
||||||
class BtrfsFsTreeFetcher : public BtrfsTreeObjectFetcher {
|
|
||||||
public:
|
|
||||||
BtrfsFsTreeFetcher(const Fd &fd, uint64_t subvol);
|
|
||||||
};
|
|
||||||
|
|
||||||
class BtrfsInodeFetcher : public BtrfsTreeObjectFetcher {
|
class BtrfsInodeFetcher : public BtrfsTreeObjectFetcher {
|
||||||
public:
|
public:
|
||||||
BtrfsInodeFetcher(const Fd &fd);
|
BtrfsInodeFetcher(const Fd &fd);
|
||||||
BtrfsTreeItem stat(uint64_t subvol, uint64_t inode);
|
BtrfsTreeItem stat(uint64_t subvol, uint64_t inode);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Fetch a root (subvol) item
|
||||||
class BtrfsRootFetcher : public BtrfsTreeObjectFetcher {
|
class BtrfsRootFetcher : public BtrfsTreeObjectFetcher {
|
||||||
public:
|
public:
|
||||||
BtrfsRootFetcher(const Fd &fd);
|
BtrfsRootFetcher(const Fd &fd);
|
||||||
BtrfsTreeItem root(uint64_t subvol);
|
BtrfsTreeItem root(uint64_t subvol);
|
||||||
|
BtrfsTreeItem root_backref(uint64_t subvol);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Fetch data extent items from extent tree, skipping metadata-only block groups
|
||||||
|
class BtrfsDataExtentTreeFetcher : public BtrfsExtentItemFetcher {
|
||||||
|
BtrfsTreeItem m_current_bg;
|
||||||
|
BtrfsTreeOffsetFetcher m_chunk_tree;
|
||||||
|
protected:
|
||||||
|
virtual void next_sk(BtrfsIoctlSearchKey &key, const BtrfsIoctlSearchHeader &hdr) override;
|
||||||
|
public:
|
||||||
|
BtrfsDataExtentTreeFetcher(const Fd &fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -201,11 +201,13 @@ namespace crucible {
|
|||||||
static thread_local size_t s_calls;
|
static thread_local size_t s_calls;
|
||||||
static thread_local size_t s_loops;
|
static thread_local size_t s_loops;
|
||||||
static thread_local size_t s_loops_empty;
|
static thread_local size_t s_loops_empty;
|
||||||
|
static thread_local shared_ptr<ostream> s_debug_ostream;
|
||||||
};
|
};
|
||||||
|
|
||||||
ostream & operator<<(ostream &os, const btrfs_ioctl_search_key &key);
|
ostream & operator<<(ostream &os, const btrfs_ioctl_search_key &key);
|
||||||
ostream & operator<<(ostream &os, const BtrfsIoctlSearchKey &key);
|
ostream & operator<<(ostream &os, const BtrfsIoctlSearchKey &key);
|
||||||
|
|
||||||
|
string btrfs_chunk_type_ntoa(uint64_t type);
|
||||||
string btrfs_search_type_ntoa(unsigned type);
|
string btrfs_search_type_ntoa(unsigned type);
|
||||||
string btrfs_search_objectid_ntoa(uint64_t objectid);
|
string btrfs_search_objectid_ntoa(uint64_t objectid);
|
||||||
string btrfs_compress_type_ntoa(uint8_t type);
|
string btrfs_compress_type_ntoa(uint8_t type);
|
||||||
@@ -246,9 +248,11 @@ namespace crucible {
|
|||||||
struct BtrfsIoctlFsInfoArgs : public btrfs_ioctl_fs_info_args_v3 {
|
struct BtrfsIoctlFsInfoArgs : public btrfs_ioctl_fs_info_args_v3 {
|
||||||
BtrfsIoctlFsInfoArgs();
|
BtrfsIoctlFsInfoArgs();
|
||||||
void do_ioctl(int fd);
|
void do_ioctl(int fd);
|
||||||
|
bool do_ioctl_nothrow(int fd);
|
||||||
uint16_t csum_type() const;
|
uint16_t csum_type() const;
|
||||||
uint16_t csum_size() const;
|
uint16_t csum_size() const;
|
||||||
uint64_t generation() const;
|
uint64_t generation() const;
|
||||||
|
vector<uint8_t> fsid() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
ostream & operator<<(ostream &os, const BtrfsIoctlFsInfoArgs &a);
|
ostream & operator<<(ostream &os, const BtrfsIoctlFsInfoArgs &a);
|
||||||
|
@@ -1,11 +1,46 @@
|
|||||||
#ifndef CRUCIBLE_OPENAT2_H
|
#ifndef CRUCIBLE_OPENAT2_H
|
||||||
#define CRUCIBLE_OPENAT2_H
|
#define CRUCIBLE_OPENAT2_H
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
// Compatibility for building on old libc for new kernel
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
|
||||||
|
|
||||||
#include <linux/openat2.h>
|
#include <linux/openat2.h>
|
||||||
|
|
||||||
#include <fcntl.h>
|
#else
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <unistd.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#ifndef RESOLVE_NO_XDEV
|
||||||
|
#define RESOLVE_NO_XDEV 1
|
||||||
|
|
||||||
|
// RESOLVE_NO_XDEV was there from the beginning of openat2,
|
||||||
|
// so if that's missing, so is open_how
|
||||||
|
|
||||||
|
struct open_how {
|
||||||
|
__u64 flags;
|
||||||
|
__u64 mode;
|
||||||
|
__u64 resolve;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RESOLVE_NO_MAGICLINKS
|
||||||
|
#define RESOLVE_NO_MAGICLINKS 2
|
||||||
|
#endif
|
||||||
|
#ifndef RESOLVE_NO_SYMLINKS
|
||||||
|
#define RESOLVE_NO_SYMLINKS 4
|
||||||
|
#endif
|
||||||
|
#ifndef RESOLVE_BENEATH
|
||||||
|
#define RESOLVE_BENEATH 8
|
||||||
|
#endif
|
||||||
|
#ifndef RESOLVE_IN_ROOT
|
||||||
|
#define RESOLVE_IN_ROOT 16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // Linux version >= v5.6
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#if 1
|
#if 0
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#define DINIT(__x) __x
|
#define DINIT(__x) __x
|
||||||
|
@@ -5,6 +5,12 @@
|
|||||||
#include "crucible/hexdump.h"
|
#include "crucible/hexdump.h"
|
||||||
#include "crucible/seeker.h"
|
#include "crucible/seeker.h"
|
||||||
|
|
||||||
|
#define CRUCIBLE_BTRFS_TREE_DEBUG(x) do { \
|
||||||
|
if (BtrfsIoctlSearchKey::s_debug_ostream) { \
|
||||||
|
(*BtrfsIoctlSearchKey::s_debug_ostream) << x; \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
namespace crucible {
|
namespace crucible {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -355,6 +361,7 @@ namespace crucible {
|
|||||||
BtrfsTreeItem
|
BtrfsTreeItem
|
||||||
BtrfsTreeFetcher::at(uint64_t logical)
|
BtrfsTreeFetcher::at(uint64_t logical)
|
||||||
{
|
{
|
||||||
|
CRUCIBLE_BTRFS_TREE_DEBUG("at " << logical);
|
||||||
BtrfsIoctlSearchKey &sk = m_sk;
|
BtrfsIoctlSearchKey &sk = m_sk;
|
||||||
fill_sk(sk, logical);
|
fill_sk(sk, logical);
|
||||||
// Exact match, should return 0 or 1 items
|
// Exact match, should return 0 or 1 items
|
||||||
@@ -397,16 +404,17 @@ namespace crucible {
|
|||||||
BtrfsTreeFetcher::rlower_bound(uint64_t logical)
|
BtrfsTreeFetcher::rlower_bound(uint64_t logical)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
#define BTFRLB_DEBUG(x) do { cerr << x; } while (false)
|
static bool btfrlb_debug = getenv("BTFLRB_DEBUG");
|
||||||
|
#define BTFRLB_DEBUG(x) do { if (btfrlb_debug) cerr << x; } while (false)
|
||||||
#else
|
#else
|
||||||
#define BTFRLB_DEBUG(x) do { } while (false)
|
#define BTFRLB_DEBUG(x) CRUCIBLE_BTRFS_TREE_DEBUG(x)
|
||||||
#endif
|
#endif
|
||||||
BtrfsTreeItem closest_item;
|
BtrfsTreeItem closest_item;
|
||||||
uint64_t closest_logical = 0;
|
uint64_t closest_logical = 0;
|
||||||
BtrfsIoctlSearchKey &sk = m_sk;
|
BtrfsIoctlSearchKey &sk = m_sk;
|
||||||
size_t loops = 0;
|
size_t loops = 0;
|
||||||
BTFRLB_DEBUG("rlower_bound: " << to_hex(logical) << endl);
|
BTFRLB_DEBUG("rlower_bound: " << to_hex(logical) << " in tree " << tree() << endl);
|
||||||
seek_backward(scale_logical(logical), [&](uint64_t lower_bound, uint64_t upper_bound) {
|
seek_backward(scale_logical(logical), [&](uint64_t const lower_bound, uint64_t const upper_bound) {
|
||||||
++loops;
|
++loops;
|
||||||
fill_sk(sk, unscale_logical(min(scaled_max_logical(), lower_bound)));
|
fill_sk(sk, unscale_logical(min(scaled_max_logical(), lower_bound)));
|
||||||
set<uint64_t> rv;
|
set<uint64_t> rv;
|
||||||
@@ -416,29 +424,31 @@ namespace crucible {
|
|||||||
BTFRLB_DEBUG("fetch: loop " << loops << " lower_bound..upper_bound " << to_hex(lower_bound) << ".." << to_hex(upper_bound));
|
BTFRLB_DEBUG("fetch: loop " << loops << " lower_bound..upper_bound " << to_hex(lower_bound) << ".." << to_hex(upper_bound));
|
||||||
for (auto &i : sk.m_result) {
|
for (auto &i : sk.m_result) {
|
||||||
next_sk(sk, i);
|
next_sk(sk, i);
|
||||||
const auto this_logical = hdr_logical(i);
|
// If hdr_stop or !hdr_match, don't inspect the item
|
||||||
const auto scaled_hdr_logical = scale_logical(this_logical);
|
if (hdr_stop(i)) {
|
||||||
BTFRLB_DEBUG(" " << to_hex(scaled_hdr_logical));
|
rv.insert(numeric_limits<uint64_t>::max());
|
||||||
if (hdr_match(i)) {
|
BTFRLB_DEBUG("(stop)");
|
||||||
if (this_logical <= logical && this_logical > closest_logical) {
|
|
||||||
closest_logical = this_logical;
|
|
||||||
closest_item = i;
|
|
||||||
}
|
|
||||||
BTFRLB_DEBUG("(match)");
|
|
||||||
rv.insert(scaled_hdr_logical);
|
|
||||||
}
|
|
||||||
if (scaled_hdr_logical > upper_bound || hdr_stop(i)) {
|
|
||||||
if (scaled_hdr_logical >= upper_bound) {
|
|
||||||
BTFRLB_DEBUG("(" << to_hex(scaled_hdr_logical) << " >= " << to_hex(upper_bound) << ")");
|
|
||||||
}
|
|
||||||
if (hdr_stop(i)) {
|
|
||||||
rv.insert(numeric_limits<uint64_t>::max());
|
|
||||||
BTFRLB_DEBUG("(stop)");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
BTFRLB_DEBUG("(cont'd)");
|
|
||||||
}
|
}
|
||||||
|
if (!hdr_match(i)) {
|
||||||
|
BTFRLB_DEBUG("(no match)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto this_logical = hdr_logical(i);
|
||||||
|
BTFRLB_DEBUG(" " << to_hex(this_logical) << " " << i);
|
||||||
|
const auto scaled_hdr_logical = scale_logical(this_logical);
|
||||||
|
BTFRLB_DEBUG(" " << "(match)");
|
||||||
|
if (this_logical <= logical && this_logical > closest_logical) {
|
||||||
|
closest_logical = this_logical;
|
||||||
|
closest_item = i;
|
||||||
|
BTFRLB_DEBUG("(closest)");
|
||||||
|
}
|
||||||
|
rv.insert(scaled_hdr_logical);
|
||||||
|
if (scaled_hdr_logical > upper_bound) {
|
||||||
|
BTFRLB_DEBUG("(" << to_hex(scaled_hdr_logical) << " >= " << to_hex(upper_bound) << ")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BTFRLB_DEBUG("(cont'd)");
|
||||||
}
|
}
|
||||||
BTFRLB_DEBUG(endl);
|
BTFRLB_DEBUG(endl);
|
||||||
// We might get a search result that contains only non-matching items.
|
// We might get a search result that contains only non-matching items.
|
||||||
@@ -474,6 +484,7 @@ namespace crucible {
|
|||||||
BtrfsTreeItem
|
BtrfsTreeItem
|
||||||
BtrfsTreeFetcher::next(uint64_t logical)
|
BtrfsTreeFetcher::next(uint64_t logical)
|
||||||
{
|
{
|
||||||
|
CRUCIBLE_BTRFS_TREE_DEBUG("next " << logical);
|
||||||
const auto scaled_logical = scale_logical(logical);
|
const auto scaled_logical = scale_logical(logical);
|
||||||
if (scaled_logical + 1 > scaled_max_logical()) {
|
if (scaled_logical + 1 > scaled_max_logical()) {
|
||||||
return BtrfsTreeItem();
|
return BtrfsTreeItem();
|
||||||
@@ -484,6 +495,7 @@ namespace crucible {
|
|||||||
BtrfsTreeItem
|
BtrfsTreeItem
|
||||||
BtrfsTreeFetcher::prev(uint64_t logical)
|
BtrfsTreeFetcher::prev(uint64_t logical)
|
||||||
{
|
{
|
||||||
|
CRUCIBLE_BTRFS_TREE_DEBUG("prev " << logical);
|
||||||
const auto scaled_logical = scale_logical(logical);
|
const auto scaled_logical = scale_logical(logical);
|
||||||
if (scaled_logical < 1) {
|
if (scaled_logical < 1) {
|
||||||
return BtrfsTreeItem();
|
return BtrfsTreeItem();
|
||||||
@@ -568,9 +580,10 @@ namespace crucible {
|
|||||||
BtrfsCsumTreeFetcher::get_sums(uint64_t const logical, size_t count, function<void(uint64_t logical, const uint8_t *buf, size_t bytes)> output)
|
BtrfsCsumTreeFetcher::get_sums(uint64_t const logical, size_t count, function<void(uint64_t logical, const uint8_t *buf, size_t bytes)> output)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
#define BCTFGS_DEBUG(x) do { cerr << x; } while (false)
|
static bool bctfgs_debug = getenv("BCTFGS_DEBUG");
|
||||||
|
#define BCTFGS_DEBUG(x) do { if (bctfgs_debug) cerr << x; } while (false)
|
||||||
#else
|
#else
|
||||||
#define BCTFGS_DEBUG(x) do { } while (false)
|
#define BCTFGS_DEBUG(x) CRUCIBLE_BTRFS_TREE_DEBUG(x)
|
||||||
#endif
|
#endif
|
||||||
const uint64_t logical_end = logical + count * block_size();
|
const uint64_t logical_end = logical + count * block_size();
|
||||||
BtrfsTreeItem bti = rlower_bound(logical);
|
BtrfsTreeItem bti = rlower_bound(logical);
|
||||||
@@ -662,14 +675,6 @@ namespace crucible {
|
|||||||
type(BTRFS_EXTENT_DATA_KEY);
|
type(BTRFS_EXTENT_DATA_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
BtrfsFsTreeFetcher::BtrfsFsTreeFetcher(const Fd &new_fd, uint64_t subvol) :
|
|
||||||
BtrfsTreeObjectFetcher(new_fd)
|
|
||||||
{
|
|
||||||
tree(subvol);
|
|
||||||
type(BTRFS_EXTENT_DATA_KEY);
|
|
||||||
scale_size(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
BtrfsInodeFetcher::BtrfsInodeFetcher(const Fd &fd) :
|
BtrfsInodeFetcher::BtrfsInodeFetcher(const Fd &fd) :
|
||||||
BtrfsTreeObjectFetcher(fd)
|
BtrfsTreeObjectFetcher(fd)
|
||||||
{
|
{
|
||||||
@@ -693,18 +698,86 @@ namespace crucible {
|
|||||||
BtrfsTreeObjectFetcher(fd)
|
BtrfsTreeObjectFetcher(fd)
|
||||||
{
|
{
|
||||||
tree(BTRFS_ROOT_TREE_OBJECTID);
|
tree(BTRFS_ROOT_TREE_OBJECTID);
|
||||||
type(BTRFS_ROOT_ITEM_KEY);
|
|
||||||
scale_size(1);
|
scale_size(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
BtrfsTreeItem
|
BtrfsTreeItem
|
||||||
BtrfsRootFetcher::root(uint64_t subvol)
|
BtrfsRootFetcher::root(const uint64_t subvol)
|
||||||
{
|
{
|
||||||
|
const auto my_type = BTRFS_ROOT_ITEM_KEY;
|
||||||
|
type(my_type);
|
||||||
const auto item = at(subvol);
|
const auto item = at(subvol);
|
||||||
if (!!item) {
|
if (!!item) {
|
||||||
THROW_CHECK2(runtime_error, item.objectid(), subvol, subvol == item.objectid());
|
THROW_CHECK2(runtime_error, item.objectid(), subvol, subvol == item.objectid());
|
||||||
THROW_CHECK2(runtime_error, item.type(), BTRFS_ROOT_ITEM_KEY, item.type() == BTRFS_ROOT_ITEM_KEY);
|
THROW_CHECK2(runtime_error, item.type(), my_type, item.type() == my_type);
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BtrfsTreeItem
|
||||||
|
BtrfsRootFetcher::root_backref(const uint64_t subvol)
|
||||||
|
{
|
||||||
|
const auto my_type = BTRFS_ROOT_BACKREF_KEY;
|
||||||
|
type(my_type);
|
||||||
|
const auto item = at(subvol);
|
||||||
|
if (!!item) {
|
||||||
|
THROW_CHECK2(runtime_error, item.objectid(), subvol, subvol == item.objectid());
|
||||||
|
THROW_CHECK2(runtime_error, item.type(), my_type, item.type() == my_type);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
BtrfsDataExtentTreeFetcher::BtrfsDataExtentTreeFetcher(const Fd &fd) :
|
||||||
|
BtrfsExtentItemFetcher(fd),
|
||||||
|
m_chunk_tree(fd)
|
||||||
|
{
|
||||||
|
tree(BTRFS_EXTENT_TREE_OBJECTID);
|
||||||
|
type(BTRFS_EXTENT_ITEM_KEY);
|
||||||
|
m_chunk_tree.tree(BTRFS_CHUNK_TREE_OBJECTID);
|
||||||
|
m_chunk_tree.type(BTRFS_CHUNK_ITEM_KEY);
|
||||||
|
m_chunk_tree.objectid(BTRFS_FIRST_CHUNK_TREE_OBJECTID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BtrfsDataExtentTreeFetcher::next_sk(BtrfsIoctlSearchKey &key, const BtrfsIoctlSearchHeader &hdr)
|
||||||
|
{
|
||||||
|
key.min_type = key.max_type = type();
|
||||||
|
key.max_objectid = key.max_offset = numeric_limits<uint64_t>::max();
|
||||||
|
key.min_offset = 0;
|
||||||
|
key.min_objectid = hdr.objectid;
|
||||||
|
const auto step = scale_size();
|
||||||
|
if (key.min_objectid < numeric_limits<uint64_t>::max() - step) {
|
||||||
|
key.min_objectid += step;
|
||||||
|
} else {
|
||||||
|
key.min_objectid = numeric_limits<uint64_t>::max();
|
||||||
|
}
|
||||||
|
// If we're still in our current block group, check here
|
||||||
|
if (!!m_current_bg) {
|
||||||
|
const auto bg_begin = m_current_bg.offset();
|
||||||
|
const auto bg_end = bg_begin + m_current_bg.chunk_length();
|
||||||
|
// If we are still in our current block group, return early
|
||||||
|
if (key.min_objectid >= bg_begin && key.min_objectid < bg_end) return;
|
||||||
|
}
|
||||||
|
// We don't have a current block group or we're out of range
|
||||||
|
// Find the chunk that this bytenr belongs to
|
||||||
|
m_current_bg = m_chunk_tree.rlower_bound(key.min_objectid);
|
||||||
|
// Make sure it's a data block group
|
||||||
|
while (!!m_current_bg) {
|
||||||
|
// Data block group, stop here
|
||||||
|
if (m_current_bg.chunk_type() & BTRFS_BLOCK_GROUP_DATA) break;
|
||||||
|
// Not a data block group, skip to end
|
||||||
|
key.min_objectid = m_current_bg.offset() + m_current_bg.chunk_length();
|
||||||
|
m_current_bg = m_chunk_tree.lower_bound(key.min_objectid);
|
||||||
|
}
|
||||||
|
if (!m_current_bg) {
|
||||||
|
// Ran out of data block groups, stop here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check to see if bytenr is in the current data block group
|
||||||
|
const auto bg_begin = m_current_bg.offset();
|
||||||
|
if (key.min_objectid < bg_begin) {
|
||||||
|
// Move forward to start of data block group
|
||||||
|
key.min_objectid = bg_begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
43
lib/fs.cc
43
lib/fs.cc
@@ -757,6 +757,7 @@ namespace crucible {
|
|||||||
thread_local size_t BtrfsIoctlSearchKey::s_calls = 0;
|
thread_local size_t BtrfsIoctlSearchKey::s_calls = 0;
|
||||||
thread_local size_t BtrfsIoctlSearchKey::s_loops = 0;
|
thread_local size_t BtrfsIoctlSearchKey::s_loops = 0;
|
||||||
thread_local size_t BtrfsIoctlSearchKey::s_loops_empty = 0;
|
thread_local size_t BtrfsIoctlSearchKey::s_loops_empty = 0;
|
||||||
|
thread_local shared_ptr<ostream> BtrfsIoctlSearchKey::s_debug_ostream;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BtrfsIoctlSearchKey::do_ioctl_nothrow(int fd)
|
BtrfsIoctlSearchKey::do_ioctl_nothrow(int fd)
|
||||||
@@ -776,6 +777,9 @@ namespace crucible {
|
|||||||
ioctl_ptr = ioctl_arg.get<btrfs_ioctl_search_args_v2>();
|
ioctl_ptr = ioctl_arg.get<btrfs_ioctl_search_args_v2>();
|
||||||
ioctl_ptr->key = static_cast<const btrfs_ioctl_search_key&>(*this);
|
ioctl_ptr->key = static_cast<const btrfs_ioctl_search_key&>(*this);
|
||||||
ioctl_ptr->buf_size = buf_size;
|
ioctl_ptr->buf_size = buf_size;
|
||||||
|
if (s_debug_ostream) {
|
||||||
|
(*s_debug_ostream) << "bisk " << (ioctl_ptr->key) << "\n";
|
||||||
|
}
|
||||||
// Don't bother supporting V1. Kernels that old have other problems.
|
// Don't bother supporting V1. Kernels that old have other problems.
|
||||||
int rv = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, ioctl_arg.data());
|
int rv = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, ioctl_arg.data());
|
||||||
++s_calls;
|
++s_calls;
|
||||||
@@ -881,6 +885,26 @@ namespace crucible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
btrfs_chunk_type_ntoa(uint64_t type)
|
||||||
|
{
|
||||||
|
static const bits_ntoa_table table[] = {
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_DATA),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_METADATA),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_SYSTEM),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_DUP),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_RAID0),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_RAID1),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_RAID10),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_RAID1C3),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_RAID1C4),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_RAID5),
|
||||||
|
NTOA_TABLE_ENTRY_BITS(BTRFS_BLOCK_GROUP_RAID6),
|
||||||
|
NTOA_TABLE_ENTRY_END()
|
||||||
|
};
|
||||||
|
return bits_ntoa(type, table);
|
||||||
|
}
|
||||||
|
|
||||||
string
|
string
|
||||||
btrfs_search_type_ntoa(unsigned type)
|
btrfs_search_type_ntoa(unsigned type)
|
||||||
{
|
{
|
||||||
@@ -1138,11 +1162,17 @@ namespace crucible {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
BtrfsIoctlFsInfoArgs::do_ioctl(int fd)
|
BtrfsIoctlFsInfoArgs::do_ioctl_nothrow(int const fd)
|
||||||
{
|
{
|
||||||
btrfs_ioctl_fs_info_args_v3 *p = static_cast<btrfs_ioctl_fs_info_args_v3 *>(this);
|
btrfs_ioctl_fs_info_args_v3 *p = static_cast<btrfs_ioctl_fs_info_args_v3 *>(this);
|
||||||
if (ioctl(fd, BTRFS_IOC_FS_INFO, p)) {
|
return 0 == ioctl(fd, BTRFS_IOC_FS_INFO, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BtrfsIoctlFsInfoArgs::do_ioctl(int const fd)
|
||||||
|
{
|
||||||
|
if (!do_ioctl_nothrow(fd)) {
|
||||||
THROW_ERRNO("BTRFS_IOC_FS_INFO: fd " << fd);
|
THROW_ERRNO("BTRFS_IOC_FS_INFO: fd " << fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1159,6 +1189,13 @@ namespace crucible {
|
|||||||
return this->btrfs_ioctl_fs_info_args_v3::csum_size;
|
return this->btrfs_ioctl_fs_info_args_v3::csum_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<uint8_t>
|
||||||
|
BtrfsIoctlFsInfoArgs::fsid() const
|
||||||
|
{
|
||||||
|
const auto begin = btrfs_ioctl_fs_info_args_v3::fsid;
|
||||||
|
return vector<uint8_t>(begin, begin + BTRFS_FSID_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
BtrfsIoctlFsInfoArgs::generation() const
|
BtrfsIoctlFsInfoArgs::generation() const
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,27 @@
|
|||||||
#include "crucible/openat2.h"
|
#include "crucible/openat2.h"
|
||||||
|
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
// Compatibility for building on old libc for new kernel
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
|
||||||
|
|
||||||
|
// Every arch that defines this uses 437, except Alpha, where 437 is
|
||||||
|
// mq_getsetattr.
|
||||||
|
|
||||||
|
#ifndef SYS_openat2
|
||||||
|
#ifdef __alpha__
|
||||||
|
#define SYS_openat2 547
|
||||||
|
#else
|
||||||
|
#define SYS_openat2 437
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // Linux version >= v5.6
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -7,7 +29,12 @@ __attribute__((weak))
|
|||||||
openat2(int const dirfd, const char *const pathname, struct open_how *const how, size_t const size)
|
openat2(int const dirfd, const char *const pathname, struct open_how *const how, size_t const size)
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
|
#ifdef SYS_openat2
|
||||||
return syscall(SYS_openat2, dirfd, pathname, how, size);
|
return syscall(SYS_openat2, dirfd, pathname, how, size);
|
||||||
|
#else
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# if not called from systemd try to replicate mount unsharing on ctrl+c
|
||||||
|
# see: https://github.com/Zygo/bees/issues/281
|
||||||
|
if [ -z "${SYSTEMD_EXEC_PID}" -a -z "${UNSHARE_DONE}" ]; then
|
||||||
|
UNSHARE_DONE=true
|
||||||
|
export UNSHARE_DONE
|
||||||
|
exec unshare -m --propagation private -- "$0" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
## Helpful functions
|
## Helpful functions
|
||||||
INFO(){ echo "INFO:" "$@"; }
|
INFO(){ echo "INFO:" "$@"; }
|
||||||
ERRO(){ echo "ERROR:" "$@"; exit 1; }
|
ERRO(){ echo "ERROR:" "$@"; exit 1; }
|
||||||
@@ -108,13 +116,11 @@ mkdir -p "$WORK_DIR" || exit 1
|
|||||||
INFO "MOUNT DIR: $MNT_DIR"
|
INFO "MOUNT DIR: $MNT_DIR"
|
||||||
mkdir -p "$MNT_DIR" || exit 1
|
mkdir -p "$MNT_DIR" || exit 1
|
||||||
|
|
||||||
mount --make-private -osubvolid=5 /dev/disk/by-uuid/$UUID "$MNT_DIR" || exit 1
|
mount --make-private -osubvolid=5,nodev,noexec /dev/disk/by-uuid/$UUID "$MNT_DIR" || exit 1
|
||||||
|
|
||||||
if [ ! -d "$BEESHOME" ]; then
|
if [ ! -d "$BEESHOME" ]; then
|
||||||
INFO "Create subvol $BEESHOME for store bees data"
|
INFO "Create subvol $BEESHOME for store bees data"
|
||||||
btrfs sub cre "$BEESHOME"
|
btrfs sub cre "$BEESHOME"
|
||||||
else
|
|
||||||
btrfs sub show "$BEESHOME" &> /dev/null || ERRO "$BEESHOME MUST BE A SUBVOL!"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check DB size
|
# Check DB size
|
||||||
|
@@ -259,7 +259,7 @@ BeesContext::dedup(const BeesRangePair &brp_in)
|
|||||||
BEESCOUNTADD(dedup_bytes, brp.first.size());
|
BEESCOUNTADD(dedup_bytes, brp.first.size());
|
||||||
} else {
|
} else {
|
||||||
BEESCOUNT(dedup_miss);
|
BEESCOUNT(dedup_miss);
|
||||||
BEESLOGWARN("NO Dedup! " << brp);
|
BEESLOGINFO("NO Dedup! " << brp);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.reset();
|
lock.reset();
|
||||||
@@ -373,7 +373,7 @@ BeesContext::scan_one_extent(const BeesFileRange &bfr, const Extent &e)
|
|||||||
Extent::OBSCURED | Extent::PREALLOC
|
Extent::OBSCURED | Extent::PREALLOC
|
||||||
)) {
|
)) {
|
||||||
BEESCOUNT(scan_interesting);
|
BEESCOUNT(scan_interesting);
|
||||||
BEESLOGWARN("Interesting extent flags " << e << " from fd " << name_fd(bfr.fd()));
|
BEESLOGINFO("Interesting extent flags " << e << " from fd " << name_fd(bfr.fd()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.flags() & Extent::HOLE) {
|
if (e.flags() & Extent::HOLE) {
|
||||||
@@ -534,7 +534,7 @@ BeesContext::scan_one_extent(const BeesFileRange &bfr, const Extent &e)
|
|||||||
|
|
||||||
// Hash is toxic
|
// Hash is toxic
|
||||||
if (found_addr.is_toxic()) {
|
if (found_addr.is_toxic()) {
|
||||||
BEESLOGWARN("WORKAROUND: abandoned toxic match for hash " << hash << " addr " << found_addr << " matching bbd " << bbd);
|
BEESLOGDEBUG("WORKAROUND: abandoned toxic match for hash " << hash << " addr " << found_addr << " matching bbd " << bbd);
|
||||||
// Don't push these back in because we'll never delete them.
|
// Don't push these back in because we'll never delete them.
|
||||||
// Extents may become non-toxic so give them a chance to expire.
|
// Extents may become non-toxic so give them a chance to expire.
|
||||||
// hash_table->push_front_hash_addr(hash, found_addr);
|
// hash_table->push_front_hash_addr(hash, found_addr);
|
||||||
@@ -556,7 +556,7 @@ BeesContext::scan_one_extent(const BeesFileRange &bfr, const Extent &e)
|
|||||||
BeesResolver resolved(m_ctx, found_addr);
|
BeesResolver resolved(m_ctx, found_addr);
|
||||||
// Toxic extents are really toxic
|
// Toxic extents are really toxic
|
||||||
if (resolved.is_toxic()) {
|
if (resolved.is_toxic()) {
|
||||||
BEESLOGWARN("WORKAROUND: discovered toxic match at found_addr " << found_addr << " matching bbd " << bbd);
|
BEESLOGDEBUG("WORKAROUND: discovered toxic match at found_addr " << found_addr << " matching bbd " << bbd);
|
||||||
BEESCOUNT(scan_toxic_match);
|
BEESCOUNT(scan_toxic_match);
|
||||||
// Make sure we never see this hash again.
|
// Make sure we never see this hash again.
|
||||||
// It has become toxic since it was inserted into the hash table.
|
// It has become toxic since it was inserted into the hash table.
|
||||||
@@ -917,7 +917,7 @@ BeesContext::scan_forward(const BeesFileRange &bfr_in)
|
|||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (bfr.begin() >= bfr.file_size()) {
|
if (bfr.begin() >= bfr.file_size()) {
|
||||||
BEESLOGWARN("past EOF: " << bfr);
|
BEESLOGDEBUG("past EOF: " << bfr);
|
||||||
BEESCOUNT(scanf_eof);
|
BEESCOUNT(scanf_eof);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -797,7 +797,7 @@ BeesHashTable::BeesHashTable(shared_ptr<BeesContext> ctx, string filename, off_t
|
|||||||
for (auto fp = madv_flags; fp->value; ++fp) {
|
for (auto fp = madv_flags; fp->value; ++fp) {
|
||||||
BEESTOOLONG("madvise(" << fp->name << ")");
|
BEESTOOLONG("madvise(" << fp->name << ")");
|
||||||
if (madvise(m_byte_ptr, m_size, fp->value)) {
|
if (madvise(m_byte_ptr, m_size, fp->value)) {
|
||||||
BEESLOGWARN("madvise(..., " << fp->name << "): " << strerror(errno) << " (ignored)");
|
BEESLOGNOTICE("madvise(..., " << fp->name << "): " << strerror(errno) << " (ignored)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,8 +811,18 @@ BeesHashTable::BeesHashTable(shared_ptr<BeesContext> ctx, string filename, off_t
|
|||||||
prefetch_loop();
|
prefetch_loop();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Blacklist might fail if the hash table is not stored on a btrfs
|
// Blacklist might fail if the hash table is not stored on a btrfs,
|
||||||
|
// or if it's on a _different_ btrfs
|
||||||
catch_all([&]() {
|
catch_all([&]() {
|
||||||
|
// Root is definitely a btrfs
|
||||||
|
BtrfsIoctlFsInfoArgs root_info;
|
||||||
|
root_info.do_ioctl(m_ctx->root_fd());
|
||||||
|
// Hash might not be a btrfs
|
||||||
|
BtrfsIoctlFsInfoArgs hash_info;
|
||||||
|
if (hash_info.do_ioctl_nothrow(m_fd)) return;
|
||||||
|
// If Hash is a btrfs, Root must be the same one
|
||||||
|
if (root_info.fsid() != hash_info.fsid()) return;
|
||||||
|
// Hash is on the same one, blacklist it
|
||||||
m_ctx->blacklist_insert(BeesFileId(m_fd));
|
m_ctx->blacklist_insert(BeesFileId(m_fd));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -457,7 +457,7 @@ BeesRangePair::grow(shared_ptr<BeesContext> ctx, bool constrained)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found_toxic) {
|
if (found_toxic) {
|
||||||
BEESLOGWARN("WORKAROUND: found toxic hash in " << first_bbd << " while extending backward:\n" << *this);
|
BEESLOGDEBUG("WORKAROUND: found toxic hash in " << first_bbd << " while extending backward:\n" << *this);
|
||||||
BEESCOUNT(pairbackward_toxic_hash);
|
BEESCOUNT(pairbackward_toxic_hash);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -558,7 +558,7 @@ BeesRangePair::grow(shared_ptr<BeesContext> ctx, bool constrained)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found_toxic) {
|
if (found_toxic) {
|
||||||
BEESLOGWARN("WORKAROUND: found toxic hash in " << first_bbd << " while extending forward:\n" << *this);
|
BEESLOGDEBUG("WORKAROUND: found toxic hash in " << first_bbd << " while extending forward:\n" << *this);
|
||||||
BEESCOUNT(pairforward_toxic_hash);
|
BEESCOUNT(pairforward_toxic_hash);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
37
src/bees.cc
37
src/bees.cc
@@ -198,7 +198,7 @@ BeesTooLong::check() const
|
|||||||
if (age() > m_limit) {
|
if (age() > m_limit) {
|
||||||
ostringstream oss;
|
ostringstream oss;
|
||||||
m_func(oss);
|
m_func(oss);
|
||||||
BEESLOGWARN("PERFORMANCE: " << *this << " sec: " << oss.str());
|
BEESLOGINFO("PERFORMANCE: " << *this << " sec: " << oss.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,10 +246,6 @@ bees_readahead_nolock(int const fd, const off_t offset, const size_t size)
|
|||||||
Timer readahead_timer;
|
Timer readahead_timer;
|
||||||
BEESNOTE("readahead " << name_fd(fd) << " offset " << to_hex(offset) << " len " << pretty(size));
|
BEESNOTE("readahead " << name_fd(fd) << " offset " << to_hex(offset) << " len " << pretty(size));
|
||||||
BEESTOOLONG("readahead " << name_fd(fd) << " offset " << to_hex(offset) << " len " << pretty(size));
|
BEESTOOLONG("readahead " << name_fd(fd) << " offset " << to_hex(offset) << " len " << pretty(size));
|
||||||
#if 0
|
|
||||||
// In the kernel, readahead() is identical to posix_fadvise(..., POSIX_FADV_DONTNEED)
|
|
||||||
DIE_IF_NON_ZERO(readahead(fd, offset, size));
|
|
||||||
#else
|
|
||||||
// Make sure this data is in page cache by brute force
|
// Make sure this data is in page cache by brute force
|
||||||
// The btrfs kernel code does readahead with lower ioprio
|
// The btrfs kernel code does readahead with lower ioprio
|
||||||
// and might discard the readahead request entirely.
|
// and might discard the readahead request entirely.
|
||||||
@@ -263,13 +259,16 @@ bees_readahead_nolock(int const fd, const off_t offset, const size_t size)
|
|||||||
// Ignore errors and short reads. It turns out our size
|
// Ignore errors and short reads. It turns out our size
|
||||||
// parameter isn't all that accurate, so we can't use
|
// parameter isn't all that accurate, so we can't use
|
||||||
// the pread_or_die template.
|
// the pread_or_die template.
|
||||||
(void)!pread(fd, dummy, this_read_size, working_offset);
|
const auto pr_rv = pread(fd, dummy, this_read_size, working_offset);
|
||||||
BEESCOUNT(readahead_count);
|
if (pr_rv >= 0) {
|
||||||
BEESCOUNTADD(readahead_bytes, this_read_size);
|
BEESCOUNT(readahead_count);
|
||||||
|
BEESCOUNTADD(readahead_bytes, pr_rv);
|
||||||
|
} else {
|
||||||
|
BEESCOUNT(readahead_fail);
|
||||||
|
}
|
||||||
working_offset += this_read_size;
|
working_offset += this_read_size;
|
||||||
working_size -= this_read_size;
|
working_size -= this_read_size;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
BEESCOUNTADD(readahead_ms, readahead_timer.age() * 1000);
|
BEESCOUNTADD(readahead_ms, readahead_timer.age() * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,9 +703,8 @@ bees_main(int argc, char *argv[])
|
|||||||
shared_ptr<BeesContext> bc = make_shared<BeesContext>();
|
shared_ptr<BeesContext> bc = make_shared<BeesContext>();
|
||||||
BEESLOGDEBUG("context constructed");
|
BEESLOGDEBUG("context constructed");
|
||||||
|
|
||||||
string cwd(readlink_or_die("/proc/self/cwd"));
|
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
|
bool use_relative_paths = false;
|
||||||
bool chatter_prefix_timestamp = true;
|
bool chatter_prefix_timestamp = true;
|
||||||
double thread_factor = 0;
|
double thread_factor = 0;
|
||||||
unsigned thread_count = 0;
|
unsigned thread_count = 0;
|
||||||
@@ -778,7 +776,7 @@ bees_main(int argc, char *argv[])
|
|||||||
thread_min = stoul(optarg);
|
thread_min = stoul(optarg);
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
crucible::set_relative_path(cwd);
|
use_relative_paths = true;
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
chatter_prefix_timestamp = false;
|
chatter_prefix_timestamp = false;
|
||||||
@@ -796,7 +794,7 @@ bees_main(int argc, char *argv[])
|
|||||||
root_scan_mode = static_cast<BeesRoots::ScanMode>(stoul(optarg));
|
root_scan_mode = static_cast<BeesRoots::ScanMode>(stoul(optarg));
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
crucible::set_relative_path("");
|
use_relative_paths = false;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
chatter_prefix_timestamp = true;
|
chatter_prefix_timestamp = true;
|
||||||
@@ -866,18 +864,19 @@ bees_main(int argc, char *argv[])
|
|||||||
BEESLOGNOTICE("setting root path to '" << root_path << "'");
|
BEESLOGNOTICE("setting root path to '" << root_path << "'");
|
||||||
bc->set_root_path(root_path);
|
bc->set_root_path(root_path);
|
||||||
|
|
||||||
|
// Set path prefix
|
||||||
|
if (use_relative_paths) {
|
||||||
|
crucible::set_relative_path(name_fd(bc->root_fd()));
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround for btrfs send
|
// Workaround for btrfs send
|
||||||
bc->roots()->set_workaround_btrfs_send(workaround_btrfs_send);
|
bc->roots()->set_workaround_btrfs_send(workaround_btrfs_send);
|
||||||
|
|
||||||
// Set root scan mode
|
// Set root scan mode
|
||||||
bc->roots()->set_scan_mode(root_scan_mode);
|
bc->roots()->set_scan_mode(root_scan_mode);
|
||||||
|
|
||||||
if (root_scan_mode == BeesRoots::SCAN_MODE_EXTENT) {
|
// Workaround for the logical-ino-vs-clone kernel bug
|
||||||
MultiLocker::enable_locking(false);
|
MultiLocker::enable_locking(true);
|
||||||
} else {
|
|
||||||
// Workaround for a kernel bug that the subvol-based crawlers keep triggering
|
|
||||||
MultiLocker::enable_locking(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start crawlers
|
// Start crawlers
|
||||||
bc->start();
|
bc->start();
|
||||||
|
11
src/bees.h
11
src/bees.h
@@ -521,7 +521,7 @@ class BeesCrawl {
|
|||||||
|
|
||||||
bool fetch_extents();
|
bool fetch_extents();
|
||||||
void fetch_extents_harder();
|
void fetch_extents_harder();
|
||||||
bool restart_crawl();
|
bool restart_crawl_unlocked();
|
||||||
BeesFileRange bti_to_bfr(const BtrfsTreeItem &bti) const;
|
BeesFileRange bti_to_bfr(const BtrfsTreeItem &bti) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -535,6 +535,7 @@ public:
|
|||||||
void deferred(bool def_setting);
|
void deferred(bool def_setting);
|
||||||
bool deferred() const;
|
bool deferred() const;
|
||||||
bool finished() const;
|
bool finished() const;
|
||||||
|
bool restart_crawl();
|
||||||
};
|
};
|
||||||
|
|
||||||
class BeesScanMode;
|
class BeesScanMode;
|
||||||
@@ -543,7 +544,8 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
|||||||
shared_ptr<BeesContext> m_ctx;
|
shared_ptr<BeesContext> m_ctx;
|
||||||
|
|
||||||
BeesStringFile m_crawl_state_file;
|
BeesStringFile m_crawl_state_file;
|
||||||
map<uint64_t, shared_ptr<BeesCrawl>> m_root_crawl_map;
|
using CrawlMap = map<uint64_t, shared_ptr<BeesCrawl>>;
|
||||||
|
CrawlMap m_root_crawl_map;
|
||||||
mutex m_mutex;
|
mutex m_mutex;
|
||||||
uint64_t m_crawl_dirty = 0;
|
uint64_t m_crawl_dirty = 0;
|
||||||
uint64_t m_crawl_clean = 0;
|
uint64_t m_crawl_clean = 0;
|
||||||
@@ -562,7 +564,7 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
|||||||
condition_variable m_stop_condvar;
|
condition_variable m_stop_condvar;
|
||||||
bool m_stop_requested = false;
|
bool m_stop_requested = false;
|
||||||
|
|
||||||
void insert_new_crawl();
|
CrawlMap insert_new_crawl();
|
||||||
Fd open_root_nocache(uint64_t root);
|
Fd open_root_nocache(uint64_t root);
|
||||||
Fd open_root_ino_nocache(uint64_t root, uint64_t ino);
|
Fd open_root_ino_nocache(uint64_t root, uint64_t ino);
|
||||||
uint64_t transid_max_nocache();
|
uint64_t transid_max_nocache();
|
||||||
@@ -579,12 +581,13 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
|||||||
bool crawl_batch(shared_ptr<BeesCrawl> crawl);
|
bool crawl_batch(shared_ptr<BeesCrawl> crawl);
|
||||||
void clear_caches();
|
void clear_caches();
|
||||||
|
|
||||||
friend class BeesScanModeExtent;
|
|
||||||
shared_ptr<BeesCrawl> insert_root(const BeesCrawlState &bcs);
|
shared_ptr<BeesCrawl> insert_root(const BeesCrawlState &bcs);
|
||||||
|
|
||||||
friend class BeesCrawl;
|
friend class BeesCrawl;
|
||||||
friend class BeesFdCache;
|
friend class BeesFdCache;
|
||||||
friend class BeesScanMode;
|
friend class BeesScanMode;
|
||||||
|
friend class BeesScanModeSubvol;
|
||||||
|
friend class BeesScanModeExtent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BeesRoots(shared_ptr<BeesContext> ctx);
|
BeesRoots(shared_ptr<BeesContext> ctx);
|
||||||
|
Reference in New Issue
Block a user