#include "test/librbd/test_support.h"
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librbd/mock/exclusive_lock/MockPolicy.h"
+#include "librbd/io/ImageDispatchSpec.h"
#include "librbd/io/ImageRequestWQ.h"
#include "librbd/io/ImageRequest.h"
template <>
struct ImageRequest<librbd::MockTestImageCtx> {
static ImageRequest* s_instance;
- AioCompletion *aio_comp;
-
- static ImageRequest* create_write_request(librbd::MockTestImageCtx &image_ctx,
- AioCompletion *aio_comp,
- Extents &&image_extents,
- bufferlist &&bl, int op_flags,
- const ZTracer::Trace &parent_trace) {
- assert(s_instance != nullptr);
- s_instance->aio_comp = aio_comp;
- return s_instance;
- }
+ AioCompletion *aio_comp = nullptr;
+
static void aio_write(librbd::MockTestImageCtx *ictx, AioCompletion *c,
Extents &&image_extents, bufferlist &&bl, int op_flags,
const ZTracer::Trace &parent_trace) {
}
+ ImageRequest() {
+ s_instance = this;
+ }
+};
+
+template <>
+struct ImageDispatchSpec<librbd::MockTestImageCtx> {
+ static ImageDispatchSpec* s_instance;
+ AioCompletion *aio_comp = nullptr;
+
+ static ImageDispatchSpec* create_write_request(
+ librbd::MockTestImageCtx &image_ctx, AioCompletion *aio_comp,
+ Extents &&image_extents, bufferlist &&bl, int op_flags,
+ const ZTracer::Trace &parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_comp = aio_comp;
+ return s_instance;
+ }
+
+ static ImageDispatchSpec* create_flush_request(
+ librbd::MockTestImageCtx &image_ctx, AioCompletion *aio_comp,
+ FlushSource flush_source, const ZTracer::Trace &parent_trace) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->aio_comp = aio_comp;
+ return s_instance;
+ }
MOCK_CONST_METHOD0(is_write_op, bool());
MOCK_CONST_METHOD0(start_op, void());
MOCK_CONST_METHOD0(send, void());
MOCK_CONST_METHOD1(fail, void(int));
+ MOCK_CONST_METHOD1(was_throttled, bool(uint64_t));
+ MOCK_CONST_METHOD0(were_all_throttled, bool());
+ MOCK_CONST_METHOD1(set_throttled, void(uint64_t));
+ MOCK_CONST_METHOD1(tokens_requested, uint64_t(uint64_t));
- ImageRequest() {
+ ImageDispatchSpec() {
s_instance = this;
}
};
-
} // namespace io
namespace util {
} // namespace librbd
template <>
-struct ThreadPool::PointerWQ<librbd::io::ImageRequest<librbd::MockTestImageCtx>> {
- typedef librbd::io::ImageRequest<librbd::MockTestImageCtx> ImageRequest;
+struct ThreadPool::PointerWQ<librbd::io::ImageDispatchSpec<librbd::MockTestImageCtx>> {
+ typedef librbd::io::ImageDispatchSpec<librbd::MockTestImageCtx> ImageDispatchSpec;
static PointerWQ* s_instance;
Mutex m_lock;
MOCK_METHOD0(drain, void());
MOCK_METHOD0(empty, bool());
+ MOCK_METHOD0(mock_empty, bool());
MOCK_METHOD0(signal, void());
MOCK_METHOD0(process_finish, void());
- MOCK_METHOD0(front, ImageRequest*());
- MOCK_METHOD1(requeue, void(ImageRequest*));
+ MOCK_METHOD0(front, ImageDispatchSpec*());
+ MOCK_METHOD1(requeue, void(ImageDispatchSpec*));
MOCK_METHOD0(dequeue, void*());
- MOCK_METHOD1(queue, void(ImageRequest*));
+ MOCK_METHOD1(queue, void(ImageDispatchSpec*));
void register_work_queue() {
// no-op
Mutex::Locker locker(m_lock);
return _void_dequeue();
}
- void invoke_process(ImageRequest *image_request) {
+ void invoke_process(ImageDispatchSpec *image_request) {
process(image_request);
}
virtual void *_void_dequeue() {
return dequeue();
}
- virtual void process(ImageRequest *req) = 0;
+ virtual void process(ImageDispatchSpec *req) = 0;
+ virtual bool _empty() {
+ return mock_empty();
+ }
};
-ThreadPool::PointerWQ<librbd::io::ImageRequest<librbd::MockTestImageCtx>>*
- ThreadPool::PointerWQ<librbd::io::ImageRequest<librbd::MockTestImageCtx>>::s_instance = nullptr;
+ThreadPool::PointerWQ<librbd::io::ImageDispatchSpec<librbd::MockTestImageCtx>>*
+ ThreadPool::PointerWQ<librbd::io::ImageDispatchSpec<librbd::MockTestImageCtx>>::s_instance = nullptr;
librbd::io::ImageRequest<librbd::MockTestImageCtx>*
librbd::io::ImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+librbd::io::ImageDispatchSpec<librbd::MockTestImageCtx>*
+ librbd::io::ImageDispatchSpec<librbd::MockTestImageCtx>::s_instance = nullptr;
#include "librbd/io/ImageRequestWQ.cc"
struct TestMockIoImageRequestWQ : public TestMockFixture {
typedef ImageRequestWQ<librbd::MockTestImageCtx> MockImageRequestWQ;
typedef ImageRequest<librbd::MockTestImageCtx> MockImageRequest;
+ typedef ImageDispatchSpec<librbd::MockTestImageCtx> MockImageDispatchSpec;
- void expect_is_write_op(MockImageRequest &image_request, bool write_op) {
+ void expect_is_write_op(MockImageDispatchSpec &image_request,
+ bool write_op) {
EXPECT_CALL(image_request, is_write_op()).WillOnce(Return(write_op));
}
EXPECT_CALL(image_request_wq, queue(_));
}
+ void expect_requeue(MockImageRequestWQ &image_request_wq) {
+ EXPECT_CALL(image_request_wq, requeue(_));
+ }
+
void expect_front(MockImageRequestWQ &image_request_wq,
- MockImageRequest *image_request) {
+ MockImageDispatchSpec *image_request) {
EXPECT_CALL(image_request_wq, front()).WillOnce(Return(image_request));
}
}
void expect_dequeue(MockImageRequestWQ &image_request_wq,
- MockImageRequest *image_request) {
+ MockImageDispatchSpec *image_request) {
EXPECT_CALL(image_request_wq, dequeue()).WillOnce(Return(image_request));
}
EXPECT_CALL(mock_image_request_wq, process_finish()).Times(1);
}
- void expect_fail(MockImageRequest &mock_image_request, int r) {
+ void expect_fail(MockImageDispatchSpec &mock_image_request, int r) {
EXPECT_CALL(mock_image_request, fail(r))
.WillOnce(Invoke([&mock_image_request](int r) {
mock_image_request.aio_comp->get();
*on_finish = ctx;
}));
}
+
+ void expect_set_throttled(MockImageDispatchSpec &mock_image_request) {
+ EXPECT_CALL(mock_image_request, set_throttled(_)).Times(6);
+ }
+
+ void expect_was_throttled(MockImageDispatchSpec &mock_image_request, bool value) {
+ EXPECT_CALL(mock_image_request, was_throttled(_)).Times(6).WillRepeatedly(Return(value));
+ }
+
+ void expect_tokens_requested(MockImageDispatchSpec &mock_image_request, uint64_t value) {
+ EXPECT_CALL(mock_image_request, tokens_requested(_)).WillOnce(Return(value));
+ }
+
+ void expect_all_throttled(MockImageDispatchSpec &mock_image_request, bool value) {
+ EXPECT_CALL(mock_image_request, were_all_throttled()).WillOnce(Return(value));
+ }
+
+ void expect_start_op(MockImageDispatchSpec &mock_image_request) {
+ EXPECT_CALL(mock_image_request, start_op()).Times(1);
+ }
};
TEST_F(TestMockIoImageRequestWQ, AcquireLockError) {
MockExclusiveLock mock_exclusive_lock;
mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ auto mock_queued_image_request = new MockImageDispatchSpec();
+ expect_was_throttled(*mock_queued_image_request, false);
+ expect_set_throttled(*mock_queued_image_request);
+
InSequence seq;
MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
expect_signal(mock_image_request_wq);
mock_image_request_wq.set_require_lock(DIRECTION_WRITE, true);
- auto mock_image_request = new MockImageRequest();
- expect_is_write_op(*mock_image_request, true);
+ expect_is_write_op(*mock_queued_image_request, true);
expect_queue(mock_image_request_wq);
auto *aio_comp = new librbd::io::AioCompletion();
mock_image_request_wq.aio_write(aio_comp, 0, 0, {}, 0);
librbd::exclusive_lock::MockPolicy mock_exclusive_lock_policy;
- expect_front(mock_image_request_wq, mock_image_request);
+ expect_front(mock_image_request_wq, mock_queued_image_request);
expect_is_refresh_request(mock_image_ctx, false);
- expect_is_write_op(*mock_image_request, true);
- expect_dequeue(mock_image_request_wq, mock_image_request);
+ expect_is_write_op(*mock_queued_image_request, true);
+ expect_dequeue(mock_image_request_wq, mock_queued_image_request);
expect_get_exclusive_lock_policy(mock_image_ctx, mock_exclusive_lock_policy);
expect_may_auto_request_lock(mock_exclusive_lock_policy, true);
Context *on_acquire = nullptr;
ASSERT_TRUE(on_acquire != nullptr);
expect_process_finish(mock_image_request_wq);
- expect_fail(*mock_image_request, -EPERM);
- expect_is_write_op(*mock_image_request, true);
+ expect_fail(*mock_queued_image_request, -EPERM);
+ expect_is_write_op(*mock_queued_image_request, true);
expect_signal(mock_image_request_wq);
on_acquire->complete(-EPERM);
MockExclusiveLock mock_exclusive_lock;
mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ auto mock_queued_image_request = new MockImageDispatchSpec();
+ expect_was_throttled(*mock_queued_image_request, false);
+ expect_set_throttled(*mock_queued_image_request);
+
InSequence seq;
MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
expect_signal(mock_image_request_wq);
mock_image_request_wq.set_require_lock(DIRECTION_WRITE, true);
- auto mock_image_request = new MockImageRequest();
- expect_is_write_op(*mock_image_request, true);
+ expect_is_write_op(*mock_queued_image_request, true);
expect_queue(mock_image_request_wq);
auto *aio_comp = new librbd::io::AioCompletion();
mock_image_request_wq.aio_write(aio_comp, 0, 0, {}, 0);
librbd::exclusive_lock::MockPolicy mock_exclusive_lock_policy;
- expect_front(mock_image_request_wq, mock_image_request);
+ expect_front(mock_image_request_wq, mock_queued_image_request);
expect_is_refresh_request(mock_image_ctx, false);
- expect_is_write_op(*mock_image_request, true);
- expect_dequeue(mock_image_request_wq, mock_image_request);
+ expect_is_write_op(*mock_queued_image_request, true);
+ expect_dequeue(mock_image_request_wq, mock_queued_image_request);
expect_get_exclusive_lock_policy(mock_image_ctx, mock_exclusive_lock_policy);
expect_may_auto_request_lock(mock_exclusive_lock_policy, false);
EXPECT_CALL(*mock_image_ctx.exclusive_lock, get_unlocked_op_error())
.WillOnce(Return(-EBLACKLISTED));
-
expect_process_finish(mock_image_request_wq);
- expect_fail(*mock_image_request, -EBLACKLISTED);
- expect_is_write_op(*mock_image_request, true);
+ expect_fail(*mock_queued_image_request, -EBLACKLISTED);
+ expect_is_write_op(*mock_queued_image_request, true);
expect_signal(mock_image_request_wq);
ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr);
MockTestImageCtx mock_image_ctx(*ictx);
+ auto mock_queued_image_request = new MockImageDispatchSpec();
+ expect_was_throttled(*mock_queued_image_request, false);
+ expect_set_throttled(*mock_queued_image_request);
+
InSequence seq;
MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
- auto mock_image_request = new MockImageRequest();
- expect_is_write_op(*mock_image_request, true);
+ expect_is_write_op(*mock_queued_image_request, true);
expect_queue(mock_image_request_wq);
auto *aio_comp = new librbd::io::AioCompletion();
mock_image_request_wq.aio_write(aio_comp, 0, 0, {}, 0);
- expect_front(mock_image_request_wq, mock_image_request);
+ expect_front(mock_image_request_wq, mock_queued_image_request);
expect_is_refresh_request(mock_image_ctx, true);
- expect_is_write_op(*mock_image_request, true);
- expect_dequeue(mock_image_request_wq, mock_image_request);
+ expect_is_write_op(*mock_queued_image_request, true);
+ expect_dequeue(mock_image_request_wq, mock_queued_image_request);
Context *on_refresh = nullptr;
expect_refresh(mock_image_ctx, &on_refresh);
ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr);
ASSERT_TRUE(on_refresh != nullptr);
expect_process_finish(mock_image_request_wq);
- expect_fail(*mock_image_request, -EPERM);
- expect_is_write_op(*mock_image_request, true);
+ expect_fail(*mock_queued_image_request, -EPERM);
+ expect_is_write_op(*mock_queued_image_request, true);
expect_signal(mock_image_request_wq);
on_refresh->complete(-EPERM);
aio_comp->release();
}
+TEST_F(TestMockIoImageRequestWQ, QosNoLimit) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatchSpec mock_queued_image_request;
+ expect_was_throttled(mock_queued_image_request, false);
+ expect_set_throttled(mock_queued_image_request);
+
+ InSequence seq;
+ MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
+
+ mock_image_request_wq.apply_qos_limit(RBD_QOS_BPS_THROTTLE, 0, 0);
+
+ expect_front(mock_image_request_wq, &mock_queued_image_request);
+ expect_is_refresh_request(mock_image_ctx, false);
+ expect_is_write_op(mock_queued_image_request, true);
+ expect_dequeue(mock_image_request_wq, &mock_queued_image_request);
+ expect_start_op(mock_queued_image_request);
+ ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == &mock_queued_image_request);
+}
+
+TEST_F(TestMockIoImageRequestWQ, BPSQosNoBurst) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatchSpec mock_queued_image_request;
+ expect_was_throttled(mock_queued_image_request, false);
+ expect_set_throttled(mock_queued_image_request);
+
+ InSequence seq;
+ MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
+
+ mock_image_request_wq.apply_qos_limit(RBD_QOS_BPS_THROTTLE, 1, 0);
+
+ expect_front(mock_image_request_wq, &mock_queued_image_request);
+ expect_tokens_requested(mock_queued_image_request, 2);
+ expect_dequeue(mock_image_request_wq, &mock_queued_image_request);
+ expect_all_throttled(mock_queued_image_request, true);
+ expect_requeue(mock_image_request_wq);
+ expect_signal(mock_image_request_wq);
+ ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr);
+}
+
+TEST_F(TestMockIoImageRequestWQ, BPSQosWithBurst) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+
+ MockImageDispatchSpec mock_queued_image_request;
+ expect_was_throttled(mock_queued_image_request, false);
+ expect_set_throttled(mock_queued_image_request);
+
+ InSequence seq;
+ MockImageRequestWQ mock_image_request_wq(&mock_image_ctx, "io", 60, nullptr);
+
+ mock_image_request_wq.apply_qos_limit(RBD_QOS_BPS_THROTTLE, 1, 1);
+
+ expect_front(mock_image_request_wq, &mock_queued_image_request);
+ expect_tokens_requested(mock_queued_image_request, 2);
+ expect_dequeue(mock_image_request_wq, &mock_queued_image_request);
+ expect_all_throttled(mock_queued_image_request, true);
+ expect_requeue(mock_image_request_wq);
+ expect_signal(mock_image_request_wq);
+ ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr);
+}
+
} // namespace io
} // namespace librbd