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
45using namespace std;
46
47/**
48 * Max number of Adatper ports per HBA that VSL supports.
49 *
50 */
51const 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 */
64void 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 */
91uint8_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 */
107HBAPort* 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 */
131typedef 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 */
142bool 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 */
173HBAPort* 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 */
204bool 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 */
233void 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 */
260void 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 */
278int 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 */
308void 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
351HBA::~HBA() {
352	Trace log("HBA::~HBA");
353	for (int i = 0; i < getNumberOfPorts(); i++) {
354	    delete (getPortByIndex(i));
355	}
356}
357
358