[2/5] qcow2: record fragmentation statistics during check

Submitted by Stefan Hajnoczi on Feb. 5, 2013, 6:54 p.m.

Details

Message ID 1360090451-26543-3-git-send-email-stefanha@redhat.com
State New
Headers show

Commit Message

Stefan Hajnoczi Feb. 5, 2013, 6:54 p.m.
The qemu-img check command can display fragmentation statistics:
 * Total number of clusters in virtual disk
 * Number of allocated clusters
 * Number of fragmented clusters

This patch adds fragmentation statistics support to qcow2.

Compressed and normal clusters count as allocated.  Zero clusters are
not counted as allocated unless their L2 entry has a non-zero offset
(e.g. preallocation).

Only the current L1 table counts towards the statistics - snapshots are
ignored.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/qcow2-refcount.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

Comments

Eric Blake Feb. 5, 2013, 9:36 p.m.
On 02/05/2013 11:54 AM, Stefan Hajnoczi wrote:
> The qemu-img check command can display fragmentation statistics:
>  * Total number of clusters in virtual disk
>  * Number of allocated clusters
>  * Number of fragmented clusters
> 
> This patch adds fragmentation statistics support to qcow2.
> 
> Compressed and normal clusters count as allocated.  Zero clusters are
> not counted as allocated unless their L2 entry has a non-zero offset
> (e.g. preallocation).
> 
> Only the current L1 table counts towards the statistics - snapshots are
> ignored.
> 
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>  block/qcow2-refcount.c | 25 ++++++++++++++++++++++++-
>  1 file changed, 24 insertions(+), 1 deletion(-)

Reviewed-by: Eric Blake <eblake@redhat.com>

> @@ -963,6 +965,17 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
>              l2_entry &= s->cluster_offset_mask;
>              inc_refcounts(bs, res, refcount_table, refcount_table_size,
>                  l2_entry & ~511, nb_csectors * 512);
> +
> +            if (flags & CHECK_FRAG_INFO) {
> +                res->bfi.allocated_clusters++;
> +                if (next_contiguous_offset &&
> +                    (l2_entry & ~511) != next_contiguous_offset) {
> +                    res->bfi.fragmented_clusters++;
> +                }
> +                /* Round down again, see nb_sectors above */
> +                next_contiguous_offset = (l2_entry & ~511) +

That's a lot of repetition of (l2_entry & ~511), which feels somewhat
magic; is it worth a dedicated temporary variable better named to
explain why l2_entry is being rounded down to a 512-byte boundary?
Stefan Hajnoczi Feb. 6, 2013, 9:37 a.m.
On Tue, Feb 05, 2013 at 02:36:08PM -0700, Eric Blake wrote:
> On 02/05/2013 11:54 AM, Stefan Hajnoczi wrote:
> > @@ -963,6 +965,17 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
> >              l2_entry &= s->cluster_offset_mask;
> >              inc_refcounts(bs, res, refcount_table, refcount_table_size,
> >                  l2_entry & ~511, nb_csectors * 512);
> > +
> > +            if (flags & CHECK_FRAG_INFO) {
> > +                res->bfi.allocated_clusters++;
> > +                if (next_contiguous_offset &&
> > +                    (l2_entry & ~511) != next_contiguous_offset) {
> > +                    res->bfi.fragmented_clusters++;
> > +                }
> > +                /* Round down again, see nb_sectors above */
> > +                next_contiguous_offset = (l2_entry & ~511) +
> 
> That's a lot of repetition of (l2_entry & ~511), which feels somewhat
> magic; is it worth a dedicated temporary variable better named to
> explain why l2_entry is being rounded down to a 512-byte boundary?

You are right.  The rounding down is refers to nb_csectors, not
l2_entry.  I have added a comment to explain the reason.

Stefan

Patch hide | download patch | download mbox

diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index e2509ab..3d85e99 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -917,6 +917,7 @@  static void inc_refcounts(BlockDriverState *bs,
 /* Flags for check_refcounts_l1() and check_refcounts_l2() */
 enum {
     CHECK_OFLAG_COPIED = 0x1,   /* check QCOW_OFLAG_COPIED matches refcount */
+    CHECK_FRAG_INFO = 0x2,      /* update BlockFragInfo counters */
 };
 
 /*
@@ -933,6 +934,7 @@  static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
 {
     BDRVQcowState *s = bs->opaque;
     uint64_t *l2_table, l2_entry;
+    uint64_t next_contiguous_offset = 0;
     int i, l2_size, nb_csectors, refcount;
 
     /* Read L2 table from disk */
@@ -963,6 +965,17 @@  static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
             l2_entry &= s->cluster_offset_mask;
             inc_refcounts(bs, res, refcount_table, refcount_table_size,
                 l2_entry & ~511, nb_csectors * 512);
+
+            if (flags & CHECK_FRAG_INFO) {
+                res->bfi.allocated_clusters++;
+                if (next_contiguous_offset &&
+                    (l2_entry & ~511) != next_contiguous_offset) {
+                    res->bfi.fragmented_clusters++;
+                }
+                /* Round down again, see nb_sectors above */
+                next_contiguous_offset = (l2_entry & ~511) +
+                                         (nb_csectors - 1) * 512;
+            }
             break;
 
         case QCOW2_CLUSTER_ZERO:
@@ -990,6 +1003,15 @@  static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                 }
             }
 
+            if (flags & CHECK_FRAG_INFO) {
+                res->bfi.allocated_clusters++;
+                if (next_contiguous_offset &&
+                    offset != next_contiguous_offset) {
+                    res->bfi.fragmented_clusters++;
+                }
+                next_contiguous_offset = offset + s->cluster_size;
+            }
+
             /* Mark cluster as used */
             inc_refcounts(bs, res, refcount_table,refcount_table_size,
                 offset, s->cluster_size);
@@ -1125,6 +1147,7 @@  int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
 
     size = bdrv_getlength(bs->file);
     nb_clusters = size_to_clusters(s, size);
+    res->bfi.total_clusters = nb_clusters;
     refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t));
 
     /* header */
@@ -1134,7 +1157,7 @@  int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
     /* current L1 table */
     ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
                        s->l1_table_offset, s->l1_size,
-                       CHECK_OFLAG_COPIED);
+                       CHECK_OFLAG_COPIED | CHECK_FRAG_INFO);
     if (ret < 0) {
         goto fail;
     }