]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mds: initial code for lock cache
authorYan, Zheng <zyan@redhat.com>
Mon, 30 Sep 2019 06:20:17 +0000 (14:20 +0800)
committerYan, Zheng <zyan@redhat.com>
Thu, 12 Dec 2019 18:04:12 +0000 (02:04 +0800)
The lock cache preserves locks and authpins required for directory
operations. MDS can create a lock cache when it has acquired all locks
of a directory operation. The lock cache can be used to for later
operations of the same type on the same directory.

For example, when mds has acquired all locks of a unlink operation,
it creates a lock cache, which holds holds wrlocks on direcotry inode's
filelock and nestlock, rdlocks on ancestor inodes' snaplocks. For later
unlink operations on the same directory, MDS only needs to xlock the
dentry to unlink and xlock linklock of the inode to unlink.

Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
src/mds/CInode.h
src/mds/Capability.cc
src/mds/Capability.h
src/mds/Locker.cc
src/mds/Locker.h
src/mds/MDCache.cc
src/mds/Mutation.cc
src/mds/Mutation.h
src/mds/journal.cc

index c0b8a5fd65747a647ef2f74ffe76baed4f962226..1d29dd700f806e01adf378ff9fbcc30dcdbad60e 100644 (file)
@@ -513,7 +513,7 @@ class CInode : public MDSCacheObject, public InodeStoreBase, public Counter<CIno
   bool has_snap_data(snapid_t s);
   void purge_stale_snap_data(const std::set<snapid_t>& snaps);
 
-  bool has_dirfrags() { return !dirfrags.empty(); }
+  size_t get_num_dirfrags() const { return dirfrags.size(); }
   CDir* get_dirfrag(frag_t fg) {
     auto pi = dirfrags.find(fg);
     if (pi != dirfrags.end()) {
index ceaa4e069a4e68416cdf3677e7cb18166852c5b8..32f780ee20267ae8086ffc77687ec9b01c52afa4 100644 (file)
@@ -148,6 +148,7 @@ void Capability::revoke_info::generate_test_instances(std::list<Capability::revo
 Capability::Capability(CInode *i, Session *s, uint64_t id) :
   item_session_caps(this), item_snaprealm_caps(this),
   item_revoking_caps(this), item_client_revoking_caps(this),
+  lock_caches(member_offset(MDLockCache, item_cap_lock_cache)),
   inode(i), session(s), cap_id(id)
 {
   if (session) {
index c176327593e542458dffe575af7de28b40cc6683..a20685e2456465a018f6ea61fcdbfb88f7c04b57 100644 (file)
@@ -20,6 +20,7 @@
 #include "include/counter.h"
 #include "include/mempool.h"
 #include "include/xlist.h"
+#include "include/elist.h"
 
 #include "common/config.h"
 
@@ -62,6 +63,7 @@
 
 class CInode;
 class Session;
+class MDLockCache;
 
 namespace ceph {
   class Formatter;
@@ -361,6 +363,7 @@ public:
   xlist<Capability*>::item item_revoking_caps;
   xlist<Capability*>::item item_client_revoking_caps;
 
+  elist<MDLockCache*> lock_caches;
 private:
   void calc_issued() {
     _issued = _pending;
index 60db205f8911a0235c8d942dbd480f0fdd2a3d24..66d015e9fcfa55acb5eefa321e62265ff6bb54b5 100644 (file)
@@ -362,6 +362,7 @@ bool Locker::acquire_locks(MDRequestRef& mdr,
     }
     
     if (!object->is_auth()) {
+      ceph_assert(!mdr->lock_cache);
       if (object->is_ambiguous_auth()) {
        // wait
        dout(10) << " ambiguous auth, waiting to authpin " << *object << dendl;
@@ -376,6 +377,20 @@ bool Locker::acquire_locks(MDRequestRef& mdr,
     }
     int err = 0;
     if (!object->can_auth_pin(&err)) {
+      if (mdr->lock_cache) {
+       CDir *dir;
+       if (CInode *in = dynamic_cast<CInode*>(object)) {
+         dir = in->get_projected_parent_dir();
+       } else if (CDentry *dn = dynamic_cast<CDentry*>(object)) {
+         dir = dn->get_dir();
+       } else {
+         ceph_assert(0 == "unknown type of lock parent");
+       }
+       ceph_assert(dir->get_inode() == mdr->lock_cache->get_dir_inode());
+       /* forcibly auth pin if lock cache is used */
+       continue;
+      }
+
       // wait
       drop_locks(mdr.get());
       mdr->drop_local_auth_pins();
@@ -640,6 +655,13 @@ void Locker::_drop_locks(MutationImpl *mut, set<CInode*> *pneed_issue,
     }
   }
 
+  if (drop_rdlocks) {
+    if (mut->lock_cache) {
+      put_lock_cache(mut->lock_cache);
+      mut->lock_cache = nullptr;
+    }
+  }
+
   for (set<mds_rank_t>::iterator p = slaves.begin(); p != slaves.end(); ++p) {
     if (!mds->is_cluster_degraded() ||
        mds->mdsmap->get_state(*p) >= MDSMap::STATE_REJOIN) {
@@ -742,6 +764,169 @@ void Locker::drop_locks_for_fragment_unfreeze(MutationImpl *mut)
   issue_caps_set(need_issue);
 }
 
+class C_MDL_DropCache : public LockerContext {
+  MDLockCache *lock_cache;
+public:
+  C_MDL_DropCache(Locker *l, MDLockCache *lc) :
+    LockerContext(l), lock_cache(lc) { }
+  void finish(int r) override {
+    locker->drop_locks(lock_cache);
+    lock_cache->cleanup();
+    delete lock_cache;
+  }
+};
+
+void Locker::put_lock_cache(MDLockCache* lock_cache)
+{
+  ceph_assert(lock_cache->ref > 0);
+  if (--lock_cache->ref > 0)
+    return;
+
+  ceph_assert(lock_cache->invalidating);
+  mds->queue_waiter(new C_MDL_DropCache(this, lock_cache));
+}
+
+void Locker::invalidate_lock_cache(MDLockCache *lock_cache)
+{
+  ceph_assert(lock_cache->item_cap_lock_cache.is_on_list());
+  ceph_assert(!lock_cache->invalidating);
+  lock_cache->invalidating = true;
+  // XXX check issued caps
+  lock_cache->item_cap_lock_cache.remove_myself();
+  put_lock_cache(lock_cache);
+}
+
+void Locker::create_lock_cache(MDRequestRef& mdr, CInode *diri)
+{
+  if (mdr->lock_cache)
+    return;
+
+  client_t client = mdr->get_client();
+  int opcode = mdr->client_request->get_op();
+  dout(10) << "create_lock_cache for client." << client << "/" << ceph_mds_op_name(opcode)<< " on " << *diri << dendl;
+
+  if (!diri->is_auth()) {
+    dout(10) << " dir inode is not auth, noop" << dendl;
+    return;
+  }
+
+  if (mdr->has_more() && !mdr->more()->slaves.empty()) {
+    dout(10) << " there are slaves requests for " << *mdr << ", noop" << dendl;
+    return;
+  }
+
+  Capability *cap = diri->get_client_cap(client);
+  if (!cap) {
+    dout(10) << " there is no cap for client." << client << ", noop" << dendl;
+    return;
+  }
+
+  set<MDSCacheObject*> ancestors;
+  for (CInode *in = diri; ; ) {
+    CDentry *pdn = in->get_projected_parent_dn();
+    if (!pdn)
+      break;
+    // ancestors.insert(pdn);
+    in = pdn->get_dir()->get_inode();
+    ancestors.insert(in);
+  }
+
+  for (auto& p : mdr->object_states) {
+    if (p.first != diri && !ancestors.count(p.first))
+      continue;
+    auto& stat = p.second;
+    if (stat.auth_pinned && !p.first->can_auth_pin()) {
+      dout(10) << " can't auth_pin(freezing?) lock parent " << *p.first << ", noop" << dendl;
+      return;
+    }
+  }
+
+  std::vector<CDir*> dfv;
+  dfv.reserve(diri->get_num_dirfrags());
+
+  diri->get_dirfrags(dfv);
+  for (auto dir : dfv) {
+    if (!dir->is_auth() || !dir->can_auth_pin()) {
+      dout(10) << " can't auth_pin(!auth|freezing?) dirfrag " << *dir << ", noop" << dendl;
+      return;
+    }
+  }
+
+  for (auto& p : mdr->locks) {
+    MDSCacheObject *obj = p.lock->get_parent();
+    if (obj != diri && !ancestors.count(obj))
+      continue;
+    if (!p.lock->is_stable()) {
+      dout(10) << " unstable " << *p.lock << " on " << *obj << ", noop" << dendl;
+      return;
+    }
+  }
+
+  auto lock_cache = new MDLockCache(cap, opcode);
+
+  // prevent subtree migration
+  for (auto dir : dfv)
+    lock_cache->auth_pin(dir);
+
+  for (auto& p : mdr->object_states) {
+    if (p.first != diri && !ancestors.count(p.first))
+      continue;
+    auto& stat = p.second;
+    if (stat.auth_pinned)
+      lock_cache->auth_pin(p.first);
+    else
+      lock_cache->pin(p.first);
+  }
+
+  for (auto it = mdr->locks.begin(); it != mdr->locks.end(); ) {
+    MDSCacheObject *obj = it->lock->get_parent();
+    if (obj != diri && !ancestors.count(obj)) {
+      ++it;
+      continue;
+    }
+    unsigned lock_flag = 0;
+    if (it->is_wrlock()) {
+      // skip wrlocks that were added by MDCache::predirty_journal_parent()
+      if (obj == diri)
+       lock_flag = MutationImpl::LockOp::WRLOCK;
+    } else {
+      ceph_assert(it->is_rdlock());
+      lock_flag = MutationImpl::LockOp::RDLOCK;
+    }
+    if (lock_flag) {
+      lock_cache->emplace_lock(it->lock, lock_flag);
+      mdr->locks.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+
+  lock_cache->ref++;
+  mdr->lock_cache = lock_cache;
+}
+
+bool Locker::find_and_attach_lock_cache(MDRequestRef& mdr, CInode *diri)
+{
+  if (mdr->lock_cache)
+    return true;
+
+  Capability *cap = diri->get_client_cap(mdr->get_client());
+  if (!cap)
+    return false;
+
+  int opcode = mdr->client_request->get_op();
+  for (auto p = cap->lock_caches.begin(); !p.end(); ++p) {
+    MDLockCache *lock_cache = *p;
+    if (lock_cache->opcode == opcode) {
+      dout(10) << "found lock cache for " << ceph_mds_op_name(opcode) << " on " << *diri << dendl;
+      mdr->lock_cache = lock_cache;
+      mdr->lock_cache->ref++;
+      return true;
+    }
+  }
+  return false;
+}
+
 // generics
 
 void Locker::eval_gather(SimpleLock *lock, bool first, bool *pneed_issue, MDSContext::vec *pfinishers)
@@ -3599,6 +3784,11 @@ void Locker::remove_client_cap(CInode *in, Capability *cap, bool kill)
   if (!in->client_need_snapflush.empty())
     _do_null_snapflush(in, client);
 
+  while (!cap->lock_caches.empty()) {
+    MDLockCache* lock_cache = cap->lock_caches.front();
+    invalidate_lock_cache(lock_cache);
+  }
+
   bool notable = cap->is_notable();
   in->remove_client_cap(client);
   if (!notable)
index 5fbf124db9b0be4c62d8ee2140e4cd31a01824c1..8735d92cee81df0027bb4655be399a0a7c9d5fd7 100644 (file)
@@ -67,6 +67,11 @@ public:
   void drop_rdlocks_for_early_reply(MutationImpl *mut);
   void drop_locks_for_fragment_unfreeze(MutationImpl *mut);
 
+  void create_lock_cache(MDRequestRef& mdr, CInode *diri);
+  bool find_and_attach_lock_cache(MDRequestRef& mdr, CInode *diri);
+  void invalidate_lock_cache(MDLockCache *lock_cache);
+  void put_lock_cache(MDLockCache* lock_cache);
+
   void eval_gather(SimpleLock *lock, bool first=false, bool *need_issue=0, MDSContext::vec *pfinishers=0);
   void eval(SimpleLock *lock, bool *need_issue);
   void eval_any(SimpleLock *lock, bool *need_issue, MDSContext::vec *pfinishers=0, bool first=false) {
index 65db633c723e83e243ef7dbe92570e1613570ed6..c61690902979d2f643da1d644e04cc988e4b13fb 100644 (file)
@@ -5969,7 +5969,7 @@ void MDCache::opened_undef_inode(CInode *in) {
   if (in->is_dir()) {
     // FIXME: re-hash dentries if necessary
     ceph_assert(in->inode.dir_layout.dl_dir_hash == g_conf()->mds_default_dir_hash);
-    if (in->has_dirfrags() && !in->dirfragtree.is_leaf(frag_t())) {
+    if (in->get_num_dirfrags() && !in->dirfragtree.is_leaf(frag_t())) {
       CDir *dir = in->get_dirfrag(frag_t());
       ceph_assert(dir);
       rejoin_undef_dirfrags.erase(dir);
index b14d6983758240878509b6b03aaf42d95e1b55f8..5d59475b7c0601ea4ec4c4555d9d247fa4650908 100644 (file)
@@ -82,6 +82,24 @@ void MutationImpl::finish_locking(SimpleLock *lock)
   locking_target_mds = -1;
 }
 
+bool MutationImpl::is_rdlocked(SimpleLock *lock) const {
+  auto it = locks.find(lock);
+  if (it != locks.end() && it->is_rdlock())
+    return true;
+  if (lock_cache)
+    return static_cast<const MutationImpl*>(lock_cache)->is_rdlocked(lock);
+  return false;
+}
+
+bool MutationImpl::is_wrlocked(SimpleLock *lock) const {
+  auto it = locks.find(lock);
+  if (it != locks.end() && it->is_wrlock())
+    return true;
+  if (lock_cache)
+    return static_cast<const MutationImpl*>(lock_cache)->is_wrlocked(lock);
+  return false;
+}
+
 void MutationImpl::LockOpVec::erase_rdlock(SimpleLock* lock)
 {
   for (int i = size() - 1; i >= 0; --i) {
index d8227e0e1bb30a5acaef17256c4503813744d147..1deb963000895e2eb01cca66de478922e5104fb9 100644 (file)
 #include "messages/MClientReply.h"
 
 class LogSegment;
-class Capability;
 class CInode;
 class CDir;
 class CDentry;
 class Session;
 class ScatterLock;
 struct sr_t;
+struct MDLockCache;
 
 struct MutationImpl : public TrackedOp {
   metareqid_t reqid;
@@ -137,23 +137,19 @@ public:
   using lock_iterator = lock_set::iterator;
   lock_set locks;  // full ordering
 
+  MDLockCache* lock_cache = nullptr;
+
   lock_iterator emplace_lock(SimpleLock *l, unsigned f=0, mds_rank_t t=MDS_RANK_NONE) {
     last_locked = l;
     return locks.emplace(l, f, t).first;
   }
 
-  bool is_rdlocked(SimpleLock *lock) const {
-    auto it = locks.find(lock);
-    return it != locks.end() && it->is_rdlock();
-  }
+  bool is_rdlocked(SimpleLock *lock) const;
+  bool is_wrlocked(SimpleLock *lock) const;
   bool is_xlocked(SimpleLock *lock) const {
     auto it = locks.find(lock);
     return it != locks.end() && it->is_xlock();
   }
-  bool is_wrlocked(SimpleLock *lock) const {
-    auto it = locks.find(lock);
-    return it != locks.end() && it->is_wrlock();
-  }
   bool is_remote_wrlocked(SimpleLock *lock) const {
     auto it = locks.find(lock);
     return it != locks.end() && it->is_remote_wrlock();
@@ -198,7 +194,8 @@ public:
       reqid(ri), attempt(att),
       slave_to_mds(slave_to) { }
   ~MutationImpl() override {
-    ceph_assert(locking == NULL);
+    ceph_assert(!locking);
+    ceph_assert(!lock_cache);
     ceph_assert(num_pins == 0);
     ceph_assert(num_auth_pins == 0);
   }
@@ -228,8 +225,8 @@ public:
   }
 
   // pin items in cache
-  void pin(MDSCacheObject *o);
-  void unpin(MDSCacheObject *o);
+  void pin(MDSCacheObject *object);
+  void unpin(MDSCacheObject *object);
   void set_stickydirs(CInode *in);
   void put_stickydirs();
   void drop_pins();
@@ -487,5 +484,22 @@ struct MDSlaveUpdate {
   }
 };
 
+struct MDLockCache : public MutationImpl {
+  CInode *diri;
+  Capability *client_cap;
+  int opcode;
+
+  elist<MDLockCache*>::item item_cap_lock_cache;
+
+  int ref = 1;
+  bool invalidating = false;
+
+  MDLockCache(Capability *cap, int op) :
+    MutationImpl(), diri(cap->get_inode()), client_cap(cap), opcode(op) {
+    client_cap->lock_caches.push_back(&item_cap_lock_cache);
+  }
+
+  CInode *get_dir_inode() { return diri; }
+};
 
 #endif
index f60e0e7dffffe6bce0417bb9b30cb2e1f89c37c8..e62c0e0a35397627e0db4dfd8209dd19354d27a9 100644 (file)
@@ -531,7 +531,7 @@ void EMetaBlob::fullbit::update_inode(MDSRank *mds, CInode *in)
               << dirfragtree << " on " << *in << dendl;
       in->dirfragtree = dirfragtree;
       in->force_dirfrags();
-      if (in->has_dirfrags() && in->authority() == CDIR_AUTH_UNDEF) {
+      if (in->get_num_dirfrags() && in->authority() == CDIR_AUTH_UNDEF) {
        auto&& ls = in->get_nested_dirfrags();
        for (const auto& dir : ls) {
          if (dir->get_num_any() == 0 &&