#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
#include "librbd/io/ImageRequestWQ.h"
+#include "librbd/io/ObjectDispatcher.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << dendl;
- send_invalidate_cache(false);
+ send_invalidate_cache();
}
template <typename I>
-void PreReleaseRequest<I>::send_invalidate_cache(bool purge_on_error) {
- if (m_image_ctx.object_cacher == nullptr) {
- send_flush_notifies();
- return;
- }
-
+void PreReleaseRequest<I>::send_invalidate_cache() {
CephContext *cct = m_image_ctx.cct;
- ldout(cct, 10) << "purge_on_error=" << purge_on_error << dendl;
+ ldout(cct, 10) << dendl;
RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
- Context *ctx = create_async_context_callback(
- m_image_ctx, create_context_callback<
+ Context *ctx = create_context_callback<
PreReleaseRequest<I>,
- &PreReleaseRequest<I>::handle_invalidate_cache>(this));
- m_image_ctx.invalidate_cache(purge_on_error, ctx);
+ &PreReleaseRequest<I>::handle_invalidate_cache>(this);
+ m_image_ctx.io_object_dispatcher->invalidate_cache(ctx);
}
template <typename I>
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << "r=" << r << dendl;
- if (r == -EBLACKLISTED) {
- lderr(cct) << "failed to invalidate cache because client is blacklisted"
- << dendl;
- if (!m_image_ctx.is_cache_empty()) {
- // force purge the cache after after being blacklisted
- send_invalidate_cache(true);
- return;
- }
- } else if (r < 0 && r != -EBUSY) {
+ if (r < 0 && r != -EBLACKLISTED && r != -EBUSY) {
lderr(cct) << "failed to invalidate cache: " << cpp_strerror(r)
<< dendl;
m_image_ctx.io_work_queue->unblock_writes();
void send_wait_for_ops();
void handle_wait_for_ops(int r);
- void send_invalidate_cache(bool purge_on_error);
+ void send_invalidate_cache();
void handle_invalidate_cache(int r);
void send_flush_notifies();
CephContext *cct = m_image_ctx->cct;
ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl;
- send_shut_down_cache();
-}
-
-template <typename I>
-void CloseRequest<I>::send_shut_down_cache() {
- CephContext *cct = m_image_ctx->cct;
- ldout(cct, 10) << this << " " << __func__ << dendl;
-
- m_image_ctx->shut_down_cache(create_context_callback<
- CloseRequest<I>, &CloseRequest<I>::handle_shut_down_cache>(this));
-}
-
-template <typename I>
-void CloseRequest<I>::handle_shut_down_cache(int r) {
- CephContext *cct = m_image_ctx->cct;
- ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl;
-
- save_result(r);
- if (r < 0) {
- lderr(cct) << "failed to shut down cache: " << cpp_strerror(r) << dendl;
- }
send_shut_down_object_dispatcher();
}
* FLUSH_READAHEAD
* |
* v
- * SHUTDOWN_CACHE
- * |
- * v
* SHUT_DOWN_OBJECT_DISPATCHER
* |
* v
void send_flush_readahead();
void handle_flush_readahead(int r);
- void send_shut_down_cache();
- void handle_shut_down_cache(int r);
-
void send_shut_down_object_dispatcher();
void handle_shut_down_object_dispatcher(int r);
#include "cls/rbd/cls_rbd_client.h"
#include "librbd/ImageCtx.h"
#include "librbd/Utils.h"
+#include "librbd/cache/ObjectCacherObjectDispatch.h"
#include "librbd/image/CloseRequest.h"
#include "librbd/image/RefreshRequest.h"
#include "librbd/image/SetSnapRequest.h"
return nullptr;
}
- m_image_ctx->init_cache();
+ return send_init_cache(result);
+}
+
+template <typename I>
+Context *OpenRequest<I>::send_init_cache(int *result) {
+ // cache is disabled or parent image context
+ if (!m_image_ctx->cache || m_image_ctx->child != nullptr) {
+ return send_register_watch(result);
+ }
+
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ auto cache = cache::ObjectCacherObjectDispatch<I>::create(m_image_ctx);
+ cache->init();
+
+ // readahead requires the cache
+ m_image_ctx->readahead.set_trigger_requests(
+ m_image_ctx->readahead_trigger_requests);
+ m_image_ctx->readahead.set_max_readahead_size(
+ m_image_ctx->readahead_max_bytes);
+
return send_register_watch(result);
}
* V2_GET_DATA_POOL --------------> REFRESH
* |
* v
+ * INIT_CACHE
+ * |
+ * v
* REGISTER_WATCH (skip if
* | read-only)
* v
void send_refresh();
Context *handle_refresh(int *result);
+ Context *send_init_cache(int *result);
+
Context *send_register_watch(int *result);
Context *handle_register_watch(int *result);
#include "common/errno.h"
#include "common/Throttle.h"
#include "common/event_socket.h"
-#include "cls/lock/cls_lock_client.h"
+#include "common/perf_counters.h"
+#include "osdc/Striper.h"
#include "include/stringify.h"
+#include "cls/lock/cls_lock_client.h"
#include "cls/rbd/cls_rbd.h"
#include "cls/rbd/cls_rbd_types.h"
#include "cls/rbd/cls_rbd_client.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ImageRequest.h"
#include "librbd/io/ImageRequestWQ.h"
+#include "librbd/io/ObjectDispatcher.h"
+#include "librbd/io/ObjectDispatchSpec.h"
#include "librbd/io/ObjectRequest.h"
#include "librbd/io/ReadResult.h"
#include "librbd/journal/Types.h"
return r;
}
- RWLock::RLocker owner_locker(ictx->owner_lock);
- r = ictx->invalidate_cache(false);
+ C_SaferCond ctx;
+ {
+ RWLock::RLocker owner_locker(ictx->owner_lock);
+ ictx->io_object_dispatcher->invalidate_cache(&ctx);
+ }
+ r = ctx.wait();
ictx->perfcounter->inc(l_librbd_invalidate_cache);
return r;
}
object_t oid;
uint64_t offset;
uint64_t length;
+
+ bufferlist read_data;
+ io::ExtentMap extent_map;
+
C_RBD_Readahead(ImageCtx *ictx, object_t oid, uint64_t offset, uint64_t length)
- : ictx(ictx), oid(oid), offset(offset), length(length) { }
+ : ictx(ictx), oid(oid), offset(offset), length(length) {
+ ictx->readahead.inc_pending();
+ }
+
void finish(int r) override {
- ldout(ictx->cct, 20) << "C_RBD_Readahead on " << oid << ": " << offset << "+" << length << dendl;
+ ldout(ictx->cct, 20) << "C_RBD_Readahead on " << oid << ": "
+ << offset << "~" << length << dendl;
ictx->readahead.dec_pending();
}
};
++p) {
total_bytes += p->second;
}
-
+
ictx->md_lock.get_write();
bool abort = ictx->readahead_disable_after_bytes != 0 &&
ictx->total_bytes_read > ictx->readahead_disable_after_bytes;
ictx->total_bytes_read += total_bytes;
ictx->snap_lock.get_read();
uint64_t image_size = ictx->get_image_size(ictx->snap_id);
+ auto snap_id = ictx->snap_id;
ictx->snap_lock.put_read();
ictx->md_lock.put_write();
-
+
pair<uint64_t, uint64_t> readahead_extent = ictx->readahead.update(image_extents, image_size);
uint64_t readahead_offset = readahead_extent.first;
uint64_t readahead_length = readahead_extent.second;
for (vector<ObjectExtent>::iterator q = p->second.begin(); q != p->second.end(); ++q) {
ldout(ictx->cct, 20) << "(readahead) oid " << q->oid << " " << q->offset << "~" << q->length << dendl;
- Context *req_comp = new C_RBD_Readahead(ictx, q->oid, q->offset, q->length);
- ictx->readahead.inc_pending();
- ictx->aio_read_from_cache(q->oid, q->objectno, NULL,
- q->length, q->offset,
- req_comp, 0, nullptr);
+ auto req_comp = new C_RBD_Readahead(ictx, q->oid, q->offset,
+ q->length);
+ auto req = io::ObjectDispatchSpec::create_read(
+ ictx, io::OBJECT_DISPATCH_LAYER_NONE, q->oid.name, q->objectno,
+ q->offset, q->length, snap_id, 0, {}, &req_comp->read_data,
+ &req_comp->extent_map, req_comp);
+ req->send(0);
}
}
ictx->perfcounter->inc(l_librbd_readahead);
}
template <typename T, void (T::*MF)(int) = &T::complete>
- static AioCompletion *create_and_start(T *obj, ImageCtx *image_ctx,
- aio_type_t type) {
+ static AioCompletion *create(T *obj, ImageCtx *image_ctx, aio_type_t type) {
AioCompletion *comp = create<T, MF>(obj);
comp->init_time(image_ctx, type);
+ return comp;
+ }
+
+ template <typename T, void (T::*MF)(int) = &T::complete>
+ static AioCompletion *create_and_start(T *obj, ImageCtx *image_ctx,
+ aio_type_t type) {
+ AioCompletion *comp = create<T, MF>(obj, image_ctx, type);
comp->start_op();
return comp;
}
template <typename I>
void ObjectReadRequest<I>::read_parent() {
I *image_ctx = this->m_ictx;
- if (m_cache_initiated) {
- this->finish(-ENOENT);
- return;
- }
uint64_t object_overlap = 0;
Extents parent_extents;
#include "librbd/internal.h"
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
#include "librbd/io/ImageRequestWQ.h"
+#include "librbd/io/ObjectDispatcher.h"
#include "librbd/operation/TrimRequest.h"
#include "common/dout.h"
#include "common/errno.h"
template <typename I>
void ResizeRequest<I>::send_flush_cache() {
I &image_ctx = this->m_image_ctx;
- if (image_ctx.object_cacher == nullptr) {
- send_trim_image();
- return;
- }
CephContext *cct = image_ctx.cct;
ldout(cct, 5) << this << " " << __func__ << dendl;
RWLock::RLocker owner_locker(image_ctx.owner_lock);
- image_ctx.flush_cache(create_async_context_callback(
- image_ctx, create_context_callback<
- ResizeRequest<I>, &ResizeRequest<I>::handle_flush_cache>(this)));
+ auto ctx = create_context_callback<
+ ResizeRequest<I>, &ResizeRequest<I>::handle_flush_cache>(this);
+ auto aio_comp = io::AioCompletion::create(
+ ctx, util::get_image_ctx(&image_ctx), io::AIO_TYPE_FLUSH);
+ auto req = io::ImageDispatchSpec<I>::create_flush_request(
+ image_ctx, aio_comp, io::FLUSH_SOURCE_INTERNAL, {});
+ req->send();
+ delete req;
}
template <typename I>
// need to invalidate since we're deleting objects, and
// ObjectCacher doesn't track non-existent objects
RWLock::RLocker owner_locker(image_ctx.owner_lock);
- image_ctx.invalidate_cache(false, create_async_context_callback(
- image_ctx, create_context_callback<
- ResizeRequest<I>, &ResizeRequest<I>::handle_invalidate_cache>(this)));
+ image_ctx.io_object_dispatcher->invalidate_cache(create_context_callback<
+ ResizeRequest<I>, &ResizeRequest<I>::handle_invalidate_cache>(this));
}
template <typename I>
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
#include "librbd/io/ImageRequestWQ.h"
+#include "librbd/io/ObjectDispatcher.h"
#include "librbd/operation/ResizeRequest.h"
#include "osdc/Striper.h"
#include <boost/lambda/bind.hpp>
I &image_ctx = this->m_image_ctx;
apply();
- if (image_ctx.object_cacher == NULL) {
- return this->create_context_finisher(0);
- }
CephContext *cct = image_ctx.cct;
ldout(cct, 5) << this << " " << __func__ << dendl;
Context *ctx = create_context_callback<
SnapshotRollbackRequest<I>,
&SnapshotRollbackRequest<I>::handle_invalidate_cache>(this);
- image_ctx.invalidate_cache(true, ctx);
+ image_ctx.io_object_dispatcher->invalidate_cache(ctx);
return nullptr;
}
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librbd/mock/MockJournal.h"
#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/AsyncOpTracker.h"
#include "librbd/exclusive_lock/PreReleaseRequest.h"
.WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
}
- void expect_invalidate_cache(MockImageCtx &mock_image_ctx, bool purge,
+ void expect_invalidate_cache(MockImageCtx &mock_image_ctx,
int r) {
- if (mock_image_ctx.object_cacher != nullptr) {
- EXPECT_CALL(mock_image_ctx, invalidate_cache(purge, _))
- .WillOnce(WithArg<1>(CompleteContext(r, static_cast<ContextWQ*>(NULL))));
- }
- }
-
- void expect_is_cache_empty(MockImageCtx &mock_image_ctx, bool empty) {
- if (mock_image_ctx.object_cacher != nullptr) {
- EXPECT_CALL(mock_image_ctx, is_cache_empty())
- .WillOnce(Return(empty));
- }
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, invalidate_cache(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
}
void expect_flush_notifies(MockImageCtx &mock_image_ctx) {
expect_prepare_lock(mock_image_ctx);
expect_cancel_op_requests(mock_image_ctx, 0);
expect_block_writes(mock_image_ctx, 0);
- expect_invalidate_cache(mock_image_ctx, false, 0);
+ expect_invalidate_cache(mock_image_ctx, 0);
+
expect_flush_notifies(mock_image_ctx);
MockJournal *mock_journal = new MockJournal();
InSequence seq;
expect_prepare_lock(mock_image_ctx);
expect_cancel_op_requests(mock_image_ctx, 0);
- expect_invalidate_cache(mock_image_ctx, false, 0);
+ expect_invalidate_cache(mock_image_ctx, 0);
+
expect_flush_notifies(mock_image_ctx);
MockObjectMap *mock_object_map = new MockObjectMap();
InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
- expect_invalidate_cache(mock_image_ctx, false, 0);
+ expect_invalidate_cache(mock_image_ctx, 0);
+
expect_flush_notifies(mock_image_ctx);
C_SaferCond release_ctx;
expect_prepare_lock(mock_image_ctx);
expect_cancel_op_requests(mock_image_ctx, 0);
expect_block_writes(mock_image_ctx, -EBLACKLISTED);
- expect_invalidate_cache(mock_image_ctx, false, -EBLACKLISTED);
- expect_is_cache_empty(mock_image_ctx, false);
- expect_invalidate_cache(mock_image_ctx, true, -EBLACKLISTED);
- expect_is_cache_empty(mock_image_ctx, true);
+ expect_invalidate_cache(mock_image_ctx, -EBLACKLISTED);
+
expect_flush_notifies(mock_image_ctx);
MockJournal *mock_journal = new MockJournal();
InSequence seq;
expect_cancel_op_requests(mock_image_ctx, 0);
expect_block_writes(mock_image_ctx, 0);
- expect_invalidate_cache(mock_image_ctx, false, 0);
+ expect_invalidate_cache(mock_image_ctx, 0);
+
expect_flush_notifies(mock_image_ctx);
C_SaferCond ctx;
}));
}
- void expect_flush(MockImageCtx &mock_image_ctx, int r) {
- EXPECT_CALL(mock_image_ctx, flush_cache(_))
- .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
- }
-
void expect_flush_async_operations(MockImageCtx &mock_image_ctx, int r) {
EXPECT_CALL(mock_image_ctx, flush_async_operations(_))
.WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
InSequence seq;
expect_is_journal_appending(mock_journal, false);
- if (mock_image_ctx.image_ctx->cache) {
- expect_write_to_cache(mock_image_ctx, ictx->get_object_name(0),
- 0, 1, 0, 0);
- } else {
- expect_object_request_send(mock_image_ctx, 0);
- }
+ expect_object_request_send(mock_image_ctx, 0);
C_SaferCond aio_comp_ctx;
AioCompletion *aio_comp = AioCompletion::create_and_start(
expect_is_journal_appending(mock_journal, false);
expect_flush_async_operations(mock_image_ctx, 0);
expect_object_request_send(mock_image_ctx, 0);
- expect_flush(mock_image_ctx, 0);
C_SaferCond aio_comp_ctx;
AioCompletion *aio_comp = AioCompletion::create_and_start(
InSequence seq;
expect_is_journal_appending(mock_journal, false);
- if (mock_image_ctx.image_ctx->cache) {
- expect_write_to_cache(mock_image_ctx, ictx->get_object_name(0),
- 0, 1, 0, 0);
- } else {
- expect_object_request_send(mock_image_ctx, 0);
- }
-
+ expect_object_request_send(mock_image_ctx, 0);
C_SaferCond aio_comp_ctx;
AioCompletion *aio_comp = AioCompletion::create_and_start(
}
}
- void expect_cache_read(MockTestImageCtx &mock_image_ctx,
- const std::string& oid, uint64_t object_no,
- uint64_t off, uint64_t len, const std::string& data,
- int r) {
- bufferlist bl;
- bl.append(data);
-
- EXPECT_CALL(mock_image_ctx, aio_read_from_cache({oid}, object_no, _, len,
- off, _, _, _))
- .WillOnce(WithArgs<2, 5>(Invoke([bl, r](bufferlist *out_bl,
- Context *on_finish) {
- out_bl->append(bl);
- on_finish->complete(r);
- })));
- }
-
void expect_aio_read(MockImageRequest& mock_image_request,
Extents&& extents, int r) {
EXPECT_CALL(mock_image_request, aio_read(_, extents))
ASSERT_EQ(-EPERM, ctx.wait());
}
-TEST_F(TestMockIoObjectRequest, CacheRead) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ictx->sparse_read_threshold_bytes = 8096;
-
- MockTestImageCtx mock_image_ctx(*ictx);
- MockObjectMap mock_object_map;
- if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
- mock_image_ctx.object_map = &mock_object_map;
- }
-
- expect_op_work_queue(mock_image_ctx);
-
- InSequence seq;
- expect_cache_read(mock_image_ctx, ictx->get_object_name(0), 0, 0, 4096,
- std::string(4096, '1'), 0);
-
- bufferlist bl;
- ExtentMap extent_map;
- C_SaferCond ctx;
- auto req = MockObjectReadRequest::create(
- &mock_image_ctx, ictx->get_object_name(0), 0, 0, 4096, CEPH_NOSNAP, 0,
- false, {}, &bl, &extent_map, &ctx);
- req->send();
- ASSERT_EQ(0, ctx.wait());
-}
-
-TEST_F(TestMockIoObjectRequest, CacheReadError) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
- ictx->sparse_read_threshold_bytes = 8096;
-
- MockTestImageCtx mock_image_ctx(*ictx);
- MockObjectMap mock_object_map;
- if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
- mock_image_ctx.object_map = &mock_object_map;
- }
-
- expect_op_work_queue(mock_image_ctx);
-
- InSequence seq;
- expect_cache_read(mock_image_ctx, ictx->get_object_name(0), 0, 0, 4096,
- "", -EPERM);
-
- bufferlist bl;
- ExtentMap extent_map;
- C_SaferCond ctx;
- auto req = MockObjectReadRequest::create(
- &mock_image_ctx, ictx->get_object_name(0), 0, 0, 4096, CEPH_NOSNAP, 0,
- false, {}, &bl, &extent_map, &ctx);
- req->send();
- ASSERT_EQ(-EPERM, ctx.wait());
-}
-
TEST_F(TestMockIoObjectRequest, ParentRead) {
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
ASSERT_EQ(-EPERM, ctx.wait());
}
-TEST_F(TestMockIoObjectRequest, CacheInitiated) {
- REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
-
- librbd::Image image;
- librbd::RBD rbd;
- ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
- ASSERT_EQ(0, image.snap_create("one"));
- ASSERT_EQ(0, image.snap_protect("one"));
- image.close();
-
- std::string clone_name = get_temp_image_name();
- int order = 0;
- ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
- clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
-
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(clone_name, &ictx));
- ictx->sparse_read_threshold_bytes = 8096;
- ictx->clone_copy_on_read = false;
-
- MockTestImageCtx mock_image_ctx(*ictx);
- mock_image_ctx.parent = &mock_image_ctx;
-
- MockObjectMap mock_object_map;
- if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
- mock_image_ctx.object_map = &mock_object_map;
- }
-
- InSequence seq;
- expect_object_may_exist(mock_image_ctx, 0, true);
- expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
- expect_read(mock_image_ctx, ictx->get_object_name(0), 0, 4096, "", -ENOENT);
-
- bufferlist bl;
- ExtentMap extent_map;
- C_SaferCond ctx;
- auto req = MockObjectReadRequest::create(
- &mock_image_ctx, ictx->get_object_name(0), 0, 0, 4096, CEPH_NOSNAP, 0, true,
- {}, &bl, &extent_map, &ctx);
- req->send();
- ASSERT_EQ(-ENOENT, ctx.wait());
-}
-
TEST_F(TestMockIoObjectRequest, CopyOnRead) {
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "common/bit_vector.hpp"
#include "librbd/internal.h"
#include "librbd/ObjectMap.h"
+#include "librbd/io/ImageDispatchSpec.h"
#include "librbd/operation/ResizeRequest.h"
#include "librbd/operation/TrimRequest.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace librbd {
+
+namespace util {
+
+inline ImageCtx* get_image_ctx(MockImageCtx* image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace io {
+
+template <>
+struct ImageDispatchSpec<MockImageCtx> {
+ static ImageDispatchSpec* s_instance;
+ AioCompletion *aio_comp = nullptr;
+
+ static ImageDispatchSpec* create_flush_request(
+ MockImageCtx &image_ctx, AioCompletion *aio_comp,
+ FlushSource flush_source, const ZTracer::Trace &parent_trace) {
+ assert(s_instance != nullptr);
+ s_instance->aio_comp = aio_comp;
+ return s_instance;
+ }
+
+ MOCK_CONST_METHOD0(send, void());
+
+ ImageDispatchSpec() {
+ s_instance = this;
+ }
+};
+
+ImageDispatchSpec<MockImageCtx>* ImageDispatchSpec<MockImageCtx>::s_instance = nullptr;
+
+} // namespace io
+
namespace operation {
template <>
using ::testing::_;
using ::testing::DoAll;
+using ::testing::Invoke;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::StrEq;
public:
typedef ResizeRequest<MockImageCtx> MockResizeRequest;
typedef TrimRequest<MockImageCtx> MockTrimRequest;
+ typedef io::ImageDispatchSpec<MockImageCtx> MockIoImageDispatchSpec;
void expect_block_writes(MockImageCtx &mock_image_ctx, int r) {
EXPECT_CALL(*mock_image_ctx.io_work_queue, block_writes(_))
.WillOnce(FinishRequest(&mock_trim_request, r, &mock_image_ctx));
}
- void expect_flush_cache(MockImageCtx &mock_image_ctx, int r) {
- if (!mock_image_ctx.image_ctx->cache) {
- return;
- }
- EXPECT_CALL(mock_image_ctx, flush_cache(_))
- .WillOnce(CompleteContext(r, static_cast<ContextWQ*>(NULL)));
- expect_op_work_queue(mock_image_ctx);
+ void expect_flush_cache(MockImageCtx &mock_image_ctx,
+ MockIoImageDispatchSpec& mock_io_image_dispatch_spec,
+ int r) {
+ EXPECT_CALL(mock_io_image_dispatch_spec, send())
+ .WillOnce(Invoke([&mock_image_ctx, &mock_io_image_dispatch_spec, r]() {
+ auto aio_comp = mock_io_image_dispatch_spec.s_instance->aio_comp;
+ auto ctx = new FunctionContext([aio_comp](int r) {
+ aio_comp->get();
+ aio_comp->fail(r);
+ });
+ mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
+ }));
}
- void expect_invalidate_cache(MockImageCtx &mock_image_ctx, int r) {
- if (!mock_image_ctx.image_ctx->cache) {
- return;
- }
- EXPECT_CALL(mock_image_ctx, invalidate_cache(false, _))
- .WillOnce(WithArg<1>(CompleteContext(r, static_cast<ContextWQ*>(NULL))));
+ void expect_invalidate_cache(MockImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, invalidate_cache(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
expect_op_work_queue(mock_image_ctx);
}
expect_unblock_writes(mock_image_ctx);
MockTrimRequest mock_trim_request;
- expect_flush_cache(mock_image_ctx, 0);
+ auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
+ expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, 0);
expect_invalidate_cache(mock_image_ctx, 0);
expect_trim(mock_image_ctx, mock_trim_request, 0);
expect_block_writes(mock_image_ctx, 0);
expect_unblock_writes(mock_image_ctx);
MockTrimRequest mock_trim_request;
- expect_flush_cache(mock_image_ctx, 0);
+ auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
+ expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, 0);
expect_invalidate_cache(mock_image_ctx, -EBUSY);
expect_trim(mock_image_ctx, mock_trim_request, -EINVAL);
expect_commit_op_event(mock_image_ctx, -EINVAL);
expect_unblock_writes(mock_image_ctx);
MockTrimRequest mock_trim_request;
- expect_flush_cache(mock_image_ctx, -EINVAL);
+ auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
+ expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, -EINVAL);
expect_commit_op_event(mock_image_ctx, -EINVAL);
ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
}
expect_unblock_writes(mock_image_ctx);
MockTrimRequest mock_trim_request;
- expect_flush_cache(mock_image_ctx, 0);
+ auto mock_io_image_dispatch_spec = new MockIoImageDispatchSpec();
+ expect_flush_cache(mock_image_ctx, *mock_io_image_dispatch_spec, 0);
expect_invalidate_cache(mock_image_ctx, -EINVAL);
expect_commit_op_event(mock_image_ctx, -EINVAL);
ASSERT_EQ(-EINVAL, when_resize(mock_image_ctx, ictx->size / 2, true, 0, false));
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/io/MockObjectDispatch.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "include/stringify.h"
#include "common/bit_vector.hpp"
}
}
- void expect_invalidate_cache(MockOperationImageCtx &mock_image_ctx, int r) {
- if (mock_image_ctx.object_cacher != nullptr) {
- EXPECT_CALL(mock_image_ctx, invalidate_cache(true, _))
- .WillOnce(WithArg<1>(CompleteContext(r, static_cast<ContextWQ*>(NULL))));
- }
+ void expect_invalidate_cache(MockOperationImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(*mock_image_ctx.io_object_dispatcher, invalidate_cache(_))
+ .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
}
int when_snap_rollback(MockOperationImageCtx &mock_image_ctx,