svn commit: r286544 - in vendor-sys/illumos/dist/uts/common/fs/zfs: . sys
Alexander Motin
mav at FreeBSD.org
Sun Aug 9 19:35:10 UTC 2015
Author: mav
Date: Sun Aug 9 19:35:08 2015
New Revision: 286544
URL: https://svnweb.freebsd.org/changeset/base/286544
Log:
5630 stale bonus buffer in recycled dnode_t leads to data corruption
Reviewed by: Matthew Ahrens <mahrens at delphix.com>
Reviewed by: George Wilson <george at delphix.com>
Reviewed by: Will Andrews <will at freebsd.org>
Approved by: Robert Mustacchi <rm at joyent.com>
Author: Justin T. Gibbs <justing at spectralogic.com>
Modified:
vendor-sys/illumos/dist/uts/common/fs/zfs/dbuf.c
vendor-sys/illumos/dist/uts/common/fs/zfs/dnode.c
vendor-sys/illumos/dist/uts/common/fs/zfs/dnode_sync.c
vendor-sys/illumos/dist/uts/common/fs/zfs/sys/dnode.h
Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/dbuf.c
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/dbuf.c Sun Aug 9 19:29:10 2015 (r286543)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/dbuf.c Sun Aug 9 19:35:08 2015 (r286544)
@@ -2203,21 +2203,60 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db,
if (holds == 0) {
if (db->db_blkid == DMU_BONUS_BLKID) {
- mutex_exit(&db->db_mtx);
+ dnode_t *dn;
/*
- * If the dnode moves here, we cannot cross this barrier
- * until the move completes.
+ * If the dnode moves here, we cannot cross this
+ * barrier until the move completes.
*/
DB_DNODE_ENTER(db);
- atomic_dec_32(&DB_DNODE(db)->dn_dbufs_count);
+
+ dn = DB_DNODE(db);
+ atomic_dec_32(&dn->dn_dbufs_count);
+
+ /*
+ * Decrementing the dbuf count means that the bonus
+ * buffer's dnode hold is no longer discounted in
+ * dnode_move(). The dnode cannot move until after
+ * the dnode_rele_and_unlock() below.
+ */
DB_DNODE_EXIT(db);
+
/*
- * The bonus buffer's dnode hold is no longer discounted
- * in dnode_move(). The dnode cannot move until after
- * the dnode_rele().
+ * Do not reference db after its lock is dropped.
+ * Another thread may evict it.
*/
- dnode_rele(DB_DNODE(db), db);
+ mutex_exit(&db->db_mtx);
+
+ /*
+ * If the dnode has been freed, evict the bonus
+ * buffer immediately. The data in the bonus
+ * buffer is no longer relevant and this prevents
+ * a stale bonus buffer from being associated
+ * with this dnode_t should the dnode_t be reused
+ * prior to being destroyed.
+ */
+ mutex_enter(&dn->dn_mtx);
+ if (dn->dn_type == DMU_OT_NONE ||
+ dn->dn_free_txg != 0) {
+ /*
+ * Drop dn_mtx. It is a leaf lock and
+ * cannot be held when dnode_evict_bonus()
+ * acquires other locks in order to
+ * perform the eviction.
+ *
+ * Freed dnodes cannot be reused until the
+ * last hold is released. Since this bonus
+ * buffer has a hold, the dnode will remain
+ * in the free state, even without dn_mtx
+ * held, until the dnode_rele_and_unlock()
+ * below.
+ */
+ mutex_exit(&dn->dn_mtx);
+ dnode_evict_bonus(dn);
+ mutex_enter(&dn->dn_mtx);
+ }
+ dnode_rele_and_unlock(dn, db);
} else if (db->db_buf == NULL) {
/*
* This is a special case: we never associated this
Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/dnode.c
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/dnode.c Sun Aug 9 19:29:10 2015 (r286543)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/dnode.c Sun Aug 9 19:35:08 2015 (r286544)
@@ -1223,12 +1223,18 @@ dnode_add_ref(dnode_t *dn, void *tag)
void
dnode_rele(dnode_t *dn, void *tag)
{
+ mutex_enter(&dn->dn_mtx);
+ dnode_rele_and_unlock(dn, tag);
+}
+
+void
+dnode_rele_and_unlock(dnode_t *dn, void *tag)
+{
uint64_t refs;
/* Get while the hold prevents the dnode from moving. */
dmu_buf_impl_t *db = dn->dn_dbuf;
dnode_handle_t *dnh = dn->dn_handle;
- mutex_enter(&dn->dn_mtx);
refs = refcount_remove(&dn->dn_holds, tag);
mutex_exit(&dn->dn_mtx);
Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/dnode_sync.c
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/dnode_sync.c Sun Aug 9 19:29:10 2015 (r286543)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/dnode_sync.c Sun Aug 9 19:35:08 2015 (r286544)
@@ -430,6 +430,12 @@ dnode_evict_dbufs(dnode_t *dn)
}
mutex_exit(&dn->dn_dbufs_mtx);
+ dnode_evict_bonus(dn);
+}
+
+void
+dnode_evict_bonus(dnode_t *dn)
+{
rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
if (dn->dn_bonus && refcount_is_zero(&dn->dn_bonus->db_holds)) {
mutex_enter(&dn->dn_bonus->db_mtx);
Modified: vendor-sys/illumos/dist/uts/common/fs/zfs/sys/dnode.h
==============================================================================
--- vendor-sys/illumos/dist/uts/common/fs/zfs/sys/dnode.h Sun Aug 9 19:29:10 2015 (r286543)
+++ vendor-sys/illumos/dist/uts/common/fs/zfs/sys/dnode.h Sun Aug 9 19:35:08 2015 (r286544)
@@ -281,6 +281,7 @@ int dnode_hold_impl(struct objset *dd, u
void *ref, dnode_t **dnp);
boolean_t dnode_add_ref(dnode_t *dn, void *ref);
void dnode_rele(dnode_t *dn, void *ref);
+void dnode_rele_and_unlock(dnode_t *dn, void *tag);
void dnode_setdirty(dnode_t *dn, dmu_tx_t *tx);
void dnode_sync(dnode_t *dn, dmu_tx_t *tx);
void dnode_allocate(dnode_t *dn, dmu_object_type_t ot, int blocksize, int ibs,
@@ -302,6 +303,7 @@ void dnode_fini(void);
int dnode_next_offset(dnode_t *dn, int flags, uint64_t *off,
int minlvl, uint64_t blkfill, uint64_t txg);
void dnode_evict_dbufs(dnode_t *dn);
+void dnode_evict_bonus(dnode_t *dn);
#ifdef ZFS_DEBUG
More information about the svn-src-vendor
mailing list