/*
* 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 "Handle.h"
#include "Exceptions.h"
#include "Trace.h"
#include <libdevinfo.h>
#include <iostream>
#include <iomanip>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>
#define MAX_INIT_HANDLE_ID 0x7fff
#define MAX_TGT_HANDLE_ID 0xffff
using namespace std;
/**
* Global lock for list of Handles
*/
pthread_mutex_t Handle::staticLock = PTHREAD_MUTEX_INITIALIZER;
/**
* Tracking for the previous handle we have opened
*/
HBA_HANDLE Handle::prevOpen = 0;
/**
* Tracking for the previous target HBA handle we have opened
*/
HBA_HANDLE Handle::prevTgtOpen = 0x8000;
/**
* Global map from HBA_HANDLE to Handle pointers (our global list)
*/
map<HBA_HANDLE, Handle*> Handle::openHandles;
/**
* @memo Create a new open handle for a specified HBA
* @precondition HBA port(s) must be loaded
* @postcondition An open handle will be present in the global tracking list
* and must be closed at some point to prevent leakage. If no
* handle could be assigned (the track list is full), an
* exception will be thrown. Scope for valid ids in the track
* list is [1, MAX_INIT_HANDLE_ID].
* @param myhba The HBA to open a handle for
*/
Handle::Handle(HBA *myhba) {
map<HBA_HANDLE, Handle*>::iterator mapend;
Trace log("Handle::Handle");
modeVal = INITIATOR;
lock(&staticLock);
mapend = openHandles.end();
/* Start the search for a free id from the previously assigned one */
id = prevOpen + 1;
while (id != prevOpen) {
/* Exceeds the max valid value, continue the search from 1 */
if (id > MAX_INIT_HANDLE_ID)
id = 1;
if (openHandles.find(id) == mapend) {
/* the id is not in use */
break;
}
id ++;
}
if (id == prevOpen) {
/* no usable id for now */
unlock(&staticLock);
throw TryAgainException();
}
prevOpen = id;
hba = myhba;
openHandles[id] = this;
unlock(&staticLock);
}
/**
* @memo Create a new open handle for a specified HBA
* @precondition HBA port(s) must be loaded
* @postcondition An open handle will be present in the global tracking list
* and must be closed at some point to prevent leakage. If no
* handle could be assigned (the track list is full), an
* exception will be thrown. Scope for valid ids in the track
* list is [0x8000, MAX_TGT_HANDLE_ID].
* @param myhba The HBA to open a handle for
* m The mode of HBA to open handle for
*/
#if 0
// appears unused
Handle::Handle(HBA *myhba, MODE m) {
map<HBA_HANDLE, Handle*>::iterator mapend;
Trace log("Handle::Handle");
lock(&staticLock);
modeVal = m;
// if initiator mode call constructor for initiator.
if (m == INITIATOR) {
Handle(myhba, TARGET);
}
mapend = openHandles.end();
/* Start the search for a free id from the previously assigned one */
id = prevTgtOpen + 1;
while (id != prevTgtOpen) {
/*
* Exceeds the max valid target id value,
* continue the search from 1.
*/
if (id > MAX_TGT_HANDLE_ID)
id = 0x8001;
if (openHandles.find(id) == mapend) {
/* the id is not in use */
break;
}
id ++;
}
if (id == prevTgtOpen) {
/* no usable id for now */
unlock(&staticLock);
throw TryAgainException();
}
prevTgtOpen = id;
hba = myhba;
openHandles[id] = this;
unlock(&staticLock);
}
#endif
/**
* @memo Free up the handle (aka, close it)
* @postcondition This handle will be removed from the global list
* @exception ... underlying exceptions will be thrown
*/
Handle::~Handle() {
Trace log("Handle::~Handle");
// Remove this handle from the global list
lock(&staticLock);
try {
openHandles.erase(openHandles.find(getHandle()));
unlock(&staticLock);
} catch (...) {
unlock(&staticLock);
throw;
}
// Now nuke all internal dynamic allocations
typedef map<uint64_t, HandlePort *>::const_iterator CI;
lock();
try {
for (CI port = portHandles.begin(); port != portHandles.end();
port++) {
delete port->second;
}
portHandles.clear();
unlock();
} catch (...) {
unlock();
throw;
}
}
/**
* @memo Locate a handle in the global list of open handles
* @precondition The requested handle must already be open
* @exception InvalidHandleException Thrown if the id does not match
* an open handle
* @return The open Handle
* @param id The id of the handle to fetch
*
* @doc The HBA API uses a simple integer type to represent
* an open Handle, but we use an instance of the Handle
* class. This interface allows a caller to quickly convert
* from the API integer value to related the Handle instance.
*/
Handle* Handle::findHandle(HBA_HANDLE id) {
Trace log("Handle::findHandle(id)");
Handle *tmp = NULL;
lock(&staticLock);
try {
if (openHandles.find(id) == openHandles.end()) {
throw InvalidHandleException();
}
tmp = openHandles[id];
unlock(&staticLock);
return (tmp);
} catch (...) {
unlock(&staticLock);
throw;
}
}
/**
* @memo Find an open handle based on Node or Port WWN
* @precondition The given HBA must already be open
* @exception IllegalWWNException Thrown if no matching open Handle found
* @return The open handle matching the wwn argument
* @param wwn The Node or Port WWN of the HBA whos open handle
* is requested.
*
*/
Handle* Handle::findHandle(uint64_t wwn) {
Trace log("Handle::findHandle(wwn)");
Handle *tmp = NULL;
lock(&staticLock);
try {
for (int i = 0; i < openHandles.size(); i++) {
tmp = openHandles[i];
if (tmp->getHBA()->containsWWN(wwn)) {
unlock(&staticLock);
return (tmp);
}
}
tmp = NULL;
} catch (...) { tmp = NULL; }
unlock(&staticLock);
if (tmp == NULL) {
throw IllegalWWNException();
}
return (tmp);
}
/**
* @memo Refresh underlying index values
* @postcondition All HandlePorts will be reset and prior index values
* will be undefined.
* @exception ... underlying exceptions will be thrown
*
* @doc A number of APIs in the standard interface require
* the use of index values for identifying what "thing"
* to operate on. When dynamic reconfiguration occurs
* these indexes may become inconsistent. This routine
* is called to reset the indexes and signify that the caller
* no longer holds or will refer to any old indexes.
*/
void Handle::refresh() {
Trace log("Handle::refresh");
lock();
try {
typedef map<uint64_t, HandlePort *>::const_iterator CI;
for (CI port = portHandles.begin(); port != portHandles.end();
port++) {
port->second->refresh();
}
unlock();
} catch (...) {
unlock();
throw;
}
}
/**
* @memo Close the specified handle
* @precondition The handle must be open
* @postcondition The handle will be closed and should be discarded.
* @param id The handle to close
*/
void Handle::closeHandle(HBA_HANDLE id) {
Trace log("Handle::closeHandle");
Handle *myHandle = findHandle(id);
delete myHandle;
}
/**
* @memo Get the integer value for return to the API
* @exception ... underlying exceptions will be thrown
* @return The integer value representing the handle
*
* @doc The HBA API uses integer values to represent handles.
* Call this routine to convert a Handle instance into
* its representative integer value.
*/
HBA_HANDLE Handle::getHandle() {
Trace log("Handle::getHandle");
HBA_HANDLE tmp;
lock();
try {
tmp = (HBA_HANDLE) id;
unlock();
return (tmp);
} catch (...) {
unlock();
throw;
}
}
/**
* @memo Compare two handles for equality
* @return TRUE if the handles are the same
* @return FALSE if the handles are different
*/
bool Handle::operator==(Handle comp) {
Trace log("Handle::operator==");
return (this->id == comp.id);
}
/**
* @memo Get the underlying Handle port based on index
* @return The Handle port for the given port index
* @param index The index of the desired port
*/
HandlePort* Handle::getHandlePortByIndex(int index) {
Trace log("Handle::getHandlePortByIndex");
HBAPort* port = hba->getPortByIndex(index);
return (getHandlePort(port->getPortWWN()));
}
/**
* @memo Get the underlying Handle port based on Port wwn
* @exception IllegalWWNException thrown if the wwn is not found
* @return The handle port for the specified WWN
* @param wwn The Port WWN of the HBA port
*
*/
HandlePort* Handle::getHandlePort(uint64_t wwn) {
Trace log("Handle::getHandlePort");
lock();
try {
// Check to see if the wwn is in the map
if (portHandles.find(wwn) == portHandles.end()) {
// Not found, add a new one
HBAPort* port = hba->getPort(wwn);
portHandles[wwn] = new HandlePort(this, hba, port);
}
HandlePort *portHandle = portHandles[wwn];
unlock();
return (portHandle);
} catch (...) {
unlock();
throw;
}
}
/**
* @memo Get the HBA attributes from the underlying HBA
*
* @see HBA::getHBAAttributes
*/
HBA_ADAPTERATTRIBUTES Handle::getHBAAttributes() {
Trace log("Handle::getHBAAttributes");
lock();
try {
HBA_ADAPTERATTRIBUTES attributes = hba->getHBAAttributes();
unlock();
return (attributes);
} catch (...) {
unlock();
throw;
}
}
/**
* @memo Do FORCELIP
*
* @see HBA::doForceLip
*/
int Handle::doForceLip() {
Trace log("Handle::doForceLip");
lock();
try {
int rval = hba->doForceLip();
unlock();
return (rval);
} catch (...) {
unlock();
throw;
}
}
HBA_ADAPTERATTRIBUTES Handle::npivGetHBAAttributes() {
Trace log("Handle::npivGetHBAAttributes");
lock();
try {
HBA_ADAPTERATTRIBUTES attributes = hba->npivGetHBAAttributes();
unlock();
return (attributes);
} catch (...) {
unlock();
throw;
}
}
/**
* @memo Get the HBA port attributes from the HBA
* @see HBAPort::getPortAttributes
* @see HBAPort::getDisoveredAttributes
*
* @doc This routine will return either HBA port
* attributes, or discovered port attributes
*
*/
HBA_PORTATTRIBUTES Handle::getPortAttributes(uint64_t wwn) {
Trace log("Handle::getPortAttributes");
uint64_t tmp;
HBA_PORTATTRIBUTES attributes;
lock();
try {
// Is this a WWN for one of the adapter ports?
if (hba->containsWWN(wwn)) {
attributes = hba->getPort(wwn)->getPortAttributes(tmp);
unlock();
return (attributes);
} else { // Is this a target we know about?
// Loop through all ports and look for the first match
for (int i = 0; i < hba->getNumberOfPorts(); i++) {
try {
attributes =
hba->getPortByIndex(i)->getDiscoveredAttributes(
wwn, tmp);
unlock();
return (attributes);
} catch (HBAException &e) {
continue;
}
}
// If we get to here, then we don't see this WWN on this HBA
throw IllegalWWNException();
}
} catch (...) {
unlock();
throw;
}
}