cnxfer.c   [plain text]


/*
 * Copyright (c) 2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Portions of this software have been released under the following terms:
 *
 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
 *
 * To anyone who acknowledges that this file is provided "AS IS"
 * without any express or implied warranty:
 * permission to use, copy, modify, and distribute this file for any
 * purpose is hereby granted without fee, provided that the above
 * copyright notices and this notice appears in all source code copies,
 * and that none of the names of Open Software Foundation, Inc., Hewlett-
 * Packard Company or Digital Equipment Corporation be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Neither Open Software
 * Foundation, Inc., Hewlett-Packard Company nor Digital
 * Equipment Corporation makes any representations about the suitability
 * of this software for any purpose.
 *
 * Copyright (c) 2007, Novell, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Novell Inc. nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

/*
**
**  NAME
**
**      cnxfer.c
**
**  FACILITY:
**
**      Remote Procedure Call (RPC)
**
**  ABSTRACT:
**
**  Entrypoints to support buffered data transfer within the
**  Connection-oriented protocol services component of the RPC
**  runtime.
**
**
*/

#include <commonp.h>    /* Common declarations for all RPC runtime */
#include <com.h>        /* Common communications services */
#include <comprot.h>    /* Common protocol services */
#include <cnp.h>        /* NCA Connection private declarations */
#include <cnfbuf.h>     /* NCA Connection fragment buffer declarations */
#include <cnpkt.h>      /* NCA Connection protocol header */
#include <cncall.h>     /* NCA Connection call service */
#include <cnassoc.h>    /* NCA Connection association service */
#include <cnxfer.h>

/*
 * Prototype for internal entrypoints.
 */

INTERNAL void rpc__cn_prep_next_iovector_elmt (
        rpc_cn_call_rep_p_t /*call_rep*/,
        unsigned32     * /*status*/
    );


/*
**++
**
**  ROUTINE NAME:       rpc__cn_copy_buffer
**
**  SCOPE:              PRIVATE
**
**  DESCRIPTION:
**
**  Copies an iovector element to the iovector array in the
**  call rep.  This routine will buffer data until the total
**  byte count reaches the max segment size for the transport
**  or when we have exhausted the size of the iovector in the
**  call rep.  If either of those conditions hold, the data
**  would be transferred.
**
**  INPUTS:
**
**      call_rep        The call rep.
**
**      iov_elt_p       The iovector element we are copying.
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:
**
**      status          The completion status
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_copy_buffer
(
  rpc_cn_call_rep_p_t     call_rep,
  rpc_iovector_elt_p_t    iov_elt_p,
  unsigned32              *status
)
{
    unsigned32              xfer_size;
    unsigned32              bytes_to_segment_size;
    unsigned32              bytes_left_to_xfer;
    unsigned32              cur_iov_index;
    byte_p_t                src;

    src = iov_elt_p->data_addr;
    bytes_left_to_xfer = iov_elt_p->data_len;
    *status = rpc_s_ok;

    bytes_to_segment_size = call_rep->max_seg_size -
                            RPC_CN_CREP_ACC_BYTCNT (call_rep);

    cur_iov_index = RPC_CN_CREP_CUR_IOV_INDX (call_rep);
    while (bytes_left_to_xfer > 0)
    {
        /*
         * See if we've reached our transmit segment size.
         * If so, send what we have accumulated so far.
         */
        if (bytes_to_segment_size == 0)
        {
            /*
             * Transmit all the data buffered thus far.
             */
            rpc__cn_transmit_buffers (call_rep, status);
            rpc__cn_dealloc_buffered_data (call_rep);

            /*
             * Fix up the iovector in the call rep so
             * that we again have only the cached protocol
             * header (and no stub data).
             */
            RPC_CN_FREE_ALL_EXCEPT_PROT_HDR (call_rep);
            cur_iov_index = RPC_CN_CREP_CUR_IOV_INDX (call_rep);
            if (*status != rpc_s_ok)
            {
                return;
            }
        }
        /*
         * Check to see if the current iovector element is full.
         */
        else if (RPC_CN_CREP_FREE_BYTES (call_rep) == 0)
        {
            /*
             * If the current iovector element is full and we've
             * reached the end of our iovector, send what we
             * have accumulated so far.
             */
            if (RPC_CN_CREP_IOVLEN (call_rep) >= RPC_C_MAX_IOVEC_LEN)
            {
                /*
                 * Transmit all the data buffered thus far.
                 */
                rpc__cn_transmit_buffers (call_rep, status);
                rpc__cn_dealloc_buffered_data (call_rep);

                /*
                 * Fix up the iovector in the call rep so
                 * that we again have only the cached protocol
                 * header (and no stub data).
                 */
                RPC_CN_FREE_ALL_EXCEPT_PROT_HDR (call_rep);
                cur_iov_index = RPC_CN_CREP_CUR_IOV_INDX (call_rep);
                if (*status != rpc_s_ok)
                {
                    return;
                }

            }
            else
            {
                /*
                 * We have not reached the end of our iovector.
                 * In this case, we can use a new iovector element.
                 */
                rpc__cn_prep_next_iovector_elmt (call_rep, status);
                cur_iov_index ++;
            }
        }

        /*
         * Copy the minimum of:
         *   1) what will fit into current fragment,
         *   2) number of bytes left to transfer,
         *   3) remaining bytes left before we reach max_seg_size.
         */
        xfer_size = RPC_CN_CREP_FREE_BYTES (call_rep);
        bytes_to_segment_size = call_rep->max_seg_size -
                                RPC_CN_CREP_ACC_BYTCNT (call_rep);
        if (xfer_size > bytes_to_segment_size)
        {
            xfer_size = bytes_to_segment_size;
        }
        if (xfer_size > bytes_left_to_xfer)
        {
            xfer_size = bytes_left_to_xfer;
        }

        memcpy (RPC_CN_CREP_FREE_BYTE_PTR (call_rep), src, xfer_size);
        bytes_left_to_xfer -= xfer_size;
        RPC_CN_CREP_ACC_BYTCNT (call_rep) += xfer_size;
        src += xfer_size;
        RPC_CN_CREP_FREE_BYTE_PTR (call_rep) += xfer_size;
        RPC_CN_CREP_FREE_BYTES (call_rep) -= xfer_size;
        RPC_CN_CREP_IOV (call_rep) [cur_iov_index].data_len +=
            xfer_size;
    }
}


/*
**++
**
**  ROUTINE NAME:       rpc__cn_add_new_iovector_elmt
**
**  SCOPE:              PRIVATE
**
**  DESCRIPTION:
**
**  Append the specified buffer as a new iovector element to
**  the iovector in the call rep.  This routine will transfer
**  data as necessary over the association until the total
**  accumulated data in the call rep is less than the
**  negotiated segment size.
**
**  INPUTS:
**
**      call_rep        The call rep.
**
**      iovector_elmt   The iovector element describing the
**                      data to add.
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:
**
**      status          The completion status
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_add_new_iovector_elmt
(
  rpc_cn_call_rep_p_t     call_rep,
  rpc_iovector_elt_p_t    iov_elt_p,
  unsigned32              *status
)
{
    unsigned32              bytes_to_segment_size;
    unsigned32              cur_iov_index;
    rpc_iovector_elt_p_t    iov_p;

    *status = rpc_s_ok;

    /*
     * If the current iovector element is full and we've
     * reached the end of our iovector, send what we
     * have accumulated so far.
     */
    if (RPC_CN_CREP_IOVLEN (call_rep) >= RPC_C_MAX_IOVEC_LEN)
    {
        /*
         * Transmit all the data buffered thus far.
         */
        rpc__cn_transmit_buffers (call_rep, status);
        rpc__cn_dealloc_buffered_data (call_rep);

        /*
         * Fix up the iovector in the call rep so
         * that we again have only the cached protocol
         * header (and no stub data).
         */
        RPC_CN_FREE_ALL_EXCEPT_PROT_HDR (call_rep);
        if (*status != rpc_s_ok)
        {
            return;
        }
    }

    /*
     * At this point, we know that there is at least one
     * iovector element available for us to use in the
     * call rep.  There may be other elements between
     * this and the first element (containing the protocol
     * header).
     */

    /*
     * Fill in a new iovector element.
     */
    RPC_CN_CREP_IOVLEN (call_rep)++;
    cur_iov_index = ++RPC_CN_CREP_CUR_IOV_INDX (call_rep);
    RPC_CN_CREP_FREE_BYTES (call_rep) = 0;

    iov_p = &(RPC_CN_CREP_IOV (call_rep)[cur_iov_index]);
    *iov_p = *iov_elt_p;

    /*
     * If the new iovector element causes the total amount
     * of buffered data to exceed our max segment size,
     * transmit chunks of data from the current iovector
     * element until the total remaining size (including
     * protocol header) is less than our segment size.
     */
    bytes_to_segment_size = call_rep->max_seg_size -
                            RPC_CN_CREP_ACC_BYTCNT (call_rep);

    /*
     * Only invoke rpc__cn_transmit_buffers() if iov_elt_p->data_len is
     * greater than bytes_to_segment_size.
     */
    while (iov_elt_p->data_len > bytes_to_segment_size)
    {
        /*
         * Adjust the new iovector element to reflect only
         * enough data that can fit into current segment;
         * send it.
         */
        iov_p->data_len = bytes_to_segment_size;
        RPC_CN_CREP_ACC_BYTCNT (call_rep) += bytes_to_segment_size;
        rpc__cn_transmit_buffers (call_rep, status);
        if (*status != rpc_s_ok)
        {
            /*
             * Fix up the iovector in the call rep so
             * that we again have only the cached protocol
             * header (and no stub data).
             */
            rpc__cn_dealloc_buffered_data (call_rep);
            RPC_CN_FREE_ALL_EXCEPT_PROT_HDR (call_rep);
            return;
        }
        iov_elt_p->data_len -= bytes_to_segment_size;
        iov_elt_p->data_addr += bytes_to_segment_size;

        /*
         * Deallocate all the buffers except the 1st and last.
         * Then adjust iovector so that we have only 2 elements:
         * the header plus the current stub data.
         */
        if (RPC_CN_CREP_IOVLEN (call_rep) > (call_rep->sec != NULL) ? 3 : 2)
        {
            /*
             * rpc__cn_dealloc_buffered_data will always skip the
             * first iovector element (protocol header).  Decrementing
             * iovlen will cause it to skip the last element also.
             */
            RPC_CN_CREP_IOVLEN (call_rep) --;
            rpc__cn_dealloc_buffered_data (call_rep);

            /*
             * Now we rebuild the iovector.  It will have only
             * 2 elements: the header, plus the iovector element
             * which we are processing.
             */
            RPC_CN_CREP_IOVLEN (call_rep) = (call_rep->sec != NULL) ? 3 : 2;
            RPC_CN_CREP_CUR_IOV_INDX (call_rep) = 1;
            iov_p = &(RPC_CN_CREP_IOV (call_rep)[1]);
        }

        /*
         * Now logically, the only data is the header.
         * We are going to chain on the next iovector element
         * during the next iteration of this while loop.
         */
        RPC_CN_CREP_ACC_BYTCNT (call_rep) = RPC_CN_CREP_SIZEOF_HDR (call_rep);
        RPC_CN_CREP_IOV(call_rep)[0].data_len =
                RPC_CN_CREP_SIZEOF_HDR (call_rep);

        *iov_p = *iov_elt_p;

        bytes_to_segment_size = call_rep->max_seg_size -
                            RPC_CN_CREP_ACC_BYTCNT (call_rep);

    }

    /*
     * At this point, the iovector element added (plus the
     * header) cannot exceed the max segment size.
     */

    /* If we started out with an iovector element whose
     * size (combined with the header) is a multiple of
     * our segment size, then the newly added iovector
     * element would have length = 0 (since all the data
     * would have been transmitted in the while loop).
     * Free the element in this case.
     *
     */
    if (iov_p->data_len == 0)
    {
        if (iov_p->buff_dealloc != (rpc_buff_dealloc_fn_t) NULL)
        {
            (iov_p->buff_dealloc) (iov_p->buff_addr);
        }
        RPC_CN_CREP_IOVLEN (call_rep) --;
        RPC_CN_CREP_CUR_IOV_INDX (call_rep) --;
    }
    else
    {
        /*
         * Update the total bytecount to account for the new
         * iovector element.
         */
        RPC_CN_CREP_ACC_BYTCNT (call_rep) += iov_elt_p->data_len;

        /*
         * Set free bytes to 0 so that we would allocate
         * a new iovector element next time instead of
         * copying data past the end of the current iovector
         * element.
         */
        RPC_CN_CREP_FREE_BYTES (call_rep) = 0;
    }
}

#if 0

/*
**++
**
**  ROUTINE NAME:       rpc__cn_flush_buffers
**
**  SCOPE:              PRIVATE - declared in cnxfer.h
**
**  DESCRIPTION:
**
**  Transmit a final fragment if any or all of the iovector element
**  buffers would have to be copied as indicated by the make "reusable"
**  bit. A final fragment can only be sent if the total number of bytes
**  is greater than the RT->stub guaranteed minumum. The data sent must
**  be a mutiple of 8 bytes.
**
**  INPUTS:
**
**      call_rep        The call rep.
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:
**
**      status          The completion status
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_flush_buffers
(
  rpc_cn_call_rep_p_t     call_rep,
  unsigned32              *status
)
{
    unsigned32          i;
    rpc_iovector_elt_t  *iov_p;

    *status = rpc_s_ok;

    if (RPC_CN_CREP_ACC_BYTCNT (call_rep) >= rpc_c_assoc_must_recv_frag_size)
    {
        /*
         * There's enough data to do another send.
         */
        rpc__cn_transmit_buffers (call_rep, status);
        rpc__cn_dealloc_buffered_data (call_rep);
        RPC_CN_FREE_ALL_EXCEPT_PROT_HDR (call_rep);
    }
    else
    {
        /*
         * There's not enough data to send. Copy all that's
         * buffered.
         */
        for (i = 1;
             i < RPC_CN_CREP_IOVLEN (call_rep);
             i++)
        {
            rpc__cn_copy_buffer (call_rep,
                                 &RPC_CN_CREP_IOV (call_rep)[i],
                                 status);
        }
    }
}
#endif /* 0 */


/*
**++
**
**  ROUTINE NAME:       rpc__cn_transmit_buffers
**
**  SCOPE:              PRIVATE
**
**  DESCRIPTION:
**
**  Transmits the data buffered in the call rep's iovector
**  over the association.
**
**  INPUTS:
**
**      call_rep        The call rep.
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:
**
**      status          The completion status
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_transmit_buffers
(
 rpc_cn_call_rep_p_t     call_rep,
 unsigned32              *status
)
{
    rpc_cn_packet_p_t   header_p;

    /*
     * Write the bytecount accumulated thus far into the fragment
     * length field of the cached protocol header.
     */
    *status = rpc_s_ok;
    header_p = (rpc_cn_packet_p_t) RPC_CN_CREP_SEND_HDR (call_rep);
    RPC_CN_PKT_FRAG_LEN (header_p) = RPC_CN_CREP_ACC_BYTCNT (call_rep);

    /*
     * Set the alloc hint; appears that NetApp's RPC implementation
     * depends on this.
     * Three possible cases to deal with
     * 1) call_rep->alloc_hint is not 0 - just use that value
     * 2) call_rep->alloc_hint is 0 AND both first/last frag flags set -
            calculate alloc_hint
     * 3) call_rep->alloc_hint is 0 AND both first/last frag flags not set -
            leave alloc_hint at 0 so its ignored and print warning
            since this should not happen.
     */
    if (call_rep->alloc_hint != 0)
    {
        RPC_CN_PKT_ALLOC_HINT (header_p) = call_rep->alloc_hint;
        call_rep->alloc_hint -= (RPC_CN_CREP_ACC_BYTCNT (call_rep) - RPC_CN_CREP_SIZEOF_HDR (call_rep));
    }
    else
    {
        if ( (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_FIRST_FRAG) &&
            (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_LAST_FRAG) )
        {
            /* single fragment being set, so we can calculate alloc_hint */
            RPC_CN_PKT_ALLOC_HINT (header_p) = RPC_CN_CREP_ACC_BYTCNT (call_rep) -
                RPC_CN_CREP_SIZEOF_HDR (call_rep);
        }
        else
        {
            /* not a single fragment, yet call_rep->alloc_hint is 0
             print out a warning as this should be fixed */
            RPC_DBG_PRINTF (rpc_es_dbg_general, RPC_C_CN_DBG_GENERAL,
                            ("(rpc__cn_transmit_buffers) setting alloc_hint is 0\n"));
        }
    }

    if (RPC_CALL_IS_CLIENT (((rpc_call_rep_t *) call_rep)))
    {
        /*
         * Check for pending cancels if sending a request. Set the flag
         * in the request header to forward the cancel if there is one
         * pending and this is the first fragment of the request.
         */
        if (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_FIRST_FRAG)
        {
            if (call_rep->u.client.cancel.local_count)
            {
                RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                               ("(rpc__cn_transmit_buffers) setting alert pending bit in request header for queued cancel\n"));
                RPC_CN_PKT_FLAGS (header_p) |= RPC_C_CN_FLAGS_ALERT_PENDING;
                call_rep->u.client.cancel.local_count--;
            }
            else
            {
                DCETHREAD_TRY
                {
                    dcethread_checkinterrupt ();
                }
                DCETHREAD_CATCH (dcethread_interrupt_e)
                {
                    RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                                   ("(rpc__cn_transmit_buffers) setting alert pending bit in request header for cancel just detected\n"));
                    RPC_CN_PKT_FLAGS (header_p) |= RPC_C_CN_FLAGS_ALERT_PENDING;
                    rpc__cn_call_start_cancel_timer (call_rep, status);
                }
                DCETHREAD_ENDTRY
            }
            if (*status != rpc_s_ok)
            {
                return;
            }
        }
        RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                       ("(rpc__cn_transmit_buffers) setting flag indicating first frag has been sent\n"));
        call_rep->u.client.cancel.server_is_accepting = true;
        call_rep->num_pkts = 0;
    }

    /*
     * If security was requested attach the authentication trailer
     * to the last iovector element. Make sure to add padding, if
     * required to the stub data to ensure the trailer starts on a
     * 4-byte boundary.
     */
    if (call_rep->sec != NULL)
    {
        rpc_iovector_elt_p_t    iov_p;
        rpc_cn_auth_tlr_t       *auth_tlr;

        /*
         * Remove the authentication trailer size from the header
         * iovector element. This was added by
         * RPC_CN_CREP_ADJ_IOV_FOR_TLR.
         */
        (RPC_CN_CREP_IOV(call_rep)[0]).data_len -= call_rep->prot_tlr->data_size;

        /*
         * Now adjust some fields in the auth trailer. The auth
         * trailer must start on a 4-byte boundary. Pad the user, or
         * stub, data to make it so. The amount of padding is
         * contained in the auth trailer so that the receiver can
         * determine the real user data size.
         */
        auth_tlr = (rpc_cn_auth_tlr_t *)call_rep->prot_tlr->data_p;
        auth_tlr->stub_pad_length =
            (4 - ((RPC_CN_CREP_ACC_BYTCNT (call_rep) -
                   call_rep->prot_tlr->data_size) & 0x03)) & 0x03;
        (RPC_CN_CREP_IOV(call_rep)[RPC_CN_CREP_IOVLEN(call_rep) - 2]).data_len +=
                                   auth_tlr->stub_pad_length;
        RPC_CN_PKT_FRAG_LEN (header_p) +=
            auth_tlr->stub_pad_length -
            RPC_CN_CREP_SIZEOF_TLR_PAD (call_rep);

        /*
         * Hook the auth trailer iovector element after the last
         * iovector element.
         */
        iov_p = &(RPC_CN_CREP_IOV(call_rep)[RPC_CN_CREP_IOVLEN(call_rep) - 1]);
        iov_p->buff_dealloc = NULL;
        iov_p->data_len =
            call_rep->prot_tlr->data_size -
            RPC_CN_CREP_SIZEOF_TLR_PAD (call_rep) ;
        iov_p->data_addr = (byte_p_t) call_rep->prot_tlr->data_p;
    }

    /*
     * Send the buffers in the iovector out over the association.
     */
    rpc__cn_assoc_send_frag (call_rep->assoc,
                             &(call_rep->buffered_output.iov),
                             call_rep->sec,
                             status);

    /*
     * Clear the first frag flag bit in the cached protocol header
     * so that subsequent packets will not have the bit set.
     */
    RPC_CN_PKT_FLAGS (header_p) &= ~RPC_C_CN_FLAGS_FIRST_FRAG;

    /*
     * Update the count of packets sent and received for this call.
     */
    call_rep->num_pkts++;
}


/*
**++
**
**  ROUTINE NAME:       rpc__cn_prep_iovector_elmt
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**
**  Prepare a new iovector element from the call rep for use
**  in buffering data.
**
**  INPUTS:
**
**      call_rep        The call rep.
**
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:
**
**      status          The completion status
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

INTERNAL void rpc__cn_prep_next_iovector_elmt
(
  rpc_cn_call_rep_p_t     call_rep,
  unsigned32              *status
)
{
    unsigned32              cur_iov_index;
    rpc_iovector_elt_p_t    iov_p;
    rpc_cn_fragbuf_p_t      buf_p;

    /*
     * Allocate a new [large] fragment buffer.
     */
    buf_p = rpc__cn_fragbuf_alloc (true);
    if (buf_p == NULL)
    {
        *status = rpc_s_no_memory;
        return;
    }

    /*
     * Make the next iovector element point to it.
     * Initialize pointers.
     */
    RPC_CN_CREP_IOVLEN (call_rep) ++;
    cur_iov_index = ++ (RPC_CN_CREP_CUR_IOV_INDX (call_rep));
    iov_p = &(RPC_CN_CREP_IOV (call_rep)[cur_iov_index]);
    iov_p->buff_dealloc = (rpc_buff_dealloc_fn_t)buf_p->fragbuf_dealloc;
    iov_p->buff_addr = (byte_p_t) buf_p;
    iov_p->buff_len = buf_p->max_data_size;
    iov_p->data_addr = (byte_p_t) buf_p->data_p;
    iov_p->data_len = 0;

    RPC_CN_CREP_FREE_BYTES (call_rep) = rpc_g_cn_large_frag_size;
    RPC_CN_CREP_FREE_BYTE_PTR (call_rep) = (byte_p_t) buf_p->data_p;
    *status = rpc_s_ok;

}

/*
**++
**
**  ROUTINE NAME:       rpc__cn_dealloc_buffered_data
**
**  SCOPE:              PRIVATE
**
**  DESCRIPTION:
**
**  Deallocates all the elements of an iovector (except for the
**  first element).  The first element is assumed to contain the
**  protocol header which will be reused on subsequent transfers.
**
**  NOTE that this routine does not adjust any of the data
**  pointers (cur_iov_indx, iovlen, etc.) in the call rep.
**  This is done so that the caller can have better control.
**
**  INPUTS:
**
**      call_rep        The call rep.
**
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     none
**
**  SIDE EFFECTS:       none
**
**--
**/

PRIVATE void rpc__cn_dealloc_buffered_data
(
  rpc_cn_call_rep_p_t     call_rep
)
{
    unsigned32      cur_iov_index;
    unsigned32      iov_elmnts;

    iov_elmnts = RPC_CN_CREP_IOVLEN (call_rep);
    /*
     * If authenticated RPC is used, the last iovector
     * element is the auth trailer.
     * Don't free it; it is freed explicitly in call_end.
     */
    if (call_rep->sec != NULL)
    {
	iov_elmnts--;
    }

    for (cur_iov_index = 1;
         cur_iov_index < iov_elmnts;
         cur_iov_index++)
    {
        if (RPC_CN_CREP_IOV (call_rep) [cur_iov_index].buff_dealloc != NULL)
        {
            (RPC_CN_CREP_IOV (call_rep) [cur_iov_index].buff_dealloc)
                (RPC_CN_CREP_IOV (call_rep) [cur_iov_index].buff_addr);
        }
        RPC_CN_CREP_IOV (call_rep) [cur_iov_index].buff_addr = NULL;
    }
}

/*
 **++
 **
 **  ROUTINE NAME:       rpc__cn_get_alloc_hint
 **
 **  SCOPE:              PRIVATE
 **
 **  DESCRIPTION:
 **
 **  Walks through all the iov elements and adds up their data lengths.
 **  The total amount is the alloc_hint.
 **
 **  INPUTS:
 **
 **      stub_data_p     iovector containing data to be added up
 **
 **  INPUTS/OUTPUTS:     none
 **
 **  OUTPUTS:            none
 **
 **  IMPLICIT INPUTS:    none
 **
 **  IMPLICIT OUTPUTS:   none
 **
 **  FUNCTION VALUE:     sum of the data lengths which is alloc_hint
 **
 **  SIDE EFFECTS:       none
 **
 **--
 **/

PRIVATE unsigned32 rpc__cn_get_alloc_hint
(
 rpc_iovector_p_t stub_data_p
)
{
    rpc_iovector_elt_p_t iov_elt_p;
    unsigned32 i;
    unsigned32 alloc_hint = 0;

    for (i = 0, iov_elt_p = stub_data_p->elt;
         i < stub_data_p->num_elt;
         i++, iov_elt_p++)
    {
        alloc_hint += iov_elt_p->data_len;
    }

    return (alloc_hint);
}