1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 2212115SChris.Kirby@oracle.com * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23789Sahrens */ 24789Sahrens 25789Sahrens #include <sys/dmu_objset.h> 26789Sahrens #include <sys/dsl_dataset.h> 27789Sahrens #include <sys/dsl_dir.h> 282082Seschrock #include <sys/dsl_prop.h> 292199Sahrens #include <sys/dsl_synctask.h> 30789Sahrens #include <sys/dmu_traverse.h> 31789Sahrens #include <sys/dmu_tx.h> 32789Sahrens #include <sys/arc.h> 33789Sahrens #include <sys/zio.h> 34789Sahrens #include <sys/zap.h> 35789Sahrens #include <sys/unique.h> 36789Sahrens #include <sys/zfs_context.h> 374007Smmusante #include <sys/zfs_ioctl.h> 384543Smarks #include <sys/spa.h> 397046Sahrens #include <sys/zfs_znode.h> 4012527SChris.Kirby@oracle.com #include <sys/zfs_onexit.h> 4110242Schris.kirby@sun.com #include <sys/zvol.h> 4212296SLin.Ling@Sun.COM #include <sys/dsl_scan.h> 4312470SMatthew.Ahrens@Sun.COM #include <sys/dsl_deadlist.h> 44789Sahrens 456689Smaybee static char *dsl_reaper = "the grim reaper"; 466689Smaybee 472199Sahrens static dsl_checkfunc_t dsl_dataset_destroy_begin_check; 482199Sahrens static dsl_syncfunc_t dsl_dataset_destroy_begin_sync; 495378Sck153898 static dsl_syncfunc_t dsl_dataset_set_reservation_sync; 501731Sbonwick 5112470SMatthew.Ahrens@Sun.COM #define SWITCH64(x, y) \ 5212470SMatthew.Ahrens@Sun.COM { \ 5312470SMatthew.Ahrens@Sun.COM uint64_t __tmp = (x); \ 5412470SMatthew.Ahrens@Sun.COM (x) = (y); \ 5512470SMatthew.Ahrens@Sun.COM (y) = __tmp; \ 5612470SMatthew.Ahrens@Sun.COM } 5712470SMatthew.Ahrens@Sun.COM 583444Sek110237 #define DS_REF_MAX (1ULL << 62) 59789Sahrens 60789Sahrens #define DSL_DEADLIST_BLOCKSIZE SPA_MAXBLOCKSIZE 61789Sahrens 626689Smaybee #define DSL_DATASET_IS_DESTROYED(ds) ((ds)->ds_owner == dsl_reaper) 636689Smaybee 64789Sahrens 655378Sck153898 /* 665378Sck153898 * Figure out how much of this delta should be propogated to the dsl_dir 675378Sck153898 * layer. If there's a refreservation, that space has already been 685378Sck153898 * partially accounted for in our ancestors. 695378Sck153898 */ 705378Sck153898 static int64_t 715378Sck153898 parent_delta(dsl_dataset_t *ds, int64_t delta) 725378Sck153898 { 735378Sck153898 uint64_t old_bytes, new_bytes; 745378Sck153898 755378Sck153898 if (ds->ds_reserved == 0) 765378Sck153898 return (delta); 775378Sck153898 785378Sck153898 old_bytes = MAX(ds->ds_phys->ds_unique_bytes, ds->ds_reserved); 795378Sck153898 new_bytes = MAX(ds->ds_phys->ds_unique_bytes + delta, ds->ds_reserved); 805378Sck153898 815378Sck153898 ASSERT3U(ABS((int64_t)(new_bytes - old_bytes)), <=, ABS(delta)); 825378Sck153898 return (new_bytes - old_bytes); 835378Sck153898 } 84789Sahrens 85789Sahrens void 8610922SJeff.Bonwick@Sun.COM dsl_dataset_block_born(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx) 87789Sahrens { 8810922SJeff.Bonwick@Sun.COM int used = bp_get_dsize_sync(tx->tx_pool->dp_spa, bp); 89789Sahrens int compressed = BP_GET_PSIZE(bp); 90789Sahrens int uncompressed = BP_GET_UCSIZE(bp); 915378Sck153898 int64_t delta; 92789Sahrens 9312296SLin.Ling@Sun.COM dprintf_bp(bp, "ds=%p", ds); 94789Sahrens 95789Sahrens ASSERT(dmu_tx_is_syncing(tx)); 96789Sahrens /* It could have been compressed away to nothing */ 97789Sahrens if (BP_IS_HOLE(bp)) 98789Sahrens return; 99789Sahrens ASSERT(BP_GET_TYPE(bp) != DMU_OT_NONE); 100789Sahrens ASSERT3U(BP_GET_TYPE(bp), <, DMU_OT_NUMTYPES); 101789Sahrens if (ds == NULL) { 102789Sahrens /* 103789Sahrens * Account for the meta-objset space in its placeholder 104789Sahrens * dsl_dir. 105789Sahrens */ 106789Sahrens ASSERT3U(compressed, ==, uncompressed); /* it's all metadata */ 1077390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(tx->tx_pool->dp_mos_dir, DD_USED_HEAD, 108789Sahrens used, compressed, uncompressed, tx); 109789Sahrens dsl_dir_dirty(tx->tx_pool->dp_mos_dir, tx); 110789Sahrens return; 111789Sahrens } 112789Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 11312296SLin.Ling@Sun.COM 1147595SMatthew.Ahrens@Sun.COM mutex_enter(&ds->ds_dir->dd_lock); 115789Sahrens mutex_enter(&ds->ds_lock); 1165378Sck153898 delta = parent_delta(ds, used); 117789Sahrens ds->ds_phys->ds_used_bytes += used; 118789Sahrens ds->ds_phys->ds_compressed_bytes += compressed; 119789Sahrens ds->ds_phys->ds_uncompressed_bytes += uncompressed; 120789Sahrens ds->ds_phys->ds_unique_bytes += used; 121789Sahrens mutex_exit(&ds->ds_lock); 1227390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, delta, 1237390SMatthew.Ahrens@Sun.COM compressed, uncompressed, tx); 1247390SMatthew.Ahrens@Sun.COM dsl_dir_transfer_space(ds->ds_dir, used - delta, 1257390SMatthew.Ahrens@Sun.COM DD_USED_REFRSRV, DD_USED_HEAD, tx); 1267595SMatthew.Ahrens@Sun.COM mutex_exit(&ds->ds_dir->dd_lock); 127789Sahrens } 128789Sahrens 1296992Smaybee int 13010922SJeff.Bonwick@Sun.COM dsl_dataset_block_kill(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx, 13110922SJeff.Bonwick@Sun.COM boolean_t async) 132789Sahrens { 13310922SJeff.Bonwick@Sun.COM if (BP_IS_HOLE(bp)) 13410922SJeff.Bonwick@Sun.COM return (0); 13510922SJeff.Bonwick@Sun.COM 13610922SJeff.Bonwick@Sun.COM ASSERT(dmu_tx_is_syncing(tx)); 13710922SJeff.Bonwick@Sun.COM ASSERT(bp->blk_birth <= tx->tx_txg); 13810922SJeff.Bonwick@Sun.COM 13910922SJeff.Bonwick@Sun.COM int used = bp_get_dsize_sync(tx->tx_pool->dp_spa, bp); 140789Sahrens int compressed = BP_GET_PSIZE(bp); 141789Sahrens int uncompressed = BP_GET_UCSIZE(bp); 142789Sahrens 143789Sahrens ASSERT(used > 0); 144789Sahrens if (ds == NULL) { 145789Sahrens /* 146789Sahrens * Account for the meta-objset space in its placeholder 147789Sahrens * dataset. 148789Sahrens */ 14910922SJeff.Bonwick@Sun.COM dsl_free(tx->tx_pool, tx->tx_txg, bp); 150789Sahrens 1517390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(tx->tx_pool->dp_mos_dir, DD_USED_HEAD, 152789Sahrens -used, -compressed, -uncompressed, tx); 153789Sahrens dsl_dir_dirty(tx->tx_pool->dp_mos_dir, tx); 1546992Smaybee return (used); 155789Sahrens } 156789Sahrens ASSERT3P(tx->tx_pool, ==, ds->ds_dir->dd_pool); 157789Sahrens 1587390SMatthew.Ahrens@Sun.COM ASSERT(!dsl_dataset_is_snapshot(ds)); 159789Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 160789Sahrens 161789Sahrens if (bp->blk_birth > ds->ds_phys->ds_prev_snap_txg) { 1625378Sck153898 int64_t delta; 1633547Smaybee 16412296SLin.Ling@Sun.COM dprintf_bp(bp, "freeing ds=%llu", ds->ds_object); 16510922SJeff.Bonwick@Sun.COM dsl_free(tx->tx_pool, tx->tx_txg, bp); 166789Sahrens 1677595SMatthew.Ahrens@Sun.COM mutex_enter(&ds->ds_dir->dd_lock); 168789Sahrens mutex_enter(&ds->ds_lock); 1695378Sck153898 ASSERT(ds->ds_phys->ds_unique_bytes >= used || 1705378Sck153898 !DS_UNIQUE_IS_ACCURATE(ds)); 1715378Sck153898 delta = parent_delta(ds, -used); 172789Sahrens ds->ds_phys->ds_unique_bytes -= used; 173789Sahrens mutex_exit(&ds->ds_lock); 1747390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, 1755378Sck153898 delta, -compressed, -uncompressed, tx); 1767390SMatthew.Ahrens@Sun.COM dsl_dir_transfer_space(ds->ds_dir, -used - delta, 1777390SMatthew.Ahrens@Sun.COM DD_USED_REFRSRV, DD_USED_HEAD, tx); 1787595SMatthew.Ahrens@Sun.COM mutex_exit(&ds->ds_dir->dd_lock); 179789Sahrens } else { 180789Sahrens dprintf_bp(bp, "putting on dead list: %s", ""); 18110922SJeff.Bonwick@Sun.COM if (async) { 18210922SJeff.Bonwick@Sun.COM /* 18310922SJeff.Bonwick@Sun.COM * We are here as part of zio's write done callback, 18410922SJeff.Bonwick@Sun.COM * which means we're a zio interrupt thread. We can't 18512470SMatthew.Ahrens@Sun.COM * call dsl_deadlist_insert() now because it may block 18610922SJeff.Bonwick@Sun.COM * waiting for I/O. Instead, put bp on the deferred 18710922SJeff.Bonwick@Sun.COM * queue and let dsl_pool_sync() finish the job. 18810922SJeff.Bonwick@Sun.COM */ 18912470SMatthew.Ahrens@Sun.COM bplist_append(&ds->ds_pending_deadlist, bp); 19010922SJeff.Bonwick@Sun.COM } else { 19112470SMatthew.Ahrens@Sun.COM dsl_deadlist_insert(&ds->ds_deadlist, bp, tx); 19210922SJeff.Bonwick@Sun.COM } 1935712Sahrens ASSERT3U(ds->ds_prev->ds_object, ==, 1945712Sahrens ds->ds_phys->ds_prev_snap_obj); 1955712Sahrens ASSERT(ds->ds_prev->ds_phys->ds_num_children > 0); 196789Sahrens /* if (bp->blk_birth > prev prev snap txg) prev unique += bs */ 1975712Sahrens if (ds->ds_prev->ds_phys->ds_next_snap_obj == 1985712Sahrens ds->ds_object && bp->blk_birth > 1995712Sahrens ds->ds_prev->ds_phys->ds_prev_snap_txg) { 2005712Sahrens dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx); 2015712Sahrens mutex_enter(&ds->ds_prev->ds_lock); 2025712Sahrens ds->ds_prev->ds_phys->ds_unique_bytes += used; 2035712Sahrens mutex_exit(&ds->ds_prev->ds_lock); 204789Sahrens } 20512296SLin.Ling@Sun.COM if (bp->blk_birth > ds->ds_dir->dd_origin_txg) { 2067390SMatthew.Ahrens@Sun.COM dsl_dir_transfer_space(ds->ds_dir, used, 2077390SMatthew.Ahrens@Sun.COM DD_USED_HEAD, DD_USED_SNAP, tx); 2087390SMatthew.Ahrens@Sun.COM } 209789Sahrens } 210789Sahrens mutex_enter(&ds->ds_lock); 211789Sahrens ASSERT3U(ds->ds_phys->ds_used_bytes, >=, used); 212789Sahrens ds->ds_phys->ds_used_bytes -= used; 213789Sahrens ASSERT3U(ds->ds_phys->ds_compressed_bytes, >=, compressed); 214789Sahrens ds->ds_phys->ds_compressed_bytes -= compressed; 215789Sahrens ASSERT3U(ds->ds_phys->ds_uncompressed_bytes, >=, uncompressed); 216789Sahrens ds->ds_phys->ds_uncompressed_bytes -= uncompressed; 217789Sahrens mutex_exit(&ds->ds_lock); 2186992Smaybee 2196992Smaybee return (used); 220789Sahrens } 221789Sahrens 2221544Seschrock uint64_t 2231544Seschrock dsl_dataset_prev_snap_txg(dsl_dataset_t *ds) 224789Sahrens { 2252885Sahrens uint64_t trysnap = 0; 2262885Sahrens 227789Sahrens if (ds == NULL) 2281544Seschrock return (0); 229789Sahrens /* 230789Sahrens * The snapshot creation could fail, but that would cause an 231789Sahrens * incorrect FALSE return, which would only result in an 232789Sahrens * overestimation of the amount of space that an operation would 233789Sahrens * consume, which is OK. 234789Sahrens * 235789Sahrens * There's also a small window where we could miss a pending 236789Sahrens * snapshot, because we could set the sync task in the quiescing 237789Sahrens * phase. So this should only be used as a guess. 238789Sahrens */ 2392885Sahrens if (ds->ds_trysnap_txg > 2402885Sahrens spa_last_synced_txg(ds->ds_dir->dd_pool->dp_spa)) 2412885Sahrens trysnap = ds->ds_trysnap_txg; 2422885Sahrens return (MAX(ds->ds_phys->ds_prev_snap_txg, trysnap)); 2431544Seschrock } 2441544Seschrock 2459653SSanjeev.Bagewadi@Sun.COM boolean_t 24612450SGeorge.Wilson@Sun.COM dsl_dataset_block_freeable(dsl_dataset_t *ds, const blkptr_t *bp, 24712450SGeorge.Wilson@Sun.COM uint64_t blk_birth) 2481544Seschrock { 24912450SGeorge.Wilson@Sun.COM if (blk_birth <= dsl_dataset_prev_snap_txg(ds)) 25012450SGeorge.Wilson@Sun.COM return (B_FALSE); 25112450SGeorge.Wilson@Sun.COM 25212587SGeorge.Wilson@Sun.COM ddt_prefetch(dsl_dataset_get_spa(ds), bp); 25312450SGeorge.Wilson@Sun.COM 25412450SGeorge.Wilson@Sun.COM return (B_TRUE); 255789Sahrens } 256789Sahrens 257789Sahrens /* ARGSUSED */ 258789Sahrens static void 259789Sahrens dsl_dataset_evict(dmu_buf_t *db, void *dsv) 260789Sahrens { 261789Sahrens dsl_dataset_t *ds = dsv; 262789Sahrens 2636689Smaybee ASSERT(ds->ds_owner == NULL || DSL_DATASET_IS_DESTROYED(ds)); 264789Sahrens 2654787Sahrens unique_remove(ds->ds_fsid_guid); 266789Sahrens 26710298SMatthew.Ahrens@Sun.COM if (ds->ds_objset != NULL) 26810298SMatthew.Ahrens@Sun.COM dmu_objset_evict(ds->ds_objset); 269789Sahrens 270789Sahrens if (ds->ds_prev) { 2716689Smaybee dsl_dataset_drop_ref(ds->ds_prev, ds); 272789Sahrens ds->ds_prev = NULL; 273789Sahrens } 274789Sahrens 27512470SMatthew.Ahrens@Sun.COM bplist_destroy(&ds->ds_pending_deadlist); 27612470SMatthew.Ahrens@Sun.COM if (db != NULL) { 27712470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds->ds_deadlist); 27812470SMatthew.Ahrens@Sun.COM } else { 27912470SMatthew.Ahrens@Sun.COM ASSERT(ds->ds_deadlist.dl_dbuf == NULL); 28012470SMatthew.Ahrens@Sun.COM ASSERT(!ds->ds_deadlist.dl_oldfmt); 28112470SMatthew.Ahrens@Sun.COM } 2826689Smaybee if (ds->ds_dir) 2836689Smaybee dsl_dir_close(ds->ds_dir, ds); 284789Sahrens 2854787Sahrens ASSERT(!list_link_active(&ds->ds_synced_link)); 286789Sahrens 2872856Snd150628 mutex_destroy(&ds->ds_lock); 28810204SMatthew.Ahrens@Sun.COM mutex_destroy(&ds->ds_recvlock); 2894787Sahrens mutex_destroy(&ds->ds_opening_lock); 2906689Smaybee rw_destroy(&ds->ds_rwlock); 2916689Smaybee cv_destroy(&ds->ds_exclusive_cv); 2922856Snd150628 293789Sahrens kmem_free(ds, sizeof (dsl_dataset_t)); 294789Sahrens } 295789Sahrens 2961544Seschrock static int 297789Sahrens dsl_dataset_get_snapname(dsl_dataset_t *ds) 298789Sahrens { 299789Sahrens dsl_dataset_phys_t *headphys; 300789Sahrens int err; 301789Sahrens dmu_buf_t *headdbuf; 302789Sahrens dsl_pool_t *dp = ds->ds_dir->dd_pool; 303789Sahrens objset_t *mos = dp->dp_meta_objset; 304789Sahrens 305789Sahrens if (ds->ds_snapname[0]) 3061544Seschrock return (0); 307789Sahrens if (ds->ds_phys->ds_next_snap_obj == 0) 3081544Seschrock return (0); 309789Sahrens 3101544Seschrock err = dmu_bonus_hold(mos, ds->ds_dir->dd_phys->dd_head_dataset_obj, 3111544Seschrock FTAG, &headdbuf); 3121544Seschrock if (err) 3131544Seschrock return (err); 314789Sahrens headphys = headdbuf->db_data; 315789Sahrens err = zap_value_search(dp->dp_meta_objset, 3164577Sahrens headphys->ds_snapnames_zapobj, ds->ds_object, 0, ds->ds_snapname); 3171544Seschrock dmu_buf_rele(headdbuf, FTAG); 3181544Seschrock return (err); 319789Sahrens } 320789Sahrens 3216492Stimh static int 3226689Smaybee dsl_dataset_snap_lookup(dsl_dataset_t *ds, const char *name, uint64_t *value) 3236492Stimh { 3246689Smaybee objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 3256689Smaybee uint64_t snapobj = ds->ds_phys->ds_snapnames_zapobj; 3266492Stimh matchtype_t mt; 3276492Stimh int err; 3286492Stimh 3296689Smaybee if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET) 3306492Stimh mt = MT_FIRST; 3316492Stimh else 3326492Stimh mt = MT_EXACT; 3336492Stimh 3346689Smaybee err = zap_lookup_norm(mos, snapobj, name, 8, 1, 3356492Stimh value, mt, NULL, 0, NULL); 3366492Stimh if (err == ENOTSUP && mt == MT_FIRST) 3376689Smaybee err = zap_lookup(mos, snapobj, name, 8, 1, value); 3386492Stimh return (err); 3396492Stimh } 3406492Stimh 3416492Stimh static int 3426689Smaybee dsl_dataset_snap_remove(dsl_dataset_t *ds, char *name, dmu_tx_t *tx) 3436492Stimh { 3446689Smaybee objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 3456689Smaybee uint64_t snapobj = ds->ds_phys->ds_snapnames_zapobj; 3466492Stimh matchtype_t mt; 3476492Stimh int err; 3486492Stimh 34910373Schris.kirby@sun.com dsl_dir_snap_cmtime_update(ds->ds_dir); 35010373Schris.kirby@sun.com 3516689Smaybee if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET) 3526492Stimh mt = MT_FIRST; 3536492Stimh else 3546492Stimh mt = MT_EXACT; 3556492Stimh 3566689Smaybee err = zap_remove_norm(mos, snapobj, name, mt, tx); 3576492Stimh if (err == ENOTSUP && mt == MT_FIRST) 3586689Smaybee err = zap_remove(mos, snapobj, name, tx); 3596492Stimh return (err); 3606492Stimh } 3616492Stimh 3626689Smaybee static int 3636689Smaybee dsl_dataset_get_ref(dsl_pool_t *dp, uint64_t dsobj, void *tag, 3646689Smaybee dsl_dataset_t **dsp) 365789Sahrens { 366789Sahrens objset_t *mos = dp->dp_meta_objset; 367789Sahrens dmu_buf_t *dbuf; 368789Sahrens dsl_dataset_t *ds; 3691544Seschrock int err; 37012786SChris.Kirby@oracle.com dmu_object_info_t doi; 371789Sahrens 372789Sahrens ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock) || 373789Sahrens dsl_pool_sync_context(dp)); 374789Sahrens 3751544Seschrock err = dmu_bonus_hold(mos, dsobj, tag, &dbuf); 3761544Seschrock if (err) 3771544Seschrock return (err); 37812786SChris.Kirby@oracle.com 37912786SChris.Kirby@oracle.com /* Make sure dsobj has the correct object type. */ 38012786SChris.Kirby@oracle.com dmu_object_info_from_db(dbuf, &doi); 38112786SChris.Kirby@oracle.com if (doi.doi_type != DMU_OT_DSL_DATASET) 38212786SChris.Kirby@oracle.com return (EINVAL); 38312786SChris.Kirby@oracle.com 384789Sahrens ds = dmu_buf_get_user(dbuf); 385789Sahrens if (ds == NULL) { 386789Sahrens dsl_dataset_t *winner; 387789Sahrens 388789Sahrens ds = kmem_zalloc(sizeof (dsl_dataset_t), KM_SLEEP); 389789Sahrens ds->ds_dbuf = dbuf; 390789Sahrens ds->ds_object = dsobj; 391789Sahrens ds->ds_phys = dbuf->db_data; 392789Sahrens 3932856Snd150628 mutex_init(&ds->ds_lock, NULL, MUTEX_DEFAULT, NULL); 39410204SMatthew.Ahrens@Sun.COM mutex_init(&ds->ds_recvlock, NULL, MUTEX_DEFAULT, NULL); 3954787Sahrens mutex_init(&ds->ds_opening_lock, NULL, MUTEX_DEFAULT, NULL); 3966689Smaybee rw_init(&ds->ds_rwlock, 0, 0, 0); 3976689Smaybee cv_init(&ds->ds_exclusive_cv, NULL, CV_DEFAULT, NULL); 39812470SMatthew.Ahrens@Sun.COM 39912470SMatthew.Ahrens@Sun.COM bplist_create(&ds->ds_pending_deadlist); 40012470SMatthew.Ahrens@Sun.COM dsl_deadlist_open(&ds->ds_deadlist, 401789Sahrens mos, ds->ds_phys->ds_deadlist_obj); 40212470SMatthew.Ahrens@Sun.COM 4031544Seschrock if (err == 0) { 4041544Seschrock err = dsl_dir_open_obj(dp, 4051544Seschrock ds->ds_phys->ds_dir_obj, NULL, ds, &ds->ds_dir); 4061544Seschrock } 4071544Seschrock if (err) { 4082856Snd150628 mutex_destroy(&ds->ds_lock); 40910204SMatthew.Ahrens@Sun.COM mutex_destroy(&ds->ds_recvlock); 4104787Sahrens mutex_destroy(&ds->ds_opening_lock); 4116689Smaybee rw_destroy(&ds->ds_rwlock); 4126689Smaybee cv_destroy(&ds->ds_exclusive_cv); 41312470SMatthew.Ahrens@Sun.COM bplist_destroy(&ds->ds_pending_deadlist); 41412470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds->ds_deadlist); 4151544Seschrock kmem_free(ds, sizeof (dsl_dataset_t)); 4161544Seschrock dmu_buf_rele(dbuf, tag); 4171544Seschrock return (err); 4181544Seschrock } 419789Sahrens 4207390SMatthew.Ahrens@Sun.COM if (!dsl_dataset_is_snapshot(ds)) { 421789Sahrens ds->ds_snapname[0] = '\0'; 422789Sahrens if (ds->ds_phys->ds_prev_snap_obj) { 4236689Smaybee err = dsl_dataset_get_ref(dp, 4246689Smaybee ds->ds_phys->ds_prev_snap_obj, 4256689Smaybee ds, &ds->ds_prev); 426789Sahrens } 42710242Schris.kirby@sun.com } else { 42810242Schris.kirby@sun.com if (zfs_flags & ZFS_DEBUG_SNAPNAMES) 42910242Schris.kirby@sun.com err = dsl_dataset_get_snapname(ds); 43010242Schris.kirby@sun.com if (err == 0 && ds->ds_phys->ds_userrefs_obj != 0) { 43110242Schris.kirby@sun.com err = zap_count( 43210242Schris.kirby@sun.com ds->ds_dir->dd_pool->dp_meta_objset, 43310242Schris.kirby@sun.com ds->ds_phys->ds_userrefs_obj, 43410242Schris.kirby@sun.com &ds->ds_userrefs); 43510242Schris.kirby@sun.com } 436789Sahrens } 437789Sahrens 4387390SMatthew.Ahrens@Sun.COM if (err == 0 && !dsl_dataset_is_snapshot(ds)) { 4395569Sck153898 /* 4405569Sck153898 * In sync context, we're called with either no lock 4415569Sck153898 * or with the write lock. If we're not syncing, 4425569Sck153898 * we're always called with the read lock held. 4435569Sck153898 */ 4445475Sck153898 boolean_t need_lock = 4455569Sck153898 !RW_WRITE_HELD(&dp->dp_config_rwlock) && 4465569Sck153898 dsl_pool_sync_context(dp); 4475475Sck153898 4485475Sck153898 if (need_lock) 4495475Sck153898 rw_enter(&dp->dp_config_rwlock, RW_READER); 4505475Sck153898 4517265Sahrens err = dsl_prop_get_ds(ds, 4525475Sck153898 "refreservation", sizeof (uint64_t), 1, 4535475Sck153898 &ds->ds_reserved, NULL); 4545475Sck153898 if (err == 0) { 4557265Sahrens err = dsl_prop_get_ds(ds, 4565475Sck153898 "refquota", sizeof (uint64_t), 1, 4575475Sck153898 &ds->ds_quota, NULL); 4585475Sck153898 } 4595475Sck153898 4605475Sck153898 if (need_lock) 4615475Sck153898 rw_exit(&dp->dp_config_rwlock); 4625475Sck153898 } else { 4635475Sck153898 ds->ds_reserved = ds->ds_quota = 0; 4645475Sck153898 } 4655475Sck153898 4661544Seschrock if (err == 0) { 4671544Seschrock winner = dmu_buf_set_user_ie(dbuf, ds, &ds->ds_phys, 4681544Seschrock dsl_dataset_evict); 4691544Seschrock } 4701544Seschrock if (err || winner) { 47112470SMatthew.Ahrens@Sun.COM bplist_destroy(&ds->ds_pending_deadlist); 47212470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds->ds_deadlist); 4736689Smaybee if (ds->ds_prev) 4746689Smaybee dsl_dataset_drop_ref(ds->ds_prev, ds); 475789Sahrens dsl_dir_close(ds->ds_dir, ds); 4762856Snd150628 mutex_destroy(&ds->ds_lock); 47710204SMatthew.Ahrens@Sun.COM mutex_destroy(&ds->ds_recvlock); 4784787Sahrens mutex_destroy(&ds->ds_opening_lock); 4796689Smaybee rw_destroy(&ds->ds_rwlock); 4806689Smaybee cv_destroy(&ds->ds_exclusive_cv); 481789Sahrens kmem_free(ds, sizeof (dsl_dataset_t)); 4821544Seschrock if (err) { 4831544Seschrock dmu_buf_rele(dbuf, tag); 4841544Seschrock return (err); 4851544Seschrock } 486789Sahrens ds = winner; 487789Sahrens } else { 4884787Sahrens ds->ds_fsid_guid = 489789Sahrens unique_insert(ds->ds_phys->ds_fsid_guid); 490789Sahrens } 491789Sahrens } 492789Sahrens ASSERT3P(ds->ds_dbuf, ==, dbuf); 493789Sahrens ASSERT3P(ds->ds_phys, ==, dbuf->db_data); 4947046Sahrens ASSERT(ds->ds_phys->ds_prev_snap_obj != 0 || 4957061Sahrens spa_version(dp->dp_spa) < SPA_VERSION_ORIGIN || 4967077Sahrens dp->dp_origin_snap == NULL || ds == dp->dp_origin_snap); 497789Sahrens mutex_enter(&ds->ds_lock); 4986689Smaybee if (!dsl_pool_sync_context(dp) && DSL_DATASET_IS_DESTROYED(ds)) { 499789Sahrens mutex_exit(&ds->ds_lock); 5006689Smaybee dmu_buf_rele(ds->ds_dbuf, tag); 5016689Smaybee return (ENOENT); 5026689Smaybee } 5036689Smaybee mutex_exit(&ds->ds_lock); 5046689Smaybee *dsp = ds; 5056689Smaybee return (0); 5066689Smaybee } 5076689Smaybee 5086689Smaybee static int 5096689Smaybee dsl_dataset_hold_ref(dsl_dataset_t *ds, void *tag) 5106689Smaybee { 5116689Smaybee dsl_pool_t *dp = ds->ds_dir->dd_pool; 5126689Smaybee 5136689Smaybee /* 5146689Smaybee * In syncing context we don't want the rwlock lock: there 5156689Smaybee * may be an existing writer waiting for sync phase to 5166689Smaybee * finish. We don't need to worry about such writers, since 5176689Smaybee * sync phase is single-threaded, so the writer can't be 5186689Smaybee * doing anything while we are active. 5196689Smaybee */ 5206689Smaybee if (dsl_pool_sync_context(dp)) { 5216689Smaybee ASSERT(!DSL_DATASET_IS_DESTROYED(ds)); 5226689Smaybee return (0); 523789Sahrens } 5246689Smaybee 5256689Smaybee /* 5266689Smaybee * Normal users will hold the ds_rwlock as a READER until they 5276689Smaybee * are finished (i.e., call dsl_dataset_rele()). "Owners" will 5286689Smaybee * drop their READER lock after they set the ds_owner field. 5296689Smaybee * 5306689Smaybee * If the dataset is being destroyed, the destroy thread will 5316689Smaybee * obtain a WRITER lock for exclusive access after it's done its 5326689Smaybee * open-context work and then change the ds_owner to 5336689Smaybee * dsl_reaper once destruction is assured. So threads 5346689Smaybee * may block here temporarily, until the "destructability" of 5356689Smaybee * the dataset is determined. 5366689Smaybee */ 5376689Smaybee ASSERT(!RW_WRITE_HELD(&dp->dp_config_rwlock)); 5386689Smaybee mutex_enter(&ds->ds_lock); 5396689Smaybee while (!rw_tryenter(&ds->ds_rwlock, RW_READER)) { 5406689Smaybee rw_exit(&dp->dp_config_rwlock); 5416689Smaybee cv_wait(&ds->ds_exclusive_cv, &ds->ds_lock); 5426689Smaybee if (DSL_DATASET_IS_DESTROYED(ds)) { 5436689Smaybee mutex_exit(&ds->ds_lock); 5446689Smaybee dsl_dataset_drop_ref(ds, tag); 5456689Smaybee rw_enter(&dp->dp_config_rwlock, RW_READER); 5466689Smaybee return (ENOENT); 5476689Smaybee } 54811546SChris.Kirby@sun.com /* 54911546SChris.Kirby@sun.com * The dp_config_rwlock lives above the ds_lock. And 55011546SChris.Kirby@sun.com * we need to check DSL_DATASET_IS_DESTROYED() while 55111546SChris.Kirby@sun.com * holding the ds_lock, so we have to drop and reacquire 55211546SChris.Kirby@sun.com * the ds_lock here. 55311546SChris.Kirby@sun.com */ 55411546SChris.Kirby@sun.com mutex_exit(&ds->ds_lock); 5556689Smaybee rw_enter(&dp->dp_config_rwlock, RW_READER); 55611546SChris.Kirby@sun.com mutex_enter(&ds->ds_lock); 5576689Smaybee } 558789Sahrens mutex_exit(&ds->ds_lock); 5591544Seschrock return (0); 560789Sahrens } 561789Sahrens 562789Sahrens int 5636689Smaybee dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag, 5646689Smaybee dsl_dataset_t **dsp) 5656689Smaybee { 5666689Smaybee int err = dsl_dataset_get_ref(dp, dsobj, tag, dsp); 5676689Smaybee 5686689Smaybee if (err) 5696689Smaybee return (err); 5706689Smaybee return (dsl_dataset_hold_ref(*dsp, tag)); 5716689Smaybee } 5726689Smaybee 5736689Smaybee int 57410298SMatthew.Ahrens@Sun.COM dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, boolean_t inconsistentok, 57510298SMatthew.Ahrens@Sun.COM void *tag, dsl_dataset_t **dsp) 5766689Smaybee { 57710298SMatthew.Ahrens@Sun.COM int err = dsl_dataset_hold_obj(dp, dsobj, tag, dsp); 5786689Smaybee if (err) 5796689Smaybee return (err); 58010298SMatthew.Ahrens@Sun.COM if (!dsl_dataset_tryown(*dsp, inconsistentok, tag)) { 58110298SMatthew.Ahrens@Sun.COM dsl_dataset_rele(*dsp, tag); 5828779SMark.Musante@Sun.COM *dsp = NULL; 5836689Smaybee return (EBUSY); 5846689Smaybee } 5856689Smaybee return (0); 5866689Smaybee } 5876689Smaybee 5886689Smaybee int 5896689Smaybee dsl_dataset_hold(const char *name, void *tag, dsl_dataset_t **dsp) 590789Sahrens { 591789Sahrens dsl_dir_t *dd; 592789Sahrens dsl_pool_t *dp; 5936689Smaybee const char *snapname; 594789Sahrens uint64_t obj; 595789Sahrens int err = 0; 596789Sahrens 5976689Smaybee err = dsl_dir_open_spa(NULL, name, FTAG, &dd, &snapname); 5981544Seschrock if (err) 5991544Seschrock return (err); 600789Sahrens 601789Sahrens dp = dd->dd_pool; 602789Sahrens obj = dd->dd_phys->dd_head_dataset_obj; 603789Sahrens rw_enter(&dp->dp_config_rwlock, RW_READER); 6046689Smaybee if (obj) 6056689Smaybee err = dsl_dataset_get_ref(dp, obj, tag, dsp); 6066689Smaybee else 607789Sahrens err = ENOENT; 6086689Smaybee if (err) 609789Sahrens goto out; 6106689Smaybee 6116689Smaybee err = dsl_dataset_hold_ref(*dsp, tag); 6126689Smaybee 6136689Smaybee /* we may be looking for a snapshot */ 6146689Smaybee if (err == 0 && snapname != NULL) { 6156689Smaybee dsl_dataset_t *ds = NULL; 6166689Smaybee 6176689Smaybee if (*snapname++ != '@') { 6186689Smaybee dsl_dataset_rele(*dsp, tag); 619789Sahrens err = ENOENT; 620789Sahrens goto out; 621789Sahrens } 6226689Smaybee 6236689Smaybee dprintf("looking for snapshot '%s'\n", snapname); 6246689Smaybee err = dsl_dataset_snap_lookup(*dsp, snapname, &obj); 6256689Smaybee if (err == 0) 6266689Smaybee err = dsl_dataset_get_ref(dp, obj, tag, &ds); 6276689Smaybee dsl_dataset_rele(*dsp, tag); 6286689Smaybee 6296689Smaybee ASSERT3U((err == 0), ==, (ds != NULL)); 6306689Smaybee 6316689Smaybee if (ds) { 6326689Smaybee mutex_enter(&ds->ds_lock); 6336689Smaybee if (ds->ds_snapname[0] == 0) 6346689Smaybee (void) strlcpy(ds->ds_snapname, snapname, 6356689Smaybee sizeof (ds->ds_snapname)); 6366689Smaybee mutex_exit(&ds->ds_lock); 6376689Smaybee err = dsl_dataset_hold_ref(ds, tag); 6386689Smaybee *dsp = err ? NULL : ds; 639789Sahrens } 640789Sahrens } 641789Sahrens out: 642789Sahrens rw_exit(&dp->dp_config_rwlock); 643789Sahrens dsl_dir_close(dd, FTAG); 644789Sahrens return (err); 645789Sahrens } 646789Sahrens 647789Sahrens int 64810298SMatthew.Ahrens@Sun.COM dsl_dataset_own(const char *name, boolean_t inconsistentok, 64910298SMatthew.Ahrens@Sun.COM void *tag, dsl_dataset_t **dsp) 650789Sahrens { 65110298SMatthew.Ahrens@Sun.COM int err = dsl_dataset_hold(name, tag, dsp); 6526689Smaybee if (err) 6536689Smaybee return (err); 65410298SMatthew.Ahrens@Sun.COM if (!dsl_dataset_tryown(*dsp, inconsistentok, tag)) { 65510298SMatthew.Ahrens@Sun.COM dsl_dataset_rele(*dsp, tag); 6566689Smaybee return (EBUSY); 6576689Smaybee } 6586689Smaybee return (0); 659789Sahrens } 660789Sahrens 661789Sahrens void 662789Sahrens dsl_dataset_name(dsl_dataset_t *ds, char *name) 663789Sahrens { 664789Sahrens if (ds == NULL) { 665789Sahrens (void) strcpy(name, "mos"); 666789Sahrens } else { 667789Sahrens dsl_dir_name(ds->ds_dir, name); 6681544Seschrock VERIFY(0 == dsl_dataset_get_snapname(ds)); 669789Sahrens if (ds->ds_snapname[0]) { 670789Sahrens (void) strcat(name, "@"); 6716689Smaybee /* 6726689Smaybee * We use a "recursive" mutex so that we 6736689Smaybee * can call dprintf_ds() with ds_lock held. 6746689Smaybee */ 675789Sahrens if (!MUTEX_HELD(&ds->ds_lock)) { 676789Sahrens mutex_enter(&ds->ds_lock); 677789Sahrens (void) strcat(name, ds->ds_snapname); 678789Sahrens mutex_exit(&ds->ds_lock); 679789Sahrens } else { 680789Sahrens (void) strcat(name, ds->ds_snapname); 681789Sahrens } 682789Sahrens } 683789Sahrens } 684789Sahrens } 685789Sahrens 6863978Smmusante static int 6873978Smmusante dsl_dataset_namelen(dsl_dataset_t *ds) 6883978Smmusante { 6893978Smmusante int result; 6903978Smmusante 6913978Smmusante if (ds == NULL) { 6923978Smmusante result = 3; /* "mos" */ 6933978Smmusante } else { 6943978Smmusante result = dsl_dir_namelen(ds->ds_dir); 6953978Smmusante VERIFY(0 == dsl_dataset_get_snapname(ds)); 6963978Smmusante if (ds->ds_snapname[0]) { 6973978Smmusante ++result; /* adding one for the @-sign */ 6983978Smmusante if (!MUTEX_HELD(&ds->ds_lock)) { 6993978Smmusante mutex_enter(&ds->ds_lock); 7003978Smmusante result += strlen(ds->ds_snapname); 7013978Smmusante mutex_exit(&ds->ds_lock); 7023978Smmusante } else { 7033978Smmusante result += strlen(ds->ds_snapname); 7043978Smmusante } 7053978Smmusante } 7063978Smmusante } 7073978Smmusante 7083978Smmusante return (result); 7093978Smmusante } 7103978Smmusante 7117046Sahrens void 7126689Smaybee dsl_dataset_drop_ref(dsl_dataset_t *ds, void *tag) 713789Sahrens { 7141544Seschrock dmu_buf_rele(ds->ds_dbuf, tag); 715789Sahrens } 716789Sahrens 717789Sahrens void 7186689Smaybee dsl_dataset_rele(dsl_dataset_t *ds, void *tag) 7195367Sahrens { 7206689Smaybee if (!dsl_pool_sync_context(ds->ds_dir->dd_pool)) { 7216689Smaybee rw_exit(&ds->ds_rwlock); 7226689Smaybee } 7236689Smaybee dsl_dataset_drop_ref(ds, tag); 7246689Smaybee } 7256689Smaybee 7266689Smaybee void 72710298SMatthew.Ahrens@Sun.COM dsl_dataset_disown(dsl_dataset_t *ds, void *tag) 7286689Smaybee { 72910298SMatthew.Ahrens@Sun.COM ASSERT((ds->ds_owner == tag && ds->ds_dbuf) || 7306689Smaybee (DSL_DATASET_IS_DESTROYED(ds) && ds->ds_dbuf == NULL)); 7316689Smaybee 7325367Sahrens mutex_enter(&ds->ds_lock); 7336689Smaybee ds->ds_owner = NULL; 7346689Smaybee if (RW_WRITE_HELD(&ds->ds_rwlock)) { 7356689Smaybee rw_exit(&ds->ds_rwlock); 7366689Smaybee cv_broadcast(&ds->ds_exclusive_cv); 7376689Smaybee } 7385367Sahrens mutex_exit(&ds->ds_lock); 7396689Smaybee if (ds->ds_dbuf) 74010298SMatthew.Ahrens@Sun.COM dsl_dataset_drop_ref(ds, tag); 7416689Smaybee else 74212470SMatthew.Ahrens@Sun.COM dsl_dataset_evict(NULL, ds); 7435367Sahrens } 7445367Sahrens 7455367Sahrens boolean_t 74610298SMatthew.Ahrens@Sun.COM dsl_dataset_tryown(dsl_dataset_t *ds, boolean_t inconsistentok, void *tag) 7475367Sahrens { 7486689Smaybee boolean_t gotit = FALSE; 7496689Smaybee 7505367Sahrens mutex_enter(&ds->ds_lock); 7516689Smaybee if (ds->ds_owner == NULL && 7526689Smaybee (!DS_IS_INCONSISTENT(ds) || inconsistentok)) { 75310298SMatthew.Ahrens@Sun.COM ds->ds_owner = tag; 7546689Smaybee if (!dsl_pool_sync_context(ds->ds_dir->dd_pool)) 7556689Smaybee rw_exit(&ds->ds_rwlock); 7566689Smaybee gotit = TRUE; 7575367Sahrens } 7585367Sahrens mutex_exit(&ds->ds_lock); 7596689Smaybee return (gotit); 7606689Smaybee } 7616689Smaybee 7626689Smaybee void 7636689Smaybee dsl_dataset_make_exclusive(dsl_dataset_t *ds, void *owner) 7646689Smaybee { 7656689Smaybee ASSERT3P(owner, ==, ds->ds_owner); 7666689Smaybee if (!RW_WRITE_HELD(&ds->ds_rwlock)) 7676689Smaybee rw_enter(&ds->ds_rwlock, RW_WRITER); 7685367Sahrens } 7695367Sahrens 7702199Sahrens uint64_t 7717046Sahrens dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, 7726492Stimh uint64_t flags, dmu_tx_t *tx) 773789Sahrens { 7745367Sahrens dsl_pool_t *dp = dd->dd_pool; 775789Sahrens dmu_buf_t *dbuf; 776789Sahrens dsl_dataset_phys_t *dsphys; 7775367Sahrens uint64_t dsobj; 778789Sahrens objset_t *mos = dp->dp_meta_objset; 779789Sahrens 7807046Sahrens if (origin == NULL) 7817046Sahrens origin = dp->dp_origin_snap; 7827046Sahrens 7835367Sahrens ASSERT(origin == NULL || origin->ds_dir->dd_pool == dp); 7845367Sahrens ASSERT(origin == NULL || origin->ds_phys->ds_num_children > 0); 785789Sahrens ASSERT(dmu_tx_is_syncing(tx)); 7865367Sahrens ASSERT(dd->dd_phys->dd_head_dataset_obj == 0); 787789Sahrens 788928Stabriz dsobj = dmu_object_alloc(mos, DMU_OT_DSL_DATASET, 0, 789928Stabriz DMU_OT_DSL_DATASET, sizeof (dsl_dataset_phys_t), tx); 7901544Seschrock VERIFY(0 == dmu_bonus_hold(mos, dsobj, FTAG, &dbuf)); 791789Sahrens dmu_buf_will_dirty(dbuf, tx); 792789Sahrens dsphys = dbuf->db_data; 7936689Smaybee bzero(dsphys, sizeof (dsl_dataset_phys_t)); 794789Sahrens dsphys->ds_dir_obj = dd->dd_object; 7956492Stimh dsphys->ds_flags = flags; 796789Sahrens dsphys->ds_fsid_guid = unique_create(); 797789Sahrens (void) random_get_pseudo_bytes((void*)&dsphys->ds_guid, 798789Sahrens sizeof (dsphys->ds_guid)); 799789Sahrens dsphys->ds_snapnames_zapobj = 8006492Stimh zap_create_norm(mos, U8_TEXTPREP_TOUPPER, DMU_OT_DSL_DS_SNAP_MAP, 8016492Stimh DMU_OT_NONE, 0, tx); 802789Sahrens dsphys->ds_creation_time = gethrestime_sec(); 8037046Sahrens dsphys->ds_creation_txg = tx->tx_txg == TXG_INITIAL ? 1 : tx->tx_txg; 80412470SMatthew.Ahrens@Sun.COM 80512470SMatthew.Ahrens@Sun.COM if (origin == NULL) { 80612470SMatthew.Ahrens@Sun.COM dsphys->ds_deadlist_obj = dsl_deadlist_alloc(mos, tx); 80712470SMatthew.Ahrens@Sun.COM } else { 80812470SMatthew.Ahrens@Sun.COM dsl_dataset_t *ohds; 80912470SMatthew.Ahrens@Sun.COM 8105367Sahrens dsphys->ds_prev_snap_obj = origin->ds_object; 811789Sahrens dsphys->ds_prev_snap_txg = 8125367Sahrens origin->ds_phys->ds_creation_txg; 813789Sahrens dsphys->ds_used_bytes = 8145367Sahrens origin->ds_phys->ds_used_bytes; 815789Sahrens dsphys->ds_compressed_bytes = 8165367Sahrens origin->ds_phys->ds_compressed_bytes; 817789Sahrens dsphys->ds_uncompressed_bytes = 8185367Sahrens origin->ds_phys->ds_uncompressed_bytes; 8195367Sahrens dsphys->ds_bp = origin->ds_phys->ds_bp; 8206502Stimh dsphys->ds_flags |= origin->ds_phys->ds_flags; 821789Sahrens 8225367Sahrens dmu_buf_will_dirty(origin->ds_dbuf, tx); 8235367Sahrens origin->ds_phys->ds_num_children++; 824789Sahrens 82512470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, 82612470SMatthew.Ahrens@Sun.COM origin->ds_dir->dd_phys->dd_head_dataset_obj, FTAG, &ohds)); 82712470SMatthew.Ahrens@Sun.COM dsphys->ds_deadlist_obj = dsl_deadlist_clone(&ohds->ds_deadlist, 82812470SMatthew.Ahrens@Sun.COM dsphys->ds_prev_snap_txg, dsphys->ds_prev_snap_obj, tx); 82912470SMatthew.Ahrens@Sun.COM dsl_dataset_rele(ohds, FTAG); 83012470SMatthew.Ahrens@Sun.COM 8317046Sahrens if (spa_version(dp->dp_spa) >= SPA_VERSION_NEXT_CLONES) { 8327046Sahrens if (origin->ds_phys->ds_next_clones_obj == 0) { 8337046Sahrens origin->ds_phys->ds_next_clones_obj = 8347046Sahrens zap_create(mos, 8357046Sahrens DMU_OT_NEXT_CLONES, DMU_OT_NONE, 0, tx); 8367046Sahrens } 8377046Sahrens VERIFY(0 == zap_add_int(mos, 8387046Sahrens origin->ds_phys->ds_next_clones_obj, 8397046Sahrens dsobj, tx)); 8407046Sahrens } 8417046Sahrens 842789Sahrens dmu_buf_will_dirty(dd->dd_dbuf, tx); 8435367Sahrens dd->dd_phys->dd_origin_obj = origin->ds_object; 84412470SMatthew.Ahrens@Sun.COM if (spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) { 84512470SMatthew.Ahrens@Sun.COM if (origin->ds_dir->dd_phys->dd_clones == 0) { 84612470SMatthew.Ahrens@Sun.COM dmu_buf_will_dirty(origin->ds_dir->dd_dbuf, tx); 84712470SMatthew.Ahrens@Sun.COM origin->ds_dir->dd_phys->dd_clones = 84812470SMatthew.Ahrens@Sun.COM zap_create(mos, 84912470SMatthew.Ahrens@Sun.COM DMU_OT_DSL_CLONES, DMU_OT_NONE, 0, tx); 85012470SMatthew.Ahrens@Sun.COM } 85112470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, zap_add_int(mos, 85212470SMatthew.Ahrens@Sun.COM origin->ds_dir->dd_phys->dd_clones, dsobj, tx)); 85312470SMatthew.Ahrens@Sun.COM } 854789Sahrens } 8556492Stimh 8566492Stimh if (spa_version(dp->dp_spa) >= SPA_VERSION_UNIQUE_ACCURATE) 8576492Stimh dsphys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE; 8586492Stimh 8591544Seschrock dmu_buf_rele(dbuf, FTAG); 860789Sahrens 861789Sahrens dmu_buf_will_dirty(dd->dd_dbuf, tx); 862789Sahrens dd->dd_phys->dd_head_dataset_obj = dsobj; 8635367Sahrens 8645367Sahrens return (dsobj); 8655367Sahrens } 8665367Sahrens 8675367Sahrens uint64_t 8686492Stimh dsl_dataset_create_sync(dsl_dir_t *pdd, const char *lastname, 8696492Stimh dsl_dataset_t *origin, uint64_t flags, cred_t *cr, dmu_tx_t *tx) 8705367Sahrens { 8715367Sahrens dsl_pool_t *dp = pdd->dd_pool; 8725367Sahrens uint64_t dsobj, ddobj; 8735367Sahrens dsl_dir_t *dd; 8745367Sahrens 8755367Sahrens ASSERT(lastname[0] != '@'); 8765367Sahrens 8777046Sahrens ddobj = dsl_dir_create_sync(dp, pdd, lastname, tx); 8785367Sahrens VERIFY(0 == dsl_dir_open_obj(dp, ddobj, lastname, FTAG, &dd)); 8795367Sahrens 8807046Sahrens dsobj = dsl_dataset_create_sync_dd(dd, origin, flags, tx); 8815367Sahrens 8825367Sahrens dsl_deleg_set_create_perms(dd, tx, cr); 8835367Sahrens 884789Sahrens dsl_dir_close(dd, FTAG); 885789Sahrens 886*12970SMark.Maybee@Sun.COM /* 887*12970SMark.Maybee@Sun.COM * If we are creating a clone, make sure we zero out any stale 888*12970SMark.Maybee@Sun.COM * data from the origin snapshots zil header. 889*12970SMark.Maybee@Sun.COM */ 890*12970SMark.Maybee@Sun.COM if (origin != NULL) { 891*12970SMark.Maybee@Sun.COM dsl_dataset_t *ds; 892*12970SMark.Maybee@Sun.COM objset_t *os; 893*12970SMark.Maybee@Sun.COM 894*12970SMark.Maybee@Sun.COM VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds)); 895*12970SMark.Maybee@Sun.COM VERIFY3U(0, ==, dmu_objset_from_ds(ds, &os)); 896*12970SMark.Maybee@Sun.COM bzero(&os->os_zil_header, sizeof (os->os_zil_header)); 897*12970SMark.Maybee@Sun.COM dsl_dataset_dirty(ds, tx); 898*12970SMark.Maybee@Sun.COM dsl_dataset_rele(ds, FTAG); 899*12970SMark.Maybee@Sun.COM } 900*12970SMark.Maybee@Sun.COM 9012199Sahrens return (dsobj); 9022199Sahrens } 9032199Sahrens 9042199Sahrens struct destroyarg { 9052199Sahrens dsl_sync_task_group_t *dstg; 9062199Sahrens char *snapname; 9072199Sahrens char *failed; 90810242Schris.kirby@sun.com boolean_t defer; 9092199Sahrens }; 9102199Sahrens 9112199Sahrens static int 91211209SMatthew.Ahrens@Sun.COM dsl_snapshot_destroy_one(const char *name, void *arg) 9132199Sahrens { 9142199Sahrens struct destroyarg *da = arg; 9152199Sahrens dsl_dataset_t *ds; 9162199Sahrens int err; 91710242Schris.kirby@sun.com char *dsname; 91810272SMatthew.Ahrens@Sun.COM 91910272SMatthew.Ahrens@Sun.COM dsname = kmem_asprintf("%s@%s", name, da->snapname); 92010298SMatthew.Ahrens@Sun.COM err = dsl_dataset_own(dsname, B_TRUE, da->dstg, &ds); 92110272SMatthew.Ahrens@Sun.COM strfree(dsname); 9226689Smaybee if (err == 0) { 92310242Schris.kirby@sun.com struct dsl_ds_destroyarg *dsda; 92410242Schris.kirby@sun.com 9256689Smaybee dsl_dataset_make_exclusive(ds, da->dstg); 92610242Schris.kirby@sun.com dsda = kmem_zalloc(sizeof (struct dsl_ds_destroyarg), KM_SLEEP); 92710242Schris.kirby@sun.com dsda->ds = ds; 92810242Schris.kirby@sun.com dsda->defer = da->defer; 9296689Smaybee dsl_sync_task_create(da->dstg, dsl_dataset_destroy_check, 93010242Schris.kirby@sun.com dsl_dataset_destroy_sync, dsda, da->dstg, 0); 9316689Smaybee } else if (err == ENOENT) { 9326689Smaybee err = 0; 9336689Smaybee } else { 9342199Sahrens (void) strcpy(da->failed, name); 9352199Sahrens } 9366689Smaybee return (err); 937789Sahrens } 938789Sahrens 9392199Sahrens /* 9402199Sahrens * Destroy 'snapname' in all descendants of 'fsname'. 9412199Sahrens */ 9422199Sahrens #pragma weak dmu_snapshots_destroy = dsl_snapshots_destroy 9432199Sahrens int 94410242Schris.kirby@sun.com dsl_snapshots_destroy(char *fsname, char *snapname, boolean_t defer) 9452199Sahrens { 9462199Sahrens int err; 9472199Sahrens struct destroyarg da; 9482199Sahrens dsl_sync_task_t *dst; 9492199Sahrens spa_t *spa; 9502199Sahrens 9514603Sahrens err = spa_open(fsname, &spa, FTAG); 9522199Sahrens if (err) 9532199Sahrens return (err); 9542199Sahrens da.dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); 9552199Sahrens da.snapname = snapname; 9562199Sahrens da.failed = fsname; 95710242Schris.kirby@sun.com da.defer = defer; 9582199Sahrens 9592199Sahrens err = dmu_objset_find(fsname, 9602417Sahrens dsl_snapshot_destroy_one, &da, DS_FIND_CHILDREN); 9612199Sahrens 9622199Sahrens if (err == 0) 9632199Sahrens err = dsl_sync_task_group_wait(da.dstg); 9642199Sahrens 9652199Sahrens for (dst = list_head(&da.dstg->dstg_tasks); dst; 9662199Sahrens dst = list_next(&da.dstg->dstg_tasks, dst)) { 96710242Schris.kirby@sun.com struct dsl_ds_destroyarg *dsda = dst->dst_arg1; 96810242Schris.kirby@sun.com dsl_dataset_t *ds = dsda->ds; 96910242Schris.kirby@sun.com 9706689Smaybee /* 9716689Smaybee * Return the file system name that triggered the error 9726689Smaybee */ 9732199Sahrens if (dst->dst_err) { 9742199Sahrens dsl_dataset_name(ds, fsname); 9754603Sahrens *strchr(fsname, '@') = '\0'; 9762199Sahrens } 97710242Schris.kirby@sun.com ASSERT3P(dsda->rm_origin, ==, NULL); 9786689Smaybee dsl_dataset_disown(ds, da.dstg); 97910242Schris.kirby@sun.com kmem_free(dsda, sizeof (struct dsl_ds_destroyarg)); 9802199Sahrens } 9812199Sahrens 9822199Sahrens dsl_sync_task_group_destroy(da.dstg); 9832199Sahrens spa_close(spa, FTAG); 9842199Sahrens return (err); 9852199Sahrens } 9862199Sahrens 98710242Schris.kirby@sun.com static boolean_t 98810242Schris.kirby@sun.com dsl_dataset_might_destroy_origin(dsl_dataset_t *ds) 98910242Schris.kirby@sun.com { 99010242Schris.kirby@sun.com boolean_t might_destroy = B_FALSE; 99110242Schris.kirby@sun.com 99210242Schris.kirby@sun.com mutex_enter(&ds->ds_lock); 99310242Schris.kirby@sun.com if (ds->ds_phys->ds_num_children == 2 && ds->ds_userrefs == 0 && 99410242Schris.kirby@sun.com DS_IS_DEFER_DESTROY(ds)) 99510242Schris.kirby@sun.com might_destroy = B_TRUE; 99610242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 99710242Schris.kirby@sun.com 99810242Schris.kirby@sun.com return (might_destroy); 99910242Schris.kirby@sun.com } 100010242Schris.kirby@sun.com 100110242Schris.kirby@sun.com /* 100210242Schris.kirby@sun.com * If we're removing a clone, and these three conditions are true: 100310242Schris.kirby@sun.com * 1) the clone's origin has no other children 100410242Schris.kirby@sun.com * 2) the clone's origin has no user references 100510242Schris.kirby@sun.com * 3) the clone's origin has been marked for deferred destruction 100610242Schris.kirby@sun.com * Then, prepare to remove the origin as part of this sync task group. 100710242Schris.kirby@sun.com */ 100810242Schris.kirby@sun.com static int 100910242Schris.kirby@sun.com dsl_dataset_origin_rm_prep(struct dsl_ds_destroyarg *dsda, void *tag) 101010242Schris.kirby@sun.com { 101110242Schris.kirby@sun.com dsl_dataset_t *ds = dsda->ds; 101210242Schris.kirby@sun.com dsl_dataset_t *origin = ds->ds_prev; 101310242Schris.kirby@sun.com 101410242Schris.kirby@sun.com if (dsl_dataset_might_destroy_origin(origin)) { 101510242Schris.kirby@sun.com char *name; 101610242Schris.kirby@sun.com int namelen; 101710242Schris.kirby@sun.com int error; 101810242Schris.kirby@sun.com 101910242Schris.kirby@sun.com namelen = dsl_dataset_namelen(origin) + 1; 102010242Schris.kirby@sun.com name = kmem_alloc(namelen, KM_SLEEP); 102110242Schris.kirby@sun.com dsl_dataset_name(origin, name); 102210242Schris.kirby@sun.com #ifdef _KERNEL 102310242Schris.kirby@sun.com error = zfs_unmount_snap(name, NULL); 102410242Schris.kirby@sun.com if (error) { 102510242Schris.kirby@sun.com kmem_free(name, namelen); 102610242Schris.kirby@sun.com return (error); 102710242Schris.kirby@sun.com } 102810242Schris.kirby@sun.com #endif 102910298SMatthew.Ahrens@Sun.COM error = dsl_dataset_own(name, B_TRUE, tag, &origin); 103010242Schris.kirby@sun.com kmem_free(name, namelen); 103110242Schris.kirby@sun.com if (error) 103210242Schris.kirby@sun.com return (error); 103310242Schris.kirby@sun.com dsda->rm_origin = origin; 103410242Schris.kirby@sun.com dsl_dataset_make_exclusive(origin, tag); 103510242Schris.kirby@sun.com } 103610242Schris.kirby@sun.com 103710242Schris.kirby@sun.com return (0); 103810242Schris.kirby@sun.com } 103910242Schris.kirby@sun.com 10405367Sahrens /* 10416689Smaybee * ds must be opened as OWNER. On return (whether successful or not), 10426689Smaybee * ds will be closed and caller can no longer dereference it. 10435367Sahrens */ 1044789Sahrens int 104510242Schris.kirby@sun.com dsl_dataset_destroy(dsl_dataset_t *ds, void *tag, boolean_t defer) 1046789Sahrens { 1047789Sahrens int err; 10482199Sahrens dsl_sync_task_group_t *dstg; 10492199Sahrens objset_t *os; 1050789Sahrens dsl_dir_t *dd; 10512199Sahrens uint64_t obj; 105211022STom.Erickson@Sun.COM struct dsl_ds_destroyarg dsda = { 0 }; 105311022STom.Erickson@Sun.COM dsl_dataset_t dummy_ds = { 0 }; 105410242Schris.kirby@sun.com 105510242Schris.kirby@sun.com dsda.ds = ds; 10562199Sahrens 10575367Sahrens if (dsl_dataset_is_snapshot(ds)) { 10582199Sahrens /* Destroying a snapshot is simpler */ 10596689Smaybee dsl_dataset_make_exclusive(ds, tag); 10607237Sek110237 106110242Schris.kirby@sun.com dsda.defer = defer; 10622199Sahrens err = dsl_sync_task_do(ds->ds_dir->dd_pool, 10632199Sahrens dsl_dataset_destroy_check, dsl_dataset_destroy_sync, 106410242Schris.kirby@sun.com &dsda, tag, 0); 106510242Schris.kirby@sun.com ASSERT3P(dsda.rm_origin, ==, NULL); 10665367Sahrens goto out; 106710385Schris.kirby@sun.com } else if (defer) { 106810385Schris.kirby@sun.com err = EINVAL; 106910385Schris.kirby@sun.com goto out; 10702199Sahrens } 10712199Sahrens 10722199Sahrens dd = ds->ds_dir; 107311022STom.Erickson@Sun.COM dummy_ds.ds_dir = dd; 107411022STom.Erickson@Sun.COM dummy_ds.ds_object = ds->ds_object; 1075789Sahrens 10762199Sahrens /* 10772199Sahrens * Check for errors and mark this ds as inconsistent, in 10782199Sahrens * case we crash while freeing the objects. 10792199Sahrens */ 10802199Sahrens err = dsl_sync_task_do(dd->dd_pool, dsl_dataset_destroy_begin_check, 10812199Sahrens dsl_dataset_destroy_begin_sync, ds, NULL, 0); 10825367Sahrens if (err) 10835367Sahrens goto out; 10845367Sahrens 108510298SMatthew.Ahrens@Sun.COM err = dmu_objset_from_ds(ds, &os); 10865367Sahrens if (err) 10875367Sahrens goto out; 10882199Sahrens 10892199Sahrens /* 10902199Sahrens * remove the objects in open context, so that we won't 10912199Sahrens * have too much to do in syncing context. 10922199Sahrens */ 10933025Sahrens for (obj = 0; err == 0; err = dmu_object_next(os, &obj, FALSE, 10943025Sahrens ds->ds_phys->ds_prev_snap_txg)) { 10956992Smaybee /* 10966992Smaybee * Ignore errors, if there is not enough disk space 10976992Smaybee * we will deal with it in dsl_dataset_destroy_sync(). 10986992Smaybee */ 10996992Smaybee (void) dmu_free_object(os, obj); 11002199Sahrens } 1101*12970SMark.Maybee@Sun.COM if (err != ESRCH) 1102*12970SMark.Maybee@Sun.COM goto out; 11032199Sahrens 11049396SMatthew.Ahrens@Sun.COM /* 1105*12970SMark.Maybee@Sun.COM * Only the ZIL knows how to free log blocks. 1106*12970SMark.Maybee@Sun.COM */ 1107*12970SMark.Maybee@Sun.COM zil_destroy(dmu_objset_zil(os), B_FALSE); 1108*12970SMark.Maybee@Sun.COM 1109*12970SMark.Maybee@Sun.COM /* 1110*12970SMark.Maybee@Sun.COM * Sync out all in-flight IO. 11119396SMatthew.Ahrens@Sun.COM */ 11129396SMatthew.Ahrens@Sun.COM txg_wait_synced(dd->dd_pool, 0); 11139396SMatthew.Ahrens@Sun.COM 11149396SMatthew.Ahrens@Sun.COM /* 11159396SMatthew.Ahrens@Sun.COM * If we managed to free all the objects in open 11169396SMatthew.Ahrens@Sun.COM * context, the user space accounting should be zero. 11179396SMatthew.Ahrens@Sun.COM */ 11189396SMatthew.Ahrens@Sun.COM if (ds->ds_phys->ds_bp.blk_fill == 0 && 111910298SMatthew.Ahrens@Sun.COM dmu_objset_userused_enabled(os)) { 11209396SMatthew.Ahrens@Sun.COM uint64_t count; 11219396SMatthew.Ahrens@Sun.COM 11229396SMatthew.Ahrens@Sun.COM ASSERT(zap_count(os, DMU_USERUSED_OBJECT, &count) != 0 || 11239396SMatthew.Ahrens@Sun.COM count == 0); 11249396SMatthew.Ahrens@Sun.COM ASSERT(zap_count(os, DMU_GROUPUSED_OBJECT, &count) != 0 || 11259396SMatthew.Ahrens@Sun.COM count == 0); 11269396SMatthew.Ahrens@Sun.COM } 11279396SMatthew.Ahrens@Sun.COM 11286975Smaybee rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 11296975Smaybee err = dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, FTAG, &dd); 11306975Smaybee rw_exit(&dd->dd_pool->dp_config_rwlock); 11316975Smaybee 11326975Smaybee if (err) 11336975Smaybee goto out; 11346975Smaybee 11352199Sahrens /* 11362199Sahrens * Blow away the dsl_dir + head dataset. 11372199Sahrens */ 11386689Smaybee dsl_dataset_make_exclusive(ds, tag); 113910242Schris.kirby@sun.com /* 114010242Schris.kirby@sun.com * If we're removing a clone, we might also need to remove its 114110242Schris.kirby@sun.com * origin. 114210242Schris.kirby@sun.com */ 114310242Schris.kirby@sun.com do { 114410242Schris.kirby@sun.com dsda.need_prep = B_FALSE; 114510242Schris.kirby@sun.com if (dsl_dir_is_clone(dd)) { 114610242Schris.kirby@sun.com err = dsl_dataset_origin_rm_prep(&dsda, tag); 114710242Schris.kirby@sun.com if (err) { 114810242Schris.kirby@sun.com dsl_dir_close(dd, FTAG); 114910242Schris.kirby@sun.com goto out; 115010242Schris.kirby@sun.com } 115110242Schris.kirby@sun.com } 115210242Schris.kirby@sun.com 115310242Schris.kirby@sun.com dstg = dsl_sync_task_group_create(ds->ds_dir->dd_pool); 115410242Schris.kirby@sun.com dsl_sync_task_create(dstg, dsl_dataset_destroy_check, 115510242Schris.kirby@sun.com dsl_dataset_destroy_sync, &dsda, tag, 0); 115610242Schris.kirby@sun.com dsl_sync_task_create(dstg, dsl_dir_destroy_check, 115711022STom.Erickson@Sun.COM dsl_dir_destroy_sync, &dummy_ds, FTAG, 0); 115810242Schris.kirby@sun.com err = dsl_sync_task_group_wait(dstg); 115910242Schris.kirby@sun.com dsl_sync_task_group_destroy(dstg); 116010242Schris.kirby@sun.com 116110242Schris.kirby@sun.com /* 116210242Schris.kirby@sun.com * We could be racing against 'zfs release' or 'zfs destroy -d' 116310242Schris.kirby@sun.com * on the origin snap, in which case we can get EBUSY if we 116410242Schris.kirby@sun.com * needed to destroy the origin snap but were not ready to 116510242Schris.kirby@sun.com * do so. 116610242Schris.kirby@sun.com */ 116710242Schris.kirby@sun.com if (dsda.need_prep) { 116810242Schris.kirby@sun.com ASSERT(err == EBUSY); 116910242Schris.kirby@sun.com ASSERT(dsl_dir_is_clone(dd)); 117010242Schris.kirby@sun.com ASSERT(dsda.rm_origin == NULL); 117110242Schris.kirby@sun.com } 117210242Schris.kirby@sun.com } while (dsda.need_prep); 117310242Schris.kirby@sun.com 117410242Schris.kirby@sun.com if (dsda.rm_origin != NULL) 117510242Schris.kirby@sun.com dsl_dataset_disown(dsda.rm_origin, tag); 117610242Schris.kirby@sun.com 11776689Smaybee /* if it is successful, dsl_dir_destroy_sync will close the dd */ 11785367Sahrens if (err) 11792199Sahrens dsl_dir_close(dd, FTAG); 11805367Sahrens out: 11816689Smaybee dsl_dataset_disown(ds, tag); 1182789Sahrens return (err); 1183789Sahrens } 1184789Sahrens 11853547Smaybee blkptr_t * 11863547Smaybee dsl_dataset_get_blkptr(dsl_dataset_t *ds) 1187789Sahrens { 11883547Smaybee return (&ds->ds_phys->ds_bp); 1189789Sahrens } 1190789Sahrens 1191789Sahrens void 1192789Sahrens dsl_dataset_set_blkptr(dsl_dataset_t *ds, blkptr_t *bp, dmu_tx_t *tx) 1193789Sahrens { 1194789Sahrens ASSERT(dmu_tx_is_syncing(tx)); 1195789Sahrens /* If it's the meta-objset, set dp_meta_rootbp */ 1196789Sahrens if (ds == NULL) { 1197789Sahrens tx->tx_pool->dp_meta_rootbp = *bp; 1198789Sahrens } else { 1199789Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 1200789Sahrens ds->ds_phys->ds_bp = *bp; 1201789Sahrens } 1202789Sahrens } 1203789Sahrens 1204789Sahrens spa_t * 1205789Sahrens dsl_dataset_get_spa(dsl_dataset_t *ds) 1206789Sahrens { 1207789Sahrens return (ds->ds_dir->dd_pool->dp_spa); 1208789Sahrens } 1209789Sahrens 1210789Sahrens void 1211789Sahrens dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx) 1212789Sahrens { 1213789Sahrens dsl_pool_t *dp; 1214789Sahrens 1215789Sahrens if (ds == NULL) /* this is the meta-objset */ 1216789Sahrens return; 1217789Sahrens 121810298SMatthew.Ahrens@Sun.COM ASSERT(ds->ds_objset != NULL); 12192885Sahrens 12202885Sahrens if (ds->ds_phys->ds_next_snap_obj != 0) 12212885Sahrens panic("dirtying snapshot!"); 1222789Sahrens 1223789Sahrens dp = ds->ds_dir->dd_pool; 1224789Sahrens 1225789Sahrens if (txg_list_add(&dp->dp_dirty_datasets, ds, tx->tx_txg) == 0) { 1226789Sahrens /* up the hold count until we can be written out */ 1227789Sahrens dmu_buf_add_ref(ds->ds_dbuf, ds); 1228789Sahrens } 1229789Sahrens } 1230789Sahrens 12315378Sck153898 /* 12325378Sck153898 * The unique space in the head dataset can be calculated by subtracting 12335378Sck153898 * the space used in the most recent snapshot, that is still being used 12345378Sck153898 * in this file system, from the space currently in use. To figure out 12355378Sck153898 * the space in the most recent snapshot still in use, we need to take 12365378Sck153898 * the total space used in the snapshot and subtract out the space that 12375378Sck153898 * has been freed up since the snapshot was taken. 12385378Sck153898 */ 12395378Sck153898 static void 12405378Sck153898 dsl_dataset_recalc_head_uniq(dsl_dataset_t *ds) 12415378Sck153898 { 12425378Sck153898 uint64_t mrs_used; 12435378Sck153898 uint64_t dlused, dlcomp, dluncomp; 12445378Sck153898 124512296SLin.Ling@Sun.COM ASSERT(!dsl_dataset_is_snapshot(ds)); 12465378Sck153898 12475378Sck153898 if (ds->ds_phys->ds_prev_snap_obj != 0) 12485378Sck153898 mrs_used = ds->ds_prev->ds_phys->ds_used_bytes; 12495378Sck153898 else 12505378Sck153898 mrs_used = 0; 12515378Sck153898 125212470SMatthew.Ahrens@Sun.COM dsl_deadlist_space(&ds->ds_deadlist, &dlused, &dlcomp, &dluncomp); 12535378Sck153898 12545378Sck153898 ASSERT3U(dlused, <=, mrs_used); 12555378Sck153898 ds->ds_phys->ds_unique_bytes = 12565378Sck153898 ds->ds_phys->ds_used_bytes - (mrs_used - dlused); 12575378Sck153898 125812296SLin.Ling@Sun.COM if (spa_version(ds->ds_dir->dd_pool->dp_spa) >= 12595378Sck153898 SPA_VERSION_UNIQUE_ACCURATE) 12605378Sck153898 ds->ds_phys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE; 12615378Sck153898 } 12625378Sck153898 1263789Sahrens struct killarg { 12647390SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds; 1265789Sahrens dmu_tx_t *tx; 1266789Sahrens }; 1267789Sahrens 12687390SMatthew.Ahrens@Sun.COM /* ARGSUSED */ 1269789Sahrens static int 127012296SLin.Ling@Sun.COM kill_blkptr(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, arc_buf_t *pbuf, 127110922SJeff.Bonwick@Sun.COM const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg) 1272789Sahrens { 1273789Sahrens struct killarg *ka = arg; 127410922SJeff.Bonwick@Sun.COM dmu_tx_t *tx = ka->tx; 12757837SMatthew.Ahrens@Sun.COM 12767837SMatthew.Ahrens@Sun.COM if (bp == NULL) 12777837SMatthew.Ahrens@Sun.COM return (0); 1278789Sahrens 127910922SJeff.Bonwick@Sun.COM if (zb->zb_level == ZB_ZIL_LEVEL) { 128010922SJeff.Bonwick@Sun.COM ASSERT(zilog != NULL); 12818746SMatthew.Ahrens@Sun.COM /* 12828746SMatthew.Ahrens@Sun.COM * It's a block in the intent log. It has no 12838746SMatthew.Ahrens@Sun.COM * accounting, so just free it. 12848746SMatthew.Ahrens@Sun.COM */ 128510922SJeff.Bonwick@Sun.COM dsl_free(ka->tx->tx_pool, ka->tx->tx_txg, bp); 12868746SMatthew.Ahrens@Sun.COM } else { 128710922SJeff.Bonwick@Sun.COM ASSERT(zilog == NULL); 12888746SMatthew.Ahrens@Sun.COM ASSERT3U(bp->blk_birth, >, ka->ds->ds_phys->ds_prev_snap_txg); 128910922SJeff.Bonwick@Sun.COM (void) dsl_dataset_block_kill(ka->ds, bp, tx, B_FALSE); 12908746SMatthew.Ahrens@Sun.COM } 12917390SMatthew.Ahrens@Sun.COM 1292789Sahrens return (0); 1293789Sahrens } 1294789Sahrens 1295789Sahrens /* ARGSUSED */ 12962199Sahrens static int 12972199Sahrens dsl_dataset_destroy_begin_check(void *arg1, void *arg2, dmu_tx_t *tx) 12981731Sbonwick { 12992199Sahrens dsl_dataset_t *ds = arg1; 13005367Sahrens objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 13015367Sahrens uint64_t count; 13025367Sahrens int err; 13031731Sbonwick 13041731Sbonwick /* 13051731Sbonwick * Can't delete a head dataset if there are snapshots of it. 13061731Sbonwick * (Except if the only snapshots are from the branch we cloned 13071731Sbonwick * from.) 13081731Sbonwick */ 13091731Sbonwick if (ds->ds_prev != NULL && 13101731Sbonwick ds->ds_prev->ds_phys->ds_next_snap_obj == ds->ds_object) 131110816SVitezslav.Batrla@Sun.COM return (EBUSY); 13121731Sbonwick 13135367Sahrens /* 13145367Sahrens * This is really a dsl_dir thing, but check it here so that 13155367Sahrens * we'll be less likely to leave this dataset inconsistent & 13165367Sahrens * nearly destroyed. 13175367Sahrens */ 13185367Sahrens err = zap_count(mos, ds->ds_dir->dd_phys->dd_child_dir_zapobj, &count); 13195367Sahrens if (err) 13205367Sahrens return (err); 13215367Sahrens if (count != 0) 13225367Sahrens return (EEXIST); 13235367Sahrens 13241731Sbonwick return (0); 13251731Sbonwick } 13261731Sbonwick 13272199Sahrens /* ARGSUSED */ 13282199Sahrens static void 132912296SLin.Ling@Sun.COM dsl_dataset_destroy_begin_sync(void *arg1, void *arg2, dmu_tx_t *tx) 1330789Sahrens { 13312199Sahrens dsl_dataset_t *ds = arg1; 13324543Smarks dsl_pool_t *dp = ds->ds_dir->dd_pool; 1333789Sahrens 13342199Sahrens /* Mark it as inconsistent on-disk, in case we crash */ 13352199Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 13362199Sahrens ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT; 13374543Smarks 133812296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_DESTROY_BEGIN, dp->dp_spa, tx, 133912296SLin.Ling@Sun.COM "dataset = %llu", ds->ds_object); 13402199Sahrens } 1341789Sahrens 134210242Schris.kirby@sun.com static int 134310242Schris.kirby@sun.com dsl_dataset_origin_check(struct dsl_ds_destroyarg *dsda, void *tag, 134410242Schris.kirby@sun.com dmu_tx_t *tx) 134510242Schris.kirby@sun.com { 134610242Schris.kirby@sun.com dsl_dataset_t *ds = dsda->ds; 134710242Schris.kirby@sun.com dsl_dataset_t *ds_prev = ds->ds_prev; 134810242Schris.kirby@sun.com 134910242Schris.kirby@sun.com if (dsl_dataset_might_destroy_origin(ds_prev)) { 135010242Schris.kirby@sun.com struct dsl_ds_destroyarg ndsda = {0}; 135110242Schris.kirby@sun.com 135210242Schris.kirby@sun.com /* 135310242Schris.kirby@sun.com * If we're not prepared to remove the origin, don't remove 135410242Schris.kirby@sun.com * the clone either. 135510242Schris.kirby@sun.com */ 135610242Schris.kirby@sun.com if (dsda->rm_origin == NULL) { 135710242Schris.kirby@sun.com dsda->need_prep = B_TRUE; 135810242Schris.kirby@sun.com return (EBUSY); 135910242Schris.kirby@sun.com } 136010242Schris.kirby@sun.com 136110242Schris.kirby@sun.com ndsda.ds = ds_prev; 136210242Schris.kirby@sun.com ndsda.is_origin_rm = B_TRUE; 136310242Schris.kirby@sun.com return (dsl_dataset_destroy_check(&ndsda, tag, tx)); 136410242Schris.kirby@sun.com } 136510242Schris.kirby@sun.com 136610242Schris.kirby@sun.com /* 136710242Schris.kirby@sun.com * If we're not going to remove the origin after all, 136810242Schris.kirby@sun.com * undo the open context setup. 136910242Schris.kirby@sun.com */ 137010242Schris.kirby@sun.com if (dsda->rm_origin != NULL) { 137110242Schris.kirby@sun.com dsl_dataset_disown(dsda->rm_origin, tag); 137210242Schris.kirby@sun.com dsda->rm_origin = NULL; 137310242Schris.kirby@sun.com } 137410242Schris.kirby@sun.com 137510242Schris.kirby@sun.com return (0); 137610242Schris.kirby@sun.com } 137710242Schris.kirby@sun.com 13782199Sahrens /* ARGSUSED */ 13795367Sahrens int 13802199Sahrens dsl_dataset_destroy_check(void *arg1, void *arg2, dmu_tx_t *tx) 13812199Sahrens { 138210242Schris.kirby@sun.com struct dsl_ds_destroyarg *dsda = arg1; 138310242Schris.kirby@sun.com dsl_dataset_t *ds = dsda->ds; 1384789Sahrens 13856689Smaybee /* we have an owner hold, so noone else can destroy us */ 13866689Smaybee ASSERT(!DSL_DATASET_IS_DESTROYED(ds)); 13876689Smaybee 138810242Schris.kirby@sun.com /* 138910242Schris.kirby@sun.com * Only allow deferred destroy on pools that support it. 139010242Schris.kirby@sun.com * NOTE: deferred destroy is only supported on snapshots. 139110242Schris.kirby@sun.com */ 139210242Schris.kirby@sun.com if (dsda->defer) { 139310242Schris.kirby@sun.com if (spa_version(ds->ds_dir->dd_pool->dp_spa) < 139410242Schris.kirby@sun.com SPA_VERSION_USERREFS) 139510242Schris.kirby@sun.com return (ENOTSUP); 139610242Schris.kirby@sun.com ASSERT(dsl_dataset_is_snapshot(ds)); 139710242Schris.kirby@sun.com return (0); 139810242Schris.kirby@sun.com } 1399789Sahrens 1400789Sahrens /* 1401789Sahrens * Can't delete a head dataset if there are snapshots of it. 1402789Sahrens * (Except if the only snapshots are from the branch we cloned 1403789Sahrens * from.) 1404789Sahrens */ 1405789Sahrens if (ds->ds_prev != NULL && 14062199Sahrens ds->ds_prev->ds_phys->ds_next_snap_obj == ds->ds_object) 140710816SVitezslav.Batrla@Sun.COM return (EBUSY); 1408789Sahrens 1409789Sahrens /* 1410789Sahrens * If we made changes this txg, traverse_dsl_dataset won't find 1411789Sahrens * them. Try again. 1412789Sahrens */ 14132199Sahrens if (ds->ds_phys->ds_bp.blk_birth >= tx->tx_txg) 1414789Sahrens return (EAGAIN); 14152199Sahrens 141610242Schris.kirby@sun.com if (dsl_dataset_is_snapshot(ds)) { 141710242Schris.kirby@sun.com /* 141810242Schris.kirby@sun.com * If this snapshot has an elevated user reference count, 141910242Schris.kirby@sun.com * we can't destroy it yet. 142010242Schris.kirby@sun.com */ 142110242Schris.kirby@sun.com if (ds->ds_userrefs > 0 && !dsda->releasing) 142210242Schris.kirby@sun.com return (EBUSY); 142310242Schris.kirby@sun.com 142410242Schris.kirby@sun.com mutex_enter(&ds->ds_lock); 142510242Schris.kirby@sun.com /* 142610242Schris.kirby@sun.com * Can't delete a branch point. However, if we're destroying 142710242Schris.kirby@sun.com * a clone and removing its origin due to it having a user 142810242Schris.kirby@sun.com * hold count of 0 and having been marked for deferred destroy, 142910242Schris.kirby@sun.com * it's OK for the origin to have a single clone. 143010242Schris.kirby@sun.com */ 143110242Schris.kirby@sun.com if (ds->ds_phys->ds_num_children > 143210242Schris.kirby@sun.com (dsda->is_origin_rm ? 2 : 1)) { 143310242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 143410242Schris.kirby@sun.com return (EEXIST); 143510242Schris.kirby@sun.com } 143610242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 143710242Schris.kirby@sun.com } else if (dsl_dir_is_clone(ds->ds_dir)) { 143810242Schris.kirby@sun.com return (dsl_dataset_origin_check(dsda, arg2, tx)); 143910242Schris.kirby@sun.com } 144010242Schris.kirby@sun.com 14412199Sahrens /* XXX we should do some i/o error checking... */ 14422199Sahrens return (0); 14432199Sahrens } 14442199Sahrens 14456689Smaybee struct refsarg { 14466689Smaybee kmutex_t lock; 14476689Smaybee boolean_t gone; 14486689Smaybee kcondvar_t cv; 14496689Smaybee }; 14506689Smaybee 14516689Smaybee /* ARGSUSED */ 14526689Smaybee static void 14536689Smaybee dsl_dataset_refs_gone(dmu_buf_t *db, void *argv) 14546689Smaybee { 14556689Smaybee struct refsarg *arg = argv; 14566689Smaybee 14576689Smaybee mutex_enter(&arg->lock); 14586689Smaybee arg->gone = TRUE; 14596689Smaybee cv_signal(&arg->cv); 14606689Smaybee mutex_exit(&arg->lock); 14616689Smaybee } 14626689Smaybee 14636689Smaybee static void 14646689Smaybee dsl_dataset_drain_refs(dsl_dataset_t *ds, void *tag) 14656689Smaybee { 14666689Smaybee struct refsarg arg; 14676689Smaybee 14686689Smaybee mutex_init(&arg.lock, NULL, MUTEX_DEFAULT, NULL); 14696689Smaybee cv_init(&arg.cv, NULL, CV_DEFAULT, NULL); 14706689Smaybee arg.gone = FALSE; 14716689Smaybee (void) dmu_buf_update_user(ds->ds_dbuf, ds, &arg, &ds->ds_phys, 14726689Smaybee dsl_dataset_refs_gone); 14736689Smaybee dmu_buf_rele(ds->ds_dbuf, tag); 14746689Smaybee mutex_enter(&arg.lock); 14756689Smaybee while (!arg.gone) 14766689Smaybee cv_wait(&arg.cv, &arg.lock); 14776689Smaybee ASSERT(arg.gone); 14786689Smaybee mutex_exit(&arg.lock); 14796689Smaybee ds->ds_dbuf = NULL; 14806689Smaybee ds->ds_phys = NULL; 14816689Smaybee mutex_destroy(&arg.lock); 14826689Smaybee cv_destroy(&arg.cv); 14836689Smaybee } 14846689Smaybee 148510801SMatthew.Ahrens@Sun.COM static void 148610801SMatthew.Ahrens@Sun.COM remove_from_next_clones(dsl_dataset_t *ds, uint64_t obj, dmu_tx_t *tx) 148710801SMatthew.Ahrens@Sun.COM { 148810801SMatthew.Ahrens@Sun.COM objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 148910801SMatthew.Ahrens@Sun.COM uint64_t count; 149010801SMatthew.Ahrens@Sun.COM int err; 149110801SMatthew.Ahrens@Sun.COM 149210801SMatthew.Ahrens@Sun.COM ASSERT(ds->ds_phys->ds_num_children >= 2); 149310801SMatthew.Ahrens@Sun.COM err = zap_remove_int(mos, ds->ds_phys->ds_next_clones_obj, obj, tx); 149410801SMatthew.Ahrens@Sun.COM /* 149510801SMatthew.Ahrens@Sun.COM * The err should not be ENOENT, but a bug in a previous version 149610801SMatthew.Ahrens@Sun.COM * of the code could cause upgrade_clones_cb() to not set 149710801SMatthew.Ahrens@Sun.COM * ds_next_snap_obj when it should, leading to a missing entry. 149810801SMatthew.Ahrens@Sun.COM * If we knew that the pool was created after 149910801SMatthew.Ahrens@Sun.COM * SPA_VERSION_NEXT_CLONES, we could assert that it isn't 150010801SMatthew.Ahrens@Sun.COM * ENOENT. However, at least we can check that we don't have 150110801SMatthew.Ahrens@Sun.COM * too many entries in the next_clones_obj even after failing to 150210801SMatthew.Ahrens@Sun.COM * remove this one. 150310801SMatthew.Ahrens@Sun.COM */ 150410801SMatthew.Ahrens@Sun.COM if (err != ENOENT) { 150510801SMatthew.Ahrens@Sun.COM VERIFY3U(err, ==, 0); 150610801SMatthew.Ahrens@Sun.COM } 150710801SMatthew.Ahrens@Sun.COM ASSERT3U(0, ==, zap_count(mos, ds->ds_phys->ds_next_clones_obj, 150810801SMatthew.Ahrens@Sun.COM &count)); 150910801SMatthew.Ahrens@Sun.COM ASSERT3U(count, <=, ds->ds_phys->ds_num_children - 2); 151010801SMatthew.Ahrens@Sun.COM } 151110801SMatthew.Ahrens@Sun.COM 151212470SMatthew.Ahrens@Sun.COM static void 151312470SMatthew.Ahrens@Sun.COM dsl_dataset_remove_clones_key(dsl_dataset_t *ds, uint64_t mintxg, dmu_tx_t *tx) 151412470SMatthew.Ahrens@Sun.COM { 151512470SMatthew.Ahrens@Sun.COM objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 151612470SMatthew.Ahrens@Sun.COM zap_cursor_t zc; 151712470SMatthew.Ahrens@Sun.COM zap_attribute_t za; 151812470SMatthew.Ahrens@Sun.COM 151912470SMatthew.Ahrens@Sun.COM /* 152012470SMatthew.Ahrens@Sun.COM * If it is the old version, dd_clones doesn't exist so we can't 152112470SMatthew.Ahrens@Sun.COM * find the clones, but deadlist_remove_key() is a no-op so it 152212470SMatthew.Ahrens@Sun.COM * doesn't matter. 152312470SMatthew.Ahrens@Sun.COM */ 152412470SMatthew.Ahrens@Sun.COM if (ds->ds_dir->dd_phys->dd_clones == 0) 152512470SMatthew.Ahrens@Sun.COM return; 152612470SMatthew.Ahrens@Sun.COM 152712470SMatthew.Ahrens@Sun.COM for (zap_cursor_init(&zc, mos, ds->ds_dir->dd_phys->dd_clones); 152812470SMatthew.Ahrens@Sun.COM zap_cursor_retrieve(&zc, &za) == 0; 152912470SMatthew.Ahrens@Sun.COM zap_cursor_advance(&zc)) { 153012470SMatthew.Ahrens@Sun.COM dsl_dataset_t *clone; 153112470SMatthew.Ahrens@Sun.COM 153212470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, dsl_dataset_hold_obj(ds->ds_dir->dd_pool, 153312470SMatthew.Ahrens@Sun.COM za.za_first_integer, FTAG, &clone)); 153412470SMatthew.Ahrens@Sun.COM if (clone->ds_dir->dd_origin_txg > mintxg) { 153512470SMatthew.Ahrens@Sun.COM dsl_deadlist_remove_key(&clone->ds_deadlist, 153612470SMatthew.Ahrens@Sun.COM mintxg, tx); 153712470SMatthew.Ahrens@Sun.COM dsl_dataset_remove_clones_key(clone, mintxg, tx); 153812470SMatthew.Ahrens@Sun.COM } 153912470SMatthew.Ahrens@Sun.COM dsl_dataset_rele(clone, FTAG); 154012470SMatthew.Ahrens@Sun.COM } 154112470SMatthew.Ahrens@Sun.COM zap_cursor_fini(&zc); 154212470SMatthew.Ahrens@Sun.COM } 154312470SMatthew.Ahrens@Sun.COM 154412470SMatthew.Ahrens@Sun.COM struct process_old_arg { 154512470SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds; 154612470SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds_prev; 154712470SMatthew.Ahrens@Sun.COM boolean_t after_branch_point; 154812470SMatthew.Ahrens@Sun.COM zio_t *pio; 154912470SMatthew.Ahrens@Sun.COM uint64_t used, comp, uncomp; 155012470SMatthew.Ahrens@Sun.COM }; 155112470SMatthew.Ahrens@Sun.COM 155212470SMatthew.Ahrens@Sun.COM static int 155312470SMatthew.Ahrens@Sun.COM process_old_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) 155412470SMatthew.Ahrens@Sun.COM { 155512470SMatthew.Ahrens@Sun.COM struct process_old_arg *poa = arg; 155612470SMatthew.Ahrens@Sun.COM dsl_pool_t *dp = poa->ds->ds_dir->dd_pool; 155712470SMatthew.Ahrens@Sun.COM 155812470SMatthew.Ahrens@Sun.COM if (bp->blk_birth <= poa->ds->ds_phys->ds_prev_snap_txg) { 155912470SMatthew.Ahrens@Sun.COM dsl_deadlist_insert(&poa->ds->ds_deadlist, bp, tx); 156012470SMatthew.Ahrens@Sun.COM if (poa->ds_prev && !poa->after_branch_point && 156112470SMatthew.Ahrens@Sun.COM bp->blk_birth > 156212470SMatthew.Ahrens@Sun.COM poa->ds_prev->ds_phys->ds_prev_snap_txg) { 156312470SMatthew.Ahrens@Sun.COM poa->ds_prev->ds_phys->ds_unique_bytes += 156412470SMatthew.Ahrens@Sun.COM bp_get_dsize_sync(dp->dp_spa, bp); 156512470SMatthew.Ahrens@Sun.COM } 156612470SMatthew.Ahrens@Sun.COM } else { 156712470SMatthew.Ahrens@Sun.COM poa->used += bp_get_dsize_sync(dp->dp_spa, bp); 156812470SMatthew.Ahrens@Sun.COM poa->comp += BP_GET_PSIZE(bp); 156912470SMatthew.Ahrens@Sun.COM poa->uncomp += BP_GET_UCSIZE(bp); 157012470SMatthew.Ahrens@Sun.COM dsl_free_sync(poa->pio, dp, tx->tx_txg, bp); 157112470SMatthew.Ahrens@Sun.COM } 157212470SMatthew.Ahrens@Sun.COM return (0); 157312470SMatthew.Ahrens@Sun.COM } 157412470SMatthew.Ahrens@Sun.COM 157512470SMatthew.Ahrens@Sun.COM static void 157612470SMatthew.Ahrens@Sun.COM process_old_deadlist(dsl_dataset_t *ds, dsl_dataset_t *ds_prev, 157712470SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds_next, boolean_t after_branch_point, dmu_tx_t *tx) 157812470SMatthew.Ahrens@Sun.COM { 157912470SMatthew.Ahrens@Sun.COM struct process_old_arg poa = { 0 }; 158012470SMatthew.Ahrens@Sun.COM dsl_pool_t *dp = ds->ds_dir->dd_pool; 158112470SMatthew.Ahrens@Sun.COM objset_t *mos = dp->dp_meta_objset; 158212470SMatthew.Ahrens@Sun.COM 158312470SMatthew.Ahrens@Sun.COM ASSERT(ds->ds_deadlist.dl_oldfmt); 158412470SMatthew.Ahrens@Sun.COM ASSERT(ds_next->ds_deadlist.dl_oldfmt); 158512470SMatthew.Ahrens@Sun.COM 158612470SMatthew.Ahrens@Sun.COM poa.ds = ds; 158712470SMatthew.Ahrens@Sun.COM poa.ds_prev = ds_prev; 158812470SMatthew.Ahrens@Sun.COM poa.after_branch_point = after_branch_point; 158912470SMatthew.Ahrens@Sun.COM poa.pio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED); 159012470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, bpobj_iterate(&ds_next->ds_deadlist.dl_bpobj, 159112470SMatthew.Ahrens@Sun.COM process_old_cb, &poa, tx)); 159212470SMatthew.Ahrens@Sun.COM VERIFY3U(zio_wait(poa.pio), ==, 0); 159312470SMatthew.Ahrens@Sun.COM ASSERT3U(poa.used, ==, ds->ds_phys->ds_unique_bytes); 159412470SMatthew.Ahrens@Sun.COM 159512470SMatthew.Ahrens@Sun.COM /* change snapused */ 159612470SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP, 159712470SMatthew.Ahrens@Sun.COM -poa.used, -poa.comp, -poa.uncomp, tx); 159812470SMatthew.Ahrens@Sun.COM 159912470SMatthew.Ahrens@Sun.COM /* swap next's deadlist to our deadlist */ 160012470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds->ds_deadlist); 160112470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds_next->ds_deadlist); 160212470SMatthew.Ahrens@Sun.COM SWITCH64(ds_next->ds_phys->ds_deadlist_obj, 160312470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_deadlist_obj); 160412470SMatthew.Ahrens@Sun.COM dsl_deadlist_open(&ds->ds_deadlist, mos, ds->ds_phys->ds_deadlist_obj); 160512470SMatthew.Ahrens@Sun.COM dsl_deadlist_open(&ds_next->ds_deadlist, mos, 160612470SMatthew.Ahrens@Sun.COM ds_next->ds_phys->ds_deadlist_obj); 160712470SMatthew.Ahrens@Sun.COM } 160812470SMatthew.Ahrens@Sun.COM 16095367Sahrens void 161012296SLin.Ling@Sun.COM dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx) 16112199Sahrens { 161210242Schris.kirby@sun.com struct dsl_ds_destroyarg *dsda = arg1; 161310242Schris.kirby@sun.com dsl_dataset_t *ds = dsda->ds; 16142199Sahrens int err; 16152199Sahrens int after_branch_point = FALSE; 16162199Sahrens dsl_pool_t *dp = ds->ds_dir->dd_pool; 16172199Sahrens objset_t *mos = dp->dp_meta_objset; 16182199Sahrens dsl_dataset_t *ds_prev = NULL; 16192199Sahrens uint64_t obj; 16202199Sahrens 16216689Smaybee ASSERT(ds->ds_owner); 162210242Schris.kirby@sun.com ASSERT(dsda->defer || ds->ds_phys->ds_num_children <= 1); 16232199Sahrens ASSERT(ds->ds_prev == NULL || 16242199Sahrens ds->ds_prev->ds_phys->ds_next_snap_obj != ds->ds_object); 16252199Sahrens ASSERT3U(ds->ds_phys->ds_bp.blk_birth, <=, tx->tx_txg); 16262199Sahrens 162710242Schris.kirby@sun.com if (dsda->defer) { 162810242Schris.kirby@sun.com ASSERT(spa_version(dp->dp_spa) >= SPA_VERSION_USERREFS); 162910242Schris.kirby@sun.com if (ds->ds_userrefs > 0 || ds->ds_phys->ds_num_children > 1) { 163010242Schris.kirby@sun.com dmu_buf_will_dirty(ds->ds_dbuf, tx); 163110242Schris.kirby@sun.com ds->ds_phys->ds_flags |= DS_FLAG_DEFER_DESTROY; 163210242Schris.kirby@sun.com return; 163310242Schris.kirby@sun.com } 163410242Schris.kirby@sun.com } 163510242Schris.kirby@sun.com 16366689Smaybee /* signal any waiters that this dataset is going away */ 16376689Smaybee mutex_enter(&ds->ds_lock); 16386689Smaybee ds->ds_owner = dsl_reaper; 16396689Smaybee cv_broadcast(&ds->ds_exclusive_cv); 16406689Smaybee mutex_exit(&ds->ds_lock); 16416689Smaybee 16425378Sck153898 /* Remove our reservation */ 16435378Sck153898 if (ds->ds_reserved != 0) { 164411022STom.Erickson@Sun.COM dsl_prop_setarg_t psa; 164511022STom.Erickson@Sun.COM uint64_t value = 0; 164611022STom.Erickson@Sun.COM 164711022STom.Erickson@Sun.COM dsl_prop_setarg_init_uint64(&psa, "refreservation", 164811022STom.Erickson@Sun.COM (ZPROP_SRC_NONE | ZPROP_SRC_LOCAL | ZPROP_SRC_RECEIVED), 164911022STom.Erickson@Sun.COM &value); 165011022STom.Erickson@Sun.COM psa.psa_effective_value = 0; /* predict default value */ 165111022STom.Erickson@Sun.COM 165212296SLin.Ling@Sun.COM dsl_dataset_set_reservation_sync(ds, &psa, tx); 16535378Sck153898 ASSERT3U(ds->ds_reserved, ==, 0); 16545378Sck153898 } 16555378Sck153898 16562199Sahrens ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 16572199Sahrens 165812296SLin.Ling@Sun.COM dsl_scan_ds_destroyed(ds, tx); 16597046Sahrens 16602199Sahrens obj = ds->ds_object; 1661789Sahrens 1662789Sahrens if (ds->ds_phys->ds_prev_snap_obj != 0) { 1663789Sahrens if (ds->ds_prev) { 1664789Sahrens ds_prev = ds->ds_prev; 1665789Sahrens } else { 16666689Smaybee VERIFY(0 == dsl_dataset_hold_obj(dp, 16676689Smaybee ds->ds_phys->ds_prev_snap_obj, FTAG, &ds_prev)); 1668789Sahrens } 1669789Sahrens after_branch_point = 1670789Sahrens (ds_prev->ds_phys->ds_next_snap_obj != obj); 1671789Sahrens 1672789Sahrens dmu_buf_will_dirty(ds_prev->ds_dbuf, tx); 1673789Sahrens if (after_branch_point && 16747046Sahrens ds_prev->ds_phys->ds_next_clones_obj != 0) { 167510801SMatthew.Ahrens@Sun.COM remove_from_next_clones(ds_prev, obj, tx); 16767046Sahrens if (ds->ds_phys->ds_next_snap_obj != 0) { 16777046Sahrens VERIFY(0 == zap_add_int(mos, 16787046Sahrens ds_prev->ds_phys->ds_next_clones_obj, 16797046Sahrens ds->ds_phys->ds_next_snap_obj, tx)); 16807046Sahrens } 16817046Sahrens } 16827046Sahrens if (after_branch_point && 1683789Sahrens ds->ds_phys->ds_next_snap_obj == 0) { 1684789Sahrens /* This clone is toast. */ 1685789Sahrens ASSERT(ds_prev->ds_phys->ds_num_children > 1); 1686789Sahrens ds_prev->ds_phys->ds_num_children--; 168710242Schris.kirby@sun.com 168810242Schris.kirby@sun.com /* 168910242Schris.kirby@sun.com * If the clone's origin has no other clones, no 169010242Schris.kirby@sun.com * user holds, and has been marked for deferred 169110242Schris.kirby@sun.com * deletion, then we should have done the necessary 169210242Schris.kirby@sun.com * destroy setup for it. 169310242Schris.kirby@sun.com */ 169410242Schris.kirby@sun.com if (ds_prev->ds_phys->ds_num_children == 1 && 169510242Schris.kirby@sun.com ds_prev->ds_userrefs == 0 && 169610242Schris.kirby@sun.com DS_IS_DEFER_DESTROY(ds_prev)) { 169710242Schris.kirby@sun.com ASSERT3P(dsda->rm_origin, !=, NULL); 169810242Schris.kirby@sun.com } else { 169910242Schris.kirby@sun.com ASSERT3P(dsda->rm_origin, ==, NULL); 170010242Schris.kirby@sun.com } 1701789Sahrens } else if (!after_branch_point) { 1702789Sahrens ds_prev->ds_phys->ds_next_snap_obj = 1703789Sahrens ds->ds_phys->ds_next_snap_obj; 1704789Sahrens } 1705789Sahrens } 1706789Sahrens 170712296SLin.Ling@Sun.COM if (dsl_dataset_is_snapshot(ds)) { 1708789Sahrens dsl_dataset_t *ds_next; 17095378Sck153898 uint64_t old_unique; 171012470SMatthew.Ahrens@Sun.COM uint64_t used = 0, comp = 0, uncomp = 0; 1711789Sahrens 17126689Smaybee VERIFY(0 == dsl_dataset_hold_obj(dp, 17136689Smaybee ds->ds_phys->ds_next_snap_obj, FTAG, &ds_next)); 1714789Sahrens ASSERT3U(ds_next->ds_phys->ds_prev_snap_obj, ==, obj); 1715789Sahrens 171612296SLin.Ling@Sun.COM old_unique = ds_next->ds_phys->ds_unique_bytes; 17175378Sck153898 1718789Sahrens dmu_buf_will_dirty(ds_next->ds_dbuf, tx); 1719789Sahrens ds_next->ds_phys->ds_prev_snap_obj = 1720789Sahrens ds->ds_phys->ds_prev_snap_obj; 1721789Sahrens ds_next->ds_phys->ds_prev_snap_txg = 1722789Sahrens ds->ds_phys->ds_prev_snap_txg; 1723789Sahrens ASSERT3U(ds->ds_phys->ds_prev_snap_txg, ==, 1724789Sahrens ds_prev ? ds_prev->ds_phys->ds_creation_txg : 0); 1725789Sahrens 172612470SMatthew.Ahrens@Sun.COM 172712470SMatthew.Ahrens@Sun.COM if (ds_next->ds_deadlist.dl_oldfmt) { 172812470SMatthew.Ahrens@Sun.COM process_old_deadlist(ds, ds_prev, ds_next, 172912470SMatthew.Ahrens@Sun.COM after_branch_point, tx); 173012470SMatthew.Ahrens@Sun.COM } else { 173112470SMatthew.Ahrens@Sun.COM /* Adjust prev's unique space. */ 173212470SMatthew.Ahrens@Sun.COM if (ds_prev && !after_branch_point) { 173312470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&ds_next->ds_deadlist, 173412470SMatthew.Ahrens@Sun.COM ds_prev->ds_phys->ds_prev_snap_txg, 173512470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_prev_snap_txg, 173612470SMatthew.Ahrens@Sun.COM &used, &comp, &uncomp); 173712470SMatthew.Ahrens@Sun.COM ds_prev->ds_phys->ds_unique_bytes += used; 1738789Sahrens } 173912470SMatthew.Ahrens@Sun.COM 174012470SMatthew.Ahrens@Sun.COM /* Adjust snapused. */ 174112470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&ds_next->ds_deadlist, 174212470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_prev_snap_txg, UINT64_MAX, 174312470SMatthew.Ahrens@Sun.COM &used, &comp, &uncomp); 174412470SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP, 174512470SMatthew.Ahrens@Sun.COM -used, -comp, -uncomp, tx); 174612470SMatthew.Ahrens@Sun.COM 174712470SMatthew.Ahrens@Sun.COM /* Move blocks to be freed to pool's free list. */ 174812470SMatthew.Ahrens@Sun.COM dsl_deadlist_move_bpobj(&ds_next->ds_deadlist, 174912470SMatthew.Ahrens@Sun.COM &dp->dp_free_bpobj, ds->ds_phys->ds_prev_snap_txg, 175012470SMatthew.Ahrens@Sun.COM tx); 175112470SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(tx->tx_pool->dp_free_dir, 175212470SMatthew.Ahrens@Sun.COM DD_USED_HEAD, used, comp, uncomp, tx); 175312470SMatthew.Ahrens@Sun.COM dsl_dir_dirty(tx->tx_pool->dp_free_dir, tx); 175412470SMatthew.Ahrens@Sun.COM 175512470SMatthew.Ahrens@Sun.COM /* Merge our deadlist into next's and free it. */ 175612470SMatthew.Ahrens@Sun.COM dsl_deadlist_merge(&ds_next->ds_deadlist, 175712470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_deadlist_obj, tx); 1758789Sahrens } 175912470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds->ds_deadlist); 176012470SMatthew.Ahrens@Sun.COM dsl_deadlist_free(mos, ds->ds_phys->ds_deadlist_obj, tx); 176112470SMatthew.Ahrens@Sun.COM 176212470SMatthew.Ahrens@Sun.COM /* Collapse range in clone heads */ 176312470SMatthew.Ahrens@Sun.COM dsl_dataset_remove_clones_key(ds, 176412470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_creation_txg, tx); 1765789Sahrens 176612296SLin.Ling@Sun.COM if (dsl_dataset_is_snapshot(ds_next)) { 176712470SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds_nextnext; 176812470SMatthew.Ahrens@Sun.COM 1769789Sahrens /* 1770789Sahrens * Update next's unique to include blocks which 1771789Sahrens * were previously shared by only this snapshot 1772789Sahrens * and it. Those blocks will be born after the 1773789Sahrens * prev snap and before this snap, and will have 1774789Sahrens * died after the next snap and before the one 1775789Sahrens * after that (ie. be on the snap after next's 1776789Sahrens * deadlist). 1777789Sahrens */ 17786689Smaybee VERIFY(0 == dsl_dataset_hold_obj(dp, 17796689Smaybee ds_next->ds_phys->ds_next_snap_obj, 178012470SMatthew.Ahrens@Sun.COM FTAG, &ds_nextnext)); 178112470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&ds_nextnext->ds_deadlist, 17827390SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_prev_snap_txg, 178312470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_creation_txg, 178412470SMatthew.Ahrens@Sun.COM &used, &comp, &uncomp); 178512470SMatthew.Ahrens@Sun.COM ds_next->ds_phys->ds_unique_bytes += used; 178612470SMatthew.Ahrens@Sun.COM dsl_dataset_rele(ds_nextnext, FTAG); 1787789Sahrens ASSERT3P(ds_next->ds_prev, ==, NULL); 178812470SMatthew.Ahrens@Sun.COM 178912470SMatthew.Ahrens@Sun.COM /* Collapse range in this head. */ 179012470SMatthew.Ahrens@Sun.COM dsl_dataset_t *hds; 179112470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, 179212470SMatthew.Ahrens@Sun.COM ds->ds_dir->dd_phys->dd_head_dataset_obj, 179312470SMatthew.Ahrens@Sun.COM FTAG, &hds)); 179412470SMatthew.Ahrens@Sun.COM dsl_deadlist_remove_key(&hds->ds_deadlist, 179512470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_creation_txg, tx); 179612470SMatthew.Ahrens@Sun.COM dsl_dataset_rele(hds, FTAG); 179712470SMatthew.Ahrens@Sun.COM 1798789Sahrens } else { 1799789Sahrens ASSERT3P(ds_next->ds_prev, ==, ds); 18006689Smaybee dsl_dataset_drop_ref(ds_next->ds_prev, ds_next); 18016689Smaybee ds_next->ds_prev = NULL; 1802789Sahrens if (ds_prev) { 18036689Smaybee VERIFY(0 == dsl_dataset_get_ref(dp, 18046689Smaybee ds->ds_phys->ds_prev_snap_obj, 18056689Smaybee ds_next, &ds_next->ds_prev)); 1806789Sahrens } 18075378Sck153898 18085378Sck153898 dsl_dataset_recalc_head_uniq(ds_next); 18095378Sck153898 18105378Sck153898 /* 18115378Sck153898 * Reduce the amount of our unconsmed refreservation 18125378Sck153898 * being charged to our parent by the amount of 18135378Sck153898 * new unique data we have gained. 18145378Sck153898 */ 18155378Sck153898 if (old_unique < ds_next->ds_reserved) { 18165378Sck153898 int64_t mrsdelta; 18175378Sck153898 uint64_t new_unique = 18185378Sck153898 ds_next->ds_phys->ds_unique_bytes; 18195378Sck153898 18205378Sck153898 ASSERT(old_unique <= new_unique); 18215378Sck153898 mrsdelta = MIN(new_unique - old_unique, 18225378Sck153898 ds_next->ds_reserved - old_unique); 18237390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(ds->ds_dir, 18247390SMatthew.Ahrens@Sun.COM DD_USED_REFRSRV, -mrsdelta, 0, 0, tx); 18255378Sck153898 } 1826789Sahrens } 18276689Smaybee dsl_dataset_rele(ds_next, FTAG); 1828789Sahrens } else { 1829789Sahrens /* 1830789Sahrens * There's no next snapshot, so this is a head dataset. 1831789Sahrens * Destroy the deadlist. Unless it's a clone, the 1832789Sahrens * deadlist should be empty. (If it's a clone, it's 1833789Sahrens * safe to ignore the deadlist contents.) 1834789Sahrens */ 1835789Sahrens struct killarg ka; 1836789Sahrens 183712470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds->ds_deadlist); 183812470SMatthew.Ahrens@Sun.COM dsl_deadlist_free(mos, ds->ds_phys->ds_deadlist_obj, tx); 1839789Sahrens ds->ds_phys->ds_deadlist_obj = 0; 1840789Sahrens 1841789Sahrens /* 1842789Sahrens * Free everything that we point to (that's born after 1843789Sahrens * the previous snapshot, if we are a clone) 1844789Sahrens * 18457390SMatthew.Ahrens@Sun.COM * NB: this should be very quick, because we already 18467390SMatthew.Ahrens@Sun.COM * freed all the objects in open context. 1847789Sahrens */ 18487390SMatthew.Ahrens@Sun.COM ka.ds = ds; 1849789Sahrens ka.tx = tx; 18507837SMatthew.Ahrens@Sun.COM err = traverse_dataset(ds, ds->ds_phys->ds_prev_snap_txg, 18517837SMatthew.Ahrens@Sun.COM TRAVERSE_POST, kill_blkptr, &ka); 1852789Sahrens ASSERT3U(err, ==, 0); 18539390Schris.kirby@sun.com ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) || 18547390SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_unique_bytes == 0); 185510342Schris.kirby@sun.com 185610342Schris.kirby@sun.com if (ds->ds_prev != NULL) { 185712470SMatthew.Ahrens@Sun.COM if (spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) { 185812470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, zap_remove_int(mos, 185912470SMatthew.Ahrens@Sun.COM ds->ds_prev->ds_dir->dd_phys->dd_clones, 186012470SMatthew.Ahrens@Sun.COM ds->ds_object, tx)); 186112470SMatthew.Ahrens@Sun.COM } 186210342Schris.kirby@sun.com dsl_dataset_rele(ds->ds_prev, ds); 186310342Schris.kirby@sun.com ds->ds_prev = ds_prev = NULL; 186410342Schris.kirby@sun.com } 1865789Sahrens } 1866789Sahrens 186712827SMatthew.Ahrens@Sun.COM /* 186812827SMatthew.Ahrens@Sun.COM * This must be done after the dsl_traverse(), because it will 186912827SMatthew.Ahrens@Sun.COM * re-open the objset. 187012827SMatthew.Ahrens@Sun.COM */ 187112827SMatthew.Ahrens@Sun.COM if (ds->ds_objset) { 187212827SMatthew.Ahrens@Sun.COM dmu_objset_evict(ds->ds_objset); 187312827SMatthew.Ahrens@Sun.COM ds->ds_objset = NULL; 187412827SMatthew.Ahrens@Sun.COM } 187512827SMatthew.Ahrens@Sun.COM 18766689Smaybee if (ds->ds_dir->dd_phys->dd_head_dataset_obj == ds->ds_object) { 18776689Smaybee /* Erase the link in the dir */ 18786689Smaybee dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx); 18796689Smaybee ds->ds_dir->dd_phys->dd_head_dataset_obj = 0; 18806689Smaybee ASSERT(ds->ds_phys->ds_snapnames_zapobj != 0); 1881789Sahrens err = zap_destroy(mos, ds->ds_phys->ds_snapnames_zapobj, tx); 1882789Sahrens ASSERT(err == 0); 1883789Sahrens } else { 1884789Sahrens /* remove from snapshot namespace */ 1885789Sahrens dsl_dataset_t *ds_head; 18866689Smaybee ASSERT(ds->ds_phys->ds_snapnames_zapobj == 0); 18876689Smaybee VERIFY(0 == dsl_dataset_hold_obj(dp, 18886689Smaybee ds->ds_dir->dd_phys->dd_head_dataset_obj, FTAG, &ds_head)); 18892207Sahrens VERIFY(0 == dsl_dataset_get_snapname(ds)); 1890789Sahrens #ifdef ZFS_DEBUG 1891789Sahrens { 1892789Sahrens uint64_t val; 18936492Stimh 18946689Smaybee err = dsl_dataset_snap_lookup(ds_head, 18956492Stimh ds->ds_snapname, &val); 1896789Sahrens ASSERT3U(err, ==, 0); 1897789Sahrens ASSERT3U(val, ==, obj); 1898789Sahrens } 1899789Sahrens #endif 19006689Smaybee err = dsl_dataset_snap_remove(ds_head, ds->ds_snapname, tx); 1901789Sahrens ASSERT(err == 0); 19026689Smaybee dsl_dataset_rele(ds_head, FTAG); 1903789Sahrens } 1904789Sahrens 1905789Sahrens if (ds_prev && ds->ds_prev != ds_prev) 19066689Smaybee dsl_dataset_rele(ds_prev, FTAG); 1907789Sahrens 19085094Slling spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx); 190912296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_DESTROY, dp->dp_spa, tx, 191012296SLin.Ling@Sun.COM "dataset = %llu", ds->ds_object); 19114543Smarks 19127046Sahrens if (ds->ds_phys->ds_next_clones_obj != 0) { 19137046Sahrens uint64_t count; 19147046Sahrens ASSERT(0 == zap_count(mos, 19157046Sahrens ds->ds_phys->ds_next_clones_obj, &count) && count == 0); 19167046Sahrens VERIFY(0 == dmu_object_free(mos, 19177046Sahrens ds->ds_phys->ds_next_clones_obj, tx)); 19187046Sahrens } 19197390SMatthew.Ahrens@Sun.COM if (ds->ds_phys->ds_props_obj != 0) 19207390SMatthew.Ahrens@Sun.COM VERIFY(0 == zap_destroy(mos, ds->ds_phys->ds_props_obj, tx)); 192110242Schris.kirby@sun.com if (ds->ds_phys->ds_userrefs_obj != 0) 192210242Schris.kirby@sun.com VERIFY(0 == zap_destroy(mos, ds->ds_phys->ds_userrefs_obj, tx)); 19236689Smaybee dsl_dir_close(ds->ds_dir, ds); 19246689Smaybee ds->ds_dir = NULL; 19256689Smaybee dsl_dataset_drain_refs(ds, tag); 19262199Sahrens VERIFY(0 == dmu_object_free(mos, obj, tx)); 192710242Schris.kirby@sun.com 192810242Schris.kirby@sun.com if (dsda->rm_origin) { 192910242Schris.kirby@sun.com /* 193010242Schris.kirby@sun.com * Remove the origin of the clone we just destroyed. 193110242Schris.kirby@sun.com */ 193210242Schris.kirby@sun.com struct dsl_ds_destroyarg ndsda = {0}; 193310242Schris.kirby@sun.com 193410342Schris.kirby@sun.com ndsda.ds = dsda->rm_origin; 193512296SLin.Ling@Sun.COM dsl_dataset_destroy_sync(&ndsda, tag, tx); 193610242Schris.kirby@sun.com } 19372199Sahrens } 19382199Sahrens 19395378Sck153898 static int 19405378Sck153898 dsl_dataset_snapshot_reserve_space(dsl_dataset_t *ds, dmu_tx_t *tx) 19415378Sck153898 { 19425378Sck153898 uint64_t asize; 19435378Sck153898 19445378Sck153898 if (!dmu_tx_is_syncing(tx)) 19455378Sck153898 return (0); 19465378Sck153898 19475378Sck153898 /* 19485378Sck153898 * If there's an fs-only reservation, any blocks that might become 19495378Sck153898 * owned by the snapshot dataset must be accommodated by space 19505378Sck153898 * outside of the reservation. 19515378Sck153898 */ 195212296SLin.Ling@Sun.COM ASSERT(ds->ds_reserved == 0 || DS_UNIQUE_IS_ACCURATE(ds)); 195312296SLin.Ling@Sun.COM asize = MIN(ds->ds_phys->ds_unique_bytes, ds->ds_reserved); 195412827SMatthew.Ahrens@Sun.COM if (asize > dsl_dir_space_available(ds->ds_dir, NULL, 0, TRUE)) 19555378Sck153898 return (ENOSPC); 19565378Sck153898 19575378Sck153898 /* 19585378Sck153898 * Propogate any reserved space for this snapshot to other 19595378Sck153898 * snapshot checks in this sync group. 19605378Sck153898 */ 19615378Sck153898 if (asize > 0) 19625378Sck153898 dsl_dir_willuse_space(ds->ds_dir, asize, tx); 19635378Sck153898 19645378Sck153898 return (0); 19655378Sck153898 } 19665378Sck153898 19672199Sahrens int 19682199Sahrens dsl_dataset_snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx) 19692199Sahrens { 19705367Sahrens dsl_dataset_t *ds = arg1; 19712199Sahrens const char *snapname = arg2; 19722199Sahrens int err; 19732199Sahrens uint64_t value; 1974789Sahrens 1975789Sahrens /* 19762199Sahrens * We don't allow multiple snapshots of the same txg. If there 19772199Sahrens * is already one, try again. 19782199Sahrens */ 19792199Sahrens if (ds->ds_phys->ds_prev_snap_txg >= tx->tx_txg) 19802199Sahrens return (EAGAIN); 19812199Sahrens 19822199Sahrens /* 19832199Sahrens * Check for conflicting name snapshot name. 1984789Sahrens */ 19856689Smaybee err = dsl_dataset_snap_lookup(ds, snapname, &value); 19862199Sahrens if (err == 0) 19872199Sahrens return (EEXIST); 19882199Sahrens if (err != ENOENT) 19892199Sahrens return (err); 1990789Sahrens 19913978Smmusante /* 19923978Smmusante * Check that the dataset's name is not too long. Name consists 19933978Smmusante * of the dataset's length + 1 for the @-sign + snapshot name's length 19943978Smmusante */ 19953978Smmusante if (dsl_dataset_namelen(ds) + 1 + strlen(snapname) >= MAXNAMELEN) 19963978Smmusante return (ENAMETOOLONG); 19973978Smmusante 19985378Sck153898 err = dsl_dataset_snapshot_reserve_space(ds, tx); 19995378Sck153898 if (err) 20005378Sck153898 return (err); 20015378Sck153898 20022199Sahrens ds->ds_trysnap_txg = tx->tx_txg; 2003789Sahrens return (0); 2004789Sahrens } 2005789Sahrens 20062199Sahrens void 200712296SLin.Ling@Sun.COM dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx) 2008789Sahrens { 20095367Sahrens dsl_dataset_t *ds = arg1; 20102199Sahrens const char *snapname = arg2; 20112199Sahrens dsl_pool_t *dp = ds->ds_dir->dd_pool; 2012789Sahrens dmu_buf_t *dbuf; 2013789Sahrens dsl_dataset_phys_t *dsphys; 20147046Sahrens uint64_t dsobj, crtxg; 2015789Sahrens objset_t *mos = dp->dp_meta_objset; 2016789Sahrens int err; 2017789Sahrens 20182199Sahrens ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock)); 2019789Sahrens 20207046Sahrens /* 20217046Sahrens * The origin's ds_creation_txg has to be < TXG_INITIAL 20227046Sahrens */ 20237046Sahrens if (strcmp(snapname, ORIGIN_DIR_NAME) == 0) 20247046Sahrens crtxg = 1; 20257046Sahrens else 20267046Sahrens crtxg = tx->tx_txg; 20277046Sahrens 2028928Stabriz dsobj = dmu_object_alloc(mos, DMU_OT_DSL_DATASET, 0, 2029928Stabriz DMU_OT_DSL_DATASET, sizeof (dsl_dataset_phys_t), tx); 20301544Seschrock VERIFY(0 == dmu_bonus_hold(mos, dsobj, FTAG, &dbuf)); 2031789Sahrens dmu_buf_will_dirty(dbuf, tx); 2032789Sahrens dsphys = dbuf->db_data; 20336689Smaybee bzero(dsphys, sizeof (dsl_dataset_phys_t)); 20342199Sahrens dsphys->ds_dir_obj = ds->ds_dir->dd_object; 2035789Sahrens dsphys->ds_fsid_guid = unique_create(); 2036789Sahrens (void) random_get_pseudo_bytes((void*)&dsphys->ds_guid, 2037789Sahrens sizeof (dsphys->ds_guid)); 2038789Sahrens dsphys->ds_prev_snap_obj = ds->ds_phys->ds_prev_snap_obj; 2039789Sahrens dsphys->ds_prev_snap_txg = ds->ds_phys->ds_prev_snap_txg; 2040789Sahrens dsphys->ds_next_snap_obj = ds->ds_object; 2041789Sahrens dsphys->ds_num_children = 1; 2042789Sahrens dsphys->ds_creation_time = gethrestime_sec(); 20437046Sahrens dsphys->ds_creation_txg = crtxg; 2044789Sahrens dsphys->ds_deadlist_obj = ds->ds_phys->ds_deadlist_obj; 2045789Sahrens dsphys->ds_used_bytes = ds->ds_phys->ds_used_bytes; 2046789Sahrens dsphys->ds_compressed_bytes = ds->ds_phys->ds_compressed_bytes; 2047789Sahrens dsphys->ds_uncompressed_bytes = ds->ds_phys->ds_uncompressed_bytes; 20482082Seschrock dsphys->ds_flags = ds->ds_phys->ds_flags; 2049789Sahrens dsphys->ds_bp = ds->ds_phys->ds_bp; 20501544Seschrock dmu_buf_rele(dbuf, FTAG); 2051789Sahrens 20522199Sahrens ASSERT3U(ds->ds_prev != 0, ==, ds->ds_phys->ds_prev_snap_obj != 0); 20532199Sahrens if (ds->ds_prev) { 20547046Sahrens uint64_t next_clones_obj = 20557046Sahrens ds->ds_prev->ds_phys->ds_next_clones_obj; 20562199Sahrens ASSERT(ds->ds_prev->ds_phys->ds_next_snap_obj == 2057789Sahrens ds->ds_object || 20582199Sahrens ds->ds_prev->ds_phys->ds_num_children > 1); 20592199Sahrens if (ds->ds_prev->ds_phys->ds_next_snap_obj == ds->ds_object) { 20602199Sahrens dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx); 2061789Sahrens ASSERT3U(ds->ds_phys->ds_prev_snap_txg, ==, 20622199Sahrens ds->ds_prev->ds_phys->ds_creation_txg); 20632199Sahrens ds->ds_prev->ds_phys->ds_next_snap_obj = dsobj; 20647046Sahrens } else if (next_clones_obj != 0) { 206510801SMatthew.Ahrens@Sun.COM remove_from_next_clones(ds->ds_prev, 206610801SMatthew.Ahrens@Sun.COM dsphys->ds_next_snap_obj, tx); 20677046Sahrens VERIFY3U(0, ==, zap_add_int(mos, 20687046Sahrens next_clones_obj, dsobj, tx)); 2069789Sahrens } 2070789Sahrens } 2071789Sahrens 20725378Sck153898 /* 20735378Sck153898 * If we have a reference-reservation on this dataset, we will 20745378Sck153898 * need to increase the amount of refreservation being charged 20755378Sck153898 * since our unique space is going to zero. 20765378Sck153898 */ 20775378Sck153898 if (ds->ds_reserved) { 207812296SLin.Ling@Sun.COM int64_t delta; 207912296SLin.Ling@Sun.COM ASSERT(DS_UNIQUE_IS_ACCURATE(ds)); 208012296SLin.Ling@Sun.COM delta = MIN(ds->ds_phys->ds_unique_bytes, ds->ds_reserved); 20817390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(ds->ds_dir, DD_USED_REFRSRV, 208212296SLin.Ling@Sun.COM delta, 0, 0, tx); 20835378Sck153898 } 20845378Sck153898 2085789Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 208612470SMatthew.Ahrens@Sun.COM zfs_dbgmsg("taking snapshot %s@%s/%llu; newkey=%llu", 208712470SMatthew.Ahrens@Sun.COM ds->ds_dir->dd_myname, snapname, dsobj, 208812470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_prev_snap_txg); 208912470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_deadlist_obj = dsl_deadlist_clone(&ds->ds_deadlist, 209012470SMatthew.Ahrens@Sun.COM UINT64_MAX, ds->ds_phys->ds_prev_snap_obj, tx); 209112470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&ds->ds_deadlist); 209212470SMatthew.Ahrens@Sun.COM dsl_deadlist_open(&ds->ds_deadlist, mos, ds->ds_phys->ds_deadlist_obj); 209312470SMatthew.Ahrens@Sun.COM dsl_deadlist_add_key(&ds->ds_deadlist, 209412470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_prev_snap_txg, tx); 209512470SMatthew.Ahrens@Sun.COM 20965712Sahrens ASSERT3U(ds->ds_phys->ds_prev_snap_txg, <, tx->tx_txg); 2097789Sahrens ds->ds_phys->ds_prev_snap_obj = dsobj; 20987046Sahrens ds->ds_phys->ds_prev_snap_txg = crtxg; 2099789Sahrens ds->ds_phys->ds_unique_bytes = 0; 21005378Sck153898 if (spa_version(dp->dp_spa) >= SPA_VERSION_UNIQUE_ACCURATE) 21015378Sck153898 ds->ds_phys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE; 210212470SMatthew.Ahrens@Sun.COM 2103789Sahrens err = zap_add(mos, ds->ds_phys->ds_snapnames_zapobj, 2104789Sahrens snapname, 8, 1, &dsobj, tx); 2105789Sahrens ASSERT(err == 0); 2106789Sahrens 2107789Sahrens if (ds->ds_prev) 21086689Smaybee dsl_dataset_drop_ref(ds->ds_prev, ds); 21096689Smaybee VERIFY(0 == dsl_dataset_get_ref(dp, 21106689Smaybee ds->ds_phys->ds_prev_snap_obj, ds, &ds->ds_prev)); 21114543Smarks 211212296SLin.Ling@Sun.COM dsl_scan_ds_snapshotted(ds, tx); 21137046Sahrens 211410373Schris.kirby@sun.com dsl_dir_snap_cmtime_update(ds->ds_dir); 211510373Schris.kirby@sun.com 211612296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_SNAPSHOT, dp->dp_spa, tx, 21174603Sahrens "dataset = %llu", dsobj); 2118789Sahrens } 2119789Sahrens 2120789Sahrens void 21213547Smaybee dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx) 2122789Sahrens { 2123789Sahrens ASSERT(dmu_tx_is_syncing(tx)); 212410298SMatthew.Ahrens@Sun.COM ASSERT(ds->ds_objset != NULL); 2125789Sahrens ASSERT(ds->ds_phys->ds_next_snap_obj == 0); 2126789Sahrens 21274787Sahrens /* 21284787Sahrens * in case we had to change ds_fsid_guid when we opened it, 21294787Sahrens * sync it out now. 21304787Sahrens */ 21314787Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 21324787Sahrens ds->ds_phys->ds_fsid_guid = ds->ds_fsid_guid; 21334787Sahrens 2134789Sahrens dsl_dir_dirty(ds->ds_dir, tx); 213510298SMatthew.Ahrens@Sun.COM dmu_objset_sync(ds->ds_objset, zio, tx); 2136789Sahrens } 2137789Sahrens 2138789Sahrens void 21392885Sahrens dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv) 2140789Sahrens { 21415378Sck153898 uint64_t refd, avail, uobjs, aobjs; 21425378Sck153898 21432885Sahrens dsl_dir_stats(ds->ds_dir, nv); 2144789Sahrens 21455378Sck153898 dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs); 21465378Sck153898 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE, avail); 21475378Sck153898 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED, refd); 21485378Sck153898 21492885Sahrens dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATION, 21502885Sahrens ds->ds_phys->ds_creation_time); 21512885Sahrens dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATETXG, 21522885Sahrens ds->ds_phys->ds_creation_txg); 21535378Sck153898 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFQUOTA, 21545378Sck153898 ds->ds_quota); 21555378Sck153898 dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRESERVATION, 21565378Sck153898 ds->ds_reserved); 21576643Seschrock dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_GUID, 21586643Seschrock ds->ds_phys->ds_guid); 215910575SEric.Schrock@Sun.COM dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_UNIQUE, 216012296SLin.Ling@Sun.COM ds->ds_phys->ds_unique_bytes); 216110575SEric.Schrock@Sun.COM dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_OBJSETID, 216210575SEric.Schrock@Sun.COM ds->ds_object); 216311022STom.Erickson@Sun.COM dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USERREFS, 216411022STom.Erickson@Sun.COM ds->ds_userrefs); 216510242Schris.kirby@sun.com dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_DEFER_DESTROY, 216610242Schris.kirby@sun.com DS_IS_DEFER_DESTROY(ds) ? 1 : 0); 2167789Sahrens 2168789Sahrens if (ds->ds_phys->ds_next_snap_obj) { 2169789Sahrens /* 2170789Sahrens * This is a snapshot; override the dd's space used with 21712885Sahrens * our unique space and compression ratio. 2172789Sahrens */ 21732885Sahrens dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, 21742885Sahrens ds->ds_phys->ds_unique_bytes); 21752885Sahrens dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, 21762885Sahrens ds->ds_phys->ds_compressed_bytes == 0 ? 100 : 21772885Sahrens (ds->ds_phys->ds_uncompressed_bytes * 100 / 21782885Sahrens ds->ds_phys->ds_compressed_bytes)); 2179789Sahrens } 2180789Sahrens } 2181789Sahrens 21822885Sahrens void 21832885Sahrens dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat) 2184789Sahrens { 21852885Sahrens stat->dds_creation_txg = ds->ds_phys->ds_creation_txg; 21862885Sahrens stat->dds_inconsistent = ds->ds_phys->ds_flags & DS_FLAG_INCONSISTENT; 21875367Sahrens stat->dds_guid = ds->ds_phys->ds_guid; 21882885Sahrens if (ds->ds_phys->ds_next_snap_obj) { 21892885Sahrens stat->dds_is_snapshot = B_TRUE; 21902885Sahrens stat->dds_num_clones = ds->ds_phys->ds_num_children - 1; 21918228SEric.Taylor@Sun.COM } else { 21928228SEric.Taylor@Sun.COM stat->dds_is_snapshot = B_FALSE; 21938228SEric.Taylor@Sun.COM stat->dds_num_clones = 0; 21942885Sahrens } 21952885Sahrens 21962885Sahrens /* clone origin is really a dsl_dir thing... */ 21975446Sahrens rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER); 21987046Sahrens if (dsl_dir_is_clone(ds->ds_dir)) { 21992885Sahrens dsl_dataset_t *ods; 22002885Sahrens 22016689Smaybee VERIFY(0 == dsl_dataset_get_ref(ds->ds_dir->dd_pool, 22026689Smaybee ds->ds_dir->dd_phys->dd_origin_obj, FTAG, &ods)); 22035367Sahrens dsl_dataset_name(ods, stat->dds_origin); 22046689Smaybee dsl_dataset_drop_ref(ods, FTAG); 22058228SEric.Taylor@Sun.COM } else { 22068228SEric.Taylor@Sun.COM stat->dds_origin[0] = '\0'; 22072885Sahrens } 22085446Sahrens rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock); 22092885Sahrens } 22102885Sahrens 22112885Sahrens uint64_t 22122885Sahrens dsl_dataset_fsid_guid(dsl_dataset_t *ds) 22132885Sahrens { 22144787Sahrens return (ds->ds_fsid_guid); 22152885Sahrens } 22162885Sahrens 22172885Sahrens void 22182885Sahrens dsl_dataset_space(dsl_dataset_t *ds, 22192885Sahrens uint64_t *refdbytesp, uint64_t *availbytesp, 22202885Sahrens uint64_t *usedobjsp, uint64_t *availobjsp) 22212885Sahrens { 22222885Sahrens *refdbytesp = ds->ds_phys->ds_used_bytes; 22232885Sahrens *availbytesp = dsl_dir_space_available(ds->ds_dir, NULL, 0, TRUE); 22245378Sck153898 if (ds->ds_reserved > ds->ds_phys->ds_unique_bytes) 22255378Sck153898 *availbytesp += ds->ds_reserved - ds->ds_phys->ds_unique_bytes; 22265378Sck153898 if (ds->ds_quota != 0) { 22275378Sck153898 /* 22285378Sck153898 * Adjust available bytes according to refquota 22295378Sck153898 */ 22305378Sck153898 if (*refdbytesp < ds->ds_quota) 22315378Sck153898 *availbytesp = MIN(*availbytesp, 22325378Sck153898 ds->ds_quota - *refdbytesp); 22335378Sck153898 else 22345378Sck153898 *availbytesp = 0; 22355378Sck153898 } 22362885Sahrens *usedobjsp = ds->ds_phys->ds_bp.blk_fill; 22372885Sahrens *availobjsp = DN_MAX_OBJECT - *usedobjsp; 2238789Sahrens } 2239789Sahrens 22405326Sek110237 boolean_t 22415326Sek110237 dsl_dataset_modified_since_lastsnap(dsl_dataset_t *ds) 22425326Sek110237 { 22435326Sek110237 dsl_pool_t *dp = ds->ds_dir->dd_pool; 22445326Sek110237 22455326Sek110237 ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock) || 22465326Sek110237 dsl_pool_sync_context(dp)); 22475326Sek110237 if (ds->ds_prev == NULL) 22485326Sek110237 return (B_FALSE); 22495326Sek110237 if (ds->ds_phys->ds_bp.blk_birth > 225012827SMatthew.Ahrens@Sun.COM ds->ds_prev->ds_phys->ds_creation_txg) { 225112827SMatthew.Ahrens@Sun.COM objset_t *os, *os_prev; 225212827SMatthew.Ahrens@Sun.COM /* 225312827SMatthew.Ahrens@Sun.COM * It may be that only the ZIL differs, because it was 225412827SMatthew.Ahrens@Sun.COM * reset in the head. Don't count that as being 225512827SMatthew.Ahrens@Sun.COM * modified. 225612827SMatthew.Ahrens@Sun.COM */ 225712827SMatthew.Ahrens@Sun.COM if (dmu_objset_from_ds(ds, &os) != 0) 225812827SMatthew.Ahrens@Sun.COM return (B_TRUE); 225912827SMatthew.Ahrens@Sun.COM if (dmu_objset_from_ds(ds->ds_prev, &os_prev) != 0) 226012827SMatthew.Ahrens@Sun.COM return (B_TRUE); 226112827SMatthew.Ahrens@Sun.COM return (bcmp(&os->os_phys->os_meta_dnode, 226212827SMatthew.Ahrens@Sun.COM &os_prev->os_phys->os_meta_dnode, 226312827SMatthew.Ahrens@Sun.COM sizeof (os->os_phys->os_meta_dnode)) != 0); 226412827SMatthew.Ahrens@Sun.COM } 22655326Sek110237 return (B_FALSE); 22665326Sek110237 } 22675326Sek110237 22682199Sahrens /* ARGSUSED */ 2269789Sahrens static int 22702199Sahrens dsl_dataset_snapshot_rename_check(void *arg1, void *arg2, dmu_tx_t *tx) 2271789Sahrens { 22722199Sahrens dsl_dataset_t *ds = arg1; 22732199Sahrens char *newsnapname = arg2; 22742199Sahrens dsl_dir_t *dd = ds->ds_dir; 22752199Sahrens dsl_dataset_t *hds; 22762199Sahrens uint64_t val; 2277789Sahrens int err; 2278789Sahrens 22796689Smaybee err = dsl_dataset_hold_obj(dd->dd_pool, 22806689Smaybee dd->dd_phys->dd_head_dataset_obj, FTAG, &hds); 2281789Sahrens if (err) 2282789Sahrens return (err); 2283789Sahrens 22842199Sahrens /* new name better not be in use */ 22856689Smaybee err = dsl_dataset_snap_lookup(hds, newsnapname, &val); 22866689Smaybee dsl_dataset_rele(hds, FTAG); 2287789Sahrens 22882199Sahrens if (err == 0) 22892199Sahrens err = EEXIST; 22902199Sahrens else if (err == ENOENT) 22912199Sahrens err = 0; 22924007Smmusante 22934007Smmusante /* dataset name + 1 for the "@" + the new snapshot name must fit */ 22944007Smmusante if (dsl_dir_namelen(ds->ds_dir) + 1 + strlen(newsnapname) >= MAXNAMELEN) 22954007Smmusante err = ENAMETOOLONG; 22964007Smmusante 22972199Sahrens return (err); 22982199Sahrens } 2299789Sahrens 23002199Sahrens static void 230112296SLin.Ling@Sun.COM dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx) 23022199Sahrens { 23032199Sahrens dsl_dataset_t *ds = arg1; 23044543Smarks const char *newsnapname = arg2; 23052199Sahrens dsl_dir_t *dd = ds->ds_dir; 23062199Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 23072199Sahrens dsl_dataset_t *hds; 23082199Sahrens int err; 2309789Sahrens 23102199Sahrens ASSERT(ds->ds_phys->ds_next_snap_obj != 0); 2311789Sahrens 23126689Smaybee VERIFY(0 == dsl_dataset_hold_obj(dd->dd_pool, 23136689Smaybee dd->dd_phys->dd_head_dataset_obj, FTAG, &hds)); 2314789Sahrens 23152199Sahrens VERIFY(0 == dsl_dataset_get_snapname(ds)); 23166689Smaybee err = dsl_dataset_snap_remove(hds, ds->ds_snapname, tx); 2317789Sahrens ASSERT3U(err, ==, 0); 23182199Sahrens mutex_enter(&ds->ds_lock); 23192199Sahrens (void) strcpy(ds->ds_snapname, newsnapname); 23202199Sahrens mutex_exit(&ds->ds_lock); 23212199Sahrens err = zap_add(mos, hds->ds_phys->ds_snapnames_zapobj, 23222199Sahrens ds->ds_snapname, 8, 1, &ds->ds_object, tx); 2323789Sahrens ASSERT3U(err, ==, 0); 2324789Sahrens 232512296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_RENAME, dd->dd_pool->dp_spa, tx, 232612296SLin.Ling@Sun.COM "dataset = %llu", ds->ds_object); 23276689Smaybee dsl_dataset_rele(hds, FTAG); 2328789Sahrens } 2329789Sahrens 23305326Sek110237 struct renamesnaparg { 23314007Smmusante dsl_sync_task_group_t *dstg; 23324007Smmusante char failed[MAXPATHLEN]; 23334007Smmusante char *oldsnap; 23344007Smmusante char *newsnap; 23354007Smmusante }; 23364007Smmusante 23374007Smmusante static int 233811209SMatthew.Ahrens@Sun.COM dsl_snapshot_rename_one(const char *name, void *arg) 23394007Smmusante { 23405326Sek110237 struct renamesnaparg *ra = arg; 23414007Smmusante dsl_dataset_t *ds = NULL; 234211209SMatthew.Ahrens@Sun.COM char *snapname; 23434007Smmusante int err; 23444007Smmusante 234511209SMatthew.Ahrens@Sun.COM snapname = kmem_asprintf("%s@%s", name, ra->oldsnap); 234611209SMatthew.Ahrens@Sun.COM (void) strlcpy(ra->failed, snapname, sizeof (ra->failed)); 23474543Smarks 23484543Smarks /* 23494543Smarks * For recursive snapshot renames the parent won't be changing 23504543Smarks * so we just pass name for both the to/from argument. 23514543Smarks */ 235211209SMatthew.Ahrens@Sun.COM err = zfs_secpolicy_rename_perms(snapname, snapname, CRED()); 235311209SMatthew.Ahrens@Sun.COM if (err != 0) { 235411209SMatthew.Ahrens@Sun.COM strfree(snapname); 235511209SMatthew.Ahrens@Sun.COM return (err == ENOENT ? 0 : err); 23564543Smarks } 23574543Smarks 23586689Smaybee #ifdef _KERNEL 23596689Smaybee /* 23606689Smaybee * For all filesystems undergoing rename, we'll need to unmount it. 23616689Smaybee */ 236211209SMatthew.Ahrens@Sun.COM (void) zfs_unmount_snap(snapname, NULL); 23636689Smaybee #endif 236411209SMatthew.Ahrens@Sun.COM err = dsl_dataset_hold(snapname, ra->dstg, &ds); 236511609SMatthew.Ahrens@Sun.COM strfree(snapname); 236611609SMatthew.Ahrens@Sun.COM if (err != 0) 236711209SMatthew.Ahrens@Sun.COM return (err == ENOENT ? 0 : err); 23684007Smmusante 23694007Smmusante dsl_sync_task_create(ra->dstg, dsl_dataset_snapshot_rename_check, 23704007Smmusante dsl_dataset_snapshot_rename_sync, ds, ra->newsnap, 0); 23714007Smmusante 23724007Smmusante return (0); 23734007Smmusante } 23744007Smmusante 23754007Smmusante static int 23764007Smmusante dsl_recursive_rename(char *oldname, const char *newname) 23774007Smmusante { 23784007Smmusante int err; 23795326Sek110237 struct renamesnaparg *ra; 23804007Smmusante dsl_sync_task_t *dst; 23814007Smmusante spa_t *spa; 23824007Smmusante char *cp, *fsname = spa_strdup(oldname); 238311209SMatthew.Ahrens@Sun.COM int len = strlen(oldname) + 1; 23844007Smmusante 23854007Smmusante /* truncate the snapshot name to get the fsname */ 23864007Smmusante cp = strchr(fsname, '@'); 23874007Smmusante *cp = '\0'; 23884007Smmusante 23894603Sahrens err = spa_open(fsname, &spa, FTAG); 23904007Smmusante if (err) { 239111209SMatthew.Ahrens@Sun.COM kmem_free(fsname, len); 23924007Smmusante return (err); 23934007Smmusante } 23945326Sek110237 ra = kmem_alloc(sizeof (struct renamesnaparg), KM_SLEEP); 23954007Smmusante ra->dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); 23964007Smmusante 23974007Smmusante ra->oldsnap = strchr(oldname, '@') + 1; 23984007Smmusante ra->newsnap = strchr(newname, '@') + 1; 23994007Smmusante *ra->failed = '\0'; 24004007Smmusante 24014007Smmusante err = dmu_objset_find(fsname, dsl_snapshot_rename_one, ra, 24024007Smmusante DS_FIND_CHILDREN); 240311209SMatthew.Ahrens@Sun.COM kmem_free(fsname, len); 24044007Smmusante 24054007Smmusante if (err == 0) { 24064007Smmusante err = dsl_sync_task_group_wait(ra->dstg); 24074007Smmusante } 24084007Smmusante 24094007Smmusante for (dst = list_head(&ra->dstg->dstg_tasks); dst; 24104007Smmusante dst = list_next(&ra->dstg->dstg_tasks, dst)) { 24114007Smmusante dsl_dataset_t *ds = dst->dst_arg1; 24124007Smmusante if (dst->dst_err) { 24134007Smmusante dsl_dir_name(ds->ds_dir, ra->failed); 241411209SMatthew.Ahrens@Sun.COM (void) strlcat(ra->failed, "@", sizeof (ra->failed)); 241511209SMatthew.Ahrens@Sun.COM (void) strlcat(ra->failed, ra->newsnap, 241611209SMatthew.Ahrens@Sun.COM sizeof (ra->failed)); 24174007Smmusante } 24186689Smaybee dsl_dataset_rele(ds, ra->dstg); 24194007Smmusante } 24204007Smmusante 24214543Smarks if (err) 242211209SMatthew.Ahrens@Sun.COM (void) strlcpy(oldname, ra->failed, sizeof (ra->failed)); 24234007Smmusante 24244007Smmusante dsl_sync_task_group_destroy(ra->dstg); 24255326Sek110237 kmem_free(ra, sizeof (struct renamesnaparg)); 24264007Smmusante spa_close(spa, FTAG); 24274007Smmusante return (err); 24284007Smmusante } 24294007Smmusante 24304569Smmusante static int 243111209SMatthew.Ahrens@Sun.COM dsl_valid_rename(const char *oldname, void *arg) 24324569Smmusante { 24334569Smmusante int delta = *(int *)arg; 24344569Smmusante 24354569Smmusante if (strlen(oldname) + delta >= MAXNAMELEN) 24364569Smmusante return (ENAMETOOLONG); 24374569Smmusante 24384569Smmusante return (0); 24394569Smmusante } 24404569Smmusante 2441789Sahrens #pragma weak dmu_objset_rename = dsl_dataset_rename 2442789Sahrens int 24436689Smaybee dsl_dataset_rename(char *oldname, const char *newname, boolean_t recursive) 2444789Sahrens { 2445789Sahrens dsl_dir_t *dd; 24462199Sahrens dsl_dataset_t *ds; 2447789Sahrens const char *tail; 2448789Sahrens int err; 2449789Sahrens 24502199Sahrens err = dsl_dir_open(oldname, FTAG, &dd, &tail); 24511544Seschrock if (err) 24521544Seschrock return (err); 245311701SSanjeev.Bagewadi@Sun.COM 2454789Sahrens if (tail == NULL) { 24554569Smmusante int delta = strlen(newname) - strlen(oldname); 24564569Smmusante 24577046Sahrens /* if we're growing, validate child name lengths */ 24584569Smmusante if (delta > 0) 24594569Smmusante err = dmu_objset_find(oldname, dsl_valid_rename, 24604569Smmusante &delta, DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS); 24614569Smmusante 246211823SMatthew.Ahrens@Sun.COM if (err == 0) 24634569Smmusante err = dsl_dir_rename(dd, newname); 2464789Sahrens dsl_dir_close(dd, FTAG); 2465789Sahrens return (err); 2466789Sahrens } 246711701SSanjeev.Bagewadi@Sun.COM 2468789Sahrens if (tail[0] != '@') { 246910588SEric.Taylor@Sun.COM /* the name ended in a nonexistent component */ 2470789Sahrens dsl_dir_close(dd, FTAG); 2471789Sahrens return (ENOENT); 2472789Sahrens } 2473789Sahrens 24742199Sahrens dsl_dir_close(dd, FTAG); 24752199Sahrens 24762199Sahrens /* new name must be snapshot in same filesystem */ 24772199Sahrens tail = strchr(newname, '@'); 24782199Sahrens if (tail == NULL) 24792199Sahrens return (EINVAL); 24802199Sahrens tail++; 24812199Sahrens if (strncmp(oldname, newname, tail - newname) != 0) 24822199Sahrens return (EXDEV); 2483789Sahrens 24844007Smmusante if (recursive) { 24854007Smmusante err = dsl_recursive_rename(oldname, newname); 24864007Smmusante } else { 24876689Smaybee err = dsl_dataset_hold(oldname, FTAG, &ds); 24884007Smmusante if (err) 24894007Smmusante return (err); 24902199Sahrens 24914007Smmusante err = dsl_sync_task_do(ds->ds_dir->dd_pool, 24924007Smmusante dsl_dataset_snapshot_rename_check, 24934007Smmusante dsl_dataset_snapshot_rename_sync, ds, (char *)tail, 1); 24942199Sahrens 24956689Smaybee dsl_dataset_rele(ds, FTAG); 24964007Smmusante } 24972199Sahrens 2498789Sahrens return (err); 2499789Sahrens } 25002082Seschrock 25017046Sahrens struct promotenode { 25026689Smaybee list_node_t link; 25036689Smaybee dsl_dataset_t *ds; 25046689Smaybee }; 25056689Smaybee 25062199Sahrens struct promotearg { 25077390SMatthew.Ahrens@Sun.COM list_t shared_snaps, origin_snaps, clone_snaps; 250812296SLin.Ling@Sun.COM dsl_dataset_t *origin_origin; 25097390SMatthew.Ahrens@Sun.COM uint64_t used, comp, uncomp, unique, cloneusedsnap, originusedsnap; 251010588SEric.Taylor@Sun.COM char *err_ds; 25112199Sahrens }; 25122199Sahrens 25137390SMatthew.Ahrens@Sun.COM static int snaplist_space(list_t *l, uint64_t mintxg, uint64_t *spacep); 251412296SLin.Ling@Sun.COM static boolean_t snaplist_unstable(list_t *l); 251512296SLin.Ling@Sun.COM 25162082Seschrock static int 25172199Sahrens dsl_dataset_promote_check(void *arg1, void *arg2, dmu_tx_t *tx) 25182082Seschrock { 25192199Sahrens dsl_dataset_t *hds = arg1; 25202199Sahrens struct promotearg *pa = arg2; 25217390SMatthew.Ahrens@Sun.COM struct promotenode *snap = list_head(&pa->shared_snaps); 25226689Smaybee dsl_dataset_t *origin_ds = snap->ds; 25236689Smaybee int err; 252412470SMatthew.Ahrens@Sun.COM uint64_t unused; 25252199Sahrens 25267046Sahrens /* Check that it is a real clone */ 25277046Sahrens if (!dsl_dir_is_clone(hds->ds_dir)) 25282082Seschrock return (EINVAL); 25292082Seschrock 25302199Sahrens /* Since this is so expensive, don't do the preliminary check */ 25312199Sahrens if (!dmu_tx_is_syncing(tx)) 25322199Sahrens return (0); 25332199Sahrens 25346689Smaybee if (hds->ds_phys->ds_flags & DS_FLAG_NOPROMOTE) 25356689Smaybee return (EXDEV); 25362082Seschrock 25375367Sahrens /* compute origin's new unique space */ 25387390SMatthew.Ahrens@Sun.COM snap = list_tail(&pa->clone_snaps); 25397390SMatthew.Ahrens@Sun.COM ASSERT3U(snap->ds->ds_phys->ds_prev_snap_obj, ==, origin_ds->ds_object); 254012470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&snap->ds->ds_deadlist, 254112470SMatthew.Ahrens@Sun.COM origin_ds->ds_phys->ds_prev_snap_txg, UINT64_MAX, 254212470SMatthew.Ahrens@Sun.COM &pa->unique, &unused, &unused); 25436689Smaybee 25446689Smaybee /* 25456689Smaybee * Walk the snapshots that we are moving 25466689Smaybee * 25477390SMatthew.Ahrens@Sun.COM * Compute space to transfer. Consider the incremental changes 25487390SMatthew.Ahrens@Sun.COM * to used for each snapshot: 25497390SMatthew.Ahrens@Sun.COM * (my used) = (prev's used) + (blocks born) - (blocks killed) 25507390SMatthew.Ahrens@Sun.COM * So each snapshot gave birth to: 25517390SMatthew.Ahrens@Sun.COM * (blocks born) = (my used) - (prev's used) + (blocks killed) 25526689Smaybee * So a sequence would look like: 25537390SMatthew.Ahrens@Sun.COM * (uN - u(N-1) + kN) + ... + (u1 - u0 + k1) + (u0 - 0 + k0) 25546689Smaybee * Which simplifies to: 25557390SMatthew.Ahrens@Sun.COM * uN + kN + kN-1 + ... + k1 + k0 25566689Smaybee * Note however, if we stop before we reach the ORIGIN we get: 25577390SMatthew.Ahrens@Sun.COM * uN + kN + kN-1 + ... + kM - uM-1 25586689Smaybee */ 25596689Smaybee pa->used = origin_ds->ds_phys->ds_used_bytes; 25606689Smaybee pa->comp = origin_ds->ds_phys->ds_compressed_bytes; 25616689Smaybee pa->uncomp = origin_ds->ds_phys->ds_uncompressed_bytes; 25627390SMatthew.Ahrens@Sun.COM for (snap = list_head(&pa->shared_snaps); snap; 25637390SMatthew.Ahrens@Sun.COM snap = list_next(&pa->shared_snaps, snap)) { 25642082Seschrock uint64_t val, dlused, dlcomp, dluncomp; 25656689Smaybee dsl_dataset_t *ds = snap->ds; 25662082Seschrock 25672082Seschrock /* Check that the snapshot name does not conflict */ 25687390SMatthew.Ahrens@Sun.COM VERIFY(0 == dsl_dataset_get_snapname(ds)); 25696689Smaybee err = dsl_dataset_snap_lookup(hds, ds->ds_snapname, &val); 257010588SEric.Taylor@Sun.COM if (err == 0) { 257110588SEric.Taylor@Sun.COM err = EEXIST; 257210588SEric.Taylor@Sun.COM goto out; 257310588SEric.Taylor@Sun.COM } 25746689Smaybee if (err != ENOENT) 257510588SEric.Taylor@Sun.COM goto out; 25766689Smaybee 25776689Smaybee /* The very first snapshot does not have a deadlist */ 25787390SMatthew.Ahrens@Sun.COM if (ds->ds_phys->ds_prev_snap_obj == 0) 25797390SMatthew.Ahrens@Sun.COM continue; 25807390SMatthew.Ahrens@Sun.COM 258112470SMatthew.Ahrens@Sun.COM dsl_deadlist_space(&ds->ds_deadlist, 258212470SMatthew.Ahrens@Sun.COM &dlused, &dlcomp, &dluncomp); 25837390SMatthew.Ahrens@Sun.COM pa->used += dlused; 25847390SMatthew.Ahrens@Sun.COM pa->comp += dlcomp; 25857390SMatthew.Ahrens@Sun.COM pa->uncomp += dluncomp; 25867390SMatthew.Ahrens@Sun.COM } 25876689Smaybee 25886689Smaybee /* 25896689Smaybee * If we are a clone of a clone then we never reached ORIGIN, 25906689Smaybee * so we need to subtract out the clone origin's used space. 25916689Smaybee */ 25927390SMatthew.Ahrens@Sun.COM if (pa->origin_origin) { 25937390SMatthew.Ahrens@Sun.COM pa->used -= pa->origin_origin->ds_phys->ds_used_bytes; 25947390SMatthew.Ahrens@Sun.COM pa->comp -= pa->origin_origin->ds_phys->ds_compressed_bytes; 25957390SMatthew.Ahrens@Sun.COM pa->uncomp -= pa->origin_origin->ds_phys->ds_uncompressed_bytes; 25962082Seschrock } 25972082Seschrock 25982082Seschrock /* Check that there is enough space here */ 25997390SMatthew.Ahrens@Sun.COM err = dsl_dir_transfer_possible(origin_ds->ds_dir, hds->ds_dir, 26007390SMatthew.Ahrens@Sun.COM pa->used); 26017390SMatthew.Ahrens@Sun.COM if (err) 26027390SMatthew.Ahrens@Sun.COM return (err); 26037390SMatthew.Ahrens@Sun.COM 26047390SMatthew.Ahrens@Sun.COM /* 26057390SMatthew.Ahrens@Sun.COM * Compute the amounts of space that will be used by snapshots 26067390SMatthew.Ahrens@Sun.COM * after the promotion (for both origin and clone). For each, 26077390SMatthew.Ahrens@Sun.COM * it is the amount of space that will be on all of their 26087390SMatthew.Ahrens@Sun.COM * deadlists (that was not born before their new origin). 26097390SMatthew.Ahrens@Sun.COM */ 26107390SMatthew.Ahrens@Sun.COM if (hds->ds_dir->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) { 26117390SMatthew.Ahrens@Sun.COM uint64_t space; 26127390SMatthew.Ahrens@Sun.COM 26137390SMatthew.Ahrens@Sun.COM /* 26147390SMatthew.Ahrens@Sun.COM * Note, typically this will not be a clone of a clone, 261512296SLin.Ling@Sun.COM * so dd_origin_txg will be < TXG_INITIAL, so 261612470SMatthew.Ahrens@Sun.COM * these snaplist_space() -> dsl_deadlist_space_range() 26177390SMatthew.Ahrens@Sun.COM * calls will be fast because they do not have to 26187390SMatthew.Ahrens@Sun.COM * iterate over all bps. 26197390SMatthew.Ahrens@Sun.COM */ 26207390SMatthew.Ahrens@Sun.COM snap = list_head(&pa->origin_snaps); 26217390SMatthew.Ahrens@Sun.COM err = snaplist_space(&pa->shared_snaps, 262212296SLin.Ling@Sun.COM snap->ds->ds_dir->dd_origin_txg, &pa->cloneusedsnap); 26237390SMatthew.Ahrens@Sun.COM if (err) 26247390SMatthew.Ahrens@Sun.COM return (err); 26257390SMatthew.Ahrens@Sun.COM 26267390SMatthew.Ahrens@Sun.COM err = snaplist_space(&pa->clone_snaps, 262712296SLin.Ling@Sun.COM snap->ds->ds_dir->dd_origin_txg, &space); 26287390SMatthew.Ahrens@Sun.COM if (err) 26297390SMatthew.Ahrens@Sun.COM return (err); 26307390SMatthew.Ahrens@Sun.COM pa->cloneusedsnap += space; 26316689Smaybee } 26327390SMatthew.Ahrens@Sun.COM if (origin_ds->ds_dir->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) { 26337390SMatthew.Ahrens@Sun.COM err = snaplist_space(&pa->origin_snaps, 26347390SMatthew.Ahrens@Sun.COM origin_ds->ds_phys->ds_creation_txg, &pa->originusedsnap); 26357390SMatthew.Ahrens@Sun.COM if (err) 26367390SMatthew.Ahrens@Sun.COM return (err); 26377390SMatthew.Ahrens@Sun.COM } 26387390SMatthew.Ahrens@Sun.COM 26397390SMatthew.Ahrens@Sun.COM return (0); 264010588SEric.Taylor@Sun.COM out: 264110588SEric.Taylor@Sun.COM pa->err_ds = snap->ds->ds_snapname; 264210588SEric.Taylor@Sun.COM return (err); 26432199Sahrens } 26442082Seschrock 26452199Sahrens static void 264612296SLin.Ling@Sun.COM dsl_dataset_promote_sync(void *arg1, void *arg2, dmu_tx_t *tx) 26472199Sahrens { 26482199Sahrens dsl_dataset_t *hds = arg1; 26492199Sahrens struct promotearg *pa = arg2; 26507390SMatthew.Ahrens@Sun.COM struct promotenode *snap = list_head(&pa->shared_snaps); 26516689Smaybee dsl_dataset_t *origin_ds = snap->ds; 26527390SMatthew.Ahrens@Sun.COM dsl_dataset_t *origin_head; 26532199Sahrens dsl_dir_t *dd = hds->ds_dir; 26542199Sahrens dsl_pool_t *dp = hds->ds_dir->dd_pool; 26555367Sahrens dsl_dir_t *odd = NULL; 26567046Sahrens uint64_t oldnext_obj; 26577390SMatthew.Ahrens@Sun.COM int64_t delta; 26582199Sahrens 26592199Sahrens ASSERT(0 == (hds->ds_phys->ds_flags & DS_FLAG_NOPROMOTE)); 26602199Sahrens 26617390SMatthew.Ahrens@Sun.COM snap = list_head(&pa->origin_snaps); 26627390SMatthew.Ahrens@Sun.COM origin_head = snap->ds; 26637390SMatthew.Ahrens@Sun.COM 26642417Sahrens /* 26655367Sahrens * We need to explicitly open odd, since origin_ds's dd will be 26662417Sahrens * changing. 26672417Sahrens */ 26685367Sahrens VERIFY(0 == dsl_dir_open_obj(dp, origin_ds->ds_dir->dd_object, 26695367Sahrens NULL, FTAG, &odd)); 26702082Seschrock 26716689Smaybee /* change origin's next snap */ 26726689Smaybee dmu_buf_will_dirty(origin_ds->ds_dbuf, tx); 26737046Sahrens oldnext_obj = origin_ds->ds_phys->ds_next_snap_obj; 26747390SMatthew.Ahrens@Sun.COM snap = list_tail(&pa->clone_snaps); 26757390SMatthew.Ahrens@Sun.COM ASSERT3U(snap->ds->ds_phys->ds_prev_snap_obj, ==, origin_ds->ds_object); 26767390SMatthew.Ahrens@Sun.COM origin_ds->ds_phys->ds_next_snap_obj = snap->ds->ds_object; 26776689Smaybee 26787046Sahrens /* change the origin's next clone */ 26797046Sahrens if (origin_ds->ds_phys->ds_next_clones_obj) { 268010801SMatthew.Ahrens@Sun.COM remove_from_next_clones(origin_ds, snap->ds->ds_object, tx); 26817046Sahrens VERIFY3U(0, ==, zap_add_int(dp->dp_meta_objset, 26827046Sahrens origin_ds->ds_phys->ds_next_clones_obj, 26837046Sahrens oldnext_obj, tx)); 26847046Sahrens } 26857046Sahrens 26866689Smaybee /* change origin */ 26876689Smaybee dmu_buf_will_dirty(dd->dd_dbuf, tx); 26886689Smaybee ASSERT3U(dd->dd_phys->dd_origin_obj, ==, origin_ds->ds_object); 26896689Smaybee dd->dd_phys->dd_origin_obj = odd->dd_phys->dd_origin_obj; 269012296SLin.Ling@Sun.COM dd->dd_origin_txg = origin_head->ds_dir->dd_origin_txg; 26916689Smaybee dmu_buf_will_dirty(odd->dd_dbuf, tx); 26926689Smaybee odd->dd_phys->dd_origin_obj = origin_ds->ds_object; 269312296SLin.Ling@Sun.COM origin_head->ds_dir->dd_origin_txg = 269412296SLin.Ling@Sun.COM origin_ds->ds_phys->ds_creation_txg; 26956689Smaybee 269612470SMatthew.Ahrens@Sun.COM /* change dd_clone entries */ 269712470SMatthew.Ahrens@Sun.COM if (spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) { 269812470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, zap_remove_int(dp->dp_meta_objset, 269912470SMatthew.Ahrens@Sun.COM odd->dd_phys->dd_clones, hds->ds_object, tx)); 270012470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, zap_add_int(dp->dp_meta_objset, 270112470SMatthew.Ahrens@Sun.COM pa->origin_origin->ds_dir->dd_phys->dd_clones, 270212470SMatthew.Ahrens@Sun.COM hds->ds_object, tx)); 270312470SMatthew.Ahrens@Sun.COM 270412470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, zap_remove_int(dp->dp_meta_objset, 270512470SMatthew.Ahrens@Sun.COM pa->origin_origin->ds_dir->dd_phys->dd_clones, 270612470SMatthew.Ahrens@Sun.COM origin_head->ds_object, tx)); 270712470SMatthew.Ahrens@Sun.COM if (dd->dd_phys->dd_clones == 0) { 270812470SMatthew.Ahrens@Sun.COM dd->dd_phys->dd_clones = zap_create(dp->dp_meta_objset, 270912470SMatthew.Ahrens@Sun.COM DMU_OT_DSL_CLONES, DMU_OT_NONE, 0, tx); 271012470SMatthew.Ahrens@Sun.COM } 271112470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, zap_add_int(dp->dp_meta_objset, 271212470SMatthew.Ahrens@Sun.COM dd->dd_phys->dd_clones, origin_head->ds_object, tx)); 271312470SMatthew.Ahrens@Sun.COM 271412470SMatthew.Ahrens@Sun.COM } 271512470SMatthew.Ahrens@Sun.COM 27162082Seschrock /* move snapshots to this dir */ 27177390SMatthew.Ahrens@Sun.COM for (snap = list_head(&pa->shared_snaps); snap; 27187390SMatthew.Ahrens@Sun.COM snap = list_next(&pa->shared_snaps, snap)) { 27196689Smaybee dsl_dataset_t *ds = snap->ds; 27202082Seschrock 27217237Sek110237 /* unregister props as dsl_dir is changing */ 272210298SMatthew.Ahrens@Sun.COM if (ds->ds_objset) { 272310298SMatthew.Ahrens@Sun.COM dmu_objset_evict(ds->ds_objset); 272410298SMatthew.Ahrens@Sun.COM ds->ds_objset = NULL; 27257237Sek110237 } 27262082Seschrock /* move snap name entry */ 27277390SMatthew.Ahrens@Sun.COM VERIFY(0 == dsl_dataset_get_snapname(ds)); 27287390SMatthew.Ahrens@Sun.COM VERIFY(0 == dsl_dataset_snap_remove(origin_head, 27296689Smaybee ds->ds_snapname, tx)); 27302199Sahrens VERIFY(0 == zap_add(dp->dp_meta_objset, 27312082Seschrock hds->ds_phys->ds_snapnames_zapobj, ds->ds_snapname, 27322082Seschrock 8, 1, &ds->ds_object, tx)); 273312470SMatthew.Ahrens@Sun.COM 27342082Seschrock /* change containing dsl_dir */ 27352082Seschrock dmu_buf_will_dirty(ds->ds_dbuf, tx); 27365367Sahrens ASSERT3U(ds->ds_phys->ds_dir_obj, ==, odd->dd_object); 27372082Seschrock ds->ds_phys->ds_dir_obj = dd->dd_object; 27385367Sahrens ASSERT3P(ds->ds_dir, ==, odd); 27392082Seschrock dsl_dir_close(ds->ds_dir, ds); 27402199Sahrens VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object, 27412082Seschrock NULL, ds, &ds->ds_dir)); 27422082Seschrock 274312470SMatthew.Ahrens@Sun.COM /* move any clone references */ 274412470SMatthew.Ahrens@Sun.COM if (ds->ds_phys->ds_next_clones_obj && 274512470SMatthew.Ahrens@Sun.COM spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) { 274612470SMatthew.Ahrens@Sun.COM zap_cursor_t zc; 274712470SMatthew.Ahrens@Sun.COM zap_attribute_t za; 274812470SMatthew.Ahrens@Sun.COM 274912470SMatthew.Ahrens@Sun.COM for (zap_cursor_init(&zc, dp->dp_meta_objset, 275012470SMatthew.Ahrens@Sun.COM ds->ds_phys->ds_next_clones_obj); 275112470SMatthew.Ahrens@Sun.COM zap_cursor_retrieve(&zc, &za) == 0; 275212470SMatthew.Ahrens@Sun.COM zap_cursor_advance(&zc)) { 275312470SMatthew.Ahrens@Sun.COM dsl_dataset_t *cnds; 275412470SMatthew.Ahrens@Sun.COM uint64_t o; 275512470SMatthew.Ahrens@Sun.COM 275612470SMatthew.Ahrens@Sun.COM if (za.za_first_integer == oldnext_obj) { 275712470SMatthew.Ahrens@Sun.COM /* 275812470SMatthew.Ahrens@Sun.COM * We've already moved the 275912470SMatthew.Ahrens@Sun.COM * origin's reference. 276012470SMatthew.Ahrens@Sun.COM */ 276112470SMatthew.Ahrens@Sun.COM continue; 276212470SMatthew.Ahrens@Sun.COM } 276312470SMatthew.Ahrens@Sun.COM 276412470SMatthew.Ahrens@Sun.COM VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, 276512470SMatthew.Ahrens@Sun.COM za.za_first_integer, FTAG, &cnds)); 276612470SMatthew.Ahrens@Sun.COM o = cnds->ds_dir->dd_phys->dd_head_dataset_obj; 276712470SMatthew.Ahrens@Sun.COM 276812470SMatthew.Ahrens@Sun.COM VERIFY3U(zap_remove_int(dp->dp_meta_objset, 276912470SMatthew.Ahrens@Sun.COM odd->dd_phys->dd_clones, o, tx), ==, 0); 277012470SMatthew.Ahrens@Sun.COM VERIFY3U(zap_add_int(dp->dp_meta_objset, 277112470SMatthew.Ahrens@Sun.COM dd->dd_phys->dd_clones, o, tx), ==, 0); 277212470SMatthew.Ahrens@Sun.COM dsl_dataset_rele(cnds, FTAG); 277312470SMatthew.Ahrens@Sun.COM } 277412470SMatthew.Ahrens@Sun.COM zap_cursor_fini(&zc); 277512470SMatthew.Ahrens@Sun.COM } 277612470SMatthew.Ahrens@Sun.COM 27772082Seschrock ASSERT3U(dsl_prop_numcb(ds), ==, 0); 27787390SMatthew.Ahrens@Sun.COM } 27797390SMatthew.Ahrens@Sun.COM 27807390SMatthew.Ahrens@Sun.COM /* 27817390SMatthew.Ahrens@Sun.COM * Change space accounting. 27827390SMatthew.Ahrens@Sun.COM * Note, pa->*usedsnap and dd_used_breakdown[SNAP] will either 27837390SMatthew.Ahrens@Sun.COM * both be valid, or both be 0 (resulting in delta == 0). This 27847390SMatthew.Ahrens@Sun.COM * is true for each of {clone,origin} independently. 27857390SMatthew.Ahrens@Sun.COM */ 27867390SMatthew.Ahrens@Sun.COM 27877390SMatthew.Ahrens@Sun.COM delta = pa->cloneusedsnap - 27887390SMatthew.Ahrens@Sun.COM dd->dd_phys->dd_used_breakdown[DD_USED_SNAP]; 27897390SMatthew.Ahrens@Sun.COM ASSERT3S(delta, >=, 0); 27907390SMatthew.Ahrens@Sun.COM ASSERT3U(pa->used, >=, delta); 27917390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(dd, DD_USED_SNAP, delta, 0, 0, tx); 27927390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(dd, DD_USED_HEAD, 27937390SMatthew.Ahrens@Sun.COM pa->used - delta, pa->comp, pa->uncomp, tx); 27947390SMatthew.Ahrens@Sun.COM 27957390SMatthew.Ahrens@Sun.COM delta = pa->originusedsnap - 27967390SMatthew.Ahrens@Sun.COM odd->dd_phys->dd_used_breakdown[DD_USED_SNAP]; 27977390SMatthew.Ahrens@Sun.COM ASSERT3S(delta, <=, 0); 27987390SMatthew.Ahrens@Sun.COM ASSERT3U(pa->used, >=, -delta); 27997390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(odd, DD_USED_SNAP, delta, 0, 0, tx); 28007390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(odd, DD_USED_HEAD, 28017390SMatthew.Ahrens@Sun.COM -pa->used - delta, -pa->comp, -pa->uncomp, tx); 28027390SMatthew.Ahrens@Sun.COM 28035367Sahrens origin_ds->ds_phys->ds_unique_bytes = pa->unique; 28042082Seschrock 28054543Smarks /* log history record */ 280612296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_PROMOTE, dd->dd_pool->dp_spa, tx, 280712296SLin.Ling@Sun.COM "dataset = %llu", hds->ds_object); 28084543Smarks 28095367Sahrens dsl_dir_close(odd, FTAG); 28102082Seschrock } 28112082Seschrock 28127390SMatthew.Ahrens@Sun.COM static char *snaplist_tag = "snaplist"; 28137390SMatthew.Ahrens@Sun.COM /* 28147390SMatthew.Ahrens@Sun.COM * Make a list of dsl_dataset_t's for the snapshots between first_obj 28157390SMatthew.Ahrens@Sun.COM * (exclusive) and last_obj (inclusive). The list will be in reverse 28167390SMatthew.Ahrens@Sun.COM * order (last_obj will be the list_head()). If first_obj == 0, do all 28177390SMatthew.Ahrens@Sun.COM * snapshots back to this dataset's origin. 28187390SMatthew.Ahrens@Sun.COM */ 28197390SMatthew.Ahrens@Sun.COM static int 28207390SMatthew.Ahrens@Sun.COM snaplist_make(dsl_pool_t *dp, boolean_t own, 28217390SMatthew.Ahrens@Sun.COM uint64_t first_obj, uint64_t last_obj, list_t *l) 28227390SMatthew.Ahrens@Sun.COM { 28237390SMatthew.Ahrens@Sun.COM uint64_t obj = last_obj; 28247390SMatthew.Ahrens@Sun.COM 28257390SMatthew.Ahrens@Sun.COM ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock)); 28267390SMatthew.Ahrens@Sun.COM 28277390SMatthew.Ahrens@Sun.COM list_create(l, sizeof (struct promotenode), 28287390SMatthew.Ahrens@Sun.COM offsetof(struct promotenode, link)); 28297390SMatthew.Ahrens@Sun.COM 28307390SMatthew.Ahrens@Sun.COM while (obj != first_obj) { 28317390SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds; 28327390SMatthew.Ahrens@Sun.COM struct promotenode *snap; 28337390SMatthew.Ahrens@Sun.COM int err; 28347390SMatthew.Ahrens@Sun.COM 28357390SMatthew.Ahrens@Sun.COM if (own) { 28367390SMatthew.Ahrens@Sun.COM err = dsl_dataset_own_obj(dp, obj, 28377390SMatthew.Ahrens@Sun.COM 0, snaplist_tag, &ds); 28387390SMatthew.Ahrens@Sun.COM if (err == 0) 28397390SMatthew.Ahrens@Sun.COM dsl_dataset_make_exclusive(ds, snaplist_tag); 28407390SMatthew.Ahrens@Sun.COM } else { 28417390SMatthew.Ahrens@Sun.COM err = dsl_dataset_hold_obj(dp, obj, snaplist_tag, &ds); 28427390SMatthew.Ahrens@Sun.COM } 28437390SMatthew.Ahrens@Sun.COM if (err == ENOENT) { 28447390SMatthew.Ahrens@Sun.COM /* lost race with snapshot destroy */ 28457390SMatthew.Ahrens@Sun.COM struct promotenode *last = list_tail(l); 28467390SMatthew.Ahrens@Sun.COM ASSERT(obj != last->ds->ds_phys->ds_prev_snap_obj); 28477390SMatthew.Ahrens@Sun.COM obj = last->ds->ds_phys->ds_prev_snap_obj; 28487390SMatthew.Ahrens@Sun.COM continue; 28497390SMatthew.Ahrens@Sun.COM } else if (err) { 28507390SMatthew.Ahrens@Sun.COM return (err); 28517390SMatthew.Ahrens@Sun.COM } 28527390SMatthew.Ahrens@Sun.COM 28537390SMatthew.Ahrens@Sun.COM if (first_obj == 0) 28547390SMatthew.Ahrens@Sun.COM first_obj = ds->ds_dir->dd_phys->dd_origin_obj; 28557390SMatthew.Ahrens@Sun.COM 28567390SMatthew.Ahrens@Sun.COM snap = kmem_alloc(sizeof (struct promotenode), KM_SLEEP); 28577390SMatthew.Ahrens@Sun.COM snap->ds = ds; 28587390SMatthew.Ahrens@Sun.COM list_insert_tail(l, snap); 28597390SMatthew.Ahrens@Sun.COM obj = ds->ds_phys->ds_prev_snap_obj; 28607390SMatthew.Ahrens@Sun.COM } 28617390SMatthew.Ahrens@Sun.COM 28627390SMatthew.Ahrens@Sun.COM return (0); 28637390SMatthew.Ahrens@Sun.COM } 28647390SMatthew.Ahrens@Sun.COM 28657390SMatthew.Ahrens@Sun.COM static int 28667390SMatthew.Ahrens@Sun.COM snaplist_space(list_t *l, uint64_t mintxg, uint64_t *spacep) 28677390SMatthew.Ahrens@Sun.COM { 28687390SMatthew.Ahrens@Sun.COM struct promotenode *snap; 28697390SMatthew.Ahrens@Sun.COM 28707390SMatthew.Ahrens@Sun.COM *spacep = 0; 28717390SMatthew.Ahrens@Sun.COM for (snap = list_head(l); snap; snap = list_next(l, snap)) { 287212470SMatthew.Ahrens@Sun.COM uint64_t used, comp, uncomp; 287312470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&snap->ds->ds_deadlist, 287412470SMatthew.Ahrens@Sun.COM mintxg, UINT64_MAX, &used, &comp, &uncomp); 28757390SMatthew.Ahrens@Sun.COM *spacep += used; 28767390SMatthew.Ahrens@Sun.COM } 28777390SMatthew.Ahrens@Sun.COM return (0); 28787390SMatthew.Ahrens@Sun.COM } 28797390SMatthew.Ahrens@Sun.COM 28807390SMatthew.Ahrens@Sun.COM static void 28817390SMatthew.Ahrens@Sun.COM snaplist_destroy(list_t *l, boolean_t own) 28827390SMatthew.Ahrens@Sun.COM { 28837390SMatthew.Ahrens@Sun.COM struct promotenode *snap; 28847390SMatthew.Ahrens@Sun.COM 28858779SMark.Musante@Sun.COM if (!l || !list_link_active(&l->list_head)) 28867390SMatthew.Ahrens@Sun.COM return; 28877390SMatthew.Ahrens@Sun.COM 28887390SMatthew.Ahrens@Sun.COM while ((snap = list_tail(l)) != NULL) { 28897390SMatthew.Ahrens@Sun.COM list_remove(l, snap); 28907390SMatthew.Ahrens@Sun.COM if (own) 28917390SMatthew.Ahrens@Sun.COM dsl_dataset_disown(snap->ds, snaplist_tag); 28927390SMatthew.Ahrens@Sun.COM else 28937390SMatthew.Ahrens@Sun.COM dsl_dataset_rele(snap->ds, snaplist_tag); 28947390SMatthew.Ahrens@Sun.COM kmem_free(snap, sizeof (struct promotenode)); 28957390SMatthew.Ahrens@Sun.COM } 28967390SMatthew.Ahrens@Sun.COM list_destroy(l); 28977390SMatthew.Ahrens@Sun.COM } 28987390SMatthew.Ahrens@Sun.COM 28997390SMatthew.Ahrens@Sun.COM /* 29007390SMatthew.Ahrens@Sun.COM * Promote a clone. Nomenclature note: 29017390SMatthew.Ahrens@Sun.COM * "clone" or "cds": the original clone which is being promoted 29027390SMatthew.Ahrens@Sun.COM * "origin" or "ods": the snapshot which is originally clone's origin 29037390SMatthew.Ahrens@Sun.COM * "origin head" or "ohds": the dataset which is the head 29047390SMatthew.Ahrens@Sun.COM * (filesystem/volume) for the origin 29057390SMatthew.Ahrens@Sun.COM * "origin origin": the origin of the origin's filesystem (typically 29067390SMatthew.Ahrens@Sun.COM * NULL, indicating that the clone is not a clone of a clone). 29077390SMatthew.Ahrens@Sun.COM */ 29082082Seschrock int 290910588SEric.Taylor@Sun.COM dsl_dataset_promote(const char *name, char *conflsnap) 29102082Seschrock { 29112082Seschrock dsl_dataset_t *ds; 29126689Smaybee dsl_dir_t *dd; 29136689Smaybee dsl_pool_t *dp; 29142082Seschrock dmu_object_info_t doi; 29157390SMatthew.Ahrens@Sun.COM struct promotearg pa = { 0 }; 29167046Sahrens struct promotenode *snap; 29176689Smaybee int err; 29186689Smaybee 29196689Smaybee err = dsl_dataset_hold(name, FTAG, &ds); 29202082Seschrock if (err) 29212082Seschrock return (err); 29226689Smaybee dd = ds->ds_dir; 29236689Smaybee dp = dd->dd_pool; 29246689Smaybee 29256689Smaybee err = dmu_object_info(dp->dp_meta_objset, 29262082Seschrock ds->ds_phys->ds_snapnames_zapobj, &doi); 29272082Seschrock if (err) { 29286689Smaybee dsl_dataset_rele(ds, FTAG); 29292082Seschrock return (err); 29302082Seschrock } 29312082Seschrock 29327390SMatthew.Ahrens@Sun.COM if (dsl_dataset_is_snapshot(ds) || dd->dd_phys->dd_origin_obj == 0) { 29337390SMatthew.Ahrens@Sun.COM dsl_dataset_rele(ds, FTAG); 29347390SMatthew.Ahrens@Sun.COM return (EINVAL); 29357390SMatthew.Ahrens@Sun.COM } 29367390SMatthew.Ahrens@Sun.COM 29372082Seschrock /* 29386689Smaybee * We are going to inherit all the snapshots taken before our 29396689Smaybee * origin (i.e., our new origin will be our parent's origin). 29406689Smaybee * Take ownership of them so that we can rename them into our 29416689Smaybee * namespace. 29426689Smaybee */ 29436689Smaybee rw_enter(&dp->dp_config_rwlock, RW_READER); 29447390SMatthew.Ahrens@Sun.COM 29457390SMatthew.Ahrens@Sun.COM err = snaplist_make(dp, B_TRUE, 0, dd->dd_phys->dd_origin_obj, 29467390SMatthew.Ahrens@Sun.COM &pa.shared_snaps); 29477390SMatthew.Ahrens@Sun.COM if (err != 0) 29487390SMatthew.Ahrens@Sun.COM goto out; 29497390SMatthew.Ahrens@Sun.COM 29507390SMatthew.Ahrens@Sun.COM err = snaplist_make(dp, B_FALSE, 0, ds->ds_object, &pa.clone_snaps); 29517390SMatthew.Ahrens@Sun.COM if (err != 0) 29527390SMatthew.Ahrens@Sun.COM goto out; 29537390SMatthew.Ahrens@Sun.COM 29547390SMatthew.Ahrens@Sun.COM snap = list_head(&pa.shared_snaps); 29557390SMatthew.Ahrens@Sun.COM ASSERT3U(snap->ds->ds_object, ==, dd->dd_phys->dd_origin_obj); 29567390SMatthew.Ahrens@Sun.COM err = snaplist_make(dp, B_FALSE, dd->dd_phys->dd_origin_obj, 29577390SMatthew.Ahrens@Sun.COM snap->ds->ds_dir->dd_phys->dd_head_dataset_obj, &pa.origin_snaps); 29587390SMatthew.Ahrens@Sun.COM if (err != 0) 29597390SMatthew.Ahrens@Sun.COM goto out; 29607390SMatthew.Ahrens@Sun.COM 296112470SMatthew.Ahrens@Sun.COM if (snap->ds->ds_dir->dd_phys->dd_origin_obj != 0) { 296212470SMatthew.Ahrens@Sun.COM err = dsl_dataset_hold_obj(dp, 29637390SMatthew.Ahrens@Sun.COM snap->ds->ds_dir->dd_phys->dd_origin_obj, 296412470SMatthew.Ahrens@Sun.COM FTAG, &pa.origin_origin); 29657390SMatthew.Ahrens@Sun.COM if (err != 0) 29666689Smaybee goto out; 29676689Smaybee } 29687390SMatthew.Ahrens@Sun.COM 29697390SMatthew.Ahrens@Sun.COM out: 29706689Smaybee rw_exit(&dp->dp_config_rwlock); 29716689Smaybee 29726689Smaybee /* 29732082Seschrock * Add in 128x the snapnames zapobj size, since we will be moving 29742082Seschrock * a bunch of snapnames to the promoted ds, and dirtying their 29752082Seschrock * bonus buffers. 29762082Seschrock */ 29777390SMatthew.Ahrens@Sun.COM if (err == 0) { 29787390SMatthew.Ahrens@Sun.COM err = dsl_sync_task_do(dp, dsl_dataset_promote_check, 29797390SMatthew.Ahrens@Sun.COM dsl_dataset_promote_sync, ds, &pa, 298010922SJeff.Bonwick@Sun.COM 2 + 2 * doi.doi_physical_blocks_512); 298110588SEric.Taylor@Sun.COM if (err && pa.err_ds && conflsnap) 298210588SEric.Taylor@Sun.COM (void) strncpy(conflsnap, pa.err_ds, MAXNAMELEN); 29836689Smaybee } 29847390SMatthew.Ahrens@Sun.COM 29857390SMatthew.Ahrens@Sun.COM snaplist_destroy(&pa.shared_snaps, B_TRUE); 29867390SMatthew.Ahrens@Sun.COM snaplist_destroy(&pa.clone_snaps, B_FALSE); 29877390SMatthew.Ahrens@Sun.COM snaplist_destroy(&pa.origin_snaps, B_FALSE); 29887390SMatthew.Ahrens@Sun.COM if (pa.origin_origin) 298912470SMatthew.Ahrens@Sun.COM dsl_dataset_rele(pa.origin_origin, FTAG); 29906689Smaybee dsl_dataset_rele(ds, FTAG); 29912082Seschrock return (err); 29922082Seschrock } 29933912Slling 29945367Sahrens struct cloneswaparg { 29955367Sahrens dsl_dataset_t *cds; /* clone dataset */ 29965367Sahrens dsl_dataset_t *ohds; /* origin's head dataset */ 29975367Sahrens boolean_t force; 29985481Sck153898 int64_t unused_refres_delta; /* change in unconsumed refreservation */ 29995367Sahrens }; 30005326Sek110237 30015326Sek110237 /* ARGSUSED */ 30025326Sek110237 static int 30035326Sek110237 dsl_dataset_clone_swap_check(void *arg1, void *arg2, dmu_tx_t *tx) 30045326Sek110237 { 30055367Sahrens struct cloneswaparg *csa = arg1; 30065326Sek110237 30075367Sahrens /* they should both be heads */ 30085367Sahrens if (dsl_dataset_is_snapshot(csa->cds) || 30095367Sahrens dsl_dataset_is_snapshot(csa->ohds)) 30105326Sek110237 return (EINVAL); 30115326Sek110237 30125367Sahrens /* the branch point should be just before them */ 30135367Sahrens if (csa->cds->ds_prev != csa->ohds->ds_prev) 30145326Sek110237 return (EINVAL); 30155326Sek110237 301610272SMatthew.Ahrens@Sun.COM /* cds should be the clone (unless they are unrelated) */ 301710272SMatthew.Ahrens@Sun.COM if (csa->cds->ds_prev != NULL && 301810272SMatthew.Ahrens@Sun.COM csa->cds->ds_prev != csa->cds->ds_dir->dd_pool->dp_origin_snap && 301910272SMatthew.Ahrens@Sun.COM csa->ohds->ds_object != 302010272SMatthew.Ahrens@Sun.COM csa->cds->ds_prev->ds_phys->ds_next_snap_obj) 30215367Sahrens return (EINVAL); 30225326Sek110237 30235367Sahrens /* the clone should be a child of the origin */ 30245367Sahrens if (csa->cds->ds_dir->dd_parent != csa->ohds->ds_dir) 30255367Sahrens return (EINVAL); 30265326Sek110237 30275367Sahrens /* ohds shouldn't be modified unless 'force' */ 30285367Sahrens if (!csa->force && dsl_dataset_modified_since_lastsnap(csa->ohds)) 30295367Sahrens return (ETXTBSY); 30305481Sck153898 30315481Sck153898 /* adjust amount of any unconsumed refreservation */ 30325481Sck153898 csa->unused_refres_delta = 30335481Sck153898 (int64_t)MIN(csa->ohds->ds_reserved, 30345481Sck153898 csa->ohds->ds_phys->ds_unique_bytes) - 30355481Sck153898 (int64_t)MIN(csa->ohds->ds_reserved, 30365481Sck153898 csa->cds->ds_phys->ds_unique_bytes); 30375481Sck153898 30385481Sck153898 if (csa->unused_refres_delta > 0 && 30395481Sck153898 csa->unused_refres_delta > 30405481Sck153898 dsl_dir_space_available(csa->ohds->ds_dir, NULL, 0, TRUE)) 30415481Sck153898 return (ENOSPC); 30425481Sck153898 304310799SChris.Kirby@sun.com if (csa->ohds->ds_quota != 0 && 304410799SChris.Kirby@sun.com csa->cds->ds_phys->ds_unique_bytes > csa->ohds->ds_quota) 304510799SChris.Kirby@sun.com return (EDQUOT); 304610799SChris.Kirby@sun.com 30475367Sahrens return (0); 30485326Sek110237 } 30495326Sek110237 30505326Sek110237 /* ARGSUSED */ 30515326Sek110237 static void 305212296SLin.Ling@Sun.COM dsl_dataset_clone_swap_sync(void *arg1, void *arg2, dmu_tx_t *tx) 30535326Sek110237 { 30545367Sahrens struct cloneswaparg *csa = arg1; 30555367Sahrens dsl_pool_t *dp = csa->cds->ds_dir->dd_pool; 30565326Sek110237 30575481Sck153898 ASSERT(csa->cds->ds_reserved == 0); 305810799SChris.Kirby@sun.com ASSERT(csa->ohds->ds_quota == 0 || 305910799SChris.Kirby@sun.com csa->cds->ds_phys->ds_unique_bytes <= csa->ohds->ds_quota); 30605481Sck153898 30615367Sahrens dmu_buf_will_dirty(csa->cds->ds_dbuf, tx); 30625367Sahrens dmu_buf_will_dirty(csa->ohds->ds_dbuf, tx); 30635326Sek110237 306410298SMatthew.Ahrens@Sun.COM if (csa->cds->ds_objset != NULL) { 306510298SMatthew.Ahrens@Sun.COM dmu_objset_evict(csa->cds->ds_objset); 306610298SMatthew.Ahrens@Sun.COM csa->cds->ds_objset = NULL; 30675367Sahrens } 30685326Sek110237 306910298SMatthew.Ahrens@Sun.COM if (csa->ohds->ds_objset != NULL) { 307010298SMatthew.Ahrens@Sun.COM dmu_objset_evict(csa->ohds->ds_objset); 307110298SMatthew.Ahrens@Sun.COM csa->ohds->ds_objset = NULL; 30725367Sahrens } 30735326Sek110237 307410272SMatthew.Ahrens@Sun.COM /* 307510272SMatthew.Ahrens@Sun.COM * Reset origin's unique bytes, if it exists. 307610272SMatthew.Ahrens@Sun.COM */ 307710272SMatthew.Ahrens@Sun.COM if (csa->cds->ds_prev) { 307810272SMatthew.Ahrens@Sun.COM dsl_dataset_t *origin = csa->cds->ds_prev; 307912470SMatthew.Ahrens@Sun.COM uint64_t comp, uncomp; 308012470SMatthew.Ahrens@Sun.COM 308110272SMatthew.Ahrens@Sun.COM dmu_buf_will_dirty(origin->ds_dbuf, tx); 308212470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&csa->cds->ds_deadlist, 308310272SMatthew.Ahrens@Sun.COM origin->ds_phys->ds_prev_snap_txg, UINT64_MAX, 308412470SMatthew.Ahrens@Sun.COM &origin->ds_phys->ds_unique_bytes, &comp, &uncomp); 308510272SMatthew.Ahrens@Sun.COM } 30865326Sek110237 30875326Sek110237 /* swap blkptrs */ 30885326Sek110237 { 30895326Sek110237 blkptr_t tmp; 30905367Sahrens tmp = csa->ohds->ds_phys->ds_bp; 30915367Sahrens csa->ohds->ds_phys->ds_bp = csa->cds->ds_phys->ds_bp; 30925367Sahrens csa->cds->ds_phys->ds_bp = tmp; 30935326Sek110237 } 30945326Sek110237 30955326Sek110237 /* set dd_*_bytes */ 30965326Sek110237 { 30975326Sek110237 int64_t dused, dcomp, duncomp; 30985326Sek110237 uint64_t cdl_used, cdl_comp, cdl_uncomp; 30995326Sek110237 uint64_t odl_used, odl_comp, odl_uncomp; 31005326Sek110237 31017390SMatthew.Ahrens@Sun.COM ASSERT3U(csa->cds->ds_dir->dd_phys-> 31027390SMatthew.Ahrens@Sun.COM dd_used_breakdown[DD_USED_SNAP], ==, 0); 31037390SMatthew.Ahrens@Sun.COM 310412470SMatthew.Ahrens@Sun.COM dsl_deadlist_space(&csa->cds->ds_deadlist, 310512470SMatthew.Ahrens@Sun.COM &cdl_used, &cdl_comp, &cdl_uncomp); 310612470SMatthew.Ahrens@Sun.COM dsl_deadlist_space(&csa->ohds->ds_deadlist, 310712470SMatthew.Ahrens@Sun.COM &odl_used, &odl_comp, &odl_uncomp); 31087390SMatthew.Ahrens@Sun.COM 31095367Sahrens dused = csa->cds->ds_phys->ds_used_bytes + cdl_used - 31105367Sahrens (csa->ohds->ds_phys->ds_used_bytes + odl_used); 31115367Sahrens dcomp = csa->cds->ds_phys->ds_compressed_bytes + cdl_comp - 31125367Sahrens (csa->ohds->ds_phys->ds_compressed_bytes + odl_comp); 31135367Sahrens duncomp = csa->cds->ds_phys->ds_uncompressed_bytes + 31145367Sahrens cdl_uncomp - 31155367Sahrens (csa->ohds->ds_phys->ds_uncompressed_bytes + odl_uncomp); 31165326Sek110237 31177390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(csa->ohds->ds_dir, DD_USED_HEAD, 31185367Sahrens dused, dcomp, duncomp, tx); 31197390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(csa->cds->ds_dir, DD_USED_HEAD, 31205367Sahrens -dused, -dcomp, -duncomp, tx); 31217390SMatthew.Ahrens@Sun.COM 31227390SMatthew.Ahrens@Sun.COM /* 31237390SMatthew.Ahrens@Sun.COM * The difference in the space used by snapshots is the 31247390SMatthew.Ahrens@Sun.COM * difference in snapshot space due to the head's 31257390SMatthew.Ahrens@Sun.COM * deadlist (since that's the only thing that's 31267390SMatthew.Ahrens@Sun.COM * changing that affects the snapused). 31277390SMatthew.Ahrens@Sun.COM */ 312812470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&csa->cds->ds_deadlist, 312912470SMatthew.Ahrens@Sun.COM csa->ohds->ds_dir->dd_origin_txg, UINT64_MAX, 313012470SMatthew.Ahrens@Sun.COM &cdl_used, &cdl_comp, &cdl_uncomp); 313112470SMatthew.Ahrens@Sun.COM dsl_deadlist_space_range(&csa->ohds->ds_deadlist, 313212470SMatthew.Ahrens@Sun.COM csa->ohds->ds_dir->dd_origin_txg, UINT64_MAX, 313312470SMatthew.Ahrens@Sun.COM &odl_used, &odl_comp, &odl_uncomp); 31347390SMatthew.Ahrens@Sun.COM dsl_dir_transfer_space(csa->ohds->ds_dir, cdl_used - odl_used, 31357390SMatthew.Ahrens@Sun.COM DD_USED_HEAD, DD_USED_SNAP, tx); 31365367Sahrens } 31375367Sahrens 31385326Sek110237 /* swap ds_*_bytes */ 31395367Sahrens SWITCH64(csa->ohds->ds_phys->ds_used_bytes, 31405367Sahrens csa->cds->ds_phys->ds_used_bytes); 31415367Sahrens SWITCH64(csa->ohds->ds_phys->ds_compressed_bytes, 31425367Sahrens csa->cds->ds_phys->ds_compressed_bytes); 31435367Sahrens SWITCH64(csa->ohds->ds_phys->ds_uncompressed_bytes, 31445367Sahrens csa->cds->ds_phys->ds_uncompressed_bytes); 31455481Sck153898 SWITCH64(csa->ohds->ds_phys->ds_unique_bytes, 31465481Sck153898 csa->cds->ds_phys->ds_unique_bytes); 31475481Sck153898 31485481Sck153898 /* apply any parent delta for change in unconsumed refreservation */ 31497390SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(csa->ohds->ds_dir, DD_USED_REFRSRV, 31507390SMatthew.Ahrens@Sun.COM csa->unused_refres_delta, 0, 0, tx); 31515326Sek110237 315212470SMatthew.Ahrens@Sun.COM /* 315312470SMatthew.Ahrens@Sun.COM * Swap deadlists. 315412470SMatthew.Ahrens@Sun.COM */ 315512470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&csa->cds->ds_deadlist); 315612470SMatthew.Ahrens@Sun.COM dsl_deadlist_close(&csa->ohds->ds_deadlist); 31575367Sahrens SWITCH64(csa->ohds->ds_phys->ds_deadlist_obj, 31585367Sahrens csa->cds->ds_phys->ds_deadlist_obj); 315912470SMatthew.Ahrens@Sun.COM dsl_deadlist_open(&csa->cds->ds_deadlist, dp->dp_meta_objset, 316012470SMatthew.Ahrens@Sun.COM csa->cds->ds_phys->ds_deadlist_obj); 316112470SMatthew.Ahrens@Sun.COM dsl_deadlist_open(&csa->ohds->ds_deadlist, dp->dp_meta_objset, 316212470SMatthew.Ahrens@Sun.COM csa->ohds->ds_phys->ds_deadlist_obj); 31637837SMatthew.Ahrens@Sun.COM 316412296SLin.Ling@Sun.COM dsl_scan_ds_clone_swapped(csa->ohds, csa->cds, tx); 31655326Sek110237 } 31665326Sek110237 31675326Sek110237 /* 316810272SMatthew.Ahrens@Sun.COM * Swap 'clone' with its origin head datasets. Used at the end of "zfs 316910272SMatthew.Ahrens@Sun.COM * recv" into an existing fs to swizzle the file system to the new 317010272SMatthew.Ahrens@Sun.COM * version, and by "zfs rollback". Can also be used to swap two 317110272SMatthew.Ahrens@Sun.COM * independent head datasets if neither has any snapshots. 31725326Sek110237 */ 31735326Sek110237 int 31745367Sahrens dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head, 31755367Sahrens boolean_t force) 31765326Sek110237 { 31775367Sahrens struct cloneswaparg csa; 31786689Smaybee int error; 31796689Smaybee 31806689Smaybee ASSERT(clone->ds_owner); 31816689Smaybee ASSERT(origin_head->ds_owner); 31826689Smaybee retry: 318312711SChris.Kirby@oracle.com /* 318412711SChris.Kirby@oracle.com * Need exclusive access for the swap. If we're swapping these 318512711SChris.Kirby@oracle.com * datasets back after an error, we already hold the locks. 318612711SChris.Kirby@oracle.com */ 318712711SChris.Kirby@oracle.com if (!RW_WRITE_HELD(&clone->ds_rwlock)) 318812711SChris.Kirby@oracle.com rw_enter(&clone->ds_rwlock, RW_WRITER); 318912711SChris.Kirby@oracle.com if (!RW_WRITE_HELD(&origin_head->ds_rwlock) && 319012711SChris.Kirby@oracle.com !rw_tryenter(&origin_head->ds_rwlock, RW_WRITER)) { 31916689Smaybee rw_exit(&clone->ds_rwlock); 31926689Smaybee rw_enter(&origin_head->ds_rwlock, RW_WRITER); 31936689Smaybee if (!rw_tryenter(&clone->ds_rwlock, RW_WRITER)) { 31946689Smaybee rw_exit(&origin_head->ds_rwlock); 31956689Smaybee goto retry; 31966689Smaybee } 31976689Smaybee } 31985367Sahrens csa.cds = clone; 31995367Sahrens csa.ohds = origin_head; 32005367Sahrens csa.force = force; 32016689Smaybee error = dsl_sync_task_do(clone->ds_dir->dd_pool, 32025326Sek110237 dsl_dataset_clone_swap_check, 32036689Smaybee dsl_dataset_clone_swap_sync, &csa, NULL, 9); 32046689Smaybee return (error); 32055326Sek110237 } 32065326Sek110237 32073912Slling /* 32083912Slling * Given a pool name and a dataset object number in that pool, 32093912Slling * return the name of that dataset. 32103912Slling */ 32113912Slling int 32123912Slling dsl_dsobj_to_dsname(char *pname, uint64_t obj, char *buf) 32133912Slling { 32143912Slling spa_t *spa; 32153912Slling dsl_pool_t *dp; 32166689Smaybee dsl_dataset_t *ds; 32173912Slling int error; 32183912Slling 32193912Slling if ((error = spa_open(pname, &spa, FTAG)) != 0) 32203912Slling return (error); 32213912Slling dp = spa_get_dsl(spa); 32223912Slling rw_enter(&dp->dp_config_rwlock, RW_READER); 32236689Smaybee if ((error = dsl_dataset_hold_obj(dp, obj, FTAG, &ds)) == 0) { 32246689Smaybee dsl_dataset_name(ds, buf); 32256689Smaybee dsl_dataset_rele(ds, FTAG); 32263912Slling } 32273912Slling rw_exit(&dp->dp_config_rwlock); 32283912Slling spa_close(spa, FTAG); 32293912Slling 32306689Smaybee return (error); 32313912Slling } 32325378Sck153898 32335378Sck153898 int 32345378Sck153898 dsl_dataset_check_quota(dsl_dataset_t *ds, boolean_t check_quota, 32356689Smaybee uint64_t asize, uint64_t inflight, uint64_t *used, uint64_t *ref_rsrv) 32365378Sck153898 { 32375378Sck153898 int error = 0; 32385378Sck153898 32395378Sck153898 ASSERT3S(asize, >, 0); 32405378Sck153898 32415831Sck153898 /* 32425831Sck153898 * *ref_rsrv is the portion of asize that will come from any 32435831Sck153898 * unconsumed refreservation space. 32445831Sck153898 */ 32455831Sck153898 *ref_rsrv = 0; 32465831Sck153898 32475378Sck153898 mutex_enter(&ds->ds_lock); 32485378Sck153898 /* 32495378Sck153898 * Make a space adjustment for reserved bytes. 32505378Sck153898 */ 32515378Sck153898 if (ds->ds_reserved > ds->ds_phys->ds_unique_bytes) { 32525378Sck153898 ASSERT3U(*used, >=, 32535378Sck153898 ds->ds_reserved - ds->ds_phys->ds_unique_bytes); 32545378Sck153898 *used -= (ds->ds_reserved - ds->ds_phys->ds_unique_bytes); 32555831Sck153898 *ref_rsrv = 32565831Sck153898 asize - MIN(asize, parent_delta(ds, asize + inflight)); 32575378Sck153898 } 32585378Sck153898 32595378Sck153898 if (!check_quota || ds->ds_quota == 0) { 32605378Sck153898 mutex_exit(&ds->ds_lock); 32615378Sck153898 return (0); 32625378Sck153898 } 32635378Sck153898 /* 32645378Sck153898 * If they are requesting more space, and our current estimate 32655378Sck153898 * is over quota, they get to try again unless the actual 32665378Sck153898 * on-disk is over quota and there are no pending changes (which 32675378Sck153898 * may free up space for us). 32685378Sck153898 */ 32695378Sck153898 if (ds->ds_phys->ds_used_bytes + inflight >= ds->ds_quota) { 32705378Sck153898 if (inflight > 0 || ds->ds_phys->ds_used_bytes < ds->ds_quota) 32715378Sck153898 error = ERESTART; 32725378Sck153898 else 32735378Sck153898 error = EDQUOT; 32745378Sck153898 } 32755378Sck153898 mutex_exit(&ds->ds_lock); 32765378Sck153898 32775378Sck153898 return (error); 32785378Sck153898 } 32795378Sck153898 32805378Sck153898 /* ARGSUSED */ 32815378Sck153898 static int 32825378Sck153898 dsl_dataset_set_quota_check(void *arg1, void *arg2, dmu_tx_t *tx) 32835378Sck153898 { 32845378Sck153898 dsl_dataset_t *ds = arg1; 328511022STom.Erickson@Sun.COM dsl_prop_setarg_t *psa = arg2; 328611022STom.Erickson@Sun.COM int err; 32875378Sck153898 32885378Sck153898 if (spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_REFQUOTA) 32895378Sck153898 return (ENOTSUP); 32905378Sck153898 329111022STom.Erickson@Sun.COM if ((err = dsl_prop_predict_sync(ds->ds_dir, psa)) != 0) 329211022STom.Erickson@Sun.COM return (err); 329311022STom.Erickson@Sun.COM 329411022STom.Erickson@Sun.COM if (psa->psa_effective_value == 0) 32955378Sck153898 return (0); 32965378Sck153898 329711022STom.Erickson@Sun.COM if (psa->psa_effective_value < ds->ds_phys->ds_used_bytes || 329811022STom.Erickson@Sun.COM psa->psa_effective_value < ds->ds_reserved) 32995378Sck153898 return (ENOSPC); 33005378Sck153898 33015378Sck153898 return (0); 33025378Sck153898 } 33035378Sck153898 330412296SLin.Ling@Sun.COM extern void dsl_prop_set_sync(void *, void *, dmu_tx_t *); 330511022STom.Erickson@Sun.COM 33065378Sck153898 void 330712296SLin.Ling@Sun.COM dsl_dataset_set_quota_sync(void *arg1, void *arg2, dmu_tx_t *tx) 33085378Sck153898 { 33095378Sck153898 dsl_dataset_t *ds = arg1; 331011022STom.Erickson@Sun.COM dsl_prop_setarg_t *psa = arg2; 331111022STom.Erickson@Sun.COM uint64_t effective_value = psa->psa_effective_value; 331211022STom.Erickson@Sun.COM 331312296SLin.Ling@Sun.COM dsl_prop_set_sync(ds, psa, tx); 331411022STom.Erickson@Sun.COM DSL_PROP_CHECK_PREDICTION(ds->ds_dir, psa); 331511022STom.Erickson@Sun.COM 331611022STom.Erickson@Sun.COM if (ds->ds_quota != effective_value) { 331711022STom.Erickson@Sun.COM dmu_buf_will_dirty(ds->ds_dbuf, tx); 331811022STom.Erickson@Sun.COM ds->ds_quota = effective_value; 331911022STom.Erickson@Sun.COM 332012296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_REFQUOTA, 332112296SLin.Ling@Sun.COM ds->ds_dir->dd_pool->dp_spa, tx, "%lld dataset = %llu ", 332211022STom.Erickson@Sun.COM (longlong_t)ds->ds_quota, ds->ds_object); 332311022STom.Erickson@Sun.COM } 33245378Sck153898 } 33255378Sck153898 33265378Sck153898 int 332711022STom.Erickson@Sun.COM dsl_dataset_set_quota(const char *dsname, zprop_source_t source, uint64_t quota) 33285378Sck153898 { 33295378Sck153898 dsl_dataset_t *ds; 333011022STom.Erickson@Sun.COM dsl_prop_setarg_t psa; 33315378Sck153898 int err; 33325378Sck153898 333311022STom.Erickson@Sun.COM dsl_prop_setarg_init_uint64(&psa, "refquota", source, "a); 333411022STom.Erickson@Sun.COM 33356689Smaybee err = dsl_dataset_hold(dsname, FTAG, &ds); 33365378Sck153898 if (err) 33375378Sck153898 return (err); 33385378Sck153898 333911022STom.Erickson@Sun.COM /* 334011022STom.Erickson@Sun.COM * If someone removes a file, then tries to set the quota, we 334111022STom.Erickson@Sun.COM * want to make sure the file freeing takes effect. 334211022STom.Erickson@Sun.COM */ 334311022STom.Erickson@Sun.COM txg_wait_open(ds->ds_dir->dd_pool, 0); 334411022STom.Erickson@Sun.COM 334511022STom.Erickson@Sun.COM err = dsl_sync_task_do(ds->ds_dir->dd_pool, 334611022STom.Erickson@Sun.COM dsl_dataset_set_quota_check, dsl_dataset_set_quota_sync, 334711022STom.Erickson@Sun.COM ds, &psa, 0); 334811022STom.Erickson@Sun.COM 33496689Smaybee dsl_dataset_rele(ds, FTAG); 33505378Sck153898 return (err); 33515378Sck153898 } 33525378Sck153898 33535378Sck153898 static int 33545378Sck153898 dsl_dataset_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx) 33555378Sck153898 { 33565378Sck153898 dsl_dataset_t *ds = arg1; 335711022STom.Erickson@Sun.COM dsl_prop_setarg_t *psa = arg2; 335811022STom.Erickson@Sun.COM uint64_t effective_value; 33595378Sck153898 uint64_t unique; 336011022STom.Erickson@Sun.COM int err; 33615378Sck153898 33625378Sck153898 if (spa_version(ds->ds_dir->dd_pool->dp_spa) < 33635378Sck153898 SPA_VERSION_REFRESERVATION) 33645378Sck153898 return (ENOTSUP); 33655378Sck153898 33665378Sck153898 if (dsl_dataset_is_snapshot(ds)) 33675378Sck153898 return (EINVAL); 33685378Sck153898 336911022STom.Erickson@Sun.COM if ((err = dsl_prop_predict_sync(ds->ds_dir, psa)) != 0) 337011022STom.Erickson@Sun.COM return (err); 337111022STom.Erickson@Sun.COM 337211022STom.Erickson@Sun.COM effective_value = psa->psa_effective_value; 337311022STom.Erickson@Sun.COM 33745378Sck153898 /* 33755378Sck153898 * If we are doing the preliminary check in open context, the 33765378Sck153898 * space estimates may be inaccurate. 33775378Sck153898 */ 33785378Sck153898 if (!dmu_tx_is_syncing(tx)) 33795378Sck153898 return (0); 33805378Sck153898 33815378Sck153898 mutex_enter(&ds->ds_lock); 338212296SLin.Ling@Sun.COM if (!DS_UNIQUE_IS_ACCURATE(ds)) 338312296SLin.Ling@Sun.COM dsl_dataset_recalc_head_uniq(ds); 338412296SLin.Ling@Sun.COM unique = ds->ds_phys->ds_unique_bytes; 33855378Sck153898 mutex_exit(&ds->ds_lock); 33865378Sck153898 338711022STom.Erickson@Sun.COM if (MAX(unique, effective_value) > MAX(unique, ds->ds_reserved)) { 338811022STom.Erickson@Sun.COM uint64_t delta = MAX(unique, effective_value) - 33898525SEric.Schrock@Sun.COM MAX(unique, ds->ds_reserved); 33908525SEric.Schrock@Sun.COM 33918525SEric.Schrock@Sun.COM if (delta > dsl_dir_space_available(ds->ds_dir, NULL, 0, TRUE)) 33928525SEric.Schrock@Sun.COM return (ENOSPC); 33938525SEric.Schrock@Sun.COM if (ds->ds_quota > 0 && 339411022STom.Erickson@Sun.COM effective_value > ds->ds_quota) 33958525SEric.Schrock@Sun.COM return (ENOSPC); 33968525SEric.Schrock@Sun.COM } 33975378Sck153898 33985378Sck153898 return (0); 33995378Sck153898 } 34005378Sck153898 34015378Sck153898 static void 340212296SLin.Ling@Sun.COM dsl_dataset_set_reservation_sync(void *arg1, void *arg2, dmu_tx_t *tx) 34035378Sck153898 { 34045378Sck153898 dsl_dataset_t *ds = arg1; 340511022STom.Erickson@Sun.COM dsl_prop_setarg_t *psa = arg2; 340611022STom.Erickson@Sun.COM uint64_t effective_value = psa->psa_effective_value; 34077595SMatthew.Ahrens@Sun.COM uint64_t unique; 34087595SMatthew.Ahrens@Sun.COM int64_t delta; 34097595SMatthew.Ahrens@Sun.COM 341012296SLin.Ling@Sun.COM dsl_prop_set_sync(ds, psa, tx); 341111022STom.Erickson@Sun.COM DSL_PROP_CHECK_PREDICTION(ds->ds_dir, psa); 341211022STom.Erickson@Sun.COM 34137595SMatthew.Ahrens@Sun.COM dmu_buf_will_dirty(ds->ds_dbuf, tx); 34147595SMatthew.Ahrens@Sun.COM 34157595SMatthew.Ahrens@Sun.COM mutex_enter(&ds->ds_dir->dd_lock); 34167595SMatthew.Ahrens@Sun.COM mutex_enter(&ds->ds_lock); 341712296SLin.Ling@Sun.COM ASSERT(DS_UNIQUE_IS_ACCURATE(ds)); 341812296SLin.Ling@Sun.COM unique = ds->ds_phys->ds_unique_bytes; 341911022STom.Erickson@Sun.COM delta = MAX(0, (int64_t)(effective_value - unique)) - 34207595SMatthew.Ahrens@Sun.COM MAX(0, (int64_t)(ds->ds_reserved - unique)); 342111022STom.Erickson@Sun.COM ds->ds_reserved = effective_value; 34227595SMatthew.Ahrens@Sun.COM mutex_exit(&ds->ds_lock); 34237595SMatthew.Ahrens@Sun.COM 34247595SMatthew.Ahrens@Sun.COM dsl_dir_diduse_space(ds->ds_dir, DD_USED_REFRSRV, delta, 0, 0, tx); 34257595SMatthew.Ahrens@Sun.COM mutex_exit(&ds->ds_dir->dd_lock); 34267595SMatthew.Ahrens@Sun.COM 342712296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_REFRESERV, 342812296SLin.Ling@Sun.COM ds->ds_dir->dd_pool->dp_spa, tx, "%lld dataset = %llu", 342911022STom.Erickson@Sun.COM (longlong_t)effective_value, ds->ds_object); 34305378Sck153898 } 34315378Sck153898 34325378Sck153898 int 343311022STom.Erickson@Sun.COM dsl_dataset_set_reservation(const char *dsname, zprop_source_t source, 343411022STom.Erickson@Sun.COM uint64_t reservation) 34355378Sck153898 { 34365378Sck153898 dsl_dataset_t *ds; 343711022STom.Erickson@Sun.COM dsl_prop_setarg_t psa; 34385378Sck153898 int err; 34395378Sck153898 344011022STom.Erickson@Sun.COM dsl_prop_setarg_init_uint64(&psa, "refreservation", source, 344111022STom.Erickson@Sun.COM &reservation); 344211022STom.Erickson@Sun.COM 34436689Smaybee err = dsl_dataset_hold(dsname, FTAG, &ds); 34445378Sck153898 if (err) 34455378Sck153898 return (err); 34465378Sck153898 34475378Sck153898 err = dsl_sync_task_do(ds->ds_dir->dd_pool, 34485378Sck153898 dsl_dataset_set_reservation_check, 344911022STom.Erickson@Sun.COM dsl_dataset_set_reservation_sync, ds, &psa, 0); 345011022STom.Erickson@Sun.COM 34516689Smaybee dsl_dataset_rele(ds, FTAG); 34525378Sck153898 return (err); 34535378Sck153898 } 345410242Schris.kirby@sun.com 345510342Schris.kirby@sun.com struct dsl_ds_holdarg { 345610342Schris.kirby@sun.com dsl_sync_task_group_t *dstg; 345710342Schris.kirby@sun.com char *htag; 345810342Schris.kirby@sun.com char *snapname; 345910342Schris.kirby@sun.com boolean_t recursive; 346010342Schris.kirby@sun.com boolean_t gotone; 346110342Schris.kirby@sun.com boolean_t temphold; 346210342Schris.kirby@sun.com char failed[MAXPATHLEN]; 346310342Schris.kirby@sun.com }; 346410342Schris.kirby@sun.com 346512527SChris.Kirby@oracle.com typedef struct zfs_hold_cleanup_arg { 346612786SChris.Kirby@oracle.com dsl_pool_t *dp; 346712786SChris.Kirby@oracle.com uint64_t dsobj; 346812527SChris.Kirby@oracle.com char htag[MAXNAMELEN]; 346912527SChris.Kirby@oracle.com } zfs_hold_cleanup_arg_t; 347012527SChris.Kirby@oracle.com 347112527SChris.Kirby@oracle.com static void 347212527SChris.Kirby@oracle.com dsl_dataset_user_release_onexit(void *arg) 347312527SChris.Kirby@oracle.com { 347412527SChris.Kirby@oracle.com zfs_hold_cleanup_arg_t *ca = arg; 347512527SChris.Kirby@oracle.com 347612786SChris.Kirby@oracle.com (void) dsl_dataset_user_release_tmp(ca->dp, ca->dsobj, ca->htag, 347712786SChris.Kirby@oracle.com B_TRUE); 347812527SChris.Kirby@oracle.com kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t)); 347912527SChris.Kirby@oracle.com } 348012527SChris.Kirby@oracle.com 348112786SChris.Kirby@oracle.com void 348212786SChris.Kirby@oracle.com dsl_register_onexit_hold_cleanup(dsl_dataset_t *ds, const char *htag, 348312786SChris.Kirby@oracle.com minor_t minor) 348412786SChris.Kirby@oracle.com { 348512786SChris.Kirby@oracle.com zfs_hold_cleanup_arg_t *ca; 348612786SChris.Kirby@oracle.com 348712786SChris.Kirby@oracle.com ca = kmem_alloc(sizeof (zfs_hold_cleanup_arg_t), KM_SLEEP); 348812786SChris.Kirby@oracle.com ca->dp = ds->ds_dir->dd_pool; 348912786SChris.Kirby@oracle.com ca->dsobj = ds->ds_object; 349012786SChris.Kirby@oracle.com (void) strlcpy(ca->htag, htag, sizeof (ca->htag)); 349112786SChris.Kirby@oracle.com VERIFY3U(0, ==, zfs_onexit_add_cb(minor, 349212786SChris.Kirby@oracle.com dsl_dataset_user_release_onexit, ca, NULL)); 349312786SChris.Kirby@oracle.com } 349412786SChris.Kirby@oracle.com 349510951SChris.Kirby@sun.com /* 349610951SChris.Kirby@sun.com * The max length of a temporary tag prefix is the number of hex digits 349710951SChris.Kirby@sun.com * required to express UINT64_MAX plus one for the hyphen. 349810951SChris.Kirby@sun.com */ 349910951SChris.Kirby@sun.com #define MAX_TAG_PREFIX_LEN 17 350010951SChris.Kirby@sun.com 350110242Schris.kirby@sun.com static int 350210242Schris.kirby@sun.com dsl_dataset_user_hold_check(void *arg1, void *arg2, dmu_tx_t *tx) 350310242Schris.kirby@sun.com { 350410242Schris.kirby@sun.com dsl_dataset_t *ds = arg1; 350510342Schris.kirby@sun.com struct dsl_ds_holdarg *ha = arg2; 350610342Schris.kirby@sun.com char *htag = ha->htag; 350710242Schris.kirby@sun.com objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 350810242Schris.kirby@sun.com int error = 0; 350910242Schris.kirby@sun.com 351010242Schris.kirby@sun.com if (spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_USERREFS) 351110242Schris.kirby@sun.com return (ENOTSUP); 351210242Schris.kirby@sun.com 351310242Schris.kirby@sun.com if (!dsl_dataset_is_snapshot(ds)) 351410242Schris.kirby@sun.com return (EINVAL); 351510242Schris.kirby@sun.com 351610242Schris.kirby@sun.com /* tags must be unique */ 351710242Schris.kirby@sun.com mutex_enter(&ds->ds_lock); 351810242Schris.kirby@sun.com if (ds->ds_phys->ds_userrefs_obj) { 351910242Schris.kirby@sun.com error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj, htag, 352010242Schris.kirby@sun.com 8, 1, tx); 352110242Schris.kirby@sun.com if (error == 0) 352210242Schris.kirby@sun.com error = EEXIST; 352310242Schris.kirby@sun.com else if (error == ENOENT) 352410242Schris.kirby@sun.com error = 0; 352510242Schris.kirby@sun.com } 352610242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 352710242Schris.kirby@sun.com 352810951SChris.Kirby@sun.com if (error == 0 && ha->temphold && 352910951SChris.Kirby@sun.com strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN) 353010951SChris.Kirby@sun.com error = E2BIG; 353110951SChris.Kirby@sun.com 353210242Schris.kirby@sun.com return (error); 353310242Schris.kirby@sun.com } 353410242Schris.kirby@sun.com 353510242Schris.kirby@sun.com static void 353612296SLin.Ling@Sun.COM dsl_dataset_user_hold_sync(void *arg1, void *arg2, dmu_tx_t *tx) 353710242Schris.kirby@sun.com { 353810242Schris.kirby@sun.com dsl_dataset_t *ds = arg1; 353910342Schris.kirby@sun.com struct dsl_ds_holdarg *ha = arg2; 354010342Schris.kirby@sun.com char *htag = ha->htag; 354110342Schris.kirby@sun.com dsl_pool_t *dp = ds->ds_dir->dd_pool; 354210342Schris.kirby@sun.com objset_t *mos = dp->dp_meta_objset; 354310951SChris.Kirby@sun.com uint64_t now = gethrestime_sec(); 354410242Schris.kirby@sun.com uint64_t zapobj; 354510242Schris.kirby@sun.com 354610242Schris.kirby@sun.com mutex_enter(&ds->ds_lock); 354710242Schris.kirby@sun.com if (ds->ds_phys->ds_userrefs_obj == 0) { 354810242Schris.kirby@sun.com /* 354910242Schris.kirby@sun.com * This is the first user hold for this dataset. Create 355010242Schris.kirby@sun.com * the userrefs zap object. 355110242Schris.kirby@sun.com */ 355210242Schris.kirby@sun.com dmu_buf_will_dirty(ds->ds_dbuf, tx); 355310242Schris.kirby@sun.com zapobj = ds->ds_phys->ds_userrefs_obj = 355410242Schris.kirby@sun.com zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); 355510242Schris.kirby@sun.com } else { 355610242Schris.kirby@sun.com zapobj = ds->ds_phys->ds_userrefs_obj; 355710242Schris.kirby@sun.com } 355810242Schris.kirby@sun.com ds->ds_userrefs++; 355910242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 356010242Schris.kirby@sun.com 356110242Schris.kirby@sun.com VERIFY(0 == zap_add(mos, zapobj, htag, 8, 1, &now, tx)); 356210242Schris.kirby@sun.com 356310342Schris.kirby@sun.com if (ha->temphold) { 356410342Schris.kirby@sun.com VERIFY(0 == dsl_pool_user_hold(dp, ds->ds_object, 356510342Schris.kirby@sun.com htag, &now, tx)); 356610342Schris.kirby@sun.com } 356710342Schris.kirby@sun.com 356812296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_USER_HOLD, 356912296SLin.Ling@Sun.COM dp->dp_spa, tx, "<%s> temp = %d dataset = %llu", htag, 357010342Schris.kirby@sun.com (int)ha->temphold, ds->ds_object); 357110242Schris.kirby@sun.com } 357210242Schris.kirby@sun.com 357310242Schris.kirby@sun.com static int 357411209SMatthew.Ahrens@Sun.COM dsl_dataset_user_hold_one(const char *dsname, void *arg) 357510242Schris.kirby@sun.com { 357610242Schris.kirby@sun.com struct dsl_ds_holdarg *ha = arg; 357710242Schris.kirby@sun.com dsl_dataset_t *ds; 357810242Schris.kirby@sun.com int error; 357910242Schris.kirby@sun.com char *name; 358010242Schris.kirby@sun.com 358110242Schris.kirby@sun.com /* alloc a buffer to hold dsname@snapname plus terminating NULL */ 358210272SMatthew.Ahrens@Sun.COM name = kmem_asprintf("%s@%s", dsname, ha->snapname); 358310242Schris.kirby@sun.com error = dsl_dataset_hold(name, ha->dstg, &ds); 358410272SMatthew.Ahrens@Sun.COM strfree(name); 358510242Schris.kirby@sun.com if (error == 0) { 358610268Schris.kirby@sun.com ha->gotone = B_TRUE; 358710242Schris.kirby@sun.com dsl_sync_task_create(ha->dstg, dsl_dataset_user_hold_check, 358810342Schris.kirby@sun.com dsl_dataset_user_hold_sync, ds, ha, 0); 358910242Schris.kirby@sun.com } else if (error == ENOENT && ha->recursive) { 359010242Schris.kirby@sun.com error = 0; 359110242Schris.kirby@sun.com } else { 359211209SMatthew.Ahrens@Sun.COM (void) strlcpy(ha->failed, dsname, sizeof (ha->failed)); 359310242Schris.kirby@sun.com } 359410242Schris.kirby@sun.com return (error); 359510242Schris.kirby@sun.com } 359610242Schris.kirby@sun.com 359710242Schris.kirby@sun.com int 359812786SChris.Kirby@oracle.com dsl_dataset_user_hold_for_send(dsl_dataset_t *ds, char *htag, 359912786SChris.Kirby@oracle.com boolean_t temphold) 360012786SChris.Kirby@oracle.com { 360112786SChris.Kirby@oracle.com struct dsl_ds_holdarg *ha; 360212786SChris.Kirby@oracle.com int error; 360312786SChris.Kirby@oracle.com 360412786SChris.Kirby@oracle.com ha = kmem_zalloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP); 360512786SChris.Kirby@oracle.com ha->htag = htag; 360612786SChris.Kirby@oracle.com ha->temphold = temphold; 360712786SChris.Kirby@oracle.com error = dsl_sync_task_do(ds->ds_dir->dd_pool, 360812786SChris.Kirby@oracle.com dsl_dataset_user_hold_check, dsl_dataset_user_hold_sync, 360912786SChris.Kirby@oracle.com ds, ha, 0); 361012786SChris.Kirby@oracle.com kmem_free(ha, sizeof (struct dsl_ds_holdarg)); 361112786SChris.Kirby@oracle.com 361212786SChris.Kirby@oracle.com return (error); 361312786SChris.Kirby@oracle.com } 361412786SChris.Kirby@oracle.com 361512786SChris.Kirby@oracle.com int 361610242Schris.kirby@sun.com dsl_dataset_user_hold(char *dsname, char *snapname, char *htag, 361712527SChris.Kirby@oracle.com boolean_t recursive, boolean_t temphold, int cleanup_fd) 361810242Schris.kirby@sun.com { 361910242Schris.kirby@sun.com struct dsl_ds_holdarg *ha; 362010242Schris.kirby@sun.com dsl_sync_task_t *dst; 362110242Schris.kirby@sun.com spa_t *spa; 362210242Schris.kirby@sun.com int error; 362312786SChris.Kirby@oracle.com minor_t minor = 0; 362412786SChris.Kirby@oracle.com 362512786SChris.Kirby@oracle.com if (cleanup_fd != -1) { 362612786SChris.Kirby@oracle.com /* Currently we only support cleanup-on-exit of tempholds. */ 362712786SChris.Kirby@oracle.com if (!temphold) 362812786SChris.Kirby@oracle.com return (EINVAL); 362912786SChris.Kirby@oracle.com error = zfs_onexit_fd_hold(cleanup_fd, &minor); 363012786SChris.Kirby@oracle.com if (error) 363112786SChris.Kirby@oracle.com return (error); 363212786SChris.Kirby@oracle.com } 363310242Schris.kirby@sun.com 363410242Schris.kirby@sun.com ha = kmem_zalloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP); 363510242Schris.kirby@sun.com 363610242Schris.kirby@sun.com (void) strlcpy(ha->failed, dsname, sizeof (ha->failed)); 363710242Schris.kirby@sun.com 363810242Schris.kirby@sun.com error = spa_open(dsname, &spa, FTAG); 363910242Schris.kirby@sun.com if (error) { 364010242Schris.kirby@sun.com kmem_free(ha, sizeof (struct dsl_ds_holdarg)); 364112786SChris.Kirby@oracle.com if (cleanup_fd != -1) 364212786SChris.Kirby@oracle.com zfs_onexit_fd_rele(cleanup_fd); 364310242Schris.kirby@sun.com return (error); 364410242Schris.kirby@sun.com } 364510242Schris.kirby@sun.com 364610242Schris.kirby@sun.com ha->dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); 364710242Schris.kirby@sun.com ha->htag = htag; 364810242Schris.kirby@sun.com ha->snapname = snapname; 364910242Schris.kirby@sun.com ha->recursive = recursive; 365010342Schris.kirby@sun.com ha->temphold = temphold; 365112527SChris.Kirby@oracle.com 365210242Schris.kirby@sun.com if (recursive) { 365310242Schris.kirby@sun.com error = dmu_objset_find(dsname, dsl_dataset_user_hold_one, 365410242Schris.kirby@sun.com ha, DS_FIND_CHILDREN); 365510242Schris.kirby@sun.com } else { 365610242Schris.kirby@sun.com error = dsl_dataset_user_hold_one(dsname, ha); 365710242Schris.kirby@sun.com } 365810242Schris.kirby@sun.com if (error == 0) 365910242Schris.kirby@sun.com error = dsl_sync_task_group_wait(ha->dstg); 366010242Schris.kirby@sun.com 366110242Schris.kirby@sun.com for (dst = list_head(&ha->dstg->dstg_tasks); dst; 366210242Schris.kirby@sun.com dst = list_next(&ha->dstg->dstg_tasks, dst)) { 366310242Schris.kirby@sun.com dsl_dataset_t *ds = dst->dst_arg1; 366410242Schris.kirby@sun.com 366510242Schris.kirby@sun.com if (dst->dst_err) { 366610242Schris.kirby@sun.com dsl_dataset_name(ds, ha->failed); 366710242Schris.kirby@sun.com *strchr(ha->failed, '@') = '\0'; 366812786SChris.Kirby@oracle.com } else if (error == 0 && minor != 0 && temphold) { 366912786SChris.Kirby@oracle.com /* 367012786SChris.Kirby@oracle.com * If this hold is to be released upon process exit, 367112786SChris.Kirby@oracle.com * register that action now. 367212786SChris.Kirby@oracle.com */ 367312786SChris.Kirby@oracle.com dsl_register_onexit_hold_cleanup(ds, htag, minor); 367410242Schris.kirby@sun.com } 367510242Schris.kirby@sun.com dsl_dataset_rele(ds, ha->dstg); 367610242Schris.kirby@sun.com } 367710242Schris.kirby@sun.com 367810268Schris.kirby@sun.com if (error == 0 && recursive && !ha->gotone) 367910268Schris.kirby@sun.com error = ENOENT; 368010268Schris.kirby@sun.com 368110242Schris.kirby@sun.com if (error) 368211209SMatthew.Ahrens@Sun.COM (void) strlcpy(dsname, ha->failed, sizeof (ha->failed)); 368310242Schris.kirby@sun.com 368410242Schris.kirby@sun.com dsl_sync_task_group_destroy(ha->dstg); 368512527SChris.Kirby@oracle.com 368610242Schris.kirby@sun.com kmem_free(ha, sizeof (struct dsl_ds_holdarg)); 368710242Schris.kirby@sun.com spa_close(spa, FTAG); 368812786SChris.Kirby@oracle.com if (cleanup_fd != -1) 368912786SChris.Kirby@oracle.com zfs_onexit_fd_rele(cleanup_fd); 369010242Schris.kirby@sun.com return (error); 369110242Schris.kirby@sun.com } 369210242Schris.kirby@sun.com 369310242Schris.kirby@sun.com struct dsl_ds_releasearg { 369410242Schris.kirby@sun.com dsl_dataset_t *ds; 369510242Schris.kirby@sun.com const char *htag; 369610242Schris.kirby@sun.com boolean_t own; /* do we own or just hold ds? */ 369710242Schris.kirby@sun.com }; 369810242Schris.kirby@sun.com 369910242Schris.kirby@sun.com static int 370010242Schris.kirby@sun.com dsl_dataset_release_might_destroy(dsl_dataset_t *ds, const char *htag, 370110242Schris.kirby@sun.com boolean_t *might_destroy) 370210242Schris.kirby@sun.com { 370310242Schris.kirby@sun.com objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 370410242Schris.kirby@sun.com uint64_t zapobj; 370510242Schris.kirby@sun.com uint64_t tmp; 370610242Schris.kirby@sun.com int error; 370710242Schris.kirby@sun.com 370810242Schris.kirby@sun.com *might_destroy = B_FALSE; 370910242Schris.kirby@sun.com 371010242Schris.kirby@sun.com mutex_enter(&ds->ds_lock); 371110242Schris.kirby@sun.com zapobj = ds->ds_phys->ds_userrefs_obj; 371210242Schris.kirby@sun.com if (zapobj == 0) { 371310242Schris.kirby@sun.com /* The tag can't possibly exist */ 371410242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 371510242Schris.kirby@sun.com return (ESRCH); 371610242Schris.kirby@sun.com } 371710242Schris.kirby@sun.com 371810242Schris.kirby@sun.com /* Make sure the tag exists */ 371910242Schris.kirby@sun.com error = zap_lookup(mos, zapobj, htag, 8, 1, &tmp); 372010242Schris.kirby@sun.com if (error) { 372110242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 372210242Schris.kirby@sun.com if (error == ENOENT) 372310242Schris.kirby@sun.com error = ESRCH; 372410242Schris.kirby@sun.com return (error); 372510242Schris.kirby@sun.com } 372610242Schris.kirby@sun.com 372710242Schris.kirby@sun.com if (ds->ds_userrefs == 1 && ds->ds_phys->ds_num_children == 1 && 372810242Schris.kirby@sun.com DS_IS_DEFER_DESTROY(ds)) 372910242Schris.kirby@sun.com *might_destroy = B_TRUE; 373010242Schris.kirby@sun.com 373110242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 373210242Schris.kirby@sun.com return (0); 373310242Schris.kirby@sun.com } 373410242Schris.kirby@sun.com 373510242Schris.kirby@sun.com static int 373610242Schris.kirby@sun.com dsl_dataset_user_release_check(void *arg1, void *tag, dmu_tx_t *tx) 373710242Schris.kirby@sun.com { 373810242Schris.kirby@sun.com struct dsl_ds_releasearg *ra = arg1; 373910242Schris.kirby@sun.com dsl_dataset_t *ds = ra->ds; 374010242Schris.kirby@sun.com boolean_t might_destroy; 374110242Schris.kirby@sun.com int error; 374210242Schris.kirby@sun.com 374310242Schris.kirby@sun.com if (spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_USERREFS) 374410242Schris.kirby@sun.com return (ENOTSUP); 374510242Schris.kirby@sun.com 374610242Schris.kirby@sun.com error = dsl_dataset_release_might_destroy(ds, ra->htag, &might_destroy); 374710242Schris.kirby@sun.com if (error) 374810242Schris.kirby@sun.com return (error); 374910242Schris.kirby@sun.com 375010242Schris.kirby@sun.com if (might_destroy) { 375110242Schris.kirby@sun.com struct dsl_ds_destroyarg dsda = {0}; 375210242Schris.kirby@sun.com 375310242Schris.kirby@sun.com if (dmu_tx_is_syncing(tx)) { 375410242Schris.kirby@sun.com /* 375510242Schris.kirby@sun.com * If we're not prepared to remove the snapshot, 375610242Schris.kirby@sun.com * we can't allow the release to happen right now. 375710242Schris.kirby@sun.com */ 375810242Schris.kirby@sun.com if (!ra->own) 375910242Schris.kirby@sun.com return (EBUSY); 376010242Schris.kirby@sun.com } 376110242Schris.kirby@sun.com dsda.ds = ds; 376210242Schris.kirby@sun.com dsda.releasing = B_TRUE; 376310242Schris.kirby@sun.com return (dsl_dataset_destroy_check(&dsda, tag, tx)); 376410242Schris.kirby@sun.com } 376510242Schris.kirby@sun.com 376610242Schris.kirby@sun.com return (0); 376710242Schris.kirby@sun.com } 376810242Schris.kirby@sun.com 376910242Schris.kirby@sun.com static void 377012296SLin.Ling@Sun.COM dsl_dataset_user_release_sync(void *arg1, void *tag, dmu_tx_t *tx) 377110242Schris.kirby@sun.com { 377210242Schris.kirby@sun.com struct dsl_ds_releasearg *ra = arg1; 377310242Schris.kirby@sun.com dsl_dataset_t *ds = ra->ds; 377410342Schris.kirby@sun.com dsl_pool_t *dp = ds->ds_dir->dd_pool; 377510342Schris.kirby@sun.com objset_t *mos = dp->dp_meta_objset; 377610242Schris.kirby@sun.com uint64_t zapobj; 377710242Schris.kirby@sun.com uint64_t dsobj = ds->ds_object; 377810242Schris.kirby@sun.com uint64_t refs; 377910342Schris.kirby@sun.com int error; 378010242Schris.kirby@sun.com 378110242Schris.kirby@sun.com mutex_enter(&ds->ds_lock); 378210242Schris.kirby@sun.com ds->ds_userrefs--; 378310242Schris.kirby@sun.com refs = ds->ds_userrefs; 378410242Schris.kirby@sun.com mutex_exit(&ds->ds_lock); 378510342Schris.kirby@sun.com error = dsl_pool_user_release(dp, ds->ds_object, ra->htag, tx); 378610342Schris.kirby@sun.com VERIFY(error == 0 || error == ENOENT); 378710242Schris.kirby@sun.com zapobj = ds->ds_phys->ds_userrefs_obj; 378810242Schris.kirby@sun.com VERIFY(0 == zap_remove(mos, zapobj, ra->htag, tx)); 378910242Schris.kirby@sun.com if (ds->ds_userrefs == 0 && ds->ds_phys->ds_num_children == 1 && 379010242Schris.kirby@sun.com DS_IS_DEFER_DESTROY(ds)) { 379110242Schris.kirby@sun.com struct dsl_ds_destroyarg dsda = {0}; 379210242Schris.kirby@sun.com 379310242Schris.kirby@sun.com ASSERT(ra->own); 379410242Schris.kirby@sun.com dsda.ds = ds; 379510242Schris.kirby@sun.com dsda.releasing = B_TRUE; 379610242Schris.kirby@sun.com /* We already did the destroy_check */ 379712296SLin.Ling@Sun.COM dsl_dataset_destroy_sync(&dsda, tag, tx); 379810242Schris.kirby@sun.com } 379910242Schris.kirby@sun.com 380012296SLin.Ling@Sun.COM spa_history_log_internal(LOG_DS_USER_RELEASE, 380112296SLin.Ling@Sun.COM dp->dp_spa, tx, "<%s> %lld dataset = %llu", 380210242Schris.kirby@sun.com ra->htag, (longlong_t)refs, dsobj); 380310242Schris.kirby@sun.com } 380410242Schris.kirby@sun.com 380510242Schris.kirby@sun.com static int 380611209SMatthew.Ahrens@Sun.COM dsl_dataset_user_release_one(const char *dsname, void *arg) 380710242Schris.kirby@sun.com { 380810242Schris.kirby@sun.com struct dsl_ds_holdarg *ha = arg; 380910242Schris.kirby@sun.com struct dsl_ds_releasearg *ra; 381010242Schris.kirby@sun.com dsl_dataset_t *ds; 381110242Schris.kirby@sun.com int error; 381210242Schris.kirby@sun.com void *dtag = ha->dstg; 381310242Schris.kirby@sun.com char *name; 381410242Schris.kirby@sun.com boolean_t own = B_FALSE; 381510242Schris.kirby@sun.com boolean_t might_destroy; 381610242Schris.kirby@sun.com 381710242Schris.kirby@sun.com /* alloc a buffer to hold dsname@snapname, plus the terminating NULL */ 381810272SMatthew.Ahrens@Sun.COM name = kmem_asprintf("%s@%s", dsname, ha->snapname); 381910242Schris.kirby@sun.com error = dsl_dataset_hold(name, dtag, &ds); 382010272SMatthew.Ahrens@Sun.COM strfree(name); 382110242Schris.kirby@sun.com if (error == ENOENT && ha->recursive) 382210242Schris.kirby@sun.com return (0); 382311209SMatthew.Ahrens@Sun.COM (void) strlcpy(ha->failed, dsname, sizeof (ha->failed)); 382410242Schris.kirby@sun.com if (error) 382510242Schris.kirby@sun.com return (error); 382610242Schris.kirby@sun.com 382710268Schris.kirby@sun.com ha->gotone = B_TRUE; 382810268Schris.kirby@sun.com 382910242Schris.kirby@sun.com ASSERT(dsl_dataset_is_snapshot(ds)); 383010242Schris.kirby@sun.com 383110242Schris.kirby@sun.com error = dsl_dataset_release_might_destroy(ds, ha->htag, &might_destroy); 383210242Schris.kirby@sun.com if (error) { 383310242Schris.kirby@sun.com dsl_dataset_rele(ds, dtag); 383410242Schris.kirby@sun.com return (error); 383510242Schris.kirby@sun.com } 383610242Schris.kirby@sun.com 383710242Schris.kirby@sun.com if (might_destroy) { 383810242Schris.kirby@sun.com #ifdef _KERNEL 383912115SChris.Kirby@oracle.com name = kmem_asprintf("%s@%s", dsname, ha->snapname); 384010242Schris.kirby@sun.com error = zfs_unmount_snap(name, NULL); 384112115SChris.Kirby@oracle.com strfree(name); 384210242Schris.kirby@sun.com if (error) { 384310242Schris.kirby@sun.com dsl_dataset_rele(ds, dtag); 384410242Schris.kirby@sun.com return (error); 384510242Schris.kirby@sun.com } 384610242Schris.kirby@sun.com #endif 384710298SMatthew.Ahrens@Sun.COM if (!dsl_dataset_tryown(ds, B_TRUE, dtag)) { 384810242Schris.kirby@sun.com dsl_dataset_rele(ds, dtag); 384910242Schris.kirby@sun.com return (EBUSY); 385010242Schris.kirby@sun.com } else { 385110242Schris.kirby@sun.com own = B_TRUE; 385210242Schris.kirby@sun.com dsl_dataset_make_exclusive(ds, dtag); 385310242Schris.kirby@sun.com } 385410242Schris.kirby@sun.com } 385510242Schris.kirby@sun.com 385610242Schris.kirby@sun.com ra = kmem_alloc(sizeof (struct dsl_ds_releasearg), KM_SLEEP); 385710242Schris.kirby@sun.com ra->ds = ds; 385810242Schris.kirby@sun.com ra->htag = ha->htag; 385910242Schris.kirby@sun.com ra->own = own; 386010242Schris.kirby@sun.com dsl_sync_task_create(ha->dstg, dsl_dataset_user_release_check, 386110242Schris.kirby@sun.com dsl_dataset_user_release_sync, ra, dtag, 0); 386210242Schris.kirby@sun.com 386310242Schris.kirby@sun.com return (0); 386410242Schris.kirby@sun.com } 386510242Schris.kirby@sun.com 386610242Schris.kirby@sun.com int 386710242Schris.kirby@sun.com dsl_dataset_user_release(char *dsname, char *snapname, char *htag, 386810242Schris.kirby@sun.com boolean_t recursive) 386910242Schris.kirby@sun.com { 387010242Schris.kirby@sun.com struct dsl_ds_holdarg *ha; 387110242Schris.kirby@sun.com dsl_sync_task_t *dst; 387210242Schris.kirby@sun.com spa_t *spa; 387310242Schris.kirby@sun.com int error; 387410242Schris.kirby@sun.com 387511546SChris.Kirby@sun.com top: 387610242Schris.kirby@sun.com ha = kmem_zalloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP); 387710242Schris.kirby@sun.com 387810242Schris.kirby@sun.com (void) strlcpy(ha->failed, dsname, sizeof (ha->failed)); 387910242Schris.kirby@sun.com 388010242Schris.kirby@sun.com error = spa_open(dsname, &spa, FTAG); 388110242Schris.kirby@sun.com if (error) { 388210242Schris.kirby@sun.com kmem_free(ha, sizeof (struct dsl_ds_holdarg)); 388310242Schris.kirby@sun.com return (error); 388410242Schris.kirby@sun.com } 388510242Schris.kirby@sun.com 388610242Schris.kirby@sun.com ha->dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); 388710242Schris.kirby@sun.com ha->htag = htag; 388810242Schris.kirby@sun.com ha->snapname = snapname; 388910242Schris.kirby@sun.com ha->recursive = recursive; 389010242Schris.kirby@sun.com if (recursive) { 389110242Schris.kirby@sun.com error = dmu_objset_find(dsname, dsl_dataset_user_release_one, 389210242Schris.kirby@sun.com ha, DS_FIND_CHILDREN); 389310242Schris.kirby@sun.com } else { 389410242Schris.kirby@sun.com error = dsl_dataset_user_release_one(dsname, ha); 389510242Schris.kirby@sun.com } 389610242Schris.kirby@sun.com if (error == 0) 389710242Schris.kirby@sun.com error = dsl_sync_task_group_wait(ha->dstg); 389810242Schris.kirby@sun.com 389910242Schris.kirby@sun.com for (dst = list_head(&ha->dstg->dstg_tasks); dst; 390010242Schris.kirby@sun.com dst = list_next(&ha->dstg->dstg_tasks, dst)) { 390110242Schris.kirby@sun.com struct dsl_ds_releasearg *ra = dst->dst_arg1; 390210242Schris.kirby@sun.com dsl_dataset_t *ds = ra->ds; 390310242Schris.kirby@sun.com 390410242Schris.kirby@sun.com if (dst->dst_err) 390510242Schris.kirby@sun.com dsl_dataset_name(ds, ha->failed); 390610242Schris.kirby@sun.com 390710242Schris.kirby@sun.com if (ra->own) 390810242Schris.kirby@sun.com dsl_dataset_disown(ds, ha->dstg); 390910242Schris.kirby@sun.com else 391010242Schris.kirby@sun.com dsl_dataset_rele(ds, ha->dstg); 391110242Schris.kirby@sun.com 391210242Schris.kirby@sun.com kmem_free(ra, sizeof (struct dsl_ds_releasearg)); 391310242Schris.kirby@sun.com } 391410242Schris.kirby@sun.com 391510268Schris.kirby@sun.com if (error == 0 && recursive && !ha->gotone) 391610268Schris.kirby@sun.com error = ENOENT; 391710268Schris.kirby@sun.com 391811546SChris.Kirby@sun.com if (error && error != EBUSY) 391911209SMatthew.Ahrens@Sun.COM (void) strlcpy(dsname, ha->failed, sizeof (ha->failed)); 392010242Schris.kirby@sun.com 392110242Schris.kirby@sun.com dsl_sync_task_group_destroy(ha->dstg); 392210242Schris.kirby@sun.com kmem_free(ha, sizeof (struct dsl_ds_holdarg)); 392310242Schris.kirby@sun.com spa_close(spa, FTAG); 392411546SChris.Kirby@sun.com 392511546SChris.Kirby@sun.com /* 392611546SChris.Kirby@sun.com * We can get EBUSY if we were racing with deferred destroy and 392711546SChris.Kirby@sun.com * dsl_dataset_user_release_check() hadn't done the necessary 392811546SChris.Kirby@sun.com * open context setup. We can also get EBUSY if we're racing 392911546SChris.Kirby@sun.com * with destroy and that thread is the ds_owner. Either way 393011546SChris.Kirby@sun.com * the busy condition should be transient, and we should retry 393111546SChris.Kirby@sun.com * the release operation. 393211546SChris.Kirby@sun.com */ 393311546SChris.Kirby@sun.com if (error == EBUSY) 393411546SChris.Kirby@sun.com goto top; 393511546SChris.Kirby@sun.com 393610242Schris.kirby@sun.com return (error); 393710242Schris.kirby@sun.com } 393810242Schris.kirby@sun.com 393910342Schris.kirby@sun.com /* 394012786SChris.Kirby@oracle.com * Called at spa_load time (with retry == B_FALSE) to release a stale 394112786SChris.Kirby@oracle.com * temporary user hold. Also called by the onexit code (with retry == B_TRUE). 394210342Schris.kirby@sun.com */ 394310342Schris.kirby@sun.com int 394412786SChris.Kirby@oracle.com dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, char *htag, 394512786SChris.Kirby@oracle.com boolean_t retry) 394610342Schris.kirby@sun.com { 394710342Schris.kirby@sun.com dsl_dataset_t *ds; 394810342Schris.kirby@sun.com char *snap; 394910342Schris.kirby@sun.com char *name; 395010342Schris.kirby@sun.com int namelen; 395110342Schris.kirby@sun.com int error; 395210342Schris.kirby@sun.com 395312786SChris.Kirby@oracle.com do { 395412786SChris.Kirby@oracle.com rw_enter(&dp->dp_config_rwlock, RW_READER); 395512786SChris.Kirby@oracle.com error = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); 395612786SChris.Kirby@oracle.com rw_exit(&dp->dp_config_rwlock); 395712786SChris.Kirby@oracle.com if (error) 395812786SChris.Kirby@oracle.com return (error); 395912786SChris.Kirby@oracle.com namelen = dsl_dataset_namelen(ds)+1; 396012786SChris.Kirby@oracle.com name = kmem_alloc(namelen, KM_SLEEP); 396112786SChris.Kirby@oracle.com dsl_dataset_name(ds, name); 396212786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG); 396312786SChris.Kirby@oracle.com 396412786SChris.Kirby@oracle.com snap = strchr(name, '@'); 396512786SChris.Kirby@oracle.com *snap = '\0'; 396612786SChris.Kirby@oracle.com ++snap; 396712786SChris.Kirby@oracle.com error = dsl_dataset_user_release(name, snap, htag, B_FALSE); 396812786SChris.Kirby@oracle.com kmem_free(name, namelen); 396912786SChris.Kirby@oracle.com 397012786SChris.Kirby@oracle.com /* 397112786SChris.Kirby@oracle.com * The object can't have been destroyed because we have a hold, 397212786SChris.Kirby@oracle.com * but it might have been renamed, resulting in ENOENT. Retry 397312786SChris.Kirby@oracle.com * if we've been requested to do so. 397412786SChris.Kirby@oracle.com * 397512786SChris.Kirby@oracle.com * It would be nice if we could use the dsobj all the way 397612786SChris.Kirby@oracle.com * through and avoid ENOENT entirely. But we might need to 397712786SChris.Kirby@oracle.com * unmount the snapshot, and there's currently no way to lookup 397812786SChris.Kirby@oracle.com * a vfsp using a ZFS object id. 397912786SChris.Kirby@oracle.com */ 398012786SChris.Kirby@oracle.com } while ((error == ENOENT) && retry); 398112786SChris.Kirby@oracle.com 398212786SChris.Kirby@oracle.com return (error); 398310342Schris.kirby@sun.com } 398410342Schris.kirby@sun.com 398510242Schris.kirby@sun.com int 398610242Schris.kirby@sun.com dsl_dataset_get_holds(const char *dsname, nvlist_t **nvp) 398710242Schris.kirby@sun.com { 398810242Schris.kirby@sun.com dsl_dataset_t *ds; 398910242Schris.kirby@sun.com int err; 399010242Schris.kirby@sun.com 399110242Schris.kirby@sun.com err = dsl_dataset_hold(dsname, FTAG, &ds); 399210242Schris.kirby@sun.com if (err) 399310242Schris.kirby@sun.com return (err); 399410242Schris.kirby@sun.com 399510242Schris.kirby@sun.com VERIFY(0 == nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP)); 399610242Schris.kirby@sun.com if (ds->ds_phys->ds_userrefs_obj != 0) { 399710242Schris.kirby@sun.com zap_attribute_t *za; 399810242Schris.kirby@sun.com zap_cursor_t zc; 399910242Schris.kirby@sun.com 400010242Schris.kirby@sun.com za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); 400110242Schris.kirby@sun.com for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, 400210242Schris.kirby@sun.com ds->ds_phys->ds_userrefs_obj); 400310242Schris.kirby@sun.com zap_cursor_retrieve(&zc, za) == 0; 400410242Schris.kirby@sun.com zap_cursor_advance(&zc)) { 400510242Schris.kirby@sun.com VERIFY(0 == nvlist_add_uint64(*nvp, za->za_name, 400610242Schris.kirby@sun.com za->za_first_integer)); 400710242Schris.kirby@sun.com } 400810242Schris.kirby@sun.com zap_cursor_fini(&zc); 400910242Schris.kirby@sun.com kmem_free(za, sizeof (zap_attribute_t)); 401010242Schris.kirby@sun.com } 401110242Schris.kirby@sun.com dsl_dataset_rele(ds, FTAG); 401210242Schris.kirby@sun.com return (0); 401310242Schris.kirby@sun.com } 401410298SMatthew.Ahrens@Sun.COM 401510298SMatthew.Ahrens@Sun.COM /* 401610298SMatthew.Ahrens@Sun.COM * Note, this fuction is used as the callback for dmu_objset_find(). We 401710298SMatthew.Ahrens@Sun.COM * always return 0 so that we will continue to find and process 401810298SMatthew.Ahrens@Sun.COM * inconsistent datasets, even if we encounter an error trying to 401910298SMatthew.Ahrens@Sun.COM * process one of them. 402010298SMatthew.Ahrens@Sun.COM */ 402110298SMatthew.Ahrens@Sun.COM /* ARGSUSED */ 402210298SMatthew.Ahrens@Sun.COM int 402311209SMatthew.Ahrens@Sun.COM dsl_destroy_inconsistent(const char *dsname, void *arg) 402410298SMatthew.Ahrens@Sun.COM { 402510298SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds; 402610298SMatthew.Ahrens@Sun.COM 402710298SMatthew.Ahrens@Sun.COM if (dsl_dataset_own(dsname, B_TRUE, FTAG, &ds) == 0) { 402810298SMatthew.Ahrens@Sun.COM if (DS_IS_INCONSISTENT(ds)) 402910298SMatthew.Ahrens@Sun.COM (void) dsl_dataset_destroy(ds, FTAG, B_FALSE); 403010298SMatthew.Ahrens@Sun.COM else 403110298SMatthew.Ahrens@Sun.COM dsl_dataset_disown(ds, FTAG); 403210298SMatthew.Ahrens@Sun.COM } 403310298SMatthew.Ahrens@Sun.COM return (0); 403410298SMatthew.Ahrens@Sun.COM } 4035