in turbonfs/src/rpc_readdir.cpp [765:877]
void readdirectory_cache::clear(bool acquire_lock)
{
/*
* TODO: Later when we implement readdirectory_cache purging due to
* memory pressure, we need to ensure that any directory which
* is currently being enumerated by nfs_inode::lookup_dircache(),
* should not be purged, as that may cause those inodes to be
* orphanned (they will have lookupcnt and dircachecnt of 0 and
* still lying aroung in the inode_map.
*/
std::vector<struct nfs_inode*> tofree_vec;
{
std::shared_mutex dummy_lock;
std::unique_lock<std::shared_mutex> lock(
acquire_lock ? readdircache_lock_2 : dummy_lock);
/*
* If dir_entries has one or more entries those must have been returned
* by the server along with the cookieverifier, hence it must be set.
* lookuponly caches may not have cookie_verifier as they may be
* populated from LOOKUP response, so exempt them.
*/
assert(dir_entries.empty() ||
(*(uint64_t *)&cookie_verifier != 0) || is_lookuponly());
for (auto it = dir_entries.begin(); it != dir_entries.end(); ++it) {
struct nfs_inode *inode = it->second->nfs_inode;
if (inode) {
assert(inode->magic == NFS_INODE_MAGIC);
/*
* Any inode referenced by a directory_entry added to
* a readdirectory_cache must have one reference held,
* by readdirectory_cache::add().
*/
assert(inode->dircachecnt > 0);
AZLogDebug("[{}] Removing {} \"{}\" fuse ino {}, cookie {}, from "
"readdir cache (dircachecnt {}, lookupcnt {}, "
"forget_expected {})",
dir_inode->get_fuse_ino(),
inode->is_dir() ? "directory" : "file",
it->second->name,
inode->get_fuse_ino(),
it->second->cookie,
inode->dircachecnt.load(),
inode->lookupcnt.load(),
inode->forget_expected.load());
} else {
AZLogDebug("[{}] Removing \"{}\", cookie {}, from readdir cache",
dir_inode->get_fuse_ino(),
it->second->name,
it->second->cookie);
}
/*
* If this is the last dircachecnt on this inode, it means
* there are no more readdirectory_cache,s referencing this
* inode. If there are no lookupcnt refs then we can free it.
* For safely freeing the inode against any races, we need to call
* decref() and for that we need to make sure we have at least one
* ref on the inode, so we call incref() before deleting the
* directory_entry, and add the inode to a vector which we later
* iterate over and call decref() for all the inodes.
*/
if (inode && (inode->dircachecnt == 1)) {
tofree_vec.emplace_back(inode);
inode->incref();
}
/*
* This will call ~directory_entry(), which will drop the
* dircachecnt. Note that we grabbed a lookupcnt ref on the
* inode so the following decref() will free the inode if that
* was the only ref.
*/
it->second.reset();
}
// For every entry added to dir_entries we add one to dnlc_map.
assert(dir_entries.size() == dnlc_map.size());
dir_entries.clear();
dnlc_map.clear();
/*
* No cookies in the cache, hence no sequence.
* Also clear eof, eof_cookie, cache_size as cache is purged mostly
* because directory changed on the server and thus we don't know any
* better about the directory than when we started.
*/
seq_last_cookie = 0;
eof = false;
eof_cookie = -1;
cache_size = 0;
clear_confirmed();
clear_lookuponly();
}
if (!tofree_vec.empty()) {
AZLogDebug("[{}] {} inodes to be freed, after readdir cache purge",
dir_inode->get_fuse_ino(),
tofree_vec.size());
/*
* Drop the extra ref we held above, for all inodes in tofree_vec.
*/
for (struct nfs_inode *inode : tofree_vec) {
assert(inode->magic == NFS_INODE_MAGIC);
assert(inode->lookupcnt > 0);
inode->decref();
}
}
}