diff options
author | Dan McDonald <[email protected]> | 2015-08-02 08:01:14 -0500 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2016-01-26 14:16:03 -0800 |
commit | 460a021391d32b7f2e9eb05a9a01946714174e3b (patch) | |
tree | a54672787d147121b669126c31c218868e1b1e56 /module/zfs/zvol.c | |
parent | d93b45aefc74b9c345ba3cdf3a227ee979a990cd (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 <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Diffstat (limited to 'module/zfs/zvol.c')
-rw-r--r-- | module/zfs/zvol.c | 67 |
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); |