xref: /titanic_52/usr/src/lib/scsi/libscsi/common/scsi_engine.c (revision 9241eba2ee6b19f556bc3779eef7f62da5efffe5)
1275c9da8Seschrock /*
2275c9da8Seschrock  * CDDL HEADER START
3275c9da8Seschrock  *
4275c9da8Seschrock  * The contents of this file are subject to the terms of the
5275c9da8Seschrock  * Common Development and Distribution License (the "License").
6275c9da8Seschrock  * You may not use this file except in compliance with the License.
7275c9da8Seschrock  *
8275c9da8Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9275c9da8Seschrock  * or http://www.opensolaris.org/os/licensing.
10275c9da8Seschrock  * See the License for the specific language governing permissions
11275c9da8Seschrock  * and limitations under the License.
12275c9da8Seschrock  *
13275c9da8Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14275c9da8Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15275c9da8Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16275c9da8Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17275c9da8Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18275c9da8Seschrock  *
19275c9da8Seschrock  * CDDL HEADER END
20275c9da8Seschrock  */
21275c9da8Seschrock 
22275c9da8Seschrock /*
23ac88567aSHyon Kim  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24267b4424SRobert Mustacchi  * Copyright (c) 2017, Joyent, Inc.
25275c9da8Seschrock  */
26275c9da8Seschrock 
27275c9da8Seschrock #include <sys/types.h>
28275c9da8Seschrock #include <sys/isa_defs.h>
29275c9da8Seschrock #include <sys/systeminfo.h>
30275c9da8Seschrock #include <sys/scsi/generic/commands.h>
31275c9da8Seschrock #include <sys/scsi/impl/commands.h>
32275c9da8Seschrock #include <sys/scsi/impl/uscsi.h>
33275c9da8Seschrock 
34275c9da8Seschrock #include <stdio.h>
35275c9da8Seschrock #include <stdlib.h>
36275c9da8Seschrock #include <stddef.h>
37275c9da8Seschrock #include <string.h>
38275c9da8Seschrock #include <dlfcn.h>
39275c9da8Seschrock #include <limits.h>
40275c9da8Seschrock 
41275c9da8Seschrock #include <scsi/libscsi.h>
42275c9da8Seschrock #include "libscsi_impl.h"
43275c9da8Seschrock 
44275c9da8Seschrock static const libscsi_engine_t *
45275c9da8Seschrock get_engine(libscsi_hdl_t *hp, const char *name)
46275c9da8Seschrock {
47275c9da8Seschrock 	libscsi_engine_impl_t *eip;
48275c9da8Seschrock 	const libscsi_engine_t *ep;
49275c9da8Seschrock 	const char *engine_path, *p, *q;
50275c9da8Seschrock 	char engine_dir[MAXPATHLEN];
51275c9da8Seschrock 	char engine_lib[MAXPATHLEN];
52275c9da8Seschrock 	char init_name[MAXPATHLEN];
53275c9da8Seschrock 	void *dl_hdl;
54275c9da8Seschrock 	libscsi_engine_init_f init;
55275c9da8Seschrock 	boolean_t found_lib = B_FALSE, found_init = B_FALSE;
56275c9da8Seschrock 	int dirs_tried = 0;
57275c9da8Seschrock 	char isa[257];
58275c9da8Seschrock 
59275c9da8Seschrock 	for (eip = hp->lsh_engines; eip != NULL; eip = eip->lsei_next) {
60275c9da8Seschrock 		if (strcmp(eip->lsei_engine->lse_name, name) == 0)
61275c9da8Seschrock 			return (eip->lsei_engine);
62275c9da8Seschrock 	}
63275c9da8Seschrock 
64275c9da8Seschrock 	if ((engine_path = getenv("LIBSCSI_ENGINE_PATH")) == NULL)
65275c9da8Seschrock 		engine_path = LIBSCSI_DEFAULT_ENGINE_PATH;
66275c9da8Seschrock 
67275c9da8Seschrock #if defined(_LP64)
68275c9da8Seschrock 	if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
69275c9da8Seschrock 		isa[0] = '\0';
70275c9da8Seschrock #else
71275c9da8Seschrock 	isa[0] = '\0';
72275c9da8Seschrock #endif
73275c9da8Seschrock 
74ac88567aSHyon Kim 	for (p = engine_path; p != NULL; p = q) {
75ac88567aSHyon Kim 		if ((q = strchr(p, ':')) != NULL) {
76275c9da8Seschrock 			ptrdiff_t len = q - p;
77275c9da8Seschrock 			(void) strncpy(engine_dir, p, len);
78275c9da8Seschrock 			engine_dir[len] = '\0';
79275c9da8Seschrock 			while (*q == ':')
80275c9da8Seschrock 				++q;
81275c9da8Seschrock 			if (*q == '\0')
82275c9da8Seschrock 				q = NULL;
83275c9da8Seschrock 			if (len == 0)
84275c9da8Seschrock 				continue;
85275c9da8Seschrock 		} else {
86275c9da8Seschrock 			(void) strcpy(engine_dir, p);
87275c9da8Seschrock 		}
88275c9da8Seschrock 		if (engine_dir[0] != '/')
89275c9da8Seschrock 			continue;
90275c9da8Seschrock 
91275c9da8Seschrock 		++dirs_tried;
92275c9da8Seschrock 
93275c9da8Seschrock 		(void) snprintf(engine_lib, MAXPATHLEN, "%s/%s/%s%s",
94275c9da8Seschrock 		    engine_dir, isa, name, LIBSCSI_ENGINE_EXT);
95275c9da8Seschrock 
96275c9da8Seschrock 		dl_hdl = dlopen(engine_lib,
97275c9da8Seschrock 		    RTLD_LOCAL | RTLD_LAZY | RTLD_PARENT);
98275c9da8Seschrock 		if (dl_hdl == NULL) {
99275c9da8Seschrock 			if (!found_lib)
100275c9da8Seschrock 				(void) libscsi_error(hp, ESCSI_NOENGINE,
101275c9da8Seschrock 				    "unable to dlopen %s: %s", engine_lib,
102275c9da8Seschrock 				    dlerror());
103275c9da8Seschrock 			continue;
104275c9da8Seschrock 		}
105275c9da8Seschrock 		found_lib = B_TRUE;
106275c9da8Seschrock 		(void) snprintf(init_name, MAXPATHLEN, "libscsi_%s_init", name);
107275c9da8Seschrock 		init = (libscsi_engine_init_f)dlsym(dl_hdl, init_name);
108275c9da8Seschrock 		if (init == NULL) {
109275c9da8Seschrock 			if (!found_init)
110275c9da8Seschrock 				(void) libscsi_error(hp, ESCSI_NOENGINE,
111275c9da8Seschrock 				    "failed to find %s in %s: %s", init_name,
112275c9da8Seschrock 				    engine_lib, dlerror());
113275c9da8Seschrock 			(void) dlclose(dl_hdl);
114275c9da8Seschrock 			continue;
115275c9da8Seschrock 		}
116275c9da8Seschrock 		if ((ep = init(hp)) == NULL) {
117275c9da8Seschrock 			(void) dlclose(dl_hdl);
118275c9da8Seschrock 			/*
119275c9da8Seschrock 			 * libscsi errno set by init.
120275c9da8Seschrock 			 */
121275c9da8Seschrock 			return (NULL);
122275c9da8Seschrock 		}
123275c9da8Seschrock 		if (ep->lse_libversion != hp->lsh_version) {
124275c9da8Seschrock 			(void) dlclose(dl_hdl);
125275c9da8Seschrock 			(void) libscsi_error(hp, ESCSI_ENGINE_VER, "engine "
126275c9da8Seschrock 			    "%s version %u does not match library version %u",
127275c9da8Seschrock 			    engine_lib, ep->lse_libversion, hp->lsh_version);
128275c9da8Seschrock 			return (NULL);
129275c9da8Seschrock 		}
130275c9da8Seschrock 
131275c9da8Seschrock 		eip = libscsi_zalloc(hp, sizeof (libscsi_engine_impl_t));
132275c9da8Seschrock 		if (eip == NULL) {
133275c9da8Seschrock 			(void) dlclose(dl_hdl);
134275c9da8Seschrock 			return (NULL);
135275c9da8Seschrock 		}
136275c9da8Seschrock 		eip->lsei_engine = ep;
137275c9da8Seschrock 		eip->lsei_dl_hdl = dl_hdl;
138275c9da8Seschrock 		eip->lsei_next = hp->lsh_engines;
139275c9da8Seschrock 		hp->lsh_engines = eip;
140275c9da8Seschrock 
141275c9da8Seschrock 		return (ep);
142275c9da8Seschrock 	}
143275c9da8Seschrock 
144275c9da8Seschrock 	if (dirs_tried == 0)
145275c9da8Seschrock 		(void) libscsi_error(hp, ESCSI_ENGINE_BADPATH, "no valid "
146275c9da8Seschrock 		    "directories found in engine path %s", engine_path);
147275c9da8Seschrock 
148275c9da8Seschrock 	return (NULL);
149275c9da8Seschrock }
150275c9da8Seschrock 
151275c9da8Seschrock static void
152275c9da8Seschrock scsi_parse_mtbf(const char *envvar, uint_t *intp)
153275c9da8Seschrock {
154275c9da8Seschrock 	const char *strval;
155275c9da8Seschrock 	int intval;
156275c9da8Seschrock 
157275c9da8Seschrock 	if ((strval = getenv(envvar)) != NULL &&
158275c9da8Seschrock 	    (intval = atoi(strval)) > 0) {
159275c9da8Seschrock 		srand48(gethrtime());
160275c9da8Seschrock 		*intp = intval;
161275c9da8Seschrock 	}
162275c9da8Seschrock }
163275c9da8Seschrock 
164275c9da8Seschrock libscsi_target_t *
165275c9da8Seschrock libscsi_open(libscsi_hdl_t *hp, const char *engine, const void *target)
166275c9da8Seschrock {
167275c9da8Seschrock 	const libscsi_engine_t *ep;
168275c9da8Seschrock 	libscsi_target_t *tp;
169275c9da8Seschrock 	void *private;
170275c9da8Seschrock 
171275c9da8Seschrock 	if (engine == NULL) {
172275c9da8Seschrock 		if ((engine = getenv("LIBSCSI_DEFAULT_ENGINE")) == NULL)
173275c9da8Seschrock 			engine = LIBSCSI_DEFAULT_ENGINE;
174275c9da8Seschrock 	}
175275c9da8Seschrock 
176275c9da8Seschrock 	if ((ep = get_engine(hp, engine)) == NULL)
177275c9da8Seschrock 		return (NULL);
178275c9da8Seschrock 
179275c9da8Seschrock 	if ((tp = libscsi_zalloc(hp, sizeof (libscsi_target_t))) == NULL)
180275c9da8Seschrock 		return (NULL);
181275c9da8Seschrock 
182275c9da8Seschrock 	if ((private = ep->lse_ops->lseo_open(hp, target)) == NULL) {
183275c9da8Seschrock 		libscsi_free(hp, tp);
184275c9da8Seschrock 		return (NULL);
185275c9da8Seschrock 	}
186275c9da8Seschrock 
187275c9da8Seschrock 	scsi_parse_mtbf("LIBSCSI_MTBF_CDB", &tp->lst_mtbf_cdb);
188275c9da8Seschrock 	scsi_parse_mtbf("LIBSCSI_MTBF_READ", &tp->lst_mtbf_read);
189275c9da8Seschrock 	scsi_parse_mtbf("LIBSCSI_MTBF_WRITE", &tp->lst_mtbf_write);
190275c9da8Seschrock 
191275c9da8Seschrock 	tp->lst_hdl = hp;
192275c9da8Seschrock 	tp->lst_engine = ep;
193275c9da8Seschrock 	tp->lst_priv = private;
194275c9da8Seschrock 
195275c9da8Seschrock 	++hp->lsh_targets;
196275c9da8Seschrock 
197275c9da8Seschrock 	if (libscsi_get_inquiry(hp, tp) != 0) {
198275c9da8Seschrock 		libscsi_close(hp, tp);
199275c9da8Seschrock 		return (NULL);
200275c9da8Seschrock 	}
201275c9da8Seschrock 
202275c9da8Seschrock 	return (tp);
203275c9da8Seschrock }
204275c9da8Seschrock 
205275c9da8Seschrock libscsi_hdl_t *
206275c9da8Seschrock libscsi_get_handle(libscsi_target_t *tp)
207275c9da8Seschrock {
208275c9da8Seschrock 	return (tp->lst_hdl);
209275c9da8Seschrock }
210275c9da8Seschrock 
211275c9da8Seschrock void
212275c9da8Seschrock libscsi_close(libscsi_hdl_t *hp, libscsi_target_t *tp)
213275c9da8Seschrock {
214275c9da8Seschrock 	tp->lst_engine->lse_ops->lseo_close(hp, tp->lst_priv);
215275c9da8Seschrock 	libscsi_free(hp, tp->lst_vendor);
216275c9da8Seschrock 	libscsi_free(hp, tp->lst_product);
217275c9da8Seschrock 	libscsi_free(hp, tp->lst_revision);
218275c9da8Seschrock 	libscsi_free(hp, tp);
219275c9da8Seschrock 	--hp->lsh_targets;
220275c9da8Seschrock }
221275c9da8Seschrock 
222275c9da8Seschrock sam4_status_t
223275c9da8Seschrock libscsi_action_get_status(const libscsi_action_t *ap)
224275c9da8Seschrock {
225275c9da8Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
226275c9da8Seschrock 
227275c9da8Seschrock 	return (aip->lsai_status);
228275c9da8Seschrock }
229275c9da8Seschrock 
230275c9da8Seschrock /*
231275c9da8Seschrock  * Set the timeout in seconds for this action.  If no timeout is specified
232275c9da8Seschrock  * or if the timeout is set to 0, an implementation-specific timeout will be
233275c9da8Seschrock  * used (which may vary based on the target, command or other variables).
234275c9da8Seschrock  * Not all engines support all timeout values.  Setting the timeout to a value
235275c9da8Seschrock  * not supported by the engine will cause engine-defined behavior when the
236275c9da8Seschrock  * action is executed.
237275c9da8Seschrock  */
238275c9da8Seschrock void
239275c9da8Seschrock libscsi_action_set_timeout(libscsi_action_t *ap, uint32_t timeout)
240275c9da8Seschrock {
241275c9da8Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
242275c9da8Seschrock 
243275c9da8Seschrock 	aip->lsai_timeout = timeout;
244275c9da8Seschrock }
245275c9da8Seschrock 
246275c9da8Seschrock /*
247275c9da8Seschrock  * Obtain the timeout setting for this action.
248275c9da8Seschrock  */
249275c9da8Seschrock uint32_t
250275c9da8Seschrock libscsi_action_get_timeout(const libscsi_action_t *ap)
251275c9da8Seschrock {
252275c9da8Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
253275c9da8Seschrock 
254275c9da8Seschrock 	return (aip->lsai_timeout);
255275c9da8Seschrock }
256275c9da8Seschrock 
257275c9da8Seschrock /*
258275c9da8Seschrock  * Returns the flags associated with this action.  Never fails.
259275c9da8Seschrock  */
260275c9da8Seschrock uint_t
261275c9da8Seschrock libscsi_action_get_flags(const libscsi_action_t *ap)
262275c9da8Seschrock {
263275c9da8Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
264275c9da8Seschrock 
265275c9da8Seschrock 	return (aip->lsai_flags);
266275c9da8Seschrock }
267275c9da8Seschrock 
268275c9da8Seschrock /*
269267b4424SRobert Mustacchi  * Return the length of the CDB buffer associated with this action.  Never
270267b4424SRobert Mustacchi  * fails.
271267b4424SRobert Mustacchi  */
272267b4424SRobert Mustacchi size_t
273267b4424SRobert Mustacchi libscsi_action_get_cdblen(const libscsi_action_t *ap)
274267b4424SRobert Mustacchi {
275267b4424SRobert Mustacchi 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
276267b4424SRobert Mustacchi 
277267b4424SRobert Mustacchi 	return (aip->lsai_cdb_len);
278267b4424SRobert Mustacchi }
279267b4424SRobert Mustacchi 
280267b4424SRobert Mustacchi /*
281275c9da8Seschrock  * Returns the address of the action's CDB.  The CDB buffer is guaranteed to
282275c9da8Seschrock  * be large enough to hold the complete CDB for the command specified when the
283275c9da8Seschrock  * action was allocated.  Therefore, changing the command/opcode portion of
284275c9da8Seschrock  * the CDB has undefined effects.  The remainder of the CDB may be modified.
285275c9da8Seschrock  */
286275c9da8Seschrock uint8_t *
287275c9da8Seschrock libscsi_action_get_cdb(const libscsi_action_t *ap)
288275c9da8Seschrock {
289275c9da8Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
290275c9da8Seschrock 
291275c9da8Seschrock 	return (aip->lsai_cdb);
292275c9da8Seschrock }
293275c9da8Seschrock 
294275c9da8Seschrock /*
295275c9da8Seschrock  * Places the address of the action buffer in the location pointed to by bp,
296275c9da8Seschrock  * if bp is not NULL.  If ap is not NULL, it will contain the allocated size
297275c9da8Seschrock  * of the buffer itself.  If vp is not NULL, it will contain the number of
298275c9da8Seschrock  * bytes of valid data currently stored in the buffer.
299275c9da8Seschrock  *
300275c9da8Seschrock  * If the action has LIBSCSI_AF_WRITE set and it has not yet been executed
301275c9da8Seschrock  * successfully, the entire buffer is assumed to contain valid data.
302275c9da8Seschrock  *
303275c9da8Seschrock  * If the action has LIBSCSI_AF_READ set and it has not yet been executed
304275c9da8Seschrock  * successfully, the amount of valid data is 0.
305275c9da8Seschrock  *
306275c9da8Seschrock  * If both LIBSCSI_AF_READ and LIBSCSI_AF_WRITE are clear, this function
307275c9da8Seschrock  * fails with ESCSI_BADFLAGS to indicate that the action flags are
308275c9da8Seschrock  * incompatible with the action data buffer.
309275c9da8Seschrock  */
310275c9da8Seschrock int
311275c9da8Seschrock libscsi_action_get_buffer(const libscsi_action_t *ap, uint8_t **bp,
312275c9da8Seschrock     size_t *sp, size_t *vp)
313275c9da8Seschrock {
314275c9da8Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
315275c9da8Seschrock 
316275c9da8Seschrock 	if ((aip->lsai_flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) == 0)
317275c9da8Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
318275c9da8Seschrock 		    "data buffer not supported for actions with both "
319275c9da8Seschrock 		    "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE clear"));
320275c9da8Seschrock 
321275c9da8Seschrock 	if ((aip->lsai_flags & LIBSCSI_AF_WRITE) &&
322275c9da8Seschrock 	    aip->lsai_status == LIBSCSI_STATUS_INVALID) {
323275c9da8Seschrock 		if (bp != NULL)
324275c9da8Seschrock 			*bp = aip->lsai_data;
325275c9da8Seschrock 		if (sp != NULL)
326275c9da8Seschrock 			*sp = aip->lsai_data_alloc;
327275c9da8Seschrock 		if (vp != NULL)
328275c9da8Seschrock 			*vp = aip->lsai_data_alloc;
329275c9da8Seschrock 
330275c9da8Seschrock 		return (0);
331275c9da8Seschrock 	}
332275c9da8Seschrock 
333275c9da8Seschrock 	if ((aip->lsai_flags & LIBSCSI_AF_READ) &&
334275c9da8Seschrock 	    aip->lsai_status != LIBSCSI_STATUS_INVALID) {
335275c9da8Seschrock 		if (bp != NULL)
336275c9da8Seschrock 			*bp = aip->lsai_data;
337275c9da8Seschrock 		if (sp != NULL)
338275c9da8Seschrock 			*sp = aip->lsai_data_alloc;
339275c9da8Seschrock 		if (vp != NULL)
340275c9da8Seschrock 			*vp = aip->lsai_data_len;
341275c9da8Seschrock 
342275c9da8Seschrock 		return (0);
343275c9da8Seschrock 	}
344275c9da8Seschrock 
345275c9da8Seschrock 	if (aip->lsai_flags & LIBSCSI_AF_WRITE) {
346275c9da8Seschrock 		if (bp != NULL)
347275c9da8Seschrock 			*bp = NULL;
348275c9da8Seschrock 		if (sp != NULL)
349275c9da8Seschrock 			*sp = NULL;
350275c9da8Seschrock 		if (vp != NULL)
351275c9da8Seschrock 			*vp = 0;
352275c9da8Seschrock 	} else {
353275c9da8Seschrock 		if (bp != NULL)
354275c9da8Seschrock 			*bp = aip->lsai_data;
355275c9da8Seschrock 		if (sp != NULL)
356275c9da8Seschrock 			*sp = aip->lsai_data_alloc;
357275c9da8Seschrock 		if (vp != NULL)
358275c9da8Seschrock 			*vp = 0;
359275c9da8Seschrock 	}
360275c9da8Seschrock 
361275c9da8Seschrock 	return (0);
362275c9da8Seschrock }
363275c9da8Seschrock 
364275c9da8Seschrock /*
365275c9da8Seschrock  * Obtain a pointer to the sense buffer for this action, if any, along with
366275c9da8Seschrock  * the size of the sense buffer and the amount of valid data it contains.
367275c9da8Seschrock  */
368275c9da8Seschrock int
369275c9da8Seschrock libscsi_action_get_sense(const libscsi_action_t *ap, uint8_t **bp,
370275c9da8Seschrock     size_t *sp, size_t *vp)
371275c9da8Seschrock {
372275c9da8Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
373275c9da8Seschrock 
374275c9da8Seschrock 	if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
375275c9da8Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
376275c9da8Seschrock 		    "sense data unavailable: LIBSCSI_AF_RQSENSE is clear"));
377275c9da8Seschrock 
378275c9da8Seschrock 	if (vp != NULL) {
379275c9da8Seschrock 		if (aip->lsai_status == LIBSCSI_STATUS_INVALID)
380275c9da8Seschrock 			*vp = 0;
381275c9da8Seschrock 		else
382275c9da8Seschrock 			*vp = aip->lsai_sense_len;
383275c9da8Seschrock 	}
384275c9da8Seschrock 
385275c9da8Seschrock 	if (bp != NULL) {
386275c9da8Seschrock 		ASSERT(aip->lsai_sense_data != NULL);
387275c9da8Seschrock 		*bp = aip->lsai_sense_data;
388275c9da8Seschrock 	}
389275c9da8Seschrock 
390275c9da8Seschrock 	if (sp != NULL)
391275c9da8Seschrock 		*sp = UINT8_MAX;
392275c9da8Seschrock 
393275c9da8Seschrock 	return (0);
394275c9da8Seschrock }
395275c9da8Seschrock 
396275c9da8Seschrock /*
397275c9da8Seschrock  * Set the SCSI status of the action.
398275c9da8Seschrock  *
399275c9da8Seschrock  * Engines only.
400275c9da8Seschrock  */
401275c9da8Seschrock void
402275c9da8Seschrock libscsi_action_set_status(libscsi_action_t *ap, sam4_status_t status)
403275c9da8Seschrock {
404275c9da8Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
405275c9da8Seschrock 
406275c9da8Seschrock 	ASSERT(aip->lsai_status == LIBSCSI_STATUS_INVALID);
407275c9da8Seschrock 
408275c9da8Seschrock 	aip->lsai_status = status;
409275c9da8Seschrock }
410275c9da8Seschrock 
411275c9da8Seschrock /*
412275c9da8Seschrock  * Set the length of valid data returned by a READ action.  If the action is
413275c9da8Seschrock  * not a READ action, or the length exceeds the size of the buffer, an error
414275c9da8Seschrock  * results.
415275c9da8Seschrock  *
416275c9da8Seschrock  * Engines only.
417275c9da8Seschrock  */
418275c9da8Seschrock int
419275c9da8Seschrock libscsi_action_set_datalen(libscsi_action_t *ap, size_t len)
420275c9da8Seschrock {
421275c9da8Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
422275c9da8Seschrock 
423275c9da8Seschrock 	if ((aip->lsai_flags & LIBSCSI_AF_READ) == 0)
424275c9da8Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
425275c9da8Seschrock 		    "data cannot be returned for actions with LIBSCSI_AF_READ "
426275c9da8Seschrock 		    "clear"));
427275c9da8Seschrock 	if (len > aip->lsai_data_alloc)
428275c9da8Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
429275c9da8Seschrock 		    "data length %lu exceeds allocated buffer capacity %lu",
430275c9da8Seschrock 		    (ulong_t)len, (ulong_t)aip->lsai_data_alloc));
431275c9da8Seschrock 
432275c9da8Seschrock 	ASSERT(aip->lsai_data_len == 0);
433275c9da8Seschrock 	aip->lsai_data_len = len;
434275c9da8Seschrock 
435275c9da8Seschrock 	return (0);
436275c9da8Seschrock }
437275c9da8Seschrock 
438275c9da8Seschrock /*
439275c9da8Seschrock  * Set the length of the valid sense data returned following the command, if
440275c9da8Seschrock  * LIBSCSI_AF_RQSENSE is set for this action.  Otherwise, fail.
441275c9da8Seschrock  *
442275c9da8Seschrock  * Engines only.
443275c9da8Seschrock  */
444275c9da8Seschrock int
445275c9da8Seschrock libscsi_action_set_senselen(libscsi_action_t *ap, size_t len)
446275c9da8Seschrock {
447275c9da8Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
448275c9da8Seschrock 
449275c9da8Seschrock 	if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
450275c9da8Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
451275c9da8Seschrock 		    "sense data not supported: LIBSCSI_AF_RQSENSE is clear"));
452275c9da8Seschrock 
453275c9da8Seschrock 	if (len > UINT8_MAX)
454275c9da8Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
455275c9da8Seschrock 		    "sense length %lu exceeds allocated buffer capacity %lu",
456275c9da8Seschrock 		    (ulong_t)len, (ulong_t)UINT8_MAX));
457275c9da8Seschrock 
458275c9da8Seschrock 	ASSERT(aip->lsai_sense_len == 0);
459275c9da8Seschrock 	aip->lsai_sense_len = len;
460275c9da8Seschrock 
461275c9da8Seschrock 	return (0);
462275c9da8Seschrock }
463275c9da8Seschrock 
464275c9da8Seschrock /*
465275c9da8Seschrock  * Allocate an action object.  The object will contain a CDB area sufficiently
466275c9da8Seschrock  * large to hold a CDB for the given command, and the CDB's opcode will be
467275c9da8Seschrock  * filled in.  A pointer to this CDB, the contents of which may be modified by
468275c9da8Seschrock  * the caller, may be obtained by a subsequent call to libscsi_action_cdb().
469275c9da8Seschrock  *
470275c9da8Seschrock  * If flags includes LIBSCSI_AF_READ or LIBSCSI_AF_WRITE, buflen must be
471275c9da8Seschrock  * greater than zero.  Otherwise, buflen must be 0 and buf must be NULL.
472275c9da8Seschrock  * If buflen is nonzero but buf is NULL, a suitably-sized buffer will be
473275c9da8Seschrock  * allocated; otherwise, the specified buffer will be used.  In either case,
474275c9da8Seschrock  * a pointer to the buffer may be obtained via a subsequent call to
475275c9da8Seschrock  * libscsi_action_buffer().
476275c9da8Seschrock  *
477275c9da8Seschrock  * If flags includes LIBSCSI_AF_RQSENSE, a REQUEST SENSE command will be
478275c9da8Seschrock  * issued immediately following the termination of the specified command.
479275c9da8Seschrock  * A buffer will be allocated to receive this sense data.  Following successful
480275c9da8Seschrock  * execution of the action, a pointer to this buffer and the length of
481275c9da8Seschrock  * valid sense data may be obtained by a call to libscsi_action_sense().
482275c9da8Seschrock  * If cmd is SPC3_CMD_REQUEST_SENSE, this flag must be clear.
483275c9da8Seschrock  */
484275c9da8Seschrock libscsi_action_t *
485267b4424SRobert Mustacchi libscsi_action_alloc_vendor(libscsi_hdl_t *hp, spc3_cmd_t cmd, size_t cdbsz,
486267b4424SRobert Mustacchi     uint_t flags, void *buf, size_t buflen)
487275c9da8Seschrock {
488275c9da8Seschrock 	libscsi_action_impl_t *aip;
489267b4424SRobert Mustacchi 	size_t sz;
490275c9da8Seschrock 	ptrdiff_t off;
491275c9da8Seschrock 
492275c9da8Seschrock 	/*
493275c9da8Seschrock 	 * If there's no buffer, it makes no sense to try to read or write
494275c9da8Seschrock 	 * data.  Likewise, if we're neither reading nor writing data, we
495275c9da8Seschrock 	 * should not have a buffer.  Both of these are programmer error.
496275c9da8Seschrock 	 */
497275c9da8Seschrock 	if (buflen == 0 && (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
498275c9da8Seschrock 		(void) libscsi_error(hp, ESCSI_NEEDBUF, "a buffer is "
499275c9da8Seschrock 		    "required when reading or writing");
500275c9da8Seschrock 		return (NULL);
501275c9da8Seschrock 	}
502275c9da8Seschrock 	if (buflen > 0 && !(flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
503275c9da8Seschrock 		(void) libscsi_error(hp, ESCSI_BADFLAGS, "one of "
504275c9da8Seschrock 		    "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE must be specified "
505275c9da8Seschrock 		    "in order to use a buffer");
506275c9da8Seschrock 		return (NULL);
507275c9da8Seschrock 	}
508267b4424SRobert Mustacchi 
509267b4424SRobert Mustacchi 	if (cdbsz == 0) {
510267b4424SRobert Mustacchi 		(void) libscsi_error(hp, ESCSI_BADLENGTH, "the supplied CDB "
511267b4424SRobert Mustacchi 		    "buffer size has an invalid length, it must be non-zero.");
512275c9da8Seschrock 		return (NULL);
513275c9da8Seschrock 	}
514275c9da8Seschrock 
515267b4424SRobert Mustacchi 	sz = cdbsz;
516275c9da8Seschrock 
517275c9da8Seschrock 	/*
518275c9da8Seschrock 	 * If the caller has asked for a buffer but has not provided one, we
519275c9da8Seschrock 	 * will allocate it in our internal buffer along with the CDB and
520275c9da8Seschrock 	 * request sense space (if requested).
521275c9da8Seschrock 	 */
522275c9da8Seschrock 	if (buf == NULL)
523275c9da8Seschrock 		sz += buflen;
524275c9da8Seschrock 
525275c9da8Seschrock 	if (flags & LIBSCSI_AF_RQSENSE)
526275c9da8Seschrock 		sz += UINT8_MAX;
527275c9da8Seschrock 
528275c9da8Seschrock 	sz += offsetof(libscsi_action_impl_t, lsai_buf[0]);
529275c9da8Seschrock 
530275c9da8Seschrock 	if ((aip = libscsi_zalloc(hp, sz)) == NULL)
531275c9da8Seschrock 		return (NULL);
532275c9da8Seschrock 
533275c9da8Seschrock 	aip->lsai_hdl = hp;
534275c9da8Seschrock 	aip->lsai_flags = flags;
535275c9da8Seschrock 
536275c9da8Seschrock 	off = 0;
537275c9da8Seschrock 
538275c9da8Seschrock 	aip->lsai_cdb = aip->lsai_buf + off;
539275c9da8Seschrock 	aip->lsai_cdb_len = cdbsz;
540275c9da8Seschrock 	off += cdbsz;
541275c9da8Seschrock 	aip->lsai_cdb[0] = (uint8_t)cmd;
542275c9da8Seschrock 
543275c9da8Seschrock 	if (buflen > 0) {
544275c9da8Seschrock 		if (buf != NULL) {
545275c9da8Seschrock 			aip->lsai_data = buf;
546275c9da8Seschrock 		} else {
547275c9da8Seschrock 			aip->lsai_data = aip->lsai_buf + off;
548275c9da8Seschrock 			off += buflen;
549275c9da8Seschrock 		}
550275c9da8Seschrock 		aip->lsai_data_alloc = buflen;
551275c9da8Seschrock 		if (flags & LIBSCSI_AF_WRITE)
552275c9da8Seschrock 			aip->lsai_data_len = buflen;
553275c9da8Seschrock 	}
554275c9da8Seschrock 
555275c9da8Seschrock 	if (flags & LIBSCSI_AF_RQSENSE) {
556275c9da8Seschrock 		aip->lsai_sense_data = aip->lsai_buf + off;
557275c9da8Seschrock 		off += UINT8_MAX;
558275c9da8Seschrock 	}
559275c9da8Seschrock 
560275c9da8Seschrock 	aip->lsai_status = LIBSCSI_STATUS_INVALID;
561275c9da8Seschrock 
562275c9da8Seschrock 	return ((libscsi_action_t *)aip);
563275c9da8Seschrock }
564275c9da8Seschrock 
565267b4424SRobert Mustacchi libscsi_action_t *
566267b4424SRobert Mustacchi libscsi_action_alloc(libscsi_hdl_t *hp, spc3_cmd_t cmd, uint_t flags,
567267b4424SRobert Mustacchi     void *buf, size_t buflen)
568267b4424SRobert Mustacchi {
569267b4424SRobert Mustacchi 	size_t cdbsz;
570267b4424SRobert Mustacchi 
571267b4424SRobert Mustacchi 	if (cmd == SPC3_CMD_REQUEST_SENSE && (flags & LIBSCSI_AF_RQSENSE)) {
572267b4424SRobert Mustacchi 		(void) libscsi_error(hp, ESCSI_BADFLAGS, "request sense "
573267b4424SRobert Mustacchi 		    "flag not allowed for request sense command");
574267b4424SRobert Mustacchi 		return (NULL);
575267b4424SRobert Mustacchi 	}
576267b4424SRobert Mustacchi 
577267b4424SRobert Mustacchi 	if ((cdbsz = libscsi_cmd_cdblen(hp, cmd)) == 0)
578267b4424SRobert Mustacchi 		return (NULL);
579267b4424SRobert Mustacchi 
580267b4424SRobert Mustacchi 	return (libscsi_action_alloc_vendor(hp, cmd, cdbsz, flags, buf,
581267b4424SRobert Mustacchi 	    buflen));
582267b4424SRobert Mustacchi }
583267b4424SRobert Mustacchi 
584275c9da8Seschrock void
585275c9da8Seschrock libscsi_action_free(libscsi_action_t *ap)
586275c9da8Seschrock {
587275c9da8Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
588275c9da8Seschrock 
589275c9da8Seschrock 	libscsi_free(aip->lsai_hdl, aip);
590275c9da8Seschrock }
591275c9da8Seschrock 
592275c9da8Seschrock /*
593275c9da8Seschrock  * For testing purposes, we allow data to be corrupted via an environment
594275c9da8Seschrock  * variable setting.  This helps ensure that higher level software can cope with
595275c9da8Seschrock  * arbitrarily broken targets.  The mtbf value represents the number of bytes we
596275c9da8Seschrock  * will see, on average, in between each failure.  Therefore, for each N bytes,
597275c9da8Seschrock  * we would expect to see (N / mtbf) bytes of corruption.
598275c9da8Seschrock  */
599275c9da8Seschrock static void
600275c9da8Seschrock scsi_inject_errors(void *data, size_t len, uint_t mtbf)
601275c9da8Seschrock {
602275c9da8Seschrock 	char *buf = data;
603275c9da8Seschrock 	double prob;
604275c9da8Seschrock 	size_t index;
605275c9da8Seschrock 
606275c9da8Seschrock 	if (len == 0)
607275c9da8Seschrock 		return;
608275c9da8Seschrock 
609275c9da8Seschrock 	prob = (double)len / mtbf;
610275c9da8Seschrock 
611275c9da8Seschrock 	while (prob > 1) {
612275c9da8Seschrock 		index = lrand48() % len;
613275c9da8Seschrock 		buf[index] = (lrand48() % 256);
614275c9da8Seschrock 		prob -= 1;
615275c9da8Seschrock 	}
616275c9da8Seschrock 
617275c9da8Seschrock 	if (drand48() <= prob) {
618275c9da8Seschrock 		index = lrand48() % len;
619275c9da8Seschrock 		buf[index] = (lrand48() % 256);
620275c9da8Seschrock 	}
621275c9da8Seschrock }
622275c9da8Seschrock 
623275c9da8Seschrock int
624275c9da8Seschrock libscsi_exec(libscsi_action_t *ap, libscsi_target_t *tp)
625275c9da8Seschrock {
626275c9da8Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
627275c9da8Seschrock 	libscsi_hdl_t *hp = aip->lsai_hdl;
628275c9da8Seschrock 	int ret;
629275c9da8Seschrock 
630275c9da8Seschrock 	if (tp->lst_mtbf_write != 0 &&
631275c9da8Seschrock 	    (aip->lsai_flags & LIBSCSI_AF_WRITE)) {
632275c9da8Seschrock 		scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
633275c9da8Seschrock 		    tp->lst_mtbf_write);
634275c9da8Seschrock 	}
635275c9da8Seschrock 
636275c9da8Seschrock 	if (tp->lst_mtbf_cdb != 0) {
637275c9da8Seschrock 		scsi_inject_errors(aip->lsai_cdb, aip->lsai_cdb_len,
638275c9da8Seschrock 		    tp->lst_mtbf_cdb);
639275c9da8Seschrock 	}
640275c9da8Seschrock 
641275c9da8Seschrock 	ret = tp->lst_engine->lse_ops->lseo_exec(hp, tp->lst_priv, ap);
642275c9da8Seschrock 
643275c9da8Seschrock 	if (ret == 0 && tp->lst_mtbf_read != 0 &&
644275c9da8Seschrock 	    (aip->lsai_flags & LIBSCSI_AF_READ)) {
645275c9da8Seschrock 		scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
646275c9da8Seschrock 		    tp->lst_mtbf_read);
647275c9da8Seschrock 	}
648275c9da8Seschrock 
649275c9da8Seschrock 	return (ret);
650275c9da8Seschrock }
651*9241eba2SRobert Mustacchi 
652*9241eba2SRobert Mustacchi int
653*9241eba2SRobert Mustacchi libscsi_max_transfer(libscsi_target_t *tp, size_t *sizep)
654*9241eba2SRobert Mustacchi {
655*9241eba2SRobert Mustacchi 	libscsi_hdl_t *hp = tp->lst_hdl;
656*9241eba2SRobert Mustacchi 	if (tp->lst_engine->lse_ops->lseo_max_transfer == NULL) {
657*9241eba2SRobert Mustacchi 		return (libscsi_error(hp, ESCSI_NOTSUP, "max transfer "
658*9241eba2SRobert Mustacchi 		    "request not supported by engine"));
659*9241eba2SRobert Mustacchi 	}
660*9241eba2SRobert Mustacchi 
661*9241eba2SRobert Mustacchi 	return (tp->lst_engine->lse_ops->lseo_max_transfer(hp, tp->lst_priv,
662*9241eba2SRobert Mustacchi 	    sizep));
663*9241eba2SRobert Mustacchi }
664