Path: blob/main/sys/contrib/openzfs/module/zfs/dsl_destroy.c
48383 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.23* Copyright (c) 2012, 2018 by Delphix. All rights reserved.24* Copyright (c) 2013 Steven Hartland. All rights reserved.25* Copyright (c) 2013 by Joyent, Inc. All rights reserved.26* Copyright (c) 2016 Actifio, Inc. All rights reserved.27*/2829#include <sys/zfs_context.h>30#include <sys/dsl_userhold.h>31#include <sys/dsl_dataset.h>32#include <sys/dsl_synctask.h>33#include <sys/dsl_destroy.h>34#include <sys/dsl_bookmark.h>35#include <sys/dmu_tx.h>36#include <sys/dsl_pool.h>37#include <sys/dsl_dir.h>38#include <sys/dmu_traverse.h>39#include <sys/dsl_scan.h>40#include <sys/dmu_objset.h>41#include <sys/zap.h>42#include <sys/zfeature.h>43#include <sys/zfs_ioctl.h>44#include <sys/dsl_deleg.h>45#include <sys/dmu_impl.h>46#include <sys/zvol.h>47#include <sys/zcp.h>48#include <sys/dsl_deadlist.h>49#include <sys/zthr.h>50#include <sys/spa_impl.h>5152extern int zfs_snapshot_history_enabled;5354int55dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer)56{57if (!ds->ds_is_snapshot)58return (SET_ERROR(EINVAL));5960if (dsl_dataset_long_held(ds))61return (SET_ERROR(EBUSY));6263/*64* Only allow deferred destroy on pools that support it.65* NOTE: deferred destroy is only supported on snapshots.66*/67if (defer) {68if (spa_version(ds->ds_dir->dd_pool->dp_spa) <69SPA_VERSION_USERREFS)70return (SET_ERROR(ENOTSUP));71return (0);72}7374/*75* If this snapshot has an elevated user reference count,76* we can't destroy it yet.77*/78if (ds->ds_userrefs > 0)79return (SET_ERROR(EBUSY));8081/*82* Can't delete a branch point.83*/84if (dsl_dataset_phys(ds)->ds_num_children > 1)85return (SET_ERROR(EEXIST));8687return (0);88}8990int91dsl_destroy_snapshot_check(void *arg, dmu_tx_t *tx)92{93dsl_destroy_snapshot_arg_t *ddsa = arg;94const char *dsname = ddsa->ddsa_name;95boolean_t defer = ddsa->ddsa_defer;9697dsl_pool_t *dp = dmu_tx_pool(tx);98int error = 0;99dsl_dataset_t *ds;100101error = dsl_dataset_hold(dp, dsname, FTAG, &ds);102103/*104* If the snapshot does not exist, silently ignore it, and105* dsl_destroy_snapshot_sync() will be a no-op106* (it's "already destroyed").107*/108if (error == ENOENT)109return (0);110111if (error == 0) {112error = dsl_destroy_snapshot_check_impl(ds, defer);113dsl_dataset_rele(ds, FTAG);114}115116return (error);117}118119struct process_old_arg {120dsl_dataset_t *ds;121dsl_dataset_t *ds_prev;122boolean_t after_branch_point;123zio_t *pio;124uint64_t used, comp, uncomp;125};126127static int128process_old_cb(void *arg, const blkptr_t *bp, boolean_t bp_freed, dmu_tx_t *tx)129{130struct process_old_arg *poa = arg;131dsl_pool_t *dp = poa->ds->ds_dir->dd_pool;132133ASSERT(!BP_IS_HOLE(bp));134135if (BP_GET_BIRTH(bp) <=136dsl_dataset_phys(poa->ds)->ds_prev_snap_txg) {137dsl_deadlist_insert(&poa->ds->ds_deadlist, bp, bp_freed, tx);138if (poa->ds_prev && !poa->after_branch_point &&139BP_GET_BIRTH(bp) >140dsl_dataset_phys(poa->ds_prev)->ds_prev_snap_txg) {141dsl_dataset_phys(poa->ds_prev)->ds_unique_bytes +=142bp_get_dsize_sync(dp->dp_spa, bp);143}144} else {145poa->used += bp_get_dsize_sync(dp->dp_spa, bp);146poa->comp += BP_GET_PSIZE(bp);147poa->uncomp += BP_GET_UCSIZE(bp);148dsl_free_sync(poa->pio, dp, tx->tx_txg, bp);149}150return (0);151}152153static void154process_old_deadlist(dsl_dataset_t *ds, dsl_dataset_t *ds_prev,155dsl_dataset_t *ds_next, boolean_t after_branch_point, dmu_tx_t *tx)156{157struct process_old_arg poa = { 0 };158dsl_pool_t *dp = ds->ds_dir->dd_pool;159objset_t *mos = dp->dp_meta_objset;160uint64_t deadlist_obj;161162ASSERT(ds->ds_deadlist.dl_oldfmt);163ASSERT(ds_next->ds_deadlist.dl_oldfmt);164165poa.ds = ds;166poa.ds_prev = ds_prev;167poa.after_branch_point = after_branch_point;168poa.pio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);169VERIFY0(bpobj_iterate(&ds_next->ds_deadlist.dl_bpobj,170process_old_cb, &poa, tx));171VERIFY0(zio_wait(poa.pio));172ASSERT3U(poa.used, ==, dsl_dataset_phys(ds)->ds_unique_bytes);173174/* change snapused */175dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP,176-poa.used, -poa.comp, -poa.uncomp, tx);177178/* swap next's deadlist to our deadlist */179dsl_deadlist_close(&ds->ds_deadlist);180dsl_deadlist_close(&ds_next->ds_deadlist);181deadlist_obj = dsl_dataset_phys(ds)->ds_deadlist_obj;182dsl_dataset_phys(ds)->ds_deadlist_obj =183dsl_dataset_phys(ds_next)->ds_deadlist_obj;184dsl_dataset_phys(ds_next)->ds_deadlist_obj = deadlist_obj;185VERIFY0(dsl_deadlist_open(&ds->ds_deadlist, mos,186dsl_dataset_phys(ds)->ds_deadlist_obj));187VERIFY0(dsl_deadlist_open(&ds_next->ds_deadlist, mos,188dsl_dataset_phys(ds_next)->ds_deadlist_obj));189}190191typedef struct remaining_clones_key {192dsl_dataset_t *rck_clone;193list_node_t rck_node;194} remaining_clones_key_t;195196static remaining_clones_key_t *197rck_alloc(dsl_dataset_t *clone)198{199remaining_clones_key_t *rck = kmem_alloc(sizeof (*rck), KM_SLEEP);200rck->rck_clone = clone;201return (rck);202}203204static void205dsl_dir_remove_clones_key_impl(dsl_dir_t *dd, uint64_t mintxg, dmu_tx_t *tx,206list_t *stack, const void *tag)207{208objset_t *mos = dd->dd_pool->dp_meta_objset;209210/*211* If it is the old version, dd_clones doesn't exist so we can't212* find the clones, but dsl_deadlist_remove_key() is a no-op so it213* doesn't matter.214*/215if (dsl_dir_phys(dd)->dd_clones == 0)216return;217218zap_cursor_t *zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP);219zap_attribute_t *za = zap_attribute_alloc();220221for (zap_cursor_init(zc, mos, dsl_dir_phys(dd)->dd_clones);222zap_cursor_retrieve(zc, za) == 0;223zap_cursor_advance(zc)) {224dsl_dataset_t *clone;225226VERIFY0(dsl_dataset_hold_obj(dd->dd_pool,227za->za_first_integer, tag, &clone));228229if (clone->ds_dir->dd_origin_txg > mintxg) {230dsl_deadlist_remove_key(&clone->ds_deadlist,231mintxg, tx);232233if (dsl_dataset_remap_deadlist_exists(clone)) {234dsl_deadlist_remove_key(235&clone->ds_remap_deadlist, mintxg, tx);236}237238list_insert_head(stack, rck_alloc(clone));239} else {240dsl_dataset_rele(clone, tag);241}242}243zap_cursor_fini(zc);244245zap_attribute_free(za);246kmem_free(zc, sizeof (zap_cursor_t));247}248249void250dsl_dir_remove_clones_key(dsl_dir_t *top_dd, uint64_t mintxg, dmu_tx_t *tx)251{252list_t stack;253254list_create(&stack, sizeof (remaining_clones_key_t),255offsetof(remaining_clones_key_t, rck_node));256257dsl_dir_remove_clones_key_impl(top_dd, mintxg, tx, &stack, FTAG);258for (remaining_clones_key_t *rck = list_remove_head(&stack);259rck != NULL; rck = list_remove_head(&stack)) {260dsl_dataset_t *clone = rck->rck_clone;261dsl_dir_t *clone_dir = clone->ds_dir;262263kmem_free(rck, sizeof (*rck));264265dsl_dir_remove_clones_key_impl(clone_dir, mintxg, tx,266&stack, FTAG);267dsl_dataset_rele(clone, FTAG);268}269270list_destroy(&stack);271}272273static void274dsl_destroy_snapshot_handle_remaps(dsl_dataset_t *ds, dsl_dataset_t *ds_next,275dmu_tx_t *tx)276{277dsl_pool_t *dp = ds->ds_dir->dd_pool;278279/* Move blocks to be obsoleted to pool's obsolete list. */280if (dsl_dataset_remap_deadlist_exists(ds_next)) {281if (!bpobj_is_open(&dp->dp_obsolete_bpobj))282dsl_pool_create_obsolete_bpobj(dp, tx);283284dsl_deadlist_move_bpobj(&ds_next->ds_remap_deadlist,285&dp->dp_obsolete_bpobj,286dsl_dataset_phys(ds)->ds_prev_snap_txg, tx);287}288289/* Merge our deadlist into next's and free it. */290if (dsl_dataset_remap_deadlist_exists(ds)) {291uint64_t remap_deadlist_object =292dsl_dataset_get_remap_deadlist_object(ds);293ASSERT(remap_deadlist_object != 0);294295mutex_enter(&ds_next->ds_remap_deadlist_lock);296if (!dsl_dataset_remap_deadlist_exists(ds_next))297dsl_dataset_create_remap_deadlist(ds_next, tx);298mutex_exit(&ds_next->ds_remap_deadlist_lock);299300dsl_deadlist_merge(&ds_next->ds_remap_deadlist,301remap_deadlist_object, tx);302dsl_dataset_destroy_remap_deadlist(ds, tx);303}304}305306void307dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)308{309int after_branch_point = FALSE;310dsl_pool_t *dp = ds->ds_dir->dd_pool;311objset_t *mos = dp->dp_meta_objset;312dsl_dataset_t *ds_prev = NULL;313uint64_t obj;314315ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));316rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);317ASSERT3U(BP_GET_BIRTH(&dsl_dataset_phys(ds)->ds_bp), <=, tx->tx_txg);318rrw_exit(&ds->ds_bp_rwlock, FTAG);319ASSERT(zfs_refcount_is_zero(&ds->ds_longholds));320321if (defer &&322(ds->ds_userrefs > 0 ||323dsl_dataset_phys(ds)->ds_num_children > 1)) {324ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS);325dmu_buf_will_dirty(ds->ds_dbuf, tx);326dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_DEFER_DESTROY;327if (zfs_snapshot_history_enabled) {328spa_history_log_internal_ds(ds, "defer_destroy", tx,329" ");330}331return;332}333334ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);335336if (zfs_snapshot_history_enabled) {337/* We need to log before removing it from the namespace. */338spa_history_log_internal_ds(ds, "destroy", tx, " ");339}340341dsl_scan_ds_destroyed(ds, tx);342343obj = ds->ds_object;344345boolean_t book_exists = dsl_bookmark_ds_destroyed(ds, tx);346347for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {348if (dsl_dataset_feature_is_active(ds, f))349dsl_dataset_deactivate_feature(ds, f, tx);350}351if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {352ASSERT0P(ds->ds_prev);353VERIFY0(dsl_dataset_hold_obj(dp,354dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &ds_prev));355after_branch_point =356(dsl_dataset_phys(ds_prev)->ds_next_snap_obj != obj);357358dmu_buf_will_dirty(ds_prev->ds_dbuf, tx);359if (after_branch_point &&360dsl_dataset_phys(ds_prev)->ds_next_clones_obj != 0) {361dsl_dataset_remove_from_next_clones(ds_prev, obj, tx);362if (dsl_dataset_phys(ds)->ds_next_snap_obj != 0) {363VERIFY0(zap_add_int(mos,364dsl_dataset_phys(ds_prev)->365ds_next_clones_obj,366dsl_dataset_phys(ds)->ds_next_snap_obj,367tx));368}369}370if (!after_branch_point) {371dsl_dataset_phys(ds_prev)->ds_next_snap_obj =372dsl_dataset_phys(ds)->ds_next_snap_obj;373}374}375376dsl_dataset_t *ds_next;377uint64_t old_unique;378uint64_t used = 0, comp = 0, uncomp = 0;379380VERIFY0(dsl_dataset_hold_obj(dp,381dsl_dataset_phys(ds)->ds_next_snap_obj, FTAG, &ds_next));382ASSERT3U(dsl_dataset_phys(ds_next)->ds_prev_snap_obj, ==, obj);383384old_unique = dsl_dataset_phys(ds_next)->ds_unique_bytes;385386dmu_buf_will_dirty(ds_next->ds_dbuf, tx);387dsl_dataset_phys(ds_next)->ds_prev_snap_obj =388dsl_dataset_phys(ds)->ds_prev_snap_obj;389dsl_dataset_phys(ds_next)->ds_prev_snap_txg =390dsl_dataset_phys(ds)->ds_prev_snap_txg;391ASSERT3U(dsl_dataset_phys(ds)->ds_prev_snap_txg, ==,392ds_prev ? dsl_dataset_phys(ds_prev)->ds_creation_txg : 0);393394if (ds_next->ds_deadlist.dl_oldfmt) {395process_old_deadlist(ds, ds_prev, ds_next,396after_branch_point, tx);397} else {398/* Adjust prev's unique space. */399if (ds_prev && !after_branch_point) {400dsl_deadlist_space_range(&ds_next->ds_deadlist,401dsl_dataset_phys(ds_prev)->ds_prev_snap_txg,402dsl_dataset_phys(ds)->ds_prev_snap_txg,403&used, &comp, &uncomp);404dsl_dataset_phys(ds_prev)->ds_unique_bytes += used;405}406407/* Adjust snapused. */408dsl_deadlist_space_range(&ds_next->ds_deadlist,409dsl_dataset_phys(ds)->ds_prev_snap_txg, UINT64_MAX,410&used, &comp, &uncomp);411dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP,412-used, -comp, -uncomp, tx);413414/* Move blocks to be freed to pool's free list. */415dsl_deadlist_move_bpobj(&ds_next->ds_deadlist,416&dp->dp_free_bpobj, dsl_dataset_phys(ds)->ds_prev_snap_txg,417tx);418dsl_dir_diduse_space(tx->tx_pool->dp_free_dir,419DD_USED_HEAD, used, comp, uncomp, tx);420421/* Merge our deadlist into next's and free it. */422dsl_deadlist_merge(&ds_next->ds_deadlist,423dsl_dataset_phys(ds)->ds_deadlist_obj, tx);424425/*426* We are done with the deadlist tree (generated/used427* by dsl_deadlist_move_bpobj() and dsl_deadlist_merge()).428* Discard it to save memory.429*/430dsl_deadlist_discard_tree(&ds_next->ds_deadlist);431}432433dsl_deadlist_close(&ds->ds_deadlist);434dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx);435dmu_buf_will_dirty(ds->ds_dbuf, tx);436dsl_dataset_phys(ds)->ds_deadlist_obj = 0;437438dsl_destroy_snapshot_handle_remaps(ds, ds_next, tx);439440if (!book_exists) {441/* Collapse range in clone heads */442dsl_dir_remove_clones_key(ds->ds_dir,443dsl_dataset_phys(ds)->ds_creation_txg, tx);444}445446if (ds_next->ds_is_snapshot) {447dsl_dataset_t *ds_nextnext;448449/*450* Update next's unique to include blocks which451* were previously shared by only this snapshot452* and it. Those blocks will be born after the453* prev snap and before this snap, and will have454* died after the next snap and before the one455* after that (ie. be on the snap after next's456* deadlist).457*/458VERIFY0(dsl_dataset_hold_obj(dp,459dsl_dataset_phys(ds_next)->ds_next_snap_obj,460FTAG, &ds_nextnext));461dsl_deadlist_space_range(&ds_nextnext->ds_deadlist,462dsl_dataset_phys(ds)->ds_prev_snap_txg,463dsl_dataset_phys(ds)->ds_creation_txg,464&used, &comp, &uncomp);465dsl_dataset_phys(ds_next)->ds_unique_bytes += used;466dsl_dataset_rele(ds_nextnext, FTAG);467ASSERT0P(ds_next->ds_prev);468469/* Collapse range in this head. */470dsl_dataset_t *hds;471VERIFY0(dsl_dataset_hold_obj(dp,472dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj,473FTAG, &hds));474if (!book_exists) {475/* Collapse range in this head. */476dsl_deadlist_remove_key(&hds->ds_deadlist,477dsl_dataset_phys(ds)->ds_creation_txg, tx);478}479if (dsl_dataset_remap_deadlist_exists(hds)) {480dsl_deadlist_remove_key(&hds->ds_remap_deadlist,481dsl_dataset_phys(ds)->ds_creation_txg, tx);482}483dsl_dataset_rele(hds, FTAG);484485} else {486ASSERT3P(ds_next->ds_prev, ==, ds);487dsl_dataset_rele(ds_next->ds_prev, ds_next);488ds_next->ds_prev = NULL;489if (ds_prev) {490VERIFY0(dsl_dataset_hold_obj(dp,491dsl_dataset_phys(ds)->ds_prev_snap_obj,492ds_next, &ds_next->ds_prev));493}494495dsl_dataset_recalc_head_uniq(ds_next);496497/*498* Reduce the amount of our unconsumed refreservation499* being charged to our parent by the amount of500* new unique data we have gained.501*/502if (old_unique < ds_next->ds_reserved) {503int64_t mrsdelta;504uint64_t new_unique =505dsl_dataset_phys(ds_next)->ds_unique_bytes;506507ASSERT(old_unique <= new_unique);508mrsdelta = MIN(new_unique - old_unique,509ds_next->ds_reserved - old_unique);510dsl_dir_diduse_space(ds->ds_dir,511DD_USED_REFRSRV, -mrsdelta, 0, 0, tx);512}513}514dsl_dataset_rele(ds_next, FTAG);515516/*517* This must be done after the dsl_traverse(), because it will518* re-open the objset.519*/520if (ds->ds_objset) {521dmu_objset_evict(ds->ds_objset);522ds->ds_objset = NULL;523}524525/* remove from snapshot namespace */526dsl_dataset_t *ds_head;527ASSERT0(dsl_dataset_phys(ds)->ds_snapnames_zapobj);528VERIFY0(dsl_dataset_hold_obj(dp,529dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &ds_head));530VERIFY0(dsl_dataset_get_snapname(ds));531#ifdef ZFS_DEBUG532{533uint64_t val;534int err;535536err = dsl_dataset_snap_lookup(ds_head,537ds->ds_snapname, &val);538ASSERT0(err);539ASSERT3U(val, ==, obj);540}541#endif542VERIFY0(dsl_dataset_snap_remove(ds_head, ds->ds_snapname, tx, B_TRUE));543dsl_dataset_rele(ds_head, FTAG);544545if (ds_prev != NULL)546dsl_dataset_rele(ds_prev, FTAG);547548spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);549550if (dsl_dataset_phys(ds)->ds_next_clones_obj != 0) {551uint64_t count __maybe_unused;552ASSERT0(zap_count(mos,553dsl_dataset_phys(ds)->ds_next_clones_obj, &count) &&554count == 0);555VERIFY0(dmu_object_free(mos,556dsl_dataset_phys(ds)->ds_next_clones_obj, tx));557}558if (dsl_dataset_phys(ds)->ds_props_obj != 0)559VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_props_obj,560tx));561if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0)562VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,563tx));564dsl_dir_rele(ds->ds_dir, ds);565ds->ds_dir = NULL;566dmu_object_free_zapified(mos, obj, tx);567}568569void570dsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx)571{572dsl_destroy_snapshot_arg_t *ddsa = arg;573const char *dsname = ddsa->ddsa_name;574boolean_t defer = ddsa->ddsa_defer;575576dsl_pool_t *dp = dmu_tx_pool(tx);577dsl_dataset_t *ds;578579int error = dsl_dataset_hold(dp, dsname, FTAG, &ds);580if (error == ENOENT)581return;582ASSERT0(error);583dsl_destroy_snapshot_sync_impl(ds, defer, tx);584zvol_remove_minors(dp->dp_spa, dsname, B_TRUE);585dsl_dataset_rele(ds, FTAG);586}587588/*589* The semantics of this function are described in the comment above590* lzc_destroy_snaps(). To summarize:591*592* The snapshots must all be in the same pool.593*594* Snapshots that don't exist will be silently ignored (considered to be595* "already deleted").596*597* On success, all snaps will be destroyed and this will return 0.598* On failure, no snaps will be destroyed, the errlist will be filled in,599* and this will return an errno.600*/601int602dsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer,603nvlist_t *errlist)604{605if (nvlist_next_nvpair(snaps, NULL) == NULL)606return (0);607608/*609* lzc_destroy_snaps() is documented to take an nvlist whose610* values "don't matter". We need to convert that nvlist to611* one that we know can be converted to LUA.612*/613nvlist_t *snaps_normalized = fnvlist_alloc();614for (nvpair_t *pair = nvlist_next_nvpair(snaps, NULL);615pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) {616fnvlist_add_boolean_value(snaps_normalized,617nvpair_name(pair), B_TRUE);618}619620nvlist_t *arg = fnvlist_alloc();621fnvlist_add_nvlist(arg, "snaps", snaps_normalized);622fnvlist_free(snaps_normalized);623fnvlist_add_boolean_value(arg, "defer", defer);624625nvlist_t *wrapper = fnvlist_alloc();626fnvlist_add_nvlist(wrapper, ZCP_ARG_ARGLIST, arg);627fnvlist_free(arg);628629const char *program =630"arg = ...\n"631"snaps = arg['snaps']\n"632"defer = arg['defer']\n"633"errors = { }\n"634"has_errors = false\n"635"for snap, v in pairs(snaps) do\n"636" errno = zfs.check.destroy{snap, defer=defer}\n"637" zfs.debug('snap: ' .. snap .. ' errno: ' .. errno)\n"638" if errno == ENOENT then\n"639" snaps[snap] = nil\n"640" elseif errno ~= 0 then\n"641" errors[snap] = errno\n"642" has_errors = true\n"643" end\n"644"end\n"645"if has_errors then\n"646" return errors\n"647"end\n"648"for snap, v in pairs(snaps) do\n"649" errno = zfs.sync.destroy{snap, defer=defer}\n"650" assert(errno == 0)\n"651"end\n"652"return { }\n";653654nvlist_t *result = fnvlist_alloc();655int error = zcp_eval(nvpair_name(nvlist_next_nvpair(snaps, NULL)),656program,657B_TRUE,6580,659zfs_lua_max_memlimit,660fnvlist_lookup_nvpair(wrapper, ZCP_ARG_ARGLIST), result);661if (error != 0) {662const char *errorstr = NULL;663(void) nvlist_lookup_string(result, ZCP_RET_ERROR, &errorstr);664if (errorstr != NULL) {665zfs_dbgmsg("%s", errorstr);666}667fnvlist_free(wrapper);668fnvlist_free(result);669return (error);670}671fnvlist_free(wrapper);672673/*674* lzc_destroy_snaps() is documented to fill the errlist with675* int32 values, so we need to convert the int64 values that are676* returned from LUA.677*/678int rv = 0;679nvlist_t *errlist_raw = fnvlist_lookup_nvlist(result, ZCP_RET_RETURN);680for (nvpair_t *pair = nvlist_next_nvpair(errlist_raw, NULL);681pair != NULL; pair = nvlist_next_nvpair(errlist_raw, pair)) {682int32_t val = (int32_t)fnvpair_value_int64(pair);683if (rv == 0)684rv = val;685fnvlist_add_int32(errlist, nvpair_name(pair), val);686}687fnvlist_free(result);688return (rv);689}690691int692dsl_destroy_snapshot(const char *name, boolean_t defer)693{694int error;695nvlist_t *nvl = fnvlist_alloc();696nvlist_t *errlist = fnvlist_alloc();697698fnvlist_add_boolean(nvl, name);699error = dsl_destroy_snapshots_nvl(nvl, defer, errlist);700fnvlist_free(errlist);701fnvlist_free(nvl);702return (error);703}704705struct killarg {706dsl_dataset_t *ds;707dmu_tx_t *tx;708};709710static int711kill_blkptr(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,712const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg)713{714(void) spa, (void) dnp;715struct killarg *ka = arg;716dmu_tx_t *tx = ka->tx;717718if (zb->zb_level == ZB_DNODE_LEVEL || BP_IS_HOLE(bp) ||719BP_IS_EMBEDDED(bp))720return (0);721722if (zb->zb_level == ZB_ZIL_LEVEL) {723ASSERT(zilog != NULL);724/*725* It's a block in the intent log. It has no726* accounting, so just free it.727*/728dsl_free(ka->tx->tx_pool, ka->tx->tx_txg, bp);729} else {730ASSERT0P(zilog);731ASSERT3U(BP_GET_BIRTH(bp), >,732dsl_dataset_phys(ka->ds)->ds_prev_snap_txg);733(void) dsl_dataset_block_kill(ka->ds, bp, tx, B_FALSE);734}735736return (0);737}738739static void740old_synchronous_dataset_destroy(dsl_dataset_t *ds, dmu_tx_t *tx)741{742struct killarg ka;743744spa_history_log_internal_ds(ds, "destroy", tx,745"(synchronous, mintxg=%llu)",746(long long)dsl_dataset_phys(ds)->ds_prev_snap_txg);747748/*749* Free everything that we point to (that's born after750* the previous snapshot, if we are a clone)751*752* NB: this should be very quick, because we already753* freed all the objects in open context.754*/755ka.ds = ds;756ka.tx = tx;757VERIFY0(traverse_dataset(ds,758dsl_dataset_phys(ds)->ds_prev_snap_txg, TRAVERSE_POST |759TRAVERSE_NO_DECRYPT, kill_blkptr, &ka));760ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||761dsl_dataset_phys(ds)->ds_unique_bytes == 0);762}763764int765dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds)766{767int error;768uint64_t count;769objset_t *mos;770771ASSERT(!ds->ds_is_snapshot);772if (ds->ds_is_snapshot)773return (SET_ERROR(EINVAL));774775if (zfs_refcount_count(&ds->ds_longholds) != expected_holds)776return (SET_ERROR(EBUSY));777778ASSERT0(ds->ds_dir->dd_activity_waiters);779780mos = ds->ds_dir->dd_pool->dp_meta_objset;781782/*783* Can't delete a head dataset if there are snapshots of it.784* (Except if the only snapshots are from the branch we cloned785* from.)786*/787if (ds->ds_prev != NULL &&788dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj == ds->ds_object)789return (SET_ERROR(EBUSY));790791/*792* Can't delete if there are children of this fs.793*/794error = zap_count(mos,795dsl_dir_phys(ds->ds_dir)->dd_child_dir_zapobj, &count);796if (error != 0)797return (error);798if (count != 0)799return (SET_ERROR(EEXIST));800801if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev) &&802dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 &&803ds->ds_prev->ds_userrefs == 0) {804/* We need to remove the origin snapshot as well. */805if (!zfs_refcount_is_zero(&ds->ds_prev->ds_longholds))806return (SET_ERROR(EBUSY));807}808return (0);809}810811int812dsl_destroy_head_check(void *arg, dmu_tx_t *tx)813{814dsl_destroy_head_arg_t *ddha = arg;815dsl_pool_t *dp = dmu_tx_pool(tx);816dsl_dataset_t *ds;817int error;818819error = dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds);820if (error != 0)821return (error);822823error = dsl_destroy_head_check_impl(ds, 0);824dsl_dataset_rele(ds, FTAG);825return (error);826}827828static void829dsl_dir_destroy_sync(uint64_t ddobj, dmu_tx_t *tx)830{831dsl_dir_t *dd;832dsl_pool_t *dp = dmu_tx_pool(tx);833objset_t *mos = dp->dp_meta_objset;834dd_used_t t;835836ASSERT(RRW_WRITE_HELD(&dmu_tx_pool(tx)->dp_config_rwlock));837838VERIFY0(dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd));839840ASSERT0(dsl_dir_phys(dd)->dd_head_dataset_obj);841842/* Decrement the filesystem count for all parent filesystems. */843if (dd->dd_parent != NULL)844dsl_fs_ss_count_adjust(dd->dd_parent, -1,845DD_FIELD_FILESYSTEM_COUNT, tx);846847/*848* Remove our reservation. The impl() routine avoids setting the849* actual property, which would require the (already destroyed) ds.850*/851dsl_dir_set_reservation_sync_impl(dd, 0, tx);852853ASSERT0(dsl_dir_phys(dd)->dd_used_bytes);854ASSERT0(dsl_dir_phys(dd)->dd_reserved);855for (t = 0; t < DD_USED_NUM; t++)856ASSERT0(dsl_dir_phys(dd)->dd_used_breakdown[t]);857858if (dd->dd_crypto_obj != 0) {859dsl_crypto_key_destroy_sync(dd->dd_crypto_obj, tx);860(void) spa_keystore_unload_wkey_impl(dp->dp_spa, dd->dd_object);861}862863VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_child_dir_zapobj, tx));864VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_props_zapobj, tx));865if (dsl_dir_phys(dd)->dd_clones != 0)866VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_clones, tx));867VERIFY0(dsl_deleg_destroy(mos, dsl_dir_phys(dd)->dd_deleg_zapobj, tx));868VERIFY0(zap_remove(mos,869dsl_dir_phys(dd->dd_parent)->dd_child_dir_zapobj,870dd->dd_myname, tx));871872dsl_dir_rele(dd, FTAG);873dmu_object_free_zapified(mos, ddobj, tx);874}875876static void877dsl_clone_destroy_assert(dsl_dir_t *dd)878{879uint64_t used, comp, uncomp;880881ASSERT(dsl_dir_is_clone(dd));882dsl_deadlist_space(&dd->dd_livelist, &used, &comp, &uncomp);883884ASSERT3U(dsl_dir_phys(dd)->dd_used_bytes, ==, used);885ASSERT3U(dsl_dir_phys(dd)->dd_compressed_bytes, ==, comp);886/*887* Greater than because we do not track embedded block pointers in888* the livelist889*/890ASSERT3U(dsl_dir_phys(dd)->dd_uncompressed_bytes, >=, uncomp);891892ASSERT(list_is_empty(&dd->dd_pending_allocs.bpl_list));893ASSERT(list_is_empty(&dd->dd_pending_frees.bpl_list));894}895896/*897* Start the delete process for a clone. Free its zil, verify the space usage898* and queue the blkptrs for deletion by adding the livelist to the pool-wide899* delete queue.900*/901static void902dsl_async_clone_destroy(dsl_dataset_t *ds, dmu_tx_t *tx)903{904uint64_t zap_obj, to_delete, used, comp, uncomp;905objset_t *os;906dsl_dir_t *dd = ds->ds_dir;907dsl_pool_t *dp = dmu_tx_pool(tx);908objset_t *mos = dp->dp_meta_objset;909spa_t *spa = dmu_tx_pool(tx)->dp_spa;910VERIFY0(dmu_objset_from_ds(ds, &os));911912uint64_t mintxg = 0;913dsl_deadlist_entry_t *dle = dsl_deadlist_first(&dd->dd_livelist);914if (dle != NULL)915mintxg = dle->dle_mintxg;916917spa_history_log_internal_ds(ds, "destroy", tx,918"(livelist, mintxg=%llu)", (long long)mintxg);919920/* Check that the clone is in a correct state to be deleted */921dsl_clone_destroy_assert(dd);922923/* Destroy the zil */924zil_destroy_sync(dmu_objset_zil(os), tx);925926VERIFY0(zap_lookup(mos, dd->dd_object,927DD_FIELD_LIVELIST, sizeof (uint64_t), 1, &to_delete));928/* Initialize deleted_clones entry to track livelists to cleanup */929int error = zap_lookup(mos, DMU_POOL_DIRECTORY_OBJECT,930DMU_POOL_DELETED_CLONES, sizeof (uint64_t), 1, &zap_obj);931if (error == ENOENT) {932zap_obj = zap_create(mos, DMU_OTN_ZAP_METADATA,933DMU_OT_NONE, 0, tx);934VERIFY0(zap_add(mos, DMU_POOL_DIRECTORY_OBJECT,935DMU_POOL_DELETED_CLONES, sizeof (uint64_t), 1,936&(zap_obj), tx));937spa->spa_livelists_to_delete = zap_obj;938} else if (error != 0) {939zfs_panic_recover("zfs: error %d was returned while looking "940"up DMU_POOL_DELETED_CLONES in the zap", error);941return;942}943VERIFY0(zap_add_int(mos, zap_obj, to_delete, tx));944945/* Clone is no longer using space, now tracked by dp_free_dir */946dsl_deadlist_space(&dd->dd_livelist, &used, &comp, &uncomp);947dsl_dir_diduse_space(dd, DD_USED_HEAD,948-used, -comp, -dsl_dir_phys(dd)->dd_uncompressed_bytes,949tx);950dsl_dir_diduse_space(dp->dp_free_dir, DD_USED_HEAD,951used, comp, uncomp, tx);952dsl_dir_remove_livelist(dd, tx, B_FALSE);953zthr_wakeup(spa->spa_livelist_delete_zthr);954}955956/*957* Move the bptree into the pool's list of trees to clean up, update space958* accounting information and destroy the zil.959*/960static void961dsl_async_dataset_destroy(dsl_dataset_t *ds, dmu_tx_t *tx)962{963uint64_t used, comp, uncomp;964objset_t *os;965966VERIFY0(dmu_objset_from_ds(ds, &os));967dsl_pool_t *dp = dmu_tx_pool(tx);968objset_t *mos = dp->dp_meta_objset;969970spa_history_log_internal_ds(ds, "destroy", tx,971"(bptree, mintxg=%llu)",972(long long)dsl_dataset_phys(ds)->ds_prev_snap_txg);973974zil_destroy_sync(dmu_objset_zil(os), tx);975976if (!spa_feature_is_active(dp->dp_spa,977SPA_FEATURE_ASYNC_DESTROY)) {978dsl_scan_t *scn = dp->dp_scan;979spa_feature_incr(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY,980tx);981dp->dp_bptree_obj = bptree_alloc(mos, tx);982VERIFY0(zap_add(mos,983DMU_POOL_DIRECTORY_OBJECT,984DMU_POOL_BPTREE_OBJ, sizeof (uint64_t), 1,985&dp->dp_bptree_obj, tx));986ASSERT(!scn->scn_async_destroying);987scn->scn_async_destroying = B_TRUE;988}989990used = dsl_dir_phys(ds->ds_dir)->dd_used_bytes;991comp = dsl_dir_phys(ds->ds_dir)->dd_compressed_bytes;992uncomp = dsl_dir_phys(ds->ds_dir)->dd_uncompressed_bytes;993994ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) ||995dsl_dataset_phys(ds)->ds_unique_bytes == used);996997rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);998bptree_add(mos, dp->dp_bptree_obj,999&dsl_dataset_phys(ds)->ds_bp,1000dsl_dataset_phys(ds)->ds_prev_snap_txg,1001used, comp, uncomp, tx);1002rrw_exit(&ds->ds_bp_rwlock, FTAG);1003dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD,1004-used, -comp, -uncomp, tx);1005dsl_dir_diduse_space(dp->dp_free_dir, DD_USED_HEAD,1006used, comp, uncomp, tx);1007}10081009void1010dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)1011{1012dsl_pool_t *dp = dmu_tx_pool(tx);1013objset_t *mos = dp->dp_meta_objset;1014uint64_t obj, ddobj, prevobj = 0;1015boolean_t rmorigin;10161017ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1);1018ASSERT(ds->ds_prev == NULL ||1019dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj != ds->ds_object);1020rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);1021ASSERT3U(BP_GET_BIRTH(&dsl_dataset_phys(ds)->ds_bp), <=, tx->tx_txg);1022rrw_exit(&ds->ds_bp_rwlock, FTAG);1023ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));10241025dsl_dir_cancel_waiters(ds->ds_dir);10261027rmorigin = (dsl_dir_is_clone(ds->ds_dir) &&1028DS_IS_DEFER_DESTROY(ds->ds_prev) &&1029dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 &&1030ds->ds_prev->ds_userrefs == 0);10311032/* Remove our reservation. */1033if (ds->ds_reserved != 0) {1034dsl_dataset_set_refreservation_sync_impl(ds,1035(ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED),10360, tx);1037ASSERT0(ds->ds_reserved);1038}10391040obj = ds->ds_object;10411042for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {1043if (dsl_dataset_feature_is_active(ds, f))1044dsl_dataset_deactivate_feature(ds, f, tx);1045}10461047dsl_scan_ds_destroyed(ds, tx);10481049if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {1050/* This is a clone */1051ASSERT(ds->ds_prev != NULL);1052ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj, !=,1053obj);1054ASSERT0(dsl_dataset_phys(ds)->ds_next_snap_obj);10551056dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);1057if (dsl_dataset_phys(ds->ds_prev)->ds_next_clones_obj != 0) {1058dsl_dataset_remove_from_next_clones(ds->ds_prev,1059obj, tx);1060}10611062ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_num_children, >, 1);1063dsl_dataset_phys(ds->ds_prev)->ds_num_children--;1064}10651066/*1067* Destroy the deadlist. Unless it's a clone, the1068* deadlist should be empty since the dataset has no snapshots.1069* (If it's a clone, it's safe to ignore the deadlist contents1070* since they are still referenced by the origin snapshot.)1071*/1072dsl_deadlist_close(&ds->ds_deadlist);1073dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx);1074dmu_buf_will_dirty(ds->ds_dbuf, tx);1075dsl_dataset_phys(ds)->ds_deadlist_obj = 0;10761077if (dsl_dataset_remap_deadlist_exists(ds))1078dsl_dataset_destroy_remap_deadlist(ds, tx);10791080/*1081* Each destroy is responsible for both destroying (enqueuing1082* to be destroyed) the blkptrs comprising the dataset as well as1083* those belonging to the zil.1084*/1085if (dsl_deadlist_is_open(&ds->ds_dir->dd_livelist)) {1086dsl_async_clone_destroy(ds, tx);1087} else if (spa_feature_is_enabled(dp->dp_spa,1088SPA_FEATURE_ASYNC_DESTROY)) {1089dsl_async_dataset_destroy(ds, tx);1090} else {1091old_synchronous_dataset_destroy(ds, tx);1092}10931094if (ds->ds_prev != NULL) {1095if (spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) {1096VERIFY0(zap_remove_int(mos,1097dsl_dir_phys(ds->ds_prev->ds_dir)->dd_clones,1098ds->ds_object, tx));1099}1100prevobj = ds->ds_prev->ds_object;1101dsl_dataset_rele(ds->ds_prev, ds);1102ds->ds_prev = NULL;1103}11041105/*1106* This must be done after the dsl_traverse(), because it will1107* re-open the objset.1108*/1109if (ds->ds_objset) {1110dmu_objset_evict(ds->ds_objset);1111ds->ds_objset = NULL;1112}11131114/* Erase the link in the dir */1115dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);1116dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj = 0;1117ddobj = ds->ds_dir->dd_object;1118ASSERT(dsl_dataset_phys(ds)->ds_snapnames_zapobj != 0);1119VERIFY0(zap_destroy(mos,1120dsl_dataset_phys(ds)->ds_snapnames_zapobj, tx));11211122if (ds->ds_bookmarks_obj != 0) {1123void *cookie = NULL;1124dsl_bookmark_node_t *dbn;11251126while ((dbn = avl_destroy_nodes(&ds->ds_bookmarks, &cookie)) !=1127NULL) {1128if (dbn->dbn_phys.zbm_redaction_obj != 0) {1129dnode_t *rl;1130VERIFY0(dnode_hold(mos,1131dbn->dbn_phys.zbm_redaction_obj, FTAG,1132&rl));1133if (rl->dn_have_spill) {1134spa_feature_decr(dmu_objset_spa(mos),1135SPA_FEATURE_REDACTION_LIST_SPILL,1136tx);1137}1138dnode_rele(rl, FTAG);1139VERIFY0(dmu_object_free(mos,1140dbn->dbn_phys.zbm_redaction_obj, tx));1141spa_feature_decr(dmu_objset_spa(mos),1142SPA_FEATURE_REDACTION_BOOKMARKS, tx);1143}1144if (dbn->dbn_phys.zbm_flags & ZBM_FLAG_HAS_FBN) {1145spa_feature_decr(dmu_objset_spa(mos),1146SPA_FEATURE_BOOKMARK_WRITTEN, tx);1147}1148spa_strfree(dbn->dbn_name);1149mutex_destroy(&dbn->dbn_lock);1150kmem_free(dbn, sizeof (*dbn));1151}1152avl_destroy(&ds->ds_bookmarks);1153VERIFY0(zap_destroy(mos, ds->ds_bookmarks_obj, tx));1154spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);1155}11561157spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);11581159ASSERT0(dsl_dataset_phys(ds)->ds_next_clones_obj);1160ASSERT0(dsl_dataset_phys(ds)->ds_props_obj);1161ASSERT0(dsl_dataset_phys(ds)->ds_userrefs_obj);1162dsl_dir_rele(ds->ds_dir, ds);1163ds->ds_dir = NULL;1164dmu_object_free_zapified(mos, obj, tx);11651166dsl_dir_destroy_sync(ddobj, tx);11671168if (rmorigin) {1169dsl_dataset_t *prev;1170VERIFY0(dsl_dataset_hold_obj(dp, prevobj, FTAG, &prev));1171dsl_destroy_snapshot_sync_impl(prev, B_FALSE, tx);1172dsl_dataset_rele(prev, FTAG);1173}1174/* Delete errlog. */1175if (spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_HEAD_ERRLOG))1176spa_delete_dataset_errlog(dp->dp_spa, ds->ds_object, tx);1177}11781179void1180dsl_destroy_head_sync(void *arg, dmu_tx_t *tx)1181{1182dsl_destroy_head_arg_t *ddha = arg;1183dsl_pool_t *dp = dmu_tx_pool(tx);1184dsl_dataset_t *ds;11851186VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds));1187dsl_destroy_head_sync_impl(ds, tx);1188zvol_remove_minors(dp->dp_spa, ddha->ddha_name, B_TRUE);1189dsl_dataset_rele(ds, FTAG);1190}11911192static void1193dsl_destroy_head_begin_sync(void *arg, dmu_tx_t *tx)1194{1195dsl_destroy_head_arg_t *ddha = arg;1196dsl_pool_t *dp = dmu_tx_pool(tx);1197dsl_dataset_t *ds;11981199VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds));12001201/* Mark it as inconsistent on-disk, in case we crash */1202dmu_buf_will_dirty(ds->ds_dbuf, tx);1203dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT;12041205spa_history_log_internal_ds(ds, "destroy begin", tx, " ");1206dsl_dataset_rele(ds, FTAG);1207}12081209int1210dsl_destroy_head(const char *name)1211{1212dsl_destroy_head_arg_t ddha;1213int error;1214spa_t *spa;1215boolean_t isenabled;12161217#ifdef _KERNEL1218zfs_destroy_unmount_origin(name);1219#endif12201221error = spa_open(name, &spa, FTAG);1222if (error != 0)1223return (error);1224isenabled = spa_feature_is_enabled(spa, SPA_FEATURE_ASYNC_DESTROY);1225spa_close(spa, FTAG);12261227ddha.ddha_name = name;12281229if (!isenabled) {1230objset_t *os;12311232error = dsl_sync_task(name, dsl_destroy_head_check,1233dsl_destroy_head_begin_sync, &ddha,12340, ZFS_SPACE_CHECK_DESTROY);1235if (error != 0)1236return (error);12371238/*1239* Head deletion is processed in one txg on old pools;1240* remove the objects from open context so that the txg sync1241* is not too long. This optimization can only work for1242* encrypted datasets if the wrapping key is loaded.1243*/1244error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, B_TRUE,1245FTAG, &os);1246if (error == 0) {1247uint64_t prev_snap_txg =1248dsl_dataset_phys(dmu_objset_ds(os))->1249ds_prev_snap_txg;1250for (uint64_t obj = 0; error == 0;1251error = dmu_object_next(os, &obj, FALSE,1252prev_snap_txg))1253(void) dmu_free_long_object(os, obj);1254/* sync out all frees */1255txg_wait_synced(dmu_objset_pool(os), 0);1256dmu_objset_disown(os, B_TRUE, FTAG);1257}1258}12591260return (dsl_sync_task(name, dsl_destroy_head_check,1261dsl_destroy_head_sync, &ddha, 0, ZFS_SPACE_CHECK_DESTROY));1262}12631264/*1265* Note, this function is used as the callback for dmu_objset_find(). We1266* always return 0 so that we will continue to find and process1267* inconsistent datasets, even if we encounter an error trying to1268* process one of them.1269*/1270int1271dsl_destroy_inconsistent(const char *dsname, void *arg)1272{1273(void) arg;1274objset_t *os;12751276if (dmu_objset_hold(dsname, FTAG, &os) == 0) {1277boolean_t need_destroy = DS_IS_INCONSISTENT(dmu_objset_ds(os));12781279/*1280* If the dataset is inconsistent because a resumable receive1281* has failed, then do not destroy it.1282*/1283if (dsl_dataset_has_resume_receive_state(dmu_objset_ds(os)))1284need_destroy = B_FALSE;12851286dmu_objset_rele(os, FTAG);1287if (need_destroy)1288(void) dsl_destroy_head(dsname);1289}1290return (0);1291}129212931294#if defined(_KERNEL)1295EXPORT_SYMBOL(dsl_destroy_head);1296EXPORT_SYMBOL(dsl_destroy_head_sync_impl);1297EXPORT_SYMBOL(dsl_dataset_user_hold_check_one);1298EXPORT_SYMBOL(dsl_destroy_snapshot_sync_impl);1299EXPORT_SYMBOL(dsl_destroy_inconsistent);1300EXPORT_SYMBOL(dsl_dataset_user_release_tmp);1301EXPORT_SYMBOL(dsl_destroy_head_check_impl);1302#endif130313041305