OpenSolaris_b135/lib/sun_sas/common/sun_sas.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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);
}