* the same cluster. In this case we need to wait until the previous
* request has completed and updated the L2 table accordingly.
*
+ * If allow_shortening == true, instead of waiting for a dependency, *cur_bytes
+ * can be shortened so that the cluster allocations don't overlap.
+ *
* Returns:
* 0 if there was no dependency. *cur_bytes indicates the number of
* bytes from guest_offset that can be read before the next
*/
static int coroutine_fn handle_dependencies(BlockDriverState *bs,
uint64_t guest_offset,
- uint64_t *cur_bytes, QCowL2Meta **m)
+ uint64_t *cur_bytes,
+ bool allow_shortening,
+ QCowL2Meta **m)
{
BDRVQcow2State *s = bs->opaque;
QCowL2Meta *old_alloc;
/* Conflict */
- if (start < old_start) {
+ if (start < old_start && allow_shortening) {
/* Stop at the start of a running allocation */
bytes = old_start - start;
} else {
return 0;
}
+static void coroutine_mixed_fn wait_for_dependencies(BlockDriverState *bs,
+ uint64_t guest_offset,
+ uint64_t bytes)
+{
+ BDRVQcow2State *s = bs->opaque;
+ QCowL2Meta *m = NULL;
+ int ret;
+
+ /*
+ * Discard has some non-coroutine callers (creating internal snapshots and
+ * make empty). They are calling from qemu-img or in a drained section, so
+ * we know that no writes can be in progress.
+ */
+ if (!qemu_in_coroutine()) {
+ assert(QLIST_EMPTY(&s->cluster_allocs));
+ return;
+ }
+
+ do {
+ ret = handle_dependencies(bs, guest_offset, &bytes, false, &m);
+ } while (ret == -EAGAIN);
+}
+
/*
* Checks how many already allocated clusters that don't require a new
* allocation there are at the given guest_offset (up to *bytes).
* the right synchronisation between the in-flight request and
* the new one.
*/
- ret = handle_dependencies(bs, start, &cur_bytes, m);
+ ret = handle_dependencies(bs, start, &cur_bytes, true, m);
if (ret == -EAGAIN) {
/* Currently handle_dependencies() doesn't yield if we already had
* an allocation. If it did, we would have to clean up the L2Meta
int64_t cleared;
int ret;
+ /*
+ * If we're touching a cluster for which allocating writes are in flight,
+ * wait for them to complete to avoid conflicting metadata updates.
+ *
+ * We don't need to allocate a QCowL2Meta for the discard operation because
+ * s->lock is held for the duration of the whole operation.
+ */
+ wait_for_dependencies(bs, offset, bytes);
+
/* Caller must pass aligned values, except at image end */
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
int64_t cleared;
int ret;
+ /*
+ * If we're touching a cluster for which allocating writes are in flight,
+ * wait for them to complete to avoid conflicting metadata updates.
+ *
+ * We don't need to allocate a QCowL2Meta for the zeroize operation because
+ * s->lock is held for the duration of the whole operation.
+ */
+ wait_for_dependencies(bs, offset, bytes);
+
/* If we have to stay in sync with an external data file, zero out
* s->data_file first. */
if (data_file_is_raw(bs)) {