qio: general non-default GMainContext support
cover qio: general non-default GMainContext support
This is another preparation work for monitor OOB seires.
This series tries to allow QIO code to run with non-default GMainContext. Note that for most places I kept the old code untouched, and only modified/introduced new interfaces where there can be a non-default GMainContext. The "where" is mostly migration and chardev submodules, since these two parts of code can be run with monitor IOThread in the future (which holds a non-default GMainContext).
These are existing known issue to be solved with GSources that bound to main thread:
- migration
- incoming side: still always running on main context, while we need to be able to run some command in OOB thread [1]
- tcp chardev (non-tcp chardevs should all support non-NULL context now)
- server listening mode: QIO net listener used [2]
- TELNET session: an isolated GSource used (tcp_chr_telnet_init) [3]
- when "reconnect=N" is used, QIO threaded task is used [4]
- TLS session: QIO tls handshake is used (tcp_chr_tls_init) [5]
Patch 1-2 are cleanups and fixes.
Patch 3 introduced qio_channel_add_watch_full(), which is the core API for QIO to support non-default context.
Patch 4 fixes the migration usage of QIO, which is problem [1] above.
Patch 5-7 fixes the net listeners to use non-default gcontext, which solves problem [2] above.
Patch 8 fixes the TELNET GSource, which solves problem [3].
Patch 9-13 fixes the threaded QIOTask usage, which is for problem [4].
Patch 14 fixes the last TLS usage, which is problem [5].
The whole series survives with "make check". There are quite a few QIO tests there. Let's see whether this can be acceptable before more tests.
Please review. Thanks.
Peter Xu (14):
chardev: fix leak in tcp_chr_telnet_init_io()
qio: rename qio_task_thread_result
qio: introduce qio_channel_add_watch_full()
migration: let incoming side use thread context
qio: refactor net listener source operations
qio: store gsources for net listeners qio/chardev: update net listener gcontext
chardev: allow telnet gsource to switch gcontext
qio: basic non-default context support for thread
qio: refcount QIOTask
qio/chardev: return QIOTask when connect async
qio: move QIOTaskThreadData into QIOTask
qio: allow threaded qiotask to switch contexts qio/chardev: specify gcontext for TLS handshake
chardev/char-socket.c | 114 ++++++++++++++++++++++++++------- include/io/channel-socket.h | 14 +++-- include/io/channel-tls.h | 22 ++++++- include/io/channel.h | 31 ++++++++- include/io/net-listener.h | 33 +++++++++- include/io/task.h | 10 ++- io/channel-socket.c | 21 ++++--- io/channel-tls.c | 91 ++++++++++++++++++++++----- io/channel.c | 24 +++++-- io/dns-resolver.c | 3 +- io/net-listener.c | 119 +++++++++++++++++++++-------------- io/task.c | 149 ++++++++++++++++++++++++++++++++++++-------- migration/exec.c | 11 ++-- migration/fd.c | 11 ++-- migration/socket.c | 12 ++-- tests/test-io-task.c | 2 + 16 files changed, 514 insertions(+), 153 deletions(-)
Signature · 1 line
2.14.3
PATCH 1/14 chardev: fix leak in tcp_chr_telnet_init_io()
Need to free TCPChardevTelnetInit when session established.
Since at it, switch to use G_SOURCE_* macros.
--- chardev/char-socket.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
chardev/char-socket.c +7 -3
@@ -591,19 +591,23 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, ret =0 ; }else {tcp_chr_disconnect (init->chr); -return FALSE; + goto end; } } init->buflen -= ret;if (init->buflen ==0 ) {tcp_chr_connect (init->chr); -return FALSE; + goto end; }memmove (init->buf, init->buf + ret, init->buflen); -return TRUE; +return G_SOURCE_CONTINUE ; + +end: +g_free (init); +return G_SOURCE_REMOVE ; }static void tcp_chr_telnet_init (Chardev *chr)
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 01/14] chardev: fix leak in tcp_chr_telnet_init_io()
Quoted 8 lines · Peter Xu
Need to free TCPChardevTelnetInit when session established.
Since at it, switch to use G_SOURCE_* macros.
Signed-off-by Peter Xu <[email protected]>--- chardev/char-socket.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
PATCH 2/14 qio: rename qio_task_thread_result
In reply to Peter XuIt is strange that it was called gio_task_thread_result. Rename it to follow the naming rule of the file.
--- io/task.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
io/task.c +2 -2
@@ -80,7 +80,7 @@ struct QIOTaskThreadData { }; -static gboolean gio_task_thread_result (gpointer opaque) +static gboolean qio_task_thread_result (gpointer opaque) {struct QIOTaskThreadData *data = opaque;
@@ -110,7 +110,7 @@ static gpointer qio_task_thread_worker(gpointer opaque)* the worker results */ trace_qio_task_thread_exit (data->task); -g_idle_add (gio_task_thread_result, data); +g_idle_add (qio_task_thread_result, data);return NULL ; }
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 02/14] qio: rename qio_task_thread_result
Quoted 7 lines · Peter Xu
It is strange that it was called
gio_task_thread_result. Rename it to follow the naming rule of the file.Signed-off-by Peter Xu <[email protected]>--- io/task.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Quoted 26 lines
io/task.c +2
-2@@ -80,7 +80,7 @@ struct QIOTaskThreadData { }; -static gboolean gio_task_thread_result (gpointer opaque) +static gboolean qio_task_thread_result (gpointer opaque) {struct QIOTaskThreadData *data = opaque;@@ -110,7 +110,7 @@ static gpointer qio_task_thread_worker(gpointer opaque)* the worker results */ trace_qio_task_thread_exit (data->task); -g_idle_add (gio_task_thread_result, data); +g_idle_add (qio_task_thread_result, data);return NULL ; }Signature · 1 line
2.14.3
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
PATCH 3/14 qio: introduce qio_channel_add_watch_full()
In reply to Peter XuIt's a more powerful version of qio_channel_add_watch(), which supports non-default gcontext. It's stripped from the old one, then we have g_source_get_id() to fetch the tag ID to keep the old interface.
Note that the new API will return a gsource, meanwhile keep a reference of it so that callers need to unref them explicitly.
--- include/io/channel.h | 31 ++++++++++++++++++++++++++++--- io/channel.c | 24 +++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-)
include/io/channel.h +28 -3
@@ -620,20 +620,45 @@ GSource *qio_channel_create_watch(QIOChannel *ioc, GIOCondition condition);/** -* qio_channel_add_watch: +* qio_channel_add_watch_full: * @ioc: the channel object * @condition: the I/O condition to monitor * @func: callback to invoke when the source becomes ready * @user_data: opaque data to pass to @func * @notify: callback to free @user_data +* @context: gcontext to bind the source to * -* Create a new main loop source that is used to watch +* Create a new source that is used to watch * for the I/O condition @condition. The callback @func * will be registered against the source, to be invoked * when the source becomes ready. The optional @user_data * will be passed to @func when it is invoked. The @notify * callback will be used to free @user_data when the -* watch is deleted +* watch is deleted. The source will be bound to @context if +* provided, or main context if it is NULL. +* +* Note: if a valid source is returned, we need to explicitly unref +* the source to destroy it. +* +* Returns: the source pointer +*/ +GSource *qio_channel_add_watch_full (QIOChannel *ioc, +GIOCondition condition, +QIOChannelFunc func, +gpointer user_data, +GDestroyNotify notify, +GMainContext *context); + +/** +* qio_channel_add_watch: +* @ioc: the channel object +* @condition: the I/O condition to monitor +* @func: callback to invoke when the source becomes ready +* @user_data: opaque data to pass to @func +* @notify: callback to free @user_data +* +* Wrapper of qio_channel_add_watch_full(), but it'll only bind the +* source object to default main context. * * The returned source ID can be used with g_source_remove() * to remove and free the source when no longer required.
io/channel.c +19 -5
@@ -299,6 +299,22 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc, klass->io_set_aio_fd_handler (ioc, ctx, io_read, io_write, opaque); } +GSource *qio_channel_add_watch_full (QIOChannel *ioc, +GIOCondition condition, +QIOChannelFunc func, +gpointer user_data, +GDestroyNotify notify, +GMainContext *context) +{ +GSource *source; + + source =qio_channel_create_watch (ioc, condition); +g_source_set_callback (source, (GSourceFunc )func, user_data, notify); +g_source_attach (source, context); + +return source; +} + guint qio_channel_add_watch(QIOChannel *ioc, GIOCondition condition, QIOChannelFunc func,
@@ -308,11 +324,9 @@ guint qio_channel_add_watch(QIOChannel *ioc,GSource *source;guint id; - source =qio_channel_create_watch (ioc, condition); - -g_source_set_callback (source, (GSourceFunc )func, user_data, notify); - - id =g_source_attach (source,NULL ); + source =qio_channel_add_watch_full (ioc, condition, func, + user_data, notify,NULL ); + id =g_source_get_id (source);g_source_unref (source);return id;
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 03/14] qio: introduce qio_channel_add_watch_full()
Quoted 6 lines · Peter Xu
It's a more powerful version of
qio_channel_add_watch(), which supports non-default gcontext. It's stripped from the old one, then we haveg_source_get_id()to fetch the tag ID to keep the old interface.Note that the new API will return a gsource, meanwhile keep a reference of it so that callers need to unref them explicitly.
I don't really like this. Having qio_channel_add_watch and qio_channel_add_watch_full with differing return values is really very surprising. They should be functionally identical, except for the extra context arg.
Quoted 105 lines
Signed-off-by Peter Xu <[email protected]>--- include/io/channel.h | 31 ++++++++++++++++++++++++++++--- io/channel.c | 24 +++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-)include/io/channel.h +28
-3@@ -620,20 +620,45 @@ GSource *qio_channel_create_watch(QIOChannel *ioc, GIOCondition condition);/** -* qio_channel_add_watch: +* qio_channel_add_watch_full: * @ioc: the channel object * @condition: the I/O condition to monitor * @func: callback to invoke when the source becomes ready * @user_data: opaque data to pass to @func * @notify: callback to free @user_data +* @context: gcontext to bind the source to * -* Create a new main loop source that is used to watch +* Create a new source that is used to watch * for the I/O condition @condition. The callback @func * will be registered against the source, to be invoked * when the source becomes ready. The optional @user_data * will be passed to @func when it is invoked. The @notify * callback will be used to free @user_data when the -* watch is deleted +* watch is deleted. The source will be bound to @context if +* provided, or main context if it is NULL. +* +* Note: if a valid source is returned, we need to explicitly unref +* the source to destroy it. +* +* Returns: the source pointer +*/ +GSource *qio_channel_add_watch_full (QIOChannel *ioc, +GIOCondition condition, +QIOChannelFunc func, +gpointer user_data, +GDestroyNotify notify, +GMainContext *context); + +/** +* qio_channel_add_watch: +* @ioc: the channel object +* @condition: the I/O condition to monitor +* @func: callback to invoke when the source becomes ready +* @user_data: opaque data to pass to @func +* @notify: callback to free @user_data +* +* Wrapper of qio_channel_add_watch_full(), but it'll only bind the +* source object to default main context. * * The returned source ID can be used with g_source_remove() * to remove and free the source when no longer required. io/channel.c +19
-5@@ -299,6 +299,22 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc, klass->io_set_aio_fd_handler (ioc, ctx, io_read, io_write, opaque); } +GSource *qio_channel_add_watch_full (QIOChannel *ioc, +GIOCondition condition, +QIOChannelFunc func, +gpointer user_data, +GDestroyNotify notify, +GMainContext *context) +{ +GSource *source; + + source =qio_channel_create_watch (ioc, condition); +g_source_set_callback (source, (GSourceFunc )func, user_data, notify); +g_source_attach (source, context); + +return source; +} + guint qio_channel_add_watch(QIOChannel *ioc, GIOCondition condition, QIOChannelFunc func,@@ -308,11 +324,9 @@ guint qio_channel_add_watch(QIOChannel *ioc,GSource *source;guint id; - source =qio_channel_create_watch (ioc, condition); - -g_source_set_callback (source, (GSourceFunc )func, user_data, notify); - - id =g_source_attach (source,NULL ); + source =qio_channel_add_watch_full (ioc, condition, func, + user_data, notify,NULL ); + id =g_source_get_id (source);g_source_unref (source);return id;Signature · 1 line
2.14.3
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 03/14] qio: introduce qio_channel_add_watch_full()
Quoted 5 lines · Daniel P. Berrangé
Quoted 4 lines · Peter Xu
It's a more powerful version of
qio_channel_add_watch(), which supports non-default gcontext. It's stripped from the old one, then we haveg_source_get_id()to fetch the tag ID to keep the old interface.
Quoted 7 lines
Quoted 2 lines
Note that the new API will return a gsource, meanwhile keep a reference of it so that callers need to unref them explicitly.
I don't really like this. Having
qio_channel_add_watchandqio_channel_add_watch_fullwith differing return values is really very surprising. They should be functionally identical, except for the extra context arg.
Yeah it's not nice, but I do need the GSource and the tag ID does not help in the series.
An alternative would be that I modify qio_channel_add_watch() to return GSource too. Is there an third choice that you could suggest?
Thanks,
Signature · 1 line
Peter Xu
Re: [Qemu-devel] [PATCH 03/14] qio: introduce qio_channel_add_watch_full()
Quoted 14 lines · Peter Xu
Quoted 5 lines · Daniel P. Berrangé
Quoted 4 lines · Peter Xu
It's a more powerful version of
qio_channel_add_watch(), which supports non-default gcontext. It's stripped from the old one, then we haveg_source_get_id()to fetch the tag ID to keep the old interface.Quoted 7 lines
Quoted 2 lines
Note that the new API will return a gsource, meanwhile keep a reference of it so that callers need to unref them explicitly.
I don't really like this. Having
qio_channel_add_watchandqio_channel_add_watch_fullwith differing return values is really very surprising. They should be functionally identical, except for the extra context arg.
Quoted 5 lines
Yeah it's not nice, but I do need the GSource and the tag ID does not help in the series.
An alternative would be that I modify
qio_channel_add_watch()to return GSource too. Is there an third choice that you could suggest?
Given you have the id + GMainContext you can just acquire the GSource, if needed, using g_main_context_find_source_by_id.
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 03/14] qio: introduce qio_channel_add_watch_full()
Quoted 15 lines · Daniel P. Berrangé
Quoted 14 lines · Peter Xu
Quoted 12 lines · Daniel P. Berrangé
Quoted 6 lines · Peter Xu
It's a more powerful version of
qio_channel_add_watch(), which supports non-default gcontext. It's stripped from the old one, then we haveg_source_get_id()to fetch the tag ID to keep the old interface.Note that the new API will return a gsource, meanwhile keep a reference of it so that callers need to unref them explicitly.
I don't really like this. Having
qio_channel_add_watchandqio_channel_add_watch_fullwith differing return values is really very surprising. They should be functionally identical, except for the extra context arg.
Quoted 8 lines
Quoted 5 lines
Yeah it's not nice, but I do need the GSource and the tag ID does not help in the series.
An alternative would be that I modify
qio_channel_add_watch()to return GSource too. Is there an third choice that you could suggest?Given you have the id + GMainContext you can just acquire the GSource, if needed, using
g_main_context_find_source_by_id.
I always feel unsafe to play around with tag IDs since the IDs can change after GSource removed and new GSource added, and also the result of the call will depend on a correct pairing of context (so if the context is incorrect, instead of failure, we possibly got everything screwed up while we never know we failed...).
But indeed for this one it seems pretty safe if I call g_main_context_find_source_by_id() right after I call qio_channel_add_watch_full() to fetch the GSource. If you agree, I can use this approach in my next post. Thanks,
Signature · 1 line
Peter Xu
PATCH 4/14 migration: let incoming side use thread context
In reply to Peter XuThe old incoming migration is running in main thread and default gcontext. With the new qio_channel_add_watch_full() we can now let it run in the thread's own gcontext (if there is one).
Currently this patch does nothing alone. But when any of the incoming migration is run in another iothread (e.g., the upcoming migrate-recover command), this patch will bind the incoming logic to the iothread instead of the main thread (which may already get page faulted and hanged).
RDMA is not considered for now since it's not even using the QIO APIs at all.
--- migration/exec.c | 11 ++++++----- migration/fd.c | 11 ++++++----- migration/socket.c | 12 +++++++----- 3 files changed, 19 insertions(+), 15 deletions(-)
migration/exec.c +6 -5
@@ -55,6 +55,7 @@ void exec_start_incoming_migration(const char *command, Error **errp) {QIOChannel *ioc;const char *argv[] = {"/bin/sh" ,"-c" , command,NULL }; +GSource *source;trace_migration_exec_incoming (command); ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,
@@ -65,9 +66,9 @@ void exec_start_incoming_migration(const char *command, Error **errp) }qio_channel_set_name (ioc,"migration-exec-incoming" ); -qio_channel_add_watch (ioc, -G_IO_IN , - exec_accept_incoming_migration, -NULL , -NULL ); + source =qio_channel_add_watch_full (ioc,G_IO_IN , + exec_accept_incoming_migration, +NULL ,NULL , +g_main_context_get_thread_default ()); +g_source_unref (source); }
migration/fd.c +6 -5
@@ -55,6 +55,7 @@ void fd_start_incoming_migration(const char *infd, Error **errp) {QIOChannel *ioc;int fd; +GSource *source; fd =strtol (infd,NULL ,0 );trace_migration_fd_incoming (fd);
@@ -66,9 +67,9 @@ void fd_start_incoming_migration(const char *infd, Error **errp) }qio_channel_set_name (QIO_CHANNEL (ioc),"migration-fd-incoming" ); -qio_channel_add_watch (ioc, -G_IO_IN , - fd_accept_incoming_migration, -NULL , -NULL ); + source =qio_channel_add_watch_full (ioc,G_IO_IN , + fd_accept_incoming_migration, +NULL ,NULL , +g_main_context_get_thread_default ()); +g_source_unref (source); }
migration/socket.c +7 -5
@@ -164,6 +164,7 @@ static void socket_start_incoming_migration(SocketAddress *saddr, Error **errp) {QIOChannelSocket *listen_ioc =qio_channel_socket_new (); +GSource *source;qio_channel_set_name (QIO_CHANNEL (listen_ioc),"migration-socket-listener" );
@@ -173,11 +174,12 @@ static void socket_start_incoming_migration(SocketAddress *saddr,return ; } -qio_channel_add_watch (QIO_CHANNEL (listen_ioc), -G_IO_IN , - socket_accept_incoming_migration, - listen_ioc, - (GDestroyNotify )object_unref); + source =qio_channel_add_watch_full (QIO_CHANNEL (listen_ioc),G_IO_IN , + socket_accept_incoming_migration, + listen_ioc, + (GDestroyNotify )object_unref, +g_main_context_get_thread_default ()); +g_source_unref (source); }void tcp_start_incoming_migration (const char *host_port,Error **errp)
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 04/14] migration: let incoming side use thread context
Quoted 4 lines · Peter Xu
The old incoming migration is running in main thread and default gcontext. With the new
qio_channel_add_watch_full()we can now let it run in the thread's own gcontext (if there is one).
Quoted 8 lines
Currently this patch does nothing alone. But when any of the incoming migration is run in another iothread (e.g., the upcoming migrate-recover command), this patch will bind the incoming logic to the iothread instead of the main thread (which may already get page faulted and hanged).
RDMA is not considered for now since it's not even using the QIO APIs at all.
Errm, yes, it is.
struct QIOChannelRDMA {
QIOChannel parent;
RDMAContext *rdma;
QEMUFile *file;
size_t len;
bool blocking; /* XXX we don't actually honour this yet */
};
....Quoted 98 lines
CC Juan Quintela <[email protected]>CC Dr. David Alan Gilbert <[email protected]>CC Laurent Vivier <[email protected]>Signed-off-by Peter Xu <[email protected]>--- migration/exec.c | 11 ++++++----- migration/fd.c | 11 ++++++----- migration/socket.c | 12 +++++++----- 3 files changed, 19 insertions(+), 15 deletions(-)migration/exec.c +6
-5@@ -55,6 +55,7 @@ void exec_start_incoming_migration(const char *command, Error **errp) {QIOChannel *ioc;const char *argv[] = {"/bin/sh" ,"-c" , command,NULL }; +GSource *source;trace_migration_exec_incoming (command); ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,@@ -65,9 +66,9 @@ void exec_start_incoming_migration(const char *command, Error **errp) }qio_channel_set_name (ioc,"migration-exec-incoming" ); -qio_channel_add_watch (ioc, -G_IO_IN , - exec_accept_incoming_migration, -NULL , -NULL ); + source =qio_channel_add_watch_full (ioc,G_IO_IN , + exec_accept_incoming_migration, +NULL ,NULL , +g_main_context_get_thread_default ()); +g_source_unref (source); }migration/fd.c +6
-5@@ -55,6 +55,7 @@ void fd_start_incoming_migration(const char *infd, Error **errp) {QIOChannel *ioc;int fd; +GSource *source; fd =strtol (infd,NULL ,0 );trace_migration_fd_incoming (fd);@@ -66,9 +67,9 @@ void fd_start_incoming_migration(const char *infd, Error **errp) }qio_channel_set_name (QIO_CHANNEL (ioc),"migration-fd-incoming" ); -qio_channel_add_watch (ioc, -G_IO_IN , - fd_accept_incoming_migration, -NULL , -NULL ); + source =qio_channel_add_watch_full (ioc,G_IO_IN , + fd_accept_incoming_migration, +NULL ,NULL , +g_main_context_get_thread_default ()); +g_source_unref (source); }migration/socket.c +7
-5@@ -164,6 +164,7 @@ static void socket_start_incoming_migration(SocketAddress *saddr, Error **errp) {QIOChannelSocket *listen_ioc =qio_channel_socket_new (); +GSource *source;qio_channel_set_name (QIO_CHANNEL (listen_ioc),"migration-socket-listener" );@@ -173,11 +174,12 @@ static void socket_start_incoming_migration(SocketAddress *saddr,return ; } -qio_channel_add_watch (QIO_CHANNEL (listen_ioc), -G_IO_IN , - socket_accept_incoming_migration, - listen_ioc, - (GDestroyNotify )object_unref); + source =qio_channel_add_watch_full (QIO_CHANNEL (listen_ioc),G_IO_IN , + socket_accept_incoming_migration, + listen_ioc, + (GDestroyNotify )object_unref, +g_main_context_get_thread_default ()); +g_source_unref (source); }void tcp_start_incoming_migration (const char *host_port,Error **errp)Signature · 1 line
2.14.3
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 04/14] migration: let incoming side use thread context
Quoted 16 lines · Daniel P. Berrangé
Quoted 4 lines · Peter Xu
The old incoming migration is running in main thread and default gcontext. With the new
qio_channel_add_watch_full()we can now let it run in the thread's own gcontext (if there is one).Quoted 8 lines
Currently this patch does nothing alone. But when any of the incoming migration is run in another iothread (e.g., the upcoming migrate-recover command), this patch will bind the incoming logic to the iothread instead of the main thread (which may already get page faulted and hanged).
RDMA is not considered for now since it's not even using the QIO APIs at all.
Errm, yes, it is.
Quoted 8 lines
struct QIOChannelRDMA {
QIOChannel parent;
RDMAContext *rdma;
QEMUFile *file;
size_t len;
bool blocking; /* XXX we don't actually honour this yet */
};
....Ah, you are right. :)
I should say that "it's not using QIO watch framework" since it's using qemu_set_fd_handler() so it's always on main thread.
Thanks,
Signature · 1 line
Peter Xu
Re: [Qemu-devel] [PATCH 04/14] migration: let incoming side use thread context
In reply to Peter XuQuoted 9 lines · * Peter Xu ([email protected]) wrote:
The old incoming migration is running in main thread and default gcontext. With the new
qio_channel_add_watch_full()we can now let it run in the thread's own gcontext (if there is one).Currently this patch does nothing alone. But when any of the incoming migration is run in another iothread (e.g., the upcoming migrate-recover command), this patch will bind the incoming logic to the iothread instead of the main thread (which may already get page faulted and hanged).
Does this make any difference to the Postcopy listener thread, which takes over reading from the main thread once in postcopy mode? (See savevm.c:postcopy_ram_listen_thread).
Dave
Quoted 100 lines
RDMA is not considered for now since it's not even using the QIO APIs at all.
CC Juan Quintela <[email protected]>CC Dr. David Alan Gilbert <[email protected]>CC Laurent Vivier <[email protected]>Signed-off-by Peter Xu <[email protected]>--- migration/exec.c | 11 ++++++----- migration/fd.c | 11 ++++++----- migration/socket.c | 12 +++++++----- 3 files changed, 19 insertions(+), 15 deletions(-)migration/exec.c +6
-5@@ -55,6 +55,7 @@ void exec_start_incoming_migration(const char *command, Error **errp) {QIOChannel *ioc;const char *argv[] = {"/bin/sh" ,"-c" , command,NULL }; +GSource *source;trace_migration_exec_incoming (command); ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,@@ -65,9 +66,9 @@ void exec_start_incoming_migration(const char *command, Error **errp) }qio_channel_set_name (ioc,"migration-exec-incoming" ); -qio_channel_add_watch (ioc, -G_IO_IN , - exec_accept_incoming_migration, -NULL , -NULL ); + source =qio_channel_add_watch_full (ioc,G_IO_IN , + exec_accept_incoming_migration, +NULL ,NULL , +g_main_context_get_thread_default ()); +g_source_unref (source); }migration/fd.c +6
-5@@ -55,6 +55,7 @@ void fd_start_incoming_migration(const char *infd, Error **errp) {QIOChannel *ioc;int fd; +GSource *source; fd =strtol (infd,NULL ,0 );trace_migration_fd_incoming (fd);@@ -66,9 +67,9 @@ void fd_start_incoming_migration(const char *infd, Error **errp) }qio_channel_set_name (QIO_CHANNEL (ioc),"migration-fd-incoming" ); -qio_channel_add_watch (ioc, -G_IO_IN , - fd_accept_incoming_migration, -NULL , -NULL ); + source =qio_channel_add_watch_full (ioc,G_IO_IN , + fd_accept_incoming_migration, +NULL ,NULL , +g_main_context_get_thread_default ()); +g_source_unref (source); }migration/socket.c +7
-5@@ -164,6 +164,7 @@ static void socket_start_incoming_migration(SocketAddress *saddr, Error **errp) {QIOChannelSocket *listen_ioc =qio_channel_socket_new (); +GSource *source;qio_channel_set_name (QIO_CHANNEL (listen_ioc),"migration-socket-listener" );@@ -173,11 +174,12 @@ static void socket_start_incoming_migration(SocketAddress *saddr,return ; } -qio_channel_add_watch (QIO_CHANNEL (listen_ioc), -G_IO_IN , - socket_accept_incoming_migration, - listen_ioc, - (GDestroyNotify )object_unref); + source =qio_channel_add_watch_full (QIO_CHANNEL (listen_ioc),G_IO_IN , + socket_accept_incoming_migration, + listen_ioc, + (GDestroyNotify )object_unref, +g_main_context_get_thread_default ()); +g_source_unref (source); }void tcp_start_incoming_migration (const char *host_port,Error **errp)Signature · 1 line
2.14.3
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
Re: [Qemu-devel] [PATCH 04/14] migration: let incoming side use thread context
Quoted 11 lines · Dr. David Alan Gilbert
Quoted 9 lines · * Peter Xu ([email protected]) wrote:
The old incoming migration is running in main thread and default gcontext. With the new
qio_channel_add_watch_full()we can now let it run in the thread's own gcontext (if there is one).Currently this patch does nothing alone. But when any of the incoming migration is run in another iothread (e.g., the upcoming migrate-recover command), this patch will bind the incoming logic to the iothread instead of the main thread (which may already get page faulted and hanged).
Quoted 3 lines
Does this make any difference to the Postcopy listener thread, which takes over reading from the main thread once in postcopy mode? (See savevm.c:postcopy_ram_listen_thread).
It should not. It should only affect when use sends a "migrate-recover" with "run-oob=true". The rest should be the same as before. And since the postcopy ram load thread is a standalone thread with its own initial thread stack (so it's not really in a gmainloop), I can hardly tell how that can be affected since it'll always use its own thread stack.
Or, have I missed anything?
Signature · 1 line
Peter Xu
Re: [Qemu-devel] [PATCH 04/14] migration: let incoming side use thread context
Quoted 16 lines · * Peter Xu ([email protected]) wrote:
Quoted 11 lines · Dr. David Alan Gilbert
Quoted 9 lines · * Peter Xu ([email protected]) wrote:
The old incoming migration is running in main thread and default gcontext. With the new
qio_channel_add_watch_full()we can now let it run in the thread's own gcontext (if there is one).Currently this patch does nothing alone. But when any of the incoming migration is run in another iothread (e.g., the upcoming migrate-recover command), this patch will bind the incoming logic to the iothread instead of the main thread (which may already get page faulted and hanged).
Quoted 3 lines
Does this make any difference to the Postcopy listener thread, which takes over reading from the main thread once in postcopy mode? (See savevm.c:postcopy_ram_listen_thread).
Quoted 6 lines
It should not. It should only affect when use sends a "migrate-recover" with "run-oob=true". The rest should be the same as before. And since the postcopy ram load thread is a standalone thread with its own initial thread stack (so it's not really in a gmainloop), I can hardly tell how that can be affected since it'll always use its own thread stack.
OK, I think that's the bit I was worrying about; just since it was another thread that ended up reading from the fd originally handled by the incoming side main thread.
Dave
Quoted 4 lines
Or, have I missed anything?
Signature · 1 line
Peter Xu
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
PATCH 5/14 qio: refactor net listener source operations
In reply to Peter XuThree functions are abstracted from the old code:
- qio_net_listener_source_add(): create one source for listener
- qio_net_listener_sources_clear(): unset existing net lister sources
- qio_net_listener_sources_update(): setup all sources for listener
Use them where possible.
--- io/net-listener.c | 82 +++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 41 deletions(-)
io/net-listener.c +41 -41
@@ -106,6 +106,39 @@ int qio_net_listener_open_sync(QIONetListener *listener, } } +static guint qio_net_listener_source_add (QIONetListener *listener, +QIOChannelSocket *sioc) +{ +return qio_channel_add_watch (QIO_CHANNEL (sioc),G_IO_IN , + qio_net_listener_channel_func, + listener, (GDestroyNotify )object_unref); +} + +static void qio_net_listener_sources_clear (QIONetListener *listener) +{ +size_t i; + +for (i =0 ; i < listener->nsioc; i++) { +if (listener->io_tag[i]) { +g_source_remove (listener->io_tag[i]); + listener->io_tag[i] =0 ; + } + } +} + +static void qio_net_listener_sources_update (QIONetListener *listener) +{ +size_t i; + +if (listener->io_func !=NULL ) { +for (i =0 ; i < listener->nsioc; i++) { +assert (listener->io_tag[i] ==0 ); +object_ref (OBJECT (listener)); + listener->io_tag[i] =qio_net_listener_source_add ( + listener, listener->sioc[i]); + } + } +}void qio_net_listener_add (QIONetListener *listener,QIOChannelSocket *sioc)
@@ -127,10 +160,8 @@ void qio_net_listener_add(QIONetListener *listener,if (listener->io_func !=NULL ) {object_ref (OBJECT (listener)); - listener->io_tag[listener->nsioc] =qio_channel_add_watch ( -QIO_CHANNEL (listener->sioc[listener->nsioc]),G_IO_IN , - qio_net_listener_channel_func, - listener, (GDestroyNotify )object_unref); + listener->io_tag[listener->nsioc] =qio_net_listener_source_add ( + listener, listener->sioc[listener->nsioc]); } listener->nsioc++;
@@ -142,8 +173,6 @@ void qio_net_listener_set_client_func(QIONetListener *listener, gpointer data, GDestroyNotify notify) { - size_t i; - if (listener->io_notify) { listener->io_notify (listener->io_data); }
@@ -151,22 +180,8 @@ void qio_net_listener_set_client_func(QIONetListener *listener, listener->io_data = data; listener->io_notify = notify; -for (i =0 ; i < listener->nsioc; i++) { -if (listener->io_tag[i]) { -g_source_remove (listener->io_tag[i]); - listener->io_tag[i] =0 ; - } - } - -if (listener->io_func !=NULL ) { -for (i =0 ; i < listener->nsioc; i++) { -object_ref (OBJECT (listener)); - listener->io_tag[i] =qio_channel_add_watch ( -QIO_CHANNEL (listener->sioc[i]),G_IO_IN , - qio_net_listener_channel_func, - listener, (GDestroyNotify )object_unref); - } - } +qio_net_listener_sources_clear (listener); +qio_net_listener_sources_update (listener); }
@@ -210,12 +225,7 @@ QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener) };size_t i; -for (i =0 ; i < listener->nsioc; i++) { -if (listener->io_tag[i]) { -g_source_remove (listener->io_tag[i]); - listener->io_tag[i] =0 ; - } - } +qio_net_listener_sources_clear (listener); sources = g_new0(GSource *, listener->nsioc);for (i =0 ; i < listener->nsioc; i++) {
@@ -238,15 +248,7 @@ QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener)g_main_loop_unref (loop);g_main_context_unref (ctxt); -if (listener->io_func !=NULL ) { -for (i =0 ; i < listener->nsioc; i++) { -object_ref (OBJECT (listener)); - listener->io_tag[i] =qio_channel_add_watch ( -QIO_CHANNEL (listener->sioc[i]),G_IO_IN , - qio_net_listener_channel_func, - listener, (GDestroyNotify )object_unref); - } - } +qio_net_listener_sources_update (listener);return data.sioc; }
@@ -259,11 +261,9 @@ void qio_net_listener_disconnect(QIONetListener *listener)return ; } +qio_net_listener_sources_clear (listener); +for (i =0 ; i < listener->nsioc; i++) { -if (listener->io_tag[i]) { -g_source_remove (listener->io_tag[i]); - listener->io_tag[i] =0 ; - }qio_channel_close (QIO_CHANNEL (listener->sioc[i]),NULL ); } listener->connected = false;
Signature · 1 line
2.14.3
PATCH 6/14 qio: store gsources for net listeners
In reply to Peter XuOriginally we were storing the GSources tag IDs. That'll be not enough if we are going to support non-default gcontext for QIO code. Switch to GSources without changing anything real. Now we still always pass in NULL, which means the default gcontext.
--- include/io/net-listener.h | 21 ++++++++++++++-- io/net-listener.c | 62 +++++++++++++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 25 deletions(-)
include/io/net-listener.h +19 -2
@@ -53,7 +53,7 @@ struct QIONetListener {char *name;QIOChannelSocket **sioc; -gulong *io_tag; +GSource **io_source;size_t nsioc;bool connected;
@@ -120,17 +120,34 @@ void qio_net_listener_add(QIONetListener *listener, QIOChannelSocket *sioc);/** -* qio_net_listener_set_client_func: +* qio_net_listener_set_client_func_full: * @listener: the network listener object * @func: the callback function * @data: opaque data to pass to @func * @notify: callback to free @data +* @context: the context that the sources will be bound to * * Register @func to be invoked whenever a new client * connects to the listener. @func will be invoked * passing in the QIOChannelSocket instance for the * client. */ +void qio_net_listener_set_client_func_full (QIONetListener *listener, +QIONetListenerClientFunc func, +gpointer data, +GDestroyNotify notify, +GMainContext *context); + +/** +* qio_net_listener_set_client_func: +* @listener: the network listener object +* @func: the callback function +* @data: opaque data to pass to @func +* @notify: callback to free @data +* +* Wrapper of qio_net_listener_set_client_func_full(), only that the +* sources will always be bound to default main context. +*/ void qio_net_listener_set_client_func(QIONetListener *listener, QIONetListenerClientFunc func, gpointer data,
io/net-listener.c +39 -23
124 lines not on this page · raw message
Signature · 1 line
2.14.3
PATCH 7/14 qio/chardev: update net listener gcontext
In reply to Peter XuTCP chardevs can be using QIO network listeners working in the background when in listening mode. However the network listeners are always running in main context. This can race with chardevs that are running in non-main contexts.
To solve this: firstly introduce qio_net_listener_set_context() to allow caller to set gcontext for network listeners. Then call it in tcp_chr_update_read_handler(), with the newly cached gcontext.
It's fairly straightforward after we have introduced some net listener helper functions - basically we unregister the GSources and add them back with the correct context.
--- chardev/char-socket.c | 9 +++++++++ include/io/net-listener.h | 12 ++++++++++++ io/net-listener.c | 7 +++++++ 3 files changed, 28 insertions(+)
chardev/char-socket.c +9 -0
@@ -559,6 +559,15 @@ static void tcp_chr_update_read_handler(Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr); +if (s->listener) { +/* +* It's possible that chardev context is changed in +* qemu_chr_be_update_read_handlers(). Reset it for QIO net +* listener if there is. +*/ +qio_net_listener_set_context (s->listener, chr->gcontext); + } +if (!s->connected) {return ; }
include/io/net-listener.h +12 -0
@@ -106,6 +106,18 @@ int qio_net_listener_open_sync(QIONetListener *listener, SocketAddress *addr, Error **errp); +/** +* qio_net_listener_set_context: +* @listener: the net listener object +* @context: the context that we'd like to bind the sources to +* +* This helper does not do anything but moves existing net listener +* sources from the old one to the new one. It can be seen as a +* no-operation if there is no listening source at all. +*/ +void qio_net_listener_set_context (QIONetListener *listener, +GMainContext *context); +/** * qio_net_listener_add: * @listener: the network listener object
io/net-listener.c +7 -0
@@ -145,6 +145,13 @@ static void qio_net_listener_sources_update(QIONetListener *listener, } } +void qio_net_listener_set_context (QIONetListener *listener, +GMainContext *context) +{ +qio_net_listener_sources_clear (listener); +qio_net_listener_sources_update (listener, context); +} +void qio_net_listener_add (QIONetListener *listener,QIOChannelSocket *sioc) {
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 07/14] qio/chardev: update net listener gcontext
Quoted 49 lines · Peter Xu
TCP chardevs can be using QIO network listeners working in the background when in listening mode. However the network listeners are always running in main context. This can race with chardevs that are running in non-main contexts.
To solve this: firstly introduce
qio_net_listener_set_context()to allow caller to set gcontext for network listeners. Then call it intcp_chr_update_read_handler(), with the newly cached gcontext.It's fairly straightforward after we have introduced some net listener helper functions - basically we unregister the GSources and add them back with the correct context.
Signed-off-by Peter Xu <[email protected]>--- chardev/char-socket.c | 9 +++++++++ include/io/net-listener.h | 12 ++++++++++++ io/net-listener.c | 7 +++++++ 3 files changed, 28 insertions(+)chardev/char-socket.c +9
-0@@ -559,6 +559,15 @@ static void tcp_chr_update_read_handler(Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr); +if (s->listener) { +/* +* It's possible that chardev context is changed in +* qemu_chr_be_update_read_handlers(). Reset it for QIO net +* listener if there is. +*/ +qio_net_listener_set_context (s->listener, chr->gcontext); + } +if (!s->connected) {return ; }include/io/net-listener.h +1
-0@@ -106,6 +106,18 @@ int qio_net_listener_open_sync(QIONetListener *listener, SocketAddress *addr, Error **errp); +/**
include/io/net-listener.h int qio_net_listener_open_sync(QIONetListener *listener, · line 119
Quoted 10 lines
include/io/net-listener.h +10
-0+* qio_net_listener_set_context: +* @listener: the net listener object +* @context: the context that we'd like to bind the sources to +* +* This helper does not do anything but moves existing net listener +* sources from the old one to the new one. It can be seen as a +* no-operation if there is no listening source at all. +*/ +void qio_net_listener_set_context (QIONetListener *listener, +GMainContext *context);
I don't think this is a good design. The GMainContext should be provided to the qio_net_listener_set_client_func method immediately, not updated after the fact
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 07/14] qio/chardev: update net listener gcontext
Quoted 49 lines · Daniel P. Berrangé
Quoted 48 lines · Peter Xu
TCP chardevs can be using QIO network listeners working in the background when in listening mode. However the network listeners are always running in main context. This can race with chardevs that are running in non-main contexts.
To solve this: firstly introduce
qio_net_listener_set_context()to allow caller to set gcontext for network listeners. Then call it intcp_chr_update_read_handler(), with the newly cached gcontext.It's fairly straightforward after we have introduced some net listener helper functions - basically we unregister the GSources and add them back with the correct context.
Signed-off-by Peter Xu <[email protected]>--- chardev/char-socket.c | 9 +++++++++ include/io/net-listener.h | 12 ++++++++++++ io/net-listener.c | 7 +++++++ 3 files changed, 28 insertions(+)chardev/char-socket.c +9
-0@@ -559,6 +559,15 @@ static void tcp_chr_update_read_handler(Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr); +if (s->listener) { +/* +* It's possible that chardev context is changed in +* qemu_chr_be_update_read_handlers(). Reset it for QIO net +* listener if there is. +*/ +qio_net_listener_set_context (s->listener, chr->gcontext); + } +if (!s->connected) {return ; }include/io/net-listener.h +0
-0@@ -106,6 +106,18 @@ int qio_net_listener_open_sync(QIONetListener *listener, SocketAddress *addr, Error **errp);
Quoted 15 lines
include/io/net-listener.h int qio_net_listener_open_sync(QIONetListener *listener, · line 119
Quoted 11 lines
include/io/net-listener.h +11
-0+/** +* qio_net_listener_set_context: +* @listener: the net listener object +* @context: the context that we'd like to bind the sources to +* +* This helper does not do anything but moves existing net listener +* sources from the old one to the new one. It can be seen as a +* no-operation if there is no listening source at all. +*/ +void qio_net_listener_set_context (QIONetListener *listener, +GMainContext *context);I don't think this is a good design. The GMainContext should be provided to the
qio_net_listener_set_client_funcmethod immediately, not updated after the fact
After the patches, this is qio_net_listener_set_client_func_full():
void qio_net_listener_set_client_func_full(QIONetListener *listener,
QIONetListenerClientFunc func, gpointer data, GDestroyNotify notify, GMainContext *context)
{
if (listener->io_notify) {
listener->io_notify(listener->io_data);
}
listener->io_func = func;
listener->io_data = data;
listener->io_notify = notify;
qio_net_listener_sources_clear(listener);
qio_net_listener_sources_update(listener, context);
}This is qio_net_listener_set_context():
void qio_net_listener_set_context(QIONetListener *listener,
GMainContext *context)
{
qio_net_listener_sources_clear(listener);
qio_net_listener_sources_update(listener, context);
}So... Basically qio_net_listener_set_context() is just a simplified version of qio_net_listener_set_client_func_full(), but without passing in the funcs again. So do you mean that I can just avoid introducing this function and call qio_net_listener_set_client_func_full() directly?
Thanks,
Signature · 1 line
Peter Xu
Re: [Qemu-devel] [PATCH 07/14] qio/chardev: update net listener gcontext
Quoted 94 lines · Peter Xu
Quoted 49 lines · Daniel P. Berrangé
Quoted 48 lines · Peter Xu
TCP chardevs can be using QIO network listeners working in the background when in listening mode. However the network listeners are always running in main context. This can race with chardevs that are running in non-main contexts.
To solve this: firstly introduce
qio_net_listener_set_context()to allow caller to set gcontext for network listeners. Then call it intcp_chr_update_read_handler(), with the newly cached gcontext.It's fairly straightforward after we have introduced some net listener helper functions - basically we unregister the GSources and add them back with the correct context.
Signed-off-by Peter Xu <[email protected]>--- chardev/char-socket.c | 9 +++++++++ include/io/net-listener.h | 12 ++++++++++++ io/net-listener.c | 7 +++++++ 3 files changed, 28 insertions(+)chardev/char-socket.c +9
-0@@ -559,6 +559,15 @@ static void tcp_chr_update_read_handler(Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr); +if (s->listener) { +/* +* It's possible that chardev context is changed in +* qemu_chr_be_update_read_handlers(). Reset it for QIO net +* listener if there is. +*/ +qio_net_listener_set_context (s->listener, chr->gcontext); + } +if (!s->connected) {return ; }include/io/net-listener.h +0
-0@@ -106,6 +106,18 @@ int qio_net_listener_open_sync(QIONetListener *listener, SocketAddress *addr, Error **errp);Quoted 15 lines
include/io/net-listener.h int qio_net_listener_open_sync(QIONetListener *listener, · line 119
Quoted 11 lines
include/io/net-listener.h +11
-0+/** +* qio_net_listener_set_context: +* @listener: the net listener object +* @context: the context that we'd like to bind the sources to +* +* This helper does not do anything but moves existing net listener +* sources from the old one to the new one. It can be seen as a +* no-operation if there is no listening source at all. +*/ +void qio_net_listener_set_context (QIONetListener *listener, +GMainContext *context);I don't think this is a good design. The GMainContext should be provided to the
qio_net_listener_set_client_funcmethod immediately, not updated after the factAfter the patches, this is
qio_net_listener_set_client_func_full():void qio_net_listener_set_client_func_full(QIONetListener *listener,
QIONetListenerClientFunc func, gpointer data, GDestroyNotify notify, GMainContext *context){ if (listener->io_notify) { listener->io_notify(listener->io_data); } listener->io_func = func; listener->io_data = data; listener->io_notify = notify; qio_net_listener_sources_clear(listener); qio_net_listener_sources_update(listener, context); }This is
qio_net_listener_set_context():void qio_net_listener_set_context(QIONetListener *listener,
GMainContext *context){ qio_net_listener_sources_clear(listener); qio_net_listener_sources_update(listener, context); }
Quoted 5 lines
So... Basically
qio_net_listener_set_context()is just a simplified version ofqio_net_listener_set_client_func_full(), but without passing in the funcs again. So do you mean that I can just avoid introducing this function and callqio_net_listener_set_client_func_full()directly?
Yes, I don't see any reason to allow GMainContext to be changed separately from setting the callback functions. The caller can easily just call qio_net_listener_set_client_func_full() directly with the new GMainContext
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
PATCH 8/14 chardev: allow telnet gsource to switch gcontext
In reply to Peter XuIt was originally created by qio_channel_add_watch() so it's always assigning the task to main context. Now we use the new API called qio_channel_add_watch_full() so that we get the GSource handle rather than the tag ID.
Meanwhile, caching the gsource in SocketChardev.telnet_source so that we can also do dynamic context switch when update read handlers.
--- chardev/char-socket.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-)
chardev/char-socket.c +24 -4
@@ -59,6 +59,7 @@ typedef struct {bool is_listen;bool is_telnet;bool is_tn3270; +GSource *telnet_source;GSource *reconnect_timer;int64_t reconnect_time;
@@ -69,6 +70,7 @@ typedef struct { OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)static gboolean socket_reconnect_timeout (gpointer opaque); +static void tcp_chr_telnet_init (Chardev *chr);static void tcp_chr_reconn_timer_cancel (SocketChardev *s) {
@@ -555,6 +557,15 @@ static void tcp_chr_connect(void *opaque)qemu_chr_be_event (chr,CHR_EVENT_OPENED ); } +static void tcp_chr_telnet_destroy (SocketChardev *s) +{ +if (s->telnet_source) { +g_source_destroy (s->telnet_source); +g_source_unref (s->telnet_source); + s->telnet_source =NULL ; + } +} +static void tcp_chr_update_read_handler (Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr);
@@ -568,6 +579,11 @@ static void tcp_chr_update_read_handler(Chardev *chr)qio_net_listener_set_context (s->listener, chr->gcontext); } +if (s->telnet_source) { +tcp_chr_telnet_destroy (s); +tcp_chr_telnet_init (CHARDEV (s)); + } +if (!s->connected) {return ; }
@@ -592,6 +608,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, gpointer user_data) {TCPChardevTelnetInit *init = user_data; +SocketChardev *s =SOCKET_CHARDEV (init->chr);ssize_t ret; ret =qio_channel_write (ioc, init->buf, init->buflen,NULL );
@@ -616,6 +633,8 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, end:g_free (init); +g_source_unref (s->telnet_source); + s->telnet_source =NULL ;return G_SOURCE_REMOVE ; }
@@ -655,10 +674,10 @@ static void tcp_chr_telnet_init(Chardev *chr)#undef IACSET -qio_channel_add_watch ( - s->ioc,G_IO_OUT , - tcp_chr_telnet_init_io, - init,NULL ); + s->telnet_source =qio_channel_add_watch_full (s->ioc,G_IO_OUT , + tcp_chr_telnet_init_io, + init,NULL , + chr->gcontext); }
@@ -831,6 +850,7 @@ static void char_socket_finalize(Object *obj)tcp_chr_free_connection (chr);tcp_chr_reconn_timer_cancel (s);qapi_free_SocketAddress (s->addr); +tcp_chr_telnet_destroy (s);if (s->listener) {qio_net_listener_set_client_func (s->listener,NULL ,NULL ,NULL );object_unref (OBJECT (s->listener));
Signature · 1 line
2.14.3
PATCH 9/14 qio: basic non-default context support for thread
In reply to Peter Xuqio_task_run_in_thread() allows main thread to run blocking operations in the background. However it has an assumption on that it's always working with the default context. This patch tries to allow the QIO task framework to run with non-default gcontext.
Currently no functional change so far, so the QIOTasks are still always running on main context.
--- include/io/task.h | 6 ++++-- io/channel-socket.c | 9 ++++++--- io/dns-resolver.c | 3 ++- io/task.c | 28 ++++++++++++++++++++++++++-- tests/test-io-task.c | 2 ++ 5 files changed, 40 insertions(+), 8 deletions(-)
include/io/task.h +4 -2
@@ -227,15 +227,17 @@ QIOTask *qio_task_new(Object *source,* @worker: the function to invoke in a thread * @opaque: opaque data to pass to @worker * @destroy: function to free @opaque +* @context: the context to run the complete hook * * Run a task in a background thread. When @worker * returns it will call qio_task_complete() in -* the main event thread context. +* the event thread context that provided. */ void qio_task_run_in_thread (QIOTask *task,QIOTaskWorker worker,gpointer opaque, -GDestroyNotify destroy); +GDestroyNotify destroy, +GMainContext *context);/** * qio_task_complete:
io/channel-socket.c +6 -3
@@ -187,7 +187,8 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc,qio_task_run_in_thread (task, qio_channel_socket_connect_worker, addrCopy, - (GDestroyNotify )qapi_free_SocketAddress); + (GDestroyNotify )qapi_free_SocketAddress, +NULL ); }
@@ -245,7 +246,8 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc,qio_task_run_in_thread (task, qio_channel_socket_listen_worker, addrCopy, - (GDestroyNotify )qapi_free_SocketAddress); + (GDestroyNotify )qapi_free_SocketAddress, +NULL ); }
@@ -321,7 +323,8 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,qio_task_run_in_thread (task, qio_channel_socket_dgram_worker, data, - qio_channel_socket_dgram_worker_free); + qio_channel_socket_dgram_worker_free, +NULL ); }
io/dns-resolver.c +2 -1
@@ -233,7 +233,8 @@ void qio_dns_resolver_lookup_async(QIODNSResolver *resolver,qio_task_run_in_thread (task, qio_dns_resolver_lookup_worker, data, - qio_dns_resolver_lookup_data_free); + qio_dns_resolver_lookup_data_free, +NULL ); }
io/task.c +26 -2
@@ -32,6 +32,10 @@ struct QIOTask {Error *err;gpointer result;GDestroyNotify destroyResult; + +/* Threaded QIO task specific fields */ +GSource *idle_source;/* The idle task to run complete routine */ +GMainContext *context;/* The context that idle task will run with */ };
@@ -49,6 +53,7 @@ QIOTask *qio_task_new(Object *source, task->func = func; task->opaque = opaque; task->destroy = destroy; + task->idle_source =NULL ;trace_qio_task_new (task, source, func, opaque);
@@ -66,6 +71,12 @@ static void qio_task_free(QIOTask *task)if (task->err) {error_free (task->err); } +if (task->idle_source) { +g_source_unref (task->idle_source); + } +if (task->context) { +g_main_context_unref (task->context); + }object_unref (task->source);g_free (task);
@@ -100,6 +111,8 @@ static gboolean qio_task_thread_result(gpointer opaque)static gpointer qio_task_thread_worker (gpointer opaque) {struct QIOTaskThreadData *data = opaque; +QIOTask *task = data->task; +GSource *idle;trace_qio_task_thread_run (data->task); data->worker (data->task, data->opaque);
@@ -110,7 +123,12 @@ static gpointer qio_task_thread_worker(gpointer opaque)* the worker results */ trace_qio_task_thread_exit (data->task); -g_idle_add (qio_task_thread_result, data); + + idle =g_idle_source_new (); +g_source_set_callback (idle, qio_task_thread_result, data,NULL ); +g_source_attach (idle, task->context); + task->idle_source = idle; +return NULL ; }
@@ -118,11 +136,17 @@ static gpointer qio_task_thread_worker(gpointer opaque)void qio_task_run_in_thread (QIOTask *task,QIOTaskWorker worker,gpointer opaque, -GDestroyNotify destroy) +GDestroyNotify destroy, +GMainContext *context) { struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1);QemuThread thread; +if (context) { +g_main_context_ref (context); + task->context = context; + } + data->task = task; data->worker = worker; data->opaque = opaque;
tests/test-io-task.c +2 -0
@@ -187,6 +187,7 @@ static void test_task_thread_complete(void)qio_task_run_in_thread (task, test_task_thread_worker, &data, +NULL ,NULL );g_main_loop_run (data.loop);
@@ -228,6 +229,7 @@ static void test_task_thread_failure(void)qio_task_run_in_thread (task, test_task_thread_worker, &data, +NULL ,NULL );g_main_loop_run (data.loop);
Signature · 1 line
2.14.3
PATCH 10/14 qio: refcount QIOTask
In reply to Peter XuIt will be used in multiple threads in follow-up patches. Let it start to have refcounts.
--- include/io/task.h | 3 +++ io/task.c | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-)
include/io/task.h +3 -0
@@ -322,4 +322,7 @@ gpointer qio_task_get_result_pointer(QIOTask *task);*/ Object *qio_task_get_source (QIOTask *task); +void qio_task_ref (QIOTask *task); +void qio_task_unref (QIOTask *task); +#endif /* QIO_TASK_H */
io/task.c +22 -1
@@ -32,6 +32,7 @@ struct QIOTask {Error *err;gpointer result;GDestroyNotify destroyResult; +uint32_t refcount;/* Threaded QIO task specific fields */ GSource *idle_source;/* The idle task to run complete routine */
@@ -57,6 +58,8 @@ QIOTask *qio_task_new(Object *source,trace_qio_task_new (task, source, func, opaque); +qio_task_ref (task); +return task; }
@@ -165,7 +168,7 @@ void qio_task_complete(QIOTask *task) { task->func (task, task->opaque);trace_qio_task_complete (task); -qio_task_free (task); +qio_task_unref (task); }
@@ -208,3 +211,21 @@ Object *qio_task_get_source(QIOTask *task) {return task->source; } + +void qio_task_ref (QIOTask *task) +{ +if (!task) { +return ; + } +atomic_inc (&task->refcount); +} + +void qio_task_unref (QIOTask *task) +{ +if (!task) { +return ; + } +if (atomic_fetch_dec (&task->refcount) ==1 ) { +qio_task_free (task); + } +}
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 10/14] qio: refcount QIOTask
Quoted 14 lines · Peter Xu
It will be used in multiple threads in follow-up patches. Let it start to have refcounts.
Signed-off-by Peter Xu <[email protected]>--- include/io/task.h | 3 +++ io/task.c | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-)include/io/task.h +0
-0
include/io/task.h gpointer qio_task_get_result_pointer(QIOTask *task); · line 326
Quoted 5 lines
include/io/task.h +2
-0@@ -322,4 +322,7 @@ gpointer qio_task_get_result_pointer(QIOTask *task);*/ Object *qio_task_get_source (QIOTask *task); +void qio_task_ref (QIOTask *task); +void qio_task_unref (QIOTask *task);
It should just be turned back into a QObject as it was originally, so we get refcounting for free.
Quoted 55 lines
io/task.c +22
-1@@ -32,6 +32,7 @@ struct QIOTask {Error *err;gpointer result;GDestroyNotify destroyResult; +uint32_t refcount;/* Threaded QIO task specific fields */ GSource *idle_source;/* The idle task to run complete routine */ @@ -57,6 +58,8 @@ QIOTask *qio_task_new(Object *source,trace_qio_task_new (task, source, func, opaque); +qio_task_ref (task); +return task; }@@ -165,7 +168,7 @@ void qio_task_complete(QIOTask *task) { task->func (task, task->opaque);trace_qio_task_complete (task); -qio_task_free (task); +qio_task_unref (task); }@@ -208,3 +211,21 @@ Object *qio_task_get_source(QIOTask *task) {return task->source; } + +void qio_task_ref (QIOTask *task) +{ +if (!task) { +return ; + } +atomic_inc (&task->refcount); +} + +void qio_task_unref (QIOTask *task) +{ +if (!task) { +return ; + } +if (atomic_fetch_dec (&task->refcount) ==1 ) { +qio_task_free (task); + } +}Signature · 1 line
2.14.3
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 10/14] qio: refcount QIOTask
Quoted 18 lines · Daniel P. Berrangé
Quoted 17 lines · Peter Xu
It will be used in multiple threads in follow-up patches. Let it start to have refcounts.
Signed-off-by Peter Xu <[email protected]>--- include/io/task.h | 3 +++ io/task.c | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-)include/io/task.h +0
-0@@ -322,4 +322,7 @@ gpointer qio_task_get_result_pointer(QIOTask *task);*/ Object *qio_task_get_source (QIOTask *task);
Quoted 5 lines
include/io/task.h gpointer qio_task_get_result_pointer(QIOTask *task); · line 326
Quoted 2 lines
include/io/task.h +2
-0+void qio_task_ref (QIOTask *task); +void qio_task_unref (QIOTask *task);It should just be turned back into a QObject as it was originally, so we get refcounting for free.
Could you point me the commit that has the original code? I would be glad to revert to that, or yeah I can switch to QObject too. Thanks,
Signature · 1 line
Peter Xu
Re: [Qemu-devel] [PATCH 10/14] qio: refcount QIOTask
Quoted 19 lines · Peter Xu
Quoted 18 lines · Daniel P. Berrangé
Quoted 17 lines · Peter Xu
It will be used in multiple threads in follow-up patches. Let it start to have refcounts.
Signed-off-by Peter Xu <[email protected]>--- include/io/task.h | 3 +++ io/task.c | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-)include/io/task.h +0
-0@@ -322,4 +322,7 @@ gpointer qio_task_get_result_pointer(QIOTask *task);*/ Object *qio_task_get_source (QIOTask *task);
Quoted 8 lines
Quoted 5 lines
include/io/task.h gpointer qio_task_get_result_pointer(QIOTask *task); · line 326
Quoted 2 lines
include/io/task.h +2
-0+void qio_task_ref (QIOTask *task); +void qio_task_unref (QIOTask *task);It should just be turned back into a QObject as it was originally, so we get refcounting for free.
Could you point me the commit that has the original code? I would be glad to revert to that, or yeah I can switch to QObject too. Thanks,
It was never actually committed - it was just that way during initial review but was suggested to be simplified as ref counting wasn't needed.
That all said, having now seen the later parts of this patch series, I don't think we would need todo this at all, because we should not be exposing the GTask to callers and thus the refcounting question goes away
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 10/14] qio: refcount QIOTask
Quoted 29 lines · Daniel P. Berrangé
Quoted 19 lines · Peter Xu
Quoted 18 lines · Daniel P. Berrangé
Quoted 17 lines · Peter Xu
It will be used in multiple threads in follow-up patches. Let it start to have refcounts.
Signed-off-by Peter Xu <[email protected]>--- include/io/task.h | 3 +++ io/task.c | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-)include/io/task.h +0
-0@@ -322,4 +322,7 @@ gpointer qio_task_get_result_pointer(QIOTask *task);*/ Object *qio_task_get_source (QIOTask *task);Quoted 8 lines
Quoted 5 lines
include/io/task.h gpointer qio_task_get_result_pointer(QIOTask *task); · line 326
Quoted 2 lines
include/io/task.h +2
-0+void qio_task_ref (QIOTask *task); +void qio_task_unref (QIOTask *task);It should just be turned back into a QObject as it was originally, so we get refcounting for free.
Could you point me the commit that has the original code? I would be glad to revert to that, or yeah I can switch to QObject too. Thanks,
Quoted 6 lines
It was never actually committed - it was just that way during initial review but was suggested to be simplified as ref counting wasn't needed.
That all said, having now seen the later parts of this patch series, I don't think we would need todo this at all, because we should not be exposing the GTask to callers and thus the refcounting question goes away
Indeed. I suppose that means you like my proposal in the other thread. But I'll wait for your explicit acknowledgement in that too.
Thanks,
Signature · 1 line
Peter Xu
PATCH 11/14 qio/chardev: return QIOTask when connect async
In reply to Peter XuLet qio_channel_socket_connect_async() return the created QIOTask object for the async connection. In tcp chardev, cache that in SocketChardev for further use. With the QIOTask refcount, this is pretty safe.
Since at it, generalize out tcp_chr_socket_connect_async() since the logic is used in both initial phase and reconnect timeout.
--- chardev/char-socket.c | 33 ++++++++++++++++++++++----------- include/io/channel-socket.h | 14 +++++++++----- io/channel-socket.c | 12 +++++++----- 3 files changed, 38 insertions(+), 21 deletions(-)
chardev/char-socket.c +22 -11
@@ -64,6 +64,7 @@ typedef struct {GSource *reconnect_timer;int64_t reconnect_time;bool connect_err_reported; +QIOTask *thread_task; } SocketChardev;#define SOCKET_CHARDEV (obj) \
@@ -879,14 +880,32 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque)tcp_chr_new_client (chr, sioc); cleanup: +assert (s->thread_task == task); +qio_task_unref (task); + s->thread_task =NULL ;object_unref (OBJECT (sioc)); } +static void tcp_chr_socket_connect_async (SocketChardev *s) +{ +QIOChannelSocket *sioc =qio_channel_socket_new (); +Chardev *chr =CHARDEV (s); +QIOTask *task; + +assert (s->thread_task ==NULL ); + +tcp_chr_set_client_ioc_name (chr, sioc); + task =qio_channel_socket_connect_async (sioc, s->addr, + qemu_chr_socket_connected, + chr,NULL ); +qio_task_ref (task); + s->thread_task = task; +} +static gboolean socket_reconnect_timeout (gpointer opaque) {Chardev *chr =CHARDEV (opaque);SocketChardev *s =SOCKET_CHARDEV (opaque); -QIOChannelSocket *sioc;g_source_unref (s->reconnect_timer); s->reconnect_timer =NULL ;
@@ -895,11 +914,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)return false; } - sioc =qio_channel_socket_new (); -tcp_chr_set_client_ioc_name (chr, sioc); -qio_channel_socket_connect_async (sioc, s->addr, - qemu_chr_socket_connected, - chr,NULL ); +tcp_chr_socket_connect_async (s);return false; }
@@ -979,11 +994,7 @@ static void qmp_chardev_open_socket(Chardev *chr, }if (s->reconnect_time) { - sioc =qio_channel_socket_new (); -tcp_chr_set_client_ioc_name (chr, sioc); -qio_channel_socket_connect_async (sioc, s->addr, - qemu_chr_socket_connected, - chr,NULL ); +tcp_chr_socket_connect_async (s); }else {if (s->is_listen) {char *name;
include/io/channel-socket.h +9 -5
@@ -108,12 +108,16 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc,* will be invoked on completion or failure. The @addr * parameter will be copied, so may be freed as soon * as this function returns without waiting for completion. +* +* Returns the IOTask created. NOTE: if the caller is going to use +* the returned QIOTask, the caller is responsible to reference the +* task and unref it when it's not needed any more. */ -void qio_channel_socket_connect_async (QIOChannelSocket *ioc, -SocketAddress *addr, -QIOTaskFunc callback, -gpointer opaque, -GDestroyNotify destroy); +QIOTask *qio_channel_socket_connect_async (QIOChannelSocket *ioc, +SocketAddress *addr, +QIOTaskFunc callback, +gpointer opaque, +GDestroyNotify destroy);/**
io/channel-socket.c +7 -5
@@ -169,11 +169,11 @@ static void qio_channel_socket_connect_worker(QIOTask *task, } -void qio_channel_socket_connect_async (QIOChannelSocket *ioc, -SocketAddress *addr, -QIOTaskFunc callback, -gpointer opaque, -GDestroyNotify destroy) +QIOTask *qio_channel_socket_connect_async (QIOChannelSocket *ioc, +SocketAddress *addr, +QIOTaskFunc callback, +gpointer opaque, +GDestroyNotify destroy) {QIOTask *task =qio_task_new (OBJECT (ioc), callback, opaque, destroy);
@@ -189,6 +189,8 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc, addrCopy, (GDestroyNotify )qapi_free_SocketAddress, NULL); + +return task; }
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 11/14] qio/chardev: return QIOTask when connect async
Quoted 3 lines · Peter Xu
Let
qio_channel_socket_connect_async()return the created QIOTask object for the async connection. In tcp chardev, cache that in SocketChardev for further use. With the QIOTask refcount, this is pretty safe.
Why do you want to return QIOTask ? This is going against the intended design pattern for QIOTask (that was based on that in GLib). The task supposed to be an internal use only helper that callers should never be touching until the completion callback is invoked.
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 11/14] qio/chardev: return QIOTask when connect async
Quoted 9 lines · Daniel P. Berrangé
Quoted 3 lines · Peter Xu
Let
qio_channel_socket_connect_async()return the created QIOTask object for the async connection. In tcp chardev, cache that in SocketChardev for further use. With the QIOTask refcount, this is pretty safe.Why do you want to return QIOTask ? This is going against the intended design pattern for QIOTask (that was based on that in GLib). The task supposed to be an internal use only helper that callers should never be touching until the completion callback is invoked.
I proposed another solution in the other comment reply to split the threaded QIO task into create() and run(). If you like that, I can try. Any other suggestion would be welcomed too. Thanks,
Signature · 1 line
Peter Xu
PATCH 12/14 qio: move QIOTaskThreadData into QIOTask
In reply to Peter XuThe major reason to do this is that, after the upper level can cache the QIOTask, it should also be able to further manage the QIOTask. And, it can't if it does not have the information in QIOTaskThreadData. So let's just merge this struct with QIOTask. Actually by doing this, it'll simplify the code a bit too.
This will be needed in the next patch, when we want to rebuild the completion GSource when the GMainContext changed.
--- io/task.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-)
io/task.c +26 -20
@@ -24,6 +24,13 @@#include "qemu/thread.h" #include "trace.h" +struct QIOTaskThreadData { +QIOTaskWorker worker; +gpointer opaque; +GDestroyNotify destroy; +}; +typedef struct QIOTaskThreadData QIOTaskThreadData ; +struct QIOTask {Object *source;QIOTaskFunc func;
@@ -37,6 +44,7 @@ struct QIOTask {/* Threaded QIO task specific fields */ GSource *idle_source;/* The idle task to run complete routine */ GMainContext *context;/* The context that idle task will run with */ +QIOTaskThreadData thread_data; };
@@ -86,26 +94,25 @@ static void qio_task_free(QIOTask *task) } -struct QIOTaskThreadData { -QIOTask *task; -QIOTaskWorker worker; -gpointer opaque; -GDestroyNotify destroy; -}; - -static gboolean qio_task_thread_result (gpointer opaque) { -struct QIOTaskThreadData *data = opaque; +QIOTask *task = opaque; +QIOTaskThreadData *data = &task->thread_data; -trace_qio_task_thread_result (data->task); -qio_task_complete (data->task); +/* +* Take one more refcount since qio_task_complete() may otherwise +* release the last refcount and free, then "data" may be invalid. +*/ +qio_task_ref (task); + +trace_qio_task_thread_result (task); +qio_task_complete (task);if (data->destroy) { data->destroy (data->opaque); } -g_free (data); +qio_task_unref (task);return FALSE; }
@@ -113,19 +120,19 @@ static gboolean qio_task_thread_result(gpointer opaque)static gpointer qio_task_thread_worker (gpointer opaque) { -struct QIOTaskThreadData *data = opaque; -QIOTask *task = data->task; +QIOTask *task = opaque; +QIOTaskThreadData *data = &task->thread_data;GSource *idle; -trace_qio_task_thread_run (data->task); - data->worker (data->task, data->opaque); +trace_qio_task_thread_run (task); + data->worker (task, data->opaque);/* We're running in the background thread, and must only * ever report the task results in the main event loop * thread. So we schedule an idle callback to report * the worker results */ -trace_qio_task_thread_exit (data->task); +trace_qio_task_thread_exit (task); idle =g_idle_source_new ();g_source_set_callback (idle, qio_task_thread_result, data,NULL );
@@ -142,15 +149,14 @@ void qio_task_run_in_thread(QIOTask *task,GDestroyNotify destroy, GMainContext *context) { - struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1); QemuThread thread; +QIOTaskThreadData *data = &task->thread_data;if (context) {g_main_context_ref (context); task->context = context; } - data->task = task; data->worker = worker; data->opaque = opaque; data->destroy = destroy;
@@ -159,7 +165,7 @@ void qio_task_run_in_thread(QIOTask *task,qemu_thread_create (&thread,"io-task-worker" , qio_task_thread_worker, - data, + task,QEMU_THREAD_DETACHED ); }
Signature · 1 line
2.14.3
PATCH 13/14 qio: allow threaded qiotask to switch contexts
In reply to Peter XuThis is the part of work to allow the QIOTask to use a different gcontext rather than the default main gcontext, by providing qio_task_context_set() API.
We have done some work before on doing similar things to add non-default gcontext support. The general idea is that we delete the old GSource from the main context, then re-add a new one to the new context when context changed to a non-default one. However this trick won't work easily for threaded QIOTasks since we can't easily stop a real thread and re-setup the whole thing from the very beginning.
But luckily, we don't need to do anything to the thread. We just need to keep an eye on the GSource that completes the QIOTask, which is assigned to gcontext after the sync operation finished.
So when we setup a non-default GMainContext for a threaded QIO task, we may face two cases:
- the thread is still running the sync task: then we don't need to do anything, only to update QIOTask.context to the new context
- the thread has finished the sync task and queued an idle task to main
thread: then we destroy that old idle task, and re-create it on the new GMainContext.
Note that along the way when we modify either idle GSource or the context, we need to take the mutex before hand, since the thread may be modifying them at the same time.
Finally, call qio_task_context_set() in the tcp chardev update read handler hook if QIOTask is running.
--- chardev/char-socket.c | 4 +++ include/io/task.h | 1 + io/task.c | 70 ++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 12 deletions(-)
chardev/char-socket.c +4 -0
@@ -585,6 +585,10 @@ static void tcp_chr_update_read_handler(Chardev *chr)tcp_chr_telnet_init (CHARDEV (s)); } +if (s->thread_task) { +qio_task_context_set (s->thread_task, chr->gcontext); + } +if (!s->connected) {return ; }
include/io/task.h +1 -0
@@ -324,5 +324,6 @@ Object *qio_task_get_source(QIOTask *task);void qio_task_ref (QIOTask *task);void qio_task_unref (QIOTask *task); +void qio_task_context_set (QIOTask *task,GMainContext *context);#endif /* QIO_TASK_H */
io/task.c +58 -12
126 lines not on this page · raw message
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 13/14] qio: allow threaded qiotask to switch contexts
Quoted 10 lines · Peter Xu
This is the part of work to allow the QIOTask to use a different gcontext rather than the default main gcontext, by providing
qio_task_context_set()API.We have done some work before on doing similar things to add non-default gcontext support. The general idea is that we delete the old GSource from the main context, then re-add a new one to the new context when context changed to a non-default one. However this trick won't work easily for threaded QIOTasks since we can't easily stop a real thread and re-setup the whole thing from the very beginning.
I think this entire usage pattern is really broken. We should not provide a way to change the GMainContext on an existing task. We should always just set the correct GMainContxt right from the start. This will avoid much of the complexity you're introducing into this patch series, and avoid having to expose GTasks to the callers of the async methods at all.
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 13/14] qio: allow threaded qiotask to switch contexts
Quoted 12 lines · Daniel P. Berrangé
Quoted 10 lines · Peter Xu
This is the part of work to allow the QIOTask to use a different gcontext rather than the default main gcontext, by providing
qio_task_context_set()API.We have done some work before on doing similar things to add non-default gcontext support. The general idea is that we delete the old GSource from the main context, then re-add a new one to the new context when context changed to a non-default one. However this trick won't work easily for threaded QIOTasks since we can't easily stop a real thread and re-setup the whole thing from the very beginning.
Quoted 6 lines
I think this entire usage pattern is really broken. We should not provide a way to change the GMainContext on an existing task. We should always just set the correct GMainContxt right from the start. This will avoid much of the complexity you're introducing into this patch series, and avoid having to expose GTasks to the callers of the async methods at all.
Yeah I agree with you that the threaded QIO patches are complicated. Then how about I introduce:
- qio_task_thread_new(): to create QIO task and its thread (but not running)
- qio_task_thread_run(): to run the thread
Then I can setup the context correctly between the two in some way.
After that, qio_task_run_in_thread() will be two calls of above two APIs.
Thanks,
Signature · 1 line
Peter Xu
Re: [Qemu-devel] [PATCH 13/14] qio: allow threaded qiotask to switch contexts
Quoted 23 lines · Peter Xu
Quoted 12 lines · Daniel P. Berrangé
Quoted 10 lines · Peter Xu
This is the part of work to allow the QIOTask to use a different gcontext rather than the default main gcontext, by providing
qio_task_context_set()API.We have done some work before on doing similar things to add non-default gcontext support. The general idea is that we delete the old GSource from the main context, then re-add a new one to the new context when context changed to a non-default one. However this trick won't work easily for threaded QIOTasks since we can't easily stop a real thread and re-setup the whole thing from the very beginning.
Quoted 6 lines
I think this entire usage pattern is really broken. We should not provide a way to change the GMainContext on an existing task. We should always just set the correct GMainContxt right from the start. This will avoid much of the complexity you're introducing into this patch series, and avoid having to expose GTasks to the callers of the async methods at all.
Yeah I agree with you that the threaded QIO patches are complicated. Then how about I introduce:
Quoted 8 lines
-
qio_task_thread_new(): to create QIO task and its thread (but not running)
-qio_task_thread_run(): to run the threadThen I can setup the context correctly between the two in some way.
After that,
qio_task_run_in_thread()will be two calls of above two APIs.
I don't see why it is not enough to just pass the right GMainContext into qio_task_run_in_thread() immediately. There should not be any need to split this into two parts. ALl that's needed here is for the completion callback to rnu in the right context, which just means passing the GMainContext from qio_task_run_in_thread, through to qio_task_thread_worker via QIOTaskThreadData. Changing the GMainContext after the thread is already created/running is not something we should try todo.
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 13/14] qio: allow threaded qiotask to switch contexts
Quoted 33 lines · Daniel P. Berrangé
Quoted 23 lines · Peter Xu
Quoted 12 lines · Daniel P. Berrangé
Quoted 10 lines · Peter Xu
This is the part of work to allow the QIOTask to use a different gcontext rather than the default main gcontext, by providing
qio_task_context_set()API.We have done some work before on doing similar things to add non-default gcontext support. The general idea is that we delete the old GSource from the main context, then re-add a new one to the new context when context changed to a non-default one. However this trick won't work easily for threaded QIOTasks since we can't easily stop a real thread and re-setup the whole thing from the very beginning.
Quoted 6 lines
I think this entire usage pattern is really broken. We should not provide a way to change the GMainContext on an existing task. We should always just set the correct GMainContxt right from the start. This will avoid much of the complexity you're introducing into this patch series, and avoid having to expose GTasks to the callers of the async methods at all.
Yeah I agree with you that the threaded QIO patches are complicated. Then how about I introduce:
Quoted 8 lines
-
qio_task_thread_new(): to create QIO task and its thread (but not running)
-qio_task_thread_run(): to run the threadThen I can setup the context correctly between the two in some way.
After that,
qio_task_run_in_thread()will be two calls of above two APIs.
Quoted 8 lines
I don't see why it is not enough to just pass the right GMainContext into
qio_task_run_in_thread()immediately. There should not be any need to split this into two parts. ALl that's needed here is for the completion callback to rnu in the right context, which just means passing the GMainContext fromqio_task_run_in_thread, through toqio_task_thread_workervia QIOTaskThreadData. Changing the GMainContext after the thread is already created/running is not something we should try todo.
I dropped patches 9-14 in version two and rewrote as you suggested, by introducing a chardev machine done notifier. Please have a looks. Thanks,
Signature · 1 line
Peter Xu
PATCH 14/14 qio/chardev: specify gcontext for TLS handshake
In reply to Peter XuWe allow the TLS code to be run with non-default gcontext by providing a new qio_channel_tls_handshake_full() API.
With the new API, we can re-setup the TLS handshake GSource by calling it again with the correct gcontext. Any call to the function will clean up existing GSource tasks, and re-setup using the new gcontext.
--- chardev/char-socket.c | 30 +++++++++++++--- include/io/channel-tls.h | 22 +++++++++++- io/channel-tls.c | 91 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 123 insertions(+), 20 deletions(-)
chardev/char-socket.c +26 -4
@@ -72,6 +72,9 @@ typedef struct {static gboolean socket_reconnect_timeout (gpointer opaque);static void tcp_chr_telnet_init (Chardev *chr); +static void tcp_chr_tls_handshake_setup (Chardev *chr, +QIOChannelTLS *tioc, +GMainContext *context);static void tcp_chr_reconn_timer_cancel (SocketChardev *s) {
@@ -570,6 +573,7 @@ static void tcp_chr_telnet_destroy(SocketChardev *s)static void tcp_chr_update_read_handler (Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr); +QIOChannelTLS *tioc;if (s->listener) {/*
@@ -589,6 +593,17 @@ static void tcp_chr_update_read_handler(Chardev *chr)qio_task_context_set (s->thread_task, chr->gcontext); } + tioc = (QIOChannelTLS *)object_dynamic_cast (OBJECT (s->ioc), +TYPE_QIO_CHANNEL_TLS ); +if (tioc) { +/* +* TLS session enabled; reconfigure things up. Note that, if +* there is existing handshake task, it'll be cleaned up first +* in QIO code. +*/ +tcp_chr_tls_handshake_setup (chr, tioc, chr->gcontext); + } +if (!s->connected) {return ; }
@@ -704,6 +719,16 @@ static void tcp_chr_tls_handshake(QIOTask *task, } } +static void tcp_chr_tls_handshake_setup (Chardev *chr, +QIOChannelTLS *tioc, +GMainContext *context) +{ +qio_channel_tls_handshake_full (tioc, + tcp_chr_tls_handshake, + chr, +NULL , + context); +}static void tcp_chr_tls_init (Chardev *chr) {
@@ -736,10 +761,7 @@ static void tcp_chr_tls_init(Chardev *chr)object_unref (OBJECT (s->ioc)); s->ioc =QIO_CHANNEL (tioc); -qio_channel_tls_handshake (tioc, - tcp_chr_tls_handshake, - chr, -NULL ); +tcp_chr_tls_handshake_setup (chr, tioc,NULL ); }
include/io/channel-tls.h +21 -1
@@ -48,6 +48,9 @@ struct QIOChannelTLS {QIOChannel parent;QIOChannel *master;QCryptoTLSSession *session; +GMainContext *context; +GSource *tls_source; +QIOTask *task; };/**
@@ -111,11 +114,12 @@ qio_channel_tls_new_client(QIOChannel *master, Error **errp);/** -* qio_channel_tls_handshake: +* qio_channel_tls_handshake_full: * @ioc: the TLS channel object * @func: the callback to invoke when completed * @opaque: opaque data to pass to @func * @destroy: optional callback to free @opaque +* @context: the context that will run the handshake task * * Perform the TLS session handshake. This method * will return immediately and the handshake will
@@ -123,6 +127,22 @@ qio_channel_tls_new_client(QIOChannel *master,* loop is running. When the handshake is complete, * or fails, the @func callback will be invoked. */ +void qio_channel_tls_handshake_full (QIOChannelTLS *ioc, +QIOTaskFunc func, +gpointer opaque, +GDestroyNotify destroy, +GMainContext *context); + +/** +* qio_channel_tls_handshake: +* @ioc: the TLS channel object +* @func: the callback to invoke when completed +* @opaque: opaque data to pass to @func +* @destroy: optional callback to free @opaque +* +* Wrapper of qio_channel_tls_handshake_full(), only that we are +* running the handshake always on default main context. +*/ void qio_channel_tls_handshake(QIOChannelTLS *ioc, QIOTaskFunc func, gpointer opaque,
io/channel-tls.c +76 -15
144 lines not on this page · raw message
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH 14/14] qio/chardev: specify gcontext for TLS handshake
Quoted 40 lines · Peter Xu
We allow the TLS code to be run with non-default gcontext by providing a new
qio_channel_tls_handshake_full()API.With the new API, we can re-setup the TLS handshake GSource by calling it again with the correct gcontext. Any call to the function will clean up existing GSource tasks, and re-setup using the new gcontext.
Signed-off-by Peter Xu <[email protected]>--- chardev/char-socket.c | 30 +++++++++++++--- include/io/channel-tls.h | 22 +++++++++++- io/channel-tls.c | 91 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 123 insertions(+), 20 deletions(-)chardev/char-socket.c +4
-0@@ -72,6 +72,9 @@ typedef struct {static gboolean socket_reconnect_timeout (gpointer opaque);static void tcp_chr_telnet_init (Chardev *chr); +static void tcp_chr_tls_handshake_setup (Chardev *chr, +QIOChannelTLS *tioc, +GMainContext *context);static void tcp_chr_reconn_timer_cancel (SocketChardev *s) {@@ -570,6 +573,7 @@ static void tcp_chr_telnet_destroy(SocketChardev *s)static void tcp_chr_update_read_handler (Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr); +QIOChannelTLS *tioc;if (s->listener) {/* @@ -589,6 +593,17 @@ static void tcp_chr_update_read_handler(Chardev *chr)qio_task_context_set (s->thread_task, chr->gcontext); }
chardev/char-socket.c static void tcp_chr_update_read_handler(Chardev *chr) · line 605
Quoted 10 lines
chardev/char-socket.c +10
-0+ tioc = (QIOChannelTLS *)object_dynamic_cast (OBJECT (s->ioc), +TYPE_QIO_CHANNEL_TLS ); +if (tioc) { +/* +* TLS session enabled; reconfigure things up. Note that, if +* there is existing handshake task, it'll be cleaned up first +* in QIO code. +*/ +tcp_chr_tls_handshake_setup (chr, tioc, chr->gcontext); + }
This is crazy - we should not be looking at specific implementations of the channel. If the TLS object needs to use a specific GMainContext we should make sure that is done right from the start and not try to change the GMainContext on the fly.
Regards,
Daniel
Signature · 3 lines
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Re: [Qemu-devel] [PATCH 14/14] qio/chardev: specify gcontext for TLS handshake
Quoted 41 lines · Daniel P. Berrangé
Quoted 40 lines · Peter Xu
We allow the TLS code to be run with non-default gcontext by providing a new
qio_channel_tls_handshake_full()API.With the new API, we can re-setup the TLS handshake GSource by calling it again with the correct gcontext. Any call to the function will clean up existing GSource tasks, and re-setup using the new gcontext.
Signed-off-by Peter Xu <[email protected]>--- chardev/char-socket.c | 30 +++++++++++++--- include/io/channel-tls.h | 22 +++++++++++- io/channel-tls.c | 91 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 123 insertions(+), 20 deletions(-)chardev/char-socket.c +4
-0@@ -72,6 +72,9 @@ typedef struct {static gboolean socket_reconnect_timeout (gpointer opaque);static void tcp_chr_telnet_init (Chardev *chr); +static void tcp_chr_tls_handshake_setup (Chardev *chr, +QIOChannelTLS *tioc, +GMainContext *context);static void tcp_chr_reconn_timer_cancel (SocketChardev *s) {@@ -570,6 +573,7 @@ static void tcp_chr_telnet_destroy(SocketChardev *s)static void tcp_chr_update_read_handler (Chardev *chr) {SocketChardev *s =SOCKET_CHARDEV (chr); +QIOChannelTLS *tioc;if (s->listener) {/* @@ -589,6 +593,17 @@ static void tcp_chr_update_read_handler(Chardev *chr)qio_task_context_set (s->thread_task, chr->gcontext); }
Quoted 15 lines
chardev/char-socket.c static void tcp_chr_update_read_handler(Chardev *chr) · line 605
Quoted 10 lines
chardev/char-socket.c +10
-0+ tioc = (QIOChannelTLS *)object_dynamic_cast (OBJECT (s->ioc), +TYPE_QIO_CHANNEL_TLS ); +if (tioc) { +/* +* TLS session enabled; reconfigure things up. Note that, if +* there is existing handshake task, it'll be cleaned up first +* in QIO code. +*/ +tcp_chr_tls_handshake_setup (chr, tioc, chr->gcontext); + }This is crazy - we should not be looking at specific implementations of the channel. If the TLS object needs to use a specific GMainContext we should make sure that is done right from the start and not try to change the GMainContext on the fly.
I'm not sure whether I can do it since current code has already let the chardev frontends depend on the backends, so we cannot simply let it be reverted (setup context basically means we need to have the frontend be inited before backends since the context is now frontend-specific).
However I'm thinking maybe I can postpone some of the chardev initialization process after everything has been setup. Then it'll look like:
- init chardev backends, phase 1 (e.g., only create chardevs but postpone open)
- init chardev frontends (e.g., monitors)
- init chardev backends, phase 2 (e.g., do the real socket open work)
Actually I already spotted an existing user of it (muxes_realize_notify). Maybe I can do similar thing to postpone some of the socket chardev operations after machine init finished. Thanks,
Signature · 1 line
Peter Xu