From 96bd4634a5bc79b5e5c65b5ea5af111e49c09e5c Mon Sep 17 00:00:00 2001 From: Or Ozeri Date: Wed, 22 Jul 2020 09:14:19 +0300 Subject: [PATCH] librbd: support writing with version assertion on object dispatch This commit extends the object dispatch write function to support RADOS object version assertion. In addition, we add a write flag which allows to assert that an object exists before writing. Signed-off-by: Or Ozeri --- .../cache/ObjectCacherObjectDispatch.cc | 24 ++- src/librbd/cache/ObjectCacherObjectDispatch.h | 3 +- src/librbd/cache/ObjectCacherWriteback.cc | 2 +- src/librbd/cache/ParentCacheObjectDispatch.h | 3 +- src/librbd/cache/WriteAroundObjectDispatch.cc | 3 +- src/librbd/cache/WriteAroundObjectDispatch.h | 3 +- src/librbd/crypto/CryptoObjectDispatch.cc | 5 +- src/librbd/crypto/CryptoObjectDispatch.h | 3 +- src/librbd/io/ImageRequest.cc | 8 +- src/librbd/io/ObjectDispatch.cc | 4 +- src/librbd/io/ObjectDispatch.h | 3 +- src/librbd/io/ObjectDispatchInterface.h | 3 +- src/librbd/io/ObjectDispatchSpec.h | 10 +- src/librbd/io/ObjectDispatcher.cc | 3 +- src/librbd/io/ObjectRequest.cc | 9 +- src/librbd/io/ObjectRequest.h | 7 +- .../io/SimpleSchedulerObjectDispatch.cc | 12 +- src/librbd/io/SimpleSchedulerObjectDispatch.h | 3 +- src/librbd/io/Types.h | 4 + src/librbd/journal/ObjectDispatch.cc | 3 +- src/librbd/journal/ObjectDispatch.h | 3 +- src/librbd/operation/FlattenRequest.cc | 4 +- src/librbd/operation/MigrateRequest.cc | 4 +- .../test_mock_ParentCacheObjectDispatch.cc | 2 +- .../test_mock_WriteAroundObjectDispatch.cc | 140 ++++++++------ .../crypto/test_mock_CryptoObjectDispatch.cc | 4 +- src/test/librbd/io/test_mock_ObjectRequest.cc | 182 +++++++++++++++++- ...test_mock_SimpleSchedulerObjectDispatch.cc | 81 ++++---- src/test/librbd/mock/io/MockObjectDispatch.h | 14 +- 29 files changed, 399 insertions(+), 150 deletions(-) diff --git a/src/librbd/cache/ObjectCacherObjectDispatch.cc b/src/librbd/cache/ObjectCacherObjectDispatch.cc index db0c71f186a42..524808a5097a9 100644 --- a/src/librbd/cache/ObjectCacherObjectDispatch.cc +++ b/src/librbd/cache/ObjectCacherObjectDispatch.cc @@ -267,7 +267,8 @@ bool ObjectCacherObjectDispatch::discard( template bool ObjectCacherObjectDispatch::write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) { @@ -279,6 +280,23 @@ bool ObjectCacherObjectDispatch::write( on_dispatched = util::create_async_context_callback(*m_image_ctx, on_dispatched); + // cache layer does not handle version checking + if (assert_version.has_value() || + (write_flags & io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE) != 0) { + ObjectExtents object_extents; + object_extents.emplace_back(data_object_name(m_image_ctx, object_no), + object_no, object_off, data.length(), 0); + + *dispatch_result = io::DISPATCH_RESULT_CONTINUE; + + // ensure any in-flight writeback is complete before advancing + // the write request + std::lock_guard locker{m_cache_lock}; + m_object_cacher->discard_writeback(m_object_set, object_extents, + on_dispatched); + return true; + } + m_image_ctx->image_lock.lock_shared(); ObjectCacher::OSDWrite *wr = m_object_cacher->prepare_write( snapc, data, ceph::real_time::min(), op_flags, *journal_tid); @@ -317,8 +335,8 @@ bool ObjectCacherObjectDispatch::write_same( bufferlist ws_data; io::util::assemble_write_same_extent(extent, data, &ws_data, true); - return write(object_no, object_off, std::move(ws_data), snapc, op_flags, - parent_trace, object_dispatch_flags, journal_tid, + return write(object_no, object_off, std::move(ws_data), snapc, op_flags, 0, + std::nullopt, parent_trace, object_dispatch_flags, journal_tid, dispatch_result, on_finish, on_dispatched); } diff --git a/src/librbd/cache/ObjectCacherObjectDispatch.h b/src/librbd/cache/ObjectCacherObjectDispatch.h index 6e4ab23ae77e6..4f3d96707eccc 100644 --- a/src/librbd/cache/ObjectCacherObjectDispatch.h +++ b/src/librbd/cache/ObjectCacherObjectDispatch.h @@ -58,7 +58,8 @@ public: bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) override; diff --git a/src/librbd/cache/ObjectCacherWriteback.cc b/src/librbd/cache/ObjectCacherWriteback.cc index 529b6c54ad105..cfa358d05ae31 100644 --- a/src/librbd/cache/ObjectCacherWriteback.cc +++ b/src/librbd/cache/ObjectCacherWriteback.cc @@ -197,7 +197,7 @@ ceph_tid_t ObjectCacherWriteback::write(const object_t& oid, auto req = io::ObjectDispatchSpec::create_write( m_ictx, io::OBJECT_DISPATCH_LAYER_CACHE, object_no, off, std::move(bl_copy), - snapc, 0, journal_tid, trace, ctx); + snapc, 0, 0, std::nullopt, journal_tid, trace, ctx); req->object_dispatch_flags = ( io::OBJECT_DISPATCH_FLAG_FLUSH | io::OBJECT_DISPATCH_FLAG_WILL_RETRY_ON_ERROR); diff --git a/src/librbd/cache/ParentCacheObjectDispatch.h b/src/librbd/cache/ParentCacheObjectDispatch.h index 588d284f2fd10..314c9a585def3 100644 --- a/src/librbd/cache/ParentCacheObjectDispatch.h +++ b/src/librbd/cache/ParentCacheObjectDispatch.h @@ -58,7 +58,8 @@ public: bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) { diff --git a/src/librbd/cache/WriteAroundObjectDispatch.cc b/src/librbd/cache/WriteAroundObjectDispatch.cc index bbcf4a65b24f1..d206cc3f6c915 100644 --- a/src/librbd/cache/WriteAroundObjectDispatch.cc +++ b/src/librbd/cache/WriteAroundObjectDispatch.cc @@ -85,7 +85,8 @@ bool WriteAroundObjectDispatch::discard( template bool WriteAroundObjectDispatch::write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context**on_finish, Context* on_dispatched) { diff --git a/src/librbd/cache/WriteAroundObjectDispatch.h b/src/librbd/cache/WriteAroundObjectDispatch.h index bf1e1d985513f..e08cdd4357471 100644 --- a/src/librbd/cache/WriteAroundObjectDispatch.h +++ b/src/librbd/cache/WriteAroundObjectDispatch.h @@ -58,7 +58,8 @@ public: bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context**on_finish, Context* on_dispatched) override; diff --git a/src/librbd/crypto/CryptoObjectDispatch.cc b/src/librbd/crypto/CryptoObjectDispatch.cc index 99ccc49c1a134..12cc7e899ad65 100644 --- a/src/librbd/crypto/CryptoObjectDispatch.cc +++ b/src/librbd/crypto/CryptoObjectDispatch.cc @@ -141,7 +141,8 @@ bool CryptoObjectDispatch::read( template bool CryptoObjectDispatch::write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) { @@ -188,7 +189,7 @@ bool CryptoObjectDispatch::write_same( *dispatch_result = io::DISPATCH_RESULT_COMPLETE; auto req = io::ObjectDispatchSpec::create_write( m_image_ctx, io::OBJECT_DISPATCH_LAYER_NONE, object_no, - object_off, std::move(ws_data), snapc, op_flags, 0, + object_off, std::move(ws_data), snapc, op_flags, 0, std::nullopt, 0, parent_trace, ctx); req->send(); return true; diff --git a/src/librbd/crypto/CryptoObjectDispatch.h b/src/librbd/crypto/CryptoObjectDispatch.h index 1d9edda909be9..6b95256f60df8 100644 --- a/src/librbd/crypto/CryptoObjectDispatch.h +++ b/src/librbd/crypto/CryptoObjectDispatch.h @@ -48,7 +48,8 @@ public: bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) override; diff --git a/src/librbd/io/ImageRequest.cc b/src/librbd/io/ImageRequest.cc index 3e9d1d99fd624..c98d00dd8ae71 100644 --- a/src/librbd/io/ImageRequest.cc +++ b/src/librbd/io/ImageRequest.cc @@ -579,8 +579,8 @@ ObjectDispatchSpec *ImageWriteRequest::create_object_request( auto req = ObjectDispatchSpec::create_write( &image_ctx, OBJECT_DISPATCH_LAYER_NONE, object_extent.object_no, - object_extent.offset, std::move(bl), snapc, m_op_flags, journal_tid, - this->m_trace, on_finish); + object_extent.offset, std::move(bl), snapc, m_op_flags, 0, std::nullopt, + journal_tid, this->m_trace, on_finish); return req; } @@ -811,8 +811,8 @@ ObjectDispatchSpec *ImageWriteSameRequest::create_object_request( } req = ObjectDispatchSpec::create_write( &image_ctx, OBJECT_DISPATCH_LAYER_NONE, object_extent.object_no, - object_extent.offset, std::move(bl), snapc, m_op_flags, journal_tid, - this->m_trace, on_finish); + object_extent.offset, std::move(bl), snapc, m_op_flags, 0, std::nullopt, + journal_tid, this->m_trace, on_finish); return req; } diff --git a/src/librbd/io/ObjectDispatch.cc b/src/librbd/io/ObjectDispatch.cc index c6099316685a8..1823be38222cf 100644 --- a/src/librbd/io/ObjectDispatch.cc +++ b/src/librbd/io/ObjectDispatch.cc @@ -71,7 +71,8 @@ bool ObjectDispatch::discard( template bool ObjectDispatch::write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) { @@ -82,6 +83,7 @@ bool ObjectDispatch::write( *dispatch_result = DISPATCH_RESULT_COMPLETE; auto req = new ObjectWriteRequest(m_image_ctx, object_no, object_off, std::move(data), snapc, op_flags, + write_flags, assert_version, parent_trace, on_dispatched); req->send(); return true; diff --git a/src/librbd/io/ObjectDispatch.h b/src/librbd/io/ObjectDispatch.h index 4a65ec417beef..f06271f26dc57 100644 --- a/src/librbd/io/ObjectDispatch.h +++ b/src/librbd/io/ObjectDispatch.h @@ -50,7 +50,8 @@ public: bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) override; diff --git a/src/librbd/io/ObjectDispatchInterface.h b/src/librbd/io/ObjectDispatchInterface.h index 1ade6adbd5186..5774787ebdeeb 100644 --- a/src/librbd/io/ObjectDispatchInterface.h +++ b/src/librbd/io/ObjectDispatchInterface.h @@ -49,7 +49,8 @@ struct ObjectDispatchInterface { virtual bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, DispatchResult* dispatch_result, Context**on_finish, Context* on_dispatched) = 0; diff --git a/src/librbd/io/ObjectDispatchSpec.h b/src/librbd/io/ObjectDispatchSpec.h index 942b1d719cf86..e4fa32b42e271 100644 --- a/src/librbd/io/ObjectDispatchSpec.h +++ b/src/librbd/io/ObjectDispatchSpec.h @@ -83,12 +83,16 @@ public: struct WriteRequest : public WriteRequestBase { ceph::bufferlist data; + int write_flags; + std::optional assert_version; WriteRequest(uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, const ::SnapContext& snapc, + int write_flags, std::optional assert_version, uint64_t journal_tid) : WriteRequestBase(object_no, object_off, snapc, journal_tid), - data(std::move(data)) { + data(std::move(data)), write_flags(write_flags), + assert_version(assert_version) { } }; @@ -182,12 +186,14 @@ public: static ObjectDispatchSpec* create_write( ImageCtxT* image_ctx, ObjectDispatchLayer object_dispatch_layer, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, uint64_t journal_tid, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, uint64_t journal_tid, const ZTracer::Trace &parent_trace, Context *on_finish) { return new ObjectDispatchSpec(image_ctx->io_object_dispatcher, object_dispatch_layer, WriteRequest{object_no, object_off, std::move(data), snapc, + write_flags, assert_version, journal_tid}, op_flags, parent_trace, on_finish); } diff --git a/src/librbd/io/ObjectDispatcher.cc b/src/librbd/io/ObjectDispatcher.cc index 4732aa68e27c8..6e0ee89673b10 100644 --- a/src/librbd/io/ObjectDispatcher.cc +++ b/src/librbd/io/ObjectDispatcher.cc @@ -129,7 +129,8 @@ struct ObjectDispatcher::SendVisitor : public boost::static_visitor { bool operator()(ObjectDispatchSpec::WriteRequest& write) const { return object_dispatch->write( write.object_no, write.object_off, std::move(write.data), write.snapc, - object_dispatch_spec->op_flags, object_dispatch_spec->parent_trace, + object_dispatch_spec->op_flags, write.write_flags, write.assert_version, + object_dispatch_spec->parent_trace, &object_dispatch_spec->object_dispatch_flags, &write.journal_tid, &object_dispatch_spec->dispatch_result, &object_dispatch_spec->dispatcher_ctx.on_finish, diff --git a/src/librbd/io/ObjectRequest.cc b/src/librbd/io/ObjectRequest.cc index 87879eb8607a0..60b05ea6115fe 100644 --- a/src/librbd/io/ObjectRequest.cc +++ b/src/librbd/io/ObjectRequest.cc @@ -54,10 +54,12 @@ template ObjectRequest* ObjectRequest::create_write( I *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, Context *completion) { return new ObjectWriteRequest(ictx, object_no, object_off, std::move(data), snapc, op_flags, + write_flags, assert_version, parent_trace, completion); } @@ -642,6 +644,11 @@ void AbstractObjectWriteRequest::handle_post_write_object_map_update(int r) { template void ObjectWriteRequest::add_write_ops(neorados::WriteOp* wr) { + if ((m_write_flags & OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE) != 0) { + wr->create(true); + } else if (m_assert_version.has_value()) { + wr->assert_version(m_assert_version.value()); + } if (this->m_full_object) { wr->write_full(bufferlist{m_write_data}); } else { diff --git a/src/librbd/io/ObjectRequest.h b/src/librbd/io/ObjectRequest.h index c34a1f7f3efc2..2750da98cd860 100644 --- a/src/librbd/io/ObjectRequest.h +++ b/src/librbd/io/ObjectRequest.h @@ -38,6 +38,7 @@ public: static ObjectRequest* create_write( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, const ::SnapContext &snapc, int op_flags, + int write_flags, std::optional assert_version, const ZTracer::Trace &parent_trace, Context *completion); static ObjectRequest* create_discard( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, @@ -265,11 +266,13 @@ public: ObjectWriteRequest( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, const ::SnapContext &snapc, int op_flags, + int write_flags, std::optional assert_version, const ZTracer::Trace &parent_trace, Context *completion) : AbstractObjectWriteRequest(ictx, object_no, object_off, data.length(), snapc, "write", parent_trace, completion), - m_write_data(std::move(data)), m_op_flags(op_flags) { + m_write_data(std::move(data)), m_op_flags(op_flags), + m_write_flags(write_flags), m_assert_version(assert_version) { } bool is_empty_write_op() const override { @@ -286,6 +289,8 @@ protected: private: ceph::bufferlist m_write_data; int m_op_flags; + int m_write_flags; + std::optional m_assert_version; }; template diff --git a/src/librbd/io/SimpleSchedulerObjectDispatch.cc b/src/librbd/io/SimpleSchedulerObjectDispatch.cc index 8b83468fe89af..1f50da8c29bf0 100644 --- a/src/librbd/io/SimpleSchedulerObjectDispatch.cc +++ b/src/librbd/io/SimpleSchedulerObjectDispatch.cc @@ -162,7 +162,7 @@ void SimpleSchedulerObjectDispatch::ObjectRequests::dispatch_delayed_requests auto req = ObjectDispatchSpec::create_write( image_ctx, OBJECT_DISPATCH_LAYER_SCHEDULER, m_object_no, offset, std::move(merged_requests.data), m_snapc, - m_op_flags, 0, {}, ctx); + m_op_flags, 0, std::nullopt, 0, {}, ctx); req->object_dispatch_flags = m_object_dispatch_flags; req->send(); @@ -256,7 +256,8 @@ bool SimpleSchedulerObjectDispatch::discard( template bool SimpleSchedulerObjectDispatch::write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) { @@ -264,6 +265,13 @@ bool SimpleSchedulerObjectDispatch::write( ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " " << object_off << "~" << data.length() << dendl; + // don't try to batch assert version writes + if (assert_version.has_value() || + (write_flags & OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE) != 0) { + dispatch_delayed_requests(object_no); + return false; + } + std::lock_guard locker{m_lock}; if (try_delay_write(object_no, object_off, std::move(data), snapc, op_flags, *object_dispatch_flags, on_dispatched)) { diff --git a/src/librbd/io/SimpleSchedulerObjectDispatch.h b/src/librbd/io/SimpleSchedulerObjectDispatch.h index def70af5e906c..9b7d1fbad1c6a 100644 --- a/src/librbd/io/SimpleSchedulerObjectDispatch.h +++ b/src/librbd/io/SimpleSchedulerObjectDispatch.h @@ -66,7 +66,8 @@ public: bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) override; diff --git a/src/librbd/io/Types.h b/src/librbd/io/Types.h index 6a46788d2ed9d..66a7635cd5321 100644 --- a/src/librbd/io/Types.h +++ b/src/librbd/io/Types.h @@ -99,6 +99,10 @@ enum ObjectDispatchLayer { OBJECT_DISPATCH_LAYER_LAST }; +enum { + OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE = 1UL << 0 +}; + enum { OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE = 1UL << 0, OBJECT_DISCARD_FLAG_DISABLE_OBJECT_MAP_UPDATE = 1UL << 1 diff --git a/src/librbd/journal/ObjectDispatch.cc b/src/librbd/journal/ObjectDispatch.cc index 91dae20d76107..be47abbaa6e5b 100644 --- a/src/librbd/journal/ObjectDispatch.cc +++ b/src/librbd/journal/ObjectDispatch.cc @@ -107,7 +107,8 @@ bool ObjectDispatch::discard( template bool ObjectDispatch::write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) { diff --git a/src/librbd/journal/ObjectDispatch.h b/src/librbd/journal/ObjectDispatch.h index cd03389c5c134..f4426be3e28ba 100644 --- a/src/librbd/journal/ObjectDispatch.h +++ b/src/librbd/journal/ObjectDispatch.h @@ -56,7 +56,8 @@ public: bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* object_dispatch_flags, uint64_t* journal_tid, io::DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) override; diff --git a/src/librbd/operation/FlattenRequest.cc b/src/librbd/operation/FlattenRequest.cc index d94e8421083f4..6d94b5da84722 100644 --- a/src/librbd/operation/FlattenRequest.cc +++ b/src/librbd/operation/FlattenRequest.cc @@ -56,8 +56,8 @@ public: bufferlist bl; auto req = new io::ObjectWriteRequest(&image_ctx, m_object_no, 0, - std::move(bl), m_snapc, 0, {}, - this); + std::move(bl), m_snapc, 0, 0, + std::nullopt, {}, this); if (!req->has_parent()) { // stop early if the parent went away - it just means // another flatten finished first or the image was resized diff --git a/src/librbd/operation/MigrateRequest.cc b/src/librbd/operation/MigrateRequest.cc index 0da4be9936a54..1e5f9ff0f442e 100644 --- a/src/librbd/operation/MigrateRequest.cc +++ b/src/librbd/operation/MigrateRequest.cc @@ -118,8 +118,8 @@ private: if (is_within_overlap_bounds()) { bufferlist bl; auto req = new io::ObjectWriteRequest(&image_ctx, m_object_no, 0, - std::move(bl), m_snapc, 0, {}, - ctx); + std::move(bl), m_snapc, 0, 0, + std::nullopt, {}, ctx); ldout(cct, 20) << "copyup object req " << req << ", object_no " << m_object_no << dendl; diff --git a/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc b/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc index c65c4974dcd92..f66cf92e24ec0 100644 --- a/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc +++ b/src/test/librbd/cache/test_mock_ParentCacheObjectDispatch.cc @@ -313,7 +313,7 @@ TEST_F(TestMockParentCacheObjectDispatch, test_disble_interface) { ASSERT_EQ(mock_parent_image_cache->discard(0, 0, 0, *temp_snapc, 0, *temp_trace, temp_op_flags, temp_journal_tid, temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); - ASSERT_EQ(mock_parent_image_cache->write(0, 0, std::move(temp_bl), *temp_snapc, 0, + ASSERT_EQ(mock_parent_image_cache->write(0, 0, std::move(temp_bl), *temp_snapc, 0, 0, std::nullopt, *temp_trace, temp_op_flags, temp_journal_tid, temp_dispatch_result, temp_on_finish, temp_on_dispatched), false); ASSERT_EQ(mock_parent_image_cache->write_same(0, 0, 0, std::move(buffer_extents), diff --git a/src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc b/src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc index ee7cfc5fd3911..52d2bd295069f 100644 --- a/src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc +++ b/src/test/librbd/cache/test_mock_WriteAroundObjectDispatch.cc @@ -72,9 +72,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, WriteThrough) { MockContext finish_ctx; MockContext dispatch_ctx; Context* finish_ctx_ptr = &finish_ctx; - ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr, &dispatch_ctx)); + ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr, + &dispatch_ctx)); ASSERT_EQ(finish_ctx_ptr, &finish_ctx); } @@ -95,9 +96,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, WriteThroughUntilFlushed) { MockContext finish_ctx; MockContext dispatch_ctx; Context* finish_ctx_ptr = &finish_ctx; - ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr, &dispatch_ctx)); + ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr, + &dispatch_ctx)); ASSERT_EQ(finish_ctx_ptr, &finish_ctx); ASSERT_FALSE(object_dispatch.flush(io::FLUSH_SOURCE_USER, {}, nullptr, @@ -107,9 +109,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, WriteThroughUntilFlushed) { expect_context_complete(dispatch_ctx, 0); expect_context_complete(finish_ctx, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr, &dispatch_ctx)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr, + &dispatch_ctx)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr, &finish_ctx); ASSERT_EQ(0, dispatch_ctx.wait()); @@ -138,9 +141,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, DispatchIO) { expect_context_complete(dispatch_ctx, 0); expect_context_complete(finish_ctx, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr, &dispatch_ctx)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr, + &dispatch_ctx)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr, &finish_ctx); @@ -170,9 +174,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedIO) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt,{}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); @@ -183,9 +188,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedIO) { expect_context_complete(dispatch_ctx2, 0); expect_context_complete(finish_ctx2, 0); - ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr2, &dispatch_ctx2)); + ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr2, + &dispatch_ctx2)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr2, &finish_ctx2); @@ -193,9 +199,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedIO) { MockContext dispatch_ctx3; Context* finish_ctx_ptr3 = &finish_ctx3; - ASSERT_TRUE(object_dispatch.write(0, 1024, std::move(data), {}, 0, - {}, nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr3, &dispatch_ctx3)); + ASSERT_TRUE(object_dispatch.write(0, 1024, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr3, + &dispatch_ctx3)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr3, &finish_ctx3); @@ -235,9 +242,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, QueuedIO) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); @@ -245,9 +253,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, QueuedIO) { MockContext dispatch_ctx2; Context* finish_ctx_ptr2 = &finish_ctx2; - ASSERT_TRUE(object_dispatch.write(0, 8192, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr2, &dispatch_ctx2)); + ASSERT_TRUE(object_dispatch.write(0, 8192, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr2, + &dispatch_ctx2)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr2, &finish_ctx2); ASSERT_EQ(0, dispatch_ctx1.wait()); @@ -283,9 +292,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedAndQueuedIO) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); @@ -296,9 +306,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedAndQueuedIO) { expect_context_complete(dispatch_ctx2, 0); expect_context_complete(finish_ctx2, 0); - ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr2, &dispatch_ctx2)); + ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr2, + &dispatch_ctx2)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr2, &finish_ctx2); @@ -306,9 +317,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, BlockedAndQueuedIO) { MockContext dispatch_ctx3; Context* finish_ctx_ptr3 = &finish_ctx3; - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, - {}, nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr3, &dispatch_ctx3)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr3, + &dispatch_ctx3)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr3, &finish_ctx3); @@ -368,9 +380,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, FlushQueuedOnInFlightIO) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); ASSERT_EQ(0, dispatch_ctx1.wait()); @@ -413,9 +426,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, FlushQueuedOnQueuedIO) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); ASSERT_EQ(0, dispatch_ctx1.wait()); @@ -424,9 +438,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, FlushQueuedOnQueuedIO) { MockContext dispatch_ctx2; Context* finish_ctx_ptr2 = &finish_ctx2; - ASSERT_TRUE(object_dispatch.write(0, 8192, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr2, &dispatch_ctx2)); + ASSERT_TRUE(object_dispatch.write(0, 8192, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr2, + &dispatch_ctx2)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr2, &finish_ctx2); ASSERT_EQ(0, dispatch_ctx1.wait()); @@ -477,9 +492,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, FlushError) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); ASSERT_EQ(0, dispatch_ctx1.wait()); @@ -547,9 +563,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, UnoptimizedIOInFlightIO) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); ASSERT_EQ(0, dispatch_ctx1.wait()); @@ -593,9 +610,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, UnoptimizedIOBlockedIO) { expect_context_complete(dispatch_ctx1, 0); expect_context_complete(finish_ctx1, 0); - ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr1, &dispatch_ctx1)); + ASSERT_TRUE(object_dispatch.write(0, 0, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr1, + &dispatch_ctx1)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr1, &finish_ctx1); ASSERT_EQ(0, dispatch_ctx1.wait()); @@ -604,9 +622,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, UnoptimizedIOBlockedIO) { MockContext finish_ctx2; MockContext dispatch_ctx2; Context* finish_ctx_ptr2 = &finish_ctx2; - ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr2, &dispatch_ctx2)); + ASSERT_TRUE(object_dispatch.write(0, 4096, std::move(data), {}, 0, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr2, + &dispatch_ctx2)); ASSERT_EQ(io::DISPATCH_RESULT_CONTINUE, dispatch_result); ASSERT_NE(finish_ctx_ptr2, &finish_ctx2); @@ -649,9 +668,10 @@ TEST_F(TestMockCacheWriteAroundObjectDispatch, FUA) { MockContext dispatch_ctx; Context* finish_ctx_ptr = &finish_ctx; ASSERT_FALSE(object_dispatch.write(0, 0, std::move(data), {}, - LIBRADOS_OP_FLAG_FADVISE_FUA, {}, - nullptr, nullptr, &dispatch_result, - &finish_ctx_ptr, &dispatch_ctx)); + LIBRADOS_OP_FLAG_FADVISE_FUA, 0, + std::nullopt, {}, nullptr, nullptr, + &dispatch_result, &finish_ctx_ptr, + &dispatch_ctx)); ASSERT_EQ(finish_ctx_ptr, &finish_ctx); } diff --git a/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc b/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc index d38d10dae07ee..6fc8491aae956 100644 --- a/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc +++ b/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc @@ -163,8 +163,8 @@ TEST_F(TestMockCryptoCryptoObjectDispatch, Read) { TEST_F(TestMockCryptoCryptoObjectDispatch, Write) { expect_encrypt(); ASSERT_TRUE(mock_crypto_object_dispatch->write( - 0, 0, std::move(data), mock_image_ctx->snapc, 0, {}, nullptr, nullptr, - &dispatch_result, &on_finish, on_dispatched)); + 0, 0, std::move(data), mock_image_ctx->snapc, 0, 0, std::nullopt, {}, + nullptr, nullptr, &dispatch_result, &on_finish, on_dispatched)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_CONTINUE); ASSERT_EQ(on_finish, &finished_cond); // not modified on_finish->complete(0); diff --git a/src/test/librbd/io/test_mock_ObjectRequest.cc b/src/test/librbd/io/test_mock_ObjectRequest.cc index 123bf52c149d9..0803ec32d8d31 100644 --- a/src/test/librbd/io/test_mock_ObjectRequest.cc +++ b/src/test/librbd/io/test_mock_ObjectRequest.cc @@ -606,11 +606,167 @@ TEST_F(TestMockIoObjectRequest, Write) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } +TEST_F(TestMockIoObjectRequest, WriteWithCreateExclusiveFlag) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_get_object_size(mock_image_ctx); + + MockExclusiveLock mock_exclusive_lock; + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + expect_is_lock_owner(mock_exclusive_lock); + } + + MockObjectMap mock_object_map; + if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) { + mock_image_ctx.object_map = &mock_object_map; + } + + // exclusive create should succeed + { + bufferlist bl; + bl.append(std::string(4096, '1')); + + InSequence seq; + expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0); + expect_object_may_exist(mock_image_ctx, 0, true); + expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, + 0); + expect_write(mock_image_ctx, 0, 4096, 0); + + C_SaferCond ctx; + auto req = MockObjectWriteRequest::create_write( + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, + OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, std::nullopt, {}, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + } + + // exclusive create should fail since object already exists + { + bufferlist bl; + bl.append(std::string(4096, '1')); + + InSequence seq; + expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0); + expect_object_may_exist(mock_image_ctx, 0, true); + expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, + 0); + + C_SaferCond ctx; + auto req = MockObjectWriteRequest::create_write( + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, + OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, std::nullopt, {}, &ctx); + req->send(); + ASSERT_EQ(-EEXIST, ctx.wait()); + } +} + +TEST_F(TestMockIoObjectRequest, WriteWithAssertVersion) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_get_object_size(mock_image_ctx); + + MockExclusiveLock mock_exclusive_lock; + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + expect_is_lock_owner(mock_exclusive_lock); + } + + MockObjectMap mock_object_map; + if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) { + mock_image_ctx.object_map = &mock_object_map; + } + + // write an object + { + bufferlist bl; + bl.append(std::string(4096, '1')); + + InSequence seq; + expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0); + expect_object_may_exist(mock_image_ctx, 0, true); + expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, + 0); + expect_write(mock_image_ctx, 0, 4096, 0); + + C_SaferCond ctx; + auto req = MockObjectWriteRequest::create_write( + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + } + + // assert version should succeed (version = 1) + { + bufferlist bl; + bl.append(std::string(4096, '1')); + + InSequence seq; + expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0); + expect_object_may_exist(mock_image_ctx, 0, true); + expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, + 0); + expect_write(mock_image_ctx, 0, 4096, 0); + + C_SaferCond ctx; + auto req = MockObjectWriteRequest::create_write( + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::make_optional(1), {}, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + } + + // assert with wrong (lower) version (version = 2) + { + bufferlist bl; + bl.append(std::string(4096, '1')); + + InSequence seq; + expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0); + expect_object_may_exist(mock_image_ctx, 0, true); + expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, + 0); + + C_SaferCond ctx; + auto req = MockObjectWriteRequest::create_write( + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::make_optional(1), {}, &ctx); + req->send(); + ASSERT_EQ(-ERANGE, ctx.wait()); + } + + // assert with wrong (higher) version (version = 2) + { + bufferlist bl; + bl.append(std::string(4096, '1')); + + InSequence seq; + expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0); + expect_object_may_exist(mock_image_ctx, 0, true); + expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, + 0); + + C_SaferCond ctx; + auto req = MockObjectWriteRequest::create_write( + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::make_optional(3), {}, &ctx); + req->send(); + ASSERT_EQ(-EOVERFLOW, ctx.wait()); + } +} + TEST_F(TestMockIoObjectRequest, WriteFull) { librbd::ImageCtx *ictx; ASSERT_EQ(0, open_image(m_image_name, &ictx)); @@ -640,7 +796,8 @@ TEST_F(TestMockIoObjectRequest, WriteFull) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } @@ -673,7 +830,8 @@ TEST_F(TestMockIoObjectRequest, WriteObjectMap) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } @@ -694,7 +852,8 @@ TEST_F(TestMockIoObjectRequest, WriteError) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(-EPERM, ctx.wait()); } @@ -747,7 +906,8 @@ TEST_F(TestMockIoObjectRequest, Copyup) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } @@ -802,7 +962,8 @@ TEST_F(TestMockIoObjectRequest, CopyupRestart) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } @@ -849,7 +1010,8 @@ TEST_F(TestMockIoObjectRequest, CopyupOptimization) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } @@ -889,7 +1051,8 @@ TEST_F(TestMockIoObjectRequest, CopyupError) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(-EPERM, ctx.wait()); } @@ -1407,7 +1570,8 @@ TEST_F(TestMockIoObjectRequest, ObjectMapError) { C_SaferCond ctx; auto req = MockObjectWriteRequest::create_write( - &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, {}, &ctx); + &mock_image_ctx, 0, 0, std::move(bl), mock_image_ctx.snapc, 0, 0, + std::nullopt, {}, &ctx); req->send(); ASSERT_EQ(-EBLACKLISTED, ctx.wait()); } diff --git a/src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc b/src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc index 9ce77ad0fb068..852dcc120f3a2 100644 --- a/src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc +++ b/src/test/librbd/io/test_mock_SimpleSchedulerObjectDispatch.cc @@ -171,7 +171,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Write) { C_SaferCond cond; Context *on_finish = &cond; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish, nullptr)); ASSERT_NE(on_finish, &cond); on_finish->complete(0); @@ -256,7 +256,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteDelayed) { C_SaferCond cond1; Context *on_finish1 = &cond1; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); ASSERT_NE(on_finish1, &cond1); @@ -268,7 +268,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteDelayed) { Context *on_finish2 = &cond2; C_SaferCond on_dispatched; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, &on_dispatched)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); @@ -303,7 +303,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteDelayedFlush) { C_SaferCond cond1; Context *on_finish1 = &cond1; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); ASSERT_NE(on_finish1, &cond1); @@ -315,7 +315,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteDelayedFlush) { Context *on_finish2 = &cond2; C_SaferCond on_dispatched; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, &on_dispatched)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); @@ -359,7 +359,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteMerged) { C_SaferCond cond1; Context *on_finish1 = &cond1; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); ASSERT_NE(on_finish1, &cond1); @@ -374,8 +374,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteMerged) { Context *on_finish2 = &cond2; C_SaferCond on_dispatched2; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, &on_dispatched2)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish2, &cond2); @@ -388,8 +388,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteMerged) { Context *on_finish3 = &cond3; C_SaferCond on_dispatched3; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish3, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish3, &on_dispatched3)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish3, &cond3); @@ -401,8 +401,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteMerged) { Context *on_finish4 = &cond4; C_SaferCond on_dispatched4; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish4, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {},&object_dispatch_flags, nullptr, &dispatch_result, &on_finish4, &on_dispatched4)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish4, &cond4); @@ -414,8 +414,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteMerged) { Context *on_finish5 = &cond5; C_SaferCond on_dispatched5; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish5, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish5, &on_dispatched5)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish5, &cond5); @@ -427,8 +427,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteMerged) { Context *on_finish6 = &cond6; C_SaferCond on_dispatched6; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish6, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish6, &on_dispatched6)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish6, &cond6); @@ -475,7 +475,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteNonSequential) { C_SaferCond cond1; Context *on_finish1 = &cond1; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); ASSERT_NE(on_finish1, &cond1); @@ -490,8 +490,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteNonSequential) { Context *on_finish2 = &cond2; C_SaferCond on_dispatched2; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, &on_dispatched2)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish2, &cond2); @@ -506,8 +506,9 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, WriteNonSequential) { C_SaferCond cond3; Context *on_finish3 = &cond3; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish3, nullptr)); + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish3, + nullptr)); ASSERT_NE(on_finish3, &cond3); on_finish1->complete(0); @@ -538,7 +539,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Mixed) { C_SaferCond cond1; Context *on_finish1 = &cond1; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); ASSERT_NE(on_finish1, &cond1); @@ -554,8 +555,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Mixed) { Context *on_finish2 = &cond2; C_SaferCond on_dispatched2; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, &on_dispatched2)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish2, &cond2); @@ -570,8 +571,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Mixed) { Context *on_finish3 = &cond3; C_SaferCond on_dispatched3; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish3, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish3, &on_dispatched3)); ASSERT_NE(on_finish3, &cond3); @@ -600,8 +601,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Mixed) { Context *on_finish5 = &cond5; C_SaferCond on_dispatched5; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish5, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish5, &on_dispatched5)); ASSERT_NE(on_finish5, &cond5); ASSERT_NE(timer_task, nullptr); @@ -630,8 +631,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Mixed) { Context *on_finish7 = &cond7; C_SaferCond on_dispatched7; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, object_off, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish7, + 0, object_off, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish7, &on_dispatched7)); ASSERT_NE(on_finish7, &cond7); ASSERT_NE(timer_task, nullptr); @@ -693,8 +694,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, DispatchQueue) { C_SaferCond cond1; Context *on_finish1 = &cond1; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - object_no, 0, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); + object_no, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); ASSERT_NE(on_finish1, &cond1); Context *timer_task = nullptr; @@ -706,8 +707,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, DispatchQueue) { Context *on_finish2 = &cond2; C_SaferCond on_dispatched2; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - object_no, 0, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, + object_no, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, &on_dispatched2)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish2, &cond2); @@ -720,8 +721,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, DispatchQueue) { C_SaferCond cond3; Context *on_finish3 = &cond3; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - object_no, 0, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, nullptr, &on_finish3, nullptr)); + object_no, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, nullptr, &on_finish3, nullptr)); ASSERT_NE(on_finish3, &cond3); data.clear(); @@ -729,8 +730,8 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, DispatchQueue) { Context *on_finish4 = &cond4; C_SaferCond on_dispatched4; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - object_no, 0, std::move(data), mock_image_ctx.snapc, 0, {}, - &object_dispatch_flags, nullptr, &dispatch_result, &on_finish4, + object_no, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, + {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish4, &on_dispatched4)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); ASSERT_NE(on_finish4, &cond4); @@ -776,7 +777,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Timer) { C_SaferCond cond1; Context *on_finish1 = &cond1; ASSERT_FALSE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, nullptr, &on_finish1, nullptr)); ASSERT_NE(on_finish1, &cond1); @@ -789,7 +790,7 @@ TEST_F(TestMockIoSimpleSchedulerObjectDispatch, Timer) { Context *on_finish2 = &cond2; C_SaferCond on_dispatched; ASSERT_TRUE(mock_simple_scheduler_object_dispatch.write( - 0, 0, std::move(data), mock_image_ctx.snapc, 0, {}, + 0, 0, std::move(data), mock_image_ctx.snapc, 0, 0, std::nullopt, {}, &object_dispatch_flags, nullptr, &dispatch_result, &on_finish2, &on_dispatched)); ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); diff --git a/src/test/librbd/mock/io/MockObjectDispatch.h b/src/test/librbd/mock/io/MockObjectDispatch.h index 943b3a06a1e08..e16be79ef437d 100644 --- a/src/test/librbd/mock/io/MockObjectDispatch.h +++ b/src/test/librbd/mock/io/MockObjectDispatch.h @@ -52,18 +52,20 @@ public: dispatch_result, on_dispatched); } - MOCK_METHOD8(execute_write, + MOCK_METHOD10(execute_write, bool(uint64_t, uint64_t, const ceph::bufferlist&, - const ::SnapContext &, int*, uint64_t*, DispatchResult*, - Context *)); + const ::SnapContext &, int, std::optional, int*, + uint64_t*, DispatchResult*, Context *)); bool write( uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, - const ::SnapContext &snapc, int op_flags, + const ::SnapContext &snapc, int op_flags, int write_flags, + std::optional assert_version, const ZTracer::Trace &parent_trace, int* dispatch_flags, uint64_t* journal_tid, DispatchResult* dispatch_result, Context** on_finish, Context* on_dispatched) override { - return execute_write(object_no, object_off, data, snapc, dispatch_flags, - journal_tid, dispatch_result, on_dispatched); + return execute_write(object_no, object_off, data, snapc, write_flags, + assert_version, dispatch_flags, journal_tid, + dispatch_result, on_dispatched); } MOCK_METHOD10(execute_write_same, -- 2.39.5