Some work on thin_repair.

This commit is contained in:
Joe Thornber 2011-08-23 11:55:37 +01:00
parent 29c2831f3e
commit 511456f903
9 changed files with 292 additions and 29 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@
*_t *_t
*.d *.d
test.data test.data
thin_dump
thin_repair

View File

@ -1,9 +1,8 @@
SOURCE=\ SOURCE=\
endian_utils.cc \ endian_utils.cc \
metadata.cc \
metadata_disk_structures.cc metadata_disk_structures.cc
# metadata.cc \
TEST_SOURCE=\ TEST_SOURCE=\
unit-tests/block_t.cc \ unit-tests/block_t.cc \
unit-tests/btree_t.cc \ unit-tests/btree_t.cc \
@ -12,8 +11,6 @@ TEST_SOURCE=\
unit-tests/space_map_disk_t.cc \ unit-tests/space_map_disk_t.cc \
unit-tests/transaction_manager_t.cc \ unit-tests/transaction_manager_t.cc \
# unit-tests/metadata_t.cc \
OBJECTS=$(subst .cc,.o,$(SOURCE)) OBJECTS=$(subst .cc,.o,$(SOURCE))
TEST_PROGRAMS=$(subst .cc,,$(TEST_SOURCE)) TEST_PROGRAMS=$(subst .cc,,$(TEST_SOURCE))
TOP_DIR:=$(PWD) TOP_DIR:=$(PWD)
@ -40,6 +37,12 @@ unit-test: $(TEST_PROGRAMS)
multisnap_display: $(OBJECTS) main.o multisnap_display: $(OBJECTS) main.o
g++ $(CPPFLAGS) -o $@ $+ $(LIBS) g++ $(CPPFLAGS) -o $@ $+ $(LIBS)
thin_dump: $(OBJECTS) thin_dump.o
g++ $(CPPFLAGS) -o $@ $+ $(LIBS)
thin_repair: $(OBJECTS) thin_repair.o
g++ $(CPPFLAGS) -o $@ $+ $(LIBS)
unit-tests/block_t: unit-tests/block_t.o unit-tests/block_t: unit-tests/block_t.o
g++ $(CPPFLAGS) -o $@ $+ $(LIBS) g++ $(CPPFLAGS) -o $@ $+ $(LIBS)

View File

@ -52,6 +52,8 @@ namespace persistent_data {
__le32 nr_entries; __le32 nr_entries;
__le32 max_entries; __le32 max_entries;
__le32 value_size;
__le32 padding;
} __attribute__((packed)); } __attribute__((packed));
struct disk_node { struct disk_node {
@ -76,6 +78,8 @@ namespace persistent_data {
return location_; return location_;
} }
block_address get_block_nr() const;
node_type get_type() const; node_type get_type() const;
void set_type(node_type t); void set_type(node_type t);
@ -88,6 +92,8 @@ namespace persistent_data {
// FIXME: remove this, and get the constructor to do it. // FIXME: remove this, and get the constructor to do it.
void set_max_entries(); // calculates the max for you. void set_max_entries(); // calculates the max for you.
size_t get_value_size() const;
uint64_t key_at(unsigned i) const; uint64_t key_at(unsigned i) const;
void set_key(unsigned i, uint64_t k); void set_key(unsigned i, uint64_t k);

View File

@ -16,6 +16,13 @@ node_ref<ValueTraits, BlockSize>::node_ref(block_address location, disk_node *ra
{ {
} }
template <typename ValueTraits, uint32_t BlockSize>
block_address
node_ref<ValueTraits, BlockSize>::get_block_nr() const
{
return to_cpu<uint64_t>(raw_->header.blocknr);
}
template <typename ValueTraits, uint32_t BlockSize> template <typename ValueTraits, uint32_t BlockSize>
btree_detail::node_type btree_detail::node_type
node_ref<ValueTraits, BlockSize>::get_type() const node_ref<ValueTraits, BlockSize>::get_type() const
@ -81,6 +88,13 @@ node_ref<ValueTraits, BlockSize>::set_max_entries()
set_max_entries(calc_max_entries()); set_max_entries(calc_max_entries());
} }
template <typename ValueTraits, uint32_t BlockSize>
size_t
node_ref<ValueTraits, BlockSize>::get_value_size() const
{
return to_cpu<uint32_t>(raw_->header.value_size);
}
template <typename ValueTraits, uint32_t BlockSize> template <typename ValueTraits, uint32_t BlockSize>
uint64_t uint64_t
node_ref<ValueTraits, BlockSize>::key_at(unsigned i) const node_ref<ValueTraits, BlockSize>::key_at(unsigned i) const
@ -610,6 +624,7 @@ walk_tree(typename visitor::ptr visitor,
unsigned level, block_address b) unsigned level, block_address b)
{ {
using namespace btree_detail; using namespace btree_detail;
auto blk = tm_->read_lock(b); auto blk = tm_->read_lock(b);
auto o = to_node<uint64_traits, BlockSize>(blk); auto o = to_node<uint64_traits, BlockSize>(blk);
if (o.get_type() == INTERNAL) { if (o.get_type() == INTERNAL) {

View File

@ -1,7 +1,12 @@
#include "metadata.h" #include "metadata.h"
#include <stdexcept> #include "core_map.h"
#include <stdexcept>
#include <sstream>
#include <iostream>
using namespace std;
using namespace persistent_data; using namespace persistent_data;
using namespace thin_provisioning; using namespace thin_provisioning;
@ -13,6 +18,164 @@ namespace {
uint32_t const VERSION = 1; uint32_t const VERSION = 1;
unsigned const METADATA_CACHE_SIZE = 1024; unsigned const METADATA_CACHE_SIZE = 1024;
unsigned const SECTOR_TO_BLOCK_SHIFT = 3; unsigned const SECTOR_TO_BLOCK_SHIFT = 3;
// FIXME: get the file size
unsigned const NR_BLOCKS = 1024;
transaction_manager<4096>::ptr
open_tm(string const &dev_path) {
block_manager<4096>::ptr bm(new block_manager<4096>(dev_path, NR_BLOCKS));
space_map::ptr sm(new core_map(NR_BLOCKS));
transaction_manager<4096>::ptr tm(new transaction_manager<4096>(bm, sm));
return tm;
}
superblock read_superblock(block_manager<4096>::ptr bm) {
superblock sb;
auto r = bm->read_lock(SUPERBLOCK_LOCATION);
superblock_disk const *sbd = reinterpret_cast<superblock_disk const *>(&r.data());
superblock_traits::unpack(*sbd, sb);
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.
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,
btree_detail::node_ref<uint64_traits, MD_BLOCK_SIZE> const &n) {
btree_validator<2, block_traits, MD_BLOCK_SIZE>::visit_internal_leaf(level, n);
for (unsigned i = 0; i < n.get_nr_entries(); i++)
devices_.insert(n.key_at(i));
}
set<uint64_t> get_devices() const {
return devices_;
}
private:
set<uint64_t> devices_;
};
class details_validator : public btree_validator<1, device_details_traits, MD_BLOCK_SIZE> {
public:
typedef boost::shared_ptr<details_validator> ptr;
void visit_leaf(unsigned level,
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);
for (unsigned i = 0; i < n.get_nr_entries(); i++)
devices_.insert(n.key_at(i));
}
set<uint64_t> get_devices() const {
return devices_;
}
private:
set<uint64_t> devices_;
};
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -87,22 +250,20 @@ thin::set_mapped_blocks(block_address count)
//-------------------------------- //--------------------------------
metadata::metadata(transaction_manager<MD_BLOCK_SIZE>::ptr tm, metadata::metadata(std::string const &dev_path)
block_address superblock, : tm_(open_tm(dev_path)),
sector_t data_block_size, sb_(read_superblock(tm_->get_bm())),
block_address nr_data_blocks, details_(tm_, sb_.device_details_root_, typename device_details_traits::ref_counter()),
bool create) mappings_top_level_(tm_, sb_.data_mapping_root_, mtree_ref_counter<MD_BLOCK_SIZE>(tm_)),
: superblock_(superblock), mappings_(tm_, sb_.data_mapping_root_, space_map_ref_counter(data_sm_))
tm_(tm),
details_(tm, typename device_details_traits::ref_counter()),
mappings_top_level_(tm, mtree_ref_counter<MD_BLOCK_SIZE>(tm)),
mappings_(tm, space_map_ref_counter(data_sm_))
{ {
#if 0
::memset(&sb_, 0, sizeof(sb_)); ::memset(&sb_, 0, sizeof(sb_));
sb_.data_mapping_root_ = mappings_.get_root(); sb_.data_mapping_root_ = mappings_.get_root();
sb_.device_details_root_ = details_.get_root(); sb_.device_details_root_ = details_.get_root();
sb_.metadata_block_size_ = MD_BLOCK_SIZE; sb_.metadata_block_size_ = MD_BLOCK_SIZE;
sb_.metadata_nr_blocks_ = tm->get_bm()->get_nr_blocks(); sb_.metadata_nr_blocks_ = tm_->get_bm()->get_nr_blocks();
#endif
} }
metadata::~metadata() metadata::~metadata()
@ -116,7 +277,7 @@ metadata::commit()
sb_.data_mapping_root_ = mappings_.get_root(); sb_.data_mapping_root_ = mappings_.get_root();
sb_.device_details_root_ = details_.get_root(); sb_.device_details_root_ = details_.get_root();
auto superblock = tm_->get_bm()->superblock(superblock_); auto superblock = tm_->get_bm()->superblock(SUPERBLOCK_LOCATION);
auto disk = reinterpret_cast<superblock_disk *>(superblock.data()); auto disk = reinterpret_cast<superblock_disk *>(superblock.data());
superblock_traits::pack(sb_, *disk); superblock_traits::pack(sb_, *disk);
} }
@ -235,4 +396,24 @@ metadata::device_exists(thin_dev_t dev) const
return details_.lookup(key); return details_.lookup(key);
} }
void
metadata::check()
{
mapping_validator::ptr mv(new mapping_validator);
mappings_.visit(mv);
auto mapped_devs = mv->get_devices();
details_validator::ptr dv(new details_validator);
details_.visit(dv);
auto details_devs = dv->get_devices();
for (auto 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());
}
}
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -143,13 +143,14 @@ namespace thin_provisioning {
thin::ptr open_thin(thin_dev_t); thin::ptr open_thin(thin_dev_t);
// Validation and repair
void check();
private: private:
friend class thin; friend class thin;
bool device_exists(thin_dev_t dev) const; bool device_exists(thin_dev_t dev) const;
block_address superblock_;
typedef persistent_data::transaction_manager<MD_BLOCK_SIZE>::ptr tm_ptr; typedef persistent_data::transaction_manager<MD_BLOCK_SIZE>::ptr tm_ptr;
typedef persistent_data::btree<1, device_details_traits, MD_BLOCK_SIZE> detail_tree; typedef persistent_data::btree<1, device_details_traits, MD_BLOCK_SIZE> detail_tree;
@ -157,7 +158,9 @@ namespace thin_provisioning {
typedef persistent_data::btree<2, block_traits, MD_BLOCK_SIZE> mapping_tree; typedef persistent_data::btree<2, block_traits, MD_BLOCK_SIZE> mapping_tree;
typedef persistent_data::btree<1, block_traits, MD_BLOCK_SIZE> single_mapping_tree; typedef persistent_data::btree<1, block_traits, MD_BLOCK_SIZE> single_mapping_tree;
// Declaration order is important here
tm_ptr tm_; tm_ptr tm_;
superblock sb_;
// Ignoring the metadata sm for now, since we don't need it for the basic 'dump' tool // 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 metadata_sm_;
@ -165,7 +168,6 @@ namespace thin_provisioning {
detail_tree details_; detail_tree details_;
dev_tree mappings_top_level_; dev_tree mappings_top_level_;
mapping_tree mappings_; mapping_tree mappings_;
superblock sb_;
}; };
}; };

31
thin_dump.cc Normal file
View File

@ -0,0 +1,31 @@
#include <iostream>
#include "metadata.h"
using namespace persistent_data;
using namespace std;
using namespace thin_provisioning;
namespace {
void dump(string const &path) {
metadata md(path);
md.check();
}
void usage(string const &cmd) {
cerr << "Usage: " << cmd << " <metadata device>" << endl;
}
}
int main(int argc, char **argv)
{
if (argc != 2) {
usage(argv[0]);
exit(1);
}
dump(argv[1]);
return 0;
}

31
thin_repair.cc Normal file
View File

@ -0,0 +1,31 @@
#include <iostream>
#include "metadata.h"
using namespace persistent_data;
using namespace std;
using namespace thin_provisioning;
namespace {
void check(string const &path) {
metadata md(path);
md.check();
}
void usage(string const &cmd) {
cerr << "Usage: " << cmd << " <metadata device>" << endl;
}
}
int main(int argc, char **argv)
{
if (argc != 2) {
usage(argv[0]);
exit(1);
}
check(argv[1]);
return 0;
}

View File

@ -15,14 +15,6 @@ namespace {
block_address const NR_BLOCKS = 1024; block_address const NR_BLOCKS = 1024;
block_address const SUPERBLOCK = 0; block_address const SUPERBLOCK = 0;
transaction_manager<4096>::ptr
create_tm() {
block_manager<4096>::ptr bm(new block_manager<4096>("./test.data", NR_BLOCKS));
space_map::ptr sm(new core_map(NR_BLOCKS));
transaction_manager<4096>::ptr tm(new transaction_manager<4096>(bm, sm));
return tm;
}
metadata::ptr metadata::ptr
create_metadata() { create_metadata() {
auto tm = create_tm(); auto tm = create_tm();