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