/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* MODULE: dapl_ia_open.c
*
* PURPOSE: Interface Adapter management
* Description: Interfaces in this file are completely described in
* the DAPL 1.1 API, Chapter 6, section 2
*
* $Id: dapl_ia_open.c,v 1.30 2003/07/31 14:04:17 jlentini Exp $
*/
#include "dapl.h"
#include "dapl_provider.h"
#include "dapl_evd_util.h"
#include "dapl_hca_util.h"
#include "dapl_ia_util.h"
#include "dapl_adapter_util.h"
#include <sys/systeminfo.h>
#include <libdevinfo.h>
/*
* LOCAL PROTOTYPES
*/
#if defined(IBHOSTS_NAMING)
void dapli_assign_hca_ip_address(
DAPL_HCA *hca_ptr,
char *device_name);
#endif /* IBHOSTS_NAMING */
static void dapli_hca_cleanup(DAPL_HCA *hca_ptr, DAT_BOOLEAN dec_ref);
/*
* Determine whether the platform supports RO (Relaxed ordering)
* Return B_TRUE if it does support RO and B_FALSE if it does not support RO
*
* udapl_ro_disallowed is an out paramter returning whether or not
* relaxed ordering should be disabled (regardless of whether the platform
* is capable of supporting relaxed ordering)
*
*/
static boolean_t
dapl_ro_disallowed(void)
{
static const char * const non_ro_capable_platforms[] = {
"i86pc",
"i86xpv",
"SUNW,Sun-Fire-V215",
"SUNW,Sun-Fire-V245",
"SUNW,Sun-Fire-V445",
"SUNW,Sun-Fire-T1000",
"SUNW,Sun-Fire-T200",
"SUNW,Sun-Blade-T6300",
"SUNW,Sun-Blade-T6320",
"SUNW,SPARC-Enterprise-T1000",
"SUNW,SPARC-Enterprise-T2000",
"SUNW,SPARC-Enterprise-T5120",
"SUNW,SPARC-Enterprise-T5220",
NULL
};
char platform[256 + 1];
register int i;
register const char *cp;
int ret;
di_node_t root_node, node;
boolean_t ro_disallowed;
static const char *ro_disallowed_property =
"pci-relaxed-ordering-disallowed";
int bool;
int *boolp = &bool;
ret = sysinfo(SI_PLATFORM, platform, sizeof (platform));
if ((ret != -1) && (ret <= sizeof (platform))) {
for (i = 0; (cp = non_ro_capable_platforms[i]) != NULL; ++i) {
if (strcmp(platform, cp) == 0)
return (B_TRUE);
}
}
/*
* This function only finds and looks at the FIRST udapl node.
* It is assumed that there can only be one such node.
*/
if ((root_node = di_init("/", DINFOSUBTREE | DINFOPROP)) == DI_NODE_NIL)
return (B_FALSE);
node = di_drv_first_node("daplt", root_node);
if (node != DI_NODE_NIL) {
ret = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
ro_disallowed_property, &boolp);
switch (ret) {
case 0:
case 1:
ro_disallowed = B_TRUE;
break;
default:
ro_disallowed = B_FALSE;
break;
}
}
else
ro_disallowed = B_FALSE;
di_fini(root_node);
return (ro_disallowed);
}
/*
* dapl_ia_open
*
* DAPL Requirements Version xxx, 6.2.1.1
*
* Open a provider and return a handle. The handle enables the user
* to invoke operations on this provider.
*
* The dat_ia_open call is actually part of the DAT registration module.
* That function maps the DAT_NAME parameter of dat_ia_open to a DAT_PROVIDER,
* and calls this function.
*
* Input:
* provider
* async_evd_qlen
* async_evd_handle_ptr
*
* Output:
* async_evd_handle
* ia_handle
*
* Return Values:
* DAT_SUCCESS
* DAT_INSUFFICIENT_RESOURCES
* DAT_INVALID_PARAMETER
* DAT_INVALID_HANDLE
* DAT_NAME_NOT_FOUND (returned by dat registry if necessary)
*/
DAT_RETURN
dapl_ia_open(
IN const DAT_NAME_PTR name,
IN DAT_COUNT async_evd_qlen,
INOUT DAT_EVD_HANDLE *async_evd_handle_ptr,
OUT DAT_IA_HANDLE *ia_handle_ptr,
IN boolean_t ro_aware_client)
{
DAT_RETURN dat_status;
DAT_PROVIDER *provider;
DAPL_HCA *hca_ptr;
DAPL_IA *ia_ptr;
DAPL_EVD *evd_ptr;
boolean_t ro_disallowed;
dat_status = DAT_SUCCESS;
hca_ptr = NULL;
ia_ptr = NULL;
dapl_dbg_log(DAPL_DBG_TYPE_API,
"dapl_ia_open(%s, %d, %p, %p, %d)\n",
name,
async_evd_qlen,
async_evd_handle_ptr,
ia_handle_ptr,
ro_aware_client);
dat_status = dapl_provider_list_search(name, &provider);
if (DAT_SUCCESS != dat_status) {
dapl_dbg_log(DAPL_DBG_TYPE_API,
"dapl_ia_open: dapl_provider_list_search(\"%s\") returned "
"%d\n",
name,
dat_status);
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
goto bail;
}
/* ia_handle_ptr and async_evd_handle_ptr cannot be NULL */
if (ia_handle_ptr == NULL) {
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG4);
goto bail;
}
if (async_evd_handle_ptr == NULL) {
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG3);
goto bail;
}
/* initialize the caller's OUT param */
*ia_handle_ptr = DAT_HANDLE_NULL;
/* get the hca_ptr */
hca_ptr = (DAPL_HCA *)provider->extension;
/*
* Open the HCA if it has not been done before.
*/
dapl_os_lock(&hca_ptr->lock);
if (hca_ptr->ib_hca_handle == IB_INVALID_HANDLE) {
/* register with the HW */
dat_status = dapls_ib_open_hca(hca_ptr,
&hca_ptr->ib_hca_handle);
if (dat_status != DAT_SUCCESS) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"dapls_ib_open_hca failed %d\n", dat_status);
dapl_os_unlock(&hca_ptr->lock);
goto bail;
}
/* create a cq domain for this HCA */
dat_status = dapls_ib_cqd_create(hca_ptr);
if (dat_status != DAT_SUCCESS) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"ERR: Cannot allocate CQD: err %x\n", dat_status);
dapli_hca_cleanup(hca_ptr, DAT_FALSE);
dapl_os_unlock(&hca_ptr->lock);
goto bail;
}
/*
* Obtain the IP address associated with this name and HCA.
*/
#ifdef IBHOSTS_NAMING
dapli_assign_hca_ip_address(hca_ptr, name);
#endif /* IBHOSTS_NAMING */
/*
* Obtain IA attributes from the HCA to limit certain
* operations.
* If using DAPL_ATS naming, ib_query_hca will also set the ip
* address.
*/
dat_status = dapls_ib_query_hca(hca_ptr,
&hca_ptr->ia_attr,
NULL,
&hca_ptr->hca_address, NULL);
if (dat_status != DAT_SUCCESS) {
dapli_hca_cleanup(hca_ptr, DAT_FALSE);
dapl_os_unlock(&hca_ptr->lock);
goto bail;
}
}
/* is the IA going to use the ConnectX? */
if (hca_ptr->hermon_resize_cq != 0) {
/*
* We are running with a ConnectX.
* Determine whether platform is RO capable.
* If platform support RO and client does not
* support RO and we are not disabling RO, reject the open.
*/
ro_disallowed = dapl_ro_disallowed();
if (! ro_aware_client && ! ro_disallowed) {
dapl_dbg_log(DAPL_DBG_TYPE_API,
"dapl_ia_open: failing ro_disallowed %d "
"ro_aware_client %d \n",
ro_disallowed, ro_aware_client);
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER,
DAT_INVALID_RO_COOKIE);
dapli_hca_cleanup(hca_ptr, DAT_FALSE);
dapl_os_unlock(&hca_ptr->lock);
goto bail;
}
} else {
/* We are not running with a Connect X */
ro_disallowed = B_TRUE;
}
/* Take a reference on the hca_handle */
dapl_os_atomic_inc(&hca_ptr->handle_ref_count);
dapl_os_unlock(&hca_ptr->lock);
/* Allocate and initialize ia structure */
ia_ptr = dapl_ia_alloc(provider, hca_ptr);
if (!ia_ptr) {
dapl_os_lock(&hca_ptr->lock);
dapli_hca_cleanup(hca_ptr, DAT_TRUE);
dapl_os_unlock(&hca_ptr->lock);
dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
DAT_RESOURCE_MEMORY);
goto bail;
}
/*
* Note when we should be disabling relaxed ordering.
* If the property indicates that we should not use relaxed ordering
* we remember that fact. If the platform is supposed to be
* non relaxed ordering capable, we disable relaxed ordering as
* well, just in case the property or the list indicating that
* this platform is not relaxed ordering capable is mistaken.
*/
if (ro_disallowed)
ia_ptr->dapl_flags |= DAPL_DISABLE_RO;
/*
* we need an async EVD for this IA
* use the one passed in (if non-NULL) or create one
*/
evd_ptr = (DAPL_EVD *) *async_evd_handle_ptr;
if (evd_ptr) {
if (DAPL_BAD_HANDLE(evd_ptr, DAPL_MAGIC_EVD) ||
! (evd_ptr->evd_flags & DAT_EVD_ASYNC_FLAG)) {
dat_status = DAT_ERROR(DAT_INVALID_HANDLE,
DAT_INVALID_HANDLE_EVD_ASYNC);
goto bail;
}
/*
* InfiniBand allows only 1 asychronous event handler per HCA
* (see InfiniBand Spec, release 1.1, vol I, section 11.5.2,
* page 559).
*
* We only need to make sure that this EVD's CQ belongs to
* the same HCA as is being opened.
*/
if (evd_ptr->header.owner_ia->hca_ptr->ib_hca_handle !=
hca_ptr->ib_hca_handle) {
dat_status = DAT_ERROR(DAT_INVALID_HANDLE,
DAT_INVALID_HANDLE_EVD_ASYNC);
goto bail;
}
ia_ptr->cleanup_async_error_evd = DAT_FALSE;
ia_ptr->async_error_evd = evd_ptr;
} else {
/*
* Verify we have >0 length, and let the provider check the
* size
*/
if (async_evd_qlen <= 0) {
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER,
DAT_INVALID_ARG2);
goto bail;
}
dat_status = dapls_evd_internal_create(ia_ptr,
NULL, /* CNO ptr */
async_evd_qlen,
DAT_EVD_ASYNC_FLAG,
&evd_ptr);
if (dat_status != DAT_SUCCESS) {
goto bail;
}
dapl_os_atomic_inc(&evd_ptr->evd_ref_count);
dapl_os_lock(&hca_ptr->lock);
if (hca_ptr->async_evd != (DAPL_EVD *) 0) {
#if 0
/*
* The async EVD for this HCA has already been assigned.
* It's an error to try and assign another one.
*
* However, we need to somehow allow multiple IAs
* off of the same HCA. The right way to do this
* is by dispatching events off the HCA to the
* appropriate IA, but we aren't there yet. So for
* now we create the EVD but don't connect it to
* anything.
*/
dapl_os_atomic_dec(&evd_ptr->evd_ref_count);
dapl_evd_free(evd_ptr);
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER,
DAT_INVALID_ARG4);
goto bail;
#endif
dapl_os_unlock(&hca_ptr->lock);
} else {
hca_ptr->async_evd = evd_ptr;
dapl_os_unlock(&hca_ptr->lock);
/*
* Register the handlers associated with the async EVD.
*/
dat_status = dapls_ia_setup_callbacks(ia_ptr, evd_ptr);
if (dat_status != DAT_SUCCESS) {
/* Assign the EVD so it gets cleaned up */
ia_ptr->cleanup_async_error_evd = DAT_TRUE;
ia_ptr->async_error_evd = evd_ptr;
goto bail;
}
}
ia_ptr->cleanup_async_error_evd = DAT_TRUE;
ia_ptr->async_error_evd = evd_ptr;
}
dat_status = DAT_SUCCESS;
*ia_handle_ptr = ia_ptr;
*async_evd_handle_ptr = evd_ptr;
bail:
if (dat_status != DAT_SUCCESS) {
if (ia_ptr) {
/* This will release the async EVD if needed. */
(void) dapl_ia_close(ia_ptr, DAT_CLOSE_ABRUPT_FLAG);
}
}
dapl_dbg_log(DAPL_DBG_TYPE_RTN,
"dapl_ia_open () returns 0x%x\n",
dat_status);
return (dat_status);
}
/*
* dapli_hca_cleanup
*
* Clean up partially allocated HCA stuff. Strictly to make cleanup
* simple.
*/
void
dapli_hca_cleanup(
DAPL_HCA *hca_ptr,
DAT_BOOLEAN dec_ref)
{
(void) dapls_ib_close_hca(hca_ptr->ib_hca_handle);
hca_ptr->ib_hca_handle = IB_INVALID_HANDLE;
if (dec_ref == DAT_TRUE) {
dapl_os_atomic_dec(&hca_ptr->handle_ref_count);
}
}
#if defined(IBHOSTS_NAMING)
char *dapli_get_adapter_num(
char *device_name);
void dapli_setup_dummy_addr(
IN DAPL_HCA *hca_ptr,
IN char *hca_name);
/*
* dapli_assign_hca_ip_address
*
* Obtain the IP address of the passed in name, which represents a
* port on the hca. There are three methods here to obtain the
* appropriate IP address, each with their own shortcoming:
* 1) IPOIB_NAMING. Requires the implementation of the IPoIB
* interface defined in include/dapl/ipoib_names.h. This is
* not the recommended interface as IPoIB is limited at
* the point we need to obtain an IP address on the
* passive side of a connection. The code supporting this
* implementation has been removed.
*
* 2) IBHOSTS. An entry exists in DNS and in the /etc/dapl/ibhosts
* file. The immediate drawback here is that we must dictate
* how to name the interface, which is a stated DAPL non-goal.
* In the broader perspective, this method requires us to xmit
* the IP address in the private data of a connection, which has
* other fun problems. This is the default method and is known to
* work, but it has problems.
*
* 3) Obtain the IP address from the driver, which has registered
* the address with the SA for retrieval.
*
*
* Input:
* hca_ptr Pointer to HCA structure
* device_name Name of device as reported by the provider
*
* Output:
* none
*
* Returns:
* char * to string number
*/
void
dapli_assign_hca_ip_address(
DAPL_HCA *hca_ptr,
char *device_name)
{
char *adapter_num;
#define NAMELEN 128
struct addrinfo *addr;
char hostname[NAMELEN];
char *str;
int rc;
/*
* Obtain the IP address of the adapter. This is a simple
* scheme that creates a name that must appear available to
* DNS, e.g. it must be in the local site DNS or in the local
* /etc/hosts file, etc.
*
* <hostname>-ib<index>
*
* This scheme obviously doesn't work with adapters from
* multiple vendors, but will suffice in common installations.
*/
rc = gethostname(hostname, NAMELEN);
/*
* Strip off domain info if it exists (e.g. mynode.mydomain.com)
*/
for (str = hostname; *str && *str != '.'; ) {
str++;
}
if (*str == '.') {
*str = '\0';
}
dapl_os_strcat(hostname, "-ib");
adapter_num = dapli_get_adapter_num(device_name);
dapl_os_strcat(hostname, adapter_num);
rc = dapls_osd_getaddrinfo(hostname, &addr);
if (rc != 0) {
/* Not registered in DNS, provide a dummy value */
dapli_setup_dummy_addr(hca_ptr, hostname);
} else {
/*
* hca_address is defined as a DAT_SOCK_ADDR6 whereas ai_addr
* is a sockaddr
*/
(void) dapl_os_memcpy((void *)&hca_ptr->hca_address,
(void *)(addr->ai_addr), sizeof (DAT_SOCK_ADDR6));
}
}
/*
* dapli_stup_dummy_addr
*
* Set up a dummy local address for the HCA. Things are not going
* to work too well if this happens.
* We call this routine if:
* - remote host adapter name is not in DNS
* - IPoIB implementation is not correctly set up
* - Similar nonsense.
*
* Input:
* hca_ptr
* rhost_name Name of remote adapter
*
* Output:
* none
*
* Returns:
* none
*/
void
dapli_setup_dummy_addr(
IN DAPL_HCA *hca_ptr,
IN char *rhost_name)
{
struct sockaddr_in *si;
/* Not registered in DNS, provide a dummy value */
dapl_dbg_log(DAPL_DBG_TYPE_ERR, "WARNING: <%s> not registered in DNS,"
" using dummy IP value\n", rhost_name);
si = (struct sockaddr_in *)&hca_ptr->hca_address;
si->sin_family = AF_INET;
si->sin_addr.s_addr = 0x01020304;
}
/*
* dapls_get_adapter_num
*
* Given a device name, return a string of the device number
*
* Input:
* device_name Name of device as reported by the provider
*
* Output:
* none
*
* Returns:
* char * to string number
*/
char *
dapli_get_adapter_num(
char *device_name)
{
static char *zero = "0";
char *p;
/*
* Optimisticaly simple algorithm: the device number appears at
* the end of the device name string. Device that do not end
* in a number are by default "0".
*/
for (p = device_name; *p; p++) {
if (isdigit(*p)) {
return (p);
}
}
return (zero);
}
#endif /* IBHOSTS_NAMING */
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 8
* End:
*/