Currently, IOThreads do not maintain a record of which devices are associated with them. This makes it difficult to monitor the workload distribution of IOThreads, especially in complex hotplug scenarios involving multiple virtio-blk or virtio-scsi devices.
This patch introduces a reference counting and tracking mechanism within the IOThread object: - iothread_ref(): Prepends the device's IOThreadHolder to a list. - iothread_unref(): Searches for the IOThreadHolder using a custom string comparison (g_strcmp0), releases the associated memory upon a successful match. - holders: A GList storing the IOThreadHolder of attached devices for runtime introspection. A later commit will add QMP commands to let management applications query the attachment status of IOThreads. Signed-off-by: Zhang Chen <[email protected]> --- include/system/iothread.h | 5 +++ iothread.c | 94 +++++++++++++++++++++++++++++++++++++++ qapi/misc.json | 61 +++++++++++++++++++++++++ 3 files changed, 160 insertions(+) diff --git a/include/system/iothread.h b/include/system/iothread.h index a1ef7696cb..b9207ad829 100644 --- a/include/system/iothread.h +++ b/include/system/iothread.h @@ -50,6 +50,11 @@ struct IOThread { bool stopping; /* has iothread_stop() been called? */ bool running; /* should iothread_run() continue? */ int thread_id; + /* + * The list elements are of type IOThreadHolder, which can + * represent either a QOM path or a block node name. + */ + GList *holders; /* AioContext poll parameters */ int64_t poll_max_ns; diff --git a/iothread.c b/iothread.c index 3558535b40..3301b8d495 100644 --- a/iothread.c +++ b/iothread.c @@ -25,6 +25,92 @@ #include "qemu/rcu.h" #include "qemu/main-loop.h" +/* + * iothread_ref: + * @iothread: the iothread to track + * @holder: the IOThreadHolder object initialized by the caller + * + * Add the @holder to the iothread's tracking list. + */ +static void iothread_ref(IOThread *iothread, const IOThreadHolder *holder) +{ + assert(holder); + IOThreadHolder *h = g_new0(IOThreadHolder, 1); + + h->type = holder->type; + switch (holder->type) { + case IO_THREAD_HOLDER_KIND_QOM_OBJECT: + h->u.qom_object.qom_path = g_strdup(holder->u.qom_object.qom_path); + break; + case IO_THREAD_HOLDER_KIND_BLOCK_NODE: + h->u.block_node.node_name = + g_strdup(holder->u.block_node.node_name); + break; + case IO_THREAD_HOLDER_KIND_MONITOR_NAME: + h->u.monitor_name.monitor_name = + g_strdup(holder->u.monitor_name.monitor_name); + break; + default: + g_assert_not_reached(); + } + + iothread->holders = g_list_prepend(iothread->holders, h); +} + +static int iothread_holder_compare(gconstpointer a, gconstpointer b) +{ + const IOThreadHolder *holder_a = a; + const IOThreadHolder *holder_b = b; + const char *name_a, *name_b; + + if (holder_a->type != holder_b->type) { + return -1; + } + + switch (holder_a->type) { + case IO_THREAD_HOLDER_KIND_QOM_OBJECT: + name_a = holder_a->u.qom_object.qom_path; + name_b = holder_b->u.qom_object.qom_path; + break; + case IO_THREAD_HOLDER_KIND_BLOCK_NODE: + name_a = holder_a->u.block_node.node_name; + name_b = holder_b->u.block_node.node_name; + break; + case IO_THREAD_HOLDER_KIND_MONITOR_NAME: + name_a = holder_a->u.monitor_name.monitor_name; + name_b = holder_b->u.monitor_name.monitor_name; + break; + default: + /* + * This should not happen. If it does, name_a/b remains + * NULL and g_strcmp0 will handle it safely. + */ + name_a = NULL; + name_b = NULL; + } + + return g_strcmp0(name_a, name_b); +} + +/* + * This function removes the @holder from the @iothread's tracking list. + * The @holder must match the one used previously in iothread_ref(). + * It is a programming error to call this with a @holder that is not + * currently associated with the @iothread. + */ +static void iothread_unref(IOThread *iothread, const IOThreadHolder *holder) +{ + assert(holder); + GList *link = g_list_find_custom(iothread->holders, holder, + (GCompareFunc)iothread_holder_compare); + + assert(link); + + IOThreadHolder *h = (IOThreadHolder *)link->data; + qapi_free_IOThreadHolder(h); + iothread->holders = g_list_delete_link(iothread->holders, link); +} + static void *iothread_run(void *opaque) { IOThread *iothread = opaque; @@ -356,6 +442,14 @@ char *iothread_get_id(IOThread *iothread) AioContext *iothread_get_aio_context(IOThread *iothread) { + /* Remove in next patch for build */ + IOThreadHolder holder = { + .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT, + .u.qom_object.qom_path = (char *)"tmp_path", + }; + iothread_ref(iothread, &holder); + iothread_unref(iothread, &holder); + return iothread->ctx; } diff --git a/qapi/misc.json b/qapi/misc.json index c71a5fe657..d9f82f0922 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -67,6 +67,67 @@ ## { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true } + +## +# @IOThreadHolderBlockNode: +# +# @node-name: A block node. +# +# Since: 11.1 +# +## +{ 'struct': 'IOThreadHolderBlockNode', + 'data': { 'node-name': 'str' } } + +## +# @IOThreadHolderQomObject: +# +# @qom-path: A QOM Object. +# +# Since: 11.1 +# +## +{ 'struct': 'IOThreadHolderQomObject', + 'data': { 'qom-path': 'str' } } + +## +# @IOThreadHolderMonitor: +# +# @monitor-name: A HMP/QMP monitor. +# +# Since: 11.1 +# +## +{ 'struct': 'IOThreadHolderMonitor', + 'data': { 'monitor-name': 'str' } } + +## +# @IOThreadHolderKind: +# +# @block-node: A block node. +# @qom-object: A QOM Object. +# @monitor-name: A HMP/QMP monitor. +# +# Since: 11.1 +## +{ 'enum': 'IOThreadHolderKind', + 'data': [ 'block-node', 'qom-object', 'monitor-name' ] } + +## +# @IOThreadHolder: +# +# @type: the kind of I/O thread holder. +# +# Since: 11.1 +## +{ 'union': 'IOThreadHolder', + 'base': { 'type': 'IOThreadHolderKind' }, + 'discriminator': 'type', + 'data': { + 'block-node': 'IOThreadHolderBlockNode', + 'qom-object': 'IOThreadHolderQomObject', + 'monitor-name': 'IOThreadHolderMonitor' } } + ## # @IOThreadInfo: # -- 2.49.0
