summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/zfs/dnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs/zfs/dnode.c')
-rw-r--r--usr/src/uts/common/fs/zfs/dnode.c21
1 files changed, 18 insertions, 3 deletions
diff --git a/usr/src/uts/common/fs/zfs/dnode.c b/usr/src/uts/common/fs/zfs/dnode.c
index a2a985eb26..8fd083167b 100644
--- a/usr/src/uts/common/fs/zfs/dnode.c
+++ b/usr/src/uts/common/fs/zfs/dnode.c
@@ -395,6 +395,7 @@ dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
{
int i;
+ dmu_buf_impl_t *db = NULL;
ASSERT3U(blocksize, >=, SPA_MINBLOCKSIZE);
ASSERT3U(blocksize, <=, SPA_MAXBLOCKSIZE);
@@ -425,17 +426,25 @@ dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
/* change blocksize */
rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
+ if (blocksize != dn->dn_datablksz &&
+ (!BP_IS_HOLE(&dn->dn_phys->dn_blkptr[0]) ||
+ list_head(&dn->dn_dbufs) != NULL)) {
+ db = dbuf_hold(dn, 0, FTAG);
+ dbuf_new_size(db, blocksize, tx);
+ }
dnode_setdblksz(dn, blocksize);
dnode_setdirty(dn, tx);
dn->dn_next_blksz[tx->tx_txg&TXG_MASK] = blocksize;
rw_exit(&dn->dn_struct_rwlock);
+ if (db) {
+ dbuf_rele(db, FTAG);
+ db = NULL;
+ }
/* change type */
dn->dn_type = ot;
if (dn->dn_bonuslen != bonuslen) {
- dmu_buf_impl_t *db = NULL;
-
/* change bonus size */
if (bonuslen == 0)
bonuslen = 1; /* XXX */
@@ -453,7 +462,6 @@ dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
db->db.db_size = bonuslen;
mutex_exit(&db->db_mtx);
dbuf_dirty(db, tx);
- dbuf_rele(db, FTAG);
}
/* change bonus size and type */
@@ -465,6 +473,13 @@ dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
dn->dn_compress = ZIO_COMPRESS_INHERIT;
ASSERT3U(dn->dn_nblkptr, <=, DN_MAX_NBLKPTR);
+ /*
+ * NB: we have to do the dbuf_rele after we've changed the
+ * dn_bonuslen, for the sake of dbuf_verify().
+ */
+ if (db)
+ dbuf_rele(db, FTAG);
+
dn->dn_allocated_txg = tx->tx_txg;
mutex_exit(&dn->dn_mtx);
}