X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Ftest%2Fobjectstore%2Ftest_bluestore_types.cc;fp=src%2Fceph%2Fsrc%2Ftest%2Fobjectstore%2Ftest_bluestore_types.cc;h=f16f8452c4c55bf27d75cd716b49230691417a43;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/test/objectstore/test_bluestore_types.cc b/src/ceph/src/test/objectstore/test_bluestore_types.cc new file mode 100644 index 0000000..f16f845 --- /dev/null +++ b/src/ceph/src/test/objectstore/test_bluestore_types.cc @@ -0,0 +1,1455 @@ +// -*- 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 "common/ceph_time.h" +#include "os/bluestore/BlueStore.h" +#include "common/ceph_argparse.h" +#include "global/global_init.h" +#include "global/global_context.h" + +#include + +#define _STR(x) #x +#define STRINGIFY(x) _STR(x) + +TEST(bluestore, sizeof) { +#define P(t) cout << STRINGIFY(t) << "\t" << sizeof(t) << std::endl + P(BlueStore::Onode); + P(BlueStore::Extent); + P(BlueStore::Blob); + P(BlueStore::SharedBlob); + P(BlueStore::ExtentMap); + P(BlueStore::extent_map_t); + P(BlueStore::blob_map_t); + P(BlueStore::BufferSpace); + P(BlueStore::Buffer); + P(bluestore_onode_t); + P(bluestore_blob_t); + P(PExtentVector); + P(bluestore_shared_blob_t); + P(bluestore_extent_ref_map_t); + P(bluestore_extent_ref_map_t::record_t); + P(bluestore_blob_use_tracker_t); + P(std::atomic_int); + P(BlueStore::SharedBlobRef); + P(boost::intrusive::set_base_hook<>); + P(boost::intrusive::unordered_set_base_hook<>); + P(bufferlist); + P(bufferptr); + cout << "map\t" << sizeof(map) << std::endl; + cout << "map\t" << sizeof(map) << std::endl; +} + +TEST(bluestore_extent_ref_map_t, add) +{ + bluestore_extent_ref_map_t m; + m.get(10, 10); + ASSERT_EQ(1u, m.ref_map.size()); + cout << m << std::endl; + m.get(20, 10); + cout << m << std::endl; + ASSERT_EQ(1u, m.ref_map.size()); + ASSERT_EQ(20u, m.ref_map[10].length); + ASSERT_EQ(1u, m.ref_map[10].refs); + m.get(40, 10); + cout << m << std::endl; + ASSERT_EQ(2u, m.ref_map.size()); + m.get(30, 10); + cout << m << std::endl; + ASSERT_EQ(1u, m.ref_map.size()); + m.get(50, 10); + cout << m << std::endl; + ASSERT_EQ(1u, m.ref_map.size()); + m.get(5, 5); + cout << m << std::endl; + ASSERT_EQ(1u, m.ref_map.size()); +} + +TEST(bluestore_extent_ref_map_t, get) +{ + bluestore_extent_ref_map_t m; + m.get(00, 30); + cout << m << std::endl; + m.get(10, 10); + cout << m << std::endl; + ASSERT_EQ(3u, m.ref_map.size()); + ASSERT_EQ(10u, m.ref_map[0].length); + ASSERT_EQ(1u, m.ref_map[0].refs); + ASSERT_EQ(10u, m.ref_map[10].length); + ASSERT_EQ(2u, m.ref_map[10].refs); + ASSERT_EQ(10u, m.ref_map[20].length); + ASSERT_EQ(1u, m.ref_map[20].refs); + m.get(20, 5); + cout << m << std::endl; + ASSERT_EQ(3u, m.ref_map.size()); + ASSERT_EQ(15u, m.ref_map[10].length); + ASSERT_EQ(2u, m.ref_map[10].refs); + ASSERT_EQ(5u, m.ref_map[25].length); + ASSERT_EQ(1u, m.ref_map[25].refs); + m.get(5, 20); + cout << m << std::endl; + ASSERT_EQ(4u, m.ref_map.size()); + ASSERT_EQ(5u, m.ref_map[0].length); + ASSERT_EQ(1u, m.ref_map[0].refs); + ASSERT_EQ(5u, m.ref_map[5].length); + ASSERT_EQ(2u, m.ref_map[5].refs); + ASSERT_EQ(15u, m.ref_map[10].length); + ASSERT_EQ(3u, m.ref_map[10].refs); + ASSERT_EQ(5u, m.ref_map[25].length); + ASSERT_EQ(1u, m.ref_map[25].refs); + m.get(25, 3); + cout << m << std::endl; + ASSERT_EQ(5u, m.ref_map.size()); + ASSERT_EQ(5u, m.ref_map[0].length); + ASSERT_EQ(1u, m.ref_map[0].refs); + ASSERT_EQ(5u, m.ref_map[5].length); + ASSERT_EQ(2u, m.ref_map[5].refs); + ASSERT_EQ(15u, m.ref_map[10].length); + ASSERT_EQ(3u, m.ref_map[10].refs); + ASSERT_EQ(3u, m.ref_map[25].length); + ASSERT_EQ(2u, m.ref_map[25].refs); + ASSERT_EQ(2u, m.ref_map[28].length); + ASSERT_EQ(1u, m.ref_map[28].refs); +} + +TEST(bluestore_extent_ref_map_t, put) +{ + bluestore_extent_ref_map_t m; + PExtentVector r; + bool maybe_unshared = false; + m.get(10, 30); + maybe_unshared = true; + m.put(10, 30, &r, &maybe_unshared); + cout << m << " " << r << " " << (int)maybe_unshared << std::endl; + ASSERT_EQ(0u, m.ref_map.size()); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(10u, r[0].offset); + ASSERT_EQ(30u, r[0].length); + ASSERT_TRUE(maybe_unshared); + r.clear(); + m.get(10, 30); + m.get(20, 10); + maybe_unshared = true; + m.put(10, 30, &r, &maybe_unshared); + cout << m << " " << r << " " << (int)maybe_unshared << std::endl; + ASSERT_EQ(1u, m.ref_map.size()); + ASSERT_EQ(10u, m.ref_map[20].length); + ASSERT_EQ(1u, m.ref_map[20].refs); + ASSERT_EQ(2u, r.size()); + ASSERT_EQ(10u, r[0].offset); + ASSERT_EQ(10u, r[0].length); + ASSERT_EQ(30u, r[1].offset); + ASSERT_EQ(10u, r[1].length); + ASSERT_TRUE(maybe_unshared); + r.clear(); + m.get(30, 10); + m.get(30, 10); + maybe_unshared = true; + m.put(20, 15, &r, &maybe_unshared); + cout << m << " " << r << " " << (int)maybe_unshared << std::endl; + ASSERT_EQ(2u, m.ref_map.size()); + ASSERT_EQ(5u, m.ref_map[30].length); + ASSERT_EQ(1u, m.ref_map[30].refs); + ASSERT_EQ(5u, m.ref_map[35].length); + ASSERT_EQ(2u, m.ref_map[35].refs); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(20u, r[0].offset); + ASSERT_EQ(10u, r[0].length); + ASSERT_FALSE(maybe_unshared); + r.clear(); + maybe_unshared = true; + m.put(33, 5, &r, &maybe_unshared); + cout << m << " " << r << " " << (int)maybe_unshared << std::endl; + ASSERT_EQ(3u, m.ref_map.size()); + ASSERT_EQ(3u, m.ref_map[30].length); + ASSERT_EQ(1u, m.ref_map[30].refs); + ASSERT_EQ(3u, m.ref_map[35].length); + ASSERT_EQ(1u, m.ref_map[35].refs); + ASSERT_EQ(2u, m.ref_map[38].length); + ASSERT_EQ(2u, m.ref_map[38].refs); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(33u, r[0].offset); + ASSERT_EQ(2u, r[0].length); + ASSERT_FALSE(maybe_unshared); + r.clear(); + maybe_unshared = true; + m.put(38, 2, &r, &maybe_unshared); + cout << m << " " << r << " " << (int)maybe_unshared << std::endl; + ASSERT_TRUE(maybe_unshared); +} + +TEST(bluestore_extent_ref_map_t, contains) +{ + bluestore_extent_ref_map_t m; + m.get(10, 30); + ASSERT_TRUE(m.contains(10, 30)); + ASSERT_TRUE(m.contains(10, 10)); + ASSERT_TRUE(m.contains(30, 10)); + ASSERT_FALSE(m.contains(0, 10)); + ASSERT_FALSE(m.contains(0, 20)); + ASSERT_FALSE(m.contains(0, 100)); + ASSERT_FALSE(m.contains(40, 10)); + ASSERT_FALSE(m.contains(30, 11)); + m.get(40, 10); + m.get(40, 10); + ASSERT_TRUE(m.contains(30, 11)); + ASSERT_TRUE(m.contains(30, 20)); + ASSERT_TRUE(m.contains(10, 40)); + ASSERT_FALSE(m.contains(0, 50)); + ASSERT_FALSE(m.contains(40, 20)); + m.get(60, 100); + ASSERT_TRUE(m.contains(60, 10)); + ASSERT_TRUE(m.contains(40, 10)); + ASSERT_FALSE(m.contains(40, 11)); + ASSERT_FALSE(m.contains(40, 20)); + ASSERT_FALSE(m.contains(40, 30)); + ASSERT_FALSE(m.contains(40, 3000)); + ASSERT_FALSE(m.contains(4000, 30)); +} + +TEST(bluestore_extent_ref_map_t, intersects) +{ + bluestore_extent_ref_map_t m; + m.get(10, 30); + ASSERT_TRUE(m.intersects(10, 30)); + ASSERT_TRUE(m.intersects(0, 11)); + ASSERT_TRUE(m.intersects(10, 40)); + ASSERT_TRUE(m.intersects(15, 40)); + ASSERT_FALSE(m.intersects(0, 10)); + ASSERT_FALSE(m.intersects(0, 5)); + ASSERT_FALSE(m.intersects(40, 20)); + ASSERT_FALSE(m.intersects(41, 20)); + m.get(40, 10); + m.get(40, 10); + ASSERT_TRUE(m.intersects(0, 100)); + ASSERT_TRUE(m.intersects(10, 35)); + ASSERT_TRUE(m.intersects(45, 10)); + ASSERT_FALSE(m.intersects(50, 5)); + m.get(60, 100); + ASSERT_TRUE(m.intersects(45, 10)); + ASSERT_TRUE(m.intersects(55, 10)); + ASSERT_TRUE(m.intersects(50, 11)); + ASSERT_FALSE(m.intersects(50, 10)); + ASSERT_FALSE(m.intersects(51, 9)); + ASSERT_FALSE(m.intersects(55, 1)); +} + +TEST(bluestore_blob_t, calc_csum) +{ + bufferlist bl; + bl.append("asdfghjkqwertyuizxcvbnm,"); + bufferlist bl2; + bl2.append("xxxxXXXXyyyyYYYYzzzzZZZZ"); + bufferlist f; + f.substr_of(bl, 0, 8); + bufferlist m; + m.substr_of(bl, 8, 8); + bufferlist e; + e.substr_of(bl, 16, 8); + bufferlist n; + n.append("12345678"); + + for (unsigned csum_type = Checksummer::CSUM_NONE + 1; + csum_type < Checksummer::CSUM_MAX; + ++csum_type) { + cout << "csum_type " << Checksummer::get_csum_type_string(csum_type) + << std::endl; + + bluestore_blob_t b; + int bad_off; + uint64_t bad_csum; + ASSERT_EQ(0, b.verify_csum(0, bl, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + + b.init_csum(csum_type, 3, 24); + cout << " value size " << b.get_csum_value_size() << std::endl; + b.calc_csum(0, bl); + ASSERT_EQ(0, b.verify_csum(0, bl, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + ASSERT_EQ(-1, b.verify_csum(0, bl2, &bad_off, &bad_csum)); + ASSERT_EQ(0, bad_off); + + ASSERT_EQ(0, b.verify_csum(0, f, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + ASSERT_EQ(-1, b.verify_csum(8, f, &bad_off, &bad_csum)); + ASSERT_EQ(8, bad_off); + ASSERT_EQ(-1, b.verify_csum(16, f, &bad_off, &bad_csum)); + ASSERT_EQ(16, bad_off); + + ASSERT_EQ(-1, b.verify_csum(0, m, &bad_off, &bad_csum)); + ASSERT_EQ(0, bad_off); + ASSERT_EQ(0, b.verify_csum(8, m, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + ASSERT_EQ(-1, b.verify_csum(16, m, &bad_off, &bad_csum)); + ASSERT_EQ(16, bad_off); + + ASSERT_EQ(-1, b.verify_csum(0, e, &bad_off, &bad_csum)); + ASSERT_EQ(0, bad_off); + ASSERT_EQ(-1, b.verify_csum(8, e, &bad_off, &bad_csum)); + ASSERT_EQ(8, bad_off); + ASSERT_EQ(0, b.verify_csum(16, e, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + + b.calc_csum(8, n); + ASSERT_EQ(0, b.verify_csum(0, f, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + ASSERT_EQ(0, b.verify_csum(8, n, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + ASSERT_EQ(0, b.verify_csum(16, e, &bad_off, &bad_csum)); + ASSERT_EQ(-1, bad_off); + ASSERT_EQ(-1, b.verify_csum(0, bl, &bad_off, &bad_csum)); + ASSERT_EQ(8, bad_off); + } +} + +TEST(bluestore_blob_t, csum_bench) +{ + bufferlist bl; + bufferptr bp(10485760); + for (char *a = bp.c_str(); a < bp.c_str() + bp.length(); ++a) + *a = (unsigned long)a & 0xff; + bl.append(bp); + int count = 256; + for (unsigned csum_type = 1; + csum_type < Checksummer::CSUM_MAX; + ++csum_type) { + bluestore_blob_t b; + b.init_csum(csum_type, 12, bl.length()); + ceph::mono_clock::time_point start = ceph::mono_clock::now(); + for (int i = 0; i(end - start); + double mbsec = (double)count * (double)bl.length() / 1000000.0 / (double)dur.count() * 1000000000.0; + cout << "csum_type " << Checksummer::get_csum_type_string(csum_type) + << ", " << dur << " seconds, " + << mbsec << " MB/sec" << std::endl; + } +} + +TEST(Blob, put_ref) +{ + { + BlueStore store(g_ceph_context, "", 4096); + BlueStore::Cache *cache = BlueStore::Cache::create( + g_ceph_context, "lru", NULL); + BlueStore::Collection coll(&store, cache, coll_t()); + BlueStore::Blob b; + b.shared_blob = new BlueStore::SharedBlob(nullptr); + b.shared_blob->get(); // hack to avoid dtor from running + b.dirty_blob().allocated_test(bluestore_pextent_t(0x40715000, 0x2000)); + b.dirty_blob().allocated_test( + bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x8000)); + b.dirty_blob().allocated_test(bluestore_pextent_t(0x4071f000, 0x5000)); + b.get_ref(&coll, 0, 0x1200); + b.get_ref(&coll, 0xae00, 0x4200); + ASSERT_EQ(0x5400u, b.get_referenced_bytes()); + cout << b << std::endl; + PExtentVector r; + + ASSERT_FALSE(b.put_ref(&coll, 0, 0x1200, &r)); + ASSERT_EQ(0x4200u, b.get_referenced_bytes()); + cout << " r " << r << std::endl; + cout << b << std::endl; + + r.clear(); + ASSERT_TRUE(b.put_ref(&coll, 0xae00, 0x4200, &r)); + ASSERT_EQ(0u, b.get_referenced_bytes()); + cout << " r " << r << std::endl; + cout << b << std::endl; + } + + unsigned mas = 4096; + BlueStore store(g_ceph_context, "", 8192); + BlueStore::Cache *cache = BlueStore::Cache::create( + g_ceph_context, "lru", NULL); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, cache, coll_t())); + + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(0, mas * 2)); + B.get_ref(coll.get(), 0, mas*2); + ASSERT_EQ(mas * 2, B.get_referenced_bytes()); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_TRUE(B.put_ref(coll.get(), 0, mas*2, &r)); + ASSERT_EQ(0u, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_FALSE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(0, mas)); + ASSERT_FALSE(b.is_allocated(mas, 0)); + ASSERT_FALSE(b.get_extents()[0].is_valid()); + ASSERT_EQ(mas*2, b.get_extents()[0].length); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(123, mas * 2)); + B.get_ref(coll.get(), 0, mas*2); + ASSERT_EQ(mas * 2, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), 0, mas, &r)); + ASSERT_EQ(mas, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_TRUE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(0u, B.get_referenced_bytes()); + ASSERT_EQ(0u, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(123u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_FALSE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.get_extents()[0].is_valid()); + ASSERT_EQ(mas*2, b.get_extents()[0].length); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(1, mas)); + b.allocated_test(bluestore_pextent_t(2, mas)); + b.allocated_test(bluestore_pextent_t(3, mas)); + b.allocated_test(bluestore_pextent_t(4, mas)); + B.get_ref(coll.get(), 0, mas*4); + ASSERT_EQ(mas * 4, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(mas * 3, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*4)); + ASSERT_TRUE(b.is_allocated(mas, mas)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r)); + ASSERT_EQ(mas * 2, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(mas*2, mas)); + ASSERT_TRUE(b.is_allocated(0, mas*4)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas, &r)); + ASSERT_EQ(mas, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(2u, r.size()); + ASSERT_EQ(3u, r[0].offset); + ASSERT_EQ(mas, r[0].length); + ASSERT_EQ(4u, r[1].offset); + ASSERT_EQ(mas, r[1].length); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(mas*2, mas*2)); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_TRUE(b.get_extents()[1].is_valid()); + ASSERT_FALSE(b.get_extents()[2].is_valid()); + ASSERT_EQ(3u, b.get_extents().size()); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(1, mas)); + b.allocated_test(bluestore_pextent_t(2, mas)); + b.allocated_test(bluestore_pextent_t(3, mas)); + b.allocated_test(bluestore_pextent_t(4, mas)); + b.allocated_test(bluestore_pextent_t(5, mas)); + b.allocated_test(bluestore_pextent_t(6, mas)); + B.get_ref(coll.get(), 0, mas*6); + ASSERT_EQ(mas * 6, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(mas * 5, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*6)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r)); + ASSERT_EQ(mas * 4, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*6)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas, &r)); + ASSERT_EQ(mas * 3, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(2u, r.size()); + ASSERT_EQ(3u, r[0].offset); + ASSERT_EQ(mas, r[0].length); + ASSERT_EQ(4u, r[1].offset); + ASSERT_EQ(mas, r[1].length); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(mas*2, mas*2)); + ASSERT_TRUE(b.is_allocated(mas*4, mas*2)); + ASSERT_EQ(5u, b.get_extents().size()); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_TRUE(b.get_extents()[1].is_valid()); + ASSERT_FALSE(b.get_extents()[2].is_valid()); + ASSERT_TRUE(b.get_extents()[3].is_valid()); + ASSERT_TRUE(b.get_extents()[4].is_valid()); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(1, mas * 6)); + B.get_ref(coll.get(), 0, mas*6); + ASSERT_EQ(mas * 6, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(mas * 5, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*6)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r)); + ASSERT_EQ(mas * 4, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*6)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas, &r)); + ASSERT_EQ(mas * 3, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x2001u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(mas*2, mas*2)); + ASSERT_TRUE(b.is_allocated(mas*4, mas*2)); + ASSERT_EQ(3u, b.get_extents().size()); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_FALSE(b.get_extents()[1].is_valid()); + ASSERT_TRUE(b.get_extents()[2].is_valid()); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(1, mas * 4)); + b.allocated_test(bluestore_pextent_t(2, mas * 4)); + b.allocated_test(bluestore_pextent_t(3, mas * 4)); + B.get_ref(coll.get(), 0, mas*12); + ASSERT_EQ(mas * 12, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(mas * 11, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*12)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*9, mas, &r)); + ASSERT_EQ(mas * 10, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*12)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas*7, &r)); + ASSERT_EQ(mas * 3, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(3u, r.size()); + ASSERT_EQ(0x2001u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(0x2u, r[1].offset); + ASSERT_EQ(mas*4, r[1].length); + ASSERT_EQ(0x3u, r[2].offset); + ASSERT_EQ(mas*2, r[2].length); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(mas*2, mas*8)); + ASSERT_TRUE(b.is_allocated(mas*10, mas*2)); + ASSERT_EQ(3u, b.get_extents().size()); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_FALSE(b.get_extents()[1].is_valid()); + ASSERT_TRUE(b.get_extents()[2].is_valid()); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(1, mas * 4)); + b.allocated_test(bluestore_pextent_t(2, mas * 4)); + b.allocated_test(bluestore_pextent_t(3, mas * 4)); + B.get_ref(coll.get(), 0, mas*12); + ASSERT_EQ(mas * 12, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(mas * 11, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*12)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*9, mas, &r)); + ASSERT_EQ(mas * 10, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*12)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas*7, &r)); + ASSERT_EQ(mas * 3, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(3u, r.size()); + ASSERT_EQ(0x2001u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(0x2u, r[1].offset); + ASSERT_EQ(mas*4, r[1].length); + ASSERT_EQ(0x3u, r[2].offset); + ASSERT_EQ(mas*2, r[2].length); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(mas*2, mas*8)); + ASSERT_TRUE(b.is_allocated(mas*10, mas*2)); + ASSERT_EQ(3u, b.get_extents().size()); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_FALSE(b.get_extents()[1].is_valid()); + ASSERT_TRUE(b.get_extents()[2].is_valid()); + ASSERT_FALSE(B.put_ref(coll.get(), 0, mas, &r)); + ASSERT_EQ(mas * 2, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x1u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(2u, b.get_extents().size()); + ASSERT_FALSE(b.get_extents()[0].is_valid()); + ASSERT_TRUE(b.get_extents()[1].is_valid()); + ASSERT_TRUE(B.put_ref(coll.get(), mas*10, mas*2, &r)); + ASSERT_EQ(mas * 0, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x2003u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(1u, b.get_extents().size()); + ASSERT_FALSE(b.get_extents()[0].is_valid()); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(1, mas * 4)); + b.allocated_test(bluestore_pextent_t(2, mas * 4)); + b.allocated_test(bluestore_pextent_t(3, mas * 4)); + B.get_ref(coll.get(), 0, mas*12); + ASSERT_EQ(mas * 12, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(mas * 11, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*12)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*9, mas, &r)); + ASSERT_EQ(mas * 10, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*12)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas*7, &r)); + ASSERT_EQ(mas * 3, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(3u, r.size()); + ASSERT_EQ(0x2001u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(0x2u, r[1].offset); + ASSERT_EQ(mas*4, r[1].length); + ASSERT_EQ(0x3u, r[2].offset); + ASSERT_EQ(mas*2, r[2].length); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(mas*2, mas*8)); + ASSERT_TRUE(b.is_allocated(mas*10, mas*2)); + ASSERT_EQ(3u, b.get_extents().size()); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_FALSE(b.get_extents()[1].is_valid()); + ASSERT_TRUE(b.get_extents()[2].is_valid()); + ASSERT_FALSE(B.put_ref(coll.get(), mas*10, mas*2, &r)); + ASSERT_EQ(mas * 1, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x2003u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(2u, b.get_extents().size()); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_FALSE(b.get_extents()[1].is_valid()); + ASSERT_TRUE(B.put_ref(coll.get(), 0, mas, &r)); + ASSERT_EQ(mas * 0, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x1u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(1u, b.get_extents().size()); + ASSERT_FALSE(b.get_extents()[0].is_valid()); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(1, mas * 8)); + B.get_ref(coll.get(), 0, mas*8); + ASSERT_EQ(mas * 8, B.get_referenced_bytes()); + ASSERT_FALSE(B.put_ref(coll.get(), 0, mas, &r)); + ASSERT_EQ(mas * 7, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*8)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*7, mas, &r)); + ASSERT_EQ(mas * 6, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*8)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*2, mas, &r)); + ASSERT_EQ(mas * 5, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, 8)); + ASSERT_FALSE(B.put_ref(coll.get(), mas*3, mas*4, &r)); + ASSERT_EQ(mas * 1, B.get_referenced_bytes()); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x2001u, r[0].offset); + ASSERT_EQ(mas*6, r[0].length); + ASSERT_TRUE(b.is_allocated(0, mas*2)); + ASSERT_FALSE(b.is_allocated(mas*2, mas*6)); + ASSERT_EQ(2u, b.get_extents().size()); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_FALSE(b.get_extents()[1].is_valid()); + ASSERT_TRUE(B.put_ref(coll.get(), mas, mas, &r)); + ASSERT_EQ(mas * 0, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x1u, r[0].offset); + ASSERT_EQ(mas*2, r[0].length); + ASSERT_EQ(1u, b.get_extents().size()); + ASSERT_FALSE(b.get_extents()[0].is_valid()); + } + // verify csum chunk size if factored in properly + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + PExtentVector r; + b.allocated_test(bluestore_pextent_t(0, mas*4)); + b.init_csum(Checksummer::CSUM_CRC32C, 14, mas * 4); + B.get_ref(coll.get(), 0, mas*4); + ASSERT_EQ(mas * 4, B.get_referenced_bytes()); + ASSERT_TRUE(b.is_allocated(0, mas*4)); + ASSERT_FALSE(B.put_ref(coll.get(), 0, mas*3, &r)); + ASSERT_EQ(mas * 1, B.get_referenced_bytes()); + cout << "r " << r << " " << b << std::endl; + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(b.is_allocated(0, mas*4)); + ASSERT_TRUE(b.get_extents()[0].is_valid()); + ASSERT_EQ(mas*4, b.get_extents()[0].length); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + b.allocated_test(bluestore_pextent_t(0x40101000, 0x4000)); + b.allocated_test(bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, + 0x13000)); + + b.allocated_test(bluestore_pextent_t(0x40118000, 0x7000)); + B.get_ref(coll.get(), 0x0, 0x3800); + B.get_ref(coll.get(), 0x17c00, 0x6400); + ASSERT_EQ(0x3800u + 0x6400u, B.get_referenced_bytes()); + b.set_flag(bluestore_blob_t::FLAG_SHARED); + b.init_csum(Checksummer::CSUM_CRC32C, 12, 0x1e000); + + cout << "before: " << B << std::endl; + PExtentVector r; + ASSERT_FALSE(B.put_ref(coll.get(), 0x1800, 0x2000, &r)); + ASSERT_EQ(0x3800u + 0x6400u - 0x2000u, B.get_referenced_bytes()); + cout << "after: " << B << std::endl; + cout << "r " << r << std::endl; + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + b.allocated_test(bluestore_pextent_t(1, 0x5000)); + b.allocated_test(bluestore_pextent_t(2, 0x5000)); + B.get_ref(coll.get(), 0x0, 0xa000); + ASSERT_EQ(0xa000u, B.get_referenced_bytes()); + cout << "before: " << B << std::endl; + PExtentVector r; + ASSERT_FALSE(B.put_ref(coll.get(), 0x8000, 0x2000, &r)); + cout << "after: " << B << std::endl; + cout << "r " << r << std::endl; + ASSERT_EQ(0x8000u, B.get_referenced_bytes()); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(0x3002u, r[0].offset); + ASSERT_EQ(0x2000u, r[0].length); + } + { + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + b.allocated_test(bluestore_pextent_t(1, 0x7000)); + b.allocated_test(bluestore_pextent_t(2, 0x7000)); + B.get_ref(coll.get(), 0x0, 0xe000); + ASSERT_EQ(0xe000u, B.get_referenced_bytes()); + cout << "before: " << B << std::endl; + PExtentVector r; + ASSERT_FALSE(B.put_ref(coll.get(), 0, 0xb000, &r)); + ASSERT_EQ(0x3000u, B.get_referenced_bytes()); + cout << "after: " << B << std::endl; + cout << "r " << r << std::endl; + ASSERT_EQ(0x3000u, B.get_referenced_bytes()); + ASSERT_EQ(2u, r.size()); + ASSERT_EQ(1u, r[0].offset); + ASSERT_EQ(0x7000u, r[0].length); + ASSERT_EQ(2u, r[1].offset); + ASSERT_EQ(0x3000u, r[1].length); // we have 0x1000 bytes less due to + // alignment caused by min_alloc_size = 0x2000 + } + { + BlueStore store(g_ceph_context, "", 0x4000); + BlueStore::Cache *cache = BlueStore::Cache::create( + g_ceph_context, "lru", NULL); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, cache, coll_t())); + BlueStore::Blob B; + B.shared_blob = new BlueStore::SharedBlob(nullptr); + B.shared_blob->get(); // hack to avoid dtor from running + bluestore_blob_t& b = B.dirty_blob(); + b.allocated_test(bluestore_pextent_t(1, 0x5000)); + b.allocated_test(bluestore_pextent_t(2, 0x7000)); + B.get_ref(coll.get(), 0x0, 0xc000); + ASSERT_EQ(0xc000u, B.get_referenced_bytes()); + cout << "before: " << B << std::endl; + PExtentVector r; + ASSERT_FALSE(B.put_ref(coll.get(), 0x2000, 0xa000, &r)); + cout << "after: " << B << std::endl; + cout << "r " << r << std::endl; + ASSERT_EQ(0x2000u, B.get_referenced_bytes()); + ASSERT_EQ(2u, r.size()); + ASSERT_EQ(0x4001u, r[0].offset); + ASSERT_EQ(0x1000u, r[0].length); + ASSERT_EQ(2u, r[1].offset); + ASSERT_EQ(0x7000u, r[1].length); + ASSERT_EQ(1u, b.get_extents()[0].offset); + ASSERT_EQ(0x4000u, b.get_extents()[0].length); + } +} + +TEST(bluestore_blob_t, can_split) +{ + bluestore_blob_t a; + ASSERT_TRUE(a.can_split()); + a.flags = bluestore_blob_t::FLAG_SHARED; + ASSERT_FALSE(a.can_split()); + a.flags = bluestore_blob_t::FLAG_COMPRESSED; + ASSERT_FALSE(a.can_split()); + a.flags = bluestore_blob_t::FLAG_HAS_UNUSED; + ASSERT_FALSE(a.can_split()); +} + +TEST(bluestore_blob_t, can_split_at) +{ + bluestore_blob_t a; + a.allocated_test(bluestore_pextent_t(0x10000, 0x2000)); + a.allocated_test(bluestore_pextent_t(0x20000, 0x2000)); + ASSERT_TRUE(a.can_split_at(0x1000)); + ASSERT_TRUE(a.can_split_at(0x1800)); + a.init_csum(Checksummer::CSUM_CRC32C, 12, 0x4000); + ASSERT_TRUE(a.can_split_at(0x1000)); + ASSERT_TRUE(a.can_split_at(0x2000)); + ASSERT_TRUE(a.can_split_at(0x3000)); + ASSERT_FALSE(a.can_split_at(0x2800)); +} + +TEST(bluestore_blob_t, prune_tail) +{ + bluestore_blob_t a; + a.allocated_test(bluestore_pextent_t(0x10000, 0x2000)); + a.allocated_test(bluestore_pextent_t(0x20000, 0x2000)); + ASSERT_FALSE(a.can_prune_tail()); + a.allocated_test( + bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x2000)); + ASSERT_TRUE(a.can_prune_tail()); + a.prune_tail(); + ASSERT_FALSE(a.can_prune_tail()); + ASSERT_EQ(2u, a.get_extents().size()); + ASSERT_EQ(0x4000u, a.get_logical_length()); + + a.allocated_test( + bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x2000)); + a.init_csum(Checksummer::CSUM_CRC32C_8, 12, 0x6000); + ASSERT_EQ(6u, a.csum_data.length()); + ASSERT_TRUE(a.can_prune_tail()); + a.prune_tail(); + ASSERT_FALSE(a.can_prune_tail()); + ASSERT_EQ(2u, a.get_extents().size()); + ASSERT_EQ(0x4000u, a.get_logical_length()); + ASSERT_EQ(4u, a.csum_data.length()); + + bluestore_blob_t b; + b.allocated_test( + bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x2000)); + ASSERT_FALSE(a.can_prune_tail()); +} + +TEST(Blob, split) +{ + BlueStore store(g_ceph_context, "", 4096); + BlueStore::Cache *cache = BlueStore::Cache::create( + g_ceph_context, "lru", NULL); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, cache, coll_t())); + { + BlueStore::Blob L, R; + L.shared_blob = new BlueStore::SharedBlob(coll.get()); + L.shared_blob->get(); // hack to avoid dtor from running + R.shared_blob = new BlueStore::SharedBlob(coll.get()); + R.shared_blob->get(); // hack to avoid dtor from running + L.dirty_blob().allocated_test(bluestore_pextent_t(0x2000, 0x2000)); + L.dirty_blob().init_csum(Checksummer::CSUM_CRC32C, 12, 0x2000); + L.get_ref(coll.get(), 0, 0x2000); + L.split(coll.get(), 0x1000, &R); + ASSERT_EQ(0x1000u, L.get_blob().get_logical_length()); + ASSERT_EQ(4u, L.get_blob().csum_data.length()); + ASSERT_EQ(1u, L.get_blob().get_extents().size()); + ASSERT_EQ(0x2000u, L.get_blob().get_extents().front().offset); + ASSERT_EQ(0x1000u, L.get_blob().get_extents().front().length); + ASSERT_EQ(0x1000u, L.get_referenced_bytes()); + ASSERT_EQ(0x1000u, R.get_blob().get_logical_length()); + ASSERT_EQ(4u, R.get_blob().csum_data.length()); + ASSERT_EQ(1u, R.get_blob().get_extents().size()); + ASSERT_EQ(0x3000u, R.get_blob().get_extents().front().offset); + ASSERT_EQ(0x1000u, R.get_blob().get_extents().front().length); + ASSERT_EQ(0x1000u, R.get_referenced_bytes()); + } + { + BlueStore::Blob L, R; + L.shared_blob = new BlueStore::SharedBlob(coll.get()); + L.shared_blob->get(); // hack to avoid dtor from running + R.shared_blob = new BlueStore::SharedBlob(coll.get()); + R.shared_blob->get(); // hack to avoid dtor from running + L.dirty_blob().allocated_test(bluestore_pextent_t(0x2000, 0x1000)); + L.dirty_blob().allocated_test(bluestore_pextent_t(0x12000, 0x1000)); + L.dirty_blob().init_csum(Checksummer::CSUM_CRC32C, 12, 0x2000); + L.get_ref(coll.get(), 0, 0x1000); + L.get_ref(coll.get(), 0x1000, 0x1000); + L.split(coll.get(), 0x1000, &R); + ASSERT_EQ(0x1000u, L.get_blob().get_logical_length()); + ASSERT_EQ(4u, L.get_blob().csum_data.length()); + ASSERT_EQ(1u, L.get_blob().get_extents().size()); + ASSERT_EQ(0x2000u, L.get_blob().get_extents().front().offset); + ASSERT_EQ(0x1000u, L.get_blob().get_extents().front().length); + ASSERT_EQ(0x1000u, L.get_referenced_bytes()); + ASSERT_EQ(0x1000u, R.get_blob().get_logical_length()); + ASSERT_EQ(4u, R.get_blob().csum_data.length()); + ASSERT_EQ(1u, R.get_blob().get_extents().size()); + ASSERT_EQ(0x12000u, R.get_blob().get_extents().front().offset); + ASSERT_EQ(0x1000u, R.get_blob().get_extents().front().length); + ASSERT_EQ(0x1000u, R.get_referenced_bytes()); + } +} + +TEST(Blob, legacy_decode) +{ + BlueStore store(g_ceph_context, "", 4096); + BlueStore::Cache *cache = BlueStore::Cache::create( + g_ceph_context, "lru", NULL); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, cache, coll_t())); + bufferlist bl, bl2; + { + BlueStore::Blob B; + + B.shared_blob = new BlueStore::SharedBlob(coll.get()); + B.dirty_blob().allocated_test(bluestore_pextent_t(0x1, 0x2000)); + B.dirty_blob().init_csum(Checksummer::CSUM_CRC32C, 12, 0x2000); + B.get_ref(coll.get(), 0, 0xff0); + B.get_ref(coll.get(), 0x1fff, 1); + + bluestore_extent_ref_map_t fake_ref_map; + fake_ref_map.get(0, 0xff0); + fake_ref_map.get(0x1fff, 1); + + size_t bound = 0, bound2 = 0; + + B.bound_encode( + bound, + 1, /*struct_v*/ + 0, /*sbid*/ + false); + fake_ref_map.bound_encode(bound); + + B.bound_encode( + bound2, + 2, /*struct_v*/ + 0, /*sbid*/ + true); + + { + auto app = bl.get_contiguous_appender(bound); + auto app2 = bl2.get_contiguous_appender(bound2); + B.encode( + app, + 1, /*struct_v*/ + 0, /*sbid*/ + false); + fake_ref_map.encode(app); + + B.encode( + app2, + 2, /*struct_v*/ + 0, /*sbid*/ + true); + } + + auto p = bl.front().begin_deep(); + auto p2 = bl2.front().begin_deep(); + BlueStore::Blob Bres, Bres2; + Bres.shared_blob = new BlueStore::SharedBlob(coll.get()); + Bres2.shared_blob = new BlueStore::SharedBlob(coll.get()); + + uint64_t sbid, sbid2; + Bres.decode( + coll.get(), + p, + 1, /*struct_v*/ + &sbid, + true); + Bres2.decode( + coll.get(), + p2, + 2, /*struct_v*/ + &sbid2, + true); + + ASSERT_EQ(0xff0u + 1u, Bres.get_blob_use_tracker().get_referenced_bytes()); + ASSERT_EQ(0xff0u + 1u, Bres2.get_blob_use_tracker().get_referenced_bytes()); + ASSERT_TRUE(Bres.get_blob_use_tracker().equal(Bres2.get_blob_use_tracker())); + } +} + +TEST(ExtentMap, seek_lextent) +{ + BlueStore store(g_ceph_context, "", 4096); + BlueStore::LRUCache cache(g_ceph_context); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, &cache, coll_t())); + BlueStore::Onode onode(coll.get(), ghobject_t(), ""); + BlueStore::ExtentMap em(&onode); + BlueStore::BlobRef br(new BlueStore::Blob); + br->shared_blob = new BlueStore::SharedBlob(coll.get()); + + ASSERT_EQ(em.extent_map.end(), em.seek_lextent(0)); + ASSERT_EQ(em.extent_map.end(), em.seek_lextent(100)); + + em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, br)); + auto a = em.find(100); + ASSERT_EQ(a, em.seek_lextent(0)); + ASSERT_EQ(a, em.seek_lextent(99)); + ASSERT_EQ(a, em.seek_lextent(100)); + ASSERT_EQ(a, em.seek_lextent(101)); + ASSERT_EQ(a, em.seek_lextent(199)); + ASSERT_EQ(em.extent_map.end(), em.seek_lextent(200)); + + em.extent_map.insert(*new BlueStore::Extent(200, 0, 100, br)); + auto b = em.find(200); + ASSERT_EQ(a, em.seek_lextent(0)); + ASSERT_EQ(a, em.seek_lextent(99)); + ASSERT_EQ(a, em.seek_lextent(100)); + ASSERT_EQ(a, em.seek_lextent(101)); + ASSERT_EQ(a, em.seek_lextent(199)); + ASSERT_EQ(b, em.seek_lextent(200)); + ASSERT_EQ(b, em.seek_lextent(299)); + ASSERT_EQ(em.extent_map.end(), em.seek_lextent(300)); + + em.extent_map.insert(*new BlueStore::Extent(400, 0, 100, br)); + auto d = em.find(400); + ASSERT_EQ(a, em.seek_lextent(0)); + ASSERT_EQ(a, em.seek_lextent(99)); + ASSERT_EQ(a, em.seek_lextent(100)); + ASSERT_EQ(a, em.seek_lextent(101)); + ASSERT_EQ(a, em.seek_lextent(199)); + ASSERT_EQ(b, em.seek_lextent(200)); + ASSERT_EQ(b, em.seek_lextent(299)); + ASSERT_EQ(d, em.seek_lextent(300)); + ASSERT_EQ(d, em.seek_lextent(399)); + ASSERT_EQ(d, em.seek_lextent(400)); + ASSERT_EQ(d, em.seek_lextent(499)); + ASSERT_EQ(em.extent_map.end(), em.seek_lextent(500)); +} + +TEST(ExtentMap, has_any_lextents) +{ + BlueStore store(g_ceph_context, "", 4096); + BlueStore::LRUCache cache(g_ceph_context); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, &cache, coll_t())); + BlueStore::Onode onode(coll.get(), ghobject_t(), ""); + BlueStore::ExtentMap em(&onode); + BlueStore::BlobRef b(new BlueStore::Blob); + b->shared_blob = new BlueStore::SharedBlob(coll.get()); + + ASSERT_FALSE(em.has_any_lextents(0, 0)); + ASSERT_FALSE(em.has_any_lextents(0, 1000)); + ASSERT_FALSE(em.has_any_lextents(1000, 1000)); + + em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, b)); + ASSERT_FALSE(em.has_any_lextents(0, 50)); + ASSERT_FALSE(em.has_any_lextents(0, 100)); + ASSERT_FALSE(em.has_any_lextents(50, 50)); + ASSERT_TRUE(em.has_any_lextents(50, 51)); + ASSERT_TRUE(em.has_any_lextents(50, 100051)); + ASSERT_TRUE(em.has_any_lextents(100, 100)); + ASSERT_TRUE(em.has_any_lextents(100, 1)); + ASSERT_TRUE(em.has_any_lextents(199, 1)); + ASSERT_TRUE(em.has_any_lextents(199, 2)); + ASSERT_FALSE(em.has_any_lextents(200, 2)); + + em.extent_map.insert(*new BlueStore::Extent(200, 0, 100, b)); + ASSERT_TRUE(em.has_any_lextents(199, 1)); + ASSERT_TRUE(em.has_any_lextents(199, 2)); + ASSERT_TRUE(em.has_any_lextents(200, 2)); + ASSERT_TRUE(em.has_any_lextents(200, 200)); + ASSERT_TRUE(em.has_any_lextents(299, 1)); + ASSERT_FALSE(em.has_any_lextents(300, 1)); + + em.extent_map.insert(*new BlueStore::Extent(400, 0, 100, b)); + ASSERT_TRUE(em.has_any_lextents(0, 10000)); + ASSERT_TRUE(em.has_any_lextents(199, 1)); + ASSERT_FALSE(em.has_any_lextents(300, 1)); + ASSERT_FALSE(em.has_any_lextents(300, 100)); + ASSERT_FALSE(em.has_any_lextents(399, 1)); + ASSERT_TRUE(em.has_any_lextents(400, 1)); + ASSERT_TRUE(em.has_any_lextents(400, 100)); + ASSERT_TRUE(em.has_any_lextents(400, 1000)); + ASSERT_TRUE(em.has_any_lextents(499, 1000)); + ASSERT_FALSE(em.has_any_lextents(500, 1000)); +} + +TEST(ExtentMap, compress_extent_map) +{ + BlueStore store(g_ceph_context, "", 4096); + BlueStore::LRUCache cache(g_ceph_context); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, &cache, coll_t())); + BlueStore::Onode onode(coll.get(), ghobject_t(), ""); + BlueStore::ExtentMap em(&onode); + BlueStore::BlobRef b1(new BlueStore::Blob); + BlueStore::BlobRef b2(new BlueStore::Blob); + BlueStore::BlobRef b3(new BlueStore::Blob); + b1->shared_blob = new BlueStore::SharedBlob(coll.get()); + b2->shared_blob = new BlueStore::SharedBlob(coll.get()); + b3->shared_blob = new BlueStore::SharedBlob(coll.get()); + + em.extent_map.insert(*new BlueStore::Extent(0, 0, 100, b1)); + em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, b2)); + ASSERT_EQ(0, em.compress_extent_map(0, 10000)); + ASSERT_EQ(2u, em.extent_map.size()); + + em.extent_map.insert(*new BlueStore::Extent(200, 100, 100, b2)); + em.extent_map.insert(*new BlueStore::Extent(300, 200, 100, b2)); + ASSERT_EQ(0, em.compress_extent_map(0, 0)); + ASSERT_EQ(0, em.compress_extent_map(100000, 1000)); + ASSERT_EQ(2, em.compress_extent_map(0, 100000)); + ASSERT_EQ(2u, em.extent_map.size()); + + em.extent_map.erase(em.find(100)); + em.extent_map.insert(*new BlueStore::Extent(100, 0, 100, b2)); + em.extent_map.insert(*new BlueStore::Extent(200, 100, 100, b3)); + em.extent_map.insert(*new BlueStore::Extent(300, 200, 100, b2)); + ASSERT_EQ(0, em.compress_extent_map(0, 1)); + ASSERT_EQ(0, em.compress_extent_map(0, 100000)); + ASSERT_EQ(4u, em.extent_map.size()); + + em.extent_map.insert(*new BlueStore::Extent(400, 300, 100, b2)); + em.extent_map.insert(*new BlueStore::Extent(500, 500, 100, b2)); + em.extent_map.insert(*new BlueStore::Extent(600, 600, 100, b2)); + em.extent_map.insert(*new BlueStore::Extent(700, 0, 100, b1)); + em.extent_map.insert(*new BlueStore::Extent(800, 0, 100, b3)); + ASSERT_EQ(0, em.compress_extent_map(0, 99)); + ASSERT_EQ(0, em.compress_extent_map(800, 1000)); + ASSERT_EQ(2, em.compress_extent_map(100, 500)); + ASSERT_EQ(7u, em.extent_map.size()); + em.extent_map.erase(em.find(300)); + em.extent_map.erase(em.find(500)); + em.extent_map.erase(em.find(700)); + em.extent_map.insert(*new BlueStore::Extent(400, 300, 100, b2)); + em.extent_map.insert(*new BlueStore::Extent(500, 400, 100, b2)); + em.extent_map.insert(*new BlueStore::Extent(700, 500, 100, b2)); + ASSERT_EQ(1, em.compress_extent_map(0, 1000)); + ASSERT_EQ(6u, em.extent_map.size()); +} + +TEST(GarbageCollector, BasicTest) +{ + BlueStore::LRUCache cache(g_ceph_context); + BlueStore store(g_ceph_context, "", 4096); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, &cache, coll_t())); + BlueStore::Onode onode(coll.get(), ghobject_t(), ""); + BlueStore::ExtentMap em(&onode); + + BlueStore::old_extent_map_t old_extents; + + + /* + min_alloc_size = 4096 + original disposition + extent1 + -> blob1 + extent2 + -> blob2 + extent3 + -> blob1 + extent4 + -> blob3 + on write(300~100) resulted in + extent1 + -> blob1 + extent2 + -> blob2 + extent3 + -> blob4 + extent4 + -> blob3 + */ + { + BlueStore::GarbageCollector gc(g_ceph_context); + int64_t saving; + BlueStore::BlobRef b1(new BlueStore::Blob); + BlueStore::BlobRef b2(new BlueStore::Blob); + BlueStore::BlobRef b3(new BlueStore::Blob); + BlueStore::BlobRef b4(new BlueStore::Blob); + b1->shared_blob = new BlueStore::SharedBlob(coll.get()); + b2->shared_blob = new BlueStore::SharedBlob(coll.get()); + b3->shared_blob = new BlueStore::SharedBlob(coll.get()); + b4->shared_blob = new BlueStore::SharedBlob(coll.get()); + b1->dirty_blob().set_compressed(0x2000, 0x1000); + b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x1000)); + b2->dirty_blob().allocated_test(bluestore_pextent_t(1, 0x1000)); + b3->dirty_blob().allocated_test(bluestore_pextent_t(2, 0x1000)); + b4->dirty_blob().allocated_test(bluestore_pextent_t(3, 0x1000)); + em.extent_map.insert(*new BlueStore::Extent(100, 100, 10, b1)); + b1->get_ref(coll.get(), 100, 10); + em.extent_map.insert(*new BlueStore::Extent(200, 200, 10, b2)); + b2->get_ref(coll.get(), 200, 10); + em.extent_map.insert(*new BlueStore::Extent(300, 300, 100, b4)); + b4->get_ref(coll.get(), 300, 100); + em.extent_map.insert(*new BlueStore::Extent(4096, 0, 10, b3)); + b3->get_ref(coll.get(), 0, 10); + + old_extents.push_back(*new BlueStore::OldExtent(300, 300, 10, b1)); + + saving = gc.estimate(300, 100, em, old_extents, 4096); + ASSERT_EQ(saving, 1); + auto& to_collect = gc.get_extents_to_collect(); + ASSERT_EQ(to_collect.size(), 1u); + ASSERT_EQ(to_collect[0], AllocExtent(100,10) ); + + em.clear(); + old_extents.clear(); + } + /* + original disposition + min_alloc_size = 0x10000 + extent1 + -> blob1 + Write 0x8000~37000 resulted in the following extent map prior to GC + for the last write_small(0x30000~0xf000): + + extent1 + -> blob1 + extent2 + -> blob2 + extent3 + -> blob3 + extent4 + -> blob4 + extent5 + -> blob1 + */ + { + BlueStore store(g_ceph_context, "", 0x10000); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, &cache, coll_t())); + BlueStore::Onode onode(coll.get(), ghobject_t(), ""); + BlueStore::ExtentMap em(&onode); + + BlueStore::old_extent_map_t old_extents; + BlueStore::GarbageCollector gc(g_ceph_context); + int64_t saving; + BlueStore::BlobRef b1(new BlueStore::Blob); + BlueStore::BlobRef b2(new BlueStore::Blob); + BlueStore::BlobRef b3(new BlueStore::Blob); + BlueStore::BlobRef b4(new BlueStore::Blob); + b1->shared_blob = new BlueStore::SharedBlob(coll.get()); + b2->shared_blob = new BlueStore::SharedBlob(coll.get()); + b3->shared_blob = new BlueStore::SharedBlob(coll.get()); + b4->shared_blob = new BlueStore::SharedBlob(coll.get()); + b1->dirty_blob().set_compressed(0x40000, 0x20000); + b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x20000)); + b2->dirty_blob().allocated_test(bluestore_pextent_t(1, 0x10000)); + b3->dirty_blob().allocated_test(bluestore_pextent_t(2, 0x20000)); + b4->dirty_blob().allocated_test(bluestore_pextent_t(3, 0x10000)); + + em.extent_map.insert(*new BlueStore::Extent(0, 0, 0x8000, b1)); + b1->get_ref(coll.get(), 0, 0x8000); + em.extent_map.insert( + *new BlueStore::Extent(0x8000, 0x8000, 0x8000, b2)); // new extent + b2->get_ref(coll.get(), 0x8000, 0x8000); + em.extent_map.insert( + *new BlueStore::Extent(0x10000, 0, 0x20000, b3)); // new extent + b3->get_ref(coll.get(), 0, 0x20000); + em.extent_map.insert( + *new BlueStore::Extent(0x30000, 0, 0xf000, b4)); // new extent + b4->get_ref(coll.get(), 0, 0xf000); + em.extent_map.insert(*new BlueStore::Extent(0x3f000, 0x3f000, 0x1000, b1)); + b1->get_ref(coll.get(), 0x3f000, 0x1000); + + old_extents.push_back(*new BlueStore::OldExtent(0x8000, 0x8000, 0x8000, b1)); + old_extents.push_back( + *new BlueStore::OldExtent(0x10000, 0x10000, 0x20000, b1)); + old_extents.push_back(*new BlueStore::OldExtent(0x30000, 0x30000, 0xf000, b1)); + + saving = gc.estimate(0x30000, 0xf000, em, old_extents, 0x10000); + ASSERT_EQ(saving, 2); + auto& to_collect = gc.get_extents_to_collect(); + ASSERT_EQ(to_collect.size(), 2u); + ASSERT_TRUE(to_collect[0] == AllocExtent(0x0,0x8000) || + to_collect[1] == AllocExtent(0x0,0x8000)); + ASSERT_TRUE(to_collect[0] == AllocExtent(0x3f000,0x1000) || + to_collect[1] == AllocExtent(0x3f000,0x1000)); + + em.clear(); + old_extents.clear(); + } + /* + original disposition + min_alloc_size = 0x1000 + extent1 + -> blob1 + write 0x3000~4000 resulted in the following extent map + (future feature - suppose we can compress incoming write prior to + GC invocation) + + extent1 + -> blob1 + extent2 + -> blob2 + */ + { + BlueStore::GarbageCollector gc(g_ceph_context); + int64_t saving; + BlueStore::BlobRef b1(new BlueStore::Blob); + BlueStore::BlobRef b2(new BlueStore::Blob); + b1->shared_blob = new BlueStore::SharedBlob(coll.get()); + b2->shared_blob = new BlueStore::SharedBlob(coll.get()); + b1->dirty_blob().set_compressed(0x4000, 0x2000); + b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x2000)); + b2->dirty_blob().set_compressed(0x4000, 0x2000); + b2->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x2000)); + + em.extent_map.insert(*new BlueStore::Extent(0, 0, 0x3000, b1)); + b1->get_ref(coll.get(), 0, 0x3000); + em.extent_map.insert( + *new BlueStore::Extent(0x3000, 0, 0x4000, b2)); // new extent + b2->get_ref(coll.get(), 0, 0x4000); + + old_extents.push_back(*new BlueStore::OldExtent(0x3000, 0x3000, 0x1000, b1)); + + saving = gc.estimate(0x3000, 0x4000, em, old_extents, 0x1000); + ASSERT_EQ(saving, 0); + auto& to_collect = gc.get_extents_to_collect(); + ASSERT_EQ(to_collect.size(), 0u); + em.clear(); + old_extents.clear(); + } + /* + original disposition + min_alloc_size = 0x10000 + extent0 + -> blob0 + extent1 + -> blob1 + write 0x8000~37000 resulted in the following extent map prior + to GC for the last write_small(0x30000~0xf000) + + extent0 + -> blob0 + extent2 + -> blob2 + extent3 + -> blob3 + extent4 + -> blob4 + extent5 + -> blob1 + */ + { + BlueStore store(g_ceph_context, "", 0x10000); + BlueStore::CollectionRef coll(new BlueStore::Collection(&store, &cache, coll_t())); + BlueStore::Onode onode(coll.get(), ghobject_t(), ""); + BlueStore::ExtentMap em(&onode); + + BlueStore::old_extent_map_t old_extents; + BlueStore::GarbageCollector gc(g_ceph_context); + int64_t saving; + BlueStore::BlobRef b0(new BlueStore::Blob); + BlueStore::BlobRef b1(new BlueStore::Blob); + BlueStore::BlobRef b2(new BlueStore::Blob); + BlueStore::BlobRef b3(new BlueStore::Blob); + BlueStore::BlobRef b4(new BlueStore::Blob); + b0->shared_blob = new BlueStore::SharedBlob(coll.get()); + b1->shared_blob = new BlueStore::SharedBlob(coll.get()); + b2->shared_blob = new BlueStore::SharedBlob(coll.get()); + b3->shared_blob = new BlueStore::SharedBlob(coll.get()); + b4->shared_blob = new BlueStore::SharedBlob(coll.get()); + b0->dirty_blob().set_compressed(0x2000, 0x1000); + b0->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x10000)); + b1->dirty_blob().set_compressed(0x20000, 0x10000); + b1->dirty_blob().allocated_test(bluestore_pextent_t(0, 0x10000)); + b2->dirty_blob().allocated_test(bluestore_pextent_t(1, 0x10000)); + b3->dirty_blob().allocated_test(bluestore_pextent_t(2, 0x20000)); + b4->dirty_blob().allocated_test(bluestore_pextent_t(3, 0x1000)); + + em.extent_map.insert(*new BlueStore::Extent(0, 0, 0x8000, b0)); + b0->get_ref(coll.get(), 0, 0x8000); + em.extent_map.insert( + *new BlueStore::Extent(0x8000, 0x8000, 0x8000, b2)); // new extent + b2->get_ref(coll.get(), 0x8000, 0x8000); + em.extent_map.insert( + *new BlueStore::Extent(0x10000, 0, 0x20000, b3)); // new extent + b3->get_ref(coll.get(), 0, 0x20000); + em.extent_map.insert( + *new BlueStore::Extent(0x30000, 0, 0xf000, b4)); // new extent + b4->get_ref(coll.get(), 0, 0xf000); + em.extent_map.insert(*new BlueStore::Extent(0x3f000, 0x1f000, 0x1000, b1)); + b1->get_ref(coll.get(), 0x1f000, 0x1000); + + old_extents.push_back(*new BlueStore::OldExtent(0x8000, 0x8000, 0x8000, b0)); + old_extents.push_back( + *new BlueStore::OldExtent(0x10000, 0x10000, 0x10000, b0)); + old_extents.push_back( + *new BlueStore::OldExtent(0x20000, 0x00000, 0x1f000, b1)); + + saving = gc.estimate(0x30000, 0xf000, em, old_extents, 0x10000); + ASSERT_EQ(saving, 2); + auto& to_collect = gc.get_extents_to_collect(); + ASSERT_EQ(to_collect.size(), 2u); + ASSERT_TRUE(to_collect[0] == AllocExtent(0x0,0x8000) || + to_collect[1] == AllocExtent(0x0,0x8000)); + ASSERT_TRUE(to_collect[0] == AllocExtent(0x3f000,0x1000) || + to_collect[1] == AllocExtent(0x3f000,0x1000)); + + em.clear(); + old_extents.clear(); + } + } + +int main(int argc, char **argv) { + vector args; + argv_to_vec(argc, (const char **)argv, args); + env_to_vec(args); + auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, + CODE_ENVIRONMENT_UTILITY, 0); + common_init_finish(g_ceph_context); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}