.WillOnce(Return(r));
}
+ void expect_sparse_copyup(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
+ const std::string &oid,
+ const std::map<uint64_t, uint64_t> &extent_map,
+ const std::string &data, int r) {
+ bufferlist data_bl;
+ data_bl.append(data);
+
+ bufferlist in_bl;
+ encode(extent_map, in_bl);
+ encode(data_bl, in_bl);
+
+ SnapContext snapc;
+ if (snap_id == CEPH_NOSNAP) {
+ snapc = mock_image_ctx.snapc;
+ }
+
+ EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+ exec(oid, _, StrEq("rbd"), StrEq("sparse_copyup"),
+ ContentsEqual(in_bl), _, snapc))
+ .WillOnce(Return(r));
+ }
+
void expect_write(MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
const std::string& oid, int r) {
SnapContext snapc;
0);
expect_add_copyup_ops(mock_write_request);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", data, 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {{0, 4096}}, data,
+ 0);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
0);
expect_add_copyup_ops(mock_write_request);
- expect_copyup(mock_image_ctx, 0, "oid", data, 0);
+ expect_sparse_copyup(mock_image_ctx, 0, "oid", {{0, 4096}}, data, 0);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
0);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", data, 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {{0, 4096}}, data,
+ 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
{{0, 4096}}, {});
expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS_CLEAN,
true, 0);
- expect_copyup(mock_image_ctx, 0, "oid", data, 0);
+ expect_sparse_copyup(mock_image_ctx, 0, "oid", {{0, 4096}}, data, 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
{{0, 4096}}, {});
0);
expect_add_copyup_ops(mock_write_request);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", "", 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {}, "", 0);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
0);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", "", 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {}, "", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
{{0, 4096}}, {});
0);
expect_add_copyup_ops(mock_write_request);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", "", 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {}, "", 0);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
0);
expect_add_copyup_ops(mock_write_request);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", "", 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {}, "", 0);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
0);
expect_add_copyup_ops(mock_write_request);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", "", 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {}, "", 0);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
0);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", "", 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {}, "", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
{{0, 4096}}, {});
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
{{0, 4096}}, {});
expect_add_copyup_ops(mock_write_request1);
- expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", data, 0);
+ expect_sparse_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", {{0, 4096}}, data,
+ 0);
MockAbstractObjectWriteRequest mock_write_request2;
EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
0);
expect_add_copyup_ops(mock_write_request);
- expect_copyup(mock_image_ctx, 0, "oid", data, -EPERM);
+ expect_sparse_copyup(mock_image_ctx, 0, "oid", {{0, 4096}}, data, -EPERM);
expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
flush_async_operations(ictx);
}
+TEST_F(TestMockIoCopyupRequest, SparseCopyupNotSupported) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockTestImageCtx mock_parent_image_ctx(*ictx->parent);
+ MockTestImageCtx mock_image_ctx(*ictx, &mock_parent_image_ctx);
+ mock_image_ctx.enable_sparse_copyup = false;
+
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_op_work_queue(mock_image_ctx);
+ expect_is_lock_owner(mock_image_ctx);
+
+ InSequence seq;
+
+ MockImageRequest mock_image_request;
+ std::string data(4096, '1');
+ expect_read_parent(mock_parent_image_ctx, mock_image_request, {{0, 4096}},
+ data, 0);
+
+ MockAbstractObjectWriteRequest mock_write_request;
+ expect_get_pre_write_object_map_state(mock_image_ctx, mock_write_request,
+ OBJECT_EXISTS);
+ expect_object_map_at(mock_image_ctx, 0, OBJECT_NONEXISTENT);
+ expect_object_map_update(mock_image_ctx, CEPH_NOSNAP, 0, OBJECT_EXISTS, true,
+ 0);
+
+ expect_add_copyup_ops(mock_write_request);
+ expect_copyup(mock_image_ctx, CEPH_NOSNAP, "oid", data, 0);
+ expect_write(mock_image_ctx, CEPH_NOSNAP, "oid", 0);
+
+ auto req = new MockCopyupRequest(&mock_image_ctx, "oid", 0,
+ {{0, 4096}}, {});
+ mock_image_ctx.copyup_list[0] = req;
+ req->append_request(&mock_write_request);
+ req->send();
+
+ ASSERT_EQ(0, mock_write_request.ctx.wait());
+}
+
} // namespace io
} // namespace librbd
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ bool sparse_read_supported = is_sparse_read_supported(
+ ictx->data_ctx, ictx->get_object_name(10));
+
bufferlist bl;
bl.append(std::string(256, '1'));
ASSERT_EQ(256, ictx->io_work_queue->write(0, bl.length(), bufferlist{bl}, 0));
+ ASSERT_EQ(256, ictx->io_work_queue->write(1024, bl.length(), bufferlist{bl},
+ 0));
ASSERT_EQ(0, snap_create(*ictx, "snap1"));
ASSERT_EQ(0,
librados::snap_set_t snap_set;
ASSERT_EQ(0, snap_ctx.list_snaps(ictx2->get_object_name(0), &snap_set));
+ uint64_t copyup_end = ictx2->enable_sparse_copyup ? 1024 + 256 : 1 << order;
std::vector< std::pair<uint64_t,uint64_t> > expected_overlap =
boost::assign::list_of(
std::make_pair(0, 256))(
- std::make_pair(512, 2096640));
+ std::make_pair(512, copyup_end - 512));
ASSERT_EQ(2U, snap_set.clones.size());
ASSERT_NE(CEPH_NOSNAP, snap_set.clones[0].cloneid);
ASSERT_EQ(2U, snap_set.clones[0].snaps.size());
0));
ASSERT_TRUE(bl.contents_equal(read_bl));
+ ASSERT_EQ(256,
+ ictx2->io_work_queue->read(1024, 256,
+ librbd::io::ReadResult{read_result},
+ 0));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
ASSERT_EQ(256,
ictx2->io_work_queue->read(256, 256,
librbd::io::ReadResult{read_result},
ASSERT_TRUE(read_bl.is_zero());
}
+ // verify sparseness was preserved
+ {
+ librados::IoCtx io_ctx;
+ io_ctx.dup(m_ioctx);
+ librados::Rados rados(io_ctx);
+ EXPECT_EQ(0, rados.conf_set("rbd_cache", "false"));
+ EXPECT_EQ(0, rados.conf_set("rbd_sparse_read_threshold_bytes", "256"));
+ auto ictx3 = new librbd::ImageCtx(clone_name, "", snap_name, io_ctx,
+ true);
+ ASSERT_EQ(0, ictx3->state->open(0));
+ BOOST_SCOPE_EXIT(ictx3) {
+ ictx3->state->close();
+ } BOOST_SCOPE_EXIT_END;
+ std::map<uint64_t, uint64_t> expected_m;
+ bufferlist expected_bl;
+ if (ictx3->enable_sparse_copyup && sparse_read_supported) {
+ if (snap_name == NULL) {
+ expected_m = {{0, 512}, {1024, 256}};
+ expected_bl.append(std::string(256 * 3, '1'));
+ } else {
+ expected_m = {{0, 256}, {1024, 256}};
+ expected_bl.append(std::string(256 * 2, '1'));
+ }
+ } else {
+ expected_m = {{0, 1024 + 256}};
+ if (snap_name == NULL) {
+ expected_bl.append(std::string(256 * 2, '1'));
+ expected_bl.append(std::string(256 * 2, '\0'));
+ expected_bl.append(std::string(256 * 1, '1'));
+ } else {
+ expected_bl.append(std::string(256 * 1, '1'));
+ expected_bl.append(std::string(256 * 3, '\0'));
+ expected_bl.append(std::string(256 * 1, '1'));
+ }
+ }
+ std::map<uint64_t, uint64_t> read_m;
+ librbd::io::ReadResult sparse_read_result{&read_m, &read_bl};
+ EXPECT_EQ(1024 + 256,
+ ictx3->io_work_queue->read(0, 1024 + 256,
+ librbd::io::ReadResult{sparse_read_result},
+ 0));
+ EXPECT_EQ(expected_m, read_m);
+ EXPECT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+
// verify the object map was properly updated
if ((ictx2->features & RBD_FEATURE_OBJECT_MAP) != 0) {
uint8_t state = OBJECT_EXISTS;