xref: /illumos-gate/usr/src/lib/scsi/libscsi/common/scsi_engine.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 *
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
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 *
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 *
206 libscsi_get_handle(libscsi_target_t *tp)
207 {
208 	return (tp->lst_hdl);
209 }
210 
211 void
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
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
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
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
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
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 *
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
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
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
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
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
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 *
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 *
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
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
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
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
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