#include "persistent-data/file_utils.h" #include "block-cache/copier.h" #include "caching/commands.h" #include "caching/mapping_array.h" #include "caching/metadata.h" #include "version.h" #include #include #include #include using namespace bcache; using namespace caching; using namespace boost; using namespace std; //---------------------------------------------------------------- namespace { struct flags { flags() : cache_size(1024 * 1024 * 128) { } using maybe_string = boost::optional; size_t cache_size; maybe_string metadata_dev; maybe_string origin_dev; maybe_string fast_dev; }; class copy_visitor : public mapping_visitor { public: copy_visitor(copier &c, bool only_dirty) : copier_(c), block_size_(c.get_block_size()), only_dirty_(only_dirty) { } virtual void visit(block_address cblock, mapping const &m) { if (!(m.flags_ & M_VALID)) return; if (only_dirty_ && !(m.flags_ & M_DIRTY)) return; copy_op cop; cop.src_b = cblock; cop.src_e = cblock + 1ull; cop.dest_b = m.oblock_; // blocks copier_.issue(cop); stats_.blocks_issued++; #if 0 if (sectors_copied < block_size_) { stats_.blocks_failed++; stats_.sectors_failed += block_size_ - sectors_copied; } #endif } struct copy_stats { copy_stats() : blocks_issued(0), blocks_failed(0), sectors_failed(0) { } block_address blocks_issued; block_address blocks_failed; block_address sectors_failed; }; copy_stats const &get_stats() const { return stats_; } private: copier &copier_; unsigned block_size_; bool only_dirty_; copy_stats stats_; }; using namespace mapping_array_damage; class ignore_damage_visitor : public damage_visitor { public: ignore_damage_visitor() : corruption_(false) { } void visit(missing_mappings const &d) { cerr << "missing mappings (" << d.keys_.begin_ << ", " << d.keys_.end_ << "]\n"; corruption_ = true; } void visit(invalid_mapping const &d) { cerr << "invalid mapping cblock = " << d.cblock_ << ", oblock = " << d.m_.oblock_ << "\n"; corruption_ = true; } bool was_corruption() const { return corruption_; } private: bool corruption_; }; bool clean_shutdown(metadata const &md) { return md.sb_.flags.get_flag(superblock_flags::CLEAN_SHUTDOWN); } int writeback_(flags const &f) { block_manager<>::ptr bm = open_bm(*f.metadata_dev, block_manager<>::READ_ONLY); metadata md(bm, metadata::OPEN); aio_engine engine(f.cache_size / md.sb_.data_block_size); copier c(engine, *f.fast_dev, *f.origin_dev, md.sb_.data_block_size, f.cache_size); copy_visitor cv(c, clean_shutdown(md)); ignore_damage_visitor dv; walk_mapping_array(*md.mappings_, cv, dv); auto stats = cv.get_stats(); cout << stats.blocks_issued << " copies issued\n" << stats.blocks_failed << " copies failed\n"; if (stats.blocks_failed) cout << stats.sectors_failed << " sectors were not copied\n"; if (dv.was_corruption()) cout << "Metadata corruption was found, some data may not have been copied.\n"; return (stats.blocks_failed || dv.was_corruption()) ? 1 : 0; } int writeback(flags const &f) { int r; try { r = writeback_(f); } catch (std::exception &e) { cerr << e.what() << endl; return 1; } return r; } } //---------------------------------------------------------------- cache_writeback_cmd::cache_writeback_cmd() : command("cache_writeback") { } void cache_writeback_cmd::usage(std::ostream &out) const { out << "Usage: " << get_name() << " [options]\n" << "\t\t--metadata-device \n" << "\t\t--origin-device \n" << "\t\t--fast-device \n" << "Options:\n" << " {-h|--help}\n" << " {-V|--version}" << endl; } int cache_writeback_cmd::run(int argc, char **argv) { int c; flags fs; char const *short_opts = "hV"; option const long_opts[] = { { "metadata-device", required_argument, NULL, 0 }, { "origin-device", required_argument, NULL, 1 }, { "fast-device", required_argument, NULL, 2 }, { "help", no_argument, NULL, 'h'}, { "version", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 } }; while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch(c) { case 0: fs.metadata_dev = optarg; break; case 1: fs.origin_dev = optarg; break; case 2: fs.fast_dev = optarg; break; case 'h': usage(cout); return 0; case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; default: usage(cerr); return 1; } } if (argc != optind) { usage(cerr); return 1; } if (!fs.metadata_dev) { cerr << "No metadata device provided.\n\n"; usage(cerr); return 1; } if (!fs.origin_dev) { cerr << "No origin device provided.\n\n"; usage(cerr); return 1; } if (!fs.fast_dev) { cerr << "No fast device provided.\n\n"; usage(cerr); return 1; } return writeback(fs); } //----------------------------------------------------------------