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