xref: /onnv-gate/usr/src/lib/scsi/libscsi/common/scsi_engine.c (revision 12126:60364f3f65c7)
16316Seschrock /*
26316Seschrock  * CDDL HEADER START
36316Seschrock  *
46316Seschrock  * The contents of this file are subject to the terms of the
56316Seschrock  * Common Development and Distribution License (the "License").
66316Seschrock  * You may not use this file except in compliance with the License.
76316Seschrock  *
86316Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96316Seschrock  * or http://www.opensolaris.org/os/licensing.
106316Seschrock  * See the License for the specific language governing permissions
116316Seschrock  * and limitations under the License.
126316Seschrock  *
136316Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
146316Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156316Seschrock  * If applicable, add the following below this CDDL HEADER, with the
166316Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
176316Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
186316Seschrock  *
196316Seschrock  * CDDL HEADER END
206316Seschrock  */
216316Seschrock 
226316Seschrock /*
23*12126SHyon.Kim@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
246316Seschrock  */
256316Seschrock 
266316Seschrock #include <sys/types.h>
276316Seschrock #include <sys/isa_defs.h>
286316Seschrock #include <sys/systeminfo.h>
296316Seschrock #include <sys/scsi/generic/commands.h>
306316Seschrock #include <sys/scsi/impl/commands.h>
316316Seschrock #include <sys/scsi/impl/uscsi.h>
326316Seschrock 
336316Seschrock #include <stdio.h>
346316Seschrock #include <stdlib.h>
356316Seschrock #include <stddef.h>
366316Seschrock #include <string.h>
376316Seschrock #include <dlfcn.h>
386316Seschrock #include <limits.h>
396316Seschrock 
406316Seschrock #include <scsi/libscsi.h>
416316Seschrock #include "libscsi_impl.h"
426316Seschrock 
436316Seschrock static const libscsi_engine_t *
get_engine(libscsi_hdl_t * hp,const char * name)446316Seschrock get_engine(libscsi_hdl_t *hp, const char *name)
456316Seschrock {
466316Seschrock 	libscsi_engine_impl_t *eip;
476316Seschrock 	const libscsi_engine_t *ep;
486316Seschrock 	const char *engine_path, *p, *q;
496316Seschrock 	char engine_dir[MAXPATHLEN];
506316Seschrock 	char engine_lib[MAXPATHLEN];
516316Seschrock 	char init_name[MAXPATHLEN];
526316Seschrock 	void *dl_hdl;
536316Seschrock 	libscsi_engine_init_f init;
546316Seschrock 	boolean_t found_lib = B_FALSE, found_init = B_FALSE;
556316Seschrock 	int dirs_tried = 0;
566316Seschrock 	char isa[257];
576316Seschrock 
586316Seschrock 	for (eip = hp->lsh_engines; eip != NULL; eip = eip->lsei_next) {
596316Seschrock 		if (strcmp(eip->lsei_engine->lse_name, name) == 0)
606316Seschrock 			return (eip->lsei_engine);
616316Seschrock 	}
626316Seschrock 
636316Seschrock 	if ((engine_path = getenv("LIBSCSI_ENGINE_PATH")) == NULL)
646316Seschrock 		engine_path = LIBSCSI_DEFAULT_ENGINE_PATH;
656316Seschrock 
666316Seschrock #if defined(_LP64)
676316Seschrock 	if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
686316Seschrock 		isa[0] = '\0';
696316Seschrock #else
706316Seschrock 	isa[0] = '\0';
716316Seschrock #endif
726316Seschrock 
73*12126SHyon.Kim@Sun.COM 	for (p = engine_path; p != NULL; p = q) {
74*12126SHyon.Kim@Sun.COM 		if ((q = strchr(p, ':')) != NULL) {
756316Seschrock 			ptrdiff_t len = q - p;
766316Seschrock 			(void) strncpy(engine_dir, p, len);
776316Seschrock 			engine_dir[len] = '\0';
786316Seschrock 			while (*q == ':')
796316Seschrock 				++q;
806316Seschrock 			if (*q == '\0')
816316Seschrock 				q = NULL;
826316Seschrock 			if (len == 0)
836316Seschrock 				continue;
846316Seschrock 		} else {
856316Seschrock 			(void) strcpy(engine_dir, p);
866316Seschrock 		}
876316Seschrock 		if (engine_dir[0] != '/')
886316Seschrock 			continue;
896316Seschrock 
906316Seschrock 		++dirs_tried;
916316Seschrock 
926316Seschrock 		(void) snprintf(engine_lib, MAXPATHLEN, "%s/%s/%s%s",
936316Seschrock 		    engine_dir, isa, name, LIBSCSI_ENGINE_EXT);
946316Seschrock 
956316Seschrock 		dl_hdl = dlopen(engine_lib,
966316Seschrock 		    RTLD_LOCAL | RTLD_LAZY | RTLD_PARENT);
976316Seschrock 		if (dl_hdl == NULL) {
986316Seschrock 			if (!found_lib)
996316Seschrock 				(void) libscsi_error(hp, ESCSI_NOENGINE,
1006316Seschrock 				    "unable to dlopen %s: %s", engine_lib,
1016316Seschrock 				    dlerror());
1026316Seschrock 			continue;
1036316Seschrock 		}
1046316Seschrock 		found_lib = B_TRUE;
1056316Seschrock 		(void) snprintf(init_name, MAXPATHLEN, "libscsi_%s_init", name);
1066316Seschrock 		init = (libscsi_engine_init_f)dlsym(dl_hdl, init_name);
1076316Seschrock 		if (init == NULL) {
1086316Seschrock 			if (!found_init)
1096316Seschrock 				(void) libscsi_error(hp, ESCSI_NOENGINE,
1106316Seschrock 				    "failed to find %s in %s: %s", init_name,
1116316Seschrock 				    engine_lib, dlerror());
1126316Seschrock 			(void) dlclose(dl_hdl);
1136316Seschrock 			continue;
1146316Seschrock 		}
1156316Seschrock 		if ((ep = init(hp)) == NULL) {
1166316Seschrock 			(void) dlclose(dl_hdl);
1176316Seschrock 			/*
1186316Seschrock 			 * libscsi errno set by init.
1196316Seschrock 			 */
1206316Seschrock 			return (NULL);
1216316Seschrock 		}
1226316Seschrock 		if (ep->lse_libversion != hp->lsh_version) {
1236316Seschrock 			(void) dlclose(dl_hdl);
1246316Seschrock 			(void) libscsi_error(hp, ESCSI_ENGINE_VER, "engine "
1256316Seschrock 			    "%s version %u does not match library version %u",
1266316Seschrock 			    engine_lib, ep->lse_libversion, hp->lsh_version);
1276316Seschrock 			return (NULL);
1286316Seschrock 		}
1296316Seschrock 
1306316Seschrock 		eip = libscsi_zalloc(hp, sizeof (libscsi_engine_impl_t));
1316316Seschrock 		if (eip == NULL) {
1326316Seschrock 			(void) dlclose(dl_hdl);
1336316Seschrock 			return (NULL);
1346316Seschrock 		}
1356316Seschrock 		eip->lsei_engine = ep;
1366316Seschrock 		eip->lsei_dl_hdl = dl_hdl;
1376316Seschrock 		eip->lsei_next = hp->lsh_engines;
1386316Seschrock 		hp->lsh_engines = eip;
1396316Seschrock 
1406316Seschrock 		return (ep);
1416316Seschrock 	}
1426316Seschrock 
1436316Seschrock 	if (dirs_tried == 0)
1446316Seschrock 		(void) libscsi_error(hp, ESCSI_ENGINE_BADPATH, "no valid "
1456316Seschrock 		    "directories found in engine path %s", engine_path);
1466316Seschrock 
1476316Seschrock 	return (NULL);
1486316Seschrock }
1496316Seschrock 
1506316Seschrock static void
scsi_parse_mtbf(const char * envvar,uint_t * intp)1516316Seschrock scsi_parse_mtbf(const char *envvar, uint_t *intp)
1526316Seschrock {
1536316Seschrock 	const char *strval;
1546316Seschrock 	int intval;
1556316Seschrock 
1566316Seschrock 	if ((strval = getenv(envvar)) != NULL &&
1576316Seschrock 	    (intval = atoi(strval)) > 0) {
1586316Seschrock 		srand48(gethrtime());
1596316Seschrock 		*intp = intval;
1606316Seschrock 	}
1616316Seschrock }
1626316Seschrock 
1636316Seschrock libscsi_target_t *
libscsi_open(libscsi_hdl_t * hp,const char * engine,const void * target)1646316Seschrock libscsi_open(libscsi_hdl_t *hp, const char *engine, const void *target)
1656316Seschrock {
1666316Seschrock 	const libscsi_engine_t *ep;
1676316Seschrock 	libscsi_target_t *tp;
1686316Seschrock 	void *private;
1696316Seschrock 
1706316Seschrock 	if (engine == NULL) {
1716316Seschrock 		if ((engine = getenv("LIBSCSI_DEFAULT_ENGINE")) == NULL)
1726316Seschrock 			engine = LIBSCSI_DEFAULT_ENGINE;
1736316Seschrock 	}
1746316Seschrock 
1756316Seschrock 	if ((ep = get_engine(hp, engine)) == NULL)
1766316Seschrock 		return (NULL);
1776316Seschrock 
1786316Seschrock 	if ((tp = libscsi_zalloc(hp, sizeof (libscsi_target_t))) == NULL)
1796316Seschrock 		return (NULL);
1806316Seschrock 
1816316Seschrock 	if ((private = ep->lse_ops->lseo_open(hp, target)) == NULL) {
1826316Seschrock 		libscsi_free(hp, tp);
1836316Seschrock 		return (NULL);
1846316Seschrock 	}
1856316Seschrock 
1866316Seschrock 	scsi_parse_mtbf("LIBSCSI_MTBF_CDB", &tp->lst_mtbf_cdb);
1876316Seschrock 	scsi_parse_mtbf("LIBSCSI_MTBF_READ", &tp->lst_mtbf_read);
1886316Seschrock 	scsi_parse_mtbf("LIBSCSI_MTBF_WRITE", &tp->lst_mtbf_write);
1896316Seschrock 
1906316Seschrock 	tp->lst_hdl = hp;
1916316Seschrock 	tp->lst_engine = ep;
1926316Seschrock 	tp->lst_priv = private;
1936316Seschrock 
1946316Seschrock 	++hp->lsh_targets;
1956316Seschrock 
1966316Seschrock 	if (libscsi_get_inquiry(hp, tp) != 0) {
1976316Seschrock 		libscsi_close(hp, tp);
1986316Seschrock 		return (NULL);
1996316Seschrock 	}
2006316Seschrock 
2016316Seschrock 	return (tp);
2026316Seschrock }
2036316Seschrock 
2046316Seschrock libscsi_hdl_t *
libscsi_get_handle(libscsi_target_t * tp)2056316Seschrock libscsi_get_handle(libscsi_target_t *tp)
2066316Seschrock {
2076316Seschrock 	return (tp->lst_hdl);
2086316Seschrock }
2096316Seschrock 
2106316Seschrock void
libscsi_close(libscsi_hdl_t * hp,libscsi_target_t * tp)2116316Seschrock libscsi_close(libscsi_hdl_t *hp, libscsi_target_t *tp)
2126316Seschrock {
2136316Seschrock 	tp->lst_engine->lse_ops->lseo_close(hp, tp->lst_priv);
2146316Seschrock 	libscsi_free(hp, tp->lst_vendor);
2156316Seschrock 	libscsi_free(hp, tp->lst_product);
2166316Seschrock 	libscsi_free(hp, tp->lst_revision);
2176316Seschrock 	libscsi_free(hp, tp);
2186316Seschrock 	--hp->lsh_targets;
2196316Seschrock }
2206316Seschrock 
2216316Seschrock sam4_status_t
libscsi_action_get_status(const libscsi_action_t * ap)2226316Seschrock libscsi_action_get_status(const libscsi_action_t *ap)
2236316Seschrock {
2246316Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
2256316Seschrock 
2266316Seschrock 	return (aip->lsai_status);
2276316Seschrock }
2286316Seschrock 
2296316Seschrock /*
2306316Seschrock  * Set the timeout in seconds for this action.  If no timeout is specified
2316316Seschrock  * or if the timeout is set to 0, an implementation-specific timeout will be
2326316Seschrock  * used (which may vary based on the target, command or other variables).
2336316Seschrock  * Not all engines support all timeout values.  Setting the timeout to a value
2346316Seschrock  * not supported by the engine will cause engine-defined behavior when the
2356316Seschrock  * action is executed.
2366316Seschrock  */
2376316Seschrock void
libscsi_action_set_timeout(libscsi_action_t * ap,uint32_t timeout)2386316Seschrock libscsi_action_set_timeout(libscsi_action_t *ap, uint32_t timeout)
2396316Seschrock {
2406316Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
2416316Seschrock 
2426316Seschrock 	aip->lsai_timeout = timeout;
2436316Seschrock }
2446316Seschrock 
2456316Seschrock /*
2466316Seschrock  * Obtain the timeout setting for this action.
2476316Seschrock  */
2486316Seschrock uint32_t
libscsi_action_get_timeout(const libscsi_action_t * ap)2496316Seschrock libscsi_action_get_timeout(const libscsi_action_t *ap)
2506316Seschrock {
2516316Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
2526316Seschrock 
2536316Seschrock 	return (aip->lsai_timeout);
2546316Seschrock }
2556316Seschrock 
2566316Seschrock /*
2576316Seschrock  * Returns the flags associated with this action.  Never fails.
2586316Seschrock  */
2596316Seschrock uint_t
libscsi_action_get_flags(const libscsi_action_t * ap)2606316Seschrock libscsi_action_get_flags(const libscsi_action_t *ap)
2616316Seschrock {
2626316Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
2636316Seschrock 
2646316Seschrock 	return (aip->lsai_flags);
2656316Seschrock }
2666316Seschrock 
2676316Seschrock /*
2686316Seschrock  * Returns the address of the action's CDB.  The CDB buffer is guaranteed to
2696316Seschrock  * be large enough to hold the complete CDB for the command specified when the
2706316Seschrock  * action was allocated.  Therefore, changing the command/opcode portion of
2716316Seschrock  * the CDB has undefined effects.  The remainder of the CDB may be modified.
2726316Seschrock  */
2736316Seschrock uint8_t *
libscsi_action_get_cdb(const libscsi_action_t * ap)2746316Seschrock libscsi_action_get_cdb(const libscsi_action_t *ap)
2756316Seschrock {
2766316Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
2776316Seschrock 
2786316Seschrock 	return (aip->lsai_cdb);
2796316Seschrock }
2806316Seschrock 
2816316Seschrock /*
2826316Seschrock  * Places the address of the action buffer in the location pointed to by bp,
2836316Seschrock  * if bp is not NULL.  If ap is not NULL, it will contain the allocated size
2846316Seschrock  * of the buffer itself.  If vp is not NULL, it will contain the number of
2856316Seschrock  * bytes of valid data currently stored in the buffer.
2866316Seschrock  *
2876316Seschrock  * If the action has LIBSCSI_AF_WRITE set and it has not yet been executed
2886316Seschrock  * successfully, the entire buffer is assumed to contain valid data.
2896316Seschrock  *
2906316Seschrock  * If the action has LIBSCSI_AF_READ set and it has not yet been executed
2916316Seschrock  * successfully, the amount of valid data is 0.
2926316Seschrock  *
2936316Seschrock  * If both LIBSCSI_AF_READ and LIBSCSI_AF_WRITE are clear, this function
2946316Seschrock  * fails with ESCSI_BADFLAGS to indicate that the action flags are
2956316Seschrock  * incompatible with the action data buffer.
2966316Seschrock  */
2976316Seschrock int
libscsi_action_get_buffer(const libscsi_action_t * ap,uint8_t ** bp,size_t * sp,size_t * vp)2986316Seschrock libscsi_action_get_buffer(const libscsi_action_t *ap, uint8_t **bp,
2996316Seschrock     size_t *sp, size_t *vp)
3006316Seschrock {
3016316Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
3026316Seschrock 
3036316Seschrock 	if ((aip->lsai_flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) == 0)
3046316Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
3056316Seschrock 		    "data buffer not supported for actions with both "
3066316Seschrock 		    "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE clear"));
3076316Seschrock 
3086316Seschrock 	if ((aip->lsai_flags & LIBSCSI_AF_WRITE) &&
3096316Seschrock 	    aip->lsai_status == LIBSCSI_STATUS_INVALID) {
3106316Seschrock 		if (bp != NULL)
3116316Seschrock 			*bp = aip->lsai_data;
3126316Seschrock 		if (sp != NULL)
3136316Seschrock 			*sp = aip->lsai_data_alloc;
3146316Seschrock 		if (vp != NULL)
3156316Seschrock 			*vp = aip->lsai_data_alloc;
3166316Seschrock 
3176316Seschrock 		return (0);
3186316Seschrock 	}
3196316Seschrock 
3206316Seschrock 	if ((aip->lsai_flags & LIBSCSI_AF_READ) &&
3216316Seschrock 	    aip->lsai_status != LIBSCSI_STATUS_INVALID) {
3226316Seschrock 		if (bp != NULL)
3236316Seschrock 			*bp = aip->lsai_data;
3246316Seschrock 		if (sp != NULL)
3256316Seschrock 			*sp = aip->lsai_data_alloc;
3266316Seschrock 		if (vp != NULL)
3276316Seschrock 			*vp = aip->lsai_data_len;
3286316Seschrock 
3296316Seschrock 		return (0);
3306316Seschrock 	}
3316316Seschrock 
3326316Seschrock 	if (aip->lsai_flags & LIBSCSI_AF_WRITE) {
3336316Seschrock 		if (bp != NULL)
3346316Seschrock 			*bp = NULL;
3356316Seschrock 		if (sp != NULL)
3366316Seschrock 			*sp = NULL;
3376316Seschrock 		if (vp != NULL)
3386316Seschrock 			*vp = 0;
3396316Seschrock 	} else {
3406316Seschrock 		if (bp != NULL)
3416316Seschrock 			*bp = aip->lsai_data;
3426316Seschrock 		if (sp != NULL)
3436316Seschrock 			*sp = aip->lsai_data_alloc;
3446316Seschrock 		if (vp != NULL)
3456316Seschrock 			*vp = 0;
3466316Seschrock 	}
3476316Seschrock 
3486316Seschrock 	return (0);
3496316Seschrock }
3506316Seschrock 
3516316Seschrock /*
3526316Seschrock  * Obtain a pointer to the sense buffer for this action, if any, along with
3536316Seschrock  * the size of the sense buffer and the amount of valid data it contains.
3546316Seschrock  */
3556316Seschrock int
libscsi_action_get_sense(const libscsi_action_t * ap,uint8_t ** bp,size_t * sp,size_t * vp)3566316Seschrock libscsi_action_get_sense(const libscsi_action_t *ap, uint8_t **bp,
3576316Seschrock     size_t *sp, size_t *vp)
3586316Seschrock {
3596316Seschrock 	const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
3606316Seschrock 
3616316Seschrock 	if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
3626316Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
3636316Seschrock 		    "sense data unavailable: LIBSCSI_AF_RQSENSE is clear"));
3646316Seschrock 
3656316Seschrock 	if (vp != NULL) {
3666316Seschrock 		if (aip->lsai_status == LIBSCSI_STATUS_INVALID)
3676316Seschrock 			*vp = 0;
3686316Seschrock 		else
3696316Seschrock 			*vp = aip->lsai_sense_len;
3706316Seschrock 	}
3716316Seschrock 
3726316Seschrock 	if (bp != NULL) {
3736316Seschrock 		ASSERT(aip->lsai_sense_data != NULL);
3746316Seschrock 		*bp = aip->lsai_sense_data;
3756316Seschrock 	}
3766316Seschrock 
3776316Seschrock 	if (sp != NULL)
3786316Seschrock 		*sp = UINT8_MAX;
3796316Seschrock 
3806316Seschrock 	return (0);
3816316Seschrock }
3826316Seschrock 
3836316Seschrock /*
3846316Seschrock  * Set the SCSI status of the action.
3856316Seschrock  *
3866316Seschrock  * Engines only.
3876316Seschrock  */
3886316Seschrock void
libscsi_action_set_status(libscsi_action_t * ap,sam4_status_t status)3896316Seschrock libscsi_action_set_status(libscsi_action_t *ap, sam4_status_t status)
3906316Seschrock {
3916316Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
3926316Seschrock 
3936316Seschrock 	ASSERT(aip->lsai_status == LIBSCSI_STATUS_INVALID);
3946316Seschrock 
3956316Seschrock 	aip->lsai_status = status;
3966316Seschrock }
3976316Seschrock 
3986316Seschrock /*
3996316Seschrock  * Set the length of valid data returned by a READ action.  If the action is
4006316Seschrock  * not a READ action, or the length exceeds the size of the buffer, an error
4016316Seschrock  * results.
4026316Seschrock  *
4036316Seschrock  * Engines only.
4046316Seschrock  */
4056316Seschrock int
libscsi_action_set_datalen(libscsi_action_t * ap,size_t len)4066316Seschrock libscsi_action_set_datalen(libscsi_action_t *ap, size_t len)
4076316Seschrock {
4086316Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
4096316Seschrock 
4106316Seschrock 	if ((aip->lsai_flags & LIBSCSI_AF_READ) == 0)
4116316Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
4126316Seschrock 		    "data cannot be returned for actions with LIBSCSI_AF_READ "
4136316Seschrock 		    "clear"));
4146316Seschrock 	if (len > aip->lsai_data_alloc)
4156316Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
4166316Seschrock 		    "data length %lu exceeds allocated buffer capacity %lu",
4176316Seschrock 		    (ulong_t)len, (ulong_t)aip->lsai_data_alloc));
4186316Seschrock 
4196316Seschrock 	ASSERT(aip->lsai_data_len == 0);
4206316Seschrock 	aip->lsai_data_len = len;
4216316Seschrock 
4226316Seschrock 	return (0);
4236316Seschrock }
4246316Seschrock 
4256316Seschrock /*
4266316Seschrock  * Set the length of the valid sense data returned following the command, if
4276316Seschrock  * LIBSCSI_AF_RQSENSE is set for this action.  Otherwise, fail.
4286316Seschrock  *
4296316Seschrock  * Engines only.
4306316Seschrock  */
4316316Seschrock int
libscsi_action_set_senselen(libscsi_action_t * ap,size_t len)4326316Seschrock libscsi_action_set_senselen(libscsi_action_t *ap, size_t len)
4336316Seschrock {
4346316Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
4356316Seschrock 
4366316Seschrock 	if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
4376316Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
4386316Seschrock 		    "sense data not supported: LIBSCSI_AF_RQSENSE is clear"));
4396316Seschrock 
4406316Seschrock 	if (len > UINT8_MAX)
4416316Seschrock 		return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
4426316Seschrock 		    "sense length %lu exceeds allocated buffer capacity %lu",
4436316Seschrock 		    (ulong_t)len, (ulong_t)UINT8_MAX));
4446316Seschrock 
4456316Seschrock 	ASSERT(aip->lsai_sense_len == 0);
4466316Seschrock 	aip->lsai_sense_len = len;
4476316Seschrock 
4486316Seschrock 	return (0);
4496316Seschrock }
4506316Seschrock 
4516316Seschrock /*
4526316Seschrock  * Allocate an action object.  The object will contain a CDB area sufficiently
4536316Seschrock  * large to hold a CDB for the given command, and the CDB's opcode will be
4546316Seschrock  * filled in.  A pointer to this CDB, the contents of which may be modified by
4556316Seschrock  * the caller, may be obtained by a subsequent call to libscsi_action_cdb().
4566316Seschrock  *
4576316Seschrock  * If flags includes LIBSCSI_AF_READ or LIBSCSI_AF_WRITE, buflen must be
4586316Seschrock  * greater than zero.  Otherwise, buflen must be 0 and buf must be NULL.
4596316Seschrock  * If buflen is nonzero but buf is NULL, a suitably-sized buffer will be
4606316Seschrock  * allocated; otherwise, the specified buffer will be used.  In either case,
4616316Seschrock  * a pointer to the buffer may be obtained via a subsequent call to
4626316Seschrock  * libscsi_action_buffer().
4636316Seschrock  *
4646316Seschrock  * If flags includes LIBSCSI_AF_RQSENSE, a REQUEST SENSE command will be
4656316Seschrock  * issued immediately following the termination of the specified command.
4666316Seschrock  * A buffer will be allocated to receive this sense data.  Following successful
4676316Seschrock  * execution of the action, a pointer to this buffer and the length of
4686316Seschrock  * valid sense data may be obtained by a call to libscsi_action_sense().
4696316Seschrock  * If cmd is SPC3_CMD_REQUEST_SENSE, this flag must be clear.
4706316Seschrock  */
4716316Seschrock libscsi_action_t *
libscsi_action_alloc(libscsi_hdl_t * hp,spc3_cmd_t cmd,uint_t flags,void * buf,size_t buflen)4726316Seschrock libscsi_action_alloc(libscsi_hdl_t *hp, spc3_cmd_t cmd, uint_t flags,
4736316Seschrock     void *buf, size_t buflen)
4746316Seschrock {
4756316Seschrock 	libscsi_action_impl_t *aip;
4766316Seschrock 	size_t cdbsz, sz;
4776316Seschrock 	ptrdiff_t off;
4786316Seschrock 
4796316Seschrock 	/*
4806316Seschrock 	 * If there's no buffer, it makes no sense to try to read or write
4816316Seschrock 	 * data.  Likewise, if we're neither reading nor writing data, we
4826316Seschrock 	 * should not have a buffer.  Both of these are programmer error.
4836316Seschrock 	 */
4846316Seschrock 	if (buflen == 0 && (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
4856316Seschrock 		(void) libscsi_error(hp, ESCSI_NEEDBUF, "a buffer is "
4866316Seschrock 		    "required when reading or writing");
4876316Seschrock 		return (NULL);
4886316Seschrock 	}
4896316Seschrock 	if (buflen > 0 && !(flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
4906316Seschrock 		(void) libscsi_error(hp, ESCSI_BADFLAGS, "one of "
4916316Seschrock 		    "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE must be specified "
4926316Seschrock 		    "in order to use a buffer");
4936316Seschrock 		return (NULL);
4946316Seschrock 	}
4956316Seschrock 	if (cmd == SPC3_CMD_REQUEST_SENSE && (flags & LIBSCSI_AF_RQSENSE)) {
4966316Seschrock 		(void) libscsi_error(hp, ESCSI_BADFLAGS, "request sense "
4976316Seschrock 		    "flag not allowed for request sense command");
4986316Seschrock 		return (NULL);
4996316Seschrock 	}
5006316Seschrock 
5016316Seschrock 	if ((sz = cdbsz = libscsi_cmd_cdblen(hp, cmd)) == 0)
5026316Seschrock 		return (NULL);
5036316Seschrock 
5046316Seschrock 	/*
5056316Seschrock 	 * If the caller has asked for a buffer but has not provided one, we
5066316Seschrock 	 * will allocate it in our internal buffer along with the CDB and
5076316Seschrock 	 * request sense space (if requested).
5086316Seschrock 	 */
5096316Seschrock 	if (buf == NULL)
5106316Seschrock 		sz += buflen;
5116316Seschrock 
5126316Seschrock 	if (flags & LIBSCSI_AF_RQSENSE)
5136316Seschrock 		sz += UINT8_MAX;
5146316Seschrock 
5156316Seschrock 	sz += offsetof(libscsi_action_impl_t, lsai_buf[0]);
5166316Seschrock 
5176316Seschrock 	if ((aip = libscsi_zalloc(hp, sz)) == NULL)
5186316Seschrock 		return (NULL);
5196316Seschrock 
5206316Seschrock 	aip->lsai_hdl = hp;
5216316Seschrock 	aip->lsai_flags = flags;
5226316Seschrock 
5236316Seschrock 	off = 0;
5246316Seschrock 
5256316Seschrock 	aip->lsai_cdb = aip->lsai_buf + off;
5266316Seschrock 	aip->lsai_cdb_len = cdbsz;
5276316Seschrock 	off += cdbsz;
5286316Seschrock 	aip->lsai_cdb[0] = (uint8_t)cmd;
5296316Seschrock 
5306316Seschrock 	if (buflen > 0) {
5316316Seschrock 		if (buf != NULL) {
5326316Seschrock 			aip->lsai_data = buf;
5336316Seschrock 		} else {
5346316Seschrock 			aip->lsai_data = aip->lsai_buf + off;
5356316Seschrock 			off += buflen;
5366316Seschrock 		}
5376316Seschrock 		aip->lsai_data_alloc = buflen;
5386316Seschrock 		if (flags & LIBSCSI_AF_WRITE)
5396316Seschrock 			aip->lsai_data_len = buflen;
5406316Seschrock 	}
5416316Seschrock 
5426316Seschrock 	if (flags & LIBSCSI_AF_RQSENSE) {
5436316Seschrock 		aip->lsai_sense_data = aip->lsai_buf + off;
5446316Seschrock 		off += UINT8_MAX;
5456316Seschrock 	}
5466316Seschrock 
5476316Seschrock 	aip->lsai_status = LIBSCSI_STATUS_INVALID;
5486316Seschrock 
5496316Seschrock 	return ((libscsi_action_t *)aip);
5506316Seschrock }
5516316Seschrock 
5526316Seschrock void
libscsi_action_free(libscsi_action_t * ap)5536316Seschrock libscsi_action_free(libscsi_action_t *ap)
5546316Seschrock {
5556316Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
5566316Seschrock 
5576316Seschrock 	libscsi_free(aip->lsai_hdl, aip);
5586316Seschrock }
5596316Seschrock 
5606316Seschrock /*
5616316Seschrock  * For testing purposes, we allow data to be corrupted via an environment
5626316Seschrock  * variable setting.  This helps ensure that higher level software can cope with
5636316Seschrock  * arbitrarily broken targets.  The mtbf value represents the number of bytes we
5646316Seschrock  * will see, on average, in between each failure.  Therefore, for each N bytes,
5656316Seschrock  * we would expect to see (N / mtbf) bytes of corruption.
5666316Seschrock  */
5676316Seschrock static void
scsi_inject_errors(void * data,size_t len,uint_t mtbf)5686316Seschrock scsi_inject_errors(void *data, size_t len, uint_t mtbf)
5696316Seschrock {
5706316Seschrock 	char *buf = data;
5716316Seschrock 	double prob;
5726316Seschrock 	size_t index;
5736316Seschrock 
5746316Seschrock 	if (len == 0)
5756316Seschrock 		return;
5766316Seschrock 
5776316Seschrock 	prob = (double)len / mtbf;
5786316Seschrock 
5796316Seschrock 	while (prob > 1) {
5806316Seschrock 		index = lrand48() % len;
5816316Seschrock 		buf[index] = (lrand48() % 256);
5826316Seschrock 		prob -= 1;
5836316Seschrock 	}
5846316Seschrock 
5856316Seschrock 	if (drand48() <= prob) {
5866316Seschrock 		index = lrand48() % len;
5876316Seschrock 		buf[index] = (lrand48() % 256);
5886316Seschrock 	}
5896316Seschrock }
5906316Seschrock 
5916316Seschrock int
libscsi_exec(libscsi_action_t * ap,libscsi_target_t * tp)5926316Seschrock libscsi_exec(libscsi_action_t *ap, libscsi_target_t *tp)
5936316Seschrock {
5946316Seschrock 	libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
5956316Seschrock 	libscsi_hdl_t *hp = aip->lsai_hdl;
5966316Seschrock 	int ret;
5976316Seschrock 
5986316Seschrock 	if (tp->lst_mtbf_write != 0 &&
5996316Seschrock 	    (aip->lsai_flags & LIBSCSI_AF_WRITE)) {
6006316Seschrock 		scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
6016316Seschrock 		    tp->lst_mtbf_write);
6026316Seschrock 	}
6036316Seschrock 
6046316Seschrock 	if (tp->lst_mtbf_cdb != 0) {
6056316Seschrock 		scsi_inject_errors(aip->lsai_cdb, aip->lsai_cdb_len,
6066316Seschrock 		    tp->lst_mtbf_cdb);
6076316Seschrock 	}
6086316Seschrock 
6096316Seschrock 	ret = tp->lst_engine->lse_ops->lseo_exec(hp, tp->lst_priv, ap);
6106316Seschrock 
6116316Seschrock 	if (ret == 0 && tp->lst_mtbf_read != 0 &&
6126316Seschrock 	    (aip->lsai_flags & LIBSCSI_AF_READ)) {
6136316Seschrock 		scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
6146316Seschrock 		    tp->lst_mtbf_read);
6156316Seschrock 	}
6166316Seschrock 
6176316Seschrock 	return (ret);
6186316Seschrock }
619