[PATCH] ceph: write back a cap snap's dirty pages as soon as it is finished
From: Xiubo Li via B4 Relay
Date: Sun Nov 08 2026 - 19:29:23 EST
From: Xiubo Li <xiubo.li@xxxxxxxxx>
When a snapshot is taken while an inode has dirty pages, the pages
move to a cap snap, which can only be flushed to the MDS once they have
been written back. __ceph_finish_cap_snap() starts writeback at once
when the cap snap has no dirty pages of its own but the inode still has
some, but when the cap snap has some it starts nothing: its pages wait
for periodic writeback, up to dirty_expire_centisecs, or for the next
write to one of them. ceph-fuse flushes the buffered data at once in
both cases.
With lazy I/O that loses data from the snapshot. Several clients
buffer writes to one file then, and when another one writes to the
same object with the new snap context first, the OSD clones the object
for the snapshot on that write; what this client writes with the old
snap context after it goes to the head only. With two lazy writers
that each dirty a page, take a snapshot and write again, the snapshot
had zeros instead of the first client's data whenever the second
client's new write reached the OSD first, as the OSD op history shows:
client.4157.0:4 [write 4096~2000] snapc 2=[2]
client.4156.0:3 [write 0~2000] snapc 1=[]
Start writeback of the cap snap's pages at once, too, as ceph-fuse does.
This also gets the cap snap to the MDS sooner without lazy I/O.
Fixes: 2d12ad950b0c ("ceph: trigger to flush the buffer when making snapshot")
Signed-off-by: Xiubo Li <xiubo.li@xxxxxxxxx>
---
When a snapshot is taken while an inode has dirty pages, the kernel
client leaves the cap snap's pages for periodic writeback, while
ceph-fuse flushes them at once. With lazy I/O another client may write
to the same objects with the new snap context first, and what this
client writes with the old one afterwards misses the snapshot.
---
fs/ceph/snap.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
index f59b83a97d52..e44378b07fd3 100644
--- a/fs/ceph/snap.c
+++ b/fs/ceph/snap.c
@@ -683,6 +683,15 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci,
capsnap->context->seq,
ceph_cap_string(capsnap->dirty),
capsnap->size, capsnap->dirty_pages);
+ /*
+ * Write them back now, not when writeback or the next
+ * write to them gets to it: they belong to the snapshot,
+ * and with lazy I/O another client may already write to
+ * the same objects with the new snap context. Its first
+ * write clones the objects for the snapshot, and what we
+ * write with the old one after that misses the snapshot.
+ */
+ ceph_queue_writeback(inode);
return 0;
}
---
base-commit: 5dcec86cc31f8144857c0d9bf2a7fa13b6e6f531
change-id: 20261108-ceph-capsnap-writeback-51ea200d5b59
Best regards,
--
Xiubo Li <xiubo.li@xxxxxxxxx>