/* * 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 #include /* * 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. * * -ib * * 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: */