summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/lib/libdevid/Makefile.com4
-rw-r--r--usr/src/lib/libdiskmgt/Makefile.com3
-rw-r--r--usr/src/lib/libfdisk/Makefile.com3
-rw-r--r--usr/src/lib/libsmedia/library/Makefile.com4
-rw-r--r--usr/src/pkg/manifests/system-header.mf1
-rw-r--r--usr/src/uts/common/Makefile.files1
-rw-r--r--usr/src/uts/common/fs/zfs/zvol.c82
-rw-r--r--usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c26
-rw-r--r--usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c50
-rw-r--r--usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h6
-rw-r--r--usr/src/uts/common/io/sata/impl/sata.c4
-rw-r--r--usr/src/uts/common/io/scsi/targets/sd.c397
-rw-r--r--usr/src/uts/common/os/dkioc_free_util.c80
-rw-r--r--usr/src/uts/common/sys/Makefile3
-rw-r--r--usr/src/uts/common/sys/dkio.h24
-rw-r--r--usr/src/uts/common/sys/dkioc_free_util.h34
-rw-r--r--usr/src/uts/common/sys/scsi/targets/sddef.h42
17 files changed, 691 insertions, 73 deletions
diff --git a/usr/src/lib/libdevid/Makefile.com b/usr/src/lib/libdevid/Makefile.com
index 59c39c0b9c..fe065fc59a 100644
--- a/usr/src/lib/libdevid/Makefile.com
+++ b/usr/src/lib/libdevid/Makefile.com
@@ -22,6 +22,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
LIBRARY= libdevid.a
VERS= .1
OBJECTS= deviceid.o devid.o devid_scsi.o devid_smp.o
@@ -41,6 +43,8 @@ LDLIBS += -ldevinfo -lc
CFLAGS += $(CCVERBOSE)
+CSTD = $(CSTD_GNU99)
+
.KEEP_STATE:
all: $(LIBS)
diff --git a/usr/src/lib/libdiskmgt/Makefile.com b/usr/src/lib/libdiskmgt/Makefile.com
index 9c9e20908e..03a779d11e 100644
--- a/usr/src/lib/libdiskmgt/Makefile.com
+++ b/usr/src/lib/libdiskmgt/Makefile.com
@@ -23,6 +23,7 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright 2016 Nexenta Systems, Inc.
+# Copyright 2018 Joyent, Inc.
#
LIBRARY = libdiskmgt.a
@@ -65,6 +66,8 @@ CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-uninitialized
CPPFLAGS += -D_REENTRANT -I$(SRC)/lib/libdiskmgt/common
+CSTD = $(CSTD_GNU99)
+
.KEEP_STATE:
all: $(LIBS)
diff --git a/usr/src/lib/libfdisk/Makefile.com b/usr/src/lib/libfdisk/Makefile.com
index 46572b0b2b..198669d353 100644
--- a/usr/src/lib/libfdisk/Makefile.com
+++ b/usr/src/lib/libfdisk/Makefile.com
@@ -22,6 +22,7 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
#
LIBRARY= libfdisk.a
@@ -49,6 +50,8 @@ CFLAGS += -D_FILE_OFFSET_BITS=64
CFLAGS64 += -D_LARGEFILE64_SOURCE
CFLAGS64 += -D_FILE_OFFSET_BITS=64
+CSTD = $(CSTD_GNU99)
+
LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN
LINTFLAGS64 += -erroff=E_BAD_PTR_CAST_ALIGN
diff --git a/usr/src/lib/libsmedia/library/Makefile.com b/usr/src/lib/libsmedia/library/Makefile.com
index e570948866..bcdb37b2e7 100644
--- a/usr/src/lib/libsmedia/library/Makefile.com
+++ b/usr/src/lib/libsmedia/library/Makefile.com
@@ -22,6 +22,8 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
LIBRARY= libsmedia.a
VERS=.1
@@ -54,6 +56,8 @@ CFLAGS64 += $(CCVERBOSE)
CERRWARN += -_gcc=-Wno-unused-variable
+CSTD = $(CSTD_GNU99)
+
LDLIBS += -lnsl -lc
.KEEP_STATE:
diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf
index 3f62bda986..a65dbd02b9 100644
--- a/usr/src/pkg/manifests/system-header.mf
+++ b/usr/src/pkg/manifests/system-header.mf
@@ -919,6 +919,7 @@ file path=usr/include/sys/dirent.h
file path=usr/include/sys/disp.h
file path=usr/include/sys/dkbad.h
file path=usr/include/sys/dkio.h
+file path=usr/include/sys/dkioc_free_util.h
file path=usr/include/sys/dklabel.h
$(sparc_ONLY)file path=usr/include/sys/dkmpio.h
$(i386_ONLY)file path=usr/include/sys/dktp/altsctr.h
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index c9d04fb798..c963c34ce4 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -160,6 +160,7 @@ GENUNIX_OBJS += \
devid_smp.o \
devpolicy.o \
disp_lock.o \
+ dkioc_free_util.o \
dnlc.o \
driver.o \
dumpsubr.o \
diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c
index 2127de2bf0..b58cf54a17 100644
--- a/usr/src/uts/common/fs/zfs/zvol.c
+++ b/usr/src/uts/common/fs/zfs/zvol.c
@@ -23,7 +23,7 @@
*
* Portions Copyright 2010 Robert Milkowski
*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012, 2017 by Delphix. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
* Copyright 2018 Joyent, Inc.
@@ -91,6 +91,7 @@
#include <sys/zio_checksum.h>
#include <sys/zil_impl.h>
#include <sys/ht.h>
+#include <sys/dkioc_free_util.h>
#include "zfs_namecheck.h"
@@ -1900,45 +1901,65 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
case DKIOCFREE:
{
- dkioc_free_t df;
+ dkioc_free_list_t *dfl;
dmu_tx_t *tx;
if (!zvol_unmap_enabled)
break;
- if (ddi_copyin((void *)arg, &df, sizeof (df), flag)) {
- error = SET_ERROR(EFAULT);
- break;
+ if (!(flag & FKIOCTL)) {
+ error = dfl_copyin((void *)arg, &dfl, flag, KM_SLEEP);
+ if (error != 0)
+ break;
+ } else {
+ dfl = (dkioc_free_list_t *)arg;
+ ASSERT3U(dfl->dfl_num_exts, <=, DFL_COPYIN_MAX_EXTS);
+ if (dfl->dfl_num_exts > DFL_COPYIN_MAX_EXTS) {
+ error = SET_ERROR(EINVAL);
+ 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... */
-
mutex_exit(&zfsdev_state_lock);
ht_begin_unsafe();
- rl = zfs_range_lock(&zv->zv_znode, df.df_start, df.df_length,
- RL_WRITER);
- tx = dmu_tx_create(zv->zv_objset);
- dmu_tx_mark_netfree(tx);
- 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);
- dmu_tx_commit(tx);
- error = dmu_free_long_range(zv->zv_objset, ZVOL_OBJ,
- df.df_start, df.df_length);
- }
+ for (int i = 0; i < dfl->dfl_num_exts; i++) {
+ uint64_t start = dfl->dfl_exts[i].dfle_start,
+ length = dfl->dfl_exts[i].dfle_length,
+ end = start + length;
+
+ /*
+ * 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 (start >= zv->zv_volsize)
+ continue; /* No need to do anything... */
+ if (end > zv->zv_volsize) {
+ end = DMU_OBJECT_END;
+ length = end - start;
+ }
- zfs_range_unlock(rl);
+ rl = zfs_range_lock(&zv->zv_znode, start, 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, start, length,
+ B_TRUE);
+ dmu_tx_commit(tx);
+ error = dmu_free_long_range(zv->zv_objset,
+ ZVOL_OBJ, start, length);
+ }
+
+ zfs_range_unlock(rl);
+
+ if (error != 0)
+ break;
+ }
/*
* If the write-cache is disabled, 'sync' property
@@ -1951,10 +1972,13 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
if ((error == 0) && zvol_unmap_sync_enabled &&
(!(zv->zv_flags & ZVOL_WCE) ||
(zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS) ||
- (df.df_flags & DF_WAIT_SYNC))) {
+ (dfl->dfl_flags & DF_WAIT_SYNC))) {
zil_commit(zv->zv_zilog, ZVOL_OBJ);
}
+ if (!(flag & FKIOCTL))
+ dfl_free(dfl);
+
ht_end_unsafe();
return (error);
diff --git a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c
index 81e63367c5..d2dca92dfc 100644
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -318,7 +318,7 @@ sbd_close(dev_t dev, int flag, int otype, cred_t *credp)
/* 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;
@@ -3689,22 +3689,26 @@ out:
/*
* 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;
- /* 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 +3717,6 @@ sbd_unmap(sbd_lu_t *sl, uint64_t offset, uint64_t length)
return (EIO);
}
- return (VOP_IOCTL(vp, DKIOCFREE, (intptr_t)(&df), FKIOCTL, kcred,
+ return (VOP_IOCTL(vp, DKIOCFREE, (intptr_t)dfl, FKIOCTL, kcred,
&unused, NULL));
}
diff --git a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c
index cb6e115fe9..0ca48aed9d 100644
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -37,6 +37,7 @@
#include <sys/atomic.h>
#include <sys/sdt.h>
#include <sys/dkio.h>
+#include <sys/dkioc_free_util.h>
#include <sys/stmf.h>
#include <sys/lpif.h>
@@ -130,7 +131,7 @@ static void sbd_handle_write_same_xfer_completion(struct scsi_task *task,
void
sbd_do_read_xfer(struct scsi_task *task, sbd_cmd_t *scmd,
- struct stmf_data_buf *dbuf)
+ struct stmf_data_buf *dbuf)
{
sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
uint64_t laddr;
@@ -450,7 +451,7 @@ sbd_do_sgl_read_xfer(struct scsi_task *task, sbd_cmd_t *scmd, int first_xfer)
void
sbd_handle_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
- struct stmf_data_buf *dbuf)
+ struct stmf_data_buf *dbuf)
{
if (dbuf->db_xfer_status != STMF_SUCCESS) {
stmf_abort(STMF_QUEUE_TASK_ABORT, task,
@@ -503,7 +504,7 @@ sbd_handle_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
*/
void
sbd_handle_sgl_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
- struct stmf_data_buf *dbuf)
+ struct stmf_data_buf *dbuf)
{
sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
stmf_status_t xfer_status;
@@ -592,7 +593,7 @@ sbd_handle_sgl_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
void
sbd_handle_sgl_write_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
- struct stmf_data_buf *dbuf)
+ struct stmf_data_buf *dbuf)
{
sbd_zvol_io_t *zvio = dbuf->db_lu_private;
sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
@@ -1641,7 +1642,7 @@ sbd_handle_short_read_transfers(scsi_task_t *task, stmf_data_buf_t *dbuf,
void
sbd_handle_short_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
- struct stmf_data_buf *dbuf)
+ struct stmf_data_buf *dbuf)
{
if (dbuf->db_xfer_status != STMF_SUCCESS) {
stmf_abort(STMF_QUEUE_TASK_ABORT, task,
@@ -2189,7 +2190,8 @@ sbd_handle_identifying_info(struct scsi_task *task,
* and returns the length of the URL
*/
uint16_t
-sbd_parse_mgmt_url(char **url_addr) {
+sbd_parse_mgmt_url(char **url_addr)
+{
uint16_t url_length = 0;
char *url;
url = *url_addr;
@@ -2467,12 +2469,18 @@ sbd_handle_write_same(scsi_task_t *task, struct stmf_data_buf *initial_dbuf)
/* Check if the command is for the unmap function */
if (unmap) {
- if (sbd_unmap(sl, addr, len) != 0) {
+ dkioc_free_list_t *dfl = kmem_zalloc(DFL_SZ(1), KM_SLEEP);
+
+ dfl->dfl_num_exts = 1;
+ dfl->dfl_exts[0].dfle_start = addr;
+ dfl->dfl_exts[0].dfle_length = len;
+ if (sbd_unmap(sl, dfl) != 0) {
stmf_scsilib_send_status(task, STATUS_CHECK,
STMF_SAA_LBA_OUT_OF_RANGE);
} else {
stmf_scsilib_send_status(task, STATUS_GOOD, 0);
}
+ dfl_free(dfl);
return;
}
@@ -2576,7 +2584,9 @@ sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf, uint32_t buflen)
uint32_t ulen, dlen, num_desc;
uint64_t addr, len;
uint8_t *p;
+ dkioc_free_list_t *dfl;
int ret;
+ int i;
if (buflen < 24) {
stmf_scsilib_send_status(task, STATUS_CHECK,
@@ -2593,20 +2603,28 @@ sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf, uint32_t buflen)
return;
}
- for (p = buf + 8; num_desc; num_desc--, p += 16) {
+ dfl = kmem_zalloc(DFL_SZ(num_desc), KM_SLEEP);
+ dfl->dfl_num_exts = num_desc;
+ for (p = buf + 8, i = 0; num_desc; num_desc--, p += 16, i++) {
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;
- }
+ /* Prepare a list of extents to unmap */
+ dfl->dfl_exts[i].dfle_start = addr;
+ dfl->dfl_exts[i].dfle_length = len;
+ }
+ ASSERT(i == dfl->dfl_num_exts);
+
+ /* Finally execute the unmap operations in a single step */
+ ret = sbd_unmap(sl, dfl);
+ dfl_free(dfl);
+ 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);
}
diff --git a/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h b/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h
index a402ad0ee3..f3ab44f8f4 100644
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h
@@ -21,12 +21,14 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _STMF_SBD_H
#define _STMF_SBD_H
+#include <sys/dkio.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -300,7 +302,7 @@ sbd_status_t sbd_write_lu_info(sbd_lu_t *sl);
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);
+int sbd_unmap(sbd_lu_t *sl, dkioc_free_list_t *dfl);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/io/sata/impl/sata.c b/usr/src/uts/common/io/sata/impl/sata.c
index 17c6da2813..29b7cf2005 100644
--- a/usr/src/uts/common/io/sata/impl/sata.c
+++ b/usr/src/uts/common/io/sata/impl/sata.c
@@ -23,7 +23,7 @@
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 Argo Technologies SA
* Copyright (c) 2018, Joyent, Inc.
*/
@@ -4851,7 +4851,7 @@ sata_txlt_unmap(sata_pkt_txlate_t *spx)
bdlen = scsipkt->pkt_cdbp[7];
bdlen = (bdlen << 8) + scsipkt->pkt_cdbp[8] - paramlen;
if ((bdlen < 0) || ((bdlen % 16) != 0) ||
- (bdlen > (bp->b_bcount - paramlen))) {
+ ((bp != NULL) && (bdlen > (bp->b_bcount - paramlen)))) {
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_unmap: invalid block descriptor length", NULL);
mutex_exit(cport_mutex);
diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c
index 3a3494aa50..550d1afe23 100644
--- a/usr/src/uts/common/io/scsi/targets/sd.c
+++ b/usr/src/uts/common/io/scsi/targets/sd.c
@@ -53,6 +53,7 @@
#include <sys/efi_partition.h>
#include <sys/var.h>
#include <sys/aio_req.h>
+#include <sys/dkioc_free_util.h>
#ifdef __lock_lint
#define _LP64
@@ -1152,6 +1153,24 @@ static int sd_pm_idletime = 1;
#endif /* #if (defined(__fibre)) */
+typedef struct unmap_param_hdr_s {
+ uint16_t uph_data_len;
+ uint16_t uph_descr_data_len;
+ uint32_t uph_reserved;
+} unmap_param_hdr_t;
+
+typedef struct unmap_blk_descr_s {
+ uint64_t ubd_lba;
+ uint32_t ubd_lba_cnt;
+ uint32_t ubd_reserved;
+} unmap_blk_descr_t;
+
+/* Max number of block descriptors in UNMAP command */
+#define SD_UNMAP_MAX_DESCR \
+ ((UINT16_MAX - sizeof (unmap_param_hdr_t)) / sizeof (unmap_blk_descr_t))
+/* Max size of the UNMAP parameter list in bytes */
+#define SD_UNMAP_PARAM_LIST_MAXSZ (sizeof (unmap_param_hdr_t) + \
+ SD_UNMAP_MAX_DESCR * sizeof (unmap_blk_descr_t))
int _init(void);
int _fini(void);
@@ -1534,6 +1553,8 @@ static int sd_send_scsi_PERSISTENT_RESERVE_OUT(sd_ssc_t *ssc,
static int sd_send_scsi_SYNCHRONIZE_CACHE(struct sd_lun *un,
struct dk_callback *dkc);
static int sd_send_scsi_SYNCHRONIZE_CACHE_biodone(struct buf *bp);
+static int sd_send_scsi_UNMAP(dev_t dev, sd_ssc_t *ssc, dkioc_free_list_t *dfl,
+ int flag);
static int sd_send_scsi_GET_CONFIGURATION(sd_ssc_t *ssc,
struct uscsi_cmd *ucmdbuf, uchar_t *rqbuf, uint_t rqbuflen,
uchar_t *bufaddr, uint_t buflen, int path_flag);
@@ -3481,8 +3502,8 @@ sd_set_mmc_caps(sd_ssc_t *ssc)
* The following warning occurs due to the KVM CD-ROM
* mishandling the multi-media commands. Ignore it.
* scsi_log(SD_DEVINFO(un), sd_label, CE_WARN,
- * "sd_set_mmc_caps: Mode Sense returned "
- * "invalid block descriptor length\n");
+ * "sd_set_mmc_caps: Mode Sense returned "
+ * "invalid block descriptor length\n");
*/
kmem_free(buf, BUFLEN_MODE_CDROM_CAP);
return;
@@ -5372,6 +5393,91 @@ sd_update_block_info(struct sd_lun *un, uint32_t lbasize, uint64_t capacity)
}
}
+/*
+ * Parses the SCSI Block Limits VPD page (0xB0). It's legal to pass NULL for
+ * vpd_pg, in which case all the block limits will be reset to the defaults.
+ */
+static void
+sd_parse_blk_limits_vpd(struct sd_lun *un, uchar_t *vpd_pg)
+{
+ sd_blk_limits_t *lim = &un->un_blk_lim;
+ unsigned pg_len;
+
+ if (vpd_pg != NULL)
+ pg_len = BE_IN16(&vpd_pg[2]);
+ else
+ pg_len = 0;
+
+ /* Block Limits VPD can be 16 bytes or 64 bytes long - support both */
+ if (pg_len >= 0x10) {
+ lim->lim_opt_xfer_len_gran = BE_IN16(&vpd_pg[6]);
+ lim->lim_max_xfer_len = BE_IN32(&vpd_pg[8]);
+ lim->lim_opt_xfer_len = BE_IN32(&vpd_pg[12]);
+
+ /* Zero means not reported, so use "unlimited" */
+ if (lim->lim_max_xfer_len == 0)
+ lim->lim_max_xfer_len = UINT32_MAX;
+ if (lim->lim_opt_xfer_len == 0)
+ lim->lim_opt_xfer_len = UINT32_MAX;
+ } else {
+ lim->lim_opt_xfer_len_gran = 0;
+ lim->lim_max_xfer_len = UINT32_MAX;
+ lim->lim_opt_xfer_len = UINT32_MAX;
+ }
+ if (pg_len >= 0x3c) {
+ lim->lim_max_pfetch_len = BE_IN32(&vpd_pg[16]);
+ /*
+ * A zero in either of the following two fields indicates lack
+ * of UNMAP support.
+ */
+ lim->lim_max_unmap_lba_cnt = BE_IN32(&vpd_pg[20]);
+ lim->lim_max_unmap_descr_cnt = BE_IN32(&vpd_pg[24]);
+ lim->lim_opt_unmap_gran = BE_IN32(&vpd_pg[28]);
+ if ((vpd_pg[32] >> 7) == 1) {
+ lim->lim_unmap_gran_align =
+ ((vpd_pg[32] & 0x7f) << 24) | (vpd_pg[33] << 16) |
+ (vpd_pg[34] << 8) | vpd_pg[35];
+ } else {
+ lim->lim_unmap_gran_align = 0;
+ }
+ lim->lim_max_write_same_len = BE_IN64(&vpd_pg[36]);
+ } else {
+ lim->lim_max_pfetch_len = UINT32_MAX;
+ lim->lim_max_unmap_lba_cnt = UINT32_MAX;
+ lim->lim_max_unmap_descr_cnt = SD_UNMAP_MAX_DESCR;
+ lim->lim_opt_unmap_gran = 0;
+ lim->lim_unmap_gran_align = 0;
+ lim->lim_max_write_same_len = UINT64_MAX;
+ }
+}
+
+/*
+ * Collects VPD page B0 data if available (block limits). If the data is
+ * not available or querying the device failed, we revert to the defaults.
+ */
+static void
+sd_setup_blk_limits(sd_ssc_t *ssc)
+{
+ struct sd_lun *un = ssc->ssc_un;
+ uchar_t *inqB0 = NULL;
+ size_t inqB0_resid = 0;
+ int rval;
+
+ if (un->un_vpd_page_mask & SD_VPD_BLK_LIMITS_PG) {
+ inqB0 = kmem_zalloc(MAX_INQUIRY_SIZE, KM_SLEEP);
+ rval = sd_send_scsi_INQUIRY(ssc, inqB0, MAX_INQUIRY_SIZE, 0x01,
+ 0xB0, &inqB0_resid);
+ if (rval != 0) {
+ sd_ssc_assessment(ssc, SD_FMT_IGNORE);
+ kmem_free(inqB0, MAX_INQUIRY_SIZE);
+ inqB0 = NULL;
+ }
+ }
+ /* passing NULL inqB0 will reset to defaults */
+ sd_parse_blk_limits_vpd(ssc->ssc_un, inqB0);
+ if (inqB0)
+ kmem_free(inqB0, MAX_INQUIRY_SIZE);
+}
/*
* Function: sd_register_devid
@@ -5901,6 +6007,9 @@ sd_check_vpd_page_support(sd_ssc_t *ssc)
case 0x86:
un->un_vpd_page_mask |= SD_VPD_EXTENDED_DATA_PG;
break;
+ case 0xB0:
+ un->un_vpd_page_mask |= SD_VPD_BLK_LIMITS_PG;
+ break;
case 0xB1:
un->un_vpd_page_mask |= SD_VPD_DEV_CHARACTER_PG;
break;
@@ -7805,6 +7914,27 @@ sd_unit_attach(dev_info_t *devi)
SD_TRACE(SD_LOG_ATTACH_DETACH, un,
"sd_unit_attach: un:0x%p un_stats created\n", un);
+ un->un_unmapstats_ks = kstat_create(sd_label, instance, "unmapstats",
+ "misc", KSTAT_TYPE_NAMED, sizeof (*un->un_unmapstats) /
+ sizeof (kstat_named_t), 0);
+ if (un->un_unmapstats_ks) {
+ un->un_unmapstats = un->un_unmapstats_ks->ks_data;
+
+ kstat_named_init(&un->un_unmapstats->us_cmds,
+ "commands", KSTAT_DATA_UINT64);
+ kstat_named_init(&un->un_unmapstats->us_errs,
+ "errors", KSTAT_DATA_UINT64);
+ kstat_named_init(&un->un_unmapstats->us_extents,
+ "extents", KSTAT_DATA_UINT64);
+ kstat_named_init(&un->un_unmapstats->us_bytes,
+ "bytes", KSTAT_DATA_UINT64);
+
+ kstat_install(un->un_unmapstats_ks);
+ } else {
+ cmn_err(CE_NOTE, "!Cannot create unmap kstats for disk %d",
+ instance);
+ }
+
sd_create_errstats(un, instance);
if (un->un_errstats == NULL) {
goto create_errstats_failed;
@@ -8442,6 +8572,7 @@ sd_unit_attach(dev_info_t *devi)
SD_TRACE(SD_LOG_ATTACH_DETACH, un,
"sd_unit_attach: un:0x%p errstats set\n", un);
+ sd_setup_blk_limits(ssc);
/*
* After successfully attaching an instance, we record the information
@@ -9015,6 +9146,11 @@ sd_unit_detach(dev_info_t *devi)
kstat_delete(un->un_stats);
un->un_stats = NULL;
}
+ if (un->un_unmapstats != NULL) {
+ kstat_delete(un->un_unmapstats_ks);
+ un->un_unmapstats_ks = NULL;
+ un->un_unmapstats = NULL;
+ }
if (un->un_errstats != NULL) {
kstat_delete(un->un_errstats);
un->un_errstats = NULL;
@@ -20392,11 +20528,21 @@ sd_send_scsi_READ_CAPACITY_16(sd_ssc_t *ssc, uint64_t *capp, uint32_t *lbap,
* (MSB in byte:8 & LSB in byte:11)
*
* byte 13: LOGICAL BLOCKS PER PHYSICAL BLOCK EXPONENT
+ *
+ * byte 14:
+ * bit 7: Thin-Provisioning Enabled
+ * bit 6: Thin-Provisioning Read Zeros
*/
capacity = BE_64(capacity16_buf[0]);
lbasize = BE_32(*(uint32_t *)&capacity16_buf[1]);
lbpb_exp = (BE_64(capacity16_buf[1]) >> 16) & 0x0f;
+ un->un_thin_flags = 0;
+ if (((uint8_t *)capacity16_buf)[14] & (1 << 7))
+ un->un_thin_flags |= SD_THIN_PROV_ENABLED;
+ if (((uint8_t *)capacity16_buf)[14] & (1 << 6))
+ un->un_thin_flags |= SD_THIN_PROV_READ_ZEROS;
+
pbsize = lbasize << lbpb_exp;
/*
@@ -21451,6 +21597,237 @@ done:
return (status);
}
+/*
+ * Issues a single SCSI UNMAP command with a prepared UNMAP parameter list.
+ * Returns zero on success, or the non-zero command error code on failure.
+ */
+static int
+sd_send_scsi_UNMAP_issue_one(sd_ssc_t *ssc, unmap_param_hdr_t *uph,
+ uint64_t num_descr, uint64_t bytes)
+{
+ struct sd_lun *un = ssc->ssc_un;
+ struct scsi_extended_sense sense_buf;
+ union scsi_cdb cdb;
+ struct uscsi_cmd ucmd_buf;
+ int status;
+ const uint64_t param_size = sizeof (unmap_param_hdr_t) +
+ num_descr * sizeof (unmap_blk_descr_t);
+
+ ASSERT3U(param_size - 2, <=, UINT16_MAX);
+ uph->uph_data_len = BE_16(param_size - 2);
+ uph->uph_descr_data_len = BE_16(param_size - 8);
+
+ bzero(&cdb, sizeof (cdb));
+ bzero(&ucmd_buf, sizeof (ucmd_buf));
+ bzero(&sense_buf, sizeof (struct scsi_extended_sense));
+
+ cdb.scc_cmd = SCMD_UNMAP;
+ FORMG1COUNT(&cdb, param_size);
+
+ ucmd_buf.uscsi_cdb = (char *)&cdb;
+ ucmd_buf.uscsi_cdblen = (uchar_t)CDB_GROUP1;
+ ucmd_buf.uscsi_bufaddr = (caddr_t)uph;
+ ucmd_buf.uscsi_buflen = param_size;
+ ucmd_buf.uscsi_rqbuf = (caddr_t)&sense_buf;
+ ucmd_buf.uscsi_rqlen = sizeof (struct scsi_extended_sense);
+ ucmd_buf.uscsi_flags = USCSI_WRITE | USCSI_RQENABLE | USCSI_SILENT;
+ ucmd_buf.uscsi_timeout = un->un_cmd_timeout;
+
+ status = sd_ssc_send(ssc, &ucmd_buf, FKIOCTL, UIO_SYSSPACE,
+ SD_PATH_STANDARD);
+
+ switch (status) {
+ case 0:
+ sd_ssc_assessment(ssc, SD_FMT_STANDARD);
+
+ if (un->un_unmapstats) {
+ atomic_inc_64(&un->un_unmapstats->us_cmds.value.ui64);
+ atomic_add_64(&un->un_unmapstats->us_extents.value.ui64,
+ num_descr);
+ atomic_add_64(&un->un_unmapstats->us_bytes.value.ui64,
+ bytes);
+ }
+ break; /* Success! */
+ case EIO:
+ if (un->un_unmapstats)
+ atomic_inc_64(&un->un_unmapstats->us_errs.value.ui64);
+ switch (ucmd_buf.uscsi_status) {
+ case STATUS_RESERVATION_CONFLICT:
+ status = EACCES;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ if (un->un_unmapstats)
+ atomic_inc_64(&un->un_unmapstats->us_errs.value.ui64);
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * Returns a pointer to the i'th block descriptor inside an UNMAP param list.
+ */
+static inline unmap_blk_descr_t *
+UNMAP_blk_descr_i(void *buf, size_t i)
+{
+ return ((unmap_blk_descr_t *)((uintptr_t)buf +
+ sizeof (unmap_param_hdr_t) + (i * sizeof (unmap_blk_descr_t))));
+}
+
+/*
+ * Takes the list of extents from sd_send_scsi_UNMAP, chops it up, prepares
+ * UNMAP block descriptors and issues individual SCSI UNMAP commands. While
+ * doing so we consult the block limits to determine at most how many
+ * extents and LBAs we can UNMAP in one command.
+ * If a command fails for whatever, reason, extent list processing is aborted
+ * and the failed command's status is returned. Otherwise returns 0 on
+ * success.
+ */
+static int
+sd_send_scsi_UNMAP_issue(dev_t dev, sd_ssc_t *ssc, const dkioc_free_list_t *dfl)
+{
+ struct sd_lun *un = ssc->ssc_un;
+ unmap_param_hdr_t *uph;
+ sd_blk_limits_t *lim = &un->un_blk_lim;
+ int rval = 0;
+ int partition;
+ /* partition offset & length in system blocks */
+ diskaddr_t part_off_sysblks = 0, part_len_sysblks = 0;
+ uint64_t part_off, part_len;
+ uint64_t descr_cnt_lim, byte_cnt_lim;
+ uint64_t descr_issued = 0, bytes_issued = 0;
+
+ uph = kmem_zalloc(SD_UNMAP_PARAM_LIST_MAXSZ, KM_SLEEP);
+
+ partition = SDPART(dev);
+ rval = cmlb_partinfo(un->un_cmlbhandle, partition, &part_len_sysblks,
+ &part_off_sysblks, NULL, NULL, (void *)SD_PATH_DIRECT);
+ if (rval != 0)
+ goto out;
+ part_off = SD_SYSBLOCKS2BYTES(part_off_sysblks);
+ part_len = SD_SYSBLOCKS2BYTES(part_len_sysblks);
+
+ ASSERT(un->un_blk_lim.lim_max_unmap_lba_cnt != 0);
+ ASSERT(un->un_blk_lim.lim_max_unmap_descr_cnt != 0);
+ /* Spec says 0xffffffff are special values, so compute maximums. */
+ byte_cnt_lim = lim->lim_max_unmap_lba_cnt < UINT32_MAX ?
+ (uint64_t)lim->lim_max_unmap_lba_cnt * un->un_tgt_blocksize :
+ UINT64_MAX;
+ descr_cnt_lim = MIN(lim->lim_max_unmap_descr_cnt, SD_UNMAP_MAX_DESCR);
+
+ if (dfl->dfl_offset >= part_len) {
+ rval = SET_ERROR(EINVAL);
+ goto out;
+ }
+
+ for (size_t i = 0; i < dfl->dfl_num_exts; i++) {
+ const dkioc_free_list_ext_t *ext = &dfl->dfl_exts[i];
+ uint64_t ext_start = ext->dfle_start;
+ uint64_t ext_length = ext->dfle_length;
+
+ while (ext_length > 0) {
+ unmap_blk_descr_t *ubd;
+ /* Respect device limit on LBA count per command */
+ uint64_t len = MIN(MIN(ext_length, byte_cnt_lim -
+ bytes_issued), SD_TGTBLOCKS2BYTES(un, UINT32_MAX));
+
+ /* check partition limits */
+ if (ext_start >= part_len ||
+ ext_start + len < ext_start ||
+ dfl->dfl_offset + ext_start + len <
+ dfl->dfl_offset ||
+ dfl->dfl_offset + ext_start + len > part_len) {
+ rval = SET_ERROR(EINVAL);
+ goto out;
+ }
+
+ ASSERT3U(descr_issued, <, descr_cnt_lim);
+ ASSERT3U(bytes_issued, <, byte_cnt_lim);
+ ubd = UNMAP_blk_descr_i(uph, descr_issued);
+
+ /* adjust in-partition addresses to be device-global */
+ ubd->ubd_lba = BE_64(SD_BYTES2TGTBLOCKS(un,
+ dfl->dfl_offset + ext_start + part_off));
+ ubd->ubd_lba_cnt = BE_32(SD_BYTES2TGTBLOCKS(un, len));
+
+ descr_issued++;
+ bytes_issued += len;
+
+ /* Issue command when device limits reached */
+ if (descr_issued == descr_cnt_lim ||
+ bytes_issued == byte_cnt_lim) {
+ rval = sd_send_scsi_UNMAP_issue_one(ssc, uph,
+ descr_issued, bytes_issued);
+ if (rval != 0)
+ goto out;
+ descr_issued = 0;
+ bytes_issued = 0;
+ }
+
+ ext_start += len;
+ ext_length -= len;
+ }
+ }
+
+ if (descr_issued > 0) {
+ /* issue last command */
+ rval = sd_send_scsi_UNMAP_issue_one(ssc, uph, descr_issued,
+ bytes_issued);
+ }
+
+out:
+ kmem_free(uph, SD_UNMAP_PARAM_LIST_MAXSZ);
+ return (rval);
+}
+
+/*
+ * Issues one or several UNMAP commands based on a list of extents to be
+ * unmapped. The internal multi-command processing is hidden, as the exact
+ * number of commands and extents per command is limited by both SCSI
+ * command syntax and device limits (as expressed in the SCSI Block Limits
+ * VPD page and un_blk_lim in struct sd_lun).
+ * Returns zero on success, or the error code of the first failed SCSI UNMAP
+ * command.
+ */
+static int
+sd_send_scsi_UNMAP(dev_t dev, sd_ssc_t *ssc, dkioc_free_list_t *dfl, int flag)
+{
+ struct sd_lun *un = ssc->ssc_un;
+ int rval = 0;
+
+ ASSERT(!mutex_owned(SD_MUTEX(un)));
+ ASSERT(dfl != NULL);
+
+ /* Per spec, any of these conditions signals lack of UNMAP support. */
+ if (!(un->un_thin_flags & SD_THIN_PROV_ENABLED) ||
+ un->un_blk_lim.lim_max_unmap_descr_cnt == 0 ||
+ un->un_blk_lim.lim_max_unmap_lba_cnt == 0) {
+ return (SET_ERROR(ENOTSUP));
+ }
+
+ /* For userspace calls we must copy in. */
+ if (!(flag & FKIOCTL)) {
+ int err = dfl_copyin(dfl, &dfl, flag, KM_SLEEP);
+ if (err != 0)
+ return (err);
+ } else if (dfl->dfl_num_exts > DFL_COPYIN_MAX_EXTS) {
+ ASSERT3U(dfl->dfl_num_exts, <=, DFL_COPYIN_MAX_EXTS);
+ return (SET_ERROR(EINVAL));
+ }
+
+ rval = sd_send_scsi_UNMAP_issue(dev, ssc, dfl);
+
+ if (!(flag & FKIOCTL)) {
+ dfl_free(dfl);
+ dfl = NULL;
+ }
+
+ return (rval);
+}
/*
* Function: sd_send_scsi_GET_CONFIGURATION
@@ -23206,6 +23583,22 @@ skip_ready_valid:
}
break;
+ case DKIOCFREE:
+ {
+ dkioc_free_list_t *dfl = (dkioc_free_list_t *)arg;
+
+ /* bad ioctls shouldn't panic */
+ if (dfl == NULL) {
+ /* check kernel callers strictly in debug */
+ ASSERT0(flag & FKIOCTL);
+ err = SET_ERROR(EINVAL);
+ break;
+ }
+ /* synchronous UNMAP request */
+ err = sd_send_scsi_UNMAP(dev, ssc, dfl, flag);
+ }
+ break;
+
case DKIOCGETWCE: {
int wce;
diff --git a/usr/src/uts/common/os/dkioc_free_util.c b/usr/src/uts/common/os/dkioc_free_util.c
new file mode 100644
index 0000000000..85470f7e28
--- /dev/null
+++ b/usr/src/uts/common/os/dkioc_free_util.c
@@ -0,0 +1,80 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017 Nexenta Inc. All rights reserved.
+ */
+
+/* needed when building libzpool */
+#ifndef _KERNEL
+#include <sys/zfs_context.h>
+#endif
+
+#include <sys/sunddi.h>
+#include <sys/dkio.h>
+#include <sys/dkioc_free_util.h>
+#include <sys/sysmacros.h>
+#include <sys/file.h>
+#include <sys/sdt.h>
+
+/*
+ * Copy-in convenience function for variable-length dkioc_free_list_t
+ * structures. The pointer to be copied from is in `arg' (may be a pointer
+ * to userspace). A new buffer is allocated and a pointer to it is placed
+ * in `out'. `ddi_flags' indicates whether the pointer is from user-
+ * or kernelspace (FKIOCTL) and `kmflags' are the flags passed to
+ * kmem_zalloc when allocating the new structure.
+ * Returns 0 on success, or an errno on failure.
+ */
+int
+dfl_copyin(void *arg, dkioc_free_list_t **out, int ddi_flags, int kmflags)
+{
+ dkioc_free_list_t *dfl;
+
+ if (ddi_flags & FKIOCTL) {
+ dkioc_free_list_t *dfl_in = arg;
+
+ if (dfl_in->dfl_num_exts == 0 ||
+ dfl_in->dfl_num_exts > DFL_COPYIN_MAX_EXTS)
+ return (SET_ERROR(EINVAL));
+ dfl = kmem_alloc(DFL_SZ(dfl_in->dfl_num_exts), kmflags);
+ if (dfl == NULL)
+ return (SET_ERROR(ENOMEM));
+ bcopy(dfl_in, dfl, DFL_SZ(dfl_in->dfl_num_exts));
+ } else {
+ uint64_t num_exts;
+
+ if (ddi_copyin(((uint8_t *)arg) + offsetof(dkioc_free_list_t,
+ dfl_num_exts), &num_exts, sizeof (num_exts),
+ ddi_flags) != 0)
+ return (SET_ERROR(EFAULT));
+ if (num_exts == 0 || num_exts > DFL_COPYIN_MAX_EXTS)
+ return (SET_ERROR(EINVAL));
+ dfl = kmem_alloc(DFL_SZ(num_exts), kmflags);
+ if (dfl == NULL)
+ return (SET_ERROR(ENOMEM));
+ if (ddi_copyin(arg, dfl, DFL_SZ(num_exts), ddi_flags) != 0 ||
+ dfl->dfl_num_exts != num_exts) {
+ kmem_free(dfl, DFL_SZ(num_exts));
+ return (SET_ERROR(EFAULT));
+ }
+ }
+
+ *out = dfl;
+ return (0);
+}
+
+/* Frees a variable-length dkioc_free_list_t structure. */
+void
+dfl_free(dkioc_free_list_t *dfl)
+{
+ kmem_free(dfl, DFL_SZ(dfl->dfl_num_exts));
+}
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 0fa800d39e..787774e03a 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -26,7 +26,7 @@
# Copyright 2015, Joyent, Inc. All rights reserved.
# Copyright 2013 Saso Kiselkov. All rights reserved.
# Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
-# Copyright 2016 Nexenta Systems, Inc.
+# Copyright 2017 Nexenta Systems, Inc.
# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
#
@@ -189,6 +189,7 @@ CHKHDRS= \
disp.h \
dkbad.h \
dkio.h \
+ dkioc_free_util.h \
dklabel.h \
dl.h \
dlpi.h \
diff --git a/usr/src/uts/common/sys/dkio.h b/usr/src/uts/common/sys/dkio.h
index 3d1a839164..dcb817fc72 100644
--- a/usr/src/uts/common/sys/dkio.h
+++ b/usr/src/uts/common/sys/dkio.h
@@ -22,7 +22,7 @@
/*
* Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
*/
@@ -523,17 +523,25 @@ typedef struct dk_updatefw_32 {
/*
* ioctl to free space (e.g. SCSI UNMAP) off a disk.
+ * Pass a dkioc_free_list_t containing a list of extents to be freed.
*/
#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. */
+typedef struct dkioc_free_list_ext_s {
+ uint64_t dfle_start;
+ uint64_t dfle_length;
+} dkioc_free_list_ext_t;
+
+typedef struct dkioc_free_list_s {
+ uint64_t dfl_flags;
+ uint64_t dfl_num_exts;
+ uint64_t dfl_offset;
+ dkioc_free_list_ext_t dfl_exts[];
+} dkioc_free_list_t;
+#define DFL_SZ(num_exts) \
+ (sizeof (dkioc_free_list_t) + \
+ (num_exts) * sizeof (dkioc_free_list_ext_t))
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/dkioc_free_util.h b/usr/src/uts/common/sys/dkioc_free_util.h
new file mode 100644
index 0000000000..9e83ab3bff
--- /dev/null
+++ b/usr/src/uts/common/sys/dkioc_free_util.h
@@ -0,0 +1,34 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017 Nexenta Inc. All rights reserved.
+ */
+
+#ifndef _SYS_DKIOC_FREE_UTIL_H
+#define _SYS_DKIOC_FREE_UTIL_H
+
+#include <sys/dkio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DFL_COPYIN_MAX_EXTS (1024 * 1024)
+
+int dfl_copyin(void *arg, dkioc_free_list_t **out, int ddi_flags, int kmflags);
+void dfl_free(dkioc_free_list_t *dfl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DKIOC_FREE_UTIL_H */
diff --git a/usr/src/uts/common/sys/scsi/targets/sddef.h b/usr/src/uts/common/sys/scsi/targets/sddef.h
index da9f25c4ae..30c6ae54d1 100644
--- a/usr/src/uts/common/sys/scsi/targets/sddef.h
+++ b/usr/src/uts/common/sys/scsi/targets/sddef.h
@@ -23,7 +23,7 @@
*/
/*
* Copyright 2011 cyril.galibern@opensvc.com
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SYS_SCSI_TARGETS_SDDEF_H
@@ -215,6 +215,35 @@ struct sd_mapblocksize_info {
_NOTE(SCHEME_PROTECTS_DATA("unshared data", sd_mapblocksize_info))
+/* Thin-provisioning (UNMAP) flags for un_thin_flags. */
+enum {
+ SD_THIN_PROV_ENABLED = 1 << 0, /* UNMAP available */
+ SD_THIN_PROV_READ_ZEROS = 1 << 1 /* unmapped blk = zeros */
+};
+
+/*
+ * Device limits as read from the Block Limits VPD page (0xB0). If the page
+ * is unavailable, will be filled with some defaults.
+ */
+typedef struct sd_blk_limits_s {
+ uint16_t lim_opt_xfer_len_gran;
+ uint32_t lim_max_xfer_len;
+ uint32_t lim_opt_xfer_len;
+ uint32_t lim_max_pfetch_len;
+ uint32_t lim_max_unmap_lba_cnt;
+ uint32_t lim_max_unmap_descr_cnt;
+ uint32_t lim_opt_unmap_gran;
+ uint32_t lim_unmap_gran_align;
+ uint64_t lim_max_write_same_len;
+} sd_blk_limits_t;
+
+typedef struct sd_unmapstats {
+ kstat_named_t us_cmds;
+ kstat_named_t us_errs;
+ kstat_named_t us_extents;
+ kstat_named_t us_bytes;
+} sd_unmapstats_t;
+
/*
* sd_lun: The main data structure for a scsi logical unit.
* Stored as the softstate structure for each device.
@@ -356,6 +385,8 @@ struct sd_lun {
union ocmap un_ocmap; /* open partition map */
struct kstat *un_pstats[NSDMAP]; /* partition statistics */
struct kstat *un_stats; /* disk statistics */
+ sd_unmapstats_t *un_unmapstats; /* UNMAP stats structure */
+ struct kstat *un_unmapstats_ks; /* UNMAP kstat */
kstat_t *un_errstats; /* for error statistics */
uint64_t un_exclopen; /* exclusive open bitmask */
ddi_devid_t un_devid; /* device id */
@@ -508,6 +539,12 @@ struct sd_lun {
uint64_t un_rmw_incre_count; /* count I/O */
timeout_id_t un_rmw_msg_timeid; /* for RMW message control */
+ /* Thin provisioning support (see SD_THIN_PROV_*) */
+ uint64_t un_thin_flags;
+
+ /* Block limits (0xB0 VPD page) */
+ sd_blk_limits_t un_blk_lim;
+
/* For timeout callback to issue a START STOP UNIT command */
timeout_id_t un_startstop_timeid;
@@ -2385,7 +2422,8 @@ typedef struct disk_power_attr_pc {
#define SD_VPD_ASCII_OP_PG 0x08 /* 0x82 - ASCII Op Defs */
#define SD_VPD_DEVID_WWN_PG 0x10 /* 0x83 - Device Identification */
#define SD_VPD_EXTENDED_DATA_PG 0x80 /* 0x86 - Extended data about the lun */
-#define SD_VPD_DEV_CHARACTER_PG 0x400 /* 0xB1 - Device Characteristics */
+#define SD_VPD_BLK_LIMITS_PG 0x400 /* 0xB0 - Block Limits */
+#define SD_VPD_DEV_CHARACTER_PG 0x800 /* 0xB1 - Device Characteristics */
/*
* Non-volatile cache support