diff options
Diffstat (limited to 'usr/src/lib/sun_sas/common/sun_sas.c')
-rw-r--r-- | usr/src/lib/sun_sas/common/sun_sas.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/usr/src/lib/sun_sas/common/sun_sas.c b/usr/src/lib/sun_sas/common/sun_sas.c new file mode 100644 index 0000000000..152a25a723 --- /dev/null +++ b/usr/src/lib/sun_sas/common/sun_sas.c @@ -0,0 +1,431 @@ +/* + * 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); +} |