aboutsummaryrefslogtreecommitdiffstats
path: root/uts/common/fs/zfs/zfs_znode.c
diff options
context:
space:
mode:
Diffstat (limited to 'uts/common/fs/zfs/zfs_znode.c')
-rw-r--r--uts/common/fs/zfs/zfs_znode.c71
1 files changed, 49 insertions, 22 deletions
diff --git a/uts/common/fs/zfs/zfs_znode.c b/uts/common/fs/zfs/zfs_znode.c
index 93545ee4a10a..536216deafe9 100644
--- a/uts/common/fs/zfs/zfs_znode.c
+++ b/uts/common/fs/zfs/zfs_znode.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2018 by Delphix. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
*/
@@ -110,6 +110,37 @@ znode_evict_error(dmu_buf_t *dbuf, void *user_ptr)
panic("evicting znode %p\n", user_ptr);
}
+/*
+ * This callback is invoked when acquiring a RL_WRITER or RL_APPEND lock on
+ * z_rangelock. It will modify the offset and length of the lock to reflect
+ * znode-specific information, and convert RL_APPEND to RL_WRITER. This is
+ * called with the rangelock_t's rl_lock held, which avoids races.
+ */
+static void
+zfs_rangelock_cb(locked_range_t *new, void *arg)
+{
+ znode_t *zp = arg;
+
+ /*
+ * If in append mode, convert to writer and lock starting at the
+ * current end of file.
+ */
+ if (new->lr_type == RL_APPEND) {
+ new->lr_offset = zp->z_size;
+ new->lr_type = RL_WRITER;
+ }
+
+ /*
+ * If we need to grow the block size then lock the whole file range.
+ */
+ uint64_t end_size = MAX(zp->z_size, new->lr_offset + new->lr_length);
+ if (end_size > zp->z_blksz && (!ISP2(zp->z_blksz) ||
+ zp->z_blksz < zp->z_zfsvfs->z_max_blksz)) {
+ new->lr_offset = 0;
+ new->lr_length = UINT64_MAX;
+ }
+}
+
/*ARGSUSED*/
static int
zfs_znode_cache_constructor(void *buf, void *arg, int kmflags)
@@ -131,9 +162,7 @@ zfs_znode_cache_constructor(void *buf, void *arg, int kmflags)
rw_init(&zp->z_name_lock, NULL, RW_DEFAULT, NULL);
mutex_init(&zp->z_acl_lock, NULL, MUTEX_DEFAULT, NULL);
- mutex_init(&zp->z_range_lock, NULL, MUTEX_DEFAULT, NULL);
- avl_create(&zp->z_range_avl, zfs_range_compare,
- sizeof (rl_t), offsetof(rl_t, r_node));
+ rangelock_init(&zp->z_rangelock, zfs_rangelock_cb, zp);
zp->z_dirlocks = NULL;
zp->z_acl_cached = NULL;
@@ -155,8 +184,7 @@ zfs_znode_cache_destructor(void *buf, void *arg)
rw_destroy(&zp->z_parent_lock);
rw_destroy(&zp->z_name_lock);
mutex_destroy(&zp->z_acl_lock);
- avl_destroy(&zp->z_range_avl);
- mutex_destroy(&zp->z_range_lock);
+ rangelock_fini(&zp->z_rangelock);
ASSERT(zp->z_dirlocks == NULL);
ASSERT(zp->z_acl_cached == NULL);
@@ -191,7 +219,6 @@ zfs_znode_move_impl(znode_t *ozp, znode_t *nzp)
nzp->z_id = ozp->z_id;
ASSERT(ozp->z_dirlocks == NULL); /* znode not in use */
- ASSERT(avl_numnodes(&ozp->z_range_avl) == 0);
nzp->z_unlinked = ozp->z_unlinked;
nzp->z_atime_dirty = ozp->z_atime_dirty;
nzp->z_zn_prefetch = ozp->z_zn_prefetch;
@@ -1470,20 +1497,20 @@ zfs_extend(znode_t *zp, uint64_t end)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
dmu_tx_t *tx;
- rl_t *rl;
+ locked_range_t *lr;
uint64_t newblksz;
int error;
/*
* We will change zp_size, lock the whole file.
*/
- rl = zfs_range_lock(zp, 0, UINT64_MAX, RL_WRITER);
+ lr = rangelock_enter(&zp->z_rangelock, 0, UINT64_MAX, RL_WRITER);
/*
* Nothing to do if file already at desired length.
*/
if (end <= zp->z_size) {
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (0);
}
tx = dmu_tx_create(zfsvfs->z_os);
@@ -1513,7 +1540,7 @@ zfs_extend(znode_t *zp, uint64_t end)
error = dmu_tx_assign(tx, TXG_WAIT);
if (error) {
dmu_tx_abort(tx);
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (error);
}
@@ -1525,7 +1552,7 @@ zfs_extend(znode_t *zp, uint64_t end)
VERIFY(0 == sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zp->z_zfsvfs),
&zp->z_size, sizeof (zp->z_size), tx));
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
dmu_tx_commit(tx);
@@ -1545,19 +1572,19 @@ static int
zfs_free_range(znode_t *zp, uint64_t off, uint64_t len)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- rl_t *rl;
+ locked_range_t *lr;
int error;
/*
* Lock the range being freed.
*/
- rl = zfs_range_lock(zp, off, len, RL_WRITER);
+ lr = rangelock_enter(&zp->z_rangelock, off, len, RL_WRITER);
/*
* Nothing to do if file already at desired length.
*/
if (off >= zp->z_size) {
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (0);
}
@@ -1566,7 +1593,7 @@ zfs_free_range(znode_t *zp, uint64_t off, uint64_t len)
error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, off, len);
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (error);
}
@@ -1585,7 +1612,7 @@ zfs_trunc(znode_t *zp, uint64_t end)
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
vnode_t *vp = ZTOV(zp);
dmu_tx_t *tx;
- rl_t *rl;
+ locked_range_t *lr;
int error;
sa_bulk_attr_t bulk[2];
int count = 0;
@@ -1593,20 +1620,20 @@ zfs_trunc(znode_t *zp, uint64_t end)
/*
* We will change zp_size, lock the whole file.
*/
- rl = zfs_range_lock(zp, 0, UINT64_MAX, RL_WRITER);
+ lr = rangelock_enter(&zp->z_rangelock, 0, UINT64_MAX, RL_WRITER);
/*
* Nothing to do if file already at desired length.
*/
if (end >= zp->z_size) {
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (0);
}
error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, end,
DMU_OBJECT_END);
if (error) {
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (error);
}
tx = dmu_tx_create(zfsvfs->z_os);
@@ -1616,7 +1643,7 @@ zfs_trunc(znode_t *zp, uint64_t end)
error = dmu_tx_assign(tx, TXG_WAIT);
if (error) {
dmu_tx_abort(tx);
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (error);
}
@@ -1657,7 +1684,7 @@ zfs_trunc(znode_t *zp, uint64_t end)
ASSERT(error == 0);
}
- zfs_range_unlock(rl);
+ rangelock_exit(lr);
return (0);
}