root/usr/src/lib/sun_fc/common/Handle.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 "Handle.h"
#include "Exceptions.h"
#include "Trace.h"
#include <libdevinfo.h>
#include <iostream>
#include <iomanip>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>

#define MAX_INIT_HANDLE_ID      0x7fff
#define MAX_TGT_HANDLE_ID       0xffff

using namespace std;

/**
 * Global lock for list of Handles
 */
pthread_mutex_t Handle::staticLock = PTHREAD_MUTEX_INITIALIZER;

/**
 * Tracking for the previous handle we have opened
 */
HBA_HANDLE Handle::prevOpen = 0;

/**
 * Tracking for the previous target HBA handle we have opened
 */
HBA_HANDLE Handle::prevTgtOpen = 0x8000;

/**
 * Global map from HBA_HANDLE to Handle pointers (our global list)
 */
map<HBA_HANDLE, Handle*> Handle::openHandles;

/**
 * @memo            Create a new open handle for a specified HBA
 * @precondition    HBA port(s) must be loaded
 * @postcondition   An open handle will be present in the global tracking list
 *                  and must be closed at some point to prevent leakage. If no
 *                  handle could be assigned (the track list is full), an
 *                  exception will be thrown. Scope for valid ids in the track
 *                  list is [1, MAX_INIT_HANDLE_ID].
 * @param           myhba The HBA to open a handle for
 */
Handle::Handle(HBA *myhba) {
        map<HBA_HANDLE, Handle*>::iterator mapend;
        Trace log("Handle::Handle");
        modeVal = INITIATOR;
        lock(&staticLock);
        mapend = openHandles.end();
        /* Start the search for a free id from the previously assigned one */
        id = prevOpen + 1;
        while (id != prevOpen) {
                /* Exceeds the max valid value, continue the search from 1 */
                if (id > MAX_INIT_HANDLE_ID)
                        id = 1;

                if (openHandles.find(id) == mapend) {
                        /* the id is not in use */
                        break;
                }
                id ++;
        }
        if (id == prevOpen) {
                /* no usable id for now */
                unlock(&staticLock);
                throw TryAgainException();
        }
        prevOpen = id;
        hba = myhba;
        openHandles[id] = this;
        unlock(&staticLock);
}

/**
 * @memo            Create a new open handle for a specified HBA
 * @precondition    HBA port(s) must be loaded
 * @postcondition   An open handle will be present in the global tracking list
 *                  and must be closed at some point to prevent leakage. If no
 *                  handle could be assigned (the track list is full), an
 *                  exception will be thrown. Scope for valid ids in the track
 *                  list is [0x8000, MAX_TGT_HANDLE_ID].
 * @param           myhba The HBA to open a handle for
 *                  m The mode of HBA to open handle for
 */
#if 0
// appears unused
Handle::Handle(HBA *myhba, MODE m) {
        map<HBA_HANDLE, Handle*>::iterator mapend;
        Trace log("Handle::Handle");
        lock(&staticLock);
        modeVal = m;


        // if initiator mode call constructor for initiator.
        if (m == INITIATOR) {
                Handle(myhba, TARGET);
        }

        mapend = openHandles.end();
        /* Start the search for a free id from the previously assigned one */
        id = prevTgtOpen + 1;
        while (id != prevTgtOpen) {
                /*
                 * Exceeds the max valid target id value,
                 * continue the search from 1.
                 */
                if (id > MAX_TGT_HANDLE_ID)
                        id = 0x8001;

                if (openHandles.find(id) == mapend) {
                        /* the id is not in use */
                        break;
                }
                id ++;
        }
        if (id == prevTgtOpen) {
                /* no usable id for now */
                unlock(&staticLock);
                throw TryAgainException();
        }
        prevTgtOpen = id;
        hba = myhba;
        openHandles[id] = this;
        unlock(&staticLock);
}
#endif
/**
 * @memo            Free up the handle (aka, close it)
 * @postcondition   This handle will be removed from the global list
 * @exception       ... underlying exceptions will be thrown
 */
Handle::~Handle() {
        Trace log("Handle::~Handle");
        // Remove this handle from the global list
        lock(&staticLock);
        try {
            openHandles.erase(openHandles.find(getHandle()));
            unlock(&staticLock);
        } catch (...) {
            unlock(&staticLock);
            throw;
        }

        // Now nuke all internal dynamic allocations
        typedef map<uint64_t, HandlePort *>::const_iterator CI;
        lock();
        try {
            for (CI port = portHandles.begin(); port != portHandles.end();
                    port++) {
                delete port->second;
            }
            portHandles.clear();
            unlock();
        } catch (...) {
            unlock();
            throw;
        }
}

/**
 * @memo            Locate a handle in the global list of open handles
 * @precondition    The requested handle must already be open
 * @exception       InvalidHandleException Thrown if the id does not match
 *                  an open handle
 * @return          The open Handle
 * @param           id The id of the handle to fetch
 *
 * @doc             The HBA API uses a simple integer type to represent
 *                  an open Handle, but we use an instance of the Handle
 *                  class.  This interface allows a caller to quickly convert
 *                  from the API integer value to related the Handle instance.
 */
Handle* Handle::findHandle(HBA_HANDLE id) {
        Trace log("Handle::findHandle(id)");
        Handle *tmp = NULL;
        lock(&staticLock);
        try {
            if (openHandles.find(id) == openHandles.end()) {
                throw InvalidHandleException();
            }
            tmp = openHandles[id];
            unlock(&staticLock);
            return (tmp);
        } catch (...) {
            unlock(&staticLock);
            throw;
        }
}

/**
 * @memo            Find an open handle based on Node or Port WWN
 * @precondition    The given HBA must already be open
 * @exception       IllegalWWNException Thrown if no matching open Handle found
 * @return          The open handle matching the wwn argument
 * @param           wwn The Node or Port WWN of the HBA whos open handle
 *                  is requested.
 *
 */
Handle* Handle::findHandle(uint64_t wwn) {
        Trace log("Handle::findHandle(wwn)");
        Handle *tmp = NULL;
        lock(&staticLock);
        try {
            for (int i = 0; i < openHandles.size(); i++) {
                tmp = openHandles[i];
                if (tmp->getHBA()->containsWWN(wwn)) {
                    unlock(&staticLock);
                    return (tmp);
                }
            }
            tmp = NULL;
        } catch (...) { tmp = NULL; }
        unlock(&staticLock);
        if (tmp == NULL) {
            throw IllegalWWNException();
        }
        return (tmp);
}

/**
 * @memo            Refresh underlying index values
 * @postcondition   All HandlePorts will be reset and prior index values
 *                  will be undefined.
 * @exception       ... underlying exceptions will be thrown
 *
 * @doc             A number of APIs in the standard interface require
 *                  the use of index values for identifying what "thing"
 *                  to operate on.  When dynamic reconfiguration occurs
 *                  these indexes may become inconsistent.  This routine
 *                  is called to reset the indexes and signify that the caller
 *                  no longer holds or will refer to any old indexes.
 */
void Handle::refresh() {
        Trace log("Handle::refresh");
        lock();
        try {
            typedef map<uint64_t, HandlePort *>::const_iterator CI;
            for (CI port = portHandles.begin(); port != portHandles.end();
                    port++) {
                port->second->refresh();
            }
            unlock();
        } catch (...) {
            unlock();
            throw;
        }
}

/**
 * @memo            Close the specified handle
 * @precondition    The handle must be open
 * @postcondition   The handle will be closed and should be discarded.
 * @param           id The handle to close
 */
void Handle::closeHandle(HBA_HANDLE id) {
        Trace log("Handle::closeHandle");
        Handle *myHandle = findHandle(id);
        delete myHandle;
}

/**
 * @memo            Get the integer value for return to the API
 * @exception       ... underlying exceptions will be thrown
 * @return          The integer value representing the handle
 *
 * @doc             The HBA API uses integer values to represent handles.
 *                  Call this routine to convert a Handle instance into
 *                  its representative integer value.
 */
HBA_HANDLE Handle::getHandle() {
        Trace log("Handle::getHandle");
        HBA_HANDLE tmp;
        lock();
        try {
            tmp = (HBA_HANDLE) id;
            unlock();
            return (tmp);
        } catch (...) {
            unlock();
            throw;
        }
}

/**
 * @memo            Compare two handles for equality
 * @return          TRUE if the handles are the same
 * @return          FALSE if the handles are different
 */
bool Handle::operator==(Handle comp) {
        Trace log("Handle::operator==");
        return (this->id == comp.id);
}

/**
 * @memo            Get the underlying Handle port based on index
 * @return          The Handle port for the given port index
 * @param           index The index of the desired port
 */
HandlePort* Handle::getHandlePortByIndex(int index) {
        Trace log("Handle::getHandlePortByIndex");
        HBAPort* port = hba->getPortByIndex(index);
        return (getHandlePort(port->getPortWWN()));
}

/**
 * @memo            Get the underlying Handle port based on Port wwn
 * @exception       IllegalWWNException thrown if the wwn is not found
 * @return          The handle port for the specified WWN
 * @param           wwn The Port WWN of the HBA port
 *
 */
HandlePort* Handle::getHandlePort(uint64_t wwn) {
        Trace log("Handle::getHandlePort");
        lock();
        try {
            // Check to see if the wwn is in the map
            if (portHandles.find(wwn) == portHandles.end()) {
                // Not found, add a new one
                HBAPort* port = hba->getPort(wwn);
                portHandles[wwn] = new HandlePort(this, hba, port);
            }
            HandlePort *portHandle = portHandles[wwn];
            unlock();
            return (portHandle);
        } catch (...) {
            unlock();
            throw;
        }
}

/**
 * @memo            Get the HBA attributes from the underlying HBA
 *
 * @see             HBA::getHBAAttributes
 */
HBA_ADAPTERATTRIBUTES Handle::getHBAAttributes() {
        Trace log("Handle::getHBAAttributes");
        lock();
        try {
            HBA_ADAPTERATTRIBUTES attributes = hba->getHBAAttributes();
            unlock();
            return (attributes);
        } catch (...) {
            unlock();
            throw;
        }
}

/**
 * @memo            Do FORCELIP
 *
 * @see             HBA::doForceLip
 */
int Handle::doForceLip() {
        Trace log("Handle::doForceLip");
        lock();
        try {
            int rval = hba->doForceLip();
            unlock();
            return (rval);
        } catch (...) {
            unlock();
            throw;
        }
}

HBA_ADAPTERATTRIBUTES Handle::npivGetHBAAttributes() {
        Trace log("Handle::npivGetHBAAttributes");
        lock();
        try {
                HBA_ADAPTERATTRIBUTES attributes = hba->npivGetHBAAttributes();
                unlock();
                return (attributes);
        } catch (...) {
                unlock();
                throw;
        }
}


/**
 * @memo            Get the HBA port attributes from the HBA
 * @see             HBAPort::getPortAttributes
 * @see             HBAPort::getDisoveredAttributes
 *
 * @doc             This routine will return either HBA port
 *                  attributes, or discovered port attributes
 *
 */
HBA_PORTATTRIBUTES Handle::getPortAttributes(uint64_t wwn) {
        Trace log("Handle::getPortAttributes");
        uint64_t tmp;
        HBA_PORTATTRIBUTES attributes;

        lock();
        try {
            // Is this a WWN for one of the adapter ports?
            if (hba->containsWWN(wwn)) {
                attributes = hba->getPort(wwn)->getPortAttributes(tmp);
                unlock();
                return (attributes);
            } else { // Is this a target we know about?
                // Loop through all ports and look for the first match

                for (int i = 0; i < hba->getNumberOfPorts(); i++) {
                    try {
                        attributes =
                            hba->getPortByIndex(i)->getDiscoveredAttributes(
                            wwn, tmp);
                        unlock();
                        return (attributes);
                    } catch (HBAException &e) {
                        continue;
                    }
                }

                // If we get to here, then we don't see this WWN on this HBA
                throw IllegalWWNException();
            }
        } catch (...) {
            unlock();
            throw;
        }
}