Commit 6460440f authored by Kevin Wolf's avatar Kevin Wolf
Browse files

block: Allow wait_serialising_requests() at any point



We can only have a single wait_serialising_requests() call per request
because otherwise we can run into deadlocks where requests are waiting
for each other. The same is true when wait_serialising_requests() is not
at the very beginning of a request, so that other requests can be issued
between the start of the tracking and wait_serialising_requests().

Fix this by changing wait_serialising_requests() to ignore requests that
are already (directly or indirectly) waiting for the calling request.

Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
Reviewed-by: default avatarMax Reitz <mreitz@redhat.com>
Reviewed-by: default avatarBenoit Canet <benoit@irqsave.net>
parent 7327145f
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -2328,11 +2328,18 @@ static void coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
                 */
                assert(qemu_coroutine_self() != req->co);

                /* If the request is already (indirectly) waiting for us, or
                 * will wait for us as soon as it wakes up, then just go on
                 * (instead of producing a deadlock in the former case). */
                if (!req->waiting_for) {
                    self->waiting_for = req;
                    qemu_co_queue_wait(&req->wait_queue);
                    self->waiting_for = NULL;
                    retry = true;
                    break;
                }
            }
        }
    } while (retry);
}

+2 −0
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ typedef struct BdrvTrackedRequest {
    QLIST_ENTRY(BdrvTrackedRequest) list;
    Coroutine *co; /* owner, used for deadlock detection */
    CoQueue wait_queue; /* coroutines blocked on this request */

    struct BdrvTrackedRequest *waiting_for;
} BdrvTrackedRequest;

struct BlockDriver {