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>
*** 24,33 ****
--- 24,34 ----
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright 2013 DEY Storage Systems, Inc.
* Copyright 2014 HybridCluster. All rights reserved.
+ * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
*/
/* Portions Copyright 2010 Robert Milkowski */
#ifndef _SYS_DMU_H
*** 39,53 ****
*
* The DMU also interacts with the SPA. That interface is described in
* dmu_spa.h.
*/
#include <sys/inttypes.h>
- #include <sys/types.h>
- #include <sys/param.h>
#include <sys/cred.h>
- #include <sys/time.h>
#include <sys/fs/zfs.h>
#ifdef __cplusplus
extern "C" {
#endif
--- 40,52 ----
*
* The DMU also interacts with the SPA. That interface is described in
* dmu_spa.h.
*/
+ #include <sys/zfs_context.h>
#include <sys/inttypes.h>
#include <sys/cred.h>
#include <sys/fs/zfs.h>
#ifdef __cplusplus
extern "C" {
#endif
*** 289,300 ****
uint64_t db_offset; /* byte offset in this object */
uint64_t db_size; /* size of buffer in bytes */
void *db_data; /* data in buffer */
} dmu_buf_t;
- typedef void dmu_buf_evict_func_t(struct dmu_buf *db, void *user_ptr);
-
/*
* The names of zap entries in the DIRECTORY_OBJECT of the MOS.
*/
#define DMU_POOL_DIRECTORY_OBJECT 1
#define DMU_POOL_CONFIG "config"
--- 288,297 ----
*** 476,515 ****
*/
int dmu_buf_hold_array_by_bonus(dmu_buf_t *db, uint64_t offset,
uint64_t length, int read, void *tag, int *numbufsp, dmu_buf_t ***dbpp);
void dmu_buf_rele_array(dmu_buf_t **, int numbufs, void *tag);
/*
! * Returns NULL on success, or the existing user ptr if it's already
! * been set.
*
! * user_ptr is for use by the user and can be obtained via dmu_buf_get_user().
*
! * If non-NULL, pageout func will be called when this buffer is being
! * excised from the cache, so that you can clean up the data structure
! * pointed to by user_ptr.
*
! * dmu_evict_user() will call the pageout func for all buffers in a
! * objset with a given pageout func.
*/
! void *dmu_buf_set_user(dmu_buf_t *db, void *user_ptr,
! dmu_buf_evict_func_t *pageout_func);
/*
! * set_user_ie is the same as set_user, but request immediate eviction
! * when hold count goes to zero.
*/
! void *dmu_buf_set_user_ie(dmu_buf_t *db, void *user_ptr,
! dmu_buf_evict_func_t *pageout_func);
! void *dmu_buf_update_user(dmu_buf_t *db_fake, void *old_user_ptr,
! void *user_ptr, dmu_buf_evict_func_t *pageout_func);
! void dmu_evict_user(objset_t *os, dmu_buf_evict_func_t *func);
/*
! * Returns the user_ptr set with dmu_buf_set_user(), or NULL if not set.
*/
void *dmu_buf_get_user(dmu_buf_t *db);
/*
* Returns the blkptr associated with this dbuf, or NULL if not set.
*/
struct blkptr *dmu_buf_get_blkptr(dmu_buf_t *db);
--- 473,602 ----
*/
int dmu_buf_hold_array_by_bonus(dmu_buf_t *db, uint64_t offset,
uint64_t length, int read, void *tag, int *numbufsp, dmu_buf_t ***dbpp);
void dmu_buf_rele_array(dmu_buf_t **, int numbufs, void *tag);
+ typedef void dmu_buf_evict_func_t(void *user_ptr);
+
/*
! * A DMU buffer user object may be associated with a dbuf for the
! * duration of its lifetime. This allows the user of a dbuf (client)
! * to attach private data to a dbuf (e.g. in-core only data such as a
! * dnode_children_t, zap_t, or zap_leaf_t) and be optionally notified
! * when that dbuf has been evicted. Clients typically respond to the
! * eviction notification by freeing their private data, thus ensuring
! * the same lifetime for both dbuf and private data.
*
! * The mapping from a dmu_buf_user_t to any client private data is the
! * client's responsibility. All current consumers of the API with private
! * data embed a dmu_buf_user_t as the first member of the structure for
! * their private data. This allows conversions between the two types
! * with a simple cast. Since the DMU buf user API never needs access
! * to the private data, other strategies can be employed if necessary
! * or convenient for the client (e.g. using container_of() to do the
! * conversion for private data that cannot have the dmu_buf_user_t as
! * its first member).
*
! * Eviction callbacks are executed without the dbuf mutex held or any
! * other type of mechanism to guarantee that the dbuf is still available.
! * For this reason, users must assume the dbuf has already been freed
! * and not reference the dbuf from the callback context.
*
! * Users requesting "immediate eviction" are notified as soon as the dbuf
! * is only referenced by dirty records (dirties == holds). Otherwise the
! * notification occurs after eviction processing for the dbuf begins.
*/
! typedef struct dmu_buf_user {
! /*
! * Asynchronous user eviction callback state.
! */
! taskq_ent_t dbu_tqent;
!
! /* This instance's eviction function pointer. */
! dmu_buf_evict_func_t *dbu_evict_func;
! #ifdef ZFS_DEBUG
! /*
! * Pointer to user's dbuf pointer. NULL for clients that do
! * not associate a dbuf with their user data.
! *
! * The dbuf pointer is cleared upon eviction so as to catch
! * use-after-evict bugs in clients.
! */
! dmu_buf_t **dbu_clear_on_evict_dbufp;
! #endif
! } dmu_buf_user_t;
!
/*
! * Initialize the given dmu_buf_user_t instance with the eviction function
! * evict_func, to be called when the user is evicted.
! *
! * NOTE: This function should only be called once on a given dmu_buf_user_t.
! * To allow enforcement of this, dbu must already be zeroed on entry.
*/
! #ifdef __lint
! /* Very ugly, but it beats issuing suppression directives in many Makefiles. */
! extern void
! dmu_buf_init_user(dmu_buf_user_t *dbu, dmu_buf_evict_func_t *evict_func,
! dmu_buf_t **clear_on_evict_dbufp);
! #else /* __lint */
! inline void
! dmu_buf_init_user(dmu_buf_user_t *dbu, dmu_buf_evict_func_t *evict_func,
! dmu_buf_t **clear_on_evict_dbufp)
! {
! ASSERT(dbu->dbu_evict_func == NULL);
! ASSERT(evict_func != NULL);
! dbu->dbu_evict_func = evict_func;
! #ifdef ZFS_DEBUG
! dbu->dbu_clear_on_evict_dbufp = clear_on_evict_dbufp;
! #endif
! }
! #endif /* __lint */
/*
! * Attach user data to a dbuf and mark it for normal (when the dbuf's
! * data is cleared or its reference count goes to zero) eviction processing.
! *
! * Returns NULL on success, or the existing user if another user currently
! * owns the buffer.
*/
+ void *dmu_buf_set_user(dmu_buf_t *db, dmu_buf_user_t *user);
+
+ /*
+ * Attach user data to a dbuf and mark it for immediate (its dirty and
+ * reference counts are equal) eviction processing.
+ *
+ * Returns NULL on success, or the existing user if another user currently
+ * owns the buffer.
+ */
+ void *dmu_buf_set_user_ie(dmu_buf_t *db, dmu_buf_user_t *user);
+
+ /*
+ * Replace the current user of a dbuf.
+ *
+ * If given the current user of a dbuf, replaces the dbuf's user with
+ * "new_user" and returns the user data pointer that was replaced.
+ * Otherwise returns the current, and unmodified, dbuf user pointer.
+ */
+ void *dmu_buf_replace_user(dmu_buf_t *db,
+ dmu_buf_user_t *old_user, dmu_buf_user_t *new_user);
+
+ /*
+ * Remove the specified user data for a DMU buffer.
+ *
+ * Returns the user that was removed on success, or the current user if
+ * another user currently owns the buffer.
+ */
+ void *dmu_buf_remove_user(dmu_buf_t *db, dmu_buf_user_t *user);
+
+ /*
+ * Returns the user data (dmu_buf_user_t *) associated with this dbuf.
+ */
void *dmu_buf_get_user(dmu_buf_t *db);
+ /* Block until any in-progress dmu buf user evictions complete. */
+ void dmu_buf_user_evict_wait(void);
+
/*
* Returns the blkptr associated with this dbuf, or NULL if not set.
*/
struct blkptr *dmu_buf_get_blkptr(dmu_buf_t *db);