HAMMER 30/many: blockmap work.
authorMatthew Dillon <dillon@dragonflybsd.org>
Sat, 23 Feb 2008 03:01:08 +0000 (03:01 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Sat, 23 Feb 2008 03:01:08 +0000 (03:01 +0000)
* Give the hammer utility the ability to decode blockmaps.  'hammer show'
  can dump the B-Tree again.

* Fix a couple of bugs in newfs_hammer's initialization of the freemap.

* Implement reallocation within the freemap (poor-man's version).

* Implement reallocation within a zone (poor-man's version).  Add a
  sysctl vfs.hammer.zone_limit to artificially reduce the size of the
  zone to force it to cycle.

12 files changed:
sbin/hammer/Makefile
sbin/hammer/blockmap.c [new file with mode: 0644]
sbin/hammer/cmd_show.c
sbin/hammer/hammer_util.h
sbin/hammer/ondisk.c
sbin/newfs_hammer/Makefile
sys/vfs/hammer/hammer.h
sys/vfs/hammer/hammer_blockmap.c
sys/vfs/hammer/hammer_disk.h
sys/vfs/hammer/hammer_freemap.c
sys/vfs/hammer/hammer_ondisk.c
sys/vfs/hammer/hammer_vfsops.c

index 39aa20b..ea3397e 100644 (file)
@@ -1,8 +1,8 @@
 #
-# $DragonFly: src/sbin/hammer/Makefile,v 1.5 2008/02/08 08:30:56 dillon Exp $
+# $DragonFly: src/sbin/hammer/Makefile,v 1.6 2008/02/23 03:01:06 dillon Exp $
 
 PROG=  hammer
-SRCS=  hammer.c ondisk.c cache.c \
+SRCS=  hammer.c ondisk.c blockmap.c cache.c \
        misc.c cmd_show.c cmd_prune.c cmd_history.c
 MAN=   hammer.8
 
diff --git a/sbin/hammer/blockmap.c b/sbin/hammer/blockmap.c
new file mode 100644 (file)
index 0000000..823a996
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * $DragonFly: src/sbin/hammer/blockmap.c,v 1.1 2008/02/23 03:01:06 dillon Exp $
+ */
+
+#include "hammer.h"
+
+hammer_off_t
+blockmap_lookup(hammer_off_t bmap_off,
+               struct hammer_blockmap_layer1 *save_layer1,
+               struct hammer_blockmap_layer2 *save_layer2)
+{
+       struct volume_info *root_volume;
+       hammer_blockmap_t rootmap;
+       struct hammer_blockmap_layer1 *layer1;
+       struct hammer_blockmap_layer2 *layer2;
+       struct buffer_info *buffer = NULL;
+       hammer_off_t layer1_offset;
+       hammer_off_t layer2_offset;
+       hammer_off_t result_offset;
+       int zone;
+
+       zone = HAMMER_ZONE_DECODE(bmap_off);
+       assert(zone >= HAMMER_ZONE_BTREE_INDEX && zone < HAMMER_MAX_ZONES);
+       assert(RootVolNo >= 0);
+       root_volume = get_volume(RootVolNo);
+       rootmap = &root_volume->ondisk->vol0_blockmap[zone];
+       assert(rootmap->phys_offset != 0);
+       assert(HAMMER_ZONE_DECODE(rootmap->phys_offset) ==
+                HAMMER_ZONE_RAW_BUFFER_INDEX);
+       assert(HAMMER_ZONE_DECODE(rootmap->alloc_offset) == zone);
+
+       if (bmap_off >= rootmap->alloc_offset) {
+               panic("hammer_blockmap_lookup: %016llx beyond EOF %016llx",
+                     bmap_off, rootmap->alloc_offset);
+               result_offset = 0;
+               goto done;
+       }
+
+       /*
+        * Dive layer 1.
+        */
+       layer1_offset = rootmap->phys_offset +
+                       HAMMER_BLOCKMAP_LAYER1_OFFSET(bmap_off);
+       layer1 = get_buffer_data(layer1_offset, &buffer, 0);
+       assert(layer1->phys_offset);
+       if (save_layer1)
+               *save_layer1 = *layer1;
+
+       /*
+        * Dive layer 2, each entry represents a large-block.
+        */
+       layer2_offset = layer1->phys_offset +
+                       HAMMER_BLOCKMAP_LAYER2_OFFSET(bmap_off);
+       layer2 = get_buffer_data(layer2_offset, &buffer, 0);
+       if (save_layer2)
+               *save_layer2 = *layer2;
+
+       assert(layer2->u.phys_offset);
+
+       result_offset = layer2->u.phys_offset +
+                       (bmap_off & HAMMER_LARGEBLOCK_MASK64);
+done:
+       if (buffer)
+               rel_buffer(buffer);
+       rel_volume(root_volume);
+       return(result_offset);
+}
+
index 4c25f28..dceb480 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/cmd_show.c,v 1.5 2008/02/08 08:30:56 dillon Exp $
+ * $DragonFly: src/sbin/hammer/cmd_show.c,v 1.6 2008/02/23 03:01:06 dillon Exp $
  */
 
 #include "hammer.h"
@@ -48,6 +48,7 @@ static void print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type,
 static int print_elm_flags(hammer_node_ondisk_t node, hammer_btree_elm_t elm,
                        u_int8_t btype, hammer_base_elm_t left_bound,
                        hammer_base_elm_t right_bound);
+static void print_bigblock_fill(hammer_off_t offset);
 
 void
 hammer_cmd_show(hammer_off_t node_offset, int depth,
@@ -83,9 +84,12 @@ print_btree_node(hammer_off_t node_offset, int depth, int spike,
 
        if (spike == 0) {
                printf("    NODE %016llx count=%d parent=%016llx "
-                      "type=%c depth=%d {\n",
+                      "type=%c depth=%d fill=",
                       node_offset, node->count, node->parent,
                       (node->type ? node->type : '?'), depth);
+               if (VerboseOpt)
+                       print_bigblock_fill(node_offset);
+               printf(" {\n");
 
                for (i = 0; i < node->count; ++i) {
                        elm = &node->elms[i];
@@ -154,7 +158,16 @@ print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type,
        case HAMMER_BTREE_TYPE_LEAF:
                switch(elm->base.btype) {
                case HAMMER_BTREE_TYPE_RECORD:
+                       printf("\n\t         ");
                        printf("recoff=%016llx", elm->leaf.rec_offset);
+                       printf(" dataoff=%016llx/%d",
+                               elm->leaf.data_offset, elm->leaf.data_len);
+                       if (VerboseOpt) {
+                               printf("\n\t         fills=");
+                               print_bigblock_fill(elm->leaf.rec_offset);
+                               printf(", ");
+                               print_bigblock_fill(elm->leaf.data_offset);
+                       }
                        break;
                }
                break;
@@ -218,3 +231,22 @@ print_elm_flags(hammer_node_ondisk_t node, hammer_btree_elm_t elm,
        return(flags);
 }
 
+static
+void
+print_bigblock_fill(hammer_off_t offset)
+{
+       struct hammer_blockmap_layer1 layer1;
+       struct hammer_blockmap_layer2 layer2;
+       int fill;
+
+       blockmap_lookup(offset, &layer1, &layer2);
+       fill = layer2.bytes_free * 100 / HAMMER_LARGEBLOCK_SIZE;
+       fill = 100 - fill;
+
+       printf("z%d:%lld=%d%%",
+               HAMMER_ZONE_DECODE(offset),
+               (offset & ~HAMMER_OFF_ZONE_MASK) / HAMMER_LARGEBLOCK_SIZE,
+               fill
+       );
+}
+
index 2ef48ba..50f53d1 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/hammer_util.h,v 1.10 2008/02/20 00:55:48 dillon Exp $
+ * $DragonFly: src/sbin/hammer/hammer_util.h,v 1.11 2008/02/23 03:01:06 dillon Exp $
  */
 
 #include <sys/types.h>
@@ -115,6 +115,9 @@ hammer_node_ondisk_t get_node(hammer_off_t node_offset,
 void rel_volume(struct volume_info *volume);
 void rel_buffer(struct buffer_info *buffer);
 
+hammer_off_t blockmap_lookup(hammer_off_t bmap_off,
+                               struct hammer_blockmap_layer1 *layer1,
+                               struct hammer_blockmap_layer2 *layer2);
 void format_blockmap(hammer_blockmap_t blockmap, hammer_off_t zone_off);
 void *alloc_btree_element(hammer_off_t *offp);
 hammer_record_ondisk_t alloc_record_element(hammer_off_t *offp,
index f51c6e0..a495468 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/hammer/ondisk.c,v 1.12 2008/02/20 00:55:48 dillon Exp $
+ * $DragonFly: src/sbin/hammer/ondisk.c,v 1.13 2008/02/23 03:01:06 dillon Exp $
  */
 
 #include <sys/types.h>
@@ -179,11 +179,15 @@ get_buffer(hammer_off_t buf_offset, int isnew)
        void *ondisk;
        struct buffer_info *buf;
        struct volume_info *volume;
-       int n;
        int vol_no;
+       int zone;
+       int n;
 
+       zone = HAMMER_ZONE_DECODE(buf_offset);
+       if (zone > HAMMER_ZONE_RAW_BUFFER_INDEX) {
+               buf_offset = blockmap_lookup(buf_offset, NULL, NULL);
+       }
        assert((buf_offset & HAMMER_OFF_ZONE_MASK) == HAMMER_ZONE_RAW_BUFFER);
-
        vol_no = HAMMER_VOL_DECODE(buf_offset);
        volume = get_volume(vol_no);
        buf_offset &= ~HAMMER_BUFMASK64;
@@ -388,24 +392,36 @@ initialize_freemap(struct volume_info *vol)
        printf("initialize freemap volume %d\n", vol->vol_no);
 
        /*
-        * Initialize the freemap.  Loop through all buffers.  Fix-up the
-        * ones which have already been allocated (should only be self
-        * bootstrap large-blocks).
+        * Initialize the freemap.  First preallocate the bigblocks required
+        * to implement layer2.   This preallocation is a bootstrap allocation
+        * using blocks from the target volume.
         */
        layer1_base = root_vol->ondisk->vol0_blockmap[
                                        HAMMER_ZONE_FREEMAP_INDEX].phys_offset;
        for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
             phys_offset < aligned_vol_free_end;
-            phys_offset += HAMMER_LARGEBLOCK_SIZE) {
+            phys_offset += HAMMER_BLOCKMAP_LAYER2) {
                layer1_offset = layer1_base +
                                HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset);
                layer1 = get_buffer_data(layer1_offset, &buffer1, 0);
-
                if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) {
-                       layer1->phys_offset = alloc_bigblock(root_vol, 0);
+                       layer1->phys_offset = alloc_bigblock(vol, 0);
                        layer1->blocks_free = 0;
                        buffer1->cache.modified = 1;
                }
+       }
+
+       /*
+        * Now fill everything in.
+        */
+       for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
+            phys_offset < aligned_vol_free_end;
+            phys_offset += HAMMER_LARGEBLOCK_SIZE) {
+               layer1_offset = layer1_base +
+                               HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset);
+               layer1 = get_buffer_data(layer1_offset, &buffer1, 0);
+
+               assert(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
                layer2_offset = layer1->phys_offset +
                                HAMMER_BLOCKMAP_LAYER2_OFFSET(phys_offset);
 
@@ -430,7 +446,7 @@ initialize_freemap(struct volume_info *vol)
                /*
                 * Finish-up layer 1
                 */
-               if (layer1_offset - layer1_base != HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset + HAMMER_LARGEBLOCK_SIZE)) {
+               if (((phys_offset + HAMMER_LARGEBLOCK_SIZE) & HAMMER_BLOCKMAP_LAYER2_MASK) == 0) {
                        layer1->layer1_crc = crc32(layer1, sizeof(*layer1));
                        buffer1->cache.modified = 1;
                }
@@ -517,7 +533,8 @@ void *
 alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp,
               struct buffer_info **bufferp)
 {
-       struct buffer_info *buffer;
+       struct buffer_info *buffer1 = NULL;
+       struct buffer_info *buffer2 = NULL;
        struct volume_info *volume;
        hammer_blockmap_t rootmap;
        struct hammer_blockmap_layer1 *layer1;
@@ -548,10 +565,9 @@ alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp,
        layer1_offset = rootmap->phys_offset +
                        HAMMER_BLOCKMAP_LAYER1_OFFSET(rootmap->alloc_offset);
 
-       layer1 = get_buffer_data(layer1_offset, bufferp, 0);
-       buffer = *bufferp;
+       layer1 = get_buffer_data(layer1_offset, &buffer1, 0);
        if ((rootmap->alloc_offset & HAMMER_BLOCKMAP_LAYER2_MASK) == 0) {
-               buffer->cache.modified = 1;
+               buffer1->cache.modified = 1;
                bzero(layer1, sizeof(*layer1));
                layer1->blocks_free = HAMMER_BLOCKMAP_RADIX2;
                layer1->phys_offset = alloc_bigblock(NULL,
@@ -564,18 +580,19 @@ alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp,
        layer2_offset = layer1->phys_offset +
                        HAMMER_BLOCKMAP_LAYER2_OFFSET(rootmap->alloc_offset);
 
-       layer2 = get_buffer_data(layer2_offset, bufferp, 0);
-       buffer = *bufferp;
+       layer2 = get_buffer_data(layer2_offset, &buffer2, 0);
 
        if ((rootmap->alloc_offset & HAMMER_LARGEBLOCK_MASK64) == 0) {
-               buffer->cache.modified = 1;
+               buffer2->cache.modified = 1;
                bzero(layer2, sizeof(*layer2));
                layer2->u.phys_offset = alloc_bigblock(NULL,
                                                       rootmap->alloc_offset);
                layer2->bytes_free = HAMMER_LARGEBLOCK_SIZE;
+               --layer1->blocks_free;
        }
 
-       buffer->cache.modified = 1;
+       buffer1->cache.modified = 1;
+       buffer2->cache.modified = 1;
        volume->cache.modified = 1;
        layer2->bytes_free -= bytes;
        *result_offp = rootmap->alloc_offset;
@@ -585,8 +602,12 @@ alloc_blockmap(int zone, int bytes, hammer_off_t *result_offp,
        bigblock_offset = layer2->u.phys_offset + 
                          (*result_offp & HAMMER_LARGEBLOCK_MASK);
        ptr = get_buffer_data(bigblock_offset, bufferp, 0);
-       buffer = *bufferp;
-       buffer->cache.modified = 1;
+       (*bufferp)->cache.modified = 1;
+
+       if (buffer1)
+               rel_buffer(buffer1);
+       if (buffer2)
+               rel_buffer(buffer2);
 
        rel_volume(volume);
        return(ptr);
index 96063f7..5af1c7c 100644 (file)
@@ -1,5 +1,5 @@
 #
-# $DragonFly: src/sbin/newfs_hammer/Makefile,v 1.4 2008/02/08 08:30:58 dillon Exp $
+# $DragonFly: src/sbin/newfs_hammer/Makefile,v 1.5 2008/02/23 03:01:07 dillon Exp $
 
 PROG=  newfs_hammer
 MAN=   newfs_hammer.8
@@ -9,6 +9,6 @@ SRCS= newfs_hammer.c
 .PATH: ${.CURDIR}/../../sys/libkern
 SRCS+= crc32.c
 .PATH: ${.CURDIR}/../hammer
-SRCS+= ondisk.c cache.c
+SRCS+= ondisk.c cache.c blockmap.c
 
 .include <bsd.prog.mk>
index 6e3cdb8..993385b 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.38 2008/02/20 00:55:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.39 2008/02/23 03:01:08 dillon Exp $
  */
 /*
  * This header file contains structures used internally by the HAMMERFS
@@ -395,6 +395,7 @@ struct hammer_mount {
        udev_t  fsid_udev;
        hammer_tid_t asof;
        u_int32_t namekey_iterator;
+       hammer_off_t zone_limits[HAMMER_MAX_ZONES];
        struct netexport export;
        struct lock blockmap_lock;
 };
index f2c82da..2d30e52 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_blockmap.c,v 1.3 2008/02/20 00:55:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_blockmap.c,v 1.4 2008/02/23 03:01:08 dillon Exp $
  */
 
 /*
@@ -49,12 +49,15 @@ hammer_blockmap_alloc(hammer_mount_t hmp, int zone, int bytes, int *errorp)
        hammer_blockmap_t rootmap;
        struct hammer_blockmap_layer1 *layer1;
        struct hammer_blockmap_layer2 *layer2;
-       hammer_buffer_t buffer = NULL;
+       hammer_buffer_t buffer1 = NULL;
+       hammer_buffer_t buffer2 = NULL;
+       hammer_buffer_t buffer3 = NULL;
        hammer_off_t tmp_offset;
-       hammer_off_t alloc_offset;
+       hammer_off_t next_offset;
        hammer_off_t layer1_offset;
        hammer_off_t layer2_offset;
        hammer_off_t bigblock_offset;
+       int loops = 0;
 
        KKASSERT(zone >= HAMMER_ZONE_BTREE_INDEX && zone < HAMMER_MAX_ZONES);
        root_volume = hammer_get_root_volume(hmp, errorp);
@@ -78,90 +81,145 @@ hammer_blockmap_alloc(hammer_mount_t hmp, int zone, int bytes, int *errorp)
        KKASSERT(rootmap->next_offset <= rootmap->alloc_offset);
 
        lockmgr(&hmp->blockmap_lock, LK_EXCLUSIVE|LK_RETRY);
-       alloc_offset = rootmap->next_offset;
-       tmp_offset = alloc_offset + bytes;
-       if ((alloc_offset ^ (tmp_offset - 1)) & ~HAMMER_BUFMASK64) {
-               alloc_offset = (tmp_offset - 1) & ~HAMMER_BUFMASK64;
-       }
+       next_offset = rootmap->next_offset;
+
+again:
+       /*
+        * The allocation request may not cross a buffer boundary
+        */
+       tmp_offset = next_offset + bytes;
+       if ((next_offset ^ (tmp_offset - 1)) & ~HAMMER_BUFMASK64)
+               next_offset = (tmp_offset - 1) & ~HAMMER_BUFMASK64;
 
        /*
         * Dive layer 1.  If we are starting a new layer 1 entry,
         * allocate a layer 2 block for it.
         */
        layer1_offset = rootmap->phys_offset +
-                       HAMMER_BLOCKMAP_LAYER1_OFFSET(alloc_offset);
-       layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer);
+                       HAMMER_BLOCKMAP_LAYER1_OFFSET(next_offset);
+       layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer1);
        KKASSERT(*errorp == 0);
 
        /*
-        * Allocate layer2 backing store if necessary
+        * Allocate layer2 backing store in layer1 if necessary
         */
-       if ((alloc_offset == rootmap->alloc_offset &&
-           (alloc_offset & HAMMER_BLOCKMAP_LAYER2_MASK) == 0) ||
+       if ((next_offset == rootmap->alloc_offset &&
+           (next_offset & HAMMER_BLOCKMAP_LAYER2_MASK) == 0) ||
            layer1->phys_offset == HAMMER_BLOCKMAP_FREE
        ) {
-               hammer_modify_buffer(buffer, layer1, sizeof(*layer1));
+               hammer_modify_buffer(buffer1, layer1, sizeof(*layer1));
                bzero(layer1, sizeof(*layer1));
-               layer1->phys_offset = hammer_freemap_alloc(hmp, alloc_offset,
+               layer1->phys_offset = hammer_freemap_alloc(hmp, next_offset,
                                                           errorp);
+               layer1->blocks_free = HAMMER_BLOCKMAP_RADIX2;
                KKASSERT(*errorp == 0);
        }
        KKASSERT(layer1->phys_offset);
 
        /*
-        * Dive layer 2, each entry represents a large-block.  If we are at
-        * the start of a new entry, allocate a large-block.
+        * If layer1 indicates no free blocks in layer2 and our alloc_offset
+        * is not in layer2, skip layer2 entirely.
         */
-       layer2_offset = layer1->phys_offset +
-                       HAMMER_BLOCKMAP_LAYER2_OFFSET(alloc_offset);
-       layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer);
-       KKASSERT(*errorp == 0);
+       if (layer1->blocks_free == 0 &&
+           ((next_offset ^ rootmap->alloc_offset) & ~HAMMER_BLOCKMAP_LAYER2_MASK) != 0) {
+               kprintf("blockmap skip1 %016llx\n", next_offset);
+               next_offset = (next_offset + HAMMER_BLOCKMAP_LAYER2_MASK) &
+                             ~HAMMER_BLOCKMAP_LAYER2_MASK;
+               if (next_offset >= hmp->zone_limits[zone]) {
+                       kprintf("blockmap wrap1\n");
+                       next_offset = HAMMER_ZONE_ENCODE(zone, 0);
+                       if (++loops == 2) {     /* XXX poor-man's */
+                               next_offset = 0;
+                               *errorp = ENOSPC;
+                               goto done;
+                       }
+               }
+               goto again;
+       }
 
        /*
-        * Allocate the bigblock in layer2 if necesasry.
+        * Dive layer 2, each entry represents a large-block.
         */
-       if ((alloc_offset == rootmap->alloc_offset &&
-           (alloc_offset & HAMMER_LARGEBLOCK_MASK64) == 0) ||
-           layer2->u.phys_offset == HAMMER_BLOCKMAP_FREE
-       ) {
-               hammer_modify_buffer(buffer, layer2, sizeof(*layer2));
-               /* XXX rootmap changed */
-               bzero(layer2, sizeof(*layer2));
-               layer2->u.phys_offset = hammer_freemap_alloc(hmp, alloc_offset,
-                                                            errorp);
-               layer2->bytes_free = HAMMER_LARGEBLOCK_SIZE;
-               KKASSERT(*errorp == 0);
+       layer2_offset = layer1->phys_offset +
+                       HAMMER_BLOCKMAP_LAYER2_OFFSET(next_offset);
+       layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer2);
+       KKASSERT(*errorp == 0);
+
+       if ((next_offset & HAMMER_LARGEBLOCK_MASK64) == 0) {
+               /*
+                * We are at the beginning of a new bigblock
+                */
+               if (next_offset == rootmap->alloc_offset ||
+                   layer2->u.phys_offset == HAMMER_BLOCKMAP_FREE) {
+                       /*
+                        * Allocate the bigblock in layer2 if diving into
+                        * uninitialized space or if the block was previously
+                        * freed.
+                        */
+                       hammer_modify_buffer(buffer1, layer1, sizeof(*layer1));
+                       KKASSERT(layer1->blocks_free);
+                       --layer1->blocks_free;
+                       hammer_modify_buffer(buffer2, layer2, sizeof(*layer2));
+                       bzero(layer2, sizeof(*layer2));
+                       layer2->u.phys_offset =
+                               hammer_freemap_alloc(hmp, next_offset, errorp);
+                       layer2->bytes_free = HAMMER_LARGEBLOCK_SIZE;
+                       KKASSERT(*errorp == 0);
+               } else if (layer2->bytes_free != HAMMER_LARGEBLOCK_SIZE) {
+                       /*
+                        * We have encountered a block that is already
+                        * partially allocated.  We must skip this block.
+                        */
+                       kprintf("blockmap skip2 %016llx\n", next_offset);
+                       next_offset += HAMMER_LARGEBLOCK_SIZE;
+                       if (next_offset >= hmp->zone_limits[zone]) {
+                               next_offset = HAMMER_ZONE_ENCODE(zone, 0);
+                               kprintf("blockmap wrap2\n");
+                               if (++loops == 2) {     /* XXX poor-man's */
+                                       next_offset = 0;
+                                       *errorp = ENOSPC;
+                                       goto done;
+                               }
+                       }
+                       goto again;
+               }
+       } else {
+               /*
+                * We are appending within a bigblock.
+                */
+               KKASSERT(layer2->u.phys_offset != HAMMER_BLOCKMAP_FREE);
        }
 
-       hammer_modify_buffer(buffer, layer2, sizeof(*layer2));
+       hammer_modify_buffer(buffer2, layer2, sizeof(*layer2));
        layer2->bytes_free -= bytes;
 
        /*
-        * Calling bnew on the buffer backing the allocation gets it into
-        * the system without a disk read.
-        *
-        * XXX This can only be done when appending into a new buffer.
+        * If the buffer was completely free we do not have to read it from
+        * disk, call hammer_bnew() to instantiate it.
         */
-       if (alloc_offset == rootmap->alloc_offset &&
-           (alloc_offset & HAMMER_BUFMASK) == 0) {
+       if ((next_offset & HAMMER_BUFMASK) == 0) {
                bigblock_offset = layer2->u.phys_offset +
-                                 (alloc_offset & HAMMER_LARGEBLOCK_MASK64);
-               hammer_bnew(hmp, bigblock_offset, errorp, &buffer);
+                                 (next_offset & HAMMER_LARGEBLOCK_MASK64);
+               hammer_bnew(hmp, bigblock_offset, errorp, &buffer3);
        }
 
        /*
         * Adjust our iterator
         */
        hammer_modify_volume(root_volume, rootmap, sizeof(*rootmap));
-       rootmap->next_offset = alloc_offset + bytes;
+       rootmap->next_offset = next_offset + bytes;
        if (rootmap->alloc_offset < rootmap->next_offset)
                rootmap->alloc_offset = rootmap->next_offset;
-
-       if (buffer)
-               hammer_rel_buffer(buffer, 0);
+done:
+       if (buffer1)
+               hammer_rel_buffer(buffer1, 0);
+       if (buffer2)
+               hammer_rel_buffer(buffer2, 0);
+       if (buffer3)
+               hammer_rel_buffer(buffer3, 0);
        hammer_rel_volume(root_volume, 0);
        lockmgr(&hmp->blockmap_lock, LK_RELEASE);
-       return(alloc_offset);
+       return(next_offset);
 }
 
 /*
@@ -174,7 +232,8 @@ hammer_blockmap_free(hammer_mount_t hmp, hammer_off_t bmap_off, int bytes)
        hammer_blockmap_t rootmap;
        struct hammer_blockmap_layer1 *layer1;
        struct hammer_blockmap_layer2 *layer2;
-       hammer_buffer_t buffer = NULL;
+       hammer_buffer_t buffer1 = NULL;
+       hammer_buffer_t buffer2 = NULL;
        hammer_off_t layer1_offset;
        hammer_off_t layer2_offset;
        int error;
@@ -187,6 +246,9 @@ hammer_blockmap_free(hammer_mount_t hmp, hammer_off_t bmap_off, int bytes)
        root_volume = hammer_get_root_volume(hmp, &error);
        if (error)
                return;
+
+       lockmgr(&hmp->blockmap_lock, LK_EXCLUSIVE|LK_RETRY);
+
        rootmap = &root_volume->ondisk->vol0_blockmap[zone];
        KKASSERT(rootmap->phys_offset != 0);
        KKASSERT(HAMMER_ZONE_DECODE(rootmap->phys_offset) ==
@@ -206,7 +268,7 @@ hammer_blockmap_free(hammer_mount_t hmp, hammer_off_t bmap_off, int bytes)
         */
        layer1_offset = rootmap->phys_offset +
                        HAMMER_BLOCKMAP_LAYER1_OFFSET(bmap_off);
-       layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer);
+       layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1);
        KKASSERT(error == 0);
        KKASSERT(layer1->phys_offset);
 
@@ -215,11 +277,11 @@ hammer_blockmap_free(hammer_mount_t hmp, hammer_off_t bmap_off, int bytes)
         */
        layer2_offset = layer1->phys_offset +
                        HAMMER_BLOCKMAP_LAYER2_OFFSET(bmap_off);
-       layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer);
+       layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2);
 
        KKASSERT(error == 0);
        KKASSERT(layer2->u.phys_offset);
-       hammer_modify_buffer(buffer, layer2, sizeof(*layer2));
+       hammer_modify_buffer(buffer2, layer2, sizeof(*layer2));
        layer2->bytes_free += bytes;
 
        /*
@@ -233,6 +295,16 @@ hammer_blockmap_free(hammer_mount_t hmp, hammer_off_t bmap_off, int bytes)
                        hammer_freemap_free(hmp, layer2->u.phys_offset,
                                            bmap_off, &error);
                        layer2->u.phys_offset = HAMMER_BLOCKMAP_FREE;
+
+                       hammer_modify_buffer(buffer1, layer1, sizeof(*layer1));
+                       ++layer1->blocks_free;
+                       if (layer1->blocks_free == HAMMER_BLOCKMAP_RADIX2) {
+                               hammer_freemap_free(
+                                       hmp, layer1->phys_offset,
+                                       bmap_off & ~HAMMER_BLOCKMAP_LAYER2_MASK,
+                                       &error);
+                               layer1->phys_offset = HAMMER_BLOCKMAP_FREE;
+                       }
                } else {
                        hammer_modify_volume(root_volume, rootmap,
                                             sizeof(*rootmap));
@@ -240,8 +312,12 @@ hammer_blockmap_free(hammer_mount_t hmp, hammer_off_t bmap_off, int bytes)
                }
        }
 done:
-       if (buffer)
-               hammer_rel_buffer(buffer, 0);
+       lockmgr(&hmp->blockmap_lock, LK_RELEASE);
+
+       if (buffer1)
+               hammer_rel_buffer(buffer1, 0);
+       if (buffer2)
+               hammer_rel_buffer(buffer2, 0);
        hammer_rel_volume(root_volume, 0);
 }
 
index 4045c51..e4d6547 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.24 2008/02/20 00:55:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.25 2008/02/23 03:01:08 dillon Exp $
  */
 
 #ifndef VFS_HAMMER_DISK_H_
@@ -146,6 +146,13 @@ typedef u_int64_t hammer_off_t;
 #define HAMMER_ZONE_LARGE_DATA_INDEX   10
 #define HAMMER_ZONE_SMALL_DATA_INDEX   11
 
+/*
+ * Per-zone size limitation.  This just makes the iterator easier
+ * to deal with by preventing an iterator overflow.
+ */
+#define HAMMER_ZONE_LIMIT              \
+       (0x1000000000000000ULL - HAMMER_BLOCKMAP_LAYER2)
+
 #define HAMMER_MAX_ZONES               16
 
 #define HAMMER_VOL_ENCODE(vol_no)                      \
@@ -154,6 +161,8 @@ typedef u_int64_t hammer_off_t;
        (int32_t)(((hammer_off_t)(ham_off) >> 52) & 255)
 #define HAMMER_ZONE_DECODE(ham_off)                    \
        (int32_t)(((hammer_off_t)(ham_off) >> 60))
+#define HAMMER_ZONE_ENCODE(zone, ham_off)              \
+       (((hammer_off_t)(zone) << 60) | (ham_off))
 #define HAMMER_SHORT_OFF_ENCODE(offset)                        \
        ((hammer_off_t)(offset) & HAMMER_OFF_SHORT_MASK)
 #define HAMMER_LONG_OFF_ENCODE(offset)                 \
index 99a3b3e..878f5b7 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_freemap.c,v 1.2 2008/02/20 00:55:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_freemap.c,v 1.3 2008/02/23 03:01:08 dillon Exp $
  */
 
 /*
@@ -84,18 +84,11 @@ new_volume:
                        if (vol_no >= hmp->nvolumes)
                                vol_no = 0;
                        result_offset = HAMMER_ENCODE_RAW_BUFFER(vol_no, 0);
-                       if (++loops == 2) {
+                       if (vol_no == 0 && ++loops == 2) {
                                *errorp = ENOSPC;
                                result_offset = 0;
                                goto done;
                        }
-               } else if (layer1->blocks_free == 0) {
-                       /*
-                        * layer2 has no free blocks, skip to the next layer.
-                        */
-                       result_offset = (result_offset + HAMMER_BLOCKMAP_LAYER2_MASK) & ~HAMMER_BLOCKMAP_LAYER2_MASK;
-                       if (HAMMER_VOL_DECODE(result_offset) != vol_no)
-                               goto new_volume;
                } else {
                        layer2_offset = layer1->phys_offset +
                                HAMMER_BLOCKMAP_LAYER2_OFFSET(result_offset);
@@ -114,9 +107,20 @@ new_volume:
                                --ondisk->vol0_stat_freebigblocks;
                                break;
                        }
-                       result_offset += HAMMER_LARGEBLOCK_SIZE;
-                       if (HAMMER_VOL_DECODE(result_offset) != vol_no)
-                               goto new_volume;
+                       if (layer1->blocks_free == 0 ||
+                           layer2->u.owner == HAMMER_BLOCKMAP_UNAVAIL) {
+                               /*
+                                * layer2 has no free blocks remaining,
+                                * skip to the next layer.
+                                */
+                               result_offset = (result_offset + HAMMER_BLOCKMAP_LAYER2_MASK) & ~HAMMER_BLOCKMAP_LAYER2_MASK;
+                               if (HAMMER_VOL_DECODE(result_offset) != vol_no)
+                                       goto new_volume;
+                       } else {
+                               result_offset += HAMMER_LARGEBLOCK_SIZE;
+                               if (HAMMER_VOL_DECODE(result_offset) != vol_no)
+                                       goto new_volume;
+                       }
                }
        }
        kprintf("hammer_freemap_alloc %016llx\n", result_offset);
index 26cb8f7..2dff23d 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_ondisk.c,v 1.30 2008/02/10 18:58:22 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_ondisk.c,v 1.31 2008/02/23 03:01:08 dillon Exp $
  */
 /*
  * Manage HAMMER's on-disk structures.  These routines are primarily
@@ -1176,10 +1176,11 @@ hammer_alloc_record(hammer_mount_t hmp,
         */
        *rec_offp = rec_offset;
        rec = hammer_bread(hmp, rec_offset, errorp, rec_bufferp);
+       hammer_modify_buffer(*rec_bufferp, NULL, 0);
+       bzero(rec, sizeof(*rec));
        KKASSERT(*errorp == 0);
        rec->base.data_off = data_offset;
        rec->base.data_len = data_len;
-       hammer_modify_buffer(*rec_bufferp, NULL, 0);
 
        if (data_bufferp) {
                if (data_len) {
index faed12b..b22b016 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_vfsops.c,v 1.20 2008/02/20 00:55:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_vfsops.c,v 1.21 2008/02/23 03:01:08 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -58,6 +58,7 @@ int hammer_count_record_datas;
 int hammer_count_volumes;
 int hammer_count_buffers;
 int hammer_count_nodes;
+int64_t hammer_zone_limit;
 
 SYSCTL_NODE(_vfs, OID_AUTO, hammer, CTLFLAG_RW, 0, "HAMMER filesystem");
 SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_general, CTLFLAG_RW,
@@ -82,6 +83,8 @@ SYSCTL_INT(_vfs_hammer, OID_AUTO, count_buffers, CTLFLAG_RD,
           &hammer_count_buffers, 0, "");
 SYSCTL_INT(_vfs_hammer, OID_AUTO, count_nodes, CTLFLAG_RD,
           &hammer_count_nodes, 0, "");
+SYSCTL_QUAD(_vfs_hammer, OID_AUTO, zone_limit, CTLFLAG_RW,
+          &hammer_zone_limit, 0, "");
 
 /*
  * VFS ABI
@@ -176,6 +179,19 @@ hammer_vfs_mount(struct mount *mp, char *mntpt, caddr_t data,
                hmp->root_btree_end.rec_type = 0xFFFFU;
                hmp->root_btree_end.obj_type = 0;
                lockinit(&hmp->blockmap_lock, "blkmap", 0, 0);
+
+               for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
+                       hmp->zone_limits[i] =
+                               HAMMER_ZONE_ENCODE(i, HAMMER_ZONE_LIMIT);
+                       /*
+                        * Sysctl override for debugging (force the zone
+                        * the cycle more quickly then every 2^60 bytes).
+                        */
+                       if (hammer_zone_limit) {
+                               hmp->zone_limits[i] =
+                                   HAMMER_ZONE_ENCODE(i, hammer_zone_limit);
+                       }
+               }
        }
        hmp->hflags = info.hflags;
        if (info.asof) {