Print this page
5056 ZFS deadlock on db_mtx and dn_holds
Reviewed by: Will Andrews <willa@spectralogic.com>
Reviewed by: Matt Ahrens <mahrens@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Approved by: Dan McDonald <danmcd@omniti.com>

@@ -21,10 +21,11 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Portions Copyright 2011 iXsystems, Inc
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
  */
 
 #include <sys/zfs_context.h>
 #include <sys/types.h>
 #include <sys/param.h>

@@ -208,10 +209,11 @@
 static int
 sa_cache_constructor(void *buf, void *unused, int kmflag)
 {
         sa_handle_t *hdl = buf;
 
+        hdl->sa_dbu.dbu_evict_func = NULL;
         hdl->sa_bonus_tab = NULL;
         hdl->sa_spill_tab = NULL;
         hdl->sa_os = NULL;
         hdl->sa_userp = NULL;
         hdl->sa_bonus = NULL;

@@ -223,10 +225,11 @@
 /*ARGSUSED*/
 static void
 sa_cache_destructor(void *buf, void *unused)
 {
         sa_handle_t *hdl = buf;
+        hdl->sa_dbu.dbu_evict_func = NULL;
         mutex_destroy(&hdl->sa_lock);
 }
 
 void
 sa_cache_init(void)

@@ -1299,14 +1302,14 @@
         mutex_exit(&sa->sa_lock);
         return (0);
 }
 
 /*ARGSUSED*/
-void
-sa_evict(dmu_buf_t *db, void *sap)
+static void
+sa_evict(void *dbu)
 {
-        panic("evicting sa dbuf %p\n", (void *)db);
+        panic("evicting sa dbuf\n");
 }
 
 static void
 sa_idx_tab_rele(objset_t *os, void *arg)
 {

@@ -1341,13 +1344,14 @@
 }
 
 void
 sa_handle_destroy(sa_handle_t *hdl)
 {
+        dmu_buf_t *db = hdl->sa_bonus;
+
         mutex_enter(&hdl->sa_lock);
-        (void) dmu_buf_update_user((dmu_buf_t *)hdl->sa_bonus, hdl,
-            NULL, NULL);
+        (void) dmu_buf_remove_user(db, &hdl->sa_dbu);
 
         if (hdl->sa_bonus_tab) {
                 sa_idx_tab_rele(hdl->sa_os, hdl->sa_bonus_tab);
                 hdl->sa_bonus_tab = NULL;
         }

@@ -1369,36 +1373,42 @@
 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;
+        sa_handle_t *handle = NULL;
 
 #ifdef ZFS_DEBUG
         dmu_object_info_from_db(db, &doi);
         ASSERT(doi.doi_bonus_type == DMU_OT_SA ||
             doi.doi_bonus_type == DMU_OT_ZNODE);
 #endif
         /* find handle, if it exists */
         /* if one doesn't exist then create a new one, and initialize it */
 
-        handle = (hdl_type == SA_HDL_SHARED) ? dmu_buf_get_user(db) : NULL;
+        if (hdl_type == SA_HDL_SHARED)
+                handle = dmu_buf_get_user(db);
+
         if (handle == NULL) {
-                sa_handle_t *newhandle;
+                sa_handle_t *winner = NULL;
+
                 handle = kmem_cache_alloc(sa_cache, KM_SLEEP);
                 handle->sa_userp = userp;
                 handle->sa_bonus = db;
                 handle->sa_os = os;
                 handle->sa_spill = NULL;
 
                 error = sa_build_index(handle, SA_BONUS);
-                newhandle = (hdl_type == SA_HDL_SHARED) ?
-                    dmu_buf_set_user_ie(db, handle, sa_evict) : NULL;
 
-                if (newhandle != NULL) {
+                if (hdl_type == SA_HDL_SHARED) {
+                        dmu_buf_init_user(&handle->sa_dbu, sa_evict, NULL);
+                        winner = dmu_buf_set_user_ie(db, &handle->sa_dbu);
+                }
+
+                if (winner != NULL) {
                         kmem_cache_free(sa_cache, handle);
-                        handle = newhandle;
+                        handle = winner;
                 }
         }
         *handlepp = handle;
 
         return (error);

@@ -1904,18 +1914,10 @@
 {
         dmu_object_size_from_db((dmu_buf_t *)hdl->sa_bonus,
             blksize, nblocks);
 }
 
-void
-sa_update_user(sa_handle_t *newhdl, sa_handle_t *oldhdl)
-{
-        (void) dmu_buf_update_user((dmu_buf_t *)newhdl->sa_bonus,
-            oldhdl, newhdl, sa_evict);
-        oldhdl->sa_bonus = NULL;
-}
-
 void
 sa_set_userp(sa_handle_t *hdl, void *ptr)
 {
         hdl->sa_userp = ptr;
 }