From 41204dceaa56470201b762d31b74b9e96f4394f1 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 24 May 2013 13:30:17 +0100 Subject: [PATCH] [thin_rmap] rmap_visitor --- Makefile.in | 1 + thin-provisioning/rmap_visitor.cc | 103 ++++++++++++++++++++ thin-provisioning/rmap_visitor.h | 68 +++++++++++++ unit-tests/Makefile.in | 1 + unit-tests/rmap_visitor_t.cc | 156 ++++++++++++++++++++++++++++++ 5 files changed, 329 insertions(+) create mode 100644 thin-provisioning/rmap_visitor.cc create mode 100644 thin-provisioning/rmap_visitor.h create mode 100644 unit-tests/rmap_visitor_t.cc diff --git a/Makefile.in b/Makefile.in index bcdba26..ced5d08 100644 --- a/Makefile.in +++ b/Makefile.in @@ -57,6 +57,7 @@ SOURCE=\ thin-provisioning/metadata_checker.cc \ thin-provisioning/metadata_dumper.cc \ thin-provisioning/restore_emitter.cc \ + thin-provisioning/rmap_visitor.cc \ thin-provisioning/superblock.cc \ thin-provisioning/thin_pool.cc \ thin-provisioning/xml_format.cc diff --git a/thin-provisioning/rmap_visitor.cc b/thin-provisioning/rmap_visitor.cc new file mode 100644 index 0000000..d0914e1 --- /dev/null +++ b/thin-provisioning/rmap_visitor.cc @@ -0,0 +1,103 @@ +#include "thin-provisioning/rmap_visitor.h" + +#include + +using namespace thin_provisioning; + +//---------------------------------------------------------------- + +rmap_visitor::rmap_visitor() +{ +} + +void +rmap_visitor::add_data_region(region const &r) +{ + regions_.push_back(r); +} + +void +rmap_visitor::visit(btree_path const &path, + mapping_tree_detail::block_time const &bt) +{ + if (in_regions(bt.block_)) { + uint32_t thin_dev = path[0]; + block_address thin_block = path[1]; + + visit_block(thin_dev, thin_block, bt.block_); + } +} + +void +rmap_visitor::complete() +{ + if (current_rmap_) + push_current(); +} + +vector const & +rmap_visitor::get_rmap() const +{ + return rmap_; +} + +// Slow, but I suspect we wont run with many regions. +bool +rmap_visitor::in_regions(block_address b) const +{ + for (region const &r : regions_) + if (r.contains(b)) + return true; + + return false; +} + +bool +rmap_visitor::adjacent_block(rmap_region const &rr, + uint32_t thin_dev, block_address thin_block, + block_address data_block) const +{ + block_address run_length = rr.data_end - rr.data_begin; + + return (rr.thin_dev == thin_dev) && + (data_block == rr.data_end) && + (thin_block == rr.thin_begin + run_length); +} + +void +rmap_visitor::insert_new_region(uint32_t thin_dev, block_address thin_block, + block_address data_block) +{ + rmap_region rr; + rr.data_begin = data_block; + rr.data_end = data_block + 1; + rr.thin_dev = thin_dev; + rr.thin_begin = thin_block; + + current_rmap_ = rr; +} + +void +rmap_visitor::push_current() +{ + rmap_.push_back(*current_rmap_); + current_rmap_ = boost::optional(); +} + +void +rmap_visitor::visit_block(uint32_t thin_dev, block_address thin_block, + block_address data_block) +{ + if (current_rmap_) { + if (adjacent_block(*current_rmap_, thin_dev, thin_block, data_block)) + current_rmap_->data_end++; + else { + push_current(); + insert_new_region(thin_dev, thin_block, data_block); + } + + } else + insert_new_region(thin_dev, thin_block, data_block); +} + +//---------------------------------------------------------------- diff --git a/thin-provisioning/rmap_visitor.h b/thin-provisioning/rmap_visitor.h new file mode 100644 index 0000000..7d19731 --- /dev/null +++ b/thin-provisioning/rmap_visitor.h @@ -0,0 +1,68 @@ +#ifndef THIN_RMAP_VISITOR_H +#define THIN_RMAP_VISITOR_H + +#include "persistent-data/range.h" +#include "thin-provisioning/mapping_tree.h" + +//---------------------------------------------------------------- + +namespace thin_provisioning { + // This value visitor (see btree_damage_visitor.h) produces a + // reverse mapping for parts of the data device. + // + // i) Use add_data_region() to specify which areas of the data device you're interested in. + // ii) visit the mapping tree + // iii) call complete() + // iv) get the rmaps with get_rmap(); + class rmap_visitor { + public: + typedef range region; + + rmap_visitor(); + + // Specify which regions of the data device you want the rmap for. + void add_data_region(region const &r); + void visit(btree_path const &path, mapping_tree_detail::block_time const &bt); + + struct rmap_region { + // FIXME: surely we don't need to provide this for + // a POD structure? + bool operator ==(rmap_region const &rhs) const { + return ((data_begin == rhs.data_begin) && + (data_end == rhs.data_end) && + (thin_dev == rhs.thin_dev) && + (thin_begin == rhs.thin_begin)); + } + + block_address data_begin; + block_address data_end; + + uint32_t thin_dev; + block_address thin_begin; + }; + + void complete(); + vector const &get_rmap() const; + + private: + bool in_regions(block_address b) const; + bool adjacent_block(rmap_region const &rr, + uint32_t thin_dev, block_address thin_block, + block_address data_block) const; + void insert_new_region(uint32_t thin_dev, block_address thin_block, + block_address data_block); + void push_current(); + + void visit_block(uint32_t thin_dev, block_address thin_block, + block_address data_block); + + vector regions_; + + boost::optional current_rmap_; + vector rmap_; + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/unit-tests/Makefile.in b/unit-tests/Makefile.in index cde62d9..eb4e579 100644 --- a/unit-tests/Makefile.in +++ b/unit-tests/Makefile.in @@ -52,6 +52,7 @@ TEST_SOURCE=\ unit-tests/cache_t.cc \ unit-tests/damage_tracker_t.cc \ unit-tests/endian_t.cc \ + unit-tests/rmap_visitor_t.cc \ unit-tests/space_map_t.cc \ unit-tests/span_iterator_t.cc \ unit-tests/transaction_manager_t.cc diff --git a/unit-tests/rmap_visitor_t.cc b/unit-tests/rmap_visitor_t.cc new file mode 100644 index 0000000..6f78e6a --- /dev/null +++ b/unit-tests/rmap_visitor_t.cc @@ -0,0 +1,156 @@ +#include "gmock/gmock.h" + +#include "thin-provisioning/rmap_visitor.h" + +using namespace std; +using namespace thin_provisioning; +using namespace testing; + +//---------------------------------------------------------------- + +namespace { + typedef rmap_visitor::rmap_region rmap_region; + + class RMapVisitorTests : public Test { + public: + RMapVisitorTests() { + } + + void visit(uint32_t thin_dev, block_address thin_block, block_address data_block) { + btree_path path; + path.push_back(thin_dev); + path.push_back(thin_block); + + mapping_tree_detail::block_time bt; + bt.block_ = data_block; + bt.time_ = 0; + + rmap_v_.visit(path, bt); + } + + void run() { + rmap_v_.complete(); + } + + void linear_thins(unsigned nr_thins, unsigned blocks_per_thin) { + for (uint32_t thin_dev = 0; thin_dev < nr_thins; thin_dev++) + for (block_address b = 0; b < blocks_per_thin; b++) + visit(thin_dev, b, thin_dev * blocks_per_thin + b); + } + + void check_rmap_size(unsigned expected) { + ASSERT_THAT(rmap_v_.get_rmap().size(), Eq(expected)); + } + + void check_rmap_at(unsigned index, + block_address data_begin, block_address data_end, + uint32_t thin_dev, block_address thin_begin) { + + rmap_region expected; + + expected.data_begin = data_begin; + expected.data_end = data_end; + expected.thin_dev = thin_dev; + expected.thin_begin = thin_begin; + + rmap_region actual = rmap_v_.get_rmap().at(index); + + ASSERT_THAT(actual, Eq(expected)); + } + + void add_data_region(block_address data_begin, block_address data_end) { + rmap_v_.add_data_region(rmap_visitor::region(data_begin, data_end)); + } + + rmap_visitor rmap_v_; + }; + + ostream &operator <<(ostream &out, rmap_region const &r) { + out << "rmap_region [data_begin = " << r.data_begin + << ", data_end = " << r.data_end + << ", thin_dev = " << r.thin_dev + << ", thin_begin = " << r.thin_begin + << endl; + return out; + } +} + +//---------------------------------------------------------------- + +TEST_F(RMapVisitorTests, no_mapped_blocks) +{ + run(); + check_rmap_size(0); +} + +TEST_F(RMapVisitorTests, no_rmap_regions) +{ + linear_thins(10, 100); + + run(); + check_rmap_size(0); +} + +TEST_F(RMapVisitorTests, region_exactly_covers_thin) +{ + add_data_region(0, 100); + linear_thins(10, 100); + + run(); + + check_rmap_size(1); + check_rmap_at(0, 0, 100, 0, 0); +} + +TEST_F(RMapVisitorTests, region_smaller_than_a_thin) +{ + add_data_region(25, 75); + linear_thins(10, 100); + + run(); + + check_rmap_size(1); + check_rmap_at(0, 25, 75, 0, 25); +} + +TEST_F(RMapVisitorTests, region_overlaps_two_thins) +{ + add_data_region(75, 125); + linear_thins(10, 100); + + run(); + + check_rmap_size(2); + check_rmap_at(0, 75, 100, 0, 75); + check_rmap_at(1, 100, 125, 1, 0); +} + +TEST_F(RMapVisitorTests, two_regions) +{ + add_data_region(75, 125); + add_data_region(350, 450); + linear_thins(10, 100); + + run(); + + check_rmap_size(4); + check_rmap_at(0, 75, 100, 0, 75); + check_rmap_at(1, 100, 125, 1, 0); + check_rmap_at(2, 350, 400, 3, 50); + check_rmap_at(3, 400, 450, 4, 0); +} + +TEST_F(RMapVisitorTests, overlapping_regions) +{ + add_data_region(25, 75); + add_data_region(50, 125); + linear_thins(10, 100); + + run(); + + check_rmap_size(2); + check_rmap_at(0, 25, 100, 0, 25); + check_rmap_at(1, 100, 125, 1, 0); +} + +//----------------------------------------------------------------