aboutsummaryrefslogtreecommitdiff
path: root/block/qcow2.c
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2009-08-31 16:48:49 +0200
committerAnthony Liguori <aliguori@us.ibm.com>2009-09-09 17:31:26 -0500
commitf214978a427ea40b0b86af10548fe0270f6d0db0 (patch)
treefbb324e8752868bf8f0ba6ba15c01bbb9be47cda /block/qcow2.c
parentea80b906f445969a5ccc781beb4e8bb7f60bbdbb (diff)
downloadqemu-f214978a427ea40b0b86af10548fe0270f6d0db0.zip
qemu-f214978a427ea40b0b86af10548fe0270f6d0db0.tar.gz
qemu-f214978a427ea40b0b86af10548fe0270f6d0db0.tar.bz2
qcow2: Order concurrent AIO requests on the same unallocated cluster
When two AIO requests write to the same cluster, and this cluster is unallocated, currently both requests allocate a new cluster and the second one merges the first one when it is completed. This means an cluster allocation, a read and a cluster deallocation which cause some overhead. If we simply let the second request wait until the first one is done, we improve overall performance with AIO requests (specifially, qcow2/virtio combinations). This patch maintains a list of in-flight requests that have allocated new clusters. A second request touching the same cluster is limited so that it either doesn't touch the allocation of the first request (so it can have a non-overlapping allocation) or it waits for the first request to complete. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'block/qcow2.c')
-rw-r--r--block/qcow2.c55
1 files changed, 50 insertions, 5 deletions
diff --git a/block/qcow2.c b/block/qcow2.c
index b8eae90..8579e01 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -219,6 +219,8 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
if (qcow2_refcount_init(bs) < 0)
goto fail;
+ LIST_INIT(&s->cluster_allocs);
+
/* read qcow2 extensions */
if (header.backing_file_offset)
ext_end = header.backing_file_offset;
@@ -338,6 +340,7 @@ typedef struct QCowAIOCB {
QEMUIOVector hd_qiov;
QEMUBH *bh;
QCowL2Meta l2meta;
+ LIST_ENTRY(QCowAIOCB) next_depend;
} QCowAIOCB;
static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
@@ -500,6 +503,7 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
acb->n = 0;
acb->cluster_offset = 0;
acb->l2meta.nb_clusters = 0;
+ LIST_INIT(&acb->l2meta.dependent_requests);
return acb;
}
@@ -517,6 +521,33 @@ static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs,
return &acb->common;
}
+static void qcow_aio_write_cb(void *opaque, int ret);
+
+static void run_dependent_requests(QCowL2Meta *m)
+{
+ QCowAIOCB *req;
+ QCowAIOCB *next;
+
+ /* Take the request off the list of running requests */
+ if (m->nb_clusters != 0) {
+ LIST_REMOVE(m, next_in_flight);
+ }
+
+ /*
+ * Restart all dependent requests.
+ * Can't use LIST_FOREACH here - the next link might not be the same
+ * any more after the callback (request could depend on a different
+ * request now)
+ */
+ for (req = m->dependent_requests.lh_first; req != NULL; req = next) {
+ next = req->next_depend.le_next;
+ qcow_aio_write_cb(req, 0);
+ }
+
+ /* Empty the list for the next part of the request */
+ LIST_INIT(&m->dependent_requests);
+}
+
static void qcow_aio_write_cb(void *opaque, int ret)
{
QCowAIOCB *acb = opaque;
@@ -528,14 +559,15 @@ static void qcow_aio_write_cb(void *opaque, int ret)
acb->hd_aiocb = NULL;
+ if (ret >= 0) {
+ ret = qcow2_alloc_cluster_link_l2(bs, acb->cluster_offset, &acb->l2meta);
+ }
+
+ run_dependent_requests(&acb->l2meta);
+
if (ret < 0)
goto done;
- if (qcow2_alloc_cluster_link_l2(bs, acb->cluster_offset, &acb->l2meta) < 0) {
- qcow2_free_any_clusters(bs, acb->cluster_offset, acb->l2meta.nb_clusters);
- goto done;
- }
-
acb->nb_sectors -= acb->n;
acb->sector_num += acb->n;
acb->buf += acb->n * 512;
@@ -555,6 +587,14 @@ static void qcow_aio_write_cb(void *opaque, int ret)
acb->cluster_offset = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9,
index_in_cluster,
n_end, &acb->n, &acb->l2meta);
+
+ /* Need to wait for another request? If so, we are done for now. */
+ if (!acb->cluster_offset && acb->l2meta.depends_on != NULL) {
+ LIST_INSERT_HEAD(&acb->l2meta.depends_on->dependent_requests,
+ acb, next_depend);
+ return;
+ }
+
if (!acb->cluster_offset || (acb->cluster_offset & 511) != 0) {
ret = -EIO;
goto done;
@@ -650,6 +690,7 @@ static int preallocate(BlockDriverState *bs)
nb_sectors = bdrv_getlength(bs) >> 9;
offset = 0;
+ LIST_INIT(&meta.dependent_requests);
while (nb_sectors) {
num = MIN(nb_sectors, INT_MAX >> 9);
@@ -665,6 +706,10 @@ static int preallocate(BlockDriverState *bs)
return -1;
}
+ /* There are no dependent requests, but we need to remove our request
+ * from the list of in-flight requests */
+ run_dependent_requests(&meta);
+
/* TODO Preallocate data if requested */
nb_sectors -= num;