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