* Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2016, Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2015 by Chunwei Chen. All rights reserved.
+ * Copyright (c) 2019 Datto Inc.
*/
#include <sys/dmu.h>
int zfs_nopwrite_enabled = 1;
/*
- * Tunable to control percentage of dirtied blocks from frees in one TXG.
- * After this threshold is crossed, additional dirty blocks from frees
- * wait until the next TXG.
+ * Tunable to control percentage of dirtied L1 blocks from frees allowed into
+ * one TXG. After this threshold is crossed, additional dirty blocks from frees
+ * will wait until the next TXG.
* A value of zero will disable this throttle.
*/
-unsigned long zfs_per_txg_dirty_frees_percent = 30;
+unsigned long zfs_per_txg_dirty_frees_percent = 5;
/*
* Enable/disable forcing txg sync when dirty in dmu_offset_next.
/*
* This can be used for testing, to ensure that certain actions happen
* while in the middle of a remap (which might otherwise complete too
- * quickly).
+ * quickly). Used by ztest(8).
*/
-int zfs_object_remap_one_indirect_delay_ticks = 0;
+int zfs_object_remap_one_indirect_delay_ms = 0;
const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = {
- { DMU_BSWAP_UINT8, TRUE, FALSE, "unallocated" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "object directory" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "object array" },
- { DMU_BSWAP_UINT8, TRUE, FALSE, "packed nvlist" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "packed nvlist size" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "bpobj" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "bpobj header" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "SPA space map header" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "SPA space map" },
- { DMU_BSWAP_UINT64, TRUE, TRUE, "ZIL intent log" },
- { DMU_BSWAP_DNODE, TRUE, TRUE, "DMU dnode" },
- { DMU_BSWAP_OBJSET, TRUE, FALSE, "DMU objset" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "DSL directory" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL directory child map"},
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL dataset snap map" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL props" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "DSL dataset" },
- { DMU_BSWAP_ZNODE, TRUE, FALSE, "ZFS znode" },
- { DMU_BSWAP_OLDACL, TRUE, TRUE, "ZFS V0 ACL" },
- { DMU_BSWAP_UINT8, FALSE, TRUE, "ZFS plain file" },
- { DMU_BSWAP_ZAP, TRUE, TRUE, "ZFS directory" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "ZFS master node" },
- { DMU_BSWAP_ZAP, TRUE, TRUE, "ZFS delete queue" },
- { DMU_BSWAP_UINT8, FALSE, TRUE, "zvol object" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "zvol prop" },
- { DMU_BSWAP_UINT8, FALSE, TRUE, "other uint8[]" },
- { DMU_BSWAP_UINT64, FALSE, TRUE, "other uint64[]" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "other ZAP" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "persistent error log" },
- { DMU_BSWAP_UINT8, TRUE, FALSE, "SPA history" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "SPA history offsets" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "Pool properties" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL permissions" },
- { DMU_BSWAP_ACL, TRUE, TRUE, "ZFS ACL" },
- { DMU_BSWAP_UINT8, TRUE, TRUE, "ZFS SYSACL" },
- { DMU_BSWAP_UINT8, TRUE, TRUE, "FUID table" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "FUID table size" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL dataset next clones"},
- { DMU_BSWAP_ZAP, TRUE, FALSE, "scan work queue" },
- { DMU_BSWAP_ZAP, TRUE, TRUE, "ZFS user/group/project used" },
- { DMU_BSWAP_ZAP, TRUE, TRUE, "ZFS user/group/project quota"},
- { DMU_BSWAP_ZAP, TRUE, FALSE, "snapshot refcount tags"},
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DDT ZAP algorithm" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DDT statistics" },
- { DMU_BSWAP_UINT8, TRUE, TRUE, "System attributes" },
- { DMU_BSWAP_ZAP, TRUE, TRUE, "SA master node" },
- { DMU_BSWAP_ZAP, TRUE, TRUE, "SA attr registration" },
- { DMU_BSWAP_ZAP, TRUE, TRUE, "SA attr layouts" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "scan translations" },
- { DMU_BSWAP_UINT8, FALSE, TRUE, "deduplicated block" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL deadlist map" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "DSL deadlist map hdr" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL dir clones" },
- { DMU_BSWAP_UINT64, TRUE, FALSE, "bpobj subobj" }
+ {DMU_BSWAP_UINT8, TRUE, FALSE, FALSE, "unallocated" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "object directory" },
+ {DMU_BSWAP_UINT64, TRUE, TRUE, FALSE, "object array" },
+ {DMU_BSWAP_UINT8, TRUE, FALSE, FALSE, "packed nvlist" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "packed nvlist size" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "bpobj" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "bpobj header" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "SPA space map header" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "SPA space map" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, TRUE, "ZIL intent log" },
+ {DMU_BSWAP_DNODE, TRUE, FALSE, TRUE, "DMU dnode" },
+ {DMU_BSWAP_OBJSET, TRUE, TRUE, FALSE, "DMU objset" },
+ {DMU_BSWAP_UINT64, TRUE, TRUE, FALSE, "DSL directory" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL directory child map"},
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL dataset snap map" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL props" },
+ {DMU_BSWAP_UINT64, TRUE, TRUE, FALSE, "DSL dataset" },
+ {DMU_BSWAP_ZNODE, TRUE, FALSE, FALSE, "ZFS znode" },
+ {DMU_BSWAP_OLDACL, TRUE, FALSE, TRUE, "ZFS V0 ACL" },
+ {DMU_BSWAP_UINT8, FALSE, FALSE, TRUE, "ZFS plain file" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS directory" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "ZFS master node" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS delete queue" },
+ {DMU_BSWAP_UINT8, FALSE, FALSE, TRUE, "zvol object" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "zvol prop" },
+ {DMU_BSWAP_UINT8, FALSE, FALSE, TRUE, "other uint8[]" },
+ {DMU_BSWAP_UINT64, FALSE, FALSE, TRUE, "other uint64[]" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "other ZAP" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "persistent error log" },
+ {DMU_BSWAP_UINT8, TRUE, FALSE, FALSE, "SPA history" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "SPA history offsets" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "Pool properties" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL permissions" },
+ {DMU_BSWAP_ACL, TRUE, FALSE, TRUE, "ZFS ACL" },
+ {DMU_BSWAP_UINT8, TRUE, FALSE, TRUE, "ZFS SYSACL" },
+ {DMU_BSWAP_UINT8, TRUE, FALSE, TRUE, "FUID table" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "FUID table size" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL dataset next clones"},
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "scan work queue" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS user/group/project used" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS user/group/project quota"},
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "snapshot refcount tags"},
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "DDT ZAP algorithm" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "DDT statistics" },
+ {DMU_BSWAP_UINT8, TRUE, FALSE, TRUE, "System attributes" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "SA master node" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "SA attr registration" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "SA attr layouts" },
+ {DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "scan translations" },
+ {DMU_BSWAP_UINT8, FALSE, FALSE, TRUE, "deduplicated block" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL deadlist map" },
+ {DMU_BSWAP_UINT64, TRUE, TRUE, FALSE, "DSL deadlist map hdr" },
+ {DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL dir clones" },
+ {DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "bpobj subobj" }
};
const dmu_object_byteswap_info_t dmu_ot_byteswap[DMU_BSWAP_NUMFUNCS] = {
}
/*
- * returns ENOENT, EIO, or 0.
+ * Lookup and hold the bonus buffer for the provided dnode. If the dnode
+ * has not yet been allocated a new bonus dbuf a will be allocated.
+ * Returns ENOENT, EIO, or 0.
*/
-int
-dmu_bonus_hold_impl(objset_t *os, uint64_t object, void *tag, uint32_t flags,
- dmu_buf_t **dbp)
+int dmu_bonus_hold_by_dnode(dnode_t *dn, void *tag, dmu_buf_t **dbp,
+ uint32_t flags)
{
- dnode_t *dn;
dmu_buf_impl_t *db;
int error;
uint32_t db_flags = DB_RF_MUST_SUCCEED;
if (flags & DMU_READ_NO_DECRYPT)
db_flags |= DB_RF_NO_DECRYPT;
- error = dnode_hold(os, object, FTAG, &dn);
- if (error)
- return (error);
-
rw_enter(&dn->dn_struct_rwlock, RW_READER);
if (dn->dn_bonus == NULL) {
rw_exit(&dn->dn_struct_rwlock);
db = dn->dn_bonus;
/* as long as the bonus buf is held, the dnode will be held */
- if (refcount_add(&db->db_holds, tag) == 1) {
+ if (zfs_refcount_add(&db->db_holds, tag) == 1) {
VERIFY(dnode_add_ref(dn, db));
atomic_inc_32(&dn->dn_dbufs_count);
}
*/
rw_exit(&dn->dn_struct_rwlock);
- dnode_rele(dn, FTAG);
-
error = dbuf_read(db, NULL, db_flags);
if (error) {
dnode_evict_bonus(dn);
}
int
-dmu_bonus_hold(objset_t *os, uint64_t obj, void *tag, dmu_buf_t **dbp)
+dmu_bonus_hold(objset_t *os, uint64_t object, void *tag, dmu_buf_t **dbp)
{
- return (dmu_bonus_hold_impl(os, obj, tag, DMU_READ_NO_PREFETCH, dbp));
+ dnode_t *dn;
+ int error;
+
+ error = dnode_hold(os, object, FTAG, &dn);
+ if (error)
+ return (error);
+
+ error = dmu_bonus_hold_by_dnode(dn, tag, dbp, DMU_READ_NO_PREFETCH);
+ dnode_rele(dn, FTAG);
+
+ return (error);
}
/*
*
* On input, *start should be the first offset that does not need to be
* freed (e.g. "offset + length"). On return, *start will be the first
- * offset that should be freed.
+ * offset that should be freed and l1blks is set to the number of level 1
+ * indirect blocks found within the chunk.
*/
static int
-get_next_chunk(dnode_t *dn, uint64_t *start, uint64_t minimum)
+get_next_chunk(dnode_t *dn, uint64_t *start, uint64_t minimum, uint64_t *l1blks)
{
+ uint64_t blks;
uint64_t maxblks = DMU_MAX_ACCESS >> (dn->dn_indblkshift + 1);
/* bytes of data covered by a level-1 indirect block */
uint64_t iblkrange =
if (*start - minimum <= iblkrange * maxblks) {
*start = minimum;
+ /*
+ * Assume full L1 blocks and 128k recordsize to approximate the
+ * expected number of L1 blocks in this chunk
+ */
+ *l1blks = minimum / (1024 * 128 * 1024);
return (0);
}
ASSERT(ISP2(iblkrange));
- for (uint64_t blks = 0; *start > minimum && blks < maxblks; blks++) {
+ for (blks = 0; *start > minimum && blks < maxblks; blks++) {
int err;
/*
*start = minimum;
break;
} else if (err != 0) {
+ *l1blks = blks;
return (err);
}
}
if (*start < minimum)
*start = minimum;
+ *l1blks = blks;
return (0);
}
dirty_frees_threshold =
zfs_per_txg_dirty_frees_percent * zfs_dirty_data_max / 100;
else
- dirty_frees_threshold = zfs_dirty_data_max / 4;
+ dirty_frees_threshold = zfs_dirty_data_max / 20;
if (length == DMU_OBJECT_END || offset + length > object_size)
length = object_size - offset;
while (length != 0) {
uint64_t chunk_end, chunk_begin, chunk_len;
uint64_t long_free_dirty_all_txgs = 0;
+ uint64_t l1blks;
dmu_tx_t *tx;
if (dmu_objset_zfs_unmounting(dn->dn_objset))
chunk_end = chunk_begin = offset + length;
/* move chunk_begin backwards to the beginning of this chunk */
- err = get_next_chunk(dn, &chunk_begin, offset);
+ err = get_next_chunk(dn, &chunk_begin, offset, &l1blks);
if (err)
return (err);
ASSERT3U(chunk_begin, >=, offset);
*/
if (dirty_frees_threshold != 0 &&
long_free_dirty_all_txgs >= dirty_frees_threshold) {
+ DMU_TX_STAT_BUMP(dmu_tx_dirty_frees_delay);
txg_wait_open(dp, 0);
continue;
}
return (err);
}
+ /*
+ * In order to prevent unnecessary write throttling, for each
+ * TXG, we track the cumulative size of L1 blocks being dirtied
+ * in dnode_free_range() below. We compare this number to a
+ * tunable threshold, past which we prevent new L1 dirty freeing
+ * blocks from being added into the open TXG. See
+ * dmu_free_long_range_impl() for details. The threshold
+ * prevents write throttle activation due to dirty freeing L1
+ * blocks taking up a large percentage of zfs_dirty_data_max.
+ */
mutex_enter(&dp->dp_lock);
dp->dp_long_free_dirty_pertxg[dmu_tx_get_txg(tx) & TXG_MASK] +=
- chunk_len;
+ l1blks << dn->dn_indblkshift;
mutex_exit(&dp->dp_lock);
DTRACE_PROBE3(free__long__range,
uint64_t, long_free_dirty_all_txgs, uint64_t, chunk_len,
uint64_t last_removal_txg, uint64_t offset)
{
uint64_t l1blkid = dbuf_whichblock(dn, 1, offset);
+ dnode_t *dn_tx;
int err = 0;
rw_enter(&dn->dn_struct_rwlock, RW_READER);
/*
* If this L1 was already written after the last removal, then we've
- * already tried to remap it.
+ * already tried to remap it. An additional hold is taken after the
+ * dmu_tx_assign() to handle the case where the dnode is freed while
+ * waiting for the next open txg.
*/
if (birth <= last_removal_txg &&
dbuf_read(dbuf, NULL, DB_RF_MUST_SUCCEED) == 0 &&
dmu_tx_hold_remap_l1indirect(tx, dn->dn_object);
err = dmu_tx_assign(tx, TXG_WAIT);
if (err == 0) {
- (void) dbuf_dirty(dbuf, tx);
+ err = dnode_hold(os, dn->dn_object, FTAG, &dn_tx);
+ if (err == 0) {
+ (void) dbuf_dirty(dbuf, tx);
+ dnode_rele(dn_tx, FTAG);
+ }
dmu_tx_commit(tx);
} else {
dmu_tx_abort(tx);
dbuf_rele(dbuf, FTAG);
- delay(zfs_object_remap_one_indirect_delay_ticks);
+ delay(MSEC_TO_TICK(zfs_object_remap_one_indirect_delay_ms));
return (err);
}
{
uint64_t offset, l1span;
int err;
- dnode_t *dn;
+ dnode_t *dn, *dn_tx;
err = dnode_hold(os, object, FTAG, &dn);
if (err != 0) {
/*
* If the dnode has no indirect blocks, we cannot dirty them.
* We still want to remap the blkptr(s) in the dnode if
- * appropriate, so mark it as dirty.
+ * appropriate, so mark it as dirty. An additional hold is
+ * taken after the dmu_tx_assign() to handle the case where
+ * the dnode is freed while waiting for the next open txg.
*/
if (err == 0 && dnode_needs_remap(dn)) {
dmu_tx_t *tx = dmu_tx_create(os);
- dmu_tx_hold_bonus(tx, dn->dn_object);
- if ((err = dmu_tx_assign(tx, TXG_WAIT)) == 0) {
- dnode_setdirty(dn, tx);
+ dmu_tx_hold_bonus(tx, object);
+ err = dmu_tx_assign(tx, TXG_WAIT);
+ if (err == 0) {
+ err = dnode_hold(os, object, FTAG, &dn_tx);
+ if (err == 0) {
+ dnode_setdirty(dn_tx, tx);
+ dnode_rele(dn_tx, FTAG);
+ }
dmu_tx_commit(tx);
} else {
dmu_tx_abort(tx);
dmu_buf_t *dst_handle;
dmu_buf_impl_t *dstdb;
dmu_buf_impl_t *srcdb = (dmu_buf_impl_t *)handle;
+ dmu_object_type_t type;
arc_buf_t *abuf;
uint64_t datalen;
boolean_t byteorder;
dstdb = (dmu_buf_impl_t *)dst_handle;
datalen = arc_buf_size(srcdb->db_buf);
+ DB_DNODE_ENTER(dstdb);
+ type = DB_DNODE(dstdb)->dn_type;
+ DB_DNODE_EXIT(dstdb);
+
/* allocated an arc buffer that matches the type of srcdb->db_buf */
if (arc_is_encrypted(srcdb->db_buf)) {
arc_get_raw_params(srcdb->db_buf, &byteorder, salt, iv, mac);
abuf = arc_loan_raw_buf(os->os_spa, dmu_objset_id(os),
- byteorder, salt, iv, mac, DB_DNODE(dstdb)->dn_type,
+ byteorder, salt, iv, mac, type,
datalen, arc_buf_lsize(srcdb->db_buf),
arc_get_compression(srcdb->db_buf));
} else {
ASSERT3U(arc_get_compression(srcdb->db_buf),
==, ZIO_COMPRESS_OFF);
abuf = arc_loan_buf(os->os_spa,
- DMU_OT_IS_METADATA(DB_DNODE(dstdb)->dn_type), datalen);
+ DMU_OT_IS_METADATA(type), datalen);
}
ASSERT3U(datalen, ==, arc_buf_size(abuf));
* If this is not possible copy the contents of passed arc buf via
* dmu_write().
*/
-void
+int
dmu_assign_arcbuf_by_dnode(dnode_t *dn, uint64_t offset, arc_buf_t *buf,
dmu_tx_t *tx)
{
rw_enter(&dn->dn_struct_rwlock, RW_READER);
blkid = dbuf_whichblock(dn, 0, offset);
- VERIFY((db = dbuf_hold(dn, blkid, FTAG)) != NULL);
+ db = dbuf_hold(dn, blkid, FTAG);
+ if (db == NULL)
+ return (SET_ERROR(EIO));
rw_exit(&dn->dn_struct_rwlock);
/*
dmu_return_arcbuf(buf);
XUIOSTAT_BUMP(xuiostat_wbuf_copied);
}
+
+ return (0);
}
-void
+int
dmu_assign_arcbuf_by_dbuf(dmu_buf_t *handle, uint64_t offset, arc_buf_t *buf,
dmu_tx_t *tx)
{
+ int err;
dmu_buf_impl_t *dbuf = (dmu_buf_impl_t *)handle;
DB_DNODE_ENTER(dbuf);
- dmu_assign_arcbuf_by_dnode(DB_DNODE(dbuf), offset, buf, tx);
+ err = dmu_assign_arcbuf_by_dnode(DB_DNODE(dbuf), offset, buf, tx);
DB_DNODE_EXIT(dbuf);
+
+ return (err);
}
typedef struct {
dmu_sync_arg_t *dsa = varg;
dbuf_dirty_record_t *dr = dsa->dsa_dr;
dmu_buf_impl_t *db = dr->dr_dbuf;
+ zgd_t *zgd = dsa->dsa_zgd;
+
+ /*
+ * Record the vdev(s) backing this blkptr so they can be flushed after
+ * the writes for the lwb have completed.
+ */
+ if (zio->io_error == 0) {
+ zil_lwb_add_block(zgd->zgd_lwb, zgd->zgd_bp);
+ }
mutex_enter(&db->db_mtx);
ASSERT(dr->dt.dl.dr_override_state == DR_IN_DMU_SYNC);
{
blkptr_t *bp = zio->io_bp;
dmu_sync_arg_t *dsa = zio->io_private;
- ASSERTV(blkptr_t *bp_orig = &zio->io_bp_orig);
-
- if (zio->io_error == 0 && !BP_IS_HOLE(bp)) {
- ASSERT(!(zio->io_flags & ZIO_FLAG_NOPWRITE));
- ASSERT(BP_IS_HOLE(bp_orig) || !BP_EQUAL(bp, bp_orig));
- ASSERT(zio->io_bp->blk_birth == zio->io_txg);
- ASSERT(zio->io_txg > spa_syncing_txg(zio->io_spa));
- zio_free(zio->io_spa, zio->io_txg, zio->io_bp);
+ zgd_t *zgd = dsa->dsa_zgd;
+
+ if (zio->io_error == 0) {
+ /*
+ * Record the vdev(s) backing this blkptr so they can be
+ * flushed after the writes for the lwb have completed.
+ */
+ zil_lwb_add_block(zgd->zgd_lwb, zgd->zgd_bp);
+
+ if (!BP_IS_HOLE(bp)) {
+ ASSERTV(blkptr_t *bp_orig = &zio->io_bp_orig);
+ ASSERT(!(zio->io_flags & ZIO_FLAG_NOPWRITE));
+ ASSERT(BP_IS_HOLE(bp_orig) || !BP_EQUAL(bp, bp_orig));
+ ASSERT(zio->io_bp->blk_birth == zio->io_txg);
+ ASSERT(zio->io_txg > spa_syncing_txg(zio->io_spa));
+ zio_free(zio->io_spa, zio->io_txg, zio->io_bp);
+ }
}
dmu_tx_commit(dsa->dsa_tx);
ASSERT(pio != NULL);
ASSERT(txg != 0);
- /* dbuf is within the locked range */
- ASSERT3U(db->db.db_offset, >=, zgd->zgd_rl->r_off);
- ASSERT3U(db->db.db_offset + db->db.db_size, <=,
- zgd->zgd_rl->r_off + zgd->zgd_rl->r_len);
-
SET_BOOKMARK(&zb, ds->ds_object,
db->db.db_object, db->db_level, db->db_blkid);
bzero(zp->zp_salt, ZIO_DATA_SALT_LEN);
bzero(zp->zp_iv, ZIO_DATA_IV_LEN);
bzero(zp->zp_mac, ZIO_DATA_MAC_LEN);
+ zp->zp_zpl_smallblk = DMU_OT_IS_FILE(zp->zp_type) ?
+ os->os_zpl_special_smallblock : 0;
ASSERT3U(zp->zp_compress, !=, ZIO_COMPRESS_INHERIT);
}
#if defined(_KERNEL)
EXPORT_SYMBOL(dmu_bonus_hold);
+EXPORT_SYMBOL(dmu_bonus_hold_by_dnode);
EXPORT_SYMBOL(dmu_buf_hold_array_by_bonus);
EXPORT_SYMBOL(dmu_buf_rele_array);
EXPORT_SYMBOL(dmu_prefetch);