rdsib_ib.c revision 03494a9880d80f834bec10a1e8f0a2f8f7c97bf4
/*
* 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 2009 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.
*/
static struct ibt_clnt_modinfo_s rds_ib_modinfo = {
NULL,
"RDS"
};
/* performance tunables */
uint_t rds_no_interrupts = 0;
extern dev_info_t *rdsib_dev_info;
extern void rds_close_sessions();
static void
{
/* The SQ size should not be more than that supported by the HCA */
"than that supported by the HCA driver "
"(%d + %d > %d or %d), lowering it to a supported value.",
hattrp->hca_max_cq_sz) ?
}
/* The RQ size should not be more than that supported by the HCA */
"supported by the HCA driver (%d > %d or %d), lowering it "
"to a supported value.", MaxDataRecvBuffers,
}
/* The SQ size should not be more than that supported by the HCA */
"supported by the HCA driver (%d > %d or %d), lowering it "
"to a supported value.", MaxCtrlSendBuffers,
}
/* The RQ size should not be more than that supported by the HCA */
"supported by the HCA driver (%d > %d or %d), lowering it "
"to a supported value.", MaxCtrlRecvBuffers,
}
/* The MaxRecvMemory should be less than that supported by the HCA */
"supported by the HCA driver (%d > %d), lowering it to %d",
}
}
/* Return hcap, given the hca guid */
{
}
return (hcap);
}
static rds_hca_t *
{
int ret;
/* Do a HCA lookup */
/*
* This can happen if we get IBT_HCA_ATTACH_EVENT on an HCA
* that we have already opened. Just return NULL so that
* we'll not end up reinitializing the HCA again.
*/
return (NULL);
}
}
if (ret != IBT_SUCCESS) {
if (ret == IBT_HCA_IN_USE) {
RDS_DPRINTF2("rdsib_init_hca",
"ibt_open_hca: 0x%llx returned IBT_HCA_IN_USE",
hca_guid);
} else {
RDS_DPRINTF2("rdsib_init_hca",
}
}
return (NULL);
}
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rdsib_init_hca",
} else {
}
return (NULL);
}
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rdsib_init_hca",
"Query HCA 0x%llx ports failed: %d", hca_guid,
ret);
} else {
}
return (NULL);
}
/* Only one PD per HCA is allocated, so do it here */
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rdsib_init_hca",
hcap->hca_pinfo_sz);
} else {
}
return (NULL);
}
/* this is a new HCA, add it to the list */
}
return (hcap);
}
/*
* Called from attach
*/
int
{
int ret;
if (rdsib_statep == NULL) {
RDS_DPRINTF1("rdsib_initialize_ib",
"RDS Statep not initialized");
return (-1);
}
/* How many hcas are there? */
if (nhcas == 0) {
return (-1);
}
/* Register with IBTF */
if (ret != IBT_SUCCESS) {
ret);
return (-1);
}
/*
* Open each HCA and gather its information. Don't care about HCAs
* that cannot be opened. It is OK as long as atleast one HCA can be
* opened.
* Initialize a HCA only if all the information is available.
*/
}
/* free the HCA list, we are done with it */
if (hcaix == 0) {
/* Failed to Initialize even one HCA */
return (-1);
}
}
return (0);
}
/*
* Called from detach
*/
void
{
int ret;
/* close and destroy all the sessions */
/* Release all HCA resources */
rdsib_statep->rds_nhcas = 0;
hcap->hca_pinfo_sz);
}
}
/* Deregister with IBTF */
}
}
/*
* Called on open of first RDS socket
*/
int
{
int ret;
/* Enable incoming connection requests */
RDS_DPRINTF2("rdsib_open_ib",
"Service registration failed");
return (-1);
} else {
/* bind the service on all available ports */
if (ret != 0) {
RDS_DPRINTF2("rdsib_open_ib",
"Bind service failed: %d", ret);
}
}
}
return (0);
}
/*
* Called when all ports are closed.
*/
void
{
int ret;
/* Disable incoming connection requests */
if (ret != 0) {
RDS_DPRINTF2("rdsib_close_ib",
"ibt_unbind_all_services failed: %d\n", ret);
}
if (ret != 0) {
RDS_DPRINTF2("rdsib_close_ib",
"ibt_deregister_service failed: %d\n", ret);
} else {
}
}
}
/* Return hcap, given the hca guid */
{
}
/*
* don't let anyone use this HCA until the RECV memory
* is registered with this HCA
*/
return (hcap);
}
RDS_DPRINTF2("rds_get_hcap",
return (NULL);
}
/* Return hcap, given a gid */
{
/*
* don't let anyone use this HCA until the RECV memory
* is registered with this HCA
*/
RDS_DPRINTF3("rds_gid_to_hcap",
"HCA (0x%p, 0x%llx) is not initialized",
continue;
}
gid.gid_prefix) &&
RDS_DPRINTF4("rds_gid_to_hcap",
"gid found in hcap: 0x%p", hcap);
return (hcap);
}
}
}
return (NULL);
}
/* This is called from the send CQ handler */
void
{
int ret;
/*
* The previous ACK completed successfully, send the next one
* if more messages were received after sending the last ACK
*/
/* send acknowledgement */
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rds_send_acknowledgement",
"EP(%p): ibt_post_send for acknowledgement "
"failed: %d, SQ depth: %d",
ep->ep_rdmacnt--;
}
} else {
/* ACKed all messages, no more to ACK */
ep->ep_rdmacnt--;
return;
}
}
static int
{
int ret = IBT_SUCCESS;
if (ret != IBT_SUCCESS) {
if (ret != IBT_CQ_EMPTY) {
} else {
}
return (ret);
}
/* Free the buffer */
/* Receive completion failure */
RDS_DPRINTF2("rds_poll_ctrl_completions",
"EP(%p) CQ(%p) BP(%p): WC Error Status: %d",
}
return (ret);
}
/* there is one less in the RQ */
/* Time to post more buffers into the RQ */
if (ret != DDI_SUCCESS) {
ret);
}
} else {
}
return (ret);
}
#define RDS_POST_FEW_ATATIME 100
/* Post recv WRs into the RQ. Assumes the ep->refcnt is already incremented */
void
rds_post_recv_buf(void *arg)
{
int ret;
/* get the hcap for the HCA hosting this channel */
ep->ep_hca_guid);
return;
}
/* Make sure the session is still connected */
return;
}
/* how many can be posted */
if (nspace == 0) {
return;
}
} else {
}
/* try again later */
if (ret != DDI_SUCCESS) {
ret);
}
return;
}
}
}
#if 0
KM_SLEEP);
#else
#endif
while (npost) {
}
break;
}
}
if (npost != 0) {
RDS_DPRINTF2("rds_post_recv_buf",
} else {
}
/*
* sometimes, the recv WRs can get consumed as soon as they are
* posted. In that case, taskq thread to post more WRs to the RQ will
* not be scheduled as the taskqpending flag is still set.
*/
if (ret != DDI_SUCCESS) {
RDS_DPRINTF2("rds_post_recv_buf",
"ddi_taskq_dispatch failed: %d", ret);
}
} else {
}
#if 0
#endif
}
static int
{
int ret = IBT_SUCCESS;
if (ret != IBT_SUCCESS) {
if (ret != IBT_CQ_EMPTY) {
} else {
}
return (ret);
}
/* free the buffer */
/* Receive completion failure */
RDS_DPRINTF2("rds_poll_data_completions",
"EP(%p) CQ(%p) BP(%p): WC Error Status: %d",
}
return (ret);
}
/* there is one less in the RQ */
/* Time to post more buffers into the RQ */
if (ret != DDI_SUCCESS) {
ret);
}
} else {
}
/* single pkt or last packet */
/* last packet of a segmented message */
} else {
/* single packet */
}
} else {
/* multi-pkt msg */
/* first packet */
} else {
/* intermediate packet */
}
}
return (ret);
}
void
{
int ret = IBT_SUCCESS;
} else {
}
do {
} while (ret != IBT_CQ_EMPTY);
/* enable the CQ */
if (ret != IBT_SUCCESS) {
return;
}
do {
} while (ret != IBT_CQ_EMPTY);
}
void
{
npolled = 0;
do {
if (ret != IBT_SUCCESS) {
if (ret != IBT_CQ_EMPTY) {
} else {
"ibt_poll_cq returned: IBT_CQ_EMPTY",
}
break;
}
continue;
}
RDS_DPRINTF5("rds_poll_send_completions",
"EP(%p): WC ID: %p ERROR: %d", ep,
ep->ep_rdmacnt--;
continue;
}
} else {
RDS_DPRINTF2("rds_poll_send_completions",
"EP(%p): WC ID: %p ERROR: %d", ep,
if (send_error == 0) {
/* don't let anyone send anymore */
if (sp->session_state !=
sp->session_state =
/* Make this the active end */
sp->session_type =
}
}
send_error++;
ep->ep_rdmacnt--;
continue;
}
}
if (headp) {
} else {
}
npolled++;
}
break;
}
if (rds_no_interrupts == 1) {
break;
}
} while (ret != IBT_CQ_EMPTY);
/* put the buffers to the pool */
if (npolled != 0) {
}
if (send_error != 0) {
}
}
void
{
int ret;
/* enable the CQ */
if (ret != IBT_SUCCESS) {
return;
}
}
void
{
int ret;
/* free the QP */
/* wait until the RQ is empty */
if (ret != IBT_SUCCESS) {
}
} else {
RDS_DPRINTF2("rds_ep_free_rc_channel",
"EP(%p) Channel is ALREADY FREE", ep);
}
/* free the Send CQ */
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rds_ep_free_rc_channel",
"EP(%p) - for sendcq, ibt_free_cq returned %d",
}
} else {
RDS_DPRINTF2("rds_ep_free_rc_channel",
"EP(%p) SendCQ is ALREADY FREE", ep);
}
/* free the Recv CQ */
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rds_ep_free_rc_channel",
"EP(%p) - for recvcq, ibt_free_cq returned %d",
}
} else {
RDS_DPRINTF2("rds_ep_free_rc_channel",
"EP(%p) RecvCQ is ALREADY FREE", ep);
}
}
/* Allocate resources for RC channel */
{
int ret = IBT_SUCCESS;
/* Update the EP with the right IP address and HCA guid */
/* reset taskqpending flag here */
} else {
}
/* returned size is always greater than the requested size */
if (ret != IBT_SUCCESS) {
return (NULL);
}
ep);
if (rds_no_interrupts == 0) {
if (ret != IBT_SUCCESS) {
"ibt_enable_cq_notify failed: %d", ret);
return (NULL);
}
}
}
/* returned size is always greater than the requested size */
if (ret != IBT_SUCCESS) {
return (NULL);
}
ep);
if (ret != IBT_SUCCESS) {
"ibt_enable_cq_notify failed: %d", ret);
return (NULL);
}
}
if (ret != IBT_SUCCESS) {
ret);
return (NULL);
}
/* Chan private should contain the ep */
return (chanhdl);
}
#if 0
/* Return node guid given a port gid */
{
int ret;
if (ret != IBT_SUCCESS) {
return (0LL);
}
return (nodeinfo.n_node_guid);
}
#endif
static void
{
int ret;
RDS_DPRINTF2("rds_handle_portup_event",
}
return;
}
if (ret != IBT_SUCCESS) {
return;
}
/* If RDS service is not registered then no bind is needed */
RDS_DPRINTF2("rds_handle_portup_event",
"RDS Service is not registered, so no action needed");
return;
}
/*
* If the service was previously bound on this port and
* if this port has changed state down and now up, we do not
* need to bind the service again. The bind is expected to
* persist across state changes. If the service was never bound
* before then we bind it this time.
*/
/* structure copy */
/* bind RDS service on the port, pass statep as cm_private */
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rds_handle_portup_event",
"Bind service for HCA: 0x%llx Port: %d "
}
}
event->ev_hca_guid);
}
static void
{
int ret;
return;
/* register the recv memory with this hca */
/* no memory to register */
return;
}
if (ret != IBT_SUCCESS) {
ret);
} else {
}
}
static void
{
/*
* This should be a write lock as we don't want anyone to get access
* to the hcap while we are modifing its contents
*/
}
/* Prevent initiating any new activity on this HCA */
/*
* stop the outgoing traffic and close any active sessions on this hca.
* Any pending messages in the SQ will be allowed to complete.
*/
while (sp) {
continue;
}
sp->session_state);
/*
* We are changing the session state in advance. This prevents
* further messages to be posted to the SQ. We then
* send a control message to the remote and tell it close
* the session.
*/
"RDS_SESSION_STATE_PASSIVE_CLOSING", sp);
/*
* wait until the sendq is empty then tell the remote to
* close this session. This enables for graceful shutdown of
* the session
*/
(void) rds_post_control_message(sp,
}
/* wait until all the sessions are off this HCA */
while (sp) {
continue;
}
sp->session_state);
sp->session_state);
}
}
/*
* if rdsib_close_ib was called before this, then that would have
* unbound the service on all ports. In that case, the HCA structs
* will contain stale bindhdls. Hence, we do not call unbind unless
* the service is still registered.
*/
/* unbind RDS service on all ports on this HCA */
continue;
}
RDS_DPRINTF2("rdsib_del_hca",
"Unbinding Service: port: %d, bindhdl: %p",
}
}
switch (saved_state) {
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rdsib_del_hca",
"ibt_deregister_mr failed: %d", ret);
return;
}
/* FALLTHRU */
case RDS_HCA_STATE_OPEN:
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rdsib_del_hca",
"ibt_free_pd failed: %d", ret);
}
if (ret != IBT_SUCCESS) {
RDS_DPRINTF2("rdsib_del_hca",
"ibt_close_hca failed: %d", ret);
}
}
/*
* This should be a write lock as we don't want anyone to get access
* to the hcap while we are modifing its contents
*/
}
static void
{
switch (code) {
case IBT_EVENT_PORT_UP:
break;
case IBT_HCA_ATTACH_EVENT:
/*
* NOTE: In some error recovery paths, it is possible to
* receive IBT_HCA_ATTACH_EVENTs on already known HCAs.
*/
break;
case IBT_HCA_DETACH_EVENT:
break;
default:
}
}