summaryrefslogtreecommitdiffstats
path: root/module/zfs/zvol.c
diff options
context:
space:
mode:
authorDan McDonald <danmcd@nexenta.com>2015-08-02 08:01:14 -0500
committerBrian Behlendorf <behlendorf1@llnl.gov>2016-01-26 14:16:03 -0800
commit460a021391d32b7f2e9eb05a9a01946714174e3b (patch)
treea54672787d147121b669126c31c218868e1b1e56 /module/zfs/zvol.c
parentd93b45aefc74b9c345ba3cdf3a227ee979a990cd (diff)
Log zvol truncate/discard operations
As the comments in zvol_discard() suggested, the discard operation could be logged to the zil. This is a port of the relevant code from Nexenta as it was added in "701 UNMAP support for COMSTAR" and has been attributed to the author of that commit. References: https://github.com/Nexenta/illumos-nexenta/commit/b77b923 https://github.com/zfsonlinux/zfs/blob/089fa91b/module/zfs/zvol.c#L637 Ported-by: Tim Chase <tim@chase2k.com> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Diffstat (limited to 'module/zfs/zvol.c')
-rw-r--r--module/zfs/zvol.c67
1 files changed, 59 insertions, 8 deletions
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c
index 57d36468f..fe93b249d 100644
--- a/module/zfs/zvol.c
+++ b/module/zfs/zvol.c
@@ -33,6 +33,8 @@
*
* Volumes are persistent through reboot and module load. No user command
* needs to be run before opening and using a device.
+ *
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/dbuf.h>
@@ -42,6 +44,7 @@
#include <sys/zap.h>
#include <sys/zfeature.h>
#include <sys/zil_impl.h>
+#include <sys/dmu_tx.h>
#include <sys/zio.h>
#include <sys/zfs_rlock.h>
#include <sys/zfs_znode.h>
@@ -447,6 +450,24 @@ out:
}
/*
+ * 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
*/
@@ -484,7 +505,7 @@ zvol_replay_err(zvol_state_t *zv, lr_t *lr, boolean_t byteswap)
/*
* 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] = {
(zil_replay_func_t)zvol_replay_err, /* no such transaction type */
@@ -497,7 +518,7 @@ zil_replay_func_t zvol_replay_vector[TX_MAX_TYPE] = {
(zil_replay_func_t)zvol_replay_err, /* TX_LINK */
(zil_replay_func_t)zvol_replay_err, /* TX_RENAME */
(zil_replay_func_t)zvol_replay_write, /* TX_WRITE */
- (zil_replay_func_t)zvol_replay_err, /* TX_TRUNCATE */
+ (zil_replay_func_t)zvol_replay_truncate, /* TX_TRUNCATE */
(zil_replay_func_t)zvol_replay_err, /* TX_SETATTR */
(zil_replay_func_t)zvol_replay_err, /* TX_ACL */
};
@@ -636,6 +657,30 @@ out:
return (error);
}
+/*
+ * 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);
+}
+
static int
zvol_discard(struct bio *bio)
{
@@ -645,6 +690,7 @@ zvol_discard(struct bio *bio)
uint64_t end = start + size;
int error;
rl_t *rl;
+ dmu_tx_t *tx;
if (end > zv->zv_volsize)
return (SET_ERROR(EIO));
@@ -669,12 +715,17 @@ zvol_discard(struct bio *bio)
return (0);
rl = zfs_range_lock(&zv->zv_znode, start, size, RL_WRITER);
-
- error = dmu_free_long_range(zv->zv_objset, ZVOL_OBJ, start, size);
-
- /*
- * TODO: maybe we should add the operation to the log.
- */
+ 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, start, size, B_TRUE);
+ dmu_tx_commit(tx);
+ error = dmu_free_long_range(zv->zv_objset,
+ ZVOL_OBJ, start, size);
+ }
zfs_range_unlock(rl);