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/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26/*
27 * Copyright 2019 Joyent, Inc.
28 */
29
30#include <sys/byteorder.h>
31#include <sun_sas.h>
32
33/*
34 * creates a handle each time Sun_sas_OpenAdapter() is called.
35 *
36 * a open_handle_struct was created to keep track of which handles are currently
37 * open.  This prevents a user from using an old handle that corresponds to
38 * an hba that has already been closed.
39 */
40HBA_HANDLE
41CreateHandle(int adapterIndex)
42{
43	const char		ROUTINE[] = "CreateHandle";
44	struct open_handle	*new_open_handle;
45	HBA_UINT32		new_handle_index;
46	HBA_UINT8		max_handle_wrap = 0;
47
48	if (global_hba_head == NULL) {
49		log(LOG_DEBUG, ROUTINE,
50		    "an error as occurred.  global_hba_head is "
51		    "NULL.  Library may not be loaded yet.");
52		return (HANDLE_ERROR);
53	}
54
55	while (RetrieveIndex(open_handle_index) != -1)  {
56		open_handle_index = open_handle_index + 1;
57		if (open_handle_index == 0) {
58			/*
59			 * If open_handle_index wraps back to zero again,
60			 * that means all handles are currently in use.
61			 * Spec only allows for 16 bits of handles
62			 */
63			if (max_handle_wrap == 1) {
64				log(LOG_DEBUG, ROUTINE,
65				    "Max number of handles reached.");
66				return (HANDLE_ERROR);
67			}
68			open_handle_index = 1;
69			max_handle_wrap = 1;
70		}
71	}
72
73	new_handle_index = open_handle_index;
74	if ((new_open_handle = (struct open_handle *)calloc(1,
75	    sizeof (struct open_handle))) == NULL) {
76		OUT_OF_MEMORY(ROUTINE);
77		return (HANDLE_ERROR);
78	}
79	(void) memset(new_open_handle, 0, sizeof (struct open_handle));
80	new_open_handle->adapterIndex = adapterIndex;
81	new_open_handle->handle = new_handle_index;
82
83	lock(&open_handles_lock);
84
85	/* add new open handle struct to the open_handles list */
86	if (global_hba_head->open_handles == NULL) {
87		global_hba_head->open_handles = new_open_handle;
88	} else {
89		new_open_handle->next = global_hba_head->open_handles;
90		global_hba_head->open_handles = new_open_handle;
91	}
92
93	unlock(&open_handles_lock);
94	open_handle_index = open_handle_index + 1;
95	if (open_handle_index == 0) {
96		open_handle_index = 1;
97	}
98
99	return (new_handle_index);
100}
101
102/*
103 * given a handle, returns the adapterIndex number.
104 *
105 * This functions checkes to see if the given handle corresponds to an open
106 * HBA.  If it does, the adapterIndex is returned.
107 */
108int
109RetrieveIndex(HBA_HANDLE handle)
110{
111
112	struct open_handle	*open_handle_ptr;
113
114	lock(&open_handles_lock);
115
116	open_handle_ptr = RetrieveOpenHandle(handle);
117
118	unlock(&open_handles_lock);
119	if (open_handle_ptr == NULL) {
120		return (-1);
121	}
122
123	return (open_handle_ptr->adapterIndex);
124}
125/*
126 * Given a handle, returns the open_handle structure
127 * The routine assumes that the open_handles_lock has already
128 * been taken.
129 */
130struct open_handle *
131RetrieveOpenHandle(HBA_HANDLE handle)
132{
133
134	const char		ROUTINE[] = "RetrieveOpenHandle";
135	struct open_handle	*open_handle_ptr = NULL;
136
137	if (global_hba_head == NULL) {
138		log(LOG_DEBUG, ROUTINE, "No adapter is found.");
139		return (NULL);
140	}
141
142	for (open_handle_ptr = global_hba_head->open_handles;
143	    open_handle_ptr != NULL;
144	    open_handle_ptr = open_handle_ptr->next) {
145		if (open_handle_ptr->handle == handle) {
146			break;
147		}
148	}
149
150	return (open_handle_ptr);
151}
152
153/*
154 * Given an adapterIndex, this functions returns a pointer to the handle
155 * structure.  This handle structure holds the hba's information
156 * Caller must take all_hbas_lock first.
157 */
158struct sun_sas_hba *
159RetrieveHandle(int index)
160{
161	struct sun_sas_hba *hba_ptr = NULL;
162
163	for (hba_ptr = global_hba_head; hba_ptr != NULL;
164	    hba_ptr = hba_ptr->next) {
165		if (hba_ptr->index == index)
166			break;
167	}
168
169	return (hba_ptr);
170}
171
172/*
173 * Given an adapterIndex, this functions returns a pointer to the handle
174 * structure and extracts it from the global list.
175 *
176 * all_hbas_lock must be taken already.
177 */
178struct sun_sas_hba *
179ExtractHandle(int index)
180{
181	struct sun_sas_hba *last = NULL;
182	struct sun_sas_hba *hba_ptr = NULL;
183
184	for (hba_ptr = global_hba_head;
185	    hba_ptr != NULL;
186	    last = hba_ptr, hba_ptr = hba_ptr->next) {
187		if (hba_ptr->index == index) {
188			if (last) {
189				last->next = hba_ptr->next;
190			} else {
191				/* Hmm, must be the head of the list. */
192				global_hba_head = hba_ptr->next;
193			}
194			hba_ptr->next = NULL; /* Zap it to be safe */
195			break;
196		}
197	}
198
199	return (hba_ptr);
200}
201
202
203/*
204 * Given an handle, this functions returns a pointer to the handle structure
205 * for that hba
206 *
207 * Caller must take all_hbas_lock first.
208 */
209struct sun_sas_hba *
210Retrieve_Sun_sasHandle(HBA_HANDLE handle)
211{
212	const char		    ROUTINE[] = "Retrieve_Sun_sasHandle";
213	struct	sun_sas_hba	    *handle_struct = NULL;
214	int			    index;
215
216	/* Retrieve fp device path from handle */
217	index = RetrieveIndex(handle);
218	if (index == -1) {
219		log(LOG_DEBUG, ROUTINE,
220		    "handle could not be found.");
221		return (handle_struct);
222	}
223	lock(&open_handles_lock);
224	handle_struct = RetrieveHandle(index);
225	if (handle_struct == NULL) {
226		log(LOG_DEBUG, ROUTINE,
227		    "could not find index in the handle list.");
228		unlock(&open_handles_lock);
229		return (handle_struct);
230	}
231	unlock(&open_handles_lock);
232
233	return (handle_struct);
234}
235
236/*
237 * Take a mutex lock.  The routine will try, and if it fails,
238 * it will loop for a while and retry.  If it fails many times,
239 * it will start writing to the log file.
240 */
241void
242lock(mutex_t *mp)
243{
244	int status;
245	int loop = 0;
246	const char ROUTINE[] = "lock";
247
248	do {
249		loop++;
250		status = mutex_trylock(mp);
251		switch (status) {
252			case 0:
253				break;
254			case EFAULT:
255				log(LOG_DEBUG, ROUTINE,
256				    "Lock failed: fault 0x%x", mp);
257				break;
258			case EINVAL:
259				log(LOG_DEBUG, ROUTINE,
260				    "Lock failed: invalid 0x%x", mp);
261				break;
262			case EBUSY:
263				if (loop > DEADLOCK_WARNING) {
264					log(LOG_DEBUG, ROUTINE,
265					    "Lock busy, possible deadlock:0x%x",
266					    mp);
267				}
268				break;
269			case EOWNERDEAD:
270				log(LOG_DEBUG, ROUTINE,
271				    "Lock failed: owner dead 0x%x",
272				    mp);
273				break;
274			case ELOCKUNMAPPED:
275				log(LOG_DEBUG, ROUTINE,
276				    "Lock failed: unmapped 0x%x",
277				    mp);
278				break;
279			case ENOTRECOVERABLE:
280				log(LOG_DEBUG, ROUTINE,
281				    "Lock failed: not recoverable 0x%x", mp);
282				break;
283			default:
284				if (loop > DEADLOCK_WARNING) {
285					log(LOG_DEBUG, ROUTINE,
286					    "Lock failed: %s 0x%x",
287					    strerror(status), mp);
288					break;
289				}
290		}
291
292		if (status) {
293			(void) sleep(LOCK_SLEEP);
294		}
295
296	} while (status);
297}
298
299/*
300 * Unlock a mutex lock.
301 */
302void
303unlock(mutex_t *mp)
304{
305	(void) mutex_unlock(mp);
306}
307
308
309/*
310 * Get the Port WWN of the first adapter port.  This routine
311 * is used by the old V1 interfaces so that they can call
312 * the new V2 interfaces and exhibit the same behavior.
313 * In the event of error the WWN will be zero.
314 *
315 * This function will transition to PAA state but it will not
316 * verfiy whether data is stale or not
317 */
318HBA_WWN
319getFirstAdapterPortWWN(HBA_HANDLE handle)
320{
321	const char	ROUTINE[] = "getFirstAdapterPortWWN";
322	HBA_WWN			pwwn = {0, 0, 0, 0, 0, 0, 0, 0};
323	struct sun_sas_hba	*hba_ptr = NULL;
324	int			index = 0;
325	HBA_STATUS		status;
326
327	lock(&all_hbas_lock);
328	index = RetrieveIndex(handle);
329	lock(&open_handles_lock);
330	hba_ptr = RetrieveHandle(index);
331	if (hba_ptr == NULL) {
332		log(LOG_DEBUG, ROUTINE, "Invalid handle %08lx", handle);
333		unlock(&open_handles_lock);
334		unlock(&all_hbas_lock);
335		return (pwwn); /* zero WWN */
336	}
337
338	/* Check for stale data */
339	status = verifyAdapter(hba_ptr);
340	if (status != HBA_STATUS_OK) {
341		log(LOG_DEBUG, ROUTINE, "Verify adapter failed");
342		unlock(&open_handles_lock);
343		unlock(&all_hbas_lock);
344		return (pwwn);
345	}
346
347	if (hba_ptr->first_port == NULL) {
348		/* This is probably an internal failure of the library */
349		if (hba_ptr->device_path[0] != '\0') {
350			log(LOG_DEBUG, ROUTINE,
351			    "Internal failure:  Adapter %s contains no "
352			    "port data", hba_ptr->device_path);
353		} else {
354			log(LOG_DEBUG, ROUTINE,
355			    "Internal failure:  Adapter at index %d contains "
356			    " no support data", hba_ptr->index);
357		}
358		unlock(&open_handles_lock);
359		unlock(&all_hbas_lock);
360		return (pwwn); /* zero WWN */
361	}
362	/* Set the WWN now and return it */
363	pwwn = hba_ptr->first_port->port_attributes.PortSpecificAttribute.\
364	    SASPort->LocalSASAddress;
365	unlock(&open_handles_lock);
366	unlock(&all_hbas_lock);
367
368	return (pwwn);
369}
370
371u_longlong_t
372wwnConversion(uchar_t *wwn)
373{
374	u_longlong_t tmp;
375	(void) memcpy(&tmp, wwn, sizeof (u_longlong_t));
376	tmp = ntohll(tmp);
377	return (tmp);
378}
379
380/*
381 * Using ioctl to send uscsi command out
382 */
383HBA_STATUS
384send_uscsi_cmd(const char *devpath, struct uscsi_cmd *ucmd)
385{
386	const char	ROUTINE[] = "send_uscsi_cmd";
387	int		fd;
388	HBA_STATUS	ret;
389
390	/* set default timeout to 200 */
391	ucmd->uscsi_timeout = 200;
392
393	/* reset errno. */
394	errno = 0;
395	if ((fd = open(devpath, O_RDONLY | O_NDELAY)) == -1) {
396		log(LOG_DEBUG, ROUTINE,
397		    "open devpath %s failed: %s", devpath, strerror(errno));
398		return (HBA_STATUS_ERROR);
399	}
400
401	if (ioctl(fd, USCSICMD, ucmd) == -1) {
402		if (errno == EBUSY) {
403			ret = HBA_STATUS_ERROR_BUSY;
404		} else if (errno == EAGAIN) {
405			ret = HBA_STATUS_ERROR_TRY_AGAIN;
406		} else {
407			ret = HBA_STATUS_ERROR;
408		}
409		log(LOG_DEBUG, ROUTINE,
410		    "ioctl send uscsi to devpath: %s failed: %s",
411		    devpath, strerror(errno));
412		(void) close(fd);
413		return (ret);
414	}
415
416	(void) close(fd);
417
418	return (HBA_STATUS_OK);
419}
420
421/*
422 * Check whether the given Domain Address is valid.
423 */
424HBA_STATUS
425validateDomainAddress(struct sun_sas_port *hba_port_ptr, HBA_WWN DomainAddr)
426{
427	if (hba_port_ptr->first_phy != NULL &&
428	    wwnConversion(hba_port_ptr->first_phy->
429	    phy.domainPortWWN.wwn) ==
430	    wwnConversion(DomainAddr.wwn)) {
431		return (HBA_STATUS_OK);
432	}
433	return (HBA_STATUS_ERROR);
434}
435