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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 353 HBA::~HBA() { 354 Trace log("HBA::~HBA"); 355 for (int i = 0; i < getNumberOfPorts(); i++) { 356 delete (getPortByIndex(i)); 357 } 358 } 359 360