From 8989bb0f326d56e0f9c32d7a37ad9004530fc41f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 14 Jun 2016 16:29:37 +0100 Subject: [PATCH] [caching] cache_writeback --- caching/cache_writeback.cc | 256 +++++++++++++++++++++++++++++++++---- 1 file changed, 234 insertions(+), 22 deletions(-) diff --git a/caching/cache_writeback.cc b/caching/cache_writeback.cc index f7b9a28..7dbf4c3 100644 --- a/caching/cache_writeback.cc +++ b/caching/cache_writeback.cc @@ -1,3 +1,4 @@ +#include "base/progress_monitor.h" #include "persistent-data/file_utils.h" #include "block-cache/copier.h" #include "caching/commands.h" @@ -18,28 +19,99 @@ using namespace std; //---------------------------------------------------------------- namespace { + + template T safe_div(T const n, T const d, T const def) { + return (d == T()) ? def : (n / d); + } + + //-------------------------------- + struct flags { flags() - : cache_size(1024 * 1024 * 128) { + : cache_size(4 * 1024 * 1024), + sort_buffers(16 * 1024), + list_failed_blocks(false), + update_metadata(true) { + } + + // The sort buffers have a dramatic effect on the + // performance. We give up 10% of the general buffer space + // for them. + void calc_sort_buffer_size() { + size_t sbs = cache_size / 10; + cache_size = cache_size - sbs; + + sort_buffers = sbs / sizeof(copy_op); } using maybe_string = boost::optional; size_t cache_size; + unsigned sort_buffers; maybe_string metadata_dev; maybe_string origin_dev; maybe_string fast_dev; + bool list_failed_blocks; + bool update_metadata; + }; + + //-------------------------------- + + class copy_batch { + public: + copy_batch(unsigned nr) + : max_(nr), + count_(0), + ops_(nr) { + } + + bool space() const { + return count_ < max_; + } + + void push_op(copy_op const &op) { + if (!space()) + throw runtime_error("copy_batch out of space"); + + ops_[count_++] = op; + } + + void reset() { + count_ = 0; + } + + vector::iterator begin() { + return ops_.begin(); + } + + vector::iterator end() { + return ops_.begin() + count_; + } + + private: + unsigned max_; + unsigned count_; + vector ops_; }; class copy_visitor : public mapping_visitor { public: - copy_visitor(copier &c, bool only_dirty) + copy_visitor(copier &c, unsigned sort_buffer, bool only_dirty, + bool list_failed_blocks, + progress_monitor &monitor, unsigned cache_blocks) : copier_(c), block_size_(c.get_block_size()), - only_dirty_(only_dirty) { + only_dirty_(only_dirty), + list_failed_blocks_(list_failed_blocks), + batch_(sort_buffer), + monitor_(monitor), + cache_blocks_(cache_blocks) { } virtual void visit(block_address cblock, mapping const &m) { + stats_.blocks_scanned = cblock; + update_monitor(); + if (!(m.flags_ & M_VALID)) return; @@ -52,41 +124,122 @@ namespace { cop.dest_b = m.oblock_; // blocks - copier_.issue(cop); + stats_.blocks_needed++; + batch_.push_op(cop); + if (!batch_.space()) + issue(); + } - stats_.blocks_issued++; + void issue() { + auto compare_dest = [](copy_op const &lhs, copy_op const &rhs) { + return lhs.dest_b < rhs.dest_b; + }; + sort(batch_.begin(), batch_.end(), compare_dest); -#if 0 - if (sectors_copied < block_size_) { - stats_.blocks_failed++; - stats_.sectors_failed += block_size_ - sectors_copied; + auto e = batch_.end(); + for (auto it = batch_.begin(); it != e; ++it) { + copier_.issue(*it); + stats_.blocks_issued++; + update_monitor(); + + check_for_completed_copies(); } -#endif + check_for_completed_copies(); + + batch_.reset(); + } + + void check_for_completed_copies(bool block = false) { + optional mop; + + do { + if (block) + mop = copier_.wait(); + + else { + unsigned micro = 0; + mop = copier_.wait(micro); + } + + if (mop) { + inc_completed(*mop); + if (!mop->success()) { + failed_blocks_.insert(*mop); + failed_cblocks_.insert(mop->src_b); + } + } + + } while (mop); + } + + void complete() { + issue(); + + while (copier_.nr_pending()) + check_for_completed_copies(true); + + monitor_.update_percent(100); + cerr << "\n"; + } + + void inc_completed(copy_op const &op) { + stats_.blocks_completed++; + update_monitor(); + } + + void update_monitor() { + static unsigned call_count = 0; + if (call_count++ % 128) + return; + + uint64_t scanned = stats_.blocks_scanned * 100 / cache_blocks_; + uint64_t copied = safe_div(stats_.blocks_completed * 100, + stats_.blocks_needed, 100ull); + uint64_t percent = min(scanned, copied); + monitor_.update_percent(percent); } struct copy_stats { copy_stats() - : blocks_issued(0), - blocks_failed(0), - sectors_failed(0) { + : blocks_scanned(0), + blocks_needed(0), + blocks_issued(0), + blocks_completed(0), + blocks_failed(0) { } + block_address blocks_scanned; + block_address blocks_needed; block_address blocks_issued; + block_address blocks_completed; block_address blocks_failed; - block_address sectors_failed; }; copy_stats const &get_stats() const { return stats_; } + set failed_writebacks() const { + return failed_cblocks_; + } + private: copier &copier_; unsigned block_size_; bool only_dirty_; + bool list_failed_blocks_; + copy_stats stats_; + copy_batch batch_; + progress_monitor &monitor_; + unsigned cache_blocks_; + + set failed_blocks_; + set failed_cblocks_; }; + //-------------------------------- + using namespace mapping_array_damage; class ignore_damage_visitor : public damage_visitor { @@ -117,25 +270,65 @@ namespace { return md.sb_.flags.get_flag(superblock_flags::CLEAN_SHUTDOWN); } + void update_metadata(metadata &md, set const &failed_writebacks) { + cout << "Updating metadata ... "; + + cout.flush(); + + auto &mappings = md.mappings_; + for (block_address cblock = 0; cblock < mappings->get_nr_entries(); cblock++) { + auto m = mappings->get(cblock); + if (!(m.flags_ & M_VALID)) + continue; + + if (!(m.flags_ & M_DIRTY)) + continue; + + if (failed_writebacks.count(cblock)) + continue; + + m.flags_ &= ~M_DIRTY; + cerr << "clearing dirty flag for block " << cblock << "\n"; + mappings->set(cblock, m); + } + md.commit(true); + cout << "done\n"; + cout.flush(); + } + int writeback_(flags const &f) { - block_manager<>::ptr bm = open_bm(*f.metadata_dev, block_manager<>::READ_ONLY); + block_manager<>::ptr bm = open_bm(*f.metadata_dev, block_manager<>::READ_WRITE); metadata md(bm, metadata::OPEN); - aio_engine engine(f.cache_size / md.sb_.data_block_size); + + // FIXME: we're going to have to copy runs to get the through put with small block sizes + unsigned max_ios = f.cache_size / (md.sb_.data_block_size << SECTOR_SHIFT); + aio_engine engine(max_ios); copier c(engine, *f.fast_dev, *f.origin_dev, md.sb_.data_block_size, f.cache_size); - copy_visitor cv(c, clean_shutdown(md)); + + auto bar = create_progress_bar("Copying data"); + copy_visitor cv(c, f.sort_buffers, clean_shutdown(md), f.list_failed_blocks, + *bar, md.sb_.cache_blocks); + ignore_damage_visitor dv; + walk_mapping_array(*md.mappings_, cv, dv); + cv.complete(); auto stats = cv.get_stats(); - cout << stats.blocks_issued << " copies issued\n" - << stats.blocks_failed << " copies failed\n"; + cout << stats.blocks_issued - stats.blocks_failed << "/" + << stats.blocks_issued << " blocks successfully copied.\n"; if (stats.blocks_failed) - cout << stats.sectors_failed << " sectors were not copied\n"; + cout << stats.blocks_failed << " blocks were not copied\n"; - if (dv.was_corruption()) + if (dv.was_corruption()) { cout << "Metadata corruption was found, some data may not have been copied.\n"; + if (f.update_metadata) + cout << "Unable to update metadata.\n"; + + } else if (f.update_metadata) + update_metadata(md, cv.failed_writebacks()); return (stats.blocks_failed || dv.was_corruption()) ? 1 : 0; } @@ -169,6 +362,9 @@ cache_writeback_cmd::usage(std::ostream &out) const << "\t\t--metadata-device \n" << "\t\t--origin-device \n" << "\t\t--fast-device \n" + << "\t\t--buffer-size-meg \n" + << "\t\t--list-failed-blocks\n" + << "\t\t--no-metadata-update\n" << "Options:\n" << " {-h|--help}\n" << " {-V|--version}" << endl; @@ -184,6 +380,9 @@ cache_writeback_cmd::run(int argc, char **argv) { "metadata-device", required_argument, NULL, 0 }, { "origin-device", required_argument, NULL, 1 }, { "fast-device", required_argument, NULL, 2 }, + { "buffer-size-meg", required_argument, NULL, 3 }, + { "list-failed-blocks", no_argument, NULL, 4 }, + { "no-metadata-update", no_argument, NULL, 5 }, { "help", no_argument, NULL, 'h'}, { "version", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 } @@ -203,6 +402,18 @@ cache_writeback_cmd::run(int argc, char **argv) fs.fast_dev = optarg; break; + case 3: + fs.cache_size = parse_uint64(optarg, "buffer size") * 1024 * 1024; + break; + + case 4: + fs.list_failed_blocks = true; + break; + + case 5: + fs.update_metadata = false; + break; + case 'h': usage(cout); return 0; @@ -217,6 +428,8 @@ cache_writeback_cmd::run(int argc, char **argv) } } + fs.calc_sort_buffer_size(); + if (argc != optind) { usage(cerr); return 1; @@ -241,7 +454,6 @@ cache_writeback_cmd::run(int argc, char **argv) } return writeback(fs); - } //----------------------------------------------------------------