dmu_tx: Fix possible NULL pointer dereference
[zfs.git] / module / zfs / dmu_tx.c
index 17eb527..fd71413 100644 (file)
@@ -20,6 +20,8 @@
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
 #include <sys/dmu.h>
@@ -60,7 +62,7 @@ static kstat_t *dmu_tx_ksp;
 dmu_tx_t *
 dmu_tx_create_dd(dsl_dir_t *dd)
 {
-       dmu_tx_t *tx = kmem_zalloc(sizeof (dmu_tx_t), KM_SLEEP);
+       dmu_tx_t *tx = kmem_zalloc(sizeof (dmu_tx_t), KM_PUSHPAGE);
        tx->tx_dir = dd;
        if (dd)
                tx->tx_pool = dd->dd_pool;
@@ -68,7 +70,7 @@ dmu_tx_create_dd(dsl_dir_t *dd)
            offsetof(dmu_tx_hold_t, txh_node));
        list_create(&tx->tx_callbacks, sizeof (dmu_tx_callback_t),
            offsetof(dmu_tx_callback_t, dcb_node));
-#ifdef ZFS_DEBUG
+#ifdef DEBUG_DMU_TX
        refcount_create(&tx->tx_space_written);
        refcount_create(&tx->tx_space_freed);
 #endif
@@ -138,10 +140,10 @@ dmu_tx_hold_object_impl(dmu_tx_t *tx, objset_t *os, uint64_t object,
                }
        }
 
-       txh = kmem_zalloc(sizeof (dmu_tx_hold_t), KM_SLEEP);
+       txh = kmem_zalloc(sizeof (dmu_tx_hold_t), KM_PUSHPAGE);
        txh->txh_tx = tx;
        txh->txh_dnode = dn;
-#ifdef ZFS_DEBUG
+#ifdef DEBUG_DMU_TX
        txh->txh_type = type;
        txh->txh_arg1 = arg1;
        txh->txh_arg2 = arg2;
@@ -299,6 +301,7 @@ dmu_tx_count_write(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
                        delta = P2NPHASE(off, dn->dn_datablksz);
                }
 
+               min_ibs = max_ibs = dn->dn_indblkshift;
                if (dn->dn_maxblkid > 0) {
                        /*
                         * The blocksize can't change,
@@ -306,13 +309,6 @@ dmu_tx_count_write(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
                         */
                        ASSERT(dn->dn_datablkshift != 0);
                        min_bs = max_bs = dn->dn_datablkshift;
-                       min_ibs = max_ibs = dn->dn_indblkshift;
-               } else if (dn->dn_indblkshift > max_ibs) {
-                       /*
-                        * This ensures that if we reduce DN_MAX_INDBLKSHIFT,
-                        * the code will still work correctly on older pools.
-                        */
-                       min_ibs = max_ibs = dn->dn_indblkshift;
                }
 
                /*
@@ -444,6 +440,7 @@ dmu_tx_count_free(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
        dsl_dataset_t *ds = dn->dn_objset->os_dsl_dataset;
        spa_t *spa = txh->txh_tx->tx_pool->dp_spa;
        int epbs;
+       uint64_t l0span = 0, nl1blks = 0;
 
        if (dn->dn_nlevels == 0)
                return;
@@ -476,6 +473,7 @@ dmu_tx_count_free(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
                        nblks = dn->dn_maxblkid - blkid;
 
        }
+       l0span = nblks;    /* save for later use to calc level > 1 overhead */
        if (dn->dn_nlevels == 1) {
                int i;
                for (i = 0; i < nblks; i++) {
@@ -488,24 +486,10 @@ dmu_tx_count_free(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
                        }
                        unref += BP_GET_ASIZE(bp);
                }
+               nl1blks = 1;
                nblks = 0;
        }
 
-       /*
-        * Add in memory requirements of higher-level indirects.
-        * This assumes a worst-possible scenario for dn_nlevels.
-        */
-       {
-               uint64_t blkcnt = 1 + ((nblks >> epbs) >> epbs);
-               int level = (dn->dn_nlevels > 1) ? 2 : 1;
-
-               while (level++ < DN_MAX_LEVELS) {
-                       txh->txh_memory_tohold += blkcnt << dn->dn_indblkshift;
-                       blkcnt = 1 + (blkcnt >> epbs);
-               }
-               ASSERT(blkcnt <= dn->dn_nblkptr);
-       }
-
        lastblk = blkid + nblks - 1;
        while (nblks) {
                dmu_buf_impl_t *dbuf;
@@ -576,11 +560,35 @@ dmu_tx_count_free(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
                }
                dbuf_rele(dbuf, FTAG);
 
+               ++nl1blks;
                blkid += tochk;
                nblks -= tochk;
        }
        rw_exit(&dn->dn_struct_rwlock);
 
+       /*
+        * Add in memory requirements of higher-level indirects.
+        * This assumes a worst-possible scenario for dn_nlevels and a
+        * worst-possible distribution of l1-blocks over the region to free.
+        */
+       {
+               uint64_t blkcnt = 1 + ((l0span >> epbs) >> epbs);
+               int level = 2;
+               /*
+                * Here we don't use DN_MAX_LEVEL, but calculate it with the
+                * given datablkshift and indblkshift. This makes the
+                * difference between 19 and 8 on large files.
+                */
+               int maxlevel = 2 + (DN_MAX_OFFSET_SHIFT - dn->dn_datablkshift) /
+                   (dn->dn_indblkshift - SPA_BLKPTRSHIFT);
+
+               while (level++ < maxlevel) {
+                       txh->txh_memory_tohold += MAX(MIN(blkcnt, nl1blks), 1)
+                           << dn->dn_indblkshift;
+                       blkcnt = 1 + (blkcnt >> epbs);
+               }
+       }
+
        /* account for new level 1 indirect blocks that might show up */
        if (skipped > 0) {
                txh->txh_fudge += skipped << dn->dn_indblkshift;
@@ -690,9 +698,11 @@ dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, const char *name)
                return;
        }
 
-       ASSERT3P(dmu_ot[dn->dn_type].ot_byteswap, ==, zap_byteswap);
+       ASSERT3U(DMU_OT_BYTESWAP(dn->dn_type), ==, DMU_BSWAP_ZAP);
 
        if (dn->dn_maxblkid == 0 && !add) {
+               blkptr_t *bp;
+
                /*
                 * If there is only one block  (i.e. this is a micro-zap)
                 * and we are not adding anything, the accounting is simple.
@@ -707,14 +717,13 @@ dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, const char *name)
                 * Use max block size here, since we don't know how much
                 * the size will change between now and the dbuf dirty call.
                 */
+               bp = &dn->dn_phys->dn_blkptr[0];
                if (dsl_dataset_block_freeable(dn->dn_objset->os_dsl_dataset,
-                   &dn->dn_phys->dn_blkptr[0],
-                   dn->dn_phys->dn_blkptr[0].blk_birth)) {
+                   bp, bp->blk_birth))
                        txh->txh_space_tooverwrite += SPA_MAXBLOCKSIZE;
-               } else {
+               else
                        txh->txh_space_towrite += SPA_MAXBLOCKSIZE;
-               }
-               if (dn->dn_phys->dn_blkptr[0].blk_birth)
+               if (!BP_IS_HOLE(bp))
                        txh->txh_space_tounref += SPA_MAXBLOCKSIZE;
                return;
        }
@@ -764,12 +773,13 @@ void
 dmu_tx_hold_space(dmu_tx_t *tx, uint64_t space)
 {
        dmu_tx_hold_t *txh;
+
        ASSERT(tx->tx_txg == 0);
 
        txh = dmu_tx_hold_object_impl(tx, tx->tx_objset,
            DMU_NEW_OBJECT, THT_SPACE, space, 0);
-
-       txh->txh_space_towrite += space;
+       if (txh)
+               txh->txh_space_towrite += space;
 }
 
 int
@@ -798,7 +808,7 @@ dmu_tx_holds(dmu_tx_t *tx, uint64_t object)
        return (holds);
 }
 
-#ifdef ZFS_DEBUG
+#ifdef DEBUG_DMU_TX
 void
 dmu_tx_dirty_buf(dmu_tx_t *tx, dmu_buf_impl_t *db)
 {
@@ -808,6 +818,7 @@ dmu_tx_dirty_buf(dmu_tx_t *tx, dmu_buf_impl_t *db)
 
        DB_DNODE_ENTER(db);
        dn = DB_DNODE(db);
+       ASSERT(dn != NULL);
        ASSERT(tx->tx_txg != 0);
        ASSERT(tx->tx_objset == NULL || dn->dn_objset == tx->tx_objset);
        ASSERT3U(dn->dn_object, ==, db->db.db_object);
@@ -825,7 +836,7 @@ dmu_tx_dirty_buf(dmu_tx_t *tx, dmu_buf_impl_t *db)
 
        for (txh = list_head(&tx->tx_holds); txh;
            txh = list_next(&tx->tx_holds, txh)) {
-               ASSERT(dn == NULL || dn->dn_assigned_txg == tx->tx_txg);
+               ASSERT3U(dn->dn_assigned_txg, ==, tx->tx_txg);
                if (txh->txh_dnode == dn && txh->txh_type != THT_NEWOBJECT)
                        match_object = TRUE;
                if (txh->txh_dnode == NULL || txh->txh_dnode == dn) {
@@ -913,7 +924,7 @@ dmu_tx_try_assign(dmu_tx_t *tx, uint64_t txg_how)
        uint64_t memory, asize, fsize, usize;
        uint64_t towrite, tofree, tooverwrite, tounref, tohold, fudge;
 
-       ASSERT3U(tx->tx_txg, ==, 0);
+       ASSERT0(tx->tx_txg);
 
        if (tx->tx_err) {
                DMU_TX_STAT_BUMP(dmu_tx_error);
@@ -1003,7 +1014,7 @@ dmu_tx_try_assign(dmu_tx_t *tx, uint64_t txg_how)
        /* calculate memory footprint estimate */
        memory = towrite + tooverwrite + tohold;
 
-#ifdef ZFS_DEBUG
+#ifdef DEBUG_DMU_TX
        /*
         * Add in 'tohold' to account for our dirty holds on this memory
         * XXX - the "fudge" factor is to account for skipped blocks that
@@ -1079,12 +1090,15 @@ dmu_tx_unassign(dmu_tx_t *tx)
 int
 dmu_tx_assign(dmu_tx_t *tx, uint64_t txg_how)
 {
+       hrtime_t before, after;
        int err;
 
        ASSERT(tx->tx_txg == 0);
        ASSERT(txg_how != 0);
        ASSERT(!dsl_pool_sync_context(tx->tx_pool));
 
+       before = gethrtime();
+
        while ((err = dmu_tx_try_assign(tx, txg_how)) != 0) {
                dmu_tx_unassign(tx);
 
@@ -1096,6 +1110,11 @@ dmu_tx_assign(dmu_tx_t *tx, uint64_t txg_how)
 
        txg_rele_to_quiesce(&tx->tx_txgh);
 
+       after = gethrtime();
+
+       dsl_pool_tx_assign_add_usecs(tx->tx_pool,
+           (after - before) / NSEC_PER_USEC);
+
        return (0);
 }
 
@@ -1129,7 +1148,7 @@ dmu_tx_wait(dmu_tx_t *tx)
 void
 dmu_tx_willuse_space(dmu_tx_t *tx, int64_t delta)
 {
-#ifdef ZFS_DEBUG
+#ifdef DEBUG_DMU_TX
        if (tx->tx_dir == NULL || delta == 0)
                return;
 
@@ -1179,7 +1198,7 @@ dmu_tx_commit(dmu_tx_t *tx)
 
        list_destroy(&tx->tx_callbacks);
        list_destroy(&tx->tx_holds);
-#ifdef ZFS_DEBUG
+#ifdef DEBUG_DMU_TX
        dprintf("towrite=%llu written=%llu tofree=%llu freed=%llu\n",
            tx->tx_space_towrite, refcount_count(&tx->tx_space_written),
            tx->tx_space_tofree, refcount_count(&tx->tx_space_freed));
@@ -1215,7 +1234,7 @@ dmu_tx_abort(dmu_tx_t *tx)
 
        list_destroy(&tx->tx_callbacks);
        list_destroy(&tx->tx_holds);
-#ifdef ZFS_DEBUG
+#ifdef DEBUG_DMU_TX
        refcount_destroy_many(&tx->tx_space_written,
            refcount_count(&tx->tx_space_written));
        refcount_destroy_many(&tx->tx_space_freed,
@@ -1236,7 +1255,7 @@ dmu_tx_callback_register(dmu_tx_t *tx, dmu_tx_callback_func_t *func, void *data)
 {
        dmu_tx_callback_t *dcb;
 
-       dcb = kmem_alloc(sizeof (dmu_tx_callback_t), KM_SLEEP);
+       dcb = kmem_alloc(sizeof (dmu_tx_callback_t), KM_PUSHPAGE);
 
        dcb->dcb_func = func;
        dcb->dcb_data = data;
@@ -1299,10 +1318,11 @@ dmu_tx_hold_spill(dmu_tx_t *tx, uint64_t object)
 {
        dnode_t *dn;
        dmu_tx_hold_t *txh;
-       blkptr_t *bp;
 
        txh = dmu_tx_hold_object_impl(tx, tx->tx_objset, object,
            THT_SPILL, 0, 0);
+       if (txh == NULL)
+               return;
 
        dn = txh->txh_dnode;
 
@@ -1310,17 +1330,18 @@ dmu_tx_hold_spill(dmu_tx_t *tx, uint64_t object)
                return;
 
        /* If blkptr doesn't exist then add space to towrite */
-       bp = &dn->dn_phys->dn_spill;
-       if (BP_IS_HOLE(bp)) {
+       if (!(dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR)) {
                txh->txh_space_towrite += SPA_MAXBLOCKSIZE;
-               txh->txh_space_tounref = 0;
        } else {
+               blkptr_t *bp;
+
+               bp = &dn->dn_phys->dn_spill;
                if (dsl_dataset_block_freeable(dn->dn_objset->os_dsl_dataset,
                    bp, bp->blk_birth))
                        txh->txh_space_tooverwrite += SPA_MAXBLOCKSIZE;
                else
                        txh->txh_space_towrite += SPA_MAXBLOCKSIZE;
-               if (bp->blk_birth)
+               if (!BP_IS_HOLE(bp))
                        txh->txh_space_tounref += SPA_MAXBLOCKSIZE;
        }
 }