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 "HBAPort.h" 29 #include "Exceptions.h" 30 #include "Trace.h" 31 #include <iostream> 32 #include <iomanip> 33 #include <cerrno> 34 #include <cstring> 35 #include <sys/types.h> 36 #include <sys/mkdev.h> 37 #include <sys/stat.h> 38 #include <fcntl.h> 39 #include <unistd.h> 40 #include <stropts.h> 41 #include <dirent.h> 42 #include <libdevinfo.h> 43 44 using namespace std; 45 46 /** 47 * Standard definition for general topology lookup (See T11 FC-FS) 48 */ 49 const int HBAPort::RNID_GENERAL_TOPOLOGY_DATA_FORMAT = 0xDF; 50 const uint8_t HBAPort::HBA_NPIV_PORT_MAX = UCHAR_MAX; 51 52 /** 53 * @memo Construct a new default HBA Port 54 */ 55 HBAPort::HBAPort() { 56 } 57 58 /** 59 * @memo Compare two HBA ports for equality 60 * @return TRUE if both ports are the same 61 * @return FALSE if the ports are different 62 * 63 * @doc Comparison is based on Node WWN, Port WWN and path 64 */ 65 bool HBAPort::operator==(HBAPort &comp) { 66 return (this->getPortWWN() == comp.getPortWWN() && 67 this->getNodeWWN() == comp.getNodeWWN() && 68 this->getPath() == comp.getPath()); 69 } 70 71 /** 72 * @memo Validate that the port is still present in the system 73 * @exception UnavailableException if the port is not present 74 * 75 * @doc If the port is still present on the system, the routine 76 * will return normally. If the port is not present 77 * an exception will be thrown. 78 */ 79 void HBAPort::validatePresent() { 80 Trace log("HBAPort::validatePresent"); 81 string path = getPath(); 82 struct stat sbuf; 83 if (stat(path.c_str(), &sbuf) == -1) { 84 if (errno == ENOENT) { 85 throw UnavailableException(); 86 } else { 87 log.debug("Unable to stat %s: %s", path.c_str(), 88 strerror(errno)); 89 throw InternalError(); 90 } 91 } 92 } 93 94 95 /* 96 * structure for di_devlink_walk 97 */ 98 typedef struct walk_devlink { 99 char *path; 100 size_t len; 101 char **linkpp; 102 } walk_devlink_t; 103 104 /** 105 * @memo callback funtion for di_devlink_walk 106 * @postcondition Find matching /dev link for the given path argument. 107 * @param devlink element and callback function argument. 108 * 109 * @doc The input path is expected to not have "/devices". 110 */ 111 extern "C" int 112 get_devlink(di_devlink_t devlink, void *arg) { 113 Trace log("get_devlink"); 114 walk_devlink_t *warg = (walk_devlink_t *)arg; 115 116 /* 117 * When path is specified, it doesn't have minor 118 * name. Therefore, the ../.. prefixes needs to be stripped. 119 */ 120 if (warg->path) { 121 // di_devlink_content contains /devices 122 char *content = (char *)di_devlink_content(devlink); 123 char *start = strstr(content, "/devices"); 124 125 if (start == NULL || 126 strncmp(start, warg->path, warg->len) != 0 || 127 // make it sure the device path has minor name 128 start[warg->len] != ':') 129 return (DI_WALK_CONTINUE); 130 } 131 132 *(warg->linkpp) = strdup(di_devlink_path(devlink)); 133 return (DI_WALK_TERMINATE); 134 } 135 136 /** 137 * @memo Convert /devices paths to /dev sym-link paths. 138 * @postcondition The mapping buffer OSDeviceName paths will be 139 * converted to short names. 140 * @param mappings The target mappings data to convert to 141 * short names 142 * 143 * @doc If no link 144 * is found, the long path is left as is. 145 * Note: The NumberOfEntries field MUST not be greater than the size 146 * of the array passed in. 147 */ 148 void HBAPort::convertToShortNames(PHBA_FCPTARGETMAPPINGV2 mappings) { 149 Trace log("HBAPort::convertToShortNames"); 150 di_devlink_handle_t hdl; 151 walk_devlink_t warg; 152 char *minor_path, *devlinkp; 153 154 if ((hdl = di_devlink_init(NULL, 0)) == NULL) { 155 log.internalError("di_devlink_init failed. Errno:%d", errno); 156 // no need to check further, just return here. 157 return; 158 } 159 160 for (int j = 0; j < mappings->NumberOfEntries; j++) { 161 if (strchr(mappings->entry[j].ScsiId.OSDeviceName, ':')) { 162 // search link for minor node 163 minor_path = mappings->entry[j].ScsiId.OSDeviceName; 164 if (strstr(minor_path, "/devices") != NULL) { 165 minor_path = mappings->entry[j].ScsiId.OSDeviceName + 166 strlen("/devices"); 167 } else { 168 minor_path = mappings->entry[j].ScsiId.OSDeviceName; 169 } 170 warg.path = NULL; 171 } else { 172 minor_path = NULL; 173 if (strstr(mappings->entry[j].ScsiId.OSDeviceName, 174 "/devices") != NULL) { 175 warg.len = strlen (mappings->entry[j].ScsiId.OSDeviceName) - 176 strlen ("/devices"); 177 warg.path = mappings->entry[j].ScsiId.OSDeviceName + 178 strlen ("/devices"); 179 } else { 180 warg.len = strlen(mappings->entry[j].ScsiId.OSDeviceName); 181 warg.path = mappings->entry[j].ScsiId.OSDeviceName; 182 } 183 } 184 185 devlinkp = NULL; 186 warg.linkpp = &devlinkp; 187 (void) di_devlink_walk(hdl, NULL, minor_path, DI_PRIMARY_LINK, 188 (void *)&warg, get_devlink); 189 190 if (devlinkp != NULL) { 191 snprintf(mappings->entry[j].ScsiId.OSDeviceName, 192 sizeof (mappings->entry[j].ScsiId.OSDeviceName), 193 "%s", devlinkp); 194 free(devlinkp); 195 } // else leave OSDeviceName alone. 196 197 } 198 199 di_devlink_fini(&hdl); 200 201 } 202 203 /* 204 * Finds controller path for a give device path. 205 * 206 * Return vale: controller path. 207 */ 208 string HBAPort::lookupControllerPath(string path) { 209 Trace log("lookupControllerPath"); 210 DIR *dp; 211 char buf[MAXPATHLEN]; 212 char node[MAXPATHLEN]; 213 struct dirent **dirpp, *dirp; 214 const char dir[] = "/dev/cfg"; 215 ssize_t count; 216 uchar_t *dir_buf = new uchar_t[sizeof (struct dirent) + MAXPATHLEN]; 217 218 if ((dp = opendir(dir)) == NULL) { 219 string tmp = "Unable to open "; 220 tmp += dir; 221 tmp += "to find controller number."; 222 delete[] (dir_buf); 223 throw IOError(tmp); 224 } 225 226 dirp = (struct dirent *) dir_buf; 227 dirpp = &dirp; 228 while ((readdir_r(dp, dirp, dirpp)) == 0 && dirp != NULL) { 229 if (strcmp(dirp->d_name, ".") == 0 || 230 strcmp(dirp->d_name, "..") == 0) { 231 continue; 232 } 233 sprintf(node, "%s/%s", dir, dirp->d_name); 234 if ((count = readlink(node,buf,sizeof(buf)))) { 235 buf[count] = '\0'; 236 if (strstr(buf, path.c_str())) { 237 string cfg_path = dir; 238 cfg_path += "/"; 239 cfg_path += dirp->d_name; 240 closedir(dp); 241 delete[] (dir_buf); 242 return (cfg_path); 243 } 244 } 245 } 246 247 closedir(dp); 248 delete[] (dir_buf); 249 throw InternalError("Unable to find controller path"); 250 } 251 252 void HBAPort::addPort(HBANPIVPort *port) { 253 Trace log("HBAPort::addPort"); 254 lock(); 255 // support hba with up to UCHAR_MAX number of ports. 256 if (npivportsByIndex.size() + 1 > HBA_NPIV_PORT_MAX) { 257 unlock(); 258 throw InternalError("HBA NPIV Port count exceeds max number of ports"); 259 } 260 261 try { 262 npivportsByWWN[port->getPortWWN()] = port; 263 npivportsByIndex.insert(npivportsByIndex.end(), port); 264 unlock(); 265 } catch (...) { 266 unlock(); 267 throw; 268 } 269 } 270 271 HBANPIVPort* HBAPort::getPort(uint64_t wwn) { 272 Trace log("HBAPort::getPort"); 273 HBANPIVPort *port = NULL; 274 275 lock(); 276 try { 277 if (npivportsByWWN.find(wwn) == npivportsByWWN.end()) { 278 throw IllegalWWNException(); 279 } 280 port = npivportsByWWN[wwn]; 281 unlock(); 282 return (port); 283 } catch (...) { 284 unlock(); 285 throw; 286 } 287 } 288 289 HBANPIVPort* HBAPort::getPortByIndex(int index) { 290 Trace log("HBAPort::getPortByIndex"); 291 lock(); 292 try { 293 if (index >= npivportsByIndex.size() || index < 0) { 294 throw IllegalIndexException(); 295 } 296 HBANPIVPort *tmp = npivportsByIndex[index]; 297 unlock(); 298 return (tmp); 299 } catch (...) { 300 unlock(); 301 throw; 302 } 303 } 304 305