1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27
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
41 #define NSECS_PER_SEC 1000000000l
42 #define BUSY_SLEEP NSECS_PER_SEC/10 /* 1/10 second */
43 #define BUSY_RETRY_TIMER 3000000000UL /* Retry for 3 seconds */
44
45 using namespace std;
46
47 /**
48 * Max number of Adatper ports per HBA that VSL supports.
49 *
50 */
51 const uint8_t HBA::HBA_PORT_MAX = UCHAR_MAX;
52
53 /**
54 * @memo Add a new port to this HBA
55 * @precondition Port must be a valid port on this HBA
56 * @postcondition Port will be exposed as one of the ports on this HBA
57 * @exception Throws InternalError when the HBA port count exceeds
58 * max number of ports and throws any underlying exception
59 * @param port The Port to add to this HBA
60 *
61 * @doc When discovering HBAs and their ports, use this
62 * routine to add a port to its existing HBA instance.
63 */
addPort(HBAPort * port)64 void HBA::addPort(HBAPort* port) {
65 Trace log("HBA::addPort");
66 lock();
67 // support hba with up to UCHAR_MAX number of ports.
68 if (portsByIndex.size() + 1 > HBA_PORT_MAX) {
69 unlock();
70 throw InternalError("HBA Port count exceeds max number of ports");
71 }
72
73 try {
74 portsByWWN[port->getPortWWN()] = port;
75 portsByIndex.insert(portsByIndex.end(), port);
76 unlock();
77 } catch (...) {
78 unlock();
79 throw;
80 }
81 }
82
83 /**
84 * @memo Return number of ports to this HBA
85 * @exception No exception for this method.
86 *
87 * @doc Returns the number of ports on this HBA. The max
88 * number of ports that VSL support is up to max uint8_t
89 * size.
90 */
getNumberOfPorts()91 uint8_t HBA::getNumberOfPorts() {
92 Trace log("HBA::getNumberOfPorts");
93 return (uint8_t)portsByIndex.size();
94 }
95
96 /**
97 * @memo Retrieve an HBA port based on a Port WWN
98 * @exception IllegalWWNException Thrown if WWN does not match any
99 * known HBA port.
100 * @return HBAPort* to the port with a matching Port WWN
101 * @param wwn The wwn of the desired HBA port
102 *
103 * @doc Fetch an HBA port based on WWN. If the port is not
104 * found, an exception will be thrown. NULL will never
105 * be returned.
106 */
getPort(uint64_t wwn)107 HBAPort* HBA::getPort(uint64_t wwn) {
108 Trace log("HBA::getPort");
109 HBAPort *port = NULL;
110 lock();
111
112 log.debug("getPort(wwn): WWN %016llx", wwn);
113
114 try {
115 // Make sure it is in the map
116 if (portsByWWN.find(wwn) == portsByWWN.end()) {
117 throw IllegalWWNException();
118 }
119 port = portsByWWN[wwn];
120 unlock();
121 return (port);
122 } catch (...) {
123 unlock();
124 throw;
125 }
126 }
127
128 /**
129 * Iterator for WWN to HBAPort map type
130 */
131 typedef map<uint64_t, HBAPort *>::const_iterator CI;
132
133 /**
134 * @memo Return true if this HBA contains the stated WWN
135 * (node or port)
136 * @exception ... underlying exceptions will be thrown
137 * @return TRUE if the wwn is found
138 * @return FALSE if the wwn is not found
139 * @param wwn The wwn to look for
140 *
141 */
containsWWN(uint64_t wwn)142 bool HBA::containsWWN(uint64_t wwn) {
143 Trace log("HBA::containsWWN");
144 lock();
145
146 try {
147 for (CI port = portsByWWN.begin(); port != portsByWWN.end();
148 port++) {
149 if (port->second->getPortWWN() == wwn) {
150 unlock();
151 return (true);
152 }
153 if (port->second->getNodeWWN() == wwn) {
154 unlock();
155 return (true);
156 }
157 }
158 unlock();
159 return (false);
160 } catch (...) {
161 unlock();
162 throw;
163 }
164 }
165
166 /**
167 * @memo Fetch the port based on index.
168 * @exception IllegalIndexException Thrown if the index is not valid
169 * @return HBAPort* the port matching the index
170 * @param index - the zero based index of the port to retrieve
171 *
172 */
getPortByIndex(int index)173 HBAPort* HBA::getPortByIndex(int index) {
174 Trace log("HBA::getPortByIndex");
175 lock();
176 try {
177 log.debug("Port index size %d index %d ", portsByIndex.size(),
178 index);
179
180 if (index >= portsByIndex.size() || index < 0) {
181 throw IllegalIndexException();
182 }
183
184 HBAPort *tmp = portsByIndex[index];
185 unlock();
186 return (tmp);
187 } catch (...) {
188 unlock();
189 throw;
190 }
191 }
192
193 /**
194 * @memo Compare two HBAs for equality
195 * @precondition Both HBAs should be fully discovered (all ports added)
196 * @exception ... underlying exceptions will be thrown
197 * @return TRUE The two HBA instances represent the same HBA
198 * @return FALSE The two HBA instances are different
199 *
200 * @doc This routine will compare each port within both
201 * HBAs and verify they are the same. The ports must
202 * have been added in the same order.
203 */
operator ==(HBA & comp)204 bool HBA::operator==(HBA &comp) {
205 Trace log("HBA::operator==");
206 lock();
207
208 try {
209 bool ret = false;
210 if (portsByIndex.size() == comp.portsByIndex.size()) {
211 if (portsByIndex.size() > 0) {
212 ret = (*portsByIndex[0] == *comp.portsByIndex[0]);
213 }
214 }
215 unlock();
216 return (ret);
217 } catch (...) {
218 unlock();
219 throw;
220 }
221 }
222
223 /**
224 * @memo Set the RNID data for all the ports in this HBA
225 * @precondition All ports must be added
226 * @postcondition Each port will have the same RNID value set
227 * @exception ... underlying exceptions will be thrown. Partial failure
228 * is possible and will not be cleaned up.
229 * @param info The RNID information to program for each HBA port
230 * @see HBAPort::setRNID
231 *
232 */
setRNID(HBA_MGMTINFO info)233 void HBA::setRNID(HBA_MGMTINFO info) {
234 Trace log("HBA::setRNID");
235 lock();
236
237 try {
238 for (CI port = portsByWWN.begin(); port != portsByWWN.end();
239 port++) {
240 port->second->setRNID(info);
241 }
242 unlock();
243 } catch (...) {
244 unlock();
245 throw;
246 }
247 }
248
249 /**
250 * @memo Verify that this HBA is present on the system
251 * @exception UnavailableException Thrown when HBA not present
252 * @see HBAPort::validatePresent
253 *
254 * @doc This routine is used to verify that a given HBA
255 * has not been removed through dynamic reconfiguration.
256 * If the HBA is present, the routine will return.
257 * If the HBA is not present (if any port is not present)
258 * an exception will be thrown
259 */
validatePresent()260 void HBA::validatePresent() {
261 Trace log("HBA::validatePresent");
262 lock();
263 try {
264 for (CI port = portsByWWN.begin(); port != portsByWWN.end();
265 port++) {
266 port->second->validatePresent();
267 }
268 unlock();
269 } catch (...) {
270 unlock();
271 throw;
272 }
273 }
274
275 /**
276 * Opens a file, throwing exceptions on error.
277 */
_open(std::string path,int flag)278 int HBA::_open(std::string path, int flag) {
279 Trace log("HBA::open");
280 int fd;
281 errno = 0;
282 if ((fd = open(path.c_str(), flag)) < 0) {
283 log.debug("Unable to open \"%s\" - reason (%d) %s",
284 path.c_str(), errno, strerror(errno));
285 if (errno == EBUSY) {
286 throw BusyException();
287 } else if (errno == EAGAIN) {
288 throw TryAgainException();
289 } else if (errno == ENOTSUP) {
290 throw NotSupportedException();
291 } else if (errno == ENOENT) {
292 throw UnavailableException();
293 } else {
294 string msg = "Unable to open ";
295 msg += path;
296 throw IOError(msg);
297 }
298 }
299 return (fd);
300 }
301
302 /**
303 * Issues IOCTL, throwing exceptions on error.
304 * Note, if the IOCTL succeeds, but some IOCTL specific
305 * error is recorded in the response, this routine
306 * will not throw an exception.
307 */
_ioctl(int fd,int type,uchar_t * arg)308 void HBA::_ioctl(int fd, int type, uchar_t *arg) {
309 Trace log("HBA::ioctl");
310 hrtime_t cur;
311 int saved_errno = 0;
312 struct timespec ts;
313
314 hrtime_t start = gethrtime();
315 hrtime_t end = start + BUSY_RETRY_TIMER;
316 ts.tv_sec = 0;
317 ts.tv_nsec = BUSY_SLEEP;
318 for (cur = start; cur < end; cur = gethrtime()) {
319 errno = 0;
320 if (ioctl(fd, type, arg) != 0) {
321 if (errno == EAGAIN) {
322 saved_errno = errno;
323 nanosleep(&ts, NULL);
324 continue;
325 } else if (errno == EBUSY) {
326 saved_errno = errno;
327 nanosleep(&ts, NULL);
328 continue;
329 } else if (errno == ENOTSUP) {
330 throw NotSupportedException();
331 } else if (errno == ENOENT) {
332 throw UnavailableException();
333 } else {
334 throw IOError("IOCTL failed");
335 }
336 } else {
337 break;
338 }
339 }
340 if (cur >= end) {
341 if (saved_errno == EAGAIN) {
342 throw TryAgainException();
343 } else if (saved_errno == EBUSY) {
344 throw BusyException();
345 } else {
346 throw IOError("IOCTL failed");
347 }
348 }
349 }
350
~HBA()351 HBA::~HBA() {
352 Trace log("HBA::~HBA");
353 for (int i = 0; i < getNumberOfPorts(); i++) {
354 delete (getPortByIndex(i));
355 }
356 }
357
358