class TransContext;
+ /// an extent map, shared by a group of objects (clones)
+ struct ObjectGroup {
+ atomic_t nref; ///< reference count
+ extent_ref_map_t m;
+ };
+
/// an in-memory object
struct Onode {
atomic_t nref; ///< reference count
return out;
}
+// extent_ref_map_t
+
+void extent_ref_map_t::add(uint64_t offset, uint32_t len, unsigned ref)
+{
+ map<uint64_t,record_t>::iterator p = ref_map.insert(
+ map<uint64_t,record_t>::value_type(offset, record_t(len, ref))).first;
+ _maybe_merge_left(p);
+ ++p;
+ if (p != ref_map.end())
+ _maybe_merge_left(p);
+ _check();
+}
+
+void extent_ref_map_t::_check()
+{
+ uint64_t pos = 0;
+ unsigned refs = 0;
+ for (const auto &p : ref_map) {
+ if (p.first < pos)
+ assert(0 == "overlap");
+ if (p.first == pos && p.second.refs == refs)
+ assert(0 == "unmerged");
+ pos = p.first + p.second.length;
+ refs = p.second.refs;
+ }
+}
+
+void extent_ref_map_t::_maybe_merge_left(map<uint64_t,record_t>::iterator& p)
+{
+ if (p == ref_map.begin())
+ return;
+ auto q = p;
+ --q;
+ if (q->second.refs == p->second.refs &&
+ q->first + q->second.length == p->first) {
+ q->second.length += p->second.length;
+ ref_map.erase(p);
+ p = q;
+ }
+}
+
+void extent_ref_map_t::get(uint64_t offset, uint32_t length)
+{
+ map<uint64_t,record_t>::iterator p = ref_map.lower_bound(offset);
+ if (p == ref_map.end() || p->first > offset) {
+ if (p == ref_map.begin()) {
+ assert(0 == "get on missing extent (nothing before)");
+ }
+ --p;
+ if (p->first + p->second.length <= offset) {
+ assert(0 == "get on missing extent (gap)");
+ }
+ }
+ if (p->first < offset) {
+ uint64_t left = p->first + p->second.length - offset;
+ p->second.length = offset - p->first;
+ p = ref_map.insert(map<uint64_t,record_t>::value_type(
+ offset, record_t(left, p->second.refs))).first;
+ }
+ while (length > 0) {
+ assert(p->first == offset);
+ if (length < p->second.length) {
+ ref_map.insert(make_pair(offset + length,
+ record_t(p->second.length - length,
+ p->second.refs)));
+ p->second.length = length;
+ ++p->second.refs;
+ _maybe_merge_left(p);
+ return;
+ }
+ ++p->second.refs;
+ offset += p->second.length;
+ length -= p->second.length;
+ _maybe_merge_left(p);
+ ++p;
+ }
+ if (p != ref_map.end())
+ _maybe_merge_left(p);
+ _check();
+}
+
+void extent_ref_map_t::put(uint64_t offset, uint32_t length,
+ vector<extent_t> *release)
+{
+ map<uint64_t,record_t>::iterator p = ref_map.lower_bound(offset);
+ if (p == ref_map.end() || p->first > offset) {
+ if (p == ref_map.begin()) {
+ assert(0 == "put on missing extent (nothing before)");
+ }
+ --p;
+ if (p->first + p->second.length <= offset) {
+ assert(0 == "put on missing extent (gap)");
+ }
+ }
+ if (p->first < offset) {
+ uint64_t left = p->first + p->second.length - offset;
+ p->second.length = offset - p->first;
+ p = ref_map.insert(map<uint64_t,record_t>::value_type(
+ offset, record_t(left, p->second.refs))).first;
+ }
+ while (length > 0) {
+ assert(p->first == offset);
+ if (length < p->second.length) {
+ ref_map.insert(make_pair(offset + length,
+ record_t(p->second.length - length,
+ p->second.refs)));
+ if (p->second.refs > 1) {
+ p->second.length = length;
+ --p->second.refs;
+ _maybe_merge_left(p);
+ } else {
+ release->push_back(extent_t(p->first, length));
+ ref_map.erase(p);
+ }
+ return;
+ }
+ offset += p->second.length;
+ length -= p->second.length;
+ if (p->second.refs > 1) {
+ --p->second.refs;
+ _maybe_merge_left(p);
+ ++p;
+ } else {
+ release->push_back(extent_t(p->first, p->second.length));
+ ref_map.erase(p++);
+ }
+ }
+ if (p != ref_map.end())
+ _maybe_merge_left(p);
+ _check();
+}
+
+void extent_ref_map_t::encode(bufferlist& bl) const
+{
+ ENCODE_START(1, 1, bl);
+ ::encode(ref_map, bl);
+ ENCODE_FINISH(bl);
+}
+
+void extent_ref_map_t::decode(bufferlist::iterator& p)
+{
+ DECODE_START(1, p);
+ ::decode(ref_map, p);
+ DECODE_FINISH(p);
+}
+
+void extent_ref_map_t::dump(Formatter *f) const
+{
+ f->open_array_section("ref_map");
+ for (auto& p : ref_map) {
+ f->open_object_section("ref");
+ f->dump_unsigned("offset", p.first);
+ f->dump_unsigned("length", p.second.length);
+ f->dump_unsigned("refs", p.second.refs);
+ f->close_section();
+ }
+ f->close_section();
+}
+
+void extent_ref_map_t::generate_test_instances(list<extent_ref_map_t*>& o)
+{
+ o.push_back(new extent_ref_map_t);
+ o.push_back(new extent_ref_map_t);
+ o.back()->add(10, 10);
+ o.back()->add(30, 10, 3);
+ o.back()->get(15, 20);
+}
+
+ostream& operator<<(ostream& out, const extent_ref_map_t& m)
+{
+ out << "ref_map(";
+ for (auto p = m.ref_map.begin(); p != m.ref_map.end(); ++p) {
+ if (p != m.ref_map.begin())
+ out << ",";
+ out << p->first << "~" << p->second.length << "=" << p->second.refs;
+ }
+ out << ")";
+ return out;
+}
+
// overlay_t
void overlay_t::encode(bufferlist& bl) const
struct extent_t {
enum {
FLAG_UNWRITTEN = 1, ///< extent is unwritten (and defined to be zero)
-// FLAG_SHARED = 2, ///< extent is shared by another object, and read-only
+ FLAG_SHARED = 2, ///< extent is shared by another object, and refcounted
};
static string get_flags_string(unsigned flags);
ostream& operator<<(ostream& out, const extent_t& bp);
+/// extent_map: a map of reference counted extents
+struct extent_ref_map_t {
+ struct record_t {
+ uint32_t length;
+ uint32_t refs;
+ record_t(uint32_t l=0, uint32_t r=0) : length(l), refs(r) {}
+ void encode(bufferlist& bl) const {
+ ::encode(length, bl);
+ ::encode(refs, bl);
+ }
+ void decode(bufferlist::iterator& p) {
+ ::decode(length, p);
+ ::decode(refs, p);
+ }
+ };
+ WRITE_CLASS_ENCODER(record_t)
+
+ map<uint64_t,record_t> ref_map;
+
+ void _check() const;
+ void _maybe_merge_left(map<uint64_t,record_t>::iterator& p);
+
+ void add(uint64_t offset, uint32_t len, unsigned ref=2);
+ void get(uint64_t offset, uint32_t len);
+ void put(uint64_t offset, uint32_t len, vector<extent_t> *release);
+
+ void encode(bufferlist& bl) const;
+ void decode(bufferlist::iterator& p);
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<extent_ref_map_t*>& o);
+};
+WRITE_CLASS_ENCODER(extent_ref_map_t::record_t)
+WRITE_CLASS_ENCODER(extent_ref_map_t)
+
+ostream& operator<<(ostream& out, const extent_ref_map_t& rm);
+
+
/// overlay: a byte extent backed by kv pair, logically overlaying other content
struct overlay_t {
uint64_t key; ///< key (nid+key identify the kv pair in the kvdb)
map<uint64_t,extent_t>::iterator find_extent(uint64_t offset) {
map<uint64_t,extent_t>::iterator fp = block_map.lower_bound(offset);
- fp = block_map.lower_bound(offset);
if (fp != block_map.begin()) {
--fp;
if (fp->first + fp->second.length <= offset) {
map<uint64_t,extent_t>::iterator seek_extent(uint64_t offset) {
map<uint64_t,extent_t>::iterator fp = block_map.lower_bound(offset);
- fp = block_map.lower_bound(offset);
if (fp != block_map.begin()) {
--fp;
if (fp->first + fp->second.length <= offset) {
ceph_test_bluefs_CXXFLAGS = $(UNITTEST_CXXFLAGS)
bin_DEBUGPROGRAMS += ceph_test_bluefs
+ceph_test_bluestore_types_SOURCES = test/objectstore/test_bluestore_types.cc
+ceph_test_bluestore_types_LDADD = $(LIBOS) $(UNITTEST_LDADD) $(CEPH_GLOBAL)
+ceph_test_bluestore_types_CXXFLAGS = $(UNITTEST_CXXFLAGS)
+bin_DEBUGPROGRAMS += ceph_test_bluestore_types
+
endif
ceph_test_objectstore_workloadgen_SOURCES = \
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "os/bluestore/bluestore_types.h"
+#include "gtest/gtest.h"
+#include "include/stringify.h"
+
+#include <sstream>
+
+TEST(extent_ref_map_t, add)
+{
+ extent_ref_map_t m;
+ m.add(10, 10);
+ ASSERT_EQ(1, m.ref_map.size());
+ cout << m << std::endl;
+ m.add(20, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(1, m.ref_map.size());
+ ASSERT_EQ(20, m.ref_map[10].length);
+ ASSERT_EQ(2, m.ref_map[10].refs);
+ m.add(40, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(2, m.ref_map.size());
+ m.add(30, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(1, m.ref_map.size());
+ m.add(50, 10, 3);
+ cout << m << std::endl;
+ ASSERT_EQ(2, m.ref_map.size());
+}
+
+TEST(extent_ref_map_t, get)
+{
+ extent_ref_map_t m;
+ m.add(00, 30);
+ cout << m << std::endl;
+ m.get(10, 10);
+ cout << m << std::endl;
+ ASSERT_EQ(3, m.ref_map.size());
+ ASSERT_EQ(10, m.ref_map[0].length);
+ ASSERT_EQ(2, m.ref_map[0].refs);
+ ASSERT_EQ(10, m.ref_map[10].length);
+ ASSERT_EQ(3, m.ref_map[10].refs);
+ ASSERT_EQ(10, m.ref_map[20].length);
+ ASSERT_EQ(2, m.ref_map[20].refs);
+ m.get(20, 5);
+ cout << m << std::endl;
+ ASSERT_EQ(3, m.ref_map.size());
+ ASSERT_EQ(15, m.ref_map[10].length);
+ ASSERT_EQ(3, m.ref_map[10].refs);
+ ASSERT_EQ(5, m.ref_map[25].length);
+ ASSERT_EQ(2, m.ref_map[25].refs);
+ m.get(5, 20);
+ cout << m << std::endl;
+ ASSERT_EQ(4, m.ref_map.size());
+ ASSERT_EQ(5, m.ref_map[0].length);
+ ASSERT_EQ(2, m.ref_map[0].refs);
+ ASSERT_EQ(5, m.ref_map[5].length);
+ ASSERT_EQ(3, m.ref_map[5].refs);
+ ASSERT_EQ(15, m.ref_map[10].length);
+ ASSERT_EQ(4, m.ref_map[10].refs);
+ ASSERT_EQ(5, m.ref_map[25].length);
+ ASSERT_EQ(2, m.ref_map[25].refs);
+ m.get(25, 3);
+ cout << m << std::endl;
+ ASSERT_EQ(5, m.ref_map.size());
+ ASSERT_EQ(5, m.ref_map[0].length);
+ ASSERT_EQ(2, m.ref_map[0].refs);
+ ASSERT_EQ(5, m.ref_map[5].length);
+ ASSERT_EQ(3, m.ref_map[5].refs);
+ ASSERT_EQ(15, m.ref_map[10].length);
+ ASSERT_EQ(4, m.ref_map[10].refs);
+ ASSERT_EQ(3, m.ref_map[25].length);
+ ASSERT_EQ(3, m.ref_map[25].refs);
+ ASSERT_EQ(2, m.ref_map[28].length);
+ ASSERT_EQ(2, m.ref_map[28].refs);
+}
+
+TEST(extent_ref_map_t, put)
+{
+ extent_ref_map_t m;
+ vector<extent_t> r;
+ m.add(10, 30, 1);
+ m.put(10, 30, &r);
+ cout << m << " " << r << std::endl;
+ ASSERT_EQ(0, m.ref_map.size());
+ ASSERT_EQ(1, r.size());
+ ASSERT_EQ(10, r[0].offset);
+ ASSERT_EQ(30, r[0].length);
+ r.clear();
+ m.add(10, 30, 1);
+ m.get(20, 10);
+ m.put(10, 30, &r);
+ cout << m << " " << r << std::endl;
+ ASSERT_EQ(1, m.ref_map.size());
+ ASSERT_EQ(10, m.ref_map[20].length);
+ ASSERT_EQ(1, m.ref_map[20].refs);
+ ASSERT_EQ(2, r.size());
+ ASSERT_EQ(10, r[0].offset);
+ ASSERT_EQ(10, r[0].length);
+ ASSERT_EQ(30, r[1].offset);
+ ASSERT_EQ(10, r[1].length);
+ r.clear();
+ m.add(30, 10);
+ m.put(20, 15, &r);
+ cout << m << " " << r << std::endl;
+ ASSERT_EQ(2, m.ref_map.size());
+ ASSERT_EQ(5, m.ref_map[30].length);
+ ASSERT_EQ(1, m.ref_map[30].refs);
+ ASSERT_EQ(5, m.ref_map[35].length);
+ ASSERT_EQ(2, m.ref_map[35].refs);
+ ASSERT_EQ(1, r.size());
+ ASSERT_EQ(20, r[0].offset);
+ ASSERT_EQ(10, r[0].length);
+ r.clear();
+ m.put(33, 5, &r);
+ cout << m << " " << r << std::endl;
+ ASSERT_EQ(3, m.ref_map.size());
+ ASSERT_EQ(3, m.ref_map[30].length);
+ ASSERT_EQ(1, m.ref_map[30].refs);
+ ASSERT_EQ(3, m.ref_map[35].length);
+ ASSERT_EQ(1, m.ref_map[35].refs);
+ ASSERT_EQ(2, m.ref_map[38].length);
+ ASSERT_EQ(2, m.ref_map[38].refs);
+ ASSERT_EQ(1, r.size());
+ ASSERT_EQ(33, r[0].offset);
+ ASSERT_EQ(2, r[0].length);
+}