1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5ea8dc4b6Seschrock * Common Development and Distribution License (the "License"). 6ea8dc4b6Seschrock * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21fa9e4066Sahrens /* 2206e0070dSMark Shellenbaum * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23bf26014cSMatthew Ahrens * Copyright (c) 2013, 2017 by Delphix. All rights reserved. 24e77d42eaSMatthew Ahrens * Copyright 2014 HybridCluster. All rights reserved. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #include <sys/dmu.h> 28fa9e4066Sahrens #include <sys/dmu_objset.h> 29fa9e4066Sahrens #include <sys/dmu_tx.h> 30fa9e4066Sahrens #include <sys/dnode.h> 312acef22dSMatthew Ahrens #include <sys/zap.h> 322acef22dSMatthew Ahrens #include <sys/zfeature.h> 3354811da5SToomas Soome #include <sys/dsl_dataset.h> 34fa9e4066Sahrens 3554811da5SToomas Soome /* 3654811da5SToomas Soome * Each of the concurrent object allocators will grab 3754811da5SToomas Soome * 2^dmu_object_alloc_chunk_shift dnode slots at a time. The default is to 3854811da5SToomas Soome * grab 128 slots, which is 4 blocks worth. This was experimentally 3954811da5SToomas Soome * determined to be the lowest value that eliminates the measurable effect 4054811da5SToomas Soome * of lock contention from this code path. 4154811da5SToomas Soome */ 4254811da5SToomas Soome int dmu_object_alloc_chunk_shift = 7; 4354811da5SToomas Soome 4454811da5SToomas Soome static uint64_t 4554811da5SToomas Soome dmu_object_alloc_impl(objset_t *os, dmu_object_type_t ot, int blocksize, 4654811da5SToomas Soome int indirect_blockshift, dmu_object_type_t bonustype, int bonuslen, 4754811da5SToomas Soome int dnodesize, dmu_tx_t *tx) 48fa9e4066Sahrens { 49fa9e4066Sahrens uint64_t object; 50af346df5SNed Bass uint64_t L1_dnode_count = DNODES_PER_BLOCK << 51744947dcSTom Erickson (DMU_META_DNODE(os)->dn_indblkshift - SPA_BLKPTRSHIFT); 52ea8dc4b6Seschrock dnode_t *dn = NULL; 5354811da5SToomas Soome int dn_slots = dnodesize >> DNODE_SHIFT; 5454811da5SToomas Soome boolean_t restarted = B_FALSE; 5554811da5SToomas Soome uint64_t *cpuobj = &os->os_obj_next_percpu[CPU_SEQID % 5654811da5SToomas Soome os->os_obj_next_percpu_len]; 5754811da5SToomas Soome int dnodes_per_chunk = 1 << dmu_object_alloc_chunk_shift; 5854811da5SToomas Soome int error; 5954811da5SToomas Soome 6054811da5SToomas Soome if (dn_slots == 0) { 6154811da5SToomas Soome dn_slots = DNODE_MIN_SLOTS; 6254811da5SToomas Soome } else { 6354811da5SToomas Soome ASSERT3S(dn_slots, >=, DNODE_MIN_SLOTS); 6454811da5SToomas Soome ASSERT3S(dn_slots, <=, DNODE_MAX_SLOTS); 6554811da5SToomas Soome } 6654811da5SToomas Soome 6754811da5SToomas Soome /* 6854811da5SToomas Soome * The "chunk" of dnodes that is assigned to a CPU-specific 6954811da5SToomas Soome * allocator needs to be at least one block's worth, to avoid 7054811da5SToomas Soome * lock contention on the dbuf. It can be at most one L1 block's 7154811da5SToomas Soome * worth, so that the "rescan after polishing off a L1's worth" 7254811da5SToomas Soome * logic below will be sure to kick in. 7354811da5SToomas Soome */ 7454811da5SToomas Soome if (dnodes_per_chunk < DNODES_PER_BLOCK) 7554811da5SToomas Soome dnodes_per_chunk = DNODES_PER_BLOCK; 7654811da5SToomas Soome if (dnodes_per_chunk > L1_dnode_count) 7754811da5SToomas Soome dnodes_per_chunk = L1_dnode_count; 7854811da5SToomas Soome 7954811da5SToomas Soome object = *cpuobj; 80fa9e4066Sahrens 81fa9e4066Sahrens for (;;) { 82fa9e4066Sahrens /* 8354811da5SToomas Soome * If we finished a chunk of dnodes, get a new one from 8454811da5SToomas Soome * the global allocator. 85fa9e4066Sahrens */ 8654811da5SToomas Soome if ((P2PHASE(object, dnodes_per_chunk) == 0) || 8754811da5SToomas Soome (P2PHASE(object + dn_slots - 1, dnodes_per_chunk) < 8854811da5SToomas Soome dn_slots)) { 8954811da5SToomas Soome DNODE_STAT_BUMP(dnode_alloc_next_chunk); 9054811da5SToomas Soome mutex_enter(&os->os_obj_lock); 9154811da5SToomas Soome ASSERT0(P2PHASE(os->os_obj_next_chunk, 9254811da5SToomas Soome dnodes_per_chunk)); 9354811da5SToomas Soome object = os->os_obj_next_chunk; 9454811da5SToomas Soome 9554811da5SToomas Soome /* 9654811da5SToomas Soome * Each time we polish off a L1 bp worth of dnodes 9754811da5SToomas Soome * (2^12 objects), move to another L1 bp that's 9854811da5SToomas Soome * still reasonably sparse (at most 1/4 full). Look 9954811da5SToomas Soome * from the beginning at most once per txg. If we 10054811da5SToomas Soome * still can't allocate from that L1 block, search 10154811da5SToomas Soome * for an empty L0 block, which will quickly skip 10254811da5SToomas Soome * to the end of the metadnode if the no nearby L0 10354811da5SToomas Soome * blocks are empty. This fallback avoids a 10454811da5SToomas Soome * pathology where full dnode blocks containing 10554811da5SToomas Soome * large dnodes appear sparse because they have a 10654811da5SToomas Soome * low blk_fill, leading to many failed allocation 10754811da5SToomas Soome * attempts. In the long term a better mechanism to 10854811da5SToomas Soome * search for sparse metadnode regions, such as 10954811da5SToomas Soome * spacemaps, could be implemented. 11054811da5SToomas Soome * 11154811da5SToomas Soome * os_scan_dnodes is set during txg sync if enough 11254811da5SToomas Soome * objects have been freed since the previous 11354811da5SToomas Soome * rescan to justify backfilling again. 11454811da5SToomas Soome * 11554811da5SToomas Soome * Note that dmu_traverse depends on the behavior 11654811da5SToomas Soome * that we use multiple blocks of the dnode object 11754811da5SToomas Soome * before going back to reuse objects. Any change 11854811da5SToomas Soome * to this algorithm should preserve that property 11954811da5SToomas Soome * or find another solution to the issues described 12054811da5SToomas Soome * in traverse_visitbp. 12154811da5SToomas Soome */ 12254811da5SToomas Soome if (P2PHASE(object, L1_dnode_count) == 0) { 12354811da5SToomas Soome uint64_t offset; 12454811da5SToomas Soome uint64_t blkfill; 12554811da5SToomas Soome int minlvl; 12654811da5SToomas Soome if (os->os_rescan_dnodes) { 12754811da5SToomas Soome offset = 0; 12854811da5SToomas Soome os->os_rescan_dnodes = B_FALSE; 12954811da5SToomas Soome } else { 13054811da5SToomas Soome offset = object << DNODE_SHIFT; 13154811da5SToomas Soome } 13254811da5SToomas Soome blkfill = restarted ? 1 : DNODES_PER_BLOCK >> 2; 13354811da5SToomas Soome minlvl = restarted ? 1 : 2; 13454811da5SToomas Soome restarted = B_TRUE; 13554811da5SToomas Soome error = dnode_next_offset(DMU_META_DNODE(os), 13654811da5SToomas Soome DNODE_FIND_HOLE, &offset, minlvl, 13754811da5SToomas Soome blkfill, 0); 13854811da5SToomas Soome if (error == 0) { 13954811da5SToomas Soome object = offset >> DNODE_SHIFT; 14054811da5SToomas Soome } 141af346df5SNed Bass } 14254811da5SToomas Soome /* 14354811da5SToomas Soome * Note: if "restarted", we may find a L0 that 14454811da5SToomas Soome * is not suitably aligned. 14554811da5SToomas Soome */ 14654811da5SToomas Soome os->os_obj_next_chunk = 14754811da5SToomas Soome P2ALIGN(object, dnodes_per_chunk) + 14854811da5SToomas Soome dnodes_per_chunk; 14954811da5SToomas Soome (void) atomic_swap_64(cpuobj, object); 15054811da5SToomas Soome mutex_exit(&os->os_obj_lock); 151fa9e4066Sahrens } 15254811da5SToomas Soome 15354811da5SToomas Soome /* 15454811da5SToomas Soome * The value of (*cpuobj) before adding dn_slots is the object 15554811da5SToomas Soome * ID assigned to us. The value afterwards is the object ID 15654811da5SToomas Soome * assigned to whoever wants to do an allocation next. 15754811da5SToomas Soome */ 15854811da5SToomas Soome object = atomic_add_64_nv(cpuobj, dn_slots) - dn_slots; 159fa9e4066Sahrens 160ea8dc4b6Seschrock /* 161ea8dc4b6Seschrock * XXX We should check for an i/o error here and return 162ea8dc4b6Seschrock * up to our caller. Actually we should pre-read it in 163ea8dc4b6Seschrock * dmu_tx_assign(), but there is currently no mechanism 164ea8dc4b6Seschrock * to do so. 165ea8dc4b6Seschrock */ 16654811da5SToomas Soome error = dnode_hold_impl(os, object, DNODE_MUST_BE_FREE, 16754811da5SToomas Soome dn_slots, FTAG, &dn); 16854811da5SToomas Soome if (error == 0) { 16954811da5SToomas Soome rw_enter(&dn->dn_struct_rwlock, RW_WRITER); 17054811da5SToomas Soome /* 17154811da5SToomas Soome * Another thread could have allocated it; check 17254811da5SToomas Soome * again now that we have the struct lock. 17354811da5SToomas Soome */ 17454811da5SToomas Soome if (dn->dn_type == DMU_OT_NONE) { 17554811da5SToomas Soome dnode_allocate(dn, ot, blocksize, 0, 17654811da5SToomas Soome bonustype, bonuslen, dn_slots, tx); 17754811da5SToomas Soome rw_exit(&dn->dn_struct_rwlock); 17854811da5SToomas Soome dmu_tx_add_new_object(tx, dn); 17954811da5SToomas Soome dnode_rele(dn, FTAG); 18054811da5SToomas Soome return (object); 18154811da5SToomas Soome } 18254811da5SToomas Soome rw_exit(&dn->dn_struct_rwlock); 18354811da5SToomas Soome dnode_rele(dn, FTAG); 18454811da5SToomas Soome DNODE_STAT_BUMP(dnode_alloc_race); 18554811da5SToomas Soome } 186fa9e4066Sahrens 18754811da5SToomas Soome /* 18854811da5SToomas Soome * Skip to next known valid starting point on error. This 18954811da5SToomas Soome * is the start of the next block of dnodes. 19054811da5SToomas Soome */ 19154811da5SToomas Soome if (dmu_object_next(os, &object, B_TRUE, 0) != 0) { 19254811da5SToomas Soome object = P2ROUNDUP(object + 1, DNODES_PER_BLOCK); 19354811da5SToomas Soome DNODE_STAT_BUMP(dnode_alloc_next_block); 19454811da5SToomas Soome } 19554811da5SToomas Soome (void) atomic_swap_64(cpuobj, object); 196fa9e4066Sahrens } 197fa9e4066Sahrens } 198fa9e4066Sahrens 199221813c1SMatthew Ahrens uint64_t 200221813c1SMatthew Ahrens dmu_object_alloc(objset_t *os, dmu_object_type_t ot, int blocksize, 201221813c1SMatthew Ahrens dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx) 202221813c1SMatthew Ahrens { 20354811da5SToomas Soome return (dmu_object_alloc_impl(os, ot, blocksize, 0, bonustype, 20454811da5SToomas Soome bonuslen, 0, tx)); 20554811da5SToomas Soome } 20654811da5SToomas Soome 20754811da5SToomas Soome uint64_t 20854811da5SToomas Soome dmu_object_alloc_ibs(objset_t *os, dmu_object_type_t ot, int blocksize, 20954811da5SToomas Soome int indirect_blockshift, dmu_object_type_t bonustype, int bonuslen, 21054811da5SToomas Soome dmu_tx_t *tx) 21154811da5SToomas Soome { 21254811da5SToomas Soome return (dmu_object_alloc_impl(os, ot, blocksize, indirect_blockshift, 21354811da5SToomas Soome bonustype, bonuslen, 0, tx)); 21454811da5SToomas Soome } 21554811da5SToomas Soome 21654811da5SToomas Soome uint64_t 21754811da5SToomas Soome dmu_object_alloc_dnsize(objset_t *os, dmu_object_type_t ot, int blocksize, 21854811da5SToomas Soome dmu_object_type_t bonustype, int bonuslen, int dnodesize, dmu_tx_t *tx) 21954811da5SToomas Soome { 22054811da5SToomas Soome return (dmu_object_alloc_impl(os, ot, blocksize, 0, bonustype, 22154811da5SToomas Soome bonuslen, dnodesize, tx)); 222221813c1SMatthew Ahrens } 223221813c1SMatthew Ahrens 224fa9e4066Sahrens int 225fa9e4066Sahrens dmu_object_claim(objset_t *os, uint64_t object, dmu_object_type_t ot, 226fa9e4066Sahrens int blocksize, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx) 22754811da5SToomas Soome { 22854811da5SToomas Soome return (dmu_object_claim_dnsize(os, object, ot, blocksize, bonustype, 22954811da5SToomas Soome bonuslen, 0, tx)); 23054811da5SToomas Soome } 23154811da5SToomas Soome 23254811da5SToomas Soome int 23354811da5SToomas Soome dmu_object_claim_dnsize(objset_t *os, uint64_t object, dmu_object_type_t ot, 23454811da5SToomas Soome int blocksize, dmu_object_type_t bonustype, int bonuslen, 23554811da5SToomas Soome int dnodesize, dmu_tx_t *tx) 236fa9e4066Sahrens { 237fa9e4066Sahrens dnode_t *dn; 23854811da5SToomas Soome int dn_slots = dnodesize >> DNODE_SHIFT; 239ea8dc4b6Seschrock int err; 240fa9e4066Sahrens 24154811da5SToomas Soome if (dn_slots == 0) 24254811da5SToomas Soome dn_slots = DNODE_MIN_SLOTS; 24354811da5SToomas Soome ASSERT3S(dn_slots, >=, DNODE_MIN_SLOTS); 24454811da5SToomas Soome ASSERT3S(dn_slots, <=, DNODE_MAX_SLOTS); 24554811da5SToomas Soome 246ea8dc4b6Seschrock if (object == DMU_META_DNODE_OBJECT && !dmu_tx_private_ok(tx)) 247be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 248fa9e4066Sahrens 24954811da5SToomas Soome err = dnode_hold_impl(os, object, DNODE_MUST_BE_FREE, dn_slots, 25054811da5SToomas Soome FTAG, &dn); 251ea8dc4b6Seschrock if (err) 252ea8dc4b6Seschrock return (err); 25354811da5SToomas Soome dnode_allocate(dn, ot, blocksize, 0, bonustype, bonuslen, dn_slots, tx); 254b0c42cd4Sbzzz dmu_tx_add_new_object(tx, dn); 255b0c42cd4Sbzzz 256fa9e4066Sahrens dnode_rele(dn, FTAG); 257fa9e4066Sahrens 258fa9e4066Sahrens return (0); 259fa9e4066Sahrens } 260fa9e4066Sahrens 261fa9e4066Sahrens int 262fa9e4066Sahrens dmu_object_reclaim(objset_t *os, uint64_t object, dmu_object_type_t ot, 263e77d42eaSMatthew Ahrens int blocksize, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx) 26454811da5SToomas Soome { 26554811da5SToomas Soome return (dmu_object_reclaim_dnsize(os, object, ot, blocksize, bonustype, 26654811da5SToomas Soome bonuslen, 0, tx)); 26754811da5SToomas Soome } 26854811da5SToomas Soome 26954811da5SToomas Soome int 27054811da5SToomas Soome dmu_object_reclaim_dnsize(objset_t *os, uint64_t object, dmu_object_type_t ot, 27154811da5SToomas Soome int blocksize, dmu_object_type_t bonustype, int bonuslen, int dnodesize, 27254811da5SToomas Soome dmu_tx_t *tx) 273fa9e4066Sahrens { 274fa9e4066Sahrens dnode_t *dn; 27554811da5SToomas Soome int dn_slots = dnodesize >> DNODE_SHIFT; 276ea8dc4b6Seschrock int err; 277fa9e4066Sahrens 278*811964cdSTom Caputi if (dn_slots == 0) 279*811964cdSTom Caputi dn_slots = DNODE_MIN_SLOTS; 280*811964cdSTom Caputi 2812bf405a2SMark Maybee if (object == DMU_META_DNODE_OBJECT) 282be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 283fa9e4066Sahrens 28454811da5SToomas Soome err = dnode_hold_impl(os, object, DNODE_MUST_BE_ALLOCATED, 0, 285ea8dc4b6Seschrock FTAG, &dn); 286ea8dc4b6Seschrock if (err) 287ea8dc4b6Seschrock return (err); 2882bf405a2SMark Maybee 28954811da5SToomas Soome dnode_reallocate(dn, ot, blocksize, bonustype, bonuslen, dn_slots, tx); 2902bf405a2SMark Maybee 291fa9e4066Sahrens dnode_rele(dn, FTAG); 292cf04dda1SMark Maybee return (err); 293fa9e4066Sahrens } 294fa9e4066Sahrens 295fa9e4066Sahrens int 296fa9e4066Sahrens dmu_object_free(objset_t *os, uint64_t object, dmu_tx_t *tx) 297fa9e4066Sahrens { 298fa9e4066Sahrens dnode_t *dn; 299ea8dc4b6Seschrock int err; 300fa9e4066Sahrens 301ea8dc4b6Seschrock ASSERT(object != DMU_META_DNODE_OBJECT || dmu_tx_private_ok(tx)); 302fa9e4066Sahrens 30354811da5SToomas Soome err = dnode_hold_impl(os, object, DNODE_MUST_BE_ALLOCATED, 0, 304ea8dc4b6Seschrock FTAG, &dn); 305ea8dc4b6Seschrock if (err) 306ea8dc4b6Seschrock return (err); 307fa9e4066Sahrens 308fa9e4066Sahrens ASSERT(dn->dn_type != DMU_OT_NONE); 309738e2a3cSPaul Dagnelie /* 310738e2a3cSPaul Dagnelie * If we don't create this free range, we'll leak indirect blocks when 311738e2a3cSPaul Dagnelie * we get to freeing the dnode in syncing context. 312738e2a3cSPaul Dagnelie */ 313cdb0ab79Smaybee dnode_free_range(dn, 0, DMU_OBJECT_END, tx); 314fa9e4066Sahrens dnode_free(dn, tx); 315fa9e4066Sahrens dnode_rele(dn, FTAG); 316fa9e4066Sahrens 317fa9e4066Sahrens return (0); 318fa9e4066Sahrens } 319fa9e4066Sahrens 320a2cdcdd2SPaul Dagnelie /* 321a2cdcdd2SPaul Dagnelie * Return (in *objectp) the next object which is allocated (or a hole) 322a2cdcdd2SPaul Dagnelie * after *object, taking into account only objects that may have been modified 323a2cdcdd2SPaul Dagnelie * after the specified txg. 324a2cdcdd2SPaul Dagnelie */ 325fa9e4066Sahrens int 3266754306eSahrens dmu_object_next(objset_t *os, uint64_t *objectp, boolean_t hole, uint64_t txg) 327fa9e4066Sahrens { 32854811da5SToomas Soome uint64_t offset; 32954811da5SToomas Soome uint64_t start_obj; 33054811da5SToomas Soome struct dsl_dataset *ds = os->os_dsl_dataset; 331fa9e4066Sahrens int error; 332fa9e4066Sahrens 33354811da5SToomas Soome if (*objectp == 0) { 33454811da5SToomas Soome start_obj = 1; 33554811da5SToomas Soome } else if (ds && ds->ds_feature_inuse[SPA_FEATURE_LARGE_DNODE]) { 33654811da5SToomas Soome uint64_t i = *objectp + 1; 33754811da5SToomas Soome uint64_t last_obj = *objectp | (DNODES_PER_BLOCK - 1); 33854811da5SToomas Soome dmu_object_info_t doi; 33954811da5SToomas Soome 34054811da5SToomas Soome /* 34154811da5SToomas Soome * Scan through the remaining meta dnode block. The contents 34254811da5SToomas Soome * of each slot in the block are known so it can be quickly 34354811da5SToomas Soome * checked. If the block is exhausted without a match then 34454811da5SToomas Soome * hand off to dnode_next_offset() for further scanning. 34554811da5SToomas Soome */ 34654811da5SToomas Soome while (i <= last_obj) { 34754811da5SToomas Soome error = dmu_object_info(os, i, &doi); 34854811da5SToomas Soome if (error == ENOENT) { 34954811da5SToomas Soome if (hole) { 35054811da5SToomas Soome *objectp = i; 35154811da5SToomas Soome return (0); 35254811da5SToomas Soome } else { 35354811da5SToomas Soome i++; 35454811da5SToomas Soome } 35554811da5SToomas Soome } else if (error == EEXIST) { 35654811da5SToomas Soome i++; 35754811da5SToomas Soome } else if (error == 0) { 35854811da5SToomas Soome if (hole) { 35954811da5SToomas Soome i += doi.doi_dnodesize >> DNODE_SHIFT; 36054811da5SToomas Soome } else { 36154811da5SToomas Soome *objectp = i; 36254811da5SToomas Soome return (0); 36354811da5SToomas Soome } 36454811da5SToomas Soome } else { 36554811da5SToomas Soome return (error); 36654811da5SToomas Soome } 36754811da5SToomas Soome } 36854811da5SToomas Soome 36954811da5SToomas Soome start_obj = i; 37054811da5SToomas Soome } else { 37154811da5SToomas Soome start_obj = *objectp + 1; 37254811da5SToomas Soome } 37354811da5SToomas Soome 37454811da5SToomas Soome offset = start_obj << DNODE_SHIFT; 37554811da5SToomas Soome 376744947dcSTom Erickson error = dnode_next_offset(DMU_META_DNODE(os), 377cdb0ab79Smaybee (hole ? DNODE_FIND_HOLE : 0), &offset, 0, DNODES_PER_BLOCK, txg); 378fa9e4066Sahrens 379fa9e4066Sahrens *objectp = offset >> DNODE_SHIFT; 380fa9e4066Sahrens 381fa9e4066Sahrens return (error); 382fa9e4066Sahrens } 3832acef22dSMatthew Ahrens 3842acef22dSMatthew Ahrens /* 3852acef22dSMatthew Ahrens * Turn this object from old_type into DMU_OTN_ZAP_METADATA, and bump the 3862acef22dSMatthew Ahrens * refcount on SPA_FEATURE_EXTENSIBLE_DATASET. 3872acef22dSMatthew Ahrens * 3882acef22dSMatthew Ahrens * Only for use from syncing context, on MOS objects. 3892acef22dSMatthew Ahrens */ 3902acef22dSMatthew Ahrens void 3912acef22dSMatthew Ahrens dmu_object_zapify(objset_t *mos, uint64_t object, dmu_object_type_t old_type, 3922acef22dSMatthew Ahrens dmu_tx_t *tx) 3932acef22dSMatthew Ahrens { 3942acef22dSMatthew Ahrens dnode_t *dn; 3952acef22dSMatthew Ahrens 3962acef22dSMatthew Ahrens ASSERT(dmu_tx_is_syncing(tx)); 3972acef22dSMatthew Ahrens 3982acef22dSMatthew Ahrens VERIFY0(dnode_hold(mos, object, FTAG, &dn)); 3992acef22dSMatthew Ahrens if (dn->dn_type == DMU_OTN_ZAP_METADATA) { 4002acef22dSMatthew Ahrens dnode_rele(dn, FTAG); 4012acef22dSMatthew Ahrens return; 4022acef22dSMatthew Ahrens } 4032acef22dSMatthew Ahrens ASSERT3U(dn->dn_type, ==, old_type); 4042acef22dSMatthew Ahrens ASSERT0(dn->dn_maxblkid); 405bf26014cSMatthew Ahrens 406bf26014cSMatthew Ahrens /* 407bf26014cSMatthew Ahrens * We must initialize the ZAP data before changing the type, 408bf26014cSMatthew Ahrens * so that concurrent calls to *_is_zapified() can determine if 409bf26014cSMatthew Ahrens * the object has been completely zapified by checking the type. 410bf26014cSMatthew Ahrens */ 411bf26014cSMatthew Ahrens mzap_create_impl(mos, object, 0, 0, tx); 412bf26014cSMatthew Ahrens 4132acef22dSMatthew Ahrens dn->dn_next_type[tx->tx_txg & TXG_MASK] = dn->dn_type = 4142acef22dSMatthew Ahrens DMU_OTN_ZAP_METADATA; 4152acef22dSMatthew Ahrens dnode_setdirty(dn, tx); 4162acef22dSMatthew Ahrens dnode_rele(dn, FTAG); 4172acef22dSMatthew Ahrens 4182acef22dSMatthew Ahrens spa_feature_incr(dmu_objset_spa(mos), 4192acef22dSMatthew Ahrens SPA_FEATURE_EXTENSIBLE_DATASET, tx); 4202acef22dSMatthew Ahrens } 4212acef22dSMatthew Ahrens 4222acef22dSMatthew Ahrens void 4232acef22dSMatthew Ahrens dmu_object_free_zapified(objset_t *mos, uint64_t object, dmu_tx_t *tx) 4242acef22dSMatthew Ahrens { 4252acef22dSMatthew Ahrens dnode_t *dn; 4262acef22dSMatthew Ahrens dmu_object_type_t t; 4272acef22dSMatthew Ahrens 4282acef22dSMatthew Ahrens ASSERT(dmu_tx_is_syncing(tx)); 4292acef22dSMatthew Ahrens 4302acef22dSMatthew Ahrens VERIFY0(dnode_hold(mos, object, FTAG, &dn)); 4312acef22dSMatthew Ahrens t = dn->dn_type; 4322acef22dSMatthew Ahrens dnode_rele(dn, FTAG); 4332acef22dSMatthew Ahrens 4342acef22dSMatthew Ahrens if (t == DMU_OTN_ZAP_METADATA) { 4352acef22dSMatthew Ahrens spa_feature_decr(dmu_objset_spa(mos), 4362acef22dSMatthew Ahrens SPA_FEATURE_EXTENSIBLE_DATASET, tx); 4372acef22dSMatthew Ahrens } 4382acef22dSMatthew Ahrens VERIFY0(dmu_object_free(mos, object, tx)); 4392acef22dSMatthew Ahrens } 440