]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
client: reset requested_max_size if file write is not wanted 34767/head
authorYan, Zheng <zyan@redhat.com>
Fri, 20 Mar 2020 05:15:45 +0000 (13:15 +0800)
committerNathan Cutler <ncutler@suse.com>
Mon, 27 Apr 2020 10:30:09 +0000 (12:30 +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'.

Fixes: https://tracker.ceph.com/issues/44801
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
(cherry picked from commit 1d676851e3491c26d3bf543bf357731d11d82a85)

src/client/Client.cc

index d91f42b429d1b6178cd0e31c1a03b629e3984e54..95b1e6719c726b8398a7e6adf18e28d95a45d2ee 100644 (file)
@@ -1839,7 +1839,7 @@ int Client::encode_inode_release(Inode *in, MetaRequest *req,
 {
   ldout(cct, 20) << __func__ << " enter(in:" << *in << ", req:" << req
           << " mds:" << mds << ", drop:" << drop << ", unless:" << unless
-          << ", have:" << ", force:" << force << ")" << dendl;
+          << ", force:" << force << ")" << dendl;
   int released = 0;
   auto it = in->caps.find(mds);
   if (it != in->caps.end()) {
@@ -1847,15 +1847,20 @@ int Client::encode_inode_release(Inode *in, MetaRequest *req,
     drop &= ~(in->dirty_caps | get_caps_used(in));
     if ((drop & cap.issued) &&
        !(unless & cap.issued)) {
-      ldout(cct, 25) << "Dropping caps. Initial " << ccap_string(cap.issued) << dendl;
+      ldout(cct, 25) << "dropping caps " << ccap_string(drop) << dendl;
       cap.issued &= ~drop;
       cap.implemented &= ~drop;
       released = 1;
-      ldout(cct, 25) << "Now have: " << ccap_string(cap.issued) << dendl;
     } else {
       released = force;
     }
     if (released) {
+      cap.wanted = in->caps_wanted();
+      if (&cap == in->auth_cap &&
+         !(cap.wanted & CEPH_CAP_ANY_FILE_WR)) {
+       in->requested_max_size = 0;
+       ldout(cct, 25) << "reset requested_max_size due to not wanting any file write cap" << dendl;
+      }
       ceph_mds_request_release rel;
       rel.ino = in->ino;
       rel.cap_id = cap.cap_id;
@@ -3221,13 +3226,16 @@ int Client::get_caps(Inode *in, int need, int want, int *phave, loff_t endoff)
     bool waitfor_commit = false;
 
     if (have & need & CEPH_CAP_FILE_WR) {
-      if (endoff > 0 &&
-         (endoff >= (loff_t)in->max_size ||
-          endoff > (loff_t)(in->size << 1)) &&
-         endoff > (loff_t)in->wanted_max_size) {
-       ldout(cct, 10) << "wanted_max_size " << in->wanted_max_size << " -> " << endoff << dendl;
-       in->wanted_max_size = endoff;
-       check_caps(in, 0);
+      if (endoff > 0) {
+        if ((endoff >= (loff_t)in->max_size ||
+             endoff > (loff_t)(in->size << 1)) &&
+            endoff > (loff_t)in->wanted_max_size) {
+          ldout(cct, 10) << "wanted_max_size " << in->wanted_max_size << " -> " << endoff << dendl;
+          in->wanted_max_size = endoff;
+        }
+        if (in->wanted_max_size > in->max_size &&
+            in->wanted_max_size > in->requested_max_size)
+          check_caps(in, 0);
       }
 
       if (endoff >= 0 && endoff > (loff_t)in->max_size) {
@@ -3411,9 +3419,14 @@ void Client::send_cap(Inode *in, MetaSession *session, Cap *cap,
   m->set_snap_follows(follows);
   cap->wanted = want;
   if (cap == in->auth_cap) {
-    m->set_max_size(in->wanted_max_size);
-    in->requested_max_size = in->wanted_max_size;
-    ldout(cct, 15) << "auth cap, setting max_size = " << in->requested_max_size << dendl;
+    if (want & CEPH_CAP_ANY_FILE_WR) {
+      m->set_max_size(in->wanted_max_size);
+      in->requested_max_size = in->wanted_max_size;
+      ldout(cct, 15) << "auth cap, requesting max_size " << in->requested_max_size << dendl;
+    } else {
+      in->requested_max_size = 0;
+      ldout(cct, 15) << "auth cap, reset requested_max_size due to not wanting any file write cap" << dendl;
+    }
   }
 
   if (!session->flushing_caps_tids.empty())
@@ -4882,8 +4895,10 @@ void Client::handle_cap_import(MetaSession *session, Inode *in, const MConstRef<
   SnapRealm *realm = NULL;
   update_snap_trace(m->snapbl, &realm);
 
+  int issued = m->get_caps();
+  int wanted = m->get_wanted();
   add_update_cap(in, session, m->get_cap_id(),
-                m->get_caps(), m->get_wanted(), m->get_seq(), m->get_mseq(),
+                issued, wanted, m->get_seq(), m->get_mseq(),
                 m->get_realm(), CEPH_CAP_FLAG_AUTH, cap_perms);
   
   if (cap && cap->cap_id == m->peer.cap_id) {
@@ -4894,6 +4909,11 @@ void Client::handle_cap_import(MetaSession *session, Inode *in, const MConstRef<
     put_snap_realm(realm);
   
   if (in->auth_cap && in->auth_cap->session == session) {
+    if (!(wanted & CEPH_CAP_ANY_FILE_WR) ||
+       in->requested_max_size > m->get_max_size()) {
+      in->requested_max_size = 0;
+      ldout(cct, 15) << "reset requested_max_size after cap import" << dendl;
+    }
     // reflush any/all caps (if we are now the auth_cap)
     kick_flushing_caps(in, session);
   }