rdsib_ep.c revision c1f8b08e52d9b30bd55daeac694e3a7f50d3cd21
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2005 SilverStorm Technologies, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/*
* Sun elects to include this software in Sun product
* under the OpenIB BSD license.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#define RDS_POLL_CQ_IN_2TICKS 1
/*
* This File contains the endpoint related calls
*/
extern uint_t rds_wc_signal;
static uint8_t
{
} else {
}
return (ret);
}
static uint8_t
{
if (!ret) {
/* port is not marked, mark it */
}
} else {
if (!ret) {
/* port is not marked, mark it */
}
}
return (ret);
}
static uint8_t
{
if (ret) {
/* port is marked, unmark it */
}
} else {
if (ret) {
/* port is marked, unmark it */
}
}
return (ret);
}
static void
{
} else {
}
}
static void
{
} else {
}
}
static void
{
if (!locked) {
}
if (!locked) {
}
}
/* Session lookup based on destination IP or destination node guid */
{
while (sp) {
break;
}
}
return (sp);
}
static void
{
/* free send pool */
/* free recv pool */
}
/* Assumes SP write lock is held */
int
{
/* send pool */
if (ret != 0) {
return (-1);
}
/* recv pool */
if (ret != 0) {
return (-1);
}
/* reset the ep state */
/* Initialize the WR to send acknowledgements */
return (0);
}
static int
{
int ret;
/* Re-initialize send pool */
if (ret != 0) {
RDS_DPRINTF2("rds_ep_reinit",
return (-1);
}
/* free all the receive buffers in the pool */
return (0);
}
void
{
}
/*
* Allocate and initialize the resources needed for the control and
* data channels
*/
int
{
int ret;
/* CALLED WITH SESSION WRITE LOCK */
/* allocate and initialize the ctrl channel */
if (ret != 0) {
return (-1);
}
/* allocate and initialize the data channel */
if (ret != 0) {
return (-1);
}
return (0);
}
/*
* This should be called before moving a session from ERROR state to
* INIT state. This will update the HCA keys incase the session has moved from
* one HCA to another.
*/
int
{
int ret;
/* CALLED WITH SESSION WRITE LOCK */
return (-1);
}
/*
* No action is needed as the session did not move across
* HCAs
*/
return (0);
}
/* re-initialize the control channel */
if (ret != 0) {
RDS_DPRINTF2("rds_session_reinit",
"SP(%p): Ctrl EP(%p) re-initialization failed",
return (-1);
}
/* re-initialize the data channel */
if (ret != 0) {
RDS_DPRINTF2("rds_session_reinit",
"SP(%p): Data EP(%p) re-initialization failed",
return (-1);
}
return (0);
}
static int
{
int ret;
/* get paths to the destination */
if (ret != IBT_SUCCESS) {
return (-1);
}
/* Override the packet life time based on the conf file */
if (IBPktLifeTime != 0) {
}
/* Session type may change if we run into peer-to-peer case. */
"active end", sp);
return (0); /* return success */
}
/* connect the data ep first */
if (ret != IBT_SUCCESS) {
return (-1);
}
} else {
return (-1);
}
if (ret != IBT_SUCCESS) {
return (-1);
}
} else {
return (-1);
}
return (0);
}
/*
* Can be called with or without session_lock.
*/
void
{
sp->session_state);
/* wait until the SQ is empty before closing */
}
}
/* wait until the SQ is empty before closing */
}
}
}
/* Free the session */
static void
{
sp->session_state);
}
/* data channel */
/* send pool locks */
/* recv pool locks */
/* control channel */
/* send pool locks */
/* recv pool locks */
/* session */
/* free the session */
}
/* This is called on the taskq thread */
static void
rds_failover_session(void *arg)
{
/*
* The remote side must have seen the error and initiated
* a re-connect.
*/
RDS_DPRINTF2("rds_failover_session",
"SP(%p) has become passive", sp);
return;
}
sp->session_failover++;
/*
* The session is in ERROR state but close both channels
* for a clean start.
*/
/* wait 1 sec before re-connecting */
do {
/* The ipaddr should be in the network order */
if (ret == 0) {
}
/* check if we have (new) path from the source to destination */
if (ret == 0) {
break;
}
/* wait 1 sec before re-trying */
cnt++;
} while (cnt < 3);
if (ret != 0) {
RDS_DPRINTF3("rds_failover_session",
"SP(%p) State RDS_SESSION_STATE_FAILED", sp);
} else {
RDS_DPRINTF2("rds_failover_session",
"SP(%p) has become passive", sp);
}
return;
}
/*
* The remote side must have seen the error and initiated
* a re-connect.
*/
RDS_DPRINTF2("rds_failover_session",
"SP(%p) has become passive", sp);
return;
}
/* move the session to init state */
if (ret != 0) {
RDS_DPRINTF3("rds_failover_session",
"SP(%p) State RDS_SESSION_STATE_FAILED", sp);
return;
} else {
RDS_DPRINTF3("rds_failover_session",
"SP(%p) State RDS_SESSION_STATE_INIT", sp);
}
}
void
{
if (rds_is_sendq_empty(ep, 0)) {
/* Session should already be in ERROR, try to reconnect */
RDS_DPRINTF2("rds_handle_send_error",
}
}
/*
* Called in the CM handler on the passive side
* Called on a taskq thread.
*/
void
rds_cleanup_passive_session(void *arg)
{
sp->session_state);
RDS_DPRINTF3("rds_cleanup_passive_session",
"SP(%p) State RDS_SESSION_STATE_FINI", sp);
RDS_DPRINTF3("rds_cleanup_passive_session",
"SP(%p) State RDS_SESSION_STATE_FAILED", sp);
}
}
/*
* Called by the CM handler on the passive side
* Called with WRITE lock on the session
*/
void
{
sp->session_state);
/* clean the data channel */
/* clean the control channel */
}
/*
* Can be called:
* 1. on driver detach
* 2. on taskq thread
* arg is always NULL
*/
/* ARGSUSED */
void
rds_close_sessions(void *arg)
{
/* wait until all the buffers are freed by the sockets */
while (RDS_GET_RXPKTS_PEND() != 0) {
/* wait one second and try again */
"pending packets", RDS_GET_RXPKTS_PEND());
}
/* close all the sessions */
while (sp) {
sp->session_state);
switch (sp->session_state) {
RDS_DPRINTF3("rds_close_sessions",
"SP(%p) State RDS_SESSION_STATE_CLOSED", sp);
RDS_DPRINTF3("rds_close_sessions",
"SP(%p) State RDS_SESSION_STATE_FINI", sp);
break;
case RDS_SESSION_STATE_ERROR:
case RDS_SESSION_STATE_INIT:
RDS_DPRINTF3("rds_close_sessions",
"SP(%p) State RDS_SESSION_STATE_CLOSED", sp);
/* FALLTHRU */
case RDS_SESSION_STATE_CLOSED:
RDS_DPRINTF3("rds_close_sessions",
"SP(%p) State RDS_SESSION_STATE_FINI", sp);
break;
}
}
rdsib_statep->rds_nsessions = 0;
while (sp) {
}
/* free the global pool */
}
void
{
int ret;
if (ret == -1) {
/*
* may be the session has become passive due to
* hitting peer-to-peer case
*/
"has become passive from active", sp);
return;
}
/* get the lock for writing */
RDS_DPRINTF3("rds_session_open",
"SP(%p) State RDS_SESSION_STATE_ERROR", sp);
/* Connect request failed */
RDS_DPRINTF3("rds_session_open",
"SP(%p) State RDS_SESSION_STATE_FAILED", sp);
return;
}
}
/*
* Creates a session and inserts it into the list of sessions. The session
* state would be CREATED.
* Return Values:
* EWOULDBLOCK
*/
{
int ret;
/* Allocate and initialize global buffer pool */
if (ret != 0) {
return (NULL);
}
/* enough memory for session (includes 2 endpoints) */
RDS_DPRINTF3("rds_session_create",
"SP(%p) State RDS_SESSION_STATE_CREATED", newp);
/* Initialize data endpoint */
/* Initialize send pool locks */
/* Initialize recv pool locks */
/* Initialize control endpoint */
/* Initialize send pool locks */
/* Initialize recv pool locks */
/* lkup if there is already a session */
/* A session to this destination exists */
return (NULL);
}
/* Insert this session into the list */
/* unlock the session list */
if (type == RDS_SESSION_ACTIVE) {
/* The ipaddr should be in the network order */
if (ret == 0) {
}
/* Get the gids for the source and destination ip addrs */
if (ret != 0) {
return (NULL);
}
}
/* check for peer-to-peer case */
/* no peer-to-peer case */
if (type == RDS_SESSION_ACTIVE) {
} else {
/* rgid is requester gid & lgid is receiver gid */
}
RDS_DPRINTF3("rds_session_create",
"SP(%p) State RDS_SESSION_STATE_FAILED", newp);
return (NULL);
}
}
return (newp);
}
void
{
case RDS_CTRL_CODE_STALL:
break;
case RDS_CTRL_CODE_UNSTALL:
break;
break;
break;
case RDS_CTRL_CODE_HEARTBEAT:
break;
default:
break;
}
}
void
{
int ret;
return;
}
if (ret != IBT_SUCCESS) {
return;
}
}
void
rds_send_control_message(void *arg)
{
/* send the stall message on all sessions */
}
}
/* free the arg */
}
void
{
"to send control message: Code: %d "
return;
}
#if 0
/*
* Taskq runs at some later point in time and the port may
* not be in stall state anymore at that time.
*/
(void) ddi_taskq_dispatch(rds_taskq,
#else
rds_send_control_message((void *)bp);
#endif
} else {
"Port %d is already in stall state", port);
}
}
void
{
"to send control message: Code: %d "
return;
}
/* send control message to resume the port for remote traffic */
(void) ddi_taskq_dispatch(rds_taskq,
} else {
"Port %d is not stalled anymore", port);
}
}
static int
{
int ret;
/* how many pkts are needed to carry this msg */
/* Get the buffers needed to post this message */
return (ENOBUFS);
}
if (npkts > 1) {
/*
* multi-pkt messages are posted at the same time as a list
* of WRs
*/
}
pktno = 0;
do {
/* prepare the header */
/* copy the data */
if (ret != 0) {
break;
}
break;
}
pktno++;
if (ret) {
/* uiomove failed */
if (npkts > 1) {
}
return (ret);
}
if (npkts > 1) {
/* multi-pkt message */
}
if (ret != IBT_SUCCESS) {
return (ret);
}
} else {
/* single pkt */
if (ret != IBT_SUCCESS) {
return (ret);
}
}
return (0);
}
static int
{
int ret;
return (ENOSPC);
}
if (ret) {
return (ret);
}
zoneid);
if (ret != 0) {
/*
* The message is delivered but cannot take more,
* stall the port, if it is not already stalled
*/
} else {
return (ret);
}
}
return (0);
}
static void
rds_resend_messages(void *arg)
{
int ret;
RDS_DPRINTF2("rds_resend_messages",
"SP(%p) Remote session is cleaned up ", sp);
/*
* The remote end cleaned up its session. There may be loss
* of messages. Mark all buffers as acknowledged.
*/
} else {
RDS_DPRINTF2("rds_resend_messages",
}
jx = 0;
jx++;
}
"found in the list", tmp);
} else {
}
sp->session_failover--;
return;
}
/* Found the match */
jx++;
if (bp) {
KM_SLEEP);
while (nwr) {
}
if (ret != IBT_SUCCESS) {
break;
}
}
if (nwr != 0) {
/*
* An error while failover is in progress. Some WRs are
* posted while other remain. If any of the posted WRs
* complete in error then they would dispatch a taskq to
* do a failover. Getting the session lock will prevent
* the taskq to wait until we are done here.
*/
/*
* Wait until all the previous WRs are completed and
* then queue the remaining, otherwise the order of
* the messages may change.
*/
/* free the remaining buffers */
return;
}
}
} else {
}
sp->session_failover--;
}
/*
* This is called when a channel is connected. Transition the session to
* CONNECTED state iff both channels are connected.
*/
void
{
/*
* we establish the data channel first, so check the control channel
* first but make sure it is initialized.
*/
/* the session is not ready yet */
return;
}
/* control channel is connected, check the data channel */
/* data channel is not yet connected */
return;
}
if (failover) {
/*
* The session has failed over. Previous msgs have to be
* re-sent before the session is moved to the connected
* state.
*/
"to re-send messages", sp);
(void) ddi_taskq_dispatch(rds_taskq,
return;
}
/* the session is ready */
RDS_DPRINTF3("rds_session_active",
"SP(%p) State RDS_SESSION_STATE_CONNECTED", sp);
}
static int
{
int ret;
/* make sure the port is not stalled */
} else {
}
return (ret);
}
/* Send a message to a destination socket */
int
{
int ret;
/* If msg length is 0, just return success */
return (0);
}
/* Is there a session to the destination? */
/* Is this a loopback message? */
/* make sure the port is not stalled */
recvport);
return (ENOMEM);
}
return (ret);
}
/* Not a loopback message */
/* There is no session to the destination, create one. */
"IP: 0x%x", recvip);
if (ret != 0) {
RDS_DPRINTF2("rds_sendmsg",
"SP(%p): rds_session_init failed",
sp);
sp->session_state =
RDS_DPRINTF3("rds_sendmsg",
"SP(%p) State "
"RDS_SESSION_STATE_FAILED", sp);
return (EFAULT);
}
RDS_DPRINTF3("rds_sendmsg",
"SP(%p) State "
"RDS_SESSION_STATE_INIT", sp);
} else {
}
} else {
/* Is a session created for this destination */
return (EFAULT);
}
}
}
/* There is a session to the destination */
recvport);
return (ret);
"%d", sp);
"RDS_SESSION_STATE_CREATED", sp);
/* The ipaddr should be in the network order */
if (ret == 0) {
}
/* Resolve the IP addresses */
if (ret != 0) {
ret);
sp->session_state =
RDS_DPRINTF3("rds_sendmsg",
"SP(%p) State "
"RDS_SESSION_STATE_FAILED", sp);
return (EFAULT);
} else {
return (ENOMEM);
}
}
"an uninitialized HCA: %llx",
sp->session_state =
RDS_DPRINTF3("rds_sendmsg",
"SP(%p) State "
"RDS_SESSION_STATE_FAILED", sp);
return (ENOMEM);
}
if (ret != 0) {
RDS_DPRINTF2("rds_sendmsg",
"SP(%p): rds_session_init failed",
sp);
sp->session_state =
RDS_DPRINTF3("rds_sendmsg",
"SP(%p) State "
"RDS_SESSION_STATE_FAILED", sp);
return (EFAULT);
}
} else {
return (ENOMEM);
}
} else {
return (ENOMEM);
}
} else {
return (ENOMEM);
}
recvport);
} else {
}
return (ret);
}
/* Note: This is called on the CQ handler thread */
void
{
int ret;
/* increment rx pending here */
/* this will get freed by sockfs */
return;
}
return;
}
}
/* store the last buffer id, no lock needed */
if (npkts > 1) {
} else {
}
if (ret != 0) {
/*
* The message is delivered but cannot take more,
* stall the port
*/
pktp->dh_recvport);
} else {
ret);
}
}
if (ep->ep_rdmacnt == 0) {
ep->ep_rdmacnt++;
/* send acknowledgement */
if (ret != IBT_SUCCESS) {
"acknowledgement failed: %d, SQ depth: %d",
ep->ep_rdmacnt--;
}
} else {
/* no room to send acknowledgement */
}
}