Block patches for 2.1.0-rc0
-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJTrbz4AAoJEH8JsnLIjy/WlIkP/RepIwS29f19i3B/idGzUdYW
 9XJnVowRvpkUzDqUprrr7lPHMW/CwAswLNis9B1hZ59rx+tx4Hm/rZGARlqhSOSO
 ZMdW32GFW0SyC5PglFBwGQAk4U0FxwW5cJD6US7h3L4pACIdCkzFSNxehyfCMyU/
 oJkjuAH4a2IQoQf/M7WMm5kPkrdpRp6ZgbQvJGHaR63cuulZDb7rbHMyG66MWH8P
 wahhFFPY1wOeMBiISxPbmcTus+AlfCffG5qPqq83OtaIuWzINTmWlpiFmtx+Aqwy
 HSvGnFJ4Rf7J6Fw8sdTsABdqUTc/gxDYmhAuftm/hsjD9MvPeuFSLPMPLfGg6aPR
 umKaeBOw8NoMTPgbxg403gxFTrHar+TidBu8KgZw5T189/oJSSpT2J53uHWazmd9
 8USkcYQ7VHdFUQVXluLEzHMIWc7kf87ylQ8c9S1yCkNeWYxRZDZGgHEU49ov7FFU
 FnA0w+ZFyDkU8d5gryG+vxOeBDlmXD4UHa676gGlaYhs7YC/BY/JaMgqY4Fd6MMW
 dS5ibPjdtbxEZTh29eWEByMWpzuitr+iPPzsJEdC29LeIIj3XRQq/4FyiQ6EMAAO
 iOlcqE3tws0Ty8GEp78xsAYjaLuH3zmvOTa4aHUQ+K9kwpMPFSJKEcLkwPWWYRbs
 qR2ZL6M+95oQTYkYzv8i
 =Wwqx
 -----END PGP SIGNATURE-----
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block patches for 2.1.0-rc0
# gpg: Signature made Fri 27 Jun 2014 19:50:32 BST using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
* remotes/kevin/tags/for-upstream: (47 commits)
  iotests: Fix 083 for out-of-tree builds
  iotests: Drop Python version from 065's Shebang
  iotests: Use $PYTHON for Python scripts
  iotests: Source common.env
  configure: Enable out-of-tree iotests
  iotests: Allow out-of-tree run
  block.c: Don't return success for bdrv_append_temp_snapshot() failure
  qemu-iotests: Add TestRepairQuorum to 041 to test drive-mirror node-name mode.
  block: Add replaces argument to drive-mirror
  blockjob: Fix recent BLOCK_JOB_ERROR regression
  blockjob: Fix recent BLOCK_JOB_READY regression
  virtio-blk: Rename complete_request_early to complete_request_vring
  virtio-blk: Unify {non-,}dataplane's request handlings
  virtio-blk: Schedule BH in the right context
  virtio-blk: Export request handling functions to dataplane
  virtio-blk: Make request completion function virtual
  block: acquire AioContext in qmp_query_blockstats()
  block: make bdrv_query_stats() static
  virtio-blk: Fix and clean up the in_sg and out_sg check
  virtio-blk: Fill in VirtIOBlockReq.out in dataplane code
  ...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
			
			
This commit is contained in:
		
						commit
						2d40fa6987
					
				
							
								
								
									
										324
									
								
								block.c
								
								
								
								
							
							
						
						
									
										324
									
								
								block.c
								
								
								
								
							| 
						 | 
				
			
			@ -831,7 +831,7 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
 | 
			
		|||
     * Clear flags that are internal to the block layer before opening the
 | 
			
		||||
     * image.
 | 
			
		||||
     */
 | 
			
		||||
    open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
 | 
			
		||||
    open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Snapshots should be writable.
 | 
			
		||||
| 
						 | 
				
			
			@ -928,6 +928,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
 | 
			
		|||
    bs->zero_beyond_eof = true;
 | 
			
		||||
    open_flags = bdrv_open_flags(bs, flags);
 | 
			
		||||
    bs->read_only = !(open_flags & BDRV_O_RDWR);
 | 
			
		||||
    bs->growable = !!(flags & BDRV_O_PROTOCOL);
 | 
			
		||||
 | 
			
		||||
    if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
| 
						 | 
				
			
			@ -1005,94 +1006,124 @@ free_and_fail:
 | 
			
		|||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Opens a file using a protocol (file, host_device, nbd, ...)
 | 
			
		||||
 *
 | 
			
		||||
 * options is an indirect pointer to a QDict of options to pass to the block
 | 
			
		||||
 * drivers, or pointer to NULL for an empty set of options. If this function
 | 
			
		||||
 * takes ownership of the QDict reference, it will set *options to NULL;
 | 
			
		||||
 * otherwise, it will contain unused/unrecognized options after this function
 | 
			
		||||
 * returns. Then, the caller is responsible for freeing it. If it intends to
 | 
			
		||||
 * reuse the QDict, QINCREF() should be called beforehand.
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_file_open(BlockDriverState *bs, const char *filename,
 | 
			
		||||
                          QDict **options, int flags, Error **errp)
 | 
			
		||||
static QDict *parse_json_filename(const char *filename, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriver *drv;
 | 
			
		||||
    const char *drvname;
 | 
			
		||||
    bool parse_filename = false;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    QObject *options_obj;
 | 
			
		||||
    QDict *options;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = strstart(filename, "json:", &filename);
 | 
			
		||||
    assert(ret);
 | 
			
		||||
 | 
			
		||||
    options_obj = qobject_from_json(filename);
 | 
			
		||||
    if (!options_obj) {
 | 
			
		||||
        error_setg(errp, "Could not parse the JSON options");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qobject_type(options_obj) != QTYPE_QDICT) {
 | 
			
		||||
        qobject_decref(options_obj);
 | 
			
		||||
        error_setg(errp, "Invalid JSON object given");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    options = qobject_to_qdict(options_obj);
 | 
			
		||||
    qdict_flatten(options);
 | 
			
		||||
 | 
			
		||||
    return options;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fills in default options for opening images and converts the legacy
 | 
			
		||||
 * filename/flags pair to option QDict entries.
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
 | 
			
		||||
                             BlockDriver *drv, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    const char *filename = *pfilename;
 | 
			
		||||
    const char *drvname;
 | 
			
		||||
    bool protocol = flags & BDRV_O_PROTOCOL;
 | 
			
		||||
    bool parse_filename = false;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Parse json: pseudo-protocol */
 | 
			
		||||
    if (filename && g_str_has_prefix(filename, "json:")) {
 | 
			
		||||
        QDict *json_options = parse_json_filename(filename, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Options given in the filename have lower priority than options
 | 
			
		||||
         * specified directly */
 | 
			
		||||
        qdict_join(*options, json_options, false);
 | 
			
		||||
        QDECREF(json_options);
 | 
			
		||||
        *pfilename = filename = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Fetch the file name from the options QDict if necessary */
 | 
			
		||||
    if (!filename) {
 | 
			
		||||
        filename = qdict_get_try_str(*options, "filename");
 | 
			
		||||
    } else if (filename && !qdict_haskey(*options, "filename")) {
 | 
			
		||||
        qdict_put(*options, "filename", qstring_from_str(filename));
 | 
			
		||||
        parse_filename = true;
 | 
			
		||||
    } else {
 | 
			
		||||
        error_setg(errp, "Can't specify 'file' and 'filename' options at the "
 | 
			
		||||
                   "same time");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    if (protocol && filename) {
 | 
			
		||||
        if (!qdict_haskey(*options, "filename")) {
 | 
			
		||||
            qdict_put(*options, "filename", qstring_from_str(filename));
 | 
			
		||||
            parse_filename = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            error_setg(errp, "Can't specify 'file' and 'filename' options at "
 | 
			
		||||
                             "the same time");
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Find the right block driver */
 | 
			
		||||
    filename = qdict_get_try_str(*options, "filename");
 | 
			
		||||
    drvname = qdict_get_try_str(*options, "driver");
 | 
			
		||||
    if (drvname) {
 | 
			
		||||
        drv = bdrv_find_format(drvname);
 | 
			
		||||
        if (!drv) {
 | 
			
		||||
            error_setg(errp, "Unknown driver '%s'", drvname);
 | 
			
		||||
        }
 | 
			
		||||
        qdict_del(*options, "driver");
 | 
			
		||||
    } else if (filename) {
 | 
			
		||||
        drv = bdrv_find_protocol(filename, parse_filename);
 | 
			
		||||
        if (!drv) {
 | 
			
		||||
            error_setg(errp, "Unknown protocol");
 | 
			
		||||
 | 
			
		||||
    if (drv) {
 | 
			
		||||
        if (drvname) {
 | 
			
		||||
            error_setg(errp, "Driver specified twice");
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
        drvname = drv->format_name;
 | 
			
		||||
        qdict_put(*options, "driver", qstring_from_str(drvname));
 | 
			
		||||
    } else {
 | 
			
		||||
        error_setg(errp, "Must specify either driver or file");
 | 
			
		||||
        drv = NULL;
 | 
			
		||||
        if (!drvname && protocol) {
 | 
			
		||||
            if (filename) {
 | 
			
		||||
                drv = bdrv_find_protocol(filename, parse_filename);
 | 
			
		||||
                if (!drv) {
 | 
			
		||||
                    error_setg(errp, "Unknown protocol");
 | 
			
		||||
                    return -EINVAL;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                drvname = drv->format_name;
 | 
			
		||||
                qdict_put(*options, "driver", qstring_from_str(drvname));
 | 
			
		||||
            } else {
 | 
			
		||||
                error_setg(errp, "Must specify either driver or file");
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (drvname) {
 | 
			
		||||
            drv = bdrv_find_format(drvname);
 | 
			
		||||
            if (!drv) {
 | 
			
		||||
                error_setg(errp, "Unknown driver '%s'", drvname);
 | 
			
		||||
                return -ENOENT;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!drv) {
 | 
			
		||||
        /* errp has been set already */
 | 
			
		||||
        ret = -ENOENT;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    assert(drv || !protocol);
 | 
			
		||||
 | 
			
		||||
    /* Parse the filename and open it */
 | 
			
		||||
    if (drv->bdrv_parse_filename && parse_filename) {
 | 
			
		||||
    /* Driver-specific filename parsing */
 | 
			
		||||
    if (drv && drv->bdrv_parse_filename && parse_filename) {
 | 
			
		||||
        drv->bdrv_parse_filename(filename, *options, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto fail;
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!drv->bdrv_needs_filename) {
 | 
			
		||||
            qdict_del(*options, "filename");
 | 
			
		||||
        } else {
 | 
			
		||||
            filename = qdict_get_str(*options, "filename");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!drv->bdrv_file_open) {
 | 
			
		||||
        ret = bdrv_open(&bs, filename, NULL, *options, flags, drv, &local_err);
 | 
			
		||||
        *options = NULL;
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = bdrv_open_common(bs, NULL, *options, flags, drv, &local_err);
 | 
			
		||||
    }
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->growable = 1;
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
 | 
			
		||||
| 
						 | 
				
			
			@ -1162,6 +1193,13 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
 | 
			
		|||
        bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!bs->drv || !bs->drv->supports_backing) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        error_setg(errp, "Driver doesn't support backing files");
 | 
			
		||||
        QDECREF(options);
 | 
			
		||||
        goto free_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    backing_hd = bdrv_new("", errp);
 | 
			
		||||
 | 
			
		||||
    if (bs->backing_format[0] != '\0') {
 | 
			
		||||
| 
						 | 
				
			
			@ -1240,7 +1278,7 @@ done:
 | 
			
		|||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
 | 
			
		||||
int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
 | 
			
		||||
    char *tmp_filename = g_malloc0(PATH_MAX + 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -1258,6 +1296,7 @@ void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
 | 
			
		|||
    /* Get the required size from the image */
 | 
			
		||||
    total_size = bdrv_getlength(bs);
 | 
			
		||||
    if (total_size < 0) {
 | 
			
		||||
        ret = total_size;
 | 
			
		||||
        error_setg_errno(errp, -total_size, "Could not get image size");
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1304,33 +1343,7 @@ void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
 | 
			
		|||
 | 
			
		||||
out:
 | 
			
		||||
    g_free(tmp_filename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QDict *parse_json_filename(const char *filename, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QObject *options_obj;
 | 
			
		||||
    QDict *options;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = strstart(filename, "json:", &filename);
 | 
			
		||||
    assert(ret);
 | 
			
		||||
 | 
			
		||||
    options_obj = qobject_from_json(filename);
 | 
			
		||||
    if (!options_obj) {
 | 
			
		||||
        error_setg(errp, "Could not parse the JSON options");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qobject_type(options_obj) != QTYPE_QDICT) {
 | 
			
		||||
        qobject_decref(options_obj);
 | 
			
		||||
        error_setg(errp, "Invalid JSON object given");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    options = qobject_to_qdict(options_obj);
 | 
			
		||||
    qdict_flatten(options);
 | 
			
		||||
 | 
			
		||||
    return options;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -1396,77 +1409,62 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
 | 
			
		|||
        options = qdict_new();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (filename && g_str_has_prefix(filename, "json:")) {
 | 
			
		||||
        QDict *json_options = parse_json_filename(filename, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Options given in the filename have lower priority than options
 | 
			
		||||
         * specified directly */
 | 
			
		||||
        qdict_join(options, json_options, false);
 | 
			
		||||
        QDECREF(json_options);
 | 
			
		||||
        filename = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->options = options;
 | 
			
		||||
    options = qdict_clone_shallow(options);
 | 
			
		||||
 | 
			
		||||
    if (flags & BDRV_O_PROTOCOL) {
 | 
			
		||||
        assert(!drv);
 | 
			
		||||
        ret = bdrv_file_open(bs, filename, &options, flags & ~BDRV_O_PROTOCOL,
 | 
			
		||||
                             &local_err);
 | 
			
		||||
        if (!ret) {
 | 
			
		||||
            drv = bs->drv;
 | 
			
		||||
            goto done;
 | 
			
		||||
        } else if (bs->drv) {
 | 
			
		||||
            goto close_and_fail;
 | 
			
		||||
        } else {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Open image file without format layer */
 | 
			
		||||
    if (flags & BDRV_O_RDWR) {
 | 
			
		||||
        flags |= BDRV_O_ALLOW_RDWR;
 | 
			
		||||
    }
 | 
			
		||||
    if (flags & BDRV_O_SNAPSHOT) {
 | 
			
		||||
        snapshot_flags = bdrv_temp_snapshot_flags(flags);
 | 
			
		||||
        flags = bdrv_backing_flags(flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(file == NULL);
 | 
			
		||||
    ret = bdrv_open_image(&file, filename, options, "file",
 | 
			
		||||
                          bdrv_inherited_flags(flags),
 | 
			
		||||
                          true, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
    ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Find the right image format driver */
 | 
			
		||||
    drv = NULL;
 | 
			
		||||
    drvname = qdict_get_try_str(options, "driver");
 | 
			
		||||
    if (drvname) {
 | 
			
		||||
        drv = bdrv_find_format(drvname);
 | 
			
		||||
        qdict_del(options, "driver");
 | 
			
		||||
        if (!drv) {
 | 
			
		||||
            error_setg(errp, "Invalid driver: '%s'", drvname);
 | 
			
		||||
            error_setg(errp, "Unknown driver: '%s'", drvname);
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!drv) {
 | 
			
		||||
        if (file) {
 | 
			
		||||
            ret = find_image_format(file, filename, &drv, &local_err);
 | 
			
		||||
        } else {
 | 
			
		||||
            error_setg(errp, "Must specify either driver or file");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
    assert(drvname || !(flags & BDRV_O_PROTOCOL));
 | 
			
		||||
    if (drv && !drv->bdrv_file_open) {
 | 
			
		||||
        /* If the user explicitly wants a format driver here, we'll need to add
 | 
			
		||||
         * another layer for the protocol in bs->file */
 | 
			
		||||
        flags &= ~BDRV_O_PROTOCOL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->options = options;
 | 
			
		||||
    options = qdict_clone_shallow(options);
 | 
			
		||||
 | 
			
		||||
    /* Open image file without format layer */
 | 
			
		||||
    if ((flags & BDRV_O_PROTOCOL) == 0) {
 | 
			
		||||
        if (flags & BDRV_O_RDWR) {
 | 
			
		||||
            flags |= BDRV_O_ALLOW_RDWR;
 | 
			
		||||
        }
 | 
			
		||||
        if (flags & BDRV_O_SNAPSHOT) {
 | 
			
		||||
            snapshot_flags = bdrv_temp_snapshot_flags(flags);
 | 
			
		||||
            flags = bdrv_backing_flags(flags);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert(file == NULL);
 | 
			
		||||
        ret = bdrv_open_image(&file, filename, options, "file",
 | 
			
		||||
                              bdrv_inherited_flags(flags),
 | 
			
		||||
                              true, &local_err);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!drv) {
 | 
			
		||||
    /* Image format probing */
 | 
			
		||||
    if (!drv && file) {
 | 
			
		||||
        ret = find_image_format(file, filename, &drv, &local_err);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (!drv) {
 | 
			
		||||
        error_setg(errp, "Must specify either driver or file");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1495,15 +1493,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
 | 
			
		|||
    /* For snapshot=on, create a temporary qcow2 overlay. bs points to the
 | 
			
		||||
     * temporary snapshot afterwards. */
 | 
			
		||||
    if (snapshot_flags) {
 | 
			
		||||
        bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err);
 | 
			
		||||
        ret = bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            goto close_and_fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    /* Check if any unknown options were used */
 | 
			
		||||
    if (options && (qdict_size(options) != 0)) {
 | 
			
		||||
        const QDictEntry *entry = qdict_first(options);
 | 
			
		||||
| 
						 | 
				
			
			@ -3489,9 +3484,7 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
 | 
			
		|||
        return -ENOTSUP;
 | 
			
		||||
    if (bs->read_only)
 | 
			
		||||
        return -EACCES;
 | 
			
		||||
    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
 | 
			
		||||
        return -EBUSY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = drv->bdrv_truncate(bs, offset);
 | 
			
		||||
    if (ret == 0) {
 | 
			
		||||
        ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
 | 
			
		||||
| 
						 | 
				
			
			@ -5774,3 +5767,28 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate)
 | 
			
		|||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockDriverState *check_to_replace_node(const char *node_name, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *to_replace_bs = bdrv_find_node(node_name);
 | 
			
		||||
    if (!to_replace_bs) {
 | 
			
		||||
        error_setg(errp, "Node name '%s' not found", node_name);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We don't want arbitrary node of the BDS chain to be replaced only the top
 | 
			
		||||
     * most non filter in order to prevent data corruption.
 | 
			
		||||
     * Another benefit is that this tests exclude backing files which are
 | 
			
		||||
     * blocked by the backing blockers.
 | 
			
		||||
     */
 | 
			
		||||
    if (!bdrv_is_first_non_filter(to_replace_bs)) {
 | 
			
		||||
        error_setg(errp, "Only top most non filter can be replaced");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return to_replace_bs;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -414,6 +414,7 @@ static BlockDriver bdrv_cow = {
 | 
			
		|||
    .bdrv_close     = cow_close,
 | 
			
		||||
    .bdrv_create    = cow_create,
 | 
			
		||||
    .bdrv_has_zero_init     = bdrv_has_zero_init_1,
 | 
			
		||||
    .supports_backing       = true,
 | 
			
		||||
 | 
			
		||||
    .bdrv_read              = cow_co_read,
 | 
			
		||||
    .bdrv_write             = cow_co_write,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,12 @@ typedef struct MirrorBlockJob {
 | 
			
		|||
    RateLimit limit;
 | 
			
		||||
    BlockDriverState *target;
 | 
			
		||||
    BlockDriverState *base;
 | 
			
		||||
    /* The name of the graph node to replace */
 | 
			
		||||
    char *replaces;
 | 
			
		||||
    /* The BDS to replace */
 | 
			
		||||
    BlockDriverState *to_replace;
 | 
			
		||||
    /* Used to block operations on the drive-mirror-replace target */
 | 
			
		||||
    Error *replace_blocker;
 | 
			
		||||
    bool is_none_mode;
 | 
			
		||||
    BlockdevOnError on_source_error, on_target_error;
 | 
			
		||||
    bool synced;
 | 
			
		||||
| 
						 | 
				
			
			@ -324,9 +330,18 @@ static void coroutine_fn mirror_run(void *opaque)
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    s->common.len = bdrv_getlength(bs);
 | 
			
		||||
    if (s->common.len <= 0) {
 | 
			
		||||
    if (s->common.len < 0) {
 | 
			
		||||
        ret = s->common.len;
 | 
			
		||||
        goto immediate_exit;
 | 
			
		||||
    } else if (s->common.len == 0) {
 | 
			
		||||
        /* Report BLOCK_JOB_READY and wait for complete. */
 | 
			
		||||
        block_job_event_ready(&s->common);
 | 
			
		||||
        s->synced = true;
 | 
			
		||||
        while (!block_job_is_cancelled(&s->common) && !s->should_complete) {
 | 
			
		||||
            block_job_yield(&s->common);
 | 
			
		||||
        }
 | 
			
		||||
        s->common.cancelled = false;
 | 
			
		||||
        goto immediate_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    length = DIV_ROUND_UP(s->common.len, s->granularity);
 | 
			
		||||
| 
						 | 
				
			
			@ -491,10 +506,14 @@ immediate_exit:
 | 
			
		|||
    bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
 | 
			
		||||
    bdrv_iostatus_disable(s->target);
 | 
			
		||||
    if (s->should_complete && ret == 0) {
 | 
			
		||||
        if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) {
 | 
			
		||||
            bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL);
 | 
			
		||||
        BlockDriverState *to_replace = s->common.bs;
 | 
			
		||||
        if (s->to_replace) {
 | 
			
		||||
            to_replace = s->to_replace;
 | 
			
		||||
        }
 | 
			
		||||
        bdrv_swap(s->target, s->common.bs);
 | 
			
		||||
        if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
 | 
			
		||||
            bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
 | 
			
		||||
        }
 | 
			
		||||
        bdrv_swap(s->target, to_replace);
 | 
			
		||||
        if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
 | 
			
		||||
            /* drop the bs loop chain formed by the swap: break the loop then
 | 
			
		||||
             * trigger the unref from the top one */
 | 
			
		||||
| 
						 | 
				
			
			@ -503,6 +522,12 @@ immediate_exit:
 | 
			
		|||
            bdrv_unref(p);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (s->to_replace) {
 | 
			
		||||
        bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
 | 
			
		||||
        error_free(s->replace_blocker);
 | 
			
		||||
        bdrv_unref(s->to_replace);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(s->replaces);
 | 
			
		||||
    bdrv_unref(s->target);
 | 
			
		||||
    block_job_completed(&s->common, ret);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -541,6 +566,20 @@ static void mirror_complete(BlockJob *job, Error **errp)
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* check the target bs is not blocked and block all operations on it */
 | 
			
		||||
    if (s->replaces) {
 | 
			
		||||
        s->to_replace = check_to_replace_node(s->replaces, &local_err);
 | 
			
		||||
        if (!s->to_replace) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        error_setg(&s->replace_blocker,
 | 
			
		||||
                   "block device is in use by block-job-complete");
 | 
			
		||||
        bdrv_op_block_all(s->to_replace, s->replace_blocker);
 | 
			
		||||
        bdrv_ref(s->to_replace);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->should_complete = true;
 | 
			
		||||
    block_job_resume(job);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -563,14 +602,15 @@ static const BlockJobDriver commit_active_job_driver = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		||||
                            int64_t speed, int64_t granularity,
 | 
			
		||||
                            int64_t buf_size,
 | 
			
		||||
                            BlockdevOnError on_source_error,
 | 
			
		||||
                            BlockdevOnError on_target_error,
 | 
			
		||||
                            BlockDriverCompletionFunc *cb,
 | 
			
		||||
                            void *opaque, Error **errp,
 | 
			
		||||
                            const BlockJobDriver *driver,
 | 
			
		||||
                            bool is_none_mode, BlockDriverState *base)
 | 
			
		||||
                             const char *replaces,
 | 
			
		||||
                             int64_t speed, int64_t granularity,
 | 
			
		||||
                             int64_t buf_size,
 | 
			
		||||
                             BlockdevOnError on_source_error,
 | 
			
		||||
                             BlockdevOnError on_target_error,
 | 
			
		||||
                             BlockDriverCompletionFunc *cb,
 | 
			
		||||
                             void *opaque, Error **errp,
 | 
			
		||||
                             const BlockJobDriver *driver,
 | 
			
		||||
                             bool is_none_mode, BlockDriverState *base)
 | 
			
		||||
{
 | 
			
		||||
    MirrorBlockJob *s;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -601,6 +641,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->replaces = g_strdup(replaces);
 | 
			
		||||
    s->on_source_error = on_source_error;
 | 
			
		||||
    s->on_target_error = on_target_error;
 | 
			
		||||
    s->target = target;
 | 
			
		||||
| 
						 | 
				
			
			@ -622,6 +663,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		||||
                  const char *replaces,
 | 
			
		||||
                  int64_t speed, int64_t granularity, int64_t buf_size,
 | 
			
		||||
                  MirrorSyncMode mode, BlockdevOnError on_source_error,
 | 
			
		||||
                  BlockdevOnError on_target_error,
 | 
			
		||||
| 
						 | 
				
			
			@ -633,7 +675,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		|||
 | 
			
		||||
    is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
 | 
			
		||||
    base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
 | 
			
		||||
    mirror_start_job(bs, target, speed, granularity, buf_size,
 | 
			
		||||
    mirror_start_job(bs, target, replaces,
 | 
			
		||||
                     speed, granularity, buf_size,
 | 
			
		||||
                     on_source_error, on_target_error, cb, opaque, errp,
 | 
			
		||||
                     &mirror_job_driver, is_none_mode, base);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -681,7 +724,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_ref(base);
 | 
			
		||||
    mirror_start_job(bs, base, speed, 0, 0,
 | 
			
		||||
    mirror_start_job(bs, base, NULL, speed, 0, 0,
 | 
			
		||||
                     on_error, on_error, cb, opaque, &local_err,
 | 
			
		||||
                     &commit_active_job_driver, false, base);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								block/nfs.c
								
								
								
								
							
							
						
						
									
										22
									
								
								block/nfs.c
								
								
								
								
							| 
						 | 
				
			
			@ -304,17 +304,27 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
 | 
			
		|||
 | 
			
		||||
    qp = query_params_parse(uri->query);
 | 
			
		||||
    for (i = 0; i < qp->n; i++) {
 | 
			
		||||
        unsigned long long val;
 | 
			
		||||
        if (!qp->p[i].value) {
 | 
			
		||||
            error_setg(errp, "Value for NFS parameter expected: %s",
 | 
			
		||||
                       qp->p[i].name);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
        if (!strncmp(qp->p[i].name, "uid", 3)) {
 | 
			
		||||
            nfs_set_uid(client->context, atoi(qp->p[i].value));
 | 
			
		||||
        } else if (!strncmp(qp->p[i].name, "gid", 3)) {
 | 
			
		||||
            nfs_set_gid(client->context, atoi(qp->p[i].value));
 | 
			
		||||
        } else if (!strncmp(qp->p[i].name, "tcp-syncnt", 10)) {
 | 
			
		||||
            nfs_set_tcp_syncnt(client->context, atoi(qp->p[i].value));
 | 
			
		||||
        if (parse_uint_full(qp->p[i].value, &val, 0)) {
 | 
			
		||||
            error_setg(errp, "Illegal value for NFS parameter: %s",
 | 
			
		||||
                       qp->p[i].name);
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
        if (!strcmp(qp->p[i].name, "uid")) {
 | 
			
		||||
            nfs_set_uid(client->context, val);
 | 
			
		||||
        } else if (!strcmp(qp->p[i].name, "gid")) {
 | 
			
		||||
            nfs_set_gid(client->context, val);
 | 
			
		||||
        } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
 | 
			
		||||
            nfs_set_tcp_syncnt(client->context, val);
 | 
			
		||||
#ifdef LIBNFS_FEATURE_READAHEAD
 | 
			
		||||
        } else if (!strcmp(qp->p[i].name, "readahead")) {
 | 
			
		||||
            nfs_set_readahead(client->context, val);
 | 
			
		||||
#endif
 | 
			
		||||
        } else {
 | 
			
		||||
            error_setg(errp, "Unknown NFS parameter name: %s",
 | 
			
		||||
                       qp->p[i].name);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -293,7 +293,7 @@ void bdrv_query_info(BlockDriverState *bs,
 | 
			
		|||
    qapi_free_BlockInfo(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs)
 | 
			
		||||
static BlockStats *bdrv_query_stats(const BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BlockStats *s;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -360,7 +360,11 @@ BlockStatsList *qmp_query_blockstats(Error **errp)
 | 
			
		|||
 | 
			
		||||
     while ((bs = bdrv_next(bs))) {
 | 
			
		||||
        BlockStatsList *info = g_malloc0(sizeof(*info));
 | 
			
		||||
        AioContext *ctx = bdrv_get_aio_context(bs);
 | 
			
		||||
 | 
			
		||||
        aio_context_acquire(ctx);
 | 
			
		||||
        info->value = bdrv_query_stats(bs);
 | 
			
		||||
        aio_context_release(ctx);
 | 
			
		||||
 | 
			
		||||
        *p_next = info;
 | 
			
		||||
        p_next = &info->next;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -941,6 +941,7 @@ static BlockDriver bdrv_qcow = {
 | 
			
		|||
    .bdrv_reopen_prepare    = qcow_reopen_prepare,
 | 
			
		||||
    .bdrv_create            = qcow_create,
 | 
			
		||||
    .bdrv_has_zero_init     = bdrv_has_zero_init_1,
 | 
			
		||||
    .supports_backing       = true,
 | 
			
		||||
 | 
			
		||||
    .bdrv_co_readv          = qcow_co_readv,
 | 
			
		||||
    .bdrv_co_writev         = qcow_co_writev,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2418,6 +2418,7 @@ static BlockDriver bdrv_qcow2 = {
 | 
			
		|||
    .bdrv_save_vmstate    = qcow2_save_vmstate,
 | 
			
		||||
    .bdrv_load_vmstate    = qcow2_load_vmstate,
 | 
			
		||||
 | 
			
		||||
    .supports_backing           = true,
 | 
			
		||||
    .bdrv_change_backing_file   = qcow2_change_backing_file,
 | 
			
		||||
 | 
			
		||||
    .bdrv_refresh_limits        = qcow2_refresh_limits,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1652,6 +1652,7 @@ static BlockDriver bdrv_qed = {
 | 
			
		|||
    .format_name              = "qed",
 | 
			
		||||
    .instance_size            = sizeof(BDRVQEDState),
 | 
			
		||||
    .create_opts              = &qed_create_opts,
 | 
			
		||||
    .supports_backing         = true,
 | 
			
		||||
 | 
			
		||||
    .bdrv_probe               = bdrv_qed_probe,
 | 
			
		||||
    .bdrv_rebind              = bdrv_qed_rebind,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@
 | 
			
		|||
 | 
			
		||||
#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold"
 | 
			
		||||
#define QUORUM_OPT_BLKVERIFY      "blkverify"
 | 
			
		||||
#define QUORUM_OPT_REWRITE        "rewrite-corrupted"
 | 
			
		||||
 | 
			
		||||
/* This union holds a vote hash value */
 | 
			
		||||
typedef union QuorumVoteValue {
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +71,9 @@ typedef struct BDRVQuorumState {
 | 
			
		|||
                            * It is useful to debug other block drivers by
 | 
			
		||||
                            * comparing them with a reference one.
 | 
			
		||||
                            */
 | 
			
		||||
    bool rewrite_corrupted;/* true if the driver must rewrite-on-read corrupted
 | 
			
		||||
                            * block if Quorum is reached.
 | 
			
		||||
                            */
 | 
			
		||||
} BDRVQuorumState;
 | 
			
		||||
 | 
			
		||||
typedef struct QuorumAIOCB QuorumAIOCB;
 | 
			
		||||
| 
						 | 
				
			
			@ -105,13 +109,17 @@ struct QuorumAIOCB {
 | 
			
		|||
    int count;                  /* number of completed AIOCB */
 | 
			
		||||
    int success_count;          /* number of successfully completed AIOCB */
 | 
			
		||||
 | 
			
		||||
    int rewrite_count;          /* number of replica to rewrite: count down to
 | 
			
		||||
                                 * zero once writes are fired
 | 
			
		||||
                                 */
 | 
			
		||||
 | 
			
		||||
    QuorumVotes votes;
 | 
			
		||||
 | 
			
		||||
    bool is_read;
 | 
			
		||||
    int vote_ret;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void quorum_vote(QuorumAIOCB *acb);
 | 
			
		||||
static bool quorum_vote(QuorumAIOCB *acb);
 | 
			
		||||
 | 
			
		||||
static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -183,6 +191,7 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
 | 
			
		|||
    acb->qcrs = g_new0(QuorumChildRequest, s->num_children);
 | 
			
		||||
    acb->count = 0;
 | 
			
		||||
    acb->success_count = 0;
 | 
			
		||||
    acb->rewrite_count = 0;
 | 
			
		||||
    acb->votes.compare = quorum_sha256_compare;
 | 
			
		||||
    QLIST_INIT(&acb->votes.vote_list);
 | 
			
		||||
    acb->is_read = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -232,11 +241,27 @@ static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
 | 
			
		|||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void quorum_rewrite_aio_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    QuorumAIOCB *acb = opaque;
 | 
			
		||||
 | 
			
		||||
    /* one less rewrite to do */
 | 
			
		||||
    acb->rewrite_count--;
 | 
			
		||||
 | 
			
		||||
    /* wait until all rewrite callbacks have completed */
 | 
			
		||||
    if (acb->rewrite_count) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    quorum_aio_finalize(acb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void quorum_aio_cb(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    QuorumChildRequest *sacb = opaque;
 | 
			
		||||
    QuorumAIOCB *acb = sacb->parent;
 | 
			
		||||
    BDRVQuorumState *s = acb->common.bs->opaque;
 | 
			
		||||
    bool rewrite = false;
 | 
			
		||||
 | 
			
		||||
    sacb->ret = ret;
 | 
			
		||||
    acb->count++;
 | 
			
		||||
| 
						 | 
				
			
			@ -253,12 +278,15 @@ static void quorum_aio_cb(void *opaque, int ret)
 | 
			
		|||
 | 
			
		||||
    /* Do the vote on read */
 | 
			
		||||
    if (acb->is_read) {
 | 
			
		||||
        quorum_vote(acb);
 | 
			
		||||
        rewrite = quorum_vote(acb);
 | 
			
		||||
    } else {
 | 
			
		||||
        quorum_has_too_much_io_failed(acb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    quorum_aio_finalize(acb);
 | 
			
		||||
    /* if no rewrite is done the code will finish right away */
 | 
			
		||||
    if (!rewrite) {
 | 
			
		||||
        quorum_aio_finalize(acb);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void quorum_report_bad_versions(BDRVQuorumState *s,
 | 
			
		||||
| 
						 | 
				
			
			@ -278,6 +306,43 @@ static void quorum_report_bad_versions(BDRVQuorumState *s,
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb,
 | 
			
		||||
                                        QuorumVoteValue *value)
 | 
			
		||||
{
 | 
			
		||||
    QuorumVoteVersion *version;
 | 
			
		||||
    QuorumVoteItem *item;
 | 
			
		||||
    int count = 0;
 | 
			
		||||
 | 
			
		||||
    /* first count the number of bad versions: done first to avoid concurrency
 | 
			
		||||
     * issues.
 | 
			
		||||
     */
 | 
			
		||||
    QLIST_FOREACH(version, &acb->votes.vote_list, next) {
 | 
			
		||||
        if (acb->votes.compare(&version->value, value)) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        QLIST_FOREACH(item, &version->items, next) {
 | 
			
		||||
            count++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* quorum_rewrite_aio_cb will count down this to zero */
 | 
			
		||||
    acb->rewrite_count = count;
 | 
			
		||||
 | 
			
		||||
    /* now fire the correcting rewrites */
 | 
			
		||||
    QLIST_FOREACH(version, &acb->votes.vote_list, next) {
 | 
			
		||||
        if (acb->votes.compare(&version->value, value)) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        QLIST_FOREACH(item, &version->items, next) {
 | 
			
		||||
            bdrv_aio_writev(s->bs[item->index], acb->sector_num, acb->qiov,
 | 
			
		||||
                            acb->nb_sectors, quorum_rewrite_aio_cb, acb);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* return true if any rewrite is done else false */
 | 
			
		||||
    return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
| 
						 | 
				
			
			@ -468,16 +533,17 @@ static int quorum_vote_error(QuorumAIOCB *acb)
 | 
			
		|||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void quorum_vote(QuorumAIOCB *acb)
 | 
			
		||||
static bool quorum_vote(QuorumAIOCB *acb)
 | 
			
		||||
{
 | 
			
		||||
    bool quorum = true;
 | 
			
		||||
    bool rewrite = false;
 | 
			
		||||
    int i, j, ret;
 | 
			
		||||
    QuorumVoteValue hash;
 | 
			
		||||
    BDRVQuorumState *s = acb->common.bs->opaque;
 | 
			
		||||
    QuorumVoteVersion *winner;
 | 
			
		||||
 | 
			
		||||
    if (quorum_has_too_much_io_failed(acb)) {
 | 
			
		||||
        return;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* get the index of the first successful read */
 | 
			
		||||
| 
						 | 
				
			
			@ -505,7 +571,7 @@ static void quorum_vote(QuorumAIOCB *acb)
 | 
			
		|||
    /* Every successful read agrees */
 | 
			
		||||
    if (quorum) {
 | 
			
		||||
        quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov);
 | 
			
		||||
        return;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* compute hashes for each successful read, also store indexes */
 | 
			
		||||
| 
						 | 
				
			
			@ -538,9 +604,15 @@ static void quorum_vote(QuorumAIOCB *acb)
 | 
			
		|||
    /* some versions are bad print them */
 | 
			
		||||
    quorum_report_bad_versions(s, acb, &winner->value);
 | 
			
		||||
 | 
			
		||||
    /* corruption correction is enabled */
 | 
			
		||||
    if (s->rewrite_corrupted) {
 | 
			
		||||
        rewrite = quorum_rewrite_bad_versions(s, acb, &winner->value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
free_exit:
 | 
			
		||||
    /* free lists */
 | 
			
		||||
    quorum_free_vote_list(&acb->votes);
 | 
			
		||||
    return rewrite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs,
 | 
			
		||||
| 
						 | 
				
			
			@ -705,6 +777,11 @@ static QemuOptsList quorum_runtime_opts = {
 | 
			
		|||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "Trigger block verify mode if set",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = QUORUM_OPT_REWRITE,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "Rewrite corrupted block on read quorum",
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -766,6 +843,14 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		|||
                "and using two files with vote_threshold=2\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE, false);
 | 
			
		||||
    if (s->rewrite_corrupted && s->is_blkverify) {
 | 
			
		||||
        error_setg(&local_err,
 | 
			
		||||
                   "rewrite-corrupted=on cannot be used with blkverify=on");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* allocate the children BlockDriverState array */
 | 
			
		||||
    s->bs = g_new0(BlockDriverState *, s->num_children);
 | 
			
		||||
    opened = g_new0(bool, s->num_children);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2180,6 +2180,7 @@ static BlockDriver bdrv_vmdk = {
 | 
			
		|||
    .bdrv_detach_aio_context      = vmdk_detach_aio_context,
 | 
			
		||||
    .bdrv_attach_aio_context      = vmdk_attach_aio_context,
 | 
			
		||||
 | 
			
		||||
    .supports_backing             = true,
 | 
			
		||||
    .create_opts                  = &vmdk_create_opts,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								blockdev.c
								
								
								
								
							
							
						
						
									
										47
									
								
								blockdev.c
								
								
								
								
							| 
						 | 
				
			
			@ -1819,6 +1819,11 @@ void qmp_block_resize(bool has_device, const char *device,
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
 | 
			
		||||
        error_set(errp, QERR_DEVICE_IN_USE, device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* complete all in-flight operations before resizing the device */
 | 
			
		||||
    bdrv_drain_all();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2094,6 +2099,8 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
 | 
			
		|||
 | 
			
		||||
void qmp_drive_mirror(const char *device, const char *target,
 | 
			
		||||
                      bool has_format, const char *format,
 | 
			
		||||
                      bool has_node_name, const char *node_name,
 | 
			
		||||
                      bool has_replaces, const char *replaces,
 | 
			
		||||
                      enum MirrorSyncMode sync,
 | 
			
		||||
                      bool has_mode, enum NewImageMode mode,
 | 
			
		||||
                      bool has_speed, int64_t speed,
 | 
			
		||||
| 
						 | 
				
			
			@ -2107,6 +2114,7 @@ void qmp_drive_mirror(const char *device, const char *target,
 | 
			
		|||
    BlockDriverState *source, *target_bs;
 | 
			
		||||
    BlockDriver *drv = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    QDict *options = NULL;
 | 
			
		||||
    int flags;
 | 
			
		||||
    int64_t size;
 | 
			
		||||
    int ret;
 | 
			
		||||
| 
						 | 
				
			
			@ -2180,6 +2188,29 @@ void qmp_drive_mirror(const char *device, const char *target,
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (has_replaces) {
 | 
			
		||||
        BlockDriverState *to_replace_bs;
 | 
			
		||||
 | 
			
		||||
        if (!has_node_name) {
 | 
			
		||||
            error_setg(errp, "a node-name must be provided when replacing a"
 | 
			
		||||
                             " named node of the graph");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        to_replace_bs = check_to_replace_node(replaces, &local_err);
 | 
			
		||||
 | 
			
		||||
        if (!to_replace_bs) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (size != bdrv_getlength(to_replace_bs)) {
 | 
			
		||||
            error_setg(errp, "cannot replace image with a mirror image of "
 | 
			
		||||
                             "different size");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((sync == MIRROR_SYNC_MODE_FULL || !source)
 | 
			
		||||
        && mode != NEW_IMAGE_MODE_EXISTING)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -2208,18 +2239,28 @@ void qmp_drive_mirror(const char *device, const char *target,
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (has_node_name) {
 | 
			
		||||
        options = qdict_new();
 | 
			
		||||
        qdict_put(options, "node-name", qstring_from_str(node_name));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Mirroring takes care of copy-on-write using the source's backing
 | 
			
		||||
     * file.
 | 
			
		||||
     */
 | 
			
		||||
    target_bs = NULL;
 | 
			
		||||
    ret = bdrv_open(&target_bs, target, NULL, NULL, flags | BDRV_O_NO_BACKING,
 | 
			
		||||
                    drv, &local_err);
 | 
			
		||||
    ret = bdrv_open(&target_bs, target, NULL, options,
 | 
			
		||||
                    flags | BDRV_O_NO_BACKING, drv, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mirror_start(bs, target_bs, speed, granularity, buf_size, sync,
 | 
			
		||||
    /* pass the node name to replace to mirror start since it's loose coupling
 | 
			
		||||
     * and will allow to check whether the node still exist at mirror completion
 | 
			
		||||
     */
 | 
			
		||||
    mirror_start(bs, target_bs,
 | 
			
		||||
                 has_replaces ? replaces : NULL,
 | 
			
		||||
                 speed, granularity, buf_size, sync,
 | 
			
		||||
                 on_source_error, on_target_error,
 | 
			
		||||
                 block_job_cb, bs, &local_err);
 | 
			
		||||
    if (local_err != NULL) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								blockjob.c
								
								
								
								
							
							
						
						
									
										22
									
								
								blockjob.c
								
								
								
								
							| 
						 | 
				
			
			@ -210,6 +210,20 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
 | 
			
		|||
    job->busy = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void block_job_yield(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    assert(job->busy);
 | 
			
		||||
 | 
			
		||||
    /* Check cancellation *before* setting busy = false, too!  */
 | 
			
		||||
    if (block_job_is_cancelled(job)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    job->busy = false;
 | 
			
		||||
    qemu_coroutine_yield();
 | 
			
		||||
    job->busy = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockJobInfo *block_job_query(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    BlockJobInfo *info = g_new0(BlockJobInfo, 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +270,11 @@ void block_job_event_completed(BlockJob *job, const char *msg)
 | 
			
		|||
 | 
			
		||||
void block_job_event_ready(BlockJob *job)
 | 
			
		||||
{
 | 
			
		||||
    qapi_event_send_block_job_ready(bdrv_get_device_name(job->bs), &error_abort);
 | 
			
		||||
    qapi_event_send_block_job_ready(job->driver->job_type,
 | 
			
		||||
                                    bdrv_get_device_name(job->bs),
 | 
			
		||||
                                    job->len,
 | 
			
		||||
                                    job->offset,
 | 
			
		||||
                                    job->speed, &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
 | 
			
		||||
| 
						 | 
				
			
			@ -282,7 +300,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
 | 
			
		|||
    default:
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
    qapi_event_send_block_job_error(bdrv_get_device_name(bs),
 | 
			
		||||
    qapi_event_send_block_job_error(bdrv_get_device_name(job->bs),
 | 
			
		||||
                                    is_read ? IO_OPERATION_TYPE_READ :
 | 
			
		||||
                                    IO_OPERATION_TYPE_WRITE,
 | 
			
		||||
                                    action, &error_abort);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5273,6 +5273,18 @@ if test "$docs" = "yes" ; then
 | 
			
		|||
  mkdir -p QMP
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# set up qemu-iotests in this build directory
 | 
			
		||||
iotests_common_env="tests/qemu-iotests/common.env"
 | 
			
		||||
iotests_check="tests/qemu-iotests/check"
 | 
			
		||||
 | 
			
		||||
echo "# Automatically generated by configure - do not modify" > "$iotests_common_env"
 | 
			
		||||
echo >> "$iotests_common_env"
 | 
			
		||||
echo "export PYTHON='$python'" >> "$iotests_common_env"
 | 
			
		||||
 | 
			
		||||
if [ ! -e "$iotests_check" ]; then
 | 
			
		||||
    symlink "$source_path/$iotests_check" "$iotests_check"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Save the configure command line for later reuse.
 | 
			
		||||
cat <<EOD >config.status
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								hmp.c
								
								
								
								
							
							
						
						
									
										1
									
								
								hmp.c
								
								
								
								
							| 
						 | 
				
			
			@ -933,6 +933,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    qmp_drive_mirror(device, filename, !!format, format,
 | 
			
		||||
                     false, NULL, false, NULL,
 | 
			
		||||
                     full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
 | 
			
		||||
                     true, mode, false, 0, false, 0, false, 0,
 | 
			
		||||
                     false, 0, false, 0, &err);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,16 +24,6 @@
 | 
			
		|||
#include "hw/virtio/virtio-bus.h"
 | 
			
		||||
#include "qom/object_interfaces.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    VirtIOBlockDataPlane *s;
 | 
			
		||||
    QEMUIOVector *inhdr;            /* iovecs for virtio_blk_inhdr */
 | 
			
		||||
    VirtQueueElement *elem;         /* saved data from the virtqueue */
 | 
			
		||||
    QEMUIOVector qiov;              /* original request iovecs */
 | 
			
		||||
    struct iovec bounce_iov;        /* used if guest buffers are unaligned */
 | 
			
		||||
    QEMUIOVector bounce_qiov;       /* bounce buffer iovecs */
 | 
			
		||||
    bool read;                      /* read or write? */
 | 
			
		||||
} VirtIOBlockRequest;
 | 
			
		||||
 | 
			
		||||
struct VirtIOBlockDataPlane {
 | 
			
		||||
    bool started;
 | 
			
		||||
    bool starting;
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +47,8 @@ struct VirtIOBlockDataPlane {
 | 
			
		|||
 | 
			
		||||
    /* Operation blocker on BDS */
 | 
			
		||||
    Error *blocker;
 | 
			
		||||
    void (*saved_complete_request)(struct VirtIOBlockReq *req,
 | 
			
		||||
                                   unsigned char status);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Raise an interrupt to signal guest, if necessary */
 | 
			
		||||
| 
						 | 
				
			
			@ -69,215 +61,14 @@ static void notify_guest(VirtIOBlockDataPlane *s)
 | 
			
		|||
    event_notifier_set(s->guest_notifier);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void complete_rdwr(void *opaque, int ret)
 | 
			
		||||
static void complete_request_vring(VirtIOBlockReq *req, unsigned char status)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlockRequest *req = opaque;
 | 
			
		||||
    struct virtio_blk_inhdr hdr;
 | 
			
		||||
    int len;
 | 
			
		||||
    stb_p(&req->in->status, status);
 | 
			
		||||
 | 
			
		||||
    if (likely(ret == 0)) {
 | 
			
		||||
        hdr.status = VIRTIO_BLK_S_OK;
 | 
			
		||||
        len = req->qiov.size;
 | 
			
		||||
    } else {
 | 
			
		||||
        hdr.status = VIRTIO_BLK_S_IOERR;
 | 
			
		||||
        len = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_virtio_blk_data_plane_complete_request(req->s, req->elem->index, ret);
 | 
			
		||||
 | 
			
		||||
    if (req->read && req->bounce_iov.iov_base) {
 | 
			
		||||
        qemu_iovec_from_buf(&req->qiov, 0, req->bounce_iov.iov_base, len);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (req->bounce_iov.iov_base) {
 | 
			
		||||
        qemu_vfree(req->bounce_iov.iov_base);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_iovec_from_buf(req->inhdr, 0, &hdr, sizeof(hdr));
 | 
			
		||||
    qemu_iovec_destroy(req->inhdr);
 | 
			
		||||
    g_slice_free(QEMUIOVector, req->inhdr);
 | 
			
		||||
 | 
			
		||||
    /* According to the virtio specification len should be the number of bytes
 | 
			
		||||
     * written to, but for virtio-blk it seems to be the number of bytes
 | 
			
		||||
     * transferred plus the status bytes.
 | 
			
		||||
     */
 | 
			
		||||
    vring_push(&req->s->vring, req->elem, len + sizeof(hdr));
 | 
			
		||||
    notify_guest(req->s);
 | 
			
		||||
    g_slice_free(VirtIOBlockRequest, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void complete_request_early(VirtIOBlockDataPlane *s, VirtQueueElement *elem,
 | 
			
		||||
                                   QEMUIOVector *inhdr, unsigned char status)
 | 
			
		||||
{
 | 
			
		||||
    struct virtio_blk_inhdr hdr = {
 | 
			
		||||
        .status = status,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    qemu_iovec_from_buf(inhdr, 0, &hdr, sizeof(hdr));
 | 
			
		||||
    qemu_iovec_destroy(inhdr);
 | 
			
		||||
    g_slice_free(QEMUIOVector, inhdr);
 | 
			
		||||
 | 
			
		||||
    vring_push(&s->vring, elem, sizeof(hdr));
 | 
			
		||||
    notify_guest(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get disk serial number */
 | 
			
		||||
static void do_get_id_cmd(VirtIOBlockDataPlane *s,
 | 
			
		||||
                          struct iovec *iov, unsigned int iov_cnt,
 | 
			
		||||
                          VirtQueueElement *elem, QEMUIOVector *inhdr)
 | 
			
		||||
{
 | 
			
		||||
    char id[VIRTIO_BLK_ID_BYTES];
 | 
			
		||||
 | 
			
		||||
    /* Serial number not NUL-terminated when longer than buffer */
 | 
			
		||||
    strncpy(id, s->blk->serial ? s->blk->serial : "", sizeof(id));
 | 
			
		||||
    iov_from_buf(iov, iov_cnt, 0, id, sizeof(id));
 | 
			
		||||
    complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_rdwr_cmd(VirtIOBlockDataPlane *s, bool read,
 | 
			
		||||
                        struct iovec *iov, unsigned iov_cnt,
 | 
			
		||||
                        int64_t sector_num, VirtQueueElement *elem,
 | 
			
		||||
                        QEMUIOVector *inhdr)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlockRequest *req = g_slice_new0(VirtIOBlockRequest);
 | 
			
		||||
    QEMUIOVector *qiov;
 | 
			
		||||
    int nb_sectors;
 | 
			
		||||
 | 
			
		||||
    /* Fill in virtio block metadata needed for completion */
 | 
			
		||||
    req->s = s;
 | 
			
		||||
    req->elem = elem;
 | 
			
		||||
    req->inhdr = inhdr;
 | 
			
		||||
    req->read = read;
 | 
			
		||||
    qemu_iovec_init_external(&req->qiov, iov, iov_cnt);
 | 
			
		||||
 | 
			
		||||
    qiov = &req->qiov;
 | 
			
		||||
 | 
			
		||||
    if (!bdrv_qiov_is_aligned(s->blk->conf.bs, qiov)) {
 | 
			
		||||
        void *bounce_buffer = qemu_blockalign(s->blk->conf.bs, qiov->size);
 | 
			
		||||
 | 
			
		||||
        /* Populate bounce buffer with data for writes */
 | 
			
		||||
        if (!read) {
 | 
			
		||||
            qemu_iovec_to_buf(qiov, 0, bounce_buffer, qiov->size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Redirect I/O to aligned bounce buffer */
 | 
			
		||||
        req->bounce_iov.iov_base = bounce_buffer;
 | 
			
		||||
        req->bounce_iov.iov_len = qiov->size;
 | 
			
		||||
        qemu_iovec_init_external(&req->bounce_qiov, &req->bounce_iov, 1);
 | 
			
		||||
        qiov = &req->bounce_qiov;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nb_sectors = qiov->size / BDRV_SECTOR_SIZE;
 | 
			
		||||
 | 
			
		||||
    if (read) {
 | 
			
		||||
        bdrv_aio_readv(s->blk->conf.bs, sector_num, qiov, nb_sectors,
 | 
			
		||||
                       complete_rdwr, req);
 | 
			
		||||
    } else {
 | 
			
		||||
        bdrv_aio_writev(s->blk->conf.bs, sector_num, qiov, nb_sectors,
 | 
			
		||||
                        complete_rdwr, req);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void complete_flush(void *opaque, int ret)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlockRequest *req = opaque;
 | 
			
		||||
    unsigned char status;
 | 
			
		||||
 | 
			
		||||
    if (ret == 0) {
 | 
			
		||||
        status = VIRTIO_BLK_S_OK;
 | 
			
		||||
    } else {
 | 
			
		||||
        status = VIRTIO_BLK_S_IOERR;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    complete_request_early(req->s, req->elem, req->inhdr, status);
 | 
			
		||||
    g_slice_free(VirtIOBlockRequest, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_flush_cmd(VirtIOBlockDataPlane *s, VirtQueueElement *elem,
 | 
			
		||||
                         QEMUIOVector *inhdr)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlockRequest *req = g_slice_new(VirtIOBlockRequest);
 | 
			
		||||
    req->s = s;
 | 
			
		||||
    req->elem = elem;
 | 
			
		||||
    req->inhdr = inhdr;
 | 
			
		||||
 | 
			
		||||
    bdrv_aio_flush(s->blk->conf.bs, complete_flush, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_scsi_cmd(VirtIOBlockDataPlane *s, VirtQueueElement *elem,
 | 
			
		||||
                        QEMUIOVector *inhdr)
 | 
			
		||||
{
 | 
			
		||||
    int status;
 | 
			
		||||
 | 
			
		||||
    status = virtio_blk_handle_scsi_req(VIRTIO_BLK(s->vdev), elem);
 | 
			
		||||
    complete_request_early(s, elem, inhdr, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int process_request(VirtIOBlockDataPlane *s, VirtQueueElement *elem)
 | 
			
		||||
{
 | 
			
		||||
    struct iovec *iov = elem->out_sg;
 | 
			
		||||
    struct iovec *in_iov = elem->in_sg;
 | 
			
		||||
    unsigned out_num = elem->out_num;
 | 
			
		||||
    unsigned in_num = elem->in_num;
 | 
			
		||||
    struct virtio_blk_outhdr outhdr;
 | 
			
		||||
    QEMUIOVector *inhdr;
 | 
			
		||||
    size_t in_size;
 | 
			
		||||
 | 
			
		||||
    /* Copy in outhdr */
 | 
			
		||||
    if (unlikely(iov_to_buf(iov, out_num, 0, &outhdr,
 | 
			
		||||
                            sizeof(outhdr)) != sizeof(outhdr))) {
 | 
			
		||||
        error_report("virtio-blk request outhdr too short");
 | 
			
		||||
        return -EFAULT;
 | 
			
		||||
    }
 | 
			
		||||
    iov_discard_front(&iov, &out_num, sizeof(outhdr));
 | 
			
		||||
 | 
			
		||||
    /* Grab inhdr for later */
 | 
			
		||||
    in_size = iov_size(in_iov, in_num);
 | 
			
		||||
    if (in_size < sizeof(struct virtio_blk_inhdr)) {
 | 
			
		||||
        error_report("virtio_blk request inhdr too short");
 | 
			
		||||
        return -EFAULT;
 | 
			
		||||
    }
 | 
			
		||||
    inhdr = g_slice_new(QEMUIOVector);
 | 
			
		||||
    qemu_iovec_init(inhdr, 1);
 | 
			
		||||
    qemu_iovec_concat_iov(inhdr, in_iov, in_num,
 | 
			
		||||
            in_size - sizeof(struct virtio_blk_inhdr),
 | 
			
		||||
            sizeof(struct virtio_blk_inhdr));
 | 
			
		||||
    iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr));
 | 
			
		||||
 | 
			
		||||
    /* TODO Linux sets the barrier bit even when not advertised! */
 | 
			
		||||
    outhdr.type &= ~VIRTIO_BLK_T_BARRIER;
 | 
			
		||||
 | 
			
		||||
    switch (outhdr.type) {
 | 
			
		||||
    case VIRTIO_BLK_T_IN:
 | 
			
		||||
        do_rdwr_cmd(s, true, in_iov, in_num,
 | 
			
		||||
                    outhdr.sector * 512 / BDRV_SECTOR_SIZE,
 | 
			
		||||
                    elem, inhdr);
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case VIRTIO_BLK_T_OUT:
 | 
			
		||||
        do_rdwr_cmd(s, false, iov, out_num,
 | 
			
		||||
                    outhdr.sector * 512 / BDRV_SECTOR_SIZE,
 | 
			
		||||
                    elem, inhdr);
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case VIRTIO_BLK_T_SCSI_CMD:
 | 
			
		||||
        do_scsi_cmd(s, elem, inhdr);
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case VIRTIO_BLK_T_FLUSH:
 | 
			
		||||
        do_flush_cmd(s, elem, inhdr);
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case VIRTIO_BLK_T_GET_ID:
 | 
			
		||||
        do_get_id_cmd(s, in_iov, in_num, elem, inhdr);
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        error_report("virtio-blk unsupported request type %#x", outhdr.type);
 | 
			
		||||
        qemu_iovec_destroy(inhdr);
 | 
			
		||||
        g_slice_free(QEMUIOVector, inhdr);
 | 
			
		||||
        return -EFAULT;
 | 
			
		||||
    }
 | 
			
		||||
    vring_push(&req->dev->dataplane->vring, req->elem,
 | 
			
		||||
               req->qiov.size + sizeof(*req->in));
 | 
			
		||||
    notify_guest(req->dev->dataplane);
 | 
			
		||||
    g_slice_free(VirtIOBlockReq, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_notify(EventNotifier *e)
 | 
			
		||||
| 
						 | 
				
			
			@ -286,7 +77,11 @@ static void handle_notify(EventNotifier *e)
 | 
			
		|||
                                           host_notifier);
 | 
			
		||||
 | 
			
		||||
    VirtQueueElement *elem;
 | 
			
		||||
    VirtIOBlockReq *req;
 | 
			
		||||
    int ret;
 | 
			
		||||
    MultiReqBuffer mrb = {
 | 
			
		||||
        .num_writes = 0,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    event_notifier_test_and_clear(&s->host_notifier);
 | 
			
		||||
    for (;;) {
 | 
			
		||||
| 
						 | 
				
			
			@ -303,14 +98,14 @@ static void handle_notify(EventNotifier *e)
 | 
			
		|||
            trace_virtio_blk_data_plane_process_request(s, elem->out_num,
 | 
			
		||||
                                                        elem->in_num, elem->index);
 | 
			
		||||
 | 
			
		||||
            if (process_request(s, elem) < 0) {
 | 
			
		||||
                vring_set_broken(&s->vring);
 | 
			
		||||
                vring_free_element(elem);
 | 
			
		||||
                ret = -EFAULT;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            req = g_slice_new(VirtIOBlockReq);
 | 
			
		||||
            req->dev = VIRTIO_BLK(s->vdev);
 | 
			
		||||
            req->elem = elem;
 | 
			
		||||
            virtio_blk_handle_request(req, &mrb);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        virtio_submit_multiwrite(s->blk->conf.bs, &mrb);
 | 
			
		||||
 | 
			
		||||
        if (likely(ret == -EAGAIN)) { /* vring emptied */
 | 
			
		||||
            /* Re-enable guest->host notifies and stop processing the vring.
 | 
			
		||||
             * But if the guest has snuck in more descriptors, keep processing.
 | 
			
		||||
| 
						 | 
				
			
			@ -330,6 +125,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
 | 
			
		|||
                                  Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlockDataPlane *s;
 | 
			
		||||
    VirtIOBlock *vblk = VIRTIO_BLK(vdev);
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    *dataplane = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -372,6 +168,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
 | 
			
		|||
    bdrv_op_block_all(blk->conf.bs, s->blocker);
 | 
			
		||||
 | 
			
		||||
    *dataplane = s;
 | 
			
		||||
    s->saved_complete_request = vblk->complete_request;
 | 
			
		||||
    vblk->complete_request = complete_request_vring;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Context: QEMU global mutex held */
 | 
			
		||||
| 
						 | 
				
			
			@ -446,10 +244,12 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
 | 
			
		|||
{
 | 
			
		||||
    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
 | 
			
		||||
    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 | 
			
		||||
    VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
 | 
			
		||||
    if (!s->started || s->stopping) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    s->stopping = true;
 | 
			
		||||
    vblk->complete_request = s->saved_complete_request;
 | 
			
		||||
    trace_virtio_blk_data_plane_stop(s);
 | 
			
		||||
 | 
			
		||||
    aio_context_acquire(s->ctx);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "qemu/iov.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
#include "hw/block/block.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -27,18 +28,24 @@
 | 
			
		|||
#endif
 | 
			
		||||
#include "hw/virtio/virtio-bus.h"
 | 
			
		||||
 | 
			
		||||
typedef struct VirtIOBlockReq
 | 
			
		||||
static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlock *dev;
 | 
			
		||||
    VirtQueueElement elem;
 | 
			
		||||
    struct virtio_blk_inhdr *in;
 | 
			
		||||
    struct virtio_blk_outhdr *out;
 | 
			
		||||
    QEMUIOVector qiov;
 | 
			
		||||
    struct VirtIOBlockReq *next;
 | 
			
		||||
    BlockAcctCookie acct;
 | 
			
		||||
} VirtIOBlockReq;
 | 
			
		||||
    VirtIOBlockReq *req = g_slice_new0(VirtIOBlockReq);
 | 
			
		||||
    req->dev = s;
 | 
			
		||||
    req->elem = g_slice_new0(VirtQueueElement);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
 | 
			
		||||
static void virtio_blk_free_request(VirtIOBlockReq *req)
 | 
			
		||||
{
 | 
			
		||||
    if (req) {
 | 
			
		||||
        g_slice_free(VirtQueueElement, req->elem);
 | 
			
		||||
        g_slice_free(VirtIOBlockReq, req);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virtio_blk_complete_request(VirtIOBlockReq *req,
 | 
			
		||||
                                        unsigned char status)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlock *s = req->dev;
 | 
			
		||||
    VirtIODevice *vdev = VIRTIO_DEVICE(s);
 | 
			
		||||
| 
						 | 
				
			
			@ -46,10 +53,15 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
 | 
			
		|||
    trace_virtio_blk_req_complete(req, status);
 | 
			
		||||
 | 
			
		||||
    stb_p(&req->in->status, status);
 | 
			
		||||
    virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
 | 
			
		||||
    virtqueue_push(s->vq, req->elem, req->qiov.size + sizeof(*req->in));
 | 
			
		||||
    virtio_notify(vdev, s->vq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
 | 
			
		||||
{
 | 
			
		||||
    req->dev->complete_request(req, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
 | 
			
		||||
    bool is_read)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +74,7 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
 | 
			
		|||
    } else if (action == BLOCK_ERROR_ACTION_REPORT) {
 | 
			
		||||
        virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
 | 
			
		||||
        bdrv_acct_done(s->bs, &req->acct);
 | 
			
		||||
        g_free(req);
 | 
			
		||||
        virtio_blk_free_request(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bdrv_error_action(s->bs, action, is_read, error);
 | 
			
		||||
| 
						 | 
				
			
			@ -76,14 +88,14 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
 | 
			
		|||
    trace_virtio_blk_rw_complete(req, ret);
 | 
			
		||||
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        bool is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT);
 | 
			
		||||
        bool is_read = !(ldl_p(&req->out.type) & VIRTIO_BLK_T_OUT);
 | 
			
		||||
        if (virtio_blk_handle_rw_error(req, -ret, is_read))
 | 
			
		||||
            return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
 | 
			
		||||
    bdrv_acct_done(req->dev->bs, &req->acct);
 | 
			
		||||
    g_free(req);
 | 
			
		||||
    virtio_blk_free_request(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virtio_blk_flush_complete(void *opaque, int ret)
 | 
			
		||||
| 
						 | 
				
			
			@ -98,27 +110,16 @@ static void virtio_blk_flush_complete(void *opaque, int ret)
 | 
			
		|||
 | 
			
		||||
    virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
 | 
			
		||||
    bdrv_acct_done(req->dev->bs, &req->acct);
 | 
			
		||||
    g_free(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlockReq *req = g_malloc(sizeof(*req));
 | 
			
		||||
    req->dev = s;
 | 
			
		||||
    req->qiov.size = 0;
 | 
			
		||||
    req->next = NULL;
 | 
			
		||||
    return req;
 | 
			
		||||
    virtio_blk_free_request(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
 | 
			
		||||
{
 | 
			
		||||
    VirtIOBlockReq *req = virtio_blk_alloc_request(s);
 | 
			
		||||
 | 
			
		||||
    if (req != NULL) {
 | 
			
		||||
        if (!virtqueue_pop(s->vq, &req->elem)) {
 | 
			
		||||
            g_free(req);
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    if (!virtqueue_pop(s->vq, req->elem)) {
 | 
			
		||||
        virtio_blk_free_request(req);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return req;
 | 
			
		||||
| 
						 | 
				
			
			@ -247,17 +248,12 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
 | 
			
		|||
{
 | 
			
		||||
    int status;
 | 
			
		||||
 | 
			
		||||
    status = virtio_blk_handle_scsi_req(req->dev, &req->elem);
 | 
			
		||||
    status = virtio_blk_handle_scsi_req(req->dev, req->elem);
 | 
			
		||||
    virtio_blk_req_complete(req, status);
 | 
			
		||||
    g_free(req);
 | 
			
		||||
    virtio_blk_free_request(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct MultiReqBuffer {
 | 
			
		||||
    BlockRequest        blkreq[32];
 | 
			
		||||
    unsigned int        num_writes;
 | 
			
		||||
} MultiReqBuffer;
 | 
			
		||||
 | 
			
		||||
static void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb)
 | 
			
		||||
void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb)
 | 
			
		||||
{
 | 
			
		||||
    int i, ret;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -293,7 +289,7 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb)
 | 
			
		|||
    BlockRequest *blkreq;
 | 
			
		||||
    uint64_t sector;
 | 
			
		||||
 | 
			
		||||
    sector = ldq_p(&req->out->sector);
 | 
			
		||||
    sector = ldq_p(&req->out.sector);
 | 
			
		||||
 | 
			
		||||
    bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_WRITE);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -327,7 +323,7 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req)
 | 
			
		|||
{
 | 
			
		||||
    uint64_t sector;
 | 
			
		||||
 | 
			
		||||
    sector = ldq_p(&req->out->sector);
 | 
			
		||||
    sector = ldq_p(&req->out.sector);
 | 
			
		||||
 | 
			
		||||
    bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -346,26 +342,39 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req)
 | 
			
		|||
                   virtio_blk_rw_complete, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virtio_blk_handle_request(VirtIOBlockReq *req,
 | 
			
		||||
    MultiReqBuffer *mrb)
 | 
			
		||||
void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t type;
 | 
			
		||||
    struct iovec *in_iov = req->elem->in_sg;
 | 
			
		||||
    struct iovec *iov = req->elem->out_sg;
 | 
			
		||||
    unsigned in_num = req->elem->in_num;
 | 
			
		||||
    unsigned out_num = req->elem->out_num;
 | 
			
		||||
 | 
			
		||||
    if (req->elem.out_num < 1 || req->elem.in_num < 1) {
 | 
			
		||||
    if (req->elem->out_num < 1 || req->elem->in_num < 1) {
 | 
			
		||||
        error_report("virtio-blk missing headers");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
 | 
			
		||||
        req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
 | 
			
		||||
        error_report("virtio-blk header not in correct element");
 | 
			
		||||
    if (unlikely(iov_to_buf(iov, out_num, 0, &req->out,
 | 
			
		||||
                            sizeof(req->out)) != sizeof(req->out))) {
 | 
			
		||||
        error_report("virtio-blk request outhdr too short");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    req->out = (void *)req->elem.out_sg[0].iov_base;
 | 
			
		||||
    req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
 | 
			
		||||
    iov_discard_front(&iov, &out_num, sizeof(req->out));
 | 
			
		||||
 | 
			
		||||
    type = ldl_p(&req->out->type);
 | 
			
		||||
    if (in_num < 1 ||
 | 
			
		||||
        in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
 | 
			
		||||
        error_report("virtio-blk request inhdr too short");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    req->in = (void *)in_iov[in_num - 1].iov_base
 | 
			
		||||
              + in_iov[in_num - 1].iov_len
 | 
			
		||||
              - sizeof(struct virtio_blk_inhdr);
 | 
			
		||||
    iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr));
 | 
			
		||||
 | 
			
		||||
    type = ldl_p(&req->out.type);
 | 
			
		||||
 | 
			
		||||
    if (type & VIRTIO_BLK_T_FLUSH) {
 | 
			
		||||
        virtio_blk_handle_flush(req, mrb);
 | 
			
		||||
| 
						 | 
				
			
			@ -378,23 +387,23 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req,
 | 
			
		|||
         * NB: per existing s/n string convention the string is
 | 
			
		||||
         * terminated by '\0' only when shorter than buffer.
 | 
			
		||||
         */
 | 
			
		||||
        strncpy(req->elem.in_sg[0].iov_base,
 | 
			
		||||
        strncpy(req->elem->in_sg[0].iov_base,
 | 
			
		||||
                s->blk.serial ? s->blk.serial : "",
 | 
			
		||||
                MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES));
 | 
			
		||||
                MIN(req->elem->in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES));
 | 
			
		||||
        virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
 | 
			
		||||
        g_free(req);
 | 
			
		||||
        virtio_blk_free_request(req);
 | 
			
		||||
    } else if (type & VIRTIO_BLK_T_OUT) {
 | 
			
		||||
        qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
 | 
			
		||||
                                 req->elem.out_num - 1);
 | 
			
		||||
        qemu_iovec_init_external(&req->qiov, &req->elem->out_sg[1],
 | 
			
		||||
                                 req->elem->out_num - 1);
 | 
			
		||||
        virtio_blk_handle_write(req, mrb);
 | 
			
		||||
    } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) {
 | 
			
		||||
        /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */
 | 
			
		||||
        qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
 | 
			
		||||
                                 req->elem.in_num - 1);
 | 
			
		||||
        qemu_iovec_init_external(&req->qiov, &req->elem->in_sg[0],
 | 
			
		||||
                                 req->elem->in_num - 1);
 | 
			
		||||
        virtio_blk_handle_read(req);
 | 
			
		||||
    } else {
 | 
			
		||||
        virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
 | 
			
		||||
        g_free(req);
 | 
			
		||||
        virtio_blk_free_request(req);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -460,7 +469,8 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    if (!s->bh) {
 | 
			
		||||
        s->bh = qemu_bh_new(virtio_blk_dma_restart_bh, s);
 | 
			
		||||
        s->bh = aio_bh_new(bdrv_get_aio_context(s->blk.conf.bs),
 | 
			
		||||
                           virtio_blk_dma_restart_bh, s);
 | 
			
		||||
        qemu_bh_schedule(s->bh);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -609,7 +619,8 @@ static void virtio_blk_save(QEMUFile *f, void *opaque)
 | 
			
		|||
    
 | 
			
		||||
    while (req) {
 | 
			
		||||
        qemu_put_sbyte(f, 1);
 | 
			
		||||
        qemu_put_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
 | 
			
		||||
        qemu_put_buffer(f, (unsigned char *)req->elem,
 | 
			
		||||
                        sizeof(VirtQueueElement));
 | 
			
		||||
        req = req->next;
 | 
			
		||||
    }
 | 
			
		||||
    qemu_put_sbyte(f, 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -631,14 +642,15 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id)
 | 
			
		|||
 | 
			
		||||
    while (qemu_get_sbyte(f)) {
 | 
			
		||||
        VirtIOBlockReq *req = virtio_blk_alloc_request(s);
 | 
			
		||||
        qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
 | 
			
		||||
        qemu_get_buffer(f, (unsigned char *)req->elem,
 | 
			
		||||
                        sizeof(VirtQueueElement));
 | 
			
		||||
        req->next = s->rq;
 | 
			
		||||
        s->rq = req;
 | 
			
		||||
 | 
			
		||||
        virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
 | 
			
		||||
            req->elem.in_num, 1);
 | 
			
		||||
        virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
 | 
			
		||||
            req->elem.out_num, 0);
 | 
			
		||||
        virtqueue_map_sg(req->elem->in_sg, req->elem->in_addr,
 | 
			
		||||
            req->elem->in_num, 1);
 | 
			
		||||
        virtqueue_map_sg(req->elem->out_sg, req->elem->out_addr,
 | 
			
		||||
            req->elem->out_num, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -729,6 +741,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
 | 
			
		|||
    s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1;
 | 
			
		||||
 | 
			
		||||
    s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
 | 
			
		||||
    s->complete_request = virtio_blk_complete_request;
 | 
			
		||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
 | 
			
		||||
    virtio_blk_data_plane_create(vdev, blk, &s->dataplane, &err);
 | 
			
		||||
    if (err != NULL) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,6 +175,7 @@ typedef enum BlockOpType {
 | 
			
		|||
    BLOCK_OP_TYPE_MIRROR,
 | 
			
		||||
    BLOCK_OP_TYPE_RESIZE,
 | 
			
		||||
    BLOCK_OP_TYPE_STREAM,
 | 
			
		||||
    BLOCK_OP_TYPE_REPLACE,
 | 
			
		||||
    BLOCK_OP_TYPE_MAX,
 | 
			
		||||
} BlockOpType;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +214,7 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
 | 
			
		|||
                    bool allow_none, Error **errp);
 | 
			
		||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd);
 | 
			
		||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
 | 
			
		||||
void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp);
 | 
			
		||||
int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp);
 | 
			
		||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
 | 
			
		||||
              const char *reference, QDict *options, int flags,
 | 
			
		||||
              BlockDriver *drv, Error **errp);
 | 
			
		||||
| 
						 | 
				
			
			@ -314,6 +315,9 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
 | 
			
		|||
                                      BlockDriverState *candidate);
 | 
			
		||||
bool bdrv_is_first_non_filter(BlockDriverState *candidate);
 | 
			
		||||
 | 
			
		||||
/* check if a named node can be replaced when doing drive-mirror */
 | 
			
		||||
BlockDriverState *check_to_replace_node(const char *node_name, Error **errp);
 | 
			
		||||
 | 
			
		||||
/* async block I/O */
 | 
			
		||||
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
 | 
			
		||||
                                     int sector_num);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,9 @@ struct BlockDriver {
 | 
			
		|||
     */
 | 
			
		||||
    bool bdrv_needs_filename;
 | 
			
		||||
 | 
			
		||||
    /* Set if a driver can support backing files */
 | 
			
		||||
    bool supports_backing;
 | 
			
		||||
 | 
			
		||||
    /* For handling image reopen for split or non-split files */
 | 
			
		||||
    int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state,
 | 
			
		||||
                               BlockReopenQueue *queue, Error **errp);
 | 
			
		||||
| 
						 | 
				
			
			@ -486,6 +489,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
 | 
			
		|||
 * mirror_start:
 | 
			
		||||
 * @bs: Block device to operate on.
 | 
			
		||||
 * @target: Block device to write to.
 | 
			
		||||
 * @replaces: Block graph node name to replace once the mirror is done. Can
 | 
			
		||||
 *            only be used when full mirroring is selected.
 | 
			
		||||
 * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
 | 
			
		||||
 * @granularity: The chosen granularity for the dirty bitmap.
 | 
			
		||||
 * @buf_size: The amount of data that can be in flight at one time.
 | 
			
		||||
| 
						 | 
				
			
			@ -502,6 +507,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
 | 
			
		|||
 * @bs will be switched to read from @target.
 | 
			
		||||
 */
 | 
			
		||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		||||
                  const char *replaces,
 | 
			
		||||
                  int64_t speed, int64_t granularity, int64_t buf_size,
 | 
			
		||||
                  MirrorSyncMode mode, BlockdevOnError on_source_error,
 | 
			
		||||
                  BlockdevOnError on_target_error,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,6 +146,14 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
 | 
			
		|||
 */
 | 
			
		||||
void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * block_job_yield:
 | 
			
		||||
 * @job: The job that calls the function.
 | 
			
		||||
 *
 | 
			
		||||
 * Yield the block job coroutine.
 | 
			
		||||
 */
 | 
			
		||||
void block_job_yield(BlockJob *job);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * block_job_completed:
 | 
			
		||||
 * @job: The job being completed.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,6 @@ void bdrv_query_image_info(BlockDriverState *bs,
 | 
			
		|||
void bdrv_query_info(BlockDriverState *bs,
 | 
			
		||||
                     BlockInfo **p_info,
 | 
			
		||||
                     Error **errp);
 | 
			
		||||
BlockStats *bdrv_query_stats(const BlockDriverState *bs);
 | 
			
		||||
 | 
			
		||||
void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f,
 | 
			
		||||
                        QEMUSnapshotInfo *sn);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
#include "hw/virtio/virtio.h"
 | 
			
		||||
#include "hw/block/block.h"
 | 
			
		||||
#include "sysemu/iothread.h"
 | 
			
		||||
#include "block/block.h"
 | 
			
		||||
 | 
			
		||||
#define TYPE_VIRTIO_BLK "virtio-blk-device"
 | 
			
		||||
#define VIRTIO_BLK(obj) \
 | 
			
		||||
| 
						 | 
				
			
			@ -116,6 +117,7 @@ struct VirtIOBlkConf
 | 
			
		|||
 | 
			
		||||
struct VirtIOBlockDataPlane;
 | 
			
		||||
 | 
			
		||||
struct VirtIOBlockReq;
 | 
			
		||||
typedef struct VirtIOBlock {
 | 
			
		||||
    VirtIODevice parent_obj;
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
| 
						 | 
				
			
			@ -127,12 +129,29 @@ typedef struct VirtIOBlock {
 | 
			
		|||
    unsigned short sector_mask;
 | 
			
		||||
    bool original_wce;
 | 
			
		||||
    VMChangeStateEntry *change;
 | 
			
		||||
    /* Function to push to vq and notify guest */
 | 
			
		||||
    void (*complete_request)(struct VirtIOBlockReq *req, unsigned char status);
 | 
			
		||||
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
 | 
			
		||||
    Notifier migration_state_notifier;
 | 
			
		||||
    struct VirtIOBlockDataPlane *dataplane;
 | 
			
		||||
#endif
 | 
			
		||||
} VirtIOBlock;
 | 
			
		||||
 | 
			
		||||
typedef struct MultiReqBuffer {
 | 
			
		||||
    BlockRequest        blkreq[32];
 | 
			
		||||
    unsigned int        num_writes;
 | 
			
		||||
} MultiReqBuffer;
 | 
			
		||||
 | 
			
		||||
typedef struct VirtIOBlockReq {
 | 
			
		||||
    VirtIOBlock *dev;
 | 
			
		||||
    VirtQueueElement *elem;
 | 
			
		||||
    struct virtio_blk_inhdr *in;
 | 
			
		||||
    struct virtio_blk_outhdr out;
 | 
			
		||||
    QEMUIOVector qiov;
 | 
			
		||||
    struct VirtIOBlockReq *next;
 | 
			
		||||
    BlockAcctCookie acct;
 | 
			
		||||
} VirtIOBlockReq;
 | 
			
		||||
 | 
			
		||||
#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \
 | 
			
		||||
        DEFINE_VIRTIO_COMMON_FEATURES(_state, _field)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -158,4 +177,8 @@ void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk);
 | 
			
		|||
int virtio_blk_handle_scsi_req(VirtIOBlock *blk,
 | 
			
		||||
                               VirtQueueElement *elem);
 | 
			
		||||
 | 
			
		||||
void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb);
 | 
			
		||||
 | 
			
		||||
void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -765,6 +765,13 @@
 | 
			
		|||
# @format: #optional the format of the new destination, default is to
 | 
			
		||||
#          probe if @mode is 'existing', else the format of the source
 | 
			
		||||
#
 | 
			
		||||
# @node-name: #optional the new block driver state node name in the graph
 | 
			
		||||
#             (Since 2.1)
 | 
			
		||||
#
 | 
			
		||||
# @replaces: #optional with sync=full graph node name to be replaced by the new
 | 
			
		||||
#            image when a whole image copy is done. This can be used to repair
 | 
			
		||||
#            broken Quorum files. (Since 2.1)
 | 
			
		||||
#
 | 
			
		||||
# @mode: #optional whether and how QEMU should create a new image, default is
 | 
			
		||||
#        'absolute-paths'.
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -797,6 +804,7 @@
 | 
			
		|||
##
 | 
			
		||||
{ 'command': 'drive-mirror',
 | 
			
		||||
  'data': { 'device': 'str', 'target': 'str', '*format': 'str',
 | 
			
		||||
            '*node-name': 'str', '*replaces': 'str',
 | 
			
		||||
            'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
 | 
			
		||||
            '*speed': 'int', '*granularity': 'uint32',
 | 
			
		||||
            '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
 | 
			
		||||
| 
						 | 
				
			
			@ -1329,12 +1337,15 @@
 | 
			
		|||
#
 | 
			
		||||
# @vote-threshold: the vote limit under which a read will fail
 | 
			
		||||
#
 | 
			
		||||
# @rewrite-corrupted: #optional rewrite corrupted data when quorum is reached
 | 
			
		||||
#                     (Since 2.1)
 | 
			
		||||
#
 | 
			
		||||
# Since: 2.0
 | 
			
		||||
##
 | 
			
		||||
{ 'type': 'BlockdevOptionsQuorum',
 | 
			
		||||
  'data': { '*blkverify': 'bool',
 | 
			
		||||
            'children': [ 'BlockdevRef' ],
 | 
			
		||||
            'vote-threshold': 'int' } }
 | 
			
		||||
            'vote-threshold': 'int', '*rewrite-corrupted': 'bool' } }
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# @BlockdevOptions
 | 
			
		||||
| 
						 | 
				
			
			@ -1545,19 +1556,32 @@
 | 
			
		|||
{ 'event': 'BLOCK_JOB_ERROR',
 | 
			
		||||
  'data': { 'device'   : 'str',
 | 
			
		||||
            'operation': 'IoOperationType',
 | 
			
		||||
            'action'   : 'BlockdevOnError' } }
 | 
			
		||||
            'action'   : 'BlockErrorAction' } }
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
# @BLOCK_JOB_READY
 | 
			
		||||
#
 | 
			
		||||
# Emitted when a block job is ready to complete
 | 
			
		||||
#
 | 
			
		||||
# @type: job type
 | 
			
		||||
#
 | 
			
		||||
# @device: device name
 | 
			
		||||
#
 | 
			
		||||
# @len: maximum progress value
 | 
			
		||||
#
 | 
			
		||||
# @offset: current progress value. On success this is equal to len.
 | 
			
		||||
#          On failure this is less than len
 | 
			
		||||
#
 | 
			
		||||
# @speed: rate limit, bytes per second
 | 
			
		||||
#
 | 
			
		||||
# Note: The "ready to complete" status is always reset by a @BLOCK_JOB_ERROR
 | 
			
		||||
# event
 | 
			
		||||
#
 | 
			
		||||
# Since: 1.3
 | 
			
		||||
##
 | 
			
		||||
{ 'event': 'BLOCK_JOB_READY',
 | 
			
		||||
  'data': { 'device': 'str' } }
 | 
			
		||||
  'data': { 'type'  : 'BlockJobType',
 | 
			
		||||
            'device': 'str',
 | 
			
		||||
            'len'   : 'int',
 | 
			
		||||
            'offset': 'int',
 | 
			
		||||
            'speed' : 'int' } }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1293,6 +1293,7 @@ EQMP
 | 
			
		|||
    {
 | 
			
		||||
        .name       = "drive-mirror",
 | 
			
		||||
        .args_type  = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
 | 
			
		||||
                      "node-name:s?,replaces:s?,"
 | 
			
		||||
                      "on-source-error:s?,on-target-error:s?,"
 | 
			
		||||
                      "granularity:i?,buf-size:i?",
 | 
			
		||||
        .mhandler.cmd_new = qmp_marshal_input_drive_mirror,
 | 
			
		||||
| 
						 | 
				
			
			@ -1314,6 +1315,10 @@ Arguments:
 | 
			
		|||
- "device": device name to operate on (json-string)
 | 
			
		||||
- "target": name of new image file (json-string)
 | 
			
		||||
- "format": format of new image (json-string, optional)
 | 
			
		||||
- "node-name": the name of the new block driver state in the node graph
 | 
			
		||||
               (json-string, optional)
 | 
			
		||||
- "replaces": the block driver node name to replace when finished
 | 
			
		||||
              (json-string, optional)
 | 
			
		||||
- "mode": how an image file should be created into the target
 | 
			
		||||
  file/device (NewImageMode, optional, default 'absolute-paths')
 | 
			
		||||
- "speed": maximum speed of the streaming job, in bytes per second
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,22 +56,22 @@ for IMGOPTS in "compat=0.10" "compat=1.1"; do
 | 
			
		|||
    echo === Create image with unknown header extension ===
 | 
			
		||||
    echo
 | 
			
		||||
    _make_test_img 64M
 | 
			
		||||
    ./qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension"
 | 
			
		||||
    ./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
    $PYTHON qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension"
 | 
			
		||||
    $PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
    _check_test_img
 | 
			
		||||
 | 
			
		||||
    echo
 | 
			
		||||
    echo === Rewrite header with no backing file ===
 | 
			
		||||
    echo
 | 
			
		||||
    $QEMU_IMG rebase -u -b "" "$TEST_IMG"
 | 
			
		||||
    ./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
    $PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
    _check_test_img
 | 
			
		||||
 | 
			
		||||
    echo
 | 
			
		||||
    echo === Add a backing file and format ===
 | 
			
		||||
    echo
 | 
			
		||||
    $QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device "$TEST_IMG"
 | 
			
		||||
    ./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
    $PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# success, all done
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,15 +53,15 @@ IMGOPTS="compat=1.1"
 | 
			
		|||
echo === Create image with unknown autoclear feature bit ===
 | 
			
		||||
echo
 | 
			
		||||
_make_test_img 64M
 | 
			
		||||
./qcow2.py "$TEST_IMG" set-feature-bit autoclear 63
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 63
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo === Repair image ===
 | 
			
		||||
echo
 | 
			
		||||
_check_test_img -r all
 | 
			
		||||
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
 | 
			
		||||
# success, all done
 | 
			
		||||
echo "*** done"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ _make_test_img $size
 | 
			
		|||
$QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
# The dirty bit must not be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ _make_test_img $size
 | 
			
		|||
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
# The dirty bit must be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +84,7 @@ echo "== Read-only access must still work =="
 | 
			
		|||
$QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
# The dirty bit must be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "== Repairing the image file must succeed =="
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +92,7 @@ echo "== Repairing the image file must succeed =="
 | 
			
		|||
_check_test_img -r all
 | 
			
		||||
 | 
			
		||||
# The dirty bit must not be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "== Data should still be accessible after repair =="
 | 
			
		||||
| 
						 | 
				
			
			@ -108,12 +108,12 @@ _make_test_img $size
 | 
			
		|||
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
# The dirty bit must be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
$QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
# The dirty bit must not be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "== Creating an image file with lazy_refcounts=off =="
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +124,7 @@ _make_test_img $size
 | 
			
		|||
_no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
# The dirty bit must not be set since lazy_refcounts=off
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
| 
						 | 
				
			
			@ -140,8 +140,8 @@ $QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io
 | 
			
		|||
$QEMU_IMG commit "$TEST_IMG"
 | 
			
		||||
 | 
			
		||||
# The dirty bit must not be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
./qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
_check_test_img
 | 
			
		||||
TEST_IMG="$TEST_IMG".base _check_test_img
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,12 +35,13 @@ test_img = os.path.join(iotests.test_dir, 'test.img')
 | 
			
		|||
class ImageCommitTestCase(iotests.QMPTestCase):
 | 
			
		||||
    '''Abstract base class for image commit test cases'''
 | 
			
		||||
 | 
			
		||||
    def run_commit_test(self, top, base):
 | 
			
		||||
    def run_commit_test(self, top, base, need_ready=False):
 | 
			
		||||
        self.assert_no_active_block_jobs()
 | 
			
		||||
        result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        completed = False
 | 
			
		||||
        ready = False
 | 
			
		||||
        while not completed:
 | 
			
		||||
            for event in self.vm.get_qmp_events(wait=True):
 | 
			
		||||
                if event['event'] == 'BLOCK_JOB_COMPLETED':
 | 
			
		||||
| 
						 | 
				
			
			@ -48,8 +49,11 @@ class ImageCommitTestCase(iotests.QMPTestCase):
 | 
			
		|||
                    self.assert_qmp(event, 'data/device', 'drive0')
 | 
			
		||||
                    self.assert_qmp(event, 'data/offset', self.image_len)
 | 
			
		||||
                    self.assert_qmp(event, 'data/len', self.image_len)
 | 
			
		||||
                    if need_ready:
 | 
			
		||||
                        self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event")
 | 
			
		||||
                    completed = True
 | 
			
		||||
                elif event['event'] == 'BLOCK_JOB_READY':
 | 
			
		||||
                    ready = True
 | 
			
		||||
                    self.assert_qmp(event, 'data/type', 'commit')
 | 
			
		||||
                    self.assert_qmp(event, 'data/device', 'drive0')
 | 
			
		||||
                    self.assert_qmp(event, 'data/len', self.image_len)
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +67,7 @@ class TestSingleDrive(ImageCommitTestCase):
 | 
			
		|||
    test_len = 1 * 1024 * 256
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        iotests.create_image(backing_img, TestSingleDrive.image_len)
 | 
			
		||||
        iotests.create_image(backing_img, self.image_len)
 | 
			
		||||
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
 | 
			
		||||
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
 | 
			
		||||
        qemu_io('-c', 'write -P 0xab 0 524288', backing_img)
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +109,7 @@ class TestSingleDrive(ImageCommitTestCase):
 | 
			
		|||
        self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
 | 
			
		||||
 | 
			
		||||
    def test_top_is_active(self):
 | 
			
		||||
        self.run_commit_test(test_img, backing_img)
 | 
			
		||||
        self.run_commit_test(test_img, backing_img, need_ready=True)
 | 
			
		||||
        self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
 | 
			
		||||
        self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -238,6 +242,8 @@ class TestSetSpeed(ImageCommitTestCase):
 | 
			
		|||
 | 
			
		||||
        self.cancel_and_wait(resume=True)
 | 
			
		||||
 | 
			
		||||
class TestActiveZeroLengthImage(TestSingleDrive):
 | 
			
		||||
    image_len = 0
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    iotests.main(supported_fmts=['qcow2', 'qed'])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
................
 | 
			
		||||
........................
 | 
			
		||||
----------------------------------------------------------------------
 | 
			
		||||
Ran 16 tests
 | 
			
		||||
Ran 24 tests
 | 
			
		||||
 | 
			
		||||
OK
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,12 @@ target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
 | 
			
		|||
test_img = os.path.join(iotests.test_dir, 'test.img')
 | 
			
		||||
target_img = os.path.join(iotests.test_dir, 'target.img')
 | 
			
		||||
 | 
			
		||||
quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img')
 | 
			
		||||
quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img')
 | 
			
		||||
quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
 | 
			
		||||
quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
 | 
			
		||||
quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
 | 
			
		||||
 | 
			
		||||
class ImageMirroringTestCase(iotests.QMPTestCase):
 | 
			
		||||
    '''Abstract base class for image mirroring test cases'''
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,8 +48,8 @@ class ImageMirroringTestCase(iotests.QMPTestCase):
 | 
			
		|||
                    ready = True
 | 
			
		||||
 | 
			
		||||
    def wait_ready_and_cancel(self, drive='drive0'):
 | 
			
		||||
        self.wait_ready(drive)
 | 
			
		||||
        event = self.cancel_and_wait()
 | 
			
		||||
        self.wait_ready(drive=drive)
 | 
			
		||||
        event = self.cancel_and_wait(drive=drive)
 | 
			
		||||
        self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
 | 
			
		||||
        self.assert_qmp(event, 'data/type', 'mirror')
 | 
			
		||||
        self.assert_qmp(event, 'data/offset', self.image_len)
 | 
			
		||||
| 
						 | 
				
			
			@ -52,19 +58,19 @@ class ImageMirroringTestCase(iotests.QMPTestCase):
 | 
			
		|||
    def complete_and_wait(self, drive='drive0', wait_ready=True):
 | 
			
		||||
        '''Complete a block job and wait for it to finish'''
 | 
			
		||||
        if wait_ready:
 | 
			
		||||
            self.wait_ready()
 | 
			
		||||
            self.wait_ready(drive=drive)
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('block-job-complete', device=drive)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        event = self.wait_until_completed()
 | 
			
		||||
        event = self.wait_until_completed(drive=drive)
 | 
			
		||||
        self.assert_qmp(event, 'data/type', 'mirror')
 | 
			
		||||
 | 
			
		||||
class TestSingleDrive(ImageMirroringTestCase):
 | 
			
		||||
    image_len = 1 * 1024 * 1024 # MB
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        iotests.create_image(backing_img, TestSingleDrive.image_len)
 | 
			
		||||
        iotests.create_image(backing_img, self.image_len)
 | 
			
		||||
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
 | 
			
		||||
        self.vm = iotests.VM().add_drive(test_img)
 | 
			
		||||
        self.vm.launch()
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +169,7 @@ class TestSingleDrive(ImageMirroringTestCase):
 | 
			
		|||
        self.assert_no_active_block_jobs()
 | 
			
		||||
 | 
			
		||||
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
 | 
			
		||||
                        % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img)
 | 
			
		||||
                        % (self.image_len, self.image_len), target_img)
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
 | 
			
		||||
                             buf_size=65536, mode='existing', target=target_img)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +185,7 @@ class TestSingleDrive(ImageMirroringTestCase):
 | 
			
		|||
        self.assert_no_active_block_jobs()
 | 
			
		||||
 | 
			
		||||
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
 | 
			
		||||
                        % (TestSingleDrive.image_len, backing_img), target_img)
 | 
			
		||||
                        % (self.image_len, backing_img), target_img)
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
 | 
			
		||||
                             mode='existing', target=target_img)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +212,11 @@ class TestSingleDrive(ImageMirroringTestCase):
 | 
			
		|||
                             target=target_img)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
 | 
			
		||||
 | 
			
		||||
class TestSingleDriveZeroLength(TestSingleDrive):
 | 
			
		||||
    image_len = 0
 | 
			
		||||
    test_small_buffer2 = None
 | 
			
		||||
    test_large_cluster = None
 | 
			
		||||
 | 
			
		||||
class TestMirrorNoBacking(ImageMirroringTestCase):
 | 
			
		||||
    image_len = 2 * 1024 * 1024 # MB
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -718,5 +729,187 @@ class TestUnbackedSource(ImageMirroringTestCase):
 | 
			
		|||
        self.complete_and_wait()
 | 
			
		||||
        self.assert_no_active_block_jobs()
 | 
			
		||||
 | 
			
		||||
class TestRepairQuorum(ImageMirroringTestCase):
 | 
			
		||||
    """ This class test quorum file repair using drive-mirror.
 | 
			
		||||
        It's mostly a fork of TestSingleDrive """
 | 
			
		||||
    image_len = 1 * 1024 * 1024 # MB
 | 
			
		||||
    IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ]
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.vm = iotests.VM()
 | 
			
		||||
 | 
			
		||||
        # Add each individual quorum images
 | 
			
		||||
        for i in self.IMAGES:
 | 
			
		||||
            qemu_img('create', '-f', iotests.imgfmt, i,
 | 
			
		||||
                     str(TestSingleDrive.image_len))
 | 
			
		||||
            # Assign a node name to each quorum image in order to manipulate
 | 
			
		||||
            # them
 | 
			
		||||
            opts = "node-name=img%i" % self.IMAGES.index(i)
 | 
			
		||||
            self.vm = self.vm.add_drive(i, opts)
 | 
			
		||||
 | 
			
		||||
        self.vm.launch()
 | 
			
		||||
 | 
			
		||||
        #assemble the quorum block device from the individual files
 | 
			
		||||
        args = { "options" : { "driver": "quorum", "id": "quorum0",
 | 
			
		||||
                 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
 | 
			
		||||
        result = self.vm.qmp("blockdev-add", **args)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        self.vm.shutdown()
 | 
			
		||||
        for i in self.IMAGES + [ quorum_repair_img ]:
 | 
			
		||||
            # Do a try/except because the test may have deleted some images
 | 
			
		||||
            try:
 | 
			
		||||
                os.remove(i)
 | 
			
		||||
            except OSError:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
    def test_complete(self):
 | 
			
		||||
        self.assert_no_active_block_jobs()
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name="repair0",
 | 
			
		||||
                             replaces="img1",
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        self.complete_and_wait(drive="quorum0")
 | 
			
		||||
        result = self.vm.qmp('query-named-block-nodes')
 | 
			
		||||
        self.assert_qmp(result, 'return[0]/file', quorum_repair_img)
 | 
			
		||||
        # TODO: a better test requiring some QEMU infrastructure will be added
 | 
			
		||||
        #       to check that this file is really driven by quorum
 | 
			
		||||
        self.vm.shutdown()
 | 
			
		||||
        self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
 | 
			
		||||
                        'target image does not match source after mirroring')
 | 
			
		||||
 | 
			
		||||
    def test_cancel(self):
 | 
			
		||||
        self.assert_no_active_block_jobs()
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name="repair0",
 | 
			
		||||
                             replaces="img1",
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        self.cancel_and_wait(drive="quorum0", force=True)
 | 
			
		||||
        # here we check that the last registered quorum file has not been
 | 
			
		||||
        # swapped out and unref
 | 
			
		||||
        result = self.vm.qmp('query-named-block-nodes')
 | 
			
		||||
        self.assert_qmp(result, 'return[0]/file', quorum_img3)
 | 
			
		||||
        self.vm.shutdown()
 | 
			
		||||
 | 
			
		||||
    def test_cancel_after_ready(self):
 | 
			
		||||
        self.assert_no_active_block_jobs()
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name="repair0",
 | 
			
		||||
                             replaces="img1",
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        self.wait_ready_and_cancel(drive="quorum0")
 | 
			
		||||
        result = self.vm.qmp('query-named-block-nodes')
 | 
			
		||||
        # here we check that the last registered quorum file has not been
 | 
			
		||||
        # swapped out and unref
 | 
			
		||||
        self.assert_qmp(result, 'return[0]/file', quorum_img3)
 | 
			
		||||
        self.vm.shutdown()
 | 
			
		||||
        self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
 | 
			
		||||
                        'target image does not match source after mirroring')
 | 
			
		||||
 | 
			
		||||
    def test_pause(self):
 | 
			
		||||
        self.assert_no_active_block_jobs()
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name="repair0",
 | 
			
		||||
                             replaces="img1",
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('block-job-pause', device='quorum0')
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        time.sleep(1)
 | 
			
		||||
        result = self.vm.qmp('query-block-jobs')
 | 
			
		||||
        offset = self.dictpath(result, 'return[0]/offset')
 | 
			
		||||
 | 
			
		||||
        time.sleep(1)
 | 
			
		||||
        result = self.vm.qmp('query-block-jobs')
 | 
			
		||||
        self.assert_qmp(result, 'return[0]/offset', offset)
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('block-job-resume', device='quorum0')
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        self.complete_and_wait(drive="quorum0")
 | 
			
		||||
        self.vm.shutdown()
 | 
			
		||||
        self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
 | 
			
		||||
                        'target image does not match source after mirroring')
 | 
			
		||||
 | 
			
		||||
    def test_medium_not_found(self):
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full',
 | 
			
		||||
                             node_name='repair0',
 | 
			
		||||
                             replaces='img1',
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'GenericError')
 | 
			
		||||
 | 
			
		||||
    def test_image_not_found(self):
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name='repair0',
 | 
			
		||||
                             replaces='img1',
 | 
			
		||||
                             mode='existing',
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'GenericError')
 | 
			
		||||
 | 
			
		||||
    def test_device_not_found(self):
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full',
 | 
			
		||||
                             node_name='repair0',
 | 
			
		||||
                             replaces='img1',
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
 | 
			
		||||
 | 
			
		||||
    def test_wrong_sync_mode(self):
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0',
 | 
			
		||||
                             node_name='repair0',
 | 
			
		||||
                             replaces='img1',
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'GenericError')
 | 
			
		||||
 | 
			
		||||
    def test_no_node_name(self):
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             replaces='img1',
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'GenericError')
 | 
			
		||||
 | 
			
		||||
    def test_unexistant_replaces(self):
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name='repair0',
 | 
			
		||||
                             replaces='img77',
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'GenericError')
 | 
			
		||||
 | 
			
		||||
    def test_after_a_quorum_snapshot(self):
 | 
			
		||||
        result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
 | 
			
		||||
                             snapshot_file=quorum_snapshot_file,
 | 
			
		||||
                             snapshot_node_name="snap1");
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name='repair0',
 | 
			
		||||
                             replaces="img1",
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'error/class', 'GenericError')
 | 
			
		||||
 | 
			
		||||
        result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
 | 
			
		||||
                             node_name='repair0',
 | 
			
		||||
                             replaces="snap1",
 | 
			
		||||
                             target=quorum_repair_img, format=iotests.imgfmt)
 | 
			
		||||
        self.assert_qmp(result, 'return', {})
 | 
			
		||||
 | 
			
		||||
        self.complete_and_wait(drive="quorum0")
 | 
			
		||||
        result = self.vm.qmp('query-named-block-nodes')
 | 
			
		||||
        self.assert_qmp(result, 'return[0]/file', quorum_repair_img)
 | 
			
		||||
        # TODO: a better test requiring some QEMU infrastructure will be added
 | 
			
		||||
        #       to check that this file is really driven by quorum
 | 
			
		||||
        self.vm.shutdown()
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    iotests.main(supported_fmts=['qcow2', 'qed'])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
...........................
 | 
			
		||||
..............................................
 | 
			
		||||
----------------------------------------------------------------------
 | 
			
		||||
Ran 27 tests
 | 
			
		||||
Ran 46 tests
 | 
			
		||||
 | 
			
		||||
OK
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -92,6 +92,7 @@ echo
 | 
			
		|||
 | 
			
		||||
run_qemu -drive file="$TEST_IMG",format=foo
 | 
			
		||||
run_qemu -drive file="$TEST_IMG",driver=foo
 | 
			
		||||
run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo === Overriding backing file ===
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +100,11 @@ echo
 | 
			
		|||
 | 
			
		||||
echo "info block" | run_qemu -drive file="$TEST_IMG",driver=qcow2,backing.file.filename="$TEST_IMG.orig" -nodefaults
 | 
			
		||||
 | 
			
		||||
# Drivers that don't support backing files
 | 
			
		||||
run_qemu -drive file="$TEST_IMG",driver=raw,backing.file.filename="$TEST_IMG.orig"
 | 
			
		||||
run_qemu -drive file="$TEST_IMG",file.backing.driver=file,file.backing.filename="$TEST_IMG.orig"
 | 
			
		||||
run_qemu -drive file="$TEST_IMG",file.backing.driver=qcow2,file.backing.file.filename="$TEST_IMG.orig"
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo === Enable and disable lazy refcounting on the command line, plus some invalid values ===
 | 
			
		||||
echo
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,10 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=foo
 | 
			
		|||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=foo: 'foo' invalid format
 | 
			
		||||
 | 
			
		||||
Testing: -drive file=TEST_DIR/t.qcow2,driver=foo
 | 
			
		||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: could not open disk image TEST_DIR/t.qcow2: Invalid driver: 'foo'
 | 
			
		||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: could not open disk image TEST_DIR/t.qcow2: Unknown driver 'foo'
 | 
			
		||||
 | 
			
		||||
Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2
 | 
			
		||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,format=qcow2: could not open disk image TEST_DIR/t.qcow2: Driver specified twice
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Overriding backing file ===
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +53,15 @@ ide0-hd0: TEST_DIR/t.qcow2 (qcow2)
 | 
			
		|||
    Backing file:     TEST_DIR/t.qcow2.orig (chain depth: 1)
 | 
			
		||||
(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K
 | 
			
		||||
 | 
			
		||||
Testing: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig
 | 
			
		||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=raw,backing.file.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files
 | 
			
		||||
 | 
			
		||||
Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig
 | 
			
		||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=file,file.backing.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files
 | 
			
		||||
 | 
			
		||||
Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig
 | 
			
		||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig: could not open disk image TEST_DIR/t.qcow2: Driver doesn't support backing files
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Enable and disable lazy refcounting on the command line, plus some invalid values ===
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ _make_test_img $((1024*1024))T
 | 
			
		|||
echo
 | 
			
		||||
echo "creating too large image (1 EB) using qcow2.py"
 | 
			
		||||
_make_test_img 4G
 | 
			
		||||
./qcow2.py "$TEST_IMG" set-header size $((1024 ** 6))
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" set-header size $((1024 ** 6))
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
# success, all done
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,13 +68,13 @@ poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x03\x00\x00"
 | 
			
		|||
_check_test_img
 | 
			
		||||
 | 
			
		||||
# The corrupt bit should not be set anyway
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
# Try to write something, thereby forcing the corrupt bit to be set
 | 
			
		||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
# The corrupt bit must now be set
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
# Try to open the image R/W (which should fail)
 | 
			
		||||
$QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \
 | 
			
		||||
| 
						 | 
				
			
			@ -99,19 +99,19 @@ poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01"
 | 
			
		|||
# Redirect new data cluster onto refcount block
 | 
			
		||||
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
 | 
			
		||||
_check_test_img
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
# Try to fix it
 | 
			
		||||
_check_test_img -r all
 | 
			
		||||
 | 
			
		||||
# The corrupt bit should be cleared
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
# Look if it's really really fixed
 | 
			
		||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "=== Testing cluster data reference into inactive L2 table ==="
 | 
			
		||||
| 
						 | 
				
			
			@ -124,13 +124,13 @@ $QEMU_IO -c "$OPEN_RW" -c "write -P 2 0 512" | _filter_qemu_io
 | 
			
		|||
poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \
 | 
			
		||||
                      "\x80\x00\x00\x00\x00\x04\x00\x00"
 | 
			
		||||
_check_test_img
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
_check_test_img -r all
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 | 
			
		||||
 | 
			
		||||
# Check data
 | 
			
		||||
$QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,9 +48,9 @@ echo "=== Testing version downgrade with zero expansion ==="
 | 
			
		|||
echo
 | 
			
		||||
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
 | 
			
		||||
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,9 +59,9 @@ echo "=== Testing dirty version downgrade ==="
 | 
			
		|||
echo
 | 
			
		||||
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
 | 
			
		||||
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -69,11 +69,11 @@ echo
 | 
			
		|||
echo "=== Testing version downgrade with unknown compat/autoclear flags ==="
 | 
			
		||||
echo
 | 
			
		||||
IMGOPTS="compat=1.1" _make_test_img 64M
 | 
			
		||||
./qcow2.py "$TEST_IMG" set-feature-bit compatible 42
 | 
			
		||||
./qcow2.py "$TEST_IMG" set-feature-bit autoclear 42
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit compatible 42
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 42
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
| 
						 | 
				
			
			@ -81,9 +81,9 @@ echo "=== Testing version upgrade and resize ==="
 | 
			
		|||
echo
 | 
			
		||||
IMGOPTS="compat=0.10" _make_test_img 64M
 | 
			
		||||
$QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG"
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -92,9 +92,9 @@ echo "=== Testing dirty lazy_refcounts=off ==="
 | 
			
		|||
echo
 | 
			
		||||
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
 | 
			
		||||
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG"
 | 
			
		||||
./qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
 | 
			
		||||
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
 | 
			
		||||
_check_test_img
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#!/usr/bin/env python2
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
#
 | 
			
		||||
# Test for additional information emitted by qemu-img info on qcow2
 | 
			
		||||
# images
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,16 +133,29 @@ run_qemu -drive "file=$TEST_DIR/2.raw,format=$IMGFMT,if=none,id=drive2" <<EOF
 | 
			
		|||
{ "execute": "quit" }
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "== using quorum rewrite corrupted mode =="
 | 
			
		||||
 | 
			
		||||
quorum="$quorum,file.rewrite-corrupted=on"
 | 
			
		||||
 | 
			
		||||
$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "== checking that quorum has corrected the corrupted file =="
 | 
			
		||||
 | 
			
		||||
$QEMU_IO -c "read -P 0x32 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "== breaking quorum =="
 | 
			
		||||
 | 
			
		||||
$QEMU_IO -c "write -P 0x41 0 $size" "$TEST_DIR/1.raw" | _filter_qemu_io
 | 
			
		||||
$QEMU_IO -c "write -P 0x42 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "== checking that quorum is broken =="
 | 
			
		||||
 | 
			
		||||
$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# success, all done
 | 
			
		||||
echo "*** done"
 | 
			
		||||
rm -f $seq.full
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,9 +40,19 @@ read 10485760/10485760 bytes at offset 0
 | 
			
		|||
{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== using quorum rewrite corrupted mode ==
 | 
			
		||||
read 10485760/10485760 bytes at offset 0
 | 
			
		||||
10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
			
		||||
 | 
			
		||||
== checking that quorum has corrected the corrupted file ==
 | 
			
		||||
read 10485760/10485760 bytes at offset 0
 | 
			
		||||
10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
			
		||||
 | 
			
		||||
== breaking quorum ==
 | 
			
		||||
wrote 10485760/10485760 bytes at offset 0
 | 
			
		||||
10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
			
		||||
wrote 10485760/10485760 bytes at offset 0
 | 
			
		||||
10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
			
		||||
 | 
			
		||||
== checking that quorum is broken ==
 | 
			
		||||
qemu-io: can't open: Could not read image for determining its format: Input/output error
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ choose_tcp_port() {
 | 
			
		|||
 | 
			
		||||
wait_for_tcp_port() {
 | 
			
		||||
	while ! (netstat --tcp --listening --numeric | \
 | 
			
		||||
		 grep "$1.*0.0.0.0:\*.*LISTEN") 2>&1 >/dev/null; do
 | 
			
		||||
		 grep "$1.*0\\.0\\.0\\.0:\\*.*LISTEN") 2>&1 >/dev/null; do
 | 
			
		||||
		sleep 0.1
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -55,8 +55,8 @@ filter_nbd() {
 | 
			
		|||
	# callbacks sometimes, making them unreliable.
 | 
			
		||||
	#
 | 
			
		||||
	# Filter out the TCP port number since this changes between runs.
 | 
			
		||||
	sed -e 's#^nbd.c:.*##g' \
 | 
			
		||||
	    -e 's#nbd:127.0.0.1:[^:]*:#nbd:127.0.0.1:PORT:#g'
 | 
			
		||||
	sed -e 's#^.*nbd\.c:.*##g' \
 | 
			
		||||
	    -e 's#nbd:127\.0\.0\.1:[^:]*:#nbd:127\.0\.0\.1:PORT:#g'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
check_disconnect() {
 | 
			
		||||
| 
						 | 
				
			
			@ -81,8 +81,8 @@ EOF
 | 
			
		|||
		nbd_url="nbd:127.0.0.1:$port:exportname=foo"
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	./nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null &
 | 
			
		||||
	wait_for_tcp_port "127.0.0.1:$port"
 | 
			
		||||
	$PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null &
 | 
			
		||||
	wait_for_tcp_port "127\\.0\\.0\\.1:$port"
 | 
			
		||||
	$QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | filter_nbd
 | 
			
		||||
 | 
			
		||||
	echo
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
#
 | 
			
		||||
# Test for commit of larger active layer
 | 
			
		||||
#
 | 
			
		||||
# This tests live snapshots of images on a running QEMU instance, using
 | 
			
		||||
# QMP commands.  Both single disk snapshots, and transactional group
 | 
			
		||||
# snapshots are performed.
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2014 Red Hat, Inc.
 | 
			
		||||
#
 | 
			
		||||
# This program is free software; you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
# creator
 | 
			
		||||
owner=jcody@redhat.com
 | 
			
		||||
 | 
			
		||||
seq=`basename $0`
 | 
			
		||||
echo "QA output created by $seq"
 | 
			
		||||
 | 
			
		||||
here=`pwd`
 | 
			
		||||
status=1	# failure is the default!
 | 
			
		||||
 | 
			
		||||
_cleanup()
 | 
			
		||||
{
 | 
			
		||||
    _cleanup_qemu
 | 
			
		||||
    rm  -f "${TEST_IMG}.base" "${TEST_IMG}.snp1"
 | 
			
		||||
	_cleanup_test_img
 | 
			
		||||
}
 | 
			
		||||
trap "_cleanup; exit \$status" 0 1 2 3 15
 | 
			
		||||
 | 
			
		||||
# get standard environment, filters and checks
 | 
			
		||||
. ./common.rc
 | 
			
		||||
. ./common.filter
 | 
			
		||||
. ./common.qemu
 | 
			
		||||
 | 
			
		||||
_supported_fmt qcow2
 | 
			
		||||
_supported_proto file
 | 
			
		||||
_supported_os Linux
 | 
			
		||||
 | 
			
		||||
size_smaller=5M
 | 
			
		||||
size_larger=100M
 | 
			
		||||
 | 
			
		||||
_make_test_img $size_smaller
 | 
			
		||||
mv "${TEST_IMG}" "${TEST_IMG}.base"
 | 
			
		||||
 | 
			
		||||
_make_test_img -b "${TEST_IMG}.base" $size_larger
 | 
			
		||||
mv "${TEST_IMG}" "${TEST_IMG}.snp1"
 | 
			
		||||
 | 
			
		||||
_make_test_img -b "${TEST_IMG}.snp1" $size_larger
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "=== Base image info before commit and resize ==="
 | 
			
		||||
$QEMU_IMG info "${TEST_IMG}.base" | _filter_testdir
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo === Running QEMU Live Commit Test ===
 | 
			
		||||
echo
 | 
			
		||||
 | 
			
		||||
qemu_comm_method="qmp"
 | 
			
		||||
_launch_qemu -drive file="${TEST_IMG}",if=virtio,id=test
 | 
			
		||||
h=$QEMU_HANDLE
 | 
			
		||||
 | 
			
		||||
_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return"
 | 
			
		||||
 | 
			
		||||
_send_qemu_cmd $h "{ 'execute': 'block-commit',
 | 
			
		||||
                                 'arguments': { 'device': 'test',
 | 
			
		||||
                                 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED"
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "=== Base image info after commit and resize ==="
 | 
			
		||||
$QEMU_IMG info "${TEST_IMG}.base" | _filter_testdir
 | 
			
		||||
 | 
			
		||||
# success, all done
 | 
			
		||||
echo "*** done"
 | 
			
		||||
rm -f $seq.full
 | 
			
		||||
status=0
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
QA output created by 095
 | 
			
		||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=5242880 
 | 
			
		||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.base' 
 | 
			
		||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file='TEST_DIR/t.IMGFMT.snp1' 
 | 
			
		||||
 | 
			
		||||
=== Base image info before commit and resize ===
 | 
			
		||||
image: TEST_DIR/t.qcow2.base
 | 
			
		||||
file format: qcow2
 | 
			
		||||
virtual size: 5.0M (5242880 bytes)
 | 
			
		||||
disk size: 196K
 | 
			
		||||
cluster_size: 65536
 | 
			
		||||
Format specific information:
 | 
			
		||||
    compat: 1.1
 | 
			
		||||
    lazy refcounts: false
 | 
			
		||||
 | 
			
		||||
=== Running QEMU Live Commit Test ===
 | 
			
		||||
 | 
			
		||||
{"return": {}}
 | 
			
		||||
{"return": {}}
 | 
			
		||||
{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}}
 | 
			
		||||
 | 
			
		||||
=== Base image info after commit and resize ===
 | 
			
		||||
image: TEST_DIR/t.qcow2.base
 | 
			
		||||
file format: qcow2
 | 
			
		||||
virtual size: 100M (104857600 bytes)
 | 
			
		||||
disk size: 196K
 | 
			
		||||
cluster_size: 65536
 | 
			
		||||
Format specific information:
 | 
			
		||||
    compat: 1.1
 | 
			
		||||
    lazy refcounts: false
 | 
			
		||||
*** done
 | 
			
		||||
| 
						 | 
				
			
			@ -34,22 +34,95 @@ timestamp=${TIMESTAMP:=false}
 | 
			
		|||
# generic initialization
 | 
			
		||||
iam=check
 | 
			
		||||
 | 
			
		||||
# we need common.config
 | 
			
		||||
if ! . ./common.config
 | 
			
		||||
then
 | 
			
		||||
    echo "$iam: failed to source common.config"
 | 
			
		||||
_init_error()
 | 
			
		||||
{
 | 
			
		||||
    echo "$iam: $1" >&2
 | 
			
		||||
    exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if [ -L "$0" ]
 | 
			
		||||
then
 | 
			
		||||
    # called from the build tree
 | 
			
		||||
    source_iotests=$(dirname "$(readlink "$0")")
 | 
			
		||||
    if [ -z "$source_iotests" ]
 | 
			
		||||
    then
 | 
			
		||||
        _init_error "failed to obtain source tree name from check symlink"
 | 
			
		||||
    fi
 | 
			
		||||
    source_iotests=$(cd "$source_iotests"; pwd) || _init_error "failed to enter source tree"
 | 
			
		||||
    build_iotests=$PWD
 | 
			
		||||
else
 | 
			
		||||
    # called from the source tree
 | 
			
		||||
    source_iotests=$PWD
 | 
			
		||||
    # this may be an in-tree build (note that in the following code we may not
 | 
			
		||||
    # assume that it truly is and have to test whether the build results
 | 
			
		||||
    # actually exist)
 | 
			
		||||
    build_iotests=$PWD
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
build_root="$build_iotests/../.."
 | 
			
		||||
 | 
			
		||||
if [ -x "$build_iotests/socket_scm_helper" ]
 | 
			
		||||
then
 | 
			
		||||
    export SOCKET_SCM_HELPER="$build_iotests/socket_scm_helper"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# if ./qemu exists, it should be prioritized and will be chosen by common.config
 | 
			
		||||
if [[ -z "$QEMU_PROG" && ! -x './qemu' ]]
 | 
			
		||||
then
 | 
			
		||||
    arch=$(uname -m 2> /dev/null)
 | 
			
		||||
 | 
			
		||||
    if [[ -n $arch && -x "$build_root/$arch-softmmu/qemu-system-$arch" ]]
 | 
			
		||||
    then
 | 
			
		||||
        export QEMU_PROG="$build_root/$arch-softmmu/qemu-system-$arch"
 | 
			
		||||
    else
 | 
			
		||||
        pushd "$build_root" > /dev/null
 | 
			
		||||
        for binary in *-softmmu/qemu-system-*
 | 
			
		||||
        do
 | 
			
		||||
            if [ -x "$binary" ]
 | 
			
		||||
            then
 | 
			
		||||
                export QEMU_PROG="$build_root/$binary"
 | 
			
		||||
                break
 | 
			
		||||
            fi
 | 
			
		||||
        done
 | 
			
		||||
        popd > /dev/null
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ -z $QEMU_IMG_PROG && -x "$build_root/qemu-img" && ! -x './qemu-img' ]]
 | 
			
		||||
then
 | 
			
		||||
    export QEMU_IMG_PROG="$build_root/qemu-img"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ -z $QEMU_IO_PROG && -x "$build_root/qemu-io" && ! -x './qemu-io' ]]
 | 
			
		||||
then
 | 
			
		||||
    export QEMU_IO_PROG="$build_root/qemu-io"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ -z $QEMU_NBD_PROG && -x "$build_root/qemu-nbd" && ! -x './qemu-nbd' ]]
 | 
			
		||||
then
 | 
			
		||||
    export QEMU_NBD_PROG="$build_root/qemu-nbd"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# we need common.env
 | 
			
		||||
if ! . "$build_iotests/common.env"
 | 
			
		||||
then
 | 
			
		||||
    _init_error "failed to source common.env (make sure the qemu-iotests are run from tests/qemu-iotests in the build tree)"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# we need common.config
 | 
			
		||||
if ! . "$source_iotests/common.config"
 | 
			
		||||
then
 | 
			
		||||
    _init_error "failed to source common.config"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# we need common.rc
 | 
			
		||||
if ! . ./common.rc
 | 
			
		||||
if ! . "$source_iotests/common.rc"
 | 
			
		||||
then
 | 
			
		||||
    echo "check: failed to source common.rc"
 | 
			
		||||
    exit 1
 | 
			
		||||
    _init_error "failed to source common.rc"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# we need common
 | 
			
		||||
. ./common
 | 
			
		||||
. "$source_iotests/common"
 | 
			
		||||
 | 
			
		||||
#if [ `id -u` -ne 0 ]
 | 
			
		||||
#then
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +267,7 @@ do
 | 
			
		|||
        echo " - expunged"
 | 
			
		||||
        rm -f $seq.out.bad
 | 
			
		||||
        echo "/^$seq\$/d" >>$tmp.expunged
 | 
			
		||||
    elif [ ! -f $seq ]
 | 
			
		||||
    elif [ ! -f "$source_iotests/$seq" ]
 | 
			
		||||
    then
 | 
			
		||||
        echo " - no such test?"
 | 
			
		||||
        echo "/^$seq\$/d" >>$tmp.expunged
 | 
			
		||||
| 
						 | 
				
			
			@ -215,9 +288,16 @@ do
 | 
			
		|||
 | 
			
		||||
        start=`_wallclock`
 | 
			
		||||
        $timestamp && echo -n "        ["`date "+%T"`"]"
 | 
			
		||||
        [ ! -x $seq ] && chmod u+x $seq # ensure we can run it
 | 
			
		||||
 | 
			
		||||
        if [ "$(head -n 1 "$source_iotests/$seq")" == "#!/usr/bin/env python" ]; then
 | 
			
		||||
            run_command="$PYTHON $seq"
 | 
			
		||||
        else
 | 
			
		||||
            run_command="./$seq"
 | 
			
		||||
        fi
 | 
			
		||||
        export OUTPUT_DIR=$PWD
 | 
			
		||||
        (cd "$source_iotests";
 | 
			
		||||
        MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
 | 
			
		||||
                ./$seq >$tmp.out 2>&1
 | 
			
		||||
                $run_command >$tmp.out 2>&1)
 | 
			
		||||
        sts=$?
 | 
			
		||||
        $timestamp && _timestamp
 | 
			
		||||
        stop=`_wallclock`
 | 
			
		||||
| 
						 | 
				
			
			@ -242,17 +322,17 @@ do
 | 
			
		|||
                err=true
 | 
			
		||||
            fi
 | 
			
		||||
 | 
			
		||||
            reference=$seq.out
 | 
			
		||||
            reference="$source_iotests/$seq.out"
 | 
			
		||||
            if [ "$CACHEMODE" = "none" ]; then
 | 
			
		||||
                [ -f $seq.out.nocache ] && reference=$seq.out.nocache
 | 
			
		||||
                [ -f "$source_iotests/$seq.out.nocache" ] && reference="$source_iotests/$seq.out.nocache"
 | 
			
		||||
            fi
 | 
			
		||||
 | 
			
		||||
            if [ ! -f $reference ]
 | 
			
		||||
            if [ ! -f "$reference" ]
 | 
			
		||||
            then
 | 
			
		||||
                echo " - no qualified output"
 | 
			
		||||
                err=true
 | 
			
		||||
            else
 | 
			
		||||
                if diff -w $reference $tmp.out >/dev/null 2>&1
 | 
			
		||||
                if diff -w "$reference" $tmp.out >/dev/null 2>&1
 | 
			
		||||
                then
 | 
			
		||||
                    echo ""
 | 
			
		||||
                    if $err
 | 
			
		||||
| 
						 | 
				
			
			@ -264,7 +344,7 @@ do
 | 
			
		|||
                else
 | 
			
		||||
                    echo " - output mismatch (see $seq.out.bad)"
 | 
			
		||||
                    mv $tmp.out $seq.out.bad
 | 
			
		||||
                    $diff -w $reference $seq.out.bad
 | 
			
		||||
                    $diff -w "$reference" $seq.out.bad
 | 
			
		||||
                    err=true
 | 
			
		||||
                fi
 | 
			
		||||
            fi
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,8 +25,7 @@ _setenvironment()
 | 
			
		|||
    export MSGVERB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
here=`pwd`
 | 
			
		||||
rm -f $here/$iam.out
 | 
			
		||||
rm -f "$OUTPUT_DIR/$iam.out"
 | 
			
		||||
_setenvironment
 | 
			
		||||
 | 
			
		||||
check=${check-true}
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +58,7 @@ do
 | 
			
		|||
    if $group
 | 
			
		||||
    then
 | 
			
		||||
        # arg after -g
 | 
			
		||||
        group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
 | 
			
		||||
        group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
 | 
			
		||||
s/ .*//p
 | 
			
		||||
}'`
 | 
			
		||||
        if [ -z "$group_list" ]
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +83,7 @@ s/ .*//p
 | 
			
		|||
    then
 | 
			
		||||
        # arg after -x
 | 
			
		||||
        [ ! -s $tmp.list ] && ls [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] >$tmp.list 2>/dev/null
 | 
			
		||||
        group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
 | 
			
		||||
        group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
 | 
			
		||||
s/ .*//p
 | 
			
		||||
}'`
 | 
			
		||||
        if [ -z "$group_list" ]
 | 
			
		||||
| 
						 | 
				
			
			@ -366,7 +365,7 @@ testlist options
 | 
			
		|||
BEGIN        { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
 | 
			
		||||
        | while read id
 | 
			
		||||
        do
 | 
			
		||||
            if grep -s "^$id " group >/dev/null
 | 
			
		||||
            if grep -s "^$id " "$source_iotests/group" >/dev/null
 | 
			
		||||
            then
 | 
			
		||||
                # in group file ... OK
 | 
			
		||||
                echo $id >>$tmp.list
 | 
			
		||||
| 
						 | 
				
			
			@ -402,7 +401,7 @@ else
 | 
			
		|||
        touch $tmp.list
 | 
			
		||||
    else
 | 
			
		||||
        # no test numbers, do everything from group file
 | 
			
		||||
        sed -n -e '/^[0-9][0-9][0-9]*/s/[         ].*//p' <group >$tmp.list
 | 
			
		||||
        sed -n -e '/^[0-9][0-9][0-9]*/s/[         ].*//p' <"$source_iotests/group" >$tmp.list
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -126,7 +126,7 @@ fi
 | 
			
		|||
export TEST_DIR
 | 
			
		||||
 | 
			
		||||
if [ -z "$SAMPLE_IMG_DIR" ]; then
 | 
			
		||||
        SAMPLE_IMG_DIR=`pwd`/sample_images
 | 
			
		||||
        SAMPLE_IMG_DIR="$source_iotests/sample_images"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ ! -d "$SAMPLE_IMG_DIR" ]; then
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -318,9 +318,9 @@ _do()
 | 
			
		|||
        status=1; exit
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    (eval "echo '---' \"$_cmd\"") >>$here/$seq.full
 | 
			
		||||
    (eval "echo '---' \"$_cmd\"") >>"$OUTPUT_DIR/$seq.full"
 | 
			
		||||
    (eval "$_cmd") >$tmp._out 2>&1; ret=$?
 | 
			
		||||
    cat $tmp._out >>$here/$seq.full
 | 
			
		||||
    cat $tmp._out >>"$OUTPUT_DIR/$seq.full"
 | 
			
		||||
    if [ $# -eq 2 ]; then
 | 
			
		||||
        if [ $ret -eq 0 ]; then
 | 
			
		||||
            echo "done"
 | 
			
		||||
| 
						 | 
				
			
			@ -344,7 +344,7 @@ _do()
 | 
			
		|||
#
 | 
			
		||||
_notrun()
 | 
			
		||||
{
 | 
			
		||||
    echo "$*" >$seq.notrun
 | 
			
		||||
    echo "$*" >"$OUTPUT_DIR/$seq.notrun"
 | 
			
		||||
    echo "$seq not run: $*"
 | 
			
		||||
    status=0
 | 
			
		||||
    exit
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +354,7 @@ _notrun()
 | 
			
		|||
#
 | 
			
		||||
_fail()
 | 
			
		||||
{
 | 
			
		||||
    echo "$*" | tee -a $here/$seq.full
 | 
			
		||||
    echo "$*" | tee -a "$OUTPUT_DIR/$seq.full"
 | 
			
		||||
    echo "(see $seq.full for details)"
 | 
			
		||||
    status=1
 | 
			
		||||
    exit 1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,3 +99,4 @@
 | 
			
		|||
090 rw auto quick
 | 
			
		||||
091 rw auto
 | 
			
		||||
092 rw auto quick
 | 
			
		||||
095 rw auto
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ qemu_args = os.environ.get('QEMU', 'qemu').strip().split(' ')
 | 
			
		|||
imgfmt = os.environ.get('IMGFMT', 'raw')
 | 
			
		||||
imgproto = os.environ.get('IMGPROTO', 'file')
 | 
			
		||||
test_dir = os.environ.get('TEST_DIR', '/var/tmp')
 | 
			
		||||
output_dir = os.environ.get('OUTPUT_DIR', '.')
 | 
			
		||||
cachemode = os.environ.get('CACHEMODE')
 | 
			
		||||
 | 
			
		||||
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
 | 
			
		||||
| 
						 | 
				
			
			@ -278,7 +279,7 @@ def notrun(reason):
 | 
			
		|||
    # Each test in qemu-iotests has a number ("seq")
 | 
			
		||||
    seq = os.path.basename(sys.argv[0])
 | 
			
		||||
 | 
			
		||||
    open('%s.notrun' % seq, 'wb').write(reason + '\n')
 | 
			
		||||
    open('%s/%s.notrun' % (output_dir, seq), 'wb').write(reason + '\n')
 | 
			
		||||
    print '%s not run: %s' % (seq, reason)
 | 
			
		||||
    sys.exit(0)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1111,6 +1111,7 @@ QemuOptsList *qemu_opts_append(QemuOptsList *dst,
 | 
			
		|||
    size_t num_opts, num_dst_opts;
 | 
			
		||||
    QemuOptDesc *desc;
 | 
			
		||||
    bool need_init = false;
 | 
			
		||||
    bool need_head_update;
 | 
			
		||||
 | 
			
		||||
    if (!list) {
 | 
			
		||||
        return dst;
 | 
			
		||||
| 
						 | 
				
			
			@ -1121,6 +1122,12 @@ QemuOptsList *qemu_opts_append(QemuOptsList *dst,
 | 
			
		|||
     */
 | 
			
		||||
    if (!dst) {
 | 
			
		||||
        need_init = true;
 | 
			
		||||
        need_head_update = true;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Moreover, even if dst is not NULL, the realloc may move it to a
 | 
			
		||||
         * different address in which case we may get a stale tail pointer
 | 
			
		||||
         * in dst->head. */
 | 
			
		||||
        need_head_update = QTAILQ_EMPTY(&dst->head);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    num_opts = count_opts_list(dst);
 | 
			
		||||
| 
						 | 
				
			
			@ -1131,9 +1138,11 @@ QemuOptsList *qemu_opts_append(QemuOptsList *dst,
 | 
			
		|||
    if (need_init) {
 | 
			
		||||
        dst->name = NULL;
 | 
			
		||||
        dst->implied_opt_name = NULL;
 | 
			
		||||
        QTAILQ_INIT(&dst->head);
 | 
			
		||||
        dst->merge_lists = false;
 | 
			
		||||
    }
 | 
			
		||||
    if (need_head_update) {
 | 
			
		||||
        QTAILQ_INIT(&dst->head);
 | 
			
		||||
    }
 | 
			
		||||
    dst->desc[num_dst_opts].name = NULL;
 | 
			
		||||
 | 
			
		||||
    /* append list->desc to dst->desc */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue