156 lines
5.4 KiB
Diff
156 lines
5.4 KiB
Diff
|
From: Anthony PERARD <anthony.perard@citrix.com>
|
||
|
Date: Mon, 6 Apr 2020 15:02:17 +0100
|
||
|
Subject: xen-block: Fix double qlist remove and request leak
|
||
|
|
||
|
Git-commit: 36d883ba0de8a281072ded2b51e0a711fd002139
|
||
|
|
||
|
Commit a31ca6801c02 ("qemu/queue.h: clear linked list pointers on
|
||
|
remove") revealed that a request was removed twice from a list, once
|
||
|
in xen_block_finish_request() and a second time in
|
||
|
xen_block_release_request() when both function are called from
|
||
|
xen_block_complete_aio(). But also, the `requests_inflight' counter is
|
||
|
decreased twice, and thus became negative.
|
||
|
|
||
|
This is a bug that was introduced in bfd0d6366043 ("xen-block: improve
|
||
|
response latency"), where a `finished' list was removed.
|
||
|
|
||
|
That commit also introduced a leak of request in xen_block_do_aio().
|
||
|
That function calls xen_block_finish_request() but the request is
|
||
|
never released after that.
|
||
|
|
||
|
To fix both issue, we do two changes:
|
||
|
- we squash finish_request() and release_request() together as we want
|
||
|
to remove a request from 'inflight' list to add it to 'freelist'.
|
||
|
- before releasing a request, we need to let the other end know the
|
||
|
result, thus we should call xen_block_send_response() before
|
||
|
releasing a request.
|
||
|
|
||
|
The first change fixes the double QLIST_REMOVE() as we remove the extra
|
||
|
call. The second change makes the leak go away because if we want to
|
||
|
call finish_request(), we need to call a function that does all of
|
||
|
finish, send response, and release.
|
||
|
|
||
|
Fixes: bfd0d6366043 ("xen-block: improve response latency")
|
||
|
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
|
||
|
Message-Id: <20200406140217.1441858-1-anthony.perard@citrix.com>
|
||
|
Reviewed-by: Paul Durrant <paul@xen.org>
|
||
|
[mreitz: Amended commit message as per Paul's suggestions]
|
||
|
Signed-off-by: Max Reitz <mreitz@redhat.com>
|
||
|
Signed-off-by: Bruce Rogers <brogers@suse.com>
|
||
|
---
|
||
|
hw/block/dataplane/xen-block.c | 48 ++++++++++++----------------------
|
||
|
1 file changed, 16 insertions(+), 32 deletions(-)
|
||
|
|
||
|
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
|
||
|
index 3b9caeb2fa00a1f4eb338fca7a89..c4ed2870ecd779bf40eb4f3eded3 100644
|
||
|
--- a/hw/block/dataplane/xen-block.c
|
||
|
+++ b/hw/block/dataplane/xen-block.c
|
||
|
@@ -64,6 +64,8 @@ struct XenBlockDataPlane {
|
||
|
AioContext *ctx;
|
||
|
};
|
||
|
|
||
|
+static int xen_block_send_response(XenBlockRequest *request);
|
||
|
+
|
||
|
static void reset_request(XenBlockRequest *request)
|
||
|
{
|
||
|
memset(&request->req, 0, sizeof(request->req));
|
||
|
@@ -115,23 +117,26 @@ out:
|
||
|
return request;
|
||
|
}
|
||
|
|
||
|
-static void xen_block_finish_request(XenBlockRequest *request)
|
||
|
+static void xen_block_complete_request(XenBlockRequest *request)
|
||
|
{
|
||
|
XenBlockDataPlane *dataplane = request->dataplane;
|
||
|
|
||
|
- QLIST_REMOVE(request, list);
|
||
|
- dataplane->requests_inflight--;
|
||
|
-}
|
||
|
+ if (xen_block_send_response(request)) {
|
||
|
+ Error *local_err = NULL;
|
||
|
|
||
|
-static void xen_block_release_request(XenBlockRequest *request)
|
||
|
-{
|
||
|
- XenBlockDataPlane *dataplane = request->dataplane;
|
||
|
+ xen_device_notify_event_channel(dataplane->xendev,
|
||
|
+ dataplane->event_channel,
|
||
|
+ &local_err);
|
||
|
+ if (local_err) {
|
||
|
+ error_report_err(local_err);
|
||
|
+ }
|
||
|
+ }
|
||
|
|
||
|
QLIST_REMOVE(request, list);
|
||
|
+ dataplane->requests_inflight--;
|
||
|
reset_request(request);
|
||
|
request->dataplane = dataplane;
|
||
|
QLIST_INSERT_HEAD(&dataplane->freelist, request, list);
|
||
|
- dataplane->requests_inflight--;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
@@ -246,7 +251,6 @@ static int xen_block_copy_request(XenBlockRequest *request)
|
||
|
}
|
||
|
|
||
|
static int xen_block_do_aio(XenBlockRequest *request);
|
||
|
-static int xen_block_send_response(XenBlockRequest *request);
|
||
|
|
||
|
static void xen_block_complete_aio(void *opaque, int ret)
|
||
|
{
|
||
|
@@ -286,7 +290,6 @@ static void xen_block_complete_aio(void *opaque, int ret)
|
||
|
}
|
||
|
|
||
|
request->status = request->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
|
||
|
- xen_block_finish_request(request);
|
||
|
|
||
|
switch (request->req.operation) {
|
||
|
case BLKIF_OP_WRITE:
|
||
|
@@ -306,17 +309,8 @@ static void xen_block_complete_aio(void *opaque, int ret)
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
- if (xen_block_send_response(request)) {
|
||
|
- Error *local_err = NULL;
|
||
|
|
||
|
- xen_device_notify_event_channel(dataplane->xendev,
|
||
|
- dataplane->event_channel,
|
||
|
- &local_err);
|
||
|
- if (local_err) {
|
||
|
- error_report_err(local_err);
|
||
|
- }
|
||
|
- }
|
||
|
- xen_block_release_request(request);
|
||
|
+ xen_block_complete_request(request);
|
||
|
|
||
|
if (dataplane->more_work) {
|
||
|
qemu_bh_schedule(dataplane->bh);
|
||
|
@@ -420,8 +414,8 @@ static int xen_block_do_aio(XenBlockRequest *request)
|
||
|
return 0;
|
||
|
|
||
|
err:
|
||
|
- xen_block_finish_request(request);
|
||
|
request->status = BLKIF_RSP_ERROR;
|
||
|
+ xen_block_complete_request(request);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
@@ -575,17 +569,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane)
|
||
|
break;
|
||
|
};
|
||
|
|
||
|
- if (xen_block_send_response(request)) {
|
||
|
- Error *local_err = NULL;
|
||
|
-
|
||
|
- xen_device_notify_event_channel(dataplane->xendev,
|
||
|
- dataplane->event_channel,
|
||
|
- &local_err);
|
||
|
- if (local_err) {
|
||
|
- error_report_err(local_err);
|
||
|
- }
|
||
|
- }
|
||
|
- xen_block_release_request(request);
|
||
|
+ xen_block_complete_request(request);
|
||
|
continue;
|
||
|
}
|
||
|
|