sun_sas.c revision 9e86db79b7d1bbc5f2f04e99954cbd5eae0e22bb
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/byteorder.h>
#include <sun_sas.h>
/*
* creates a handle each time Sun_sas_OpenAdapter() is called.
*
* a open_handle_struct was created to keep track of which handles are currently
* open. This prevents a user from using an old handle that corresponds to
* an hba that has already been closed.
*/
HBA_HANDLE
CreateHandle(int adapterIndex)
{
const char ROUTINE[] = "CreateHandle";
struct open_handle *new_open_handle;
HBA_UINT32 new_handle_index;
HBA_UINT8 max_handle_wrap = 0;
if (global_hba_head == NULL) {
log(LOG_DEBUG, ROUTINE,
"an error as occurred. global_hba_head is "
"NULL. Library may not be loaded yet.");
return (HANDLE_ERROR);
}
while (RetrieveIndex(open_handle_index) != -1) {
open_handle_index = open_handle_index + 1;
if (open_handle_index == 0) {
/*
* If open_handle_index wraps back to zero again,
* that means all handles are currently in use.
* Spec only allows for 16 bits of handles
*/
if (max_handle_wrap == 1) {
log(LOG_DEBUG, ROUTINE,
"Max number of handles reached.");
return (HANDLE_ERROR);
}
open_handle_index = 1;
max_handle_wrap = 1;
}
}
new_handle_index = open_handle_index;
if ((new_open_handle = (struct open_handle *)calloc(1,
sizeof (struct open_handle))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
return (HANDLE_ERROR);
}
(void) memset(new_open_handle, 0, sizeof (struct open_handle));
new_open_handle->adapterIndex = adapterIndex;
new_open_handle->handle = new_handle_index;
lock(&open_handles_lock);
/* add new open handle struct to the open_handles list */
if (global_hba_head->open_handles == NULL) {
global_hba_head->open_handles = new_open_handle;
} else {
new_open_handle->next = global_hba_head->open_handles;
global_hba_head->open_handles = new_open_handle;
}
unlock(&open_handles_lock);
open_handle_index = open_handle_index + 1;
if (open_handle_index == 0) {
open_handle_index = 1;
}
return (new_handle_index);
}
/*
* given a handle, returns the adapterIndex number.
*
* This functions checkes to see if the given handle corresponds to an open
* HBA. If it does, the adapterIndex is returned.
*/
int
RetrieveIndex(HBA_HANDLE handle)
{
struct open_handle *open_handle_ptr;
lock(&open_handles_lock);
open_handle_ptr = RetrieveOpenHandle(handle);
unlock(&open_handles_lock);
if (open_handle_ptr == NULL) {
return (-1);
}
return (open_handle_ptr->adapterIndex);
}
/*
* Given a handle, returns the open_handle structure
* The routine assumes that the open_handles_lock has already
* been taken.
*/
struct open_handle *
RetrieveOpenHandle(HBA_HANDLE handle)
{
const char ROUTINE[] = "RetrieveOpenHandle";
struct open_handle *open_handle_ptr = NULL;
if (global_hba_head == NULL) {
log(LOG_DEBUG, ROUTINE, "No adpater is found.");
return (NULL);
}
for (open_handle_ptr = global_hba_head->open_handles;
open_handle_ptr != NULL;
open_handle_ptr = open_handle_ptr->next) {
if (open_handle_ptr->handle == handle) {
break;
}
}
return (open_handle_ptr);
}
/*
* Given an adapterIndex, this functions returns a pointer to the handle
* structure. This handle structure holds the hba's information
* Caller must take all_hbas_lock first.
*/
struct sun_sas_hba *
RetrieveHandle(int index)
{
struct sun_sas_hba *hba_ptr = NULL;
for (hba_ptr = global_hba_head; hba_ptr != NULL;
hba_ptr = hba_ptr->next) {
if (hba_ptr->index == index)
break;
}
return (hba_ptr);
}
/*
* Given an adapterIndex, this functions returns a pointer to the handle
* structure and extracts it from the global list.
*
* all_hbas_lock must be taken already.
*/
struct sun_sas_hba *
ExtractHandle(int index)
{
struct sun_sas_hba *last = NULL;
struct sun_sas_hba *hba_ptr = NULL;
for (hba_ptr = global_hba_head;
hba_ptr != NULL;
last = hba_ptr, hba_ptr = hba_ptr->next) {
if (hba_ptr->index == index) {
if (last) {
last->next = hba_ptr->next;
} else {
/* Hmm, must be the head of the list. */
global_hba_head = hba_ptr->next;
}
hba_ptr->next = NULL; /* Zap it to be safe */
break;
}
}
return (hba_ptr);
}
/*
* Given an handle, this functions returns a pointer to the handle structure
* for that hba
*
* Caller must take all_hbas_lock first.
*/
struct sun_sas_hba *
Retrieve_Sun_sasHandle(HBA_HANDLE handle)
{
const char ROUTINE[] = "Retrieve_Sun_sasHandle";
struct sun_sas_hba *handle_struct = NULL;
int index;
/* Retrieve fp device path from handle */
index = RetrieveIndex(handle);
if (index == -1) {
log(LOG_DEBUG, ROUTINE,
"handle could not be found.");
return (handle_struct);
}
lock(&open_handles_lock);
handle_struct = RetrieveHandle(index);
if (handle_struct == NULL) {
log(LOG_DEBUG, ROUTINE,
"could not find index in the handle list.");
unlock(&open_handles_lock);
return (handle_struct);
}
unlock(&open_handles_lock);
return (handle_struct);
}
/*
* Take a mutex lock. The routine will try, and if it fails,
* it will loop for a while and retry. If it fails many times,
* it will start writing to the log file.
*/
void
lock(mutex_t *mp)
{
int status;
int loop = 0;
const char ROUTINE[] = "lock";
do {
loop++;
status = mutex_trylock(mp);
switch (status) {
case 0:
break;
case EFAULT:
log(LOG_DEBUG, ROUTINE,
"Lock failed: fault 0x%x", mp);
break;
case EINVAL:
log(LOG_DEBUG, ROUTINE,
"Lock failed: invalid 0x%x", mp);
break;
case EBUSY:
if (loop > DEADLOCK_WARNING) {
log(LOG_DEBUG, ROUTINE,
"Lock busy, possible deadlock:0x%x",
mp);
}
break;
case EOWNERDEAD:
log(LOG_DEBUG, ROUTINE,
"Lock failed: owner dead 0x%x",
mp);
break;
case ELOCKUNMAPPED:
log(LOG_DEBUG, ROUTINE,
"Lock failed: unmapped 0x%x",
mp);
break;
case ENOTRECOVERABLE:
log(LOG_DEBUG, ROUTINE,
"Lock failed: not recoverable 0x%x", mp);
default:
if (loop > DEADLOCK_WARNING) {
log(LOG_DEBUG, ROUTINE,
"Lock failed: %s 0x%x",
strerror(status), mp);
break;
}
}
if (status) {
(void) sleep(LOCK_SLEEP);
}
} while (status);
}
/*
* Unlock a mutex lock.
*/
void
unlock(mutex_t *mp)
{
(void) mutex_unlock(mp);
}
/*
* Get the Port WWN of the first adapter port. This routine
* is used by the old V1 interfaces so that they can call
* the new V2 interfaces and exhibit the same behavior.
* In the event of error the WWN will be zero.
*
* This function will transition to PAA state but it will not
* verfiy whether data is stale or not
*/
HBA_WWN
getFirstAdapterPortWWN(HBA_HANDLE handle)
{
const char ROUTINE[] = "getFirstAdapterPortWWN";
HBA_WWN pwwn = {0, 0, 0, 0, 0, 0, 0, 0};
struct sun_sas_hba *hba_ptr = NULL;
int index = 0;
HBA_STATUS status;
lock(&all_hbas_lock);
index = RetrieveIndex(handle);
lock(&open_handles_lock);
hba_ptr = RetrieveHandle(index);
if (hba_ptr == NULL) {
log(LOG_DEBUG, ROUTINE, "Invalid handle %08lx", handle);
unlock(&open_handles_lock);
unlock(&all_hbas_lock);
return (pwwn); /* zero WWN */
}
/* Check for stale data */
status = verifyAdapter(hba_ptr);
if (status != HBA_STATUS_OK) {
log(LOG_DEBUG, ROUTINE, "Verify adapter failed");
unlock(&open_handles_lock);
unlock(&all_hbas_lock);
return (pwwn);
}
if (hba_ptr->first_port == NULL) {
/* This is probably an internal failure of the library */
if (hba_ptr->device_path) {
log(LOG_DEBUG, ROUTINE,
"Internal failure: Adapter %s contains no "
"port data", hba_ptr->device_path);
} else {
log(LOG_DEBUG, ROUTINE,
"Internal failure: Adapter at index %d contains "
" no support data", hba_ptr->index);
}
unlock(&open_handles_lock);
unlock(&all_hbas_lock);
return (pwwn); /* zero WWN */
}
/* Set the WWN now and return it */
pwwn = hba_ptr->first_port->port_attributes.PortSpecificAttribute.\
SASPort->LocalSASAddress;
unlock(&open_handles_lock);
unlock(&all_hbas_lock);
return (pwwn);
}
u_longlong_t
wwnConversion(uchar_t *wwn)
{
u_longlong_t tmp;
(void) memcpy(&tmp, wwn, sizeof (u_longlong_t));
tmp = ntohll(tmp);
return (tmp);
}
/*
* Using ioctl to send uscsi command out
*/
HBA_STATUS
send_uscsi_cmd(const char *devpath, struct uscsi_cmd *ucmd)
{
const char ROUTINE[] = "send_uscsi_cmd";
int fd;
HBA_STATUS ret;
/* set default timeout to 200 */
ucmd->uscsi_timeout = 200;
/* reset errno. */
errno = 0;
if ((fd = open(devpath, O_RDONLY | O_NDELAY)) == -1) {
log(LOG_DEBUG, ROUTINE,
"open devpath %s failed: %s", devpath, strerror(errno));
return (HBA_STATUS_ERROR);
}
if (ioctl(fd, USCSICMD, ucmd) == -1) {
if (errno == EBUSY) {
ret = HBA_STATUS_ERROR_BUSY;
} else if (errno == EAGAIN) {
ret = HBA_STATUS_ERROR_TRY_AGAIN;
} else {
ret = HBA_STATUS_ERROR;
}
log(LOG_DEBUG, ROUTINE,
"ioctl send uscsi to devpath: %s failed: %s",
devpath, strerror(errno));
(void) close(fd);
return (ret);
}
(void) close(fd);
return (HBA_STATUS_OK);
}
/*
* Check whether the given Domain Address is valid.
*/
HBA_STATUS
validateDomainAddress(struct sun_sas_port *hba_port_ptr, HBA_WWN DomainAddr)
{
if (hba_port_ptr->first_phy != NULL &&
wwnConversion(hba_port_ptr->first_phy->
phy.domainPortWWN.wwn) ==
wwnConversion(DomainAddr.wwn)) {
return (HBA_STATUS_OK);
}
return (HBA_STATUS_ERROR);
}