From: Matthew Dillon Date: Sat, 23 Feb 2008 03:01:08 +0000 (+0000) Subject: HAMMER 30/many: blockmap work. X-Git-Tag: v2.0.1~1056 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/f03c9cf46513827193d1ab9b71e27e0b13566eb8?ds=sidebyside HAMMER 30/many: blockmap work. * 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. --- diff --git a/sbin/hammer/Makefile b/sbin/hammer/Makefile index 39aa20b6b1..ea3397e48a 100644 --- a/sbin/hammer/Makefile +++ b/sbin/hammer/Makefile @@ -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 index 0000000000..823a99646e --- /dev/null +++ b/sbin/hammer/blockmap.c @@ -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 + * + * 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); +} + diff --git a/sbin/hammer/cmd_show.c b/sbin/hammer/cmd_show.c index 4c25f28065..dceb480abc 100644 --- a/sbin/hammer/cmd_show.c +++ b/sbin/hammer/cmd_show.c @@ -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 + ); +} + diff --git a/sbin/hammer/hammer_util.h b/sbin/hammer/hammer_util.h index 2ef48ba674..50f53d1900 100644 --- a/sbin/hammer/hammer_util.h +++ b/sbin/hammer/hammer_util.h @@ -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 @@ -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, diff --git a/sbin/hammer/ondisk.c b/sbin/hammer/ondisk.c index f51c6e0ebf..a495468d2b 100644 --- a/sbin/hammer/ondisk.c +++ b/sbin/hammer/ondisk.c @@ -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 @@ -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); diff --git a/sbin/newfs_hammer/Makefile b/sbin/newfs_hammer/Makefile index 96063f71d3..5af1c7c690 100644 --- a/sbin/newfs_hammer/Makefile +++ b/sbin/newfs_hammer/Makefile @@ -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 diff --git a/sys/vfs/hammer/hammer.h b/sys/vfs/hammer/hammer.h index 6e3cdb8cd3..993385b782 100644 --- a/sys/vfs/hammer/hammer.h +++ b/sys/vfs/hammer/hammer.h @@ -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; }; diff --git a/sys/vfs/hammer/hammer_blockmap.c b/sys/vfs/hammer/hammer_blockmap.c index f2c82da086..2d30e52434 100644 --- a/sys/vfs/hammer/hammer_blockmap.c +++ b/sys/vfs/hammer/hammer_blockmap.c @@ -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); } diff --git a/sys/vfs/hammer/hammer_disk.h b/sys/vfs/hammer/hammer_disk.h index 4045c514ed..e4d65475ec 100644 --- a/sys/vfs/hammer/hammer_disk.h +++ b/sys/vfs/hammer/hammer_disk.h @@ -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) \ diff --git a/sys/vfs/hammer/hammer_freemap.c b/sys/vfs/hammer/hammer_freemap.c index 99a3b3e294..878f5b79b4 100644 --- a/sys/vfs/hammer/hammer_freemap.c +++ b/sys/vfs/hammer/hammer_freemap.c @@ -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); diff --git a/sys/vfs/hammer/hammer_ondisk.c b/sys/vfs/hammer/hammer_ondisk.c index 26cb8f7db0..2dff23d7e7 100644 --- a/sys/vfs/hammer/hammer_ondisk.c +++ b/sys/vfs/hammer/hammer_ondisk.c @@ -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) { diff --git a/sys/vfs/hammer/hammer_vfsops.c b/sys/vfs/hammer/hammer_vfsops.c index faed12bf17..b22b016aae 100644 --- a/sys/vfs/hammer/hammer_vfsops.c +++ b/sys/vfs/hammer/hammer_vfsops.c @@ -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 @@ -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) {