diff options
author | Justin T. Gibbs <[email protected]> | 2015-03-12 11:10:35 +1100 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2015-03-12 15:40:39 -0700 |
commit | 4c7b7eedcde7fababf457ca04445e6d9d1617e29 (patch) | |
tree | 4aad4ad89dc24211d74bcc64b285b98b6b03ff31 /module/zfs/dbuf.c | |
parent | 73ad4a9f3cfc2e830de45c2a8be2823d01ab07a6 (diff) |
Illumos 5630 - stale bonus buffer in recycled dnode_t leads to data corruption
5630 stale bonus buffer in recycled dnode_t leads to data corruption
Author: Justin T. Gibbs <[email protected]>
Reviewed by: Matthew Ahrens <[email protected]>
Reviewed by: George Wilson <[email protected]>
Reviewed by: Will Andrews <[email protected]>
Approved by: Robert Mustacchi <[email protected]>
References:
https://www.illumos.org/issues/5630
https://github.com/illumos/illumos-gate/commit/cd485b4
Ported-by: Chris Dunlop <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Signed-off-by: Richard Yao <[email protected]>
Issue #3172
Diffstat (limited to 'module/zfs/dbuf.c')
-rw-r--r-- | module/zfs/dbuf.c | 55 |
1 files changed, 47 insertions, 8 deletions
diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c index 347fc3535..f10a04d11 100644 --- a/module/zfs/dbuf.c +++ b/module/zfs/dbuf.c @@ -2200,21 +2200,60 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag) 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); + + /* + * Do not reference db after its lock is dropped. + * Another thread may evict it. + */ + mutex_exit(&db->db_mtx); + /* - * The bonus buffer's dnode hold is no longer discounted - * in dnode_move(). The dnode cannot move until after - * the dnode_rele(). + * 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. */ - dnode_rele(DB_DNODE(db), db); + 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 |