qcow2: Metadata preallocation
This introduces a qemu-img create option for qcow2 which allows the metadata to be preallocated, i.e. clusters are reserved in the refcount table and L1/L2 tables, but no data is written to them. Metadata is quite small, so this happens in almost no time. Especially with qcow2 on virtio this helps to gain a bit of performance during the initial writes. However, as soon as create a snapshot, we're back to the normal slow speed, obviously. So this isn't the real fix, but kind of a cheat while we're still having trouble with qcow2 on virtio. Note that the option is disabled by default and needs to be specified explicitly using qemu-img create -f qcow2 -o preallocation=metadata. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
		
							parent
							
								
									e8935eefe5
								
							
						
					
					
						commit
						a35e1c177d
					
				| 
						 | 
				
			
			@ -638,9 +638,54 @@ static int get_bits_from_size(size_t size)
 | 
			
		|||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int preallocate(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcowState *s = bs->opaque;
 | 
			
		||||
    uint64_t cluster_offset;
 | 
			
		||||
    uint64_t nb_sectors;
 | 
			
		||||
    uint64_t offset;
 | 
			
		||||
    int num;
 | 
			
		||||
    QCowL2Meta meta;
 | 
			
		||||
 | 
			
		||||
    nb_sectors = bdrv_getlength(bs) >> 9;
 | 
			
		||||
    offset = 0;
 | 
			
		||||
 | 
			
		||||
    while (nb_sectors) {
 | 
			
		||||
        num = MIN(nb_sectors, INT_MAX >> 9);
 | 
			
		||||
        cluster_offset = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num,
 | 
			
		||||
            &meta);
 | 
			
		||||
 | 
			
		||||
        if (cluster_offset == 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (qcow2_alloc_cluster_link_l2(bs, cluster_offset, &meta) < 0) {
 | 
			
		||||
            qcow2_free_any_clusters(bs, cluster_offset, meta.nb_clusters);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* TODO Preallocate data if requested */
 | 
			
		||||
 | 
			
		||||
        nb_sectors -= num;
 | 
			
		||||
        offset += num << 9;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * It is expected that the image file is large enough to actually contain
 | 
			
		||||
     * all of the allocated clusters (otherwise we get failing reads after
 | 
			
		||||
     * EOF). Extend the image to the last allocated sector.
 | 
			
		||||
     */
 | 
			
		||||
    if (cluster_offset != 0) {
 | 
			
		||||
        bdrv_truncate(s->hd, cluster_offset + (num <<  9));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qcow_create2(const char *filename, int64_t total_size,
 | 
			
		||||
                        const char *backing_file, const char *backing_format,
 | 
			
		||||
                        int flags, size_t cluster_size)
 | 
			
		||||
                        int flags, size_t cluster_size, int prealloc)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits;
 | 
			
		||||
| 
						 | 
				
			
			@ -762,6 +807,16 @@ static int qcow_create2(const char *filename, int64_t total_size,
 | 
			
		|||
    qemu_free(s->refcount_table);
 | 
			
		||||
    qemu_free(s->refcount_block);
 | 
			
		||||
    close(fd);
 | 
			
		||||
 | 
			
		||||
    /* Preallocate metadata */
 | 
			
		||||
    if (prealloc) {
 | 
			
		||||
        BlockDriverState *bs;
 | 
			
		||||
        bs = bdrv_new("");
 | 
			
		||||
        bdrv_open(bs, filename, BDRV_O_CACHE_WB);
 | 
			
		||||
        preallocate(bs);
 | 
			
		||||
        bdrv_close(bs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -772,6 +827,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		|||
    uint64_t sectors = 0;
 | 
			
		||||
    int flags = 0;
 | 
			
		||||
    size_t cluster_size = 65536;
 | 
			
		||||
    int prealloc = 0;
 | 
			
		||||
 | 
			
		||||
    /* Read out options */
 | 
			
		||||
    while (options && options->name) {
 | 
			
		||||
| 
						 | 
				
			
			@ -787,12 +843,28 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
 | 
			
		|||
            if (options->value.n) {
 | 
			
		||||
                cluster_size = options->value.n;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
 | 
			
		||||
            if (!options->value.s || !strcmp(options->value.s, "off")) {
 | 
			
		||||
                prealloc = 0;
 | 
			
		||||
            } else if (!strcmp(options->value.s, "metadata")) {
 | 
			
		||||
                prealloc = 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                fprintf(stderr, "Invalid preallocation mode: '%s'\n",
 | 
			
		||||
                    options->value.s);
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        options++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (backing_file && prealloc) {
 | 
			
		||||
        fprintf(stderr, "Backing file and preallocation cannot be used at "
 | 
			
		||||
            "the same time\n");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return qcow_create2(filename, sectors, backing_file, backing_fmt, flags,
 | 
			
		||||
        cluster_size);
 | 
			
		||||
        cluster_size, prealloc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qcow_make_empty(BlockDriverState *bs)
 | 
			
		||||
| 
						 | 
				
			
			@ -982,6 +1054,11 @@ static QEMUOptionParameter qcow_create_options[] = {
 | 
			
		|||
        .type = OPT_SIZE,
 | 
			
		||||
        .help = "qcow2 cluster size"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .name = BLOCK_OPT_PREALLOC,
 | 
			
		||||
        .type = OPT_STRING,
 | 
			
		||||
        .help = "Preallocation mode (allowed values: off, metadata)"
 | 
			
		||||
    },
 | 
			
		||||
    { NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@
 | 
			
		|||
#define BLOCK_OPT_BACKING_FILE  "backing_file"
 | 
			
		||||
#define BLOCK_OPT_BACKING_FMT   "backing_fmt"
 | 
			
		||||
#define BLOCK_OPT_CLUSTER_SIZE  "cluster_size"
 | 
			
		||||
#define BLOCK_OPT_PREALLOC      "preallocation"
 | 
			
		||||
 | 
			
		||||
typedef struct AIOPool {
 | 
			
		||||
    void (*cancel)(BlockDriverAIOCB *acb);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue