]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore: add extent_ref_map_t
authorSage Weil <sage@redhat.com>
Thu, 10 Dec 2015 22:27:04 +0000 (17:27 -0500)
committerSage Weil <sage@redhat.com>
Fri, 1 Jan 2016 18:06:59 +0000 (13:06 -0500)
This will be used to refcount extents for some subset
of the store (objects with same name or hash value?).

Signed-off-by: Sage Weil <sage@redhat.com>
src/os/bluestore/BlueStore.h
src/os/bluestore/bluestore_types.cc
src/os/bluestore/bluestore_types.h
src/test/Makefile-server.am
src/test/objectstore/test_bluestore_types.cc [new file with mode: 0644]

index fe8dafbbbf512e41220780d116087e1622015fc2..4b1c3b01ca6433850713a49fa3a1bd68e5f39444 100644 (file)
@@ -45,6 +45,12 @@ public:
 
   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
index b4c46077d6eaa8853b2ed0f5b9cd7dd2219bdcf3..fc0acf369db0feed408d292239036fe662febd11 100644 (file)
@@ -132,6 +132,186 @@ ostream& operator<<(ostream& out, const extent_t& e)
   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
index 96449ae9723a7b159bb1d7c12489022e7516e6e6..576624c2cc61d3600eac32593f0e7cbe683bf634 100644 (file)
@@ -58,7 +58,7 @@ WRITE_CLASS_ENCODER(cnode_t)
 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);
 
@@ -100,6 +100,43 @@ WRITE_CLASS_ENCODER(extent_t)
 
 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)
@@ -144,7 +181,6 @@ struct onode_t {
 
   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) {
@@ -158,7 +194,6 @@ struct onode_t {
 
   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) {
index 7bea0b82ad8d106e86b5fb8c4c233e984e004163..fd30c3f0b7d58f48ee8bd362277cc8f67c1fe3b4 100644 (file)
@@ -70,6 +70,11 @@ ceph_test_bluefs_LDADD = $(LIBOS) $(UNITTEST_LDADD) $(CEPH_GLOBAL)
 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 = \
diff --git a/src/test/objectstore/test_bluestore_types.cc b/src/test/objectstore/test_bluestore_types.cc
new file mode 100644 (file)
index 0000000..225ad6b
--- /dev/null
@@ -0,0 +1,129 @@
+// -*- 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);
+}