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#include <sun_sas.h>
28
29/*
30 * Pass request buffer into uscsi command and sent it out via ioctl
31 */
32static HBA_STATUS
33SendScsiReadCapacity(const char *devpath, void *responseBuffer,
34    HBA_UINT32 *responseSize, HBA_UINT8 *scsiStatus,
35    void *senseBuffer, HBA_UINT32 *senseSize)
36{
37	HBA_UINT32		status;
38	struct uscsi_cmd	ucmd_buf;
39	union scsi_cdb		cdb;
40
41	bzero(&cdb, sizeof (cdb));
42	bzero(&ucmd_buf, sizeof (ucmd_buf));
43	bzero(senseBuffer, *senseSize);
44	bzero(responseBuffer, *responseSize);
45
46	cdb.scc_cmd = SCMD_READ_CAPACITY;
47
48	ucmd_buf.uscsi_cdb = (char *)&cdb;
49	ucmd_buf.uscsi_cdblen = CDB_GROUP1;
50	ucmd_buf.uscsi_bufaddr = (caddr_t)responseBuffer;
51	ucmd_buf.uscsi_buflen = *responseSize;
52	ucmd_buf.uscsi_rqbuf = (caddr_t)senseBuffer;
53	ucmd_buf.uscsi_rqlen = *senseSize;
54	ucmd_buf.uscsi_flags = USCSI_READ | USCSI_SILENT | USCSI_RQENABLE;
55	ucmd_buf.uscsi_timeout = 60;
56
57	status = send_uscsi_cmd(devpath, &ucmd_buf);
58	*scsiStatus = ucmd_buf.uscsi_status;
59	return (status);
60}
61
62/*
63 * Send a read capacity to a remote WWN
64 */
65HBA_STATUS
66Sun_sasScsiReadCapacity(HBA_HANDLE handle, HBA_WWN portWWN,
67    HBA_WWN targetPortWWN, HBA_WWN domainPortWWN, SMHBA_SCSILUN smhbaLUN,
68    void *responseBuffer, HBA_UINT32 *responseSize,
69    HBA_UINT8 *scsiStatus, void *senseBuffer, HBA_UINT32 *senseSize)
70{
71	const char		ROUTINE[] = "Sun_sasScsiReadCapacity";
72	HBA_STATUS		status;
73	int			index = 0, domainPortFound = 0;
74	int			hbaPortFound = 0;
75	int			chkDomainPort = 0;
76	struct sun_sas_hba	*hba_ptr = NULL;
77	struct sun_sas_port	*hba_port_ptr, *hba_disco_port;
78	struct ScsiEntryList	*mapping_ptr;
79	hrtime_t		start, end;
80	double			duration;
81	HBA_SCSILUN		hba_lun;
82
83	start = gethrtime();
84	/* Validate the arguments */
85	if (responseBuffer == NULL) {
86		log(LOG_DEBUG, ROUTINE, "NULL response buffer");
87		return (HBA_STATUS_ERROR_ARG);
88	}
89	if (senseBuffer == NULL) {
90		log(LOG_DEBUG, ROUTINE, "NULL sense buffer");
91		return (HBA_STATUS_ERROR_ARG);
92	}
93	if (responseSize == NULL) {
94		log(LOG_DEBUG, ROUTINE, "NULL response size");
95		return (HBA_STATUS_ERROR_ARG);
96	}
97	if (senseSize == NULL) {
98		log(LOG_DEBUG, ROUTINE, "NULL sense size");
99		return (HBA_STATUS_ERROR_ARG);
100	}
101	if (scsiStatus == NULL) {
102		log(LOG_DEBUG, ROUTINE, "NULL scsi status");
103		return (HBA_STATUS_ERROR_ARG);
104	}
105
106	lock(&all_hbas_lock);
107	index = RetrieveIndex(handle);
108	lock(&open_handles_lock);
109	if ((hba_ptr = RetrieveHandle(index)) == NULL) {
110		log(LOG_DEBUG, ROUTINE, "Invalid handle %08lx", handle);
111		unlock(&open_handles_lock);
112		unlock(&all_hbas_lock);
113		return (HBA_STATUS_ERROR_INVALID_HANDLE);
114	}
115
116	/* Check for stale data */
117	status = verifyAdapter(hba_ptr);
118	if (status != HBA_STATUS_OK) {
119		log(LOG_DEBUG, ROUTINE, "Verify adapter failed");
120		unlock(&open_handles_lock);
121		unlock(&all_hbas_lock);
122		return (status);
123	}
124
125	if (wwnConversion(domainPortWWN.wwn))
126		chkDomainPort = 1;
127
128	/*
129	 * We are not checking to see if our data is stale.
130	 * By verifying this information here, we will take a big performance
131	 * hit.  This check will be done later only if the FCSM ioctl fails
132	 */
133
134	/* Determine which port to use */
135	for (hba_port_ptr = hba_ptr->first_port;
136	    hba_port_ptr != NULL;
137	    hba_port_ptr = hba_port_ptr->next) {
138
139		if (hbaPortFound == 0) {
140			if (wwnConversion(hba_port_ptr->port_attributes.
141			    PortSpecificAttribute.SASPort->LocalSASAddress.wwn)
142			    != wwnConversion(portWWN.wwn)) {
143				/*
144				 * Since all the ports under the same HBA have
145				 * the same LocalSASAddress, we should break
146				 * the loop once we find it dosn't match.
147				 */
148				break;
149			} else {
150				hbaPortFound = 1;
151			}
152		}
153
154		if (chkDomainPort != 0) {
155			if (hba_port_ptr->first_phy != NULL &&
156			    wwnConversion(hba_port_ptr->first_phy->
157			    phy.domainPortWWN.wwn) ==
158			    wwnConversion(domainPortWWN.wwn)) {
159				domainPortFound = 1;
160			}
161			if (!(domainPortFound)) {
162				continue;
163			}
164		}
165
166		for (hba_disco_port = hba_port_ptr->first_attached_port;
167		    hba_disco_port != NULL;
168		    hba_disco_port = hba_disco_port->next) {
169
170			/*
171			 * If discoveredPort is not given targetPort, just skip
172			 */
173			if (wwnConversion(hba_disco_port->port_attributes.\
174			    PortSpecificAttribute.SASPort->LocalSASAddress.wwn)
175			    != wwnConversion(targetPortWWN.wwn)) {
176				/* Does not match */
177				continue;
178			}
179
180			/*
181			 * If discoveredPort is not a SAS/SATA port, it is not a
182			 * target port
183			 */
184			if ((hba_disco_port->port_attributes.PortType !=
185			    HBA_PORTTYPE_SATADEVICE) &&
186			    (hba_disco_port->port_attributes.PortType !=
187			    HBA_PORTTYPE_SASDEVICE)) {
188				unlock(&open_handles_lock);
189				unlock(&all_hbas_lock);
190				log(LOG_DEBUG, ROUTINE, "Target Port WWN "
191				    "%016llx on handle %08lx is not a Target",
192				    wwnConversion(targetPortWWN.wwn), handle);
193				return (HBA_STATUS_ERROR_NOT_A_TARGET);
194			}
195
196			/*
197			 * Iterating and matching is needed.
198			 */
199			for (mapping_ptr = hba_disco_port->scsiInfo;
200			    mapping_ptr != NULL;
201			    mapping_ptr = mapping_ptr->next) {
202
203				if (memcmp(
204				    &mapping_ptr->entry.PortLun.TargetLun,
205				    &smhbaLUN, sizeof (HBA_SCSILUN))
206				    != 0) {
207					continue;
208				}
209
210				status = SendScsiReadCapacity(
211				    mapping_ptr->entry.ScsiId.\
212				    OSDeviceName,
213				    responseBuffer, responseSize,
214				    scsiStatus, senseBuffer, senseSize);
215
216				unlock(&open_handles_lock);
217				unlock(&all_hbas_lock);
218				end = gethrtime();
219				duration = end - start;
220				duration /= HR_SECOND;
221				log(LOG_DEBUG, ROUTINE, "Took total\
222				    of %.4f seconds", duration);
223				return (status);
224			}
225			unlock(&open_handles_lock);
226			unlock(&all_hbas_lock);
227			(void *) memcpy(&hba_lun, &smhbaLUN,
228			    sizeof (HBA_SCSILUN));
229			log(LOG_DEBUG, ROUTINE, "Unable to locate lun"
230			    " %08lx for target %016llx on handle %08lx",
231			    hba_lun, wwnConversion(targetPortWWN.wwn), handle);
232			return (HBA_STATUS_ERROR_INVALID_LUN);
233		}
234
235		if (chkDomainPort) {
236			unlock(&open_handles_lock);
237			unlock(&all_hbas_lock);
238			log(LOG_DEBUG, ROUTINE, "Unable to locate requested "
239			    "Port WWN %016llx on handle %08lx",
240			    wwnConversion(targetPortWWN.wwn), handle);
241			return (HBA_STATUS_ERROR_ILLEGAL_WWN);
242		}
243	}
244
245	unlock(&open_handles_lock);
246	unlock(&all_hbas_lock);
247	if (hbaPortFound == 0) {
248		log(LOG_DEBUG, ROUTINE,
249		    "Unable to locate requested Port WWN %016llx on "
250		    "handle %08lx", wwnConversion(portWWN.wwn), handle);
251	} else if (chkDomainPort && !domainPortFound) {
252		log(LOG_DEBUG, ROUTINE, "Unable to locate requested"
253		    " domainPortWWN %016llx on handle %08lx",
254		    wwnConversion(domainPortWWN.wwn), handle);
255	} else {
256		log(LOG_DEBUG, ROUTINE, "Unable to locate requested "
257		    "Port WWN %016llx on handle %08lx",
258		    wwnConversion(targetPortWWN.wwn), handle);
259	}
260	return (HBA_STATUS_ERROR_ILLEGAL_WWN);
261}
262