ibmf_saa.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
*/
/*
* As a primitive error checking scheme, the first 4 bytes of the client state
* have a well-known pattern. We write this pattern during session_open, make
* sure all subsequent calls still have this pattern in the client state, and
* clear the pattern on session_close. Clients could still run into trouble
* providing a bad handle since we don't check a known list of handles. But
* this mechanism will protect against making ibmf_saa calls after the session
* has been closed.
*/
#define IBMF_SAA_SET_CLIENT_SIGNATURE(clientp) { \
}
#define IBMF_SAA_VERIFY_CLIENT_SIGNATURE(clientp) \
#define IBMF_SAA_CLEAR_CLIENT_SIGNATURE(clientp) { \
(clientp)->saa_client_sig = 0; \
}
/* Global Sa_access State Pointer */
extern saa_state_t *saa_statep;
extern int ibmf_trace_level;
/*
* Locking scheme:
* ibmf_saa maintains a linked list of port entries. Each element of the list
* contains information about a certain port. There may be multiple clients
* associated with each of these entries. The list is synchronized with a state
* port_list_mutex. Each of the entries has their own individual mutex. When
* adding a new port entry to the mutex the client, with the list mutex, marks
* the port as registering, adds the port, and releases the list mutex.
* Subsequent clients aquire the list mutex, find the port, acquire the port
* mutex, release the list mutex, and wait if the port is marked as registering.
* Clients should never try to acquire the list mutex when they have a port
* mutex.
*/
/*
* ibmf_sa_session_open():
*
* Before using the ibmf_saa interface, consumers should register with the
* ibmf_saa interface by calling ibmf_sa_session_open(). Upon a successful
* registration, a handle is returned for use in subsequent interaction with the
* ibmf_saa interface; this handle is also provided as an argument to subnet
* event notification function.
*
* Consumers can register to be notified of subnet events such as GID
* being available/unavailable. Clients which provide a non-NULL event args
* structure will have the is_event_callback function called when an event is
* received or there is a failure in subscribing for events. This callback may
* be generated before the ibmf_sa_session_open() call returns.
*
* This interface blocks allocating memory, but not waiting for any packet
* responses.
*
* Arguments:
* port_guid - GUID of the port.
* event_args - subnet event registration details
* sm_key - only filled in if the consumer is an SM
* ibmf_version - version of the interface (IBMF_VERSION)
* flags - unused
*
* Output Arguments:
* ibmf_sa_handle - pointer to ibmf_saa_handle to be used in future calls
*
* Return values:
* IBMF_SUCCESS - registration succeeded
* IBMF_BAD_PORT - registration failed; active port not found
* IBMF_BAD_PORT_STATE - registration failed; port found but not active or
* previous registration failed
* IBMF_NO_MEMORY - registration failed; could not allocate memory
* IBMF_NO_RESOURCES - registration failed due to a resource issue
* IBMF_BUSY - registration failed; too many clients registered
* for this port
* IBMF_TRANSPORT_FAILURE - failure with underlying transport framework
* IBMF_INVALID_ARG - ibmf_saa_handle arg was NULL
*
* The ibmf_saa module maintains a linked list of ports which it knows about.
* For each port, a reference count is kept. When the first client for a
* port registers with ibmf_saa, ibmf_saa registers with ibmf.
* The reference count checking must be serialized to
* ensure that only one client modifies the reference count at a time.
* When a client determines that it is responsible for registering it
* sets the state field to "registering" in the port. Clients registering with
* sa_acess will cv_wait on this field before modifying the reference count.
* Unregistering clients do not need to wait on this field since no one else
* will be registering while they are completing (the port's ref count will
* be greater than 0).
* If ibmf registration fails, the entry is set to "invalid"; we decrement
* the reference count that we just incremented.
*
* WARNING: after decrementing the reference count, NO further access to
* the entry should be performed in the same thread, because invalid entries
* with ref counts of 0 are purged.
*/
/* ARGSUSED */
int
{
int status = IBMF_SUCCESS;
"ibmf_sa_session_open() enter\n");
if (ibmf_version != IBMF_VERSION) {
"ibmf_sa_session_open: Bad Version\n");
goto bail;
}
if (ibmf_saa_handle == NULL) {
"ibmf_sa_session_open: invalid argument, null pointer\n");
goto bail;
}
/*
* Find a valid entry matching the port guid
* Refcount is immediately incremented
*/
/* acquire list mutex (and keep it locked until after creation) */
break;
}
}
"ibmf_sa_session_open(): %s\n",
/* release list mutex */
/*
* now add client to existing port
* (will wait till end of ibmf registering)
* Note that the state may have changed in the meantime...
*/
if (status != IBMF_SUCCESS) {
"ibmf_sa_session_open: %s, status = %d\n",
goto bail;
}
} else {
/* create minimal port entry, non blocking */
if (status != IBMF_SUCCESS) {
/* release list mutex */
"ibmf_sa_session_open: %s, status = %d\n",
goto bail;
}
/* link to list */
/*
* release the list mutex since we now have the minimum amount
* of port data initialized to prevent subsequent clients from
* continuing with registration (they will cv_wait on registe-
* -ring state). We don't want to hold the list mutex since
* other ports may need it and since we're about to make calls
* to functions which may block.
*
* We do not need the port registering mutex since clients will
* not proceed while saa_pt_state ==
* IBMF_SAA_PORT_STATE_REGISTERING.
*/
if (status != IBMF_SUCCESS) {
"ibmf_sa_session_open: %s, status = %d\n",
goto bail;
}
if (status != IBMF_SUCCESS) {
"ibmf_sa_session_open: %s, ibmf_status = %d\n",
"ibmf_saa_impl_register_port failed",
/*
* Note: we don't update kstats as this entry
* will eventually go away...
*/
goto bail;
}
"ibmf_sa_session_open: %s, prefix = %016" PRIx64
/* mark port as registered */
/* incremement reference count to account for cpi */
/* kick waiters */
"port is up. Sending classportinfo request");
}
/* create new client structure */
"ibmf_sa_session_open: clientp = %p, subnetp = %p\n",
NULL);
/* if client is interested in subnet event notifications */
if (event_args != NULL) {
}
bail:
/* purge invalid entries */
return (status);
}
/*
* ibmf_sa_session_close()
*
* Unregister a consumer of the SA_Access interface
*
* This interface blocks.
*
* Arguments:
* SA_Access handle
*
* Return values:
* IBMF_SUCCESS - unregistration succeeded
* IBMF_FAILURE - unregistration failed for unknown reasons
*
* All outstanding callbacks will be canceled before this function returns.
*
*/
/* ARGSUSED */
int
{
int status = IBMF_SUCCESS;
"ibmf_sa_session_close() enter\n");
if (ibmf_saa_handle == NULL) {
"ibmf_sa_session_close: %s\n",
goto bail;
}
/* ibmf_saa_handle is pointer to the client data structure */
/* sanity check to make sure nothing happened to handle */
"ibmf_sa_session_close: %s\n",
goto bail;
}
"", "ibmf_sa_session_close: saa_portp = %p\n",
/*
* if there are pending async transactions, wait for them to finish
* note that we wait only once, not loop....
* note we test the state outside saa_pt_mutex
*/
if ((client_data->saa_client_num_pending_trans > 0) &&
(port_state == IBMF_SAA_PORT_STATE_READY)) {
"", "ibmf_sa_session_close: %s, num_pending_trans = %d\n",
/*
* we rely on IBMF calling the callback in all cases,
* callback signals cv
*/
}
/* mark state as closed so no more event callbacks will be generated */
/*
* if there are pending subnet event callbacks wait for them to finish
*/
if (client_data->saa_client_event_cb_num_active > 0) {
"", "ibmf_sa_session_close: %s, num_active_cb = %d\n",
}
/*
* if client was subscribed for events then remove the callback from the
* list, and possibly unsubscribe from the SA
*/
/* remove the client from the port's list of clients */
prev_clientp = NULL;
while (curr_clientp != NULL) {
if (curr_clientp == client_data) {
break;
}
}
/* should have found the client */
if (curr_clientp == NULL) {
"ibmf_sa_session_close: %s. ref_count = %d\n",
} else {
if (prev_clientp == NULL) {
} else
"Removed client from event subscriber list");
}
}
/* decrementing refcount is last thing we do on port entry */
IBMF_TNF_TRACE, "",
"ibmf_sa_session_close: ref_count = %d\n",
/* destroy client */
*ibmf_saa_handle = NULL;
bail:
/* purge invalid entries */
return (status);
}
/*
* ibmf_sa_access
*
* Retrieve records from the SA given an AttributeID, ComponentMask,
* and a template
*
* This interface blocks if the callback parameter is NULL.
*
* Input Arguments:
* ibmf_saa_handle - handle returned from ibmf_sa_session_open()
* access_args - structure containing various parameters for the query
* flags - unsused
*
* Output Arguments:
* length - size of buffer returned
* result - pointer to buffer of records returned in response.
* Buffer is host-endian, unpacked and can be cast to one
* of the record types in sa_recs.h
* Return values:
* IBMF_SUCCESS - query succeeded
* IBMF_BAD_HANDLE - sa session handle is invalid
* IBMF_BAD_PORT_STATE - port in incorrect state
* IBMF_INVALID_ARG - one of the pointer parameters was NULL
* IBMF_NO_RESOURCES - ibmf could not allocate ib resources or SA returned
* ERR_NO_RESOURCES
* IBMF_TRANS_TIMEOUT - transaction timed out
* IBMF_TRANS_FAILURE - transaction failure
* IBMF_NO_MEMORY - ibmf could not allocate memory
* IBMF_REQ_INVALID - send and recv buffer the same for a sequenced
* transaction or the SA returned an ERR_REQ_INVALID
* IBMF_NO_RECORDS - no records matched query
* IBMF_TOO_MANY_RECORDS- SA returned SA_ERR_TOO_MANY_RECORDS
* IBMF_INVALID_GID - SA returned SA_INVALID_GID
* IBMF_INSUFF_COMPS - SA returned SA_ERR_INSUFFICIENT_COMPS
* IBMF_UNSUPP_METHOD - SA returned MAD_STATUS_UNSUPP_METHOD
* IBMF_UNSUPP_METHOD_ATTR - SA returned MAD_STATUS_UNSUPP_METHOD_ATTR
* IBMF_INVALID_FIELD - SA returned MAD_STATUS_INVALID_FIELD
*
* Upon successful completion, result points to a buffer containing the records.
* length is the size in bytes of the buffer returned in result. If there are
* no records or the call failed the length is 0.
*
* The consumer is responsible for freeing the memory associated with result.
*/
/* ARGSUSED */
int
void **result)
{
int res = IBMF_SUCCESS;
"ibmf_sa_access_start() enter. attr_id = 0x%x, access_type ="
"ibmf_sa_access: %s\n",
goto bail;
}
/* sanity check to make sure nothing happened to handle */
"ibmf_sa_access: %s\n",
"", "ibmf_sa_access() exit\n");
goto bail;
}
KM_SLEEP);
} else {
if (trans_info == NULL) {
"could not allocate memory for trans_info");
goto bail;
}
}
/*
* method is get_multi if attribute is multipath; otherwise method is
* based on query type
*/
"ibmf_sa_access: %s, access_type = 0x%x\n",
" records must be IBMF_SAA_RETRIEVE",
goto bail;
}
"ibmf_sa_access: %s, access_type = 0x%x\n",
" records must be IBMF_SAA_RETRIEVE",
goto bail;
}
} else {
switch (access_args->sq_access_type) {
case IBMF_SAA_RETRIEVE:
break;
case IBMF_SAA_UPDATE:
break;
case IBMF_SAA_DELETE:
break;
default:
"ibmf_sa_access: %s, access_type = 0x%x\n",
sizeof (saa_impl_trans_info_t));
goto bail;
}
}
if (res != IBMF_SUCCESS) {
"ibmf_sa_access: %s, ibmf_status = %d\n",
*length = 0;
if (res == IBMF_TRANS_TIMEOUT)
1);
goto bail;
}
/*
* if async call don't do anything as callback will take care of
* everything; for sync call, copy parameters back to client and free
* trans_info structure
*/
if (res != IBMF_SUCCESS)
1);
if (res == IBMF_TRANS_TIMEOUT)
1);
}
bail:
if (res != IBMF_SUCCESS) {
*length = 0;
}
"", "ibmf_sa_access() exit: result = 0x%x\n",
return (res);
}
/*
* Helper Functions.
* Ease of use functions so that the consumer doesn't
* have to do the overhead of calling ibmf_sa_access for
* commonly used queries
*/
/*
* ibmf_saa_gid_to_pathrecords
* Given a source gid and a destination gid, return paths
* between the gids.
*
* This interface blocks.
*
* Input Arguments:
* ibmf_saa_handle - handle returned from ibmf_sa_session_open()
* sgid - source gid of path
* dgid - destination gid of path
* p_key - partition of path. This value may be wildcarded with
* IBMF_SAA_PKEY_WC.
* mtu - preferred MTU of the path. This argument may be
* wildcarded with IBMF_SAA_MTU_WC.
* reversible - if B_TRUE, ibmf will query only reversible paths
* see Infiniband Specification table 171
* num_paths - maximum number of paths to return
* num_paths should be checked for the actual number of
* records returned.
* flags - unused
*
* Output Arguments:
* num_paths - actual number of paths returned
* length - size of buffer returned
* result - pointer to buffer of path records returned in response
*
* Return values:
* Error codes are the same as ibmf_sa_access() return values
*
* Upon successful completion, result points to a buffer containing the records.
* length is the size in bytes of the buffer returned in result. If there are
* no records or the call failed the length is 0.
*
* The consumer is responsible for freeing the memory associated with result.
*/
/* ARGSUSED */
int
{
int res;
"ibmf_saa_gid_to_pathrecords() enter\n");
/*
* check num_paths pointer here since we dereference before calling
* ibmf_sa_access
*/
"ibmf_saa_gid_to_pathrecords: %s\n",
"", "ibmf_saa_gid_to_pathrecords() exit\n");
*length = 0;
return (IBMF_INVALID_ARG);
}
/* check valid handle; in non-debug system ibmf_sa_access() will fail */
*length = 0;
if (reversible == B_TRUE) {
}
if (p_key != IBMF_SAA_PKEY_WC) {
}
/*
* gid_to_pathrecords specifies greater than or equal to MTU. Path
* records can only do strictly greater. Set the mtu value to one
* less than the mtu parameter. If it's the lowest value possible (256)
* don't do anything and any path mtu will be allowed.
*/
}
(void **)result);
if (res != IBMF_SUCCESS) {
"ibmf_saa_gid_to_pathrecords: %s, ibmf_status = %d\n",
}
"ibmf_saa_gid_to_pathrecords() exit: result = 0x%x\n",
return (res);
}
/*
* ibmf_saa_paths_from_gid
* Given a source GID, return a path from the source gid
* to every other port on the subnet. It is assumed that the
* subnet is fully connected. Only one path per port on the subnet
* is returned.
*
* This interface blocks.
*
* Input Arguments:
* ibmf_saa_handle - handle returned from ibmf_sa_session_open()
* sgid - source gid of path
* pkey - paritition of path. This value may be wildcarded with
* IBMF_SAA_PKEY_WC.
* reversible - if B_TRUE, ibmf will query only reversible paths;
* see Infiniband Specification table 171
* flags - unused
*
* Output Arguments:
* num_paths - number of paths returned
* length - size of buffer returned
* result - pointer to buffer of path records returned in response
*
* Return values:
* Error codes are the same as ibmf_sa_access() return values
*
* Upon successful completion, result points to a buffer containing the records.
* and num_records is the number of path records returned. length is the size
* in bytes of the buffer returned in result. If there are no records or the
* call failed the length is 0.
*
* The consumer is responsible for freeing the memory associated with result.
*/
/* ARGSUSED */
int
{
int res;
"ibmf_saa_paths_from_gid() enter\n");
/* check valid handle; in non-debug system ibmf_sa_access() will fail */
if (reversible == B_TRUE) {
}
if (p_key != IBMF_SAA_PKEY_WC) {
}
(void **)result);
if (res != IBMF_SUCCESS) {
"ibmf_saa_gid_to_pathrecords: %s, ibmf_status = %d\n",
}
"ibmf_saa_paths_from_gid() exit: result = 0x%x\n",
return (res);
}
/*
* ibmf_saa_name_to_service_record:
* Given a service name, return the service records associated
* with it.
*
* This interface blocks.
*
* Input Arguments:
* ibmf_saa_handle - handle returned from ibmf_sa_session_open()
* name - service name, a null terminated string
* p_key - partition that the service is requested on. This
* value may be wildcarded with IBMF_SAA_PKEY_WC.
* flags - unused
*
* Output Arguments:
* num_records - number of service records returned
* length - size of buffer returned
* result - pointer to buffer of service records returned in
* response
* Return values:
* Error codes are the same as ibmf_sa_access() return values
*
* Upon successful completion, result points to a buffer containing the records.
* and num_records is the number of service records returned. length is the
* size in bytes of the buffer returned in result. If there are no records or
* the call failed the length is 0.
*
* The consumer is responsible for freeing the memory associated with result.
*/
/* ARGSUSED */
int
{
int res;
"ibmf_saa_name_to_service_record() enter\n");
/* check valid handle; in non-debug system ibmf_sa_access() will fail */
"ibmf_saa_gid_to_pathrecords: %s, service_name = %s\n",
"ibmf_saa_name_to_service_record() exit\n");
*num_records = 0;
*length = 0;
return (IBMF_REQ_INVALID);
}
/* copy IB_SVC_NAME_LEN bytes, leaving room at end for null char */
IB_SVC_NAME_LEN-1);
if (p_key != IBMF_SAA_PKEY_WC) {
} else
(void *)result);
if (res != IBMF_SUCCESS) {
"ibmf_saa_name_to_service_record: %s, ibmf_status = %d\n",
}
"ibmf_saa_name_to_service_record() exit: result = 0x%x\n",
return (res);
}
/*
* ibmf_saa_id_to_service_record:
* Given a service id, return the service records associated
* with it.
*
* This interface blocks.
*
* Input Arguments:
* ibmf_saa_handle - handle returned from ibmf_sa_session_open()
* id - service id
* p_key - partition that the service is requested on. This
* value may be wildcarded with IBMF_SAA_PKEY_WC.
* flags - unused
*
* Output Arguments:
* num_records - number of service records returned
* length - size of buffer returned
* result - pointer to buffer of service records returned in
* response
*
* Return values:
* Error codes are the same as ibmf_sa_access() return values
*
* Upon successful completion, result points to a buffer containing the records.
* and num_records is the number of service records returned. length is the
* size in bytes of the buffer returned in result. If there are no records or
* the call failed the length is 0.
*
* The consumer is responsible for freeing the memory associated with result.
*/
/* ARGSUSED */
int
{
int res;
"ibmf_saa_id_to_service_record() enter\n");
/* check valid handle; in non-debug system ibmf_sa_access() will fail */
if (p_key != IBMF_SAA_PKEY_WC) {
} else
(void **)result);
if (res != IBMF_SUCCESS) {
"ibmf_saa_id_to_service_record: %s, ibmf_status = %d\n",
}
"ibmf_saa_id_to_service_record() exit: result = 0x%x\n",
return (res);
}
/*
* ibmf_saa_update_service_record
* Given a pointer to a service record, either insert or delete it
*
* This interface blocks.
*
* Input Arguments:
* ibmf_saa_handle - handle returned from ibmf_sa_session_open()
* service_record - service record is to be inserted or deleted. To
* delete a service record the GID, ID, P_Key, and
* Service Key must match what is in the SA.
* access_type - indicates whether this is an insertion or deletion.
* valid values are IBMF_SAA_UPDATE or IBMF_SAA_DELETE
* flags - unused
*
* Output Arguments
* none
*
* Return values:
* Error codes are the same as ibmf_sa_access() return values
*/
/* ARGSUSED */
int
{
void *result;
int res;
"ibmf_saa_update_service_record() enter\n");
/* check valid handle; in non-debug system ibmf_sa_access() will fail */
if ((access_type != IBMF_SAA_UPDATE) &&
(access_type != IBMF_SAA_DELETE)) {
"ibmf_saa_update_service_record: %s, access_type = 0x%x\n",
"ibmf_saa_update_service_record() exit\n");
return (IBMF_REQ_INVALID);
}
/*
* call ibmf_sa_access with the following special parameters:
* attrid : service_record
* component_mask : RID fields of service record (GID, ID, and P_key)
* and service key
*/
&result);
/* if a valid add request, response buffer should be one service rec */
if (length > sizeof (sa_service_record_t)) {
"ibmf_saa_update_service_record: %s\n",
"SA returned more than one record");
}
}
"ibmf_saa_update_service_record() exit: result = 0x%x\n",
return (res);
}