Add -p switch to "zpool get"
[zfs.git] / module / zfs / sa.c
index a91b379..581cf4b 100644 (file)
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 #include <sys/zfs_context.h>
@@ -134,7 +136,7 @@ static int sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr,
     sa_data_op_t action, sa_data_locator_t *locator, void *datastart,
     uint16_t buflen, dmu_tx_t *tx);
 
-arc_byteswap_func_t *sa_bswap_table[] = {
+arc_byteswap_func_t sa_bswap_table[] = {
        byteswap_uint64_array,
        byteswap_uint32_array,
        byteswap_uint16_array,
@@ -201,6 +203,7 @@ sa_attr_type_t sa_dummy_zpl_layout[] = { 0 };
 
 static int sa_legacy_attr_count = 16;
 static kmem_cache_t *sa_cache = NULL;
+static kmem_cache_t *spill_cache = NULL;
 
 /*ARGSUSED*/
 static int
@@ -232,6 +235,8 @@ sa_cache_init(void)
        sa_cache = kmem_cache_create("sa_cache",
            sizeof (sa_handle_t), 0, sa_cache_constructor,
            sa_cache_destructor, NULL, NULL, NULL, 0);
+       spill_cache = kmem_cache_create("spill_cache",
+           SPA_MAXBLOCKSIZE, 0, NULL, NULL, NULL, NULL, NULL, 0);
 }
 
 void
@@ -239,6 +244,21 @@ sa_cache_fini(void)
 {
        if (sa_cache)
                kmem_cache_destroy(sa_cache);
+
+       if (spill_cache)
+               kmem_cache_destroy(spill_cache);
+}
+
+void *
+sa_spill_alloc(int flags)
+{
+       return kmem_cache_alloc(spill_cache, flags);
+}
+
+void
+sa_spill_free(void *obj)
+{
+       kmem_cache_free(spill_cache, obj);
 }
 
 static int
@@ -300,8 +320,8 @@ sa_layout_info_hash(sa_attr_type_t *attrs, int attr_count)
        return (crc);
 }
 
-static boolean_t
-sa_has_blkptr(sa_handle_t *hdl)
+static int
+sa_get_spill(sa_handle_t *hdl)
 {
        int rc;
        if (hdl->sa_spill == NULL) {
@@ -312,7 +332,7 @@ sa_has_blkptr(sa_handle_t *hdl)
                rc = 0;
        }
 
-       return (rc == 0 ? B_TRUE : B_FALSE);
+       return (rc);
 }
 
 /*
@@ -349,7 +369,8 @@ sa_attr_op(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count,
                                buftypes |= SA_BONUS;
                        }
                }
-               if (bulk[i].sa_addr == NULL && sa_has_blkptr(hdl)) {
+               if (bulk[i].sa_addr == NULL &&
+                   ((error = sa_get_spill(hdl)) == 0)) {
                        if (TOC_ATTR_PRESENT(
                            hdl->sa_spill_tab->sa_idx_tab[bulk[i].sa_attr])) {
                                SA_ATTR_INFO(sa, hdl->sa_spill_tab,
@@ -362,6 +383,10 @@ sa_attr_op(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count,
                                }
                        }
                }
+               if (error && error != ENOENT) {
+                       return ((error == ECKSUM) ? EIO : error);
+               }
+
                switch (data_op) {
                case SA_LOOKUP:
                        if (bulk[i].sa_addr == NULL)
@@ -393,6 +418,8 @@ sa_attr_op(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count,
                        if (error)
                                return (error);
                        break;
+               default:
+                       break;
                }
        }
        return (error);
@@ -408,10 +435,10 @@ sa_add_layout_entry(objset_t *os, sa_attr_type_t *attrs, int attr_count,
        avl_index_t loc;
 
        ASSERT(MUTEX_HELD(&sa->sa_lock));
-       tb = kmem_zalloc(sizeof (sa_lot_t), KM_SLEEP);
+       tb = kmem_zalloc(sizeof (sa_lot_t), KM_PUSHPAGE);
        tb->lot_attr_count = attr_count;
        tb->lot_attrs = kmem_alloc(sizeof (sa_attr_type_t) * attr_count,
-           KM_SLEEP);
+           KM_PUSHPAGE);
        bcopy(attrs, tb->lot_attrs, sizeof (sa_attr_type_t) * attr_count);
        tb->lot_num = lot_num;
        tb->lot_hash = hash;
@@ -421,12 +448,9 @@ sa_add_layout_entry(objset_t *os, sa_attr_type_t *attrs, int attr_count,
                char attr_name[8];
 
                if (sa->sa_layout_attr_obj == 0) {
-                       int error;
-                       sa->sa_layout_attr_obj = zap_create(os,
-                           DMU_OT_SA_ATTR_LAYOUTS, DMU_OT_NONE, 0, tx);
-                       error = zap_add(os, sa->sa_master_obj, SA_LAYOUTS, 8, 1,
-                           &sa->sa_layout_attr_obj, tx);
-                       ASSERT3U(error, ==, 0);
+                       sa->sa_layout_attr_obj = zap_create_link(os,
+                           DMU_OT_SA_ATTR_LAYOUTS,
+                           sa->sa_master_obj, SA_LAYOUTS, tx);
                }
 
                (void) snprintf(attr_name, sizeof (attr_name),
@@ -548,6 +572,7 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count,
 {
        int var_size = 0;
        int i;
+       int j = -1;
        int full_space;
        int hdrsize;
        boolean_t done = B_FALSE;
@@ -569,10 +594,12 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count,
            sizeof (sa_hdr_phys_t);
 
        full_space = (buftype == SA_BONUS) ? DN_MAX_BONUSLEN : db->db_size;
+       ASSERT(IS_P2ALIGNED(full_space, 8));
 
        for (i = 0; i != attr_count; i++) {
                boolean_t is_var_sz;
 
+               *total = P2ROUNDUP(*total, 8);
                *total += attr_desc[i].sa_length;
                if (done)
                        goto next;
@@ -585,7 +612,14 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count,
                if (is_var_sz && var_size > 1) {
                        if (P2ROUNDUP(hdrsize + sizeof (uint16_t), 8) +
                            *total < full_space) {
+                               /*
+                                * Account for header space used by array of
+                                * optional sizes of variable-length attributes.
+                                * Record the index in case this increase needs
+                                * to be reversed due to spill-over.
+                                */
                                hdrsize += sizeof (uint16_t);
+                               j = i;
                        } else {
                                done = B_TRUE;
                                *index = i;
@@ -602,18 +636,26 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count,
                 * and spill buffer.
                 */
                if (buftype == SA_BONUS && *index == -1 &&
-                   P2ROUNDUP(*total + hdrsize, 8) >
+                   (*total + P2ROUNDUP(hdrsize, 8)) >
                    (full_space - sizeof (blkptr_t))) {
                        *index = i;
                        done = B_TRUE;
                }
 
 next:
-               if (P2ROUNDUP(*total + hdrsize, 8) > full_space &&
+               if ((*total + P2ROUNDUP(hdrsize, 8)) > full_space &&
                    buftype == SA_BONUS)
                        *will_spill = B_TRUE;
        }
 
+       /*
+        * j holds the index of the last variable-sized attribute for
+        * which hdrsize was increased.  Reverse the increase if that
+        * attribute will be relocated to the spill block.
+        */
+       if (*will_spill && j == *index)
+               hdrsize -= sizeof (uint16_t);
+
        hdrsize = P2ROUNDUP(hdrsize, 8);
        return (hdrsize);
 }
@@ -637,7 +679,7 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
        int buf_space;
        sa_attr_type_t *attrs, *attrs_start;
        int i, lot_count;
-       int hdrsize, spillhdrsize;
+       int hdrsize, spillhdrsize = 0;
        int used;
        dmu_object_type_t bonustype;
        sa_lot_t *lot;
@@ -667,10 +709,8 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
                boolean_t dummy;
 
                if (hdl->sa_spill == NULL) {
-                       int error;
-                       error = dmu_spill_hold_by_bonus(hdl->sa_bonus, NULL,
-                           &hdl->sa_spill);
-                       ASSERT3U(error, ==, 0);
+                       VERIFY(dmu_spill_hold_by_bonus(hdl->sa_bonus, NULL,
+                           &hdl->sa_spill) == 0);
                }
                dmu_buf_will_dirty(hdl->sa_spill, tx);
 
@@ -700,19 +740,22 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
                buf_space = hdl->sa_bonus->db_size - hdrsize;
 
        attrs_start = attrs = kmem_alloc(sizeof (sa_attr_type_t) * attr_count,
-           KM_SLEEP);
+           KM_PUSHPAGE);
        lot_count = 0;
 
        for (i = 0, len_idx = 0, hash = -1ULL; i != attr_count; i++) {
                uint16_t length;
 
+               ASSERT(IS_P2ALIGNED(data_start, 8));
+               ASSERT(IS_P2ALIGNED(buf_space, 8));
                attrs[i] = attr_desc[i].sa_attr;
                length = SA_REGISTERED_LEN(sa, attrs[i]);
                if (length == 0)
                        length = attr_desc[i].sa_length;
 
                if (buf_space < length) {  /* switch to spill buffer */
-                       ASSERT(bonustype != DMU_OT_ZNODE);
+                       VERIFY(spilling);
+                       VERIFY(bonustype == DMU_OT_SA);
                        if (buftype == SA_BONUS && !sa->sa_force_spill) {
                                sa_find_layout(hdl->sa_os, hash, attrs_start,
                                    lot_count, tx, &lot);
@@ -746,6 +789,14 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
        }
 
        sa_find_layout(hdl->sa_os, hash, attrs_start, lot_count, tx, &lot);
+
+       /*
+        * Verify that old znodes always have layout number 0.
+        * Must be DMU_OT_SA for arbitrary layouts
+        */
+       VERIFY((bonustype == DMU_OT_ZNODE && lot->lot_num == 0) ||
+           (bonustype == DMU_OT_SA && lot->lot_num > 1));
+
        if (bonustype == DMU_OT_SA) {
                SA_SET_HDR(sahdr, lot->lot_num,
                    buftype == SA_BONUS ? hdrsize : spillhdrsize);
@@ -763,11 +814,6 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
                if (!spilling) {
                        /*
                         * remove spill block that is no longer needed.
-                        * set sa_spill_remove to prevent sa_attr_op
-                        * from trying to retrieve spill block before its
-                        * been removed.  The flag will be cleared if/when
-                        * the handle is destroyed recreated or
-                        * sa_build_layouts() needs to spill again.
                         */
                        dmu_buf_rele(hdl->sa_spill, NULL);
                        hdl->sa_spill = NULL;
@@ -783,10 +829,31 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
 }
 
 static void
+sa_free_attr_table(sa_os_t *sa)
+{
+       int i;
+
+       if (sa->sa_attr_table == NULL)
+               return;
+
+       for (i = 0; i != sa->sa_num_attrs; i++) {
+               if (sa->sa_attr_table[i].sa_name)
+                       kmem_free(sa->sa_attr_table[i].sa_name,
+                           strlen(sa->sa_attr_table[i].sa_name) + 1);
+       }
+
+       kmem_free(sa->sa_attr_table,
+           sizeof (sa_attr_table_t) * sa->sa_num_attrs);
+
+       sa->sa_attr_table = NULL;
+}
+
+static int
 sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
 {
        sa_os_t *sa = os->os_sa;
        uint64_t sa_attr_count = 0;
+       uint64_t sa_reg_count = 0;
        int error = 0;
        uint64_t attr_value;
        sa_attr_table_t *tb;
@@ -797,11 +864,23 @@ sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
        dmu_objset_type_t ostype = dmu_objset_type(os);
 
        sa->sa_user_table =
-           kmem_zalloc(count * sizeof (sa_attr_type_t), KM_SLEEP);
+           kmem_zalloc(count * sizeof (sa_attr_type_t), KM_PUSHPAGE);
        sa->sa_user_table_sz = count * sizeof (sa_attr_type_t);
 
-       if (sa->sa_reg_attr_obj != 0)
-               VERIFY(zap_count(os, sa->sa_reg_attr_obj, &sa_attr_count) == 0);
+       if (sa->sa_reg_attr_obj != 0) {
+               error = zap_count(os, sa->sa_reg_attr_obj,
+                   &sa_attr_count);
+
+               /*
+                * Make sure we retrieved a count and that it isn't zero
+                */
+               if (error || (error == 0 && sa_attr_count == 0)) {
+                       if (error == 0)
+                               error = EINVAL;
+                       goto bail;
+               }
+               sa_reg_count = sa_attr_count;
+       }
 
        if (ostype == DMU_OST_ZFS && sa_attr_count == 0)
                sa_attr_count += sa_legacy_attr_count;
@@ -830,7 +909,6 @@ sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
                else
                        error = ENOENT;
                switch (error) {
-               default:
                case ENOENT:
                        sa->sa_user_table[i] = (sa_attr_type_t)sa_attr_count;
                        sa_attr_count++;
@@ -838,12 +916,14 @@ sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
                case 0:
                        sa->sa_user_table[i] = ATTR_NUM(attr_value);
                        break;
+               default:
+                       goto bail;
                }
        }
 
-       os->os_sa->sa_num_attrs = sa_attr_count;
-       tb = os->os_sa->sa_attr_table =
-           kmem_zalloc(sizeof (sa_attr_table_t) * sa_attr_count, KM_SLEEP);
+       sa->sa_num_attrs = sa_attr_count;
+       tb = sa->sa_attr_table =
+           kmem_zalloc(sizeof (sa_attr_table_t) * sa_attr_count, KM_PUSHPAGE);
 
        /*
         * Attribute table is constructed from requested attribute list,
@@ -853,7 +933,7 @@ sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
 
        if (sa->sa_reg_attr_obj) {
                for (zap_cursor_init(&zc, os, sa->sa_reg_attr_obj);
-                   zap_cursor_retrieve(&zc, &za) == 0;
+                   (error = zap_cursor_retrieve(&zc, &za)) == 0;
                    zap_cursor_advance(&zc)) {
                        uint64_t value;
                        value  = za.za_first_integer;
@@ -868,11 +948,20 @@ sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
                                continue;
                        }
                        tb[ATTR_NUM(value)].sa_name =
-                           kmem_zalloc(strlen(za.za_name) +1, KM_SLEEP);
+                           kmem_zalloc(strlen(za.za_name) +1, KM_PUSHPAGE);
                        (void) strlcpy(tb[ATTR_NUM(value)].sa_name, za.za_name,
                            strlen(za.za_name) +1);
                }
                zap_cursor_fini(&zc);
+               /*
+                * Make sure we processed the correct number of registered
+                * attributes
+                */
+               if (registered_count != sa_reg_count) {
+                       ASSERT(error != 0);
+                       goto bail;
+               }
+
        }
 
        if (ostype == DMU_OST_ZFS) {
@@ -885,7 +974,7 @@ sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
                        tb[i].sa_registered = B_FALSE;
                        tb[i].sa_name =
                            kmem_zalloc(strlen(sa_legacy_attrs[i].sa_name) +1,
-                           KM_SLEEP);
+                           KM_PUSHPAGE);
                        (void) strlcpy(tb[i].sa_name,
                            sa_legacy_attrs[i].sa_name,
                            strlen(sa_legacy_attrs[i].sa_name) + 1);
@@ -903,23 +992,32 @@ sa_attr_table_setup(objset_t *os, sa_attr_reg_t *reg_attrs, int count)
                tb[attr_id].sa_byteswap = reg_attrs[i].sa_byteswap;
                tb[attr_id].sa_attr = attr_id;
                tb[attr_id].sa_name =
-                   kmem_zalloc(strlen(reg_attrs[i].sa_name) + 1, KM_SLEEP);
+                   kmem_zalloc(strlen(reg_attrs[i].sa_name) + 1, KM_PUSHPAGE);
                (void) strlcpy(tb[attr_id].sa_name, reg_attrs[i].sa_name,
                    strlen(reg_attrs[i].sa_name) + 1);
        }
 
-       os->os_sa->sa_need_attr_registration =
+       sa->sa_need_attr_registration =
            (sa_attr_count != registered_count);
+
+       return (0);
+bail:
+       kmem_free(sa->sa_user_table, count * sizeof (sa_attr_type_t));
+       sa->sa_user_table = NULL;
+       sa_free_attr_table(sa);
+       return ((error != 0) ? error : EINVAL);
 }
 
-sa_attr_type_t *
-sa_setup(objset_t *os, uint64_t sa_obj, sa_attr_reg_t *reg_attrs, int count)
+int
+sa_setup(objset_t *os, uint64_t sa_obj, sa_attr_reg_t *reg_attrs, int count,
+    sa_attr_type_t **user_table)
 {
        zap_cursor_t zc;
        zap_attribute_t za;
        sa_os_t *sa;
        dmu_objset_type_t ostype = dmu_objset_type(os);
        sa_attr_type_t *tb;
+       int error;
 
        mutex_enter(&os->os_lock);
        if (os->os_sa) {
@@ -927,13 +1025,15 @@ sa_setup(objset_t *os, uint64_t sa_obj, sa_attr_reg_t *reg_attrs, int count)
                mutex_exit(&os->os_lock);
                tb = os->os_sa->sa_user_table;
                mutex_exit(&os->os_sa->sa_lock);
-               return (tb);
+               *user_table = tb;
+               return (0);
        }
 
-       sa = kmem_zalloc(sizeof (sa_os_t), KM_SLEEP);
+       sa = kmem_zalloc(sizeof (sa_os_t), KM_PUSHPAGE);
        mutex_init(&sa->sa_lock, NULL, MUTEX_DEFAULT, NULL);
        sa->sa_master_obj = sa_obj;
 
+       os->os_sa = sa;
        mutex_enter(&sa->sa_lock);
        mutex_exit(&os->os_lock);
        avl_create(&sa->sa_layout_num_tree, layout_num_compare,
@@ -942,35 +1042,50 @@ sa_setup(objset_t *os, uint64_t sa_obj, sa_attr_reg_t *reg_attrs, int count)
            sizeof (sa_lot_t), offsetof(sa_lot_t, lot_hash_node));
 
        if (sa_obj) {
-               int error;
                error = zap_lookup(os, sa_obj, SA_LAYOUTS,
                    8, 1, &sa->sa_layout_attr_obj);
-               if (error != 0 && error != ENOENT) {
-                       return (NULL);
-               }
+               if (error != 0 && error != ENOENT)
+                       goto fail;
                error = zap_lookup(os, sa_obj, SA_REGISTRY,
                    8, 1, &sa->sa_reg_attr_obj);
-               if (error != 0 && error != ENOENT) {
-                       mutex_exit(&sa->sa_lock);
-                       return (NULL);
-               }
+               if (error != 0 && error != ENOENT)
+                       goto fail;
        }
 
-       os->os_sa = sa;
-       sa_attr_table_setup(os, reg_attrs, count);
+       if ((error = sa_attr_table_setup(os, reg_attrs, count)) != 0)
+               goto fail;
 
        if (sa->sa_layout_attr_obj != 0) {
+               uint64_t layout_count;
+
+               error = zap_count(os, sa->sa_layout_attr_obj,
+                   &layout_count);
+
+               /*
+                * Layout number count should be > 0
+                */
+               if (error || (error == 0 && layout_count == 0)) {
+                       if (error == 0)
+                               error = EINVAL;
+                       goto fail;
+               }
+
                for (zap_cursor_init(&zc, os, sa->sa_layout_attr_obj);
-                   zap_cursor_retrieve(&zc, &za) == 0;
+                   (error = zap_cursor_retrieve(&zc, &za)) == 0;
                    zap_cursor_advance(&zc)) {
                        sa_attr_type_t *lot_attrs;
                        uint64_t lot_num;
 
                        lot_attrs = kmem_zalloc(sizeof (sa_attr_type_t) *
-                           za.za_num_integers, KM_SLEEP);
+                           za.za_num_integers, KM_PUSHPAGE);
 
-                       VERIFY(zap_lookup(os, sa->sa_layout_attr_obj,
-                           za.za_name, 2, za.za_num_integers, lot_attrs) == 0);
+                       if ((error = (zap_lookup(os, sa->sa_layout_attr_obj,
+                           za.za_name, 2, za.za_num_integers,
+                           lot_attrs))) != 0) {
+                               kmem_free(lot_attrs, sizeof (sa_attr_type_t) *
+                                   za.za_num_integers);
+                               break;
+                       }
                        VERIFY(ddi_strtoull(za.za_name, NULL, 10,
                            (unsigned long long *)&lot_num) == 0);
 
@@ -982,6 +1097,15 @@ sa_setup(objset_t *os, uint64_t sa_obj, sa_attr_reg_t *reg_attrs, int count)
                            za.za_num_integers);
                }
                zap_cursor_fini(&zc);
+
+               /*
+                * Make sure layout count matches number of entries added
+                * to AVL tree
+                */
+               if (avl_numnodes(&sa->sa_layout_num_tree) != layout_count) {
+                       ASSERT(error != 0);
+                       goto fail;
+               }
        }
 
        /* Add special layout number for old ZNODES */
@@ -994,8 +1118,17 @@ sa_setup(objset_t *os, uint64_t sa_obj, sa_attr_reg_t *reg_attrs, int count)
                (void) sa_add_layout_entry(os, sa_dummy_zpl_layout, 0, 1,
                    0, B_FALSE, NULL);
        }
+       *user_table = os->os_sa->sa_user_table;
        mutex_exit(&sa->sa_lock);
-       return (os->os_sa->sa_user_table);
+       return (0);
+fail:
+       os->os_sa = NULL;
+       sa_free_attr_table(sa);
+       if (sa->sa_user_table)
+               kmem_free(sa->sa_user_table, sa->sa_user_table_sz);
+       mutex_exit(&sa->sa_lock);
+       kmem_free(sa, sizeof (sa_os_t));
+       return ((error == ECKSUM) ? EIO : error);
 }
 
 void
@@ -1004,32 +1137,24 @@ sa_tear_down(objset_t *os)
        sa_os_t *sa = os->os_sa;
        sa_lot_t *layout;
        void *cookie;
-       int i;
 
        kmem_free(sa->sa_user_table, sa->sa_user_table_sz);
 
        /* Free up attr table */
 
-       for (i = 0; i != sa->sa_num_attrs; i++) {
-               if (sa->sa_attr_table[i].sa_name)
-                       kmem_free(sa->sa_attr_table[i].sa_name,
-                           strlen(sa->sa_attr_table[i].sa_name) + 1);
-       }
-
-       kmem_free(sa->sa_attr_table,
-           sizeof (sa_attr_table_t) * sa->sa_num_attrs);
+       sa_free_attr_table(sa);
 
        cookie = NULL;
-       while (layout = avl_destroy_nodes(&sa->sa_layout_hash_tree, &cookie)) {
+       while ((layout = avl_destroy_nodes(&sa->sa_layout_hash_tree, &cookie))){
                sa_idx_tab_t *tab;
-               while (tab = list_head(&layout->lot_idx_tab)) {
+               while ((tab = list_head(&layout->lot_idx_tab))) {
                        ASSERT(refcount_count(&tab->sa_refcount));
                        sa_idx_tab_rele(os, tab);
                }
        }
 
        cookie = NULL;
-       while (layout = avl_destroy_nodes(&sa->sa_layout_num_tree, &cookie)) {
+       while ((layout = avl_destroy_nodes(&sa->sa_layout_num_tree, &cookie))){
                kmem_free(layout->lot_attrs,
                    sizeof (sa_attr_type_t) * layout->lot_attr_count);
                kmem_free(layout, sizeof (sa_lot_t));
@@ -1121,9 +1246,9 @@ sa_byteswap(sa_handle_t *hdl, sa_buf_type_t buftype)
 {
        sa_hdr_phys_t *sa_hdr_phys = SA_GET_HDR(hdl, buftype);
        dmu_buf_impl_t *db;
-       sa_os_t *sa = hdl->sa_os->os_sa;
        int num_lengths = 1;
        int i;
+       ASSERTV(sa_os_t *sa = hdl->sa_os->os_sa);
 
        ASSERT(MUTEX_HELD(&sa->sa_lock));
        if (sa_hdr_phys->sa_magic == SA_MAGIC)
@@ -1224,13 +1349,26 @@ sa_idx_tab_rele(objset_t *os, void *arg)
 static void
 sa_idx_tab_hold(objset_t *os, sa_idx_tab_t *idx_tab)
 {
-       sa_os_t *sa = os->os_sa;
+       ASSERTV(sa_os_t *sa = os->os_sa);
 
        ASSERT(MUTEX_HELD(&sa->sa_lock));
        (void) refcount_add(&idx_tab->sa_refcount, NULL);
 }
 
 void
+sa_spill_rele(sa_handle_t *hdl)
+{
+       mutex_enter(&hdl->sa_lock);
+       if (hdl->sa_spill) {
+               sa_idx_tab_rele(hdl->sa_os, hdl->sa_spill_tab);
+               dmu_buf_rele(hdl->sa_spill, NULL);
+               hdl->sa_spill = NULL;
+               hdl->sa_spill_tab = NULL;
+       }
+       mutex_exit(&hdl->sa_lock);
+}
+
+void
 sa_handle_destroy(sa_handle_t *hdl)
 {
        mutex_enter(&hdl->sa_lock);
@@ -1260,10 +1398,10 @@ sa_handle_get_from_db(objset_t *os, dmu_buf_t *db, void *userp,
     sa_handle_type_t hdl_type, sa_handle_t **handlepp)
 {
        int error = 0;
-       dmu_object_info_t doi;
        sa_handle_t *handle;
-
 #ifdef ZFS_DEBUG
+       dmu_object_info_t doi;
+
        dmu_object_info_from_db(db, &doi);
        ASSERT(doi.doi_bonus_type == DMU_OT_SA ||
            doi.doi_bonus_type == DMU_OT_ZNODE);
@@ -1302,7 +1440,7 @@ sa_handle_get(objset_t *objset, uint64_t objid, void *userp,
        dmu_buf_t *db;
        int error;
 
-       if (error = dmu_bonus_hold(objset, objid, NULL, &db))
+       if ((error = dmu_bonus_hold(objset, objid, NULL, &db)))
                return (error);
 
        return (sa_handle_get_from_db(objset, db, userp, hdl_type,
@@ -1361,23 +1499,15 @@ sa_lookup_uio(sa_handle_t *hdl, sa_attr_type_t attr, uio_t *uio)
        ASSERT(hdl);
 
        mutex_enter(&hdl->sa_lock);
-       if (sa_attr_op(hdl, &bulk, 1, SA_LOOKUP, NULL) == 0) {
+       if ((error = sa_attr_op(hdl, &bulk, 1, SA_LOOKUP, NULL)) == 0) {
                error = uiomove((void *)bulk.sa_addr, MIN(bulk.sa_size,
                    uio->uio_resid), UIO_READ, uio);
-       } else {
-               error = ENOENT;
        }
        mutex_exit(&hdl->sa_lock);
        return (error);
-
 }
 #endif
 
-/*
- * Find an already existing TOC from given os and data
- * This is a special interface to be used by the ZPL for
- * finding the uid/gid/gen attributes.
- */
 void *
 sa_find_idx_tab(objset_t *os, dmu_object_type_t bonustype, void *data)
 {
@@ -1402,8 +1532,8 @@ sa_find_idx_tab(objset_t *os, dmu_object_type_t bonustype, void *data)
 
        /* Verify header size is consistent with layout information */
        ASSERT(tb);
-       ASSERT(IS_SA_BONUSTYPE(bonustype) &&
-           SA_HDR_SIZE_MATCH_LAYOUT(hdr, tb) || !IS_SA_BONUSTYPE(bonustype) ||
+       ASSERT((IS_SA_BONUSTYPE(bonustype) &&
+           SA_HDR_SIZE_MATCH_LAYOUT(hdr, tb)) || !IS_SA_BONUSTYPE(bonustype) ||
            (IS_SA_BONUSTYPE(bonustype) && hdr->sa_layout_info == 0));
 
        /*
@@ -1432,14 +1562,14 @@ sa_find_idx_tab(objset_t *os, dmu_object_type_t bonustype, void *data)
        }
 
        /* No such luck, create a new entry */
-       idx_tab = kmem_zalloc(sizeof (sa_idx_tab_t), KM_SLEEP);
+       idx_tab = kmem_zalloc(sizeof (sa_idx_tab_t), KM_PUSHPAGE);
        idx_tab->sa_idx_tab =
-           kmem_zalloc(sizeof (uint32_t) * sa->sa_num_attrs, KM_SLEEP);
+           kmem_zalloc(sizeof (uint32_t) * sa->sa_num_attrs, KM_PUSHPAGE);
        idx_tab->sa_layout = tb;
        refcount_create(&idx_tab->sa_refcount);
        if (tb->lot_var_sizes)
                idx_tab->sa_variable_lengths = kmem_alloc(sizeof (uint16_t) *
-                   tb->lot_var_sizes, KM_SLEEP);
+                   tb->lot_var_sizes, KM_PUSHPAGE);
 
        sa_attr_iter(os, hdr, bonustype, sa_build_idx_tab,
            tb, idx_tab);
@@ -1469,18 +1599,15 @@ sa_attr_register_sync(sa_handle_t *hdl, dmu_tx_t *tx)
 
        mutex_enter(&sa->sa_lock);
 
-       if (!sa->sa_need_attr_registration || sa->sa_master_obj == NULL) {
+       if (!sa->sa_need_attr_registration || sa->sa_master_obj == 0) {
                mutex_exit(&sa->sa_lock);
                return;
        }
 
-       if (sa->sa_reg_attr_obj == NULL) {
-               int error;
-               sa->sa_reg_attr_obj = zap_create(hdl->sa_os,
-                   DMU_OT_SA_ATTR_REGISTRATION, DMU_OT_NONE, 0, tx);
-               error = zap_add(hdl->sa_os, sa->sa_master_obj,
-                   SA_REGISTRY, 8, 1, &sa->sa_reg_attr_obj, tx);
-               ASSERT(error == 0);
+       if (sa->sa_reg_attr_obj == 0) {
+               sa->sa_reg_attr_obj = zap_create_link(hdl->sa_os,
+                   DMU_OT_SA_ATTR_REGISTRATION,
+                   sa->sa_master_obj, SA_REGISTRY, tx);
        }
        for (i = 0; i != sa->sa_num_attrs; i++) {
                if (sa->sa_attr_table[i].sa_registered)
@@ -1538,10 +1665,12 @@ sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr,
     uint16_t buflen, dmu_tx_t *tx)
 {
        sa_os_t *sa = hdl->sa_os->os_sa;
+       dmu_buf_impl_t *db = (dmu_buf_impl_t *)hdl->sa_bonus;
+       dnode_t *dn;
        sa_bulk_attr_t *attr_desc;
        void *old_data[2];
        int bonus_attr_count = 0;
-       int bonus_data_size, spill_data_size;
+       int bonus_data_size = 0;
        int spill_attr_count = 0;
        int error;
        uint16_t length;
@@ -1555,7 +1684,9 @@ sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr,
 
        /* First make of copy of the old data */
 
-       if (((dmu_buf_impl_t *)hdl->sa_bonus)->db_dnode->dn_bonuslen) {
+       DB_DNODE_ENTER(db);
+       dn = DB_DNODE(db);
+       if (dn->dn_bonuslen != 0) {
                bonus_data_size = hdl->sa_bonus->db_size;
                old_data[0] = kmem_alloc(bonus_data_size, KM_SLEEP);
                bcopy(hdl->sa_bonus->db_data, old_data[0],
@@ -1564,16 +1695,21 @@ sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr,
        } else {
                old_data[0] = NULL;
        }
+       DB_DNODE_EXIT(db);
 
        /* Bring spill buffer online if it isn't currently */
 
-       if (sa_has_blkptr(hdl)) {
-               spill_data_size = hdl->sa_spill->db_size;
-               old_data[1] = kmem_alloc(spill_data_size, KM_SLEEP);
+       if ((error = sa_get_spill(hdl)) == 0) {
+               ASSERT3U(hdl->sa_spill->db_size, <=, SPA_MAXBLOCKSIZE);
+               old_data[1] = sa_spill_alloc(KM_SLEEP);
                bcopy(hdl->sa_spill->db_data, old_data[1],
                    hdl->sa_spill->db_size);
                spill_attr_count =
                    hdl->sa_spill_tab->sa_layout->lot_attr_count;
+       } else if (error && error != ENOENT) {
+               if (old_data[0])
+                       kmem_free(old_data[0], bonus_data_size);
+               return (error);
        } else {
                old_data[1] = NULL;
        }
@@ -1645,7 +1781,7 @@ sa_modify_attrs(sa_handle_t *hdl, sa_attr_type_t newattr,
        if (old_data[0])
                kmem_free(old_data[0], bonus_data_size);
        if (old_data[1])
-               kmem_free(old_data[1], spill_data_size);
+               sa_spill_free(old_data[1]);
        kmem_free(attr_desc, sizeof (sa_bulk_attr_t) * attr_count);
 
        return (error);
@@ -1658,12 +1794,14 @@ sa_bulk_update_impl(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count,
        int error;
        sa_os_t *sa = hdl->sa_os->os_sa;
        dmu_object_type_t bonustype;
-
-       bonustype = SA_BONUSTYPE_FROM_DB(SA_GET_DB(hdl, SA_BONUS));
+       dmu_buf_t *saved_spill;
 
        ASSERT(hdl);
        ASSERT(MUTEX_HELD(&hdl->sa_lock));
 
+       bonustype = SA_BONUSTYPE_FROM_DB(SA_GET_DB(hdl, SA_BONUS));
+       saved_spill = hdl->sa_spill;
+
        /* sync out registration table if necessary */
        if (sa->sa_need_attr_registration)
                sa_attr_register_sync(hdl, tx);
@@ -1672,6 +1810,24 @@ sa_bulk_update_impl(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count,
        if (error == 0 && !IS_SA_BONUSTYPE(bonustype) && sa->sa_update_cb)
                sa->sa_update_cb(hdl, tx);
 
+       /*
+        * If saved_spill is NULL and current sa_spill is not NULL that
+        * means we increased the refcount of the spill buffer through
+        * sa_get_spill() or dmu_spill_hold_by_dnode().  Therefore we
+        * must release the hold before calling dmu_tx_commit() to avoid
+        * making a copy of this buffer in dbuf_sync_leaf() due to the
+        * reference count now being greater than 1.
+        */
+       if (!saved_spill && hdl->sa_spill) {
+               if (hdl->sa_spill_tab) {
+                       sa_idx_tab_rele(hdl->sa_os, hdl->sa_spill_tab);
+                       hdl->sa_spill_tab = NULL;
+               }
+
+               dmu_buf_rele((dmu_buf_t *)hdl->sa_spill, NULL);
+               hdl->sa_spill = NULL;
+       }
+
        return (error);
 }
 
@@ -1722,6 +1878,7 @@ int
 sa_size(sa_handle_t *hdl, sa_attr_type_t attr, int *size)
 {
        sa_bulk_attr_t bulk;
+       int error;
 
        bulk.sa_data = NULL;
        bulk.sa_attr = attr;
@@ -1729,9 +1886,9 @@ sa_size(sa_handle_t *hdl, sa_attr_type_t attr, int *size)
 
        ASSERT(hdl);
        mutex_enter(&hdl->sa_lock);
-       if (sa_attr_op(hdl, &bulk, 1, SA_LOOKUP, NULL)) {
+       if ((error = sa_attr_op(hdl, &bulk, 1, SA_LOOKUP, NULL)) != 0) {
                mutex_exit(&hdl->sa_lock);
-               return (ENOENT);
+               return (error);
        }
        *size = bulk.sa_size;
 
@@ -1884,3 +2041,41 @@ sa_handle_unlock(sa_handle_t *hdl)
        ASSERT(hdl);
        mutex_exit(&hdl->sa_lock);
 }
+
+#ifdef _KERNEL
+EXPORT_SYMBOL(sa_handle_get);
+EXPORT_SYMBOL(sa_handle_get_from_db);
+EXPORT_SYMBOL(sa_handle_destroy);
+EXPORT_SYMBOL(sa_buf_hold);
+EXPORT_SYMBOL(sa_buf_rele);
+EXPORT_SYMBOL(sa_spill_rele);
+EXPORT_SYMBOL(sa_lookup);
+EXPORT_SYMBOL(sa_update);
+EXPORT_SYMBOL(sa_remove);
+EXPORT_SYMBOL(sa_bulk_lookup);
+EXPORT_SYMBOL(sa_bulk_lookup_locked);
+EXPORT_SYMBOL(sa_bulk_update);
+EXPORT_SYMBOL(sa_size);
+EXPORT_SYMBOL(sa_update_from_cb);
+EXPORT_SYMBOL(sa_object_info);
+EXPORT_SYMBOL(sa_object_size);
+EXPORT_SYMBOL(sa_update_user);
+EXPORT_SYMBOL(sa_get_userdata);
+EXPORT_SYMBOL(sa_set_userp);
+EXPORT_SYMBOL(sa_get_db);
+EXPORT_SYMBOL(sa_handle_object);
+EXPORT_SYMBOL(sa_register_update_callback);
+EXPORT_SYMBOL(sa_setup);
+EXPORT_SYMBOL(sa_replace_all_by_template);
+EXPORT_SYMBOL(sa_replace_all_by_template_locked);
+EXPORT_SYMBOL(sa_enabled);
+EXPORT_SYMBOL(sa_cache_init);
+EXPORT_SYMBOL(sa_cache_fini);
+EXPORT_SYMBOL(sa_spill_alloc);
+EXPORT_SYMBOL(sa_spill_free);
+EXPORT_SYMBOL(sa_set_sa_object);
+EXPORT_SYMBOL(sa_hdrsize);
+EXPORT_SYMBOL(sa_handle_lock);
+EXPORT_SYMBOL(sa_handle_unlock);
+EXPORT_SYMBOL(sa_lookup_uio);
+#endif /* _KERNEL */