Skip to content
Permalink
Browse files

1700 Add SCSI UNMAP support

Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Igor Kozhukhov <igor@dilos.org>
Approved by: Dan McDonald <danmcd@joyent.com>
  • Loading branch information...
Saso Kiselkov authored and danmcd committed Sep 17, 2018
1 parent 008b34b commit 047c81d31d0f571d6652e97143cd15281de61e84
@@ -920,6 +920,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
@@ -160,6 +160,7 @@ GENUNIX_OBJS += \
devid_smp.o \
devpolicy.o \
disp_lock.o \
dkioc_free_util.o \
dnlc.o \
driver.o \
dumpsubr.o \
@@ -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) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
@@ -89,6 +89,7 @@
#include <sys/zfeature.h>
#include <sys/zio_checksum.h>
#include <sys/zil_impl.h>
#include <sys/dkioc_free_util.h>

#include "zfs_namecheck.h"

@@ -1778,43 +1779,63 @@ 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);

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
@@ -1827,10 +1848,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);

return (error);
}

@@ -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 @@ sbd_zvolset(char *zvol_name, char *comstarprop)

/*
* 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));
}
@@ -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);
}

@@ -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
}
@@ -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
*/

@@ -4845,7 +4845,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);
Oops, something went wrong.

0 comments on commit 047c81d

Please sign in to comment.
You can’t perform that action at this time.