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