701 UNMAP support for COMSTAR
authorDan McDonald <danmcd@nexenta.com>
Fri, 04 Mar 2011 13:57:09 -0800
changeset 13297 4b9dc4ca8e9f
parent 13296 f7734d6b8e80
child 13298 c234f438c0d3
701 UNMAP support for COMSTAR Reviewed by: Garrett D'Amore <[email protected]> Reviewed by: Eric Schrock <[email protected]> Reviewed by: George Wilson <[email protected]> Approved by: Garrett D'Amore <[email protected]>
usr/src/uts/common/fs/zfs/zvol.c
usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c
usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c
usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h
usr/src/uts/common/sys/dkio.h
usr/src/uts/common/sys/scsi/generic/commands.h
usr/src/uts/common/sys/stmf_sbd_ioctl.h
--- a/usr/src/uts/common/fs/zfs/zvol.c	Fri Mar 04 15:27:25 2011 -0500
+++ b/usr/src/uts/common/fs/zfs/zvol.c	Fri Mar 04 13:57:09 2011 -0800
@@ -20,10 +20,12 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Portions Copyright 2010 Robert Milkowski
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
-/* Portions Copyright 2010 Robert Milkowski */
-
 /*
  * ZFS volume emulation driver.
  *
@@ -342,6 +344,24 @@
 }
 
 /*
+ * Replay a TX_TRUNCATE ZIL transaction if asked.  TX_TRUNCATE is how we
+ * implement DKIOCFREE/free-long-range.
+ */
+static int
+zvol_replay_truncate(zvol_state_t *zv, lr_truncate_t *lr, boolean_t byteswap)
+{
+	uint64_t offset, length;
+
+	if (byteswap)
+		byteswap_uint64_array(lr, sizeof (*lr));
+
+	offset = lr->lr_offset;
+	length = lr->lr_length;
+
+	return (dmu_free_long_range(zv->zv_objset, ZVOL_OBJ, offset, length));
+}
+
+/*
  * Replay a TX_WRITE ZIL transaction that didn't get committed
  * after a system failure
  */
@@ -391,7 +411,7 @@
 
 /*
  * Callback vectors for replaying records.
- * Only TX_WRITE is needed for zvol.
+ * Only TX_WRITE and TX_TRUNCATE are needed for zvol.
  */
 zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = {
 	zvol_replay_err,	/* 0 no such transaction type */
@@ -404,7 +424,7 @@
 	zvol_replay_err,	/* TX_LINK */
 	zvol_replay_err,	/* TX_RENAME */
 	zvol_replay_write,	/* TX_WRITE */
-	zvol_replay_err,	/* TX_TRUNCATE */
+	zvol_replay_truncate,	/* TX_TRUNCATE */
 	zvol_replay_err,	/* TX_SETATTR */
 	zvol_replay_err,	/* TX_ACL */
 	zvol_replay_err,	/* TX_CREATE_ACL */
@@ -1512,7 +1532,32 @@
  */
 
 /*
+ * Log a DKIOCFREE/free-long-range to the ZIL with TX_TRUNCATE.
+ */
+static void
+zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off, uint64_t len,
+    boolean_t sync)
+{
+	itx_t *itx;
+	lr_truncate_t *lr;
+	zilog_t *zilog = zv->zv_zilog;
+
+	if (zil_replaying(zilog, tx))
+		return;
+
+	itx = zil_itx_create(TX_TRUNCATE, sizeof (*lr));
+	lr = (lr_truncate_t *)&itx->itx_lr;
+	lr->lr_foid = ZVOL_OBJ;
+	lr->lr_offset = off;
+	lr->lr_length = len;
+
+	itx->itx_sync = sync;
+	zil_itx_assign(zilog, itx, tx);
+}
+
+/*
  * Dirtbag ioctls to support mkfs(1M) for UFS filesystems.  See dkio(7I).
+ * Also a dirtbag dkio ioctl for unmap/free-block functionality.
  */
 /*ARGSUSED*/
 int
@@ -1631,6 +1676,65 @@
 		zfs_range_unlock(rl);
 		break;
 
+	case DKIOCFREE:
+	{
+		dkioc_free_t df;
+		dmu_tx_t *tx;
+
+		if (ddi_copyin((void *)arg, &df, sizeof (df), flag)) {
+			error = EFAULT;
+			break;
+		}
+
+		/*
+		 * Apply Postel's Law to length-checking.  If they overshoot,
+		 * just blank out until the end, if there's a need to blank
+		 * out anything.
+		 */
+		if (df.df_start >= zv->zv_volsize)
+			break;	/* No need to do anything... */
+		if (df.df_start + df.df_length > zv->zv_volsize)
+			df.df_length = DMU_OBJECT_END;
+
+		rl = zfs_range_lock(&zv->zv_znode, df.df_start, df.df_length,
+		    RL_WRITER);
+		tx = dmu_tx_create(zv->zv_objset);
+		error = dmu_tx_assign(tx, TXG_WAIT);
+		if (error != 0) {
+			dmu_tx_abort(tx);
+		} else {
+			zvol_log_truncate(zv, tx, df.df_start,
+			    df.df_length, B_TRUE);
+			error = dmu_free_long_range(zv->zv_objset, ZVOL_OBJ,
+			    df.df_start, df.df_length);
+			dmu_tx_commit(tx);
+		}
+
+		zfs_range_unlock(rl);
+
+		if (error == 0) {
+			/*
+			 * If the write-cache is disabled or 'sync' property
+			 * is set to 'always' then treat this as a synchronous
+			 * operation (i.e. commit to zil).
+			 */
+			if (!(zv->zv_flags & ZVOL_WCE) ||
+			    (zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS))
+				zil_commit(zv->zv_zilog, ZVOL_OBJ);
+
+			/*
+			 * If the caller really wants synchronous writes, and
+			 * can't wait for them, don't return until the write
+			 * is done.
+			 */
+			if (df.df_flags & DF_WAIT_SYNC) {
+				txg_wait_synced(
+				    dmu_objset_pool(zv->zv_objset), 0);
+			}
+		}
+		break;
+	}
+
 	default:
 		error = ENOTTY;
 		break;
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c	Fri Mar 04 15:27:25 2011 -0500
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c	Fri Mar 04 13:57:09 2011 -0800
@@ -20,6 +20,8 @@
  */
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/conf.h>
@@ -83,7 +85,9 @@
     uint32_t *err_ret);
 int sbd_get_lu_props(sbd_lu_props_t *islp, uint32_t islp_sz,
     sbd_lu_props_t *oslp, uint32_t oslp_sz, uint32_t *err_ret);
-char *sbd_get_zvol_name(sbd_lu_t *sl);
+static char *sbd_get_zvol_name(sbd_lu_t *);
+static int sbd_get_unmap_props(sbd_unmap_props_t *sup, sbd_unmap_props_t *osup,
+    uint32_t *err_ret);
 sbd_status_t sbd_create_zfs_meta_object(sbd_lu_t *sl);
 sbd_status_t sbd_open_zfs_meta(sbd_lu_t *sl);
 sbd_status_t sbd_read_zfs_meta(sbd_lu_t *sl, uint8_t *buf, uint64_t sz,
@@ -448,6 +452,18 @@
 		ret = 0;
 		iocd->stmf_error = 0;
 		break;
+	case SBD_IOCTL_GET_UNMAP_PROPS:
+		if (iocd->stmf_ibuf_size < sizeof (sbd_unmap_props_t)) {
+			ret = EFAULT;
+			break;
+		}
+		if (iocd->stmf_obuf_size < sizeof (sbd_unmap_props_t)) {
+			ret = EINVAL;
+			break;
+		}
+		ret = sbd_get_unmap_props((sbd_unmap_props_t *)ibuf,
+		    (sbd_unmap_props_t *)obuf, &iocd->stmf_error);
+		break;
 	default:
 		ret = ENOTTY;
 	}
@@ -1372,12 +1388,28 @@
 	return (ret);
 }
 
+/*
+ * Will scribble SL_UNMAP_ENABLED into sl_flags if we succeed.
+ */
+static void
+do_unmap_setup(sbd_lu_t *sl)
+{
+	ASSERT((sl->sl_flags & SL_UNMAP_ENABLED) == 0);
+
+	if ((sl->sl_flags & SL_ZFS_META) == 0)
+		return;	/* No UNMAP for you. */
+
+	sl->sl_flags |= SL_UNMAP_ENABLED;
+}
+
 int
 sbd_populate_and_register_lu(sbd_lu_t *sl, uint32_t *err_ret)
 {
 	stmf_lu_t *lu = sl->sl_lu;
 	stmf_status_t ret;
 
+	do_unmap_setup(sl);
+
 	lu->lu_id = (scsi_devid_desc_t *)sl->sl_device_id;
 	if (sl->sl_alias) {
 		lu->lu_alias = sl->sl_alias;
@@ -3132,6 +3164,46 @@
 	return (0);
 }
 
+static int
+sbd_get_unmap_props(sbd_unmap_props_t *sup,
+    sbd_unmap_props_t *osup, uint32_t *err_ret)
+{
+	sbd_status_t sret;
+	sbd_lu_t *sl = NULL;
+
+	if (sup->sup_guid_valid) {
+		sret = sbd_find_and_lock_lu(sup->sup_guid,
+		    NULL, SL_OP_LU_PROPS, &sl);
+	} else {
+		sret = sbd_find_and_lock_lu(NULL,
+		    (uint8_t *)sup->sup_zvol_path, SL_OP_LU_PROPS,
+		    &sl);
+	}
+	if (sret != SBD_SUCCESS) {
+		if (sret == SBD_BUSY) {
+			*err_ret = SBD_RET_LU_BUSY;
+			return (EBUSY);
+		} else if (sret == SBD_NOT_FOUND) {
+			*err_ret = SBD_RET_NOT_FOUND;
+			return (ENOENT);
+		}
+		return (EIO);
+	}
+
+	sup->sup_found_lu = 1;
+	sup->sup_guid_valid = 1;
+	bcopy(sl->sl_device_id + 4, sup->sup_guid, 16);
+	if (sl->sl_flags & SL_UNMAP_ENABLED)
+		sup->sup_unmap_enabled = 1;
+	else
+		sup->sup_unmap_enabled = 0;
+
+	*osup = *sup;
+	sl->sl_trans_op = SL_OP_NONE;
+
+	return (0);
+}
+
 int
 sbd_get_lu_props(sbd_lu_props_t *islp, uint32_t islp_sz,
     sbd_lu_props_t *oslp, uint32_t oslp_sz, uint32_t *err_ret)
@@ -3272,7 +3344,10 @@
 	return (0);
 }
 
-char *
+/*
+ * Returns an allocated string with the "<pool>/..." form of the zvol name.
+ */
+static char *
 sbd_get_zvol_name(sbd_lu_t *sl)
 {
 	char *src;
@@ -3286,9 +3361,9 @@
 	if (SBD_IS_ZVOL(src) != 0) {
 		ASSERT(0);
 	}
-	src += 14;
+	src += 14;	/* Past /dev/zvol/dsk/ */
 	if (*src == '/')
-		src++;
+		src++;	/* or /dev/zvol/rdsk/ */
 	p = (char *)kmem_alloc(strlen(src) + 1, KM_SLEEP);
 	(void) strcpy(p, src);
 	return (p);
@@ -3623,3 +3698,33 @@
 	(void) ldi_close(zfs_lh, FREAD|FWRITE, kcred);
 	return (rc);
 }
+
+/*
+ * Unmap a region in a volume.  Currently only supported for zvols.
+ */
+int
+sbd_unmap(sbd_lu_t *sl, uint64_t offset, uint64_t length)
+{
+	vnode_t *vp;
+	int unused;
+	dkioc_free_t df;
+
+	/* Right now, we only support UNMAP on zvols. */
+	if (!(sl->sl_flags & SL_ZFS_META))
+		return (EIO);
+
+	df.df_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;
+	if (vp == NULL) {
+		cmn_err(CE_WARN, "Cannot unmap - no vnode pointer.");
+		return (EIO);
+	}
+
+	return (VOP_IOCTL(vp, DKIOCFREE, (intptr_t)(&df), FKIOCTL, kcred,
+	    &unused, NULL));
+}
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c	Fri Mar 04 15:27:25 2011 -0500
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c	Fri Mar 04 13:57:09 2011 -0800
@@ -20,6 +20,8 @@
  */
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/conf.h>
@@ -98,6 +100,11 @@
 void sbd_handle_mode_select(scsi_task_t *task, stmf_data_buf_t *dbuf);
 void sbd_handle_identifying_info(scsi_task_t *task, stmf_data_buf_t *dbuf);
 
+static void sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf,
+    uint32_t buflen);
+static void sbd_handle_unmap(scsi_task_t *task, stmf_data_buf_t *dbuf);
+static void sbd_handle_write_same(scsi_task_t *task);
+
 extern void sbd_pgr_initialize_it(scsi_task_t *, sbd_it_data_t *);
 extern int sbd_pgr_reservation_conflict(scsi_task_t *);
 extern void sbd_pgr_reset(sbd_lu_t *);
@@ -1754,6 +1761,10 @@
 			    dbuf->db_sglist[0].seg_addr, dbuf->db_data_size);
 		}
 		break;
+	case SCMD_UNMAP:
+		sbd_handle_unmap_xfer(task,
+		    dbuf->db_sglist[0].seg_addr, dbuf->db_data_size);
+		break;
 	case SCMD_PERSISTENT_RESERVE_OUT:
 		if (sl->sl_access_state == SBD_LU_STANDBY) {
 			st_ret = stmf_proxy_scsi_cmd(task, dbuf);
@@ -1815,6 +1826,9 @@
 		p[7] = s & 0xff;
 		p[10] = (blksize >> 8) & 0xff;
 		p[11] = blksize & 0xff;
+		if (sl->sl_flags & SL_UNMAP_ENABLED) {
+			p[14] = 0x80;
+		}
 		sbd_handle_short_read_transfers(task, initial_dbuf, p,
 		    cdb_len, 32);
 		break;
@@ -2216,6 +2230,105 @@
 	return (url_length);
 }
 
+static void
+sbd_handle_write_same(scsi_task_t *task)
+{
+	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
+	uint64_t addr, len;
+	uint8_t *p;
+
+	task->task_cmd_xfer_length = 0;
+	if (task->task_additional_flags &
+	    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
+		task->task_expected_xfer_length = 0;
+	}
+	if (task->task_cdb[1] & 0xF7) {
+		stmf_scsilib_send_status(task, STATUS_CHECK,
+		    STMF_SAA_INVALID_FIELD_IN_CDB);
+		return;
+	}
+	p = &task->task_cdb[2];
+	addr = READ_SCSI64(p, uint64_t);
+	addr <<= sl->sl_data_blocksize_shift;
+	len = READ_SCSI32(p+8, uint64_t);
+	len <<= sl->sl_data_blocksize_shift;
+
+	/* TODO -> full write_same support with data checks... */
+	if (sbd_unmap(sl, addr, len) != 0) {
+		stmf_scsilib_send_status(task, STATUS_CHECK,
+		    STMF_SAA_LBA_OUT_OF_RANGE);
+		return;
+	}
+	stmf_scsilib_send_status(task, STATUS_GOOD, 0);
+}
+
+static void
+sbd_handle_unmap(scsi_task_t *task, stmf_data_buf_t *dbuf)
+{
+	uint32_t cmd_xfer_len;
+
+	cmd_xfer_len = READ_SCSI16(&task->task_cdb[7], uint32_t);
+
+	if (task->task_cdb[1] & 1) {
+		stmf_scsilib_send_status(task, STATUS_CHECK,
+		    STMF_SAA_INVALID_FIELD_IN_CDB);
+		return;
+	}
+
+	if (cmd_xfer_len == 0) {
+		task->task_cmd_xfer_length = 0;
+		if (task->task_additional_flags &
+		    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
+			task->task_expected_xfer_length = 0;
+		}
+		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
+		return;
+	}
+
+	sbd_handle_short_write_transfers(task, dbuf, cmd_xfer_len);
+}
+
+static void
+sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf, uint32_t buflen)
+{
+	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
+	uint32_t ulen, dlen, num_desc;
+	uint64_t addr, len;
+	uint8_t *p;
+	int ret;
+
+	if (buflen < 24) {
+		stmf_scsilib_send_status(task, STATUS_CHECK,
+		    STMF_SAA_INVALID_FIELD_IN_CDB);
+		return;
+	}
+	ulen = READ_SCSI16(buf, uint32_t);
+	dlen = READ_SCSI16(buf + 2, uint32_t);
+	num_desc = dlen >> 4;
+	if (((ulen + 2) != buflen) || ((dlen + 8) != buflen) || (dlen & 0xf) ||
+	    (num_desc == 0)) {
+		stmf_scsilib_send_status(task, STATUS_CHECK,
+		    STMF_SAA_INVALID_FIELD_IN_CDB);
+		return;
+	}
+
+	for (p = buf + 8; num_desc; num_desc--, p += 16) {
+		addr = READ_SCSI64(p, uint64_t);
+		addr <<= sl->sl_data_blocksize_shift;
+		len = READ_SCSI32(p+8, uint64_t);
+		len <<= sl->sl_data_blocksize_shift;
+		ret = sbd_unmap(sl, addr, len);
+		if (ret != 0) {
+			stmf_scsilib_send_status(task, STATUS_CHECK,
+			    STMF_SAA_LBA_OUT_OF_RANGE);
+			return;
+		}
+	}
+
+unmap_done:
+	stmf_scsilib_send_status(task, STATUS_GOOD, 0);
+}
+
 void
 sbd_handle_inquiry(struct scsi_task *task, struct stmf_data_buf *initial_dbuf)
 {
@@ -2228,6 +2341,8 @@
 	uint16_t cmd_size;
 	uint32_t xfer_size = 4;
 	uint32_t mgmt_url_size = 0;
+	uint8_t exp;
+	uint64_t s;
 	char *mgmt_url = NULL;
 
 
@@ -2380,6 +2495,8 @@
 	switch (cdbp[2]) {
 	case 0x00:
 		page_length = 4 + (mgmt_url_size ? 1 : 0);
+		if (sl->sl_flags & SL_UNMAP_ENABLED)
+			page_length += 2;
 
 		p[0] = byte0;
 		p[3] = page_length;
@@ -2392,6 +2509,10 @@
 			if (mgmt_url_size != 0)
 				p[i++] = 0x85;
 			p[i++] = 0x86;
+			if (sl->sl_flags & SL_UNMAP_ENABLED) {
+				p[i++] = 0xb0;
+				p[i++] = 0xb2;
+			}
 		}
 		xfer_size = page_length + 4;
 		break;
@@ -2482,6 +2603,43 @@
 		xfer_size = page_length + 4;
 		break;
 
+	case 0xb0:
+		if ((sl->sl_flags & SL_UNMAP_ENABLED) == 0) {
+			stmf_scsilib_send_status(task, STATUS_CHECK,
+			    STMF_SAA_INVALID_FIELD_IN_CDB);
+			goto err_done;
+		}
+		page_length = 0x3c;
+		p[0] = byte0;
+		p[1] = 0xb0;
+		p[3] = page_length;
+		p[20] = p[21] = p[22] = p[23] = 0xFF;
+		p[24] = p[25] = p[26] = p[27] = 0xFF;
+		xfer_size = page_length + 4;
+		break;
+
+	case 0xb2:
+		if ((sl->sl_flags & SL_UNMAP_ENABLED) == 0) {
+			stmf_scsilib_send_status(task, STATUS_CHECK,
+			    STMF_SAA_INVALID_FIELD_IN_CDB);
+			goto err_done;
+		}
+		page_length = 4;
+		p[0] = byte0;
+		p[1] = 0xb2;
+		p[3] = page_length;
+
+		exp = (uint8_t)sl->sl_data_blocksize_shift;
+		s = sl->sl_lu_size >> sl->sl_data_blocksize_shift;
+		while (s & ((uint64_t)0xFFFFFFFF80000000ull)) {
+			s >>= 1;
+			exp++;
+		}
+		p[4] = exp;
+		p[5] = 0xc0;
+		xfer_size = page_length + 4;
+		break;
+
 	default:
 		stmf_scsilib_send_status(task, STATUS_CHECK,
 		    STMF_SAA_INVALID_FIELD_IN_CDB);
@@ -2908,6 +3066,16 @@
 		return;
 	}
 
+	if ((cdb0 == SCMD_UNMAP) && (sl->sl_flags & SL_UNMAP_ENABLED)) {
+		sbd_handle_unmap(task, initial_dbuf);
+		return;
+	}
+
+	if ((cdb0 == SCMD_WRITE_SAME_G4) && (sl->sl_flags & SL_UNMAP_ENABLED)) {
+		sbd_handle_write_same(task);
+		return;
+	}
+
 	if (cdb0 == SCMD_TEST_UNIT_READY) {	/* Test unit ready */
 		task->task_cmd_xfer_length = 0;
 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h	Fri Mar 04 15:27:25 2011 -0500
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h	Fri Mar 04 13:57:09 2011 -0800
@@ -20,6 +20,8 @@
  */
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #ifndef	_STMF_SBD_H
@@ -247,26 +249,27 @@
 /*
  * sl_flags
  */
-#define	SL_LINKED			    0x00001
-#define	SL_META_OPENED			    0x00002
-#define	SL_REGISTERED			    0x00004
-#define	SL_META_NEEDS_FLUSH		    0x00008
-#define	SL_DATA_NEEDS_FLUSH		    0x00010
-#define	SL_VID_VALID			    0x00020
-#define	SL_PID_VALID			    0x00040
-#define	SL_REV_VALID			    0x00080
-#define	SL_WRITE_PROTECTED		    0x00100
-#define	SL_MEDIA_LOADED			    0x00200
-#define	SL_LU_HAS_SCSI2_RESERVATION	    0x00400
-#define	SL_WRITEBACK_CACHE_DISABLE	    0x00800
-#define	SL_SAVED_WRITE_CACHE_DISABLE	    0x01000
-#define	SL_MEDIUM_REMOVAL_PREVENTED	    0x02000
-#define	SL_NO_DATA_DKIOFLUSH		    0x04000
-#define	SL_SHARED_META			    0x08000
-#define	SL_ZFS_META			    0x10000
-#define	SL_WRITEBACK_CACHE_SET_UNSUPPORTED  0x20000
-#define	SL_FLUSH_ON_DISABLED_WRITECACHE	    0x40000
-#define	SL_CALL_ZVOL			    0x80000
+#define	SL_LINKED			    0x00000001
+#define	SL_META_OPENED			    0x00000002
+#define	SL_REGISTERED			    0x00000004
+#define	SL_META_NEEDS_FLUSH		    0x00000008
+#define	SL_DATA_NEEDS_FLUSH		    0x00000010
+#define	SL_VID_VALID			    0x00000020
+#define	SL_PID_VALID			    0x00000040
+#define	SL_REV_VALID			    0x00000080
+#define	SL_WRITE_PROTECTED		    0x00000100
+#define	SL_MEDIA_LOADED			    0x00000200
+#define	SL_LU_HAS_SCSI2_RESERVATION	    0x00000400
+#define	SL_WRITEBACK_CACHE_DISABLE	    0x00000800
+#define	SL_SAVED_WRITE_CACHE_DISABLE	    0x00001000
+#define	SL_MEDIUM_REMOVAL_PREVENTED	    0x00002000
+#define	SL_NO_DATA_DKIOFLUSH		    0x00004000
+#define	SL_SHARED_META			    0x00008000
+#define	SL_ZFS_META			    0x00010000
+#define	SL_WRITEBACK_CACHE_SET_UNSUPPORTED  0x00020000
+#define	SL_FLUSH_ON_DISABLED_WRITECACHE	    0x00040000
+#define	SL_CALL_ZVOL			    0x00080000
+#define	SL_UNMAP_ENABLED		    0x00100000
 
 /*
  * sl_trans_op. LU is undergoing some transition and this field
@@ -297,6 +300,7 @@
 sbd_status_t sbd_flush_data_cache(sbd_lu_t *sl, int fsync_done);
 sbd_status_t sbd_wcd_set(int wcd, sbd_lu_t *sl);
 void sbd_wcd_get(int *wcd, sbd_lu_t *sl);
+int sbd_unmap(sbd_lu_t *, uint64_t, uint64_t);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/sys/dkio.h	Fri Mar 04 15:27:25 2011 -0500
+++ b/usr/src/uts/common/sys/dkio.h	Fri Mar 04 13:57:09 2011 -0800
@@ -21,6 +21,8 @@
 
 /*
  * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #ifndef _SYS_DKIO_H
@@ -515,6 +517,19 @@
 #define	FW_TYPE_TEMP	0x0		/* temporary use */
 #define	FW_TYPE_PERM	0x1		/* permanent use */
 
+/*
+ * ioctl to free space (e.g. SCSI UNMAP) off a disk.
+ */
+#define	DKIOCFREE	(DKIOC|50)
+
+typedef struct dkioc_free_s {
+	uint32_t df_flags;
+	uint32_t df_reserved;   /* For easy 64-bit alignment below... */
+	diskaddr_t df_start;
+	diskaddr_t df_length;
+} dkioc_free_t;
+
+#define	DF_WAIT_SYNC	0x00000001	/* Wait for full write-out of free. */
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/sys/scsi/generic/commands.h	Fri Mar 04 15:27:25 2011 -0500
+++ b/usr/src/uts/common/sys/scsi/generic/commands.h	Fri Mar 04 13:57:09 2011 -0800
@@ -21,6 +21,8 @@
 
 /*
  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #ifndef	_SYS_SCSI_GENERIC_COMMANDS_H
@@ -233,13 +235,15 @@
 #define	SCMD_READ_BUFFER	0x3c
 #define	SCMD_READ_LONG		0x3E
 #define	SCMD_WRITE_LONG		0x3F
+#define	SCMD_WRITE_SAME_G1	0x41
+#define	SCMD_UNMAP		0x42
+#define	SCMD_GET_CONFIGURATION	0x46
+#define	SCMD_LOG_SELECT_G1	0x4c
+#define	SCMD_LOG_SENSE_G1	0x4d
 #define	SCMD_RESERVE_G1		0x56
 #define	SCMD_RELEASE_G1		0x57
 #define	SCMD_MODE_SELECT_G1	0x55
 #define	SCMD_MODE_SENSE_G1	0x5A
-#define	SCMD_GET_CONFIGURATION	0x46
-#define	SCMD_LOG_SELECT_G1	0x4C
-#define	SCMD_LOG_SENSE_G1	0x4d
 
 
 /*
@@ -333,6 +337,7 @@
 #define	SCMD_READ_G4		0x88
 #define	SCMD_WRITE_G4		0x8a
 #define	SCMD_WRITE_VERIFY_G4	0x8e
+#define	SCMD_WRITE_SAME_G4	0x93
 #define	SCMD_SVC_ACTION_IN_G4	0x9e
 #define	SCMD_SVC_ACTION_OUT_G4	0x9f
 
@@ -455,9 +460,12 @@
 /* 0x3c */ SCMD_READ_BUFFER,		"read_buffer",			\
 /* 0x3e */ SCMD_READ_LONG,		"read_long",			\
 /* 0x3f */ SCMD_WRITE_LONG,		"write_long",			\
+/* 0x41 */ SCMD_WRITE_SAME_G1,		"write_same(10)",		\
+/* 0x42 */ SCMD_UNMAP,			"unmap",			\
 /* 0x44 */ SCMD_REPORT_DENSITIES |					\
 		/* SCMD_READ_HEADER (from cdio.h) | */			\
 		0,			"report_densities/read_header",	\
+/* 0x46 */ SCMD_GET_CONFIGURATION,	"get_configuration",		\
 /* 0x4c */ SCMD_LOG_SELECT_G1,		"log_select",			\
 /* 0x4d */ SCMD_LOG_SENSE_G1,		"log_sense",			\
 /* 0x55 */ SCMD_MODE_SELECT_G1,		"mode_select(10)",		\
@@ -477,6 +485,7 @@
 /* 0x8f */ SCMD_VERIFY_G4,		"verify(16)",			\
 /* 0x91 */ SCMD_SPACE_G4,		"space(16)",			\
 /* 0x92 */ SCMD_LOCATE_G4,		"locate(16)",			\
+/* 0x92 */ SCMD_WRITE_SAME_G4,		"write_same(16)",		\
 /* 0x9e */ SCMD_SVC_ACTION_IN_G4,	"service_action_in(16)",	\
 /* 0x9f */ SCMD_SVC_ACTION_OUT_G4,	"service_action_out(16)",	\
 /* 0xa0 */ SCMD_REPORT_LUNS,		"report_luns",			\
--- a/usr/src/uts/common/sys/stmf_sbd_ioctl.h	Fri Mar 04 15:27:25 2011 -0500
+++ b/usr/src/uts/common/sys/stmf_sbd_ioctl.h	Fri Mar 04 13:57:09 2011 -0800
@@ -21,6 +21,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #ifndef	_STMF_SBD_IOCTL_H
@@ -76,6 +78,7 @@
 #define	SBD_IOCTL_SET_LU_STANDBY			SBD_IOCTL_DEF(7)
 #define	SBD_IOCTL_SET_GLOBAL_LU				SBD_IOCTL_DEF(8)
 #define	SBD_IOCTL_GET_GLOBAL_LU				SBD_IOCTL_DEF(9)
+#define	SBD_IOCTL_GET_UNMAP_PROPS			SBD_IOCTL_DEF(10)
 
 typedef struct sbd_create_and_reg_lu {
 	uint32_t	slu_struct_size;
@@ -222,6 +225,16 @@
 	uint8_t		slp_buf[8];	/* likely more than 8 */
 } sbd_lu_props_t;
 
+typedef struct sbd_unmap_props {
+	uint32_t	sup_found_lu:1,
+			sup_zvol_path_valid:1,
+			sup_guid_valid:1,
+			sup_unmap_enabled;
+	uint32_t	sup_rsvd;
+	char		sup_zvol_path[256];
+	uint8_t		sup_guid[16];
+} sbd_unmap_props_t;
+
 #ifdef	__cplusplus
 }
 #endif