bees-config-v1.o \
bees-config.o \
bees-context.o \
+ bees-fdcache.o \
bees-hash.o \
bees-lib.o \
bees-log.o \
using namespace crucible;
using namespace std;
-BeesFdCache::BeesFdCache(shared_ptr<BeesContext> ctx) :
- m_ctx(ctx)
-{
- m_root_cache.func([&](uint64_t root) -> Fd {
- Timer open_timer;
- auto rv = m_ctx->roots()->open_root_nocache(root);
- BEESCOUNTADD(open_root_ms, open_timer.age() * 1000);
- return rv;
- });
- m_root_cache.max_size(BEES_ROOT_FD_CACHE_SIZE);
- m_file_cache.func([&](uint64_t root, uint64_t ino) -> Fd {
- Timer open_timer;
- auto rv = m_ctx->roots()->open_root_ino_nocache(root, ino);
- BEESCOUNTADD(open_ino_ms, open_timer.age() * 1000);
- return rv;
- });
- m_file_cache.max_size(BEES_FILE_FD_CACHE_SIZE);
-}
-
-void
-BeesFdCache::clear()
-{
- BEESLOGDEBUG("Clearing root FD cache with size " << m_root_cache.size() << " to enable subvol delete");
- BEESNOTE("Clearing root FD cache with size " << m_root_cache.size());
- m_root_cache.clear();
- BEESCOUNT(root_clear);
-
- BEESLOGDEBUG("Clearing open FD cache with size " << m_file_cache.size() << " to enable file delete");
- BEESNOTE("Clearing open FD cache with size " << m_file_cache.size());
- m_file_cache.clear();
- BEESCOUNT(open_clear);
-}
-
-Fd
-BeesFdCache::open_root(uint64_t root)
-{
- return m_root_cache(root);
-}
-
-Fd
-BeesFdCache::open_root_ino(uint64_t root, uint64_t ino)
-{
- return m_file_cache(root, ino);
-}
-
void
BeesContext::set_reporter(shared_ptr<BeesReporter> reporter)
{
--- /dev/null
+#include "bees-fdcache.h"
+
+#include "bees.h"
+
+#include "crucible/string.h"
+
+#include <map>
+
+using namespace crucible;
+using namespace std;
+
+class BeesFdCache::Impl {
+public:
+ LRUCache<Fd, uint64_t> m_root_cache;
+ LRUCache<Fd, uint64_t, uint64_t> m_file_cache;
+ Timer m_root_cache_timer;
+ Timer m_file_cache_timer;
+ mutex m_tmpfiles_mutex;
+ map<BeesFileId, Fd> m_tmpfiles;
+};
+
+BeesFdCache::BeesFdCache(shared_ptr<BeesContext> ctx) :
+ m_ctx(ctx),
+ m_impl(make_unique<Impl>())
+{
+ m_impl->m_root_cache.func([this](uint64_t root) -> Fd {
+ Timer open_timer;
+ auto rv = m_ctx->roots()->open_root_nocache(root);
+ BEESCOUNTADD(open_root_ms, open_timer.age() * 1000);
+ return rv;
+ });
+ m_impl->m_root_cache.max_size(BEES_ROOT_FD_CACHE_SIZE);
+ m_impl->m_file_cache.func([this](uint64_t root, uint64_t ino) -> Fd {
+ Timer open_timer;
+ auto rv = m_ctx->roots()->open_root_ino_nocache(root, ino);
+ BEESCOUNTADD(open_ino_ms, open_timer.age() * 1000);
+ return rv;
+ });
+ m_impl->m_file_cache.max_size(BEES_FILE_FD_CACHE_SIZE);
+}
+
+BeesFdCache::~BeesFdCache() = default;
+
+void
+BeesFdCache::clear()
+{
+ BEESLOGDEBUG("Clearing root FD cache with size " << m_impl->m_root_cache.size() << " to enable subvol delete");
+ BEESNOTE("Clearing root FD cache with size " << m_impl->m_root_cache.size());
+ m_impl->m_root_cache.clear();
+ BEESCOUNT(root_clear);
+
+ BEESLOGDEBUG("Clearing open FD cache with size " << m_impl->m_file_cache.size() << " to enable file delete");
+ BEESNOTE("Clearing open FD cache with size " << m_impl->m_file_cache.size());
+ m_impl->m_file_cache.clear();
+ BEESCOUNT(open_clear);
+}
+
+Fd
+BeesFdCache::open_root(uint64_t root)
+{
+ return m_impl->m_root_cache(root);
+}
+
+Fd
+BeesFdCache::open_root_ino(uint64_t root, uint64_t ino)
+{
+ // The temp-file registry wins: O_TMPFILE files have no name, so we
+ // must return the registered Fd rather than trying to re-open by path.
+ {
+ unique_lock<mutex> lock(m_impl->m_tmpfiles_mutex);
+ auto found = m_impl->m_tmpfiles.find(BeesFileId(root, ino));
+ if (found != m_impl->m_tmpfiles.end()) {
+ BEESCOUNT(open_tmpfile);
+ return found->second;
+ }
+ }
+ return m_impl->m_file_cache(root, ino);
+}
+
+void
+BeesFdCache::insert_tmpfile(Fd fd)
+{
+ BeesFileId fid(fd);
+ unique_lock<mutex> lock(m_impl->m_tmpfiles_mutex);
+ auto rv = m_impl->m_tmpfiles.insert(make_pair(fid, fd));
+ THROW_CHECK1(runtime_error, fd, rv.second);
+}
+
+void
+BeesFdCache::erase_tmpfile(Fd fd)
+{
+ BeesFileId fid(fd);
+ unique_lock<mutex> lock(m_impl->m_tmpfiles_mutex);
+ auto found = m_impl->m_tmpfiles.find(fid);
+ THROW_CHECK1(runtime_error, fd, found != m_impl->m_tmpfiles.end());
+ m_impl->m_tmpfiles.erase(found);
+}
--- /dev/null
+#pragma once
+
+/// @file bees-fdcache.h
+/// LRU-cached Fds for subvolume roots and inodes, plus the temp-file registry.
+///
+/// LOGICAL_INO returns (root, inode, offset) tuples; resolving a tuple to an
+/// open Fd requires two open() calls and a tree search. BeesFdCache keeps
+/// recently-used Fds in two LRU caches sized by BEES_ROOT_FD_CACHE_SIZE and
+/// BEES_FILE_FD_CACHE_SIZE.
+///
+/// BeesFdCache also owns the temp-file registry. Temp files are created
+/// with O_TMPFILE and have no filesystem name, so open_root_ino() cannot
+/// find them by ordinary path resolution. Callers register temp files via
+/// insert_tmpfile() so open_root_ino() can return them by (root, inode).
+
+#include "bees-fwd.h"
+
+#include "crucible/fd.h"
+
+#include <cstdint>
+#include <memory>
+
+using namespace crucible;
+using namespace std;
+
+class BeesFdCache {
+ shared_ptr<BeesContext> m_ctx;
+ class Impl;
+ unique_ptr<Impl> m_impl;
+
+public:
+ BeesFdCache(shared_ptr<BeesContext> ctx);
+ ~BeesFdCache();
+ /// Cached-or-freshly-opened Fd for subvolume root @p root.
+ Fd open_root(uint64_t root);
+ /// Cached-or-freshly-opened Fd for inode @p ino in root @p root.
+ /// Consults the temp-file registry first.
+ Fd open_root_ino(uint64_t root, uint64_t ino);
+ /// Register an O_TMPFILE Fd so open_root_ino() can find it by (root, inode).
+ void insert_tmpfile(Fd fd);
+ /// Deregister a temp file when it is returned to the pool.
+ void erase_tmpfile(Fd fd);
+ /// Evict all cached Fds.
+ void clear();
+};
{
BEESTRACE("opening root " << root << " ino " << ino);
- // Check the tmpfiles map first
- {
- unique_lock<mutex> lock(m_tmpfiles_mutex);
- auto found = m_tmpfiles.find(BeesFileId(root, ino));
- if (found != m_tmpfiles.end()) {
- BEESCOUNT(open_tmpfile);
- return found->second;
- }
- }
-
Fd root_fd = open_root(root);
if (!root_fd) {
BEESCOUNT(open_no_root);
return m_ctx->fd_cache()->open_root_ino(root, ino);
}
-void
-BeesRoots::insert_tmpfile(Fd fd)
-{
- BeesFileId fid(fd);
- unique_lock<mutex> lock(m_tmpfiles_mutex);
- auto rv = m_tmpfiles.insert(make_pair(fid, fd));
- THROW_CHECK1(runtime_error, fd, rv.second);
-}
-
-void
-BeesRoots::erase_tmpfile(Fd fd)
-{
- BeesFileId fid(fd);
- unique_lock<mutex> lock(m_tmpfiles_mutex);
- auto found = m_tmpfiles.find(fid);
- THROW_CHECK1(runtime_error, fd, found != m_tmpfiles.end());
- m_tmpfiles.erase(found);
-}
-
BeesCrawl::BeesCrawl(shared_ptr<BeesContext> ctx, BeesCrawlState initial_state) :
m_ctx(ctx),
m_state(initial_state),
BEESLOGDEBUG("destroying temporary file " << this << " in " << m_ctx->root_path() << " fd " << name_fd(m_fd));
// Remove this file from open_root_ino lookup table
- m_roots->erase_tmpfile(m_fd);
+ m_fd_cache->erase_tmpfile(m_fd);
// Remove from blacklist
m_ctx->blacklist_erase(BeesFileId(m_fd));
BeesTempFile::BeesTempFile(shared_ptr<BeesContext> ctx, const BeesTempFileConfig &config) :
m_ctx(ctx),
- m_roots(ctx->roots()),
+ m_fd_cache(ctx->fd_cache()),
m_end_offset(0)
{
BEESLOGDEBUG("creating temporary file " << this << " in " << m_ctx->root_path());
m_ctx->blacklist_insert(BeesFileId(m_fd));
// Add this file to open_root_ino lookup table
- m_roots->insert_tmpfile(m_fd);
+ m_fd_cache->insert_tmpfile(m_fd);
// Count time spent here
BEESCOUNTADD(tmp_create_ms, create_timer.age() * 1000);
*/
class BeesTempFile {
shared_ptr<BeesContext> m_ctx;
- shared_ptr<BeesRoots> m_roots;
+ shared_ptr<BeesFdCache> m_fd_cache;
Fd m_fd;
/// Current logical end of used data within the temp file.
off_t m_end_offset;
#include "bees-fwd.h"
+#include "bees-fdcache.h"
#include "bees-lib.h"
#include "bees-log.h"
#include "bees-reporter.h"
vector<shared_ptr<BeesScanMode>> m_scanners; ///< Active scan-mode strategy objects.
- mutex m_tmpfiles_mutex;
- map<BeesFileId, Fd> m_tmpfiles; ///< Temp files in use by active dedup operations.
-
mutex m_stop_mutex;
condition_variable m_stop_condvar;
bool m_stop_requested = false;
/// Block until background threads have stopped.
void stop_wait();
- /// Register a temp file in the open_root_ino() lookup table.
- /// Temp files have no names, so this registry is the only way to resolve
- /// a BeesFileId back to a temp file fd.
- void insert_tmpfile(Fd fd);
- /// Deregister a temp file from the open_root_ino() lookup table
- /// when it is returned to the pool.
- void erase_tmpfile(Fd fd);
-
/// Open (with caching) the root directory for subvolume @p root.
Fd open_root(uint64_t root);
/// Open (with caching) the file at inode @p ino in subvolume @p root.
friend ostream & operator<<(ostream &os, const BeesRangePair &brp);
};
-/**
- * LRU cache for btrfs root and inode file descriptors.
- *
- * Opening a file in btrfs requires opening the subvolume root first, then
- * opening the inode within it. Both operations are expensive (ioctl + open),
- * so BeesFdCache keeps recently-used FDs in two separate LRU caches.
- * Cache capacity is controlled by BEES_ROOT_FD_CACHE_SIZE and
- * BEES_FILE_FD_CACHE_SIZE.
- */
-class BeesFdCache {
- shared_ptr<BeesContext> m_ctx;
- LRUCache<Fd, uint64_t> m_root_cache; ///< Cache keyed by root ID.
- LRUCache<Fd, uint64_t, uint64_t> m_file_cache; ///< Cache keyed by (root, inode).
- Timer m_root_cache_timer; ///< Tracks time since last cache clear.
- Timer m_file_cache_timer;
-
-public:
- BeesFdCache(shared_ptr<BeesContext> ctx);
- /// Return a cached (or freshly opened) Fd for the subvolume root @p root.
- Fd open_root(uint64_t root);
- /// Return a cached (or freshly opened) Fd for inode @p ino in root @p root.
- Fd open_root_ino(uint64_t root, uint64_t ino);
- /// Evict all cached file descriptors.
- void clear();
-};
-
/// Result of a LOGICAL_INO ioctl lookup for one physical block address.
struct BeesResolveAddrResult {
BeesResolveAddrResult();