@samitouri / QOSamiQemu / commits / 34a6763776

block: Add blk_co_start/end_request() and BDRV_REQ_NO_QUEUE

If a device uses blk_inc/dec_in_flight() in order to build macro operations that involve multiple requests for the block layer and that need to be completed as a unit before the BlockBackend can be considered drained, it sets the stage for a deadlock: When a drain is requested, the inner request at the BlockBackend level will be queued in blk_wait_while_drained() and wait until the drained section ends, but at the same time, drain_begin can only return if the whole macro operation at the device level has completed. Introduce a new interface to allow implementing the logic correctly: Instead of queueing individual requests, blk_co_start_request() calls blk_wait_while_drained() once at the beginning. The individual requests must then set BDRV_REQ_NO_QUEUE to avoid being queued and running into the deadlock; being wrapped in blk_co_start/end_request() makes sure that drain_begin waits for them and they don't sneak in when the BlockBackend is supposed to already be quiescent. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20260421161132.99878-3-kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>

Kevin Wolf committed Apr 21, 2026 at 18:11 UTC 34a67637767d3ed1ac813c44effe827bbfba5996
3 files changed +41 -10
block/block-backend.c
+29 -9
@@ -82,6 +82,7 @@ struct BlockBackend {
82 QemuMutex queued_requests_lock; /* protects queued_requests */
83 CoQueue queued_requests;
84 bool disable_request_queuing; /* atomic */
85 + int start_request_count; /* atomic */
86
87 VMChangeStateEntry *vmsh;
88 bool force_allow_inactivate;
@@ -1306,10 +1307,16 @@ bool blk_in_drain(BlockBackend *blk)
1307 }
1308
1309 /* To be called between exactly one pair of blk_inc/dec_in_flight() */
1309 -static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
1310 +static void coroutine_fn blk_wait_while_drained(BlockBackend *blk,
1311 + BdrvRequestFlags flags)
1312 {
1313 assert(blk->in_flight > 0);
1314
1315 + if (flags & BDRV_REQ_NO_QUEUE) {
1316 + assert(qatomic_read(&blk->start_request_count));
1317 + return;
1318 + }
1319 +
1320 if (qatomic_read(&blk->quiesce_counter) &&
1321 !qatomic_read(&blk->disable_request_queuing)) {
1322 /*
@@ -1335,7 +1342,7 @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes,
1342 BlockDriverState *bs;
1343 IO_CODE();
1344
1338 - blk_wait_while_drained(blk);
1345 + blk_wait_while_drained(blk, flags);
1346 GRAPH_RDLOCK_GUARD();
1347
1348 /* Call blk_bs() only after waiting, the graph may have changed */
@@ -1410,7 +1417,7 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes,
1417 BlockDriverState *bs;
1418 IO_CODE();
1419
1413 - blk_wait_while_drained(blk);
1420 + blk_wait_while_drained(blk, flags);
1421 GRAPH_RDLOCK_GUARD();
1422
1423 /* Call blk_bs() only after waiting, the graph may have changed */
@@ -1523,6 +1530,19 @@ void blk_dec_in_flight(BlockBackend *blk)
1530 aio_wait_kick();
1531 }
1532
1533 +void coroutine_fn blk_co_start_request(BlockBackend *blk)
1534 +{
1535 + blk_inc_in_flight(blk);
1536 + blk_wait_while_drained(blk, 0);
1537 + qatomic_inc(&blk->start_request_count);
1538 +}
1539 +
1540 +void blk_end_request(BlockBackend *blk)
1541 +{
1542 + qatomic_dec(&blk->start_request_count);
1543 + blk_dec_in_flight(blk);
1544 +}
1545 +
1546 static void error_callback_bh(void *opaque)
1547 {
1548 struct BlockBackendAIOCB *acb = opaque;
@@ -1741,7 +1761,7 @@ blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
1761 {
1762 IO_CODE();
1763
1744 - blk_wait_while_drained(blk);
1764 + blk_wait_while_drained(blk, 0);
1765 GRAPH_RDLOCK_GUARD();
1766
1767 if (!blk_co_is_available(blk)) {
@@ -1788,7 +1808,7 @@ blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes)
1808 int ret;
1809 IO_CODE();
1810
1791 - blk_wait_while_drained(blk);
1811 + blk_wait_while_drained(blk, 0);
1812 GRAPH_RDLOCK_GUARD();
1813
1814 ret = blk_check_byte_request(blk, offset, bytes);
@@ -1834,7 +1854,7 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset,
1854 static int coroutine_fn blk_co_do_flush(BlockBackend *blk)
1855 {
1856 IO_CODE();
1837 - blk_wait_while_drained(blk);
1857 + blk_wait_while_drained(blk, 0);
1858 GRAPH_RDLOCK_GUARD();
1859
1860 if (!blk_co_is_available(blk)) {
@@ -2009,7 +2029,7 @@ int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset,
2029 IO_CODE();
2030
2031 blk_inc_in_flight(blk); /* increase before waiting */
2012 - blk_wait_while_drained(blk);
2032 + blk_wait_while_drained(blk, 0);
2033 GRAPH_RDLOCK_GUARD();
2034 if (!blk_is_available(blk)) {
2035 blk_dec_in_flight(blk);
@@ -2034,7 +2054,7 @@ int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op,
2054 IO_CODE();
2055
2056 blk_inc_in_flight(blk);
2037 - blk_wait_while_drained(blk);
2057 + blk_wait_while_drained(blk, 0);
2058 GRAPH_RDLOCK_GUARD();
2059
2060 ret = blk_check_byte_request(blk, offset, len);
@@ -2058,7 +2078,7 @@ int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset,
2078 IO_CODE();
2079
2080 blk_inc_in_flight(blk);
2061 - blk_wait_while_drained(blk);
2081 + blk_wait_while_drained(blk, flags);
2082 GRAPH_RDLOCK_GUARD();
2083 if (!blk_is_available(blk)) {
2084 blk_dec_in_flight(blk);
include/block/block-common.h
+10 -1
@@ -215,8 +215,17 @@ typedef enum {
215 */
216 BDRV_REQ_NO_WAIT = 0x400,
217
218 + /*
219 + * Used between blk_co_start_request() and blk_end_request() to avoid
220 + * that the request waits in a drained BlockBackend until the drained
221 + * section ends. Waiting would cause a deadlock because drain waits for
222 + * blk_end_request() to be called, but the request never completes
223 + * because it waits for the drain to end.
224 + */
225 + BDRV_REQ_NO_QUEUE = 0x800,
226 +
227 /* Mask of valid flags */
219 - BDRV_REQ_MASK = 0x7ff,
228 + BDRV_REQ_MASK = 0xfff,
229 } BdrvRequestFlags;
230
231 #define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */
include/system/block-backend-io.h
+2
@@ -71,6 +71,8 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
71
72 void blk_inc_in_flight(BlockBackend *blk);
73 void blk_dec_in_flight(BlockBackend *blk);
74 +void coroutine_fn blk_co_start_request(BlockBackend *blk);
75 +void blk_end_request(BlockBackend *blk);
76
77 bool coroutine_fn GRAPH_RDLOCK blk_co_is_inserted(BlockBackend *blk);
78 bool co_wrapper_mixed_bdrv_rdlock blk_is_inserted(BlockBackend *blk);