[thin_repair] repair now checks the data reference counts.
This commit is contained in:
parent
511456f903
commit
987a8360c9
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@
|
||||
test.data
|
||||
thin_dump
|
||||
thin_repair
|
||||
*.metadata
|
1
Makefile
1
Makefile
@ -1,5 +1,6 @@
|
||||
SOURCE=\
|
||||
endian_utils.cc \
|
||||
error_set.cc \
|
||||
metadata.cc \
|
||||
metadata_disk_structures.cc
|
||||
|
||||
|
12
btree.h
12
btree.h
@ -320,9 +320,12 @@ namespace persistent_data {
|
||||
virtual ~visitor() {}
|
||||
typedef boost::shared_ptr<visitor> ptr;
|
||||
|
||||
virtual void visit_internal(unsigned level, btree_detail::node_ref<uint64_traits, BlockSize> const &n) = 0;
|
||||
virtual void visit_internal_leaf(unsigned level, btree_detail::node_ref<uint64_traits, BlockSize> const &n) = 0;
|
||||
virtual void visit_leaf(unsigned level, btree_detail::node_ref<ValueTraits, BlockSize> const &n) = 0;
|
||||
virtual void visit_internal(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<uint64_traits, BlockSize> const &n) = 0;
|
||||
virtual void visit_internal_leaf(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<uint64_traits, BlockSize> const &n) = 0;
|
||||
virtual void visit_leaf(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<ValueTraits, BlockSize> const &n) = 0;
|
||||
};
|
||||
|
||||
// Walks the tree in depth first order
|
||||
@ -351,7 +354,8 @@ namespace persistent_data {
|
||||
int *index);
|
||||
|
||||
void walk_tree(typename visitor::ptr visitor,
|
||||
unsigned level, block_address b);
|
||||
unsigned level, bool is_root,
|
||||
block_address b);
|
||||
|
||||
typename persistent_data::transaction_manager<BlockSize>::ptr tm_;
|
||||
bool destroy_;
|
||||
|
15
btree.tcc
15
btree.tcc
@ -614,32 +614,33 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
void
|
||||
btree<Levels, ValueTraits, BlockSize>::visit(typename visitor::ptr visitor)
|
||||
{
|
||||
walk_tree(visitor, 0, root_);
|
||||
walk_tree(visitor, 0, true, root_);
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
void
|
||||
btree<Levels, ValueTraits, BlockSize>::
|
||||
walk_tree(typename visitor::ptr visitor,
|
||||
unsigned level, block_address b)
|
||||
unsigned level, bool is_root,
|
||||
block_address b)
|
||||
{
|
||||
using namespace btree_detail;
|
||||
|
||||
auto blk = tm_->read_lock(b);
|
||||
auto o = to_node<uint64_traits, BlockSize>(blk);
|
||||
if (o.get_type() == INTERNAL) {
|
||||
visitor->visit_internal(level, o);
|
||||
visitor->visit_internal(level, is_root, o);
|
||||
for (unsigned i = 0; i < o.get_nr_entries(); i++)
|
||||
walk_tree(visitor, level, o.value_at(i));
|
||||
walk_tree(visitor, level, false, o.value_at(i));
|
||||
|
||||
} else if (level < Levels - 1) {
|
||||
visitor->visit_internal_leaf(level, o);
|
||||
visitor->visit_internal_leaf(level, is_root, o);
|
||||
for (unsigned i = 0; i < o.get_nr_entries(); i++)
|
||||
walk_tree(visitor, level + 1, o.value_at(i));
|
||||
walk_tree(visitor, level + 1, true, o.value_at(i));
|
||||
|
||||
} else {
|
||||
auto ov = to_node<ValueTraits, BlockSize>(blk);
|
||||
visitor->visit_leaf(level, ov);
|
||||
visitor->visit_leaf(level, is_root, ov);
|
||||
}
|
||||
}
|
||||
|
||||
|
162
btree_validator.h
Normal file
162
btree_validator.h
Normal file
@ -0,0 +1,162 @@
|
||||
#ifndef BTREE_VALIDATOR_H
|
||||
#define BTREE_VALIDATOR_H
|
||||
|
||||
#include "btree.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
//----------------------------------------------------------------
|
||||
// Little helper class that keeps track of how many times blocks
|
||||
// are referenced.
|
||||
//----------------------------------------------------------------
|
||||
class block_counter {
|
||||
public:
|
||||
void inc(block_address b) {
|
||||
auto it = counts_.find(b);
|
||||
if (it == counts_.end())
|
||||
counts_.insert(make_pair(b, 1));
|
||||
#if 0
|
||||
else
|
||||
it->second++;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned get_count(block_address b) const {
|
||||
auto it = counts_.find(b);
|
||||
return (it == counts_.end()) ? 0 : it->second;
|
||||
}
|
||||
|
||||
std::map<block_address, unsigned> const &get_counts() const {
|
||||
return counts_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<block_address, unsigned> counts_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// This class implements consistency checking for the btrees in
|
||||
// general. Derive from this if you want some additional checks.
|
||||
// It's worth summarising what is checked:
|
||||
//
|
||||
// Implemented
|
||||
// -----------
|
||||
//
|
||||
// - block_nr
|
||||
// - nr_entries < max_entries
|
||||
// - max_entries fits in block
|
||||
// - max_entries is divisible by 3
|
||||
// - nr_entries > minimum (except for root nodes)
|
||||
//
|
||||
// Not implemented
|
||||
// ---------------
|
||||
//
|
||||
// - checksum
|
||||
// - leaf | internal flags (this can be inferred from siblings)
|
||||
//----------------------------------------------------------------
|
||||
template <uint32_t Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
class btree_validator : public btree<Levels, ValueTraits, BlockSize>::visitor {
|
||||
public:
|
||||
btree_validator(block_counter &counter)
|
||||
: counter_(counter) {
|
||||
}
|
||||
|
||||
void visit_internal(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<uint64_traits, BlockSize> const &n) {
|
||||
counter_.inc(n.get_location());
|
||||
check_duplicate_block(n.get_location());
|
||||
check_block_nr(n);
|
||||
check_max_entries(n);
|
||||
check_nr_entries(n, is_root);
|
||||
}
|
||||
|
||||
void visit_internal_leaf(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<uint64_traits, BlockSize> const &n) {
|
||||
counter_.inc(n.get_location());
|
||||
check_duplicate_block(n.get_location());
|
||||
check_block_nr(n);
|
||||
check_max_entries(n);
|
||||
check_nr_entries(n, is_root);
|
||||
}
|
||||
|
||||
void visit_leaf(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<ValueTraits, BlockSize> const &n) {
|
||||
counter_.inc(n.get_location());
|
||||
check_duplicate_block(n.get_location());
|
||||
check_block_nr(n);
|
||||
check_max_entries(n);
|
||||
check_nr_entries(n, is_root);
|
||||
}
|
||||
|
||||
private:
|
||||
void check_duplicate_block(block_address b) {
|
||||
if (seen_.count(b)) {
|
||||
std::ostringstream out;
|
||||
out << "duplicate block in btree: " << b;
|
||||
throw std::runtime_error(out.str());
|
||||
}
|
||||
|
||||
seen_.insert(b);
|
||||
}
|
||||
|
||||
template <typename node>
|
||||
void check_block_nr(node const &n) const {
|
||||
if (n.get_location() != n.get_block_nr()) {
|
||||
std::ostringstream out;
|
||||
out << "block number mismatch: actually "
|
||||
<< n.get_location()
|
||||
<< ", claims " << n.get_block_nr();
|
||||
throw std::runtime_error(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename node>
|
||||
void check_max_entries(node const &n) const {
|
||||
size_t elt_size = sizeof(uint64_t) + n.get_value_size();
|
||||
if (elt_size * n.get_max_entries() + sizeof(node_header) > BlockSize) {
|
||||
std::ostringstream out;
|
||||
out << "max entries too large: " << n.get_max_entries();
|
||||
throw std::runtime_error(out.str());
|
||||
}
|
||||
|
||||
if (n.get_max_entries() % 3) {
|
||||
std::ostringstream out;
|
||||
out << "max entries is not divisible by 3: " << n.get_max_entries();
|
||||
throw std::runtime_error(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename node>
|
||||
void check_nr_entries(node const &n, bool is_root) const {
|
||||
if (n.get_nr_entries() > n.get_max_entries()) {
|
||||
std::ostringstream out;
|
||||
out << "bad nr_entries: "
|
||||
<< n.get_nr_entries() << " < "
|
||||
<< n.get_max_entries();
|
||||
throw std::runtime_error(out.str());
|
||||
}
|
||||
|
||||
block_address min = n.get_max_entries() / 3;
|
||||
if (!is_root && (n.get_nr_entries() < min)) {
|
||||
ostringstream out;
|
||||
out << "too few entries in btree: "
|
||||
<< n.get_nr_entries()
|
||||
<< ", expected at least "
|
||||
<< min;
|
||||
throw runtime_error(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
block_counter &counter_;
|
||||
std::set<block_address> seen_;
|
||||
};
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
80
error_set.cc
Normal file
80
error_set.cc
Normal file
@ -0,0 +1,80 @@
|
||||
#include "error_set.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace persistent_data;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
error_set::error_set(std::string const &err)
|
||||
: err_(err) {
|
||||
}
|
||||
|
||||
std::string const &
|
||||
error_set::get_description() const
|
||||
{
|
||||
return err_;
|
||||
}
|
||||
|
||||
std::list<error_set::ptr> const &
|
||||
error_set::get_children() const
|
||||
{
|
||||
return children_;
|
||||
}
|
||||
|
||||
void
|
||||
error_set::add_child(error_set::ptr err)
|
||||
{
|
||||
children_.push_back(err);
|
||||
}
|
||||
|
||||
void
|
||||
error_set::add_child(std::string const &err)
|
||||
{
|
||||
error_set::ptr e(new error_set(err));
|
||||
add_child(e);
|
||||
}
|
||||
|
||||
//--------------------------------
|
||||
|
||||
namespace {
|
||||
void indent_by(std::ostream &out, unsigned indent) {
|
||||
for (unsigned i = 0; i < indent; i++)
|
||||
out << ' ';
|
||||
}
|
||||
|
||||
void print_errs(std::ostream &out, error_set::ptr e, unsigned depth, unsigned indent) {
|
||||
if (depth == 0)
|
||||
return;
|
||||
|
||||
indent_by(out, indent);
|
||||
|
||||
out << e->get_description() << std::endl;
|
||||
if (depth > 1) {
|
||||
auto children = e->get_children();
|
||||
for (auto it = children.begin(); it != children.end(); ++it)
|
||||
print_errs(out, *it, depth - 1, indent + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_selector::error_selector(error_set::ptr errs, unsigned depth)
|
||||
: errs_(errs),
|
||||
depth_(depth)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
error_selector::print(std::ostream &out) const
|
||||
{
|
||||
print_errs(out, errs_, depth_, 0);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
persistent_data::operator << (std::ostream &out, error_selector const &errs)
|
||||
{
|
||||
errs.print(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
49
error_set.h
Normal file
49
error_set.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef ERROR_SET_H
|
||||
#define ERROR_SET_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <list>
|
||||
#include <iosfwd>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
// When checking the metadata for a thin device we don't want to
|
||||
// stop at the first error. Instead should collect as much
|
||||
// information as possible. The errors are hierarchical, so the
|
||||
// user can control how much detail is displayed.
|
||||
class error_set {
|
||||
public:
|
||||
typedef boost::shared_ptr<error_set> ptr;
|
||||
|
||||
error_set(std::string const &err);
|
||||
|
||||
std::string const &get_description() const;
|
||||
std::list<error_set::ptr> const &get_children() const;
|
||||
void add_child(error_set::ptr err);
|
||||
void add_child(std::string const &err);
|
||||
|
||||
private:
|
||||
std::string err_;
|
||||
std::list<error_set::ptr> children_;
|
||||
};
|
||||
|
||||
// The error_selector is a little proxy class used when printing
|
||||
// errors to a stream.
|
||||
class error_selector {
|
||||
public:
|
||||
error_selector(error_set::ptr errs, unsigned depth);
|
||||
|
||||
void print(std::ostream &out) const;
|
||||
|
||||
private:
|
||||
error_set::ptr errs_;
|
||||
unsigned depth_;
|
||||
};
|
||||
|
||||
std::ostream &operator << (std::ostream &out, error_selector const &errs);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
177
metadata.cc
177
metadata.cc
@ -1,10 +1,13 @@
|
||||
#include "metadata.h"
|
||||
|
||||
#include "btree_validator.h"
|
||||
#include "core_map.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
using namespace persistent_data;
|
||||
@ -38,122 +41,41 @@ namespace {
|
||||
return sb;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// This class implements consistency checking for the
|
||||
// btrees in general. It's worth summarising what is checked:
|
||||
//
|
||||
// Implemented
|
||||
// -----------
|
||||
//
|
||||
// - No block appears in the tree more than once.
|
||||
// - block_nr
|
||||
// - nr_entries < max_entries
|
||||
// - max_entries fits in block
|
||||
// - max_entries is divisible by 3
|
||||
//
|
||||
// Not implemented
|
||||
// ---------------
|
||||
//
|
||||
// - checksum
|
||||
// - leaf | internal flags (this can be inferred from siblings)
|
||||
// - nr_entries > minimum
|
||||
//----------------------------------------------------------------
|
||||
template <uint32_t Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
class btree_validator : public btree<Levels, ValueTraits, BlockSize>::visitor {
|
||||
public:
|
||||
void visit_internal(unsigned level, btree_detail::node_ref<uint64_traits, BlockSize> const &n) {
|
||||
check_duplicate_block(n.get_location());
|
||||
check_block_nr(n);
|
||||
check_max_entries(n);
|
||||
check_nr_entries(n);
|
||||
}
|
||||
|
||||
void visit_internal_leaf(unsigned level, btree_detail::node_ref<uint64_traits, BlockSize> const &n) {
|
||||
check_duplicate_block(n.get_location());
|
||||
check_block_nr(n);
|
||||
check_max_entries(n);
|
||||
check_nr_entries(n);
|
||||
}
|
||||
|
||||
void visit_leaf(unsigned level, btree_detail::node_ref<ValueTraits, BlockSize> const &n) {
|
||||
check_duplicate_block(n.get_location());
|
||||
check_block_nr(n);
|
||||
check_max_entries(n);
|
||||
check_nr_entries(n);
|
||||
}
|
||||
|
||||
private:
|
||||
void check_duplicate_block(block_address b) {
|
||||
if (seen_.count(b)) {
|
||||
ostringstream out;
|
||||
out << "duplicate block in btree: " << b;
|
||||
throw runtime_error(out.str());
|
||||
}
|
||||
|
||||
seen_.insert(b);
|
||||
}
|
||||
|
||||
template <typename node>
|
||||
void check_block_nr(node const &n) const {
|
||||
if (n.get_location() != n.get_block_nr()) {
|
||||
ostringstream out;
|
||||
out << "block number mismatch: actually "
|
||||
<< n.get_location()
|
||||
<< ", claims " << n.get_block_nr();
|
||||
throw runtime_error(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename node>
|
||||
void check_max_entries(node const &n) const {
|
||||
size_t elt_size = sizeof(uint64_t) + n.get_value_size();
|
||||
if (elt_size * n.get_max_entries() + sizeof(node_header) > BlockSize) {
|
||||
ostringstream out;
|
||||
out << "max entries too large: " << n.get_max_entries();
|
||||
throw runtime_error(out.str());
|
||||
}
|
||||
|
||||
if (n.get_max_entries() % 3) {
|
||||
ostringstream out;
|
||||
out << "max entries is not divisible by 3: " << n.get_max_entries();
|
||||
throw runtime_error(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename node>
|
||||
void check_nr_entries(node const &n) const {
|
||||
if (n.get_nr_entries() > n.get_max_entries()) {
|
||||
ostringstream out;
|
||||
out << "bad nr_entries: "
|
||||
<< n.get_nr_entries() << " < "
|
||||
<< n.get_max_entries();
|
||||
throw runtime_error(out.str());
|
||||
}
|
||||
}
|
||||
|
||||
set<block_address> seen_;
|
||||
};
|
||||
|
||||
// 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.
|
||||
// referenced with the details tree. A separate block_counter is
|
||||
// used to later verify the data space map.
|
||||
class mapping_validator : public btree_validator<2, block_traits, MD_BLOCK_SIZE> {
|
||||
public:
|
||||
typedef boost::shared_ptr<mapping_validator> ptr;
|
||||
|
||||
void visit_internal_leaf(unsigned level,
|
||||
mapping_validator(block_counter &metadata_counter, block_counter &data_counter)
|
||||
: btree_validator<2, block_traits, MD_BLOCK_SIZE>(metadata_counter),
|
||||
data_counter_(data_counter) {
|
||||
}
|
||||
|
||||
void visit_internal_leaf(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<uint64_traits, MD_BLOCK_SIZE> const &n) {
|
||||
btree_validator<2, block_traits, MD_BLOCK_SIZE>::visit_internal_leaf(level, n);
|
||||
btree_validator<2, block_traits, MD_BLOCK_SIZE>::visit_internal_leaf(level, is_root, n);
|
||||
|
||||
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
||||
devices_.insert(n.key_at(i));
|
||||
}
|
||||
|
||||
void visit_leaf(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<block_traits, MD_BLOCK_SIZE> const &n) {
|
||||
btree_validator<2, block_traits, MD_BLOCK_SIZE>::visit_leaf(level, is_root, n);
|
||||
|
||||
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
||||
data_counter_.inc(n.value_at(i).block_);
|
||||
}
|
||||
|
||||
set<uint64_t> get_devices() const {
|
||||
return devices_;
|
||||
}
|
||||
|
||||
private:
|
||||
block_counter &data_counter_;
|
||||
set<uint64_t> devices_;
|
||||
};
|
||||
|
||||
@ -161,9 +83,13 @@ namespace {
|
||||
public:
|
||||
typedef boost::shared_ptr<details_validator> ptr;
|
||||
|
||||
void visit_leaf(unsigned level,
|
||||
details_validator(block_counter &counter)
|
||||
: btree_validator<1, device_details_traits, MD_BLOCK_SIZE>(counter) {
|
||||
}
|
||||
|
||||
void visit_leaf(unsigned level, bool is_root,
|
||||
btree_detail::node_ref<device_details_traits, MD_BLOCK_SIZE> const &n) {
|
||||
btree_validator<1, device_details_traits, MD_BLOCK_SIZE>::visit_leaf(level, n);
|
||||
btree_validator<1, device_details_traits, MD_BLOCK_SIZE>::visit_leaf(level, is_root, n);
|
||||
|
||||
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
||||
devices_.insert(n.key_at(i));
|
||||
@ -203,7 +129,10 @@ void
|
||||
thin::insert(block_address thin_block, block_address data_block)
|
||||
{
|
||||
uint64_t key[2] = {dev_, thin_block};
|
||||
return metadata_->mappings_.insert(key, data_block);
|
||||
block_time bt;
|
||||
bt.block_ = data_block;
|
||||
bt.time_ = 0; // FIXME: use current time.
|
||||
return metadata_->mappings_.insert(key, bt);
|
||||
}
|
||||
|
||||
void
|
||||
@ -253,9 +182,10 @@ thin::set_mapped_blocks(block_address count)
|
||||
metadata::metadata(std::string const &dev_path)
|
||||
: tm_(open_tm(dev_path)),
|
||||
sb_(read_superblock(tm_->get_bm())),
|
||||
data_sm_(open_disk_sm<MD_BLOCK_SIZE>(tm_, static_cast<void *>(&sb_.data_space_map_root_))),
|
||||
details_(tm_, sb_.device_details_root_, typename device_details_traits::ref_counter()),
|
||||
mappings_top_level_(tm_, sb_.data_mapping_root_, mtree_ref_counter<MD_BLOCK_SIZE>(tm_)),
|
||||
mappings_(tm_, sb_.data_mapping_root_, space_map_ref_counter(data_sm_))
|
||||
mappings_(tm_, sb_.data_mapping_root_, block_time_ref_counter(data_sm_))
|
||||
{
|
||||
#if 0
|
||||
::memset(&sb_, 0, sizeof(sb_));
|
||||
@ -290,7 +220,7 @@ metadata::create_thin(thin_dev_t dev)
|
||||
if (device_exists(dev))
|
||||
throw std::runtime_error("Device already exists");
|
||||
|
||||
single_mapping_tree::ptr new_tree(new single_mapping_tree(tm_, space_map_ref_counter(data_sm_)));
|
||||
single_mapping_tree::ptr new_tree(new single_mapping_tree(tm_, block_time_ref_counter(data_sm_)));
|
||||
mappings_top_level_.insert(key, new_tree->get_root());
|
||||
mappings_.set_root(mappings_top_level_.get_root()); // FIXME: ugly
|
||||
}
|
||||
@ -306,7 +236,7 @@ metadata::create_snap(thin_dev_t dev, thin_dev_t origin)
|
||||
throw std::runtime_error("unknown origin");
|
||||
|
||||
single_mapping_tree otree(tm_, *mtree_root,
|
||||
space_map_ref_counter(data_sm_));
|
||||
block_time_ref_counter(data_sm_));
|
||||
|
||||
single_mapping_tree::ptr clone(otree.clone());
|
||||
mappings_top_level_.insert(snap_key, clone->get_root());
|
||||
@ -396,14 +326,19 @@ metadata::device_exists(thin_dev_t dev) const
|
||||
return details_.lookup(key);
|
||||
}
|
||||
|
||||
void
|
||||
boost::optional<error_set::ptr>
|
||||
metadata::check()
|
||||
{
|
||||
mapping_validator::ptr mv(new mapping_validator);
|
||||
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));
|
||||
mappings_.visit(mv);
|
||||
auto mapped_devs = mv->get_devices();
|
||||
|
||||
details_validator::ptr dv(new details_validator);
|
||||
details_validator::ptr dv(new details_validator(metadata_counter));
|
||||
details_.visit(dv);
|
||||
auto details_devs = dv->get_devices();
|
||||
|
||||
@ -414,6 +349,32 @@ metadata::check()
|
||||
<< ", yet there is no entry in the details tree.";
|
||||
throw runtime_error(out.str());
|
||||
}
|
||||
|
||||
data_sm_->check(metadata_counter);
|
||||
|
||||
{
|
||||
error_set::ptr data_errors(new error_set("Errors in data reference counts"));
|
||||
|
||||
bool bad = false;
|
||||
auto data_counts = data_counter.get_counts();
|
||||
for (auto it = data_counts.begin(); it != data_counts.end(); ++it) {
|
||||
uint32_t ref_count = data_sm_->get_count(it->first);
|
||||
if (ref_count != it->second) {
|
||||
ostringstream out;
|
||||
out << it->first << ": was " << ref_count
|
||||
<< ", expected " << it->second;
|
||||
data_errors->add_child(out.str());
|
||||
bad = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bad)
|
||||
errors->add_child(data_errors);
|
||||
}
|
||||
|
||||
return (errors->get_children().size() > 0) ?
|
||||
optional<error_set::ptr>(errors) :
|
||||
optional<error_set::ptr>();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
44
metadata.h
44
metadata.h
@ -2,10 +2,12 @@
|
||||
#define MULTISNAP_METADATA_H
|
||||
|
||||
#include "block.h"
|
||||
#include "transaction_manager.h"
|
||||
#include "btree.h"
|
||||
#include "endian_utils.h"
|
||||
#include "error_set.h"
|
||||
#include "metadata_disk_structures.h"
|
||||
#include "space_map_disk.h"
|
||||
#include "transaction_manager.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -43,17 +45,43 @@ namespace thin_provisioning {
|
||||
space_map::ptr sm_;
|
||||
};
|
||||
|
||||
struct block_time {
|
||||
uint64_t block_;
|
||||
uint32_t time_;
|
||||
};
|
||||
|
||||
class block_time_ref_counter {
|
||||
public:
|
||||
block_time_ref_counter(space_map::ptr sm)
|
||||
: sm_(sm) {
|
||||
}
|
||||
|
||||
void inc(block_time bt) {
|
||||
sm_->inc(bt.block_);
|
||||
}
|
||||
|
||||
void dec(block_time bt) {
|
||||
sm_->dec(bt.block_);
|
||||
}
|
||||
|
||||
private:
|
||||
space_map::ptr sm_;
|
||||
};
|
||||
|
||||
struct block_traits {
|
||||
typedef base::__le64 disk_type;
|
||||
typedef uint64_t value_type;
|
||||
typedef space_map_ref_counter ref_counter;
|
||||
typedef block_time value_type;
|
||||
typedef block_time_ref_counter ref_counter;
|
||||
|
||||
static void unpack(disk_type const &disk, value_type &value) {
|
||||
value = base::to_cpu<uint64_t>(disk);
|
||||
uint64_t v = to_cpu<uint64_t>(disk);
|
||||
value.block_ = v >> 24;
|
||||
value.time_ = v & ((1 << 24) - 1);
|
||||
}
|
||||
|
||||
static void pack(value_type const &value, disk_type &disk) {
|
||||
disk = base::to_disk<base::__le64>(value);
|
||||
uint64_t v = (value.block_ << 24) | value.time_;
|
||||
disk = base::to_disk<base::__le64>(v);
|
||||
}
|
||||
};
|
||||
|
||||
@ -95,7 +123,7 @@ namespace thin_provisioning {
|
||||
class thin {
|
||||
public:
|
||||
typedef boost::shared_ptr<thin> ptr;
|
||||
typedef boost::optional<block_address> maybe_address;
|
||||
typedef boost::optional<block_time> maybe_address;
|
||||
|
||||
thin_dev_t get_dev_t() const;
|
||||
maybe_address lookup(block_address thin_block);
|
||||
@ -144,7 +172,7 @@ namespace thin_provisioning {
|
||||
thin::ptr open_thin(thin_dev_t);
|
||||
|
||||
// Validation and repair
|
||||
void check();
|
||||
boost::optional<persistent_data::error_set::ptr> check();
|
||||
|
||||
private:
|
||||
friend class thin;
|
||||
@ -164,7 +192,7 @@ namespace thin_provisioning {
|
||||
|
||||
// Ignoring the metadata sm for now, since we don't need it for the basic 'dump' tool
|
||||
// space_map::ptr metadata_sm_;
|
||||
space_map::ptr data_sm_;
|
||||
sm_disk_detail::sm_disk<MD_BLOCK_SIZE>::ptr data_sm_;
|
||||
detail_tree details_;
|
||||
dev_tree mappings_top_level_;
|
||||
mapping_tree mappings_;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef SPACE_MAP_DISK_H
|
||||
#define SPACE_MAP_DISK_H
|
||||
|
||||
#include "btree_validator.h"
|
||||
#include "space_map.h"
|
||||
#include "transaction_manager.h"
|
||||
#include "endian_utils.h"
|
||||
@ -124,6 +125,7 @@ namespace persistent_data {
|
||||
sm_disk_base(typename transaction_manager<BlockSize>::ptr tm,
|
||||
sm_root const &root)
|
||||
: tm_(tm),
|
||||
entries_per_block_((BlockSize - sizeof(bitmap_header)) * 4),
|
||||
nr_blocks_(root.nr_blocks_),
|
||||
nr_allocated_(root.nr_allocated_),
|
||||
ref_counts_(tm_, root.ref_count_root_, ref_count_traits::ref_counter()) {
|
||||
@ -288,6 +290,16 @@ namespace persistent_data {
|
||||
btree<1, ref_count_traits, BlockSize> ref_counts_;
|
||||
};
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
class bitmap_tree_validator : public btree_validator<1, index_entry_traits, BlockSize> {
|
||||
public:
|
||||
typedef boost::shared_ptr<bitmap_tree_validator> ptr;
|
||||
|
||||
bitmap_tree_validator(block_counter &counter)
|
||||
: btree_validator<1, index_entry_traits, BlockSize>(counter) {
|
||||
}
|
||||
};
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
class sm_disk : public sm_disk_base<BlockSize> {
|
||||
public:
|
||||
@ -295,13 +307,13 @@ namespace persistent_data {
|
||||
|
||||
sm_disk(typename transaction_manager<BlockSize>::ptr tm)
|
||||
: sm_disk_base<BlockSize>(tm),
|
||||
bitmaps_(sm_disk_base<BlockSize>::get_tm(), typename sm_disk_detail::index_entry_traits::ref_counter()) {
|
||||
bitmaps_(sm_disk_base<BlockSize>::get_tm(), typename index_entry_traits::ref_counter()) {
|
||||
}
|
||||
|
||||
sm_disk(typename transaction_manager<BlockSize>::ptr tm,
|
||||
sm_root const &root)
|
||||
: sm_disk_base<BlockSize>(tm, root),
|
||||
bitmaps_(sm_disk_base<BlockSize>::get_tm(), root.bitmap_root_, typename sm_disk_detail::index_entry_traits::ref_counter()) {
|
||||
bitmaps_(sm_disk_base<BlockSize>::get_tm(), root.bitmap_root_, typename index_entry_traits::ref_counter()) {
|
||||
}
|
||||
|
||||
size_t root_size() {
|
||||
@ -323,6 +335,11 @@ namespace persistent_data {
|
||||
::memcpy(dest, &d, sizeof(d));
|
||||
}
|
||||
|
||||
void check(block_counter &counter) {
|
||||
typename bitmap_tree_validator<BlockSize>::ptr v(new bitmap_tree_validator<BlockSize>(counter));
|
||||
bitmaps_.visit(v);
|
||||
}
|
||||
|
||||
private:
|
||||
index_entry find_ie(block_address b) const {
|
||||
uint64_t key[1] = {b / sm_disk_base<BlockSize>::get_entries_per_block()};
|
||||
@ -343,7 +360,7 @@ namespace persistent_data {
|
||||
}
|
||||
|
||||
template <uint32_t MetadataBlockSize>
|
||||
persistent_space_map::ptr
|
||||
typename sm_disk_detail::sm_disk<MetadataBlockSize>::ptr
|
||||
create_disk_sm(typename transaction_manager<MetadataBlockSize>::ptr tm,
|
||||
block_address nr_blocks)
|
||||
{
|
||||
@ -355,7 +372,7 @@ namespace persistent_data {
|
||||
}
|
||||
|
||||
template <uint32_t MetadataBlockSize>
|
||||
persistent_space_map::ptr
|
||||
typename sm_disk_detail::sm_disk<MetadataBlockSize>::ptr
|
||||
open_disk_sm(typename transaction_manager<MetadataBlockSize>::ptr tm,
|
||||
void *root)
|
||||
{
|
||||
@ -366,7 +383,7 @@ namespace persistent_data {
|
||||
|
||||
::memcpy(&d, root, sizeof(d));
|
||||
sm_root_traits::unpack(d, v);
|
||||
return typename persistent_space_map::ptr(
|
||||
return typename sm_disk<MetadataBlockSize>::ptr(
|
||||
new sm_disk<MetadataBlockSize>(tm, v));
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,16 @@ using namespace std;
|
||||
using namespace thin_provisioning;
|
||||
|
||||
namespace {
|
||||
void check(string const &path) {
|
||||
int check(string const &path) {
|
||||
metadata md(path);
|
||||
|
||||
md.check();
|
||||
auto maybe_errors = md.check();
|
||||
if (maybe_errors) {
|
||||
cerr << error_selector(*maybe_errors, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usage(string const &cmd) {
|
||||
@ -25,7 +31,5 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
check(argv[1]);
|
||||
|
||||
return 0;
|
||||
return check(argv[1]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user