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 deafult HBA Port
54 */
HBAPort()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 */
operator ==(HBAPort & comp)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 */
validatePresent()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
get_devlink(di_devlink_t devlink,void * arg)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 */
convertToShortNames(PHBA_FCPTARGETMAPPINGV2 mappings)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 */
lookupControllerPath(string path)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
addPort(HBANPIVPort * port)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
getPort(uint64_t wwn)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
getPortByIndex(int index)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