write a lot of the persistent data classes
This commit is contained in:
parent
6b8b16c70a
commit
2e39670ff9
14
Makefile
14
Makefile
@ -1,8 +1,11 @@
|
||||
SOURCE=\
|
||||
main.cc
|
||||
block.cc \
|
||||
main.cc \
|
||||
metadata.cc \
|
||||
transaction_manager.cc
|
||||
|
||||
OBJECTS=$(subst .cc,.o,$(SOURCE))
|
||||
CPPFLAGS=-Wall -Weffc++ -std=c++0x
|
||||
CPPFLAGS=-Wall -std=c++0x
|
||||
INCLUDES=
|
||||
LIBS=-lstdc++
|
||||
|
||||
@ -13,4 +16,9 @@ LIBS=-lstdc++
|
||||
g++ -c $(CPPFLAGS) $(INCLUDES) -o $@ $<
|
||||
|
||||
multisnap_display: $(OBJECTS)
|
||||
g++ -o $@ $+ $(LIBS)
|
||||
g++ -o $@ $+ $(LIBS)
|
||||
|
||||
main.o: block.h
|
||||
block.o: block.h
|
||||
transaction_manager.o: transaction_manager.h block.h
|
||||
metadata.o: block.h transaction_manager.h btree.h metadata.h
|
116
block.h
Normal file
116
block.h
Normal file
@ -0,0 +1,116 @@
|
||||
#ifndef BLOCK_H
|
||||
#define BLOCK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
|
||||
typedef uint64_t block_address;
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
class block_manager : private boost::noncopyable {
|
||||
public:
|
||||
typedef boost::shared_ptr<block_manager> ptr;
|
||||
|
||||
block_manager(std::string const &path);
|
||||
~block_manager();
|
||||
|
||||
typedef unsigned char buffer[BlockSize];
|
||||
typedef unsigned char const const_buffer[BlockSize];
|
||||
|
||||
class block;
|
||||
|
||||
class block_validator {
|
||||
public:
|
||||
virtual ~block_validator() {}
|
||||
|
||||
virtual void check(block const &b) const = 0;
|
||||
virtual void prepare(block &b) const = 0;
|
||||
};
|
||||
|
||||
struct block {
|
||||
typedef boost::optional<block_validator> maybe_validator;
|
||||
|
||||
block(block_address location,
|
||||
maybe_validator v = maybe_validator())
|
||||
: location_(location),
|
||||
validator_(v) {
|
||||
}
|
||||
|
||||
block_address location_;
|
||||
buffer data_;
|
||||
boost::optional<block_validator> validator_;
|
||||
};
|
||||
typedef boost::shared_ptr<block> block_ptr;
|
||||
|
||||
class read_ref {
|
||||
public:
|
||||
read_ref(block_ptr b);
|
||||
virtual ~read_ref() {}
|
||||
|
||||
block_address get_location() const;
|
||||
const_buffer &data() const;
|
||||
|
||||
protected:
|
||||
block_ptr block_;
|
||||
};
|
||||
|
||||
// Inherited from read_ref, since you can read a block that's write
|
||||
// locked.
|
||||
class write_ref : public read_ref {
|
||||
public:
|
||||
using read_ref::data;
|
||||
buffer &data();
|
||||
};
|
||||
|
||||
// Locking methods
|
||||
read_ref
|
||||
read_lock(block_address location);
|
||||
|
||||
boost::optional<read_ref>
|
||||
read_try_lock(block_address location);
|
||||
|
||||
write_ref
|
||||
write_lock(block_address location);
|
||||
|
||||
write_ref
|
||||
write_lock_zero(block_address location);
|
||||
|
||||
// Validator variants
|
||||
read_ref
|
||||
read_lock(block_address location, block_validator const &v);
|
||||
|
||||
boost::optional<read_ref>
|
||||
read_try_lock(block_address location, block_validator const &v);
|
||||
|
||||
write_ref
|
||||
write_lock(block_address location, block_validator const &v);
|
||||
|
||||
write_ref
|
||||
write_lock_zero(block_address location, block_validator const &v);
|
||||
|
||||
// Use this to commit changes
|
||||
void flush(write_ref super_block);
|
||||
|
||||
private:
|
||||
void read_block(block &b);
|
||||
void write_block(block const &b);
|
||||
void zero_block(block &b);
|
||||
|
||||
void write_and_release(block *b);
|
||||
|
||||
int fd_;
|
||||
};
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
207
block.tcc
Normal file
207
block.tcc
Normal file
@ -0,0 +1,207 @@
|
||||
#include "block.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace boost;
|
||||
using namespace persistent_data;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
block_manager<BlockSize>::read_ref::read_ref(block_manager::block_ptr b)
|
||||
: block_(b)
|
||||
{
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
block_address
|
||||
block_manager<BlockSize>::read_ref::get_location() const
|
||||
{
|
||||
return block_->location_;
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::const_buffer &
|
||||
block_manager<BlockSize>::read_ref::data() const
|
||||
{
|
||||
return block_->data_;
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::buffer &
|
||||
block_manager<BlockSize>::write_ref::data()
|
||||
{
|
||||
return read_ref::block_->data_;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
block_manager<BlockSize>::block_manager(std::string const &path)
|
||||
{
|
||||
fd_ = ::open(path.c_str(), O_RDWR | O_EXCL);
|
||||
if (fd_ < 0)
|
||||
throw std::runtime_error("couldn't open file");
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
block_manager<BlockSize>::~block_manager()
|
||||
{
|
||||
::close(fd_);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::read_ref
|
||||
block_manager<BlockSize>::read_lock(block_address location)
|
||||
{
|
||||
block_ptr b(new block(location));
|
||||
read_block(b);
|
||||
return read_ref(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
optional<typename block_manager<BlockSize>::read_ref>
|
||||
block_manager<BlockSize>::read_try_lock(block_address location)
|
||||
{
|
||||
return read_lock(location);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::write_ref
|
||||
block_manager<BlockSize>::write_lock(block_address location)
|
||||
{
|
||||
block_ptr b(new block(location), write_and_release);
|
||||
read_block(b);
|
||||
return write_ref(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::write_ref
|
||||
block_manager<BlockSize>::write_lock_zero(block_address location)
|
||||
{
|
||||
block_ptr b(new block(location), write_and_release);
|
||||
zero_block(b);
|
||||
return write_ref(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::read_ref
|
||||
block_manager<BlockSize>::read_lock(block_address location,
|
||||
block_manager<BlockSize>::block_validator const &v)
|
||||
{
|
||||
block_ptr b(new block(location, v));
|
||||
read_block(b);
|
||||
return read_ref(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
optional<typename block_manager<BlockSize>::read_ref>
|
||||
block_manager<BlockSize>::read_try_lock(block_address location,
|
||||
block_manager<BlockSize>::block_validator const &v)
|
||||
{
|
||||
return read_lock(location, v);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::write_ref
|
||||
block_manager<BlockSize>::write_lock(block_address location,
|
||||
block_manager<BlockSize>::block_validator const &v)
|
||||
{
|
||||
block_ptr b(new block(location, v), write_and_release);
|
||||
read_block(b);
|
||||
return write_ref(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename block_manager<BlockSize>::write_ref
|
||||
block_manager<BlockSize>::write_lock_zero(block_address location,
|
||||
block_manager<BlockSize>::block_validator const &v)
|
||||
{
|
||||
block_ptr b(new block(location, v), write_and_release);
|
||||
zero_block(b);
|
||||
return write_ref(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
block_manager<BlockSize>::flush(block_manager<BlockSize>::write_ref super_block)
|
||||
{
|
||||
write_block(super_block);
|
||||
::fsync(fd_);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
block_manager<BlockSize>::read_block(block &b)
|
||||
{
|
||||
std::cerr << "reading block: " << b->location << std::endl;
|
||||
|
||||
off_t r;
|
||||
r = ::lseek(fd_, BlockSize * b->location_, SEEK_SET);
|
||||
if (r == (off_t) -1)
|
||||
throw std::runtime_error("lseek failed");
|
||||
|
||||
ssize_t n;
|
||||
size_t remaining = BlockSize;
|
||||
unsigned char *buf = b->data_;
|
||||
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 <uint32_t BlockSize>
|
||||
void
|
||||
block_manager<BlockSize>::write_block(block const &b)
|
||||
{
|
||||
|
||||
std::cerr << "writing block: " << b->location << std::endl;
|
||||
|
||||
off_t r;
|
||||
r = ::lseek(fd_, BlockSize * b->location_, SEEK_SET);
|
||||
if (r == (off_t) -1)
|
||||
throw std::runtime_error("lseek failed");
|
||||
|
||||
ssize_t n;
|
||||
size_t remaining = BlockSize;
|
||||
unsigned char *buf = b->data_;
|
||||
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("write failed");
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
block_manager<BlockSize>::zero_block(block &b)
|
||||
{
|
||||
memset(b->data_, 0, BlockSize);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
block_manager<BlockSize>::write_and_release(block *b)
|
||||
{
|
||||
write_block(b);
|
||||
delete b;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
49
btree.h
Normal file
49
btree.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef BTREE_H
|
||||
#define BTREE_H
|
||||
|
||||
#include "transaction_manager.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
class btree {
|
||||
public:
|
||||
typedef uint64_t key[Levels];
|
||||
typedef typename ValueTraits::value_type value_type;
|
||||
typedef boost::optional<value_type> maybe_value;
|
||||
typedef boost::optional<std::pair<unsigned, value_type> > maybe_pair;
|
||||
typedef boost::shared_ptr<btree<Levels, ValueTraits, BlockSize> > ptr;
|
||||
typedef typename block_manager<BlockSize>::read_ref read_ref;
|
||||
typedef typename block_manager<BlockSize>::write_ref write_ref;
|
||||
|
||||
btree(boost::shared_ptr<transaction_manager<BlockSize> > tm);
|
||||
btree(boost::shared_ptr<transaction_manager<BlockSize> > tm,
|
||||
block_address root);
|
||||
~btree();
|
||||
|
||||
maybe_value lookup(key const &key) const;
|
||||
maybe_pair lookup_le(key const &key) const;
|
||||
maybe_pair lookup_ge(key const &key) const;
|
||||
|
||||
void insert(key const &key, typename ValueTraits::value_type const &value);
|
||||
void remove(key const &key);
|
||||
|
||||
void set_root(block_address root);
|
||||
block_address get_root() const;
|
||||
|
||||
ptr clone() const;
|
||||
|
||||
// free the on disk btree when the destructor is called
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
typename persistent_data::transaction_manager<BlockSize>::ptr tm_;
|
||||
bool destroy_;
|
||||
block_address root_;
|
||||
};
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
255
btree.tcc
Normal file
255
btree.tcc
Normal file
@ -0,0 +1,255 @@
|
||||
#include "btree.h"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
using namespace boost;
|
||||
using namespace persistent_data;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
//------------------------------------------------
|
||||
// On disk data layout for btree nodes
|
||||
enum node_flags {
|
||||
INTERNAL_NODE = 1,
|
||||
LEAF_NODE = 1 << 1
|
||||
};
|
||||
|
||||
struct node_header {
|
||||
__le32 csum;
|
||||
__le32 flags;
|
||||
__le64 blocknr; /* which block this node is supposed to live in */
|
||||
|
||||
__le32 nr_entries;
|
||||
__le32 max_entries;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct node {
|
||||
struct node_header header;
|
||||
__le64 keys[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
//------------------------------------------------
|
||||
// Class that acts as an interface over the raw little endian btree
|
||||
// node data.
|
||||
class node {
|
||||
public:
|
||||
enum type {
|
||||
INTERNAL,
|
||||
LEAF
|
||||
};
|
||||
|
||||
type get_type() const;
|
||||
void set_type(type t);
|
||||
|
||||
unsigned get_nr_entries() const;
|
||||
void set_nr_entries(unsigned n);
|
||||
|
||||
unsigned get_max_entries() const;
|
||||
void set_max_entries(unsigned n);
|
||||
|
||||
uint64_t key_at(unsigned i) const;
|
||||
|
||||
template <typename ValueTraits>
|
||||
typename ValueTraits::value_type value_at(unsigned i) const;
|
||||
|
||||
private:
|
||||
struct node *raw_;
|
||||
};
|
||||
|
||||
//------------------------------------------------
|
||||
// Various searches
|
||||
int bsearch(node const &n, uint64_t key, int want_hi)
|
||||
{
|
||||
int lo = -1, hi = n.get_nr_entries();
|
||||
|
||||
while(hi - lo > 1) {
|
||||
int mid = lo + ((hi - lo) / 2);
|
||||
uint64_t mid_key = n.key_at(mid);
|
||||
|
||||
if (mid_key == key)
|
||||
return mid;
|
||||
|
||||
if (mid_key < key)
|
||||
lo = mid;
|
||||
else
|
||||
hi = mid;
|
||||
}
|
||||
|
||||
return want_hi ? hi : lo;
|
||||
}
|
||||
|
||||
optional<unsigned> exact_search(node const &n, uint64_t key) {
|
||||
int i = bsearch(n, key, 0);
|
||||
if (i < 0 || static_cast<unsigned>(i) >= n.get_nr_entries())
|
||||
return optional<unsigned>();
|
||||
|
||||
return optional<unsigned>(i);
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
//
|
||||
template <uint32_t BlockSize>
|
||||
node &to_node(typename block_manager<BlockSize>::write_ref b);
|
||||
|
||||
unsigned
|
||||
calc_max_entries(uint32_t bs);
|
||||
|
||||
// Spines
|
||||
template <uint32_t BlockSize>
|
||||
class ro_spine : private noncopyable {
|
||||
public:
|
||||
void step(block_address b);
|
||||
node get_node() const;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
class internal_traits {
|
||||
public:
|
||||
typedef uint64_t value_type;
|
||||
};
|
||||
|
||||
template <typename ValueTraits, uint32_t BlockSize, typename Search>
|
||||
optional<typename ValueTraits::value_type>
|
||||
lookup_raw(ro_spine<BlockSize> &spine, block_address block, uint64_t key) {
|
||||
|
||||
using namespace boost;
|
||||
typedef typename ValueTraits::value_type leaf_type;
|
||||
typedef typename internal_traits::value_type internal_type;
|
||||
|
||||
Search find;
|
||||
|
||||
for (;;) {
|
||||
spine.step(block);
|
||||
node &n = spine.node();
|
||||
|
||||
auto mi = find(n, key);
|
||||
if (!mi)
|
||||
return optional<leaf_type>();
|
||||
|
||||
if (n.get_type() == node::LEAF)
|
||||
return optional<leaf_type>(n.value_at(*mi));
|
||||
|
||||
block = n.value_at<internal_type>(*mi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
btree<Levels, ValueTraits, BlockSize>::btree(shared_ptr<transaction_manager<BlockSize> > tm)
|
||||
: tm_(tm),
|
||||
destroy_(false)
|
||||
{
|
||||
write_ref root = tm_.new_block();
|
||||
|
||||
node &n = to_node(root);
|
||||
n.set_type(node::LEAF);
|
||||
n.set_nr_entries(0);
|
||||
n.set_max_entries(calc_max_entries(BlockSize));
|
||||
|
||||
root_ = root.location();
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
btree<Levels, ValueTraits, BlockSize>::btree(shared_ptr<transaction_manager<BlockSize> > tm,
|
||||
block_address root)
|
||||
: tm_(tm),
|
||||
destroy_(false),
|
||||
root_(root)
|
||||
{
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
btree<Levels, ValueTraits, BlockSize>::~btree()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
typename btree<Levels, ValueTraits, BlockSize>::maybe_value
|
||||
btree<Levels, ValueTraits, BlockSize>::lookup(key const &key) const
|
||||
{
|
||||
ro_spine<BlockSize> spine;
|
||||
block_address root = root_;
|
||||
|
||||
for (unsigned level = 0; level < Levels - 1; ++level) {
|
||||
auto mroot = lookup_raw<internal_traits, BlockSize, exact_search>(spine, root, key[level]);
|
||||
if (!mroot)
|
||||
return maybe_value();
|
||||
|
||||
root = *mroot;
|
||||
}
|
||||
|
||||
return lookup_raw<ValueTraits, BlockSize, exact_search>(spine, root, key[Levels - 1]);
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
typename btree<Levels, ValueTraits, BlockSize>::maybe_pair
|
||||
btree<Levels, ValueTraits, BlockSize>::lookup_le(key const &key) const
|
||||
{
|
||||
return maybe_pair();
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
typename btree<Levels, ValueTraits, BlockSize>::maybe_pair
|
||||
btree<Levels, ValueTraits, BlockSize>::lookup_ge(key const &key) const
|
||||
{
|
||||
return maybe_pair();
|
||||
}
|
||||
|
||||
#if 0
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
void
|
||||
btree<Levels, ValueTraits, BlockSize>::insert(key const &key, typename ValueTraits::value_type const &value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
void
|
||||
btree<Levels, ValueTraits, BlockSize>::remove(key const &key)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
block_address
|
||||
btree<Levels, ValueTraits, BlockSize>::get_root() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
void
|
||||
btree<Levels, ValueTraits, BlockSize>::set_root(block_address root)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
block_address
|
||||
btree<Levels, ValueTraits, BlockSize>::get_root() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
ptr
|
||||
btree<Levels, ValueTraits, BlockSize>::clone() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
|
||||
void
|
||||
btree<Levels, ValueTraits, BlockSize>::destroy()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
//----------------------------------------------------------------
|
61
core-map.h
Normal file
61
core-map.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef CORE_MAP_H
|
||||
#define CORE_MAP_H
|
||||
|
||||
#include "space_map.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
class core_map : public space_map {
|
||||
public:
|
||||
core_map(block_address nr_blocks)
|
||||
: counts_(nr_blocks, 0) {
|
||||
}
|
||||
|
||||
block_address get_nr_blocks() const {
|
||||
counts_.size();
|
||||
}
|
||||
|
||||
block_address get_nr_free() const {
|
||||
nr_free_;
|
||||
}
|
||||
|
||||
ref_t get_count(block_address b) const {
|
||||
return counts_[b];
|
||||
}
|
||||
|
||||
void set_count(block_address b, ref_t c) {
|
||||
counts_[b] = c;
|
||||
}
|
||||
|
||||
void commit() {
|
||||
}
|
||||
|
||||
void inc_block(block_address b) {
|
||||
counts_[b]++;
|
||||
}
|
||||
|
||||
void dec_block(block_address b) {
|
||||
counts_[b]--;
|
||||
}
|
||||
|
||||
block_address new_block() {
|
||||
for (block_address i = 0; i < counts_.size(); i++)
|
||||
if (counts_[i] == 0) {
|
||||
counts_[i] = 1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
bool count_possibly_greater_than_one(block_address b) const {
|
||||
return counts_[i] > 1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ref_t> counts_;
|
||||
};
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
2
main.cc
2
main.cc
@ -1,3 +1,5 @@
|
||||
#include "block.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
215
metadata.cc
Normal file
215
metadata.cc
Normal file
@ -0,0 +1,215 @@
|
||||
#include "metadata.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace persistent_data;
|
||||
using namespace multisnap;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
typedef uint8_t __le8;
|
||||
typedef uint8_t __u8;
|
||||
typedef uint32_t __le32;
|
||||
typedef uint64_t __le64;
|
||||
|
||||
|
||||
|
||||
uint32_t const SUPERBLOCK_MAGIC = 27022010;
|
||||
block_address const SUPERBLOCK_LOCATION = 0;
|
||||
uint32_t const VERSION = 1;
|
||||
unsigned const METADATA_CACHE_SIZE = 1024;
|
||||
unsigned const SECTOR_TO_BLOCK_SHIFT = 3;
|
||||
unsigned const SPACE_MAP_ROOT_SIZE = 128;
|
||||
|
||||
struct multisnap_super_block {
|
||||
__le32 csum_;
|
||||
__le32 flags_;
|
||||
__le64 blocknr_; /* this block number, dm_block_t */
|
||||
|
||||
__u8 uuid_[16];
|
||||
__le64 magic_;
|
||||
__le32 version_;
|
||||
__le32 time_;
|
||||
|
||||
__le64 trans_id_;
|
||||
/* root for userspace's transaction (for migration and friends) */
|
||||
__le64 held_root_;
|
||||
|
||||
__u8 data_space_map_root_[SPACE_MAP_ROOT_SIZE];
|
||||
__u8 metadata_space_map_root_[SPACE_MAP_ROOT_SIZE];
|
||||
|
||||
/* 2 level btree mapping (dev_id, (dev block, time)) -> data block */
|
||||
__le64 data_mapping_root_;
|
||||
|
||||
/* device detail root mapping dev_id -> device_details */
|
||||
__le64 device_details_root_;
|
||||
|
||||
__le32 data_block_size_; /* in 512-byte sectors */
|
||||
|
||||
__le32 metadata_block_size_; /* in 512-byte sectors */
|
||||
__le64 metadata_nr_blocks_;
|
||||
|
||||
__le32 compat_flags_;
|
||||
__le32 incompat_flags_;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct device_details {
|
||||
__le64 dev_size_;
|
||||
__le64 mapped_blocks_;
|
||||
__le64 transaction_id_; /* when created */
|
||||
__le32 creation_time_;
|
||||
__le32 snapshotted_time_;
|
||||
} __attribute__ ((packed));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
metadata::thin::maybe_address
|
||||
metadata::thin::lookup(block_address thin_block)
|
||||
{
|
||||
uint64_t key[2] = {dev_, thin_block};
|
||||
return metadata_->mappings_.lookup(key);
|
||||
}
|
||||
|
||||
void
|
||||
metadata::thin::insert(block_address thin_block, block_address data_block)
|
||||
{
|
||||
uint64_t key[2] = {dev_, thin_block};
|
||||
return metadata_->mappings_.insert(key, data_block);
|
||||
}
|
||||
|
||||
void
|
||||
metadata::thin::remove(block_address thin_block)
|
||||
{
|
||||
uint64_t key[2] = {dev_, thin_block};
|
||||
metadata_->mappings_.remove(key);
|
||||
}
|
||||
#if 0
|
||||
//--------------------------------
|
||||
|
||||
metadata::metadata(std::string const &metadata_dev,
|
||||
sector_t data_block_size,
|
||||
block_address nr_data_blocks)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
metadata::~metadata()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
metadata::commit()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
void
|
||||
metadata::create_thin(dev_t dev)
|
||||
{
|
||||
uint64_t key[1] = {dev};
|
||||
|
||||
if (device_exists(dev))
|
||||
throw std::runtime_error("Device already exists");
|
||||
|
||||
single_mapping_tree::ptr new_tree(new single_mapping_tree(tm_));
|
||||
mappings_top_level_.insert(key, *new_tree);
|
||||
mappings_.set_root(mappings_top_level_.get_root()); // FIXME: ugly
|
||||
}
|
||||
|
||||
void
|
||||
metadata::create_snap(dev_t dev, dev_t origin)
|
||||
{
|
||||
uint64_t snap_key[1] = {dev};
|
||||
uint64_t origin_key[1] = {origin};
|
||||
|
||||
auto mtree = mappings_top_level_.lookup(origin_key);
|
||||
if (!mtree)
|
||||
throw std::runtime_error("unknown origin");
|
||||
|
||||
single_mapping_tree::ptr clone(mtree->clone());
|
||||
mappings_top_level_.insert(snap_key, *clone);
|
||||
mappings_.set_root(mappings_top_level_.get_root()); // FIXME: ugly
|
||||
|
||||
time_++;
|
||||
|
||||
auto o = open(origin);
|
||||
auto s = open(dev);
|
||||
o->set_snapshot_time(time_);
|
||||
s->set_snapshot_time(time_);
|
||||
s->set_mapped_blocks(o->get_mapped_blocks());
|
||||
}
|
||||
|
||||
void
|
||||
metadata::del(dev_t dev)
|
||||
{
|
||||
uint64_t key[1] = {dev};
|
||||
mappings_top_level_.remove(key);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
metadata::set_transaction_id(uint64_t id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint64_t
|
||||
metadata::get_transaction_id() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
block_address
|
||||
metadata::get_held_root() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
thin_ptr
|
||||
metadata::open_device(dev_t)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
block_address
|
||||
metadata::alloc_data_block()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
metadata::free_data_block(block_address b)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
block_address
|
||||
metadata::get_nr_free_data_blocks() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
sector_t
|
||||
metadata::get_data_block_size() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
block_address
|
||||
metadata::get_data_dev_size() const
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
bool
|
||||
metadata::device_exists(dev_t dev) const
|
||||
{
|
||||
uint64_t key[1] = {dev};
|
||||
auto mval = details_.lookup(key);
|
||||
return mval;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
110
metadata.h
Normal file
110
metadata.h
Normal file
@ -0,0 +1,110 @@
|
||||
#ifndef MULTISNAP_METADATA_H
|
||||
#define MULTISNAP_METADATA_H
|
||||
|
||||
#include "block.h"
|
||||
#include "transaction_manager.h"
|
||||
#include "btree.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// FIXME: make a const
|
||||
#define BLOCK_SIZE 4096
|
||||
|
||||
namespace multisnap {
|
||||
typedef uint64_t sector_t;
|
||||
|
||||
class metadata {
|
||||
public:
|
||||
typedef boost::shared_ptr<metadata> ptr;
|
||||
typedef persistent_data::block_address block_address;
|
||||
|
||||
metadata(std::string const &metadata_dev,
|
||||
sector_t data_block_size,
|
||||
persistent_data::block_address nr_data_blocks);
|
||||
~metadata();
|
||||
|
||||
void commit();
|
||||
|
||||
typedef uint32_t dev_t;
|
||||
void create_thin(dev_t dev);
|
||||
void create_snap(dev_t dev, dev_t origin);
|
||||
void del(dev_t);
|
||||
|
||||
void set_transaction_id(uint64_t id);
|
||||
uint64_t get_transaction_id() const;
|
||||
|
||||
block_address get_held_root() const;
|
||||
|
||||
block_address alloc_data_block();
|
||||
void free_data_block(block_address b);
|
||||
|
||||
// accessors
|
||||
block_address get_nr_free_data_blocks() const;
|
||||
sector_t get_data_block_size() const;
|
||||
block_address get_data_dev_size() const;
|
||||
|
||||
class thin {
|
||||
public:
|
||||
typedef boost::shared_ptr<thin> ptr;
|
||||
|
||||
dev_t get_dev_t() const;
|
||||
|
||||
typedef boost::optional<block_address> maybe_address;
|
||||
maybe_address lookup(block_address thin_block);
|
||||
void insert(block_address thin_block, block_address data_block);
|
||||
void remove(block_address thin_block);
|
||||
|
||||
void set_snapshot_time(uint32_t time);
|
||||
|
||||
persistent_data::block_address get_mapped_blocks() const;
|
||||
void set_mapped_blocks(persistent_data::block_address count);
|
||||
|
||||
private:
|
||||
dev_t dev_;
|
||||
metadata::ptr metadata_;
|
||||
};
|
||||
|
||||
thin::ptr open(dev_t);
|
||||
|
||||
private:
|
||||
friend class thin;
|
||||
|
||||
bool device_exists(dev_t dev) const;
|
||||
|
||||
class detail_traits {
|
||||
public:
|
||||
typedef uint64_t value_type;
|
||||
};
|
||||
|
||||
class map_traits {
|
||||
public:
|
||||
typedef block_address value_type;
|
||||
};
|
||||
|
||||
class dev_traits {
|
||||
public:
|
||||
typedef persistent_data::btree<1, map_traits, BLOCK_SIZE> value_type;
|
||||
};
|
||||
|
||||
uint32_t time_;
|
||||
|
||||
persistent_data::transaction_manager<BLOCK_SIZE>::ptr tm_;
|
||||
|
||||
typedef persistent_data::btree<1, detail_traits, BLOCK_SIZE> detail_tree;
|
||||
typedef persistent_data::btree<1, dev_traits, BLOCK_SIZE> dev_tree;
|
||||
typedef persistent_data::btree<2, map_traits, BLOCK_SIZE> mapping_tree;
|
||||
typedef persistent_data::btree<1, map_traits, BLOCK_SIZE> single_mapping_tree;
|
||||
|
||||
detail_tree details_;
|
||||
dev_tree mappings_top_level_;
|
||||
mapping_tree mappings_;
|
||||
};
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
41
space_map.h
Normal file
41
space_map.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef SPACE_MAP_H
|
||||
#define SPACE_MAP_H
|
||||
|
||||
#include "block.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
typedef uint32_t ref_t;
|
||||
|
||||
class space_map {
|
||||
public:
|
||||
typedef boost::shared_ptr<space_map> ptr;
|
||||
|
||||
virtual ~space_map() {};
|
||||
|
||||
virtual block_address get_nr_blocks() const = 0;
|
||||
virtual block_address get_nr_free() const = 0;
|
||||
virtual ref_t get_count(block_address b) const = 0;
|
||||
virtual void set_count(block_address b, ref_t c) = 0;
|
||||
virtual void commit() = 0;
|
||||
|
||||
virtual void inc_block(block_address b) = 0;
|
||||
virtual void dec_block(block_address b) = 0;
|
||||
virtual block_address new_block() = 0;
|
||||
|
||||
virtual bool count_possibly_greater_than_one(block_address b) const = 0;
|
||||
};
|
||||
|
||||
class persistent_space_map {
|
||||
public:
|
||||
virtual size_t root_size() = 0;
|
||||
virtual void copy_root(void *dest, size_t len) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
59
transaction_manager.h
Normal file
59
transaction_manager.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef TRANSACTION_MANAGER_H
|
||||
#define TRANSACTION_MANAGER_H
|
||||
|
||||
#include "block.h"
|
||||
#include "space_map.h"
|
||||
|
||||
#include <set>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
template <uint32_t MetadataBlockSize>
|
||||
class transaction_manager : public boost::noncopyable {
|
||||
public:
|
||||
typedef boost::shared_ptr<transaction_manager<MetadataBlockSize> > ptr;
|
||||
|
||||
transaction_manager(typename block_manager<MetadataBlockSize>::ptr bm,
|
||||
space_map::ptr sm);
|
||||
~transaction_manager();
|
||||
|
||||
typedef typename block_manager<MetadataBlockSize>::read_ref read_ref;
|
||||
typedef typename block_manager<MetadataBlockSize>::write_ref write_ref;
|
||||
typedef typename block_manager<MetadataBlockSize>::block_validator block_validator;
|
||||
|
||||
void reserve_block(block_address location);
|
||||
void begin();
|
||||
void pre_commit();
|
||||
void commit(write_ref superblock);
|
||||
|
||||
block_address alloc_block();
|
||||
write_ref new_block();
|
||||
write_ref new_block(block_validator const &v);
|
||||
|
||||
write_ref shadow(block_address orig, bool &inc_children);
|
||||
write_ref shadow(block_address orig, block_validator const &v, bool &inc_children);
|
||||
|
||||
read_ref read_lock(block_address b);
|
||||
read_ref read_lock(block_address b, block_validator const &v);
|
||||
|
||||
void inc(block_address b);
|
||||
void dec(block_address b);
|
||||
uint32_t ref_count(block_address b) const;
|
||||
|
||||
private:
|
||||
void add_shadow(block_address b);
|
||||
bool is_shadow(block_address b);
|
||||
void wipe_shadow_table();
|
||||
|
||||
typename block_manager<MetadataBlockSize>::ptr bm_;
|
||||
space_map::ptr sm_;
|
||||
|
||||
std::set<block_address> shadows_;
|
||||
};
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
174
transaction_manager.tcc
Normal file
174
transaction_manager.tcc
Normal file
@ -0,0 +1,174 @@
|
||||
#include "transaction_manager.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace boost;
|
||||
using namespace persistent_data;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
transaction_manager<BlockSize>::transaction_manager(typename block_manager<BlockSize>::ptr bm,
|
||||
space_map::ptr sm)
|
||||
: bm_(bm),
|
||||
sm_(sm)
|
||||
{
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
transaction_manager<BlockSize>::~transaction_manager()
|
||||
{
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::reserve_block(block_address location)
|
||||
{
|
||||
sm_->inc_block(location);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::begin()
|
||||
{
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::pre_commit()
|
||||
{
|
||||
sm_->commit();
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::commit(write_ref superblock)
|
||||
{
|
||||
bm_->flush(superblock);
|
||||
wipe_shadow_table();
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
block_address
|
||||
transaction_manager<BlockSize>::alloc_block()
|
||||
{
|
||||
return sm_->new_block();
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename transaction_manager<BlockSize>::write_ref
|
||||
transaction_manager<BlockSize>::new_block()
|
||||
{
|
||||
block_address b = sm_->new_block();
|
||||
add_shadow(b);
|
||||
return bm_->write_lock_zero(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename transaction_manager<BlockSize>::write_ref
|
||||
transaction_manager<BlockSize>::new_block(block_validator const &v)
|
||||
{
|
||||
block_address b = sm_->new_block();
|
||||
add_shadow(b);
|
||||
return bm_->write_lock_zero(b, v);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename transaction_manager<BlockSize>::write_ref
|
||||
transaction_manager<BlockSize>::shadow(block_address orig, bool &inc_children)
|
||||
{
|
||||
if (is_shadow(orig) &&
|
||||
sm_->count_possibly_greater_than_one(orig)) {
|
||||
inc_children = false;
|
||||
return bm_->write_lock(orig);
|
||||
}
|
||||
|
||||
auto src = bm_->read_lock(orig);
|
||||
auto dest = bm_->write_lock_zero(sm_->new_block());
|
||||
::memcpy(dest->data_, src->data_, BlockSize);
|
||||
|
||||
ref_t count = sm_->get_count(orig);
|
||||
sm_->dec_block(orig);
|
||||
inc_children = count > 1;
|
||||
add_shadow(dest->location_);
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename transaction_manager<BlockSize>::write_ref
|
||||
transaction_manager<BlockSize>::shadow(block_address orig, block_validator const &v, bool &inc_children)
|
||||
{
|
||||
if (is_shadow(orig) &&
|
||||
sm_->count_possibly_greater_than_one(orig)) {
|
||||
inc_children = false;
|
||||
return bm_->write_lock(orig);
|
||||
}
|
||||
|
||||
auto src = bm_->read_lock(orig, v);
|
||||
auto dest = bm_->write_lock_zero(sm_->new_block(), v);
|
||||
::memcpy(dest->data_, src->data_, BlockSize);
|
||||
|
||||
ref_t count = sm_->get_count(orig);
|
||||
sm_->dec_block(orig);
|
||||
inc_children = count > 1;
|
||||
add_shadow(dest->location_);
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename transaction_manager<BlockSize>::read_ref
|
||||
transaction_manager<BlockSize>::read_lock(block_address b)
|
||||
{
|
||||
return bm_->read_lock(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
typename transaction_manager<BlockSize>::read_ref
|
||||
transaction_manager<BlockSize>::read_lock(block_address b, block_validator const &v)
|
||||
{
|
||||
return bm_->read_lock(b, v);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::inc(block_address b)
|
||||
{
|
||||
sm_->inc_block(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::dec(block_address b)
|
||||
{
|
||||
sm_->dec_block(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
uint32_t
|
||||
transaction_manager<BlockSize>::ref_count(block_address b) const
|
||||
{
|
||||
return sm_->get_count(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::add_shadow(block_address b)
|
||||
{
|
||||
shadows_.insert(b);
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
bool
|
||||
transaction_manager<BlockSize>::is_shadow(block_address b)
|
||||
{
|
||||
return shadows_.count(b) > 0;
|
||||
}
|
||||
|
||||
template <uint32_t BlockSize>
|
||||
void
|
||||
transaction_manager<BlockSize>::wipe_shadow_table()
|
||||
{
|
||||
shadows_.clear();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
Loading…
Reference in New Issue
Block a user