xref: /illumos-gate/usr/src/lib/sun_fc/common/HBA.cc (revision f3aaec0a)
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 #include <climits>
41 #include <cstring>
42 
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 */
46 
47 using namespace std;
48 
49 /**
50  * Max number of Adatper ports per HBA that VSL supports.
51  *
52  */
53 const uint8_t HBA::HBA_PORT_MAX = UCHAR_MAX;
54 
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
62  *
63  * @doc		    When discovering HBAs and their ports, use this
64  *		    routine to add a port to its existing HBA instance.
65  */
addPort(HBAPort * port)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");
73 	}
74 
75 	try {
76 	    portsByWWN[port->getPortWWN()] = port;
77 	    portsByIndex.insert(portsByIndex.end(), port);
78 	    unlock();
79 	} catch (...) {
80 	    unlock();
81 	    throw;
82 	}
83 }
84 
85 /**
86  * @memo	    Return number of ports to this HBA
87  * @exception	    No exception for this method.
88  *
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.
92  */
getNumberOfPorts()93 uint8_t HBA::getNumberOfPorts() {
94 	Trace log("HBA::getNumberOfPorts");
95 	return (uint8_t)portsByIndex.size();
96 }
97 
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
104  *
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.
108  */
getPort(uint64_t wwn)109 HBAPort* HBA::getPort(uint64_t wwn) {
110 	Trace log("HBA::getPort");
111 	HBAPort *port = NULL;
112 	lock();
113 
114 	log.debug("getPort(wwn): WWN %016llx", wwn);
115 
116 	try {
117 	    // Make sure it is in the map
118 	    if (portsByWWN.find(wwn) == portsByWWN.end()) {
119 		throw IllegalWWNException();
120 	    }
121 	    port = portsByWWN[wwn];
122 	    unlock();
123 	    return (port);
124 	} catch (...) {
125 	    unlock();
126 	    throw;
127 	}
128 }
129 
130 /**
131  * Iterator for WWN to HBAPort map type
132  */
133 typedef map<uint64_t, HBAPort *>::const_iterator CI;
134 
135 /**
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
142  *
143  */
containsWWN(uint64_t wwn)144 bool HBA::containsWWN(uint64_t wwn) {
145 	Trace log("HBA::containsWWN");
146 	lock();
147 
148 	try {
149 	    for (CI port = portsByWWN.begin(); port != portsByWWN.end();
150 		    port++) {
151 		if (port->second->getPortWWN() == wwn) {
152 		    unlock();
153 		    return (true);
154 		}
155 		if (port->second->getNodeWWN() == wwn) {
156 		    unlock();
157 		    return (true);
158 		}
159 	    }
160 	    unlock();
161 	    return (false);
162 	} catch (...) {
163 	    unlock();
164 	    throw;
165 	}
166 }
167 
168 /**
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
173  *
174  */
getPortByIndex(int index)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);
181 
182 	    if (index >= portsByIndex.size() || index < 0) {
183 		throw IllegalIndexException();
184 	    }
185 
186 	    HBAPort *tmp = portsByIndex[index];
187 	    unlock();
188 	    return (tmp);
189 	} catch (...) {
190 	    unlock();
191 	    throw;
192 	}
193 }
194 
195 /**
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
201  *
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.
205  */
operator ==(HBA & comp)206 bool HBA::operator==(HBA &comp) {
207 	Trace log("HBA::operator==");
208 	lock();
209 
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]);
215 		}
216 	    }
217 	    unlock();
218 	    return (ret);
219 	} catch (...) {
220 	    unlock();
221 	    throw;
222 	}
223 }
224 
225 /**
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
233  *
234  */
setRNID(HBA_MGMTINFO info)235 void HBA::setRNID(HBA_MGMTINFO info) {
236 	Trace log("HBA::setRNID");
237 	lock();
238 
239 	try {
240 	    for (CI port = portsByWWN.begin(); port != portsByWWN.end();
241 		    port++) {
242 		port->second->setRNID(info);
243 	    }
244 	    unlock();
245 	} catch (...) {
246 	    unlock();
247 	    throw;
248 	}
249 }
250 
251 /**
252  * @memo	    Verify that this HBA is present on the system
253  * @exception	    UnavailableException Thrown when HBA not present
254  * @see		    HBAPort::validatePresent
255  *
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
261  */
validatePresent()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();
269 	    }
270 	    unlock();
271 	} catch (...) {
272 	    unlock();
273 	    throw;
274 	}
275 }
276 
277 /**
278  * Opens a file, throwing exceptions on error.
279  */
_open(std::string path,int flag)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);
299 	    }
300 	}
301 	return (fd);
302 }
303 
304 /**
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.
309  */
_ioctl(int fd,int type,uchar_t * arg)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;
315 
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");
337 			}
338 		} else {
339 			break;
340 		}
341 	}
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");
349 		}
350 	}
351 }
352 
~HBA()353 HBA::~HBA() {
354 	Trace log("HBA::~HBA");
355 	for (int i = 0; i < getNumberOfPorts(); i++) {
356 	    delete (getPortByIndex(i));
357 	}
358 }
359 
360