Merge remote branch 'kwolf/for-anthony' into staging
This commit is contained in:
commit
358c360feb
13
block.c
13
block.c
|
@ -56,7 +56,6 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
|
||||||
uint8_t *buf, int nb_sectors);
|
uint8_t *buf, int nb_sectors);
|
||||||
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
|
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
|
||||||
const uint8_t *buf, int nb_sectors);
|
const uint8_t *buf, int nb_sectors);
|
||||||
static BlockDriver *find_protocol(const char *filename);
|
|
||||||
|
|
||||||
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
|
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
|
||||||
QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
||||||
|
@ -210,7 +209,7 @@ int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
|
||||||
{
|
{
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
|
|
||||||
drv = find_protocol(filename);
|
drv = bdrv_find_protocol(filename);
|
||||||
if (drv == NULL) {
|
if (drv == NULL) {
|
||||||
drv = bdrv_find_format("file");
|
drv = bdrv_find_format("file");
|
||||||
}
|
}
|
||||||
|
@ -283,7 +282,7 @@ static BlockDriver *find_hdev_driver(const char *filename)
|
||||||
return drv;
|
return drv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockDriver *find_protocol(const char *filename)
|
BlockDriver *bdrv_find_protocol(const char *filename)
|
||||||
{
|
{
|
||||||
BlockDriver *drv1;
|
BlockDriver *drv1;
|
||||||
char protocol[128];
|
char protocol[128];
|
||||||
|
@ -333,8 +332,10 @@ static BlockDriver *find_image_format(const char *filename)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Return the raw BlockDriver * to scsi-generic devices */
|
/* Return the raw BlockDriver * to scsi-generic devices */
|
||||||
if (bs->sg)
|
if (bs->sg) {
|
||||||
|
bdrv_delete(bs);
|
||||||
return bdrv_find_format("raw");
|
return bdrv_find_format("raw");
|
||||||
|
}
|
||||||
|
|
||||||
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
|
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
|
||||||
bdrv_delete(bs);
|
bdrv_delete(bs);
|
||||||
|
@ -478,7 +479,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
drv = find_protocol(filename);
|
drv = bdrv_find_protocol(filename);
|
||||||
if (!drv) {
|
if (!drv) {
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
@ -1950,7 +1951,7 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
|
||||||
// Add the second request
|
// Add the second request
|
||||||
qemu_iovec_concat(qiov, reqs[i].qiov, reqs[i].qiov->size);
|
qemu_iovec_concat(qiov, reqs[i].qiov, reqs[i].qiov->size);
|
||||||
|
|
||||||
reqs[outidx].nb_sectors += reqs[i].nb_sectors;
|
reqs[outidx].nb_sectors = qiov->size >> 9;
|
||||||
reqs[outidx].qiov = qiov;
|
reqs[outidx].qiov = qiov;
|
||||||
|
|
||||||
mcb->callbacks[i].free_qiov = reqs[outidx].qiov;
|
mcb->callbacks[i].free_qiov = reqs[outidx].qiov;
|
||||||
|
|
3
block.h
3
block.h
|
@ -38,7 +38,7 @@ typedef struct QEMUSnapshotInfo {
|
||||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
|
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
|
||||||
|
|
||||||
#define BDRV_SECTOR_BITS 9
|
#define BDRV_SECTOR_BITS 9
|
||||||
#define BDRV_SECTOR_SIZE (1 << BDRV_SECTOR_BITS)
|
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
|
||||||
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
|
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -54,6 +54,7 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data);
|
||||||
|
|
||||||
void bdrv_init(void);
|
void bdrv_init(void);
|
||||||
void bdrv_init_with_whitelist(void);
|
void bdrv_init_with_whitelist(void);
|
||||||
|
BlockDriver *bdrv_find_protocol(const char *filename);
|
||||||
BlockDriver *bdrv_find_format(const char *format_name);
|
BlockDriver *bdrv_find_format(const char *format_name);
|
||||||
BlockDriver *bdrv_find_whitelisted_format(const char *format_name);
|
BlockDriver *bdrv_find_whitelisted_format(const char *format_name);
|
||||||
int bdrv_create(BlockDriver *drv, const char* filename,
|
int bdrv_create(BlockDriver *drv, const char* filename,
|
||||||
|
|
|
@ -157,31 +157,36 @@ static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset)
|
||||||
* the image file failed.
|
* the image file failed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset)
|
static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||||
|
uint64_t **l2_table)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
int min_index;
|
int min_index;
|
||||||
uint64_t *l2_table;
|
int ret;
|
||||||
|
|
||||||
/* seek if the table for the given offset is in the cache */
|
/* seek if the table for the given offset is in the cache */
|
||||||
|
|
||||||
l2_table = seek_l2_table(s, l2_offset);
|
*l2_table = seek_l2_table(s, l2_offset);
|
||||||
if (l2_table != NULL)
|
if (*l2_table != NULL) {
|
||||||
return l2_table;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* not found: load a new entry in the least used one */
|
/* not found: load a new entry in the least used one */
|
||||||
|
|
||||||
min_index = l2_cache_new_entry(bs);
|
min_index = l2_cache_new_entry(bs);
|
||||||
l2_table = s->l2_cache + (min_index << s->l2_bits);
|
*l2_table = s->l2_cache + (min_index << s->l2_bits);
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
|
BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
|
||||||
if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
|
ret = bdrv_pread(bs->file, l2_offset, *l2_table,
|
||||||
s->l2_size * sizeof(uint64_t))
|
s->l2_size * sizeof(uint64_t));
|
||||||
return NULL;
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
s->l2_cache_offsets[min_index] = l2_offset;
|
s->l2_cache_offsets[min_index] = l2_offset;
|
||||||
s->l2_cache_counts[min_index] = 1;
|
s->l2_cache_counts[min_index] = 1;
|
||||||
|
|
||||||
return l2_table;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -239,14 +244,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||||
return l2_offset;
|
return l2_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update the L1 entry */
|
|
||||||
|
|
||||||
s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
|
|
||||||
ret = write_l1_entry(bs, l1_index);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate a new entry in the l2 cache */
|
/* allocate a new entry in the l2 cache */
|
||||||
|
|
||||||
min_index = l2_cache_new_entry(bs);
|
min_index = l2_cache_new_entry(bs);
|
||||||
|
@ -261,7 +258,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||||
ret = bdrv_pread(bs->file, old_l2_offset, l2_table,
|
ret = bdrv_pread(bs->file, old_l2_offset, l2_table,
|
||||||
s->l2_size * sizeof(uint64_t));
|
s->l2_size * sizeof(uint64_t));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* write the l2 table to the file */
|
/* write the l2 table to the file */
|
||||||
|
@ -269,7 +266,14 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||||
ret = bdrv_pwrite(bs->file, l2_offset, l2_table,
|
ret = bdrv_pwrite(bs->file, l2_offset, l2_table,
|
||||||
s->l2_size * sizeof(uint64_t));
|
s->l2_size * sizeof(uint64_t));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the L1 entry */
|
||||||
|
s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
|
||||||
|
ret = write_l1_entry(bs, l1_index);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update the l2 cache entry */
|
/* update the l2 cache entry */
|
||||||
|
@ -279,6 +283,10 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||||
|
|
||||||
*table = l2_table;
|
*table = l2_table;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
qcow2_l2_cache_reset(bs);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
|
static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
|
||||||
|
@ -342,7 +350,13 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
|
||||||
while (nb_sectors > 0) {
|
while (nb_sectors > 0) {
|
||||||
n = nb_sectors;
|
n = nb_sectors;
|
||||||
cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, &n);
|
|
||||||
|
ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n,
|
||||||
|
&cluster_offset);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||||
if (!cluster_offset) {
|
if (!cluster_offset) {
|
||||||
if (bs->backing_hd) {
|
if (bs->backing_hd) {
|
||||||
|
@ -409,28 +423,29 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
|
||||||
/*
|
/*
|
||||||
* get_cluster_offset
|
* get_cluster_offset
|
||||||
*
|
*
|
||||||
* For a given offset of the disk image, return cluster offset in
|
* For a given offset of the disk image, find the cluster offset in
|
||||||
* qcow2 file.
|
* qcow2 file. The offset is stored in *cluster_offset.
|
||||||
*
|
*
|
||||||
* on entry, *num is the number of contiguous clusters we'd like to
|
* on entry, *num is the number of contiguous clusters we'd like to
|
||||||
* access following offset.
|
* access following offset.
|
||||||
*
|
*
|
||||||
* on exit, *num is the number of contiguous clusters we can read.
|
* on exit, *num is the number of contiguous clusters we can read.
|
||||||
*
|
*
|
||||||
* Return 1, if the offset is found
|
* Return 0, if the offset is found
|
||||||
* Return 0, otherwise.
|
* Return -errno, otherwise.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int *num)
|
int *num, uint64_t *cluster_offset)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
unsigned int l1_index, l2_index;
|
unsigned int l1_index, l2_index;
|
||||||
uint64_t l2_offset, *l2_table, cluster_offset;
|
uint64_t l2_offset, *l2_table;
|
||||||
int l1_bits, c;
|
int l1_bits, c;
|
||||||
unsigned int index_in_cluster, nb_clusters;
|
unsigned int index_in_cluster, nb_clusters;
|
||||||
uint64_t nb_available, nb_needed;
|
uint64_t nb_available, nb_needed;
|
||||||
|
int ret;
|
||||||
|
|
||||||
index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1);
|
index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1);
|
||||||
nb_needed = *num + index_in_cluster;
|
nb_needed = *num + index_in_cluster;
|
||||||
|
@ -451,7 +466,7 @@ uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
nb_needed = nb_available;
|
nb_needed = nb_available;
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster_offset = 0;
|
*cluster_offset = 0;
|
||||||
|
|
||||||
/* seek the the l2 offset in the l1 table */
|
/* seek the the l2 offset in the l1 table */
|
||||||
|
|
||||||
|
@ -469,17 +484,18 @@ uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
/* load the l2 table in memory */
|
/* load the l2 table in memory */
|
||||||
|
|
||||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||||
l2_table = l2_load(bs, l2_offset);
|
ret = l2_load(bs, l2_offset, &l2_table);
|
||||||
if (l2_table == NULL)
|
if (ret < 0) {
|
||||||
return 0;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* find the cluster offset for the given disk offset */
|
/* find the cluster offset for the given disk offset */
|
||||||
|
|
||||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||||
cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||||
nb_clusters = size_to_clusters(s, nb_needed << 9);
|
nb_clusters = size_to_clusters(s, nb_needed << 9);
|
||||||
|
|
||||||
if (!cluster_offset) {
|
if (!*cluster_offset) {
|
||||||
/* how many empty clusters ? */
|
/* how many empty clusters ? */
|
||||||
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
|
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -495,7 +511,8 @@ out:
|
||||||
|
|
||||||
*num = nb_available - index_in_cluster;
|
*num = nb_available - index_in_cluster;
|
||||||
|
|
||||||
return cluster_offset & ~QCOW_OFLAG_COPIED;
|
*cluster_offset &=~QCOW_OFLAG_COPIED;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -536,9 +553,9 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||||
if (l2_offset & QCOW_OFLAG_COPIED) {
|
if (l2_offset & QCOW_OFLAG_COPIED) {
|
||||||
/* load the l2 table in memory */
|
/* load the l2 table in memory */
|
||||||
l2_offset &= ~QCOW_OFLAG_COPIED;
|
l2_offset &= ~QCOW_OFLAG_COPIED;
|
||||||
l2_table = l2_load(bs, l2_offset);
|
ret = l2_load(bs, l2_offset, &l2_table);
|
||||||
if (l2_table == NULL) {
|
if (ret < 0) {
|
||||||
return -EIO;
|
return ret;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (l2_offset)
|
if (l2_offset)
|
||||||
|
@ -696,6 +713,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||||
|
|
||||||
ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters);
|
ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
qcow2_l2_cache_reset(bs);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,8 +221,6 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
|
||||||
|
|
||||||
/* Allocate the refcount block itself and mark it as used */
|
/* Allocate the refcount block itself and mark it as used */
|
||||||
uint64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
|
uint64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
|
||||||
memset(s->refcount_block_cache, 0, s->cluster_size);
|
|
||||||
s->refcount_block_cache_offset = new_block;
|
|
||||||
|
|
||||||
#ifdef DEBUG_ALLOC2
|
#ifdef DEBUG_ALLOC2
|
||||||
fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64
|
fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64
|
||||||
|
@ -231,6 +229,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) {
|
if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) {
|
||||||
|
/* Zero the new refcount block before updating it */
|
||||||
|
memset(s->refcount_block_cache, 0, s->cluster_size);
|
||||||
|
s->refcount_block_cache_offset = new_block;
|
||||||
|
|
||||||
/* The block describes itself, need to update the cache */
|
/* The block describes itself, need to update the cache */
|
||||||
int block_index = (new_block >> s->cluster_bits) &
|
int block_index = (new_block >> s->cluster_bits) &
|
||||||
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
|
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
|
||||||
|
@ -242,6 +244,11 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail_block;
|
goto fail_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Initialize the new refcount block only after updating its refcount,
|
||||||
|
* update_refcount uses the refcount cache itself */
|
||||||
|
memset(s->refcount_block_cache, 0, s->cluster_size);
|
||||||
|
s->refcount_block_cache_offset = new_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now the new refcount block needs to be written to disk */
|
/* Now the new refcount block needs to be written to disk */
|
||||||
|
@ -404,22 +411,28 @@ static int write_refcount_block_entries(BlockDriverState *bs,
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (cache_refcount_updates) {
|
if (cache_refcount_updates) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (first_index < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
|
first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
|
||||||
last_index = (last_index + REFCOUNTS_PER_SECTOR)
|
last_index = (last_index + REFCOUNTS_PER_SECTOR)
|
||||||
& ~(REFCOUNTS_PER_SECTOR - 1);
|
& ~(REFCOUNTS_PER_SECTOR - 1);
|
||||||
|
|
||||||
size = (last_index - first_index) << REFCOUNT_SHIFT;
|
size = (last_index - first_index) << REFCOUNT_SHIFT;
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
|
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
|
||||||
if (bdrv_pwrite(bs->file,
|
ret = bdrv_pwrite(bs->file,
|
||||||
refcount_block_offset + (first_index << REFCOUNT_SHIFT),
|
refcount_block_offset + (first_index << REFCOUNT_SHIFT),
|
||||||
&s->refcount_block_cache[first_index], size) != size)
|
&s->refcount_block_cache[first_index], size);
|
||||||
{
|
if (ret < 0) {
|
||||||
return -EIO;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -460,10 +473,10 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||||
table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
|
table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
|
||||||
if ((old_table_index >= 0) && (table_index != old_table_index)) {
|
if ((old_table_index >= 0) && (table_index != old_table_index)) {
|
||||||
|
|
||||||
if (write_refcount_block_entries(bs, refcount_block_offset,
|
ret = write_refcount_block_entries(bs, refcount_block_offset,
|
||||||
first_index, last_index) < 0)
|
first_index, last_index);
|
||||||
{
|
if (ret < 0) {
|
||||||
return -EIO;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
first_index = -1;
|
first_index = -1;
|
||||||
|
@ -505,10 +518,11 @@ fail:
|
||||||
|
|
||||||
/* Write last changed block to disk */
|
/* Write last changed block to disk */
|
||||||
if (refcount_block_offset != 0) {
|
if (refcount_block_offset != 0) {
|
||||||
if (write_refcount_block_entries(bs, refcount_block_offset,
|
int wret;
|
||||||
first_index, last_index) < 0)
|
wret = write_refcount_block_entries(bs, refcount_block_offset,
|
||||||
{
|
first_index, last_index);
|
||||||
return ret < 0 ? ret : -EIO;
|
if (wret < 0) {
|
||||||
|
return ret < 0 ? ret : wret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -297,9 +297,15 @@ static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||||
int nb_sectors, int *pnum)
|
int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
|
int ret;
|
||||||
|
|
||||||
*pnum = nb_sectors;
|
*pnum = nb_sectors;
|
||||||
cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, pnum);
|
/* FIXME We can get errors here, but the bdrv_is_allocated interface can't
|
||||||
|
* pass them on today */
|
||||||
|
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);
|
||||||
|
if (ret < 0) {
|
||||||
|
*pnum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return (cluster_offset != 0);
|
return (cluster_offset != 0);
|
||||||
}
|
}
|
||||||
|
@ -409,8 +415,12 @@ static void qcow_aio_read_cb(void *opaque, int ret)
|
||||||
|
|
||||||
/* prepare next AIO request */
|
/* prepare next AIO request */
|
||||||
acb->cur_nr_sectors = acb->remaining_sectors;
|
acb->cur_nr_sectors = acb->remaining_sectors;
|
||||||
acb->cluster_offset = qcow2_get_cluster_offset(bs, acb->sector_num << 9,
|
ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9,
|
||||||
&acb->cur_nr_sectors);
|
&acb->cur_nr_sectors, &acb->cluster_offset);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
|
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
|
||||||
|
|
||||||
if (!acb->cluster_offset) {
|
if (!acb->cluster_offset) {
|
||||||
|
|
|
@ -196,8 +196,8 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||||
int nb_sectors, int enc,
|
int nb_sectors, int enc,
|
||||||
const AES_KEY *key);
|
const AES_KEY *key);
|
||||||
|
|
||||||
uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int *num);
|
int *num, uint64_t *cluster_offset);
|
||||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int n_start, int n_end, int *num, QCowL2Meta *m);
|
int n_start, int n_end, int *num, QCowL2Meta *m);
|
||||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||||
|
|
49
qemu-img.c
49
qemu-img.c
|
@ -252,8 +252,8 @@ static int img_create(int argc, char **argv)
|
||||||
const char *base_fmt = NULL;
|
const char *base_fmt = NULL;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
const char *base_filename = NULL;
|
const char *base_filename = NULL;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv, *proto_drv;
|
||||||
QEMUOptionParameter *param = NULL;
|
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||||
char *options = NULL;
|
char *options = NULL;
|
||||||
|
|
||||||
flags = 0;
|
flags = 0;
|
||||||
|
@ -286,33 +286,42 @@ static int img_create(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the filename */
|
||||||
|
if (optind >= argc)
|
||||||
|
help();
|
||||||
|
filename = argv[optind++];
|
||||||
|
|
||||||
/* Find driver and parse its options */
|
/* Find driver and parse its options */
|
||||||
drv = bdrv_find_format(fmt);
|
drv = bdrv_find_format(fmt);
|
||||||
if (!drv)
|
if (!drv)
|
||||||
error("Unknown file format '%s'", fmt);
|
error("Unknown file format '%s'", fmt);
|
||||||
|
|
||||||
|
proto_drv = bdrv_find_protocol(filename);
|
||||||
|
if (!proto_drv)
|
||||||
|
error("Unknown protocol '%s'", filename);
|
||||||
|
|
||||||
|
create_options = append_option_parameters(create_options,
|
||||||
|
drv->create_options);
|
||||||
|
create_options = append_option_parameters(create_options,
|
||||||
|
proto_drv->create_options);
|
||||||
|
|
||||||
if (options && !strcmp(options, "?")) {
|
if (options && !strcmp(options, "?")) {
|
||||||
print_option_help(drv->create_options);
|
print_option_help(create_options);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create parameter list with default values */
|
/* Create parameter list with default values */
|
||||||
param = parse_option_parameters("", drv->create_options, param);
|
param = parse_option_parameters("", create_options, param);
|
||||||
set_option_parameter_int(param, BLOCK_OPT_SIZE, -1);
|
set_option_parameter_int(param, BLOCK_OPT_SIZE, -1);
|
||||||
|
|
||||||
/* Parse -o options */
|
/* Parse -o options */
|
||||||
if (options) {
|
if (options) {
|
||||||
param = parse_option_parameters(options, drv->create_options, param);
|
param = parse_option_parameters(options, create_options, param);
|
||||||
if (param == NULL) {
|
if (param == NULL) {
|
||||||
error("Invalid options for file format '%s'.", fmt);
|
error("Invalid options for file format '%s'.", fmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the filename */
|
|
||||||
if (optind >= argc)
|
|
||||||
help();
|
|
||||||
filename = argv[optind++];
|
|
||||||
|
|
||||||
/* Add size to parameters */
|
/* Add size to parameters */
|
||||||
if (optind < argc) {
|
if (optind < argc) {
|
||||||
set_option_parameter(param, BLOCK_OPT_SIZE, argv[optind++]);
|
set_option_parameter(param, BLOCK_OPT_SIZE, argv[optind++]);
|
||||||
|
@ -362,6 +371,7 @@ static int img_create(int argc, char **argv)
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
ret = bdrv_create(drv, filename, param);
|
ret = bdrv_create(drv, filename, param);
|
||||||
|
free_option_parameters(create_options);
|
||||||
free_option_parameters(param);
|
free_option_parameters(param);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -543,14 +553,14 @@ static int img_convert(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c, ret, n, n1, bs_n, bs_i, flags, cluster_size, cluster_sectors;
|
int c, ret, n, n1, bs_n, bs_i, flags, cluster_size, cluster_sectors;
|
||||||
const char *fmt, *out_fmt, *out_baseimg, *out_filename;
|
const char *fmt, *out_fmt, *out_baseimg, *out_filename;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv, *proto_drv;
|
||||||
BlockDriverState **bs, *out_bs;
|
BlockDriverState **bs, *out_bs;
|
||||||
int64_t total_sectors, nb_sectors, sector_num, bs_offset;
|
int64_t total_sectors, nb_sectors, sector_num, bs_offset;
|
||||||
uint64_t bs_sectors;
|
uint64_t bs_sectors;
|
||||||
uint8_t * buf;
|
uint8_t * buf;
|
||||||
const uint8_t *buf1;
|
const uint8_t *buf1;
|
||||||
BlockDriverInfo bdi;
|
BlockDriverInfo bdi;
|
||||||
QEMUOptionParameter *param = NULL;
|
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||||
char *options = NULL;
|
char *options = NULL;
|
||||||
|
|
||||||
fmt = NULL;
|
fmt = NULL;
|
||||||
|
@ -615,19 +625,27 @@ static int img_convert(int argc, char **argv)
|
||||||
if (!drv)
|
if (!drv)
|
||||||
error("Unknown file format '%s'", out_fmt);
|
error("Unknown file format '%s'", out_fmt);
|
||||||
|
|
||||||
|
proto_drv = bdrv_find_protocol(out_filename);
|
||||||
|
if (!proto_drv)
|
||||||
|
error("Unknown protocol '%s'", out_filename);
|
||||||
|
|
||||||
|
create_options = append_option_parameters(create_options,
|
||||||
|
drv->create_options);
|
||||||
|
create_options = append_option_parameters(create_options,
|
||||||
|
proto_drv->create_options);
|
||||||
if (options && !strcmp(options, "?")) {
|
if (options && !strcmp(options, "?")) {
|
||||||
print_option_help(drv->create_options);
|
print_option_help(create_options);
|
||||||
free(bs);
|
free(bs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
param = parse_option_parameters(options, drv->create_options, param);
|
param = parse_option_parameters(options, create_options, param);
|
||||||
if (param == NULL) {
|
if (param == NULL) {
|
||||||
error("Invalid options for file format '%s'.", out_fmt);
|
error("Invalid options for file format '%s'.", out_fmt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
param = parse_option_parameters("", drv->create_options, param);
|
param = parse_option_parameters("", create_options, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_option_parameter_int(param, BLOCK_OPT_SIZE, total_sectors * 512);
|
set_option_parameter_int(param, BLOCK_OPT_SIZE, total_sectors * 512);
|
||||||
|
@ -649,6 +667,7 @@ static int img_convert(int argc, char **argv)
|
||||||
|
|
||||||
/* Create the new image */
|
/* Create the new image */
|
||||||
ret = bdrv_create(drv, out_filename, param);
|
ret = bdrv_create(drv, out_filename, param);
|
||||||
|
free_option_parameters(create_options);
|
||||||
free_option_parameters(param);
|
free_option_parameters(param);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
192
qemu-io.c
192
qemu-io.c
|
@ -267,6 +267,47 @@ static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total)
|
||||||
return async_ret < 0 ? async_ret : 1;
|
return async_ret < 0 ? async_ret : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct multiwrite_async_ret {
|
||||||
|
int num_done;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void multiwrite_cb(void *opaque, int ret)
|
||||||
|
{
|
||||||
|
struct multiwrite_async_ret *async_ret = opaque;
|
||||||
|
|
||||||
|
async_ret->num_done++;
|
||||||
|
if (ret < 0) {
|
||||||
|
async_ret->error = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
struct multiwrite_async_ret async_ret = {
|
||||||
|
.num_done = 0,
|
||||||
|
.error = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
*total = 0;
|
||||||
|
for (i = 0; i < num_reqs; i++) {
|
||||||
|
reqs[i].cb = multiwrite_cb;
|
||||||
|
reqs[i].opaque = &async_ret;
|
||||||
|
*total += reqs[i].qiov->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_aio_multiwrite(bs, reqs, num_reqs);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (async_ret.num_done < num_reqs) {
|
||||||
|
qemu_aio_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
return async_ret.error < 0 ? async_ret.error : 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
read_help(void)
|
read_help(void)
|
||||||
|
@ -811,6 +852,156 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
multiwrite_help(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"\n"
|
||||||
|
" writes a range of bytes from the given offset source from multiple buffers,\n"
|
||||||
|
" in a batch of requests that may be merged by qemu\n"
|
||||||
|
"\n"
|
||||||
|
" Example:\n"
|
||||||
|
" 'multiwrite 512 1k 1k ; 4k 1k' \n"
|
||||||
|
" writes 2 kB at 512 bytes and 1 kB at 4 kB into the open file\n"
|
||||||
|
"\n"
|
||||||
|
" Writes into a segment of the currently open file, using a buffer\n"
|
||||||
|
" filled with a set pattern (0xcdcdcdcd). The pattern byte is increased\n"
|
||||||
|
" by one for each request contained in the multiwrite command.\n"
|
||||||
|
" -P, -- use different pattern to fill file\n"
|
||||||
|
" -C, -- report statistics in a machine parsable format\n"
|
||||||
|
" -q, -- quiet mode, do not show I/O statistics\n"
|
||||||
|
"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int multiwrite_f(int argc, char **argv);
|
||||||
|
|
||||||
|
static const cmdinfo_t multiwrite_cmd = {
|
||||||
|
.name = "multiwrite",
|
||||||
|
.cfunc = multiwrite_f,
|
||||||
|
.argmin = 2,
|
||||||
|
.argmax = -1,
|
||||||
|
.args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]",
|
||||||
|
.oneline = "issues multiple write requests at once",
|
||||||
|
.help = multiwrite_help,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
multiwrite_f(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct timeval t1, t2;
|
||||||
|
int Cflag = 0, qflag = 0;
|
||||||
|
int c, cnt;
|
||||||
|
char **buf;
|
||||||
|
int64_t offset, first_offset = 0;
|
||||||
|
/* Some compilers get confused and warn if this is not initialized. */
|
||||||
|
int total = 0;
|
||||||
|
int nr_iov;
|
||||||
|
int nr_reqs;
|
||||||
|
int pattern = 0xcd;
|
||||||
|
QEMUIOVector *qiovs;
|
||||||
|
int i;
|
||||||
|
BlockRequest *reqs;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "CqP:")) != EOF) {
|
||||||
|
switch (c) {
|
||||||
|
case 'C':
|
||||||
|
Cflag = 1;
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
qflag = 1;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
pattern = parse_pattern(optarg);
|
||||||
|
if (pattern < 0)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return command_usage(&writev_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind > argc - 2)
|
||||||
|
return command_usage(&writev_cmd);
|
||||||
|
|
||||||
|
nr_reqs = 1;
|
||||||
|
for (i = optind; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], ";")) {
|
||||||
|
nr_reqs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reqs = qemu_malloc(nr_reqs * sizeof(*reqs));
|
||||||
|
buf = qemu_malloc(nr_reqs * sizeof(*buf));
|
||||||
|
qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs));
|
||||||
|
|
||||||
|
for (i = 0; i < nr_reqs; i++) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
/* Read the offset of the request */
|
||||||
|
offset = cvtnum(argv[optind]);
|
||||||
|
if (offset < 0) {
|
||||||
|
printf("non-numeric offset argument -- %s\n", argv[optind]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
optind++;
|
||||||
|
|
||||||
|
if (offset & 0x1ff) {
|
||||||
|
printf("offset %lld is not sector aligned\n",
|
||||||
|
(long long)offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
first_offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read lengths for qiov entries */
|
||||||
|
for (j = optind; j < argc; j++) {
|
||||||
|
if (!strcmp(argv[j], ";")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nr_iov = j - optind;
|
||||||
|
|
||||||
|
/* Build request */
|
||||||
|
reqs[i].qiov = &qiovs[i];
|
||||||
|
buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern);
|
||||||
|
reqs[i].sector = offset >> 9;
|
||||||
|
reqs[i].nb_sectors = reqs[i].qiov->size >> 9;
|
||||||
|
|
||||||
|
optind = j + 1;
|
||||||
|
|
||||||
|
offset += reqs[i].qiov->size;
|
||||||
|
pattern++;
|
||||||
|
}
|
||||||
|
|
||||||
|
gettimeofday(&t1, NULL);
|
||||||
|
cnt = do_aio_multiwrite(reqs, nr_reqs, &total);
|
||||||
|
gettimeofday(&t2, NULL);
|
||||||
|
|
||||||
|
if (cnt < 0) {
|
||||||
|
printf("aio_multiwrite failed: %s\n", strerror(-cnt));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qflag)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Finally, report back -- -C gives a parsable format */
|
||||||
|
t2 = tsub(t2, t1);
|
||||||
|
print_report("wrote", &t2, first_offset, total, total, cnt, Cflag);
|
||||||
|
out:
|
||||||
|
for (i = 0; i < nr_reqs; i++) {
|
||||||
|
qemu_io_free(buf[i]);
|
||||||
|
qemu_iovec_destroy(&qiovs[i]);
|
||||||
|
}
|
||||||
|
qemu_free(buf);
|
||||||
|
qemu_free(reqs);
|
||||||
|
qemu_free(qiovs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct aio_ctx {
|
struct aio_ctx {
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov;
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
|
@ -1483,6 +1674,7 @@ int main(int argc, char **argv)
|
||||||
add_command(&readv_cmd);
|
add_command(&readv_cmd);
|
||||||
add_command(&write_cmd);
|
add_command(&write_cmd);
|
||||||
add_command(&writev_cmd);
|
add_command(&writev_cmd);
|
||||||
|
add_command(&multiwrite_cmd);
|
||||||
add_command(&aio_read_cmd);
|
add_command(&aio_read_cmd);
|
||||||
add_command(&aio_write_cmd);
|
add_command(&aio_write_cmd);
|
||||||
add_command(&aio_flush_cmd);
|
add_command(&aio_flush_cmd);
|
||||||
|
|
|
@ -345,6 +345,51 @@ void free_option_parameters(QEMUOptionParameter *list)
|
||||||
qemu_free(list);
|
qemu_free(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Count valid options in list
|
||||||
|
*/
|
||||||
|
static size_t count_option_parameters(QEMUOptionParameter *list)
|
||||||
|
{
|
||||||
|
size_t num_options = 0;
|
||||||
|
|
||||||
|
while (list && list->name) {
|
||||||
|
num_options++;
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append an option list (list) to an option list (dest).
|
||||||
|
*
|
||||||
|
* If dest is NULL, a new copy of list is created.
|
||||||
|
*
|
||||||
|
* Returns a pointer to the first element of dest (or the newly allocated copy)
|
||||||
|
*/
|
||||||
|
QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest,
|
||||||
|
QEMUOptionParameter *list)
|
||||||
|
{
|
||||||
|
size_t num_options, num_dest_options;
|
||||||
|
|
||||||
|
num_options = count_option_parameters(dest);
|
||||||
|
num_dest_options = num_options;
|
||||||
|
|
||||||
|
num_options += count_option_parameters(list);
|
||||||
|
|
||||||
|
dest = qemu_realloc(dest, (num_options + 1) * sizeof(QEMUOptionParameter));
|
||||||
|
|
||||||
|
while (list && list->name) {
|
||||||
|
if (get_option_parameter(dest, list->name) == NULL) {
|
||||||
|
dest[num_dest_options++] = *list;
|
||||||
|
dest[num_dest_options].name = NULL;
|
||||||
|
}
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a parameter string (param) into an option list (dest).
|
* Parses a parameter string (param) into an option list (dest).
|
||||||
*
|
*
|
||||||
|
@ -365,7 +410,6 @@ void free_option_parameters(QEMUOptionParameter *list)
|
||||||
QEMUOptionParameter *parse_option_parameters(const char *param,
|
QEMUOptionParameter *parse_option_parameters(const char *param,
|
||||||
QEMUOptionParameter *list, QEMUOptionParameter *dest)
|
QEMUOptionParameter *list, QEMUOptionParameter *dest)
|
||||||
{
|
{
|
||||||
QEMUOptionParameter *cur;
|
|
||||||
QEMUOptionParameter *allocated = NULL;
|
QEMUOptionParameter *allocated = NULL;
|
||||||
char name[256];
|
char name[256];
|
||||||
char value[256];
|
char value[256];
|
||||||
|
@ -379,12 +423,7 @@ QEMUOptionParameter *parse_option_parameters(const char *param,
|
||||||
|
|
||||||
if (dest == NULL) {
|
if (dest == NULL) {
|
||||||
// Count valid options
|
// Count valid options
|
||||||
num_options = 0;
|
num_options = count_option_parameters(list);
|
||||||
cur = list;
|
|
||||||
while (cur->name) {
|
|
||||||
num_options++;
|
|
||||||
cur++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy of the option list to fill in values
|
// Create a copy of the option list to fill in values
|
||||||
dest = qemu_mallocz((num_options + 1) * sizeof(QEMUOptionParameter));
|
dest = qemu_mallocz((num_options + 1) * sizeof(QEMUOptionParameter));
|
||||||
|
|
|
@ -70,6 +70,8 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
|
||||||
const char *value);
|
const char *value);
|
||||||
int set_option_parameter_int(QEMUOptionParameter *list, const char *name,
|
int set_option_parameter_int(QEMUOptionParameter *list, const char *name,
|
||||||
uint64_t value);
|
uint64_t value);
|
||||||
|
QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest,
|
||||||
|
QEMUOptionParameter *list);
|
||||||
QEMUOptionParameter *parse_option_parameters(const char *param,
|
QEMUOptionParameter *parse_option_parameters(const char *param,
|
||||||
QEMUOptionParameter *list, QEMUOptionParameter *dest);
|
QEMUOptionParameter *list, QEMUOptionParameter *dest);
|
||||||
void free_option_parameters(QEMUOptionParameter *list);
|
void free_option_parameters(QEMUOptionParameter *list);
|
||||||
|
|
6
vl.c
6
vl.c
|
@ -953,7 +953,7 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque,
|
||||||
|
|
||||||
on_write_error = BLOCK_ERR_STOP_ENOSPC;
|
on_write_error = BLOCK_ERR_STOP_ENOSPC;
|
||||||
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
||||||
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO) {
|
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
|
||||||
fprintf(stderr, "werror is no supported by this format\n");
|
fprintf(stderr, "werror is no supported by this format\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -966,7 +966,7 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque,
|
||||||
|
|
||||||
on_read_error = BLOCK_ERR_REPORT;
|
on_read_error = BLOCK_ERR_REPORT;
|
||||||
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
||||||
if (type != IF_IDE && type != IF_VIRTIO) {
|
if (type != IF_IDE && type != IF_VIRTIO && type != IF_NONE) {
|
||||||
fprintf(stderr, "rerror is no supported by this format\n");
|
fprintf(stderr, "rerror is no supported by this format\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1114,7 +1114,7 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque,
|
||||||
/* CDROM is fine for any interface, don't check. */
|
/* CDROM is fine for any interface, don't check. */
|
||||||
ro = 1;
|
ro = 1;
|
||||||
} else if (ro == 1) {
|
} else if (ro == 1) {
|
||||||
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY) {
|
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
|
||||||
fprintf(stderr, "qemu: readonly flag not supported for drive with this interface\n");
|
fprintf(stderr, "qemu: readonly flag not supported for drive with this interface\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue