handle_detained_aio_update(cell, r, on_finish);
});
aio_update(CEPH_NOSNAP, op.start_object_no, op.end_object_no, op.new_state,
- op.current_state, op.parent_trace, ctx);
+ op.current_state, op.parent_trace, op.ignore_enoent, ctx);
}
template <typename I>
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
const ZTracer::Trace &parent_trace,
- Context *on_finish) {
+ bool ignore_enoent, Context *on_finish) {
ceph_assert(m_image_ctx.snap_lock.is_locked());
ceph_assert((m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) != 0);
ceph_assert(m_image_ctx.image_watcher != nullptr);
auto req = object_map::UpdateRequest<I>::create(
m_image_ctx, &m_object_map, snap_id, start_object_no, end_object_no,
- new_state, current_state, parent_trace, on_finish);
+ new_state, current_state, parent_trace, ignore_enoent, on_finish);
req->send();
}
template <typename T, void(T::*MF)(int) = &T::complete>
bool aio_update(uint64_t snap_id, uint64_t start_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- const ZTracer::Trace &parent_trace, T *callback_object) {
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ T *callback_object) {
return aio_update<T, MF>(snap_id, start_object_no, start_object_no + 1,
new_state, current_state, parent_trace,
- callback_object);
+ ignore_enoent, callback_object);
}
template <typename T, void(T::*MF)(int) = &T::complete>
bool aio_update(uint64_t snap_id, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- const ZTracer::Trace &parent_trace, T *callback_object) {
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ T *callback_object) {
ceph_assert(start_object_no < end_object_no);
if (snap_id == CEPH_NOSNAP) {
end_object_no = std::min(end_object_no, m_object_map.size());
UpdateOperation update_operation(start_object_no, end_object_no,
new_state, current_state, parent_trace,
+ ignore_enoent,
util::create_context_callback<T, MF>(
callback_object));
detained_aio_update(std::move(update_operation));
} else {
aio_update(snap_id, start_object_no, end_object_no, new_state,
- current_state, parent_trace,
+ current_state, parent_trace, ignore_enoent,
util::create_context_callback<T, MF>(callback_object));
}
return true;
uint8_t new_state;
boost::optional<uint8_t> current_state;
ZTracer::Trace parent_trace;
+ bool ignore_enoent;
Context *on_finish;
UpdateOperation(uint64_t start_object_no, uint64_t end_object_no,
uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- const ZTracer::Trace &parent_trace, Context *on_finish)
+ const ZTracer::Trace &parent_trace,
+ bool ignore_enoent, Context *on_finish)
: start_object_no(start_object_no), end_object_no(end_object_no),
new_state(new_state), current_state(current_state),
- parent_trace(parent_trace), on_finish(on_finish) {
+ parent_trace(parent_trace), ignore_enoent(ignore_enoent),
+ on_finish(on_finish) {
}
};
void aio_update(uint64_t snap_id, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- const ZTracer::Trace &parent_trace, Context *on_finish);
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ Context *on_finish);
bool update_required(const ceph::BitVector<2>::Iterator &it,
uint8_t new_state);
m_dst_image_ctx->object_map_lock.get_write();
bool sent = m_dst_image_ctx->object_map->template aio_update<
Context, &Context::complete>(dst_snap_id, m_dst_object_number, object_state,
- {}, {}, ctx);
+ {}, {}, false, ctx);
m_dst_image_ctx->object_map_lock.put_write();
m_dst_image_ctx->snap_lock.put_read();
m_dst_image_ctx->owner_lock.put_read();
ceph_assert(m_image_ctx.exclusive_lock->is_lock_owner());
ceph_assert(m_image_ctx.object_map != nullptr);
bool sent = m_image_ctx.object_map->aio_update<Context>(
- CEPH_NOSNAP, m_object_no, OBJECT_EXISTS, {}, m_trace, this);
+ CEPH_NOSNAP, m_object_no, OBJECT_EXISTS, {}, m_trace, false, this);
return (sent ? 0 : 1);
}
}
bool sent = m_image_ctx.object_map->aio_update<Context>(
- snap_id, m_object_no, state, {}, m_trace, this);
+ snap_id, m_object_no, state, {}, m_trace, true, this);
ceph_assert(sent);
return 0;
}
if (may_update && (new_state != current_state) &&
m_ictx->object_map->aio_update<CopyupRequest>(
CEPH_NOSNAP, m_object_no, new_state, current_state, m_trace,
- this)) {
+ false, this)) {
return false;
}
}
if (image_ctx->object_map->template aio_update<
AbstractObjectWriteRequest<I>,
&AbstractObjectWriteRequest<I>::handle_pre_write_object_map_update>(
- CEPH_NOSNAP, this->m_object_no, new_state, {}, this->m_trace, this)) {
+ CEPH_NOSNAP, this->m_object_no, new_state, {}, this->m_trace, false,
+ this)) {
image_ctx->object_map_lock.put_write();
image_ctx->snap_lock.put_read();
return;
AbstractObjectWriteRequest<I>,
&AbstractObjectWriteRequest<I>::handle_post_write_object_map_update>(
CEPH_NOSNAP, this->m_object_no, OBJECT_NONEXISTENT, OBJECT_PENDING,
- this->m_trace, this)) {
+ this->m_trace, false, this)) {
image_ctx->object_map_lock.put_write();
image_ctx->snap_lock.put_read();
return;
void UpdateRequest<I>::handle_update_object_map(int r) {
ldout(m_image_ctx.cct, 20) << "r=" << r << dendl;
+ if (r == -ENOENT && m_ignore_enoent) {
+ r = 0;
+ }
+ if (r < 0 && m_ret_val == 0) {
+ m_ret_val = r;
+ }
+
{
RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
RWLock::WLocker object_map_locker(m_image_ctx.object_map_lock);
}
// no more batch updates to send
- complete(r);
+ complete(m_ret_val);
}
template <typename I>
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
const ZTracer::Trace &parent_trace,
- Context *on_finish) {
+ bool ignore_enoent, Context *on_finish) {
return new UpdateRequest(image_ctx, object_map, snap_id, start_object_no,
end_object_no, new_state, current_state,
- parent_trace, on_finish);
+ parent_trace, ignore_enoent, on_finish);
}
UpdateRequest(ImageCtx &image_ctx, ceph::BitVector<2> *object_map,
uint64_t snap_id, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- const ZTracer::Trace &parent_trace, Context *on_finish)
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ Context *on_finish)
: Request(image_ctx, snap_id, on_finish), m_object_map(*object_map),
m_start_object_no(start_object_no), m_end_object_no(end_object_no),
m_update_start_object_no(start_object_no), m_new_state(new_state),
m_current_state(current_state),
- m_trace(util::create_trace(image_ctx, "update object map", parent_trace))
+ m_trace(util::create_trace(image_ctx, "update object map", parent_trace)),
+ m_ignore_enoent(ignore_enoent)
{
m_trace.event("start");
}
uint8_t m_new_state;
boost::optional<uint8_t> m_current_state;
ZTracer::Trace m_trace;
+ bool m_ignore_enoent;
+
+ int m_ret_val = 0;
void update_object_map();
void handle_update_object_map(int r);
RWLock::WLocker object_map_locker(image_ctx.object_map_lock);
if (image_ctx.object_map->template aio_update<AsyncRequest<I> >(
CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_PENDING,
- OBJECT_EXISTS, {}, this)) {
+ OBJECT_EXISTS, {}, false, this)) {
return;
}
}
RWLock::WLocker object_map_locker(image_ctx.object_map_lock);
if (image_ctx.object_map->template aio_update<AsyncRequest<I> >(
CEPH_NOSNAP, m_delete_start_min, m_num_objects, OBJECT_NONEXISTENT,
- OBJECT_PENDING, {}, this)) {
+ OBJECT_PENDING, {}, false, this)) {
return;
}
}
librados::snap_t snap_id, uint8_t state,
int r) {
if (mock_image_ctx.image_ctx->object_map != nullptr) {
- auto &expect = EXPECT_CALL(mock_object_map, aio_update(snap_id, 0, 1, state, _, _, _));
+ auto &expect = EXPECT_CALL(mock_object_map, aio_update(snap_id, 0, 1, state, _, _, false, _));
if (r < 0) {
- expect.WillOnce(DoAll(WithArg<6>(Invoke([this, r](Context *ctx) {
+ expect.WillOnce(DoAll(WithArg<7>(Invoke([this, r](Context *ctx) {
m_work_queue->queue(ctx, r);
})),
Return(true)));
} else {
- expect.WillOnce(DoAll(WithArg<6>(Invoke([&mock_image_ctx, snap_id, state](Context *ctx) {
+ expect.WillOnce(DoAll(WithArg<7>(Invoke([&mock_image_ctx, snap_id, state](Context *ctx) {
ceph_assert(mock_image_ctx.image_ctx->snap_lock.is_locked());
ceph_assert(mock_image_ctx.image_ctx->object_map_lock.is_wlocked());
mock_image_ctx.image_ctx->object_map->aio_update<Context>(
- snap_id, 0, 1, state, boost::none, {}, ctx);
+ snap_id, 0, 1, state, boost::none, {}, false, ctx);
})),
Return(true)));
}
if (mock_image_ctx.object_map != nullptr) {
EXPECT_CALL(*mock_image_ctx.object_map,
aio_update(CEPH_NOSNAP, start_object, end_object, state,
- current_state, _, _))
- .WillOnce(WithArg<6>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
+ current_state, _, false, _))
+ .WillOnce(WithArg<7>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
if (updated) {
mock_image_ctx.op_work_queue->queue(ctx, ret_val);
}
template <typename T, void(T::*MF)(int) = &T::complete>
bool aio_update(uint64_t snap_id, uint64_t start_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- const ZTracer::Trace &parent_trace, T *callback_object) {
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ T *callback_object) {
return aio_update<T, MF>(snap_id, start_object_no, start_object_no + 1,
new_state, current_state, parent_trace,
- callback_object);
+ ignore_enoent, callback_object);
}
template <typename T, void(T::*MF)(int) = &T::complete>
bool aio_update(uint64_t snap_id, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- const ZTracer::Trace &parent_trace, T *callback_object) {
+ const ZTracer::Trace &parent_trace, bool ignore_enoent,
+ T *callback_object) {
auto ctx = util::create_context_callback<T, MF>(callback_object);
bool updated = aio_update(snap_id, start_object_no, end_object_no,
- new_state, current_state, parent_trace, ctx);
+ new_state, current_state, parent_trace,
+ ignore_enoent, ctx);
if (!updated) {
delete ctx;
}
return updated;
}
- MOCK_METHOD7(aio_update, bool(uint64_t snap_id, uint64_t start_object_no,
+ MOCK_METHOD8(aio_update, bool(uint64_t snap_id, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
const ZTracer::Trace &parent_trace,
- Context *on_finish));
+ bool ignore_enoent, Context *on_finish));
MOCK_METHOD2(snapshot_add, void(uint64_t snap_id, Context *on_finish));
MOCK_METHOD2(snapshot_remove, void(uint64_t snap_id, Context *on_finish));
C_SaferCond cond_ctx;
AsyncRequest<> *req = new UpdateRequest<>(
*ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
- OBJECT_EXISTS, {}, &cond_ctx);
+ OBJECT_EXISTS, {}, false, &cond_ctx);
{
RWLock::RLocker snap_locker(ictx->snap_lock);
RWLock::WLocker object_map_locker(ictx->object_map_lock);
C_SaferCond cond_ctx;
AsyncRequest<> *req = new UpdateRequest<>(
*ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
- OBJECT_EXISTS, {}, &cond_ctx);
+ OBJECT_EXISTS, {}, false, &cond_ctx);
{
RWLock::RLocker snap_locker(ictx->snap_lock);
RWLock::WLocker object_map_locker(ictx->object_map_lock);
C_SaferCond cond_ctx;
AsyncRequest<> *req = new UpdateRequest<>(
*ictx, &object_map, snap_id, 0, object_map.size(), OBJECT_NONEXISTENT,
- OBJECT_EXISTS, {}, &cond_ctx);
+ OBJECT_EXISTS, {}, false, &cond_ctx);
{
RWLock::RLocker snap_locker(ictx->snap_lock);
RWLock::WLocker object_map_locker(ictx->object_map_lock);
C_SaferCond cond_ctx;
AsyncRequest<> *req = new UpdateRequest<>(
*ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
- OBJECT_EXISTS, {}, &cond_ctx);
+ OBJECT_EXISTS, {}, false, &cond_ctx);
{
RWLock::RLocker snap_locker(ictx->snap_lock);
RWLock::WLocker object_map_locker(ictx->object_map_lock);
C_SaferCond cond_ctx;
AsyncRequest<> *req = new UpdateRequest<>(
*ictx, &object_map, snap_id, 0, object_map.size(), OBJECT_EXISTS_CLEAN,
- boost::optional<uint8_t>(), {}, &cond_ctx);
+ boost::optional<uint8_t>(), {}, false, &cond_ctx);
{
RWLock::RLocker snap_locker(ictx->snap_lock);
RWLock::WLocker object_map_locker(ictx->object_map_lock);
C_SaferCond cond_ctx;
AsyncRequest<> *req = new UpdateRequest<>(
*ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
- OBJECT_EXISTS, {}, &cond_ctx);
+ OBJECT_EXISTS, {}, false, &cond_ctx);
{
RWLock::RLocker snap_locker(ictx->snap_lock);
RWLock::WLocker object_map_locker(ictx->object_map_lock);
ASSERT_EQ(0, cond_ctx.wait());
}
+TEST_F(TestMockObjectMapUpdateRequest, IgnoreMissingObjectMap) {
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, acquire_exclusive_lock(*ictx));
+
+ expect_update(ictx, CEPH_NOSNAP, 0, 1, OBJECT_NONEXISTENT, OBJECT_EXISTS,
+ -ENOENT);
+
+ ceph::BitVector<2> object_map;
+ object_map.resize(1);
+
+ C_SaferCond cond_ctx;
+ AsyncRequest<> *req = new UpdateRequest<>(
+ *ictx, &object_map, CEPH_NOSNAP, 0, object_map.size(), OBJECT_NONEXISTENT,
+ OBJECT_EXISTS, {}, true, &cond_ctx);
+ {
+ RWLock::RLocker snap_locker(ictx->snap_lock);
+ RWLock::WLocker object_map_locker(ictx->object_map_lock);
+ req->send();
+ }
+ ASSERT_EQ(0, cond_ctx.wait());
+
+ expect_unlock_exclusive_lock(*ictx);
+}
+
} // namespace object_map
} // namespace librbd
if (mock_image_ctx.object_map != nullptr) {
EXPECT_CALL(*mock_image_ctx.object_map,
aio_update(CEPH_NOSNAP, start_object, end_object, state,
- boost::optional<uint8_t>(current_state), _, _))
- .WillOnce(WithArg<6>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
+ boost::optional<uint8_t>(current_state), _, false, _))
+ .WillOnce(WithArg<7>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
if (updated) {
mock_image_ctx.op_work_queue->queue(ctx, ret_val);
}
uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
const ZTracer::Trace &parent_trace,
- Context *on_finish) {
+ bool ignore_enoent, Context *on_finish) {
ceph_assert(s_instance != nullptr);
s_instance->on_finish = on_finish;
s_instance->construct(snap_id, start_object_no, end_object_no, new_state,
- current_state);
+ current_state, ignore_enoent);
return s_instance;
}
- MOCK_METHOD5(construct, void(uint64_t snap_id, uint64_t start_object_no,
+ MOCK_METHOD6(construct, void(uint64_t snap_id, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
- const boost::optional<uint8_t> ¤t_state));
+ const boost::optional<uint8_t> ¤t_state,
+ bool ignore_enoent));
MOCK_METHOD0(send, void());
UpdateRequest() {
s_instance = this;
uint64_t snap_id, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
const boost::optional<uint8_t> ¤t_state,
- Context **on_finish) {
+ bool ignore_enoent, Context **on_finish) {
EXPECT_CALL(mock_update_request, construct(snap_id, start_object_no,
end_object_no, new_state,
- current_state))
+ current_state, ignore_enoent))
.Times(1);
EXPECT_CALL(mock_update_request, send())
.WillOnce(Invoke([&mock_update_request, on_finish]() {
MockUpdateRequest mock_update_request;
Context *finish_update_1;
expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
- 0, 1, 1, {}, &finish_update_1);
+ 0, 1, 1, {}, false, &finish_update_1);
Context *finish_update_2;
expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
- 1, 2, 1, {}, &finish_update_2);
+ 1, 2, 1, {}, false, &finish_update_2);
MockUnlockRequest mock_unlock_request;
expect_unlock(mock_image_ctx, mock_unlock_request, 0);
{
RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
- mock_object_map.aio_update(CEPH_NOSNAP, 0, 1, {}, {}, &update_ctx1);
- mock_object_map.aio_update(CEPH_NOSNAP, 1, 1, {}, {}, &update_ctx2);
+ mock_object_map.aio_update(CEPH_NOSNAP, 0, 1, {}, {}, false, &update_ctx1);
+ mock_object_map.aio_update(CEPH_NOSNAP, 1, 1, {}, {}, false, &update_ctx2);
}
finish_update_2->complete(0);
MockUpdateRequest mock_update_request;
Context *finish_update_1;
expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
- 1, 4, 1, {}, &finish_update_1);
+ 1, 4, 1, {}, false, &finish_update_1);
Context *finish_update_2 = nullptr;
expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
- 1, 3, 1, {}, &finish_update_2);
+ 1, 3, 1, {}, false, &finish_update_2);
Context *finish_update_3 = nullptr;
expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
- 2, 3, 1, {}, &finish_update_3);
+ 2, 3, 1, {}, false, &finish_update_3);
Context *finish_update_4 = nullptr;
expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
- 0, 2, 1, {}, &finish_update_4);
+ 0, 2, 1, {}, false, &finish_update_4);
MockUnlockRequest mock_unlock_request;
expect_unlock(mock_image_ctx, mock_unlock_request, 0);
{
RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
- mock_object_map.aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, &update_ctx1);
- mock_object_map.aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, &update_ctx2);
- mock_object_map.aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, &update_ctx3);
- mock_object_map.aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, &update_ctx4);
+ mock_object_map.aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, false,
+ &update_ctx1);
+ mock_object_map.aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, false,
+ &update_ctx2);
+ mock_object_map.aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, false,
+ &update_ctx3);
+ mock_object_map.aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, false,
+ &update_ctx4);
}
// updates 2, 3, and 4 are blocked on update 1