root/usr/src/lib/udapl/udapl_tavor/common/dapl_ia_util.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 *
 * MODULE: dapl_ia_util.c
 *
 * PURPOSE: Manage IA Info structure
 *
 * $Id: dapl_ia_util.c,v 1.29 2003/07/25 19:24:11 sjs2 Exp $
 */

#include "dapl.h"
#include "dapl_hca_util.h"
#include "dapl_ia_util.h"
#include "dapl_evd_util.h"
#include "dapl_adapter_util.h"

/* Internal prototype */
void dapli_ia_release_hca(
        DAPL_HCA                *hca_ptr);


/*
 * dapl_ia_alloc
 *
 * alloc and initialize an IA INFO struct
 *
 * Input:
 *      none
 *
 * Output:
 *      ia_ptr
 *
 * Returns:
 *      none
 *
 */
DAPL_IA *
dapl_ia_alloc(DAT_PROVIDER * provider, DAPL_HCA * hca_ptr)
{
        DAPL_IA * ia_ptr;

        /* Allocate IA */
        ia_ptr = (DAPL_IA *) dapl_os_alloc(sizeof (DAPL_IA));
        if (ia_ptr == NULL) {
                return (NULL);
        }

        /* zero the structure */
        (void) dapl_os_memzero(ia_ptr, sizeof (DAPL_IA));

        /*
         * initialize the header
         */
        ia_ptr->header.provider         = provider;
        ia_ptr->header.magic            = DAPL_MAGIC_IA;
        ia_ptr->header.handle_type      = DAT_HANDLE_TYPE_IA;
        ia_ptr->header.owner_ia         = ia_ptr;
        ia_ptr->header.user_context.as_64 = 0;
        ia_ptr->header.user_context.as_ptr = NULL;
        dapl_llist_init_entry(&ia_ptr->header.ia_list_entry);
        dapl_os_lock_init(&ia_ptr->header.lock);

        /*
         * initialize the body
         */
        ia_ptr->hca_ptr = hca_ptr;
        ia_ptr->async_error_evd = NULL;
        ia_ptr->cleanup_async_error_evd = DAT_FALSE;
        dapl_llist_init_entry(&ia_ptr->hca_ia_list_entry);
        dapl_llist_init_head(&ia_ptr->ep_list_head);
        dapl_llist_init_head(&ia_ptr->lmr_list_head);
        dapl_llist_init_head(&ia_ptr->rmr_list_head);
        dapl_llist_init_head(&ia_ptr->pz_list_head);
        dapl_llist_init_head(&ia_ptr->evd_list_head);
        dapl_llist_init_head(&ia_ptr->cno_list_head);
        dapl_llist_init_head(&ia_ptr->rsp_list_head);
        dapl_llist_init_head(&ia_ptr->psp_list_head);

        /*
         * initialize the flags
         */
        ia_ptr->dapl_flags = 0;

        dapl_hca_link_ia(hca_ptr, ia_ptr);

        return (ia_ptr);
}


/*
 * dapl_ia_abrupt_close
 *
 * Performs an abrupt close of the IA
 *
 * Input:
 *      ia_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      status
 *
 */

DAT_RETURN
dapl_ia_abrupt_close(IN DAPL_IA *ia_ptr)
{
        DAT_RETURN      dat_status;
        DAPL_EP         *ep_ptr, *next_ep_ptr;
        DAPL_LMR        *lmr_ptr, *next_lmr_ptr;
        DAPL_RMR        *rmr_ptr, *next_rmr_ptr;
        DAPL_PZ         *pz_ptr, *next_pz_ptr;
        DAPL_EVD        *evd_ptr, *next_evd_ptr;
        DAPL_CNO        *cno_ptr, *next_cno_ptr;
        DAPL_SP         *sp_ptr, *next_sp_ptr; /* for PSP and RSP queues */
        DAPL_HCA        *hca_ptr;

        dat_status = DAT_SUCCESS;

        /*
         * clear all the data structures associated with the IA.
         * this must be done in order (rmr,rsp) before (ep lmr psp) before
         * (pz evd)
         *
         * Note that in all the following we can leave the loop either
         * when we run out of entries, or when we get back to the head
         * if we end up skipping an entry.
         */

        rmr_ptr = (dapl_llist_is_empty(&ia_ptr->rmr_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->rmr_list_head));
        while (rmr_ptr != NULL) {
                next_rmr_ptr = dapl_llist_next_entry(&ia_ptr->rmr_list_head,
                    &rmr_ptr->header.ia_list_entry);
                dat_status = dapl_rmr_free(rmr_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): rmr_free(%p) returns %x\n",
                            rmr_ptr,
                            dat_status);
                }
                rmr_ptr = next_rmr_ptr;
        }

        sp_ptr = (dapl_llist_is_empty(&ia_ptr->rsp_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->rsp_list_head));
        while (sp_ptr != NULL) {
                next_sp_ptr = dapl_llist_next_entry(&ia_ptr->rsp_list_head,
                    &sp_ptr->header.ia_list_entry);
                dat_status = dapl_rsp_free(sp_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): rsp_free(%p) returns %x\n",
                            sp_ptr,
                            dat_status);
                }
                sp_ptr = next_sp_ptr;
        }

        ep_ptr = (dapl_llist_is_empty(&ia_ptr->ep_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->ep_list_head));
        while (ep_ptr != NULL) {
                next_ep_ptr = dapl_llist_next_entry(&ia_ptr->ep_list_head,
                    &ep_ptr->header.ia_list_entry);
                dat_status = dapl_ep_disconnect(ep_ptr, DAT_CLOSE_ABRUPT_FLAG);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): ep_disconnect(%p) returns %x\n",
                            ep_ptr,
                            dat_status);
                }
                dat_status = dapl_ep_free(ep_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): ep_free(%p) returns %x\n",
                            ep_ptr,
                            dat_status);
                }
                ep_ptr = next_ep_ptr;
        }

        lmr_ptr = (dapl_llist_is_empty(&ia_ptr->lmr_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->lmr_list_head));
        while (lmr_ptr != NULL) {
                next_lmr_ptr = dapl_llist_next_entry(&ia_ptr->lmr_list_head,
                    &lmr_ptr->header.ia_list_entry);
                dat_status = dapl_lmr_free(lmr_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): lmr_free(%p) returns %x\n",
                            lmr_ptr,
                            dat_status);
                }
                lmr_ptr = next_lmr_ptr;
        }

        sp_ptr = (dapl_llist_is_empty(&ia_ptr->psp_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->psp_list_head));
        while (sp_ptr != NULL) {
                next_sp_ptr = dapl_llist_next_entry(&ia_ptr->psp_list_head,
                    &sp_ptr->header.ia_list_entry);
                dat_status = dapl_psp_free(sp_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): psp_free(%p) returns %x\n",
                            sp_ptr,
                            dat_status);
                }
                sp_ptr = next_sp_ptr;
        }

        pz_ptr = (dapl_llist_is_empty(&ia_ptr->pz_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->pz_list_head));
        while (pz_ptr != NULL) {
                next_pz_ptr = dapl_llist_next_entry(&ia_ptr->pz_list_head,
                    &pz_ptr->header.ia_list_entry);
                dat_status = dapl_pz_free(pz_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): pz_free(%p) returns %x\n",
                            pz_ptr,
                            dat_status);
                }
                pz_ptr = next_pz_ptr;
        }

        /*
         * EVDs are tricky; we want to release all except for the async
         * EVD.  That EVD needs to stick around until after we close the
         * HCA, to accept any async events that occur.  So we cycle through
         * the list with dapl_llist_next_entry instead of dapl_llist_is_empty.
         */
        evd_ptr = (dapl_llist_is_empty(&ia_ptr->evd_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->evd_list_head));
        while (evd_ptr != NULL) {
                next_evd_ptr = dapl_llist_next_entry(&ia_ptr->evd_list_head,
                    &evd_ptr->header.ia_list_entry);
                if (evd_ptr == ia_ptr->async_error_evd) {
                                /*
                                 * Don't delete the EVD, but break any CNO
                                 * connections.
                                 */
                                (void) dapl_evd_disable(evd_ptr);
                                (void) dapl_evd_modify_cno(evd_ptr,
                                    DAT_HANDLE_NULL);
                } else {
                        /* it isn't the async EVD; delete it.  */
                        dat_status = dapl_evd_free(evd_ptr);
                        if (dat_status != DAT_SUCCESS) {
                                dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                                    "ia_close(ABRUPT): evd_free(%p) "
                                    "returns %x\n",
                                    evd_ptr,
                                    dat_status);
                        }
                }
                evd_ptr = next_evd_ptr;
        }

        cno_ptr = (dapl_llist_is_empty(&ia_ptr->cno_list_head)
            ? NULL : dapl_llist_peek_head(&ia_ptr->cno_list_head));
        while (cno_ptr != NULL) {
                next_cno_ptr = dapl_llist_next_entry(&ia_ptr->cno_list_head,
                    &cno_ptr->header.ia_list_entry);
                dat_status = dapl_cno_free(cno_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): cno_free(%p) returns %x\n",
                            cno_ptr,
                            dat_status);
                }
                cno_ptr = next_cno_ptr;
                }

        hca_ptr = ia_ptr->hca_ptr;

        /*
         * Free the async EVD, shutting down callbacks from the HCA.
         */
        if (ia_ptr->async_error_evd &&
            (DAT_TRUE == ia_ptr->cleanup_async_error_evd)) {
                dat_status = dapls_ia_teardown_callbacks(ia_ptr);

                hca_ptr->async_evd = NULL; /* It was our async EVD; nuke it.  */

                dapl_os_atomic_dec(& ia_ptr->async_error_evd->evd_ref_count);
                dat_status = dapl_evd_free(ia_ptr->async_error_evd);

                if (DAT_SUCCESS != dat_status) {
                        dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                            "ia_close(ABRUPT): evd_free(%p) returns %x\n",
                            ia_ptr->async_error_evd,
                            dat_status);
                }

                ia_ptr->async_error_evd = NULL;
        }

        /*
         * Release our reference on the hca_handle. If we are the last
         * one, close it
         */
        dapli_ia_release_hca(hca_ptr);

        dapls_ia_free(ia_ptr);

        return (DAT_SUCCESS);           /* Abrupt close can't fail.  */
}


/*
 * dapl_ia_graceful_close
 *
 * Performs an graceful close of the IA
 *
 * Input:
 *      ia_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      status
 *
 */

DAT_RETURN
dapl_ia_graceful_close(IN DAPL_IA *ia_ptr)
{
        DAT_RETURN              dat_status;
        DAT_RETURN              cur_dat_status;
        DAPL_EVD                *evd_ptr;
        DAPL_LLIST_ENTRY        *entry;
        DAPL_HCA                *hca_ptr;

        dat_status = DAT_SUCCESS;

        if (!dapl_llist_is_empty(&ia_ptr->rmr_list_head) ||
            !dapl_llist_is_empty(&ia_ptr->rsp_list_head) ||
            !dapl_llist_is_empty(&ia_ptr->ep_list_head) ||
            !dapl_llist_is_empty(&ia_ptr->lmr_list_head) ||
            !dapl_llist_is_empty(&ia_ptr->psp_list_head) ||
            !dapl_llist_is_empty(&ia_ptr->pz_list_head)) {
                dat_status = DAT_ERROR(DAT_INVALID_STATE,
                    DAT_INVALID_STATE_IA_IN_USE);
                goto bail;
        }

        /* if the async evd does not need to be cleaned up      */
        /* (ie. it was not created by dapl_ia_open)             */
        /*  then the evd list should be empty                   */
        if (DAT_FALSE == ia_ptr->cleanup_async_error_evd) {
                if (!dapl_llist_is_empty(&ia_ptr->evd_list_head)) {
                        dat_status = DAT_ERROR(DAT_INVALID_STATE,
                            DAT_INVALID_STATE_IA_IN_USE);
                        goto bail;
                }
        } else {
        /* else the async evd should be the only evd in */
        /* the list.                                    */
                evd_ptr = (DAPL_EVD *)
                    dapl_llist_peek_head(&ia_ptr->evd_list_head);

                if (!(evd_ptr->evd_flags & DAT_EVD_ASYNC_FLAG)) {
                        dat_status = DAT_ERROR(DAT_INVALID_STATE,
                            DAT_INVALID_STATE_IA_IN_USE);
                        goto bail;
                }

                entry = ia_ptr->evd_list_head;

                /* if the async evd is not the only element in the list */
                if (entry->blink != entry->flink) {
                        dat_status = DAT_ERROR(DAT_INVALID_STATE,
                            DAT_INVALID_STATE_IA_IN_USE);
                        goto bail;
                }

                /*
                 * If the async evd has a non-unary ref count (i.e. it's in
                 * use by someone besides us.
                 */
                if (evd_ptr->evd_ref_count != 1) {
                        dat_status = DAT_ERROR(DAT_INVALID_STATE,
                            DAT_INVALID_STATE_IA_IN_USE);
                        goto bail;
                }
        }

        /*
         * We've validated the call; now we can start the teardown.
         * Because we're in the IA close routine, we're safe from races with
         * DAPL consumers on this IA (operate/destroy races are disallowed in
         * DAPL).
         */
        hca_ptr = ia_ptr->hca_ptr;

        /* Tear down the async EVD if needed, first shutting down callbacks.  */
        if (ia_ptr->async_error_evd &&
            (DAT_TRUE == ia_ptr->cleanup_async_error_evd)) {
                cur_dat_status = dapls_ia_teardown_callbacks(ia_ptr);
                if (DAT_SUCCESS != cur_dat_status) {
                        dat_status = cur_dat_status;
                }
                hca_ptr->async_evd = NULL;
                dapl_os_atomic_dec(& ia_ptr->async_error_evd->evd_ref_count);
                cur_dat_status = dapl_evd_free(ia_ptr->async_error_evd);
                if (DAT_SUCCESS != cur_dat_status) {
                        dat_status = cur_dat_status;
                }

                ia_ptr->async_error_evd = NULL;
        }

        dapli_ia_release_hca(hca_ptr);

        dapls_ia_free(ia_ptr);

bail:
        return (dat_status);
}

/*
 * Release a reference on the HCA handle. If it is 0, close the
 * handle. Manipulate under lock to prevent races with threads trying to
 * open the HCA.
 */
void
dapli_ia_release_hca(
    DAPL_HCA            *hca_ptr)
{
        dapl_os_lock(&hca_ptr->lock);
        dapl_os_atomic_dec(& hca_ptr->handle_ref_count);
        if (hca_ptr->handle_ref_count == 0) {
                DAT_RETURN dat_status;

                /*
                 * Get rid of the cqd associated with the hca.
                 * Print out instead of status return as this routine
                 * shouldn't fail.
                 */
                dat_status = dapls_ib_cqd_destroy(hca_ptr);
                if (dat_status != DAT_SUCCESS) {
                        dapl_dbg_log(DAPL_DBG_TYPE_ERR,
                            "ERR: Cannot free CQD: err %x\n", dat_status);
                }

                (void) dapls_ib_close_hca(hca_ptr->ib_hca_handle);
                hca_ptr->ib_hca_handle = IB_INVALID_HANDLE;
        }
        dapl_os_unlock(&hca_ptr->lock);
}


/*
 * dapls_ia_free
 *
 * free an IA INFO struct
 *
 * Input:
 *      ia_ptr
 *
 * Output:
 *      one
 *
 * Returns:
 *      none
 *
 */
void
dapls_ia_free(DAPL_IA *ia_ptr)
{
        dapl_os_assert(ia_ptr->header.magic == DAPL_MAGIC_IA);

        dapl_os_assert(ia_ptr->async_error_evd == NULL);
        dapl_os_assert(dapl_llist_is_empty(&ia_ptr->lmr_list_head));
        dapl_os_assert(dapl_llist_is_empty(&ia_ptr->rmr_list_head));
        dapl_os_assert(dapl_llist_is_empty(&ia_ptr->ep_list_head));
        dapl_os_assert(dapl_llist_is_empty(&ia_ptr->evd_list_head));
        dapl_os_assert(dapl_llist_is_empty(&ia_ptr->cno_list_head));
        dapl_os_assert(dapl_llist_is_empty(&ia_ptr->psp_list_head));
        dapl_os_assert(dapl_llist_is_empty(&ia_ptr->rsp_list_head));

        /*
         * deinitialize the header
         */
        dapl_hca_unlink_ia(ia_ptr->hca_ptr, ia_ptr);
        /* reset magic to prevent reuse */
        ia_ptr->header.magic = DAPL_MAGIC_INVALID;
        dapl_os_lock_destroy(&ia_ptr->header.lock);

        dapl_os_free(ia_ptr, sizeof (DAPL_IA));
}

/*
 * dapl_ia_link_ep
 *
 * Add an ep to the IA structure
 *
 * Input:
 *      ia_ptr
 *      ep_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_ep(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_EP    *ep_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->ep_list_head,
            &ep_ptr->header.ia_list_entry,
            ep_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_unlink_ep
 *
 * Remove an ep from the ia info structure
 *
 * Input:
 *      ia_ptr
 *      ep_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_unlink_ep(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_EP    *ep_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(&ia_ptr->ep_list_head,
            &ep_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_link_lmr
 *
 * Add an lmr to the IA structure
 *
 * Input:
 *      ia_ptr
 *      lmr_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_lmr(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_LMR   *lmr_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->lmr_list_head,
            &lmr_ptr->header.ia_list_entry,
            lmr_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_unlink_lmr
 *
 * Remove an lmr from the ia info structure
 *
 * Input:
 *      ia_ptr
 *      lmr_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_unlink_lmr(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_LMR   *lmr_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(&ia_ptr->lmr_list_head,
            &lmr_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_link_rmr
 *
 * Add an rmr to the IA structure
 *
 * Input:
 *      ia_ptr
 *      rmr_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_rmr(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_RMR   *rmr_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->rmr_list_head,
            &rmr_ptr->header.ia_list_entry,
            rmr_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_unlink_rmr
 *
 * Remove an rmr from the ia info structure
 *
 * Input:
 *      ia_ptr
 *      rmr_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_unlink_rmr(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_RMR   *rmr_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(&ia_ptr->rmr_list_head,
            &rmr_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_link_pz
 *
 * Add an pz to the IA structure
 *
 * Input:
 *      ia_ptr
 *      pz_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_pz(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_PZ    *pz_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->pz_list_head,
            &pz_ptr->header.ia_list_entry,
            pz_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_unlink_pz
 *
 * Remove an pz from the ia info structure
 *
 * Input:
 *      ia_ptr
 *      pz_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_unlink_pz(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_PZ    *pz_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(&ia_ptr->pz_list_head,
            &pz_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_link_evd
 *
 * Add an evd to the IA structure
 *
 * Input:
 *      ia_ptr
 *      evd_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_evd(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_EVD   *evd_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->evd_list_head,
            &evd_ptr->header.ia_list_entry,
            evd_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_unlink_evd
 *
 * Remove an evd from the ia info structure
 *
 * Input:
 *      ia_ptr
 *      evd_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_unlink_evd(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_EVD   *evd_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(&ia_ptr->evd_list_head,
            &evd_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_link_cno
 *
 * Add an cno to the IA structure
 *
 * Input:
 *      ia_ptr
 *      cno_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_cno(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_CNO   *cno_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->cno_list_head,
            &cno_ptr->header.ia_list_entry,
            cno_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_unlink_cno
 *
 * Remove an cno from the ia info structure
 *
 * Input:
 *      ia_ptr
 *      cno_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_unlink_cno(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_CNO   *cno_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(&ia_ptr->cno_list_head,
            &cno_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_link_psp
 *
 * Add an psp to the IA structure
 *
 * Input:
 *      ia_ptr
 *      sp_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_psp(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_SP    *sp_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->psp_list_head,
            &sp_ptr->header.ia_list_entry,
            sp_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * daps_ia_unlink_sp
 *
 * Remove an sp from the appropriate ia rsp or psp queue
 *
 * Input:
 *      ia_ptr
 *      sp_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapls_ia_unlink_sp(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_SP    *sp_ptr)
{
        DAPL_LLIST_HEAD *list_head;

        if (sp_ptr->header.handle_type == DAT_HANDLE_TYPE_PSP) {
                list_head = &ia_ptr->psp_list_head;
        } else {
                dapl_os_assert(sp_ptr->header.handle_type ==
                    DAT_HANDLE_TYPE_RSP);
                list_head = &ia_ptr->rsp_list_head;
        }

        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(list_head,
            &sp_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapls_ia_sp_search
 *
 * Find an RSP or PSP on the IA list with a matching conn_qual value
 *
 * Input:
 *      ia_ptr
 *      sp_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
DAPL_SP *
dapls_ia_sp_search(
        IN      DAPL_IA            *ia_ptr,
        IN      DAT_CONN_QUAL      conn_qual,
        IN      DAT_BOOLEAN        is_psp)
{
        DAPL_SP         *sp_ptr;
        DAPL_LLIST_HEAD *list_head;

        if (is_psp) {
                list_head = &ia_ptr->psp_list_head;
        } else {
                list_head = &ia_ptr->rsp_list_head;
        }

        dapl_os_lock(&ia_ptr->header.lock);

        sp_ptr = (dapl_llist_is_empty(list_head) ? NULL :
            dapl_llist_peek_head(list_head));

        while (sp_ptr != NULL) {
                if (sp_ptr->conn_qual == conn_qual) {
                        break;
                }
                sp_ptr = dapl_llist_next_entry(list_head,
                    &sp_ptr->header.ia_list_entry);
        }

        dapl_os_unlock(&ia_ptr->header.lock);

        return (sp_ptr);
}


/*
 * dapl_ia_link_rsp
 *
 * Add an rsp to the IA structure
 *
 * Input:
 *      ia_ptr
 *      sp_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_rsp(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_SP    *sp_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->rsp_list_head,
            &sp_ptr->header.ia_list_entry,
            sp_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_link_srq
 *
 * Add an srq to the IA structure
 *
 * Input:
 *      ia_ptr
 *      srq_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_link_srq(
        IN      DAPL_IA         *ia_ptr,
        IN      DAPL_SRQ        *srq_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        dapl_llist_add_head(&ia_ptr->srq_list_head,
            &srq_ptr->header.ia_list_entry,
            srq_ptr);
        dapl_os_unlock(&ia_ptr->header.lock);
}

/*
 * dapl_ia_unlink_srq
 *
 * Remove an srq from the ia info structure
 *
 * Input:
 *      ia_ptr
 *      srq_ptr
 *
 * Output:
 *      none
 *
 * Returns:
 *      none
 *
 */
void
dapl_ia_unlink_srq(
        IN      DAPL_IA    *ia_ptr,
        IN      DAPL_SRQ           *srq_ptr)
{
        dapl_os_lock(&ia_ptr->header.lock);
        (void) dapl_llist_remove_entry(&ia_ptr->srq_list_head,
            &srq_ptr->header.ia_list_entry);
        dapl_os_unlock(&ia_ptr->header.lock);
}

DAT_RETURN
dapls_ia_setup_callbacks(
    IN  DAPL_IA         *ia_ptr,
    IN  DAPL_EVD        *async_evd_ptr)
{
        DAT_RETURN dat_status = DAT_SUCCESS;

#if 0
        /*
         * Current implementation of dapls_ib_setup_async_callback() does
         * nothing and returns DAT_SUCCESS. However, it is declared to expect
         * function pointers with different signatures. We do leave the code
         * block out till dapls_ib_setup_async_callback() is implemented.
         */
        /* unaffiliated handler */
        dat_status =
            dapls_ib_setup_async_callback(
            ia_ptr,
            DAPL_ASYNC_UNAFILIATED,
            NULL,
            (ib_async_handler_t)dapl_evd_un_async_error_callback,
            async_evd_ptr);

        if (dat_status != DAT_SUCCESS) {
                dapl_dbg_log(DAPL_DBG_TYPE_ERR,
                    "ib_set_un_async_error_eh failed %d\n",
                    dat_status);
                goto bail;
        }

        /* affiliated cq handler */
        dat_status = dapls_ib_setup_async_callback(
            ia_ptr,
            DAPL_ASYNC_CQ_ERROR,
            NULL,
            (ib_async_handler_t)dapl_evd_cq_async_error_callback,
            async_evd_ptr);

        if (dat_status != DAT_SUCCESS) {
                dapl_dbg_log(DAPL_DBG_TYPE_ERR,
                    "ib_set_cq_async_error_eh failed %d\n",
                    dat_status);
                goto bail;
        }

        /* affiliated qp handler */
        dat_status = dapls_ib_setup_async_callback(
            ia_ptr,
            DAPL_ASYNC_QP_ERROR,
            NULL,
            (ib_async_handler_t)dapl_evd_qp_async_error_callback,
            ia_ptr);
        if (dat_status != DAT_SUCCESS) {
                dapl_dbg_log(DAPL_DBG_TYPE_ERR,
                    "ib_set_qp_async_error_eh failed %d\n",
                    dat_status);
                goto bail;
        }
bail:
#endif
        return (dat_status);
}

DAT_RETURN
dapls_ia_teardown_callbacks(
    IN  DAPL_IA         *ia_ptr)
{
        DAT_RETURN dat_status = DAT_SUCCESS;

        /* unaffiliated handler */
        dat_status =
            dapls_ib_setup_async_callback(
            ia_ptr,
            DAPL_ASYNC_UNAFILIATED,
            NULL,
            (ib_async_handler_t)0,
            NULL);

        if (dat_status != DAT_SUCCESS) {
                dapl_dbg_log(DAPL_DBG_TYPE_ERR,
                    "ib_set_un_async_error_eh failed %d\n",
                    dat_status);
                goto bail;
        }

        /* affiliated cq handler */
        dat_status = dapls_ib_setup_async_callback(
            ia_ptr,
            DAPL_ASYNC_CQ_ERROR,
            NULL,
            (ib_async_handler_t)0,
            NULL);

        if (dat_status != DAT_SUCCESS) {
                dapl_dbg_log(DAPL_DBG_TYPE_ERR,
                    "ib_set_cq_async_error_eh failed %d\n",
                    dat_status);
                goto bail;
        }

        /* affiliated qp handler */
        dat_status = dapls_ib_setup_async_callback(
            ia_ptr,
            DAPL_ASYNC_QP_ERROR,
            NULL,
            (ib_async_handler_t)0,
            NULL);
        if (dat_status != DAT_SUCCESS) {
                dapl_dbg_log(DAPL_DBG_TYPE_ERR,
                    "ib_set_qp_async_error_eh failed %d\n",
                    dat_status);
                goto bail;
        }

bail:
        return (dat_status);
}