hw/block/dataplane/xen-block.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
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, where a `finished'
list was removed.
This patch simply re-add the `finish' parameter of
xen_block_release_request() so that we can distinguish when we need to
remove a request from the inflight list and when not.
Fixes: bfd0d6366043 ("xen-block: improve response latency")
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
---
hw/block/dataplane/xen-block.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index 288a87a814ad..6cc089fc561f 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -123,15 +123,19 @@ static void xen_block_finish_request(XenBlockRequest *request)
dataplane->requests_inflight--;
}
-static void xen_block_release_request(XenBlockRequest *request)
+static void xen_block_release_request(XenBlockRequest *request, bool finish)
{
XenBlockDataPlane *dataplane = request->dataplane;
- QLIST_REMOVE(request, list);
+ if (!finish) {
+ QLIST_REMOVE(request, list);
+ }
reset_request(request);
request->dataplane = dataplane;
QLIST_INSERT_HEAD(&dataplane->freelist, request, list);
- dataplane->requests_inflight--;
+ if (!finish) {
+ dataplane->requests_inflight--;
+ }
}
/*
@@ -316,7 +320,7 @@ static void xen_block_complete_aio(void *opaque, int ret)
error_report_err(local_err);
}
}
- xen_block_release_request(request);
+ xen_block_release_request(request, true);
if (dataplane->more_work) {
qemu_bh_schedule(dataplane->bh);
@@ -585,7 +589,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane)
error_report_err(local_err);
}
}
- xen_block_release_request(request);
+ xen_block_release_request(request, false);
continue;
}
--
Anthony PERARD
> -----Original Message----- > From: Anthony PERARD <anthony.perard@citrix.com> > Sent: 02 April 2020 14:08 > To: qemu-devel@nongnu.org > Cc: qemu-stable@nongnu.org; Anthony PERARD <anthony.perard@citrix.com>; Stefano Stabellini > <sstabellini@kernel.org>; Paul Durrant <paul@xen.org>; Stefan Hajnoczi <stefanha@redhat.com>; Kevin > Wolf <kwolf@redhat.com>; Max Reitz <mreitz@redhat.com>; xen-devel@lists.xenproject.org; qemu- > block@nongnu.org > Subject: [PATCH for-5.0] xen-block: Fix double qlist remove > > 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, where a `finished' > list was removed. > > This patch simply re-add the `finish' parameter of > xen_block_release_request() so that we can distinguish when we need to > remove a request from the inflight list and when not. > > Fixes: bfd0d6366043 ("xen-block: improve response latency") > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> It looks to me like it would just be more straightforward to simply drop the QLIST_REMOVE and requests_inflight-- from xen_block_release_request() and simply insist that xen_block_finish_request() is called in all cases (which I think means adding one extra call to it in xen_block_handle_requests()). Paul > --- > hw/block/dataplane/xen-block.c | 14 +++++++++----- > 1 file changed, 9 insertions(+), 5 deletions(-) > > diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c > index 288a87a814ad..6cc089fc561f 100644 > --- a/hw/block/dataplane/xen-block.c > +++ b/hw/block/dataplane/xen-block.c > @@ -123,15 +123,19 @@ static void xen_block_finish_request(XenBlockRequest *request) > dataplane->requests_inflight--; > } > > -static void xen_block_release_request(XenBlockRequest *request) > +static void xen_block_release_request(XenBlockRequest *request, bool finish) > { > XenBlockDataPlane *dataplane = request->dataplane; > > - QLIST_REMOVE(request, list); > + if (!finish) { > + QLIST_REMOVE(request, list); > + } > reset_request(request); > request->dataplane = dataplane; > QLIST_INSERT_HEAD(&dataplane->freelist, request, list); > - dataplane->requests_inflight--; > + if (!finish) { > + dataplane->requests_inflight--; > + } > } > > /* > @@ -316,7 +320,7 @@ static void xen_block_complete_aio(void *opaque, int ret) > error_report_err(local_err); > } > } > - xen_block_release_request(request); > + xen_block_release_request(request, true); > > if (dataplane->more_work) { > qemu_bh_schedule(dataplane->bh); > @@ -585,7 +589,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) > error_report_err(local_err); > } > } > - xen_block_release_request(request); > + xen_block_release_request(request, false); > continue; > } > > -- > Anthony PERARD
On Thu, Apr 02, 2020 at 03:27:22PM +0100, Paul Durrant wrote: > > -----Original Message----- > > From: Anthony PERARD <anthony.perard@citrix.com> > > Sent: 02 April 2020 14:08 > > To: qemu-devel@nongnu.org > > Cc: qemu-stable@nongnu.org; Anthony PERARD <anthony.perard@citrix.com>; Stefano Stabellini > > <sstabellini@kernel.org>; Paul Durrant <paul@xen.org>; Stefan Hajnoczi <stefanha@redhat.com>; Kevin > > Wolf <kwolf@redhat.com>; Max Reitz <mreitz@redhat.com>; xen-devel@lists.xenproject.org; qemu- > > block@nongnu.org > > Subject: [PATCH for-5.0] xen-block: Fix double qlist remove > > > > 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, where a `finished' > > list was removed. > > > > This patch simply re-add the `finish' parameter of > > xen_block_release_request() so that we can distinguish when we need to > > remove a request from the inflight list and when not. > > > > Fixes: bfd0d6366043 ("xen-block: improve response latency") > > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > > It looks to me like it would just be more straightforward to simply drop the QLIST_REMOVE and requests_inflight-- from > xen_block_release_request() and simply insist that xen_block_finish_request() is called in all cases (which I think means adding one > extra call to it in xen_block_handle_requests()). I'm thinking of going further than that. I've notice another bug, in case of error in xen_block_do_aio(), xen_block_finish_request() is called without ever calling send_response() or release_request(). I think that mean a leak of request. So, I'm thinking of creating a function that would do finish_request(), send_response(), release_request(), has I believe those operations needs to be done together anyway. I'll rework the patch. -- Anthony PERARD
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, 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 now the result to the
other end, thus we should call xen_block_send_response() before
releasing a request.
The first change fix 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 do all of
finish, send response, and release.
Fixes: bfd0d6366043 ("xen-block: improve response latency")
Signed-off-by: Anthony PERARD <anthony.perard@citrix.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 288a87a814ad..5f8f15778ba5 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 @@ static XenBlockRequest *xen_block_start_request(XenBlockDataPlane *dataplane)
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;
}
--
Anthony PERARD
On 06.04.20 16:02, Anthony PERARD wrote: > 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, 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 now the result to the > other end, thus we should call xen_block_send_response() before > releasing a request. > > The first change fix 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 do all of > finish, send response, and release. > > Fixes: bfd0d6366043 ("xen-block: improve response latency") > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> > --- > hw/block/dataplane/xen-block.c | 48 ++++++++++++---------------------- > 1 file changed, 16 insertions(+), 32 deletions(-) I’m going to send a pull request today anyway, so I hope you won’t mind and let me take this patch to my branch (with Paul’s suggestions incorporated): https://git.xanclic.moe/XanClic/qemu/commits/branch/block Max
> -----Original Message----- > From: Anthony PERARD <anthony.perard@citrix.com> > Sent: 06 April 2020 15:02 > To: qemu-devel@nongnu.org > Cc: qemu-stable@nongnu.org; Anthony PERARD <anthony.perard@citrix.com>; Stefano Stabellini > <sstabellini@kernel.org>; Paul Durrant <paul@xen.org>; Stefan Hajnoczi <stefanha@redhat.com>; Kevin > Wolf <kwolf@redhat.com>; Max Reitz <mreitz@redhat.com>; xen-devel@lists.xenproject.org; qemu- > block@nongnu.org > Subject: [PATCH v2 for-5.0] xen-block: Fix double qlist remove and request leak > > 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 NIT: I guess you should quote the patch title here as well. > , 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 now the result to the > other end, "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 fix the double QLIST_REMOVE() as we remove the extra s/fix/fixes > call. The second change makes the leak go away because if we want to > call finish_request(), we need to call a function that do all of s/do/does > finish, send response, and release. > > Fixes: bfd0d6366043 ("xen-block: improve response latency") > Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> The code looks ok, so with the cosmetic fixes... Reviewed-by: Paul Durrant <paul@xen.org>
© 2016 - 2024 Red Hat, Inc.