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
28#include "Handle.h"
29#include "Exceptions.h"
30#include "Trace.h"
31#include <libdevinfo.h>
32#include <iostream>
33#include <iomanip>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <fcntl.h>
37#include <unistd.h>
38#include <stropts.h>
39
40#define	MAX_INIT_HANDLE_ID	0x7fff
41#define	MAX_TGT_HANDLE_ID	0xffff
42
43using namespace std;
44
45/**
46 * Global lock for list of Handles
47 */
48pthread_mutex_t Handle::staticLock = PTHREAD_MUTEX_INITIALIZER;
49
50/**
51 * Tracking for the previous handle we have opened
52 */
53HBA_HANDLE Handle::prevOpen = 0;
54
55/**
56 * Tracking for the previous target HBA handle we have opened
57 */
58HBA_HANDLE Handle::prevTgtOpen = 0x8000;
59
60/**
61 * Global map from HBA_HANDLE to Handle pointers (our global list)
62 */
63map<HBA_HANDLE, Handle*> Handle::openHandles;
64
65/**
66 * @memo	    Create a new open handle for a specified HBA
67 * @precondition    HBA port(s) must be loaded
68 * @postcondition   An open handle will be present in the global tracking list
69 *		    and must be closed at some point to prevent leakage. If no
70 *		    handle could be assigned (the track list is full), an
71 *		    exception will be thrown. Scope for valid ids in the track
72 *		    list is [1, MAX_INIT_HANDLE_ID].
73 * @param	    myhba The HBA to open a handle for
74 */
75Handle::Handle(HBA *myhba) {
76	map<HBA_HANDLE, Handle*>::iterator mapend;
77	Trace log("Handle::Handle");
78	modeVal = INITIATOR;
79	lock(&staticLock);
80	mapend = openHandles.end();
81	/* Start the search for a free id from the previously assigned one */
82	id = prevOpen + 1;
83	while (id != prevOpen) {
84		/* Exceeds the max valid value, continue the search from 1 */
85		if (id > MAX_INIT_HANDLE_ID)
86			id = 1;
87
88		if (openHandles.find(id) == mapend) {
89			/* the id is not in use */
90			break;
91		}
92		id ++;
93	}
94	if (id == prevOpen) {
95		/* no usable id for now */
96		unlock(&staticLock);
97		throw TryAgainException();
98	}
99	prevOpen = id;
100	hba = myhba;
101	openHandles[id] = this;
102	unlock(&staticLock);
103}
104
105/**
106 * @memo	    Create a new open handle for a specified HBA
107 * @precondition    HBA port(s) must be loaded
108 * @postcondition   An open handle will be present in the global tracking list
109 *		    and must be closed at some point to prevent leakage. If no
110 *		    handle could be assigned (the track list is full), an
111 *		    exception will be thrown. Scope for valid ids in the track
112 *		    list is [0x8000, MAX_TGT_HANDLE_ID].
113 * @param	    myhba The HBA to open a handle for
114 *		    m The mode of HBA to open handle for
115 */
116#if 0
117// appears unused
118Handle::Handle(HBA *myhba, MODE m) {
119	map<HBA_HANDLE, Handle*>::iterator mapend;
120	Trace log("Handle::Handle");
121	lock(&staticLock);
122	modeVal = m;
123
124
125	// if initiator mode call constructor for initiator.
126	if (m == INITIATOR) {
127		Handle(myhba, TARGET);
128	}
129
130	mapend = openHandles.end();
131	/* Start the search for a free id from the previously assigned one */
132	id = prevTgtOpen + 1;
133	while (id != prevTgtOpen) {
134		/*
135		 * Exceeds the max valid target id value,
136		 * continue the search from 1.
137		 */
138		if (id > MAX_TGT_HANDLE_ID)
139			id = 0x8001;
140
141		if (openHandles.find(id) == mapend) {
142			/* the id is not in use */
143			break;
144		}
145		id ++;
146	}
147	if (id == prevTgtOpen) {
148		/* no usable id for now */
149		unlock(&staticLock);
150		throw TryAgainException();
151	}
152	prevTgtOpen = id;
153	hba = myhba;
154	openHandles[id] = this;
155	unlock(&staticLock);
156}
157#endif
158/**
159 * @memo	    Free up the handle (aka, close it)
160 * @postcondition   This handle will be removed from the global list
161 * @exception	    ... underlying exceptions will be thrown
162 */
163Handle::~Handle() {
164	Trace log("Handle::~Handle");
165	// Remove this handle from the global list
166	lock(&staticLock);
167	try {
168	    openHandles.erase(openHandles.find(getHandle()));
169	    unlock(&staticLock);
170	} catch (...) {
171	    unlock(&staticLock);
172	    throw;
173	}
174
175	// Now nuke all internal dynamic allocations
176	typedef map<uint64_t, HandlePort *>::const_iterator CI;
177	lock();
178	try {
179	    for (CI port = portHandles.begin(); port != portHandles.end();
180		    port++) {
181		delete port->second;
182	    }
183	    portHandles.clear();
184	    unlock();
185	} catch (...) {
186	    unlock();
187	    throw;
188	}
189}
190
191/**
192 * @memo	    Locate a handle in the global list of open handles
193 * @precondition    The requested handle must already be open
194 * @exception	    InvalidHandleException Thrown if the id does not match
195 *		    an open handle
196 * @return	    The open Handle
197 * @param	    id The id of the handle to fetch
198 *
199 * @doc		    The HBA API uses a simple integer type to represent
200 *		    an open Handle, but we use an instance of the Handle
201 *		    class.  This interface allows a caller to quickly convert
202 *		    from the API integer value to related the Handle instance.
203 */
204Handle* Handle::findHandle(HBA_HANDLE id) {
205	Trace log("Handle::findHandle(id)");
206	Handle *tmp = NULL;
207	lock(&staticLock);
208	try {
209	    if (openHandles.find(id) == openHandles.end()) {
210		throw InvalidHandleException();
211	    }
212	    tmp = openHandles[id];
213	    unlock(&staticLock);
214	    return (tmp);
215	} catch (...) {
216	    unlock(&staticLock);
217	    throw;
218	}
219}
220
221/**
222 * @memo	    Find an open handle based on Node or Port WWN
223 * @precondition    The given HBA must already be open
224 * @exception	    IllegalWWNException Thrown if no matching open Handle found
225 * @return	    The open handle matching the wwn argument
226 * @param	    wwn The Node or Port WWN of the HBA whos open handle
227 *		    is requested.
228 *
229 */
230Handle* Handle::findHandle(uint64_t wwn) {
231	Trace log("Handle::findHandle(wwn)");
232	Handle *tmp = NULL;
233	lock(&staticLock);
234	try {
235	    for (int i = 0; i < openHandles.size(); i++) {
236		tmp = openHandles[i];
237		if (tmp->getHBA()->containsWWN(wwn)) {
238		    unlock(&staticLock);
239		    return (tmp);
240		}
241	    }
242	    tmp = NULL;
243	} catch (...) { tmp = NULL; }
244	unlock(&staticLock);
245	if (tmp == NULL) {
246	    throw IllegalWWNException();
247	}
248	return (tmp);
249}
250
251/**
252 * @memo	    Refresh underlying index values
253 * @postcondition   All HandlePorts will be reset and prior index values
254 *		    will be undefined.
255 * @exception	    ... underlying exceptions will be thrown
256 *
257 * @doc		    A number of APIs in the standard interface require
258 *		    the use of index values for identifying what "thing"
259 *		    to operate on.  When dynamic reconfiguration occurs
260 *		    these indexes may become inconsistent.  This routine
261 *		    is called to reset the indexes and signify that the caller
262 *		    no longer holds or will refer to any old indexes.
263 */
264void Handle::refresh() {
265	Trace log("Handle::refresh");
266	lock();
267	try {
268	    typedef map<uint64_t, HandlePort *>::const_iterator CI;
269	    for (CI port = portHandles.begin(); port != portHandles.end();
270		    port++) {
271		port->second->refresh();
272	    }
273	    unlock();
274	} catch (...) {
275	    unlock();
276	    throw;
277	}
278}
279
280/**
281 * @memo	    Close the specified handle
282 * @precondition    The handle must be open
283 * @postcondition   The handle will be closed and should be discarded.
284 * @param	    id The handle to close
285 */
286void Handle::closeHandle(HBA_HANDLE id) {
287	Trace log("Handle::closeHandle");
288	Handle *myHandle = findHandle(id);
289	delete myHandle;
290}
291
292/**
293 * @memo	    Get the integer value for return to the API
294 * @exception	    ... underlying exceptions will be thrown
295 * @return	    The integer value representing the handle
296 *
297 * @doc		    The HBA API uses integer values to represent handles.
298 *		    Call this routine to convert a Handle instance into
299 *		    its representative integer value.
300 */
301HBA_HANDLE Handle::getHandle() {
302	Trace log("Handle::getHandle");
303	HBA_HANDLE tmp;
304	lock();
305	try {
306	    tmp = (HBA_HANDLE) id;
307	    unlock();
308	    return (tmp);
309	} catch (...) {
310	    unlock();
311	    throw;
312	}
313}
314
315/**
316 * @memo	    Compare two handles for equality
317 * @return	    TRUE if the handles are the same
318 * @return	    FALSE if the handles are different
319 */
320bool Handle::operator==(Handle comp) {
321	Trace log("Handle::operator==");
322	return (this->id == comp.id);
323}
324
325/**
326 * @memo	    Get the underlying Handle port based on index
327 * @return	    The Handle port for the given port index
328 * @param	    index The index of the desired port
329 */
330HandlePort* Handle::getHandlePortByIndex(int index) {
331	Trace log("Handle::getHandlePortByIndex");
332	HBAPort* port = hba->getPortByIndex(index);
333	return (getHandlePort(port->getPortWWN()));
334}
335
336/**
337 * @memo	    Get the underlying Handle port based on Port wwn
338 * @exception	    IllegalWWNException thrown if the wwn is not found
339 * @return	    The handle port for the specified WWN
340 * @param	    wwn The Port WWN of the HBA port
341 *
342 */
343HandlePort* Handle::getHandlePort(uint64_t wwn) {
344	Trace log("Handle::getHandlePort");
345	lock();
346	try {
347	    // Check to see if the wwn is in the map
348	    if (portHandles.find(wwn) == portHandles.end()) {
349		// Not found, add a new one
350		HBAPort* port = hba->getPort(wwn);
351		portHandles[wwn] = new HandlePort(this, hba, port);
352	    }
353	    HandlePort *portHandle = portHandles[wwn];
354	    unlock();
355	    return (portHandle);
356	} catch (...) {
357	    unlock();
358	    throw;
359	}
360}
361
362/**
363 * @memo	    Get the HBA attributes from the underlying HBA
364 *
365 * @see		    HBA::getHBAAttributes
366 */
367HBA_ADAPTERATTRIBUTES Handle::getHBAAttributes() {
368	Trace log("Handle::getHBAAttributes");
369	lock();
370	try {
371	    HBA_ADAPTERATTRIBUTES attributes = hba->getHBAAttributes();
372	    unlock();
373	    return (attributes);
374	} catch (...) {
375	    unlock();
376	    throw;
377	}
378}
379
380/**
381 * @memo	    Do FORCELIP
382 *
383 * @see		    HBA::doForceLip
384 */
385int Handle::doForceLip() {
386	Trace log("Handle::doForceLip");
387	lock();
388	try {
389	    int rval = hba->doForceLip();
390	    unlock();
391	    return (rval);
392	} catch (...) {
393	    unlock();
394	    throw;
395	}
396}
397
398HBA_ADAPTERATTRIBUTES Handle::npivGetHBAAttributes() {
399	Trace log("Handle::npivGetHBAAttributes");
400	lock();
401	try {
402		HBA_ADAPTERATTRIBUTES attributes = hba->npivGetHBAAttributes();
403		unlock();
404		return (attributes);
405	} catch (...) {
406		unlock();
407		throw;
408	}
409}
410
411
412/**
413 * @memo	    Get the HBA port attributes from the HBA
414 * @see		    HBAPort::getPortAttributes
415 * @see		    HBAPort::getDisoveredAttributes
416 *
417 * @doc		    This routine will return either HBA port
418 *		    attributes, or discovered port attributes
419 *
420 */
421HBA_PORTATTRIBUTES Handle::getPortAttributes(uint64_t wwn) {
422	Trace log("Handle::getPortAttributes");
423	uint64_t tmp;
424	HBA_PORTATTRIBUTES attributes;
425
426	lock();
427	try {
428	    // Is this a WWN for one of the adapter ports?
429	    if (hba->containsWWN(wwn)) {
430		attributes = hba->getPort(wwn)->getPortAttributes(tmp);
431		unlock();
432		return (attributes);
433	    } else { // Is this a target we know about?
434		// Loop through all ports and look for the first match
435
436		for (int i = 0; i < hba->getNumberOfPorts(); i++) {
437		    try {
438			attributes =
439			    hba->getPortByIndex(i)->getDiscoveredAttributes(
440			    wwn, tmp);
441			unlock();
442			return (attributes);
443		    } catch (HBAException &e) {
444			continue;
445		    }
446		}
447
448		// If we get to here, then we don't see this WWN on this HBA
449		throw IllegalWWNException();
450	    }
451	} catch (...) {
452	    unlock();
453	    throw;
454	}
455}
456