[thin_rmap] rmap_visitor
This commit is contained in:
parent
54604161eb
commit
41204dceaa
@ -57,6 +57,7 @@ SOURCE=\
|
|||||||
thin-provisioning/metadata_checker.cc \
|
thin-provisioning/metadata_checker.cc \
|
||||||
thin-provisioning/metadata_dumper.cc \
|
thin-provisioning/metadata_dumper.cc \
|
||||||
thin-provisioning/restore_emitter.cc \
|
thin-provisioning/restore_emitter.cc \
|
||||||
|
thin-provisioning/rmap_visitor.cc \
|
||||||
thin-provisioning/superblock.cc \
|
thin-provisioning/superblock.cc \
|
||||||
thin-provisioning/thin_pool.cc \
|
thin-provisioning/thin_pool.cc \
|
||||||
thin-provisioning/xml_format.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/cache_t.cc \
|
||||||
unit-tests/damage_tracker_t.cc \
|
unit-tests/damage_tracker_t.cc \
|
||||||
unit-tests/endian_t.cc \
|
unit-tests/endian_t.cc \
|
||||||
|
unit-tests/rmap_visitor_t.cc \
|
||||||
unit-tests/space_map_t.cc \
|
unit-tests/space_map_t.cc \
|
||||||
unit-tests/span_iterator_t.cc \
|
unit-tests/span_iterator_t.cc \
|
||||||
unit-tests/transaction_manager_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
Loading…
Reference in New Issue
Block a user