void readdirectory_cache::clear()

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();
        }
    }
}