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