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); 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;

View file

@ -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,

View file

@ -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;
} }

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 */ /* 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;
} }
} }

View file

@ -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) {

View file

@ -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,

View file

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

View file

@ -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
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; 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);

View file

@ -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));

View file

@ -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
View file

@ -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;
} }