1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2017, Joyent, Inc.
25 */
26
27 #include <sys/types.h>
28 #include <sys/isa_defs.h>
29 #include <sys/systeminfo.h>
30 #include <sys/scsi/generic/commands.h>
31 #include <sys/scsi/impl/commands.h>
32 #include <sys/scsi/impl/uscsi.h>
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stddef.h>
37 #include <string.h>
38 #include <dlfcn.h>
39 #include <limits.h>
40
41 #include <scsi/libscsi.h>
42 #include "libscsi_impl.h"
43
44 static const libscsi_engine_t *
get_engine(libscsi_hdl_t * hp,const char * name)45 get_engine(libscsi_hdl_t *hp, const char *name)
46 {
47 libscsi_engine_impl_t *eip;
48 const libscsi_engine_t *ep;
49 const char *engine_path, *p, *q;
50 char engine_dir[MAXPATHLEN];
51 char engine_lib[MAXPATHLEN];
52 char init_name[MAXPATHLEN];
53 void *dl_hdl;
54 libscsi_engine_init_f init;
55 boolean_t found_lib = B_FALSE, found_init = B_FALSE;
56 int dirs_tried = 0;
57 char isa[257];
58
59 for (eip = hp->lsh_engines; eip != NULL; eip = eip->lsei_next) {
60 if (strcmp(eip->lsei_engine->lse_name, name) == 0)
61 return (eip->lsei_engine);
62 }
63
64 if ((engine_path = getenv("LIBSCSI_ENGINE_PATH")) == NULL)
65 engine_path = LIBSCSI_DEFAULT_ENGINE_PATH;
66
67 #if defined(_LP64)
68 if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
69 isa[0] = '\0';
70 #else
71 isa[0] = '\0';
72 #endif
73
74 for (p = engine_path; p != NULL; p = q) {
75 if ((q = strchr(p, ':')) != NULL) {
76 ptrdiff_t len = q - p;
77 (void) strncpy(engine_dir, p, len);
78 engine_dir[len] = '\0';
79 while (*q == ':')
80 ++q;
81 if (*q == '\0')
82 q = NULL;
83 if (len == 0)
84 continue;
85 } else {
86 (void) strcpy(engine_dir, p);
87 }
88 if (engine_dir[0] != '/')
89 continue;
90
91 ++dirs_tried;
92
93 (void) snprintf(engine_lib, MAXPATHLEN, "%s/%s/%s%s",
94 engine_dir, isa, name, LIBSCSI_ENGINE_EXT);
95
96 dl_hdl = dlopen(engine_lib,
97 RTLD_LOCAL | RTLD_LAZY | RTLD_PARENT);
98 if (dl_hdl == NULL) {
99 if (!found_lib)
100 (void) libscsi_error(hp, ESCSI_NOENGINE,
101 "unable to dlopen %s: %s", engine_lib,
102 dlerror());
103 continue;
104 }
105 found_lib = B_TRUE;
106 (void) snprintf(init_name, MAXPATHLEN, "libscsi_%s_init", name);
107 init = (libscsi_engine_init_f)dlsym(dl_hdl, init_name);
108 if (init == NULL) {
109 if (!found_init)
110 (void) libscsi_error(hp, ESCSI_NOENGINE,
111 "failed to find %s in %s: %s", init_name,
112 engine_lib, dlerror());
113 (void) dlclose(dl_hdl);
114 continue;
115 }
116 if ((ep = init(hp)) == NULL) {
117 (void) dlclose(dl_hdl);
118 /*
119 * libscsi errno set by init.
120 */
121 return (NULL);
122 }
123 if (ep->lse_libversion != hp->lsh_version) {
124 (void) dlclose(dl_hdl);
125 (void) libscsi_error(hp, ESCSI_ENGINE_VER, "engine "
126 "%s version %u does not match library version %u",
127 engine_lib, ep->lse_libversion, hp->lsh_version);
128 return (NULL);
129 }
130
131 eip = libscsi_zalloc(hp, sizeof (libscsi_engine_impl_t));
132 if (eip == NULL) {
133 (void) dlclose(dl_hdl);
134 return (NULL);
135 }
136 eip->lsei_engine = ep;
137 eip->lsei_dl_hdl = dl_hdl;
138 eip->lsei_next = hp->lsh_engines;
139 hp->lsh_engines = eip;
140
141 return (ep);
142 }
143
144 if (dirs_tried == 0)
145 (void) libscsi_error(hp, ESCSI_ENGINE_BADPATH, "no valid "
146 "directories found in engine path %s", engine_path);
147
148 return (NULL);
149 }
150
151 static void
scsi_parse_mtbf(const char * envvar,uint_t * intp)152 scsi_parse_mtbf(const char *envvar, uint_t *intp)
153 {
154 const char *strval;
155 int intval;
156
157 if ((strval = getenv(envvar)) != NULL &&
158 (intval = atoi(strval)) > 0) {
159 srand48(gethrtime());
160 *intp = intval;
161 }
162 }
163
164 libscsi_target_t *
libscsi_open(libscsi_hdl_t * hp,const char * engine,const void * target)165 libscsi_open(libscsi_hdl_t *hp, const char *engine, const void *target)
166 {
167 const libscsi_engine_t *ep;
168 libscsi_target_t *tp;
169 void *private;
170
171 if (engine == NULL) {
172 if ((engine = getenv("LIBSCSI_DEFAULT_ENGINE")) == NULL)
173 engine = LIBSCSI_DEFAULT_ENGINE;
174 }
175
176 if ((ep = get_engine(hp, engine)) == NULL)
177 return (NULL);
178
179 if ((tp = libscsi_zalloc(hp, sizeof (libscsi_target_t))) == NULL)
180 return (NULL);
181
182 if ((private = ep->lse_ops->lseo_open(hp, target)) == NULL) {
183 libscsi_free(hp, tp);
184 return (NULL);
185 }
186
187 scsi_parse_mtbf("LIBSCSI_MTBF_CDB", &tp->lst_mtbf_cdb);
188 scsi_parse_mtbf("LIBSCSI_MTBF_READ", &tp->lst_mtbf_read);
189 scsi_parse_mtbf("LIBSCSI_MTBF_WRITE", &tp->lst_mtbf_write);
190
191 tp->lst_hdl = hp;
192 tp->lst_engine = ep;
193 tp->lst_priv = private;
194
195 ++hp->lsh_targets;
196
197 if (libscsi_get_inquiry(hp, tp) != 0) {
198 libscsi_close(hp, tp);
199 return (NULL);
200 }
201
202 return (tp);
203 }
204
205 libscsi_hdl_t *
libscsi_get_handle(libscsi_target_t * tp)206 libscsi_get_handle(libscsi_target_t *tp)
207 {
208 return (tp->lst_hdl);
209 }
210
211 void
libscsi_close(libscsi_hdl_t * hp,libscsi_target_t * tp)212 libscsi_close(libscsi_hdl_t *hp, libscsi_target_t *tp)
213 {
214 tp->lst_engine->lse_ops->lseo_close(hp, tp->lst_priv);
215 libscsi_free(hp, tp->lst_vendor);
216 libscsi_free(hp, tp->lst_product);
217 libscsi_free(hp, tp->lst_revision);
218 libscsi_free(hp, tp);
219 --hp->lsh_targets;
220 }
221
222 sam4_status_t
libscsi_action_get_status(const libscsi_action_t * ap)223 libscsi_action_get_status(const libscsi_action_t *ap)
224 {
225 const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
226
227 return (aip->lsai_status);
228 }
229
230 /*
231 * Set the timeout in seconds for this action. If no timeout is specified
232 * or if the timeout is set to 0, an implementation-specific timeout will be
233 * used (which may vary based on the target, command or other variables).
234 * Not all engines support all timeout values. Setting the timeout to a value
235 * not supported by the engine will cause engine-defined behavior when the
236 * action is executed.
237 */
238 void
libscsi_action_set_timeout(libscsi_action_t * ap,uint32_t timeout)239 libscsi_action_set_timeout(libscsi_action_t *ap, uint32_t timeout)
240 {
241 libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
242
243 aip->lsai_timeout = timeout;
244 }
245
246 /*
247 * Obtain the timeout setting for this action.
248 */
249 uint32_t
libscsi_action_get_timeout(const libscsi_action_t * ap)250 libscsi_action_get_timeout(const libscsi_action_t *ap)
251 {
252 const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
253
254 return (aip->lsai_timeout);
255 }
256
257 /*
258 * Returns the flags associated with this action. Never fails.
259 */
260 uint_t
libscsi_action_get_flags(const libscsi_action_t * ap)261 libscsi_action_get_flags(const libscsi_action_t *ap)
262 {
263 const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
264
265 return (aip->lsai_flags);
266 }
267
268 /*
269 * Return the length of the CDB buffer associated with this action. Never
270 * fails.
271 */
272 size_t
libscsi_action_get_cdblen(const libscsi_action_t * ap)273 libscsi_action_get_cdblen(const libscsi_action_t *ap)
274 {
275 const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
276
277 return (aip->lsai_cdb_len);
278 }
279
280 /*
281 * Returns the address of the action's CDB. The CDB buffer is guaranteed to
282 * be large enough to hold the complete CDB for the command specified when the
283 * action was allocated. Therefore, changing the command/opcode portion of
284 * the CDB has undefined effects. The remainder of the CDB may be modified.
285 */
286 uint8_t *
libscsi_action_get_cdb(const libscsi_action_t * ap)287 libscsi_action_get_cdb(const libscsi_action_t *ap)
288 {
289 const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
290
291 return (aip->lsai_cdb);
292 }
293
294 /*
295 * Places the address of the action buffer in the location pointed to by bp,
296 * if bp is not NULL. If ap is not NULL, it will contain the allocated size
297 * of the buffer itself. If vp is not NULL, it will contain the number of
298 * bytes of valid data currently stored in the buffer.
299 *
300 * If the action has LIBSCSI_AF_WRITE set and it has not yet been executed
301 * successfully, the entire buffer is assumed to contain valid data.
302 *
303 * If the action has LIBSCSI_AF_READ set and it has not yet been executed
304 * successfully, the amount of valid data is 0.
305 *
306 * If both LIBSCSI_AF_READ and LIBSCSI_AF_WRITE are clear, this function
307 * fails with ESCSI_BADFLAGS to indicate that the action flags are
308 * incompatible with the action data buffer.
309 */
310 int
libscsi_action_get_buffer(const libscsi_action_t * ap,uint8_t ** bp,size_t * sp,size_t * vp)311 libscsi_action_get_buffer(const libscsi_action_t *ap, uint8_t **bp,
312 size_t *sp, size_t *vp)
313 {
314 const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
315
316 if ((aip->lsai_flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) == 0)
317 return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
318 "data buffer not supported for actions with both "
319 "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE clear"));
320
321 if ((aip->lsai_flags & LIBSCSI_AF_WRITE) &&
322 aip->lsai_status == LIBSCSI_STATUS_INVALID) {
323 if (bp != NULL)
324 *bp = aip->lsai_data;
325 if (sp != NULL)
326 *sp = aip->lsai_data_alloc;
327 if (vp != NULL)
328 *vp = aip->lsai_data_alloc;
329
330 return (0);
331 }
332
333 if ((aip->lsai_flags & LIBSCSI_AF_READ) &&
334 aip->lsai_status != LIBSCSI_STATUS_INVALID) {
335 if (bp != NULL)
336 *bp = aip->lsai_data;
337 if (sp != NULL)
338 *sp = aip->lsai_data_alloc;
339 if (vp != NULL)
340 *vp = aip->lsai_data_len;
341
342 return (0);
343 }
344
345 if (aip->lsai_flags & LIBSCSI_AF_WRITE) {
346 if (bp != NULL)
347 *bp = NULL;
348 if (sp != NULL)
349 *sp = 0;
350 if (vp != NULL)
351 *vp = 0;
352 } else {
353 if (bp != NULL)
354 *bp = aip->lsai_data;
355 if (sp != NULL)
356 *sp = aip->lsai_data_alloc;
357 if (vp != NULL)
358 *vp = 0;
359 }
360
361 return (0);
362 }
363
364 /*
365 * Obtain a pointer to the sense buffer for this action, if any, along with
366 * the size of the sense buffer and the amount of valid data it contains.
367 */
368 int
libscsi_action_get_sense(const libscsi_action_t * ap,uint8_t ** bp,size_t * sp,size_t * vp)369 libscsi_action_get_sense(const libscsi_action_t *ap, uint8_t **bp,
370 size_t *sp, size_t *vp)
371 {
372 const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
373
374 if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
375 return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
376 "sense data unavailable: LIBSCSI_AF_RQSENSE is clear"));
377
378 if (vp != NULL) {
379 if (aip->lsai_status == LIBSCSI_STATUS_INVALID)
380 *vp = 0;
381 else
382 *vp = aip->lsai_sense_len;
383 }
384
385 if (bp != NULL) {
386 ASSERT(aip->lsai_sense_data != NULL);
387 *bp = aip->lsai_sense_data;
388 }
389
390 if (sp != NULL)
391 *sp = UINT8_MAX;
392
393 return (0);
394 }
395
396 /*
397 * Set the SCSI status of the action.
398 *
399 * Engines only.
400 */
401 void
libscsi_action_set_status(libscsi_action_t * ap,sam4_status_t status)402 libscsi_action_set_status(libscsi_action_t *ap, sam4_status_t status)
403 {
404 libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
405
406 ASSERT(aip->lsai_status == LIBSCSI_STATUS_INVALID);
407
408 aip->lsai_status = status;
409 }
410
411 /*
412 * Set the length of valid data returned by a READ action. If the action is
413 * not a READ action, or the length exceeds the size of the buffer, an error
414 * results.
415 *
416 * Engines only.
417 */
418 int
libscsi_action_set_datalen(libscsi_action_t * ap,size_t len)419 libscsi_action_set_datalen(libscsi_action_t *ap, size_t len)
420 {
421 libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
422
423 if ((aip->lsai_flags & LIBSCSI_AF_READ) == 0)
424 return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
425 "data cannot be returned for actions with LIBSCSI_AF_READ "
426 "clear"));
427 if (len > aip->lsai_data_alloc)
428 return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
429 "data length %lu exceeds allocated buffer capacity %lu",
430 (ulong_t)len, (ulong_t)aip->lsai_data_alloc));
431
432 ASSERT(aip->lsai_data_len == 0);
433 aip->lsai_data_len = len;
434
435 return (0);
436 }
437
438 /*
439 * Set the length of the valid sense data returned following the command, if
440 * LIBSCSI_AF_RQSENSE is set for this action. Otherwise, fail.
441 *
442 * Engines only.
443 */
444 int
libscsi_action_set_senselen(libscsi_action_t * ap,size_t len)445 libscsi_action_set_senselen(libscsi_action_t *ap, size_t len)
446 {
447 libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
448
449 if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
450 return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
451 "sense data not supported: LIBSCSI_AF_RQSENSE is clear"));
452
453 if (len > UINT8_MAX)
454 return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
455 "sense length %lu exceeds allocated buffer capacity %lu",
456 (ulong_t)len, (ulong_t)UINT8_MAX));
457
458 ASSERT(aip->lsai_sense_len == 0);
459 aip->lsai_sense_len = len;
460
461 return (0);
462 }
463
464 /*
465 * Allocate an action object. The object will contain a CDB area sufficiently
466 * large to hold a CDB for the given command, and the CDB's opcode will be
467 * filled in. A pointer to this CDB, the contents of which may be modified by
468 * the caller, may be obtained by a subsequent call to libscsi_action_cdb().
469 *
470 * If flags includes LIBSCSI_AF_READ or LIBSCSI_AF_WRITE, buflen must be
471 * greater than zero. Otherwise, buflen must be 0 and buf must be NULL.
472 * If buflen is nonzero but buf is NULL, a suitably-sized buffer will be
473 * allocated; otherwise, the specified buffer will be used. In either case,
474 * a pointer to the buffer may be obtained via a subsequent call to
475 * libscsi_action_buffer().
476 *
477 * If flags includes LIBSCSI_AF_RQSENSE, a REQUEST SENSE command will be
478 * issued immediately following the termination of the specified command.
479 * A buffer will be allocated to receive this sense data. Following successful
480 * execution of the action, a pointer to this buffer and the length of
481 * valid sense data may be obtained by a call to libscsi_action_sense().
482 * If cmd is SPC3_CMD_REQUEST_SENSE, this flag must be clear.
483 */
484 libscsi_action_t *
libscsi_action_alloc_vendor(libscsi_hdl_t * hp,spc3_cmd_t cmd,size_t cdbsz,uint_t flags,void * buf,size_t buflen)485 libscsi_action_alloc_vendor(libscsi_hdl_t *hp, spc3_cmd_t cmd, size_t cdbsz,
486 uint_t flags, void *buf, size_t buflen)
487 {
488 libscsi_action_impl_t *aip;
489 size_t sz;
490 ptrdiff_t off;
491
492 /*
493 * If there's no buffer, it makes no sense to try to read or write
494 * data. Likewise, if we're neither reading nor writing data, we
495 * should not have a buffer. Both of these are programmer error.
496 */
497 if (buflen == 0 && (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
498 (void) libscsi_error(hp, ESCSI_NEEDBUF, "a buffer is "
499 "required when reading or writing");
500 return (NULL);
501 }
502 if (buflen > 0 && !(flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
503 (void) libscsi_error(hp, ESCSI_BADFLAGS, "one of "
504 "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE must be specified "
505 "in order to use a buffer");
506 return (NULL);
507 }
508
509 if (cdbsz == 0) {
510 (void) libscsi_error(hp, ESCSI_BADLENGTH, "the supplied CDB "
511 "buffer size has an invalid length, it must be non-zero.");
512 return (NULL);
513 }
514
515 sz = cdbsz;
516
517 /*
518 * If the caller has asked for a buffer but has not provided one, we
519 * will allocate it in our internal buffer along with the CDB and
520 * request sense space (if requested).
521 */
522 if (buf == NULL)
523 sz += buflen;
524
525 if (flags & LIBSCSI_AF_RQSENSE)
526 sz += UINT8_MAX;
527
528 sz += offsetof(libscsi_action_impl_t, lsai_buf[0]);
529
530 if ((aip = libscsi_zalloc(hp, sz)) == NULL)
531 return (NULL);
532
533 aip->lsai_hdl = hp;
534 aip->lsai_flags = flags;
535
536 off = 0;
537
538 aip->lsai_cdb = aip->lsai_buf + off;
539 aip->lsai_cdb_len = cdbsz;
540 off += cdbsz;
541 aip->lsai_cdb[0] = (uint8_t)cmd;
542
543 if (buflen > 0) {
544 if (buf != NULL) {
545 aip->lsai_data = buf;
546 } else {
547 aip->lsai_data = aip->lsai_buf + off;
548 off += buflen;
549 }
550 aip->lsai_data_alloc = buflen;
551 if (flags & LIBSCSI_AF_WRITE)
552 aip->lsai_data_len = buflen;
553 }
554
555 if (flags & LIBSCSI_AF_RQSENSE) {
556 aip->lsai_sense_data = aip->lsai_buf + off;
557 off += UINT8_MAX;
558 }
559
560 aip->lsai_status = LIBSCSI_STATUS_INVALID;
561
562 return ((libscsi_action_t *)aip);
563 }
564
565 libscsi_action_t *
libscsi_action_alloc(libscsi_hdl_t * hp,spc3_cmd_t cmd,uint_t flags,void * buf,size_t buflen)566 libscsi_action_alloc(libscsi_hdl_t *hp, spc3_cmd_t cmd, uint_t flags,
567 void *buf, size_t buflen)
568 {
569 size_t cdbsz;
570
571 if (cmd == SPC3_CMD_REQUEST_SENSE && (flags & LIBSCSI_AF_RQSENSE)) {
572 (void) libscsi_error(hp, ESCSI_BADFLAGS, "request sense "
573 "flag not allowed for request sense command");
574 return (NULL);
575 }
576
577 if ((cdbsz = libscsi_cmd_cdblen(hp, cmd)) == 0)
578 return (NULL);
579
580 return (libscsi_action_alloc_vendor(hp, cmd, cdbsz, flags, buf,
581 buflen));
582 }
583
584 void
libscsi_action_free(libscsi_action_t * ap)585 libscsi_action_free(libscsi_action_t *ap)
586 {
587 libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
588
589 libscsi_free(aip->lsai_hdl, aip);
590 }
591
592 /*
593 * For testing purposes, we allow data to be corrupted via an environment
594 * variable setting. This helps ensure that higher level software can cope with
595 * arbitrarily broken targets. The mtbf value represents the number of bytes we
596 * will see, on average, in between each failure. Therefore, for each N bytes,
597 * we would expect to see (N / mtbf) bytes of corruption.
598 */
599 static void
scsi_inject_errors(void * data,size_t len,uint_t mtbf)600 scsi_inject_errors(void *data, size_t len, uint_t mtbf)
601 {
602 char *buf = data;
603 double prob;
604 size_t index;
605
606 if (len == 0)
607 return;
608
609 prob = (double)len / mtbf;
610
611 while (prob > 1) {
612 index = lrand48() % len;
613 buf[index] = (lrand48() % 256);
614 prob -= 1;
615 }
616
617 if (drand48() <= prob) {
618 index = lrand48() % len;
619 buf[index] = (lrand48() % 256);
620 }
621 }
622
623 int
libscsi_exec(libscsi_action_t * ap,libscsi_target_t * tp)624 libscsi_exec(libscsi_action_t *ap, libscsi_target_t *tp)
625 {
626 libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
627 libscsi_hdl_t *hp = aip->lsai_hdl;
628 int ret;
629
630 if (tp->lst_mtbf_write != 0 &&
631 (aip->lsai_flags & LIBSCSI_AF_WRITE)) {
632 scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
633 tp->lst_mtbf_write);
634 }
635
636 if (tp->lst_mtbf_cdb != 0) {
637 scsi_inject_errors(aip->lsai_cdb, aip->lsai_cdb_len,
638 tp->lst_mtbf_cdb);
639 }
640
641 ret = tp->lst_engine->lse_ops->lseo_exec(hp, tp->lst_priv, ap);
642
643 if (ret == 0 && tp->lst_mtbf_read != 0 &&
644 (aip->lsai_flags & LIBSCSI_AF_READ)) {
645 scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
646 tp->lst_mtbf_read);
647 }
648
649 return (ret);
650 }
651
652 int
libscsi_max_transfer(libscsi_target_t * tp,size_t * sizep)653 libscsi_max_transfer(libscsi_target_t *tp, size_t *sizep)
654 {
655 libscsi_hdl_t *hp = tp->lst_hdl;
656 if (tp->lst_engine->lse_ops->lseo_max_transfer == NULL) {
657 return (libscsi_error(hp, ESCSI_NOTSUP, "max transfer "
658 "request not supported by engine"));
659 }
660
661 return (tp->lst_engine->lse_ops->lseo_max_transfer(hp, tp->lst_priv,
662 sizep));
663 }
664