]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mds: do remove the cap when seqs equal or larger than last issue 58296/head
authorXiubo Li <xiubli@redhat.com>
Thu, 11 Apr 2024 01:53:04 +0000 (09:53 +0800)
committerPatrick Donnelly <pdonnell@redhat.com>
Mon, 30 Sep 2024 14:47:00 +0000 (10:47 -0400)
There is a race in case of:

   MDS                            rw Client
- Issue the 'Asx' caps to
  rw client
                             - Adds the cap, then removes it
       later by queuing it to the cap
       release list. But the cap->seq
       may have been updated by previous
       cap grant requests.
       And the cap grant request won't
       increase the 'last_issue' seq in
       MDS.
- ro client's lookup
  request comes and the
  MDS sends a 'Ax' caps
  revoke request to rw
  client by increasing
  the 'seq'.
                             - The revoke request just finds
       that the cap doesn't exist, then
       queues a new cap release
       immediately with the new 'seq'.
       Then trigger to flush the pending
       cap releases to MDS.
- Just receives the cap
  release request but the
  'seq' > cap's 'last_issue',
  then MDS will skip
  removing the cap. And
  then the _do_cap_release()
  will issue the 'Ax' caps
  back to rw client.

  Then wakes up the ro
  client's lookup request,
  while the lookup request
  will try to revoke the
  'Ax' caps again from the
  rw client.

This will cause a spinlock infinitely in mds side.

Fixes: https://tracker.ceph.com/issues/64977
Signed-off-by: Xiubo Li <xiubli@redhat.com>
(cherry picked from commit 345978e7607e227854ccc78b066b274b97940391)

src/mds/Locker.cc

index 7b0c2eabac53c2edd4e78bde7654cd97439bf4b1..055cdbecd956e430a137579b1e36645d3283f82e 100644 (file)
@@ -4035,8 +4035,8 @@ void Locker::_do_cap_release(client_t client, inodeno_t ino, uint64_t cap_id,
                   new C_Locker_RetryCapRelease(this, client, ino, cap_id, mseq, seq));
     return;
   }
-  if (seq != cap->get_last_issue()) {
-    dout(7) << " issue_seq " << seq << " != " << cap->get_last_issue() << dendl;
+  if (seq < cap->get_last_issue()) {
+    dout(7) << " issue_seq " << seq << " < " << cap->get_last_issue() << dendl;
     // clean out any old revoke history
     cap->clean_revoke_from(seq);
     eval_cap_gather(in);