int nfs_client::lookup_sync()

in turbonfs/src/nfs_client.cpp [1231:1345]


int nfs_client::lookup_sync(fuse_ino_t parent_ino,
                            const char* name,
                            fuse_ino_t& child_ino)
{
    assert(name != nullptr);

    struct nfs_inode *parent_inode = get_nfs_inode_from_ino(parent_ino);
    const uint32_t fh_hash = parent_inode->get_crc();
    struct nfs_context *nfs_context =
        get_nfs_context(CONN_SCHED_FH_HASH, fh_hash);
    struct rpc_task *task = nullptr;
    struct sync_rpc_context *ctx = nullptr;
    struct rpc_pdu *pdu = nullptr;
    struct rpc_context *rpc = nullptr;
    bool rpc_retry = false;
    int status = -1;

    child_ino = 0;
    AZLogDebug("lookup_sync({}/{})", parent_ino, name);

try_again:
    do {
        LOOKUP3args args;
        args.what.dir = parent_inode->get_fh();
        args.what.name = (char *) name;

        if (task) {
            task->free_rpc_task();
        }

        task = get_rpc_task_helper()->alloc_rpc_task(FUSE_LOOKUP);
        task->init_lookup(nullptr /* fuse_req */, name, parent_ino);
        task->rpc_api->pvt = &child_ino;

        if (ctx) {
            delete ctx;
        }

        ctx = new sync_rpc_context(task, nullptr);
        rpc = nfs_get_rpc_context(nfs_context);
        assert(!ctx->callback_called);

        rpc_retry = false;
        task->get_stats().on_rpc_issue();
        if ((pdu = rpc_nfs3_lookup_task(rpc, lookup_sync_callback,
                                        &args, ctx)) == NULL) {
            task->get_stats().on_rpc_cancel();
            /*
             * This call fails due to internal issues like OOM etc
             * and not due to an actual error, hence retry.
             */
            rpc_retry = true;
        }
    } while (rpc_retry);

    /*
     * If the LOOKUP response doesn't come for 60 secs we give up and send
     * a new one. We must cancel the old one.
     */
    {
        std::unique_lock<std::mutex> lock(ctx->mutex);
wait_more:
        if (!ctx->cv.wait_for(lock, std::chrono::seconds(60),
                              [&ctx] { return (ctx->callback_called == true); })) {
            if (rpc_cancel_pdu(rpc, pdu) == 0) {
                task->get_stats().on_rpc_cancel();
                AZLogWarn("Timed out waiting for lookup response, re-issuing "
                          "lookup!");
                // This goto will cause the above lock to unlock.
                goto try_again;
            } else {
                /*
                 * If rpc_cancel_pdu() fails it most likely means we got the RPC
                 * response right after we timed out waiting. It's best to wait
                 * for the callback to be called.
                 */
                AZLogWarn("Timed out waiting for lookup response, couldn't "
                          "cancel existing pdu, waiting some more!");
                // This goto will *not* cause the above lock to unlock.
                goto wait_more;
            }
        } else {
            assert(ctx->callback_called);
            assert(ctx->rpc_status != -1);
            assert(ctx->nfs_status != -1);

            status = task->status(ctx->rpc_status, ctx->nfs_status);
            if (status == 0) {
                assert(child_ino != 0);
            } else if (ctx->rpc_status == RPC_STATUS_SUCCESS &&
                       ctx->nfs_status == NFS3ERR_JUKEBOX) {
                AZLogInfo("Got NFS3ERR_JUKEBOX for LOOKUP, re-issuing "
                          "after 1 sec!");
                ::usleep(1000 * 1000);
                // This goto will cause the above lock to unlock.
                goto try_again;
            } else {
                AZLogDebug("lookup_sync({}/{}) failed, status={}, "
                           "rpc_status={}, nfs_status={}",
                           parent_ino, name, status, ctx->rpc_status,
                           ctx->nfs_status);
                assert(child_ino == 0);
            }
        }
    }

    if (task) {
        task->free_rpc_task();
    }

    delete ctx;

    assert(status >= 0);
    return status;
}