Zhang Chen <[email protected]> writes:
> 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.
You forgot monitors.
Suggest "which can either be a QOM object, a block node, or a monitor."
> + */
> + 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)
Yes, this is the interface I want :)
> +{
> + 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();
> + }
This makes @h a deep copy of @holder. Try
IOThreadHolder *h = QAPI_CLONE(IOThreadHolder, holder);
instead.
Instead of making a copy, the function could take ownership of @holder,
and require @holder's members to be dynamically allocated. More
efficient, but complicates the callers somewhat. Not worthwhile, I
guess.
> +
> + 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;
Please use
return holder_b->type - holder_a->type;
Why? By convention, an int-valued function fn() comparing A to B
returns less than 0 when A<B, 0 when A == B, and greater than 0 when
A>B. Anything else needs a prominent comment. Best to stick to the
convention whenever practical.
Since A < B iff B > A, we want fn(A, B) == -fn(B, A).
Your function does not satisfy that! Harmless for now, because you only
ever pass it to g_list_find_custom(), which treats all non-zero values
the same. But if anyone passes it to something like qsort() in the
future, they can look forward to some debugging "fun".
> + }
> +
> + 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;
Replace this by
g_assert_not_reached();
Then neither @name_a nor @name_b can be null, because a non-optional
QAPI str is never null. Simply use strcmp().
> + }
> +
> + 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);
Removing @link from the list before freeing link->data would be more
obviously correct, wouldn't it?
> +}
> +
> 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);
> +
Is this really needed to make this patch work?
> 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.
Let's use
# @node-name: Name of the block node
to make it more similar to node-name descriptions elsewhere.
> +#
> +# Since: 11.1
> +#
> +##
> +{ 'struct': 'IOThreadHolderBlockNode',
> + 'data': { 'node-name': 'str' } }
> +
> +##
> +# @IOThreadHolderQomObject:
> +#
> +# @qom-path: A QOM Object.
Likewise, let's use
# @qom-path: Path to the object in the QOM tree
> +#
> +# Since: 11.1
> +#
> +##
> +{ 'struct': 'IOThreadHolderQomObject',
> + 'data': { 'qom-path': 'str' } }
> +
> +##
> +# @IOThreadHolderMonitor:
> +#
Let's use
# @monitor-name: Name of the HMP/QMP monitor
> +# @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.
Rename to @monitor for consistency with the other two.
Remark, not a request to change anything in your patch: kind monitor
will become obsolete when monitors become QOM objects. Doing that
before we release this interface is desirable.
> +#
> +# Since: 11.1
> +##
> +{ 'enum': 'IOThreadHolderKind',
> + 'data': [ 'block-node', 'qom-object', 'monitor-name' ] }
> +
> +##
> +# @IOThreadHolder:
> +#
Let's add a short description:
# The block node, QOM object, or monitor holding the I/O thread.
> +# @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:
> #