[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v3 019/114] qom: inherit is_available and filter after MODULE_INIT_QOM



Parents are not all registered at type_register time, so a type
cannot inherit is_available then.

After MODULE_INIT_QOM, and after a late DSO, inherit the callback
from the parent, drop types that fail target_info(), and report a
missing parent. Skip that filter for MODULE_INIT_PHASE_DSO_BEFORE.
The inherit walk colors each type once, so it is O(N) in the number
of types. TYPE_INTERFACE and TYPE_OBJECT use target_is_any().
type_register_static aborts if it runs outside the QOM before/after
hooks.

Signed-off-by: Yonggang Luo <luoyonggang@xxxxxxxxx>
---
 include/qemu/target-info.h |   9 +++
 qom/object.c               | 134 +++++++++++++++++++++++++++++++++++--
 target-info.c              |   6 ++
 3 files changed, 145 insertions(+), 4 deletions(-)

diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h
index 440da63e6cf..790fc74e9d3 100644
--- a/include/qemu/target-info.h
+++ b/include/qemu/target-info.h
@@ -53,6 +53,15 @@ const char *target_cpu_type(void);
  */
 bool target_big_endian(void);
 
+/**
+ * target_is_any:
+ * @ti: TargetInfo to inspect
+ *
+ * Returns true for every TargetInfo. Usable as TypeInfo.is_available
+ * for types that are not target-scoped (TYPE_OBJECT).
+ */
+bool target_is_any(const TargetInfo *ti);
+
 /**
  * target_is_base_arm:
  * @ti: TargetInfo to inspect
diff --git a/qom/object.c b/qom/object.c
index a3e75927e82..d0f9242ac99 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -14,6 +14,7 @@
 #include "qapi/error.h"
 #include "qom/compat-properties.h"
 #include "qom/object.h"
+#include "qemu/module.h"
 #include "qom/object_interfaces.h"
 #include "qemu/target-info-qapi.h"
 #include "qemu/cutils.h"
@@ -40,6 +41,7 @@
 #include "qobject/qnum.h"
 #include "qobject/qstring.h"
 #include "qemu/error-report.h"
+#include "qemu/queue.h"
 
 #define MAX_INTERFACES 32
 
@@ -81,11 +83,16 @@ struct TypeImpl
 
     int num_interfaces;
     InterfaceImpl interfaces[MAX_INTERFACES];
+
+    QTAILQ_ENTRY(TypeImpl) register_node;
 };
 
 static Type type_interface;
 
 static GHashTable *type_table;
+static QTAILQ_HEAD(, TypeImpl) type_register_list =
+    QTAILQ_HEAD_INITIALIZER(type_register_list);
+static bool type_qom_registering;
 
 static bool enumerating_types;
 
@@ -95,6 +102,21 @@ static void type_table_add(TypeImpl *ti)
     g_hash_table_insert(type_table, (void *)ti->name, ti);
 }
 
+static void type_table_remove(TypeImpl *ti)
+{
+    int i;
+
+    assert(!enumerating_types);
+    g_hash_table_remove(type_table, (void *)ti->name);
+    QTAILQ_REMOVE(&type_register_list, ti, register_node);
+    g_free((char *)ti->name);
+    g_free((char *)ti->parent);
+    for (i = 0; i < ti->num_interfaces; i++) {
+        g_free((char *)ti->interfaces[i].typename);
+    }
+    g_free(ti);
+}
+
 static TypeImpl *type_table_lookup(const char *name)
 {
     return g_hash_table_lookup(type_table, name);
@@ -171,18 +193,20 @@ static TypeImpl *type_register_internal(const TypeInfo 
*info)
         abort();
     }
 
-    if (info->is_available && !info->is_available(target_info())) {
-        return NULL;
-    }
-
     ti = type_new(info);
 
     type_table_add(ti);
+    QTAILQ_INSERT_TAIL(&type_register_list, ti, register_node);
     return ti;
 }
 
 TypeImpl *type_register_static(const TypeInfo *info)
 {
+    if (!type_qom_registering) {
+        fprintf(stderr, "Registering '%s' outside QOM before/after\n",
+                info->name);
+        abort();
+    }
     assert(info->parent);
     return type_register_internal(info);
 }
@@ -316,6 +340,8 @@ static void type_initialize_interface(TypeImpl *ti, 
TypeImpl *interface_type,
 
     iface_impl = type_new(&info);
     iface_impl->parent_type = parent_type;
+    g_assert(interface_type->is_available);
+    iface_impl->is_available = interface_type->is_available;
     type_initialize(iface_impl);
     g_free((char *)info.name);
 
@@ -1159,6 +1185,7 @@ bool object_class_is_secure(ObjectClass *klass)
 
 TypeIsAvailable *object_class_get_is_available(ObjectClass *klass)
 {
+    g_assert(klass->type->is_available);
     return klass->type->is_available;
 }
 
@@ -3297,12 +3324,109 @@ static void object_class_init(ObjectClass *klass, 
const void *data)
                                   NULL);
 }
 
+static bool type_qom_before(ModuleInitPhase phase, Error **errp)
+{
+    (void)errp;
+    (void)phase;
+
+    type_qom_registering = true;
+    return true;
+}
+
+enum TypeVisitColor {
+    TYPE_VISIT_GRAY = 1,
+    TYPE_VISIT_BLACK,
+};
+
+static bool type_update_is_available(TypeImpl *type, GHashTable *visit,
+                                     Error **errp)
+{
+    TypeImpl *parent;
+    gpointer color;
+
+    color = g_hash_table_lookup(visit, type);
+    if (color == GINT_TO_POINTER(TYPE_VISIT_BLACK)) {
+        return true;
+    }
+    if (color == GINT_TO_POINTER(TYPE_VISIT_GRAY)) {
+        error_setg(errp, "Type '%s' has a cyclic parent chain",
+                   type->name);
+        return false;
+    }
+
+    g_hash_table_insert(visit, type, GINT_TO_POINTER(TYPE_VISIT_GRAY));
+
+    parent = type_get_parent(type);
+    if (!parent) {
+        if (strcmp(type->name, TYPE_OBJECT) != 0 &&
+            strcmp(type->name, TYPE_INTERFACE) != 0) {
+            error_setg(errp,
+                       "Type '%s' does not inherit from %s or %s",
+                       type->name, TYPE_OBJECT, TYPE_INTERFACE);
+            return false;
+        }
+    } else {
+        if (!type_update_is_available(parent, visit, errp)) {
+            return false;
+        }
+        if (!type->is_available) {
+            type->is_available = parent->is_available;
+        }
+    }
+
+    g_hash_table_insert(visit, type, GINT_TO_POINTER(TYPE_VISIT_BLACK));
+    return true;
+}
+
+static bool type_qom_after(ModuleInitPhase phase, Error **errp)
+{
+    TypeImpl *type, *next;
+    GHashTable *visit;
+    Error *local_err = NULL;
+
+    type_qom_registering = false;
+    if (phase == MODULE_INIT_PHASE_DSO_BEFORE) {
+        return true;
+    }
+
+    visit = g_hash_table_new(NULL, NULL);
+    QTAILQ_FOREACH(type, &type_register_list, register_node) {
+        if (!type_update_is_available(type, visit, &local_err)) {
+            break;
+        }
+    }
+    g_hash_table_destroy(visit);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return false;
+    }
+
+    QTAILQ_FOREACH_SAFE(type, &type_register_list, register_node, next) {
+        g_assert(type->is_available);
+        if (!type->is_available(target_info())) {
+            type_table_remove(type);
+        }
+    }
+
+    QTAILQ_FOREACH(type, &type_register_list, register_node) {
+        if (type->parent && !type_table_lookup(type->parent)) {
+            error_setg(errp, "Type '%s' is missing its parent '%s'",
+                       type->name, type->parent);
+            return false;
+        }
+    }
+
+    return true;
+}
+
 static void __attribute__((constructor)) register_types(void)
 {
     static const TypeInfo interface_info = {
         .name = TYPE_INTERFACE,
         .class_size = sizeof(InterfaceClass),
         .abstract = true,
+        .is_available = target_is_any,
     };
 
     static const TypeInfo object_info = {
@@ -3310,9 +3434,11 @@ static void __attribute__((constructor)) 
register_types(void)
         .instance_size = sizeof(Object),
         .class_init = object_class_init,
         .abstract = true,
+        .is_available = target_is_any,
     };
 
     type_table = g_hash_table_new(g_str_hash, g_str_equal);
     type_interface = type_register_internal(&interface_info);
     type_register_internal(&object_info);
+    register_module_hooks(MODULE_INIT_QOM, type_qom_before, type_qom_after);
 }
diff --git a/target-info.c b/target-info.c
index 36f75ece36f..a9ab4b465d3 100644
--- a/target-info.c
+++ b/target-info.c
@@ -96,6 +96,12 @@ bool target_big_endian(void)
     return target_endian_mode() == ENDIAN_MODE_BIG;
 }
 
+bool target_is_any(const TargetInfo *ti)
+{
+    (void)ti;
+    return true;
+}
+
 bool target_is_base_arm(const TargetInfo *ti)
 {
     switch (ti->target_arch) {
-- 
2.52.0.windows.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.