From 12a50cb5680a411781a45e3fa874038b501167b5 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 15 Nov 2013 13:59:12 +0000 Subject: [PATCH 001/165] Some experiments with bloom filters for dm-era --- unit-tests/Makefile.in | 1 + unit-tests/bloom_filter_t.cc | 308 +++++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 unit-tests/bloom_filter_t.cc diff --git a/unit-tests/Makefile.in b/unit-tests/Makefile.in index db790e6..403e67a 100644 --- a/unit-tests/Makefile.in +++ b/unit-tests/Makefile.in @@ -50,6 +50,7 @@ TEST_SOURCE=\ unit-tests/base64_t.cc \ unit-tests/bitset_t.cc \ unit-tests/block_t.cc \ + unit-tests/bloom_filter_t.cc \ unit-tests/btree_t.cc \ unit-tests/btree_damage_visitor_t.cc \ unit-tests/buffer_t.cc \ diff --git a/unit-tests/bloom_filter_t.cc b/unit-tests/bloom_filter_t.cc new file mode 100644 index 0000000..af95f38 --- /dev/null +++ b/unit-tests/bloom_filter_t.cc @@ -0,0 +1,308 @@ +#include "gmock/gmock.h" +#include "persistent-data/transaction_manager.h" +#include "persistent-data/space-maps/core.h" +#include "persistent-data/data-structures/array_block.h" +#include "test_utils.h" + +#include +#include +#include +#include +#include +#include + +using namespace persistent_data; +using namespace std; +using namespace test; +using namespace testing; + +//---------------------------------------------------------------- + +namespace { + struct block_address_bloom_traits { + typedef block_address value_type; + + static const uint64_t ones = ~0ull; + static const uint64_t m1 = 0x9e37fffffffc0001UL; + static const uint64_t m2 = ones - 82; + + static const unsigned bits = 18; + + static uint64_t hash1(block_address const &b) { + return (b * m1) >> bits; + } + + static uint64_t hash2(block_address const &b) { + uint32_t n = b; + + n = n ^ (n >> 16); + n = n * 0x85ebca6bu; + n = n ^ (n >> 13); + n = n * 0xc2b2ae35u; + n = n ^ (n >> 16); + + return n; + } + + static uint64_t hash3(block_address const &b) { + return (b * m2) >> bits; + } + }; + + template + class bloom_filter { + public: + bloom_filter(unsigned nr_bits_power, unsigned nr_probes) + : bits_(1ull << nr_bits_power, false), + nr_probes_(nr_probes), + mask_((1ull << nr_bits_power) - 1) { + + cerr << "nr entries = " << bits_.size() << ", mask = " << mask_ << endl; + } + + bool test(typename Traits::value_type const &v) { + vector probes(nr_probes_); + fill_probes(v, probes); + + for (unsigned p = 0; p < nr_probes_; p++) + if (!bits_.at(probes[p])) + return false; + + return true; + } + + void add(typename Traits::value_type const &v) { + vector probes(nr_probes_); + fill_probes(v, probes); + + for (unsigned p = 0; p < nr_probes_; p++) { + //cerr << probes[p] << ", "; + bits_.at(probes[p]) = true; + } + //cerr << endl; + } + + void dump() const { + residency(); + + map runs; + + for (unsigned i = 0; i < bits_.size();) { + bool v = bits_[i]; + unsigned run_length = 1; + + while (bits_[++i] == v && i < bits_.size()) + run_length++; + + map::iterator it = runs.find(run_length); + if (it != runs.end()) + it->second++; + else + runs.insert(make_pair(run_length, 1)); + } + + { + map::const_iterator it; + for (it = runs.begin(); it != runs.end(); ++it) + cout << it->first << ": " << it->second << endl; + } + } + + void residency() const { + unsigned count = 0; + for (unsigned i = 0; i < bits_.size(); i++) + if (bits_[i]) + count++; + + cout << "residency: " << count << "/" << bits_.size() << endl; + } + + private: + void fill_probes(typename Traits::value_type const &v, vector &probes) { + uint32_t h1 = Traits::hash1(v) & mask_; + uint32_t h2 = Traits::hash2(v) & mask_; + + probes[0] = h1; + for (unsigned p = 1; p < nr_probes_; p++) { + h1 = (h1 + h2) & mask_; + h2 = (h2 + p) & mask_; + probes[p] = h1; + } + } + + vector bits_; + unsigned nr_probes_; + uint64_t mask_; + }; + + //-------------------------------- +#if 0 + class dm_era { + public: + dm_era(block_address nr_blocks) + : nr_blocks_(nr_blocks), + era_base_(0), + base_(nr_blocks, false) { + } + + set blocks_written_since(unsigned era) const { + + } + + unsigned get_era() const { + return era_base_ + eras_.size() - 1; + } + + void record_write(block_address b) { + current_era.record_write(b); + } + + void resize(block_address new_size) { + nr_blocks_ = new_size; + push_era(); + base_.resize(new_size, false); + } + + private: + era_details ¤t_era() { + return eras_.back(); + } + + void need_new_era() { + // ??? + } + + void push_era() { + eras_.push_back(era(nr_blocks_)); + if (eras_.size() > 100) + pop_era(); + } + + void pop_era() { + era_base_++; + + + + eras_.pop_front(); + } + + static const unsigned NR_PROBES = 6; + + class era_details { + public: + era_details(block_address nr_blocks) + : nr_blocks_(nr_blocks), + f(power_bits(nr_blocks, NR_PROBES)) { + } + + void record_write(block_address b) { + f.add(b); + } + + void add_blocks_written(set filter; + + block_address nr_blocks; + filter f; + }; + + block_address nr_blocks_; + unsigned era_base_; + vector base_; + deque eras_; + }; +#endif + + //-------------------------------- + + class BloomFilterTests : public Test { + public: + + set generate_random_blocks(unsigned count, + block_address max = std::numeric_limits::max()) { + set r; + + using namespace boost::random; + + mt19937 rng; + uniform_int_distribution uniform_dist(0, max); + + while (r.size() < count) { + block_address b = uniform_dist(rng); + r.insert(b); + } + + return r; + } + }; +} + +//---------------------------------------------------------------- + +TEST_F(BloomFilterTests, can_create_a_bloom_filter) +{ + bloom_filter f(10, 3); +} + +TEST_F(BloomFilterTests, no_false_negatives) +{ + bloom_filter f(12, 6); + set bs = generate_random_blocks(1000); + + set::const_iterator it; + for (it = bs.begin(); it != bs.end(); ++it) + f.add(*it); + + for (it = bs.begin(); it != bs.end(); ++it) + ASSERT_THAT(f.test(*it), Eq(true)); + + f.dump(); +} + +TEST_F(BloomFilterTests, count_false_positives) +{ + block_address nr_blocks = 128 * 1024 * 1024; + block_address written_blocks = nr_blocks / 100; + + unsigned shift = 1; + + while ((1ull << shift) < (16 * written_blocks)) + shift++; + cerr << "bitset " << ((1 << shift) / (8 * 1024)) << "k" << endl; + + bloom_filter f(shift, 6); + set bs = generate_random_blocks(written_blocks, nr_blocks); + set::const_iterator it; + + for (it = bs.begin(); it != bs.end(); ++it) + f.add(*it); + + f.dump(); + + unsigned count = 0; + for (unsigned i = 0; i < nr_blocks; i++) + if (!bs.count(i) && f.test(i)) + count++; + + cerr << count << "false positives out of " << nr_blocks << endl; + cerr << static_cast(count * 100) / static_cast(nr_blocks) << "%" << endl; +} + +//---------------------------------------------------------------- From fd061fab15c32674501ba280db435446da5753a2 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 18 Nov 2013 13:06:35 +0000 Subject: [PATCH 002/165] [bitset] change the name of the #include guard --- persistent-data/data-structures/bitset.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/persistent-data/data-structures/bitset.h b/persistent-data/data-structures/bitset.h index b01845e..9d2b590 100644 --- a/persistent-data/data-structures/bitset.h +++ b/persistent-data/data-structures/bitset.h @@ -16,8 +16,8 @@ // with thin-provisioning-tools. If not, see // . -#ifndef BITSET_H -#define BITSET_H +#ifndef PERSISTENT_DATA_DATA_STRUCTURES_BITSET_H +#define PERSISTENT_DATA_DATA_STRUCTURES_BITSET_H #include "persistent-data/run.h" From b32a3b9fb9201a11878deccece8f9df759069dea Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 18 Nov 2013 13:07:08 +0000 Subject: [PATCH 003/165] [bitset] get_nr_bits() method --- persistent-data/data-structures/bitset.cc | 10 ++++++++++ persistent-data/data-structures/bitset.h | 1 + 2 files changed, 11 insertions(+) diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index 9625dae..55b157d 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -46,6 +46,10 @@ namespace persistent_data { return array_.get_root(); } + unsigned get_nr_bits() const { + return nr_bits_; + } + void grow(unsigned new_nr_bits, bool default_value) { pad_last_block(default_value); resize_array(new_nr_bits, default_value); @@ -214,6 +218,12 @@ bitset::get_root() const return impl_->get_root(); } +unsigned +bitset::get_nr_bits() const +{ + return impl_->get_nr_bits(); +} + void bitset::grow(unsigned new_nr_bits, bool default_value) { diff --git a/persistent-data/data-structures/bitset.h b/persistent-data/data-structures/bitset.h index 9d2b590..b143278 100644 --- a/persistent-data/data-structures/bitset.h +++ b/persistent-data/data-structures/bitset.h @@ -54,6 +54,7 @@ namespace persistent_data { bitset(tm_ptr tm); bitset(tm_ptr tm, block_address root, unsigned nr_bits); block_address get_root() const; + unsigned get_nr_bits() const; void grow(unsigned new_nr_bits, bool default_value); void destroy(); From 9402f0940813e5f82406f0deac12b835d50bc5ae Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 18 Nov 2013 13:07:21 +0000 Subject: [PATCH 004/165] [bitset] tweak an exception message --- persistent-data/data-structures/bitset.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index 55b157d..c9ad0b4 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -188,7 +188,7 @@ namespace persistent_data { if (n >= nr_bits_) { std::ostringstream str; str << "bitset index out of bounds (" - << n << " >= " << nr_bits_ << endl; + << n << " >= " << nr_bits_ << ")"; throw runtime_error(str.str()); } } From 180f7e61877bfbcf71f28e2cba013f056e9a1353 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 18 Nov 2013 13:08:07 +0000 Subject: [PATCH 005/165] persistent-data/data-structures/bloom_filter --- Makefile.in | 1 + .../data-structures/bloom_filter.cc | 146 +++++++++ .../data-structures/bloom_filter.h | 47 +++ unit-tests/bloom_filter_t.cc | 287 ++++-------------- 4 files changed, 260 insertions(+), 221 deletions(-) create mode 100644 persistent-data/data-structures/bloom_filter.cc create mode 100644 persistent-data/data-structures/bloom_filter.h diff --git a/Makefile.in b/Makefile.in index 73326b1..d08f8cf 100644 --- a/Makefile.in +++ b/Makefile.in @@ -55,6 +55,7 @@ SOURCE=\ persistent-data/transaction_manager.cc \ \ persistent-data/data-structures/bitset.cc \ + persistent-data/data-structures/bloom_filter.cc \ persistent-data/data-structures/btree.cc \ \ persistent-data/space_map.cc \ diff --git a/persistent-data/data-structures/bloom_filter.cc b/persistent-data/data-structures/bloom_filter.cc new file mode 100644 index 0000000..3ca6ffb --- /dev/null +++ b/persistent-data/data-structures/bloom_filter.cc @@ -0,0 +1,146 @@ +#include "persistent-data/data-structures/bloom_filter.h" + +#include + +using namespace persistent_data; + +//---------------------------------------------------------------- + +namespace { + static const uint64_t m1 = 0x9e37fffffffc0001UL; + static const unsigned bits = 18; + + static uint32_t hash1(block_address const &b) { + return (b * m1) >> bits; + } + + static uint32_t hash2(block_address const &b) { + uint32_t n = b; + + n = n ^ (n >> 16); + n = n * 0x85ebca6bu; + n = n ^ (n >> 13); + n = n * 0xc2b2ae35u; + n = n ^ (n >> 16); + + return n; + } + + void check_power_of_two(unsigned nr_bits) { + if (nr_bits & (nr_bits - 1)) + throw std::runtime_error("bloom filter needs a power of two nr_bits"); + } +} + +//---------------------------------------------------------------- + +bloom_filter::bloom_filter(tm_ptr tm, + unsigned nr_bits, unsigned nr_probes) + : tm_(tm), + bits_(tm), + nr_probes_(nr_probes), + mask_(nr_bits - 1) +{ + check_power_of_two(nr_bits); + bits_.grow(nr_bits, false); +} + +bloom_filter::bloom_filter(tm_ptr tm, block_address root, + unsigned nr_bits, unsigned nr_probes) + : tm_(tm), + bits_(tm, root, nr_bits), + nr_probes_(nr_probes), + mask_(nr_bits - 1) +{ + check_power_of_two(nr_bits); +} + +block_address +bloom_filter::get_root() const +{ + return bits_.get_root(); +} + +bool +bloom_filter::test(uint64_t b) +{ + vector probes(nr_probes_); + fill_probes(b, probes); + + for (unsigned p = 0; p < nr_probes_; p++) + if (!bits_.get(probes[p])) + return false; + + return true; +} + +void +bloom_filter::set(uint64_t b) +{ + vector probes(nr_probes_); + fill_probes(b, probes); + + for (unsigned p = 0; p < nr_probes_; p++) + bits_.set(probes[p], true); +} + +void +bloom_filter::flush() +{ + bits_.flush(); +} + +void +bloom_filter::fill_probes(block_address b, vector &probes) const +{ + uint32_t h1 = hash1(b) & mask_; + uint32_t h2 = hash2(b) & mask_; + + probes[0] = h1; + for (unsigned p = 1; p < nr_probes_; p++) { + h1 = (h1 + h2) & mask_; + h2 = (h2 + p) & mask_; + probes[p] = h1; + } +} + +void +bloom_filter::print_debug(ostream &out) +{ + print_residency(out); + + map runs; + + for (unsigned i = 0; i < bits_.get_nr_bits();) { + bool v = bits_.get(i); + unsigned run_length = 1; + + while (++i < bits_.get_nr_bits() && bits_.get(i) == v) + run_length++; + + map::iterator it = runs.find(run_length); + if (it != runs.end()) + it->second++; + else + runs.insert(make_pair(run_length, 1)); + } + + { + map::const_iterator it; + for (it = runs.begin(); it != runs.end(); ++it) + out << it->first << ": " << it->second << endl; + } +} + +void +bloom_filter::print_residency(ostream &out) +{ + unsigned count = 0; + for (unsigned i = 0; i < bits_.get_nr_bits(); i++) + if (bits_.get(i)) + count++; + + out << "residency: " << count << "/" << bits_.get_nr_bits() << endl; +} + +//---------------------------------------------------------------- diff --git a/persistent-data/data-structures/bloom_filter.h b/persistent-data/data-structures/bloom_filter.h new file mode 100644 index 0000000..6703a7d --- /dev/null +++ b/persistent-data/data-structures/bloom_filter.h @@ -0,0 +1,47 @@ +#ifndef PERSISTENT_DATA_DATA_STRUCTURES_BLOOM_FILTER_H +#define PERSISTENT_DATA_DATA_STRUCTURES_BLOOM_FILTER_H + +#include "persistent-data/transaction_manager.h" +#include "persistent-data/data-structures/bitset.h" + +#include + +//---------------------------------------------------------------- + +namespace persistent_data { + class bloom_filter { + public: + typedef boost::shared_ptr ptr; + typedef typename persistent_data::transaction_manager::ptr tm_ptr; + + // nr_bits must be a power of two + bloom_filter(tm_ptr tm, + unsigned nr_bits, unsigned nr_probes); + + bloom_filter(tm_ptr tm, block_address root, + unsigned nr_bits_power, unsigned nr_probes); + + block_address get_root() const; + + bool test(uint64_t b); // not const due to caching effects in bitset + void set(uint64_t b); + void flush(); + + void print_debug(ostream &out); + + private: + void print_residency(ostream &out); + + void fill_probes(block_address b, vector &probes) const; + + tm_ptr tm_; + unsigned nr_bits_; + persistent_data::bitset bits_; + unsigned nr_probes_; + uint64_t mask_; + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/unit-tests/bloom_filter_t.cc b/unit-tests/bloom_filter_t.cc index af95f38..bb879ed 100644 --- a/unit-tests/bloom_filter_t.cc +++ b/unit-tests/bloom_filter_t.cc @@ -1,4 +1,5 @@ #include "gmock/gmock.h" +#include "persistent-data/data-structures/bloom_filter.h" #include "persistent-data/transaction_manager.h" #include "persistent-data/space-maps/core.h" #include "persistent-data/data-structures/array_block.h" @@ -19,221 +20,19 @@ using namespace testing; //---------------------------------------------------------------- namespace { - struct block_address_bloom_traits { - typedef block_address value_type; - - static const uint64_t ones = ~0ull; - static const uint64_t m1 = 0x9e37fffffffc0001UL; - static const uint64_t m2 = ones - 82; - - static const unsigned bits = 18; - - static uint64_t hash1(block_address const &b) { - return (b * m1) >> bits; - } - - static uint64_t hash2(block_address const &b) { - uint32_t n = b; - - n = n ^ (n >> 16); - n = n * 0x85ebca6bu; - n = n ^ (n >> 13); - n = n * 0xc2b2ae35u; - n = n ^ (n >> 16); - - return n; - } - - static uint64_t hash3(block_address const &b) { - return (b * m2) >> bits; - } - }; - - template - class bloom_filter { - public: - bloom_filter(unsigned nr_bits_power, unsigned nr_probes) - : bits_(1ull << nr_bits_power, false), - nr_probes_(nr_probes), - mask_((1ull << nr_bits_power) - 1) { - - cerr << "nr entries = " << bits_.size() << ", mask = " << mask_ << endl; - } - - bool test(typename Traits::value_type const &v) { - vector probes(nr_probes_); - fill_probes(v, probes); - - for (unsigned p = 0; p < nr_probes_; p++) - if (!bits_.at(probes[p])) - return false; - - return true; - } - - void add(typename Traits::value_type const &v) { - vector probes(nr_probes_); - fill_probes(v, probes); - - for (unsigned p = 0; p < nr_probes_; p++) { - //cerr << probes[p] << ", "; - bits_.at(probes[p]) = true; - } - //cerr << endl; - } - - void dump() const { - residency(); - - map runs; - - for (unsigned i = 0; i < bits_.size();) { - bool v = bits_[i]; - unsigned run_length = 1; - - while (bits_[++i] == v && i < bits_.size()) - run_length++; - - map::iterator it = runs.find(run_length); - if (it != runs.end()) - it->second++; - else - runs.insert(make_pair(run_length, 1)); - } - - { - map::const_iterator it; - for (it = runs.begin(); it != runs.end(); ++it) - cout << it->first << ": " << it->second << endl; - } - } - - void residency() const { - unsigned count = 0; - for (unsigned i = 0; i < bits_.size(); i++) - if (bits_[i]) - count++; - - cout << "residency: " << count << "/" << bits_.size() << endl; - } - - private: - void fill_probes(typename Traits::value_type const &v, vector &probes) { - uint32_t h1 = Traits::hash1(v) & mask_; - uint32_t h2 = Traits::hash2(v) & mask_; - - probes[0] = h1; - for (unsigned p = 1; p < nr_probes_; p++) { - h1 = (h1 + h2) & mask_; - h2 = (h2 + p) & mask_; - probes[p] = h1; - } - } - - vector bits_; - unsigned nr_probes_; - uint64_t mask_; - }; - - //-------------------------------- -#if 0 - class dm_era { - public: - dm_era(block_address nr_blocks) - : nr_blocks_(nr_blocks), - era_base_(0), - base_(nr_blocks, false) { - } - - set blocks_written_since(unsigned era) const { - - } - - unsigned get_era() const { - return era_base_ + eras_.size() - 1; - } - - void record_write(block_address b) { - current_era.record_write(b); - } - - void resize(block_address new_size) { - nr_blocks_ = new_size; - push_era(); - base_.resize(new_size, false); - } - - private: - era_details ¤t_era() { - return eras_.back(); - } - - void need_new_era() { - // ??? - } - - void push_era() { - eras_.push_back(era(nr_blocks_)); - if (eras_.size() > 100) - pop_era(); - } - - void pop_era() { - era_base_++; - - - - eras_.pop_front(); - } - - static const unsigned NR_PROBES = 6; - - class era_details { - public: - era_details(block_address nr_blocks) - : nr_blocks_(nr_blocks), - f(power_bits(nr_blocks, NR_PROBES)) { - } - - void record_write(block_address b) { - f.add(b); - } - - void add_blocks_written(set filter; - - block_address nr_blocks; - filter f; - }; - - block_address nr_blocks_; - unsigned era_base_; - vector base_; - deque eras_; - }; -#endif + block_address const BLOCK_SIZE = 4096; + block_address const NR_BLOCKS = 102400; + block_address const SUPERBLOCK = 0; //-------------------------------- class BloomFilterTests : public Test { public: + BloomFilterTests() + : bm_(create_bm(NR_BLOCKS)), + sm_(setup_core_map()), + tm_(new transaction_manager(bm_, sm_)) { + } set generate_random_blocks(unsigned count, block_address max = std::numeric_limits::max()) { @@ -251,58 +50,104 @@ namespace { return r; } + + void commit() { + block_manager<>::write_ref superblock(bm_->superblock(SUPERBLOCK)); + } + + space_map::ptr setup_core_map() { + space_map::ptr sm(new core_map(NR_BLOCKS)); + sm->inc(SUPERBLOCK); + return sm; + } + + with_temp_directory dir_; + block_manager<>::ptr bm_; + space_map::ptr sm_; + transaction_manager::ptr tm_; }; } //---------------------------------------------------------------- +TEST_F(BloomFilterTests, nr_bits_must_be_a_power_of_two) +{ + ASSERT_THROW(bloom_filter f(tm_, 1023, 3), runtime_error); +} + TEST_F(BloomFilterTests, can_create_a_bloom_filter) { - bloom_filter f(10, 3); + bloom_filter f(tm_, 1024, 3); } TEST_F(BloomFilterTests, no_false_negatives) { - bloom_filter f(12, 6); + bloom_filter f(tm_, 4096, 6); set bs = generate_random_blocks(1000); set::const_iterator it; for (it = bs.begin(); it != bs.end(); ++it) - f.add(*it); + f.set(*it); for (it = bs.begin(); it != bs.end(); ++it) ASSERT_THAT(f.test(*it), Eq(true)); +} - f.dump(); +TEST_F(BloomFilterTests, reload_works) +{ + block_address root; + set bs = generate_random_blocks(1000); + + { + bloom_filter f(tm_, 4096, 6); + + set::const_iterator it; + for (it = bs.begin(); it != bs.end(); ++it) + f.set(*it); + + f.flush(); + root = f.get_root(); + commit(); + } + + { + bloom_filter f(tm_, root, 4096, 6); + + set::const_iterator it; + for (it = bs.begin(); it != bs.end(); ++it) + ASSERT_THAT(f.test(*it), Eq(true)); + } } TEST_F(BloomFilterTests, count_false_positives) { - block_address nr_blocks = 128 * 1024 * 1024; + block_address nr_blocks = 1024 * 1024; block_address written_blocks = nr_blocks / 100; unsigned shift = 1; while ((1ull << shift) < (16 * written_blocks)) shift++; - cerr << "bitset " << ((1 << shift) / (8 * 1024)) << "k" << endl; + cerr << "bitset size: " << ((1 << shift) / (8 * 1024)) << "k" << endl; + + bloom_filter f(tm_, 1 << shift, 6); - bloom_filter f(shift, 6); set bs = generate_random_blocks(written_blocks, nr_blocks); set::const_iterator it; for (it = bs.begin(); it != bs.end(); ++it) - f.add(*it); + f.set(*it); - f.dump(); + // f.print_debug(cerr); unsigned count = 0; for (unsigned i = 0; i < nr_blocks; i++) if (!bs.count(i) && f.test(i)) count++; - cerr << count << "false positives out of " << nr_blocks << endl; - cerr << static_cast(count * 100) / static_cast(nr_blocks) << "%" << endl; + cerr << count << " false positives out of " << nr_blocks << ", " + << static_cast(count * 100) / static_cast(nr_blocks) + << "%" << endl; } //---------------------------------------------------------------- From 983a5e84e6e0c189aa70897da900f8b56fe3d33b Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 19 Nov 2013 10:23:35 +0000 Subject: [PATCH 006/165] Move endian_utils to base/ --- Makefile.in | 15 +++++++++++---- {persistent-data => base}/endian_utils.cc | 0 {persistent-data => base}/endian_utils.h | 5 ++--- caching/mapping_array.cc | 2 +- caching/metadata.h | 3 ++- caching/superblock.h | 2 +- persistent-data/data-structures/array_block.h | 2 +- persistent-data/data-structures/btree.h | 2 +- persistent-data/space-maps/disk.cc | 3 ++- persistent-data/space-maps/disk_structures.h | 2 +- thin-provisioning/metadata.h | 3 ++- thin-provisioning/superblock.h | 3 ++- 12 files changed, 26 insertions(+), 16 deletions(-) rename {persistent-data => base}/endian_utils.cc (100%) rename {persistent-data => base}/endian_utils.h (97%) diff --git a/Makefile.in b/Makefile.in index d08f8cf..e54fbd5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -36,6 +36,7 @@ all: $(PROGRAMS) SOURCE=\ base/base64.cc \ + base/endian_utils.cc \ base/error_state.cc \ \ caching/hint_array.cc \ @@ -46,8 +47,9 @@ SOURCE=\ caching/restore_emitter.cc \ caching/xml_format.cc \ \ + era/era_detail.cc \ + \ persistent-data/checksum.cc \ - persistent-data/endian_utils.cc \ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ @@ -154,8 +156,9 @@ THIN_REPAIR_SOURCE=$(SOURCE) THIN_RESTORE_SOURCE=$(SOURCE) THIN_CHECK_SOURCE=\ base/error_state.cc \ + base/endian_utils.cc \ + \ persistent-data/checksum.cc \ - persistent-data/endian_utils.cc \ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ @@ -166,6 +169,7 @@ THIN_CHECK_SOURCE=\ persistent-data/space-maps/recursive.cc \ persistent-data/space-maps/careful_alloc.cc \ persistent-data/transaction_manager.cc \ + \ thin-provisioning/device_tree.cc \ thin-provisioning/mapping_tree.cc \ thin-provisioning/metadata.cc \ @@ -173,8 +177,9 @@ THIN_CHECK_SOURCE=\ thin-provisioning/superblock.cc THIN_RMAP_SOURCE=\ + base/endian_utils.cc \ + \ persistent-data/checksum.cc \ - persistent-data/endian_utils.cc \ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ @@ -233,8 +238,9 @@ thin_metadata_size: thin-provisioning/thin_metadata_size.o CACHE_CHECK_SOURCE=\ base/base64.cc \ base/error_state.cc \ + base/endian_utils.cc \ + \ persistent-data/checksum.cc \ - persistent-data/endian_utils.cc \ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ @@ -246,6 +252,7 @@ CACHE_CHECK_SOURCE=\ persistent-data/space-maps/recursive.cc \ persistent-data/space-maps/careful_alloc.cc \ persistent-data/transaction_manager.cc \ + \ caching/hint_array.cc \ caching/superblock.cc \ caching/mapping_array.cc \ diff --git a/persistent-data/endian_utils.cc b/base/endian_utils.cc similarity index 100% rename from persistent-data/endian_utils.cc rename to base/endian_utils.cc diff --git a/persistent-data/endian_utils.h b/base/endian_utils.h similarity index 97% rename from persistent-data/endian_utils.h rename to base/endian_utils.h index 39276e6..3ad75ae 100644 --- a/persistent-data/endian_utils.h +++ b/base/endian_utils.h @@ -16,8 +16,8 @@ // with thin-provisioning-tools. If not, see // . -#ifndef ENDIAN_H -#define ENDIAN_H +#ifndef BASE_ENDIAN_H +#define BASE_ENDIAN_H #include #include @@ -25,7 +25,6 @@ //---------------------------------------------------------------- -// FIXME: rename to endian namespace base { // These are just little wrapper types to make the compiler diff --git a/caching/mapping_array.cc b/caching/mapping_array.cc index d31c2c9..c6af4ef 100644 --- a/caching/mapping_array.cc +++ b/caching/mapping_array.cc @@ -1,5 +1,5 @@ +#include "base/endian_utils.h" #include "caching/mapping_array.h" -#include "persistent-data/endian_utils.h" #include diff --git a/caching/metadata.h b/caching/metadata.h index 611b424..a606878 100644 --- a/caching/metadata.h +++ b/caching/metadata.h @@ -1,10 +1,11 @@ #ifndef CACHE_METADATA_H #define CACHE_METADATA_H +#include "base/endian_utils.h" + #include "persistent-data/block.h" #include "persistent-data/data-structures/array.h" #include "persistent-data/data-structures/bitset.h" -#include "persistent-data/endian_utils.h" #include "persistent-data/space-maps/disk.h" #include "persistent-data/transaction_manager.h" diff --git a/caching/superblock.h b/caching/superblock.h index 2c2cf30..d1a24aa 100644 --- a/caching/superblock.h +++ b/caching/superblock.h @@ -1,7 +1,7 @@ #ifndef CACHE_SUPERBLOCK_H #define CACHE_SUPERBLOCK_H -#include "persistent-data/endian_utils.h" +#include "base/endian_utils.h" #include "persistent-data/data-structures/btree.h" #include diff --git a/persistent-data/data-structures/array_block.h b/persistent-data/data-structures/array_block.h index 1bb3d4c..638e164 100644 --- a/persistent-data/data-structures/array_block.h +++ b/persistent-data/data-structures/array_block.h @@ -19,7 +19,7 @@ #ifndef ARRAY_BLOCK_H #define ARRAY_BLOCK_H -#include "persistent-data/endian_utils.h" +#include "base/endian_utils.h" //---------------------------------------------------------------- diff --git a/persistent-data/data-structures/btree.h b/persistent-data/data-structures/btree.h index 26e687a..93c3284 100644 --- a/persistent-data/data-structures/btree.h +++ b/persistent-data/data-structures/btree.h @@ -19,7 +19,7 @@ #ifndef BTREE_H #define BTREE_H -#include "persistent-data/endian_utils.h" +#include "base/endian_utils.h" #include "persistent-data/transaction_manager.h" #include "persistent-data/data-structures/ref_counter.h" diff --git a/persistent-data/space-maps/disk.cc b/persistent-data/space-maps/disk.cc index 36937ec..1de3688 100644 --- a/persistent-data/space-maps/disk.cc +++ b/persistent-data/space-maps/disk.cc @@ -16,6 +16,8 @@ // with thin-provisioning-tools. If not, see // . +#include "base/endian_utils.h" + #include "persistent-data/space-maps/disk.h" #include "persistent-data/space-maps/disk_structures.h" #include "persistent-data/space-maps/recursive.h" @@ -23,7 +25,6 @@ #include "persistent-data/data-structures/btree_checker.h" #include "persistent-data/checksum.h" -#include "persistent-data/endian_utils.h" #include "persistent-data/math_utils.h" #include "persistent-data/transaction_manager.h" diff --git a/persistent-data/space-maps/disk_structures.h b/persistent-data/space-maps/disk_structures.h index a92f490..1429d36 100644 --- a/persistent-data/space-maps/disk_structures.h +++ b/persistent-data/space-maps/disk_structures.h @@ -19,7 +19,7 @@ #ifndef SPACE_MAP_DISK_STRUCTURES_H #define SPACE_MAP_DISK_STRUCTURES_H -#include "persistent-data/endian_utils.h" +#include "base/endian_utils.h" // FIXME: what's this included for? #include "persistent-data/data-structures/btree.h" diff --git a/thin-provisioning/metadata.h b/thin-provisioning/metadata.h index 9749acb..c0913a0 100644 --- a/thin-provisioning/metadata.h +++ b/thin-provisioning/metadata.h @@ -19,9 +19,10 @@ #ifndef METADATA_LL_H #define METADATA_LL_H +#include "base/endian_utils.h" + #include "persistent-data/block.h" #include "persistent-data/data-structures/btree.h" -#include "persistent-data/endian_utils.h" #include "persistent-data/space-maps/disk.h" #include "persistent-data/transaction_manager.h" diff --git a/thin-provisioning/superblock.h b/thin-provisioning/superblock.h index d6d78e3..3a3d90a 100644 --- a/thin-provisioning/superblock.h +++ b/thin-provisioning/superblock.h @@ -1,8 +1,9 @@ #ifndef THIN_SUPERBLOCK_H #define THIN_SUPERBLOCK_H +#include "base/endian_utils.h" + #include "persistent-data/block.h" -#include "persistent-data/endian_utils.h" #include "persistent-data/data-structures/ref_counter.h" //---------------------------------------------------------------- From 804108fb737d2ea4a65bd8f967427e604052ee2d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 19 Nov 2013 10:24:31 +0000 Subject: [PATCH 007/165] [era] era_detail --- era/era_detail.cc | 36 ++++++++++++++++++++++++++++++++++++ era/era_detail.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 era/era_detail.cc create mode 100644 era/era_detail.h diff --git a/era/era_detail.cc b/era/era_detail.cc new file mode 100644 index 0000000..43fbc51 --- /dev/null +++ b/era/era_detail.cc @@ -0,0 +1,36 @@ +#include "era/era_detail.h" + +#include + +using namespace base; +using namespace era; + +//---------------------------------------------------------------- + +namespace { + le32 pack_hash_detail(uint32_t hash1, uint32_t hash2, uint32_t nr_probes) { + throw std::runtime_error("not implemented"); + } + + void unpack_hash_detail(le32 packed, uint32_t &hash1, uint32_t &hash2, uint32_t &nr_probes) { + throw std::runtime_error("not implemented"); + } +} + +void +era_detail_traits::unpack(disk_type const &disk, value_type &value) +{ + value.nr_bits = to_cpu(disk.nr_bits); + unpack_hash_detail(disk.hash_fns_and_probes, value.hash1, value.hash2, value.nr_probes); + value.bloom_root = to_cpu(disk.bloom_root); +} + +void +era_detail_traits::pack(value_type const &value, disk_type &disk) +{ + disk.nr_bits = to_disk(value.nr_bits); + disk.hash_fns_and_probes = pack_hash_detail(value.hash1, value.hash2, value.nr_probes); + disk.bloom_root = to_disk(value.bloom_root); +} + +//---------------------------------------------------------------- diff --git a/era/era_detail.h b/era/era_detail.h new file mode 100644 index 0000000..1b907fb --- /dev/null +++ b/era/era_detail.h @@ -0,0 +1,36 @@ +#ifndef ERA_DETAIL_H +#define ERA_DETAIL_H + +#include "base/endian_utils.h" + +//---------------------------------------------------------------- + +namespace era { + struct era_detail_disk { + base::le32 nr_bits; + base::le32 hash_fns_and_probes; + base::le64 bloom_root; + }; + + struct era_detail { + uint32_t nr_bits; + + uint32_t hash1; + uint32_t hash2; + uint32_t nr_probes; + + uint64_t bloom_root; + }; + + struct era_detail_traits { + typedef era_detail_disk disk_type; + typedef era_detail value_type; + + static void unpack(disk_type const &disk, value_type &value); + static void pack(value_type const &value, disk_type &disk); + }; +} + +//---------------------------------------------------------------- + +#endif From cedaf28a563149a033ea41ad9af1343bbf1061da Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 19 Nov 2013 10:48:39 +0000 Subject: [PATCH 008/165] Forgot to set packed attribute for era_detail --- era/era_detail.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/era/era_detail.h b/era/era_detail.h index 1b907fb..adaec57 100644 --- a/era/era_detail.h +++ b/era/era_detail.h @@ -10,7 +10,7 @@ namespace era { base::le32 nr_bits; base::le32 hash_fns_and_probes; base::le64 bloom_root; - }; + } __attribute__ ((packed)); struct era_detail { uint32_t nr_bits; From e2bb628c57fb8551e3ca4698f90f0947aecc5ac4 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 8 Jan 2014 10:53:05 +0000 Subject: [PATCH 009/165] [era] add in some wip files (transferring development to another machine) --- era/bloom_tree.h | 112 +++++++++++++++++++++++++++++++++++++++++++ era/superblock.cc | 118 ++++++++++++++++++++++++++++++++++++++++++++++ era/superblock.h | 84 +++++++++++++++++++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 era/bloom_tree.h create mode 100644 era/superblock.cc create mode 100644 era/superblock.h diff --git a/era/bloom_tree.h b/era/bloom_tree.h new file mode 100644 index 0000000..e98ed37 --- /dev/null +++ b/era/bloom_tree.h @@ -0,0 +1,112 @@ +#ifndef ERA_BLOOM_TREE_H +#define ERA_BLOOM_TREE_H + +//---------------------------------------------------------------- + +namespace era { +} + +//---------------------------------------------------------------- + +#endif + + +#if 0 + class dm_era { + public: + dm_era(block_address nr_blocks) + : nr_blocks_(nr_blocks), + era_base_(0), + base_(nr_blocks, false) { + } + + set blocks_written_since(unsigned era) const { + + } + + unsigned get_era() const { + return era_base_ + eras_.size() - 1; + } + + void record_write(block_address b) { + current_era.record_write(b); + } + + void resize(block_address new_size) { + nr_blocks_ = new_size; + push_era(); + base_.resize(new_size, false); + } + + private: + era_details ¤t_era() { + return eras_.back(); + } + + void need_new_era() { + // ??? + } + + void push_era() { + eras_.push_back(era(nr_blocks_)); + if (eras_.size() > 100) + pop_era(); + } + + void pop_era() { + era_base_++; + + + + eras_.pop_front(); + } + + static const unsigned NR_PROBES = 6; + + class era_details { + public: + era_details(block_address nr_blocks) + : nr_blocks_(nr_blocks), + f(power_bits(nr_blocks, NR_PROBES)) { + } + + void record_write(block_address b) { + f.add(b); + } + + void add_blocks_written(set filter; + + block_address nr_blocks; + filter f; + }; + + space_map::ptr setup_core_map() { + space_map::ptr sm(new core_map(NR_BLOCKS)); + sm->inc(SUPERBLOCK); + return sm; + } + + block_address nr_blocks_; + unsigned era_base_; + vector base_; + deque eras_; + }; +#endif diff --git a/era/superblock.cc b/era/superblock.cc new file mode 100644 index 0000000..ed3ebb8 --- /dev/null +++ b/era/superblock.cc @@ -0,0 +1,118 @@ +#include "era/superblock.h" + +#include "persistent-data/checksum.h" + +using namespace base; +using namespace era; +using namespace persistent_data; + +//---------------------------------------------------------------- + +namespace { + using namespace base; + + struct superblock_disk { + le32 csum; + le32 flags; + le64 blocknr; + + __u8 uuid[16]; + le64 magic; + le32 version; + + le32 data_block_size; + le32 metadata_block_size; + le32 nr_blocks; + + le32 current_era; + era_detail_disk current_detail; + + le64 bloom_filters_root; + le64 era_array_root; + } __attribute__ ((packed)); + + struct superblock_traits { + typedef superblock_disk disk_type; + typedef superblock value_type; + + static void unpack(disk_type const &disk, value_type &value); + static void pack(value_type const &value, disk_type &disk); + }; + + uint32_t const SUPERBLOCK_MAGIC = 2126579579; + uint32_t const VERSION_BEGIN = 1; + uint32_t const VERSION_END = 2; +} + +//---------------------------------------------------------------- + +superblock::superblock() + : csum(0), + blocknr(0), + flags(), + magic(SUPERBLOCK_MAGIC), + version(VERSION_END - 1), + data_block_size(0), + metadata_block_size(8), + nr_blocks(0), + current_era(0), + era_root(0), + era_array_root(0) +{ + memset(uuid, 0, sizeof(uuid)); + memset(metadata_space_map_root, 0, sizeof(metadata_space_map_root)); +} + +//---------------------------------------------------------------- + +namespace validator { + using namespace persistent_data; + + uint32_t const VERSION = 1; + unsigned const SECTOR_TO_BLOCK_SHIFT = 3; + uint32_t const SUPERBLOCK_CSUM_SEED = 146538381; + + // FIXME: turn into a template, we have 3 similar classes now + struct sb_validator : public block_manager<>::validator { + virtual void check(buffer<> const &b, block_address location) const { + superblock_disk const *sbd = reinterpret_cast(&b); + crc32c sum(SUPERBLOCK_CSUM_SEED); + sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); + if (sum.get_sum() != to_cpu(sbd->csum)) + throw checksum_error("bad checksum in superblock"); + } + + virtual void prepare(buffer<> &b, block_address location) const { + superblock_disk *sbd = reinterpret_cast(&b); + crc32c sum(SUPERBLOCK_CSUM_SEED); + sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); + sbd->csum = to_disk(sum.get_sum()); + } + }; + + block_manager<>::validator::ptr mk_v() { + return block_manager<>::validator::ptr(new sb_validator); + } +} + +//---------------------------------------------------------------- + +superblock +era::read_superblock(block_manager<>::ptr bm, block_address location) +{ + superblock sb; + block_manager<>::read_ref r = bm->read_lock(location, validator::mk_v()); + superblock_disk const *sbd = reinterpret_cast(&r.data()); + superblock_traits::unpack(*sbd, sb); + + return sb; +} + +void +era::write_superblock(block_manager<>::ptr bm, superblock const &sb, block_address location) +{ + block_manager<>::write_ref w = bm->superblock_zero(location, validator::mk_v()); + superblock_traits::pack(sb, *reinterpret_cast(w.data().raw())); +} + +//---------------------------------------------------------------- diff --git a/era/superblock.h b/era/superblock.h new file mode 100644 index 0000000..5980daf --- /dev/null +++ b/era/superblock.h @@ -0,0 +1,84 @@ +#ifndef ERA_SUPERBLOCK_H +#define ERA_SUPERBLOCK_h + +#include "persistent-data/block.h" +#include "era/era_detail.h" + +#include + +//---------------------------------------------------------------- + +namespace era { + typedef unsigned char __u8; + + class superblock_flags { + public: + enum flag { + CLEAN_SHUTDOWN + }; + + enum flag_bits { + CLEAN_SHUTDOWN_BIT = 0 + }; + + superblock_flags(); + superblock_flags(uint32_t bits); + + void set_flag(flag f); + void clear_flag(flag f); + bool get_flag(flag f) const; + uint32_t encode() const; + uint32_t get_unhandled_flags() const; + + private: + uint32_t unhandled_flags_; + std::set flags_; + }; + + unsigned const SPACE_MAP_ROOT_SIZE = 128; + uint64_t const SUPERBLOCK_LOCATION = 0; + + struct superblock { + superblock(); + + uint32_t csum; + uint64_t blocknr; + superblock_flags flags; + + __u8 uuid[16]; // FIXME: do we really need this? + uint64_t magic; + uint32_t version; + + __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; + + uint32_t data_block_size; + uint32_t metadata_block_size; + uint32_t nr_blocks; + + uint32_t current_era; + era_detail current_detail; + + // A btree of undigested era_details + uint64_t era_root; + + // Big array holding the digested era/block info. + uint64_t era_array_root; + }; + + //-------------------------------- + + superblock read_superblock(persistent_data::block_manager<>::ptr bm, + persistent_data::block_address location = SUPERBLOCK_LOCATION); + + void write_superblock(persistent_data::block_manager<>::ptr bm, + superblock const &sb, + persistent_data::block_address location = SUPERBLOCK_LOCATION); +#if 0 + void check_superblock(superblock const &sb, + superblock_damage::damage_visitor &visitor); +#endif +} + +//---------------------------------------------------------------- + +#endif From 4df679174c3fc79a88fea75cc35acf30f07662cb Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 8 Jan 2014 11:04:56 +0000 Subject: [PATCH 010/165] correct a couple of #includes that were pointing to the old location of endian_utils.h --- unit-tests/btree_damage_visitor_t.cc | 2 +- unit-tests/endian_t.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unit-tests/btree_damage_visitor_t.cc b/unit-tests/btree_damage_visitor_t.cc index d6eb9c4..201d8e3 100644 --- a/unit-tests/btree_damage_visitor_t.cc +++ b/unit-tests/btree_damage_visitor_t.cc @@ -2,8 +2,8 @@ #include "test_utils.h" +#include "base/endian_utils.h" #include "persistent-data/data-structures/btree_damage_visitor.h" -#include "persistent-data/endian_utils.h" #include "persistent-data/space-maps/core.h" #include "persistent-data/transaction_manager.h" #include "persistent-data/run.h" diff --git a/unit-tests/endian_t.cc b/unit-tests/endian_t.cc index 9fbbc06..be75e63 100644 --- a/unit-tests/endian_t.cc +++ b/unit-tests/endian_t.cc @@ -17,7 +17,7 @@ // . #include "gmock/gmock.h" -#include "persistent-data/endian_utils.h" +#include "base/endian_utils.h" using namespace base; using namespace std; From 2250abe605d38af24d0b01ab6ef87cac1214dfd8 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 8 Jan 2014 15:31:42 +0000 Subject: [PATCH 011/165] [era] superblock packing --- Makefile.in | 1 + era/era_detail.cc | 11 ++++++++-- era/era_detail.h | 5 ++++- era/superblock.cc | 55 ++++++++++++++++++++++++++++++++++++++++++++--- era/superblock.h | 2 +- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/Makefile.in b/Makefile.in index e54fbd5..423025e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -48,6 +48,7 @@ SOURCE=\ caching/xml_format.cc \ \ era/era_detail.cc \ + era/superblock.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ diff --git a/era/era_detail.cc b/era/era_detail.cc index 43fbc51..7a88fcc 100644 --- a/era/era_detail.cc +++ b/era/era_detail.cc @@ -8,6 +8,7 @@ using namespace era; //---------------------------------------------------------------- namespace { +#if 0 le32 pack_hash_detail(uint32_t hash1, uint32_t hash2, uint32_t nr_probes) { throw std::runtime_error("not implemented"); } @@ -15,22 +16,28 @@ namespace { void unpack_hash_detail(le32 packed, uint32_t &hash1, uint32_t &hash2, uint32_t &nr_probes) { throw std::runtime_error("not implemented"); } +#endif } void era_detail_traits::unpack(disk_type const &disk, value_type &value) { + value.nr_blocks = to_cpu(disk.nr_blocks); value.nr_bits = to_cpu(disk.nr_bits); - unpack_hash_detail(disk.hash_fns_and_probes, value.hash1, value.hash2, value.nr_probes); + value.nr_set = to_cpu(disk.nr_set); value.bloom_root = to_cpu(disk.bloom_root); + + //unpack_hash_detail(disk.hash_fns_and_probes, value.hash1, value.hash2, value.nr_probes); } void era_detail_traits::pack(value_type const &value, disk_type &disk) { + disk.nr_blocks = to_disk(value.nr_blocks); disk.nr_bits = to_disk(value.nr_bits); - disk.hash_fns_and_probes = pack_hash_detail(value.hash1, value.hash2, value.nr_probes); + disk.nr_set = to_disk(value.nr_set); disk.bloom_root = to_disk(value.bloom_root); +// disk.hash_fns_and_probes = pack_hash_detail(value.hash1, value.hash2, value.nr_probes); } //---------------------------------------------------------------- diff --git a/era/era_detail.h b/era/era_detail.h index adaec57..718afe4 100644 --- a/era/era_detail.h +++ b/era/era_detail.h @@ -7,13 +7,16 @@ namespace era { struct era_detail_disk { + base::le32 nr_blocks; base::le32 nr_bits; - base::le32 hash_fns_and_probes; + base::le32 nr_set; base::le64 bloom_root; } __attribute__ ((packed)); struct era_detail { + uint32_t nr_blocks; uint32_t nr_bits; + uint32_t nr_set; uint32_t hash1; uint32_t hash2; diff --git a/era/superblock.cc b/era/superblock.cc index ed3ebb8..561d469 100644 --- a/era/superblock.cc +++ b/era/superblock.cc @@ -1,6 +1,7 @@ #include "era/superblock.h" #include "persistent-data/checksum.h" +#include "persistent-data/errors.h" using namespace base; using namespace era; @@ -11,15 +12,20 @@ using namespace persistent_data; namespace { using namespace base; + size_t const SPACE_MAP_ROOT_SIZE = 128; + size_t const UUID_LEN = 16; + struct superblock_disk { le32 csum; le32 flags; le64 blocknr; - __u8 uuid[16]; + __u8 uuid[UUID_LEN]; le64 magic; le32 version; + __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; + le32 data_block_size; le32 metadata_block_size; le32 nr_blocks; @@ -27,8 +33,9 @@ namespace { le32 current_era; era_detail_disk current_detail; - le64 bloom_filters_root; + le64 bloom_tree_root; le64 era_array_root; + } __attribute__ ((packed)); struct superblock_traits { @@ -56,7 +63,7 @@ superblock::superblock() metadata_block_size(8), nr_blocks(0), current_era(0), - era_root(0), + bloom_tree_root(0), era_array_root(0) { memset(uuid, 0, sizeof(uuid)); @@ -65,6 +72,48 @@ superblock::superblock() //---------------------------------------------------------------- +void +superblock_traits::unpack(disk_type const &disk, value_type &value) +{ + //value.flags = to_cpu(disk.flags); + value.blocknr = to_cpu(disk.blocknr); + value.magic = to_cpu(disk.magic); + value.version = to_cpu(disk.version); + + memcpy(value.metadata_space_map_root, disk.metadata_space_map_root, + sizeof(value.metadata_space_map_root)); + + value.data_block_size = to_cpu(disk.data_block_size); + value.metadata_block_size = to_cpu(disk.metadata_block_size); + value.nr_blocks = to_cpu(disk.nr_blocks); + value.current_era = to_cpu(disk.current_era); + era_detail_traits::unpack(disk.current_detail, value.current_detail); + value.bloom_tree_root = to_cpu(disk.bloom_tree_root); + value.era_array_root = to_cpu(disk.era_array_root); +} + +void +superblock_traits::pack(value_type const &value, disk_type &disk) +{ + //disk.flags = to_disk(value.flags); + disk.blocknr = to_disk(value.blocknr); + disk.magic = to_disk(value.magic); + disk.version = to_disk(value.version); + + memcpy(disk.metadata_space_map_root, value.metadata_space_map_root, + sizeof(disk.metadata_space_map_root)); + + disk.data_block_size = to_disk(value.data_block_size); + disk.metadata_block_size = to_disk(value.metadata_block_size); + disk.nr_blocks = to_disk(value.nr_blocks); + disk.current_era = to_disk(value.current_era); + era_detail_traits::pack(value.current_detail, disk.current_detail); + disk.bloom_tree_root = to_disk(value.bloom_tree_root); + disk.era_array_root = to_disk(value.era_array_root); +} + +//---------------------------------------------------------------- + namespace validator { using namespace persistent_data; diff --git a/era/superblock.h b/era/superblock.h index 5980daf..6d43e12 100644 --- a/era/superblock.h +++ b/era/superblock.h @@ -59,7 +59,7 @@ namespace era { era_detail current_detail; // A btree of undigested era_details - uint64_t era_root; + uint64_t bloom_tree_root; // Big array holding the digested era/block info. uint64_t era_array_root; From 81fa131748079e88a06a8c93cc47cb37b3d06a2c Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 8 Jan 2014 20:05:55 +0000 Subject: [PATCH 012/165] [cache_check] --super-block-only rather than --superblock-only Be consistent with help and thin_check --- caching/cache_check.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caching/cache_check.cc b/caching/cache_check.cc index 8f6ab7d..94ea86b 100644 --- a/caching/cache_check.cc +++ b/caching/cache_check.cc @@ -329,7 +329,7 @@ int main(int argc, char **argv) const char shortopts[] = "qhV"; const struct option longopts[] = { { "quiet", no_argument, NULL, 'q' }, - { "superblock-only", no_argument, NULL, 1 }, + { "super-block-only", no_argument, NULL, 1 }, { "skip-mappings", no_argument, NULL, 2 }, { "skip-hints", no_argument, NULL, 3 }, { "skip-discards", no_argument, NULL, 4 }, From 2db5e0265dccafd2e57cd0956d3007e9181e0776 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 8 Jan 2014 20:39:46 +0000 Subject: [PATCH 013/165] [cache_check features] rename a step --- features/cache_check.feature | 7 ++++--- features/step_definitions/cache_steps.rb | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/features/cache_check.feature b/features/cache_check.feature index 74fb0ba..a47274c 100644 --- a/features/cache_check.feature +++ b/features/cache_check.feature @@ -13,19 +13,19 @@ Feature: cache_check When I run `cache_check --help` Then it should pass - And usage to stdout + And cache_usage to stdout Scenario: print help When I run `cache_check -h` Then it should pass - And usage to stdout + And cache_usage to stdout Scenario: Metadata file must be specified When I run `cache_check` Then it should fail - And usage to stderr + And cache_usage to stderr And the stderr should contain: """ @@ -52,6 +52,7 @@ Feature: cache_check foo: Not a block device or regular file """ + # This test will fail if you're running as root Scenario: Metadata file exists, but can't be opened Given input without read permissions When I run `cache_check input` diff --git a/features/step_definitions/cache_steps.rb b/features/step_definitions/cache_steps.rb index 58217f4..81d6194 100644 --- a/features/step_definitions/cache_steps.rb +++ b/features/step_definitions/cache_steps.rb @@ -34,7 +34,7 @@ Then /^it should fail$/ do assert_success(false) end -USAGE =< Date: Thu, 9 Jan 2014 22:40:34 +0000 Subject: [PATCH 014/165] [era] era_superblock_t and era_check --- .gitignore | 2 + Makefile.in | 34 ++++ era/era_check.cc | 249 +++++++++++++++++++++++++ era/superblock.cc | 165 +++++++++++++++- era/superblock.h | 52 +++++- features/era_check.feature | 83 +++++++++ features/step_definitions/era_steps.rb | 16 ++ unit-tests/Makefile.in | 1 + unit-tests/era_superblock_t.cc | 112 +++++++++++ 9 files changed, 709 insertions(+), 5 deletions(-) create mode 100644 era/era_check.cc create mode 100644 features/era_check.feature create mode 100644 features/step_definitions/era_steps.rb create mode 100644 unit-tests/era_superblock_t.cc diff --git a/.gitignore b/.gitignore index 55164d5..7d541d7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ cache_dump cache_restore cache_repair +era_check + *.metadata bad-metadata Makefile diff --git a/Makefile.in b/Makefile.in index 423025e..82c785f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,6 +25,8 @@ PROGRAMS=\ cache_restore \ cache_repair \ \ + era_check \ + \ thin_check \ thin_dump \ thin_restore \ @@ -289,6 +291,38 @@ cache_restore: $(CACHE_RESTORE_OBJECTS) caching/cache_restore.o @echo " [LD] $@" $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) +#---------------------------------------------------------------- +# Era tools + +ERA_CHECK_SOURCE=\ + base/base64.cc \ + base/error_state.cc \ + base/endian_utils.cc \ + \ + era/superblock.cc \ + era/era_detail.cc \ + \ + persistent-data/checksum.cc \ + persistent-data/error_set.cc \ + persistent-data/file_utils.cc \ + persistent-data/hex_dump.cc \ + persistent-data/lock_tracker.cc \ + persistent-data/data-structures/btree.cc \ + persistent-data/data-structures/bitset.cc \ + persistent-data/space_map.cc \ + persistent-data/space-maps/disk.cc \ + persistent-data/space-maps/recursive.cc \ + persistent-data/space-maps/careful_alloc.cc \ + persistent-data/transaction_manager.cc \ + +ERA_CHECK_OBJECTS=$(subst .cc,.o,$(ERA_CHECK_SOURCE)) + +era_check: $(ERA_CHECK_OBJECTS) era/era_check.o + @echo " [LD] $@" + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) + +#---------------------------------------------------------------- + DEPEND_FILES=\ $(subst .cc,.d,$(SOURCE)) \ $(subst .cc,.d,$(TEST_SOURCE)) \ diff --git a/era/era_check.cc b/era/era_check.cc new file mode 100644 index 0000000..de73527 --- /dev/null +++ b/era/era_check.cc @@ -0,0 +1,249 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/error_state.h" +#include "base/nested_output.h" +#include "era/superblock.h" +#include "persistent-data/block.h" +#include "persistent-data/file_utils.h" +#include "persistent-data/space_map.h" +#include "persistent-data/space-maps/core.h" +#include "persistent-data/transaction_manager.h" +#include "version.h" + +using namespace base; +using namespace boost; +using namespace era; +using namespace persistent_data; +using namespace std; + +//---------------------------------------------------------------- + +namespace { + class reporter_base { + public: + reporter_base(nested_output &o) + : out_(o), + err_(NO_ERROR) { + } + + virtual ~reporter_base() {} + + nested_output &out() { + return out_; + } + + nested_output::nest push() { + return out_.push(); + } + + base::error_state get_error() const { + return err_; + } + + void mplus_error(error_state err) { + err_ = combine_errors(err_, err); + } + + private: + nested_output &out_; + error_state err_; + }; + + class superblock_reporter : public superblock_damage::damage_visitor, reporter_base { + public: + superblock_reporter(nested_output &o) + : reporter_base(o) { + } + + virtual void visit(superblock_damage::superblock_corrupt const &d) { + out() << "superblock is corrupt" << end_message(); + { + nested_output::nest _ = push(); + 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); + } + + using reporter_base::get_error; + }; + + //-------------------------------- + + transaction_manager::ptr open_tm(block_manager<>::ptr bm) { + space_map::ptr sm(new core_map(bm->get_nr_blocks())); + sm->inc(SUPERBLOCK_LOCATION); + transaction_manager::ptr tm(new transaction_manager(bm, sm)); + return tm; + } + + //-------------------------------- + + struct flags { + flags() + : superblock_only_(false), + quiet_(false) { + } + + bool superblock_only_; + bool quiet_; + }; + + struct stat guarded_stat(string const &path) { + struct stat info; + + int r = ::stat(path.c_str(), &info); + if (r) { + ostringstream msg; + char buffer[128], *ptr; + + ptr = ::strerror_r(errno, buffer, sizeof(buffer)); + msg << path << ": " << ptr; + throw runtime_error(msg.str()); + } + + return info; + } + + error_state metadata_check(block_manager<>::ptr bm, flags const &fs) { + nested_output out(cerr, 2); + if (fs.quiet_) + out.disable(); + + superblock_reporter sb_rep(out); + + out << "examining superblock" << end_message(); + { + nested_output::nest _ = out.push(); + check_superblock(bm, bm->get_nr_blocks(), sb_rep); + } + + if (sb_rep.get_error() == FATAL) + return FATAL; + +#if 0 + superblock sb = read_superblock(bm); + transaction_manager::ptr tm = open_tm(bm); + + // FIXME: make an error class that's an instance of mplus + return combine_errors(sb_rep.get_error(), + combine_errors(mapping_rep.get_error(), + combine_errors(hint_rep.get_error(), + discard_rep.get_error()))); +#endif + + return FATAL; + } + + int check(string const &path, flags const &fs) { + error_state err; + struct stat info = guarded_stat(path); + + if (!S_ISREG(info.st_mode) && !S_ISBLK(info.st_mode)) { + ostringstream msg; + msg << path << ": " << "Not a block device or regular file"; + throw runtime_error(msg.str()); + } + + block_manager<>::ptr bm = open_bm(path, block_io<>::READ_ONLY); + err = metadata_check(bm, fs); + + return err == NO_ERROR ? 0 : 1; + } + + int check_with_exception_handling(string const &path, flags const &fs) { + int r; + try { + r = check(path, fs); + + } catch (std::exception &e) { + if (!fs.quiet_) + cerr << e.what() << endl; + r = 1; + } + + return r; + + } + + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options] {device|file}" << endl + << "Options:" << endl + << " {-q|--quiet}" << endl + << " {-h|--help}" << endl + << " {-V|--version}" << endl + << " {--super-block-only}" << endl; + } +} + +//---------------------------------------------------------------- + +int main(int argc, char **argv) +{ + int c; + flags fs; + const char shortopts[] = "qhV"; + const struct option longopts[] = { + { "quiet", no_argument, NULL, 'q' }, + { "super-block-only", no_argument, NULL, 1 }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, no_argument, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch(c) { + case 1: + fs.superblock_only_ = true; + break; + + case 'h': + usage(cout, basename(argv[0])); + return 0; + + case 'q': + fs.quiet_ = true; + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; + } + } + + if (argc == optind) { + cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); + return 1; + } + + return check_with_exception_handling(argv[optind], fs); +} + +//---------------------------------------------------------------- diff --git a/era/superblock.cc b/era/superblock.cc index 561d469..99e51e0 100644 --- a/era/superblock.cc +++ b/era/superblock.cc @@ -5,6 +5,7 @@ using namespace base; using namespace era; +using namespace superblock_damage; using namespace persistent_data; //---------------------------------------------------------------- @@ -53,6 +54,58 @@ namespace { //---------------------------------------------------------------- +superblock_flags::superblock_flags() + : unhandled_flags_(0) +{ +} + +superblock_flags::superblock_flags(uint32_t bits) +{ + if (bits & (1 << CLEAN_SHUTDOWN_BIT)) { + flags_.insert(CLEAN_SHUTDOWN); + bits &= ~(1 << CLEAN_SHUTDOWN_BIT); + } + + unhandled_flags_ = bits; +} + +void +superblock_flags::set_flag(flag f) +{ + flags_.insert(f); +} + +void +superblock_flags::clear_flag(flag f) +{ + flags_.erase(f); +} + +bool +superblock_flags::get_flag(flag f) const +{ + return flags_.find(f) != flags_.end(); +} + +uint32_t +superblock_flags::encode() const +{ + uint32_t r = 0; + + if (get_flag(CLEAN_SHUTDOWN)) + r = r | (1 << CLEAN_SHUTDOWN_BIT); + + return r; +} + +uint32_t +superblock_flags::get_unhandled_flags() const +{ + return unhandled_flags_; +} + +//---------------------------------------------------------------- + superblock::superblock() : csum(0), blocknr(0), @@ -112,9 +165,33 @@ superblock_traits::pack(value_type const &value, disk_type &disk) disk.era_array_root = to_disk(value.era_array_root); } +//-------------------------------- + +superblock_corrupt::superblock_corrupt(std::string const &desc) + : damage(desc) +{ +} + +void +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); +} + //---------------------------------------------------------------- -namespace validator { +namespace era_validator { using namespace persistent_data; uint32_t const VERSION = 1; @@ -150,7 +227,7 @@ superblock era::read_superblock(block_manager<>::ptr bm, block_address location) { superblock sb; - block_manager<>::read_ref r = bm->read_lock(location, validator::mk_v()); + block_manager<>::read_ref r = bm->read_lock(location, era_validator::mk_v()); superblock_disk const *sbd = reinterpret_cast(&r.data()); superblock_traits::unpack(*sbd, sb); @@ -160,8 +237,90 @@ era::read_superblock(block_manager<>::ptr bm, block_address location) void era::write_superblock(block_manager<>::ptr bm, superblock const &sb, block_address location) { - block_manager<>::write_ref w = bm->superblock_zero(location, validator::mk_v()); + block_manager<>::write_ref w = bm->superblock_zero(location, era_validator::mk_v()); superblock_traits::pack(sb, *reinterpret_cast(w.data().raw())); } +void +era::check_superblock(superblock const &sb, + block_address nr_metadata_blocks, + damage_visitor &visitor) +{ + if (sb.flags.get_unhandled_flags()) { + ostringstream msg; + msg << "invalid flags: " << sb.flags.get_unhandled_flags(); + visitor.visit(superblock_invalid(msg.str())); + } + + if (sb.blocknr >= nr_metadata_blocks) { + ostringstream msg; + msg << "blocknr out of bounds: " << sb.blocknr << " >= " << nr_metadata_blocks; + visitor.visit(superblock_invalid(msg.str())); + } + + if (sb.magic != SUPERBLOCK_MAGIC) { + ostringstream msg; + msg << "magic in incorrect: " << sb.magic; + visitor.visit(superblock_invalid(msg.str())); + } + + 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 (sb.metadata_block_size != 8) { + ostringstream msg; + msg << "metadata block size incorrect: " << sb.metadata_block_size; + visitor.visit(superblock_invalid(msg.str())); + } + + if (sb.bloom_tree_root == SUPERBLOCK_LOCATION) { + string msg("bloom tree root points back to the superblock"); + visitor.visit(superblock_invalid(msg)); + } + + if (sb.era_array_root == SUPERBLOCK_LOCATION) { + string msg("era array root points back to the superblock"); + visitor.visit(superblock_invalid(msg)); + } + + if (sb.bloom_tree_root == sb.era_array_root) { + ostringstream msg; + msg << "bloom tree root and era array both point to the same block: " + << sb.era_array_root; + visitor.visit(superblock_invalid(msg.str())); + } +} + +void +era::check_superblock(persistent_data::block_manager<>::ptr bm, + block_address nr_metadata_blocks, + damage_visitor &visitor) +{ + superblock sb; + + try { + sb = read_superblock(bm, SUPERBLOCK_LOCATION); + + } catch (std::exception const &e) { + + // 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); +} + + //---------------------------------------------------------------- diff --git a/era/superblock.h b/era/superblock.h index 6d43e12..2ff1179 100644 --- a/era/superblock.h +++ b/era/superblock.h @@ -67,16 +67,64 @@ namespace era { //-------------------------------- + namespace superblock_damage { + + class damage_visitor; + + class damage { + public: + damage(std::string const &desc) + : desc_(desc) { + } + + virtual ~damage() {} + virtual void visit(damage_visitor &v) const = 0; + + std::string const &get_desc() const { + return desc_; + } + + private: + std::string desc_; + }; + + struct superblock_corrupt : public damage { + superblock_corrupt(std::string const &desc); + void visit(damage_visitor &v) const; + }; + + struct superblock_invalid : public damage { + superblock_invalid(std::string const &desc); + void visit(damage_visitor &v) const; + }; + + class damage_visitor { + public: + virtual ~damage_visitor() {} + + void visit(damage const &d); + + virtual void visit(superblock_corrupt const &d) = 0; + virtual void visit(superblock_invalid const &d) = 0; + }; + } + + //-------------------------------- + superblock read_superblock(persistent_data::block_manager<>::ptr bm, persistent_data::block_address location = SUPERBLOCK_LOCATION); void write_superblock(persistent_data::block_manager<>::ptr bm, superblock const &sb, persistent_data::block_address location = SUPERBLOCK_LOCATION); -#if 0 + 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, + persistent_data::block_address nr_metadata_blocks, superblock_damage::damage_visitor &visitor); -#endif } //---------------------------------------------------------------- diff --git a/features/era_check.feature b/features/era_check.feature new file mode 100644 index 0000000..430c702 --- /dev/null +++ b/features/era_check.feature @@ -0,0 +1,83 @@ +Feature: era_check + Scenario: print version (-V flag) + When I run `era_check -V` + + Then it should pass with version + + Scenario: print version (--version flag) + When I run `era_check --version` + + Then it should pass with version + + Scenario: print help + When I run `era_check --help` + + Then it should pass + And era_usage to stdout + + Scenario: print help + When I run `era_check -h` + + Then it should pass + And era_usage to stdout + + Scenario: Metadata file must be specified + When I run `era_check` + + Then it should fail + And era_usage to stderr + And the stderr should contain: + + """ + No input file provided. + """ + + Scenario: Metadata file doesn't exist + When I run `era_check /arbitrary/filename` + + Then it should fail + And the stderr should contain: + """ + /arbitrary/filename: No such file or directory + """ + + Scenario: Metadata file cannot be a directory + Given a directory called foo + + When I run `era_check foo` + + Then it should fail + And the stderr should contain: + """ + foo: Not a block device or regular file + """ + + # This test will fail if you're running as root + Scenario: Metadata file exists, but can't be opened + Given input without read permissions + When I run `era_check input` + Then it should fail + And the stderr should contain: + """ + Permission denied + """ + + Scenario: Metadata file full of zeroes + Given input file + And block 1 is zeroed + When I run `era_check input` + Then it should fail + + Scenario: --quiet is observed + Given input file + And block 1 is zeroed + When I run `era_check --quiet input` + Then it should fail + And it should give no output + + Scenario: -q is observed + Given input file + And block 1 is zeroed + When I run `era_check -q input` + Then it should fail + And it should give no output diff --git a/features/step_definitions/era_steps.rb b/features/step_definitions/era_steps.rb new file mode 100644 index 0000000..a1e9f29 --- /dev/null +++ b/features/step_definitions/era_steps.rb @@ -0,0 +1,16 @@ +ERA_USAGE =<(_))).Times(1); + check(); + } + + damage_visitor_mock visitor_; + superblock sb_; + }; +} + +//---------------------------------------------------------------- + +TEST_F(EraSuperblockTests, default_constructed_superblock_is_valid) +{ + check(); +} + +TEST_F(EraSuperblockTests, clean_shutdown_flag_is_valid) +{ + sb_.flags.set_flag(superblock_flags::CLEAN_SHUTDOWN); + check(); +} + +TEST_F(EraSuperblockTests, unhandled_flags_get_set_correctly_and_is_invalid) +{ + uint32_t bad_flag = 1 << 12; + sb_.flags = superblock_flags(bad_flag | 1); + ASSERT_THAT(sb_.flags.get_unhandled_flags(), Eq(bad_flag)); + check_invalid(); +} + +TEST_F(EraSuperblockTests, blocknr_is_in_range) +{ + sb_.blocknr = NR_METADATA_BLOCKS; + check_invalid(); +} + +TEST_F(EraSuperblockTests, magic_is_checked) +{ + sb_.magic = 12345; + check_invalid(); +} + +TEST_F(EraSuperblockTests, version_gt_1_is_checked) +{ + sb_.version = 2; + check_invalid(); +} + +TEST_F(EraSuperblockTests, version_lt_1_is_checked) +{ + sb_.version = 0; + check_invalid(); +} + +TEST_F(EraSuperblockTests, metadata_block_size_checked) +{ + sb_.metadata_block_size = 16; + check_invalid(); +} + +TEST_F(EraSuperblockTests, bloom_tree_root_isnt_0) +{ + sb_.bloom_tree_root = 0; + check_invalid(); +} + +TEST_F(EraSuperblockTests, era_array_root_isnt_0) +{ + sb_.era_array_root = 0; + check_invalid(); +} + +TEST_F(EraSuperblockTests, bloom_root_isnt_era_array_root) +{ + sb_.bloom_tree_root = 10; + sb_.era_array_root = 10; + check_invalid(); +} + +//---------------------------------------------------------------- From 41d25aa9e465de6490f1bd6f6d314689509c7c1f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 13 Jan 2014 13:58:38 +0000 Subject: [PATCH 015/165] [era_check] Was always returning FATAL --- era/era_check.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/era/era_check.cc b/era/era_check.cc index de73527..5b143f5 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -143,6 +143,8 @@ namespace { if (sb_rep.get_error() == FATAL) return FATAL; + return sb_rep.get_error(); + #if 0 superblock sb = read_superblock(bm); transaction_manager::ptr tm = open_tm(bm); @@ -153,8 +155,6 @@ namespace { combine_errors(hint_rep.get_error(), discard_rep.get_error()))); #endif - - return FATAL; } int check(string const &path, flags const &fs) { From bed0f369a8ad0bb7ee5a1a0495398e612092ba62 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 14 Jan 2014 23:59:12 +0000 Subject: [PATCH 016/165] [era_check] Now checks bloom tree --- Makefile.in | 2 + era/bloom_tree.cc | 126 +++++++++++++++++++++++++++++++++ era/bloom_tree.h | 174 +++++++++++++++++++--------------------------- era/era_check.cc | 47 +++++++++++-- era/era_detail.h | 9 +++ 5 files changed, 251 insertions(+), 107 deletions(-) create mode 100644 era/bloom_tree.cc diff --git a/Makefile.in b/Makefile.in index 82c785f..6d086a1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -51,6 +51,7 @@ SOURCE=\ \ era/era_detail.cc \ era/superblock.cc \ + era/bloom_tree.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ @@ -299,6 +300,7 @@ ERA_CHECK_SOURCE=\ base/error_state.cc \ base/endian_utils.cc \ \ + era/bloom_tree.cc \ era/superblock.cc \ era/era_detail.cc \ \ diff --git a/era/bloom_tree.cc b/era/bloom_tree.cc new file mode 100644 index 0000000..022d447 --- /dev/null +++ b/era/bloom_tree.cc @@ -0,0 +1,126 @@ +#include "era/bloom_tree.h" +#include "persistent-data/data-structures/btree_damage_visitor.h" +#include "persistent-data/data-structures/bitset.h" + +using namespace boost; +using namespace era; +using namespace bloom_tree_detail; +using namespace persistent_data; +using namespace std; + +//---------------------------------------------------------------- + +missing_eras::missing_eras(string const &desc, + run const &eras) + : damage(desc), + eras_(eras) +{ +} + +void +missing_eras::visit(damage_visitor &v) const { + v.visit(*this); +} + +damaged_bloom_filter::damaged_bloom_filter(string const &desc, + uint32_t era, + run missing_bits) + : damage(desc), + era_(era), + missing_bits_(missing_bits) +{ +} + +void +damaged_bloom_filter::visit(damage_visitor &v) const +{ + v.visit(*this); +} + +//---------------------------------------------------------------- + +namespace { + class ll_bloom_visitor : public bitset_detail::bitset_visitor { + public: + typedef persistent_data::transaction_manager::ptr tm_ptr; + + ll_bloom_visitor(tm_ptr tm, + bloom_tree_detail::bloom_visitor &bloom_v, + bloom_tree_detail::damage_visitor &dv) + : tm_(tm), + bloom_v_(bloom_v), + dv_(dv) { + } + + void visit(btree_path const &path, era_detail const &era) { + era_ = path[0]; + bitset bs(tm_, era.bloom_root, era.nr_bits); + bs.walk_bitset(*this); + } + + void visit(uint32_t index, bool value) { + bloom_v_.visit(index, value); + } + + void visit(bitset_detail::missing_bits const &d) { + dv_.visit(bloom_tree_detail::damaged_bloom_filter("missing bits", era_, d.keys_)); + } + + private: + tm_ptr tm_; + uint64_t era_; + bloom_tree_detail::bloom_visitor &bloom_v_; + bloom_tree_detail::damage_visitor &dv_; + }; + + class ll_damage_visitor { + public: + ll_damage_visitor(damage_visitor &v) + : v_(v) { + } + + virtual void visit(btree_path const &path, + btree_detail::damage const &d) { + v_.visit(missing_eras(d.desc_, to_uint32(d.lost_keys_))); + } + + private: + template + run to_uint32(run const &r) { + return run(optional(r.begin_), + optional(r.end_)); + } + + damage_visitor &v_; + }; +} + +void +era::walk_bloom_tree(persistent_data::transaction_manager::ptr tm, + bloom_tree const &tree, + bloom_tree_detail::bloom_visitor &bloom_v, + bloom_tree_detail::damage_visitor &dv) +{ + ll_bloom_visitor ll_bv(tm, bloom_v, dv); + ll_damage_visitor ll_dv(dv); + btree_visit_values(tree, ll_bv, ll_dv); +} + +namespace { + class noop_bloom_visitor : public bloom_tree_detail::bloom_visitor { + public: + void visit(uint32_t index, bool value) { + } + }; +}; + +void +era::check_bloom_tree(persistent_data::transaction_manager::ptr tm, + bloom_tree const &tree, + bloom_tree_detail::damage_visitor &dv) +{ + noop_bloom_visitor bv; + walk_bloom_tree(tm, tree, bv, dv); +} + +//---------------------------------------------------------------- diff --git a/era/bloom_tree.h b/era/bloom_tree.h index e98ed37..8d89a96 100644 --- a/era/bloom_tree.h +++ b/era/bloom_tree.h @@ -1,112 +1,84 @@ #ifndef ERA_BLOOM_TREE_H #define ERA_BLOOM_TREE_H +#include "era/era_detail.h" +#include "persistent-data/data-structures/btree.h" + //---------------------------------------------------------------- namespace era { + namespace bloom_tree_detail { + class damage_visitor; + + class damage { + public: + damage(std::string const &desc) + : desc_(desc) { + } + + virtual ~damage() {} + virtual void visit(damage_visitor &v) const = 0; + + std::string const &get_desc() const { + return desc_; + } + + private: + std::string desc_; + }; + + struct missing_eras : public damage { + missing_eras(std::string const &desc, run const &eras); + virtual void visit(damage_visitor &v) const; + + run eras_; + }; + + struct damaged_bloom_filter : public damage { + damaged_bloom_filter(std::string const &desc, + uint32_t era, + run missing_bits); + virtual void visit(damage_visitor &v) const; + + uint32_t era_; + run missing_bits_; + }; + + class damage_visitor { + public: + typedef boost::shared_ptr ptr; + + virtual ~damage_visitor() {} + + void visit(damage const &d) { + d.visit(*this); + } + + virtual void visit(missing_eras const &d) = 0; + virtual void visit(damaged_bloom_filter const &d) = 0; + }; + + class bloom_visitor { + public: + typedef boost::shared_ptr ptr; + + virtual ~bloom_visitor() {} + virtual void visit(uint32_t index, bool value) = 0; + }; + } + + typedef persistent_data::btree<1, era_detail_traits> bloom_tree; + + void walk_bloom_tree(persistent_data::transaction_manager::ptr tm, + bloom_tree const &tree, + bloom_tree_detail::bloom_visitor &bloom_v, + bloom_tree_detail::damage_visitor &dv); + + void check_bloom_tree(persistent_data::transaction_manager::ptr tm, + bloom_tree const &tree, + bloom_tree_detail::damage_visitor &dv); } //---------------------------------------------------------------- #endif - - -#if 0 - class dm_era { - public: - dm_era(block_address nr_blocks) - : nr_blocks_(nr_blocks), - era_base_(0), - base_(nr_blocks, false) { - } - - set blocks_written_since(unsigned era) const { - - } - - unsigned get_era() const { - return era_base_ + eras_.size() - 1; - } - - void record_write(block_address b) { - current_era.record_write(b); - } - - void resize(block_address new_size) { - nr_blocks_ = new_size; - push_era(); - base_.resize(new_size, false); - } - - private: - era_details ¤t_era() { - return eras_.back(); - } - - void need_new_era() { - // ??? - } - - void push_era() { - eras_.push_back(era(nr_blocks_)); - if (eras_.size() > 100) - pop_era(); - } - - void pop_era() { - era_base_++; - - - - eras_.pop_front(); - } - - static const unsigned NR_PROBES = 6; - - class era_details { - public: - era_details(block_address nr_blocks) - : nr_blocks_(nr_blocks), - f(power_bits(nr_blocks, NR_PROBES)) { - } - - void record_write(block_address b) { - f.add(b); - } - - void add_blocks_written(set filter; - - block_address nr_blocks; - filter f; - }; - - space_map::ptr setup_core_map() { - space_map::ptr sm(new core_map(NR_BLOCKS)); - sm->inc(SUPERBLOCK); - return sm; - } - - block_address nr_blocks_; - unsigned era_base_; - vector base_; - deque eras_; - }; -#endif diff --git a/era/era_check.cc b/era/era_check.cc index 5b143f5..991da05 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -14,6 +14,7 @@ #include "base/error_state.h" #include "base/nested_output.h" +#include "era/bloom_tree.h" #include "era/superblock.h" #include "persistent-data/block.h" #include "persistent-data/file_utils.h" @@ -90,6 +91,40 @@ namespace { using reporter_base::get_error; }; + class bloom_tree_reporter : public bloom_tree_detail::damage_visitor, reporter_base { + public: + bloom_tree_reporter(nested_output &o) + : reporter_base(o) { + } + + void visit(bloom_tree_detail::missing_eras const &d) { + out() << "missing eras" << end_message(); + { + nested_output::nest _ = push(); + out() << d.get_desc() << end_message(); + out() << "Effected eras: [" << d.eras_.begin_.get() + << ", " << d.eras_.end_.get() << ")" << end_message(); + } + + mplus_error(FATAL); + } + + void visit(bloom_tree_detail::damaged_bloom_filter const &d) { + out() << "damaged bloom filter" << end_message(); + { + nested_output::nest _ = push(); + out() << d.get_desc() << end_message(); + out() << "Era: " << d.era_ << end_message(); + out() << "Missing bits: [" << d.missing_bits_.begin_.get() + << ", " << d.missing_bits_.end_.get() << ")" << end_message(); + } + + mplus_error(FATAL); + } + + using reporter_base::get_error; + }; + //-------------------------------- transaction_manager::ptr open_tm(block_manager<>::ptr bm) { @@ -145,16 +180,16 @@ namespace { return sb_rep.get_error(); -#if 0 superblock sb = read_superblock(bm); transaction_manager::ptr tm = open_tm(bm); - // FIXME: make an error class that's an instance of mplus + bloom_tree_reporter bt_rep(out); + era_detail_traits::ref_counter rc(tm); + bloom_tree bt(tm, rc); + check_bloom_tree(tm, bt, bt_rep); + return combine_errors(sb_rep.get_error(), - combine_errors(mapping_rep.get_error(), - combine_errors(hint_rep.get_error(), - discard_rep.get_error()))); -#endif + bt_rep.get_error()); } int check(string const &path, flags const &fs) { diff --git a/era/era_detail.h b/era/era_detail.h index 718afe4..7afaaaf 100644 --- a/era/era_detail.h +++ b/era/era_detail.h @@ -2,6 +2,7 @@ #define ERA_DETAIL_H #include "base/endian_utils.h" +#include "persistent-data/transaction_manager.h" //---------------------------------------------------------------- @@ -25,9 +26,17 @@ namespace era { uint64_t bloom_root; }; + struct era_detail_ref_counter { + era_detail_ref_counter(persistent_data::transaction_manager::ptr tm); + + void inc(persistent_data::block_address b); + void dec(persistent_data::block_address b); + }; + struct era_detail_traits { typedef era_detail_disk disk_type; typedef era_detail value_type; + typedef era_detail_ref_counter ref_counter; static void unpack(disk_type const &disk, value_type &value); static void pack(value_type const &value, disk_type &disk); From 9e0540e1b60fdb6c511ede0fae552fab18c047d4 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 23 Jan 2014 00:46:03 +0000 Subject: [PATCH 017/165] era_check, era_dump --- Makefile.in | 41 +++++- caching/metadata_dump.cc | 2 +- era/bloom_tree.cc | 13 +- era/bloom_tree.h | 6 +- era/emitter.h | 35 +++++ era/era_array.cc | 87 +++++++++++ era/era_array.h | 78 ++++++++++ era/era_check.cc | 53 ++++++- era/era_detail.h | 11 +- era/era_dump.cc | 118 +++++++++++++++ era/metadata.cc | 60 ++++++++ era/metadata.h | 47 ++++++ era/metadata_dump.cc | 135 ++++++++++++++++++ era/metadata_dump.h | 15 ++ era/xml_format.cc | 103 +++++++++++++ era/xml_format.h | 17 +++ persistent-data/data-structures/bitset.cc | 4 +- persistent-data/data-structures/btree.h | 16 --- .../data-structures/simple_traits.h | 38 +++++ 19 files changed, 849 insertions(+), 30 deletions(-) create mode 100644 era/emitter.h create mode 100644 era/era_array.cc create mode 100644 era/era_array.h create mode 100644 era/era_dump.cc create mode 100644 era/metadata.cc create mode 100644 era/metadata.h create mode 100644 era/metadata_dump.cc create mode 100644 era/metadata_dump.h create mode 100644 era/xml_format.cc create mode 100644 era/xml_format.h create mode 100644 persistent-data/data-structures/simple_traits.h diff --git a/Makefile.in b/Makefile.in index 6d086a1..f199a80 100644 --- a/Makefile.in +++ b/Makefile.in @@ -26,6 +26,7 @@ PROGRAMS=\ cache_repair \ \ era_check \ + era_dump \ \ thin_check \ thin_dump \ @@ -49,9 +50,13 @@ SOURCE=\ caching/restore_emitter.cc \ caching/xml_format.cc \ \ + era/era_array.cc \ era/era_detail.cc \ era/superblock.cc \ era/bloom_tree.cc \ + era/metadata.cc \ + era/metadata_dump.cc \ + era/xml_format.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ @@ -301,8 +306,10 @@ ERA_CHECK_SOURCE=\ base/endian_utils.cc \ \ era/bloom_tree.cc \ - era/superblock.cc \ era/era_detail.cc \ + era/era_array.cc \ + era/metadata.cc \ + era/superblock.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ @@ -323,6 +330,38 @@ era_check: $(ERA_CHECK_OBJECTS) era/era_check.o @echo " [LD] $@" $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) +ERA_DUMP_SOURCE=\ + base/base64.cc \ + base/error_state.cc \ + base/endian_utils.cc \ + \ + era/bloom_tree.cc \ + era/era_detail.cc \ + era/era_array.cc \ + era/metadata.cc \ + era/metadata_dump.cc \ + era/superblock.cc \ + era/xml_format.cc \ + \ + persistent-data/checksum.cc \ + persistent-data/error_set.cc \ + persistent-data/file_utils.cc \ + persistent-data/hex_dump.cc \ + persistent-data/lock_tracker.cc \ + persistent-data/data-structures/btree.cc \ + persistent-data/data-structures/bitset.cc \ + persistent-data/space_map.cc \ + persistent-data/space-maps/disk.cc \ + persistent-data/space-maps/recursive.cc \ + persistent-data/space-maps/careful_alloc.cc \ + persistent-data/transaction_manager.cc \ + +ERA_DUMP_OBJECTS=$(subst .cc,.o,$(ERA_DUMP_SOURCE)) + +era_dump: $(ERA_DUMP_OBJECTS) era/era_dump.o + @echo " [LD] $@" + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) + #---------------------------------------------------------------- DEPEND_FILES=\ diff --git a/caching/metadata_dump.cc b/caching/metadata_dump.cc index 2f66766..0b50145 100644 --- a/caching/metadata_dump.cc +++ b/caching/metadata_dump.cc @@ -15,7 +15,7 @@ namespace { void raise_metadata_damage() { throw std::runtime_error("metadata contains errors (run cache_check for details).\n" - "perhaps you wanted to run with --repair"); + "perhaps you wanted to run with --repair ?"); } //-------------------------------- diff --git a/era/bloom_tree.cc b/era/bloom_tree.cc index 022d447..b49e533 100644 --- a/era/bloom_tree.cc +++ b/era/bloom_tree.cc @@ -55,11 +55,13 @@ namespace { void visit(btree_path const &path, era_detail const &era) { era_ = path[0]; bitset bs(tm_, era.bloom_root, era.nr_bits); + bloom_v_.bloom_begin(era_, era.nr_blocks, era.nr_bits, era.nr_set); bs.walk_bitset(*this); + bloom_v_.bloom_end(); } void visit(uint32_t index, bool value) { - bloom_v_.visit(index, value); + bloom_v_.bit(index, value); } void visit(bitset_detail::missing_bits const &d) { @@ -109,7 +111,14 @@ era::walk_bloom_tree(persistent_data::transaction_manager::ptr tm, namespace { class noop_bloom_visitor : public bloom_tree_detail::bloom_visitor { public: - void visit(uint32_t index, bool value) { + void bloom_begin(uint32_t era, uint32_t nr_blocks, + uint32_t nr_bits, uint32_t nr_set) { + } + + void bit(uint32_t index, bool value) { + } + + void bloom_end() { } }; }; diff --git a/era/bloom_tree.h b/era/bloom_tree.h index 8d89a96..ad4bbd6 100644 --- a/era/bloom_tree.h +++ b/era/bloom_tree.h @@ -63,7 +63,11 @@ namespace era { typedef boost::shared_ptr ptr; virtual ~bloom_visitor() {} - virtual void visit(uint32_t index, bool value) = 0; + + virtual void bloom_begin(uint32_t era, uint32_t nr_blocks, + uint32_t nr_bits, uint32_t nr_set) = 0; + virtual void bit(uint32_t index, bool value) = 0; + virtual void bloom_end() = 0; }; } diff --git a/era/emitter.h b/era/emitter.h new file mode 100644 index 0000000..0fb7520 --- /dev/null +++ b/era/emitter.h @@ -0,0 +1,35 @@ +#ifndef ERA_EMITTER_H +#define ERA_EMITTER_H + +#include "persistent-data/block.h" + +//---------------------------------------------------------------- + +namespace era { + namespace pd = persistent_data; + + class emitter { + public: + typedef boost::shared_ptr ptr; + + virtual ~emitter() {} + + virtual void begin_superblock(std::string const &uuid, + uint32_t data_block_size, + pd::block_address nr_blocks, + uint32_t current_era) = 0; + virtual void end_superblock() = 0; + + virtual void begin_bloom(uint32_t era, uint32_t nr_bits, pd::block_address nr_blocks) = 0; + virtual void bloom_bit(uint32_t bit, bool value) = 0; + virtual void end_bloom() = 0; + + virtual void begin_era_array() = 0; + virtual void era(pd::block_address block, uint32_t era) = 0; + virtual void end_era_array() = 0; + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/era/era_array.cc b/era/era_array.cc new file mode 100644 index 0000000..72ef82c --- /dev/null +++ b/era/era_array.cc @@ -0,0 +1,87 @@ +#include "era/era_array.h" + +using namespace era; +using namespace era_array_detail; +using namespace std; + +//---------------------------------------------------------------- + +missing_eras::missing_eras(string const &desc, run const &eras) + : damage(desc), + eras_(eras) +{ +} + +void +missing_eras::visit(damage_visitor &v) const +{ + v.visit(*this); +} + +invalid_era::invalid_era(string const &desc, block_address block, uint32_t era) + : damage(desc), + block_(block), + era_(era) +{ +} + +void +invalid_era::visit(damage_visitor &v) const +{ + v.visit(*this); +} + +//---------------------------------------------------------------- + +namespace { + class check_era_visitor : public era_array_visitor { + public: + check_era_visitor(damage_visitor &visitor, uint32_t current_era) + : visitor_(visitor), + current_era_(current_era) { + } + + virtual void visit(uint32_t cblock, uint32_t era) { + if (era > current_era_) + visitor_.visit(invalid_era("era too great", cblock, era)); + } + + private: + damage_visitor &visitor_; + uint32_t current_era_; + }; + + class ll_damage_visitor { + public: + ll_damage_visitor(damage_visitor &v) + : v_(v) { + } + + virtual void visit(array_detail::damage const &d) { + v_.visit(missing_eras(d.desc_, d.lost_keys_)); + } + + private: + damage_visitor &v_; + }; +} + +void +era::walk_era_array(era_array const &array, + era_array_visitor &ev, + era_array_detail::damage_visitor &dv) +{ + ll_damage_visitor ll(dv); + array.visit_values(ev, ll); +} + +void +era::check_era_array(era_array const &array, + uint32_t current_era, + era_array_detail::damage_visitor &dv) +{ + check_era_visitor cv(dv, current_era); + walk_era_array(array, cv, dv); +} + +//---------------------------------------------------------------- diff --git a/era/era_array.h b/era/era_array.h new file mode 100644 index 0000000..49d7aca --- /dev/null +++ b/era/era_array.h @@ -0,0 +1,78 @@ +#ifndef ERA_ARRAY_H +#define ERA_ARRAY_H + +#include "persistent-data/data-structures/array.h" +#include "persistent-data/data-structures/simple_traits.h" + +//---------------------------------------------------------------- + +namespace era { + namespace era_array_detail { + class damage_visitor; + + class damage { + public: + damage(std::string const &desc) + : desc_(desc) { + } + + virtual ~damage() {} + virtual void visit(damage_visitor &v) const = 0; + + std::string get_desc() const { + return desc_; + } + + private: + std::string desc_; + }; + + struct missing_eras : public damage { + missing_eras(std::string const &desc, run const &eras); + virtual void visit(damage_visitor &v) const; + + run eras_; + }; + + struct invalid_era : public damage { + invalid_era(std::string const &desc, block_address block, uint32_t era); + virtual void visit(damage_visitor &v) const; + + block_address block_; + uint32_t era_; + }; + + class damage_visitor { + public: + virtual ~damage_visitor() {} + + void visit(era_array_detail::damage const &d) { + d.visit(*this); + } + + virtual void visit(missing_eras const &d) = 0; + virtual void visit(invalid_era const &d) = 0; + }; + } + + typedef persistent_data::array era_array; + + class era_array_visitor { + public: + virtual ~era_array_visitor() {} + + virtual void visit(uint32_t index, uint32_t era) = 0; + }; + + void walk_era_array(era_array const &array, + era_array_visitor &ev, + era_array_detail::damage_visitor &dv); + + void check_era_array(era_array const &array, + uint32_t current_era, + era_array_detail::damage_visitor &dv); +} + +//---------------------------------------------------------------- + +#endif diff --git a/era/era_check.cc b/era/era_check.cc index 991da05..de65106 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -15,6 +15,7 @@ #include "base/error_state.h" #include "base/nested_output.h" #include "era/bloom_tree.h" +#include "era/era_array.h" #include "era/superblock.h" #include "persistent-data/block.h" #include "persistent-data/file_utils.h" @@ -98,7 +99,7 @@ namespace { } void visit(bloom_tree_detail::missing_eras const &d) { - out() << "missing eras" << end_message(); + out() << "missing eras from bloom tree" << end_message(); { nested_output::nest _ = push(); out() << d.get_desc() << end_message(); @@ -125,6 +126,38 @@ namespace { using reporter_base::get_error; }; + class era_array_reporter : public era_array_detail::damage_visitor, reporter_base { + public: + era_array_reporter(nested_output &o) + : reporter_base(o) { + } + + void visit(era_array_detail::missing_eras const &d) { + out() << "missing eras from era array" << end_message(); + { + nested_output::nest _ = push(); + out() << d.get_desc() << end_message(); + out() << "Effected eras: [" << d.eras_.begin_.get() + << ", " << d.eras_.end_.get() << ")" << end_message(); + } + + mplus_error(FATAL); + } + + void visit(era_array_detail::invalid_era const &d) { + out() << "invalid era in era array" << end_message(); + { + nested_output::nest _ = push(); + out() << d.get_desc() << end_message(); + out() << "block: " << d.block_ << ", era: " << d.era_ << end_message(); + } + + mplus_error(FATAL); + } + + using reporter_base::get_error; + }; + //-------------------------------- transaction_manager::ptr open_tm(block_manager<>::ptr bm) { @@ -184,12 +217,22 @@ namespace { transaction_manager::ptr tm = open_tm(bm); bloom_tree_reporter bt_rep(out); - era_detail_traits::ref_counter rc(tm); - bloom_tree bt(tm, rc); - check_bloom_tree(tm, bt, bt_rep); + { + era_detail_traits::ref_counter rc(tm); + bloom_tree bt(tm, sb.bloom_tree_root, rc); + check_bloom_tree(tm, bt, bt_rep); + } + + era_array_reporter ea_rep(out); + { + uint32_traits::ref_counter rc; + era_array ea(tm, rc, sb.era_array_root, sb.nr_blocks); + check_era_array(ea, ea_rep); + } return combine_errors(sb_rep.get_error(), - bt_rep.get_error()); + combine_errors(bt_rep.get_error(), + ea_rep.get_error())); } int check(string const &path, flags const &fs) { diff --git a/era/era_detail.h b/era/era_detail.h index 7afaaaf..6cacc09 100644 --- a/era/era_detail.h +++ b/era/era_detail.h @@ -26,11 +26,16 @@ namespace era { uint64_t bloom_root; }; + // FIXME: implement struct era_detail_ref_counter { - era_detail_ref_counter(persistent_data::transaction_manager::ptr tm); + era_detail_ref_counter(persistent_data::transaction_manager::ptr tm) { + } - void inc(persistent_data::block_address b); - void dec(persistent_data::block_address b); + void inc(persistent_data::block_address b) { + } + + void dec(persistent_data::block_address b) { + } }; struct era_detail_traits { diff --git a/era/era_dump.cc b/era/era_dump.cc new file mode 100644 index 0000000..1f13abd --- /dev/null +++ b/era/era_dump.cc @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +#include "version.h" +#include "era/era_array.h" +#include "era/bloom_tree.h" +#include "era/metadata.h" +#include "era/metadata_dump.h" +#include "era/xml_format.h" +#include "persistent-data/file_utils.h" + +using namespace era; +using namespace std; + +//---------------------------------------------------------------- + +namespace { + struct flags { + flags() + : repair_(false) { + } + + bool repair_; + }; + + //-------------------------------- + + string const STDOUT_PATH("-"); + + bool want_stdout(string const &output) { + return output == STDOUT_PATH; + } + + int dump(string const &dev, string const &output, flags const &fs) { + try { + block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_ONLY); + metadata::ptr md(new metadata(bm, metadata::OPEN)); + + if (want_stdout(output)) { + emitter::ptr e = create_xml_emitter(cout); + metadata_dump(md, e, fs.repair_); + } else { + ofstream out(output.c_str()); + emitter::ptr e = create_xml_emitter(out); + metadata_dump(md, e, fs.repair_); + } + + } catch (std::exception &e) { + cerr << e.what() << endl; + return 1; + } + + return 0; + } + + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options] {device|file}" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-o }" << endl + << " {-V|--version}" << endl + << " {--repair}" << endl; + } +} + +//---------------------------------------------------------------- + +int main(int argc, char **argv) +{ + int c; + flags fs; + string output("-"); + char const shortopts[] = "ho:V"; + + option const longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "output", required_argument, NULL, 'o' }, + { "version", no_argument, NULL, 'V' }, + { "repair", no_argument, NULL, 1 }, + { NULL, no_argument, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch(c) { + case 1: + fs.repair_ = true; + break; + + case 'h': + usage(cout, basename(argv[0])); + return 0; + + case 'o': + output = optarg; + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; + } + } + + if (argc == optind) { + cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); + return 1; + } + + return dump(argv[optind], output, fs); +} + +//---------------------------------------------------------------- diff --git a/era/metadata.cc b/era/metadata.cc new file mode 100644 index 0000000..270bd15 --- /dev/null +++ b/era/metadata.cc @@ -0,0 +1,60 @@ +#include "era/metadata.h" +#include "persistent-data/space-maps/core.h" + +using namespace era; + +//---------------------------------------------------------------- + +namespace { + unsigned const METADATA_CACHE_SIZ = 1024; + + // FIXME: duplication + transaction_manager::ptr + open_tm(block_manager<>::ptr bm) { + space_map::ptr sm(new core_map(bm->get_nr_blocks())); + sm->inc(SUPERBLOCK_LOCATION); + transaction_manager::ptr tm(new transaction_manager(bm, sm)); + return tm; + } + + void + copy_space_maps(space_map::ptr lhs, space_map::ptr rhs) { + for (block_address b = 0; b < rhs->get_nr_blocks(); b++) { + uint32_t count = rhs->get_count(b); + if (count > 0) + lhs->set_count(b, rhs->get_count(b)); + } + } +} + +metadata::metadata(block_manager<>::ptr bm, open_type ot) +{ + switch (ot) { + case CREATE: + // finish + throw runtime_error("not imlemented"); + break; + + case OPEN: + open_metadata(bm); + break; + } +} + +void +metadata::open_metadata(block_manager<>::ptr bm) +{ + tm_ = open_tm(bm); + sb_ = read_superblock(tm_->get_bm()); + + bloom_tree_ = bloom_tree::ptr(new bloom_tree(tm_, + sb_.bloom_tree_root, + era_detail_traits::ref_counter(tm_))); + + era_array_ = era_array::ptr(new era_array(tm_, + uint32_traits::ref_counter(), + sb_.era_array_root, + sb_.nr_blocks)); +} + +//---------------------------------------------------------------- diff --git a/era/metadata.h b/era/metadata.h new file mode 100644 index 0000000..0d8a23b --- /dev/null +++ b/era/metadata.h @@ -0,0 +1,47 @@ +#ifndef ERA_METADATA_H +#define ERA_METADATA_H + +#include "base/endian_utils.h" + +#include "persistent-data/block.h" +#include "persistent-data/data-structures/array.h" +#include "persistent-data/data-structures/bitset.h" +#include "persistent-data/space-maps/disk.h" +#include "persistent-data/transaction_manager.h" + +#include "era/superblock.h" +#include "era/bloom_tree.h" +#include "era/era_array.h" + +//---------------------------------------------------------------- + +namespace era { + class metadata { + public: + enum open_type { + CREATE, + OPEN + }; + + typedef block_manager<>::read_ref read_ref; + typedef block_manager<>::write_ref write_ref; + typedef boost::shared_ptr ptr; + + metadata(block_manager<>::ptr bm, open_type ot); + void commit(bool clean_shutdown = true); + + typedef persistent_data::transaction_manager tm; + tm::ptr tm_; + superblock sb_; + checked_space_map::ptr metadata_sm_; + bloom_tree::ptr bloom_tree_; + era_array::ptr era_array_; + + private: + void open_metadata(block_manager<>::ptr bm); + }; +}; + +//---------------------------------------------------------------- + +#endif diff --git a/era/metadata_dump.cc b/era/metadata_dump.cc new file mode 100644 index 0000000..edf4a1d --- /dev/null +++ b/era/metadata_dump.cc @@ -0,0 +1,135 @@ +#include "era/metadata_dump.h" +#include "era/era_array.h" + +using namespace era; +using namespace std; + +//---------------------------------------------------------------- + +namespace { + string to_string(unsigned char const *data) { + // FIXME: we're assuming the data is zero terminated here + return std::string(reinterpret_cast(data)); + } + + void raise_metadata_damage() { + throw std::runtime_error("metadata contains errors (run era_check for details).\n" + "perhaps you wanted to run with --repair ?"); + } + + class bloom_tree_emitter : public bloom_tree_detail::bloom_visitor { + public: + bloom_tree_emitter(emitter::ptr e) + : e_(e) { + } + + virtual void bloom_begin(uint32_t era, uint32_t nr_blocks, uint32_t nr_bits, uint32_t nr_set) { + e_->begin_bloom(era, nr_bits, nr_blocks); + } + + virtual void bit(uint32_t bit, bool value) { + e_->bloom_bit(bit, value); + } + + virtual void bloom_end() { + e_->end_bloom(); + } + + private: + emitter::ptr e_; + }; + + struct ignore_bloom_tree_damage : public bloom_tree_detail::damage_visitor { + void visit(bloom_tree_detail::missing_eras const &d) { + } + + void visit(bloom_tree_detail::damaged_bloom_filter const &d) { + } + }; + + struct fatal_bloom_tree_damage : public bloom_tree_detail::damage_visitor { + void visit(bloom_tree_detail::missing_eras const &d) { + raise_metadata_damage(); + } + + void visit(bloom_tree_detail::damaged_bloom_filter const &d) { + raise_metadata_damage(); + } + }; + + //-------------------------------- + + class era_array_emitter : public era_array_visitor { + public: + era_array_emitter(emitter::ptr e) + : e_(e) { + } + + virtual void visit(uint32_t index, uint32_t era) { + e_->era(index, era); + } + + private: + emitter::ptr e_; + }; + + struct ignore_era_array_damage : public era_array_detail::damage_visitor { + void visit(era_array_detail::missing_eras const &d) { + } + + void visit(era_array_detail::invalid_era const &d) { + } + }; + + class fatal_era_array_damage : public era_array_detail::damage_visitor { + void visit(era_array_detail::missing_eras const &d) { + raise_metadata_damage(); + } + + void visit(era_array_detail::invalid_era const &d) { + raise_metadata_damage(); + } + }; +} + +//---------------------------------------------------------------- + +void +era::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair) +{ + superblock const &sb = md->sb_; + + e->begin_superblock(to_string(sb.uuid), sb.data_block_size, + sb.nr_blocks, + sb.current_era); + { + { + bloom_tree_emitter visitor(e); + + ignore_bloom_tree_damage ignore; + fatal_bloom_tree_damage fatal; + bloom_tree_detail::damage_visitor &dv = repair ? + static_cast(ignore) : + static_cast(fatal); + + walk_bloom_tree(md->tm_, *md->bloom_tree_, visitor, dv); + } + + e->begin_era_array(); + { + era_array_emitter visitor(e); + + ignore_era_array_damage ignore; + fatal_era_array_damage fatal; + era_array_detail::damage_visitor &dv = repair ? + static_cast(ignore) : + static_cast(fatal); + + walk_era_array(*md->era_array_, visitor, dv); + } + e->end_era_array(); + } + e->end_superblock(); +} + +//---------------------------------------------------------------- diff --git a/era/metadata_dump.h b/era/metadata_dump.h new file mode 100644 index 0000000..59d9d67 --- /dev/null +++ b/era/metadata_dump.h @@ -0,0 +1,15 @@ +#ifndef ERA_METADATA_DUMP_H +#define ERA_METADATA_DUMP_H + +#include "era/metadata.h" +#include "era/emitter.h" + +//---------------------------------------------------------------- + +namespace era { + void metadata_dump(metadata::ptr md, emitter::ptr out, bool repair); +} + +//---------------------------------------------------------------- + +#endif diff --git a/era/xml_format.cc b/era/xml_format.cc new file mode 100644 index 0000000..9a63b8a --- /dev/null +++ b/era/xml_format.cc @@ -0,0 +1,103 @@ +#include "era/xml_format.h" + +using namespace boost; +using namespace era; +using namespace persistent_data; +using namespace std; + +//---------------------------------------------------------------- + +namespace { + class xml_emitter : public emitter { + public: + xml_emitter(ostream &out) + : out_(out), + indent_(0) { + } + + void begin_superblock(std::string const &uuid, + uint32_t block_size, + pd::block_address nr_blocks, + uint32_t current_era) { + indent(); + out_ << "" << endl; + inc(); + } + + void end_superblock() { + dec(); + indent(); + out_ << "" << endl; + } + + void begin_bloom(uint32_t era, uint32_t nr_bits, + pd::block_address nr_blocks) { + indent(); + out_ << "" << endl; + inc(); + } + + void bloom_bit(uint32_t bit, bool value) { + indent(); + // FIXME: collect all the bits, then uuencode + out_ << "" << endl; + } + + void end_bloom() { + dec(); + indent(); + out_ << "" << endl; + } + + void begin_era_array() { + indent(); + out_ << "" << endl; + inc(); + } + + void era(pd::block_address block, uint32_t era) { + indent(); + out_ << "" << endl; + } + + void end_era_array() { + dec(); + indent(); + out_ << "" << endl; + } + + private: + // FIXME: factor out a common class with the thin_provisioning emitter + void indent() { + for (unsigned i = 0; i < indent_ * 2; i++) + out_ << ' '; + } + + void inc() { + indent_++; + } + + void dec() { + indent_--; + } + + ostream &out_; + unsigned indent_; + }; +} + +//---------------------------------------------------------------- + +emitter::ptr +era::create_xml_emitter(std::ostream &out) +{ + return emitter::ptr(new xml_emitter(out)); +} + +//---------------------------------------------------------------- diff --git a/era/xml_format.h b/era/xml_format.h new file mode 100644 index 0000000..30b7174 --- /dev/null +++ b/era/xml_format.h @@ -0,0 +1,17 @@ +#ifndef ERA_XML_FORMAT_H +#define ERA_XML_FORMAT_H + +#include "emitter.h" + +#include + +//---------------------------------------------------------------- + +namespace era { + emitter::ptr create_xml_emitter(std::ostream &out); + void parse_xml(std::istream &in, emitter::ptr e); +} + +//---------------------------------------------------------------- + +#endif diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index 6bab965..47a47bf 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -27,6 +27,8 @@ namespace { namespace persistent_data { namespace bitset_detail { + size_t BITS_PER_ULL = 64; + class bitset_impl { public: typedef boost::shared_ptr ptr; @@ -39,7 +41,7 @@ namespace persistent_data { bitset_impl(tm_ptr tm, block_address root, unsigned nr_bits) : nr_bits_(nr_bits), - array_(tm, rc_, root, nr_bits) { + array_(tm, rc_, root, nr_bits / BITS_PER_ULL) { } block_address get_root() const { diff --git a/persistent-data/data-structures/btree.h b/persistent-data/data-structures/btree.h index 93c3284..f060279 100644 --- a/persistent-data/data-structures/btree.h +++ b/persistent-data/data-structures/btree.h @@ -43,22 +43,6 @@ namespace persistent_data { space_map::ptr sm_; }; - // FIXME: move to sep file. I don't think it's directly used by - // the btree code. - struct uint64_traits { - typedef base::le64 disk_type; - typedef uint64_t value_type; - typedef no_op_ref_counter ref_counter; - - static void unpack(disk_type const &disk, value_type &value) { - value = base::to_cpu(disk); - } - - static void pack(value_type const &value, disk_type &disk) { - disk = base::to_disk(value); - } - }; - struct block_traits { typedef base::le64 disk_type; typedef block_address value_type; diff --git a/persistent-data/data-structures/simple_traits.h b/persistent-data/data-structures/simple_traits.h new file mode 100644 index 0000000..fa01737 --- /dev/null +++ b/persistent-data/data-structures/simple_traits.h @@ -0,0 +1,38 @@ +#ifndef PERSISTENT_DATA_DATA_STRUCTURES_SIMPLE_TRAITS_H +#define PERSISTENT_DATA_DATA_STRUCTURES_SIMPLE_TRAITS_H + +//---------------------------------------------------------------- + +namespace persistent_data { + struct uint64_traits { + typedef base::le64 disk_type; + typedef uint64_t value_type; + typedef no_op_ref_counter ref_counter; + + static void unpack(disk_type const &disk, value_type &value) { + value = base::to_cpu(disk); + } + + static void pack(value_type const &value, disk_type &disk) { + disk = base::to_disk(value); + } + }; + + struct uint32_traits { + typedef base::le32 disk_type; + typedef uint32_t value_type; + typedef no_op_ref_counter ref_counter; + + static void unpack(disk_type const &disk, value_type &value) { + value = base::to_cpu(disk); + } + + static void pack(value_type const &value, disk_type &disk) { + disk = base::to_disk(value); + } + }; +} + +//---------------------------------------------------------------- + +#endif From e7bbfbc1fdaab7d2a891647e61de3ca71d86831d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 23 Jan 2014 00:46:29 +0000 Subject: [PATCH 018/165] update ignore file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7d541d7..3d21d5c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ cache_restore cache_repair era_check +era_dump +era_invalidate *.metadata bad-metadata From 8531a2befa45e1d71332be055f0a5d155bac58d9 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 29 Jan 2014 21:37:25 +0000 Subject: [PATCH 019/165] [era] switch from bloom filters to writesets --- Makefile.in | 39 ++++- era/emitter.h | 6 +- era/era_check.cc | 24 +-- era/era_detail.cc | 23 +-- era/era_detail.h | 13 +- era/era_dump.cc | 2 +- era/era_invalidate.cc | 114 +++++++++++++++ era/metadata.cc | 6 +- era/metadata.h | 4 +- era/metadata_dump.cc | 40 ++--- era/superblock.cc | 16 +- era/superblock.h | 2 +- era/{bloom_tree.cc => writeset_tree.cc} | 63 ++++---- era/{bloom_tree.h => writeset_tree.h} | 43 +++--- era/xml_format.cc | 14 +- persistent-data/block.tcc | 2 +- unit-tests/array_t.cc | 1 + unit-tests/bloom_filter_t.cc | 185 ++++++++++++++++++++++-- unit-tests/btree_counter_t.cc | 1 + unit-tests/btree_t.cc | 1 + 20 files changed, 439 insertions(+), 160 deletions(-) create mode 100644 era/era_invalidate.cc rename era/{bloom_tree.cc => writeset_tree.cc} (54%) rename era/{bloom_tree.h => writeset_tree.h} (54%) diff --git a/Makefile.in b/Makefile.in index f199a80..6e88395 100644 --- a/Makefile.in +++ b/Makefile.in @@ -27,6 +27,7 @@ PROGRAMS=\ \ era_check \ era_dump \ + era_invalidate \ \ thin_check \ thin_dump \ @@ -53,7 +54,7 @@ SOURCE=\ era/era_array.cc \ era/era_detail.cc \ era/superblock.cc \ - era/bloom_tree.cc \ + era/writeset_tree.cc \ era/metadata.cc \ era/metadata_dump.cc \ era/xml_format.cc \ @@ -305,7 +306,7 @@ ERA_CHECK_SOURCE=\ base/error_state.cc \ base/endian_utils.cc \ \ - era/bloom_tree.cc \ + era/writeset_tree.cc \ era/era_detail.cc \ era/era_array.cc \ era/metadata.cc \ @@ -335,7 +336,7 @@ ERA_DUMP_SOURCE=\ base/error_state.cc \ base/endian_utils.cc \ \ - era/bloom_tree.cc \ + era/writeset_tree.cc \ era/era_detail.cc \ era/era_array.cc \ era/metadata.cc \ @@ -362,6 +363,38 @@ era_dump: $(ERA_DUMP_OBJECTS) era/era_dump.o @echo " [LD] $@" $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) +ERA_INVALIDATE_SOURCE=\ + base/base64.cc \ + base/error_state.cc \ + base/endian_utils.cc \ + \ + era/writeset_tree.cc \ + era/era_detail.cc \ + era/era_array.cc \ + era/metadata.cc \ + era/metadata_dump.cc \ + era/superblock.cc \ + era/xml_format.cc \ + \ + persistent-data/checksum.cc \ + persistent-data/error_set.cc \ + persistent-data/file_utils.cc \ + persistent-data/hex_dump.cc \ + persistent-data/lock_tracker.cc \ + persistent-data/data-structures/btree.cc \ + persistent-data/data-structures/bitset.cc \ + persistent-data/space_map.cc \ + persistent-data/space-maps/disk.cc \ + persistent-data/space-maps/recursive.cc \ + persistent-data/space-maps/careful_alloc.cc \ + persistent-data/transaction_manager.cc \ + +ERA_INVALIDATE_OBJECTS=$(subst .cc,.o,$(ERA_INVALIDATE_SOURCE)) + +era_invalidate: $(ERA_INVALIDATE_OBJECTS) era/era_invalidate.o + @echo " [LD] $@" + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) + #---------------------------------------------------------------- DEPEND_FILES=\ diff --git a/era/emitter.h b/era/emitter.h index 0fb7520..c175384 100644 --- a/era/emitter.h +++ b/era/emitter.h @@ -20,9 +20,9 @@ namespace era { uint32_t current_era) = 0; virtual void end_superblock() = 0; - virtual void begin_bloom(uint32_t era, uint32_t nr_bits, pd::block_address nr_blocks) = 0; - virtual void bloom_bit(uint32_t bit, bool value) = 0; - virtual void end_bloom() = 0; + virtual void begin_writeset(uint32_t era, uint32_t nr_bits) = 0; + virtual void writeset_bit(uint32_t bit, bool value) = 0; + virtual void end_writeset() = 0; virtual void begin_era_array() = 0; virtual void era(pd::block_address block, uint32_t era) = 0; diff --git a/era/era_check.cc b/era/era_check.cc index de65106..3e3f1fd 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -14,7 +14,7 @@ #include "base/error_state.h" #include "base/nested_output.h" -#include "era/bloom_tree.h" +#include "era/writeset_tree.h" #include "era/era_array.h" #include "era/superblock.h" #include "persistent-data/block.h" @@ -92,14 +92,14 @@ namespace { using reporter_base::get_error; }; - class bloom_tree_reporter : public bloom_tree_detail::damage_visitor, reporter_base { + class writeset_tree_reporter : public writeset_tree_detail::damage_visitor, reporter_base { public: - bloom_tree_reporter(nested_output &o) + writeset_tree_reporter(nested_output &o) : reporter_base(o) { } - void visit(bloom_tree_detail::missing_eras const &d) { - out() << "missing eras from bloom tree" << end_message(); + void visit(writeset_tree_detail::missing_eras const &d) { + out() << "missing eras from writeset tree" << end_message(); { nested_output::nest _ = push(); out() << d.get_desc() << end_message(); @@ -110,8 +110,8 @@ namespace { mplus_error(FATAL); } - void visit(bloom_tree_detail::damaged_bloom_filter const &d) { - out() << "damaged bloom filter" << end_message(); + void visit(writeset_tree_detail::damaged_writeset const &d) { + out() << "damaged writeset" << end_message(); { nested_output::nest _ = push(); out() << d.get_desc() << end_message(); @@ -216,22 +216,22 @@ namespace { superblock sb = read_superblock(bm); transaction_manager::ptr tm = open_tm(bm); - bloom_tree_reporter bt_rep(out); + writeset_tree_reporter wt_rep(out); { era_detail_traits::ref_counter rc(tm); - bloom_tree bt(tm, sb.bloom_tree_root, rc); - check_bloom_tree(tm, bt, bt_rep); + writeset_tree wt(tm, sb.writeset_tree_root, rc); + check_writeset_tree(tm, wt, wt_rep); } era_array_reporter ea_rep(out); { uint32_traits::ref_counter rc; era_array ea(tm, rc, sb.era_array_root, sb.nr_blocks); - check_era_array(ea, ea_rep); + check_era_array(ea, sb.current_era, ea_rep); } return combine_errors(sb_rep.get_error(), - combine_errors(bt_rep.get_error(), + combine_errors(wt_rep.get_error(), ea_rep.get_error())); } diff --git a/era/era_detail.cc b/era/era_detail.cc index 7a88fcc..587102d 100644 --- a/era/era_detail.cc +++ b/era/era_detail.cc @@ -7,37 +7,18 @@ using namespace era; //---------------------------------------------------------------- -namespace { -#if 0 - le32 pack_hash_detail(uint32_t hash1, uint32_t hash2, uint32_t nr_probes) { - throw std::runtime_error("not implemented"); - } - - void unpack_hash_detail(le32 packed, uint32_t &hash1, uint32_t &hash2, uint32_t &nr_probes) { - throw std::runtime_error("not implemented"); - } -#endif -} - void era_detail_traits::unpack(disk_type const &disk, value_type &value) { - value.nr_blocks = to_cpu(disk.nr_blocks); value.nr_bits = to_cpu(disk.nr_bits); - value.nr_set = to_cpu(disk.nr_set); - value.bloom_root = to_cpu(disk.bloom_root); - - //unpack_hash_detail(disk.hash_fns_and_probes, value.hash1, value.hash2, value.nr_probes); + value.writeset_root = to_cpu(disk.writeset_root); } void era_detail_traits::pack(value_type const &value, disk_type &disk) { - disk.nr_blocks = to_disk(value.nr_blocks); disk.nr_bits = to_disk(value.nr_bits); - disk.nr_set = to_disk(value.nr_set); - disk.bloom_root = to_disk(value.bloom_root); -// disk.hash_fns_and_probes = pack_hash_detail(value.hash1, value.hash2, value.nr_probes); + disk.writeset_root = to_disk(value.writeset_root); } //---------------------------------------------------------------- diff --git a/era/era_detail.h b/era/era_detail.h index 6cacc09..8cb8b06 100644 --- a/era/era_detail.h +++ b/era/era_detail.h @@ -8,22 +8,13 @@ namespace era { struct era_detail_disk { - base::le32 nr_blocks; base::le32 nr_bits; - base::le32 nr_set; - base::le64 bloom_root; + base::le64 writeset_root; } __attribute__ ((packed)); struct era_detail { - uint32_t nr_blocks; uint32_t nr_bits; - uint32_t nr_set; - - uint32_t hash1; - uint32_t hash2; - uint32_t nr_probes; - - uint64_t bloom_root; + uint64_t writeset_root; }; // FIXME: implement diff --git a/era/era_dump.cc b/era/era_dump.cc index 1f13abd..a2c82aa 100644 --- a/era/era_dump.cc +++ b/era/era_dump.cc @@ -5,7 +5,7 @@ #include "version.h" #include "era/era_array.h" -#include "era/bloom_tree.h" +#include "era/writeset_tree.h" #include "era/metadata.h" #include "era/metadata_dump.h" #include "era/xml_format.h" diff --git a/era/era_invalidate.cc b/era/era_invalidate.cc new file mode 100644 index 0000000..c96b286 --- /dev/null +++ b/era/era_invalidate.cc @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +#include "version.h" +#include "era/era_array.h" +#include "era/writeset_tree.h" +#include "era/metadata.h" +#include "era/xml_format.h" +#include "persistent-data/file_utils.h" + +using namespace era; +using namespace std; + +//---------------------------------------------------------------- + +namespace { + struct flags { + flags() { + } + }; + + //-------------------------------- + + void emit_blocks() { + + } + + //-------------------------------- + + string const STDOUT_PATH("-"); + + bool want_stdout(string const &output) { + return output == STDOUT_PATH; + } + + int invalidate(string const &dev, string const &output, flags const &fs) { + try { + block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_ONLY); + metadata::ptr md(new metadata(bm, metadata::OPEN)); + + if (want_stdout(output)) { + emitter::ptr e = create_xml_emitter(cout); + //emit_blocks(md, e, fs); + } else { + ofstream out(output.c_str()); + emitter::ptr e = create_xml_emitter(out); + //emit_blocks(md, e, fs); + } + + } catch (std::exception &e) { + cerr << e.what() << endl; + return 1; + } + + return 0; + } + + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options] {device|file}" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-o }" << endl + << " {-V|--version}" << endl; + } +} + +//---------------------------------------------------------------- + +int main(int argc, char **argv) +{ + int c; + flags fs; + string output("-"); + char const shortopts[] = "ho:V"; + + option const longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "output", required_argument, NULL, 'o' }, + { "version", no_argument, NULL, 'V' }, + { NULL, no_argument, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch(c) { + case 'h': + usage(cout, basename(argv[0])); + return 0; + + case 'o': + output = optarg; + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; + } + } + + if (argc == optind) { + cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); + return 1; + } + + return invalidate(argv[optind], output, fs); +} + +//---------------------------------------------------------------- diff --git a/era/metadata.cc b/era/metadata.cc index 270bd15..ab2eb7b 100644 --- a/era/metadata.cc +++ b/era/metadata.cc @@ -47,9 +47,9 @@ metadata::open_metadata(block_manager<>::ptr bm) tm_ = open_tm(bm); sb_ = read_superblock(tm_->get_bm()); - bloom_tree_ = bloom_tree::ptr(new bloom_tree(tm_, - sb_.bloom_tree_root, - era_detail_traits::ref_counter(tm_))); + writeset_tree_ = writeset_tree::ptr(new writeset_tree(tm_, + sb_.writeset_tree_root, + era_detail_traits::ref_counter(tm_))); era_array_ = era_array::ptr(new era_array(tm_, uint32_traits::ref_counter(), diff --git a/era/metadata.h b/era/metadata.h index 0d8a23b..ca31a50 100644 --- a/era/metadata.h +++ b/era/metadata.h @@ -10,7 +10,7 @@ #include "persistent-data/transaction_manager.h" #include "era/superblock.h" -#include "era/bloom_tree.h" +#include "era/writeset_tree.h" #include "era/era_array.h" //---------------------------------------------------------------- @@ -34,7 +34,7 @@ namespace era { tm::ptr tm_; superblock sb_; checked_space_map::ptr metadata_sm_; - bloom_tree::ptr bloom_tree_; + writeset_tree::ptr writeset_tree_; era_array::ptr era_array_; private: diff --git a/era/metadata_dump.cc b/era/metadata_dump.cc index edf4a1d..e7eac5f 100644 --- a/era/metadata_dump.cc +++ b/era/metadata_dump.cc @@ -17,42 +17,42 @@ namespace { "perhaps you wanted to run with --repair ?"); } - class bloom_tree_emitter : public bloom_tree_detail::bloom_visitor { + class writeset_tree_emitter : public writeset_tree_detail::writeset_visitor { public: - bloom_tree_emitter(emitter::ptr e) + writeset_tree_emitter(emitter::ptr e) : e_(e) { } - virtual void bloom_begin(uint32_t era, uint32_t nr_blocks, uint32_t nr_bits, uint32_t nr_set) { - e_->begin_bloom(era, nr_bits, nr_blocks); + virtual void writeset_begin(uint32_t era, uint32_t nr_bits) { + e_->begin_writeset(era, nr_bits); } virtual void bit(uint32_t bit, bool value) { - e_->bloom_bit(bit, value); + e_->writeset_bit(bit, value); } - virtual void bloom_end() { - e_->end_bloom(); + virtual void writeset_end() { + e_->end_writeset(); } private: emitter::ptr e_; }; - struct ignore_bloom_tree_damage : public bloom_tree_detail::damage_visitor { - void visit(bloom_tree_detail::missing_eras const &d) { + struct ignore_writeset_tree_damage : public writeset_tree_detail::damage_visitor { + void visit(writeset_tree_detail::missing_eras const &d) { } - void visit(bloom_tree_detail::damaged_bloom_filter const &d) { + void visit(writeset_tree_detail::damaged_writeset const &d) { } }; - struct fatal_bloom_tree_damage : public bloom_tree_detail::damage_visitor { - void visit(bloom_tree_detail::missing_eras const &d) { + struct fatal_writeset_tree_damage : public writeset_tree_detail::damage_visitor { + void visit(writeset_tree_detail::missing_eras const &d) { raise_metadata_damage(); } - void visit(bloom_tree_detail::damaged_bloom_filter const &d) { + void visit(writeset_tree_detail::damaged_writeset const &d) { raise_metadata_damage(); } }; @@ -104,15 +104,15 @@ era::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair) sb.current_era); { { - bloom_tree_emitter visitor(e); + writeset_tree_emitter visitor(e); - ignore_bloom_tree_damage ignore; - fatal_bloom_tree_damage fatal; - bloom_tree_detail::damage_visitor &dv = repair ? - static_cast(ignore) : - static_cast(fatal); + ignore_writeset_tree_damage ignore; + fatal_writeset_tree_damage fatal; + writeset_tree_detail::damage_visitor &dv = repair ? + static_cast(ignore) : + static_cast(fatal); - walk_bloom_tree(md->tm_, *md->bloom_tree_, visitor, dv); + walk_writeset_tree(md->tm_, *md->writeset_tree_, visitor, dv); } e->begin_era_array(); diff --git a/era/superblock.cc b/era/superblock.cc index 99e51e0..0572314 100644 --- a/era/superblock.cc +++ b/era/superblock.cc @@ -34,7 +34,7 @@ namespace { le32 current_era; era_detail_disk current_detail; - le64 bloom_tree_root; + le64 writeset_tree_root; le64 era_array_root; } __attribute__ ((packed)); @@ -116,7 +116,7 @@ superblock::superblock() metadata_block_size(8), nr_blocks(0), current_era(0), - bloom_tree_root(0), + writeset_tree_root(0), era_array_root(0) { memset(uuid, 0, sizeof(uuid)); @@ -141,7 +141,7 @@ superblock_traits::unpack(disk_type const &disk, value_type &value) value.nr_blocks = to_cpu(disk.nr_blocks); value.current_era = to_cpu(disk.current_era); era_detail_traits::unpack(disk.current_detail, value.current_detail); - value.bloom_tree_root = to_cpu(disk.bloom_tree_root); + value.writeset_tree_root = to_cpu(disk.writeset_tree_root); value.era_array_root = to_cpu(disk.era_array_root); } @@ -161,7 +161,7 @@ superblock_traits::pack(value_type const &value, disk_type &disk) disk.nr_blocks = to_disk(value.nr_blocks); disk.current_era = to_disk(value.current_era); era_detail_traits::pack(value.current_detail, disk.current_detail); - disk.bloom_tree_root = to_disk(value.bloom_tree_root); + disk.writeset_tree_root = to_disk(value.writeset_tree_root); disk.era_array_root = to_disk(value.era_array_root); } @@ -282,8 +282,8 @@ era::check_superblock(superblock const &sb, visitor.visit(superblock_invalid(msg.str())); } - if (sb.bloom_tree_root == SUPERBLOCK_LOCATION) { - string msg("bloom tree root points back to the superblock"); + if (sb.writeset_tree_root == SUPERBLOCK_LOCATION) { + string msg("writeset tree root points back to the superblock"); visitor.visit(superblock_invalid(msg)); } @@ -292,9 +292,9 @@ era::check_superblock(superblock const &sb, visitor.visit(superblock_invalid(msg)); } - if (sb.bloom_tree_root == sb.era_array_root) { + if (sb.writeset_tree_root == sb.era_array_root) { ostringstream msg; - msg << "bloom tree root and era array both point to the same block: " + msg << "writeset tree root and era array both point to the same block: " << sb.era_array_root; visitor.visit(superblock_invalid(msg.str())); } diff --git a/era/superblock.h b/era/superblock.h index 2ff1179..346582e 100644 --- a/era/superblock.h +++ b/era/superblock.h @@ -59,7 +59,7 @@ namespace era { era_detail current_detail; // A btree of undigested era_details - uint64_t bloom_tree_root; + uint64_t writeset_tree_root; // Big array holding the digested era/block info. uint64_t era_array_root; diff --git a/era/bloom_tree.cc b/era/writeset_tree.cc similarity index 54% rename from era/bloom_tree.cc rename to era/writeset_tree.cc index b49e533..763427e 100644 --- a/era/bloom_tree.cc +++ b/era/writeset_tree.cc @@ -1,10 +1,10 @@ -#include "era/bloom_tree.h" +#include "era/writeset_tree.h" #include "persistent-data/data-structures/btree_damage_visitor.h" #include "persistent-data/data-structures/bitset.h" using namespace boost; using namespace era; -using namespace bloom_tree_detail; +using namespace writeset_tree_detail; using namespace persistent_data; using namespace std; @@ -22,9 +22,9 @@ missing_eras::visit(damage_visitor &v) const { v.visit(*this); } -damaged_bloom_filter::damaged_bloom_filter(string const &desc, - uint32_t era, - run missing_bits) +damaged_writeset::damaged_writeset(string const &desc, + uint32_t era, + run missing_bits) : damage(desc), era_(era), missing_bits_(missing_bits) @@ -32,7 +32,7 @@ damaged_bloom_filter::damaged_bloom_filter(string const &desc, } void -damaged_bloom_filter::visit(damage_visitor &v) const +damaged_writeset::visit(damage_visitor &v) const { v.visit(*this); } @@ -40,39 +40,39 @@ damaged_bloom_filter::visit(damage_visitor &v) const //---------------------------------------------------------------- namespace { - class ll_bloom_visitor : public bitset_detail::bitset_visitor { + class ll_writeset_visitor : public bitset_detail::bitset_visitor { public: typedef persistent_data::transaction_manager::ptr tm_ptr; - ll_bloom_visitor(tm_ptr tm, - bloom_tree_detail::bloom_visitor &bloom_v, - bloom_tree_detail::damage_visitor &dv) + ll_writeset_visitor(tm_ptr tm, + writeset_tree_detail::writeset_visitor &writeset_v, + writeset_tree_detail::damage_visitor &dv) : tm_(tm), - bloom_v_(bloom_v), + writeset_v_(writeset_v), dv_(dv) { } void visit(btree_path const &path, era_detail const &era) { era_ = path[0]; - bitset bs(tm_, era.bloom_root, era.nr_bits); - bloom_v_.bloom_begin(era_, era.nr_blocks, era.nr_bits, era.nr_set); + bitset bs(tm_, era.writeset_root, era.nr_bits); + writeset_v_.writeset_begin(era_, era.nr_bits); bs.walk_bitset(*this); - bloom_v_.bloom_end(); + writeset_v_.writeset_end(); } void visit(uint32_t index, bool value) { - bloom_v_.bit(index, value); + writeset_v_.bit(index, value); } void visit(bitset_detail::missing_bits const &d) { - dv_.visit(bloom_tree_detail::damaged_bloom_filter("missing bits", era_, d.keys_)); + dv_.visit(writeset_tree_detail::damaged_writeset("missing bits", era_, d.keys_)); } private: tm_ptr tm_; uint64_t era_; - bloom_tree_detail::bloom_visitor &bloom_v_; - bloom_tree_detail::damage_visitor &dv_; + writeset_tree_detail::writeset_visitor &writeset_v_; + writeset_tree_detail::damage_visitor &dv_; }; class ll_damage_visitor { @@ -98,38 +98,37 @@ namespace { } void -era::walk_bloom_tree(persistent_data::transaction_manager::ptr tm, - bloom_tree const &tree, - bloom_tree_detail::bloom_visitor &bloom_v, - bloom_tree_detail::damage_visitor &dv) +era::walk_writeset_tree(persistent_data::transaction_manager::ptr tm, + writeset_tree const &tree, + writeset_tree_detail::writeset_visitor &writeset_v, + writeset_tree_detail::damage_visitor &dv) { - ll_bloom_visitor ll_bv(tm, bloom_v, dv); + ll_writeset_visitor ll_bv(tm, writeset_v, dv); ll_damage_visitor ll_dv(dv); btree_visit_values(tree, ll_bv, ll_dv); } namespace { - class noop_bloom_visitor : public bloom_tree_detail::bloom_visitor { + class noop_writeset_visitor : public writeset_tree_detail::writeset_visitor { public: - void bloom_begin(uint32_t era, uint32_t nr_blocks, - uint32_t nr_bits, uint32_t nr_set) { + void writeset_begin(uint32_t era, uint32_t nr_bits) { } void bit(uint32_t index, bool value) { } - void bloom_end() { + void writeset_end() { } }; }; void -era::check_bloom_tree(persistent_data::transaction_manager::ptr tm, - bloom_tree const &tree, - bloom_tree_detail::damage_visitor &dv) +era::check_writeset_tree(persistent_data::transaction_manager::ptr tm, + writeset_tree const &tree, + writeset_tree_detail::damage_visitor &dv) { - noop_bloom_visitor bv; - walk_bloom_tree(tm, tree, bv, dv); + noop_writeset_visitor bv; + walk_writeset_tree(tm, tree, bv, dv); } //---------------------------------------------------------------- diff --git a/era/bloom_tree.h b/era/writeset_tree.h similarity index 54% rename from era/bloom_tree.h rename to era/writeset_tree.h index ad4bbd6..9157ae6 100644 --- a/era/bloom_tree.h +++ b/era/writeset_tree.h @@ -1,5 +1,5 @@ -#ifndef ERA_BLOOM_TREE_H -#define ERA_BLOOM_TREE_H +#ifndef ERA_WRITESET_TREE_H +#define ERA_WRITESET_TREE_H #include "era/era_detail.h" #include "persistent-data/data-structures/btree.h" @@ -7,7 +7,7 @@ //---------------------------------------------------------------- namespace era { - namespace bloom_tree_detail { + namespace writeset_tree_detail { class damage_visitor; class damage { @@ -34,10 +34,10 @@ namespace era { run eras_; }; - struct damaged_bloom_filter : public damage { - damaged_bloom_filter(std::string const &desc, - uint32_t era, - run missing_bits); + struct damaged_writeset : public damage { + damaged_writeset(std::string const &desc, + uint32_t era, + run missing_bits); virtual void visit(damage_visitor &v) const; uint32_t era_; @@ -55,32 +55,31 @@ namespace era { } virtual void visit(missing_eras const &d) = 0; - virtual void visit(damaged_bloom_filter const &d) = 0; + virtual void visit(damaged_writeset const &d) = 0; }; - class bloom_visitor { + class writeset_visitor { public: - typedef boost::shared_ptr ptr; + typedef boost::shared_ptr ptr; - virtual ~bloom_visitor() {} + virtual ~writeset_visitor() {} - virtual void bloom_begin(uint32_t era, uint32_t nr_blocks, - uint32_t nr_bits, uint32_t nr_set) = 0; + virtual void writeset_begin(uint32_t era, uint32_t nr_bits) = 0; virtual void bit(uint32_t index, bool value) = 0; - virtual void bloom_end() = 0; + virtual void writeset_end() = 0; }; } - typedef persistent_data::btree<1, era_detail_traits> bloom_tree; + typedef persistent_data::btree<1, era_detail_traits> writeset_tree; - void walk_bloom_tree(persistent_data::transaction_manager::ptr tm, - bloom_tree const &tree, - bloom_tree_detail::bloom_visitor &bloom_v, - bloom_tree_detail::damage_visitor &dv); + void walk_writeset_tree(persistent_data::transaction_manager::ptr tm, + writeset_tree const &tree, + writeset_tree_detail::writeset_visitor &writeset_v, + writeset_tree_detail::damage_visitor &dv); - void check_bloom_tree(persistent_data::transaction_manager::ptr tm, - bloom_tree const &tree, - bloom_tree_detail::damage_visitor &dv); + void check_writeset_tree(persistent_data::transaction_manager::ptr tm, + writeset_tree const &tree, + writeset_tree_detail::damage_visitor &dv); } //---------------------------------------------------------------- diff --git a/era/xml_format.cc b/era/xml_format.cc index 9a63b8a..924a93b 100644 --- a/era/xml_format.cc +++ b/era/xml_format.cc @@ -33,25 +33,23 @@ namespace { out_ << "" << endl; } - void begin_bloom(uint32_t era, uint32_t nr_bits, - pd::block_address nr_blocks) { + void begin_writeset(uint32_t era, uint32_t nr_bits) { indent(); - out_ << "" << endl; + out_ << "" << endl; inc(); } - void bloom_bit(uint32_t bit, bool value) { + void writeset_bit(uint32_t bit, bool value) { indent(); // FIXME: collect all the bits, then uuencode out_ << "" << endl; } - void end_bloom() { + void end_writeset() { dec(); indent(); - out_ << "" << endl; + out_ << "" << endl; } void begin_era_array() { diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 09cd313..431cdd0 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -343,7 +343,7 @@ namespace persistent_data { unsigned max_concurrent_blocks, typename block_io::mode mode) : io_(new block_io(path, nr_blocks, mode)), - cache_(max(64u, max_concurrent_blocks)), + cache_(max(1024u, max_concurrent_blocks)), tracker_(0, nr_blocks) { } diff --git a/unit-tests/array_t.cc b/unit-tests/array_t.cc index fa2dae1..0c688b4 100644 --- a/unit-tests/array_t.cc +++ b/unit-tests/array_t.cc @@ -20,6 +20,7 @@ #include "persistent-data/transaction_manager.h" #include "persistent-data/space-maps/core.h" #include "persistent-data/data-structures/array.h" +#include "persistent-data/data-structures/simple_traits.h" #include diff --git a/unit-tests/bloom_filter_t.cc b/unit-tests/bloom_filter_t.cc index bb879ed..313355e 100644 --- a/unit-tests/bloom_filter_t.cc +++ b/unit-tests/bloom_filter_t.cc @@ -40,17 +40,26 @@ namespace { using namespace boost::random; - mt19937 rng; uniform_int_distribution uniform_dist(0, max); while (r.size() < count) { - block_address b = uniform_dist(rng); + block_address b = uniform_dist(rng_); r.insert(b); } return r; } + set generate_linear_blocks(unsigned count, + block_address max = std::numeric_limits::max()) { + set r; + + for (unsigned i = 0; i < count; i++) + r.insert(i); + + return r; + } + void commit() { block_manager<>::write_ref superblock(bm_->superblock(SUPERBLOCK)); } @@ -65,6 +74,8 @@ namespace { block_manager<>::ptr bm_; space_map::ptr sm_; transaction_manager::ptr tm_; + + boost::random::mt19937 rng_; }; } @@ -119,18 +130,75 @@ TEST_F(BloomFilterTests, reload_works) } } -TEST_F(BloomFilterTests, count_false_positives) +unsigned next_power(unsigned n) { - block_address nr_blocks = 1024 * 1024; - block_address written_blocks = nr_blocks / 100; + unsigned r = 1; + while (r < n) + r <<= 1; - unsigned shift = 1; + return r; +} - while ((1ull << shift) < (16 * written_blocks)) - shift++; - cerr << "bitset size: " << ((1 << shift) / (8 * 1024)) << "k" << endl; +unsigned calc_nr_bits(double false_positive_rate, unsigned dirty_blocks_per_era, unsigned nr_probes) +{ + double k = (double) nr_probes; + double kth_root = exp(log(false_positive_rate) / k); // can be precomputed - bloom_filter f(tm_, 1 << shift, 6); + // FIXME: we need a way to calulate this in kernel? or should we + // just pass in the bloom params on the target line? + double tmp = log(1.0 - kth_root); + double n = (- k * (double) dirty_blocks_per_era) / tmp; + + return next_power(ceil(n)); +} + +unsigned calc_m(double fp, unsigned nr_probes, unsigned n) +{ + double k = (double) nr_probes; + double kth_root = exp(log(fp) / k); + double tmp = log(1.0 - kth_root); + double m = (- ((double) n) / k) * tmp; + + return ceil(m); +} + +void print_nr_bits_table(double fp, unsigned nr_probes) +{ + cout << "fp = " << fp << ", k = " << nr_probes << endl; + + for (unsigned long long m = 1024; m < (1ull << 25); m *= 2) { + unsigned n = calc_nr_bits(fp, m, nr_probes); + unsigned actual_m = calc_m(fp, nr_probes, n); + + cout << " m = " << m << ", n = " << n << ", " << n / (1024 * 8) + << "k, actual_m = " << actual_m << endl; + } +} + +// Not really a test +TEST_F(BloomFilterTests, nr_bits_table) +{ + print_nr_bits_table(0.001, 4); + print_nr_bits_table(0.001, 6); + print_nr_bits_table(0.001, 8); + print_nr_bits_table(0.001, 16); +} + +TEST_F(BloomFilterTests, count_false_positives_with_random_inserts) +{ + block_address nr_blocks = 1 << 27; + block_address written_blocks = nr_blocks / 1024; + + unsigned nr_probes = 6; + unsigned n = calc_nr_bits(0.001, written_blocks, nr_probes); + + cerr << "bitset size: " << (n / (8 * 1024)) << "k" << endl; + + double ideal_k = log(2) * ((double) n / (double) written_blocks); + cerr << "Ideal k = " << ideal_k << endl; + + + bloom_filter f(tm_, n, nr_probes); set bs = generate_random_blocks(written_blocks, nr_blocks); set::const_iterator it; @@ -138,8 +206,6 @@ TEST_F(BloomFilterTests, count_false_positives) for (it = bs.begin(); it != bs.end(); ++it) f.set(*it); - // f.print_debug(cerr); - unsigned count = 0; for (unsigned i = 0; i < nr_blocks; i++) if (!bs.count(i) && f.test(i)) @@ -150,4 +216,99 @@ TEST_F(BloomFilterTests, count_false_positives) << "%" << endl; } +TEST_F(BloomFilterTests, count_false_positives_with_linear_inserts) +{ + block_address nr_blocks = 1 << 25; + block_address written_blocks = nr_blocks / 100; + + double fp = 0.001; + unsigned nr_probes = 6; + unsigned n = calc_nr_bits(fp, written_blocks, nr_probes); + + cerr << "bitset size: " << (n / (8 * 1024)) << "k" << endl; + + double ideal_k = log(2) * ((double) n / (double) written_blocks); + cerr << "Ideal k = " << ideal_k << endl; + + + bloom_filter f(tm_, n, nr_probes); + + set bs = generate_linear_blocks(written_blocks, nr_blocks); + set::const_iterator it; + + for (it = bs.begin(); it != bs.end(); ++it) + f.set(*it); + + unsigned count = 0; + for (unsigned i = 0; i < nr_blocks; i++) + if (!bs.count(i) && f.test(i)) + count++; + + double actual_fp = static_cast(count) / static_cast(nr_blocks); + + ASSERT_THAT(actual_fp, Lt(fp)); + + cerr << count << " false positives out of " << nr_blocks << ", " + << actual_fp * 100.0 << "%" << endl; +} + +TEST_F(BloomFilterTests, false_positives_over_multiple_eras) +{ + unsigned nr_eras = 10; + block_address nr_blocks = 1 << 20; + block_address written_blocks = nr_blocks / nr_eras; + + double fp = 0.001; + unsigned nr_probes = 6; + unsigned n = calc_nr_bits(fp, written_blocks, nr_probes); + + cerr << "bitset size: " << (n / (8 * 1024)) << "k" << endl; + + double ideal_k = log(2) * ((double) n / (double) written_blocks); + cerr << "Ideal k = " << ideal_k << endl; + + vector > writes(nr_eras); + vector filters(nr_eras); + + for (unsigned era = 0; era < writes.size(); era++) { + cerr << "inserting era " << era << endl; + + writes[era] = generate_random_blocks(written_blocks, nr_blocks); + set const &bs = writes[era]; + + filters[era] = bloom_filter::ptr(new bloom_filter(tm_, n, nr_probes)); + bloom_filter::ptr &f = filters[era]; + + set::const_iterator it; + for (it = bs.begin(); it != bs.end(); ++it) + f->set(*it); + } + + set write_sum; + set filter_sum; + for (unsigned era_plus_1 = writes.size(); era_plus_1 > 0; era_plus_1--) { + unsigned era = era_plus_1 - 1; + + set const &era_writes = writes[era]; + write_sum.insert(era_writes.begin(), era_writes.end()); + + for (unsigned i = 0; i < nr_blocks; i++) + if (filters[era]->test(i)) + filter_sum.insert(i); + + unsigned count = 0; + for (unsigned i = 0; i < nr_blocks; i++) { + if (write_sum.count(i) > 0) + ASSERT_THAT(filter_sum.count(i), Gt(0ull)); + + else if (filter_sum.count(i)) + count++; + } + + cerr << "blocks >= era " << era << ", false positives = " + << static_cast(count * 100) / static_cast(nr_blocks) + << "%" << endl; + } +} + //---------------------------------------------------------------- diff --git a/unit-tests/btree_counter_t.cc b/unit-tests/btree_counter_t.cc index 5f4a7d0..adbf523 100644 --- a/unit-tests/btree_counter_t.cc +++ b/unit-tests/btree_counter_t.cc @@ -5,6 +5,7 @@ #include "persistent-data/data-structures/btree.h" #include "persistent-data/data-structures/btree_counter.h" #include "persistent-data/space-maps/core.h" +#include "persistent-data/data-structures/simple_traits.h" using namespace base; using namespace std; diff --git a/unit-tests/btree_t.cc b/unit-tests/btree_t.cc index b2f2647..8fe3a71 100644 --- a/unit-tests/btree_t.cc +++ b/unit-tests/btree_t.cc @@ -20,6 +20,7 @@ #include "persistent-data/transaction_manager.h" #include "persistent-data/space-maps/core.h" #include "persistent-data/data-structures/btree.h" +#include "persistent-data/data-structures/simple_traits.h" using namespace std; using namespace persistent_data; From 344f4b1e08a5e49db5f7bb960b84222b4cb6499c Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 31 Jan 2014 13:43:39 +0000 Subject: [PATCH 020/165] Factor out base/indented_stream.h --- base/indented_stream.h | 48 +++++++++++++++++++++++++++ caching/xml_format.cc | 59 ++++++++++++--------------------- era/xml_format.cc | 53 +++++++++++------------------ thin-provisioning/xml_format.cc | 53 +++++++++++------------------ 4 files changed, 109 insertions(+), 104 deletions(-) create mode 100644 base/indented_stream.h diff --git a/base/indented_stream.h b/base/indented_stream.h new file mode 100644 index 0000000..b47bca2 --- /dev/null +++ b/base/indented_stream.h @@ -0,0 +1,48 @@ +#ifndef BASE_INDENTED_STREAM_H +#define BASE_INDENTED_STREAM_H + +#include + +//---------------------------------------------------------------- + +namespace { + class indented_stream { + public: + indented_stream(std::ostream &out) + : out_(out), + indent_(0) { + } + + void indent() { + for (unsigned i = 0; i < indent_ * 2; i++) + out_ << ' '; + } + + void inc() { + indent_++; + } + + void dec() { + indent_--; + } + + template + indented_stream &operator <<(T const &t) { + out_ << t; + return *this; + } + + indented_stream &operator <<(std::ostream &(*fp)(std::ostream &)) { + out_ << fp; + return *this; + } + + private: + std::ostream &out_; + unsigned indent_; + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/caching/xml_format.cc b/caching/xml_format.cc index b03e997..cb03018 100644 --- a/caching/xml_format.cc +++ b/caching/xml_format.cc @@ -1,4 +1,5 @@ #include "base/base64.h" +#include "base/indented_stream.h" #include "caching/xml_format.h" #include @@ -18,8 +19,7 @@ namespace { class xml_emitter : public emitter { public: xml_emitter(ostream &out) - : out_(out), - indent_(0) { + : out_(out) { } void begin_superblock(std::string const &uuid, @@ -27,37 +27,37 @@ namespace { block_address nr_cache_blocks, std::string const &policy, size_t hint_width) { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } virtual void end_superblock() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } virtual void begin_mappings() { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } virtual void end_mappings() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } virtual void mapping(block_address cblock, block_address oblock, bool dirty) { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } virtual void end_hints() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } @@ -81,7 +81,7 @@ namespace { vector const &data) { using namespace base; - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } virtual void end_discards() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } virtual void discard(block_address dblock_b, block_address dblock_e) { - indent(); + out_.indent(); out_ << "" << endl; } @@ -111,22 +111,7 @@ namespace { return v ? "true" : "false"; } - // FIXME: factor out a common class with the thin_provisioning emitter - void indent() { - for (unsigned i = 0; i < indent_ * 2; i++) - out_ << ' '; - } - - void inc() { - indent_++; - } - - void dec() { - indent_--; - } - - ostream &out_; - unsigned indent_; + indented_stream out_; }; //-------------------------------- diff --git a/era/xml_format.cc b/era/xml_format.cc index 924a93b..0878410 100644 --- a/era/xml_format.cc +++ b/era/xml_format.cc @@ -1,5 +1,7 @@ #include "era/xml_format.h" +#include "base/indented_stream.h" + using namespace boost; using namespace era; using namespace persistent_data; @@ -11,82 +13,67 @@ namespace { class xml_emitter : public emitter { public: xml_emitter(ostream &out) - : out_(out), - indent_(0) { + : out_(out) { } void begin_superblock(std::string const &uuid, uint32_t block_size, pd::block_address nr_blocks, uint32_t current_era) { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + << " current_era=\"" << current_era << "\">"; + out_ << endl; + out_.inc(); } void end_superblock() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } void begin_writeset(uint32_t era, uint32_t nr_bits) { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } void writeset_bit(uint32_t bit, bool value) { - indent(); + out_.indent(); // FIXME: collect all the bits, then uuencode out_ << "" << endl; } void end_writeset() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } void begin_era_array() { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } void era(pd::block_address block, uint32_t era) { - indent(); + out_.indent(); out_ << "" << endl; } void end_era_array() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } private: - // FIXME: factor out a common class with the thin_provisioning emitter - void indent() { - for (unsigned i = 0; i < indent_ * 2; i++) - out_ << ' '; - } - - void inc() { - indent_++; - } - - void dec() { - indent_--; - } - - ostream &out_; - unsigned indent_; + indented_stream out_; }; } diff --git a/thin-provisioning/xml_format.cc b/thin-provisioning/xml_format.cc index 55007ca..2767b9a 100644 --- a/thin-provisioning/xml_format.cc +++ b/thin-provisioning/xml_format.cc @@ -17,6 +17,7 @@ // . #include "xml_format.h" +#include "base/indented_stream.h" #include #include @@ -41,8 +42,7 @@ namespace { class xml_emitter : public emitter { public: xml_emitter(ostream &out) - : out_(out), - indent_(0) { + : out_(out) { } void begin_superblock(string const &uuid, @@ -51,7 +51,7 @@ namespace { uint32_t data_block_size, uint64_t nr_data_blocks, boost::optional metadata_snap) { - indent(); + out_.indent(); out_ << "" - << endl; - inc(); + out_ << "\">" << endl; + out_.inc(); } void end_superblock() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } @@ -77,40 +76,40 @@ namespace { uint64_t trans_id, uint64_t creation_time, uint64_t snap_time) { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } void end_device() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } void begin_named_mapping(string const &name) { - indent(); + out_.indent(); out_ << "" << endl; - inc(); + out_.inc(); } void end_named_mapping() { - dec(); - indent(); + out_.dec(); + out_.indent(); out_ << "" << endl; } void identifier(string const &name) { - indent(); + out_.indent(); out_ << "" << endl; } void range_map(uint64_t origin_begin, uint64_t data_begin, uint32_t time, uint64_t len) { - indent(); + out_.indent(); out_ << " Date: Fri, 31 Jan 2014 16:31:27 +0000 Subject: [PATCH 021/165] era_invalidate --- era/era_invalidate.cc | 135 ++++++++++++++++++++++++++++++++++++++---- era/metadata.cc | 9 ++- era/metadata.h | 4 +- era/superblock.cc | 12 ++++ era/superblock.h | 3 + 5 files changed, 150 insertions(+), 13 deletions(-) diff --git a/era/era_invalidate.cc b/era/era_invalidate.cc index c96b286..9e63578 100644 --- a/era/era_invalidate.cc +++ b/era/era_invalidate.cc @@ -4,12 +4,16 @@ #include #include "version.h" +#include "base/indented_stream.h" #include "era/era_array.h" #include "era/writeset_tree.h" #include "era/metadata.h" #include "era/xml_format.h" #include "persistent-data/file_utils.h" +#include + +using namespace boost; using namespace era; using namespace std; @@ -17,14 +21,97 @@ using namespace std; namespace { struct flags { - flags() { + flags() + : metadata_snapshot_(false) { } + + bool metadata_snapshot_; + optional era_threshold_; }; //-------------------------------- - void emit_blocks() { - + void walk_array(era_array const &array, uint32_t nr_blocks, + uint32_t threshold, set &blocks) { + for (uint32_t b = 0; b < nr_blocks; b++) { + uint32_t era = array.get(b); + if (era >= threshold) + blocks.insert(b); + } + } + + class writesets_marked_since : public writeset_tree_detail::writeset_visitor { + public: + writesets_marked_since(uint32_t threshold, set &blocks) + : current_era_(0), + threshold_(threshold), + blocks_(blocks) { + } + + void writeset_begin(uint32_t era, uint32_t nr_bits) { + current_era_ = era; + } + + void bit(uint32_t index, bool value) { + if (value && current_era_ >= threshold_) + blocks_.insert(index); + } + + void writeset_end() { + } + + private: + uint32_t current_era_; + uint32_t threshold_; + set &blocks_; + }; + + void raise_metadata_damage() { + throw std::runtime_error("metadata contains errors (run era_check for details)."); + } + + struct fatal_writeset_tree_damage : public writeset_tree_detail::damage_visitor { + void visit(writeset_tree_detail::missing_eras const &d) { + raise_metadata_damage(); + } + + void visit(writeset_tree_detail::damaged_writeset const &d) { + raise_metadata_damage(); + } + }; + + void walk_writesets(metadata const &md, uint32_t threshold, set &result) { + writesets_marked_since v(threshold, result); + fatal_writeset_tree_damage dv; + + walk_writeset_tree(md.tm_, *md.writeset_tree_, v, dv); + } + + void mark_blocks_since(metadata const &md, uint32_t threshold, set &result) { + walk_array(*md.era_array_, md.sb_.nr_blocks, threshold, result); + walk_writesets(md, threshold, result); + } + + //-------------------------------- + + void emit_blocks(ostream &out, set const &blocks) { + indented_stream o(out); + + o.indent(); + o << "" << endl; + + o.inc(); + { + set::const_iterator it; + for (it = blocks.begin(); it != blocks.end(); ++it) { + o.indent(); + o << "" << endl; + } + } + o.dec(); + + o.indent(); + o << "" << endl; } //-------------------------------- @@ -37,16 +124,28 @@ namespace { int invalidate(string const &dev, string const &output, flags const &fs) { try { + set blocks; block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_ONLY); - metadata::ptr md(new metadata(bm, metadata::OPEN)); - if (want_stdout(output)) { - emitter::ptr e = create_xml_emitter(cout); - //emit_blocks(md, e, fs); + if (fs.metadata_snapshot_) { + superblock sb = read_superblock(bm); + if (!sb.metadata_snap) + throw runtime_error("no metadata snapshot taken."); + + metadata::ptr md(new metadata(bm, *sb.metadata_snap)); + mark_blocks_since(*md, *fs.era_threshold_, blocks); + } else { + metadata::ptr md(new metadata(bm, metadata::OPEN)); + mark_blocks_since(*md, *fs.era_threshold_, blocks); + } + + if (want_stdout(output)) + emit_blocks(cout, blocks); + + else { ofstream out(output.c_str()); - emitter::ptr e = create_xml_emitter(out); - //emit_blocks(md, e, fs); + emit_blocks(out, blocks); } } catch (std::exception &e) { @@ -58,7 +157,7 @@ namespace { } void usage(ostream &out, string const &cmd) { - out << "Usage: " << cmd << " [options] {device|file}" << endl + out << "Usage: " << cmd << " [options] --written-since {device|file}" << endl << "Options:" << endl << " {-h|--help}" << endl << " {-o }" << endl @@ -79,11 +178,21 @@ int main(int argc, char **argv) { "help", no_argument, NULL, 'h' }, { "output", required_argument, NULL, 'o' }, { "version", no_argument, NULL, 'V' }, + { "metadata-snapshot", no_argument, NULL, 1}, + { "written-since", required_argument, NULL, 2}, { NULL, no_argument, NULL, 0 } }; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch(c) { + case 1: + fs.metadata_snapshot_ = true; + break; + + case 2: + fs.era_threshold_ = lexical_cast(optarg); + break; + case 'h': usage(cout, basename(argv[0])); return 0; @@ -108,6 +217,12 @@ int main(int argc, char **argv) return 1; } + if (!fs.era_threshold_) { + cerr << "Please specify --written-since" << endl; + usage(cerr, basename(argv[0])); + return 1; + } + return invalidate(argv[optind], output, fs); } diff --git a/era/metadata.cc b/era/metadata.cc index ab2eb7b..baae96a 100644 --- a/era/metadata.cc +++ b/era/metadata.cc @@ -41,11 +41,16 @@ metadata::metadata(block_manager<>::ptr bm, open_type ot) } } +metadata::metadata(block_manager<>::ptr bm, block_address metadata_snap) +{ + open_metadata(bm); +} + void -metadata::open_metadata(block_manager<>::ptr bm) +metadata::open_metadata(block_manager<>::ptr bm, block_address loc) { tm_ = open_tm(bm); - sb_ = read_superblock(tm_->get_bm()); + sb_ = read_superblock(tm_->get_bm(), loc); writeset_tree_ = writeset_tree::ptr(new writeset_tree(tm_, sb_.writeset_tree_root, diff --git a/era/metadata.h b/era/metadata.h index ca31a50..ea3a132 100644 --- a/era/metadata.h +++ b/era/metadata.h @@ -28,6 +28,7 @@ namespace era { typedef boost::shared_ptr ptr; metadata(block_manager<>::ptr bm, open_type ot); + metadata(block_manager<>::ptr bm, block_address metadata_snap); void commit(bool clean_shutdown = true); typedef persistent_data::transaction_manager tm; @@ -38,7 +39,8 @@ namespace era { era_array::ptr era_array_; private: - void open_metadata(block_manager<>::ptr bm); + void open_metadata(block_manager<>::ptr bm, + block_address loc = SUPERBLOCK_LOCATION); }; }; diff --git a/era/superblock.cc b/era/superblock.cc index 0572314..1bd1a4f 100644 --- a/era/superblock.cc +++ b/era/superblock.cc @@ -4,6 +4,7 @@ #include "persistent-data/errors.h" using namespace base; +using namespace boost; using namespace era; using namespace superblock_damage; using namespace persistent_data; @@ -37,6 +38,8 @@ namespace { le64 writeset_tree_root; le64 era_array_root; + le64 metadata_snap; + } __attribute__ ((packed)); struct superblock_traits { @@ -143,6 +146,11 @@ superblock_traits::unpack(disk_type const &disk, value_type &value) era_detail_traits::unpack(disk.current_detail, value.current_detail); value.writeset_tree_root = to_cpu(disk.writeset_tree_root); value.era_array_root = to_cpu(disk.era_array_root); + + block_address ms = to_cpu(disk.metadata_snap); + value.metadata_snap = (ms == SUPERBLOCK_LOCATION) ? + optional() : + optional(ms); } void @@ -163,6 +171,10 @@ superblock_traits::pack(value_type const &value, disk_type &disk) era_detail_traits::pack(value.current_detail, disk.current_detail); disk.writeset_tree_root = to_disk(value.writeset_tree_root); disk.era_array_root = to_disk(value.era_array_root); + + disk.metadata_snap = value.metadata_snap ? + to_disk(*value.metadata_snap) : + to_disk(SUPERBLOCK_LOCATION); } //-------------------------------- diff --git a/era/superblock.h b/era/superblock.h index 346582e..bea7af1 100644 --- a/era/superblock.h +++ b/era/superblock.h @@ -3,6 +3,7 @@ #include "persistent-data/block.h" #include "era/era_detail.h" +#include #include @@ -63,6 +64,8 @@ namespace era { // Big array holding the digested era/block info. uint64_t era_array_root; + + boost::optional metadata_snap; }; //-------------------------------- From 54f38e6702c860af9363ca1125740321ec5da192 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 12 Feb 2014 11:49:41 +0000 Subject: [PATCH 022/165] [era_invalidate] Change to output ranges. --- era/era_invalidate.cc | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/era/era_invalidate.cc b/era/era_invalidate.cc index 9e63578..42bf67b 100644 --- a/era/era_invalidate.cc +++ b/era/era_invalidate.cc @@ -94,6 +94,20 @@ namespace { //-------------------------------- + template + pair next_run(Iterator &it, Iterator end) { + uint32_t b, e; + + b = *it++; + e = b + 1; + while (it != end && *it == e) { + e++; + it++; + } + + return make_pair(b, e); + } + void emit_blocks(ostream &out, set const &blocks) { indented_stream o(out); @@ -102,10 +116,17 @@ namespace { o.inc(); { - set::const_iterator it; - for (it = blocks.begin(); it != blocks.end(); ++it) { + set::const_iterator it = blocks.begin(); + while (it != blocks.end()) { o.indent(); - o << "" << endl; + + pair range = next_run(it, blocks.end()); + if (range.second - range.first == 1) + o << "" << endl; + + else + o << "" << endl; } } o.dec(); From 49e59ca781a8259f5be7f20c270519230b2595e1 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 14 Feb 2014 14:35:25 +0000 Subject: [PATCH 023/165] [era_dump] add --logical --- era/era_dump.cc | 16 +++++-- era/metadata_dump.cc | 107 +++++++++++++++++++++++++++++++++++++------ era/metadata_dump.h | 3 +- 3 files changed, 106 insertions(+), 20 deletions(-) diff --git a/era/era_dump.cc b/era/era_dump.cc index a2c82aa..c9e14ad 100644 --- a/era/era_dump.cc +++ b/era/era_dump.cc @@ -19,10 +19,12 @@ using namespace std; namespace { struct flags { flags() - : repair_(false) { + : repair_(false), + logical_(false) { } bool repair_; + bool logical_; }; //-------------------------------- @@ -40,11 +42,11 @@ namespace { if (want_stdout(output)) { emitter::ptr e = create_xml_emitter(cout); - metadata_dump(md, e, fs.repair_); + metadata_dump(md, e, fs.repair_, fs.logical_); } else { ofstream out(output.c_str()); emitter::ptr e = create_xml_emitter(out); - metadata_dump(md, e, fs.repair_); + metadata_dump(md, e, fs.repair_, fs.logical_); } } catch (std::exception &e) { @@ -61,7 +63,8 @@ namespace { << " {-h|--help}" << endl << " {-o }" << endl << " {-V|--version}" << endl - << " {--repair}" << endl; + << " {--repair}" << endl + << " {--logical}" << endl; } } @@ -79,6 +82,7 @@ int main(int argc, char **argv) { "output", required_argument, NULL, 'o' }, { "version", no_argument, NULL, 'V' }, { "repair", no_argument, NULL, 1 }, + { "logical", no_argument, NULL, 2 }, { NULL, no_argument, NULL, 0 } }; @@ -88,6 +92,10 @@ int main(int argc, char **argv) fs.repair_ = true; break; + case 2: + fs.logical_ = true; + break; + case 'h': usage(cout, basename(argv[0])); return 0; diff --git a/era/metadata_dump.cc b/era/metadata_dump.cc index e7eac5f..0d48ebf 100644 --- a/era/metadata_dump.cc +++ b/era/metadata_dump.cc @@ -39,6 +39,34 @@ namespace { emitter::ptr e_; }; + class writeset_tree_collator : public writeset_tree_detail::writeset_visitor { + public: + writeset_tree_collator(map &exceptions) + : exceptions_(exceptions), + current_era_(0) { + } + + virtual void writeset_begin(uint32_t era, uint32_t nr_bits) { + current_era_ = era; + } + + virtual void bit(uint32_t bit, bool value) { + if (value) { + map::const_iterator it = exceptions_.find(bit); + if (it == exceptions_.end() || it->second < current_era_) + exceptions_.insert(make_pair(bit, current_era_)); + } + } + + virtual void writeset_end() { + } + + private: + map &exceptions_; + uint32_t current_era_; + }; + + struct ignore_writeset_tree_damage : public writeset_tree_detail::damage_visitor { void visit(writeset_tree_detail::missing_eras const &d) { } @@ -61,16 +89,22 @@ namespace { class era_array_emitter : public era_array_visitor { public: - era_array_emitter(emitter::ptr e) - : e_(e) { + era_array_emitter(emitter::ptr e, map const &exceptions) + : e_(e), + exceptions_(exceptions) { } virtual void visit(uint32_t index, uint32_t era) { - e_->era(index, era); + map::const_iterator it = exceptions_.find(index); + if (it != exceptions_.end() && it->second > era) + e_->era(index, it->second); + else + e_->era(index, era); } private: emitter::ptr e_; + map exceptions_; }; struct ignore_era_array_damage : public era_array_detail::damage_visitor { @@ -90,18 +124,9 @@ namespace { raise_metadata_damage(); } }; -} -//---------------------------------------------------------------- - -void -era::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair) -{ - superblock const &sb = md->sb_; - - e->begin_superblock(to_string(sb.uuid), sb.data_block_size, - sb.nr_blocks, - sb.current_era); + void + dump(metadata::ptr md, emitter::ptr e, bool repair) { { writeset_tree_emitter visitor(e); @@ -117,7 +142,8 @@ era::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair) e->begin_era_array(); { - era_array_emitter visitor(e); + map exceptions; + era_array_emitter visitor(e, exceptions); ignore_era_array_damage ignore; fatal_era_array_damage fatal; @@ -129,6 +155,57 @@ era::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair) } e->end_era_array(); } + + void dump_logical(metadata::ptr md, emitter::ptr e, bool repair) + { + // This will potentially use a lot of memory, but I don't + // see a way around it. + map exceptions; + + { + writeset_tree_collator visitor(exceptions); + + ignore_writeset_tree_damage ignore; + fatal_writeset_tree_damage fatal; + writeset_tree_detail::damage_visitor &dv = repair ? + static_cast(ignore) : + static_cast(fatal); + + walk_writeset_tree(md->tm_, *md->writeset_tree_, visitor, dv); + } + + e->begin_era_array(); + { + era_array_emitter visitor(e, exceptions); + + ignore_era_array_damage ignore; + fatal_era_array_damage fatal; + era_array_detail::damage_visitor &dv = repair ? + static_cast(ignore) : + static_cast(fatal); + + walk_era_array(*md->era_array_, visitor, dv); + } + e->end_era_array(); + } +} + +//---------------------------------------------------------------- + +void +era::metadata_dump(metadata::ptr md, emitter::ptr e, + bool repair, bool logical) +{ + superblock const &sb = md->sb_; + e->begin_superblock(to_string(sb.uuid), sb.data_block_size, + sb.nr_blocks, + sb.current_era); + { + if (logical) + dump_logical(md, e, repair); + else + dump(md, e, repair); + } e->end_superblock(); } diff --git a/era/metadata_dump.h b/era/metadata_dump.h index 59d9d67..43cf81f 100644 --- a/era/metadata_dump.h +++ b/era/metadata_dump.h @@ -7,7 +7,8 @@ //---------------------------------------------------------------- namespace era { - void metadata_dump(metadata::ptr md, emitter::ptr out, bool repair); + void metadata_dump(metadata::ptr md, emitter::ptr out, + bool repair, bool logical); } //---------------------------------------------------------------- From fe019f6946c382c2c8b766c265ac67e6e7c7660f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 6 Mar 2014 16:30:33 +0000 Subject: [PATCH 024/165] install era tools --- Makefile.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.in b/Makefile.in index ca23474..27d0292 100644 --- a/Makefile.in +++ b/Makefile.in @@ -431,6 +431,9 @@ install: $(PROGRAMS) $(INSTALL_PROGRAM) thin_restore $(BINDIR) $(INSTALL_PROGRAM) thin_rmap $(BINDIR) $(INSTALL_PROGRAM) thin_metadata_size $(BINDIR) + $(INSTALL_PROGRAM) era_check $(BINDIR) + $(INSTALL_PROGRAM) era_dump $(BINDIR) + $(INSTALL_PROGRAM) era_invalidate $(BINDIR) $(INSTALL_DIR) $(MANPATH)/man8 $(INSTALL_DATA) man8/cache_check.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/cache_dump.8 $(MANPATH)/man8 From c3249ff7570fd4e8c12f4ff7f57b15dee76c97e9 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 27 Mar 2014 12:00:17 +0000 Subject: [PATCH 025/165] [thin_check] --clear-needs-check-flag --- features/thin_check.feature | 15 +++++++--- thin-provisioning/superblock.cc | 24 ++++++++++++++++ thin-provisioning/superblock.h | 10 ++++++- thin-provisioning/thin_check.cc | 50 +++++++++++++++++++++++++-------- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/features/thin_check.feature b/features/thin_check.feature index 3b896a1..8b910af 100644 --- a/features/thin_check.feature +++ b/features/thin_check.feature @@ -17,9 +17,10 @@ Feature: thin_check {-q|--quiet} {-h|--help} {-V|--version} - {--super-block-only} - {--skip-mappings} + {--clear-needs-check-flag} {--ignore-non-fatal-errors} + {--skip-mappings} + {--super-block-only} """ Scenario: print help @@ -32,9 +33,10 @@ Feature: thin_check {-q|--quiet} {-h|--help} {-V|--version} - {--super-block-only} - {--skip-mappings} + {--clear-needs-check-flag} {--ignore-non-fatal-errors} + {--skip-mappings} + {--super-block-only} """ Scenario: Unrecognised option should cause failure @@ -77,3 +79,8 @@ Feature: thin_check When I run thin_check with --quiet Then it should fail And it should give no output + + Scenario: Accepts --clear-needs-check-flag + Given valid metadata + When I run thin_check with --clear-needs-check-flag + Then it should pass diff --git a/thin-provisioning/superblock.cc b/thin-provisioning/superblock.cc index b89ac64..4412696 100644 --- a/thin-provisioning/superblock.cc +++ b/thin-provisioning/superblock.cc @@ -113,6 +113,23 @@ thin_provisioning::superblock_validator() namespace thin_provisioning { namespace superblock_detail { + namespace { + unsigned const NEEDS_CHECK_BIT = 0; + } + + bool + superblock::get_needs_check_flag() const { + return flags_ & (1 << NEEDS_CHECK_BIT); + } + + void + superblock::set_needs_check_flag(bool val) { + if (val) + flags_ |= (1 << NEEDS_CHECK_BIT); + else + flags_ &= ~(1 << NEEDS_CHECK_BIT); + }; + superblock_corruption::superblock_corruption(std::string const &desc) : desc_(desc) { } @@ -144,6 +161,13 @@ namespace thin_provisioning { return read_superblock(bm, SUPERBLOCK_LOCATION); } + void write_superblock(block_manager<>::ptr bm, superblock_detail::superblock const &sb) + { + block_manager<>::write_ref w = bm->write_lock(SUPERBLOCK_LOCATION, superblock_validator()); + superblock_disk *disk = reinterpret_cast(w.data().raw()); + superblock_traits::pack(sb, *disk); + } + void check_superblock(block_manager<>::ptr bm, superblock_detail::damage_visitor &visitor) { diff --git a/thin-provisioning/superblock.h b/thin-provisioning/superblock.h index 3a3d90a..354e83c 100644 --- a/thin-provisioning/superblock.h +++ b/thin-provisioning/superblock.h @@ -81,6 +81,9 @@ namespace thin_provisioning { uint32_t compat_flags_; uint32_t compat_ro_flags_; uint32_t incompat_flags_; + + bool get_needs_check_flag() const; + void set_needs_check_flag(bool val = true); }; struct superblock_traits { @@ -126,7 +129,12 @@ namespace thin_provisioning { // FIXME: should we put init_superblock in here too? superblock_detail::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_detail::superblock read_superblock(persistent_data::block_manager<>::ptr bm, + persistent_data::block_address location); + + void write_superblock(persistent_data::block_manager<>::ptr bm, + superblock_detail::superblock const &sb); + void check_superblock(persistent_data::block_manager<>::ptr bm, superblock_detail::damage_visitor &visitor); } diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc index ea5e56e..f5b52c4 100644 --- a/thin-provisioning/thin_check.cc +++ b/thin-provisioning/thin_check.cc @@ -148,6 +148,15 @@ namespace { //-------------------------------- struct flags { + flags() + : check_device_tree(true), + check_mapping_tree_level1(true), + check_mapping_tree_level2(true), + ignore_non_fatal_errors(false), + quiet(false), + clear_needs_check_flag_on_success(false) { + } + bool check_device_tree; bool check_mapping_tree_level1; bool check_mapping_tree_level2; @@ -155,6 +164,7 @@ namespace { bool ignore_non_fatal_errors; bool quiet; + bool clear_needs_check_flag_on_success; }; error_state metadata_check(string const &path, flags fs) { @@ -214,12 +224,29 @@ namespace { dev_rep.get_error())); } - int check(string const &path, flags fs) { + void clear_needs_check(string const &path) { + block_manager<>::ptr bm = open_bm(path, block_io<>::READ_WRITE); + + superblock_detail::superblock sb = read_superblock(bm); + sb.set_needs_check_flag(false); + write_superblock(bm, sb); + } + + bool check(string const &path, flags fs) { error_state err; + bool success = false; try { err = metadata_check(path, fs); + if (fs.ignore_non_fatal_errors) + success = (err == FATAL) ? 1 : 0; + else + success = (err == NO_ERROR) ? 0 : 1; + + if (success && fs.clear_needs_check_flag_on_success) + clear_needs_check(path); + } catch (std::exception &e) { if (!fs.quiet) cerr << e.what() << endl; @@ -227,10 +254,7 @@ namespace { return 1; } - if (fs.ignore_non_fatal_errors) - return (err == FATAL) ? 1 : 0; - else - return (err == NO_ERROR) ? 0 : 1; + return success; } void usage(ostream &out, string const &cmd) { @@ -239,9 +263,10 @@ namespace { << " {-q|--quiet}" << endl << " {-h|--help}" << endl << " {-V|--version}" << endl - << " {--super-block-only}" << endl + << " {--clear-needs-check-flag}" << endl + << " {--ignore-non-fatal-errors}" << endl << " {--skip-mappings}" << endl - << " {--ignore-non-fatal-errors}" << endl; + << " {--super-block-only}" << endl; } } @@ -249,11 +274,6 @@ int main(int argc, char **argv) { int c; flags fs; - fs.check_device_tree = true; - fs.check_mapping_tree_level1 = true, - fs.check_mapping_tree_level2 = true, - fs.ignore_non_fatal_errors = false, - fs.quiet = false; char const shortopts[] = "qhV"; option const longopts[] = { @@ -263,6 +283,7 @@ int main(int argc, char **argv) { "super-block-only", no_argument, NULL, 1}, { "skip-mappings", no_argument, NULL, 2}, { "ignore-non-fatal-errors", no_argument, NULL, 3}, + { "clear-needs-check-flag", no_argument, NULL, 4 }, { NULL, no_argument, NULL, 0 } }; @@ -297,6 +318,11 @@ int main(int argc, char **argv) fs.ignore_non_fatal_errors = true; break; + case 4: + // clear needs-check flag + fs.clear_needs_check_flag_on_success = true; + break; + default: usage(cerr, basename(argv[0])); return 1; From 8c24419238e4d02e2d26c928da3c88acb4e85d07 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 27 Mar 2014 12:23:00 +0000 Subject: [PATCH 026/165] Knock out the era_superblock_t for now. It's still got some bloom stuff in it. Will tidy up after release. --- unit-tests/Makefile.in | 1 - 1 file changed, 1 deletion(-) diff --git a/unit-tests/Makefile.in b/unit-tests/Makefile.in index 4310a01..585ddff 100644 --- a/unit-tests/Makefile.in +++ b/unit-tests/Makefile.in @@ -59,7 +59,6 @@ TEST_SOURCE=\ unit-tests/cache_superblock_t.cc \ unit-tests/damage_tracker_t.cc \ unit-tests/endian_t.cc \ - unit-tests/era_superblock_t.cc \ unit-tests/rmap_visitor_t.cc \ unit-tests/run_set_t.cc \ unit-tests/space_map_t.cc \ From df8447de3d7908d3a5c327e722936023f624cf99 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 27 Mar 2014 12:24:31 +0000 Subject: [PATCH 027/165] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a45be46..0d91a54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.8 +0.3.0 From 47ec37a9fddbd6cae6bcf21d6236a65b8aa05eee Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 1 Apr 2014 16:39:45 +0100 Subject: [PATCH 028/165] [thin tools] fix ambiguous reference to 'bitset' --- persistent-data/data-structures/bitset.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index 47a47bf..5851e28 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -221,7 +221,7 @@ persistent_data::bitset::get_root() const } unsigned -bitset::get_nr_bits() const +persistent_data::bitset::get_nr_bits() const { return impl_->get_nr_bits(); } From c802bf81d5a52510d0c2686f407d3282800c6359 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 1 Apr 2014 16:40:14 +0100 Subject: [PATCH 029/165] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0d91a54..9e11b32 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.0 +0.3.1 From 7eabc78d6dc4fc8e5b96adeb80e6b2edc150b18e Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 1 Apr 2014 17:43:15 +0100 Subject: [PATCH 030/165] [era] qualify btiset --- era/writeset_tree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/era/writeset_tree.cc b/era/writeset_tree.cc index 763427e..2fe47fe 100644 --- a/era/writeset_tree.cc +++ b/era/writeset_tree.cc @@ -54,7 +54,7 @@ namespace { void visit(btree_path const &path, era_detail const &era) { era_ = path[0]; - bitset bs(tm_, era.writeset_root, era.nr_bits); + persistent_data::bitset bs(tm_, era.writeset_root, era.nr_bits); writeset_v_.writeset_begin(era_, era.nr_bits); bs.walk_bitset(*this); writeset_v_.writeset_end(); From 05b18a5b9941989f2ae40f6c7a73cfcaf7093163 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 2 Apr 2014 13:43:16 +0100 Subject: [PATCH 031/165] [thin_check] Fix bug in clear needs check logic. Mixed up bools and int returns. --- thin-provisioning/thin_check.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc index f5b52c4..e468a02 100644 --- a/thin-provisioning/thin_check.cc +++ b/thin-provisioning/thin_check.cc @@ -232,7 +232,9 @@ namespace { write_superblock(bm, sb); } - bool check(string const &path, flags fs) { + // Returns 0 on success, 1 on failure (this gets returned directly + // by main). + int check(string const &path, flags fs) { error_state err; bool success = false; @@ -244,7 +246,7 @@ namespace { else success = (err == NO_ERROR) ? 0 : 1; - if (success && fs.clear_needs_check_flag_on_success) + if (!success && fs.clear_needs_check_flag_on_success) clear_needs_check(path); } catch (std::exception &e) { From cf5d2e3225304e06410c903fdbef7ae28ca1e220 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 11 Apr 2014 14:11:59 +0100 Subject: [PATCH 032/165] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 9e11b32..d15723f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.1 +0.3.2 From 82359c4835d82cbcae0277c660a42a32940d85ae Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 11 Apr 2014 14:52:06 +0100 Subject: [PATCH 033/165] remove a typename that rhel 6.6 objects to --- persistent-data/data-structures/bloom_filter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistent-data/data-structures/bloom_filter.h b/persistent-data/data-structures/bloom_filter.h index 6703a7d..cfc1681 100644 --- a/persistent-data/data-structures/bloom_filter.h +++ b/persistent-data/data-structures/bloom_filter.h @@ -12,7 +12,7 @@ namespace persistent_data { class bloom_filter { public: typedef boost::shared_ptr ptr; - typedef typename persistent_data::transaction_manager::ptr tm_ptr; + typedef persistent_data::transaction_manager::ptr tm_ptr; // nr_bits must be a power of two bloom_filter(tm_ptr tm, From e7e3e93ec6a902a5b3431e34d9c91ea5c8c9fa66 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Sat, 17 May 2014 02:17:33 -0400 Subject: [PATCH 034/165] thin_metadata_size: Drop executable bit on source file --- thin-provisioning/thin_metadata_size.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 thin-provisioning/thin_metadata_size.c diff --git a/thin-provisioning/thin_metadata_size.c b/thin-provisioning/thin_metadata_size.c old mode 100755 new mode 100644 From 2679e7f0883388ccac416f844935739187154ff9 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Sat, 17 May 2014 03:48:13 -0400 Subject: [PATCH 035/165] thin_metadata_size: Fix long names of units Both implementations of thin_metadata_size believe units are defined like this: 1024 == kilobyte == k 1000 == kibibyte == K and so on. Fix the 1000/1024 confusion, while continuing to follow the LVM convention of using lowercase letters for binary units, so that we have: 1024 == kibibyte == k 1000 == kilobyte == K --- man8/thin_metadata_size.8 | 24 ++++++++++++------------ thin-provisioning/thin_metadata_size.c | 8 ++++---- thin-provisioning/thin_metadata_size.rb | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/man8/thin_metadata_size.8 b/man8/thin_metadata_size.8 index 80e572f..d38a0cc 100644 --- a/man8/thin_metadata_size.8 +++ b/man8/thin_metadata_size.8 @@ -15,13 +15,13 @@ Because thin provisioning pools are holding widely variable contents, this tool is needed to provide sensible initial default size. .IP "\fB\-b, \-\-block-size\fP \fIBLOCKSIZE[bskKmMgGtTpPeEzZyY]\fP" -Block size of thin provisioned devices in units of bytes,sectors,kilobytes,kibibytes,... respectively. +Block size of thin provisioned devices in units of bytes, sectors, kibibytes, kilobytes, ... respectively. Default is in sectors without a block size unit specifier. Size/number option arguments can be followed by unit specifiers in short one character -and long form (eg. -b1m or -b1megabytes). +and long form (eg. -b1m or -b1mebibytes). .IP "\fB\-s, \-\-pool-size\fP \fIPOOLSIZE[bskKmMgGtTpPeEzZyY]\fP" -Thin provisioning pool size in units of bytes,sectors,kilobytes,kibibytes,... respectively. +Thin provisioning pool size in units of bytes, sectors, kibibytes, kilobytes, ... respectively. Default is in sectors without a pool size unit specifier. .IP "\fB\-m, \-\-max-thins\fP \fI#[bskKmMgGtTpPeEzZyY]\fP" @@ -30,7 +30,7 @@ Unit identifier supported to allow for convenient entry of large quantities, eg. Default is absolute quantity without a number unit specifier. .IP "\fB\-u, \-\-unit\fP \fI{bskKmMgGtTpPeEzZyY}\fP" -Output unit specifier in units of bytes,sectors,kilobytes,kibibytes,... respectively. +Output unit specifier in units of bytes, sectors, kibibytes, kilobytes, ... respectively. Default is in sectors without an output unit specifier. .IP "\fB\-n, \-\-numeric-only [short|long]\fP" @@ -43,24 +43,24 @@ Print help and exit. Output version information and exit. .SH EXAMPLES -Calculates the thin provisioning metadata device size for block size 64 kilobytes, -pool size 1 terabytes and maximum number of thin provisioned devices and snapshots of 1000 +Calculates the thin provisioning metadata device size for block size 64 kibibytes, +pool size 1 tebibytes and maximum number of thin provisioned devices and snapshots of 1000 in units of sectors with long output: .sp .B thin_metadata_size -b64k -s1t -m1000 -Or (using the long options instead) for block size 1 gigabyte, pool size 1 petabytes and maximum number of thin provisioned devices -and snapshots of 1 million with numeric only output in units of gigabytes: +Or (using the long options instead) for block size 1 gibibyte, pool size 1 petabyte and maximum number of thin provisioned devices +and snapshots of 1 million with numeric-only output in units of gigabytes: .sp -.B thin_metadata_size --block-size=1g --pool-size=1p --max-thins=1M --unit=g --numeric-only +.B thin_metadata_size --block-size=1g --pool-size=1P --max-thins=1M --unit=G --numeric-only -Same as before (1g,1p,1M,numeric-only) but with unit specifier character appended: +Same as before (1g, 1P, 1M, numeric-only) but with unit specifier character appended: .sp -.B thin_metadata_size --block-size=1giga --pool-size=1petabytes --max-thins=1mebi --unit=g --numeric-only=short +.B thin_metadata_size --block-size=1gibi --pool-size=1petabytes --max-thins=1mega --unit=G --numeric-only=short Or with unit specifier string appended: .sp -.B thin_metadata_size --block-size=1giga --pool-size=1petabytes --max-thins=1mebi --unit=g -nlong +.B thin_metadata_size --block-size=1gibi --pool-size=1petabytes --max-thins=1mega --unit=G -nlong .SH DIAGNOSTICS .B thin_metadata_size diff --git a/thin-provisioning/thin_metadata_size.c b/thin-provisioning/thin_metadata_size.c index 2b78954..c98abc5 100644 --- a/thin-provisioning/thin_metadata_size.c +++ b/thin-provisioning/thin_metadata_size.c @@ -111,10 +111,10 @@ static struct global *init_prg(char *prg_path) unsigned u; static char *unit_chars = "bskKmMgGtTpPeEzZyY"; static char *unit_strings[] = { "bytes", "sectors", - "kilobytes", "kibibytes", "megabytes", "mebibytes", - "gigabytes", "gibibytes", "terabytes", "tebibytes", - "petabytes", "pebibytes", "exabytes", "ebibytes", - "zetabytes", "zebibytes", "yottabytes", "yobibytes", NULL }; + "kibibytes", "kilobytes", "mebibytes", "megabytes", + "gibibytes", "gigabytes", "tebibytes", "terabytes", + "pebibytes", "petabytes", "ebibytes", "exabytes", + "zebibytes", "zetabytes", "yobibytes", "yottabytes", NULL }; static unsigned long long unit_factors[ARRAY_SIZE(unit_strings) - 1] = { 1, 512, 1024, 1000 }; struct global *r = malloc(sizeof(*r)); diff --git a/thin-provisioning/thin_metadata_size.rb b/thin-provisioning/thin_metadata_size.rb index 4d10c4f..e45d7b1 100755 --- a/thin-provisioning/thin_metadata_size.rb +++ b/thin-provisioning/thin_metadata_size.rb @@ -22,10 +22,10 @@ def init_units units[:bytes_per_sector] = 512 units[:chars] = "bskKmMgGtTpPeEzZyY" units[:strings] = [ 'bytes', 'sectors', - 'kilobytes', 'kibibytes', 'megabytes', 'mebibytes', - 'gigabytes', 'gibibytes', 'terabytes', 'tebibytes', - 'petabytes', 'pebibytes', 'exabytes', 'ebibytes', - 'zetabytes', 'zebibytes', 'yottabytes', 'yobibytes' ] + 'kibibytes', 'kilobytes', 'mebibytes', 'megabytes', + 'gibibytes', 'gigabytes', 'tebibytes', 'terabytes', + 'pebibytes', 'petabytes', 'ebibytes', 'exabytes', + 'zebibytes', 'zetabytes', 'yobibytes', 'yottabytes' ] units[:factors] = [ 1, units[:bytes_per_sector] ] 1.step(8) { |e| units[:factors] += [ 1024**e, 1000**e ] } units From 256cd16ab3363d9cc537da589bee4f99b5a535d6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 6 Jun 2014 15:05:41 +0100 Subject: [PATCH 036/165] node_ref now checks the value_type sizes match, and the data fits within a single block. --- persistent-data/data-structures/btree.h | 3 +++ persistent-data/data-structures/btree.tcc | 33 ++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/persistent-data/data-structures/btree.h b/persistent-data/data-structures/btree.h index f060279..e3b6175 100644 --- a/persistent-data/data-structures/btree.h +++ b/persistent-data/data-structures/btree.h @@ -163,12 +163,15 @@ namespace persistent_data { private: static unsigned calc_max_entries(void); + void check_fits_within_block() const; void *key_ptr(unsigned i) const; void *value_ptr(unsigned i) const; block_address location_; disk_node *raw_; + + mutable bool checked_; // flag indicating we've checked the data fits in the block }; //------------------------------------------------ diff --git a/persistent-data/data-structures/btree.tcc b/persistent-data/data-structures/btree.tcc index 9674ade..42ec41e 100644 --- a/persistent-data/data-structures/btree.tcc +++ b/persistent-data/data-structures/btree.tcc @@ -87,7 +87,8 @@ namespace persistent_data { template node_ref::node_ref(block_address location, disk_node *raw) : location_(location), - raw_(raw) + raw_(raw), + checked_(false) { } @@ -330,6 +331,8 @@ namespace persistent_data { void * node_ref::key_ptr(unsigned i) const { + check_fits_within_block(); + return raw_->keys + i; } @@ -337,6 +340,8 @@ namespace persistent_data { void * node_ref::value_ptr(unsigned i) const { + check_fits_within_block(); + void *value_base = &raw_->keys[to_cpu(raw_->header.max_entries)]; return static_cast(value_base) + sizeof(typename ValueTraits::disk_type) * i; @@ -357,6 +362,32 @@ namespace persistent_data { } } + template + void + node_ref::check_fits_within_block() const { + if (checked_) + return; + + if (sizeof(typename ValueTraits::disk_type) != get_value_size()) { + std::ostringstream out; + out << "value size mismatch: expected " << sizeof(typename ValueTraits::disk_type) + << ", but got " << get_value_size() + << ". This is not the btree you are looking for." << std::endl; + throw std::runtime_error(out.str()); + } + + unsigned max = calc_max_entries(); + + if (max < get_nr_entries()) { + std::ostringstream out; + out << "Bad nr of elements: max per block = " + << max << ", actual = " << get_nr_entries() << std::endl; + throw std::runtime_error(out.str()); + } + + checked_ = true; + } + //-------------------------------- template From 87fa6dc07954fb6e897962b49b734e69634ddee8 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 9 Jun 2014 10:37:46 +0100 Subject: [PATCH 037/165] [thin_delta] --version/-V --- Makefile.in | 29 +++++++++++++++++++++++++++++ features/thin_delta.feature | 8 ++++++++ thin-provisioning/thin_delta.cc | 29 +++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 features/thin_delta.feature create mode 100644 thin-provisioning/thin_delta.cc diff --git a/Makefile.in b/Makefile.in index 27d0292..eab3006 100644 --- a/Makefile.in +++ b/Makefile.in @@ -31,6 +31,7 @@ PROGRAMS=\ era_invalidate \ \ thin_check \ + thin_delta \ thin_dump \ thin_restore \ thin_repair \ @@ -95,6 +96,7 @@ CXX_PROGRAM_SOURCE=\ caching/cache_restore.cc \ \ thin-provisioning/thin_check.cc \ + thin-provisioning/thin_delta.cc \ thin-provisioning/thin_dump.cc \ thin-provisioning/thin_restore.cc \ thin-provisioning/thin_repair.cc \ @@ -187,6 +189,28 @@ THIN_CHECK_SOURCE=\ thin-provisioning/metadata_checker.cc \ thin-provisioning/superblock.cc +THIN_DELTA_SOURCE=\ + base/error_state.cc \ + base/endian_utils.cc \ + \ + persistent-data/checksum.cc \ + persistent-data/error_set.cc \ + persistent-data/file_utils.cc \ + persistent-data/hex_dump.cc \ + persistent-data/lock_tracker.cc \ + persistent-data/data-structures/btree.cc \ + persistent-data/space_map.cc \ + persistent-data/space-maps/disk.cc \ + persistent-data/space-maps/recursive.cc \ + persistent-data/space-maps/careful_alloc.cc \ + persistent-data/transaction_manager.cc \ + \ + thin-provisioning/device_tree.cc \ + thin-provisioning/mapping_tree.cc \ + thin-provisioning/metadata.cc \ + thin-provisioning/metadata_checker.cc \ + thin-provisioning/superblock.cc + THIN_RMAP_SOURCE=\ base/endian_utils.cc \ \ @@ -213,6 +237,7 @@ THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_SOURCE)) THIN_REPAIR_OBJECTS=$(subst .cc,.o,$(THIN_REPAIR_SOURCE)) THIN_RESTORE_OBJECTS=$(subst .cc,.o,$(THIN_RESTORE_SOURCE)) THIN_CHECK_OBJECTS=$(subst .cc,.o,$(THIN_CHECK_SOURCE)) +THIN_DELTA_OBJECTS=$(subst .cc,.o,$(THIN_DELTA_SOURCE)) THIN_RMAP_OBJECTS=$(subst .cc,.o,$(THIN_RMAP_SOURCE)) thin_debug: $(THIN_DEBUG_OBJECTS) thin-provisioning/thin_debug.o @@ -235,6 +260,10 @@ thin_check: $(THIN_CHECK_OBJECTS) thin-provisioning/thin_check.o @echo " [LD] $@" $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) +thin_delta: $(THIN_DELTA_OBJECTS) thin-provisioning/thin_delta.o + @echo " [LD] $@" + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) + thin_rmap: $(THIN_RMAP_OBJECTS) thin-provisioning/thin_rmap.o @echo " [LD] $@" $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) diff --git a/features/thin_delta.feature b/features/thin_delta.feature new file mode 100644 index 0000000..9fd55b4 --- /dev/null +++ b/features/thin_delta.feature @@ -0,0 +1,8 @@ +Feature: thin_delta + Scenario: print version (-V flag) + When I run `thin_delta -V` + Then it should pass with version + + Scenario: print version (--version flag) + When I run `thin_delta --version` + Then it should pass with version diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc new file mode 100644 index 0000000..0959c2b --- /dev/null +++ b/thin-provisioning/thin_delta.cc @@ -0,0 +1,29 @@ +#include +#include + +#include "version.h" + +using namespace std; + +//---------------------------------------------------------------- + +int main(int argc, char **argv) +{ + int c; + char const shortopts[] = "V"; + option const longopts[] = { + { "version", no_argument, NULL, 'V'} + }; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + } + } + + return 0; +} + +//---------------------------------------------------------------- From 83f2cce5973ececb0159510db7f992398d3d0d67 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 9 Jun 2014 10:48:29 +0100 Subject: [PATCH 038/165] [thin_delta] -h/--help --- features/thin_delta.feature | 21 +++++++++++++++++++++ thin-provisioning/thin_delta.cc | 21 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/features/thin_delta.feature b/features/thin_delta.feature index 9fd55b4..249d4a6 100644 --- a/features/thin_delta.feature +++ b/features/thin_delta.feature @@ -6,3 +6,24 @@ Feature: thin_delta Scenario: print version (--version flag) When I run `thin_delta --version` Then it should pass with version + + Scenario: print help + When I run `thin_delta --help` + Then it should pass with: + + """ + Usage: thin_delta [options] + Options: + {-h|--help} + {-V|--version} + """ + + Scenario: print help + When I run `thin_delta -h` + Then it should pass with: + """ + Usage: thin_delta [options] + Options: + {-h|--help} + {-V|--version} + """ diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 0959c2b..969b714 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -1,5 +1,6 @@ #include #include +#include #include "version.h" @@ -7,16 +8,32 @@ using namespace std; //---------------------------------------------------------------- +namespace { + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options]" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-V|--version}" << endl; + } +} + +//---------------------------------------------------------------- + int main(int argc, char **argv) { int c; - char const shortopts[] = "V"; + char const shortopts[] = "hV"; option const longopts[] = { - { "version", no_argument, NULL, 'V'} + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' } }; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch (c) { + case 'h': + usage(cout, basename(argv[0])); + return 0; + case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; From b03fa373a1073b7902fcbb022d5497b2752e4119 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 9 Jun 2014 10:51:10 +0100 Subject: [PATCH 039/165] [thin_delta] Print usage on unrecognised switch --- features/thin_delta.feature | 4 ++++ thin-provisioning/thin_delta.cc | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/features/thin_delta.feature b/features/thin_delta.feature index 249d4a6..2bfe0db 100644 --- a/features/thin_delta.feature +++ b/features/thin_delta.feature @@ -27,3 +27,7 @@ Feature: thin_delta {-h|--help} {-V|--version} """ + + Scenario: Unrecognised option should cause failure + When I run `thin_delta --unleash-the-hedeghogs` + Then it should fail diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 969b714..0c4299e 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -37,6 +37,10 @@ int main(int argc, char **argv) case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; } } From e851b359549a90e81c63403aee737ee8efb100b6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 9 Jun 2014 13:26:55 +0100 Subject: [PATCH 040/165] [thin_delta] read in the snaps and dev --- features/thin_delta.feature | 25 +++++++++- thin-provisioning/thin_delta.cc | 81 +++++++++++++++++++++++++++++---- 2 files changed, 95 insertions(+), 11 deletions(-) diff --git a/features/thin_delta.feature b/features/thin_delta.feature index 2bfe0db..4bab632 100644 --- a/features/thin_delta.feature +++ b/features/thin_delta.feature @@ -12,7 +12,7 @@ Feature: thin_delta Then it should pass with: """ - Usage: thin_delta [options] + Usage: thin_delta [options] --snap1 --snap2 Options: {-h|--help} {-V|--version} @@ -22,7 +22,7 @@ Feature: thin_delta When I run `thin_delta -h` Then it should pass with: """ - Usage: thin_delta [options] + Usage: thin_delta [options] --snap1 --snap2 Options: {-h|--help} {-V|--version} @@ -31,3 +31,24 @@ Feature: thin_delta Scenario: Unrecognised option should cause failure When I run `thin_delta --unleash-the-hedeghogs` Then it should fail + + Scenario: --snap1 must be specified + When I run `thin_delta --snap2 45 foo` + Then it should fail with: + """ + --snap1 not specified. + """ + + Scenario: --snap2 must be specified + When I run `thin_delta --snap1 45 foo` + Then it should fail with: + """ + --snap2 not specified. + """ + + Scenario: device must be specified + When I run `thin_delta --snap1 45 --snap2 50` + Then it should fail with: + """ + No input device provided. + """ diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 0c4299e..bbd1ec2 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -9,41 +11,102 @@ using namespace std; //---------------------------------------------------------------- namespace { - void usage(ostream &out, string const &cmd) { - out << "Usage: " << cmd << " [options]" << endl - << "Options:" << endl - << " {-h|--help}" << endl - << " {-V|--version}" << endl; - } + class application { + public: + application(string const &cmd) + : cmd_(cmd) { + } + + void usage(ostream &out) { + out << "Usage: " << cmd_ << " [options] --snap1 --snap2 " << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-V|--version}" << endl; + } + + void die(string const &msg) { + cerr << msg << endl; + usage(cerr); + exit(1); + } + + unsigned parse_snap(string const &str) { + try { + return boost::lexical_cast(str); + + } catch (...) { + ostringstream out; + out << "Couldn't parse snapshot designator: '" << str << "'"; + die(out.str()); + } + + return 0; // never get here + } + + private: + string cmd_; + }; + + struct flags { + boost::optional dev; + boost::optional snap1; + boost::optional snap2; + }; } //---------------------------------------------------------------- +// FIXME: add metadata snap switch + int main(int argc, char **argv) { int c; + flags fs; + application app(basename(argv[0])); + char const shortopts[] = "hV"; option const longopts[] = { { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' } + { "version", no_argument, NULL, 'V' }, + { "snap1", required_argument, NULL, 1 }, + { "snap2", required_argument, NULL, 2 }, }; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch (c) { case 'h': - usage(cout, basename(argv[0])); + app.usage(cout); return 0; case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + case 1: + fs.snap1 = app.parse_snap(optarg); + break; + + case 2: + fs.snap2 = app.parse_snap(optarg); + break; + default: - usage(cerr, basename(argv[0])); + app.usage(cerr); return 1; } } + if (argc == optind) + app.die("No input device provided."); + else + fs.dev = argv[optind]; + + if (!fs.snap1) + app.die("--snap1 not specified."); + + if (!fs.snap2) + app.die("--snap2 not specified."); + return 0; } From d1989f4a14b3462cbfc70e6dbada86ca25b44a48 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 10 Jun 2014 16:38:10 +0100 Subject: [PATCH 041/165] whitespace --- thin-provisioning/emitter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/thin-provisioning/emitter.h b/thin-provisioning/emitter.h index c7dc1e3..58658a9 100644 --- a/thin-provisioning/emitter.h +++ b/thin-provisioning/emitter.h @@ -39,6 +39,7 @@ namespace thin_provisioning { // single_map := // named_mapping := //------------------------------------------------ + class emitter { public: typedef boost::shared_ptr ptr; From d7d293ee4fd15649222143ca3bcaeeb9f43014e9 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 10 Jun 2014 16:38:20 +0100 Subject: [PATCH 042/165] thin_delta now works --- thin-provisioning/thin_delta.cc | 153 ++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 5 deletions(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index bbd1ec2..c12c084 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -6,7 +6,15 @@ #include "version.h" +#include "persistent-data/data-structures/btree_damage_visitor.h" +#include "persistent-data/run.h" +#include "persistent-data/space-maps/core.h" +#include "persistent-data/file_utils.h" +#include "thin-provisioning/superblock.h" +#include "thin-provisioning/mapping_tree.h" + using namespace std; +using namespace thin_provisioning; //---------------------------------------------------------------- @@ -30,9 +38,9 @@ namespace { exit(1); } - unsigned parse_snap(string const &str) { + uint64_t parse_snap(string const &str) { try { - return boost::lexical_cast(str); + return boost::lexical_cast(str); } catch (...) { ostringstream out; @@ -49,9 +57,143 @@ namespace { struct flags { boost::optional dev; - boost::optional snap1; - boost::optional snap2; + boost::optional snap1; + boost::optional snap2; }; + + //-------------------------------- + + block_manager<>::ptr + open_bm(string const &path) { + block_address nr_blocks = get_nr_blocks(path); + block_io<>::mode m = block_io<>::READ_ONLY; + return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m)); + } + + transaction_manager::ptr + open_tm(block_manager<>::ptr bm) { + space_map::ptr sm(new core_map(bm->get_nr_blocks())); + sm->inc(superblock_detail::SUPERBLOCK_LOCATION); + transaction_manager::ptr tm(new transaction_manager(bm, sm)); + return tm; + } + + //-------------------------------- + + class delta_visitor { + public: + delta_visitor(single_mapping_tree const &origin) + : origin_(origin) { + reset_range(); + } + + // This is slow, but easy to write. Faster would be to + // iterate both trees simultaneously. + void visit(btree_path const &path, mapping_tree_detail::block_time const &bt) { + single_mapping_tree::key k = {path[0]}; + boost::optional origin_bt = origin_.lookup(k); + if (!origin_bt || origin_bt->block_ != bt.block_) + emit(path[0], bt.block_); + } + + private: + void reset_range() { + obegin_ = oend_ = 0; + dbegin_ = dend_ = 0; + } + + void emit(uint64_t oblock, uint64_t dblock) { + if (obegin_ == oend_) { + // We're starting a new range + obegin_ = oblock; + oend_ = obegin_; + + dbegin_ = dblock; + dend_ = dbegin_; + + } else { + if (oblock != oend_ || dblock != dend_) { + // Emit the current range ... + if (oend_ - obegin_ > 1) { + cout << "" << endl; + } else { + cout << "" << endl; + } + + obegin_ = oblock; + oend_ = obegin_; + + dbegin_ = dblock; + dend_ = dbegin_; + } + } + + oend_++; + dend_++; + } + + uint64_t obegin_, oend_; + uint64_t dbegin_, dend_; + + single_mapping_tree const &origin_; + }; + + class damage_visitor { + public: + virtual void visit(btree_path const &path, btree_detail::damage const &d) { + throw std::runtime_error("damage in mapping tree, please run thin_check"); + } + }; + + void delta_(application &app, flags const &fs) { + block_manager<>::ptr bm = open_bm(*fs.dev); + transaction_manager::ptr tm = open_tm(bm); + + superblock_detail::superblock sb = read_superblock(bm); + + dev_tree dtree(tm, sb.data_mapping_root_, + mapping_tree_detail::mtree_traits::ref_counter(tm)); + + dev_tree::key k = {*fs.snap1}; + boost::optional snap1_root = dtree.lookup(k); + + if (!snap1_root) { + ostringstream out; + out << "Unable to find mapping tree for snap1 (" << *fs.snap1 << ")"; + app.die(out.str()); + } + + single_mapping_tree snap1(tm, *snap1_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); + + k[0] = *fs.snap2; + boost::optional snap2_root = dtree.lookup(k); + + if (!snap2_root) { + ostringstream out; + out << "Unable to find mapping tree for snap2 (" << *fs.snap2 << ")"; + app.die(out.str()); + } + + single_mapping_tree snap2(tm, *snap2_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); + + delta_visitor delta_v(snap1); + damage_visitor damage_v; + btree_visit_values(snap2, delta_v, damage_v); + } + + int delta(application &app, flags const &fs) { + try { + delta_(app, fs); + } catch (exception const &e) { + app.die(e.what()); + return 1; // never get here + } + + return 0; + } } //---------------------------------------------------------------- @@ -70,6 +212,7 @@ int main(int argc, char **argv) { "version", no_argument, NULL, 'V' }, { "snap1", required_argument, NULL, 1 }, { "snap2", required_argument, NULL, 2 }, + { "metadata-snap", optional_argument, NULL, 3 }, }; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { @@ -107,7 +250,7 @@ int main(int argc, char **argv) if (!fs.snap2) app.die("--snap2 not specified."); - return 0; + return delta(app, fs); } //---------------------------------------------------------------- From 72e20c2d400770f89ad6a085b1f8b413640de9d2 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 16 Jun 2014 13:41:07 +0100 Subject: [PATCH 043/165] remove some dead code --- era/metadata.cc | 9 --------- thin-provisioning/metadata.cc | 16 ---------------- 2 files changed, 25 deletions(-) diff --git a/era/metadata.cc b/era/metadata.cc index baae96a..d344b6c 100644 --- a/era/metadata.cc +++ b/era/metadata.cc @@ -16,15 +16,6 @@ namespace { transaction_manager::ptr tm(new transaction_manager(bm, sm)); return tm; } - - void - copy_space_maps(space_map::ptr lhs, space_map::ptr rhs) { - for (block_address b = 0; b < rhs->get_nr_blocks(); b++) { - uint32_t count = rhs->get_count(b); - if (count > 0) - lhs->set_count(b, rhs->get_count(b)); - } - } } metadata::metadata(block_manager<>::ptr bm, open_type ot) diff --git a/thin-provisioning/metadata.cc b/thin-provisioning/metadata.cc index fdc96e3..908d16b 100644 --- a/thin-provisioning/metadata.cc +++ b/thin-provisioning/metadata.cc @@ -56,22 +56,6 @@ namespace { lhs->set_count(b, rhs->get_count(b)); } } - - void print_superblock(superblock const &sb) { - using namespace std; - - cerr << "superblock " << sb.csum_ << endl - << "flags " << sb.flags_ << endl - << "blocknr " << sb.blocknr_ << endl - << "transaction id " << sb.trans_id_ << endl - << "data mapping root " << sb.data_mapping_root_ << endl - << "details root " << sb.device_details_root_ << endl - << "data block size " << sb.data_block_size_ << endl - << "metadata block size " << sb.metadata_block_size_ << endl - << "metadata nr blocks " << sb.metadata_nr_blocks_ << endl - << "metadata snapshot block " << sb.metadata_snap_ << endl - ; - } } //---------------------------------------------------------------- From cb6edde141afbeb54e5e7b324b6ea253fb97cb36 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 16 Jun 2014 13:41:45 +0100 Subject: [PATCH 044/165] add comment --- persistent-data/run_set.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/persistent-data/run_set.h b/persistent-data/run_set.h index a061487..b3098d6 100644 --- a/persistent-data/run_set.h +++ b/persistent-data/run_set.h @@ -99,6 +99,8 @@ namespace base { replacement.insert(run()); else { typename rset::const_iterator b = runs_.begin(); + + // Some versions of gcc give a spurious warning here. maybe last = b->end_; if (b->begin_) From ebb49db5d3f160614ffb77c552b46daabe2a59a5 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 16 Jun 2014 13:42:13 +0100 Subject: [PATCH 045/165] cache_calc_blocks; tidy up the calculation of nr blocks --- caching/cache_metadata_size.cc | 37 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/caching/cache_metadata_size.cc b/caching/cache_metadata_size.cc index 97de889..6ead5c7 100644 --- a/caching/cache_metadata_size.cc +++ b/caching/cache_metadata_size.cc @@ -93,39 +93,42 @@ namespace { return CONTINUE; } - void expand_flags(flags &fs) { - if (!fs.device_size && !fs.nr_blocks) - throw runtime_error("Please specify either --device-size and --block-size, or --nr-blocks."); + uint64_t get_nr_blocks(flags &fs) { + if (fs.device_size) { + if (!fs.block_size) + throw runtime_error("If you specify --device-size you must also give --block-size."); - if (fs.device_size && !fs.block_size) - throw runtime_error("If you specify --device-size you must also give --block-size."); - - if (fs.block_size && !fs.device_size) - throw runtime_error("If you specify --block-size you must also give --device-size."); - - if (fs.device_size && fs.block_size) { uint64_t nr_blocks = *fs.device_size / *fs.block_size; if (fs.nr_blocks) { if (nr_blocks != *fs.nr_blocks) throw runtime_error( "Contradictory arguments given, --nr-blocks doesn't match the --device-size and --block-size."); - } else - fs.nr_blocks = nr_blocks; + } + + return nr_blocks; } + + if (fs.block_size && !fs.device_size) + throw runtime_error("If you specify --block-size you must also give --device-size."); + + if (fs.nr_blocks) + return *fs.nr_blocks; + + throw runtime_error("Please specify either --device-size and --block-size, or --nr-blocks."); } uint64_t meg(uint64_t n) { return n * 2048; } - uint64_t calc_size(flags const &fs) { + uint64_t calc_size(uint64_t nr_blocks, uint32_t max_hint_width) { uint64_t const SECTOR_SIZE = 512; uint64_t const TRANSACTION_OVERHEAD = meg(4); uint64_t const BYTES_PER_BLOCK = 16; uint64_t const HINT_OVERHEAD_PER_BLOCK = 8; - uint64_t mapping_size = (*fs.nr_blocks * BYTES_PER_BLOCK) / SECTOR_SIZE; - uint64_t hint_size = (*fs.nr_blocks * (fs.max_hint_width + HINT_OVERHEAD_PER_BLOCK)) / SECTOR_SIZE; + uint64_t mapping_size = (nr_blocks * BYTES_PER_BLOCK) / SECTOR_SIZE; + uint64_t hint_size = (nr_blocks * (max_hint_width + HINT_OVERHEAD_PER_BLOCK)) / SECTOR_SIZE; return TRANSACTION_OVERHEAD + mapping_size + hint_size; } } @@ -143,8 +146,8 @@ int main(int argc, char **argv) break; } - expand_flags(fs); - cout << calc_size(fs) << " sectors" << endl; + uint64_t nr_blocks = get_nr_blocks(fs); + cout << calc_size(nr_blocks, fs.max_hint_width) << " sectors" << endl; } catch (std::exception const &e) { cerr << e.what(); From c5c699e563216c16410ce489efd3a8d90eac73a2 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 16 Jun 2014 13:44:43 +0100 Subject: [PATCH 046/165] whitespace --- caching/cache_metadata_size.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caching/cache_metadata_size.cc b/caching/cache_metadata_size.cc index 6ead5c7..7584da6 100644 --- a/caching/cache_metadata_size.cc +++ b/caching/cache_metadata_size.cc @@ -110,7 +110,7 @@ namespace { if (fs.block_size && !fs.device_size) throw runtime_error("If you specify --block-size you must also give --device-size."); - + if (fs.nr_blocks) return *fs.nr_blocks; From f80c2dc77f5d0c1ce4bd18656603b7630f3d3aea Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 16 Jun 2014 13:58:25 +0100 Subject: [PATCH 047/165] [thin_delta] Don't use endl which also flushes --- thin-provisioning/thin_delta.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index c12c084..ed32763 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -117,10 +117,10 @@ namespace { if (oend_ - obegin_ > 1) { cout << "" << endl; + << " length=\"" << oend_ - obegin_ << "\"/>" << '\n'; } else { cout << "" << endl; + << " data_block=\"" << dbegin_ << "\"/>" << '\n'; } obegin_ = oblock; From 0e62a1c4dedcf5effc1aaa3399ce3c291ea9d8a8 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 17 Jun 2014 12:39:13 +0100 Subject: [PATCH 048/165] [thin_delta] provide a more complete diff output. --- thin-provisioning/thin_delta.cc | 287 ++++++++++++++++++++++++++------ 1 file changed, 236 insertions(+), 51 deletions(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index ed32763..d0d5fee 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -80,20 +80,44 @@ namespace { //-------------------------------- - class delta_visitor { + struct mapping { + mapping() + : vbegin_(0), + dbegin_(0), + len_(0) { + } + + mapping(uint64_t vbegin, uint64_t dbegin, uint64_t len) + : vbegin_(vbegin), + dbegin_(dbegin), + len_(len) { + } + + void consume(uint64_t delta) { + delta = min(delta, len_); + vbegin_ += delta; + dbegin_ += delta; + len_ -= delta; + } + + uint64_t vbegin_, dbegin_, len_; + }; + + typedef std::deque mapping_deque; + + // Builds up an in core rep of the mappings for a device. + class mapping_recorder { public: - delta_visitor(single_mapping_tree const &origin) - : origin_(origin) { + mapping_recorder() { reset_range(); } - // This is slow, but easy to write. Faster would be to - // iterate both trees simultaneously. void visit(btree_path const &path, mapping_tree_detail::block_time const &bt) { - single_mapping_tree::key k = {path[0]}; - boost::optional origin_bt = origin_.lookup(k); - if (!origin_bt || origin_bt->block_ != bt.block_) - emit(path[0], bt.block_); + record(path[0], bt.block_); + } + + mapping_deque const &get_mappings() const { + return mappings_; } private: @@ -102,7 +126,7 @@ namespace { dbegin_ = dend_ = 0; } - void emit(uint64_t oblock, uint64_t dblock) { + void record(uint64_t oblock, uint64_t dblock) { if (obegin_ == oend_) { // We're starting a new range obegin_ = oblock; @@ -114,14 +138,7 @@ namespace { } else { if (oblock != oend_ || dblock != dend_) { // Emit the current range ... - if (oend_ - obegin_ > 1) { - cout << "" << '\n'; - } else { - cout << "" << '\n'; - } + push_mapping(obegin_, dbegin_, oend_ - obegin_); obegin_ = oblock; oend_ = obegin_; @@ -135,12 +152,18 @@ namespace { dend_++; } + void push_mapping(uint64_t vbegin, uint64_t dbegin, uint64_t len) { + mappings_.push_back(mapping(vbegin, dbegin, len)); + } + uint64_t obegin_, oend_; uint64_t dbegin_, dend_; - single_mapping_tree const &origin_; + mapping_deque mappings_; }; + //-------------------------------- + class damage_visitor { public: virtual void visit(btree_path const &path, btree_detail::damage const &d) { @@ -148,40 +171,202 @@ namespace { } }; + class diff_emitter { + public: + diff_emitter(ostream &out) + : out_(out) { + } + + void left_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) { + begin_block(LEFT_ONLY); + out_ << " \n"; + } + + void right_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) { + begin_block(RIGHT_ONLY); + out_ << " \n"; + } + + void blocks_differ(uint64_t vbegin, uint64_t left_dbegin, uint64_t right_dbegin, uint64_t len) { + begin_block(DIFFER); + out_ << " \n"; + } + + void blocks_same(uint64_t vbegin, uint64_t dbegin, uint64_t len) { + begin_block(SAME); + out_ << " \n"; + } + + void complete() { + if (current_type_) + close(*current_type_); + } + + private: + enum block_type { + LEFT_ONLY, + RIGHT_ONLY, + DIFFER, + SAME + }; + + void begin_block(block_type t) { + if (!current_type_) { + current_type_ = t; + open(t); + + } else if (*current_type_ != t) { + close(*current_type_); + current_type_ = t; + open(t); + } + } + + void open(block_type t) { + switch (t) { + case LEFT_ONLY: + out_ << "\n"; + break; + + case RIGHT_ONLY: + out_ << "\n"; + break; + + case DIFFER: + out_ << "\n"; + break; + + case SAME: + out_ << "\n"; + break; + } + } + + void close(block_type t) { + switch (t) { + case LEFT_ONLY: + out_ << "\n\n"; + break; + + case RIGHT_ONLY: + out_ << "\n\n"; + break; + + case DIFFER: + out_ << "\n\n"; + break; + + case SAME: + out_ << "\n\n"; + break; + } + + } + + boost::optional current_type_; + ostream &out_; + }; + + //---------------------------------------------------------------- + + void dump_diff(mapping_deque const &left, + mapping_deque const &right) { + + diff_emitter e(cout); + + // We iterate through both sets of mappings in parallel + // noting any differences. + mapping_deque::const_iterator left_it = left.begin(); + mapping_deque::const_iterator right_it = right.begin(); + + mapping left_mapping; + mapping right_mapping; + + while (left_it != left.end() && right_it != right.end()) { + if (!left_mapping.len_ && left_it != left.end()) + left_mapping = *left_it++; + + if (!right_mapping.len_ && right_it != right.end()) + right_mapping = *right_it++; + + while (left_mapping.len_ && right_mapping.len_) { + if (left_mapping.vbegin_ < right_mapping.vbegin_) { + uint64_t delta = min(left_mapping.len_, right_mapping.vbegin_ - left_mapping.vbegin_); + e.left_only(left_mapping.vbegin_, left_mapping.dbegin_, delta); + left_mapping.consume(delta); + + } else if (left_mapping.vbegin_ > left_mapping.vbegin_) { + uint64_t delta = min(right_mapping.len_, left_mapping.vbegin_ - right_mapping.vbegin_); + e.right_only(right_mapping.vbegin_, right_mapping.dbegin_, delta); + right_mapping.consume(delta); + + } else if (left_mapping.dbegin_ != right_mapping.dbegin_) { + uint64_t delta = min(left_mapping.len_, right_mapping.len_); + e.blocks_differ(left_mapping.vbegin_, left_mapping.dbegin_, right_mapping.dbegin_, delta); + left_mapping.consume(delta); + right_mapping.consume(delta); + + } else { + uint64_t delta = min(left_mapping.len_, right_mapping.len_); + e.blocks_same(left_mapping.vbegin_, left_mapping.dbegin_, delta); + left_mapping.consume(delta); + right_mapping.consume(delta); + } + } + } + + e.complete(); + } + void delta_(application &app, flags const &fs) { - block_manager<>::ptr bm = open_bm(*fs.dev); - transaction_manager::ptr tm = open_tm(bm); - - superblock_detail::superblock sb = read_superblock(bm); - - dev_tree dtree(tm, sb.data_mapping_root_, - mapping_tree_detail::mtree_traits::ref_counter(tm)); - - dev_tree::key k = {*fs.snap1}; - boost::optional snap1_root = dtree.lookup(k); - - if (!snap1_root) { - ostringstream out; - out << "Unable to find mapping tree for snap1 (" << *fs.snap1 << ")"; - app.die(out.str()); - } - - single_mapping_tree snap1(tm, *snap1_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); - - k[0] = *fs.snap2; - boost::optional snap2_root = dtree.lookup(k); - - if (!snap2_root) { - ostringstream out; - out << "Unable to find mapping tree for snap2 (" << *fs.snap2 << ")"; - app.die(out.str()); - } - - single_mapping_tree snap2(tm, *snap2_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); - - delta_visitor delta_v(snap1); + mapping_recorder mr1; + mapping_recorder mr2; damage_visitor damage_v; - btree_visit_values(snap2, delta_v, damage_v); + + { + block_manager<>::ptr bm = open_bm(*fs.dev); + transaction_manager::ptr tm = open_tm(bm); + + superblock_detail::superblock sb = read_superblock(bm); + + dev_tree dtree(tm, sb.data_mapping_root_, + mapping_tree_detail::mtree_traits::ref_counter(tm)); + + dev_tree::key k = {*fs.snap1}; + boost::optional snap1_root = dtree.lookup(k); + + if (!snap1_root) { + ostringstream out; + out << "Unable to find mapping tree for snap1 (" << *fs.snap1 << ")"; + app.die(out.str()); + } + + single_mapping_tree snap1(tm, *snap1_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); + + k[0] = *fs.snap2; + boost::optional snap2_root = dtree.lookup(k); + + if (!snap2_root) { + ostringstream out; + out << "Unable to find mapping tree for snap2 (" << *fs.snap2 << ")"; + app.die(out.str()); + } + + single_mapping_tree snap2(tm, *snap2_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); + btree_visit_values(snap1, mr1, damage_v); + btree_visit_values(snap2, mr2, damage_v); + } + + dump_diff(mr1.get_mappings(), mr2.get_mappings()); } int delta(application &app, flags const &fs) { From bb571045424d9eb1f62616c202c10c354b62b22e Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 17 Jun 2014 14:20:33 +0100 Subject: [PATCH 049/165] [thin_delta] By default output a simpler format that ignores the data locations. Most people will access the data via the thin device itself, so there's really no need to output the locations of the data on pool device. Doing this allows us to run together adjacent thin blocks (even if they are not adjacent on the pool dev). Simple format: The old format is available using the --verbose switch: --- thin-provisioning/thin_delta.cc | 173 +++++++++++++++++++++++++++----- 1 file changed, 146 insertions(+), 27 deletions(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index d0d5fee..186a38c 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -56,9 +56,14 @@ namespace { }; struct flags { + flags() + : verbose(false) { + } + boost::optional dev; boost::optional snap1; boost::optional snap2; + bool verbose; }; //-------------------------------- @@ -171,39 +176,140 @@ namespace { } }; + //-------------------------------- + class diff_emitter { public: diff_emitter(ostream &out) : out_(out) { } + virtual void left_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) = 0; + virtual void right_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) = 0; + virtual void blocks_differ(uint64_t vbegin, uint64_t left_dbegin, uint64_t right_dbegin, uint64_t len) = 0; + virtual void blocks_same(uint64_t vbegin, uint64_t dbegin, uint64_t len) = 0; + virtual void complete() = 0; + + protected: + ostream &out() { + return out_; + } + + private: + ostream &out_; + }; + + + class simple_emitter : public diff_emitter { + public: + simple_emitter(ostream &out) + : diff_emitter(out) { + } + + void left_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) { + add_range(LEFT_ONLY, vbegin, len); + } + + void right_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) { + add_range(RIGHT_ONLY, vbegin, len); + } + + void blocks_differ(uint64_t vbegin, uint64_t left_dbegin, uint64_t right_dbegin, uint64_t len) { + add_range(DIFFER, vbegin, len); + } + + void blocks_same(uint64_t vbegin, uint64_t dbegin, uint64_t len) { + add_range(SAME, vbegin, len); + } + + void complete() { + if (current_type_) + emit_range(); + } + + private: + enum block_type { + LEFT_ONLY, + RIGHT_ONLY, + DIFFER, + SAME + }; + + void add_range(block_type t, uint64_t vbegin, uint64_t len) { + if (current_type_ && *current_type_ == t && vbegin == vend_) { + vend_ += len; + return; + } + + emit_range(); + current_type_ = t; + vbegin_ = vbegin; + vend_ = vbegin_ + len; + } + + void emit_range() { + if (!current_type_) + return; + + switch (*current_type_) { + case LEFT_ONLY: + out() << "\n"; + } + + boost::optional current_type_; + uint64_t vbegin_, vend_; + }; + + class verbose_emitter : public diff_emitter { + public: + verbose_emitter(ostream &out) + : diff_emitter(out) { + } + void left_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) { begin_block(LEFT_ONLY); - out_ << " \n"; + out() << " \n"; } void right_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) { begin_block(RIGHT_ONLY); - out_ << " \n"; + out() << " \n"; } void blocks_differ(uint64_t vbegin, uint64_t left_dbegin, uint64_t right_dbegin, uint64_t len) { begin_block(DIFFER); - out_ << " \n"; + out() << " \n"; } void blocks_same(uint64_t vbegin, uint64_t dbegin, uint64_t len) { begin_block(SAME); - out_ << " \n"; + out() << " \n"; } void complete() { @@ -234,19 +340,19 @@ namespace { void open(block_type t) { switch (t) { case LEFT_ONLY: - out_ << "\n"; + out() << "\n"; break; case RIGHT_ONLY: - out_ << "\n"; + out() << "\n"; break; case DIFFER: - out_ << "\n"; + out() << "\n"; break; case SAME: - out_ << "\n"; + out() << "\n"; break; } } @@ -254,34 +360,32 @@ namespace { void close(block_type t) { switch (t) { case LEFT_ONLY: - out_ << "\n\n"; + out() << "\n\n"; break; case RIGHT_ONLY: - out_ << "\n\n"; + out() << "\n\n"; break; case DIFFER: - out_ << "\n\n"; + out() << "\n\n"; break; case SAME: - out_ << "\n\n"; + out() << "\n\n"; break; } } boost::optional current_type_; - ostream &out_; }; //---------------------------------------------------------------- void dump_diff(mapping_deque const &left, - mapping_deque const &right) { - - diff_emitter e(cout); + mapping_deque const &right, + diff_emitter &e) { // We iterate through both sets of mappings in parallel // noting any differences. @@ -366,7 +470,13 @@ namespace { btree_visit_values(snap2, mr2, damage_v); } - dump_diff(mr1.get_mappings(), mr2.get_mappings()); + if (fs.verbose) { + verbose_emitter e(cout); + dump_diff(mr1.get_mappings(), mr2.get_mappings(), e); + } else { + simple_emitter e(cout); + dump_diff(mr1.get_mappings(), mr2.get_mappings(), e); + } } int delta(application &app, flags const &fs) { @@ -397,7 +507,8 @@ int main(int argc, char **argv) { "version", no_argument, NULL, 'V' }, { "snap1", required_argument, NULL, 1 }, { "snap2", required_argument, NULL, 2 }, - { "metadata-snap", optional_argument, NULL, 3 }, + { "metadata-snap", no_argument, NULL, 3 }, + { "verbose", no_argument, NULL, 4 } }; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { @@ -418,6 +529,14 @@ int main(int argc, char **argv) fs.snap2 = app.parse_snap(optarg); break; + case 3: + abort(); + break; + + case 4: + fs.verbose = true; + break; + default: app.usage(cerr); return 1; From c8f408ac3c25d286a32537af87f2b6f24ea5e473 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 17 Jun 2014 15:00:38 +0100 Subject: [PATCH 050/165] [thin_delta] Update the usage --- features/thin_delta.feature | 2 ++ thin-provisioning/thin_delta.cc | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/features/thin_delta.feature b/features/thin_delta.feature index 4bab632..8658b9e 100644 --- a/features/thin_delta.feature +++ b/features/thin_delta.feature @@ -14,6 +14,7 @@ Feature: thin_delta """ Usage: thin_delta [options] --snap1 --snap2 Options: + {--verbose} {-h|--help} {-V|--version} """ @@ -24,6 +25,7 @@ Feature: thin_delta """ Usage: thin_delta [options] --snap1 --snap2 Options: + {--verbose} {-h|--help} {-V|--version} """ diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 186a38c..7cd96f8 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -26,9 +26,10 @@ namespace { } void usage(ostream &out) { - out << "Usage: " << cmd_ << " [options] --snap1 --snap2 " << endl - << "Options:" << endl - << " {-h|--help}" << endl + out << "Usage: " << cmd_ << " [options] --snap1 --snap2 \n" + << "Options:\n" + << " {--verbose}\n" + << " {-h|--help}\n" << " {-V|--version}" << endl; } From e4d82d04bdb6cb4a38f941abd109f901ff846f2e Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 17 Jun 2014 16:20:38 +0100 Subject: [PATCH 051/165] [thin_delta] Fix bug when detecting right_only mappings --- thin-provisioning/thin_delta.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 7cd96f8..396422b 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -109,6 +109,13 @@ namespace { uint64_t vbegin_, dbegin_, len_; }; + ostream &operator <<(ostream &out, mapping const &m) { + out << "mapping[vbegin = " << m.vbegin_ + << ", dbegin = " << m.dbegin_ + << ", len = " << m.len_ << "]"; + return out; + } + typedef std::deque mapping_deque; // Builds up an in core rep of the mappings for a device. @@ -409,7 +416,7 @@ namespace { e.left_only(left_mapping.vbegin_, left_mapping.dbegin_, delta); left_mapping.consume(delta); - } else if (left_mapping.vbegin_ > left_mapping.vbegin_) { + } else if (left_mapping.vbegin_ > right_mapping.vbegin_) { uint64_t delta = min(right_mapping.len_, left_mapping.vbegin_ - right_mapping.vbegin_); e.right_only(right_mapping.vbegin_, right_mapping.dbegin_, delta); right_mapping.consume(delta); From c6decff50c7fff4e6e841dfeee870cb34de3db5b Mon Sep 17 00:00:00 2001 From: Philipp Marek Date: Mon, 26 May 2014 15:36:30 +0200 Subject: [PATCH 052/165] [doc] Fix a typo and a docu bug. --- man8/thin_dump.8 | 2 +- man8/thin_rmap.8 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man8/thin_dump.8 b/man8/thin_dump.8 index 4827e1a..a8c92fe 100644 --- a/man8/thin_dump.8 +++ b/man8/thin_dump.8 @@ -47,7 +47,7 @@ Output version information and exit. Dumps the thin provisioning metadata on logical volume /dev/vg/metadata to standard output in human readable format: .sp -.B thin_dump -f human_redable /dev/vg/metadata +.B thin_dump -f human_readable /dev/vg/metadata Dumps the thin provisioning metadata on logical volume /dev/vg/metadata to standard output in XML format: diff --git a/man8/thin_rmap.8 b/man8/thin_rmap.8 index 684c86c..125e49c 100644 --- a/man8/thin_rmap.8 +++ b/man8/thin_rmap.8 @@ -29,7 +29,7 @@ Output version information and exit. output reverse map for pool blocks 5..45 (denotes blocks 5 to 44 inclusive, but not block 45) .sp -.B thin_rmap -r 5..45 /dev/vg/pool +.B thin_rmap --region 5..45 /dev/vg/pool .SH DIAGNOSTICS .B thin_rmap From d3687db3a8eb57ac45fcfc757a98a4bf56a9d1ae Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 26 Jun 2014 15:26:39 +0100 Subject: [PATCH 053/165] [bloom filter] remove an unused data member --- persistent-data/data-structures/bloom_filter.h | 1 - 1 file changed, 1 deletion(-) diff --git a/persistent-data/data-structures/bloom_filter.h b/persistent-data/data-structures/bloom_filter.h index cfc1681..da91088 100644 --- a/persistent-data/data-structures/bloom_filter.h +++ b/persistent-data/data-structures/bloom_filter.h @@ -35,7 +35,6 @@ namespace persistent_data { void fill_probes(block_address b, vector &probes) const; tm_ptr tm_; - unsigned nr_bits_; persistent_data::bitset bits_; unsigned nr_probes_; uint64_t mask_; From 37f4c38ec3db5537b7a250289cf520bddb4b468f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 26 Jun 2014 15:29:33 +0100 Subject: [PATCH 054/165] [era/writeset_tree] Initialise era_ in the ctr to quieten coverity --- era/writeset_tree.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/era/writeset_tree.cc b/era/writeset_tree.cc index 2fe47fe..54aa6a1 100644 --- a/era/writeset_tree.cc +++ b/era/writeset_tree.cc @@ -48,6 +48,7 @@ namespace { writeset_tree_detail::writeset_visitor &writeset_v, writeset_tree_detail::damage_visitor &dv) : tm_(tm), + era_(0), writeset_v_(writeset_v), dv_(dv) { } From 5e3f4cf532c6da3957f50e1579936e3cb1d70e2c Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 26 Jun 2014 15:35:48 +0100 Subject: [PATCH 055/165] [era] Provide a default ctr for era_detail to quieten coverity --- era/era_detail.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/era/era_detail.h b/era/era_detail.h index 8cb8b06..4269afd 100644 --- a/era/era_detail.h +++ b/era/era_detail.h @@ -13,6 +13,11 @@ namespace era { } __attribute__ ((packed)); struct era_detail { + era_detail() + : nr_bits(0), + writeset_root(0) { + } + uint32_t nr_bits; uint64_t writeset_root; }; From e77e8715b0a91a60cf2c4b85933b1063385da122 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 2 Jul 2014 08:19:20 +0000 Subject: [PATCH 056/165] Remove ambiguity between boost::uint64_t and ::uint64_t. This has been causing people problems on uclibc builds. --- caching/cache_metadata_size.cc | 15 +++++++-------- caching/xml_format.cc | 7 +++---- era/superblock.cc | 5 ++--- era/writeset_tree.cc | 5 ++--- persistent-data/data-structures/bitset.cc | 7 +++---- thin-provisioning/device_tree.h | 4 +--- thin-provisioning/metadata_dumper.cc | 2 +- 7 files changed, 19 insertions(+), 26 deletions(-) diff --git a/caching/cache_metadata_size.cc b/caching/cache_metadata_size.cc index 7584da6..5792c49 100644 --- a/caching/cache_metadata_size.cc +++ b/caching/cache_metadata_size.cc @@ -7,7 +7,6 @@ #include #include -using namespace boost; using namespace std; //---------------------------------------------------------------- @@ -18,9 +17,9 @@ namespace { : max_hint_width(4) { } - optional device_size; - optional block_size; - optional nr_blocks; + boost::optional device_size; + boost::optional block_size; + boost::optional nr_blocks; uint32_t max_hint_width; }; @@ -58,19 +57,19 @@ namespace { while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (c) { case 0: - fs.block_size = lexical_cast(optarg); + fs.block_size = boost::lexical_cast(optarg); break; case 1: - fs.device_size = lexical_cast(optarg); + fs.device_size = boost::lexical_cast(optarg); break; case 2: - fs.nr_blocks = lexical_cast(optarg); + fs.nr_blocks = boost::lexical_cast(optarg); break; case 3: - fs.max_hint_width = lexical_cast(optarg); + fs.max_hint_width = boost::lexical_cast(optarg); break; case 'h': diff --git a/caching/xml_format.cc b/caching/xml_format.cc index cb03018..84d6fc2 100644 --- a/caching/xml_format.cc +++ b/caching/xml_format.cc @@ -5,7 +5,6 @@ #include #include -using namespace boost; using namespace caching; using namespace persistent_data; using namespace std; @@ -189,14 +188,14 @@ namespace { block_address cblock = get_attr(attr, "cache_block"); decoded_or_error doe = base64_decode(get_attr(attr, "data")); - if (!get >(&doe)) { + if (!boost::get >(&doe)) { ostringstream msg; msg << "invalid base64 encoding of hint for cache block " - << cblock << ": " << get(doe); + << cblock << ": " << boost::get(doe); throw runtime_error(msg.str()); } - e->hint(cblock, get >(doe)); + e->hint(cblock, boost::get >(doe)); } // FIXME: why passing e by ptr? diff --git a/era/superblock.cc b/era/superblock.cc index 1bd1a4f..c319e9b 100644 --- a/era/superblock.cc +++ b/era/superblock.cc @@ -4,7 +4,6 @@ #include "persistent-data/errors.h" using namespace base; -using namespace boost; using namespace era; using namespace superblock_damage; using namespace persistent_data; @@ -149,8 +148,8 @@ superblock_traits::unpack(disk_type const &disk, value_type &value) block_address ms = to_cpu(disk.metadata_snap); value.metadata_snap = (ms == SUPERBLOCK_LOCATION) ? - optional() : - optional(ms); + boost::optional() : + boost::optional(ms); } void diff --git a/era/writeset_tree.cc b/era/writeset_tree.cc index 54aa6a1..4e2c478 100644 --- a/era/writeset_tree.cc +++ b/era/writeset_tree.cc @@ -2,7 +2,6 @@ #include "persistent-data/data-structures/btree_damage_visitor.h" #include "persistent-data/data-structures/bitset.h" -using namespace boost; using namespace era; using namespace writeset_tree_detail; using namespace persistent_data; @@ -90,8 +89,8 @@ namespace { private: template run to_uint32(run const &r) { - return run(optional(r.begin_), - optional(r.end_)); + return run(boost::optional(r.begin_), + boost::optional(r.end_)); } damage_visitor &v_; diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index 5851e28..e49e19f 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -2,7 +2,6 @@ #include "persistent-data/data-structures/bitset.h" #include "persistent-data/math_utils.h" -using namespace boost; using namespace persistent_data; using namespace persistent_data::bitset_detail; using namespace std; @@ -12,7 +11,7 @@ using namespace std; namespace { struct bitset_traits { typedef base::le64 disk_type; - typedef uint64_t value_type; + typedef ::uint64_t value_type; typedef no_op_ref_counter ref_counter; static void unpack(disk_type const &disk, value_type &value) { @@ -118,11 +117,11 @@ namespace persistent_data { } private: - optional lifted_mult64(optional const &m) { + boost::optional lifted_mult64(boost::optional const &m) { if (!m) return m; - return optional(*m * 64); + return boost::optional(*m * 64); } bitset_visitor &v_; diff --git a/thin-provisioning/device_tree.h b/thin-provisioning/device_tree.h index 320eb73..23ae924 100644 --- a/thin-provisioning/device_tree.h +++ b/thin-provisioning/device_tree.h @@ -4,8 +4,6 @@ #include "persistent-data/data-structures/btree.h" #include "persistent-data/run.h" -using namespace boost; - //---------------------------------------------------------------- namespace thin_provisioning { @@ -50,7 +48,7 @@ namespace thin_provisioning { class damage_visitor { public: - typedef shared_ptr ptr; + typedef boost::shared_ptr ptr; virtual ~damage_visitor() {} diff --git a/thin-provisioning/metadata_dumper.cc b/thin-provisioning/metadata_dumper.cc index dfe18e0..0bd284e 100644 --- a/thin-provisioning/metadata_dumper.cc +++ b/thin-provisioning/metadata_dumper.cc @@ -226,7 +226,7 @@ thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair) md->sb_.trans_id_, md->sb_.data_block_size_, md->data_sm_->get_nr_blocks(), - optional()); + boost::optional()); { mapping_tree_detail::damage_visitor::ptr md_policy(mapping_damage_policy(repair)); From 9470937b10954667894372cea9d531a67261f7ac Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 2 Jul 2014 15:02:32 +0100 Subject: [PATCH 057/165] [btree] When creating a new multilayer btree the initial node should have block_traits, rather than the ValueTraits. --- persistent-data/data-structures/btree.tcc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/persistent-data/data-structures/btree.tcc b/persistent-data/data-structures/btree.tcc index 42ec41e..1234aa0 100644 --- a/persistent-data/data-structures/btree.tcc +++ b/persistent-data/data-structures/btree.tcc @@ -404,11 +404,20 @@ namespace persistent_data { write_ref root = tm_->new_block(validator_); - leaf_node n = to_node(root); - n.set_type(btree_detail::LEAF); - n.set_nr_entries(0); - n.set_max_entries(); - n.set_value_size(sizeof(typename ValueTraits::disk_type)); + if (Levels > 1) { + internal_node n = to_node(root); + n.set_type(btree_detail::LEAF); + n.set_nr_entries(0); + n.set_max_entries(); + n.set_value_size(sizeof(typename block_traits::disk_type)); + + } else { + leaf_node n = to_node(root); + n.set_type(btree_detail::LEAF); + n.set_nr_entries(0); + n.set_max_entries(); + n.set_value_size(sizeof(typename ValueTraits::disk_type)); + } root_ = root.get_location(); } From fbce4d96f4065883c7e8f5df2c265c49e65f5e98 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 21 Jul 2014 11:58:48 +0100 Subject: [PATCH 058/165] [cache_metadata_size] tweak an error message --- features/cache_metadata_size.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/cache_metadata_size.feature b/features/cache_metadata_size.feature index 9b33e34..7430fd9 100644 --- a/features/cache_metadata_size.feature +++ b/features/cache_metadata_size.feature @@ -63,7 +63,7 @@ Feature: cache_metadata_size When I run cache_metadata_size with --block-size 64 Then it should fail with: """ - Please specify either --device-size and --block-size, or --nr-blocks. + If you specify --block-size you must also give --device-size. """ Scenario: Contradictory info causes fail From d517684c95dd4fee53e1f04155bd105d98bfc7f7 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 22 Jul 2014 16:41:39 +0100 Subject: [PATCH 059/165] Initial code drop for merging block_cache --- Makefile.in | 26 +- caching/cache_check.cc | 2 +- caching/cache_dump.cc | 2 +- caching/cache_repair.cc | 4 +- caching/cache_restore.cc | 2 +- era/era_check.cc | 2 +- era/era_dump.cc | 2 +- era/era_invalidate.cc | 2 +- persistent-data/block.h | 74 ++--- persistent-data/block.tcc | 358 +++++++----------------- persistent-data/buffer.h | 47 ++-- persistent-data/cache.h | 284 ------------------- persistent-data/file_utils.cc | 2 +- persistent-data/file_utils.h | 2 +- persistent-data/lock_tracker.cc | 127 --------- persistent-data/lock_tracker.h | 61 ---- persistent-data/space-maps/recursive.cc | 4 +- thin-provisioning/metadata.cc | 6 +- thin-provisioning/metadata_checker.cc | 2 +- thin-provisioning/thin_check.cc | 4 +- thin-provisioning/thin_delta.cc | 2 +- thin-provisioning/thin_rmap.cc | 2 +- unit-tests/array_t.cc | 2 +- unit-tests/bitset_t.cc | 2 +- unit-tests/block_t.cc | 2 +- unit-tests/btree_t.cc | 2 +- unit-tests/space_map_t.cc | 4 +- unit-tests/transaction_manager_t.cc | 2 +- 28 files changed, 186 insertions(+), 845 deletions(-) delete mode 100644 persistent-data/cache.h delete mode 100644 persistent-data/lock_tracker.cc delete mode 100644 persistent-data/lock_tracker.h diff --git a/Makefile.in b/Makefile.in index eab3006..e65667d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -41,6 +41,8 @@ PROGRAMS=\ all: $(PROGRAMS) SOURCE=\ + block-cache/block_cache.cc \ + \ base/base64.cc \ base/endian_utils.cc \ base/error_state.cc \ @@ -65,7 +67,6 @@ SOURCE=\ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/transaction_manager.cc \ \ persistent-data/data-structures/bitset.cc \ @@ -115,7 +116,7 @@ CXXFLAGS+=-g -Wall -fno-strict-aliasing CXXFLAGS+=@CXXOPTIMISE_FLAG@ CXXFLAGS+=@CXXDEBUG_FLAG@ INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR) -I$(TOP_DIR)/thin-provisioning -LIBS:=-lstdc++ +LIBS:=-lstdc++ -laio LIBEXPAT:=-lexpat INSTALL:=@INSTALL@ PREFIX:=@prefix@ @@ -168,6 +169,8 @@ THIN_DUMP_SOURCE=$(SOURCE) THIN_REPAIR_SOURCE=$(SOURCE) THIN_RESTORE_SOURCE=$(SOURCE) THIN_CHECK_SOURCE=\ + block-cache/block_cache.cc \ + \ base/error_state.cc \ base/endian_utils.cc \ \ @@ -175,7 +178,6 @@ THIN_CHECK_SOURCE=\ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/data-structures/btree.cc \ persistent-data/space_map.cc \ persistent-data/space-maps/disk.cc \ @@ -190,6 +192,8 @@ THIN_CHECK_SOURCE=\ thin-provisioning/superblock.cc THIN_DELTA_SOURCE=\ + block-cache/block_cache.cc \ + \ base/error_state.cc \ base/endian_utils.cc \ \ @@ -197,7 +201,6 @@ THIN_DELTA_SOURCE=\ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/data-structures/btree.cc \ persistent-data/space_map.cc \ persistent-data/space-maps/disk.cc \ @@ -212,13 +215,14 @@ THIN_DELTA_SOURCE=\ thin-provisioning/superblock.cc THIN_RMAP_SOURCE=\ + block-cache/block_cache.cc \ + \ base/endian_utils.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/data-structures/btree.cc \ persistent-data/space_map.cc \ persistent-data/space-maps/disk.cc \ @@ -276,6 +280,8 @@ thin_metadata_size: thin-provisioning/thin_metadata_size.o # Cache tools CACHE_CHECK_SOURCE=\ + block-cache/block_cache.cc \ + \ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ @@ -284,7 +290,6 @@ CACHE_CHECK_SOURCE=\ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/data-structures/btree.cc \ persistent-data/data-structures/bitset.cc \ persistent-data/space_map.cc \ @@ -336,6 +341,8 @@ cache_metadata_size: caching/cache_metadata_size.o # Era tools ERA_CHECK_SOURCE=\ + block-cache/block_cache.cc \ + \ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ @@ -350,7 +357,6 @@ ERA_CHECK_SOURCE=\ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/data-structures/btree.cc \ persistent-data/data-structures/bitset.cc \ persistent-data/space_map.cc \ @@ -366,6 +372,8 @@ era_check: $(ERA_CHECK_OBJECTS) era/era_check.o $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) ERA_DUMP_SOURCE=\ + block-cache/block_cache.cc \ + \ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ @@ -382,7 +390,6 @@ ERA_DUMP_SOURCE=\ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/data-structures/btree.cc \ persistent-data/data-structures/bitset.cc \ persistent-data/space_map.cc \ @@ -398,6 +405,8 @@ era_dump: $(ERA_DUMP_OBJECTS) era/era_dump.o $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) ERA_INVALIDATE_SOURCE=\ + block-cache/block_cache.cc \ + \ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ @@ -414,7 +423,6 @@ ERA_INVALIDATE_SOURCE=\ persistent-data/error_set.cc \ persistent-data/file_utils.cc \ persistent-data/hex_dump.cc \ - persistent-data/lock_tracker.cc \ persistent-data/data-structures/btree.cc \ persistent-data/data-structures/bitset.cc \ persistent-data/space_map.cc \ diff --git a/caching/cache_check.cc b/caching/cache_check.cc index 94ea86b..d5ebc19 100644 --- a/caching/cache_check.cc +++ b/caching/cache_check.cc @@ -286,7 +286,7 @@ namespace { throw runtime_error(msg.str()); } - block_manager<>::ptr bm = open_bm(path, block_io<>::READ_ONLY); + block_manager<>::ptr bm = open_bm(path, block_manager<>::READ_ONLY); err = metadata_check(bm, fs); return err == NO_ERROR ? 0 : 1; diff --git a/caching/cache_dump.cc b/caching/cache_dump.cc index ea13c55..938ddd5 100644 --- a/caching/cache_dump.cc +++ b/caching/cache_dump.cc @@ -34,7 +34,7 @@ namespace { int dump(string const &dev, string const &output, flags const &fs) { try { - block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_ONLY); + block_manager<>::ptr bm = open_bm(dev, block_manager<>::READ_ONLY); metadata::ptr md(new metadata(bm, metadata::OPEN)); if (want_stdout(output)) { diff --git a/caching/cache_repair.cc b/caching/cache_repair.cc index 4a8cd9c..77e5ff6 100644 --- a/caching/cache_repair.cc +++ b/caching/cache_repair.cc @@ -16,12 +16,12 @@ using namespace caching; namespace { metadata::ptr open_metadata_for_read(string const &path) { - block_manager<>::ptr bm = open_bm(path, block_io<>::READ_ONLY); + block_manager<>::ptr bm = open_bm(path, block_manager<>::READ_ONLY); return metadata::ptr(new metadata(bm, metadata::OPEN)); } emitter::ptr output_emitter(string const &path) { - block_manager<>::ptr bm = open_bm(path, block_io<>::READ_WRITE); + block_manager<>::ptr bm = open_bm(path, block_manager<>::READ_WRITE); metadata::ptr md(new metadata(bm, metadata::CREATE)); return create_restore_emitter(md, true); } diff --git a/caching/cache_restore.cc b/caching/cache_restore.cc index 536ef82..d38d748 100644 --- a/caching/cache_restore.cc +++ b/caching/cache_restore.cc @@ -37,7 +37,7 @@ namespace { int restore(flags const &fs) { try { - block_manager<>::ptr bm = open_bm(*fs.output, block_io<>::READ_WRITE); + block_manager<>::ptr bm = open_bm(*fs.output, block_manager<>::READ_WRITE); metadata::ptr md(new metadata(bm, metadata::CREATE)); emitter::ptr restorer = create_restore_emitter(md, fs.clean_shutdown); diff --git a/era/era_check.cc b/era/era_check.cc index 3e3f1fd..98af455 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -245,7 +245,7 @@ namespace { throw runtime_error(msg.str()); } - block_manager<>::ptr bm = open_bm(path, block_io<>::READ_ONLY); + block_manager<>::ptr bm = open_bm(path, block_manager<>::READ_ONLY); err = metadata_check(bm, fs); return err == NO_ERROR ? 0 : 1; diff --git a/era/era_dump.cc b/era/era_dump.cc index c9e14ad..760ebff 100644 --- a/era/era_dump.cc +++ b/era/era_dump.cc @@ -37,7 +37,7 @@ namespace { int dump(string const &dev, string const &output, flags const &fs) { try { - block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_ONLY); + block_manager<>::ptr bm = open_bm(dev, block_manager<>::READ_ONLY); metadata::ptr md(new metadata(bm, metadata::OPEN)); if (want_stdout(output)) { diff --git a/era/era_invalidate.cc b/era/era_invalidate.cc index 42bf67b..aba7db8 100644 --- a/era/era_invalidate.cc +++ b/era/era_invalidate.cc @@ -146,7 +146,7 @@ namespace { int invalidate(string const &dev, string const &output, flags const &fs) { try { set blocks; - block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_ONLY); + block_manager<>::ptr bm = open_bm(dev, block_manager<>::READ_ONLY); if (fs.metadata_snapshot_) { superblock sb = read_superblock(bm); diff --git a/persistent-data/block.h b/persistent-data/block.h index f7020d2..c514383 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -19,9 +19,8 @@ #ifndef BLOCK_H #define BLOCK_H +#include "block-cache/block_cache.h" #include "persistent-data/buffer.h" -#include "persistent-data/cache.h" -#include "persistent-data/lock_tracker.h" #include #include @@ -42,40 +41,20 @@ namespace persistent_data { typedef uint64_t block_address; template - class block_io : private boost::noncopyable { + class block_manager : private boost::noncopyable { public: - typedef boost::shared_ptr ptr; + typedef boost::shared_ptr ptr; + enum mode { READ_ONLY, READ_WRITE, CREATE }; - block_io(std::string const &path, block_address nr_blocks, mode m); - ~block_io(); - - block_address get_nr_blocks() const { - return nr_blocks_; - } - - void read_buffer(block_address location, buffer &buf) const; - void write_buffer(block_address location, buffer const &buf); - - private: - int fd_; - block_address nr_blocks_; - mode mode_; - }; - - template - class block_manager : private boost::noncopyable { - public: - typedef boost::shared_ptr ptr; - block_manager(std::string const &path, block_address nr_blocks, unsigned max_concurrent_locks, - typename block_io::mode m); + mode m); class validator { public: @@ -101,7 +80,7 @@ namespace persistent_data { struct block : private boost::noncopyable { typedef boost::shared_ptr ptr; - block(typename block_io::ptr io, + block(block_cache *bc, block_address location, block_type bt, typename validator::ptr v, @@ -116,17 +95,27 @@ namespace persistent_data { // FIXME: finish } - void flush(); - void change_validator(typename block_manager::validator::ptr v, bool check = true); - typename block_io::ptr io_; - block_address location_; - std::auto_ptr > data_; + block_type get_type() const; + uint64_t get_location() const; + + buffer const &get_buffer() const; + buffer &get_buffer(); + + void mark_dirty(); + void unlock(); + + private: + void check_not_unlocked() const; + + bc_block *internal_; typename validator::ptr validator_; block_type bt_; bool dirty_; + bool unlocked_; + buffer buffer_; }; class read_ref { @@ -206,25 +195,8 @@ namespace persistent_data { void check(block_address b) const; void write_block(typename block::ptr b) const; - enum lock_type { - READ_LOCK, - WRITE_LOCK - }; - - struct cache_traits { - typedef typename block::ptr value_type; - typedef block_address key_type; - - static key_type get_key(value_type const &v) { - return v->location_; - } - }; - - typename block_io::ptr io_; - mutable base::cache cache_; - - // FIXME: we need a dirty list as well as a cache - mutable lock_tracker tracker_; + int fd_; + block_cache *bc_; }; // A little utility to help build validators diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 431cdd0..ab9a129 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -37,6 +37,7 @@ namespace { using namespace std; int const DEFAULT_MODE = 0666; + unsigned const SECTOR_SHIFT = 9; // FIXME: these will slow it down until we start doing async io. int const OPEN_FLAGS = O_DIRECT | O_SYNC; @@ -105,134 +106,84 @@ namespace { namespace persistent_data { template - block_io::block_io(std::string const &path, block_address nr_blocks, mode m) - : nr_blocks_(nr_blocks), - mode_(m) - { - off_t file_size = nr_blocks * BlockSize; - - switch (m) { - case READ_ONLY: - fd_ = open_block_file(path, file_size, false); - break; - - case READ_WRITE: - fd_ = open_block_file(path, file_size, true); - break; - - case CREATE: - fd_ = create_block_file(path, file_size); - break; - - default: - throw runtime_error("unsupported mode"); - } - } - - template - block_io::~block_io() - { - if (::close(fd_) < 0) - syscall_failed("close"); - } - - template - void - block_io::read_buffer(block_address location, buffer &buffer) const - { - off_t r; - r = ::lseek(fd_, BlockSize * location, SEEK_SET); - if (r == (off_t) -1) - throw std::runtime_error("lseek failed"); - - ssize_t n; - size_t remaining = BlockSize; - unsigned char *buf = buffer.raw(); - do { - n = ::read(fd_, buf, remaining); - if (n > 0) { - remaining -= n; - buf += n; - } - } while (remaining && ((n > 0) || (n == EINTR) || (n == EAGAIN))); - - if (n < 0) - throw std::runtime_error("read failed"); - } - - template - void - block_io::write_buffer(block_address location, buffer const &buffer) - { - off_t r; - r = ::lseek(fd_, BlockSize * location, SEEK_SET); - if (r == (off_t) -1) - throw std::runtime_error("lseek failed"); - - ssize_t n; - size_t remaining = BlockSize; - unsigned char const *buf = buffer.raw(); - do { - n = ::write(fd_, buf, remaining); - if (n > 0) { - remaining -= n; - buf += n; - } - } while (remaining && ((n > 0) || (n == EINTR) || (n == EAGAIN))); - - if (n < 0) { - std::ostringstream out; - out << "write failed to block " << location - << ", block size = " << BlockSize - << ", remaining = " << remaining - << ", n = " << n - << ", errno = " << errno - << ", fd_ = " << fd_ - << std::endl; - throw std::runtime_error(out.str()); - } - } - -//---------------------------------------------------------------- - - template - block_manager::block::block(typename block_io::ptr io, + block_manager::block::block(block_cache *bc, block_address location, block_type bt, typename validator::ptr v, bool zero) - : io_(io), - location_(location), - data_(new buffer()), - validator_(v), + : validator_(v), bt_(bt), - dirty_(false) + dirty_(false), + unlocked_(false), + buffer_(0, true) // FIXME: we don't know if it's writeable here :( { if (zero) { - // FIXME: duplicate memset - memset(data_->raw(), 0, BlockSize); - dirty_ = true; // redundant? + internal_ = block_cache_get(bc, location, GF_ZERO | GF_CAN_BLOCK); + if (!internal_) + throw std::runtime_error("Couldn't get block"); + dirty_ = true; } else { - io_->read_buffer(location_, *data_); - validator_->check(*data_, location_); + internal_ = block_cache_get(bc, location, GF_CAN_BLOCK); + if (!internal_) + throw std::runtime_error("Couldn't get block"); + + validator_->check(buffer_, internal_->index); } + + buffer_.set_data(internal_->data); } template block_manager::block::~block() { - flush(); + if (!unlocked_) + unlock(); } template void - block_manager::block::flush() + block_manager::block::unlock() { - if (dirty_) { - validator_->prepare(*data_, location_); - io_->write_buffer(location_, *data_); - dirty_ = false; - } + validator_->prepare(buffer_, internal_->index); + block_cache_put(internal_, dirty_ ? PF_DIRTY : 0); + unlocked_ = true; + } + + template + typename block_manager::block_type + block_manager::block::get_type() const + { + return bt_; + } + + template + uint64_t + block_manager::block::get_location() const + { + check_not_unlocked(); + return internal_->index; + } + + template + buffer const & + block_manager::block::get_buffer() const + { + return buffer_; + } + + template + buffer & + block_manager::block::get_buffer() + { + return buffer_; + } + + template + void + block_manager::block::mark_dirty() + { + check_not_unlocked(); + dirty_ = true; } template @@ -240,20 +191,29 @@ namespace persistent_data { block_manager::block::change_validator(typename block_manager::validator::ptr v, bool check) { + check_not_unlocked(); if (v.get() != validator_.get()) { if (dirty_) // It may have already happened, by calling // this we ensure we're consistent. - validator_->prepare(*data_, location_); + validator_->prepare(*internal_->data, internal_->index); validator_ = v; if (check) - validator_->check(*data_, location_); + validator_->check(*internal_->data, internal_->index); } } -//---------------------------------------------------------------- + template + void + block_manager::block::check_not_unlocked() const + { + if (unlocked_) + throw std::runtime_error("block prematurely unlocked"); + } + + //---------------------------------------------------------------- template block_manager::read_ref::read_ref(block_manager const &bm, @@ -278,14 +238,13 @@ namespace persistent_data { block_manager::read_ref::~read_ref() { if (!--(*holders_)) { - if (block_->bt_ == BT_SUPERBLOCK) { + if (block_->get_type() == BT_SUPERBLOCK) { bm_->flush(); - bm_->cache_.put(block_); + block_->unlock(); bm_->flush(); } else - bm_->cache_.put(block_); + block_->unlock(); - bm_->tracker_.unlock(block_->location_); delete holders_; } } @@ -308,44 +267,48 @@ namespace persistent_data { block_address block_manager::read_ref::get_location() const { - return block_->location_; + return block_->get_location(); } template buffer const & block_manager::read_ref::data() const { - return *block_->data_; + return block_->get_buffer(); } -//-------------------------------- + //-------------------------------- template block_manager::write_ref::write_ref(block_manager const &bm, typename block::ptr b) : read_ref(bm, b) { - b->dirty_ = true; + b->mark_dirty(); } template buffer & block_manager::write_ref::data() { - return *read_ref::block_->data_; + return read_ref::block_->get_buffer(); } -//---------------------------------------------------------------- + //---------------------------------------------------------------- template block_manager::block_manager(std::string const &path, block_address nr_blocks, unsigned max_concurrent_blocks, - typename block_io::mode mode) - : io_(new block_io(path, nr_blocks, mode)), - cache_(max(1024u, max_concurrent_blocks)), - tracker_(0, nr_blocks) + mode m) { + // Open the file descriptor + fd_ = open_block_file(path, nr_blocks * BlockSize, m == READ_WRITE); + + // Create the cache + bc_ = block_cache_create(fd_, BlockSize << SECTOR_SHIFT, nr_blocks, 1024u * BlockSize * 1.2); + if (!bc_) + throw std::runtime_error("couldn't create block cache"); } template @@ -353,27 +316,8 @@ namespace persistent_data { block_manager::read_lock(block_address location, typename block_manager::validator::ptr v) const { - tracker_.read_lock(location); - try { - check(location); - boost::optional cached_block = cache_.get(location); - - if (cached_block) { - typename block::ptr cb = *cached_block; - cb->check_read_lockable(); - cb->change_validator(v); - - return read_ref(*this, *cached_block); - } - - typename block::ptr b(new block(io_, location, BT_NORMAL, v)); - cache_.insert(b); - return read_ref(*this, b); - - } catch (...) { - tracker_.unlock(location); - throw; - } + typename block::ptr b(new block(bc_, location, BT_NORMAL, v, false)); + return read_ref(*this, b); } template @@ -381,29 +325,8 @@ namespace persistent_data { block_manager::write_lock(block_address location, typename block_manager::validator::ptr v) { - tracker_.write_lock(location); - try { - check(location); - - boost::optional cached_block = cache_.get(location); - - if (cached_block) { - typename block::ptr cb = *cached_block; - cb->check_write_lockable(); - cb->change_validator(v); - - return write_ref(*this, *cached_block); - } - - typename block::ptr b(new block(io_, location, BT_NORMAL, v)); - cache_.insert(b); - return write_ref(*this, b); - - } catch (...) { - tracker_.unlock(location); - throw; - } - + typename block::ptr b(new block(bc_, location, BT_NORMAL, v, false)); + return write_ref(*this, b); } template @@ -411,28 +334,8 @@ namespace persistent_data { block_manager::write_lock_zero(block_address location, typename block_manager::validator::ptr v) { - tracker_.write_lock(location); - try { - check(location); - - boost::optional cached_block = cache_.get(location); - if (cached_block) { - typename block::ptr cb = *cached_block; - cb->check_write_lockable(); - cb->change_validator(v, false); - memset((*cached_block)->data_->raw(), 0, BlockSize); - - return write_ref(*this, *cached_block); - } - - typename block::ptr b(new block(io_, location, BT_NORMAL, v, true)); - cache_.insert(b); - return write_ref(*this, b); - - } catch (...) { - tracker_.unlock(location); - throw; - } + typename block::ptr b(new block(bc_, location, BT_NORMAL, v, true)); + return write_ref(*this, b); } template @@ -440,29 +343,8 @@ namespace persistent_data { block_manager::superblock(block_address location, typename block_manager::validator::ptr v) { - tracker_.superblock_lock(location); - try { - check(location); - - boost::optional cached_block = cache_.get(location); - - if (cached_block) { - typename block::ptr cb = *cached_block; - cb->check_write_lockable(); - cb->bt_ = BT_SUPERBLOCK; - cb->change_validator(v); - - return write_ref(*this, *cached_block); - } - - typename block::ptr b(new block(io_, location, BT_SUPERBLOCK, v)); - cache_.insert(b); - return write_ref(*this, b); - - } catch (...) { - tracker_.unlock(location); - throw; - } + typename block::ptr b(new block(bc_, location, BT_SUPERBLOCK, v, false)); + return write_ref(*this, b); } template @@ -470,45 +352,15 @@ namespace persistent_data { block_manager::superblock_zero(block_address location, typename block_manager::validator::ptr v) { - tracker_.superblock_lock(location); - try { - check(location); - - boost::optional cached_block = cache_.get(location); - - if (cached_block) { - typename block::ptr cb = *cached_block; - cb->check_write_lockable(); - cb->bt_ = BT_SUPERBLOCK; - cb->change_validator(v, false); - memset(cb->data_->raw(), 0, BlockSize); // FIXME: add a zero method to buffer - - return write_ref(*this, *cached_block); - } - - typename block::ptr b(new block(io_, location, BT_SUPERBLOCK, v, true)); - cache_.insert(b); - return write_ref(*this, b); - - } catch (...) { - tracker_.unlock(location); - throw; - } - } - - template - void - block_manager::check(block_address b) const - { - if (b >= io_->get_nr_blocks()) - throw std::runtime_error("block address out of bounds"); + typename block::ptr b(new block(bc_, location, BT_SUPERBLOCK, v, true)); + return write_ref(*this, b); } template block_address block_manager::get_nr_blocks() const { - return io_->get_nr_blocks(); + return block_cache_get_nr_blocks(bc_); } template @@ -522,15 +374,7 @@ namespace persistent_data { void block_manager::flush() const { - cache_.iterate_unheld( - boost::bind(&block_manager::write_block, this, _1)); - } - - template - bool - block_manager::is_locked(block_address b) const - { - return tracker_.is_locked(b); + block_cache_flush(bc_); } } diff --git a/persistent-data/buffer.h b/persistent-data/buffer.h index 527a239..34aa31f 100644 --- a/persistent-data/buffer.h +++ b/persistent-data/buffer.h @@ -20,10 +20,8 @@ #define BUFFER_H #include -// #include #include -#include #include #include #include @@ -35,18 +33,14 @@ namespace persistent_data { uint32_t const DEFAULT_BUFFER_SIZE = 4096; - // Allocate buffer of Size with Alignment imposed. - // - // Allocation needs to be on the heap in order to provide alignment - // guarantees. - // - // Alignment must be a power of two. - template - class buffer : private boost::noncopyable { + template + class buffer { public: - BOOST_STATIC_ASSERT((Alignment > 1) & !(Alignment & (Alignment - 1))); + buffer(void *data, bool writeable = true) + : data_(static_cast(data)), + writeable_(writeable) { + } - static uint32_t const ALIGNMENT = Alignment; typedef boost::shared_ptr ptr; typedef boost::shared_ptr const_ptr; @@ -55,6 +49,7 @@ namespace persistent_data { } unsigned char &operator[](unsigned index) { + check_writeable(); check_index(index); return data_[index]; @@ -74,31 +69,23 @@ namespace persistent_data { return data_; } - static void *operator new(size_t s) { - // void *r; - // return posix_memalign(&r, Alignment, s) ? NULL : r; - - // Allocates size bytes and returns a pointer to the - // allocated memory. The memory address will be a - // multiple of 'Alignment', which must be a power of two - void *mem = memalign(Alignment, s); - if (!mem) - throw std::bad_alloc(); - - return mem; - } - - static void operator delete(void *p) { - free(p); + void set_data(void *data) { + data_ = static_cast(data); } private: - unsigned char data_[Size]; - static void check_index(unsigned index) { if (index >= Size) throw std::range_error("buffer index out of bounds"); } + + void check_writeable() const { + if (!writeable_) + throw std::runtime_error("buffer isn't writeable"); + } + + unsigned char *data_; + bool writeable_; }; } diff --git a/persistent-data/cache.h b/persistent-data/cache.h deleted file mode 100644 index 6c4e660..0000000 --- a/persistent-data/cache.h +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (C) 2011 Red Hat, Inc. All rights reserved. -// -// This file is part of the thin-provisioning-tools source. -// -// thin-provisioning-tools is free software: you can redistribute it -// and/or modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation, either version 3 of -// the License, or (at your option) any later version. -// -// thin-provisioning-tools is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty -// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with thin-provisioning-tools. If not, see -// . - -#ifndef CACHE_H -#define CACHE_H - -#include "deleter.h" - -#include -#include -#include -#include -#include -#include -#include - -//---------------------------------------------------------------- - -namespace base { - // ValueTraits needs to define value_type, key_type and a get_key() - // static function. Commonly you will want value_type to be a - // shared_ptr, with any teardown specific stuff in the destructor. - template - class cache { - public: - typedef typename ValueTraits::value_type value_type; - typedef typename ValueTraits::key_type key_type; - - cache(unsigned max_entries); - ~cache(); - - void insert(value_type const &v); - - boost::optional get(key_type const &k); - void put(value_type const &k); - - template - void iterate_unheld(T fn) const; - - private: - void make_space(); - - struct value_entry { - // FIXME: this means the cached object must have a - // default constructor also, which is a shame. - // so we can construct the headers. - value_entry() - : ref_count_(1) { - } - - explicit value_entry(value_type v) - : ref_count_(1), - v_(v) { - } - - struct lru { - lru() - : next_(0), - prev_(0) { - } - - value_entry *next_, *prev_; - }; - - struct lookup { - lookup() - : parent_(0), - left_(0), - right_(0), - color_() { - } - - value_entry *parent_, *left_, *right_; - int color_; - }; - - lru lru_; - lookup lookup_; - unsigned ref_count_; - value_type v_; - }; - - struct value_ptr_cmp { - bool operator() (value_entry const *lhs, value_entry const *rhs) { - key_type k1 = ValueTraits::get_key(lhs->v_); - key_type k2 = ValueTraits::get_key(rhs->v_); - - return k1 < k2; - } - }; - - struct key_value_ptr_cmp { - bool operator() (key_type const &k1, value_entry const *rhs) { - key_type k2 = ValueTraits::get_key(rhs->v_); - return k1 < k2; - } - - bool operator() (value_entry const *lhs, key_type const &k2) { - key_type k1 = ValueTraits::get_key(lhs->v_); - return k1 < k2; - } - - }; - - struct list_node_traits { - typedef value_entry node; - typedef value_entry *node_ptr; - typedef const value_entry *const_node_ptr; - - static node_ptr get_next(const_node_ptr n) { - return n->lru_.next_; - } - - static void set_next(node_ptr n, node_ptr next) { - n->lru_.next_ = next; - } - - static node_ptr get_previous(const_node_ptr n) { - return n->lru_.prev_; - } - - static void set_previous(node_ptr n, node_ptr prev) { - n->lru_.prev_ = prev; - } - }; - - struct rbtree_node_traits { - typedef value_entry node; - typedef value_entry *node_ptr; - typedef const value_entry * const_node_ptr; - typedef int color; - - static node_ptr get_parent(const_node_ptr n) { - return n->lookup_.parent_; - } - - static void set_parent(node_ptr n, node_ptr parent) { - n->lookup_.parent_ = parent; - } - - static node_ptr get_left(const_node_ptr n) { - return n->lookup_.left_; - } - - static void set_left(node_ptr n, node_ptr left) { - n->lookup_.left_ = left; - } - - static node_ptr get_right(const_node_ptr n) { - return n->lookup_.right_; - } - - static void set_right(node_ptr n, node_ptr right) { - n->lookup_.right_ = right; - } - - static int get_color(const_node_ptr n) { - return n->lookup_.color_; - } - - static void set_color(node_ptr n, color c) { - n->lookup_.color_ = c; - } - - static color red() { - return 0; - } - - static color black() { - return 1; - } - }; - - typedef boost::intrusive::circular_list_algorithms lru_algo; - typedef boost::intrusive::rbtree_algorithms lookup_algo; - - unsigned max_entries_; - unsigned current_entries_; - - value_entry lru_header_; - value_entry lookup_header_; - }; - - template - cache::cache(unsigned max_entries) - : max_entries_(max_entries), - current_entries_(0) { - lru_algo::init_header(&lru_header_); - lookup_algo::init_header(&lookup_header_); - } - - template - cache::~cache() { - utils::deleter d; - lookup_algo::clear_and_dispose(&lookup_header_, d); - } - - template - void - cache::insert(value_type const &v) { - make_space(); - - std::auto_ptr node(new value_entry(v)); - value_ptr_cmp cmp; - lookup_algo::insert_equal(&lookup_header_, &lookup_header_, node.get(), cmp); - node.release(); - current_entries_++; - } - - template - boost::optional - cache::get(key_type const &k) { - key_value_ptr_cmp cmp; - value_entry *node = lookup_algo::find(&lookup_header_, k, cmp); - if (node == &lookup_header_) - return boost::optional(); - - if (!node->ref_count_++) - lru_algo::unlink(node); - return boost::optional(node->v_); - } - - template - void - cache::put(value_type const &v) { - // FIXME: the lookup will go once we use a proper hook - key_value_ptr_cmp cmp; - key_type k = ValueTraits::get_key(v); - value_entry *node = lookup_algo::find(&lookup_header_, k, cmp); - if (node == &lookup_header_) - throw std::runtime_error("invalid put"); - - if (node->ref_count_ == 0) - throw std::runtime_error("invalid put"); - - if (!--node->ref_count_) - lru_algo::link_after(&lru_header_, node); - } - - template - void - cache::make_space() { - if (current_entries_ == max_entries_) { - value_entry *node = lru_header_.lru_.prev_; - if (node == &lru_header_) - throw std::runtime_error("cache full"); - - lru_algo::unlink(node); - lookup_algo::unlink(node); - delete node; - current_entries_--; - } - } - - template - template - void - cache::iterate_unheld(T fn) const { - value_entry *n = lru_header_.lru_.next_; - while (n != &lru_header_) { - fn(n->v_); - n = n->lru_.next_; - } - } -} - -//---------------------------------------------------------------- - -#endif diff --git a/persistent-data/file_utils.cc b/persistent-data/file_utils.cc index a96aec7..3dc9e2d 100644 --- a/persistent-data/file_utils.cc +++ b/persistent-data/file_utils.cc @@ -48,7 +48,7 @@ persistent_data::get_nr_blocks(string const &path) } persistent_data::block_manager<>::ptr -persistent_data::open_bm(std::string const &dev_path, block_io<>::mode m) +persistent_data::open_bm(std::string const &dev_path, block_manager<>::mode m) { block_address nr_blocks = get_nr_blocks(dev_path); return block_manager<>::ptr(new block_manager<>(dev_path, nr_blocks, 1, m)); diff --git a/persistent-data/file_utils.h b/persistent-data/file_utils.h index be1e492..d08fa96 100644 --- a/persistent-data/file_utils.h +++ b/persistent-data/file_utils.h @@ -10,7 +10,7 @@ // FIXME: move to a different unit namespace persistent_data { persistent_data::block_address get_nr_blocks(string const &path); - block_manager<>::ptr open_bm(std::string const &dev_path, block_io<>::mode m); + block_manager<>::ptr open_bm(std::string const &dev_path, block_manager<>::mode m); void check_file_exists(std::string const &file); } diff --git a/persistent-data/lock_tracker.cc b/persistent-data/lock_tracker.cc deleted file mode 100644 index b7800c3..0000000 --- a/persistent-data/lock_tracker.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (C) 2012 Red Hat, Inc. All rights reserved. -// -// This file is part of the thin-provisioning-tools source. -// -// thin-provisioning-tools is free software: you can redistribute it -// and/or modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation, either version 3 of -// the License, or (at your option) any later version. -// -// thin-provisioning-tools is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty -// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with thin-provisioning-tools. If not, see -// . - -#include "lock_tracker.h" - -#include - -using namespace persistent_data; -using namespace std; - -//---------------------------------------------------------------- - -lock_tracker::lock_tracker(uint64_t low, uint64_t high) - : low_(low), - high_(high) -{ -} - -void -lock_tracker::read_lock(uint64_t key) -{ - check_key(key); - - LockMap::iterator it = locks_.find(key); - if (found(it)) { - if (it->second < 0) - throw runtime_error("already write locked"); - - it->second++; - - } else - locks_.insert(make_pair(key, 1)); -} - -void -lock_tracker::write_lock(uint64_t key) -{ - check_key(key); - - LockMap::const_iterator it = locks_.find(key); - if (found(it)) - throw runtime_error("already locked"); - - locks_.insert(make_pair(key, -1)); -} - -void -lock_tracker::superblock_lock(uint64_t key) -{ - if (superblock_) - throw runtime_error("superblock already held"); - - superblock_ = boost::optional(key); - try { - write_lock(key); - - } catch (...) { - superblock_ = boost::optional(); - } -} - -void -lock_tracker::unlock(uint64_t key) -{ - check_key(key); - - LockMap::const_iterator it = locks_.find(key); - if (!found(it)) - throw runtime_error("not locked"); - - if (superblock_ && *superblock_ == key) { - if (locks_.size() > 1) - throw runtime_error("superblock unlocked while other locks still held"); - - superblock_ = boost::optional(); - } - - if (it->second > 1) - locks_.insert(make_pair(key, it->second - 1)); - else - locks_.erase(key); - -} - -bool -lock_tracker::found(LockMap::const_iterator it) const -{ - return it != locks_.end(); -} - -bool -lock_tracker::valid_key(uint64_t key) const -{ - return (key >= low_ && key <= high_); -} - -void -lock_tracker::check_key(uint64_t key) const -{ - if (!valid_key(key)) - throw runtime_error("invalid key"); -} - -bool -lock_tracker::is_locked(uint64_t key) const -{ - check_key(key); - return found(locks_.find(key)); -} - -//---------------------------------------------------------------- - diff --git a/persistent-data/lock_tracker.h b/persistent-data/lock_tracker.h deleted file mode 100644 index 497e2cd..0000000 --- a/persistent-data/lock_tracker.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2011 Red Hat, Inc. All rights reserved. -// -// This file is part of the thin-provisioning-tools source. -// -// thin-provisioning-tools is free software: you can redistribute it -// and/or modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation, either version 3 of -// the License, or (at your option) any later version. -// -// thin-provisioning-tools is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty -// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with thin-provisioning-tools. If not, see -// . - -#ifndef LOCK_TRACKER_H -#define LOCK_TRACKER_H - -#include -#include -#include -#include - - -//---------------------------------------------------------------- - -namespace persistent_data { - class lock_tracker : private boost::noncopyable { - public: - lock_tracker(uint64_t low, uint64_t high); - - void read_lock(uint64_t key); - void write_lock(uint64_t key); - void superblock_lock(uint64_t key); - void unlock(uint64_t key); - - bool is_locked(uint64_t key) const; - - private: - typedef std::map LockMap; - - bool found(LockMap::const_iterator it) const; - - bool valid_key(uint64_t key) const; - void check_key(uint64_t key) const; - - // Positive for read lock, negative for write lock - LockMap locks_; - boost::optional superblock_; - - uint64_t low_; - uint64_t high_; - }; -} - -//---------------------------------------------------------------- - -#endif diff --git a/persistent-data/space-maps/recursive.cc b/persistent-data/space-maps/recursive.cc index 6533c20..dff28dc 100644 --- a/persistent-data/space-maps/recursive.cc +++ b/persistent-data/space-maps/recursive.cc @@ -19,6 +19,8 @@ #include "persistent-data/space-maps/recursive.h" #include "persistent-data/space-maps/subtracting_span_iterator.h" +#include + using namespace persistent_data; //---------------------------------------------------------------- @@ -286,7 +288,7 @@ namespace { BOP_SET }; - typedef map > op_map; + typedef map > op_map; op_map ops_; subtracting_span_iterator::block_set allocated_blocks_; diff --git a/thin-provisioning/metadata.cc b/thin-provisioning/metadata.cc index 908d16b..6136722 100644 --- a/thin-provisioning/metadata.cc +++ b/thin-provisioning/metadata.cc @@ -65,7 +65,7 @@ metadata::metadata(std::string const &dev_path, open_type ot, { switch (ot) { case OPEN: - tm_ = open_tm(open_bm(dev_path, block_io<>::READ_ONLY)); + tm_ = open_tm(open_bm(dev_path, block_manager<>::READ_ONLY)); sb_ = read_superblock(tm_->get_bm()); if (sb_.version_ != 1) @@ -90,7 +90,7 @@ metadata::metadata(std::string const &dev_path, open_type ot, break; case CREATE: - tm_ = open_tm(open_bm(dev_path, block_io<>::READ_WRITE)); + tm_ = open_tm(open_bm(dev_path, block_manager<>::READ_WRITE)); space_map::ptr core = tm_->get_sm(); metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks()); copy_space_maps(metadata_sm_, core); @@ -118,7 +118,7 @@ metadata::metadata(std::string const &dev_path, open_type ot, metadata::metadata(std::string const &dev_path, block_address metadata_snap) { - tm_ = open_tm(open_bm(dev_path, block_io<>::READ_ONLY)); + tm_ = open_tm(open_bm(dev_path, block_manager<>::READ_ONLY)); sb_ = read_superblock(tm_->get_bm(), metadata_snap); // We don't open the metadata sm for a held root diff --git a/thin-provisioning/metadata_checker.cc b/thin-provisioning/metadata_checker.cc index e8bd6d3..7f3124a 100644 --- a/thin-provisioning/metadata_checker.cc +++ b/thin-provisioning/metadata_checker.cc @@ -378,7 +378,7 @@ namespace { static block_manager<>::ptr open_bm(string const &dev_path) { block_address nr_blocks = thin_provisioning::get_nr_blocks(dev_path); - return block_manager<>::ptr(new block_manager<>(dev_path, nr_blocks, 1, block_io<>::READ_ONLY)); + return block_manager<>::ptr(new block_manager<>(dev_path, nr_blocks, 1, block_manager<>::READ_ONLY)); } // FIXME: common code with metadata.cc diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc index e468a02..e4cae83 100644 --- a/thin-provisioning/thin_check.cc +++ b/thin-provisioning/thin_check.cc @@ -42,7 +42,7 @@ namespace { block_manager<>::ptr open_bm(string const &path) { block_address nr_blocks = get_nr_blocks(path); - block_io<>::mode m = block_io<>::READ_ONLY; + block_manager<>::mode m = block_manager<>::READ_ONLY; return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m)); } @@ -225,7 +225,7 @@ namespace { } void clear_needs_check(string const &path) { - block_manager<>::ptr bm = open_bm(path, block_io<>::READ_WRITE); + block_manager<>::ptr bm = open_bm(path, block_manager<>::READ_WRITE); superblock_detail::superblock sb = read_superblock(bm); sb.set_needs_check_flag(false); diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 396422b..59fefd3 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -72,7 +72,7 @@ namespace { block_manager<>::ptr open_bm(string const &path) { block_address nr_blocks = get_nr_blocks(path); - block_io<>::mode m = block_io<>::READ_ONLY; + block_manager<>::mode m = block_manager<>::READ_ONLY; return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m)); } diff --git a/thin-provisioning/thin_rmap.cc b/thin-provisioning/thin_rmap.cc index 9211459..70c7b38 100644 --- a/thin-provisioning/thin_rmap.cc +++ b/thin-provisioning/thin_rmap.cc @@ -23,7 +23,7 @@ namespace { block_manager<>::ptr open_bm(string const &path) { block_address nr_blocks = get_nr_blocks(path); - block_io<>::mode m = block_io<>::READ_ONLY; + block_manager<>::mode m = block_manager<>::READ_ONLY; return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m)); } diff --git a/unit-tests/array_t.cc b/unit-tests/array_t.cc index 0c688b4..5d0a733 100644 --- a/unit-tests/array_t.cc +++ b/unit-tests/array_t.cc @@ -78,7 +78,7 @@ namespace { private: static transaction_manager::ptr create_tm() { - block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_io<>::READ_WRITE)); + block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)); space_map::ptr sm(new core_map(NR_BLOCKS)); transaction_manager::ptr tm(new transaction_manager(bm, sm)); return tm; diff --git a/unit-tests/bitset_t.cc b/unit-tests/bitset_t.cc index e2dc4cd..20d1b1f 100644 --- a/unit-tests/bitset_t.cc +++ b/unit-tests/bitset_t.cc @@ -34,7 +34,7 @@ namespace { transaction_manager::ptr create_tm() { - block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_io<>::READ_WRITE)); + block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)); space_map::ptr sm(new core_map(NR_BLOCKS)); transaction_manager::ptr tm(new transaction_manager(bm, sm)); return tm; diff --git a/unit-tests/block_t.cc b/unit-tests/block_t.cc index 75dfa68..c336f46 100644 --- a/unit-tests/block_t.cc +++ b/unit-tests/block_t.cc @@ -64,7 +64,7 @@ namespace { TEST(BlockTests, bad_path) { - ASSERT_THROW(bm4096("/bogus/bogus/bogus", 1234, 4, block_io<>::READ_WRITE), + ASSERT_THROW(bm4096("/bogus/bogus/bogus", 1234, 4, block_manager<>::READ_WRITE), runtime_error); } diff --git a/unit-tests/btree_t.cc b/unit-tests/btree_t.cc index 8fe3a71..0d68343 100644 --- a/unit-tests/btree_t.cc +++ b/unit-tests/btree_t.cc @@ -33,7 +33,7 @@ namespace { transaction_manager::ptr create_tm() { - block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_io<>::READ_WRITE)); + block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)); space_map::ptr sm(new core_map(NR_BLOCKS)); transaction_manager::ptr tm(new transaction_manager(bm, sm)); return tm; diff --git a/unit-tests/space_map_t.cc b/unit-tests/space_map_t.cc index c505ffb..c87d6f4 100644 --- a/unit-tests/space_map_t.cc +++ b/unit-tests/space_map_t.cc @@ -36,7 +36,7 @@ namespace { transaction_manager::ptr create_tm() { block_manager<>::ptr bm( - new block_manager<>("./test.data", NR_BLOCKS, MAX_LOCKS, block_io<>::READ_WRITE)); + new block_manager<>("./test.data", NR_BLOCKS, MAX_LOCKS, block_manager<>::READ_WRITE)); space_map::ptr sm(new core_map(NR_BLOCKS)); transaction_manager::ptr tm( new transaction_manager(bm, sm)); @@ -297,7 +297,7 @@ TEST(SpaceMapTests, test_sm_metadata) TEST(SpaceMapTests, test_metadata_and_disk) { block_manager<>::ptr bm( - new block_manager<>("./test.data", NR_BLOCKS, MAX_LOCKS, block_io<>::READ_WRITE)); + new block_manager<>("./test.data", NR_BLOCKS, MAX_LOCKS, block_manager<>::READ_WRITE)); space_map::ptr core_sm(new core_map(NR_BLOCKS)); transaction_manager::ptr tm(new transaction_manager(bm, core_sm)); persistent_space_map::ptr metadata_sm = persistent_data::create_metadata_sm(tm, NR_BLOCKS); diff --git a/unit-tests/transaction_manager_t.cc b/unit-tests/transaction_manager_t.cc index 384d82a..1697685 100644 --- a/unit-tests/transaction_manager_t.cc +++ b/unit-tests/transaction_manager_t.cc @@ -33,7 +33,7 @@ namespace { transaction_manager::ptr create_tm() { block_manager<>::ptr bm( - new block_manager<>("./test.data", NR_BLOCKS, MAX_HELD_LOCKS, block_io<>::READ_WRITE)); + new block_manager<>("./test.data", NR_BLOCKS, MAX_HELD_LOCKS, block_manager<>::READ_WRITE)); space_map::ptr sm(new core_map(NR_BLOCKS)); transaction_manager::ptr tm(new transaction_manager(bm, sm)); tm->get_sm()->inc(0); From d9040949fc96c942200d812bd0e0d67f3047ea8a Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 22 Jul 2014 16:43:44 +0100 Subject: [PATCH 060/165] forgot block_cache itself --- block-cache/block_cache.cc | 666 +++++++++++++++++++++++++++++++++++++ block-cache/block_cache.h | 54 +++ block-cache/list.h | 216 ++++++++++++ 3 files changed, 936 insertions(+) create mode 100644 block-cache/block_cache.cc create mode 100644 block-cache/block_cache.h create mode 100644 block-cache/list.h diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc new file mode 100644 index 0000000..0cca970 --- /dev/null +++ b/block-cache/block_cache.cc @@ -0,0 +1,666 @@ +#include "block-cache/block_cache.h" + +#include "block-cache/list.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// FIXME: get from linux headers +#define SECTOR_SHIFT 9 +#define PAGE_SIZE 4096 + +#define MIN_BLOCKS 16 +#define WRITEBACK_LOW_THRESHOLD_PERCENT 33 +#define WRITEBACK_HIGH_THRESHOLD_PERCENT 66 + +/*---------------------------------------------------------------- + * Structures + *--------------------------------------------------------------*/ +struct block_cache; + +enum block_flags { + IO_PENDING = (1 << 0), + DIRTY = (1 << 1) +}; + +struct block { + struct list_head list; + struct list_head hash_list; + + struct block_cache *bc; + unsigned ref_count; + + int error; + unsigned flags; + + struct iocb control_block; + + struct bc_block b; +}; + +struct block_cache { + int fd; + sector_t block_size; + uint64_t nr_data_blocks; + uint64_t nr_cache_blocks; + + void *blocks_memory; + void *blocks_data; + + io_context_t aio_context; + struct io_event *events; + + /* + * Blocks on the free list are not initialised, apart from the + * b.data field. + */ + struct list_head free; + struct list_head errored; + struct list_head dirty; + struct list_head clean; + + unsigned nr_io_pending; + struct list_head io_pending; + + unsigned nr_dirty; + + /* + * Hash table fields. + */ + unsigned nr_buckets; + unsigned mask; + struct list_head buckets[0]; +}; + +/*---------------------------------------------------------------- + * Logging + *--------------------------------------------------------------*/ +static void info(struct block_cache *bc, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); + +static void info(struct block_cache *bc, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + +/*---------------------------------------------------------------- + * Allocation + *--------------------------------------------------------------*/ +static void *alloc_aligned(size_t len, size_t alignment) +{ + void *result = NULL; + int r = posix_memalign(&result, alignment, len); + if (r) + return NULL; + + return result; +} + +static int init_free_list(struct block_cache *bc, unsigned count) +{ + size_t len; + struct block *blocks; + size_t block_size = bc->block_size << SECTOR_SHIFT; + void *data; + unsigned i; + + /* Allocate the block structures */ + len = sizeof(struct block) * count; + blocks = static_cast(malloc(len)); + if (!blocks) + return -ENOMEM; + + bc->blocks_memory = blocks; + + /* Allocate the data for each block. We page align the data. */ + data = alloc_aligned(count * block_size, PAGE_SIZE); + if (!data) { + free(blocks); + return -ENOMEM; + } + + bc->blocks_data = data; + + for (i = 0; i < count; i++) { + struct block *b = blocks + i; + INIT_LIST_HEAD(&b->list); + b->b.data = data + block_size * i; + + list_add(&b->list, &bc->free); + } + + return 0; +} + +static struct block *__alloc_block(struct block_cache *bc) +{ + struct block *b; + + if (list_empty(&bc->free)) + return NULL; + + b = list_first_entry(&bc->free, struct block, list); + list_del(&b->list); + + return b; +} + +/*---------------------------------------------------------------- + * Flags handling + *--------------------------------------------------------------*/ +static unsigned test_flags(struct block *b, unsigned flags) +{ + return b->flags & flags; +} + +static void clear_flags(struct block *b, unsigned flags) +{ + b->flags &= ~flags; +} + +static void set_flags(struct block *b, unsigned flags) +{ + b->flags |= flags; +} + +/*---------------------------------------------------------------- + * Low level IO handling + * + * We cannot have two concurrent writes on the same block. + * eg, background writeback, put with dirty, flush? + * + * To avoid this we introduce some restrictions: + * + * i) A held block can never be written back. + * ii) You cannot get a block until writeback has completed. + * + *--------------------------------------------------------------*/ + +/* + * This can be called from the context of the aio thread. So we have a + * separate 'top half' complete function that we know is only called by the + * main cache thread. + */ +static void complete_io(struct block *b, int result) +{ + b->error = result; + clear_flags(b, IO_PENDING); + b->bc->nr_io_pending--; + + if (b->error) + list_move_tail(&b->list, &b->bc->errored); + else { + if (test_flags(b, DIRTY)) { + clear_flags(b, DIRTY); + b->bc->nr_dirty--; + } + + list_move_tail(&b->list, &b->bc->clean); + } +} + +/* + * |b->list| should be valid (either pointing to itself, on one of the other + * lists. + */ +static int issue_low_level(struct block *b, enum io_iocb_cmd opcode, const char *desc) +{ + int r; + struct block_cache *bc = b->bc; + struct iocb *control_blocks[1]; + + assert(!test_flags(b, IO_PENDING)); + set_flags(b, IO_PENDING); + bc->nr_io_pending++; + list_move_tail(&b->list, &bc->io_pending); + + b->control_block.aio_lio_opcode = opcode; + control_blocks[0] = &b->control_block; + r = io_submit(bc->aio_context, 1, control_blocks); + if (r != 1) { + if (r < 0) { + perror("io_submit error"); + info(bc, "io_submit failed with %s op: %d\n", desc, r); + } else + info(bc, "could not submit IOs, with %s op\n", desc); + + complete_io(b, EIO); + return -EIO; + } + + return 0; +} + +static int issue_read(struct block *b) +{ + return issue_low_level(b, IO_CMD_PREAD, "read"); +} + +static int issue_write(struct block *b) +{ + return issue_low_level(b, IO_CMD_PWRITE, "write"); +} + +static void wait_io(struct block_cache *bc) +{ + int r; + unsigned i; + + // FIXME: use a timeout to prevent hanging + r = io_getevents(bc->aio_context, 1, bc->nr_cache_blocks, bc->events, NULL); + if (r < 0) { + info(bc, "io_getevents failed %d\n", r); + exit(1); /* FIXME: handle more gracefully */ + } + + for (i = 0; i < static_cast(r); i++) { + struct io_event *e = bc->events + i; + struct block *b = container_of(e->obj, struct block, control_block); + + if (e->res == bc->block_size << SECTOR_SHIFT) + complete_io(b, 0); + else if (e->res < 0) + complete_io(b, e->res); + else { + info(bc, "incomplete io, unexpected\n"); + } + } +} + +/*---------------------------------------------------------------- + * Clean/dirty list management + *--------------------------------------------------------------*/ + +/* + * We're using lru lists atm, but I think it would be worth + * experimenting with a multiqueue approach. + */ +static struct list_head *__categorise(struct block *b) +{ + if (b->error) + return &b->bc->errored; + + return (b->flags & DIRTY) ? &b->bc->dirty : &b->bc->clean; +} + +static void hit(struct block *b) +{ + list_move_tail(&b->list, __categorise(b)); +} + +/*---------------------------------------------------------------- + * High level IO handling + *--------------------------------------------------------------*/ +static void wait_all(struct block_cache *bc) +{ + while (!list_empty(&bc->io_pending)) + wait_io(bc); +} + +static void wait_specific(struct block *b) +{ + while (test_flags(b, IO_PENDING)) + wait_io(b->bc); +} + +static unsigned writeback(struct block_cache *bc, unsigned count) +{ + int r; + struct block *b, *tmp; + unsigned actual = 0; + + list_for_each_entry_safe (b, tmp, &bc->dirty, list) { + if (actual == count) + break; + + if (b->ref_count) + continue; + + r = issue_write(b); + if (!r) + actual++; + } + + info(bc, "writeback: requested %u, actual %u\n", count, actual); + return actual; +} + +/*---------------------------------------------------------------- + * Hash table + *---------------------------------------------------------------*/ + +/* + * |nr_buckets| must be a power of two. + */ +static void hash_init(struct block_cache *bc, unsigned nr_buckets) +{ + unsigned i; + + bc->nr_buckets = nr_buckets; + bc->mask = nr_buckets - 1; + + for (i = 0; i < nr_buckets; i++) + INIT_LIST_HEAD(bc->buckets + i); +} + +static unsigned hash(struct block_cache *bc, uint64_t index) +{ + const unsigned BIG_PRIME = 4294967291UL; + return (((unsigned) index) * BIG_PRIME) & bc->mask; +} + +static struct block *hash_lookup(struct block_cache *bc, block_index index) +{ + struct block *b; + unsigned bucket = hash(bc, index); + + list_for_each_entry (b, bc->buckets + bucket, hash_list) { + if (b->b.index == index) + return b; + } + + return NULL; +} + +static void hash_insert(struct block *b) +{ + unsigned bucket = hash(b->bc, b->b.index); + + list_move_tail(&b->hash_list, b->bc->buckets + bucket); +} + +static void hash_remove(struct block *b) +{ + list_del_init(&b->hash_list); +} + +/*---------------------------------------------------------------- + * High level allocation + *--------------------------------------------------------------*/ +static void setup_control_block(struct block *b) +{ + struct iocb *cb = &b->control_block; + size_t block_size_bytes = b->bc->block_size << SECTOR_SHIFT; + + memset(cb, 0, sizeof(*cb)); + cb->aio_fildes = b->bc->fd; + + cb->u.c.buf = b->b.data; + cb->u.c.offset = block_size_bytes * b->b.index; + cb->u.c.nbytes = block_size_bytes; +} + +static struct block *new_block(struct block_cache *bc, + block_index index) +{ + struct block *b; + + b = __alloc_block(bc); + if (!b) { + if (list_empty(&bc->clean)) { + if (list_empty(&bc->io_pending)) + writeback(bc, 9000); + wait_io(bc); + } + + if (!list_empty(&bc->clean)) { + b = list_first_entry(&bc->clean, struct block, list); + hash_remove(b); + list_del(&b->list); + } + } + + if (b) { + INIT_LIST_HEAD(&b->list); + INIT_LIST_HEAD(&b->hash_list); + b->bc = bc; + b->ref_count = 0; + + b->error = 0; + clear_flags(b, IO_PENDING | DIRTY); + + b->b.index = index; + setup_control_block(b); + + hash_insert(b); + } + + return b; +} + +/*---------------------------------------------------------------- + * Block reference counting + *--------------------------------------------------------------*/ +static void get_block(struct block *b) +{ + b->ref_count++; +} + +static void put_block(struct block *b) +{ + assert(b->ref_count); + b->ref_count--; +} + +static void mark_dirty(struct block *b) +{ + struct block_cache *bc = b->bc; + + if (!test_flags(b, DIRTY)) { + set_flags(b, DIRTY); + list_move_tail(&b->list, &b->bc->dirty); + bc->nr_dirty++; + } +} + +/*---------------------------------------------------------------- + * Public interface + *--------------------------------------------------------------*/ +unsigned calc_nr_cache_blocks(size_t mem, sector_t block_size) +{ + size_t space_per_block = (block_size << SECTOR_SHIFT) + sizeof(struct block); + unsigned r = mem / space_per_block; + + return (r < MIN_BLOCKS) ? MIN_BLOCKS : r; +} + +unsigned calc_nr_buckets(unsigned nr_blocks) +{ + unsigned r = 8; + unsigned n = nr_blocks / 4; + + if (n < 8) + n = 8; + + while (r < n) + r <<= 1; + + return r; +} + +void +block_cache_destroy(struct block_cache *bc) +{ + wait_all(bc); + + if (bc->aio_context) + io_destroy(bc->aio_context); + + if (bc->events) + free(bc->events); + + if (bc->blocks_memory) + free(bc->blocks_memory); + + if (bc->blocks_data) + free(bc->blocks_data); + + free(bc); +} + +struct block_cache * +block_cache_create(int fd, sector_t block_size, uint64_t on_disk_blocks, size_t mem) +{ + int r; + struct block_cache *bc; + unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); + unsigned nr_buckets = calc_nr_buckets(nr_cache_blocks); + + bc = static_cast(malloc(sizeof(*bc) + sizeof(*bc->buckets) * nr_buckets)); + if (bc) { + memset(bc, 0, sizeof(*bc)); + + bc->fd = fd; + bc->block_size = block_size; + bc->nr_data_blocks = on_disk_blocks; + bc->nr_cache_blocks = nr_cache_blocks; + + bc->events = static_cast(malloc(sizeof(*bc->events) * nr_cache_blocks)); + if (!bc->events) { + info(bc, "couldn't allocate events array\n"); + goto bad; + } + + bc->aio_context = 0; /* needed or io_setup will fail */ + r = io_setup(nr_cache_blocks, &bc->aio_context); + if (r < 0) { + info(bc, "io_setup failed: %d\n", r); + goto bad; + } + + hash_init(bc, nr_buckets); + INIT_LIST_HEAD(&bc->free); + INIT_LIST_HEAD(&bc->errored); + INIT_LIST_HEAD(&bc->dirty); + INIT_LIST_HEAD(&bc->clean); + INIT_LIST_HEAD(&bc->io_pending); + + r = init_free_list(bc, nr_cache_blocks); + if (r) { + info(bc, "couldn't allocate blocks: %d\n", r); + goto bad; + } + } + + return bc; + +bad: + block_cache_destroy(bc); + return NULL; +} + +uint64_t block_cache_get_nr_blocks(struct block_cache *bc) +{ + return bc->nr_data_blocks; +} + +static void zero_block(struct block *b) +{ + memset(b->b.data, 0, b->bc->block_size << SECTOR_SHIFT); + mark_dirty(b); +} + +static struct block *lookup_or_read_block(struct block_cache *bc, block_index index, unsigned flags) +{ + struct block *b = hash_lookup(bc, index); + + if (b) { + if (test_flags(b, IO_PENDING)) + wait_specific(b); + + if (flags & GF_ZERO) + zero_block(b); + + } else { + if (flags & GF_CAN_BLOCK) { + b = new_block(bc, index); + if (b) { + if (flags & GF_ZERO) + zero_block(b); + else { + issue_read(b); + wait_specific(b); + } + } + } + } + + return (!b || b->error) ? NULL : b; +} + +struct bc_block * +block_cache_get(struct block_cache *bc, block_index index, unsigned flags) +{ + struct block *b = lookup_or_read_block(bc, index, flags); + + if (b) { + hit(b); + get_block(b); + + return &b->b; + } + + return NULL; +} + +void +block_cache_put(struct bc_block *bcb, unsigned flags) +{ + unsigned nr_available; + struct block *b = container_of(bcb, struct block, b); + struct block_cache *bc = b->bc; + + put_block(b); + + if (flags & PF_DIRTY) { + mark_dirty(b); + + nr_available = bc->nr_cache_blocks - (bc->nr_dirty - bc->nr_io_pending); + if (nr_available < (WRITEBACK_LOW_THRESHOLD_PERCENT * bc->nr_cache_blocks / 100)) + writeback(bc, (WRITEBACK_HIGH_THRESHOLD_PERCENT * bc->nr_cache_blocks / 100) - nr_available); + } +} + +int +block_cache_flush(struct block_cache *bc) +{ + struct block *b; + + list_for_each_entry (b, &bc->dirty, list) { + if (b->ref_count) { + info(bc, "attempt to lock an already locked block\n"); + return -EAGAIN; + } + + issue_write(b); + } + + wait_all(bc); + + return list_empty(&bc->errored) ? 0 : -EIO; +} + +void +block_cache_prefetch(struct block_cache *bc, block_index index) +{ + struct block *b = hash_lookup(bc, index); + + if (!b) { + b = new_block(bc, index); + if (b) + issue_read(b); + } +} + +/*----------------------------------------------------------------*/ + diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h new file mode 100644 index 0000000..046083f --- /dev/null +++ b/block-cache/block_cache.h @@ -0,0 +1,54 @@ +#ifndef BLOCK_CACHE_H +#define BLOCK_CACHE_H + +#include +#include + +/*----------------------------------------------------------------*/ + +/* FIXME: add logging */ + +/*----------------------------------------------------------------*/ + +/* + * This library is not thread-safe. + */ +typedef uint64_t block_index; + +struct block_cache; + +struct bc_block { + block_index index; + void *data; +}; + +typedef uint64_t sector_t; + +struct block_cache *block_cache_create(int fd, sector_t block_size, + uint64_t max_nr_blocks, size_t mem); +void block_cache_destroy(struct block_cache *bc); + +uint64_t block_cache_get_nr_blocks(struct block_cache *bc); + +enum get_flags { + GF_ZERO = (1 << 0), + GF_CAN_BLOCK = (1 << 1) +}; +struct bc_block *block_cache_get(struct block_cache *bc, block_index index, unsigned flags); + +enum put_flags { + PF_DIRTY = (1 << 0), +}; +void block_cache_put(struct bc_block *b, unsigned flags); + +/* + * Flush can fail if an earlier write failed. You do not know which block + * failed. Make sure you build your recovery with this in mind. + */ +int block_cache_flush(struct block_cache *bc); + +void block_cache_prefetch(struct block_cache *bc, block_index index); + +/*----------------------------------------------------------------*/ + +#endif diff --git a/block-cache/list.h b/block-cache/list.h new file mode 100644 index 0000000..63e8830 --- /dev/null +++ b/block-cache/list.h @@ -0,0 +1,216 @@ +#ifndef LIB_BLOCK_CACHE_LIST_H +#define LIB_BLOCK_CACHE_LIST_H + +#include + +/*----------------------------------------------------------------*/ + +/* + * Simple intrusive linked list code. Lifted from Linux kernel. + */ + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct list_head { + struct list_head *next, *prev; +}; + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new_, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new_; + new_->next = next; + new_->prev = prev; + prev->next = new_; +} + +/** + * list_add - add a new entry + * @new_: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new_, struct list_head *head) +{ + __list_add(new_, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new_: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new_, struct list_head *head) +{ + __list_add(new_, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del_entry(entry); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_struct within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + + +/*----------------------------------------------------------------*/ + +#endif From b32908d5c200a616c7a4fdca1d4147bec5f78f8f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 25 Jul 2014 10:35:04 +0100 Subject: [PATCH 061/165] work in progress --- block-cache/block_cache.cc | 1095 ++++++++++----------- block-cache/block_cache.h | 185 +++- {persistent-data => block-cache}/buffer.h | 0 caching/superblock.cc | 4 +- era/superblock.cc | 4 +- persistent-data/block.h | 14 +- persistent-data/block.tcc | 54 +- persistent-data/data-structures/array.h | 4 +- persistent-data/data-structures/btree.tcc | 4 +- persistent-data/space-maps/disk.cc | 12 +- thin-provisioning/superblock.cc | 4 +- 11 files changed, 717 insertions(+), 663 deletions(-) rename {persistent-data => block-cache}/buffer.h (100%) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 0cca970..6127a23 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -1,7 +1,5 @@ #include "block-cache/block_cache.h" -#include "block-cache/list.h" - #include #include #include @@ -11,6 +9,10 @@ #include #include +#include + +//---------------------------------------------------------------- + // FIXME: get from linux headers #define SECTOR_SHIFT 9 #define PAGE_SIZE 4096 @@ -19,648 +21,581 @@ #define WRITEBACK_LOW_THRESHOLD_PERCENT 33 #define WRITEBACK_HIGH_THRESHOLD_PERCENT 66 -/*---------------------------------------------------------------- - * Structures - *--------------------------------------------------------------*/ -struct block_cache; +//---------------------------------------------------------------- -enum block_flags { - IO_PENDING = (1 << 0), - DIRTY = (1 << 1) -}; +namespace { + // FIXME: remove -struct block { - struct list_head list; - struct list_head hash_list; + /*---------------------------------------------------------------- + * Logging + *--------------------------------------------------------------*/ + void info(const char *format, ...) + { + va_list ap; - struct block_cache *bc; - unsigned ref_count; - - int error; - unsigned flags; - - struct iocb control_block; - - struct bc_block b; -}; - -struct block_cache { - int fd; - sector_t block_size; - uint64_t nr_data_blocks; - uint64_t nr_cache_blocks; - - void *blocks_memory; - void *blocks_data; - - io_context_t aio_context; - struct io_event *events; - - /* - * Blocks on the free list are not initialised, apart from the - * b.data field. - */ - struct list_head free; - struct list_head errored; - struct list_head dirty; - struct list_head clean; - - unsigned nr_io_pending; - struct list_head io_pending; - - unsigned nr_dirty; - - /* - * Hash table fields. - */ - unsigned nr_buckets; - unsigned mask; - struct list_head buckets[0]; -}; - -/*---------------------------------------------------------------- - * Logging - *--------------------------------------------------------------*/ -static void info(struct block_cache *bc, const char *format, ...) - __attribute__ ((format (printf, 2, 3))); - -static void info(struct block_cache *bc, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); -} - -/*---------------------------------------------------------------- - * Allocation - *--------------------------------------------------------------*/ -static void *alloc_aligned(size_t len, size_t alignment) -{ - void *result = NULL; - int r = posix_memalign(&result, alignment, len); - if (r) - return NULL; - - return result; -} - -static int init_free_list(struct block_cache *bc, unsigned count) -{ - size_t len; - struct block *blocks; - size_t block_size = bc->block_size << SECTOR_SHIFT; - void *data; - unsigned i; - - /* Allocate the block structures */ - len = sizeof(struct block) * count; - blocks = static_cast(malloc(len)); - if (!blocks) - return -ENOMEM; - - bc->blocks_memory = blocks; - - /* Allocate the data for each block. We page align the data. */ - data = alloc_aligned(count * block_size, PAGE_SIZE); - if (!data) { - free(blocks); - return -ENOMEM; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); } - bc->blocks_data = data; + void *alloc_aligned(size_t len, size_t alignment) + { + void *result = NULL; + int r = posix_memalign(&result, alignment, len); + if (r) + return NULL; - for (i = 0; i < count; i++) { - struct block *b = blocks + i; - INIT_LIST_HEAD(&b->list); - b->b.data = data + block_size * i; - - list_add(&b->list, &bc->free); + return result; } - - return 0; } -static struct block *__alloc_block(struct block_cache *bc) -{ - struct block *b; +//---------------------------------------------------------------- - if (list_empty(&bc->free)) - return NULL; +namespace bcache { + int + block_cache::init_free_list(unsigned count) + { + size_t len; + block *blocks; + size_t block_size = block_size_ << SECTOR_SHIFT; + void *data; + unsigned i; - b = list_first_entry(&bc->free, struct block, list); - list_del(&b->list); + /* Allocate the block structures */ + len = sizeof(block) * count; + blocks = static_cast(malloc(len)); + if (!blocks) + return -ENOMEM; - return b; -} + blocks_memory_.reset(reinterpret_cast(blocks)); -/*---------------------------------------------------------------- - * Flags handling - *--------------------------------------------------------------*/ -static unsigned test_flags(struct block *b, unsigned flags) -{ - return b->flags & flags; -} - -static void clear_flags(struct block *b, unsigned flags) -{ - b->flags &= ~flags; -} - -static void set_flags(struct block *b, unsigned flags) -{ - b->flags |= flags; -} - -/*---------------------------------------------------------------- - * Low level IO handling - * - * We cannot have two concurrent writes on the same block. - * eg, background writeback, put with dirty, flush? - * - * To avoid this we introduce some restrictions: - * - * i) A held block can never be written back. - * ii) You cannot get a block until writeback has completed. - * - *--------------------------------------------------------------*/ - -/* - * This can be called from the context of the aio thread. So we have a - * separate 'top half' complete function that we know is only called by the - * main cache thread. - */ -static void complete_io(struct block *b, int result) -{ - b->error = result; - clear_flags(b, IO_PENDING); - b->bc->nr_io_pending--; - - if (b->error) - list_move_tail(&b->list, &b->bc->errored); - else { - if (test_flags(b, DIRTY)) { - clear_flags(b, DIRTY); - b->bc->nr_dirty--; + /* Allocate the data for each block. We page align the data. */ + data = alloc_aligned(count * block_size, PAGE_SIZE); + if (!data) { + free(blocks); + return -ENOMEM; } - list_move_tail(&b->list, &b->bc->clean); - } -} + blocks_data_.reset(reinterpret_cast(data)); -/* - * |b->list| should be valid (either pointing to itself, on one of the other - * lists. - */ -static int issue_low_level(struct block *b, enum io_iocb_cmd opcode, const char *desc) -{ - int r; - struct block_cache *bc = b->bc; - struct iocb *control_blocks[1]; + for (i = 0; i < count; i++) { + block *b = blocks + i; + INIT_LIST_HEAD(&b->list_); + b->data_ = data + block_size * i; - assert(!test_flags(b, IO_PENDING)); - set_flags(b, IO_PENDING); - bc->nr_io_pending++; - list_move_tail(&b->list, &bc->io_pending); + list_add(&b->list_, &free_); + } - b->control_block.aio_lio_opcode = opcode; - control_blocks[0] = &b->control_block; - r = io_submit(bc->aio_context, 1, control_blocks); - if (r != 1) { - if (r < 0) { - perror("io_submit error"); - info(bc, "io_submit failed with %s op: %d\n", desc, r); - } else - info(bc, "could not submit IOs, with %s op\n", desc); - - complete_io(b, EIO); - return -EIO; + return 0; } - return 0; -} + block_cache::block * + block_cache::__alloc_block() + { + block *b; -static int issue_read(struct block *b) -{ - return issue_low_level(b, IO_CMD_PREAD, "read"); -} + if (list_empty(&free_)) + return NULL; -static int issue_write(struct block *b) -{ - return issue_low_level(b, IO_CMD_PWRITE, "write"); -} + b = list_first_entry(&free_, block, list_); + list_del(&b->list_); -static void wait_io(struct block_cache *bc) -{ - int r; - unsigned i; - - // FIXME: use a timeout to prevent hanging - r = io_getevents(bc->aio_context, 1, bc->nr_cache_blocks, bc->events, NULL); - if (r < 0) { - info(bc, "io_getevents failed %d\n", r); - exit(1); /* FIXME: handle more gracefully */ + return b; } - for (i = 0; i < static_cast(r); i++) { - struct io_event *e = bc->events + i; - struct block *b = container_of(e->obj, struct block, control_block); + /*---------------------------------------------------------------- + * Low level IO handling + * + * We cannot have two concurrent writes on the same block. + * eg, background writeback, put with dirty, flush? + * + * To avoid this we introduce some restrictions: + * + * i) A held block can never be written back. + * ii) You cannot get a block until writeback has completed. + * + *--------------------------------------------------------------*/ - if (e->res == bc->block_size << SECTOR_SHIFT) - complete_io(b, 0); - else if (e->res < 0) - complete_io(b, e->res); + /* + * This can be called from the context of the aio thread. So we have a + * separate 'top half' complete function that we know is only called by the + * main cache thread. + */ + void + block_cache::complete_io(block &b, int result) + { + b.error_ = result; + clear_flags(b, IO_PENDING); + nr_io_pending_--; + + if (b.error_) + list_move_tail(&b.list_, &errored_); else { - info(bc, "incomplete io, unexpected\n"); - } - } -} + if (test_flags(b, DIRTY)) { + clear_flags(b, DIRTY); + nr_dirty_--; + } -/*---------------------------------------------------------------- - * Clean/dirty list management - *--------------------------------------------------------------*/ - -/* - * We're using lru lists atm, but I think it would be worth - * experimenting with a multiqueue approach. - */ -static struct list_head *__categorise(struct block *b) -{ - if (b->error) - return &b->bc->errored; - - return (b->flags & DIRTY) ? &b->bc->dirty : &b->bc->clean; -} - -static void hit(struct block *b) -{ - list_move_tail(&b->list, __categorise(b)); -} - -/*---------------------------------------------------------------- - * High level IO handling - *--------------------------------------------------------------*/ -static void wait_all(struct block_cache *bc) -{ - while (!list_empty(&bc->io_pending)) - wait_io(bc); -} - -static void wait_specific(struct block *b) -{ - while (test_flags(b, IO_PENDING)) - wait_io(b->bc); -} - -static unsigned writeback(struct block_cache *bc, unsigned count) -{ - int r; - struct block *b, *tmp; - unsigned actual = 0; - - list_for_each_entry_safe (b, tmp, &bc->dirty, list) { - if (actual == count) - break; - - if (b->ref_count) - continue; - - r = issue_write(b); - if (!r) - actual++; - } - - info(bc, "writeback: requested %u, actual %u\n", count, actual); - return actual; -} - -/*---------------------------------------------------------------- - * Hash table - *---------------------------------------------------------------*/ - -/* - * |nr_buckets| must be a power of two. - */ -static void hash_init(struct block_cache *bc, unsigned nr_buckets) -{ - unsigned i; - - bc->nr_buckets = nr_buckets; - bc->mask = nr_buckets - 1; - - for (i = 0; i < nr_buckets; i++) - INIT_LIST_HEAD(bc->buckets + i); -} - -static unsigned hash(struct block_cache *bc, uint64_t index) -{ - const unsigned BIG_PRIME = 4294967291UL; - return (((unsigned) index) * BIG_PRIME) & bc->mask; -} - -static struct block *hash_lookup(struct block_cache *bc, block_index index) -{ - struct block *b; - unsigned bucket = hash(bc, index); - - list_for_each_entry (b, bc->buckets + bucket, hash_list) { - if (b->b.index == index) - return b; - } - - return NULL; -} - -static void hash_insert(struct block *b) -{ - unsigned bucket = hash(b->bc, b->b.index); - - list_move_tail(&b->hash_list, b->bc->buckets + bucket); -} - -static void hash_remove(struct block *b) -{ - list_del_init(&b->hash_list); -} - -/*---------------------------------------------------------------- - * High level allocation - *--------------------------------------------------------------*/ -static void setup_control_block(struct block *b) -{ - struct iocb *cb = &b->control_block; - size_t block_size_bytes = b->bc->block_size << SECTOR_SHIFT; - - memset(cb, 0, sizeof(*cb)); - cb->aio_fildes = b->bc->fd; - - cb->u.c.buf = b->b.data; - cb->u.c.offset = block_size_bytes * b->b.index; - cb->u.c.nbytes = block_size_bytes; -} - -static struct block *new_block(struct block_cache *bc, - block_index index) -{ - struct block *b; - - b = __alloc_block(bc); - if (!b) { - if (list_empty(&bc->clean)) { - if (list_empty(&bc->io_pending)) - writeback(bc, 9000); - wait_io(bc); - } - - if (!list_empty(&bc->clean)) { - b = list_first_entry(&bc->clean, struct block, list); - hash_remove(b); - list_del(&b->list); + list_move_tail(&b.list_, &clean_); } } - if (b) { - INIT_LIST_HEAD(&b->list); - INIT_LIST_HEAD(&b->hash_list); - b->bc = bc; - b->ref_count = 0; + /* + * |b->list| should be valid (either pointing to itself, on one of the other + * lists. + */ + int + block_cache::issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc) + { + int r; + iocb *control_blocks[1]; - b->error = 0; - clear_flags(b, IO_PENDING | DIRTY); + assert(!test_flags(b, IO_PENDING)); + set_flags(b, IO_PENDING); + nr_io_pending_++; + list_move_tail(&b.list_, &io_pending_); - b->b.index = index; - setup_control_block(b); + b.control_block_.aio_lio_opcode = opcode; + control_blocks[0] = &b.control_block_; + r = io_submit(aio_context_, 1, control_blocks); + if (r != 1) { + if (r < 0) { + perror("io_submit error"); + info("io_submit failed with %s op: %d\n", desc, r); + } else + info("could not submit IOs, with %s op\n", desc); - hash_insert(b); - } - - return b; -} - -/*---------------------------------------------------------------- - * Block reference counting - *--------------------------------------------------------------*/ -static void get_block(struct block *b) -{ - b->ref_count++; -} - -static void put_block(struct block *b) -{ - assert(b->ref_count); - b->ref_count--; -} - -static void mark_dirty(struct block *b) -{ - struct block_cache *bc = b->bc; - - if (!test_flags(b, DIRTY)) { - set_flags(b, DIRTY); - list_move_tail(&b->list, &b->bc->dirty); - bc->nr_dirty++; - } -} - -/*---------------------------------------------------------------- - * Public interface - *--------------------------------------------------------------*/ -unsigned calc_nr_cache_blocks(size_t mem, sector_t block_size) -{ - size_t space_per_block = (block_size << SECTOR_SHIFT) + sizeof(struct block); - unsigned r = mem / space_per_block; - - return (r < MIN_BLOCKS) ? MIN_BLOCKS : r; -} - -unsigned calc_nr_buckets(unsigned nr_blocks) -{ - unsigned r = 8; - unsigned n = nr_blocks / 4; - - if (n < 8) - n = 8; - - while (r < n) - r <<= 1; - - return r; -} - -void -block_cache_destroy(struct block_cache *bc) -{ - wait_all(bc); - - if (bc->aio_context) - io_destroy(bc->aio_context); - - if (bc->events) - free(bc->events); - - if (bc->blocks_memory) - free(bc->blocks_memory); - - if (bc->blocks_data) - free(bc->blocks_data); - - free(bc); -} - -struct block_cache * -block_cache_create(int fd, sector_t block_size, uint64_t on_disk_blocks, size_t mem) -{ - int r; - struct block_cache *bc; - unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); - unsigned nr_buckets = calc_nr_buckets(nr_cache_blocks); - - bc = static_cast(malloc(sizeof(*bc) + sizeof(*bc->buckets) * nr_buckets)); - if (bc) { - memset(bc, 0, sizeof(*bc)); - - bc->fd = fd; - bc->block_size = block_size; - bc->nr_data_blocks = on_disk_blocks; - bc->nr_cache_blocks = nr_cache_blocks; - - bc->events = static_cast(malloc(sizeof(*bc->events) * nr_cache_blocks)); - if (!bc->events) { - info(bc, "couldn't allocate events array\n"); - goto bad; + complete_io(b, EIO); + return -EIO; } - bc->aio_context = 0; /* needed or io_setup will fail */ - r = io_setup(nr_cache_blocks, &bc->aio_context); + return 0; + } + + int + block_cache::issue_read(block &b) + { + return issue_low_level(b, IO_CMD_PREAD, "read"); + } + + int + block_cache::issue_write(block &b) + { + std::cerr << "issuing write for block " << b.index_ << "\n"; + return issue_low_level(b, IO_CMD_PWRITE, "write"); + } + + void + block_cache::wait_io() + { + int r; + unsigned i; + + // FIXME: use a timeout to prevent hanging + r = io_getevents(aio_context_, 1, nr_cache_blocks_, &events_[0], NULL); if (r < 0) { - info(bc, "io_setup failed: %d\n", r); - goto bad; + info("io_getevents failed %d\n", r); + exit(1); /* FIXME: handle more gracefully */ } - hash_init(bc, nr_buckets); - INIT_LIST_HEAD(&bc->free); - INIT_LIST_HEAD(&bc->errored); - INIT_LIST_HEAD(&bc->dirty); - INIT_LIST_HEAD(&bc->clean); - INIT_LIST_HEAD(&bc->io_pending); + for (i = 0; i < static_cast(r); i++) { + io_event const &e = events_[i]; + block *b = container_of(e.obj, block, control_block_); - r = init_free_list(bc, nr_cache_blocks); - if (r) { - info(bc, "couldn't allocate blocks: %d\n", r); - goto bad; + if (e.res == block_size_ << SECTOR_SHIFT) + complete_io(*b, 0); + + else if (e.res < 0) + complete_io(*b, e.res); + + else + info("incomplete io, unexpected: %d\n", r); } } - return bc; + /*---------------------------------------------------------------- + * Clean/dirty list management + *--------------------------------------------------------------*/ -bad: - block_cache_destroy(bc); - return NULL; -} + /* + * We're using lru lists atm, but I think it would be worth + * experimenting with a multiqueue approach. + */ + list_head * + block_cache::__categorise(block &b) + { + if (b.error_) + return &errored_; -uint64_t block_cache_get_nr_blocks(struct block_cache *bc) -{ - return bc->nr_data_blocks; -} + return (b.flags_ & DIRTY) ? &dirty_ : &clean_; + } -static void zero_block(struct block *b) -{ - memset(b->b.data, 0, b->bc->block_size << SECTOR_SHIFT); - mark_dirty(b); -} + void + block_cache::hit(block &b) + { + list_move_tail(&b.list_, __categorise(b)); + } -static struct block *lookup_or_read_block(struct block_cache *bc, block_index index, unsigned flags) -{ - struct block *b = hash_lookup(bc, index); + /*---------------------------------------------------------------- + * High level IO handling + *--------------------------------------------------------------*/ + void + block_cache::wait_all() + { + while (!list_empty(&io_pending_)) + wait_io(); + } - if (b) { - if (test_flags(b, IO_PENDING)) - wait_specific(b); + void + block_cache::wait_specific(block &b) + { + while (test_flags(b, IO_PENDING)) + wait_io(); + } - if (flags & GF_ZERO) - zero_block(b); + unsigned + block_cache::writeback(unsigned count) + { + int r; + block *b, *tmp; + unsigned actual = 0; - } else { - if (flags & GF_CAN_BLOCK) { - b = new_block(bc, index); - if (b) { - if (flags & GF_ZERO) - zero_block(b); - else { - issue_read(b); - wait_specific(b); + list_for_each_entry_safe (b, tmp, &dirty_, list_) { + if (actual == count) + break; + + if (b->ref_count_) + continue; + + r = issue_write(*b); + if (!r) + actual++; + } + + info("writeback: requested %u, actual %u\n", count, actual); + return actual; + } + + /*---------------------------------------------------------------- + * Hash table + *---------------------------------------------------------------*/ + + /* + * |nr_buckets| must be a power of two. + */ + void + block_cache::hash_init(unsigned nr_buckets) + { + unsigned i; + + nr_buckets_ = nr_buckets; + mask_ = nr_buckets - 1; + + for (i = 0; i < nr_buckets; i++) + INIT_LIST_HEAD(&buckets_[i]); + } + + unsigned + block_cache::hash(uint64_t index) + { + const unsigned BIG_PRIME = 4294967291UL; + return (((unsigned) index) * BIG_PRIME) & mask_; + } + + block_cache::block * + block_cache::hash_lookup(block_index index) + { + block *b; + unsigned bucket = hash(index); + + list_for_each_entry (b, &buckets_[bucket], hash_list_) { + if (b->index_ == index) + return b; + } + + return NULL; + } + + void + block_cache::hash_insert(block &b) + { + unsigned bucket = hash(b.index_); + list_move_tail(&b.hash_list_, &buckets_[bucket]); + } + + void + block_cache::hash_remove(block &b) + { + list_del_init(&b.hash_list_); + } + + /*---------------------------------------------------------------- + * High level allocation + *--------------------------------------------------------------*/ + void + block_cache::setup_control_block(block &b) + { + iocb *cb = &b.control_block_; + size_t block_size_bytes = block_size_ << SECTOR_SHIFT; + + memset(cb, 0, sizeof(*cb)); + cb->aio_fildes = fd_; + + cb->u.c.buf = b.data_; + cb->u.c.offset = block_size_bytes * b.index_; + cb->u.c.nbytes = block_size_bytes; + } + + block_cache::block * + block_cache::new_block(block_index index) + { + block *b; + + b = __alloc_block(); + if (!b) { + if (list_empty(&clean_)) { + if (list_empty(&io_pending_)) + writeback(9000); + wait_io(); + } + + if (!list_empty(&clean_)) { + b = list_first_entry(&clean_, block, list_); + hash_remove(*b); + list_del(&b->list_); + } + } + + if (b) { + INIT_LIST_HEAD(&b->list_); + INIT_LIST_HEAD(&b->hash_list_); + b->bc_ = this; + b->ref_count_ = 0; + + b->error_ = 0; + clear_flags(*b, IO_PENDING | DIRTY); + + b->index_ = index; + setup_control_block(*b); + + hash_insert(*b); + } + + return b; + } + + /*---------------------------------------------------------------- + * Block reference counting + *--------------------------------------------------------------*/ + void + block_cache::mark_dirty(block &b) + { + if (!test_flags(b, DIRTY)) { + set_flags(b, DIRTY); + list_move_tail(&b.list_, &dirty_); + nr_dirty_++; + } + } + + unsigned + block_cache::calc_nr_cache_blocks(size_t mem, sector_t block_size) + { + size_t space_per_block = (block_size << SECTOR_SHIFT) + sizeof(block); + unsigned r = mem / space_per_block; + + return (r < MIN_BLOCKS) ? MIN_BLOCKS : r; + } + + unsigned + block_cache::calc_nr_buckets(unsigned nr_blocks) + { + unsigned r = 8; + unsigned n = nr_blocks / 4; + + if (n < 8) + n = 8; + + while (r < n) + r <<= 1; + + return r; + } + + block_cache::block_cache(int fd, sector_t block_size, uint64_t on_disk_blocks, size_t mem) + : nr_dirty_(0), + nr_io_pending_(0) + { + int r; + unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); + unsigned nr_buckets = calc_nr_buckets(nr_cache_blocks); + + info("block_size = %llu, on_disk_blocks = %llu, mem = %llu, nr_cache_blocks = %llu\n", + (unsigned long long) block_size, + (unsigned long long) on_disk_blocks, + (unsigned long long) mem, + (unsigned long long) nr_cache_blocks); + + + buckets_.resize(nr_buckets); + + fd_ = fd; + block_size_ = block_size; + nr_data_blocks_ = on_disk_blocks; + nr_cache_blocks_ = nr_cache_blocks; + + events_.resize(nr_cache_blocks); + + aio_context_ = 0; /* needed or io_setup will fail */ + r = io_setup(nr_cache_blocks, &aio_context_); + if (r < 0) + throw std::runtime_error("io_setup failed"); + + hash_init(nr_buckets); + INIT_LIST_HEAD(&free_); + INIT_LIST_HEAD(&errored_); + INIT_LIST_HEAD(&dirty_); + INIT_LIST_HEAD(&clean_); + INIT_LIST_HEAD(&io_pending_); + + r = init_free_list(nr_cache_blocks); + if (r) + throw std::runtime_error("couldn't allocate blocks"); + } + + block_cache::~block_cache() + { + wait_all(); + + // FIXME: use unique_ptrs + if (aio_context_) + io_destroy(aio_context_); + } + + uint64_t + block_cache::get_nr_blocks() const + { + return nr_data_blocks_; + } + + void + block_cache::zero_block(block &b) + { + memset(b.data_, 0, block_size_ << SECTOR_SHIFT); + mark_dirty(b); + } + + block_cache::block * + block_cache::lookup_or_read_block(block_index index, unsigned flags) + { + block *b = hash_lookup(index); + + if (b) { + if (test_flags(*b, IO_PENDING)) + wait_specific(*b); + + if (flags & GF_ZERO) + zero_block(*b); + + } else { + if (flags & GF_CAN_BLOCK) { + b = new_block(index); + if (b) { + if (flags & GF_ZERO) + zero_block(*b); + else { + issue_read(*b); + wait_specific(*b); + } } } } + + return (!b || b->error_) ? NULL : b; } - return (!b || b->error) ? NULL : b; -} + block_cache::block & + block_cache::get(block_index index, unsigned flags) + { + block *b = lookup_or_read_block(index, flags); -struct bc_block * -block_cache_get(struct block_cache *bc, block_index index, unsigned flags) -{ - struct block *b = lookup_or_read_block(bc, index, flags); + if (b) { + hit(*b); + b->ref_count_++; - if (b) { - hit(b); - get_block(b); - - return &b->b; - } - - return NULL; -} - -void -block_cache_put(struct bc_block *bcb, unsigned flags) -{ - unsigned nr_available; - struct block *b = container_of(bcb, struct block, b); - struct block_cache *bc = b->bc; - - put_block(b); - - if (flags & PF_DIRTY) { - mark_dirty(b); - - nr_available = bc->nr_cache_blocks - (bc->nr_dirty - bc->nr_io_pending); - if (nr_available < (WRITEBACK_LOW_THRESHOLD_PERCENT * bc->nr_cache_blocks / 100)) - writeback(bc, (WRITEBACK_HIGH_THRESHOLD_PERCENT * bc->nr_cache_blocks / 100) - nr_available); - } -} - -int -block_cache_flush(struct block_cache *bc) -{ - struct block *b; - - list_for_each_entry (b, &bc->dirty, list) { - if (b->ref_count) { - info(bc, "attempt to lock an already locked block\n"); - return -EAGAIN; + return *b; } - issue_write(b); + throw std::runtime_error("couldn't get block"); } - wait_all(bc); + void + block_cache::put(block_cache::block &b, unsigned flags) + { + if (b.ref_count_ == 0) + throw std::runtime_error("bad put"); - return list_empty(&bc->errored) ? 0 : -EIO; -} + b.ref_count_--; -void -block_cache_prefetch(struct block_cache *bc, block_index index) -{ - struct block *b = hash_lookup(bc, index); + if (flags & PF_DIRTY) { + mark_dirty(b); - if (!b) { - b = new_block(bc, index); - if (b) - issue_read(b); + // FIXME: factor out + unsigned nr_available = nr_cache_blocks_ - (nr_dirty_ - nr_io_pending_); + if (nr_available < (WRITEBACK_LOW_THRESHOLD_PERCENT * nr_cache_blocks_ / 100)) + writeback((WRITEBACK_HIGH_THRESHOLD_PERCENT * nr_cache_blocks_ / 100) - nr_available); + } + } + + int + block_cache::flush() + { + block *b; + + list_for_each_entry (b, &dirty_, list_) { + if (b->ref_count_) { + info("attempt to lock an already locked block\n"); + return -EAGAIN; + } + + issue_write(*b); + } + + wait_all(); + + return list_empty(&errored_) ? 0 : -EIO; + } + + void + block_cache::prefetch(block_index index) + { + block *b = hash_lookup(index); + + if (!b) { + b = new_block(index); + if (b) + issue_read(*b); + } + } + + //-------------------------------- + + unsigned + block_cache::test_flags(block &b, unsigned flags) + { + return b.flags_ & flags; + } + + void + block_cache::clear_flags(block &b, unsigned flags) + { + b.flags_ &= ~flags; + } + + void + block_cache::set_flags(block &b, unsigned flags) + { + b.flags_ |= flags; } } -/*----------------------------------------------------------------*/ - +//---------------------------------------------------------------- diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 046083f..f59a1a0 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -1,54 +1,173 @@ #ifndef BLOCK_CACHE_H #define BLOCK_CACHE_H +#include "block-cache/buffer.h" +#include "block-cache/list.h" + +#include +#include + +#include +#include #include #include +#include -/*----------------------------------------------------------------*/ +//---------------------------------------------------------------- -/* FIXME: add logging */ +namespace bcache { +#if 0 + class validator { + public: + typedef boost::shared_ptr ptr; -/*----------------------------------------------------------------*/ + virtual ~validator() {} -/* - * This library is not thread-safe. - */ -typedef uint64_t block_index; + virtual void check(buffer const &b, block_address location) const = 0; + virtual void prepare(buffer &b, block_address location) const = 0; + }; -struct block_cache; + class noop_validator : public validator { + public: + void check(buffer const &b, block_address location) const {} + void prepare(buffer &b, block_address location) const {} + }; +#endif + //---------------------------------------------------------------- -struct bc_block { - block_index index; - void *data; -}; + // FIXME: throw exceptions rather than returning errors + class block_cache : private boost::noncopyable { + public: + enum block_flags { + IO_PENDING = (1 << 0), + DIRTY = (1 << 1) + }; -typedef uint64_t sector_t; + class block : private boost::noncopyable { + public: + uint64_t get_index() const { + return index_; + } -struct block_cache *block_cache_create(int fd, sector_t block_size, - uint64_t max_nr_blocks, size_t mem); -void block_cache_destroy(struct block_cache *bc); + void *get_data() const { + return data_; + } -uint64_t block_cache_get_nr_blocks(struct block_cache *bc); + private: + friend class block_cache; -enum get_flags { - GF_ZERO = (1 << 0), - GF_CAN_BLOCK = (1 << 1) -}; -struct bc_block *block_cache_get(struct block_cache *bc, block_index index, unsigned flags); + uint64_t index_; + void *data_; -enum put_flags { - PF_DIRTY = (1 << 0), -}; -void block_cache_put(struct bc_block *b, unsigned flags); + list_head list_; + list_head hash_list_; -/* - * Flush can fail if an earlier write failed. You do not know which block - * failed. Make sure you build your recovery with this in mind. - */ -int block_cache_flush(struct block_cache *bc); + block_cache *bc_; + unsigned ref_count_; -void block_cache_prefetch(struct block_cache *bc, block_index index); + int error_; + unsigned flags_; -/*----------------------------------------------------------------*/ + iocb control_block_; + }; + + typedef uint64_t block_index; + typedef uint64_t sector_t; + + //-------------------------------- + + block_cache(int fd, sector_t block_size, + uint64_t max_nr_blocks, size_t mem); + ~block_cache(); + + uint64_t get_nr_blocks() const; + + enum get_flags { + GF_ZERO = (1 << 0), + GF_CAN_BLOCK = (1 << 1) + }; + + // FIXME: what if !GF_CAN_BLOCK? + block_cache::block &get(block_index index, unsigned flags); + + enum put_flags { + PF_DIRTY = (1 << 0), + }; + + void put(block_cache::block &block, unsigned flags); + + /* + * Flush can fail if an earlier write failed. You do not know which block + * failed. Make sure you build your recovery with this in mind. + */ + int flush(); + void prefetch(block_index index); + + private: + int init_free_list(unsigned count); + block *__alloc_block(); + void complete_io(block &b, int result); + int issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc); + int issue_read(block &b); + int issue_write(block &b); + void wait_io(); + list_head *__categorise(block &b); + void hit(block &b); + void wait_all(); + void wait_specific(block &b); + unsigned writeback(unsigned count); + void hash_init(unsigned nr_buckets); + unsigned hash(uint64_t index); + block *hash_lookup(block_index index); + void hash_insert(block &b); + void hash_remove(block &b); + void setup_control_block(block &b); + block *new_block(block_index index); + void mark_dirty(block &b); + unsigned calc_nr_cache_blocks(size_t mem, sector_t block_size); + unsigned calc_nr_buckets(unsigned nr_blocks); + void zero_block(block &b); + block *lookup_or_read_block(block_index index, unsigned flags); + unsigned test_flags(block &b, unsigned flags); + void clear_flags(block &b, unsigned flags); + void set_flags(block &b, unsigned flags); + + //-------------------------------- + + int fd_; + sector_t block_size_; + uint64_t nr_data_blocks_; + uint64_t nr_cache_blocks_; + + std::auto_ptr blocks_memory_; // FIXME: change to a vector + std::auto_ptr blocks_data_; + + io_context_t aio_context_; + std::vector events_; + + /* + * Blocks on the free list are not initialised, apart from the + * b.data field. + */ + list_head free_; + list_head errored_; + list_head dirty_; + list_head clean_; + + unsigned nr_io_pending_; + struct list_head io_pending_; + + unsigned nr_dirty_; + + /* + * Hash table fields. + */ + unsigned nr_buckets_; + unsigned mask_; + std::vector buckets_; + }; +} + +//---------------------------------------------------------------- #endif diff --git a/persistent-data/buffer.h b/block-cache/buffer.h similarity index 100% rename from persistent-data/buffer.h rename to block-cache/buffer.h diff --git a/caching/superblock.cc b/caching/superblock.cc index 93a8d60..8b34af3 100644 --- a/caching/superblock.cc +++ b/caching/superblock.cc @@ -277,7 +277,7 @@ namespace validator { struct sb_validator : public block_manager<>::validator { virtual void check(buffer<> const &b, block_address location) const { - superblock_disk const *sbd = reinterpret_cast(&b); + superblock_disk const *sbd = reinterpret_cast(b.raw()); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(sbd->csum)) @@ -285,7 +285,7 @@ namespace validator { } virtual void prepare(buffer<> &b, block_address location) const { - superblock_disk *sbd = reinterpret_cast(&b); + superblock_disk *sbd = reinterpret_cast(b.raw()); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); sbd->csum = to_disk(sum.get_sum()); diff --git a/era/superblock.cc b/era/superblock.cc index c319e9b..9d0ae57 100644 --- a/era/superblock.cc +++ b/era/superblock.cc @@ -212,7 +212,7 @@ namespace era_validator { // FIXME: turn into a template, we have 3 similar classes now struct sb_validator : public block_manager<>::validator { virtual void check(buffer<> const &b, block_address location) const { - superblock_disk const *sbd = reinterpret_cast(&b); + superblock_disk const *sbd = reinterpret_cast(b.raw()); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(sbd->csum)) @@ -220,7 +220,7 @@ namespace era_validator { } virtual void prepare(buffer<> &b, block_address location) const { - superblock_disk *sbd = reinterpret_cast(&b); + superblock_disk *sbd = reinterpret_cast(b.raw()); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); sbd->csum = to_disk(sum.get_sum()); diff --git a/persistent-data/block.h b/persistent-data/block.h index c514383..487a0e1 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -20,7 +20,7 @@ #define BLOCK_H #include "block-cache/block_cache.h" -#include "persistent-data/buffer.h" +#include "block-cache/buffer.h" #include #include @@ -35,6 +35,8 @@ //---------------------------------------------------------------- namespace persistent_data { + using namespace bcache; + uint32_t const MD_BLOCK_SIZE = 4096; @@ -77,10 +79,11 @@ namespace persistent_data { BT_NORMAL }; + // FIXME: eventually this will disappear to be replaced with block_cache::block struct block : private boost::noncopyable { typedef boost::shared_ptr ptr; - block(block_cache *bc, + block(block_cache &bc, block_address location, block_type bt, typename validator::ptr v, @@ -110,7 +113,8 @@ namespace persistent_data { private: void check_not_unlocked() const; - bc_block *internal_; + block_cache &bc_; + block_cache::block *internal_; typename validator::ptr validator_; block_type bt_; bool dirty_; @@ -196,7 +200,9 @@ namespace persistent_data { void write_block(typename block::ptr b) const; int fd_; - block_cache *bc_; + + // FIXME: the mutable is a fudge to allow flush() to be const, which I'm not sure is necc. + mutable block_cache bc_; }; // A little utility to help build validators diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index ab9a129..4c39c7a 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -40,7 +40,7 @@ namespace { unsigned const SECTOR_SHIFT = 9; // FIXME: these will slow it down until we start doing async io. - int const OPEN_FLAGS = O_DIRECT | O_SYNC; + int const OPEN_FLAGS = O_DIRECT; // FIXME: introduce a new exception for this, or at least lift this // to exception.h @@ -106,31 +106,27 @@ namespace { namespace persistent_data { template - block_manager::block::block(block_cache *bc, + block_manager::block::block(block_cache &bc, block_address location, block_type bt, typename validator::ptr v, bool zero) - : validator_(v), - bt_(bt), - dirty_(false), - unlocked_(false), - buffer_(0, true) // FIXME: we don't know if it's writeable here :( + : bc_(bc), + validator_(v), + bt_(bt), + dirty_(false), + unlocked_(false), + buffer_(0, true) // FIXME: we don't know if it's writeable here :( { if (zero) { - internal_ = block_cache_get(bc, location, GF_ZERO | GF_CAN_BLOCK); - if (!internal_) - throw std::runtime_error("Couldn't get block"); + internal_ = &bc.get(location, block_cache::GF_ZERO | block_cache::GF_CAN_BLOCK); dirty_ = true; + buffer_.set_data(internal_->get_data()); } else { - internal_ = block_cache_get(bc, location, GF_CAN_BLOCK); - if (!internal_) - throw std::runtime_error("Couldn't get block"); - - validator_->check(buffer_, internal_->index); + internal_ = &bc.get(location, block_cache::GF_CAN_BLOCK); + buffer_.set_data(internal_->get_data()); + validator_->check(buffer_, internal_->get_index()); } - - buffer_.set_data(internal_->data); } template @@ -144,8 +140,9 @@ namespace persistent_data { void block_manager::block::unlock() { - validator_->prepare(buffer_, internal_->index); - block_cache_put(internal_, dirty_ ? PF_DIRTY : 0); + if (dirty_) + validator_->prepare(buffer_, internal_->get_index()); + bc_.put(*internal_, dirty_ ? block_cache::PF_DIRTY : 0); unlocked_ = true; } @@ -161,7 +158,7 @@ namespace persistent_data { block_manager::block::get_location() const { check_not_unlocked(); - return internal_->index; + return internal_->get_index(); } template @@ -196,12 +193,12 @@ namespace persistent_data { if (dirty_) // It may have already happened, by calling // this we ensure we're consistent. - validator_->prepare(*internal_->data, internal_->index); + validator_->prepare(*internal_->get_data(), internal_->get_index()); validator_ = v; if (check) - validator_->check(*internal_->data, internal_->index); + validator_->check(*internal_->get_data(), internal_->get_index()); } } @@ -301,14 +298,9 @@ namespace persistent_data { block_address nr_blocks, unsigned max_concurrent_blocks, mode m) + : fd_(open_block_file(path, nr_blocks * BlockSize, m == READ_WRITE)), + bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 4) { - // Open the file descriptor - fd_ = open_block_file(path, nr_blocks * BlockSize, m == READ_WRITE); - - // Create the cache - bc_ = block_cache_create(fd_, BlockSize << SECTOR_SHIFT, nr_blocks, 1024u * BlockSize * 1.2); - if (!bc_) - throw std::runtime_error("couldn't create block cache"); } template @@ -360,7 +352,7 @@ namespace persistent_data { block_address block_manager::get_nr_blocks() const { - return block_cache_get_nr_blocks(bc_); + return bc_.get_nr_blocks(); } template @@ -374,7 +366,7 @@ namespace persistent_data { void block_manager::flush() const { - block_cache_flush(bc_); + bc_.flush(); } } diff --git a/persistent-data/data-structures/array.h b/persistent-data/data-structures/array.h index 6c76bf2..1d8fce6 100644 --- a/persistent-data/data-structures/array.h +++ b/persistent-data/data-structures/array.h @@ -33,7 +33,7 @@ namespace persistent_data { struct array_block_validator : public block_manager<>::validator { virtual void check(buffer<> const &b, block_address location) const { - array_block_disk const *data = reinterpret_cast(&b); + array_block_disk const *data = reinterpret_cast(b.raw()); crc32c sum(ARRAY_CSUM_XOR); sum.append(&data->max_entries, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(data->csum)) @@ -44,7 +44,7 @@ namespace persistent_data { } virtual void prepare(buffer<> &b, block_address location) const { - array_block_disk *data = reinterpret_cast(&b); + array_block_disk *data = reinterpret_cast(b.raw()); data->blocknr = to_disk(location); crc32c sum(ARRAY_CSUM_XOR); diff --git a/persistent-data/data-structures/btree.tcc b/persistent-data/data-structures/btree.tcc index 1234aa0..a220334 100644 --- a/persistent-data/data-structures/btree.tcc +++ b/persistent-data/data-structures/btree.tcc @@ -34,7 +34,7 @@ namespace { struct btree_node_validator : public block_manager<>::validator { virtual void check(buffer<> const &b, block_address location) const { - disk_node const *data = reinterpret_cast(&b); + disk_node const *data = reinterpret_cast(b.raw()); node_header const *n = &data->header; crc32c sum(BTREE_CSUM_XOR); sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); @@ -46,7 +46,7 @@ namespace { } virtual void prepare(buffer<> &b, block_address location) const { - disk_node *data = reinterpret_cast(&b); + disk_node *data = reinterpret_cast(b.raw()); node_header *n = &data->header; n->blocknr = to_disk(location); diff --git a/persistent-data/space-maps/disk.cc b/persistent-data/space-maps/disk.cc index 0c851f6..f75f0a9 100644 --- a/persistent-data/space-maps/disk.cc +++ b/persistent-data/space-maps/disk.cc @@ -39,7 +39,7 @@ namespace { struct bitmap_block_validator : public block_manager<>::validator { virtual void check(buffer<> const &b, block_address location) const { - bitmap_header const *data = reinterpret_cast(&b); + bitmap_header const *data = reinterpret_cast(b.raw()); crc32c sum(BITMAP_CSUM_XOR); sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(data->csum)) @@ -50,7 +50,7 @@ namespace { } virtual void prepare(buffer<> &b, block_address location) const { - bitmap_header *data = reinterpret_cast(&b); + bitmap_header *data = reinterpret_cast(b.raw()); data->blocknr = to_disk(location); crc32c sum(BITMAP_CSUM_XOR); @@ -66,7 +66,8 @@ namespace { // FIXME: factor out the common code in these validators struct index_block_validator : public block_manager<>::validator { virtual void check(buffer<> const &b, block_address location) const { - metadata_index const *mi = reinterpret_cast(&b); + metadata_index const *mi = reinterpret_cast(b.raw()); + std::cerr << "check mi = " << mi << "\n"; crc32c sum(INDEX_CSUM_XOR); sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(mi->csum_)) @@ -77,7 +78,8 @@ namespace { } virtual void prepare(buffer<> &b, block_address location) const { - metadata_index *mi = reinterpret_cast(&b); + metadata_index *mi = reinterpret_cast(b.raw()); + std::cerr << "prepare mi = " << mi << "\n"; mi->blocknr_ = to_disk(location); crc32c sum(INDEX_CSUM_XOR); @@ -630,7 +632,7 @@ namespace { tm_->shadow(bitmap_root_, index_validator()); bitmap_root_ = p.first.get_location(); - metadata_index *mdi = reinterpret_cast(&p.first.data()); + metadata_index *mdi = reinterpret_cast(p.first.data().raw()); for (unsigned i = 0; i < entries_.size(); i++) index_entry_traits::pack(entries_[i], mdi->index[i]); diff --git a/thin-provisioning/superblock.cc b/thin-provisioning/superblock.cc index 4412696..e808c6c 100644 --- a/thin-provisioning/superblock.cc +++ b/thin-provisioning/superblock.cc @@ -87,7 +87,7 @@ namespace { struct sb_validator : public block_manager<>::validator { virtual void check(buffer<> const &b, block_address location) const { - superblock_disk const *sbd = reinterpret_cast(&b); + superblock_disk const *sbd = reinterpret_cast(b.raw()); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(sbd->csum_)) @@ -95,7 +95,7 @@ namespace { } virtual void prepare(buffer<> &b, block_address location) const { - superblock_disk *sbd = reinterpret_cast(&b); + superblock_disk *sbd = reinterpret_cast(b.raw()); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t)); sbd->csum_ = to_disk(sum.get_sum()); From 7e870ea5a66197277db96eeb4f178b5fac6c1db6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 25 Jul 2014 14:46:51 +0100 Subject: [PATCH 062/165] start removing block_manager<>::block --- block-cache/block_cache.cc | 43 ++++++--- block-cache/block_cache.h | 90 +++++++++++++++---- block-cache/buffer.h | 2 +- caching/superblock.cc | 18 ++-- caching/superblock.h | 2 +- era/superblock.cc | 18 ++-- persistent-data/block.h | 49 +++------- persistent-data/block.tcc | 64 ++++--------- persistent-data/data-structures/array.h | 12 +-- persistent-data/data-structures/array_block.h | 4 +- persistent-data/data-structures/btree.h | 15 ++-- persistent-data/data-structures/btree.tcc | 10 +-- persistent-data/space-maps/disk.cc | 38 ++++---- persistent-data/transaction_manager.cc | 2 +- persistent-data/transaction_manager.h | 2 +- thin-provisioning/metadata.cc | 2 +- thin-provisioning/superblock.cc | 18 ++-- thin-provisioning/superblock.h | 2 +- 18 files changed, 208 insertions(+), 183 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 6127a23..9a2944c 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -150,6 +150,7 @@ namespace bcache { int r; iocb *control_blocks[1]; + // FIXME: put this back in assert(!test_flags(b, IO_PENDING)); set_flags(b, IO_PENDING); nr_io_pending_++; @@ -175,13 +176,16 @@ namespace bcache { int block_cache::issue_read(block &b) { + std::cerr << "issuing read - that's a shame: " << b.index_ << "\n"; + assert(!test_flags(b, IO_PENDING)); return issue_low_level(b, IO_CMD_PREAD, "read"); } int block_cache::issue_write(block &b) { - std::cerr << "issuing write for block " << b.index_ << "\n"; + assert(!test_flags(b, IO_PENDING)); + b.v_->prepare(b.data_, b.index_); return issue_low_level(b, IO_CMD_PWRITE, "write"); } @@ -303,7 +307,7 @@ namespace bcache { } block_cache::block * - block_cache::hash_lookup(block_index index) + block_cache::hash_lookup(block_address index) { block *b; unsigned bucket = hash(index); @@ -347,7 +351,7 @@ namespace bcache { } block_cache::block * - block_cache::new_block(block_index index) + block_cache::new_block(block_address index) { block *b; @@ -485,7 +489,8 @@ namespace bcache { } block_cache::block * - block_cache::lookup_or_read_block(block_index index, unsigned flags) + block_cache::lookup_or_read_block(block_address index, unsigned flags, + validator::ptr v) { block *b = hash_lookup(index); @@ -495,16 +500,26 @@ namespace bcache { if (flags & GF_ZERO) zero_block(*b); + else { + if (b->v_.get() && + b->v_.get() != v.get() && + test_flags(*b, DIRTY)) + b->v_->prepare(b->data_, b->index_); + } + b->v_ = v; } else { if (flags & GF_CAN_BLOCK) { b = new_block(index); if (b) { + b->v_ = v; + if (flags & GF_ZERO) zero_block(*b); else { issue_read(*b); wait_specific(*b); + v->check(b->data_, b->index_); } } } @@ -514,9 +529,9 @@ namespace bcache { } block_cache::block & - block_cache::get(block_index index, unsigned flags) + block_cache::get(block_address index, unsigned flags, validator::ptr v) { - block *b = lookup_or_read_block(index, flags); + block *b = lookup_or_read_block(index, flags, v); if (b) { hit(*b); @@ -549,24 +564,26 @@ namespace bcache { int block_cache::flush() { - block *b; + block *b, *tmp; - list_for_each_entry (b, &dirty_, list_) { - if (b->ref_count_) { - info("attempt to lock an already locked block\n"); - return -EAGAIN; - } + std::cerr << "in flush\n"; + list_for_each_entry_safe (b, tmp, &dirty_, list_) { + if (b->ref_count_ || test_flags(*b, IO_PENDING)) + // The superblock may well be still locked. + continue; issue_write(*b); } + std::cerr << "issued all writes\n"; wait_all(); + std::cerr << "wait all returned\n"; return list_empty(&errored_) ? 0 : -EIO; } void - block_cache::prefetch(block_index index) + block_cache::prefetch(block_address index) { block *b = hash_lookup(index); diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index f59a1a0..9955af3 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -16,23 +16,25 @@ //---------------------------------------------------------------- namespace bcache { -#if 0 + typedef uint64_t block_address; + typedef uint64_t sector_t; + class validator { public: typedef boost::shared_ptr ptr; virtual ~validator() {} - virtual void check(buffer const &b, block_address location) const = 0; - virtual void prepare(buffer &b, block_address location) const = 0; + virtual void check(void const *data, block_address location) const = 0; + virtual void prepare(void *data, block_address location) const = 0; }; class noop_validator : public validator { public: - void check(buffer const &b, block_address location) const {} - void prepare(buffer &b, block_address location) const {} + void check(void const *data, block_address location) const {} + void prepare(void *data, block_address location) const {} }; -#endif + //---------------------------------------------------------------- // FIXME: throw exceptions rather than returning errors @@ -45,6 +47,10 @@ namespace bcache { class block : private boost::noncopyable { public: + block() + : v_() { + } + uint64_t get_index() const { return index_; } @@ -69,11 +75,9 @@ namespace bcache { unsigned flags_; iocb control_block_; + validator::ptr v_; }; - typedef uint64_t block_index; - typedef uint64_t sector_t; - //-------------------------------- block_cache(int fd, sector_t block_size, @@ -88,7 +92,7 @@ namespace bcache { }; // FIXME: what if !GF_CAN_BLOCK? - block_cache::block &get(block_index index, unsigned flags); + block_cache::block &get(block_address index, unsigned flags, validator::ptr v); enum put_flags { PF_DIRTY = (1 << 0), @@ -101,7 +105,7 @@ namespace bcache { * failed. Make sure you build your recovery with this in mind. */ int flush(); - void prefetch(block_index index); + void prefetch(block_address index); private: int init_free_list(unsigned count); @@ -118,16 +122,16 @@ namespace bcache { unsigned writeback(unsigned count); void hash_init(unsigned nr_buckets); unsigned hash(uint64_t index); - block *hash_lookup(block_index index); + block *hash_lookup(block_address index); void hash_insert(block &b); void hash_remove(block &b); void setup_control_block(block &b); - block *new_block(block_index index); + block *new_block(block_address index); void mark_dirty(block &b); unsigned calc_nr_cache_blocks(size_t mem, sector_t block_size); unsigned calc_nr_buckets(unsigned nr_blocks); void zero_block(block &b); - block *lookup_or_read_block(block_index index, unsigned flags); + block *lookup_or_read_block(block_address index, unsigned flags, validator::ptr v); unsigned test_flags(block &b, unsigned flags); void clear_flags(block &b, unsigned flags); void set_flags(block &b, unsigned flags); @@ -154,11 +158,11 @@ namespace bcache { list_head dirty_; list_head clean_; + unsigned nr_dirty_; + unsigned nr_io_pending_; struct list_head io_pending_; - unsigned nr_dirty_; - /* * Hash table fields. */ @@ -166,6 +170,60 @@ namespace bcache { unsigned mask_; std::vector buckets_; }; + +#if 0 + class auto_lock { + public: + auto_lock(block_cache &bc, block_address index, bool zero, validator::ptr v, unsigned put_flags) + : bc_(bc), + b_(bc.get(index, (zero ? block_cache::GF_ZERO : 0) | block_cache::GF_CAN_BLOCK, v)), + put_flags_(put_flags), + holders_(new unsigned) { + *holders_ = 1; + } + + virtual ~auto_lock() { + bc_.put(b_, put_flags_); + } + + auto_lock operator =(auto_lock const &rhs) { + if (this != &rhs) { + bc_ = rhs.bc_; + + + + void const *data() const { + return b_.get_data(); + } + + private: + block_cache &bc_; + block_cache::block &b_; + unsigned put_flags_; + unsigned *holders_; + }; + + class auto_read_lock : public auto_lock { + public: + auto_read_lock(block_cache &bc, block_address index, bool zero, validator::ptr v) + : auto_lock(bc, index, zero, v, 0) { + } + + using auto_lock::data(); + }; + + class auto_write_lock : public auto_lock { + public: + auto_write_lock(block_cache &bc, block_address index, bool zero, validator::ptr v) + : auto_lock(bc, index, zero, v, block_cache::DIRTY) { + } + + using auto_lock::data(); + void *data() { + return b_.get_data(); + } + }; +#endif } //---------------------------------------------------------------- diff --git a/block-cache/buffer.h b/block-cache/buffer.h index 34aa31f..cea152a 100644 --- a/block-cache/buffer.h +++ b/block-cache/buffer.h @@ -30,7 +30,7 @@ //---------------------------------------------------------------- -namespace persistent_data { +namespace bcache { uint32_t const DEFAULT_BUFFER_SIZE = 4096; template diff --git a/caching/superblock.cc b/caching/superblock.cc index 8b34af3..7ecc916 100644 --- a/caching/superblock.cc +++ b/caching/superblock.cc @@ -275,25 +275,25 @@ namespace validator { unsigned const SECTOR_TO_BLOCK_SHIFT = 3; uint32_t const SUPERBLOCK_CSUM_SEED = 9031977; - struct sb_validator : public block_manager<>::validator { - virtual void check(buffer<> const &b, block_address location) const { - superblock_disk const *sbd = reinterpret_cast(b.raw()); + struct sb_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + superblock_disk const *sbd = reinterpret_cast(raw); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(sbd->csum)) throw checksum_error("bad checksum in superblock"); } - virtual void prepare(buffer<> &b, block_address location) const { - superblock_disk *sbd = reinterpret_cast(b.raw()); + virtual void prepare(void *raw, block_address location) const { + superblock_disk *sbd = reinterpret_cast(raw); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); sbd->csum = to_disk(sum.get_sum()); } }; - block_manager<>::validator::ptr mk_v() { - return block_manager<>::validator::ptr(new sb_validator); + bcache::validator::ptr mk_v() { + return bcache::validator::ptr(new sb_validator); } } @@ -304,7 +304,7 @@ caching::read_superblock(block_manager<>::ptr bm, block_address location) { superblock sb; block_manager<>::read_ref r = bm->read_lock(location, validator::mk_v()); - superblock_disk const *sbd = reinterpret_cast(&r.data()); + superblock_disk const *sbd = reinterpret_cast(r.data()); superblock_traits::unpack(*sbd, sb); return sb; @@ -314,7 +314,7 @@ void caching::write_superblock(block_manager<>::ptr bm, superblock const &sb, block_address location) { block_manager<>::write_ref w = bm->superblock_zero(location, validator::mk_v()); - superblock_traits::pack(sb, *reinterpret_cast(w.data().raw())); + superblock_traits::pack(sb, *reinterpret_cast(w.data())); } void diff --git a/caching/superblock.h b/caching/superblock.h index d1a24aa..b59365a 100644 --- a/caching/superblock.h +++ b/caching/superblock.h @@ -128,7 +128,7 @@ namespace caching { //-------------------------------- - persistent_data::block_manager<>::validator::ptr superblock_validator(); + bcache::validator::ptr superblock_validator(); superblock read_superblock(persistent_data::block_manager<>::ptr bm, persistent_data::block_address location = SUPERBLOCK_LOCATION); diff --git a/era/superblock.cc b/era/superblock.cc index 9d0ae57..e013064 100644 --- a/era/superblock.cc +++ b/era/superblock.cc @@ -210,25 +210,25 @@ namespace era_validator { uint32_t const SUPERBLOCK_CSUM_SEED = 146538381; // FIXME: turn into a template, we have 3 similar classes now - struct sb_validator : public block_manager<>::validator { - virtual void check(buffer<> const &b, block_address location) const { - superblock_disk const *sbd = reinterpret_cast(b.raw()); + struct sb_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + superblock_disk const *sbd = reinterpret_cast(raw); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(sbd->csum)) throw checksum_error("bad checksum in superblock"); } - virtual void prepare(buffer<> &b, block_address location) const { - superblock_disk *sbd = reinterpret_cast(b.raw()); + virtual void prepare(void *raw, block_address location) const { + superblock_disk *sbd = reinterpret_cast(raw); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); sbd->csum = to_disk(sum.get_sum()); } }; - block_manager<>::validator::ptr mk_v() { - return block_manager<>::validator::ptr(new sb_validator); + bcache::validator::ptr mk_v() { + return bcache::validator::ptr(new sb_validator); } } @@ -239,7 +239,7 @@ era::read_superblock(block_manager<>::ptr bm, block_address location) { superblock sb; block_manager<>::read_ref r = bm->read_lock(location, era_validator::mk_v()); - superblock_disk const *sbd = reinterpret_cast(&r.data()); + superblock_disk const *sbd = reinterpret_cast(r.data()); superblock_traits::unpack(*sbd, sb); return sb; @@ -249,7 +249,7 @@ void era::write_superblock(block_manager<>::ptr bm, superblock const &sb, block_address location) { block_manager<>::write_ref w = bm->superblock_zero(location, era_validator::mk_v()); - superblock_traits::pack(sb, *reinterpret_cast(w.data().raw())); + superblock_traits::pack(sb, *reinterpret_cast(w.data())); } void diff --git a/persistent-data/block.h b/persistent-data/block.h index 487a0e1..7ade102 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -40,8 +40,6 @@ namespace persistent_data { uint32_t const MD_BLOCK_SIZE = 4096; - typedef uint64_t block_address; - template class block_manager : private boost::noncopyable { public: @@ -58,22 +56,6 @@ namespace persistent_data { unsigned max_concurrent_locks, mode m); - class validator { - public: - typedef boost::shared_ptr ptr; - - virtual ~validator() {} - - virtual void check(buffer const &b, block_address location) const = 0; - virtual void prepare(buffer &b, block_address location) const = 0; - }; - - class noop_validator : public validator { - public: - void check(buffer const &b, block_address location) const {} - void prepare(buffer &b, block_address location) const {} - }; - enum block_type { BT_SUPERBLOCK, BT_NORMAL @@ -86,7 +68,7 @@ namespace persistent_data { block(block_cache &bc, block_address location, block_type bt, - typename validator::ptr v, + typename bcache::validator::ptr v, bool zero = false); ~block(); @@ -98,14 +80,11 @@ namespace persistent_data { // FIXME: finish } - void change_validator(typename block_manager::validator::ptr v, - bool check = true); - block_type get_type() const; uint64_t get_location() const; - buffer const &get_buffer() const; - buffer &get_buffer(); + void const *get_data() const; + void *get_data(); void mark_dirty(); void unlock(); @@ -115,11 +94,9 @@ namespace persistent_data { block_cache &bc_; block_cache::block *internal_; - typename validator::ptr validator_; block_type bt_; bool dirty_; bool unlocked_; - buffer buffer_; }; class read_ref { @@ -134,7 +111,7 @@ namespace persistent_data { read_ref const &operator =(read_ref const &rhs); block_address get_location() const; - buffer const &data() const; + void const * data() const; protected: block_manager const *bm_; @@ -150,24 +127,24 @@ namespace persistent_data { typename block::ptr b); using read_ref::data; - buffer &data(); + void *data(); }; // Locking methods read_ref read_lock(block_address location, typename validator::ptr v = - typename validator::ptr(new noop_validator())) const; + typename validator::ptr(new bcache::noop_validator())) const; write_ref write_lock(block_address location, typename validator::ptr v = - typename validator::ptr(new noop_validator())); + typename validator::ptr(new bcache::noop_validator())); write_ref write_lock_zero(block_address location, typename validator::ptr v = - typename validator::ptr(new noop_validator())); + typename validator::ptr(new bcache::noop_validator())); // The super block is the one that should be written last. // Unlocking this block triggers the following events: @@ -181,10 +158,10 @@ namespace persistent_data { // being unlocked then an exception will be thrown. write_ref superblock(block_address b, typename validator::ptr v = - typename validator::ptr(new noop_validator())); + typename validator::ptr(new bcache::noop_validator())); write_ref superblock_zero(block_address b, typename validator::ptr v = - typename validator::ptr(new noop_validator())); + typename validator::ptr(new bcache::noop_validator())); block_address get_nr_blocks() const; @@ -206,9 +183,9 @@ namespace persistent_data { }; // A little utility to help build validators - inline block_manager<>::validator::ptr - mk_validator(block_manager<>::validator *v) { - return block_manager<>::validator::ptr(v); + inline bcache::validator::ptr + mk_validator(bcache::validator *v) { + return bcache::validator::ptr(v); } } diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 4c39c7a..ccab185 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -112,20 +112,15 @@ namespace persistent_data { typename validator::ptr v, bool zero) : bc_(bc), - validator_(v), bt_(bt), dirty_(false), - unlocked_(false), - buffer_(0, true) // FIXME: we don't know if it's writeable here :( + unlocked_(false) { if (zero) { - internal_ = &bc.get(location, block_cache::GF_ZERO | block_cache::GF_CAN_BLOCK); + internal_ = &bc.get(location, block_cache::GF_ZERO | block_cache::GF_CAN_BLOCK, v); dirty_ = true; - buffer_.set_data(internal_->get_data()); } else { - internal_ = &bc.get(location, block_cache::GF_CAN_BLOCK); - buffer_.set_data(internal_->get_data()); - validator_->check(buffer_, internal_->get_index()); + internal_ = &bc.get(location, block_cache::GF_CAN_BLOCK, v); } } @@ -140,8 +135,6 @@ namespace persistent_data { void block_manager::block::unlock() { - if (dirty_) - validator_->prepare(buffer_, internal_->get_index()); bc_.put(*internal_, dirty_ ? block_cache::PF_DIRTY : 0); unlocked_ = true; } @@ -162,17 +155,17 @@ namespace persistent_data { } template - buffer const & - block_manager::block::get_buffer() const + void const * + block_manager::block::get_data() const { - return buffer_; + return internal_->get_data(); } template - buffer & - block_manager::block::get_buffer() + void * + block_manager::block::get_data() { - return buffer_; + return internal_->get_data(); } template @@ -183,25 +176,6 @@ namespace persistent_data { dirty_ = true; } - template - void - block_manager::block::change_validator(typename block_manager::validator::ptr v, - bool check) - { - check_not_unlocked(); - if (v.get() != validator_.get()) { - if (dirty_) - // It may have already happened, by calling - // this we ensure we're consistent. - validator_->prepare(*internal_->get_data(), internal_->get_index()); - - validator_ = v; - - if (check) - validator_->check(*internal_->get_data(), internal_->get_index()); - } - } - template void block_manager::block::check_not_unlocked() const @@ -268,10 +242,10 @@ namespace persistent_data { } template - buffer const & + void const * block_manager::read_ref::data() const { - return block_->get_buffer(); + return block_->get_data(); } //-------------------------------- @@ -285,10 +259,10 @@ namespace persistent_data { } template - buffer & + void * block_manager::write_ref::data() { - return read_ref::block_->get_buffer(); + return read_ref::block_->get_data(); } //---------------------------------------------------------------- @@ -299,14 +273,14 @@ namespace persistent_data { unsigned max_concurrent_blocks, mode m) : fd_(open_block_file(path, nr_blocks * BlockSize, m == READ_WRITE)), - bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 4) + bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 256) { } template typename block_manager::read_ref block_manager::read_lock(block_address location, - typename block_manager::validator::ptr v) const + typename bcache::validator::ptr v) const { typename block::ptr b(new block(bc_, location, BT_NORMAL, v, false)); return read_ref(*this, b); @@ -315,7 +289,7 @@ namespace persistent_data { template typename block_manager::write_ref block_manager::write_lock(block_address location, - typename block_manager::validator::ptr v) + typename bcache::validator::ptr v) { typename block::ptr b(new block(bc_, location, BT_NORMAL, v, false)); return write_ref(*this, b); @@ -324,7 +298,7 @@ namespace persistent_data { template typename block_manager::write_ref block_manager::write_lock_zero(block_address location, - typename block_manager::validator::ptr v) + typename bcache::validator::ptr v) { typename block::ptr b(new block(bc_, location, BT_NORMAL, v, true)); return write_ref(*this, b); @@ -333,7 +307,7 @@ namespace persistent_data { template typename block_manager::write_ref block_manager::superblock(block_address location, - typename block_manager::validator::ptr v) + typename bcache::validator::ptr v) { typename block::ptr b(new block(bc_, location, BT_SUPERBLOCK, v, false)); return write_ref(*this, b); @@ -342,7 +316,7 @@ namespace persistent_data { template typename block_manager::write_ref block_manager::superblock_zero(block_address location, - typename block_manager::validator::ptr v) + typename bcache::validator::ptr v) { typename block::ptr b(new block(bc_, location, BT_SUPERBLOCK, v, true)); return write_ref(*this, b); diff --git a/persistent-data/data-structures/array.h b/persistent-data/data-structures/array.h index 1d8fce6..cf8ad4e 100644 --- a/persistent-data/data-structures/array.h +++ b/persistent-data/data-structures/array.h @@ -31,9 +31,9 @@ namespace persistent_data { namespace array_detail { uint32_t const ARRAY_CSUM_XOR = 595846735; - struct array_block_validator : public block_manager<>::validator { - virtual void check(buffer<> const &b, block_address location) const { - array_block_disk const *data = reinterpret_cast(b.raw()); + struct array_block_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + array_block_disk const *data = reinterpret_cast(raw); crc32c sum(ARRAY_CSUM_XOR); sum.append(&data->max_entries, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(data->csum)) @@ -43,8 +43,8 @@ namespace persistent_data { throw checksum_error("bad block nr in array block"); } - virtual void prepare(buffer<> &b, block_address location) const { - array_block_disk *data = reinterpret_cast(b.raw()); + virtual void prepare(void *raw, block_address location) const { + array_block_disk *data = reinterpret_cast(raw); data->blocknr = to_disk(location); crc32c sum(ARRAY_CSUM_XOR); @@ -432,7 +432,7 @@ namespace persistent_data { block_ref_counter block_rc_; btree<1, block_traits> block_tree_; typename ValueTraits::ref_counter rc_; - block_manager<>::validator::ptr validator_; + bcache::validator::ptr validator_; }; } diff --git a/persistent-data/data-structures/array_block.h b/persistent-data/data-structures/array_block.h index 638e164..496c7a7 100644 --- a/persistent-data/data-structures/array_block.h +++ b/persistent-data/data-structures/array_block.h @@ -163,11 +163,11 @@ namespace persistent_data { } array_block_disk *get_header() { - return reinterpret_cast(ref_.data().raw()); + return reinterpret_cast(ref_.data()); } array_block_disk const *get_header() const { - return reinterpret_cast(ref_.data().raw()); + return reinterpret_cast(ref_.data()); } disk_type &element_at(unsigned int index) { diff --git a/persistent-data/data-structures/btree.h b/persistent-data/data-structures/btree.h index e3b6175..9c9c1a2 100644 --- a/persistent-data/data-structures/btree.h +++ b/persistent-data/data-structures/btree.h @@ -184,7 +184,7 @@ namespace persistent_data { return node_ref( b.get_location(), reinterpret_cast( - const_cast(b.data().raw()))); + const_cast(b.data()))); } template @@ -193,14 +193,13 @@ namespace persistent_data { { return node_ref( b.get_location(), - reinterpret_cast( - const_cast(b.data().raw()))); + reinterpret_cast(b.data())); } class ro_spine : private boost::noncopyable { public: ro_spine(transaction_manager::ptr tm, - block_manager<>::validator::ptr v) + bcache::validator::ptr v) : tm_(tm), validator_(v) { } @@ -214,7 +213,7 @@ namespace persistent_data { private: transaction_manager::ptr tm_; - block_manager<>::validator::ptr validator_; + bcache::validator::ptr validator_; std::list::read_ref> spine_; }; @@ -225,7 +224,7 @@ namespace persistent_data { typedef boost::optional maybe_block; shadow_spine(transaction_manager::ptr tm, - block_manager<>::validator::ptr v) + bcache::validator::ptr v) : tm_(tm), validator_(v) { @@ -278,7 +277,7 @@ namespace persistent_data { private: transaction_manager::ptr tm_; - block_manager<>::validator::ptr validator_; + bcache::validator::ptr validator_; std::list::write_ref> spine_; maybe_block root_; }; @@ -440,7 +439,7 @@ namespace persistent_data { block_address root_; block_ref_counter internal_rc_; typename ValueTraits::ref_counter rc_; - typename block_manager<>::validator::ptr validator_; + typename bcache::validator::ptr validator_; }; }; diff --git a/persistent-data/data-structures/btree.tcc b/persistent-data/data-structures/btree.tcc index a220334..593e29b 100644 --- a/persistent-data/data-structures/btree.tcc +++ b/persistent-data/data-structures/btree.tcc @@ -32,9 +32,9 @@ namespace { using namespace btree_detail; using namespace std; - struct btree_node_validator : public block_manager<>::validator { - virtual void check(buffer<> const &b, block_address location) const { - disk_node const *data = reinterpret_cast(b.raw()); + struct btree_node_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + disk_node const *data = reinterpret_cast(raw); node_header const *n = &data->header; crc32c sum(BTREE_CSUM_XOR); sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); @@ -45,8 +45,8 @@ namespace { throw checksum_error("bad block nr in btree node"); } - virtual void prepare(buffer<> &b, block_address location) const { - disk_node *data = reinterpret_cast(b.raw()); + virtual void prepare(void *raw, block_address location) const { + disk_node *data = reinterpret_cast(raw); node_header *n = &data->header; n->blocknr = to_disk(location); diff --git a/persistent-data/space-maps/disk.cc b/persistent-data/space-maps/disk.cc index f75f0a9..b83f5c9 100644 --- a/persistent-data/space-maps/disk.cc +++ b/persistent-data/space-maps/disk.cc @@ -37,9 +37,9 @@ using namespace sm_disk_detail; namespace { uint64_t const BITMAP_CSUM_XOR = 240779; - struct bitmap_block_validator : public block_manager<>::validator { - virtual void check(buffer<> const &b, block_address location) const { - bitmap_header const *data = reinterpret_cast(b.raw()); + struct bitmap_block_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + bitmap_header const *data = reinterpret_cast(raw); crc32c sum(BITMAP_CSUM_XOR); sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(data->csum)) @@ -49,8 +49,8 @@ namespace { throw checksum_error("bad block nr in space map bitmap"); } - virtual void prepare(buffer<> &b, block_address location) const { - bitmap_header *data = reinterpret_cast(b.raw()); + virtual void prepare(void *raw, block_address location) const { + bitmap_header *data = reinterpret_cast(raw); data->blocknr = to_disk(location); crc32c sum(BITMAP_CSUM_XOR); @@ -64,9 +64,9 @@ namespace { uint64_t const INDEX_CSUM_XOR = 160478; // FIXME: factor out the common code in these validators - struct index_block_validator : public block_manager<>::validator { - virtual void check(buffer<> const &b, block_address location) const { - metadata_index const *mi = reinterpret_cast(b.raw()); + struct index_block_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + metadata_index const *mi = reinterpret_cast(raw); std::cerr << "check mi = " << mi << "\n"; crc32c sum(INDEX_CSUM_XOR); sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t)); @@ -77,8 +77,8 @@ namespace { throw checksum_error("bad block nr in metadata index block"); } - virtual void prepare(buffer<> &b, block_address location) const { - metadata_index *mi = reinterpret_cast(b.raw()); + virtual void prepare(void *raw, block_address location) const { + metadata_index *mi = reinterpret_cast(raw); std::cerr << "prepare mi = " << mi << "\n"; mi->blocknr_ = to_disk(location); @@ -88,9 +88,9 @@ namespace { } }; - block_manager<>::validator::ptr + bcache::validator::ptr index_validator() { - return block_manager<>::validator::ptr(new index_block_validator()); + return bcache::validator::ptr(new index_block_validator()); } //-------------------------------- @@ -102,7 +102,7 @@ namespace { bitmap(transaction_manager::ptr tm, index_entry const &ie, - block_manager<>::validator::ptr v) + bcache::validator::ptr v) : tm_(tm), validator_(v), ie_(ie) { @@ -174,17 +174,17 @@ namespace { private: void *bitmap_data(transaction_manager::write_ref &wr) { - bitmap_header *h = reinterpret_cast(&wr.data()[0]); + bitmap_header *h = reinterpret_cast(wr.data()); return h + 1; } void const *bitmap_data(transaction_manager::read_ref &rr) const { - bitmap_header const *h = reinterpret_cast(&rr.data()[0]); + bitmap_header const *h = reinterpret_cast(rr.data()); return h + 1; } transaction_manager::ptr tm_; - block_manager<>::validator::ptr validator_; + bcache::validator::ptr validator_; index_entry ie_; }; @@ -504,7 +504,7 @@ namespace { } transaction_manager::ptr tm_; - block_manager<>::validator::ptr bitmap_validator_; + bcache::validator::ptr bitmap_validator_; index_store::ptr indexes_; block_address nr_blocks_; block_address nr_allocated_; @@ -632,7 +632,7 @@ namespace { tm_->shadow(bitmap_root_, index_validator()); bitmap_root_ = p.first.get_location(); - metadata_index *mdi = reinterpret_cast(p.first.data().raw()); + metadata_index *mdi = reinterpret_cast(p.first.data()); for (unsigned i = 0; i < entries_.size(); i++) index_entry_traits::pack(entries_[i], mdi->index[i]); @@ -665,7 +665,7 @@ namespace { block_manager<>::read_ref rr = tm_->read_lock(bitmap_root_, index_validator()); - metadata_index const *mdi = reinterpret_cast(&rr.data()); + metadata_index const *mdi = reinterpret_cast(rr.data()); for (unsigned i = 0; i < entries_.size(); i++) index_entry_traits::unpack(*(mdi->index + i), entries_[i]); } diff --git a/persistent-data/transaction_manager.cc b/persistent-data/transaction_manager.cc index d333159..279b188 100644 --- a/persistent-data/transaction_manager.cc +++ b/persistent-data/transaction_manager.cc @@ -72,7 +72,7 @@ transaction_manager::shadow(block_address orig, validator v) throw runtime_error("transaction_manager::shadow() couldn't allocate new block"); write_ref dest = bm_->write_lock_zero(*mb, v); - ::memcpy(dest.data().raw(), src.data().raw(), MD_BLOCK_SIZE); // FIXME: use buffer copy method + ::memcpy(dest.data(), src.data(), MD_BLOCK_SIZE); sm_->dec(orig); add_shadow(dest.get_location()); diff --git a/persistent-data/transaction_manager.h b/persistent-data/transaction_manager.h index 769caef..196ea4a 100644 --- a/persistent-data/transaction_manager.h +++ b/persistent-data/transaction_manager.h @@ -33,7 +33,7 @@ namespace persistent_data { typedef boost::shared_ptr ptr; typedef block_manager<>::read_ref read_ref; typedef block_manager<>::write_ref write_ref; - typedef block_manager<>::validator::ptr validator; + typedef bcache::validator::ptr validator; // If the space map is persistent, then the caller should // hold onto a reference and remember to call sm_->commit() diff --git a/thin-provisioning/metadata.cc b/thin-provisioning/metadata.cc index 6136722..8725b17 100644 --- a/thin-provisioning/metadata.cc +++ b/thin-provisioning/metadata.cc @@ -197,7 +197,7 @@ metadata::commit() metadata_sm_->copy_root(&sb_.metadata_space_map_root_, sizeof(sb_.metadata_space_map_root_)); write_ref superblock = tm_->get_bm()->superblock_zero(SUPERBLOCK_LOCATION, superblock_validator()); - superblock_disk *disk = reinterpret_cast(superblock.data().raw()); + superblock_disk *disk = reinterpret_cast(superblock.data()); superblock_traits::pack(sb_, *disk); } diff --git a/thin-provisioning/superblock.cc b/thin-provisioning/superblock.cc index e808c6c..1f54b64 100644 --- a/thin-provisioning/superblock.cc +++ b/thin-provisioning/superblock.cc @@ -85,17 +85,17 @@ namespace { unsigned const SECTOR_TO_BLOCK_SHIFT = 3; uint32_t const SUPERBLOCK_CSUM_SEED = 160774; - struct sb_validator : public block_manager<>::validator { - virtual void check(buffer<> const &b, block_address location) const { - superblock_disk const *sbd = reinterpret_cast(b.raw()); + struct sb_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + superblock_disk const *sbd = reinterpret_cast(raw); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(sbd->csum_)) throw checksum_error("bad checksum in superblock"); } - virtual void prepare(buffer<> &b, block_address location) const { - superblock_disk *sbd = reinterpret_cast(b.raw()); + virtual void prepare(void *raw, block_address location) const { + superblock_disk *sbd = reinterpret_cast(raw); crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t)); sbd->csum_ = to_disk(sum.get_sum()); @@ -103,10 +103,10 @@ namespace { }; } -block_manager<>::validator::ptr +bcache::validator::ptr thin_provisioning::superblock_validator() { - return block_manager<>::validator::ptr(new sb_validator); + return bcache::validator::ptr(new sb_validator); } //---------------------------------------------------------------- @@ -151,7 +151,7 @@ namespace thin_provisioning { superblock sb; block_manager<>::read_ref r = bm->read_lock(location, superblock_validator()); - superblock_disk const *sbd = reinterpret_cast(&r.data()); + superblock_disk const *sbd = reinterpret_cast(r.data()); superblock_traits::unpack(*sbd, sb); return sb; } @@ -164,7 +164,7 @@ namespace thin_provisioning { void write_superblock(block_manager<>::ptr bm, superblock_detail::superblock const &sb) { block_manager<>::write_ref w = bm->write_lock(SUPERBLOCK_LOCATION, superblock_validator()); - superblock_disk *disk = reinterpret_cast(w.data().raw()); + superblock_disk *disk = reinterpret_cast(w.data()); superblock_traits::pack(sb, *disk); } diff --git a/thin-provisioning/superblock.h b/thin-provisioning/superblock.h index 354e83c..f527a15 100644 --- a/thin-provisioning/superblock.h +++ b/thin-provisioning/superblock.h @@ -124,7 +124,7 @@ namespace thin_provisioning { }; } - persistent_data::block_manager<>::validator::ptr superblock_validator(); + bcache::validator::ptr superblock_validator(); // FIXME: should we put init_superblock in here too? From 11469a2fdafab4ed39087bc996e77700713d5c18 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 25 Jul 2014 16:14:24 +0100 Subject: [PATCH 063/165] Beginning to work --- block-cache/block_cache.cc | 7 +- block-cache/block_cache.h | 1 - block-cache/buffer.h | 94 -------------------- persistent-data/block.h | 69 +++++---------- persistent-data/block.tcc | 176 ++++++++++++------------------------- 5 files changed, 79 insertions(+), 268 deletions(-) delete mode 100644 block-cache/buffer.h diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 9a2944c..65ec90d 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -10,6 +10,7 @@ #include #include +#include //---------------------------------------------------------------- @@ -176,7 +177,6 @@ namespace bcache { int block_cache::issue_read(block &b) { - std::cerr << "issuing read - that's a shame: " << b.index_ << "\n"; assert(!test_flags(b, IO_PENDING)); return issue_low_level(b, IO_CMD_PREAD, "read"); } @@ -359,7 +359,7 @@ namespace bcache { if (!b) { if (list_empty(&clean_)) { if (list_empty(&io_pending_)) - writeback(9000); + writeback(16); wait_io(); } @@ -566,7 +566,6 @@ namespace bcache { { block *b, *tmp; - std::cerr << "in flush\n"; list_for_each_entry_safe (b, tmp, &dirty_, list_) { if (b->ref_count_ || test_flags(*b, IO_PENDING)) // The superblock may well be still locked. @@ -575,9 +574,7 @@ namespace bcache { issue_write(*b); } - std::cerr << "issued all writes\n"; wait_all(); - std::cerr << "wait all returned\n"; return list_empty(&errored_) ? 0 : -EIO; } diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 9955af3..9284500 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -1,7 +1,6 @@ #ifndef BLOCK_CACHE_H #define BLOCK_CACHE_H -#include "block-cache/buffer.h" #include "block-cache/list.h" #include diff --git a/block-cache/buffer.h b/block-cache/buffer.h deleted file mode 100644 index cea152a..0000000 --- a/block-cache/buffer.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2013 Red Hat, Inc. All rights reserved. -// -// This file is part of the thin-provisioning-tools source. -// -// thin-provisioning-tools is free software: you can redistribute it -// and/or modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation, either version 3 of -// the License, or (at your option) any later version. -// -// thin-provisioning-tools is distributed in the hope that it will be -// useful, but WITHOUT ANY WARRANTY; without even the implied warranty -// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with thin-provisioning-tools. If not, see -// . - -#ifndef BUFFER_H -#define BUFFER_H - -#include -#include - -#include -#include -#include - -#include - -//---------------------------------------------------------------- - -namespace bcache { - uint32_t const DEFAULT_BUFFER_SIZE = 4096; - - template - class buffer { - public: - buffer(void *data, bool writeable = true) - : data_(static_cast(data)), - writeable_(writeable) { - } - - typedef boost::shared_ptr ptr; - typedef boost::shared_ptr const_ptr; - - size_t size() const { - return Size; - } - - unsigned char &operator[](unsigned index) { - check_writeable(); - check_index(index); - - return data_[index]; - } - - unsigned char const &operator[](unsigned index) const { - check_index(index); - - return data_[index]; - } - - unsigned char *raw() { - return data_; - } - - unsigned char const *raw() const { - return data_; - } - - void set_data(void *data) { - data_ = static_cast(data); - } - - private: - static void check_index(unsigned index) { - if (index >= Size) - throw std::range_error("buffer index out of bounds"); - } - - void check_writeable() const { - if (!writeable_) - throw std::runtime_error("buffer isn't writeable"); - } - - unsigned char *data_; - bool writeable_; - }; -} - -//---------------------------------------------------------------- - -#endif diff --git a/persistent-data/block.h b/persistent-data/block.h index 7ade102..9d571c3 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -20,7 +20,6 @@ #define BLOCK_H #include "block-cache/block_cache.h" -#include "block-cache/buffer.h" #include #include @@ -61,61 +60,28 @@ namespace persistent_data { BT_NORMAL }; - // FIXME: eventually this will disappear to be replaced with block_cache::block - struct block : private boost::noncopyable { - typedef boost::shared_ptr ptr; - - block(block_cache &bc, - block_address location, - block_type bt, - typename bcache::validator::ptr v, - bool zero = false); - ~block(); - - void check_read_lockable() const { - // FIXME: finish - } - - void check_write_lockable() const { - // FIXME: finish - } - - block_type get_type() const; - uint64_t get_location() const; - - void const *get_data() const; - void *get_data(); - - void mark_dirty(); - void unlock(); - - private: - void check_not_unlocked() const; - - block_cache &bc_; - block_cache::block *internal_; - block_type bt_; - bool dirty_; - bool unlocked_; - }; + typedef void (*put_behaviour_fn)(block_cache &, block_cache::block &); class read_ref { public: static uint32_t const BLOCK_SIZE = BlockSize; - read_ref(block_manager const &bm, - typename block::ptr b); + read_ref(block_cache &bc, + block_cache::block &b, + put_behaviour_fn fn); + read_ref(read_ref const &rhs); virtual ~read_ref(); read_ref const &operator =(read_ref const &rhs); block_address get_location() const; - void const * data() const; + void const *data() const; protected: - block_manager const *bm_; - typename block::ptr block_; + block_cache &bc_; + block_cache::block &b_; + put_behaviour_fn fn_; unsigned *holders_; }; @@ -123,13 +89,24 @@ namespace persistent_data { // locked. class write_ref : public read_ref { public: - write_ref(block_manager const &bm, - typename block::ptr b); + write_ref(block_cache &bc, + block_cache::block &b, + put_behaviour_fn fn); using read_ref::data; void *data(); }; + class super_ref : public write_ref { + public: + super_ref(block_cache &bc, + block_cache::block &b, + put_behaviour_fn fn); + + using read_ref::data; + using write_ref::data; + }; + // Locking methods read_ref read_lock(block_address location, @@ -174,11 +151,9 @@ namespace persistent_data { private: void check(block_address b) const; - void write_block(typename block::ptr b) const; int fd_; - // FIXME: the mutable is a fudge to allow flush() to be const, which I'm not sure is necc. mutable block_cache bc_; }; diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index ccab185..6b75ab8 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -105,102 +105,39 @@ namespace { }; namespace persistent_data { + + inline void read_put(block_cache &bc, block_cache::block &b) { + bc.put(b, 0); + } + + inline void write_put(block_cache &bc, block_cache::block &b) { + bc.put(b, block_cache::PF_DIRTY); + } + + inline void super_put(block_cache &bc, block_cache::block &b) { + bc.flush(); + bc.put(b, block_cache::PF_DIRTY); + bc.flush(); + } + template - block_manager::block::block(block_cache &bc, - block_address location, - block_type bt, - typename validator::ptr v, - bool zero) + block_manager::read_ref::read_ref(block_cache &bc, + block_cache::block &b, + put_behaviour_fn fn) : bc_(bc), - bt_(bt), - dirty_(false), - unlocked_(false) - { - if (zero) { - internal_ = &bc.get(location, block_cache::GF_ZERO | block_cache::GF_CAN_BLOCK, v); - dirty_ = true; - } else { - internal_ = &bc.get(location, block_cache::GF_CAN_BLOCK, v); - } - } - - template - block_manager::block::~block() - { - if (!unlocked_) - unlock(); - } - - template - void - block_manager::block::unlock() - { - bc_.put(*internal_, dirty_ ? block_cache::PF_DIRTY : 0); - unlocked_ = true; - } - - template - typename block_manager::block_type - block_manager::block::get_type() const - { - return bt_; - } - - template - uint64_t - block_manager::block::get_location() const - { - check_not_unlocked(); - return internal_->get_index(); - } - - template - void const * - block_manager::block::get_data() const - { - return internal_->get_data(); - } - - template - void * - block_manager::block::get_data() - { - return internal_->get_data(); - } - - template - void - block_manager::block::mark_dirty() - { - check_not_unlocked(); - dirty_ = true; - } - - template - void - block_manager::block::check_not_unlocked() const - { - if (unlocked_) - throw std::runtime_error("block prematurely unlocked"); - } - - //---------------------------------------------------------------- - - template - block_manager::read_ref::read_ref(block_manager const &bm, - typename block::ptr b) - : bm_(&bm), - block_(b), - holders_(new unsigned) + b_(b), + fn_(fn), + holders_(new unsigned) { *holders_ = 1; } template block_manager::read_ref::read_ref(read_ref const &rhs) - : bm_(rhs.bm_), - block_(rhs.block_), - holders_(rhs.holders_) + : bc_(rhs.bc_), + b_(rhs.b_), + fn_(rhs.fn_), + holders_(rhs.holders_) { (*holders_)++; } @@ -209,13 +146,7 @@ namespace persistent_data { block_manager::read_ref::~read_ref() { if (!--(*holders_)) { - if (block_->get_type() == BT_SUPERBLOCK) { - bm_->flush(); - block_->unlock(); - bm_->flush(); - } else - block_->unlock(); - + fn_(bc_, b_); delete holders_; } } @@ -225,8 +156,9 @@ namespace persistent_data { block_manager::read_ref::operator =(read_ref const &rhs) { if (this != &rhs) { - block_ = rhs.block_; - bm_ = rhs.bm_; + bc_ = rhs.bc_; + b_ = rhs.b_; + fn_ = rhs.fn_; holders_ = rhs.holders_; (*holders_)++; } @@ -238,31 +170,40 @@ namespace persistent_data { block_address block_manager::read_ref::get_location() const { - return block_->get_location(); + return b_.get_index(); } template void const * block_manager::read_ref::data() const { - return block_->get_data(); + return b_.get_data(); } //-------------------------------- template - block_manager::write_ref::write_ref(block_manager const &bm, - typename block::ptr b) - : read_ref(bm, b) + block_manager::write_ref::write_ref(block_cache &bc, + block_cache::block &b, + put_behaviour_fn fn) + : read_ref(bc, b, fn) { - b->mark_dirty(); } template void * block_manager::write_ref::data() { - return read_ref::block_->get_data(); + return read_ref::b_.get_data(); + } + + //-------------------------------- + + template + block_manager::super_ref::super_ref(block_cache &bc, + block_cache::block &b, + put_behaviour_fn fn) + : write_ref(bc, b, fn) { } //---------------------------------------------------------------- @@ -282,8 +223,8 @@ namespace persistent_data { block_manager::read_lock(block_address location, typename bcache::validator::ptr v) const { - typename block::ptr b(new block(bc_, location, BT_NORMAL, v, false)); - return read_ref(*this, b); + block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK, v); + return read_ref(bc_, b, read_put); } template @@ -291,8 +232,8 @@ namespace persistent_data { block_manager::write_lock(block_address location, typename bcache::validator::ptr v) { - typename block::ptr b(new block(bc_, location, BT_NORMAL, v, false)); - return write_ref(*this, b); + block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK, v); + return write_ref(bc_, b, write_put); } template @@ -300,8 +241,8 @@ namespace persistent_data { block_manager::write_lock_zero(block_address location, typename bcache::validator::ptr v) { - typename block::ptr b(new block(bc_, location, BT_NORMAL, v, true)); - return write_ref(*this, b); + block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK | block_cache::GF_ZERO, v); + return write_ref(bc_, b, write_put); } template @@ -309,8 +250,8 @@ namespace persistent_data { block_manager::superblock(block_address location, typename bcache::validator::ptr v) { - typename block::ptr b(new block(bc_, location, BT_SUPERBLOCK, v, false)); - return write_ref(*this, b); + block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK, v); + return super_ref(bc_, b, super_put); } template @@ -318,8 +259,8 @@ namespace persistent_data { block_manager::superblock_zero(block_address location, typename bcache::validator::ptr v) { - typename block::ptr b(new block(bc_, location, BT_SUPERBLOCK, v, true)); - return write_ref(*this, b); + block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK | block_cache::GF_ZERO, v); + return super_ref(bc_, b, super_put); } template @@ -329,13 +270,6 @@ namespace persistent_data { return bc_.get_nr_blocks(); } - template - void - block_manager::write_block(typename block::ptr b) const - { - b->flush(); - } - template void block_manager::flush() const From ecb6bee2b24a0c0ae3cb7857b623f4a0414a1518 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 25 Jul 2014 16:32:59 +0100 Subject: [PATCH 064/165] prefetch btree nodes when walking a tree --- persistent-data/block.h | 6 +----- persistent-data/block.tcc | 7 +++++++ persistent-data/data-structures/btree.tcc | 6 +++++- persistent-data/transaction_manager.h | 4 ++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/persistent-data/block.h b/persistent-data/block.h index 9d571c3..a3e9b0e 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -55,11 +55,6 @@ namespace persistent_data { unsigned max_concurrent_locks, mode m); - enum block_type { - BT_SUPERBLOCK, - BT_NORMAL - }; - typedef void (*put_behaviour_fn)(block_cache &, block_cache::block &); class read_ref { @@ -142,6 +137,7 @@ namespace persistent_data { block_address get_nr_blocks() const; + void prefetch(block_address b) const; void flush() const; diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 6b75ab8..61fea7c 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -270,6 +270,13 @@ namespace persistent_data { return bc_.get_nr_blocks(); } + template + void + block_manager::prefetch(block_address b) const + { + bc_.prefetch(b); + } + template void block_manager::flush() const diff --git a/persistent-data/data-structures/btree.tcc b/persistent-data/data-structures/btree.tcc index 593e29b..9034aca 100644 --- a/persistent-data/data-structures/btree.tcc +++ b/persistent-data/data-structures/btree.tcc @@ -827,7 +827,10 @@ namespace persistent_data { // FIXME: use a switch statement if (o.get_type() == INTERNAL) { - if (v.visit_internal(loc, o)) + if (v.visit_internal(loc, o)) { + for (unsigned i = 0; i < o.get_nr_entries(); i++) + tm_->prefetch(o.value_at(i)); + for (unsigned i = 0; i < o.get_nr_entries(); i++) { node_location loc2(loc); @@ -836,6 +839,7 @@ namespace persistent_data { walk_tree(v, loc2, o.value_at(i)); } + } } else if (loc.path.size() < Levels - 1) { if (v.visit_internal_leaf(loc, o)) diff --git a/persistent-data/transaction_manager.h b/persistent-data/transaction_manager.h index 196ea4a..10ac8b7 100644 --- a/persistent-data/transaction_manager.h +++ b/persistent-data/transaction_manager.h @@ -66,6 +66,10 @@ namespace persistent_data { return bm_; } + void prefetch(block_address b) { + bm_->prefetch(b); + } + private: void add_shadow(block_address b); void remove_shadow(block_address b); From ab6d31f136a1e2e31b3c597000c2d4cf72343e2c Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 25 Jul 2014 17:53:37 +0100 Subject: [PATCH 065/165] fluff --- persistent-data/block.h | 1 - persistent-data/block.tcc | 1 - 2 files changed, 2 deletions(-) diff --git a/persistent-data/block.h b/persistent-data/block.h index a3e9b0e..e3a2407 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -149,7 +149,6 @@ namespace persistent_data { void check(block_address b) const; int fd_; - mutable block_cache bc_; }; diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 61fea7c..926abe4 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -39,7 +39,6 @@ namespace { int const DEFAULT_MODE = 0666; unsigned const SECTOR_SHIFT = 9; - // FIXME: these will slow it down until we start doing async io. int const OPEN_FLAGS = O_DIRECT; // FIXME: introduce a new exception for this, or at least lift this From 840be1b6c9660b0e2cdfac57addd57c09228d401 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 28 Jul 2014 14:13:28 +0100 Subject: [PATCH 066/165] wip --- block-cache/block_cache.cc | 150 +++++++++++++++++++++---------------- block-cache/block_cache.h | 114 +++++++++++----------------- persistent-data/block.h | 17 +---- persistent-data/block.tcc | 77 ++++++------------- 4 files changed, 155 insertions(+), 203 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 65ec90d..7aae48b 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -11,6 +11,7 @@ #include #include +#include //---------------------------------------------------------------- @@ -126,14 +127,14 @@ namespace bcache { block_cache::complete_io(block &b, int result) { b.error_ = result; - clear_flags(b, IO_PENDING); + b.clear_flags(BF_IO_PENDING); nr_io_pending_--; if (b.error_) list_move_tail(&b.list_, &errored_); else { - if (test_flags(b, DIRTY)) { - clear_flags(b, DIRTY); + if (b.test_flags(BF_DIRTY)) { + b.clear_flags(BF_DIRTY | BF_PREVIOUSLY_DIRTY); nr_dirty_--; } @@ -152,8 +153,8 @@ namespace bcache { iocb *control_blocks[1]; // FIXME: put this back in - assert(!test_flags(b, IO_PENDING)); - set_flags(b, IO_PENDING); + assert(!b.test_flags(BF_IO_PENDING)); + b.set_flags(BF_IO_PENDING); nr_io_pending_++; list_move_tail(&b.list_, &io_pending_); @@ -177,14 +178,14 @@ namespace bcache { int block_cache::issue_read(block &b) { - assert(!test_flags(b, IO_PENDING)); + assert(!b.test_flags(BF_IO_PENDING)); return issue_low_level(b, IO_CMD_PREAD, "read"); } int block_cache::issue_write(block &b) { - assert(!test_flags(b, IO_PENDING)); + assert(!b.test_flags(BF_IO_PENDING)); b.v_->prepare(b.data_, b.index_); return issue_low_level(b, IO_CMD_PWRITE, "write"); } @@ -213,7 +214,8 @@ namespace bcache { complete_io(*b, e.res); else - info("incomplete io, unexpected: %d\n", r); + info("incomplete io for block %llu, unexpected: %d\n", + b->index_, e.res); } } @@ -231,7 +233,7 @@ namespace bcache { if (b.error_) return &errored_; - return (b.flags_ & DIRTY) ? &dirty_ : &clean_; + return b.test_flags(BF_DIRTY) ? &dirty_ : &clean_; } void @@ -253,7 +255,7 @@ namespace bcache { void block_cache::wait_specific(block &b) { - while (test_flags(b, IO_PENDING)) + while (b.test_flags(BF_IO_PENDING)) wait_io(); } @@ -262,12 +264,16 @@ namespace bcache { { int r; block *b, *tmp; - unsigned actual = 0; + unsigned actual = 0, dirty_length = 0; list_for_each_entry_safe (b, tmp, &dirty_, list_) { + dirty_length++; + if (actual == count) break; + // The block may be on the dirty list from a prior + // acquisition. if (b->ref_count_) continue; @@ -276,7 +282,7 @@ namespace bcache { actual++; } - info("writeback: requested %u, actual %u\n", count, actual); + info("writeback: requested %u, actual %u, dirty length %u\n", count, actual, dirty_length); return actual; } @@ -377,7 +383,7 @@ namespace bcache { b->ref_count_ = 0; b->error_ = 0; - clear_flags(*b, IO_PENDING | DIRTY); + b->flags_ = 0; b->index_ = index; setup_control_block(*b); @@ -391,16 +397,6 @@ namespace bcache { /*---------------------------------------------------------------- * Block reference counting *--------------------------------------------------------------*/ - void - block_cache::mark_dirty(block &b) - { - if (!test_flags(b, DIRTY)) { - set_flags(b, DIRTY); - list_move_tail(&b.list_, &dirty_); - nr_dirty_++; - } - } - unsigned block_cache::calc_nr_cache_blocks(size_t mem, sector_t block_size) { @@ -451,8 +447,11 @@ namespace bcache { aio_context_ = 0; /* needed or io_setup will fail */ r = io_setup(nr_cache_blocks, &aio_context_); - if (r < 0) + if (r < 0) { + std::cerr << "r = " << r << "\n"; + perror("io_setup failed"); throw std::runtime_error("io_setup failed"); + } hash_init(nr_buckets); INIT_LIST_HEAD(&free_); @@ -485,7 +484,7 @@ namespace bcache { block_cache::zero_block(block &b) { memset(b.data_, 0, block_size_ << SECTOR_SHIFT); - mark_dirty(b); + b.mark_dirty(); } block_cache::block * @@ -495,7 +494,7 @@ namespace bcache { block *b = hash_lookup(index); if (b) { - if (test_flags(*b, IO_PENDING)) + if (b->test_flags(BF_IO_PENDING)) wait_specific(*b); if (flags & GF_ZERO) @@ -503,24 +502,22 @@ namespace bcache { else { if (b->v_.get() && b->v_.get() != v.get() && - test_flags(*b, DIRTY)) + b->test_flags(BF_DIRTY)) b->v_->prepare(b->data_, b->index_); } b->v_ = v; } else { - if (flags & GF_CAN_BLOCK) { - b = new_block(index); - if (b) { - b->v_ = v; + b = new_block(index); + if (b) { + b->v_ = v; - if (flags & GF_ZERO) - zero_block(*b); - else { - issue_read(*b); - wait_specific(*b); - v->check(b->data_, b->index_); - } + if (flags & GF_ZERO) + zero_block(*b); + else { + issue_read(*b); + wait_specific(*b); + v->check(b->data_, b->index_); } } } @@ -531,12 +528,23 @@ namespace bcache { block_cache::block & block_cache::get(block_address index, unsigned flags, validator::ptr v) { + check_index(index); + block *b = lookup_or_read_block(index, flags, v); if (b) { + if (b->ref_count_) + throw std::runtime_error("block already locked"); + hit(*b); b->ref_count_++; + if (flags & GF_BARRIER) + b->set_flags(BF_FLUSH); + + if (flags & GF_DIRTY) + b->set_flags(BF_DIRTY); + return *b; } @@ -544,20 +552,39 @@ namespace bcache { } void - block_cache::put(block_cache::block &b, unsigned flags) + block_cache::preemptive_writeback() { - if (b.ref_count_ == 0) - throw std::runtime_error("bad put"); + unsigned nr_available = nr_cache_blocks_ - (nr_dirty_ - nr_io_pending_); + if (nr_available < (WRITEBACK_LOW_THRESHOLD_PERCENT * nr_cache_blocks_ / 100)) + writeback((WRITEBACK_HIGH_THRESHOLD_PERCENT * nr_cache_blocks_ / 100) - nr_available); - b.ref_count_--; + } - if (flags & PF_DIRTY) { - mark_dirty(b); + void + block_cache::release(block_cache::block &b) + { + assert(!b.ref_count_); - // FIXME: factor out - unsigned nr_available = nr_cache_blocks_ - (nr_dirty_ - nr_io_pending_); - if (nr_available < (WRITEBACK_LOW_THRESHOLD_PERCENT * nr_cache_blocks_ / 100)) - writeback((WRITEBACK_HIGH_THRESHOLD_PERCENT * nr_cache_blocks_ / 100) - nr_available); +#if 0 + if (b.test_flags(BF_FLUSH)) + flush(); +#endif + + if (b.test_flags(BF_DIRTY)) { + if (!b.test_flags(BF_PREVIOUSLY_DIRTY)) { + list_move_tail(&b.list_, &dirty_); + nr_dirty_++; + b.set_flags(BF_PREVIOUSLY_DIRTY); + } + +#if 0 + if (b.test_flags(BF_FLUSH)) + flush(); + else +#endif + preemptive_writeback(); + + b.clear_flags(BF_FLUSH); } } @@ -567,7 +594,7 @@ namespace bcache { block *b, *tmp; list_for_each_entry_safe (b, tmp, &dirty_, list_) { - if (b->ref_count_ || test_flags(*b, IO_PENDING)) + if (b->ref_count_ || b->test_flags(BF_IO_PENDING)) // The superblock may well be still locked. continue; @@ -582,6 +609,8 @@ namespace bcache { void block_cache::prefetch(block_address index) { + check_index(index); + block *b = hash_lookup(index); if (!b) { @@ -591,24 +620,15 @@ namespace bcache { } } - //-------------------------------- - - unsigned - block_cache::test_flags(block &b, unsigned flags) - { - return b.flags_ & flags; - } - void - block_cache::clear_flags(block &b, unsigned flags) + block_cache::check_index(block_address index) const { - b.flags_ &= ~flags; - } - - void - block_cache::set_flags(block &b, unsigned flags) - { - b.flags_ |= flags; + if (index >= nr_data_blocks_) { + std::ostringstream out; + out << "block out of bounds (" + << index << " >= " << nr_data_blocks_ << ")\n"; + throw std::runtime_error(out.str()); + } } } diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 9284500..cb991ab 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -36,12 +37,13 @@ namespace bcache { //---------------------------------------------------------------- - // FIXME: throw exceptions rather than returning errors class block_cache : private boost::noncopyable { public: enum block_flags { - IO_PENDING = (1 << 0), - DIRTY = (1 << 1) + BF_IO_PENDING = (1 << 0), + BF_DIRTY = (1 << 1), + BF_FLUSH = (1 << 2), + BF_PREVIOUSLY_DIRTY = (1 << 3) }; class block : private boost::noncopyable { @@ -58,16 +60,49 @@ namespace bcache { return data_; } + void mark_dirty() { + flags_ |= BF_DIRTY; + } + + void mark_flush() { + flags_ |= BF_FLUSH; + } + + void set_flags(unsigned flags) { + flags_ |= flags; + } + + unsigned test_flags(unsigned flags) const { + return flags_ & flags; + } + + void clear_flags(unsigned flags) { + flags_ &= ~flags; + } + + void get() { + ref_count_++; + }; + + void put() { + if (!ref_count_) + throw std::runtime_error("bad put"); + + if (!--ref_count_) + bc_->release(*this); + } + private: friend class block_cache; + block_cache *bc_; + uint64_t index_; void *data_; list_head list_; list_head hash_list_; - block_cache *bc_; unsigned ref_count_; int error_; @@ -87,18 +122,12 @@ namespace bcache { enum get_flags { GF_ZERO = (1 << 0), - GF_CAN_BLOCK = (1 << 1) + GF_DIRTY = (1 << 1), + GF_BARRIER = (1 << 1) }; - // FIXME: what if !GF_CAN_BLOCK? block_cache::block &get(block_address index, unsigned flags, validator::ptr v); - enum put_flags { - PF_DIRTY = (1 << 0), - }; - - void put(block_cache::block &block, unsigned flags); - /* * Flush can fail if an earlier write failed. You do not know which block * failed. Make sure you build your recovery with this in mind. @@ -131,9 +160,10 @@ namespace bcache { unsigned calc_nr_buckets(unsigned nr_blocks); void zero_block(block &b); block *lookup_or_read_block(block_address index, unsigned flags, validator::ptr v); - unsigned test_flags(block &b, unsigned flags); - void clear_flags(block &b, unsigned flags); - void set_flags(block &b, unsigned flags); + + void preemptive_writeback(); + void release(block_cache::block &block); + void check_index(block_address index) const; //-------------------------------- @@ -169,60 +199,6 @@ namespace bcache { unsigned mask_; std::vector buckets_; }; - -#if 0 - class auto_lock { - public: - auto_lock(block_cache &bc, block_address index, bool zero, validator::ptr v, unsigned put_flags) - : bc_(bc), - b_(bc.get(index, (zero ? block_cache::GF_ZERO : 0) | block_cache::GF_CAN_BLOCK, v)), - put_flags_(put_flags), - holders_(new unsigned) { - *holders_ = 1; - } - - virtual ~auto_lock() { - bc_.put(b_, put_flags_); - } - - auto_lock operator =(auto_lock const &rhs) { - if (this != &rhs) { - bc_ = rhs.bc_; - - - - void const *data() const { - return b_.get_data(); - } - - private: - block_cache &bc_; - block_cache::block &b_; - unsigned put_flags_; - unsigned *holders_; - }; - - class auto_read_lock : public auto_lock { - public: - auto_read_lock(block_cache &bc, block_address index, bool zero, validator::ptr v) - : auto_lock(bc, index, zero, v, 0) { - } - - using auto_lock::data(); - }; - - class auto_write_lock : public auto_lock { - public: - auto_write_lock(block_cache &bc, block_address index, bool zero, validator::ptr v) - : auto_lock(bc, index, zero, v, block_cache::DIRTY) { - } - - using auto_lock::data(); - void *data() { - return b_.get_data(); - } - }; -#endif } //---------------------------------------------------------------- diff --git a/persistent-data/block.h b/persistent-data/block.h index e3a2407..ab15687 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -55,15 +55,11 @@ namespace persistent_data { unsigned max_concurrent_locks, mode m); - typedef void (*put_behaviour_fn)(block_cache &, block_cache::block &); - class read_ref { public: static uint32_t const BLOCK_SIZE = BlockSize; - read_ref(block_cache &bc, - block_cache::block &b, - put_behaviour_fn fn); + read_ref(block_cache::block &b); read_ref(read_ref const &rhs); virtual ~read_ref(); @@ -74,19 +70,14 @@ namespace persistent_data { void const *data() const; protected: - block_cache &bc_; block_cache::block &b_; - put_behaviour_fn fn_; - unsigned *holders_; }; // Inherited from read_ref, since you can read a block that's write // locked. class write_ref : public read_ref { public: - write_ref(block_cache &bc, - block_cache::block &b, - put_behaviour_fn fn); + write_ref(block_cache::block &b); using read_ref::data; void *data(); @@ -94,9 +85,7 @@ namespace persistent_data { class super_ref : public write_ref { public: - super_ref(block_cache &bc, - block_cache::block &b, - put_behaviour_fn fn); + super_ref(block_cache::block &b); using read_ref::data; using write_ref::data; diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 926abe4..897550c 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -104,50 +104,23 @@ namespace { }; namespace persistent_data { - - inline void read_put(block_cache &bc, block_cache::block &b) { - bc.put(b, 0); - } - - inline void write_put(block_cache &bc, block_cache::block &b) { - bc.put(b, block_cache::PF_DIRTY); - } - - inline void super_put(block_cache &bc, block_cache::block &b) { - bc.flush(); - bc.put(b, block_cache::PF_DIRTY); - bc.flush(); - } - template - block_manager::read_ref::read_ref(block_cache &bc, - block_cache::block &b, - put_behaviour_fn fn) - : bc_(bc), - b_(b), - fn_(fn), - holders_(new unsigned) + block_manager::read_ref::read_ref(block_cache::block &b) + : b_(b) { - *holders_ = 1; } template block_manager::read_ref::read_ref(read_ref const &rhs) - : bc_(rhs.bc_), - b_(rhs.b_), - fn_(rhs.fn_), - holders_(rhs.holders_) + : b_(rhs.b_) { - (*holders_)++; + b_.get(); } template block_manager::read_ref::~read_ref() { - if (!--(*holders_)) { - fn_(bc_, b_); - delete holders_; - } + b_.put(); } template @@ -155,11 +128,8 @@ namespace persistent_data { block_manager::read_ref::operator =(read_ref const &rhs) { if (this != &rhs) { - bc_ = rhs.bc_; b_ = rhs.b_; - fn_ = rhs.fn_; - holders_ = rhs.holders_; - (*holders_)++; + b_.get(); } return *this; @@ -182,10 +152,8 @@ namespace persistent_data { //-------------------------------- template - block_manager::write_ref::write_ref(block_cache &bc, - block_cache::block &b, - put_behaviour_fn fn) - : read_ref(bc, b, fn) + block_manager::write_ref::write_ref(block_cache::block &b) + : read_ref(b) { } @@ -199,10 +167,9 @@ namespace persistent_data { //-------------------------------- template - block_manager::super_ref::super_ref(block_cache &bc, - block_cache::block &b, - put_behaviour_fn fn) - : write_ref(bc, b, fn) { + block_manager::super_ref::super_ref(block_cache::block &b) + : write_ref(b) + { } //---------------------------------------------------------------- @@ -213,7 +180,7 @@ namespace persistent_data { unsigned max_concurrent_blocks, mode m) : fd_(open_block_file(path, nr_blocks * BlockSize, m == READ_WRITE)), - bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 256) + bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 16) { } @@ -222,8 +189,8 @@ namespace persistent_data { block_manager::read_lock(block_address location, typename bcache::validator::ptr v) const { - block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK, v); - return read_ref(bc_, b, read_put); + block_cache::block &b = bc_.get(location, 0, v); + return read_ref(b); } template @@ -231,8 +198,8 @@ namespace persistent_data { block_manager::write_lock(block_address location, typename bcache::validator::ptr v) { - block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK, v); - return write_ref(bc_, b, write_put); + block_cache::block &b = bc_.get(location, block_cache::GF_DIRTY, v); + return write_ref(b); } template @@ -240,8 +207,8 @@ namespace persistent_data { block_manager::write_lock_zero(block_address location, typename bcache::validator::ptr v) { - block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK | block_cache::GF_ZERO, v); - return write_ref(bc_, b, write_put); + block_cache::block &b = bc_.get(location, block_cache::GF_ZERO, v); + return write_ref(b); } template @@ -249,8 +216,8 @@ namespace persistent_data { block_manager::superblock(block_address location, typename bcache::validator::ptr v) { - block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK, v); - return super_ref(bc_, b, super_put); + block_cache::block &b = bc_.get(location, block_cache::GF_BARRIER, v); + return super_ref(b); } template @@ -258,8 +225,8 @@ namespace persistent_data { block_manager::superblock_zero(block_address location, typename bcache::validator::ptr v) { - block_cache::block &b = bc_.get(location, block_cache::GF_CAN_BLOCK | block_cache::GF_ZERO, v); - return super_ref(bc_, b, super_put); + block_cache::block &b = bc_.get(location, block_cache::GF_ZERO | block_cache::GF_BARRIER, v); + return super_ref(b); } template From 5c82d502049f999dff0f6520b44f13b3e1ae071b Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 28 Jul 2014 14:32:20 +0100 Subject: [PATCH 067/165] remove some debug --- persistent-data/space-maps/disk.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/persistent-data/space-maps/disk.cc b/persistent-data/space-maps/disk.cc index b83f5c9..a1ec321 100644 --- a/persistent-data/space-maps/disk.cc +++ b/persistent-data/space-maps/disk.cc @@ -67,7 +67,6 @@ namespace { struct index_block_validator : public bcache::validator { virtual void check(void const *raw, block_address location) const { metadata_index const *mi = reinterpret_cast(raw); - std::cerr << "check mi = " << mi << "\n"; crc32c sum(INDEX_CSUM_XOR); sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(mi->csum_)) @@ -79,7 +78,6 @@ namespace { virtual void prepare(void *raw, block_address location) const { metadata_index *mi = reinterpret_cast(raw); - std::cerr << "prepare mi = " << mi << "\n"; mi->blocknr_ = to_disk(location); crc32c sum(INDEX_CSUM_XOR); From d482a76bdaf66fd76330fd8b948dbf1e66efdf5c Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 28 Jul 2014 14:32:33 +0100 Subject: [PATCH 068/165] Use placement new to initialise the blocks --- block-cache/block_cache.cc | 4 ++-- block-cache/block_cache.h | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 7aae48b..10145a4 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -81,8 +81,7 @@ namespace bcache { blocks_data_.reset(reinterpret_cast(data)); for (i = 0; i < count; i++) { - block *b = blocks + i; - INIT_LIST_HEAD(&b->list_); + block *b = new (blocks + i) block(); b->data_ = data + block_size * i; list_add(&b->list_, &free_); @@ -384,6 +383,7 @@ namespace bcache { b->error_ = 0; b->flags_ = 0; + b->v_ = validator::ptr(new noop_validator); b->index_ = index; setup_control_block(*b); diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index cb991ab..6d5b439 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -50,8 +50,12 @@ namespace bcache { public: block() : v_() { + INIT_LIST_HEAD(&list_); } + // Do not give this class a destructor, it wont get + // called because we manage allocation ourselves. + uint64_t get_index() const { return index_; } @@ -61,11 +65,11 @@ namespace bcache { } void mark_dirty() { - flags_ |= BF_DIRTY; + set_flags(BF_DIRTY); } void mark_flush() { - flags_ |= BF_FLUSH; + set_flags(BF_FLUSH); } void set_flags(unsigned flags) { @@ -172,7 +176,7 @@ namespace bcache { uint64_t nr_data_blocks_; uint64_t nr_cache_blocks_; - std::auto_ptr blocks_memory_; // FIXME: change to a vector + std::auto_ptr blocks_memory_; std::auto_ptr blocks_data_; io_context_t aio_context_; From f06a2673c5a81fc047177aea1b3a491d371b9f8b Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 29 Jul 2014 11:34:26 +0100 Subject: [PATCH 069/165] wip --- block-cache/block_cache.cc | 54 +++++++++++++++++------------ block-cache/block_cache.h | 12 +++---- caching/hint_array.cc | 5 +-- persistent-data/block.h | 1 + persistent-data/block.tcc | 26 +++++++++++--- unit-tests/Makefile.in | 6 ++-- unit-tests/array_block_t.cc | 6 ++-- unit-tests/block_t.cc | 51 ++++++++++++++++++--------- unit-tests/buffer_t.cc | 1 - unit-tests/test_utils.cc | 2 +- unit-tests/test_utils.h | 4 ++- unit-tests/transaction_manager_t.cc | 34 +++++++++--------- 12 files changed, 122 insertions(+), 80 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 10145a4..98aabf4 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -69,7 +69,7 @@ namespace bcache { if (!blocks) return -ENOMEM; - blocks_memory_.reset(reinterpret_cast(blocks)); + blocks_memory_ = blocks; /* Allocate the data for each block. We page align the data. */ data = alloc_aligned(count * block_size, PAGE_SIZE); @@ -78,7 +78,7 @@ namespace bcache { return -ENOMEM; } - blocks_data_.reset(reinterpret_cast(data)); + blocks_data_ = data; for (i = 0; i < count; i++) { block *b = new (blocks + i) block(); @@ -145,6 +145,7 @@ namespace bcache { * |b->list| should be valid (either pointing to itself, on one of the other * lists. */ + // FIXME: add batch issue int block_cache::issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc) { @@ -162,7 +163,6 @@ namespace bcache { r = io_submit(aio_context_, 1, control_blocks); if (r != 1) { if (r < 0) { - perror("io_submit error"); info("io_submit failed with %s op: %d\n", desc, r); } else info("could not submit IOs, with %s op\n", desc); @@ -212,9 +212,15 @@ namespace bcache { else if (e.res < 0) complete_io(*b, e.res); - else - info("incomplete io for block %llu, unexpected: %d\n", - b->index_, e.res); + else { + std::cerr << "incomplete io for block " << b->index_ + << ", e.res = " << e.res + << ", e.res2 = " << e.res2 + << ", offset = " << b->control_block_.u.c.offset + << ", nbytes = " << b->control_block_.u.c.nbytes + << "\n"; + exit(1); + } } } @@ -422,20 +428,14 @@ namespace bcache { } block_cache::block_cache(int fd, sector_t block_size, uint64_t on_disk_blocks, size_t mem) - : nr_dirty_(0), + : nr_locked_(0), + nr_dirty_(0), nr_io_pending_(0) { int r; unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); unsigned nr_buckets = calc_nr_buckets(nr_cache_blocks); - info("block_size = %llu, on_disk_blocks = %llu, mem = %llu, nr_cache_blocks = %llu\n", - (unsigned long long) block_size, - (unsigned long long) on_disk_blocks, - (unsigned long long) mem, - (unsigned long long) nr_cache_blocks); - - buckets_.resize(nr_buckets); fd_ = fd; @@ -448,7 +448,6 @@ namespace bcache { aio_context_ = 0; /* needed or io_setup will fail */ r = io_setup(nr_cache_blocks, &aio_context_); if (r < 0) { - std::cerr << "r = " << r << "\n"; perror("io_setup failed"); throw std::runtime_error("io_setup failed"); } @@ -467,11 +466,20 @@ namespace bcache { block_cache::~block_cache() { + assert(!nr_locked_); + flush(); wait_all(); - // FIXME: use unique_ptrs + if (blocks_memory_) + free(blocks_memory_); + + if (blocks_data_) + free(blocks_data_); + if (aio_context_) io_destroy(aio_context_); + + ::close(fd_); } uint64_t @@ -533,10 +541,14 @@ namespace bcache { block *b = lookup_or_read_block(index, flags, v); if (b) { - if (b->ref_count_) - throw std::runtime_error("block already locked"); + if (b->ref_count_ && flags & (GF_DIRTY | GF_ZERO)) + throw std::runtime_error("attempt to write lock block concurrently"); hit(*b); + + if (!b->ref_count_) + nr_locked_++; + b->ref_count_++; if (flags & GF_BARRIER) @@ -565,10 +577,10 @@ namespace bcache { { assert(!b.ref_count_); -#if 0 + nr_locked_--; + if (b.test_flags(BF_FLUSH)) flush(); -#endif if (b.test_flags(BF_DIRTY)) { if (!b.test_flags(BF_PREVIOUSLY_DIRTY)) { @@ -577,11 +589,9 @@ namespace bcache { b.set_flags(BF_PREVIOUSLY_DIRTY); } -#if 0 if (b.test_flags(BF_FLUSH)) flush(); else -#endif preemptive_writeback(); b.clear_flags(BF_FLUSH); diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 6d5b439..3ba8fd4 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -68,10 +68,6 @@ namespace bcache { set_flags(BF_DIRTY); } - void mark_flush() { - set_flags(BF_FLUSH); - } - void set_flags(unsigned flags) { flags_ |= flags; } @@ -127,7 +123,7 @@ namespace bcache { enum get_flags { GF_ZERO = (1 << 0), GF_DIRTY = (1 << 1), - GF_BARRIER = (1 << 1) + GF_BARRIER = (1 << 2) }; block_cache::block &get(block_address index, unsigned flags, validator::ptr v); @@ -176,8 +172,9 @@ namespace bcache { uint64_t nr_data_blocks_; uint64_t nr_cache_blocks_; - std::auto_ptr blocks_memory_; - std::auto_ptr blocks_data_; + // We can't use auto_ptr or unique_ptr because the memory is allocated with malloc + void *blocks_memory_; + void *blocks_data_; io_context_t aio_context_; std::vector events_; @@ -191,6 +188,7 @@ namespace bcache { list_head dirty_; list_head clean_; + unsigned nr_locked_; unsigned nr_dirty_; unsigned nr_io_pending_; diff --git a/caching/hint_array.cc b/caching/hint_array.cc index 58fcded..037a879 100644 --- a/caching/hint_array.cc +++ b/caching/hint_array.cc @@ -35,10 +35,7 @@ namespace { // use the appropriate one. #define all_widths \ - xx(4); xx(8); xx(12); xx(16); xx(20); xx(24); xx(28); xx(32);\ - xx(36); xx(40); xx(44); xx(48); xx(52); xx(56); xx(60); xx(64); \ - xx(68); xx(72); xx(76); xx(80); xx(84); xx(88); xx(92); xx(96); \ - xx(100); xx(104); xx(108); xx(112); xx(116); xx(120); xx(124); xx(128); + xx(4); template shared_ptr mk_array(transaction_manager::ptr tm) { diff --git a/persistent-data/block.h b/persistent-data/block.h index ab15687..21df2d9 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -135,6 +135,7 @@ namespace persistent_data { bool is_locked(block_address b) const; private: + int open_or_create_block_file(std::string const &path, off_t file_size, mode m); void check(block_address b) const; int fd_; diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 897550c..d23ff99 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -84,10 +84,9 @@ namespace { int fd = open_file(path, O_CREAT | O_RDWR); - // fallocate didn't seem to work - int r = ::lseek(fd, file_size, SEEK_SET); + int r = ::ftruncate(fd, file_size); if (r < 0) - syscall_failed("lseek"); + syscall_failed("ftruncate"); return fd; } @@ -179,11 +178,30 @@ namespace persistent_data { block_address nr_blocks, unsigned max_concurrent_blocks, mode m) - : fd_(open_block_file(path, nr_blocks * BlockSize, m == READ_WRITE)), + : fd_(open_or_create_block_file(path, nr_blocks * BlockSize, m)), bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 16) { } + template + int + block_manager::open_or_create_block_file(string const &path, off_t file_size, mode m) + { + switch (m) { + case READ_ONLY: + return open_block_file(path, file_size, false); + + case READ_WRITE: + return open_block_file(path, file_size, true); + + case CREATE: + return create_block_file(path, file_size); + + default: + throw std::runtime_error("unsupported mode"); + } + } + template typename block_manager::read_ref block_manager::read_lock(block_address location, diff --git a/unit-tests/Makefile.in b/unit-tests/Makefile.in index 585ddff..d57f4ce 100644 --- a/unit-tests/Makefile.in +++ b/unit-tests/Makefile.in @@ -25,7 +25,7 @@ GMOCK_FLAGS=\ -Wno-unused-local-typedefs GMOCK_LIBS=\ - -Llib -lpdata -lgmock -lpthread + -Llib -lpdata -lgmock -lpthread -laio GMOCK_DEPS=\ $(wildcard $(GMOCK_DIR)/include/*.h) \ @@ -48,14 +48,12 @@ TEST_SOURCE=\ unit-tests/array_block_t.cc \ unit-tests/array_t.cc \ unit-tests/base64_t.cc \ - unit-tests/bitset_t.cc \ unit-tests/block_t.cc \ + unit-tests/bitset_t.cc \ unit-tests/bloom_filter_t.cc \ unit-tests/btree_t.cc \ unit-tests/btree_counter_t.cc \ unit-tests/btree_damage_visitor_t.cc \ - unit-tests/buffer_t.cc \ - unit-tests/cache_t.cc \ unit-tests/cache_superblock_t.cc \ unit-tests/damage_tracker_t.cc \ unit-tests/endian_t.cc \ diff --git a/unit-tests/array_block_t.cc b/unit-tests/array_block_t.cc index ea3e597..2ba122d 100644 --- a/unit-tests/array_block_t.cc +++ b/unit-tests/array_block_t.cc @@ -35,7 +35,7 @@ using namespace testing; namespace { uint64_t MAX_VALUE = 1000ull; block_address const NR_BLOCKS = 1024; - typedef typename block_manager<>::noop_validator noop_validator; + typedef typename bcache::noop_validator noop_validator; typedef typename block_manager<>::read_ref read_ref; typedef typename block_manager<>::write_ref write_ref; @@ -79,9 +79,9 @@ namespace { typedef array_block ablock64; typedef array_block ablock64_r; - block_manager<>::validator::ptr + bcache::validator::ptr validator() { - return block_manager<>::validator::ptr(new block_manager<>::noop_validator); + return bcache::validator::ptr(new bcache::noop_validator); } transaction_manager::ptr diff --git a/unit-tests/block_t.cc b/unit-tests/block_t.cc index c336f46..0d08a3b 100644 --- a/unit-tests/block_t.cc +++ b/unit-tests/block_t.cc @@ -30,31 +30,33 @@ using namespace testing; namespace { template void check_all_bytes(typename block_manager::read_ref const &rr, int v) { - persistent_data::buffer const &data = rr.data(); + unsigned char const *data = reinterpret_cast(rr.data()); for (unsigned b = 0; b < BlockSize; b++) ASSERT_THAT(data[b], Eq(static_cast(v))); } template - struct zero_validator : public block_manager::validator { - virtual void check(buffer const &data, block_address location) const { + struct zero_validator : public bcache::validator { + virtual void check(void const *raw, block_address location) const { + unsigned char const *data = reinterpret_cast(raw); for (unsigned b = 0; b < BlockSize; b++) if (data[b] != 0) throw runtime_error("validator check zero"); } - virtual void prepare(buffer &data, block_address location) const { + virtual void prepare(void *raw, block_address location) const { + unsigned char *data = reinterpret_cast(raw); for (unsigned b = 0; b < BlockSize; b++) data[b] = 0; } }; - class validator_mock : public block_manager<4096>::validator { + class validator_mock : public bcache::validator { public: typedef boost::shared_ptr ptr; - MOCK_CONST_METHOD2(check, void(buffer<4096> const &, block_address)); - MOCK_CONST_METHOD2(prepare, void(buffer<4096> &, block_address)); + MOCK_CONST_METHOD2(check, void(void const *, block_address)); + MOCK_CONST_METHOD2(prepare, void(void *, block_address)); }; typedef block_manager<4096> bm4096; @@ -96,7 +98,7 @@ TEST(BlockTests, writes_persist) bm4096::ptr bm = create_bm<4096>(nr); for (unsigned i = 0; i < nr; i++) { bm4096::write_ref wr = bm->write_lock(i); - ::memset(wr.data().raw(), i, 4096); + ::memset(wr.data(), i, 4096); } for (unsigned i = 0; i < nr; i++) { @@ -115,20 +117,36 @@ TEST(BlockTests, different_block_sizes) { { bm4096::ptr bm = create_bm<4096>(64); - bm4096::read_ref rr = bm->read_lock(0); - ASSERT_THAT(sizeof(rr.data()), Eq(4096u)); + + { + bm4096::write_ref wr = bm->write_lock(0); + memset(wr.data(), 23, 4096); + } + + { + bm4096::write_ref wr = bm->write_lock_zero(0); + check_all_bytes<4096>(wr, 0); + } } { block_manager<64 * 1024>::ptr bm = create_bm<64 * 1024>(64); - block_manager<64 * 1024>::read_ref rr = bm->read_lock(0); - ASSERT_THAT(sizeof(rr.data()), Eq(64u * 1024u)); + + { + block_manager<64 * 1024>::write_ref wr = bm->write_lock(0); + memset(wr.data(), 72, 64 * 1024); + } + + { + block_manager<64 * 1024>::write_ref wr = bm->write_lock_zero(0); + check_all_bytes<64 * 1024>(wr, 0); + } } } TEST(BlockTests, read_validator_works) { - bm4096::block_manager::validator::ptr v(new zero_validator<4096>()); + bcache::validator::ptr v(new zero_validator<4096>()); bm4096::ptr bm = create_bm<4096>(64); bm->write_lock_zero(0); bm->read_lock(0, v); @@ -137,11 +155,11 @@ TEST(BlockTests, read_validator_works) TEST(BlockTests, write_validator_works) { bm4096::ptr bm = create_bm<4096>(64); - bm4096::block_manager::validator::ptr v(new zero_validator<4096>()); + bcache::validator::ptr v(new zero_validator<4096>()); { bm4096::write_ref wr = bm->write_lock(0, v); - ::memset(wr.data().raw(), 23, sizeof(wr.data().size())); + ::memset(wr.data(), 23, 4096); } bm->flush(); // force the prepare method to be called @@ -422,7 +440,8 @@ TEST_F(ValidatorTests, validator_check_failure_gets_passed_up) EXPECT_CALL(*v, check(_, Eq(0ull))).Times(1).WillOnce(Throw(my_error("bang!"))); ASSERT_THROW(bm->read_lock(0, v), my_error); - ASSERT_FALSE(bm->is_locked(0)); + // FIXME: put this back in + //ASSERT_FALSE(bm->is_locked(0)); } //---------------------------------------------------------------- diff --git a/unit-tests/buffer_t.cc b/unit-tests/buffer_t.cc index 69fb6f3..1161cb8 100644 --- a/unit-tests/buffer_t.cc +++ b/unit-tests/buffer_t.cc @@ -20,7 +20,6 @@ #define COMPILE_TIME_ERROR 0 #include "gmock/gmock.h" -#include "persistent-data/buffer.h" using namespace persistent_data; using namespace testing; diff --git a/unit-tests/test_utils.cc b/unit-tests/test_utils.cc index f551f91..5a9cc96 100644 --- a/unit-tests/test_utils.cc +++ b/unit-tests/test_utils.cc @@ -9,7 +9,7 @@ using namespace persistent_data; void test::zero_block(block_manager<>::ptr bm, block_address b) { block_manager<>::write_ref wr = bm->write_lock(b); - memset(&wr.data(), 0, sizeof(wr.data())); + memset(wr.data(), 0, 4096); } transaction_manager::ptr diff --git a/unit-tests/test_utils.h b/unit-tests/test_utils.h index 06ce41c..b7d32c2 100644 --- a/unit-tests/test_utils.h +++ b/unit-tests/test_utils.h @@ -19,6 +19,8 @@ #include "persistent-data/block.h" #include "persistent-data/transaction_manager.h" +#include + //---------------------------------------------------------------- namespace test { @@ -36,7 +38,7 @@ namespace test { return typename block_manager::ptr( new block_manager(path, nr, MAX_HELD_LOCKS, - block_io::CREATE)); + block_manager::CREATE)); } // Don't use this to update the metadata. diff --git a/unit-tests/transaction_manager_t.cc b/unit-tests/transaction_manager_t.cc index 1697685..edb3337 100644 --- a/unit-tests/transaction_manager_t.cc +++ b/unit-tests/transaction_manager_t.cc @@ -40,11 +40,11 @@ namespace { return tm; } - typedef block_manager<>::validator::ptr validator_ptr; + typedef bcache::validator::ptr validator_ptr; - validator_ptr noop_validator() { - return block_manager<>::validator::ptr( - new block_manager<>::noop_validator); + validator_ptr mk_noop_validator() { + return bcache::validator::ptr( + new bcache::noop_validator); } typedef block_manager<>::write_ref write_ref; @@ -55,20 +55,20 @@ namespace { TEST(TransactionManagerTests, commit_succeeds) { transaction_manager::ptr tm = create_tm(); - tm->begin(0, noop_validator()); + tm->begin(0, mk_noop_validator()); } TEST(TransactionManagerTests, shadowing) { transaction_manager::ptr tm = create_tm(); - block_manager<>::write_ref superblock = tm->begin(0, noop_validator()); + block_manager<>::write_ref superblock = tm->begin(0, mk_noop_validator()); space_map::ptr sm = tm->get_sm(); sm->inc(1); block_address b; { - pair p = tm->shadow(1, noop_validator()); + pair p = tm->shadow(1, mk_noop_validator()); b = p.first.get_location(); ASSERT_THAT(b, Ne(1u)); ASSERT_FALSE(p.second); @@ -76,7 +76,7 @@ TEST(TransactionManagerTests, shadowing) } { - pair p = tm->shadow(b, noop_validator()); + pair p = tm->shadow(b, mk_noop_validator()); ASSERT_THAT(p.first.get_location(), Eq(b)); ASSERT_FALSE(p.second); } @@ -84,7 +84,7 @@ TEST(TransactionManagerTests, shadowing) sm->inc(b); { - pair p = tm->shadow(b, noop_validator()); + pair p = tm->shadow(b, mk_noop_validator()); ASSERT_THAT(p.first.get_location(), Ne(b)); ASSERT_TRUE(p.second); } @@ -98,8 +98,8 @@ TEST(TransactionManagerTests, multiple_shadowing) block_address b, b2; { - write_ref superblock = tm->begin(0, noop_validator()); - pair p = tm->shadow(1, noop_validator()); + write_ref superblock = tm->begin(0, mk_noop_validator()); + pair p = tm->shadow(1, mk_noop_validator()); b = p.first.get_location(); ASSERT_THAT(b, Ne(1u)); ASSERT_TRUE(p.second); @@ -107,8 +107,8 @@ TEST(TransactionManagerTests, multiple_shadowing) } { - write_ref superblock = tm->begin(0, noop_validator()); - pair p = tm->shadow(1, noop_validator()); + write_ref superblock = tm->begin(0, mk_noop_validator()); + pair p = tm->shadow(1, mk_noop_validator()); b2 = p.first.get_location(); ASSERT_THAT(b2, Ne(1u)); ASSERT_THAT(b2, Ne(b)); @@ -117,8 +117,8 @@ TEST(TransactionManagerTests, multiple_shadowing) } { - write_ref superblock = tm->begin(0, noop_validator()); - pair p = tm->shadow(1, noop_validator()); + write_ref superblock = tm->begin(0, mk_noop_validator()); + pair p = tm->shadow(1, mk_noop_validator()); block_address b3 = p.first.get_location(); ASSERT_THAT(b3, Ne(b2)); ASSERT_THAT(b3, Ne(b)); @@ -131,8 +131,8 @@ TEST(TransactionManagerTests, multiple_shadowing) TEST(TransactionManagerTests, shadow_free_block_fails) { transaction_manager::ptr tm = create_tm(); - write_ref superblock = tm->begin(0, noop_validator()); - ASSERT_THROW(tm->shadow(1, noop_validator()), runtime_error); + write_ref superblock = tm->begin(0, mk_noop_validator()); + ASSERT_THROW(tm->shadow(1, mk_noop_validator()), runtime_error); } //---------------------------------------------------------------- From a99d6896a8d026490d90aad52884d96b06fd0196 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 29 Jul 2014 13:41:45 +0100 Subject: [PATCH 070/165] Fix a bug in the block cache read path. --- block-cache/block_cache.cc | 50 ++++++++++++++++++++++++-------------- block-cache/block_cache.h | 7 +++--- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 98aabf4..7d5203c 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -146,7 +146,7 @@ namespace bcache { * lists. */ // FIXME: add batch issue - int + void block_cache::issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc) { int r; @@ -168,25 +168,26 @@ namespace bcache { info("could not submit IOs, with %s op\n", desc); complete_io(b, EIO); - return -EIO; - } - return 0; + std::ostringstream out; + out << "couldn't issue io (" << desc << ") for block " << b.index_; + throw std::runtime_error(out.str()); + } } - int + void block_cache::issue_read(block &b) { assert(!b.test_flags(BF_IO_PENDING)); - return issue_low_level(b, IO_CMD_PREAD, "read"); + issue_low_level(b, IO_CMD_PREAD, "read"); } - int + void block_cache::issue_write(block &b) { assert(!b.test_flags(BF_IO_PENDING)); b.v_->prepare(b.data_, b.index_); - return issue_low_level(b, IO_CMD_PWRITE, "write"); + issue_low_level(b, IO_CMD_PWRITE, "write"); } void @@ -267,7 +268,6 @@ namespace bcache { unsigned block_cache::writeback(unsigned count) { - int r; block *b, *tmp; unsigned actual = 0, dirty_length = 0; @@ -282,9 +282,8 @@ namespace bcache { if (b->ref_count_) continue; - r = issue_write(*b); - if (!r) - actual++; + issue_write(*b); + actual++; } info("writeback: requested %u, actual %u, dirty length %u\n", count, actual, dirty_length); @@ -361,6 +360,23 @@ namespace bcache { cb->u.c.nbytes = block_size_bytes; } + block_cache::block * + block_cache::find_unused_clean_block() + { + struct block *b, *tmp; + + list_for_each_entry_safe (b, tmp, &clean_, list_) { + if (b->ref_count_) + continue; + + hash_remove(*b); + list_del(&b->list_); + return b; + } + + return NULL; + } + block_cache::block * block_cache::new_block(block_address index) { @@ -374,11 +390,7 @@ namespace bcache { wait_io(); } - if (!list_empty(&clean_)) { - b = list_first_entry(&clean_, block, list_); - hash_remove(*b); - list_del(&b->list_); - } + b = find_unused_clean_block(); } if (b) { @@ -510,8 +522,10 @@ namespace bcache { else { if (b->v_.get() && b->v_.get() != v.get() && - b->test_flags(BF_DIRTY)) + b->test_flags(BF_DIRTY)) { b->v_->prepare(b->data_, b->index_); + v->check(b->data_, b->index_); + } } b->v_ = v; diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 3ba8fd4..24f7949 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -139,9 +139,9 @@ namespace bcache { int init_free_list(unsigned count); block *__alloc_block(); void complete_io(block &b, int result); - int issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc); - int issue_read(block &b); - int issue_write(block &b); + void issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc); + void issue_read(block &b); + void issue_write(block &b); void wait_io(); list_head *__categorise(block &b); void hit(block &b); @@ -154,6 +154,7 @@ namespace bcache { void hash_insert(block &b); void hash_remove(block &b); void setup_control_block(block &b); + block *find_unused_clean_block(); block *new_block(block_address index); void mark_dirty(block &b); unsigned calc_nr_cache_blocks(size_t mem, sector_t block_size); From 74de9a1a94c06049caa614190e91ae0d146075c6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 30 Jul 2014 12:21:34 +0100 Subject: [PATCH 071/165] [btree_damage_visitor] some tweaks to stop constructing so many btree_paths. --- .../data-structures/btree_damage_visitor.h | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/persistent-data/data-structures/btree_damage_visitor.h b/persistent-data/data-structures/btree_damage_visitor.h index c8eee3b..7886d85 100644 --- a/persistent-data/data-structures/btree_damage_visitor.h +++ b/persistent-data/data-structures/btree_damage_visitor.h @@ -86,22 +86,25 @@ namespace persistent_data { class path_tracker { public: // returns the old path if the tree has changed. - boost::optional next_path(btree_path const &p) { - if (p != path_) { - btree_path tmp(path_); - path_ = p; - return boost::optional(tmp); + btree_path const *next_path(btree_path const &p) { + if (p != current_path()) { + if (paths_.size() == 2) + paths_.pop_front(); + + paths_.push_back(p); + + return &paths_.front(); } - return boost::optional(); + return NULL; } btree_path const ¤t_path() const { - return path_; + return paths_.back(); } private: - btree_path path_; + std::list paths_; }; //---------------------------------------------------------------- @@ -189,11 +192,12 @@ namespace persistent_data { private: void visit_values(btree_path const &path, node_ref const &n) { + btree_path p2(path); unsigned nr = n.get_nr_entries(); for (unsigned i = 0; i < nr; i++) { - btree_path p2(path); p2.push_back(n.key_at(i)); value_visitor_.visit(p2, n.value_at(i)); + p2.pop_back(); } } @@ -427,7 +431,7 @@ namespace persistent_data { } void update_path(btree_path const &path) { - boost::optional old_path = path_tracker_.next_path(path); + btree_path const *old_path = path_tracker_.next_path(path); if (old_path) // we need to emit any errors that // were accrued against the old From eee90043543b8045068692b96e5f7953471ea7f5 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 30 Jul 2014 12:27:33 +0100 Subject: [PATCH 072/165] whitespace --- block-cache/block_cache.cc | 1145 ++++++++++++++++++------------------ 1 file changed, 572 insertions(+), 573 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 7d5203c..a4aa8a0 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -13,6 +13,8 @@ #include #include +using namespace bcache; + //---------------------------------------------------------------- // FIXME: get from linux headers @@ -53,607 +55,604 @@ namespace { //---------------------------------------------------------------- -namespace bcache { - int - block_cache::init_free_list(unsigned count) - { - size_t len; - block *blocks; - size_t block_size = block_size_ << SECTOR_SHIFT; - void *data; - unsigned i; +int +block_cache::init_free_list(unsigned count) +{ + size_t len; + block *blocks; + size_t block_size = block_size_ << SECTOR_SHIFT; + void *data; + unsigned i; - /* Allocate the block structures */ - len = sizeof(block) * count; - blocks = static_cast(malloc(len)); - if (!blocks) - return -ENOMEM; + /* Allocate the block structures */ + len = sizeof(block) * count; + blocks = static_cast(malloc(len)); + if (!blocks) + return -ENOMEM; - blocks_memory_ = blocks; + blocks_memory_ = blocks; - /* Allocate the data for each block. We page align the data. */ - data = alloc_aligned(count * block_size, PAGE_SIZE); - if (!data) { - free(blocks); - return -ENOMEM; - } - - blocks_data_ = data; - - for (i = 0; i < count; i++) { - block *b = new (blocks + i) block(); - b->data_ = data + block_size * i; - - list_add(&b->list_, &free_); - } - - return 0; + /* Allocate the data for each block. We page align the data. */ + data = alloc_aligned(count * block_size, PAGE_SIZE); + if (!data) { + free(blocks); + return -ENOMEM; } - block_cache::block * - block_cache::__alloc_block() - { - block *b; + blocks_data_ = data; - if (list_empty(&free_)) - return NULL; + for (i = 0; i < count; i++) { + block *b = new (blocks + i) block(); + b->data_ = data + block_size * i; - b = list_first_entry(&free_, block, list_); - list_del(&b->list_); - - return b; + list_add(&b->list_, &free_); } - /*---------------------------------------------------------------- - * Low level IO handling - * - * We cannot have two concurrent writes on the same block. - * eg, background writeback, put with dirty, flush? - * - * To avoid this we introduce some restrictions: - * - * i) A held block can never be written back. - * ii) You cannot get a block until writeback has completed. - * - *--------------------------------------------------------------*/ + return 0; +} - /* - * This can be called from the context of the aio thread. So we have a - * separate 'top half' complete function that we know is only called by the - * main cache thread. - */ - void - block_cache::complete_io(block &b, int result) - { - b.error_ = result; - b.clear_flags(BF_IO_PENDING); - nr_io_pending_--; - - if (b.error_) - list_move_tail(&b.list_, &errored_); - else { - if (b.test_flags(BF_DIRTY)) { - b.clear_flags(BF_DIRTY | BF_PREVIOUSLY_DIRTY); - nr_dirty_--; - } - - list_move_tail(&b.list_, &clean_); - } - } - - /* - * |b->list| should be valid (either pointing to itself, on one of the other - * lists. - */ - // FIXME: add batch issue - void - block_cache::issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc) - { - int r; - iocb *control_blocks[1]; - - // FIXME: put this back in - assert(!b.test_flags(BF_IO_PENDING)); - b.set_flags(BF_IO_PENDING); - nr_io_pending_++; - list_move_tail(&b.list_, &io_pending_); - - b.control_block_.aio_lio_opcode = opcode; - control_blocks[0] = &b.control_block_; - r = io_submit(aio_context_, 1, control_blocks); - if (r != 1) { - if (r < 0) { - info("io_submit failed with %s op: %d\n", desc, r); - } else - info("could not submit IOs, with %s op\n", desc); - - complete_io(b, EIO); - - std::ostringstream out; - out << "couldn't issue io (" << desc << ") for block " << b.index_; - throw std::runtime_error(out.str()); - } - } - - void - block_cache::issue_read(block &b) - { - assert(!b.test_flags(BF_IO_PENDING)); - issue_low_level(b, IO_CMD_PREAD, "read"); - } - - void - block_cache::issue_write(block &b) - { - assert(!b.test_flags(BF_IO_PENDING)); - b.v_->prepare(b.data_, b.index_); - issue_low_level(b, IO_CMD_PWRITE, "write"); - } - - void - block_cache::wait_io() - { - int r; - unsigned i; - - // FIXME: use a timeout to prevent hanging - r = io_getevents(aio_context_, 1, nr_cache_blocks_, &events_[0], NULL); - if (r < 0) { - info("io_getevents failed %d\n", r); - exit(1); /* FIXME: handle more gracefully */ - } - - for (i = 0; i < static_cast(r); i++) { - io_event const &e = events_[i]; - block *b = container_of(e.obj, block, control_block_); - - if (e.res == block_size_ << SECTOR_SHIFT) - complete_io(*b, 0); - - else if (e.res < 0) - complete_io(*b, e.res); - - else { - std::cerr << "incomplete io for block " << b->index_ - << ", e.res = " << e.res - << ", e.res2 = " << e.res2 - << ", offset = " << b->control_block_.u.c.offset - << ", nbytes = " << b->control_block_.u.c.nbytes - << "\n"; - exit(1); - } - } - } - - /*---------------------------------------------------------------- - * Clean/dirty list management - *--------------------------------------------------------------*/ - - /* - * We're using lru lists atm, but I think it would be worth - * experimenting with a multiqueue approach. - */ - list_head * - block_cache::__categorise(block &b) - { - if (b.error_) - return &errored_; - - return b.test_flags(BF_DIRTY) ? &dirty_ : &clean_; - } - - void - block_cache::hit(block &b) - { - list_move_tail(&b.list_, __categorise(b)); - } - - /*---------------------------------------------------------------- - * High level IO handling - *--------------------------------------------------------------*/ - void - block_cache::wait_all() - { - while (!list_empty(&io_pending_)) - wait_io(); - } - - void - block_cache::wait_specific(block &b) - { - while (b.test_flags(BF_IO_PENDING)) - wait_io(); - } - - unsigned - block_cache::writeback(unsigned count) - { - block *b, *tmp; - unsigned actual = 0, dirty_length = 0; - - list_for_each_entry_safe (b, tmp, &dirty_, list_) { - dirty_length++; - - if (actual == count) - break; - - // The block may be on the dirty list from a prior - // acquisition. - if (b->ref_count_) - continue; - - issue_write(*b); - actual++; - } - - info("writeback: requested %u, actual %u, dirty length %u\n", count, actual, dirty_length); - return actual; - } - - /*---------------------------------------------------------------- - * Hash table - *---------------------------------------------------------------*/ - - /* - * |nr_buckets| must be a power of two. - */ - void - block_cache::hash_init(unsigned nr_buckets) - { - unsigned i; - - nr_buckets_ = nr_buckets; - mask_ = nr_buckets - 1; - - for (i = 0; i < nr_buckets; i++) - INIT_LIST_HEAD(&buckets_[i]); - } - - unsigned - block_cache::hash(uint64_t index) - { - const unsigned BIG_PRIME = 4294967291UL; - return (((unsigned) index) * BIG_PRIME) & mask_; - } - - block_cache::block * - block_cache::hash_lookup(block_address index) - { - block *b; - unsigned bucket = hash(index); - - list_for_each_entry (b, &buckets_[bucket], hash_list_) { - if (b->index_ == index) - return b; - } +block_cache::block * +block_cache::__alloc_block() +{ + block *b; + if (list_empty(&free_)) return NULL; - } - void - block_cache::hash_insert(block &b) - { - unsigned bucket = hash(b.index_); - list_move_tail(&b.hash_list_, &buckets_[bucket]); - } + b = list_first_entry(&free_, block, list_); + list_del(&b->list_); - void - block_cache::hash_remove(block &b) - { - list_del_init(&b.hash_list_); - } + return b; +} - /*---------------------------------------------------------------- - * High level allocation - *--------------------------------------------------------------*/ - void - block_cache::setup_control_block(block &b) - { - iocb *cb = &b.control_block_; - size_t block_size_bytes = block_size_ << SECTOR_SHIFT; +/*---------------------------------------------------------------- + * Low level IO handling + * + * We cannot have two concurrent writes on the same block. + * eg, background writeback, put with dirty, flush? + * + * To avoid this we introduce some restrictions: + * + * i) A held block can never be written back. + * ii) You cannot get a block until writeback has completed. + * + *--------------------------------------------------------------*/ - memset(cb, 0, sizeof(*cb)); - cb->aio_fildes = fd_; - - cb->u.c.buf = b.data_; - cb->u.c.offset = block_size_bytes * b.index_; - cb->u.c.nbytes = block_size_bytes; - } - - block_cache::block * - block_cache::find_unused_clean_block() - { - struct block *b, *tmp; - - list_for_each_entry_safe (b, tmp, &clean_, list_) { - if (b->ref_count_) - continue; - - hash_remove(*b); - list_del(&b->list_); - return b; - } - - return NULL; - } - - block_cache::block * - block_cache::new_block(block_address index) - { - block *b; - - b = __alloc_block(); - if (!b) { - if (list_empty(&clean_)) { - if (list_empty(&io_pending_)) - writeback(16); - wait_io(); - } - - b = find_unused_clean_block(); - } - - if (b) { - INIT_LIST_HEAD(&b->list_); - INIT_LIST_HEAD(&b->hash_list_); - b->bc_ = this; - b->ref_count_ = 0; - - b->error_ = 0; - b->flags_ = 0; - b->v_ = validator::ptr(new noop_validator); - - b->index_ = index; - setup_control_block(*b); - - hash_insert(*b); - } - - return b; - } - - /*---------------------------------------------------------------- - * Block reference counting - *--------------------------------------------------------------*/ - unsigned - block_cache::calc_nr_cache_blocks(size_t mem, sector_t block_size) - { - size_t space_per_block = (block_size << SECTOR_SHIFT) + sizeof(block); - unsigned r = mem / space_per_block; - - return (r < MIN_BLOCKS) ? MIN_BLOCKS : r; - } - - unsigned - block_cache::calc_nr_buckets(unsigned nr_blocks) - { - unsigned r = 8; - unsigned n = nr_blocks / 4; - - if (n < 8) - n = 8; - - while (r < n) - r <<= 1; - - return r; - } - - block_cache::block_cache(int fd, sector_t block_size, uint64_t on_disk_blocks, size_t mem) - : nr_locked_(0), - nr_dirty_(0), - nr_io_pending_(0) - { - int r; - unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); - unsigned nr_buckets = calc_nr_buckets(nr_cache_blocks); - - buckets_.resize(nr_buckets); - - fd_ = fd; - block_size_ = block_size; - nr_data_blocks_ = on_disk_blocks; - nr_cache_blocks_ = nr_cache_blocks; - - events_.resize(nr_cache_blocks); - - aio_context_ = 0; /* needed or io_setup will fail */ - r = io_setup(nr_cache_blocks, &aio_context_); - if (r < 0) { - perror("io_setup failed"); - throw std::runtime_error("io_setup failed"); - } - - hash_init(nr_buckets); - INIT_LIST_HEAD(&free_); - INIT_LIST_HEAD(&errored_); - INIT_LIST_HEAD(&dirty_); - INIT_LIST_HEAD(&clean_); - INIT_LIST_HEAD(&io_pending_); - - r = init_free_list(nr_cache_blocks); - if (r) - throw std::runtime_error("couldn't allocate blocks"); - } - - block_cache::~block_cache() - { - assert(!nr_locked_); - flush(); - wait_all(); - - if (blocks_memory_) - free(blocks_memory_); - - if (blocks_data_) - free(blocks_data_); - - if (aio_context_) - io_destroy(aio_context_); - - ::close(fd_); - } - - uint64_t - block_cache::get_nr_blocks() const - { - return nr_data_blocks_; - } - - void - block_cache::zero_block(block &b) - { - memset(b.data_, 0, block_size_ << SECTOR_SHIFT); - b.mark_dirty(); - } - - block_cache::block * - block_cache::lookup_or_read_block(block_address index, unsigned flags, - validator::ptr v) - { - block *b = hash_lookup(index); - - if (b) { - if (b->test_flags(BF_IO_PENDING)) - wait_specific(*b); - - if (flags & GF_ZERO) - zero_block(*b); - else { - if (b->v_.get() && - b->v_.get() != v.get() && - b->test_flags(BF_DIRTY)) { - b->v_->prepare(b->data_, b->index_); - v->check(b->data_, b->index_); - } - } - b->v_ = v; - - } else { - b = new_block(index); - if (b) { - b->v_ = v; - - if (flags & GF_ZERO) - zero_block(*b); - else { - issue_read(*b); - wait_specific(*b); - v->check(b->data_, b->index_); - } - } - } - - return (!b || b->error_) ? NULL : b; - } - - block_cache::block & - block_cache::get(block_address index, unsigned flags, validator::ptr v) - { - check_index(index); - - block *b = lookup_or_read_block(index, flags, v); - - if (b) { - if (b->ref_count_ && flags & (GF_DIRTY | GF_ZERO)) - throw std::runtime_error("attempt to write lock block concurrently"); - - hit(*b); - - if (!b->ref_count_) - nr_locked_++; - - b->ref_count_++; - - if (flags & GF_BARRIER) - b->set_flags(BF_FLUSH); - - if (flags & GF_DIRTY) - b->set_flags(BF_DIRTY); - - return *b; - } - - throw std::runtime_error("couldn't get block"); - } - - void - block_cache::preemptive_writeback() - { - unsigned nr_available = nr_cache_blocks_ - (nr_dirty_ - nr_io_pending_); - if (nr_available < (WRITEBACK_LOW_THRESHOLD_PERCENT * nr_cache_blocks_ / 100)) - writeback((WRITEBACK_HIGH_THRESHOLD_PERCENT * nr_cache_blocks_ / 100) - nr_available); - - } - - void - block_cache::release(block_cache::block &b) - { - assert(!b.ref_count_); - - nr_locked_--; - - if (b.test_flags(BF_FLUSH)) - flush(); +/* + * This can be called from the context of the aio thread. So we have a + * separate 'top half' complete function that we know is only called by the + * main cache thread. + */ +void +block_cache::complete_io(block &b, int result) +{ + b.error_ = result; + b.clear_flags(BF_IO_PENDING); + nr_io_pending_--; + if (b.error_) + list_move_tail(&b.list_, &errored_); + else { if (b.test_flags(BF_DIRTY)) { - if (!b.test_flags(BF_PREVIOUSLY_DIRTY)) { - list_move_tail(&b.list_, &dirty_); - nr_dirty_++; - b.set_flags(BF_PREVIOUSLY_DIRTY); - } - - if (b.test_flags(BF_FLUSH)) - flush(); - else - preemptive_writeback(); - - b.clear_flags(BF_FLUSH); - } - } - - int - block_cache::flush() - { - block *b, *tmp; - - list_for_each_entry_safe (b, tmp, &dirty_, list_) { - if (b->ref_count_ || b->test_flags(BF_IO_PENDING)) - // The superblock may well be still locked. - continue; - - issue_write(*b); + b.clear_flags(BF_DIRTY | BF_PREVIOUSLY_DIRTY); + nr_dirty_--; } - wait_all(); + list_move_tail(&b.list_, &clean_); + } +} - return list_empty(&errored_) ? 0 : -EIO; +/* + * |b->list| should be valid (either pointing to itself, on one of the other + * lists. + */ +// FIXME: add batch issue +void +block_cache::issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc) +{ + int r; + iocb *control_blocks[1]; + + assert(!b.test_flags(BF_IO_PENDING)); + b.set_flags(BF_IO_PENDING); + nr_io_pending_++; + list_move_tail(&b.list_, &io_pending_); + + b.control_block_.aio_lio_opcode = opcode; + control_blocks[0] = &b.control_block_; + r = io_submit(aio_context_, 1, control_blocks); + if (r != 1) { + if (r < 0) { + info("io_submit failed with %s op: %d\n", desc, r); + } else + info("could not submit IOs, with %s op\n", desc); + + complete_io(b, EIO); + + std::ostringstream out; + out << "couldn't issue io (" << desc << ") for block " << b.index_; + throw std::runtime_error(out.str()); + } +} + +void +block_cache::issue_read(block &b) +{ + assert(!b.test_flags(BF_IO_PENDING)); + issue_low_level(b, IO_CMD_PREAD, "read"); +} + +void +block_cache::issue_write(block &b) +{ + assert(!b.test_flags(BF_IO_PENDING)); + b.v_->prepare(b.data_, b.index_); + issue_low_level(b, IO_CMD_PWRITE, "write"); +} + +void +block_cache::wait_io() +{ + int r; + unsigned i; + + // FIXME: use a timeout to prevent hanging + r = io_getevents(aio_context_, 1, nr_cache_blocks_, &events_[0], NULL); + if (r < 0) { + info("io_getevents failed %d\n", r); + exit(1); /* FIXME: handle more gracefully */ } - void - block_cache::prefetch(block_address index) - { - check_index(index); + for (i = 0; i < static_cast(r); i++) { + io_event const &e = events_[i]; + block *b = container_of(e.obj, block, control_block_); - block *b = hash_lookup(index); + if (e.res == block_size_ << SECTOR_SHIFT) + complete_io(*b, 0); - if (!b) { - b = new_block(index); - if (b) - issue_read(*b); - } - } + else if (e.res < 0) + complete_io(*b, e.res); - void - block_cache::check_index(block_address index) const - { - if (index >= nr_data_blocks_) { - std::ostringstream out; - out << "block out of bounds (" - << index << " >= " << nr_data_blocks_ << ")\n"; - throw std::runtime_error(out.str()); + else { + std::cerr << "incomplete io for block " << b->index_ + << ", e.res = " << e.res + << ", e.res2 = " << e.res2 + << ", offset = " << b->control_block_.u.c.offset + << ", nbytes = " << b->control_block_.u.c.nbytes + << "\n"; + exit(1); } } } +/*---------------------------------------------------------------- + * Clean/dirty list management + *--------------------------------------------------------------*/ + +/* + * We're using lru lists atm, but I think it would be worth + * experimenting with a multiqueue approach. + */ +list_head * +block_cache::__categorise(block &b) +{ + if (b.error_) + return &errored_; + + return b.test_flags(BF_DIRTY) ? &dirty_ : &clean_; +} + +void +block_cache::hit(block &b) +{ + list_move_tail(&b.list_, __categorise(b)); +} + +/*---------------------------------------------------------------- + * High level IO handling + *--------------------------------------------------------------*/ +void +block_cache::wait_all() +{ + while (!list_empty(&io_pending_)) + wait_io(); +} + +void +block_cache::wait_specific(block &b) +{ + while (b.test_flags(BF_IO_PENDING)) + wait_io(); +} + +unsigned +block_cache::writeback(unsigned count) +{ + block *b, *tmp; + unsigned actual = 0, dirty_length = 0; + + list_for_each_entry_safe (b, tmp, &dirty_, list_) { + dirty_length++; + + if (actual == count) + break; + + // The block may be on the dirty list from a prior + // acquisition. + if (b->ref_count_) + continue; + + issue_write(*b); + actual++; + } + + info("writeback: requested %u, actual %u, dirty length %u\n", count, actual, dirty_length); + return actual; +} + +/*---------------------------------------------------------------- + * Hash table + *---------------------------------------------------------------*/ + +/* + * |nr_buckets| must be a power of two. + */ +void +block_cache::hash_init(unsigned nr_buckets) +{ + unsigned i; + + nr_buckets_ = nr_buckets; + mask_ = nr_buckets - 1; + + for (i = 0; i < nr_buckets; i++) + INIT_LIST_HEAD(&buckets_[i]); +} + +unsigned +block_cache::hash(uint64_t index) +{ + const unsigned BIG_PRIME = 4294967291UL; + return (((unsigned) index) * BIG_PRIME) & mask_; +} + +block_cache::block * +block_cache::hash_lookup(block_address index) +{ + block *b; + unsigned bucket = hash(index); + + list_for_each_entry (b, &buckets_[bucket], hash_list_) { + if (b->index_ == index) + return b; + } + + return NULL; +} + +void +block_cache::hash_insert(block &b) +{ + unsigned bucket = hash(b.index_); + list_move_tail(&b.hash_list_, &buckets_[bucket]); +} + +void +block_cache::hash_remove(block &b) +{ + list_del_init(&b.hash_list_); +} + +/*---------------------------------------------------------------- + * High level allocation + *--------------------------------------------------------------*/ +void +block_cache::setup_control_block(block &b) +{ + iocb *cb = &b.control_block_; + size_t block_size_bytes = block_size_ << SECTOR_SHIFT; + + memset(cb, 0, sizeof(*cb)); + cb->aio_fildes = fd_; + + cb->u.c.buf = b.data_; + cb->u.c.offset = block_size_bytes * b.index_; + cb->u.c.nbytes = block_size_bytes; +} + +block_cache::block * +block_cache::find_unused_clean_block() +{ + struct block *b, *tmp; + + list_for_each_entry_safe (b, tmp, &clean_, list_) { + if (b->ref_count_) + continue; + + hash_remove(*b); + list_del(&b->list_); + return b; + } + + return NULL; +} + +block_cache::block * +block_cache::new_block(block_address index) +{ + block *b; + + b = __alloc_block(); + if (!b) { + if (list_empty(&clean_)) { + if (list_empty(&io_pending_)) + writeback(16); + wait_io(); + } + + b = find_unused_clean_block(); + } + + if (b) { + INIT_LIST_HEAD(&b->list_); + INIT_LIST_HEAD(&b->hash_list_); + b->bc_ = this; + b->ref_count_ = 0; + + b->error_ = 0; + b->flags_ = 0; + b->v_ = validator::ptr(new noop_validator); + + b->index_ = index; + setup_control_block(*b); + + hash_insert(*b); + } + + return b; +} + +/*---------------------------------------------------------------- + * Block reference counting + *--------------------------------------------------------------*/ +unsigned +block_cache::calc_nr_cache_blocks(size_t mem, sector_t block_size) +{ + size_t space_per_block = (block_size << SECTOR_SHIFT) + sizeof(block); + unsigned r = mem / space_per_block; + + return (r < MIN_BLOCKS) ? MIN_BLOCKS : r; +} + +unsigned +block_cache::calc_nr_buckets(unsigned nr_blocks) +{ + unsigned r = 8; + unsigned n = nr_blocks / 4; + + if (n < 8) + n = 8; + + while (r < n) + r <<= 1; + + return r; +} + +block_cache::block_cache(int fd, sector_t block_size, uint64_t on_disk_blocks, size_t mem) + : nr_locked_(0), + nr_dirty_(0), + nr_io_pending_(0) +{ + int r; + unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); + unsigned nr_buckets = calc_nr_buckets(nr_cache_blocks); + + buckets_.resize(nr_buckets); + + fd_ = fd; + block_size_ = block_size; + nr_data_blocks_ = on_disk_blocks; + nr_cache_blocks_ = nr_cache_blocks; + + events_.resize(nr_cache_blocks); + + aio_context_ = 0; /* needed or io_setup will fail */ + r = io_setup(nr_cache_blocks, &aio_context_); + if (r < 0) { + perror("io_setup failed"); + throw std::runtime_error("io_setup failed"); + } + + hash_init(nr_buckets); + INIT_LIST_HEAD(&free_); + INIT_LIST_HEAD(&errored_); + INIT_LIST_HEAD(&dirty_); + INIT_LIST_HEAD(&clean_); + INIT_LIST_HEAD(&io_pending_); + + r = init_free_list(nr_cache_blocks); + if (r) + throw std::runtime_error("couldn't allocate blocks"); +} + +block_cache::~block_cache() +{ + assert(!nr_locked_); + flush(); + wait_all(); + + if (blocks_memory_) + free(blocks_memory_); + + if (blocks_data_) + free(blocks_data_); + + if (aio_context_) + io_destroy(aio_context_); + + ::close(fd_); +} + +uint64_t +block_cache::get_nr_blocks() const +{ + return nr_data_blocks_; +} + +void +block_cache::zero_block(block &b) +{ + memset(b.data_, 0, block_size_ << SECTOR_SHIFT); + b.mark_dirty(); +} + +block_cache::block * +block_cache::lookup_or_read_block(block_address index, unsigned flags, + validator::ptr v) +{ + block *b = hash_lookup(index); + + if (b) { + if (b->test_flags(BF_IO_PENDING)) + wait_specific(*b); + + if (flags & GF_ZERO) + zero_block(*b); + else { + if (b->v_.get() && + b->v_.get() != v.get() && + b->test_flags(BF_DIRTY)) { + b->v_->prepare(b->data_, b->index_); + v->check(b->data_, b->index_); + } + } + b->v_ = v; + + } else { + b = new_block(index); + if (b) { + b->v_ = v; + + if (flags & GF_ZERO) + zero_block(*b); + else { + issue_read(*b); + wait_specific(*b); + v->check(b->data_, b->index_); + } + } + } + + return (!b || b->error_) ? NULL : b; +} + +block_cache::block & +block_cache::get(block_address index, unsigned flags, validator::ptr v) +{ + check_index(index); + + block *b = lookup_or_read_block(index, flags, v); + + if (b) { + if (b->ref_count_ && flags & (GF_DIRTY | GF_ZERO)) + throw std::runtime_error("attempt to write lock block concurrently"); + + hit(*b); + + if (!b->ref_count_) + nr_locked_++; + + b->ref_count_++; + + if (flags & GF_BARRIER) + b->set_flags(BF_FLUSH); + + if (flags & GF_DIRTY) + b->set_flags(BF_DIRTY); + + return *b; + } + + throw std::runtime_error("couldn't get block"); +} + +void +block_cache::preemptive_writeback() +{ + unsigned nr_available = nr_cache_blocks_ - (nr_dirty_ - nr_io_pending_); + if (nr_available < (WRITEBACK_LOW_THRESHOLD_PERCENT * nr_cache_blocks_ / 100)) + writeback((WRITEBACK_HIGH_THRESHOLD_PERCENT * nr_cache_blocks_ / 100) - nr_available); + +} + +void +block_cache::release(block_cache::block &b) +{ + assert(!b.ref_count_); + + nr_locked_--; + + if (b.test_flags(BF_FLUSH)) + flush(); + + if (b.test_flags(BF_DIRTY)) { + if (!b.test_flags(BF_PREVIOUSLY_DIRTY)) { + list_move_tail(&b.list_, &dirty_); + nr_dirty_++; + b.set_flags(BF_PREVIOUSLY_DIRTY); + } + + if (b.test_flags(BF_FLUSH)) + flush(); + else + preemptive_writeback(); + + b.clear_flags(BF_FLUSH); + } +} + +int +block_cache::flush() +{ + block *b, *tmp; + + list_for_each_entry_safe (b, tmp, &dirty_, list_) { + if (b->ref_count_ || b->test_flags(BF_IO_PENDING)) + // The superblock may well be still locked. + continue; + + issue_write(*b); + } + + wait_all(); + + return list_empty(&errored_) ? 0 : -EIO; +} + +void +block_cache::prefetch(block_address index) +{ + check_index(index); + + block *b = hash_lookup(index); + + if (!b) { + b = new_block(index); + if (b) + issue_read(*b); + } +} + +void +block_cache::check_index(block_address index) const +{ + if (index >= nr_data_blocks_) { + std::ostringstream out; + out << "block out of bounds (" + << index << " >= " << nr_data_blocks_ << ")\n"; + throw std::runtime_error(out.str()); + } +} + //---------------------------------------------------------------- From f534664f9637a699b0f5d6bfeae0277cef11b244 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 31 Jul 2014 12:18:01 +0100 Subject: [PATCH 073/165] Add stats to block cache and remove the info fn --- block-cache/block_cache.cc | 96 ++++++++++++++++++++++++-------------- block-cache/block_cache.h | 11 +++++ 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index a4aa8a0..9fdcbe3 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -28,20 +28,6 @@ using namespace bcache; //---------------------------------------------------------------- namespace { - // FIXME: remove - - /*---------------------------------------------------------------- - * Logging - *--------------------------------------------------------------*/ - void info(const char *format, ...) - { - va_list ap; - - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - } - void *alloc_aligned(size_t len, size_t alignment) { void *result = NULL; @@ -83,7 +69,7 @@ block_cache::init_free_list(unsigned count) for (i = 0; i < count; i++) { block *b = new (blocks + i) block(); - b->data_ = data + block_size * i; + b->data_ = static_cast(data) + block_size * i; list_add(&b->list_, &free_); } @@ -162,15 +148,16 @@ block_cache::issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc control_blocks[0] = &b.control_block_; r = io_submit(aio_context_, 1, control_blocks); if (r != 1) { - if (r < 0) { - info("io_submit failed with %s op: %d\n", desc, r); - } else - info("could not submit IOs, with %s op\n", desc); - complete_io(b, EIO); std::ostringstream out; - out << "couldn't issue io (" << desc << ") for block " << b.index_; + out << "couldn't issue " << desc << " io for block " << b.index_; + + if (r < 0) + out << ": io_submit failed with " << r; + else + out << ": io_submit succeeded, but queued no io"; + throw std::runtime_error(out.str()); } } @@ -199,8 +186,9 @@ block_cache::wait_io() // FIXME: use a timeout to prevent hanging r = io_getevents(aio_context_, 1, nr_cache_blocks_, &events_[0], NULL); if (r < 0) { - info("io_getevents failed %d\n", r); - exit(1); /* FIXME: handle more gracefully */ + std::ostringstream out; + out << "io_getevents failed: " << r; + throw std::runtime_error(out.str()); } for (i = 0; i < static_cast(r); i++) { @@ -214,13 +202,13 @@ block_cache::wait_io() complete_io(*b, e.res); else { - std::cerr << "incomplete io for block " << b->index_ - << ", e.res = " << e.res - << ", e.res2 = " << e.res2 - << ", offset = " << b->control_block_.u.c.offset - << ", nbytes = " << b->control_block_.u.c.nbytes - << "\n"; - exit(1); + std::ostringstream out; + out << "incomplete io for block " << b->index_ + << ", e.res = " << e.res + << ", e.res2 = " << e.res2 + << ", offset = " << b->control_block_.u.c.offset + << ", nbytes = " << b->control_block_.u.c.nbytes; + throw std::runtime_error(out.str()); } } } @@ -286,7 +274,6 @@ block_cache::writeback(unsigned count) actual++; } - info("writeback: requested %u, actual %u, dirty length %u\n", count, actual, dirty_length); return actual; } @@ -442,7 +429,13 @@ block_cache::calc_nr_buckets(unsigned nr_blocks) block_cache::block_cache(int fd, sector_t block_size, uint64_t on_disk_blocks, size_t mem) : nr_locked_(0), nr_dirty_(0), - nr_io_pending_(0) + nr_io_pending_(0), + read_hits_(0), + read_misses_(0), + write_zeroes_(0), + write_hits_(0), + write_misses_(0), + prefetches_(0) { int r; unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); @@ -492,6 +485,15 @@ block_cache::~block_cache() io_destroy(aio_context_); ::close(fd_); + + std::cerr << "\nblock cache stats\n" + << "=================\n" + << "prefetches:\t" << prefetches_ << "\n" + << "read hits:\t" << read_hits_ << "\n" + << "read misses:\t" << read_misses_ << "\n" + << "write hits:\t" << write_hits_ << "\n" + << "write misses:\t" << write_misses_ << "\n" + << "write zeroes:\t" << write_zeroes_ << "\n"; } uint64_t @@ -503,10 +505,29 @@ block_cache::get_nr_blocks() const void block_cache::zero_block(block &b) { + write_zeroes_++; memset(b.data_, 0, block_size_ << SECTOR_SHIFT); b.mark_dirty(); } +void +block_cache::inc_hit_counter(unsigned flags) +{ + if (flags & (GF_ZERO | GF_DIRTY)) + write_hits_++; + else + read_hits_++; +} + +void +block_cache::inc_miss_counter(unsigned flags) +{ + if (flags & (GF_ZERO | GF_DIRTY)) + write_misses_++; + else + read_misses_++; +} + block_cache::block * block_cache::lookup_or_read_block(block_address index, unsigned flags, validator::ptr v) @@ -514,8 +535,11 @@ block_cache::lookup_or_read_block(block_address index, unsigned flags, block *b = hash_lookup(index); if (b) { - if (b->test_flags(BF_IO_PENDING)) + if (b->test_flags(BF_IO_PENDING)) { + inc_miss_counter(flags); wait_specific(*b); + } else + inc_hit_counter(flags); if (flags & GF_ZERO) zero_block(*b); @@ -530,6 +554,8 @@ block_cache::lookup_or_read_block(block_address index, unsigned flags, b->v_ = v; } else { + inc_miss_counter(flags); + b = new_block(index); if (b) { b->v_ = v; @@ -558,6 +584,7 @@ block_cache::get(block_address index, unsigned flags, validator::ptr v) if (b->ref_count_ && flags & (GF_DIRTY | GF_ZERO)) throw std::runtime_error("attempt to write lock block concurrently"); + // FIXME: this gets called even for new blocks hit(*b); if (!b->ref_count_) @@ -636,8 +663,9 @@ block_cache::prefetch(block_address index) check_index(index); block *b = hash_lookup(index); - if (!b) { + prefetches_++; + b = new_block(index); if (b) issue_read(*b); diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 24f7949..5b25e24 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -166,6 +166,9 @@ namespace bcache { void release(block_cache::block &block); void check_index(block_address index) const; + void inc_hit_counter(unsigned flags); + void inc_miss_counter(unsigned flags); + //-------------------------------- int fd_; @@ -201,6 +204,14 @@ namespace bcache { unsigned nr_buckets_; unsigned mask_; std::vector buckets_; + + // Stats + unsigned read_hits_; + unsigned read_misses_; + unsigned write_zeroes_; + unsigned write_hits_; + unsigned write_misses_; + unsigned prefetches_; }; } From fccc1dfcb0d8a6dd900342cf3fb0920116467632 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 5 Aug 2014 11:47:57 +0100 Subject: [PATCH 074/165] [thin_restore] Add a progress monitor --- Makefile.in | 2 + base/progress_monitor.cc | 75 +++++++++++++++++++++++++++++++ base/progress_monitor.h | 25 +++++++++++ thin-provisioning/thin_restore.cc | 22 ++++++++- thin-provisioning/xml_format.cc | 10 ++++- thin-provisioning/xml_format.h | 4 +- 6 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 base/progress_monitor.cc create mode 100644 base/progress_monitor.h diff --git a/Makefile.in b/Makefile.in index e65667d..9f4167d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -46,6 +46,7 @@ SOURCE=\ base/base64.cc \ base/endian_utils.cc \ base/error_state.cc \ + base/progress_monitor.cc \ \ caching/hint_array.cc \ caching/superblock.cc \ @@ -377,6 +378,7 @@ ERA_DUMP_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/progress_monitor.cc \ \ era/writeset_tree.cc \ era/era_detail.cc \ diff --git a/base/progress_monitor.cc b/base/progress_monitor.cc new file mode 100644 index 0000000..4e6792d --- /dev/null +++ b/base/progress_monitor.cc @@ -0,0 +1,75 @@ +#include "base/progress_monitor.h" + +#include + +//---------------------------------------------------------------- + +namespace { + using namespace std; + + class progress_bar : public base::progress_monitor { + public: + progress_bar(string const &title) + : title_(title), + progress_width_(50), + spinner_(0) { + + update_percent(0); + } + + void update_percent(unsigned p) { + unsigned nr_equals = max(progress_width_ * p / 100, 1); + unsigned nr_spaces = progress_width_ - nr_equals; + + cout << title_ << ": ["; + + for (unsigned i = 0; i < nr_equals - 1; i++) + cout << '='; + + if (nr_equals < progress_width_) + cout << '>'; + + for (unsigned i = 0; i < nr_spaces; i++) + cout << ' '; + + cout << "] " << spinner_char() << " " << p << "%\r" << flush; + + spinner_++; + } + + private: + char spinner_char() const { + char cs[] = {'|', '/', '-', '\\'}; + + unsigned index = spinner_ % sizeof(cs); + return cs[index]; + } + + std::string title_; + unsigned progress_width_; + unsigned spinner_; + }; + + class quiet_progress : public base::progress_monitor { + public: + void update_percent(unsigned p) { + } + }; + +} + +//---------------------------------------------------------------- + +base::progress_monitor::ptr +base::create_progress_bar(std::string const &title) +{ + return progress_monitor::ptr(new progress_bar(title)); +} + +base::progress_monitor::ptr +base::create_quiet_progress_monitor() +{ + return progress_monitor::ptr(new quiet_progress()); +} + +//---------------------------------------------------------------- diff --git a/base/progress_monitor.h b/base/progress_monitor.h new file mode 100644 index 0000000..29b3d36 --- /dev/null +++ b/base/progress_monitor.h @@ -0,0 +1,25 @@ +#ifndef BASE_PROGRESS_MONITOR_H +#define BASE_PROGRESS_MONITOR_H + +#include +#include + +//---------------------------------------------------------------- + +namespace base { + class progress_monitor { + public: + typedef boost::shared_ptr ptr; + + virtual ~progress_monitor() {} + + virtual void update_percent(unsigned) = 0; + }; + + progress_monitor::ptr create_progress_bar(std::string const &title); + progress_monitor::ptr create_quiet_progress_monitor(); +} + +//---------------------------------------------------------------- + +#endif diff --git a/thin-provisioning/thin_restore.cc b/thin-provisioning/thin_restore.cc index 2aa9173..86c7801 100644 --- a/thin-provisioning/thin_restore.cc +++ b/thin-provisioning/thin_restore.cc @@ -41,6 +41,24 @@ using namespace thin_provisioning; //---------------------------------------------------------------- namespace { + size_t get_file_length(string const &file) { + struct stat info; + int r; + + r = ::stat(file.c_str(), &info); + if (r) + throw runtime_error("Couldn't stat backup path"); + + return info.st_size; + } + + progress_monitor::ptr create_monitor() { + if (isatty(fileno(stdout))) + return create_progress_bar("Restoring"); + else + return create_quiet_progress_monitor(); + } + int restore(string const &backup_file, string const &dev) { try { // The block size gets updated by the restorer. @@ -49,7 +67,9 @@ namespace { check_file_exists(backup_file); ifstream in(backup_file.c_str(), ifstream::in); - parse_xml(in, restorer); + + progress_monitor::ptr monitor = create_monitor(); + parse_xml(in, restorer, get_file_length(backup_file), monitor); } catch (std::exception &e) { cerr << e.what() << endl; diff --git a/thin-provisioning/xml_format.cc b/thin-provisioning/xml_format.cc index 2767b9a..f868e2c 100644 --- a/thin-provisioning/xml_format.cc +++ b/thin-provisioning/xml_format.cc @@ -256,7 +256,8 @@ tp::create_xml_emitter(ostream &out) } void -tp::parse_xml(std::istream &in, emitter::ptr e) +tp::parse_xml(std::istream &in, emitter::ptr e, + size_t input_length, base::progress_monitor::ptr monitor) { XML_Parser parser = XML_ParserCreate(NULL); if (!parser) @@ -265,8 +266,10 @@ tp::parse_xml(std::istream &in, emitter::ptr e) XML_SetUserData(parser, e.get()); XML_SetElementHandler(parser, start_tag, end_tag); + size_t total = 0; + while (!in.eof()) { - char buffer[4096]; + char buffer[1024 * 1024]; in.read(buffer, sizeof(buffer)); size_t len = in.gcount(); int done = in.eof(); @@ -280,6 +283,9 @@ tp::parse_xml(std::istream &in, emitter::ptr e) << endl; throw runtime_error(out.str()); } + + total += len; + monitor->update_percent(total * 100 / input_length); } } diff --git a/thin-provisioning/xml_format.h b/thin-provisioning/xml_format.h index cf520e2..7cd7d4e 100644 --- a/thin-provisioning/xml_format.h +++ b/thin-provisioning/xml_format.h @@ -20,6 +20,7 @@ #define XML_FORMAT_H #include "emitter.h" +#include "base/progress_monitor.h" #include @@ -27,7 +28,8 @@ namespace thin_provisioning { emitter::ptr create_xml_emitter(std::ostream &out); - void parse_xml(std::istream &in, emitter::ptr e); + void parse_xml(std::istream &in, emitter::ptr e, + size_t input_length, base::progress_monitor::ptr p); } //---------------------------------------------------------------- From d00aef9219aa6f7a8b7f240b338649868a872a89 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 5 Aug 2014 12:10:34 +0100 Subject: [PATCH 075/165] [thini_restore] add a quiet option to turn off the progress monitor --- features/thin_restore.feature | 2 ++ thin-provisioning/thin_restore.cc | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/features/thin_restore.feature b/features/thin_restore.feature index d02ebb0..7f5b5be 100644 --- a/features/thin_restore.feature +++ b/features/thin_restore.feature @@ -17,6 +17,7 @@ Feature: thin_restore {-h|--help} {-i|--input} {-o|--output} + {-q|--quiet} {-V|--version} """ @@ -30,6 +31,7 @@ Feature: thin_restore {-h|--help} {-i|--input} {-o|--output} + {-q|--quiet} {-V|--version} """ diff --git a/thin-provisioning/thin_restore.cc b/thin-provisioning/thin_restore.cc index 86c7801..d82eae1 100644 --- a/thin-provisioning/thin_restore.cc +++ b/thin-provisioning/thin_restore.cc @@ -52,14 +52,14 @@ namespace { return info.st_size; } - progress_monitor::ptr create_monitor() { - if (isatty(fileno(stdout))) + progress_monitor::ptr create_monitor(bool quiet) { + if (!quiet && isatty(fileno(stdout))) return create_progress_bar("Restoring"); else return create_quiet_progress_monitor(); } - int restore(string const &backup_file, string const &dev) { + int restore(string const &backup_file, string const &dev, bool quiet) { try { // The block size gets updated by the restorer. metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0)); @@ -68,7 +68,7 @@ namespace { check_file_exists(backup_file); ifstream in(backup_file.c_str(), ifstream::in); - progress_monitor::ptr monitor = create_monitor(); + progress_monitor::ptr monitor = create_monitor(quiet); parse_xml(in, restorer, get_file_length(backup_file), monitor); } catch (std::exception &e) { @@ -85,6 +85,7 @@ namespace { << " {-h|--help}" << endl << " {-i|--input} " << endl << " {-o|--output} " << endl + << " {-q|--quiet}" << endl << " {-V|--version}" << endl; } } @@ -93,12 +94,14 @@ int main(int argc, char **argv) { int c; char const *prog_name = basename(argv[0]); - const char *shortopts = "hi:o:V"; + const char *shortopts = "hi:o:qV"; string input, output; + bool quiet = false; const struct option longopts[] = { { "help", no_argument, NULL, 'h'}, { "input", required_argument, NULL, 'i' }, { "output", required_argument, NULL, 'o'}, + { "quiet", no_argument, NULL, 'q'}, { "version", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 } }; @@ -117,6 +120,10 @@ int main(int argc, char **argv) output = optarg; break; + case 'q': + quiet = true; + break; + case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; @@ -144,7 +151,7 @@ int main(int argc, char **argv) return 1; } - return restore(input, output); + return restore(input, output, quiet); } //---------------------------------------------------------------- From ea62f6e06ee3f1b7765178a7ae8805f2f5b7f5f3 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 5 Aug 2014 12:21:06 +0100 Subject: [PATCH 076/165] add changes file --- CHANGES | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 CHANGES diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..9871c86 --- /dev/null +++ b/CHANGES @@ -0,0 +1,15 @@ +v0.4 +==== + +- All tools switch to using libaio. This gives a large performance + boost, especially to the write focused tools like thin_restore. + +- Added a progress monitor to thin_restore + +- Added a --quiet/-q option to thin_restore to turn off the progress bar + +- Removed variable hint size support from cache tools. The kernel + still only supports a fixed 32bit width. This will have a side + effect of reducing the executable sizes due to less template + instatiation. + From bf138c2a50a3aee7ea748a9cd56bdf9f18f22880 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 5 Aug 2014 14:50:35 +0100 Subject: [PATCH 077/165] update gem lock file --- Gemfile.lock | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 069c068..ab69a42 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,30 +1,32 @@ GEM remote: https://rubygems.org/ specs: - aruba (0.5.3) + aruba (0.6.0) childprocess (>= 0.3.6) cucumber (>= 1.1.1) rspec-expectations (>= 2.7.0) builder (3.2.2) - childprocess (0.3.9) + childprocess (0.5.3) ffi (~> 1.0, >= 1.0.11) - cucumber (1.3.8) + cucumber (1.3.16) builder (>= 2.1.2) diff-lcs (>= 1.1.3) - gherkin (~> 2.12.1) + gherkin (~> 2.12) multi_json (>= 1.7.5, < 2.0) - multi_test (>= 0.0.2) - diff-lcs (1.2.4) - ejt_command_line (0.0.2) - ffi (1.9.0) + multi_test (>= 0.1.1) + diff-lcs (1.2.5) + ejt_command_line (0.0.4) + ffi (1.9.3) gherkin (2.12.2) multi_json (~> 1.3) - multi_json (1.8.2) - multi_test (0.0.2) - rspec-expectations (2.14.3) - diff-lcs (>= 1.1.3, < 2.0) - thinp_xml (0.0.12) - ejt_command_line (= 0.0.2) + multi_json (1.10.1) + multi_test (0.1.1) + rspec-expectations (3.0.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.0.0) + rspec-support (3.0.3) + thinp_xml (0.0.15) + ejt_command_line (>= 0.0.2) PLATFORMS ruby From 48c6dc07627b44d676ec1b436e854087efbffd33 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 5 Aug 2014 14:53:03 +0100 Subject: [PATCH 078/165] Flush the block cache stats when they're complete --- block-cache/block_cache.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 9fdcbe3..574973d 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -493,7 +493,7 @@ block_cache::~block_cache() << "read misses:\t" << read_misses_ << "\n" << "write hits:\t" << write_hits_ << "\n" << "write misses:\t" << write_misses_ << "\n" - << "write zeroes:\t" << write_zeroes_ << "\n"; + << "write zeroes:\t" << write_zeroes_ << std::endl; } uint64_t From 460c0ef8d225c04bc3c812140a376aac1d9ab2d2 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 5 Aug 2014 15:17:43 +0100 Subject: [PATCH 079/165] era_check man page --- man8/era_check.8 | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 man8/era_check.8 diff --git a/man8/era_check.8 b/man8/era_check.8 new file mode 100644 index 0000000..cdf9ec6 --- /dev/null +++ b/man8/era_check.8 @@ -0,0 +1,57 @@ +.TH ERA_CHECK 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*- +.SH NAME +era_check \- validate era metadata on device or file + +.SH SYNOPSIS +.B era_check +.RB [ options ] +.I {device|file} + +.SH DESCRIPTION +.B era_check +checks era metadata created by +the device-mapper era target on a +.I device +or +.I file. + +.SH OPTIONS +.IP "\fB\-q, \-\-quiet\fP" +Suppress output messages, return only exit code. + +.IP "\fB\-h, \-\-help\fP" +Print help and exit. + +.IP "\fB\-V, \-\-version\fP" +Output version information and exit. + +.IP "\fB\-\-super\-block\-only\fP" +Only check the superblock is present. + +.B era_check +will return a non-zero exit code if it finds a fatal +error. If any errors are discovered use +.B era_repair +to correct. + +.SH EXAMPLE +Analyses thin provisioning metadata on logical volume +/dev/vg/metadata: +.sp +.B era_check /dev/vg/metadata + +The device may not be actively used by the target +when running. + +.SH DIAGNOSTICS +.B era_check +returns an exit code of 0 for success or 1 for error. + +.SH SEE ALSO +.B era_dump(8) +.B era_repair(8) +.B era_restore(8) +.B era_invalidate(8) + +.SH AUTHOR +Joe Thornber From a2d6e86c5148a24f252010d3ecef14b9a68c0f48 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 5 Aug 2014 15:43:54 +0100 Subject: [PATCH 080/165] Add era_dump man page. --- man8/era_dump.8 | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 man8/era_dump.8 diff --git a/man8/era_dump.8 b/man8/era_dump.8 new file mode 100644 index 0000000..275ad97 --- /dev/null +++ b/man8/era_dump.8 @@ -0,0 +1,56 @@ +.TH ERA_DUMP 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*- +.SH NAME +era_dump \- dump era metadata from device or file to standard output + +.SH SYNOPSIS +.B era_dump +.RB [options] +.I {device|file} + +.SH DESCRIPTION +.B era_dump +dumps binary era metadata created by the device-mapper +era target on a +.I device +or +.I file +to standard output for +analysis or postprocessing in XML format. +XML formated metadata can be fed into era_restore (see +.BR era_restore(8) ) +in order to put it back onto a metadata +.I device +(to process by the device-mapper target) or +.I file. + +.IP "\fB\-r, \-\-repair\fP". +Repair the metadata whilst dumping it. + +.IP "\fB\-h, \-\-help\fP". +Print help and exit. + +.IP "\fB\-V, \-\-version\fP". +Output version information and exit. + +.IP "\fB\-\-logical\fP". +Fold any unprocessed write sets into the final era array. You +probably want to do this if you're intending to process the results as +it simplifies the XML. + +.SH EXAMPLES +Dumps era metadata on logical volume /dev/vg/metadata +to standard output in XML format: +.sp +.B era_dump /dev/vg/metadata + +.SH DIAGNOSTICS +.B era_dump +returns an exit code of 0 for success or 1 for error. + +.SH SEE ALSO +.B era_check(8) +.B era_repair(8) +.B era_restore(8) +.B era_invalidate(8) +.SH AUTHOR +Joe Thornber From 41ade2a2593ae30cd2a10e51da5b44554ed56305 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 10:32:39 +0100 Subject: [PATCH 081/165] Add era_invalidate manpage --- man8/era_invalidate.8 | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 man8/era_invalidate.8 diff --git a/man8/era_invalidate.8 b/man8/era_invalidate.8 new file mode 100644 index 0000000..8c10728 --- /dev/null +++ b/man8/era_invalidate.8 @@ -0,0 +1,46 @@ +.TH ERA_INVALIDATE 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*- +.SH NAME +era_invalidate \- Provide a list of blocks that have changed since a particular era. + +.SH SYNOPSIS +.B era_invalidate +.RB [ options ] +.I {device|file} + +.SH DESCRIPTION +.B era_invalidate +Examines era metadata and lists blocks that may have changed since a given era. + +.SH OPTIONS +.IP "\fB\-h, \-\-help\fP" +Print help and exit. + +.IP "\fB\-V, \-\-version\fP" +Output version information and exit. + +.IP "\fB\-o \fP" +Write output to a file rather than +.B stdout +. + +.SH EXAMPLE +List the blocks that may have been written since the beginning of era +13 on the metadata device /dev/vg/metadata. +.sp +.B era_invalidate --written-since 13 /dev/vg/metadata + +The device may not be actively used by the target +when running. + +.SH DIAGNOSTICS +.B era_invalidate +returns an exit code of 0 for success or 1 for error (eg, metadata corruption). + +.SH SEE ALSO +.B era_check(8), +.B era_dump(8), +.B era_repair(8), +.B era_restore(8) + +.SH AUTHOR +Joe Thornber From bdec4b353c0008eaa9dedf43047b2b834af87894 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 10:33:23 +0100 Subject: [PATCH 082/165] tweak some man pages --- man8/era_check.8 | 2 +- man8/thin_check.8 | 9 ++++----- man8/thin_restore.8 | 3 +++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/man8/era_check.8 b/man8/era_check.8 index cdf9ec6..e8610eb 100644 --- a/man8/era_check.8 +++ b/man8/era_check.8 @@ -35,7 +35,7 @@ error. If any errors are discovered use to correct. .SH EXAMPLE -Analyses thin provisioning metadata on logical volume +Analyse thin provisioning metadata on logical volume /dev/vg/metadata: .sp .B era_check /dev/vg/metadata diff --git a/man8/thin_check.8 b/man8/thin_check.8 index a8dc81b..0981d77 100644 --- a/man8/thin_check.8 +++ b/man8/thin_check.8 @@ -1,6 +1,6 @@ .TH THIN_CHECK 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*- .SH NAME -thin_check \- repair thin provisioning metadata on device or file +thin_check \- validate thin provisioning metadata on device or file .SH SYNOPSIS .B thin_check @@ -35,19 +35,18 @@ metadata. .IP "\fB\-\-ignore\-non\-fatal\-errors\fP" .B thin_check will only return a non-zero exit code if it finds a fatal -error. An example of a on fatal error is an incorrect data block +error. An example of a non fatal error is an incorrect data block reference count causing a block to be considered allocated when it in fact isn't. Ignoring errors for a long time is not advised, you really should be using thin_repair to fix them. .SH EXAMPLE -Analyses and repairs thin provisioning metadata on logical volume +Analyses thin provisioning metadata on logical volume /dev/vg/metadata: .sp .B thin_check /dev/vg/metadata -The device may not be actively used by the target -when running. +The device must not be actively used by the target when running. .SH DIAGNOSTICS .B thin_check diff --git a/man8/thin_restore.8 b/man8/thin_restore.8 index dc7eb90..17b2e6e 100644 --- a/man8/thin_restore.8 +++ b/man8/thin_restore.8 @@ -24,6 +24,9 @@ If restored to a metadata .I device , the metadata can be processed by the device-mapper target. +.IP "\fB\-q, \-\-quiet\fP" +Suppress output messages, return only exit code. + .IP "\fB\-i, \-\-input\fP \fI{device|file}\fP" Input file or device with metadata. From 672582b2a85d9a0be754b3515a5564bcd03820bb Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 11:10:22 +0100 Subject: [PATCH 083/165] Install era manpages --- Makefile.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.in b/Makefile.in index 9f4167d..14eafa1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -484,6 +484,9 @@ install: $(PROGRAMS) $(INSTALL_DATA) man8/thin_restore.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_rmap.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_metadata_size.8 $(MANPATH)/man8 + $(INSTALL_DATA) man8/era_check.8 $(MANPATH)/man8 + $(INSTALL_DATA) man8/era_dump.8 $(MANPATH)/man8 + $(INSTALL_DATA) man8/era_invalidate.8 $(MANPATH)/man8 .PHONY: install From aa08c8393aa0236b67ac1b90c52137f94e3b2ed1 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 11:14:15 +0100 Subject: [PATCH 084/165] Man pages were being installed in a v. funny place. --- Makefile.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 14eafa1..4c13737 100644 --- a/Makefile.in +++ b/Makefile.in @@ -122,7 +122,8 @@ LIBEXPAT:=-lexpat INSTALL:=@INSTALL@ PREFIX:=@prefix@ BINDIR:=$(DESTDIR)$(PREFIX)/sbin -MANPATH:=$(DESTDIR)$(MANDIR) +DATADIR:=$(DESTDIR)$(PREFIX)/share +MANPATH:=$(DATADIR)/man vpath %.cc $(TOP_DIR) From a75a2118a0c70c70269f9e4a8c508155f1ba746b Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 11:15:18 +0100 Subject: [PATCH 085/165] era_invalidate: update help to reflect --metadata-snapshot --- era/era_invalidate.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/era/era_invalidate.cc b/era/era_invalidate.cc index aba7db8..6f109b3 100644 --- a/era/era_invalidate.cc +++ b/era/era_invalidate.cc @@ -178,10 +178,11 @@ namespace { } void usage(ostream &out, string const &cmd) { - out << "Usage: " << cmd << " [options] --written-since {device|file}" << endl - << "Options:" << endl - << " {-h|--help}" << endl - << " {-o }" << endl + out << "Usage: " << cmd << " [options] --written-since {device|file}\n" + << "Options:\n" + << " {-h|--help}\n" + << " {-o }\n" + << " {--metadata-snapshot}\n" << " {-V|--version}" << endl; } } From d8f678b0797ddc99a18cd3710aa1e2951cdc72e5 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 15:20:40 +0100 Subject: [PATCH 086/165] Rename configure.in to configure.ac to stop auto tools bleating --- configure.in => configure.ac | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename configure.in => configure.ac (100%) diff --git a/configure.in b/configure.ac similarity index 100% rename from configure.in rename to configure.ac From 1d38b390b5e1559e0465f29fa98457fd566e1285 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 15:29:02 +0100 Subject: [PATCH 087/165] Factor common code out of the xml_format files. --- Makefile.in | 7 +++++ base/xml_utils.cc | 25 +++++++++++++++++ base/xml_utils.h | 42 +++++++++++++++++++++++++++++ caching/xml_format.cc | 48 +++------------------------------ thin-provisioning/xml_format.cc | 44 +++--------------------------- 5 files changed, 81 insertions(+), 85 deletions(-) create mode 100644 base/xml_utils.cc create mode 100644 base/xml_utils.h diff --git a/Makefile.in b/Makefile.in index 4c13737..eb6ecde 100644 --- a/Makefile.in +++ b/Makefile.in @@ -47,6 +47,7 @@ SOURCE=\ base/endian_utils.cc \ base/error_state.cc \ base/progress_monitor.cc \ + base/xml_utils.cc \ \ caching/hint_array.cc \ caching/superblock.cc \ @@ -175,6 +176,7 @@ THIN_CHECK_SOURCE=\ \ base/error_state.cc \ base/endian_utils.cc \ + base/xml_utils.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ @@ -198,6 +200,7 @@ THIN_DELTA_SOURCE=\ \ base/error_state.cc \ base/endian_utils.cc \ + base/xml_utils.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ @@ -287,6 +290,7 @@ CACHE_CHECK_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/xml_utils.cc \ \ persistent-data/checksum.cc \ persistent-data/error_set.cc \ @@ -348,6 +352,7 @@ ERA_CHECK_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/xml_utils.cc \ \ era/writeset_tree.cc \ era/era_detail.cc \ @@ -380,6 +385,7 @@ ERA_DUMP_SOURCE=\ base/error_state.cc \ base/endian_utils.cc \ base/progress_monitor.cc \ + base/xml_utils.cc \ \ era/writeset_tree.cc \ era/era_detail.cc \ @@ -413,6 +419,7 @@ ERA_INVALIDATE_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/xml_utils.cc \ \ era/writeset_tree.cc \ era/era_detail.cc \ diff --git a/base/xml_utils.cc b/base/xml_utils.cc new file mode 100644 index 0000000..cc60f2d --- /dev/null +++ b/base/xml_utils.cc @@ -0,0 +1,25 @@ +#include "xml_utils.h" + +//---------------------------------------------------------------- + +void +xml_utils::build_attributes(attributes &a, char const **attr) +{ + while (*attr) { + char const *key = *attr; + + attr++; + if (!*attr) { + ostringstream out; + out << "No value given for xml attribute: " << key; + throw runtime_error(out.str()); + } + + char const *value = *attr; + a.insert(make_pair(string(key), string(value))); + attr++; + } +} + + +//---------------------------------------------------------------- diff --git a/base/xml_utils.h b/base/xml_utils.h new file mode 100644 index 0000000..279f43f --- /dev/null +++ b/base/xml_utils.h @@ -0,0 +1,42 @@ +#ifndef BASE_XML_UTILS_H +#define BASE_XML_UTILS_H + +#include +#include +#include + +using namespace std; + +//---------------------------------------------------------------- + +namespace xml_utils { + typedef std::map attributes; + + void build_attributes(attributes &a, char const **attr); + + template + T get_attr(attributes const &attr, string const &key) { + attributes::const_iterator it = attr.find(key); + if (it == attr.end()) { + ostringstream out; + out << "could not find attribute: " << key; + throw runtime_error(out.str()); + } + + return boost::lexical_cast(it->second); + } + + template + boost::optional get_opt_attr(attributes const &attr, string const &key) { + typedef boost::optional rtype; + attributes::const_iterator it = attr.find(key); + if (it == attr.end()) + return rtype(); + + return rtype(boost::lexical_cast(it->second)); + } +} + +//---------------------------------------------------------------- + +#endif diff --git a/caching/xml_format.cc b/caching/xml_format.cc index 84d6fc2..4eb1f14 100644 --- a/caching/xml_format.cc +++ b/caching/xml_format.cc @@ -1,6 +1,8 @@ +#include "caching/xml_format.h" + #include "base/base64.h" #include "base/indented_stream.h" -#include "caching/xml_format.h" +#include "base/xml_utils.h" #include #include @@ -8,6 +10,7 @@ using namespace caching; using namespace persistent_data; using namespace std; +using namespace xml_utils; //---------------------------------------------------------------- @@ -116,49 +119,6 @@ namespace { //-------------------------------- // Parser //-------------------------------- - - // FIXME: factor out common code with thinp one - typedef std::map attributes; - - void build_attributes(attributes &a, char const **attr) { - while (*attr) { - char const *key = *attr; - - attr++; - if (!*attr) { - ostringstream out; - out << "No value given for xml attribute: " << key; - throw runtime_error(out.str()); - } - - char const *value = *attr; - a.insert(make_pair(string(key), string(value))); - attr++; - } - } - - template - T get_attr(attributes const &attr, string const &key) { - attributes::const_iterator it = attr.find(key); - if (it == attr.end()) { - ostringstream out; - out << "could not find attribute: " << key; - throw runtime_error(out.str()); - } - - return boost::lexical_cast(it->second); - } - - template - boost::optional get_opt_attr(attributes const &attr, string const &key) { - typedef boost::optional rtype; - attributes::const_iterator it = attr.find(key); - if (it == attr.end()) - return rtype(); - - return rtype(boost::lexical_cast(it->second)); - } - void parse_superblock(emitter *e, attributes const &attr) { e->begin_superblock(get_attr(attr, "uuid"), get_attr(attr, "block_size"), diff --git a/thin-provisioning/xml_format.cc b/thin-provisioning/xml_format.cc index f868e2c..488fac5 100644 --- a/thin-provisioning/xml_format.cc +++ b/thin-provisioning/xml_format.cc @@ -17,7 +17,9 @@ // . #include "xml_format.h" + #include "base/indented_stream.h" +#include "base/xml_utils.h" #include #include @@ -30,6 +32,7 @@ using namespace std; using namespace thin_provisioning; +using namespace xml_utils; namespace tp = thin_provisioning; @@ -134,47 +137,6 @@ namespace { //------------------------------------------------ // XML parser //------------------------------------------------ - typedef std::map attributes; - - void build_attributes(attributes &a, char const **attr) { - while (*attr) { - char const *key = *attr; - - attr++; - if (!*attr) { - ostringstream out; - out << "No value given for xml attribute: " << key; - throw runtime_error(out.str()); - } - - char const *value = *attr; - a.insert(make_pair(string(key), string(value))); - attr++; - } - } - - template - T get_attr(attributes const &attr, string const &key) { - attributes::const_iterator it = attr.find(key); - if (it == attr.end()) { - ostringstream out; - out << "could not find attribute: " << key; - throw runtime_error(out.str()); - } - - return boost::lexical_cast(it->second); - } - - template - boost::optional get_opt_attr(attributes const &attr, string const &key) { - typedef boost::optional rtype; - attributes::const_iterator it = attr.find(key); - if (it == attr.end()) - return rtype(); - - return rtype(boost::lexical_cast(it->second)); - } - void parse_superblock(emitter *e, attributes const &attr) { e->begin_superblock(get_attr(attr, "uuid"), get_attr(attr, "time"), From 7e2ea38534474f3e3aa03aa641982ed068f6ba77 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 16:27:18 +0100 Subject: [PATCH 088/165] [configure.ac] Make sure libaio.h is present, and remove deps for a couple of boost headers we're no longer using. --- configure.ac | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8f25002..f15906b 100644 --- a/configure.ac +++ b/configure.ac @@ -48,10 +48,9 @@ AC_PREFIX_DEFAULT(/usr) AC_CHECK_HEADERS([expat.h \ iostream \ + libaio.h \ boost/bind.hpp \ boost/crc.hpp \ - boost/intrusive/circular_list_algorithms.hpp \ - boost/intrusive/rbtree_algorithms.hpp \ boost/lexical_cast.hpp \ boost/noncopyable.hpp \ boost/optional.hpp \ From efe597f097b6fede5db50adee7baec6dc8174840 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 16:29:50 +0100 Subject: [PATCH 089/165] [README] Add dep for libaio --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 66c7199..d23dae5 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Requirements A C++ compiler that supports the c++11 standard (eg, g++). The [Boost C++ library](http://www.boost.org/). The [expat](http://expat.sourceforge.net/) xml parser library (version 1). +The libaio library (note this is not the same as the aio library that you get by linking -lrt) make, autoconf etc. There are more requirements for testing, detailed below. From 3e5c02459cc71c6e377ca7a9046097bc165e0210 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 16:30:26 +0100 Subject: [PATCH 090/165] whitespace --- caching/xml_format.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/caching/xml_format.cc b/caching/xml_format.cc index 4eb1f14..d36d7b0 100644 --- a/caching/xml_format.cc +++ b/caching/xml_format.cc @@ -262,7 +262,6 @@ caching::parse_xml(istream &in, emitter::ptr e) throw runtime_error(out.str()); } } - } //---------------------------------------------------------------- From 8f732411197151af38e8d8a8e2d41675a7d6b348 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 6 Aug 2014 16:30:38 +0100 Subject: [PATCH 091/165] [era] parser for the XML format --- Makefile.in | 4 +-- era/xml_format.cc | 82 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index eb6ecde..ab9837c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -411,7 +411,7 @@ ERA_DUMP_OBJECTS=$(subst .cc,.o,$(ERA_DUMP_SOURCE)) era_dump: $(ERA_DUMP_OBJECTS) era/era_dump.o @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) ERA_INVALIDATE_SOURCE=\ block-cache/block_cache.cc \ @@ -445,7 +445,7 @@ ERA_INVALIDATE_OBJECTS=$(subst .cc,.o,$(ERA_INVALIDATE_SOURCE)) era_invalidate: $(ERA_INVALIDATE_OBJECTS) era/era_invalidate.o @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) #---------------------------------------------------------------- diff --git a/era/xml_format.cc b/era/xml_format.cc index 0878410..0112aae 100644 --- a/era/xml_format.cc +++ b/era/xml_format.cc @@ -1,11 +1,15 @@ #include "era/xml_format.h" #include "base/indented_stream.h" +#include "base/xml_utils.h" + +#include using namespace boost; using namespace era; using namespace persistent_data; using namespace std; +using namespace xml_utils; //---------------------------------------------------------------- @@ -75,6 +79,56 @@ namespace { private: indented_stream out_; }; + + //-------------------------------- + // Parser + //-------------------------------- + void start_tag(void *data, char const *el, char const **attr) { + emitter *e = static_cast(data); + attributes a; + + build_attributes(a, attr); + + if (!strcmp(el, "superblock")) + e->begin_superblock(get_attr(a, "uuid"), + get_attr(a, "block_size"), + get_attr(a, "nr_blocks"), + get_attr(a, "current_era")); + + else if (!strcmp(el, "writeset")) + e->begin_writeset(get_attr(a, "era"), + get_attr(a, "nr_bits")); + + else if (!strcmp(el, "bit")) + e->writeset_bit(get_attr(a, "bit"), + get_attr(a, "value")); + + else if (!strcmp(el, "era_array")) + e->begin_era_array(); + + else if (!strcmp(el, "era")) + e->era(get_attr(a, "block"), + get_attr(a, "era")); + + else + throw runtime_error("unknown tag type"); + } + + void end_tag(void *data, const char *el) { + emitter *e = static_cast(data); + + if (!strcmp(el, "superblock")) + e->end_superblock(); + + else if (!strcmp(el, "writeset")) + e->end_writeset(); + + else if (!strcmp(el, "era_array")) + e->end_era_array(); + + else + throw runtime_error("unknown tag type"); + } } //---------------------------------------------------------------- @@ -85,4 +139,32 @@ era::create_xml_emitter(std::ostream &out) return emitter::ptr(new xml_emitter(out)); } +void +era::parse_xml(std::istream &in, emitter::ptr e) +{ + XML_Parser parser = XML_ParserCreate(NULL); + if (!parser) + throw runtime_error("couldn't create xml parser"); + + XML_SetUserData(parser, e.get()); + XML_SetElementHandler(parser, start_tag, end_tag); + + while (!in.eof()) { + char buffer[4096]; + in.read(buffer, sizeof(buffer)); + size_t len = in.gcount(); + int done = in.eof(); + + if (!XML_Parse(parser, buffer, len, done)) { + ostringstream out; + out << "Parse error at line " + << XML_GetCurrentLineNumber(parser) + << ":\n" + << XML_ErrorString(XML_GetErrorCode(parser)) + << endl; + throw runtime_error(out.str()); + } + } +} + //---------------------------------------------------------------- From 1d12d0ff8ea9d12b616e8f8e77c92fb3328457d9 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 7 Aug 2014 15:42:10 +0100 Subject: [PATCH 092/165] remove empty destructor --- caching/restore_emitter.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/caching/restore_emitter.cc b/caching/restore_emitter.cc index fba7e60..12da592 100644 --- a/caching/restore_emitter.cc +++ b/caching/restore_emitter.cc @@ -17,9 +17,6 @@ namespace { clean_shutdown_(clean_shutdown) { } - virtual ~restorer() { - } - virtual void begin_superblock(std::string const &uuid, pd::block_address block_size, pd::block_address nr_cache_blocks, From 283ab4ad995fda4f755a6d35721ef543162df6bc Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 7 Aug 2014 15:43:01 +0100 Subject: [PATCH 093/165] first drop of era_restore --- Makefile.in | 38 +++++++++++++- era/era_detail.h | 12 +++-- era/era_restore.cc | 115 +++++++++++++++++++++++++++++++++++++++++ era/metadata.cc | 34 ++++++++++++ era/metadata.h | 7 ++- era/restore_emitter.cc | 107 ++++++++++++++++++++++++++++++++++++++ era/restore_emitter.h | 15 ++++++ era/superblock.h | 2 +- 8 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 era/era_restore.cc create mode 100644 era/restore_emitter.cc create mode 100644 era/restore_emitter.h diff --git a/Makefile.in b/Makefile.in index ab9837c..8d0d53a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -63,6 +63,7 @@ SOURCE=\ era/writeset_tree.cc \ era/metadata.cc \ era/metadata_dump.cc \ + era/restore_emitter.cc \ era/xml_format.cc \ \ persistent-data/checksum.cc \ @@ -443,7 +444,42 @@ ERA_INVALIDATE_SOURCE=\ ERA_INVALIDATE_OBJECTS=$(subst .cc,.o,$(ERA_INVALIDATE_SOURCE)) -era_invalidate: $(ERA_INVALIDATE_OBJECTS) era/era_invalidate.o +era_invalidate: $(ERA_INVALIDATE_OBJECTS) era/era_invalidate.o + @echo " [LD] $@" + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) + +ERA_RESTORE_SOURCE=\ + block-cache/block_cache.cc \ + \ + base/base64.cc \ + base/error_state.cc \ + base/endian_utils.cc \ + base/xml_utils.cc \ + \ + era/writeset_tree.cc \ + era/era_detail.cc \ + era/era_array.cc \ + era/metadata.cc \ + era/metadata_dump.cc \ + era/restore_emitter.cc \ + era/superblock.cc \ + era/xml_format.cc \ + \ + persistent-data/checksum.cc \ + persistent-data/error_set.cc \ + persistent-data/file_utils.cc \ + persistent-data/hex_dump.cc \ + persistent-data/data-structures/btree.cc \ + persistent-data/data-structures/bitset.cc \ + persistent-data/space_map.cc \ + persistent-data/space-maps/disk.cc \ + persistent-data/space-maps/recursive.cc \ + persistent-data/space-maps/careful_alloc.cc \ + persistent-data/transaction_manager.cc \ + +ERA_RESTORE_OBJECTS=$(subst .cc,.o,$(ERA_RESTORE_SOURCE)) + +era_restore: $(ERA_RESTORE_OBJECTS) era/era_restore.o @echo " [LD] $@" $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) diff --git a/era/era_detail.h b/era/era_detail.h index 4269afd..80961d5 100644 --- a/era/era_detail.h +++ b/era/era_detail.h @@ -22,16 +22,22 @@ namespace era { uint64_t writeset_root; }; - // FIXME: implement struct era_detail_ref_counter { - era_detail_ref_counter(persistent_data::transaction_manager::ptr tm) { + era_detail_ref_counter(persistent_data::transaction_manager::ptr tm) + : tm_(tm) { } - void inc(persistent_data::block_address b) { + void inc(era_detail const &d) { + tm_->get_sm()->inc(d.writeset_root); } void dec(persistent_data::block_address b) { + // I don't think we ever do this in the tools + throw std::runtime_error("not implemented"); } + + private: + persistent_data::transaction_manager::ptr tm_; }; struct era_detail_traits { diff --git a/era/era_restore.cc b/era/era_restore.cc new file mode 100644 index 0000000..56618ac --- /dev/null +++ b/era/era_restore.cc @@ -0,0 +1,115 @@ +#include "version.h" + +#include "era/metadata.h" +#include "era/restore_emitter.h" +#include "era/xml_format.h" +#include "persistent-data/file_utils.h" + +#include +#include +#include +#include +#include +#include + +using namespace boost; +using namespace caching; +using namespace persistent_data; +using namespace std; + +//---------------------------------------------------------------- + +namespace { + struct flags { + optional input; + optional output; + }; + + int restore(flags const &fs) { + try { + block_manager<>::ptr bm = open_bm(*fs.output, block_manager<>::READ_WRITE); + metadata::ptr md(new metadata(bm, metadata::CREATE)); + emitter::ptr restorer = create_restore_emitter(md); + + check_file_exists(*fs.input); + ifstream in(fs.input->c_str(), ifstream::in); + parse_xml(in, restorer); + + } catch (std::exception &e) { + cerr << e.what() << endl; + return 1; + } + + return 0; + } + + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options]" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-i|--input} " << endl + << " {-o|--output} " << endl + << " {-V|--version}" << endl + << endl; + } +} + +int main(int argc, char **argv) +{ + int c; + flags fs; + char const *prog_name = basename(argv[0]); + char const *short_opts = "hi:o:V"; + option const long_opts[] = { + { "help", no_argument, NULL, 'h'}, + { "input", required_argument, NULL, 'i' }, + { "output", required_argument, NULL, 'o'}, + { "version", no_argument, NULL, 'V'}, + { NULL, no_argument, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + switch(c) { + case 'h': + usage(cout, prog_name); + return 0; + + case 'i': + fs.input = optional(string(optarg)); + break; + + case 'o': + fs.output = optional(string(optarg)); + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr, prog_name); + return 1; + } + } + + if (argc != optind) { + usage(cerr, prog_name); + return 1; + } + + if (!fs.input) { + cerr << "No input file provided." << endl << endl; + usage(cerr, prog_name); + return 1; + } + + if (!fs.output) { + cerr << "No output file provided." << endl << endl; + usage(cerr, prog_name); + return 1; + } + + return restore(fs); +} + +//---------------------------------------------------------------- diff --git a/era/metadata.cc b/era/metadata.cc index d344b6c..d3535d8 100644 --- a/era/metadata.cc +++ b/era/metadata.cc @@ -53,4 +53,38 @@ metadata::open_metadata(block_manager<>::ptr bm, block_address loc) sb_.nr_blocks)); } +void +metadata::commit() +{ + commit_space_map(); + commit_writesets(); + commit_era_array(); + commit_superblock(); +} + +void +metadata::commit_space_map() +{ + metadata_sm_->commit(); + metadata_sm_->copy_root(&sb_.metadata_space_map_root, sizeof(sb_.metadata_space_map_root)); +} + +void +metadata::commit_writesets() +{ + sb_.writeset_tree_root = writeset_tree_->get_root(); +} + +void +metadata::commit_era_array() +{ + sb_.era_array_root = era_array_->get_root(); +} + +void +metadata::commit_superblock() +{ + write_superblock(tm_->get_bm(), sb_); +} + //---------------------------------------------------------------- diff --git a/era/metadata.h b/era/metadata.h index ea3a132..a88a1e9 100644 --- a/era/metadata.h +++ b/era/metadata.h @@ -29,7 +29,7 @@ namespace era { metadata(block_manager<>::ptr bm, open_type ot); metadata(block_manager<>::ptr bm, block_address metadata_snap); - void commit(bool clean_shutdown = true); + void commit(); typedef persistent_data::transaction_manager tm; tm::ptr tm_; @@ -41,6 +41,11 @@ namespace era { private: void open_metadata(block_manager<>::ptr bm, block_address loc = SUPERBLOCK_LOCATION); + + void commit_space_map(); + void commit_writesets(); + void commit_era_array(); + void commit_superblock(); }; }; diff --git a/era/restore_emitter.cc b/era/restore_emitter.cc new file mode 100644 index 0000000..aba16c0 --- /dev/null +++ b/era/restore_emitter.cc @@ -0,0 +1,107 @@ +#include "era/restore_emitter.h" + +#include "era/superblock.h" + +using namespace era; +using namespace persistent_data; + +//---------------------------------------------------------------- + +namespace { + class restorer : public emitter { + public: + restorer(metadata &md) + : md_(md), + in_superblock_(false), + in_writeset_(false), + in_era_array_(false) { + } + + virtual void begin_superblock(std::string const &uuid, + uint32_t data_block_size, + pd::block_address nr_blocks, + uint32_t current_era) { + superblock &sb = md_.sb_; + memcpy(sb.uuid, reinterpret_cast<__u8 const *>(uuid.c_str()), + min(sizeof(sb.uuid), uuid.length())); + sb.data_block_size = data_block_size; + sb.nr_blocks = nr_blocks; + sb.current_era = current_era; + + nr_blocks = nr_blocks; + } + + virtual void end_superblock() { + if (!in_superblock_) + throw runtime_error("missing superblock"); + + md_.commit(); + } + + virtual void begin_writeset(uint32_t era, uint32_t nr_bits) { + if (!in_superblock_) + throw runtime_error("missing superblock"); + + in_writeset_ = true; + era_ = era; + + bits_.reset(new bitset(md_.tm_)); + bits_->grow(nr_bits, false); + } + + virtual void writeset_bit(uint32_t bit, bool value) { + bits_->set(bit, value); + } + + virtual void end_writeset() { + in_writeset_ = false; + + era_detail e; + e.nr_bits = bits_->get_nr_bits(); + e.writeset_root = bits_->get_root(); + + uint64_t key[1] = {era_}; + md_.writeset_tree_->insert(key, e); + } + + virtual void begin_era_array() { + if (!in_superblock_) + throw runtime_error("missing superblock"); + + in_era_array_ = true; + } + + virtual void era(pd::block_address block, uint32_t era) { + if (!in_era_array_) + throw runtime_error("missing era array"); + + md_.era_array_->set(block, era); + } + + virtual void end_era_array() { + in_era_array_ = false; + } + + private: + metadata &md_; + + bool in_superblock_; + + bool in_writeset_; + uint32_t era_; + pd::bitset::ptr bits_; + + bool in_era_array_; + uint32_t nr_blocks_; + }; +} + +//---------------------------------------------------------------- + +emitter::ptr +era::create_restore_emitter(metadata &md) +{ + return emitter::ptr(new restorer(md)); +} + +//---------------------------------------------------------------- diff --git a/era/restore_emitter.h b/era/restore_emitter.h new file mode 100644 index 0000000..3c907bc --- /dev/null +++ b/era/restore_emitter.h @@ -0,0 +1,15 @@ +#ifndef ERA_RESTORE_EMITTER_H +#define ERA_RESTORE_EMITTER_H + +#include "era/emitter.h" +#include "era/metadata.h" + +//---------------------------------------------------------------- + +namespace era { + emitter::ptr create_restore_emitter(metadata &md); +} + +//---------------------------------------------------------------- + +#endif diff --git a/era/superblock.h b/era/superblock.h index bea7af1..408039d 100644 --- a/era/superblock.h +++ b/era/superblock.h @@ -1,5 +1,5 @@ #ifndef ERA_SUPERBLOCK_H -#define ERA_SUPERBLOCK_h +#define ERA_SUPERBLOCK_H #include "persistent-data/block.h" #include "era/era_detail.h" From 56e79676f32ad74850fa02a732b1e9261d999417 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 12 Aug 2014 10:09:30 +0100 Subject: [PATCH 094/165] update Gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index ab69a42..9cf220f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.0.0) rspec-support (3.0.3) - thinp_xml (0.0.15) + thinp_xml (0.0.16) ejt_command_line (>= 0.0.2) PLATFORMS From ad38cfe9c9da983508408c8d71a4dee23922431d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 12 Aug 2014 10:15:14 +0100 Subject: [PATCH 095/165] [block cache] Comment out the statistics output for now --- block-cache/block_cache.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 574973d..4aa1c7a 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -486,6 +486,7 @@ block_cache::~block_cache() ::close(fd_); +#if 0 std::cerr << "\nblock cache stats\n" << "=================\n" << "prefetches:\t" << prefetches_ << "\n" @@ -494,6 +495,7 @@ block_cache::~block_cache() << "write hits:\t" << write_hits_ << "\n" << "write misses:\t" << write_misses_ << "\n" << "write zeroes:\t" << write_zeroes_ << std::endl; +#endif } uint64_t From 75a797253e1c71d15dc5fc8e108bcd1807b15265 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 12 Aug 2014 10:18:58 +0100 Subject: [PATCH 096/165] [build] build era_restore by default --- Makefile.in | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.in b/Makefile.in index 8d0d53a..9f95eea 100644 --- a/Makefile.in +++ b/Makefile.in @@ -29,6 +29,7 @@ PROGRAMS=\ era_check \ era_dump \ era_invalidate \ + era_restore \ \ thin_check \ thin_delta \ From 157eca4dd01ac9d5c536658db849b170ee546e4e Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 12 Aug 2014 10:20:29 +0100 Subject: [PATCH 097/165] [era_restore] tweak help message --- era/era_restore.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/era/era_restore.cc b/era/era_restore.cc index 56618ac..3bd37d4 100644 --- a/era/era_restore.cc +++ b/era/era_restore.cc @@ -13,7 +13,7 @@ #include using namespace boost; -using namespace caching; +using namespace era; using namespace persistent_data; using namespace std; @@ -29,7 +29,7 @@ namespace { try { block_manager<>::ptr bm = open_bm(*fs.output, block_manager<>::READ_WRITE); metadata::ptr md(new metadata(bm, metadata::CREATE)); - emitter::ptr restorer = create_restore_emitter(md); + emitter::ptr restorer = create_restore_emitter(*md); check_file_exists(*fs.input); ifstream in(fs.input->c_str(), ifstream::in); @@ -49,8 +49,7 @@ namespace { << " {-h|--help}" << endl << " {-i|--input} " << endl << " {-o|--output} " << endl - << " {-V|--version}" << endl - << endl; + << " {-V|--version}" << endl; } } From 94356a1648816c705aee76211a4aabf6e5bb43a1 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 15 Aug 2014 10:11:35 +0100 Subject: [PATCH 098/165] [era_check] Was returning after just checking the superblock. Presumably old debug. --- era/era_check.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/era/era_check.cc b/era/era_check.cc index 98af455..c4008c2 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -211,8 +211,6 @@ namespace { if (sb_rep.get_error() == FATAL) return FATAL; - return sb_rep.get_error(); - superblock sb = read_superblock(bm); transaction_manager::ptr tm = open_tm(bm); From 6f804cab24367bd0f3d9b94ac22a01b055c24b67 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:17:20 +0100 Subject: [PATCH 099/165] [block_manager] Track the number of superblocks, throw an exception if someone tries to open two a once. --- persistent-data/block.h | 15 +++++----- persistent-data/block.tcc | 58 +++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/persistent-data/block.h b/persistent-data/block.h index 21df2d9..4aaf8f9 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -78,17 +78,17 @@ namespace persistent_data { class write_ref : public read_ref { public: write_ref(block_cache::block &b); + write_ref(block_cache::block &b, unsigned &ref_count); + write_ref(write_ref const &rhs); + ~write_ref(); + + write_ref const &operator =(write_ref const &rhs); using read_ref::data; void *data(); - }; - class super_ref : public write_ref { - public: - super_ref(block_cache::block &b); - - using read_ref::data; - using write_ref::data; + private: + unsigned *ref_count_; }; // Locking methods @@ -140,6 +140,7 @@ namespace persistent_data { int fd_; mutable block_cache bc_; + unsigned superblock_ref_count_; }; // A little utility to help build validators diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index d23ff99..faafe67 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -152,10 +152,51 @@ namespace persistent_data { template block_manager::write_ref::write_ref(block_cache::block &b) - : read_ref(b) + : read_ref(b), + ref_count_(NULL) { } + template + block_manager::write_ref::write_ref(block_cache::block &b, unsigned &ref_count) + : read_ref(b), + ref_count_(&ref_count) { + if (*ref_count_) + throw std::runtime_error("superblock already locked"); + (*ref_count_)++; + } + + template + block_manager::write_ref::write_ref(write_ref const &rhs) + : read_ref(rhs), + ref_count_(rhs.ref_count_) { + if (ref_count_) + (*ref_count_)++; + } + + template + block_manager::write_ref::~write_ref() + { + if (ref_count_) { + if (!*ref_count_) + throw std::runtime_error("write_ref ref_count going below zero"); + + (*ref_count_)--; + } + } + + template + typename block_manager::write_ref const & + block_manager::write_ref::operator =(write_ref const &rhs) + { + if (&rhs != this) { + read_ref::operator =(rhs); + ref_count_ = rhs.ref_count_; + if (ref_count_) + (*ref_count_)++; + } + } + template void * block_manager::write_ref::data() @@ -163,14 +204,6 @@ namespace persistent_data { return read_ref::b_.get_data(); } - //-------------------------------- - - template - block_manager::super_ref::super_ref(block_cache::block &b) - : write_ref(b) - { - } - //---------------------------------------------------------------- template @@ -179,7 +212,8 @@ namespace persistent_data { unsigned max_concurrent_blocks, mode m) : fd_(open_or_create_block_file(path, nr_blocks * BlockSize, m)), - bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 16) + bc_(fd_, BlockSize >> SECTOR_SHIFT, nr_blocks, 1024u * 1024u * 16), + superblock_ref_count_(0) { } @@ -235,7 +269,7 @@ namespace persistent_data { typename bcache::validator::ptr v) { block_cache::block &b = bc_.get(location, block_cache::GF_BARRIER, v); - return super_ref(b); + return write_ref(b, superblock_ref_count_); } template @@ -244,7 +278,7 @@ namespace persistent_data { typename bcache::validator::ptr v) { block_cache::block &b = bc_.get(location, block_cache::GF_ZERO | block_cache::GF_BARRIER, v); - return super_ref(b); + return write_ref(b, superblock_ref_count_); } template From bf1b65e62f275815cceb7f39f466390b6ab42267 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:18:06 +0100 Subject: [PATCH 100/165] Update gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9cf220f..06a7697 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.0.0) rspec-support (3.0.3) - thinp_xml (0.0.16) + thinp_xml (0.0.18) ejt_command_line (>= 0.0.2) PLATFORMS From 85ab149685ecae73b605e7036716396a092c163d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:20:43 +0100 Subject: [PATCH 101/165] [era/metadata.{h,cc}] Support creation of new metadata --- era/metadata.cc | 27 +++++++++++++++++++++++++-- era/metadata.h | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/era/metadata.cc b/era/metadata.cc index d3535d8..2b2d072 100644 --- a/era/metadata.cc +++ b/era/metadata.cc @@ -16,14 +16,22 @@ namespace { transaction_manager::ptr tm(new transaction_manager(bm, sm)); return tm; } + + void + copy_space_maps(space_map::ptr lhs, space_map::ptr rhs) { + for (block_address b = 0; b < rhs->get_nr_blocks(); b++) { + uint32_t count = rhs->get_count(b); + if (count > 0) + lhs->set_count(b, rhs->get_count(b)); + } + } } metadata::metadata(block_manager<>::ptr bm, open_type ot) { switch (ot) { case CREATE: - // finish - throw runtime_error("not imlemented"); + create_metadata(bm); break; case OPEN: @@ -37,6 +45,21 @@ metadata::metadata(block_manager<>::ptr bm, block_address metadata_snap) open_metadata(bm); } +void +metadata::create_metadata(block_manager<>::ptr bm) +{ + tm_ = open_tm(bm); + + space_map::ptr core = tm_->get_sm(); + metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks()); + copy_space_maps(metadata_sm_, core); + tm_->set_sm(metadata_sm_); + + writeset_tree_ = writeset_tree::ptr(new writeset_tree(tm_, era_detail_traits::ref_counter(tm_))); + era_array_ = era_array::ptr(new era_array(tm_, + uint32_traits::ref_counter())); +} + void metadata::open_metadata(block_manager<>::ptr bm, block_address loc) { diff --git a/era/metadata.h b/era/metadata.h index a88a1e9..687e2b9 100644 --- a/era/metadata.h +++ b/era/metadata.h @@ -39,6 +39,7 @@ namespace era { era_array::ptr era_array_; private: + void create_metadata(block_manager<>::ptr bm); void open_metadata(block_manager<>::ptr bm, block_address loc = SUPERBLOCK_LOCATION); From 5465c95134d20ee5eb8921102584b251788d242c Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:21:47 +0100 Subject: [PATCH 102/165] [era/xml format] bring in line with the Ruby library --- era/xml_format.cc | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/era/xml_format.cc b/era/xml_format.cc index 0112aae..876356b 100644 --- a/era/xml_format.cc +++ b/era/xml_format.cc @@ -49,7 +49,7 @@ namespace { void writeset_bit(uint32_t bit, bool value) { out_.indent(); // FIXME: collect all the bits, then uuencode - out_ << "" << endl; + out_ << "" << endl; } void end_writeset() { @@ -67,7 +67,7 @@ namespace { void era(pd::block_address block, uint32_t era) { out_.indent(); out_ << "" << endl; + << "\" era=\"" << era << "\"/>" << endl; } void end_era_array() { @@ -83,6 +83,20 @@ namespace { //-------------------------------- // Parser //-------------------------------- + void parse_bit(attributes const &a, emitter *e) { + bool value; + + string txt = get_attr(a, "value"); + if (txt == "true") + value = true; + else if (txt == "false") + value = false; + else + throw runtime_error("invalid boolean"); + + e->writeset_bit(get_attr(a, "block"), value); + } + void start_tag(void *data, char const *el, char const **attr) { emitter *e = static_cast(data); attributes a; @@ -100,8 +114,7 @@ namespace { get_attr(a, "nr_bits")); else if (!strcmp(el, "bit")) - e->writeset_bit(get_attr(a, "bit"), - get_attr(a, "value")); + parse_bit(a, e); else if (!strcmp(el, "era_array")) e->begin_era_array(); @@ -126,6 +139,14 @@ namespace { else if (!strcmp(el, "era_array")) e->end_era_array(); + else if (!strcmp(el, "era")) + /* do nothing */ + ; + + else if (!strcmp(el, "bit")) + /* do nothing */ + ; + else throw runtime_error("unknown tag type"); } From 1a632f9d53b2c29be4f9c162e00dbda476e854ad Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:22:12 +0100 Subject: [PATCH 103/165] [era/xml restorer] bug fixes --- era/restore_emitter.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/era/restore_emitter.cc b/era/restore_emitter.cc index aba16c0..c3465c0 100644 --- a/era/restore_emitter.cc +++ b/era/restore_emitter.cc @@ -29,11 +29,15 @@ namespace { sb.current_era = current_era; nr_blocks = nr_blocks; + + md_.era_array_->grow(nr_blocks, 0); + + in_superblock_ = true; } virtual void end_superblock() { if (!in_superblock_) - throw runtime_error("missing superblock"); + throw runtime_error("xml missing superblock"); md_.commit(); } @@ -42,6 +46,9 @@ namespace { if (!in_superblock_) throw runtime_error("missing superblock"); + if (in_writeset_) + throw runtime_error("attempt to begin writeset when already in one"); + in_writeset_ = true; era_ = era; @@ -56,6 +63,8 @@ namespace { virtual void end_writeset() { in_writeset_ = false; + bits_->flush(); + era_detail e; e.nr_bits = bits_->get_nr_bits(); e.writeset_root = bits_->get_root(); From feb8a03d5f2893281a34026b7eccbe891d18a909 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:25:07 +0100 Subject: [PATCH 104/165] [btree_damage_tracker] Initialise all path_tracker objects with a null path. This fixes an insidious bug that took me ages to track down. Without it the non-existent front() of a list was sometimes being accessed. --- persistent-data/data-structures/btree_damage_visitor.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/persistent-data/data-structures/btree_damage_visitor.h b/persistent-data/data-structures/btree_damage_visitor.h index 7886d85..1eede99 100644 --- a/persistent-data/data-structures/btree_damage_visitor.h +++ b/persistent-data/data-structures/btree_damage_visitor.h @@ -85,12 +85,17 @@ namespace persistent_data { // different sub tree (by looking at the btree_path). class path_tracker { public: + path_tracker() { + // We push an empty path, to ensure there + // is always a current_path. + paths_.push_back(btree_path()); + } + // returns the old path if the tree has changed. btree_path const *next_path(btree_path const &p) { if (p != current_path()) { if (paths_.size() == 2) paths_.pop_front(); - paths_.push_back(p); return &paths_.front(); From e3d459a148f1d445993e6d9af763b5c100a2f278 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:27:52 +0100 Subject: [PATCH 105/165] [era_restore] add some cucumber tests --- features/era_restore.feature | 66 ++++++++++++++++++++++++++ features/step_definitions/era_steps.rb | 15 ++++++ 2 files changed, 81 insertions(+) create mode 100644 features/era_restore.feature diff --git a/features/era_restore.feature b/features/era_restore.feature new file mode 100644 index 0000000..b62989c --- /dev/null +++ b/features/era_restore.feature @@ -0,0 +1,66 @@ +Feature: thin_restore + Scenario: print version (-V flag) + When I run era_restore with -V + Then it should pass with version + + Scenario: print version (--version flag) + When I run era_restore with --version + Then it should pass with version + + Scenario: print help (-h) + When I run era_restore with -h + Then it should pass + And the output should contain exactly: + + """ + Usage: era_restore [options] + Options: + {-h|--help} + {-i|--input} + {-o|--output} + {-V|--version} + + """ + + Scenario: print help (--help) + When I run era_restore with -h + Then it should pass + And the output should contain exactly: + + """ + Usage: era_restore [options] + Options: + {-h|--help} + {-i|--input} + {-o|--output} + {-V|--version} + + """ + + Scenario: missing input file + Given the dev file metadata.bin + When I run era_restore with -o metadata.bin + Then it should fail with: + """ + No input file provided. + """ + + Scenario: input file not found + Given the dev file metadata.bin + When I run era_restore with -i foo.xml -o metadata.bin + Then it should fail + + Scenario: missing output file + When I run era_restore with -i metadata.xml + Then it should fail with: + """ + No output file provided. + """ + + Scenario: successfully restores a valid xml file + Given a small era xml file + And an empty dev file + When I run era_restore with -i metadata.xml -o metadata.bin + Then it should pass + And the metadata should be valid + \ No newline at end of file diff --git a/features/step_definitions/era_steps.rb b/features/step_definitions/era_steps.rb index a1e9f29..bbdbb2a 100644 --- a/features/step_definitions/era_steps.rb +++ b/features/step_definitions/era_steps.rb @@ -14,3 +14,18 @@ end Then /^era_usage to stderr$/ do assert_partial_output(ERA_USAGE, all_stderr) end + +When(/^I run era_restore with (.*?)$/) do |opts| + run_simple("era_restore #{opts}", false) +end + +Given(/^a small era xml file$/) do + in_current_dir do + system("era_xml create --nr-blocks 100 --nr-writesets 2 --current-era 1000 > #{xml_file}") + end +end + +Then(/^the metadata should be valid$/) do + run_simple("era_check #{dev_file}", true) +end + From 3724f78a13e4d889ee2ecccbf110a15a759da502 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 11:51:04 +0100 Subject: [PATCH 106/165] [block_t] Fix a validator test --- unit-tests/block_t.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unit-tests/block_t.cc b/unit-tests/block_t.cc index 0d08a3b..c2a6d58 100644 --- a/unit-tests/block_t.cc +++ b/unit-tests/block_t.cc @@ -360,6 +360,8 @@ TEST_F(ValidatorTests, validator_can_be_changed_by_write_lock_zero) expect_prepare(vmock); bm4096::write_ref wr = bm->write_lock_zero(0, vmock); } + // We need to flush to ensure the vmock->prepare has occurred + bm->flush(); expect_no_check(vmock2); expect_prepare(vmock2); From 0d3942cae860e38f48a646582b8662eadda28154 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 12:18:05 +0100 Subject: [PATCH 107/165] [block-manager] ensure the superblock cannot be taken concurrently with any other lock. --- block-cache/block_cache.cc | 6 ++++++ block-cache/block_cache.h | 1 + persistent-data/block.h | 1 - persistent-data/block.tcc | 6 ++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 4aa1c7a..0fbc9db 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -504,6 +504,12 @@ block_cache::get_nr_blocks() const return nr_data_blocks_; } +uint64_t +block_cache::get_nr_locked() const +{ + return nr_locked_; +} + void block_cache::zero_block(block &b) { diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 5b25e24..ff38141 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -119,6 +119,7 @@ namespace bcache { ~block_cache(); uint64_t get_nr_blocks() const; + uint64_t get_nr_locked() const; enum get_flags { GF_ZERO = (1 << 0), diff --git a/persistent-data/block.h b/persistent-data/block.h index 4aaf8f9..fa587ac 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -36,7 +36,6 @@ namespace persistent_data { using namespace bcache; - uint32_t const MD_BLOCK_SIZE = 4096; template diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index faafe67..4867efc 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -268,6 +268,9 @@ namespace persistent_data { block_manager::superblock(block_address location, typename bcache::validator::ptr v) { + if (bc_.get_nr_locked() > 0) + throw std::runtime_error("attempt to lock superblock while other locks are still held"); + block_cache::block &b = bc_.get(location, block_cache::GF_BARRIER, v); return write_ref(b, superblock_ref_count_); } @@ -277,6 +280,9 @@ namespace persistent_data { block_manager::superblock_zero(block_address location, typename bcache::validator::ptr v) { + if (bc_.get_nr_locked() > 0) + throw std::runtime_error("attempt to lock superblock while other locks are still held"); + block_cache::block &b = bc_.get(location, block_cache::GF_ZERO | block_cache::GF_BARRIER, v); return write_ref(b, superblock_ref_count_); } From 4799becb0122a4292a89d11f58b69541a274bad0 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 12:54:39 +0100 Subject: [PATCH 108/165] [block-cache] fix leaking validators The memory for the blocks is explicitly managed, and the destructors for the blocks wasn't being called. --- block-cache/block_cache.cc | 21 ++++++++++++++++----- block-cache/block_cache.h | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 0fbc9db..3b44601 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -77,6 +77,21 @@ block_cache::init_free_list(unsigned count) return 0; } +void +block_cache::exit_free_list() +{ + if (blocks_data_) + free(blocks_data_); + + if (blocks_memory_) { + struct block *blocks = static_cast(blocks_memory_); + for (unsigned i = 0; i < nr_cache_blocks_; i++) + (blocks + i)->~block(); + + free(blocks_memory_); + } +} + block_cache::block * block_cache::__alloc_block() { @@ -475,11 +490,7 @@ block_cache::~block_cache() flush(); wait_all(); - if (blocks_memory_) - free(blocks_memory_); - - if (blocks_data_) - free(blocks_data_); + exit_free_list(); if (aio_context_) io_destroy(aio_context_); diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index ff38141..5cf9b27 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -138,6 +138,7 @@ namespace bcache { private: int init_free_list(unsigned count); + void exit_free_list(); block *__alloc_block(); void complete_io(block &b, int result); void issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc); From b5a9cd6043477cecb7d2521e0eefaa61de084e5d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 14:20:36 +0100 Subject: [PATCH 109/165] [block-cache] Create just one noop_validator No need to create a separate one for each block. --- block-cache/block_cache.cc | 5 +++-- block-cache/block_cache.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 3b44601..5bb9bfe 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -403,7 +403,7 @@ block_cache::new_block(block_address index) b->error_ = 0; b->flags_ = 0; - b->v_ = validator::ptr(new noop_validator); + b->v_ = noop_validator_; b->index_ = index; setup_control_block(*b); @@ -450,7 +450,8 @@ block_cache::block_cache(int fd, sector_t block_size, uint64_t on_disk_blocks, s write_zeroes_(0), write_hits_(0), write_misses_(0), - prefetches_(0) + prefetches_(0), + noop_validator_(new noop_validator()) { int r; unsigned nr_cache_blocks = calc_nr_cache_blocks(mem, block_size); diff --git a/block-cache/block_cache.h b/block-cache/block_cache.h index 5cf9b27..4bc6667 100644 --- a/block-cache/block_cache.h +++ b/block-cache/block_cache.h @@ -214,6 +214,8 @@ namespace bcache { unsigned write_hits_; unsigned write_misses_; unsigned prefetches_; + + validator::ptr noop_validator_; }; } From 6f760ccd87361e6c585dfc638c63110b697081a6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 14:22:14 +0100 Subject: [PATCH 110/165] [block-cache] When changing validators the new validator check fn was only being called if the block was dirty. --- block-cache/block_cache.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 5bb9bfe..51c669b 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -564,10 +564,9 @@ block_cache::lookup_or_read_block(block_address index, unsigned flags, if (flags & GF_ZERO) zero_block(*b); else { - if (b->v_.get() && - b->v_.get() != v.get() && - b->test_flags(BF_DIRTY)) { - b->v_->prepare(b->data_, b->index_); + if (b->v_.get() != v.get()) { + if (b->test_flags(BF_DIRTY)) + b->v_->prepare(b->data_, b->index_); v->check(b->data_, b->index_); } } From b493a30b650c295e370533860b80f52c1ce0edc7 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 14:23:14 +0100 Subject: [PATCH 111/165] [block-cache] tweak when validators are swapped Makes it more consitent in the face of exceptions --- block-cache/block_cache.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block-cache/block_cache.cc b/block-cache/block_cache.cc index 51c669b..6ecce1f 100644 --- a/block-cache/block_cache.cc +++ b/block-cache/block_cache.cc @@ -577,8 +577,6 @@ block_cache::lookup_or_read_block(block_address index, unsigned flags, b = new_block(index); if (b) { - b->v_ = v; - if (flags & GF_ZERO) zero_block(*b); else { @@ -586,6 +584,8 @@ block_cache::lookup_or_read_block(block_address index, unsigned flags, wait_specific(*b); v->check(b->data_, b->index_); } + + b->v_ = v; } } From 930cc9d41275d73006cd568567e12e5b8311346e Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 21 Aug 2014 14:25:02 +0100 Subject: [PATCH 112/165] [block-manager] Superblocks weren't being marked as DIRTY Gulp --- persistent-data/block.tcc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index 4867efc..f2f3a7a 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -271,7 +271,7 @@ namespace persistent_data { if (bc_.get_nr_locked() > 0) throw std::runtime_error("attempt to lock superblock while other locks are still held"); - block_cache::block &b = bc_.get(location, block_cache::GF_BARRIER, v); + block_cache::block &b = bc_.get(location, block_cache::GF_DIRTY | block_cache::GF_BARRIER, v); return write_ref(b, superblock_ref_count_); } From a7c96c0e1ec04886c8f68018404cfdd5633ffc10 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Aug 2014 11:14:49 +0100 Subject: [PATCH 113/165] [everything] Fix circular shared pointer references. We had a cycle from transaction_manager <-> space_map, and also from the ref_counters back up to the tm. This prevented objects being destroyed when various programs exited. From now on we'll try and only use a shared ptr if ownership is implied. Otherwise a reference will be used (eg, for up pointers). --- caching/cache_check.cc | 6 +- caching/hint_array.cc | 12 +- caching/hint_array.h | 5 +- caching/metadata.cc | 14 +- era/era_check.cc | 4 +- era/metadata.cc | 10 +- era/restore_emitter.cc | 2 +- era/writeset_tree.cc | 2 +- persistent-data/data-structures/array.h | 22 +- persistent-data/data-structures/bitset.cc | 8 +- persistent-data/data-structures/bitset.h | 6 +- .../data-structures/bloom_filter.cc | 4 +- .../data-structures/bloom_filter.h | 7 +- persistent-data/data-structures/btree.h | 14 +- persistent-data/data-structures/btree.tcc | 28 +- persistent-data/space-maps/disk.cc | 46 +- persistent-data/space-maps/disk.h | 8 +- thin-provisioning/metadata.cc | 48 +-- thin-provisioning/metadata_dumper.cc | 2 +- thin-provisioning/restore_emitter.cc | 2 +- thin-provisioning/thin_check.cc | 6 +- thin-provisioning/thin_delta.cc | 6 +- thin-provisioning/thin_pool.cc | 4 +- thin-provisioning/thin_rmap.cc | 2 +- unit-tests/array_t.cc | 16 +- unit-tests/bitset_t.cc | 52 ++- unit-tests/bloom_filter_t.cc | 4 +- unit-tests/btree_counter_t.cc | 4 +- unit-tests/btree_damage_visitor_t.cc | 4 +- unit-tests/btree_t.cc | 41 +- unit-tests/space_map_t.cc | 408 +++++++++--------- 31 files changed, 391 insertions(+), 406 deletions(-) diff --git a/caching/cache_check.cc b/caching/cache_check.cc index d5ebc19..593ba4e 100644 --- a/caching/cache_check.cc +++ b/caching/cache_check.cc @@ -237,7 +237,7 @@ namespace { out << "examining mapping array" << end_message(); { nested_output::nest _ = out.push(); - mapping_array ma(tm, mapping_array::ref_counter(), sb.mapping_root, sb.cache_blocks); + mapping_array ma(*tm, mapping_array::ref_counter(), sb.mapping_root, sb.cache_blocks); check_mapping_array(ma, mapping_rep); } } @@ -250,7 +250,7 @@ namespace { out << "examining hint array" << end_message(); { nested_output::nest _ = out.push(); - hint_array ha(tm, sb.policy_hint_size, sb.hint_root, sb.cache_blocks); + hint_array ha(*tm, sb.policy_hint_size, sb.hint_root, sb.cache_blocks); ha.check(hint_rep); } } @@ -264,7 +264,7 @@ namespace { out << "examining discard bitset" << end_message(); { nested_output::nest _ = out.push(); - persistent_data::bitset discards(tm, sb.discard_root, sb.discard_nr_blocks); + persistent_data::bitset discards(*tm, sb.discard_root, sb.discard_nr_blocks); } } } diff --git a/caching/hint_array.cc b/caching/hint_array.cc index 037a879..cb9f2f1 100644 --- a/caching/hint_array.cc +++ b/caching/hint_array.cc @@ -38,7 +38,7 @@ namespace { xx(4); template - shared_ptr mk_array(transaction_manager::ptr tm) { + shared_ptr mk_array(transaction_manager &tm) { typedef hint_traits traits; typedef array ha; @@ -47,7 +47,7 @@ namespace { return r; } - shared_ptr mk_array(transaction_manager::ptr tm, uint32_t width) { + shared_ptr mk_array(transaction_manager &tm, uint32_t width) { switch (width) { #define xx(n) case n: return mk_array(tm) @@ -76,7 +76,7 @@ namespace { //-------------------------------- template - shared_ptr mk_array(transaction_manager::ptr tm, block_address root, unsigned nr_entries) { + shared_ptr mk_array(transaction_manager &tm, block_address root, unsigned nr_entries) { typedef hint_traits traits; typedef array ha; @@ -85,7 +85,7 @@ namespace { return r; } - shared_ptr mk_array(transaction_manager::ptr tm, uint32_t width, block_address root, unsigned nr_entries) { + shared_ptr mk_array(transaction_manager &tm, uint32_t width, block_address root, unsigned nr_entries) { switch (width) { #define xx(n) case n: return mk_array(tm, root, nr_entries) all_widths @@ -230,13 +230,13 @@ missing_hints::visit(damage_visitor &v) const //---------------------------------------------------------------- -hint_array::hint_array(tm_ptr tm, unsigned width) +hint_array::hint_array(transaction_manager &tm, unsigned width) : width_(check_width(width)), impl_(mk_array(tm, width)) { } -hint_array::hint_array(hint_array::tm_ptr tm, unsigned width, +hint_array::hint_array(transaction_manager &tm, unsigned width, block_address root, unsigned nr_entries) : width_(check_width(width)), impl_(mk_array(tm, width, root, nr_entries)) diff --git a/caching/hint_array.h b/caching/hint_array.h index 6e8121b..45430cc 100644 --- a/caching/hint_array.h +++ b/caching/hint_array.h @@ -56,10 +56,9 @@ namespace caching { class hint_array { public: typedef boost::shared_ptr ptr; - typedef persistent_data::transaction_manager::ptr tm_ptr; - hint_array(tm_ptr tm, unsigned width); - hint_array(tm_ptr tm, unsigned width, block_address root, unsigned nr_entries); + hint_array(transaction_manager &tm, unsigned width); + hint_array(transaction_manager &tm, unsigned width, block_address root, unsigned nr_entries); unsigned get_nr_entries() const; diff --git a/caching/metadata.cc b/caching/metadata.cc index 0a246f9..2368a3f 100644 --- a/caching/metadata.cc +++ b/caching/metadata.cc @@ -61,7 +61,7 @@ metadata::setup_hint_array(size_t width) { if (width > 0) hints_ = hint_array::ptr( - new hint_array(tm_, width)); + new hint_array(*tm_, width)); } void @@ -70,16 +70,16 @@ metadata::create_metadata(block_manager<>::ptr bm) tm_ = open_tm(bm); space_map::ptr core = tm_->get_sm(); - metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks()); + metadata_sm_ = create_metadata_sm(*tm_, tm_->get_bm()->get_nr_blocks()); copy_space_maps(metadata_sm_, core); tm_->set_sm(metadata_sm_); - mappings_ = mapping_array::ptr(new mapping_array(tm_, mapping_array::ref_counter())); + mappings_ = mapping_array::ptr(new mapping_array(*tm_, mapping_array::ref_counter())); // We can't instantiate the hint array yet, since we don't know the // hint width. - discard_bits_ = persistent_data::bitset::ptr(new persistent_data::bitset(tm_)); + discard_bits_ = persistent_data::bitset::ptr(new persistent_data::bitset(*tm_)); } void @@ -89,19 +89,19 @@ metadata::open_metadata(block_manager<>::ptr bm) sb_ = read_superblock(tm_->get_bm()); mappings_ = mapping_array::ptr( - new mapping_array(tm_, + new mapping_array(*tm_, mapping_array::ref_counter(), sb_.mapping_root, sb_.cache_blocks)); if (sb_.hint_root) hints_ = hint_array::ptr( - new hint_array(tm_, sb_.policy_hint_size, + new hint_array(*tm_, sb_.policy_hint_size, sb_.hint_root, sb_.cache_blocks)); if (sb_.discard_root) discard_bits_ = persistent_data::bitset::ptr( - new persistent_data::bitset(tm_, sb_.discard_root, sb_.discard_nr_blocks)); + new persistent_data::bitset(*tm_, sb_.discard_root, sb_.discard_nr_blocks)); } void diff --git a/era/era_check.cc b/era/era_check.cc index c4008c2..25c4d20 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -217,14 +217,14 @@ namespace { writeset_tree_reporter wt_rep(out); { era_detail_traits::ref_counter rc(tm); - writeset_tree wt(tm, sb.writeset_tree_root, rc); + writeset_tree wt(*tm, sb.writeset_tree_root, rc); check_writeset_tree(tm, wt, wt_rep); } era_array_reporter ea_rep(out); { uint32_traits::ref_counter rc; - era_array ea(tm, rc, sb.era_array_root, sb.nr_blocks); + era_array ea(*tm, rc, sb.era_array_root, sb.nr_blocks); check_era_array(ea, sb.current_era, ea_rep); } diff --git a/era/metadata.cc b/era/metadata.cc index 2b2d072..dd4fe7a 100644 --- a/era/metadata.cc +++ b/era/metadata.cc @@ -51,12 +51,12 @@ metadata::create_metadata(block_manager<>::ptr bm) tm_ = open_tm(bm); space_map::ptr core = tm_->get_sm(); - metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks()); + metadata_sm_ = create_metadata_sm(*tm_, tm_->get_bm()->get_nr_blocks()); copy_space_maps(metadata_sm_, core); tm_->set_sm(metadata_sm_); - writeset_tree_ = writeset_tree::ptr(new writeset_tree(tm_, era_detail_traits::ref_counter(tm_))); - era_array_ = era_array::ptr(new era_array(tm_, + writeset_tree_ = writeset_tree::ptr(new writeset_tree(*tm_, era_detail_traits::ref_counter(tm_))); + era_array_ = era_array::ptr(new era_array(*tm_, uint32_traits::ref_counter())); } @@ -66,11 +66,11 @@ metadata::open_metadata(block_manager<>::ptr bm, block_address loc) tm_ = open_tm(bm); sb_ = read_superblock(tm_->get_bm(), loc); - writeset_tree_ = writeset_tree::ptr(new writeset_tree(tm_, + writeset_tree_ = writeset_tree::ptr(new writeset_tree(*tm_, sb_.writeset_tree_root, era_detail_traits::ref_counter(tm_))); - era_array_ = era_array::ptr(new era_array(tm_, + era_array_ = era_array::ptr(new era_array(*tm_, uint32_traits::ref_counter(), sb_.era_array_root, sb_.nr_blocks)); diff --git a/era/restore_emitter.cc b/era/restore_emitter.cc index c3465c0..a5e714e 100644 --- a/era/restore_emitter.cc +++ b/era/restore_emitter.cc @@ -52,7 +52,7 @@ namespace { in_writeset_ = true; era_ = era; - bits_.reset(new bitset(md_.tm_)); + bits_.reset(new bitset(*md_.tm_)); bits_->grow(nr_bits, false); } diff --git a/era/writeset_tree.cc b/era/writeset_tree.cc index 4e2c478..62f5fc3 100644 --- a/era/writeset_tree.cc +++ b/era/writeset_tree.cc @@ -54,7 +54,7 @@ namespace { void visit(btree_path const &path, era_detail const &era) { era_ = path[0]; - persistent_data::bitset bs(tm_, era.writeset_root, era.nr_bits); + persistent_data::bitset bs(*tm_, era.writeset_root, era.nr_bits); writeset_v_.writeset_begin(era_, era.nr_bits); bs.walk_bitset(*this); writeset_v_.writeset_end(); diff --git a/persistent-data/data-structures/array.h b/persistent-data/data-structures/array.h index cf8ad4e..c03daaa 100644 --- a/persistent-data/data-structures/array.h +++ b/persistent-data/data-structures/array.h @@ -172,7 +172,7 @@ namespace persistent_data { unsigned visit_array_block(ValueVisitor &vv, btree_path const &p, typename block_traits::value_type const &v) const { - rblock rb(tm_->read_lock(v, validator_), rc_); + rblock rb(tm_.read_lock(v, validator_), rc_); for (uint32_t i = 0; i < rb.nr_entries(); i++) vv.visit(p[0] * rb.max_entries() + i, rb.get(i)); @@ -207,8 +207,6 @@ namespace persistent_data { unsigned entries_per_block_; }; - typedef typename persistent_data::transaction_manager::ptr tm_ptr; - typedef block_manager<>::write_ref write_ref; typedef block_manager<>::read_ref read_ref; @@ -219,23 +217,23 @@ namespace persistent_data { typedef typename ValueTraits::value_type value_type; typedef typename ValueTraits::ref_counter ref_counter; - array(tm_ptr tm, ref_counter rc) + array(transaction_manager &tm, ref_counter rc) : tm_(tm), entries_per_block_(rblock::calc_max_entries()), nr_entries_(0), - block_rc_(tm->get_sm(), *this), + block_rc_(tm.get_sm(), *this), block_tree_(tm, block_rc_), rc_(rc), validator_(new array_detail::array_block_validator) { } - array(tm_ptr tm, ref_counter rc, + array(transaction_manager &tm, ref_counter rc, block_address root, unsigned nr_entries) : tm_(tm), entries_per_block_(rblock::calc_max_entries()), nr_entries_(nr_entries), - block_rc_(tm->get_sm(), *this), + block_rc_(tm.get_sm(), *this), block_tree_(tm, root, block_rc_), rc_(rc), validator_(new array_detail::array_block_validator) { @@ -393,7 +391,7 @@ namespace persistent_data { wblock new_ablock(unsigned ablock_index) { uint64_t key[1] = {ablock_index}; - write_ref b = tm_->new_block(validator_); + write_ref b = tm_.new_block(validator_); block_address location = b.get_location(); wblock wb(b, rc_); @@ -404,13 +402,13 @@ namespace persistent_data { rblock get_ablock(unsigned ablock_index) const { block_address addr = lookup_block_address(ablock_index); - return rblock(tm_->read_lock(addr, validator_), rc_); + return rblock(tm_.read_lock(addr, validator_), rc_); } wblock shadow_ablock(unsigned ablock_index) { uint64_t key[1] = {ablock_index}; block_address addr = lookup_block_address(ablock_index); - std::pair p = tm_->shadow(addr, validator_); + std::pair p = tm_.shadow(addr, validator_); wblock wb = wblock(p.first, rc_); if (p.second) @@ -422,11 +420,11 @@ namespace persistent_data { } void dec_ablock_entries(block_address addr) { - rblock b(tm_->read_lock(addr, validator_), rc_); + rblock b(tm_.read_lock(addr, validator_), rc_); b.dec_all_entries(); } - tm_ptr tm_; + transaction_manager &tm_; unsigned entries_per_block_; unsigned nr_entries_; block_ref_counter block_rc_; diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index e49e19f..98805fa 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -33,12 +33,12 @@ namespace persistent_data { typedef boost::shared_ptr ptr; typedef persistent_data::transaction_manager::ptr tm_ptr; - bitset_impl(tm_ptr tm) + bitset_impl(transaction_manager &tm) : nr_bits_(0), array_(tm, rc_) { } - bitset_impl(tm_ptr tm, block_address root, unsigned nr_bits) + bitset_impl(transaction_manager &tm, block_address root, unsigned nr_bits) : nr_bits_(nr_bits), array_(tm, rc_, root, nr_bits / BITS_PER_ULL) { } @@ -203,12 +203,12 @@ namespace persistent_data { //---------------------------------------------------------------- -persistent_data::bitset::bitset(tm_ptr tm) +persistent_data::bitset::bitset(transaction_manager &tm) : impl_(new bitset_impl(tm)) { } -persistent_data::bitset::bitset(tm_ptr tm, block_address root, unsigned nr_bits) +persistent_data::bitset::bitset(transaction_manager &tm, block_address root, unsigned nr_bits) : impl_(new bitset_impl(tm, root, nr_bits)) { } diff --git a/persistent-data/data-structures/bitset.h b/persistent-data/data-structures/bitset.h index 70688aa..3b69cb9 100644 --- a/persistent-data/data-structures/bitset.h +++ b/persistent-data/data-structures/bitset.h @@ -49,10 +49,10 @@ namespace persistent_data { class bitset { public: typedef boost::shared_ptr ptr; - typedef persistent_data::transaction_manager::ptr tm_ptr; - bitset(tm_ptr tm); - bitset(tm_ptr tm, block_address root, unsigned nr_bits); + bitset(transaction_manager &tm); + bitset(transaction_manager &tm, + block_address root, unsigned nr_bits); block_address get_root() const; unsigned get_nr_bits() const; void grow(unsigned new_nr_bits, bool default_value); diff --git a/persistent-data/data-structures/bloom_filter.cc b/persistent-data/data-structures/bloom_filter.cc index 3ca6ffb..a250835 100644 --- a/persistent-data/data-structures/bloom_filter.cc +++ b/persistent-data/data-structures/bloom_filter.cc @@ -34,7 +34,7 @@ namespace { //---------------------------------------------------------------- -bloom_filter::bloom_filter(tm_ptr tm, +bloom_filter::bloom_filter(transaction_manager &tm, unsigned nr_bits, unsigned nr_probes) : tm_(tm), bits_(tm), @@ -45,7 +45,7 @@ bloom_filter::bloom_filter(tm_ptr tm, bits_.grow(nr_bits, false); } -bloom_filter::bloom_filter(tm_ptr tm, block_address root, +bloom_filter::bloom_filter(transaction_manager &tm, block_address root, unsigned nr_bits, unsigned nr_probes) : tm_(tm), bits_(tm, root, nr_bits), diff --git a/persistent-data/data-structures/bloom_filter.h b/persistent-data/data-structures/bloom_filter.h index da91088..6407878 100644 --- a/persistent-data/data-structures/bloom_filter.h +++ b/persistent-data/data-structures/bloom_filter.h @@ -12,13 +12,12 @@ namespace persistent_data { class bloom_filter { public: typedef boost::shared_ptr ptr; - typedef persistent_data::transaction_manager::ptr tm_ptr; // nr_bits must be a power of two - bloom_filter(tm_ptr tm, + bloom_filter(transaction_manager &tm, unsigned nr_bits, unsigned nr_probes); - bloom_filter(tm_ptr tm, block_address root, + bloom_filter(transaction_manager &tm, block_address root, unsigned nr_bits_power, unsigned nr_probes); block_address get_root() const; @@ -34,7 +33,7 @@ namespace persistent_data { void fill_probes(block_address b, vector &probes) const; - tm_ptr tm_; + transaction_manager &tm_; persistent_data::bitset bits_; unsigned nr_probes_; uint64_t mask_; diff --git a/persistent-data/data-structures/btree.h b/persistent-data/data-structures/btree.h index 9c9c1a2..f4130c7 100644 --- a/persistent-data/data-structures/btree.h +++ b/persistent-data/data-structures/btree.h @@ -198,7 +198,7 @@ namespace persistent_data { class ro_spine : private boost::noncopyable { public: - ro_spine(transaction_manager::ptr tm, + ro_spine(transaction_manager &tm, bcache::validator::ptr v) : tm_(tm), validator_(v) { @@ -212,7 +212,7 @@ namespace persistent_data { } private: - transaction_manager::ptr tm_; + transaction_manager &tm_; bcache::validator::ptr validator_; std::list::read_ref> spine_; }; @@ -223,7 +223,7 @@ namespace persistent_data { typedef transaction_manager::write_ref write_ref; typedef boost::optional maybe_block; - shadow_spine(transaction_manager::ptr tm, + shadow_spine(transaction_manager &tm, bcache::validator::ptr v) : tm_(tm), @@ -276,7 +276,7 @@ namespace persistent_data { } private: - transaction_manager::ptr tm_; + transaction_manager &tm_; bcache::validator::ptr validator_; std::list::write_ref> spine_; maybe_block root_; @@ -335,10 +335,10 @@ namespace persistent_data { typedef typename btree_detail::node_ref leaf_node; typedef typename btree_detail::node_ref internal_node; - btree(typename persistent_data::transaction_manager::ptr tm, + btree(transaction_manager &tm, typename ValueTraits::ref_counter rc); - btree(typename transaction_manager::ptr tm, + btree(transaction_manager &tm, block_address root, typename ValueTraits::ref_counter rc); @@ -434,7 +434,7 @@ namespace persistent_data { void inc_children(btree_detail::shadow_spine &spine, RefCounter &leaf_rc); - typename persistent_data::transaction_manager::ptr tm_; + transaction_manager &tm_; bool destroy_; block_address root_; block_ref_counter internal_rc_; diff --git a/persistent-data/data-structures/btree.tcc b/persistent-data/data-structures/btree.tcc index 9034aca..ef03013 100644 --- a/persistent-data/data-structures/btree.tcc +++ b/persistent-data/data-structures/btree.tcc @@ -64,7 +64,7 @@ namespace persistent_data { inline void ro_spine::step(block_address b) { - spine_.push_back(tm_->read_lock(b, validator_)); + spine_.push_back(tm_.read_lock(b, validator_)); if (spine_.size() > 2) spine_.pop_front(); } @@ -72,11 +72,11 @@ namespace persistent_data { inline bool shadow_spine::step(block_address b) { - pair p = tm_->shadow(b, validator_); + pair p = tm_.shadow(b, validator_); try { step(p.first); } catch (...) { - tm_->get_sm()->dec(p.first.get_location()); + tm_.get_sm()->dec(p.first.get_location()); throw; } return p.second; @@ -392,17 +392,17 @@ namespace persistent_data { template btree:: - btree(typename transaction_manager::ptr tm, + btree(transaction_manager &tm, typename ValueTraits::ref_counter rc) : tm_(tm), destroy_(false), - internal_rc_(tm->get_sm()), + internal_rc_(tm.get_sm()), rc_(rc), validator_(new btree_node_validator) { using namespace btree_detail; - write_ref root = tm_->new_block(validator_); + write_ref root = tm_.new_block(validator_); if (Levels > 1) { internal_node n = to_node(root); @@ -424,13 +424,13 @@ namespace persistent_data { template btree:: - btree(typename transaction_manager::ptr tm, + btree(transaction_manager &tm, block_address root, typename ValueTraits::ref_counter rc) : tm_(tm), destroy_(false), root_(root), - internal_rc_(tm->get_sm()), + internal_rc_(tm.get_sm()), rc_(rc), validator_(new btree_node_validator) { @@ -559,7 +559,7 @@ namespace persistent_data { typename btree::ptr btree::clone() const { - tm_->get_sm()->inc(root_); + tm_.get_sm()->inc(root_); return ptr(new btree(tm_, root_, rc_)); } @@ -635,13 +635,13 @@ namespace persistent_data { node_type type; unsigned nr_left, nr_right; - write_ref left = tm_->new_block(validator_); + write_ref left = tm_.new_block(validator_); node_ref l = to_node(left); l.set_nr_entries(0); l.set_max_entries(); l.set_value_size(sizeof(typename ValueTraits::disk_type)); - write_ref right = tm_->new_block(validator_); + write_ref right = tm_.new_block(validator_); node_ref r = to_node(right); r.set_nr_entries(0); r.set_max_entries(); @@ -695,7 +695,7 @@ namespace persistent_data { node_ref l = spine.template get_node(); block_address left = spine.get_block(); - write_ref right = tm_->new_block(validator_); + write_ref right = tm_.new_block(validator_); node_ref r = to_node(right); unsigned nr_left = l.get_nr_entries() / 2; @@ -822,14 +822,14 @@ namespace persistent_data { { using namespace btree_detail; - read_ref blk = tm_->read_lock(b, validator_); + read_ref blk = tm_.read_lock(b, validator_); internal_node o = to_node(blk); // FIXME: use a switch statement if (o.get_type() == INTERNAL) { if (v.visit_internal(loc, o)) { for (unsigned i = 0; i < o.get_nr_entries(); i++) - tm_->prefetch(o.value_at(i)); + tm_.prefetch(o.value_at(i)); for (unsigned i = 0; i < o.get_nr_entries(); i++) { node_location loc2(loc); diff --git a/persistent-data/space-maps/disk.cc b/persistent-data/space-maps/disk.cc index a1ec321..c9ca9ea 100644 --- a/persistent-data/space-maps/disk.cc +++ b/persistent-data/space-maps/disk.cc @@ -98,7 +98,7 @@ namespace { typedef transaction_manager::read_ref read_ref; typedef transaction_manager::write_ref write_ref; - bitmap(transaction_manager::ptr tm, + bitmap(transaction_manager &tm, index_entry const &ie, bcache::validator::ptr v) : tm_(tm), @@ -107,7 +107,7 @@ namespace { } ref_t lookup(unsigned b) const { - read_ref rr = tm_->read_lock(ie_.blocknr_, validator_); + read_ref rr = tm_.read_lock(ie_.blocknr_, validator_); void const *bits = bitmap_data(rr); ref_t b1 = test_bit_le(bits, b * 2); ref_t b2 = test_bit_le(bits, b * 2 + 1); @@ -117,7 +117,7 @@ namespace { } void insert(unsigned b, ref_t n) { - write_ref wr = tm_->shadow(ie_.blocknr_, validator_).first; + write_ref wr = tm_.shadow(ie_.blocknr_, validator_).first; void *bits = bitmap_data(wr); bool was_free = !test_bit_le(bits, b * 2) && !test_bit_le(bits, b * 2 + 1); if (n == 1 || n == 3) @@ -158,7 +158,7 @@ namespace { } void iterate(block_address offset, block_address hi, space_map::iterator &it) const { - read_ref rr = tm_->read_lock(ie_.blocknr_, validator_); + read_ref rr = tm_.read_lock(ie_.blocknr_, validator_); void const *bits = bitmap_data(rr); for (unsigned b = 0; b < hi; b++) { @@ -181,7 +181,7 @@ namespace { return h + 1; } - transaction_manager::ptr tm_; + transaction_manager &tm_; bcache::validator::ptr validator_; index_entry ie_; @@ -241,7 +241,7 @@ namespace { typedef transaction_manager::write_ref write_ref; sm_disk(index_store::ptr indexes, - transaction_manager::ptr tm) + transaction_manager &tm) : tm_(tm), bitmap_validator_(new bitmap_block_validator), indexes_(indexes), @@ -251,7 +251,7 @@ namespace { } sm_disk(index_store::ptr indexes, - transaction_manager::ptr tm, + transaction_manager &tm, sm_root const &root) : tm_(tm), bitmap_validator_(new bitmap_block_validator), @@ -354,7 +354,7 @@ namespace { indexes_->resize(bitmap_count); for (block_address i = old_bitmap_count; i < bitmap_count; i++) { - write_ref wr = tm_->new_block(bitmap_validator_); + write_ref wr = tm_.new_block(bitmap_validator_); index_entry ie; ie.blocknr_ = wr.get_location(); @@ -437,7 +437,7 @@ namespace { } protected: - transaction_manager::ptr get_tm() const { + transaction_manager &get_tm() const { return tm_; } @@ -501,7 +501,7 @@ namespace { ref_counts_.remove(key); } - transaction_manager::ptr tm_; + transaction_manager &tm_; bcache::validator::ptr bitmap_validator_; index_store::ptr indexes_; block_address nr_blocks_; @@ -544,12 +544,12 @@ namespace { public: typedef boost::shared_ptr ptr; - btree_index_store(transaction_manager::ptr tm) + btree_index_store(transaction_manager &tm) : tm_(tm), bitmaps_(tm, index_entry_traits::ref_counter()) { } - btree_index_store(transaction_manager::ptr tm, + btree_index_store(transaction_manager &tm, block_address root) : tm_(tm), bitmaps_(tm, root, index_entry_traits::ref_counter()) { @@ -592,7 +592,7 @@ namespace { } private: - transaction_manager::ptr tm_; + transaction_manager &tm_; btree<1, index_entry_traits> bitmaps_; }; @@ -600,13 +600,13 @@ namespace { public: typedef boost::shared_ptr ptr; - metadata_index_store(transaction_manager::ptr tm) + metadata_index_store(transaction_manager &tm) : tm_(tm) { - block_manager<>::write_ref wr = tm_->new_block(index_validator()); + block_manager<>::write_ref wr = tm_.new_block(index_validator()); bitmap_root_ = wr.get_location(); } - metadata_index_store(transaction_manager::ptr tm, block_address root, block_address nr_indexes) + metadata_index_store(transaction_manager &tm, block_address root, block_address nr_indexes) : tm_(tm), bitmap_root_(root) { resize(nr_indexes); @@ -627,7 +627,7 @@ namespace { virtual void commit_ies() { std::pair::write_ref, bool> p = - tm_->shadow(bitmap_root_, index_validator()); + tm_.shadow(bitmap_root_, index_validator()); bitmap_root_ = p.first.get_location(); metadata_index *mdi = reinterpret_cast(p.first.data()); @@ -661,14 +661,14 @@ namespace { private: void load_ies() { block_manager<>::read_ref rr = - tm_->read_lock(bitmap_root_, index_validator()); + tm_.read_lock(bitmap_root_, index_validator()); metadata_index const *mdi = reinterpret_cast(rr.data()); for (unsigned i = 0; i < entries_.size(); i++) index_entry_traits::unpack(*(mdi->index + i), entries_[i]); } - transaction_manager::ptr tm_; + transaction_manager &tm_; block_address bitmap_root_; std::vector entries_; }; @@ -677,7 +677,7 @@ namespace { //---------------------------------------------------------------- checked_space_map::ptr -persistent_data::create_disk_sm(transaction_manager::ptr tm, +persistent_data::create_disk_sm(transaction_manager &tm, block_address nr_blocks) { index_store::ptr store(new btree_index_store(tm)); @@ -688,7 +688,7 @@ persistent_data::create_disk_sm(transaction_manager::ptr tm, } checked_space_map::ptr -persistent_data::open_disk_sm(transaction_manager::ptr tm, void *root) +persistent_data::open_disk_sm(transaction_manager &tm, void *root) { sm_root_disk d; sm_root v; @@ -700,7 +700,7 @@ persistent_data::open_disk_sm(transaction_manager::ptr tm, void *root) } checked_space_map::ptr -persistent_data::create_metadata_sm(transaction_manager::ptr tm, block_address nr_blocks) +persistent_data::create_metadata_sm(transaction_manager &tm, block_address nr_blocks) { index_store::ptr store(new metadata_index_store(tm)); checked_space_map::ptr sm(new sm_disk(store, tm)); @@ -711,7 +711,7 @@ persistent_data::create_metadata_sm(transaction_manager::ptr tm, block_address n } checked_space_map::ptr -persistent_data::open_metadata_sm(transaction_manager::ptr tm, void *root) +persistent_data::open_metadata_sm(transaction_manager &tm, void *root) { sm_root_disk d; sm_root v; diff --git a/persistent-data/space-maps/disk.h b/persistent-data/space-maps/disk.h index 5241419..0a69f04 100644 --- a/persistent-data/space-maps/disk.h +++ b/persistent-data/space-maps/disk.h @@ -26,16 +26,16 @@ namespace persistent_data { checked_space_map::ptr - create_disk_sm(transaction_manager::ptr tm, block_address nr_blocks); + create_disk_sm(transaction_manager &tm, block_address nr_blocks); checked_space_map::ptr - open_disk_sm(transaction_manager::ptr tm, void *root); + open_disk_sm(transaction_manager &tm, void *root); checked_space_map::ptr - create_metadata_sm(transaction_manager::ptr tm, block_address nr_blocks); + create_metadata_sm(transaction_manager &tm, block_address nr_blocks); checked_space_map::ptr - open_metadata_sm(transaction_manager::ptr tm, void *root); + open_metadata_sm(transaction_manager &tm, void *root); } //---------------------------------------------------------------- diff --git a/thin-provisioning/metadata.cc b/thin-provisioning/metadata.cc index 8725b17..098314c 100644 --- a/thin-provisioning/metadata.cc +++ b/thin-provisioning/metadata.cc @@ -71,36 +71,36 @@ metadata::metadata(std::string const &dev_path, open_type ot, if (sb_.version_ != 1) throw runtime_error("unknown metadata version"); - metadata_sm_ = open_metadata_sm(tm_, &sb_.metadata_space_map_root_); + metadata_sm_ = open_metadata_sm(*tm_, &sb_.metadata_space_map_root_); tm_->set_sm(metadata_sm_); - data_sm_ = open_disk_sm(tm_, static_cast(&sb_.data_space_map_root_)); + data_sm_ = open_disk_sm(*tm_, static_cast(&sb_.data_space_map_root_)); details_ = device_tree::ptr( - new device_tree(tm_, sb_.device_details_root_, + new device_tree(*tm_, sb_.device_details_root_, device_tree_detail::device_details_traits::ref_counter())); mappings_top_level_ = dev_tree::ptr( - new dev_tree(tm_, sb_.data_mapping_root_, + new dev_tree(*tm_, sb_.data_mapping_root_, mapping_tree_detail::mtree_ref_counter(tm_))); mappings_ = mapping_tree::ptr( - new mapping_tree(tm_, sb_.data_mapping_root_, + new mapping_tree(*tm_, sb_.data_mapping_root_, mapping_tree_detail::block_time_ref_counter(data_sm_))); break; case CREATE: tm_ = open_tm(open_bm(dev_path, block_manager<>::READ_WRITE)); space_map::ptr core = tm_->get_sm(); - metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks()); + metadata_sm_ = create_metadata_sm(*tm_, tm_->get_bm()->get_nr_blocks()); copy_space_maps(metadata_sm_, core); tm_->set_sm(metadata_sm_); - data_sm_ = create_disk_sm(tm_, nr_data_blocks); - details_ = device_tree::ptr(new device_tree(tm_, device_tree_detail::device_details_traits::ref_counter())); - mappings_ = mapping_tree::ptr(new mapping_tree(tm_, + data_sm_ = create_disk_sm(*tm_, nr_data_blocks); + details_ = device_tree::ptr(new device_tree(*tm_, device_tree_detail::device_details_traits::ref_counter())); + mappings_ = mapping_tree::ptr(new mapping_tree(*tm_, mapping_tree_detail::block_time_ref_counter(data_sm_))); - mappings_top_level_ = dev_tree::ptr(new dev_tree(tm_, mappings_->get_root(), + mappings_top_level_ = dev_tree::ptr(new dev_tree(*tm_, mappings_->get_root(), mapping_tree_detail::mtree_ref_counter(tm_))); ::memset(&sb_, 0, sizeof(sb_)); @@ -125,11 +125,11 @@ metadata::metadata(std::string const &dev_path, block_address metadata_snap) //metadata_sm_ = open_metadata_sm(tm_, &sb_.metadata_space_map_root_); //tm_->set_sm(metadata_sm_); - data_sm_ = open_disk_sm(tm_, static_cast(&sb_.data_space_map_root_)); - details_ = device_tree::ptr(new device_tree(tm_, sb_.device_details_root_, device_tree_detail::device_details_traits::ref_counter())); - mappings_top_level_ = dev_tree::ptr(new dev_tree(tm_, sb_.data_mapping_root_, + data_sm_ = open_disk_sm(*tm_, static_cast(&sb_.data_space_map_root_)); + details_ = device_tree::ptr(new device_tree(*tm_, sb_.device_details_root_, device_tree_detail::device_details_traits::ref_counter())); + mappings_top_level_ = dev_tree::ptr(new dev_tree(*tm_, sb_.data_mapping_root_, mapping_tree_detail::mtree_ref_counter(tm_))); - mappings_ = mapping_tree::ptr(new mapping_tree(tm_, sb_.data_mapping_root_, + mappings_ = mapping_tree::ptr(new mapping_tree(*tm_, sb_.data_mapping_root_, mapping_tree_detail::block_time_ref_counter(data_sm_))); } @@ -146,29 +146,29 @@ metadata::metadata(block_manager<>::ptr bm, open_type ot, if (sb_.version_ != 1) throw runtime_error("unknown metadata version"); - metadata_sm_ = open_metadata_sm(tm_, &sb_.metadata_space_map_root_); + metadata_sm_ = open_metadata_sm(*tm_, &sb_.metadata_space_map_root_); tm_->set_sm(metadata_sm_); - data_sm_ = open_disk_sm(tm_, static_cast(&sb_.data_space_map_root_)); - details_ = device_tree::ptr(new device_tree(tm_, sb_.device_details_root_, device_tree_detail::device_details_traits::ref_counter())); - mappings_top_level_ = dev_tree::ptr(new dev_tree(tm_, sb_.data_mapping_root_, + data_sm_ = open_disk_sm(*tm_, static_cast(&sb_.data_space_map_root_)); + details_ = device_tree::ptr(new device_tree(*tm_, sb_.device_details_root_, device_tree_detail::device_details_traits::ref_counter())); + mappings_top_level_ = dev_tree::ptr(new dev_tree(*tm_, sb_.data_mapping_root_, mapping_tree_detail::mtree_ref_counter(tm_))); - mappings_ = mapping_tree::ptr(new mapping_tree(tm_, sb_.data_mapping_root_, + mappings_ = mapping_tree::ptr(new mapping_tree(*tm_, sb_.data_mapping_root_, mapping_tree_detail::block_time_ref_counter(data_sm_))); break; case CREATE: tm_ = open_tm(bm); space_map::ptr core = tm_->get_sm(); - metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks()); + metadata_sm_ = create_metadata_sm(*tm_, tm_->get_bm()->get_nr_blocks()); copy_space_maps(metadata_sm_, core); tm_->set_sm(metadata_sm_); - data_sm_ = create_disk_sm(tm_, nr_data_blocks); - details_ = device_tree::ptr(new device_tree(tm_, device_tree_detail::device_details_traits::ref_counter())); - mappings_ = mapping_tree::ptr(new mapping_tree(tm_, + data_sm_ = create_disk_sm(*tm_, nr_data_blocks); + details_ = device_tree::ptr(new device_tree(*tm_, device_tree_detail::device_details_traits::ref_counter())); + mappings_ = mapping_tree::ptr(new mapping_tree(*tm_, mapping_tree_detail::block_time_ref_counter(data_sm_))); - mappings_top_level_ = dev_tree::ptr(new dev_tree(tm_, mappings_->get_root(), + mappings_top_level_ = dev_tree::ptr(new dev_tree(*tm_, mappings_->get_root(), mapping_tree_detail::mtree_ref_counter(tm_))); ::memset(&sb_, 0, sizeof(sb_)); diff --git a/thin-provisioning/metadata_dumper.cc b/thin-provisioning/metadata_dumper.cc index 0bd284e..db656ee 100644 --- a/thin-provisioning/metadata_dumper.cc +++ b/thin-provisioning/metadata_dumper.cc @@ -200,7 +200,7 @@ namespace { private: void emit_mappings(block_address subtree_root) { mapping_emitter me(e_); - single_mapping_tree tree(md_->tm_, subtree_root, + single_mapping_tree tree(*md_->tm_, subtree_root, mapping_tree_detail::block_time_ref_counter(md_->data_sm_)); walk_mapping_tree(tree, static_cast(me), *damage_policy_); } diff --git a/thin-provisioning/restore_emitter.cc b/thin-provisioning/restore_emitter.cc index fd1d4ab..5fae879 100644 --- a/thin-provisioning/restore_emitter.cc +++ b/thin-provisioning/restore_emitter.cc @@ -134,7 +134,7 @@ namespace { private: single_mapping_tree::ptr new_mapping_tree() { return single_mapping_tree::ptr( - new single_mapping_tree(md_->tm_, + new single_mapping_tree(*md_->tm_, mapping_tree_detail::block_time_ref_counter(md_->data_sm_))); } diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc index e4cae83..9e90699 100644 --- a/thin-provisioning/thin_check.cc +++ b/thin-provisioning/thin_check.cc @@ -194,7 +194,7 @@ namespace { out << "examining devices tree" << end_message(); { nested_output::nest _ = out.push(); - device_tree dtree(tm, sb.device_details_root_, + device_tree dtree(*tm, sb.device_details_root_, device_tree_detail::device_details_traits::ref_counter()); check_device_tree(dtree, dev_rep); } @@ -204,7 +204,7 @@ namespace { out << "examining top level of mapping tree" << end_message(); { nested_output::nest _ = out.push(); - dev_tree dtree(tm, sb.data_mapping_root_, + dev_tree dtree(*tm, sb.data_mapping_root_, mapping_tree_detail::mtree_traits::ref_counter(tm)); check_mapping_tree(dtree, mapping_rep); } @@ -213,7 +213,7 @@ namespace { out << "examining mapping tree" << end_message(); { nested_output::nest _ = out.push(); - mapping_tree mtree(tm, sb.data_mapping_root_, + mapping_tree mtree(*tm, sb.data_mapping_root_, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); check_mapping_tree(mtree, mapping_rep); } diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 59fefd3..21ecd01 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -450,7 +450,7 @@ namespace { superblock_detail::superblock sb = read_superblock(bm); - dev_tree dtree(tm, sb.data_mapping_root_, + dev_tree dtree(*tm, sb.data_mapping_root_, mapping_tree_detail::mtree_traits::ref_counter(tm)); dev_tree::key k = {*fs.snap1}; @@ -462,7 +462,7 @@ namespace { app.die(out.str()); } - single_mapping_tree snap1(tm, *snap1_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); + single_mapping_tree snap1(*tm, *snap1_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); k[0] = *fs.snap2; boost::optional snap2_root = dtree.lookup(k); @@ -473,7 +473,7 @@ namespace { app.die(out.str()); } - single_mapping_tree snap2(tm, *snap2_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); + single_mapping_tree snap2(*tm, *snap2_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); btree_visit_values(snap1, mr1, damage_v); btree_visit_values(snap2, mr2, damage_v); } diff --git a/thin-provisioning/thin_pool.cc b/thin-provisioning/thin_pool.cc index 23725d4..53940e3 100644 --- a/thin-provisioning/thin_pool.cc +++ b/thin-provisioning/thin_pool.cc @@ -122,7 +122,7 @@ thin_pool::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(md_->tm_, + single_mapping_tree::ptr new_tree(new single_mapping_tree(*md_->tm_, mapping_tree_detail::block_time_ref_counter(md_->data_sm_))); md_->mappings_top_level_->insert(key, new_tree->get_root()); md_->mappings_->set_root(md_->mappings_top_level_->get_root()); // FIXME: ugly @@ -140,7 +140,7 @@ thin_pool::create_snap(thin_dev_t dev, thin_dev_t origin) if (!mtree_root) throw std::runtime_error("unknown origin"); - single_mapping_tree otree(md_->tm_, *mtree_root, + single_mapping_tree otree(*md_->tm_, *mtree_root, mapping_tree_detail::block_time_ref_counter(md_->data_sm_)); single_mapping_tree::ptr clone(otree.clone()); diff --git a/thin-provisioning/thin_rmap.cc b/thin-provisioning/thin_rmap.cc index 70c7b38..71180b3 100644 --- a/thin-provisioning/thin_rmap.cc +++ b/thin-provisioning/thin_rmap.cc @@ -75,7 +75,7 @@ namespace { transaction_manager::ptr tm = open_tm(bm); superblock_detail::superblock sb = read_superblock(bm); - mapping_tree mtree(tm, sb.data_mapping_root_, + mapping_tree mtree(*tm, sb.data_mapping_root_, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); btree_visit_values(mtree, rv, dv); diff --git a/unit-tests/array_t.cc b/unit-tests/array_t.cc index 5d0a733..74fac04 100644 --- a/unit-tests/array_t.cc +++ b/unit-tests/array_t.cc @@ -37,7 +37,9 @@ namespace { class ArrayTests : public Test { public: ArrayTests() - : tm_(create_tm()) { + : bm_(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)), + sm_(new core_map(NR_BLOCKS)), + tm_(bm_, sm_) { } void @@ -76,15 +78,9 @@ namespace { array64::ptr a_; private: - static transaction_manager::ptr - create_tm() { - block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)); - space_map::ptr sm(new core_map(NR_BLOCKS)); - transaction_manager::ptr tm(new transaction_manager(bm, sm)); - return tm; - } - - transaction_manager::ptr tm_; + block_manager<>::ptr bm_; + space_map::ptr sm_; + transaction_manager tm_; }; class value_visitor { diff --git a/unit-tests/bitset_t.cc b/unit-tests/bitset_t.cc index 20d1b1f..36634f6 100644 --- a/unit-tests/bitset_t.cc +++ b/unit-tests/bitset_t.cc @@ -32,34 +32,40 @@ using namespace testing; namespace { block_address const NR_BLOCKS = 102400; - transaction_manager::ptr - create_tm() { - block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)); - space_map::ptr sm(new core_map(NR_BLOCKS)); - transaction_manager::ptr tm(new transaction_manager(bm, sm)); - return tm; - } + class BitsetTests : public Test { + public: + BitsetTests() + : bm_(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)), + sm_(new core_map(NR_BLOCKS)), + tm_(bm_, sm_) { + } - bitset::ptr - create_bitset() { - return bitset::ptr(new bitset(create_tm())); - } + bitset::ptr + create_bitset() { + return bitset::ptr(new bitset(tm_)); + } - bitset::ptr - open_bitset(block_address root, unsigned count) { - return bitset::ptr(new bitset(create_tm(), root, count)); - } + bitset::ptr + open_bitset(block_address root, unsigned count) { + return bitset::ptr(new bitset(tm_, root, count)); + } + + private: + block_manager<>::ptr bm_; + space_map::ptr sm_; + transaction_manager tm_; + }; } //---------------------------------------------------------------- -TEST(BitsetTests, create_empty_bitset) +TEST_F(BitsetTests, create_empty_bitset) { bitset::ptr bs = create_bitset(); ASSERT_THROW(bs->get(0), runtime_error); } -TEST(BitsetTests, grow_default_false) +TEST_F(BitsetTests, grow_default_false) { unsigned const COUNT = 100000; @@ -70,7 +76,7 @@ TEST(BitsetTests, grow_default_false) ASSERT_FALSE(bs->get(i)); } -TEST(BitsetTests, grow_default_true) +TEST_F(BitsetTests, grow_default_true) { unsigned const COUNT = 100000; @@ -81,7 +87,7 @@ TEST(BitsetTests, grow_default_true) ASSERT_TRUE(bs->get(i)); } -TEST(BitsetTests, grow_throws_if_actualy_asked_to_shrink) +TEST_F(BitsetTests, grow_throws_if_actualy_asked_to_shrink) { unsigned const COUNT = 100000; @@ -90,7 +96,7 @@ TEST(BitsetTests, grow_throws_if_actualy_asked_to_shrink) ASSERT_THROW(bs->grow(COUNT / 2, false), runtime_error); } -TEST(BitsetTests, multiple_grow_calls) +TEST_F(BitsetTests, multiple_grow_calls) { unsigned const COUNT = 100000; unsigned const STEP = 37; @@ -121,7 +127,7 @@ TEST(BitsetTests, multiple_grow_calls) } } -TEST(BitsetTests, set_out_of_bounds_throws) +TEST_F(BitsetTests, set_out_of_bounds_throws) { unsigned const COUNT = 100000; bitset::ptr bs = create_bitset(); @@ -131,7 +137,7 @@ TEST(BitsetTests, set_out_of_bounds_throws) ASSERT_THROW(bs->set(COUNT, true), runtime_error); } -TEST(BitsetTests, set_works) +TEST_F(BitsetTests, set_works) { unsigned const COUNT = 100000; bitset::ptr bs = create_bitset(); @@ -144,7 +150,7 @@ TEST(BitsetTests, set_works) ASSERT_THAT(bs->get(i), Eq(i % 7 ? true : false)); } -TEST(BitsetTests, reopen_works) +TEST_F(BitsetTests, reopen_works) { unsigned const COUNT = 100000; block_address root; diff --git a/unit-tests/bloom_filter_t.cc b/unit-tests/bloom_filter_t.cc index 313355e..032eb48 100644 --- a/unit-tests/bloom_filter_t.cc +++ b/unit-tests/bloom_filter_t.cc @@ -31,7 +31,7 @@ namespace { BloomFilterTests() : bm_(create_bm(NR_BLOCKS)), sm_(setup_core_map()), - tm_(new transaction_manager(bm_, sm_)) { + tm_(bm_, sm_) { } set generate_random_blocks(unsigned count, @@ -73,7 +73,7 @@ namespace { with_temp_directory dir_; block_manager<>::ptr bm_; space_map::ptr sm_; - transaction_manager::ptr tm_; + transaction_manager tm_; boost::random::mt19937 rng_; }; diff --git a/unit-tests/btree_counter_t.cc b/unit-tests/btree_counter_t.cc index adbf523..dbc36f6 100644 --- a/unit-tests/btree_counter_t.cc +++ b/unit-tests/btree_counter_t.cc @@ -25,7 +25,7 @@ namespace { BTreeCounterTests() : bm_(create_bm(NR_BLOCKS)), sm_(setup_core_map()), - tm_(new transaction_manager(bm_, sm_)) { + tm_(bm_, sm_) { } void check_nr_metadata_blocks_is_ge(unsigned n) { @@ -38,7 +38,7 @@ namespace { with_temp_directory dir_; block_manager<>::ptr bm_; space_map::ptr sm_; - transaction_manager::ptr tm_; + transaction_manager tm_; uint64_traits::ref_counter rc_; btree<1, uint64_traits>::ptr tree_; diff --git a/unit-tests/btree_damage_visitor_t.cc b/unit-tests/btree_damage_visitor_t.cc index 201d8e3..f88ee18 100644 --- a/unit-tests/btree_damage_visitor_t.cc +++ b/unit-tests/btree_damage_visitor_t.cc @@ -281,7 +281,7 @@ namespace { DamageTests() : bm_(create_bm(NR_BLOCKS)), sm_(setup_core_map()), - tm_(new transaction_manager(bm_, sm_)) { + tm_(bm_, sm_) { } virtual ~DamageTests() {} @@ -315,7 +315,7 @@ namespace { with_temp_directory dir_; block_manager<>::ptr bm_; space_map::ptr sm_; - transaction_manager::ptr tm_; + transaction_manager tm_; thing_traits::ref_counter rc_; boost::optional layout_; diff --git a/unit-tests/btree_t.cc b/unit-tests/btree_t.cc index 0d68343..13a525e 100644 --- a/unit-tests/btree_t.cc +++ b/unit-tests/btree_t.cc @@ -31,21 +31,28 @@ using namespace testing; namespace { block_address const NR_BLOCKS = 102400; - transaction_manager::ptr - create_tm() { - block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)); - space_map::ptr sm(new core_map(NR_BLOCKS)); - transaction_manager::ptr tm(new transaction_manager(bm, sm)); - return tm; - } + class BtreeTests : public Test { + public: + BtreeTests() + : bm_(new block_manager<>("./test.data", NR_BLOCKS, 4, block_manager<>::READ_WRITE)), + sm_(new core_map(NR_BLOCKS)), + tm_(bm_, sm_) { + } - btree<1, uint64_traits>::ptr - create_btree() { - uint64_traits::ref_counter rc; + btree<1, uint64_traits>::ptr + create_btree() { + uint64_traits::ref_counter rc; + + return btree<1, uint64_traits>::ptr( + new btree<1, uint64_traits>(tm_, rc)); + } + + private: + block_manager<>::ptr bm_; + space_map::ptr sm_; + transaction_manager tm_; + }; - return btree<1, uint64_traits>::ptr( - new btree<1, uint64_traits>(create_tm(), rc)); - } // Checks that a btree is well formed. // @@ -99,7 +106,7 @@ namespace { //---------------------------------------------------------------- -TEST(BtreeTests, empty_btree_contains_nothing) +TEST_F(BtreeTests, empty_btree_contains_nothing) { btree<1, uint64_traits>::ptr tree = create_btree(); check_constraints(tree); @@ -110,7 +117,7 @@ TEST(BtreeTests, empty_btree_contains_nothing) } } -TEST(BtreeTests, insert_works) +TEST_F(BtreeTests, insert_works) { unsigned const COUNT = 100000; @@ -129,7 +136,7 @@ TEST(BtreeTests, insert_works) check_constraints(tree); } -TEST(BtreeTests, insert_does_not_insert_imaginary_values) +TEST_F(BtreeTests, insert_does_not_insert_imaginary_values) { btree<1, uint64_traits>::ptr tree = create_btree(); uint64_t key[1] = {0}; @@ -156,7 +163,7 @@ TEST(BtreeTests, insert_does_not_insert_imaginary_values) check_constraints(tree); } -TEST(BtreeTests, clone) +TEST_F(BtreeTests, clone) { typedef btree<1, uint64_traits> tree64; diff --git a/unit-tests/space_map_t.cc b/unit-tests/space_map_t.cc index c87d6f4..0848909 100644 --- a/unit-tests/space_map_t.cc +++ b/unit-tests/space_map_t.cc @@ -33,278 +33,258 @@ namespace { block_address const SUPERBLOCK = 0; block_address const MAX_LOCKS = 8; - transaction_manager::ptr - create_tm() { - block_manager<>::ptr bm( - new block_manager<>("./test.data", NR_BLOCKS, MAX_LOCKS, block_manager<>::READ_WRITE)); - space_map::ptr sm(new core_map(NR_BLOCKS)); - transaction_manager::ptr tm( - new transaction_manager(bm, sm)); - return tm; - } - - struct sm_core_creator { - static space_map::ptr - create() { - return space_map::ptr(new persistent_data::core_map(NR_BLOCKS)); - } - }; - - struct sm_careful_alloc_creator { - static space_map::ptr - create() { - return create_careful_alloc_sm( - checked_space_map::ptr( - new core_map(NR_BLOCKS))); - } - }; - - struct sm_recursive_creator { - static checked_space_map::ptr - create() { - return create_recursive_sm( - checked_space_map::ptr( - new core_map(NR_BLOCKS))); - } - }; - - struct sm_disk_creator { - static persistent_space_map::ptr - create() { - transaction_manager::ptr tm = create_tm(); - return persistent_data::create_disk_sm(tm, NR_BLOCKS); + class SpaceMapTests : public Test { + public: + SpaceMapTests() + : bm_(new block_manager<>("./test.data", NR_BLOCKS, MAX_LOCKS, block_manager<>::READ_WRITE)), + sm_(new core_map(NR_BLOCKS)), + tm_(bm_, sm_) { } - static persistent_space_map::ptr - open(void *root) { - transaction_manager::ptr tm = create_tm(); - return persistent_data::open_disk_sm(tm, root); - } - }; + struct sm_core_creator { + static space_map::ptr + create(transaction_manager &tm) { + return space_map::ptr(new persistent_data::core_map(NR_BLOCKS)); + } + }; - struct sm_metadata_creator { - static persistent_space_map::ptr - create() { - transaction_manager::ptr tm = create_tm(); - return persistent_data::create_metadata_sm(tm, NR_BLOCKS); + struct sm_careful_alloc_creator { + static space_map::ptr + create(transaction_manager &tm) { + return create_careful_alloc_sm( + checked_space_map::ptr( + new core_map(NR_BLOCKS))); + } + }; + + struct sm_recursive_creator { + static checked_space_map::ptr + create(transaction_manager &tm) { + return create_recursive_sm( + checked_space_map::ptr( + new core_map(NR_BLOCKS))); + } + }; + + struct sm_disk_creator { + static persistent_space_map::ptr + create(transaction_manager &tm) { + return persistent_data::create_disk_sm(tm, NR_BLOCKS); + } + + static persistent_space_map::ptr + open(transaction_manager &tm, void *root) { + return persistent_data::open_disk_sm(tm, root); + } + }; + + struct sm_metadata_creator { + static persistent_space_map::ptr + create(transaction_manager &tm) { + return persistent_data::create_metadata_sm(tm, NR_BLOCKS); + } + + static persistent_space_map::ptr + open(transaction_manager &tm, void *root) { + return persistent_data::open_metadata_sm(tm, root); + } + }; + + //-------------------------------- + + void test_get_nr_blocks(space_map::ptr sm) { + ASSERT_THAT(sm->get_nr_blocks(), Eq(NR_BLOCKS)); } - static persistent_space_map::ptr - open(void *root) { - transaction_manager::ptr tm = create_tm(); - return persistent_data::open_metadata_sm(tm, root); + void test_get_nr_free(space_map::ptr sm) { + ASSERT_THAT(sm->get_nr_free(), Eq(NR_BLOCKS)); + + for (unsigned i = 0; i < NR_BLOCKS; i++) { + boost::optional mb = sm->new_block(); + ASSERT_TRUE(mb); + ASSERT_THAT(sm->get_nr_free(), Eq(NR_BLOCKS - i - 1)); + } + + for (unsigned i = 0; i < NR_BLOCKS; i++) { + sm->dec(i); + ASSERT_THAT(sm->get_nr_free(), Eq(i + 1)); + } } - }; - //-------------------------------- + void test_runs_out_of_space(space_map::ptr sm) { + boost::optional mb; - void test_get_nr_blocks(space_map::ptr sm) - { - ASSERT_THAT(sm->get_nr_blocks(), Eq(NR_BLOCKS)); - } + for (unsigned i = 0; i < NR_BLOCKS; i++) + mb = sm->new_block(); - void test_get_nr_free(space_map::ptr sm) - { - ASSERT_THAT(sm->get_nr_free(), Eq(NR_BLOCKS)); + mb = sm->new_block(); + ASSERT_FALSE(mb); + } - for (unsigned i = 0; i < NR_BLOCKS; i++) { + void test_inc_and_dec(space_map::ptr sm) { + block_address b = 63; + + for (unsigned i = 0; i < 50; i++) { + ASSERT_THAT(sm->get_count(b), Eq(i)); + sm->inc(b); + } + + for (unsigned i = 50; i > 0; i--) { + ASSERT_THAT(sm->get_count(b), Eq(i)); + sm->dec(b); + } + } + + void test_not_allocated_twice(space_map::ptr sm) { boost::optional mb = sm->new_block(); ASSERT_TRUE(mb); - ASSERT_THAT(sm->get_nr_free(), Eq(NR_BLOCKS - i - 1)); + + for (;;) { + boost::optional b = sm->new_block(); + if (!b) + break; + + if (b) + ASSERT_TRUE(*b != *mb); + } } - for (unsigned i = 0; i < NR_BLOCKS; i++) { - sm->dec(i); - ASSERT_THAT(sm->get_nr_free(), Eq(i + 1)); - } - } - - void test_runs_out_of_space(space_map::ptr sm) - { - boost::optional mb; - - for (unsigned i = 0; i < NR_BLOCKS; i++) - mb = sm->new_block(); - - mb = sm->new_block(); - ASSERT_FALSE(mb); - } - - void test_inc_and_dec(space_map::ptr sm) - { - block_address b = 63; - - for (unsigned i = 0; i < 50; i++) { - ASSERT_THAT(sm->get_count(b), Eq(i)); - sm->inc(b); + void test_set_count(space_map::ptr sm) { + sm->set_count(43, 5); + ASSERT_THAT(sm->get_count(43), Eq(5u)); } - for (unsigned i = 50; i > 0; i--) { - ASSERT_THAT(sm->get_count(b), Eq(i)); - sm->dec(b); - } - } + void test_set_affects_nr_allocated(space_map::ptr sm) { + for (unsigned i = 0; i < NR_BLOCKS; i++) { + sm->set_count(i, 1); + ASSERT_THAT(sm->get_nr_free(), Eq(NR_BLOCKS - i - 1)); + } - void test_not_allocated_twice(space_map::ptr sm) - { - boost::optional mb = sm->new_block(); - ASSERT_TRUE(mb); - - for (;;) { - boost::optional b = sm->new_block(); - if (!b) - break; - - if (b) - ASSERT_TRUE(*b != *mb); - } - } - - void test_set_count(space_map::ptr sm) - { - sm->set_count(43, 5); - ASSERT_THAT(sm->get_count(43), Eq(5u)); - } - - void test_set_affects_nr_allocated(space_map::ptr sm) - { - for (unsigned i = 0; i < NR_BLOCKS; i++) { - sm->set_count(i, 1); - ASSERT_THAT(sm->get_nr_free(), Eq(NR_BLOCKS - i - 1)); + for (unsigned i = 0; i < NR_BLOCKS; i++) { + sm->set_count(i, 0); + ASSERT_THAT(sm->get_nr_free(), Eq(i + 1)); + } } - for (unsigned i = 0; i < NR_BLOCKS; i++) { - sm->set_count(i, 0); - ASSERT_THAT(sm->get_nr_free(), Eq(i + 1)); - } - } - - // Ref counts below 3 gets stored as bitmaps, above 3 they go into - // a btree with uint32_t values. Worth checking this thoroughly, - // especially for the metadata format which may have complications - // due to recursion. - void test_high_ref_counts(space_map::ptr sm) - { - srand(1234); - for (unsigned i = 0; i < NR_BLOCKS; i++) - sm->set_count(i, rand() % 6789); - sm->commit(); - - for (unsigned i = 0; i < NR_BLOCKS; i++) { - sm->inc(i); - sm->inc(i); - if (i % 1000) - sm->commit(); - } - sm->commit(); - - srand(1234); - for (unsigned i = 0; i < NR_BLOCKS; i++) - ASSERT_THAT(sm->get_count(i), Eq((rand() % 6789u) + 2u)); - - for (unsigned i = 0; i < NR_BLOCKS; i++) - sm->dec(i); - - srand(1234); - for (unsigned i = 0; i < NR_BLOCKS; i++) - ASSERT_THAT(sm->get_count(i), Eq((rand() % 6789u) + 1u)); - } - - template - void test_sm_reopen() - { - unsigned char buffer[128]; - - { - persistent_space_map::ptr sm = SMCreator::create(); - for (unsigned i = 0, step = 1; i < NR_BLOCKS; i += step, step++) - sm->inc(i); + // Ref counts below 3 gets stored as bitmaps, above 3 they go into + // a btree with uint32_t values. Worth checking this thoroughly, + // especially for the metadata format which may have complications + // due to recursion. + void test_high_ref_counts(space_map::ptr sm) { + srand(1234); + for (unsigned i = 0; i < NR_BLOCKS; i++) + sm->set_count(i, rand() % 6789); sm->commit(); - ASSERT_THAT(sm->root_size(), Le(sizeof(buffer))); - sm->copy_root(buffer, sizeof(buffer)); + for (unsigned i = 0; i < NR_BLOCKS; i++) { + sm->inc(i); + sm->inc(i); + if (i % 1000) + sm->commit(); + } + sm->commit(); + + srand(1234); + for (unsigned i = 0; i < NR_BLOCKS; i++) + ASSERT_THAT(sm->get_count(i), Eq((rand() % 6789u) + 2u)); + + for (unsigned i = 0; i < NR_BLOCKS; i++) + sm->dec(i); + + srand(1234); + for (unsigned i = 0; i < NR_BLOCKS; i++) + ASSERT_THAT(sm->get_count(i), Eq((rand() % 6789u) + 1u)); } - { - persistent_space_map::ptr sm = SMCreator::open(buffer); + template + void test_sm_reopen() { + unsigned char buffer[128]; - for (unsigned i = 0, step = 1; i < NR_BLOCKS; i += step, step++) - ASSERT_THAT(sm->get_count(i), Eq(1u)); + { + persistent_space_map::ptr sm = SMCreator::create(tm_); + for (unsigned i = 0, step = 1; i < NR_BLOCKS; i += step, step++) + sm->inc(i); + sm->commit(); + + ASSERT_THAT(sm->root_size(), Le(sizeof(buffer))); + sm->copy_root(buffer, sizeof(buffer)); + } + + { + persistent_space_map::ptr sm = SMCreator::open(tm_, buffer); + + for (unsigned i = 0, step = 1; i < NR_BLOCKS; i += step, step++) + ASSERT_THAT(sm->get_count(i), Eq(1u)); + } } - } - typedef void (*sm_test)(space_map::ptr); - - template - void do_tests(sm_test (&tests)[NTests]) - { - for (unsigned t = 0; t < NTests; t++) { - space_map::ptr sm = SMCreator::create(); - tests[t](sm); + template + void do_tests() { + test_get_nr_blocks(SMCreator::create(tm_)); + test_get_nr_free(SMCreator::create(tm_)); + test_runs_out_of_space(SMCreator::create(tm_)); + test_inc_and_dec(SMCreator::create(tm_)); + test_not_allocated_twice(SMCreator::create(tm_)); + test_set_count(SMCreator::create(tm_)); + test_set_affects_nr_allocated(SMCreator::create(tm_)); + test_high_ref_counts(SMCreator::create(tm_)); } - } - sm_test space_map_tests[] = { - test_get_nr_blocks, - test_get_nr_free, - test_runs_out_of_space, - test_inc_and_dec, - test_not_allocated_twice, - test_set_count, - test_set_affects_nr_allocated, - test_high_ref_counts + void + copy_space_maps(space_map::ptr lhs, space_map::ptr rhs) { + for (block_address b = 0; b < rhs->get_nr_blocks(); b++) { + uint32_t count = rhs->get_count(b); + if (count > 0) + lhs->set_count(b, rhs->get_count(b)); + } + } + + block_manager<>::ptr bm_; + space_map::ptr sm_; + transaction_manager tm_; }; - - void - copy_space_maps(space_map::ptr lhs, space_map::ptr rhs) { - for (block_address b = 0; b < rhs->get_nr_blocks(); b++) { - uint32_t count = rhs->get_count(b); - if (count > 0) - lhs->set_count(b, rhs->get_count(b)); - } - } } //---------------------------------------------------------------- -TEST(SpaceMapTests, test_sm_core) +TEST_F(SpaceMapTests, test_sm_core) { - do_tests(space_map_tests); + do_tests(); } -TEST(SpaceMapTests, test_sm_careful_alloc) +TEST_F(SpaceMapTests, test_sm_careful_alloc) { - do_tests(space_map_tests); + do_tests(); } -TEST(SpaceMapTests, test_sm_recursive) +TEST_F(SpaceMapTests, test_sm_recursive) { - do_tests(space_map_tests); + do_tests(); } -TEST(SpaceMapTests, test_sm_disk) +TEST_F(SpaceMapTests, test_sm_disk) { - do_tests(space_map_tests); + do_tests(); test_sm_reopen(); } -TEST(SpaceMapTests, test_sm_metadata) +TEST_F(SpaceMapTests, test_sm_metadata) { - do_tests(space_map_tests); + do_tests(); test_sm_reopen(); } -TEST(SpaceMapTests, test_metadata_and_disk) +TEST_F(SpaceMapTests, test_metadata_and_disk) { block_manager<>::ptr bm( new block_manager<>("./test.data", NR_BLOCKS, MAX_LOCKS, block_manager<>::READ_WRITE)); space_map::ptr core_sm(new core_map(NR_BLOCKS)); transaction_manager::ptr tm(new transaction_manager(bm, core_sm)); - persistent_space_map::ptr metadata_sm = persistent_data::create_metadata_sm(tm, NR_BLOCKS); + persistent_space_map::ptr metadata_sm = persistent_data::create_metadata_sm(*tm, NR_BLOCKS); copy_space_maps(metadata_sm, core_sm); tm->set_sm(metadata_sm); - persistent_space_map::ptr data_sm_ = create_disk_sm(tm, NR_BLOCKS * 2); + persistent_space_map::ptr data_sm_ = create_disk_sm(*tm, NR_BLOCKS * 2); } //---------------------------------------------------------------- From d17ad86a88083b7d22c2ee1e53e673875cd53751 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Aug 2014 11:23:29 +0100 Subject: [PATCH 114/165] [*_restore] Use a little wrapper class for the expat XML_Parser to ensure it gets destroyed. --- base/xml_utils.h | 24 ++++++++++++++++++++++++ caching/xml_format.cc | 15 ++++++--------- era/xml_format.cc | 16 ++++++---------- thin-provisioning/xml_format.cc | 15 ++++++--------- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/base/xml_utils.h b/base/xml_utils.h index 279f43f..892438e 100644 --- a/base/xml_utils.h +++ b/base/xml_utils.h @@ -3,6 +3,7 @@ #include #include +#include #include using namespace std; @@ -10,6 +11,29 @@ using namespace std; //---------------------------------------------------------------- namespace xml_utils { + // Simple wrapper to ensure the parser gets freed if an exception + // is thrown during parsing. + class xml_parser { + public: + xml_parser() + : parser_(XML_ParserCreate(NULL)) { + + if (!parser_) + throw runtime_error("couldn't create xml parser"); + } + + ~xml_parser() { + XML_ParserFree(parser_); + } + + XML_Parser get_parser() { + return parser_; + } + + private: + XML_Parser parser_; + }; + typedef std::map attributes; void build_attributes(attributes &a, char const **attr); diff --git a/caching/xml_format.cc b/caching/xml_format.cc index d36d7b0..57764e7 100644 --- a/caching/xml_format.cc +++ b/caching/xml_format.cc @@ -5,7 +5,6 @@ #include "base/xml_utils.h" #include -#include using namespace caching; using namespace persistent_data; @@ -239,12 +238,10 @@ caching::create_xml_emitter(ostream &out) void caching::parse_xml(istream &in, emitter::ptr e) { - XML_Parser parser = XML_ParserCreate(NULL); - if (!parser) - throw runtime_error("couldn't create xml parser"); + xml_parser p; - XML_SetUserData(parser, e.get()); - XML_SetElementHandler(parser, start_tag, end_tag); + XML_SetUserData(p.get_parser(), e.get()); + XML_SetElementHandler(p.get_parser(), start_tag, end_tag); while (!in.eof()) { char buffer[4096]; @@ -252,12 +249,12 @@ caching::parse_xml(istream &in, emitter::ptr e) size_t len = in.gcount(); int done = in.eof(); - if (!XML_Parse(parser, buffer, len, done)) { + if (!XML_Parse(p.get_parser(), buffer, len, done)) { ostringstream out; out << "Parse error at line " - << XML_GetCurrentLineNumber(parser) + << XML_GetCurrentLineNumber(p.get_parser()) << ":\n" - << XML_ErrorString(XML_GetErrorCode(parser)) + << XML_ErrorString(XML_GetErrorCode(p.get_parser())) << endl; throw runtime_error(out.str()); } diff --git a/era/xml_format.cc b/era/xml_format.cc index 876356b..eeb7903 100644 --- a/era/xml_format.cc +++ b/era/xml_format.cc @@ -3,8 +3,6 @@ #include "base/indented_stream.h" #include "base/xml_utils.h" -#include - using namespace boost; using namespace era; using namespace persistent_data; @@ -163,12 +161,10 @@ era::create_xml_emitter(std::ostream &out) void era::parse_xml(std::istream &in, emitter::ptr e) { - XML_Parser parser = XML_ParserCreate(NULL); - if (!parser) - throw runtime_error("couldn't create xml parser"); + xml_parser p; - XML_SetUserData(parser, e.get()); - XML_SetElementHandler(parser, start_tag, end_tag); + XML_SetUserData(p.get_parser(), e.get()); + XML_SetElementHandler(p.get_parser(), start_tag, end_tag); while (!in.eof()) { char buffer[4096]; @@ -176,12 +172,12 @@ era::parse_xml(std::istream &in, emitter::ptr e) size_t len = in.gcount(); int done = in.eof(); - if (!XML_Parse(parser, buffer, len, done)) { + if (!XML_Parse(p.get_parser(), buffer, len, done)) { ostringstream out; out << "Parse error at line " - << XML_GetCurrentLineNumber(parser) + << XML_GetCurrentLineNumber(p.get_parser()) << ":\n" - << XML_ErrorString(XML_GetErrorCode(parser)) + << XML_ErrorString(XML_GetErrorCode(p.get_parser())) << endl; throw runtime_error(out.str()); } diff --git a/thin-provisioning/xml_format.cc b/thin-provisioning/xml_format.cc index 488fac5..f300312 100644 --- a/thin-provisioning/xml_format.cc +++ b/thin-provisioning/xml_format.cc @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -221,12 +220,10 @@ void tp::parse_xml(std::istream &in, emitter::ptr e, size_t input_length, base::progress_monitor::ptr monitor) { - XML_Parser parser = XML_ParserCreate(NULL); - if (!parser) - throw runtime_error("couldn't create xml parser"); + xml_parser p; - XML_SetUserData(parser, e.get()); - XML_SetElementHandler(parser, start_tag, end_tag); + XML_SetUserData(p.get_parser(), e.get()); + XML_SetElementHandler(p.get_parser(), start_tag, end_tag); size_t total = 0; @@ -236,12 +233,12 @@ tp::parse_xml(std::istream &in, emitter::ptr e, size_t len = in.gcount(); int done = in.eof(); - if (!XML_Parse(parser, buffer, len, done)) { + if (!XML_Parse(p.get_parser(), buffer, len, done)) { ostringstream out; out << "Parse error at line " - << XML_GetCurrentLineNumber(parser) + << XML_GetCurrentLineNumber(p.get_parser()) << ":\n" - << XML_ErrorString(XML_GetErrorCode(parser)) + << XML_ErrorString(XML_GetErrorCode(p.get_parser())) << endl; throw runtime_error(out.str()); } From 7f6bdf2acf7b9bd202a1bae44cff2ae0c57546af Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Aug 2014 11:24:07 +0100 Subject: [PATCH 115/165] Add callgrind files to the ignore file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fd2504f..00692a7 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ config.cache config.log config.status configure + +callgrind.* \ No newline at end of file From e31ffe087418a9cac914d3975eb485d0e584c20f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Aug 2014 11:26:18 +0100 Subject: [PATCH 116/165] update ignore file --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 00692a7..3085816 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,10 @@ test.data cachegrind.* \#*\# +core thin_check +thin_delta thin_dump thin_restore thin_repair @@ -25,6 +27,7 @@ cache_metadata_size era_check era_dump era_invalidate +era_restore *.metadata bad-metadata From 828f654800d7a96ce2abcde28ffa2f5af93e0623 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Aug 2014 13:05:21 +0100 Subject: [PATCH 117/165] [*_restore] Add progress bar to cache_restore and era_restore. A lot of refactoring common code between the restore tools. --- CHANGES | 4 +- Makefile.in | 10 ++++- base/progress_monitor.cc | 8 ++-- base/progress_monitor.h | 7 ++-- base/xml_utils.cc | 63 ++++++++++++++++++++++++++++++- base/xml_utils.h | 7 ++++ caching/cache_restore.cc | 34 +++++++++++++++-- caching/xml_format.cc | 8 +++- caching/xml_format.h | 4 +- era/era_restore.cc | 21 ++++++++--- era/xml_format.cc | 19 +--------- era/xml_format.h | 5 ++- features/cache_restore.feature | 17 +++++++++ features/era_restore.feature | 18 ++++++++- features/thin_restore.feature | 14 +++++++ thin-provisioning/thin_restore.cc | 24 +----------- thin-provisioning/xml_format.cc | 25 +----------- thin-provisioning/xml_format.h | 3 +- 18 files changed, 199 insertions(+), 92 deletions(-) diff --git a/CHANGES b/CHANGES index 9871c86..0d1d79d 100644 --- a/CHANGES +++ b/CHANGES @@ -4,9 +4,9 @@ v0.4 - All tools switch to using libaio. This gives a large performance boost, especially to the write focused tools like thin_restore. -- Added a progress monitor to thin_restore +- Added a progress monitor to thin_restore, cache_restore and era_restore -- Added a --quiet/-q option to thin_restore to turn off the progress bar +- Added a --quiet/-q option to *_restore to turn off the progress bar - Removed variable hint size support from cache tools. The kernel still only supports a fixed 32bit width. This will have a side diff --git a/Makefile.in b/Makefile.in index 9f95eea..ed9e585 100644 --- a/Makefile.in +++ b/Makefile.in @@ -178,6 +178,7 @@ THIN_CHECK_SOURCE=\ \ base/error_state.cc \ base/endian_utils.cc \ + base/progress_monitor.cc \ base/xml_utils.cc \ \ persistent-data/checksum.cc \ @@ -202,6 +203,7 @@ THIN_DELTA_SOURCE=\ \ base/error_state.cc \ base/endian_utils.cc \ + base/progress_monitor.cc \ base/xml_utils.cc \ \ persistent-data/checksum.cc \ @@ -269,11 +271,11 @@ thin_restore: $(THIN_RESTORE_OBJECTS) thin-provisioning/thin_restore.o thin_check: $(THIN_CHECK_OBJECTS) thin-provisioning/thin_check.o @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) thin_delta: $(THIN_DELTA_OBJECTS) thin-provisioning/thin_delta.o @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) + $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) thin_rmap: $(THIN_RMAP_OBJECTS) thin-provisioning/thin_rmap.o @echo " [LD] $@" @@ -292,6 +294,7 @@ CACHE_CHECK_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/progress_monitor.cc \ base/xml_utils.cc \ \ persistent-data/checksum.cc \ @@ -354,6 +357,7 @@ ERA_CHECK_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/progress_monitor.cc \ base/xml_utils.cc \ \ era/writeset_tree.cc \ @@ -421,6 +425,7 @@ ERA_INVALIDATE_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/progress_monitor.cc \ base/xml_utils.cc \ \ era/writeset_tree.cc \ @@ -455,6 +460,7 @@ ERA_RESTORE_SOURCE=\ base/base64.cc \ base/error_state.cc \ base/endian_utils.cc \ + base/progress_monitor.cc \ base/xml_utils.cc \ \ era/writeset_tree.cc \ diff --git a/base/progress_monitor.cc b/base/progress_monitor.cc index 4e6792d..e6bf572 100644 --- a/base/progress_monitor.cc +++ b/base/progress_monitor.cc @@ -60,16 +60,16 @@ namespace { //---------------------------------------------------------------- -base::progress_monitor::ptr +std::auto_ptr base::create_progress_bar(std::string const &title) { - return progress_monitor::ptr(new progress_bar(title)); + return auto_ptr(new progress_bar(title)); } -base::progress_monitor::ptr +std::auto_ptr base::create_quiet_progress_monitor() { - return progress_monitor::ptr(new quiet_progress()); + return auto_ptr(new quiet_progress()); } //---------------------------------------------------------------- diff --git a/base/progress_monitor.h b/base/progress_monitor.h index 29b3d36..5472343 100644 --- a/base/progress_monitor.h +++ b/base/progress_monitor.h @@ -2,6 +2,7 @@ #define BASE_PROGRESS_MONITOR_H #include +#include #include //---------------------------------------------------------------- @@ -9,15 +10,13 @@ namespace base { class progress_monitor { public: - typedef boost::shared_ptr ptr; - virtual ~progress_monitor() {} virtual void update_percent(unsigned) = 0; }; - progress_monitor::ptr create_progress_bar(std::string const &title); - progress_monitor::ptr create_quiet_progress_monitor(); + std::auto_ptr create_progress_bar(std::string const &title); + std::auto_ptr create_quiet_progress_monitor(); } //---------------------------------------------------------------- diff --git a/base/xml_utils.cc b/base/xml_utils.cc index cc60f2d..fb34153 100644 --- a/base/xml_utils.cc +++ b/base/xml_utils.cc @@ -1,5 +1,67 @@ #include "xml_utils.h" +#include "persistent-data/file_utils.h" +#include +#include + +using namespace xml_utils; + +//---------------------------------------------------------------- + +void +xml_parser::parse(std::string const &backup_file, bool quiet) +{ + persistent_data::check_file_exists(backup_file); + ifstream in(backup_file.c_str(), ifstream::in); + + std::auto_ptr monitor = create_monitor(quiet); + + size_t total = 0; + size_t input_length = get_file_length(backup_file); + + while (!in.eof()) { + char buffer[4096]; + in.read(buffer, sizeof(buffer)); + size_t len = in.gcount(); + int done = in.eof(); + + if (!XML_Parse(parser_, buffer, len, done)) { + ostringstream out; + out << "Parse error at line " + << XML_GetCurrentLineNumber(parser_) + << ":\n" + << XML_ErrorString(XML_GetErrorCode(parser_)) + << endl; + throw runtime_error(out.str()); + } + + total += len; + monitor->update_percent(total * 100 / input_length); + } +} + +size_t +xml_parser::get_file_length(string const &file) const +{ + struct stat info; + int r; + + r = ::stat(file.c_str(), &info); + if (r) + throw runtime_error("Couldn't stat backup path"); + + return info.st_size; +} + +auto_ptr +xml_parser::create_monitor(bool quiet) +{ + if (!quiet && isatty(fileno(stdout))) + return base::create_progress_bar("Restoring"); + else + return base::create_quiet_progress_monitor(); +} + //---------------------------------------------------------------- void @@ -21,5 +83,4 @@ xml_utils::build_attributes(attributes &a, char const **attr) } } - //---------------------------------------------------------------- diff --git a/base/xml_utils.h b/base/xml_utils.h index 892438e..581e814 100644 --- a/base/xml_utils.h +++ b/base/xml_utils.h @@ -1,9 +1,11 @@ #ifndef BASE_XML_UTILS_H #define BASE_XML_UTILS_H +#include #include #include #include +#include #include using namespace std; @@ -30,7 +32,12 @@ namespace xml_utils { return parser_; } + void parse(std::string const &backup_file, bool quiet); + private: + size_t get_file_length(string const &file) const; + auto_ptr create_monitor(bool quiet); + XML_Parser parser_; }; diff --git a/caching/cache_restore.cc b/caching/cache_restore.cc index d38d748..f9290d6 100644 --- a/caching/cache_restore.cc +++ b/caching/cache_restore.cc @@ -20,11 +20,30 @@ using namespace std; //---------------------------------------------------------------- namespace { + size_t get_file_length(string const &file) { + struct stat info; + int r; + + r = ::stat(file.c_str(), &info); + if (r) + throw runtime_error("Couldn't stat backup path"); + + return info.st_size; + } + + auto_ptr create_monitor(bool quiet) { + if (!quiet && isatty(fileno(stdout))) + return create_progress_bar("Restoring"); + else + return create_quiet_progress_monitor(); + } + struct flags { flags() : metadata_version(1), override_metadata_version(false), - clean_shutdown(true) { + clean_shutdown(true), + quiet(false) { } optional input; @@ -33,6 +52,7 @@ namespace { uint32_t metadata_version; bool override_metadata_version; bool clean_shutdown; + bool quiet; }; int restore(flags const &fs) { @@ -48,7 +68,9 @@ namespace { check_file_exists(*fs.input); ifstream in(fs.input->c_str(), ifstream::in); - parse_xml(in, restorer); + + auto_ptr monitor = create_monitor(fs.quiet); + parse_xml(in, restorer, get_file_length(*fs.input), *monitor); } catch (std::exception &e) { cerr << e.what() << endl; @@ -64,6 +86,7 @@ namespace { << " {-h|--help}" << endl << " {-i|--input} " << endl << " {-o|--output} " << endl + << " {-q|--quiet}" << endl << " {-V|--version}" << endl << endl << " {--debug-override-metadata-version} " << endl @@ -77,13 +100,14 @@ int main(int argc, char **argv) int c; flags fs; char const *prog_name = basename(argv[0]); - char const *short_opts = "hi:o:V"; + char const *short_opts = "hi:o:qV"; option const long_opts[] = { { "debug-override-metadata-version", required_argument, NULL, 0 }, { "omit-clean-shutdown", no_argument, NULL, 1 }, { "help", no_argument, NULL, 'h'}, { "input", required_argument, NULL, 'i' }, { "output", required_argument, NULL, 'o'}, + { "quiet", no_argument, NULL, 'q'}, { "version", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 } }; @@ -111,6 +135,10 @@ int main(int argc, char **argv) fs.output = optional(string(optarg)); break; + case 'q': + fs.quiet = true; + break; + case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; diff --git a/caching/xml_format.cc b/caching/xml_format.cc index 57764e7..ced582d 100644 --- a/caching/xml_format.cc +++ b/caching/xml_format.cc @@ -236,13 +236,16 @@ caching::create_xml_emitter(ostream &out) } void -caching::parse_xml(istream &in, emitter::ptr e) +caching::parse_xml(istream &in, emitter::ptr e, + size_t input_length, base::progress_monitor &monitor) { xml_parser p; XML_SetUserData(p.get_parser(), e.get()); XML_SetElementHandler(p.get_parser(), start_tag, end_tag); + size_t total = 0; + while (!in.eof()) { char buffer[4096]; in.read(buffer, sizeof(buffer)); @@ -258,6 +261,9 @@ caching::parse_xml(istream &in, emitter::ptr e) << endl; throw runtime_error(out.str()); } + + total += len; + monitor.update_percent(total * 100 / input_length); } } diff --git a/caching/xml_format.h b/caching/xml_format.h index 6855fb5..1725825 100644 --- a/caching/xml_format.h +++ b/caching/xml_format.h @@ -1,6 +1,7 @@ #ifndef CACHE_XML_FORMAT_H #define CACHE_XML_FORMAT_H +#include "base/progress_monitor.h" #include "emitter.h" #include @@ -9,7 +10,8 @@ namespace caching { emitter::ptr create_xml_emitter(std::ostream &out); - void parse_xml(std::istream &in, emitter::ptr e); + void parse_xml(std::istream &in, emitter::ptr e, + size_t input_len, base::progress_monitor &monitor); } //---------------------------------------------------------------- diff --git a/era/era_restore.cc b/era/era_restore.cc index 3bd37d4..7d11c8c 100644 --- a/era/era_restore.cc +++ b/era/era_restore.cc @@ -21,19 +21,22 @@ using namespace std; namespace { struct flags { + flags() + : quiet(false) { + } + optional input; optional output; + bool quiet; }; - int restore(flags const &fs) { + int restore(flags const &fs, bool quiet) { try { block_manager<>::ptr bm = open_bm(*fs.output, block_manager<>::READ_WRITE); metadata::ptr md(new metadata(bm, metadata::CREATE)); emitter::ptr restorer = create_restore_emitter(*md); - check_file_exists(*fs.input); - ifstream in(fs.input->c_str(), ifstream::in); - parse_xml(in, restorer); + parse_xml(*fs.input, restorer, fs.quiet); } catch (std::exception &e) { cerr << e.what() << endl; @@ -49,6 +52,7 @@ namespace { << " {-h|--help}" << endl << " {-i|--input} " << endl << " {-o|--output} " << endl + << " {-q|--quiet}" << endl << " {-V|--version}" << endl; } } @@ -58,11 +62,12 @@ int main(int argc, char **argv) int c; flags fs; char const *prog_name = basename(argv[0]); - char const *short_opts = "hi:o:V"; + char const *short_opts = "hi:o:qV"; option const long_opts[] = { { "help", no_argument, NULL, 'h'}, { "input", required_argument, NULL, 'i' }, { "output", required_argument, NULL, 'o'}, + { "quiet", no_argument, NULL, 'q'}, { "version", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 } }; @@ -81,6 +86,10 @@ int main(int argc, char **argv) fs.output = optional(string(optarg)); break; + case 'q': + fs.quiet = true; + break; + case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; @@ -108,7 +117,7 @@ int main(int argc, char **argv) return 1; } - return restore(fs); + return restore(fs, fs.quiet); } //---------------------------------------------------------------- diff --git a/era/xml_format.cc b/era/xml_format.cc index eeb7903..59aa4a7 100644 --- a/era/xml_format.cc +++ b/era/xml_format.cc @@ -159,29 +159,14 @@ era::create_xml_emitter(std::ostream &out) } void -era::parse_xml(std::istream &in, emitter::ptr e) +era::parse_xml(std::string const &backup_file, emitter::ptr e, bool quiet) { xml_parser p; XML_SetUserData(p.get_parser(), e.get()); XML_SetElementHandler(p.get_parser(), start_tag, end_tag); - while (!in.eof()) { - char buffer[4096]; - in.read(buffer, sizeof(buffer)); - size_t len = in.gcount(); - int done = in.eof(); - - if (!XML_Parse(p.get_parser(), buffer, len, done)) { - ostringstream out; - out << "Parse error at line " - << XML_GetCurrentLineNumber(p.get_parser()) - << ":\n" - << XML_ErrorString(XML_GetErrorCode(p.get_parser())) - << endl; - throw runtime_error(out.str()); - } - } + p.parse(backup_file, quiet); } //---------------------------------------------------------------- diff --git a/era/xml_format.h b/era/xml_format.h index 30b7174..d56220f 100644 --- a/era/xml_format.h +++ b/era/xml_format.h @@ -1,7 +1,8 @@ #ifndef ERA_XML_FORMAT_H #define ERA_XML_FORMAT_H -#include "emitter.h" +#include "base/progress_monitor.h" +#include "era/emitter.h" #include @@ -9,7 +10,7 @@ namespace era { emitter::ptr create_xml_emitter(std::ostream &out); - void parse_xml(std::istream &in, emitter::ptr e); + void parse_xml(std::string const &backup_file, emitter::ptr e, bool quiet); } //---------------------------------------------------------------- diff --git a/features/cache_restore.feature b/features/cache_restore.feature index 1052089..ebe5300 100644 --- a/features/cache_restore.feature +++ b/features/cache_restore.feature @@ -18,6 +18,7 @@ Feature: thin_restore {-h|--help} {-i|--input} {-o|--output} + {-q|--quiet} {-V|--version} {--debug-override-metadata-version} @@ -36,6 +37,7 @@ Feature: thin_restore {-h|--help} {-i|--input} {-o|--output} + {-q|--quiet} {-V|--version} {--debug-override-metadata-version} @@ -80,3 +82,18 @@ Feature: thin_restore And an empty dev file When I run cache_restore with -i metadata.xml -o metadata.bin --omit-clean-shutdown Then it should pass + + Scenario: --quiet is accepted + Given valid metadata + When I run thin_restore with -i metadata.xml -o metadata.bin --quiet + Then it should pass with: + """ + """ + + Scenario: -q is accepted + Given valid metadata + When I run thin_restore with -i metadata.xml -o metadata.bin -q + Then it should pass with: + """ + """ + diff --git a/features/era_restore.feature b/features/era_restore.feature index b62989c..bf7a45c 100644 --- a/features/era_restore.feature +++ b/features/era_restore.feature @@ -18,6 +18,7 @@ Feature: thin_restore {-h|--help} {-i|--input} {-o|--output} + {-q|--quiet} {-V|--version} """ @@ -33,6 +34,7 @@ Feature: thin_restore {-h|--help} {-i|--input} {-o|--output} + {-q|--quiet} {-V|--version} """ @@ -63,4 +65,18 @@ Feature: thin_restore When I run era_restore with -i metadata.xml -o metadata.bin Then it should pass And the metadata should be valid - \ No newline at end of file + + Scenario: --quiet is accepted + Given valid metadata + When I run thin_restore with -i metadata.xml -o metadata.bin --quiet + Then it should pass with: + """ + """ + + Scenario: -q is accepted + Given valid metadata + When I run thin_restore with -i metadata.xml -o metadata.bin -q + Then it should pass with: + """ + """ + diff --git a/features/thin_restore.feature b/features/thin_restore.feature index 7f5b5be..c931649 100644 --- a/features/thin_restore.feature +++ b/features/thin_restore.feature @@ -55,6 +55,20 @@ Feature: thin_restore No output file provided. """ + Scenario: --quiet is accepted + Given valid metadata + When I run thin_restore with -i metadata.xml -o metadata.bin --quiet + Then it should pass with: + """ + """ + + Scenario: -q is accepted + Given valid metadata + When I run thin_restore with -i metadata.xml -o metadata.bin -q + Then it should pass with: + """ + """ + Scenario: dump/restore is a noop Given valid metadata When I dump diff --git a/thin-provisioning/thin_restore.cc b/thin-provisioning/thin_restore.cc index d82eae1..e75cfb6 100644 --- a/thin-provisioning/thin_restore.cc +++ b/thin-provisioning/thin_restore.cc @@ -41,35 +41,13 @@ using namespace thin_provisioning; //---------------------------------------------------------------- namespace { - size_t get_file_length(string const &file) { - struct stat info; - int r; - - r = ::stat(file.c_str(), &info); - if (r) - throw runtime_error("Couldn't stat backup path"); - - return info.st_size; - } - - progress_monitor::ptr create_monitor(bool quiet) { - if (!quiet && isatty(fileno(stdout))) - return create_progress_bar("Restoring"); - else - return create_quiet_progress_monitor(); - } - int restore(string const &backup_file, string const &dev, bool quiet) { try { // The block size gets updated by the restorer. metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0)); emitter::ptr restorer = create_restore_emitter(md); - check_file_exists(backup_file); - ifstream in(backup_file.c_str(), ifstream::in); - - progress_monitor::ptr monitor = create_monitor(quiet); - parse_xml(in, restorer, get_file_length(backup_file), monitor); + parse_xml(backup_file, restorer, quiet); } catch (std::exception &e) { cerr << e.what() << endl; diff --git a/thin-provisioning/xml_format.cc b/thin-provisioning/xml_format.cc index f300312..333204f 100644 --- a/thin-provisioning/xml_format.cc +++ b/thin-provisioning/xml_format.cc @@ -217,35 +217,14 @@ tp::create_xml_emitter(ostream &out) } void -tp::parse_xml(std::istream &in, emitter::ptr e, - size_t input_length, base::progress_monitor::ptr monitor) +tp::parse_xml(std::string const &backup_file, emitter::ptr e, bool quiet) { xml_parser p; XML_SetUserData(p.get_parser(), e.get()); XML_SetElementHandler(p.get_parser(), start_tag, end_tag); - size_t total = 0; - - while (!in.eof()) { - char buffer[1024 * 1024]; - in.read(buffer, sizeof(buffer)); - size_t len = in.gcount(); - int done = in.eof(); - - if (!XML_Parse(p.get_parser(), buffer, len, done)) { - ostringstream out; - out << "Parse error at line " - << XML_GetCurrentLineNumber(p.get_parser()) - << ":\n" - << XML_ErrorString(XML_GetErrorCode(p.get_parser())) - << endl; - throw runtime_error(out.str()); - } - - total += len; - monitor->update_percent(total * 100 / input_length); - } + p.parse(backup_file, quiet); } //---------------------------------------------------------------- diff --git a/thin-provisioning/xml_format.h b/thin-provisioning/xml_format.h index 7cd7d4e..ae7d77e 100644 --- a/thin-provisioning/xml_format.h +++ b/thin-provisioning/xml_format.h @@ -28,8 +28,7 @@ namespace thin_provisioning { emitter::ptr create_xml_emitter(std::ostream &out); - void parse_xml(std::istream &in, emitter::ptr e, - size_t input_length, base::progress_monitor::ptr p); + void parse_xml(std::string const &backup_file, emitter::ptr e, bool quiet); } //---------------------------------------------------------------- From c1e0799367718684d104c3cd38c7a4c0d28990e1 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Aug 2014 13:13:32 +0100 Subject: [PATCH 118/165] [build] deps weren't being calculated for the top level source file of some of the tools. --- Makefile.in | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index ed9e585..e846b26 100644 --- a/Makefile.in +++ b/Makefile.in @@ -98,14 +98,23 @@ PDATA_OBJECTS=$(subst .cc,.o,$(SOURCE)) CXX_PROGRAM_SOURCE=\ caching/cache_check.cc \ + caching/cache_dump.cc \ caching/cache_restore.cc \ + caching/cache_metadata_size.cc \ + caching/cache_repair.cc \ + \ + era/era_check.cc \ + era/era_dump.cc \ + era/era_invalidate.cc \ + era/era_restore.cc \ \ thin-provisioning/thin_check.cc \ thin-provisioning/thin_delta.cc \ thin-provisioning/thin_dump.cc \ thin-provisioning/thin_restore.cc \ thin-provisioning/thin_repair.cc \ - thin-provisioning/thin_rmap.cc + thin-provisioning/thin_rmap.cc \ + thin-provisioning/thin_metadata_size.cc C_PROGRAM_SOURCE=\ thin-provisioning/thin_metadata_size.c From 6f8b7e291430c43551dfbe7434580d5774fe9365 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 27 Aug 2014 14:01:31 +0100 Subject: [PATCH 119/165] [all] Build a single executable --- .gitignore | 19 +- Makefile.in | 457 ++---------------- base/application.cc | 62 +++ base/application.h | 52 ++ bin/cache_check | 1 + bin/cache_dump | 1 + bin/cache_metadata_size | 1 + bin/cache_repair | 1 + bin/cache_restore | 1 + bin/era_check | 1 + bin/era_dump | 1 + bin/era_invalidate | 1 + bin/era_restore | 1 + bin/thin_check | 1 + bin/thin_delta | 1 + bin/thin_dump | 1 + bin/thin_metadata_size | 1 + bin/thin_repair | 1 + bin/thin_restore | 1 + bin/thin_rmap | 1 + caching/cache_check.cc | 5 +- caching/cache_dump.cc | 5 +- caching/cache_metadata_size.cc | 6 +- caching/cache_repair.cc | 5 +- caching/cache_restore.cc | 5 +- caching/commands.h | 18 + era/commands.h | 17 + era/era_check.cc | 5 +- era/era_dump.cc | 5 +- era/era_invalidate.cc | 5 +- era/era_restore.cc | 5 +- features/cache_restore.feature | 10 +- features/era_restore.feature | 10 +- features/step_definitions/era_steps.rb | 8 + features/step_definitions/thin_steps.rb | 4 +- features/support/aruba.rb | 2 +- features/thin_check.feature | 8 +- features/thin_restore.feature | 10 +- features/thin_rmap.feature | 22 +- main.cc | 42 ++ thin-provisioning/commands.h | 21 + thin-provisioning/thin_check.cc | 8 +- thin-provisioning/thin_delta.cc | 11 +- thin-provisioning/thin_dump.cc | 7 +- ..._metadata_size.c => thin_metadata_size.cc} | 47 +- thin-provisioning/thin_repair.cc | 7 +- thin-provisioning/thin_restore.cc | 5 +- thin-provisioning/thin_rmap.cc | 5 +- 48 files changed, 418 insertions(+), 496 deletions(-) create mode 100644 base/application.cc create mode 100644 base/application.h create mode 120000 bin/cache_check create mode 120000 bin/cache_dump create mode 120000 bin/cache_metadata_size create mode 120000 bin/cache_repair create mode 120000 bin/cache_restore create mode 120000 bin/era_check create mode 120000 bin/era_dump create mode 120000 bin/era_invalidate create mode 120000 bin/era_restore create mode 120000 bin/thin_check create mode 120000 bin/thin_delta create mode 120000 bin/thin_dump create mode 120000 bin/thin_metadata_size create mode 120000 bin/thin_repair create mode 120000 bin/thin_restore create mode 120000 bin/thin_rmap create mode 100644 caching/commands.h create mode 100644 era/commands.h create mode 100644 main.cc create mode 100644 thin-provisioning/commands.h rename thin-provisioning/{thin_metadata_size.c => thin_metadata_size.cc} (88%) diff --git a/.gitignore b/.gitignore index 3085816..0db515f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,24 +10,7 @@ cachegrind.* \#*\# core -thin_check -thin_delta -thin_dump -thin_restore -thin_repair -thin_rmap -thin_metadata_size - -cache_check -cache_dump -cache_restore -cache_repair -cache_metadata_size - -era_check -era_dump -era_invalidate -era_restore +bin/pdata_tools *.metadata bad-metadata diff --git a/Makefile.in b/Makefile.in index e846b26..47bb267 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,69 +19,54 @@ .PHONY: all V=@ -PROGRAMS=\ - cache_check \ - cache_dump \ - cache_restore \ - cache_repair \ - cache_metadata_size \ - \ - era_check \ - era_dump \ - era_invalidate \ - era_restore \ - \ - thin_check \ - thin_delta \ - thin_dump \ - thin_restore \ - thin_repair \ - thin_rmap \ - thin_metadata_size -all: $(PROGRAMS) +all: bin/pdata_tools SOURCE=\ - block-cache/block_cache.cc \ - \ + base/application.cc \ base/base64.cc \ base/endian_utils.cc \ base/error_state.cc \ base/progress_monitor.cc \ base/xml_utils.cc \ - \ + block-cache/block_cache.cc \ + caching/cache_check.cc \ + caching/cache_dump.cc \ + caching/cache_metadata_size.cc \ + caching/cache_repair.cc \ + caching/cache_restore.cc \ caching/hint_array.cc \ - caching/superblock.cc \ caching/mapping_array.cc \ caching/metadata.cc \ caching/metadata_dump.cc \ caching/restore_emitter.cc \ + caching/superblock.cc \ caching/xml_format.cc \ - \ era/era_array.cc \ + era/era_check.cc \ era/era_detail.cc \ - era/superblock.cc \ - era/writeset_tree.cc \ + era/era_dump.cc \ + era/era_invalidate.cc \ + era/era_restore.cc \ era/metadata.cc \ era/metadata_dump.cc \ era/restore_emitter.cc \ + era/superblock.cc \ + era/writeset_tree.cc \ era/xml_format.cc \ - \ + main.cc \ persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/transaction_manager.cc \ - \ persistent-data/data-structures/bitset.cc \ persistent-data/data-structures/bloom_filter.cc \ persistent-data/data-structures/btree.cc \ - \ - persistent-data/space_map.cc \ + persistent-data/error_set.cc \ + persistent-data/file_utils.cc \ + persistent-data/hex_dump.cc \ + persistent-data/space-maps/careful_alloc.cc \ persistent-data/space-maps/disk.cc \ persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - \ + persistent-data/space_map.cc \ + persistent-data/transaction_manager.cc \ thin-provisioning/device_tree.cc \ thin-provisioning/human_readable_format.cc \ thin-provisioning/mapping_tree.cc \ @@ -91,33 +76,15 @@ SOURCE=\ thin-provisioning/restore_emitter.cc \ thin-provisioning/rmap_visitor.cc \ thin-provisioning/superblock.cc \ - thin-provisioning/thin_pool.cc \ - thin-provisioning/xml_format.cc - -PDATA_OBJECTS=$(subst .cc,.o,$(SOURCE)) - -CXX_PROGRAM_SOURCE=\ - caching/cache_check.cc \ - caching/cache_dump.cc \ - caching/cache_restore.cc \ - caching/cache_metadata_size.cc \ - caching/cache_repair.cc \ - \ - era/era_check.cc \ - era/era_dump.cc \ - era/era_invalidate.cc \ - era/era_restore.cc \ - \ thin-provisioning/thin_check.cc \ thin-provisioning/thin_delta.cc \ thin-provisioning/thin_dump.cc \ - thin-provisioning/thin_restore.cc \ + thin-provisioning/thin_metadata_size.cc \ + thin-provisioning/thin_pool.cc \ thin-provisioning/thin_repair.cc \ + thin-provisioning/thin_restore.cc \ thin-provisioning/thin_rmap.cc \ - thin-provisioning/thin_metadata_size.cc - -C_PROGRAM_SOURCE=\ - thin-provisioning/thin_metadata_size.c + thin-provisioning/xml_format.cc CC:=@CC@ CXX:=@CXX@ @@ -129,8 +96,7 @@ CXXFLAGS+=-g -Wall -fno-strict-aliasing CXXFLAGS+=@CXXOPTIMISE_FLAG@ CXXFLAGS+=@CXXDEBUG_FLAG@ INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR) -I$(TOP_DIR)/thin-provisioning -LIBS:=-lstdc++ -laio -LIBEXPAT:=-lexpat +LIBS:=-lstdc++ -laio -lexpat INSTALL:=@INSTALL@ PREFIX:=@prefix@ BINDIR:=$(DESTDIR)$(PREFIX)/sbin @@ -153,14 +119,6 @@ endif .SUFFIXES: .d -%.o: %.c - @echo " [CC] $<" - $(V) $(CC) -c $(INCLUDES) $(CFLAGS) -o $@ $< - @echo " [DEP] $<" - $(V) $(CC) -MM -MT $(subst .c,.o,$<) $(INCLUDES) $(CFLAGS) $< > $*.$$$$; \ - sed 's,\([^ :]*\)\.o[ :]*,\1.o \1.gmo $* : Makefile ,g' < $*.$$$$ > $*.d; \ - $(RM) $*.$$$$ - %.o: %.cc @echo " [CXX] $<" $(V) $(CXX) -c $(INCLUDES) $(CXXFLAGS) -o $@ $< @@ -171,334 +129,14 @@ endif #---------------------------------------------------------------- -lib/libpdata.a: $(PDATA_OBJECTS) +lib/libpdata.a: $(OBJECTS) @echo " [AR] $<" - $(V)ar -rv $@ $(PDATA_OBJECTS) > /dev/null 2>&1 + $(V)ar -rv $@ $(OBJECTS) > /dev/null 2>&1 -#---------------------------------------------------------------- -# Thin provisioning tools - -THIN_DEBUG_SOURCE=$(SOURCE) -THIN_DUMP_SOURCE=$(SOURCE) -THIN_REPAIR_SOURCE=$(SOURCE) -THIN_RESTORE_SOURCE=$(SOURCE) -THIN_CHECK_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/error_state.cc \ - base/endian_utils.cc \ - base/progress_monitor.cc \ - base/xml_utils.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - \ - thin-provisioning/device_tree.cc \ - thin-provisioning/mapping_tree.cc \ - thin-provisioning/metadata.cc \ - thin-provisioning/metadata_checker.cc \ - thin-provisioning/superblock.cc - -THIN_DELTA_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/error_state.cc \ - base/endian_utils.cc \ - base/progress_monitor.cc \ - base/xml_utils.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - \ - thin-provisioning/device_tree.cc \ - thin-provisioning/mapping_tree.cc \ - thin-provisioning/metadata.cc \ - thin-provisioning/metadata_checker.cc \ - thin-provisioning/superblock.cc - -THIN_RMAP_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/endian_utils.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - thin-provisioning/device_tree.cc \ - thin-provisioning/mapping_tree.cc \ - thin-provisioning/metadata.cc \ - thin-provisioning/metadata_checker.cc \ - thin-provisioning/rmap_visitor.cc \ - thin-provisioning/superblock.cc - -THIN_DEBUG_OBJECTS=$(subst .cc,.o,$(THIN_DEBUG_SOURCE)) -THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_SOURCE)) -THIN_REPAIR_OBJECTS=$(subst .cc,.o,$(THIN_REPAIR_SOURCE)) -THIN_RESTORE_OBJECTS=$(subst .cc,.o,$(THIN_RESTORE_SOURCE)) -THIN_CHECK_OBJECTS=$(subst .cc,.o,$(THIN_CHECK_SOURCE)) -THIN_DELTA_OBJECTS=$(subst .cc,.o,$(THIN_DELTA_SOURCE)) -THIN_RMAP_OBJECTS=$(subst .cc,.o,$(THIN_RMAP_SOURCE)) - -thin_debug: $(THIN_DEBUG_OBJECTS) thin-provisioning/thin_debug.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -thin_repair: $(THIN_REPAIR_OBJECTS) thin-provisioning/thin_repair.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -thin_dump: $(THIN_DUMP_OBJECTS) thin-provisioning/thin_dump.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -thin_restore: $(THIN_RESTORE_OBJECTS) thin-provisioning/thin_restore.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -thin_check: $(THIN_CHECK_OBJECTS) thin-provisioning/thin_check.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -thin_delta: $(THIN_DELTA_OBJECTS) thin-provisioning/thin_delta.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -thin_rmap: $(THIN_RMAP_OBJECTS) thin-provisioning/thin_rmap.o +bin/pdata_tools: $(OBJECTS) @echo " [LD] $@" $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) -thin_metadata_size: thin-provisioning/thin_metadata_size.o - @echo " [LD] $@" - $(V) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ -lm - -#---------------------------------------------------------------- -# Cache tools - -CACHE_CHECK_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/base64.cc \ - base/error_state.cc \ - base/endian_utils.cc \ - base/progress_monitor.cc \ - base/xml_utils.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/data-structures/bitset.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - \ - caching/hint_array.cc \ - caching/superblock.cc \ - caching/mapping_array.cc \ - caching/metadata.cc \ - caching/metadata_dump.cc \ - caching/restore_emitter.cc \ - caching/xml_format.cc - -CACHE_CHECK_OBJECTS=$(subst .cc,.o,$(CACHE_CHECK_SOURCE)) - -CACHE_DUMP_SOURCE=$(SOURCE) -CACHE_DUMP_OBJECTS=$(subst .cc,.o,$(CACHE_DUMP_SOURCE)) - -CACHE_REPAIR_SOURCE=$(SOURCE) -CACHE_REPAIR_OBJECTS=$(subst .cc,.o,$(CACHE_REPAIR_SOURCE)) - -CACHE_RESTORE_SOURCE=$(SOURCE) -CACHE_RESTORE_OBJECTS=$(subst .cc,.o,$(CACHE_RESTORE_SOURCE)) - -cache_check: $(CACHE_CHECK_OBJECTS) caching/cache_check.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -cache_dump: $(CACHE_DUMP_OBJECTS) caching/cache_dump.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -cache_repair: $(CACHE_REPAIR_OBJECTS) caching/cache_repair.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -cache_restore: $(CACHE_RESTORE_OBJECTS) caching/cache_restore.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -cache_metadata_size: caching/cache_metadata_size.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) - -#---------------------------------------------------------------- -# Era tools - -ERA_CHECK_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/base64.cc \ - base/error_state.cc \ - base/endian_utils.cc \ - base/progress_monitor.cc \ - base/xml_utils.cc \ - \ - era/writeset_tree.cc \ - era/era_detail.cc \ - era/era_array.cc \ - era/metadata.cc \ - era/superblock.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/data-structures/bitset.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - -ERA_CHECK_OBJECTS=$(subst .cc,.o,$(ERA_CHECK_SOURCE)) - -era_check: $(ERA_CHECK_OBJECTS) era/era_check.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -ERA_DUMP_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/base64.cc \ - base/error_state.cc \ - base/endian_utils.cc \ - base/progress_monitor.cc \ - base/xml_utils.cc \ - \ - era/writeset_tree.cc \ - era/era_detail.cc \ - era/era_array.cc \ - era/metadata.cc \ - era/metadata_dump.cc \ - era/superblock.cc \ - era/xml_format.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/data-structures/bitset.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - -ERA_DUMP_OBJECTS=$(subst .cc,.o,$(ERA_DUMP_SOURCE)) - -era_dump: $(ERA_DUMP_OBJECTS) era/era_dump.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -ERA_INVALIDATE_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/base64.cc \ - base/error_state.cc \ - base/endian_utils.cc \ - base/progress_monitor.cc \ - base/xml_utils.cc \ - \ - era/writeset_tree.cc \ - era/era_detail.cc \ - era/era_array.cc \ - era/metadata.cc \ - era/metadata_dump.cc \ - era/superblock.cc \ - era/xml_format.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/data-structures/bitset.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - -ERA_INVALIDATE_OBJECTS=$(subst .cc,.o,$(ERA_INVALIDATE_SOURCE)) - -era_invalidate: $(ERA_INVALIDATE_OBJECTS) era/era_invalidate.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - -ERA_RESTORE_SOURCE=\ - block-cache/block_cache.cc \ - \ - base/base64.cc \ - base/error_state.cc \ - base/endian_utils.cc \ - base/progress_monitor.cc \ - base/xml_utils.cc \ - \ - era/writeset_tree.cc \ - era/era_detail.cc \ - era/era_array.cc \ - era/metadata.cc \ - era/metadata_dump.cc \ - era/restore_emitter.cc \ - era/superblock.cc \ - era/xml_format.cc \ - \ - persistent-data/checksum.cc \ - persistent-data/error_set.cc \ - persistent-data/file_utils.cc \ - persistent-data/hex_dump.cc \ - persistent-data/data-structures/btree.cc \ - persistent-data/data-structures/bitset.cc \ - persistent-data/space_map.cc \ - persistent-data/space-maps/disk.cc \ - persistent-data/space-maps/recursive.cc \ - persistent-data/space-maps/careful_alloc.cc \ - persistent-data/transaction_manager.cc \ - -ERA_RESTORE_OBJECTS=$(subst .cc,.o,$(ERA_RESTORE_SOURCE)) - -era_restore: $(ERA_RESTORE_OBJECTS) era/era_restore.o - @echo " [LD] $@" - $(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) - #---------------------------------------------------------------- DEPEND_FILES=\ @@ -518,21 +156,24 @@ clean: distclean: clean $(RM) config.cache config.log config.status configure.h version.h Makefile unit-tests/Makefile -install: $(PROGRAMS) +install: bin/pdata_tools $(INSTALL_DIR) $(BINDIR) - $(INSTALL_PROGRAM) cache_check $(BINDIR) - $(INSTALL_PROGRAM) cache_dump $(BINDIR) - $(INSTALL_PROGRAM) cache_repair $(BINDIR) - $(INSTALL_PROGRAM) cache_restore $(BINDIR) - $(INSTALL_PROGRAM) thin_check $(BINDIR) - $(INSTALL_PROGRAM) thin_dump $(BINDIR) - $(INSTALL_PROGRAM) thin_repair $(BINDIR) - $(INSTALL_PROGRAM) thin_restore $(BINDIR) - $(INSTALL_PROGRAM) thin_rmap $(BINDIR) - $(INSTALL_PROGRAM) thin_metadata_size $(BINDIR) - $(INSTALL_PROGRAM) era_check $(BINDIR) - $(INSTALL_PROGRAM) era_dump $(BINDIR) - $(INSTALL_PROGRAM) era_invalidate $(BINDIR) + $(INSTALL_PROGRAM) bin/pdata_tools $(BINDIR) + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_check + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_dump + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_metadata_size + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_repair + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_restore + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_check + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_dump + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_repair + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_restore + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_rmap + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_metadata_size + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_check + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_dump + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_invalidate + ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_restore $(INSTALL_DIR) $(MANPATH)/man8 $(INSTALL_DATA) man8/cache_check.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/cache_dump.8 $(MANPATH)/man8 @@ -548,6 +189,8 @@ install: $(PROGRAMS) $(INSTALL_DATA) man8/era_dump.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/era_invalidate.8 $(MANPATH)/man8 +# $(INSTALL_DATA) man8/era_restore.8 $(MANPATH)/man8 + .PHONY: install ifeq ("@TESTING@", "yes") @@ -555,7 +198,7 @@ include unit-tests/Makefile .PHONEY: features -features: $(PROGRAMS) +features: pdata_tools cucumber --no-color --format progress test: features unit-test diff --git a/base/application.cc b/base/application.cc new file mode 100644 index 0000000..fa4a0b6 --- /dev/null +++ b/base/application.cc @@ -0,0 +1,62 @@ +#include "base/application.h" + +#include +#include + +using namespace base; +using namespace std; + +//---------------------------------------------------------------- + +int +application::run(int argc, char **argv) +{ + string cmd = basename(argv[0]); + + if (cmd == string("pdata-tools")) { + argc--; + argv++; + + if (!argc) { + usage(); + return 1; + } + + cmd = argv[0]; + } + + std::list::const_iterator it; + for (it = cmds_.begin(); it != cmds_.end(); ++it) { + if (cmd == (*it)->get_name()) + return (*it)->run(argc, argv); + } + + std::cerr << "Unknown command '" << cmd << "'\n"; + usage(); + return 1; +} + +void +application::usage() +{ + std::cerr << "Usage: \n" + << "commands:\n"; + + std::list::const_iterator it; + for (it = cmds_.begin(); it != cmds_.end(); ++it) { + std::cerr << " " << (*it)->get_name() << "\n"; + } +} + +std::string +application::basename(std::string const &path) const +{ + char buffer[PATH_MAX + 1]; + + memset(buffer, 0, sizeof(buffer)); + strncpy(buffer, path.c_str(), PATH_MAX); + + return ::basename(buffer); +} + +//---------------------------------------------------------------- diff --git a/base/application.h b/base/application.h new file mode 100644 index 0000000..8585129 --- /dev/null +++ b/base/application.h @@ -0,0 +1,52 @@ +#ifndef BASE_APPLICATION_H +#define BASE_APPLICATION_H + +#include +#include +#include +#include + +//---------------------------------------------------------------- + +namespace base { + class command { + public: + typedef int (*cmd_fn)(int, char **); + + command(std::string const &name, cmd_fn fn) + : name_(name), + fn_(fn) { + } + + std::string const &get_name() const { + return name_; + } + + int run(int argc, char **argv) const { + return fn_(argc, argv); + } + + private: + std::string name_; + cmd_fn fn_; + }; + + class application { + public: + void add_cmd(command const &c) { + cmds_.push_back(&c); + } + + int run(int argc, char **argv); + + private: + void usage(); + std::string basename(std::string const &path) const; + + std::list cmds_; + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/bin/cache_check b/bin/cache_check new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/cache_check @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/cache_dump b/bin/cache_dump new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/cache_dump @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/cache_metadata_size b/bin/cache_metadata_size new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/cache_metadata_size @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/cache_repair b/bin/cache_repair new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/cache_repair @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/cache_restore b/bin/cache_restore new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/cache_restore @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/era_check b/bin/era_check new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/era_check @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/era_dump b/bin/era_dump new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/era_dump @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/era_invalidate b/bin/era_invalidate new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/era_invalidate @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/era_restore b/bin/era_restore new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/era_restore @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/thin_check b/bin/thin_check new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/thin_check @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/thin_delta b/bin/thin_delta new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/thin_delta @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/thin_dump b/bin/thin_dump new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/thin_dump @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/thin_metadata_size b/bin/thin_metadata_size new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/thin_metadata_size @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/thin_repair b/bin/thin_repair new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/thin_repair @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/thin_restore b/bin/thin_restore new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/thin_restore @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/bin/thin_rmap b/bin/thin_rmap new file mode 120000 index 0000000..84c01e7 --- /dev/null +++ b/bin/thin_rmap @@ -0,0 +1 @@ +pdata_tools \ No newline at end of file diff --git a/caching/cache_check.cc b/caching/cache_check.cc index 593ba4e..400b214 100644 --- a/caching/cache_check.cc +++ b/caching/cache_check.cc @@ -14,6 +14,7 @@ #include "base/error_state.h" #include "base/nested_output.h" +#include "caching/commands.h" #include "caching/metadata.h" #include "persistent-data/block.h" #include "persistent-data/file_utils.h" @@ -322,7 +323,7 @@ namespace { //---------------------------------------------------------------- -int main(int argc, char **argv) +int cache_check_main(int argc, char **argv) { int c; flags fs; @@ -384,4 +385,6 @@ int main(int argc, char **argv) return check_with_exception_handling(argv[optind], fs); } +base::command caching::cache_check_cmd("cache_check", cache_check_main); + //---------------------------------------------------------------- diff --git a/caching/cache_dump.cc b/caching/cache_dump.cc index 938ddd5..117f86d 100644 --- a/caching/cache_dump.cc +++ b/caching/cache_dump.cc @@ -4,6 +4,7 @@ #include #include "version.h" +#include "caching/commands.h" #include "caching/mapping_array.h" #include "caching/metadata.h" #include "caching/metadata_dump.h" @@ -66,7 +67,7 @@ namespace { //---------------------------------------------------------------- -int main(int argc, char **argv) +int cache_dump_main(int argc, char **argv) { int c; flags fs; @@ -114,4 +115,6 @@ int main(int argc, char **argv) return dump(argv[optind], output, fs); } +base::command caching::cache_dump_cmd("cache_dump", cache_dump_main); + //---------------------------------------------------------------- diff --git a/caching/cache_metadata_size.cc b/caching/cache_metadata_size.cc index 5792c49..3fb1bf1 100644 --- a/caching/cache_metadata_size.cc +++ b/caching/cache_metadata_size.cc @@ -1,5 +1,7 @@ #include "version.h" +#include "caching/commands.h" + #include #include #include @@ -132,7 +134,7 @@ namespace { } } -int main(int argc, char **argv) +int cache_metadata_size_main(int argc, char **argv) { flags fs; @@ -156,4 +158,6 @@ int main(int argc, char **argv) return 0; } +base::command caching::cache_metadata_size_cmd("cache_metadata_size", cache_metadata_size_main); + //---------------------------------------------------------------- diff --git a/caching/cache_repair.cc b/caching/cache_repair.cc index 77e5ff6..8419796 100644 --- a/caching/cache_repair.cc +++ b/caching/cache_repair.cc @@ -2,6 +2,7 @@ #include #include +#include "caching/commands.h" #include "caching/metadata.h" #include "caching/metadata_dump.h" #include "caching/restore_emitter.h" @@ -52,7 +53,7 @@ namespace { //---------------------------------------------------------------- -int main(int argc, char **argv) +int cache_repair_main(int argc, char **argv) { int c; boost::optional input_path, output_path; @@ -105,4 +106,6 @@ int main(int argc, char **argv) return repair(*input_path, *output_path); } +base::command caching::cache_repair_cmd("cache_repair", cache_repair_main); + //---------------------------------------------------------------- diff --git a/caching/cache_restore.cc b/caching/cache_restore.cc index f9290d6..b0a6437 100644 --- a/caching/cache_restore.cc +++ b/caching/cache_restore.cc @@ -1,5 +1,6 @@ #include "version.h" +#include "caching/commands.h" #include "caching/metadata.h" #include "caching/restore_emitter.h" #include "caching/xml_format.h" @@ -95,7 +96,7 @@ namespace { } } -int main(int argc, char **argv) +int cache_restore_main(int argc, char **argv) { int c; flags fs; @@ -169,4 +170,6 @@ int main(int argc, char **argv) return restore(fs); } +base::command caching::cache_restore_cmd("cache_restore", cache_restore_main); + //---------------------------------------------------------------- diff --git a/caching/commands.h b/caching/commands.h new file mode 100644 index 0000000..1396b9b --- /dev/null +++ b/caching/commands.h @@ -0,0 +1,18 @@ +#ifndef CACHING_COMMANDS_H +#define CACHING_COMMANDS_H + +#include "base/application.h" + +//---------------------------------------------------------------- + +namespace caching { + extern base::command cache_check_cmd; + extern base::command cache_dump_cmd; + extern base::command cache_metadata_size_cmd; + extern base::command cache_restore_cmd; + extern base::command cache_repair_cmd; +} + +//---------------------------------------------------------------- + +#endif diff --git a/era/commands.h b/era/commands.h new file mode 100644 index 0000000..f556fbf --- /dev/null +++ b/era/commands.h @@ -0,0 +1,17 @@ +#ifndef ERA_COMMANDS_H +#define ERA_COMMANDS_H + +#include "base/application.h" + +//---------------------------------------------------------------- + +namespace era { + extern base::command era_check_cmd; + extern base::command era_dump_cmd; + extern base::command era_invalidate_cmd; + extern base::command era_restore_cmd; +} + +//---------------------------------------------------------------- + +#endif diff --git a/era/era_check.cc b/era/era_check.cc index 25c4d20..fed199e 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -14,6 +14,7 @@ #include "base/error_state.h" #include "base/nested_output.h" +#include "era/commands.h" #include "era/writeset_tree.h" #include "era/era_array.h" #include "era/superblock.h" @@ -276,7 +277,7 @@ namespace { //---------------------------------------------------------------- -int main(int argc, char **argv) +int era_check_main(int argc, char **argv) { int c; flags fs; @@ -322,4 +323,6 @@ int main(int argc, char **argv) return check_with_exception_handling(argv[optind], fs); } +base::command era::era_check_cmd("era_check", era_check_main); + //---------------------------------------------------------------- diff --git a/era/era_dump.cc b/era/era_dump.cc index 760ebff..c279cd1 100644 --- a/era/era_dump.cc +++ b/era/era_dump.cc @@ -4,6 +4,7 @@ #include #include "version.h" +#include "era/commands.h" #include "era/era_array.h" #include "era/writeset_tree.h" #include "era/metadata.h" @@ -70,7 +71,7 @@ namespace { //---------------------------------------------------------------- -int main(int argc, char **argv) +int era_dump_main(int argc, char **argv) { int c; flags fs; @@ -123,4 +124,6 @@ int main(int argc, char **argv) return dump(argv[optind], output, fs); } +base::command era::era_dump_cmd("era_dump", era_dump_main); + //---------------------------------------------------------------- diff --git a/era/era_invalidate.cc b/era/era_invalidate.cc index 6f109b3..45ae784 100644 --- a/era/era_invalidate.cc +++ b/era/era_invalidate.cc @@ -5,6 +5,7 @@ #include "version.h" #include "base/indented_stream.h" +#include "era/commands.h" #include "era/era_array.h" #include "era/writeset_tree.h" #include "era/metadata.h" @@ -189,7 +190,7 @@ namespace { //---------------------------------------------------------------- -int main(int argc, char **argv) +int era_invalidate_main(int argc, char **argv) { int c; flags fs; @@ -248,4 +249,6 @@ int main(int argc, char **argv) return invalidate(argv[optind], output, fs); } +base::command era::era_invalidate_cmd("era_invalidate", era_invalidate_main); + //---------------------------------------------------------------- diff --git a/era/era_restore.cc b/era/era_restore.cc index 7d11c8c..761f920 100644 --- a/era/era_restore.cc +++ b/era/era_restore.cc @@ -1,5 +1,6 @@ #include "version.h" +#include "era/commands.h" #include "era/metadata.h" #include "era/restore_emitter.h" #include "era/xml_format.h" @@ -57,7 +58,7 @@ namespace { } } -int main(int argc, char **argv) +int era_restore_main(int argc, char **argv) { int c; flags fs; @@ -120,4 +121,6 @@ int main(int argc, char **argv) return restore(fs, fs.quiet); } +base::command era::era_restore_cmd("era_restore", era_restore_main); + //---------------------------------------------------------------- diff --git a/features/cache_restore.feature b/features/cache_restore.feature index ebe5300..7008966 100644 --- a/features/cache_restore.feature +++ b/features/cache_restore.feature @@ -1,4 +1,4 @@ -Feature: thin_restore +Feature: cache_restore Scenario: print version (-V flag) When I run cache_restore with -V Then it should pass with version @@ -84,15 +84,15 @@ Feature: thin_restore Then it should pass Scenario: --quiet is accepted - Given valid metadata - When I run thin_restore with -i metadata.xml -o metadata.bin --quiet + Given valid cache metadata + When I run cache_restore with -i metadata.xml -o metadata.bin --quiet Then it should pass with: """ """ Scenario: -q is accepted - Given valid metadata - When I run thin_restore with -i metadata.xml -o metadata.bin -q + Given valid cache metadata + When I run cache_restore with -i metadata.xml -o metadata.bin -q Then it should pass with: """ """ diff --git a/features/era_restore.feature b/features/era_restore.feature index bf7a45c..22f002c 100644 --- a/features/era_restore.feature +++ b/features/era_restore.feature @@ -1,4 +1,4 @@ -Feature: thin_restore +Feature: era_restore Scenario: print version (-V flag) When I run era_restore with -V Then it should pass with version @@ -67,15 +67,15 @@ Feature: thin_restore And the metadata should be valid Scenario: --quiet is accepted - Given valid metadata - When I run thin_restore with -i metadata.xml -o metadata.bin --quiet + Given valid era metadata + When I run era_restore with -i metadata.xml -o metadata.bin --quiet Then it should pass with: """ """ Scenario: -q is accepted - Given valid metadata - When I run thin_restore with -i metadata.xml -o metadata.bin -q + Given valid era metadata + When I run era_restore with -i metadata.xml -o metadata.bin -q Then it should pass with: """ """ diff --git a/features/step_definitions/era_steps.rb b/features/step_definitions/era_steps.rb index bbdbb2a..42aa3b0 100644 --- a/features/step_definitions/era_steps.rb +++ b/features/step_definitions/era_steps.rb @@ -29,3 +29,11 @@ Then(/^the metadata should be valid$/) do run_simple("era_check #{dev_file}", true) end +Given(/^valid era metadata$/) do + in_current_dir do + system("era_xml create --nr-blocks 100 --nr-writesets 2 --current-era 1000 > #{xml_file}") + end + + run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024") + run_simple("era_restore -i #{xml_file} -o #{dev_file}") +end diff --git a/features/step_definitions/thin_steps.rb b/features/step_definitions/thin_steps.rb index 38fe6a3..d6674a6 100644 --- a/features/step_definitions/thin_steps.rb +++ b/features/step_definitions/thin_steps.rb @@ -1,4 +1,4 @@ -Given(/^valid metadata$/) do +Given(/^valid thin metadata$/) do in_current_dir do system("thinp_xml create --nr-thins uniform[4..9] --nr-mappings uniform[1000..10000] > #{xml_file}") end @@ -58,7 +58,7 @@ Then(/^dumps ([0-9]+) and ([0-9]+) should be identical$/) do |d1, d2| run_simple("diff -ub #{dump_files[d1.to_i]} #{dump_files[d2.to_i]}", true) end -Given(/^small metadata$/) do +Given(/^small thin metadata$/) do in_current_dir do system("thinp_xml create --nr-thins 2 --nr-mappings 1 > #{xml_file}") end diff --git a/features/support/aruba.rb b/features/support/aruba.rb index 1ad857e..de4d203 100644 --- a/features/support/aruba.rb +++ b/features/support/aruba.rb @@ -1,3 +1,3 @@ require 'aruba/cucumber' -ENV['PATH'] = "#{Dir::pwd}:#{ENV['PATH']}" +ENV['PATH'] = "#{Dir::pwd}/bin:#{ENV['PATH']}" diff --git a/features/thin_check.feature b/features/thin_check.feature index 8b910af..8a8324b 100644 --- a/features/thin_check.feature +++ b/features/thin_check.feature @@ -44,7 +44,7 @@ Feature: thin_check Then it should fail Scenario: --super-block-only check passes on valid metadata - Given valid metadata + Given valid thin metadata When I run thin_check with --super-block-only Then it should pass @@ -59,12 +59,12 @@ Feature: thin_check """ Scenario: --skip-mappings check passes on valid metadata - Given valid metadata + Given valid thin metadata When I run thin_check with --skip-mappings Then it should pass Scenario: --ignore-non-fatal-errors check passes on valid metadata - Given valid metadata + Given valid thin metadata When I run thin_check with --ignore-non-fatal-errors Then it should pass @@ -81,6 +81,6 @@ Feature: thin_check And it should give no output Scenario: Accepts --clear-needs-check-flag - Given valid metadata + Given valid thin metadata When I run thin_check with --clear-needs-check-flag Then it should pass diff --git a/features/thin_restore.feature b/features/thin_restore.feature index c931649..036daf7 100644 --- a/features/thin_restore.feature +++ b/features/thin_restore.feature @@ -56,32 +56,32 @@ Feature: thin_restore """ Scenario: --quiet is accepted - Given valid metadata + Given valid thin metadata When I run thin_restore with -i metadata.xml -o metadata.bin --quiet Then it should pass with: """ """ Scenario: -q is accepted - Given valid metadata + Given valid thin metadata When I run thin_restore with -i metadata.xml -o metadata.bin -q Then it should pass with: """ """ Scenario: dump/restore is a noop - Given valid metadata + Given valid thin metadata When I dump And I restore And I dump Then dumps 1 and 2 should be identical Scenario: dump matches original metadata - Given valid metadata + Given valid thin metadata When I dump Then dumps 0 and 1 should be identical Scenario: dump matches original metadata (small) - Given small metadata + Given small thin metadata When I dump Then dumps 0 and 1 should be identical diff --git a/features/thin_rmap.feature b/features/thin_rmap.feature index 97a3073..2d44e0d 100644 --- a/features/thin_rmap.feature +++ b/features/thin_rmap.feature @@ -42,56 +42,56 @@ Feature: thin_rmap @announce Scenario: Valid region format should pass - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 23..7890 Then it should pass Scenario: Invalid region format should fail (comma instean of dots) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 23,7890 Then it should fail Scenario: Invalid region format should fail (second number a word) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 23..six Then it should fail Scenario: Invalid region format should fail (first number a word) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region four..7890 Then it should fail Scenario: Invalid region format should fail (end is lower than begin) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 89..88 Then it should fail Scenario: Invalid region format should fail (end is equal to begin) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 89..89 Then it should fail Scenario: Invalid region format should fail (no begin) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region ..89 Then it should fail Scenario: Invalid region format should fail (no end) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 89.. Then it should fail Scenario: Invalid region format should fail (no region at all) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region Then it should fail Scenario: Invalid region format should fail (three dots) - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 89...99 Then it should fail Scenario: Multiple regions should pass - Given valid metadata + Given valid thin metadata When I run thin_rmap with --region 1..23 --region 45..78 Then it should pass diff --git a/main.cc b/main.cc new file mode 100644 index 0000000..ed69ba9 --- /dev/null +++ b/main.cc @@ -0,0 +1,42 @@ +#include + +#include "base/application.h" + +#include "caching/commands.h" +#include "era/commands.h" +#include "thin-provisioning/commands.h" + +//---------------------------------------------------------------- + +int main(int argc, char **argv) +{ + using namespace base; + + application app; + + app.add_cmd(caching::cache_check_cmd); + app.add_cmd(caching::cache_dump_cmd); + app.add_cmd(caching::cache_metadata_size_cmd); + app.add_cmd(caching::cache_restore_cmd); + app.add_cmd(caching::cache_repair_cmd); + + app.add_cmd(era::era_check_cmd); + app.add_cmd(era::era_dump_cmd); + app.add_cmd(era::era_invalidate_cmd); + app.add_cmd(era::era_restore_cmd); + + app.add_cmd(thin_provisioning::thin_check_cmd); + app.add_cmd(thin_provisioning::thin_delta_cmd); + app.add_cmd(thin_provisioning::thin_dump_cmd); + app.add_cmd(thin_provisioning::thin_metadata_size_cmd); + app.add_cmd(thin_provisioning::thin_restore_cmd); + app.add_cmd(thin_provisioning::thin_repair_cmd); + app.add_cmd(thin_provisioning::thin_rmap_cmd); + + // FIXME: convert thin_metadata_size to c++ + //app.add_cmd(thin_provisioning::thin_metadata_size_cmd); + + return app.run(argc, argv); +} + +//---------------------------------------------------------------- diff --git a/thin-provisioning/commands.h b/thin-provisioning/commands.h new file mode 100644 index 0000000..5635665 --- /dev/null +++ b/thin-provisioning/commands.h @@ -0,0 +1,21 @@ +#ifndef THIN_PROVISIONING_COMMANDS_H +#define THIN_PROVISIONING_COMMANDS_H + +#include "base/application.h" + +//---------------------------------------------------------------- + +namespace thin_provisioning { + extern base::command thin_check_cmd; + extern base::command thin_delta_cmd; + extern base::command thin_dump_cmd; + extern base::command thin_metadata_size_cmd; + extern base::command thin_restore_cmd; + extern base::command thin_repair_cmd; + extern base::command thin_rmap_cmd; + extern base::command thin_metadata_size_cmd; +} + +//---------------------------------------------------------------- + +#endif diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc index 9e90699..a01fe8b 100644 --- a/thin-provisioning/thin_check.cc +++ b/thin-provisioning/thin_check.cc @@ -22,6 +22,7 @@ #include "version.h" +#include "base/application.h" #include "base/error_state.h" #include "base/nested_output.h" #include "persistent-data/space-maps/core.h" @@ -29,6 +30,7 @@ #include "thin-provisioning/device_tree.h" #include "thin-provisioning/mapping_tree.h" #include "thin-provisioning/superblock.h" +#include "thin-provisioning/commands.h" using namespace base; using namespace std; @@ -272,7 +274,7 @@ namespace { } } -int main(int argc, char **argv) +int thin_check_main(int argc, char **argv) { int c; flags fs; @@ -342,3 +344,7 @@ int main(int argc, char **argv) return check(argv[optind], fs); } + +base::command thin_provisioning::thin_check_cmd("thin_check", thin_check_main); + +//---------------------------------------------------------------- diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 21ecd01..da3a890 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -12,13 +12,14 @@ #include "persistent-data/file_utils.h" #include "thin-provisioning/superblock.h" #include "thin-provisioning/mapping_tree.h" +#include "thin-provisioning/commands.h" using namespace std; using namespace thin_provisioning; //---------------------------------------------------------------- -namespace { +namespace local { class application { public: application(string const &cmd) @@ -503,11 +504,13 @@ namespace { // FIXME: add metadata snap switch -int main(int argc, char **argv) +int thin_delta_main(int argc, char **argv) { + using namespace local; + int c; flags fs; - application app(basename(argv[0])); + local::application app(basename(argv[0])); char const shortopts[] = "hV"; option const longopts[] = { @@ -565,4 +568,6 @@ int main(int argc, char **argv) return delta(app, fs); } +base::command thin_provisioning::thin_delta_cmd("thin_delta", thin_delta_main); + //---------------------------------------------------------------- diff --git a/thin-provisioning/thin_dump.cc b/thin-provisioning/thin_dump.cc index 3d0e8eb..853a512 100644 --- a/thin-provisioning/thin_dump.cc +++ b/thin-provisioning/thin_dump.cc @@ -26,6 +26,7 @@ #include "metadata.h" #include "xml_format.h" #include "version.h" +#include "thin-provisioning/commands.h" using namespace persistent_data; using namespace std; @@ -95,7 +96,7 @@ namespace { } } -int main(int argc, char **argv) +int thin_dump_main(int argc, char **argv) { int c; char const *output = NULL; @@ -165,3 +166,7 @@ int main(int argc, char **argv) return dump(argv[optind], output, format, flags, metadata_snap); } + +base::command thin_provisioning::thin_dump_cmd("thin_dump", thin_dump_main); + +//---------------------------------------------------------------- diff --git a/thin-provisioning/thin_metadata_size.c b/thin-provisioning/thin_metadata_size.cc similarity index 88% rename from thin-provisioning/thin_metadata_size.c rename to thin-provisioning/thin_metadata_size.cc index c98abc5..8ea3eba 100644 --- a/thin-provisioning/thin_metadata_size.c +++ b/thin-provisioning/thin_metadata_size.cc @@ -23,6 +23,8 @@ * */ +#include "thin-provisioning/commands.h" + #include #include #include @@ -38,8 +40,13 @@ enum numeric_options { BLOCKSIZE, POOLSIZE, MAXTHINS, NUMERIC, OPT_END}; enum return_units { RETURN_BYTES, RETURN_SECTORS }; enum numeric_type { NO_NUMBER, NUMBER, NUMBER_SHORT, NUMBER_LONG }; -typedef unsigned bool; -enum bool_value { false = 0, true = 1}; + +struct options_ { + unsigned unit_idx; + char *s[OPT_END]; + unsigned long long n[OPT_END]; +}; + struct global { char *prg; /* program name */ @@ -51,11 +58,7 @@ struct global { } unit; /* Command line option properties. */ - struct options { - unsigned unit_idx; - char *s[OPT_END]; - unsigned long long n[OPT_END]; - } options; + options_ options; }; static void exit_prg(struct global *g, int ret) @@ -80,7 +83,7 @@ static void abort_prg(struct global *g, const char *msg) exit_prg(g, 1); } -static int unit_index(struct global *g, char *unit_string) +static int unit_index(struct global *g, char const *unit_string) { unsigned len; @@ -109,14 +112,14 @@ static int unit_index(struct global *g, char *unit_string) static struct global *init_prg(char *prg_path) { unsigned u; - static char *unit_chars = "bskKmMgGtTpPeEzZyY"; - static char *unit_strings[] = { "bytes", "sectors", - "kibibytes", "kilobytes", "mebibytes", "megabytes", - "gibibytes", "gigabytes", "tebibytes", "terabytes", - "pebibytes", "petabytes", "ebibytes", "exabytes", - "zebibytes", "zetabytes", "yobibytes", "yottabytes", NULL }; + static char const *unit_chars = "bskKmMgGtTpPeEzZyY"; + static char const *unit_strings[] = { "bytes", "sectors", + "kibibytes", "kilobytes", "mebibytes", "megabytes", + "gibibytes", "gigabytes", "tebibytes", "terabytes", + "pebibytes", "petabytes", "ebibytes", "exabytes", + "zebibytes", "zetabytes", "yobibytes", "yottabytes", NULL }; static unsigned long long unit_factors[ARRAY_SIZE(unit_strings) - 1] = { 1, 512, 1024, 1000 }; - struct global *r = malloc(sizeof(*r)); + struct global *r = static_cast(malloc(sizeof(*r))); if (!r) abort_prg(r, "failed to allocate global context!"); @@ -129,8 +132,8 @@ static struct global *init_prg(char *prg_path) } r->prg = basename(prg_path); - r->unit.chars = unit_chars; - r->unit.strings = unit_strings; + r->unit.chars = const_cast(unit_chars); + r->unit.strings = const_cast(unit_strings); r->unit.factors = unit_factors; r->options.unit_idx = unit_index(r, NULL); @@ -144,7 +147,7 @@ static unsigned long long bytes_per_sector(struct global *g) static void check_opts(struct global *g) { - struct options *o = &g->options; + options_ *o = &g->options; if (!o->n[BLOCKSIZE]) abort_prg(g, "block size required!"); @@ -183,7 +186,7 @@ static unsigned long long to_bytes(struct global *g, char *sz, enum return_units return (!us || unit == RETURN_SECTORS) ? r / bytes_per_sector(g) : r; } -static void printf_aligned(struct global *g, char *a, char *b, char *c, bool units, bool mandatory) +static void printf_aligned(struct global *g, char const *a, char const *b, char const *c, bool units, bool mandatory) { char buf[80]; @@ -254,7 +257,7 @@ static void check_size(struct global *g, enum numeric_options o, char *arg) idx = g->options.unit_idx; } - g->options.s[o] = malloc(strlen(arg) + strlen(g->unit.strings[idx]) + 1); + g->options.s[o] = static_cast(malloc(strlen(arg) + strlen(g->unit.strings[idx]) + 1)); if (!g->options.s[o]) abort_prg(g, "failed to allocate string!"); @@ -359,7 +362,7 @@ static void print_estimated_result(struct global *g) print_precision(g, r, g->options.unit_idx); } -int main(int argc, char **argv) +int thin_metadata_size_main(int argc, char **argv) { struct global *g = init_prg(*argv); @@ -368,3 +371,5 @@ int main(int argc, char **argv) exit_prg(g, 0); return 0; /* Doesn't get here... */ } + +base::command thin_provisioning::thin_metadata_size_cmd("thin_metadata_size", thin_metadata_size_main); diff --git a/thin-provisioning/thin_repair.cc b/thin-provisioning/thin_repair.cc index f1b078a..7ba58a5 100644 --- a/thin-provisioning/thin_repair.cc +++ b/thin-provisioning/thin_repair.cc @@ -2,6 +2,7 @@ #include #include +#include "thin-provisioning/commands.h" #include "human_readable_format.h" #include "metadata_dumper.h" #include "metadata.h" @@ -40,7 +41,7 @@ namespace { } } -int main(int argc, char **argv) +int thin_repair_main(int argc, char **argv) { int c; boost::optional input_path, output_path; @@ -92,3 +93,7 @@ int main(int argc, char **argv) return repair(*input_path, *output_path); } + +base::command thin_provisioning::thin_repair_cmd("thin_repair", thin_repair_main); + +//---------------------------------------------------------------- diff --git a/thin-provisioning/thin_restore.cc b/thin-provisioning/thin_restore.cc index e75cfb6..57b82c3 100644 --- a/thin-provisioning/thin_restore.cc +++ b/thin-provisioning/thin_restore.cc @@ -17,6 +17,7 @@ // . #include "persistent-data/file_utils.h" +#include "thin-provisioning/commands.h" #include "thin-provisioning/emitter.h" #include "thin-provisioning/human_readable_format.h" #include "thin-provisioning/metadata.h" @@ -68,7 +69,7 @@ namespace { } } -int main(int argc, char **argv) +int thin_restore_main(int argc, char **argv) { int c; char const *prog_name = basename(argv[0]); @@ -132,4 +133,6 @@ int main(int argc, char **argv) return restore(input, output, quiet); } +base::command thin_provisioning::thin_restore_cmd("thin_restore", thin_restore_main); + //---------------------------------------------------------------- diff --git a/thin-provisioning/thin_rmap.cc b/thin-provisioning/thin_rmap.cc index 71180b3..9083c6f 100644 --- a/thin-provisioning/thin_rmap.cc +++ b/thin-provisioning/thin_rmap.cc @@ -10,6 +10,7 @@ #include "persistent-data/run.h" #include "persistent-data/space-maps/core.h" #include "persistent-data/file_utils.h" +#include "thin-provisioning/commands.h" #include "thin-provisioning/superblock.h" #include "thin-provisioning/mapping_tree.h" #include "thin-provisioning/rmap_visitor.h" @@ -125,7 +126,7 @@ namespace { //---------------------------------------------------------------- -int main(int argc, char **argv) +int thin_rmap_main(int argc, char **argv) { int c; vector regions; @@ -174,4 +175,6 @@ int main(int argc, char **argv) return rmap(argv[optind], regions); } +base::command thin_provisioning::thin_rmap_cmd("thin_rmap", thin_rmap_main); + //---------------------------------------------------------------- From efe1a2a70fbab1689b4915d182422439aff09158 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 27 Aug 2014 14:08:06 +0100 Subject: [PATCH 120/165] update CHANGES --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 0d1d79d..0f8a19e 100644 --- a/CHANGES +++ b/CHANGES @@ -13,3 +13,5 @@ v0.4 effect of reducing the executable sizes due to less template instatiation. +- Tools rolled into a single executable to save space. + From 3cafc0d6e80d1a0023099515252a90ae46266411 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Wed, 27 Aug 2014 22:31:50 +0200 Subject: [PATCH 121/165] fix symbolic link at install time Packaging tools often install via $ make DESTDIR="${pkgdir}" install which breaks the symbolic links. The package contains symbolic links to ${pkgdir}/$(BINDIR)/pdata_tools, which is wrong. Just use relative path, which is really easy as binary and symlinks are in the same directory. --- Makefile.in | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Makefile.in b/Makefile.in index 47bb267..525f228 100644 --- a/Makefile.in +++ b/Makefile.in @@ -159,21 +159,21 @@ distclean: clean install: bin/pdata_tools $(INSTALL_DIR) $(BINDIR) $(INSTALL_PROGRAM) bin/pdata_tools $(BINDIR) - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_check - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_dump - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_metadata_size - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_repair - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/cache_restore - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_check - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_dump - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_repair - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_restore - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_rmap - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/thin_metadata_size - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_check - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_dump - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_invalidate - ln -s -f $(BINDIR)/pdata_tools $(BINDIR)/era_restore + ln -s -f pdata_tools $(BINDIR)/cache_check + ln -s -f pdata_tools $(BINDIR)/cache_dump + ln -s -f pdata_tools $(BINDIR)/cache_metadata_size + ln -s -f pdata_tools $(BINDIR)/cache_repair + ln -s -f pdata_tools $(BINDIR)/cache_restore + ln -s -f pdata_tools $(BINDIR)/thin_check + ln -s -f pdata_tools $(BINDIR)/thin_dump + ln -s -f pdata_tools $(BINDIR)/thin_repair + ln -s -f pdata_tools $(BINDIR)/thin_restore + ln -s -f pdata_tools $(BINDIR)/thin_rmap + ln -s -f pdata_tools $(BINDIR)/thin_metadata_size + ln -s -f pdata_tools $(BINDIR)/era_check + ln -s -f pdata_tools $(BINDIR)/era_dump + ln -s -f pdata_tools $(BINDIR)/era_invalidate + ln -s -f pdata_tools $(BINDIR)/era_restore $(INSTALL_DIR) $(MANPATH)/man8 $(INSTALL_DATA) man8/cache_check.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/cache_dump.8 $(MANPATH)/man8 From 5a45f5ac95d7c691ef8902da9ef6de2b76f306f2 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 28 Aug 2014 11:06:16 +0100 Subject: [PATCH 122/165] [bild] strip the binary when installing --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 525f228..339fbc5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -106,7 +106,7 @@ MANPATH:=$(DATADIR)/man vpath %.cc $(TOP_DIR) INSTALL_DIR = $(INSTALL) -m 755 -d -INSTALL_PROGRAM = $(INSTALL) -m 755 +INSTALL_PROGRAM = $(INSTALL) -m 755 -s INSTALL_DATA = $(INSTALL) -p -m 644 ifeq ("@TESTING@", "yes") From 0608df97d8a1bcd5540cdd9b544e98da7c64b1ed Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 28 Aug 2014 11:43:02 +0100 Subject: [PATCH 123/165] bug introduced when renaming to pdata_tools --- base/application.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/application.cc b/base/application.cc index fa4a0b6..6012e06 100644 --- a/base/application.cc +++ b/base/application.cc @@ -13,7 +13,7 @@ application::run(int argc, char **argv) { string cmd = basename(argv[0]); - if (cmd == string("pdata-tools")) { + if (cmd == string("pdata_tools")) { argc--; argv++; From 5a6b92312e6aa83b81f9bcc782ce44347c5ccaae Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 28 Aug 2014 14:00:08 +0100 Subject: [PATCH 124/165] [features] check that quiet mode really outputs nothing. Previously it was checking the output contained a null string within it. --- features/cache_restore.feature | 6 ++++-- features/era_restore.feature | 6 ++++-- features/step_definitions/cache_steps.rb | 2 +- features/step_definitions/era_steps.rb | 2 +- features/step_definitions/thin_steps.rb | 2 +- features/thin_restore.feature | 6 ++++-- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/features/cache_restore.feature b/features/cache_restore.feature index 7008966..c9121ce 100644 --- a/features/cache_restore.feature +++ b/features/cache_restore.feature @@ -86,14 +86,16 @@ Feature: cache_restore Scenario: --quiet is accepted Given valid cache metadata When I run cache_restore with -i metadata.xml -o metadata.bin --quiet - Then it should pass with: + Then it should pass + And the output should contain exactly: """ """ Scenario: -q is accepted Given valid cache metadata When I run cache_restore with -i metadata.xml -o metadata.bin -q - Then it should pass with: + Then it should pass + And the output should contain exactly: """ """ diff --git a/features/era_restore.feature b/features/era_restore.feature index 22f002c..57282f6 100644 --- a/features/era_restore.feature +++ b/features/era_restore.feature @@ -69,14 +69,16 @@ Feature: era_restore Scenario: --quiet is accepted Given valid era metadata When I run era_restore with -i metadata.xml -o metadata.bin --quiet - Then it should pass with: + Then it should pass + And the output should contain exactly: """ """ Scenario: -q is accepted Given valid era metadata When I run era_restore with -i metadata.xml -o metadata.bin -q - Then it should pass with: + Then it should pass + And the output should contain exactly: """ """ diff --git a/features/step_definitions/cache_steps.rb b/features/step_definitions/cache_steps.rb index a0343cc..48ff915 100644 --- a/features/step_definitions/cache_steps.rb +++ b/features/step_definitions/cache_steps.rb @@ -80,9 +80,9 @@ end Given(/^valid cache metadata$/) do in_current_dir do system("cache_xml create --nr-cache-blocks uniform[1000..5000] --nr-mappings uniform[500..1000] > #{xml_file}") + system("dd if=/dev/zero of=#{dev_file} bs=4k count=1024 > /dev/null") end - run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024") run_simple("cache_restore -i #{xml_file} -o #{dev_file}") end diff --git a/features/step_definitions/era_steps.rb b/features/step_definitions/era_steps.rb index 42aa3b0..2e6bc0a 100644 --- a/features/step_definitions/era_steps.rb +++ b/features/step_definitions/era_steps.rb @@ -32,8 +32,8 @@ end Given(/^valid era metadata$/) do in_current_dir do system("era_xml create --nr-blocks 100 --nr-writesets 2 --current-era 1000 > #{xml_file}") + system("dd if=/dev/zero of=#{dev_file} bs=4k count=1024 > /dev/null") end - run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024") run_simple("era_restore -i #{xml_file} -o #{dev_file}") end diff --git a/features/step_definitions/thin_steps.rb b/features/step_definitions/thin_steps.rb index d6674a6..1900293 100644 --- a/features/step_definitions/thin_steps.rb +++ b/features/step_definitions/thin_steps.rb @@ -1,9 +1,9 @@ Given(/^valid thin metadata$/) do in_current_dir do system("thinp_xml create --nr-thins uniform[4..9] --nr-mappings uniform[1000..10000] > #{xml_file}") + system("dd if=/dev/zero of=#{dev_file} bs=4k count=1024 > /dev/null") end - run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024") run_simple("thin_restore -i #{xml_file} -o #{dev_file}") end diff --git a/features/thin_restore.feature b/features/thin_restore.feature index 036daf7..e5bdb04 100644 --- a/features/thin_restore.feature +++ b/features/thin_restore.feature @@ -58,14 +58,16 @@ Feature: thin_restore Scenario: --quiet is accepted Given valid thin metadata When I run thin_restore with -i metadata.xml -o metadata.bin --quiet - Then it should pass with: + Then it should pass + And the output should contain exactly: """ """ Scenario: -q is accepted Given valid thin metadata When I run thin_restore with -i metadata.xml -o metadata.bin -q - Then it should pass with: + Then it should pass + And the output should contain exactly: """ """ From 67865e07328203de08e3f8bfbc5d32654a493b01 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 14:15:32 +0100 Subject: [PATCH 125/165] [bitset_t] Add test for walk_bitset --- unit-tests/bitset_t.cc | 47 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/unit-tests/bitset_t.cc b/unit-tests/bitset_t.cc index 36634f6..0973041 100644 --- a/unit-tests/bitset_t.cc +++ b/unit-tests/bitset_t.cc @@ -32,6 +32,26 @@ using namespace testing; namespace { block_address const NR_BLOCKS = 102400; + class bitset_checker : public bitset_detail::bitset_visitor { + public: + bitset_checker(unsigned size, unsigned m) + : size_(size), m_(m) { + } + + void visit(uint32_t index, bool value) { + ASSERT_THAT(index, Lt(size_)); + ASSERT_THAT(value, Eq(index % 7 ? true : false)); + } + + void visit(bitset_detail::missing_bits const &d) { + // we aren't expecting any damage + FAIL(); + } + + private: + unsigned size_, m_; + }; + class BitsetTests : public Test { public: BitsetTests() @@ -152,7 +172,7 @@ TEST_F(BitsetTests, set_works) TEST_F(BitsetTests, reopen_works) { - unsigned const COUNT = 100000; + unsigned const COUNT = 100001; block_address root; { @@ -172,4 +192,29 @@ TEST_F(BitsetTests, reopen_works) } } +TEST_F(BitsetTests, walk_bitset) +{ + unsigned const COUNT = 100001; + block_address root; + + { + bitset::ptr bs = create_bitset(); + + bs->grow(COUNT, true); + for (unsigned i = 0; i < COUNT; i += 7) + bs->set(i, false); + + root = bs->get_root(); + + bitset_checker c(COUNT, 7); + bs->walk_bitset(c); + } + + { + bitset::ptr bs = open_bitset(root, COUNT); + bitset_checker c(COUNT, 7); + bs->walk_bitset(c); + } +} + //---------------------------------------------------------------- From 14cfcf2dfd7a838f8f5a3350f8f7e78a06df7fde Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 14:16:03 +0100 Subject: [PATCH 126/165] [bitset] Don't visit bits in the tail end of the final word if they're beyond the nr bits in the bitset. Also shift 1ULL in the test. --- persistent-data/data-structures/bitset.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index 98805fa..fae1f34 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -82,7 +82,7 @@ namespace persistent_data { } void walk_bitset(bitset_visitor &v) const { - bit_visitor vv(v); + bit_visitor vv(v, nr_bits_); damage_visitor dv(v); array_.visit_values(vv, dv); } @@ -90,18 +90,20 @@ namespace persistent_data { private: class bit_visitor { public: - bit_visitor(bitset_visitor &v) - : v_(v) { + bit_visitor(bitset_visitor &v, unsigned nr_bits) + : v_(v), + nr_bits_(nr_bits) { } void visit(uint32_t word_index, uint64_t word) { uint32_t bit_index = word_index * 64; - for (unsigned bit = 0; bit < 64; bit++, bit_index++) - v_.visit(bit_index, !!(word & (1 << bit))); + for (unsigned bit = 0; bit < 64 && bit_index < nr_bits_; bit++, bit_index++) + v_.visit(bit_index, !!(word & (1ULL << bit))); } private: bitset_visitor &v_; + unsigned nr_bits_; }; class damage_visitor { From 93468190bd4c9ed7551d359b549f024ba1f75142 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 14:17:44 +0100 Subject: [PATCH 127/165] [bitset] When reopening a bitset some bits could be lost at the far end. The number of words necessary to store the bits was being miscalculated. --- persistent-data/data-structures/bitset.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistent-data/data-structures/bitset.cc b/persistent-data/data-structures/bitset.cc index fae1f34..b0d9e34 100644 --- a/persistent-data/data-structures/bitset.cc +++ b/persistent-data/data-structures/bitset.cc @@ -40,7 +40,7 @@ namespace persistent_data { bitset_impl(transaction_manager &tm, block_address root, unsigned nr_bits) : nr_bits_(nr_bits), - array_(tm, rc_, root, nr_bits / BITS_PER_ULL) { + array_(tm, rc_, root, div_up(nr_bits, BITS_PER_ULL)) { } block_address get_root() const { From 8761b6defc9ef8a304fb7a4eb05d646bdd6a3ff0 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 14:44:37 +0100 Subject: [PATCH 128/165] [progress bar] Add an extra newline at the end --- base/progress_monitor.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/progress_monitor.cc b/base/progress_monitor.cc index e6bf572..1d88302 100644 --- a/base/progress_monitor.cc +++ b/base/progress_monitor.cc @@ -16,6 +16,9 @@ namespace { update_percent(0); } + ~progress_bar() { + cout << "\n"; + } void update_percent(unsigned p) { unsigned nr_equals = max(progress_width_ * p / 100, 1); From 4c04a18b05b01d20320872c3dd66b7e439f77e04 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 14:45:05 +0100 Subject: [PATCH 129/165] [era] era_dump should show bool values as 'true' or 'false' rather than 0 or 1 --- era/xml_format.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/era/xml_format.cc b/era/xml_format.cc index 59aa4a7..8e49ce0 100644 --- a/era/xml_format.cc +++ b/era/xml_format.cc @@ -47,7 +47,7 @@ namespace { void writeset_bit(uint32_t bit, bool value) { out_.indent(); // FIXME: collect all the bits, then uuencode - out_ << "" << endl; + out_ << "" << endl; } void end_writeset() { @@ -74,6 +74,10 @@ namespace { out_ << "" << endl; } + char const *truth_value(bool v) const { + return v ? "true" : "false"; + } + private: indented_stream out_; }; From 562661c63e581480a5d03cee1ccaefe482006659 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 14:45:52 +0100 Subject: [PATCH 130/165] [era features] Added tests to check that dump/restore is a noop --- features/era_restore.feature | 11 +++++++++++ features/step_definitions/era_steps.rb | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/features/era_restore.feature b/features/era_restore.feature index 57282f6..a0289f9 100644 --- a/features/era_restore.feature +++ b/features/era_restore.feature @@ -82,3 +82,14 @@ Feature: era_restore """ """ + Scenario: dump/restore is a noop + Given valid era metadata + When I era dump + And I era restore + And I era dump + Then dumps 1 and 2 should be identical + + Scenario: dump matches original metadata + Given valid era metadata + When I era dump + Then dumps 0 and 1 should be identical diff --git a/features/step_definitions/era_steps.rb b/features/step_definitions/era_steps.rb index 2e6bc0a..6b6a0a7 100644 --- a/features/step_definitions/era_steps.rb +++ b/features/step_definitions/era_steps.rb @@ -37,3 +37,11 @@ Given(/^valid era metadata$/) do run_simple("era_restore -i #{xml_file} -o #{dev_file}") end + +When(/^I era dump$/) do + run_simple("era_dump #{dev_file} -o #{new_dump_file}", true) +end + +When(/^I era restore$/) do + run_simple("era_restore -i #{dump_files[-1]} -o #{dev_file}", true) +end From 99d851cd2909c7f84b661f7e406e4af149b2a62e Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 14:57:10 +0100 Subject: [PATCH 131/165] [cache features] add a test for dump/restore cycle being a noop --- features/cache_restore.feature | 6 ++++++ features/step_definitions/cache_steps.rb | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/features/cache_restore.feature b/features/cache_restore.feature index c9121ce..8da011c 100644 --- a/features/cache_restore.feature +++ b/features/cache_restore.feature @@ -99,3 +99,9 @@ Feature: cache_restore """ """ + Scenario: dump/restore is a noop + Given valid cache metadata + When I cache dump + And I cache restore + And I cache dump + Then dumps 1 and 2 should be identical diff --git a/features/step_definitions/cache_steps.rb b/features/step_definitions/cache_steps.rb index 48ff915..ffc8150 100644 --- a/features/step_definitions/cache_steps.rb +++ b/features/step_definitions/cache_steps.rb @@ -100,10 +100,10 @@ Given(/^an empty dev file$/) do run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024") end -When(/^I cache_dump$/) do +When(/^I cache dump$/) do run_simple("cache_dump #{dev_file} -o #{new_dump_file}", true) end -When(/^I cache_restore$/) do +When(/^I cache restore$/) do run_simple("cache_restore -i #{dump_files[-1]} -o #{dev_file}", true) end From b2d7dec46073cd6f0bb935097b0cae377abdfe4f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 15:10:32 +0100 Subject: [PATCH 132/165] [build] add bin/pdata_tools to the PROGRAMS var so 'make clean' removes it --- Makefile.in | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile.in b/Makefile.in index 339fbc5..ff2a2a3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,11 +16,13 @@ # with thin-provisioning-tools. If not, see # . -.PHONY: all - V=@ -all: bin/pdata_tools +PROGRAMS=\ + bin/pdata_tools + +.PHONY: all +all: $(PROGRAMS) SOURCE=\ base/application.cc \ From 666c7ac105860b2c290795bee95948f491620db6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 15:11:04 +0100 Subject: [PATCH 133/165] [era] add an include that's needed on debian --- base/xml_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/base/xml_utils.h b/base/xml_utils.h index 581e814..f867f56 100644 --- a/base/xml_utils.h +++ b/base/xml_utils.h @@ -7,6 +7,7 @@ #include #include #include +#include using namespace std; From 27d754bae03949427556f10b813da8f38ad05968 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 15:26:28 +0100 Subject: [PATCH 134/165] Update Gemfile.lock --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 06a7697..d05c7a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - aruba (0.6.0) + aruba (0.6.1) childprocess (>= 0.3.6) cucumber (>= 1.1.1) rspec-expectations (>= 2.7.0) @@ -21,11 +21,11 @@ GEM multi_json (~> 1.3) multi_json (1.10.1) multi_test (0.1.1) - rspec-expectations (3.0.3) + rspec-expectations (3.0.4) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.0.0) - rspec-support (3.0.3) - thinp_xml (0.0.18) + rspec-support (3.0.4) + thinp_xml (0.0.19) ejt_command_line (>= 0.0.2) PLATFORMS From e4296c539382ca993d18565b716d29d68970e379 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 15:26:42 +0100 Subject: [PATCH 135/165] [cache_dump feature] switch to using the new /cache dump|restore/ pattern --- features/cache_dump.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/cache_dump.feature b/features/cache_dump.feature index 4011c20..3415c6a 100644 --- a/features/cache_dump.feature +++ b/features/cache_dump.feature @@ -44,7 +44,7 @@ Feature: cache_dump Scenario: dump/restore is a noop Given valid cache metadata - When I cache_dump - And I cache_restore - And I cache_dump + When I cache dump + And I cache restore + And I cache dump Then cache dumps 1 and 2 should be identical From 23735a0253b913396f816ba4c56923debd527ddc Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 1 Sep 2014 15:45:29 +0100 Subject: [PATCH 136/165] update CHANGES --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 0f8a19e..7e22fdc 100644 --- a/CHANGES +++ b/CHANGES @@ -15,3 +15,5 @@ v0.4 - Tools rolled into a single executable to save space. +- Fixed some bugs when walking bitsets (possibly effecting cache_dump + and cache_check). \ No newline at end of file From 4c026458d508d0df184453bea72cf2e755e4f29a Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 2 Sep 2014 10:13:26 +0100 Subject: [PATCH 137/165] Squash a couple of annoying compiler warnings --- caching/cache_metadata_size.cc | 9 +++++++++ era/era_invalidate.cc | 15 ++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/caching/cache_metadata_size.cc b/caching/cache_metadata_size.cc index 3fb1bf1..dd806c8 100644 --- a/caching/cache_metadata_size.cc +++ b/caching/cache_metadata_size.cc @@ -17,6 +17,15 @@ namespace { struct flags { flags() : max_hint_width(4) { + + // Dance around some spurious compiler warnings + device_size = 0; + block_size = 0; + nr_blocks = 0; + + device_size.reset(); + block_size.reset(); + nr_blocks.reset(); } boost::optional device_size; diff --git a/era/era_invalidate.cc b/era/era_invalidate.cc index 45ae784..c81b5af 100644 --- a/era/era_invalidate.cc +++ b/era/era_invalidate.cc @@ -88,9 +88,14 @@ namespace { walk_writeset_tree(md.tm_, *md.writeset_tree_, v, dv); } - void mark_blocks_since(metadata const &md, uint32_t threshold, set &result) { - walk_array(*md.era_array_, md.sb_.nr_blocks, threshold, result); - walk_writesets(md, threshold, result); + void mark_blocks_since(metadata const &md, optional const &threshold, set &result) { + if (!threshold) + // Can't get here, just putting in to pacify the compiler + throw std::runtime_error("threshold not set"); + else { + walk_array(*md.era_array_, md.sb_.nr_blocks, *threshold, result); + walk_writesets(md, *threshold, result); + } } //-------------------------------- @@ -155,11 +160,11 @@ namespace { throw runtime_error("no metadata snapshot taken."); metadata::ptr md(new metadata(bm, *sb.metadata_snap)); - mark_blocks_since(*md, *fs.era_threshold_, blocks); + mark_blocks_since(*md, fs.era_threshold_, blocks); } else { metadata::ptr md(new metadata(bm, metadata::OPEN)); - mark_blocks_since(*md, *fs.era_threshold_, blocks); + mark_blocks_since(*md, fs.era_threshold_, blocks); } if (want_stdout(output)) From 39990e675c7830f5aeb1e4d7a5582fece0932142 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 3 Sep 2014 13:15:04 +0100 Subject: [PATCH 138/165] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d15723f..1d0ba9e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.2 +0.4.0 From 41354f10f5940c72917eb4205ce5adad26f7a369 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 4 Sep 2014 11:26:43 +0100 Subject: [PATCH 139/165] provide << operator for optionals --- thin-provisioning/human_readable_format.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/thin-provisioning/human_readable_format.cc b/thin-provisioning/human_readable_format.cc index 0726aa9..3cfc188 100644 --- a/thin-provisioning/human_readable_format.cc +++ b/thin-provisioning/human_readable_format.cc @@ -26,6 +26,14 @@ using namespace thin_provisioning; //---------------------------------------------------------------- namespace { + template + std::ostream &operator << (ostream &out, boost::optional const &maybe) { + if (maybe) + out << *maybe; + + return out; + } + class hr_emitter : public emitter { public: hr_emitter(ostream &out) From bb2c4ec200e6bccf87d25e397382c43ef13f50ee Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 4 Sep 2014 11:37:31 +0100 Subject: [PATCH 140/165] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1d0ba9e..267577d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.0 +0.4.1 From 2331204475aaf064dc43171753e7670840d9b0bd Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 4 Sep 2014 11:52:12 +0100 Subject: [PATCH 141/165] Update Gemfile --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index d05c7a1..d198286 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.0.0) rspec-support (3.0.4) - thinp_xml (0.0.19) + thinp_xml (0.0.20) ejt_command_line (>= 0.0.2) PLATFORMS From 92345b4b64cfb6d2d38620dc1c8ac0210d9c105f Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Sat, 15 Nov 2014 13:45:14 +0100 Subject: [PATCH 142/165] [persistent-data/space_map.h] Make destructor for space_map_detail::damage public The compiler is unable to create a default desctructor for the derived class missing_counts if the virtual destructor for the class damage is private. This fixes compilation bugs with CXXFLAGS=-std=gnu++11 together with gcc 4.8.3 and boost 1.55. --- persistent-data/space_map.h | 1 + 1 file changed, 1 insertion(+) diff --git a/persistent-data/space_map.h b/persistent-data/space_map.h index 5395c90..823b4ba 100644 --- a/persistent-data/space_map.h +++ b/persistent-data/space_map.h @@ -119,6 +119,7 @@ namespace persistent_data { namespace space_map_detail { class damage { + public: virtual ~damage() {} }; From baa70ecfe4a465b3cbba204a025d859603bd2f35 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Sat, 15 Nov 2014 14:17:22 +0100 Subject: [PATCH 143/165] [caching/hint_array.cc] Fix ambigious shared_ptr (C++11) Class shared_ptr exist in the namespace std for C++11 as well as in boost. Explicitly use the one from boost in order to be compatible. This fixes compilation bugs with CXXFLAGS=-std=gnu++11 together with gcc 4.8.3 and boost 1.55. --- caching/hint_array.cc | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/caching/hint_array.cc b/caching/hint_array.cc index cb9f2f1..7fccb82 100644 --- a/caching/hint_array.cc +++ b/caching/hint_array.cc @@ -38,16 +38,16 @@ namespace { xx(4); template - shared_ptr mk_array(transaction_manager &tm) { + boost::shared_ptr mk_array(transaction_manager &tm) { typedef hint_traits traits; typedef array ha; - shared_ptr r = typename ha::ptr(new ha(tm, typename traits::ref_counter())); + boost::shared_ptr r = typename ha::ptr(new ha(tm, typename traits::ref_counter())); return r; } - shared_ptr mk_array(transaction_manager &tm, uint32_t width) { + boost::shared_ptr mk_array(transaction_manager &tm, uint32_t width) { switch (width) { #define xx(n) case n: return mk_array(tm) @@ -58,15 +58,15 @@ namespace { } // never get here - return shared_ptr(); + return boost::shared_ptr(); } //-------------------------------- template - shared_ptr - downcast_array(shared_ptr base) { - shared_ptr a = dynamic_pointer_cast(base); + boost::shared_ptr + downcast_array(boost::shared_ptr base) { + boost::shared_ptr a = dynamic_pointer_cast(base); if (!a) throw runtime_error("internal error: couldn't cast hint array"); @@ -76,16 +76,16 @@ namespace { //-------------------------------- template - shared_ptr mk_array(transaction_manager &tm, block_address root, unsigned nr_entries) { + boost::shared_ptr mk_array(transaction_manager &tm, block_address root, unsigned nr_entries) { typedef hint_traits traits; typedef array ha; - shared_ptr r = typename ha::ptr(new ha(tm, typename traits::ref_counter(), root, nr_entries)); + boost::shared_ptr r = typename ha::ptr(new ha(tm, typename traits::ref_counter(), root, nr_entries)); return r; } - shared_ptr mk_array(transaction_manager &tm, uint32_t width, block_address root, unsigned nr_entries) { + boost::shared_ptr mk_array(transaction_manager &tm, uint32_t width, block_address root, unsigned nr_entries) { switch (width) { #define xx(n) case n: return mk_array(tm, root, nr_entries) all_widths @@ -95,21 +95,21 @@ namespace { } // never get here - return shared_ptr(); + return boost::shared_ptr(); } //-------------------------------- template - void get_hint(shared_ptr base, unsigned index, vector &data) { + void get_hint(boost::shared_ptr base, unsigned index, vector &data) { typedef hint_traits traits; typedef array ha; - shared_ptr a = downcast_array(base); + boost::shared_ptr a = downcast_array(base); data = a->get(index); } - void get_hint_(uint32_t width, shared_ptr base, unsigned index, vector &data) { + void get_hint_(uint32_t width, boost::shared_ptr base, unsigned index, vector &data) { switch (width) { #define xx(n) case n: return get_hint(base, index, data) all_widths @@ -120,15 +120,15 @@ namespace { //-------------------------------- template - void set_hint(shared_ptr base, unsigned index, vector const &data) { + void set_hint(boost::shared_ptr base, unsigned index, vector const &data) { typedef hint_traits traits; typedef array ha; - shared_ptr a = downcast_array(base); + boost::shared_ptr a = downcast_array(base); a->set(index, data); } - void set_hint_(uint32_t width, shared_ptr base, + void set_hint_(uint32_t width, boost::shared_ptr base, unsigned index, vector const &data) { switch (width) { #define xx(n) case n: return set_hint(base, index, data) @@ -140,15 +140,15 @@ namespace { //-------------------------------- template - void grow(shared_ptr base, unsigned new_nr_entries, vector const &value) { + void grow(boost::shared_ptr base, unsigned new_nr_entries, vector const &value) { typedef hint_traits traits; typedef array ha; - shared_ptr a = downcast_array(base); + boost::shared_ptr a = downcast_array(base); a->grow(new_nr_entries, value); } - void grow_(uint32_t width, shared_ptr base, + void grow_(uint32_t width, boost::shared_ptr base, unsigned new_nr_entries, vector const &value) { switch (width) { @@ -194,17 +194,17 @@ namespace { }; template - void walk_hints(shared_ptr base, hint_visitor &hv, damage_visitor &dv) { + void walk_hints(boost::shared_ptr base, hint_visitor &hv, damage_visitor &dv) { typedef hint_traits traits; typedef array ha; - shared_ptr a = downcast_array(base); + boost::shared_ptr a = downcast_array(base); value_adapter vv(hv); ll_damage_visitor ll(dv); a->visit_values(vv, ll); } - void walk_hints_(uint32_t width, shared_ptr base, + void walk_hints_(uint32_t width, boost::shared_ptr base, hint_visitor &hv, damage_visitor &dv) { switch (width) { #define xx(n) case n: walk_hints(base, hv, dv); break From 691ad882613ad061172095d881bb36b221a64d93 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Sat, 15 Nov 2014 14:45:11 +0100 Subject: [PATCH 144/165] [caching/hint_array.cc] Fix ambigious array (C++11) Template array exist in the namespace persistent_data as well as in std of C++11. Explicitly use the one from persistent_data. This fixes compilation bugs with CXXFLAGS=-std=gnu++11 together with gcc 4.8.3 and boost 1.55. --- caching/hint_array.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/caching/hint_array.cc b/caching/hint_array.cc index 7fccb82..66a3a27 100644 --- a/caching/hint_array.cc +++ b/caching/hint_array.cc @@ -40,7 +40,7 @@ namespace { template boost::shared_ptr mk_array(transaction_manager &tm) { typedef hint_traits traits; - typedef array ha; + typedef persistent_data::array ha; boost::shared_ptr r = typename ha::ptr(new ha(tm, typename traits::ref_counter())); @@ -78,7 +78,7 @@ namespace { template boost::shared_ptr mk_array(transaction_manager &tm, block_address root, unsigned nr_entries) { typedef hint_traits traits; - typedef array ha; + typedef persistent_data::array ha; boost::shared_ptr r = typename ha::ptr(new ha(tm, typename traits::ref_counter(), root, nr_entries)); @@ -103,7 +103,7 @@ namespace { template void get_hint(boost::shared_ptr base, unsigned index, vector &data) { typedef hint_traits traits; - typedef array ha; + typedef persistent_data::array ha; boost::shared_ptr a = downcast_array(base); data = a->get(index); @@ -122,7 +122,7 @@ namespace { template void set_hint(boost::shared_ptr base, unsigned index, vector const &data) { typedef hint_traits traits; - typedef array ha; + typedef persistent_data::array ha; boost::shared_ptr a = downcast_array(base); a->set(index, data); @@ -142,7 +142,7 @@ namespace { template void grow(boost::shared_ptr base, unsigned new_nr_entries, vector const &value) { typedef hint_traits traits; - typedef array ha; + typedef persistent_data::array ha; boost::shared_ptr a = downcast_array(base); a->grow(new_nr_entries, value); @@ -196,7 +196,7 @@ namespace { template void walk_hints(boost::shared_ptr base, hint_visitor &hv, damage_visitor &dv) { typedef hint_traits traits; - typedef array ha; + typedef persistent_data::array ha; boost::shared_ptr a = downcast_array(base); value_adapter vv(hv); From b56aec4d962f5f5ff17f2f1e42d0968c85aff098 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Sat, 15 Nov 2014 15:42:58 +0100 Subject: [PATCH 145/165] [unit-tests/bloom_filter_t.cc] Fix ambigious uniform_int_distribution (C++11) uniform_int_distribution exist in the namespace boost as well as in std of C++11. Use the one provided by boost. This fixes compilation bugs with CXXFLAGS=-std=gnu++11 together with gcc 4.8.3 and boost 1.55. --- unit-tests/bloom_filter_t.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit-tests/bloom_filter_t.cc b/unit-tests/bloom_filter_t.cc index 032eb48..13c2418 100644 --- a/unit-tests/bloom_filter_t.cc +++ b/unit-tests/bloom_filter_t.cc @@ -40,7 +40,7 @@ namespace { using namespace boost::random; - uniform_int_distribution uniform_dist(0, max); + boost::random::uniform_int_distribution uniform_dist(0, max); while (r.size() < count) { block_address b = uniform_dist(rng_); From 8e921580554ed91e84bb68ea32a8c2ea47ad6ff3 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 18 Nov 2014 16:03:03 +0000 Subject: [PATCH 146/165] [thin_trim] first code drop. No testing done as yet. --- Makefile.in | 3 + thin-provisioning/commands.h | 1 + thin-provisioning/thin_trim.cc | 198 +++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 thin-provisioning/thin_trim.cc diff --git a/Makefile.in b/Makefile.in index ff2a2a3..fe666f5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -86,6 +86,7 @@ SOURCE=\ thin-provisioning/thin_repair.cc \ thin-provisioning/thin_restore.cc \ thin-provisioning/thin_rmap.cc \ + thin-provisioning/thin_trim.cc \ thin-provisioning/xml_format.cc CC:=@CC@ @@ -171,6 +172,7 @@ install: bin/pdata_tools ln -s -f pdata_tools $(BINDIR)/thin_repair ln -s -f pdata_tools $(BINDIR)/thin_restore ln -s -f pdata_tools $(BINDIR)/thin_rmap + ln -s -f pdata_tools $(BINDIR)/thin_trim ln -s -f pdata_tools $(BINDIR)/thin_metadata_size ln -s -f pdata_tools $(BINDIR)/era_check ln -s -f pdata_tools $(BINDIR)/era_dump @@ -186,6 +188,7 @@ install: bin/pdata_tools $(INSTALL_DATA) man8/thin_repair.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_restore.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_rmap.8 $(MANPATH)/man8 + $(INSTALL_DATA) man8/thin_trim.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_metadata_size.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/era_check.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/era_dump.8 $(MANPATH)/man8 diff --git a/thin-provisioning/commands.h b/thin-provisioning/commands.h index 5635665..de63e53 100644 --- a/thin-provisioning/commands.h +++ b/thin-provisioning/commands.h @@ -13,6 +13,7 @@ namespace thin_provisioning { extern base::command thin_restore_cmd; extern base::command thin_repair_cmd; extern base::command thin_rmap_cmd; + extern base::command thin_trim_cmd; extern base::command thin_metadata_size_cmd; } diff --git a/thin-provisioning/thin_trim.cc b/thin-provisioning/thin_trim.cc new file mode 100644 index 0000000..ae57092 --- /dev/null +++ b/thin-provisioning/thin_trim.cc @@ -0,0 +1,198 @@ +#include +#include +#include +#include + +#undef BLOCK_SIZE + +#include "thin-provisioning/commands.h" +#include "metadata.h" +#include "version.h" + +using namespace persistent_data; +using namespace std; +using namespace thin_provisioning; + +//---------------------------------------------------------------- + +namespace { + void confirm_pool_is_not_active() { + cout << "The pool must *not* be active when running this tool.\n" + << "Do you wish to continue? [Y/N]\n" + << endl; + + string input; + cin >> input; + if (input != "Y") + exit(0); + } + + class discard_emitter { + public: + discard_emitter(string const &data_dev, unsigned block_size, uint64_t nr_blocks) + : fd_(open_dev(data_dev, block_size * nr_blocks)), + block_size_(block_size) { + } + + ~discard_emitter() { + ::close(fd_); + } + + void emit(block_address b, block_address e) { + uint64_t range[2]; + + range[0] = block_to_byte(b); + range[1] = block_to_byte(e) - range[0]; + + if (ioctl(fd_, BLKDISCARD, &range)) + throw runtime_error("discard ioctl failed"); + } + + private: + static int open_dev(string const &data_dev, uint64_t expected_size) { + int r, fd; + uint64_t blksize; + struct stat info; + + fd = ::open(data_dev.c_str(), O_WRONLY); + if (fd < 0) { + ostringstream out; + out << "Couldn't open data device '" << data_dev << "'"; + throw runtime_error(out.str()); + } + + try { + r = fstat(fd, &info); + if (r) + throw runtime_error("Couldn't stat data device"); + + if (!S_ISBLK(info.st_mode)) + throw runtime_error("Data device is not a block device"); + + r = ioctl(fd, BLKGETSIZE64, &blksize); + if (r) + throw runtime_error("Couldn't get data device size"); + + if (blksize != (expected_size << 9)) + throw runtime_error("Data device is not the expected size"); + + } catch (...) { + ::close(fd); + throw; + } + + return fd; + } + + uint64_t block_to_byte(block_address b) { + return (b * block_size_) << 9; + } + + int fd_; + unsigned block_size_; + }; + + class trim_visitor : public space_map_detail::visitor { + public: + trim_visitor(discard_emitter &e) + : emitter_(e) { + } + + virtual void visit(space_map_detail::missing_counts const &mc) { + throw std::runtime_error("corrupt metadata, please use thin_check for details"); + } + + virtual void visit(block_address b, uint32_t count) { + if (last_visited_ && (b > *last_visited_ + 1)) + emitter_.emit(*last_visited_ + 1, b); + + last_visited_ = b; + } + + private: + discard_emitter &emitter_; + boost::optional last_visited_; + }; + + int trim(string const &metadata_dev, string const &data_dev) { + // We can trim any block that has zero count in the data + // space map. + metadata md(metadata_dev, 0); + + if (!md.data_sm_->get_nr_free()) + return 0; + + discard_emitter de(data_dev, md.sb_.data_block_size_, + md.data_sm_->get_nr_blocks()); + trim_visitor tv(de); + + confirm_pool_is_not_active(); + md.data_sm_->visit(tv); + + return 0; + } + + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options] {device|file}\n" + << "Options:\n" + << " {--pool-inactive}\n" + << " {-h|--help}\n" + << " {-V|--version}" << endl; + } + + struct flags { + boost::optional metadata_dev; + boost::optional data_dev; + }; +} + +//---------------------------------------------------------------- + +int thin_trim_main(int argc, char **argv) +{ + int c; + flags fs; + const char shortopts[] = "hV"; + + const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "metadata-dev", required_argument, NULL, 0 }, + { "data-dev", required_argument, NULL, 1 }, + { "pool-inactive", no_argument, NULL, 2 }, + { NULL, no_argument, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch(c) { + case 0: + fs.metadata_dev = optarg; + break; + + case 1: + fs.data_dev = optarg; + break; + + case 'h': + usage(cout, basename(argv[0])); + return 0; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; + } + } + + if (!fs.metadata_dev || !fs.data_dev) { + usage(cerr, basename(argv[0])); + return 1; + } + + return trim(*fs.metadata_dev, *fs.data_dev); +} + +//---------------------------------------------------------------- From dd9bd206c65f877b86ad33c02a543272c5acf876 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:12:30 +0000 Subject: [PATCH 147/165] Old glibc doesn't provide these macros, so we have to define them. Signed-off-by: Mikulas Patocka --- base/endian_utils.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/base/endian_utils.h b/base/endian_utils.h index 3ad75ae..82b5b59 100644 --- a/base/endian_utils.h +++ b/base/endian_utils.h @@ -25,6 +25,26 @@ //---------------------------------------------------------------- +/* An old glic doesn't provide these macros */ +#if !defined(htole16) || !defined(le16toh) || !defined(htole32) || !defined(le32toh) || !defined(htole64) || !defined(le64toh) +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htole16(x) (x) +#define le16toh(x) (x) +#define htole32(x) (x) +#define le32toh(x) (x) +#define htole64(x) (x) +#define le64toh(x) (x) +#else +#define htole16(x) __bswap_16(x) +#define le16toh(x) __bswap_16(x) +#define htole32(x) __bswap_32(x) +#define le32toh(x) __bswap_32(x) +#define htole64(x) __bswap_64(x) +#define le64toh(x) __bswap_64(x) +#endif +#endif + namespace base { // These are just little wrapper types to make the compiler From 150a3c486d90ac18ed2d190b3a97f5163ef127fd Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:13:36 +0000 Subject: [PATCH 148/165] Fix these errors: caching/superblock.cc:306: error: reference to 'validator' is ambiguous caching/superblock.cc:271: error: candidates are: namespace validator { } ./block-cache/block_cache.h:22: error: class bcache::validator caching/superblock.cc:316: error: reference to 'validator' is ambiguous caching/superblock.cc:271: error: candidates are: namespace validator { } ./block-cache/block_cache.h:22: error: class bcache::validator Signed-off-by: Mikulas Patocka --- caching/superblock.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/caching/superblock.cc b/caching/superblock.cc index 7ecc916..4089eee 100644 --- a/caching/superblock.cc +++ b/caching/superblock.cc @@ -302,8 +302,9 @@ namespace validator { superblock caching::read_superblock(block_manager<>::ptr bm, block_address location) { + using namespace validator; superblock sb; - block_manager<>::read_ref r = bm->read_lock(location, validator::mk_v()); + block_manager<>::read_ref r = bm->read_lock(location, mk_v()); superblock_disk const *sbd = reinterpret_cast(r.data()); superblock_traits::unpack(*sbd, sb); @@ -313,7 +314,8 @@ caching::read_superblock(block_manager<>::ptr bm, block_address location) void caching::write_superblock(block_manager<>::ptr bm, superblock const &sb, block_address location) { - block_manager<>::write_ref w = bm->superblock_zero(location, validator::mk_v()); + using namespace validator; + block_manager<>::write_ref w = bm->superblock_zero(location, mk_v()); superblock_traits::pack(sb, *reinterpret_cast(w.data())); } From fe64da2c7cffcbc1bdc229c686ba412c3461ac64 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:14:28 +0000 Subject: [PATCH 149/165] Fix these errors: thin-provisioning/thin_pool.cc:206: error: reference to 'sector_t' is ambiguous ./thin-provisioning/metadata.h:40: error: candidates are: typedef uint64_t thin_provisioning::sector_t ./block-cache/block_cache.h:20: error: typedef uint64_t bcache::sector_t thin-provisioning/thin_pool.cc:206: error: reference to 'sector_t' is ambiguous ./thin-provisioning/metadata.h:40: error: candidates are: typedef uint64_t thin_provisioning::sector_t ./block-cache/block_cache.h:20: error: typedef uint64_t bcache::sector_t thin-provisioning/thin_pool.cc:206: error: 'sector_t' does not name a type Signed-off-by: Mikulas Patocka --- thin-provisioning/thin_pool.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thin-provisioning/thin_pool.cc b/thin-provisioning/thin_pool.cc index 53940e3..1596c90 100644 --- a/thin-provisioning/thin_pool.cc +++ b/thin-provisioning/thin_pool.cc @@ -203,7 +203,7 @@ thin_pool::get_nr_free_data_blocks() const return md_->data_sm_->get_nr_free(); } -sector_t +thin_provisioning::sector_t thin_pool::get_data_block_size() const { return md_->sb_.data_block_size_; From bd2c0df22681294c01359897332dd69ba2bd6f6f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:15:01 +0000 Subject: [PATCH 150/165] Fix this error: persistent-data/data-structures/bloom_filter.cc:10: error: integer constant is too large for 'unsigned long' type Signed-off-by: Mikulas Patocka --- persistent-data/data-structures/bloom_filter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistent-data/data-structures/bloom_filter.cc b/persistent-data/data-structures/bloom_filter.cc index a250835..08516e1 100644 --- a/persistent-data/data-structures/bloom_filter.cc +++ b/persistent-data/data-structures/bloom_filter.cc @@ -7,7 +7,7 @@ using namespace persistent_data; //---------------------------------------------------------------- namespace { - static const uint64_t m1 = 0x9e37fffffffc0001UL; + static const uint64_t m1 = 0x9e37fffffffc0001ULL; static const unsigned bits = 18; static uint32_t hash1(block_address const &b) { From f25e0ca6d342e5f0d0dfb15f11bbfec59460b06f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:15:55 +0000 Subject: [PATCH 151/165] g++-4.2 and older doesn't accept binary constants. Signed-off-by: Mikulas Patocka --- persistent-data/space-maps/disk.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/persistent-data/space-maps/disk.cc b/persistent-data/space-maps/disk.cc index c9ca9ea..bc017c3 100644 --- a/persistent-data/space-maps/disk.cc +++ b/persistent-data/space-maps/disk.cc @@ -112,7 +112,7 @@ namespace { ref_t b1 = test_bit_le(bits, b * 2); ref_t b2 = test_bit_le(bits, b * 2 + 1); ref_t result = b2 ? 1 : 0; - result |= b1 ? 0b10 : 0; + result |= b1 ? 2 : 0; return result; } @@ -165,7 +165,7 @@ namespace { ref_t b1 = test_bit_le(bits, b * 2); ref_t b2 = test_bit_le(bits, b * 2 + 1); ref_t result = b2 ? 1 : 0; - result |= b1 ? 0b10 : 0; + result |= b1 ? 2 : 0; it(offset + b, result); } } From 50341faa64b2b252385305efd930ce78aca590e4 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:18:21 +0000 Subject: [PATCH 152/165] Fix these errors: unit-tests/array_block_t.cc:38: error: using 'typename' outside of template unit-tests/array_block_t.cc:39: error: using 'typename' outside of template unit-tests/array_block_t.cc:40: error: using 'typename' outside of template Signed-off-by: Mikulas Patocka --- unit-tests/array_block_t.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unit-tests/array_block_t.cc b/unit-tests/array_block_t.cc index 2ba122d..9ce3823 100644 --- a/unit-tests/array_block_t.cc +++ b/unit-tests/array_block_t.cc @@ -35,9 +35,9 @@ using namespace testing; namespace { uint64_t MAX_VALUE = 1000ull; block_address const NR_BLOCKS = 1024; - typedef typename bcache::noop_validator noop_validator; - typedef typename block_manager<>::read_ref read_ref; - typedef typename block_manager<>::write_ref write_ref; + typedef bcache::noop_validator noop_validator; + typedef block_manager<>::read_ref read_ref; + typedef block_manager<>::write_ref write_ref; // FIXME: lift to utils? class simple_ref_counter { From ef517035f114d35b7402ff03783c3017d549348a Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:19:25 +0000 Subject: [PATCH 153/165] The file boost/random/uniform_int_distribution.hpp was introduced in boost version 1.47. If we have older Boost, use random numbers from libc. Signed-off-by: Mikulas Patocka --- unit-tests/bloom_filter_t.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/unit-tests/bloom_filter_t.cc b/unit-tests/bloom_filter_t.cc index 13c2418..bf44ffa 100644 --- a/unit-tests/bloom_filter_t.cc +++ b/unit-tests/bloom_filter_t.cc @@ -5,8 +5,16 @@ #include "persistent-data/data-structures/array_block.h" #include "test_utils.h" +#include + +#if BOOST_VERSION >= 104700 +#define HAVE_RANDOM_UNIFORM_INT_DISTRIBUTION +#endif + #include +#ifdef HAVE_RANDOM_UNIFORM_INT_DISTRIBUTION #include +#endif #include #include #include @@ -40,10 +48,16 @@ namespace { using namespace boost::random; +#ifdef HAVE_RANDOM_UNIFORM_INT_DISTRIBUTION boost::random::uniform_int_distribution uniform_dist(0, max); +#endif while (r.size() < count) { +#ifdef HAVE_RANDOM_UNIFORM_INT_DISTRIBUTION block_address b = uniform_dist(rng_); +#else + block_address b = random() % max; +#endif r.insert(b); } @@ -75,7 +89,9 @@ namespace { space_map::ptr sm_; transaction_manager tm_; +#ifdef HAVE_RANDOM_UNIFORM_INT_DISTRIBUTION boost::random::mt19937 rng_; +#endif }; } @@ -312,3 +328,4 @@ TEST_F(BloomFilterTests, false_positives_over_multiple_eras) } //---------------------------------------------------------------- + From f1130198e14d99f7f5805e661367e985b7f8a4f8 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 10:48:19 +0000 Subject: [PATCH 154/165] include libgen.h in application.cc for the declaration of basename. Unfortunately it defines basename as a macro, so also change member function name from basename() to get_basename(). --- base/application.cc | 5 +++-- base/application.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/base/application.cc b/base/application.cc index 6012e06..9e1f0dd 100644 --- a/base/application.cc +++ b/base/application.cc @@ -1,5 +1,6 @@ #include "base/application.h" +#include #include #include @@ -11,7 +12,7 @@ using namespace std; int application::run(int argc, char **argv) { - string cmd = basename(argv[0]); + string cmd = get_basename(argv[0]); if (cmd == string("pdata_tools")) { argc--; @@ -49,7 +50,7 @@ application::usage() } std::string -application::basename(std::string const &path) const +application::get_basename(std::string const &path) const { char buffer[PATH_MAX + 1]; diff --git a/base/application.h b/base/application.h index 8585129..d01eb36 100644 --- a/base/application.h +++ b/base/application.h @@ -41,7 +41,7 @@ namespace base { private: void usage(); - std::string basename(std::string const &path) const; + std::string get_basename(std::string const &path) const; std::list cmds_; }; From 25b4b526f495551811778d2273bc32359e735925 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 16 Jan 2015 12:54:09 +0000 Subject: [PATCH 155/165] Introduce error_string() as a portable replacement for strerror_r() --- Makefile.in | 2 ++ caching/cache_check.cc | 6 ++---- configure.ac | 8 ++++++++ era/era_check.cc | 6 ++---- persistent-data/block.tcc | 8 +++----- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Makefile.in b/Makefile.in index fe666f5..8ce0f17 100644 --- a/Makefile.in +++ b/Makefile.in @@ -29,6 +29,7 @@ SOURCE=\ base/base64.cc \ base/endian_utils.cc \ base/error_state.cc \ + base/error_string.cc \ base/progress_monitor.cc \ base/xml_utils.cc \ block-cache/block_cache.cc \ @@ -98,6 +99,7 @@ CFLAGS+=-g -Wall -O3 CXXFLAGS+=-g -Wall -fno-strict-aliasing CXXFLAGS+=@CXXOPTIMISE_FLAG@ CXXFLAGS+=@CXXDEBUG_FLAG@ +CXXFLAGS+=@CXX_STRERROR_FLAG@ INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR) -I$(TOP_DIR)/thin-provisioning LIBS:=-lstdc++ -laio -lexpat INSTALL:=@INSTALL@ diff --git a/caching/cache_check.cc b/caching/cache_check.cc index 400b214..ba750d5 100644 --- a/caching/cache_check.cc +++ b/caching/cache_check.cc @@ -13,6 +13,7 @@ #include #include "base/error_state.h" +#include "base/error_string.h" #include "base/nested_output.h" #include "caching/commands.h" #include "caching/metadata.h" @@ -202,10 +203,7 @@ namespace { int r = ::stat(path.c_str(), &info); if (r) { ostringstream msg; - char buffer[128], *ptr; - - ptr = ::strerror_r(errno, buffer, sizeof(buffer)); - msg << path << ": " << ptr; + msg << path << ": " << error_string(errno); throw runtime_error(msg.str()); } diff --git a/configure.ac b/configure.ac index f15906b..3e6c6a9 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,13 @@ AC_PROG_MAKE_SET AC_PROG_MKDIR_P AC_PROG_INSTALL +################################################################ +dnl -- Checks for functions. +AC_FUNC_STRERROR_R +if test x$ac_cv_func_strerror_r_char_p = xyes; then + CXX_STRERROR_FLAG="-DSTRERROR_R_CHAR_P" +fi + ################################################################################ dnl -- Prefix is /usr by default, the exec_prefix default is setup later AC_PREFIX_DEFAULT(/usr) @@ -136,6 +143,7 @@ VERSION_PATCHLEVEL=`echo "$VER" | $AWK -F '[[(.]]' '{print $3}'` ################################################################ AC_SUBST(CXXDEBUG_FLAG) AC_SUBST(CXXOPTIMISE_FLAG) +AC_SUBST(CXX_STRERROR_FLAG) AC_SUBST(INSTALL) AC_SUBST(prefix) AC_SUBST(RELEASE_DATE) diff --git a/era/era_check.cc b/era/era_check.cc index fed199e..d64999d 100644 --- a/era/era_check.cc +++ b/era/era_check.cc @@ -13,6 +13,7 @@ #include #include "base/error_state.h" +#include "base/error_string.h" #include "base/nested_output.h" #include "era/commands.h" #include "era/writeset_tree.h" @@ -186,10 +187,7 @@ namespace { int r = ::stat(path.c_str(), &info); if (r) { ostringstream msg; - char buffer[128], *ptr; - - ptr = ::strerror_r(errno, buffer, sizeof(buffer)); - msg << path << ": " << ptr; + msg << path << ": " << error_string(errno);; throw runtime_error(msg.str()); } diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index f2f3a7a..529f7af 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -18,13 +18,14 @@ #include "block.h" +#include "base/error_string.h" + #include #include #include #include #include #include -#include #include #include @@ -44,11 +45,8 @@ namespace { // FIXME: introduce a new exception for this, or at least lift this // to exception.h void syscall_failed(char const *call) { - char buffer[128]; - char *msg = strerror_r(errno, buffer, sizeof(buffer)); - ostringstream out; - out << "syscall '" << call << "' failed: " << msg; + out << "syscall '" << call << "' failed: " << base::error_string(errno);; throw runtime_error(out.str()); } From 408b38a0f8d1da9ec7cb8f0d62ac2bc3e63ba123 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Sat, 17 Jan 2015 10:22:57 +0000 Subject: [PATCH 156/165] Forgot error_string.h/cc from previous commit --- base/error_string.cc | 39 +++++++++++++++++++++++++++++++++++++++ base/error_string.h | 16 ++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 base/error_string.cc create mode 100644 base/error_string.h diff --git a/base/error_string.cc b/base/error_string.cc new file mode 100644 index 0000000..6cb02c4 --- /dev/null +++ b/base/error_string.cc @@ -0,0 +1,39 @@ +#include "base/error_string.h" + +#include +#include + +#include + +using namespace std; + +//---------------------------------------------------------------- + +#ifdef STRERROR_R_CHAR_P + +string base::error_string(int err) +{ + char *ptr; + char buffer[128]; + + ptr = strerror_r(errno, buffer, sizeof(buffer)); + return string(ptr); +} + +#else + +string base::error_string(int err) +{ + int r; + char buffer[128]; + + r = strerror_r(errno, buffer, sizeof(buffer)); + if (r) + throw runtime_error("strerror_r failed"); + + return string(buffer); +} + +#endif + +//---------------------------------------------------------------- diff --git a/base/error_string.h b/base/error_string.h new file mode 100644 index 0000000..dd7549a --- /dev/null +++ b/base/error_string.h @@ -0,0 +1,16 @@ +#ifndef BASE_ERROR_STRING_H +#define BASE_ERROR_STRING_H + +#include + +//---------------------------------------------------------------- + +namespace base { + // There are a couple of version of strerror_r kicking around, so + // we wrap it. + std::string error_string(int err); +} + +//---------------------------------------------------------------- + +#endif From c6ae25417bad8ddb4240f573dd599e4995886ee2 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Sat, 17 Jan 2015 11:45:09 +0000 Subject: [PATCH 157/165] Add missing include to thin_trim --- thin-provisioning/thin_trim.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/thin-provisioning/thin_trim.cc b/thin-provisioning/thin_trim.cc index ae57092..0118b62 100644 --- a/thin-provisioning/thin_trim.cc +++ b/thin-provisioning/thin_trim.cc @@ -2,6 +2,7 @@ #include #include #include +#include #undef BLOCK_SIZE From 4d7da25859333c1756971d48367cb9b831c310fa Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 10 Mar 2015 08:52:13 +0000 Subject: [PATCH 158/165] Add thin_trim man page --- man8/thin_trim.8 | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 man8/thin_trim.8 diff --git a/man8/thin_trim.8 b/man8/thin_trim.8 new file mode 100644 index 0000000..de702f0 --- /dev/null +++ b/man8/thin_trim.8 @@ -0,0 +1,34 @@ +.TH THIN_TRIM 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*- +.SH NAME +thin_trim \- Issue discard requests for free pool space (offline tool). + +.SH SYNOPSIS +.B thin_trim +.RB [ options ] +.I {device|file} + +.SH DESCRIPTION +.B thin_trim +sends discard requests to the pool device for unprovisioned areas. It is an offline tool, +.B do not run it while the pool is active +. + +.SH OPTIONS +.IP "\fB\-\-pool-inactive\fP" +Indicates you are aware the pool should be inactive. Suppresses a warning message and prompt. + +.IP "\fB\-h, \-\-help\fP" +Print help and exit. + +.IP "\fB\-V, \-\-version\fP" +Output version information and exit. + +.SH SEE ALSO +.B thin_dump(8) +.B thin_repair(8) +.B thin_restore(8) +.B thin_rmap(8) +.B thin_metadata_size(8) + +.SH AUTHOR +Joe Thornber From 45422dbf7ae2f41ba169e6a68657745fa1309a99 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 24 Mar 2015 13:36:45 +0000 Subject: [PATCH 159/165] [thin_delta] Mappings were being missed off from the tail of a device --- thin-provisioning/thin_delta.cc | 81 ++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index da3a890..b0c460d 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -123,51 +123,63 @@ namespace local { class mapping_recorder { public: mapping_recorder() { - reset_range(); + no_range(); } void visit(btree_path const &path, mapping_tree_detail::block_time const &bt) { record(path[0], bt.block_); } + void complete() { + if (range_in_progress()) { + push_range(); + no_range(); + } + } + mapping_deque const &get_mappings() const { return mappings_; } private: - void reset_range() { + void no_range() { obegin_ = oend_ = 0; dbegin_ = dend_ = 0; } - void record(uint64_t oblock, uint64_t dblock) { - if (obegin_ == oend_) { - // We're starting a new range - obegin_ = oblock; - oend_ = obegin_; - - dbegin_ = dblock; - dend_ = dbegin_; - - } else { - if (oblock != oend_ || dblock != dend_) { - // Emit the current range ... - push_mapping(obegin_, dbegin_, oend_ - obegin_); - - obegin_ = oblock; - oend_ = obegin_; - - dbegin_ = dblock; - dend_ = dbegin_; - } - } - + void inc_range() { oend_++; dend_++; } - void push_mapping(uint64_t vbegin, uint64_t dbegin, uint64_t len) { - mappings_.push_back(mapping(vbegin, dbegin, len)); + void begin_range(uint64_t oblock, uint64_t dblock) { + obegin_ = oend_ = oblock; + dbegin_ = dend_ = dblock; + inc_range(); + } + + bool range_in_progress() { + return oend_ != obegin_; + } + + bool continues_range(uint64_t oblock, uint64_t dblock) { + return (oblock == oend_) && (dblock == dend_); + } + + void push_range() { + mapping m(obegin_, dbegin_, oend_ - obegin_); + mappings_.push_back(m); + } + + void record(uint64_t oblock, uint64_t dblock) { + if (!range_in_progress()) + begin_range(oblock, dblock); + + else if (!continues_range(oblock, dblock)) { + push_range(); + begin_range(oblock, dblock); + } else + inc_range(); } uint64_t obegin_, oend_; @@ -437,6 +449,20 @@ namespace local { } } + while (left_it != left.end()) { + left_mapping = *left_it++; + + if (left_mapping.len_) + e.left_only(left_mapping.vbegin_, left_mapping.dbegin_, left_mapping.len_); + } + + while (right_it != right.end()) { + right_mapping = *right_it++; + + if (right_mapping.len_) + e.right_only(right_mapping.vbegin_, right_mapping.dbegin_, right_mapping.len_); + } + e.complete(); } @@ -476,7 +502,10 @@ namespace local { single_mapping_tree snap2(*tm, *snap2_root, mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); btree_visit_values(snap1, mr1, damage_v); + mr1.complete(); + btree_visit_values(snap2, mr2, damage_v); + mr2.complete(); } if (fs.verbose) { From 055b64a9c04dc78a62ec78f5d03b1a50a8c12f54 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 24 Mar 2015 14:08:21 +0000 Subject: [PATCH 160/165] Add a simple man page for thin_delta --- man8/thin_delta.8 | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 man8/thin_delta.8 diff --git a/man8/thin_delta.8 b/man8/thin_delta.8 new file mode 100644 index 0000000..1ebfcbe --- /dev/null +++ b/man8/thin_delta.8 @@ -0,0 +1,47 @@ +.TH THIN_DELTA 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*- +.SH NAME +thin_delta \- Print the differences in the mappings between two thin devices. + +.SH SYNOPSIS +.B thin_delta +.RB [ options ] +.I {device|file} + +.SH DESCRIPTION +.B thin_delta +allows you to compare the mappings in two thin volumes (snapshots allow common blocks between thin volumes). +. + +.SH OPTIONS +.IP "\fB\-\-thin1, \-\-snap1\fP" +The numeric identifier for the first thin volume to diff. + +.IP "\fB\-\-thin1, \-\-snap1\fP" +The numeric identifier for the second thin volume to diff. + +.IP "\fB\-m, \-\-metadata\-snap\fP [block#]" + +If you want to get information out of a live pool then you will need +to take a metadata snapshot and use this switch. In order for the +information to be meaningful you need to ensure the thin volumes +you're examining are not changing (eg, do not activate those thins). + +.IP "\fB\-\-verbose" +Provide extra information on the mappings. + +.IP "\fB\-h, \-\-help\fP" +Print help and exit. + +.IP "\fB\-V, \-\-version\fP" +Output version information and exit. + +.SH SEE ALSO +.B thin_dump(8) +.B thin_repair(8) +.B thin_restore(8) +.B thin_rmap(8) +.B thin_trim(8) +.B thin_metadata_size(8) + +.SH AUTHOR +Joe Thornber From 182be112fa8ba915127ce44fa7cb85f1d46e56ef Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 24 Mar 2015 14:09:36 +0000 Subject: [PATCH 161/165] Add thin_delta to the build --- CHANGES | 5 +++++ Makefile.in | 2 ++ 2 files changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 7e22fdc..3de75c1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +v0.5 +==== + +- thin_delta, thin_trim + v0.4 ==== diff --git a/Makefile.in b/Makefile.in index 8ce0f17..eaff125 100644 --- a/Makefile.in +++ b/Makefile.in @@ -170,6 +170,7 @@ install: bin/pdata_tools ln -s -f pdata_tools $(BINDIR)/cache_repair ln -s -f pdata_tools $(BINDIR)/cache_restore ln -s -f pdata_tools $(BINDIR)/thin_check + ln -s -f pdata_tools $(BINDIR)/thin_delta ln -s -f pdata_tools $(BINDIR)/thin_dump ln -s -f pdata_tools $(BINDIR)/thin_repair ln -s -f pdata_tools $(BINDIR)/thin_restore @@ -186,6 +187,7 @@ install: bin/pdata_tools $(INSTALL_DATA) man8/cache_repair.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/cache_restore.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_check.8 $(MANPATH)/man8 + $(INSTALL_DATA) man8/thin_delta.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_dump.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_repair.8 $(MANPATH)/man8 $(INSTALL_DATA) man8/thin_restore.8 $(MANPATH)/man8 From 0e72f772d0bec63360fc0e69fa25147528b977ed Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 25 Mar 2015 10:09:39 +0000 Subject: [PATCH 162/165] [thin_delta] Add superblock and diff tags --- thin-provisioning/thin_delta.cc | 116 ++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 20 deletions(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index b0c460d..196887d 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -6,9 +6,11 @@ #include "version.h" +#include "base/indented_stream.h" #include "persistent-data/data-structures/btree_damage_visitor.h" #include "persistent-data/run.h" #include "persistent-data/space-maps/core.h" +#include "persistent-data/space-maps/disk.h" #include "persistent-data/file_utils.h" #include "thin-provisioning/superblock.h" #include "thin-provisioning/mapping_tree.h" @@ -27,8 +29,11 @@ namespace local { } void usage(ostream &out) { - out << "Usage: " << cmd_ << " [options] --snap1 --snap2 \n" + out << "Usage: " << cmd_ << " [options] \n" << "Options:\n" + << " {--thin1, --snap1}\n" + << " {--thin2, --snap2}\n" + << " {-m, --metadata-snap}\n" << " {--verbose}\n" << " {-h|--help}\n" << " {-V|--version}" << endl; @@ -201,7 +206,7 @@ namespace local { class diff_emitter { public: - diff_emitter(ostream &out) + diff_emitter(indented_stream &out) : out_(out) { } @@ -212,18 +217,22 @@ namespace local { virtual void complete() = 0; protected: - ostream &out() { + void indent() { + out_.indent(); + } + + indented_stream &out() { return out_; } private: - ostream &out_; + indented_stream &out_; }; class simple_emitter : public diff_emitter { public: - simple_emitter(ostream &out) + simple_emitter(indented_stream &out) : diff_emitter(out) { } @@ -272,6 +281,7 @@ namespace local { if (!current_type_) return; + indent(); switch (*current_type_) { case LEFT_ONLY: out() << "\n"; } void right_only(uint64_t vbegin, uint64_t dbegin, uint64_t len) { begin_block(RIGHT_ONLY); - out() << " \n"; } void blocks_differ(uint64_t vbegin, uint64_t left_dbegin, uint64_t right_dbegin, uint64_t len) { begin_block(DIFFER); - out() << " \n"; @@ -328,7 +341,8 @@ namespace local { void blocks_same(uint64_t vbegin, uint64_t dbegin, uint64_t len) { begin_block(SAME); - out() << " \n"; } @@ -359,6 +373,7 @@ namespace local { } void open(block_type t) { + indent(); switch (t) { case LEFT_ONLY: out() << "\n"; @@ -376,24 +391,27 @@ namespace local { out() << "\n"; break; } + out().inc(); } void close(block_type t) { + out().dec(); + indent(); switch (t) { case LEFT_ONLY: - out() << "\n\n"; + out() << "\n"; break; case RIGHT_ONLY: - out() << "\n\n"; + out() << "\n"; break; case DIFFER: - out() << "\n\n"; + out() << "\n"; break; case SAME: - out() << "\n\n"; + out() << "\n"; break; } @@ -466,16 +484,59 @@ namespace local { e.complete(); } + // FIXME: duplication with xml_format + void begin_superblock(indented_stream &out, + string const &uuid, + uint64_t time, + uint64_t trans_id, + uint32_t data_block_size, + uint64_t nr_data_blocks, + boost::optional metadata_snap) { + out.indent(); + out << "\n"; + out.inc(); + } + + void end_superblock(indented_stream &out) { + out.dec(); + out.indent(); + out << "\n"; + } + + void begin_diff(indented_stream &out, uint64_t snap1, uint64_t snap2) { + out.indent(); + out << "\n"; + out.inc(); + } + + void end_diff(indented_stream &out) { + out.dec(); + out.indent(); + out << "\n"; + } + void delta_(application &app, flags const &fs) { mapping_recorder mr1; mapping_recorder mr2; damage_visitor damage_v; + superblock_detail::superblock sb; + checked_space_map::ptr data_sm; { block_manager<>::ptr bm = open_bm(*fs.dev); transaction_manager::ptr tm = open_tm(bm); - superblock_detail::superblock sb = read_superblock(bm); + sb = read_superblock(bm); + data_sm = open_disk_sm(*tm, static_cast(&sb.data_space_map_root_)); dev_tree dtree(*tm, sb.data_mapping_root_, mapping_tree_detail::mtree_traits::ref_counter(tm)); @@ -508,13 +569,26 @@ namespace local { mr2.complete(); } + indented_stream is(cout); + begin_superblock(is, "", sb.time_, + sb.trans_id_, + sb.data_block_size_, + data_sm->get_nr_blocks(), + sb.metadata_snap_ ? + boost::optional(sb.metadata_snap_) : + boost::optional()); + begin_diff(is, *fs.snap1, *fs.snap2); + if (fs.verbose) { - verbose_emitter e(cout); + verbose_emitter e(is); dump_diff(mr1.get_mappings(), mr2.get_mappings(), e); } else { - simple_emitter e(cout); + simple_emitter e(is); dump_diff(mr1.get_mappings(), mr2.get_mappings(), e); } + + end_diff(is); + end_superblock(is); } int delta(application &app, flags const &fs) { @@ -541,13 +615,15 @@ int thin_delta_main(int argc, char **argv) flags fs; local::application app(basename(argv[0])); - char const shortopts[] = "hV"; + char const shortopts[] = "hVm"; option const longopts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, + { "thin1", required_argument, NULL, 1 }, { "snap1", required_argument, NULL, 1 }, + { "thin2", required_argument, NULL, 2 }, { "snap2", required_argument, NULL, 2 }, - { "metadata-snap", no_argument, NULL, 3 }, + { "metadata-snap", no_argument, NULL, 'm' }, { "verbose", no_argument, NULL, 4 } }; @@ -569,7 +645,7 @@ int thin_delta_main(int argc, char **argv) fs.snap2 = app.parse_snap(optarg); break; - case 3: + case 'm': abort(); break; From cc44652cc3c13a3cfc31631688c080bb43e5a5a2 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 25 Mar 2015 11:10:18 +0000 Subject: [PATCH 163/165] [thin_delta] support metadata snapshots --- thin-provisioning/thin_delta.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/thin-provisioning/thin_delta.cc b/thin-provisioning/thin_delta.cc index 196887d..7cb7d75 100644 --- a/thin-provisioning/thin_delta.cc +++ b/thin-provisioning/thin_delta.cc @@ -33,7 +33,7 @@ namespace local { << "Options:\n" << " {--thin1, --snap1}\n" << " {--thin2, --snap2}\n" - << " {-m, --metadata-snap}\n" + << " {-m, --metadata-snap} [block#]\n" << " {--verbose}\n" << " {-h|--help}\n" << " {-V|--version}" << endl; @@ -45,13 +45,13 @@ namespace local { exit(1); } - uint64_t parse_snap(string const &str) { + uint64_t parse_int(string const &str, string const &desc) { try { return boost::lexical_cast(str); } catch (...) { ostringstream out; - out << "Couldn't parse snapshot designator: '" << str << "'"; + out << "Couldn't parse " << desc << ": '" << str << "'"; die(out.str()); } @@ -68,6 +68,7 @@ namespace local { } boost::optional dev; + boost::optional metadata_snap; boost::optional snap1; boost::optional snap2; bool verbose; @@ -535,7 +536,7 @@ namespace local { block_manager<>::ptr bm = open_bm(*fs.dev); transaction_manager::ptr tm = open_tm(bm); - sb = read_superblock(bm); + sb = fs.metadata_snap ? read_superblock(bm, *fs.metadata_snap) : read_superblock(bm); data_sm = open_disk_sm(*tm, static_cast(&sb.data_space_map_root_)); dev_tree dtree(*tm, sb.data_mapping_root_, @@ -638,15 +639,15 @@ int thin_delta_main(int argc, char **argv) return 0; case 1: - fs.snap1 = app.parse_snap(optarg); + fs.snap1 = app.parse_int(optarg, "thin id 1"); break; case 2: - fs.snap2 = app.parse_snap(optarg); + fs.snap2 = app.parse_int(optarg, "thin id 2"); break; case 'm': - abort(); + fs.metadata_snap = app.parse_int(optarg, "metadata snapshot block"); break; case 4: From f581f34be868201a1ea955178fd4e6ad932c9219 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 7 Apr 2015 12:10:38 +0100 Subject: [PATCH 164/165] add comment explaining mtree_traits --- thin-provisioning/mapping_tree.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/thin-provisioning/mapping_tree.h b/thin-provisioning/mapping_tree.h index be3bcf8..d417b47 100644 --- a/thin-provisioning/mapping_tree.h +++ b/thin-provisioning/mapping_tree.h @@ -54,6 +54,9 @@ namespace thin_provisioning { transaction_manager::ptr tm_; }; + // This value type is itself a tree containing mappings. + // Used when manipulating the top level of the mapping + // tree. struct mtree_traits { typedef base::le64 disk_type; typedef uint64_t value_type; From 7f643b70507e4c188253e951eccf613a901aa79d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 7 Apr 2015 12:16:46 +0100 Subject: [PATCH 165/165] [thin] Use specific damage visitors to improve error messages. There's now a damage visitor for dev_trees, mapping_trees and single_mapping_trees. --- thin-provisioning/mapping_tree.cc | 56 +++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/thin-provisioning/mapping_tree.cc b/thin-provisioning/mapping_tree.cc index ea3292f..421c8fe 100644 --- a/thin-provisioning/mapping_tree.cc +++ b/thin-provisioning/mapping_tree.cc @@ -141,9 +141,9 @@ namespace { } }; - class ll_damage_visitor { + class dev_tree_damage_visitor { public: - ll_damage_visitor(damage_visitor &v) + dev_tree_damage_visitor(damage_visitor &v) : v_(v) { } @@ -158,14 +158,56 @@ namespace { break; default: - // shouldn't get here. - throw std::runtime_error("ll_damage_visitor: path too long"); + throw std::runtime_error("dev_tree_damage_visitor: path too long"); } } private: damage_visitor &v_; }; + + class mapping_tree_damage_visitor { + public: + mapping_tree_damage_visitor(damage_visitor &v) + : v_(v) { + } + + virtual void visit(btree_path const &path, btree_detail::damage const &d) { + switch (path.size()) { + case 0: + v_.visit(missing_devices(d.desc_, d.lost_keys_)); + break; + + default: + throw std::runtime_error("mapping_tree_damage_visitor: path too long"); + } + } + + private: + damage_visitor &v_; + }; + + class single_mapping_tree_damage_visitor { + public: + single_mapping_tree_damage_visitor(damage_visitor &v) + : v_(v) { + } + + virtual void visit(btree_path const &path, btree_detail::damage const &d) { + switch (path.size()) { + case 0: + v_.visit(missing_mappings(d.desc_, path[0], d.lost_keys_)); + break; + + default: + throw std::runtime_error("single_mapping_tree_damage_visitor: path too long"); + } + } + + private: + damage_visitor &v_; + }; + } void @@ -173,7 +215,7 @@ thin_provisioning::walk_mapping_tree(dev_tree const &tree, mapping_tree_detail::device_visitor &dev_v, mapping_tree_detail::damage_visitor &dv) { - ll_damage_visitor ll_dv(dv); + dev_tree_damage_visitor ll_dv(dv); btree_visit_values(tree, dev_v, ll_dv); } @@ -190,7 +232,7 @@ thin_provisioning::walk_mapping_tree(mapping_tree const &tree, mapping_tree_detail::mapping_visitor &mv, mapping_tree_detail::damage_visitor &dv) { - ll_damage_visitor ll_dv(dv); + mapping_tree_damage_visitor ll_dv(dv); btree_visit_values(tree, mv, ll_dv); } @@ -207,7 +249,7 @@ thin_provisioning::walk_mapping_tree(single_mapping_tree const &tree, mapping_tree_detail::mapping_visitor &mv, mapping_tree_detail::damage_visitor &dv) { - ll_damage_visitor ll_dv(dv); + single_mapping_tree_damage_visitor ll_dv(dv); btree_visit_values(tree, mv, ll_dv); }