mirror of
https://github.com/Zygo/bees.git
synced 2025-08-02 05:43: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_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_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_extent`: The extent crawler queued all references to an extent for processing.
|
||||
* `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_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.
|
||||
@@ -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_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_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_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.
|
||||
@@ -281,11 +284,14 @@ The `progress` event group consists of events related to progress estimation.
|
||||
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_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_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)`.
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
/// 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 {
|
||||
public:
|
||||
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 {
|
||||
public:
|
||||
BtrfsExtentDataFetcher(const Fd &fd);
|
||||
};
|
||||
|
||||
/// Fetch inodes from a subvol
|
||||
class BtrfsFsTreeFetcher : public BtrfsTreeObjectFetcher {
|
||||
public:
|
||||
BtrfsFsTreeFetcher(const Fd &fd, uint64_t subvol);
|
||||
};
|
||||
|
||||
/// Fetch raw inode items
|
||||
class BtrfsInodeFetcher : public BtrfsTreeObjectFetcher {
|
||||
public:
|
||||
BtrfsInodeFetcher(const Fd &fd);
|
||||
BtrfsTreeItem stat(uint64_t subvol, uint64_t inode);
|
||||
};
|
||||
|
||||
/// Fetch a root (subvol) item
|
||||
class BtrfsRootFetcher : public BtrfsTreeObjectFetcher {
|
||||
public:
|
||||
BtrfsRootFetcher(const Fd &fd);
|
||||
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_loops;
|
||||
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 BtrfsIoctlSearchKey &key);
|
||||
|
||||
string btrfs_chunk_type_ntoa(uint64_t type);
|
||||
string btrfs_search_type_ntoa(unsigned type);
|
||||
string btrfs_search_objectid_ntoa(uint64_t objectid);
|
||||
string btrfs_compress_type_ntoa(uint8_t type);
|
||||
@@ -246,9 +248,11 @@ namespace crucible {
|
||||
struct BtrfsIoctlFsInfoArgs : public btrfs_ioctl_fs_info_args_v3 {
|
||||
BtrfsIoctlFsInfoArgs();
|
||||
void do_ioctl(int fd);
|
||||
bool do_ioctl_nothrow(int fd);
|
||||
uint16_t csum_type() const;
|
||||
uint16_t csum_size() const;
|
||||
uint64_t generation() const;
|
||||
vector<uint8_t> fsid() const;
|
||||
};
|
||||
|
||||
ostream & operator<<(ostream &os, const BtrfsIoctlFsInfoArgs &a);
|
||||
|
@@ -1,11 +1,46 @@
|
||||
#ifndef 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 <fcntl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
|
||||
#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" {
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#define DINIT(__x) __x
|
||||
|
@@ -5,6 +5,12 @@
|
||||
#include "crucible/hexdump.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 {
|
||||
using namespace std;
|
||||
|
||||
@@ -355,6 +361,7 @@ namespace crucible {
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::at(uint64_t logical)
|
||||
{
|
||||
CRUCIBLE_BTRFS_TREE_DEBUG("at " << logical);
|
||||
BtrfsIoctlSearchKey &sk = m_sk;
|
||||
fill_sk(sk, logical);
|
||||
// Exact match, should return 0 or 1 items
|
||||
@@ -397,16 +404,17 @@ namespace crucible {
|
||||
BtrfsTreeFetcher::rlower_bound(uint64_t logical)
|
||||
{
|
||||
#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
|
||||
#define BTFRLB_DEBUG(x) do { } while (false)
|
||||
#define BTFRLB_DEBUG(x) CRUCIBLE_BTRFS_TREE_DEBUG(x)
|
||||
#endif
|
||||
BtrfsTreeItem closest_item;
|
||||
uint64_t closest_logical = 0;
|
||||
BtrfsIoctlSearchKey &sk = m_sk;
|
||||
size_t loops = 0;
|
||||
BTFRLB_DEBUG("rlower_bound: " << to_hex(logical) << endl);
|
||||
seek_backward(scale_logical(logical), [&](uint64_t lower_bound, uint64_t upper_bound) {
|
||||
BTFRLB_DEBUG("rlower_bound: " << to_hex(logical) << " in tree " << tree() << endl);
|
||||
seek_backward(scale_logical(logical), [&](uint64_t const lower_bound, uint64_t const upper_bound) {
|
||||
++loops;
|
||||
fill_sk(sk, unscale_logical(min(scaled_max_logical(), lower_bound)));
|
||||
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));
|
||||
for (auto &i : sk.m_result) {
|
||||
next_sk(sk, i);
|
||||
const auto this_logical = hdr_logical(i);
|
||||
const auto scaled_hdr_logical = scale_logical(this_logical);
|
||||
BTFRLB_DEBUG(" " << to_hex(scaled_hdr_logical));
|
||||
if (hdr_match(i)) {
|
||||
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)");
|
||||
}
|
||||
// If hdr_stop or !hdr_match, don't inspect the item
|
||||
if (hdr_stop(i)) {
|
||||
rv.insert(numeric_limits<uint64_t>::max());
|
||||
BTFRLB_DEBUG("(stop)");
|
||||
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);
|
||||
// We might get a search result that contains only non-matching items.
|
||||
@@ -474,6 +484,7 @@ namespace crucible {
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::next(uint64_t logical)
|
||||
{
|
||||
CRUCIBLE_BTRFS_TREE_DEBUG("next " << logical);
|
||||
const auto scaled_logical = scale_logical(logical);
|
||||
if (scaled_logical + 1 > scaled_max_logical()) {
|
||||
return BtrfsTreeItem();
|
||||
@@ -484,6 +495,7 @@ namespace crucible {
|
||||
BtrfsTreeItem
|
||||
BtrfsTreeFetcher::prev(uint64_t logical)
|
||||
{
|
||||
CRUCIBLE_BTRFS_TREE_DEBUG("prev " << logical);
|
||||
const auto scaled_logical = scale_logical(logical);
|
||||
if (scaled_logical < 1) {
|
||||
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)
|
||||
{
|
||||
#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
|
||||
#define BCTFGS_DEBUG(x) do { } while (false)
|
||||
#define BCTFGS_DEBUG(x) CRUCIBLE_BTRFS_TREE_DEBUG(x)
|
||||
#endif
|
||||
const uint64_t logical_end = logical + count * block_size();
|
||||
BtrfsTreeItem bti = rlower_bound(logical);
|
||||
@@ -662,14 +675,6 @@ namespace crucible {
|
||||
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) :
|
||||
BtrfsTreeObjectFetcher(fd)
|
||||
{
|
||||
@@ -693,18 +698,86 @@ namespace crucible {
|
||||
BtrfsTreeObjectFetcher(fd)
|
||||
{
|
||||
tree(BTRFS_ROOT_TREE_OBJECTID);
|
||||
type(BTRFS_ROOT_ITEM_KEY);
|
||||
scale_size(1);
|
||||
}
|
||||
|
||||
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);
|
||||
if (!!item) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_loops = 0;
|
||||
thread_local size_t BtrfsIoctlSearchKey::s_loops_empty = 0;
|
||||
thread_local shared_ptr<ostream> BtrfsIoctlSearchKey::s_debug_ostream;
|
||||
|
||||
bool
|
||||
BtrfsIoctlSearchKey::do_ioctl_nothrow(int fd)
|
||||
@@ -776,6 +777,9 @@ namespace crucible {
|
||||
ioctl_ptr = ioctl_arg.get<btrfs_ioctl_search_args_v2>();
|
||||
ioctl_ptr->key = static_cast<const btrfs_ioctl_search_key&>(*this);
|
||||
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.
|
||||
int rv = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, ioctl_arg.data());
|
||||
++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
|
||||
btrfs_search_type_ntoa(unsigned type)
|
||||
{
|
||||
@@ -1138,11 +1162,17 @@ namespace crucible {
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
BtrfsIoctlFsInfoArgs::do_ioctl(int fd)
|
||||
bool
|
||||
BtrfsIoctlFsInfoArgs::do_ioctl_nothrow(int const fd)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1159,6 +1189,13 @@ namespace crucible {
|
||||
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
|
||||
BtrfsIoctlFsInfoArgs::generation() const
|
||||
{
|
||||
|
@@ -1,5 +1,27 @@
|
||||
#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" {
|
||||
|
||||
int
|
||||
@@ -7,7 +29,12 @@ __attribute__((weak))
|
||||
openat2(int const dirfd, const char *const pathname, struct open_how *const how, size_t const size)
|
||||
throw()
|
||||
{
|
||||
#ifdef SYS_openat2
|
||||
return syscall(SYS_openat2, dirfd, pathname, how, size);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
@@ -1,5 +1,13 @@
|
||||
#!/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
|
||||
INFO(){ echo "INFO:" "$@"; }
|
||||
ERRO(){ echo "ERROR:" "$@"; exit 1; }
|
||||
@@ -108,13 +116,11 @@ mkdir -p "$WORK_DIR" || exit 1
|
||||
INFO "MOUNT DIR: $MNT_DIR"
|
||||
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
|
||||
INFO "Create subvol $BEESHOME for store bees data"
|
||||
btrfs sub cre "$BEESHOME"
|
||||
else
|
||||
btrfs sub show "$BEESHOME" &> /dev/null || ERRO "$BEESHOME MUST BE A SUBVOL!"
|
||||
fi
|
||||
|
||||
# Check DB size
|
||||
|
@@ -259,7 +259,7 @@ BeesContext::dedup(const BeesRangePair &brp_in)
|
||||
BEESCOUNTADD(dedup_bytes, brp.first.size());
|
||||
} else {
|
||||
BEESCOUNT(dedup_miss);
|
||||
BEESLOGWARN("NO Dedup! " << brp);
|
||||
BEESLOGINFO("NO Dedup! " << brp);
|
||||
}
|
||||
|
||||
lock.reset();
|
||||
@@ -373,7 +373,7 @@ BeesContext::scan_one_extent(const BeesFileRange &bfr, const Extent &e)
|
||||
Extent::OBSCURED | Extent::PREALLOC
|
||||
)) {
|
||||
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) {
|
||||
@@ -534,7 +534,7 @@ BeesContext::scan_one_extent(const BeesFileRange &bfr, const Extent &e)
|
||||
|
||||
// Hash 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.
|
||||
// Extents may become non-toxic so give them a chance to expire.
|
||||
// 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);
|
||||
// Toxic extents are really 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);
|
||||
// Make sure we never see this hash again.
|
||||
// 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
|
||||
if (bfr.begin() >= bfr.file_size()) {
|
||||
BEESLOGWARN("past EOF: " << bfr);
|
||||
BEESLOGDEBUG("past EOF: " << bfr);
|
||||
BEESCOUNT(scanf_eof);
|
||||
return false;
|
||||
}
|
||||
|
@@ -797,7 +797,7 @@ BeesHashTable::BeesHashTable(shared_ptr<BeesContext> ctx, string filename, off_t
|
||||
for (auto fp = madv_flags; fp->value; ++fp) {
|
||||
BEESTOOLONG("madvise(" << fp->name << ")");
|
||||
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();
|
||||
});
|
||||
|
||||
// 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([&]() {
|
||||
// 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));
|
||||
});
|
||||
}
|
||||
|
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) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@@ -558,7 +558,7 @@ BeesRangePair::grow(shared_ptr<BeesContext> ctx, bool constrained)
|
||||
}
|
||||
}
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
37
src/bees.cc
37
src/bees.cc
@@ -198,7 +198,7 @@ BeesTooLong::check() const
|
||||
if (age() > m_limit) {
|
||||
ostringstream 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;
|
||||
BEESNOTE("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
|
||||
// The btrfs kernel code does readahead with lower ioprio
|
||||
// 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
|
||||
// parameter isn't all that accurate, so we can't use
|
||||
// the pread_or_die template.
|
||||
(void)!pread(fd, dummy, this_read_size, working_offset);
|
||||
BEESCOUNT(readahead_count);
|
||||
BEESCOUNTADD(readahead_bytes, this_read_size);
|
||||
const auto pr_rv = pread(fd, dummy, this_read_size, working_offset);
|
||||
if (pr_rv >= 0) {
|
||||
BEESCOUNT(readahead_count);
|
||||
BEESCOUNTADD(readahead_bytes, pr_rv);
|
||||
} else {
|
||||
BEESCOUNT(readahead_fail);
|
||||
}
|
||||
working_offset += this_read_size;
|
||||
working_size -= this_read_size;
|
||||
}
|
||||
#endif
|
||||
BEESCOUNTADD(readahead_ms, readahead_timer.age() * 1000);
|
||||
}
|
||||
|
||||
@@ -704,9 +703,8 @@ bees_main(int argc, char *argv[])
|
||||
shared_ptr<BeesContext> bc = make_shared<BeesContext>();
|
||||
BEESLOGDEBUG("context constructed");
|
||||
|
||||
string cwd(readlink_or_die("/proc/self/cwd"));
|
||||
|
||||
// Defaults
|
||||
bool use_relative_paths = false;
|
||||
bool chatter_prefix_timestamp = true;
|
||||
double thread_factor = 0;
|
||||
unsigned thread_count = 0;
|
||||
@@ -778,7 +776,7 @@ bees_main(int argc, char *argv[])
|
||||
thread_min = stoul(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
crucible::set_relative_path(cwd);
|
||||
use_relative_paths = true;
|
||||
break;
|
||||
case 'T':
|
||||
chatter_prefix_timestamp = false;
|
||||
@@ -796,7 +794,7 @@ bees_main(int argc, char *argv[])
|
||||
root_scan_mode = static_cast<BeesRoots::ScanMode>(stoul(optarg));
|
||||
break;
|
||||
case 'p':
|
||||
crucible::set_relative_path("");
|
||||
use_relative_paths = false;
|
||||
break;
|
||||
case 't':
|
||||
chatter_prefix_timestamp = true;
|
||||
@@ -866,18 +864,19 @@ bees_main(int argc, char *argv[])
|
||||
BEESLOGNOTICE("setting root path to '" << 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
|
||||
bc->roots()->set_workaround_btrfs_send(workaround_btrfs_send);
|
||||
|
||||
// Set root scan mode
|
||||
bc->roots()->set_scan_mode(root_scan_mode);
|
||||
|
||||
if (root_scan_mode == BeesRoots::SCAN_MODE_EXTENT) {
|
||||
MultiLocker::enable_locking(false);
|
||||
} else {
|
||||
// Workaround for a kernel bug that the subvol-based crawlers keep triggering
|
||||
MultiLocker::enable_locking(true);
|
||||
}
|
||||
// Workaround for the logical-ino-vs-clone kernel bug
|
||||
MultiLocker::enable_locking(true);
|
||||
|
||||
// Start crawlers
|
||||
bc->start();
|
||||
|
11
src/bees.h
11
src/bees.h
@@ -521,7 +521,7 @@ class BeesCrawl {
|
||||
|
||||
bool fetch_extents();
|
||||
void fetch_extents_harder();
|
||||
bool restart_crawl();
|
||||
bool restart_crawl_unlocked();
|
||||
BeesFileRange bti_to_bfr(const BtrfsTreeItem &bti) const;
|
||||
|
||||
public:
|
||||
@@ -535,6 +535,7 @@ public:
|
||||
void deferred(bool def_setting);
|
||||
bool deferred() const;
|
||||
bool finished() const;
|
||||
bool restart_crawl();
|
||||
};
|
||||
|
||||
class BeesScanMode;
|
||||
@@ -543,7 +544,8 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
||||
shared_ptr<BeesContext> m_ctx;
|
||||
|
||||
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;
|
||||
uint64_t m_crawl_dirty = 0;
|
||||
uint64_t m_crawl_clean = 0;
|
||||
@@ -562,7 +564,7 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
||||
condition_variable m_stop_condvar;
|
||||
bool m_stop_requested = false;
|
||||
|
||||
void insert_new_crawl();
|
||||
CrawlMap insert_new_crawl();
|
||||
Fd open_root_nocache(uint64_t root);
|
||||
Fd open_root_ino_nocache(uint64_t root, uint64_t ino);
|
||||
uint64_t transid_max_nocache();
|
||||
@@ -579,12 +581,13 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
||||
bool crawl_batch(shared_ptr<BeesCrawl> crawl);
|
||||
void clear_caches();
|
||||
|
||||
friend class BeesScanModeExtent;
|
||||
shared_ptr<BeesCrawl> insert_root(const BeesCrawlState &bcs);
|
||||
|
||||
friend class BeesCrawl;
|
||||
friend class BeesFdCache;
|
||||
friend class BeesScanMode;
|
||||
friend class BeesScanModeSubvol;
|
||||
friend class BeesScanModeExtent;
|
||||
|
||||
public:
|
||||
BeesRoots(shared_ptr<BeesContext> ctx);
|
||||
|
Reference in New Issue
Block a user