mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 21:35:45 +02:00
workarounds: add workaround for btrfs send
Introduce --workaround options which trade performance or effectiveness to avoid triggering kernel bugs. The first such option is --workaround-btrfs-send, which avoids making any modification to read-only subvols to avoid btrfs send bugs. Clean up usage message: no tabs for formatting, split options into sections by theme. Make scan mode a non-static data member like all (most?) other options. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
parent
6c68e81da7
commit
23f3e4ec42
@ -178,15 +178,28 @@ BeesContext::BeesContext(shared_ptr<BeesContext> parent) :
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BeesContext::is_root_ro(uint64_t root)
|
||||
{
|
||||
return roots()->is_root_ro(root);
|
||||
}
|
||||
|
||||
bool
|
||||
BeesContext::dedup(const BeesRangePair &brp)
|
||||
{
|
||||
// TOOLONG and NOTE can retroactively fill in the filename details, but LOG can't
|
||||
BEESNOTE("dedup " << brp);
|
||||
|
||||
brp.first.fd(shared_from_this());
|
||||
brp.second.fd(shared_from_this());
|
||||
|
||||
if (is_root_ro(brp.second.fid().root())) {
|
||||
// BEESLOGDEBUG("WORKAROUND: dst subvol is read-only in " << name_fd(brp.second.fd()));
|
||||
BEESCOUNT(dedup_workaround_btrfs_send);
|
||||
return false;
|
||||
}
|
||||
|
||||
brp.first.fd(shared_from_this());
|
||||
|
||||
BEESTOOLONG("dedup " << brp);
|
||||
|
||||
BeesAddress first_addr(brp.first.fd(), brp.first.begin());
|
||||
|
@ -11,8 +11,6 @@
|
||||
using namespace crucible;
|
||||
using namespace std;
|
||||
|
||||
BeesRoots::ScanMode BeesRoots::s_scan_mode = BeesRoots::SCAN_MODE_ZERO;
|
||||
|
||||
string
|
||||
format_time(time_t t)
|
||||
{
|
||||
@ -67,10 +65,21 @@ void
|
||||
BeesRoots::set_scan_mode(ScanMode mode)
|
||||
{
|
||||
THROW_CHECK1(invalid_argument, mode, mode < SCAN_MODE_COUNT);
|
||||
s_scan_mode = mode;
|
||||
m_scan_mode = mode;
|
||||
BEESLOGINFO("Scan mode set to " << mode << " (" << scan_mode_ntoa(mode) << ")");
|
||||
}
|
||||
|
||||
void
|
||||
BeesRoots::set_workaround_btrfs_send(bool do_avoid)
|
||||
{
|
||||
m_workaround_btrfs_send = do_avoid;
|
||||
if (m_workaround_btrfs_send) {
|
||||
BEESLOGINFO("WORKAROUND: btrfs send workaround enabled");
|
||||
} else {
|
||||
BEESLOGINFO("btrfs send workaround disabled");
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
BeesRoots::crawl_state_filename() const
|
||||
{
|
||||
@ -281,7 +290,7 @@ BeesRoots::crawl_roots()
|
||||
BEESLOGINFO("idle: crawl map is empty!");
|
||||
}
|
||||
|
||||
switch (s_scan_mode) {
|
||||
switch (m_scan_mode) {
|
||||
|
||||
case SCAN_MODE_ZERO: {
|
||||
// Scan the same inode/offset tuple in each subvol (good for snapshots)
|
||||
@ -366,6 +375,13 @@ BeesRoots::crawl_roots()
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
BeesRoots::clear_caches()
|
||||
{
|
||||
m_ctx->fd_cache()->clear();
|
||||
m_root_ro_cache.clear();
|
||||
}
|
||||
|
||||
void
|
||||
BeesRoots::crawl_thread()
|
||||
{
|
||||
@ -408,7 +424,7 @@ BeesRoots::crawl_thread()
|
||||
// Even open files are a problem if they're big enough.
|
||||
auto new_count = m_transid_re.count();
|
||||
if (new_count != last_count) {
|
||||
m_ctx->fd_cache()->clear();
|
||||
clear_caches();
|
||||
}
|
||||
last_count = new_count;
|
||||
|
||||
@ -541,6 +557,12 @@ BeesRoots::BeesRoots(shared_ptr<BeesContext> ctx) :
|
||||
m_writeback_thread("crawl_writeback"),
|
||||
m_task_running(false)
|
||||
{
|
||||
|
||||
m_root_ro_cache.func([&](uint64_t root) -> bool {
|
||||
return is_root_ro_nocache(root);
|
||||
});
|
||||
m_root_ro_cache.max_size(BEES_ROOT_FD_CACHE_SIZE);
|
||||
|
||||
m_crawl_thread.exec([&]() {
|
||||
// Measure current transid before creating any crawlers
|
||||
catch_all([&]() {
|
||||
@ -646,6 +668,7 @@ BeesRoots::open_root_nocache(uint64_t rootid)
|
||||
Stat st(rv);
|
||||
THROW_CHECK1(runtime_error, st.st_ino, st.st_ino == BTRFS_FIRST_FREE_OBJECTID);
|
||||
// BEESLOGDEBUG("open_root_nocache " << rootid << ": " << name_fd(rv));
|
||||
|
||||
BEESCOUNT(root_ok);
|
||||
return rv;
|
||||
}
|
||||
@ -667,6 +690,32 @@ BeesRoots::open_root(uint64_t rootid)
|
||||
return m_ctx->fd_cache()->open_root(m_ctx, rootid);
|
||||
}
|
||||
|
||||
bool
|
||||
BeesRoots::is_root_ro_nocache(uint64_t root)
|
||||
{
|
||||
Fd root_fd = open_root(root);
|
||||
BEESTRACE("checking subvol flags on root " << root << " path " << name_fd(root_fd));
|
||||
|
||||
uint64_t flags = 0;
|
||||
DIE_IF_NON_ZERO(ioctl(root_fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags));
|
||||
if (flags & BTRFS_SUBVOL_RDONLY) {
|
||||
BEESLOGDEBUG("WORKAROUND: Avoiding RO subvol " << root);
|
||||
BEESCOUNT(root_workaround_btrfs_send);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
BeesRoots::is_root_ro(uint64_t root)
|
||||
{
|
||||
// If we are not implementing the workaround there is no need for cache
|
||||
if (!m_workaround_btrfs_send) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_root_ro_cache(root);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
BeesRoots::next_root(uint64_t root)
|
||||
@ -889,6 +938,20 @@ BeesCrawl::fetch_extents()
|
||||
return next_transid();
|
||||
}
|
||||
|
||||
// Check for btrfs send workaround: don't scan RO roots at all, pretend
|
||||
// they are just empty. We can't free any space there, and we
|
||||
// don't have the necessary analysis logic to be able to use
|
||||
// them as dedup src extents (yet).
|
||||
//
|
||||
// This will keep the max_transid up to date so if the root
|
||||
// is ever switched back to read-write, it won't trigger big
|
||||
// expensive in-kernel searches for ancient transids.
|
||||
if (m_ctx->is_root_ro(old_state.m_root)) {
|
||||
BEESLOGDEBUG("WORKAROUND: RO root " << old_state.m_root);
|
||||
BEESCOUNT(root_workaround_btrfs_send);
|
||||
return next_transid();
|
||||
}
|
||||
|
||||
BEESNOTE("crawling " << get_state_end());
|
||||
|
||||
Timer crawl_timer;
|
||||
|
80
src/bees.cc
80
src/bees.cc
@ -35,6 +35,7 @@ int bees_log_level = 8;
|
||||
int
|
||||
do_cmd_help(char *argv[])
|
||||
{
|
||||
// 80col 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
cerr << "Usage: " << argv[0] << " [options] fs-root-path [fs-root-path-2...]\n"
|
||||
"Performs best-effort extent-same deduplication on btrfs.\n"
|
||||
"\n"
|
||||
@ -42,25 +43,35 @@ do_cmd_help(char *argv[])
|
||||
"Other directories will be rejected.\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\t-h, --help\t\tShow this help\n"
|
||||
"\t-c, --thread-count\tWorker thread count (default CPU count * factor)\n"
|
||||
"\t-C, --thread-factor\tWorker thread factor (default " << BEES_DEFAULT_THREAD_FACTOR << ")\n"
|
||||
"\t-G, --thread-min\t\tMinimum worker thread count with load average target (default 0)\n"
|
||||
"\t-g, --loadavg-target\t\tTarget load average for worker threads (default is no target)\n"
|
||||
"\t-m, --scan-mode\t\tScanning mode (0..2, default 0)\n"
|
||||
"\t-t, --timestamps\tShow timestamps in log output (default)\n"
|
||||
"\t-T, --no-timestamps\tOmit timestamps in log output\n"
|
||||
"\t-p, --absolute-paths\tShow absolute paths (default)\n"
|
||||
"\t-P, --strip-paths\tStrip $CWD from beginning of all paths in the log\n"
|
||||
"\t-v, --verbose\tSet maximum log level (0..8, default 8)\n"
|
||||
" -h, --help Show this help\n"
|
||||
"\n"
|
||||
"Load management options:\n"
|
||||
" -c, --thread-count Worker thread count (default CPU count * factor)\n"
|
||||
" -C, --thread-factor Worker thread factor (default " << BEES_DEFAULT_THREAD_FACTOR << ")\n"
|
||||
" -G, --thread-min Minimum worker thread count (default 0)\n"
|
||||
" -g, --loadavg-target Target load average for worker threads (default none)\n"
|
||||
"\n"
|
||||
"Filesystem tree traversal options:\n"
|
||||
" -m, --scan-mode Scanning mode (0..2, default 0)\n"
|
||||
"\n"
|
||||
"Workarounds:\n"
|
||||
" -a, --workaround-btrfs-send Workaround for btrfs send\n"
|
||||
"\n"
|
||||
"Logging options:\n"
|
||||
" -t, --timestamps Show timestamps in log output (default)\n"
|
||||
" -T, --no-timestamps Omit timestamps in log output\n"
|
||||
" -p, --absolute-paths Show absolute paths (default)\n"
|
||||
" -P, --strip-paths Strip $CWD from beginning of all paths in the log\n"
|
||||
" -v, --verbose Set maximum log level (0..8, default 8)\n"
|
||||
"\n"
|
||||
"Optional environment variables:\n"
|
||||
"\tBEESHOME\tPath to hash table and configuration files\n"
|
||||
"\t\t\t(default is .beeshome/ in the root of each filesystem).\n"
|
||||
" BEESHOME Path to hash table and configuration files\n"
|
||||
" (default is .beeshome/ in the root of each filesystem).\n"
|
||||
"\n"
|
||||
"\tBEESSTATUS\tFile to write status to (tmpfs recommended, e.g. /run).\n"
|
||||
"\t\t\tNo status is written if this variable is unset.\n"
|
||||
" BEESSTATUS File to write status to (tmpfs recommended, e.g. /run).\n"
|
||||
" No status is written if this variable is unset.\n"
|
||||
"\n"
|
||||
// 80col 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
@ -657,16 +668,15 @@ bees_main(int argc, char *argv[])
|
||||
unsigned thread_count = 0;
|
||||
unsigned thread_min = 0;
|
||||
double load_target = 0;
|
||||
bool workaround_btrfs_send = false;
|
||||
|
||||
// Parse options
|
||||
int c;
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
// Configure getopt_long
|
||||
static const struct option long_options[] = {
|
||||
{ "thread-factor", required_argument, NULL, 'C' },
|
||||
{ "thread-min", required_argument, NULL, 'G' },
|
||||
{ "strip-paths", no_argument, NULL, 'P' },
|
||||
{ "no-timestamps", no_argument, NULL, 'T' },
|
||||
{ "workaround-btrfs-send", no_argument, NULL, 'a' },
|
||||
{ "thread-count", required_argument, NULL, 'c' },
|
||||
{ "loadavg-target", required_argument, NULL, 'g' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
@ -674,9 +684,31 @@ bees_main(int argc, char *argv[])
|
||||
{ "absolute-paths", no_argument, NULL, 'p' },
|
||||
{ "timestamps", no_argument, NULL, 't' },
|
||||
{ "verbose", required_argument, NULL, 'v' },
|
||||
{ 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "C:G:PTc:hg:m:ptv:", long_options, &option_index);
|
||||
// Build getopt_long's short option list from the long_options table.
|
||||
// While we're at it, make sure we didn't duplicate any options.
|
||||
string getopt_list;
|
||||
set<decltype(option::val)> option_vals;
|
||||
for (const struct option *op = long_options; op->val; ++op) {
|
||||
THROW_CHECK1(runtime_error, op->val, !option_vals.count(op->val));
|
||||
option_vals.insert(op->val);
|
||||
if ((op->val & 0xff) != op->val) {
|
||||
continue;
|
||||
}
|
||||
getopt_list += op->val;
|
||||
if (op->has_arg == required_argument) {
|
||||
getopt_list += ':';
|
||||
}
|
||||
}
|
||||
|
||||
// Parse options
|
||||
int c;
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
|
||||
c = getopt_long(argc, argv, getopt_list.c_str(), long_options, &option_index);
|
||||
if (-1 == c) {
|
||||
break;
|
||||
}
|
||||
@ -695,6 +727,9 @@ bees_main(int argc, char *argv[])
|
||||
case 'T':
|
||||
chatter_prefix_timestamp = false;
|
||||
break;
|
||||
case 'a':
|
||||
workaround_btrfs_send = true;
|
||||
break;
|
||||
case 'c':
|
||||
thread_count = stoul(optarg);
|
||||
break;
|
||||
@ -702,7 +737,7 @@ bees_main(int argc, char *argv[])
|
||||
load_target = stod(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
BeesRoots::set_scan_mode(static_cast<BeesRoots::ScanMode>(stoul(optarg)));
|
||||
bc->roots()->set_scan_mode(static_cast<BeesRoots::ScanMode>(stoul(optarg)));
|
||||
break;
|
||||
case 'p':
|
||||
crucible::set_relative_path("");
|
||||
@ -763,6 +798,9 @@ bees_main(int argc, char *argv[])
|
||||
BEESLOGNOTICE("setting worker thread pool maximum size to " << thread_count);
|
||||
TaskMaster::set_thread_count(thread_count);
|
||||
|
||||
// Workaround for btrfs send
|
||||
bc->roots()->set_workaround_btrfs_send(workaround_btrfs_send);
|
||||
|
||||
// Create a context and start crawlers
|
||||
bool did_subscription = false;
|
||||
while (optind < argc) {
|
||||
|
11
src/bees.h
11
src/bees.h
@ -524,11 +524,14 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
||||
size_t m_transid_factor = BEES_TRANSID_FACTOR;
|
||||
atomic<bool> m_task_running;
|
||||
Task m_crawl_task;
|
||||
bool m_workaround_btrfs_send = false;
|
||||
LRUCache<bool, uint64_t> m_root_ro_cache;
|
||||
|
||||
void insert_new_crawl();
|
||||
void insert_root(const BeesCrawlState &bcs);
|
||||
Fd open_root_nocache(uint64_t root);
|
||||
Fd open_root_ino_nocache(uint64_t root, uint64_t ino);
|
||||
bool is_root_ro_nocache(uint64_t root);
|
||||
uint64_t transid_min();
|
||||
uint64_t transid_max();
|
||||
uint64_t transid_max_nocache();
|
||||
@ -545,6 +548,7 @@ class BeesRoots : public enable_shared_from_this<BeesRoots> {
|
||||
void current_state_set(const BeesCrawlState &bcs);
|
||||
RateEstimator& transid_re();
|
||||
size_t crawl_batch(shared_ptr<BeesCrawl> crawl);
|
||||
void clear_caches();
|
||||
|
||||
friend class BeesFdCache;
|
||||
friend class BeesCrawl;
|
||||
@ -554,6 +558,7 @@ public:
|
||||
Fd open_root(uint64_t root);
|
||||
Fd open_root_ino(uint64_t root, uint64_t ino);
|
||||
Fd open_root_ino(const BeesFileId &bfi) { return open_root_ino(bfi.root(), bfi.ino()); }
|
||||
bool is_root_ro(uint64_t root);
|
||||
|
||||
// TODO: think of better names for these.
|
||||
// or TODO: do extent-tree scans instead
|
||||
@ -564,10 +569,11 @@ public:
|
||||
SCAN_MODE_COUNT, // must be last
|
||||
};
|
||||
|
||||
static void set_scan_mode(ScanMode new_mode);
|
||||
void set_scan_mode(ScanMode new_mode);
|
||||
void set_workaround_btrfs_send(bool do_avoid);
|
||||
|
||||
private:
|
||||
static ScanMode s_scan_mode;
|
||||
ScanMode m_scan_mode = SCAN_MODE_ZERO;
|
||||
static string scan_mode_ntoa(ScanMode new_mode);
|
||||
|
||||
};
|
||||
@ -712,6 +718,7 @@ public:
|
||||
|
||||
BeesFileRange scan_forward(const BeesFileRange &bfr);
|
||||
|
||||
bool is_root_ro(uint64_t root);
|
||||
BeesRangePair dup_extent(const BeesFileRange &src);
|
||||
bool dedup(const BeesRangePair &brp);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user