template <typename I>
void CopyupRequest<I>::append_request(AbstractObjectWriteRequest<I> *req) {
- ceph_assert(m_ictx->copyup_list_lock.is_locked());
+ Mutex::Locker locker(m_lock);
ldout(m_ictx->cct, 20) << "oid=" << m_oid << ", "
- << "object_request=" << req << dendl;
- m_pending_requests.push_back(req);
+ << "object_request=" << req << ", "
+ << "append=" << m_append_request_permitted << dendl;
+ if (m_append_request_permitted) {
+ m_pending_requests.push_back(req);
+ } else {
+ m_restart_requests.push_back(req);
+ }
}
template <typename I>
ldout(m_ictx->cct, 20) << "oid=" << m_oid << ", "
<< "r=" << r << dendl;
- m_ictx->copyup_list_lock.Lock();
+ m_lock.Lock();
m_copyup_required = is_copyup_required();
- remove_from_list();
+ disable_append_requests();
if (r < 0 && r != -ENOENT) {
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
lderr(m_ictx->cct) << "error reading from parent: " << cpp_strerror(r)
<< dendl;
}
if (!m_copyup_required) {
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
ldout(m_ictx->cct, 20) << "no-op, skipping" << dendl;
finish(0);
return;
}
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
update_object_maps();
}
ceph_assert(m_ictx->parent_lock.is_locked());
ceph_assert(m_ictx->parent != nullptr);
- m_ictx->copyup_list_lock.Lock();
+ m_lock.Lock();
m_flatten = is_copyup_required() ? true : m_ictx->migration_info.flatten;
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
ldout(m_ictx->cct, 20) << "oid=" << m_oid << ", "
<< "flatten=" << m_flatten << dendl;
<< "r=" << r << dendl;
m_ictx->snap_lock.get_read();
- m_ictx->copyup_list_lock.Lock();
+ m_lock.Lock();
m_copyup_required = is_copyup_required();
if (r == -ENOENT && !m_flatten && m_copyup_required) {
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
m_ictx->snap_lock.put_read();
ldout(m_ictx->cct, 10) << "restart deep-copy with flatten" << dendl;
return;
}
- remove_from_list();
+ disable_append_requests();
if (r < 0 && r != -ENOENT) {
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
m_ictx->snap_lock.put_read();
lderr(m_ictx->cct) << "error encountered during deep-copy: "
}
if (!m_copyup_required && !is_update_object_map_required(r)) {
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
m_ictx->snap_lock.put_read();
if (r == -ENOENT) {
return;
}
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
m_ictx->snap_lock.put_read();
update_object_maps();
template <typename I>
void CopyupRequest<I>::copyup() {
- m_ictx->copyup_list_lock.Lock();
+ m_lock.Lock();
if (!m_copyup_required) {
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
ldout(m_ictx->cct, 20) << "skipping copyup" << dendl;
finish(0);
return;
}
- m_ictx->copyup_list_lock.Unlock();
+ m_lock.Unlock();
ldout(m_ictx->cct, 20) << "oid=" << m_oid << dendl;
} else if (r < 0) {
lderr(m_ictx->cct) << "failed to copyup object: "
<< cpp_strerror(r) << dendl;
- complete_requests(r);
+ complete_requests(false, r);
}
if (pending_copyups == 0) {
ldout(m_ictx->cct, 20) << "oid=" << m_oid << ", "
<< "r=" << r << dendl;
- complete_requests(r);
+ complete_requests(true, r);
delete this;
}
template <typename I>
-void CopyupRequest<I>::complete_requests(int r) {
- // already removed from copyup list
+void CopyupRequest<I>::complete_requests(bool override_restart_retval, int r) {
+ remove_from_list();
+
while (!m_pending_requests.empty()) {
auto it = m_pending_requests.begin();
auto req = *it;
req->handle_copyup(r);
m_pending_requests.erase(it);
}
+
+ if (override_restart_retval) {
+ r = -ERESTART;
+ }
+
+ while (!m_restart_requests.empty()) {
+ auto it = m_restart_requests.begin();
+ auto req = *it;
+ ldout(m_ictx->cct, 20) << "restarting request " << req << dendl;
+ req->handle_copyup(r);
+ m_restart_requests.erase(it);
+ }
+}
+
+template <typename I>
+void CopyupRequest<I>::disable_append_requests() {
+ ceph_assert(m_lock.is_locked());
+ m_append_request_permitted = false;
}
template <typename I>
void CopyupRequest<I>::remove_from_list() {
- assert(m_ictx->copyup_list_lock.is_locked());
+ Mutex::Locker copyup_list_locker(m_ictx->copyup_list_lock);
auto it = m_ictx->copyup_list.find(m_object_no);
- ceph_assert(it != m_ictx->copyup_list.end());
- m_ictx->copyup_list.erase(it);
+ if (it != m_ictx->copyup_list.end()) {
+ m_ictx->copyup_list.erase(it);
+ }
}
template <typename I>
bool CopyupRequest<I>::is_copyup_required() {
- ceph_assert(m_ictx->copyup_list_lock.is_locked());
+ ceph_assert(m_lock.is_locked());
bool copy_on_read = m_pending_requests.empty();
if (copy_on_read) {
* no data was read from the parent *and* there are no additional ops.
*/
- typedef std::vector<AbstractObjectWriteRequest<ImageCtxT> *> PendingRequests;
+ typedef std::vector<AbstractObjectWriteRequest<ImageCtxT> *> WriteRequests;
ImageCtx *m_ictx;
std::string m_oid;
bool m_copyup_required = true;
ceph::bufferlist m_copyup_data;
- PendingRequests m_pending_requests;
- unsigned m_pending_copyups = 0;
AsyncOperation m_async_op;
std::vector<uint64_t> m_snap_ids;
Mutex m_lock;
+ WriteRequests m_pending_requests;
+ unsigned m_pending_copyups = 0;
+
+ WriteRequests m_restart_requests;
+ bool m_append_request_permitted = true;
void read_from_parent();
void handle_read_from_parent(int r);
void handle_copyup(int r);
void finish(int r);
- void complete_requests(int r);
+ void complete_requests(bool override_restart_retval, int r);
+ void disable_append_requests();
void remove_from_list();
bool is_copyup_required();
ASSERT_EQ(0, ctx.wait());
}
+TEST_F(TestMockIoObjectRequest, CopyupRestart) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::Image image;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("one"));
+ ASSERT_EQ(0, image.snap_protect("one"));
+ image.close();
+
+ std::string clone_name = get_temp_image_name();
+ int order = 0;
+ ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
+ clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(clone_name, &ictx));
+
+ MockTestImageCtx mock_image_ctx(*ictx);
+ expect_get_object_size(mock_image_ctx);
+
+ MockExclusiveLock mock_exclusive_lock;
+ if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+ mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+ expect_is_lock_owner(mock_exclusive_lock);
+ }
+
+ MockObjectMap mock_object_map;
+ if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+ mock_image_ctx.object_map = &mock_object_map;
+ }
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+
+ InSequence seq;
+ expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 4096, 0);
+ expect_prune_parent_extents(mock_image_ctx, {{0, 4096}}, 4096, 4096);
+ expect_object_may_exist(mock_image_ctx, 0, true);
+ expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_EXISTS, {}, false, 0);
+ expect_assert_exists(mock_image_ctx, -ENOENT);
+
+ MockAbstractObjectWriteRequest *write_request = nullptr;
+ MockCopyupRequest mock_copyup_request;
+ expect_copyup(mock_copyup_request, &write_request, -ERESTART);
+ expect_assert_exists(mock_image_ctx, 0);
+ expect_write(mock_image_ctx, 0, 4096, 0);
+
+ C_SaferCond ctx;
+ auto req = MockObjectWriteRequest::create_write(
+ &mock_image_ctx, ictx->get_object_name(0), 0, 0, std::move(bl),
+ mock_image_ctx.snapc, 0, {}, &ctx);
+ req->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
TEST_F(TestMockIoObjectRequest, CopyupOptimization) {
REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP);