Skip to contentSkip to conversation

qemu-img: add bitmap queries

RFC PATCH In review John Snow 4 participants 37 messages May 12, 2018 – May 17, 2018 Block layer

lore
Share & tools
lore

John Snow

author
#0 lore raw

cover qemu-img: add bitmap queries

Allow qemu-img to show information about bitmaps stored in qcow2 images. Add a 'bitmap' meta-command with 'dump' sub-command to retrieve a list of dirty regions in bitmaps stored in a qcow2 image.

RFC:
- I am not 1000% convinced the bm_list caching is perfectly safe, especially with respect to migration and inactivation. There are more efficiencies and tweaks I can make, but I think this is the minimal set.

- I decided not to gate bitmap info in the "info" command behind extra flags.

- Bitmap data-gathering in "bitmap dump" could be made more space-efficient by just reporting one segment at a time, probably.

- bitmap rm and bitmap mk need extra work, so I am not submitting them just yet: rm needs more work around the remove persistence API, and make needs more work around the "can add" API.

- None of these commands will work with "in use" bitmaps; we need qemu-img check -r bitmaps support for this. I'm not sure what the right behavior to "fix" in-use bitmaps should be:
- Cleared: This might be dangerous.
- Fully Set: This is safer, but stupid.
- Deleted: This might be the best option.
- Un-set in-use: VERY dangerous; would rather not.

John Snow (12):
qcow2-bitmap: cache bm_list
qcow2/dirty-bitmap: cache loaded bitmaps block/qcow2-bitmap: avoid adjusting bm->flags for RO bitmaps qcow2/dirty-bitmaps: load IN_USE bitmaps if disk is RO
qcow2-bitmap: track bitmap type
qapi: add bitmap info
qcow2-bitmap: add basic bitmaps info
qjson: allow caller to ask for arbitrary indent qapi/block-core: add BitmapMapping and BitmapEntry structs
qemu-img: split off common chunk of map command
qemu-img: add bitmap dump
qemu-img: add bitmap clear

 block/qcow2-bitmap.c     | 220 +++++++++++++++++--------
 block/qcow2.c            |   9 +
 block/qcow2.h            |   3 +
 include/qapi/qmp/qjson.h |   1 +
 qapi/block-core.json     |  92 ++++++++++-
 qemu-img-cmds.hx         |   6 +
 qemu-img.c               | 419 +++++++++++++++++++++++++++++++++++++----------
 qobject/qjson.c          |  21 +--
 8 files changed, 612 insertions(+), 159 deletions(-)
Signature · 1 line
2.14.3
author
#1 lore raw

RFC PATCH 1/12 qcow2-bitmap: cache bm_list

We don't need to re-read this list every time, exactly. We can keep it cached and delete our copy when we flush to disk.

Because we don't try to flush bitmaps on close if there's nothing to flush, add a new conditional to delete the state anyway for a clean exit.

---
 block/qcow2-bitmap.c | 74 ++++++++++++++++++++++++++++++++--------------------
 block/qcow2.c        |  2 ++
 block/qcow2.h        |  2 ++
 3 files changed, 50 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +46 -28
index 6e93ec43e1..fb0a4f3ec4 100644
@@ -536,8 +536,7 @@ static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list)
  * Get bitmap list from qcow2 image. Actually reads bitmap directory,
  * checks it and convert to bitmap list.
  */
-static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
-                                         uint64_t size, Error **errp)
+static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, Error **errp)
 {
     int ret;
     BDRVQcow2State *s = bs->opaque;
@@ -545,6 +544,8 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
     Qcow2BitmapDirEntry *e;
     uint32_t nb_dir_entries = 0;
     Qcow2BitmapList *bm_list = NULL;
+    uint64_t offset = s->bitmap_directory_offset;
+    uint64_t size = s->bitmap_directory_size;
 
     if (size == 0) {
         error_setg(errp, "Requested bitmap directory size is zero");
@@ -636,6 +637,30 @@ fail:
     return NULL;
 }
 
+static Qcow2BitmapList *get_bitmap_list(BlockDriverState *bs, Error **errp)
+{
+    BDRVQcow2State *s = bs->opaque;
+    Qcow2BitmapList *bm_list;
+
+    if (s->bitmap_list) {
+        return (Qcow2BitmapList *)s->bitmap_list;
+    }
+
+    bm_list = bitmap_list_load(bs, errp);
+    s->bitmap_list = bm_list;
+    return bm_list;
+}
+
+static void del_bitmap_list(BlockDriverState *bs)
+{
+    BDRVQcow2State *s = bs->opaque;
+
+    if (s->bitmap_list) {
+        bitmap_list_free(s->bitmap_list);
+        s->bitmap_list = NULL;
+    }
+}
+
 int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                                   void **refcount_table,
                                   int64_t *refcount_table_size)
@@ -656,8 +681,7 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
         return ret;
     }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, NULL);
+    bm_list = get_bitmap_list(bs, NULL);
     if (bm_list == NULL) {
         res->corruptions++;
         return -EINVAL;
@@ -707,8 +731,6 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
     }
 
 out:
-    bitmap_list_free(bm_list);
-
     return ret;
 }
 
@@ -953,8 +975,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
         return false;
     }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
     if (bm_list == NULL) {
         return false;
     }
@@ -992,14 +1013,12 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
     }
 
     g_slist_free(created_dirty_bitmaps);
-    bitmap_list_free(bm_list);
-
     return header_updated;
 
 fail:
     g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs);
     g_slist_free(created_dirty_bitmaps);
-    bitmap_list_free(bm_list);
+    del_bitmap_list(bs);
 
     return false;
 }
@@ -1027,8 +1046,7 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
         return -EINVAL;
     }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
     if (bm_list == NULL) {
         return -EINVAL;
     }
@@ -1068,7 +1086,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 
 out:
     g_slist_free(ro_dirty_bitmaps);
-    bitmap_list_free(bm_list);
 
     return ret;
 }
@@ -1277,8 +1294,7 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
         return;
     }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
     if (bm_list == NULL) {
         return;
     }
@@ -1300,7 +1316,11 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
 
 fail:
     bitmap_free(bm);
-    bitmap_list_free(bm_list);
+}
+
+void qcow2_persistent_dirty_bitmaps_cache_destroy(BlockDriverState *bs)
+{
+    del_bitmap_list(bs);
 }
 
 void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
@@ -1317,12 +1337,12 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 
     if (!bdrv_has_changed_persistent_bitmaps(bs)) {
         /* nothing to do */
-        return;
+        goto out;
     }
 
     if (!can_write(bs)) {
         error_setg(errp, "No write access");
-        return;
+        goto out;
     }
 
     QSIMPLEQ_INIT(&drop_tables);
@@ -1330,10 +1350,9 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
     if (s->nb_bitmaps == 0) {
         bm_list = bitmap_list_new();
     } else {
-        bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                                   s->bitmap_directory_size, errp);
+        bm_list = get_bitmap_list(bs, errp);
         if (bm_list == NULL) {
-            return;
+            goto out;
         }
     }
 
@@ -1414,8 +1433,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
         g_free(tb);
     }
 
-    bitmap_list_free(bm_list);
-    return;
+    goto out;
 
 fail:
     QSIMPLEQ_FOREACH(bm, bm_list, entry) {
@@ -1430,7 +1448,9 @@ fail:
         g_free(tb);
     }
 
-    bitmap_list_free(bm_list);
+ out:
+    del_bitmap_list(bs);
+    return;
 }
 
 int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
@@ -1495,14 +1515,12 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
         goto fail;
     }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
     if (bm_list == NULL) {
         goto fail;
     }
 
     found = find_bitmap_by_name(bm_list, name);
-    bitmap_list_free(bm_list);
     if (found) {
         error_setg(errp, "Bitmap with the same name is already stored");
         goto fail;
block/qcow2.c +2 -0
index 2f36e632f9..7ae9000656 100644
@@ -2135,6 +2135,8 @@ static void qcow2_close(BlockDriverState *bs)
 
     if (!(s->flags & BDRV_O_INACTIVE)) {
         qcow2_inactivate(bs);
+    } else {
+        qcow2_persistent_dirty_bitmaps_cache_destroy(bs);
     }
 
     cache_clean_timer_del(bs);
block/qcow2.h +2 -0
index adf5c3950f..796a8c914b 100644
@@ -299,6 +299,7 @@ typedef struct BDRVQcow2State {
     uint64_t bitmap_directory_size;
     uint64_t bitmap_directory_offset;
     bool dirty_bitmaps_loaded;
+    void *bitmap_list;
 
     int flags;
     int qcow_version;
@@ -675,6 +676,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
 int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
                                  Error **errp);
 int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
+void qcow2_persistent_dirty_bitmaps_cache_destroy(BlockDriverState *bs);
 void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
 int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
 bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
Signature · 1 line
2.14.3
#2 lore raw

Re: [Qemu-devel] [RFC PATCH 01/12] qcow2-bitmap: cache bm_list

Quoted 2 lines · John Snow

We don't need to re-read this list every time, exactly. We can keep it cached and delete our copy when we flush to disk.

Why not simply delete cache only on close (unconditionally)? Why do we need to remove it after flush?

Actually, I think we need to remove it only in qcow2_inactive, after storing persistent bitmaps.

Quoted 26 lines

Because we don't try to flush bitmaps on close if there's nothing to flush, add a new conditional to delete the state anyway for a clean exit.

---
  block/qcow2-bitmap.c | 74 ++++++++++++++++++++++++++++++++--------------------
  block/qcow2.c        |  2 ++
  block/qcow2.h        |  2 ++
  3 files changed, 50 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +1 -2
index 6e93ec43e1..fb0a4f3ec4 100644
@@ -536,8 +536,7 @@ static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list)
   * Get bitmap list from qcow2 image. Actually reads bitmap directory,
   * checks it and convert to bitmap list.
   */
-static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
-                                         uint64_t size, Error **errp)
+static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, Error **errp)
  {
      int ret;
      BDRVQcow2State *s = bs->opaque;

block/qcow2-bitmap.c static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset, · line 548

Quoted 5 lines
block/qcow2-bitmap.c +2 -0
@@ -545,6 +544,8 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
      Qcow2BitmapDirEntry *e;
      uint32_t nb_dir_entries = 0;
      Qcow2BitmapList *bm_list = NULL;
+    uint64_t offset = s->bitmap_directory_offset;
+    uint64_t size = s->bitmap_directory_size;

Worth split this change as a refactoring patch (just remove extra parameters)?

Quoted 218 lines
block/qcow2-bitmap.c +43 -26
      if (size == 0) {
          error_setg(errp, "Requested bitmap directory size is zero");
@@ -636,6 +637,30 @@ fail:
      return NULL;
  }
 
+static Qcow2BitmapList *get_bitmap_list(BlockDriverState *bs, Error **errp)
+{
+    BDRVQcow2State *s = bs->opaque;
+    Qcow2BitmapList *bm_list;
+
+    if (s->bitmap_list) {
+        return (Qcow2BitmapList *)s->bitmap_list;
+    }
+
+    bm_list = bitmap_list_load(bs, errp);
+    s->bitmap_list = bm_list;
+    return bm_list;
+}
+
+static void del_bitmap_list(BlockDriverState *bs)
+{
+    BDRVQcow2State *s = bs->opaque;
+
+    if (s->bitmap_list) {
+        bitmap_list_free(s->bitmap_list);
+        s->bitmap_list = NULL;
+    }
+}
+
  int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                                    void **refcount_table,
                                    int64_t *refcount_table_size)
@@ -656,8 +681,7 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
          return ret;
      }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, NULL);
+    bm_list = get_bitmap_list(bs, NULL);
      if (bm_list == NULL) {
          res->corruptions++;
          return -EINVAL;
@@ -707,8 +731,6 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
      }
 
  out:
-    bitmap_list_free(bm_list);
-
      return ret;
  }
 
@@ -953,8 +975,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
          return false;
      }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
      if (bm_list == NULL) {
          return false;
      }
@@ -992,14 +1013,12 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
      }
 
      g_slist_free(created_dirty_bitmaps);
-    bitmap_list_free(bm_list);
-
      return header_updated;
 
  fail:
      g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs);
      g_slist_free(created_dirty_bitmaps);
-    bitmap_list_free(bm_list);
+    del_bitmap_list(bs);
 
      return false;
  }
@@ -1027,8 +1046,7 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
          return -EINVAL;
      }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
      if (bm_list == NULL) {
          return -EINVAL;
      }
@@ -1068,7 +1086,6 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
 
  out:
      g_slist_free(ro_dirty_bitmaps);
-    bitmap_list_free(bm_list);
 
      return ret;
  }
@@ -1277,8 +1294,7 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
          return;
      }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
      if (bm_list == NULL) {
          return;
      }
@@ -1300,7 +1316,11 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
 
  fail:
      bitmap_free(bm);
-    bitmap_list_free(bm_list);
+}
+
+void qcow2_persistent_dirty_bitmaps_cache_destroy(BlockDriverState *bs)
+{
+    del_bitmap_list(bs);
  }
 
  void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
@@ -1317,12 +1337,12 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
 
      if (!bdrv_has_changed_persistent_bitmaps(bs)) {
          /* nothing to do */
-        return;
+        goto out;
      }
 
      if (!can_write(bs)) {
          error_setg(errp, "No write access");
-        return;
+        goto out;
      }
 
      QSIMPLEQ_INIT(&drop_tables);
@@ -1330,10 +1350,9 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
      if (s->nb_bitmaps == 0) {
          bm_list = bitmap_list_new();
      } else {
-        bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                                   s->bitmap_directory_size, errp);
+        bm_list = get_bitmap_list(bs, errp);
          if (bm_list == NULL) {
-            return;
+            goto out;
          }
      }
 
@@ -1414,8 +1433,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
          g_free(tb);
      }
 
-    bitmap_list_free(bm_list);
-    return;
+    goto out;
 
  fail:
      QSIMPLEQ_FOREACH(bm, bm_list, entry) {
@@ -1430,7 +1448,9 @@ fail:
          g_free(tb);
      }
 
-    bitmap_list_free(bm_list);
+ out:
+    del_bitmap_list(bs);
+    return;
  }
 
  int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
@@ -1495,14 +1515,12 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
          goto fail;
      }
 
-    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
      if (bm_list == NULL) {
          goto fail;
      }
 
      found = find_bitmap_by_name(bm_list, name);
-    bitmap_list_free(bm_list);
      if (found) {
          error_setg(errp, "Bitmap with the same name is already stored");
          goto fail;
block/qcow2.c +2 -0
index 2f36e632f9..7ae9000656 100644
@@ -2135,6 +2135,8 @@ static void qcow2_close(BlockDriverState *bs)
 
      if (!(s->flags & BDRV_O_INACTIVE)) {
          qcow2_inactivate(bs);
+    } else {
+        qcow2_persistent_dirty_bitmaps_cache_destroy(bs);
      }
 
      cache_clean_timer_del(bs);
block/qcow2.h +2 -0
index adf5c3950f..796a8c914b 100644
@@ -299,6 +299,7 @@ typedef struct BDRVQcow2State {
      uint64_t bitmap_directory_size;
      uint64_t bitmap_directory_offset;
      bool dirty_bitmaps_loaded;
+    void *bitmap_list;
 
      int flags;
      int qcow_version;
@@ -675,6 +676,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
  int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
                                   Error **errp);
  int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
+void qcow2_persistent_dirty_bitmaps_cache_destroy(BlockDriverState *bs);
  void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
  int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
  bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
Signature · 2 lines
Best regards,
Vladimir
#3 lore raw

Re: [Qemu-devel] [RFC PATCH 01/12] qcow2-bitmap: cache bm_list

Quoted 68 lines · Vladimir Sementsov-Ogievskiy
Quoted 2 lines · John Snow

We don't need to re-read this list every time, exactly. We can keep it cached and delete our copy when we flush to disk.

Why not simply delete cache only on close (unconditionally)? Why do we need to remove it after flush?

Actually, I think we need to remove it only in qcow2_inactive, after storing persistent bitmaps.

Quoted 30 lines

Because we don't try to flush bitmaps on close if there's nothing to flush, add a new conditional to delete the state anyway for a clean exit.

---
  block/qcow2-bitmap.c | 74
++++++++++++++++++++++++++++++++--------------------
  block/qcow2.c        |  2 ++
  block/qcow2.h        |  2 ++

  3 files changed, 50 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +1 -2
index 6e93ec43e1..fb0a4f3ec4 100644
@@ -536,8 +536,7 @@ static uint32_t bitmap_list_count(Qcow2BitmapList 
 *bm_list)
    * Get bitmap list from qcow2 image. Actually reads bitmap directory,
    * checks it and convert to bitmap list.
    */
-static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, 
 uint64_t offset,
-                                         uint64_t size, Error **errp)
+static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, Error 
 **errp)

  {
      int ret;
      BDRVQcow2State *s = bs->opaque;

block/qcow2-bitmap.c static Qcow2BitmapList · line 549

Quoted 6 lines
block/qcow2-bitmap.c +2 -0
@@ -545,6 +544,8 @@ static Qcow2BitmapList 
 *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
       Qcow2BitmapDirEntry *e;
       uint32_t nb_dir_entries = 0;
       Qcow2BitmapList *bm_list = NULL;
+    uint64_t offset = s->bitmap_directory_offset;
+    uint64_t size = s->bitmap_directory_size;

Worth split this change as a refactoring patch (just remove extra parameters)?

Quoted 20 lines
block/qcow2-bitmap.c +14 -0
         if (size == 0) {
           error_setg(errp, "Requested bitmap directory size is zero");
@@ -636,6 +637,30 @@ fail:
       return NULL;
   }
   +static Qcow2BitmapList *get_bitmap_list(BlockDriverState *bs, Error **errp)
+{
+    BDRVQcow2State *s = bs->opaque;
+    Qcow2BitmapList *bm_list;
+
+    if (s->bitmap_list) {
+        return (Qcow2BitmapList *)s->bitmap_list;
+    }
+
+    bm_list = bitmap_list_load(bs, errp);
+    s->bitmap_list = bm_list;
+    return bm_list;
+}
+
+static void del_bitmap_list(BlockDriverState *bs)
Quoted 8 lines

block/qcow2-bitmap.c fail: · line 662

Quoted 8 lines
block/qcow2-bitmap.c +8 -0
+{
+    BDRVQcow2State *s = bs->opaque;
+
+    if (s->bitmap_list) {
+        bitmap_list_free(s->bitmap_list);
+        s->bitmap_list = NULL;
+    }
+}

so, with this functions, we see, that list is always safely loaded through the cache. But we need also guarantee, that list is always saved through the cache. There are a lot of functions, which stores an abstract bitmap list, given as a parameter, but we want always store our cache..

Quoted 176 lines
Quoted 174 lines
block/qcow2-bitmap.c +2 -1
+
   int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                                     void **refcount_table,
                                     int64_t *refcount_table_size)
@@ -656,8 +681,7 @@ int 
 qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
           return ret;
       }
   -    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, NULL);
+    bm_list = get_bitmap_list(bs, NULL);
       if (bm_list == NULL) {
           res->corruptions++;

          return -EINVAL;

block/qcow2-bitmap.c +0 -0
@@ -707,8 +731,6 @@ int 
 qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
       }

    out:
-    bitmap_list_free(bm_list);
-
      return ret;
  }
  @@ -953,8 +975,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) return false;
      }
  -    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
      if (bm_list == NULL) {
          return false;
      }

block/qcow2-bitmap.c +0 -2
@@ -992,14 +1013,12 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState 
 *bs, Error **errp)
       }
         g_slist_free(created_dirty_bitmaps);
-    bitmap_list_free(bm_list);
-
       return header_updated;

    fail:
      g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs); g_slist_free(created_dirty_bitmaps);
-    bitmap_list_free(bm_list);
+    del_bitmap_list(bs);
        return false;
  }

block/qcow2-bitmap.c +1 -1
@@ -1027,8 +1046,7 @@ int 
 qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
           return -EINVAL;
       }
   -    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
       if (bm_list == NULL) {
           return -EINVAL;

      }

block/qcow2-bitmap.c +0 -0
@@ -1068,7 +1086,6 @@ int 
 qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,

    out:
      g_slist_free(ro_dirty_bitmaps);
-    bitmap_list_free(bm_list);
        return ret;
  }

block/qcow2-bitmap.c +1 -1
@@ -1277,8 +1294,7 @@ void 
 qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
           return;
       }
   -    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
       if (bm_list == NULL) {
           return;

      }

block/qcow2-bitmap.c +0 -0
@@ -1300,7 +1316,11 @@ void 
 qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,

    fail:
      bitmap_free(bm);
-    bitmap_list_free(bm_list);

block/qcow2-bitmap.c +5 -0
+}
+
+void qcow2_persistent_dirty_bitmaps_cache_destroy(BlockDriverState *bs)
+{
+    del_bitmap_list(bs);

  }
    void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)

block/qcow2-bitmap.c +4 -5
@@ -1317,12 +1337,12 @@ void 
 qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
         if (!bdrv_has_changed_persistent_bitmaps(bs)) {
           /* nothing to do */
-        return;
+        goto out;
       }
         if (!can_write(bs)) {
           error_setg(errp, "No write access");
-        return;
+        goto out;
       }
         QSIMPLEQ_INIT(&drop_tables);
@@ -1330,10 +1350,9 @@ void 
 qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
       if (s->nb_bitmaps == 0) {
           bm_list = bitmap_list_new();
       } else {
-        bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                                   s->bitmap_directory_size, errp);
+        bm_list = get_bitmap_list(bs, errp);
           if (bm_list == NULL) {
-            return;
+            goto out;
           }
       }

  @@ -1414,8 +1433,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) g_free(tb);
      }
  -    bitmap_list_free(bm_list);
-    return;
+    goto out;
    fail:
      QSIMPLEQ_FOREACH(bm, bm_list, entry) {

block/qcow2-bitmap.c +4 -2
@@ -1430,7 +1448,9 @@ fail:
           g_free(tb);
       }
   -    bitmap_list_free(bm_list);
+ out:
+    del_bitmap_list(bs);
+    return;
   }
     int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
@@ -1495,14 +1515,12 @@ bool 
 qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
           goto fail;
       }
   -    bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
-                               s->bitmap_directory_size, errp);
+    bm_list = get_bitmap_list(bs, errp);
       if (bm_list == NULL) {
           goto fail;
       }
         found = find_bitmap_by_name(bm_list, name);
-    bitmap_list_free(bm_list);
       if (found) {
           error_setg(errp, "Bitmap with the same name is already stored");
           goto fail;
block/qcow2.c +2 -0
index 2f36e632f9..7ae9000656 100644
@@ -2135,6 +2135,8 @@ static void qcow2_close(BlockDriverState *bs)
         if (!(s->flags & BDRV_O_INACTIVE)) {
           qcow2_inactivate(bs);
+    } else {
+        qcow2_persistent_dirty_bitmaps_cache_destroy(bs);
       }
         cache_clean_timer_del(bs);
block/qcow2.h +2 -0
index adf5c3950f..796a8c914b 100644
@@ -299,6 +299,7 @@ typedef struct BDRVQcow2State {
       uint64_t bitmap_directory_size;
       uint64_t bitmap_directory_offset;
       bool dirty_bitmaps_loaded;
+    void *bitmap_list;
         int flags;
       int qcow_version;
@@ -675,6 +676,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState 
 *bs, Error **errp);
   int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
                                    Error **errp);
   int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
+void qcow2_persistent_dirty_bitmaps_cache_destroy(BlockDriverState 
 *bs);
   void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);

  int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,

Signature · 2 lines
Best regards,
Vladimir
author
#4 lore raw

Re: [Qemu-devel] [RFC PATCH 01/12] qcow2-bitmap: cache bm_list

Quoted 81 lines · Vladimir Sementsov-Ogievskiy
Quoted 80 lines · Vladimir Sementsov-Ogievskiy
Quoted 3 lines · John Snow

We don't need to re-read this list every time, exactly. We can keep it cached
and delete our copy when we flush to disk.

Why not simply delete cache only on close (unconditionally)? Why do we need to remove it after flush?

Actually, I think we need to remove it only in qcow2_inactive, after storing persistent bitmaps.

Quoted 31 lines

Because we don't try to flush bitmaps on close if there's nothing to flush,
add a new conditional to delete the state anyway for a clean exit.

---
  block/qcow2-bitmap.c | 74
++++++++++++++++++++++++++++++++--------------------
  block/qcow2.c        |  2 ++
  block/qcow2.h        |  2 ++

  3 files changed, 50 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +1 -2
index 6e93ec43e1..fb0a4f3ec4 100644
@@ -536,8 +536,7 @@ static uint32_t bitmap_list_count(Qcow2BitmapList
 *bm_list)
    * Get bitmap list from qcow2 image. Actually reads bitmap directory,
    * checks it and convert to bitmap list.
    */
-static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs,
 uint64_t offset,
-                                         uint64_t size, Error **errp)
+static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, Error
 **errp)

  {
      int ret;
      BDRVQcow2State *s = bs->opaque;

block/qcow2-bitmap.c static Qcow2BitmapList · line 549

Quoted 6 lines
block/qcow2-bitmap.c +2 -0
@@ -545,6 +544,8 @@ static Qcow2BitmapList
 *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
       Qcow2BitmapDirEntry *e;
       uint32_t nb_dir_entries = 0;
       Qcow2BitmapList *bm_list = NULL;
+    uint64_t offset = s->bitmap_directory_offset;
+    uint64_t size = s->bitmap_directory_size;

Worth split this change as a refactoring patch (just remove extra parameters)?

Quoted 27 lines
block/qcow2-bitmap.c +20 -0
         if (size == 0) {
           error_setg(errp, "Requested bitmap directory size is zero");
@@ -636,6 +637,30 @@ fail:
       return NULL;
   }
   +static Qcow2BitmapList *get_bitmap_list(BlockDriverState *bs,
 Error **errp)
+{
+    BDRVQcow2State *s = bs->opaque;
+    Qcow2BitmapList *bm_list;
+
+    if (s->bitmap_list) {
+        return (Qcow2BitmapList *)s->bitmap_list;
+    }
+
+    bm_list = bitmap_list_load(bs, errp);
+    s->bitmap_list = bm_list;
+    return bm_list;
+}
+
+static void del_bitmap_list(BlockDriverState *bs)
+{
+    BDRVQcow2State *s = bs->opaque;
+
+    if (s->bitmap_list) {
+        bitmap_list_free(s->bitmap_list);
+        s->bitmap_list = NULL;
Quoted 9 lines
Quoted 2 lines

block/qcow2-bitmap.c fail: · line 662

Quoted 2 lines
block/qcow2-bitmap.c +2 -0
+    }
+}

so, with this functions, we see, that list is always safely loaded through the cache. But we need also guarantee, that list is always saved through the cache. There are a lot of functions, which stores an abstract bitmap list, given as a parameter, but we want always store our cache..

Do you have an example?

These functions return a bitmap list:

bitmap_list_new()
bitmap_list_load(...);
get_bitmap_list(...);

bitmap_list_new creates a new bitmap_list, it's called by bitmap_list_load and qcow2_store_persistent_dirty_bitmaps.

qcow2_store_persistent_dirty_bitmaps doesn't matter right now because we drop the cache by the end of that function.

bitmap_list_load is only called by get_bitmap_list now, and get_bitmap_list caches that bitmap list as s->bitmap_list.

I think any other function that takes a bitmap list must be getting it from a location where it's cached -- except in store, which trashes it anyway.

What I can do here is to make get_bitmap_list create a new bitmap list if s->nb_bitmaps is zero, and remove the new call in store -- then we can be really very confident that any bitmap list getting passed around is definitely the cached one.

author
#5 lore raw

Re: [Qemu-devel] [RFC PATCH 01/12] qcow2-bitmap: cache bm_list

In reply to Vladimir Sementsov-Ogievskiy
Quoted 8 lines · Vladimir Sementsov-Ogievskiy
Quoted 3 lines · John Snow

We don't need to re-read this list every time, exactly. We can keep it cached
and delete our copy when we flush to disk.

Why not simply delete cache only on close (unconditionally)? Why do we need to remove it after flush?

I was being imprecise with my language again -- I'm doing it on qcow2_inactivate, through qcow2_store_persistent_dirty_bitmaps.

I technically don't even need to destroy the cache *then*, I could just do it unconditionally in qcow2_close.

I just felt it was "safer" to drop the cache after a store as a natural point of synchronization, but it *should* be fine without.

Quoted 3 lines

Actually, I think we need to remove it only in qcow2_inactive, after storing persistent bitmaps.

So would you prefer the unconditional drop on close, or a drop for every inactivate? You still need a drop on close in that case, because we may not call inactivate on close, and I am still trying to build the cache for read only images, so to prevent leaks I need to clean that up.

Quoted 33 lines
Quoted 33 lines

Because we don't try to flush bitmaps on close if there's nothing to flush,
add a new conditional to delete the state anyway for a clean exit.

---
  block/qcow2-bitmap.c | 74
++++++++++++++++++++++++++++++++--------------------
  block/qcow2.c        |  2 ++
  block/qcow2.h        |  2 ++

  3 files changed, 50 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +1 -2
index 6e93ec43e1..fb0a4f3ec4 100644
@@ -536,8 +536,7 @@ static uint32_t bitmap_list_count(Qcow2BitmapList
 *bm_list)
    * Get bitmap list from qcow2 image. Actually reads bitmap directory,
    * checks it and convert to bitmap list.
    */
-static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs,
 uint64_t offset,
-                                         uint64_t size, Error **errp)
+static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, Error
 **errp)

  {
      int ret;
      BDRVQcow2State *s = bs->opaque;

block/qcow2-bitmap.c +0 -0
@@ -545,6 +544,8 @@ static Qcow2BitmapList
 *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
Quoted 9 lines

block/qcow2-bitmap.c static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset, · line 548

Quoted 5 lines
block/qcow2-bitmap.c +2 -0
       Qcow2BitmapDirEntry *e;
       uint32_t nb_dir_entries = 0;
       Qcow2BitmapList *bm_list = NULL;
+    uint64_t offset = s->bitmap_directory_offset;
+    uint64_t size = s->bitmap_directory_size;

Worth split this change as a refactoring patch (just remove extra parameters)?

Sure, this patch looks a little long.

author
#6 lore raw

RFC PATCH 2/12 qcow2/dirty-bitmap: cache loaded bitmaps

In reply to John Snow

For bitmaps that we succeeded in loading, we can cache a reference to that object. This will let us iterate over the more convenient form of in-memory bitmaps for qemu-img bitmap manipulation tools.

---
 block/qcow2-bitmap.c | 1 +
 1 file changed, 1 insertion(+)
block/qcow2-bitmap.c +1 -0
index fb0a4f3ec4..d89758f235 100644
@@ -986,6 +986,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
             if (bitmap == NULL) {
                 goto fail;
             }
+            bm->dirty_bitmap = bitmap;
 
             if (!(bm->flags & BME_FLAG_AUTO)) {
                 bdrv_disable_dirty_bitmap(bitmap);
Signature · 1 line
2.14.3
#7 lore raw

Re: [Qemu-devel] [RFC PATCH 02/12] qcow2/dirty-bitmap: cache loaded bitmaps

Quoted 14 lines · John Snow

For bitmaps that we succeeded in loading, we can cache a reference to that object. This will let us iterate over the more convenient form of in-memory bitmaps for qemu-img bitmap manipulation tools.

---
  block/qcow2-bitmap.c | 1 +
  1 file changed, 1 insertion(+)
block/qcow2-bitmap.c +0 -0
index fb0a4f3ec4..d89758f235 100644

block/qcow2-bitmap.c bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) · line 992

Quoted 7 lines
block/qcow2-bitmap.c +1 -0
@@ -986,6 +986,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
              if (bitmap == NULL) {
                  goto fail;
              }
+            bm->dirty_bitmap = bitmap;
 
              if (!(bm->flags & BME_FLAG_AUTO)) {
                  bdrv_disable_dirty_bitmap(bitmap);

Looks correct, I just suppose to reuse this information somehow in qcow2_store_persistent_dirty_bitmaps, at least for an assert.

Signature · 2 lines
Best regards,
Vladimir
author
#8 lore raw

RFC PATCH 3/12 block/qcow2-bitmap: avoid adjusting bm->flags for RO bitmaps

In reply to John Snow

Instead of always setting IN_USE, handle whether or not the bitmap is read-only instead of a two-loop pass. This will allow us to show the flags exactly as they appear for bitmaps in qemu-img info.

---
 block/qcow2-bitmap.c | 48 ++++++++++++++++++++----------------------------
 1 file changed, 20 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +20 -28
index d89758f235..fc8d52fc3e 100644
@@ -942,13 +942,6 @@ fail:
     return ret;
 }
 
-/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */
-static void release_dirty_bitmap_helper(gpointer bitmap,
-                                        gpointer bs)
-{
-    bdrv_release_dirty_bitmap(bs, bitmap);
-}
-
 /* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */
 static void set_readonly_helper(gpointer bitmap, gpointer value)
 {
@@ -967,8 +960,8 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
     BDRVQcow2State *s = bs->opaque;
     Qcow2BitmapList *bm_list;
     Qcow2Bitmap *bm;
-    GSList *created_dirty_bitmaps = NULL;
     bool header_updated = false;
+    bool needs_update = false;
 
     if (s->nb_bitmaps == 0) {
         /* No bitmaps - nothing to do */
@@ -992,33 +985,32 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
                 bdrv_disable_dirty_bitmap(bitmap);
             }
             bdrv_dirty_bitmap_set_persistance(bitmap, true);
-            bm->flags |= BME_FLAG_IN_USE;
-            created_dirty_bitmaps =
-                    g_slist_append(created_dirty_bitmaps, bitmap);
-        }
-    }
-
-    if (created_dirty_bitmaps != NULL) {
-        if (can_write(bs)) {
-            /* in_use flags must be updated */
-            int ret = update_ext_header_and_dir_in_place(bs, bm_list);
-            if (ret < 0) {
-                error_setg_errno(errp, -ret, "Can't update bitmap directory");
-                goto fail;
+            if (can_write(bs)) {
+                bm->flags |= BME_FLAG_IN_USE;
+                needs_update = true;
+            } else {
+                bdrv_dirty_bitmap_set_readonly(bitmap, true);
             }
-            header_updated = true;
-        } else {
-            g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
-                            (gpointer)true);
         }
     }
 
-    g_slist_free(created_dirty_bitmaps);
+    /* in_use flags must be updated */
+    if (needs_update) {
+        int ret = update_ext_header_and_dir_in_place(bs, bm_list);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret, "Can't update bitmap directory");
+            goto fail;
+        }
+        header_updated = true;
+    }
     return header_updated;
 
 fail:
-    g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs);
-    g_slist_free(created_dirty_bitmaps);
+    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+        if (bm->dirty_bitmap) {
+            bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
+        }
+    }
     del_bitmap_list(bs);
 
     return false;
Signature · 1 line
2.14.3
#9 lore raw

Re: [Qemu-devel] [RFC PATCH 03/12] block/qcow2-bitmap: avoid adjusting bm->flags for RO bitmaps

Quoted 49 lines · John Snow

Instead of always setting IN_USE, handle whether or not the bitmap is read-only instead of a two-loop pass. This will allow us to show the flags exactly as they appear for bitmaps in qemu-img info.

---
  block/qcow2-bitmap.c | 48 ++++++++++++++++++++----------------------------
  1 file changed, 20 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +1 -16
index d89758f235..fc8d52fc3e 100644
@@ -942,13 +942,6 @@ fail:
      return ret;
  }
 
-/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */
-static void release_dirty_bitmap_helper(gpointer bitmap,
-                                        gpointer bs)
-{
-    bdrv_release_dirty_bitmap(bs, bitmap);
-}
-
  /* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */
  static void set_readonly_helper(gpointer bitmap, gpointer value)
  {
@@ -967,8 +960,8 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
      BDRVQcow2State *s = bs->opaque;
      Qcow2BitmapList *bm_list;
      Qcow2Bitmap *bm;
-    GSList *created_dirty_bitmaps = NULL;
      bool header_updated = false;
+    bool needs_update = false;
 
      if (s->nb_bitmaps == 0) {
          /* No bitmaps - nothing to do */
@@ -992,33 +985,32 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
                  bdrv_disable_dirty_bitmap(bitmap);
              }
              bdrv_dirty_bitmap_set_persistance(bitmap, true);
-            bm->flags |= BME_FLAG_IN_USE;
-            created_dirty_bitmaps =
-                    g_slist_append(created_dirty_bitmaps, bitmap);
-        }
-    }
-
-    if (created_dirty_bitmaps != NULL) {
-        if (can_write(bs)) {

block/qcow2-bitmap.c bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) · line 992

Quoted 10 lines
block/qcow2-bitmap.c +5 -5
-            /* in_use flags must be updated */
-            int ret = update_ext_header_and_dir_in_place(bs, bm_list);
-            if (ret < 0) {
-                error_setg_errno(errp, -ret, "Can't update bitmap directory");
-                goto fail;
+            if (can_write(bs)) {
+                bm->flags |= BME_FLAG_IN_USE;
+                needs_update = true;
+            } else {
+                bdrv_dirty_bitmap_set_readonly(bitmap, true);

oops. This shows, that patch 01 was incorrect: before this (03) patch, in this case we'll have IN_USE set in cache but not in the image.

Quoted 9 lines
block/qcow2-bitmap.c +0 -5
              }
-            header_updated = true;
-        } else {
-            g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
-                            (gpointer)true);
          }
      }
 
-    g_slist_free(created_dirty_bitmaps);

block/qcow2-bitmap.c bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) · line 1006

Quoted 10 lines
block/qcow2-bitmap.c +9 -0
+    /* in_use flags must be updated */
+    if (needs_update) {
+        int ret = update_ext_header_and_dir_in_place(bs, bm_list);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret, "Can't update bitmap directory");
+            goto fail;
+        }
+        header_updated = true;
+    }
      return header_updated;

may be safely changed to return needs_update, as if we failed to update, we'll go to fail.

block/qcow2-bitmap.c bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) · line 1016

Quoted 12 lines
block/qcow2-bitmap.c +5 -2
  fail:
-    g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs);
-    g_slist_free(created_dirty_bitmaps);
+    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+        if (bm->dirty_bitmap) {
+            bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
+        }
+    }
      del_bitmap_list(bs);
 
      return false;

overall looks good, may be worth move it before 01.

Signature · 2 lines
Best regards,
Vladimir
author
#10 lore raw

Re: [Qemu-devel] [RFC PATCH 03/12] block/qcow2-bitmap: avoid adjusting bm->flags for RO bitmaps

Quoted 57 lines · Vladimir Sementsov-Ogievskiy
Quoted 56 lines · John Snow

Instead of always setting IN_USE, handle whether or not the bitmap is read-only instead of a two-loop pass. This will allow us to show the flags exactly as they appear for bitmaps in qemu-img info.

---
  block/qcow2-bitmap.c | 48
++++++++++++++++++++----------------------------

  1 file changed, 20 insertions(+), 28 deletions(-)
block/qcow2-bitmap.c +0 -0
index d89758f235..fc8d52fc3e 100644
@@ -942,13 +942,6 @@ fail:
       return ret;
   }

  -/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */

block/qcow2-bitmap.c +0 -6
-static void release_dirty_bitmap_helper(gpointer bitmap,
-                                        gpointer bs)
-{
-    bdrv_release_dirty_bitmap(bs, bitmap);
-}
-

  /* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */ static void set_readonly_helper(gpointer bitmap, gpointer value) {

block/qcow2-bitmap.c +1 -14
@@ -967,8 +960,8 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState
 *bs, Error **errp)
       BDRVQcow2State *s = bs->opaque;
       Qcow2BitmapList *bm_list;
       Qcow2Bitmap *bm;
-    GSList *created_dirty_bitmaps = NULL;
       bool header_updated = false;
+    bool needs_update = false;
         if (s->nb_bitmaps == 0) {
           /* No bitmaps - nothing to do */
@@ -992,33 +985,32 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState
 *bs, Error **errp)
                   bdrv_disable_dirty_bitmap(bitmap);
               }
               bdrv_dirty_bitmap_set_persistance(bitmap, true);
-            bm->flags |= BME_FLAG_IN_USE;
-            created_dirty_bitmaps =
-                    g_slist_append(created_dirty_bitmaps, bitmap);
-        }
-    }
-
-    if (created_dirty_bitmaps != NULL) {
-        if (can_write(bs)) {
-            /* in_use flags must be updated */
-            int ret = update_ext_header_and_dir_in_place(bs, bm_list);
-            if (ret < 0) {
-                error_setg_errno(errp, -ret, "Can't update bitmap
 directory");
-                goto fail;
Quoted 9 lines

block/qcow2-bitmap.c bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) · line 992

Quoted 5 lines
block/qcow2-bitmap.c +5 -0
+            if (can_write(bs)) {
+                bm->flags |= BME_FLAG_IN_USE;
+                needs_update = true;
+            } else {
+                bdrv_dirty_bitmap_set_readonly(bitmap, true);

oops. This shows, that patch 01 was incorrect: before this (03) patch, in this case we'll have IN_USE set in cache but not in the image.

Ah, I can try to order this patch first so that the cache stays semantically correct.

Quoted 20 lines
Quoted 9 lines
block/qcow2-bitmap.c +1 -4
               }
-            header_updated = true;
-        } else {
-            g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
-                            (gpointer)true);
           }
       }
   -    g_slist_free(created_dirty_bitmaps);
+    /* in_use flags must be updated */

block/qcow2-bitmap.c bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) · line 1006

Quoted 10 lines
block/qcow2-bitmap.c +8 -0
+    if (needs_update) {
+        int ret = update_ext_header_and_dir_in_place(bs, bm_list);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret, "Can't update bitmap
 directory");
+            goto fail;
+        }
+        header_updated = true;
+    }
       return header_updated;
Quoted 3 lines

may be safely changed to return needs_update, as if we failed to update, we'll go to fail.

Ugh, yes.

Quoted 14 lines
Quoted 11 lines

    fail:
-    g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs);
-    g_slist_free(created_dirty_bitmaps);
+    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+        if (bm->dirty_bitmap) {
+            bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap); +        }
+    }
      del_bitmap_list(bs);
        return false;

overall looks good, may be worth move it before 01.

author
#11 lore raw

RFC PATCH 4/12 qcow2/dirty-bitmaps: load IN_USE bitmaps if disk is RO

In reply to John Snow

For the purposes of qemu-img manipulation and querying of bitmaps, load bitmaps that are "in use" -- if the image is read only. This will allow us to diagnose problems with this flag using the qemu-img tool.

---
 block/qcow2-bitmap.c | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)
block/qcow2-bitmap.c +15 -17
index fc8d52fc3e..b556dbdccd 100644
@@ -346,7 +346,7 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
     uint32_t granularity;
     BdrvDirtyBitmap *bitmap = NULL;
 
-    if (bm->flags & BME_FLAG_IN_USE) {
+    if (can_write(bs) && (bm->flags & BME_FLAG_IN_USE)) {
         error_setg(errp, "Bitmap '%s' is in use", bm->name);
         goto fail;
     }
@@ -974,23 +974,21 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
     }
 
     QSIMPLEQ_FOREACH(bm, bm_list, entry) {
-        if (!(bm->flags & BME_FLAG_IN_USE)) {
-            BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
-            if (bitmap == NULL) {
-                goto fail;
-            }
-            bm->dirty_bitmap = bitmap;
+        BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
+        if (bitmap == NULL) {
+            goto fail;
+        }
+        bm->dirty_bitmap = bitmap;
 
-            if (!(bm->flags & BME_FLAG_AUTO)) {
-                bdrv_disable_dirty_bitmap(bitmap);
-            }
-            bdrv_dirty_bitmap_set_persistance(bitmap, true);
-            if (can_write(bs)) {
-                bm->flags |= BME_FLAG_IN_USE;
-                needs_update = true;
-            } else {
-                bdrv_dirty_bitmap_set_readonly(bitmap, true);
-            }
+        if (!(bm->flags & BME_FLAG_AUTO)) {
+            bdrv_disable_dirty_bitmap(bitmap);
+        }
+        bdrv_dirty_bitmap_set_persistance(bitmap, true);
+        if (can_write(bs)) {
+            bm->flags |= BME_FLAG_IN_USE;
+            needs_update = true;
+        } else {
+            bdrv_dirty_bitmap_set_readonly(bitmap, true);
         }
     }
 
Signature · 1 line
2.14.3
#12 lore raw

Re: [Qemu-devel] [RFC PATCH 04/12] qcow2/dirty-bitmaps: load IN_USE bitmaps if disk is RO

Quoted 3 lines · John Snow

For the purposes of qemu-img manipulation and querying of bitmaps, load bitmaps that are "in use" -- if the image is read only. This will allow us to diagnose problems with this flag using the qemu-img tool.

It looks unsafe.. We can load them, then reopen rw and then store them on vm close, the result would be unpredictable. And they become available for qmp commands..

If we want to load them, we may need some additional state for such dirty bitmaps, something like "invalid", to be sure, that they will not be used like normal bitmap. And we may need some additional flag to load or not load them, and this behavior will be unrelated to can_write().

Quoted 57 lines
---
  block/qcow2-bitmap.c | 32 +++++++++++++++-----------------
  1 file changed, 15 insertions(+), 17 deletions(-)
block/qcow2-bitmap.c +15 -17
index fc8d52fc3e..b556dbdccd 100644
@@ -346,7 +346,7 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
      uint32_t granularity;
      BdrvDirtyBitmap *bitmap = NULL;
 
-    if (bm->flags & BME_FLAG_IN_USE) {
+    if (can_write(bs) && (bm->flags & BME_FLAG_IN_USE)) {
          error_setg(errp, "Bitmap '%s' is in use", bm->name);
          goto fail;
      }
@@ -974,23 +974,21 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
      }
 
      QSIMPLEQ_FOREACH(bm, bm_list, entry) {
-        if (!(bm->flags & BME_FLAG_IN_USE)) {
-            BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
-            if (bitmap == NULL) {
-                goto fail;
-            }
-            bm->dirty_bitmap = bitmap;
+        BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
+        if (bitmap == NULL) {
+            goto fail;
+        }
+        bm->dirty_bitmap = bitmap;
 
-            if (!(bm->flags & BME_FLAG_AUTO)) {
-                bdrv_disable_dirty_bitmap(bitmap);
-            }
-            bdrv_dirty_bitmap_set_persistance(bitmap, true);
-            if (can_write(bs)) {
-                bm->flags |= BME_FLAG_IN_USE;
-                needs_update = true;
-            } else {
-                bdrv_dirty_bitmap_set_readonly(bitmap, true);
-            }
+        if (!(bm->flags & BME_FLAG_AUTO)) {
+            bdrv_disable_dirty_bitmap(bitmap);
+        }
+        bdrv_dirty_bitmap_set_persistance(bitmap, true);
+        if (can_write(bs)) {
+            bm->flags |= BME_FLAG_IN_USE;
+            needs_update = true;
+        } else {
+            bdrv_dirty_bitmap_set_readonly(bitmap, true);
          }
      }
 
Signature · 2 lines
Best regards,
Vladimir
author
#13 lore raw

Re: [Qemu-devel] [RFC PATCH 04/12] qcow2/dirty-bitmaps: load IN_USE bitmaps if disk is RO

Quoted 9 lines · Vladimir Sementsov-Ogievskiy
Quoted 3 lines · John Snow

For the purposes of qemu-img manipulation and querying of bitmaps, load bitmaps that are "in use" -- if the image is read only. This will allow us to diagnose problems with this flag using the qemu-img tool.

It looks unsafe.. We can load them, then reopen rw and then store them on vm close, the result would be unpredictable. And they become available for qmp commands..

Quoted 6 lines

If we want to load them, we may need some additional state for such dirty bitmaps, something like "invalid", to be sure, that they will not be used like normal bitmap. And we may
need some additional flag to load or not load them, and this behavior will be unrelated to can_write().

Yeah, you're right -- reopening is scary. I'll see what I can do.

The goal is just to get them in memory so that qemu-img can use the standard BdrvDirtyBitmap API to print various info about them for the user.

Since 'info' and 'dump' don't need RW access, it'd be nicest for these commands to work in RO mode, hence the patch.

author
#14 lore raw

RFC PATCH 5/12 qcow2-bitmap: track bitmap type

In reply to John Snow

We only have one type of persistent bitmap right now, but I'd like the qemu-img tool to be able to give good diagnostic information if it sees an unknown/unsupported type.

We do enforce it to be the dirty tracking type in check_dir_entry, but I wanted positive affirmation of the type in the forthcoming info script.

---
 block/qcow2-bitmap.c | 2 ++
 1 file changed, 2 insertions(+)
block/qcow2-bitmap.c +2 -0
index b556dbdccd..60e01abfd7 100644
@@ -83,6 +83,7 @@ typedef QSIMPLEQ_HEAD(Qcow2BitmapTableList, Qcow2BitmapTable)
 typedef struct Qcow2Bitmap {
     Qcow2BitmapTable table;
     uint32_t flags;
+    uint8_t type;
     uint8_t granularity_bits;
     char *name;
 
@@ -608,6 +609,7 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, Error **errp)
         bm->table.offset = e->bitmap_table_offset;
         bm->table.size = e->bitmap_table_size;
         bm->flags = e->flags;
+        bm->type = e->type;
         bm->granularity_bits = e->granularity_bits;
         bm->name = dir_entry_copy_name(e);
         QSIMPLEQ_INSERT_TAIL(bm_list, bm, entry);
Signature · 1 line
2.14.3
author
#15 lore raw

RFC PATCH 6/12 qapi: add bitmap info

In reply to John Snow

Add some of the necessary scaffolding for reporting bitmap information.

---
 qapi/block-core.json | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)
qapi/block-core.json +59 -1
index c50517bff3..8f33f41ce7 100644
@@ -33,6 +33,61 @@
             'date-sec': 'int', 'date-nsec': 'int',
             'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
 
+##
+# @BitmapTypeEnum:
+#
+# An enumeration of possible serialized bitmap types.
+#
+# @dirty-tracking: This bitmap records information on dirty
+#                  segments within the file.
+#
+# @unknown: This bitmap is of an unknown or reserved type.
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapTypeEnum', 'data': [ 'dirty-tracking', 'unknown' ] }
+
+##
+# @BitmapFlagEnum:
+#
+# An enumeration of possible flags for serialized bitmaps.
+#
+# @in-use: This bitmap is considered to be in-use, and may now be inconsistent.
+#
+# @auto: This bitmap must reflect any and all changes to the file it describes.
+#        The type of this bitmap must be @DirtyTrackingBitmap.
+#
+# @extra-data-compatible: This bitmap has extra information associated with it.
+#
+# @unknown: This bitmap has unknown or reserved properties.
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapFlagEnum', 'data': [ 'in-use', 'auto',
+                                      'extra-data-compatible', 'unknown' ] }
+
+##
+# @BitmapInfo:
+#
+# @name: The name of the bitmap.
+#
+# @type: The type of bitmap.
+#
+# @granularity: Bitmap granularity, in bytes.
+#
+# @count: Overall bitmap dirtiness, in bytes.
+#
+# @flags: Bitmap flags, if any.
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapInfo',
+  'data': { 'name': 'str', 'type': 'BitmapTypeEnum', 'granularity': 'int',
+            'count': 'int', '*flags': ['BitmapFlagEnum']
+  }
+}
+
 ##
 # @ImageInfoSpecificQCow2EncryptionBase:
 #
@@ -69,6 +124,8 @@
 # @encrypt: details about encryption parameters; only set if image
 #           is encrypted (since 2.10)
 #
+# @bitmaps: list of image bitmaps (since 2.13)
+#
 # Since: 1.7
 ##
 { 'struct': 'ImageInfoSpecificQCow2',
@@ -77,7 +134,8 @@
       '*lazy-refcounts': 'bool',
       '*corrupt': 'bool',
       'refcount-bits': 'int',
-      '*encrypt': 'ImageInfoSpecificQCow2Encryption'
+      '*encrypt': 'ImageInfoSpecificQCow2Encryption',
+      '*bitmaps': ['BitmapInfo']
   } }
 
 ##
Signature · 1 line
2.14.3
#16 lore raw

Re: [Qemu-devel] [RFC PATCH 06/12] qapi: add bitmap info

Quoted 28 lines · John Snow

Add some of the necessary scaffolding for reporting bitmap information.

---
  qapi/block-core.json | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++-
  1 file changed, 59 insertions(+), 1 deletion(-)
qapi/block-core.json +13 -0
index c50517bff3..8f33f41ce7 100644
@@ -33,6 +33,61 @@
              'date-sec': 'int', 'date-nsec': 'int',
              'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
 
+##
+# @BitmapTypeEnum:
+#
+# An enumeration of possible serialized bitmap types.
+#
+# @dirty-tracking: This bitmap records information on dirty
+#                  segments within the file.
+#
+# @unknown: This bitmap is of an unknown or reserved type.
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapTypeEnum', 'data': [ 'dirty-tracking', 'unknown' ] }

qapi/block-core.json line 58

Quoted 10 lines
qapi/block-core.json +10 -0
+
+##
+# @BitmapFlagEnum:
+#
+# An enumeration of possible flags for serialized bitmaps.
+#
+# @in-use: This bitmap is considered to be in-use, and may now be inconsistent.
+#
+# @auto: This bitmap must reflect any and all changes to the file it describes.
+#        The type of this bitmap must be @DirtyTrackingBitmap.

logical, but I don't see this restriction in the spec. May be we need to update the spec

qapi/block-core.json line 60

Quoted 2 lines
qapi/block-core.json +2 -0
+#
+# @extra-data-compatible: This bitmap has extra information associated with it.

no, this flag means, that extra data is compatible. So, if you don't know what is this extra data, you can read and modify the bitmap, leaving this data as is. If this flag is unset, and there are some extra data, bitmap must not be used.

Finally, this spec should be consistent (or, may be better, duplicate) spec from docs/interop/qcow2.txt..

qapi/block-core.json line 62

Quoted 2 lines
qapi/block-core.json +2 -0
+#
+# @unknown: This bitmap has unknown or reserved properties.

Better is only "reserved flags" (not unknown and not properties), they are reserved by spec.

Quoted 15 lines
qapi/block-core.json +15 -0
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapFlagEnum', 'data': [ 'in-use', 'auto',
+                                      'extra-data-compatible', 'unknown' ] }
+
+##
+# @BitmapInfo:
+#
+# @name: The name of the bitmap.
+#
+# @type: The type of bitmap.
+#
+# @granularity: Bitmap granularity, in bytes.
+#

qapi/block-core.json line 87

Quoted 10 lines
qapi/block-core.json +10 -0
+# @count: Overall bitmap dirtiness, in bytes.
+#
+# @flags: Bitmap flags, if any.
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapInfo',
+  'data': { 'name': 'str', 'type': 'BitmapTypeEnum', 'granularity': 'int',
+            'count': 'int', '*flags': ['BitmapFlagEnum']

may be worth add 'has-extra-data'

Quoted 25 lines
qapi/block-core.json +7 -1
+  }
+}
+
  ##
  # @ImageInfoSpecificQCow2EncryptionBase:
  #
@@ -69,6 +124,8 @@
  # @encrypt: details about encryption parameters; only set if image
  #           is encrypted (since 2.10)
  #
+# @bitmaps: list of image bitmaps (since 2.13)
+#
  # Since: 1.7
  ##
  { 'struct': 'ImageInfoSpecificQCow2',
@@ -77,7 +134,8 @@
        '*lazy-refcounts': 'bool',
        '*corrupt': 'bool',
        'refcount-bits': 'int',
-      '*encrypt': 'ImageInfoSpecificQCow2Encryption'
+      '*encrypt': 'ImageInfoSpecificQCow2Encryption',
+      '*bitmaps': ['BitmapInfo']
    } }
 
  ##
Signature · 2 lines
Best regards,
Vladimir
author
#17 lore raw

Re: [Qemu-devel] [RFC PATCH 06/12] qapi: add bitmap info

Quoted 36 lines · Vladimir Sementsov-Ogievskiy
Quoted 35 lines · John Snow

Add some of the necessary scaffolding for reporting bitmap information.

---
  qapi/block-core.json | 60
+++++++++++++++++++++++++++++++++++++++++++++++++++-

  1 file changed, 59 insertions(+), 1 deletion(-)
qapi/block-core.json +0 -0
index c50517bff3..8f33f41ce7 100644
@@ -33,6 +33,61 @@
               'date-sec': 'int', 'date-nsec': 'int',
               'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }

  +##

qapi/block-core.json +19 -0
+# @BitmapTypeEnum:
+#
+# An enumeration of possible serialized bitmap types.
+#
+# @dirty-tracking: This bitmap records information on dirty
+#                  segments within the file.
+#
+# @unknown: This bitmap is of an unknown or reserved type.
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapTypeEnum', 'data': [ 'dirty-tracking', 'unknown' ] }
+
+##
+# @BitmapFlagEnum:
+#
+# An enumeration of possible flags for serialized bitmaps.
+#
+# @in-use: This bitmap is considered to be in-use, and may now be
Quoted 9 lines
Quoted 5 lines

inconsistent.

qapi/block-core.json +2 -0
+#
+# @auto: This bitmap must reflect any and all changes to the file it

describes.
+#        The type of this bitmap must be @DirtyTrackingBitmap.

logical, but I don't see this restriction in the spec. May be we need to update the spec

Where did I even get this idea?

Quoted 9 lines
Quoted 3 lines
qapi/block-core.json +2 -0
+#
+# @extra-data-compatible: This bitmap has extra information

associated with it.

no, this flag means, that extra data is compatible. So, if you don't know what is this extra data, you can read and modify the bitmap, leaving this data as is. If this flag is unset, and there are some extra data, bitmap must not be used.

Oh, understood! Thank you for clearing that up.

Quoted 2 lines

Finally, this spec should be consistent (or, may be better, duplicate) spec from docs/interop/qcow2.txt..

*cough* I guess if I get enough things wrong it's fair to say "RTFM!"

Quoted 6 lines

qapi/block-core.json

Quoted 2 lines
qapi/block-core.json +2 -0
+#
+# @unknown: This bitmap has unknown or reserved properties.

Better is only "reserved flags" (not unknown and not properties), they are reserved by spec.

Yeah, I'll take that stance.

Quoted 21 lines
Quoted 21 lines
qapi/block-core.json +5 -0
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapFlagEnum', 'data': [ 'in-use', 'auto',
+                                      'extra-data-compatible',

'unknown' ] }

qapi/block-core.json +15 -0
+
+##
+# @BitmapInfo:
+#
+# @name: The name of the bitmap.
+#
+# @type: The type of bitmap.
+#
+# @granularity: Bitmap granularity, in bytes.
+#
+# @count: Overall bitmap dirtiness, in bytes.
+#
+# @flags: Bitmap flags, if any.
+#
+# Since: 2.13
Quoted 9 lines
Quoted 6 lines
qapi/block-core.json +4 -0
+#
+##
+{ 'struct': 'BitmapInfo',
+  'data': { 'name': 'str', 'type': 'BitmapTypeEnum', 'granularity':

'int',
+            'count': 'int', '*flags': ['BitmapFlagEnum']

may be worth add 'has-extra-data'

Since you pointed out the difference, I agree.

Quoted 26 lines
Quoted 24 lines

+  }

qapi/block-core.json +2 -0
+}
+

  ##
  # @ImageInfoSpecificQCow2EncryptionBase:
  #

qapi/block-core.json +4 -1
@@ -69,6 +124,8 @@
   # @encrypt: details about encryption parameters; only set if image
   #           is encrypted (since 2.10)
   #
+# @bitmaps: list of image bitmaps (since 2.13)
+#
   # Since: 1.7
   ##
   { 'struct': 'ImageInfoSpecificQCow2',
@@ -77,7 +134,8 @@
         '*lazy-refcounts': 'bool',
         '*corrupt': 'bool',
         'refcount-bits': 'int',
-      '*encrypt': 'ImageInfoSpecificQCow2Encryption'
+      '*encrypt': 'ImageInfoSpecificQCow2Encryption',
+      '*bitmaps': ['BitmapInfo']
     } }
     ##
author
#18 lore raw

Re: [Qemu-devel] [RFC PATCH 06/12] qapi: add bitmap info

In reply to Vladimir Sementsov-Ogievskiy
Quoted 36 lines · Vladimir Sementsov-Ogievskiy
Quoted 35 lines · John Snow

Add some of the necessary scaffolding for reporting bitmap information.

---
  qapi/block-core.json | 60
+++++++++++++++++++++++++++++++++++++++++++++++++++-

  1 file changed, 59 insertions(+), 1 deletion(-)
qapi/block-core.json +0 -0
index c50517bff3..8f33f41ce7 100644
@@ -33,6 +33,61 @@
               'date-sec': 'int', 'date-nsec': 'int',
               'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }

  +##

qapi/block-core.json +19 -0
+# @BitmapTypeEnum:
+#
+# An enumeration of possible serialized bitmap types.
+#
+# @dirty-tracking: This bitmap records information on dirty
+#                  segments within the file.
+#
+# @unknown: This bitmap is of an unknown or reserved type.
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapTypeEnum', 'data': [ 'dirty-tracking', 'unknown' ] }
+
+##
+# @BitmapFlagEnum:
+#
+# An enumeration of possible flags for serialized bitmaps.
+#
+# @in-use: This bitmap is considered to be in-use, and may now be
Quoted 9 lines
Quoted 5 lines

inconsistent.

qapi/block-core.json +2 -0
+#
+# @auto: This bitmap must reflect any and all changes to the file it

describes.
+#        The type of this bitmap must be @DirtyTrackingBitmap.

logical, but I don't see this restriction in the spec. May be we need to update the spec

1: auto

"The bitmap must reflect all changes of the virtual disk by any application that would write to this qcow2 file (including writes, snapshot switching, etc.). The type of this bitmap must be 'dirty tracking bitmap'."

Actually, this looks correct now that I'm looking at the spec again. I've used a terser phrasing but I think it's correct.

Quoted 11 lines
Quoted 3 lines
qapi/block-core.json +2 -0
+#
+# @extra-data-compatible: This bitmap has extra information

associated with it.

no, this flag means, that extra data is compatible. So, if you don't know what is this extra data, you can read and modify the bitmap, leaving this data as is. If this flag is unset, and there are some extra data, bitmap must not be used.

Finally, this spec should be consistent (or, may be better, duplicate) spec from docs/interop/qcow2.txt..

I might suggest a rewrite of this portion of the spec as it's a little unclear to me.

I've given this portion a rewrite.

Quoted 62 lines

qapi/block-core.json

Quoted 2 lines
qapi/block-core.json +2 -0
+#
+# @unknown: This bitmap has unknown or reserved properties.

Better is only "reserved flags" (not unknown and not properties), they are reserved by spec.

Quoted 25 lines
qapi/block-core.json +5 -0
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapFlagEnum', 'data': [ 'in-use', 'auto',
+                                      'extra-data-compatible',

'unknown' ] }

qapi/block-core.json +19 -0
+
+##
+# @BitmapInfo:
+#
+# @name: The name of the bitmap.
+#
+# @type: The type of bitmap.
+#
+# @granularity: Bitmap granularity, in bytes.
+#
+# @count: Overall bitmap dirtiness, in bytes.
+#
+# @flags: Bitmap flags, if any.
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapInfo',
+  'data': { 'name': 'str', 'type': 'BitmapTypeEnum', 'granularity':
Quoted 2 lines

'int',
+            'count': 'int', '*flags': ['BitmapFlagEnum']

may be worth add 'has-extra-data'

Quoted 24 lines

+  }

qapi/block-core.json +2 -0
+}
+

  ##
  # @ImageInfoSpecificQCow2EncryptionBase:
  #

qapi/block-core.json +4 -1
@@ -69,6 +124,8 @@
   # @encrypt: details about encryption parameters; only set if image
   #           is encrypted (since 2.10)
   #
+# @bitmaps: list of image bitmaps (since 2.13)
+#
   # Since: 1.7
   ##
   { 'struct': 'ImageInfoSpecificQCow2',
@@ -77,7 +134,8 @@
         '*lazy-refcounts': 'bool',
         '*corrupt': 'bool',
         'refcount-bits': 'int',
-      '*encrypt': 'ImageInfoSpecificQCow2Encryption'
+      '*encrypt': 'ImageInfoSpecificQCow2Encryption',
+      '*bitmaps': ['BitmapInfo']
     } }
     ##
#19 lore raw

Re: [Qemu-devel] [RFC PATCH 06/12] qapi: add bitmap info

Quoted 48 lines · John Snow
Quoted 35 lines · Vladimir Sementsov-Ogievskiy
Quoted 34 lines · John Snow

Add some of the necessary scaffolding for reporting bitmap information.

---
   qapi/block-core.json | 60
+++++++++++++++++++++++++++++++++++++++++++++++++++-
   1 file changed, 59 insertions(+), 1 deletion(-)
qapi/block-core.json +18 -0
index c50517bff3..8f33f41ce7 100644
@@ -33,6 +33,61 @@
               'date-sec': 'int', 'date-nsec': 'int',
               'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
   +##
+# @BitmapTypeEnum:
+#
+# An enumeration of possible serialized bitmap types.
+#
+# @dirty-tracking: This bitmap records information on dirty
+#                  segments within the file.
+#
+# @unknown: This bitmap is of an unknown or reserved type.
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapTypeEnum', 'data': [ 'dirty-tracking', 'unknown' ] }
+
+##
+# @BitmapFlagEnum:
+#
+# An enumeration of possible flags for serialized bitmaps.
+#
Quoted 9 lines
Quoted 6 lines
qapi/block-core.json +1 -0
+# @in-use: This bitmap is considered to be in-use, and may now be

inconsistent.

qapi/block-core.json +2 -0
+#
+# @auto: This bitmap must reflect any and all changes to the file it

describes.
+#        The type of this bitmap must be @DirtyTrackingBitmap.

logical, but I don't see this restriction in the spec. May be we need to update the spec

1: auto

Quoted 7 lines

"The bitmap must reflect all changes of the virtual disk by any application that would write to this qcow2 file (including writes, snapshot switching, etc.). The type of this bitmap must be 'dirty tracking bitmap'."

Actually, this looks correct now that I'm looking at the spec again. I've used a terser phrasing but I think it's correct.

another thought: why must? We know nothing about other types.. May be for other type this flag will have similar or other meaning.. For me, this flag looks like a property of dirty-tracking bitmap, not the thing which dictates only that type.

Quoted 75 lines
Quoted 11 lines
Quoted 4 lines
qapi/block-core.json +2 -0
+#
+# @extra-data-compatible: This bitmap has extra information

associated with it.

no, this flag means, that extra data is compatible. So, if you don't know what is this extra data, you can read and modify the bitmap, leaving this data as is. If this flag is unset, and there are some extra data, bitmap must not be used.

Finally, this spec should be consistent (or, may be better, duplicate) spec from docs/interop/qcow2.txt..

I might suggest a rewrite of this portion of the spec as it's a little unclear to me.

I've given this portion a rewrite.

Quoted 59 lines

qapi/block-core.json

Quoted 2 lines
qapi/block-core.json +2 -0
+#
+# @unknown: This bitmap has unknown or reserved properties.

Better is only "reserved flags" (not unknown and not properties), they are reserved by spec.

Quoted 25 lines
qapi/block-core.json +5 -0
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapFlagEnum', 'data': [ 'in-use', 'auto',
+                                      'extra-data-compatible',

'unknown' ] }

qapi/block-core.json +19 -0
+
+##
+# @BitmapInfo:
+#
+# @name: The name of the bitmap.
+#
+# @type: The type of bitmap.
+#
+# @granularity: Bitmap granularity, in bytes.
+#
+# @count: Overall bitmap dirtiness, in bytes.
+#
+# @flags: Bitmap flags, if any.
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapInfo',
+  'data': { 'name': 'str', 'type': 'BitmapTypeEnum', 'granularity':
Quoted 2 lines

'int',
+            'count': 'int', '*flags': ['BitmapFlagEnum']

may be worth add 'has-extra-data'

Quoted 24 lines

+  }

qapi/block-core.json +6 -1
+}
+
   ##
   # @ImageInfoSpecificQCow2EncryptionBase:
   #
@@ -69,6 +124,8 @@
   # @encrypt: details about encryption parameters; only set if image
   #           is encrypted (since 2.10)
   #
+# @bitmaps: list of image bitmaps (since 2.13)
+#
   # Since: 1.7
   ##
   { 'struct': 'ImageInfoSpecificQCow2',
@@ -77,7 +134,8 @@
         '*lazy-refcounts': 'bool',
         '*corrupt': 'bool',
         'refcount-bits': 'int',
-      '*encrypt': 'ImageInfoSpecificQCow2Encryption'
+      '*encrypt': 'ImageInfoSpecificQCow2Encryption',
+      '*bitmaps': ['BitmapInfo']
     } }
     ##
Signature · 2 lines
Best regards,
Vladimir
author
#20 lore raw

Re: [Qemu-devel] [RFC PATCH 06/12] qapi: add bitmap info

Quoted 54 lines · Vladimir Sementsov-Ogievskiy
Quoted 53 lines · John Snow
Quoted 35 lines · Vladimir Sementsov-Ogievskiy
Quoted 34 lines · John Snow

Add some of the necessary scaffolding for reporting bitmap information.

---
   qapi/block-core.json | 60
+++++++++++++++++++++++++++++++++++++++++++++++++++-

   1 file changed, 59 insertions(+), 1 deletion(-)
qapi/block-core.json +0 -0
index c50517bff3..8f33f41ce7 100644
@@ -33,6 +33,61 @@
                'date-sec': 'int', 'date-nsec': 'int',
                'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }

   +##

qapi/block-core.json +18 -0
+# @BitmapTypeEnum:
+#
+# An enumeration of possible serialized bitmap types.
+#
+# @dirty-tracking: This bitmap records information on dirty
+#                  segments within the file.
+#
+# @unknown: This bitmap is of an unknown or reserved type.
+#
+# Since: 2.13
+##
+{ 'enum': 'BitmapTypeEnum', 'data': [ 'dirty-tracking', 'unknown' ] }
+
+##
+# @BitmapFlagEnum:
+#
+# An enumeration of possible flags for serialized bitmaps.
+#
Quoted 9 lines
Quoted 6 lines

+# @in-use: This bitmap is considered to be in-use, and may now be inconsistent.

qapi/block-core.json +2 -0
+#
+# @auto: This bitmap must reflect any and all changes to the file it

describes.
+#        The type of this bitmap must be @DirtyTrackingBitmap.

logical, but I don't see this restriction in the spec. May be we need to update the spec

1: auto

"The bitmap must reflect all changes of the virtual disk by any application that would write to this qcow2 file (including writes, snapshot switching, etc.). The type of this bitmap must be 'dirty tracking bitmap'."

Quoted 8 lines
Quoted 2 lines

Actually, this looks correct now that I'm looking at the spec again. I've used a terser phrasing but I think it's correct.

another thought: why must? We know nothing about other types.. May be for other type this flag will have similar or other meaning.. For me, this flag looks like a property of dirty-tracking bitmap, not the thing which dictates only that type.

Yeah, there may be other kinds that want this behavior. The restriction can probably be eliminated from the spec, I think, but that's to be handled outside of this series.

I'll remove the restriction from the QAPI flag, because what "should" or "must" happen is not relevant to what _is_ happening. :)

--js

author
#21 lore raw

RFC PATCH 7/12 qcow2-bitmap: add basic bitmaps info

In reply to John Snow

Add functions for querying the basic information inside of bitmaps. Restructure the bitmaps flags masks to facilitate providing a list of flags belonging to the bitmap(s) being queried.

---
 block/qcow2-bitmap.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 block/qcow2.c        |  7 +++++
 block/qcow2.h        |  1 +
 3 files changed, 87 insertions(+), 2 deletions(-)
block/qcow2-bitmap.c +79 -2
index 60e01abfd7..811b82743a 100644
@@ -49,8 +49,28 @@
 
 /* Bitmap directory entry flags */
 #define BME_RESERVED_FLAGS 0xfffffffcU
-#define BME_FLAG_IN_USE (1U << 0)
-#define BME_FLAG_AUTO   (1U << 1)
+
+enum BME_FLAG_BITS {
+    BME_FLAG_BIT__BEGIN = 0,
+    BME_FLAG_BIT_IN_USE = BME_FLAG_BIT__BEGIN,
+    BME_FLAG_BIT_AUTO   = 1,
+    BME_FLAG_BIT_EXTRA  = 2,
+    BME_FLAG_BIT__MAX,
+};
+
+#define BME_FLAG_IN_USE (1U << BME_FLAG_BIT_IN_USE)
+#define BME_FLAG_AUTO   (1U << BME_FLAG_BIT_AUTO)
+#define BME_FLAG_EXTRA  (1U << BME_FLAG_BIT_EXTRA)
+
+/* Correlate canonical spec values to autogenerated QAPI values */
+struct {
+    uint32_t mask;
+    int qapi_value;
+} BMEFlagMap[BME_FLAG_BIT__MAX] = {
+    [BME_FLAG_BIT_IN_USE] = { BME_FLAG_IN_USE, BITMAP_FLAG_ENUM_IN_USE },
+    [BME_FLAG_BIT_AUTO]   = { BME_FLAG_AUTO,   BITMAP_FLAG_ENUM_AUTO },
+    [BME_FLAG_BIT_EXTRA]  = { BME_FLAG_EXTRA,  BITMAP_FLAG_ENUM_EXTRA_DATA_COMPATIBLE },
+};
 
 /* bits [1, 8] U [56, 63] are reserved */
 #define BME_TABLE_ENTRY_RESERVED_MASK 0xff000000000001feULL
@@ -663,6 +683,63 @@ static void del_bitmap_list(BlockDriverState *bs)
     }
 }
 
+static BitmapFlagEnumList *get_bitmap_flags(uint32_t flags)
+{
+    int i;
+    BitmapFlagEnumList *flist = NULL;
+    BitmapFlagEnumList *ftmp;
+
+    while (flags) {
+        i = ctz32(flags);
+        ftmp = g_new0(BitmapFlagEnumList, 1);
+        if (i >= BME_FLAG_BIT__BEGIN && i < BME_FLAG_BIT__MAX) {
+            ftmp->value = BMEFlagMap[i].qapi_value;
+        } else {
+            ftmp->value = BITMAP_FLAG_ENUM_UNKNOWN;
+        }
+        ftmp->next = flist;
+        flist = ftmp;
+        flags &= ~(1 << i);
+    }
+
+    return flist;
+}
+
+BitmapInfoList *qcow2_get_bitmap_info(BlockDriverState *bs)
+{
+    Qcow2BitmapList *bm_list;
+    Qcow2Bitmap *bm;
+    BitmapInfoList *info_list = NULL;
+    BitmapInfoList *tmp;
+    BitmapInfo *bitmap_info;
+
+    bm_list = get_bitmap_list(bs, NULL);
+    if (!bm_list) {
+        return info_list;
+    }
+
+    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+        bitmap_info = g_new0(BitmapInfo, 1);
+        bitmap_info->name = g_strdup(bm->name);
+        if (bm->type == BT_DIRTY_TRACKING_BITMAP) {
+            bitmap_info->type = BITMAP_TYPE_ENUM_DIRTY_TRACKING;
+        } else {
+            bitmap_info->type = BITMAP_TYPE_ENUM_UNKNOWN;
+        }
+        bitmap_info->granularity = 1 << bm->granularity_bits;
+        bitmap_info->count = bdrv_get_dirty_count(bm->dirty_bitmap);
+        bitmap_info->flags = get_bitmap_flags(bm->flags);
+        bitmap_info->has_flags = !!bitmap_info->flags;
+
+        tmp = g_new0(BitmapInfoList, 1);
+        tmp->value = bitmap_info;
+        tmp->next = info_list;
+        info_list = tmp;
+    }
+
+    return info_list;
+}
+
 int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                                   void **refcount_table,
                                   int64_t *refcount_table_size)
block/qcow2.c +7 -0
index 7ae9000656..0b24ecb6b2 100644
@@ -3960,6 +3960,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
     BDRVQcow2State *s = bs->opaque;
     ImageInfoSpecific *spec_info;
     QCryptoBlockInfo *encrypt_info = NULL;
+    BitmapInfoList *bitmap_list = NULL;
 
     if (s->crypto != NULL) {
         encrypt_info = qcrypto_block_get_info(s->crypto, &error_abort);
@@ -4016,6 +4017,12 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
         spec_info->u.qcow2.data->encrypt = qencrypt;
     }
 
+    bitmap_list = qcow2_get_bitmap_info(bs);
+    if (bitmap_list) {
+        spec_info->u.qcow2.data->has_bitmaps = true;
+        spec_info->u.qcow2.data->bitmaps = bitmap_list;
+    }
+
     return spec_info;
 }
 
block/qcow2.h +1 -0
index 796a8c914b..76bf5fa55d 100644
@@ -686,5 +686,6 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
 void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
                                           const char *name,
                                           Error **errp);
+BitmapInfoList *qcow2_get_bitmap_info(BlockDriverState *bs);
 
 #endif
Signature · 1 line
2.14.3
#22 lore raw

Re: [Qemu-devel] [RFC PATCH 07/12] qcow2-bitmap: add basic bitmaps info

Quoted 27 lines · John Snow

Add functions for querying the basic information inside of bitmaps. Restructure the bitmaps flags masks to facilitate providing a list of flags belonging to the bitmap(s) being queried.

---
  block/qcow2-bitmap.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++--
  block/qcow2.c        |  7 +++++
  block/qcow2.h        |  1 +
  3 files changed, 87 insertions(+), 2 deletions(-)
block/qcow2-bitmap.c +6 -2
index 60e01abfd7..811b82743a 100644
@@ -49,8 +49,28 @@
 
  /* Bitmap directory entry flags */
  #define BME_RESERVED_FLAGS 0xfffffffcU
-#define BME_FLAG_IN_USE (1U << 0)
-#define BME_FLAG_AUTO   (1U << 1)
+
+enum BME_FLAG_BITS {
+    BME_FLAG_BIT__BEGIN = 0,
+    BME_FLAG_BIT_IN_USE = BME_FLAG_BIT__BEGIN,
+    BME_FLAG_BIT_AUTO   = 1,
+    BME_FLAG_BIT_EXTRA  = 2,

block/qcow2-bitmap.c line 67

Quoted 10 lines
block/qcow2-bitmap.c +10 -0
+    BME_FLAG_BIT__MAX,
+};
+
+#define BME_FLAG_IN_USE (1U << BME_FLAG_BIT_IN_USE)
+#define BME_FLAG_AUTO   (1U << BME_FLAG_BIT_AUTO)
+#define BME_FLAG_EXTRA  (1U << BME_FLAG_BIT_EXTRA)
+
+/* Correlate canonical spec values to autogenerated QAPI values */
+struct {
+    uint32_t mask;

mask is unused in this patch

Quoted 16 lines
block/qcow2-bitmap.c +9 -0
+    int qapi_value;
+} BMEFlagMap[BME_FLAG_BIT__MAX] = {
+    [BME_FLAG_BIT_IN_USE] = { BME_FLAG_IN_USE, BITMAP_FLAG_ENUM_IN_USE },
+    [BME_FLAG_BIT_AUTO]   = { BME_FLAG_AUTO,   BITMAP_FLAG_ENUM_AUTO },
+    [BME_FLAG_BIT_EXTRA]  = { BME_FLAG_EXTRA,  BITMAP_FLAG_ENUM_EXTRA_DATA_COMPATIBLE },
+};
 
  /* bits [1, 8] U [56, 63] are reserved */
  #define BME_TABLE_ENTRY_RESERVED_MASK 0xff000000000001feULL
@@ -663,6 +683,63 @@ static void del_bitmap_list(BlockDriverState *bs)
      }
  }
 
+static BitmapFlagEnumList *get_bitmap_flags(uint32_t flags)
+{
+    int i;

block/qcow2-bitmap.c static void del_bitmap_list(BlockDriverState *bs) · line 698

Quoted 10 lines
block/qcow2-bitmap.c +10 -0
+    BitmapFlagEnumList *flist = NULL;
+    BitmapFlagEnumList *ftmp;
+
+    while (flags) {
+        i = ctz32(flags);
+        ftmp = g_new0(BitmapFlagEnumList, 1);
+        if (i >= BME_FLAG_BIT__BEGIN && i < BME_FLAG_BIT__MAX) {
+            ftmp->value = BMEFlagMap[i].qapi_value;
+        } else {
+            ftmp->value = BITMAP_FLAG_ENUM_UNKNOWN;

so, there may be several "unknown" entries. It's inconsistent with "@unknown: This bitmap has unknown or reserved properties.".

Finally, can we export values for unknown flags? It should be more informative.

Quoted 83 lines
block/qcow2-bitmap.c +44 -0
+        }
+        ftmp->next = flist;
+        flist = ftmp;
+        flags &= ~(1 << i);
+    }
+
+    return flist;
+}
+
+BitmapInfoList *qcow2_get_bitmap_info(BlockDriverState *bs)
+{
+    Qcow2BitmapList *bm_list;
+    Qcow2Bitmap *bm;
+    BitmapInfoList *info_list = NULL;
+    BitmapInfoList *tmp;
+    BitmapInfo *bitmap_info;
+
+    bm_list = get_bitmap_list(bs, NULL);
+    if (!bm_list) {
+        return info_list;
+    }
+
+    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+        bitmap_info = g_new0(BitmapInfo, 1);
+        bitmap_info->name = g_strdup(bm->name);
+        if (bm->type == BT_DIRTY_TRACKING_BITMAP) {
+            bitmap_info->type = BITMAP_TYPE_ENUM_DIRTY_TRACKING;
+        } else {
+            bitmap_info->type = BITMAP_TYPE_ENUM_UNKNOWN;
+        }
+        bitmap_info->granularity = 1 << bm->granularity_bits;
+        bitmap_info->count = bdrv_get_dirty_count(bm->dirty_bitmap);
+        bitmap_info->flags = get_bitmap_flags(bm->flags);
+        bitmap_info->has_flags = !!bitmap_info->flags;
+
+        tmp = g_new0(BitmapInfoList, 1);
+        tmp->value = bitmap_info;
+        tmp->next = info_list;
+        info_list = tmp;
+    }
+
+    return info_list;
+}
+
  int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                                    void **refcount_table,
                                    int64_t *refcount_table_size)
block/qcow2.c +7 -0
index 7ae9000656..0b24ecb6b2 100644
@@ -3960,6 +3960,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
      BDRVQcow2State *s = bs->opaque;
      ImageInfoSpecific *spec_info;
      QCryptoBlockInfo *encrypt_info = NULL;
+    BitmapInfoList *bitmap_list = NULL;
 
      if (s->crypto != NULL) {
          encrypt_info = qcrypto_block_get_info(s->crypto, &error_abort);
@@ -4016,6 +4017,12 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
          spec_info->u.qcow2.data->encrypt = qencrypt;
      }
 
+    bitmap_list = qcow2_get_bitmap_info(bs);
+    if (bitmap_list) {
+        spec_info->u.qcow2.data->has_bitmaps = true;
+        spec_info->u.qcow2.data->bitmaps = bitmap_list;
+    }
+
      return spec_info;
  }
 
block/qcow2.h +1 -0
index 796a8c914b..76bf5fa55d 100644
@@ -686,5 +686,6 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
  void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
                                            const char *name,
                                            Error **errp);
+BitmapInfoList *qcow2_get_bitmap_info(BlockDriverState *bs);
 
  #endif
Signature · 2 lines
Best regards,
Vladimir
author
#23 lore raw

Re: [Qemu-devel] [RFC PATCH 07/12] qcow2-bitmap: add basic bitmaps info

Quoted 32 lines · Vladimir Sementsov-Ogievskiy
Quoted 31 lines · John Snow

Add functions for querying the basic information inside of bitmaps. Restructure the bitmaps flags masks to facilitate providing a list of flags belonging to the bitmap(s) being queried.

---
  block/qcow2-bitmap.c | 81
++++++++++++++++++++++++++++++++++++++++++++++++++--
  block/qcow2.c        |  7 +++++
  block/qcow2.h        |  1 +

  3 files changed, 87 insertions(+), 2 deletions(-)
block/qcow2-bitmap.c +10 -2
index 60e01abfd7..811b82743a 100644
@@ -49,8 +49,28 @@
     /* Bitmap directory entry flags */
   #define BME_RESERVED_FLAGS 0xfffffffcU
-#define BME_FLAG_IN_USE (1U << 0)
-#define BME_FLAG_AUTO   (1U << 1)
+
+enum BME_FLAG_BITS {
+    BME_FLAG_BIT__BEGIN = 0,
+    BME_FLAG_BIT_IN_USE = BME_FLAG_BIT__BEGIN,
+    BME_FLAG_BIT_AUTO   = 1,
+    BME_FLAG_BIT_EXTRA  = 2,
+    BME_FLAG_BIT__MAX,
+};
+
+#define BME_FLAG_IN_USE (1U << BME_FLAG_BIT_IN_USE)
Quoted 9 lines

block/qcow2-bitmap.c line 67

Quoted 6 lines
block/qcow2-bitmap.c +6 -0
+#define BME_FLAG_AUTO   (1U << BME_FLAG_BIT_AUTO)
+#define BME_FLAG_EXTRA  (1U << BME_FLAG_BIT_EXTRA)
+
+/* Correlate canonical spec values to autogenerated QAPI values */
+struct {
+    uint32_t mask;

mask is unused in this patch

It sure is! I'll remove it. It's an artifact from an earlier revision.

Quoted 21 lines
Quoted 21 lines
block/qcow2-bitmap.c +13 -0
+    int qapi_value;
+} BMEFlagMap[BME_FLAG_BIT__MAX] = {
+    [BME_FLAG_BIT_IN_USE] = { BME_FLAG_IN_USE,
 BITMAP_FLAG_ENUM_IN_USE },
+    [BME_FLAG_BIT_AUTO]   = { BME_FLAG_AUTO,   BITMAP_FLAG_ENUM_AUTO },
+    [BME_FLAG_BIT_EXTRA]  = { BME_FLAG_EXTRA, 
 BITMAP_FLAG_ENUM_EXTRA_DATA_COMPATIBLE },
+};
     /* bits [1, 8] U [56, 63] are reserved */
   #define BME_TABLE_ENTRY_RESERVED_MASK 0xff000000000001feULL
@@ -663,6 +683,63 @@ static void del_bitmap_list(BlockDriverState *bs)
       }
   }
   +static BitmapFlagEnumList *get_bitmap_flags(uint32_t flags)
+{
+    int i;
+    BitmapFlagEnumList *flist = NULL;
+    BitmapFlagEnumList *ftmp;
+
+    while (flags) {
+        i = ctz32(flags);
Quoted 9 lines

block/qcow2-bitmap.c static void del_bitmap_list(BlockDriverState *bs) · line 698

Quoted 5 lines
block/qcow2-bitmap.c +5 -0
+        ftmp = g_new0(BitmapFlagEnumList, 1);
+        if (i >= BME_FLAG_BIT__BEGIN && i < BME_FLAG_BIT__MAX) {
+            ftmp->value = BMEFlagMap[i].qapi_value;
+        } else {
+            ftmp->value = BITMAP_FLAG_ENUM_UNKNOWN;

so, there may be several "unknown" entries. It's inconsistent with "@unknown: This bitmap has unknown or reserved properties.".

Based on your feedback from the previous patch I'll probably just call this @reserved for any unrecognized flag.

Quoted 3 lines

Finally, can we export values for unknown flags? It should be more informative.

I'll see what I can do -- I hate losing this information too.

author
#24 lore raw

Re: [Qemu-devel] [RFC PATCH 07/12] qcow2-bitmap: add basic bitmaps info

In reply to Vladimir Sementsov-Ogievskiy
Quoted 71 lines · Vladimir Sementsov-Ogievskiy
Quoted 27 lines · John Snow

Add functions for querying the basic information inside of bitmaps. Restructure the bitmaps flags masks to facilitate providing a list of flags belonging to the bitmap(s) being queried.

---
  block/qcow2-bitmap.c | 81
++++++++++++++++++++++++++++++++++++++++++++++++++--
  block/qcow2.c        |  7 +++++
  block/qcow2.h        |  1 +

  3 files changed, 87 insertions(+), 2 deletions(-)
block/qcow2-bitmap.c +6 -2
index 60e01abfd7..811b82743a 100644
@@ -49,8 +49,28 @@
     /* Bitmap directory entry flags */
   #define BME_RESERVED_FLAGS 0xfffffffcU
-#define BME_FLAG_IN_USE (1U << 0)
-#define BME_FLAG_AUTO   (1U << 1)
+
+enum BME_FLAG_BITS {
+    BME_FLAG_BIT__BEGIN = 0,
+    BME_FLAG_BIT_IN_USE = BME_FLAG_BIT__BEGIN,
+    BME_FLAG_BIT_AUTO   = 1,
+    BME_FLAG_BIT_EXTRA  = 2,

block/qcow2-bitmap.c line 66

Quoted 10 lines
block/qcow2-bitmap.c +10 -0
+    BME_FLAG_BIT__MAX,
+};
+
+#define BME_FLAG_IN_USE (1U << BME_FLAG_BIT_IN_USE)
+#define BME_FLAG_AUTO   (1U << BME_FLAG_BIT_AUTO)
+#define BME_FLAG_EXTRA  (1U << BME_FLAG_BIT_EXTRA)
+
+/* Correlate canonical spec values to autogenerated QAPI values */
+struct {
+    uint32_t mask;

mask is unused in this patch

Quoted 16 lines
block/qcow2-bitmap.c +8 -0
+    int qapi_value;
+} BMEFlagMap[BME_FLAG_BIT__MAX] = {
+    [BME_FLAG_BIT_IN_USE] = { BME_FLAG_IN_USE,
 BITMAP_FLAG_ENUM_IN_USE },
+    [BME_FLAG_BIT_AUTO]   = { BME_FLAG_AUTO,   BITMAP_FLAG_ENUM_AUTO },
+    [BME_FLAG_BIT_EXTRA]  = { BME_FLAG_EXTRA, 
 BITMAP_FLAG_ENUM_EXTRA_DATA_COMPATIBLE },
+};
     /* bits [1, 8] U [56, 63] are reserved */
   #define BME_TABLE_ENTRY_RESERVED_MASK 0xff000000000001feULL
@@ -663,6 +683,63 @@ static void del_bitmap_list(BlockDriverState *bs)
       }
   }
   +static BitmapFlagEnumList *get_bitmap_flags(uint32_t flags)
+{
+    int i;

block/qcow2-bitmap.c static void del_bitmap_list(BlockDriverState *bs) · line 697

Quoted 10 lines
block/qcow2-bitmap.c +10 -0
+    BitmapFlagEnumList *flist = NULL;
+    BitmapFlagEnumList *ftmp;
+
+    while (flags) {
+        i = ctz32(flags);
+        ftmp = g_new0(BitmapFlagEnumList, 1);
+        if (i >= BME_FLAG_BIT__BEGIN && i < BME_FLAG_BIT__MAX) {
+            ftmp->value = BMEFlagMap[i].qapi_value;
+        } else {
+            ftmp->value = BITMAP_FLAG_ENUM_UNKNOWN;

so, there may be several "unknown" entries. It's inconsistent with "@unknown: This bitmap has unknown or reserved properties.".

Quoted 3 lines

Finally, can we export values for unknown flags? It should be more informative.

I changed my mind -- qemu-img is not a debugging tool and shouldn't get lost in the weeds trying to enumerate the different kinds of reserved values that are set.

It's an error to have them set, or not -- which ones specifically is not information that an end-user need know or care about, and I don't want to create an extra structure to contain the value.

I'm going to rewrite this loop to just only ever have one unknown value at a maximum.

--js

#25 lore raw

Re: [Qemu-devel] [RFC PATCH 07/12] qcow2-bitmap: add basic bitmaps info

Quoted 78 lines · John Snow
Quoted 69 lines · Vladimir Sementsov-Ogievskiy
Quoted 27 lines · John Snow

Add functions for querying the basic information inside of bitmaps. Restructure the bitmaps flags masks to facilitate providing a list of flags belonging to the bitmap(s) being queried.

---
   block/qcow2-bitmap.c | 81
++++++++++++++++++++++++++++++++++++++++++++++++++--
   block/qcow2.c        |  7 +++++
   block/qcow2.h        |  1 +
   3 files changed, 87 insertions(+), 2 deletions(-)
block/qcow2-bitmap.c +6 -2
index 60e01abfd7..811b82743a 100644
@@ -49,8 +49,28 @@
     /* Bitmap directory entry flags */
   #define BME_RESERVED_FLAGS 0xfffffffcU
-#define BME_FLAG_IN_USE (1U << 0)
-#define BME_FLAG_AUTO   (1U << 1)
+
+enum BME_FLAG_BITS {
+    BME_FLAG_BIT__BEGIN = 0,
+    BME_FLAG_BIT_IN_USE = BME_FLAG_BIT__BEGIN,
+    BME_FLAG_BIT_AUTO   = 1,
+    BME_FLAG_BIT_EXTRA  = 2,

block/qcow2-bitmap.c line 66

Quoted 10 lines
block/qcow2-bitmap.c +10 -0
+    BME_FLAG_BIT__MAX,
+};
+
+#define BME_FLAG_IN_USE (1U << BME_FLAG_BIT_IN_USE)
+#define BME_FLAG_AUTO   (1U << BME_FLAG_BIT_AUTO)
+#define BME_FLAG_EXTRA  (1U << BME_FLAG_BIT_EXTRA)
+
+/* Correlate canonical spec values to autogenerated QAPI values */
+struct {
+    uint32_t mask;

mask is unused in this patch

Quoted 16 lines
block/qcow2-bitmap.c +8 -0
+    int qapi_value;
+} BMEFlagMap[BME_FLAG_BIT__MAX] = {
+    [BME_FLAG_BIT_IN_USE] = { BME_FLAG_IN_USE,
 BITMAP_FLAG_ENUM_IN_USE },
+    [BME_FLAG_BIT_AUTO]   = { BME_FLAG_AUTO,   BITMAP_FLAG_ENUM_AUTO },
+    [BME_FLAG_BIT_EXTRA]  = { BME_FLAG_EXTRA,
 BITMAP_FLAG_ENUM_EXTRA_DATA_COMPATIBLE },
+};
     /* bits [1, 8] U [56, 63] are reserved */
   #define BME_TABLE_ENTRY_RESERVED_MASK 0xff000000000001feULL
@@ -663,6 +683,63 @@ static void del_bitmap_list(BlockDriverState *bs)
       }
   }
   +static BitmapFlagEnumList *get_bitmap_flags(uint32_t flags)
+{
+    int i;

block/qcow2-bitmap.c static void del_bitmap_list(BlockDriverState *bs) · line 697

Quoted 10 lines
block/qcow2-bitmap.c +10 -0
+    BitmapFlagEnumList *flist = NULL;
+    BitmapFlagEnumList *ftmp;
+
+    while (flags) {
+        i = ctz32(flags);
+        ftmp = g_new0(BitmapFlagEnumList, 1);
+        if (i >= BME_FLAG_BIT__BEGIN && i < BME_FLAG_BIT__MAX) {
+            ftmp->value = BMEFlagMap[i].qapi_value;
+        } else {
+            ftmp->value = BITMAP_FLAG_ENUM_UNKNOWN;

so, there may be several "unknown" entries. It's inconsistent with "@unknown: This bitmap has unknown or reserved properties.".

Quoted 3 lines

Finally, can we export values for unknown flags? It should be more informative.

I changed my mind -- qemu-img is not a debugging tool and shouldn't get lost in the weeds trying to enumerate the different kinds of reserved values that are set.

Quoted 9 lines

It's an error to have them set, or not -- which ones specifically is not information that an end-user need know or care about, and I don't want to create an extra structure to contain the value.

I'm going to rewrite this loop to just only ever have one unknown value at a maximum.

--js

Ok, I agree.

Signature · 2 lines
Best regards,
Vladimir
author
#26 lore raw

RFC PATCH 8/12 qjson: allow caller to ask for arbitrary indent

In reply to John Snow

The function already exists, just expose it.

---
 include/qapi/qmp/qjson.h |  1 +
 qobject/qjson.c          | 21 +++++++++++----------
 2 files changed, 12 insertions(+), 10 deletions(-)
include/qapi/qmp/qjson.h +1 -0
index b274ac3a86..0e8624c1fa 100644
@@ -19,6 +19,7 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
 QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp)
     GCC_FMT_ATTR(1, 0);
 
+QString *qobject_to_json_opt(const QObject *obj, int pretty, int indent);
 QString *qobject_to_json(const QObject *obj);
 QString *qobject_to_json_pretty(const QObject *obj);
 
qobject/qjson.c +11 -10
index 9816a65c7d..d603e1cce1 100644
@@ -252,20 +252,21 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
     }
 }
 
+QString *qobject_to_json_opt(const QObject *obj, int pretty, int indent)
+{
+    QString *str = qstring_new();
+
+    to_json(obj, str, pretty, indent);
+
+    return str;
+}
+
 QString *qobject_to_json(const QObject *obj)
 {
-    QString *str = qstring_new();
-
-    to_json(obj, str, 0, 0);
-
-    return str;
+    return qobject_to_json_opt(obj, 0, 0);
 }
 
 QString *qobject_to_json_pretty(const QObject *obj)
 {
-    QString *str = qstring_new();
-
-    to_json(obj, str, 1, 0);
-
-    return str;
+    return qobject_to_json_opt(obj, 1, 0);
 }
Signature · 1 line
2.14.3
#27 lore raw

Re: [Qemu-devel] [RFC PATCH 08/12] qjson: allow caller to ask for arbitrary indent

Quoted 13 lines · John Snow

The function already exists, just expose it.

---
  include/qapi/qmp/qjson.h |  1 +
  qobject/qjson.c          | 21 +++++++++++----------
  2 files changed, 12 insertions(+), 10 deletions(-)
include/qapi/qmp/qjson.h +0 -0
index b274ac3a86..0e8624c1fa 100644

include/qapi/qmp/qjson.h QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); · line 22

Quoted 4 lines
include/qapi/qmp/qjson.h +1 -0
@@ -19,6 +19,7 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
  QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp)
      GCC_FMT_ATTR(1, 0);
 
+QString *qobject_to_json_opt(const QObject *obj, int pretty, int indent);

pretty seems like it is better as a bool. Even though to_json() isn't typed correctly, that's no excuse for the public function to copy a bad interface (and maybe it's worth a separate cleanup patch to fix to_json() first).

Signature · 3 lines
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org
author
#28 lore raw

Re: [Qemu-devel] [RFC PATCH 08/12] qjson: allow caller to ask for arbitrary indent

Quoted 9 lines · Eric Blake
Quoted 8 lines · John Snow

The function already exists, just expose it.

---
  include/qapi/qmp/qjson.h |  1 + qobject/qjson.c          | 21 +++++++++++----------

  2 files changed, 12 insertions(+), 10 deletions(-)
Quoted 17 lines
Quoted 11 lines
include/qapi/qmp/qjson.h +0 -0
index b274ac3a86..0e8624c1fa 100644
@@ -19,6 +19,7 @@ QObject *qobject_from_jsonf(const char *string, ...)
 GCC_FMT_ATTR(1, 2);

  QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp)
      GCC_FMT_ATTR(1, 0);
  +QString *qobject_to_json_opt(const QObject *obj, int pretty, int indent);

pretty seems like it is better as a bool.  Even though to_json() isn't typed correctly, that's no excuse for the public function to copy a bad interface (and maybe it's worth a separate cleanup patch to fix to_json() first).

Eh, sure, why not. Done.

author
#29 lore raw

RFC PATCH 9/12 qapi/block-core: add BitmapMapping and BitmapEntry structs

In reply to John Snow

Add two new structures for detailing the marked regions of bitmaps as saved in e.g. qcow2 files.

---
 qapi/block-core.json | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)
qapi/block-core.json +32 -0
index 8f33f41ce7..de8ad73a78 100644
@@ -298,6 +298,38 @@
            'zero': 'bool', 'depth': 'int', '*offset': 'int',
            '*filename': 'str' } }
 
+##
+# @BitmapEntry:
+#
+# Dirty Bitmap region information for a virtual block range
+#
+# @offset: the start byte of the dirty virtual range
+#
+# @length: the number of bytes of the dirty virtual range
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapEntry',
+  'data': { 'offset': 'int', 'length': 'int' } }
+
+##
+# @BitmapMapping:
+#
+# List of described regions correlated to a named bitmap.
+#
+# @name: The name of the bitmap whose range is described here
+#
+# @entries: A list of zero or more @BitmapEntry elements representing
+#           the range(s) described by the bitmap.
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapMapping',
+  'data': { 'name': 'str',
+            'entries': [ 'BitmapEntry' ] } }
+
 ##
 # @BlockdevCacheInfo:
 #
Signature · 1 line
2.14.3
#30 lore raw

Re: [Qemu-devel] [RFC PATCH 09/12] qapi/block-core: add BitmapMapping and BitmapEntry structs

Quoted 30 lines · John Snow

Add two new structures for detailing the marked regions of bitmaps as saved in e.g. qcow2 files.

---
  qapi/block-core.json | 32 ++++++++++++++++++++++++++++++++
  1 file changed, 32 insertions(+)
qapi/block-core.json +14 -0
index 8f33f41ce7..de8ad73a78 100644
@@ -298,6 +298,38 @@
             'zero': 'bool', 'depth': 'int', '*offset': 'int',
             '*filename': 'str' } }
 
+##
+# @BitmapEntry:
+#
+# Dirty Bitmap region information for a virtual block range
+#
+# @offset: the start byte of the dirty virtual range
+#
+# @length: the number of bytes of the dirty virtual range
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapEntry',
+  'data': { 'offset': 'int', 'length': 'int' } }

qapi/block-core.json line 324

Quoted 10 lines
qapi/block-core.json +10 -0
+
+##
+# @BitmapMapping:
+#
+# List of described regions correlated to a named bitmap.
+#
+# @name: The name of the bitmap whose range is described here
+#
+# @entries: A list of zero or more @BitmapEntry elements representing
+#           the range(s) described by the bitmap.

Is it also worth documenting that the list will be in ascending order, with no overlaps (no two entries covering the same offset); and in fact with a gap between all entries (as otherwise those two consecutive entries could have been consolidated to one)?

Quoted 12 lines
qapi/block-core.json +8 -0
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapMapping',
+  'data': { 'name': 'str',
+            'entries': [ 'BitmapEntry' ] } }
+
  ##
  # @BlockdevCacheInfo:
  #
Signature · 3 lines
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org
author
#31 lore raw

Re: [Qemu-devel] [RFC PATCH 09/12] qapi/block-core: add BitmapMapping and BitmapEntry structs

Quoted 6 lines · Eric Blake
Quoted 6 lines
qapi/block-core.json +6 -0
+##
+# @BitmapMapping:
+#
+# List of described regions correlated to a named bitmap.
+#
+# @name: The name of the bitmap whose range is described here
Quoted 9 lines

qapi/block-core.json line 324

Quoted 3 lines
qapi/block-core.json +3 -0
+#
+# @entries: A list of zero or more @BitmapEntry elements representing
+#           the range(s) described by the bitmap.

Is it also worth documenting that the list will be in ascending order, with no overlaps (no two entries covering the same offset); and in fact with a gap between all entries (as otherwise those two consecutive entries could have been consolidated to one)?

Hm. I didn't necessarily want to guarantee the order, but being more specific about the output will assist legibility of the spec.

I'll amend the documentation and make some stronger guarantees.

Quoted 11 lines
Quoted 11 lines
qapi/block-core.json +8 -0
+#
+# Since: 2.13
+#
+##
+{ 'struct': 'BitmapMapping',
+  'data': { 'name': 'str',
+            'entries': [ 'BitmapEntry' ] } }
+

  ##
  # @BlockdevCacheInfo:
  #

author
#32 lore raw

RFC PATCH 10/12 qemu-img: split off common chunk of map command

In reply to John Snow

It will be re-used for a bitmap listing command.

---
 qemu-img.c | 192 +++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 110 insertions(+), 82 deletions(-)
qemu-img.c +110 -82
index ea62d2d61e..e31e38f674 100644
@@ -69,8 +69,8 @@ enum {
 };
 
 typedef enum OutputFormat {
-    OFORMAT_JSON,
     OFORMAT_HUMAN,
+    OFORMAT_JSON,
 } OutputFormat;
 
 /* Default to cache=writeback as data integrity is not important for qemu-img */
@@ -2728,96 +2728,122 @@ static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next)
     return true;
 }
 
+typedef struct CommonOpts {
+    OutputFormat output_format;
+    const char *filename;
+    const char *fmt;
+    bool force_share;
+    bool image_opts;
+} CommonOpts;
+
+static int parse_opts_common(CommonOpts *opts, int argc, char **argv)
+{
+    int c;
+
+    for (;;) {
+        int option_index = 0;
+        static const struct option long_options[] = {
+            {"help", no_argument, 0, 'h'},
+            {"format", required_argument, 0, 'f'},
+            {"output", required_argument, 0, OPTION_OUTPUT},
+            {"object", required_argument, 0, OPTION_OBJECT},
+            {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+            {"force-share", no_argument, 0, 'U'},
+            {0, 0, 0, 0}
+        };
+        c = getopt_long(argc, argv, ":f:hU",
+                        long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+        switch (c) {
+        case ':':
+            missing_argument(argv[optind - 1]);
+            break;
+        case '?':
+            unrecognized_option(argv[optind - 1]);
+            break;
+        case 'h':
+            help();
+            break;
+        case 'f':
+            opts->fmt = optarg;
+            break;
+        case 'U':
+            opts->force_share = true;
+            break;
+        case OPTION_OUTPUT:
+            if (optarg && !strcmp(optarg, "json")) {
+                opts->output_format = OFORMAT_JSON;
+            } else if (optarg && !strcmp(optarg, "human")) {
+                opts->output_format = OFORMAT_HUMAN;
+            } else {
+                error_report("--output must be used with human or json as argument.");
+                return -EINVAL;
+            }
+            break;
+        case OPTION_OBJECT: {
+            QemuOpts *opts;
+            opts = qemu_opts_parse_noisily(&qemu_object_opts,
+                                           optarg, true);
+            if (!opts) {
+                return -EINVAL;
+            }
+        }   break;
+        case OPTION_IMAGE_OPTS:
+            opts->image_opts = true;
+            break;
+        }
+    }
+    if (qemu_opts_foreach(&qemu_object_opts,
+                          user_creatable_add_opts_foreach,
+                          NULL, NULL)) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static void parse_positional(int argc, char *argv[],
+                             bool opt, const char **str, const char *name) {
+    if (!opt && optind >= argc) {
+        error_exit("Expecting '%s' positional parameter", name);
+    } else if (optind < argc) {
+        *str = argv[optind++];
+    }
+}
+
+static void parse_unexpected(int argc, char *argv[]) {
+    if (optind != argc) {
+        error_exit("Unexpected argument '%s'\n", argv[optind]);
+    }
+}
+
 static int img_map(int argc, char **argv)
 {
-    int c;
-    OutputFormat output_format = OFORMAT_HUMAN;
     BlockBackend *blk;
     BlockDriverState *bs;
-    const char *filename, *fmt, *output;
     int64_t length;
+    int ret = 0;
     MapEntry curr = { .length = 0 }, next;
-    int ret = 0;
-    bool image_opts = false;
-    bool force_share = false;
+    CommonOpts *opts;
 
-    fmt = NULL;
-    output = NULL;
-    for (;;) {
-        int option_index = 0;
-        static const struct option long_options[] = {
-            {"help", no_argument, 0, 'h'},
-            {"format", required_argument, 0, 'f'},
-            {"output", required_argument, 0, OPTION_OUTPUT},
-            {"object", required_argument, 0, OPTION_OBJECT},
-            {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
-            {"force-share", no_argument, 0, 'U'},
-            {0, 0, 0, 0}
-        };
-        c = getopt_long(argc, argv, ":f:hU",
-                        long_options, &option_index);
-        if (c == -1) {
-            break;
-        }
-        switch (c) {
-        case ':':
-            missing_argument(argv[optind - 1]);
-            break;
-        case '?':
-            unrecognized_option(argv[optind - 1]);
-            break;
-        case 'h':
-            help();
-            break;
-        case 'f':
-            fmt = optarg;
-            break;
-        case 'U':
-            force_share = true;
-            break;
-        case OPTION_OUTPUT:
-            output = optarg;
-            break;
-        case OPTION_OBJECT: {
-            QemuOpts *opts;
-            opts = qemu_opts_parse_noisily(&qemu_object_opts,
-                                           optarg, true);
-            if (!opts) {
-                return 1;
-            }
-        }   break;
-        case OPTION_IMAGE_OPTS:
-            image_opts = true;
-            break;
-        }
-    }
-    if (optind != argc - 1) {
-        error_exit("Expecting one image file name");
-    }
-    filename = argv[optind];
-
-    if (output && !strcmp(output, "json")) {
-        output_format = OFORMAT_JSON;
-    } else if (output && !strcmp(output, "human")) {
-        output_format = OFORMAT_HUMAN;
-    } else if (output) {
-        error_report("--output must be used with human or json as argument.");
-        return 1;
-    }
-
-    if (qemu_opts_foreach(&qemu_object_opts,
-                          user_creatable_add_opts_foreach,
-                          NULL, NULL)) {
-        return 1;
+    opts = g_new0(CommonOpts, 1);
+    if (parse_opts_common(opts, argc, argv)) {
+        return EXIT_FAILURE;
     }
+    parse_positional(argc, argv, 0, &opts->filename, "filename");
+    parse_unexpected(argc, argv);
 
-    blk = img_open(image_opts, filename, fmt, 0, false, false, force_share);
+    blk = img_open(opts->image_opts, opts->filename, opts->fmt, 0,
+                   false, false, opts->force_share);
     if (!blk) {
-        return 1;
+        ret = -1;
+        goto out1;
     }
     bs = blk_bs(blk);
 
-    if (output_format == OFORMAT_HUMAN) {
+    if (opts->output_format == OFORMAT_HUMAN) {
         printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File");
     }
 
@@ -2832,7 +2858,7 @@ static int img_map(int argc, char **argv)
 
         if (ret < 0) {
             error_report("Could not read file metadata: %s", strerror(-ret));
-            goto out;
+            goto out2;
         }
 
         if (entry_mergeable(&curr, &next)) {
@@ -2841,15 +2867,17 @@ static int img_map(int argc, char **argv)
         }
 
         if (curr.length > 0) {
-            dump_map_entry(output_format, &curr, &next);
+            dump_map_entry(opts->output_format, &curr, &next);
         }
         curr = next;
     }
 
-    dump_map_entry(output_format, &curr, NULL);
+    dump_map_entry(opts->output_format, &curr, NULL);
 
-out:
+out2:
     blk_unref(blk);
+out1:
+    g_free(opts);
     return ret < 0;
 }
 
Signature · 1 line
2.14.3
author
#33 lore raw

RFC PATCH 11/12 qemu-img: add bitmap dump

In reply to John Snow
---
 qemu-img-cmds.hx |   6 ++
 qemu-img.c       | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 191 insertions(+)
qemu-img-cmds.hx +6 -0
index 2fe31893cf..d25f359f5a 100644
@@ -22,6 +22,12 @@ STEXI
 @item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename}
 ETEXI
 
+DEF("bitmap", img_bitmap,
+    "bitmap dump [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-U] filename [bitmap]")
+STEXI
+@item bitmap dump [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename} [@var{bitmap}]
+ETEXI
+
 DEF("check", img_check,
     "check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename")
 STEXI
qemu-img.c +185 -0

191 lines not on this page · raw message

Signature · 1 line
2.14.3
author
#34 lore raw

RFC PATCH 12/12 qemu-img: add bitmap clear

In reply to John Snow
---
 qemu-img-cmds.hx |  4 ++--
 qemu-img.c       | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 46 insertions(+), 4 deletions(-)
qemu-img-cmds.hx +2 -2
index d25f359f5a..7b6ec73488 100644
@@ -23,9 +23,9 @@ STEXI
 ETEXI
 
 DEF("bitmap", img_bitmap,
-    "bitmap dump [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-U] filename [bitmap]")
+    "bitmap <dump|clear> [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-U] filename [bitmap]")
 STEXI
-@item bitmap dump [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename} [@var{bitmap}]
+@item bitmap <dump|clear> [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename} [@var{bitmap}]
 ETEXI
 
 DEF("check", img_check,
qemu-img.c +44 -2

69 lines not on this page · raw message

Signature · 1 line
2.14.3
#35 lore raw
In reply to John Snow
This series seems to have some coding style problems. See output below for more information:

Hi,

This series seems to have some coding style problems. See output below for more information:

Type: series
Message-id: [email protected]
Subject: [Qemu-devel] [RFC PATCH 00/12] qemu-img: add bitmap queries

=== TEST SCRIPT BEGIN ===
#!/bin/bash

BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0

git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram

commits="$(git log --format=%H --reverse $BASE..)" for c in $commits; do

    echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
    if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
        failed=1
        echo
    fi
    n=$((n+1))

done

exit $failed
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
* [new tag] patchew/[email protected] -> patchew/[email protected]
Switched to a new branch 'test'
98c78cde32 qemu-img: add bitmap clear
64b46d376f qemu-img: add bitmap dump
e820e6d20d qemu-img: split off common chunk of map command 15894bec9b qapi/block-core: add BitmapMapping and BitmapEntry structs a09d1c3a65 qjson: allow caller to ask for arbitrary indent 384e244766 qcow2-bitmap: add basic bitmaps info
35cf9ace22 qapi: add bitmap info
36be89af25 qcow2-bitmap: track bitmap type
d347564ea3 qcow2/dirty-bitmaps: load IN_USE bitmaps if disk is RO c554bff42b block/qcow2-bitmap: avoid adjusting bm->flags for RO bitmaps 9d00c36fba qcow2/dirty-bitmap: cache loaded bitmaps 7f34c78783 qcow2-bitmap: cache bm_list

=== OUTPUT BEGIN ===
Checking PATCH 1/12: qcow2-bitmap: cache bm_list...
Checking PATCH 2/12: qcow2/dirty-bitmap: cache loaded bitmaps... Checking PATCH 3/12: block/qcow2-bitmap: avoid adjusting bm->flags for RO bitmaps... Checking PATCH 4/12: qcow2/dirty-bitmaps: load IN_USE bitmaps if disk is RO... Checking PATCH 5/12: qcow2-bitmap: track bitmap type...
Checking PATCH 6/12: qapi: add bitmap info...
Checking PATCH 7/12: qcow2-bitmap: add basic bitmaps info...
WARNING: line over 80 characters
#43: FILE: block/qcow2-bitmap.c:72:
+ [BME_FLAG_BIT_EXTRA] = { BME_FLAG_EXTRA, BITMAP_FLAG_ENUM_EXTRA_DATA_COMPATIBLE },

total: 0 errors, 1 warnings, 118 lines checked

Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 8/12: qjson: allow caller to ask for arbitrary indent... Checking PATCH 9/12: qapi/block-core: add BitmapMapping and BitmapEntry structs... Checking PATCH 10/12: qemu-img: split off common chunk of map command...
WARNING: line over 80 characters
#74: FILE: qemu-img.c:2781:
+ error_report("--output must be used with human or json as argument.");

ERROR: open brace '{' following function declarations go on the next line #127: FILE: qemu-img.c:2816:
+static void parse_unexpected(int argc, char *argv[]) {

total: 1 errors, 1 warnings, 173 lines checked

Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS.

Checking PATCH 11/12: qemu-img: add bitmap dump...
ERROR: space prohibited after that '*' (ctx:BxW) #179: FILE: qemu-img.c:3029:

+    int (* handler)(BlockDriverState *b, BitmapOpts *opts);
          ^

total: 1 errors, 0 warnings, 203 lines checked

Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS.

Checking PATCH 12/12: qemu-img: add bitmap clear... === OUTPUT END ===

Test command exited with code: 1

---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to [email protected]

#36 lore raw
In reply to John Snow
Quoted 21 lines · John Snow

Allow qemu-img to show information about bitmaps stored in qcow2 images. Add a 'bitmap' meta-command with 'dump' sub-command to retrieve a list of dirty regions in bitmaps stored in a qcow2 image.

RFC:
- I am not 1000% convinced the bm_list caching is perfectly safe, especially with respect to migration and inactivation. There are more efficiencies and tweaks I can make, but I think this is the minimal set.

- I decided not to gate bitmap info in the "info" command behind extra flags.

- Bitmap data-gathering in "bitmap dump" could be made more space-efficient by just reporting one segment at a time, probably.

- bitmap rm and bitmap mk need extra work, so I am not submitting them just yet: rm needs more work around the remove persistence API, and make needs more work around the "can add" API.

Quoted 6 lines

- None of these commands will work with "in use" bitmaps; we need qemu-img check -r bitmaps support for this. I'm not sure what the right behavior to "fix" in-use bitmaps should be:
- Cleared: This might be dangerous.
- Fully Set: This is safer, but stupid.
- Deleted: This might be the best option.

I think it's the only valid option.

Quoted 26 lines

- Un-set in-use: VERY dangerous; would rather not.

John Snow (12):
qcow2-bitmap: cache bm_list
qcow2/dirty-bitmap: cache loaded bitmaps block/qcow2-bitmap: avoid adjusting bm->flags for RO bitmaps qcow2/dirty-bitmaps: load IN_USE bitmaps if disk is RO
qcow2-bitmap: track bitmap type
qapi: add bitmap info
qcow2-bitmap: add basic bitmaps info
qjson: allow caller to ask for arbitrary indent qapi/block-core: add BitmapMapping and BitmapEntry structs
qemu-img: split off common chunk of map command
qemu-img: add bitmap dump
qemu-img: add bitmap clear

  block/qcow2-bitmap.c     | 220 +++++++++++++++++--------
  block/qcow2.c            |   9 +
  block/qcow2.h            |   3 +
  include/qapi/qmp/qjson.h |   1 +
  qapi/block-core.json     |  92 ++++++++++-
  qemu-img-cmds.hx         |   6 +
  qemu-img.c               | 419 +++++++++++++++++++++++++++++++++++++----------
  qobject/qjson.c          |  21 +--
  8 files changed, 612 insertions(+), 159 deletions(-)
Signature · 2 lines
Best regards,
Vladimir