Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 16 Jun 2014 11:00:15 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r267537 - in head/sys/cam: ctl scsi
Message-ID:  <201406161100.s5GB0FxY035141@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Mon Jun 16 11:00:14 2014
New Revision: 267537
URL: http://svnweb.freebsd.org/changeset/base/267537

Log:
  Add support for VERIFY(10/12/16) and COMPARE AND WRITE SCSI commands.
  
  Make data_submit backends method support not only read and write requests,
  but also two new ones: verify and compare.  Verify just checks readability
  of the data in specified location without transferring them outside.
  Compare reads the specified data and compares them to received data,
  returning error if they are different.
  
  VERIFY(10/12/16) commands request either verify or compare from backend,
  depending on BYTCHK CDB field.  COMPARE AND WRITE command executed in two
  stages: first it requests compare, and then, if succeesed, requests write.
  Atomicity of operation is guarantied by CTL request ordering code.
  
  MFC after:	2 weeks
  Sponsored by:	iXsystems, Inc.

Modified:
  head/sys/cam/ctl/ctl.c
  head/sys/cam/ctl/ctl.h
  head/sys/cam/ctl/ctl_backend_block.c
  head/sys/cam/ctl/ctl_backend_ramdisk.c
  head/sys/cam/ctl/ctl_cmd_table.c
  head/sys/cam/ctl/ctl_io.h
  head/sys/cam/ctl/ctl_private.h
  head/sys/cam/scsi/scsi_all.c
  head/sys/cam/scsi/scsi_all.h
  head/sys/cam/scsi/scsi_da.h

Modified: head/sys/cam/ctl/ctl.c
==============================================================================
--- head/sys/cam/ctl/ctl.c	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/ctl/ctl.c	Mon Jun 16 11:00:14 2014	(r267537)
@@ -4996,6 +4996,30 @@ bailout:
 
 /*
  * This gets called by a backend driver when it is done with a
+ * data_submit method.
+ */
+void
+ctl_data_submit_done(union ctl_io *io)
+{
+	/*
+	 * If the IO_CONT flag is set, we need to call the supplied
+	 * function to continue processing the I/O, instead of completing
+	 * the I/O just yet.
+	 *
+	 * If there is an error, though, we don't want to keep processing.
+	 * Instead, just send status back to the initiator.
+	 */
+	if ((io->io_hdr.flags & CTL_FLAG_IO_CONT)
+	 && (((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE)
+	  || ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS))) {
+		io->scsiio.io_cont(io);
+		return;
+	}
+	ctl_done(io);
+}
+
+/*
+ * This gets called by a backend driver when it is done with a
  * configuration write.
  */
 void
@@ -8582,7 +8606,7 @@ int
 ctl_read_write(struct ctl_scsiio *ctsio)
 {
 	struct ctl_lun *lun;
-	struct ctl_lba_len lbalen;
+	struct ctl_lba_len_flags *lbalen;
 	uint64_t lba;
 	uint32_t num_blocks;
 	int reladdr, fua, dpo, ebp;
@@ -8793,10 +8817,11 @@ ctl_read_write(struct ctl_scsiio *ctsio)
 		return (CTL_RETVAL_COMPLETE);
 	}
 
-	lbalen.lba = lba;
-	lbalen.len = num_blocks;
-	memcpy(ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, &lbalen,
-	       sizeof(lbalen));
+	lbalen = (struct ctl_lba_len_flags *)
+	    &ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
+	lbalen->lba = lba;
+	lbalen->len = num_blocks;
+	lbalen->flags = isread ? CTL_LLF_READ : CTL_LLF_WRITE;
 
 	ctsio->kern_total_len = num_blocks * lun->be_lun->blocksize;
 	ctsio->kern_rel_offset = 0;
@@ -8808,6 +8833,228 @@ ctl_read_write(struct ctl_scsiio *ctsio)
 	return (retval);
 }
 
+static int
+ctl_cnw_cont(union ctl_io *io)
+{
+	struct ctl_scsiio *ctsio;
+	struct ctl_lun *lun;
+	struct ctl_lba_len_flags *lbalen;
+	int retval;
+
+	ctsio = &io->scsiio;
+	ctsio->io_hdr.status = CTL_STATUS_NONE;
+	ctsio->io_hdr.flags &= ~CTL_FLAG_IO_CONT;
+	lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+	lbalen = (struct ctl_lba_len_flags *)
+	    &ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
+	lbalen->flags = CTL_LLF_WRITE;
+
+	CTL_DEBUG_PRINT(("ctl_cnw_cont: calling data_submit()\n"));
+	retval = lun->backend->data_submit((union ctl_io *)ctsio);
+	return (retval);
+}
+
+int
+ctl_cnw(struct ctl_scsiio *ctsio)
+{
+	struct ctl_lun *lun;
+	struct ctl_lba_len_flags *lbalen;
+	uint64_t lba;
+	uint32_t num_blocks;
+	int fua, dpo;
+	int retval;
+
+	lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+	CTL_DEBUG_PRINT(("ctl_cnw: command: %#x\n", ctsio->cdb[0]));
+
+	fua = 0;
+	dpo = 0;
+
+	retval = CTL_RETVAL_COMPLETE;
+
+	switch (ctsio->cdb[0]) {
+	case COMPARE_AND_WRITE: {
+		struct scsi_compare_and_write *cdb;
+
+		cdb = (struct scsi_compare_and_write *)ctsio->cdb;
+
+		if (cdb->byte2 & SRW10_FUA)
+			fua = 1;
+		if (cdb->byte2 & SRW10_DPO)
+			dpo = 1;
+		lba = scsi_8btou64(cdb->addr);
+		num_blocks = cdb->length;
+		break;
+	}
+	default:
+		/*
+		 * We got a command we don't support.  This shouldn't
+		 * happen, commands should be filtered out above us.
+		 */
+		ctl_set_invalid_opcode(ctsio);
+		ctl_done((union ctl_io *)ctsio);
+
+		return (CTL_RETVAL_COMPLETE);
+		break; /* NOTREACHED */
+	}
+
+	/*
+	 * XXX KDM what do we do with the DPO and FUA bits?  FUA might be
+	 * interesting for us, but if RAIDCore is in write-back mode,
+	 * getting it to do write-through for a particular transaction may
+	 * not be possible.
+	 */
+
+	/*
+	 * The first check is to make sure we're in bounds, the second
+	 * check is to catch wrap-around problems.  If the lba + num blocks
+	 * is less than the lba, then we've wrapped around and the block
+	 * range is invalid anyway.
+	 */
+	if (((lba + num_blocks) > (lun->be_lun->maxlba + 1))
+	 || ((lba + num_blocks) < lba)) {
+		ctl_set_lba_out_of_range(ctsio);
+		ctl_done((union ctl_io *)ctsio);
+		return (CTL_RETVAL_COMPLETE);
+	}
+
+	/*
+	 * According to SBC-3, a transfer length of 0 is not an error.
+	 */
+	if (num_blocks == 0) {
+		ctl_set_success(ctsio);
+		ctl_done((union ctl_io *)ctsio);
+		return (CTL_RETVAL_COMPLETE);
+	}
+
+	ctsio->kern_total_len = 2 * num_blocks * lun->be_lun->blocksize;
+	ctsio->kern_rel_offset = 0;
+
+	/*
+	 * Set the IO_CONT flag, so that if this I/O gets passed to
+	 * ctl_data_submit_done(), it'll get passed back to
+	 * ctl_ctl_cnw_cont() for further processing.
+	 */
+	ctsio->io_hdr.flags |= CTL_FLAG_IO_CONT;
+	ctsio->io_cont = ctl_cnw_cont;
+
+	lbalen = (struct ctl_lba_len_flags *)
+	    &ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
+	lbalen->lba = lba;
+	lbalen->len = num_blocks;
+	lbalen->flags = CTL_LLF_COMPARE;
+
+	CTL_DEBUG_PRINT(("ctl_cnw: calling data_submit()\n"));
+	retval = lun->backend->data_submit((union ctl_io *)ctsio);
+	return (retval);
+}
+
+int
+ctl_verify(struct ctl_scsiio *ctsio)
+{
+	struct ctl_lun *lun;
+	struct ctl_lba_len_flags *lbalen;
+	uint64_t lba;
+	uint32_t num_blocks;
+	int bytchk, dpo;
+	int retval;
+
+	lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+
+	CTL_DEBUG_PRINT(("ctl_verify: command: %#x\n", ctsio->cdb[0]));
+
+	bytchk = 0;
+	dpo = 0;
+	retval = CTL_RETVAL_COMPLETE;
+
+	switch (ctsio->cdb[0]) {
+	case VERIFY_10: {
+		struct scsi_verify_10 *cdb;
+
+		cdb = (struct scsi_verify_10 *)ctsio->cdb;
+		if (cdb->byte2 & SVFY_BYTCHK)
+			bytchk = 1;
+		if (cdb->byte2 & SVFY_DPO)
+			dpo = 1;
+		lba = scsi_4btoul(cdb->addr);
+		num_blocks = scsi_2btoul(cdb->length);
+		break;
+	}
+	case VERIFY_12: {
+		struct scsi_verify_12 *cdb;
+
+		cdb = (struct scsi_verify_12 *)ctsio->cdb;
+		if (cdb->byte2 & SVFY_BYTCHK)
+			bytchk = 1;
+		if (cdb->byte2 & SVFY_DPO)
+			dpo = 1;
+		lba = scsi_4btoul(cdb->addr);
+		num_blocks = scsi_4btoul(cdb->length);
+		break;
+	}
+	case VERIFY_16: {
+		struct scsi_rw_16 *cdb;
+
+		cdb = (struct scsi_rw_16 *)ctsio->cdb;
+		if (cdb->byte2 & SVFY_BYTCHK)
+			bytchk = 1;
+		if (cdb->byte2 & SVFY_DPO)
+			dpo = 1;
+		lba = scsi_8btou64(cdb->addr);
+		num_blocks = scsi_4btoul(cdb->length);
+		break;
+	}
+	default:
+		/*
+		 * We got a command we don't support.  This shouldn't
+		 * happen, commands should be filtered out above us.
+		 */
+		ctl_set_invalid_opcode(ctsio);
+		ctl_done((union ctl_io *)ctsio);
+		return (CTL_RETVAL_COMPLETE);
+	}
+
+	/*
+	 * The first check is to make sure we're in bounds, the second
+	 * check is to catch wrap-around problems.  If the lba + num blocks
+	 * is less than the lba, then we've wrapped around and the block
+	 * range is invalid anyway.
+	 */
+	if (((lba + num_blocks) > (lun->be_lun->maxlba + 1))
+	 || ((lba + num_blocks) < lba)) {
+		ctl_set_lba_out_of_range(ctsio);
+		ctl_done((union ctl_io *)ctsio);
+		return (CTL_RETVAL_COMPLETE);
+	}
+
+	/*
+	 * According to SBC-3, a transfer length of 0 is not an error.
+	 */
+	if (num_blocks == 0) {
+		ctl_set_success(ctsio);
+		ctl_done((union ctl_io *)ctsio);
+		return (CTL_RETVAL_COMPLETE);
+	}
+
+	lbalen = (struct ctl_lba_len_flags *)
+	    &ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
+	lbalen->lba = lba;
+	lbalen->len = num_blocks;
+	if (bytchk) {
+		lbalen->flags = CTL_LLF_COMPARE;
+		ctsio->kern_total_len = num_blocks * lun->be_lun->blocksize;
+	} else {
+		lbalen->flags = CTL_LLF_VERIFY;
+		ctsio->kern_total_len = 0;
+	}
+	ctsio->kern_rel_offset = 0;
+
+	CTL_DEBUG_PRINT(("ctl_verify: calling data_submit()\n"));
+	retval = lun->backend->data_submit((union ctl_io *)ctsio);
+	return (retval);
+}
+
 int
 ctl_report_luns(struct ctl_scsiio *ctsio)
 {
@@ -9526,6 +9773,7 @@ ctl_inquiry_evpd_block_limits(struct ctl
 
 	bl_ptr->page_code = SVPD_BLOCK_LIMITS;
 	scsi_ulto2b(sizeof(*bl_ptr), bl_ptr->page_length);
+	bl_ptr->max_cmp_write_len = 0xff;
 	scsi_ulto4b(0xffffffff, bl_ptr->max_txfer_len);
 	scsi_ulto4b(MAXPHYS / bs, bl_ptr->opt_txfer_len);
 	if (lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) {
@@ -9937,6 +10185,15 @@ ctl_get_lba_len(union ctl_io *io, uint64
 		return (1);
 
 	switch (io->scsiio.cdb[0]) {
+	case COMPARE_AND_WRITE: {
+		struct scsi_compare_and_write *cdb;
+
+		cdb = (struct scsi_compare_and_write *)io->scsiio.cdb;
+
+		*lba = scsi_8btou64(cdb->addr);
+		*len = cdb->length;
+		break;
+	}
 	case READ_6:
 	case WRITE_6: {
 		struct scsi_rw_6 *cdb;
@@ -10025,6 +10282,33 @@ ctl_get_lba_len(union ctl_io *io, uint64
 		*len = scsi_4btoul(cdb->length);
 		break;
 	}
+	case VERIFY_10: {
+		struct scsi_verify_10 *cdb;
+
+		cdb = (struct scsi_verify_10 *)io->scsiio.cdb;
+
+		*lba = scsi_4btoul(cdb->addr);
+		*len = scsi_2btoul(cdb->length);
+		break;
+	}
+	case VERIFY_12: {
+		struct scsi_verify_12 *cdb;
+
+		cdb = (struct scsi_verify_12 *)io->scsiio.cdb;
+
+		*lba = scsi_4btoul(cdb->addr);
+		*len = scsi_4btoul(cdb->length);
+		break;
+	}
+	case VERIFY_16: {
+		struct scsi_verify_16 *cdb;
+
+		cdb = (struct scsi_verify_16 *)io->scsiio.cdb;
+
+		*lba = scsi_8btou64(cdb->addr);
+		*len = scsi_4btoul(cdb->length);
+		break;
+	}
 	default:
 		return (1);
 		break; /* NOTREACHED */
@@ -12753,7 +13037,7 @@ ctl_process_done(union ctl_io *io, int h
 		switch (io->io_hdr.io_type) {
 		case CTL_IO_SCSI: {
 			int isread;
-			struct ctl_lba_len *lbalen;
+			struct ctl_lba_len_flags *lbalen;
 
 			isread = 0;
 			switch (io->scsiio.cdb[0]) {
@@ -12770,7 +13054,7 @@ ctl_process_done(union ctl_io *io, int h
 			case WRITE_VERIFY_10:
 			case WRITE_VERIFY_12:
 			case WRITE_VERIFY_16:
-				lbalen = (struct ctl_lba_len *)
+				lbalen = (struct ctl_lba_len_flags *)
 				    &io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
 
 				if (isread) {

Modified: head/sys/cam/ctl/ctl.h
==============================================================================
--- head/sys/cam/ctl/ctl.h	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/ctl/ctl.h	Mon Jun 16 11:00:14 2014	(r267537)
@@ -195,6 +195,7 @@ int ctl_debugconf_sp_select_handler(stru
 int ctl_config_move_done(union ctl_io *io);
 void ctl_datamove(union ctl_io *io);
 void ctl_done(union ctl_io *io);
+void ctl_data_submit_done(union ctl_io *io);
 void ctl_config_write_done(union ctl_io *io);
 #if 0
 int ctl_thread(void *arg);

Modified: head/sys/cam/ctl/ctl_backend_block.c
==============================================================================
--- head/sys/cam/ctl/ctl_backend_block.c	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/ctl/ctl_backend_block.c	Mon Jun 16 11:00:14 2014	(r267537)
@@ -92,9 +92,11 @@ __FBSDID("$FreeBSD$");
  * The idea here is that we'll allocate enough S/G space to hold a 1MB
  * I/O.  If we get an I/O larger than that, we'll split it.
  */
-#define	CTLBLK_MAX_IO_SIZE	(1024 * 1024)
+#define	CTLBLK_HALF_IO_SIZE	(512 * 1024)
+#define	CTLBLK_MAX_IO_SIZE	(CTLBLK_HALF_IO_SIZE * 2)
 #define	CTLBLK_MAX_SEG		MAXPHYS
-#define	CTLBLK_MAX_SEGS		MAX(CTLBLK_MAX_IO_SIZE / CTLBLK_MAX_SEG, 1)
+#define	CTLBLK_HALF_SEGS	MAX(CTLBLK_HALF_IO_SIZE / CTLBLK_MAX_SEG, 1)
+#define	CTLBLK_MAX_SEGS		(CTLBLK_HALF_SEGS * 2)
 
 #ifdef CTLBLK_DEBUG
 #define DPRINTF(fmt, args...) \
@@ -105,6 +107,8 @@ __FBSDID("$FreeBSD$");
 
 #define PRIV(io)	\
     ((struct ctl_ptr_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_BACKEND])
+#define ARGS(io)	\
+    ((struct ctl_lba_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_LBA_LEN])
 
 SDT_PROVIDER_DEFINE(cbb);
 
@@ -312,6 +316,13 @@ ctl_free_beio(struct ctl_be_block_io *be
 
 		uma_zfree(beio->lun->lun_zone, beio->sg_segs[i].addr);
 		beio->sg_segs[i].addr = NULL;
+
+		/* For compare we had two equal S/G lists. */
+		if (ARGS(beio->io)->flags & CTL_LLF_COMPARE) {
+			uma_zfree(beio->lun->lun_zone,
+			    beio->sg_segs[i + CTLBLK_HALF_SEGS].addr);
+			beio->sg_segs[i + CTLBLK_HALF_SEGS].addr = NULL;
+		}
 	}
 
 	if (duplicate_free > 0) {
@@ -346,7 +357,7 @@ ctl_complete_beio(struct ctl_be_block_io
 		beio->beio_cont(beio);
 	} else {
 		ctl_free_beio(beio);
-		ctl_done(io);
+		ctl_data_submit_done(io);
 	}
 }
 
@@ -355,9 +366,11 @@ ctl_be_block_move_done(union ctl_io *io)
 {
 	struct ctl_be_block_io *beio;
 	struct ctl_be_block_lun *be_lun;
+	struct ctl_lba_len_flags *lbalen;
 #ifdef CTL_TIME_IO
 	struct bintime cur_bt;
-#endif  
+#endif
+	int i;
 
 	beio = (struct ctl_be_block_io *)PRIV(io)->ptr;
 	be_lun = beio->lun;
@@ -370,16 +383,37 @@ ctl_be_block_move_done(union ctl_io *io)
 	bintime_add(&io->io_hdr.dma_bt, &cur_bt);
 	io->io_hdr.num_dmas++;
 #endif  
+	io->scsiio.kern_rel_offset += io->scsiio.kern_data_len;
 
 	/*
 	 * We set status at this point for read commands, and write
 	 * commands with errors.
 	 */
-	if ((beio->bio_cmd == BIO_READ)
-	 && (io->io_hdr.port_status == 0)
-	 && ((io->io_hdr.flags & CTL_FLAG_ABORT) == 0)
-	 && ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE))
-		ctl_set_success(&io->scsiio);
+	if ((io->io_hdr.port_status == 0) &&
+	    ((io->io_hdr.flags & CTL_FLAG_ABORT) == 0) &&
+	    ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE)) {
+		lbalen = ARGS(beio->io);
+		if (lbalen->flags & CTL_LLF_READ) {
+			ctl_set_success(&io->scsiio);
+		} else if (lbalen->flags & CTL_LLF_COMPARE) {
+			/* We have two data blocks ready for comparison. */
+			for (i = 0; i < beio->num_segs; i++) {
+				if (memcmp(beio->sg_segs[i].addr,
+				    beio->sg_segs[i + CTLBLK_HALF_SEGS].addr,
+				    beio->sg_segs[i].len) != 0)
+					break;
+			}
+			if (i < beio->num_segs)
+				ctl_set_sense(&io->scsiio,
+				    /*current_error*/ 1,
+				    /*sense_key*/ SSD_KEY_MISCOMPARE,
+				    /*asc*/ 0x1D,
+				    /*ascq*/ 0x00,
+				    SSD_ELEM_NONE);
+			else
+				ctl_set_success(&io->scsiio);
+		}
+	}
 	else if ((io->io_hdr.port_status != 0)
 	      && ((io->io_hdr.flags & CTL_FLAG_ABORT) == 0)
 	      && ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE)) {
@@ -491,12 +525,13 @@ ctl_be_block_biodone(struct bio *bio)
 	}
 
 	/*
-	 * If this is a write, a flush or a delete, we're all done.
+	 * If this is a write, a flush, a delete or verify, we're all done.
 	 * If this is a read, we can now send the data to the user.
 	 */
 	if ((beio->bio_cmd == BIO_WRITE)
 	 || (beio->bio_cmd == BIO_FLUSH)
-	 || (beio->bio_cmd == BIO_DELETE)) {
+	 || (beio->bio_cmd == BIO_DELETE)
+	 || (ARGS(io)->flags & CTL_LLF_VERIFY)) {
 		ctl_set_success(&io->scsiio);
 		ctl_complete_beio(beio);
 	} else {
@@ -572,18 +607,14 @@ ctl_be_block_dispatch_file(struct ctl_be
 	io = beio->io;
 	flags = beio->bio_flags;
 
+	bzero(&xuio, sizeof(xuio));
 	if (beio->bio_cmd == BIO_READ) {
 		SDT_PROBE(cbb, kernel, read, file_start, 0, 0, 0, 0, 0);
+		xuio.uio_rw = UIO_READ;
 	} else {
 		SDT_PROBE(cbb, kernel, write, file_start, 0, 0, 0, 0, 0);
-	}
-
-	bzero(&xuio, sizeof(xuio));
-	if (beio->bio_cmd == BIO_READ)
-		xuio.uio_rw = UIO_READ;
-	else
 		xuio.uio_rw = UIO_WRITE;
-
+	}
 	xuio.uio_offset = beio->io_offset;
 	xuio.uio_resid = beio->io_len;
 	xuio.uio_segflg = UIO_SYSSPACE;
@@ -626,6 +657,7 @@ ctl_be_block_dispatch_file(struct ctl_be
 				 (IO_DIRECT|IO_SYNC) : 0, file_data->cred);
 
 		VOP_UNLOCK(be_lun->vn, 0);
+		SDT_PROBE(cbb, kernel, read, file_done, 0, 0, 0, 0, 0);
 	} else {
 		struct mount *mountpoint;
 		int lock_flags;
@@ -667,6 +699,7 @@ ctl_be_block_dispatch_file(struct ctl_be
 		VOP_UNLOCK(be_lun->vn, 0);
 
 		vn_finished_write(mountpoint);
+		SDT_PROBE(cbb, kernel, write, file_done, 0, 0, 0, 0, 0);
         }
 
 	/*
@@ -693,12 +726,10 @@ ctl_be_block_dispatch_file(struct ctl_be
 	 * If this is a write, we're all done.
 	 * If this is a read, we can now send the data to the user.
 	 */
-	if (beio->bio_cmd == BIO_WRITE) {
+	if (ARGS(io)->flags & (CTL_LLF_WRITE | CTL_LLF_VERIFY)) {
 		ctl_set_success(&io->scsiio);
-		SDT_PROBE(cbb, kernel, write, file_done, 0, 0, 0, 0, 0);
 		ctl_complete_beio(beio);
 	} else {
-		SDT_PROBE(cbb, kernel, read, file_done, 0, 0, 0, 0, 0);
 #ifdef CTL_TIME_IO
         	getbintime(&io->io_hdr.dma_start_bt);
 #endif  
@@ -935,7 +966,7 @@ ctl_be_block_cw_dispatch_ws(struct ctl_b
 
 	beio = (struct ctl_be_block_io *)PRIV(io)->ptr;
 	softc = be_lun->softc;
-	lbalen = (struct ctl_lba_len_flags *)&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
+	lbalen = ARGS(beio->io);
 
 	if (lbalen->flags & ~(SWS_LBDATA | SWS_UNMAP) ||
 	    (lbalen->flags & SWS_UNMAP && be_lun->unmap == NULL)) {
@@ -1155,11 +1186,10 @@ ctl_be_block_next(struct ctl_be_block_io
 	ctl_free_beio(beio);
 	if (((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE)
 	  && ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)) {
-		ctl_done(io);
+		ctl_data_submit_done(io);
 		return;
 	}
 
-	io->scsiio.kern_rel_offset += io->scsiio.kern_data_len;
 	io->io_hdr.status &= ~CTL_STATUS_MASK;
 	io->io_hdr.status |= CTL_STATUS_NONE;
 
@@ -1181,7 +1211,7 @@ ctl_be_block_dispatch(struct ctl_be_bloc
 {
 	struct ctl_be_block_io *beio;
 	struct ctl_be_block_softc *softc;
-	struct ctl_lba_len *lbalen;
+	struct ctl_lba_len_flags *lbalen;
 	struct ctl_ptr_len_flags *bptrlen;
 	uint64_t len_left, lbas;
 	int i;
@@ -1190,10 +1220,11 @@ ctl_be_block_dispatch(struct ctl_be_bloc
 
 	DPRINTF("entered\n");
 
-	if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN) {
-		SDT_PROBE(cbb, kernel, read, start, 0, 0, 0, 0, 0);
-	} else {
+	lbalen = ARGS(io);
+	if (lbalen->flags & CTL_LLF_WRITE) {
 		SDT_PROBE(cbb, kernel, write, start, 0, 0, 0, 0, 0);
+	} else {
+		SDT_PROBE(cbb, kernel, read, start, 0, 0, 0, 0, 0);
 	}
 
 	beio = ctl_alloc_beio(softc);
@@ -1231,24 +1262,22 @@ ctl_be_block_dispatch(struct ctl_be_bloc
 		break;
 	}
 
-	/*
-	 * This path handles read and write only.  The config write path
-	 * handles flush operations.
-	 */
-	if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN) {
-		beio->bio_cmd = BIO_READ;
-		beio->ds_trans_type = DEVSTAT_READ;
-	} else {
+	if (lbalen->flags & CTL_LLF_WRITE) {
 		beio->bio_cmd = BIO_WRITE;
 		beio->ds_trans_type = DEVSTAT_WRITE;
+	} else {
+		beio->bio_cmd = BIO_READ;
+		beio->ds_trans_type = DEVSTAT_READ;
 	}
 
-	lbalen = (struct ctl_lba_len *)&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
 	DPRINTF("%s at LBA %jx len %u @%ju\n",
 	       (beio->bio_cmd == BIO_READ) ? "READ" : "WRITE",
 	       (uintmax_t)lbalen->lba, lbalen->len, bptrlen->len);
-	lbas = MIN(lbalen->len - bptrlen->len,
-	    CTLBLK_MAX_IO_SIZE / be_lun->blocksize);
+	if (lbalen->flags & CTL_LLF_COMPARE)
+		lbas = CTLBLK_HALF_IO_SIZE;
+	else
+		lbas = CTLBLK_MAX_IO_SIZE;
+	lbas = MIN(lbalen->len - bptrlen->len, lbas / be_lun->blocksize);
 	beio->io_offset = (lbalen->lba + bptrlen->len) * be_lun->blocksize;
 	beio->io_len = lbas * be_lun->blocksize;
 	bptrlen->len += lbas;
@@ -1266,13 +1295,25 @@ ctl_be_block_dispatch(struct ctl_be_bloc
 		DPRINTF("segment %d addr %p len %zd\n", i,
 			beio->sg_segs[i].addr, beio->sg_segs[i].len);
 
+		/* Set up second segment for compare operation. */
+		if (lbalen->flags & CTL_LLF_COMPARE) {
+			beio->sg_segs[i + CTLBLK_HALF_SEGS].len =
+			    beio->sg_segs[i].len;
+			beio->sg_segs[i + CTLBLK_HALF_SEGS].addr =
+			    uma_zalloc(be_lun->lun_zone, M_WAITOK);
+		}
+
 		beio->num_segs++;
 		len_left -= beio->sg_segs[i].len;
 	}
 	if (bptrlen->len < lbalen->len)
 		beio->beio_cont = ctl_be_block_next;
 	io->scsiio.be_move_done = ctl_be_block_move_done;
-	io->scsiio.kern_data_ptr = (uint8_t *)beio->sg_segs;
+	/* For compare we have separate S/G lists for read and datamove. */
+	if (lbalen->flags & CTL_LLF_COMPARE)
+		io->scsiio.kern_data_ptr = (uint8_t *)&beio->sg_segs[CTLBLK_HALF_SEGS];
+	else
+		io->scsiio.kern_data_ptr = (uint8_t *)beio->sg_segs;
 	io->scsiio.kern_data_len = beio->io_len;
 	io->scsiio.kern_data_resid = 0;
 	io->scsiio.kern_sg_entries = beio->num_segs;

Modified: head/sys/cam/ctl/ctl_backend_ramdisk.c
==============================================================================
--- head/sys/cam/ctl/ctl_backend_ramdisk.c	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/ctl/ctl_backend_ramdisk.c	Mon Jun 16 11:00:14 2014	(r267537)
@@ -267,7 +267,7 @@ ctl_backend_ramdisk_move_done(union ctl_
 					 /*retry_count*/
 					 io->io_hdr.port_status);
 	}
-	ctl_done(io);
+	ctl_data_submit_done(io);
 	return(0);
 }
 
@@ -275,11 +275,16 @@ static int
 ctl_backend_ramdisk_submit(union ctl_io *io)
 {
 	struct ctl_be_lun *ctl_be_lun;
-	struct ctl_lba_len *lbalen;
+	struct ctl_lba_len_flags *lbalen;
 
 	ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[
 		CTL_PRIV_BACKEND_LUN].ptr;
-	lbalen = (struct ctl_lba_len *)&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
+	lbalen = (struct ctl_lba_len_flags *)&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
+	if (lbalen->flags & CTL_LLF_VERIFY) {
+		ctl_set_success(&io->scsiio);
+		ctl_data_submit_done(io);
+		return (CTL_RETVAL_COMPLETE);
+	}
 	io->io_hdr.ctl_private[CTL_PRIV_BACKEND].integer =
 	    lbalen->len * ctl_be_lun->blocksize;
 	ctl_backend_ramdisk_continue(io);

Modified: head/sys/cam/ctl/ctl_cmd_table.c
==============================================================================
--- head/sys/cam/ctl/ctl_cmd_table.c	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/ctl/ctl_cmd_table.c	Mon Jun 16 11:00:14 2014	(r267537)
@@ -273,7 +273,10 @@ struct ctl_cmd_entry ctl_cmd_table[] =
  CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
 
 /* 2F VERIFY(10) */
-{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+{ctl_verify, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN |
+			      CTL_FLAG_DATA_OUT |
+			      CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
+ CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
 
 /* 30 SEARCH DATA HIGH(10) */
 {NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
@@ -591,8 +594,9 @@ struct ctl_cmd_entry ctl_cmd_table[] =
                                   CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
  CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
 
-/* 89 */
-{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+/* 89 COMPARE AND WRITE */
+{ctl_cnw, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
+ CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
 
 /* 8A WRITE(16) */
 {ctl_read_write, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN| CTL_FLAG_DATA_OUT,
@@ -612,7 +616,10 @@ struct ctl_cmd_entry ctl_cmd_table[] =
  CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
 
 /* 8F VERIFY(16) */
-{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+{ctl_verify, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN |
+			      CTL_FLAG_DATA_OUT |
+			      CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
+ CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
 
 /* 90 PRE-FETCH(16) */
 {NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
@@ -737,7 +744,10 @@ struct ctl_cmd_entry ctl_cmd_table[] =
  CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE},
 
 /* AF VERIFY(12) */
-{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
+{ctl_verify, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN |
+			      CTL_FLAG_DATA_OUT |
+			      CTL_CMD_FLAG_ALLOW_ON_PR_RESV,
+ CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE},
 
 /* B0 SEARCH DATA HIGH(12) */
 {NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},

Modified: head/sys/cam/ctl/ctl_io.h
==============================================================================
--- head/sys/cam/ctl/ctl_io.h	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/ctl/ctl_io.h	Mon Jun 16 11:00:14 2014	(r267537)
@@ -138,6 +138,10 @@ struct ctl_lba_len_flags {
 	uint64_t lba;
 	uint32_t len;
 	uint32_t flags;
+#define CTL_LLF_READ	0x10000000
+#define CTL_LLF_WRITE	0x20000000
+#define CTL_LLF_VERIFY	0x40000000
+#define CTL_LLF_COMPARE	0x80000000
 };
 
 struct ctl_ptr_len_flags {

Modified: head/sys/cam/ctl/ctl_private.h
==============================================================================
--- head/sys/cam/ctl/ctl_private.h	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/ctl/ctl_private.h	Mon Jun 16 11:00:14 2014	(r267537)
@@ -477,9 +477,11 @@ int ctl_mode_sense(struct ctl_scsiio *ct
 int ctl_read_capacity(struct ctl_scsiio *ctsio);
 int ctl_service_action_in(struct ctl_scsiio *ctsio);
 int ctl_read_write(struct ctl_scsiio *ctsio);
+int ctl_cnw(struct ctl_scsiio *ctsio);
 int ctl_report_luns(struct ctl_scsiio *ctsio);
 int ctl_request_sense(struct ctl_scsiio *ctsio);
 int ctl_tur(struct ctl_scsiio *ctsio);
+int ctl_verify(struct ctl_scsiio *ctsio);
 int ctl_inquiry(struct ctl_scsiio *ctsio);
 int ctl_persistent_reserve_in(struct ctl_scsiio *ctsio);
 int ctl_persistent_reserve_out(struct ctl_scsiio *ctsio);

Modified: head/sys/cam/scsi/scsi_all.c
==============================================================================
--- head/sys/cam/scsi/scsi_all.c	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/scsi/scsi_all.c	Mon Jun 16 11:00:14 2014	(r267537)
@@ -471,7 +471,8 @@ static struct op_table_entry scsi_op_cod
 	 */
 	/* 88  MM  O O   O     READ(16) */
 	{ 0x88,	D | T | W | O | B, "READ(16)" },
-	/* 89 */
+	/* 89  O               COMPARE AND WRITE*/
+	{ 0x89,	D, "COMPARE AND WRITE" },
 	/* 8A  OM  O O   O     WRITE(16) */
 	{ 0x8A,	D | T | W | O | B, "WRITE(16)" },
 	/* 8B  O               ORWRITE */

Modified: head/sys/cam/scsi/scsi_all.h
==============================================================================
--- head/sys/cam/scsi/scsi_all.h	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/scsi/scsi_all.h	Mon Jun 16 11:00:14 2014	(r267537)
@@ -1041,8 +1041,10 @@ struct ata_pass_16 {
 #define	PERSISTENT_RES_OUT	0x5F
 #define	ATA_PASS_16		0x85
 #define	READ_16			0x88
+#define	COMPARE_AND_WRITE	0x89
 #define	WRITE_16		0x8A
 #define	WRITE_VERIFY_16		0x8E
+#define	VERIFY_16		0x8F
 #define	SYNCHRONIZE_CACHE_16	0x91
 #define	WRITE_SAME_16		0x93
 #define	SERVICE_ACTION_IN	0x9E
@@ -1054,6 +1056,7 @@ struct ata_pass_16 {
 #define	READ_12			0xA8
 #define	WRITE_12		0xAA
 #define	WRITE_VERIFY_12		0xAE
+#define	VERIFY_12		0xAF
 #define	READ_ELEMENT_STATUS	0xB8
 #define	READ_CD			0xBE
 

Modified: head/sys/cam/scsi/scsi_da.h
==============================================================================
--- head/sys/cam/scsi/scsi_da.h	Mon Jun 16 08:54:04 2014	(r267536)
+++ head/sys/cam/scsi/scsi_da.h	Mon Jun 16 11:00:14 2014	(r267537)
@@ -222,18 +222,49 @@ struct scsi_read_format_capacities
 	uint8_t	reserved1[3];
 };
 
-struct scsi_verify
+struct scsi_verify_10
 {
-	uint8_t	opcode;		/* VERIFY */
+	uint8_t	opcode;		/* VERIFY(10) */
 	uint8_t	byte2;
 #define	SVFY_LUN_MASK	0xE0
 #define	SVFY_RELADR	0x01
-#define	SVFY_BYTECHK	0x02
+#define	SVFY_BYTCHK	0x02
 #define	SVFY_DPO	0x10
 	uint8_t	addr[4];	/* LBA to begin verification at */
-	uint8_t	reserved0[1];
-	uint8_t	len[2];		/* number of blocks to verify */
-	uint8_t	reserved1[3];
+	uint8_t	group;
+	uint8_t	length[2];		/* number of blocks to verify */
+	uint8_t	control;
+};
+
+struct scsi_verify_12
+{
+	uint8_t	opcode;		/* VERIFY(12) */
+	uint8_t	byte2;
+	uint8_t	addr[4];	/* LBA to begin verification at */
+	uint8_t	length[4];		/* number of blocks to verify */
+	uint8_t	group;
+	uint8_t	control;
+};
+
+struct scsi_verify_16
+{
+	uint8_t	opcode;		/* VERIFY(16) */
+	uint8_t	byte2;
+	uint8_t	addr[8];	/* LBA to begin verification at */
+	uint8_t	length[4];		/* number of blocks to verify */
+	uint8_t	group;
+	uint8_t	control;
+};
+
+struct scsi_compare_and_write
+{
+	uint8_t	opcode;		/* COMPARE AND WRITE */
+	uint8_t	byte2;
+	uint8_t	addr[8];	/* LBA to begin verification at */
+	uint8_t	reserved[3];
+	uint8_t	length;		/* number of blocks */
+	uint8_t	group;
+	uint8_t	control;
 };
 
 struct scsi_write_and_verify



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201406161100.s5GB0FxY035141>