diff mbox

[RFC,v4,4/4] qcow2: Add full image preallocation option

Message ID 25565d3a53f29829dc2d97480cb19ba34be49895.1388112645.git.hutao@cn.fujitsu.com
State New
Headers show

Commit Message

Hu Tao Dec. 27, 2013, 3:05 a.m. UTC
This adds a preallocation=full mode to qcow2 image creation, which
creates a non-sparse image file.

Signed-off-by: Hu Tao <hutao@cn.fujitsu.com>
---
 block/qcow2.c | 48 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 45 insertions(+), 3 deletions(-)

Comments

Stefan Hajnoczi Jan. 17, 2014, 8:48 a.m. UTC | #1
On Fri, Dec 27, 2013 at 11:05:54AM +0800, Hu Tao wrote:

This approach seems okay but the calculation isn't quite right yet.

On Windows an error would be raised since we don't have preallocate=full
support.  That's okay.

> @@ -1477,16 +1478,53 @@ static int qcow2_create2(const char *filename, int64_t total_size,
>      Error *local_err = NULL;
>      int ret;
>  
> +    if (prealloc == PREALLOC_MODE_FULL) {
> +        int64_t meta_size = 0;
> +        unsigned nrefe, nl2e;
> +        BlockDriver *drv;
> +
> +        drv = bdrv_find_protocol(filename, true);
> +        if (drv == NULL) {
> +            error_setg(errp, "Could not find protocol for file '%s'", filename);
> +            return -ENOENT;
> +        }
> +
> +        alloc_options = append_option_parameters(alloc_options,
> +                                                 drv->create_options);
> +        alloc_options = append_option_parameters(alloc_options, options);
> +
> +        /* header: 1 cluster */
> +        meta_size += cluster_size;
> +        /* total size of refblocks */
> +        nrefe = (total_size / cluster_size);
> +        nrefe = align_offset(nrefe, cluster_size / sizeof(uint16_t));
> +        meta_size += nrefe * sizeof(uint16_t);

Every host cluster is reference-counted, including metadata (even
refcount blocks are recursively included).  This calculation is wrong
because it only considers data clusters.

> +        /* total size of reftables */
> +        meta_size += nrefe * sizeof(uint16_t) * sizeof(uint16_t) / cluster_size;

I don't understand this calculation.  The refcount table consists of
contiguous clusters where each element is a 64-bit offset to a refcount
block.

> +        /* total size of L2 tables */
> +        nl2e = total_size / cluster_size;
> +        nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
> +        meta_size += nl2e * sizeof(uint64_t);
> +        /* total size of L1 tables */
> +        meta_size += nl2e * sizeof(uint64_t) * sizeof(uint64_t) / cluster_size;

Another strange calculation.  The L1 table consists of contiguous
clusters where each element is a 64-bit offset to an L1 table.
Hu Tao Jan. 20, 2014, 2:16 a.m. UTC | #2
Stefan, 

On Fri, Jan 17, 2014 at 04:48:16PM +0800, Stefan Hajnoczi wrote:
> On Fri, Dec 27, 2013 at 11:05:54AM +0800, Hu Tao wrote:
> 
> This approach seems okay but the calculation isn't quite right yet.
> 
> On Windows an error would be raised since we don't have preallocate=full
> support.  That's okay.
> 
> > @@ -1477,16 +1478,53 @@ static int qcow2_create2(const char *filename, int64_t total_size,
> >      Error *local_err = NULL;
> >      int ret;
> >  
> > +    if (prealloc == PREALLOC_MODE_FULL) {
> > +        int64_t meta_size = 0;
> > +        unsigned nrefe, nl2e;
> > +        BlockDriver *drv;
> > +
> > +        drv = bdrv_find_protocol(filename, true);
> > +        if (drv == NULL) {
> > +            error_setg(errp, "Could not find protocol for file '%s'", filename);
> > +            return -ENOENT;
> > +        }
> > +
> > +        alloc_options = append_option_parameters(alloc_options,
> > +                                                 drv->create_options);
> > +        alloc_options = append_option_parameters(alloc_options, options);
> > +
> > +        /* header: 1 cluster */
> > +        meta_size += cluster_size;
> > +        /* total size of refblocks */
> > +        nrefe = (total_size / cluster_size);
> > +        nrefe = align_offset(nrefe, cluster_size / sizeof(uint16_t));
> > +        meta_size += nrefe * sizeof(uint16_t);
> 
> Every host cluster is reference-counted, including metadata (even
> refcount blocks are recursively included).  This calculation is wrong
> because it only considers data clusters.

Right. I'll fix this.

> 
> > +        /* total size of reftables */
> > +        meta_size += nrefe * sizeof(uint16_t) * sizeof(uint16_t) / cluster_size;
> 
> I don't understand this calculation.  The refcount table consists of
> contiguous clusters where each element is a 64-bit offset to a refcount
> block.
> 
> > +        /* total size of L2 tables */
> > +        nl2e = total_size / cluster_size;
> > +        nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
> > +        meta_size += nl2e * sizeof(uint64_t);
> > +        /* total size of L1 tables */
> > +        meta_size += nl2e * sizeof(uint64_t) * sizeof(uint64_t) / cluster_size;
> 
> Another strange calculation.  The L1 table consists of contiguous
> clusters where each element is a 64-bit offset to an L1 table.

I guest you mean "...offset to an L2 table" here.

Given the number of total L2 table entries nl2e, the number of L2 tables
is:

  nl2table = nl2e * sizeof(l2e) / cluster_size

because each L1 table entry points to a L2 table, so this is also the
number of L1 table entries. So the total size of L1 tables is:

  l1table_size = nl2table * sizeof(l1e)
               = nl2e * sizeof(l2e) * sizeof(l1e) / cluster_size
               = nl2e * sizeof(uint64_t) * sizeof(uint64_t) / cluster_size

I still can't see what's wrong here. But you're welcome to fix me.


The caculation of reftable size above is wrong because of the two problem
you've pointed out. Thanks!
Hu Tao Feb. 7, 2014, 2:22 a.m. UTC | #3
On Mon, Jan 20, 2014 at 10:16:23AM +0800, Hu Tao wrote:
> Stefan, 
> 
> On Fri, Jan 17, 2014 at 04:48:16PM +0800, Stefan Hajnoczi wrote:
> > On Fri, Dec 27, 2013 at 11:05:54AM +0800, Hu Tao wrote:
> > 
> > This approach seems okay but the calculation isn't quite right yet.
> > 
> > On Windows an error would be raised since we don't have preallocate=full
> > support.  That's okay.
> > 
> > > @@ -1477,16 +1478,53 @@ static int qcow2_create2(const char *filename, int64_t total_size,
> > >      Error *local_err = NULL;
> > >      int ret;
> > >  
> > > +    if (prealloc == PREALLOC_MODE_FULL) {
> > > +        int64_t meta_size = 0;
> > > +        unsigned nrefe, nl2e;
> > > +        BlockDriver *drv;
> > > +
> > > +        drv = bdrv_find_protocol(filename, true);
> > > +        if (drv == NULL) {
> > > +            error_setg(errp, "Could not find protocol for file '%s'", filename);
> > > +            return -ENOENT;
> > > +        }
> > > +
> > > +        alloc_options = append_option_parameters(alloc_options,
> > > +                                                 drv->create_options);
> > > +        alloc_options = append_option_parameters(alloc_options, options);
> > > +
> > > +        /* header: 1 cluster */
> > > +        meta_size += cluster_size;
> > > +        /* total size of refblocks */
> > > +        nrefe = (total_size / cluster_size);
> > > +        nrefe = align_offset(nrefe, cluster_size / sizeof(uint16_t));
> > > +        meta_size += nrefe * sizeof(uint16_t);
> > 
> > Every host cluster is reference-counted, including metadata (even
> > refcount blocks are recursively included).  This calculation is wrong
> > because it only considers data clusters.
> 
> Right. I'll fix this.
> 
> > 
> > > +        /* total size of reftables */
> > > +        meta_size += nrefe * sizeof(uint16_t) * sizeof(uint16_t) / cluster_size;
> > 
> > I don't understand this calculation.  The refcount table consists of
> > contiguous clusters where each element is a 64-bit offset to a refcount
> > block.
> > 
> > > +        /* total size of L2 tables */
> > > +        nl2e = total_size / cluster_size;
> > > +        nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
> > > +        meta_size += nl2e * sizeof(uint64_t);
> > > +        /* total size of L1 tables */
> > > +        meta_size += nl2e * sizeof(uint64_t) * sizeof(uint64_t) / cluster_size;
> > 
> > Another strange calculation.  The L1 table consists of contiguous
> > clusters where each element is a 64-bit offset to an L1 table.
> 
> I guest you mean "...offset to an L2 table" here.
> 
> Given the number of total L2 table entries nl2e, the number of L2 tables
> is:
> 
>   nl2table = nl2e * sizeof(l2e) / cluster_size
> 
> because each L1 table entry points to a L2 table, so this is also the
> number of L1 table entries. So the total size of L1 tables is:
> 
>   l1table_size = nl2table * sizeof(l1e)
>                = nl2e * sizeof(l2e) * sizeof(l1e) / cluster_size
>                = nl2e * sizeof(uint64_t) * sizeof(uint64_t) / cluster_size
> 
> I still can't see what's wrong here. But you're welcome to fix me.

Hi,

Any comments?
diff mbox

Patch

diff --git a/block/qcow2.c b/block/qcow2.c
index f0a8d87..f601089 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1448,6 +1448,7 @@  static int qcow2_create2(const char *filename, int64_t total_size,
                          QEMUOptionParameter *options, int version,
                          Error **errp)
 {
+    QEMUOptionParameter *alloc_options = NULL;
     /* Calculate cluster_bits */
     int cluster_bits;
     cluster_bits = ffs(cluster_size) - 1;
@@ -1477,16 +1478,53 @@  static int qcow2_create2(const char *filename, int64_t total_size,
     Error *local_err = NULL;
     int ret;
 
+    if (prealloc == PREALLOC_MODE_FULL) {
+        int64_t meta_size = 0;
+        unsigned nrefe, nl2e;
+        BlockDriver *drv;
+
+        drv = bdrv_find_protocol(filename, true);
+        if (drv == NULL) {
+            error_setg(errp, "Could not find protocol for file '%s'", filename);
+            return -ENOENT;
+        }
+
+        alloc_options = append_option_parameters(alloc_options,
+                                                 drv->create_options);
+        alloc_options = append_option_parameters(alloc_options, options);
+
+        /* header: 1 cluster */
+        meta_size += cluster_size;
+        /* total size of refblocks */
+        nrefe = (total_size / cluster_size);
+        nrefe = align_offset(nrefe, cluster_size / sizeof(uint16_t));
+        meta_size += nrefe * sizeof(uint16_t);
+        /* total size of reftables */
+        meta_size += nrefe * sizeof(uint16_t) * sizeof(uint16_t) / cluster_size;
+        /* total size of L2 tables */
+        nl2e = total_size / cluster_size;
+        nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
+        meta_size += nl2e * sizeof(uint64_t);
+        /* total size of L1 tables */
+        meta_size += nl2e * sizeof(uint64_t) * sizeof(uint64_t) / cluster_size;
+
+        set_option_parameter_int(alloc_options, BLOCK_OPT_SIZE,
+                                 total_size + meta_size);
+        set_option_parameter(alloc_options, BLOCK_OPT_PREALLOC, "full");
+
+        options = alloc_options;
+    }
+
     ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
-        return ret;
+        goto out_options;
     }
 
     ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
-        return ret;
+        goto out_options;
     }
 
     /* Write the header */
@@ -1603,6 +1641,8 @@  static int qcow2_create2(const char *filename, int64_t total_size,
     ret = 0;
 out:
     bdrv_unref(bs);
+out_options:
+    free_option_parameters(alloc_options);
     return ret;
 }
 
@@ -1638,6 +1678,8 @@  static int qcow2_create(const char *filename, QEMUOptionParameter *options,
                 prealloc = PREALLOC_MODE_OFF;
             } else if (!strcmp(options->value.s, "metadata")) {
                 prealloc = PREALLOC_MODE_METADATA;
+            } else if (!strcmp(options->value.s, "full")) {
+                prealloc = PREALLOC_MODE_FULL;
             } else {
                 error_setg(errp, "Invalid preallocation mode: '%s'",
                            options->value.s);
@@ -2223,7 +2265,7 @@  static QEMUOptionParameter qcow2_create_options[] = {
     {
         .name = BLOCK_OPT_PREALLOC,
         .type = OPT_STRING,
-        .help = "Preallocation mode (allowed values: off, metadata)"
+        .help = "Preallocation mode (allowed values: off, metadata, full)"
     },
     {
         .name = BLOCK_OPT_LAZY_REFCOUNTS,