Add fast path for zfs_ioc_space_snaps() handling of empty_bpobj

When there are many snapshots, calls to zfs_ioc_space_snaps() (e.g. from
`zfs destroy -nv pool/fs@snap1%snap10000`) can be very slow, resulting
in poor performance because we are holding the dp_config_rwlock the
entire time, blocking spa_sync() from continuing.  With around ten
thousand snapshots, we've seen up to 500 seconds in this ioctl,
iterating over up to 50,000,000 bpobjs, ~99% of which are the empty
bpobj.

By creating a fast path for zfs_ioc_space_snaps() handling of the
empty_bpobj, we can achieve a ~5x performance improvement of this ioctl
(when there are many snapshots, and the deadlist is mostly
empty_bpobj's).

Reviewed-by: Pavel Zakharov <pavel.zakharov@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
External-issue: DLPX-58348
Closes #8744
This commit is contained in:
Matthew Ahrens
2019-08-20 11:34:52 -07:00
committed by Brian Behlendorf
parent 3beb0a7694
commit 325d288c5d
3 changed files with 180 additions and 34 deletions
+13 -1
View File
@@ -48,8 +48,10 @@ typedef struct dsl_deadlist_phys {
typedef struct dsl_deadlist {
objset_t *dl_os;
uint64_t dl_object;
avl_tree_t dl_tree;
avl_tree_t dl_tree; /* contains dsl_deadlist_entry_t */
avl_tree_t dl_cache; /* contains dsl_deadlist_cache_entry_t */
boolean_t dl_havetree;
boolean_t dl_havecache;
struct dmu_buf *dl_dbuf;
dsl_deadlist_phys_t *dl_phys;
kmutex_t dl_lock;
@@ -59,6 +61,15 @@ typedef struct dsl_deadlist {
boolean_t dl_oldfmt;
} dsl_deadlist_t;
typedef struct dsl_deadlist_cache_entry {
avl_node_t dlce_node;
uint64_t dlce_mintxg;
uint64_t dlce_bpobj;
uint64_t dlce_bytes;
uint64_t dlce_comp;
uint64_t dlce_uncomp;
} dsl_deadlist_cache_entry_t;
typedef struct dsl_deadlist_entry {
avl_node_t dle_node;
uint64_t dle_mintxg;
@@ -108,6 +119,7 @@ int dsl_process_sub_livelist(bpobj_t *bpobj, struct bplist *to_free,
zthr_t *t, uint64_t *size);
void dsl_deadlist_clear_entry(dsl_deadlist_entry_t *dle, dsl_deadlist_t *dl,
dmu_tx_t *tx);
void dsl_deadlist_discard_tree(dsl_deadlist_t *dl);
#ifdef __cplusplus
}