hammer2 - Start working on the freemap (note: freemap not yet operational)
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 30 Jan 2013 07:27:01 +0000 (23:27 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 30 Jan 2013 07:27:01 +0000 (23:27 -0800)
* hammer2 show now tries to dump all four volume headers

* Minimum storage allocation chunk size is 1K, increase MIN_RADIX and
  MIN_ALLOC appropriately.

* Preliminary reserved block mappings for the freemap indirect and bitmap
  blocks.

* Preliminary freemap design will use normal indirect blocks (but with
  a different bref.type name).  The check area of the bref will be used to
  store additional freemap hints.

  The volume header will contain a blockref to the root of the freemap,
  and newfs_hammer2 now initializes this blockref.

* Bring major hammer2_disk.h media structure comments up to snuff.

* Formalize blockref.methods and set proper defaults.  This field contains
  the crc/check and compression methods for a blockref.

sbin/hammer2/cmd_debug.c
sbin/newfs_hammer2/newfs_hammer2.c
sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_chain.c
sys/vfs/hammer2/hammer2_disk.h
sys/vfs/hammer2/hammer2_flush.c
sys/vfs/hammer2/hammer2_freemap.c
sys/vfs/hammer2/hammer2_subr.c
sys/vfs/hammer2/hammer2_vfsops.c
sys/vfs/hammer2/hammer2_vnops.c

index f7e3ecc..6e9f06c 100644 (file)
@@ -315,16 +315,19 @@ cmd_show(const char *devpath)
 {
        hammer2_blockref_t broot;
        int fd;
+       int i;
 
        fd = open(devpath, O_RDONLY);
        if (fd < 0) {
                perror("open");
                return 1;
        }
-       bzero(&broot, sizeof(broot));
-       broot.type = HAMMER2_BREF_TYPE_VOLUME;
-       broot.data_off = 0 | HAMMER2_PBUFRADIX;
-       show_bref(fd, 0, 0, &broot);
+       for (i = 0; i < 4; ++i) {
+               bzero(&broot, sizeof(broot));
+               broot.type = HAMMER2_BREF_TYPE_VOLUME;
+               broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
+               show_bref(fd, 0, i, &broot);
+       }
        close(fd);
 
        return 0;
index aae4c6e..52038d1 100644 (file)
@@ -452,6 +452,7 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
        hammer2_inode_data_t *rawip;
        hammer2_blockref_t sroot_blockref;
        hammer2_blockref_t root_blockref;
+       hammer2_blockref_t freemap_blockref;
        uint64_t now;
        hammer2_off_t volu_base = 0;
        hammer2_off_t boot_base = HAMMER2_ZONE_SEG;
@@ -484,10 +485,14 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
        }
 
        /*
+        * Make sure alloc_base won't cross the reserved area at the
+        * beginning of each 2GB zone.
+        *
         * Reserve space for the super-root inode and the root inode.
-        * Put them in the same 64K block.
+        * Make sure they are in the same 64K block to simplify our code.
         */
        assert((alloc_base & HAMMER2_PBUFMASK) == 0);
+       assert(alloc_base < HAMMER2_ZONE_BYTES64 - HAMMER2_ZONE_SEG);
 
        alloc_base &= ~HAMMER2_PBUFMASK64;
        alloc_direct(&alloc_base, &sroot_blockref, HAMMER2_INODE_BYTES);
@@ -541,8 +546,8 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
        root_blockref.check.iscsi32.value =
                        hammer2_icrc32(rawip, sizeof(*rawip));
        root_blockref.type = HAMMER2_BREF_TYPE_INODE;
-       root_blockref.methods = HAMMER2_ENC_CHECKMETHOD(HAMMER2_CHECK_ICRC) |
-                               HAMMER2_ENC_COMPMETHOD(HAMMER2_COMP_AUTOZERO);
+       root_blockref.methods = HAMMER2_ENC_CHECK(HAMMER2_CHECK_ISCSI32) |
+                               HAMMER2_ENC_COMP(HAMMER2_COMP_AUTOZERO);
 
        /*
         * Format the super-root directory inode, giving it one directory
@@ -593,8 +598,9 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
        sroot_blockref.check.iscsi32.value =
                                        hammer2_icrc32(rawip, sizeof(*rawip));
        sroot_blockref.type = HAMMER2_BREF_TYPE_INODE;
-       sroot_blockref.methods = HAMMER2_ENC_CHECKMETHOD(HAMMER2_CHECK_ICRC) |
-                                HAMMER2_ENC_COMPMETHOD(HAMMER2_COMP_AUTOZERO);
+       sroot_blockref.methods = HAMMER2_ENC_CHECK(HAMMER2_CHECK_ISCSI32) |
+                                HAMMER2_ENC_COMP(HAMMER2_COMP_AUTOZERO);
+       rawip = NULL;
 
        /*
         * Write out the 64K HAMMER2 block containing the root and sroot.
@@ -607,6 +613,58 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
        }
 
        /*
+        * Set up the freemap blockref.  This blockref must point to the
+        * appropriate reserved block (ZONE_FREEMAP_A + ZONEFM_LAYER0).
+        * We use a special check method CHECK_FREEMAP which is basically
+        * just CHECK_ISCSI32 but contains additional hinting fields to
+        * help the allocator.
+        *
+        * Even though the freemap is multi-level, all newfs2_hammer2 needs
+        * to do is set up an empty root freemap indirect block.  The HAMMER2
+        * VFS will populate the remaining layers and leaf(s) on the fly.
+        *
+        * The root freemap indirect block must represent a space large enough
+        * to cover the whole filesystem.  Since we are using normal
+        * blockref's, each indirect freemap block represents
+        * FREEMAP_NODE_RADIX (10) bits of address space.  A 64KB leaf block
+        * represents FREEMAP_LEAF_REP (256MB) bytes of storage.
+        *
+        * For now we install a MAXIMAL key range to (potentially) support
+        * a sparse storage map by default.  Only certain keybits values
+        * are allowed for the freemap root (64, 54, 44, or 34).
+        */
+       bzero(buf, HAMMER2_PBUFSIZE);
+       bzero(&freemap_blockref, sizeof(freemap_blockref));
+       freemap_blockref.vradix = HAMMER2_PBUFRADIX;
+       freemap_blockref.data_off = (HAMMER2_ZONE_FREEMAP_A +
+                                    HAMMER2_ZONEFM_LAYER0) |
+                                   HAMMER2_PBUFRADIX;
+       freemap_blockref.copyid = HAMMER2_COPYID_LOCAL;
+       freemap_blockref.keybits = 64;
+       freemap_blockref.type = HAMMER2_BREF_TYPE_FREEMAP_ROOT;
+       freemap_blockref.methods = HAMMER2_ENC_CHECK(HAMMER2_CHECK_FREEMAP) |
+                                  HAMMER2_ENC_COMP(HAMMER2_COMP_AUTOZERO);
+
+       /*
+        * check union also has hinting fields.  We can just set the (biggest)
+        * heuristic to a maximal value and let the allocator adjust it,
+        * and we must initialize (avail) properly (taking into account
+        * reserved blocks) so auto-initialized sub-trees/leafs match up
+        * to expected values.
+        */
+       freemap_blockref.check.freemap.icrc32 =
+                                       hammer2_icrc32(buf, HAMMER2_PBUFSIZE);
+       freemap_blockref.check.freemap.biggest = 64;
+       freemap_blockref.check.freemap.avail = free_space;
+
+       n = pwrite(fd, buf, HAMMER2_PBUFSIZE,
+                  freemap_blockref.data_off & HAMMER2_OFF_MASK_HI);
+       if (n != HAMMER2_PBUFSIZE) {
+               perror("write");
+               exit(1);
+       }
+
+       /*
         * Format the volume header.
         *
         * The volume header points to sroot_blockref.  Also be absolutely
@@ -634,6 +692,7 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
        vol->allocator_beg = alloc_base;
 
        vol->sroot_blockset.blockref[0] = sroot_blockref;
+       vol->freemap_blockref = freemap_blockref;
        vol->mirror_tid = 0;
        vol->alloc_tid = 16;
        vol->icrc_sects[HAMMER2_VOL_ICRC_SECT1] =
index 4ffecdc..08450bb 100644 (file)
@@ -78,6 +78,9 @@ struct hammer2_span;
 struct hammer2_state;
 struct hammer2_msg;
 
+struct hammer2_indblock;
+struct hammer2_data;
+
 /*
  * The chain structure tracks blockref recursions all the way to
  * the root volume.  These consist of indirect blocks, inodes,
@@ -270,6 +273,9 @@ struct hammer2_data {
 
 typedef struct hammer2_data hammer2_data_t;
 
+/*
+ * XXX
+ */
 struct hammer2_freecache {
        hammer2_off_t   bulk;
        hammer2_off_t   single;
@@ -358,6 +364,7 @@ extern long hammer2_iod_indr_read;
 extern long hammer2_iod_file_write;
 extern long hammer2_iod_meta_write;
 extern long hammer2_iod_indr_write;
+extern long hammer2_iod_fmap_write;
 extern long hammer2_iod_volu_write;
 extern long hammer2_ioa_file_read;
 extern long hammer2_ioa_meta_read;
@@ -365,6 +372,7 @@ extern long hammer2_ioa_indr_read;
 extern long hammer2_ioa_file_write;
 extern long hammer2_ioa_meta_write;
 extern long hammer2_ioa_indr_write;
+extern long hammer2_ioa_fmap_write;
 extern long hammer2_ioa_volu_write;
 
 /*
@@ -395,7 +403,7 @@ u_int32_t hammer2_to_unix_xid(uuid_t *uuid);
 void hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid);
 
 hammer2_key_t hammer2_dirhash(const unsigned char *name, size_t len);
-int hammer2_bytes_to_radix(size_t bytes);
+int hammer2_allocsize(size_t bytes);
 
 int hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
                         hammer2_key_t *lbasep, hammer2_key_t *leofp);
index 7acff1c..6c3c9af 100644 (file)
@@ -139,11 +139,14 @@ hammer2_chain_alloc(hammer2_mount_t *hmp, hammer2_blockref_t *bref)
                ip->hmp = hmp;
                break;
        case HAMMER2_BREF_TYPE_INDIRECT:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                np = kmalloc(sizeof(*np), hmp->mchain, M_WAITOK | M_ZERO);
                chain = &np->chain;
                chain->u.np = np;
                break;
        case HAMMER2_BREF_TYPE_DATA:
+       case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
                dp = kmalloc(sizeof(*dp), hmp->mchain, M_WAITOK | M_ZERO);
                chain = &dp->chain;
                chain->u.dp = dp;
@@ -517,6 +520,8 @@ hammer2_chain_lock(hammer2_mount_t *hmp, hammer2_chain_t *chain, int how)
                        return(0);
                if (chain->bref.type == HAMMER2_BREF_TYPE_DATA)
                        return(0);
+               if (chain->bref.type == HAMMER2_BREF_TYPE_FREEMAP_LEAF)
+                       return(0);
                /* fall through */
        case HAMMER2_RESOLVE_ALWAYS:
                break;
@@ -627,6 +632,9 @@ hammer2_chain_lock(hammer2_mount_t *hmp, hammer2_chain_t *chain, int how)
                break;
        case HAMMER2_BREF_TYPE_INDIRECT:
        case HAMMER2_BREF_TYPE_DATA:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
+       case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
        default:
                /*
                 * Point data at the device buffer and leave bp intact.
@@ -701,6 +709,11 @@ hammer2_chain_unlock(hammer2_mount_t *hmp, hammer2_chain_t *chain)
                case HAMMER2_BREF_TYPE_INDIRECT:
                        counterp = &hammer2_ioa_indr_write;
                        break;
+               case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+               case HAMMER2_BREF_TYPE_FREEMAP_NODE:
+               case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
+                       counterp = &hammer2_ioa_fmap_write;
+                       break;
                default:
                        counterp = &hammer2_ioa_volu_write;
                        break;
@@ -717,6 +730,11 @@ hammer2_chain_unlock(hammer2_mount_t *hmp, hammer2_chain_t *chain)
                case HAMMER2_BREF_TYPE_INDIRECT:
                        counterp = &hammer2_iod_indr_write;
                        break;
+               case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+               case HAMMER2_BREF_TYPE_FREEMAP_NODE:
+               case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
+                       counterp = &hammer2_iod_fmap_write;
+                       break;
                default:
                        counterp = &hammer2_iod_volu_write;
                        break;
@@ -730,6 +748,9 @@ hammer2_chain_unlock(hammer2_mount_t *hmp, hammer2_chain_t *chain)
         * If a device buffer was used for data be sure to destroy the
         * buffer when we are done to avoid aliases (XXX what about the
         * underlying VM pages?).
+        *
+        * NOTE: Freemap leaf's use reserved blocks and thus no aliasing
+        *       is possible.
         */
        if (chain->bref.type == HAMMER2_BREF_TYPE_DATA)
                chain->bp->b_flags |= B_RELBUF;
@@ -803,7 +824,8 @@ hammer2_chain_resize(hammer2_inode_t *ip, hammer2_chain_t *chain,
        int error;
 
        /*
-        * Only data and indirect blocks can be resized for now
+        * Only data and indirect blocks can be resized for now.
+        * (The volu root, inodes, and freemap elements use a fixed size).
         */
        KKASSERT(chain != &hmp->vchain);
        KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_DATA ||
@@ -1010,7 +1032,7 @@ skip1:
 
        /*
         * We currently should never instantiate a device buffer for a
-        * data chain.
+        * file data chain.  (We definitely can for a freemap chain).
         */
        KKASSERT(chain->bref.type != HAMMER2_BREF_TYPE_DATA);
 
@@ -1028,6 +1050,9 @@ skip1:
                break;
        case HAMMER2_BREF_TYPE_DATA:
        case HAMMER2_BREF_TYPE_INDIRECT:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
+       case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
                /*
                 * Perform the copy-on-write operation
                 */
@@ -1191,6 +1216,8 @@ hammer2_chain_get(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                bref = &parent->data->ipdata.u.blockset.blockref[index];
                break;
        case HAMMER2_BREF_TYPE_INDIRECT:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                KKASSERT(parent->data != NULL);
                KKASSERT(index >= 0 &&
                         index < parent->bytes / sizeof(hammer2_blockref_t));
@@ -1318,7 +1345,8 @@ hammer2_chain_lookup(hammer2_mount_t *hmp, hammer2_chain_t **parentp,
         * encloses the key range or we hit the inode.
         */
        parent = *parentp;
-       while (parent->bref.type == HAMMER2_BREF_TYPE_INDIRECT) {
+       while (parent->bref.type == HAMMER2_BREF_TYPE_INDIRECT ||
+              parent->bref.type == HAMMER2_BREF_TYPE_FREEMAP_NODE) {
                scan_beg = parent->bref.key;
                scan_end = scan_beg +
                           ((hammer2_key_t)1 << parent->bref.keybits) - 1;
@@ -1358,6 +1386,8 @@ again:
                count = HAMMER2_SET_COUNT;
                break;
        case HAMMER2_BREF_TYPE_INDIRECT:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                /*
                 * Optimize indirect blocks in the INITIAL state to avoid
                 * I/O.
@@ -1429,7 +1459,8 @@ again:
         * The parent always has to be locked with at least RESOLVE_MAYBE,
         * so it might need a fixup if the caller passed incompatible flags.
         */
-       if (chain->bref.type == HAMMER2_BREF_TYPE_INDIRECT) {
+       if (chain->bref.type == HAMMER2_BREF_TYPE_INDIRECT ||
+           chain->bref.type == HAMMER2_BREF_TYPE_FREEMAP_NODE) {
                hammer2_chain_unlock(hmp, parent);
                *parentp = parent = chain;
                if (flags & HAMMER2_LOOKUP_NOLOCK) {
@@ -1504,7 +1535,8 @@ again:
                if (chain == parent)
                        return(NULL);
                chain = NULL;
-       } else if (parent->bref.type != HAMMER2_BREF_TYPE_INDIRECT) {
+       } else if (parent->bref.type != HAMMER2_BREF_TYPE_INDIRECT &&
+                  parent->bref.type != HAMMER2_BREF_TYPE_FREEMAP_NODE) {
                /*
                 * We reached the end of the iteration.
                 */
@@ -1543,6 +1575,8 @@ again2:
                count = HAMMER2_SET_COUNT;
                break;
        case HAMMER2_BREF_TYPE_INDIRECT:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                if (parent->flags & HAMMER2_CHAIN_INITIAL) {
                        base = NULL;
                } else {
@@ -1617,7 +1651,8 @@ again2:
         * The parent always has to be locked with at least RESOLVE_MAYBE,
         * so it might need a fixup if the caller passed incompatible flags.
         */
-       if (chain->bref.type == HAMMER2_BREF_TYPE_INDIRECT) {
+       if (chain->bref.type == HAMMER2_BREF_TYPE_INDIRECT ||
+           chain->bref.type == HAMMER2_BREF_TYPE_FREEMAP_NODE) {
                hammer2_chain_unlock(hmp, parent);
                *parentp = parent = chain;
                chain = NULL;
@@ -1692,7 +1727,8 @@ hammer2_chain_create(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                dummy.type = type;
                dummy.key = key;
                dummy.keybits = keybits;
-               dummy.data_off = hammer2_bytes_to_radix(bytes);
+               dummy.data_off = hammer2_allocsize(bytes);
+               dummy.methods = parent->bref.methods;
                chain = hammer2_chain_alloc(hmp, &dummy);
                allocated = 1;
 
@@ -1719,6 +1755,12 @@ hammer2_chain_create(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                        panic("hammer2_chain_create: cannot be used to"
                              "create indirect block");
                        break;
+               case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+               case HAMMER2_BREF_TYPE_FREEMAP_NODE:
+                       panic("hammer2_chain_create: cannot be used to"
+                             "create freemap root or node");
+                       break;
+               case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
                case HAMMER2_BREF_TYPE_DATA:
                default:
                        /* leave chain->data NULL */
@@ -1746,6 +1788,8 @@ again:
                count = HAMMER2_SET_COUNT;
                break;
        case HAMMER2_BREF_TYPE_INDIRECT:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                if (parent->flags & HAMMER2_CHAIN_INITIAL) {
                        base = NULL;
                } else {
@@ -1880,17 +1924,24 @@ again:
         *                      the data onto device buffers!).
         */
        if (allocated) {
-               if (chain->bref.type == HAMMER2_BREF_TYPE_DATA) {
+               switch(chain->bref.type) {
+               case HAMMER2_BREF_TYPE_DATA:
+               case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
                        hammer2_chain_modify(hmp, chain,
                                             HAMMER2_MODIFY_OPTDATA);
-               } else if (chain->bref.type == HAMMER2_BREF_TYPE_INDIRECT) {
+                       break;
+               case HAMMER2_BREF_TYPE_INDIRECT:
+               case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+               case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                        /* not supported in this function */
                        panic("hammer2_chain_create: bad type");
                        atomic_set_int(&chain->flags, HAMMER2_CHAIN_INITIAL);
                        hammer2_chain_modify(hmp, chain,
                                             HAMMER2_MODIFY_OPTDATA);
-               } else {
+                       break;
+               default:
                        hammer2_chain_modify(hmp, chain, 0);
+                       break;
                }
        } else {
                /*
@@ -1997,6 +2048,8 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                        count = HAMMER2_SET_COUNT;
                        break;
                case HAMMER2_BREF_TYPE_INDIRECT:
+               case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+               case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                        count = parent->bytes / sizeof(hammer2_blockref_t);
                        break;
                case HAMMER2_BREF_TYPE_VOLUME:
@@ -2016,6 +2069,8 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                        count = HAMMER2_SET_COUNT;
                        break;
                case HAMMER2_BREF_TYPE_INDIRECT:
+               case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+               case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                        base = &parent->data->npdata.blockref[0];
                        count = parent->bytes / sizeof(hammer2_blockref_t);
                        break;
@@ -2145,10 +2200,19 @@ hammer2_chain_create_indirect(hammer2_mount_t *hmp, hammer2_chain_t *parent,
        /*
         * Ok, create our new indirect block
         */
-       dummy.bref.type = HAMMER2_BREF_TYPE_INDIRECT;
+       switch(parent->bref.type) {
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
+               dummy.bref.type = HAMMER2_BREF_TYPE_FREEMAP_NODE;
+               break;
+       default:
+               dummy.bref.type = HAMMER2_BREF_TYPE_INDIRECT;
+               break;
+       }
        dummy.bref.key = key;
        dummy.bref.keybits = keybits;
-       dummy.bref.data_off = hammer2_bytes_to_radix(nbytes);
+       dummy.bref.data_off = hammer2_allocsize(nbytes);
+       dummy.bref.methods = parent->bref.methods;
        ichain = hammer2_chain_alloc(hmp, &dummy.bref);
        atomic_set_int(&ichain->flags, HAMMER2_CHAIN_INITIAL);
 
@@ -2384,6 +2448,8 @@ hammer2_chain_delete(hammer2_mount_t *hmp, hammer2_chain_t *parent,
                count = HAMMER2_SET_COUNT;
                break;
        case HAMMER2_BREF_TYPE_INDIRECT:
+       case HAMMER2_BREF_TYPE_FREEMAP_ROOT:
+       case HAMMER2_BREF_TYPE_FREEMAP_NODE:
                hammer2_chain_modify(hmp, parent, HAMMER2_MODIFY_OPTDATA |
                                                  HAMMER2_MODIFY_NO_MODIFY_TID);
                if (parent->flags & HAMMER2_CHAIN_INITIAL)
index 64200e0..2041a03 100644 (file)
 
 /*
  * The data at the end of a file or directory may be a fragment in order
- * to optimize storage efficiency.  The minimum fragment size is 64 bytes.
+ * to optimize storage efficiency.  The minimum fragment size is 1KB.
  * Since allocations are in powers of 2 fragments must also be sized in
- * powers of 2 (64, 128, 256, ... 65536).
+ * powers of 2 (1024, 2048, ... 65536).
  *
  * For the moment the maximum allocation size is HAMMER2_PBUFSIZE (64K),
- * which is 2^16.  Larger extents may be supported in the future.
+ * which is 2^16.  Larger extents may be supported in the future.  Smaller
+ * fragments might be supported in the future (down to 64 bytes is possible),
+ * but probably will not be.
  *
- * A full indirect block uses supports 1024 x 64-byte blockrefs.
+ * A full indirect block use supports 1024 x 64-byte blockrefs in a 64KB
+ * buffer.  Indirect blocks down to 1KB are supported to keep small
+ * directories small.
  *
  * A maximally sized file (2^64-1 bytes) requires 5 indirect block levels.
  * The hammer2_blockset in the volume header or file inode has another 8
@@ -76,8 +80,8 @@
  * currently supports up to 8 copies, which brings the address space down
  * to 66 bits and gives us 2 bits of leeway.
  */
-#define HAMMER2_MIN_ALLOC      64      /* minimum allocation size */
-#define HAMMER2_MIN_RADIX            /* minimum allocation size 2^N */
+#define HAMMER2_MIN_ALLOC      1024    /* minimum allocation size */
+#define HAMMER2_MIN_RADIX      10      /* minimum allocation size 2^N */
 #define HAMMER2_MAX_RADIX      16      /* maximum allocation size 2^N */
 #define HAMMER2_KEY_RADIX      64      /* number of bits in key */
 
                                 sizeof(hammer2_blockref_t))
 
 /*
- * HAMMER2 processes blockrefs in sets of 8.  The set is fully associative,
- * is not sorted, and may contain holes.
- *
- * A full indirect block supports 1024 blockrefs.
- *
- * An inode embeds one set of blockrefs but may also use the data area for
- * up to 512 bytes of direct data.
+ * In HAMMER2, arrays of blockrefs are fully set-associative, meaning that
+ * any element can occur at any index and holes can be anywhere.  As a
+ * future optimization we will be able to flag that such arrays are sorted
+ * and thus optimize lookups, but for now we don't.
+ *
+ * Inodes embed either 512 bytes of direct data or an array of 8 blockrefs,
+ * resulting in highly efficient storage for files <= 512 bytes and for files
+ * <= 512KB.  Up to 8 directory entries can be referenced from a directory
+ * without requiring an indirect block.
+ *
+ * Indirect blocks are typically either 4KB (64 blockrefs / ~4MB represented),
+ * or 64KB (1024 blockrefs / ~64MB represented).
  */
-#define HAMMER2_SET_COUNT      8       /* direct entries & associativity */
+#define HAMMER2_SET_COUNT      8       /* direct entries */
 #define HAMMER2_SET_RADIX      3
 #define HAMMER2_EMBEDDED_BYTES 512
 #define HAMMER2_EMBEDDED_RADIX 9
  * A HAMMER2 filesystem is always sized in multiples of 8MB.
  *
  * A 4MB segment is reserved at the beginning of each 2GB zone.  This segment
- * contains the volume header, the free block table, and possibly other
- * information in the future.  4MB = 64 x 64K blocks.
+ * contains the volume header (or backup volume header), the free block
+ * table, and possibly other information in the future.
+ *
+ * 4MB = 64 x 64K blocks.  Each 4MB segment is broken down as follows:
+ *
+ *     +-----------------------+
+ *      |      Volume Hdr      | block 0       volume header & alternates
+ *     +-----------------------+               (first four zones only)
+ *      | (A) FreeBlk layer0    | block 1      free block table
+ *      | (A) FreeBlk layer1    |
+ *      | (A) FreeBlk layer2    |
+ *      | (A) FreeBlk layer3    |
+ *      | (A) FreeBlk layer4[8] | (note: 8x64K -> 128x4K)
+ *     +-----------------------+
+ *      | (B) FreeBlk layer0    | block 13     free block table
+ *      | (B) FreeBlk layer1    |
+ *      | (B) FreeBlk layer2    |
+ *      | (B) FreeBlk layer3    |
+ *      | (B) FreeBlk layer4[8] |
+ *     +-----------------------+
+ *      | (C) FreeBlk layer0    | block 25     free block table
+ *      | (C) FreeBlk layer1    |
+ *      | (C) FreeBlk layer2    |
+ *      | (C) FreeBlk layer3    |
+ *      | (C) FreeBlk layer4[8] |
+ *     +-----------------------+
+ *      | (D) FreeBlk layer0    | block 37     free block table
+ *      | (D) FreeBlk layer1    |
+ *      | (D) FreeBlk layer2    |
+ *      | (D) FreeBlk layer3    |
+ *      | (D) FreeBlk layer4[8] |
+ *     +-----------------------+
+ *      |                      | block 49...63
+ *      |      reserved        |
+ *      |                      |
+ *     +-----------------------+
+ *
+ * The first few 2GB zones contain volume headers and volume header backups.
+ * After that the volume header block# is reserved.  The first 2GB zone
+ * contains all four FreeBlk layers, for example, but the layer1 FreeBlk
+ * is only needed once every 1TB.  The free block topology rotates between
+ * several groups {A,B,C,D} in order to ensure that the free block table
+ * is clean upon reboot after a crash or disk failure.
+ *
+ * The Free block table has a resolution of 1KB
  */
 #define HAMMER2_VOLUME_ALIGN           (8 * 1024 * 1024)
 #define HAMMER2_VOLUME_ALIGN64         ((hammer2_off_t)HAMMER2_VOLUME_ALIGN)
 #define HAMMER2_ZONE_BLOCKS_SEG                (HAMMER2_ZONE_SEG / HAMMER2_PBUFSIZE)
 
 /*
+ * 64 x 64KB blocks are reserved at the base of each 2GB zone.  These blocks
+ * are used to store the volume header or volume header backups, allocation
+ * tree, and other information in the future.
+ *
+ * All specified blocks are not necessarily used in all 2GB zones.  However,
+ * dead areas are reserved and MUST NOT BE USED for other purposes.
+ *
+ * The freemap is arranged into four groups.  Modifications rotate through
+ * the groups on a block by block basis (so all the blocks are not necessarily
+ * synchronized to the same group).  Only three groups are actually necessary
+ * (stable, flushing, modifying).
+ *
+ * 64KB freemap indirect blocks are represented by layers 0, 1, 2, and 3.
+ * 4KB freemap leaf blocks each represent 16MB of storage so 128 x 4KB are
+ * needed per zone, which equates to 8 x 64KB layer4 blocks per zone.
+ */
+#define HAMMER2_ZONE_VOLHDR            0       /* volume header or backup */
+#define HAMMER2_ZONE_FREEMAP_A         1       /* freemap layer group A */
+#define HAMMER2_ZONE_FREEMAP_B         13      /* freemap layer group B */
+#define HAMMER2_ZONE_FREEMAP_C         25      /* freemap layer group C */
+#define HAMMER2_ZONE_FREEMAP_D         37      /* freemap layer group D */
+
+#define HAMMER2_ZONEFM_LAYER0          0       /* relative to FREEMAP_x */
+#define HAMMER2_ZONEFM_LAYER1          1
+#define HAMMER2_ZONEFM_LAYER2          2
+#define HAMMER2_ZONEFM_LAYER3          3
+#define HAMMER2_ZONEFM_LAYER4          4       /* 4-11 (8 64KB blocks) */
+
+#define HAMMER2_ZONE_BLOCK49           49      /* future */
+#define HAMMER2_ZONE_BLOCK50           50      /* future */
+#define HAMMER2_ZONE_BLOCK51           51      /* future */
+#define HAMMER2_ZONE_BLOCK52           52      /* future */
+#define HAMMER2_ZONE_BLOCK53           53      /* future */
+#define HAMMER2_ZONE_BLOCK54           54      /* future */
+#define HAMMER2_ZONE_BLOCK55           55      /* future */
+#define HAMMER2_ZONE_BLOCK56           56      /* future */
+#define HAMMER2_ZONE_BLOCK57           57      /* future */
+#define HAMMER2_ZONE_BLOCK58           58      /* future */
+#define HAMMER2_ZONE_BLOCK59           59      /* future */
+
+#define HAMMER2_ZONE_BLOCK60           60      /* future */
+#define HAMMER2_ZONE_BLOCK61           61      /* future */
+#define HAMMER2_ZONE_BLOCK62           62      /* future */
+#define HAMMER2_ZONE_BLOCK63           63      /* future */
+
+/*
  * Two linear areas can be reserved after the initial 2MB segment in the base
  * zone (the one starting at offset 0).  These areas are NOT managed by the
  * block allocator and do not fall under HAMMER2 crc checking rules based
@@ -215,9 +313,11 @@ typedef uint32_t hammer2_crc32_t;
  *
  * Indexes into physical buffers are always 64-byte aligned.  The low 6 bits
  * of the data offset field specifies how large the data chunk being pointed
- * to as a power of 2.  This value typically ranges from HAMMER2_MIN_RADIX
- * to HAMMER2_MAX_RADIX (6-16).  Larger values may be supported in the future
- * to support file extents.
+ * to as a power of 2.  The theoretical minimum radix is thus 6 (The space
+ * needed in the low bits of the data offset field).  However, the practical
+ * minimum allocation chunk size is 1KB (a radix of 10), so HAMMER2 sets
+ * HAMMER2_MIN_RADIX to 10.  The maximum radix is currently 16 (64KB), but
+ * we fully intend to support larger extents in the future.
  */
 #define HAMMER2_OFF_BAD                ((hammer2_off_t)-1)
 #define HAMMER2_OFF_MASK       0xFFFFFFFFFFFFFFC0ULL
@@ -259,6 +359,13 @@ typedef uint32_t hammer2_crc32_t;
  * UNLESS one doesn't mind once-in-a-blue-moon data corruption (such as when
  * farming web data).  HAMMER2 has an unverified dedup feature for just this
  * purpose.
+ *
+ * --
+ *
+ * NOTE: The range of keys represented by the blockref is (key) to
+ *      ((key) + (1LL << keybits) - 1).  HAMMER2 usually populates
+ *      blocks bottom-up, inserting a new root when radix expansion
+ *      is required.
  */
 struct hammer2_blockref {              /* MUST BE EXACTLY 64 BYTES */
        uint8_t         type;           /* type of underlying item */
@@ -286,15 +393,41 @@ struct hammer2_blockref {         /* MUST BE EXACTLY 64 BYTES */
                struct {
                        char data[24];
                } sha192;
+
+               /*
+                * Freemap hints are embedded in addition to the icrc32.
+                *
+                * biggest - largest possible allocation 2^N within sub-tree.
+                *           typically initialized to 64 in freemap_blockref
+                *           and to 54 in each blockref[] entry in the
+                *           FREEMAP_ROOT indirect block.
+                *
+                *           An allocation > 2^N is guaranteed to fail.  An
+                *           allocation <= 2^N MAY fail, and if it does the
+                *           biggest hint will be adjusted downward.
+                *
+                *           Used when allocating space.
+                */
+               struct {
+                       uint32_t icrc32;
+                       uint8_t biggest;
+                       uint8_t reserved05;
+                       uint8_t reserved06;
+                       uint8_t reserved07;
+                       uint64_t avail;         /* total available bytes */
+                       uint64_t unused;        /* unused must be 0 */
+               } freemap;
        } check;
 };
 
 typedef struct hammer2_blockref hammer2_blockref_t;
 
+#if 0
 #define HAMMER2_BREF_SYNC1             0x01    /* modification synchronized */
 #define HAMMER2_BREF_SYNC2             0x02    /* modification committed */
 #define HAMMER2_BREF_DESYNCCHLD                0x04    /* desynchronize children */
 #define HAMMER2_BREF_DELETED           0x80    /* indicates a deletion */
+#endif
 
 #define HAMMER2_BLOCKREF_BYTES         64      /* blockref struct in bytes */
 
@@ -302,12 +435,26 @@ typedef struct hammer2_blockref hammer2_blockref_t;
 #define HAMMER2_BREF_TYPE_INODE                1
 #define HAMMER2_BREF_TYPE_INDIRECT     2
 #define HAMMER2_BREF_TYPE_DATA         3
+#define HAMMER2_BREF_TYPE_FREEMAP_ROOT 4
+#define HAMMER2_BREF_TYPE_FREEMAP_NODE 5
+#define HAMMER2_BREF_TYPE_FREEMAP_LEAF 6
 #define HAMMER2_BREF_TYPE_VOLUME       255     /* pseudo-type */
 
-#define HAMMER2_ENC_COMPMETHOD(n)      (n)
-#define HAMMER2_ENC_CHECKMETHOD(n)     ((n) << 4)
-#define HAMMER2_DEC_COMPMETHOD(n)      ((n) & 15)
-#define HAMMER2_DEC_CHECKMETHOD(n)     (((n) >> 4) & 15)
+#define HAMMER2_ENC_CHECK(n)           ((n) << 4)
+#define HAMMER2_DEC_CHECK(n)           (((n) >> 4) & 15)
+
+#define HAMMER2_CHECK_NONE             0
+#define HAMMER2_CHECK_ISCSI32          1
+#define HAMMER2_CHECK_CRC64            2
+#define HAMMER2_CHECK_SHA192           3
+#define HAMMER2_CHECK_FREEMAP          4
+
+#define HAMMER2_ENC_COMP(n)            (n)
+#define HAMMER2_DEC_COMP(n)            ((n) & 15)
+
+#define HAMMER2_COMP_NONE              0
+#define HAMMER2_COMP_AUTOZERO          1
+
 
 /*
  * HAMMER2 block references are collected into sets of 8 blockrefs.  These
@@ -330,6 +477,9 @@ typedef struct hammer2_blockref hammer2_blockref_t;
  * until the set actually becomes full (that is, the entries in the set can
  * shortcut the indirect blocks when the set is not full).  Depending on how
  * things are filled multiple indirect blocks will eventually be created.
+ *
+ * Indirect blocks are typically 4KB (64 entres) or 64KB (1024 entries) and
+ * are also treated as fully set-associative.
  */
 struct hammer2_blockset {
        hammer2_blockref_t      blockref[HAMMER2_SET_COUNT];
@@ -504,12 +654,6 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
 #define HAMMER2_COPYID_NONE            0
 #define HAMMER2_COPYID_LOCAL           ((uint8_t)-1)
 
-#define HAMMER2_COMP_NONE              0
-#define HAMMER2_COMP_AUTOZERO          1
-
-#define HAMMER2_CHECK_NONE             0
-#define HAMMER2_CHECK_ICRC             1
-
 /*
  * PEER types identify connections and help cluster controller filter
  * out unwanted SPANs.
@@ -536,75 +680,113 @@ typedef struct hammer2_inode_data hammer2_inode_data_t;
 #define HAMMER2_PFSTYPE_MAX            DMSG_PFSTYPE_MAX
 
 /*
- * The allocref structure represents the allocation table.  One 64K block
- * is broken down into 4096 x 16 byte entries.  Each indirect block chops
- * 11 bits off the 64-bit storage space, with leaf entries representing
- * 64KB blocks.  So:  (12, 12, 12, 12, 16) = 64 bit storage space.
- *
- * Each 64K freemap block breaks the 4096 entries into a 64x64 tree with
- * big_hint1 representing the top level every 64th entry and big_hint2
- * representing the lower level in each entry.  These fields specify the
- * largest contiguous radix (1-63) available for allocation in the related
- * sub-tree.  The largest contiguous radix available for the entire block
- * is saved in the parent (for the root this will be alloc_blockref in the
- * volume header).  The hints may be larger than actual and will be corrected
- * on the fly but must not be smaller.  The allocator uses the hints to
- * very quickly locate nearby blocks of the desired size.
- *
- * In indirect blocks the 64-bit free[_or_mask] field stores the total free
- * space for each of the 4096 sub-nodes in bytes.  The total free space
- * represented by the indirect block is stored in its parent.
- *
- * Each leaf element represents a 64K block.  A bitmap replaces the free space
- * count, giving us a 1KB allocation resolution.  A micro-allocation append
- * offset replaces the icrc field.  The micro-allocation feature is not
- * currently implemented and the field will be set to 65536.
- *
- * The allocation map uses reserved blocks so no data block reference is
- * required, only a bit in the flags field to specify which of two possible
- * reserved blocks to use.  This allows the allocation map to be flushed to
- * disk with minimal synchronization.
+ *                             Allocation Table
+ *
+ * In HAMMER2 the allocation table hangs off of the volume header and
+ * utilizes somewhat customized hammer2_blockref based indirect blocks
+ * until hitting the leaf bitmap.  BREF_TYPE_FREEMAP_ROOT and
+ * BREF_TYPE_FREEMAP_NODE represent the indirect blocks but are formatted
+ * the same as BREF_TYPE_INDIRECT except for the (biggest) and (avail)
+ * fields which use some of the check union space.  Thus a special CHECK
+ * id (CHECK_FREEMAP instead of CHECK_ISCSI32) is also specified for these
+ * babies.
+ *
+ * newfs_hammer2 builds the FREEMAP_ROOT block and assigns a radix of
+ * 34, 44, 54, or 64 depending on whether the freemap is to be fitted
+ * to the storage or is to maximized for (possibly) sparse storage.
+ * Other keybits specifications for FREEMAP_ROOT are illegal.  Even fitted
+ * storage is required to specify at least a keybits value of 34.
+ *
+ *     Total possible representation is 2^64 (16 Exabytes).
+ *     10: 1024 entries / 64KB                 16EB (16PB per entry) layer0
+ *     10: 1024 entries / 64KB                 16PB (16TB per entry) layer1
+ *     10: 1024 entries / 64KB                 16TB (16GB per entry) layer2
+ *     10: 1024 entries / 64KB                 16GB (16MB per entry) layer3
+ *     24: 16384 x 1KB allocgran / 4KB         16MB                  layer4
+ *
+ * To make the radix come out to exactly 64 the leaf bitmaps are arranged
+ * into 4KB buffers, with each buffer representing a freemap for 16MB worth
+ * of storage using a 1KB allocation granularity.  The leaf bitmaps are
+ * structures and not just a plain bitmap, hence the extra space needed to
+ * represent 16384 x 1KB blocks.
+ *
+ * The reserved area at the beginning of each 2GB zone is marked as being
+ * allocated on-the-fly and does not have to be pre-set in the freemap,
+ * which is just as well as that would require newfs_hammer2 to do a lot
+ * of writing otherwise.
+ *
+ * Indirect blocks are usually created with a semi-dynamic radix but in the
+ * case of freemap-related indirect blocks, the blocks use a static radix
+ * tree with associations to specific reserved blocks.
  */
-struct hammer2_allocref {
-       uint32_t        icrc_or_app;    /* node: icrc, leaf: append offset */
-       uint16_t        flags;
-       uint8_t         big_hint1;      /* upper level hint */
-       uint8_t         big_hint2;      /* lower level hint */
-       uint64_t        free_or_mask;   /* node: free bytes, leaf: bitmask */
-};
-
-typedef struct hammer2_allocref hammer2_allocref_t;
 
 /*
- * WARNING - allocref size x entries must equate to the hammer buffer size,
- *          and 12 bits per recursion is assumed by the allocator.
+ * 4KB -> hammer2_freemap_elm[256]
+ *
+ *     bitmap     - 64 bits x 1KB representing 64KB.  A '1' bit represents
+ *                  an allocated block.
+ *
+ *     generation - Incremented upon any allocation.  Can't increment more
+ *                  than +64 per background freeing pass due to there being
+ *                  only 64 bits.
  *
- * ALTA-D      Since no data_offset is specified flags are needed to select
- *             which sub-block to recurse down into for root & internal nodes.
- *             (only ALTA and ALTB is currently supported).
+ *     biggest0   - biggest hint (radix) for freemap_elm.  Represents up to
+ *                  64KB (radix 16).
  *
- * LEAF                Terminal entry, always set for leafs.  May be used to support
- *             4MB extent allocations and early termination in the future.
- *             (not required to shortcut allocation scans as the big_hint1/2
- *             fields are used for this).
+ *     biggest1   - biggest hint (radix) for aligned groups of 16 elements,
+ *                  stored in elm[0], elm[16], etc.  Represents up to 1MB.
+ *                  (radix 20)
+ *
+ *     biggest2   - biggest hint (radix) for aligned groups of 256 elements
+ *                  (i.e. the whole array, only used by elm[0]).
+ *                  Represents up to 16MB (radix 24).
+ *
+ * The hinting is used as part of the allocation mechanism to reduce scan
+ * time, which is particularly important as a filesystem approaches full.
+ * Fill ratios are handled at the indirect block level (in the blockrefs) and
+ * not here.
  */
-#define HAMMER2_ALLOCREF_BYTES         16      /* structure size */
-#define HAMMER2_ALLOCREF_ENTRIES       4096    /* entries */
-#define HAMMER2_ALLOCREF_RADIX         12      /* log2(entries) */
+struct hammer2_freemap_elm {
+       uint64_t        bitmap;
+       uint8_t         generation;
+       uint8_t         biggest0;
+       uint8_t         biggest1;
+       uint8_t         biggest2;
+       uint32_t        reserved0C;
+};
 
-#if (HAMMER2_ALLOCREF_BYTES * HAMMER2_ALLOCREF_ENTRIES) != HAMMER2_PBUFSIZE
-#error "allocref parameters do not fit in hammer buffer"
-#endif
-#if (1 << HAMMER2_ALLOCREF_RADIX) != HAMMER2_ALLOCREF_ENTRIES
-#error "allocref parameters are inconsistent"
-#endif
+typedef struct hammer2_freemap_elm hammer2_freemap_elm_t;
 
-#define HAMMER2_ALLOCREF_ALTMASK       0x0003  /* select block for recurse */
-#define HAMMER2_ALLOCREF_ALTA          0x0000
-#define HAMMER2_ALLOCREF_ALTB          0x0001
-#define HAMMER2_ALLOCREF_ALTC          0x0002  /* unsupported */
-#define HAMMER2_ALLOCREF_ALTD          0x0003  /* unsupported */
-#define HAMMER2_ALLOCREF_LEAF          0x0004
+#define HAMMER2_FREEMAP_LEAF_BYTES     4096
+#define HAMMER2_FREEMAP_LEAF_ENTRIES   (HAMMER2_FREEMAP_LEAF_BYTES /   \
+                                        sizeof(hammer2_freemap_elm_t))
+#define HAMMER2_FREEMAP_LEAF_RADIX     24
+#define HAMMER2_FREEMAP_NODE_RADIX     10
+#define HAMMER2_FREEMAP_ELM_RADIX       5      /* 2^5 == 32 bits */
+
+#define HAMMER2_BIGF_KILLED            0x80
+
+/*
+ * Flags (8 bits) - blockref, for freemap only
+ *
+ * Note that the minimum chunk size is 1KB so we could theoretically have
+ * 10 bits here, but we might have some future extension that allows a
+ * chunk size down to 256 bytes and if so we will need bits 8 and 9.
+ */
+#define HAMMER2_AVF_SELMASK            0x03    /* select group */
+#define HAMMER2_AVF_ALL_ALLOC          0x04    /* indicate all allocated */
+#define HAMMER2_AVF_ALL_FREE           0x08    /* indicate all free */
+#define HAMMER2_AVF_RESERVED10         0x10
+#define HAMMER2_AVF_RESERVED20         0x20
+#define HAMMER2_AVF_RESERVED40         0x40
+#define HAMMER2_AVF_RESERVED80         0x80
+#define HAMMER2_AVF_AVMASK32           ((uint32_t)0xFFFFFF00LU)
+#define HAMMER2_AVF_AVMASK64           ((uint64_t)0xFFFFFFFFFFFFFF00LLU)
+
+#define HAMMER2_AV_SELECT_A            0x00
+#define HAMMER2_AV_SELECT_B            0x01
+#define HAMMER2_AV_SELECT_C            0x02
+#define HAMMER2_AV_SELECT_D            0x03
 
 /*
  * The volume header eats a 64K block.  There is currently an issue where
@@ -681,8 +863,8 @@ struct hammer2_volume_data {
         * allocator_size is precalculated at newfs time and does not include
         * reserved blocks, boot, or redo areas.
         *
-        * Initial non-reserved-area allocations do not use the allocation
-        * map but instead adjust alloc_iterator.  Dynamic allocations take
+        * Initial non-reserved-area allocations do not use the freemap
+        * but instead adjust alloc_iterator.  Dynamic allocations take
         * over starting at (allocator_beg).  This makes newfs_hammer2's
         * job a lot easier and can also serve as a testing jig.
         */
@@ -691,7 +873,7 @@ struct hammer2_volume_data {
        hammer2_off_t   allocator_beg;          /* 0070 Initial allocations */
        hammer2_tid_t   mirror_tid;             /* 0078 best committed tid */
        hammer2_tid_t   alloc_tid;              /* 0080 Alloctable modify tid */
-       hammer2_blockref_t alloc_blockref;      /* 0088-00C7 */
+       hammer2_blockref_t freemap_blockref;    /* 0088-00C7 */
 
        /*
         * Copyids are allocated dynamically from the copyexists bitmap.
index d6b1620..5490b2e 100644 (file)
@@ -691,6 +691,8 @@ hammer2_chain_flush_pass1(hammer2_mount_t *hmp, hammer2_chain_t *chain,
                bref = &chain->bref;
 
                KKASSERT((bref->data_off & HAMMER2_OFF_MASK) != 0);
+               KKASSERT(HAMMER2_DEC_CHECK(chain->bref.methods) ==
+                        HAMMER2_CHECK_ISCSI32);
 
                if (chain->bp == NULL) {
                        /*
index 13a160c..52fd402 100644 (file)
@@ -80,7 +80,7 @@ hammer2_freemap_alloc(hammer2_mount_t *hmp, int type, size_t bytes)
        /*
         * Figure out the base 2 radix of the allocation (rounded up)
         */
-       radix = hammer2_bytes_to_radix(bytes);
+       radix = hammer2_allocsize(bytes);
        bytes = 1 << radix;
 
        if (radix <= HAMMER2_MAX_RADIX)
index b31d390..262ee25 100644 (file)
@@ -345,10 +345,11 @@ hammer2_dirhash(const unsigned char *name, size_t len)
  * Return the power-of-2 radix greater or equal to
  * the specified number of bytes.
  *
- * Always returns at least HAMMER2_MIN_RADIX (2^6).
+ * Always returns at least the minimum media allocation
+ * size radix, HAMMER2_MIN_RADIX (10), which is 1KB.
  */
 int
-hammer2_bytes_to_radix(size_t bytes)
+hammer2_allocsize(size_t bytes)
 {
        int radix;
 
@@ -356,6 +357,8 @@ hammer2_bytes_to_radix(size_t bytes)
                bytes = HAMMER2_MIN_ALLOC;
        if (bytes == HAMMER2_PBUFSIZE)
                radix = HAMMER2_PBUFRADIX;
+       else if (bytes >= 16384)
+               radix = 14;
        else if (bytes >= 1024)
                radix = 10;
        else
@@ -376,8 +379,7 @@ hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
        *leofp = ip->ip_data.size & ~HAMMER2_PBUFMASK64;
        KKASSERT(*lbasep <= *leofp);
        if (*lbasep == *leofp /*&& *leofp < 1024 * 1024*/) {
-               radix = hammer2_bytes_to_radix(
-                               (size_t)(ip->ip_data.size - *leofp));
+               radix = hammer2_allocsize((size_t)(ip->ip_data.size - *leofp));
                if (radix < HAMMER2_MINALLOCRADIX)
                        radix = HAMMER2_MINALLOCRADIX;
                *leofp += 1U << radix;
index 920d43e..4a39b15 100644 (file)
@@ -66,10 +66,12 @@ long hammer2_iod_indr_read;
 long hammer2_iod_file_write;
 long hammer2_iod_meta_write;
 long hammer2_iod_indr_write;
+long hammer2_iod_fmap_write;
 long hammer2_iod_volu_write;
 long hammer2_ioa_file_read;
 long hammer2_ioa_meta_read;
 long hammer2_ioa_indr_read;
+long hammer2_ioa_fmap_write;
 long hammer2_ioa_file_write;
 long hammer2_ioa_meta_write;
 long hammer2_ioa_indr_write;
@@ -173,8 +175,6 @@ hammer2_vfs_init(struct vfsconf *conf)
                error = EINVAL;
        if (HAMMER2_INODE_BYTES != sizeof(struct hammer2_inode_data))
                error = EINVAL;
-       if (HAMMER2_ALLOCREF_BYTES != sizeof(struct hammer2_allocref))
-               error = EINVAL;
        if (HAMMER2_VOLUME_BYTES != sizeof(struct hammer2_volume_data))
                error = EINVAL;
 
index 05cad2e..89e5711 100644 (file)
@@ -1076,7 +1076,7 @@ hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                        switch(chain->bref.type) {
                        case HAMMER2_BREF_TYPE_DATA:
                                hammer2_chain_resize(ip, chain,
-                                            hammer2_bytes_to_radix(nblksize),
+                                            hammer2_allocsize(nblksize),
                                             HAMMER2_MODIFY_OPTDATA);
                                bzero(bp->b_data + loff, nblksize - loff);
                                bp->b_bio2.bio_offset = chain->bref.data_off &
@@ -1119,7 +1119,7 @@ hammer2_truncate_file(hammer2_inode_t *ip, hammer2_key_t nsize)
                        switch(chain->bref.type) {
                        case HAMMER2_BREF_TYPE_DATA:
                                hammer2_chain_resize(ip, chain,
-                                            hammer2_bytes_to_radix(nblksize),
+                                            hammer2_allocsize(nblksize),
                                             0);
                                hammer2_chain_modify(hmp, chain, 0);
                                bzero(chain->data->buf + loff, nblksize - loff);
@@ -1284,7 +1284,7 @@ retry:
                error = hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_ALWAYS);
                KKASSERT(error == 0);
 
-               nradix = hammer2_bytes_to_radix(nblksize);
+               nradix = hammer2_allocsize(nblksize);
 
                chain = hammer2_chain_lookup(hmp, &parent,
                                             obase, obase,