bool read_only) {
return new ImageCtx(image_name, image_id, snap_id, p, read_only);
}
- void destroy() {
- delete this;
- }
/**
* Either image_name or image_id must be set.
#include "common/WorkQueue.h"
#include "librbd/AsioEngine.h"
#include "librbd/ImageCtx.h"
+#include "librbd/TaskFinisher.h"
#include "librbd/Utils.h"
#include "librbd/asio/ContextWQ.h"
#include "librbd/image/CloseRequest.h"
open(flags, &ctx);
int r = ctx.wait();
- if (r < 0) {
- delete m_image_ctx;
- }
return r;
}
close(&ctx);
int r = ctx.wait();
- delete m_image_ctx;
return r;
}
m_state = next_state;
m_lock.unlock();
- for (auto ctx : action_contexts.second) {
- ctx->complete(r);
- }
+ if (next_state == STATE_CLOSED ||
+ (next_state == STATE_UNINITIALIZED && r < 0)) {
+ // the ImageCtx must be deleted outside the scope of its callback threads
+ auto ctx = new LambdaContext(
+ [image_ctx=m_image_ctx, contexts=std::move(action_contexts.second)]
+ (int r) {
+ delete image_ctx;
+ for (auto ctx : contexts) {
+ ctx->complete(r);
+ }
+ });
+ TaskFinisherSingleton::get_singleton(m_image_ctx->cct).queue(ctx, r);
+ } else {
+ for (auto ctx : action_contexts.second) {
+ ctx->complete(r);
+ }
- if (next_state != STATE_UNINITIALIZED && next_state != STATE_CLOSED) {
m_lock.lock();
if (!is_transition_state() && !m_actions_contexts.empty()) {
execute_next_action_unlock();
SafeTimer *m_safe_timer;
Finisher *m_finisher;
+ static TaskFinisherSingleton& get_singleton(CephContext* cct) {
+ return cct->lookup_or_create_singleton_object<
+ TaskFinisherSingleton>("librbd::TaskFinisherSingleton", false, cct);
+ }
+
explicit TaskFinisherSingleton(CephContext *cct) {
m_safe_timer = new SafeTimer(cct, m_lock, false);
m_safe_timer->init();
m_finisher->stop();
delete m_finisher;
}
+
+ void queue(Context* ctx, int r) {
+ m_finisher->queue(ctx, r);
+ }
};
class TaskFinisher {
public:
TaskFinisher(CephContext &cct) : m_cct(cct) {
- auto& singleton =
- cct.lookup_or_create_singleton_object<TaskFinisherSingleton>(
- "librbd::TaskFinisher::m_safe_timer", false, &cct);
+ auto& singleton = TaskFinisherSingleton::get_singleton(&cct);
m_lock = &singleton.m_lock;
m_safe_timer = singleton.m_safe_timer;
m_finisher = singleton.m_finisher;
r = on_finishes[i]->wait();
delete on_finishes[i];
if (r < 0) {
- delete ictxs[i];
ictxs[i] = nullptr;
ret_code = r;
}
r = on_finishes[i]->wait();
delete on_finishes[i];
if (r < 0) {
- delete ictxs[i];
ictxs[i] = nullptr;
ret_code = r;
}
r = on_finishes[i]->wait();
delete on_finishes[i];
if (r < 0) {
- delete ictxs[i];
ictxs[i] = nullptr;
ret_code = r;
}
#include "librbd/Utils.h"
#include "librbd/deep_copy/Handler.h"
#include "librbd/deep_copy/Utils.h"
-#include "librbd/image/CloseRequest.h"
-#include "librbd/image/OpenRequest.h"
#include "librbd/object_map/DiffRequest.h"
#include "osdc/Striper.h"
ldout(m_cct, 20) << "r=" << r << dendl;
if (r < 0) {
- m_parent_image_ctx->destroy();
m_parent_image_ctx = nullptr;
lderr(m_cct) << "failed to open parent image: " << cpp_strerror(r) << dendl;
ldout(m_cct, 15) << "r=" << r << dendl;
if (r < 0) {
- m_imctx->destroy();
m_imctx = nullptr;
lderr(m_cct) << "Error opening new image: " << cpp_strerror(r) << dendl;
ceph_assert(m_imctx != nullptr);
- using klass = CloneRequest<I>;
- Context *ctx = create_async_context_callback(
- *m_imctx, create_context_callback<
- klass, &klass::handle_close_child>(this));
+ auto ctx = create_context_callback<
+ CloneRequest<I>, &CloneRequest<I>::handle_close_child>(this);
m_imctx->state->close(ctx);
}
void CloneRequest<I>::handle_close_child(int r) {
ldout(m_cct, 15) << dendl;
- m_imctx->destroy();
m_imctx = nullptr;
if (r < 0) {
ldout(m_cct, 20) << dendl;
ceph_assert(m_parent_image_ctx != nullptr);
- Context *ctx = create_async_context_callback(
- *m_parent_image_ctx, create_context_callback<
- CloneRequest<I>, &CloneRequest<I>::handle_close_parent>(this));
+ auto ctx = create_context_callback<
+ CloneRequest<I>, &CloneRequest<I>::handle_close_parent>(this);
m_parent_image_ctx->state->close(ctx);
}
void CloneRequest<I>::handle_close_parent(int r) {
ldout(m_cct, 20) << "r=" << r << dendl;
- m_parent_image_ctx->destroy();
m_parent_image_ctx = nullptr;
if (r < 0) {
CephContext *cct = m_image_ctx->cct;
ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl;
- delete m_image_ctx->parent;
m_image_ctx->parent = nullptr;
save_result(r);
if (r < 0) {
if (r < 0) {
ldout(cct, 5) << "failed to open parent for read/write: "
<< cpp_strerror(r) << dendl;
- m_parent_image_ctx->destroy();
m_parent_image_ctx = nullptr;
finish(0);
return;
<< dendl;
}
- m_parent_image_ctx->destroy();
m_parent_image_ctx = nullptr;
finish(0);
}
#include "common/dout.h"
#include "common/errno.h"
#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
#include "librbd/Utils.h"
#include "librbd/asio/ContextWQ.h"
-#include "librbd/image/CloseRequest.h"
-#include "librbd/image/OpenRequest.h"
#include "librbd/io/ObjectDispatcherInterface.h"
#define dout_subsys ceph_subsys_rbd
m_parent_image_ctx->set_read_flag(librados::OPERATION_LOCALIZE_READS);
}
- using klass = RefreshParentRequest<I>;
- Context *ctx = create_async_context_callback(
+ auto ctx = create_async_context_callback(
m_child_image_ctx, create_context_callback<
- klass, &klass::handle_open_parent, false>(this));
- OpenRequest<I> *req = OpenRequest<I>::create(m_parent_image_ctx, flags, ctx);
- req->send();
+ RefreshParentRequest<I>,
+ &RefreshParentRequest<I>::handle_open_parent, false>(this));
+ m_parent_image_ctx->state->open(flags, ctx);
}
template <typename I>
<< dendl;
// image already closed by open state machine
- delete m_parent_image_ctx;
m_parent_image_ctx = nullptr;
}
CephContext *cct = m_child_image_ctx.cct;
ldout(cct, 10) << this << " " << __func__ << dendl;
- using klass = RefreshParentRequest<I>;
- Context *ctx = create_async_context_callback(
+ auto ctx = create_async_context_callback(
m_child_image_ctx, create_context_callback<
- klass, &klass::handle_close_parent, false>(this));
- CloseRequest<I> *req = CloseRequest<I>::create(m_parent_image_ctx, ctx);
- req->send();
+ RefreshParentRequest<I>,
+ &RefreshParentRequest<I>::handle_close_parent, false>(this));
+ m_parent_image_ctx->state->close(ctx);
}
template <typename I>
CephContext *cct = m_child_image_ctx.cct;
ldout(cct, 10) << this << " " << __func__ << " r=" << *result << dendl;
- delete m_parent_image_ctx;
m_parent_image_ctx = nullptr;
if (*result < 0) {
ldout(m_cct, 20) << "r=" << r << dendl;
if (r < 0) {
- m_image_ctx->destroy();
m_image_ctx = nullptr;
if (r != -ENOENT) {
<< cpp_strerror(r) << dendl;
}
- m_image_ctx->destroy();
m_image_ctx = nullptr;
if (m_ret_val < 0) {
r = m_ret_val;
void AioCompletion::complete() {
ceph_assert(ictx != nullptr);
- CephContext *cct = ictx->cct;
ssize_t r = rval;
- tracepoint(librbd, aio_complete_enter, this, r);
- if (ictx->perfcounter != nullptr) {
- ceph::timespan elapsed = coarse_mono_clock::now() - start_time;
- switch (aio_type) {
- case AIO_TYPE_GENERIC:
- case AIO_TYPE_OPEN:
- case AIO_TYPE_CLOSE:
- break;
- case AIO_TYPE_READ:
- ictx->perfcounter->tinc(l_librbd_rd_latency, elapsed); break;
- case AIO_TYPE_WRITE:
- ictx->perfcounter->tinc(l_librbd_wr_latency, elapsed); break;
- case AIO_TYPE_DISCARD:
- ictx->perfcounter->tinc(l_librbd_discard_latency, elapsed); break;
- case AIO_TYPE_FLUSH:
- ictx->perfcounter->tinc(l_librbd_flush_latency, elapsed); break;
- case AIO_TYPE_WRITESAME:
- ictx->perfcounter->tinc(l_librbd_ws_latency, elapsed); break;
- case AIO_TYPE_COMPARE_AND_WRITE:
- ictx->perfcounter->tinc(l_librbd_cmp_latency, elapsed); break;
- default:
- lderr(cct) << "completed invalid aio_type: " << aio_type << dendl;
- break;
- }
- }
-
- if ((aio_type == AIO_TYPE_CLOSE) ||
- (aio_type == AIO_TYPE_OPEN && r < 0)) {
- // must destroy ImageCtx prior to invoking callback
- delete ictx;
+ if ((aio_type == AIO_TYPE_CLOSE) || (aio_type == AIO_TYPE_OPEN && r < 0)) {
ictx = nullptr;
external_callback = false;
+ } else {
+ CephContext *cct = ictx->cct;
+
+ tracepoint(librbd, aio_complete_enter, this, r);
+ if (ictx->perfcounter != nullptr) {
+ ceph::timespan elapsed = coarse_mono_clock::now() - start_time;
+ switch (aio_type) {
+ case AIO_TYPE_GENERIC:
+ case AIO_TYPE_OPEN:
+ break;
+ case AIO_TYPE_READ:
+ ictx->perfcounter->tinc(l_librbd_rd_latency, elapsed); break;
+ case AIO_TYPE_WRITE:
+ ictx->perfcounter->tinc(l_librbd_wr_latency, elapsed); break;
+ case AIO_TYPE_DISCARD:
+ ictx->perfcounter->tinc(l_librbd_discard_latency, elapsed); break;
+ case AIO_TYPE_FLUSH:
+ ictx->perfcounter->tinc(l_librbd_flush_latency, elapsed); break;
+ case AIO_TYPE_WRITESAME:
+ ictx->perfcounter->tinc(l_librbd_ws_latency, elapsed); break;
+ case AIO_TYPE_COMPARE_AND_WRITE:
+ ictx->perfcounter->tinc(l_librbd_cmp_latency, elapsed); break;
+ default:
+ lderr(cct) << "completed invalid aio_type: " << aio_type << dendl;
+ break;
+ }
+ }
}
state = AIO_STATE_CALLBACK;
void AioCompletion::fail(int r)
{
ceph_assert(ictx != nullptr);
- CephContext *cct = ictx->cct;
- lderr(cct) << cpp_strerror(r) << dendl;
+ ceph_assert(r < 0);
+
+ bool queue_required = true;
+ if (aio_type == AIO_TYPE_CLOSE || aio_type == AIO_TYPE_OPEN) {
+ // executing from a safe context and the ImageCtx has been destructed
+ queue_required = false;
+ } else {
+ CephContext *cct = ictx->cct;
+ lderr(cct) << cpp_strerror(r) << dendl;
+ }
ceph_assert(!was_armed);
was_armed = true;
- error_rval = r;
+ rval = r;
uint32_t previous_pending_count = pending_count.load();
if (previous_pending_count == 0) {
- queue_complete();
+ if (queue_required) {
+ queue_complete();
+ } else {
+ complete();
+ }
}
}
}
void finish(int r) override {
- ldout(cct, 20) << "C_AioComplete::finish: r=" << r << dendl;
+ ldout(cct, 20) << "C_AioCompletion::finish: r=" << r << dendl;
if (r < 0) {
aio_comp->fail(r);
} else {
ictx(ictx), ictxp(ictxp) {
}
void finish(int r) override {
- ldout(ictx->cct, 20) << "C_OpenComplete::finish: r=" << r << dendl;
+ ldout(cct, 20) << "C_OpenComplete::finish: r=" << r << dendl;
if (r < 0) {
*ictxp = nullptr;
} else {
void finish(int r) override {
ldout(ictx->cct, 20) << "C_OpenAfterCloseComplete::finish: r=" << r
<< dendl;
- delete reinterpret_cast<librbd::ImageCtx*>(*ictxp);
*ictxp = nullptr;
ictx->state->open(0, new C_OpenComplete(ictx, comp, ictxp));
if (r < 0) {
lderr(m_cct) << "failed to open image: " << cpp_strerror(r) << dendl;
- m_image_ctx->destroy();
m_image_ctx = nullptr;
finish(r);
return;
void EnableRequest<I>::handle_close_image(int r) {
ldout(m_cct, 10) << "r=" << r << dendl;
- m_image_ctx->destroy();
m_image_ctx = nullptr;
if (r < 0) {
ldout(m_cct, 5) << "failed to close image:" << cpp_strerror(r) << dendl;
}
- m_image_ctx->destroy();
m_image_ctx = nullptr;
finish(m_ret_val);
}
#include "librbd/deep_copy/Handler.h"
#include "librbd/deep_copy/ImageCopyRequest.h"
#include "librbd/deep_copy/ObjectCopyRequest.h"
-#include "librbd/image/CloseRequest.h"
-#include "librbd/image/OpenRequest.h"
#include "librbd/object_map/DiffRequest.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "test/librbd/mock/MockImageCtx.h"
} // namespace deep_copy
-namespace image {
-
-template <>
-struct CloseRequest<MockTestImageCtx> {
- Context* on_finish = nullptr;
- static CloseRequest* s_instance;
- static CloseRequest* create(MockTestImageCtx *image_ctx, Context *on_finish) {
- ceph_assert(s_instance != nullptr);
- s_instance->on_finish = on_finish;
- return s_instance;
- }
-
- MOCK_METHOD0(send, void());
-
- CloseRequest() {
- s_instance = this;
- }
-};
-
-CloseRequest<MockTestImageCtx>* CloseRequest<MockTestImageCtx>::s_instance = nullptr;
-
-template <>
-struct OpenRequest<MockTestImageCtx> {
- Context* on_finish = nullptr;
- static OpenRequest* s_instance;
- static OpenRequest* create(MockTestImageCtx *image_ctx,
- bool skip_open_parent, Context *on_finish) {
- ceph_assert(s_instance != nullptr);
- s_instance->on_finish = on_finish;
- return s_instance;
- }
-
- MOCK_METHOD0(send, void());
-
- OpenRequest() {
- s_instance = this;
- }
-};
-
-OpenRequest<MockTestImageCtx>* OpenRequest<MockTestImageCtx>::s_instance = nullptr;
-
-} // namespace image
-
namespace object_map {
template <>
.WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
image_ctx->op_work_queue->queue(ctx, r);
})));
- if (r < 0) {
- EXPECT_CALL(mock_image_ctx, destroy());
- }
}
void expect_attach_parent(MockAttachParentRequest& mock_request, int r) {
.WillOnce(Invoke([this, r](Context* ctx) {
image_ctx->op_work_queue->queue(ctx, r);
}));
- EXPECT_CALL(mock_image_ctx, destroy());
}
void expect_remove(MockRemoveRequest& mock_remove_request, int r) {
.WillOnce(Invoke([this, r](Context* ctx) {
image_ctx->op_work_queue->queue(ctx, r);
}));
- EXPECT_CALL(mock_image_ctx, destroy());
}
void expect_snap_remove(MockImageCtx &mock_image_ctx,
{234, {cls::rbd::TrashSnapshotNamespace{}},
"snap1", 123, {}, 0}, 0);
expect_open(mock_image_ctx, -EPERM);
- EXPECT_CALL(mock_image_ctx, destroy());
C_SaferCond ctx;
auto req = MockDetachChildRequest::create(mock_image_ctx, &ctx);
.WillOnce(Invoke([r](bool open_parent, Context *on_ready) {
on_ready->complete(r);
}));
- if (r < 0) {
- EXPECT_CALL(mock_image_ctx, destroy());
- }
}
void expect_state_close(MockTestImageCtx &mock_image_ctx) {
.WillOnce(Invoke([](Context *on_ready) {
on_ready->complete(0);
}));
- EXPECT_CALL(mock_image_ctx, destroy());
}
void expect_wq_queue(ContextWQ &wq, int r) {
ceph_assert(s_instance != nullptr);
return s_instance;
}
- MOCK_METHOD0(destroy, void());
MockImageCtx(librbd::ImageCtx &image_ctx)
: image_ctx(&image_ctx),
.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 LambdaContext([aio_comp](int r) {
- aio_comp->fail(r);
+ if (r < 0) {
+ aio_comp->fail(r);
+ } else {
+ aio_comp->set_request_count(1);
+ aio_comp->add_request();
+ aio_comp->complete_request(r);
+ }
});
mock_image_ctx.image_ctx->op_work_queue->queue(ctx, r);
}));
.WillOnce(Invoke([r](Context *on_finish) {
on_finish->complete(r);
}));
- EXPECT_CALL(mock_image_ctx, destroy());
}
void expect_remove_image(MockImageRemoveRequest& mock_image_remove_request,
}));
}
- void expect_destroy(librbd::MockTestImageCtx& mock_image_ctx) {
- EXPECT_CALL(mock_image_ctx, destroy());
- }
-
librbd::ImageCtx *m_local_image_ctx;
};
0);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
0);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
InSequence seq;
expect_set_journal_policy(mock_image_ctx);
expect_open(mock_image_ctx, -EPERM);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
expect_open(mock_image_ctx, 0);
expect_acquire_lock(mock_image_ctx, -EPERM);
expect_close(mock_image_ctx, -EINVAL);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
"snap1", -EBUSY);
expect_close(mock_image_ctx, -EINVAL);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
"snap1", -EPERM);
expect_close(mock_image_ctx, -EINVAL);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
-EINVAL);
expect_close(mock_image_ctx, -EPERM);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
0);
expect_close(mock_image_ctx, -EINVAL);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockSnapshotPurgeRequest::create(m_local_io_ctx, mock_image_ctx.id,
}));
}
- void expect_destroy(librbd::MockTestImageCtx& mock_image_ctx) {
- EXPECT_CALL(mock_image_ctx, destroy());
- }
-
void expect_mirror_image_set(const std::string& image_id,
const cls::rbd::MirrorImage& mirror_image,
int r) {
expect_mirror_image_remove(m_local_io_ctx, 0);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
MockTrashWatcher mock_trash_watcher;
expect_notify_image_added(mock_trash_watcher, "image id");
expect_mirror_image_remove(m_local_io_ctx, 0);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
MockTrashWatcher mock_trash_watcher;
expect_notify_image_added(mock_trash_watcher, "image id");
expect_set_journal_policy(mock_image_ctx);
expect_open(mock_image_ctx, -EINVAL);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
expect_journal_reset(mock_journal_reset_request, -EINVAL);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
expect_acquire_lock(mock_image_ctx, -EINVAL);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
{}, -EINVAL);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
C_SaferCond ctx;
auto req = MockTrashMoveRequest::create(m_local_io_ctx, "global image id",
expect_mirror_image_remove(m_local_io_ctx, -EINVAL);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
MockTrashWatcher mock_trash_watcher;
expect_notify_image_added(mock_trash_watcher, "image id");
expect_mirror_image_remove(m_local_io_ctx, 0);
expect_close(mock_image_ctx, -EINVAL);
- expect_destroy(mock_image_ctx);
MockTrashWatcher mock_trash_watcher;
expect_notify_image_added(mock_trash_watcher, "image id");
expect_mirror_image_remove(m_local_io_ctx, 0);
expect_close(mock_image_ctx, 0);
- expect_destroy(mock_image_ctx);
MockTrashWatcher mock_trash_watcher;
expect_notify_image_added(mock_trash_watcher, "image id");
if (r < 0) {
derr << "failed to open image '" << m_image_id << "': " << cpp_strerror(r)
<< dendl;
- m_image_ctx->destroy();
m_image_ctx = nullptr;
finish(r);
void SnapshotPurgeRequest<I>::handle_close_image(int r) {
dout(10) << "r=" << r << dendl;
- m_image_ctx->destroy();
m_image_ctx = nullptr;
if (r < 0) {
if (r < 0) {
derr << "failed to open image: " << cpp_strerror(r) << dendl;
- m_image_ctx->destroy();
m_image_ctx = nullptr;
finish(r);
return;
void TrashMoveRequest<I>::handle_close_image(int r) {
dout(10) << "r=" << r << dendl;
- m_image_ctx->destroy();
m_image_ctx = nullptr;
if (r < 0) {
<< dendl;
}
- delete *m_image_ctx;
*m_image_ctx = nullptr;
m_on_finish->complete(0);
if (r < 0) {
derr << ": failed to open image '" << m_image_id << "': "
<< cpp_strerror(r) << dendl;
- (*m_image_ctx)->destroy();
*m_image_ctx = nullptr;
}
derr << ": failed to open image '" << m_local_image_id << "': "
<< cpp_strerror(r) << dendl;
}
- (*m_local_image_ctx)->destroy();
*m_local_image_ctx = nullptr;
finish(r);
return;