void rpc_task::issue_write_rpc()

in turbonfs/src/rpc_task.cpp [1504:1600]


void rpc_task::issue_write_rpc()
{
    // Must only be called for a flush task.
    assert(get_op_type() == FUSE_WRITE);
    // Must only be called for a BE task.
    assert(rpc_api->write_task.is_be());

    [[maybe_unused]]
    const struct rpc_task *parent_task = rpc_api->parent_task;

    /*
     * If parent_task is set, it must refer to the fuse write task that
     * trigerred the inline sync.
     */
    if (parent_task) {
        // Must be a frontend write task.
        assert(parent_task->magic == RPC_TASK_MAGIC);
        assert(parent_task->get_op_type() == FUSE_WRITE);
        assert(parent_task->rpc_api->write_task.is_fe());
        assert(parent_task->num_ongoing_backend_writes > 0);
    }

    const fuse_ino_t ino = rpc_api->write_task.get_ino();
    struct nfs_inode *inode = get_client()->get_nfs_inode_from_ino(ino);
    struct bc_iovec *bciov = (struct bc_iovec *) rpc_api->pvt;

    // We should come here only for FUSE_WRITE tasks with bciov set.
    assert(bciov != nullptr);
    assert(bciov->magic == BC_IOVEC_MAGIC);

    // issue_write_rpc() can be called for partial writes too.
    assert(bciov->length <= bciov->orig_length);
    assert(bciov->offset >= bciov->orig_offset);
    assert((bciov->orig_offset + bciov->orig_length) ==
           (bciov->offset + bciov->length));

    // FCSM must be marked running, if we are inside issue_write_rpc().
    assert(inode->get_fcsm()->is_running());

    WRITE3args args;
    ::memset(&args, 0, sizeof(args));
    bool rpc_retry = false;
    const uint64_t offset = bciov->offset;
    const uint64_t length = bciov->length;

    assert(bciov->iovcnt > 0 && bciov->iovcnt <= BC_IOVEC_MAX_VECTORS);
    assert(offset < AZNFSC_MAX_FILE_SIZE);
    assert((offset + length) < AZNFSC_MAX_FILE_SIZE);
    assert(length > 0);

    AZLogDebug("issue_write_iovec offset:{}, length:{}", offset, length);
    args.file = inode->get_fh();
    args.offset = offset;
    args.count = length;
    args.stable = inode->is_stable_write() ? FILE_SYNC : UNSTABLE;

    /*
     * Unstable writes want to make use of multiple connections for better perf.
     * Note that they aren't affected by the optimistic concurrency backoff
     * issues as seen by stable writes.
     */
    if (!inode->is_stable_write()) {
        set_csched(CONN_SCHED_RR_W);
    }

    do {
        rpc_retry = false;
        stats.on_rpc_issue();

        if (rpc_nfs3_writev_task(get_rpc_ctx(),
                                 write_iov_callback, &args,
                                 bciov->iov,
                                 bciov->iovcnt,
                                 this) == NULL) {
            stats.on_rpc_cancel();
            /*
             * Most common reason for this is memory allocation failure,
             * hence wait for some time before retrying. Also block the
             * current thread as we really want to slow down things.
             *
             * TODO: For soft mount should we fail this?
             */
            rpc_retry = true;

            AZLogWarn("rpc_nfs3_write_task failed to issue, retrying "
                        "after 5 secs!");
            ::sleep(5);
        }
    } while (rpc_retry);

    /*
     * Write bytes are counted when we send them to libnfs (not when we get
     * response for the WRITE RPC) as that's when they appear on the wire.
     */
    INC_GBL_STATS(server_bytes_written, bciov->length);
    INC_GBL_STATS(server_write_reqs, 1);
}