/*
* 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
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* MODULE: dapl_evd_util.c
*
* PURPOSE: Manage EVD Info structure
*
* $Id: dapl_evd_util.c,v 1.41 2003/08/20 13:18:36 sjs2 Exp $
*/
#include <strings.h>
#include "dapl_evd_util.h"
#include "dapl_ia_util.h"
#include "dapl_cno_util.h"
#include "dapl_ring_buffer_util.h"
#include "dapl_adapter_util.h"
#include "dapl_tavor_ibtf_impl.h"
#include "dapl_cookie.h"
#include "dapl.h"
#ifdef DAPL_DBG /* For debugging. */
static void
#endif
static DAT_BOOLEAN
static DAT_RETURN
/*
* dapls_evd_internal_create
*
* actually create the evd. this is called after all parameter checking
* has been performed in dapl_ep_create. it is also called from dapl_ia_open
* to create the default async evd.
*
* Input:
* ia_ptr
* cno_ptr
* qlen
* evd_flags
*
* Output:
* evd_ptr_ptr
*
* Returns:
* none
*
*/
{
*evd_ptr_ptr = NULL;
min_qlen);
if (!evd_ptr) {
goto bail;
}
/*
* If we are dealing with event streams besides a CQ event stream,
* be conservative and set producer side locking. Otherwise, no.
*/
/* Before we setup any callbacks, transition state to OPEN. */
/*
* we need to call cq_alloc even for connection/cr/async evds
* since all the allocation happens there.
*/
if (dat_status != DAT_SUCCESS) {
goto bail;
}
(unsigned int *) evd_ptr->ib_cq_handle,
evd_ptr);
if (dat_status != DAT_SUCCESS) {
goto bail;
}
/*
* cq_notify is not required since when evd_wait is called
* time we go and poll cq anyways.
* dat_status = dapls_set_cq_notify(ia_ptr, evd_ptr);
*/
/*
* We now have an accurate count of events, so allocate them into
* the EVD
*/
if (dat_status != DAT_SUCCESS) {
goto bail;
}
/* We're assuming success in the following. */
*evd_ptr_ptr = evd_ptr;
bail:
if (dat_status != DAT_SUCCESS) {
if (evd_ptr) {
(void) dapls_evd_dealloc(evd_ptr);
}
}
return (dat_status);
}
/*
* dapls_evd_alloc
*
* alloc and initialize an EVD struct
*
* Input:
* ia
*
* Output:
* evd_ptr
*
* Returns:
* none
*
*/
DAPL_EVD *
{
/* Allocate EVD */
if (!evd_ptr) {
goto bail;
}
/* zero the structure */
/*
* initialize the header
*/
/*
* Initialize the body
*/
evd_ptr->evd_ref_count = 0;
bail:
return (evd_ptr);
}
/*
* dapls_evd_event_alloc
*
* alloc events into an EVD.
*
* Input:
* evd_ptr
* qlen
*
* Output:
* NONE
*
* Returns:
* DAT_SUCCESS
* ERROR
*
*/
{
DAT_COUNT i;
/* Allocate EVENTs */
if (!event_ptr) {
goto bail;
}
/* allocate free event queue */
if (dat_status != DAT_SUCCESS) {
goto bail;
}
/* allocate pending event queue */
if (dat_status != DAT_SUCCESS) {
goto bail;
}
/* add events to free event queue */
for (i = 0; i < qlen; i++) {
(void *)event_ptr);
event_ptr++;
}
evd_ptr->cq_notified_when = 0;
evd_ptr->cno_active_count = 0;
/* Take a reference count on the CNO */
}
bail:
return (dat_status);
}
/*
* dapls_evd_dealloc
*
* Free the passed in EVD structure. If an error occurs, this function
* will clean up all of the internal data structures and report the
* error.
*
* Input:
* evd_ptr
*
* Output:
* none
*
* Returns:
* status
*
*/
{
/*
* Destroy the CQ first, to keep any more callbacks from coming
* up from it.
*/
if (dat_status != DAT_SUCCESS) {
goto bail;
}
}
/*
* We should now be safe to invalidate the EVD; reset the
* magic to prevent reuse.
*/
/* Release reference on the CNO if it exists */
}
/*
* If the ring buffer allocation failed, then the dapls_rbuf_destroy
* function will detect that the ring buffer's internal data (ex. base
* pointer) are invalid and will handle the situation appropriately
*/
}
bail:
return (dat_status);
}
/*
* dapli_evd_eh_print_cqe
*
* Input:
* cqe
*
* Output:
* none
*
* Prints out a CQE for debug purposes
*
*/
#ifdef DAPL_DBG /* For debugging. */
void
{
static char *optable[] = {
"",
"OP_SEND",
"OP_RDMA_READ",
"OP_RDMA_WRITE",
"OP_COMP_AND_SWAP",
"OP_FETCH_AND_ADD",
"OP_BIND_MW",
"OP_RECEIVE",
"OP_RECEIVE_RDMAWI",
0
};
"\t >>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<\n");
"\t dapl_evd_dto_callback : CQE \n");
} else {
}
"\t >>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<\n");
}
#endif
/*
* Event posting code follows.
*/
/*
* These next two functions (dapli_evd_get_event and dapli_evd_post_event)
* are a pair. They are always called together, from one of the functions
* at the end of this file (dapl_evd_post_*_event).
*
* Note that if producer side locking is enabled, the first one takes the
* EVD lock and the second releases it.
*/
/*
* dapli_evd_get_event
*
* Get an event struct from the evd. The caller should fill in the event
* and call dapl_evd_post_event.
*
* If there are no events available, an overflow event is generated to the
* async EVD handler.
*
* If this EVD required producer locking, a successful return implies
* that the lock is held.
*
* Input:
* evd_ptr
*
* Output:
* event
*
*/
static DAT_EVENT *
{
if (evd_ptr->evd_producer_locking_needed) {
}
/* Release the lock if it was taken and the call failed. */
}
return (event);
}
/*
* dapli_evd_post_event
*
* Post the <event> to the evd. If possible, invoke the evd's CNO.
* Otherwise post the event on the pending queue.
*
* If producer side locking is required, the EVD lock must be held upon
* entry to this function.
*
* Input:
* evd_ptr
* event
*
* Output:
* none
*
*/
static void
{
"dapli_evd_post_event: Called with event # %x\n",
(void *)event_ptr);
/* No waiter. Arrange to trigger a CNO if it exists. */
if (evd_ptr->evd_enabled) {
}
if (evd_ptr->evd_producer_locking_needed) {
}
} else {
/*
* This routine gets called
* - In the context of the waiting thread when CQ, CM or ASYNC
* events need to be put on to the EVD ring buffer.
* - Due to a post of a software event.
*
* In the first case the waiting thread is pulling the events
* from various streams into the evd so there is no need to
* wake any thread. In the second case if the evd is in waited
* state then we need to wakeup the waiting thread.
*/
/*
* We're in DAPL_EVD_STATE_WAITED. Take the lock if
* we don't have it, recheck, and signal.
*/
if (!evd_ptr->evd_producer_locking_needed) {
}
(void) dapls_ib_event_wakeup(evd_ptr);
} else {
}
} else {
if (evd_ptr->evd_producer_locking_needed) {
}
}
}
if (cno_to_trigger != NULL) {
}
}
/*
* dapli_evd_post_event_nosignal
*
* Post the <event> to the evd. Do not do any wakeup processing.
* This function should only be called if it is known that there are
* no waiters that it is appropriate to wakeup on this EVD. An example
* of such a situation is during internal dat_evd_wait() processing.
*
* If producer side locking is required, the EVD lock must be held upon
* entry to this function.
*
* Input:
* evd_ptr
* event
*
* Output:
* none
*
*/
static void
{
"dapli_evd_post_event: Called with event # %x\n",
(void *)event_ptr);
if (evd_ptr->evd_producer_locking_needed) {
}
}
/*
* dapli_evd_format_overflow_event
*
* format an overflow event for posting
*
* Input:
* evd_ptr
* event_ptr
*
* Output:
* none
*
*/
static void
{
}
/*
* dapli_evd_post_overflow_event
*
* post an overflow event
*
* Input:
* async_evd_ptr
* evd_ptr
*
* Output:
* none
*
*/
static void
{
/*
* The overflow_evd_ptr mght be the same as evd.
* In that case we've got a catastrophic overflow.
*/
if (async_evd_ptr == overflow_evd_ptr) {
return;
}
if (!overflow_event) {
/* this is not good */
return;
}
}
static DAT_EVENT *
{
} else {
}
return (event_ptr);
}
{
/*
* Note event lock may be held on successful return
* to be released by dapli_evd_post_event(), if provider side locking
* is needed.
*/
if (!event_ptr) {
return (DAT_INSUFFICIENT_RESOURCES | DAT_RESOURCE_MEMORY);
}
return (DAT_SUCCESS);
}
{
/*
* Note event lock may be held on successful return
* to be released by dapli_evd_post_event(), if provider side locking
* is needed.
*/
if (!event_ptr) {
return (DAT_INSUFFICIENT_RESOURCES | DAT_RESOURCE_MEMORY);
}
return (DAT_SUCCESS);
}
{
/*
* Note event lock may be held on successful return
* to be released by dapli_evd_post_event(), if provider side locking
* is needed.
*/
if (!event_ptr) {
return (DAT_INSUFFICIENT_RESOURCES | DAT_RESOURCE_MEMORY);
}
return (DAT_SUCCESS);
}
{
/*
* Note event lock may be held on successful return
* to be released by dapli_evd_post_event(), if provider side locking
* is needed.
*/
if (!event_ptr) {
return (DAT_QUEUE_FULL);
}
return (DAT_SUCCESS);
}
void
{
int prm_idx;
int nevents;
int i;
/* premature events are always recv events */
i = 0;
prm_idx = 0;
while (i < nevents) {
/*
* If srq_attached, premature events cannot exceed max_recv_dtos
*/
/*
* The SRQ premature event list could potentially have
* holes (ie. free entries in the middle) or premature
* events for other QPs. These need to be skipped.
*/
if (ep_ptr->srq_attached &&
prm_idx++;
continue;
}
" Premature DTO processing\n");
#ifdef DAPL_DBG /* For debugging. */
#endif
/*
* Can use DAT_DTO_COMPLETION_EVENT because
* dapli_evd_cqe_to_event will overwrite.
*/
/* We've already attempted the overflow post, return */
return;
}
event);
/*
* For SRQ attached QPs recycle the premature event
*/
if (ep_ptr->srq_attached) {
prm_idx++;
}
i++;
}
}
/*
* dapli_evd_cqe_to_event
*
* Convert a CQE into an event structure.
*
* Input:
* evd_ptr
* cqe_ptr
*
* Output:
* event_ptr
*
* Returns:
* none
*
*/
static DAT_BOOLEAN
{
int srq_enabled;
int dto_error = 0;
/*
* All that can be relied on if the status is bad is the status
* and WRID.
*/
srq_enabled = 0;
} else {
srq_enabled = 1;
}
/*
* Check if the DTO completion arrived before CONNECTION_ESTABLISHED
* event -
*
* Send DTOs can occur only if ep state is CONNECTED/DISCONNECTED
* therefore it cannot occur before connection established event.
* Receive DTO can potentially complete before connection established
* event has been delivered to the client. In this case if the
* ep state is ACTIVE_CONNECTION_PENDING (active side) or
* COMPLETION_PENDING (passive side) the event is put in a special
* event queue in the qp_handle.
*
*/
if (!process_premature_events &&
(ib_status == IB_COMP_ST_SUCCESS)) {
if ((ep_state == DAT_EP_STATE_ACTIVE_CONNECTION_PENDING) ||
(qp->qp_num_premature_events > 0)) {
/*
* not yet ready to put the event in the evd ring
* buffer
*/
return (DAT_FALSE);
}
}
case DAPL_COOKIE_TYPE_DTO:
{
if (srq_enabled) {
} else {
}
} else {
}
switch (ib_status) {
case IB_COMP_ST_SUCCESS:
{
(ibtype == OP_RECEIVE &&
(ibtype == OP_RDMA_WRITE &&
(ibtype == OP_RDMA_READ &&
break;
}
case IB_COMP_ST_LOCAL_LEN_ERR:
{
break;
}
{
break;
}
{
break;
}
{
break;
}
case IB_COMP_ST_REM_REQ_ERR:
case IB_COMP_ST_REM_OP_ERR:
{
break;
}
case IB_COMP_ST_REM_ACC_ERR:
{
break;
}
/*
* Unsupported RD errors
* case IB_COMP_ST_EE_STATE_ERR:
* case IB_COMP_ST_EE_CTX_NO_ERR:
*/
{
break;
}
case IB_COMP_ST_RNR_COUNTER:
{
break;
}
case IB_COMP_ST_MW_BIND_ERR:
{
break;
}
case IB_COMP_ST_LOCAL_OP_ERR:
{
break;
}
default:
{
" DTO completion ERROR: %d: op %#x\n",
break;
}
}
/* Most error DTO ops result in disconnecting the EP */
DAT_DTO_SUCCESS) &&
dto_error = 1;
" DTO completion ERROR: %d: op %#x\n",
}
/* Get size from DTO; CQE value may be off. */
} else {
}
break;
}
case DAPL_COOKIE_TYPE_RMR:
{
if (ib_status == IB_COMP_ST_SUCCESS) {
} else {
dto_error = 1;
}
break;
}
default:
{
dapl_os_assert(!"Invalid Operation type");
break;
}
}
/*
* A DTO failed this will cause the connection to be broken
*/
/*
* Disconnect at the IB level.
*/
}
/* convert premature rec to error flush on disconnect */
}
return (DAT_TRUE);
}
/*
* dapls_evd_copy_cq
*
* Copy all entries on a CQ associated with the EVD onto that EVD
* Up to caller to handle races, if any. Note that no EVD waiters will
* be awoken by this copy.
*
* Input:
* evd_ptr
*
* Output:
* nevents
*
* Returns:
* none
*
*/
void
int *nevents)
{
int cqe_events;
int i;
*nevents = 0;
if (cq_handle == IB_INVALID_HANDLE) {
/* Nothing to do if no CQ. */
return;
}
if (dat_status == DAT_SUCCESS) {
cqe_events = 0;
for (i = 0; i < num_cqes_polled; i++) {
#ifdef DAPL_DBG /* For debugging. */
#endif
/*
* Can use DAT_DTO_COMPLETION_EVENT because
* dapli_evd_cqe_to_event will overwrite.
*/
/*
* We've already attempted the overflow post; return.
*/
return;
}
event)) {
cqe_events++;
} else {
"dapls_evd_copy_cq: premature event\n");
/*
* We've deferred processing the CQE, so add
* the event_ptr back to free queue
*/
free_event_queue, (void *)event);
if (evd_ptr->evd_producer_locking_needed) {
}
}
}
*nevents = cqe_events;
"dapls_evd_copy_cq: dapls_ib_completion_poll "
"returned 0x%x\n", dat_status);
dapl_os_assert(!"Bad return from dapls_ib_completion_poll");
}
}
/*
* dapls_evd_copy_events
*
* Copy all events associated with the EVD onto that EVD
*
* Input:
* evd_ptr
* timeout
*
* Output:
* return status
*
* Returns:
* none
*
*/
{
int waited;
int events_needed = 0;
int nevents = 0;
int num_cqe = 0;
int i;
/* rbuf count is zero on entry */
} else {
/* need to allocate on the heap */
return (DAT_INSUFFICIENT_RESOURCES);
}
}
evpp_start = evpp;
/* for evd_dequeue, check for ke before returning Q_EMPTY */
} else {
evpp_start = NULL;
}
waited = 0;
/* calculate various time wait elements */
if (timeout == 0) {
final_time = 0;
time_left = 0;
} else if (timeout == DAT_TIMEOUT_INFINITE) {
/*
* The real value of DAT_TIMEOUT_INFINITE is fairly small
* ~71 mins, to prevent premature timeouts map it to
* 1 year. NOTE: 64-bit integers are needed here
* because 32 bits is not enough. Other types,
* such as clock_t are not 64-bit, so are not
* sufficient for this. Similarly, hrtime_t is
* defined as a "nanosecond counter", which does not
* match our need for time in microseconds, so we
* just use the more general uint64_t here.
*/
} else {
/*
* maximum time by which the routine needs to return
* DAT_TIMEOUT_INFINITE is defined as ~0 but its of type int
* so mask the MSB to avoid overflow
*/
}
do {
/*
* If this evd has a CQ event stream check the CQs first
*/
/*
* Poll CQ for events, update the total number of CQEs
* so far
*/
nevents = 0;
"dapls_evd_copy_event: copy_cq num_cqe(%d)\n",
num_cqe);
}
/*
* We use the dapls_rbuf_count since it includes
* - CQ events pulled by dapls_evd_copy_cq
* - events added by dat_evd_post_se()
*/
/*
* check for pending events
* note: threshold=0 implies dapl_evd_dequeue
*/
if (events_needed < 0) {
/* There are more than sufficient events */
break;
} else if (events_needed == 0) {
/* report queue empty on dat_evd_dequeue */
/* non CQ events are expected to be polled */
/* by dat_evd_wait */
/*
* when threshold > 0, we have sufficient events
*/
break;
} else {
/*
* when we reach here, this implies dat_evd_wait
* return on any dto completion as
* threshold > 1 will be taken as hint only
*/
if (num_cqe)
break;
}
/* check we've already waited */
if (waited > 0) {
"dapls_evd_copy_event: waited[%d]\n", waited);
if (dat_status != DAT_SUCCESS)
break;
/* exit on time expired */
if (curr_time >= final_time)
break;
}
/* check for DTO type evd's */
if (events_needed == 1) {
/*
* Need only one event so enable cq
* notification
*/
/*
* XXX: Things need to be modified here to
* implement the NOTIFICATION suppression
* correctly - relies on THRESHOLD flag
* and UNSIGNALLED flag to be stored
* in the evd.
*/
evd_ptr);
if (dat_status != DAT_SUCCESS) {
"dapls_evd_copy_event:"
" set_cq_notify(%d)\n", dat_status);
return (dat_status);
}
} else if (events_needed > 1) {
/*
* We need multiple events so lets enable CQ for
* notification on N events.
* dat_status = dapls_set_cqN_notify(ia_ptr,
* evd_ptr, (uint32_t)events_needed);
*/
evd_ptr);
if (dat_status != DAT_SUCCESS) {
"dapls_evd_copy_event:"
" set_cqN_notify:%d\n", dat_status);
return (dat_status);
}
}
/*
* Per Tavor PRM if completions occur after polling
* the CQ and before arming it, upon arming the CQ
* handler will be immediately fired. Hence it
* recommends that a re-poll of the CQ can be skipped
* as an optimization.
*/
}
nevents = 0;
/*
* non-NULL evpp_start denotes either
* DAT_EVD_CONNECTION_FLAG, DAT_EVD_CR_FLAG, DAT_EVD_ASYNC_FLAG
* is set and thus needs to check events from kernel
*/
if (evpp_start) {
/*
* Even if dat_status is not DAT_SUCCESS, num_events
* could be non-zero.
*/
&nevents);
"dapls_evd_copy_event: poll returned 0x%x(%d)\n",
} else {
/* perform a timewait */
"dapls_evd_copy_event: poll(cq_notification) "
"returned 0x%x\n", dat_status);
return (dat_status);
}
waited++;
/* process the cm events now */
for (i = 0; i < num_ke; i++) {
switch (evpp_start[i].ibe_ev_family) {
case DAPL_CR_EVENTS: /* PASSIVE side events */
"dapls_evd_copy_event: Passive side Event %d\n",
break;
case DAPL_ACTIVE_CONNECTION_EVENTS: /* ACTIVE side events */
"dapls_evd_copy_event: Active Conn Event %d\n",
break;
case DAPL_ASYNC_EVENTS:
"dapls_evd_copy_event: Async Event %d\n",
break;
default:
"dapls_evd_copy_event: dapls_ib_event_poll %d "
dapl_os_assert(!"Bad return from dapls_ib_event_poll");
break;
}
}
return (dat_status);
}
/*
* dapls_evd_cq_poll_to_event
*
* Attempt to dequeue a single CQE from a CQ and turn it into
* an event.
*
* Input:
* evd_ptr
*
* Output:
* event
*
* Returns:
* Status of operation
*
*/
{
/* skip one layer of do-nothing function */
if (dat_status == DAT_SUCCESS) {
#ifdef DAPL_DBG /* For debugging. */
#endif
event);
}
return (dat_status);
}
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 8
* End:
*/