1247580Smm/* 2247580Smm * CDDL HEADER START 3247580Smm * 4247580Smm * The contents of this file are subject to the terms of the 5247580Smm * Common Development and Distribution License (the "License"). 6247580Smm * You may not use this file except in compliance with the License. 7247580Smm * 8247580Smm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9247580Smm * or http://www.opensolaris.org/os/licensing. 10247580Smm * See the License for the specific language governing permissions 11247580Smm * and limitations under the License. 12247580Smm * 13247580Smm * When distributing Covered Code, include this CDDL HEADER in each 14247580Smm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15247580Smm * If applicable, add the following below this CDDL HEADER, with the 16247580Smm * fields enclosed by brackets "[]" replaced with your own identifying 17247580Smm * information: Portions Copyright [yyyy] [name of copyright owner] 18247580Smm * 19247580Smm * CDDL HEADER END 20247580Smm */ 21247580Smm/* 22247580Smm * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23288572Smav * Copyright (c) 2012, 2015 by Delphix. All rights reserved. 24251646Sdelphij * Copyright (c) 2013 Steven Hartland. All rights reserved. 25265744Sdelphij * Copyright (c) 2013 by Joyent, Inc. All rights reserved. 26297112Smav * Copyright (c) 2014 Integros [integros.com] 27247580Smm */ 28247580Smm 29247580Smm#include <sys/zfs_context.h> 30247580Smm#include <sys/dsl_userhold.h> 31247580Smm#include <sys/dsl_dataset.h> 32247580Smm#include <sys/dsl_synctask.h> 33247580Smm#include <sys/dmu_tx.h> 34247580Smm#include <sys/dsl_pool.h> 35247580Smm#include <sys/dsl_dir.h> 36247580Smm#include <sys/dmu_traverse.h> 37247580Smm#include <sys/dsl_scan.h> 38247580Smm#include <sys/dmu_objset.h> 39247580Smm#include <sys/zap.h> 40247580Smm#include <sys/zfeature.h> 41247580Smm#include <sys/zfs_ioctl.h> 42247580Smm#include <sys/dsl_deleg.h> 43263390Sdelphij#include <sys/dmu_impl.h> 44247580Smm 45247580Smmtypedef struct dmu_snapshots_destroy_arg { 46247580Smm nvlist_t *dsda_snaps; 47247580Smm nvlist_t *dsda_successful_snaps; 48247580Smm boolean_t dsda_defer; 49247580Smm nvlist_t *dsda_errlist; 50247580Smm} dmu_snapshots_destroy_arg_t; 51247580Smm 52253820Sdelphijint 53247580Smmdsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer) 54247580Smm{ 55288549Smav if (!ds->ds_is_snapshot) 56249195Smm return (SET_ERROR(EINVAL)); 57247580Smm 58247580Smm if (dsl_dataset_long_held(ds)) 59249195Smm return (SET_ERROR(EBUSY)); 60247580Smm 61247580Smm /* 62247580Smm * Only allow deferred destroy on pools that support it. 63247580Smm * NOTE: deferred destroy is only supported on snapshots. 64247580Smm */ 65247580Smm if (defer) { 66247580Smm if (spa_version(ds->ds_dir->dd_pool->dp_spa) < 67247580Smm SPA_VERSION_USERREFS) 68249195Smm return (SET_ERROR(ENOTSUP)); 69247580Smm return (0); 70247580Smm } 71247580Smm 72247580Smm /* 73247580Smm * If this snapshot has an elevated user reference count, 74247580Smm * we can't destroy it yet. 75247580Smm */ 76247580Smm if (ds->ds_userrefs > 0) 77249195Smm return (SET_ERROR(EBUSY)); 78247580Smm 79247580Smm /* 80247580Smm * Can't delete a branch point. 81247580Smm */ 82277585Sdelphij if (dsl_dataset_phys(ds)->ds_num_children > 1) 83249195Smm return (SET_ERROR(EEXIST)); 84247580Smm 85247580Smm return (0); 86247580Smm} 87247580Smm 88247580Smmstatic int 89247580Smmdsl_destroy_snapshot_check(void *arg, dmu_tx_t *tx) 90247580Smm{ 91247580Smm dmu_snapshots_destroy_arg_t *dsda = arg; 92247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 93247580Smm nvpair_t *pair; 94247580Smm int error = 0; 95247580Smm 96247580Smm if (!dmu_tx_is_syncing(tx)) 97247580Smm return (0); 98247580Smm 99247580Smm for (pair = nvlist_next_nvpair(dsda->dsda_snaps, NULL); 100247580Smm pair != NULL; pair = nvlist_next_nvpair(dsda->dsda_snaps, pair)) { 101247580Smm dsl_dataset_t *ds; 102247580Smm 103247580Smm error = dsl_dataset_hold(dp, nvpair_name(pair), 104247580Smm FTAG, &ds); 105247580Smm 106247580Smm /* 107247580Smm * If the snapshot does not exist, silently ignore it 108247580Smm * (it's "already destroyed"). 109247580Smm */ 110247580Smm if (error == ENOENT) 111247580Smm continue; 112247580Smm 113247580Smm if (error == 0) { 114247580Smm error = dsl_destroy_snapshot_check_impl(ds, 115247580Smm dsda->dsda_defer); 116247580Smm dsl_dataset_rele(ds, FTAG); 117247580Smm } 118247580Smm 119247580Smm if (error == 0) { 120247580Smm fnvlist_add_boolean(dsda->dsda_successful_snaps, 121247580Smm nvpair_name(pair)); 122247580Smm } else { 123247580Smm fnvlist_add_int32(dsda->dsda_errlist, 124247580Smm nvpair_name(pair), error); 125247580Smm } 126247580Smm } 127247580Smm 128247580Smm pair = nvlist_next_nvpair(dsda->dsda_errlist, NULL); 129247580Smm if (pair != NULL) 130247580Smm return (fnvpair_value_int32(pair)); 131251646Sdelphij 132247580Smm return (0); 133247580Smm} 134247580Smm 135247580Smmstruct process_old_arg { 136247580Smm dsl_dataset_t *ds; 137247580Smm dsl_dataset_t *ds_prev; 138247580Smm boolean_t after_branch_point; 139247580Smm zio_t *pio; 140247580Smm uint64_t used, comp, uncomp; 141247580Smm}; 142247580Smm 143247580Smmstatic int 144247580Smmprocess_old_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) 145247580Smm{ 146247580Smm struct process_old_arg *poa = arg; 147247580Smm dsl_pool_t *dp = poa->ds->ds_dir->dd_pool; 148247580Smm 149263397Sdelphij ASSERT(!BP_IS_HOLE(bp)); 150263397Sdelphij 151277585Sdelphij if (bp->blk_birth <= dsl_dataset_phys(poa->ds)->ds_prev_snap_txg) { 152247580Smm dsl_deadlist_insert(&poa->ds->ds_deadlist, bp, tx); 153247580Smm if (poa->ds_prev && !poa->after_branch_point && 154247580Smm bp->blk_birth > 155277585Sdelphij dsl_dataset_phys(poa->ds_prev)->ds_prev_snap_txg) { 156277585Sdelphij dsl_dataset_phys(poa->ds_prev)->ds_unique_bytes += 157247580Smm bp_get_dsize_sync(dp->dp_spa, bp); 158247580Smm } 159247580Smm } else { 160247580Smm poa->used += bp_get_dsize_sync(dp->dp_spa, bp); 161247580Smm poa->comp += BP_GET_PSIZE(bp); 162247580Smm poa->uncomp += BP_GET_UCSIZE(bp); 163247580Smm dsl_free_sync(poa->pio, dp, tx->tx_txg, bp); 164247580Smm } 165247580Smm return (0); 166247580Smm} 167247580Smm 168247580Smmstatic void 169247580Smmprocess_old_deadlist(dsl_dataset_t *ds, dsl_dataset_t *ds_prev, 170247580Smm dsl_dataset_t *ds_next, boolean_t after_branch_point, dmu_tx_t *tx) 171247580Smm{ 172247580Smm struct process_old_arg poa = { 0 }; 173247580Smm dsl_pool_t *dp = ds->ds_dir->dd_pool; 174247580Smm objset_t *mos = dp->dp_meta_objset; 175247580Smm uint64_t deadlist_obj; 176247580Smm 177247580Smm ASSERT(ds->ds_deadlist.dl_oldfmt); 178247580Smm ASSERT(ds_next->ds_deadlist.dl_oldfmt); 179247580Smm 180247580Smm poa.ds = ds; 181247580Smm poa.ds_prev = ds_prev; 182247580Smm poa.after_branch_point = after_branch_point; 183247580Smm poa.pio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED); 184247580Smm VERIFY0(bpobj_iterate(&ds_next->ds_deadlist.dl_bpobj, 185247580Smm process_old_cb, &poa, tx)); 186247580Smm VERIFY0(zio_wait(poa.pio)); 187277585Sdelphij ASSERT3U(poa.used, ==, dsl_dataset_phys(ds)->ds_unique_bytes); 188247580Smm 189247580Smm /* change snapused */ 190247580Smm dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP, 191247580Smm -poa.used, -poa.comp, -poa.uncomp, tx); 192247580Smm 193247580Smm /* swap next's deadlist to our deadlist */ 194247580Smm dsl_deadlist_close(&ds->ds_deadlist); 195247580Smm dsl_deadlist_close(&ds_next->ds_deadlist); 196277585Sdelphij deadlist_obj = dsl_dataset_phys(ds)->ds_deadlist_obj; 197277585Sdelphij dsl_dataset_phys(ds)->ds_deadlist_obj = 198277585Sdelphij dsl_dataset_phys(ds_next)->ds_deadlist_obj; 199277585Sdelphij dsl_dataset_phys(ds_next)->ds_deadlist_obj = deadlist_obj; 200277585Sdelphij dsl_deadlist_open(&ds->ds_deadlist, mos, 201277585Sdelphij dsl_dataset_phys(ds)->ds_deadlist_obj); 202247580Smm dsl_deadlist_open(&ds_next->ds_deadlist, mos, 203277585Sdelphij dsl_dataset_phys(ds_next)->ds_deadlist_obj); 204247580Smm} 205247580Smm 206247580Smmstatic void 207247580Smmdsl_dataset_remove_clones_key(dsl_dataset_t *ds, uint64_t mintxg, dmu_tx_t *tx) 208247580Smm{ 209247580Smm objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 210247580Smm zap_cursor_t zc; 211247580Smm zap_attribute_t za; 212247580Smm 213247580Smm /* 214247580Smm * If it is the old version, dd_clones doesn't exist so we can't 215247580Smm * find the clones, but dsl_deadlist_remove_key() is a no-op so it 216247580Smm * doesn't matter. 217247580Smm */ 218277585Sdelphij if (dsl_dir_phys(ds->ds_dir)->dd_clones == 0) 219247580Smm return; 220247580Smm 221277585Sdelphij for (zap_cursor_init(&zc, mos, dsl_dir_phys(ds->ds_dir)->dd_clones); 222247580Smm zap_cursor_retrieve(&zc, &za) == 0; 223247580Smm zap_cursor_advance(&zc)) { 224247580Smm dsl_dataset_t *clone; 225247580Smm 226247580Smm VERIFY0(dsl_dataset_hold_obj(ds->ds_dir->dd_pool, 227247580Smm za.za_first_integer, FTAG, &clone)); 228247580Smm if (clone->ds_dir->dd_origin_txg > mintxg) { 229247580Smm dsl_deadlist_remove_key(&clone->ds_deadlist, 230247580Smm mintxg, tx); 231247580Smm dsl_dataset_remove_clones_key(clone, mintxg, tx); 232247580Smm } 233247580Smm dsl_dataset_rele(clone, FTAG); 234247580Smm } 235247580Smm zap_cursor_fini(&zc); 236247580Smm} 237247580Smm 238247580Smmvoid 239247580Smmdsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx) 240247580Smm{ 241247580Smm int err; 242247580Smm int after_branch_point = FALSE; 243247580Smm dsl_pool_t *dp = ds->ds_dir->dd_pool; 244247580Smm objset_t *mos = dp->dp_meta_objset; 245247580Smm dsl_dataset_t *ds_prev = NULL; 246247580Smm uint64_t obj; 247247580Smm 248247580Smm ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 249308083Smav rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG); 250277585Sdelphij ASSERT3U(dsl_dataset_phys(ds)->ds_bp.blk_birth, <=, tx->tx_txg); 251308083Smav rrw_exit(&ds->ds_bp_rwlock, FTAG); 252247580Smm ASSERT(refcount_is_zero(&ds->ds_longholds)); 253247580Smm 254247580Smm if (defer && 255277585Sdelphij (ds->ds_userrefs > 0 || 256277585Sdelphij dsl_dataset_phys(ds)->ds_num_children > 1)) { 257247580Smm ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS); 258247580Smm dmu_buf_will_dirty(ds->ds_dbuf, tx); 259277585Sdelphij dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_DEFER_DESTROY; 260247580Smm spa_history_log_internal_ds(ds, "defer_destroy", tx, ""); 261247580Smm return; 262247580Smm } 263247580Smm 264277585Sdelphij ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1); 265247580Smm 266247580Smm /* We need to log before removing it from the namespace. */ 267247580Smm spa_history_log_internal_ds(ds, "destroy", tx, ""); 268247580Smm 269247580Smm dsl_scan_ds_destroyed(ds, tx); 270247580Smm 271247580Smm obj = ds->ds_object; 272247580Smm 273288572Smav for (spa_feature_t f = 0; f < SPA_FEATURES; f++) { 274288572Smav if (ds->ds_feature_inuse[f]) { 275288572Smav dsl_dataset_deactivate_feature(obj, f, tx); 276288572Smav ds->ds_feature_inuse[f] = B_FALSE; 277288572Smav } 278276081Sdelphij } 279277585Sdelphij if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) { 280247580Smm ASSERT3P(ds->ds_prev, ==, NULL); 281247580Smm VERIFY0(dsl_dataset_hold_obj(dp, 282277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &ds_prev)); 283247580Smm after_branch_point = 284277585Sdelphij (dsl_dataset_phys(ds_prev)->ds_next_snap_obj != obj); 285247580Smm 286247580Smm dmu_buf_will_dirty(ds_prev->ds_dbuf, tx); 287247580Smm if (after_branch_point && 288277585Sdelphij dsl_dataset_phys(ds_prev)->ds_next_clones_obj != 0) { 289247580Smm dsl_dataset_remove_from_next_clones(ds_prev, obj, tx); 290277585Sdelphij if (dsl_dataset_phys(ds)->ds_next_snap_obj != 0) { 291247580Smm VERIFY0(zap_add_int(mos, 292277585Sdelphij dsl_dataset_phys(ds_prev)-> 293277585Sdelphij ds_next_clones_obj, 294277585Sdelphij dsl_dataset_phys(ds)->ds_next_snap_obj, 295277585Sdelphij tx)); 296247580Smm } 297247580Smm } 298247580Smm if (!after_branch_point) { 299277585Sdelphij dsl_dataset_phys(ds_prev)->ds_next_snap_obj = 300277585Sdelphij dsl_dataset_phys(ds)->ds_next_snap_obj; 301247580Smm } 302247580Smm } 303247580Smm 304247580Smm dsl_dataset_t *ds_next; 305247580Smm uint64_t old_unique; 306247580Smm uint64_t used = 0, comp = 0, uncomp = 0; 307247580Smm 308247580Smm VERIFY0(dsl_dataset_hold_obj(dp, 309277585Sdelphij dsl_dataset_phys(ds)->ds_next_snap_obj, FTAG, &ds_next)); 310277585Sdelphij ASSERT3U(dsl_dataset_phys(ds_next)->ds_prev_snap_obj, ==, obj); 311247580Smm 312277585Sdelphij old_unique = dsl_dataset_phys(ds_next)->ds_unique_bytes; 313247580Smm 314247580Smm dmu_buf_will_dirty(ds_next->ds_dbuf, tx); 315277585Sdelphij dsl_dataset_phys(ds_next)->ds_prev_snap_obj = 316277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_obj; 317277585Sdelphij dsl_dataset_phys(ds_next)->ds_prev_snap_txg = 318277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_txg; 319277585Sdelphij ASSERT3U(dsl_dataset_phys(ds)->ds_prev_snap_txg, ==, 320277585Sdelphij ds_prev ? dsl_dataset_phys(ds_prev)->ds_creation_txg : 0); 321247580Smm 322247580Smm if (ds_next->ds_deadlist.dl_oldfmt) { 323247580Smm process_old_deadlist(ds, ds_prev, ds_next, 324247580Smm after_branch_point, tx); 325247580Smm } else { 326247580Smm /* Adjust prev's unique space. */ 327247580Smm if (ds_prev && !after_branch_point) { 328247580Smm dsl_deadlist_space_range(&ds_next->ds_deadlist, 329277585Sdelphij dsl_dataset_phys(ds_prev)->ds_prev_snap_txg, 330277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_txg, 331247580Smm &used, &comp, &uncomp); 332277585Sdelphij dsl_dataset_phys(ds_prev)->ds_unique_bytes += used; 333247580Smm } 334247580Smm 335247580Smm /* Adjust snapused. */ 336247580Smm dsl_deadlist_space_range(&ds_next->ds_deadlist, 337277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_txg, UINT64_MAX, 338247580Smm &used, &comp, &uncomp); 339247580Smm dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP, 340247580Smm -used, -comp, -uncomp, tx); 341247580Smm 342247580Smm /* Move blocks to be freed to pool's free list. */ 343247580Smm dsl_deadlist_move_bpobj(&ds_next->ds_deadlist, 344277585Sdelphij &dp->dp_free_bpobj, dsl_dataset_phys(ds)->ds_prev_snap_txg, 345247580Smm tx); 346247580Smm dsl_dir_diduse_space(tx->tx_pool->dp_free_dir, 347247580Smm DD_USED_HEAD, used, comp, uncomp, tx); 348247580Smm 349247580Smm /* Merge our deadlist into next's and free it. */ 350247580Smm dsl_deadlist_merge(&ds_next->ds_deadlist, 351277585Sdelphij dsl_dataset_phys(ds)->ds_deadlist_obj, tx); 352247580Smm } 353247580Smm dsl_deadlist_close(&ds->ds_deadlist); 354277585Sdelphij dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx); 355247580Smm dmu_buf_will_dirty(ds->ds_dbuf, tx); 356277585Sdelphij dsl_dataset_phys(ds)->ds_deadlist_obj = 0; 357247580Smm 358247580Smm /* Collapse range in clone heads */ 359247580Smm dsl_dataset_remove_clones_key(ds, 360277585Sdelphij dsl_dataset_phys(ds)->ds_creation_txg, tx); 361247580Smm 362288549Smav if (ds_next->ds_is_snapshot) { 363247580Smm dsl_dataset_t *ds_nextnext; 364247580Smm 365247580Smm /* 366247580Smm * Update next's unique to include blocks which 367247580Smm * were previously shared by only this snapshot 368247580Smm * and it. Those blocks will be born after the 369247580Smm * prev snap and before this snap, and will have 370247580Smm * died after the next snap and before the one 371247580Smm * after that (ie. be on the snap after next's 372247580Smm * deadlist). 373247580Smm */ 374247580Smm VERIFY0(dsl_dataset_hold_obj(dp, 375277585Sdelphij dsl_dataset_phys(ds_next)->ds_next_snap_obj, 376277585Sdelphij FTAG, &ds_nextnext)); 377247580Smm dsl_deadlist_space_range(&ds_nextnext->ds_deadlist, 378277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_txg, 379277585Sdelphij dsl_dataset_phys(ds)->ds_creation_txg, 380247580Smm &used, &comp, &uncomp); 381277585Sdelphij dsl_dataset_phys(ds_next)->ds_unique_bytes += used; 382247580Smm dsl_dataset_rele(ds_nextnext, FTAG); 383247580Smm ASSERT3P(ds_next->ds_prev, ==, NULL); 384247580Smm 385247580Smm /* Collapse range in this head. */ 386247580Smm dsl_dataset_t *hds; 387247580Smm VERIFY0(dsl_dataset_hold_obj(dp, 388277585Sdelphij dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &hds)); 389247580Smm dsl_deadlist_remove_key(&hds->ds_deadlist, 390277585Sdelphij dsl_dataset_phys(ds)->ds_creation_txg, tx); 391247580Smm dsl_dataset_rele(hds, FTAG); 392247580Smm 393247580Smm } else { 394247580Smm ASSERT3P(ds_next->ds_prev, ==, ds); 395247580Smm dsl_dataset_rele(ds_next->ds_prev, ds_next); 396247580Smm ds_next->ds_prev = NULL; 397247580Smm if (ds_prev) { 398247580Smm VERIFY0(dsl_dataset_hold_obj(dp, 399277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_obj, 400247580Smm ds_next, &ds_next->ds_prev)); 401247580Smm } 402247580Smm 403247580Smm dsl_dataset_recalc_head_uniq(ds_next); 404247580Smm 405247580Smm /* 406247580Smm * Reduce the amount of our unconsumed refreservation 407247580Smm * being charged to our parent by the amount of 408247580Smm * new unique data we have gained. 409247580Smm */ 410247580Smm if (old_unique < ds_next->ds_reserved) { 411247580Smm int64_t mrsdelta; 412247580Smm uint64_t new_unique = 413277585Sdelphij dsl_dataset_phys(ds_next)->ds_unique_bytes; 414247580Smm 415247580Smm ASSERT(old_unique <= new_unique); 416247580Smm mrsdelta = MIN(new_unique - old_unique, 417247580Smm ds_next->ds_reserved - old_unique); 418247580Smm dsl_dir_diduse_space(ds->ds_dir, 419247580Smm DD_USED_REFRSRV, -mrsdelta, 0, 0, tx); 420247580Smm } 421247580Smm } 422247580Smm dsl_dataset_rele(ds_next, FTAG); 423247580Smm 424247580Smm /* 425247580Smm * This must be done after the dsl_traverse(), because it will 426247580Smm * re-open the objset. 427247580Smm */ 428247580Smm if (ds->ds_objset) { 429247580Smm dmu_objset_evict(ds->ds_objset); 430247580Smm ds->ds_objset = NULL; 431247580Smm } 432247580Smm 433247580Smm /* remove from snapshot namespace */ 434247580Smm dsl_dataset_t *ds_head; 435277585Sdelphij ASSERT(dsl_dataset_phys(ds)->ds_snapnames_zapobj == 0); 436247580Smm VERIFY0(dsl_dataset_hold_obj(dp, 437277585Sdelphij dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &ds_head)); 438247580Smm VERIFY0(dsl_dataset_get_snapname(ds)); 439247580Smm#ifdef ZFS_DEBUG 440247580Smm { 441247580Smm uint64_t val; 442247580Smm 443247580Smm err = dsl_dataset_snap_lookup(ds_head, 444247580Smm ds->ds_snapname, &val); 445247580Smm ASSERT0(err); 446247580Smm ASSERT3U(val, ==, obj); 447247580Smm } 448247580Smm#endif 449265744Sdelphij VERIFY0(dsl_dataset_snap_remove(ds_head, ds->ds_snapname, tx, B_TRUE)); 450247580Smm dsl_dataset_rele(ds_head, FTAG); 451247580Smm 452247580Smm if (ds_prev != NULL) 453247580Smm dsl_dataset_rele(ds_prev, FTAG); 454247580Smm 455247580Smm spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx); 456247580Smm 457277585Sdelphij if (dsl_dataset_phys(ds)->ds_next_clones_obj != 0) { 458247580Smm uint64_t count; 459247580Smm ASSERT0(zap_count(mos, 460277585Sdelphij dsl_dataset_phys(ds)->ds_next_clones_obj, &count) && 461277585Sdelphij count == 0); 462247580Smm VERIFY0(dmu_object_free(mos, 463277585Sdelphij dsl_dataset_phys(ds)->ds_next_clones_obj, tx)); 464247580Smm } 465277585Sdelphij if (dsl_dataset_phys(ds)->ds_props_obj != 0) 466277585Sdelphij VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_props_obj, 467277585Sdelphij tx)); 468277585Sdelphij if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) 469277585Sdelphij VERIFY0(zap_destroy(mos, dsl_dataset_phys(ds)->ds_userrefs_obj, 470277585Sdelphij tx)); 471247580Smm dsl_dir_rele(ds->ds_dir, ds); 472247580Smm ds->ds_dir = NULL; 473263390Sdelphij dmu_object_free_zapified(mos, obj, tx); 474247580Smm} 475247580Smm 476247580Smmstatic void 477247580Smmdsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx) 478247580Smm{ 479247580Smm dmu_snapshots_destroy_arg_t *dsda = arg; 480247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 481247580Smm nvpair_t *pair; 482247580Smm 483247580Smm for (pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, NULL); 484247580Smm pair != NULL; 485247580Smm pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, pair)) { 486247580Smm dsl_dataset_t *ds; 487247580Smm 488247580Smm VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); 489247580Smm 490247580Smm dsl_destroy_snapshot_sync_impl(ds, dsda->dsda_defer, tx); 491247580Smm dsl_dataset_rele(ds, FTAG); 492247580Smm } 493247580Smm} 494247580Smm 495247580Smm/* 496247580Smm * The semantics of this function are described in the comment above 497247580Smm * lzc_destroy_snaps(). To summarize: 498247580Smm * 499247580Smm * The snapshots must all be in the same pool. 500247580Smm * 501247580Smm * Snapshots that don't exist will be silently ignored (considered to be 502247580Smm * "already deleted"). 503247580Smm * 504247580Smm * On success, all snaps will be destroyed and this will return 0. 505247580Smm * On failure, no snaps will be destroyed, the errlist will be filled in, 506247580Smm * and this will return an errno. 507247580Smm */ 508247580Smmint 509247580Smmdsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer, 510247580Smm nvlist_t *errlist) 511247580Smm{ 512247580Smm dmu_snapshots_destroy_arg_t dsda; 513247580Smm int error; 514247580Smm nvpair_t *pair; 515247580Smm 516247580Smm pair = nvlist_next_nvpair(snaps, NULL); 517247580Smm if (pair == NULL) 518247580Smm return (0); 519247580Smm 520247580Smm dsda.dsda_snaps = snaps; 521247580Smm dsda.dsda_successful_snaps = fnvlist_alloc(); 522247580Smm dsda.dsda_defer = defer; 523247580Smm dsda.dsda_errlist = errlist; 524247580Smm 525247580Smm error = dsl_sync_task(nvpair_name(pair), 526247580Smm dsl_destroy_snapshot_check, dsl_destroy_snapshot_sync, 527269006Sdelphij &dsda, 0, ZFS_SPACE_CHECK_NONE); 528247580Smm fnvlist_free(dsda.dsda_successful_snaps); 529247580Smm 530247580Smm return (error); 531247580Smm} 532247580Smm 533247580Smmint 534247580Smmdsl_destroy_snapshot(const char *name, boolean_t defer) 535247580Smm{ 536247580Smm int error; 537247580Smm nvlist_t *nvl = fnvlist_alloc(); 538247580Smm nvlist_t *errlist = fnvlist_alloc(); 539247580Smm 540247580Smm fnvlist_add_boolean(nvl, name); 541247580Smm error = dsl_destroy_snapshots_nvl(nvl, defer, errlist); 542247580Smm fnvlist_free(errlist); 543247580Smm fnvlist_free(nvl); 544247580Smm return (error); 545247580Smm} 546247580Smm 547247580Smmstruct killarg { 548247580Smm dsl_dataset_t *ds; 549247580Smm dmu_tx_t *tx; 550247580Smm}; 551247580Smm 552247580Smm/* ARGSUSED */ 553247580Smmstatic int 554247580Smmkill_blkptr(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 555268657Sdelphij const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) 556247580Smm{ 557247580Smm struct killarg *ka = arg; 558247580Smm dmu_tx_t *tx = ka->tx; 559247580Smm 560288571Smav if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp)) 561247580Smm return (0); 562247580Smm 563247580Smm if (zb->zb_level == ZB_ZIL_LEVEL) { 564247580Smm ASSERT(zilog != NULL); 565247580Smm /* 566247580Smm * It's a block in the intent log. It has no 567247580Smm * accounting, so just free it. 568247580Smm */ 569247580Smm dsl_free(ka->tx->tx_pool, ka->tx->tx_txg, bp); 570247580Smm } else { 571247580Smm ASSERT(zilog == NULL); 572277585Sdelphij ASSERT3U(bp->blk_birth, >, 573277585Sdelphij dsl_dataset_phys(ka->ds)->ds_prev_snap_txg); 574247580Smm (void) dsl_dataset_block_kill(ka->ds, bp, tx, B_FALSE); 575247580Smm } 576247580Smm 577247580Smm return (0); 578247580Smm} 579247580Smm 580247580Smmstatic void 581247580Smmold_synchronous_dataset_destroy(dsl_dataset_t *ds, dmu_tx_t *tx) 582247580Smm{ 583247580Smm struct killarg ka; 584247580Smm 585247580Smm /* 586247580Smm * Free everything that we point to (that's born after 587247580Smm * the previous snapshot, if we are a clone) 588247580Smm * 589247580Smm * NB: this should be very quick, because we already 590247580Smm * freed all the objects in open context. 591247580Smm */ 592247580Smm ka.ds = ds; 593247580Smm ka.tx = tx; 594247580Smm VERIFY0(traverse_dataset(ds, 595277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_txg, TRAVERSE_POST, 596247580Smm kill_blkptr, &ka)); 597277585Sdelphij ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) || 598277585Sdelphij dsl_dataset_phys(ds)->ds_unique_bytes == 0); 599247580Smm} 600247580Smm 601247580Smmtypedef struct dsl_destroy_head_arg { 602247580Smm const char *ddha_name; 603247580Smm} dsl_destroy_head_arg_t; 604247580Smm 605247580Smmint 606247580Smmdsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds) 607247580Smm{ 608247580Smm int error; 609247580Smm uint64_t count; 610247580Smm objset_t *mos; 611247580Smm 612288549Smav ASSERT(!ds->ds_is_snapshot); 613288549Smav if (ds->ds_is_snapshot) 614249195Smm return (SET_ERROR(EINVAL)); 615247580Smm 616247580Smm if (refcount_count(&ds->ds_longholds) != expected_holds) 617249195Smm return (SET_ERROR(EBUSY)); 618247580Smm 619247580Smm mos = ds->ds_dir->dd_pool->dp_meta_objset; 620247580Smm 621247580Smm /* 622247580Smm * Can't delete a head dataset if there are snapshots of it. 623247580Smm * (Except if the only snapshots are from the branch we cloned 624247580Smm * from.) 625247580Smm */ 626247580Smm if (ds->ds_prev != NULL && 627277585Sdelphij dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj == ds->ds_object) 628249195Smm return (SET_ERROR(EBUSY)); 629247580Smm 630247580Smm /* 631247580Smm * Can't delete if there are children of this fs. 632247580Smm */ 633247580Smm error = zap_count(mos, 634277585Sdelphij dsl_dir_phys(ds->ds_dir)->dd_child_dir_zapobj, &count); 635247580Smm if (error != 0) 636247580Smm return (error); 637247580Smm if (count != 0) 638249195Smm return (SET_ERROR(EEXIST)); 639247580Smm 640247580Smm if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev) && 641277585Sdelphij dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 && 642247580Smm ds->ds_prev->ds_userrefs == 0) { 643247580Smm /* We need to remove the origin snapshot as well. */ 644247580Smm if (!refcount_is_zero(&ds->ds_prev->ds_longholds)) 645249195Smm return (SET_ERROR(EBUSY)); 646247580Smm } 647247580Smm return (0); 648247580Smm} 649247580Smm 650247580Smmstatic int 651247580Smmdsl_destroy_head_check(void *arg, dmu_tx_t *tx) 652247580Smm{ 653247580Smm dsl_destroy_head_arg_t *ddha = arg; 654247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 655247580Smm dsl_dataset_t *ds; 656247580Smm int error; 657247580Smm 658247580Smm error = dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds); 659247580Smm if (error != 0) 660247580Smm return (error); 661247580Smm 662247580Smm error = dsl_destroy_head_check_impl(ds, 0); 663247580Smm dsl_dataset_rele(ds, FTAG); 664247580Smm return (error); 665247580Smm} 666247580Smm 667247580Smmstatic void 668247580Smmdsl_dir_destroy_sync(uint64_t ddobj, dmu_tx_t *tx) 669247580Smm{ 670247580Smm dsl_dir_t *dd; 671247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 672247580Smm objset_t *mos = dp->dp_meta_objset; 673247580Smm dd_used_t t; 674247580Smm 675247580Smm ASSERT(RRW_WRITE_HELD(&dmu_tx_pool(tx)->dp_config_rwlock)); 676247580Smm 677247580Smm VERIFY0(dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd)); 678247580Smm 679277585Sdelphij ASSERT0(dsl_dir_phys(dd)->dd_head_dataset_obj); 680247580Smm 681247580Smm /* 682265744Sdelphij * Decrement the filesystem count for all parent filesystems. 683265744Sdelphij * 684265744Sdelphij * When we receive an incremental stream into a filesystem that already 685265744Sdelphij * exists, a temporary clone is created. We never count this temporary 686265744Sdelphij * clone, whose name begins with a '%'. 687265744Sdelphij */ 688265744Sdelphij if (dd->dd_myname[0] != '%' && dd->dd_parent != NULL) 689265744Sdelphij dsl_fs_ss_count_adjust(dd->dd_parent, -1, 690265744Sdelphij DD_FIELD_FILESYSTEM_COUNT, tx); 691265744Sdelphij 692265744Sdelphij /* 693247580Smm * Remove our reservation. The impl() routine avoids setting the 694247580Smm * actual property, which would require the (already destroyed) ds. 695247580Smm */ 696247580Smm dsl_dir_set_reservation_sync_impl(dd, 0, tx); 697247580Smm 698277585Sdelphij ASSERT0(dsl_dir_phys(dd)->dd_used_bytes); 699277585Sdelphij ASSERT0(dsl_dir_phys(dd)->dd_reserved); 700247580Smm for (t = 0; t < DD_USED_NUM; t++) 701277585Sdelphij ASSERT0(dsl_dir_phys(dd)->dd_used_breakdown[t]); 702247580Smm 703277585Sdelphij VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_child_dir_zapobj, tx)); 704277585Sdelphij VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_props_zapobj, tx)); 705277585Sdelphij VERIFY0(dsl_deleg_destroy(mos, dsl_dir_phys(dd)->dd_deleg_zapobj, tx)); 706247580Smm VERIFY0(zap_remove(mos, 707277585Sdelphij dsl_dir_phys(dd->dd_parent)->dd_child_dir_zapobj, 708277585Sdelphij dd->dd_myname, tx)); 709247580Smm 710247580Smm dsl_dir_rele(dd, FTAG); 711263390Sdelphij dmu_object_free_zapified(mos, ddobj, tx); 712247580Smm} 713247580Smm 714247580Smmvoid 715247580Smmdsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx) 716247580Smm{ 717247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 718247580Smm objset_t *mos = dp->dp_meta_objset; 719247580Smm uint64_t obj, ddobj, prevobj = 0; 720247580Smm boolean_t rmorigin; 721247580Smm 722277585Sdelphij ASSERT3U(dsl_dataset_phys(ds)->ds_num_children, <=, 1); 723247580Smm ASSERT(ds->ds_prev == NULL || 724277585Sdelphij dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj != ds->ds_object); 725308083Smav rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG); 726277585Sdelphij ASSERT3U(dsl_dataset_phys(ds)->ds_bp.blk_birth, <=, tx->tx_txg); 727308083Smav rrw_exit(&ds->ds_bp_rwlock, FTAG); 728247580Smm ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); 729247580Smm 730247580Smm /* We need to log before removing it from the namespace. */ 731247580Smm spa_history_log_internal_ds(ds, "destroy", tx, ""); 732247580Smm 733247580Smm rmorigin = (dsl_dir_is_clone(ds->ds_dir) && 734247580Smm DS_IS_DEFER_DESTROY(ds->ds_prev) && 735277585Sdelphij dsl_dataset_phys(ds->ds_prev)->ds_num_children == 2 && 736247580Smm ds->ds_prev->ds_userrefs == 0); 737247580Smm 738268649Sdelphij /* Remove our reservation. */ 739247580Smm if (ds->ds_reserved != 0) { 740247580Smm dsl_dataset_set_refreservation_sync_impl(ds, 741247580Smm (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED), 742247580Smm 0, tx); 743247580Smm ASSERT0(ds->ds_reserved); 744247580Smm } 745247580Smm 746288572Smav obj = ds->ds_object; 747276081Sdelphij 748288572Smav for (spa_feature_t f = 0; f < SPA_FEATURES; f++) { 749288572Smav if (ds->ds_feature_inuse[f]) { 750288572Smav dsl_dataset_deactivate_feature(obj, f, tx); 751288572Smav ds->ds_feature_inuse[f] = B_FALSE; 752288572Smav } 753288572Smav } 754288572Smav 755247580Smm dsl_scan_ds_destroyed(ds, tx); 756247580Smm 757277585Sdelphij if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) { 758247580Smm /* This is a clone */ 759247580Smm ASSERT(ds->ds_prev != NULL); 760277585Sdelphij ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_next_snap_obj, !=, 761277585Sdelphij obj); 762277585Sdelphij ASSERT0(dsl_dataset_phys(ds)->ds_next_snap_obj); 763247580Smm 764247580Smm dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx); 765277585Sdelphij if (dsl_dataset_phys(ds->ds_prev)->ds_next_clones_obj != 0) { 766247580Smm dsl_dataset_remove_from_next_clones(ds->ds_prev, 767247580Smm obj, tx); 768247580Smm } 769247580Smm 770277585Sdelphij ASSERT3U(dsl_dataset_phys(ds->ds_prev)->ds_num_children, >, 1); 771277585Sdelphij dsl_dataset_phys(ds->ds_prev)->ds_num_children--; 772247580Smm } 773247580Smm 774247580Smm /* 775247580Smm * Destroy the deadlist. Unless it's a clone, the 776247580Smm * deadlist should be empty. (If it's a clone, it's 777247580Smm * safe to ignore the deadlist contents.) 778247580Smm */ 779247580Smm dsl_deadlist_close(&ds->ds_deadlist); 780277585Sdelphij dsl_deadlist_free(mos, dsl_dataset_phys(ds)->ds_deadlist_obj, tx); 781247580Smm dmu_buf_will_dirty(ds->ds_dbuf, tx); 782277585Sdelphij dsl_dataset_phys(ds)->ds_deadlist_obj = 0; 783247580Smm 784263390Sdelphij objset_t *os; 785247580Smm VERIFY0(dmu_objset_from_ds(ds, &os)); 786247580Smm 787263390Sdelphij if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY)) { 788247580Smm old_synchronous_dataset_destroy(ds, tx); 789247580Smm } else { 790247580Smm /* 791247580Smm * Move the bptree into the pool's list of trees to 792247580Smm * clean up and update space accounting information. 793247580Smm */ 794247580Smm uint64_t used, comp, uncomp; 795247580Smm 796247580Smm zil_destroy_sync(dmu_objset_zil(os), tx); 797247580Smm 798263390Sdelphij if (!spa_feature_is_active(dp->dp_spa, 799263390Sdelphij SPA_FEATURE_ASYNC_DESTROY)) { 800249858Smm dsl_scan_t *scn = dp->dp_scan; 801263390Sdelphij spa_feature_incr(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY, 802263390Sdelphij tx); 803247580Smm dp->dp_bptree_obj = bptree_alloc(mos, tx); 804247580Smm VERIFY0(zap_add(mos, 805247580Smm DMU_POOL_DIRECTORY_OBJECT, 806247580Smm DMU_POOL_BPTREE_OBJ, sizeof (uint64_t), 1, 807247580Smm &dp->dp_bptree_obj, tx)); 808249858Smm ASSERT(!scn->scn_async_destroying); 809249858Smm scn->scn_async_destroying = B_TRUE; 810247580Smm } 811247580Smm 812277585Sdelphij used = dsl_dir_phys(ds->ds_dir)->dd_used_bytes; 813277585Sdelphij comp = dsl_dir_phys(ds->ds_dir)->dd_compressed_bytes; 814277585Sdelphij uncomp = dsl_dir_phys(ds->ds_dir)->dd_uncompressed_bytes; 815247580Smm 816247580Smm ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) || 817277585Sdelphij dsl_dataset_phys(ds)->ds_unique_bytes == used); 818247580Smm 819308083Smav rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG); 820247580Smm bptree_add(mos, dp->dp_bptree_obj, 821277585Sdelphij &dsl_dataset_phys(ds)->ds_bp, 822277585Sdelphij dsl_dataset_phys(ds)->ds_prev_snap_txg, 823247580Smm used, comp, uncomp, tx); 824308083Smav rrw_exit(&ds->ds_bp_rwlock, FTAG); 825247580Smm dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, 826247580Smm -used, -comp, -uncomp, tx); 827247580Smm dsl_dir_diduse_space(dp->dp_free_dir, DD_USED_HEAD, 828247580Smm used, comp, uncomp, tx); 829247580Smm } 830247580Smm 831247580Smm if (ds->ds_prev != NULL) { 832247580Smm if (spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) { 833247580Smm VERIFY0(zap_remove_int(mos, 834277585Sdelphij dsl_dir_phys(ds->ds_prev->ds_dir)->dd_clones, 835247580Smm ds->ds_object, tx)); 836247580Smm } 837247580Smm prevobj = ds->ds_prev->ds_object; 838247580Smm dsl_dataset_rele(ds->ds_prev, ds); 839247580Smm ds->ds_prev = NULL; 840247580Smm } 841247580Smm 842247580Smm /* 843247580Smm * This must be done after the dsl_traverse(), because it will 844247580Smm * re-open the objset. 845247580Smm */ 846247580Smm if (ds->ds_objset) { 847247580Smm dmu_objset_evict(ds->ds_objset); 848247580Smm ds->ds_objset = NULL; 849247580Smm } 850247580Smm 851247580Smm /* Erase the link in the dir */ 852247580Smm dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx); 853277585Sdelphij dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj = 0; 854247580Smm ddobj = ds->ds_dir->dd_object; 855277585Sdelphij ASSERT(dsl_dataset_phys(ds)->ds_snapnames_zapobj != 0); 856277585Sdelphij VERIFY0(zap_destroy(mos, 857277585Sdelphij dsl_dataset_phys(ds)->ds_snapnames_zapobj, tx)); 858247580Smm 859263407Sdelphij if (ds->ds_bookmarks != 0) { 860277585Sdelphij VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); 861263407Sdelphij spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 862263407Sdelphij } 863263407Sdelphij 864247580Smm spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx); 865247580Smm 866277585Sdelphij ASSERT0(dsl_dataset_phys(ds)->ds_next_clones_obj); 867277585Sdelphij ASSERT0(dsl_dataset_phys(ds)->ds_props_obj); 868277585Sdelphij ASSERT0(dsl_dataset_phys(ds)->ds_userrefs_obj); 869247580Smm dsl_dir_rele(ds->ds_dir, ds); 870247580Smm ds->ds_dir = NULL; 871263390Sdelphij dmu_object_free_zapified(mos, obj, tx); 872247580Smm 873247580Smm dsl_dir_destroy_sync(ddobj, tx); 874247580Smm 875247580Smm if (rmorigin) { 876247580Smm dsl_dataset_t *prev; 877247580Smm VERIFY0(dsl_dataset_hold_obj(dp, prevobj, FTAG, &prev)); 878247580Smm dsl_destroy_snapshot_sync_impl(prev, B_FALSE, tx); 879247580Smm dsl_dataset_rele(prev, FTAG); 880247580Smm } 881247580Smm} 882247580Smm 883247580Smmstatic void 884247580Smmdsl_destroy_head_sync(void *arg, dmu_tx_t *tx) 885247580Smm{ 886247580Smm dsl_destroy_head_arg_t *ddha = arg; 887247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 888247580Smm dsl_dataset_t *ds; 889247580Smm 890247580Smm VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds)); 891247580Smm dsl_destroy_head_sync_impl(ds, tx); 892247580Smm dsl_dataset_rele(ds, FTAG); 893247580Smm} 894247580Smm 895247580Smmstatic void 896247580Smmdsl_destroy_head_begin_sync(void *arg, dmu_tx_t *tx) 897247580Smm{ 898247580Smm dsl_destroy_head_arg_t *ddha = arg; 899247580Smm dsl_pool_t *dp = dmu_tx_pool(tx); 900247580Smm dsl_dataset_t *ds; 901247580Smm 902247580Smm VERIFY0(dsl_dataset_hold(dp, ddha->ddha_name, FTAG, &ds)); 903247580Smm 904247580Smm /* Mark it as inconsistent on-disk, in case we crash */ 905247580Smm dmu_buf_will_dirty(ds->ds_dbuf, tx); 906277585Sdelphij dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT; 907247580Smm 908247580Smm spa_history_log_internal_ds(ds, "destroy begin", tx, ""); 909247580Smm dsl_dataset_rele(ds, FTAG); 910247580Smm} 911247580Smm 912247580Smmint 913247580Smmdsl_destroy_head(const char *name) 914247580Smm{ 915247580Smm dsl_destroy_head_arg_t ddha; 916247580Smm int error; 917247580Smm spa_t *spa; 918247580Smm boolean_t isenabled; 919247580Smm 920247580Smm#ifdef _KERNEL 921247580Smm zfs_destroy_unmount_origin(name); 922247580Smm#endif 923247580Smm 924247580Smm error = spa_open(name, &spa, FTAG); 925247580Smm if (error != 0) 926247580Smm return (error); 927263390Sdelphij isenabled = spa_feature_is_enabled(spa, SPA_FEATURE_ASYNC_DESTROY); 928247580Smm spa_close(spa, FTAG); 929247580Smm 930247580Smm ddha.ddha_name = name; 931247580Smm 932247580Smm if (!isenabled) { 933247580Smm objset_t *os; 934247580Smm 935247580Smm error = dsl_sync_task(name, dsl_destroy_head_check, 936269006Sdelphij dsl_destroy_head_begin_sync, &ddha, 937269006Sdelphij 0, ZFS_SPACE_CHECK_NONE); 938247580Smm if (error != 0) 939247580Smm return (error); 940247580Smm 941247580Smm /* 942247580Smm * Head deletion is processed in one txg on old pools; 943247580Smm * remove the objects from open context so that the txg sync 944247580Smm * is not too long. 945247580Smm */ 946247580Smm error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, FTAG, &os); 947247580Smm if (error == 0) { 948247580Smm uint64_t prev_snap_txg = 949277585Sdelphij dsl_dataset_phys(dmu_objset_ds(os))-> 950277585Sdelphij ds_prev_snap_txg; 951247580Smm for (uint64_t obj = 0; error == 0; 952247580Smm error = dmu_object_next(os, &obj, FALSE, 953247580Smm prev_snap_txg)) 954254753Sdelphij (void) dmu_free_long_object(os, obj); 955247580Smm /* sync out all frees */ 956247580Smm txg_wait_synced(dmu_objset_pool(os), 0); 957247580Smm dmu_objset_disown(os, FTAG); 958247580Smm } 959247580Smm } 960247580Smm 961247580Smm return (dsl_sync_task(name, dsl_destroy_head_check, 962269006Sdelphij dsl_destroy_head_sync, &ddha, 0, ZFS_SPACE_CHECK_NONE)); 963247580Smm} 964247580Smm 965247580Smm/* 966247580Smm * Note, this function is used as the callback for dmu_objset_find(). We 967247580Smm * always return 0 so that we will continue to find and process 968247580Smm * inconsistent datasets, even if we encounter an error trying to 969247580Smm * process one of them. 970247580Smm */ 971247580Smm/* ARGSUSED */ 972247580Smmint 973247580Smmdsl_destroy_inconsistent(const char *dsname, void *arg) 974247580Smm{ 975247580Smm objset_t *os; 976247580Smm 977247580Smm if (dmu_objset_hold(dsname, FTAG, &os) == 0) { 978290756Smav boolean_t need_destroy = DS_IS_INCONSISTENT(dmu_objset_ds(os)); 979290756Smav 980290756Smav /* 981290756Smav * If the dataset is inconsistent because a resumable receive 982290756Smav * has failed, then do not destroy it. 983290756Smav */ 984290756Smav if (dsl_dataset_has_resume_receive_state(dmu_objset_ds(os))) 985290756Smav need_destroy = B_FALSE; 986290756Smav 987247580Smm dmu_objset_rele(os, FTAG); 988290756Smav if (need_destroy) 989247580Smm (void) dsl_destroy_head(dsname); 990247580Smm } 991247580Smm return (0); 992247580Smm} 993