#include "metadata.h" #include "btree_checker.h" #include #include #include #include #include using namespace base; using namespace std; using namespace persistent_data; using namespace thin_provisioning; //---------------------------------------------------------------- namespace { // As well as the standard btree checks, we build up a set of what // devices having mappings defined, which can later be cross // referenced with the details tree. A separate block_counter is // used to later verify the data space map. class mapping_validator : public btree_checker<2, block_traits> { public: typedef boost::shared_ptr ptr; mapping_validator(block_counter &metadata_counter, block_counter &data_counter) : btree_checker<2, block_traits>(metadata_counter), data_counter_(data_counter) { } // Sharing can only occur in level 1 nodes. // FIXME: not true once we start having held roots. bool visit_internal_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { bool r = btree_checker<2, block_traits>::visit_internal_leaf(level, sub_root, key, n); if (!r && level == 0) { throw runtime_error("unexpected sharing in level 0 of mapping tree."); } for (unsigned i = 0; i < n.get_nr_entries(); i++) devices_.insert(n.key_at(i)); return r; } bool visit_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { bool r = btree_checker<2, block_traits>::visit_leaf(level, sub_root, key, n); if (r) for (unsigned i = 0; i < n.get_nr_entries(); i++) data_counter_.inc(n.value_at(i).block_); return r; } set const &get_devices() const { return devices_; } private: block_counter &data_counter_; set devices_; }; class details_validator : public btree_checker<1, device_details_traits> { public: typedef boost::shared_ptr ptr; details_validator(block_counter &counter) : btree_checker<1, device_details_traits>(counter) { } bool visit_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { bool r = btree_checker<1, device_details_traits>::visit_leaf(level, sub_root, key, n); if (r) for (unsigned i = 0; i < n.get_nr_entries(); i++) devices_.insert(n.key_at(i)); return r; } set const &get_devices() const { return devices_; } private: set devices_; }; } //---------------------------------------------------------------- thin::thin(thin_dev_t dev, metadata *metadata) : dev_(dev), metadata_(metadata) { } thin_dev_t thin::get_dev_t() const { return dev_; } thin::maybe_address thin::lookup(block_address thin_block) { uint64_t key[2] = {dev_, thin_block}; return metadata_->md_->mappings_.lookup(key); } void thin::insert(block_address thin_block, block_address data_block) { uint64_t key[2] = {dev_, thin_block}; block_time bt; bt.block_ = data_block; bt.time_ = 0; // FIXME: use current time. return metadata_->md_->mappings_.insert(key, bt); } void thin::remove(block_address thin_block) { uint64_t key[2] = {dev_, thin_block}; metadata_->md_->mappings_.remove(key); } void thin::set_snapshot_time(uint32_t time) { uint64_t key[1] = { dev_ }; optional mdetail = metadata_->md_->details_.lookup(key); if (!mdetail) throw runtime_error("no such device"); mdetail->snapshotted_time_ = time; metadata_->md_->details_.insert(key, *mdetail); } block_address thin::get_mapped_blocks() const { uint64_t key[1] = { dev_ }; optional mdetail = metadata_->md_->details_.lookup(key); if (!mdetail) throw runtime_error("no such device"); return mdetail->mapped_blocks_; } void thin::set_mapped_blocks(block_address count) { uint64_t key[1] = { dev_ }; optional mdetail = metadata_->md_->details_.lookup(key); if (!mdetail) throw runtime_error("no such device"); mdetail->mapped_blocks_ = count; metadata_->md_->details_.insert(key, *mdetail); } //-------------------------------- metadata::metadata(metadata_ll::ptr md) : md_(md) { } metadata::~metadata() { } void metadata::create_thin(thin_dev_t dev) { uint64_t key[1] = {dev}; if (device_exists(dev)) throw std::runtime_error("Device already exists"); single_mapping_tree::ptr new_tree(new single_mapping_tree(md_->tm_, block_time_ref_counter(md_->data_sm_))); md_->mappings_top_level_.insert(key, new_tree->get_root()); md_->mappings_.set_root(md_->mappings_top_level_.get_root()); // FIXME: ugly } void metadata::create_snap(thin_dev_t dev, thin_dev_t origin) { uint64_t snap_key[1] = {dev}; uint64_t origin_key[1] = {origin}; optional mtree_root = md_->mappings_top_level_.lookup(origin_key); if (!mtree_root) throw std::runtime_error("unknown origin"); single_mapping_tree otree(md_->tm_, *mtree_root, block_time_ref_counter(md_->data_sm_)); single_mapping_tree::ptr clone(otree.clone()); md_->mappings_top_level_.insert(snap_key, clone->get_root()); md_->mappings_.set_root(md_->mappings_top_level_.get_root()); // FIXME: ugly md_->sb_.time_++; thin::ptr o = open_thin(origin); thin::ptr s = open_thin(dev); o->set_snapshot_time(md_->sb_.time_); s->set_snapshot_time(md_->sb_.time_); s->set_mapped_blocks(o->get_mapped_blocks()); } void metadata::del(thin_dev_t dev) { uint64_t key[1] = {dev}; md_->mappings_top_level_.remove(key); } void metadata::set_transaction_id(uint64_t id) { md_->sb_.trans_id_ = id; } uint64_t metadata::get_transaction_id() const { return md_->sb_.trans_id_; } block_address metadata::get_held_root() const { return md_->sb_.held_root_; } block_address metadata::alloc_data_block() { return md_->data_sm_->new_block(); } void metadata::free_data_block(block_address b) { md_->data_sm_->dec(b); } block_address metadata::get_nr_free_data_blocks() const { return md_->data_sm_->get_nr_free(); } sector_t metadata::get_data_block_size() const { return md_->sb_.data_block_size_; } block_address metadata::get_data_dev_size() const { return md_->data_sm_->get_nr_blocks(); } thin::ptr metadata::open_thin(thin_dev_t dev) { uint64_t key[1] = {dev}; optional mdetails = md_->details_.lookup(key); if (!mdetails) throw runtime_error("no such device"); thin *ptr = new thin(dev, this); thin::ptr r(ptr); return r; } bool metadata::device_exists(thin_dev_t dev) const { uint64_t key[1] = {dev}; return md_->details_.lookup(key); } namespace { struct check_count : public space_map::iterator { check_count(string const &desc, block_counter const &expected) : bad_(false), expected_(expected), errors_(new error_set(desc)) { } virtual void operator() (block_address b, ref_t actual) { ref_t expected = expected_.get_count(b); if (actual != expected) { ostringstream out; out << b << ": was " << actual << ", expected " << expected; errors_->add_child(out.str()); bad_ = true; } } bool bad_; block_counter const &expected_; error_set::ptr errors_; }; optional check_ref_counts(string const &desc, block_counter const &counts, space_map::ptr sm) { check_count checker(desc, counts); sm->iterate(checker); return checker.bad_ ? optional(checker.errors_) : optional(); } } boost::optional metadata::check() { error_set::ptr errors(new error_set("Errors in metadata")); block_counter metadata_counter, data_counter; mapping_validator::ptr mv(new mapping_validator(metadata_counter, data_counter)); md_->mappings_.visit(mv); set const &mapped_devs = mv->get_devices(); details_validator::ptr dv(new details_validator(metadata_counter)); md_->details_.visit(dv); set const &details_devs = dv->get_devices(); for (set::const_iterator it = mapped_devs.begin(); it != mapped_devs.end(); ++it) if (details_devs.count(*it) == 0) { ostringstream out; out << "mapping exists for device " << *it << ", yet there is no entry in the details tree."; throw runtime_error(out.str()); } metadata_counter.inc(SUPERBLOCK_LOCATION); md_->metadata_sm_->check(metadata_counter); md_->data_sm_->check(metadata_counter); errors->add_child(check_ref_counts("Errors in metadata block reference counts", metadata_counter, md_->metadata_sm_)); errors->add_child(check_ref_counts("Errors in data block reference counts", data_counter, md_->data_sm_)); return (errors->get_children().size() > 0) ? optional(errors) : optional(); } //----------------------------------------------------------------