From 1149ba6478a498de65d6a8b9a289e45015a1409c Mon Sep 17 00:00:00 2001 From: George Melikov Date: Thu, 26 Jan 2017 23:46:02 +0300 Subject: [PATCH] OpenZFS 7606 - dmu_objset_find_dp() takes a long time while importing pool When importing a pool with a large number of filesystems within the same parent filesystem, we see that dmu_objset_find_dp() takes a long time. It is called from 3 places: spa_check_logs(), spa_ld_claim_log_blocks(), and spa_load_verify(). There are several ways to improve performance here: 1. We don't really need to do spa_check_logs() or spa_ld_claim_log_blocks() if the pool was closed cleanly. 2. spa_load_verify() uses dmu_objset_find_dp() to check that no datasets have too long of names. 3. dmu_objset_find_dp() is slow because it's doing zap_value_search() (which is O(N sibling datasets)) to determine the name of each dsl_dir when it's opened. In this case we actually know the name when we are opening it, so we can provide it and avoid the lookup. This change implements fix #3 from the above list; i.e. make dmu_objset_find_dp() provide the name of the dataset so that we don't have to search for it. Authored by: Matthew Ahrens Reviewed by: Steve Gonczi Reviewed by: George Wilson Reviewed by: Prashanth Sreenivasa Reviewed-by: David Quigley Reviewed-by: Brian Behlendorf Ported-by: George Melikov OpenZFS-issue: https://www.illumos.org/issues/7606 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/cac6bab Closes #5662 --- module/zfs/dmu_objset.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index add4417ac339..47e1b2d08930 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -1927,6 +1927,7 @@ typedef struct dmu_objset_find_ctx { taskq_t *dc_tq; dsl_pool_t *dc_dp; uint64_t dc_ddobj; + char *dc_ddname; /* last component of ddobj's name */ int (*dc_func)(dsl_pool_t *, dsl_dataset_t *, void *); void *dc_arg; int dc_flags; @@ -1950,7 +1951,12 @@ dmu_objset_find_dp_impl(dmu_objset_find_ctx_t *dcp) if (*dcp->dc_error != 0) goto out; - err = dsl_dir_hold_obj(dp, dcp->dc_ddobj, NULL, FTAG, &dd); + /* + * Note: passing the name (dc_ddname) here is optional, but it + * improves performance because we don't need to call + * zap_value_search() to determine the name. + */ + err = dsl_dir_hold_obj(dp, dcp->dc_ddobj, dcp->dc_ddname, FTAG, &dd); if (err != 0) goto out; @@ -1975,9 +1981,11 @@ dmu_objset_find_dp_impl(dmu_objset_find_ctx_t *dcp) sizeof (uint64_t)); ASSERT3U(attr->za_num_integers, ==, 1); - child_dcp = kmem_alloc(sizeof (*child_dcp), KM_SLEEP); + child_dcp = + kmem_alloc(sizeof (*child_dcp), KM_SLEEP); *child_dcp = *dcp; child_dcp->dc_ddobj = attr->za_first_integer; + child_dcp->dc_ddname = spa_strdup(attr->za_name); if (dcp->dc_tq != NULL) (void) taskq_dispatch(dcp->dc_tq, dmu_objset_find_dp_cb, child_dcp, TQ_SLEEP); @@ -2020,16 +2028,25 @@ dmu_objset_find_dp_impl(dmu_objset_find_ctx_t *dcp) } } - dsl_dir_rele(dd, FTAG); kmem_free(attr, sizeof (zap_attribute_t)); - if (err != 0) + if (err != 0) { + dsl_dir_rele(dd, FTAG); goto out; + } /* * Apply to self. */ err = dsl_dataset_hold_obj(dp, thisobj, FTAG, &ds); + + /* + * Note: we hold the dir while calling dsl_dataset_hold_obj() so + * that the dir will remain cached, and we won't have to re-instantiate + * it (which could be expensive due to finding its name via + * zap_value_search()). + */ + dsl_dir_rele(dd, FTAG); if (err != 0) goto out; err = dcp->dc_func(dp, ds, dcp->dc_arg); @@ -2044,6 +2061,8 @@ dmu_objset_find_dp_impl(dmu_objset_find_ctx_t *dcp) mutex_exit(dcp->dc_error_lock); } + if (dcp->dc_ddname != NULL) + spa_strfree(dcp->dc_ddname); kmem_free(dcp, sizeof (*dcp)); } @@ -2088,6 +2107,7 @@ dmu_objset_find_dp(dsl_pool_t *dp, uint64_t ddobj, dcp->dc_tq = NULL; dcp->dc_dp = dp; dcp->dc_ddobj = ddobj; + dcp->dc_ddname = NULL; dcp->dc_func = func; dcp->dc_arg = arg; dcp->dc_flags = flags;