Skip to contentSkip to conversation

Improve qemu-img check output

PATCH Superseded Kevin Wolf 2 participants 6 messages Jul 2, 2010 – Jul 6, 2010 Block layer

lore
Share & tools
lore

Kevin Wolf

author
#0 lore raw

cover Improve qemu-img check output

qemu-img check produces messages that are hard to understand. Even worse is that in the end it just says something like "42 errors" with no further explanation. Recently I got bug reports from people who though that their image was corrupted, when in fact there were only a few leaked clusters after a crash.

This series tries to tell the user if it's real corruption, leaked clusters or something that went wrong during the check.

Kevin Wolf (2):
qemu-img check: Distinguish different kinds of errors qcow2/vdi: Change check to distinguish error cases

 block.c                |    9 ++-
 block.h                |   10 ++++-
 block/qcow2-refcount.c |  120 ++++++++++++++++++++++++++----------------------
 block/qcow2.c          |    4 +-
 block/qcow2.h          |    2 +-
 block/vdi.c            |   10 ++--
 block_int.h            |    7 ++-
 qemu-img.c             |   62 +++++++++++++++++++------
 8 files changed, 140 insertions(+), 84 deletions(-)
author
#1 lore raw

PATCH 1/2 qemu-img check: Distinguish different kinds of errors

People think that their images are corrupted when in fact there are just some leaked clusters. Differentiating several error cases should make the messages more comprehensible.

---
 block.c    |   10 ++++++--
 block.h    |   10 ++++++++-
 qemu-img.c |   62 +++++++++++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 63 insertions(+), 19 deletions(-)
block.c +7 -3
index dd6dd76..b0ceef0 100644
@@ -710,15 +710,19 @@ DeviceState *bdrv_get_attached(BlockDriverState *bs)
 /*
  * Run consistency checks on an image
  *
- * Returns the number of errors or -errno when an internal error occurs
+ * Returns 0 if the check could be completed (it doesn't mean that the image is
+ * free of errors) or -errno when an internal error occured. The results of the
+ * check are stored in res.
  */
-int bdrv_check(BlockDriverState *bs)
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
 {
     if (bs->drv->bdrv_check == NULL) {
         return -ENOTSUP;
     }
 
-    return bs->drv->bdrv_check(bs);
+    memset(res, 0, sizeof(*res));
+    res->corruptions = bs->drv->bdrv_check(bs);
+    return res->corruptions < 0 ? res->corruptions : 0;
 }
 
 /* commit COW file into the raw image */
block.h +9 -1
index 3d03b3e..c2a7e4c 100644
@@ -74,7 +74,6 @@ void bdrv_close(BlockDriverState *bs);
 int bdrv_attach(BlockDriverState *bs, DeviceState *qdev);
 void bdrv_detach(BlockDriverState *bs, DeviceState *qdev);
 DeviceState *bdrv_get_attached(BlockDriverState *bs);
-int bdrv_check(BlockDriverState *bs);
 int bdrv_read(BlockDriverState *bs, int64_t sector_num,
               uint8_t *buf, int nb_sectors);
 int bdrv_write(BlockDriverState *bs, int64_t sector_num,
@@ -97,6 +96,15 @@ int bdrv_change_backing_file(BlockDriverState *bs,
     const char *backing_file, const char *backing_fmt);
 void bdrv_register(BlockDriver *bdrv);
 
+
+typedef struct BdrvCheckResult {
+    int corruptions;
+    int leaks;
+    int check_errors;
+} BdrvCheckResult;
+
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
+
 /* async block I/O */
 typedef struct BlockDriverAIOCB BlockDriverAIOCB;
 typedef void BlockDriverCompletionFunc(void *opaque, int ret);
qemu-img.c +47 -15

86 lines not on this page · raw message

Signature · 1 line
1.6.6.1
#2 lore raw

Re: [Qemu-devel] [PATCH 1/2] qemu-img check: Distinguish different kinds of errors

At Fri, 2 Jul 2010 19:14:59 +0200,

Quoted 103 lines · Kevin Wolf

People think that their images are corrupted when in fact there are just some leaked clusters. Differentiating several error cases should make the messages more comprehensible.

---
 block.c    |   10 ++++++--
 block.h    |   10 ++++++++-
 qemu-img.c |   62 +++++++++++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 63 insertions(+), 19 deletions(-)
block.c +7 -3
index dd6dd76..b0ceef0 100644
@@ -710,15 +710,19 @@ DeviceState *bdrv_get_attached(BlockDriverState *bs)
 /*
  * Run consistency checks on an image
  *
- * Returns the number of errors or -errno when an internal error occurs
+ * Returns 0 if the check could be completed (it doesn't mean that the image is
+ * free of errors) or -errno when an internal error occured. The results of the
+ * check are stored in res.
  */
-int bdrv_check(BlockDriverState *bs)
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
 {
     if (bs->drv->bdrv_check == NULL) {
         return -ENOTSUP;
     }
 
-    return bs->drv->bdrv_check(bs);
+    memset(res, 0, sizeof(*res));
+    res->corruptions = bs->drv->bdrv_check(bs);
+    return res->corruptions < 0 ? res->corruptions : 0;
 }
 
 /* commit COW file into the raw image */
block.h +9 -1
index 3d03b3e..c2a7e4c 100644
@@ -74,7 +74,6 @@ void bdrv_close(BlockDriverState *bs);
 int bdrv_attach(BlockDriverState *bs, DeviceState *qdev);
 void bdrv_detach(BlockDriverState *bs, DeviceState *qdev);
 DeviceState *bdrv_get_attached(BlockDriverState *bs);
-int bdrv_check(BlockDriverState *bs);
 int bdrv_read(BlockDriverState *bs, int64_t sector_num,
               uint8_t *buf, int nb_sectors);
 int bdrv_write(BlockDriverState *bs, int64_t sector_num,
@@ -97,6 +96,15 @@ int bdrv_change_backing_file(BlockDriverState *bs,
     const char *backing_file, const char *backing_fmt);
 void bdrv_register(BlockDriver *bdrv);
 
+
+typedef struct BdrvCheckResult {
+    int corruptions;
+    int leaks;
+    int check_errors;
+} BdrvCheckResult;
+
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
+
 /* async block I/O */
 typedef struct BlockDriverAIOCB BlockDriverAIOCB;
 typedef void BlockDriverCompletionFunc(void *opaque, int ret);
qemu-img.c +10 -6
index 700af21..1782ac9 100644
@@ -425,11 +425,20 @@ out:
     return 0;
 }
 
+/*
+ * Checks an image for consistency. Exit codes:
+ *
+ * 0 - Check completed, image is good
+ * 1 - Check not completed because of internal errors
+ * 2 - Check completed, image is corrupted
+ * 3 - Check completed, image has leaked clusters, but is good otherwise
+ */
 static int img_check(int argc, char **argv)
 {
     int c, ret;
     const char *filename, *fmt;
     BlockDriverState *bs;
+    BdrvCheckResult result;
 
     fmt = NULL;
     for(;;) {
@@ -453,28 +462,51 @@ static int img_check(int argc, char **argv)
     if (!bs) {
         return 1;
     }
-    ret = bdrv_check(bs);
-    switch(ret) {
-    case 0:
-        printf("No errors were found on the image.\n");
-        break;
-    case -ENOTSUP:
+    ret = bdrv_check(bs, &result);

qemu-img.c static int img_check(int argc, char **argv) · line 469

Quoted 10 lines
qemu-img.c +3 -6
+
+    if (ret == -ENOTSUP) {
         error("This image format does not support checks");
-        break;
-    default:
-        if (ret < 0) {
-            error("An error occurred during the check");
-        } else {
-            printf("%d errors were found on the image.\n", ret);
+        return 1;

Is it okay to call bdrv_delete(bs) before return? It is necessary for the sheepdog driver to pass qemu-iotests.

Kazutaka

qemu-img.c +1 -0
@@ -466,6 +466,7 @@ static int img_check(int argc, char **argv)
 
     if (ret == -ENOTSUP) {
         error("This image format does not support checks");
+        bdrv_delete(bs);
         return 1;
     }
 
author
#3 lore raw

Re: [Qemu-devel] [PATCH 1/2] qemu-img check: Distinguish different kinds of errors

Am 06.07.2010 12:51, schrieb MORITA Kazutaka:

Quoted 39 lines

At Fri, 2 Jul 2010 19:14:59 +0200,

Quoted 23 lines · Kevin Wolf

People think that their images are corrupted when in fact there are just some leaked clusters. Differentiating several error cases should make the messages more comprehensible.

---
 block.c    |   10 ++++++--
 block.h    |   10 ++++++++-
 qemu-img.c |   62 +++++++++++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 63 insertions(+), 19 deletions(-)
(quoted diff) +1 -6
@@ -453,28 +462,51 @@ static int img_check(int argc, char **argv)
     if (!bs) {
         return 1;
     }
-    ret = bdrv_check(bs);
-    switch(ret) {
-    case 0:
-        printf("No errors were found on the image.\n");
-        break;
-    case -ENOTSUP:
+    ret = bdrv_check(bs, &result);

Review static int img_check(int argc, char **argv) · line 469

Quoted 10 lines
(quoted diff) +3 -6
+
+    if (ret == -ENOTSUP) {
         error("This image format does not support checks");
-        break;
-    default:
-        if (ret < 0) {
-            error("An error occurred during the check");
-        } else {
-            printf("%d errors were found on the image.\n", ret);
+        return 1;

Is it okay to call bdrv_delete(bs) before return? It is necessary for the sheepdog driver to pass qemu-iotests.

Quoted 1 line

Kazutaka

Yes, you're right. Thanks for catching this, I'll send a new version.

Kevin

author
#4 lore raw

PATCH v2 1/2 qemu-img check: Distinguish different kinds of errors

In reply to Kevin Wolf

People think that their images are corrupted when in fact there are just some leaked clusters. Differentiating several error cases should make the messages more comprehensible.

---

v2:
- Call bdrv_delete in -ENOTSUP case, too

 block.c    |   10 ++++++--
 block.h    |   10 ++++++++-
 qemu-img.c |   63 +++++++++++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 64 insertions(+), 19 deletions(-)
block.c +7 -3
index dd6dd76..b0ceef0 100644
@@ -710,15 +710,19 @@ DeviceState *bdrv_get_attached(BlockDriverState *bs)
 /*
  * Run consistency checks on an image
  *
- * Returns the number of errors or -errno when an internal error occurs
+ * Returns 0 if the check could be completed (it doesn't mean that the image is
+ * free of errors) or -errno when an internal error occured. The results of the
+ * check are stored in res.
  */
-int bdrv_check(BlockDriverState *bs)
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
 {
     if (bs->drv->bdrv_check == NULL) {
         return -ENOTSUP;
     }
 
-    return bs->drv->bdrv_check(bs);
+    memset(res, 0, sizeof(*res));
+    res->corruptions = bs->drv->bdrv_check(bs);
+    return res->corruptions < 0 ? res->corruptions : 0;
 }
 
 /* commit COW file into the raw image */
block.h +9 -1
index 3d03b3e..c2a7e4c 100644
@@ -74,7 +74,6 @@ void bdrv_close(BlockDriverState *bs);
 int bdrv_attach(BlockDriverState *bs, DeviceState *qdev);
 void bdrv_detach(BlockDriverState *bs, DeviceState *qdev);
 DeviceState *bdrv_get_attached(BlockDriverState *bs);
-int bdrv_check(BlockDriverState *bs);
 int bdrv_read(BlockDriverState *bs, int64_t sector_num,
               uint8_t *buf, int nb_sectors);
 int bdrv_write(BlockDriverState *bs, int64_t sector_num,
@@ -97,6 +96,15 @@ int bdrv_change_backing_file(BlockDriverState *bs,
     const char *backing_file, const char *backing_fmt);
 void bdrv_register(BlockDriver *bdrv);
 
+
+typedef struct BdrvCheckResult {
+    int corruptions;
+    int leaks;
+    int check_errors;
+} BdrvCheckResult;
+
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
+
 /* async block I/O */
 typedef struct BlockDriverAIOCB BlockDriverAIOCB;
 typedef void BlockDriverCompletionFunc(void *opaque, int ret);
qemu-img.c +48 -15

87 lines not on this page · raw message

Signature · 1 line
1.6.6.1
author
#5 lore raw

PATCH 2/2 qcow2/vdi: Change check to distinguish error cases

In reply to Kevin Wolf

This distinguishes between harmless leaks and real corruption. Hopefully users better understand what qemu-img check wants to tell them.

---
 block.c                |    3 +-
 block/qcow2-refcount.c |  120 ++++++++++++++++++++++++++----------------------
 block/qcow2.c          |    4 +-
 block/qcow2.h          |    2 +-
 block/vdi.c            |   10 ++--
 block_int.h            |    7 ++-
 6 files changed, 79 insertions(+), 67 deletions(-)
block.c +1 -2
index b0ceef0..65cf4dc 100644
@@ -721,8 +721,7 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
     }
 
     memset(res, 0, sizeof(*res));
-    res->corruptions = bs->drv->bdrv_check(bs);
-    return res->corruptions < 0 ? res->corruptions : 0;
+    return bs->drv->bdrv_check(bs, res);
 }
 
 /* commit COW file into the raw image */
block/qcow2-refcount.c +65 -55

315 lines not on this page · raw message

block/qcow2.c +2 -2
index 24651fc..f2b1b1c 100644
@@ -1146,9 +1146,9 @@ static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
 }
 
 
-static int qcow_check(BlockDriverState *bs)
+static int qcow_check(BlockDriverState *bs, BdrvCheckResult *result)
 {
-    return qcow2_check_refcounts(bs);
+    return qcow2_check_refcounts(bs, result);
 }
 
 #if 0
block/qcow2.h +1 -1
index c59b827..3ff162e 100644
@@ -185,7 +185,7 @@ void qcow2_create_refcount_update(QCowCreateState *s, int64_t offset,
 int qcow2_update_snapshot_refcount(BlockDriverState *bs,
     int64_t l1_table_offset, int l1_size, int addend);
 
-int qcow2_check_refcounts(BlockDriverState *bs);
+int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res);
 
 /* qcow2-cluster.c functions */
 int qcow2_grow_l1_table(BlockDriverState *bs, int min_size);
block/vdi.c +5 -5
index ee8cc7b..f72633c 100644
@@ -291,11 +291,10 @@ static void vdi_header_print(VdiHeader *header)
 }
 #endif
 
-static int vdi_check(BlockDriverState *bs)
+static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
 {
     /* TODO: additional checks possible. */
     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
-    int n_errors = 0;
     uint32_t blocks_allocated = 0;
     uint32_t block;
     uint32_t *bmap;
@@ -315,11 +314,12 @@ static int vdi_check(BlockDriverState *bs)
                 } else {
                     fprintf(stderr, "ERROR: block index %" PRIu32
                             " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry);
+                    res->corruptions++;
                 }
             } else {
                 fprintf(stderr, "ERROR: block index %" PRIu32
                         " too large, is %" PRIu32 "\n", block, bmap_entry);
-                n_errors++;
+                res->corruptions++;
             }
         }
     }
@@ -327,12 +327,12 @@ static int vdi_check(BlockDriverState *bs)
         fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32
                ", should be %" PRIu32 "\n",
                blocks_allocated, s->header.blocks_allocated);
-        n_errors++;
+        res->corruptions++;
     }
 
     qemu_free(bmap);
 
-    return n_errors;
+    return 0;
 }
 
 static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
block_int.h +5 -2
index a94b801..877e1e5 100644
@@ -119,8 +119,11 @@ struct BlockDriver {
     QEMUOptionParameter *create_options;
 
 
-    /* Returns number of errors in image, -errno for internal errors */
-    int (*bdrv_check)(BlockDriverState* bs);
+    /*
+     * Returns 0 for completed check, -errno for internal errors.
+     * The check results are stored in result.
+     */
+    int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result);
 
     void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
 
Signature · 1 line
1.6.6.1