From: Yan, Zheng Date: Fri, 1 Dec 2017 02:17:18 +0000 (+0800) Subject: client: drop null child dentries before try pruning inode's alias X-Git-Tag: v13.0.2~767^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=99b1b959e13219dee10f5649be1c1c15e25f9ccd;p=ceph.git client: drop null child dentries before try pruning inode's alias 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" --- diff --git a/src/client/Client.cc b/src/client/Client.cc index ec81f3ed236..0e7c45a5783 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -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()) { diff --git a/src/client/Client.h b/src/client/Client.h index 0c1669c10da..d1f9b3f4d20 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -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& did, bool disconnected); void dump_cache(Formatter *f); // debug diff --git a/src/client/Dir.h b/src/client/Dir.h index e6d7f99c903..731a2038ecb 100644 --- a/src/client/Dir.h +++ b/src/client/Dir.h @@ -7,6 +7,8 @@ class Dir { public: Inode *parent_inode; // my inode ceph::unordered_map dentries; + unsigned num_null_dentries = 0; + vector readdir_cache; explicit Dir(Inode* in) { parent_inode = in; }