xref: /illumos-gate/usr/src/lib/sun_fc/common/HBA.cc (revision 5c51f124)
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 2008 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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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 	errno = 0;
315 	hrtime_t start = gethrtime();
316 	hrtime_t end = start + BUSY_RETRY_TIMER;
317 	ts.tv_sec = 0;
318 	ts.tv_nsec = BUSY_SLEEP;
319 	for (cur = start; cur < end; cur = gethrtime()) {
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 
351 HBA::~HBA() {
352 	Trace log("HBA::~HBA");
353 	for (int i = 0; i < getNumberOfPorts(); i++) {
354 	    delete (getPortByIndex(i));
355 	}
356 }
357 
358