--- old/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c	Mon Aug  5 17:27:17 2019
+++ new/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c	Mon Aug  5 17:27:17 2019
@@ -21,12 +21,12 @@
 
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
  */
 
-#include <sys/sysmacros.h>
 #include <sys/conf.h>
+#include <sys/list.h>
 #include <sys/file.h>
 #include <sys/ddi.h>
 #include <sys/sunddi.h>
@@ -56,6 +56,10 @@
 extern sbd_status_t sbd_pgr_meta_init(sbd_lu_t *sl);
 extern sbd_status_t sbd_pgr_meta_load(sbd_lu_t *sl);
 extern void sbd_pgr_reset(sbd_lu_t *sl);
+extern int HardwareAcceleratedLocking;
+extern int HardwareAcceleratedInit;
+extern int HardwareAcceleratedMove;
+extern uint8_t sbd_unmap_enable;
 
 static int sbd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
     void **result);
@@ -109,9 +113,10 @@
 static kmutex_t		sbd_lock;
 static dev_info_t	*sbd_dip;
 static uint32_t		sbd_lu_count = 0;
+uint8_t sbd_enable_unmap_sync = 0;
 
 /* Global property settings for the logical unit */
-char sbd_vendor_id[]	= "SUN     ";
+char sbd_vendor_id[]	= "NEXENTA ";
 char sbd_product_id[]	= "COMSTAR         ";
 char sbd_revision[]	= "1.0 ";
 char *sbd_mgmt_url = NULL;
@@ -155,7 +160,11 @@
 	NULL			/* power */
 };
 
-#define	SBD_NAME	"COMSTAR SBD"
+#ifdef DEBUG
+#define	SBD_NAME	"COMSTAR SBD+ " __DATE__ " " __TIME__ " DEBUG"
+#else
+#define	SBD_NAME	"COMSTAR SBD+"
+#endif
 
 static struct modldrv modldrv = {
 	&mod_driverops,
@@ -194,6 +203,14 @@
 	}
 	mutex_init(&sbd_lock, NULL, MUTEX_DRIVER, NULL);
 	rw_init(&sbd_global_prop_lock, NULL, RW_DRIVER, NULL);
+
+	if (HardwareAcceleratedLocking == 0)
+		cmn_err(CE_NOTE, "HardwareAcceleratedLocking Disabled");
+	if (HardwareAcceleratedMove == 0)
+		cmn_err(CE_NOTE, "HardwareAcceleratedMove  Disabled");
+	if (HardwareAcceleratedInit == 0)
+		cmn_err(CE_NOTE, "HardwareAcceleratedInit  Disabled");
+
 	return (0);
 }
 
@@ -272,6 +289,8 @@
 static int
 sbd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 {
+	char	*prop;
+
 	switch (cmd) {
 	case DDI_ATTACH:
 		sbd_dip = dip;
@@ -281,6 +300,23 @@
 			break;
 		}
 		ddi_report_dev(dip);
+
+		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
+		    DDI_PROP_DONTPASS, "vendor-id", &prop) == DDI_SUCCESS) {
+			(void) snprintf(sbd_vendor_id, 9, "%s%8s", prop, "");
+			ddi_prop_free(prop);
+		}
+		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
+		    DDI_PROP_DONTPASS, "product-id", &prop) == DDI_SUCCESS) {
+			(void) snprintf(sbd_product_id, 17, "%s%16s", prop, "");
+			ddi_prop_free(prop);
+		}
+		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
+		    DDI_PROP_DONTPASS, "revision", &prop) == DDI_SUCCESS) {
+			(void) snprintf(sbd_revision, 5, "%s%4s", prop, "");
+			ddi_prop_free(prop);
+		}
+
 		return (DDI_SUCCESS);
 	}
 
@@ -318,7 +354,7 @@
 /* ARGSUSED */
 static int
 stmf_sbd_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
-	cred_t *credp, int *rval)
+    cred_t *credp, int *rval)
 {
 	stmf_iocdata_t		*iocd;
 	void			*ibuf	= NULL;
@@ -1396,7 +1432,10 @@
 static void
 do_unmap_setup(sbd_lu_t *sl)
 {
-	ASSERT((sl->sl_flags & SL_UNMAP_ENABLED) == 0);
+	if (sbd_unmap_enable == 0) {
+		sl->sl_flags &= ~(SL_UNMAP_ENABLED);
+		return;
+	}
 
 	if ((sl->sl_flags & SL_ZFS_META) == 0)
 		return;	/* No UNMAP for you. */
@@ -1441,8 +1480,10 @@
 	lu->lu_send_status_done = sbd_send_status_done;
 	lu->lu_task_free = sbd_task_free;
 	lu->lu_abort = sbd_abort;
+	lu->lu_task_poll = sbd_task_poll;
 	lu->lu_dbuf_free = sbd_dbuf_free;
 	lu->lu_ctl = sbd_ctl;
+	lu->lu_task_done = sbd_ats_remove_by_task;
 	lu->lu_info = sbd_info;
 	sl->sl_state = STMF_STATE_OFFLINE;
 
@@ -1455,6 +1496,12 @@
 		return (EIO);
 	}
 
+	/*
+	 * setup the ATS (compare and write) lists to handle multiple
+	 * ATS commands simultaneously
+	 */
+	list_create(&sl->sl_ats_io_list, sizeof (ats_state_t),
+	    offsetof(ats_state_t, as_next));
 	*err_ret = 0;
 	return (0);
 }
@@ -1561,6 +1608,7 @@
 			sl->sl_lu_size = vattr.va_size;
 		}
 	}
+
 	if (sl->sl_lu_size < SBD_MIN_LU_SIZE) {
 		*err_ret = SBD_RET_FILE_SIZE_ERROR;
 		ret = EINVAL;
@@ -1837,7 +1885,7 @@
 		sl->sl_flags |= SL_WRITE_PROTECTED;
 	}
 	if (slu->slu_blksize_valid) {
-		if (!ISP2(slu->slu_blksize) ||
+		if ((slu->slu_blksize & (slu->slu_blksize - 1)) ||
 		    (slu->slu_blksize > (32 * 1024)) ||
 		    (slu->slu_blksize == 0)) {
 			*err_ret = SBD_RET_INVALID_BLKSIZE;
@@ -2997,8 +3045,10 @@
 sbd_data_read(sbd_lu_t *sl, struct scsi_task *task,
     uint64_t offset, uint64_t size, uint8_t *buf)
 {
-	int ret;
+	int ret, ioflag = 0;
 	long resid;
+	hrtime_t xfer_start;
+	uint8_t op = task->task_cdb[0];
 
 	if ((offset + size) > sl->sl_lu_size) {
 		return (SBD_IO_PAST_EOF);
@@ -3006,6 +3056,16 @@
 
 	offset += sl->sl_data_offset;
 
+	/*
+	 * Check to see if the command is READ(10), READ(12), or READ(16).
+	 * If it is then check for bit 3 being set to indicate if Forced
+	 * Unit Access is being requested. If so, the FSYNC flag will be set
+	 * on the read.
+	 */
+	if (((op == SCMD_READ_G1) || (op == SCMD_READ_G4) ||
+	    (op == SCMD_READ_G5)) && (task->task_cdb[1] & BIT_3)) {
+		ioflag = FSYNC;
+	}
 	if ((offset + size) > sl->sl_data_readable_size) {
 		uint64_t store_end;
 		if (offset > sl->sl_data_readable_size) {
@@ -3017,6 +3077,7 @@
 		size = store_end;
 	}
 
+	xfer_start = gethrtime();
 	DTRACE_PROBE5(backing__store__read__start, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset,
 	    scsi_task_t *, task);
@@ -3032,11 +3093,14 @@
 		rw_exit(&sl->sl_access_state_lock);
 		return (SBD_FAILURE);
 	}
+
 	ret = vn_rdwr(UIO_READ, sl->sl_data_vp, (caddr_t)buf, (ssize_t)size,
-	    (offset_t)offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, CRED(),
+	    (offset_t)offset, UIO_SYSSPACE, ioflag, RLIM64_INFINITY, CRED(),
 	    &resid);
 	rw_exit(&sl->sl_access_state_lock);
 
+	stmf_lu_xfer_done(task, B_TRUE /* read */,
+	    (gethrtime() - xfer_start));
 	DTRACE_PROBE6(backing__store__read__end, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset,
 	    int, ret, scsi_task_t *, task);
@@ -3059,6 +3123,9 @@
 	long resid;
 	sbd_status_t sret = SBD_SUCCESS;
 	int ioflag;
+	hrtime_t xfer_start;
+	uint8_t op = task->task_cdb[0];
+	boolean_t fua_bit = B_FALSE;
 
 	if ((offset + size) > sl->sl_lu_size) {
 		return (SBD_IO_PAST_EOF);
@@ -3066,13 +3133,24 @@
 
 	offset += sl->sl_data_offset;
 
-	if ((sl->sl_flags & SL_WRITEBACK_CACHE_DISABLE) &&
-	    (sl->sl_flags & SL_FLUSH_ON_DISABLED_WRITECACHE)) {
+	/*
+	 * Check to see if the command is WRITE(10), WRITE(12), or WRITE(16).
+	 * If it is then check for bit 3 being set to indicate if Forced
+	 * Unit Access is being requested. If so, the FSYNC flag will be set
+	 * on the write.
+	 */
+	if (((op == SCMD_WRITE_G1) || (op == SCMD_WRITE_G4) ||
+	    (op == SCMD_WRITE_G5)) && (task->task_cdb[1] & BIT_3)) {
+		fua_bit = B_TRUE;
+	}
+	if (((sl->sl_flags & SL_WRITEBACK_CACHE_DISABLE) &&
+	    (sl->sl_flags & SL_FLUSH_ON_DISABLED_WRITECACHE)) || fua_bit) {
 		ioflag = FSYNC;
 	} else {
 		ioflag = 0;
 	}
 
+	xfer_start = gethrtime();
 	DTRACE_PROBE5(backing__store__write__start, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset,
 	    scsi_task_t *, task);
@@ -3093,6 +3171,8 @@
 	    &resid);
 	rw_exit(&sl->sl_access_state_lock);
 
+	stmf_lu_xfer_done(task, B_FALSE /* write */,
+	    (gethrtime() - xfer_start));
 	DTRACE_PROBE6(backing__store__write__end, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset,
 	    int, ret, scsi_task_t *, task);
@@ -3103,7 +3183,6 @@
 		sret = sbd_flush_data_cache(sl, 1);
 	}
 over_sl_data_write:
-
 	if ((ret || resid) || (sret != SBD_SUCCESS)) {
 		return (SBD_FAILURE);
 	} else if ((offset + size) > sl->sl_data_readable_size) {
@@ -3639,7 +3718,8 @@
 		}
 	}
 out:
-	nvlist_free(nv);
+	if (nv != NULL)
+		nvlist_free(nv);
 	kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
 	kmem_free(zc, sizeof (zfs_cmd_t));
 	(void) ldi_close(zfs_lh, FREAD|FWRITE, kcred);
@@ -3689,22 +3769,26 @@
 
 /*
  * Unmap a region in a volume.  Currently only supported for zvols.
+ * The list of extents to be freed is passed in a dkioc_free_list_t
+ * which the caller is responsible for destroying.
  */
 int
-sbd_unmap(sbd_lu_t *sl, uint64_t offset, uint64_t length)
+sbd_unmap(sbd_lu_t *sl, dkioc_free_list_t *dfl)
 {
 	vnode_t *vp;
-	int unused;
-	dkioc_free_t df;
+	int unused, ret;
 
-	/* Right now, we only support UNMAP on zvols. */
-	if (!(sl->sl_flags & SL_ZFS_META))
-		return (EIO);
+	/* Nothing to do */
+	if (dfl->dfl_num_exts == 0)
+		return (0);
 
-	df.df_flags = (sl->sl_flags & SL_WRITEBACK_CACHE_DISABLE) ?
+	/*
+	 * TODO: unmap performance may be improved by not doing the synchronous
+	 * removal of the blocks and writing of the metadata.  The
+	 * transaction is in the zil so the state should be stable.
+	 */
+	dfl->dfl_flags = (sl->sl_flags & SL_WRITEBACK_CACHE_DISABLE) ?
 	    DF_WAIT_SYNC : 0;
-	df.df_start = offset;
-	df.df_length = length;
 
 	/* Use the data vnode we have to send a fop_ioctl(). */
 	vp = sl->sl_data_vp;
@@ -3713,6 +3797,29 @@
 		return (EIO);
 	}
 
-	return (VOP_IOCTL(vp, DKIOCFREE, (intptr_t)(&df), FKIOCTL, kcred,
-	    &unused, NULL));
+	ret = VOP_IOCTL(vp, DKIOCFREE, (intptr_t)dfl, FKIOCTL, kcred,
+	    &unused, NULL);
+
+	return (ret);
+}
+
+/*
+ * Check if this lu belongs to sbd or some other lu
+ * provider. A simple check for one of the module
+ * entry points is sufficient.
+ */
+int
+sbd_is_valid_lu(stmf_lu_t *lu)
+{
+	if (lu->lu_new_task == sbd_new_task)
+		return (1);
+	return (0);
+}
+
+uint8_t
+sbd_get_lbasize_shift(stmf_lu_t *lu)
+{
+	sbd_lu_t *sl = (sbd_lu_t *)lu->lu_provider_private;
+
+	return (sl->sl_data_blocksize_shift);
 }
