aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/io.c28
-rw-r--r--include/block/block.h14
2 files changed, 40 insertions, 2 deletions
diff --git a/block/io.c b/block/io.c
index 75ab26f..6be9c40 100644
--- a/block/io.c
+++ b/block/io.c
@@ -637,6 +637,18 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes);
}
+static bool is_request_serialising_and_aligned(BdrvTrackedRequest *req)
+{
+ /*
+ * If the request is serialising, overlap_offset and overlap_bytes are set,
+ * so we can check if the request is aligned. Otherwise, don't care and
+ * return false.
+ */
+
+ return req->serialising && (req->offset == req->overlap_offset) &&
+ (req->bytes == req->overlap_bytes);
+}
+
/**
* Round a region to cluster boundaries
*/
@@ -1311,6 +1323,9 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
mark_request_serialising(req, bdrv_get_cluster_size(bs));
}
+ /* BDRV_REQ_SERIALISING is only for write operation */
+ assert(!(flags & BDRV_REQ_SERIALISING));
+
if (!(flags & BDRV_REQ_NO_SERIALISING)) {
wait_serialising_requests(req);
}
@@ -1594,8 +1609,14 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
/* BDRV_REQ_NO_SERIALISING is only for read operation */
assert(!(flags & BDRV_REQ_NO_SERIALISING));
+
+ if (flags & BDRV_REQ_SERIALISING) {
+ mark_request_serialising(req, bdrv_get_cluster_size(bs));
+ }
+
waited = wait_serialising_requests(req);
- assert(!waited || !req->serialising);
+ assert(!waited || !req->serialising ||
+ is_request_serialising_and_aligned(req));
assert(req->overlap_offset <= offset);
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
@@ -2949,6 +2970,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(
tracked_request_begin(&req, src->bs, src_offset, bytes,
BDRV_TRACKED_READ);
+ /* BDRV_REQ_SERIALISING is only for write operation */
+ assert(!(read_flags & BDRV_REQ_SERIALISING));
if (!(read_flags & BDRV_REQ_NO_SERIALISING)) {
wait_serialising_requests(&req);
}
@@ -2968,6 +2991,9 @@ static int coroutine_fn bdrv_co_copy_range_internal(
/* BDRV_REQ_NO_SERIALISING is only for read operation */
assert(!(write_flags & BDRV_REQ_NO_SERIALISING));
+ if (write_flags & BDRV_REQ_SERIALISING) {
+ mark_request_serialising(&req, bdrv_get_cluster_size(dst->bs));
+ }
wait_serialising_requests(&req);
ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs,
diff --git a/include/block/block.h b/include/block/block.h
index e474f25..a91f37b 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -70,8 +70,20 @@ typedef enum {
* content. */
BDRV_REQ_WRITE_UNCHANGED = 0x40,
+ /*
+ * BDRV_REQ_SERIALISING forces request serialisation for writes.
+ * It is used to ensure that writes to the backing file of a backup process
+ * target cannot race with a read of the backup target that defers to the
+ * backing file.
+ *
+ * Note, that BDRV_REQ_SERIALISING is _not_ opposite in meaning to
+ * BDRV_REQ_NO_SERIALISING. A more descriptive name for the latter might be
+ * _DO_NOT_WAIT_FOR_SERIALISING, except that is too long.
+ */
+ BDRV_REQ_SERIALISING = 0x80,
+
/* Mask of valid flags */
- BDRV_REQ_MASK = 0x7f,
+ BDRV_REQ_MASK = 0xff,
} BdrvRequestFlags;
typedef struct BlockSizes {