ceph: fix generic/639 xfstests failure
The generic/639 xfstest fails for Ceph msgr2 protocol:
Ubuntu 22.04.5 LTS (GNU/Linux 6.17.0-rc7+ x86_64)
sudo mount -t ceph :/ /mnt/cephfs/ -o name=admin,fs=cephfs,ms_mode=secure
sudo ./check generic/639
FSTYP -- ceph
PLATFORM -- Linux/x86_64 ceph-0005 6.17.0-rc7+ #16 SMP PREEMPT_DYNAMIC Wed Nov 12 11:01:48 PST 2025
MKFS_OPTIONS -- 192.168.1.213:3300:/scratch
MOUNT_OPTIONS -- -o name=admin,ms_mode=secure 192.168.1.213:3300:/scratch /mnt/cephfs/scratch
generic/639 - output mismatch (see /home/slavad/XFSTESTS-2/xfstests-dev/results//generic/639.out.bad)
The simple way to reproduce the issue simply running these steps:
mount -t ceph :/ /mnt/cephfs/ -o name=admin,fs=cephfs,ms_mode=secure
xfs_io -f -c "pwrite -q 0 32" ./testfile251125-0004
umount /mnt/cephfs/
mount -t ceph :/ /mnt/cephfs/ -o name=admin,fs=cephfs,ms_mode=secure
xfs_io -c "pwrite -q 32 32" ./testfile251125-0004
Finally, we have the unexpected content of the file:
hexdump ./testfile251125-0004
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0000020 cdcd cdcd cdcd cdcd cdcd cdcd cdcd cdcd
*
0000040
Initial analysis has shown that if we try to write out of
end of file, then ceph_write_begin() is responsible for
the issue because it calls netfs_write_begin() and we have
such logic:
int netfs_write_begin(struct netfs_inode *ctx,
struct file *file, struct address_space *mapping,
loff_t pos, unsigned int len, struct folio **_folio,
void **_fsdata)
{
<skipped>
folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
mapping_gfp_mask(mapping));
<skipped>
if (folio_test_uptodate(folio))
goto have_folio;
<skipped>
}
The reason of the issue that somehow we have folio in uptodate
state and netfs_write_begin() simply skips the logic of
reading existing file's content.
Futher analysis revealed that we call ceph_fill_inode() and
ceph_fill_inline_data() before ceph_write_begin().
void ceph_fill_inline_data(struct inode *inode, struct page *locked_page,
char *data, size_t len)
{
<skipped>
if (page != locked_page) {
if (len < PAGE_SIZE)
zero_user_segment(page, len, PAGE_SIZE);
else
flush_dcache_page(page);
SetPageUptodate(page); <--- We set page uptodate if len == 0!!!!
unlock_page(page);
put_page(page);
}
}
This patch fixes the issue by checking the len argument and
setting memory page uptodate only if len > 0.
sudo ./check generic/639
FSTYP -- ceph
PLATFORM -- Linux/x86_64 ceph-0005 6.19.0-rc5+ #2 SMP PREEMPT_DYNAMIC Thu Feb 5 15:43:51 PST 2026
MKFS_OPTIONS -- 192.168.1.213:3300:/scratch
MOUNT_OPTIONS -- -o name=admin,ms_mode=secure 192.168.1.213:3300:/scratch /mnt/cephfs/scratch
generic/639 6s ... 6s
Ran: generic/639
Passed all 1 tests
[1] https://tracker.ceph.com/issues/73829
Signed-off-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
cc: Alex Markuze <amarkuze@redhat.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: Patrick Donnelly <pdonnell@redhat.com>
cc: Ceph Development <ceph-devel@vger.kernel.org>