From: Igor Fedotov Date: Wed, 8 Jun 2016 13:38:41 +0000 (+0300) Subject: os/bluestore: Adds an option to limit max allocation size. Useful primarily for testing X-Git-Tag: v11.0.0~164^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=02a5a2d14127eba4539ce2d6525c630cfbf294f4;p=ceph.git os/bluestore: Adds an option to limit max allocation size. Useful primarily for testing Signed-off-by: Igor Fedotov --- diff --git a/src/common/config_opts.h b/src/common/config_opts.h index fe8353d84665..194f92c0548c 100644 --- a/src/common/config_opts.h +++ b/src/common/config_opts.h @@ -957,6 +957,7 @@ OPTION(bluestore_max_csum_block, OPT_U32, 64*1024) OPTION(bluestore_min_alloc_size, OPT_U32, 0) OPTION(bluestore_min_alloc_size_hdd, OPT_U32, 64*1024) OPTION(bluestore_min_alloc_size_ssd, OPT_U32, 4*1024) +OPTION(bluestore_max_alloc_size, OPT_U32, 0) OPTION(bluestore_compression, OPT_STR, "none") // force|aggressive|passive|none OPTION(bluestore_compression_algorithm, OPT_STR, "snappy") OPTION(bluestore_compression_min_blob_size, OPT_U32, 256*1024) diff --git a/src/os/bluestore/BlueStore.cc b/src/os/bluestore/BlueStore.cc index 149635124c1a..dbd3fedb4d88 100644 --- a/src/os/bluestore/BlueStore.cc +++ b/src/os/bluestore/BlueStore.cc @@ -1008,6 +1008,7 @@ BlueStore::BlueStore(CephContext *cct, const string& path) logger(NULL), csum_type(bluestore_blob_t::CSUM_CRC32C), min_alloc_size(0), + max_alloc_size(0), sync_wal_apply(cct->_conf->bluestore_sync_wal_apply) { _init_logger(); @@ -1273,8 +1274,9 @@ int BlueStore::_check_or_set_bdev_label( return 0; } -void BlueStore::_set_min_alloc(void) +void BlueStore::_set_alloc_sizes(void) { + max_alloc_size = g_conf->bluestore_max_alloc_size; /* * Set device block size according to its media */ @@ -1290,6 +1292,7 @@ void BlueStore::_set_min_alloc(void) } dout(10) << __func__ << " min_alloc_size 0x" << std::hex << min_alloc_size + << " max_alloc_size 0x" << max_alloc_size << std::dec << dendl; } @@ -1317,7 +1320,8 @@ int BlueStore::_open_bdev(bool create) ++block_size_order; } - _set_min_alloc(); + _set_alloc_sizes(); + max_alloc_size = g_conf->bluestore_max_alloc_size; return 0; fail_close: @@ -5927,7 +5931,8 @@ int BlueStore::_do_alloc_write( while (final_length > 0) { bluestore_pextent_t e; uint32_t l; - int r = alloc->allocate(final_length, min_alloc_size, hint, + uint64_t want = max_alloc_size ? MIN(final_length, max_alloc_size) : final_length; + int r = alloc->allocate(want, min_alloc_size, hint, &e.offset, &l); assert(r == 0); need -= l; diff --git a/src/os/bluestore/BlueStore.h b/src/os/bluestore/BlueStore.h index d2bcfe046c1f..c4ade3d69fc5 100644 --- a/src/os/bluestore/BlueStore.h +++ b/src/os/bluestore/BlueStore.h @@ -908,6 +908,8 @@ private: uint64_t min_alloc_size; ///< minimum allocation unit (power of 2) + uint64_t max_alloc_size; ///< maximum allocation unit (power of 2) + bool sync_wal_apply; ///< see config option bluestore_sync_wal_apply // compression options @@ -944,7 +946,7 @@ private: int _read_fsid(uuid_d *f); int _write_fsid(); void _close_fsid(); - void _set_min_alloc(); + void _set_alloc_sizes(); int _open_bdev(bool create); void _close_bdev(); int _open_db(bool create); diff --git a/src/test/objectstore/store_test.cc b/src/test/objectstore/store_test.cc index c0220d22db1c..a32bd9df1f02 100644 --- a/src/test/objectstore/store_test.cc +++ b/src/test/objectstore/store_test.cc @@ -1295,6 +1295,203 @@ TEST_P(StoreTest, BluestoreStatFSTest) { g_ceph_context->_conf->apply_changes(NULL); } +TEST_P(StoreTest, BluestoreFragmentedBlobTest) { + if(string(GetParam()) != "bluestore") + return; + + ObjectStore::Sequencer osr("test"); + int r; + coll_t cid; + ghobject_t hoid(hobject_t(sobject_t("Object 1", CEPH_NOSNAP))); + { + ObjectStore::Transaction t; + t.create_collection(cid, 0); + cerr << "Creating collection " << cid << std::endl; + r = apply_transaction(store, &osr, std::move(t)); + ASSERT_EQ(r, 0); + } + { + bool exists = store->exists(cid, hoid); + ASSERT_TRUE(!exists); + + ObjectStore::Transaction t; + t.touch(cid, hoid); + cerr << "Creating object " << hoid << std::endl; + r = apply_transaction(store, &osr, std::move(t)); + ASSERT_EQ(r, 0); + + exists = store->exists(cid, hoid); + ASSERT_EQ(true, exists); + } + { + struct store_statfs_t statfs; + int r = store->statfs(&statfs); + ASSERT_EQ(r, 0); + ASSERT_EQ( 0u, statfs.allocated); + ASSERT_EQ( 0u, statfs.stored); + ASSERT_EQ(0x1000u, statfs.bsize); + ASSERT_EQ(g_conf->bluestore_block_size / 0x1000, statfs.blocks); + ASSERT_TRUE(statfs.available > 0u && statfs.available < g_conf->bluestore_block_size); + } + std::string data; + data.resize(0x10000 * 3); + { + ObjectStore::Transaction t; + for(size_t i = 0;i < data.size(); i++) + data[i] = i / 256 + 1; + bufferlist bl, newdata; + bl.append(data); + t.write(cid, hoid, 0, bl.length(), bl); + t.zero(cid, hoid, 0x10000, 0x10000); + cerr << "Append 3*0x10000 bytes and punch a hole 0x10000~10000" << std::endl; + r = apply_transaction(store, &osr, std::move(t)); + ASSERT_EQ(r, 0); + + struct store_statfs_t statfs; + int r = store->statfs(&statfs); + ASSERT_EQ(r, 0); + ASSERT_EQ(0x20000, statfs.stored); + ASSERT_EQ(0x20000, statfs.allocated); + + r = store->read(cid, hoid, 0, data.size(), newdata); + ASSERT_EQ(r, (int)data.size()); + { + bufferlist expected; + expected.append(data.substr(0, 0x10000)); + expected.append(string(0x10000, 0)); + expected.append(data.substr(0x20000, 0x10000)); + ASSERT_TRUE(newdata.contents_equal(expected)); + } + newdata.clear(); + + r = store->read(cid, hoid, 1, data.size()-2, newdata); + ASSERT_EQ(r, (int)data.size()-2); + { + bufferlist expected; + expected.append(data.substr(1, 0x10000-1)); + expected.append(string(0x10000, 0)); + expected.append(data.substr(0x20000, 0x10000 - 1)); + ASSERT_TRUE(newdata.contents_equal(expected)); + } + newdata.clear(); + } + //force fsck + EXPECT_EQ(store->umount(), 0); + EXPECT_EQ(store->mount(), 0); + + { + ObjectStore::Transaction t; + std::string data2(3, 'b'); + bufferlist bl, newdata; + bl.append(data2); + t.write(cid, hoid, 0x20000, bl.length(), bl); + cerr << "Write 3 bytes after the hole" << std::endl; + r = apply_transaction(store, &osr, std::move(t)); + ASSERT_EQ(r, 0); + + struct store_statfs_t statfs; + int r = store->statfs(&statfs); + ASSERT_EQ(r, 0); + ASSERT_EQ(0x20000, statfs.allocated); + ASSERT_EQ(0x20000, statfs.stored); + + r = store->read(cid, hoid, 0x20000-1, 21, newdata); + ASSERT_EQ(r, (int)21); + { + bufferlist expected; + expected.append(string(0x1, 0)); + expected.append(string(data2)); + expected.append(data.substr(0x20003, 21-4)); + ASSERT_TRUE(newdata.contents_equal(expected)); + } + newdata.clear(); + } + //force fsck + EXPECT_EQ(store->umount(), 0); + EXPECT_EQ(store->mount(), 0); + + { + ObjectStore::Transaction t; + std::string data2(3, 'a'); + bufferlist bl, newdata; + bl.append(data2); + t.write(cid, hoid, 0x10000+1, bl.length(), bl); + cerr << "Write 3 bytes to the hole" << std::endl; + r = apply_transaction(store, &osr, std::move(t)); + ASSERT_EQ(r, 0); + + struct store_statfs_t statfs; + int r = store->statfs(&statfs); + ASSERT_EQ(r, 0); + ASSERT_EQ(0x30000, statfs.allocated); + ASSERT_EQ(0x20003, statfs.stored); + + r = store->read(cid, hoid, 0x10000-1, 0x10000+22, newdata); + ASSERT_EQ(r, (int)0x10000+22); + { + bufferlist expected; + expected.append(data.substr(0x10000-1, 1)); + expected.append(string(0x1, 0)); + expected.append(data2); + expected.append(string(0x10000-4, 0)); + expected.append(string(0x3, 'b')); + expected.append(data.substr(0x20004, 21-3)); + ASSERT_TRUE(newdata.contents_equal(expected)); + } + newdata.clear(); + } + { + ObjectStore::Transaction t; + bufferlist bl, newdata; + bl.append(string(0x30000, 'c')); + t.write(cid, hoid, 0, 0x30000, bl); + t.zero(cid, hoid, 0, 0x10000); + t.zero(cid, hoid, 0x20000, 0x10000); + cerr << "Rewrite an object and create two holes at the begining and the end" << std::endl; + r = apply_transaction(store, &osr, std::move(t)); + ASSERT_EQ(r, 0); + + struct store_statfs_t statfs; + int r = store->statfs(&statfs); + ASSERT_EQ(r, 0); + ASSERT_EQ(0x10000, statfs.allocated); + ASSERT_EQ(0x10000, statfs.stored); + + r = store->read(cid, hoid, 0, 0x30000, newdata); + ASSERT_EQ(r, (int)0x30000); + { + bufferlist expected; + expected.append(string(0x10000, 0)); + expected.append(string(0x10000, 'c')); + expected.append(string(0x10000, 0)); + ASSERT_TRUE(newdata.contents_equal(expected)); + } + newdata.clear(); + } + + //force fsck + EXPECT_EQ(store->umount(), 0); + EXPECT_EQ(store->mount(), 0); + + { + ObjectStore::Transaction t; + t.remove(cid, hoid); + t.remove_collection(cid); + cerr << "Cleaning" << std::endl; + r = apply_transaction(store, &osr, std::move(t)); + ASSERT_EQ(r, 0); + + struct store_statfs_t statfs; + r = store->statfs(&statfs); + ASSERT_EQ(r, 0); + ASSERT_EQ( 0u, statfs.allocated); + ASSERT_EQ( 0u, statfs.stored); + ASSERT_EQ( 0u, statfs.compressed_original); + ASSERT_EQ( 0u, statfs.compressed); + ASSERT_EQ( 0u, statfs.compressed_allocated); + } +} + TEST_P(StoreTest, ManySmallWrite) { ObjectStore::Sequencer osr("test"); int r; @@ -4522,6 +4719,7 @@ int main(int argc, char **argv) { g_ceph_context->_conf->set_val("bluestore_debug_small_allocations", "4"); g_ceph_context->_conf->set_val("bluestore_debug_freelist", "true"); g_ceph_context->_conf->set_val("bluestore_clone_cow", "true"); + g_ceph_context->_conf->set_val("bluestore_max_alloc_size", "196608"); g_ceph_context->_conf->set_val( "enable_experimental_unrecoverable_data_corrupting_features", "*"); g_ceph_context->_conf->apply_changes(NULL);