Illumos 5056 - ZFS deadlock on db_mtx and dn_holds
5056 ZFS deadlock on db_mtx and dn_holds Author: Justin Gibbs <justing@spectralogic.com> 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> References: https://www.illumos.org/issues/5056 https://github.com/illumos/illumos-gate/commit/bc9014e Porting Notes: sa_handle_get_from_db(): - the original patch includes an otherwise unmentioned fix for a possible usage of an uninitialised variable dmu_objset_open_impl(): - Under Illumos list_link_init() is the same as filling a list_node_t with NULLs, so they don't notice if they miss doing list_link_init() on a zero'd containing structure (e.g. allocated with kmem_zalloc as here). Under Linux, not so much: an uninitialised list_node_t goes "Boom!" some time later when it's used or destroyed. dmu_objset_evict_dbufs(): - reduce stack usage using kmem_alloc() Ported-by: Chris Dunlop <chris@onthe.net.au> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
parent
d683ddbb72
commit
0c66c32d1d
|
@ -22,6 +22,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_DBUF_H
|
#ifndef _SYS_DBUF_H
|
||||||
|
@ -226,9 +227,8 @@ typedef struct dmu_buf_impl {
|
||||||
|
|
||||||
/* Data which is unique to data (leaf) blocks: */
|
/* Data which is unique to data (leaf) blocks: */
|
||||||
|
|
||||||
/* stuff we store for the user (see dmu_buf_set_user) */
|
/* User callback information. */
|
||||||
void *db_user_ptr;
|
dmu_buf_user_t *db_user;
|
||||||
dmu_buf_evict_func_t *db_evict_func;
|
|
||||||
|
|
||||||
uint8_t db_immediate_evict;
|
uint8_t db_immediate_evict;
|
||||||
uint8_t db_freed_in_flight;
|
uint8_t db_freed_in_flight;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||||
* Copyright 2014 HybridCluster. All rights reserved.
|
* Copyright 2014 HybridCluster. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Portions Copyright 2010 Robert Milkowski */
|
/* Portions Copyright 2010 Robert Milkowski */
|
||||||
|
@ -39,11 +40,9 @@
|
||||||
* dmu_spa.h.
|
* dmu_spa.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <sys/zfs_context.h>
|
||||||
#include <sys/inttypes.h>
|
#include <sys/inttypes.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/cred.h>
|
#include <sys/cred.h>
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/fs/zfs.h>
|
#include <sys/fs/zfs.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
@ -288,8 +287,6 @@ typedef struct dmu_buf {
|
||||||
void *db_data; /* data in buffer */
|
void *db_data; /* data in buffer */
|
||||||
} dmu_buf_t;
|
} 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.
|
* The names of zap entries in the DIRECTORY_OBJECT of the MOS.
|
||||||
*/
|
*/
|
||||||
|
@ -475,36 +472,126 @@ 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);
|
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);
|
void dmu_buf_rele_array(dmu_buf_t **, int numbufs, void *tag);
|
||||||
|
|
||||||
/*
|
typedef void dmu_buf_evict_func_t(void *user_ptr);
|
||||||
* 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.
|
* 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 */
|
||||||
|
static 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);
|
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.
|
* Returns the blkptr associated with this dbuf, or NULL if not set.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Portions Copyright 2010 Robert Milkowski */
|
/* Portions Copyright 2010 Robert Milkowski */
|
||||||
|
@ -74,22 +75,25 @@ struct objset {
|
||||||
arc_buf_t *os_phys_buf;
|
arc_buf_t *os_phys_buf;
|
||||||
objset_phys_t *os_phys;
|
objset_phys_t *os_phys;
|
||||||
/*
|
/*
|
||||||
* The following "special" dnodes have no parent and are exempt from
|
* The following "special" dnodes have no parent, are exempt
|
||||||
* dnode_move(), but they root their descendents in this objset using
|
* from dnode_move(), and are not recorded in os_dnodes, but they
|
||||||
* handles anyway, so that all access to dnodes from dbufs consistently
|
* root their descendents in this objset using handles anyway, so
|
||||||
* uses handles.
|
* that all access to dnodes from dbufs consistently uses handles.
|
||||||
*/
|
*/
|
||||||
dnode_handle_t os_meta_dnode;
|
dnode_handle_t os_meta_dnode;
|
||||||
dnode_handle_t os_userused_dnode;
|
dnode_handle_t os_userused_dnode;
|
||||||
dnode_handle_t os_groupused_dnode;
|
dnode_handle_t os_groupused_dnode;
|
||||||
zilog_t *os_zil;
|
zilog_t *os_zil;
|
||||||
|
|
||||||
|
list_node_t os_evicting_node;
|
||||||
|
|
||||||
/* can change, under dsl_dir's locks: */
|
/* can change, under dsl_dir's locks: */
|
||||||
enum zio_checksum os_checksum;
|
enum zio_checksum os_checksum;
|
||||||
enum zio_compress os_compress;
|
enum zio_compress os_compress;
|
||||||
uint8_t os_copies;
|
uint8_t os_copies;
|
||||||
enum zio_checksum os_dedup_checksum;
|
enum zio_checksum os_dedup_checksum;
|
||||||
boolean_t os_dedup_verify;
|
boolean_t os_dedup_verify;
|
||||||
|
boolean_t os_evicting;
|
||||||
zfs_logbias_op_t os_logbias;
|
zfs_logbias_op_t os_logbias;
|
||||||
zfs_cache_type_t os_primary_cache;
|
zfs_cache_type_t os_primary_cache;
|
||||||
zfs_cache_type_t os_secondary_cache;
|
zfs_cache_type_t os_secondary_cache;
|
||||||
|
@ -168,6 +172,8 @@ int dmu_objset_userspace_upgrade(objset_t *os);
|
||||||
boolean_t dmu_objset_userspace_present(objset_t *os);
|
boolean_t dmu_objset_userspace_present(objset_t *os);
|
||||||
int dmu_fsname(const char *snapname, char *buf);
|
int dmu_fsname(const char *snapname, char *buf);
|
||||||
|
|
||||||
|
void dmu_objset_evict_done(objset_t *os);
|
||||||
|
|
||||||
void dmu_objset_init(void);
|
void dmu_objset_init(void);
|
||||||
void dmu_objset_fini(void);
|
void dmu_objset_fini(void);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_DNODE_H
|
#ifndef _SYS_DNODE_H
|
||||||
|
@ -277,6 +278,7 @@ typedef struct dnode_handle {
|
||||||
} dnode_handle_t;
|
} dnode_handle_t;
|
||||||
|
|
||||||
typedef struct dnode_children {
|
typedef struct dnode_children {
|
||||||
|
dmu_buf_user_t dnc_dbu; /* User evict data */
|
||||||
size_t dnc_count; /* number of children */
|
size_t dnc_count; /* number of children */
|
||||||
dnode_handle_t dnc_children[]; /* sized dynamically */
|
dnode_handle_t dnc_children[]; /* sized dynamically */
|
||||||
} dnode_children_t;
|
} dnode_children_t;
|
||||||
|
@ -287,7 +289,7 @@ typedef struct free_range {
|
||||||
uint64_t fr_nblks;
|
uint64_t fr_nblks;
|
||||||
} free_range_t;
|
} free_range_t;
|
||||||
|
|
||||||
dnode_t *dnode_special_open(struct objset *dd, dnode_phys_t *dnp,
|
void dnode_special_open(struct objset *dd, dnode_phys_t *dnp,
|
||||||
uint64_t object, dnode_handle_t *dnh);
|
uint64_t object, dnode_handle_t *dnh);
|
||||||
void dnode_special_close(dnode_handle_t *dnh);
|
void dnode_special_close(dnode_handle_t *dnh);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_DSL_DATASET_H
|
#ifndef _SYS_DSL_DATASET_H
|
||||||
|
@ -125,11 +126,14 @@ typedef struct dsl_dataset_phys {
|
||||||
} dsl_dataset_phys_t;
|
} dsl_dataset_phys_t;
|
||||||
|
|
||||||
typedef struct dsl_dataset {
|
typedef struct dsl_dataset {
|
||||||
|
dmu_buf_user_t ds_dbu;
|
||||||
|
|
||||||
/* Immutable: */
|
/* Immutable: */
|
||||||
struct dsl_dir *ds_dir;
|
struct dsl_dir *ds_dir;
|
||||||
dmu_buf_t *ds_dbuf;
|
dmu_buf_t *ds_dbuf;
|
||||||
uint64_t ds_object;
|
uint64_t ds_object;
|
||||||
uint64_t ds_fsid_guid;
|
uint64_t ds_fsid_guid;
|
||||||
|
boolean_t ds_is_snapshot;
|
||||||
|
|
||||||
/* only used in syncing context, only valid for non-snapshots: */
|
/* only used in syncing context, only valid for non-snapshots: */
|
||||||
struct dsl_dataset *ds_prev;
|
struct dsl_dataset *ds_prev;
|
||||||
|
@ -188,12 +192,6 @@ dsl_dataset_phys(dsl_dataset_t *ds)
|
||||||
*/
|
*/
|
||||||
#define MAX_TAG_PREFIX_LEN 17
|
#define MAX_TAG_PREFIX_LEN 17
|
||||||
|
|
||||||
static inline boolean_t
|
|
||||||
dsl_dataset_is_snapshot(dsl_dataset_t *ds)
|
|
||||||
{
|
|
||||||
return (dsl_dataset_phys(ds)->ds_num_children != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DS_UNIQUE_IS_ACCURATE(ds) \
|
#define DS_UNIQUE_IS_ACCURATE(ds) \
|
||||||
((dsl_dataset_phys(ds)->ds_flags & DS_FLAG_UNIQUE_ACCURATE) != 0)
|
((dsl_dataset_phys(ds)->ds_flags & DS_FLAG_UNIQUE_ACCURATE) != 0)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_DSL_DIR_H
|
#ifndef _SYS_DSL_DIR_H
|
||||||
|
@ -84,6 +85,8 @@ typedef struct dsl_dir_phys {
|
||||||
} dsl_dir_phys_t;
|
} dsl_dir_phys_t;
|
||||||
|
|
||||||
struct dsl_dir {
|
struct dsl_dir {
|
||||||
|
dmu_buf_user_t dd_dbu;
|
||||||
|
|
||||||
/* These are immutable; no lock needed: */
|
/* These are immutable; no lock needed: */
|
||||||
uint64_t dd_object;
|
uint64_t dd_object;
|
||||||
dsl_pool_t *dd_pool;
|
dsl_pool_t *dd_pool;
|
||||||
|
@ -119,6 +122,7 @@ dsl_dir_phys(dsl_dir_t *dd)
|
||||||
}
|
}
|
||||||
|
|
||||||
void dsl_dir_rele(dsl_dir_t *dd, void *tag);
|
void dsl_dir_rele(dsl_dir_t *dd, void *tag);
|
||||||
|
void dsl_dir_async_rele(dsl_dir_t *dd, void *tag);
|
||||||
int dsl_dir_hold(dsl_pool_t *dp, const char *name, void *tag,
|
int dsl_dir_hold(dsl_pool_t *dp, const char *name, void *tag,
|
||||||
dsl_dir_t **, const char **tail);
|
dsl_dir_t **, const char **tail);
|
||||||
int dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj,
|
int dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj,
|
||||||
|
|
|
@ -133,7 +133,6 @@ int sa_update_from_cb(sa_handle_t *, sa_attr_type_t,
|
||||||
uint32_t buflen, sa_data_locator_t *, void *userdata, dmu_tx_t *);
|
uint32_t buflen, sa_data_locator_t *, void *userdata, dmu_tx_t *);
|
||||||
void sa_object_info(sa_handle_t *, dmu_object_info_t *);
|
void sa_object_info(sa_handle_t *, dmu_object_info_t *);
|
||||||
void sa_object_size(sa_handle_t *, uint32_t *, u_longlong_t *);
|
void sa_object_size(sa_handle_t *, uint32_t *, u_longlong_t *);
|
||||||
void sa_update_user(sa_handle_t *, sa_handle_t *);
|
|
||||||
void *sa_get_userdata(sa_handle_t *);
|
void *sa_get_userdata(sa_handle_t *);
|
||||||
void sa_set_userp(sa_handle_t *, void *);
|
void sa_set_userp(sa_handle_t *, void *);
|
||||||
dmu_buf_t *sa_get_db(sa_handle_t *);
|
dmu_buf_t *sa_get_db(sa_handle_t *);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_SA_IMPL_H
|
#ifndef _SYS_SA_IMPL_H
|
||||||
|
@ -208,11 +209,12 @@ typedef enum sa_data_op {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct sa_handle {
|
struct sa_handle {
|
||||||
|
dmu_buf_user_t sa_dbu;
|
||||||
kmutex_t sa_lock;
|
kmutex_t sa_lock;
|
||||||
dmu_buf_t *sa_bonus;
|
dmu_buf_t *sa_bonus;
|
||||||
dmu_buf_t *sa_spill;
|
dmu_buf_t *sa_spill;
|
||||||
objset_t *sa_os;
|
objset_t *sa_os;
|
||||||
void *sa_userp;
|
void *sa_userp;
|
||||||
sa_idx_tab_t *sa_bonus_tab; /* idx of bonus */
|
sa_idx_tab_t *sa_bonus_tab; /* idx of bonus */
|
||||||
sa_idx_tab_t *sa_spill_tab; /* only present if spill activated */
|
sa_idx_tab_t *sa_spill_tab; /* only present if spill activated */
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
||||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_SPA_H
|
#ifndef _SYS_SPA_H
|
||||||
|
@ -680,6 +681,7 @@ extern spa_t *spa_next(spa_t *prev);
|
||||||
/* Refcount functions */
|
/* Refcount functions */
|
||||||
extern void spa_open_ref(spa_t *spa, void *tag);
|
extern void spa_open_ref(spa_t *spa, void *tag);
|
||||||
extern void spa_close(spa_t *spa, void *tag);
|
extern void spa_close(spa_t *spa, void *tag);
|
||||||
|
extern void spa_async_close(spa_t *spa, void *tag);
|
||||||
extern boolean_t spa_refcount_zero(spa_t *spa);
|
extern boolean_t spa_refcount_zero(spa_t *spa);
|
||||||
|
|
||||||
#define SCL_NONE 0x00
|
#define SCL_NONE 0x00
|
||||||
|
@ -789,6 +791,9 @@ extern uint64_t spa_version(spa_t *spa);
|
||||||
extern boolean_t spa_deflate(spa_t *spa);
|
extern boolean_t spa_deflate(spa_t *spa);
|
||||||
extern metaslab_class_t *spa_normal_class(spa_t *spa);
|
extern metaslab_class_t *spa_normal_class(spa_t *spa);
|
||||||
extern metaslab_class_t *spa_log_class(spa_t *spa);
|
extern metaslab_class_t *spa_log_class(spa_t *spa);
|
||||||
|
extern void spa_evicting_os_register(spa_t *, objset_t *os);
|
||||||
|
extern void spa_evicting_os_deregister(spa_t *, objset_t *os);
|
||||||
|
extern void spa_evicting_os_wait(spa_t *spa);
|
||||||
extern int spa_max_replication(spa_t *spa);
|
extern int spa_max_replication(spa_t *spa);
|
||||||
extern int spa_prev_software_version(spa_t *spa);
|
extern int spa_prev_software_version(spa_t *spa);
|
||||||
extern uint8_t spa_get_failmode(spa_t *spa);
|
extern uint8_t spa_get_failmode(spa_t *spa);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
||||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_SPA_IMPL_H
|
#ifndef _SYS_SPA_IMPL_H
|
||||||
|
@ -144,6 +145,9 @@ struct spa {
|
||||||
uint64_t spa_claim_max_txg; /* highest claimed birth txg */
|
uint64_t spa_claim_max_txg; /* highest claimed birth txg */
|
||||||
timespec_t spa_loaded_ts; /* 1st successful open time */
|
timespec_t spa_loaded_ts; /* 1st successful open time */
|
||||||
objset_t *spa_meta_objset; /* copy of dp->dp_meta_objset */
|
objset_t *spa_meta_objset; /* copy of dp->dp_meta_objset */
|
||||||
|
kmutex_t spa_evicting_os_lock; /* Evicting objset list lock */
|
||||||
|
list_t spa_evicting_os_list; /* Objsets being evicted. */
|
||||||
|
kcondvar_t spa_evicting_os_cv; /* Objset Eviction Completion */
|
||||||
txg_list_t spa_vdev_txg_list; /* per-txg dirty vdev list */
|
txg_list_t spa_vdev_txg_list; /* per-txg dirty vdev list */
|
||||||
vdev_t *spa_root_vdev; /* top-level vdev container */
|
vdev_t *spa_root_vdev; /* top-level vdev container */
|
||||||
uint64_t spa_config_guid; /* config pool guid */
|
uint64_t spa_config_guid; /* config pool guid */
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_ZAP_IMPL_H
|
#ifndef _SYS_ZAP_IMPL_H
|
||||||
|
@ -140,6 +141,7 @@ typedef struct zap_phys {
|
||||||
typedef struct zap_table_phys zap_table_phys_t;
|
typedef struct zap_table_phys zap_table_phys_t;
|
||||||
|
|
||||||
typedef struct zap {
|
typedef struct zap {
|
||||||
|
dmu_buf_user_t zap_dbu;
|
||||||
objset_t *zap_objset;
|
objset_t *zap_objset;
|
||||||
uint64_t zap_object;
|
uint64_t zap_object;
|
||||||
struct dmu_buf *zap_dbuf;
|
struct dmu_buf *zap_dbuf;
|
||||||
|
@ -196,7 +198,7 @@ boolean_t zap_match(zap_name_t *zn, const char *matchname);
|
||||||
int zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx,
|
int zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx,
|
||||||
krw_t lti, boolean_t fatreader, boolean_t adding, zap_t **zapp);
|
krw_t lti, boolean_t fatreader, boolean_t adding, zap_t **zapp);
|
||||||
void zap_unlockdir(zap_t *zap);
|
void zap_unlockdir(zap_t *zap);
|
||||||
void zap_evict(dmu_buf_t *db, void *vmzap);
|
void zap_evict(void *dbu);
|
||||||
zap_name_t *zap_name_alloc(zap_t *zap, const char *key, matchtype_t mt);
|
zap_name_t *zap_name_alloc(zap_t *zap, const char *key, matchtype_t mt);
|
||||||
void zap_name_free(zap_name_t *zn);
|
void zap_name_free(zap_name_t *zn);
|
||||||
int zap_hashbits(zap_t *zap);
|
int zap_hashbits(zap_t *zap);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_ZAP_LEAF_H
|
#ifndef _SYS_ZAP_LEAF_H
|
||||||
|
@ -152,6 +153,7 @@ typedef union zap_leaf_chunk {
|
||||||
} zap_leaf_chunk_t;
|
} zap_leaf_chunk_t;
|
||||||
|
|
||||||
typedef struct zap_leaf {
|
typedef struct zap_leaf {
|
||||||
|
dmu_buf_user_t l_dbu;
|
||||||
krwlock_t l_rwlock;
|
krwlock_t l_rwlock;
|
||||||
uint64_t l_blkid; /* 1<<ZAP_BLOCK_SHIFT byte block off */
|
uint64_t l_blkid; /* 1<<ZAP_BLOCK_SHIFT byte block off */
|
||||||
int l_bs; /* block size shift */
|
int l_bs; /* block size shift */
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
|
@ -78,10 +79,16 @@ static void dbuf_destroy(dmu_buf_impl_t *db);
|
||||||
static boolean_t dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
|
static boolean_t dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
|
||||||
static void dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx);
|
static void dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx);
|
||||||
|
|
||||||
|
#ifndef __lint
|
||||||
|
extern 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);
|
||||||
|
#endif /* ! __lint */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global data structures and functions for the dbuf cache.
|
* Global data structures and functions for the dbuf cache.
|
||||||
*/
|
*/
|
||||||
static kmem_cache_t *dbuf_cache;
|
static kmem_cache_t *dbuf_cache;
|
||||||
|
static taskq_t *dbu_evict_taskq;
|
||||||
|
|
||||||
/* ARGSUSED */
|
/* ARGSUSED */
|
||||||
static int
|
static int
|
||||||
|
@ -247,17 +254,72 @@ dbuf_hash_remove(dmu_buf_impl_t *db)
|
||||||
|
|
||||||
static arc_evict_func_t dbuf_do_evict;
|
static arc_evict_func_t dbuf_do_evict;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DBVU_EVICTING,
|
||||||
|
DBVU_NOT_EVICTING
|
||||||
|
} dbvu_verify_type_t;
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbuf_verify_user(dmu_buf_impl_t *db, dbvu_verify_type_t verify_type)
|
||||||
|
{
|
||||||
|
#ifdef ZFS_DEBUG
|
||||||
|
int64_t holds;
|
||||||
|
|
||||||
|
if (db->db_user == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Only data blocks support the attachment of user data. */
|
||||||
|
ASSERT(db->db_level == 0);
|
||||||
|
|
||||||
|
/* Clients must resolve a dbuf before attaching user data. */
|
||||||
|
ASSERT(db->db.db_data != NULL);
|
||||||
|
ASSERT3U(db->db_state, ==, DB_CACHED);
|
||||||
|
|
||||||
|
holds = refcount_count(&db->db_holds);
|
||||||
|
if (verify_type == DBVU_EVICTING) {
|
||||||
|
/*
|
||||||
|
* Immediate eviction occurs when holds == dirtycnt.
|
||||||
|
* For normal eviction buffers, holds is zero on
|
||||||
|
* eviction, except when dbuf_fix_old_data() calls
|
||||||
|
* dbuf_clear_data(). However, the hold count can grow
|
||||||
|
* during eviction even though db_mtx is held (see
|
||||||
|
* dmu_bonus_hold() for an example), so we can only
|
||||||
|
* test the generic invariant that holds >= dirtycnt.
|
||||||
|
*/
|
||||||
|
ASSERT3U(holds, >=, db->db_dirtycnt);
|
||||||
|
} else {
|
||||||
|
if (db->db_immediate_evict == TRUE)
|
||||||
|
ASSERT3U(holds, >=, db->db_dirtycnt);
|
||||||
|
else
|
||||||
|
ASSERT3U(holds, >, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dbuf_evict_user(dmu_buf_impl_t *db)
|
dbuf_evict_user(dmu_buf_impl_t *db)
|
||||||
{
|
{
|
||||||
|
dmu_buf_user_t *dbu = db->db_user;
|
||||||
|
|
||||||
ASSERT(MUTEX_HELD(&db->db_mtx));
|
ASSERT(MUTEX_HELD(&db->db_mtx));
|
||||||
|
|
||||||
if (db->db_level != 0 || db->db_evict_func == NULL)
|
if (dbu == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
db->db_evict_func(&db->db, db->db_user_ptr);
|
dbuf_verify_user(db, DBVU_EVICTING);
|
||||||
db->db_user_ptr = NULL;
|
db->db_user = NULL;
|
||||||
db->db_evict_func = NULL;
|
|
||||||
|
#ifdef ZFS_DEBUG
|
||||||
|
if (dbu->dbu_clear_on_evict_dbufp != NULL)
|
||||||
|
*dbu->dbu_clear_on_evict_dbufp = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invoke the callback from a taskq to avoid lock order reversals
|
||||||
|
* and limit stack depth.
|
||||||
|
*/
|
||||||
|
taskq_dispatch_ent(dbu_evict_taskq, dbu->dbu_evict_func, dbu, 0,
|
||||||
|
&dbu->dbu_tqent);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean_t
|
boolean_t
|
||||||
|
@ -331,6 +393,12 @@ retry:
|
||||||
mutex_init(&h->hash_mutexes[i], NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&h->hash_mutexes[i], NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
|
||||||
dbuf_stats_init(h);
|
dbuf_stats_init(h);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All entries are queued via taskq_dispatch_ent(), so min/maxalloc
|
||||||
|
* configuration is not required.
|
||||||
|
*/
|
||||||
|
dbu_evict_taskq = taskq_create("dbu_evict", 1, minclsyspri, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -353,6 +421,7 @@ dbuf_fini(void)
|
||||||
kmem_free(h->hash_table, (h->hash_table_mask + 1) * sizeof (void *));
|
kmem_free(h->hash_table, (h->hash_table_mask + 1) * sizeof (void *));
|
||||||
#endif
|
#endif
|
||||||
kmem_cache_destroy(dbuf_cache);
|
kmem_cache_destroy(dbuf_cache);
|
||||||
|
taskq_destroy(dbu_evict_taskq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -470,22 +539,28 @@ dbuf_verify(dmu_buf_impl_t *db)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbuf_clear_data(dmu_buf_impl_t *db)
|
||||||
|
{
|
||||||
|
ASSERT(MUTEX_HELD(&db->db_mtx));
|
||||||
|
dbuf_evict_user(db);
|
||||||
|
db->db_buf = NULL;
|
||||||
|
db->db.db_data = NULL;
|
||||||
|
if (db->db_state != DB_NOFILL)
|
||||||
|
db->db_state = DB_UNCACHED;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dbuf_set_data(dmu_buf_impl_t *db, arc_buf_t *buf)
|
dbuf_set_data(dmu_buf_impl_t *db, arc_buf_t *buf)
|
||||||
{
|
{
|
||||||
ASSERT(MUTEX_HELD(&db->db_mtx));
|
ASSERT(MUTEX_HELD(&db->db_mtx));
|
||||||
|
ASSERT(buf != NULL);
|
||||||
|
|
||||||
db->db_buf = buf;
|
db->db_buf = buf;
|
||||||
if (buf != NULL) {
|
ASSERT(buf->b_data != NULL);
|
||||||
ASSERT(buf->b_data != NULL);
|
db->db.db_data = buf->b_data;
|
||||||
db->db.db_data = buf->b_data;
|
if (!arc_released(buf))
|
||||||
if (!arc_released(buf))
|
arc_set_callback(buf, dbuf_do_evict, db);
|
||||||
arc_set_callback(buf, dbuf_do_evict, db);
|
|
||||||
} else {
|
|
||||||
dbuf_evict_user(db);
|
|
||||||
db->db.db_data = NULL;
|
|
||||||
if (db->db_state != DB_NOFILL)
|
|
||||||
db->db_state = DB_UNCACHED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -507,7 +582,7 @@ dbuf_loan_arcbuf(dmu_buf_impl_t *db)
|
||||||
} else {
|
} else {
|
||||||
abuf = db->db_buf;
|
abuf = db->db_buf;
|
||||||
arc_loan_inuse_buf(abuf, db);
|
arc_loan_inuse_buf(abuf, db);
|
||||||
dbuf_set_data(db, NULL);
|
dbuf_clear_data(db);
|
||||||
mutex_exit(&db->db_mtx);
|
mutex_exit(&db->db_mtx);
|
||||||
}
|
}
|
||||||
return (abuf);
|
return (abuf);
|
||||||
|
@ -747,7 +822,7 @@ dbuf_noread(dmu_buf_impl_t *db)
|
||||||
dbuf_set_data(db, arc_buf_alloc(spa, db->db.db_size, db, type));
|
dbuf_set_data(db, arc_buf_alloc(spa, db->db.db_size, db, type));
|
||||||
db->db_state = DB_FILL;
|
db->db_state = DB_FILL;
|
||||||
} else if (db->db_state == DB_NOFILL) {
|
} else if (db->db_state == DB_NOFILL) {
|
||||||
dbuf_set_data(db, NULL);
|
dbuf_clear_data(db);
|
||||||
} else {
|
} else {
|
||||||
ASSERT3U(db->db_state, ==, DB_CACHED);
|
ASSERT3U(db->db_state, ==, DB_CACHED);
|
||||||
}
|
}
|
||||||
|
@ -803,7 +878,7 @@ dbuf_fix_old_data(dmu_buf_impl_t *db, uint64_t txg)
|
||||||
dr->dt.dl.dr_data = arc_buf_alloc(spa, size, db, type);
|
dr->dt.dl.dr_data = arc_buf_alloc(spa, size, db, type);
|
||||||
bcopy(db->db.db_data, dr->dt.dl.dr_data->b_data, size);
|
bcopy(db->db.db_data, dr->dt.dl.dr_data->b_data, size);
|
||||||
} else {
|
} else {
|
||||||
dbuf_set_data(db, NULL);
|
dbuf_clear_data(db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -854,7 +929,8 @@ void
|
||||||
dbuf_free_range(dnode_t *dn, uint64_t start_blkid, uint64_t end_blkid,
|
dbuf_free_range(dnode_t *dn, uint64_t start_blkid, uint64_t end_blkid,
|
||||||
dmu_tx_t *tx)
|
dmu_tx_t *tx)
|
||||||
{
|
{
|
||||||
dmu_buf_impl_t *db, *db_next, *db_search;
|
dmu_buf_impl_t *db_search;
|
||||||
|
dmu_buf_impl_t *db, *db_next;
|
||||||
uint64_t txg = tx->tx_txg;
|
uint64_t txg = tx->tx_txg;
|
||||||
avl_index_t where;
|
avl_index_t where;
|
||||||
boolean_t freespill =
|
boolean_t freespill =
|
||||||
|
@ -864,7 +940,7 @@ dbuf_free_range(dnode_t *dn, uint64_t start_blkid, uint64_t end_blkid,
|
||||||
end_blkid = dn->dn_maxblkid;
|
end_blkid = dn->dn_maxblkid;
|
||||||
dprintf_dnode(dn, "start=%llu end=%llu\n", start_blkid, end_blkid);
|
dprintf_dnode(dn, "start=%llu end=%llu\n", start_blkid, end_blkid);
|
||||||
|
|
||||||
db_seach = kmem_alloc(sizeof (dmu_buf_impl_t), KM_SLEEP);
|
db_search = kmem_alloc(sizeof (dmu_buf_impl_t), KM_SLEEP);
|
||||||
db_search->db_level = 0;
|
db_search->db_level = 0;
|
||||||
db_search->db_blkid = start_blkid;
|
db_search->db_blkid = start_blkid;
|
||||||
db_search->db_state = DB_SEARCH;
|
db_search->db_state = DB_SEARCH;
|
||||||
|
@ -1436,7 +1512,7 @@ dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
|
||||||
arc_buf_t *buf = db->db_buf;
|
arc_buf_t *buf = db->db_buf;
|
||||||
|
|
||||||
ASSERT(db->db_state == DB_NOFILL || arc_released(buf));
|
ASSERT(db->db_state == DB_NOFILL || arc_released(buf));
|
||||||
dbuf_set_data(db, NULL);
|
dbuf_clear_data(db);
|
||||||
VERIFY(arc_buf_remove_ref(buf, db));
|
VERIFY(arc_buf_remove_ref(buf, db));
|
||||||
dbuf_evict(db);
|
dbuf_evict(db);
|
||||||
return (B_TRUE);
|
return (B_TRUE);
|
||||||
|
@ -1785,8 +1861,7 @@ dbuf_create(dnode_t *dn, uint8_t level, uint64_t blkid,
|
||||||
db->db_parent = parent;
|
db->db_parent = parent;
|
||||||
db->db_blkptr = blkptr;
|
db->db_blkptr = blkptr;
|
||||||
|
|
||||||
db->db_user_ptr = NULL;
|
db->db_user = NULL;
|
||||||
db->db_evict_func = NULL;
|
|
||||||
db->db_immediate_evict = 0;
|
db->db_immediate_evict = 0;
|
||||||
db->db_freed_in_flight = 0;
|
db->db_freed_in_flight = 0;
|
||||||
|
|
||||||
|
@ -2273,7 +2348,7 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag)
|
||||||
/*
|
/*
|
||||||
* This dbuf has anonymous data associated with it.
|
* This dbuf has anonymous data associated with it.
|
||||||
*/
|
*/
|
||||||
dbuf_set_data(db, NULL);
|
dbuf_clear_data(db);
|
||||||
VERIFY(arc_buf_remove_ref(buf, db));
|
VERIFY(arc_buf_remove_ref(buf, db));
|
||||||
dbuf_evict(db);
|
dbuf_evict(db);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2306,7 +2381,8 @@ dbuf_rele_and_unlock(dmu_buf_impl_t *db, void *tag)
|
||||||
} else {
|
} else {
|
||||||
dbuf_clear(db);
|
dbuf_clear(db);
|
||||||
}
|
}
|
||||||
} else if (arc_buf_eviction_needed(db->db_buf)) {
|
} else if (db->db_objset->os_evicting ||
|
||||||
|
arc_buf_eviction_needed(db->db_buf)) {
|
||||||
dbuf_clear(db);
|
dbuf_clear(db);
|
||||||
} else {
|
} else {
|
||||||
mutex_exit(&db->db_mtx);
|
mutex_exit(&db->db_mtx);
|
||||||
|
@ -2325,51 +2401,57 @@ dbuf_refcount(dmu_buf_impl_t *db)
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
dmu_buf_set_user(dmu_buf_t *db_fake, void *user_ptr,
|
dmu_buf_replace_user(dmu_buf_t *db_fake, dmu_buf_user_t *old_user,
|
||||||
dmu_buf_evict_func_t *evict_func)
|
dmu_buf_user_t *new_user)
|
||||||
{
|
{
|
||||||
return (dmu_buf_update_user(db_fake, NULL, user_ptr, evict_func));
|
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
|
||||||
|
|
||||||
|
mutex_enter(&db->db_mtx);
|
||||||
|
dbuf_verify_user(db, DBVU_NOT_EVICTING);
|
||||||
|
if (db->db_user == old_user)
|
||||||
|
db->db_user = new_user;
|
||||||
|
else
|
||||||
|
old_user = db->db_user;
|
||||||
|
dbuf_verify_user(db, DBVU_NOT_EVICTING);
|
||||||
|
mutex_exit(&db->db_mtx);
|
||||||
|
|
||||||
|
return (old_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
dmu_buf_set_user_ie(dmu_buf_t *db_fake, void *user_ptr,
|
dmu_buf_set_user(dmu_buf_t *db_fake, dmu_buf_user_t *user)
|
||||||
dmu_buf_evict_func_t *evict_func)
|
{
|
||||||
|
return (dmu_buf_replace_user(db_fake, NULL, user));
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
dmu_buf_set_user_ie(dmu_buf_t *db_fake, dmu_buf_user_t *user)
|
||||||
{
|
{
|
||||||
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
|
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
|
||||||
|
|
||||||
db->db_immediate_evict = TRUE;
|
db->db_immediate_evict = TRUE;
|
||||||
return (dmu_buf_update_user(db_fake, NULL, user_ptr, evict_func));
|
return (dmu_buf_set_user(db_fake, user));
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
dmu_buf_update_user(dmu_buf_t *db_fake, void *old_user_ptr, void *user_ptr,
|
dmu_buf_remove_user(dmu_buf_t *db_fake, dmu_buf_user_t *user)
|
||||||
dmu_buf_evict_func_t *evict_func)
|
|
||||||
{
|
{
|
||||||
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
|
return (dmu_buf_replace_user(db_fake, user, NULL));
|
||||||
ASSERT(db->db_level == 0);
|
|
||||||
|
|
||||||
ASSERT((user_ptr == NULL) == (evict_func == NULL));
|
|
||||||
|
|
||||||
mutex_enter(&db->db_mtx);
|
|
||||||
|
|
||||||
if (db->db_user_ptr == old_user_ptr) {
|
|
||||||
db->db_user_ptr = user_ptr;
|
|
||||||
db->db_evict_func = evict_func;
|
|
||||||
} else {
|
|
||||||
old_user_ptr = db->db_user_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_exit(&db->db_mtx);
|
|
||||||
return (old_user_ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
dmu_buf_get_user(dmu_buf_t *db_fake)
|
dmu_buf_get_user(dmu_buf_t *db_fake)
|
||||||
{
|
{
|
||||||
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
|
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
|
||||||
ASSERT(!refcount_is_zero(&db->db_holds));
|
|
||||||
|
|
||||||
return (db->db_user_ptr);
|
dbuf_verify_user(db, DBVU_NOT_EVICTING);
|
||||||
|
return (db->db_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dmu_buf_user_evict_wait()
|
||||||
|
{
|
||||||
|
taskq_wait(dbu_evict_taskq);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean_t
|
boolean_t
|
||||||
|
@ -3037,7 +3119,6 @@ EXPORT_SYMBOL(dbuf_refcount);
|
||||||
EXPORT_SYMBOL(dbuf_sync_list);
|
EXPORT_SYMBOL(dbuf_sync_list);
|
||||||
EXPORT_SYMBOL(dmu_buf_set_user);
|
EXPORT_SYMBOL(dmu_buf_set_user);
|
||||||
EXPORT_SYMBOL(dmu_buf_set_user_ie);
|
EXPORT_SYMBOL(dmu_buf_set_user_ie);
|
||||||
EXPORT_SYMBOL(dmu_buf_update_user);
|
|
||||||
EXPORT_SYMBOL(dmu_buf_get_user);
|
EXPORT_SYMBOL(dmu_buf_get_user);
|
||||||
EXPORT_SYMBOL(dmu_buf_freeable);
|
EXPORT_SYMBOL(dmu_buf_freeable);
|
||||||
EXPORT_SYMBOL(dmu_buf_get_blkptr);
|
EXPORT_SYMBOL(dmu_buf_get_blkptr);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Portions Copyright 2010 Robert Milkowski */
|
/* Portions Copyright 2010 Robert Milkowski */
|
||||||
|
@ -347,7 +348,7 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
|
||||||
zfs_prop_to_name(ZFS_PROP_SECONDARYCACHE),
|
zfs_prop_to_name(ZFS_PROP_SECONDARYCACHE),
|
||||||
secondary_cache_changed_cb, os);
|
secondary_cache_changed_cb, os);
|
||||||
}
|
}
|
||||||
if (!dsl_dataset_is_snapshot(ds)) {
|
if (!ds->ds_is_snapshot) {
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
err = dsl_prop_register(ds,
|
err = dsl_prop_register(ds,
|
||||||
zfs_prop_to_name(ZFS_PROP_CHECKSUM),
|
zfs_prop_to_name(ZFS_PROP_CHECKSUM),
|
||||||
|
@ -404,7 +405,7 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
|
||||||
os->os_secondary_cache = ZFS_CACHE_ALL;
|
os->os_secondary_cache = ZFS_CACHE_ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds == NULL || !dsl_dataset_is_snapshot(ds))
|
if (ds == NULL || !ds->ds_is_snapshot)
|
||||||
os->os_zil_header = os->os_phys->os_zil_header;
|
os->os_zil_header = os->os_phys->os_zil_header;
|
||||||
os->os_zil = zil_alloc(os, &os->os_zil_header);
|
os->os_zil = zil_alloc(os, &os->os_zil_header);
|
||||||
|
|
||||||
|
@ -419,20 +420,19 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
|
||||||
list_create(&os->os_downgraded_dbufs, sizeof (dmu_buf_impl_t),
|
list_create(&os->os_downgraded_dbufs, sizeof (dmu_buf_impl_t),
|
||||||
offsetof(dmu_buf_impl_t, db_link));
|
offsetof(dmu_buf_impl_t, db_link));
|
||||||
|
|
||||||
|
list_link_init(&os->os_evicting_node);
|
||||||
|
|
||||||
mutex_init(&os->os_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&os->os_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
mutex_init(&os->os_obj_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&os->os_obj_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
mutex_init(&os->os_user_ptr_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&os->os_user_ptr_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
|
||||||
DMU_META_DNODE(os) = dnode_special_open(os,
|
dnode_special_open(os, &os->os_phys->os_meta_dnode,
|
||||||
&os->os_phys->os_meta_dnode, DMU_META_DNODE_OBJECT,
|
DMU_META_DNODE_OBJECT, &os->os_meta_dnode);
|
||||||
&os->os_meta_dnode);
|
|
||||||
if (arc_buf_size(os->os_phys_buf) >= sizeof (objset_phys_t)) {
|
if (arc_buf_size(os->os_phys_buf) >= sizeof (objset_phys_t)) {
|
||||||
DMU_USERUSED_DNODE(os) = dnode_special_open(os,
|
dnode_special_open(os, &os->os_phys->os_userused_dnode,
|
||||||
&os->os_phys->os_userused_dnode, DMU_USERUSED_OBJECT,
|
DMU_USERUSED_OBJECT, &os->os_userused_dnode);
|
||||||
&os->os_userused_dnode);
|
dnode_special_open(os, &os->os_phys->os_groupused_dnode,
|
||||||
DMU_GROUPUSED_DNODE(os) = dnode_special_open(os,
|
DMU_GROUPUSED_OBJECT, &os->os_groupused_dnode);
|
||||||
&os->os_phys->os_groupused_dnode, DMU_GROUPUSED_OBJECT,
|
|
||||||
&os->os_groupused_dnode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*osp = os;
|
*osp = os;
|
||||||
|
@ -520,7 +520,7 @@ dmu_objset_own(const char *name, dmu_objset_type_t type,
|
||||||
} else if (type != DMU_OST_ANY && type != (*osp)->os_phys->os_type) {
|
} else if (type != DMU_OST_ANY && type != (*osp)->os_phys->os_type) {
|
||||||
dsl_dataset_disown(ds, tag);
|
dsl_dataset_disown(ds, tag);
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
} else if (!readonly && dsl_dataset_is_snapshot(ds)) {
|
} else if (!readonly && ds->ds_is_snapshot) {
|
||||||
dsl_dataset_disown(ds, tag);
|
dsl_dataset_disown(ds, tag);
|
||||||
return (SET_ERROR(EROFS));
|
return (SET_ERROR(EROFS));
|
||||||
}
|
}
|
||||||
|
@ -576,41 +576,57 @@ dmu_objset_disown(objset_t *os, void *tag)
|
||||||
void
|
void
|
||||||
dmu_objset_evict_dbufs(objset_t *os)
|
dmu_objset_evict_dbufs(objset_t *os)
|
||||||
{
|
{
|
||||||
|
dnode_t *dn_marker;
|
||||||
dnode_t *dn;
|
dnode_t *dn;
|
||||||
|
|
||||||
|
dn_marker = kmem_alloc(sizeof (dnode_t), KM_SLEEP);
|
||||||
|
|
||||||
mutex_enter(&os->os_lock);
|
mutex_enter(&os->os_lock);
|
||||||
|
dn = list_head(&os->os_dnodes);
|
||||||
|
while (dn != NULL) {
|
||||||
|
/*
|
||||||
|
* Skip dnodes without holds. We have to do this dance
|
||||||
|
* because dnode_add_ref() only works if there is already a
|
||||||
|
* hold. If the dnode has no holds, then it has no dbufs.
|
||||||
|
*/
|
||||||
|
if (dnode_add_ref(dn, FTAG)) {
|
||||||
|
list_insert_after(&os->os_dnodes, dn, dn_marker);
|
||||||
|
mutex_exit(&os->os_lock);
|
||||||
|
|
||||||
/* process the mdn last, since the other dnodes have holds on it */
|
dnode_evict_dbufs(dn);
|
||||||
list_remove(&os->os_dnodes, DMU_META_DNODE(os));
|
dnode_rele(dn, FTAG);
|
||||||
list_insert_tail(&os->os_dnodes, DMU_META_DNODE(os));
|
|
||||||
|
|
||||||
/*
|
mutex_enter(&os->os_lock);
|
||||||
* Find the first dnode with holds. We have to do this dance
|
dn = list_next(&os->os_dnodes, dn_marker);
|
||||||
* because dnode_add_ref() only works if you already have a
|
list_remove(&os->os_dnodes, dn_marker);
|
||||||
* hold. If there are no holds then it has no dbufs so OK to
|
} else {
|
||||||
* skip.
|
dn = list_next(&os->os_dnodes, dn);
|
||||||
*/
|
}
|
||||||
for (dn = list_head(&os->os_dnodes);
|
|
||||||
dn && !dnode_add_ref(dn, FTAG);
|
|
||||||
dn = list_next(&os->os_dnodes, dn))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
while (dn) {
|
|
||||||
dnode_t *next_dn = dn;
|
|
||||||
|
|
||||||
do {
|
|
||||||
next_dn = list_next(&os->os_dnodes, next_dn);
|
|
||||||
} while (next_dn && !dnode_add_ref(next_dn, FTAG));
|
|
||||||
|
|
||||||
mutex_exit(&os->os_lock);
|
|
||||||
dnode_evict_dbufs(dn);
|
|
||||||
dnode_rele(dn, FTAG);
|
|
||||||
mutex_enter(&os->os_lock);
|
|
||||||
dn = next_dn;
|
|
||||||
}
|
}
|
||||||
mutex_exit(&os->os_lock);
|
mutex_exit(&os->os_lock);
|
||||||
|
|
||||||
|
kmem_free(dn_marker, sizeof (dnode_t));
|
||||||
|
|
||||||
|
if (DMU_USERUSED_DNODE(os) != NULL) {
|
||||||
|
dnode_evict_dbufs(DMU_GROUPUSED_DNODE(os));
|
||||||
|
dnode_evict_dbufs(DMU_USERUSED_DNODE(os));
|
||||||
|
}
|
||||||
|
dnode_evict_dbufs(DMU_META_DNODE(os));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Objset eviction processing is split into into two pieces.
|
||||||
|
* The first marks the objset as evicting, evicts any dbufs that
|
||||||
|
* have a refcount of zero, and then queues up the objset for the
|
||||||
|
* second phase of eviction. Once os->os_dnodes has been cleared by
|
||||||
|
* dnode_buf_pageout()->dnode_destroy(), the second phase is executed.
|
||||||
|
* The second phase closes the special dnodes, dequeues the objset from
|
||||||
|
* the list of those undergoing eviction, and finally frees the objset.
|
||||||
|
*
|
||||||
|
* NOTE: Due to asynchronous eviction processing (invocation of
|
||||||
|
* dnode_buf_pageout()), it is possible for the meta dnode for the
|
||||||
|
* objset to have no holds even though os->os_dnodes is not empty.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
dmu_objset_evict(objset_t *os)
|
dmu_objset_evict(objset_t *os)
|
||||||
{
|
{
|
||||||
|
@ -622,7 +638,7 @@ dmu_objset_evict(objset_t *os)
|
||||||
ASSERT(!dmu_objset_is_dirty(os, t));
|
ASSERT(!dmu_objset_is_dirty(os, t));
|
||||||
|
|
||||||
if (ds) {
|
if (ds) {
|
||||||
if (!dsl_dataset_is_snapshot(ds)) {
|
if (!ds->ds_is_snapshot) {
|
||||||
VERIFY0(dsl_prop_unregister(ds,
|
VERIFY0(dsl_prop_unregister(ds,
|
||||||
zfs_prop_to_name(ZFS_PROP_CHECKSUM),
|
zfs_prop_to_name(ZFS_PROP_CHECKSUM),
|
||||||
checksum_changed_cb, os));
|
checksum_changed_cb, os));
|
||||||
|
@ -656,8 +672,24 @@ dmu_objset_evict(objset_t *os)
|
||||||
if (os->os_sa)
|
if (os->os_sa)
|
||||||
sa_tear_down(os);
|
sa_tear_down(os);
|
||||||
|
|
||||||
|
os->os_evicting = B_TRUE;
|
||||||
dmu_objset_evict_dbufs(os);
|
dmu_objset_evict_dbufs(os);
|
||||||
|
|
||||||
|
mutex_enter(&os->os_lock);
|
||||||
|
spa_evicting_os_register(os->os_spa, os);
|
||||||
|
if (list_is_empty(&os->os_dnodes)) {
|
||||||
|
mutex_exit(&os->os_lock);
|
||||||
|
dmu_objset_evict_done(os);
|
||||||
|
} else {
|
||||||
|
mutex_exit(&os->os_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dmu_objset_evict_done(objset_t *os)
|
||||||
|
{
|
||||||
|
ASSERT3P(list_head(&os->os_dnodes), ==, NULL);
|
||||||
|
|
||||||
dnode_special_close(&os->os_meta_dnode);
|
dnode_special_close(&os->os_meta_dnode);
|
||||||
if (DMU_USERUSED_DNODE(os)) {
|
if (DMU_USERUSED_DNODE(os)) {
|
||||||
dnode_special_close(&os->os_userused_dnode);
|
dnode_special_close(&os->os_userused_dnode);
|
||||||
|
@ -665,8 +697,6 @@ dmu_objset_evict(objset_t *os)
|
||||||
}
|
}
|
||||||
zil_free(os->os_zil);
|
zil_free(os->os_zil);
|
||||||
|
|
||||||
ASSERT3P(list_head(&os->os_dnodes), ==, NULL);
|
|
||||||
|
|
||||||
VERIFY(arc_buf_remove_ref(os->os_phys_buf, &os->os_phys_buf));
|
VERIFY(arc_buf_remove_ref(os->os_phys_buf, &os->os_phys_buf));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -681,6 +711,7 @@ dmu_objset_evict(objset_t *os)
|
||||||
mutex_destroy(&os->os_lock);
|
mutex_destroy(&os->os_lock);
|
||||||
mutex_destroy(&os->os_obj_lock);
|
mutex_destroy(&os->os_obj_lock);
|
||||||
mutex_destroy(&os->os_user_ptr_lock);
|
mutex_destroy(&os->os_user_ptr_lock);
|
||||||
|
spa_evicting_os_deregister(os->os_spa, os);
|
||||||
kmem_free(os, sizeof (objset_t));
|
kmem_free(os, sizeof (objset_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,7 +919,7 @@ dmu_objset_clone_check(void *arg, dmu_tx_t *tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* You can only clone snapshots, not the head datasets. */
|
/* You can only clone snapshots, not the head datasets. */
|
||||||
if (!dsl_dataset_is_snapshot(origin)) {
|
if (!origin->ds_is_snapshot) {
|
||||||
dsl_dataset_rele(origin, FTAG);
|
dsl_dataset_rele(origin, FTAG);
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
}
|
}
|
||||||
|
@ -1453,7 +1484,7 @@ int
|
||||||
dmu_objset_is_snapshot(objset_t *os)
|
dmu_objset_is_snapshot(objset_t *os)
|
||||||
{
|
{
|
||||||
if (os->os_dsl_dataset != NULL)
|
if (os->os_dsl_dataset != NULL)
|
||||||
return (dsl_dataset_is_snapshot(os->os_dsl_dataset));
|
return (os->os_dsl_dataset->ds_is_snapshot);
|
||||||
else
|
else
|
||||||
return (B_FALSE);
|
return (B_FALSE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -615,7 +615,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
|
||||||
fromtxg = fromzb->zbm_creation_txg;
|
fromtxg = fromzb->zbm_creation_txg;
|
||||||
}
|
}
|
||||||
dsl_dataset_name(ds, drr->drr_u.drr_begin.drr_toname);
|
dsl_dataset_name(ds, drr->drr_u.drr_begin.drr_toname);
|
||||||
if (!dsl_dataset_is_snapshot(ds)) {
|
if (!ds->ds_is_snapshot) {
|
||||||
(void) strlcat(drr->drr_u.drr_begin.drr_toname, "@--head--",
|
(void) strlcat(drr->drr_u.drr_begin.drr_toname, "@--head--",
|
||||||
sizeof (drr->drr_u.drr_begin.drr_toname));
|
sizeof (drr->drr_u.drr_begin.drr_toname));
|
||||||
}
|
}
|
||||||
|
@ -820,7 +820,7 @@ dmu_send_estimate(dsl_dataset_t *ds, dsl_dataset_t *fromds, uint64_t *sizep)
|
||||||
ASSERT(dsl_pool_config_held(dp));
|
ASSERT(dsl_pool_config_held(dp));
|
||||||
|
|
||||||
/* tosnap must be a snapshot */
|
/* tosnap must be a snapshot */
|
||||||
if (!dsl_dataset_is_snapshot(ds))
|
if (!ds->ds_is_snapshot)
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1057,7 +1057,7 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx)
|
||||||
dsl_dataset_rele(ds, FTAG);
|
dsl_dataset_rele(ds, FTAG);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
if (!dsl_dataset_is_snapshot(origin)) {
|
if (!origin->ds_is_snapshot) {
|
||||||
dsl_dataset_rele(origin, FTAG);
|
dsl_dataset_rele(origin, FTAG);
|
||||||
dsl_dataset_rele(ds, FTAG);
|
dsl_dataset_rele(ds, FTAG);
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
|
|
@ -541,7 +541,7 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp,
|
||||||
ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID);
|
ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID);
|
||||||
|
|
||||||
/* See comment on ZIL traversal in dsl_scan_visitds. */
|
/* See comment on ZIL traversal in dsl_scan_visitds. */
|
||||||
if (ds != NULL && !dsl_dataset_is_snapshot(ds) && !BP_IS_HOLE(rootbp)) {
|
if (ds != NULL && !ds->ds_is_snapshot && !BP_IS_HOLE(rootbp)) {
|
||||||
uint32_t flags = ARC_WAIT;
|
uint32_t flags = ARC_WAIT;
|
||||||
objset_phys_t *osp;
|
objset_phys_t *osp;
|
||||||
arc_buf_t *buf;
|
arc_buf_t *buf;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
|
@ -405,8 +406,9 @@ static dnode_t *
|
||||||
dnode_create(objset_t *os, dnode_phys_t *dnp, dmu_buf_impl_t *db,
|
dnode_create(objset_t *os, dnode_phys_t *dnp, dmu_buf_impl_t *db,
|
||||||
uint64_t object, dnode_handle_t *dnh)
|
uint64_t object, dnode_handle_t *dnh)
|
||||||
{
|
{
|
||||||
dnode_t *dn = kmem_cache_alloc(dnode_cache, KM_SLEEP);
|
dnode_t *dn;
|
||||||
|
|
||||||
|
dn = kmem_cache_alloc(dnode_cache, KM_SLEEP);
|
||||||
ASSERT(!POINTER_IS_VALID(dn->dn_objset));
|
ASSERT(!POINTER_IS_VALID(dn->dn_objset));
|
||||||
dn->dn_moved = 0;
|
dn->dn_moved = 0;
|
||||||
|
|
||||||
|
@ -443,13 +445,31 @@ dnode_create(objset_t *os, dnode_phys_t *dnp, dmu_buf_impl_t *db,
|
||||||
ASSERT(DMU_OT_IS_VALID(dn->dn_phys->dn_type));
|
ASSERT(DMU_OT_IS_VALID(dn->dn_phys->dn_type));
|
||||||
|
|
||||||
mutex_enter(&os->os_lock);
|
mutex_enter(&os->os_lock);
|
||||||
list_insert_head(&os->os_dnodes, dn);
|
if (dnh->dnh_dnode != NULL) {
|
||||||
membar_producer();
|
/* Lost the allocation race. */
|
||||||
|
mutex_exit(&os->os_lock);
|
||||||
|
kmem_cache_free(dnode_cache, dn);
|
||||||
|
return (dnh->dnh_dnode);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Everything else must be valid before assigning dn_objset makes the
|
* Exclude special dnodes from os_dnodes so an empty os_dnodes
|
||||||
* dnode eligible for dnode_move().
|
* signifies that the special dnodes have no references from
|
||||||
|
* their children (the entries in os_dnodes). This allows
|
||||||
|
* dnode_destroy() to easily determine if the last child has
|
||||||
|
* been removed and then complete eviction of the objset.
|
||||||
|
*/
|
||||||
|
if (!DMU_OBJECT_IS_SPECIAL(object))
|
||||||
|
list_insert_head(&os->os_dnodes, dn);
|
||||||
|
membar_producer();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Everything else must be valid before assigning dn_objset
|
||||||
|
* makes the dnode eligible for dnode_move().
|
||||||
*/
|
*/
|
||||||
dn->dn_objset = os;
|
dn->dn_objset = os;
|
||||||
|
|
||||||
|
dnh->dnh_dnode = dn;
|
||||||
mutex_exit(&os->os_lock);
|
mutex_exit(&os->os_lock);
|
||||||
|
|
||||||
arc_space_consume(sizeof (dnode_t), ARC_SPACE_OTHER);
|
arc_space_consume(sizeof (dnode_t), ARC_SPACE_OTHER);
|
||||||
|
@ -463,12 +483,18 @@ static void
|
||||||
dnode_destroy(dnode_t *dn)
|
dnode_destroy(dnode_t *dn)
|
||||||
{
|
{
|
||||||
objset_t *os = dn->dn_objset;
|
objset_t *os = dn->dn_objset;
|
||||||
|
boolean_t complete_os_eviction = B_FALSE;
|
||||||
|
|
||||||
ASSERT((dn->dn_id_flags & DN_ID_NEW_EXIST) == 0);
|
ASSERT((dn->dn_id_flags & DN_ID_NEW_EXIST) == 0);
|
||||||
|
|
||||||
mutex_enter(&os->os_lock);
|
mutex_enter(&os->os_lock);
|
||||||
POINTER_INVALIDATE(&dn->dn_objset);
|
POINTER_INVALIDATE(&dn->dn_objset);
|
||||||
list_remove(&os->os_dnodes, dn);
|
if (!DMU_OBJECT_IS_SPECIAL(dn->dn_object)) {
|
||||||
|
list_remove(&os->os_dnodes, dn);
|
||||||
|
complete_os_eviction =
|
||||||
|
list_is_empty(&os->os_dnodes) &&
|
||||||
|
list_link_active(&os->os_evicting_node);
|
||||||
|
}
|
||||||
mutex_exit(&os->os_lock);
|
mutex_exit(&os->os_lock);
|
||||||
|
|
||||||
/* the dnode can no longer move, so we can release the handle */
|
/* the dnode can no longer move, so we can release the handle */
|
||||||
|
@ -503,6 +529,9 @@ dnode_destroy(dnode_t *dn)
|
||||||
dmu_zfetch_rele(&dn->dn_zfetch);
|
dmu_zfetch_rele(&dn->dn_zfetch);
|
||||||
kmem_cache_free(dnode_cache, dn);
|
kmem_cache_free(dnode_cache, dn);
|
||||||
arc_space_return(sizeof (dnode_t), ARC_SPACE_OTHER);
|
arc_space_return(sizeof (dnode_t), ARC_SPACE_OTHER);
|
||||||
|
|
||||||
|
if (complete_os_eviction)
|
||||||
|
dmu_objset_evict_done(os);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -968,33 +997,32 @@ dnode_special_close(dnode_handle_t *dnh)
|
||||||
*/
|
*/
|
||||||
while (refcount_count(&dn->dn_holds) > 0)
|
while (refcount_count(&dn->dn_holds) > 0)
|
||||||
delay(1);
|
delay(1);
|
||||||
|
ASSERT(dn->dn_dbuf == NULL ||
|
||||||
|
dmu_buf_get_user(&dn->dn_dbuf->db) == NULL);
|
||||||
zrl_add(&dnh->dnh_zrlock);
|
zrl_add(&dnh->dnh_zrlock);
|
||||||
dnode_destroy(dn); /* implicit zrl_remove() */
|
dnode_destroy(dn); /* implicit zrl_remove() */
|
||||||
zrl_destroy(&dnh->dnh_zrlock);
|
zrl_destroy(&dnh->dnh_zrlock);
|
||||||
dnh->dnh_dnode = NULL;
|
dnh->dnh_dnode = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dnode_t *
|
void
|
||||||
dnode_special_open(objset_t *os, dnode_phys_t *dnp, uint64_t object,
|
dnode_special_open(objset_t *os, dnode_phys_t *dnp, uint64_t object,
|
||||||
dnode_handle_t *dnh)
|
dnode_handle_t *dnh)
|
||||||
{
|
{
|
||||||
dnode_t *dn = dnode_create(os, dnp, NULL, object, dnh);
|
dnode_t *dn;
|
||||||
dnh->dnh_dnode = dn;
|
|
||||||
|
dn = dnode_create(os, dnp, NULL, object, dnh);
|
||||||
zrl_init(&dnh->dnh_zrlock);
|
zrl_init(&dnh->dnh_zrlock);
|
||||||
DNODE_VERIFY(dn);
|
DNODE_VERIFY(dn);
|
||||||
return (dn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dnode_buf_pageout(dmu_buf_t *db, void *arg)
|
dnode_buf_pageout(void *dbu)
|
||||||
{
|
{
|
||||||
dnode_children_t *children_dnodes = arg;
|
dnode_children_t *children_dnodes = dbu;
|
||||||
int i;
|
int i;
|
||||||
int epb = db->db_size >> DNODE_SHIFT;
|
|
||||||
|
|
||||||
ASSERT(epb == children_dnodes->dnc_count);
|
for (i = 0; i < children_dnodes->dnc_count; i++) {
|
||||||
|
|
||||||
for (i = 0; i < epb; i++) {
|
|
||||||
dnode_handle_t *dnh = &children_dnodes->dnc_children[i];
|
dnode_handle_t *dnh = &children_dnodes->dnc_children[i];
|
||||||
dnode_t *dn;
|
dnode_t *dn;
|
||||||
|
|
||||||
|
@ -1024,7 +1052,7 @@ dnode_buf_pageout(dmu_buf_t *db, void *arg)
|
||||||
dnh->dnh_dnode = NULL;
|
dnh->dnh_dnode = NULL;
|
||||||
}
|
}
|
||||||
kmem_free(children_dnodes, sizeof (dnode_children_t) +
|
kmem_free(children_dnodes, sizeof (dnode_children_t) +
|
||||||
epb * sizeof (dnode_handle_t));
|
children_dnodes->dnc_count * sizeof (dnode_handle_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1108,16 +1136,17 @@ dnode_hold_impl(objset_t *os, uint64_t object, int flag,
|
||||||
if (children_dnodes == NULL) {
|
if (children_dnodes == NULL) {
|
||||||
int i;
|
int i;
|
||||||
dnode_children_t *winner;
|
dnode_children_t *winner;
|
||||||
children_dnodes = kmem_alloc(sizeof (dnode_children_t) +
|
children_dnodes = kmem_zalloc(sizeof (dnode_children_t) +
|
||||||
epb * sizeof (dnode_handle_t), KM_SLEEP);
|
epb * sizeof (dnode_handle_t), KM_SLEEP);
|
||||||
children_dnodes->dnc_count = epb;
|
children_dnodes->dnc_count = epb;
|
||||||
dnh = &children_dnodes->dnc_children[0];
|
dnh = &children_dnodes->dnc_children[0];
|
||||||
for (i = 0; i < epb; i++) {
|
for (i = 0; i < epb; i++) {
|
||||||
zrl_init(&dnh[i].dnh_zrlock);
|
zrl_init(&dnh[i].dnh_zrlock);
|
||||||
dnh[i].dnh_dnode = NULL;
|
|
||||||
}
|
}
|
||||||
if ((winner = dmu_buf_set_user(&db->db, children_dnodes,
|
dmu_buf_init_user(&children_dnodes->dnc_dbu,
|
||||||
dnode_buf_pageout))) {
|
dnode_buf_pageout, NULL);
|
||||||
|
winner = dmu_buf_set_user(&db->db, &children_dnodes->dnc_dbu);
|
||||||
|
if (winner != NULL) {
|
||||||
|
|
||||||
for (i = 0; i < epb; i++) {
|
for (i = 0; i < epb; i++) {
|
||||||
zrl_destroy(&dnh[i].dnh_zrlock);
|
zrl_destroy(&dnh[i].dnh_zrlock);
|
||||||
|
@ -1132,17 +1161,11 @@ dnode_hold_impl(objset_t *os, uint64_t object, int flag,
|
||||||
|
|
||||||
dnh = &children_dnodes->dnc_children[idx];
|
dnh = &children_dnodes->dnc_children[idx];
|
||||||
zrl_add(&dnh->dnh_zrlock);
|
zrl_add(&dnh->dnh_zrlock);
|
||||||
if ((dn = dnh->dnh_dnode) == NULL) {
|
dn = dnh->dnh_dnode;
|
||||||
|
if (dn == NULL) {
|
||||||
dnode_phys_t *phys = (dnode_phys_t *)db->db.db_data+idx;
|
dnode_phys_t *phys = (dnode_phys_t *)db->db.db_data+idx;
|
||||||
dnode_t *winner;
|
|
||||||
|
|
||||||
dn = dnode_create(os, phys, db, object, dnh);
|
dn = dnode_create(os, phys, db, object, dnh);
|
||||||
winner = atomic_cas_ptr(&dnh->dnh_dnode, NULL, dn);
|
|
||||||
if (winner != NULL) {
|
|
||||||
zrl_add(&dnh->dnh_zrlock);
|
|
||||||
dnode_destroy(dn); /* implicit zrl_remove() */
|
|
||||||
dn = winner;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_enter(&dn->dn_mtx);
|
mutex_enter(&dn->dn_mtx);
|
||||||
|
@ -1156,10 +1179,10 @@ dnode_hold_impl(objset_t *os, uint64_t object, int flag,
|
||||||
dbuf_rele(db, FTAG);
|
dbuf_rele(db, FTAG);
|
||||||
return (type == DMU_OT_NONE ? ENOENT : EEXIST);
|
return (type == DMU_OT_NONE ? ENOENT : EEXIST);
|
||||||
}
|
}
|
||||||
mutex_exit(&dn->dn_mtx);
|
|
||||||
|
|
||||||
if (refcount_add(&dn->dn_holds, tag) == 1)
|
if (refcount_add(&dn->dn_holds, tag) == 1)
|
||||||
dbuf_add_ref(db, dnh);
|
dbuf_add_ref(db, dnh);
|
||||||
|
mutex_exit(&dn->dn_mtx);
|
||||||
|
|
||||||
/* Now we can rely on the hold to prevent the dnode from moving. */
|
/* Now we can rely on the hold to prevent the dnode from moving. */
|
||||||
zrl_remove(&dnh->dnh_zrlock);
|
zrl_remove(&dnh->dnh_zrlock);
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
|
@ -402,53 +403,41 @@ dnode_sync_free_range(void *arg, uint64_t blkid, uint64_t nblks)
|
||||||
void
|
void
|
||||||
dnode_evict_dbufs(dnode_t *dn)
|
dnode_evict_dbufs(dnode_t *dn)
|
||||||
{
|
{
|
||||||
int progress;
|
dmu_buf_impl_t *db_marker;
|
||||||
int pass = 0;
|
dmu_buf_impl_t *db, *db_next;
|
||||||
|
|
||||||
do {
|
db_marker = kmem_alloc(sizeof (dmu_buf_impl_t), KM_SLEEP);
|
||||||
dmu_buf_impl_t *db, *db_next;
|
|
||||||
int evicting = FALSE;
|
mutex_enter(&dn->dn_dbufs_mtx);
|
||||||
|
for (db = avl_first(&dn->dn_dbufs); db != NULL; db = db_next) {
|
||||||
|
|
||||||
progress = FALSE;
|
|
||||||
mutex_enter(&dn->dn_dbufs_mtx);
|
|
||||||
for (db = avl_first(&dn->dn_dbufs); db != NULL; db = db_next) {
|
|
||||||
db_next = AVL_NEXT(&dn->dn_dbufs, db);
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
DB_DNODE_ENTER(db);
|
DB_DNODE_ENTER(db);
|
||||||
ASSERT3P(DB_DNODE(db), ==, dn);
|
ASSERT3P(DB_DNODE(db), ==, dn);
|
||||||
DB_DNODE_EXIT(db);
|
DB_DNODE_EXIT(db);
|
||||||
#endif /* DEBUG */
|
#endif /* DEBUG */
|
||||||
|
|
||||||
mutex_enter(&db->db_mtx);
|
mutex_enter(&db->db_mtx);
|
||||||
if (db->db_state == DB_EVICTING) {
|
if (db->db_state != DB_EVICTING &&
|
||||||
progress = TRUE;
|
refcount_is_zero(&db->db_holds)) {
|
||||||
evicting = TRUE;
|
db_marker->db_level = db->db_level;
|
||||||
mutex_exit(&db->db_mtx);
|
db_marker->db_blkid = db->db_blkid;
|
||||||
} else if (refcount_is_zero(&db->db_holds)) {
|
db_marker->db_state = DB_SEARCH;
|
||||||
progress = TRUE;
|
avl_insert_here(&dn->dn_dbufs, db_marker, db,
|
||||||
dbuf_clear(db); /* exits db_mtx for us */
|
AVL_BEFORE);
|
||||||
} else {
|
|
||||||
mutex_exit(&db->db_mtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
dbuf_clear(db);
|
||||||
|
|
||||||
|
db_next = AVL_NEXT(&dn->dn_dbufs, db_marker);
|
||||||
|
avl_remove(&dn->dn_dbufs, db_marker);
|
||||||
|
} else {
|
||||||
|
mutex_exit(&db->db_mtx);
|
||||||
|
db_next = AVL_NEXT(&dn->dn_dbufs, db);
|
||||||
}
|
}
|
||||||
/*
|
}
|
||||||
* NB: we need to drop dn_dbufs_mtx between passes so
|
mutex_exit(&dn->dn_dbufs_mtx);
|
||||||
* that any DB_EVICTING dbufs can make progress.
|
|
||||||
* Ideally, we would have some cv we could wait on, but
|
|
||||||
* since we don't, just wait a bit to give the other
|
|
||||||
* thread a chance to run.
|
|
||||||
*/
|
|
||||||
mutex_exit(&dn->dn_dbufs_mtx);
|
|
||||||
if (evicting)
|
|
||||||
delay(1);
|
|
||||||
pass++;
|
|
||||||
if ((pass % 100) == 0)
|
|
||||||
dprintf("Exceeded %d passes evicting dbufs\n", pass);
|
|
||||||
} while (progress);
|
|
||||||
|
|
||||||
if (pass >= 100)
|
kmem_free(db_marker, sizeof (dmu_buf_impl_t));
|
||||||
dprintf("Required %d passes to evict dbufs\n", pass);
|
|
||||||
|
|
||||||
dnode_evict_bonus(dn);
|
dnode_evict_bonus(dn);
|
||||||
}
|
}
|
||||||
|
@ -513,7 +502,6 @@ dnode_sync_free(dnode_t *dn, dmu_tx_t *tx)
|
||||||
dnode_undirty_dbufs(&dn->dn_dirty_records[txgoff]);
|
dnode_undirty_dbufs(&dn->dn_dirty_records[txgoff]);
|
||||||
dnode_evict_dbufs(dn);
|
dnode_evict_dbufs(dn);
|
||||||
ASSERT(avl_is_empty(&dn->dn_dbufs));
|
ASSERT(avl_is_empty(&dn->dn_dbufs));
|
||||||
ASSERT3P(dn->dn_bonus, ==, NULL);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX - It would be nice to assert this, but we may still
|
* XXX - It would be nice to assert this, but we may still
|
||||||
|
|
|
@ -120,7 +120,7 @@ dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name,
|
||||||
int error;
|
int error;
|
||||||
zfs_bookmark_phys_t bmark_phys;
|
zfs_bookmark_phys_t bmark_phys;
|
||||||
|
|
||||||
if (!dsl_dataset_is_snapshot(snapds))
|
if (!snapds->ds_is_snapshot)
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
|
||||||
error = dsl_bookmark_hold_ds(dp, bookmark_name,
|
error = dsl_bookmark_hold_ds(dp, bookmark_name,
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
||||||
* Copyright (c) 2014 RackTop Systems.
|
* Copyright (c) 2014 RackTop Systems.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/dmu_objset.h>
|
#include <sys/dmu_objset.h>
|
||||||
|
@ -62,7 +63,6 @@
|
||||||
#define DSL_DEADLIST_BLOCKSIZE SPA_MAXBLOCKSIZE
|
#define DSL_DEADLIST_BLOCKSIZE SPA_MAXBLOCKSIZE
|
||||||
|
|
||||||
extern inline dsl_dataset_phys_t *dsl_dataset_phys(dsl_dataset_t *ds);
|
extern inline dsl_dataset_phys_t *dsl_dataset_phys(dsl_dataset_t *ds);
|
||||||
extern inline boolean_t dsl_dataset_is_snapshot(dsl_dataset_t *ds);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Figure out how much of this delta should be propogated to the dsl_dir
|
* Figure out how much of this delta should be propogated to the dsl_dir
|
||||||
|
@ -146,7 +146,7 @@ dsl_dataset_block_kill(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx,
|
||||||
}
|
}
|
||||||
ASSERT3P(tx->tx_pool, ==, ds->ds_dir->dd_pool);
|
ASSERT3P(tx->tx_pool, ==, ds->ds_dir->dd_pool);
|
||||||
|
|
||||||
ASSERT(!dsl_dataset_is_snapshot(ds));
|
ASSERT(!ds->ds_is_snapshot);
|
||||||
dmu_buf_will_dirty(ds->ds_dbuf, tx);
|
dmu_buf_will_dirty(ds->ds_dbuf, tx);
|
||||||
|
|
||||||
if (bp->blk_birth > dsl_dataset_phys(ds)->ds_prev_snap_txg) {
|
if (bp->blk_birth > dsl_dataset_phys(ds)->ds_prev_snap_txg) {
|
||||||
|
@ -244,14 +244,15 @@ dsl_dataset_block_freeable(dsl_dataset_t *ds, const blkptr_t *bp,
|
||||||
return (B_TRUE);
|
return (B_TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ARGSUSED */
|
|
||||||
static void
|
static void
|
||||||
dsl_dataset_evict(dmu_buf_t *db, void *dsv)
|
dsl_dataset_evict(void *dbu)
|
||||||
{
|
{
|
||||||
dsl_dataset_t *ds = dsv;
|
dsl_dataset_t *ds = dbu;
|
||||||
|
|
||||||
ASSERT(ds->ds_owner == NULL);
|
ASSERT(ds->ds_owner == NULL);
|
||||||
|
|
||||||
|
ds->ds_dbuf = NULL;
|
||||||
|
|
||||||
unique_remove(ds->ds_fsid_guid);
|
unique_remove(ds->ds_fsid_guid);
|
||||||
|
|
||||||
if (ds->ds_objset != NULL)
|
if (ds->ds_objset != NULL)
|
||||||
|
@ -263,10 +264,10 @@ dsl_dataset_evict(dmu_buf_t *db, void *dsv)
|
||||||
}
|
}
|
||||||
|
|
||||||
bplist_destroy(&ds->ds_pending_deadlist);
|
bplist_destroy(&ds->ds_pending_deadlist);
|
||||||
if (dsl_dataset_phys(ds)->ds_deadlist_obj != 0)
|
if (ds->ds_deadlist.dl_os != NULL)
|
||||||
dsl_deadlist_close(&ds->ds_deadlist);
|
dsl_deadlist_close(&ds->ds_deadlist);
|
||||||
if (ds->ds_dir)
|
if (ds->ds_dir)
|
||||||
dsl_dir_rele(ds->ds_dir, ds);
|
dsl_dir_async_rele(ds->ds_dir, ds);
|
||||||
|
|
||||||
ASSERT(!list_link_active(&ds->ds_synced_link));
|
ASSERT(!list_link_active(&ds->ds_synced_link));
|
||||||
|
|
||||||
|
@ -380,6 +381,7 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||||
ds = kmem_zalloc(sizeof (dsl_dataset_t), KM_SLEEP);
|
ds = kmem_zalloc(sizeof (dsl_dataset_t), KM_SLEEP);
|
||||||
ds->ds_dbuf = dbuf;
|
ds->ds_dbuf = dbuf;
|
||||||
ds->ds_object = dsobj;
|
ds->ds_object = dsobj;
|
||||||
|
ds->ds_is_snapshot = dsl_dataset_phys(ds)->ds_num_children != 0;
|
||||||
list_link_init(&ds->ds_synced_link);
|
list_link_init(&ds->ds_synced_link);
|
||||||
|
|
||||||
mutex_init(&ds->ds_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&ds->ds_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
@ -411,7 +413,7 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||||
return (err);
|
return (err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dsl_dataset_is_snapshot(ds)) {
|
if (!ds->ds_is_snapshot) {
|
||||||
ds->ds_snapname[0] = '\0';
|
ds->ds_snapname[0] = '\0';
|
||||||
if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
|
if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
|
||||||
err = dsl_dataset_hold_obj(dp,
|
err = dsl_dataset_hold_obj(dp,
|
||||||
|
@ -438,7 +440,7 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err == 0 && !dsl_dataset_is_snapshot(ds)) {
|
if (err == 0 && !ds->ds_is_snapshot) {
|
||||||
err = dsl_prop_get_int_ds(ds,
|
err = dsl_prop_get_int_ds(ds,
|
||||||
zfs_prop_to_name(ZFS_PROP_REFRESERVATION),
|
zfs_prop_to_name(ZFS_PROP_REFRESERVATION),
|
||||||
&ds->ds_reserved);
|
&ds->ds_reserved);
|
||||||
|
@ -451,8 +453,11 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||||
ds->ds_reserved = ds->ds_quota = 0;
|
ds->ds_reserved = ds->ds_quota = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err != 0 || (winner = dmu_buf_set_user_ie(dbuf, ds,
|
dmu_buf_init_user(&ds->ds_dbu, dsl_dataset_evict, &ds->ds_dbuf);
|
||||||
dsl_dataset_evict)) != NULL) {
|
if (err == 0)
|
||||||
|
winner = dmu_buf_set_user_ie(dbuf, &ds->ds_dbu);
|
||||||
|
|
||||||
|
if (err != 0 || winner != NULL) {
|
||||||
bplist_destroy(&ds->ds_pending_deadlist);
|
bplist_destroy(&ds->ds_pending_deadlist);
|
||||||
dsl_deadlist_close(&ds->ds_deadlist);
|
dsl_deadlist_close(&ds->ds_deadlist);
|
||||||
if (ds->ds_prev)
|
if (ds->ds_prev)
|
||||||
|
@ -829,7 +834,7 @@ dsl_dataset_recalc_head_uniq(dsl_dataset_t *ds)
|
||||||
uint64_t mrs_used;
|
uint64_t mrs_used;
|
||||||
uint64_t dlused, dlcomp, dluncomp;
|
uint64_t dlused, dlcomp, dluncomp;
|
||||||
|
|
||||||
ASSERT(!dsl_dataset_is_snapshot(ds));
|
ASSERT(!ds->ds_is_snapshot);
|
||||||
|
|
||||||
if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0)
|
if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0)
|
||||||
mrs_used = dsl_dataset_phys(ds->ds_prev)->ds_referenced_bytes;
|
mrs_used = dsl_dataset_phys(ds->ds_prev)->ds_referenced_bytes;
|
||||||
|
@ -1579,7 +1584,7 @@ dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv)
|
||||||
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_LOGICALREFERENCED,
|
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_LOGICALREFERENCED,
|
||||||
dsl_dataset_phys(ds)->ds_uncompressed_bytes);
|
dsl_dataset_phys(ds)->ds_uncompressed_bytes);
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, ratio);
|
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, ratio);
|
||||||
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED,
|
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED,
|
||||||
dsl_dataset_phys(ds)->ds_unique_bytes);
|
dsl_dataset_phys(ds)->ds_unique_bytes);
|
||||||
|
@ -1643,7 +1648,7 @@ dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat)
|
||||||
dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT;
|
dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT;
|
||||||
stat->dds_guid = dsl_dataset_phys(ds)->ds_guid;
|
stat->dds_guid = dsl_dataset_phys(ds)->ds_guid;
|
||||||
stat->dds_origin[0] = '\0';
|
stat->dds_origin[0] = '\0';
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
stat->dds_is_snapshot = B_TRUE;
|
stat->dds_is_snapshot = B_TRUE;
|
||||||
stat->dds_num_clones =
|
stat->dds_num_clones =
|
||||||
dsl_dataset_phys(ds)->ds_num_children - 1;
|
dsl_dataset_phys(ds)->ds_num_children - 1;
|
||||||
|
@ -1920,7 +1925,7 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/* must not be a snapshot */
|
/* must not be a snapshot */
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
dsl_dataset_rele(ds, FTAG);
|
dsl_dataset_rele(ds, FTAG);
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
}
|
}
|
||||||
|
@ -2491,7 +2496,7 @@ promote_hold(dsl_dataset_promote_arg_t *ddpa, dsl_pool_t *dp, void *tag)
|
||||||
return (error);
|
return (error);
|
||||||
dd = ddpa->ddpa_clone->ds_dir;
|
dd = ddpa->ddpa_clone->ds_dir;
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ddpa->ddpa_clone) ||
|
if (ddpa->ddpa_clone->ds_is_snapshot ||
|
||||||
!dsl_dir_is_clone(dd)) {
|
!dsl_dir_is_clone(dd)) {
|
||||||
dsl_dataset_rele(ddpa->ddpa_clone, tag);
|
dsl_dataset_rele(ddpa->ddpa_clone, tag);
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
@ -2582,8 +2587,8 @@ dsl_dataset_clone_swap_check_impl(dsl_dataset_t *clone,
|
||||||
int64_t unused_refres_delta;
|
int64_t unused_refres_delta;
|
||||||
|
|
||||||
/* they should both be heads */
|
/* they should both be heads */
|
||||||
if (dsl_dataset_is_snapshot(clone) ||
|
if (clone->ds_is_snapshot ||
|
||||||
dsl_dataset_is_snapshot(origin_head))
|
origin_head->ds_is_snapshot)
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
|
||||||
/* if we are not forcing, the branch point should be just before them */
|
/* if we are not forcing, the branch point should be just before them */
|
||||||
|
@ -2862,7 +2867,7 @@ dsl_dataset_set_refquota_check(void *arg, dmu_tx_t *tx)
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
dsl_dataset_rele(ds, FTAG);
|
dsl_dataset_rele(ds, FTAG);
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
}
|
}
|
||||||
|
@ -2945,7 +2950,7 @@ dsl_dataset_set_refreservation_check(void *arg, dmu_tx_t *tx)
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
dsl_dataset_rele(ds, FTAG);
|
dsl_dataset_rele(ds, FTAG);
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
}
|
}
|
||||||
|
@ -3158,8 +3163,8 @@ dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap,
|
||||||
uint64_t snapobj;
|
uint64_t snapobj;
|
||||||
dsl_pool_t *dp = firstsnap->ds_dir->dd_pool;
|
dsl_pool_t *dp = firstsnap->ds_dir->dd_pool;
|
||||||
|
|
||||||
ASSERT(dsl_dataset_is_snapshot(firstsnap));
|
ASSERT(firstsnap->ds_is_snapshot);
|
||||||
ASSERT(dsl_dataset_is_snapshot(lastsnap));
|
ASSERT(lastsnap->ds_is_snapshot);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that the snapshots are in the same dsl_dir, and firstsnap
|
* Check that the snapshots are in the same dsl_dir, and firstsnap
|
||||||
|
@ -3214,12 +3219,12 @@ dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier,
|
||||||
dsl_dataset_t *origin;
|
dsl_dataset_t *origin;
|
||||||
|
|
||||||
ASSERT(dsl_pool_config_held(dp));
|
ASSERT(dsl_pool_config_held(dp));
|
||||||
ASSERT(dsl_dataset_is_snapshot(earlier) || earlier_txg != 0);
|
ASSERT(earlier->ds_is_snapshot || earlier_txg != 0);
|
||||||
|
|
||||||
if (earlier_txg == 0)
|
if (earlier_txg == 0)
|
||||||
earlier_txg = dsl_dataset_phys(earlier)->ds_creation_txg;
|
earlier_txg = dsl_dataset_phys(earlier)->ds_creation_txg;
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(later) &&
|
if (later->ds_is_snapshot &&
|
||||||
earlier_txg >= dsl_dataset_phys(later)->ds_creation_txg)
|
earlier_txg >= dsl_dataset_phys(later)->ds_creation_txg)
|
||||||
return (B_FALSE);
|
return (B_FALSE);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/dsl_dataset.h>
|
#include <sys/dsl_dataset.h>
|
||||||
|
@ -121,6 +122,8 @@ dsl_deadlist_close(dsl_deadlist_t *dl)
|
||||||
void *cookie = NULL;
|
void *cookie = NULL;
|
||||||
dsl_deadlist_entry_t *dle;
|
dsl_deadlist_entry_t *dle;
|
||||||
|
|
||||||
|
dl->dl_os = NULL;
|
||||||
|
|
||||||
if (dl->dl_oldfmt) {
|
if (dl->dl_oldfmt) {
|
||||||
dl->dl_oldfmt = B_FALSE;
|
dl->dl_oldfmt = B_FALSE;
|
||||||
bpobj_close(&dl->dl_bpobj);
|
bpobj_close(&dl->dl_bpobj);
|
||||||
|
|
|
@ -570,7 +570,7 @@ dsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr)
|
||||||
SPA_VERSION_DELEGATED_PERMS)
|
SPA_VERSION_DELEGATED_PERMS)
|
||||||
return (SET_ERROR(EPERM));
|
return (SET_ERROR(EPERM));
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
/*
|
/*
|
||||||
* Snapshots are treated as descendents only,
|
* Snapshots are treated as descendents only,
|
||||||
* local permissions do not apply.
|
* local permissions do not apply.
|
||||||
|
|
|
@ -51,7 +51,7 @@ typedef struct dmu_snapshots_destroy_arg {
|
||||||
int
|
int
|
||||||
dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)
|
dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)
|
||||||
{
|
{
|
||||||
if (!dsl_dataset_is_snapshot(ds))
|
if (!ds->ds_is_snapshot)
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
|
||||||
if (dsl_dataset_long_held(ds))
|
if (dsl_dataset_long_held(ds))
|
||||||
|
@ -356,7 +356,7 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
|
||||||
dsl_dataset_remove_clones_key(ds,
|
dsl_dataset_remove_clones_key(ds,
|
||||||
dsl_dataset_phys(ds)->ds_creation_txg, tx);
|
dsl_dataset_phys(ds)->ds_creation_txg, tx);
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds_next)) {
|
if (ds_next->ds_is_snapshot) {
|
||||||
dsl_dataset_t *ds_nextnext;
|
dsl_dataset_t *ds_nextnext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -605,8 +605,8 @@ dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds)
|
||||||
uint64_t count;
|
uint64_t count;
|
||||||
objset_t *mos;
|
objset_t *mos;
|
||||||
|
|
||||||
ASSERT(!dsl_dataset_is_snapshot(ds));
|
ASSERT(!ds->ds_is_snapshot);
|
||||||
if (dsl_dataset_is_snapshot(ds))
|
if (ds->ds_is_snapshot)
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
|
||||||
if (refcount_count(&ds->ds_longholds) != expected_holds)
|
if (refcount_count(&ds->ds_longholds) != expected_holds)
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2013 Martin Matuska. All rights reserved.
|
* Copyright (c) 2013 Martin Matuska. All rights reserved.
|
||||||
* Copyright (c) 2014 Joyent, Inc. All rights reserved.
|
* Copyright (c) 2014 Joyent, Inc. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/dmu.h>
|
#include <sys/dmu.h>
|
||||||
|
@ -126,14 +127,15 @@ extern inline dsl_dir_phys_t *dsl_dir_phys(dsl_dir_t *dd);
|
||||||
|
|
||||||
static uint64_t dsl_dir_space_towrite(dsl_dir_t *dd);
|
static uint64_t dsl_dir_space_towrite(dsl_dir_t *dd);
|
||||||
|
|
||||||
/* ARGSUSED */
|
|
||||||
static void
|
static void
|
||||||
dsl_dir_evict(dmu_buf_t *db, void *arg)
|
dsl_dir_evict(void *dbu)
|
||||||
{
|
{
|
||||||
dsl_dir_t *dd = arg;
|
dsl_dir_t *dd = dbu;
|
||||||
int t;
|
int t;
|
||||||
ASSERTV(dsl_pool_t *dp = dd->dd_pool);
|
ASSERTV(dsl_pool_t *dp = dd->dd_pool);
|
||||||
|
|
||||||
|
dd->dd_dbuf = NULL;
|
||||||
|
|
||||||
for (t = 0; t < TXG_SIZE; t++) {
|
for (t = 0; t < TXG_SIZE; t++) {
|
||||||
ASSERT(!txg_list_member(&dp->dp_dirty_dirs, dd, t));
|
ASSERT(!txg_list_member(&dp->dp_dirty_dirs, dd, t));
|
||||||
ASSERT(dd->dd_tempreserved[t] == 0);
|
ASSERT(dd->dd_tempreserved[t] == 0);
|
||||||
|
@ -141,9 +143,9 @@ dsl_dir_evict(dmu_buf_t *db, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dd->dd_parent)
|
if (dd->dd_parent)
|
||||||
dsl_dir_rele(dd->dd_parent, dd);
|
dsl_dir_async_rele(dd->dd_parent, dd);
|
||||||
|
|
||||||
spa_close(dd->dd_pool->dp_spa, dd);
|
spa_async_close(dd->dd_pool->dp_spa, dd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The props callback list should have been cleaned up by
|
* The props callback list should have been cleaned up by
|
||||||
|
@ -239,8 +241,9 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj,
|
||||||
dmu_buf_rele(origin_bonus, FTAG);
|
dmu_buf_rele(origin_bonus, FTAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
winner = dmu_buf_set_user_ie(dbuf, dd, dsl_dir_evict);
|
dmu_buf_init_user(&dd->dd_dbu, dsl_dir_evict, &dd->dd_dbuf);
|
||||||
if (winner) {
|
winner = dmu_buf_set_user_ie(dbuf, &dd->dd_dbu);
|
||||||
|
if (winner != NULL) {
|
||||||
if (dd->dd_parent)
|
if (dd->dd_parent)
|
||||||
dsl_dir_rele(dd->dd_parent, dd);
|
dsl_dir_rele(dd->dd_parent, dd);
|
||||||
mutex_destroy(&dd->dd_lock);
|
mutex_destroy(&dd->dd_lock);
|
||||||
|
@ -284,6 +287,21 @@ dsl_dir_rele(dsl_dir_t *dd, void *tag)
|
||||||
dmu_buf_rele(dd->dd_dbuf, tag);
|
dmu_buf_rele(dd->dd_dbuf, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a reference to the given dsl dir that is being asynchronously
|
||||||
|
* released. Async releases occur from a taskq performing eviction of
|
||||||
|
* dsl datasets and dirs. This process is identical to a normal release
|
||||||
|
* with the exception of using the async API for releasing the reference on
|
||||||
|
* the spa.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
dsl_dir_async_rele(dsl_dir_t *dd, void *tag)
|
||||||
|
{
|
||||||
|
dprintf_dd(dd, "%s\n", "");
|
||||||
|
spa_async_close(dd->dd_pool->dp_spa, tag);
|
||||||
|
dmu_buf_rele(dd->dd_dbuf, tag);
|
||||||
|
}
|
||||||
|
|
||||||
/* buf must be long enough (MAXNAMELEN + strlen(MOS_DIR_NAME) + 1 should do) */
|
/* buf must be long enough (MAXNAMELEN + strlen(MOS_DIR_NAME) + 1 should do) */
|
||||||
void
|
void
|
||||||
dsl_dir_name(dsl_dir_t *dd, char *buf)
|
dsl_dir_name(dsl_dir_t *dd, char *buf)
|
||||||
|
@ -417,7 +435,7 @@ dsl_dir_hold(dsl_pool_t *dp, const char *name, void *tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
while (next != NULL) {
|
while (next != NULL) {
|
||||||
dsl_dir_t *child_ds;
|
dsl_dir_t *child_dd;
|
||||||
err = getcomponent(next, buf, &nextnext);
|
err = getcomponent(next, buf, &nextnext);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
break;
|
break;
|
||||||
|
@ -436,11 +454,11 @@ dsl_dir_hold(dsl_pool_t *dp, const char *name, void *tag,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dsl_dir_hold_obj(dp, ddobj, buf, tag, &child_ds);
|
err = dsl_dir_hold_obj(dp, ddobj, buf, tag, &child_dd);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
break;
|
break;
|
||||||
dsl_dir_rele(dd, tag);
|
dsl_dir_rele(dd, tag);
|
||||||
dd = child_ds;
|
dd = child_dd;
|
||||||
next = nextnext;
|
next = nextnext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/dsl_pool.h>
|
#include <sys/dsl_pool.h>
|
||||||
|
@ -319,6 +320,8 @@ dsl_pool_close(dsl_pool_t *dp)
|
||||||
arc_flush(dp->dp_spa);
|
arc_flush(dp->dp_spa);
|
||||||
txg_fini(dp);
|
txg_fini(dp);
|
||||||
dsl_scan_fini(dp);
|
dsl_scan_fini(dp);
|
||||||
|
dmu_buf_user_evict_wait();
|
||||||
|
|
||||||
rrw_destroy(&dp->dp_config_rwlock);
|
rrw_destroy(&dp->dp_config_rwlock);
|
||||||
mutex_destroy(&dp->dp_lock);
|
mutex_destroy(&dp->dp_lock);
|
||||||
taskq_destroy(dp->dp_iput_taskq);
|
taskq_destroy(dp->dp_iput_taskq);
|
||||||
|
|
|
@ -163,19 +163,17 @@ dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
|
||||||
{
|
{
|
||||||
zfs_prop_t prop = zfs_name_to_prop(propname);
|
zfs_prop_t prop = zfs_name_to_prop(propname);
|
||||||
boolean_t inheritable;
|
boolean_t inheritable;
|
||||||
boolean_t snapshot;
|
|
||||||
uint64_t zapobj;
|
uint64_t zapobj;
|
||||||
|
|
||||||
ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool));
|
ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool));
|
||||||
inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
|
inheritable = (prop == ZPROP_INVAL || zfs_prop_inheritable(prop));
|
||||||
snapshot = dsl_dataset_is_snapshot(ds);
|
|
||||||
zapobj = dsl_dataset_phys(ds)->ds_props_obj;
|
zapobj = dsl_dataset_phys(ds)->ds_props_obj;
|
||||||
|
|
||||||
if (zapobj != 0) {
|
if (zapobj != 0) {
|
||||||
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
|
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
ASSERT(snapshot);
|
ASSERT(ds->ds_is_snapshot);
|
||||||
|
|
||||||
/* Check for a local value. */
|
/* Check for a local value. */
|
||||||
err = zap_lookup(mos, zapobj, propname, intsz, numints, buf);
|
err = zap_lookup(mos, zapobj, propname, intsz, numints, buf);
|
||||||
|
@ -215,7 +213,7 @@ dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
|
||||||
}
|
}
|
||||||
|
|
||||||
return (dsl_prop_get_dd(ds->ds_dir, propname,
|
return (dsl_prop_get_dd(ds->ds_dir, propname,
|
||||||
intsz, numints, buf, setpoint, snapshot));
|
intsz, numints, buf, setpoint, ds->ds_is_snapshot));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -545,7 +543,7 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,
|
||||||
|
|
||||||
isint = (dodefault(propname, 8, 1, &intval) == 0);
|
isint = (dodefault(propname, 8, 1, &intval) == 0);
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
ASSERT(version >= SPA_VERSION_SNAP_PROPS);
|
ASSERT(version >= SPA_VERSION_SNAP_PROPS);
|
||||||
if (dsl_dataset_phys(ds)->ds_props_obj == 0) {
|
if (dsl_dataset_phys(ds)->ds_props_obj == 0) {
|
||||||
dmu_buf_will_dirty(ds->ds_dbuf, tx);
|
dmu_buf_will_dirty(ds->ds_dbuf, tx);
|
||||||
|
@ -642,7 +640,7 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,
|
||||||
if (isint) {
|
if (isint) {
|
||||||
VERIFY0(dsl_prop_get_int_ds(ds, propname, &intval));
|
VERIFY0(dsl_prop_get_int_ds(ds, propname, &intval));
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
dsl_prop_cb_record_t *cbr;
|
dsl_prop_cb_record_t *cbr;
|
||||||
/*
|
/*
|
||||||
* It's a snapshot; nothing can inherit this
|
* It's a snapshot; nothing can inherit this
|
||||||
|
@ -760,7 +758,7 @@ dsl_props_set_check(void *arg, dmu_tx_t *tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds) && version < SPA_VERSION_SNAP_PROPS) {
|
if (ds->ds_is_snapshot && version < SPA_VERSION_SNAP_PROPS) {
|
||||||
dsl_dataset_rele(ds, FTAG);
|
dsl_dataset_rele(ds, FTAG);
|
||||||
return (SET_ERROR(ENOTSUP));
|
return (SET_ERROR(ENOTSUP));
|
||||||
}
|
}
|
||||||
|
@ -983,7 +981,7 @@ dsl_prop_get_all_ds(dsl_dataset_t *ds, nvlist_t **nvp,
|
||||||
|
|
||||||
VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds))
|
if (ds->ds_is_snapshot)
|
||||||
flags |= DSL_PROP_GET_SNAPSHOT;
|
flags |= DSL_PROP_GET_SNAPSHOT;
|
||||||
|
|
||||||
ASSERT(dsl_pool_config_held(dp));
|
ASSERT(dsl_pool_config_held(dp));
|
||||||
|
|
|
@ -415,7 +415,7 @@ static uint64_t
|
||||||
dsl_scan_ds_maxtxg(dsl_dataset_t *ds)
|
dsl_scan_ds_maxtxg(dsl_dataset_t *ds)
|
||||||
{
|
{
|
||||||
uint64_t smt = ds->ds_dir->dd_pool->dp_scan->scn_phys.scn_max_txg;
|
uint64_t smt = ds->ds_dir->dd_pool->dp_scan->scn_phys.scn_max_txg;
|
||||||
if (dsl_dataset_is_snapshot(ds))
|
if (ds->ds_is_snapshot)
|
||||||
return (MIN(smt, dsl_dataset_phys(ds)->ds_creation_txg));
|
return (MIN(smt, dsl_dataset_phys(ds)->ds_creation_txg));
|
||||||
return (smt);
|
return (smt);
|
||||||
}
|
}
|
||||||
|
@ -846,7 +846,7 @@ dsl_scan_ds_destroyed(dsl_dataset_t *ds, dmu_tx_t *tx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (scn->scn_phys.scn_bookmark.zb_objset == ds->ds_object) {
|
if (scn->scn_phys.scn_bookmark.zb_objset == ds->ds_object) {
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
/* Note, scn_cur_{min,max}_txg stays the same. */
|
/* Note, scn_cur_{min,max}_txg stays the same. */
|
||||||
scn->scn_phys.scn_bookmark.zb_objset =
|
scn->scn_phys.scn_bookmark.zb_objset =
|
||||||
dsl_dataset_phys(ds)->ds_next_snap_obj;
|
dsl_dataset_phys(ds)->ds_next_snap_obj;
|
||||||
|
@ -868,7 +868,7 @@ dsl_scan_ds_destroyed(dsl_dataset_t *ds, dmu_tx_t *tx)
|
||||||
ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);
|
ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);
|
||||||
VERIFY3U(0, ==, zap_remove_int(dp->dp_meta_objset,
|
VERIFY3U(0, ==, zap_remove_int(dp->dp_meta_objset,
|
||||||
scn->scn_phys.scn_queue_obj, ds->ds_object, tx));
|
scn->scn_phys.scn_queue_obj, ds->ds_object, tx));
|
||||||
if (dsl_dataset_is_snapshot(ds)) {
|
if (ds->ds_is_snapshot) {
|
||||||
/*
|
/*
|
||||||
* We keep the same mintxg; it could be >
|
* We keep the same mintxg; it could be >
|
||||||
* ds_creation_txg if the previous snapshot was
|
* ds_creation_txg if the previous snapshot was
|
||||||
|
@ -1053,7 +1053,7 @@ dsl_scan_visitds(dsl_scan_t *scn, uint64_t dsobj, dmu_tx_t *tx)
|
||||||
* ZIL here, rather than in scan_recurse(), because the regular
|
* ZIL here, rather than in scan_recurse(), because the regular
|
||||||
* snapshot block-sharing rules don't apply to it.
|
* snapshot block-sharing rules don't apply to it.
|
||||||
*/
|
*/
|
||||||
if (DSL_SCAN_IS_SCRUB_RESILVER(scn) && !dsl_dataset_is_snapshot(ds))
|
if (DSL_SCAN_IS_SCRUB_RESILVER(scn) && !ds->ds_is_snapshot)
|
||||||
dsl_scan_zil(dp, &os->os_zil_header);
|
dsl_scan_zil(dp, &os->os_zil_header);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -354,7 +354,7 @@ dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura,
|
||||||
objset_t *mos;
|
objset_t *mos;
|
||||||
int numholds;
|
int numholds;
|
||||||
|
|
||||||
if (!dsl_dataset_is_snapshot(ds))
|
if (!ds->ds_is_snapshot)
|
||||||
return (SET_ERROR(EINVAL));
|
return (SET_ERROR(EINVAL));
|
||||||
|
|
||||||
if (nvlist_empty(holds))
|
if (nvlist_empty(holds))
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
|
@ -209,6 +210,7 @@ sa_cache_constructor(void *buf, void *unused, int kmflag)
|
||||||
{
|
{
|
||||||
sa_handle_t *hdl = buf;
|
sa_handle_t *hdl = buf;
|
||||||
|
|
||||||
|
hdl->sa_dbu.dbu_evict_func = NULL;
|
||||||
hdl->sa_bonus_tab = NULL;
|
hdl->sa_bonus_tab = NULL;
|
||||||
hdl->sa_spill_tab = NULL;
|
hdl->sa_spill_tab = NULL;
|
||||||
hdl->sa_os = NULL;
|
hdl->sa_os = NULL;
|
||||||
|
@ -224,6 +226,7 @@ static void
|
||||||
sa_cache_destructor(void *buf, void *unused)
|
sa_cache_destructor(void *buf, void *unused)
|
||||||
{
|
{
|
||||||
sa_handle_t *hdl = buf;
|
sa_handle_t *hdl = buf;
|
||||||
|
hdl->sa_dbu.dbu_evict_func = NULL;
|
||||||
mutex_destroy(&hdl->sa_lock);
|
mutex_destroy(&hdl->sa_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1306,10 +1309,10 @@ sa_build_index(sa_handle_t *hdl, sa_buf_type_t buftype)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*ARGSUSED*/
|
/*ARGSUSED*/
|
||||||
void
|
static void
|
||||||
sa_evict(dmu_buf_t *db, void *sap)
|
sa_evict(void *dbu)
|
||||||
{
|
{
|
||||||
panic("evicting sa dbuf %p\n", (void *)db);
|
panic("evicting sa dbuf\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1361,9 +1364,10 @@ sa_spill_rele(sa_handle_t *hdl)
|
||||||
void
|
void
|
||||||
sa_handle_destroy(sa_handle_t *hdl)
|
sa_handle_destroy(sa_handle_t *hdl)
|
||||||
{
|
{
|
||||||
|
dmu_buf_t *db = hdl->sa_bonus;
|
||||||
|
|
||||||
mutex_enter(&hdl->sa_lock);
|
mutex_enter(&hdl->sa_lock);
|
||||||
(void) dmu_buf_update_user((dmu_buf_t *)hdl->sa_bonus, hdl,
|
(void) dmu_buf_remove_user(db, &hdl->sa_dbu);
|
||||||
NULL, NULL);
|
|
||||||
|
|
||||||
if (hdl->sa_bonus_tab) {
|
if (hdl->sa_bonus_tab) {
|
||||||
sa_idx_tab_rele(hdl->sa_os, hdl->sa_bonus_tab);
|
sa_idx_tab_rele(hdl->sa_os, hdl->sa_bonus_tab);
|
||||||
|
@ -1388,7 +1392,7 @@ sa_handle_get_from_db(objset_t *os, dmu_buf_t *db, void *userp,
|
||||||
sa_handle_type_t hdl_type, sa_handle_t **handlepp)
|
sa_handle_type_t hdl_type, sa_handle_t **handlepp)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
sa_handle_t *handle;
|
sa_handle_t *handle = NULL;
|
||||||
#ifdef ZFS_DEBUG
|
#ifdef ZFS_DEBUG
|
||||||
dmu_object_info_t doi;
|
dmu_object_info_t doi;
|
||||||
|
|
||||||
|
@ -1399,9 +1403,12 @@ sa_handle_get_from_db(objset_t *os, dmu_buf_t *db, void *userp,
|
||||||
/* find handle, if it exists */
|
/* find handle, if it exists */
|
||||||
/* if one doesn't exist then create a new one, and initialize it */
|
/* 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) {
|
if (handle == NULL) {
|
||||||
sa_handle_t *newhandle;
|
sa_handle_t *winner = NULL;
|
||||||
|
|
||||||
handle = kmem_cache_alloc(sa_cache, KM_SLEEP);
|
handle = kmem_cache_alloc(sa_cache, KM_SLEEP);
|
||||||
handle->sa_userp = userp;
|
handle->sa_userp = userp;
|
||||||
handle->sa_bonus = db;
|
handle->sa_bonus = db;
|
||||||
|
@ -1409,12 +1416,15 @@ sa_handle_get_from_db(objset_t *os, dmu_buf_t *db, void *userp,
|
||||||
handle->sa_spill = NULL;
|
handle->sa_spill = NULL;
|
||||||
|
|
||||||
error = sa_build_index(handle, SA_BONUS);
|
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);
|
kmem_cache_free(sa_cache, handle);
|
||||||
handle = newhandle;
|
handle = winner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*handlepp = handle;
|
*handlepp = handle;
|
||||||
|
@ -1946,14 +1956,6 @@ sa_object_size(sa_handle_t *hdl, uint32_t *blksize, u_longlong_t *nblocks)
|
||||||
blksize, nblocks);
|
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
|
void
|
||||||
sa_set_userp(sa_handle_t *hdl, void *ptr)
|
sa_set_userp(sa_handle_t *hdl, void *ptr)
|
||||||
{
|
{
|
||||||
|
@ -2052,7 +2054,6 @@ EXPORT_SYMBOL(sa_size);
|
||||||
EXPORT_SYMBOL(sa_update_from_cb);
|
EXPORT_SYMBOL(sa_update_from_cb);
|
||||||
EXPORT_SYMBOL(sa_object_info);
|
EXPORT_SYMBOL(sa_object_info);
|
||||||
EXPORT_SYMBOL(sa_object_size);
|
EXPORT_SYMBOL(sa_object_size);
|
||||||
EXPORT_SYMBOL(sa_update_user);
|
|
||||||
EXPORT_SYMBOL(sa_get_userdata);
|
EXPORT_SYMBOL(sa_get_userdata);
|
||||||
EXPORT_SYMBOL(sa_set_userp);
|
EXPORT_SYMBOL(sa_set_userp);
|
||||||
EXPORT_SYMBOL(sa_get_db);
|
EXPORT_SYMBOL(sa_get_db);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2013, 2014, Nexenta Systems, Inc. All rights reserved.
|
* Copyright (c) 2013, 2014, Nexenta Systems, Inc. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1095,6 +1096,8 @@ spa_activate(spa_t *spa, int mode)
|
||||||
|
|
||||||
list_create(&spa->spa_config_dirty_list, sizeof (vdev_t),
|
list_create(&spa->spa_config_dirty_list, sizeof (vdev_t),
|
||||||
offsetof(vdev_t, vdev_config_dirty_node));
|
offsetof(vdev_t, vdev_config_dirty_node));
|
||||||
|
list_create(&spa->spa_evicting_os_list, sizeof (objset_t),
|
||||||
|
offsetof(objset_t, os_evicting_node));
|
||||||
list_create(&spa->spa_state_dirty_list, sizeof (vdev_t),
|
list_create(&spa->spa_state_dirty_list, sizeof (vdev_t),
|
||||||
offsetof(vdev_t, vdev_state_dirty_node));
|
offsetof(vdev_t, vdev_state_dirty_node));
|
||||||
|
|
||||||
|
@ -1123,9 +1126,12 @@ spa_deactivate(spa_t *spa)
|
||||||
ASSERT(spa->spa_async_zio_root == NULL);
|
ASSERT(spa->spa_async_zio_root == NULL);
|
||||||
ASSERT(spa->spa_state != POOL_STATE_UNINITIALIZED);
|
ASSERT(spa->spa_state != POOL_STATE_UNINITIALIZED);
|
||||||
|
|
||||||
|
spa_evicting_os_wait(spa);
|
||||||
|
|
||||||
txg_list_destroy(&spa->spa_vdev_txg_list);
|
txg_list_destroy(&spa->spa_vdev_txg_list);
|
||||||
|
|
||||||
list_destroy(&spa->spa_config_dirty_list);
|
list_destroy(&spa->spa_config_dirty_list);
|
||||||
|
list_destroy(&spa->spa_evicting_os_list);
|
||||||
list_destroy(&spa->spa_state_dirty_list);
|
list_destroy(&spa->spa_state_dirty_list);
|
||||||
|
|
||||||
taskq_cancel_id(system_taskq, spa->spa_deadman_tqid);
|
taskq_cancel_id(system_taskq, spa->spa_deadman_tqid);
|
||||||
|
@ -2136,6 +2142,11 @@ spa_load(spa_t *spa, spa_load_state_t state, spa_import_type_t type,
|
||||||
mosconfig, &ereport);
|
mosconfig, &ereport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't count references from objsets that are already closed
|
||||||
|
* and are making their way through the eviction process.
|
||||||
|
*/
|
||||||
|
spa_evicting_os_wait(spa);
|
||||||
spa->spa_minref = refcount_count(&spa->spa_refcount);
|
spa->spa_minref = refcount_count(&spa->spa_refcount);
|
||||||
if (error) {
|
if (error) {
|
||||||
if (error != EEXIST) {
|
if (error != EEXIST) {
|
||||||
|
@ -3775,6 +3786,11 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
|
||||||
|
|
||||||
spa_history_log_version(spa, "create");
|
spa_history_log_version(spa, "create");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't count references from objsets that are already closed
|
||||||
|
* and are making their way through the eviction process.
|
||||||
|
*/
|
||||||
|
spa_evicting_os_wait(spa);
|
||||||
spa->spa_minref = refcount_count(&spa->spa_refcount);
|
spa->spa_minref = refcount_count(&spa->spa_refcount);
|
||||||
|
|
||||||
mutex_exit(&spa_namespace_lock);
|
mutex_exit(&spa_namespace_lock);
|
||||||
|
@ -4314,8 +4330,10 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig,
|
||||||
* modify its state. Objsets may be open only because they're dirty,
|
* modify its state. Objsets may be open only because they're dirty,
|
||||||
* so we have to force it to sync before checking spa_refcnt.
|
* so we have to force it to sync before checking spa_refcnt.
|
||||||
*/
|
*/
|
||||||
if (spa->spa_sync_on)
|
if (spa->spa_sync_on) {
|
||||||
txg_wait_synced(spa->spa_dsl_pool, 0);
|
txg_wait_synced(spa->spa_dsl_pool, 0);
|
||||||
|
spa_evicting_os_wait(spa);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A pool cannot be exported or destroyed if there are active
|
* A pool cannot be exported or destroyed if there are active
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
||||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
|
@ -524,6 +525,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot)
|
||||||
mutex_init(&spa->spa_async_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&spa->spa_async_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
mutex_init(&spa->spa_errlist_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&spa->spa_errlist_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
mutex_init(&spa->spa_errlog_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&spa->spa_errlog_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
mutex_init(&spa->spa_evicting_os_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
mutex_init(&spa->spa_history_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&spa->spa_history_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
mutex_init(&spa->spa_proc_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&spa->spa_proc_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
mutex_init(&spa->spa_props_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&spa->spa_props_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
@ -533,6 +535,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot)
|
||||||
mutex_init(&spa->spa_feat_stats_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&spa->spa_feat_stats_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
|
||||||
cv_init(&spa->spa_async_cv, NULL, CV_DEFAULT, NULL);
|
cv_init(&spa->spa_async_cv, NULL, CV_DEFAULT, NULL);
|
||||||
|
cv_init(&spa->spa_evicting_os_cv, NULL, CV_DEFAULT, NULL);
|
||||||
cv_init(&spa->spa_proc_cv, NULL, CV_DEFAULT, NULL);
|
cv_init(&spa->spa_proc_cv, NULL, CV_DEFAULT, NULL);
|
||||||
cv_init(&spa->spa_scrub_io_cv, NULL, CV_DEFAULT, NULL);
|
cv_init(&spa->spa_scrub_io_cv, NULL, CV_DEFAULT, NULL);
|
||||||
cv_init(&spa->spa_suspend_cv, NULL, CV_DEFAULT, NULL);
|
cv_init(&spa->spa_suspend_cv, NULL, CV_DEFAULT, NULL);
|
||||||
|
@ -619,6 +622,7 @@ spa_remove(spa_t *spa)
|
||||||
|
|
||||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||||
ASSERT(spa->spa_state == POOL_STATE_UNINITIALIZED);
|
ASSERT(spa->spa_state == POOL_STATE_UNINITIALIZED);
|
||||||
|
ASSERT3U(refcount_count(&spa->spa_refcount), ==, 0);
|
||||||
|
|
||||||
nvlist_free(spa->spa_config_splitting);
|
nvlist_free(spa->spa_config_splitting);
|
||||||
|
|
||||||
|
@ -651,6 +655,7 @@ spa_remove(spa_t *spa)
|
||||||
bplist_destroy(&spa->spa_free_bplist[t]);
|
bplist_destroy(&spa->spa_free_bplist[t]);
|
||||||
|
|
||||||
cv_destroy(&spa->spa_async_cv);
|
cv_destroy(&spa->spa_async_cv);
|
||||||
|
cv_destroy(&spa->spa_evicting_os_cv);
|
||||||
cv_destroy(&spa->spa_proc_cv);
|
cv_destroy(&spa->spa_proc_cv);
|
||||||
cv_destroy(&spa->spa_scrub_io_cv);
|
cv_destroy(&spa->spa_scrub_io_cv);
|
||||||
cv_destroy(&spa->spa_suspend_cv);
|
cv_destroy(&spa->spa_suspend_cv);
|
||||||
|
@ -658,6 +663,7 @@ spa_remove(spa_t *spa)
|
||||||
mutex_destroy(&spa->spa_async_lock);
|
mutex_destroy(&spa->spa_async_lock);
|
||||||
mutex_destroy(&spa->spa_errlist_lock);
|
mutex_destroy(&spa->spa_errlist_lock);
|
||||||
mutex_destroy(&spa->spa_errlog_lock);
|
mutex_destroy(&spa->spa_errlog_lock);
|
||||||
|
mutex_destroy(&spa->spa_evicting_os_lock);
|
||||||
mutex_destroy(&spa->spa_history_lock);
|
mutex_destroy(&spa->spa_history_lock);
|
||||||
mutex_destroy(&spa->spa_proc_lock);
|
mutex_destroy(&spa->spa_proc_lock);
|
||||||
mutex_destroy(&spa->spa_props_lock);
|
mutex_destroy(&spa->spa_props_lock);
|
||||||
|
@ -714,6 +720,20 @@ spa_close(spa_t *spa, void *tag)
|
||||||
(void) refcount_remove(&spa->spa_refcount, tag);
|
(void) refcount_remove(&spa->spa_refcount, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a reference to the given spa_t held by a dsl dir that is
|
||||||
|
* being asynchronously released. Async releases occur from a taskq
|
||||||
|
* performing eviction of dsl datasets and dirs. The namespace lock
|
||||||
|
* isn't held and the hold by the object being evicted may contribute to
|
||||||
|
* spa_minref (e.g. dataset or directory released during pool export),
|
||||||
|
* so the asserts in spa_close() do not apply.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
spa_async_close(spa_t *spa, void *tag)
|
||||||
|
{
|
||||||
|
(void) refcount_remove(&spa->spa_refcount, tag);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check to see if the spa refcount is zero. Must be called with
|
* Check to see if the spa refcount is zero. Must be called with
|
||||||
* spa_namespace_lock held. We really compare against spa_minref, which is the
|
* spa_namespace_lock held. We really compare against spa_minref, which is the
|
||||||
|
@ -1597,6 +1617,34 @@ spa_log_class(spa_t *spa)
|
||||||
return (spa->spa_log_class);
|
return (spa->spa_log_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spa_evicting_os_register(spa_t *spa, objset_t *os)
|
||||||
|
{
|
||||||
|
mutex_enter(&spa->spa_evicting_os_lock);
|
||||||
|
list_insert_head(&spa->spa_evicting_os_list, os);
|
||||||
|
mutex_exit(&spa->spa_evicting_os_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spa_evicting_os_deregister(spa_t *spa, objset_t *os)
|
||||||
|
{
|
||||||
|
mutex_enter(&spa->spa_evicting_os_lock);
|
||||||
|
list_remove(&spa->spa_evicting_os_list, os);
|
||||||
|
cv_broadcast(&spa->spa_evicting_os_cv);
|
||||||
|
mutex_exit(&spa->spa_evicting_os_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spa_evicting_os_wait(spa_t *spa)
|
||||||
|
{
|
||||||
|
mutex_enter(&spa->spa_evicting_os_lock);
|
||||||
|
while (!list_is_empty(&spa->spa_evicting_os_list))
|
||||||
|
cv_wait(&spa->spa_evicting_os_cv, &spa->spa_evicting_os_lock);
|
||||||
|
mutex_exit(&spa->spa_evicting_os_lock);
|
||||||
|
|
||||||
|
dmu_buf_user_evict_wait();
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
spa_max_replication(spa_t *spa)
|
spa_max_replication(spa_t *spa)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -52,7 +53,6 @@ int fzap_default_block_shift = 14; /* 16k blocksize */
|
||||||
|
|
||||||
extern inline zap_phys_t *zap_f_phys(zap_t *zap);
|
extern inline zap_phys_t *zap_f_phys(zap_t *zap);
|
||||||
|
|
||||||
static void zap_leaf_pageout(dmu_buf_t *db, void *vl);
|
|
||||||
static uint64_t zap_allocate_blocks(zap_t *zap, int nblocks);
|
static uint64_t zap_allocate_blocks(zap_t *zap, int nblocks);
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -81,7 +81,7 @@ fzap_upgrade(zap_t *zap, dmu_tx_t *tx, zap_flags_t flags)
|
||||||
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
|
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
|
||||||
zap->zap_ismicro = FALSE;
|
zap->zap_ismicro = FALSE;
|
||||||
|
|
||||||
(void) dmu_buf_update_user(zap->zap_dbuf, zap, zap, zap_evict);
|
zap->zap_dbu.dbu_evict_func = zap_evict;
|
||||||
|
|
||||||
mutex_init(&zap->zap_f.zap_num_entries_mtx, 0, 0, 0);
|
mutex_init(&zap->zap_f.zap_num_entries_mtx, 0, 0, 0);
|
||||||
zap->zap_f.zap_block_shift = highbit64(zap->zap_dbuf->db_size) - 1;
|
zap->zap_f.zap_block_shift = highbit64(zap->zap_dbuf->db_size) - 1;
|
||||||
|
@ -387,11 +387,20 @@ zap_allocate_blocks(zap_t *zap, int nblocks)
|
||||||
return (newblk);
|
return (newblk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
zap_leaf_pageout(void *dbu)
|
||||||
|
{
|
||||||
|
zap_leaf_t *l = dbu;
|
||||||
|
|
||||||
|
rw_destroy(&l->l_rwlock);
|
||||||
|
kmem_free(l, sizeof (zap_leaf_t));
|
||||||
|
}
|
||||||
|
|
||||||
static zap_leaf_t *
|
static zap_leaf_t *
|
||||||
zap_create_leaf(zap_t *zap, dmu_tx_t *tx)
|
zap_create_leaf(zap_t *zap, dmu_tx_t *tx)
|
||||||
{
|
{
|
||||||
void *winner;
|
void *winner;
|
||||||
zap_leaf_t *l = kmem_alloc(sizeof (zap_leaf_t), KM_SLEEP);
|
zap_leaf_t *l = kmem_zalloc(sizeof (zap_leaf_t), KM_SLEEP);
|
||||||
|
|
||||||
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
|
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
|
||||||
|
|
||||||
|
@ -403,7 +412,8 @@ zap_create_leaf(zap_t *zap, dmu_tx_t *tx)
|
||||||
VERIFY(0 == dmu_buf_hold(zap->zap_objset, zap->zap_object,
|
VERIFY(0 == dmu_buf_hold(zap->zap_objset, zap->zap_object,
|
||||||
l->l_blkid << FZAP_BLOCK_SHIFT(zap), NULL, &l->l_dbuf,
|
l->l_blkid << FZAP_BLOCK_SHIFT(zap), NULL, &l->l_dbuf,
|
||||||
DMU_READ_NO_PREFETCH));
|
DMU_READ_NO_PREFETCH));
|
||||||
winner = dmu_buf_set_user(l->l_dbuf, l, zap_leaf_pageout);
|
dmu_buf_init_user(&l->l_dbu, zap_leaf_pageout, &l->l_dbuf);
|
||||||
|
winner = dmu_buf_set_user(l->l_dbuf, &l->l_dbu);
|
||||||
ASSERT(winner == NULL);
|
ASSERT(winner == NULL);
|
||||||
dmu_buf_will_dirty(l->l_dbuf, tx);
|
dmu_buf_will_dirty(l->l_dbuf, tx);
|
||||||
|
|
||||||
|
@ -435,16 +445,6 @@ zap_put_leaf(zap_leaf_t *l)
|
||||||
dmu_buf_rele(l->l_dbuf, NULL);
|
dmu_buf_rele(l->l_dbuf, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
_NOTE(ARGSUSED(0))
|
|
||||||
static void
|
|
||||||
zap_leaf_pageout(dmu_buf_t *db, void *vl)
|
|
||||||
{
|
|
||||||
zap_leaf_t *l = vl;
|
|
||||||
|
|
||||||
rw_destroy(&l->l_rwlock);
|
|
||||||
kmem_free(l, sizeof (zap_leaf_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static zap_leaf_t *
|
static zap_leaf_t *
|
||||||
zap_open_leaf(uint64_t blkid, dmu_buf_t *db)
|
zap_open_leaf(uint64_t blkid, dmu_buf_t *db)
|
||||||
{
|
{
|
||||||
|
@ -452,19 +452,20 @@ zap_open_leaf(uint64_t blkid, dmu_buf_t *db)
|
||||||
|
|
||||||
ASSERT(blkid != 0);
|
ASSERT(blkid != 0);
|
||||||
|
|
||||||
l = kmem_alloc(sizeof (zap_leaf_t), KM_SLEEP);
|
l = kmem_zalloc(sizeof (zap_leaf_t), KM_SLEEP);
|
||||||
rw_init(&l->l_rwlock, NULL, RW_DEFAULT, NULL);
|
rw_init(&l->l_rwlock, NULL, RW_DEFAULT, NULL);
|
||||||
rw_enter(&l->l_rwlock, RW_WRITER);
|
rw_enter(&l->l_rwlock, RW_WRITER);
|
||||||
l->l_blkid = blkid;
|
l->l_blkid = blkid;
|
||||||
l->l_bs = highbit64(db->db_size) - 1;
|
l->l_bs = highbit64(db->db_size) - 1;
|
||||||
l->l_dbuf = db;
|
l->l_dbuf = db;
|
||||||
|
|
||||||
winner = dmu_buf_set_user(db, l, zap_leaf_pageout);
|
dmu_buf_init_user(&l->l_dbu, zap_leaf_pageout, &l->l_dbuf);
|
||||||
|
winner = dmu_buf_set_user(db, &l->l_dbu);
|
||||||
|
|
||||||
rw_exit(&l->l_rwlock);
|
rw_exit(&l->l_rwlock);
|
||||||
if (winner != NULL) {
|
if (winner != NULL) {
|
||||||
/* someone else set it first */
|
/* someone else set it first */
|
||||||
zap_leaf_pageout(NULL, l);
|
zap_leaf_pageout(&l->l_dbu);
|
||||||
l = winner;
|
l = winner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
|
||||||
|
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/zio.h>
|
#include <sys/zio.h>
|
||||||
|
@ -386,7 +387,8 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db)
|
||||||
* it, because zap_lockdir() checks zap_ismicro without the lock
|
* it, because zap_lockdir() checks zap_ismicro without the lock
|
||||||
* held.
|
* held.
|
||||||
*/
|
*/
|
||||||
winner = dmu_buf_set_user(db, zap, zap_evict);
|
dmu_buf_init_user(&zap->zap_dbu, zap_evict, &zap->zap_dbuf);
|
||||||
|
winner = dmu_buf_set_user(db, &zap->zap_dbu);
|
||||||
|
|
||||||
if (winner != NULL) {
|
if (winner != NULL) {
|
||||||
rw_exit(&zap->zap_rwlock);
|
rw_exit(&zap->zap_rwlock);
|
||||||
|
@ -675,11 +677,10 @@ zap_destroy(objset_t *os, uint64_t zapobj, dmu_tx_t *tx)
|
||||||
return (dmu_object_free(os, zapobj, tx));
|
return (dmu_object_free(os, zapobj, tx));
|
||||||
}
|
}
|
||||||
|
|
||||||
_NOTE(ARGSUSED(0))
|
|
||||||
void
|
void
|
||||||
zap_evict(dmu_buf_t *db, void *vzap)
|
zap_evict(void *dbu)
|
||||||
{
|
{
|
||||||
zap_t *zap = vzap;
|
zap_t *zap = dbu;
|
||||||
|
|
||||||
rw_destroy(&zap->zap_rwlock);
|
rw_destroy(&zap->zap_rwlock);
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,7 @@
|
||||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/zfs_context.h>
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/vnode.h>
|
#include <sys/vnode.h>
|
||||||
#include <sys/sa.h>
|
#include <sys/sa.h>
|
||||||
#include <sys/zfs_acl.h>
|
#include <sys/zfs_acl.h>
|
||||||
|
|
|
@ -497,7 +497,7 @@ zilog_dirty(zilog_t *zilog, uint64_t txg)
|
||||||
dsl_pool_t *dp = zilog->zl_dmu_pool;
|
dsl_pool_t *dp = zilog->zl_dmu_pool;
|
||||||
dsl_dataset_t *ds = dmu_objset_ds(zilog->zl_os);
|
dsl_dataset_t *ds = dmu_objset_ds(zilog->zl_os);
|
||||||
|
|
||||||
if (dsl_dataset_is_snapshot(ds))
|
if (ds->ds_is_snapshot)
|
||||||
panic("dirtying snapshot!");
|
panic("dirtying snapshot!");
|
||||||
|
|
||||||
if (txg_list_add(&dp->dp_dirty_zilogs, zilog, txg)) {
|
if (txg_list_add(&dp->dp_dirty_zilogs, zilog, txg)) {
|
||||||
|
|
Loading…
Reference in New Issue