diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index f0a0b116ab..f649bba4c8 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -808,6 +808,10 @@ dsl_dir_tempreserve_clear(void *tr_cookie, dmu_tx_t *tx) * or free space, for example when dirtying data. Be conservative; it's okay * to write less space or free more, but we don't want to write more or free * less than the amount specified. + * + * NOTE: The behavior of this function is identical to the Illumos / FreeBSD + * version however it has been adjusted to use an iterative rather then + * recursive algorithm to minimize stack usage. */ void dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx) @@ -815,20 +819,22 @@ dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx) int64_t parent_space; uint64_t est_used; - mutex_enter(&dd->dd_lock); - if (space > 0) - dd->dd_space_towrite[tx->tx_txg & TXG_MASK] += space; + do { + mutex_enter(&dd->dd_lock); + if (space > 0) + dd->dd_space_towrite[tx->tx_txg & TXG_MASK] += space; - est_used = dsl_dir_space_towrite(dd) + dd->dd_phys->dd_used_bytes; - parent_space = parent_delta(dd, est_used, space); - mutex_exit(&dd->dd_lock); + est_used = dsl_dir_space_towrite(dd) + + dd->dd_phys->dd_used_bytes; + parent_space = parent_delta(dd, est_used, space); + mutex_exit(&dd->dd_lock); - /* Make sure that we clean up dd_space_to* */ - dsl_dir_dirty(dd, tx); + /* Make sure that we clean up dd_space_to* */ + dsl_dir_dirty(dd, tx); - /* XXX this is potentially expensive and unnecessary... */ - if (parent_space && dd->dd_parent) - dsl_dir_willuse_space(dd->dd_parent, parent_space, tx); + dd = dd->dd_parent; + space = parent_space; + } while (space && dd); } /* call from syncing context when we actually write/free space for this dd */