Skip to content

Commit 30103a0

Browse files
authored
Merge pull request ceph#61129 from idryomov/wip-68998
librbd: avoid data corruption on flatten when object map is inconsistent Reviewed-by: Ramana Raja <[email protected]>
2 parents 7fef0e9 + ffcd903 commit 30103a0

File tree

4 files changed

+77
-36
lines changed

4 files changed

+77
-36
lines changed

src/librbd/ObjectMap.cc

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -106,32 +106,6 @@ bool ObjectMap<I>::object_may_exist(uint64_t object_no) const
106106
return exists;
107107
}
108108

109-
template <typename I>
110-
bool ObjectMap<I>::object_may_not_exist(uint64_t object_no) const
111-
{
112-
ceph_assert(ceph_mutex_is_locked(m_image_ctx.image_lock));
113-
114-
// Fall back to default logic if object map is disabled or invalid
115-
if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP,
116-
m_image_ctx.image_lock)) {
117-
return true;
118-
}
119-
120-
bool flags_set;
121-
int r = m_image_ctx.test_flags(m_image_ctx.snap_id,
122-
RBD_FLAG_OBJECT_MAP_INVALID,
123-
m_image_ctx.image_lock, &flags_set);
124-
if (r < 0 || flags_set) {
125-
return true;
126-
}
127-
128-
uint8_t state = (*this)[object_no];
129-
bool nonexistent = (state != OBJECT_EXISTS && state != OBJECT_EXISTS_CLEAN);
130-
ldout(m_image_ctx.cct, 20) << "object_no=" << object_no << " r="
131-
<< nonexistent << dendl;
132-
return nonexistent;
133-
}
134-
135109
template <typename I>
136110
bool ObjectMap<I>::update_required(const ceph::BitVector<2>::Iterator& it,
137111
uint8_t new_state) {

src/librbd/ObjectMap.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ class ObjectMap : public RefCountedObject {
6565
void close(Context *on_finish);
6666
bool set_object_map(ceph::BitVector<2> &target_object_map);
6767
bool object_may_exist(uint64_t object_no) const;
68-
bool object_may_not_exist(uint64_t object_no) const;
6968

7069
void aio_save(Context *on_finish);
7170
void aio_resize(uint64_t new_size, uint8_t default_object_state,

src/librbd/operation/FlattenRequest.cc

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,6 @@ class C_FlattenObject : public C_AsyncObjectThrottle<I> {
4949
return -ERESTART;
5050
}
5151

52-
{
53-
std::shared_lock image_lock{image_ctx.image_lock};
54-
if (image_ctx.object_map != nullptr &&
55-
!image_ctx.object_map->object_may_not_exist(m_object_no)) {
56-
// can skip because the object already exists
57-
return 1;
58-
}
59-
}
60-
6152
if (!io::util::trigger_copyup(
6253
&image_ctx, m_object_no, m_io_context, this)) {
6354
// stop early if the parent went away - it just means

src/test/librbd/test_internal.cc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,83 @@ TEST_F(TestInternal, FlattenNoEmptyObjects)
15711571
rados_ioctx_destroy(d_ioctx);
15721572
}
15731573

1574+
TEST_F(TestInternal, FlattenInconsistentObjectMap)
1575+
{
1576+
REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP);
1577+
REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
1578+
1579+
librbd::ImageCtx* ictx;
1580+
ASSERT_EQ(0, open_image(m_image_name, &ictx));
1581+
1582+
librbd::NoOpProgressContext no_op;
1583+
ASSERT_EQ(0, ictx->operations->resize((1 << ictx->order) * 5, true, no_op));
1584+
1585+
bufferlist bl;
1586+
bl.append(std::string(256, '1'));
1587+
for (int i = 1; i < 5; i++) {
1588+
ASSERT_EQ(256, api::Io<>::write(*ictx, (1 << ictx->order) * i, 256,
1589+
bufferlist{bl}, 0));
1590+
}
1591+
1592+
ASSERT_EQ(0, snap_create(*ictx, "snap"));
1593+
ASSERT_EQ(0, snap_protect(*ictx, "snap"));
1594+
1595+
uint64_t features;
1596+
ASSERT_EQ(0, librbd::get_features(ictx, &features));
1597+
1598+
std::string clone_name = get_temp_image_name();
1599+
int order = ictx->order;
1600+
ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
1601+
clone_name.c_str(), features, &order, 0, 0));
1602+
1603+
close_image(ictx);
1604+
ASSERT_EQ(0, open_image(clone_name, &ictx));
1605+
1606+
C_SaferCond lock_ctx;
1607+
{
1608+
std::shared_lock owner_locker{ictx->owner_lock};
1609+
ictx->exclusive_lock->try_acquire_lock(&lock_ctx);
1610+
}
1611+
ASSERT_EQ(0, lock_ctx.wait());
1612+
ASSERT_TRUE(ictx->exclusive_lock->is_lock_owner());
1613+
1614+
ceph::BitVector<2> inconsistent_object_map;
1615+
inconsistent_object_map.resize(5);
1616+
inconsistent_object_map[0] = OBJECT_NONEXISTENT;
1617+
inconsistent_object_map[1] = OBJECT_NONEXISTENT;
1618+
inconsistent_object_map[2] = OBJECT_EXISTS;
1619+
inconsistent_object_map[3] = OBJECT_EXISTS_CLEAN;
1620+
// OBJECT_PENDING shouldn't happen within parent overlap, but test
1621+
// anyway
1622+
inconsistent_object_map[4] = OBJECT_PENDING;
1623+
1624+
auto object_map = new librbd::ObjectMap<>(*ictx, CEPH_NOSNAP);
1625+
C_SaferCond save_ctx;
1626+
{
1627+
std::shared_lock owner_locker{ictx->owner_lock};
1628+
std::unique_lock image_locker{ictx->image_lock};
1629+
object_map->set_object_map(inconsistent_object_map);
1630+
object_map->aio_save(&save_ctx);
1631+
}
1632+
ASSERT_EQ(0, save_ctx.wait());
1633+
object_map->put();
1634+
1635+
close_image(ictx);
1636+
ASSERT_EQ(0, open_image(clone_name, &ictx));
1637+
ASSERT_EQ(0, ictx->operations->flatten(no_op));
1638+
1639+
bufferptr read_ptr(256);
1640+
bufferlist read_bl;
1641+
read_bl.push_back(read_ptr);
1642+
1643+
librbd::io::ReadResult read_result{&read_bl};
1644+
for (int i = 1; i < 5; i++) {
1645+
ASSERT_EQ(256, api::Io<>::read(*ictx, (1 << ictx->order) * i, 256,
1646+
librbd::io::ReadResult{read_result}, 0));
1647+
EXPECT_TRUE(bl.contents_equal(read_bl));
1648+
}
1649+
}
1650+
15741651
TEST_F(TestInternal, PoolMetadataConfApply) {
15751652
REQUIRE_FORMAT_V2();
15761653

0 commit comments

Comments
 (0)