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 QOM path to a list.
- iothread_unref(): Searches for the device path using a custom
string comparison (g_strcmp0), releases the associated memory
upon a successful match.
- holders: A GList storing the QOM paths of attached devices
for runtime introspection.
This infrastructure allows management tools and QMP commands to
query the attachment status of IOThreads.
Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
include/system/iothread.h | 1 +
iothread.c | 56 +++++++++++++++++++++++++++++++++++++++
qapi/misc.json | 48 +++++++++++++++++++++++++++++++++
3 files changed, 105 insertions(+)
diff --git a/include/system/iothread.h b/include/system/iothread.h
index e26d13c6c7..21a76bd70d 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -33,6 +33,7 @@ struct IOThread {
bool stopping; /* has iothread_stop() been called? */
bool running; /* should iothread_run() continue? */
int thread_id;
+ GList *holders; /* an array of QOM paths for attached devices */
/* AioContext poll parameters */
int64_t poll_max_ns;
diff --git a/iothread.c b/iothread.c
index caf68e0764..c43266b191 100644
--- a/iothread.c
+++ b/iothread.c
@@ -36,6 +36,55 @@
#define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL
#endif
+/*
+ * Add holder device path to the list.
+ */
+static void iothread_ref(IOThread *iothread, const char *holder)
+{
+ IoThreadHolder *h = g_new0(IoThreadHolder, 1);
+
+ if (holder && holder[0] == '/') {
+ h->type = IO_THREAD_HOLDER_KIND_QOM_OBJECT;
+ h->u.qom_object.data = g_strdup(holder);
+ } else {
+ h->type = IO_THREAD_HOLDER_KIND_BLOCK_NODE;
+ h->u.block_node.data = g_strdup(holder ? holder : "unknown");
+ }
+
+ iothread->holders = g_list_prepend(iothread->holders, h);
+}
+
+static int iothread_holder_compare(gconstpointer a, gconstpointer b)
+{
+ const IoThreadHolder *holder_node = a;
+ const char *target_name = b;
+ const char *current_name = NULL;
+
+ if (holder_node->type == IO_THREAD_HOLDER_KIND_QOM_OBJECT) {
+ current_name = holder_node->u.qom_object.data;
+ } else if (holder_node->type == IO_THREAD_HOLDER_KIND_BLOCK_NODE) {
+ current_name = holder_node->u.block_node.data;
+ }
+
+ return g_strcmp0(current_name, target_name);
+}
+
+/*
+ * Delete holder device path from the list.
+ */
+static void iothread_unref(IOThread *iothread, const char *holder)
+{
+ GList *link = g_list_find_custom(iothread->holders, holder,
+ (GCompareFunc)iothread_holder_compare);
+
+ /* We don't support unref without a link */
+ 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;
@@ -115,6 +164,9 @@ static void iothread_instance_finalize(Object *obj)
iothread_stop(iothread);
+ /* We don't support finalize without holders */
+ assert(iothread->holders == NULL);
+
/*
* Before glib2 2.33.10, there is a glib2 bug that GSource context
* pointer may not be cleared even if the context has already been
@@ -336,6 +388,10 @@ char *iothread_get_id(IOThread *iothread)
AioContext *iothread_get_aio_context(IOThread *iothread)
{
+ /* Remove in next patch for build */
+ iothread_ref(iothread, "tmp");
+ iothread_unref(iothread, "tmp");
+
return iothread->ctx;
}
diff --git a/qapi/misc.json b/qapi/misc.json
index 1f5062df2a..d65d8012b2 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -67,6 +67,54 @@
##
{ 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true }
+
+##
+# @IoThreadHolderBlockNode:
+#
+# @data: Block node name.
+#
+# Since: 11.0
+#
+##
+{ 'struct': 'IoThreadHolderBlockNode',
+ 'data': { 'data': 'str' } }
+
+##
+# @IoThreadHolderQomObject:
+#
+# @data: QOM path.
+#
+# Since: 11.0
+#
+##
+{ 'struct': 'IoThreadHolderQomObject',
+ 'data': { 'data': 'str' } }
+
+##
+# @IoThreadHolderKind:
+#
+# @block-node: Block node name.
+# @qom-object: Standard QOM path.
+#
+# Since: 11.0
+##
+{ 'enum': 'IoThreadHolderKind',
+ 'data': [ 'block-node', 'qom-object' ] }
+
+##
+# @IoThreadHolder:
+#
+# @type: Current IoThread holder type support QOM path and Block node.
+#
+# Since: 11.0
+##
+{ 'union': 'IoThreadHolder',
+ 'base': { 'type': 'IoThreadHolderKind' },
+ 'discriminator': 'type',
+ 'data': {
+ 'block-node': 'IoThreadHolderBlockNode',
+ 'qom-object': 'IoThreadHolderQomObject' } }
+
##
# @IOThreadInfo:
#
--
2.49.0