[cache_check] A whole bunch of superblock checks

This commit is contained in:
Joe Thornber 2013-10-07 15:21:45 +01:00
parent 7cd4728fbf
commit 42fd6b928b
8 changed files with 416 additions and 165 deletions

View File

@ -58,17 +58,27 @@ namespace {
error_state err_; error_state err_;
}; };
class superblock_reporter : public superblock_detail::damage_visitor, reporter_base { class superblock_reporter : public superblock_damage::damage_visitor, reporter_base {
public: public:
superblock_reporter(nested_output &o) superblock_reporter(nested_output &o)
: reporter_base(o) { : reporter_base(o) {
} }
virtual void visit(superblock_detail::superblock_corruption const &d) { virtual void visit(superblock_damage::superblock_corrupt const &d) {
out() << "superblock is corrupt" << end_message(); out() << "superblock is corrupt" << end_message();
{ {
nested_output::nest _ = push(); nested_output::nest _ = push();
out() << d.desc_ << end_message(); out() << d.get_desc() << end_message();
}
mplus_error(FATAL);
}
virtual void visit(superblock_damage::superblock_invalid const &d) {
out() << "superblock is invalid" << end_message();
{
nested_output::nest _ = push();
out() << d.get_desc() << end_message();
} }
mplus_error(FATAL); mplus_error(FATAL);
@ -95,7 +105,7 @@ namespace {
transaction_manager::ptr open_tm(block_manager<>::ptr bm) { transaction_manager::ptr open_tm(block_manager<>::ptr bm) {
space_map::ptr sm(new core_map(bm->get_nr_blocks())); space_map::ptr sm(new core_map(bm->get_nr_blocks()));
sm->inc(superblock_detail::SUPERBLOCK_LOCATION); sm->inc(SUPERBLOCK_LOCATION);
transaction_manager::ptr tm(new transaction_manager(bm, sm)); transaction_manager::ptr tm(new transaction_manager(bm, sm));
return tm; return tm;
} }
@ -146,13 +156,13 @@ namespace {
out << "examining superblock" << end_message(); out << "examining superblock" << end_message();
{ {
nested_output::nest _ = out.push(); nested_output::nest _ = out.push();
check_superblock(bm, sb_rep); check_superblock(bm, bm->get_nr_blocks(), sb_rep);
} }
if (sb_rep.get_error() == FATAL) if (sb_rep.get_error() == FATAL)
return FATAL; return FATAL;
superblock_detail::superblock sb = read_superblock(bm); superblock sb = read_superblock(bm);
transaction_manager::ptr tm = open_tm(bm); transaction_manager::ptr tm = open_tm(bm);
if (fs.check_mappings_) { if (fs.check_mappings_) {

View File

@ -7,8 +7,6 @@ using namespace caching;
//---------------------------------------------------------------- //----------------------------------------------------------------
namespace { namespace {
using namespace superblock_detail;
unsigned const METADATA_CACHE_SIZE = 1024; unsigned const METADATA_CACHE_SIZE = 1024;
// FIXME: duplication // FIXME: duplication
@ -133,9 +131,7 @@ metadata::commit_hints()
void void
metadata::commit_superblock() metadata::commit_superblock()
{ {
write_ref superblock = tm_->get_bm()->superblock_zero(SUPERBLOCK_LOCATION, superblock_validator()); write_superblock(tm_->get_bm(), sb_);
superblock_disk *disk = reinterpret_cast<superblock_disk *>(superblock.data().raw());
superblock_traits::pack(sb_, *disk);
} }
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -33,7 +33,7 @@ namespace caching {
typedef persistent_data::transaction_manager tm; typedef persistent_data::transaction_manager tm;
tm::ptr tm_; tm::ptr tm_;
superblock_detail::superblock sb_; superblock sb_;
checked_space_map::ptr metadata_sm_; checked_space_map::ptr metadata_sm_;
mapping_array::ptr mappings_; mapping_array::ptr mappings_;
hint_array::ptr hints_; hint_array::ptr hints_;

View File

@ -5,7 +5,7 @@
using namespace caching; using namespace caching;
using namespace mapping_array_detail; using namespace mapping_array_detail;
using namespace std; using namespace std;
using namespace superblock_detail; using namespace superblock_damage;
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -27,38 +27,14 @@ namespace {
size_t hint_width) { size_t hint_width) {
superblock &sb = md_->sb_; superblock &sb = md_->sb_;
sb.flags = 0;
memset(sb.uuid, 0, sizeof(sb.uuid));
sb.magic = caching::superblock_detail::SUPERBLOCK_MAGIC;
sb.version = 0; // FIXME: fix
strncpy((char *) sb.policy_name, policy.c_str(), sizeof(sb.policy_name)); strncpy((char *) sb.policy_name, policy.c_str(), sizeof(sb.policy_name));
memset(sb.policy_version, 0, sizeof(sb.policy_version)); memset(sb.policy_version, 0, sizeof(sb.policy_version)); // FIXME: should come from xml
sb.policy_hint_size = hint_width; sb.policy_hint_size = hint_width;
md_->setup_hint_array(hint_width); md_->setup_hint_array(hint_width);
memset(sb.metadata_space_map_root, 0,
sizeof(sb.metadata_space_map_root));
sb.mapping_root = 0;
sb.hint_root = 0;
sb.discard_root = 0;
sb.discard_block_size = 0;
sb.discard_nr_blocks = 0;
sb.data_block_size = block_size; sb.data_block_size = block_size;
sb.metadata_block_size = 0;
sb.cache_blocks = nr_cache_blocks; sb.cache_blocks = nr_cache_blocks;
sb.compat_flags = 0;
sb.compat_ro_flags = 0;
sb.incompat_flags = 0;
sb.read_hits = 0;
sb.read_misses = 0;
sb.write_hits = 0;
sb.write_misses = 0;
struct mapping unmapped_value; struct mapping unmapped_value;
unmapped_value.oblock_ = 0; unmapped_value.oblock_ = 0;
unmapped_value.flags_ = 0; unmapped_value.flags_ = 0;

View File

@ -1,7 +1,92 @@
#include "caching/superblock.h" #include "caching/superblock.h"
using namespace caching; using namespace caching;
using namespace superblock_detail; using namespace superblock_damage;
//----------------------------------------------------------------
namespace {
using namespace base;
struct superblock_disk {
le32 csum;
le32 flags;
le64 blocknr;
__u8 uuid[16];
le64 magic;
le32 version;
__u8 policy_name[CACHE_POLICY_NAME_SIZE];
le32 policy_version[CACHE_POLICY_VERSION_SIZE];
le32 policy_hint_size;
__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
le64 mapping_root;
le64 hint_root;
le64 discard_root;
le64 discard_block_size;
le64 discard_nr_blocks;
le32 data_block_size; /* in 512-byte sectors */
le32 metadata_block_size; /* in 512-byte sectors */
le32 cache_blocks;
le32 compat_flags;
le32 compat_ro_flags;
le32 incompat_flags;
le32 read_hits;
le32 read_misses;
le32 write_hits;
le32 write_misses;
} __attribute__ ((packed));
struct superblock_traits {
typedef superblock_disk disk_type;
typedef superblock value_type;
static void unpack(superblock_disk const &disk, superblock &value);
static void pack(superblock const &value, superblock_disk &disk);
};
uint32_t const SUPERBLOCK_MAGIC = 06142003;
uint32_t const VERSION_BEGIN = 1;
uint32_t const VERSION_END = 2;
}
//----------------------------------------------------------------
superblock::superblock()
: csum(0),
flags(0),
blocknr(SUPERBLOCK_LOCATION),
magic(SUPERBLOCK_MAGIC),
version(VERSION_BEGIN),
policy_hint_size(0),
mapping_root(0),
hint_root(0),
discard_root(0),
discard_block_size(0),
discard_nr_blocks(0),
data_block_size(0),
metadata_block_size(8),
cache_blocks(0),
compat_flags(0),
compat_ro_flags(0),
incompat_flags(0),
read_hits(0),
read_misses(0),
write_hits(0),
write_misses(0)
{
::memset(uuid, 0, sizeof(uuid));
::memset(policy_name, 0, sizeof(policy_name));
::memset(policy_version, 0, sizeof(policy_version));
::memset(metadata_space_map_root, 0, sizeof(metadata_space_map_root));
}
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -94,22 +179,33 @@ superblock_traits::pack(superblock const &core, superblock_disk &disk)
//-------------------------------- //--------------------------------
superblock_corruption::superblock_corruption(std::string const &desc) superblock_corrupt::superblock_corrupt(std::string const &desc)
: desc_(desc) : damage(desc)
{ {
} }
void void
superblock_corruption::visit(damage_visitor &v) const superblock_corrupt::visit(damage_visitor &v) const
{
v.visit(*this);
}
superblock_invalid::superblock_invalid(std::string const &desc)
: damage(desc)
{
}
void
superblock_invalid::visit(damage_visitor &v) const
{ {
v.visit(*this); v.visit(*this);
} }
//-------------------------------- //--------------------------------
namespace { // anonymous namespace doesn't work for some reason
namespace validator {
using namespace persistent_data; using namespace persistent_data;
using namespace superblock_detail;
uint32_t const VERSION = 1; uint32_t const VERSION = 1;
unsigned const SECTOR_TO_BLOCK_SHIFT = 3; unsigned const SECTOR_TO_BLOCK_SHIFT = 3;
@ -131,65 +227,122 @@ namespace {
sbd->csum = to_disk<base::le32>(sum.get_sum()); sbd->csum = to_disk<base::le32>(sum.get_sum());
} }
}; };
}
persistent_data::block_manager<>::validator::ptr block_manager<>::validator::ptr mk_v() {
caching::superblock_validator() return block_manager<>::validator::ptr(new sb_validator);
{ }
return block_manager<>::validator::ptr(new sb_validator);
} }
//-------------------------------- //--------------------------------
superblock_detail::superblock superblock
caching::read_superblock(block_manager<>::ptr bm, block_address location) caching::read_superblock(block_manager<>::ptr bm, block_address location)
{ {
using namespace superblock_detail;
superblock sb; superblock sb;
block_manager<>::read_ref r = bm->read_lock(location, superblock_validator()); block_manager<>::read_ref r = bm->read_lock(location, validator::mk_v());
superblock_disk const *sbd = reinterpret_cast<superblock_disk const *>(&r.data()); superblock_disk const *sbd = reinterpret_cast<superblock_disk const *>(&r.data());
superblock_traits::unpack(*sbd, sb); superblock_traits::unpack(*sbd, sb);
return sb; return sb;
} }
superblock_detail::superblock void
caching::read_superblock(persistent_data::block_manager<>::ptr bm) caching::write_superblock(block_manager<>::ptr bm, superblock const &sb, block_address location)
{ {
return read_superblock(bm, SUPERBLOCK_LOCATION); block_manager<>::write_ref w = bm->superblock_zero(location, validator::mk_v());
superblock_traits::pack(sb, *reinterpret_cast<superblock_disk *>(w.data().raw()));
} }
void void
caching::write_superblock(block_manager<>::ptr bm, caching::check_superblock(superblock const &sb,
block_address location, block_address nr_metadata_blocks,
superblock_detail::superblock const &sb) damage_visitor &visitor)
{ {
using namespace superblock_detail; if (sb.flags != 0) {
ostringstream msg;
msg << "invalid flags: " << hex << sb.flags;
visitor.visit(superblock_invalid(msg.str()));
}
block_manager<>::write_ref w = bm->write_lock(location, superblock_validator()); if (sb.blocknr >= nr_metadata_blocks) {
superblock_traits::pack(sb, *reinterpret_cast<superblock_disk *>(&w.data())); ostringstream msg;
} msg << "blocknr out of bounds: " << sb.blocknr << " >= " << nr_metadata_blocks;
visitor.visit(superblock_invalid(msg.str()));
}
void if (sb.magic != SUPERBLOCK_MAGIC) {
caching::write_superblock(block_manager<>::ptr bm, ostringstream msg;
superblock_detail::superblock const &sb) msg << "magic in incorrect: " << sb.magic;
{ visitor.visit(superblock_invalid(msg.str()));
write_superblock(bm, SUPERBLOCK_LOCATION, sb); }
if (sb.version >= VERSION_END) {
ostringstream msg;
msg << "version incorrect: " << sb.version;
visitor.visit(superblock_invalid(msg.str()));
}
if (sb.version < VERSION_BEGIN) {
ostringstream msg;
msg << "version incorrect: " << sb.version;
visitor.visit(superblock_invalid(msg.str()));
}
if (::strnlen((char const *) sb.policy_name, CACHE_POLICY_NAME_SIZE) == CACHE_POLICY_NAME_SIZE) {
visitor.visit(superblock_invalid("policy name is not null terminated"));
}
if (sb.policy_hint_size % 4 || sb.policy_hint_size > 128) {
ostringstream msg;
msg << "policy hint size invalid: " << sb.policy_hint_size;
visitor.visit(superblock_invalid(msg.str()));
}
if (sb.metadata_block_size != 8) {
ostringstream msg;
msg << "metadata block size incorrect: " << sb.metadata_block_size;
visitor.visit(superblock_invalid(msg.str()));
}
if (sb.compat_flags != 0) {
ostringstream msg;
msg << "compat_flags invalid (can only be 0): " << sb.compat_flags;
visitor.visit(superblock_invalid(msg.str()));
}
if (sb.compat_ro_flags != 0) {
ostringstream msg;
msg << "compat_ro_flags invalid (can only be 0): " << sb.compat_ro_flags;
visitor.visit(superblock_invalid(msg.str()));
}
if (sb.incompat_flags != 0) {
ostringstream msg;
msg << "incompat_flags invalid (can only be 0): " << sb.incompat_flags;
visitor.visit(superblock_invalid(msg.str()));
}
} }
void void
caching::check_superblock(persistent_data::block_manager<>::ptr bm, caching::check_superblock(persistent_data::block_manager<>::ptr bm,
superblock_detail::damage_visitor &visitor) block_address nr_metadata_blocks,
damage_visitor &visitor)
{ {
using namespace superblock_detail; superblock sb;
try { try {
bm->read_lock(SUPERBLOCK_LOCATION, superblock_validator()); sb = read_superblock(bm, SUPERBLOCK_LOCATION);
} catch (std::exception const &e) { } catch (std::exception const &e) {
visitor.visit(superblock_corruption(e.what()));
// FIXME: what if it fails due to a zero length file? Not
// really a corruption, so much as an io error. Should we
// separate these?
visitor.visit(superblock_corrupt(e.what()));
} }
check_superblock(sb, nr_metadata_blocks, visitor);
} }
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -7,112 +7,82 @@
//---------------------------------------------------------------- //----------------------------------------------------------------
namespace caching { namespace caching {
namespace superblock_detail { typedef unsigned char __u8;
using namespace base;
unsigned const SPACE_MAP_ROOT_SIZE = 128; unsigned const SPACE_MAP_ROOT_SIZE = 128;
unsigned const CACHE_POLICY_NAME_SIZE = 16; unsigned const CACHE_POLICY_NAME_SIZE = 16;
unsigned const CACHE_POLICY_VERSION_SIZE = 3; unsigned const CACHE_POLICY_VERSION_SIZE = 3;
block_address const SUPERBLOCK_LOCATION = 0;
typedef unsigned char __u8; struct superblock {
superblock();
struct superblock_disk { uint32_t csum;
le32 csum; uint32_t flags;
le32 flags; uint64_t blocknr;
le64 blocknr;
__u8 uuid[16]; __u8 uuid[16];
le64 magic; uint64_t magic;
le32 version; uint32_t version;
__u8 policy_name[CACHE_POLICY_NAME_SIZE]; __u8 policy_name[CACHE_POLICY_NAME_SIZE];
le32 policy_version[CACHE_POLICY_VERSION_SIZE]; uint32_t policy_version[CACHE_POLICY_VERSION_SIZE];
le32 policy_hint_size; uint32_t policy_hint_size;
__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
le64 mapping_root; uint64_t mapping_root;
le64 hint_root; uint64_t hint_root;
le64 discard_root; uint64_t discard_root;
le64 discard_block_size; uint64_t discard_block_size;
le64 discard_nr_blocks; uint64_t discard_nr_blocks;
le32 data_block_size; /* in 512-byte sectors */ uint32_t data_block_size; /* in 512-byte sectors */
le32 metadata_block_size; /* in 512-byte sectors */ uint32_t metadata_block_size; /* in 512-byte sectors */
le32 cache_blocks; uint32_t cache_blocks;
le32 compat_flags; uint32_t compat_flags;
le32 compat_ro_flags; uint32_t compat_ro_flags;
le32 incompat_flags; uint32_t incompat_flags;
le32 read_hits; uint32_t read_hits;
le32 read_misses; uint32_t read_misses;
le32 write_hits; uint32_t write_hits;
le32 write_misses; uint32_t write_misses;
} __attribute__ ((packed)); };
struct superblock { //--------------------------------
uint32_t csum;
uint32_t flags;
uint64_t blocknr;
__u8 uuid[16]; namespace superblock_damage {
uint64_t magic;
uint32_t version;
__u8 policy_name[CACHE_POLICY_NAME_SIZE];
uint32_t policy_version[CACHE_POLICY_VERSION_SIZE];
uint32_t policy_hint_size;
__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
uint64_t mapping_root;
uint64_t hint_root;
uint64_t discard_root;
uint64_t discard_block_size;
uint64_t discard_nr_blocks;
uint32_t data_block_size; /* in 512-byte sectors */
uint32_t metadata_block_size; /* in 512-byte sectors */
uint32_t cache_blocks;
uint32_t compat_flags;
uint32_t compat_ro_flags;
uint32_t incompat_flags;
uint32_t read_hits;
uint32_t read_misses;
uint32_t write_hits;
uint32_t write_misses;
};
struct superblock_traits {
typedef superblock_disk disk_type;
typedef superblock value_type;
static void unpack(superblock_disk const &disk, superblock &value);
static void pack(superblock const &value, superblock_disk &disk);
};
block_address const SUPERBLOCK_LOCATION = 0;
uint32_t const SUPERBLOCK_MAGIC = 06142003;
//--------------------------------
class damage_visitor; class damage_visitor;
struct damage { class damage {
public:
damage(std::string const &desc)
: desc_(desc) {
}
virtual ~damage() {} virtual ~damage() {}
virtual void visit(damage_visitor &v) const = 0; virtual void visit(damage_visitor &v) const = 0;
std::string const &get_desc() const {
return desc_;
}
private:
std::string desc_;
}; };
struct superblock_corruption : public damage { struct superblock_corrupt : public damage {
superblock_corruption(std::string const &desc); superblock_corrupt(std::string const &desc);
void visit(damage_visitor &v) const; void visit(damage_visitor &v) const;
};
std::string desc_; struct superblock_invalid : public damage {
superblock_invalid(std::string const &desc);
void visit(damage_visitor &v) const;
}; };
class damage_visitor { class damage_visitor {
@ -121,24 +91,29 @@ namespace caching {
void visit(damage const &d); void visit(damage const &d);
virtual void visit(superblock_corruption const &d) = 0; virtual void visit(superblock_corrupt const &d) = 0;
virtual void visit(superblock_invalid const &d) = 0;
}; };
} }
//--------------------------------
persistent_data::block_manager<>::validator::ptr superblock_validator(); persistent_data::block_manager<>::validator::ptr superblock_validator();
superblock_detail::superblock read_superblock(persistent_data::block_manager<>::ptr bm); superblock read_superblock(persistent_data::block_manager<>::ptr bm,
superblock_detail::superblock read_superblock(persistent_data::block_manager<>::ptr bm, persistent_data::block_address location = SUPERBLOCK_LOCATION);
persistent_data::block_address location);
void write_superblock(persistent_data::block_manager<>::ptr bm, void write_superblock(persistent_data::block_manager<>::ptr bm,
superblock_detail::superblock const &sb); superblock const &sb,
void write_superblock(persistent_data::block_manager<>::ptr bm, persistent_data::block_address location = SUPERBLOCK_LOCATION);
persistent_data::block_address location,
superblock_detail::superblock const &sb); void check_superblock(superblock const &sb,
persistent_data::block_address nr_metadata_blocks,
superblock_damage::damage_visitor &visitor);
void check_superblock(persistent_data::block_manager<>::ptr bm, void check_superblock(persistent_data::block_manager<>::ptr bm,
superblock_detail::damage_visitor &visitor); persistent_data::block_address nr_metadata_blocks,
superblock_damage::damage_visitor &visitor);
} }
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -53,6 +53,7 @@ TEST_SOURCE=\
unit-tests/btree_damage_visitor_t.cc \ unit-tests/btree_damage_visitor_t.cc \
unit-tests/buffer_t.cc \ unit-tests/buffer_t.cc \
unit-tests/cache_t.cc \ unit-tests/cache_t.cc \
unit-tests/cache_superblock_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/rmap_visitor_t.cc \

View File

@ -0,0 +1,140 @@
#include "gmock/gmock.h"
#include "caching/superblock.h"
using namespace caching;
using namespace superblock_damage;
using namespace testing;
//----------------------------------------------------------------
namespace {
class damage_visitor_mock : public damage_visitor {
public:
MOCK_METHOD1(visit, void(superblock_corrupt const &));
MOCK_METHOD1(visit, void(superblock_invalid const &));
};
class CacheSuperblockTests : public Test {
public:
CacheSuperblockTests() {
}
void check() {
check_superblock(sb_, 100, visitor_);
}
void expect_invalid() {
EXPECT_CALL(visitor_, visit(Matcher<superblock_invalid const &>(_))).Times(1);
}
void check_invalid() {
expect_invalid();
check();
}
damage_visitor_mock visitor_;
superblock sb_;
};
bool operator ==(superblock_corrupt const &lhs, superblock_corrupt const &rhs) {
return lhs.get_desc() == rhs.get_desc();
}
bool operator ==(superblock_invalid const &lhs, superblock_invalid const &rhs) {
return lhs.get_desc() == rhs.get_desc();
}
ostream &operator <<(ostream &out, damage const &d) {
out << d.get_desc();
return out;
}
ostream &operator <<(ostream &out, superblock_invalid const &d) {
out << "superblock_invalid: " << d.get_desc();
return out;
}
}
//----------------------------------------------------------------
TEST_F(CacheSuperblockTests, default_constructed_superblock_is_valid)
{
sb_.flags = 0;
check();
}
TEST_F(CacheSuperblockTests, non_zero_flags_are_invalid)
{
sb_.flags = 1;
check_invalid();
}
TEST_F(CacheSuperblockTests, blocknr_is_in_range)
{
sb_.blocknr = 101;
check_invalid();
}
TEST_F(CacheSuperblockTests, magic_is_checked)
{
sb_.magic = 12345;
check_invalid();
}
TEST_F(CacheSuperblockTests, version_gt_1_is_checked)
{
sb_.version = 2;
check_invalid();
}
TEST_F(CacheSuperblockTests, version_lt_1_is_checked)
{
sb_.version = 0;
check_invalid();
}
TEST_F(CacheSuperblockTests, policy_name_must_be_null_terminated)
{
for (unsigned i = 0; i < CACHE_POLICY_NAME_SIZE; i++)
sb_.policy_name[i] = 'a';
check_invalid();
}
TEST_F(CacheSuperblockTests, policy_hint_size_checked)
{
sb_.policy_hint_size = 3;
check_invalid();
sb_.policy_hint_size = 129;
check_invalid();
sb_.policy_hint_size = 132;
check_invalid();
}
TEST_F(CacheSuperblockTests, metadata_block_size_checked)
{
sb_.metadata_block_size = 16;
check_invalid();
}
TEST_F(CacheSuperblockTests, compat_flags_checked)
{
sb_.compat_flags = 1;
check_invalid();
}
TEST_F(CacheSuperblockTests, compat_ro_flags_checked)
{
sb_.compat_ro_flags = 1;
check_invalid();
}
TEST_F(CacheSuperblockTests, incompat_flags_checked)
{
sb_.incompat_flags = 1;
check_invalid();
}
//----------------------------------------------------------------