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#include <unistd.h>
28
29#include <TgtFCHBA.h>
30#include <Exceptions.h>
31#include <Trace.h>
32#include <iostream>
33#include <iomanip>
34#include <cerrno>
35#include <cstring>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <fcntl.h>
39#include <unistd.h>
40#include <stropts.h>
41#include <sys/fctio.h>
42#include <sys/fibre-channel/impl/fc_error.h>
43#include <TgtFCHBAPort.h>
44#include <HBAList.h>
45#include <sun_fc.h>
46#include <cstdlib>
47
48using namespace std;
49const string TgtFCHBA::FCT_DRIVER_PATH = "/devices/pseudo/fct@0:admin";
50const string TgtFCHBA::FCT_ADAPTER_NAME_PREFIX = "/devices/pseudo/fct@0";
51const string TgtFCHBA::FCT_DRIVER_PKG	= "SUNWfct";
52const int TgtFCHBA::MAX_FCTIO_MSG_LEN = 256;
53
54TgtFCHBA::TgtFCHBA(string path) : HBA()
55{
56    Trace log("TgtFCHBA::TgtFCHBA");
57    log.debug("Constructing new Target mode HBA (%s)", path.c_str());
58
59    // Add a target FCHBA port. With fct driver architecuture, all target mode
60    // FCHBA will have a single port regardless of the multiport support on
61    // FCA layer.
62    addPort(new TgtFCHBAPort(path));
63    name = "INTERNAL-FAILURE"; // Just in case things go wrong
64    try {
65	    HBA_ADAPTERATTRIBUTES attrs = getHBAAttributes();
66	    name = attrs.Manufacturer;
67	    name += "-";
68	    name += attrs.Model;
69	    name += "-Tgt";
70
71    } catch (HBAException &e) {
72	    log.debug(
73		"Failed to get HBA attribute for %s", path.c_str());
74	    throw e;
75    }
76}
77
78std::string TgtFCHBA::getName()
79{
80    Trace log("TgtFCHBA::getName");
81    return (name);
82}
83
84HBA_ADAPTERATTRIBUTES TgtFCHBA::getHBAAttributes()
85{
86    Trace log("TgtFCHBA::getHBAAttributes");
87    int fd;
88
89    errno = 0;
90    HBAPort *port = getPortByIndex(0);
91
92    HBA_ADAPTERATTRIBUTES attributes;
93    fctio_t			    fctio;
94    fc_tgt_hba_adapter_attributes_t	    attrs;
95    uint64_t	portwwn;
96
97    if ((fd = open(FCT_DRIVER_PATH.c_str(), O_NDELAY | O_RDONLY)) == -1) {
98	// Why did we fail?
99	if (errno == EBUSY) {
100	    throw BusyException();
101	} else if (errno == EAGAIN) {
102	    throw TryAgainException();
103	} else if (errno == ENOTSUP) {
104	    throw NotSupportedException();
105	} else {
106	    throw IOError(port);
107	}
108    }
109
110    try {
111	    std::string path = port->getPath();
112	    string::size_type offset = path.find_last_of(".");
113	    if (offset >= 0) {
114		string portwwnString = path.substr(offset+1);
115		portwwn = strtoull(portwwnString.c_str(), NULL, 16);
116	    }
117    } catch (...) {
118	    throw BadArgumentException();
119    }
120
121    uint64_t en_wwn = htonll(portwwn);
122
123    memset(&fctio, 0, sizeof (fctio));
124    fctio.fctio_cmd = FCTIO_GET_ADAPTER_ATTRIBUTES;
125    fctio.fctio_olen = (uint32_t)(sizeof (attrs));
126    fctio.fctio_xfer = FCTIO_XFER_READ;
127    fctio.fctio_obuf = (uint64_t)(uintptr_t)&attrs;
128    fctio.fctio_ilen = 8;
129    fctio.fctio_ibuf = (uint64_t)(uintptr_t)&en_wwn;
130
131    errno = 0;
132    if (ioctl(fd, FCTIO_CMD, &fctio) != 0) {
133	close(fd);
134	if (errno == EBUSY) {
135	    throw BusyException();
136	} else if (errno == EAGAIN) {
137	    throw TryAgainException();
138	} else if (errno == ENOTSUP) {
139	    throw NotSupportedException();
140	} else {
141	    throw IOError("Unable to fetch adapter attributes");
142	}
143    }
144    close(fd);
145
146    /* Now copy over the payload */
147    attributes.NumberOfPorts = attrs.NumberOfPorts;
148    attributes.VendorSpecificID = attrs.VendorSpecificID;
149    memcpy(attributes.Manufacturer, attrs.Manufacturer, 64);
150    memcpy(attributes.SerialNumber, attrs.SerialNumber, 64);
151    memcpy(attributes.Model, attrs.Model, 256);
152    memcpy(attributes.ModelDescription, attrs.ModelDescription, 256);
153    memcpy(attributes.NodeSymbolicName, attrs.NodeSymbolicName, 256);
154    memcpy(attributes.HardwareVersion, attrs.HardwareVersion, 256);
155    memcpy(attributes.DriverVersion, attrs.DriverVersion, 256);
156    memcpy(attributes.OptionROMVersion, attrs.OptionROMVersion, 256);
157    memcpy(attributes.FirmwareVersion, attrs.FirmwareVersion, 256);
158    memcpy(attributes.DriverName, attrs.DriverName, 256);
159    memcpy(&attributes.NodeWWN, &attrs.NodeWWN, 8);
160
161    return (attributes);
162}
163
164int TgtFCHBA::doForceLip()
165{
166    Trace	 log("TgtFCHBA::doForceLip");
167    int		 fd;
168    HBAPort	*port = getPortByIndex(0);
169    fctio_t	 fctio;
170    uint64_t	 portwwn;
171
172    errno = 0;
173    if ((fd = open(FCT_DRIVER_PATH.c_str(), O_NDELAY | O_RDONLY)) == -1) {
174	if (errno == EBUSY) {
175	    throw BusyException();
176	} else if (errno == EAGAIN) {
177	    throw TryAgainException();
178	} else if (errno == ENOTSUP) {
179	    throw NotSupportedException();
180	} else {
181	    throw IOError(port);
182	}
183    }
184
185    try {
186	    std::string path = port->getPath();
187	    string::size_type offset = path.find_last_of(".");
188	    if (offset >= 0) {
189		string portwwnString = path.substr(offset+1);
190		portwwn = strtoull(portwwnString.c_str(), NULL, 16);
191	    }
192    } catch (...) {
193	    throw BadArgumentException();
194    }
195
196    uint64_t en_wwn = htonll(portwwn);
197    memset(&fctio, 0, sizeof (fctio));
198    fctio.fctio_cmd = FCTIO_FORCE_LIP;
199    fctio.fctio_xfer = FCTIO_XFER_READ;
200    fctio.fctio_ilen = 8;
201    fctio.fctio_ibuf = (uint64_t)(uintptr_t)&en_wwn;
202
203    errno = 0;
204    if (ioctl(fd, FCTIO_CMD, &fctio) != 0) {
205	close(fd);
206	if (errno == EBUSY) {
207	    throw BusyException();
208	} else if (errno == EAGAIN) {
209	    throw TryAgainException();
210	} else if (errno == ENOTSUP) {
211	    throw NotSupportedException();
212	} else {
213	    throw IOError("Unable to reinitialize the link");
214	}
215    } else {
216	close(fd);
217	return ((int)fctio.fctio_errno);
218    }
219}
220
221void TgtFCHBA::loadAdapters(vector<HBA*> &list)
222{
223    Trace log("TgtFCHBA::loadAdapters");
224    fctio_t			fctio;
225    fc_tgt_hba_list_t		*tgthbaList;
226    int			fd;
227    int			size = 64; // default first attempt
228    bool		retry = false;
229    struct stat		sb;
230    int bufSize;
231    char wwnStr[17];
232
233    /* Before we do anything, let's see if FCT is on the system */
234    errno = 0;
235    if (stat(FCT_DRIVER_PATH.c_str(), &sb) != 0) {
236	if (errno == ENOENT) {
237	    log.genericIOError(
238		"The %s driver is not present."
239                " Please install the %s package.",
240		FCT_DRIVER_PATH.c_str(), FCT_DRIVER_PKG.c_str());
241	    throw NotSupportedException();
242	} else {
243	    log.genericIOError(
244		"Can not stat the %s driver for reason \"%s\" "
245		"Unable to get target mode FC adapters.",
246		FCT_DRIVER_PATH.c_str(), strerror(errno));
247	    throw IOError("Unable to stat FCSM driver");
248	}
249    }
250
251
252    /* construct fcio struct */
253    memset(&fctio, 0, sizeof (fctio_t));
254    fctio.fctio_cmd	= FCTIO_ADAPTER_LIST;
255    fctio.fctio_xfer	= FCTIO_XFER_RW;
256
257    /* open the fcsm node so we can send the ioctl to */
258    errno = 0;
259    if ((fd = open(FCT_DRIVER_PATH.c_str(), O_RDONLY)) < 0) {
260	if (errno == EBUSY) {
261	    throw BusyException();
262	} else if (errno == EAGAIN) {
263	    throw TryAgainException();
264	} else if (errno == ENOTSUP) {
265	    throw NotSupportedException();
266	} else if (errno == ENOENT) {
267	    throw UnavailableException();
268	} else {
269	    throw IOError("Unable to open FCT driver");
270	}
271    }
272
273    do {
274	retry = false;
275	errno = 0;
276	bufSize = 8 * (size - 1) + (int) sizeof (fc_tgt_hba_list_t);
277	tgthbaList = (fc_tgt_hba_list_t *)new uchar_t[bufSize];
278	tgthbaList->numPorts = size;
279	fctio.fctio_olen	= bufSize;
280	fctio.fctio_obuf	= (uint64_t)(uintptr_t)tgthbaList;
281	if (ioctl(fd, FCTIO_CMD, &fctio) != 0) {
282	    /* Interpret the fcio error code */
283	    char fcioErrorString[MAX_FCTIO_MSG_LEN] = "";
284
285	    log.genericIOError(
286		"TGT_ADAPTER_LIST failed: "
287		"Errno: \"%s\"",
288		strerror(errno));
289	    delete (tgthbaList);
290	    close(fd);
291	    if (errno == EBUSY) {
292		throw BusyException();
293	    } else if (errno == EAGAIN) {
294		throw TryAgainException();
295	    } else if (errno == ENOTSUP) {
296		throw NotSupportedException();
297	    } else if (errno == ENOENT) {
298		throw UnavailableException();
299	    } else {
300		throw IOError("Unable to build HBA list");
301	    }
302	}
303	if (tgthbaList->numPorts > size) {
304	    log.debug(
305		"Buffer too small for number of target mode HBAs. Retrying.");
306	    size = tgthbaList->numPorts;
307	    retry = true;
308	    delete (tgthbaList);
309	}
310    } while (retry);
311
312    close(fd);
313    log.debug("Detected %d target mode adapters", tgthbaList->numPorts);
314    for (int i = 0; i < tgthbaList->numPorts; i++) {
315	try {
316	    std::string hbapath = FCT_ADAPTER_NAME_PREFIX.c_str();
317	    hbapath += ".";
318	    // move the row with two dimentional uint8 array for WWN
319	    uint64_t tmp = ntohll(*((uint64_t *)&tgthbaList->port_wwn[i][0]));
320	    sprintf(wwnStr, "%llx", tmp);
321	    hbapath += wwnStr;
322
323	    HBA *hba = new TgtFCHBA(hbapath);
324	    list.insert(list.begin(), hba);
325	} catch (...) {
326	    log.debug(
327		"Ignoring partial failure while loading an HBA");
328	}
329    }
330    if (tgthbaList->numPorts > HBAList::HBA_MAX_PER_LIST) {
331	delete(tgthbaList);
332	throw InternalError(
333	    "Exceeds max number of adapters that VSL supports.");
334    }
335    delete (tgthbaList);
336}
337