ceph: reset i_requested_max_size if file write is not wanted
authorYan, Zheng <zyan@redhat.com>
Mon, 30 Mar 2020 11:56:37 +0000 (19:56 +0800)
committerIlya Dryomov <idryomov@gmail.com>
Mon, 1 Jun 2020 11:22:52 +0000 (13:22 +0200)
write can stuck at waiting for larger max_size in following sequence of
events:

- client opens a file and writes to position 'A' (larger than unit of
  max size increment)
- client closes the file handle and updates wanted caps (not wanting
  file write caps)
- client opens and truncates the file, writes to position 'A' again.

At the 1st event, client set inode's requested_max_size to 'A'. At the
2nd event, mds removes client's writable range, but client does not reset
requested_max_size. At the 3rd event, client does not request max size
because requested_max_size is already larger than 'A'.

Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
fs/ceph/caps.c

index d555d27..53db1fc 100644 (file)
@@ -1369,8 +1369,12 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
        arg->size = inode->i_size;
        ci->i_reported_size = arg->size;
        arg->max_size = ci->i_wanted_max_size;
-       if (cap == ci->i_auth_cap)
-               ci->i_requested_max_size = arg->max_size;
+       if (cap == ci->i_auth_cap) {
+               if (want & CEPH_CAP_ANY_FILE_WR)
+                       ci->i_requested_max_size = arg->max_size;
+               else
+                       ci->i_requested_max_size = 0;
+       }
 
        if (flushing & CEPH_CAP_XATTR_EXCL) {
                arg->old_xattr_buf = __ceph_build_xattrs_blob(ci);
@@ -3342,10 +3346,6 @@ static void handle_cap_grant(struct inode *inode,
                                ci->i_requested_max_size = 0;
                        }
                        wake = true;
-               } else if (ci->i_wanted_max_size > ci->i_max_size &&
-                          ci->i_wanted_max_size > ci->i_requested_max_size) {
-                       /* CEPH_CAP_OP_IMPORT */
-                       wake = true;
                }
        }
 
@@ -3421,9 +3421,18 @@ static void handle_cap_grant(struct inode *inode,
                        fill_inline = true;
        }
 
-       if (le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) {
+       if (ci->i_auth_cap == cap &&
+           le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) {
                if (newcaps & ~extra_info->issued)
                        wake = true;
+
+               if (ci->i_requested_max_size > max_size ||
+                   !(le32_to_cpu(grant->wanted) & CEPH_CAP_ANY_FILE_WR)) {
+                       /* re-request max_size if necessary */
+                       ci->i_requested_max_size = 0;
+                       wake = true;
+               }
+
                ceph_kick_flushing_inode_caps(session, ci);
                spin_unlock(&ci->i_ceph_lock);
                up_read(&session->s_mdsc->snap_rwsem);
@@ -3882,9 +3891,6 @@ retry:
                __ceph_remove_cap(ocap, (ph->flags & CEPH_CAP_FLAG_RELEASE));
        }
 
-       /* make sure we re-request max_size, if necessary */
-       ci->i_requested_max_size = 0;
-
        *old_issued = issued;
        *target_cap = cap;
 }
@@ -4318,6 +4324,9 @@ int ceph_encode_inode_release(void **p, struct inode *inode,
                                cap->issued &= ~drop;
                                cap->implemented &= ~drop;
                                cap->mds_wanted = wanted;
+                               if (cap == ci->i_auth_cap &&
+                                   !(wanted & CEPH_CAP_ANY_FILE_WR))
+                                       ci->i_requested_max_size = 0;
                        } else {
                                dout("encode_inode_release %p cap %p %s"
                                     " (force)\n", inode, cap,