Merge remote branch 'kwolf/for-anthony' into staging

This commit is contained in:
Anthony Liguori 2010-06-03 14:55:49 -05:00
commit 358c360feb
12 changed files with 412 additions and 116 deletions

13
block.c
View File

@ -56,7 +56,6 @@ static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors);
static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
static BlockDriver *find_protocol(const char *filename);
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
QTAILQ_HEAD_INITIALIZER(bdrv_states);
@ -210,7 +209,7 @@ int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
{
BlockDriver *drv;
drv = find_protocol(filename);
drv = bdrv_find_protocol(filename);
if (drv == NULL) {
drv = bdrv_find_format("file");
}
@ -283,7 +282,7 @@ static BlockDriver *find_hdev_driver(const char *filename)
return drv;
}
static BlockDriver *find_protocol(const char *filename)
BlockDriver *bdrv_find_protocol(const char *filename)
{
BlockDriver *drv1;
char protocol[128];
@ -333,8 +332,10 @@ static BlockDriver *find_image_format(const char *filename)
return NULL;
/* Return the raw BlockDriver * to scsi-generic devices */
if (bs->sg)
if (bs->sg) {
bdrv_delete(bs);
return bdrv_find_format("raw");
}
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
bdrv_delete(bs);
@ -478,7 +479,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
BlockDriver *drv;
int ret;
drv = find_protocol(filename);
drv = bdrv_find_protocol(filename);
if (!drv) {
return -ENOENT;
}
@ -1950,7 +1951,7 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
// Add the second request
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;
mcb->callbacks[i].free_qiov = reqs[outidx].qiov;

View File

@ -38,7 +38,7 @@ typedef struct QEMUSnapshotInfo {
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
#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)
typedef enum {
@ -54,6 +54,7 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data);
void bdrv_init(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_whitelisted_format(const char *format_name);
int bdrv_create(BlockDriver *drv, const char* filename,

View File

@ -157,31 +157,36 @@ static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset)
* 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;
int min_index;
uint64_t *l2_table;
int ret;
/* seek if the table for the given offset is in the cache */
l2_table = seek_l2_table(s, l2_offset);
if (l2_table != NULL)
return l2_table;
*l2_table = seek_l2_table(s, l2_offset);
if (*l2_table != NULL) {
return 0;
}
/* not found: load a new entry in the least used one */
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);
if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
s->l2_size * sizeof(uint64_t))
return NULL;
ret = bdrv_pread(bs->file, l2_offset, *l2_table,
s->l2_size * sizeof(uint64_t));
if (ret < 0) {
return ret;
}
s->l2_cache_offsets[min_index] = l2_offset;
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;
}
/* 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 */
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,
s->l2_size * sizeof(uint64_t));
if (ret < 0) {
return ret;
goto fail;
}
}
/* 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,
s->l2_size * sizeof(uint64_t));
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 */
@ -279,6 +283,10 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
*table = l2_table;
return 0;
fail:
qcow2_l2_cache_reset(bs);
return ret;
}
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) {
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);
if (!cluster_offset) {
if (bs->backing_hd) {
@ -409,28 +423,29 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
/*
* get_cluster_offset
*
* For a given offset of the disk image, return cluster offset in
* qcow2 file.
* For a given offset of the disk image, find the cluster offset in
* qcow2 file. The offset is stored in *cluster_offset.
*
* on entry, *num is the number of contiguous clusters we'd like to
* access following offset.
*
* on exit, *num is the number of contiguous clusters we can read.
*
* Return 1, if the offset is found
* Return 0, otherwise.
* Return 0, if the offset is found
* Return -errno, otherwise.
*
*/
uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num)
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *cluster_offset)
{
BDRVQcowState *s = bs->opaque;
unsigned int l1_index, l2_index;
uint64_t l2_offset, *l2_table, cluster_offset;
uint64_t l2_offset, *l2_table;
int l1_bits, c;
unsigned int index_in_cluster, nb_clusters;
uint64_t nb_available, nb_needed;
int ret;
index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1);
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;
}
cluster_offset = 0;
*cluster_offset = 0;
/* 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 */
l2_offset &= ~QCOW_OFLAG_COPIED;
l2_table = l2_load(bs, l2_offset);
if (l2_table == NULL)
return 0;
ret = l2_load(bs, l2_offset, &l2_table);
if (ret < 0) {
return ret;
}
/* find the cluster offset for the given disk offset */
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);
if (!cluster_offset) {
if (!*cluster_offset) {
/* how many empty clusters ? */
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
} else {
@ -495,7 +511,8 @@ out:
*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) {
/* load the l2 table in memory */
l2_offset &= ~QCOW_OFLAG_COPIED;
l2_table = l2_load(bs, l2_offset);
if (l2_table == NULL) {
return -EIO;
ret = l2_load(bs, l2_offset, &l2_table);
if (ret < 0) {
return ret;
}
} else {
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);
if (ret < 0) {
qcow2_l2_cache_reset(bs);
goto err;
}

View File

@ -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 */
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
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
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 */
int block_index = (new_block >> s->cluster_bits) &
((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) {
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 */
@ -404,22 +411,28 @@ static int write_refcount_block_entries(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
size_t size;
int ret;
if (cache_refcount_updates) {
return 0;
}
if (first_index < 0) {
return 0;
}
first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
last_index = (last_index + REFCOUNTS_PER_SECTOR)
& ~(REFCOUNTS_PER_SECTOR - 1);
size = (last_index - first_index) << REFCOUNT_SHIFT;
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),
&s->refcount_block_cache[first_index], size) != size)
{
return -EIO;
&s->refcount_block_cache[first_index], size);
if (ret < 0) {
return ret;
}
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);
if ((old_table_index >= 0) && (table_index != old_table_index)) {
if (write_refcount_block_entries(bs, refcount_block_offset,
first_index, last_index) < 0)
{
return -EIO;
ret = write_refcount_block_entries(bs, refcount_block_offset,
first_index, last_index);
if (ret < 0) {
return ret;
}
first_index = -1;
@ -505,10 +518,11 @@ fail:
/* Write last changed block to disk */
if (refcount_block_offset != 0) {
if (write_refcount_block_entries(bs, refcount_block_offset,
first_index, last_index) < 0)
{
return ret < 0 ? ret : -EIO;
int wret;
wret = write_refcount_block_entries(bs, refcount_block_offset,
first_index, last_index);
if (wret < 0) {
return ret < 0 ? ret : wret;
}
}

View File

@ -297,9 +297,15 @@ static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
uint64_t cluster_offset;
int ret;
*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);
}
@ -409,8 +415,12 @@ static void qcow_aio_read_cb(void *opaque, int ret)
/* prepare next AIO request */
acb->cur_nr_sectors = acb->remaining_sectors;
acb->cluster_offset = qcow2_get_cluster_offset(bs, acb->sector_num << 9,
&acb->cur_nr_sectors);
ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9,
&acb->cur_nr_sectors, &acb->cluster_offset);
if (ret < 0) {
goto done;
}
index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
if (!acb->cluster_offset) {

View File

@ -196,8 +196,8 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
int nb_sectors, int enc,
const AES_KEY *key);
uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num);
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *cluster_offset);
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
int n_start, int n_end, int *num, QCowL2Meta *m);
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,

View File

@ -35,7 +35,7 @@ struct qemu_paiocb {
int aio_fildes;
union {
struct iovec *aio_iov;
void *aio_ioctl_buf;
void *aio_ioctl_buf;
};
int aio_niov;
size_t aio_nbytes;
@ -119,21 +119,21 @@ static void thread_create(pthread_t *thread, pthread_attr_t *attr,
static ssize_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb)
{
int ret;
int ret;
ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf);
if (ret == -1)
return -errno;
ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf);
if (ret == -1)
return -errno;
/*
* This looks weird, but the aio code only consideres a request
* successfull if it has written the number full number of bytes.
*
* Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command,
* so in fact we return the ioctl command here to make posix_aio_read()
* happy..
*/
return aiocb->aio_nbytes;
/*
* This looks weird, but the aio code only consideres a request
* successfull if it has written the number full number of bytes.
*
* Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command,
* so in fact we return the ioctl command here to make posix_aio_read()
* happy..
*/
return aiocb->aio_nbytes;
}
static ssize_t handle_aiocb_flush(struct qemu_paiocb *aiocb)
@ -249,10 +249,10 @@ static ssize_t handle_aiocb_rw(struct qemu_paiocb *aiocb)
* Try preadv/pwritev first and fall back to linearizing the
* buffer if it's not supported.
*/
if (preadv_present) {
if (preadv_present) {
nbytes = handle_aiocb_rw_vector(aiocb);
if (nbytes == aiocb->aio_nbytes)
return nbytes;
return nbytes;
if (nbytes < 0 && nbytes != -ENOSYS)
return nbytes;
preadv_present = 0;
@ -335,19 +335,19 @@ static void *aio_thread(void *unused)
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
case QEMU_AIO_READ:
case QEMU_AIO_WRITE:
ret = handle_aiocb_rw(aiocb);
break;
ret = handle_aiocb_rw(aiocb);
break;
case QEMU_AIO_FLUSH:
ret = handle_aiocb_flush(aiocb);
break;
ret = handle_aiocb_flush(aiocb);
break;
case QEMU_AIO_IOCTL:
ret = handle_aiocb_ioctl(aiocb);
break;
default:
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
ret = -EINVAL;
break;
}
ret = handle_aiocb_ioctl(aiocb);
break;
default:
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
ret = -EINVAL;
break;
}
mutex_lock(&lock);
aiocb->ret = ret;

View File

@ -252,8 +252,8 @@ static int img_create(int argc, char **argv)
const char *base_fmt = NULL;
const char *filename;
const char *base_filename = NULL;
BlockDriver *drv;
QEMUOptionParameter *param = NULL;
BlockDriver *drv, *proto_drv;
QEMUOptionParameter *param = NULL, *create_options = NULL;
char *options = NULL;
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 */
drv = bdrv_find_format(fmt);
if (!drv)
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, "?")) {
print_option_help(drv->create_options);
print_option_help(create_options);
return 0;
}
/* 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);
/* Parse -o options */
if (options) {
param = parse_option_parameters(options, drv->create_options, param);
param = parse_option_parameters(options, create_options, param);
if (param == NULL) {
error("Invalid options for file format '%s'.", fmt);
}
}
/* Get the filename */
if (optind >= argc)
help();
filename = argv[optind++];
/* Add size to parameters */
if (optind < argc) {
set_option_parameter(param, BLOCK_OPT_SIZE, argv[optind++]);
@ -362,6 +371,7 @@ static int img_create(int argc, char **argv)
puts("");
ret = bdrv_create(drv, filename, param);
free_option_parameters(create_options);
free_option_parameters(param);
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;
const char *fmt, *out_fmt, *out_baseimg, *out_filename;
BlockDriver *drv;
BlockDriver *drv, *proto_drv;
BlockDriverState **bs, *out_bs;
int64_t total_sectors, nb_sectors, sector_num, bs_offset;
uint64_t bs_sectors;
uint8_t * buf;
const uint8_t *buf1;
BlockDriverInfo bdi;
QEMUOptionParameter *param = NULL;
QEMUOptionParameter *param = NULL, *create_options = NULL;
char *options = NULL;
fmt = NULL;
@ -615,19 +625,27 @@ static int img_convert(int argc, char **argv)
if (!drv)
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, "?")) {
print_option_help(drv->create_options);
print_option_help(create_options);
free(bs);
return 0;
}
if (options) {
param = parse_option_parameters(options, drv->create_options, param);
param = parse_option_parameters(options, create_options, param);
if (param == NULL) {
error("Invalid options for file format '%s'.", out_fmt);
}
} 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);
@ -649,6 +667,7 @@ static int img_convert(int argc, char **argv)
/* Create the new image */
ret = bdrv_create(drv, out_filename, param);
free_option_parameters(create_options);
free_option_parameters(param);
if (ret < 0) {

192
qemu-io.c
View File

@ -267,6 +267,47 @@ static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total)
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
read_help(void)
@ -811,6 +852,156 @@ out:
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 {
QEMUIOVector qiov;
int64_t offset;
@ -1483,6 +1674,7 @@ int main(int argc, char **argv)
add_command(&readv_cmd);
add_command(&write_cmd);
add_command(&writev_cmd);
add_command(&multiwrite_cmd);
add_command(&aio_read_cmd);
add_command(&aio_write_cmd);
add_command(&aio_flush_cmd);

View File

@ -345,6 +345,51 @@ void free_option_parameters(QEMUOptionParameter *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).
*
@ -365,7 +410,6 @@ void free_option_parameters(QEMUOptionParameter *list)
QEMUOptionParameter *parse_option_parameters(const char *param,
QEMUOptionParameter *list, QEMUOptionParameter *dest)
{
QEMUOptionParameter *cur;
QEMUOptionParameter *allocated = NULL;
char name[256];
char value[256];
@ -379,12 +423,7 @@ QEMUOptionParameter *parse_option_parameters(const char *param,
if (dest == NULL) {
// Count valid options
num_options = 0;
cur = list;
while (cur->name) {
num_options++;
cur++;
}
num_options = count_option_parameters(list);
// Create a copy of the option list to fill in values
dest = qemu_mallocz((num_options + 1) * sizeof(QEMUOptionParameter));

View File

@ -70,6 +70,8 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
const char *value);
int set_option_parameter_int(QEMUOptionParameter *list, const char *name,
uint64_t value);
QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest,
QEMUOptionParameter *list);
QEMUOptionParameter *parse_option_parameters(const char *param,
QEMUOptionParameter *list, QEMUOptionParameter *dest);
void free_option_parameters(QEMUOptionParameter *list);

6
vl.c
View File

@ -953,7 +953,7 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque,
on_write_error = BLOCK_ERR_STOP_ENOSPC;
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");
return NULL;
}
@ -966,7 +966,7 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque,
on_read_error = BLOCK_ERR_REPORT;
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");
return NULL;
}
@ -1114,7 +1114,7 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque,
/* CDROM is fine for any interface, don't check. */
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");
return NULL;
}