From: Stefan Hajnoczi <stefanha@redhat.com>
To: Kevin Wolf <kwolf@redhat.com>, qemu-devel@nongnu.org
Cc: Stefan Hajnoczi <stefanha@redhat.com>,
Leonardo Bras <leobras@redhat.com>,
qemu-block@nongnu.org, Fam Zheng <fam@euphon.net>,
Paolo Bonzini <pbonzini@redhat.com>,
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>,
Fabiano Rosas <farosas@suse.de>, Eric Blake <eblake@redhat.com>,
Hanna Reitz <hreitz@redhat.com>,
Juan Quintela <quintela@redhat.com>, Peter Xu <peterx@redhat.com>
Subject: [PATCH 5/6] nbd/server: only traverse NBDExport->clients from main loop thread
Date: Wed, 20 Dec 2023 20:49:02 -0500 [thread overview]
Message-ID: <20231221014903.1537962-6-stefanha@redhat.com> (raw)
In-Reply-To: <20231221014903.1537962-1-stefanha@redhat.com>
The NBD clients list is currently accessed from both the export
AioContext and the main loop thread. When the AioContext lock is removed
there will be nothing protecting the clients list.
Adding a lock around the clients list is tricky because NBDClient
structs are refcounted and may be freed from the export AioContext or
the main loop thread. nbd_export_request_shutdown() -> client_close() ->
nbd_client_put() is also tricky because the list lock would be held
while indirectly dropping references to NDBClients.
A simpler approach is to only allow nbd_client_put() and client_close()
calls from the main loop thread. Then the NBD clients list is only
accessed from the main loop thread and no fancy locking is needed.
nbd_trip() just needs to reschedule itself in the main loop AioContext
before calling nbd_client_put() and client_close(). This costs more CPU
cycles per NBD request but is needed for thread-safety when the
AioContext lock is removed.
Note that nbd_client_get() can still be called from either thread, so
make NBDClient->refcount atomic.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
nbd/server.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/nbd/server.c b/nbd/server.c
index 0b09ccc8dc..527fbdab4a 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -122,7 +122,7 @@ struct NBDMetaContexts {
};
struct NBDClient {
- int refcount;
+ int refcount; /* atomic */
void (*close_fn)(NBDClient *client, bool negotiated);
NBDExport *exp;
@@ -1501,14 +1501,17 @@ static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest *reque
#define MAX_NBD_REQUESTS 16
+/* Runs in export AioContext and main loop thread */
void nbd_client_get(NBDClient *client)
{
- client->refcount++;
+ qatomic_inc(&client->refcount);
}
void nbd_client_put(NBDClient *client)
{
- if (--client->refcount == 0) {
+ assert(qemu_in_main_thread());
+
+ if (qatomic_fetch_dec(&client->refcount) == 1) {
/* The last reference should be dropped by client->close,
* which is called by client_close.
*/
@@ -1531,6 +1534,8 @@ void nbd_client_put(NBDClient *client)
static void client_close(NBDClient *client, bool negotiated)
{
+ assert(qemu_in_main_thread());
+
if (client->closing) {
return;
}
@@ -2938,8 +2943,15 @@ static coroutine_fn void nbd_trip(void *opaque)
int ret;
Error *local_err = NULL;
+ /*
+ * Note that nbd_client_put() and client_close() must be called from the
+ * main loop thread. Use aio_co_reschedule_self() to switch AioContext
+ * before calling these functions.
+ */
+
trace_nbd_trip();
if (client->closing) {
+ aio_co_reschedule_self(qemu_get_aio_context());
nbd_client_put(client);
return;
}
@@ -2949,6 +2961,7 @@ static coroutine_fn void nbd_trip(void *opaque)
* We're switching between AIO contexts. Don't attempt to receive a new
* request and kick the main context which may be waiting for us.
*/
+ aio_co_reschedule_self(qemu_get_aio_context());
nbd_client_put(client);
client->recv_coroutine = NULL;
aio_wait_kick();
@@ -3013,6 +3026,8 @@ static coroutine_fn void nbd_trip(void *opaque)
qio_channel_set_cork(client->ioc, false);
done:
nbd_request_put(req);
+
+ aio_co_reschedule_self(qemu_get_aio_context());
nbd_client_put(client);
return;
@@ -3021,6 +3036,8 @@ disconnect:
error_reportf_err(local_err, "Disconnect client, due to: ");
}
nbd_request_put(req);
+
+ aio_co_reschedule_self(qemu_get_aio_context());
client_close(client, true);
nbd_client_put(client);
}
--
2.43.0
next prev parent reply other threads:[~2023-12-21 1:50 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-21 1:48 [PATCH 0/6] qemu-iotests fixes for Kevin's block tree Stefan Hajnoczi
2023-12-21 1:48 ` [PATCH 1/6] fixup block-coroutine-wrapper: use qemu_get_current_aio_context() Stefan Hajnoczi
2023-12-21 1:48 ` [PATCH 2/6] fixup block: remove AioContext locking Stefan Hajnoczi
2023-12-21 1:49 ` [PATCH 3/6] fixup scsi: only access SCSIDevice->requests from one thread Stefan Hajnoczi
2023-12-21 7:29 ` Paolo Bonzini
2023-12-21 1:49 ` [PATCH 4/6] nbd/server: avoid per-NBDRequest nbd_client_get/put() Stefan Hajnoczi
2023-12-21 7:27 ` Paolo Bonzini
2023-12-21 1:49 ` Stefan Hajnoczi [this message]
2023-12-21 7:23 ` [PATCH 5/6] nbd/server: only traverse NBDExport->clients from main loop thread Paolo Bonzini
2023-12-21 14:27 ` Stefan Hajnoczi
2024-01-02 15:32 ` Eric Blake
2023-12-21 1:49 ` [PATCH 6/6] nbd/server: introduce NBDClient->lock to protect fields Stefan Hajnoczi
2023-12-21 7:26 ` Paolo Bonzini
2023-12-21 11:56 ` Stefan Hajnoczi
2023-12-21 10:45 ` Kevin Wolf
2023-12-21 14:14 ` Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231221014903.1537962-6-stefanha@redhat.com \
--to=stefanha@redhat.com \
--cc=eblake@redhat.com \
--cc=fam@euphon.net \
--cc=farosas@suse.de \
--cc=hreitz@redhat.com \
--cc=kwolf@redhat.com \
--cc=leobras@redhat.com \
--cc=pbonzini@redhat.com \
--cc=peterx@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
--cc=vsementsov@yandex-team.ru \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).