diff options
author | delphij <delphij@FreeBSD.org> | 2013-08-24 00:19:26 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2013-08-24 00:19:26 +0000 |
commit | be3d457caae387b1bb46148d42378dac65c6dde6 (patch) | |
tree | 31123a1a00fba9568e73447852a12ba31991dc58 /sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c | |
parent | 677bfa2265deec3ae900ff7a8b6f214fb69ba37a (diff) | |
download | FreeBSD-src-be3d457caae387b1bb46148d42378dac65c6dde6.zip FreeBSD-src-be3d457caae387b1bb46148d42378dac65c6dde6.tar.gz |
MFV r254747:
Fix a panic from dbuf_free_range() from dmu_free_object() while
doing zfs receive. This is a regression from FreeBSD r253821.
Illumos ZFS issues:
4047 panic from dbuf_free_range() from dmu_free_object() while
doing zfs receive
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c | 31 |
1 files changed, 21 insertions, 10 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c index 759fd7a..92ee7c3 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c @@ -40,6 +40,12 @@ #include <sys/sa.h> #include <sys/sa_impl.h> +/* + * Number of times that zfs_free_range() took the slow path while doing + * a zfs receive. A nonzero value indicates a potential performance problem. + */ +uint64_t zfs_free_range_recv_miss; + static void dbuf_destroy(dmu_buf_impl_t *db); static boolean_t dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx); static void dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx); @@ -819,20 +825,22 @@ dbuf_free_range(dnode_t *dn, uint64_t start, uint64_t end, dmu_tx_t *tx) } dprintf_dnode(dn, "start=%llu end=%llu\n", start, end); - if (dmu_objset_is_receiving(dn->dn_objset)) { + mutex_enter(&dn->dn_dbufs_mtx); + if (start >= dn->dn_unlisted_l0_blkid * dn->dn_datablksz) { + /* There can't be any dbufs in this range; no need to search. */ + mutex_exit(&dn->dn_dbufs_mtx); + return; + } else if (dmu_objset_is_receiving(dn->dn_objset)) { /* - * When processing a free record from a zfs receive, - * there should have been no previous modifications to the - * data in this range. Therefore there should be no dbufs - * in the range. Searching dn_dbufs for these non-existent - * dbufs can be very expensive, so simply ignore this. + * If we are receiving, we expect there to be no dbufs in + * the range to be freed, because receive modifies each + * block at most once, and in offset order. If this is + * not the case, it can lead to performance problems, + * so note that we unexpectedly took the slow path. */ - VERIFY3P(dbuf_find(dn, 0, start), ==, NULL); - VERIFY3P(dbuf_find(dn, 0, end), ==, NULL); - return; + atomic_inc_64(&zfs_free_range_recv_miss); } - mutex_enter(&dn->dn_dbufs_mtx); for (db = list_head(&dn->dn_dbufs); db; db = db_next) { db_next = list_next(&dn->dn_dbufs, db); ASSERT(db->db_blkid != DMU_BONUS_BLKID); @@ -1720,6 +1728,9 @@ dbuf_create(dnode_t *dn, uint8_t level, uint64_t blkid, return (odb); } list_insert_head(&dn->dn_dbufs, db); + if (db->db_level == 0 && db->db_blkid >= + dn->dn_unlisted_l0_blkid) + dn->dn_unlisted_l0_blkid = db->db_blkid + 1; db->db_state = DB_UNCACHED; mutex_exit(&dn->dn_dbufs_mtx); arc_space_consume(sizeof (dmu_buf_impl_t), ARC_SPACE_OTHER); |