summaryrefslogtreecommitdiff
path: root/usr/src/lib/sun_fc/common/HBA.cc
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/sun_fc/common/HBA.cc')
-rw-r--r--usr/src/lib/sun_fc/common/HBA.cc357
1 files changed, 357 insertions, 0 deletions
diff --git a/usr/src/lib/sun_fc/common/HBA.cc b/usr/src/lib/sun_fc/common/HBA.cc
new file mode 100644
index 0000000000..8bba2c59c3
--- /dev/null
+++ b/usr/src/lib/sun_fc/common/HBA.cc
@@ -0,0 +1,357 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include "HBA.h"
+#include "Exceptions.h"
+#include "Trace.h"
+#include <iostream>
+#include <iomanip>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <errno.h>
+
+#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<uint64_t, HBAPort *>::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;
+
+ errno = 0;
+ 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()) {
+ 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));
+ }
+}
+