ceph: trigger to flush the buffer when making snapshot
authorXiubo Li <xiubli@redhat.com>
Thu, 11 May 2023 05:19:45 +0000 (13:19 +0800)
committerIlya Dryomov <idryomov@gmail.com>
Fri, 30 Jun 2023 10:08:55 +0000 (12:08 +0200)
The 'i_wr_ref' is used to track the 'Fb' caps, while whenever the 'Fb'
caps is took the kclient will always take the 'Fw' caps at the same
time. That means it will always be a false check in __ceph_finish_cap_snap().

When writing to buffer the kclient will take both 'Fb|Fw' caps and then
write the contents to the buffer pages by increasing the 'i_wrbuffer_ref'
and then just release both 'Fb|Fw'. This is different with the user
space libcephfs, which will keep the 'Fb' being took and use 'i_wr_ref'
instead of 'i_wrbuffer_ref' to track this until the buffer is flushed
to Rados.

We need to defer flushing the capsnap until the corresponding buffer
pages are all flushed to Rados, and at the same time just trigger to
flush the buffer pages immediately.

Link: https://tracker.ceph.com/issues/48640
Link: https://tracker.ceph.com/issues/59343
Signed-off-by: Xiubo Li <xiubli@redhat.com>
Reviewed-by: Milind Changire <mchangir@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
fs/ceph/caps.c
fs/ceph/snap.c

index 2321e5ddb664de661549baf52c79857ffb7e2e18..6fb73e3a7f8e8b7adea18aba9f25a73f126b58f8 100644 (file)
@@ -3109,6 +3109,12 @@ static void __ceph_put_cap_refs(struct ceph_inode_info *ci, int had,
        }
        if (had & CEPH_CAP_FILE_WR) {
                if (--ci->i_wr_ref == 0) {
+                       /*
+                        * The Fb caps will always be took and released
+                        * together with the Fw caps.
+                        */
+                       WARN_ON_ONCE(ci->i_wb_ref);
+
                        last++;
                        check_flushsnaps = true;
                        if (ci->i_wrbuffer_ref_head == 0 &&
index 2e73ba62bd7aa73f6c0f39964d65b5aaf1ff05a2..343d738448dcd9e8c55d5dec2b67eca554334a99 100644 (file)
@@ -675,14 +675,17 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci,
                return 0;
        }
 
-       /* Fb cap still in use, delay it */
-       if (ci->i_wb_ref) {
+       /*
+        * Defer flushing the capsnap if the dirty buffer not flushed yet.
+        * And trigger to flush the buffer immediately.
+        */
+       if (ci->i_wrbuffer_ref) {
                dout("%s %p %llx.%llx cap_snap %p snapc %p %llu %s s=%llu "
                     "used WRBUFFER, delaying\n", __func__, inode,
                     ceph_vinop(inode), capsnap, capsnap->context,
                     capsnap->context->seq, ceph_cap_string(capsnap->dirty),
                     capsnap->size);
-               capsnap->writing = 1;
+               ceph_queue_writeback(inode);
                return 0;
        }