mirror of
https://github.com/ceph/ceph
synced 2024-12-30 07:23:11 +00:00
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" <zyan@redhat.com>
This commit is contained in:
parent
f3b2eb9d2d
commit
99b1b959e1
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
Loading…
Reference in New Issue
Block a user