int l2_index, ret;
uint64_t l2_offset, *l2_table, cluster_offset;
int nb_clusters, i = 0;
+ QCowL2Meta *old_alloc;
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
if (ret == 0)
}
nb_clusters = i;
+ /*
+ * Check if there already is an AIO write request in flight which allocates
+ * the same cluster. In this case we need to wait until the previous
+ * request has completed and updated the L2 table accordingly.
+ */
+ LIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
+
+ uint64_t end_offset = offset + nb_clusters * s->cluster_size;
+ uint64_t old_offset = old_alloc->offset;
+ uint64_t old_end_offset = old_alloc->offset +
+ old_alloc->nb_clusters * s->cluster_size;
+
+ if (end_offset < old_offset || offset > old_end_offset) {
+ /* No intersection */
+ } else {
+ if (offset < old_offset) {
+ /* Stop at the start of a running allocation */
+ nb_clusters = (old_offset - offset) >> s->cluster_bits;
+ } else {
+ nb_clusters = 0;
+ }
+
+ if (nb_clusters == 0) {
+ /* Set dependency and wait for a callback */
+ m->depends_on = old_alloc;
+ m->nb_clusters = 0;
+ *num = 0;
+ return 0;
+ }
+ }
+ }
+
+ if (!nb_clusters) {
+ abort();
+ }
+
+ LIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
+
/* allocate a new cluster */
cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
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;
QEMUIOVector hd_qiov;
QEMUBH *bh;
QCowL2Meta l2meta;
+ LIST_ENTRY(QCowAIOCB) next_depend;
} QCowAIOCB;
static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
acb->n = 0;
acb->cluster_offset = 0;
acb->l2meta.nb_clusters = 0;
+ LIST_INIT(&acb->l2meta.dependent_requests);
return acb;
}
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;
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;
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;
uint8_t *cluster_cache;
uint8_t *cluster_data;
uint64_t cluster_cache_offset;
+ LIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs;
uint64_t *refcount_table;
uint64_t refcount_table_offset;
int64_t refcount_block_offset;
} QCowCreateState;
+struct QCowAIOCB;
+
/* XXX This could be private for qcow2-cluster.c */
typedef struct QCowL2Meta
{
int n_start;
int nb_available;
int nb_clusters;
+ struct QCowL2Meta *depends_on;
+ LIST_HEAD(QCowAioDependencies, QCowAIOCB) dependent_requests;
+
+ LIST_ENTRY(QCowL2Meta) next_in_flight;
} QCowL2Meta;
static inline int size_to_clusters(BDRVQcowState *s, int64_t size)