diff --git a/include/dwarfs/filesystem_v2.h b/include/dwarfs/filesystem_v2.h index 8a14b5ad..518d33c5 100644 --- a/include/dwarfs/filesystem_v2.h +++ b/include/dwarfs/filesystem_v2.h @@ -42,7 +42,7 @@ struct statvfs; namespace dwarfs { -struct block_cache_options; +struct filesystem_options; struct iovec_read_buf; class filesystem_writer; @@ -55,7 +55,7 @@ class filesystem_v2 { filesystem_v2(logger& lgr, std::shared_ptr mm); filesystem_v2(logger& lgr, std::shared_ptr mm, - const block_cache_options& bc_options, + const filesystem_options& options, const struct ::stat* stat_defaults = nullptr, int inode_offset = 0); diff --git a/include/dwarfs/logger.h b/include/dwarfs/logger.h index fefe4ade..c6eec044 100644 --- a/include/dwarfs/logger.h +++ b/include/dwarfs/logger.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -60,7 +61,7 @@ class logger { policy_name_ = name; } - static level_type parse_level(const std::string& level); + static level_type parse_level(std::string_view level); private: std::string policy_name_; // TODO: const? @@ -119,14 +120,17 @@ class timed_level_logger { : data_(std::move(ll.data_)) {} ~timed_level_logger() { - std::chrono::duration sec = - std::chrono::high_resolution_clock::now() - data_->start_time; - data_->oss << " [" << time_with_unit(sec.count()) << "]"; - data_->lgr.write(data_->level, data_->oss.str()); + if (data_->output) { + std::chrono::duration sec = + std::chrono::high_resolution_clock::now() - data_->start_time; + data_->oss << " [" << time_with_unit(sec.count()) << "]"; + data_->lgr.write(data_->level, data_->oss.str()); + } } template timed_level_logger& operator<<(const T& val) { + data_->output = true; data_->oss << val; return *this; } @@ -142,6 +146,7 @@ class timed_level_logger { std::ostringstream oss; const logger::level_type level; std::chrono::time_point start_time; + bool output{false}; }; std::unique_ptr data_; diff --git a/include/dwarfs/options.h b/include/dwarfs/options.h index d2ac2ae1..f7f66845 100644 --- a/include/dwarfs/options.h +++ b/include/dwarfs/options.h @@ -26,18 +26,28 @@ namespace dwarfs { +enum class mlock_mode { NONE, TRY, MUST }; + struct block_cache_options { size_t max_bytes{0}; size_t num_workers{0}; double decompress_ratio{1.0}; }; -enum class file_order_mode { NONE, PATH, SCRIPT, SIMILARITY }; +struct filesystem_options { + mlock_mode lock_mode{mlock_mode::NONE}; + block_cache_options block_cache; +}; -std::ostream& operator<<(std::ostream& os, file_order_mode mode); +enum class file_order_mode { NONE, PATH, SCRIPT, SIMILARITY }; struct scanner_options { file_order_mode file_order; bool no_time; }; + +std::ostream& operator<<(std::ostream& os, file_order_mode mode); + +mlock_mode parse_mlock_mode(std::string_view mode); + } // namespace dwarfs diff --git a/src/dwarfs.cpp b/src/dwarfs.cpp index d4740047..2d3b92f2 100644 --- a/src/dwarfs.cpp +++ b/src/dwarfs.cpp @@ -50,9 +50,11 @@ struct options { const char* cachesize_str; // TODO: const?? -> use string? const char* debuglevel_str; // TODO: const?? -> use string? const char* workers_str; // TODO: const?? -> use string? + const char* mlock_str; // TODO: const?? -> use string? const char* decompress_ratio_str; // TODO: const?? -> use string? size_t cachesize; size_t workers; + mlock_mode lock_mode; double decompress_ratio; logger::level_type debuglevel; struct ::stat stat_defaults; @@ -68,32 +70,40 @@ struct options { const struct fuse_opt dwarfs_opts[] = { // TODO: user, group, atime, mtime, ctime for those fs who don't have it? - // second level cachesize DWARFS_OPT("cachesize=%s", cachesize_str, 0), DWARFS_OPT("debuglevel=%s", debuglevel_str, 0), DWARFS_OPT("workers=%s", workers_str, 0), - DWARFS_OPT("decratio=%s", decompress_ratio_str, 0), FUSE_OPT_END}; + DWARFS_OPT("mlock=%s", mlock_str, 0), + DWARFS_OPT("decratio=%s", decompress_ratio_str, 0), + FUSE_OPT_END}; options opts; stream_logger s_lgr(std::cerr); std::shared_ptr s_fs; +struct fuse_session* s_session; void op_init(void* /*userdata*/, struct fuse_conn_info* /*conn*/) { DEBUG_FUNC("") log_proxy log(s_lgr); - auto ti = log.timed_info(); + try { + auto ti = log.timed_info(); - block_cache_options bco; - bco.max_bytes = opts.cachesize; - bco.num_workers = opts.workers; - bco.decompress_ratio = opts.decompress_ratio; - s_fs = std::make_shared( - s_lgr, std::make_shared(opts.fsimage), bco, &opts.stat_defaults, - FUSE_ROOT_ID); + filesystem_options options; + options.lock_mode = opts.lock_mode; + options.block_cache.max_bytes = opts.cachesize; + options.block_cache.num_workers = opts.workers; + options.block_cache.decompress_ratio = opts.decompress_ratio; + s_fs = std::make_shared( + s_lgr, std::make_shared(opts.fsimage), options, + &opts.stat_defaults, FUSE_ROOT_ID); - ti << "file system initialized"; + ti << "file system initialized"; + } catch (const std::exception& e) { + log.error() << "error initializing file system: " << e.what(); + fuse_session_exit(s_session); + } } void op_destroy(void* /*userdata*/) { @@ -376,8 +386,9 @@ void usage(const char* progname) { << "DWARFS options:\n" << " -o cachesize=SIZE set size of block cache (512M)\n" << " -o workers=NUM number of worker threads (2)\n" + << " -o mlock=NAME mlock mode: (none), try, must\n" << " -o decratio=NUM ratio for full decompression (0.8)\n" - << " -o debuglevel=NAME error, warn, info, debug, trace\n" + << " -o debuglevel=NAME error, warn, (info), debug, trace\n" << std::endl; fuse_cmdline_help(); @@ -438,27 +449,27 @@ int run_fuse(struct fuse_args& args) { // fsops.getxattr = op_getxattr; // fsops.listxattr = op_listxattr; - auto se = fuse_session_new(&args, &fsops, sizeof(fsops), nullptr); + s_session = fuse_session_new(&args, &fsops, sizeof(fsops), nullptr); int err = 1; - if (se) { - if (fuse_set_signal_handlers(se) == 0) { - if (fuse_session_mount(se, fuse_opts.mountpoint) == 0) { + if (s_session) { + if (fuse_set_signal_handlers(s_session) == 0) { + if (fuse_session_mount(s_session, fuse_opts.mountpoint) == 0) { if (fuse_daemonize(fuse_opts.foreground) == 0) { if (fuse_opts.singlethread) { - err = fuse_session_loop(se); + err = fuse_session_loop(s_session); } else { struct fuse_loop_config config; config.clone_fd = fuse_opts.clone_fd; config.max_idle_threads = fuse_opts.max_idle_threads; - err = fuse_session_loop_mt(se, &config); + err = fuse_session_loop_mt(s_session, &config); } } - fuse_session_unmount(se); + fuse_session_unmount(s_session); } - fuse_remove_signal_handlers(se); + fuse_remove_signal_handlers(s_session); } - fuse_session_destroy(se); + fuse_session_destroy(s_session); } ::free(fuse_opts.mountpoint); @@ -485,6 +496,8 @@ int main(int argc, char* argv[]) { ? logger::parse_level(opts.debuglevel_str) : logger::INFO; opts.workers = opts.workers_str ? folly::to(opts.workers_str) : 2; + opts.lock_mode = + opts.mlock_str ? parse_mlock_mode(opts.mlock_str) : mlock_mode::NONE; opts.decompress_ratio = opts.decompress_ratio_str ? folly::to(opts.decompress_ratio_str) : 0.8; diff --git a/src/dwarfs/filesystem_v2.cpp b/src/dwarfs/filesystem_v2.cpp index 0b17b266..c6cf0721 100644 --- a/src/dwarfs/filesystem_v2.cpp +++ b/src/dwarfs/filesystem_v2.cpp @@ -19,6 +19,7 @@ * along with dwarfs. If not, see . */ +#include #include #include #include @@ -26,8 +27,11 @@ #include #include +#include #include +#include + #include #include @@ -141,7 +145,9 @@ make_metadata(logger& lgr, std::shared_ptr mm, section_map const& sections, std::vector& schema_buffer, std::vector& meta_buffer, const struct ::stat* stat_defaults = nullptr, - int inode_offset = 0, bool force_buffers = false) { + int inode_offset = 0, bool force_buffers = false, + mlock_mode lock_mode = mlock_mode::NONE) { + log_proxy log(lgr); auto schema_it = sections.find(section_type::METADATA_V2_SCHEMA); auto meta_it = sections.find(section_type::METADATA_V2); @@ -153,18 +159,32 @@ make_metadata(logger& lgr, std::shared_ptr mm, throw std::runtime_error("no metadata found"); } + auto meta_section = + get_section_data(mm, meta_it->second, meta_buffer, force_buffers); + + if (lock_mode != mlock_mode::NONE) { + int rv = ::mlock(meta_section.data(), meta_section.size()); + if (rv != 0) { + boost::system::error_code ec(errno, boost::system::generic_category()); + if (lock_mode == mlock_mode::MUST) { + throw boost::system::system_error(ec, "mlock"); + } else { + log.warn() << "mlock() failed: " << ec.message(); + } + } + } + return metadata_v2( lgr, get_section_data(mm, schema_it->second, schema_buffer, force_buffers), - get_section_data(mm, meta_it->second, meta_buffer, force_buffers), - stat_defaults, inode_offset); + meta_section, stat_defaults, inode_offset); } template class filesystem_ : public filesystem_v2::impl { public: filesystem_(logger& lgr_, std::shared_ptr mm, - const block_cache_options& bc_options, + const filesystem_options& options, const struct ::stat* stat_defaults, int inode_offset); void dump(std::ostream& os, int detail_level) const override; @@ -198,13 +218,13 @@ class filesystem_ : public filesystem_v2::impl { template filesystem_::filesystem_(logger& lgr, std::shared_ptr mm, - const block_cache_options& bc_options, + const filesystem_options& options, const struct ::stat* stat_defaults, int inode_offset) : log_(lgr) , mm_(mm) { filesystem_parser parser(mm_); - block_cache cache(lgr, bc_options); + block_cache cache(lgr, options.block_cache); section_map sections; @@ -223,7 +243,7 @@ filesystem_::filesystem_(logger& lgr, std::shared_ptr mm, std::vector schema_buffer; meta_ = make_metadata(lgr, mm_, sections, schema_buffer, meta_buffer_, - stat_defaults, inode_offset); + stat_defaults, inode_offset, false, options.lock_mode); log_.debug() << "read " << cache.block_count() << " blocks and " << meta_.size() << " bytes of metadata"; @@ -341,15 +361,15 @@ ssize_t filesystem_::readv(uint32_t inode, iovec_read_buf& buf, } // namespace filesystem_v2::filesystem_v2(logger& lgr, std::shared_ptr mm) - : filesystem_v2(lgr, std::move(mm), block_cache_options()) {} + : filesystem_v2(lgr, std::move(mm), filesystem_options()) {} filesystem_v2::filesystem_v2(logger& lgr, std::shared_ptr mm, - const block_cache_options& bc_options, + const filesystem_options& options, const struct ::stat* stat_defaults, int inode_offset) : impl_(make_unique_logging_object( - lgr, std::move(mm), bc_options, stat_defaults, inode_offset)) {} + lgr, std::move(mm), options, stat_defaults, inode_offset)) {} void filesystem_v2::rewrite(logger& lgr, progress& prog, std::shared_ptr mm, diff --git a/src/dwarfs/logger.cpp b/src/dwarfs/logger.cpp index b5ca5c6b..d4ffec9f 100644 --- a/src/dwarfs/logger.cpp +++ b/src/dwarfs/logger.cpp @@ -29,6 +29,8 @@ #include +#include + #include "dwarfs/logger.h" namespace dwarfs { @@ -69,18 +71,23 @@ void backtrace(std::ostream& os) { } } // namespace -logger::level_type logger::parse_level(const std::string& level) { - if (level == "error") +logger::level_type logger::parse_level(std::string_view level) { + if (level == "error") { return ERROR; - if (level == "warn") + } + if (level == "warn") { return WARN; - if (level == "info") + } + if (level == "info") { return INFO; - if (level == "debug") + } + if (level == "debug") { return DEBUG; - if (level == "trace") + } + if (level == "trace") { return TRACE; - throw std::runtime_error("invalid logger level"); + } + throw std::runtime_error(fmt::format("invalid logger level: {}", level)); } stream_logger::stream_logger(std::ostream& os, level_type threshold) diff --git a/src/dwarfs/mmap.cpp b/src/dwarfs/mmap.cpp index 604751ce..890cf4c1 100644 --- a/src/dwarfs/mmap.cpp +++ b/src/dwarfs/mmap.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include "dwarfs/mmap.h" diff --git a/src/dwarfs/options.cpp b/src/dwarfs/options.cpp index 31661cd6..887c73f4 100644 --- a/src/dwarfs/options.cpp +++ b/src/dwarfs/options.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include "dwarfs/options.h" namespace dwarfs { @@ -49,4 +51,17 @@ std::ostream& operator<<(std::ostream& os, file_order_mode mode) { return os << modestr; } +mlock_mode parse_mlock_mode(std::string_view mode) { + if (mode == "none") { + return mlock_mode::NONE; + } + if (mode == "try") { + return mlock_mode::TRY; + } + if (mode == "must") { + return mlock_mode::MUST; + } + throw std::runtime_error(fmt::format("invalid lock mode: {}", mode)); +} + } // namespace dwarfs diff --git a/src/dwarfsbench.cpp b/src/dwarfsbench.cpp index d6bf6651..a02ef6b6 100644 --- a/src/dwarfsbench.cpp +++ b/src/dwarfsbench.cpp @@ -39,7 +39,8 @@ using namespace dwarfs; namespace { int dwarfsbench(int argc, char** argv) { - std::string filesystem, cache_size_str, decompress_ratio_str, log_level; + std::string filesystem, cache_size_str, lock_mode_str, decompress_ratio_str, + log_level; size_t num_workers; size_t num_readers; @@ -58,6 +59,9 @@ int dwarfsbench(int argc, char** argv) { ("cache-size,s", po::value(&cache_size_str)->default_value("256m"), "block cache size") + ("lock-mode,m", + po::value(&cache_size_str)->default_value("none"), + "mlock mode (none, try, must)") ("decompress-ratio,r", po::value(&decompress_ratio_str)->default_value("0.8"), "block cache size") @@ -80,14 +84,15 @@ int dwarfsbench(int argc, char** argv) { } stream_logger lgr(std::cerr, logger::parse_level(log_level)); - block_cache_options bco; + filesystem_options fsopts; - bco.max_bytes = parse_size_with_unit(cache_size_str); - bco.num_workers = num_workers; - bco.decompress_ratio = folly::to(decompress_ratio_str); + fsopts.lock_mode = parse_mlock_mode(lock_mode_str); + fsopts.block_cache.max_bytes = parse_size_with_unit(cache_size_str); + fsopts.block_cache.num_workers = num_workers; + fsopts.block_cache.decompress_ratio = folly::to(decompress_ratio_str); dwarfs::filesystem_v2 fs(lgr, std::make_shared(filesystem), - bco); + fsopts); worker_group wg("reader", num_readers); diff --git a/test/dwarfs.cpp b/test/dwarfs.cpp index e68dc532..9ebd602d 100644 --- a/test/dwarfs.cpp +++ b/test/dwarfs.cpp @@ -196,10 +196,10 @@ void basic_end_to_end_test(const std::string& compressor, auto mm = std::make_shared(oss.str()); - block_cache_options bco; - bco.max_bytes = 1 << 20; + filesystem_options opts; + opts.block_cache.max_bytes = 1 << 20; - filesystem_v2 fs(lgr, mm, bco); + filesystem_v2 fs(lgr, mm, opts); auto entry = fs.find("/foo.pl"); struct ::stat st;