[thin_rmap] rmap_visitor
This commit is contained 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 | ||||
|   | ||||
							
								
								
									
										103
									
								
								thin-provisioning/rmap_visitor.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								thin-provisioning/rmap_visitor.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| #include "thin-provisioning/rmap_visitor.h" | ||||
|  | ||||
| #include <iostream> | ||||
|  | ||||
| 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<rmap_visitor::rmap_region> 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<rmap_region>(); | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
							
								
								
									
										68
									
								
								thin-provisioning/rmap_visitor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								thin-provisioning/rmap_visitor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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<block_address> 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<rmap_region> 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<region> regions_; | ||||
|  | ||||
| 		boost::optional<rmap_region> current_rmap_; | ||||
| 		vector<rmap_region> rmap_; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #endif | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										156
									
								
								unit-tests/rmap_visitor_t.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								unit-tests/rmap_visitor_t.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
| } | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
		Reference in New Issue
	
	Block a user