Fix #71: driver hangs when unmounting

The tidy thread that gets spawned when constructing the block cache
doesn't survive the fork to background when the driver is not being
run in foreground mode. This is a familar problem that we already
ran into with the cache worker threads. The fix is simple: just delay
the thread spawning until after we've forked to background.
This commit is contained in:
Marcus Holland-Moritz 2021-11-20 13:01:41 +01:00
parent 569966b752
commit 14654b6f38
8 changed files with 75 additions and 24 deletions

View File

@ -33,6 +33,7 @@
namespace dwarfs { namespace dwarfs {
struct block_cache_options; struct block_cache_options;
struct cache_tidy_config;
class block_range; class block_range;
class fs_section; class fs_section;
@ -52,6 +53,10 @@ class block_cache {
void set_num_workers(size_t num) { impl_->set_num_workers(num); } void set_num_workers(size_t num) { impl_->set_num_workers(num); }
void set_tidy_config(cache_tidy_config const& cfg) {
impl_->set_tidy_config(cfg);
}
std::future<block_range> std::future<block_range>
get(size_t block_no, size_t offset, size_t size) const { get(size_t block_no, size_t offset, size_t size) const {
return impl_->get(block_no, offset, size); return impl_->get(block_no, offset, size);
@ -65,6 +70,7 @@ class block_cache {
virtual void insert(fs_section const& section) = 0; virtual void insert(fs_section const& section) = 0;
virtual void set_block_size(size_t size) = 0; virtual void set_block_size(size_t size) = 0;
virtual void set_num_workers(size_t num) = 0; virtual void set_num_workers(size_t num) = 0;
virtual void set_tidy_config(cache_tidy_config const& cfg) = 0;
virtual std::future<block_range> virtual std::future<block_range>
get(size_t block_no, size_t offset, size_t length) const = 0; get(size_t block_no, size_t offset, size_t length) const = 0;
}; };

View File

@ -44,6 +44,7 @@ struct statvfs;
namespace dwarfs { namespace dwarfs {
struct cache_tidy_config;
struct filesystem_options; struct filesystem_options;
struct rewrite_options; struct rewrite_options;
struct iovec_read_buf; struct iovec_read_buf;
@ -152,6 +153,9 @@ class filesystem_v2 {
std::optional<folly::ByteRange> header() const { return impl_->header(); } std::optional<folly::ByteRange> header() const { return impl_->header(); }
void set_num_workers(size_t num) { return impl_->set_num_workers(num); } void set_num_workers(size_t num) { return impl_->set_num_workers(num); }
void set_cache_tidy_config(cache_tidy_config const& cfg) {
return impl_->set_cache_tidy_config(cfg);
}
class impl { class impl {
public: public:
@ -188,6 +192,7 @@ class filesystem_v2 {
readv(uint32_t inode, size_t size, off_t offset) const = 0; readv(uint32_t inode, size_t size, off_t offset) const = 0;
virtual std::optional<folly::ByteRange> header() const = 0; virtual std::optional<folly::ByteRange> header() const = 0;
virtual void set_num_workers(size_t num) = 0; virtual void set_num_workers(size_t num) = 0;
virtual void set_cache_tidy_config(cache_tidy_config const& cfg) = 0;
}; };
private: private:

View File

@ -34,6 +34,7 @@
namespace dwarfs { namespace dwarfs {
struct cache_tidy_config;
class block_cache; class block_cache;
class logger; class logger;
struct iovec_read_buf; struct iovec_read_buf;
@ -67,6 +68,10 @@ class inode_reader_v2 {
void set_num_workers(size_t num) { impl_->set_num_workers(num); } void set_num_workers(size_t num) { impl_->set_num_workers(num); }
void set_cache_tidy_config(cache_tidy_config const& cfg) {
impl_->set_cache_tidy_config(cfg);
}
class impl { class impl {
public: public:
virtual ~impl() = default; virtual ~impl() = default;
@ -80,6 +85,7 @@ class inode_reader_v2 {
virtual void dump(std::ostream& os, const std::string& indent, virtual void dump(std::ostream& os, const std::string& indent,
chunk_range chunks) const = 0; chunk_range chunks) const = 0;
virtual void set_num_workers(size_t num) = 0; virtual void set_num_workers(size_t num) = 0;
virtual void set_cache_tidy_config(cache_tidy_config const& cfg) = 0;
}; };
private: private:

View File

@ -40,9 +40,12 @@ struct block_cache_options {
double decompress_ratio{1.0}; double decompress_ratio{1.0};
bool mm_release{true}; bool mm_release{true};
bool init_workers{true}; bool init_workers{true};
cache_tidy_strategy tidy_strategy{cache_tidy_strategy::NONE}; };
std::chrono::milliseconds tidy_interval;
std::chrono::milliseconds tidy_expiry_time; struct cache_tidy_config {
cache_tidy_strategy strategy{cache_tidy_strategy::NONE};
std::chrono::milliseconds interval;
std::chrono::milliseconds expiry_time;
}; };
struct metadata_options { struct metadata_options {

View File

@ -132,6 +132,14 @@ void op_init(void* data, struct fuse_conn_info* /*conn*/) {
// we must do this *after* the fuse driver has forked into background // we must do this *after* the fuse driver has forked into background
userdata->fs.set_num_workers(userdata->opts.workers); userdata->fs.set_num_workers(userdata->opts.workers);
cache_tidy_config tidy;
tidy.strategy = userdata->opts.block_cache_tidy_strategy;
tidy.interval = userdata->opts.block_cache_tidy_interval;
tidy.expiry_time = userdata->opts.block_cache_tidy_max_age;
// we must do this *after* the fuse driver has forked into background
userdata->fs.set_cache_tidy_config(tidy);
} }
template <typename LoggerPolicy> template <typename LoggerPolicy>
@ -646,9 +654,6 @@ void load_filesystem(dwarfs_userdata& userdata) {
fsopts.block_cache.decompress_ratio = opts.decompress_ratio; fsopts.block_cache.decompress_ratio = opts.decompress_ratio;
fsopts.block_cache.mm_release = !opts.cache_image; fsopts.block_cache.mm_release = !opts.cache_image;
fsopts.block_cache.init_workers = false; fsopts.block_cache.init_workers = false;
fsopts.block_cache.tidy_strategy = opts.block_cache_tidy_strategy;
fsopts.block_cache.tidy_interval = opts.block_cache_tidy_interval;
fsopts.block_cache.tidy_expiry_time = opts.block_cache_tidy_max_age;
fsopts.metadata.enable_nlink = bool(opts.enable_nlink); fsopts.metadata.enable_nlink = bool(opts.enable_nlink);
fsopts.metadata.readonly = bool(opts.readonly); fsopts.metadata.readonly = bool(opts.readonly);

View File

@ -237,23 +237,13 @@ class block_cache_ final : public block_cache::impl {
: std::thread::hardware_concurrency(), : std::thread::hardware_concurrency(),
static_cast<size_t>(1))); static_cast<size_t>(1)));
} }
if (options.tidy_strategy != cache_tidy_strategy::NONE) {
tidy_running_ = true;
tidy_thread_ = std::thread(&block_cache_::tidy_thread, this);
}
} }
~block_cache_() noexcept override { ~block_cache_() noexcept override {
LOG_DEBUG << "stopping cache workers"; LOG_DEBUG << "stopping cache workers";
if (tidy_running_) { if (tidy_running_) {
{ stop_tidy_thread();
std::lock_guard lock(mx_);
tidy_running_ = false;
}
tidy_cond_.notify_all();
tidy_thread_.join();
} }
if (wg_) { if (wg_) {
@ -347,6 +337,25 @@ class block_cache_ final : public block_cache::impl {
wg_ = worker_group("blkcache", num); wg_ = worker_group("blkcache", num);
} }
void set_tidy_config(cache_tidy_config const& cfg) override {
if (cfg.strategy == cache_tidy_strategy::NONE) {
if (tidy_running_) {
stop_tidy_thread();
}
} else {
std::lock_guard lock(mx_);
tidy_config_ = cfg;
if (tidy_running_) {
tidy_cond_.notify_all();
} else {
tidy_running_ = true;
tidy_thread_ = std::thread(&block_cache_::tidy_thread, this);
}
}
}
std::future<block_range> std::future<block_range>
get(size_t block_no, size_t offset, size_t size) const override { get(size_t block_no, size_t offset, size_t size) const override {
++range_requests_; ++range_requests_;
@ -492,6 +501,15 @@ class block_cache_ final : public block_cache::impl {
} }
private: private:
void stop_tidy_thread() {
{
std::lock_guard lock(mx_);
tidy_running_ = false;
}
tidy_cond_.notify_all();
tidy_thread_.join();
}
void update_block_stats(cached_block const& cb) { void update_block_stats(cached_block const& cb) {
if (cb.range_end() < cb.uncompressed_size()) { if (cb.range_end() < cb.uncompressed_size()) {
++partially_decompressed_; ++partially_decompressed_;
@ -582,15 +600,16 @@ class block_cache_ final : public block_cache::impl {
} }
} }
if (options_.tidy_strategy == cache_tidy_strategy::EXPIRY_TIME) {
block->touch();
}
// Finally, put the block into the cache; it might already be // Finally, put the block into the cache; it might already be
// in there, in which case we just promote it to the front of // in there, in which case we just promote it to the front of
// the LRU queue. // the LRU queue.
{ {
std::lock_guard lock(mx_); std::lock_guard lock(mx_);
if (tidy_config_.strategy == cache_tidy_strategy::EXPIRY_TIME) {
block->touch();
}
cache_.set(block_no, std::move(block)); cache_.set(block_no, std::move(block));
} }
} }
@ -615,13 +634,13 @@ class block_cache_ final : public block_cache::impl {
std::unique_lock lock(mx_); std::unique_lock lock(mx_);
while (tidy_running_) { while (tidy_running_) {
if (tidy_cond_.wait_for(lock, options_.tidy_interval) == if (tidy_cond_.wait_for(lock, tidy_config_.interval) ==
std::cv_status::timeout) { std::cv_status::timeout) {
switch (options_.tidy_strategy) { switch (tidy_config_.strategy) {
case cache_tidy_strategy::EXPIRY_TIME: case cache_tidy_strategy::EXPIRY_TIME:
tidy_collect( tidy_collect(
[tp = std::chrono::steady_clock::now() - [tp = std::chrono::steady_clock::now() -
options_.tidy_expiry_time](cached_block const& blk) { tidy_config_.expiry_time](cached_block const& blk) {
return blk.last_used_before(tp); return blk.last_used_before(tp);
}); });
break; break;
@ -675,6 +694,7 @@ class block_cache_ final : public block_cache::impl {
std::shared_ptr<mmif> mm_; std::shared_ptr<mmif> mm_;
LOG_PROXY_DECL(LoggerPolicy); LOG_PROXY_DECL(LoggerPolicy);
const block_cache_options options_; const block_cache_options options_;
cache_tidy_config tidy_config_;
}; };
block_cache::block_cache(logger& lgr, std::shared_ptr<mmif> mm, block_cache::block_cache(logger& lgr, std::shared_ptr<mmif> mm,

View File

@ -350,6 +350,9 @@ class filesystem_ final : public filesystem_v2::impl {
readv(uint32_t inode, size_t size, off_t offset) const override; readv(uint32_t inode, size_t size, off_t offset) const override;
std::optional<folly::ByteRange> header() const override; std::optional<folly::ByteRange> header() const override;
void set_num_workers(size_t num) override { ir_.set_num_workers(num); } void set_num_workers(size_t num) override { ir_.set_num_workers(num); }
void set_cache_tidy_config(cache_tidy_config const& cfg) override {
ir_.set_cache_tidy_config(cfg);
}
private: private:
filesystem_info const& get_info() const; filesystem_info const& get_info() const;

View File

@ -69,6 +69,9 @@ class inode_reader_ final : public inode_reader_v2::impl {
void dump(std::ostream& os, const std::string& indent, void dump(std::ostream& os, const std::string& indent,
chunk_range chunks) const override; chunk_range chunks) const override;
void set_num_workers(size_t num) override { cache_.set_num_workers(num); } void set_num_workers(size_t num) override { cache_.set_num_workers(num); }
void set_cache_tidy_config(cache_tidy_config const& cfg) override {
cache_.set_tidy_config(cfg);
}
private: private:
folly::Expected<std::vector<std::future<block_range>>, int> folly::Expected<std::vector<std::future<block_range>>, int>