/*
* 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) {
(void) 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:
*/