diff mbox

[v2,5/9] qcow2: Rework qcow2_snapshot_create error handling

Message ID 1321640945-9827-6-git-send-email-kwolf@redhat.com
State New
Headers show

Commit Message

Kevin Wolf Nov. 18, 2011, 6:29 p.m. UTC
Increase refcounts only after allocating a new L1 table has succeeded in
order to make leaks less likely. If writing the snapshot table fails,
revert in-memory state to be consistent with that on disk.

While at it, make it return the real error codes instead of -1.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block/qcow2-snapshot.c |   55 +++++++++++++++++++++++++++++++++++------------
 1 files changed, 41 insertions(+), 14 deletions(-)

Comments

Stefan Hajnoczi Nov. 21, 2011, 4:47 p.m. UTC | #1
On Fri, Nov 18, 2011 at 6:29 PM, Kevin Wolf <kwolf@redhat.com> wrote:
> +    /*
> +     * Increase the refcounts of all clusters and make sure everything is
> +     * stable on disk before updating the snapshot table to contain a pointer
> +     * to the new L1 table.
> +     */
> +    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
> +    if (ret < 0) {
> +        goto fail;
> +    }
> +
> +    ret = bdrv_flush(bs->file);

Do we need to explicitly flush the qcow2 cache to ensure metadata
reaches the disk?

Stefan
Kevin Wolf Nov. 22, 2011, 9:14 a.m. UTC | #2
Am 21.11.2011 17:47, schrieb Stefan Hajnoczi:
> On Fri, Nov 18, 2011 at 6:29 PM, Kevin Wolf <kwolf@redhat.com> wrote:
>> +    /*
>> +     * Increase the refcounts of all clusters and make sure everything is
>> +     * stable on disk before updating the snapshot table to contain a pointer
>> +     * to the new L1 table.
>> +     */
>> +    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
>> +    if (ret < 0) {
>> +        goto fail;
>> +    }
>> +
>> +    ret = bdrv_flush(bs->file);
> 
> Do we need to explicitly flush the qcow2 cache to ensure metadata
> reaches the disk?

Yes, I think this should be a bdrv_flush(bs). I'm not completely sure if
it is really required, but I couldn't immediately tell why it's safe and
this isn't a fast path anyway, so I'll replace this before merging the
series (won't send out a v3 for this).

Kevin
Stefan Hajnoczi Nov. 22, 2011, 9:45 a.m. UTC | #3
On Tue, Nov 22, 2011 at 9:14 AM, Kevin Wolf <kwolf@redhat.com> wrote:
> Am 21.11.2011 17:47, schrieb Stefan Hajnoczi:
>> On Fri, Nov 18, 2011 at 6:29 PM, Kevin Wolf <kwolf@redhat.com> wrote:
>>> +    /*
>>> +     * Increase the refcounts of all clusters and make sure everything is
>>> +     * stable on disk before updating the snapshot table to contain a pointer
>>> +     * to the new L1 table.
>>> +     */
>>> +    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
>>> +    if (ret < 0) {
>>> +        goto fail;
>>> +    }
>>> +
>>> +    ret = bdrv_flush(bs->file);
>>
>> Do we need to explicitly flush the qcow2 cache to ensure metadata
>> reaches the disk?
>
> Yes, I think this should be a bdrv_flush(bs). I'm not completely sure if
> it is really required, but I couldn't immediately tell why it's safe and
> this isn't a fast path anyway, so I'll replace this before merging the
> series (won't send out a v3 for this).

Okay.  I think it's useful to include flushes in functions that are
rarely called and only manipulate metadata.  It guarantees that
updates made by the function will happen before whatever the caller
decides to do next :).

Stefan
diff mbox

Patch

diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index 53d9233..b433f47 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -277,7 +277,9 @@  static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
 int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 {
     BDRVQcowState *s = bs->opaque;
-    QCowSnapshot *snapshots1, sn1, *sn = &sn1;
+    QCowSnapshot *new_snapshot_list = NULL;
+    QCowSnapshot *old_snapshot_list = NULL;
+    QCowSnapshot sn1, *sn = &sn1;
     int i, ret;
     uint64_t *l1_table = NULL;
     int64_t l1_table_offset;
@@ -303,16 +305,12 @@  int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
     sn->date_nsec = sn_info->date_nsec;
     sn->vm_clock_nsec = sn_info->vm_clock_nsec;
 
-    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
-    if (ret < 0)
-        goto fail;
-
     /* Allocate the L1 table of the snapshot and copy the current one there. */
     l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
     if (l1_table_offset < 0) {
+        ret = l1_table_offset;
         goto fail;
     }
-    bdrv_flush(bs->file);
 
     sn->l1_table_offset = l1_table_offset;
     sn->l1_size = s->l1_size;
@@ -321,22 +319,50 @@  int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
     for(i = 0; i < s->l1_size; i++) {
         l1_table[i] = cpu_to_be64(s->l1_table[i]);
     }
-    if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset,
-                    l1_table, s->l1_size * sizeof(uint64_t)) < 0)
+
+    ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
+                      s->l1_size * sizeof(uint64_t));
+    if (ret < 0) {
         goto fail;
+    }
+
     g_free(l1_table);
     l1_table = NULL;
 
-    snapshots1 = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
+    /*
+     * Increase the refcounts of all clusters and make sure everything is
+     * stable on disk before updating the snapshot table to contain a pointer
+     * to the new L1 table.
+     */
+    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    ret = bdrv_flush(bs->file);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    /* Append the new snapshot to the snapshot list */
+    new_snapshot_list = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
     if (s->snapshots) {
-        memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot));
-        g_free(s->snapshots);
+        memcpy(new_snapshot_list, s->snapshots,
+               s->nb_snapshots * sizeof(QCowSnapshot));
+        old_snapshot_list = s->snapshots;
     }
-    s->snapshots = snapshots1;
+    s->snapshots = new_snapshot_list;
     s->snapshots[s->nb_snapshots++] = *sn;
 
-    if (qcow2_write_snapshots(bs) < 0)
+    ret = qcow2_write_snapshots(bs);
+    if (ret < 0) {
+        g_free(s->snapshots);
+        s->snapshots = old_snapshot_list;
         goto fail;
+    }
+
+    g_free(old_snapshot_list);
+
 #ifdef DEBUG_ALLOC
     {
       BdrvCheckResult result = {0};
@@ -349,7 +375,8 @@  fail:
     g_free(sn->id_str);
     g_free(sn->name);
     g_free(l1_table);
-    return -1;
+
+    return ret;
 }
 
 /* copy the snapshot 'snapshot_name' into the current disk image */