/* * 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 "HBA.h" #include "Exceptions.h" #include "Trace.h" #include #include #include #include #include #include #include #include #include #include #include #define NSECS_PER_SEC 1000000000l #define BUSY_SLEEP NSECS_PER_SEC/10 /* 1/10 second */ #define BUSY_RETRY_TIMER 3000000000UL /* Retry for 3 seconds */ using namespace std; /** * Max number of Adatper ports per HBA that VSL supports. * */ const uint8_t HBA::HBA_PORT_MAX = UCHAR_MAX; /** * @memo Add a new port to this HBA * @precondition Port must be a valid port on this HBA * @postcondition Port will be exposed as one of the ports on this HBA * @exception Throws InternalError when the HBA port count exceeds * max number of ports and throws any underlying exception * @param port The Port to add to this HBA * * @doc When discovering HBAs and their ports, use this * routine to add a port to its existing HBA instance. */ void HBA::addPort(HBAPort* port) { Trace log("HBA::addPort"); lock(); // support hba with up to UCHAR_MAX number of ports. if (portsByIndex.size() + 1 > HBA_PORT_MAX) { unlock(); throw InternalError("HBA Port count exceeds max number of ports"); } try { portsByWWN[port->getPortWWN()] = port; portsByIndex.insert(portsByIndex.end(), port); unlock(); } catch (...) { unlock(); throw; } } /** * @memo Return number of ports to this HBA * @exception No exception for this method. * * @doc Returns the number of ports on this HBA. The max * number of ports that VSL support is up to max uint8_t * size. */ uint8_t HBA::getNumberOfPorts() { Trace log("HBA::getNumberOfPorts"); return (uint8_t)portsByIndex.size(); } /** * @memo Retrieve an HBA port based on a Port WWN * @exception IllegalWWNException Thrown if WWN does not match any * known HBA port. * @return HBAPort* to the port with a matching Port WWN * @param wwn The wwn of the desired HBA port * * @doc Fetch an HBA port based on WWN. If the port is not * found, an exception will be thrown. NULL will never * be returned. */ HBAPort* HBA::getPort(uint64_t wwn) { Trace log("HBA::getPort"); HBAPort *port = NULL; lock(); log.debug("getPort(wwn): WWN %016llx", wwn); try { // Make sure it is in the map if (portsByWWN.find(wwn) == portsByWWN.end()) { throw IllegalWWNException(); } port = portsByWWN[wwn]; unlock(); return (port); } catch (...) { unlock(); throw; } } /** * Iterator for WWN to HBAPort map type */ typedef map::const_iterator CI; /** * @memo Return true if this HBA contains the stated WWN * (node or port) * @exception ... underlying exceptions will be thrown * @return TRUE if the wwn is found * @return FALSE if the wwn is not found * @param wwn The wwn to look for * */ bool HBA::containsWWN(uint64_t wwn) { Trace log("HBA::containsWWN"); lock(); try { for (CI port = portsByWWN.begin(); port != portsByWWN.end(); port++) { if (port->second->getPortWWN() == wwn) { unlock(); return (true); } if (port->second->getNodeWWN() == wwn) { unlock(); return (true); } } unlock(); return (false); } catch (...) { unlock(); throw; } } /** * @memo Fetch the port based on index. * @exception IllegalIndexException Thrown if the index is not valid * @return HBAPort* the port matching the index * @param index - the zero based index of the port to retrieve * */ HBAPort* HBA::getPortByIndex(int index) { Trace log("HBA::getPortByIndex"); lock(); try { log.debug("Port index size %d index %d ", portsByIndex.size(), index); if (index >= portsByIndex.size() || index < 0) { throw IllegalIndexException(); } HBAPort *tmp = portsByIndex[index]; unlock(); return (tmp); } catch (...) { unlock(); throw; } } /** * @memo Compare two HBAs for equality * @precondition Both HBAs should be fully discovered (all ports added) * @exception ... underlying exceptions will be thrown * @return TRUE The two HBA instances represent the same HBA * @return FALSE The two HBA instances are different * * @doc This routine will compare each port within both * HBAs and verify they are the same. The ports must * have been added in the same order. */ bool HBA::operator==(HBA &comp) { Trace log("HBA::operator=="); lock(); try { bool ret = false; if (portsByIndex.size() == comp.portsByIndex.size()) { if (portsByIndex.size() > 0) { ret = (*portsByIndex[0] == *comp.portsByIndex[0]); } } unlock(); return (ret); } catch (...) { unlock(); throw; } } /** * @memo Set the RNID data for all the ports in this HBA * @precondition All ports must be added * @postcondition Each port will have the same RNID value set * @exception ... underlying exceptions will be thrown. Partial failure * is possible and will not be cleaned up. * @param info The RNID information to program for each HBA port * @see HBAPort::setRNID * */ void HBA::setRNID(HBA_MGMTINFO info) { Trace log("HBA::setRNID"); lock(); try { for (CI port = portsByWWN.begin(); port != portsByWWN.end(); port++) { port->second->setRNID(info); } unlock(); } catch (...) { unlock(); throw; } } /** * @memo Verify that this HBA is present on the system * @exception UnavailableException Thrown when HBA not present * @see HBAPort::validatePresent * * @doc This routine is used to verify that a given HBA * has not been removed through dynamic reconfiguration. * If the HBA is present, the routine will return. * If the HBA is not present (if any port is not present) * an exception will be thrown */ void HBA::validatePresent() { Trace log("HBA::validatePresent"); lock(); try { for (CI port = portsByWWN.begin(); port != portsByWWN.end(); port++) { port->second->validatePresent(); } unlock(); } catch (...) { unlock(); throw; } } /** * Opens a file, throwing exceptions on error. */ int HBA::_open(std::string path, int flag) { Trace log("HBA::open"); int fd; errno = 0; if ((fd = open(path.c_str(), flag)) < 0) { log.debug("Unable to open \"%s\" - reason (%d) %s", path.c_str(), errno, strerror(errno)); if (errno == EBUSY) { throw BusyException(); } else if (errno == EAGAIN) { throw TryAgainException(); } else if (errno == ENOTSUP) { throw NotSupportedException(); } else if (errno == ENOENT) { throw UnavailableException(); } else { string msg = "Unable to open "; msg += path; throw IOError(msg); } } return (fd); } /** * Issues IOCTL, throwing exceptions on error. * Note, if the IOCTL succeeds, but some IOCTL specific * error is recorded in the response, this routine * will not throw an exception. */ void HBA::_ioctl(int fd, int type, uchar_t *arg) { Trace log("HBA::ioctl"); hrtime_t cur; int saved_errno = 0; struct timespec ts; hrtime_t start = gethrtime(); hrtime_t end = start + BUSY_RETRY_TIMER; ts.tv_sec = 0; ts.tv_nsec = BUSY_SLEEP; for (cur = start; cur < end; cur = gethrtime()) { errno = 0; if (ioctl(fd, type, arg) != 0) { if (errno == EAGAIN) { saved_errno = errno; nanosleep(&ts, NULL); continue; } else if (errno == EBUSY) { saved_errno = errno; nanosleep(&ts, NULL); continue; } else if (errno == ENOTSUP) { throw NotSupportedException(); } else if (errno == ENOENT) { throw UnavailableException(); } else { throw IOError("IOCTL failed"); } } else { break; } } if (cur >= end) { if (saved_errno == EAGAIN) { throw TryAgainException(); } else if (saved_errno == EBUSY) { throw BusyException(); } else { throw IOError("IOCTL failed"); } } } HBA::~HBA() { Trace log("HBA::~HBA"); for (int i = 0; i < getNumberOfPorts(); i++) { delete (getPortByIndex(i)); } }