}
template <typename I>
-void ImageWatcher<I>::notify_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
+void ImageWatcher<I>::notify_snap_create(uint64_t request_id,
+ const cls::rbd::SnapshotNamespace &snap_namespace,
const std::string &snap_name,
+ ProgressContext &prog_ctx,
Context *on_finish) {
ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock));
ceph_assert(m_image_ctx.exclusive_lock &&
!m_image_ctx.exclusive_lock->is_lock_owner());
- notify_lock_owner(new SnapCreatePayload(snap_namespace, snap_name),
- on_finish);
+ AsyncRequestId async_request_id(get_client_id(), request_id);
+
+ notify_async_request(async_request_id,
+ new SnapCreatePayload(async_request_id, snap_namespace,
+ snap_name),
+ prog_ctx, on_finish);
}
template <typename I>
}
template <typename I>
-void ImageWatcher<I>::notify_quiesce(uint64_t request_id, Context *on_finish) {
+void ImageWatcher<I>::notify_quiesce(uint64_t request_id,
+ ProgressContext &prog_ctx,
+ Context *on_finish) {
ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": request_id="
<< request_id << dendl;
auto attempts = m_image_ctx.config.template get_val<uint64_t>(
"rbd_quiesce_notification_attempts");
- notify_quiesce(async_request_id, attempts, on_finish);
+ notify_quiesce(async_request_id, attempts, prog_ctx, on_finish);
}
template <typename I>
void ImageWatcher<I>::notify_quiesce(const AsyncRequestId &async_request_id,
- size_t attempts, Context *on_finish) {
+ size_t attempts, ProgressContext &prog_ctx,
+ Context *on_finish) {
ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": async_request_id="
<< async_request_id << " attempts=" << attempts
<< dendl;
ceph_assert(attempts > 0);
auto on_notify = new LambdaContext(
- [this, async_request_id, on_finish, attempts=attempts-1](int r) {
+ [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");
+ prog_ctx.update_progress(total_attempts - attempts, total_attempts);
+
if (r == -ETIMEDOUT) {
ldout(m_image_ctx.cct, 10) << this << " " << __func__ << ": async_request_id="
<< async_request_id << " timed out" << dendl;
if (attempts > 0) {
- notify_quiesce(async_request_id, attempts, on_finish);
+ notify_quiesce(async_request_id, attempts, prog_ctx, on_finish);
return;
}
}
}
if (m_image_ctx.exclusive_lock->accept_request(request_type, &r)) {
- ldout(m_image_ctx.cct, 10) << this << " remote snap_create request: "
- << payload.snap_name << dendl;
+ bool new_request;
+ Context *ctx;
+ ProgressContext *prog_ctx;
+ bool complete;
+ if (payload.async_request_id) {
+ r = prepare_async_request(payload.async_request_id, &new_request,
+ &ctx, &prog_ctx);
+ encode(ResponseMessage(r), ack_ctx->out);
+ complete = true;
+ } else {
+ new_request = true;
+ prog_ctx = new NoOpProgressContext();
+ ctx = new LambdaContext(
+ [prog_ctx, on_finish=new C_ResponseMessage(ack_ctx)](int r) {
+ delete prog_ctx;
+ on_finish->complete(r);
+ });
+ complete = false;
+ }
+ if (r == 0 && new_request) {
+ ldout(m_image_ctx.cct, 10) << this << " remote snap_create request: "
+ << payload.async_request_id << " "
+ << payload.snap_namespace << " "
+ << payload.snap_name << dendl;
- m_image_ctx.operations->execute_snap_create(payload.snap_namespace,
- payload.snap_name,
- new C_ResponseMessage(ack_ctx),
- 0, false);
- return false;
+ m_image_ctx.operations->execute_snap_create(payload.snap_namespace,
+ payload.snap_name,
+ ctx, 0, false, *prog_ctx);
+ }
+ return complete;
} else if (r < 0) {
encode(ResponseMessage(r), ack_ctx->out);
}
Context *on_finish);
void notify_resize(uint64_t request_id, uint64_t size, bool allow_shrink,
ProgressContext &prog_ctx, Context *on_finish);
- void notify_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
+ void notify_snap_create(uint64_t request_id,
+ const cls::rbd::SnapshotNamespace &snap_namespace,
const std::string &snap_name,
+ ProgressContext &prog_ctx,
Context *on_finish);
void notify_snap_rename(const snapid_t &src_snap_id,
const std::string &dst_snap_name,
static void notify_header_update(librados::IoCtx &io_ctx,
const std::string &oid);
- void notify_quiesce(uint64_t request_id, Context *on_finish);
+ void notify_quiesce(uint64_t request_id, ProgressContext &prog_ctx,
+ Context *on_finish);
void notify_unquiesce(uint64_t request_id, Context *on_finish);
private:
Context *prepare_unquiesce_request(const watch_notify::AsyncRequestId &request);
void notify_quiesce(const watch_notify::AsyncRequestId &async_request_id,
- size_t attempts, Context *on_finish);
+ size_t attempts, ProgressContext &prog_ctx,
+ Context *on_finish);
bool handle_payload(const watch_notify::HeaderUpdatePayload& payload,
C_NotifyAck *ctx);
}
m_image_ctx.image_lock.unlock_shared();
+ auto prog_ctx = new NoOpProgressContext();
+ on_finish = new LambdaContext(
+ [prog_ctx, on_finish](int r) {
+ delete prog_ctx;
+ on_finish->complete(r);
+ });
+
+ uint64_t request_id = ++m_async_request_seq;
C_InvokeAsyncRequest<I> *req = new C_InvokeAsyncRequest<I>(
m_image_ctx, "snap_create", exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL,
true,
boost::bind(&Operations<I>::execute_snap_create, this, snap_namespace, snap_name,
- _1, 0, false),
+ _1, 0, false, boost::ref(*prog_ctx)),
boost::bind(&ImageWatcher<I>::notify_snap_create, m_image_ctx.image_watcher,
- snap_namespace, snap_name, _1),
+ request_id, snap_namespace, snap_name, boost::ref(*prog_ctx),
+ _1),
{-EEXIST}, on_finish);
req->send();
}
const std::string &snap_name,
Context *on_finish,
uint64_t journal_op_tid,
- bool skip_object_map) {
+ bool skip_object_map,
+ ProgressContext &prog_ctx) {
ceph_assert(ceph_mutex_is_locked(m_image_ctx.owner_lock));
ceph_assert(m_image_ctx.exclusive_lock == nullptr ||
m_image_ctx.exclusive_lock->is_lock_owner());
operation::SnapshotCreateRequest<I> *req =
new operation::SnapshotCreateRequest<I>(
m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish),
- snap_namespace, snap_name, journal_op_tid, request_id, skip_object_map);
+ snap_namespace, snap_name, journal_op_tid, request_id, skip_object_map,
+ prog_ctx);
req->send();
}
void execute_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
const std::string &snap_name,
Context *on_finish,
- uint64_t journal_op_tid, bool skip_object_map);
+ uint64_t journal_op_tid, bool skip_object_map,
+ ProgressContext &prog_ctx);
int snap_rollback(const cls::rbd::SnapshotNamespace& snap_namespace,
const std::string& snap_name,
}
void SnapCreatePayload::encode(bufferlist &bl) const {
+ using ceph::encode;
SnapPayloadBase::encode(bl);
+ encode(async_request_id, bl);
}
void SnapCreatePayload::decode(__u8 version, bufferlist::const_iterator &iter) {
if (version == 5) {
decode(snap_namespace, iter);
}
+ if (version >= 7) {
+ decode(async_request_id, iter);
+ }
}
void SnapCreatePayload::dump(Formatter *f) const {
+ f->open_object_section("async_request_id");
+ async_request_id.dump(f);
+ f->close_section();
SnapPayloadBase::dump(f);
}
}
void NotifyMessage::encode(bufferlist& bl) const {
- ENCODE_START(6, 1, bl);
+ ENCODE_START(7, 1, bl);
encode(static_cast<uint32_t>(payload->get_notify_op()), bl);
payload->encode(bl);
ENCODE_FINISH(bl);
o.push_back(new NotifyMessage(new AsyncCompletePayload(AsyncRequestId(ClientId(0, 1), 2), 3)));
o.push_back(new NotifyMessage(new FlattenPayload(AsyncRequestId(ClientId(0, 1), 2))));
o.push_back(new NotifyMessage(new ResizePayload(123, true, AsyncRequestId(ClientId(0, 1), 2))));
- o.push_back(new NotifyMessage(new SnapCreatePayload(cls::rbd::UserSnapshotNamespace(),
+ o.push_back(new NotifyMessage(new SnapCreatePayload(AsyncRequestId(ClientId(0, 1), 2),
+ cls::rbd::UserSnapshotNamespace(),
"foo")));
o.push_back(new NotifyMessage(new SnapRemovePayload(cls::rbd::UserSnapshotNamespace(), "foo")));
o.push_back(new NotifyMessage(new SnapProtectPayload(cls::rbd::UserSnapshotNamespace(), "foo")));
inline bool operator!=(const AsyncRequestId &rhs) const {
return (client_id != rhs.client_id || request_id != rhs.request_id);
}
+ inline operator bool() const {
+ return (*this != AsyncRequestId());
+ }
};
enum NotifyOp {
};
struct SnapCreatePayload : public SnapPayloadBase {
+ AsyncRequestId async_request_id;
+
SnapCreatePayload() {}
- SnapCreatePayload(const cls::rbd::SnapshotNamespace &_snap_namespace,
+ SnapCreatePayload(const AsyncRequestId &id,
+ const cls::rbd::SnapshotNamespace &snap_namespace,
const std::string &name)
- : SnapPayloadBase(_snap_namespace, name) {}
+ : SnapPayloadBase(snap_namespace, name), async_request_id(id) {
+ }
NotifyOp get_notify_op() const override {
return NOTIFY_OP_SNAP_CREATE;
});
std::shared_lock owner_locker{m_dst_image_ctx->owner_lock};
m_dst_image_ctx->operations->execute_snap_create(m_snap_namespace,
- m_snap_name.c_str(),
- ctx,
- 0U, true);
+ m_snap_name.c_str(), ctx, 0U,
+ true, m_prog_ctx);
}
template <typename I>
#include "common/snap_types.h"
#include "librbd/ImageCtx.h"
#include "librbd/Types.h"
+#include "librbd/internal.h"
+
#include <map>
#include <set>
#include <string>
Context *m_on_finish;
CephContext *m_cct;
+ NoOpProgressContext m_prog_ctx;
void send_set_head();
void handle_set_head(int r);
image_ctx.operations->execute_snap_create(event.snap_namespace,
event.snap_name,
on_op_complete,
- event.op_tid, false);
+ event.op_tid, false,
+ no_op_progress_callback);
}
void execute(const journal::SnapRemoveEvent &_) {
const std::string &snap_name,
uint64_t journal_op_tid,
uint64_t request_id,
- bool skip_object_map)
+ bool skip_object_map,
+ ProgressContext &prog_ctx)
: Request<I>(image_ctx, on_finish, journal_op_tid),
m_snap_namespace(snap_namespace), m_snap_name(snap_name),
- m_request_id(request_id), m_skip_object_map(skip_object_map) {
+ m_request_id(request_id), m_skip_object_map(skip_object_map),
+ m_prog_ctx(prog_ctx) {
}
template <typename I>
ldout(cct, 5) << this << " " << __func__ << dendl;
image_ctx.image_watcher->notify_quiesce(
- m_request_id, create_async_context_callback(
+ m_request_id, m_prog_ctx, create_async_context_callback(
image_ctx, create_context_callback<SnapshotCreateRequest<I>,
&SnapshotCreateRequest<I>::handle_notify_quiesce>(this)));
}
namespace librbd {
class ImageCtx;
+class ProgressContext;
namespace operation {
SnapshotCreateRequest(ImageCtxT &image_ctx, Context *on_finish,
const cls::rbd::SnapshotNamespace &snap_namespace,
const std::string &snap_name, uint64_t journal_op_tid,
- uint64_t request_id, bool skip_object_map);
+ uint64_t request_id, bool skip_object_map,
+ ProgressContext &prog_ctx);
protected:
void send_op() override;
std::string m_snap_name;
uint64_t m_request_id;
bool m_skip_object_map;
+ ProgressContext &m_prog_ctx;
int m_ret_val = 0;
void expect_snap_create(librbd::MockTestImageCtx &mock_image_ctx,
const std::string &snap_name, uint64_t snap_id, int r) {
- EXPECT_CALL(*mock_image_ctx.operations, execute_snap_create(_, StrEq(snap_name), _, 0, true))
+ EXPECT_CALL(*mock_image_ctx.operations,
+ execute_snap_create(_, StrEq(snap_name), _, 0, true, _))
.WillOnce(DoAll(InvokeWithoutArgs([&mock_image_ctx, snap_id, snap_name]() {
inject_snap(mock_image_ctx, snap_id, snap_name);
}),
Context **on_finish, const char *snap_name,
uint64_t op_tid) {
EXPECT_CALL(*mock_image_ctx.operations, execute_snap_create(_, StrEq(snap_name), _,
- op_tid, false))
+ op_tid, false, _))
.WillOnce(DoAll(SaveArg<2>(on_finish),
NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
namespace librbd {
+class ProgressContext;
+
struct MockImageWatcher {
MOCK_METHOD0(is_registered, bool());
MOCK_METHOD0(is_unregistered, bool());
MOCK_METHOD0(notify_acquired_lock, void());
MOCK_METHOD0(notify_released_lock, void());
MOCK_METHOD0(notify_request_lock, void());
-
- MOCK_METHOD2(notify_quiesce, void(uint64_t, Context *));
+
+ MOCK_METHOD3(notify_quiesce, void(uint64_t, ProgressContext &, Context *));
MOCK_METHOD2(notify_unquiesce, void(uint64_t, Context *));
};
MOCK_METHOD3(snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
const std::string &snap_name,
Context *on_finish));
- MOCK_METHOD5(execute_snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
+ MOCK_METHOD6(execute_snap_create, void(const cls::rbd::SnapshotNamespace &snapshot_namespace,
const std::string &snap_name,
Context *on_finish,
uint64_t journal_op_tid,
- bool skip_object_map));
+ bool skip_object_map,
+ ProgressContext &prog_ctx));
MOCK_METHOD3(snap_remove, void(const cls::rbd::SnapshotNamespace &snap_namespace,
const std::string &snap_name,
Context *on_finish));
typedef mirror::snapshot::SetImageStateRequest<MockImageCtx> MockSetImageStateRequest;
void expect_notify_quiesce(MockImageCtx &mock_image_ctx, int r) {
- EXPECT_CALL(*mock_image_ctx.image_watcher, notify_quiesce(_, _))
- .WillOnce(WithArg<1>(
+ EXPECT_CALL(*mock_image_ctx.image_watcher, notify_quiesce(_, _, _))
+ .WillOnce(WithArg<2>(
CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
}
expect_notify_unquiesce(mock_image_ctx, -EINVAL);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
- "snap1", 0, 0, false);
+ "snap1", 0, 0, false, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
expect_notify_quiesce(mock_image_ctx, -EINVAL);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
- "snap1", 0, 0, false);
+ "snap1", 0, 0, false, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
expect_notify_unquiesce(mock_image_ctx, 0);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
- "snap1", 0, 0, false);
+ "snap1", 0, 0, false, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
expect_notify_unquiesce(mock_image_ctx, 0);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
- "snap1", 0, 0, false);
+ "snap1", 0, 0, false, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
expect_notify_unquiesce(mock_image_ctx, 0);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
- "snap1", 0, 0, false);
+ "snap1", 0, 0, false, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
expect_notify_unquiesce(mock_image_ctx, 0);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
- "snap1", 0, 0, false);
+ "snap1", 0, 0, false, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
expect_notify_unquiesce(mock_image_ctx, 0);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(),
- "snap1", 0, 0, true);
+ "snap1", 0, 0, true, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
expect_notify_unquiesce(mock_image_ctx, 0);
C_SaferCond cond_ctx;
+ librbd::NoOpProgressContext prog_ctx;
MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
mock_image_ctx, &cond_ctx,
cls::rbd::MirrorSnapshotNamespace{
cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {}, "", CEPH_NOSNAP},
- "snap1", 0, 0, false);
+ "snap1", 0, 0, false, prog_ctx);
{
std::shared_lock owner_locker{mock_image_ctx.owner_lock};
req->send();
bufferlist payload = m_notify_payloads[op];
auto iter = payload.cbegin();
-
+
switch (op) {
case NOTIFY_OP_FLATTEN:
{
*id = payload.async_request_id;
}
return true;
+ case NOTIFY_OP_SNAP_CREATE:
+ {
+ SnapCreatePayload payload;
+ payload.decode(7, iter);
+ *id = payload.async_request_id;
+ }
+ return true;
case NOTIFY_OP_REBUILD_OBJECT_MAP:
{
RebuildObjectMapPayload payload;
}
};
+struct SnapCreateTask {
+ librbd::ImageCtx *ictx;
+ ProgressContext *progress_context;
+ int result;
+
+ SnapCreateTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
+ : ictx(ictx_), progress_context(ctx), result(0) {}
+
+ void operator()() {
+ std::shared_lock l{ictx->owner_lock};
+ C_SaferCond ctx;
+ ictx->image_watcher->notify_snap_create(0, cls::rbd::UserSnapshotNamespace(),
+ "snap", *progress_context, &ctx);
+ ASSERT_EQ(0, ctx.wait());
+ }
+};
+
struct RebuildObjectMapTask {
librbd::ImageCtx *ictx;
ProgressContext *progress_context;
m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(0)}};
- std::shared_lock l{ictx->owner_lock};
- C_SaferCond notify_ctx;
- ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(),
- "snap", ¬ify_ctx);
- ASSERT_EQ(0, notify_ctx.wait());
+ ProgressContext progress_context;
+ SnapCreateTask snap_create_task(ictx, &progress_context);
+ boost::thread thread(boost::ref(snap_create_task));
+
+ ASSERT_TRUE(wait_for_notifies(*ictx));
NotifyOps expected_notify_ops;
expected_notify_ops += NOTIFY_OP_SNAP_CREATE;
ASSERT_EQ(expected_notify_ops, m_notifies);
+
+ AsyncRequestId async_request_id;
+ ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_SNAP_CREATE,
+ &async_request_id));
+
+ ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 1, 10));
+ ASSERT_TRUE(progress_context.wait(ictx, 1, 10));
+
+ ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
+
+ ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
+ ASSERT_EQ(0, snap_create_task.result);
}
TEST_F(TestImageWatcher, NotifySnapCreateError) {
std::shared_lock l{ictx->owner_lock};
C_SaferCond notify_ctx;
- ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(),
- "snap", ¬ify_ctx);
+ librbd::NoOpProgressContext prog_ctx;
+ ictx->image_watcher->notify_snap_create(0, cls::rbd::UserSnapshotNamespace(),
+ "snap", prog_ctx, ¬ify_ctx);
ASSERT_EQ(-EEXIST, notify_ctx.wait());
NotifyOps expected_notify_ops;