1 /*
2  * Copyright (C) 2008-2014 Daisuke Aoyama <aoyama@peach.ne.jp>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <inttypes.h>
33 #include <stdint.h>
34 
35 #include <errno.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <pthread.h>
39 #include <time.h>
40 
41 #include <fcntl.h>
42 #include <unistd.h>
43 
44 #ifdef HAVE_UUID_H
45 #include <uuid.h>
46 #endif
47 
48 #include "istgt.h"
49 #include "istgt_ver.h"
50 #include "istgt_log.h"
51 #include "istgt_conf.h"
52 #include "istgt_sock.h"
53 #include "istgt_misc.h"
54 #include "istgt_crc32c.h"
55 #include "istgt_md5.h"
56 #include "istgt_iscsi.h"
57 #include "istgt_lu.h"
58 #include "istgt_proto.h"
59 #include "istgt_scsi.h"
60 #include "istgt_queue.h"
61 
62 #if !defined(__GNUC__)
63 #undef __attribute__
64 #define __attribute__(x)
65 #endif
66 
67 #ifndef O_FSYNC
68 #define O_FSYNC O_SYNC
69 #endif
70 
71 //#define ISTGT_TRACE_DISK
72 
73 typedef enum {
74 	ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE = 0x01,
75 	ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS = 0x03,
76 	ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY = 0x05,
77 	ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY = 0x06,
78 	ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS = 0x07,
79 	ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS = 0x08,
80 } ISTGT_LU_PR_TYPE;
81 
82 #define PR_ALLOW(WE,EA,ALLRR,WERR,EARR) \
83 	((((WE)&1) << 4) | (((EA)&1) << 3) | (((ALLRR)&1) << 2) \
84 	 | (((WERR)&1) << 1) | (((EARR)&1) << 0))
85 #define PR_ALLOW_WE    0x0010
86 #define PR_ALLOW_EA    0x0008
87 #define PR_ALLOW_ALLRR 0x0004
88 #define PR_ALLOW_WERR  0x0002
89 #define PR_ALLOW_EARR  0x0001
90 
91 #define BUILD_SENSE(SK,ASC,ASCQ)					\
92 	do {								\
93 		*sense_len =						\
94 			istgt_lu_disk_build_sense_data(spec, sense_data, \
95 			    ISTGT_SCSI_SENSE_ ## SK,			\
96 			    (ASC), (ASCQ));				\
97 	} while (0)
98 #define BUILD_SENSE2(SK,ASC,ASCQ)					\
99 	do {								\
100 		*sense_len =						\
101 			istgt_lu_disk_build_sense_data2(spec, sense_data, \
102 			    ISTGT_SCSI_SENSE_ ## SK,			\
103 			    (ASC), (ASCQ));				\
104 	} while (0)
105 
106 static void istgt_lu_disk_free_pr_key(ISTGT_LU_PR_KEY *prkey);
107 static int istgt_lu_disk_build_sense_data(ISTGT_LU_DISK *spec, uint8_t *data, int sk, int asc, int ascq);
108 static int istgt_lu_disk_queue_abort_ITL(ISTGT_LU_DISK *spec, const char *initiator_port);
109 
110 static int
istgt_lu_disk_open_raw(ISTGT_LU_DISK * spec,int flags,int mode)111 istgt_lu_disk_open_raw(ISTGT_LU_DISK *spec, int flags, int mode)
112 {
113 	int rc;
114 
115 	rc = open(spec->file, flags, mode);
116 	if (rc < 0) {
117 		return -1;
118 	}
119 	spec->fd = rc;
120 	spec->foffset = 0;
121 	return 0;
122 }
123 
124 static int
istgt_lu_disk_close_raw(ISTGT_LU_DISK * spec)125 istgt_lu_disk_close_raw(ISTGT_LU_DISK *spec)
126 {
127 	int rc;
128 
129 	if (spec->fd == -1)
130 		return 0;
131 	rc = close(spec->fd);
132 	if (rc < 0) {
133 		return -1;
134 	}
135 	spec->fd = -1;
136 	spec->foffset = 0;
137 	return 0;
138 }
139 
140 #if 0
141 static off_t
142 istgt_lu_disk_lseek_raw(ISTGT_LU_DISK *spec, off_t offset, int whence)
143 {
144 	off_t rc;
145 
146 	rc = lseek(spec->fd, offset, whence);
147 	if (rc < 0) {
148 		return -1;
149 	}
150 	spec->foffset = offset;
151 	return rc;
152 }
153 #endif
154 
155 static int64_t
istgt_lu_disk_seek_raw(ISTGT_LU_DISK * spec,uint64_t offset)156 istgt_lu_disk_seek_raw(ISTGT_LU_DISK *spec, uint64_t offset)
157 {
158 	off_t rc;
159 
160 	rc = lseek(spec->fd, (off_t) offset, SEEK_SET);
161 	if (rc < 0) {
162 		return -1;
163 	}
164 	spec->foffset = offset;
165 	return 0;
166 }
167 
168 static int64_t
istgt_lu_disk_read_raw(ISTGT_LU_DISK * spec,void * buf,uint64_t nbytes)169 istgt_lu_disk_read_raw(ISTGT_LU_DISK *spec, void *buf, uint64_t nbytes)
170 {
171 	int64_t rc;
172 
173 	if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) {
174 		if (spec->foffset + nbytes <= spec->fsize) {
175 			/* inside media */
176 			rc = (int64_t) read(spec->fd, buf, (size_t) nbytes);
177 		} else if (spec->foffset >= spec->fsize) {
178 			/* outside media */
179 			memset(buf, 0, nbytes);
180 			rc = nbytes;
181 			if (spec->foffset + nbytes >= spec->size) {
182 				rc = spec->size - spec->foffset;
183 			}
184 		} else if (spec->foffset + nbytes > spec->fsize) {
185 			/* both */
186 			uint64_t request = spec->fsize - spec->foffset;
187 			memset(buf, 0, nbytes);
188 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
189 			    "read %"PRIu64" bytes at %"PRIu64"/%"PRIu64"\n",
190 				    request, spec->foffset, spec->fsize);
191 			rc = (int64_t) read(spec->fd, buf, (size_t) request);
192 			if (rc < 0) {
193 				return -1;
194 			}
195 			if ((uint64_t) rc != request) {
196 				/* read size < request */
197 				if (spec->foffset + rc >= spec->size) {
198 					rc = spec->size - spec->foffset;
199 				}
200 				spec->foffset += rc;
201 				return rc;
202 			}
203 			rc = nbytes;
204 			if (spec->foffset + nbytes >= spec->size) {
205 				rc = spec->size - spec->foffset;
206 			}
207 		} else {
208 			rc = -1;
209 		}
210 		if (rc < 0) {
211 			return -1;
212 		}
213 		spec->foffset += rc;
214 		return rc;
215 	}
216 	rc = (int64_t) read(spec->fd, buf, (size_t) nbytes);
217 	if (rc < 0) {
218 		return -1;
219 	}
220 	spec->foffset += rc;
221 	return rc;
222 }
223 
224 static int64_t
istgt_lu_disk_write_raw(ISTGT_LU_DISK * spec,const void * buf,uint64_t nbytes)225 istgt_lu_disk_write_raw(ISTGT_LU_DISK *spec, const void *buf, uint64_t nbytes)
226 {
227 	int64_t rc;
228 
229 	if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) {
230 		if (spec->foffset + nbytes <= spec->fsize) {
231 			/* inside media */
232 			rc = (int64_t) write(spec->fd, buf, (size_t) nbytes);
233 		} else if (spec->foffset + nbytes <= ISTGT_LU_MEDIA_SIZE_MIN) {
234 			/* allways write in minimum size */
235 			rc = (int64_t) write(spec->fd, buf, (size_t) nbytes);
236 		} else if (spec->foffset >= spec->fsize) {
237 			/* outside media */
238 			const uint8_t *p = (const uint8_t *) buf;
239 			uint64_t n;
240 			for (n = 0; n < nbytes; n++) {
241 				if (p[n] != 0)
242 					break;
243 			}
244 			if (n == nbytes) {
245 				/* write all zero (skip) */
246 				ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
247 				    "write zero %"PRIu64" bytes at %"PRIu64"/%"PRIu64"\n",
248 				    nbytes, spec->foffset, spec->fsize);
249 				rc = nbytes;
250 				spec->foffset += rc;
251 				return rc;
252 			}
253 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
254 			    "write %"PRIu64" bytes at %"PRIu64"/%"PRIu64"\n",
255 			    nbytes, spec->foffset, spec->fsize);
256 			rc = (int64_t) write(spec->fd, buf, (size_t) nbytes);
257 		} else if (spec->foffset + nbytes > spec->fsize) {
258 			/* both */
259 			rc = (int64_t) write(spec->fd, buf, (size_t) nbytes);
260 		} else {
261 			rc = -1;
262 		}
263 		if (rc < 0) {
264 			return -1;
265 		}
266 		spec->foffset += rc;
267 		if (spec->foffset > spec->fsize) {
268 			spec->fsize = spec->foffset;
269 		}
270 		return rc;
271 	}
272 	rc = (int64_t) write(spec->fd, buf, (size_t) nbytes);
273 	if (rc < 0) {
274 		return -1;
275 	}
276 	spec->foffset += rc;
277 	if (spec->foffset > spec->fsize) {
278 		spec->fsize = spec->foffset;
279 	}
280 	return rc;
281 }
282 
283 static int64_t
istgt_lu_disk_sync_raw(ISTGT_LU_DISK * spec,uint64_t offset,uint64_t nbytes)284 istgt_lu_disk_sync_raw(ISTGT_LU_DISK *spec, uint64_t offset, uint64_t nbytes)
285 {
286 	int64_t rc;
287 
288 	rc = (int64_t) fsync(spec->fd);
289 	if (rc < 0) {
290 		return -1;
291 	}
292 	spec->foffset = offset + nbytes;
293 	return rc;
294 }
295 
296 static int
istgt_lu_disk_allocate_raw(ISTGT_LU_DISK * spec)297 istgt_lu_disk_allocate_raw(ISTGT_LU_DISK *spec)
298 {
299 	uint8_t *data;
300 	uint64_t fsize;
301 	uint64_t size;
302 	uint64_t blocklen;
303 	uint64_t offset;
304 	uint64_t nbytes;
305 	int64_t rc;
306 
307 	size = spec->size;
308 	blocklen = spec->blocklen;
309 	nbytes = blocklen;
310 	data = xmalloc(nbytes);
311 	memset(data, 0, nbytes);
312 
313 	fsize = istgt_lu_get_filesize(spec->file);
314 	if (fsize > size) {
315 		xfree(data);
316 		return 0;
317 	}
318 	spec->fsize = fsize;
319 
320 	offset = size - nbytes;
321 	rc = istgt_lu_disk_seek_raw(spec, offset);
322 	if (rc == -1) {
323 		ISTGT_ERRLOG("lu_disk_seek() failed\n");
324 		xfree(data);
325 		return -1;
326 	}
327 	rc = istgt_lu_disk_read_raw(spec, data, nbytes);
328 	/* EOF is OK */
329 	if (rc == -1) {
330 		ISTGT_ERRLOG("lu_disk_read() failed\n");
331 		xfree(data);
332 		return -1;
333 	}
334 	if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) {
335 		/* allocate minimum size */
336 		if (fsize < ISTGT_LU_MEDIA_SIZE_MIN) {
337 			fsize = ISTGT_LU_MEDIA_SIZE_MIN;
338 			if (size < ISTGT_LU_MEDIA_SIZE_MIN) {
339 				fsize = size;
340 			}
341 			offset = fsize - nbytes;
342 			rc = istgt_lu_disk_seek_raw(spec, offset);
343 			if (rc == -1) {
344 				ISTGT_ERRLOG("lu_disk_seek() failed\n");
345 				xfree(data);
346 				return -1;
347 			}
348 			rc = istgt_lu_disk_write_raw(spec, data, nbytes);
349 			if (rc == -1 || (uint64_t) rc != nbytes) {
350 				ISTGT_ERRLOG("lu_disk_write() failed\n");
351 				xfree(data);
352 				return -1;
353 			}
354 			spec->fsize = fsize;
355 			spec->foffset = fsize;
356 		}
357 	} else {
358 		/* allocate complete size */
359 		rc = istgt_lu_disk_seek_raw(spec, offset);
360 		if (rc == -1) {
361 			ISTGT_ERRLOG("lu_disk_seek() failed\n");
362 			xfree(data);
363 			return -1;
364 		}
365 		rc = istgt_lu_disk_write_raw(spec, data, nbytes);
366 		if (rc == -1 || (uint64_t) rc != nbytes) {
367 			ISTGT_ERRLOG("lu_disk_write() failed\n");
368 			xfree(data);
369 			return -1;
370 		}
371 		spec->foffset = size;
372 	}
373 
374 	xfree(data);
375 	return 0;
376 }
377 
378 static int
istgt_lu_disk_setcache_raw(ISTGT_LU_DISK * spec)379 istgt_lu_disk_setcache_raw(ISTGT_LU_DISK *spec)
380 {
381 	int flags;
382 	int rc;
383 	int fd;
384 
385 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_disk_setcache\n");
386 
387 	fd = spec->fd;
388 	if (spec->read_cache) {
389 		/* not implement */
390 	} else {
391 		/* not implement */
392 	}
393 	flags = fcntl(fd , F_GETFL, 0);
394 	if (flags != -1) {
395 		if (spec->write_cache) {
396 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "write cache enable\n");
397 			rc = fcntl(fd, F_SETFL, (flags & ~O_FSYNC));
398 			spec->write_cache = 1;
399 		} else {
400 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "write cache disable\n");
401 			rc = fcntl(fd, F_SETFL, (flags | O_FSYNC));
402 			spec->write_cache = 0;
403 		}
404 		if (rc == -1) {
405 #if 0
406 			ISTGT_ERRLOG("LU%d: LUN%d: fcntl(F_SETFL) failed(errno=%d)\n",
407 			    spec->num, spec->lun, errno);
408 #endif
409 		}
410 	} else {
411 		ISTGT_ERRLOG("LU%d: LUN%d: fcntl(F_GETFL) failed(errno=%d)\n",
412 		    spec->num, spec->lun, errno);
413 	}
414 	return 0;
415 }
416 
417 static const char *
istgt_get_disktype_by_ext(const char * file)418 istgt_get_disktype_by_ext(const char *file)
419 {
420 	size_t n;
421 
422 	if (file == NULL || file[0] == '\n')
423 		return "RAW";
424 
425 	n = strlen(file);
426 	if (n > 4 && strcasecmp(file + (n - 4), ".vdi") == 0)
427 		return "VDI";
428 	if (n > 4 && strcasecmp(file + (n - 4), ".vhd") == 0)
429 		return "VHD";
430 	if (n > 5 && strcasecmp(file + (n - 5), ".vmdk") == 0)
431 		return "VMDK";
432 
433 	if (n > 5 && strcasecmp(file + (n - 5), ".qcow") == 0)
434 		return "QCOW";
435 	if (n > 6 && strcasecmp(file + (n - 6), ".qcow2") == 0)
436 		return "QCOW";
437 	if (n > 4 && strcasecmp(file + (n - 4), ".qed") == 0)
438 		return "QED";
439 	if (n > 5 && strcasecmp(file + (n - 5), ".vhdx") == 0)
440 		return "VHDX";
441 
442 	return "RAW";
443 }
444 
445 int
istgt_lu_disk_init(ISTGT_Ptr istgt,ISTGT_LU_Ptr lu)446 istgt_lu_disk_init(ISTGT_Ptr istgt __attribute__((__unused__)), ISTGT_LU_Ptr lu)
447 {
448 	ISTGT_LU_DISK *spec;
449 	uint64_t gb_size;
450 	uint64_t mb_size;
451 #ifdef HAVE_UUID_H
452 	uint32_t status;
453 #endif /* HAVE_UUID_H */
454 	int mb_digit;
455 	int flags;
456 	int newfile;
457 	int rc;
458 	int i, j;
459 
460 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_disk_init\n");
461 
462 	printf("LU%d HDD UNIT\n", lu->num);
463 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n",
464 	    lu->num, lu->name);
465 	for (i = 0; i < lu->maxlun; i++) {
466 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
467 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n",
468 			    lu->num, i);
469 			lu->lun[i].spec = NULL;
470 			continue;
471 		}
472 		if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) {
473 			ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num);
474 			return -1;
475 		}
476 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d storage\n",
477 		    lu->num, i);
478 
479 		spec = xmalloc(sizeof *spec);
480 		memset(spec, 0, sizeof *spec);
481 		spec->lu = lu;
482 		spec->num = lu->num;
483 		spec->lun = i;
484 		spec->fd = -1;
485 		if (spec->lu->lun[i].readcache) {
486 			spec->read_cache = 1;
487 		} else {
488 			spec->read_cache = 0;
489 		}
490 		if (spec->lu->lun[i].writecache) {
491 			spec->write_cache = 1;
492 		} else {
493 			spec->write_cache = 0;
494 		}
495 		if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) {
496 			spec->wbufsize = ISTGT_LU_MAX_WRITE_CACHE_SIZE;
497 			spec->wbuf = xmalloc(spec->wbufsize);
498 			memset(spec->wbuf, 0, spec->wbufsize);
499 		} else {
500 			spec->wbufsize = 0;
501 			spec->wbuf = NULL;
502 		}
503 		spec->woffset = 0;
504 		spec->wnbytes = 0;
505 		spec->req_write_cache = 0;
506 		spec->err_write_cache = 0;
507 		spec->thin_provisioning = 0;
508 		spec->watssize = 0;
509 		spec->watsbuf = NULL;
510 
511 		rc = pthread_mutex_init(&spec->ats_mutex, NULL);
512 		if (rc != 0) {
513 			ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num);
514 			return -1;
515 		}
516 
517 		spec->queue_depth = lu->queue_depth;
518 		rc = pthread_mutex_init(&spec->cmd_queue_mutex, &istgt->mutex_attr);
519 		if (rc != 0) {
520 			ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num);
521 			return -1;
522 		}
523 		istgt_queue_init(&spec->cmd_queue);
524 		rc = pthread_mutex_init(&spec->wait_lu_task_mutex, NULL);
525 		if (rc != 0) {
526 			ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num);
527 			return -1;
528 		}
529 		spec->wait_lu_task = NULL;
530 
531 		spec->npr_keys = 0;
532 		/* spec is cleared, only pointer is handled */
533 		for (j = 0; j < MAX_LU_RESERVE; j++) {
534 			spec->pr_keys[j].registered_initiator_port = NULL;
535 			spec->pr_keys[j].registered_target_port = NULL;
536 			spec->pr_keys[j].initiator_ports = NULL;
537 		}
538 		spec->pr_generation = 0;
539 		spec->rsv_port = NULL;
540 		spec->rsv_key = 0;
541 		spec->rsv_scope = 0;
542 		spec->rsv_type = 0;
543 
544 		spec->sense = 0;
545 		{
546 			int sk, asc, ascq;
547 			/* POWER ON, RESET, OR BUS DEVICE RESET OCCURRED */
548 			sk = ISTGT_SCSI_SENSE_UNIT_ATTENTION;
549 			asc = 0x29;
550 			ascq = 0x00;
551 			spec->sense = (((sk & 0xffU) << 16)
552 			    | ((asc & 0xffU) << 8)
553 			    | ((ascq & 0xffU) << 0));
554 		}
555 
556 #ifdef HAVE_UUID_H
557 		uuid_create(&spec->uuid, &status);
558 		if (status != uuid_s_ok) {
559 			ISTGT_ERRLOG("LU%d: LUN%d: uuid_create() failed\n", lu->num, i);
560 			(void) pthread_mutex_destroy(&spec->wait_lu_task_mutex);
561 			(void) pthread_mutex_destroy(&spec->cmd_queue_mutex);
562 			(void) pthread_mutex_destroy(&spec->ats_mutex);
563 			istgt_queue_destroy(&spec->cmd_queue);
564 			xfree(spec);
565 			return -1;
566 		}
567 #endif /* HAVE_UUID_H */
568 
569 		spec->file = lu->lun[i].u.storage.file;
570 		spec->size = lu->lun[i].u.storage.size;
571 		spec->disktype = istgt_get_disktype_by_ext(spec->file);
572 		if (strcasecmp(spec->disktype, "VDI") == 0
573 		    || strcasecmp(spec->disktype, "VHD") == 0
574 		    || strcasecmp(spec->disktype, "VMDK") == 0
575 		    || strcasecmp(spec->disktype, "QCOW") == 0
576 		    || strcasecmp(spec->disktype, "QED") == 0
577 		    || strcasecmp(spec->disktype, "VHDX") == 0) {
578 			rc = istgt_lu_disk_vbox_lun_init(spec, istgt, lu);
579 			if (rc < 0) {
580 				ISTGT_ERRLOG("LU%d: LUN%d: lu_disk_vbox_lun_init() failed\n",
581 				    lu->num, i);
582 				goto error_return;
583 			}
584 		} else if (strcasecmp(spec->disktype, "RAW") == 0) {
585 			spec->open = istgt_lu_disk_open_raw;
586 			spec->close = istgt_lu_disk_close_raw;
587 			spec->seek = istgt_lu_disk_seek_raw;
588 			spec->read = istgt_lu_disk_read_raw;
589 			spec->write = istgt_lu_disk_write_raw;
590 			spec->sync = istgt_lu_disk_sync_raw;
591 			spec->allocate = istgt_lu_disk_allocate_raw;
592 			spec->setcache = istgt_lu_disk_setcache_raw;
593 
594 			spec->blocklen = lu->blocklen;
595 			if (spec->blocklen != 512
596 			    && spec->blocklen != 1024
597 			    && spec->blocklen != 2048
598 			    && spec->blocklen != 4096
599 			    && spec->blocklen != 8192
600 			    && spec->blocklen != 16384
601 			    && spec->blocklen != 32768
602 			    && spec->blocklen != 65536
603 			    && spec->blocklen != 131072
604 			    && spec->blocklen != 262144
605 			    && spec->blocklen != 524288) {
606 				ISTGT_ERRLOG("LU%d: LUN%d: invalid blocklen %"PRIu64"\n",
607 				    lu->num, i, spec->blocklen);
608 			error_return:
609 				(void) pthread_mutex_destroy(&spec->wait_lu_task_mutex);
610 				(void) pthread_mutex_destroy(&spec->cmd_queue_mutex);
611 				(void) pthread_mutex_destroy(&spec->ats_mutex);
612 				istgt_queue_destroy(&spec->cmd_queue);
613 				xfree(spec);
614 				return -1;
615 			}
616 			spec->blockcnt = spec->size / spec->blocklen;
617 			if (spec->blockcnt == 0) {
618 				ISTGT_ERRLOG("LU%d: LUN%d: size zero\n", lu->num, i);
619 				goto error_return;
620 			}
621 
622 #if 0
623 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
624 			    "LU%d: LUN%d file=%s, size=%"PRIu64"\n",
625 			    lu->num, i, spec->file, spec->size);
626 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
627 			    "LU%d: LUN%d %"PRIu64" blocks, %"
628 			    PRIu64" bytes/block\n",
629 			    lu->num, i, spec->blockcnt, spec->blocklen);
630 #endif
631 			printf("LU%d: LUN%d file=%s, size=%"PRIu64"\n",
632 			    lu->num, i, spec->file, spec->size);
633 			printf("LU%d: LUN%d %"PRIu64" blocks, %"PRIu64" bytes/block\n",
634 			    lu->num, i, spec->blockcnt, spec->blocklen);
635 
636 			flags = lu->readonly ? O_RDONLY : O_RDWR;
637 			newfile = 0;
638 			rc = spec->open(spec, flags, 0666);
639 			if (rc < 0) {
640 				newfile = 1;
641 				flags = lu->readonly ? O_RDONLY : (O_CREAT | O_EXCL | O_RDWR);
642 				rc = spec->open(spec, flags, 0666);
643 				if (rc < 0) {
644 					ISTGT_ERRLOG("LU%d: LUN%d: open error(errno=%d)\n",
645 					    lu->num, i, errno);
646 					goto error_return;
647 				}
648 			}
649 			if (!lu->readonly) {
650 				rc = spec->allocate(spec);
651 				if (rc < 0) {
652 					ISTGT_ERRLOG("LU%d: LUN%d: allocate error\n",
653 					    lu->num, i);
654 					goto error_return;
655 				}
656 			}
657 			rc = spec->setcache(spec);
658 			if (rc < 0) {
659 				ISTGT_ERRLOG("LU%d: LUN%d: setcache error\n", lu->num, i);
660 				goto error_return;
661 			}
662 		} else {
663 			ISTGT_ERRLOG("LU%d: LUN%d: unsupported format\n", lu->num, i);
664 			goto error_return;
665 		}
666 
667 		gb_size = spec->size / ISTGT_LU_1GB;
668 		mb_size = (spec->size % ISTGT_LU_1GB) / ISTGT_LU_1MB;
669 		if (gb_size > 0) {
670 			mb_digit = (int) (((mb_size * 100) / 1024) / 10);
671 #if 0
672 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
673 			    "LU%d LUN%d %"PRIu64".%dGB %sstorage for %s\n",
674 			    lu->num, i, gb_size, mb_digit,
675 			    lu->readonly ? "readonly " : "", lu->name);
676 #endif
677 			printf("LU%d: LUN%d %"PRIu64".%dGB %sstorage for %s\n",
678 			    lu->num, i, gb_size, mb_digit,
679 			    lu->readonly ? "readonly " : "", lu->name);
680 		} else {
681 #if 0
682 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
683 			    "LU%d: LUN%d %"PRIu64"MB %sstorage for %s\n",
684 			    lu->num, i, mb_size,
685 			    lu->readonly ? "readonly " : "", lu->name);
686 #endif
687 			printf("LU%d: LUN%d %"PRIu64"MB %sstorage for %s\n",
688 			    lu->num, i, mb_size,
689 			    lu->readonly ? "readonly " : "", lu->name);
690 		}
691 		if (spec->lu->lun[i].serial != NULL) {
692 			printf("LU%d: LUN%d serial %s\n",
693 			    lu->num, i, spec->lu->lun[i].serial);
694 		} else {
695 			printf("LU%d: LUN%d serial %s\n",
696 			    lu->num, i, spec->lu->inq_serial);
697 		}
698 		printf("LU%d: LUN%d ", lu->num, i);
699 		if (spec->read_cache) {
700 			printf("read cache enabled");
701 		} else {
702 			printf("read cache disabled");
703 		}
704 		printf(", ");
705 		if (spec->write_cache) {
706 			printf("write cache enabled");
707 		} else {
708 			printf("write cache disabled");
709 		}
710 		printf("\n");
711 		if (spec->queue_depth != 0) {
712 			printf("LU%d: LUN%d command queuing enabled, depth %d\n",
713 			    lu->num, i, spec->queue_depth);
714 		} else {
715 			printf("LU%d: LUN%d command queuing disabled\n",
716 			    lu->num, i);
717 		}
718 #if 0
719 		if (spec->write_cache && spec->wbufsize) {
720 			mb_size = (spec->wbufsize / ISTGT_LU_1MB);
721 			printf("LU%d: LUN%d write buffer %"PRIu64"MB\n",
722 			    lu->num, i, mb_size);
723 		}
724 #endif
725 
726 		lu->lun[i].spec = spec;
727 	}
728 
729 	return 0;
730 }
731 
732 int
istgt_lu_disk_shutdown(ISTGT_Ptr istgt,ISTGT_LU_Ptr lu)733 istgt_lu_disk_shutdown(ISTGT_Ptr istgt __attribute__((__unused__)), ISTGT_LU_Ptr lu)
734 {
735 	ISTGT_LU_DISK *spec;
736 	ISTGT_LU_PR_KEY *prkey;
737 	int rc;
738 	int i, j;
739 
740 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_disk_shutdown\n");
741 
742 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n",
743 	    lu->num, lu->name);
744 	for (i = 0; i < lu->maxlun; i++) {
745 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
746 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n",
747 			    lu->num, i);
748 			continue;
749 		}
750 		if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) {
751 			ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num);
752 			return -1;
753 		}
754 		spec = (ISTGT_LU_DISK *) lu->lun[i].spec;
755 
756 		if (strcasecmp(spec->disktype, "VDI") == 0
757 		    || strcasecmp(spec->disktype, "VHD") == 0
758 		    || strcasecmp(spec->disktype, "VMDK") == 0
759 		    || strcasecmp(spec->disktype, "QCOW") == 0
760 		    || strcasecmp(spec->disktype, "QED") == 0
761 		    || strcasecmp(spec->disktype, "VHDX") == 0) {
762 			rc = istgt_lu_disk_vbox_lun_shutdown(spec, istgt, lu);
763 			if (rc < 0) {
764 				ISTGT_ERRLOG("LU%d: lu_disk_vbox_lun_shutdown() failed\n",
765 				    lu->num);
766 				/* ignore error */
767 			}
768 		} else if (strcasecmp(spec->disktype, "RAW") == 0) {
769 			if (!spec->lu->readonly) {
770 				rc = spec->sync(spec, 0, spec->size);
771 				if (rc < 0) {
772 					//ISTGT_ERRLOG("LU%d: lu_disk_sync() failed\n", lu->num);
773 					/* ignore error */
774 				}
775 			}
776 			rc = spec->close(spec);
777 			if (rc < 0) {
778 				//ISTGT_ERRLOG("LU%d: lu_disk_close() failed\n", lu->num);
779 				/* ignore error */
780 			}
781 		} else {
782 			ISTGT_ERRLOG("LU%d: LUN%d: unsupported format\n", lu->num, i);
783 			return -1;
784 		}
785 
786 		for (j = 0; j < spec->npr_keys; j++) {
787 			prkey = &spec->pr_keys[j];
788 			istgt_lu_disk_free_pr_key(prkey);
789 		}
790 		if (spec->rsv_key != 0) {
791 			xfree(spec->rsv_port);
792 			spec->rsv_port = NULL;
793 		}
794 
795 		rc = pthread_mutex_destroy(&spec->ats_mutex);
796 		if (rc != 0) {
797 			//ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num);
798 			/* ignore error */
799 		}
800 
801 		istgt_queue_destroy(&spec->cmd_queue);
802 		rc = pthread_mutex_destroy(&spec->cmd_queue_mutex);
803 		if (rc != 0) {
804 			//ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num);
805 			/* ignore error */
806 		}
807 		rc = pthread_mutex_destroy(&spec->wait_lu_task_mutex);
808 		if (rc != 0) {
809 			//ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num);
810 			/* ignore error */
811 		}
812 		xfree(spec->watsbuf);
813 		xfree(spec->wbuf);
814 		xfree(spec);
815 		lu->lun[i].spec = NULL;
816 	}
817 
818 	return 0;
819 }
820 
821 void
istgt_scsi_dump_cdb(uint8_t * cdb)822 istgt_scsi_dump_cdb(uint8_t *cdb)
823 {
824 	int group;
825 	int cdblen = 0;
826 	int i;
827 
828 	if (cdb == NULL)
829 		return;
830 
831 	group = (cdb[0] >> 5) & 0x07;
832 	switch (group) {
833 	case 0x00:
834 		/* 6byte commands */
835 		cdblen = 6;
836 		break;
837 	case 0x01:
838 		/* 10byte commands */
839 		cdblen = 10;
840 		break;
841 	case 0x02:
842 		/* 10byte commands */
843 		cdblen = 10;
844 		break;
845 	case 0x03:
846 		/* reserved */
847 		if (cdb[0] == 0x7f) {
848 			/* variable length */
849 			cdblen = 8 + (cdb[7] & 0xff);
850 		} else {
851 			/* XXX */
852 			cdblen = 6;
853 		}
854 		break;
855 	case 0x04:
856 		/* 16byte commands */
857 		cdblen = 16;
858 		break;
859 	case 0x05:
860 		/* 12byte commands */
861 		cdblen = 12;
862 		break;
863 	case 0x06:
864 	case 0x07:
865 		/* vendor specific */
866 		cdblen = 6;
867 		break;
868 	}
869 
870 	printf("CDB=");
871 	for (i = 0; i < cdblen; i++) {
872 		printf("%2.2x ", cdb[i]);
873 	}
874 	printf("\n");
875 }
876 
877 void
istgt_strcpy_pad(uint8_t * dst,size_t size,const char * src,int pad)878 istgt_strcpy_pad(uint8_t *dst, size_t size, const char *src, int pad)
879 {
880 	size_t len;
881 
882 	len = strlen(src);
883 	if (len < size) {
884 		memcpy(dst, src, len);
885 		memset(dst + len, pad, (size - len));
886 	} else {
887 		memcpy(dst, src, size);
888 	}
889 }
890 
891 #ifdef HAVE_UUID_H
892 uint64_t
istgt_uuid2uint64(uuid_t * uuid)893 istgt_uuid2uint64(uuid_t *uuid)
894 {
895 	uint64_t low, mid, hi;
896 	uint64_t r;
897 
898 	low = (uint64_t) uuid->time_low;
899 	mid = (uint64_t) uuid->time_mid;
900 	hi  = (uint64_t) uuid->time_hi_and_version;
901 	r = (hi & 0xffffULL) << 48;
902 	r |= (mid & 0xffffULL) << 32;
903 	r |= (low & 0xffffffffULL);
904 	return r;
905 }
906 #endif /* HAVE_UUID_H */
907 
908 uint64_t
istgt_get_lui(const char * name,int lun)909 istgt_get_lui(const char *name, int lun)
910 {
911 	char buf[MAX_TMPBUF];
912 	uint32_t crc32c;
913 	uint64_t r;
914 
915 	if (lun >= 0) {
916 		snprintf(buf, sizeof buf, "%s,%d",
917 		    name, lun);
918 	} else {
919 		snprintf(buf, sizeof buf, "%s",
920 		    name);
921 	}
922 	crc32c = istgt_crc32c((uint8_t *) buf, strlen(buf));
923 	r = (uint64_t) crc32c;
924 	return r;
925 }
926 
927 uint64_t
istgt_get_rkey(const char * initiator_name,uint64_t lui)928 istgt_get_rkey(const char *initiator_name, uint64_t lui)
929 {
930 	ISTGT_MD5CTX md5ctx;
931 	uint8_t rkeymd5[ISTGT_MD5DIGEST_LEN];
932 	char buf[MAX_TMPBUF];
933 	uint64_t rkey;
934 	int idx;
935 	int i;
936 
937 	snprintf(buf, sizeof buf, "%s,%16.16" PRIx64,
938 	    initiator_name, lui);
939 
940 	istgt_md5init(&md5ctx);
941 	istgt_md5update(&md5ctx, buf, strlen(buf));
942 	istgt_md5final(rkeymd5, &md5ctx);
943 
944 	rkey = 0U;
945 	idx = ISTGT_MD5DIGEST_LEN - 8;
946 	if (idx < 0) {
947 		ISTGT_WARNLOG("missing MD5 length\n");
948 		idx = 0;
949 	}
950 	for (i = idx; i < ISTGT_MD5DIGEST_LEN; i++) {
951 		rkey |= (uint64_t) rkeymd5[i];
952 		rkey = rkey << 8;
953 	}
954 	return rkey;
955 }
956 
957 /* XXX */
958 #define COMPANY_ID 0xACDE48U // 24bits
959 
960 int
istgt_lu_set_lid(uint8_t * buf,uint64_t vid)961 istgt_lu_set_lid(uint8_t *buf, uint64_t vid)
962 {
963 	uint64_t naa;
964 	uint64_t enc;
965 	int total;
966 
967 	naa = 0x3; // Locally Assigned
968 
969 	/* NAA + LOCALLY ADMINISTERED VALUE */
970 	enc = (naa & 0xfULL) << (64-4); // 4bits
971 	enc |= vid & 0xfffffffffffffffULL; //60bits
972 	DSET64(&buf[0], enc);
973 
974 	total = 8;
975 	return total;
976 }
977 
978 int
istgt_lu_set_id(uint8_t * buf,uint64_t vid)979 istgt_lu_set_id(uint8_t *buf, uint64_t vid)
980 {
981 	uint64_t naa;
982 	uint64_t cid;
983 	uint64_t enc;
984 	int total;
985 
986 	naa = 0x5; // IEEE Registered
987 	cid = COMPANY_ID; //IEEE COMPANY_ID
988 
989 	/* NAA + COMPANY_ID + VENDOR SPECIFIC IDENTIFIER */
990 	enc = (naa & 0xfULL) << (64-4); // 4bits
991 	enc |= (cid & 0xffffffULL) << (64-4-24); // 24bits
992 	enc |= vid & 0xfffffffffULL; //36bits
993 	DSET64(&buf[0], enc);
994 
995 	total = 8;
996 	return total;
997 }
998 
999 int
istgt_lu_set_extid(uint8_t * buf,uint64_t vid,uint64_t vide)1000 istgt_lu_set_extid(uint8_t *buf, uint64_t vid, uint64_t vide)
1001 {
1002 	uint64_t naa;
1003 	uint64_t cid;
1004 	uint64_t enc;
1005 	int total;
1006 
1007 	naa = 0x6; // IEEE Registered Extended
1008 	cid = COMPANY_ID; //IEEE COMPANY_ID
1009 
1010 	/* NAA + COMPANY_ID + VENDOR SPECIFIC IDENTIFIER */
1011 	enc = (naa & 0xfULL) << (64-4); // 4bits
1012 	enc |= (cid & 0xffffffULL) << (64-4-24); // 24bits
1013 	enc |= vid & 0xfffffffffULL; //36bits
1014 	DSET64(&buf[0], enc);
1015 	/* VENDOR SPECIFIC IDENTIFIER EXTENSION */
1016 	DSET64(&buf[8], vide);
1017 
1018 	total = 16;
1019 	return total;
1020 }
1021 
1022 static int
istgt_lu_disk_scsi_report_luns(ISTGT_LU_Ptr lu,CONN_Ptr conn,uint8_t * cdb,int sel,uint8_t * data,int alloc_len)1023 istgt_lu_disk_scsi_report_luns(ISTGT_LU_Ptr lu, CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int sel, uint8_t *data, int alloc_len)
1024 {
1025 	uint64_t fmt_lun, lun, method;
1026 	int hlen = 0, len = 0;
1027 	int i;
1028 
1029 	if (alloc_len < 8) {
1030 		return -1;
1031 	}
1032 
1033 	if (sel == 0x00) {
1034 		/* logical unit with addressing method */
1035 	} else if (sel == 0x01) {
1036 		/* well known logical unit */
1037 	} else if (sel == 0x02) {
1038 		/* logical unit */
1039 	} else {
1040 		return -1;
1041 	}
1042 
1043 	/* LUN LIST LENGTH */
1044 	DSET32(&data[0], 0);
1045 	/* Reserved */
1046 	DSET32(&data[4], 0);
1047 	hlen = 8;
1048 
1049 	for (i = 0; i < lu->maxlun; i++) {
1050 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
1051 #if 0
1052 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n",
1053 			    lu->num, i);
1054 #endif
1055 			continue;
1056 		}
1057 		if (alloc_len - (hlen + len) < 8) {
1058 			return -1;
1059 		}
1060 		lun = (uint64_t) i;
1061 		if (lu->maxlun <= 0x0100) {
1062 			/* below 256 */
1063 			method = 0x00U;
1064 			fmt_lun = (method & 0x03U) << 62;
1065 			fmt_lun |= (lun & 0x00ffU) << 48;
1066 		} else if (lu->maxlun <= 0x4000) {
1067 			/* below 16384 */
1068 			method = 0x01U;
1069 			fmt_lun = (method & 0x03U) << 62;
1070 			fmt_lun |= (lun & 0x3fffU) << 48;
1071 		} else {
1072 			/* XXX */
1073 			fmt_lun = 0;
1074 		}
1075 		/* LUN */
1076 		DSET64(&data[hlen + len], fmt_lun);
1077 		len += 8;
1078 	}
1079 	/* LUN LIST LENGTH */
1080 	DSET32(&data[0], len);
1081 	return hlen + len;
1082 }
1083 
1084 static int
istgt_lu_disk_scsi_inquiry(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,uint8_t * data,int alloc_len)1085 istgt_lu_disk_scsi_inquiry(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int alloc_len)
1086 {
1087 	uint64_t LUI;
1088 	uint8_t *cp, *cp2;
1089 	uint32_t blocks;
1090 	int hlen = 0, len = 0, plen, plen2;
1091 	int pc;
1092 	int pq, pd;
1093 	int rmb;
1094 	int evpd;
1095 	int pg_tag;
1096 	int i, j;
1097 
1098 	if (alloc_len < 0xff) {
1099 		return -1;
1100 	}
1101 
1102 	pq = 0x00;
1103 	pd = SPC_PERIPHERAL_DEVICE_TYPE_DISK;
1104 	rmb = 0;
1105 
1106 #if 0
1107 	LUI = istgt_uuid2uint64(&spec->uuid);
1108 #else
1109 	LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU);
1110 #endif
1111 
1112 	pc = cdb[2];
1113 	evpd = BGET8(&cdb[1], 0);
1114 	if (evpd) {
1115 		/* Vital product data */
1116 		switch (pc) {
1117 		case SPC_VPD_SUPPORTED_VPD_PAGES:
1118 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1119 			BDSET8W(&data[0], pq, 7, 3);
1120 			BDADD8W(&data[0], pd, 4, 5);
1121 			/* PAGE CODE */
1122 			data[1] = pc;
1123 			/* Reserved */
1124 			data[2] = 0;
1125 			/* PAGE LENGTH */
1126 			data[3] = 0;
1127 			hlen = 4;
1128 
1129 			data[4] = SPC_VPD_SUPPORTED_VPD_PAGES;      /* 0x00 */
1130 			data[5] = SPC_VPD_UNIT_SERIAL_NUMBER;       /* 0x80 */
1131 			data[6] = SPC_VPD_DEVICE_IDENTIFICATION;    /* 0x83 */
1132 			data[7] = SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES; /* 0x85 */
1133 			data[8] = SPC_VPD_EXTENDED_INQUIRY_DATA;    /* 0x86 */
1134 			data[9] = SPC_VPD_MODE_PAGE_POLICY;         /* 0x87 */
1135 			data[10]= SPC_VPD_SCSI_PORTS;               /* 0x88 */
1136 			data[11]= 0xb0; /* SBC Block Limits */
1137 			data[12]= 0xb1; /* SBC Block Device Characteristics */
1138 			len = 13 - hlen;
1139 			if (spec->thin_provisioning) {
1140 				data[13]= 0xb2; /* SBC Thin Provisioning */
1141 				len++;
1142 			}
1143 
1144 			/* PAGE LENGTH */
1145 			data[3] = len;
1146 			break;
1147 
1148 		case SPC_VPD_UNIT_SERIAL_NUMBER:
1149 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1150 			BDSET8W(&data[0], pq, 7, 3);
1151 			BDADD8W(&data[0], pd, 4, 5);
1152 			/* PAGE CODE */
1153 			data[1] = pc;
1154 			/* Reserved */
1155 			data[2] = 0;
1156 			/* PAGE LENGTH */
1157 			data[3] = 0;
1158 			hlen = 4;
1159 
1160 			/* PRODUCT SERIAL NUMBER */
1161 			if (spec->lu->lun[spec->lun].serial != NULL) {
1162 				len = strlen(spec->lu->lun[spec->lun].serial);
1163 				if (len > MAX_LU_SERIAL_STRING) {
1164 					len = MAX_LU_SERIAL_STRING;
1165 				}
1166 				istgt_strcpy_pad(&data[4], len,
1167 				    spec->lu->lun[spec->lun].serial, ' ');
1168 			} else {
1169 				len = strlen(spec->lu->inq_serial);
1170 				if (len > MAX_LU_SERIAL_STRING) {
1171 					len = MAX_LU_SERIAL_STRING;
1172 				}
1173 				istgt_strcpy_pad(&data[4], len,
1174 				    spec->lu->inq_serial, ' ');
1175 			}
1176 
1177 			/* PAGE LENGTH */
1178 			data[3] = len;
1179 			break;
1180 
1181 		case SPC_VPD_DEVICE_IDENTIFICATION:
1182 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1183 			BDSET8W(&data[0], pq, 7, 3);
1184 			BDADD8W(&data[0], pd, 4, 5);
1185 			/* PAGE CODE */
1186 			data[1] = pc;
1187 			/* PAGE LENGTH */
1188 			DSET16(&data[2], 0);
1189 			hlen = 4;
1190 
1191 			/* Identification descriptor 1 */
1192 			/* Logical Unit */
1193 			cp = &data[hlen + len];
1194 
1195 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1196 			BDSET8W(&cp[0], 0, 7, 4);
1197 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
1198 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1199 			BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */
1200 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2);
1201 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_NAA, 3, 4);
1202 			/* Reserved */
1203 			cp[2] = 0;
1204 			/* IDENTIFIER LENGTH */
1205 			cp[3] = 0;
1206 
1207 			/* IDENTIFIER */
1208 #if 0
1209 			/* 16bytes ID */
1210 			plen = istgt_lu_set_extid(&cp[4], 0, LUI);
1211 #else
1212 			plen = istgt_lu_set_lid(&cp[4], LUI);
1213 #endif
1214 
1215 			cp[3] = plen;
1216 			len += 4 + plen;
1217 
1218 			/* Identification descriptor 2 */
1219 			/* T10 VENDOR IDENTIFICATION */
1220 			cp = &data[hlen + len];
1221 
1222 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1223 			BDSET8W(&cp[0], 0, 7, 4);
1224 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
1225 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1226 			BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */
1227 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2);
1228 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID, 3, 4);
1229 			/* Reserved */
1230 			cp[2] = 0;
1231 			/* IDENTIFIER LENGTH */
1232 			cp[3] = 0;
1233 
1234 			/* IDENTIFIER */
1235 			/* T10 VENDOR IDENTIFICATION */
1236 			istgt_strcpy_pad(&cp[4], 8, spec->lu->inq_vendor, ' ');
1237 			plen = 8;
1238 			/* VENDOR SPECIFIC IDENTIFIER */
1239 			/* PRODUCT IDENTIFICATION */
1240 			istgt_strcpy_pad(&cp[12], 16, spec->lu->inq_product, ' ');
1241 			/* PRODUCT SERIAL NUMBER */
1242 			if (spec->lu->lun[spec->lun].serial != NULL) {
1243 				istgt_strcpy_pad(&cp[28], MAX_LU_SERIAL_STRING,
1244 				    spec->lu->lun[spec->lun].serial, ' ');
1245 			} else {
1246 				istgt_strcpy_pad(&cp[28], MAX_LU_SERIAL_STRING,
1247 				    spec->lu->inq_serial, ' ');
1248 			}
1249 			plen += 16 + MAX_LU_SERIAL_STRING;
1250 
1251 			cp[3] = plen;
1252 			len += 4 + plen;
1253 
1254 			/* Identification descriptor 3 */
1255 			/* Target Device */
1256 			cp = &data[hlen + len];
1257 
1258 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1259 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
1260 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
1261 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1262 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
1263 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_DEVICE, 5, 2);
1264 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4);
1265 			/* Reserved */
1266 			cp[2] = 0;
1267 			/* IDENTIFIER LENGTH */
1268 			cp[3] = 0;
1269 
1270 			/* IDENTIFIER */
1271 			plen = snprintf((char *) &cp[4], MAX_TARGET_NAME,
1272 			    "%s", spec->lu->name);
1273 			cp[3] = plen;
1274 			len += 4 + plen;
1275 
1276 			/* Identification descriptor 4 */
1277 			/* Target Port */
1278 			cp = &data[hlen + len];
1279 
1280 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1281 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
1282 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
1283 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1284 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
1285 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
1286 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4);
1287 			/* Reserved */
1288 			cp[2] = 0;
1289 			/* IDENTIFIER LENGTH */
1290 			cp[3] = 0;
1291 
1292 			/* IDENTIFIER */
1293 			plen = snprintf((char *) &cp[4], MAX_TARGET_NAME,
1294 			    "%s"",t,0x""%4.4x", spec->lu->name, conn->portal.tag);
1295 			cp[3] = plen;
1296 			len += 4 + plen;
1297 
1298 			/* Identification descriptor 5 */
1299 			/* Relative Target Port */
1300 			cp = &data[hlen + len];
1301 
1302 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1303 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
1304 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
1305 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1306 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
1307 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
1308 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT,
1309 			    3, 4);
1310 			/* Reserved */
1311 			cp[2] = 0;
1312 			/* IDENTIFIER LENGTH */
1313 			cp[3] = 0;
1314 
1315 			/* IDENTIFIER */
1316 			/* Obsolete */
1317 			DSET16(&cp[4], 0);
1318 			/* Relative Target Port Identifier */
1319 			//DSET16(&cp[6], 1); /* port1 as port A */
1320 			//DSET16(&cp[6], 2); /* port2 as port B */
1321 			DSET16(&cp[6], (uint16_t) (1 + conn->portal.idx));
1322 			plen = 4;
1323 
1324 			cp[3] = plen;
1325 			len += 4 + plen;
1326 
1327 			/* Identification descriptor 6 */
1328 			/* Target port group */
1329 			cp = &data[hlen + len];
1330 
1331 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1332 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
1333 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
1334 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1335 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
1336 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
1337 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP,
1338 			    3, 4);
1339 			/* Reserved */
1340 			cp[2] = 0;
1341 			/* IDENTIFIER LENGTH */
1342 			cp[3] = 0;
1343 
1344 			/* IDENTIFIER */
1345 			/* Reserved */
1346 			DSET16(&cp[4], 0);
1347 			/* TARGET PORT GROUP */
1348 			DSET16(&cp[6], (uint16_t) (conn->portal.tag));
1349 			plen = 4;
1350 
1351 			cp[3] = plen;
1352 			len += 4 + plen;
1353 
1354 			/* Identification descriptor 7 */
1355 			/* Logical unit group */
1356 			cp = &data[hlen + len];
1357 
1358 			/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1359 			BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
1360 			BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4);
1361 			/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1362 			BDSET8W(&cp[1], 1, 7, 1); /* PIV */
1363 			BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
1364 			BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP,
1365 			    3, 4);
1366 			/* Reserved */
1367 			cp[2] = 0;
1368 			/* IDENTIFIER LENGTH */
1369 			cp[3] = 0;
1370 
1371 			/* IDENTIFIER */
1372 			/* Reserved */
1373 			DSET16(&cp[4], 0);
1374 			/* LOGICAL UNIT GROUP */
1375 			DSET16(&cp[6], (uint16_t) (spec->lu->num));
1376 			plen = 4;
1377 
1378 			cp[3] = plen;
1379 			len += 4 + plen;
1380 
1381 			/* PAGE LENGTH */
1382 			if (len > 0xffff) {
1383 				len = 0xffff;
1384 			}
1385 			DSET16(&data[2], len);
1386 			break;
1387 
1388 		case SPC_VPD_EXTENDED_INQUIRY_DATA:
1389 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1390 			BDSET8W(&data[0], pq, 7, 3);
1391 			BDADD8W(&data[0], pd, 4, 5);
1392 			/* PAGE CODE */
1393 			data[1] = pc;
1394 			/* Reserved */
1395 			data[2] = 0;
1396 			/* PAGE LENGTH */
1397 			data[3] = 0;
1398 			hlen = 4;
1399 
1400 			/* RTO(3) GRD_CHK(2) APP_CHK(1) REF_CHK(0) */
1401 			data[4] = 0;
1402 			/* GROUP_SUP(4) PRIOR_SUP(3) HEADSUP(2) ORDSUP(1) SIMPSUP(0) */
1403 			data[5] = 0;
1404 			if (spec->queue_depth != 0) {
1405 				BDADD8(&data[5], 1, 2);     /* HEADSUP */
1406 				//BDADD8(&data[5], 1, 1);     /* ORDSUP */
1407 				BDADD8(&data[5], 1, 0);     /* SIMPSUP */
1408 			}
1409 			/* NV_SUP(1) V_SUP(0) */
1410 			data[6] = 0;
1411 			/* Reserved[7-63] */
1412 			memset(&data[7], 0, (64 - 7));
1413 			len = 64 - hlen;
1414 
1415 			/* PAGE LENGTH */
1416 			data[3] = len;
1417 			break;
1418 
1419 		case SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES:
1420 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1421 			BDSET8W(&data[0], pq, 7, 3);
1422 			BDADD8W(&data[0], pd, 4, 5);
1423 			/* PAGE CODE */
1424 			data[1] = pc;
1425 			/* PAGE LENGTH */
1426 			DSET16(&data[2], 0);
1427 			hlen = 4;
1428 
1429 #if 0
1430 			/* Network services descriptor N */
1431 			cp = &data[hlen + len];
1432 
1433 			/* ASSOCIATION(6-5) SERVICE TYPE(4-0) */
1434 			BDSET8W(&cp[0], 0x00, 6, 2);
1435 			BDADD8W(&cp[0], 0x00, 4, 5);
1436 			/* Reserved */
1437 			cp[1] = 0;
1438 			/* NETWORK ADDRESS LENGTH */
1439 			DSET16(&cp[2], 0);
1440 			/* NETWORK ADDRESS */
1441 			cp[4] = 0;
1442 			/* ... */
1443 			plen = 0;
1444 			DSET16(&cp[2], plen);
1445 			len += 4 + plen;
1446 #endif
1447 
1448 			/* PAGE LENGTH */
1449 			if (len > 0xffff) {
1450 				len = 0xffff;
1451 			}
1452 			DSET16(&data[2], len);
1453 			break;
1454 
1455 		case SPC_VPD_MODE_PAGE_POLICY:
1456 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1457 			BDSET8W(&data[0], pq, 7, 3);
1458 			BDADD8W(&data[0], pd, 4, 5);
1459 			/* PAGE CODE */
1460 			data[1] = pc;
1461 			/* PAGE LENGTH */
1462 			DSET16(&data[2], 0);
1463 			hlen = 4;
1464 
1465 			/* Mode page policy descriptor 1 */
1466 			cp = &data[hlen + len];
1467 
1468 			/* POLICY PAGE CODE(5-0) */
1469 			BDSET8W(&cp[0], 0x3f, 5, 6);    /* all page code */
1470 			/* POLICY SUBPAGE CODE */
1471 			cp[1] = 0xff;                   /* all sub page */
1472 			/* MLUS(7) MODE PAGE POLICY(1-0) */
1473 			//BDSET8(&cp[2], 1, 7); /* multiple logical units share */
1474 			BDSET8(&cp[2], 0, 7); /* own copy */
1475 			BDADD8W(&cp[2], 0x00, 1, 2); /* Shared */
1476 			//BDADD8W(&cp[2], 0x01, 1, 2); /* Per target port */
1477 			//BDADD8W(&cp[2], 0x02, 1, 2); /* Per initiator port */
1478 			//BDADD8W(&cp[2], 0x03, 1, 2); /* Per I_T nexus */
1479 			/* Reserved */
1480 			cp[3] = 0;
1481 			len += 4;
1482 
1483 			/* PAGE LENGTH */
1484 			if (len > 0xffff) {
1485 				len = 0xffff;
1486 			}
1487 			DSET16(&data[2], len);
1488 			break;
1489 
1490 		case SPC_VPD_SCSI_PORTS:
1491 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1492 			BDSET8W(&data[0], pq, 7, 3);
1493 			BDADD8W(&data[0], pd, 4, 5);
1494 			/* PAGE CODE */
1495 			data[1] = pc;
1496 			/* PAGE LENGTH */
1497 			DSET16(&data[2], 0);
1498 			hlen = 4;
1499 
1500 			/* Identification descriptor list */
1501 			for (i = 0; i < spec->lu->maxmap; i++) {
1502 				pg_tag = spec->lu->map[i].pg_tag;
1503 				/* skip same pg_tag */
1504 				for (j = 0; j < i; j++) {
1505 					if (spec->lu->map[j].pg_tag == pg_tag) {
1506 						goto skip_pg_tag;
1507 					}
1508 				}
1509 
1510 				/* Identification descriptor N */
1511 				cp = &data[hlen + len];
1512 
1513 				/* Reserved */
1514 				DSET16(&cp[0], 0);
1515 				/* RELATIVE PORT IDENTIFIER */
1516 				DSET16(&cp[2], (uint16_t) (1 + pg_tag));
1517 				/* Reserved */
1518 				DSET16(&cp[4], 0);
1519 				/* INITIATOR PORT TRANSPORTID LENGTH */
1520 				DSET16(&cp[6], 0);
1521 				/* Reserved */
1522 				DSET16(&cp[8], 0);
1523 				/* TARGET PORT DESCRIPTORS LENGTH */
1524 				DSET16(&cp[10], 0);
1525 				len += 12;
1526 
1527 				plen2 = 0;
1528 				/* Target port descriptor 1 */
1529 				cp2 = &data[hlen + len + plen2];
1530 
1531 				/* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */
1532 				BDSET8W(&cp2[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4);
1533 				BDADD8W(&cp2[0], SPC_VPD_CODE_SET_UTF8, 3, 4);
1534 				/* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */
1535 				BDSET8W(&cp2[1], 1, 7, 1); /* PIV */
1536 				BDADD8W(&cp2[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2);
1537 				BDADD8W(&cp2[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4);
1538 				/* Reserved */
1539 				cp2[2] = 0;
1540 				/* IDENTIFIER LENGTH */
1541 				cp2[3] = 0;
1542 
1543 				/* IDENTIFIER */
1544 				plen = snprintf((char *) &cp2[4], MAX_TARGET_NAME,
1545 				    "%s"",t,0x""%4.4x", spec->lu->name, pg_tag);
1546 				cp2[3] = plen;
1547 				plen2 += 4 + plen;
1548 
1549 				/* TARGET PORT DESCRIPTORS LENGTH */
1550 				DSET16(&cp[10], plen2);
1551 				len += plen2;
1552 			skip_pg_tag:
1553 				;
1554 			}
1555 
1556 			/* PAGE LENGTH */
1557 			if (len > 0xffff) {
1558 				len = 0xffff;
1559 			}
1560 			DSET16(&data[2], len);
1561 			break;
1562 
1563 		case 0xb0: /* SBC Block Limits */
1564 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1565 			BDSET8W(&data[0], pq, 7, 3);
1566 			BDADD8W(&data[0], pd, 4, 5);
1567 			/* PAGE CODE */
1568 			data[1] = pc;
1569 			/* PAGE LENGTH */
1570 			DSET16(&data[2], 0);
1571 			hlen = 4;
1572 
1573 			/* WSNZ(0) */
1574 			BDSET8(&data[4], 0, 0); /* support zero length in WRITE SAME */
1575 			/* MAXIMUM COMPARE AND WRITE LENGTH */
1576 			blocks = ISTGT_LU_WORK_ATS_BLOCK_SIZE / (uint32_t) spec->blocklen;
1577 			if (blocks > 0xff) {
1578 				blocks = 0xff;
1579 			}
1580 			data[5] = (uint8_t) blocks;
1581 			if (spec->lu->istgt->swmode == ISTGT_SWMODE_TRADITIONAL) {
1582 				/* no support compare and write */
1583 				data[5] = 0;
1584 			}
1585 
1586 			/* force align to 4KB */
1587 			if (spec->blocklen < 4096) {
1588 				blocks = 4096 / (uint32_t) spec->blocklen;
1589 				/* OPTIMAL TRANSFER LENGTH GRANULARITY */
1590 				DSET16(&data[6], blocks);
1591 				/* MAXIMUM TRANSFER LENGTH */
1592 				DSET32(&data[8], 0); /* no limit */
1593 				/* OPTIMAL TRANSFER LENGTH */
1594 				blocks = ISTGT_LU_WORK_BLOCK_SIZE / (uint32_t) spec->blocklen;
1595 				DSET32(&data[12], blocks);
1596 				/* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */
1597 				DSET32(&data[16], 0);
1598 			} else {
1599 				blocks = 1;
1600 				/* OPTIMAL TRANSFER LENGTH GRANULARITY */
1601 				DSET16(&data[6], blocks);
1602 				/* MAXIMUM TRANSFER LENGTH */
1603 				DSET32(&data[8], 0); /* no limit */
1604 				/* OPTIMAL TRANSFER LENGTH */
1605 				blocks = ISTGT_LU_WORK_BLOCK_SIZE / (uint32_t) spec->blocklen;
1606 				DSET32(&data[12], blocks);
1607 				/* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */
1608 				DSET32(&data[16], 0);
1609 			}
1610 			len = 20 - hlen;
1611 
1612 			if (1 || spec->thin_provisioning) {
1613 				/* MAXIMUM UNMAP LBA COUNT */
1614 				DSET32(&data[20], 0); /* not implement UNMAP */
1615 				/* MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT */
1616 				DSET32(&data[24], 0); /* not implement UNMAP */
1617 				/* OPTIMAL UNMAP GRANULARITY */
1618 				DSET32(&data[28], 0); /* not specified */
1619 				/* UNMAP GRANULARITY ALIGNMENT */
1620 				DSET32(&data[32], (0 & 0x7fffffffU));
1621 				/* UGAVALID(7) */
1622 				BDADD8(&data[32], 0, 7); /* not valid ALIGNMENT */
1623 				/* MAXIMUM WRITE SAME LENGTH */
1624 				DSET64(&data[36], 0); /* no limit */
1625 				/* Reserved */
1626 				memset(&data[44], 0x00, 64-44);
1627 				len = 64 - hlen;
1628 			}
1629 
1630 			DSET16(&data[2], len);
1631 			break;
1632 
1633 		case 0xb1: /* SBC Block Device Characteristics */
1634 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1635 			BDSET8W(&data[0], pq, 7, 3);
1636 			BDADD8W(&data[0], pd, 4, 5);
1637 			/* PAGE CODE */
1638 			data[1] = pc;
1639 			/* PAGE LENGTH */
1640 			DSET16(&data[2], 0);
1641 			hlen = 4;
1642 
1643 			/* MEDIUM ROTATION RATE */
1644 			//DSET16(&data[4], 0x0000); /* not reported */
1645 			//DSET16(&data[4], 0x0001); /* Non-rotating medium (solid state) */
1646 			//DSET16(&data[4], 5400); /* rotation rate (5400rpm) */
1647 			//DSET16(&data[4], 7200); /* rotation rate (7200rpm) */
1648 			//DSET16(&data[4], 10000); /* rotation rate (10000rpm) */
1649 			//DSET16(&data[4], 15000); /* rotation rate (15000rpm) */
1650 			DSET16(&data[4], spec->lu->lun[spec->lun].rotationrate);
1651 			/* Reserved */
1652 			data[6] = 0;
1653 			/* NOMINAL FORM FACTOR(3-0) */
1654 			//BDSET8W(&data[7], 0x00, 3, 4); /* not reported */
1655 			//BDSET8W(&data[7], 0x01, 3, 4); /* 5.25 inch */
1656 			//BDSET8W(&data[7], 0x02, 3, 4); /* 3.5 inch */
1657 			//BDSET8W(&data[7], 0x03, 3, 4); /* 2.5 inch */
1658 			//BDSET8W(&data[7], 0x04, 3, 4); /* 1.8 inch */
1659 			//BDSET8W(&data[7], 0x05, 3, 4); /* less 1.8 inch */
1660 			BDSET8W(&data[7], spec->lu->lun[spec->lun].formfactor, 3, 4);
1661 			/* Reserved */
1662 			memset(&data[8], 0x00, 64-8);
1663 
1664 			len = 64 - hlen;
1665 			DSET16(&data[2], len);
1666 			break;
1667 
1668 		case 0xb2: /* SBC Thin Provisioning */
1669 			if (!spec->thin_provisioning) {
1670 				ISTGT_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc);
1671 				return -1;
1672 			}
1673 
1674 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1675 			BDSET8W(&data[0], pq, 7, 3);
1676 			BDADD8W(&data[0], pd, 4, 5);
1677 			/* PAGE CODE */
1678 			data[1] = pc;
1679 			/* PAGE LENGTH */
1680 			DSET16(&data[2], 0);
1681 			hlen = 4;
1682 
1683 			/* THRESHOLD EXPONENT */
1684 			data[4] = 0;
1685 			/* DP(0) */
1686 			BDSET8(&data[5], 0, 0);
1687 			/* Reserved */
1688 			DSET16(&data[6], 0);
1689 			len = 6 - hlen;
1690 #if 0
1691 			/* XXX not yet */
1692 			/* PROVISIONING GROUP DESCRIPTOR ... */
1693 			DSET16(&data[8], 0);
1694 			len = 8 - hlen;
1695 #endif
1696 
1697 			DSET16(&data[2], len);
1698 			break;
1699 
1700 		default:
1701 			if (pc >= 0xc0 && pc <= 0xff) {
1702 				ISTGT_WARNLOG("Vendor specific INQUIRY VPD page 0x%x\n", pc);
1703 			} else {
1704 				ISTGT_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc);
1705 			}
1706 			return -1;
1707 		}
1708 	} else {
1709 		/* Standard INQUIRY data */
1710 		/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
1711 		BDSET8W(&data[0], pq, 7, 3);
1712 		BDADD8W(&data[0], pd, 4, 5);
1713 		/* RMB(7) */
1714 		BDSET8W(&data[1], rmb, 7, 1);
1715 		/* VERSION */
1716 		/* See SPC3/SBC2/MMC4/SAM2 for more details */
1717 		data[2] = SPC_VERSION_SPC3;
1718 		/* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */
1719 		BDSET8W(&data[3], 2, 3, 4);		/* format 2 */
1720 		BDADD8(&data[1], 1, 4);         /* hierarchical support */
1721 		/* ADDITIONAL LENGTH */
1722 		data[4] = 0;
1723 		hlen = 5;
1724 
1725 		/* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */
1726 		data[5] = 0;
1727 		//BDADD8W(&data[5], 1, 7, 1); /* storage array controller */
1728 		BDADD8W(&data[5], 0x00, 5, 2); /* Not support TPGS */
1729 		//BDADD8W(&data[5], 0x01, 5, 2); /* Only implicit */
1730 		//BDADD8W(&data[5], 0x02, 5, 2); /* Only explicit */
1731 		//BDADD8W(&data[5], 0x03, 5, 2); /* Both explicit and implicit */
1732 		/* BQUE(7) ENCSERV(6) VS(5) MULTIP(4) MCHNGR(3) ADDR16(0) */
1733 		data[6] = 0;
1734 		BDADD8W(&data[6], 1, 4, 1); /* MULTIP */
1735 		/* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */
1736 		data[7] = 0;
1737 		if (spec->queue_depth != 0) {
1738 			BDADD8(&data[7], 1, 1);     /* CMDQUE */
1739 		}
1740 		/* T10 VENDOR IDENTIFICATION */
1741 		istgt_strcpy_pad(&data[8], 8, spec->lu->inq_vendor, ' ');
1742 		/* PRODUCT IDENTIFICATION */
1743 		istgt_strcpy_pad(&data[16], 16, spec->lu->inq_product, ' ');
1744 		/* PRODUCT REVISION LEVEL */
1745 		istgt_strcpy_pad(&data[32], 4, spec->lu->inq_revision, ' ');
1746 		/* Vendor specific */
1747 		memset(&data[36], 0x20, 20);
1748 		/* CLOCKING(3-2) QAS(1) IUS(0) */
1749 		data[56] = 0;
1750 		/* Reserved */
1751 		data[57] = 0;
1752 		/* VERSION DESCRIPTOR 1-8 */
1753 		DSET16(&data[58], 0x0960); /* iSCSI (no version claimed) */
1754 		DSET16(&data[60], 0x0300); /* SPC-3 (no version claimed) */
1755 		DSET16(&data[62], 0x0320); /* SBC-2 (no version claimed) */
1756 		DSET16(&data[64], 0x0040); /* SAM-2 (no version claimed) */
1757 		DSET16(&data[66], 0x0000);
1758 		DSET16(&data[68], 0x0000);
1759 		DSET16(&data[70], 0x0000);
1760 		DSET16(&data[72], 0x0000);
1761 		/* Reserved[74-95] */
1762 		memset(&data[74], 0, (96 - 74));
1763 		/* Vendor specific parameters[96-n] */
1764 		//data[96] = 0;
1765 		len = 96 - hlen;
1766 
1767 		/* ADDITIONAL LENGTH */
1768 		data[4] = len;
1769 	}
1770 
1771 	return hlen + len;
1772 }
1773 
1774 #define MODE_SENSE_PAGE_INIT(B,L,P,SP)					\
1775 	do {								\
1776 		memset((B), 0, (L));					\
1777 		if ((SP) != 0x00) {					\
1778 			(B)[0] = (P) | 0x40; /* PAGE + SPF=1 */		\
1779 			(B)[1] = (SP);					\
1780 			DSET16(&(B)[2], (L) - 4);			\
1781 		} else {						\
1782 			(B)[0] = (P);					\
1783 			(B)[1] = (L) - 2;				\
1784 		}							\
1785 	} while (0)
1786 
1787 static int
istgt_lu_disk_scsi_mode_sense_page(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,int pc,int page,int subpage,uint8_t * data,int alloc_len)1788 istgt_lu_disk_scsi_mode_sense_page(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int pc, int page, int subpage, uint8_t *data, int alloc_len)
1789 {
1790 	uint8_t *cp;
1791 	int len = 0;
1792 	int plen;
1793 	int rc;
1794 	int i;
1795 
1796 #if 0
1797 	printf("pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage);
1798 #endif
1799 #if 0
1800 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE: pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage);
1801 #endif
1802 
1803 	if (pc == 0x00) {
1804 		/* Current values */
1805 	} else if (pc == 0x01) {
1806 		/* Changeable values */
1807 		if (page != 0x08) {
1808 			/* not supported */
1809 			return -1;
1810 		}
1811 	} else if (pc == 0x02) {
1812 		/* Default values */
1813 	} else {
1814 		/* Saved values */
1815 	}
1816 
1817 	cp = &data[len];
1818 	switch (page) {
1819 	case 0x00:
1820 		/* Vendor specific */
1821 		break;
1822 	case 0x01:
1823 		/* Read-Write Error Recovery */
1824 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Read-Write Error Recovery\n");
1825 		if (subpage != 0x00)
1826 			break;
1827 		plen = 0x0a + 2;
1828 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1829 		len += plen;
1830 		break;
1831 	case 0x02:
1832 		/* Disconnect-Reconnect */
1833 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Disconnect-Reconnect\n");
1834 		if (subpage != 0x00)
1835 			break;
1836 		plen = 0x0e + 2;
1837 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1838 		len += plen;
1839 		break;
1840 	case 0x03:
1841 		/* Obsolete (Format Device) */
1842 		break;
1843 	case 0x04:
1844 		/* Obsolete (Rigid Disk Geometry) */
1845 		break;
1846 	case 0x05:
1847 		/* Obsolete (Rigid Disk Geometry) */
1848 		break;
1849 	case 0x06:
1850 		/* Reserved */
1851 		break;
1852 	case 0x07:
1853 		/* Verify Error Recovery */
1854 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Verify Error Recovery\n");
1855 		if (subpage != 0x00)
1856 			break;
1857 		plen = 0x0a + 2;
1858 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1859 		len += plen;
1860 		break;
1861 	case 0x08:
1862 		/* Caching */
1863 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Caching\n");
1864 		if (subpage != 0x00)
1865 			break;
1866 
1867 		plen = 0x12 + 2;
1868 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1869 		BDADD8(&cp[0], 1, 7); /* PS */
1870 		if (pc == 0x01) {
1871 			// Changeable values
1872 			BDADD8(&cp[2], 1, 2); /* WCE */
1873 			BDADD8(&cp[2], 1, 0); /* RCD */
1874 			len += plen;
1875 			break;
1876 		}
1877 		BDADD8(&cp[2], 1, 2); /* WCE */
1878 		//BDADD8(&cp[2], 1, 0); /* RCD */
1879 		{
1880 			int fd;
1881 			fd = spec->fd;
1882 			rc = fcntl(fd , F_GETFL, 0);
1883 			if (rc != -1 && !(rc & O_FSYNC)) {
1884 				BDADD8(&cp[2], 1, 2); /* WCE=1 */
1885 			} else {
1886 				BDADD8(&cp[2], 0, 2); /* WCE=0 */
1887 			}
1888 		}
1889 		if (spec->read_cache == 0) {
1890 			BDADD8(&cp[2], 1, 0); /* RCD=1 */
1891 		} else {
1892 			BDADD8(&cp[2], 0, 0); /* RCD=0 */
1893 		}
1894 		len += plen;
1895 		break;
1896 	case 0x09:
1897 		/* Obsolete */
1898 		break;
1899 	case 0x0a:
1900 		switch (subpage) {
1901 		case 0x00:
1902 			/* Control */
1903 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Control\n");
1904 			plen = 0x0a + 2;
1905 			MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1906 			len += plen;
1907 			break;
1908 		case 0x01:
1909 			/* Control Extension */
1910 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Control Extension\n");
1911 			plen = 0x1c + 4;
1912 			MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1913 			len += plen;
1914 			break;
1915 		case 0xff:
1916 			/* All subpages */
1917 			len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, 0x00, &data[len], alloc_len);
1918 			len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, 0x01, &data[len], alloc_len);
1919 			break;
1920 		default:
1921 			/* 0x02-0x3e: Reserved */
1922 			break;
1923 		}
1924 		break;
1925 	case 0x0b:
1926 		/* Obsolete (Medium Types Supported) */
1927 		break;
1928 	case 0x0c:
1929 		/* Obsolete (Notch And Partitio) */
1930 		break;
1931 	case 0x0d:
1932 		/* Obsolete */
1933 		break;
1934 	case 0x0e:
1935 	case 0x0f:
1936 		/* Reserved */
1937 		break;
1938 	case 0x10:
1939 		/* XOR Control */
1940 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE XOR Control\n");
1941 		if (subpage != 0x00)
1942 			break;
1943 		plen = 0x16 + 2;
1944 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1945 		len += plen;
1946 		break;
1947 	case 0x11:
1948 	case 0x12:
1949 	case 0x13:
1950 		/* Reserved */
1951 		break;
1952 	case 0x14:
1953 		/* Enclosure Services Management */
1954 		break;
1955 	case 0x15:
1956 	case 0x16:
1957 	case 0x17:
1958 		/* Reserved */
1959 		break;
1960 	case 0x18:
1961 		/* Protocol-Specific LUN */
1962 #if 0
1963 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Protocol-Specific LUN\n");
1964 		if (subpage != 0x00)
1965 			break;
1966 		plen = 0x04 + 0x00 + 2;
1967 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1968 		len += plen;
1969 #endif
1970 		break;
1971 	case 0x19:
1972 		/* Protocol-Specific Port */
1973 #if 0
1974 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Protocol-Specific Port\n");
1975 		if (subpage != 0x00)
1976 			break;
1977 		plen = 0x04 + 0x00 + 2;
1978 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1979 		len += plen;
1980 #endif
1981 		break;
1982 	case 0x1a:
1983 		/* Power Condition */
1984 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Power Condition\n");
1985 		if (subpage != 0x00)
1986 			break;
1987 		plen = 0x0a + 2;
1988 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
1989 		len += plen;
1990 		break;
1991 	case 0x1b:
1992 		/* Reserved */
1993 		break;
1994 	case 0x1c:
1995 		/* Informational Exceptions Control */
1996 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Informational Exceptions Control\n");
1997 		if (subpage != 0x00)
1998 			break;
1999 
2000 		plen = 0x0a + 2;
2001 		MODE_SENSE_PAGE_INIT(cp, plen, page, subpage);
2002 		len += plen;
2003 		break;
2004 	case 0x1d:
2005 	case 0x1e:
2006 	case 0x1f:
2007 		/* Reserved */
2008 		break;
2009 	case 0x20:
2010 	case 0x21:
2011 	case 0x22:
2012 	case 0x23:
2013 	case 0x24:
2014 	case 0x25:
2015 	case 0x26:
2016 	case 0x27:
2017 	case 0x28:
2018 	case 0x29:
2019 	case 0x2a:
2020 	case 0x2b:
2021 	case 0x2c:
2022 	case 0x2d:
2023 	case 0x2e:
2024 	case 0x2f:
2025 	case 0x30:
2026 	case 0x31:
2027 	case 0x32:
2028 	case 0x33:
2029 	case 0x34:
2030 	case 0x35:
2031 	case 0x36:
2032 	case 0x37:
2033 	case 0x38:
2034 	case 0x39:
2035 	case 0x3a:
2036 	case 0x3b:
2037 	case 0x3c:
2038 	case 0x3d:
2039 	case 0x3e:
2040 		/* Vendor-specific */
2041 		break;
2042 	case 0x3f:
2043 		switch (subpage) {
2044 		case 0x00:
2045 			/* All mode pages */
2046 			for (i = 0x00; i < 0x3e; i ++) {
2047 				len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len);
2048 			}
2049 			break;
2050 		case 0xff:
2051 			/* All mode pages and subpages */
2052 			for (i = 0x00; i < 0x3e; i ++) {
2053 				len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len);
2054 			}
2055 			for (i = 0x00; i < 0x3e; i ++) {
2056 				len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0xff, &cp[len], alloc_len);
2057 			}
2058 			break;
2059 		default:
2060 			/* 0x01-0x3e: Reserved */
2061 			break;
2062 		}
2063 	}
2064 
2065 	return len;
2066 }
2067 
2068 static int
istgt_lu_disk_scsi_mode_sense6(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,int dbd,int pc,int page,int subpage,uint8_t * data,int alloc_len)2069 istgt_lu_disk_scsi_mode_sense6(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int pc, int page, int subpage, uint8_t *data, int alloc_len)
2070 {
2071 	uint8_t *cp;
2072 	int hlen = 0, len = 0, plen;
2073 	int total;
2074 	int llbaa = 0;
2075 
2076 	data[0] = 0;                    /* Mode Data Length */
2077 	data[1] = 0;                    /* Medium Type */
2078 	data[2] = 0;                    /* Device-Specific Parameter */
2079 	if (spec->lu->readonly) {
2080 		BDADD8(&data[2], 1, 7);     /* WP */
2081 	}
2082 	data[3] = 0;                    /* Block Descripter Length */
2083 	hlen = 4;
2084 
2085 	cp = &data[4];
2086 	if (dbd) {                      /* Disable Block Descripters */
2087 		len = 0;
2088 	} else {
2089 		if (llbaa) {
2090 			/* Number of Blocks */
2091 			DSET64(&cp[0], spec->blockcnt);
2092 			/* Reserved */
2093 			DSET32(&cp[8], 0);
2094 			/* Block Length */
2095 			DSET32(&cp[12], (uint32_t) spec->blocklen);
2096 			len = 16;
2097 		} else {
2098 			/* Number of Blocks */
2099 			if (spec->blockcnt > 0xffffffffULL) {
2100 				DSET32(&cp[0], 0xffffffffUL);
2101 			} else {
2102 				DSET32(&cp[0], (uint32_t) spec->blockcnt);
2103 			}
2104 			/* Block Length */
2105 			DSET32(&cp[4], (uint32_t) spec->blocklen);
2106 			len = 8;
2107 		}
2108 		cp += len;
2109 	}
2110 	data[3] = len;                  /* Block Descripter Length */
2111 
2112 	plen = istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len);
2113 	if (plen < 0) {
2114 		return -1;
2115 	}
2116 	cp += plen;
2117 
2118 	total = hlen + len + plen;
2119 	data[0] = total - 1;            /* Mode Data Length */
2120 
2121 	return total;
2122 }
2123 
2124 static int
istgt_lu_disk_scsi_mode_sense10(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,int dbd,int llbaa,int pc,int page,int subpage,uint8_t * data,int alloc_len)2125 istgt_lu_disk_scsi_mode_sense10(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int llbaa, int pc, int page, int subpage, uint8_t *data, int alloc_len)
2126 {
2127 	uint8_t *cp;
2128 	int hlen = 0, len = 0, plen;
2129 	int total;
2130 
2131 	DSET16(&data[0], 0);            /* Mode Data Length */
2132 	data[2] = 0;                    /* Medium Type */
2133 	data[3] = 0;                    /* Device-Specific Parameter */
2134 	if (spec->lu->readonly) {
2135 		BDADD8(&data[3], 1, 7);     /* WP */
2136 	}
2137 	if (llbaa) {
2138 		BDSET8(&data[4], 1, 1);      /* Long LBA */
2139 	} else {
2140 		BDSET8(&data[4], 0, 1);      /* Short LBA */
2141 	}
2142 	data[5] = 0;                    /* Reserved */
2143 	DSET16(&data[6], 0);  		    /* Block Descripter Length */
2144 	hlen = 8;
2145 
2146 	cp = &data[8];
2147 	if (dbd) {                      /* Disable Block Descripters */
2148 		len = 0;
2149 	} else {
2150 		if (llbaa) {
2151 			/* Number of Blocks */
2152 			DSET64(&cp[0], spec->blockcnt);
2153 			/* Reserved */
2154 			DSET32(&cp[8], 0);
2155 			/* Block Length */
2156 			DSET32(&cp[12], (uint32_t) spec->blocklen);
2157 			len = 16;
2158 		} else {
2159 			/* Number of Blocks */
2160 			if (spec->blockcnt > 0xffffffffULL) {
2161 				DSET32(&cp[0], 0xffffffffUL);
2162 			} else {
2163 				DSET32(&cp[0], (uint32_t) spec->blockcnt);
2164 			}
2165 			/* Block Length */
2166 			DSET32(&cp[4], (uint32_t) spec->blocklen);
2167 			len = 8;
2168 		}
2169 		cp += len;
2170 	}
2171 	DSET16(&data[6], len);          /* Block Descripter Length */
2172 
2173 	plen = istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len);
2174 	if (plen < 0) {
2175 		return -1;
2176 	}
2177 	cp += plen;
2178 
2179 	total = hlen + len + plen;
2180 	DSET16(&data[0], total - 2);	/* Mode Data Length */
2181 
2182 	return total;
2183 }
2184 
2185 static int
istgt_lu_disk_transfer_data(CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint8_t * buf,size_t bufsize,size_t len)2186 istgt_lu_disk_transfer_data(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *buf, size_t bufsize, size_t len)
2187 {
2188 	int rc;
2189 
2190 	if (lu_cmd->lu->queue_depth == 0) {
2191 		if (len > bufsize) {
2192 			ISTGT_ERRLOG("bufsize(%zd) too small\n", bufsize);
2193 			return -1;
2194 		}
2195 		rc = istgt_iscsi_transfer_out(conn, lu_cmd, buf, bufsize, len);
2196 		if (rc < 0) {
2197 			ISTGT_ERRLOG("iscsi_transfer_out()\n");
2198 			return -1;
2199 		}
2200 	}
2201 	return 0;
2202 }
2203 
2204 static int
istgt_lu_disk_scsi_mode_select_page(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,int pf,int sp,uint8_t * data,size_t len)2205 istgt_lu_disk_scsi_mode_select_page(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int pf, int sp, uint8_t *data, size_t len)
2206 {
2207 	size_t hlen, plen;
2208 	int ps, spf, page, subpage;
2209 	int rc;
2210 
2211 	if (pf == 0) {
2212 		/* vendor specific */
2213 		return 0;
2214 	}
2215 
2216 	if (len < 1)
2217 		return 0;
2218 	ps = BGET8(&data[0], 7);
2219 	spf = BGET8(&data[0], 6);
2220 	page = data[0] & 0x3f;
2221 	if (spf) {
2222 		/* Sub_page mode page format */
2223 		hlen = 4;
2224 		if (len < hlen)
2225 			return 0;
2226 		subpage = data[1];
2227 
2228 		plen = DGET16(&data[2]);
2229 	} else {
2230 		/* Page_0 mode page format */
2231 		hlen = 2;
2232 		if (len < hlen)
2233 			return 0;
2234 		subpage = 0;
2235 		plen = data[1];
2236 	}
2237 	plen += hlen;
2238 	if (len < plen)
2239 		return 0;
2240 
2241 #if 0
2242 	printf("ps=%d, page=%2.2x, subpage=%2.2x\n", ps, page, subpage);
2243 #endif
2244 	switch (page) {
2245 	case 0x08:
2246 		/* Caching */
2247 		{
2248 			int wce, rcd;
2249 
2250 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Caching\n");
2251 			if (subpage != 0x00)
2252 				break;
2253 			if (plen != 0x12 + hlen) {
2254 				/* unknown format */
2255 				break;
2256 			}
2257 			wce = BGET8(&data[2], 2); /* WCE */
2258 			rcd = BGET8(&data[2], 0); /* RCD */
2259 
2260 			{
2261 				int fd;
2262 				fd = spec->fd;
2263 				rc = fcntl(fd , F_GETFL, 0);
2264 				if (rc != -1) {
2265 					if (wce) {
2266 						ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Writeback cache enable\n");
2267 						rc = fcntl(fd, F_SETFL, (rc & ~O_FSYNC));
2268 						spec->write_cache = 1;
2269 					} else {
2270 						rc = fcntl(fd, F_SETFL, (rc | O_FSYNC));
2271 						spec->write_cache = 0;
2272 					}
2273 					if (rc == -1) {
2274 						/* XXX */
2275 						//ISTGT_ERRLOG("fcntl(F_SETFL) failed\n");
2276 					}
2277 				}
2278 			}
2279 			if (rcd) {
2280 				ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Read cache disable\n");
2281 				spec->read_cache = 0;
2282 			} else {
2283 				spec->read_cache = 1;
2284 			}
2285 		}
2286 		break;
2287 	default:
2288 		/* not supported */
2289 		break;
2290 	}
2291 
2292 	len -= plen;
2293 	if (len != 0) {
2294 		rc = istgt_lu_disk_scsi_mode_select_page(spec, conn, cdb,  pf, sp, &data[plen], len);
2295 		if (rc < 0) {
2296 			return rc;
2297 		}
2298 	}
2299 	return 0;
2300 }
2301 
2302 static int
istgt_lu_disk_scsi_read_defect10(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,int req_plist,int req_glist,int list_format,uint8_t * data,int alloc_len)2303 istgt_lu_disk_scsi_read_defect10(ISTGT_LU_DISK *spec __attribute__((__unused__)), CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int req_plist, int req_glist, int list_format, uint8_t *data, int alloc_len)
2304 {
2305 	uint8_t *cp;
2306 	int hlen = 0, len = 0;
2307 	int total;
2308 
2309 	if (alloc_len < 4) {
2310 		return -1;
2311 	}
2312 
2313 	data[0] = 0;				/* Reserved */
2314 	data[1] = 0;
2315 	if (req_plist) {
2316 		BDADD8(&data[1], 1, 4);		/* PLISTV */
2317 	}
2318 	if (req_glist) {
2319 		BDADD8(&data[1], 1, 3);		/* GLISTV */
2320 	}
2321 	BDADD8W(&data[1], list_format, 2, 3);	/* DEFECT LIST FORMAT */
2322 	DSET16(&data[2], 0);			/* DEFECT LIST LENGTH */
2323 	hlen = 4;
2324 
2325 	cp = &data[4];
2326 	/* defect list (if any) */
2327 	len = 0;
2328 
2329 	total = hlen + len;
2330 	DSET16(&data[2], total - hlen);		/* DEFECT LIST LENGTH */
2331 	return total;
2332 }
2333 
2334 static int
istgt_lu_disk_scsi_read_defect12(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,int req_plist,int req_glist,int list_format,uint8_t * data,int alloc_len)2335 istgt_lu_disk_scsi_read_defect12(ISTGT_LU_DISK *spec __attribute__((__unused__)), CONN_Ptr conn __attribute__((__unused__)), uint8_t *cdb __attribute__((__unused__)), int req_plist, int req_glist, int list_format, uint8_t *data, int alloc_len)
2336 {
2337 	uint8_t *cp;
2338 	int hlen = 0, len = 0;
2339 	int total;
2340 
2341 	if (alloc_len < 8) {
2342 		return -1;
2343 	}
2344 
2345 	data[0] = 0;				/* Reserved */
2346 	data[1] = 0;
2347 	if (req_plist) {
2348 		BDADD8(&data[1], 1, 4);		/* PLISTV */
2349 	}
2350 	if (req_glist) {
2351 		BDADD8(&data[1], 1, 3);		/* GLISTV */
2352 	}
2353 	BDADD8W(&data[1], list_format, 2, 3);	/* DEFECT LIST FORMAT */
2354 	data[2] = 0;				/* Reserved */
2355 	data[3] = 0;				/* Reserved */
2356 	DSET32(&data[4], 0);			/* DEFECT LIST LENGTH */
2357 	hlen = 8;
2358 
2359 	cp = &data[8];
2360 	/* defect list (if any) */
2361 	len = 0;
2362 
2363 	total = hlen + len;
2364 	DSET32(&data[4], total - hlen);		/* DEFECT LIST LENGTH */
2365 	return total;
2366 }
2367 
2368 #if 0
2369 static int
2370 istgt_lu_disk_scsi_request_sense(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int desc, uint8_t *data, int alloc_len)
2371 {
2372 	int len = 0, plen;
2373 
2374 	if (alloc_len < 18) {
2375 		ISTGT_ERRLOG("alloc_len(%d) too small\n", alloc_len);
2376 		return -1;
2377 	}
2378 
2379 	/* XXX TODO: fix */
2380 	if (desc == 0) {
2381 		/* fixed format */
2382 		/* NO ADDITIONAL SENSE INFORMATION */
2383 		/* BUILD_SENSE(NO_SENSE, 0x00, 0x00); */
2384 
2385 		/* VALID(7) RESPONSE CODE(6-0) */
2386 		BDSET8(&data[0], 0, 7);
2387 		BDADD8W(&data[0], 0x70, 6, 7);
2388 		/* Obsolete */
2389 		data[1] = 0;
2390 		/* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
2391 		BDSET8W(&data[2], ISTGT_SCSI_SENSE_NO_SENSE, 3, 4);
2392 		/* INFORMATION */
2393 		memset(&data[3], 0, 4);
2394 		/* ADDITIONAL SENSE LENGTH */
2395 		data[7] = 0;
2396 		len = 8;
2397 
2398 		/* COMMAND-SPECIFIC INFORMATION */
2399 		memset(&data[8], 0, 4);
2400 		/* ADDITIONAL SENSE CODE */
2401 		data[12] = 0x00;
2402 		/* ADDITIONAL SENSE CODE QUALIFIER */
2403 		data[13] = 0x00;
2404 		/* FIELD REPLACEABLE UNIT CODE */
2405 		data[14] = 0;
2406 		/* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
2407 		data[15] = 0;
2408 		data[16] = 0;
2409 		data[17] = 0;
2410 		plen = 18 - len;
2411 
2412 		/* ADDITIONAL SENSE LENGTH */
2413 		data[7] = plen;
2414 	} else {
2415 		/* descriptor format */
2416 		/* NO ADDITIONAL SENSE INFORMATION */
2417 		/* BUILD_SENSE(NO_SENSE, 0x00, 0x00); */
2418 
2419 		/* RESPONSE CODE(6-0) */
2420 		BDSET8W(&data[0], 0x72, 6, 7);
2421 		/* SENSE KEY(3-0) */
2422 		BDSET8W(&data[1], ISTGT_SCSI_SENSE_NO_SENSE, 3, 4);
2423 		/* ADDITIONAL SENSE CODE */
2424 		data[2] = 0x00;
2425 		/* ADDITIONAL SENSE CODE QUALIFIER */
2426 		data[3] = 0x00;
2427 		/* Reserved */
2428 		data[4] = 0;
2429 		data[5] = 0;
2430 		data[6] = 0;
2431 		/* ADDITIONAL SENSE LENGTH */
2432 		data[7] = 0;
2433 		len = 8;
2434 
2435 		/* Sense data descriptor(s) */
2436 		plen = 8 - len;
2437 
2438 		/* ADDITIONAL SENSE LENGTH */
2439 		data[7] = plen;
2440 	}
2441 	return len;
2442 }
2443 #endif
2444 
2445 static int
istgt_lu_disk_scsi_report_target_port_groups(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,uint8_t * data,int alloc_len)2446 istgt_lu_disk_scsi_report_target_port_groups(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb __attribute__((__unused__)), uint8_t *data, int alloc_len)
2447 {
2448 	ISTGT_Ptr istgt;
2449 	ISTGT_LU_Ptr lu;
2450 	uint8_t *cp;
2451 	uint8_t *cp_count;
2452 	int hlen = 0, len = 0, plen;
2453 	int total;
2454 	int pg_tag;
2455 	int nports;
2456 	int i, j, k;
2457 	int ridx;
2458 
2459 	if (alloc_len < 0xfff) {
2460 		return -1;
2461 	}
2462 
2463 	istgt = conn->istgt;
2464 	lu = spec->lu;
2465 
2466 	/* RETURN DATA LENGTH */
2467 	DSET32(&data[0], 0);
2468 	hlen = 4;
2469 
2470 	MTX_LOCK(&istgt->mutex);
2471 	for (i = 0; i < lu->maxmap; i++) {
2472 		pg_tag = lu->map[i].pg_tag;
2473 		/* skip same pg_tag */
2474 		for (j = 0; j < i; j++) {
2475 			if (lu->map[j].pg_tag == pg_tag) {
2476 				goto skip_pg_tag;
2477 			}
2478 		}
2479 
2480 		/* Target port group descriptor N */
2481 		cp = &data[hlen + len];
2482 
2483 		/* PREF(7) ASYMMETRIC ACCESS STATE(3-0) */
2484 		cp[0] = 0;
2485 		BDSET8(&cp[0], 1, 7); /* PREF */
2486 		switch (lu->map[j].pg_aas & 0x0f) {
2487 		case AAS_ACTIVE_OPTIMIZED:
2488 			BDADD8W(&cp[0], AAS_ACTIVE_OPTIMIZED, 3, 4);
2489 			break;
2490 		case AAS_ACTIVE_NON_OPTIMIZED:
2491 			BDADD8W(&cp[0], AAS_ACTIVE_NON_OPTIMIZED, 3, 4);
2492 			break;
2493 		case AAS_STANDBY:
2494 			BDADD8W(&cp[0], AAS_STANDBY, 3, 4);
2495 			break;
2496 		case AAS_UNAVAILABLE:
2497 			BDADD8W(&cp[0], AAS_UNAVAILABLE, 3, 4);
2498 			break;
2499 		case AAS_TRANSITIONING:
2500 			BDADD8W(&cp[0], AAS_TRANSITIONING, 3, 4);
2501 			break;
2502 		default:
2503 			ISTGT_ERRLOG("unsupported AAS\n");
2504 			break;
2505 		}
2506 		/* T_SUP(7) U_SUP(3) S_SUP(2) S_SUP AN_SUP(1) AO_SUP(0) */
2507 		cp[1] = 0;
2508 		//BDADD8(&cp[1], 1, 7); /* transitioning supported */
2509 		//BDADD8(&cp[1], 1, 3); /* unavailable supported */
2510 		//BDADD8(&cp[1], 1, 2); /* standby supported */
2511 		BDADD8(&cp[1], 1, 1); /* active/non-optimized supported */
2512 		BDADD8(&cp[1], 1, 0); /* active/optimized supported */
2513 		/* TARGET PORT GROUP */
2514 		DSET16(&cp[2], pg_tag);
2515 		/* Reserved */
2516 		cp[4] = 0;
2517 		/* STATUS CODE */
2518 		if (lu->map[j].pg_aas & AAS_STATUS_IMPLICIT) {
2519 			cp[5] = 0x02; /* by implicit */
2520 		} else if (lu->map[j].pg_aas & AAS_STATUS_STPG) {
2521 			cp[5] = 0x01; /* by SET TARGET PORT GROUPS */
2522 		} else {
2523 			cp[5] = 0;    /* No status */
2524 		}
2525 		/* Vendor specific */
2526 		cp[6] = 0;
2527 		/* TARGET PORT COUNT */
2528 		cp[7] = 0;
2529 		cp_count = &cp[7];
2530 		plen = 8;
2531 		len += plen;
2532 
2533 		nports = 0;
2534 		ridx = 0;
2535 		MTX_LOCK(&istgt->mutex);
2536 		for (j = 0; j < istgt->nportal_group; j++) {
2537 			if (istgt->portal_group[j].tag == pg_tag) {
2538 				for (k = 0; k < istgt->portal_group[j].nportals; k++) {
2539 					/* Target port descriptor(s) */
2540 					cp = &data[hlen + len];
2541 					/* Obsolete */
2542 					DSET16(&cp[0], 0);
2543 					/* RELATIVE TARGET PORT IDENTIFIER */
2544 					DSET16(&cp[2], (uint16_t) (1 + ridx));
2545 					plen = 4;
2546 					len += plen;
2547 					nports++;
2548 					ridx++;
2549 				}
2550 			} else {
2551 				ridx += istgt->portal_group[j].nportals;
2552 			}
2553 		}
2554 		MTX_UNLOCK(&istgt->mutex);
2555 
2556 		if (nports > 0xff) {
2557 			ISTGT_ERRLOG("too many portals in portal group\n");
2558 			MTX_UNLOCK(&istgt->mutex);
2559 			return -1;
2560 		}
2561 
2562 		/* TARGET PORT COUNT */
2563 		cp_count[0] = nports;
2564 
2565 	skip_pg_tag:
2566 		;
2567 	}
2568 	MTX_UNLOCK(&istgt->mutex);
2569 
2570 	total = hlen + len;
2571 	if (total > alloc_len) {
2572 		ISTGT_ERRLOG("alloc_len(%d) too small\n", alloc_len);
2573 		return -1;
2574 	}
2575 
2576 	/* RETURN DATA LENGTH */
2577 	DSET32(&data[0], total - 4);
2578 
2579 	return total;
2580 }
2581 
2582 static int
istgt_lu_disk_scsi_set_target_port_groups(ISTGT_LU_DISK * spec,CONN_Ptr conn,uint8_t * cdb,uint8_t * data,int len)2583 istgt_lu_disk_scsi_set_target_port_groups(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int len)
2584 {
2585 	ISTGT_LU_Ptr lu;
2586 	int pg_tag;
2587 	int aas;
2588 	int pg;
2589 	int rc;
2590 	int i;
2591 
2592 	if (len < 4) {
2593 		return -1;
2594 	}
2595 
2596 	lu = spec->lu;
2597 
2598 	aas = BGET8W(&data[0], 3, 4);
2599 	pg = DGET16(&data[2]);
2600 
2601 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AAS=0x%x, PG=0x%4.4x\n", aas, pg);
2602 
2603 	for (i = 0; i < lu->maxmap; i++) {
2604 		pg_tag = lu->map[i].pg_tag;
2605 		if (pg != pg_tag)
2606 			continue;
2607 
2608 		switch (aas) {
2609 		case AAS_ACTIVE_OPTIMIZED:
2610 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Active/optimized\n");
2611 			break;
2612 		case AAS_ACTIVE_NON_OPTIMIZED:
2613 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Active/non-optimized\n");
2614 			break;
2615 #if 0
2616 		case AAS_STANDBY:
2617 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Standby\n");
2618 			break;
2619 		case AAS_UNAVAILABLE:
2620 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Unavailable\n");
2621 			break;
2622 #endif
2623 		case AAS_TRANSITIONING:
2624 			return -1;
2625 		default:
2626 			ISTGT_ERRLOG("unsupported AAS 0x%x\n", aas);
2627 			return -1;
2628 		}
2629 		lu->map[i].pg_aas = aas;
2630 		lu->map[i].pg_aas |= AAS_STATUS_STPG;
2631 	}
2632 
2633 	len -=4;
2634 	if (len != 0) {
2635 		rc = istgt_lu_disk_scsi_set_target_port_groups(spec, conn, cdb, data, len);
2636 		if (rc < 0) {
2637 			return rc;
2638 		}
2639 	}
2640 	return 0;
2641 }
2642 
2643 static void
istgt_lu_disk_free_pr_key(ISTGT_LU_PR_KEY * prkey)2644 istgt_lu_disk_free_pr_key(ISTGT_LU_PR_KEY *prkey)
2645 {
2646 	int i;
2647 
2648 	if (prkey == NULL)
2649 		return;
2650 	xfree(prkey->registered_initiator_port);
2651 	prkey->registered_initiator_port = NULL;
2652 	xfree(prkey->registered_target_port);
2653 	prkey->registered_target_port = NULL;
2654 	prkey->pg_idx = 0;
2655 	prkey->pg_tag = 0;
2656 	for (i = 0; i < prkey->ninitiator_ports; i++) {
2657 		xfree(prkey->initiator_ports[i]);
2658 		prkey->initiator_ports[i] = NULL;
2659 	}
2660 	xfree(prkey->initiator_ports);
2661 	prkey->initiator_ports = NULL;
2662 	prkey->all_tpg = 0;
2663 }
2664 
2665 static ISTGT_LU_PR_KEY *
istgt_lu_disk_find_pr_key(ISTGT_LU_DISK * spec,const char * initiator_port,const char * target_port,uint64_t key)2666 istgt_lu_disk_find_pr_key(ISTGT_LU_DISK *spec, const char *initiator_port, const char *target_port, uint64_t key)
2667 {
2668 	ISTGT_LU_PR_KEY *prkey;
2669 	int i;
2670 
2671 	/* return pointer if I_T nexus is registered */
2672 #ifdef ISTGT_TRACE_DISK
2673 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
2674 	    "find prkey=0x%16.16"PRIx64", port=%s\n",
2675 	    key, ((initiator_port != NULL) ? initiator_port : "N/A"));
2676 #endif /* ISTGT_TRACE_DISK */
2677 
2678 	if (initiator_port == NULL)
2679 		return NULL;
2680 	for (i = 0; i < spec->npr_keys; i++) {
2681 		prkey = &spec->pr_keys[i];
2682 		if (prkey == NULL)
2683 			continue;
2684 #ifdef ISTGT_TRACE_DISK
2685 		if (key != 0) {
2686 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
2687 			    "prkey=0x%16.16"PRIx64"\n",
2688 			    prkey->key);
2689 		}
2690 #endif /* ISTGT_TRACE_DISK */
2691 		if (key != 0 && prkey->key != key)
2692 			continue;
2693 #ifdef ISTGT_TRACE_DISK
2694 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "pript=%s, ipt=%s\n",
2695 		    prkey->registered_initiator_port,
2696 		    initiator_port);
2697 #endif /* ISTGT_TRACE_DISK */
2698 		if (strcmp(prkey->registered_initiator_port,
2699 			initiator_port) == 0) {
2700 #ifdef ISTGT_TRACE_DISK
2701 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "prtpt=%s, tpt=%s\n",
2702 			    prkey->registered_target_port,
2703 			    target_port);
2704 #endif /* ISTGT_TRACE_DISK */
2705 			if (prkey->all_tpg != 0
2706 			    || target_port == NULL
2707 			    || strcmp(prkey->registered_target_port,
2708 				target_port) == 0) {
2709 				return prkey;
2710 			}
2711 		}
2712 	}
2713 	return NULL;
2714 }
2715 
2716 static int
istgt_lu_disk_remove_other_pr_key(ISTGT_LU_DISK * spec,CONN_Ptr conn,const char * initiator_port,const char * target_port,uint64_t key)2717 istgt_lu_disk_remove_other_pr_key(ISTGT_LU_DISK *spec, CONN_Ptr conn __attribute__((__unused__)), const char *initiator_port, const char *target_port, uint64_t key)
2718 {
2719 	ISTGT_LU_PR_KEY *prkey, *prkey1, *prkey2;
2720 	int i, j;
2721 
2722 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
2723 	    "remove prkey=0x%16.16"PRIx64", iniport=%s, tgtport=%s\n",
2724 	    key, ((initiator_port != NULL) ? initiator_port : "N/A"),
2725 	    ((target_port != NULL) ? target_port : "N/A"));
2726 
2727 	if (spec->npr_keys == 0)
2728 		return 0;
2729 
2730 	/* remove specified prkey from end of array */
2731 	for (i = spec->npr_keys - 1; i >= 0; i--) {
2732 		prkey = &spec->pr_keys[i];
2733 		if (prkey == NULL)
2734 			continue;
2735 		if (key == 0 || prkey->key == key)
2736 			continue;
2737 		/* NULL means all initiator/target */
2738 		if (initiator_port == NULL ||
2739 		    strcasecmp(prkey->registered_initiator_port,
2740 			initiator_port) == 0)
2741 			continue;
2742 		if (prkey->all_tpg != 0
2743 		    || target_port == NULL
2744 		    || strcasecmp(prkey->registered_target_port,
2745 			target_port) == 0)
2746 			continue;
2747 
2748 		/* this prkey will remove */
2749 		istgt_lu_disk_free_pr_key(prkey);
2750 		/* move used array */
2751 		for (j = i; j < spec->npr_keys - 1; j++) {
2752 			prkey1 = &spec->pr_keys[j];
2753 			prkey2 = &spec->pr_keys[j+1];
2754 			*prkey1 = *prkey2;
2755 		}
2756 		/* last array is cleared */
2757 		prkey1 = &spec->pr_keys[j];
2758 		memset(prkey1, 0, sizeof(*prkey1));
2759 		prkey1->registered_initiator_port = NULL;
2760 		prkey1->registered_target_port = NULL;
2761 		prkey1->initiator_ports = NULL;
2762 		/* update counts */
2763 		spec->npr_keys--;
2764 	}
2765 #ifdef ISTGT_TRACE_DISK
2766 	if (g_trace_flag) {
2767 		for (i = 0; i < spec->npr_keys; i++) {
2768 			prkey = &spec->pr_keys[i];
2769 			if (prkey == NULL)
2770 				continue;
2771 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
2772 			    "keylist: prkey=0x%16.16"PRIx64", iniport=%s, tgtport=%s\n",
2773 			    prkey->key, prkey->registered_initiator_port,
2774 			    prkey->registered_target_port);
2775 		}
2776 	}
2777 #endif /* ISTGT_TRACE_DISK */
2778 	return 0;
2779 }
2780 
2781 static int
istgt_lu_disk_remove_pr_key(ISTGT_LU_DISK * spec,CONN_Ptr conn,const char * initiator_port,const char * target_port,uint64_t key)2782 istgt_lu_disk_remove_pr_key(ISTGT_LU_DISK *spec, CONN_Ptr conn __attribute__((__unused__)), const char *initiator_port, const char *target_port, uint64_t key)
2783 {
2784 	ISTGT_LU_PR_KEY *prkey, *prkey1, *prkey2;
2785 	int i, j;
2786 
2787 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
2788 	    "remove prkey=0x%16.16"PRIx64", iniport=%s, tgtport=%s\n",
2789 	    key, ((initiator_port != NULL) ? initiator_port : "N/A"),
2790 	    ((target_port != NULL) ? target_port : "N/A"));
2791 
2792 	if (spec->npr_keys == 0)
2793 		return 0;
2794 
2795 	/* remove specified prkey from end of array */
2796 	for (i = spec->npr_keys - 1; i >= 0; i--) {
2797 		prkey = &spec->pr_keys[i];
2798 		if (prkey == NULL)
2799 			continue;
2800 		if (key != 0 && prkey->key != key)
2801 			continue;
2802 		/* NULL means all initiator/target */
2803 		if (initiator_port != NULL
2804 		    && strcasecmp(prkey->registered_initiator_port,
2805 			initiator_port) != 0)
2806 			continue;
2807 		if (prkey->all_tpg == 0
2808 		    && target_port != NULL
2809 		    && strcasecmp(prkey->registered_target_port,
2810 			target_port) != 0)
2811 			continue;
2812 
2813 		/* this prkey will remove */
2814 		istgt_lu_disk_free_pr_key(prkey);
2815 		/* move used array */
2816 		for (j = i; j < spec->npr_keys - 1; j++) {
2817 			prkey1 = &spec->pr_keys[j];
2818 			prkey2 = &spec->pr_keys[j+1];
2819 			*prkey1 = *prkey2;
2820 		}
2821 		/* last array is cleared */
2822 		prkey1 = &spec->pr_keys[j];
2823 		memset(prkey1, 0, sizeof(*prkey1));
2824 		prkey1->registered_initiator_port = NULL;
2825 		prkey1->registered_target_port = NULL;
2826 		prkey1->initiator_ports = NULL;
2827 		/* update counts */
2828 		spec->npr_keys--;
2829 	}
2830 #ifdef ISTGT_TRACE_DISK
2831 	if (g_trace_flag) {
2832 		for (i = 0; i < spec->npr_keys; i++) {
2833 			prkey = &spec->pr_keys[i];
2834 			if (prkey == NULL)
2835 				continue;
2836 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
2837 			    "keylist: prkey=0x%16.16"PRIx64", iniport=%s, tgtport=%s\n",
2838 			    prkey->key, prkey->registered_initiator_port,
2839 			    prkey->registered_target_port);
2840 		}
2841 	}
2842 #endif /* ISTGT_TRACE_DISK */
2843 	return 0;
2844 }
2845 
2846 static int
istgt_lu_parse_transport_id(char ** tid,uint8_t * data,int len)2847 istgt_lu_parse_transport_id(char **tid, uint8_t *data, int len)
2848 {
2849 	int fc, pi;
2850 	int hlen, plen;
2851 
2852 	if (tid == NULL)
2853 		return -1;
2854 	if (data == NULL)
2855 		return -1;
2856 
2857 	fc = BGET8W(&data[0], 7, 2);
2858 	pi = BGET8W(&data[0], 3, 4);
2859 	if (fc != 0) {
2860 		ISTGT_ERRLOG("FORMAT CODE != 0\n");
2861 		return -1;
2862 	}
2863 	if (pi != SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME) {
2864 		ISTGT_ERRLOG("PROTOCOL IDENTIFIER != ISCSI\n");
2865 		return -1;
2866 	}
2867 
2868 	/* PROTOCOL IDENTIFIER = 0x05 */
2869 	hlen = 4;
2870 	/* ADDITIONAL LENGTH */
2871 	plen = DGET16(&data[2]);
2872 	if (plen > len) {
2873 		ISTGT_ERRLOG("invalid length %d (expected %d)\n",
2874 		    plen, len);
2875 		return -1;
2876 	}
2877 	if (plen > MAX_ISCSI_NAME) {
2878 		ISTGT_ERRLOG("invalid length %d (expected %d)\n",
2879 		    plen, MAX_ISCSI_NAME);
2880 		return -1;
2881 	}
2882 
2883 	/* ISCSI NAME */
2884 	*tid = xmalloc(plen + 1);
2885 	memcpy(*tid, data, plen);
2886 	(*tid)[plen] = '\0';
2887 	strlwr(*tid);
2888 
2889 	return hlen + plen;
2890 }
2891 
2892 static int
istgt_lu_disk_scsi_persistent_reserve_in(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,int sa,uint8_t * data,int alloc_len)2893 istgt_lu_disk_scsi_persistent_reserve_in(ISTGT_LU_DISK *spec, CONN_Ptr conn __attribute__((__unused__)), ISTGT_LU_CMD_Ptr lu_cmd, int sa, uint8_t *data, int alloc_len __attribute__((__unused__)))
2894 {
2895 	ISTGT_LU_PR_KEY *prkey;
2896 	size_t hlen = 0, len = 0, plen;
2897 	uint8_t *sense_data;
2898 	size_t *sense_len;
2899 	uint8_t *cp;
2900 	int total;
2901 	int i;
2902 
2903 	sense_data = lu_cmd->sense_data;
2904 	sense_len = &lu_cmd->sense_data_len;
2905 	*sense_len = 0;
2906 
2907 	cp = &data[hlen + len];
2908 	total = 0;
2909 	switch (sa) {
2910 	case 0x00: /* READ KEYS */
2911 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ KEYS\n");
2912 
2913 		/* PRGENERATION */
2914 		DSET32(&data[0], spec->pr_generation);
2915 		/* ADDITIONAL LENGTH  */
2916 		DSET32(&data[4], 0);
2917 		hlen = 8;
2918 
2919 		for (i = 0; i < spec->npr_keys; i++) {
2920 			prkey = &spec->pr_keys[i];
2921 			/* reservation key N */
2922 			cp = &data[hlen + len];
2923 			DSET64(&cp[0], prkey->key);
2924 			len += 8;
2925 		}
2926 		total = hlen + len;
2927 		/* ADDITIONAL LENGTH  */
2928 		DSET32(&data[4], total - hlen);
2929 		break;
2930 
2931 	case 0x01: /* READ RESERVATION */
2932 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ RESERVATION\n");
2933 
2934 		/* PRGENERATION */
2935 		DSET32(&data[0], spec->pr_generation);
2936 		/* ADDITIONAL LENGTH  */
2937 		DSET32(&data[4], 0);
2938 		hlen = 8;
2939 
2940 		if (spec->rsv_key != 0) {
2941 			/* RESERVATION KEY */
2942 			DSET64(&data[8], spec->rsv_key);
2943 			/* Obsolete */
2944 			DSET32(&data[16], 0);
2945 			/* Reserved */
2946 			data[20] = 0;
2947 			/* SCOPE(7-4) TYPE(3-0) */
2948 			BDSET8W(&data[21], spec->rsv_scope, 7, 4);
2949 			BDADD8W(&data[21], spec->rsv_type, 3, 4);
2950 			/* Obsolete */
2951 			DSET16(&data[22], 0);
2952 			len = 24 - hlen;
2953 		}
2954 
2955 		total = hlen + len;
2956 		/* ADDITIONAL LENGTH  */
2957 		DSET32(&data[4], total - hlen);
2958 		break;
2959 
2960 	case 0x02: /* REPORT CAPABILITIES */
2961 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT CAPABILITIES\n");
2962 
2963 		/* LENGTH */
2964 		DSET16(&data[0], 0x0008);
2965 		/* CRH(4) SIP_C(3) ATP_C(2) PTPL_C(0) */
2966 		data[2] = 0;
2967 		//BDADD8(&data[2], 1, 4); /* Compatible Reservation Handling */
2968 		BDADD8(&data[2], 1, 3); /* Specify Initiator Ports Capable */
2969 		BDADD8(&data[2], 1, 2); /* All Target Ports Capable */
2970 		//BDADD8(&data[2], 1, 0); /* Persist Through Power Loss Capable */
2971 		/* TMV(7) PTPL_A(0) */
2972 		data[3] = 0;
2973 		//BDADD8(&data[2], 1, 7); /* Type Mask Valid */
2974 		//BDADD8(&data[2], 1, 0); /* Persist Through Power Loss Activated */
2975 		/* PERSISTENT RESERVATION TYPE MASK */
2976 		DSET16(&data[4], 0);
2977 		/* Reserved */
2978 		DSET16(&data[6], 0);
2979 		hlen = 8;
2980 
2981 		total = hlen + len;
2982 		break;
2983 
2984 	case 0x03: /* READ FULL STATUS */
2985 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ FULL STATUS\n");
2986 
2987 		/* PRGENERATION */
2988 		DSET32(&data[0], spec->pr_generation);
2989 		/* ADDITIONAL LENGTH  */
2990 		DSET32(&data[4], 0);
2991 		hlen = 8;
2992 
2993 		for (i = 0; i < spec->npr_keys; i++) {
2994 			prkey = &spec->pr_keys[i];
2995 			/* Full status descriptors N */
2996 			cp = &data[hlen + len];
2997 
2998 			/* RESERVATION KEY */
2999 			DSET64(&cp[0], prkey->key);
3000 			/* Reserved */
3001 			DSET64(&cp[8], 0);
3002 			/* ALL_TG_PT(1) R_HOLDER(0) */
3003 			cp[12] = 0;
3004 			if (prkey->all_tpg) {
3005 				BDADD8(&cp[12], 1, 1);
3006 			}
3007 			/* SCOPE(7-4) TYPE(3-0) */
3008 			cp[13] = 0;
3009 			if (spec->rsv_key != 0) {
3010 				if (spec->rsv_key == prkey->key) {
3011 					BDADD8(&cp[12], 1, 0);
3012 					BDADD8W(&cp[13], spec->rsv_scope & 0x0f, 7, 4);
3013 					BDADD8W(&cp[13], spec->rsv_type & 0x0f, 3, 4);
3014 				}
3015 			}
3016 			/* Reserved */
3017 			DSET32(&cp[14], 0);
3018 			/* RELATIVE TARGET PORT IDENTIFIER */
3019 			DSET16(&cp[18], 1 + prkey->pg_idx);
3020 			/* ADDITIONAL DESCRIPTOR LENGTH */
3021 			DSET32(&cp[20], 0);
3022 
3023 			/* TRANSPORTID */
3024 			plen = snprintf((char *) &cp[24], MAX_INITIATOR_NAME,
3025 			    "%s",
3026 			    prkey->registered_initiator_port);
3027 
3028 			/* ADDITIONAL DESCRIPTOR LENGTH */
3029 			DSET32(&cp[20], plen);
3030 			len += 24 + plen;
3031 		}
3032 
3033 		total = hlen + len;
3034 		/* ADDITIONAL LENGTH  */
3035 		DSET32(&data[4], total - hlen);
3036 		break;
3037 
3038 	default:
3039 		ISTGT_ERRLOG("unsupported service action 0x%x\n", sa);
3040 		/* INVALID FIELD IN CDB */
3041 		BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3042 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3043 		return -1;
3044 	}
3045 
3046 	lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3047 	return total;
3048 }
3049 
3050 static int
istgt_lu_disk_scsi_persistent_reserve_out(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,int sa,int scope,int type,uint8_t * data,int len)3051 istgt_lu_disk_scsi_persistent_reserve_out(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, int sa, int scope, int type, uint8_t *data, int len)
3052 {
3053 	ISTGT_LU_PR_KEY *prkey;
3054 	uint8_t *sense_data;
3055 	size_t *sense_len;
3056 	char *old_rsv_port = NULL;
3057 	char **initiator_ports;
3058 	int maxports, nports;
3059 	int plen, total;
3060 	uint64_t rkey;
3061 	uint64_t sarkey;
3062 	int spec_i_pt, all_tg_pt, aptpl;
3063 	int task_abort;
3064 	int idx;
3065 	int rc;
3066 	int i;
3067 
3068 	sense_data = lu_cmd->sense_data;
3069 	sense_len = &lu_cmd->sense_data_len;
3070 	*sense_len = 0;
3071 
3072 	rkey = DGET64(&data[0]);
3073 	sarkey = DGET64(&data[8]);
3074 	spec_i_pt = BGET8(&data[20], 3);
3075 	all_tg_pt = BGET8(&data[20], 2);
3076 	aptpl = BGET8(&data[20], 0);
3077 
3078 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3079 	    "sa=0x%2.2x, key=0x%16.16"PRIx64", sakey=0x%16.16"PRIx64
3080 	    ", ipt=%d, tgpt=%d, aptpl=%d\n",
3081 	    sa, rkey, sarkey, spec_i_pt, all_tg_pt, aptpl);
3082 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "port=%s\n",
3083 	    conn->initiator_port);
3084 
3085 	switch (sa) {
3086 	case 0x00: /* REGISTER */
3087 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REGISTER\n");
3088 
3089 		if (aptpl != 0) {
3090 			/* Activate Persist Through Power Loss */
3091 			ISTGT_ERRLOG("unsupport Activate Persist Through Power Loss\n");
3092 			/* INVALID FIELD IN PARAMETER LIST */
3093 			BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00);
3094 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3095 			return -1;
3096 		}
3097 		/* lost reservations if daemon restart */
3098 
3099 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3100 		    conn->target_port, 0);
3101 		if (prkey == NULL) {
3102 			/* unregistered port */
3103 			if (rkey != 0) {
3104 				lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3105 				return -1;
3106 			}
3107 			if (sarkey != 0) {
3108 				/* XXX check spec_i_pt */
3109 			}
3110 		} else {
3111 			/* registered port */
3112 			if (spec_i_pt) {
3113 				/* INVALID FIELD IN CDB */
3114 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3115 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3116 				return -1;
3117 			}
3118 
3119 			prkey = istgt_lu_disk_find_pr_key(spec,
3120 			    conn->initiator_port, conn->target_port, rkey);
3121 			if (prkey == NULL) {
3122 				/* not found key */
3123 				lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3124 				return -1;
3125 			}
3126 			/* remove existing keys */
3127 			rc = istgt_lu_disk_remove_pr_key(spec, conn,
3128 			    conn->initiator_port, conn->target_port, 0);
3129 			if (rc < 0) {
3130 				ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n");
3131 				/* INTERNAL TARGET FAILURE */
3132 				BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3133 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3134 				return -1;
3135 			}
3136 		}
3137 
3138 		/* unregister? */
3139 		if (sarkey == 0) {
3140 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3141 			return 0;
3142 		}
3143 
3144 		goto do_register;
3145 
3146 	case 0x01: /* RESERVE */
3147 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE\n");
3148 
3149 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3150 		    conn->target_port, 0);
3151 		if (prkey == NULL) {
3152 			/* unregistered port */
3153 			lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3154 			return -1;
3155 		}
3156 
3157 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3158 		    conn->target_port, rkey);
3159 		if (prkey == NULL) {
3160 			/* not found key */
3161 			lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3162 			return -1;
3163 		}
3164 		if (spec->rsv_key == 0) {
3165 			/* no reservation */
3166 		} else {
3167 			if (prkey->key != spec->rsv_key) {
3168 				lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3169 				return -1;
3170 			}
3171 			if (strcasecmp(spec->rsv_port, conn->initiator_port) != 0) {
3172 				lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3173 				return -1;
3174 			}
3175 #if 0
3176 			/* registrants can change the prkey */
3177 			if (g_trace_flag) {
3178 				ISTGT_WARNLOG("LU%d: duplicate reserve\n", spec->lu->num);
3179 			}
3180 #endif
3181 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3182 			return 0;
3183 		}
3184 
3185 		if (scope != 0x00) { // !LU_SCOPE
3186 			/* INVALID FIELD IN CDB */
3187 			BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3188 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3189 			return -1;
3190 		}
3191 		if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE
3192 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS
3193 		    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY
3194 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY
3195 		    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS
3196 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) {
3197 			ISTGT_ERRLOG("unsupported type 0x%x\n", type);
3198 			/* INVALID FIELD IN CDB */
3199 			BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3200 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3201 			return -1;
3202 		}
3203 
3204 		/* establish reservation by key */
3205 		xfree(spec->rsv_port);
3206 		spec->rsv_port = xstrdup(conn->initiator_port);
3207 		strlwr(spec->rsv_port);
3208 		spec->rsv_key = rkey;
3209 		spec->rsv_scope = scope;
3210 		spec->rsv_type = type;
3211 
3212 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3213 		    "LU%d: reserved (scope=%d, type=%d) by key=0x%16.16"
3214 		    PRIx64"\n",
3215 		    spec->lu->num, scope, type, rkey);
3216 		break;
3217 
3218 	case 0x02: /* RELEASE */
3219 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE\n");
3220 
3221 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3222 		    conn->target_port, 0);
3223 		if (prkey == NULL) {
3224 			/* unregistered port */
3225 			lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3226 			return -1;
3227 		}
3228 
3229 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3230 		    conn->target_port, rkey);
3231 		if (prkey == NULL) {
3232 			/* not found key */
3233 			lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3234 			return -1;
3235 		}
3236 		if (spec->rsv_key == 0) {
3237 			/* no reservation */
3238 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3239 			return 0;
3240 		}
3241 		if (prkey->key != spec->rsv_key) {
3242 			/* INVALID RELEASE OF PERSISTENT RESERVATION */
3243 			BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x04);
3244 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3245 			return -1;
3246 		}
3247 		if (strcasecmp(spec->rsv_port, conn->initiator_port) != 0) {
3248 			/* INVALID RELEASE OF PERSISTENT RESERVATION */
3249 			BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x04);
3250 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3251 			return -1;
3252 		}
3253 
3254 		if (scope != 0x00) { // !LU_SCOPE
3255 			/* INVALID FIELD IN CDB */
3256 			BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3257 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3258 			return -1;
3259 		}
3260 		if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE
3261 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS
3262 		    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY
3263 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY
3264 		    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS
3265 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) {
3266 			ISTGT_ERRLOG("unsupported type 0x%x\n", type);
3267 			/* INVALID FIELD IN CDB */
3268 			BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3269 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3270 			return -1;
3271 		}
3272 		if (spec->rsv_scope != scope || spec->rsv_type != type) {
3273 			/* INVALID RELEASE OF PERSISTENT RESERVATION */
3274 			BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x04);
3275 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3276 			return -1;
3277 		}
3278 
3279 		/* release reservation by key */
3280 		xfree(spec->rsv_port);
3281 		spec->rsv_port = NULL;
3282 		spec->rsv_key = 0;
3283 		spec->rsv_scope = 0;
3284 		spec->rsv_type = 0;
3285 
3286 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3287 		    "LU%d: released (scope=%d, type=%d) by key=0x%16.16"
3288 		    PRIx64"\n",
3289 		    spec->lu->num, scope, type, rkey);
3290 		break;
3291 
3292 	case 0x03: /* CLEAR */
3293 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "CLEAR\n");
3294 
3295 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3296 		    conn->target_port, 0);
3297 		if (prkey == NULL) {
3298 			/* unregistered port */
3299 			lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3300 			return -1;
3301 		}
3302 
3303 		/* release reservation */
3304 		xfree(spec->rsv_port);
3305 		spec->rsv_port = NULL;
3306 		spec->rsv_key = 0;
3307 		spec->rsv_scope = 0;
3308 		spec->rsv_type = 0;
3309 
3310 		/* remove all registrations */
3311 		for (i = 0; i < spec->npr_keys; i++) {
3312 			prkey = &spec->pr_keys[i];
3313 			istgt_lu_disk_free_pr_key(prkey);
3314 		}
3315 		spec->npr_keys = 0;
3316 		break;
3317 
3318 	case 0x04: /* PREEMPT */
3319 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PREEMPT\n");
3320 
3321 		task_abort = 0;
3322 	do_preempt:
3323 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3324 		    conn->target_port, 0);
3325 		if (prkey == NULL) {
3326 			/* unregistered port */
3327 			lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
3328 			return -1;
3329 		}
3330 
3331 		if (spec->rsv_key == 0) {
3332 			/* no reservation */
3333 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "no reservation\n");
3334 			/* remove registration */
3335 			rc = istgt_lu_disk_remove_pr_key(spec, conn,
3336 			    NULL, NULL, sarkey);
3337 			if (rc < 0) {
3338 				ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n");
3339 				/* INTERNAL TARGET FAILURE */
3340 				BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3341 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3342 				return -1;
3343 			}
3344 
3345 			/* update generation */
3346 			spec->pr_generation++;
3347 
3348 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3349 			break;
3350 		}
3351 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "rsv_key=0x%16.16"PRIx64"\n",
3352 		    spec->rsv_key);
3353 
3354 		if (spec->rsv_type == ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS
3355 		    || spec->rsv_type == ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) {
3356 			if (sarkey != 0) {
3357 				/* remove registration */
3358 				rc = istgt_lu_disk_remove_pr_key(spec, conn,
3359 				    NULL, NULL, sarkey);
3360 				if (rc < 0) {
3361 					ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n");
3362 					/* INTERNAL TARGET FAILURE */
3363 					BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3364 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3365 					return -1;
3366 				}
3367 
3368 				/* update generation */
3369 				spec->pr_generation++;
3370 
3371 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3372 				break;
3373 			} else {
3374 				/* remove other registrations */
3375 				rc = istgt_lu_disk_remove_other_pr_key(spec, conn,
3376 				    conn->initiator_port,
3377 				    conn->target_port,
3378 				    rkey);
3379 				if (rc < 0) {
3380 					ISTGT_ERRLOG("lu_disk_remove_other_pr_key() failed\n");
3381 					/* INTERNAL TARGET FAILURE */
3382 					BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3383 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3384 					return -1;
3385 				}
3386 
3387 				if (scope != 0x00) { // !LU_SCOPE
3388 					/* INVALID FIELD IN CDB */
3389 					BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3390 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3391 					return -1;
3392 				}
3393 				if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE
3394 				    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS
3395 				    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY
3396 				    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY
3397 				    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS
3398 				    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) {
3399 					ISTGT_ERRLOG("unsupported type 0x%x\n", type);
3400 					/* INVALID FIELD IN CDB */
3401 					BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3402 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3403 					return -1;
3404 				}
3405 
3406 				/* release reservation */
3407 				//xfree(spec->rsv_port);
3408 				old_rsv_port = spec->rsv_port;
3409 				spec->rsv_port = NULL;
3410 				spec->rsv_key = 0;
3411 				spec->rsv_scope = 0;
3412 				spec->rsv_type = 0;
3413 				/* establish new reservation */
3414 				spec->rsv_port = xstrdup(conn->initiator_port);
3415 				strlwr(spec->rsv_port);
3416 				spec->rsv_key = rkey;
3417 				spec->rsv_scope = scope;
3418 				spec->rsv_type = type;
3419 
3420 				ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3421 				    "LU%d: reserved (scope=%d, type=%d)"
3422 				    "by key=0x%16.16"PRIx64"\n",
3423 				    spec->lu->num, scope, type, rkey);
3424 
3425 				/* update generation */
3426 				spec->pr_generation++;
3427 
3428 				/* XXX TODO fix */
3429 				if (task_abort) {
3430 					/* abort all tasks for preempted I_T nexus */
3431 					if (old_rsv_port != NULL) {
3432 						rc = istgt_lu_disk_queue_abort_ITL(spec, old_rsv_port);
3433 						xfree(old_rsv_port);
3434 						old_rsv_port = NULL;
3435 						if (rc < 0) {
3436 							/* INTERNAL TARGET FAILURE */
3437 							BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3438 							lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3439 							return -1;
3440 						}
3441 					}
3442 				}
3443 				if (old_rsv_port != NULL) {
3444 					xfree(old_rsv_port);
3445 					old_rsv_port = NULL;
3446 				}
3447 
3448 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3449 				break;
3450 			}
3451 		}
3452 
3453 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3454 		    conn->target_port, rkey);
3455 
3456 		if (prkey == NULL) {
3457 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3458 			    "prkey == NULL\n");
3459 		} else {
3460 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3461 			    "prkey key=%16.16"PRIx64"\n",
3462 			    prkey->key);
3463 		}
3464 
3465 		if (prkey == NULL
3466 		    || sarkey != spec->rsv_key) {
3467 			if (sarkey != 0) {
3468 				/* remove registration */
3469 				rc = istgt_lu_disk_remove_pr_key(spec, conn,
3470 				    NULL, NULL, sarkey);
3471 				if (rc < 0) {
3472 					ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n");
3473 					/* INTERNAL TARGET FAILURE */
3474 					BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3475 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3476 					return -1;
3477 				}
3478 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3479 				break;
3480 			} else {
3481 				/* INVALID FIELD IN PARAMETER LIST */
3482 				BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00);
3483 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3484 				return -1;
3485 			}
3486 		}
3487 
3488 		/* remove registration */
3489 		rc = istgt_lu_disk_remove_pr_key(spec, conn,
3490 		    NULL, NULL, sarkey);
3491 		if (rc < 0) {
3492 			ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n");
3493 			/* INTERNAL TARGET FAILURE */
3494 			BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3495 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3496 			return -1;
3497 		}
3498 
3499 		if (scope != 0x00) { // !LU_SCOPE
3500 			/* INVALID FIELD IN CDB */
3501 			BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3502 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3503 			return -1;
3504 		}
3505 		if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE
3506 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS
3507 		    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY
3508 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY
3509 		    && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS
3510 		    && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) {
3511 			ISTGT_ERRLOG("unsupported type 0x%x\n", type);
3512 			/* INVALID FIELD IN CDB */
3513 			BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3514 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3515 			return -1;
3516 		}
3517 
3518 		/* release reservation */
3519 		//xfree(spec->rsv_port);
3520 		old_rsv_port = spec->rsv_port;
3521 		spec->rsv_port = NULL;
3522 		spec->rsv_key = 0;
3523 		spec->rsv_scope = 0;
3524 		spec->rsv_type = 0;
3525 		/* establish new reservation */
3526 		spec->rsv_port = xstrdup(conn->initiator_port);
3527 		strlwr(spec->rsv_port);
3528 		spec->rsv_key = rkey;
3529 		spec->rsv_scope = scope;
3530 		spec->rsv_type = type;
3531 
3532 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3533 		    "LU%d: reserved (scope=%d, type=%d) by key=0x%16.16"
3534 		    PRIx64"\n",
3535 		    spec->lu->num, scope, type, rkey);
3536 
3537 		/* update generation */
3538 		spec->pr_generation++;
3539 
3540 		/* XXX TODO fix */
3541 		if (task_abort) {
3542 			/* abort all tasks for preempted I_T nexus */
3543 			if (old_rsv_port != NULL) {
3544 				rc = istgt_lu_disk_queue_abort_ITL(spec, old_rsv_port);
3545 				xfree(old_rsv_port);
3546 				old_rsv_port = NULL;
3547 				if (rc < 0) {
3548 					/* INTERNAL TARGET FAILURE */
3549 					BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3550 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3551 					return -1;
3552 				}
3553 			}
3554 		}
3555 		if (old_rsv_port != NULL) {
3556 			xfree(old_rsv_port);
3557 			old_rsv_port = NULL;
3558 		}
3559 
3560 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3561 		break;
3562 
3563 	case 0x05: /* PREEMPT AND ABORT */
3564 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PREEMPT AND ABORT\n");
3565 
3566 		task_abort = 1;
3567 		goto do_preempt;
3568 
3569 	case 0x06: /* REGISTER AND IGNORE EXISTING KEY */
3570 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REGISTER AND IGNORE EXISTING KEY\n");
3571 
3572 		if (aptpl != 0) {
3573 			/* Activate Persist Through Power Loss */
3574 			ISTGT_ERRLOG("unsupport Activate Persist Through Power Loss\n");
3575 			/* INVALID FIELD IN PARAMETER LIST */
3576 			BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00);
3577 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3578 			return -1;
3579 		}
3580 		/* lost reservations if daemon restart */
3581 
3582 		prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3583 		    conn->target_port, 0);
3584 		if (prkey == NULL) {
3585 			/* unregistered port */
3586 			if (sarkey != 0) {
3587 				if (spec_i_pt) {
3588 					/* INVALID FIELD IN CDB */
3589 					BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3590 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3591 					return -1;
3592 				}
3593 			}
3594 			/* unregister? */
3595 			if (sarkey == 0) {
3596 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3597 				return 0;
3598 			}
3599 		} else {
3600 			/* registered port */
3601 			if (spec_i_pt) {
3602 				/* INVALID FIELD IN CDB */
3603 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3604 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3605 				return -1;
3606 			}
3607 		}
3608 
3609 		/* remove existing keys */
3610 		rc = istgt_lu_disk_remove_pr_key(spec, conn,
3611 		    conn->initiator_port,
3612 		    conn->target_port, 0);
3613 		if (rc < 0) {
3614 			ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n");
3615 			/* INTERNAL TARGET FAILURE */
3616 			BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3617 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3618 			return -1;
3619 		}
3620 
3621 		/* unregister? */
3622 		if (sarkey == 0) {
3623 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3624 			return 0;
3625 		}
3626 
3627 	do_register:
3628 		/* specified port? */
3629 		nports = 0;
3630 		initiator_ports = NULL;
3631 		if (spec_i_pt) {
3632 			if (len < 28) {
3633 				/* INVALID FIELD IN PARAMETER LIST */
3634 				BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00);
3635 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3636 				return -1;
3637 			}
3638 
3639 			/* TRANSPORTID PARAMETER DATA LENGTH */
3640 			plen = DGET32(&data[24]);
3641 			if (28 + plen > len) {
3642 				ISTGT_ERRLOG("invalid length %d (expect %d)\n",
3643 				    len, 28 + plen);
3644 				/* INVALID FIELD IN PARAMETER LIST */
3645 				BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00);
3646 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3647 				return -1;
3648 			}
3649 
3650 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3651 			    "TransportID parameter data length %d\n",
3652 			    plen);
3653 			if (plen != 0) {
3654 				maxports = MAX_LU_RESERVE_IPT;
3655 				initiator_ports = xmalloc(sizeof (char *) * maxports);
3656 				memset(initiator_ports, 0, sizeof (char *) * maxports);
3657 				nports = 0;
3658 				total = 0;
3659 				while (total < plen) {
3660 					if (nports >= MAX_LU_RESERVE_IPT) {
3661 						ISTGT_ERRLOG("maximum transport IDs\n");
3662 						/* INSUFFICIENT REGISTRATION RESOURCES */
3663 						BUILD_SENSE(ILLEGAL_REQUEST, 0x55, 0x04);
3664 						lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3665 						return -1;
3666 					}
3667 					rc = istgt_lu_parse_transport_id
3668 						(&initiator_ports[nports],
3669 						 &data[24] + total, plen - total);
3670 					if (rc < 0) {
3671 						/* INVALID FIELD IN PARAMETER LIST */
3672 						BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00);
3673 						lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3674 						return -1;
3675 					}
3676 					ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "got TransportID %s\n",
3677 					    initiator_ports[nports]);
3678 					total += rc;
3679 					nports++;
3680 				}
3681 			}
3682 			/* check all port unregistered? */
3683 			for (i = 0; i < nports; i++) {
3684 				prkey = istgt_lu_disk_find_pr_key(spec,
3685 				    initiator_ports[i], NULL, 0);
3686 				if (prkey != NULL) {
3687 					/* registered port */
3688 					/* INVALID FIELD IN CDB */
3689 					BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3690 					lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3691 					return -1;
3692 				}
3693 			}
3694 			/* OK, all port unregistered */
3695 			idx = spec->npr_keys;
3696 			if (idx + nports >= MAX_LU_RESERVE) {
3697 				/* INSUFFICIENT REGISTRATION RESOURCES */
3698 				BUILD_SENSE(ILLEGAL_REQUEST, 0x55, 0x04);
3699 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3700 				return -1;
3701 			}
3702 			/* register each I_T nexus */
3703 			for (i = 0; i < nports; i++) {
3704 				prkey = &spec->pr_keys[idx + i];
3705 
3706 				/* register new key */
3707 				prkey->key = sarkey;
3708 
3709 				/* command received port */
3710 				prkey->registered_initiator_port
3711 					= xstrdup(conn->initiator_port);
3712 				strlwr(prkey->registered_initiator_port);
3713 				prkey->registered_target_port
3714 					= xstrdup(conn->target_port);
3715 				strlwr(prkey->registered_target_port);
3716 				prkey->pg_idx = conn->portal.idx;
3717 				prkey->pg_tag = conn->portal.tag;
3718 
3719 				/* specified ports */
3720 				prkey->ninitiator_ports = 0;
3721 				prkey->initiator_ports = NULL;
3722 				prkey->all_tpg = (all_tg_pt) ? 1 : 0;
3723 			}
3724 			spec->npr_keys = idx + nports;
3725 		}
3726 
3727 		idx = spec->npr_keys;
3728 		if (idx >= MAX_LU_RESERVE) {
3729 			/* INSUFFICIENT REGISTRATION RESOURCES */
3730 			BUILD_SENSE(ILLEGAL_REQUEST, 0x55, 0x04);
3731 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3732 			return -1;
3733 		}
3734 		prkey = &spec->pr_keys[idx];
3735 
3736 		/* register new key */
3737 		prkey->key = sarkey;
3738 		/* replace existing reservation */
3739 		if (rkey != 0 && spec->rsv_key == rkey)
3740 			spec->rsv_key = sarkey;
3741 
3742 		/* command received port */
3743 		prkey->registered_initiator_port = xstrdup(conn->initiator_port);
3744 		strlwr(prkey->registered_initiator_port);
3745 		prkey->registered_target_port = xstrdup(conn->target_port);
3746 		strlwr(prkey->registered_target_port);
3747 		prkey->pg_idx = conn->portal.idx;
3748 		prkey->pg_tag = conn->portal.tag;
3749 
3750 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3751 		    "Register Key:0x%16.16"PRIx64", InitiatorPort: %s, TargetPort: %s\n",
3752 		    prkey->key, prkey->registered_initiator_port,
3753 		    prkey->registered_target_port);
3754 
3755 		/* specified ports */
3756 		prkey->ninitiator_ports = nports;
3757 		prkey->initiator_ports = initiator_ports;
3758 		prkey->all_tpg = (all_tg_pt) ? 1 : 0;
3759 
3760 		/* count up keys */
3761 		idx++;
3762 		spec->npr_keys = idx;
3763 
3764 		/* update generation */
3765 		spec->pr_generation++;
3766 		break;
3767 
3768 	case 0x07: /* REGISTER AND MOVE */
3769 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REGISTER AND MOVE\n");
3770 		/* INVALID FIELD IN CDB */
3771 		BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3772 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3773 		return -1;
3774 
3775 	default:
3776 		ISTGT_ERRLOG("unsupported service action 0x%x\n", sa);
3777 		/* INVALID FIELD IN CDB */
3778 		BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
3779 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3780 		return -1;
3781 	}
3782 
3783 	lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3784 	return 0;
3785 }
3786 
3787 static int
istgt_lu_disk_check_pr(ISTGT_LU_DISK * spec,CONN_Ptr conn,int pr_allow)3788 istgt_lu_disk_check_pr(ISTGT_LU_DISK *spec, CONN_Ptr conn, int pr_allow)
3789 {
3790 	ISTGT_LU_PR_KEY *prkey;
3791 
3792 #ifdef ISTGT_TRACE_DISK
3793 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3794 	    "RSV_KEY=0x%16.16"PRIx64", RSV_TYPE=0x%x, PR_ALLOW=0x%x\n",
3795 	    spec->rsv_key, spec->rsv_type, pr_allow);
3796 #endif /* ISTGT_TRACE_DISK */
3797 
3798 	prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port,
3799 	    conn->target_port, 0);
3800 	if (prkey != NULL) {
3801 #ifdef ISTGT_TRACE_DISK
3802 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3803 		    "PRKEY(0x%16.16"PRIx64") found for %s\n",
3804 		    prkey->key, conn->initiator_port);
3805 #endif /* ISTGT_TRACE_DISK */
3806 
3807 		if (spec->rsv_key == prkey->key) {
3808 			/* reservation holder */
3809 			return 0;
3810 		}
3811 
3812 		switch (spec->rsv_type) {
3813 		case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS:
3814 			if (pr_allow & PR_ALLOW_ALLRR)
3815 				return 0;
3816 			return -1;
3817 		case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS:
3818 			if (pr_allow & PR_ALLOW_ALLRR)
3819 				return 0;
3820 			return -1;
3821 		case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY:
3822 			if (pr_allow & PR_ALLOW_ALLRR)
3823 				return 0;
3824 			return -1;
3825 		case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY:
3826 			if (pr_allow & PR_ALLOW_ALLRR)
3827 				return 0;
3828 			return -1;
3829 		}
3830 	} else {
3831 #ifdef ISTGT_TRACE_DISK
3832 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
3833 		    "PRKEY not found for %s\n",
3834 		    conn->initiator_port);
3835 #endif /* ISTGT_TRACE_DISK */
3836 
3837 		switch (spec->rsv_type) {
3838 		case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS:
3839 			if (pr_allow & PR_ALLOW_WERR)
3840 				return 0;
3841 			return -1;
3842 		case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY:
3843 			if (pr_allow & PR_ALLOW_WERR)
3844 				return 0;
3845 			return -1;
3846 		case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS:
3847 			if (pr_allow & PR_ALLOW_EARR)
3848 				return 0;
3849 			return -1;
3850 		case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY:
3851 			if (pr_allow & PR_ALLOW_EARR)
3852 				return 0;
3853 			return -1;
3854 		}
3855 	}
3856 
3857 #ifdef ISTGT_TRACE_DISK
3858 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "non registrans type\n");
3859 #endif /* ISTGT_TRACE_DISK */
3860 	/* any I_T nexus */
3861 	switch (spec->rsv_type) {
3862 	case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE:
3863 		if (pr_allow & PR_ALLOW_WE)
3864 			return 0;
3865 		return -1;
3866 	case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS:
3867 		if (pr_allow & PR_ALLOW_EA)
3868 			return 0;
3869 		return -1;
3870 	}
3871 
3872 	/* NG */
3873 	return -1;
3874 }
3875 
3876 static int
istgt_lu_disk_scsi_release(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd)3877 istgt_lu_disk_scsi_release(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd)
3878 {
3879 	ISTGT_LU_CMD lu_cmd2;
3880 	uint8_t *sense_data;
3881 	size_t *sense_len;
3882 	uint64_t LUI;
3883 	uint64_t rkey;
3884 	uint8_t cdb[10];
3885 	uint8_t PRO_data[24];
3886 	int parameter_len;
3887 	int rc;
3888 
3889 	sense_data = lu_cmd->sense_data;
3890 	sense_len = &lu_cmd->sense_data_len;
3891 	*sense_len = 0;
3892 
3893 	memset(&lu_cmd2, 0, sizeof lu_cmd2);
3894 	lu_cmd2.sense_data = lu_cmd->sense_data;
3895 	lu_cmd2.sense_data_len = lu_cmd->sense_data_len;
3896 	memset(&cdb, 0, sizeof cdb);
3897 	parameter_len = sizeof PRO_data;
3898 
3899 	LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU);
3900 	rkey = istgt_get_rkey(conn->initiator_name, LUI);
3901 
3902 	/* issue release action of PERSISTENT RESERVE OUT */
3903 	cdb[0] = SPC_PERSISTENT_RESERVE_OUT;
3904 	BDSET8W(&cdb[1], 0x02, 4, 5); /* RELEASE */
3905 	BDSET8W(&cdb[2], 0x00, 7, 4); /* LU_SCOPE */
3906 	BDADD8W(&cdb[2], 0x03, 3, 4); /* ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS */
3907 	cdb[3] = 0;
3908 	cdb[4] = 0;
3909 	DSET32(&cdb[5], parameter_len);
3910 	cdb[9] = 0;
3911 	lu_cmd2.cdb = &cdb[0];
3912 
3913 	memset(&PRO_data, 0, sizeof PRO_data);
3914 	DSET64(&PRO_data[0], rkey); // RESERVATION KEY
3915 	DSET64(&PRO_data[8], 0);
3916 
3917 	rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2,
3918 	    0x02, 0x00, 0x03,
3919 	    PRO_data, parameter_len);
3920 	if (rc < 0) {
3921 		lu_cmd->status = lu_cmd2.status;
3922 		if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) {
3923 			return -1;
3924 		}
3925 		/* INTERNAL TARGET FAILURE */
3926 		BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3927 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3928 		return -1;
3929 	}
3930 
3931 	/* issue unregister action of PERSISTENT RESERVE OUT */
3932 	cdb[0] = SPC_PERSISTENT_RESERVE_OUT;
3933 	BDSET8W(&cdb[1], 0x06, 4, 5); /* REGISTER AND IGNORE EXISTING KEY */
3934 	cdb[2] = 0;
3935 	cdb[3] = 0;
3936 	cdb[4] = 0;
3937 	DSET32(&cdb[5], parameter_len);
3938 	cdb[9] = 0;
3939 	lu_cmd2.cdb = &cdb[0];
3940 
3941 	memset(&PRO_data, 0, sizeof PRO_data);
3942 	DSET64(&PRO_data[0], rkey); // RESERVATION KEY
3943 	DSET64(&PRO_data[8], 0); // unregister
3944 
3945 	rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2,
3946 	    0x06, 0, 0,
3947 	    PRO_data, parameter_len);
3948 	if (rc < 0) {
3949 		lu_cmd->status = lu_cmd2.status;
3950 		if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) {
3951 			return -1;
3952 		}
3953 		/* INTERNAL TARGET FAILURE */
3954 		BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
3955 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
3956 		return -1;
3957 	}
3958 
3959 	lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
3960 	return 0;
3961 }
3962 
3963 static int
istgt_lu_disk_scsi_reserve(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd)3964 istgt_lu_disk_scsi_reserve(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd)
3965 {
3966 	ISTGT_LU_CMD lu_cmd2;
3967 	uint8_t *sense_data;
3968 	size_t *sense_len;
3969 	uint64_t LUI;
3970 	uint64_t rkey;
3971 	uint8_t cdb[10];
3972 	uint8_t PRO_data[24];
3973 	int parameter_len;
3974 	int rc;
3975 
3976 	sense_data = lu_cmd->sense_data;
3977 	sense_len = &lu_cmd->sense_data_len;
3978 	*sense_len = 0;
3979 
3980 	memset(&lu_cmd2, 0, sizeof lu_cmd2);
3981 	lu_cmd2.sense_data = lu_cmd->sense_data;
3982 	lu_cmd2.sense_data_len = lu_cmd->sense_data_len;
3983 	memset(&cdb, 0, sizeof cdb);
3984 	parameter_len = sizeof PRO_data;
3985 
3986 	LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU);
3987 	rkey = istgt_get_rkey(conn->initiator_name, LUI);
3988 
3989 	/* issue register action of PERSISTENT RESERVE OUT */
3990 	cdb[0] = SPC_PERSISTENT_RESERVE_OUT;
3991 	BDSET8W(&cdb[1], 0x06, 4, 5); /* REGISTER AND IGNORE EXISTING KEY */
3992 	cdb[2] = 0;
3993 	cdb[3] = 0;
3994 	cdb[4] = 0;
3995 	DSET32(&cdb[5], parameter_len);
3996 	cdb[9] = 0;
3997 	lu_cmd2.cdb = &cdb[0];
3998 
3999 	memset(&PRO_data, 0, sizeof PRO_data);
4000 	DSET64(&PRO_data[0], 0);
4001 	DSET64(&PRO_data[8], rkey); // SERVICE ACTION RESERVATION KEY
4002 
4003 	rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2,
4004 	    0x06, 0, 0,
4005 	    PRO_data, parameter_len);
4006 	if (rc < 0) {
4007 		lu_cmd->status = lu_cmd2.status;
4008 		if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) {
4009 			return -1;
4010 		}
4011 		/* INTERNAL TARGET FAILURE */
4012 		BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
4013 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
4014 		return -1;
4015 	}
4016 
4017 	/* issue reserve action of PERSISTENT RESERVE OUT */
4018 	cdb[0] = SPC_PERSISTENT_RESERVE_OUT;
4019 	BDSET8W(&cdb[1], 0x01, 4, 5); /* RESERVE */
4020 	BDSET8W(&cdb[2], 0x00, 7, 4); /* LU_SCOPE */
4021 	BDADD8W(&cdb[2], 0x03, 3, 4); /* ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS */
4022 	cdb[3] = 0;
4023 	cdb[4] = 0;
4024 	DSET32(&cdb[5], parameter_len);
4025 	cdb[9] = 0;
4026 	lu_cmd2.cdb = &cdb[0];
4027 
4028 	memset(&PRO_data, 0, sizeof PRO_data);
4029 	DSET64(&PRO_data[0], rkey); // RESERVATION KEY
4030 	DSET64(&PRO_data[8], 0);
4031 
4032 	rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2,
4033 	    0x01, 0x00, 0x03,
4034 	    PRO_data, parameter_len);
4035 	if (rc < 0) {
4036 		lu_cmd->status = lu_cmd2.status;
4037 		if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) {
4038 			return -1;
4039 		}
4040 		/* INTERNAL TARGET FAILURE */
4041 		BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00);
4042 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
4043 		return -1;
4044 	}
4045 
4046 	lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
4047 	return 0;
4048 }
4049 
4050 static int
istgt_lu_disk_lbread(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint64_t lba,uint32_t len)4051 istgt_lu_disk_lbread(ISTGT_LU_DISK *spec, CONN_Ptr conn __attribute__((__unused__)), ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len)
4052 {
4053 	uint8_t *data;
4054 	uint64_t maxlba;
4055 	uint64_t llen;
4056 	uint64_t blen;
4057 	uint64_t offset;
4058 	uint64_t nbytes;
4059 	int64_t rc;
4060 
4061 	if (len == 0) {
4062 		lu_cmd->data = NULL;
4063 		lu_cmd->data_len = 0;
4064 		return 0;
4065 	}
4066 
4067 	maxlba = spec->blockcnt;
4068 	llen = (uint64_t) len;
4069 	blen = spec->blocklen;
4070 	offset = lba * blen;
4071 	nbytes = llen * blen;
4072 
4073 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
4074 	    "Read: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
4075 	    maxlba, lba, len);
4076 
4077 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
4078 		ISTGT_ERRLOG("end of media\n");
4079 		return -1;
4080 	}
4081 
4082 	if (nbytes > lu_cmd->iobufsize) {
4083 		ISTGT_ERRLOG("nbytes(%zu) > iobufsize(%zu)\n",
4084 		    (size_t) nbytes, lu_cmd->iobufsize);
4085 		return -1;
4086 	}
4087 	data = lu_cmd->iobuf;
4088 
4089 	rc = spec->seek(spec, offset);
4090 	if (rc < 0) {
4091 		ISTGT_ERRLOG("lu_disk_seek() failed\n");
4092 		return -1;
4093 	}
4094 
4095 	rc = spec->read(spec, data, nbytes);
4096 	if (rc < 0) {
4097 		ISTGT_ERRLOG("lu_disk_read() failed\n");
4098 		return -1;
4099 	}
4100 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Read %"PRId64"/%"PRIu64" bytes\n",
4101 	    rc, nbytes);
4102 
4103 	lu_cmd->data = data;
4104 	lu_cmd->data_len = rc;
4105 
4106 	return 0;
4107 }
4108 
4109 static int
istgt_lu_disk_lbwrite(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint64_t lba,uint32_t len)4110 istgt_lu_disk_lbwrite(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len)
4111 {
4112 	uint8_t *data;
4113 	uint64_t maxlba;
4114 	uint64_t llen;
4115 	uint64_t blen;
4116 	uint64_t offset;
4117 	uint64_t nbytes;
4118 	int64_t rc;
4119 
4120 	if (len == 0) {
4121 		lu_cmd->data_len = 0;
4122 		return 0;
4123 	}
4124 
4125 	maxlba = spec->blockcnt;
4126 	llen = (uint64_t) len;
4127 	blen = spec->blocklen;
4128 	offset = lba * blen;
4129 	nbytes = llen * blen;
4130 
4131 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
4132 	    "Write: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
4133 	    maxlba, lba, len);
4134 
4135 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
4136 		ISTGT_ERRLOG("end of media\n");
4137 		return -1;
4138 	}
4139 
4140 	if (nbytes > lu_cmd->iobufsize) {
4141 		ISTGT_ERRLOG("nbytes(%zu) > iobufsize(%zu)\n",
4142 		    (size_t) nbytes, lu_cmd->iobufsize);
4143 		return -1;
4144 	}
4145 	data = lu_cmd->iobuf;
4146 
4147 	rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
4148 	    lu_cmd->iobufsize, nbytes);
4149 	if (rc < 0) {
4150 		ISTGT_ERRLOG("lu_disk_transfer_data() failed\n");
4151 		return -1;
4152 	}
4153 
4154 	if (spec->lu->readonly) {
4155 		ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num);
4156 		return -1;
4157 	}
4158 
4159 	spec->req_write_cache = 0;
4160 	rc = spec->seek(spec, offset);
4161 	if (rc < 0) {
4162 		ISTGT_ERRLOG("lu_disk_seek() failed\n");
4163 		return -1;
4164 	}
4165 
4166 	rc = spec->write(spec, data, nbytes);
4167 	if (rc < 0 || (uint64_t) rc != nbytes) {
4168 		ISTGT_ERRLOG("lu_disk_write() failed\n");
4169 		return -1;
4170 	}
4171 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n",
4172 	    rc, nbytes);
4173 
4174 	lu_cmd->data_len = rc;
4175 
4176 	return 0;
4177 }
4178 
4179 static int
istgt_lu_disk_lbwrite_same(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint64_t lba,uint32_t len)4180 istgt_lu_disk_lbwrite_same(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len)
4181 {
4182 	uint8_t *data;
4183 	uint64_t maxlba;
4184 	uint64_t llen;
4185 	uint64_t blen;
4186 	uint64_t offset;
4187 	uint64_t nbytes;
4188 	uint64_t nblocks;
4189 	uint64_t wblocks;
4190 	int64_t rc;
4191 
4192 	maxlba = spec->blockcnt;
4193 	llen = (uint64_t) len;
4194 	if (llen == 0) {
4195 		if (lba >= maxlba) {
4196 			ISTGT_ERRLOG("end of media\n");
4197 			return -1;
4198 		}
4199 		llen = maxlba - lba;
4200 	}
4201 	blen = spec->blocklen;
4202 	offset = lba * blen;
4203 	nbytes = 1 * blen;
4204 
4205 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
4206 	    "Write Same: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
4207 	    maxlba, lba, len);
4208 
4209 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
4210 		ISTGT_ERRLOG("end of media\n");
4211 		return -1;
4212 	}
4213 
4214 	if (nbytes > lu_cmd->iobufsize) {
4215 		ISTGT_ERRLOG("nbytes(%zu) > iobufsize(%zu)\n",
4216 		    (size_t) nbytes, lu_cmd->iobufsize);
4217 		return -1;
4218 	}
4219 	data = lu_cmd->iobuf;
4220 
4221 	rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
4222 	    lu_cmd->iobufsize, nbytes);
4223 	if (rc < 0) {
4224 		ISTGT_ERRLOG("lu_disk_transfer_data() failed\n");
4225 		return -1;
4226 	}
4227 
4228 	if (spec->lu->readonly) {
4229 		ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num);
4230 		return -1;
4231 	}
4232 
4233 	if (conn->workbuf == NULL) {
4234 		conn->worksize = ISTGT_LU_WORK_BLOCK_SIZE;
4235 		conn->workbuf = xmalloc(conn->worksize);
4236 	}
4237 	wblocks = (int64_t)conn->worksize / nbytes;
4238 	if (wblocks == 0) {
4239 		ISTGT_ERRLOG("work buffer is too small\n");
4240 		return -1;
4241 	}
4242 
4243 	spec->req_write_cache = 0;
4244 	rc = spec->seek(spec, offset);
4245 	if (rc < 0) {
4246 		ISTGT_ERRLOG("lu_disk_seek() failed\n");
4247 		return -1;
4248 	}
4249 
4250 #if 0
4251 	nblocks = 0;
4252 	while (nblocks < llen) {
4253 		rc = spec->write(spec, data, nbytes);
4254 		if (rc < 0 || rc != nbytes) {
4255 			ISTGT_ERRLOG("lu_disk_write() failed\n");
4256 			return -1;
4257 		}
4258 		nblocks++;
4259 	}
4260 #else
4261 	nblocks = 0;
4262 	while (nblocks < wblocks) {
4263 		memcpy(conn->workbuf + (nblocks * nbytes), data, nbytes);
4264 		nblocks++;
4265 	}
4266 
4267 	nblocks = 0;
4268 	while (nblocks < llen) {
4269 		uint64_t reqblocks = DMIN64(wblocks, (llen - nblocks));
4270 		rc = spec->write(spec, conn->workbuf, (reqblocks * nbytes));
4271 		if (rc < 0 || (uint64_t) rc != (reqblocks * nbytes)) {
4272 			ISTGT_ERRLOG("lu_disk_write() failed\n");
4273 			return -1;
4274 		}
4275 		nblocks += reqblocks;
4276 	}
4277 #endif
4278 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n",
4279 	    (nblocks * nbytes), (llen * nbytes));
4280 
4281 	lu_cmd->data_len = nbytes;
4282 
4283 	return 0;
4284 }
4285 
4286 static int
istgt_lu_disk_lbwrite_ats(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint64_t lba,uint32_t len)4287 istgt_lu_disk_lbwrite_ats(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len)
4288 {
4289 	uint8_t *data;
4290 	uint64_t maxlba;
4291 	uint64_t llen;
4292 	uint64_t blen;
4293 	uint64_t offset;
4294 	uint64_t nbytes;
4295 	int64_t rc;
4296 	uint8_t *sense_data;
4297 	size_t *sense_len;
4298 
4299 	if (len == 0) {
4300 		lu_cmd->data_len = 0;
4301 		return 0;
4302 	}
4303 
4304 	sense_data = lu_cmd->sense_data;
4305 	sense_len = &lu_cmd->sense_data_len;
4306 	*sense_len = 0;
4307 
4308 	maxlba = spec->blockcnt;
4309 	llen = (uint64_t) len;
4310 	blen = spec->blocklen;
4311 	offset = lba * blen;
4312 	nbytes = llen * blen;
4313 
4314 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
4315 	    "Write ATS: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
4316 	    maxlba, lba, len);
4317 
4318 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
4319 		ISTGT_ERRLOG("end of media\n");
4320 		return -1;
4321 	}
4322 
4323 	if (nbytes > lu_cmd->iobufsize) {
4324 		ISTGT_ERRLOG("nbytes(%zu) > iobufsize(%zu)\n",
4325 		    (size_t) nbytes, lu_cmd->iobufsize);
4326 		return -1;
4327 	}
4328 	data = lu_cmd->iobuf;
4329 
4330 	rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
4331 	    lu_cmd->iobufsize, nbytes * 2);
4332 	if (rc < 0) {
4333 		ISTGT_ERRLOG("lu_disk_transfer_data() failed\n");
4334 		return -1;
4335 	}
4336 
4337 	if (spec->lu->readonly) {
4338 		ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num);
4339 		return -1;
4340 	}
4341 
4342 	if (spec->watsbuf == NULL) {
4343 		spec->watssize = ISTGT_LU_WORK_ATS_BLOCK_SIZE;
4344 		spec->watsbuf = xmalloc(spec->watssize);
4345 	}
4346 	if (nbytes > (uint64_t) spec->watssize) {
4347 		ISTGT_ERRLOG("nbytes(%zu) > watssize(%zu)\n",
4348 		    (size_t) nbytes, (size_t) spec->watssize);
4349 		return -1;
4350 	}
4351 
4352 	spec->req_write_cache = 0;
4353 	/* start atomic test and set */
4354 	MTX_LOCK(&spec->ats_mutex);
4355 
4356 	rc = spec->seek(spec, offset);
4357 	if (rc < 0) {
4358 		MTX_UNLOCK(&spec->ats_mutex);
4359 		ISTGT_ERRLOG("lu_disk_seek() failed\n");
4360 		return -1;
4361 	}
4362 
4363 	rc = spec->read(spec, spec->watsbuf, nbytes);
4364 	if (rc < 0 || (uint64_t) rc != nbytes) {
4365 		MTX_UNLOCK(&spec->ats_mutex);
4366 		ISTGT_ERRLOG("lu_disk_read() failed\n");
4367 		return -1;
4368 	}
4369 
4370 #if 0
4371 	ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "ATS VERIFY", data, nbytes);
4372 	ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "ATS WRITE", data + nbytes, nbytes);
4373 	ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "ATS DATA", spec->watsbuf, nbytes);
4374 #endif
4375 	if (memcmp(spec->watsbuf, data, nbytes) != 0) {
4376 		MTX_UNLOCK(&spec->ats_mutex);
4377 		//ISTGT_ERRLOG("compare failed\n");
4378 		/* MISCOMPARE DURING VERIFY OPERATION */
4379 		BUILD_SENSE(MISCOMPARE, 0x1d, 0x00);
4380 		return -1;
4381 	}
4382 
4383 	rc = spec->seek(spec, offset);
4384 	if (rc < 0) {
4385 		MTX_UNLOCK(&spec->ats_mutex);
4386 		ISTGT_ERRLOG("lu_disk_seek() failed\n");
4387 		return -1;
4388 	}
4389 	rc = spec->write(spec, data + nbytes, nbytes);
4390 	if (rc < 0 || (uint64_t) rc != nbytes) {
4391 		MTX_UNLOCK(&spec->ats_mutex);
4392 		ISTGT_ERRLOG("lu_disk_write() failed\n");
4393 		return -1;
4394 	}
4395 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n",
4396 	    rc, nbytes);
4397 
4398 	MTX_UNLOCK(&spec->ats_mutex);
4399 	/* end atomic test and set */
4400 
4401 	lu_cmd->data_len = nbytes * 2;
4402 
4403 	return 0;
4404 }
4405 
4406 static int
istgt_lu_disk_lbsync(ISTGT_LU_DISK * spec,CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd,uint64_t lba,uint32_t len)4407 istgt_lu_disk_lbsync(ISTGT_LU_DISK *spec, CONN_Ptr conn __attribute__((__unused__)), ISTGT_LU_CMD_Ptr lu_cmd __attribute__((__unused__)), uint64_t lba, uint32_t len)
4408 {
4409 	uint64_t maxlba;
4410 	uint64_t llen;
4411 	uint64_t blen;
4412 	uint64_t offset;
4413 	uint64_t nbytes;
4414 	int64_t rc;
4415 
4416 	maxlba = spec->blockcnt;
4417 	if (len == 0 && lba < maxlba) {
4418 		llen = maxlba - lba;
4419 	} else {
4420 		llen = (uint64_t) len;
4421 	}
4422 	blen = spec->blocklen;
4423 	offset = lba * blen;
4424 	nbytes = llen * blen;
4425 
4426 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
4427 	    "Sync: max=%"PRIu64", lba=%"PRIu64", len=%u\n",
4428 	    maxlba, lba, len);
4429 
4430 	if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) {
4431 		ISTGT_ERRLOG("end of media\n");
4432 		return -1;
4433 	}
4434 
4435 	rc = spec->sync(spec, offset, nbytes);
4436 	if (rc < 0) {
4437 		ISTGT_ERRLOG("lu_disk_sync() failed\n");
4438 		return -1;
4439 	}
4440 
4441 	return 0;
4442 }
4443 
4444 int
istgt_lu_scsi_build_sense_data(uint8_t * data,int sk,int asc,int ascq)4445 istgt_lu_scsi_build_sense_data(uint8_t *data, int sk, int asc, int ascq)
4446 {
4447 	uint8_t *cp;
4448 	int resp_code;
4449 	int hlen = 0, len = 0, plen;
4450 	int total;
4451 
4452 	resp_code = 0x70; /* Current + Fixed format */
4453 
4454 	/* SenseLength */
4455 	DSET16(&data[0], 0);
4456 	hlen = 2;
4457 
4458 	/* Sense Data */
4459 	cp = &data[hlen + len];
4460 
4461 	/* VALID(7) RESPONSE CODE(6-0) */
4462 	BDSET8(&cp[0], 1, 7);
4463 	BDADD8W(&cp[0], resp_code, 6, 7);
4464 	/* Obsolete */
4465 	cp[1] = 0;
4466 	/* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
4467 	BDSET8W(&cp[2], sk, 3, 4);
4468 	/* INFORMATION */
4469 	memset(&cp[3], 0, 4);
4470 	/* ADDITIONAL SENSE LENGTH */
4471 	cp[7] = 0;
4472 	len = 8;
4473 
4474 	/* COMMAND-SPECIFIC INFORMATION */
4475 	memset(&cp[8], 0, 4);
4476 	/* ADDITIONAL SENSE CODE */
4477 	cp[12] = asc;
4478 	/* ADDITIONAL SENSE CODE QUALIFIER */
4479 	cp[13] = ascq;
4480 	/* FIELD REPLACEABLE UNIT CODE */
4481 	cp[14] = 0;
4482 	/* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
4483 	cp[15] = 0;
4484 	cp[16] = 0;
4485 	cp[17] = 0;
4486 	/* Additional sense bytes */
4487 	//data[18] = 0;
4488 	plen = 18 - len;
4489 
4490 	/* ADDITIONAL SENSE LENGTH */
4491 	cp[7] = plen;
4492 
4493 	total = hlen + len + plen;
4494 
4495 	/* SenseLength */
4496 	DSET16(&data[0], total - 2);
4497 
4498 	return total;
4499 }
4500 
4501 static int
istgt_lu_disk_build_sense_data(ISTGT_LU_DISK * spec,uint8_t * data,int sk,int asc,int ascq)4502 istgt_lu_disk_build_sense_data(ISTGT_LU_DISK *spec __attribute__((__unused__)), uint8_t *data, int sk, int asc, int ascq)
4503 {
4504 	int rc;
4505 
4506 	rc = istgt_lu_scsi_build_sense_data(data, sk, asc, ascq);
4507 	if (rc < 0) {
4508 		return -1;
4509 	}
4510 	return rc;
4511 }
4512 
4513 int
istgt_lu_scsi_build_sense_data2(uint8_t * data,int sk,int asc,int ascq)4514 istgt_lu_scsi_build_sense_data2(uint8_t *data, int sk, int asc, int ascq)
4515 {
4516 	uint8_t *cp;
4517 	int resp_code;
4518 	int hlen = 0, len = 0, plen;
4519 	int total;
4520 
4521 	resp_code = 0x71; /* Deferred + Fixed format */
4522 
4523 	/* SenseLength */
4524 	DSET16(&data[0], 0);
4525 	hlen = 2;
4526 
4527 	/* Sense Data */
4528 	cp = &data[hlen + len];
4529 
4530 	/* VALID(7) RESPONSE CODE(6-0) */
4531 	BDSET8(&cp[0], 1, 7);
4532 	BDADD8W(&cp[0], resp_code, 6, 7);
4533 	/* Obsolete */
4534 	cp[1] = 0;
4535 	/* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
4536 	BDSET8W(&cp[2], sk, 3, 4);
4537 	/* INFORMATION */
4538 	memset(&cp[3], 0, 4);
4539 	/* ADDITIONAL SENSE LENGTH */
4540 	cp[7] = 0;
4541 	len = 8;
4542 
4543 	/* COMMAND-SPECIFIC INFORMATION */
4544 	memset(&cp[8], 0, 4);
4545 	/* ADDITIONAL SENSE CODE */
4546 	cp[12] = asc;
4547 	/* ADDITIONAL SENSE CODE QUALIFIER */
4548 	cp[13] = ascq;
4549 	/* FIELD REPLACEABLE UNIT CODE */
4550 	cp[14] = 0;
4551 	/* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
4552 	cp[15] = 0;
4553 	cp[16] = 0;
4554 	cp[17] = 0;
4555 	/* Additional sense bytes */
4556 	//data[18] = 0;
4557 	plen = 18 - len;
4558 
4559 	/* ADDITIONAL SENSE LENGTH */
4560 	cp[7] = plen;
4561 
4562 	total = hlen + len + plen;
4563 
4564 	/* SenseLength */
4565 	DSET16(&data[0], total - 2);
4566 
4567 	return total;
4568 }
4569 
4570 static int
istgt_lu_disk_build_sense_data2(ISTGT_LU_DISK * spec,uint8_t * data,int sk,int asc,int ascq)4571 istgt_lu_disk_build_sense_data2(ISTGT_LU_DISK *spec __attribute__((__unused__)), uint8_t *data, int sk, int asc, int ascq)
4572 {
4573 	int rc;
4574 
4575 	rc = istgt_lu_scsi_build_sense_data2(data, sk, asc, ascq);
4576 	if (rc < 0) {
4577 		return -1;
4578 	}
4579 	return rc;
4580 }
4581 
4582 int
istgt_lu_disk_reset(ISTGT_LU_Ptr lu,int lun)4583 istgt_lu_disk_reset(ISTGT_LU_Ptr lu, int lun)
4584 {
4585 	ISTGT_LU_DISK *spec;
4586 	int flags;
4587 	int rc;
4588 
4589 	if (lu == NULL) {
4590 		return -1;
4591 	}
4592 	if (lun >= lu->maxlun) {
4593 		return -1;
4594 	}
4595 	if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) {
4596 		return -1;
4597 	}
4598 	if (lu->lun[lun].type != ISTGT_LU_LUN_TYPE_STORAGE) {
4599 		return -1;
4600 	}
4601 	spec = (ISTGT_LU_DISK *) lu->lun[lun].spec;
4602 
4603 #if 0
4604 	if (spec->lock) {
4605 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "unlock by reset\n");
4606 		spec->lock = 0;
4607 	}
4608 #endif
4609 
4610 	if (lu->queue_depth != 0) {
4611 		rc = istgt_lu_disk_queue_clear_all(lu, lun);
4612 		if (rc < 0) {
4613 			ISTGT_ERRLOG("lu_disk_queue_clear_all() failed\n");
4614 			return -1;
4615 		}
4616 	}
4617 
4618 	/* re-open file */
4619 	if (!spec->lu->readonly) {
4620 		rc = spec->sync(spec, 0, spec->size);
4621 		if (rc < 0) {
4622 			ISTGT_ERRLOG("LU%d: LUN%d: lu_disk_sync() failed\n",
4623 			    lu->num, lun);
4624 			/* ignore error */
4625 		}
4626 	}
4627 	rc = spec->close(spec);
4628 	if (rc < 0) {
4629 		ISTGT_ERRLOG("LU%d: LUN%d: lu_disk_close() failed\n",
4630 		    lu->num, lun);
4631 		/* ignore error */
4632 	}
4633 	flags = lu->readonly ? O_RDONLY : O_RDWR;
4634 	rc = spec->open(spec, flags, 0666);
4635 	if (rc < 0) {
4636 		ISTGT_ERRLOG("LU%d: LUN%d: lu_disk_open() failed\n",
4637 		    lu->num, lun);
4638 		return -1;
4639 	}
4640 
4641 	return 0;
4642 }
4643 
4644 static int
istgt_lu_disk_queue_clear_internal(ISTGT_LU_DISK * spec,const char * initiator_port,int all_cmds,uint32_t CmdSN)4645 istgt_lu_disk_queue_clear_internal(ISTGT_LU_DISK *spec, const char *initiator_port, int all_cmds, uint32_t CmdSN)
4646 {
4647 	ISTGT_LU_TASK_Ptr lu_task;
4648 	ISTGT_QUEUE saved_queue;
4649 	time_t now;
4650 	int rc;
4651 
4652 	if (spec == NULL)
4653 		return -1;
4654 
4655 	if (all_cmds != 0) {
4656 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by port=%s\n",
4657 		    initiator_port);
4658 	} else {
4659 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by port=%s, CmdSN=%u\n",
4660 		    initiator_port, CmdSN);
4661 	}
4662 
4663 	istgt_queue_init(&saved_queue);
4664 
4665 	now = time(NULL);
4666 	MTX_LOCK(&spec->cmd_queue_mutex);
4667 	while (1) {
4668 		lu_task = istgt_queue_dequeue(&spec->cmd_queue);
4669 		if (lu_task == NULL)
4670 			break;
4671 		if (((all_cmds != 0) || (lu_task->lu_cmd.CmdSN == CmdSN))
4672 		    && (strcasecmp(lu_task->initiator_port,
4673 			    initiator_port) == 0)) {
4674 			ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%lu cleared\n",
4675 			    lu_task->lu_cmd.CmdSN,
4676 			    lu_task->lu_cmd.cdb[0],
4677 			    (unsigned long) (now - lu_task->create_time));
4678 			rc = istgt_lu_destroy_task(lu_task);
4679 			if (rc < 0) {
4680 				MTX_UNLOCK(&spec->cmd_queue_mutex);
4681 				ISTGT_ERRLOG("lu_destory_task() failed\n");
4682 				goto error_return;
4683 			}
4684 			continue;
4685 		}
4686 		rc = istgt_queue_enqueue(&saved_queue, lu_task);
4687 		if (rc < 0) {
4688 			MTX_UNLOCK(&spec->cmd_queue_mutex);
4689 			ISTGT_ERRLOG("queue_enqueue() failed\n");
4690 			goto error_return;
4691 		}
4692 	}
4693 	while (1) {
4694 		lu_task = istgt_queue_dequeue(&saved_queue);
4695 		if (lu_task == NULL)
4696 			break;
4697 		rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task);
4698 		if (rc < 0) {
4699 			MTX_UNLOCK(&spec->cmd_queue_mutex);
4700 			ISTGT_ERRLOG("queue_enqueue() failed\n");
4701 			goto error_return;
4702 		}
4703 	}
4704 	MTX_UNLOCK(&spec->cmd_queue_mutex);
4705 
4706 	/* check wait task */
4707 	MTX_LOCK(&spec->wait_lu_task_mutex);
4708 	lu_task = spec->wait_lu_task;
4709 	if (lu_task != NULL) {
4710 		if (((all_cmds != 0) || (lu_task->lu_cmd.CmdSN == CmdSN))
4711 		    && (strcasecmp(lu_task->initiator_port,
4712 			    initiator_port) == 0)) {
4713 			/* conn had gone? */
4714 			rc = pthread_mutex_trylock(&lu_task->trans_mutex);
4715 			if (rc == 0) {
4716 				ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%lu aborted\n",
4717 				    lu_task->lu_cmd.CmdSN,
4718 				    lu_task->lu_cmd.cdb[0],
4719 				    (unsigned long) (now - lu_task->create_time));
4720 				/* force error */
4721 				lu_task->error = 1;
4722 				lu_task->abort = 1;
4723 				rc = pthread_cond_broadcast(&lu_task->trans_cond);
4724 				if (rc != 0) {
4725 					/* ignore error */
4726 				}
4727 				MTX_UNLOCK(&lu_task->trans_mutex);
4728 			}
4729 		}
4730 	}
4731 	MTX_UNLOCK(&spec->wait_lu_task_mutex);
4732 
4733 	rc = istgt_queue_count(&saved_queue);
4734 	if (rc != 0) {
4735 		ISTGT_ERRLOG("temporary queue is not empty\n");
4736 		goto error_return;
4737 	}
4738 
4739 	istgt_queue_destroy(&saved_queue);
4740 	return 0;
4741 
4742  error_return:
4743 	istgt_queue_destroy(&saved_queue);
4744 	return -1;
4745 }
4746 
4747 static int
istgt_lu_disk_queue_abort_ITL(ISTGT_LU_DISK * spec,const char * initiator_port)4748 istgt_lu_disk_queue_abort_ITL(ISTGT_LU_DISK *spec, const char *initiator_port)
4749 {
4750 	int rc;
4751 
4752 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue abort by port=%s\n",
4753 	    initiator_port);
4754 
4755 	rc = istgt_lu_disk_queue_clear_internal(spec, initiator_port,
4756 	    1, 0U); /* ALL, CmdSN=0 */
4757 	return rc;
4758 }
4759 
4760 int
istgt_lu_disk_queue_clear_IT(CONN_Ptr conn,ISTGT_LU_Ptr lu)4761 istgt_lu_disk_queue_clear_IT(CONN_Ptr conn, ISTGT_LU_Ptr lu)
4762 {
4763 	ISTGT_LU_DISK *spec;
4764 	int rc;
4765 	int i;
4766 
4767 	if (lu == NULL)
4768 		return -1;
4769 
4770 	for (i = 0; i < lu->maxlun; i++) {
4771 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
4772 #if 0
4773 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n",
4774 						   lu->num, i);
4775 #endif
4776 			continue;
4777 		}
4778 		if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) {
4779 			ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num);
4780 			return -1;
4781 		}
4782 		spec = (ISTGT_LU_DISK *) lu->lun[i].spec;
4783 		if (spec == NULL) {
4784 			continue;
4785 		}
4786 
4787 		rc = istgt_lu_disk_queue_clear_ITL(conn, lu, i);
4788 		if (rc < 0) {
4789 			return -1;
4790 		}
4791 	}
4792 
4793 	return 0;
4794 }
4795 
4796 int
istgt_lu_disk_queue_clear_ITL(CONN_Ptr conn,ISTGT_LU_Ptr lu,int lun)4797 istgt_lu_disk_queue_clear_ITL(CONN_Ptr conn, ISTGT_LU_Ptr lu, int lun)
4798 {
4799 	ISTGT_LU_DISK *spec;
4800 	int rc;
4801 
4802 	if (lu == NULL)
4803 		return -1;
4804 	if (lun >= lu->maxlun)
4805 		return -1;
4806 
4807 	spec = (ISTGT_LU_DISK *) lu->lun[lun].spec;
4808 	if (spec == NULL)
4809 		return -1;
4810 
4811 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by name=%s, port=%s\n",
4812 	    conn->initiator_name, conn->initiator_port);
4813 
4814 	rc = istgt_lu_disk_queue_clear_internal(spec, conn->initiator_port,
4815 	    1, 0U); /* ALL, CmdSN=0 */
4816 	return rc;
4817 }
4818 
4819 int
istgt_lu_disk_queue_clear_ITLQ(CONN_Ptr conn,ISTGT_LU_Ptr lu,int lun,uint32_t CmdSN)4820 istgt_lu_disk_queue_clear_ITLQ(CONN_Ptr conn, ISTGT_LU_Ptr lu, int lun, uint32_t CmdSN)
4821 {
4822 	ISTGT_LU_DISK *spec;
4823 	int rc;
4824 
4825 	if (lu == NULL)
4826 		return -1;
4827 	if (lun >= lu->maxlun)
4828 		return -1;
4829 
4830 	spec = (ISTGT_LU_DISK *) lu->lun[lun].spec;
4831 	if (spec == NULL)
4832 		return -1;
4833 
4834 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by name=%s, port=%s\n",
4835 	    conn->initiator_name, conn->initiator_port);
4836 
4837 	rc = istgt_lu_disk_queue_clear_internal(spec, conn->initiator_port,
4838 	    0, CmdSN);
4839 	return rc;
4840 }
4841 
4842 int
istgt_lu_disk_queue_clear_all(ISTGT_LU_Ptr lu,int lun)4843 istgt_lu_disk_queue_clear_all(ISTGT_LU_Ptr lu, int lun)
4844 {
4845 	ISTGT_LU_TASK_Ptr lu_task;
4846 	ISTGT_LU_DISK *spec;
4847 	time_t now;
4848 	int rc;
4849 
4850 	if (lu == NULL)
4851 		return -1;
4852 	if (lun >= lu->maxlun)
4853 		return -1;
4854 
4855 	if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) {
4856 		return -1;
4857 	}
4858 	if (lu->lun[lun].type != ISTGT_LU_LUN_TYPE_STORAGE) {
4859 		return -1;
4860 	}
4861 	spec = (ISTGT_LU_DISK *) lu->lun[lun].spec;
4862 	if (spec == NULL)
4863 		return -1;
4864 
4865 	now = time(NULL);
4866 	MTX_LOCK(&spec->cmd_queue_mutex);
4867 	while (1) {
4868 		lu_task = istgt_queue_dequeue(&spec->cmd_queue);
4869 		if (lu_task == NULL)
4870 			break;
4871 		ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%lu cleared\n",
4872 		    lu_task->lu_cmd.CmdSN,
4873 		    lu_task->lu_cmd.cdb[0],
4874 		    (unsigned long) (now - lu_task->create_time));
4875 		rc = istgt_lu_destroy_task(lu_task);
4876 		if (rc < 0) {
4877 			MTX_UNLOCK(&spec->cmd_queue_mutex);
4878 			ISTGT_ERRLOG("lu_destory_task() failed\n");
4879 			return -1;
4880 		}
4881 	}
4882 	MTX_UNLOCK(&spec->cmd_queue_mutex);
4883 
4884 	/* check wait task */
4885 	MTX_LOCK(&spec->wait_lu_task_mutex);
4886 	lu_task = spec->wait_lu_task;
4887 	if (lu_task != NULL) {
4888 		/* conn had gone? */
4889 		rc = pthread_mutex_trylock(&lu_task->trans_mutex);
4890 		if (rc == 0) {
4891 			ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%lu aborted\n",
4892 			    lu_task->lu_cmd.CmdSN,
4893 			    lu_task->lu_cmd.cdb[0],
4894 			    (unsigned long) (now - lu_task->create_time));
4895 			/* force error */
4896 			lu_task->error = 1;
4897 			lu_task->abort = 1;
4898 			rc = pthread_cond_broadcast(&lu_task->trans_cond);
4899 			if (rc != 0) {
4900 				/* ignore error */
4901 			}
4902 			MTX_UNLOCK(&lu_task->trans_mutex);
4903 		}
4904 	}
4905 	MTX_UNLOCK(&spec->wait_lu_task_mutex);
4906 
4907 	MTX_LOCK(&spec->cmd_queue_mutex);
4908 	rc = istgt_queue_count(&spec->cmd_queue);
4909 	MTX_UNLOCK(&spec->cmd_queue_mutex);
4910 	if (rc != 0) {
4911 		ISTGT_ERRLOG("cmd queue is not empty\n");
4912 		return -1;
4913 	}
4914 
4915 	return 0;
4916 }
4917 
4918 int
istgt_lu_disk_queue(CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd)4919 istgt_lu_disk_queue(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd)
4920 {
4921 	ISTGT_LU_TASK_Ptr lu_task;
4922 	ISTGT_LU_Ptr lu;
4923 	ISTGT_LU_DISK *spec;
4924 	uint8_t *data;
4925 	uint8_t *cdb;
4926 	uint32_t allocation_len;
4927 	int data_len;
4928 	int data_alloc_len;
4929 	uint8_t *sense_data;
4930 	size_t *sense_len;
4931 	int lun_i;
4932 	int maxq;
4933 	int qcnt;
4934 	int rc;
4935 
4936 	if (lu_cmd == NULL)
4937 		return -1;
4938 	lu = lu_cmd->lu;
4939 	if (lu == NULL) {
4940 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
4941 		return -1;
4942 	}
4943 	spec = NULL;
4944 	cdb = lu_cmd->cdb;
4945 	data = lu_cmd->data;
4946 	data_alloc_len = lu_cmd->alloc_len;
4947 	sense_data = lu_cmd->sense_data;
4948 	sense_len = &lu_cmd->sense_data_len;
4949 	*sense_len = 0;
4950 
4951 	lun_i = istgt_lu_islun2lun(lu_cmd->lun);
4952 	if (lun_i >= lu->maxlun) {
4953 #ifdef ISTGT_TRACE_DISK
4954 		ISTGT_ERRLOG("LU%d: LUN%d invalid\n",
4955 		    lu->num, lun_i);
4956 #endif /* ISTGT_TRACE_DISK */
4957 		if (cdb[0] == SPC_INQUIRY) {
4958 			allocation_len = DGET16(&cdb[3]);
4959 			if (allocation_len > (size_t) data_alloc_len) {
4960 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
4961 				    data_alloc_len);
4962 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
4963 				return -1;
4964 			}
4965 			memset(data, 0, allocation_len);
4966 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
4967 			BDSET8W(&data[0], 0x03, 7, 3);
4968 			BDADD8W(&data[0], 0x1f, 4, 5);
4969 			data_len = 96;
4970 			memset(&data[1], 0, data_len - 1);
4971 			/* ADDITIONAL LENGTH */
4972 			data[4] = data_len - 5;
4973 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
4974 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
4975 			return ISTGT_LU_TASK_RESULT_IMMEDIATE;
4976 		} else {
4977 			/* LOGICAL UNIT NOT SUPPORTED */
4978 			BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00);
4979 			lu_cmd->data_len = 0;
4980 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
4981 			return ISTGT_LU_TASK_RESULT_IMMEDIATE;
4982 		}
4983 	}
4984 	spec = (ISTGT_LU_DISK *) lu->lun[lun_i].spec;
4985 	if (spec == NULL) {
4986 		/* LOGICAL UNIT NOT SUPPORTED */
4987 		BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00);
4988 		lu_cmd->data_len = 0;
4989 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
4990 		return ISTGT_LU_TASK_RESULT_IMMEDIATE;
4991 	}
4992 	/* ready to enqueue, spec is valid for LUN access */
4993 
4994 	/* allocate task and copy LU_CMD(PDU) */
4995 	lu_task = xmalloc(sizeof *lu_task);
4996 	memset(lu_task, 0, sizeof *lu_task);
4997 	rc = istgt_lu_create_task(conn, lu_cmd, lu_task, lun_i);
4998 	if (rc < 0) {
4999 		ISTGT_ERRLOG("lu_create_task() failed\n");
5000 		xfree(lu_task);
5001 		return -1;
5002 	}
5003 
5004 	/* enqueue SCSI command */
5005 	MTX_LOCK(&spec->cmd_queue_mutex);
5006 	rc = istgt_queue_count(&spec->cmd_queue);
5007 	maxq = spec->queue_depth * lu->istgt->MaxSessions;
5008 	if (rc > maxq) {
5009 		MTX_UNLOCK(&spec->cmd_queue_mutex);
5010 		lu_cmd->data_len = 0;
5011 		lu_cmd->status = ISTGT_SCSI_STATUS_TASK_SET_FULL;
5012 		rc = istgt_lu_destroy_task(lu_task);
5013 		if (rc < 0) {
5014 			ISTGT_ERRLOG("lu_destroy_task() failed\n");
5015 			return -1;
5016 		}
5017 		return ISTGT_LU_TASK_RESULT_QUEUE_FULL;
5018 	}
5019 	qcnt = rc;
5020 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
5021 	    "Queue(%d), CmdSN=%u, OP=0x%x, LUN=0x%16.16"PRIx64"\n",
5022 	    qcnt, lu_cmd->CmdSN, lu_cmd->cdb[0], lu_cmd->lun);
5023 
5024 	/* enqueue task to LUN */
5025 	switch (lu_cmd->Attr_bit) {
5026 	case 0x03: /* Head of Queue */
5027 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Head of Queue\n");
5028 		rc = istgt_queue_enqueue_first(&spec->cmd_queue, lu_task);
5029 		break;
5030 	case 0x00: /* Untagged */
5031 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Untagged\n");
5032 		rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task);
5033 		break;
5034 	case 0x01: /* Simple */
5035 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Simple\n");
5036 		rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task);
5037 		break;
5038 	case 0x02: /* Ordered */
5039 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Ordered\n");
5040 		rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task);
5041 		break;
5042 	case 0x04: /* ACA */
5043 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert ACA\n");
5044 		rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task);
5045 		break;
5046 	default: /* Reserved */
5047 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Reserved Attribute\n");
5048 		rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task);
5049 		break;
5050 	}
5051 	MTX_UNLOCK(&spec->cmd_queue_mutex);
5052 	if (rc < 0) {
5053 		ISTGT_ERRLOG("queue_enqueue() failed\n");
5054 	error_return:
5055 		rc = istgt_lu_destroy_task(lu_task);
5056 		if (rc < 0) {
5057 			ISTGT_ERRLOG("lu_destroy_task() failed\n");
5058 			return -1;
5059 		}
5060 		return -1;
5061 	}
5062 
5063 	/* notify LUN thread */
5064 	MTX_LOCK(&lu->queue_mutex);
5065 	lu->queue_check = 1;
5066 	rc = pthread_cond_broadcast(&lu->queue_cond);
5067 	MTX_UNLOCK(&lu->queue_mutex);
5068 	if (rc != 0) {
5069 		ISTGT_ERRLOG("LU%d: cond_broadcast() failed\n", lu->num);
5070 		goto error_return;
5071 	}
5072 
5073 	return ISTGT_LU_TASK_RESULT_QUEUE_OK;
5074 }
5075 
5076 int
istgt_lu_disk_queue_count(ISTGT_LU_Ptr lu,int * lun)5077 istgt_lu_disk_queue_count(ISTGT_LU_Ptr lu, int *lun)
5078 {
5079 	ISTGT_LU_DISK *spec;
5080 	int qcnt;
5081 	int luns;
5082 	int i;
5083 
5084 	if (lun == NULL)
5085 		return -1;
5086 
5087 	i = *lun;
5088 	if (i >= lu->maxlun) {
5089 		*lun = 0;
5090 		i = 0;
5091 	}
5092 
5093 	qcnt = 0;
5094 	for (luns = lu->maxlun; luns >= 0 ; luns--) {
5095 		if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) {
5096 			goto next_lun;
5097 		}
5098 		if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) {
5099 			ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num);
5100 			goto next_lun;
5101 		}
5102 		spec = (ISTGT_LU_DISK *) lu->lun[i].spec;
5103 		if (spec == NULL) {
5104 			goto next_lun;
5105 		}
5106 
5107 		MTX_LOCK(&spec->cmd_queue_mutex);
5108 		qcnt = istgt_queue_count(&spec->cmd_queue);
5109 		MTX_UNLOCK(&spec->cmd_queue_mutex);
5110 		if (qcnt > 0) {
5111 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
5112 			    "LU%d: LUN%d queue(%d)\n",
5113 			    lu->num, i, qcnt);
5114 			*lun = spec->lun;
5115 			break;
5116 		}
5117 
5118 	next_lun:
5119 		i++;
5120 		if (i >= lu->maxlun) {
5121 			i = 0;
5122 		}
5123 	}
5124 	return qcnt;
5125 }
5126 
5127 int
istgt_lu_disk_queue_start(ISTGT_LU_Ptr lu,int lun)5128 istgt_lu_disk_queue_start(ISTGT_LU_Ptr lu, int lun)
5129 {
5130 	ISTGT_Ptr istgt;
5131 	ISTGT_LU_DISK *spec;
5132 	ISTGT_LU_TASK_Ptr lu_task;
5133 	CONN_Ptr conn;
5134 	ISTGT_LU_CMD_Ptr lu_cmd;
5135 	struct timespec abstime;
5136 	time_t start, now;
5137 	uint8_t *iobuf;
5138 	char tmp[1];
5139 	int abort_task = 0;
5140 	int rc;
5141 
5142 	if (lun < 0 || lun >= lu->maxlun) {
5143 		return -1;
5144 	}
5145 
5146 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LU%d: LUN%d queue start\n",
5147 	    lu->num, lun);
5148 	spec = (ISTGT_LU_DISK *) lu->lun[lun].spec;
5149 	if (spec == NULL)
5150 		return -1;
5151 
5152 	MTX_LOCK(&spec->cmd_queue_mutex);
5153 	lu_task = istgt_queue_dequeue(&spec->cmd_queue);
5154 	MTX_UNLOCK(&spec->cmd_queue_mutex);
5155 	if (lu_task == NULL) {
5156 		/* cleared or empty queue */
5157 		return 0;
5158 	}
5159 	lu_task->thread = pthread_self();
5160 	conn = lu_task->conn;
5161 	istgt = conn->istgt;
5162 	lu_cmd = &lu_task->lu_cmd;
5163 
5164 	/* XXX need pre-allocate? */
5165 #if 0
5166 	/* allocated in istgt_lu_create_task() */
5167 	lu_task->data = xmalloc(lu_cmd->alloc_len);
5168 	lu_task->sense_data = xmalloc(lu_cmd->sense_alloc_len);
5169 	lu_task->iobuf = NULL;
5170 #endif
5171 	lu_cmd->data = lu_task->data;
5172 	lu_cmd->data_len = 0;
5173 	lu_cmd->sense_data = lu_task->sense_data;
5174 	lu_cmd->sense_data_len = 0;
5175 
5176 	tmp[0] = 'Q';
5177 	if (lu_cmd->W_bit) {
5178 		if (lu_cmd->pdu->data_segment_len >= lu_cmd->transfer_len) {
5179 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
5180 			    "LU%d: LUN%d Task Write Immediate Start\n",
5181 			    lu->num, lun);
5182 #if 0
5183 			iobuf = xmalloc(lu_cmd->pdu->data_segment_len);
5184 			memcpy(iobuf, lu_cmd->pdu->data,
5185 			    lu_cmd->pdu->data_segment_len);
5186 			lu_task->iobuf = iobuf;
5187 #else
5188 			iobuf = lu_cmd->pdu->data;
5189 			lu_task->dup_iobuf = 1;
5190 #endif
5191 			lu_cmd->iobuf = iobuf;
5192 
5193 			MTX_LOCK(&lu_cmd->lu->mutex);
5194 			rc = istgt_lu_disk_execute(conn, lu_cmd);
5195 			MTX_UNLOCK(&lu_cmd->lu->mutex);
5196 			if (rc < 0) {
5197 				ISTGT_ERRLOG("lu_disk_execute() failed\n");
5198 			error_return:
5199 				rc = istgt_lu_destroy_task(lu_task);
5200 				if (rc < 0) {
5201 					ISTGT_ERRLOG("lu_destroy_task() failed\n");
5202 					return -1;
5203 				}
5204 				return -1;
5205 			}
5206 			lu_task->execute = 1;
5207 
5208 			/* response */
5209 			if (conn->use_sender == 0) {
5210 				MTX_LOCK(&conn->task_queue_mutex);
5211 				rc = istgt_queue_enqueue(&conn->task_queue, lu_task);
5212 				MTX_UNLOCK(&conn->task_queue_mutex);
5213 				if (rc < 0) {
5214 					ISTGT_ERRLOG("queue_enqueue() failed\n");
5215 					goto error_return;
5216 				}
5217 				rc = write(conn->task_pipe[1], tmp, 1);
5218 				if(rc < 0 || rc != 1) {
5219 					ISTGT_ERRLOG("write() failed\n");
5220 					goto error_return;
5221 				}
5222 			} else {
5223 				MTX_LOCK(&conn->result_queue_mutex);
5224 				rc = istgt_queue_enqueue(&conn->result_queue, lu_task);
5225 				if (rc < 0) {
5226 					MTX_UNLOCK(&conn->result_queue_mutex);
5227 					ISTGT_ERRLOG("queue_enqueue() failed\n");
5228 					goto error_return;
5229 				}
5230 				rc = pthread_cond_broadcast(&conn->result_queue_cond);
5231 				MTX_UNLOCK(&conn->result_queue_mutex);
5232 				if (rc != 0) {
5233 					ISTGT_ERRLOG("cond_broadcast() failed\n");
5234 					goto error_return;
5235 				}
5236 			}
5237 
5238 #if 0
5239 			/* write cache */
5240 			if (spec->req_write_cache) {
5241 				MTX_LOCK(&lu->mutex);
5242 				rc = istgt_lu_disk_write_cache(spec, conn);
5243 				MTX_UNLOCK(&lu->mutex);
5244 				if (rc < 0) {
5245 					ISTGT_ERRLOG("disk_write_cache() failed\n");
5246 					return -1;
5247 				}
5248 			}
5249 #endif
5250 		} else {
5251 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
5252 			    "LU%d: LUN%d Task Write Start\n",
5253 			    lu->num, lun);
5254 
5255 #if 0
5256 			MTX_LOCK(&spec->wait_lu_task_mutex);
5257 			spec->wait_lu_task = NULL;
5258 			MTX_UNLOCK(&spec->wait_lu_task_mutex);
5259 #endif
5260 			rc = pthread_mutex_init(&lu_task->trans_mutex, NULL);
5261 			if (rc != 0) {
5262 				ISTGT_ERRLOG("mutex_init() failed\n");
5263 				goto error_return;
5264 			}
5265 			rc = pthread_cond_init(&lu_task->trans_cond, NULL);
5266 			if (rc != 0) {
5267 				ISTGT_ERRLOG("cond_init() failed\n");
5268 				goto error_return;
5269 			}
5270 			rc = pthread_cond_init(&lu_task->exec_cond, NULL);
5271 			if (rc != 0) {
5272 				ISTGT_ERRLOG("cond_init() failed\n");
5273 				goto error_return;
5274 			}
5275 			lu_task->use_cond = 1;
5276 #if 0
5277 			lu_cmd->iobufsize = lu_cmd->transfer_len + 65536;
5278 			iobuf = xmalloc(lu_cmd->iobufsize);
5279 			lu_task->iobuf = iobuf;
5280 #else
5281 			lu_cmd->iobufsize = lu_task->lu_cmd.iobufsize;
5282 			iobuf = lu_task->iobuf;
5283 #endif
5284 			lu_cmd->iobuf = iobuf;
5285 			lu_task->req_transfer_out = 1;
5286 			memset(&abstime, 0, sizeof abstime);
5287 			abstime.tv_sec = 0;
5288 			abstime.tv_nsec = 0;
5289 
5290 			MTX_LOCK(&conn->task_queue_mutex);
5291 			rc = istgt_queue_enqueue(&conn->task_queue, lu_task);
5292 			MTX_UNLOCK(&conn->task_queue_mutex);
5293 			if (rc < 0) {
5294 				MTX_UNLOCK(&lu_task->trans_mutex);
5295 				ISTGT_ERRLOG("queue_enqueue() failed\n");
5296 				goto error_return;
5297 			}
5298 			rc = write(conn->task_pipe[1], tmp, 1);
5299 			if(rc < 0 || rc != 1) {
5300 				MTX_UNLOCK(&lu_task->trans_mutex);
5301 				ISTGT_ERRLOG("write() failed\n");
5302 				goto error_return;
5303 			}
5304 
5305 			start = now = time(NULL);
5306 			abstime.tv_sec = now + (lu_task->condwait / 1000);
5307 			abstime.tv_nsec = (lu_task->condwait % 1000) * 1000000;
5308 #if 0
5309 			ISTGT_LOG("wait CmdSN=%u\n", lu_task->lu_cmd.CmdSN);
5310 #endif
5311 			MTX_LOCK(&lu_task->trans_mutex);
5312 			MTX_LOCK(&spec->wait_lu_task_mutex);
5313 			spec->wait_lu_task = lu_task;
5314 			MTX_UNLOCK(&spec->wait_lu_task_mutex);
5315 			rc = 0;
5316 			while (lu_task->req_transfer_out == 1) {
5317 				rc = pthread_cond_timedwait(&lu_task->trans_cond,
5318 				    &lu_task->trans_mutex,
5319 				    &abstime);
5320 				if (rc == ETIMEDOUT) {
5321 					if (lu_task->req_transfer_out == 1) {
5322 						lu_task->error = 1;
5323 						MTX_LOCK(&spec->wait_lu_task_mutex);
5324 						spec->wait_lu_task = NULL;
5325 						MTX_UNLOCK(&spec->wait_lu_task_mutex);
5326 						MTX_UNLOCK(&lu_task->trans_mutex);
5327 						now = time(NULL);
5328 						ISTGT_ERRLOG("timeout trans_cond CmdSN=%u "
5329 						    "(time=%d)\n",
5330 						    lu_task->lu_cmd.CmdSN,
5331 						    istgt_difftime(now, start));
5332 						/* timeout */
5333 						return -1;
5334 					}
5335 					/* OK cond */
5336 					rc = 0;
5337 					break;
5338 				}
5339 				if (lu_task->error != 0) {
5340 					rc = -1;
5341 					break;
5342 				}
5343 				if (rc != 0) {
5344 					break;
5345 				}
5346 			}
5347 			MTX_LOCK(&spec->wait_lu_task_mutex);
5348 			spec->wait_lu_task = NULL;
5349 			MTX_UNLOCK(&spec->wait_lu_task_mutex);
5350 			MTX_UNLOCK(&lu_task->trans_mutex);
5351 			if (rc != 0) {
5352 				if (rc < 0) {
5353 					lu_task->error = 1;
5354 					if (lu_task->abort) {
5355 						ISTGT_WARNLOG("transfer abort CmdSN=%u\n",
5356 						    lu_task->lu_cmd.CmdSN);
5357 						return -2;
5358 					} else {
5359 						ISTGT_ERRLOG("transfer error CmdSN=%u\n",
5360 						    lu_task->lu_cmd.CmdSN);
5361 						return -1;
5362 					}
5363 				}
5364 				if (rc == ETIMEDOUT) {
5365 					lu_task->error = 1;
5366 					now = time(NULL);
5367 					ISTGT_ERRLOG("timeout trans_cond CmdSN=%u (time=%d)\n",
5368 					    lu_task->lu_cmd.CmdSN, istgt_difftime(now, start));
5369 					return -1;
5370 				}
5371 				lu_task->error = 1;
5372 				ISTGT_ERRLOG("cond_timedwait rc=%d\n", rc);
5373 				return -1;
5374 			}
5375 
5376 			if (lu_task->req_execute == 0) {
5377 				ISTGT_ERRLOG("wrong request\n");
5378 				goto error_return;
5379 			}
5380 			MTX_LOCK(&lu_cmd->lu->mutex);
5381 			rc = istgt_lu_disk_execute(conn, lu_cmd);
5382 			MTX_UNLOCK(&lu_cmd->lu->mutex);
5383 			if (rc < 0) {
5384 				lu_task->error = 1;
5385 				ISTGT_ERRLOG("lu_disk_execute() failed\n");
5386 				goto error_return;
5387 			}
5388 			lu_task->execute = 1;
5389 
5390 			/* response */
5391 			if (conn->use_sender == 0) {
5392 				MTX_LOCK(&conn->task_queue_mutex);
5393 				rc = istgt_queue_enqueue(&conn->task_queue, lu_task);
5394 				MTX_UNLOCK(&conn->task_queue_mutex);
5395 				if (rc < 0) {
5396 					ISTGT_ERRLOG("queue_enqueue() failed\n");
5397 					goto error_return;
5398 				}
5399 				rc = write(conn->task_pipe[1], tmp, 1);
5400 				if(rc < 0 || rc != 1) {
5401 					ISTGT_ERRLOG("write() failed\n");
5402 					goto error_return;
5403 				}
5404 			} else {
5405 				MTX_LOCK(&conn->result_queue_mutex);
5406 				rc = istgt_queue_enqueue(&conn->result_queue, lu_task);
5407 				if (rc < 0) {
5408 					MTX_UNLOCK(&conn->result_queue_mutex);
5409 					ISTGT_ERRLOG("queue_enqueue() failed\n");
5410 					goto error_return;
5411 				}
5412 				rc = pthread_cond_broadcast(&conn->result_queue_cond);
5413 				MTX_UNLOCK(&conn->result_queue_mutex);
5414 				if (rc != 0) {
5415 					ISTGT_ERRLOG("cond_broadcast() failed\n");
5416 					goto error_return;
5417 				}
5418 			}
5419 
5420 #if 0
5421 			/* write cache */
5422 			if (spec->req_write_cache) {
5423 				MTX_LOCK(&lu->mutex);
5424 				rc = istgt_lu_disk_write_cache(spec, conn);
5425 				MTX_UNLOCK(&lu->mutex);
5426 				if (rc < 0) {
5427 					ISTGT_ERRLOG("disk_write_cache() failed\n");
5428 					return -1;
5429 				}
5430 			}
5431 #endif
5432 		}
5433 	} else {
5434 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
5435 		    "LU%d: LUN%d Task Read Start\n",
5436 		    lu->num, lun);
5437 #if 0
5438 		lu_cmd->iobufsize = lu_cmd->transfer_len + 65536;
5439 		iobuf = xmalloc(lu_cmd->iobufsize);
5440 		lu_task->iobuf = iobuf;
5441 #else
5442 		lu_cmd->iobufsize = lu_task->lu_cmd.iobufsize;
5443 		iobuf = lu_task->iobuf;
5444 #endif
5445 		lu_cmd->iobuf = iobuf;
5446 		MTX_LOCK(&lu_cmd->lu->mutex);
5447 		rc = istgt_lu_disk_execute(conn, lu_cmd);
5448 		MTX_UNLOCK(&lu_cmd->lu->mutex);
5449 		if (rc < 0) {
5450 			ISTGT_ERRLOG("lu_disk_execute() failed\n");
5451 			goto error_return;
5452 		}
5453 		lu_task->execute = 1;
5454 
5455 		/* response */
5456 		if (conn->use_sender == 0) {
5457 			MTX_LOCK(&conn->task_queue_mutex);
5458 			rc = istgt_queue_enqueue(&conn->task_queue, lu_task);
5459 			MTX_UNLOCK(&conn->task_queue_mutex);
5460 			if (rc < 0) {
5461 				ISTGT_ERRLOG("queue_enqueue() failed\n");
5462 				goto error_return;
5463 			}
5464 			rc = write(conn->task_pipe[1], tmp, 1);
5465 			if(rc < 0 || rc != 1) {
5466 				ISTGT_ERRLOG("write() failed\n");
5467 				goto error_return;
5468 			}
5469 		} else {
5470 			MTX_LOCK(&conn->result_queue_mutex);
5471 			rc = istgt_queue_enqueue(&conn->result_queue, lu_task);
5472 			if (rc < 0) {
5473 				MTX_UNLOCK(&conn->result_queue_mutex);
5474 				ISTGT_ERRLOG("queue_enqueue() failed\n");
5475 				goto error_return;
5476 			}
5477 			rc = pthread_cond_broadcast(&conn->result_queue_cond);
5478 			MTX_UNLOCK(&conn->result_queue_mutex);
5479 			if (rc != 0) {
5480 				ISTGT_ERRLOG("cond_broadcast() failed\n");
5481 				goto error_return;
5482 			}
5483 		}
5484 	}
5485 
5486 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LU%d: LUN%d queue end\n",
5487 	    lu->num, lun);
5488 
5489 	if (abort_task) {
5490 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Abort Task\n");
5491 		return -1;
5492 	}
5493 	return 0;
5494 }
5495 
5496 int
istgt_lu_disk_execute(CONN_Ptr conn,ISTGT_LU_CMD_Ptr lu_cmd)5497 istgt_lu_disk_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd)
5498 {
5499 	ISTGT_LU_Ptr lu;
5500 	ISTGT_LU_DISK *spec;
5501 	uint8_t *data;
5502 	uint8_t *cdb;
5503 	uint32_t allocation_len;
5504 	int data_len;
5505 	int data_alloc_len;
5506 	uint64_t lba;
5507 	uint32_t len;
5508 	uint32_t transfer_len;
5509 	uint32_t parameter_len;
5510 	uint8_t *sense_data;
5511 	size_t *sense_len;
5512 	int lun_i;
5513 	int rc;
5514 
5515 	if (lu_cmd == NULL)
5516 		return -1;
5517 	lu = lu_cmd->lu;
5518 	if (lu == NULL) {
5519 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5520 		return -1;
5521 	}
5522 	spec = NULL;
5523 	cdb = lu_cmd->cdb;
5524 	data = lu_cmd->data;
5525 	data_alloc_len = lu_cmd->alloc_len;
5526 	sense_data = lu_cmd->sense_data;
5527 	sense_len = &lu_cmd->sense_data_len;
5528 	*sense_len = 0;
5529 
5530 	lun_i = istgt_lu_islun2lun(lu_cmd->lun);
5531 	if (lun_i >= lu->maxlun) {
5532 #ifdef ISTGT_TRACE_DISK
5533 		ISTGT_ERRLOG("LU%d: LUN%d invalid\n",
5534 		    lu->num, lun_i);
5535 #endif /* ISTGT_TRACE_DISK */
5536 		if (cdb[0] == SPC_INQUIRY) {
5537 			allocation_len = DGET16(&cdb[3]);
5538 			if (allocation_len > (size_t) data_alloc_len) {
5539 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
5540 				    data_alloc_len);
5541 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5542 				return -1;
5543 			}
5544 			memset(data, 0, allocation_len);
5545 			/* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
5546 			BDSET8W(&data[0], 0x03, 7, 3);
5547 			BDADD8W(&data[0], 0x1f, 4, 5);
5548 			data_len = 96;
5549 			memset(&data[1], 0, data_len - 1);
5550 			/* ADDITIONAL LENGTH */
5551 			data[4] = data_len - 5;
5552 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
5553 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5554 			return 0;
5555 		} else {
5556 			/* LOGICAL UNIT NOT SUPPORTED */
5557 			BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00);
5558 			lu_cmd->data_len = 0;
5559 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5560 			return 0;
5561 		}
5562 	}
5563 	spec = (ISTGT_LU_DISK *) lu->lun[lun_i].spec;
5564 	if (spec == NULL) {
5565 		/* LOGICAL UNIT NOT SUPPORTED */
5566 		BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00);
5567 		lu_cmd->data_len = 0;
5568 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5569 		return 0;
5570 	}
5571 
5572 	if (spec->sense != 0) {
5573 		int sk, asc, ascq;
5574 		if (cdb[0] != SPC_INQUIRY
5575 		    && cdb[0] != SPC_REPORT_LUNS) {
5576 			sk = (spec->sense >> 16) & 0xffU;
5577 			asc = (spec->sense >> 8) & 0xffU;
5578 			ascq = (spec->sense >> 0) & 0xffU;
5579 			spec->sense = 0;
5580 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
5581 			    "Generate sk=0x%x, asc=0x%x, ascq=0x%x\n",
5582 			    sk, asc, ascq);
5583 			*sense_len
5584 				= istgt_lu_disk_build_sense_data(spec, sense_data,
5585 				    sk, asc, ascq);
5586 			lu_cmd->data_len = 0;
5587 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5588 			return 0;
5589 		}
5590 	}
5591 
5592 	if (spec->err_write_cache) {
5593 		/* WRITE ERROR - AUTO REALLOCATION FAILED */
5594 		BUILD_SENSE2(MEDIUM_ERROR, 0x0c, 0x02);
5595 #if 0
5596 		/* WRITE ERROR - RECOMMEND REASSIGNMENT */
5597 		BUILD_SENSE2(MEDIUM_ERROR, 0x0c, 0x03);
5598 #endif
5599 		spec->err_write_cache = 0;
5600 		lba = spec->woffset / spec->blocklen;
5601 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG,
5602 		    "Deferred error (write cache) at %"PRIu64"\n", lba);
5603 		if (lba > 0xffffffffULL) {
5604 			ISTGT_WARNLOG("lba > 0xffffffff\n");
5605 		}
5606 		/* COMMAND-SPECIFIC INFORMATION */
5607 		DSET32(&sense_data[8], (uint32_t)(lba & 0xffffffffULL));
5608 		lu_cmd->data_len = 0;
5609 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5610 		return 0;
5611 	}
5612 
5613 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
5614 	    "SCSI OP=0x%x, LUN=0x%16.16"PRIx64"\n",
5615 	    cdb[0], lu_cmd->lun);
5616 #ifdef ISTGT_TRACE_DISK
5617 	if (cdb[0] != SPC_TEST_UNIT_READY) {
5618 		istgt_scsi_dump_cdb(cdb);
5619 	}
5620 #endif /* ISTGT_TRACE_DISK */
5621 	switch (cdb[0]) {
5622 	case SPC_INQUIRY:
5623 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "INQUIRY\n");
5624 		if (lu_cmd->R_bit == 0) {
5625 			ISTGT_ERRLOG("R_bit == 0\n");
5626 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5627 			return -1;
5628 		}
5629 		allocation_len = DGET16(&cdb[3]);
5630 		if (allocation_len > (size_t) data_alloc_len) {
5631 			ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
5632 			    data_alloc_len);
5633 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5634 			return -1;
5635 		}
5636 		memset(data, 0, allocation_len);
5637 		data_len = istgt_lu_disk_scsi_inquiry(spec, conn, cdb,
5638 		    data, data_alloc_len);
5639 		if (data_len < 0) {
5640 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5641 			break;
5642 		}
5643 		ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "INQUIRY", data, data_len);
5644 		lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
5645 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5646 		break;
5647 
5648 	case SPC_REPORT_LUNS:
5649 		{
5650 			int sel;
5651 
5652 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT LUNS\n");
5653 			if (lu_cmd->R_bit == 0) {
5654 				ISTGT_ERRLOG("R_bit == 0\n");
5655 				return -1;
5656 			}
5657 
5658 			sel = cdb[2];
5659 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sel=%x\n", sel);
5660 
5661 			allocation_len = DGET32(&cdb[6]);
5662 			if (allocation_len > (size_t) data_alloc_len) {
5663 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
5664 				    data_alloc_len);
5665 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5666 				return -1;
5667 			}
5668 			if (allocation_len < 16) {
5669 				/* INVALID FIELD IN CDB */
5670 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
5671 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5672 				break;
5673 			}
5674 			memset(data, 0, allocation_len);
5675 			data_len = istgt_lu_disk_scsi_report_luns(lu, conn, cdb, sel,
5676 			    data, data_alloc_len);
5677 			if (data_len < 0) {
5678 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5679 				break;
5680 			}
5681 			ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "REPORT LUNS", data, data_len);
5682 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
5683 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5684 		}
5685 		break;
5686 
5687 	case SPC_TEST_UNIT_READY:
5688 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "TEST_UNIT_READY\n");
5689 		lu_cmd->data_len = 0;
5690 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5691 		break;
5692 
5693 	case SBC_START_STOP_UNIT:
5694 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "START_STOP_UNIT\n");
5695 		{
5696 			int pc, loej, start;
5697 
5698 			pc = BGET8W(&cdb[4], 7, 4);
5699 			loej = BGET8(&cdb[4], 1);
5700 			start = BGET8(&cdb[4], 0);
5701 
5702 			if (start != 0 || pc != 0) {
5703 				if (spec->rsv_key) {
5704 					rc = istgt_lu_disk_check_pr(spec, conn,
5705 					    PR_ALLOW(0,0,1,0,0));
5706 					if (rc != 0) {
5707 						lu_cmd->status
5708 							= ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
5709 						break;
5710 					}
5711 				}
5712 			}
5713 
5714 			lu_cmd->data_len = 0;
5715 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5716 		}
5717 		break;
5718 
5719 	case SBC_READ_CAPACITY_10:
5720 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_CAPACITY_10\n");
5721 		if (lu_cmd->R_bit == 0) {
5722 			ISTGT_ERRLOG("R_bit == 0\n");
5723 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5724 			return -1;
5725 		}
5726 		if (spec->blockcnt - 1 > 0xffffffffULL) {
5727 			DSET32(&data[0], 0xffffffffUL);
5728 		} else {
5729 			DSET32(&data[0], (uint32_t) (spec->blockcnt - 1));
5730 		}
5731 		DSET32(&data[4], (uint32_t) spec->blocklen);
5732 		data_len = 8;
5733 		lu_cmd->data_len = data_len;
5734 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5735 		ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG,
5736 		    "SBC_READ_CAPACITY_10", data, data_len);
5737 		break;
5738 
5739 	case SPC_SERVICE_ACTION_IN_16:
5740 		switch (BGET8W(&cdb[1], 4, 5)) { /* SERVICE ACTION */
5741 		case SBC_SAI_READ_CAPACITY_16:
5742 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_CAPACITY_16\n");
5743 			if (lu_cmd->R_bit == 0) {
5744 				ISTGT_ERRLOG("R_bit == 0\n");
5745 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5746 				return -1;
5747 			}
5748 			allocation_len = DGET32(&cdb[10]);
5749 			if (allocation_len > (size_t) data_alloc_len) {
5750 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
5751 				    data_alloc_len);
5752 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5753 				return -1;
5754 			}
5755 			memset(data, 0, allocation_len);
5756 			DSET64(&data[0], spec->blockcnt - 1);
5757 			DSET32(&data[8], (uint32_t) spec->blocklen);
5758 			data[12] = 0;                   /* RTO_EN(1) PROT_EN(0) */
5759 			memset(&data[13], 0, 32 - (8 + 4 + 1));     /* Reserved */
5760 			data_len = 32;
5761 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
5762 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5763 			break;
5764 		case SBC_SAI_READ_LONG_16:
5765 		default:
5766 			/* INVALID COMMAND OPERATION CODE */
5767 			BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
5768 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5769 			break;
5770 		}
5771 		break;
5772 
5773 	case SPC_MODE_SELECT_6:
5774 #if 0
5775 		istgt_scsi_dump_cdb(cdb);
5776 #endif
5777 		{
5778 			int pf, sp, pllen;
5779 			int mdlen, mt, dsp, bdlen;
5780 
5781 			if (spec->rsv_key) {
5782 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
5783 				if (rc != 0) {
5784 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
5785 					break;
5786 				}
5787 			}
5788 
5789 			pf = BGET8(&cdb[1], 4);
5790 			sp = BGET8(&cdb[1], 0);
5791 			pllen = cdb[4];             /* Parameter List Length */
5792 
5793 			if (pllen == 0) {
5794 				lu_cmd->data_len = 0;
5795 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5796 				break;
5797 			}
5798 			/* Data-Out */
5799 			rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
5800 			    lu_cmd->iobufsize, pllen);
5801 			if (rc < 0) {
5802 				ISTGT_ERRLOG("lu_disk_transfer_data() failed\n");
5803 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5804 				break;
5805 			}
5806 			if (pllen < 4) {
5807 				/* INVALID FIELD IN CDB */
5808 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
5809 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5810 				break;
5811 			}
5812 #if 0
5813 			istgt_dump("MODE SELECT(6)", lu_cmd->iobuf, pllen);
5814 #endif
5815 			data = lu_cmd->iobuf;
5816 			mdlen = data[0];            /* Mode Data Length */
5817 			mt = data[1];               /* Medium Type */
5818 			dsp = data[2];              /* Device-Specific Parameter */
5819 			bdlen = data[3];            /* Block Descriptor Length */
5820 
5821 			/* Short LBA mode parameter block descriptor */
5822 			/* data[4]-data[7] Number of Blocks */
5823 			/* data[8]-data[11] Block Length */
5824 
5825 			/* page data */
5826 			data_len = istgt_lu_disk_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[4 + bdlen], pllen - (4 + bdlen));
5827 			if (data_len != 0) {
5828 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5829 				break;
5830 			}
5831 			lu_cmd->data_len = pllen;
5832 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5833 			break;
5834 		}
5835 
5836 	case SPC_MODE_SELECT_10:
5837 #if 0
5838 		istgt_scsi_dump_cdb(cdb);
5839 #endif
5840 		{
5841 			int pf, sp, pllen;
5842 			int mdlen, mt, dsp, bdlen;
5843 			int llba;
5844 
5845 			if (spec->rsv_key) {
5846 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
5847 				if (rc != 0) {
5848 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
5849 					break;
5850 				}
5851 			}
5852 
5853 			pf = BGET8(&cdb[1], 4);
5854 			sp = BGET8(&cdb[1], 0);
5855 			pllen = DGET16(&cdb[7]);    /* Parameter List Length */
5856 
5857 			if (pllen == 0) {
5858 				lu_cmd->data_len = 0;
5859 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5860 				break;
5861 			}
5862 			/* Data-Out */
5863 			rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
5864 			    lu_cmd->iobufsize, pllen);
5865 			if (rc < 0) {
5866 				ISTGT_ERRLOG("lu_disk_transfer_data() failed\n");
5867 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5868 				break;
5869 			}
5870 			if (pllen < 4) {
5871 				/* INVALID FIELD IN CDB */
5872 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
5873 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5874 				break;
5875 			}
5876 #if 0
5877 			istgt_dump("MODE SELECT(10)", lu_cmd->iobuf, pllen);
5878 #endif
5879 			data = lu_cmd->iobuf;
5880 			mdlen = DGET16(&data[0]);   /* Mode Data Length */
5881 			mt = data[2];               /* Medium Type */
5882 			dsp = data[3];              /* Device-Specific Parameter */
5883 			llba = BGET8(&data[4], 0);  /* Long LBA */
5884 			bdlen = DGET16(&data[6]);   /* Block Descriptor Length */
5885 
5886 			if (llba) {
5887 				/* Long LBA mode parameter block descriptor */
5888 				/* data[8]-data[15] Number of Blocks */
5889 				/* data[16]-data[19] Reserved */
5890 				/* data[20]-data[23] Block Length */
5891 			} else {
5892 				/* Short LBA mode parameter block descriptor */
5893 				/* data[8]-data[11] Number of Blocks */
5894 				/* data[12]-data[15] Block Length */
5895 			}
5896 
5897 			/* page data */
5898 			data_len = istgt_lu_disk_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[8 + bdlen], pllen - (8 + bdlen));
5899 			if (data_len != 0) {
5900 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5901 				break;
5902 			}
5903 			lu_cmd->data_len = pllen;
5904 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5905 			break;
5906 		}
5907 
5908 	case SPC_MODE_SENSE_6:
5909 #if 0
5910 		istgt_scsi_dump_cdb(cdb);
5911 #endif
5912 		{
5913 			int dbd, pc, page, subpage;
5914 
5915 			if (spec->rsv_key) {
5916 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
5917 				if (rc != 0) {
5918 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
5919 					break;
5920 				}
5921 			}
5922 
5923 			if (lu_cmd->R_bit == 0) {
5924 				ISTGT_ERRLOG("R_bit == 0\n");
5925 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5926 				return -1;
5927 			}
5928 
5929 			dbd = BGET8(&cdb[1], 3);
5930 			pc = BGET8W(&cdb[2], 7, 2);
5931 			page = BGET8W(&cdb[2], 5, 6);
5932 			subpage = cdb[3];
5933 
5934 			allocation_len = cdb[4];
5935 			if (allocation_len > (size_t) data_alloc_len) {
5936 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
5937 				    data_alloc_len);
5938 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5939 				return -1;
5940 			}
5941 			memset(data, 0, allocation_len);
5942 
5943 			data_len = istgt_lu_disk_scsi_mode_sense6(spec, conn, cdb, dbd, pc, page, subpage, data, data_alloc_len);
5944 			if (data_len < 0) {
5945 				/* INVALID FIELD IN CDB */
5946 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
5947 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5948 				break;
5949 			}
5950 #if 0
5951 			istgt_dump("MODE SENSE(6)", data, data_len);
5952 #endif
5953 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
5954 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
5955 			break;
5956 		}
5957 
5958 	case SPC_MODE_SENSE_10:
5959 #if 0
5960 		istgt_scsi_dump_cdb(cdb);
5961 #endif
5962 		{
5963 			int dbd, pc, page, subpage;
5964 			int llbaa;
5965 
5966 			if (spec->rsv_key) {
5967 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
5968 				if (rc != 0) {
5969 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
5970 					break;
5971 				}
5972 			}
5973 
5974 			if (lu_cmd->R_bit == 0) {
5975 				ISTGT_ERRLOG("R_bit == 0\n");
5976 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5977 				return -1;
5978 			}
5979 
5980 			llbaa = BGET8(&cdb[1], 4);
5981 			dbd = BGET8(&cdb[1], 3);
5982 			pc = BGET8W(&cdb[2], 7, 2);
5983 			page = BGET8W(&cdb[2], 5, 6);
5984 			subpage = cdb[3];
5985 
5986 			allocation_len = DGET16(&cdb[7]);
5987 			if (allocation_len > (size_t) data_alloc_len) {
5988 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
5989 				    data_alloc_len);
5990 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
5991 				return -1;
5992 			}
5993 			memset(data, 0, allocation_len);
5994 
5995 			data_len = istgt_lu_disk_scsi_mode_sense10(spec, conn, cdb, llbaa, dbd, pc, page, subpage, data, data_alloc_len);
5996 			if (data_len < 0) {
5997 				/* INVALID FIELD IN CDB */
5998 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
5999 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6000 				break;
6001 			}
6002 #if 0
6003 			istgt_dump("MODE SENSE(10)", data, data_len);
6004 #endif
6005 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
6006 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6007 			break;
6008 		}
6009 
6010 	case SPC_LOG_SELECT:
6011 	case SPC_LOG_SENSE:
6012 		/* INVALID COMMAND OPERATION CODE */
6013 		BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
6014 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6015 		break;
6016 
6017 	case SPC_REQUEST_SENSE:
6018 		{
6019 			int desc;
6020 			int sk, asc, ascq;
6021 
6022 			if (lu_cmd->R_bit == 0) {
6023 				ISTGT_ERRLOG("R_bit == 0\n");
6024 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6025 				return -1;
6026 			}
6027 
6028 			desc = BGET8(&cdb[1], 0);
6029 			if (desc != 0) {
6030 				/* INVALID FIELD IN CDB */
6031 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
6032 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6033 				break;
6034 			}
6035 
6036 			allocation_len = cdb[4];
6037 			if (allocation_len > (size_t) data_alloc_len) {
6038 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
6039 				    data_alloc_len);
6040 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6041 				return -1;
6042 			}
6043 			memset(data, 0, allocation_len);
6044 
6045 			if (!spec->sense) {
6046 				/* NO ADDITIONAL SENSE INFORMATION */
6047 				sk = ISTGT_SCSI_SENSE_NO_SENSE;
6048 				asc = 0x00;
6049 				ascq = 0x00;
6050 			} else {
6051 				sk = (spec->sense >> 16) & 0xffU;
6052 				asc = (spec->sense >> 8) & 0xffU;
6053 				ascq = spec->sense & 0xffU;
6054 			}
6055 			data_len = istgt_lu_disk_build_sense_data(spec, sense_data,
6056 			    sk, asc, ascq);
6057 			if (data_len < 0 || data_len < 2) {
6058 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6059 				break;
6060 			}
6061 			/* omit SenseLength */
6062 			data_len -= 2;
6063 			memcpy(data, sense_data + 2, data_len);
6064 #if 0
6065 			istgt_dump("REQUEST SENSE", data, data_len);
6066 #endif
6067 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
6068 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6069 			break;
6070 		}
6071 
6072 	case SBC_READ_6:
6073 		{
6074 			if (spec->rsv_key) {
6075 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0));
6076 				if (rc != 0) {
6077 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6078 					break;
6079 				}
6080 			}
6081 
6082 			if (lu_cmd->R_bit == 0) {
6083 				ISTGT_ERRLOG("R_bit == 0\n");
6084 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6085 				return -1;
6086 			}
6087 
6088 			lba = (uint64_t) (DGET24(&cdb[1]) & 0x001fffffU);
6089 			transfer_len = (uint32_t) DGET8(&cdb[4]);
6090 			if (transfer_len == 0) {
6091 				transfer_len = 256;
6092 			}
6093 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6094 			    "READ_6(lba %"PRIu64", len %u blocks)\n",
6095 			    lba, transfer_len);
6096 			rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len);
6097 			if (rc < 0) {
6098 				ISTGT_ERRLOG("lu_disk_lbread() failed\n");
6099 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6100 				break;
6101 			}
6102 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6103 			break;
6104 		}
6105 
6106 	case SBC_READ_10:
6107 		{
6108 			int dpo, fua, fua_nv;
6109 
6110 			if (spec->rsv_key) {
6111 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0));
6112 				if (rc != 0) {
6113 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6114 					break;
6115 				}
6116 			}
6117 
6118 			if (lu_cmd->R_bit == 0) {
6119 				ISTGT_ERRLOG("R_bit == 0\n");
6120 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6121 				return -1;
6122 			}
6123 
6124 			dpo = BGET8(&cdb[1], 4);
6125 			fua = BGET8(&cdb[1], 3);
6126 			fua_nv = BGET8(&cdb[1], 1);
6127 			lba = (uint64_t) DGET32(&cdb[2]);
6128 			transfer_len = (uint32_t) DGET16(&cdb[7]);
6129 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6130 			    "READ_10(lba %"PRIu64", len %u blocks)\n",
6131 			    lba, transfer_len);
6132 			rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len);
6133 			if (rc < 0) {
6134 				ISTGT_ERRLOG("lu_disk_lbread() failed\n");
6135 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6136 				break;
6137 			}
6138 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6139 			break;
6140 		}
6141 
6142 	case SBC_READ_12:
6143 		{
6144 			int dpo, fua, fua_nv;
6145 
6146 			if (spec->rsv_key) {
6147 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0));
6148 				if (rc != 0) {
6149 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6150 					break;
6151 				}
6152 			}
6153 
6154 			if (lu_cmd->R_bit == 0) {
6155 				ISTGT_ERRLOG("R_bit == 0\n");
6156 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6157 				return -1;
6158 			}
6159 
6160 			dpo = BGET8(&cdb[1], 4);
6161 			fua = BGET8(&cdb[1], 3);
6162 			fua_nv = BGET8(&cdb[1], 1);
6163 			lba = (uint64_t) DGET32(&cdb[2]);
6164 			transfer_len = (uint32_t) DGET32(&cdb[6]);
6165 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6166 			    "READ_12(lba %"PRIu64", len %u blocks)\n",
6167 			    lba, transfer_len);
6168 			rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len);
6169 			if (rc < 0) {
6170 				ISTGT_ERRLOG("lu_disk_lbread() failed\n");
6171 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6172 				break;
6173 			}
6174 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6175 			break;
6176 		}
6177 
6178 	case SBC_READ_16:
6179 		{
6180 			int dpo, fua, fua_nv;
6181 
6182 			if (spec->rsv_key) {
6183 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0));
6184 				if (rc != 0) {
6185 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6186 					break;
6187 				}
6188 			}
6189 
6190 			if (lu_cmd->R_bit == 0) {
6191 				ISTGT_ERRLOG("R_bit == 0\n");
6192 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6193 				return -1;
6194 			}
6195 
6196 			dpo = BGET8(&cdb[1], 4);
6197 			fua = BGET8(&cdb[1], 3);
6198 			fua_nv = BGET8(&cdb[1], 1);
6199 			lba = (uint64_t) DGET64(&cdb[2]);
6200 			transfer_len = (uint32_t) DGET32(&cdb[10]);
6201 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6202 			    "READ_16(lba %"PRIu64", len %u blocks)\n",
6203 			    lba, transfer_len);
6204 			rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len);
6205 			if (rc < 0) {
6206 				ISTGT_ERRLOG("lu_disk_lbread() failed\n");
6207 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6208 				break;
6209 			}
6210 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6211 			break;
6212 		}
6213 
6214 	case SBC_WRITE_6:
6215 		{
6216 			if (spec->rsv_key) {
6217 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6218 				if (rc != 0) {
6219 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6220 					break;
6221 				}
6222 			}
6223 
6224 			if (lu_cmd->W_bit == 0) {
6225 				ISTGT_ERRLOG("W_bit == 0\n");
6226 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6227 				return -1;
6228 			}
6229 
6230 			lba = (uint64_t) (DGET24(&cdb[1]) & 0x001fffffU);
6231 			transfer_len = (uint32_t) DGET8(&cdb[4]);
6232 			if (transfer_len == 0) {
6233 				transfer_len = 256;
6234 			}
6235 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6236 			    "WRITE_6(lba %"PRIu64", len %u blocks)\n",
6237 			    lba, transfer_len);
6238 			rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len);
6239 			if (rc < 0) {
6240 				ISTGT_ERRLOG("lu_disk_lbwrite() failed\n");
6241 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6242 				break;
6243 			}
6244 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6245 			break;
6246 		}
6247 
6248 	case SBC_WRITE_10:
6249 	case SBC_WRITE_AND_VERIFY_10:
6250 		{
6251 			int dpo, fua, fua_nv;
6252 
6253 			if (spec->rsv_key) {
6254 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6255 				if (rc != 0) {
6256 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6257 					break;
6258 				}
6259 			}
6260 
6261 			if (lu_cmd->W_bit == 0) {
6262 				ISTGT_ERRLOG("W_bit == 0\n");
6263 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6264 				return -1;
6265 			}
6266 
6267 			dpo = BGET8(&cdb[1], 4);
6268 			fua = BGET8(&cdb[1], 3);
6269 			fua_nv = BGET8(&cdb[1], 1);
6270 			lba = (uint64_t) DGET32(&cdb[2]);
6271 			transfer_len = (uint32_t) DGET16(&cdb[7]);
6272 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6273 			    "WRITE_10(lba %"PRIu64", len %u blocks)\n",
6274 			    lba, transfer_len);
6275 			rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len);
6276 			if (rc < 0) {
6277 				ISTGT_ERRLOG("lu_disk_lbwrite() failed\n");
6278 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6279 				break;
6280 			}
6281 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6282 			break;
6283 		}
6284 
6285 	case SBC_WRITE_12:
6286 	case SBC_WRITE_AND_VERIFY_12:
6287 		{
6288 			int dpo, fua, fua_nv;
6289 
6290 			if (spec->rsv_key) {
6291 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6292 				if (rc != 0) {
6293 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6294 					break;
6295 				}
6296 			}
6297 
6298 			if (lu_cmd->W_bit == 0) {
6299 				ISTGT_ERRLOG("W_bit == 0\n");
6300 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6301 				return -1;
6302 			}
6303 
6304 			dpo = BGET8(&cdb[1], 4);
6305 			fua = BGET8(&cdb[1], 3);
6306 			fua_nv = BGET8(&cdb[1], 1);
6307 			lba = (uint64_t) DGET32(&cdb[2]);
6308 			transfer_len = (uint32_t) DGET32(&cdb[6]);
6309 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6310 			    "WRITE_12(lba %"PRIu64", len %u blocks)\n",
6311 			    lba, transfer_len);
6312 			rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len);
6313 			if (rc < 0) {
6314 				ISTGT_ERRLOG("lu_disk_lbwrite() failed\n");
6315 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6316 				break;
6317 			}
6318 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6319 			break;
6320 		}
6321 
6322 	case SBC_WRITE_16:
6323 	case SBC_WRITE_AND_VERIFY_16:
6324 		{
6325 			int dpo, fua, fua_nv;
6326 
6327 			if (spec->rsv_key) {
6328 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6329 				if (rc != 0) {
6330 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6331 					break;
6332 				}
6333 			}
6334 
6335 			if (lu_cmd->W_bit == 0) {
6336 				ISTGT_ERRLOG("W_bit == 0\n");
6337 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6338 				return -1;
6339 			}
6340 
6341 			dpo = BGET8(&cdb[1], 4);
6342 			fua = BGET8(&cdb[1], 3);
6343 			fua_nv = BGET8(&cdb[1], 1);
6344 			lba = (uint64_t) DGET64(&cdb[2]);
6345 			transfer_len = (uint32_t) DGET32(&cdb[10]);
6346 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6347 			    "WRITE_16(lba %"PRIu64", len %u blocks)\n",
6348 			    lba, transfer_len);
6349 			rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len);
6350 			if (rc < 0) {
6351 				ISTGT_ERRLOG("lu_disk_lbwrite() failed\n");
6352 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6353 				break;
6354 			}
6355 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6356 			break;
6357 		}
6358 
6359 	case SBC_VERIFY_10:
6360 		{
6361 			int dpo, bytchk;
6362 
6363 			if (spec->rsv_key) {
6364 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0));
6365 				if (rc != 0) {
6366 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6367 					break;
6368 				}
6369 			}
6370 
6371 			dpo = BGET8(&cdb[1], 4);
6372 			bytchk = BGET8(&cdb[1], 1);
6373 			lba = (uint64_t) DGET32(&cdb[2]);
6374 			len = (uint32_t) DGET16(&cdb[7]);
6375 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6376 			    "VERIFY_10(lba %"PRIu64", len %u blocks)\n",
6377 			    lba, len);
6378 			lu_cmd->data_len = 0;
6379 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6380 			break;
6381 		}
6382 
6383 	case SBC_VERIFY_12:
6384 		{
6385 			int dpo, bytchk;
6386 
6387 			if (spec->rsv_key) {
6388 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0));
6389 				if (rc != 0) {
6390 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6391 					break;
6392 				}
6393 			}
6394 
6395 			dpo = BGET8(&cdb[1], 4);
6396 			bytchk = BGET8(&cdb[1], 1);
6397 			lba = (uint64_t) DGET32(&cdb[2]);
6398 			len = (uint32_t) DGET32(&cdb[6]);
6399 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6400 			    "VERIFY_12(lba %"PRIu64", len %u blocks)\n",
6401 			    lba, len);
6402 			lu_cmd->data_len = 0;
6403 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6404 			break;
6405 		}
6406 
6407 	case SBC_VERIFY_16:
6408 		{
6409 			int dpo, bytchk;
6410 
6411 			if (spec->rsv_key) {
6412 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0));
6413 				if (rc != 0) {
6414 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6415 					break;
6416 				}
6417 			}
6418 
6419 			dpo = BGET8(&cdb[1], 4);
6420 			bytchk = BGET8(&cdb[1], 1);
6421 			lba = (uint64_t) DGET64(&cdb[2]);
6422 			len = (uint32_t) DGET32(&cdb[10]);
6423 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6424 			    "VERIFY_16(lba %"PRIu64", len %u blocks)\n",
6425 			    lba, len);
6426 			lu_cmd->data_len = 0;
6427 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6428 			break;
6429 		}
6430 
6431 	case SBC_WRITE_SAME_10:
6432 		{
6433 			int wprotect, pbdata, lbdata, group_no;
6434 
6435 			if (spec->rsv_key) {
6436 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6437 				if (rc != 0) {
6438 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6439 					break;
6440 				}
6441 			}
6442 
6443 			if (lu_cmd->W_bit == 0) {
6444 				ISTGT_ERRLOG("W_bit == 0\n");
6445 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6446 				return -1;
6447 			}
6448 
6449 			wprotect = BGET8W(&cdb[1], 7, 3);
6450 			pbdata = BGET8(&cdb[1], 2);
6451 			lbdata = BGET8(&cdb[1], 1);
6452 			lba = (uint64_t) DGET32(&cdb[2]);
6453 			transfer_len = (uint32_t) DGET16(&cdb[7]);
6454 			group_no = BGET8W(&cdb[6], 4, 5);
6455 
6456 			/* only PBDATA=0 and LBDATA=0 support */
6457 			if (pbdata || lbdata) {
6458 				/* INVALID FIELD IN CDB */
6459 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
6460 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6461 				break;
6462 			}
6463 
6464 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6465 			    "WRITE_SAME_10(lba %"PRIu64", len %u blocks)\n",
6466 			    lba, transfer_len);
6467 			rc = istgt_lu_disk_lbwrite_same(spec, conn, lu_cmd, lba, transfer_len);
6468 			if (rc < 0) {
6469 				ISTGT_ERRLOG("lu_disk_lbwrite_same() failed\n");
6470 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6471 				break;
6472 			}
6473 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6474 			break;
6475 		}
6476 
6477 	case SBC_WRITE_SAME_16:
6478 		{
6479 			int wprotect, anchor, unmap, pbdata, lbdata, group_no;
6480 
6481 #if 0
6482 			istgt_scsi_dump_cdb(cdb);
6483 #endif
6484 			if (spec->rsv_key) {
6485 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6486 				if (rc != 0) {
6487 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6488 					break;
6489 				}
6490 			}
6491 
6492 			if (lu_cmd->W_bit == 0) {
6493 				ISTGT_ERRLOG("W_bit == 0\n");
6494 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6495 				return -1;
6496 			}
6497 
6498 			wprotect = BGET8W(&cdb[1], 7, 3);
6499 			anchor = BGET8(&cdb[1], 4);
6500 			unmap = BGET8(&cdb[1], 3);
6501 			pbdata = BGET8(&cdb[1], 2);
6502 			lbdata = BGET8(&cdb[1], 1);
6503 			lba = (uint64_t) DGET64(&cdb[2]);
6504 			transfer_len = (uint32_t) DGET32(&cdb[10]);
6505 			group_no = BGET8W(&cdb[14], 4, 5);
6506 
6507 			/* only PBDATA=0 and LBDATA=0 support */
6508 			if (pbdata || lbdata) {
6509 				/* INVALID FIELD IN CDB */
6510 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
6511 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6512 				break;
6513 			}
6514 			if (anchor) {
6515 				/* INVALID FIELD IN CDB */
6516 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
6517 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6518 				break;
6519 			}
6520 
6521 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6522 			    "WRITE_SAME_16(lba %"PRIu64", len %u blocks)\n",
6523 			    lba, transfer_len);
6524 			rc = istgt_lu_disk_lbwrite_same(spec, conn, lu_cmd, lba, transfer_len);
6525 			if (rc < 0) {
6526 				ISTGT_ERRLOG("lu_disk_lbwrite_same() failed\n");
6527 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6528 				break;
6529 			}
6530 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6531 			break;
6532 		}
6533 
6534 	case SBC_COMPARE_AND_WRITE:
6535 		{
6536 			int64_t maxlen;
6537 			int wprotect, dpo, fua, fua_nv, group_no;
6538 
6539 #if 0
6540 			istgt_scsi_dump_cdb(cdb);
6541 #endif
6542 			if (spec->lu->istgt->swmode == ISTGT_SWMODE_TRADITIONAL) {
6543 				/* INVALID COMMAND OPERATION CODE */
6544 				BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
6545 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6546 				break;
6547 			}
6548 			if (spec->rsv_key) {
6549 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6550 				if (rc != 0) {
6551 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6552 					break;
6553 				}
6554 			}
6555 
6556 			if (lu_cmd->W_bit == 0) {
6557 				ISTGT_ERRLOG("W_bit == 0\n");
6558 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6559 				return -1;
6560 			}
6561 
6562 			wprotect = BGET8W(&cdb[1], 7, 3);
6563 			dpo = BGET8(&cdb[1], 4);
6564 			fua = BGET8(&cdb[1], 3);
6565 			fua_nv = BGET8(&cdb[1], 1);
6566 			lba = (uint64_t) DGET64(&cdb[2]);
6567 			transfer_len = (uint32_t) DGET8(&cdb[13]);
6568 			group_no = BGET8W(&cdb[14], 4, 5);
6569 
6570 			maxlen = ISTGT_LU_WORK_ATS_BLOCK_SIZE / spec->blocklen;
6571 			if (maxlen > 0xff) {
6572 				maxlen = 0xff;
6573 			}
6574 			if (transfer_len > maxlen) {
6575 				/* INVALID FIELD IN CDB */
6576 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
6577 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6578 				break;
6579 			}
6580 
6581 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6582 			    "COMPARE_AND_WRITE(lba %"PRIu64", len %u blocks)\n",
6583 			    lba, transfer_len);
6584 			rc = istgt_lu_disk_lbwrite_ats(spec, conn, lu_cmd, lba, transfer_len);
6585 			if (rc < 0) {
6586 				//ISTGT_ERRLOG("lu_disk_lbwrite_ats() failed\n");
6587 				/* sense data build by function */
6588 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6589 				break;
6590 			}
6591 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6592 			break;
6593 		}
6594 
6595 	case SBC_SYNCHRONIZE_CACHE_10:
6596 		{
6597 			int sync_nv, immed;
6598 
6599 			if (spec->rsv_key) {
6600 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6601 				if (rc != 0) {
6602 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6603 					break;
6604 				}
6605 			}
6606 
6607 			sync_nv = BGET8(&cdb[1], 2);
6608 			immed = BGET8(&cdb[1], 1);
6609 			lba = (uint64_t) DGET32(&cdb[2]);
6610 			len = (uint32_t) DGET16(&cdb[7]);
6611 			if (len == 0) {
6612 				len = spec->blockcnt;
6613 			}
6614 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6615 			    "SYNCHRONIZE_CACHE_10(lba %"PRIu64
6616 			    ", len %u blocks)\n",
6617 			    lba, len);
6618 			rc = istgt_lu_disk_lbsync(spec, conn, lu_cmd, lba, len);
6619 			if (rc < 0) {
6620 				ISTGT_ERRLOG("lu_disk_lbsync() failed\n");
6621 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6622 				break;
6623 			}
6624 			lu_cmd->data_len = 0;
6625 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6626 			break;
6627 		}
6628 
6629 	case SBC_SYNCHRONIZE_CACHE_16:
6630 		{
6631 			int sync_nv, immed;
6632 
6633 			if (spec->rsv_key) {
6634 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6635 				if (rc != 0) {
6636 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6637 					break;
6638 				}
6639 			}
6640 
6641 			sync_nv = BGET8(&cdb[1], 2);
6642 			immed = BGET8(&cdb[1], 1);
6643 			lba = (uint64_t) DGET64(&cdb[2]);
6644 			len = (uint32_t) DGET32(&cdb[10]);
6645 			if (len == 0) {
6646 				len = spec->blockcnt;
6647 			}
6648 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6649 			    "SYNCHRONIZE_CACHE_16(lba %"PRIu64
6650 			    ", len %u blocks)\n",
6651 			    lba, len);
6652 			rc = istgt_lu_disk_lbsync(spec, conn, lu_cmd, lba, len);
6653 			if (rc < 0) {
6654 				ISTGT_ERRLOG("lu_disk_lbsync() failed\n");
6655 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6656 				break;
6657 			}
6658 			lu_cmd->data_len = 0;
6659 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6660 			break;
6661 		}
6662 
6663 	case SBC_READ_DEFECT_DATA_10:
6664 		{
6665 			int req_plist, req_glist, list_format;
6666 
6667 			if (lu_cmd->R_bit == 0) {
6668 				ISTGT_ERRLOG("R_bit == 0\n");
6669 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6670 				return -1;
6671 			}
6672 
6673 			req_plist = BGET8(&cdb[2], 4);
6674 			req_glist = BGET8(&cdb[2], 3);
6675 			list_format = BGET8W(&cdb[2], 2, 3);
6676 
6677 			allocation_len = (uint32_t) DGET16(&cdb[7]);
6678 			if (allocation_len > (size_t) data_alloc_len) {
6679 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
6680 				    data_alloc_len);
6681 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6682 				return -1;
6683 			}
6684 			memset(data, 0, allocation_len);
6685 
6686 			data_len = istgt_lu_disk_scsi_read_defect10(spec, conn, cdb,
6687 			    req_plist, req_glist, list_format, data, data_alloc_len);
6688 			if (data_len < 0) {
6689 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6690 				break;
6691 			}
6692 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
6693 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6694 			break;
6695 		}
6696 
6697 	case SBC_READ_DEFECT_DATA_12:
6698 		{
6699 			int req_plist, req_glist, list_format;
6700 
6701 			if (lu_cmd->R_bit == 0) {
6702 				ISTGT_ERRLOG("R_bit == 0\n");
6703 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6704 				return -1;
6705 			}
6706 
6707 			req_plist = BGET8(&cdb[2], 4);
6708 			req_glist = BGET8(&cdb[2], 3);
6709 			list_format = BGET8W(&cdb[2], 2, 3);
6710 
6711 			allocation_len = DGET32(&cdb[6]);
6712 			if (allocation_len > (size_t) data_alloc_len) {
6713 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
6714 				    data_alloc_len);
6715 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6716 				return -1;
6717 			}
6718 			memset(data, 0, allocation_len);
6719 
6720 			data_len = istgt_lu_disk_scsi_read_defect12(spec, conn, cdb,
6721 			    req_plist, req_glist, list_format, data, data_alloc_len);
6722 			if (data_len < 0) {
6723 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6724 				break;
6725 			}
6726 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
6727 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6728 			break;
6729 		}
6730 
6731 	case SCC_MAINTENANCE_IN:
6732 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MAINTENANCE_IN\n");
6733 		switch (BGET8W(&cdb[1], 4, 5)) { /* SERVICE ACTION */
6734 		case SPC_MI_REPORT_TARGET_PORT_GROUPS:
6735 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT_TARGET_PORT_GROUPS\n");
6736 			if (lu_cmd->R_bit == 0) {
6737 				ISTGT_ERRLOG("R_bit == 0\n");
6738 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6739 				return -1;
6740 			}
6741 			allocation_len = DGET32(&cdb[6]);
6742 			if (allocation_len > (size_t) data_alloc_len) {
6743 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
6744 				    data_alloc_len);
6745 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6746 				return -1;
6747 			}
6748 			memset(data, 0, allocation_len);
6749 			data_len = istgt_lu_disk_scsi_report_target_port_groups(spec, conn, cdb, data, data_alloc_len);
6750 			if (data_len < 0) {
6751 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6752 				break;
6753 			}
6754 			ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG,
6755 			    "REPORT_TARGET_PORT_GROUPS", data, data_len);
6756 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
6757 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6758 			break;
6759 		default:
6760 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SA=0x%2.2x\n",
6761 			    BGET8W(&cdb[1], 4, 5));
6762 			/* INVALID COMMAND OPERATION CODE */
6763 			BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
6764 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6765 			break;
6766 		}
6767 		break;
6768 
6769 	case SCC_MAINTENANCE_OUT:
6770 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MAINTENANCE_OUT\n");
6771 		switch (BGET8W(&cdb[1], 4, 5)) { /* SERVICE ACTION */
6772 		case SPC_MO_SET_TARGET_PORT_GROUPS:
6773 			ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SET_TARGET_PORT_GROUPS\n");
6774 			if (spec->rsv_key) {
6775 				rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0));
6776 				if (rc != 0) {
6777 					lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT;
6778 					break;
6779 				}
6780 			}
6781 			if (lu_cmd->W_bit == 0) {
6782 				ISTGT_ERRLOG("W_bit == 0\n");
6783 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6784 				return -1;
6785 			}
6786 			parameter_len = DGET32(&cdb[6]);
6787 			if (parameter_len == 0) {
6788 				lu_cmd->data_len = 0;
6789 				lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6790 				break;
6791 			}
6792 			/* Data-Out */
6793 			rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
6794 			    lu_cmd->iobufsize, parameter_len);
6795 			if (rc < 0) {
6796 				ISTGT_ERRLOG("lu_disk_transfer_data() failed\n");
6797 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6798 				break;
6799 			}
6800 			if (parameter_len < 4) {
6801 				/* INVALID FIELD IN CDB */
6802 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
6803 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6804 				break;
6805 			}
6806 			ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG,
6807 			    "SET_TARGET_PORT_GROUPS",
6808 			    lu_cmd->iobuf, parameter_len);
6809 			data = lu_cmd->iobuf;
6810 			/* data[0]-data[3] Reserved */
6811 			/* Set target port group descriptor(s) */
6812 			data_len = istgt_lu_disk_scsi_set_target_port_groups(spec, conn, cdb, &data[4], parameter_len - 4);
6813 			if (data_len < 0) {
6814 				/* INVALID FIELD IN PARAMETER LIST */
6815 				BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00);
6816 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6817 				break;
6818 			}
6819 			lu_cmd->data_len = parameter_len;
6820 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6821 			break;
6822 		default:
6823 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SA=0x%2.2x\n",
6824 			    BGET8W(&cdb[1], 4, 5));
6825 			/* INVALID COMMAND OPERATION CODE */
6826 			BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
6827 			lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6828 			break;
6829 		}
6830 		break;
6831 
6832 	case SPC_PERSISTENT_RESERVE_IN:
6833 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PERSISTENT_RESERVE_IN\n");
6834 		{
6835 			int sa;
6836 
6837 			if (lu_cmd->R_bit == 0) {
6838 				ISTGT_ERRLOG("R_bit == 0\n");
6839 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6840 				return -1;
6841 			}
6842 
6843 			sa = BGET8W(&cdb[1], 4, 5);
6844 			allocation_len = DGET16(&cdb[7]);
6845 			if (allocation_len > (size_t) data_alloc_len) {
6846 				ISTGT_ERRLOG("data_alloc_len(%d) too small\n",
6847 				    data_alloc_len);
6848 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6849 				return -1;
6850 			}
6851 			memset(data, 0, allocation_len);
6852 
6853 			data_len = istgt_lu_disk_scsi_persistent_reserve_in(spec, conn, lu_cmd, sa, data, allocation_len);
6854 			if (data_len < 0) {
6855 				/* status build by function */
6856 				break;
6857 			}
6858 			ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG,
6859 			    "PERSISTENT_RESERVE_IN", data, data_len);
6860 			lu_cmd->data_len = DMIN32((size_t)data_len, lu_cmd->transfer_len);
6861 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6862 		}
6863 		break;
6864 
6865 	case SPC_PERSISTENT_RESERVE_OUT:
6866 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PERSISTENT_RESERVE_OUT\n");
6867 		{
6868 			int sa, scope, type;
6869 
6870 			if (lu_cmd->W_bit == 0) {
6871 				ISTGT_ERRLOG("W_bit == 0\n");
6872 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6873 				return -1;
6874 			}
6875 
6876 			sa = BGET8W(&cdb[1], 4, 5);
6877 			scope = BGET8W(&cdb[2], 7, 4);
6878 			type = BGET8W(&cdb[2], 3, 4);
6879 			parameter_len = DGET32(&cdb[5]);
6880 
6881 			/* Data-Out */
6882 			rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf,
6883 			    lu_cmd->iobufsize, parameter_len);
6884 			if (rc < 0) {
6885 				ISTGT_ERRLOG("lu_disk_transfer_data() failed\n");
6886 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6887 				break;
6888 			}
6889 			if (parameter_len < 24) {
6890 				/* INVALID FIELD IN CDB */
6891 				BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00);
6892 				lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6893 				break;
6894 			}
6895 
6896 			ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG,
6897 			    "PERSISTENT_RESERVE_OUT",
6898 			    lu_cmd->iobuf, parameter_len);
6899 			data = lu_cmd->iobuf;
6900 
6901 			data_len = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, lu_cmd, sa, scope, type, &data[0], parameter_len);
6902 			if (data_len < 0) {
6903 				/* status build by function */
6904 				break;
6905 			}
6906 			lu_cmd->data_len = parameter_len;
6907 			lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6908 		}
6909 		break;
6910 
6911 	/* XXX TODO: fix */
6912 	case 0x85: /* ATA PASS-THROUGH(16) */
6913 	case 0xA1: /* ATA PASS-THROUGH(12) */
6914 		/* INVALID COMMAND OPERATION CODE */
6915 		BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
6916 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6917 		break;
6918 	case SPC_EXTENDED_COPY:
6919 		/* INVALID COMMAND OPERATION CODE */
6920 		BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
6921 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6922 		break;
6923 	case SPC2_RELEASE_6:
6924 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_6\n");
6925 		rc = istgt_lu_disk_scsi_release(spec, conn, lu_cmd);
6926 		if (rc < 0) {
6927 			/* build by function */
6928 			break;
6929 		}
6930 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6931 		break;
6932 	case SPC2_RELEASE_10:
6933 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_10\n");
6934 		rc = istgt_lu_disk_scsi_release(spec, conn, lu_cmd);
6935 		if (rc < 0) {
6936 			/* build by function */
6937 			break;
6938 		}
6939 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6940 		break;
6941 	case SPC2_RESERVE_6:
6942 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_6\n");
6943 		rc = istgt_lu_disk_scsi_reserve(spec, conn, lu_cmd);
6944 		if (rc < 0) {
6945 			/* build by function */
6946 			break;
6947 		}
6948 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6949 		break;
6950 	case SPC2_RESERVE_10:
6951 		ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_10\n");
6952 		rc = istgt_lu_disk_scsi_reserve(spec, conn, lu_cmd);
6953 		if (rc < 0) {
6954 			/* build by function */
6955 			break;
6956 		}
6957 		lu_cmd->status = ISTGT_SCSI_STATUS_GOOD;
6958 		break;
6959 
6960 	default:
6961 		ISTGT_ERRLOG("unsupported SCSI OP=0x%x\n", cdb[0]);
6962 		/* INVALID COMMAND OPERATION CODE */
6963 		BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00);
6964 		lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION;
6965 		break;
6966 	}
6967 
6968 	ISTGT_TRACELOG(ISTGT_TRACE_SCSI,
6969 	    "SCSI OP=0x%x, LUN=0x%16.16"PRIx64" status=0x%x,"
6970 	    " complete\n",
6971 	    cdb[0], lu_cmd->lun, lu_cmd->status);
6972 	return 0;
6973 }
6974