From: Jason Dillaman Date: Fri, 11 Sep 2020 19:20:45 +0000 (-0400) Subject: librbd: ensure that thread pool lock is held when processing throttled IOs X-Git-Tag: v15.2.9~122^2~91^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=2d86e0935aa6f0c392df428676d9ab0a338fccae;p=ceph.git librbd: ensure that thread pool lock is held when processing throttled IOs There previously was a potential race for throttled IOs to complete prior to the main worker thread finishing the processing of the blocked IO. Fixes: https://tracker.ceph.com/issues/47371 Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/io/ImageRequestWQ.cc b/src/librbd/io/ImageRequestWQ.cc index 94dd982180f..1ff272fc281 100644 --- a/src/librbd/io/ImageRequestWQ.cc +++ b/src/librbd/io/ImageRequestWQ.cc @@ -871,21 +871,23 @@ void ImageRequestWQ::apply_qos_limit(const uint64_t flag, } template -void ImageRequestWQ::handle_throttle_ready(int r, ImageDispatchSpec *item, uint64_t flag) { +void ImageRequestWQ::handle_throttle_ready(int r, ImageDispatchSpec *item, + uint64_t flag) { CephContext *cct = m_image_ctx.cct; ldout(cct, 15) << "r=" << r << ", " << "req=" << item << dendl; + std::lock_guard pool_locker{this->get_pool_lock()}; ceph_assert(m_io_throttled.load() > 0); item->set_throttled(flag); if (item->were_all_throttled()) { - this->requeue_back(item); + this->requeue_back(pool_locker, item); --m_io_throttled; - this->signal(); + this->signal(pool_locker); } } template -bool ImageRequestWQ::needs_throttle(ImageDispatchSpec *item) { +bool ImageRequestWQ::needs_throttle(ImageDispatchSpec *item) { uint64_t tokens = 0; uint64_t flag = 0; bool blocked = false; diff --git a/src/test/librbd/io/test_mock_ImageRequestWQ.cc b/src/test/librbd/io/test_mock_ImageRequestWQ.cc index 8ebea73cf85..39a5e5d11c6 100644 --- a/src/test/librbd/io/test_mock_ImageRequestWQ.cc +++ b/src/test/librbd/io/test_mock_ImageRequestWQ.cc @@ -105,11 +105,13 @@ struct ThreadPool::PointerWQ&)); MOCK_METHOD0(process_finish, void()); MOCK_METHOD0(front, ImageDispatchSpec*()); MOCK_METHOD1(requeue_front, void(ImageDispatchSpec*)); - MOCK_METHOD1(requeue_back, void(ImageDispatchSpec*)); + MOCK_METHOD2(requeue_back, void(const std::lock_guard&, + ImageDispatchSpec*)); MOCK_METHOD0(dequeue, void*()); MOCK_METHOD1(queue, void(ImageDispatchSpec*)); @@ -171,12 +173,16 @@ struct TestMockIoImageRequestWQ : public TestMockFixture { EXPECT_CALL(image_request_wq, signal()); } + void expect_signal_locked(MockImageRequestWQ &image_request_wq) { + EXPECT_CALL(image_request_wq, signal(_)); + } + void expect_queue(MockImageRequestWQ &image_request_wq) { EXPECT_CALL(image_request_wq, queue(_)); } void expect_requeue_back(MockImageRequestWQ &image_request_wq) { - EXPECT_CALL(image_request_wq, requeue_back(_)); + EXPECT_CALL(image_request_wq, requeue_back(_, _)); } void expect_front(MockImageRequestWQ &image_request_wq, @@ -445,7 +451,7 @@ TEST_F(TestMockIoImageRequestWQ, BPSQosNoBurst) { expect_dequeue(mock_image_request_wq, &mock_queued_image_request); expect_all_throttled(mock_queued_image_request, true); expect_requeue_back(mock_image_request_wq); - expect_signal(mock_image_request_wq); + expect_signal_locked(mock_image_request_wq); ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr); } @@ -469,7 +475,7 @@ TEST_F(TestMockIoImageRequestWQ, BPSQosWithBurst) { expect_dequeue(mock_image_request_wq, &mock_queued_image_request); expect_all_throttled(mock_queued_image_request, true); expect_requeue_back(mock_image_request_wq); - expect_signal(mock_image_request_wq); + expect_signal_locked(mock_image_request_wq); ASSERT_TRUE(mock_image_request_wq.invoke_dequeue() == nullptr); }