]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mds: add new inode quiescelock
authorPatrick Donnelly <pdonnell@redhat.com>
Tue, 9 Jan 2024 19:04:45 +0000 (14:04 -0500)
committerPatrick Donnelly <pdonnell@redhat.com>
Wed, 20 Mar 2024 14:56:54 +0000 (10:56 -0400)
Signed-off-by: Patrick Donnelly <pdonnell@redhat.com>
doc/dev/mds_internals/locking.rst
src/include/ceph_fs.h
src/mds/CInode.cc
src/mds/CInode.h
src/mds/Locker.cc
src/mds/Locker.h
src/mds/Migrator.cc
src/mds/Server.cc
src/mds/SimpleLock.cc
src/mds/SimpleLock.h

index cfd934f3f31a5c7c8cda07e156255b96e580ca91..4d21b895bea4a57acd3e706b2feaca6445636d3f 100644 (file)
@@ -17,6 +17,7 @@ MDS defines a handful of lock types associated with different metadata for an in
 
   CEPH_LOCK_DN       - dentry
   CEPH_LOCK_DVERSION - dentry version
+  CEPH_LOCK_IQUIESCE - inode quiesce lock (a type of superlock)
   CEPH_LOCK_IVERSION - inode version
   CEPH_LOCK_IAUTH    - mode, uid, gid
   CEPH_LOCK_ILINK    - nlink
index 2454216802651b133682809aaea604174219f7f6..016645ab5b020c9be6f8b9c5c8d404bf6a7d18e0 100644 (file)
@@ -333,18 +333,24 @@ extern const char *ceph_mds_state_name(int s);
  */
 #define CEPH_LOCK_DN          (1 << 0)
 #define CEPH_LOCK_DVERSION    (1 << 1)
-#define CEPH_LOCK_ISNAP       (1 << 4)  /* snapshot lock. MDS internal */
-#define CEPH_LOCK_IPOLICY     (1 << 5)  /* policy lock on dirs. MDS internal */
-#define CEPH_LOCK_IFILE       (1 << 6)
-#define CEPH_LOCK_INEST       (1 << 7)  /* mds internal */
-#define CEPH_LOCK_IDFT        (1 << 8)  /* dir frag tree */
-#define CEPH_LOCK_IAUTH       (1 << 9)
-#define CEPH_LOCK_ILINK       (1 << 10)
-#define CEPH_LOCK_IXATTR      (1 << 11)
-#define CEPH_LOCK_IFLOCK      (1 << 12)  /* advisory file locks */
-#define CEPH_LOCK_IVERSION    (1 << 13)  /* mds internal */
-
-#define CEPH_LOCK_IFIRST      CEPH_LOCK_ISNAP
+#define CEPH_LOCK_IQUIESCE    (1 << 4)  /* mds internal */
+#define CEPH_LOCK_ISNAP       (1 << 5)  /* snapshot lock. MDS internal */
+#define CEPH_LOCK_IPOLICY     (1 << 6)  /* policy lock on dirs. MDS internal */
+#define CEPH_LOCK_IFILE       (1 << 7)
+#define CEPH_LOCK_INEST       (1 << 8)  /* mds internal */
+#define CEPH_LOCK_IDFT        (1 << 9)  /* dir frag tree */
+#define CEPH_LOCK_IAUTH       (1 << 10)
+#define CEPH_LOCK_ILINK       (1 << 11)
+#define CEPH_LOCK_IXATTR      (1 << 12)
+#define CEPH_LOCK_IFLOCK      (1 << 13)  /* advisory file locks */
+#define CEPH_LOCK_IVERSION    (1 << 14)  /* mds internal */
+
+#define CEPH_LOCK_IFIRST      CEPH_LOCK_IQUIESCE
+#define CEPH_LOCK_ILAST       CEPH_LOCK_IVERSION
+
+static inline bool is_inode_lock(int l) {
+  return (CEPH_LOCK_IFIRST <= l && l <= CEPH_LOCK_ILAST);
+}
 
 
 /* client_session ops */
index 6d15908e545520ed7acb5b4f5a089e20fc65a6d6..911bedf408feb0d6d02f91900e6374194a4f19f8 100644 (file)
@@ -257,6 +257,8 @@ ostream& operator<<(ostream& out, const CInode& in)
     out << " " << in.xattrlock;
   if (!in.versionlock.is_sync_and_unlocked())  
     out << " " << in.versionlock;
+  if (!in.quiescelock.is_sync_and_unlocked())
+    out << " " << in.quiescelock;
 
   // hack: spit out crap on which clients have caps
   if (in.get_inode()->client_ranges.size())
@@ -324,6 +326,7 @@ CInode::CInode(MDCache *c, bool auth, snapid_t f, snapid_t l) :
     item_dirty_dirfrag_nest(this),
     item_dirty_dirfrag_dirfragtree(this),
     pop(c->decayrate),
+    quiescelock(this, &quiescelock_type),
     versionlock(this, &versionlock_type),
     authlock(this, &authlock_type),
     linklock(this, &linklock_type),
@@ -1660,6 +1663,7 @@ SimpleLock* CInode::get_lock(int type)
     case CEPH_LOCK_INEST: return &nestlock;
     case CEPH_LOCK_IFLOCK: return &flocklock;
     case CEPH_LOCK_IPOLICY: return &policylock;
+    case CEPH_LOCK_IQUIESCE: return &quiescelock;
   }
   return 0;
 }
@@ -2203,6 +2207,13 @@ void CInode::encode_lock_state(int type, bufferlist& bl)
   case CEPH_LOCK_IPOLICY:
     encode_lock_ipolicy(bl);
     break;
+
+  case CEPH_LOCK_IQUIESCE: {
+    ENCODE_START(1, 1, bl);
+    /* skeleton */
+    ENCODE_FINISH(bl);
+    break;
+  }
   
   default:
     ceph_abort();
@@ -2270,6 +2281,13 @@ void CInode::decode_lock_state(int type, const bufferlist& bl)
     decode_lock_ipolicy(p);
     break;
 
+  case CEPH_LOCK_IQUIESCE: {
+    DECODE_START(1, p);
+    /* skeleton */
+    DECODE_FINISH(p);
+    break;
+  }
+
   default:
     ceph_abort();
   }
@@ -4264,8 +4282,8 @@ void CInode::_encode_locks_full(bufferlist& bl)
   encode(nestlock, bl);
   encode(flocklock, bl);
   encode(policylock, bl);
-
   encode(loner_cap, bl);
+  encode(quiescelock, bl);
 }
 void CInode::_decode_locks_full(bufferlist::const_iterator& p)
 {
@@ -4279,15 +4297,15 @@ void CInode::_decode_locks_full(bufferlist::const_iterator& p)
   decode(nestlock, p);
   decode(flocklock, p);
   decode(policylock, p);
-
   decode(loner_cap, p);
   set_loner_cap(loner_cap);
   want_loner_cap = loner_cap;  // for now, we'll eval() shortly.
+  decode(quiescelock, p);
 }
 
 void CInode::_encode_locks_state_for_replica(bufferlist& bl, bool need_recover)
 {
-  ENCODE_START(1, 1, bl);
+  ENCODE_START(2, 1, bl);
   authlock.encode_state_for_replica(bl);
   linklock.encode_state_for_replica(bl);
   dirfragtreelock.encode_state_for_replica(bl);
@@ -4298,11 +4316,13 @@ void CInode::_encode_locks_state_for_replica(bufferlist& bl, bool need_recover)
   flocklock.encode_state_for_replica(bl);
   policylock.encode_state_for_replica(bl);
   encode(need_recover, bl);
+  quiescelock.encode_state_for_replica(bl);
   ENCODE_FINISH(bl);
 }
 
 void CInode::_encode_locks_state_for_rejoin(bufferlist& bl, int rep)
 {
+  // TODO versioning?
   authlock.encode_state_for_replica(bl);
   linklock.encode_state_for_replica(bl);
   dirfragtreelock.encode_state_for_rejoin(bl, rep);
@@ -4312,11 +4332,12 @@ void CInode::_encode_locks_state_for_rejoin(bufferlist& bl, int rep)
   snaplock.encode_state_for_replica(bl);
   flocklock.encode_state_for_replica(bl);
   policylock.encode_state_for_replica(bl);
+  quiescelock.encode_state_for_replica(bl);
 }
 
 void CInode::_decode_locks_state_for_replica(bufferlist::const_iterator& p, bool is_new)
 {
-  DECODE_START(1, p);
+  DECODE_START(2, p);
   authlock.decode_state(p, is_new);
   linklock.decode_state(p, is_new);
   dirfragtreelock.decode_state(p, is_new);
@@ -4329,6 +4350,11 @@ void CInode::_decode_locks_state_for_replica(bufferlist::const_iterator& p, bool
 
   bool need_recover;
   decode(need_recover, p);
+
+  if (struct_v >= 2) {
+    quiescelock.decode_state(p, is_new);
+  }
+
   if (need_recover && is_new) {
     // Auth mds replicated this inode while it's recovering. Auth mds may take xlock on the lock
     // and change the object when replaying unsafe requests.
@@ -4341,6 +4367,7 @@ void CInode::_decode_locks_state_for_replica(bufferlist::const_iterator& p, bool
     snaplock.mark_need_recover();
     flocklock.mark_need_recover();
     policylock.mark_need_recover();
+    quiescelock.mark_need_recover();
   }
   DECODE_FINISH(p);
 }
@@ -4356,6 +4383,7 @@ void CInode::_decode_locks_rejoin(bufferlist::const_iterator& p, MDSContext::vec
   snaplock.decode_state_rejoin(p, waiters, survivor);
   flocklock.decode_state_rejoin(p, waiters, survivor);
   policylock.decode_state_rejoin(p, waiters, survivor);
+  quiescelock.decode_state_rejoin(p, waiters, survivor);
 
   if (!dirfragtreelock.is_stable() && !dirfragtreelock.is_wrlocked())
     eval_locks.push_back(&dirfragtreelock);
@@ -4370,7 +4398,7 @@ void CInode::_decode_locks_rejoin(bufferlist::const_iterator& p, MDSContext::vec
 
 void CInode::encode_export(bufferlist& bl)
 {
-  ENCODE_START(5, 4, bl);
+  ENCODE_START(6, 6, bl);
   _encode_base(bl, mdcache->mds->mdsmap->get_up_features());
 
   encode(state, bl);
@@ -4421,7 +4449,7 @@ void CInode::finish_export()
 void CInode::decode_import(bufferlist::const_iterator& p,
                           LogSegment *ls)
 {
-  DECODE_START(5, p);
+  DECODE_START(6, p);
 
   _decode_base(p);
 
@@ -5099,6 +5127,10 @@ void CInode::dump(Formatter *f, int flags) const
     f->open_object_section("policylock");
     policylock.dump(f);
     f->close_section();
+
+    f->open_object_section("quiescelock");
+    quiescelock.dump(f);
+    f->close_section();
   }
 
   if (flags & DUMP_STATE) {
index 1030b7466238a06fcd874963f0c9e7e02081d443..1aaa5eadb5f524d87f4793dfb409f31a77f174da 100644 (file)
@@ -1100,18 +1100,23 @@ class CInode : public MDSCacheObject, public InodeStoreBase, public Counter<CIno
   static const LockType nestlock_type;
   static const LockType flocklock_type;
   static const LockType policylock_type;
+  static const LockType quiescelock_type;
 
-  // FIXME not part of mempool
-  LocalLockC  versionlock;
-  SimpleLock authlock;
-  SimpleLock linklock;
-  ScatterLock dirfragtreelock;
-  ScatterLock filelock;
-  SimpleLock xattrlock;
-  SimpleLock snaplock;
-  ScatterLock nestlock;
-  SimpleLock flocklock;
-  SimpleLock policylock;
+  /* Please consult doc/dev/mds_internals/quiesce.rst for information about the
+   * quiescelock.
+   */
+
+  SimpleLock quiescelock; // FIXME not part of mempool
+  LocalLockC versionlock; // FIXME not part of mempool
+  SimpleLock authlock; // FIXME not part of mempool
+  SimpleLock linklock; // FIXME not part of mempool
+  ScatterLock dirfragtreelock; // FIXME not part of mempool
+  ScatterLock filelock; // FIXME not part of mempool
+  SimpleLock xattrlock; // FIXME not part of mempool
+  SimpleLock snaplock; // FIXME not part of mempool
+  ScatterLock nestlock; // FIXME not part of mempool
+  SimpleLock flocklock; // FIXME not part of mempool
+  SimpleLock policylock; // FIXME not part of mempool
 
   // -- caps -- (new)
   // client caps
index e40e1c9b8f82fbc35afd86eae5006d399f8362a0..897f2ec5d03c25cf80ad81d47c98cd1041db77a6 100644 (file)
@@ -148,6 +148,8 @@ bool Locker::try_rdlock_snap_layout(CInode *in, const MDRequestRef& mdr,
   int depth = -1;
   bool found_locked = false;
   bool found_layout = false;
+  bool dropped_locks = false;
+  std::string_view err;
 
   ceph_assert(!want_layout || n == 0);
 
@@ -156,12 +158,13 @@ bool Locker::try_rdlock_snap_layout(CInode *in, const MDRequestRef& mdr,
   CInode *t = in;
   while (true) {
     ++depth;
+
     if (!found_locked && mdr->is_rdlocked(&t->snaplock))
       found_locked = true;
 
     if (!found_locked) {
       if (!t->snaplock.can_rdlock(client)) {
-        mdr->mark_event("failed to acquire snap lock");
+        err = "failed to acquire snap lock"sv;
        t->snaplock.add_waiter(SimpleLock::WAIT_RD, new C_MDS_RetryRequest(mdcache, mdr));
        goto failed;
       }
@@ -172,7 +175,7 @@ bool Locker::try_rdlock_snap_layout(CInode *in, const MDRequestRef& mdr,
     if (want_layout && !found_layout) {
       if (!mdr->is_rdlocked(&t->policylock)) {
        if (!t->policylock.can_rdlock(client)) {
-          mdr->mark_event("failed to acquire policy lock");
+          err = "failed to acquire policy lock"sv;
          t->policylock.add_waiter(SimpleLock::WAIT_RD, new C_MDS_RetryRequest(mdcache, mdr));
          goto failed;
        }
@@ -200,8 +203,11 @@ bool Locker::try_rdlock_snap_layout(CInode *in, const MDRequestRef& mdr,
 failed:
   dout(10) << __func__ << " failed" << dendl;
 
-  drop_locks(mdr.get(), nullptr);
-  mdr->drop_local_auth_pins();
+  mdr->mark_event(err);
+  if (!dropped_locks) {
+    drop_locks(mdr.get(), nullptr);
+    mdr->drop_local_auth_pins();
+  }
   return false;
 }
 
@@ -224,7 +230,8 @@ struct MarkEventOnDestruct {
 bool Locker::acquire_locks(const MDRequestRef& mdr,
                           MutationImpl::LockOpVec& lov,
                           CInode *auth_pin_freeze,
-                          bool auth_pin_nonblocking)
+                          bool auth_pin_nonblocking,
+                           bool skip_quiesce)
 {
   dout(10) << "acquire_locks " << *mdr << dendl;
   dout(20) << " lov = " << lov << dendl;
@@ -242,10 +249,12 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
     mustpin.insert(auth_pin_freeze);
 
   // xlocks
+  bool need_quiescelock = !skip_quiesce;
   for (size_t i = 0; i < lov.size(); ++i) {
     auto& p = lov[i];
     SimpleLock *lock = p.lock;
     MDSCacheObject *object = lock->get_parent();
+    auto t = lock->get_type();
 
     if (p.is_xlock()) {
       if ((lock->get_type() == CEPH_LOCK_ISNAP ||
@@ -301,19 +310,30 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
          lov.add_xlock(&dn->versionlock, i + 1);
        }
       }
-      if (lock->get_type() >= CEPH_LOCK_IFIRST && lock->get_type() != CEPH_LOCK_IVERSION) {
-       // inode version lock?
-       CInode *in = static_cast<CInode*>(object);
-       if (!in->is_auth())
-         continue;
-       if (mdr->is_leader()) {
-         // leader.  wrlock versionlock so we can pipeline inode updates to journal.
-         lov.add_wrlock(&in->versionlock, i + 1);
-       } else {
-         // peer.  exclusively lock the inode version (i.e. block other journal updates).
-         // this makes rollback safe.
-         lov.add_xlock(&in->versionlock, i + 1);
-       }
+      if (is_inode_lock(t)) {
+        switch (t) {
+          case CEPH_LOCK_IVERSION:
+          case CEPH_LOCK_IQUIESCE:
+            break;
+          default:
+           CInode *in = static_cast<CInode*>(object);
+            if (need_quiescelock) {
+              need_quiescelock = false;
+              lov.add_rdlock(&in->quiescelock, i + 1);
+            }
+           if (!in->is_auth())
+             continue;
+           // inode version lock?
+           if (mdr->is_leader()) {
+             // leader.  wrlock versionlock so we can pipeline inode updates to journal.
+             lov.add_wrlock(&in->versionlock, i + 1);
+           } else {
+             // peer.  exclusively lock the inode version (i.e. block other journal updates).
+             // this makes rollback safe.
+             lov.add_xlock(&in->versionlock, i + 1);
+           }
+            break;
+        }
       }
     } else if (p.is_wrlock()) {
       dout(20) << " must wrlock " << *lock << " " << *object << dendl;
@@ -327,12 +347,21 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
                 << " in case we need to request a scatter" << dendl;
        mustpin.insert(object);
       }
+      if (need_quiescelock && is_inode_lock(t) && t != CEPH_LOCK_IQUIESCE) {
+       CInode *in = static_cast<CInode*>(object);
+        lov.add_rdlock(&in->quiescelock, i + 1);
+        need_quiescelock = false;
+      }
     } else if (p.is_remote_wrlock()) {
       dout(20) << " must remote_wrlock on mds." << p.wrlock_target << " "
               << *lock << " " << *object << dendl;
       mustpin.insert(object);
+      if (need_quiescelock && is_inode_lock(t) && t != CEPH_LOCK_IQUIESCE) {
+       CInode *in = static_cast<CInode*>(object);
+        lov.add_rdlock(&in->quiescelock, i + 1);
+        need_quiescelock = false;
+      }
     } else if (p.is_rdlock()) {
-
       dout(20) << " must rdlock " << *lock << " " << *object << dendl;
       if (object->is_auth()) {
        mustpin.insert(object);
@@ -342,6 +371,33 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
                 << " in case we need to request a rdlock" << dendl;
        mustpin.insert(object);
       }
+
+      /* We treat rdlocks differently when adding the quiescelock. If the lock
+       * can be acquired immediately for reading without waiting
+       * (SimpleLock::can_rdlock), then skip adding the quiescelock. This is to
+       * allow some rdonly operations (like lookup) to proceed without blocking
+       * on the exclusively locked quiescelock. This is safe from deadlock (due
+       * to lock ordering) when Locker::acquire_locks is called more than once
+       * with different LockOpVectors for a given inode (already a dangerous
+       * thing to do) where there may be a wrlock/xlock in one set but not the
+       * other. The reason is simple: if Locker::acquire_locks ever adds the
+       * quiescelock, it is always the first lock to be acquired, and if it is
+       * xlocked, then all locks are dropped (s.f.
+       * Locker::handle_quiesce_failure). So adding the quiescelock can never
+       * contribute to deadlock.
+       */
+
+      if (need_quiescelock && !mdr->is_rdlocked(lock)) {
+        /* Can we get the lock without waiting? */
+        if (!lock->can_rdlock(client)) {
+          /* To prevent deadlock where an op holds a parent snaplock
+           * (Locker::try_rdlock_snap_layout), add quiescelock.
+           */
+         CInode *in = static_cast<CInode*>(object);
+          lov.add_rdlock(&in->quiescelock, i + 1);
+          need_quiescelock = false;
+        }
+      }
     } else {
       ceph_assert(0 == "locker unknown lock operation");
     }
@@ -508,6 +564,7 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
   // make sure they match currently acquired locks.
   for (const auto& p : lov) {
     auto lock = p.lock;
+    auto t = lock->get_type();
     if (p.is_xlock()) {
       if (mdr->is_xlocked(lock)) {
        dout(10) << " already xlocked " << *lock << " " << *lock->get_parent() << dendl;
@@ -516,7 +573,11 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
       if (mdr->locking && lock != mdr->locking)
        cancel_locking(mdr.get(), &issue_set);
       if (!xlock_start(lock, mdr)) {
-       marker.message = "failed to xlock, waiting";
+        if (t == CEPH_LOCK_IQUIESCE) {
+          handle_quiesce_failure(mdr, marker.message);
+        } else {
+         marker.message = "failed to xlock, waiting";
+        }
        goto out;
       }
       dout(10) << " got xlock on " << *lock << " " << *lock->get_parent() << dendl;
@@ -542,18 +603,26 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
        if (p.is_remote_wrlock()) {
          // nowait if we have already gotten remote wrlock
          if (!wrlock_try(lock, mdr, _client)) {
-           marker.message = "failed to wrlock, dropping remote wrlock and waiting";
            // can't take the wrlock because the scatter lock is gathering. need to
            // release the remote wrlock, so that the gathering process can finish.
            ceph_assert(it != mdr->locks.end());
            remote_wrlock_finish(it, mdr.get());
            remote_wrlock_start(lock, p.wrlock_target, mdr);
+            if (t == CEPH_LOCK_IQUIESCE) {
+              handle_quiesce_failure(mdr, marker.message);
+            } else {
+             marker.message = "failed to wrlock, dropping remote wrlock and waiting";
+            }
            goto out;
          }
        } else {
          if (!wrlock_start(p, mdr)) {
            ceph_assert(!p.is_remote_wrlock());
-           marker.message = "failed to wrlock, waiting";
+            if (t == CEPH_LOCK_IQUIESCE) {
+              handle_quiesce_failure(mdr, marker.message);
+            } else {
+             marker.message = "failed to wrlock, waiting";
+            }
            goto out;
          }
        }
@@ -584,7 +653,11 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
       }
 
       if (!rdlock_start(lock, mdr)) {
-       marker.message = "failed to rdlock, waiting";
+        if (t == CEPH_LOCK_IQUIESCE) {
+          handle_quiesce_failure(mdr, marker.message);
+        } else {
+         marker.message = "failed to rdlock, waiting";
+        }
        goto out;
       }
       dout(10) << " got rdlock on " << *lock << " " << *lock->get_parent() << dendl;
@@ -600,6 +673,23 @@ bool Locker::acquire_locks(const MDRequestRef& mdr,
   return result;
 }
 
+/* Dropping *all* locks here is necessary so parent directory
+ * snap/layout/quiesce locks are unlocked for a future mksnap.  This is the
+ * primary purpose of the new quiescelock. An op, e.g. getattr, cannot block
+ * waiting for another lock held by quiesce_subvolume_inode, e.g. filelock,
+ * which will prevent a mksnap on a subvolume inode (because getattr will
+ * already have gotten parent snaplocks, see Locker::try_rdlock_snap_layout).
+ */
+
+void Locker::handle_quiesce_failure(const MDRequestRef& mdr, std::string_view& marker)
+{
+  dout(10) << " failed to acquire quiesce lock; dropping all locks" << dendl;
+  marker = "failed to acquire quiesce lock"sv;
+  drop_locks(mdr.get(), NULL);
+  mdr->drop_local_auth_pins();
+}
+
+
 void Locker::notify_freeze_waiter(MDSCacheObject *o)
 {
   CDir *dir = NULL;
@@ -1336,6 +1426,8 @@ bool Locker::eval(CInode *in, int mask, bool caps_imported)
     eval_any(&in->flocklock, &need_issue, &finishers, caps_imported);
   if (mask & CEPH_LOCK_IPOLICY)
     eval_any(&in->policylock, &need_issue, &finishers, caps_imported);
+  if (mask & CEPH_LOCK_IQUIESCE)
+    eval_any(&in->quiescelock, &need_issue, &finishers, caps_imported);
 
   // drop loner?
   if (in->is_auth() && in->is_head() && in->get_wanted_loner() != in->get_loner()) {
@@ -4418,6 +4510,7 @@ SimpleLock *Locker::get_lock(int lock_type, const MDSCacheObjectInfo &info)
   case CEPH_LOCK_ISNAP:
   case CEPH_LOCK_IFLOCK:
   case CEPH_LOCK_IPOLICY:
+  case CEPH_LOCK_IQUIESCE:
     {
       CInode *in = mdcache->get_inode(info.ino, info.snapid);
       if (!in) {
@@ -4434,6 +4527,7 @@ SimpleLock *Locker::get_lock(int lock_type, const MDSCacheObjectInfo &info)
       case CEPH_LOCK_ISNAP: return &in->snaplock;
       case CEPH_LOCK_IFLOCK: return &in->flocklock;
       case CEPH_LOCK_IPOLICY: return &in->policylock;
+      case CEPH_LOCK_IQUIESCE: return &in->quiescelock;
       }
     }
 
@@ -4459,6 +4553,7 @@ void Locker::handle_lock(const cref_t<MLock> &m)
 
   switch (lock->get_type()) {
   case CEPH_LOCK_DN:
+  case CEPH_LOCK_IQUIESCE:
   case CEPH_LOCK_IAUTH:
   case CEPH_LOCK_ILINK:
   case CEPH_LOCK_ISNAP:
index eed421ba0db9e14d192b77b0c98e665f778fd940..e3ecdf8131b1470ecc069a8d7f37dcd44243c0bd 100644 (file)
@@ -54,7 +54,8 @@ public:
   bool acquire_locks(const MDRequestRef& mdr,
                     MutationImpl::LockOpVec& lov,
                     CInode *auth_pin_freeze=NULL,
-                    bool auth_pin_nonblocking=false);
+                    bool auth_pin_nonblocking=false,
+                     bool skip_quiesce=false);
 
   bool try_rdlock_snap_layout(CInode *in, const MDRequestRef& mdr,
                              int n=0, bool want_layout=false);
@@ -259,6 +260,8 @@ private:
   friend class LockerContext;
   friend class LockerLogContext;
 
+  void handle_quiesce_failure(const MDRequestRef& mdr, std::string_view& marker);
+
   bool any_late_revoking_caps(xlist<Capability*> const &revoking, double timeout) const;
   uint64_t calc_new_max_size(const CInode::inode_const_ptr& pi, uint64_t size);
   __u32 get_xattr_total_length(CInode::mempool_xattr_map &xattr);
index a3e9d31ded14132fa5e719877ed726ed6b1fb686..b5690f4e2d780563fd5d4a332e98105dcaa020cb 100644 (file)
@@ -1698,6 +1698,7 @@ void Migrator::finish_export_inode(CInode *in, mds_rank_t peer,
   in->snaplock.export_twiddle();
   in->flocklock.export_twiddle();
   in->policylock.export_twiddle();
+  in->quiescelock.export_twiddle();
   
   // mark auth
   ceph_assert(in->is_auth());
@@ -3245,6 +3246,10 @@ void Migrator::decode_import_inode(CDentry *dn, bufferlist::const_iterator& blp,
   if (in->policylock.is_stable() &&
       in->policylock.get_state() != LOCK_SYNC)
       mds->locker->try_eval(&in->policylock, NULL);
+
+  if (in->quiescelock.is_stable() &&
+      in->quiescelock.get_state() != LOCK_SYNC)
+      mds->locker->try_eval(&in->quiescelock, NULL);
 }
 
 void Migrator::decode_import_inode_caps(CInode *in, bool auth_cap,
index 5749bfa7f263d9546dc080097cf4bc4075e13e9e..d3d14a61ce6fd29c37e84fe284de680aa85f007f 100644 (file)
@@ -3076,8 +3076,9 @@ void Server::dispatch_peer_request(const MDRequestRef& mdr)
          replycode = MMDSPeerRequest::OP_WRLOCKACK;
          break;
        }
-       
-       if (!mds->locker->acquire_locks(mdr, lov))
+
+        // don't add quiescelock, let the peer acquire that rdlock themselves
+       if (!mds->locker->acquire_locks(mdr, lov, nullptr, {}, false, true))
          return;
        
        // ack
@@ -4890,6 +4891,8 @@ void Server::handle_client_readdir(const MDRequestRef& mdr)
       return;
   }
 
+  /* readdir can add dentries to cache: acquire the quiescelock */
+  lov.add_rdlock(&diri->quiescelock);
   lov.add_rdlock(&diri->filelock);
   lov.add_rdlock(&diri->dirfragtreelock);
 
index 4dea69f2b5d11cbc3a4c8554071a15cd792b808d..43f710ec68a50eef73a568cd0aac2831f4966167 100644 (file)
@@ -57,6 +57,7 @@ int SimpleLock::get_wait_shift() const {
     case CEPH_LOCK_INEST:    return 9*SimpleLock::WAIT_BITS;
     case CEPH_LOCK_IFLOCK:   return 10*SimpleLock::WAIT_BITS;
     case CEPH_LOCK_IPOLICY:  return 11*SimpleLock::WAIT_BITS;
+    case CEPH_LOCK_IQUIESCE: return 12*SimpleLock::WAIT_BITS;
     default:
       ceph_abort();
   }
index 8d3ea32e2442c8c09c06253d29321df81d326f06..d0b6578302d7720ce3a9eeaa0af118e7ecc946dd 100644 (file)
@@ -41,6 +41,7 @@ struct LockType {
   explicit LockType(int t) : type(t) {
     switch (type) {
     case CEPH_LOCK_DN:
+    case CEPH_LOCK_IQUIESCE:
     case CEPH_LOCK_IAUTH:
     case CEPH_LOCK_ILINK:
     case CEPH_LOCK_IXATTR:
@@ -150,6 +151,7 @@ public:
       case CEPH_LOCK_ISNAP: return "isnap";
       case CEPH_LOCK_IFLOCK: return "iflock";
       case CEPH_LOCK_IPOLICY: return "ipolicy";
+      case CEPH_LOCK_IQUIESCE: return "iquiesce";
       default: return "unknown";
     }
   }