public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Fabian Ebner <f.ebner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH v2 qemu 2/2] vma: allow partial restore
Date: Thu, 21 Apr 2022 13:26:48 +0200	[thread overview]
Message-ID: <20220421112659.74011-3-f.ebner@proxmox.com> (raw)
In-Reply-To: <20220421112659.74011-1-f.ebner@proxmox.com>

Introduce a new map line for skipping a certain drive, of the form
skip=drive-scsi0

Since in PVE, most archives are compressed and piped to vma for
restore, it's not easily possible to skip reads.

For the reader, a new skip flag for VmaRestoreState is added and the
target is allowed to be NULL if skip is specified when registering. If
the skip flag is set, no writes will be made as well as no check for
duplicate clusters. Therefore, the flag is not set for verify.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
---

Better viewed with -w

Changes from v1:
    * Use dedicated map line skip=<drivename> rather than a special path
      "/dev/null" to communicate that a drive should be skipped.

 vma-reader.c |  64 ++++++++++++---------
 vma.c        | 157 +++++++++++++++++++++++++++++----------------------
 vma.h        |   2 +-
 3 files changed, 126 insertions(+), 97 deletions(-)

diff --git a/vma-reader.c b/vma-reader.c
index 4f4ee2b47b..844d95a5ba 100644
--- a/vma-reader.c
+++ b/vma-reader.c
@@ -29,6 +29,7 @@ typedef struct VmaRestoreState {
     bool write_zeroes;
     unsigned long *bitmap;
     int bitmap_size;
+    bool skip;
 }  VmaRestoreState;
 
 struct VmaReader {
@@ -426,13 +427,14 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
 }
 
 static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
-                            BlockBackend *target, bool write_zeroes)
+                            BlockBackend *target, bool write_zeroes, bool skip)
 {
     assert(vmar);
     assert(dev_id);
 
     vmar->rstate[dev_id].target = target;
     vmar->rstate[dev_id].write_zeroes = write_zeroes;
+    vmar->rstate[dev_id].skip = skip;
 
     int64_t size = vmar->devinfo[dev_id].size;
 
@@ -447,28 +449,30 @@ static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
 }
 
 int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockBackend *target,
-                           bool write_zeroes, Error **errp)
+                           bool write_zeroes, bool skip, Error **errp)
 {
     assert(vmar);
-    assert(target != NULL);
+    assert(target != NULL || skip);
     assert(dev_id);
-    assert(vmar->rstate[dev_id].target == NULL);
-
-    int64_t size = blk_getlength(target);
-    int64_t size_diff = size - vmar->devinfo[dev_id].size;
-
-    /* storage types can have different size restrictions, so it
-     * is not always possible to create an image with exact size.
-     * So we tolerate a size difference up to 4MB.
-     */
-    if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
-        error_setg(errp, "vma_reader_register_bs for stream %s failed - "
-                   "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
-                   size, vmar->devinfo[dev_id].size);
-        return -1;
+    assert(vmar->rstate[dev_id].target == NULL && !vmar->rstate[dev_id].skip);
+
+    if (target != NULL) {
+        int64_t size = blk_getlength(target);
+        int64_t size_diff = size - vmar->devinfo[dev_id].size;
+
+        /* storage types can have different size restrictions, so it
+         * is not always possible to create an image with exact size.
+         * So we tolerate a size difference up to 4MB.
+         */
+        if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
+            error_setg(errp, "vma_reader_register_bs for stream %s failed - "
+                       "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
+                       size, vmar->devinfo[dev_id].size);
+            return -1;
+        }
     }
 
-    allocate_rstate(vmar, dev_id, target, write_zeroes);
+    allocate_rstate(vmar, dev_id, target, write_zeroes, skip);
 
     return 0;
 }
@@ -561,19 +565,23 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
         VmaRestoreState *rstate = &vmar->rstate[dev_id];
         BlockBackend *target = NULL;
 
+        bool skip = rstate->skip;
+
         if (dev_id != vmar->vmstate_stream) {
             target = rstate->target;
-            if (!verify && !target) {
+            if (!verify && !target && !skip) {
                 error_setg(errp, "got wrong dev id %d", dev_id);
                 return -1;
             }
 
-            if (vma_reader_get_bitmap(rstate, cluster_num)) {
-                error_setg(errp, "found duplicated cluster %zd for stream %s",
-                          cluster_num, vmar->devinfo[dev_id].devname);
-                return -1;
+            if (!skip) {
+                if (vma_reader_get_bitmap(rstate, cluster_num)) {
+                    error_setg(errp, "found duplicated cluster %zd for stream %s",
+                              cluster_num, vmar->devinfo[dev_id].devname);
+                    return -1;
+                }
+                vma_reader_set_bitmap(rstate, cluster_num, 1);
             }
-            vma_reader_set_bitmap(rstate, cluster_num, 1);
 
             max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE;
         } else {
@@ -619,7 +627,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
                 return -1;
             }
 
-            if (!verify) {
+            if (!verify && !skip) {
                 int nb_sectors = end_sector - sector_num;
                 if (restore_write_data(vmar, dev_id, target, vmstate_fd,
                                        buf + start, sector_num, nb_sectors,
@@ -655,7 +663,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
                         return -1;
                     }
 
-                    if (!verify) {
+                    if (!verify && !skip) {
                         int nb_sectors = end_sector - sector_num;
                         if (restore_write_data(vmar, dev_id, target, vmstate_fd,
                                                buf + start, sector_num,
@@ -680,7 +688,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
                             vmar->partial_zero_cluster_data += zero_size;
                         }
 
-                        if (rstate->write_zeroes && !verify) {
+                        if (rstate->write_zeroes && !verify && !skip) {
                             if (restore_write_data(vmar, dev_id, target, vmstate_fd,
                                                    zero_vma_block, sector_num,
                                                    nb_sectors, errp) < 0) {
@@ -851,7 +859,7 @@ int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp)
 
     for (dev_id = 1; dev_id < 255; dev_id++) {
         if (vma_reader_get_device_info(vmar, dev_id)) {
-            allocate_rstate(vmar, dev_id, NULL, false);
+            allocate_rstate(vmar, dev_id, NULL, false, false);
         }
     }
 
diff --git a/vma.c b/vma.c
index 89440733b1..21e765a469 100644
--- a/vma.c
+++ b/vma.c
@@ -138,6 +138,7 @@ typedef struct RestoreMap {
     char *throttling_group;
     char *cache;
     bool write_zero;
+    bool skip;
 } RestoreMap;
 
 static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) {
@@ -245,47 +246,61 @@ static int extract_content(int argc, char **argv)
             char *bps = NULL;
             char *group = NULL;
             char *cache = NULL;
+            char *devname = NULL;
+            bool skip = false;
+            uint64_t bps_value = 0;
+            const char *path = NULL;
+            bool write_zero = true;
+
             if (!line || line[0] == '\0' || !strcmp(line, "done\n")) {
                 break;
             }
             int len = strlen(line);
             if (line[len - 1] == '\n') {
                 line[len - 1] = '\0';
-                if (len == 1) {
+                len = len - 1;
+                if (len == 0) {
                     break;
                 }
             }
 
-            while (1) {
-                if (!try_parse_option(&line, "format", &format, inbuf) &&
-                    !try_parse_option(&line, "throttling.bps", &bps, inbuf) &&
-                    !try_parse_option(&line, "throttling.group", &group, inbuf) &&
-                    !try_parse_option(&line, "cache", &cache, inbuf))
-                {
-                    break;
+            if (strncmp(line, "skip", 4) == 0) {
+                if (len < 6 || line[4] != '=') {
+                    g_error("read map failed - option 'skip' has no value ('%s')",
+                            inbuf);
+                } else {
+                    devname = line + 5;
+                    skip = true;
+                }
+            } else {
+                while (1) {
+                    if (!try_parse_option(&line, "format", &format, inbuf) &&
+                        !try_parse_option(&line, "throttling.bps", &bps, inbuf) &&
+                        !try_parse_option(&line, "throttling.group", &group, inbuf) &&
+                        !try_parse_option(&line, "cache", &cache, inbuf))
+                    {
+                        break;
+                    }
                 }
-            }
 
-            uint64_t bps_value = 0;
-            if (bps) {
-                bps_value = verify_u64(bps);
-                g_free(bps);
-            }
+                if (bps) {
+                    bps_value = verify_u64(bps);
+                    g_free(bps);
+                }
 
-            const char *path;
-            bool write_zero;
-            if (line[0] == '0' && line[1] == ':') {
-                path = line + 2;
-                write_zero = false;
-            } else if (line[0] == '1' && line[1] == ':') {
-                path = line + 2;
-                write_zero = true;
-            } else {
-                g_error("read map failed - parse error ('%s')", inbuf);
+                if (line[0] == '0' && line[1] == ':') {
+                    path = line + 2;
+                    write_zero = false;
+                } else if (line[0] == '1' && line[1] == ':') {
+                    path = line + 2;
+                    write_zero = true;
+                } else {
+                    g_error("read map failed - parse error ('%s')", inbuf);
+                }
+
+                path = extract_devname(path, &devname, -1);
             }
 
-            char *devname = NULL;
-            path = extract_devname(path, &devname, -1);
             if (!devname) {
                 g_error("read map failed - no dev name specified ('%s')",
                         inbuf);
@@ -299,6 +314,7 @@ static int extract_content(int argc, char **argv)
             map->throttling_group = group;
             map->cache = cache;
             map->write_zero = write_zero;
+            map->skip = skip;
 
             g_hash_table_insert(devmap, map->devname, map);
 
@@ -328,6 +344,7 @@ static int extract_content(int argc, char **argv)
             const char *cache = NULL;
             int flags = BDRV_O_RDWR;
             bool write_zero = true;
+            bool skip = false;
 
             BlockBackend *blk = NULL;
 
@@ -343,6 +360,7 @@ static int extract_content(int argc, char **argv)
                 throttling_group = map->throttling_group;
                 cache = map->cache;
                 write_zero = map->write_zero;
+                skip = map->skip;
             } else {
                 devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
                                         dirname, di->devname);
@@ -361,57 +379,60 @@ static int extract_content(int argc, char **argv)
                 write_zero = false;
             }
 
-	    size_t devlen = strlen(devfn);
-	    QDict *options = NULL;
-            bool writethrough;
-            if (format) {
-                /* explicit format from commandline */
-                options = qdict_new();
-                qdict_put_str(options, "driver", format);
-            } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
-	               strncmp(devfn, "/dev/", 5) == 0)
-	    {
-                /* This part is now deprecated for PVE as well (just as qemu
-                 * deprecated not specifying an explicit raw format, too.
-                 */
-		/* explicit raw format */
-		options = qdict_new();
-		qdict_put_str(options, "driver", "raw");
-	    }
-            if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
-                g_error("invalid cache option: %s\n", cache);
-            }
+            if (!skip) {
+                size_t devlen = strlen(devfn);
+                QDict *options = NULL;
+                bool writethrough;
+                if (format) {
+                    /* explicit format from commandline */
+                    options = qdict_new();
+                    qdict_put_str(options, "driver", format);
+                } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
+                    strncmp(devfn, "/dev/", 5) == 0)
+                {
+                    /* This part is now deprecated for PVE as well (just as qemu
+                     * deprecated not specifying an explicit raw format, too.
+                     */
+                    /* explicit raw format */
+                    options = qdict_new();
+                    qdict_put_str(options, "driver", "raw");
+                }
 
-	    if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
-                g_error("can't open file %s - %s", devfn,
-                        error_get_pretty(errp));
-            }
+                if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
+                    g_error("invalid cache option: %s\n", cache);
+                }
 
-            if (cache) {
-                blk_set_enable_write_cache(blk, !writethrough);
-            }
+                if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
+                    g_error("can't open file %s - %s", devfn,
+                            error_get_pretty(errp));
+                }
 
-            if (throttling_group) {
-                blk_io_limits_enable(blk, throttling_group);
-            }
+                if (cache) {
+                    blk_set_enable_write_cache(blk, !writethrough);
+                }
 
-            if (throttling_bps) {
-                if (!throttling_group) {
-                    blk_io_limits_enable(blk, devfn);
+                if (throttling_group) {
+                    blk_io_limits_enable(blk, throttling_group);
                 }
 
-                ThrottleConfig cfg;
-                throttle_config_init(&cfg);
-                cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps;
-                Error *err = NULL;
-                if (!throttle_is_valid(&cfg, &err)) {
-                    error_report_err(err);
-                    g_error("failed to apply throttling");
+                if (throttling_bps) {
+                    if (!throttling_group) {
+                        blk_io_limits_enable(blk, devfn);
+                    }
+
+                    ThrottleConfig cfg;
+                    throttle_config_init(&cfg);
+                    cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps;
+                    Error *err = NULL;
+                    if (!throttle_is_valid(&cfg, &err)) {
+                        error_report_err(err);
+                        g_error("failed to apply throttling");
+                    }
+                    blk_set_io_limits(blk, &cfg);
                 }
-                blk_set_io_limits(blk, &cfg);
             }
 
-            if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
+            if (vma_reader_register_bs(vmar, i, blk, write_zero, skip, &errp) < 0) {
                 g_error("%s", error_get_pretty(errp));
             }
 
diff --git a/vma.h b/vma.h
index c895c97f6d..1b62859165 100644
--- a/vma.h
+++ b/vma.h
@@ -142,7 +142,7 @@ GList *vma_reader_get_config_data(VmaReader *vmar);
 VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id);
 int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id,
                            BlockBackend *target, bool write_zeroes,
-                           Error **errp);
+                           bool skip, Error **errp);
 int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
                        Error **errp);
 int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp);
-- 
2.30.2





  parent reply	other threads:[~2022-04-21 11:28 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-21 11:26 [pve-devel] [PATCH-SERIES v2 qemu/qemu-server/widget-toolkit/manager] more flexible restore Fabian Ebner
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu 1/2] vma: restore: call blk_unref for all opened block devices Fabian Ebner
2022-04-25  6:37   ` Wolfgang Bumiller
2022-04-25 16:10   ` [pve-devel] applied: " Thomas Lamprecht
2022-04-21 11:26 ` Fabian Ebner [this message]
2022-04-25  6:40   ` [pve-devel] [PATCH v2 qemu 2/2] vma: allow partial restore Wolfgang Bumiller
2022-04-25 16:10   ` [pve-devel] applied: " Thomas Lamprecht
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu-server 1/7] restore: cleanup oldconf: also clean up snapshots from kept volumes Fabian Ebner
2022-04-25 16:20   ` [pve-devel] applied: " Thomas Lamprecht
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu-server 2/7] restore destroy volumes: remove check for absolute path Fabian Ebner
2022-04-25 16:20   ` [pve-devel] applied: " Thomas Lamprecht
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu-server 3/7] restore deactivate volumes: never die Fabian Ebner
2022-04-23  9:18   ` Thomas Lamprecht
2022-04-25  6:45     ` Fabian Ebner
2022-04-25 16:21   ` [pve-devel] applied: " Thomas Lamprecht
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu-server 4/7] restore: also deactivate/destroy cloud-init disk upon error Fabian Ebner
2022-04-25 16:21   ` [pve-devel] applied: " Thomas Lamprecht
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu-server 5/7] api: create: refactor parameter check logic Fabian Ebner
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu-server 6/7] api: create: allow overriding non-disk options during restore Fabian Ebner
2022-04-21 11:26 ` [pve-devel] [PATCH v2 qemu-server 7/7] restore: allow preserving drives " Fabian Ebner
2022-04-21 11:26 ` [pve-devel] [PATCH v2 widget-toolkit 1/1] css: add proxmox-good-row class Fabian Ebner
2022-04-23  8:32   ` [pve-devel] applied: " Thomas Lamprecht
2022-04-21 11:26 ` [pve-devel] [PATCH v2 manager 1/3] ui: restore: disallow empty storage selection if it wouldn't work Fabian Ebner
2022-04-23  9:38   ` Thomas Lamprecht
2022-04-25  7:28     ` Fabian Ebner
2022-04-27  7:05       ` Thomas Lamprecht
2022-04-27  8:21         ` Fabian Ebner
2022-04-21 11:26 ` [pve-devel] [PATCH v2 manager 2/3] ui: restore: allow override of some settings Fabian Ebner
2022-04-23 10:07   ` Thomas Lamprecht
2022-04-21 11:26 ` [pve-devel] [PATCH v2 manager 3/3] ui: restore: allow preserving disks Fabian Ebner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220421112659.74011-3-f.ebner@proxmox.com \
    --to=f.ebner@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal