root/usr/src/lib/sun_fc/common/FCHBA.cc
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <unistd.h>

#include <FCHBA.h>
#include <Exceptions.h>
#include <Trace.h>
#include <iostream>
#include <iomanip>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>
#include <sys/fibre-channel/fcio.h>
#include <sys/fibre-channel/ulp/fcsm.h>
#include <FCHBAPort.h>
#include <HBAList.h>

#define EXCPT_RETRY_COUNT    10

using namespace std;
const string FCHBA::FCSM_DRIVER_PATH = "/devices/pseudo/fcsm@0:fcsm";
const string FCHBA::FCSM_DRIVER_PKG     = "SUNWfcsm";
const int FCHBA::MAX_FCIO_MSG_LEN = 256;

FCHBA::FCHBA(string path) : HBA() {
    Trace log("FCHBA::FCHBA");
    log.debug("Constructing new HBA (%s)", path.c_str());

    // Add first port
    addPort(new FCHBAPort(path));

    name = "INTERNAL-FAILURE"; // Just in case things go wrong
    try {
        HBA_ADAPTERATTRIBUTES attrs = getHBAAttributes();
        name = attrs.Manufacturer;
        name += "-";
        name += attrs.Model;

        // Grab any other ports on this adapter
        for (int i = 1; i < attrs.NumberOfPorts; i++) {
            fcio_t                      fcio;
            int                 fd;
            char                nextPath[MAXPATHLEN];

            log.debug("Fetching other port %d", i);

            // construct fcio struct
            memset(&fcio, 0, sizeof (fcio_t));
            memset(nextPath, 0, sizeof (nextPath));
            fcio.fcio_cmd       = FCIO_GET_OTHER_ADAPTER_PORTS;
            fcio.fcio_xfer      = FCIO_XFER_RW;

            fcio.fcio_olen      = MAXPATHLEN;
            fcio.fcio_obuf      = (char *)nextPath;
            fcio.fcio_ilen      = sizeof (i);
            fcio.fcio_ibuf      = (char *)&i;

            // open the fcsm node so we can send the ioctl to
            errno = 0;
            HBAPort *port = getPortByIndex(0);
            if ((fd = open(port->getPath().c_str(), O_NDELAY | O_RDONLY)) ==
                    -1) {
                log.debug("Unable to open %d opened (%s)", i,
                port->getPath().c_str());
                if (errno == EBUSY) {
                    throw BusyException();
                } else if (errno == EAGAIN) {
                    throw TryAgainException();
                } else if (errno == ENOTSUP) {
                    throw NotSupportedException();
                } else if (errno == ENOENT) {
                    throw UnavailableException();
                } else {
                    throw IOError("Unable to open FCSM driver");
                }
            }
            log.debug("Other port %d opened", i);

            errno = 0;
            if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
                // Interpret the fcio error code
                char fcioErrorString[MAX_FCIO_MSG_LEN] = "";

                log.genericIOError(
                    "ADAPTER_LIST failed: "
                    "Errno: \"%s\"",
                    strerror(errno));
                close(fd);
                if (errno == EBUSY) {
                    throw BusyException();
                } else if (errno == EAGAIN) {
                    throw TryAgainException();
                } else if (errno == ENOTSUP) {
                    throw NotSupportedException();
                } else if (errno == ENOENT) {
                    throw UnavailableException();
                } else {
                    throw IOError("Unable to build HBA list");
                }
            }
            close(fd);
            log.debug("About to add port %d (%s)", i, nextPath);
            addPort(new FCHBAPort(nextPath));
        }
    } catch (BusyException &e) {
        throw e;
    } catch (TryAgainException &e) {
        throw e;
    } catch (UnavailableException &e) {
        throw e;
    } catch (HBAException &e) {
        log.internalError(
                "Unable to construct HBA.");
        throw e;
    }
}

std::string FCHBA::getName() {
    Trace log("FCHBA::getName");
    return (name);
}

HBA_ADAPTERATTRIBUTES FCHBA::getHBAAttributes() {
    Trace log("FCHBA::getHBAAttributes");
    int fd;

    errno = 0;
    HBAPort *port = getPortByIndex(0);
    if ((fd = open(port->getPath().c_str(), O_NDELAY | O_RDONLY)) == -1) {
        // Why did we fail?
        if (errno == EBUSY) {
            throw BusyException();
        } else if (errno == EAGAIN) {
            throw TryAgainException();
        } else if (errno == ENOTSUP) {
            throw NotSupportedException();
        } else {
            throw IOError(port);
        }
    }

    HBA_ADAPTERATTRIBUTES attributes;
    fcio_t                          fcio;
    fc_hba_adapter_attributes_t     attrs;

    memset(&fcio, 0, sizeof (fcio));

    fcio.fcio_cmd = FCIO_GET_ADAPTER_ATTRIBUTES;
    fcio.fcio_olen = sizeof (attrs);
    fcio.fcio_xfer = FCIO_XFER_READ;
    fcio.fcio_obuf = (caddr_t)&attrs;


    errno = 0;
    if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
        close(fd);
        if (errno == EBUSY) {
            throw BusyException();
        } else if (errno == EAGAIN) {
            throw TryAgainException();
        } else if (errno == ENOTSUP) {
            throw NotSupportedException();
        } else {
            throw IOError("Unable to fetch adapter attributes");
        }
    }
    close(fd);

    /* Now copy over the payload */
    attributes.NumberOfPorts = attrs.NumberOfPorts;
    attributes.VendorSpecificID = attrs.VendorSpecificID;
    memcpy(attributes.Manufacturer, attrs.Manufacturer, 64);
    memcpy(attributes.SerialNumber, attrs.SerialNumber, 64);
    memcpy(attributes.Model, attrs.Model, 256);
    memcpy(attributes.ModelDescription, attrs.ModelDescription, 256);
    memcpy(attributes.NodeSymbolicName, attrs.NodeSymbolicName, 256);
    memcpy(attributes.HardwareVersion, attrs.HardwareVersion, 256);
    memcpy(attributes.DriverVersion, attrs.DriverVersion, 256);
    memcpy(attributes.OptionROMVersion, attrs.OptionROMVersion, 256);
    memcpy(attributes.FirmwareVersion, attrs.FirmwareVersion, 256);
    memcpy(attributes.DriverName, attrs.DriverName, 256);
    memcpy(&attributes.NodeWWN, &attrs.NodeWWN, 8);

    return (attributes);
}

int FCHBA::doForceLip() {
    Trace        log("FCHBA::doForceLip");
    int          fd;
    fcio_t       fcio;
    uint64_t     wwn  = 0;
    HBAPort     *port = getPortByIndex(0);

    errno = 0;
    if ((fd = open(port->getPath().c_str(), O_RDONLY | O_EXCL)) == -1) {
        if (errno == EBUSY) {
            throw BusyException();
        } else if (errno == EAGAIN) {
            throw TryAgainException();
        } else if (errno == ENOTSUP) {
            throw NotSupportedException();
        } else {
            throw IOError(port);
        }
    }

    memset(&fcio, 0, sizeof (fcio));
    fcio.fcio_cmd = FCIO_RESET_LINK;
    fcio.fcio_xfer = FCIO_XFER_WRITE;
    fcio.fcio_ilen = sizeof (wwn);
    fcio.fcio_ibuf = (caddr_t)&wwn;

    errno = 0;
    if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
        close(fd);

        if (errno == EBUSY) {
            throw BusyException();
        } else if (errno == EAGAIN) {
            throw TryAgainException();
        } else if (errno == ENOTSUP) {
            throw NotSupportedException();
        } else {
            throw IOError("Unable to reinitialize the link");
        }
    } else {
        close(fd);
        return (fcio.fcio_errno);
    }
}

HBA_ADAPTERATTRIBUTES FCHBA::npivGetHBAAttributes() {
        Trace log("FCHBA::npivGetHBAAttributes");
        int fd;

        errno = 0;
        HBAPort *port = getPortByIndex(0);
        if ((fd = open(port->getPath().c_str(), O_NDELAY | O_RDONLY)) == -1) {
                // Why did we fail?
                if (errno == EBUSY) {
                        throw BusyException();
                } else if (errno == EAGAIN) {
                        throw TryAgainException();
                } else if (errno == ENOTSUP) {
                        throw NotSupportedException();
                } else {
                        throw IOError(port);
                }
        }

        HBA_ADAPTERATTRIBUTES attributes;
        fcio_t fcio;
        fc_hba_adapter_attributes_t attrs;

        memset(&fcio, 0, sizeof (fcio));
        fcio.fcio_cmd = FCIO_NPIV_GET_ADAPTER_ATTRIBUTES;
        fcio.fcio_olen = sizeof (attrs);
        fcio.fcio_xfer = FCIO_XFER_READ;
        fcio.fcio_obuf = (caddr_t)&attrs;
        errno = 0;

        if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
                close(fd);
                if (errno == EBUSY) {
                        throw BusyException();
                } else if (errno == EAGAIN) {
                        throw TryAgainException();
                } else if (errno == ENOTSUP) {
                        throw NotSupportedException();
                } else {
                        throw IOError("Unable to fetch adapter attributes");
                }
        }
        close(fd);

        /* Now copy over the payload */
        attributes.NumberOfPorts = attrs.NumberOfPorts;
        attributes.VendorSpecificID = attrs.VendorSpecificID;
        memcpy(attributes.Manufacturer, attrs.Manufacturer, 64);
        memcpy(attributes.SerialNumber, attrs.SerialNumber, 64);
        memcpy(attributes.Model, attrs.Model, 256);
        memcpy(attributes.ModelDescription, attrs.ModelDescription, 256);
        memcpy(attributes.NodeSymbolicName, attrs.NodeSymbolicName, 256);
        memcpy(attributes.HardwareVersion, attrs.HardwareVersion, 256);
        memcpy(attributes.DriverVersion, attrs.DriverVersion, 256);
        memcpy(attributes.OptionROMVersion, attrs.OptionROMVersion, 256);
        memcpy(attributes.FirmwareVersion, attrs.FirmwareVersion, 256);
        memcpy(attributes.DriverName, attrs.DriverName, 256);
        memcpy(&attributes.NodeWWN, &attrs.NodeWWN, 8);

        return (attributes);
}

void FCHBA::loadAdapters(vector<HBA*> &list) {
    Trace log("FCHBA::loadAdapters");
    fcio_t                      fcio;
    fc_hba_list_t               *pathList;
    int                 fd;
    int                 size = 64; // default first attempt
    bool                retry = false;
    struct stat         sb;
    int bufSize;

    /* Before we do anything, let's see if FCSM is on the system */
    errno = 0;
    if (stat(FCSM_DRIVER_PATH.c_str(), &sb) != 0) {
        if (errno == ENOENT) {
            log.genericIOError(
                "The %s driver is not present. Unable to issue "
                "CT commands. Please install the %s package.",
                FCSM_DRIVER_PATH.c_str(), FCSM_DRIVER_PKG.c_str());
            throw NotSupportedException();
        } else {
            log.genericIOError(
                "Can not stat the %s driver for reason \"%s\" "
                "Unable to issue CT commands.",
                FCSM_DRIVER_PATH.c_str(), strerror(errno));
            throw IOError("Unable to stat FCSM driver");
        }
    }


    /* construct fcio struct */
    memset(&fcio, 0, sizeof (fcio_t));
    fcio.fcio_cmd       = FCSMIO_ADAPTER_LIST;
    fcio.fcio_xfer      = FCIO_XFER_RW;


    /* open the fcsm node so we can send the ioctl to */
    errno = 0;
    if ((fd = open(FCSM_DRIVER_PATH.c_str(), O_RDONLY)) < 0) {
        if (errno == EBUSY) {
            throw BusyException();
        } else if (errno == EAGAIN) {
            throw TryAgainException();
        } else if (errno == ENOTSUP) {
            throw NotSupportedException();
        } else if (errno == ENOENT) {
            throw UnavailableException();
        } else {
            throw IOError("Unable to open FCSM driver");
        }
    }

    do {
        retry = false;
        errno = 0;
        bufSize = MAXPATHLEN * size + (int) sizeof (fc_hba_list_t) - 1;
        pathList = (fc_hba_list_t *)new uchar_t[bufSize];
        pathList->numAdapters = size;
        fcio.fcio_olen  = bufSize;
        fcio.fcio_obuf  = (char *)pathList;
        if (ioctl(fd, FCSMIO_CMD, &fcio) != 0) {
            /* Interpret the fcio error code */
            char fcioErrorString[MAX_FCIO_MSG_LEN] = "";

            log.genericIOError(
                "ADAPTER_LIST failed: "
                "Errno: \"%s\"",
                strerror(errno));
            delete[] (pathList);
            close(fd);
            if (errno == EBUSY) {
                throw BusyException();
            } else if (errno == EAGAIN) {
                throw TryAgainException();
            } else if (errno == ENOTSUP) {
                throw NotSupportedException();
            } else if (errno == ENOENT) {
                throw UnavailableException();
            } else {
                throw IOError("Unable to build HBA list");
            }
        }
        if (pathList->numAdapters > size) {
            log.debug(
                "Buffer too small for number of HBAs. Retrying.");
            size = pathList->numAdapters;
            retry = true;
            delete[] (pathList);
        }
    } while (retry);

    close(fd);
    log.debug("Detected %d adapters", pathList->numAdapters);
    for (int i = 0, times =0; i < pathList->numAdapters;) {
        try {
            HBA *hba = new FCHBA(pathList->hbaPaths[i]);
            list.insert(list.begin(), hba);
            i++;
        } catch (BusyException &e) {
            sleep(1);
            if (times++ > EXCPT_RETRY_COUNT) {
                i++;
                times = 0;
            }
            continue;
        } catch (TryAgainException &e) {
            sleep(1);
            if (times++ > EXCPT_RETRY_COUNT) {
                i++;
                times = 0;
            }
            continue;
        } catch (UnavailableException &e) {
            sleep(1);
            if (times++ > EXCPT_RETRY_COUNT) {
                i++;
                times = 0;
            }
            continue;
        } catch (HBAException &e) {
            i++;
            times = 0;
            log.debug(
                "Ignoring partial failure while loading an HBA");
        }
    }
    if (pathList->numAdapters > HBAList::HBA_MAX_PER_LIST) {
        delete[](pathList);
        throw InternalError(
            "Exceeds max number of adapters that VSL supports.");
    }
    delete[] (pathList);
}