Some work on thin_repair.
This commit is contained in:
parent
29c2831f3e
commit
511456f903
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,4 +2,6 @@
|
||||
*.o
|
||||
*_t
|
||||
*.d
|
||||
test.data
|
||||
test.data
|
||||
thin_dump
|
||||
thin_repair
|
||||
|
11
Makefile
11
Makefile
@ -1,9 +1,8 @@
|
||||
SOURCE=\
|
||||
endian_utils.cc \
|
||||
metadata.cc \
|
||||
metadata_disk_structures.cc
|
||||
|
||||
# metadata.cc \
|
||||
|
||||
TEST_SOURCE=\
|
||||
unit-tests/block_t.cc \
|
||||
unit-tests/btree_t.cc \
|
||||
@ -12,8 +11,6 @@ TEST_SOURCE=\
|
||||
unit-tests/space_map_disk_t.cc \
|
||||
unit-tests/transaction_manager_t.cc \
|
||||
|
||||
# unit-tests/metadata_t.cc \
|
||||
|
||||
OBJECTS=$(subst .cc,.o,$(SOURCE))
|
||||
TEST_PROGRAMS=$(subst .cc,,$(TEST_SOURCE))
|
||||
TOP_DIR:=$(PWD)
|
||||
@ -40,6 +37,12 @@ unit-test: $(TEST_PROGRAMS)
|
||||
multisnap_display: $(OBJECTS) main.o
|
||||
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
|
||||
g++ $(CPPFLAGS) -o $@ $+ $(LIBS)
|
||||
|
||||
|
6
btree.h
6
btree.h
@ -52,6 +52,8 @@ namespace persistent_data {
|
||||
|
||||
__le32 nr_entries;
|
||||
__le32 max_entries;
|
||||
__le32 value_size;
|
||||
__le32 padding;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct disk_node {
|
||||
@ -76,6 +78,8 @@ namespace persistent_data {
|
||||
return location_;
|
||||
}
|
||||
|
||||
block_address get_block_nr() const;
|
||||
|
||||
node_type get_type() const;
|
||||
void set_type(node_type t);
|
||||
|
||||
@ -88,6 +92,8 @@ namespace persistent_data {
|
||||
// FIXME: remove this, and get the constructor to do it.
|
||||
void set_max_entries(); // calculates the max for you.
|
||||
|
||||
size_t get_value_size() const;
|
||||
|
||||
uint64_t key_at(unsigned i) const;
|
||||
void set_key(unsigned i, uint64_t k);
|
||||
|
||||
|
15
btree.tcc
15
btree.tcc
@ -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>
|
||||
btree_detail::node_type
|
||||
node_ref<ValueTraits, BlockSize>::get_type() const
|
||||
@ -81,6 +88,13 @@ node_ref<ValueTraits, BlockSize>::set_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>
|
||||
uint64_t
|
||||
node_ref<ValueTraits, BlockSize>::key_at(unsigned i) const
|
||||
@ -610,6 +624,7 @@ walk_tree(typename visitor::ptr visitor,
|
||||
unsigned level, 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) {
|
||||
|
207
metadata.cc
207
metadata.cc
@ -1,7 +1,12 @@
|
||||
#include "metadata.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include "core_map.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace persistent_data;
|
||||
using namespace thin_provisioning;
|
||||
|
||||
@ -13,6 +18,164 @@ namespace {
|
||||
uint32_t const VERSION = 1;
|
||||
unsigned const METADATA_CACHE_SIZE = 1024;
|
||||
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,
|
||||
block_address superblock,
|
||||
sector_t data_block_size,
|
||||
block_address nr_data_blocks,
|
||||
bool create)
|
||||
: superblock_(superblock),
|
||||
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_))
|
||||
metadata::metadata(std::string const &dev_path)
|
||||
: tm_(open_tm(dev_path)),
|
||||
sb_(read_superblock(tm_->get_bm())),
|
||||
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_))
|
||||
{
|
||||
#if 0
|
||||
::memset(&sb_, 0, sizeof(sb_));
|
||||
sb_.data_mapping_root_ = mappings_.get_root();
|
||||
sb_.device_details_root_ = details_.get_root();
|
||||
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()
|
||||
@ -116,7 +277,7 @@ metadata::commit()
|
||||
sb_.data_mapping_root_ = mappings_.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());
|
||||
superblock_traits::pack(sb_, *disk);
|
||||
}
|
||||
@ -235,4 +396,24 @@ metadata::device_exists(thin_dev_t dev) const
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
@ -143,13 +143,14 @@ namespace thin_provisioning {
|
||||
|
||||
thin::ptr open_thin(thin_dev_t);
|
||||
|
||||
// Validation and repair
|
||||
void check();
|
||||
|
||||
private:
|
||||
friend class thin;
|
||||
|
||||
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::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<1, block_traits, MD_BLOCK_SIZE> single_mapping_tree;
|
||||
|
||||
// Declaration order is important here
|
||||
tm_ptr tm_;
|
||||
superblock sb_;
|
||||
|
||||
// Ignoring the metadata sm for now, since we don't need it for the basic 'dump' tool
|
||||
// space_map::ptr metadata_sm_;
|
||||
@ -165,7 +168,6 @@ namespace thin_provisioning {
|
||||
detail_tree details_;
|
||||
dev_tree mappings_top_level_;
|
||||
mapping_tree mappings_;
|
||||
superblock sb_;
|
||||
};
|
||||
};
|
||||
|
||||
|
31
thin_dump.cc
Normal file
31
thin_dump.cc
Normal 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
31
thin_repair.cc
Normal 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;
|
||||
}
|
@ -15,14 +15,6 @@ namespace {
|
||||
block_address const NR_BLOCKS = 1024;
|
||||
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
|
||||
create_metadata() {
|
||||
auto tm = create_tm();
|
||||
|
Loading…
Reference in New Issue
Block a user