bool nfs_client::getattr_sync()

in turbonfs/src/nfs_client.cpp [2309:2421]


bool nfs_client::getattr_sync(const struct nfs_fh3& fh,
                              fuse_ino_t ino,
                              struct fattr3& fattr)
{
    const uint32_t fh_hash = calculate_crc32(
            (const unsigned char *) fh.data.data_val, fh.data.data_len);
    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;
    bool rpc_retry = false;
    bool success = false;

try_again:
    do {
        struct GETATTR3args args;
        args.object = fh;

        /*
         * Very first call to getattr_sync(), called from nfs_client::init(), for
         * getting the root filehandle attributes won't have the rpc_task_helper
         * set, so that single GETATTR RPC won't be accounted in rpc stats.
         */
        if (get_rpc_task_helper() != nullptr) {
            if (task) {
                task->free_rpc_task();
            }
            task = get_rpc_task_helper()->alloc_rpc_task(FUSE_GETATTR);
            task->init_getattr(nullptr /* fuse_req */, ino);
        } else {
            assert(ino == FUSE_ROOT_ID);
        }

        if (ctx) {
            delete ctx;
        }

        ctx = new sync_rpc_context(task, &fattr);
        rpc = nfs_get_rpc_context(nfs_context);

        rpc_retry = false;
        if (task) {
            task->get_stats().on_rpc_issue();
        }
        if ((pdu = rpc_nfs3_getattr_task(rpc, getattr_sync_callback,
                                         &args, ctx)) == NULL) {
            if (task) {
                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 GETATTR 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) {
                if (task) {
                    task->get_stats().on_rpc_cancel();
                }
                AZLogWarn("Timed out waiting for getattr response, re-issuing "
                          "getattr!");
                // 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 getattr 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);

            if ((ctx->rpc_status == RPC_STATUS_SUCCESS) &&
                    (ctx->nfs_status == NFS3_OK)) {
                success = true;
            } else if (ctx->rpc_status == RPC_STATUS_SUCCESS &&
                       ctx->nfs_status == NFS3ERR_JUKEBOX) {
                AZLogInfo("Got NFS3ERR_JUKEBOX for GETATTR, re-issuing "
                          "after 1 sec!");
                ::usleep(1000 * 1000);
                // This goto will cause the above lock to unlock.
                goto try_again;
            }
        }
    }

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

    delete ctx;

    return success;
}