dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / sun_fc / common / HBA.cc
blob074a522705c0a4762e2cc01c91ff8bb428757dfa
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 #include "HBA.h"
29 #include "Exceptions.h"
30 #include "Trace.h"
31 #include <iostream>
32 #include <iomanip>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <time.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <stropts.h>
39 #include <errno.h>
40 #include <climits>
41 #include <cstring>
43 #define NSECS_PER_SEC 1000000000l
44 #define BUSY_SLEEP NSECS_PER_SEC/10 /* 1/10 second */
45 #define BUSY_RETRY_TIMER 3000000000UL /* Retry for 3 seconds */
47 using namespace std;
49 /**
50 * Max number of Adatper ports per HBA that VSL supports.
53 const uint8_t HBA::HBA_PORT_MAX = UCHAR_MAX;
55 /**
56 * @memo Add a new port to this HBA
57 * @precondition Port must be a valid port on this HBA
58 * @postcondition Port will be exposed as one of the ports on this HBA
59 * @exception Throws InternalError when the HBA port count exceeds
60 * max number of ports and throws any underlying exception
61 * @param port The Port to add to this HBA
63 * @doc When discovering HBAs and their ports, use this
64 * routine to add a port to its existing HBA instance.
66 void HBA::addPort(HBAPort* port) {
67 Trace log("HBA::addPort");
68 lock();
69 // support hba with up to UCHAR_MAX number of ports.
70 if (portsByIndex.size() + 1 > HBA_PORT_MAX) {
71 unlock();
72 throw InternalError("HBA Port count exceeds max number of ports");
75 try {
76 portsByWWN[port->getPortWWN()] = port;
77 portsByIndex.insert(portsByIndex.end(), port);
78 unlock();
79 } catch (...) {
80 unlock();
81 throw;
85 /**
86 * @memo Return number of ports to this HBA
87 * @exception No exception for this method.
89 * @doc Returns the number of ports on this HBA. The max
90 * number of ports that VSL support is up to max uint8_t
91 * size.
93 uint8_t HBA::getNumberOfPorts() {
94 Trace log("HBA::getNumberOfPorts");
95 return (uint8_t)portsByIndex.size();
98 /**
99 * @memo Retrieve an HBA port based on a Port WWN
100 * @exception IllegalWWNException Thrown if WWN does not match any
101 * known HBA port.
102 * @return HBAPort* to the port with a matching Port WWN
103 * @param wwn The wwn of the desired HBA port
105 * @doc Fetch an HBA port based on WWN. If the port is not
106 * found, an exception will be thrown. NULL will never
107 * be returned.
109 HBAPort* HBA::getPort(uint64_t wwn) {
110 Trace log("HBA::getPort");
111 HBAPort *port = NULL;
112 lock();
114 log.debug("getPort(wwn): WWN %016llx", wwn);
116 try {
117 // Make sure it is in the map
118 if (portsByWWN.find(wwn) == portsByWWN.end()) {
119 throw IllegalWWNException();
121 port = portsByWWN[wwn];
122 unlock();
123 return (port);
124 } catch (...) {
125 unlock();
126 throw;
131 * Iterator for WWN to HBAPort map type
133 typedef map<uint64_t, HBAPort *>::const_iterator CI;
136 * @memo Return true if this HBA contains the stated WWN
137 * (node or port)
138 * @exception ... underlying exceptions will be thrown
139 * @return TRUE if the wwn is found
140 * @return FALSE if the wwn is not found
141 * @param wwn The wwn to look for
144 bool HBA::containsWWN(uint64_t wwn) {
145 Trace log("HBA::containsWWN");
146 lock();
148 try {
149 for (CI port = portsByWWN.begin(); port != portsByWWN.end();
150 port++) {
151 if (port->second->getPortWWN() == wwn) {
152 unlock();
153 return (true);
155 if (port->second->getNodeWWN() == wwn) {
156 unlock();
157 return (true);
160 unlock();
161 return (false);
162 } catch (...) {
163 unlock();
164 throw;
169 * @memo Fetch the port based on index.
170 * @exception IllegalIndexException Thrown if the index is not valid
171 * @return HBAPort* the port matching the index
172 * @param index - the zero based index of the port to retrieve
175 HBAPort* HBA::getPortByIndex(int index) {
176 Trace log("HBA::getPortByIndex");
177 lock();
178 try {
179 log.debug("Port index size %d index %d ", portsByIndex.size(),
180 index);
182 if (index >= portsByIndex.size() || index < 0) {
183 throw IllegalIndexException();
186 HBAPort *tmp = portsByIndex[index];
187 unlock();
188 return (tmp);
189 } catch (...) {
190 unlock();
191 throw;
196 * @memo Compare two HBAs for equality
197 * @precondition Both HBAs should be fully discovered (all ports added)
198 * @exception ... underlying exceptions will be thrown
199 * @return TRUE The two HBA instances represent the same HBA
200 * @return FALSE The two HBA instances are different
202 * @doc This routine will compare each port within both
203 * HBAs and verify they are the same. The ports must
204 * have been added in the same order.
206 bool HBA::operator==(HBA &comp) {
207 Trace log("HBA::operator==");
208 lock();
210 try {
211 bool ret = false;
212 if (portsByIndex.size() == comp.portsByIndex.size()) {
213 if (portsByIndex.size() > 0) {
214 ret = (*portsByIndex[0] == *comp.portsByIndex[0]);
217 unlock();
218 return (ret);
219 } catch (...) {
220 unlock();
221 throw;
226 * @memo Set the RNID data for all the ports in this HBA
227 * @precondition All ports must be added
228 * @postcondition Each port will have the same RNID value set
229 * @exception ... underlying exceptions will be thrown. Partial failure
230 * is possible and will not be cleaned up.
231 * @param info The RNID information to program for each HBA port
232 * @see HBAPort::setRNID
235 void HBA::setRNID(HBA_MGMTINFO info) {
236 Trace log("HBA::setRNID");
237 lock();
239 try {
240 for (CI port = portsByWWN.begin(); port != portsByWWN.end();
241 port++) {
242 port->second->setRNID(info);
244 unlock();
245 } catch (...) {
246 unlock();
247 throw;
252 * @memo Verify that this HBA is present on the system
253 * @exception UnavailableException Thrown when HBA not present
254 * @see HBAPort::validatePresent
256 * @doc This routine is used to verify that a given HBA
257 * has not been removed through dynamic reconfiguration.
258 * If the HBA is present, the routine will return.
259 * If the HBA is not present (if any port is not present)
260 * an exception will be thrown
262 void HBA::validatePresent() {
263 Trace log("HBA::validatePresent");
264 lock();
265 try {
266 for (CI port = portsByWWN.begin(); port != portsByWWN.end();
267 port++) {
268 port->second->validatePresent();
270 unlock();
271 } catch (...) {
272 unlock();
273 throw;
278 * Opens a file, throwing exceptions on error.
280 int HBA::_open(std::string path, int flag) {
281 Trace log("HBA::open");
282 int fd;
283 errno = 0;
284 if ((fd = open(path.c_str(), flag)) < 0) {
285 log.debug("Unable to open \"%s\" - reason (%d) %s",
286 path.c_str(), errno, strerror(errno));
287 if (errno == EBUSY) {
288 throw BusyException();
289 } else if (errno == EAGAIN) {
290 throw TryAgainException();
291 } else if (errno == ENOTSUP) {
292 throw NotSupportedException();
293 } else if (errno == ENOENT) {
294 throw UnavailableException();
295 } else {
296 string msg = "Unable to open ";
297 msg += path;
298 throw IOError(msg);
301 return (fd);
305 * Issues IOCTL, throwing exceptions on error.
306 * Note, if the IOCTL succeeds, but some IOCTL specific
307 * error is recorded in the response, this routine
308 * will not throw an exception.
310 void HBA::_ioctl(int fd, int type, uchar_t *arg) {
311 Trace log("HBA::ioctl");
312 hrtime_t cur;
313 int saved_errno = 0;
314 struct timespec ts;
316 hrtime_t start = gethrtime();
317 hrtime_t end = start + BUSY_RETRY_TIMER;
318 ts.tv_sec = 0;
319 ts.tv_nsec = BUSY_SLEEP;
320 for (cur = start; cur < end; cur = gethrtime()) {
321 errno = 0;
322 if (ioctl(fd, type, arg) != 0) {
323 if (errno == EAGAIN) {
324 saved_errno = errno;
325 nanosleep(&ts, NULL);
326 continue;
327 } else if (errno == EBUSY) {
328 saved_errno = errno;
329 nanosleep(&ts, NULL);
330 continue;
331 } else if (errno == ENOTSUP) {
332 throw NotSupportedException();
333 } else if (errno == ENOENT) {
334 throw UnavailableException();
335 } else {
336 throw IOError("IOCTL failed");
338 } else {
339 break;
342 if (cur >= end) {
343 if (saved_errno == EAGAIN) {
344 throw TryAgainException();
345 } else if (saved_errno == EBUSY) {
346 throw BusyException();
347 } else {
348 throw IOError("IOCTL failed");
353 HBA::~HBA() {
354 Trace log("HBA::~HBA");
355 for (int i = 0; i < getNumberOfPorts(); i++) {
356 delete (getPortByIndex(i));