From: Igor Fedotov Date: Tue, 28 Jun 2016 14:48:21 +0000 (+0300) Subject: os/bluestore: introduce set_lextent/deref_lextent methods to bluestore_onode_t inter... X-Git-Tag: ses5-milestone5~268^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=dd5efb11857139c2ffc736d0dbf64ede48790cea;p=ceph.git os/bluestore: introduce set_lextent/deref_lextent methods to bluestore_onode_t interface to handle reference counting for both ref_map/no ref_map cases in a single place Signed-off-by: Igor Fedotov --- diff --git a/src/os/bluestore/bluestore_types.cc b/src/os/bluestore/bluestore_types.cc index 33de6d98dffa..efad0a24c8c9 100644 --- a/src/os/bluestore/bluestore_types.cc +++ b/src/os/bluestore/bluestore_types.cc @@ -990,6 +990,50 @@ void bluestore_onode_t::punch_hole( } } +void bluestore_onode_t::set_lextent(uint64_t offset, + const bluestore_lextent_t& lext, + bluestore_blob_t* b, + vector *deref) +{ + punch_hole(offset, lext.length, deref); + extent_map[offset] = lext; + //increment reference for shared blobs only + if (b->has_refmap()) { + b->get_ref(lext.offset, lext.length); + } +} + +bool bluestore_onode_t::deref_lextent( uint64_t offset, + bluestore_lextent_t& lext, + bluestore_blob_t* b, + uint64_t min_alloc_size, + vector* r) +{ + bool empty = false; + if (b->has_refmap()) { + empty = b->put_ref(lext.offset, lext.length, min_alloc_size, r); + } else { + bluestore_extent_ref_map_t temp_ref_map; + assert(offset >= lext.offset); + //determine the range in lextents map where specific blob can be referenced to. + uint64_t search_offset = offset - lext.offset; + uint64_t search_end = search_offset + + (b->is_compressed() ? + b->get_compressed_payload_original_length() : + b->get_ondisk_length()); + auto lp = seek_lextent(search_offset); + while (lp != extent_map.end() && + lp->first < search_end) { + if (lp->second.blob == lext.blob) { + temp_ref_map.fill(lp->second.offset, lp->second.length); + } + ++lp; + } + temp_ref_map.get(lext.offset, lext.length); //insert a fake reference for the removed lextent + empty = b->put_ref_external( temp_ref_map, lext.offset, lext.length, min_alloc_size, r); + } + return empty; +} // bluestore_wal_op_t void bluestore_wal_op_t::encode(bufferlist& bl) const diff --git a/src/os/bluestore/bluestore_types.h b/src/os/bluestore/bluestore_types.h index 5171ed35de5f..5547936b1fb6 100644 --- a/src/os/bluestore/bluestore_types.h +++ b/src/os/bluestore/bluestore_types.h @@ -632,6 +632,20 @@ struct bluestore_onode_t { void punch_hole(uint64_t offset, uint64_t length, vector *deref); + /// put new lextent into lextent_map overwriting existing ones if any and update references accordingly + void set_lextent(uint64_t offset, + const bluestore_lextent_t& lext, + bluestore_blob_t* b, + vector *deref); + + /// post process removed lextent to take care of blob references + /// returns true is underlying blob has to be released + bool deref_lextent(uint64_t offset, + bluestore_lextent_t& lext, + bluestore_blob_t* b, + uint64_t min_alloc_size, + vector* r); + void encode(bufferlist& bl) const; void decode(bufferlist::iterator& p); void dump(Formatter *f) const; diff --git a/src/test/objectstore/test_bluestore_types.cc b/src/test/objectstore/test_bluestore_types.cc index 823986a00434..1fbff0e515ec 100644 --- a/src/test/objectstore/test_bluestore_types.cc +++ b/src/test/objectstore/test_bluestore_types.cc @@ -854,3 +854,197 @@ TEST(bluestore_onode_t, punch_hole) ASSERT_EQ(30u, r[2].length); r.clear(); } + +TEST(bluestore_onode_t, insert_remove_lextent) +{ + bluestore_onode_t on; + vector r; + vector rp; + + bluestore_pextent_t pext1(1, 0x10000); + bluestore_pextent_t pext2(2, 0x10000); + bluestore_pextent_t pext3(3, 0x10000); + + bluestore_blob_t blob(bluestore_blob_t::FLAG_HAS_REFMAP); + blob.extents.push_back(pext1); + + bluestore_blob_t blob2, blob3; + blob2.clear_flag(bluestore_blob_t::FLAG_HAS_REFMAP); + blob2.extents.push_back(pext2); + blob3.clear_flag(bluestore_blob_t::FLAG_HAS_REFMAP); + blob3.extents.push_back(pext3); + + bool empty; + + bluestore_lextent_t lextent(1, 0, 100); + on.set_lextent(0, lextent, &blob, &r); + + ASSERT_EQ(1u, on.extent_map.size()); + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(blob.ref_map.contains(0,100)); + r.clear(); + + lextent.blob = 2; + lextent.offset = 1; + lextent.length = 99; + on.set_lextent(101, lextent, &blob2, &r); + + ASSERT_EQ(2u, on.extent_map.size()); + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(blob.ref_map.contains(0,100)); + ASSERT_TRUE(blob2.ref_map.empty()); + r.clear(); + + //overwrite lextent/blob that doesn't have REF_MAP + lextent.blob = 3; + lextent.offset = 1; + lextent.length = 99; + on.set_lextent(101, lextent, &blob3, &r); + + ASSERT_EQ(2u, on.extent_map.size()); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(2, r[0].blob); + ASSERT_EQ(1u, r[0].offset); + ASSERT_EQ(99u, r[0].length); + ASSERT_TRUE(blob.ref_map.contains(0,100)); + ASSERT_TRUE(blob2.ref_map.empty()); + ASSERT_TRUE(blob3.ref_map.empty()); + + //deref overwritten lextent + empty = on.deref_lextent(100, r[0], &blob2, 0x10000, &rp); + ASSERT_TRUE(empty); + ASSERT_EQ(1u, rp.size()); + ASSERT_TRUE(pext2.offset == rp[0].offset); + ASSERT_TRUE(pext2.length == rp[0].length); + + r.clear(); + rp.clear(); + + //overwrite lextent/blob that has a REF_MAP with one that doesn't + lextent.blob = 2; + lextent.offset = 0; + lextent.length = 100; + blob2.extents.clear(); //for sure + blob2.extents.push_back(pext2); + on.set_lextent(0, lextent, &blob2, &r); + + ASSERT_EQ(2u, on.extent_map.size()); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(1, r[0].blob); + ASSERT_EQ(0u, r[0].offset); + ASSERT_EQ(100u, r[0].length); + ASSERT_TRUE(blob.ref_map.contains(0,100)); + ASSERT_TRUE(blob2.ref_map.empty()); + ASSERT_TRUE(blob3.ref_map.empty()); + + //deref overwritten lextent + empty = on.deref_lextent(0, r[0], &blob, 0x10000, &rp); + ASSERT_TRUE(empty); + ASSERT_EQ(1u, rp.size()); + ASSERT_TRUE(pext1.offset == rp[0].offset); + ASSERT_TRUE(pext1.length == rp[0].length); + ASSERT_TRUE(blob.ref_map.empty()); + + r.clear(); + rp.clear(); + + //append an lextent pointing to already present blob 3 + lextent.blob = 3; + lextent.offset = 200; + lextent.length = 50; + on.set_lextent(300, lextent, &blob3, &r); + + ASSERT_EQ(3u, on.extent_map.size()); + ASSERT_EQ(0u, r.size()); + ASSERT_TRUE(blob3.ref_map.empty()); + + //deref lextent with underlying blob having multiple references (no ref_map case) + on.punch_hole(100, 100, &r); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(3, r[0].blob); + ASSERT_EQ(1u, r[0].offset); + ASSERT_EQ(99u, r[0].length); + + empty = on.deref_lextent(100, r[0], &blob3, 0x10000, &rp); + ASSERT_FALSE(empty); + ASSERT_EQ(0u, rp.size()); + + r.clear(); + rp.clear(); + + //deref lextent with underlying blob having single reference (no ref_map case) + on.punch_hole(300, 100, &r); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(3, r[0].blob); + ASSERT_EQ(200u, r[0].offset); + ASSERT_EQ(50u, r[0].length); + + empty = on.deref_lextent(300, r[0], &blob3, 0x10000, &rp); + ASSERT_TRUE(empty); + ASSERT_EQ(1u, rp.size()); + ASSERT_TRUE(pext3.offset == rp[0].offset); + ASSERT_TRUE(pext3.length == rp[0].length); + ASSERT_TRUE(blob3.ref_map.empty()); + + r.clear(); + rp.clear(); + + //deref lextent partially (no ref_map case) + on.punch_hole(20, 10, &r); + ASSERT_EQ(2, r[0].blob); + ASSERT_EQ(20u, r[0].offset); + ASSERT_EQ(10u, r[0].length); + + empty = on.deref_lextent(20, r[0], &blob2, 0x10000, &rp); + ASSERT_FALSE(empty); + ASSERT_EQ(0u, rp.size()); + + r.clear(); + rp.clear(); + + //deref lextent partially once again(no ref_map case) + on.punch_hole(70, 10, &r); + ASSERT_EQ(1u, r.size()); + ASSERT_EQ(2, r[0].blob); + ASSERT_EQ(70u, r[0].offset); + ASSERT_EQ(10u, r[0].length); + + empty = on.deref_lextent(70, r[0], &blob2, 0x10000, &rp); + ASSERT_FALSE(empty); + ASSERT_EQ(0u, rp.size()); + + r.clear(); + rp.clear(); + + //deref fragmented lextent totally (no ref_map case) + on.punch_hole(0, 100, &r); + ASSERT_EQ(3u, r.size()); + ASSERT_EQ(2, r[0].blob); + ASSERT_EQ(0u, r[0].offset); + ASSERT_EQ(20u, r[0].length); + ASSERT_EQ(2, r[1].blob); + ASSERT_EQ(30u, r[1].offset); + ASSERT_EQ(40u, r[1].length); + ASSERT_EQ(2, r[2].blob); + ASSERT_EQ(80u, r[2].offset); + ASSERT_EQ(20u, r[2].length); + + empty = on.deref_lextent(0, r[0], &blob2, 0x10000, &rp); + ASSERT_TRUE(empty); + ASSERT_EQ(1u, rp.size()); + ASSERT_TRUE(pext2.offset == rp[0].offset); + ASSERT_TRUE(pext2.length == rp[0].length); + rp.clear(); + + empty = on.deref_lextent(30, r[1], &blob2, 0x10000, &rp); + ASSERT_TRUE(empty); + ASSERT_EQ(0u, rp.size()); //no more pextents for the blob, already deallocated above + rp.clear(); + + empty = on.deref_lextent(80, r[2], &blob2, 0x10000, &rp); + ASSERT_TRUE(empty); + ASSERT_EQ(0u, rp.size()); //no more pextents for the blob, already deallocated above + + r.clear(); + rp.clear(); +}