root/usr/src/lib/udapl/udapl_tavor/common/dapl_cookie.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2002-2003, Network Appliance, Inc. All rights reserved.
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 *
 * MODULE: dapl_cookie.c
 *
 * PURPOSE: Manage CQE cookie structures
 *
 * The DAPL spec requires that all a cookies passed to a posting operation
 * be returned in the operation's corresponding completion.
 *
 * Implementing this feature is complicated by the user's ability to
 * suppress event generation for specific operations. When these operations
 * complete successfully, the provider does not have an easy way to
 * deallocate resources devoted to storing context data for these operations.
 *
 * To support this feature, a pool of memory is allocated up front large
 * enough to hold cookie data for the maximum number of operations possible
 * on an endpoint.
 *
 * Two pieces of information are maintained to manage cookie allocation:
 *
 * head index : index of next unallocated cookie
 * tail index : index of last unallocated cookie
 *
 * Each cookie store its index in this memory pool.
 *
 * When an event is received, the index stored in the event's cookie will be
 * used to update the tail. This will implicitly deallocate all of the cookies
 * "between" the old tail and the new tail.
 *
 * The implementation relies on the following assumptions:
 *
 * - there can be only 1 thread in dat_ep_post_send(), dat_ep_post_rdma_write(),
 *   dat_ep_post_rdma_read(), or dat_rmr_bind() at a time, therefore
 *   dapls_cb_get() does not need to be thread safe when manipulating
 *   request data structures.
 *
 * - there can be only 1 thread in dat_ep_post_recv(), therefore
 *   dapls_cb_get() does not need to be thread safe when manipulating
 *   receive data structures.
 *
 * - there can be only 1 thread generating completions for a given EP's request
 *   opeartions, therefore dapls_cb_put() does not need to be thread safe when
 *   manipulating request data structures.
 *
 * - there can be only 1 thread generating completions for a given EP's receive
 *   opeartions therefore dapls_cb_put() does not need to be thread safe when
 *   manipulating receive data structures.
 *
 * - completions are delivered in order
 *
 * $Id: dapl_cookie.c,v 1.13 2003/06/16 17:53:32 sjs2 Exp $
 */

#include "dapl_cookie.h"
#include "dapl_ring_buffer_util.h"

/*
 *
 * Function Prototypes
 *
 */

DAT_RETURN
dapls_cb_get(
        DAPL_COOKIE_BUFFER              *buffer,
        DAPL_COOKIE             **cookie_ptr);

DAT_RETURN
dapls_cb_put(
        DAPL_COOKIE_BUFFER              *buffer,
        DAPL_COOKIE             *cookie);


/*
 *
 * Function Definitions
 *
 */

/*
 * dapls_cb_create
 *
 * Given a DAPL_COOKIE_BUFFER, allocate and initialize memory for
 * the data structure.
 *
 * Input:
 *      buffer          pointer to DAPL_COOKIE_BUFFER
 *      ep              endpoint to associate with cookies
 *      size            number of elements to allocate & manage
 *
 * Output:
 *      none
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INSUFFICIENT_RESOURCES
 *
 */
DAT_RETURN
dapls_cb_create(
        IN      DAPL_COOKIE_BUFFER      *buffer,
        IN      void                    *queue,
        IN      DAPL_COOKIE_QUEUE_TYPE  type,
        IN      DAT_COUNT               size)

{
        DAT_COUNT                       i;

        /*
         * allocate one additional entry so that the tail
         * can always point at an empty location
         */
        size++;
        /* round up to multiple of 2 */
        i = 2;
        while (size > i) {
            i <<= 1;
        }
        size = i;

        buffer->pool = dapl_os_alloc(size * sizeof (DAPL_COOKIE));
        if (NULL != buffer->pool) {
                buffer->pool_size = size;
                buffer->head = 0;
                buffer->tail = 0;

                for (i = 0; i < size; i++) {
                        buffer->pool[i].index = i;
                        buffer->pool[i].queue_type = type;
                        if (type == DAPL_COOKIE_QUEUE_EP) {
                                buffer->pool[i].queue.ep = queue;
                        } else {
                                buffer->pool[i].queue.srq = queue;
                        }
                }

                return (DAT_SUCCESS);
        } else {
                return (DAT_INSUFFICIENT_RESOURCES);
        }
}

/*
 * dapls_cb_resize
 *
 * Given a DAPL_COOKIE_BUFFER, reallocate a larger buffer and initialize
 * memory for the data structure from an old one
 *
 * Input:
 *      curr_buffer     pointer to existing DAPL_COOKIE_BUFFER
 *      new_size        new number of elements to allocate & manage,
 *                      has to be > current buffer's size
 *      new_buffer      pointer to the newly allocated cookie buffer
 *
 * Output:
 *      none
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INVALID_PARAMETER
 *      DAT_INSUFFICIENT_RESOURCES
 *
 */
DAT_RETURN
dapls_cb_resize(
        IN      DAPL_COOKIE_BUFFER      *curr_buffer,
        IN      DAT_COUNT               new_size,
        IN      DAPL_COOKIE_BUFFER      *new_buffer)
{
        int             index;
        DAPL_ATOMIC     head;
        DAPL_ATOMIC     tail;

        DAT_RETURN      dat_return;

        if (new_size < curr_buffer->pool_size) {
                return (DAT_ERROR(DAT_INVALID_PARAMETER, 0));
        }

        /*
         * create a new cookie buffer, the queue type and queue ptr remain the
         * same as the curr_buffer so use the values from there
         */
        dat_return = dapls_cb_create(new_buffer,
            curr_buffer->pool[0].queue.ptr, curr_buffer->pool[0].queue_type,
            new_size);

        if (dat_return != DAT_SUCCESS) {
                return (dat_return);
        }

        /* copy all the free cookies to the new buffer */
        head = curr_buffer->head;
        tail = curr_buffer->tail;
        index = 0;
        while (head != tail) {
                new_buffer->pool[index] = curr_buffer->pool[head];
                head = (head + 1) % curr_buffer->pool_size;
                index++;
        }
        new_buffer->head = 0;
        new_buffer->tail = index;

        return (DAT_SUCCESS);
}

/*
 * dapls_cb_free
 *
 * Free the data structure
 *
 * Input:
 *      buffer          pointer to DAPL_COOKIE_BUFFER
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapls_cb_free(
        IN  DAPL_COOKIE_BUFFER  *buffer)
{
        if (NULL != buffer->pool) {
                dapl_os_free(buffer->pool, buffer->pool_size *
                    sizeof (DAPL_COOKIE));
        }
}


/*
 * dapls_cb_get
 *
 * Remove an entry from the buffer
 *
 * Input:
 *      buffer          pointer to DAPL_COOKIE_BUFFER
 *
 * Output:
 *      cookie_ptr      pointer to pointer to cookie
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INVALID_PARAMETER
 *      DAT_INSUFFICIENT_RESOURCES
 *
 */
DAT_RETURN
dapls_cb_get(
        IN  DAPL_COOKIE_BUFFER  *buffer,
        OUT DAPL_COOKIE                 **cookie_ptr)
{
        DAT_RETURN      dat_status;
        DAT_COUNT       new_head;

        dapl_os_assert(NULL != cookie_ptr);

        new_head = (buffer->head + 1) % buffer->pool_size;

        if (new_head == buffer->tail) {
                dat_status = DAT_INSUFFICIENT_RESOURCES;
                goto bail;
        } else {
                buffer->head = new_head;
                *cookie_ptr = &buffer->pool[buffer->head];
                dat_status = DAT_SUCCESS;
        }

bail:
        return (dat_status);
}

/*
 * dapls_cb_put
 *
 * Add entry(s) to the buffer
 *
 * Input:
 *      buffer          pointer to DAPL_COOKIE_BUFFER
 *      cookie          pointer to cookie
 *
 * Output:
 *      entry           entry removed from the ring buffer
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INSUFFICIENT_EMPTY
 *
 */
DAT_RETURN
dapls_cb_put(
        IN  DAPL_COOKIE_BUFFER  *buffer,
        IN  DAPL_COOKIE         *cookie)
{
        buffer->tail = cookie->index;

        return (DAT_SUCCESS);
}

/*
 * dapls_rmr_cookie_alloc
 *
 * Allocate an RMR Bind cookie
 *
 * Input:
 *      buffer          pointer to DAPL_COOKIE_BUFFER
 *      rmr             rmr to associate with the cookie
 *      user_cookie     user's cookie data
 *
 * Output:
 *      cookie_ptr      pointer to pointer to allocated cookie
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INSUFFICIENT_EMPTY
 *
 */
DAT_RETURN
dapls_rmr_cookie_alloc(
        IN DAPL_COOKIE_BUFFER   *buffer,
        IN DAPL_RMR             *rmr,
        IN DAT_RMR_COOKIE       user_cookie,
        OUT DAPL_COOKIE         **cookie_ptr)
{
        DAPL_COOKIE             *cookie;
        DAT_RETURN              dat_status;

        if (DAT_SUCCESS != dapls_cb_get(buffer, &cookie)) {
                *cookie_ptr = NULL;
                dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
                    DAT_RESOURCE_MEMORY);
                goto bail;
        }

        dat_status = DAT_SUCCESS;
        cookie->type = DAPL_COOKIE_TYPE_RMR;
        cookie->val.rmr.rmr = rmr;
        cookie->val.rmr.cookie = user_cookie;
        *cookie_ptr =  cookie;

bail:
        return (dat_status);
}


/*
 * dapls_dto_cookie_alloc
 *
 * Allocate a DTO cookie
 *
 * Input:
 *      buffer          pointer to DAPL_COOKIE_BUFFER
 *      type            DTO type
 *      user_cookie     user's cookie data
 *
 * Output:
 *      cookie_ptr      pointer to pointer to allocated cookie
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INSUFFICIENT_EMPTY
 *
 */
DAT_RETURN
dapls_dto_cookie_alloc(
        IN DAPL_COOKIE_BUFFER   *buffer,
        IN DAPL_DTO_TYPE        type,
        IN DAT_DTO_COOKIE       user_cookie,
        OUT DAPL_COOKIE         **cookie_ptr)
{
        DAPL_COOKIE             *cookie;

        if (DAT_SUCCESS != dapls_cb_get(buffer, &cookie)) {
                *cookie_ptr = NULL;
                return (DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
                    DAT_RESOURCE_MEMORY));
        }
        cookie->type = DAPL_COOKIE_TYPE_DTO;
        cookie->val.dto.type = type;
        cookie->val.dto.cookie = user_cookie;
        cookie->val.dto.size = 0;

        *cookie_ptr = cookie;
        return (DAT_SUCCESS);
}

void
dapls_cookie_dealloc(
        IN  DAPL_COOKIE_BUFFER  *buffer,
        IN      DAPL_COOKIE     *cookie)
{
        buffer->tail = cookie->index;
}