<< dendl;
ceph_assert(attempts > 0);
+ auto notify_response = new watcher::NotifyResponse();
auto on_notify = new LambdaContext(
- [this, async_request_id, &prog_ctx, on_finish, attempts=attempts-1](int r) {
+ [notify_response=std::unique_ptr<watcher::NotifyResponse>(notify_response),
+ this, async_request_id, &prog_ctx, on_finish, attempts=attempts-1](int r) {
auto total_attempts = m_image_ctx.config.template get_val<uint64_t>(
"rbd_quiesce_notification_attempts");
+ if (total_attempts < attempts) {
+ total_attempts = attempts;
+ }
prog_ctx.update_progress(total_attempts - attempts, total_attempts);
if (r == -ETIMEDOUT) {
notify_quiesce(async_request_id, attempts, prog_ctx, on_finish);
return;
}
+ } else if (r == 0) {
+ for (auto &[client_id, bl] : notify_response->acks) {
+ if (bl.length() == 0) {
+ continue;
+ }
+ try {
+ auto iter = bl.cbegin();
+
+ ResponseMessage response_message;
+ using ceph::decode;
+ decode(response_message, iter);
+
+ if (response_message.result != -EOPNOTSUPP) {
+ r = response_message.result;
+ }
+ } catch (const buffer::error &err) {
+ r = -EINVAL;
+ }
+ if (r < 0) {
+ break;
+ }
+ }
}
if (r < 0) {
lderr(m_image_ctx.cct) << this << " failed to notify quiesce: "
on_finish->complete(r);
});
- send_notify(new QuiescePayload(async_request_id), on_notify);
+ bufferlist bl;
+ encode(NotifyMessage(new QuiescePayload(async_request_id)), bl);
+ Watcher::send_notify(bl, notify_response, on_notify);
}
template <typename I>
return new LambdaContext(
[this, request, unquiesce_ctx, timeout](int r) {
- ceph_assert(r == 0);
-
- m_task_finisher->add_event_after(Task(TASK_CODE_QUIESCE, request),
- timeout, unquiesce_ctx);
-
+ if (r < 0) {
+ std::unique_lock async_request_locker{m_async_request_lock};
+ m_async_pending.erase(request);
+ } else {
+ m_task_finisher->add_event_after(Task(TASK_CODE_QUIESCE, request),
+ timeout, unquiesce_ctx);
+ }
auto ctx = remove_async_request(request);
ceph_assert(ctx != nullptr);
ctx = new C_ResponseMessage(static_cast<C_NotifyAck *>(ctx));
void handle_quiesce() {
ASSERT_EQ(quiesce_count, unquiesce_count);
quiesce_count++;
- rbd_quiesce_complete(image);
+ rbd_quiesce_complete(image, 0);
}
void handle_unquiesce() {
unquiesce_count++;
void handle_quiesce() override {
ASSERT_EQ(quiesce_count, unquiesce_count);
quiesce_count++;
- image.quiesce_complete();
+ image.quiesce_complete(0);
}
void handle_unquiesce() override {
unquiesce_count++;
ioctx.close();
}
+TEST_F(TestLibRBD, QuiesceWatchError)
+{
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ {
+ librbd::Image image1, image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ struct Watcher : public librbd::QuiesceWatchCtx {
+ librbd::Image ℑ
+ int r;
+ mutex m_lock;
+ condition_variable m_cond;
+ size_t quiesce_count = 0;
+ size_t unquiesce_count = 0;
+
+ Watcher(librbd::Image &image, int r) : image(image), r(r) {
+ }
+
+ void handle_quiesce() override {
+ lock_guard<mutex> locker(m_lock);
+ quiesce_count++;
+ image.quiesce_complete(r);
+ m_cond.notify_one();
+ }
+
+ void handle_unquiesce() override {
+ lock_guard<mutex> locker(m_lock);
+ unquiesce_count++;
+ m_cond.notify_one();
+ }
+
+ void wait_for_unquiesce_count(size_t count) {
+ unique_lock<mutex> locker(m_lock);
+ ASSERT_TRUE(m_cond.wait_for(locker, seconds(60),
+ [this, count] {
+ return this->unquiesce_count == count;
+ }));
+ }
+ } watcher1(image1, -EINVAL), watcher2(image2, 0);
+ uint64_t handle1, handle2;
+
+ ASSERT_EQ(0, image1.quiesce_watch(&watcher1, &handle1));
+ ASSERT_EQ(0, image2.quiesce_watch(&watcher2, &handle2));
+
+ ASSERT_EQ(-EINVAL, image1.snap_create("snap1"));
+ ASSERT_EQ(1U, watcher2.quiesce_count);
+ watcher2.wait_for_unquiesce_count(1U);
+ ASSERT_EQ(1U, watcher1.quiesce_count);
+ ASSERT_EQ(0U, watcher1.unquiesce_count);
+
+ ASSERT_EQ(-EINVAL, image2.snap_create("snap2"));
+ ASSERT_EQ(2U, watcher2.quiesce_count);
+ watcher2.wait_for_unquiesce_count(2U);
+ ASSERT_EQ(2U, watcher1.quiesce_count);
+ ASSERT_EQ(0U, watcher1.unquiesce_count);
+
+ ASSERT_EQ(0, image1.quiesce_unwatch(handle1));
+
+ ASSERT_EQ(0, image1.snap_create("snap3"));
+ ASSERT_EQ(3U, watcher2.quiesce_count);
+ ASSERT_EQ(3U, watcher2.unquiesce_count);
+
+ ASSERT_EQ(0, image2.quiesce_unwatch(handle2));
+
+ ASSERT_EQ(0, image1.snap_remove("snap3"));
+ }
+
+ ASSERT_EQ(0, rbd.remove(ioctx, name.c_str()));
+ ioctx.close();
+}
+
TEST_F(TestLibRBD, QuiesceWatchTimeout)
{
REQUIRE(!is_librados_test_stub(_rados));
thread quiesce1([&image, &watcher]() {
watcher.wait_for_quiesce_count(1);
sleep(8);
- image.quiesce_complete();
+ image.quiesce_complete(0);
});
ASSERT_EQ(0, image.snap_create("snap1"));
std::cerr << "waiting for timed out ... " << i << std::endl;
sleep(1);
}
- image.quiesce_complete();
+ image.quiesce_complete(0);
});
ASSERT_EQ(-ETIMEDOUT, image.snap_create("snap2"));
thread quiesce3([&image, &watcher]() {
watcher.wait_for_quiesce_count(3);
- image.quiesce_complete();
+ image.quiesce_complete(0);
});
std::cerr << "test retry succeeds" << std::endl;