1240afd8cSMark Johnston /*- 2240afd8cSMark Johnston * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3240afd8cSMark Johnston * 4240afd8cSMark Johnston * Copyright (c) 2022 The FreeBSD Foundation 5240afd8cSMark Johnston * 6240afd8cSMark Johnston * This software was developed by Mark Johnston under sponsorship from 7240afd8cSMark Johnston * the FreeBSD Foundation. 8240afd8cSMark Johnston * 9240afd8cSMark Johnston * Redistribution and use in source and binary forms, with or without 10240afd8cSMark Johnston * modification, are permitted provided that the following conditions are 11240afd8cSMark Johnston * met: 12240afd8cSMark Johnston * 1. Redistributions of source code must retain the above copyright 13240afd8cSMark Johnston * notice, this list of conditions and the following disclaimer. 14240afd8cSMark Johnston * 2. Redistributions in binary form must reproduce the above copyright 15240afd8cSMark Johnston * notice, this list of conditions and the following disclaimer in 16240afd8cSMark Johnston * the documentation and/or other materials provided with the distribution. 17240afd8cSMark Johnston * 18240afd8cSMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19240afd8cSMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20240afd8cSMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21240afd8cSMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22240afd8cSMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23240afd8cSMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24240afd8cSMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25240afd8cSMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26240afd8cSMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27240afd8cSMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28240afd8cSMark Johnston * SUCH DAMAGE. 29240afd8cSMark Johnston */ 30240afd8cSMark Johnston 31240afd8cSMark Johnston #include <assert.h> 32240afd8cSMark Johnston #include <string.h> 33240afd8cSMark Johnston 34240afd8cSMark Johnston #include <util.h> 35240afd8cSMark Johnston 36240afd8cSMark Johnston #include "zfs.h" 37240afd8cSMark Johnston 38240afd8cSMark Johnston #define DNODES_PER_CHUNK (MAXBLOCKSIZE / sizeof(dnode_phys_t)) 39240afd8cSMark Johnston 40240afd8cSMark Johnston struct objset_dnode_chunk { 41240afd8cSMark Johnston dnode_phys_t buf[DNODES_PER_CHUNK]; 42240afd8cSMark Johnston unsigned int nextfree; 43240afd8cSMark Johnston STAILQ_ENTRY(objset_dnode_chunk) next; 44240afd8cSMark Johnston }; 45240afd8cSMark Johnston 46240afd8cSMark Johnston typedef struct zfs_objset { 47240afd8cSMark Johnston /* Physical object set. */ 48240afd8cSMark Johnston objset_phys_t *phys; 49240afd8cSMark Johnston off_t osloc; 50240afd8cSMark Johnston off_t osblksz; 51240afd8cSMark Johnston blkptr_t osbp; /* set in objset_write() */ 52240afd8cSMark Johnston 53240afd8cSMark Johnston /* Accounting. */ 54240afd8cSMark Johnston off_t space; /* bytes allocated to this objset */ 55240afd8cSMark Johnston 56240afd8cSMark Johnston /* dnode allocator. */ 57240afd8cSMark Johnston uint64_t dnodecount; 58240afd8cSMark Johnston STAILQ_HEAD(, objset_dnode_chunk) dnodechunks; 59240afd8cSMark Johnston } zfs_objset_t; 60240afd8cSMark Johnston 61240afd8cSMark Johnston static void 62240afd8cSMark Johnston dnode_init(dnode_phys_t *dnode, uint8_t type, uint8_t bonustype, 63240afd8cSMark Johnston uint16_t bonuslen) 64240afd8cSMark Johnston { 65240afd8cSMark Johnston dnode->dn_indblkshift = MAXBLOCKSHIFT; 66240afd8cSMark Johnston dnode->dn_type = type; 67240afd8cSMark Johnston dnode->dn_bonustype = bonustype; 68240afd8cSMark Johnston dnode->dn_bonuslen = bonuslen; 69240afd8cSMark Johnston dnode->dn_checksum = ZIO_CHECKSUM_FLETCHER_4; 70240afd8cSMark Johnston dnode->dn_nlevels = 1; 71240afd8cSMark Johnston dnode->dn_nblkptr = 1; 72240afd8cSMark Johnston dnode->dn_flags = DNODE_FLAG_USED_BYTES; 73240afd8cSMark Johnston } 74240afd8cSMark Johnston 75240afd8cSMark Johnston zfs_objset_t * 76240afd8cSMark Johnston objset_alloc(zfs_opt_t *zfs, uint64_t type) 77240afd8cSMark Johnston { 78240afd8cSMark Johnston struct objset_dnode_chunk *chunk; 79240afd8cSMark Johnston zfs_objset_t *os; 80240afd8cSMark Johnston 81240afd8cSMark Johnston os = ecalloc(1, sizeof(*os)); 82240afd8cSMark Johnston os->osblksz = sizeof(objset_phys_t); 83240afd8cSMark Johnston os->osloc = objset_space_alloc(zfs, os, &os->osblksz); 84240afd8cSMark Johnston 85240afd8cSMark Johnston /* 86240afd8cSMark Johnston * Object ID zero is always reserved for the meta dnode, which is 87240afd8cSMark Johnston * embedded in the objset itself. 88240afd8cSMark Johnston */ 89240afd8cSMark Johnston STAILQ_INIT(&os->dnodechunks); 90240afd8cSMark Johnston chunk = ecalloc(1, sizeof(*chunk)); 91240afd8cSMark Johnston chunk->nextfree = 1; 92240afd8cSMark Johnston STAILQ_INSERT_HEAD(&os->dnodechunks, chunk, next); 93240afd8cSMark Johnston os->dnodecount = 1; 94240afd8cSMark Johnston 95240afd8cSMark Johnston os->phys = ecalloc(1, os->osblksz); 96240afd8cSMark Johnston os->phys->os_type = type; 97240afd8cSMark Johnston 98240afd8cSMark Johnston dnode_init(&os->phys->os_meta_dnode, DMU_OT_DNODE, DMU_OT_NONE, 0); 99240afd8cSMark Johnston os->phys->os_meta_dnode.dn_datablkszsec = 100240afd8cSMark Johnston DNODE_BLOCK_SIZE >> MINBLOCKSHIFT; 101240afd8cSMark Johnston 102240afd8cSMark Johnston return (os); 103240afd8cSMark Johnston } 104240afd8cSMark Johnston 105240afd8cSMark Johnston /* 106240afd8cSMark Johnston * Write the dnode array and physical object set to disk. 107240afd8cSMark Johnston */ 108240afd8cSMark Johnston static void 109240afd8cSMark Johnston _objset_write(zfs_opt_t *zfs, zfs_objset_t *os, struct dnode_cursor *c, 110240afd8cSMark Johnston off_t loc) 111240afd8cSMark Johnston { 112240afd8cSMark Johnston struct objset_dnode_chunk *chunk, *tmp; 113240afd8cSMark Johnston unsigned int total; 114240afd8cSMark Johnston 115240afd8cSMark Johnston /* 116240afd8cSMark Johnston * Write out the dnode array, i.e., the meta-dnode. For some reason its 117240afd8cSMark Johnston * data blocks must be 16KB in size no matter how large the array is. 118240afd8cSMark Johnston */ 119240afd8cSMark Johnston total = 0; 120240afd8cSMark Johnston STAILQ_FOREACH_SAFE(chunk, &os->dnodechunks, next, tmp) { 121240afd8cSMark Johnston unsigned int i; 122240afd8cSMark Johnston 123240afd8cSMark Johnston assert(chunk->nextfree <= os->dnodecount); 124240afd8cSMark Johnston assert(chunk->nextfree <= DNODES_PER_CHUNK); 125240afd8cSMark Johnston 126240afd8cSMark Johnston for (i = 0; i < chunk->nextfree; i += DNODES_PER_BLOCK) { 127240afd8cSMark Johnston blkptr_t *bp; 128240afd8cSMark Johnston uint64_t fill; 129240afd8cSMark Johnston 130240afd8cSMark Johnston if (chunk->nextfree - i < DNODES_PER_BLOCK) 131240afd8cSMark Johnston fill = DNODES_PER_BLOCK - (chunk->nextfree - i); 132240afd8cSMark Johnston else 133240afd8cSMark Johnston fill = 0; 134240afd8cSMark Johnston bp = dnode_cursor_next(zfs, c, 135240afd8cSMark Johnston (total + i) * sizeof(dnode_phys_t)); 136240afd8cSMark Johnston vdev_pwrite_dnode_indir(zfs, &os->phys->os_meta_dnode, 137240afd8cSMark Johnston 0, fill, chunk->buf + i, DNODE_BLOCK_SIZE, loc, bp); 138240afd8cSMark Johnston loc += DNODE_BLOCK_SIZE; 139240afd8cSMark Johnston } 140240afd8cSMark Johnston total += i; 141240afd8cSMark Johnston 142240afd8cSMark Johnston free(chunk); 143240afd8cSMark Johnston } 144240afd8cSMark Johnston dnode_cursor_finish(zfs, c); 145240afd8cSMark Johnston STAILQ_INIT(&os->dnodechunks); 146240afd8cSMark Johnston 147240afd8cSMark Johnston /* 148240afd8cSMark Johnston * Write the object set itself. The saved block pointer will be copied 149240afd8cSMark Johnston * into the referencing DSL dataset or the uberblocks. 150240afd8cSMark Johnston */ 151240afd8cSMark Johnston vdev_pwrite_data(zfs, DMU_OT_OBJSET, ZIO_CHECKSUM_FLETCHER_4, 0, 1, 152240afd8cSMark Johnston os->phys, os->osblksz, os->osloc, &os->osbp); 153240afd8cSMark Johnston } 154240afd8cSMark Johnston 155240afd8cSMark Johnston void 156240afd8cSMark Johnston objset_write(zfs_opt_t *zfs, zfs_objset_t *os) 157240afd8cSMark Johnston { 158240afd8cSMark Johnston struct dnode_cursor *c; 159240afd8cSMark Johnston off_t dnodeloc, dnodesz; 160240afd8cSMark Johnston uint64_t dnodecount; 161240afd8cSMark Johnston 162240afd8cSMark Johnston /* 163240afd8cSMark Johnston * There is a chicken-and-egg problem here when writing the MOS: we 164240afd8cSMark Johnston * cannot write space maps before we're finished allocating space from 165240afd8cSMark Johnston * the vdev, and we can't write the MOS without having allocated space 166240afd8cSMark Johnston * for indirect dnode blocks. Thus, rather than lazily allocating 167240afd8cSMark Johnston * indirect blocks for the meta-dnode (which would be simpler), they are 168240afd8cSMark Johnston * allocated up-front and before writing space maps. 169240afd8cSMark Johnston */ 170240afd8cSMark Johnston dnodecount = os->dnodecount; 171240afd8cSMark Johnston if (os == zfs->mos) 172240afd8cSMark Johnston dnodecount += zfs->mscount; 173240afd8cSMark Johnston dnodesz = dnodecount * sizeof(dnode_phys_t); 174240afd8cSMark Johnston c = dnode_cursor_init(zfs, os, &os->phys->os_meta_dnode, dnodesz, 175240afd8cSMark Johnston DNODE_BLOCK_SIZE); 176240afd8cSMark Johnston dnodesz = roundup2(dnodesz, DNODE_BLOCK_SIZE); 177240afd8cSMark Johnston dnodeloc = objset_space_alloc(zfs, os, &dnodesz); 178240afd8cSMark Johnston 179240afd8cSMark Johnston if (os == zfs->mos) { 180240afd8cSMark Johnston vdev_spacemap_write(zfs); 181240afd8cSMark Johnston 182240afd8cSMark Johnston /* 183*4f816f5bSMark Johnston * We've finished allocating space, account for it in $MOS and 184*4f816f5bSMark Johnston * in the parent directory. 185240afd8cSMark Johnston */ 186*4f816f5bSMark Johnston dsl_dir_size_add(zfs->mosdsldir, os->space); 187*4f816f5bSMark Johnston dsl_dir_size_add(zfs->rootdsldir, os->space); 188240afd8cSMark Johnston } 189240afd8cSMark Johnston _objset_write(zfs, os, c, dnodeloc); 190240afd8cSMark Johnston } 191240afd8cSMark Johnston 192240afd8cSMark Johnston dnode_phys_t * 193240afd8cSMark Johnston objset_dnode_bonus_alloc(zfs_objset_t *os, uint8_t type, uint8_t bonustype, 194240afd8cSMark Johnston uint16_t bonuslen, uint64_t *idp) 195240afd8cSMark Johnston { 196240afd8cSMark Johnston struct objset_dnode_chunk *chunk; 197240afd8cSMark Johnston dnode_phys_t *dnode; 198240afd8cSMark Johnston 199240afd8cSMark Johnston assert(bonuslen <= DN_OLD_MAX_BONUSLEN); 200240afd8cSMark Johnston assert(!STAILQ_EMPTY(&os->dnodechunks)); 201240afd8cSMark Johnston 202240afd8cSMark Johnston chunk = STAILQ_LAST(&os->dnodechunks, objset_dnode_chunk, next); 203240afd8cSMark Johnston if (chunk->nextfree == DNODES_PER_CHUNK) { 204240afd8cSMark Johnston chunk = ecalloc(1, sizeof(*chunk)); 205240afd8cSMark Johnston STAILQ_INSERT_TAIL(&os->dnodechunks, chunk, next); 206240afd8cSMark Johnston } 207240afd8cSMark Johnston *idp = os->dnodecount++; 208240afd8cSMark Johnston dnode = &chunk->buf[chunk->nextfree++]; 209240afd8cSMark Johnston dnode_init(dnode, type, bonustype, bonuslen); 210240afd8cSMark Johnston dnode->dn_datablkszsec = os->osblksz >> MINBLOCKSHIFT; 211240afd8cSMark Johnston return (dnode); 212240afd8cSMark Johnston } 213240afd8cSMark Johnston 214240afd8cSMark Johnston dnode_phys_t * 215240afd8cSMark Johnston objset_dnode_alloc(zfs_objset_t *os, uint8_t type, uint64_t *idp) 216240afd8cSMark Johnston { 217240afd8cSMark Johnston return (objset_dnode_bonus_alloc(os, type, DMU_OT_NONE, 0, idp)); 218240afd8cSMark Johnston } 219240afd8cSMark Johnston 220240afd8cSMark Johnston /* 221240afd8cSMark Johnston * Look up a physical dnode by ID. This is not used often so a linear search is 222240afd8cSMark Johnston * fine. 223240afd8cSMark Johnston */ 224240afd8cSMark Johnston dnode_phys_t * 225240afd8cSMark Johnston objset_dnode_lookup(zfs_objset_t *os, uint64_t id) 226240afd8cSMark Johnston { 227240afd8cSMark Johnston struct objset_dnode_chunk *chunk; 228240afd8cSMark Johnston 229240afd8cSMark Johnston assert(id > 0); 230240afd8cSMark Johnston assert(id < os->dnodecount); 231240afd8cSMark Johnston 232240afd8cSMark Johnston STAILQ_FOREACH(chunk, &os->dnodechunks, next) { 233240afd8cSMark Johnston if (id < DNODES_PER_CHUNK) 234240afd8cSMark Johnston return (&chunk->buf[id]); 235240afd8cSMark Johnston id -= DNODES_PER_CHUNK; 236240afd8cSMark Johnston } 237240afd8cSMark Johnston assert(0); 238240afd8cSMark Johnston return (NULL); 239240afd8cSMark Johnston } 240240afd8cSMark Johnston 241240afd8cSMark Johnston off_t 242240afd8cSMark Johnston objset_space_alloc(zfs_opt_t *zfs, zfs_objset_t *os, off_t *lenp) 243240afd8cSMark Johnston { 244240afd8cSMark Johnston off_t loc; 245240afd8cSMark Johnston 246240afd8cSMark Johnston loc = vdev_space_alloc(zfs, lenp); 247240afd8cSMark Johnston os->space += *lenp; 248240afd8cSMark Johnston return (loc); 249240afd8cSMark Johnston } 250240afd8cSMark Johnston 251240afd8cSMark Johnston uint64_t 252240afd8cSMark Johnston objset_space(const zfs_objset_t *os) 253240afd8cSMark Johnston { 254240afd8cSMark Johnston return (os->space); 255240afd8cSMark Johnston } 256240afd8cSMark Johnston 257240afd8cSMark Johnston void 258240afd8cSMark Johnston objset_root_blkptr_copy(const zfs_objset_t *os, blkptr_t *bp) 259240afd8cSMark Johnston { 260240afd8cSMark Johnston memcpy(bp, &os->osbp, sizeof(blkptr_t)); 261240afd8cSMark Johnston } 262