]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
client: drop null child dentries before try pruning inode's alias
authorYan, Zheng <zyan@redhat.com>
Fri, 1 Dec 2017 02:17:18 +0000 (10:17 +0800)
committerYan, Zheng <zyan@redhat.com>
Thu, 7 Dec 2017 05:26:12 +0000 (13:26 +0800)
Null child dentries holds reference on inode's alias, they prevents
Client::trim_caps() from trimming the inode. Null dentries are trimmed
by Client::trim_cache() according to 'client_cache_size' config option.
So Client::trim_caps() may fail to trim as many caps as MDS asked when
client cache size is smaller than the config limit.

Fixes: http://tracker.ceph.com/issues/22293
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
src/client/Client.cc
src/client/Client.h
src/client/Dir.h

index ec81f3ed236ef9592dc8f92f5f5748f728a074d5..0e7c45a5783765b9fc0e645f159ff60c0acb1f65 100644 (file)
@@ -2964,10 +2964,15 @@ Dentry* Client::link(Dir *dir, const string& name, Inode *in, Dentry *dn)
     dn->dir = dir;
     dir->dentries[dn->name] = dn;
     lru.lru_insert_mid(dn);    // mid or top?
+    if (!in)
+      dir->num_null_dentries++;
 
     ldout(cct, 15) << "link dir " << dir->parent_inode << " '" << name << "' to inode " << in
                   << " dn " << dn << " (new dn)" << dendl;
   } else {
+    assert(!dn->inode);
+    if (in)
+      dir->num_null_dentries--;
     ldout(cct, 15) << "link dir " << dir->parent_inode << " '" << name << "' to inode " << in
                   << " dn " << dn << " (old dn)" << dendl;
   }
@@ -3004,11 +3009,15 @@ void Client::unlink(Dentry *dn, bool keepdir, bool keepdentry)
 
   if (keepdentry) {
     dn->lease_mds = -1;
+    if (in)
+      dn->dir->num_null_dentries++;
   } else {
     ldout(cct, 15) << "unlink  removing '" << dn->name << "' dn " << dn << dendl;
 
     // unlink from dir
     dn->dir->dentries.erase(dn->name);
+    if (!in)
+      dn->dir->num_null_dentries--;
     if (dn->dir->is_empty() && !keepdir)
       close_dir(dn->dir);
     dn->dir = 0;
@@ -4042,6 +4051,31 @@ void Client::_invalidate_kernel_dcache()
   }
 }
 
+void Client::_trim_negative_child_dentries(InodeRef& in)
+{
+  if (!in->is_dir())
+    return;
+
+  Dir* dir = in->dir;
+  if (dir && dir->dentries.size() == dir->num_null_dentries) {
+    for (auto p = dir->dentries.begin(); p != dir->dentries.end(); ) {
+      Dentry *dn = p->second;
+      ++p;
+      assert(!dn->inode);
+      if (dn->lru_is_expireable())
+       unlink(dn, true, false);  // keep dir, drop dentry
+    }
+    if (dir->dentries.empty()) {
+      close_dir(dir);
+    }
+  }
+
+  if (in->flags & I_SNAPDIR_OPEN) {
+    InodeRef snapdir = open_snapdir(in.get());
+    _trim_negative_child_dentries(snapdir);
+  }
+}
+
 void Client::trim_caps(MetaSession *s, uint64_t max)
 {
   mds_rank_t mds = s->mds_num;
@@ -4073,6 +4107,7 @@ void Client::trim_caps(MetaSession *s, uint64_t max)
       }
     } else {
       ldout(cct, 20) << " trying to trim dentries for " << *in << dendl;
+      _trim_negative_child_dentries(in);
       bool all = true;
       auto q = in->dentries.begin();
       while (q != in->dentries.end()) {
index 0c1669c10da2716e44dc85cff0e7174ad1844c8c..d1f9b3f4d20e4c09200641a0a80f9f4c64a71183 100644 (file)
@@ -537,6 +537,7 @@ protected:
   void trim_dentry(Dentry *dn);
   void trim_caps(MetaSession *s, uint64_t max);
   void _invalidate_kernel_dcache();
+  void _trim_negative_child_dentries(InodeRef& in);
   
   void dump_inode(Formatter *f, Inode *in, set<Inode*>& did, bool disconnected);
   void dump_cache(Formatter *f);  // debug
index e6d7f99c903f865bbe95a6f14e542376255c8c77..731a2038ecb292ace8d04ef9d2409b5e10946ca9 100644 (file)
@@ -7,6 +7,8 @@ class Dir {
  public:
   Inode    *parent_inode;  // my inode
   ceph::unordered_map<string, Dentry*> dentries;
+  unsigned num_null_dentries = 0;
+
   vector<Dentry*> readdir_cache;
 
   explicit Dir(Inode* in) { parent_inode = in; }