in turbonfs/src/nfs_client.cpp [1989:2114]
void nfs_client::reply_entry(
struct rpc_task *task,
const nfs_fh3 *fh,
const struct fattr3 *fattr,
const struct fuse_file_info *file)
{
assert(task->magic == RPC_TASK_MAGIC);
enum fuse_opcode optype = task->get_op_type();
struct nfs_inode *inode = nullptr;
fuse_entry_param entry;
/*
* Kernel must cache lookup result.
*/
const bool cache_positive =
(aznfsc_cfg.lookupcache_int == AZNFSCFG_LOOKUPCACHE_ALL ||
aznfsc_cfg.lookupcache_int == AZNFSCFG_LOOKUPCACHE_POS);
memset(&entry, 0, sizeof(entry));
if (fh) {
const fuse_ino_t parent_ino = task->rpc_api->get_parent_ino();
struct nfs_inode *parent_inode = get_nfs_inode_from_ino(parent_ino);
/*
* This will grab a lookupcnt ref on the inode, which will be freed
* from fuse forget callback.
*/
inode = get_nfs_inode(fh, fattr);
entry.ino = inode->get_fuse_ino();
entry.generation = inode->get_generation();
/*
* This takes shared lock on inode->ilock_1.
*/
entry.attr = inode->get_attr();
if (cache_positive) {
entry.attr_timeout = inode->get_actimeo();
entry.entry_timeout = inode->get_actimeo();
} else {
entry.attr_timeout = 0;
entry.entry_timeout = 0;
}
/*
* If it's a proxy task, optype of the original task should be used.
* Currently we use proxy task only for LOOKUP, so assert for that.
*/
if (task->get_proxy_op_type() != (fuse_opcode) 0) {
AZLogDebug("Completing proxy task {} -> {}",
rpc_task::fuse_opcode_to_string(optype),
rpc_task::fuse_opcode_to_string(
task->get_proxy_op_type()));
assert(optype == FUSE_LOOKUP);
optype = task->get_proxy_op_type();
// LOOKUP cannot be proxying a LOOKUP.
assert(optype != FUSE_LOOKUP);
}
/*
* Note: reply_create()/reply_entry() below will increment
* forget_expected just before replying to fuse, so we log the
* updated count here.
*/
AZLogDebug("[{}] <{}> Returning ino {} to fuse (filename: {}, "
"lookupcnt: {}, dircachecnt: {}, forget_expected: {})",
parent_ino,
rpc_task::fuse_opcode_to_string(optype),
inode->get_fuse_ino(),
task->rpc_api->get_file_name(),
inode->lookupcnt.load(),
inode->dircachecnt.load(),
inode->forget_expected.load() + 1);
/*
* This is the common place where we return inode to fuse.
* After this fuse can call any of the functions that might need file
* or dir cache, so allocate them now.
*/
switch (optype) {
case FUSE_CREATE:
assert(file);
inode->on_fuse_open(optype);
break;
case FUSE_LOOKUP:
case FUSE_MKNOD:
case FUSE_MKDIR:
case FUSE_SYMLINK:
assert(!file);
inode->on_fuse_lookup(optype);
break;
default:
AZLogError("[{}] Invalid optype: {}",
inode->get_fuse_ino(), (int) optype);
assert(0);
}
/*
* Add lookup results to DNLC cache.
*/
parent_inode->dnlc_add(task->rpc_api->get_file_name(), inode);
} else {
/*
* The only valid case where reply_entry() is called with null fh
* is the case where lookup yielded "not found". We are using the
* fuse support for negative dentry where we should respond with
* success but ino set to 0 to convey to fuse that it must cache
* the negative dentry for entry_timeout period.
* This caching helps to improve performance by avoiding repeated
* lookup requests for entries that are known not to exist.
*
* TODO: See if negative entries must be cached for lesser time.
*/
assert(aznfsc_cfg.lookupcache_int == AZNFSCFG_LOOKUPCACHE_ALL);
assert(!fattr);
entry.attr_timeout = aznfsc_cfg.actimeo;
entry.entry_timeout = aznfsc_cfg.actimeo;
}
if (file) {
task->reply_create(&entry, file);
} else {
task->reply_entry(&entry);
}
}