]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
client: drop null child dentries before try pruning inode's alias 24119/head
authorYan, Zheng <zyan@redhat.com>
Fri, 1 Dec 2017 02:17:18 +0000 (10:17 +0800)
committerYan, Zheng <zyan@redhat.com>
Mon, 17 Sep 2018 01:28:43 +0000 (09:28 +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/22504
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
(cherry picked from commit 99b1b959e13219dee10f5649be1c1c15e25f9ccd)

src/client/Client.cc
src/client/Client.h
src/client/Dir.h

index 7043f9058a405acc430f77774054f75da93cafb9..1b24d0f7e9dee4a8410aa56600fb5cdf90eaf719 100644 (file)
@@ -2989,10 +2989,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;
   }
@@ -3049,11 +3054,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;
@@ -4102,6 +4111,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;
@@ -4132,6 +4166,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;
       set<Dentry*>::iterator q = in->dn_set.begin();
       while (q != in->dn_set.end()) {
index 2616f6d7169194eccaec4709c58b04bb348f9b8f..72f8c32ce280411afb70f3c21b9996de70095d39 100644 (file)
@@ -539,6 +539,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; }