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

/*
 *
 * MODULE: dapl_osd.c
 *
 * PURPOSE: Operating System Dependent layer
 * Description:
 *      Provide OS dependent functions with a canonical DAPL
 *      interface. Designed to be portable and hide OS specific quirks
 *      of common functions.
 *
 *
 * $Id: dapl_osd.c,v 1.26 2003/07/31 14:04:18 jlentini Exp $
 */

#include "dapl_osd.h"
#include "dapl.h"
#include "dapl_hca_util.h"
#include "dapl_ia_util.h"
#include "dapl_rmr_util.h"
#include "dapl_lmr_util.h"
#include "dapl_pz_util.h"
#include "dapl_ep_util.h"
#include "dapl_cr_util.h"
#include "dapl_evd_util.h"
#include "dapl_sp_util.h"
#include "dapl_adapter_util.h"
#include "dapl_provider.h"
#include "dapl_hash.h"
#include "dapl_debug.h"

#include <sys/time.h>
#include <stdlib.h>                     /* needed for getenv() */
#include <pthread.h>                    /* needed for pthread_atfork() */
#include <signal.h>                     /* needed for thread setup */

static void dapls_osd_fork_cleanup(void);

/*
 * dapl_osd_init
 *
 * Do Linux initialization:
 * - Set up fork handler to clean up DAPL resources in the child
 *   process after a fork().
 *
 * Input:
 *      none
 *
 * Returns:
 *      DAT_SUCCESS
 */
void
dapl_os_init()
{
        int status;

        /*
         * Set up fork control
         */
        status = pthread_atfork(NULL, NULL, dapls_osd_fork_cleanup);
        if (status != 0) {
                dapl_dbg_log(DAPL_DBG_TYPE_WARN,
                    "WARNING: pthread_atfork %d\n", status);
        }
}


/*
 * dapl_os_get_time
 *
 * Return 64 bit value of current time in microseconds.
 *
 * Input:
 *      loc       User location to place current time
 *
 * Returns:
 *      DAT_SUCCESS
 */

DAT_RETURN
dapl_os_get_time(
    OUT DAPL_OS_TIMEVAL * loc)
{
        struct timeval  tv;
        struct timezone tz;


        (void) gettimeofday(&tv, &tz);
        *loc = ((DAT_UINT64)(tv.tv_sec) * 1000000L) + (DAT_UINT64) tv.tv_usec;

        return (DAT_SUCCESS);
}


/*
 * dapl_os_get__env_bool
 *
 * Return boolean value of passed in environment variable: 1 if present,
 * 0 if not
 *
 * Input:
 *
 *
 * Returns:
 *      TRUE or FALSE
 */
int
dapl_os_get_env_bool(
        char            *env_str)
{
        char            *env_var;

        env_var = getenv(env_str);
        if (env_var != NULL) {
                return (1);
        }

        return (0);
}


/*
 * dapl_os_get_env_val
 *
 * Update val to  value of passed in environment variable if present
 *
 * Input:
 *      env_str
 *      def_val         default value if environment variable does not exist
 *
 * Returns:
 *      TRUE or FALSE
 */
int
dapl_os_get_env_val(
        char            *env_str,
        int             def_val)
{
        char            *env_var;

        env_var = getenv(env_str);
        if (env_var != NULL) {
                def_val = strtol(env_var, NULL, 0);
        }

        return (def_val);
}


/*
 * Wait object routines
 */

/*
 * dapl_os_wait_object_init
 *
 * Initialize a wait object
 *
 * Input:
 *      wait_obj
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INTERNAL_ERROR
 */
DAT_RETURN
dapl_os_wait_object_init(
    IN DAPL_OS_WAIT_OBJECT *wait_obj)
{
        wait_obj->signaled = DAT_FALSE;
        if (0 != pthread_cond_init(&wait_obj->cv, NULL)) {
                return (DAT_ERROR(DAT_INTERNAL_ERROR, 0));
        }

        /* Always returns 0.  */
        (void) pthread_mutex_init(&wait_obj->lock, NULL);

        return (DAT_SUCCESS);
}


/*
 * Wait on the supplied wait object, up to the specified time_out.
 * A timeout of DAT_TIMEOUT_INFINITE will wait indefinitely.
 * Timeout should be specified in micro seconds.
 *
 * Functional returns:
 *      DAT_SUCCESS -- another thread invoked dapl_os_wait object_wakeup
 *      DAT_INVALID_STATE -- someone else is already waiting in this wait
 *      object.
 *                           only one waiter is allowed at a time.
 *      DAT_ABORT -- another thread invoked dapl_os_wait_object_destroy
 *      DAT_TIMEOUT -- the specified time limit was reached.
 */

DAT_RETURN
dapl_os_wait_object_wait(
        IN  DAPL_OS_WAIT_OBJECT *wait_obj,
        IN  DAT_TIMEOUT timeout_val)
{
        DAT_RETURN              dat_status;
        int                     pthread_status;
        struct timespec         future;

        dat_status = DAT_SUCCESS;
        pthread_status = 0;

        if (timeout_val != DAT_TIMEOUT_INFINITE) {
                struct timeval now;
                struct timezone tz;
                unsigned int microsecs;

                (void) gettimeofday(&now, &tz);
                microsecs = now.tv_usec + (timeout_val % 1000000);
                if (microsecs > 1000000) {
                        now.tv_sec = now.tv_sec + timeout_val / 1000000 + 1;
                        now.tv_usec = microsecs - 1000000;
                } else {
                        now.tv_sec = now.tv_sec + timeout_val / 1000000;
                        now.tv_usec = microsecs;
                }

                /* Convert timeval to timespec */
                future.tv_sec = now.tv_sec;
                future.tv_nsec = now.tv_usec * 1000;

                (void) pthread_mutex_lock(&wait_obj->lock);
                while (wait_obj->signaled == DAT_FALSE && pthread_status == 0) {
                        pthread_status = pthread_cond_timedwait(
                            &wait_obj->cv, &wait_obj->lock, &future);

                        /*
                         * No need to reset &future if we go around the loop;
                         * It's an absolute time.
                         */
                }
                /* Reset the signaled status if we were woken up.  */
                if (pthread_status == 0) {
                        wait_obj->signaled = DAT_FALSE;
                }
                (void) pthread_mutex_unlock(&wait_obj->lock);
        } else {
                (void) pthread_mutex_lock(&wait_obj->lock);
                while (wait_obj->signaled == DAT_FALSE && pthread_status == 0) {
                        pthread_status = pthread_cond_wait(
                            &wait_obj->cv, &wait_obj->lock);
                }
                /* Reset the signaled status if we were woken up.  */
                if (pthread_status == 0) {
                        wait_obj->signaled = DAT_FALSE;
                }
                (void) pthread_mutex_unlock(&wait_obj->lock);
        }

        if (ETIMEDOUT == pthread_status) {
                dat_status = DAT_ERROR(DAT_TIMEOUT_EXPIRED, 0);
        } else if (0 != pthread_status) {
                dat_status = DAT_ERROR(DAT_INTERNAL_ERROR, 0);
        }

        return (dat_status);
}


/*
 * dapl_os_wait_object_wakeup
 *
 * Wakeup a thread waiting on a wait object
 *
 * Input:
 *      wait_obj
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INTERNAL_ERROR
 */
DAT_RETURN
dapl_os_wait_object_wakeup(
    IN  DAPL_OS_WAIT_OBJECT *wait_obj)
{
        (void) pthread_mutex_lock(&wait_obj->lock);
        wait_obj->signaled = DAT_TRUE;
        (void) pthread_mutex_unlock(&wait_obj->lock);
        if (0 != pthread_cond_signal(&wait_obj->cv)) {
                return (DAT_ERROR(DAT_INTERNAL_ERROR, 0));
        }

        return (DAT_SUCCESS);
}


/*
 * dapl_os_wait_object_destroy
 *
 * Destroy a wait object
 *
 * Input:
 *      wait_obj
 *
 * Returns:
 *      DAT_SUCCESS
 *      DAT_INTERNAL_ERROR
 */
DAT_RETURN
dapl_os_wait_object_destroy(
    IN  DAPL_OS_WAIT_OBJECT *wait_obj)
{
        if (0 != pthread_cond_destroy(&wait_obj->cv)) {
                return (DAT_ERROR(DAT_INTERNAL_ERROR, 0));
        }
        if (0 != pthread_mutex_destroy(&wait_obj->lock)) {
                return (DAT_ERROR(DAT_INTERNAL_ERROR, 0));
        }

        return (DAT_SUCCESS);
}


/*
 * dapls_osd_fork_cleanup
 *
 * Update val to  value of passed in environment variable if present
 *
 * Input:
 *      env_str
 *      val             Updated if environment variable exists
 *
 * Returns:
 *      TRUE or FALSE
 */
void
dapls_osd_fork_cleanup(void)
{
        DAPL_PROVIDER_LIST_NODE         *cur_node;
        DAPL_HCA                        *hca_ptr;
        DAPL_IA                         *ia_ptr;
        DAPL_LMR                        *lmr_ptr;
        DAPL_RMR                        *rmr_ptr;
        DAPL_PZ                         *pz_ptr;
        DAPL_CR                         *cr_ptr;
        DAPL_EP                         *ep_ptr;
        DAPL_EVD                        *evd_ptr;
        DAT_EP_PARAM                    *param;
        DAPL_SP                         *sp_ptr;

        while (NULL != g_dapl_provider_list.head) {
                cur_node = g_dapl_provider_list.head;
                g_dapl_provider_list.head = cur_node->next;

                hca_ptr = (DAPL_HCA *) cur_node->data.extension;

                /*
                 * Walk the list of IA ptrs & clean up. This is purposely
                 * a destructive list walk, we really don't want to preserve
                 * any of it.
                 */
                while (!dapl_llist_is_empty(&hca_ptr->ia_list_head)) {
                        ia_ptr = (DAPL_IA *)
                            dapl_llist_peek_head(&hca_ptr->ia_list_head);

                        /*
                         * The rest of the cleanup code is similar to
                         * dapl_ia_close, the big difference is that we don't
                         * release IB resources, only memory; the underlying IB
                         * subsystem doesn't deal with fork at all, so leave
                         * IB handles alone.
                         */
                        while (!dapl_llist_is_empty(&ia_ptr->rmr_list_head)) {
                                rmr_ptr = (DAPL_RMR *)
                                    dapl_llist_peek_head(&ia_ptr->
                                    rmr_list_head);
                                if (rmr_ptr->param.lmr_triplet.
                                    virtual_address != 0) {
                                        dapl_os_atomic_dec(&rmr_ptr->
                                            lmr->lmr_ref_count);
                                        rmr_ptr->param.lmr_triplet.
                                            virtual_address = 0;
                                }
                                dapl_os_atomic_dec(&rmr_ptr->pz->pz_ref_count);
                                dapl_ia_unlink_rmr(rmr_ptr->header.owner_ia,
                                    rmr_ptr);
                                dapl_rmr_dealloc(rmr_ptr);
                        }

                        while (!dapl_llist_is_empty(&ia_ptr->rsp_list_head)) {
                                sp_ptr = (DAPL_SP *) dapl_llist_peek_head(
                                    &ia_ptr->rsp_list_head);
                                dapl_os_atomic_dec(&((DAPL_EVD *)sp_ptr->
                                    evd_handle)->evd_ref_count);
                                dapls_ia_unlink_sp(ia_ptr, sp_ptr);
                                dapls_sp_free_sp(sp_ptr);
                        }

                        while (!dapl_llist_is_empty(&ia_ptr->ep_list_head)) {
                                ep_ptr = (DAPL_EP *) dapl_llist_peek_head(
                                    &ia_ptr->ep_list_head);
                                param = &ep_ptr->param;
                                if (param->pz_handle != NULL) {
                                        dapl_os_atomic_dec(&((DAPL_PZ *)param->
                                            pz_handle)->pz_ref_count);
                                }
                                if (param->recv_evd_handle != NULL) {
                                        dapl_os_atomic_dec(&((DAPL_EVD *)param->
                                            recv_evd_handle)->evd_ref_count);
                                }
                                if (param->request_evd_handle) {
                                        dapl_os_atomic_dec(&((DAPL_EVD *)param->
                                            request_evd_handle)->evd_ref_count);
                                }
                                if (param->connect_evd_handle != NULL) {
                                        dapl_os_atomic_dec(&((DAPL_EVD *)param->
                                            connect_evd_handle)->evd_ref_count);
                                }

                                /* ...and free the resource */
                                dapl_ia_unlink_ep(ia_ptr, ep_ptr);
                                dapl_ep_dealloc(ep_ptr);
                        }

                        while (!dapl_llist_is_empty(&ia_ptr->lmr_list_head)) {
                                lmr_ptr = (DAPL_LMR *) dapl_llist_peek_head(
                                    &ia_ptr->lmr_list_head);

                                (void) dapls_hash_remove(lmr_ptr->header.
                                    owner_ia->hca_ptr->lmr_hash_table,
                                    lmr_ptr->param.lmr_context, NULL);

                                pz_ptr = (DAPL_PZ *) lmr_ptr->param.pz_handle;
                                dapl_os_atomic_dec(&pz_ptr->pz_ref_count);
                                dapl_ia_unlink_lmr(lmr_ptr->header.owner_ia,
                                    lmr_ptr);
                                dapl_lmr_dealloc(lmr_ptr);
                        }

                        while (!dapl_llist_is_empty(&ia_ptr->psp_list_head)) {
                                sp_ptr = (DAPL_SP *) dapl_llist_peek_head(
                                    &ia_ptr->psp_list_head);
                                while (!dapl_llist_is_empty(&sp_ptr->
                                    cr_list_head)) {
                                        cr_ptr = (DAPL_CR *)
                                            dapl_llist_peek_head(
                                            &sp_ptr->cr_list_head);
                                        dapl_sp_remove_cr(sp_ptr, cr_ptr);
                                        dapls_cr_free(cr_ptr);
                                }

                                dapls_ia_unlink_sp(ia_ptr, sp_ptr);
                                dapl_os_atomic_dec(&((DAPL_EVD *)sp_ptr->
                                    evd_handle)->evd_ref_count);
                                dapls_sp_free_sp(sp_ptr);
                        }

                        while (!dapl_llist_is_empty(&ia_ptr->pz_list_head)) {
                                pz_ptr = (DAPL_PZ *)
                                    dapl_llist_peek_head(&ia_ptr->pz_list_head);
                                dapl_ia_unlink_pz(pz_ptr->header.owner_ia,
                                    pz_ptr);
                                dapl_pz_dealloc(pz_ptr);
                        }

                        while (!dapl_llist_is_empty(&ia_ptr->evd_list_head)) {
                                evd_ptr = (DAPL_EVD *) dapl_llist_peek_head(
                                    &ia_ptr->evd_list_head);
                                dapl_ia_unlink_evd(evd_ptr->header.owner_ia,
                                    evd_ptr);
                                /*
                                 * reset the cq_handle to avoid having it
                                 * removed
                                 */
                                evd_ptr->ib_cq_handle = IB_INVALID_HANDLE;
                                (void) dapls_evd_dealloc(evd_ptr);
                        }

                        dapl_hca_unlink_ia(ia_ptr->hca_ptr, ia_ptr);
                        /*
                         * asycn error evd was taken care of above, reset the
                         * pointer
                         */
                        ia_ptr->async_error_evd = NULL;
                        dapls_ia_free(ia_ptr);
                }       /* end while( ia_ptr != NULL ) */


                dapl_os_free(cur_node, sizeof (DAPL_PROVIDER_LIST_NODE));
        } /* end while (NULL != g_dapl_provider_list.head) */
}


/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 8
 * End:
 */