svn commit: r337671 - head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs

Matt Macy mmacy at FreeBSD.org
Sun Aug 12 01:17:33 UTC 2018


Author: mmacy
Date: Sun Aug 12 01:17:32 2018
New Revision: 337671
URL: https://svnweb.freebsd.org/changeset/base/337671

Log:
  MFV/ZoL:    Fix PANIC: metaslab_free_dva(): bad DVA X:Y:Z
  
  commit 81edd3e83409218879e7af293daa86b0c40eb015
  Author: Peng <peng.hse at xtaotech.com>
  Date:   Wed Jun 8 15:22:07 2016 +0800
  
      Fix PANIC: metaslab_free_dva(): bad DVA X:Y:Z
  
      The following scenario can result in garbage in the dn_spill field.
      The db->db_blkptr must be set to NULL when DNODE_FLAG_SPILL_BLKPTR
      is clear to ensure the dn_spill field is cleared.
  
      Current txg = A.
      * A new spill buffer is created. Its dbuf is initialized with
        db_blkptr = NULL and it's dirtied.
  
      Current txg = B.
      * The spill buffer is modified. It's marked as dirty in this txg.
      * Additional changes make the spill buffer unnecessary because the
        xattr fits into the bonus buffer, so it's removed. The dbuf is
        undirtied in this txg, but it's still referenced and cannot be
        destroyed.
  
      Current txg = C.
      * Starts syncing of txg A
      * dbuf_sync_leaf() is called for the spill buffer. Since db_blkptr
        is NULL, dbuf_check_blkptr() is called.
      * The dbuf starts being written and it reaches the ready state
        (not done yet).
      * A new change makes the spill buffer necessary again.
        sa_build_layouts() ends up calling dbuf_find() to locate the
        dbuf.  It finds the old dbuf because it has not been destroyed yet
        (it will be destroyed when the previous write is done and there
        are no more references). The old dbuf has db_blkptr != NULL.
      * txg A write is complete and the dbuf released. However it's still
        referenced, so it's not destroyed.
  
      Current txg = D.
      * Starts syncing of txg B
      * dbuf_sync_leaf() is called for the bonus buffer. Its contents are
        directly copied into the dnode, overwriting the blkptr area because,
        in txg B, the bonus buffer was big enough to hold the entire xattr.
      * At this point, the db_blkptr of the spill buffer used in txg C
        gets corrupted.
  
      Signed-off-by: Peng <peng.hse at xtaotech.com>
      Signed-off-by: Tim Chase <tim at chase2k.com>
      Signed-off-by: Brian Behlendorf <behlendorf1 at llnl.gov>
      Closes #3937

Modified:
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c	Sun Aug 12 01:10:18 2018	(r337670)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c	Sun Aug 12 01:17:32 2018	(r337671)
@@ -3182,6 +3182,22 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
 
 	if (db->db_blkid == DMU_SPILL_BLKID) {
 		mutex_enter(&dn->dn_mtx);
+		if (!(dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR)) {
+			/*
+			 * In the previous transaction group, the bonus buffer
+			 * was entirely used to store the attributes for the
+			 * dnode which overrode the dn_spill field.  However,
+			 * when adding more attributes to the file a spill
+			 * block was required to hold the extra attributes.
+			 *
+			 * Make sure to clear the garbage left in the dn_spill
+			 * field from the previous attributes in the bonus
+			 * buffer.  Otherwise, after writing out the spill
+			 * block to the new allocated dva, it will free
+			 * the old block pointed to by the invalid dn_spill.
+			 */
+			db->db_blkptr = NULL;
+		}
 		dn->dn_phys->dn_flags |= DNODE_FLAG_SPILL_BLKPTR;
 		mutex_exit(&dn->dn_mtx);
 	}


More information about the svn-src-head mailing list