QOM infrastructure fixes and device conversions
* Fix for properties on objects > 4 GiB * Performance improvements for QOM property handling * Assertion cleanups * MAINTAINERS additions -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABAgAGBQJWTd1wAAoJEPou0S0+fgE/v4oQAKdUcb8kDg8cb1rfjNHOGSxh GLrBnpCE22ePtugMJqyGRE/bM2cMrXk/NLMGy1hXeNt+46zl6eUadZSV4UCjrq8q I9S5/DuGpwc7NT5zw5/ZTx7b9rzCjwpvyq17Ljwme5QbKZvC86OiaZ5OjD7HZdYO wY1vXuDoJXuj0r8hp6uS/mkfXx7R6O3bsmnOaz1yxSZqs0gi1r9En6Y/aoOCgz1V bc09iWIAer0U71E9C+kinWwqBBx/PjhrkKxBGMmFEtf3O7Kd8irXpZPoafpRkgsJ mvvUaiHFapJaXjjsSlknRfdspXdhwrrYhoCPso8vwEDEWMB03th2eBcau2rsfFXj nHPAGwjxKETSQHD+/EbtCL+y94IkSbkdf1qF+TWnCiAHIF/yvoMbjRy5+7I/bsbC Mp+qzjP+09E/qSclbeBH/EA/4ukjF2UbDGDh17/019aEpDVt016PKjoRhAWgbOJR QKumj8y7+UQMvKo1jkqcOVf7pFTkKXeAsVvWjtA089X9iEczJQo6lrTxmtvLZ7K6 PehJPZFlm7hLTEykq+xZmgQAGrhx2MdQbbEgEDM5flGPRViypmihgRzFWIAT6rBY WBEFRohRuHwTARDcmyP9MWeR5/hAlH3kD3O0qCYNbCZgQroXBW6bHQ913rerfwXh uatso/iKOJ6YOlc7scPU =/IfF -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/afaerber/tags/qom-devices-for-peter' into staging QOM infrastructure fixes and device conversions * Fix for properties on objects > 4 GiB * Performance improvements for QOM property handling * Assertion cleanups * MAINTAINERS additions # gpg: Signature made Thu 19 Nov 2015 14:32:16 GMT using RSA key ID 3E7E013F # gpg: Good signature from "Andreas Färber <afaerber@suse.de>" # gpg: aka "Andreas Färber <afaerber@suse.com>" * remotes/afaerber/tags/qom-devices-for-peter: MAINTAINERS: Add check-qom-{interface,proplist} to QOM qom: Clean up assertions to display values on failure qom: Replace object property list with GHashTable qom: Add a test case for complex property finalization net: Convert net filter code to use object property iterators ppc: Convert spapr code to use object property iterators vl: Convert machine help code to use object property iterators qmp: Convert QMP code to use object property iterators qom: Introduce ObjectPropertyIterator struct for iteration qdev: Change Property::offset field to ptrdiff_t type Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
28c3e6ee72
@ -1164,6 +1164,8 @@ F: include/qom/
|
||||
X: include/qom/cpu.h
|
||||
F: qom/
|
||||
X: qom/cpu.c
|
||||
F: tests/check-qom-interface.c
|
||||
F: tests/check-qom-proplist.c
|
||||
F: tests/qom-test.c
|
||||
|
||||
QMP
|
||||
|
@ -657,6 +657,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
|
||||
{
|
||||
Object *root_container;
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator *iter;
|
||||
uint32_t drc_count = 0;
|
||||
GArray *drc_indexes, *drc_power_domains;
|
||||
GString *drc_names, *drc_types;
|
||||
@ -680,7 +681,8 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
|
||||
*/
|
||||
root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
|
||||
|
||||
QTAILQ_FOREACH(prop, &root_container->properties, node) {
|
||||
iter = object_property_iter_init(root_container);
|
||||
while ((prop = object_property_iter_next(iter))) {
|
||||
Object *obj;
|
||||
sPAPRDRConnector *drc;
|
||||
sPAPRDRConnectorClass *drck;
|
||||
@ -721,6 +723,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
|
||||
spapr_drc_get_type_str(drc->type));
|
||||
drc_types = g_string_insert_len(drc_types, -1, "\0", 1);
|
||||
}
|
||||
object_property_iter_free(iter);
|
||||
|
||||
/* now write the drc count into the space we reserved at the
|
||||
* beginning of the arrays previously
|
||||
|
@ -237,7 +237,7 @@ struct BusState {
|
||||
struct Property {
|
||||
const char *name;
|
||||
PropertyInfo *info;
|
||||
int offset;
|
||||
ptrdiff_t offset;
|
||||
uint8_t bitnr;
|
||||
qtype_code qtype;
|
||||
int64_t defval;
|
||||
|
@ -344,8 +344,6 @@ typedef struct ObjectProperty
|
||||
ObjectPropertyResolve *resolve;
|
||||
ObjectPropertyRelease *release;
|
||||
void *opaque;
|
||||
|
||||
QTAILQ_ENTRY(ObjectProperty) node;
|
||||
} ObjectProperty;
|
||||
|
||||
/**
|
||||
@ -405,7 +403,7 @@ struct Object
|
||||
/*< private >*/
|
||||
ObjectClass *class;
|
||||
ObjectFree *free;
|
||||
QTAILQ_HEAD(, ObjectProperty) properties;
|
||||
GHashTable *properties;
|
||||
uint32_t ref;
|
||||
Object *parent;
|
||||
};
|
||||
@ -960,6 +958,55 @@ void object_property_del(Object *obj, const char *name, Error **errp);
|
||||
ObjectProperty *object_property_find(Object *obj, const char *name,
|
||||
Error **errp);
|
||||
|
||||
typedef struct ObjectPropertyIterator ObjectPropertyIterator;
|
||||
|
||||
/**
|
||||
* object_property_iter_init:
|
||||
* @obj: the object
|
||||
*
|
||||
* Initializes an iterator for traversing all properties
|
||||
* registered against an object instance.
|
||||
*
|
||||
* It is forbidden to modify the property list while iterating,
|
||||
* whether removing or adding properties.
|
||||
*
|
||||
* Typical usage pattern would be
|
||||
*
|
||||
* <example>
|
||||
* <title>Using object property iterators</title>
|
||||
* <programlisting>
|
||||
* ObjectProperty *prop;
|
||||
* ObjectPropertyIterator *iter;
|
||||
*
|
||||
* iter = object_property_iter_init(obj);
|
||||
* while ((prop = object_property_iter_next(iter))) {
|
||||
* ... do something with prop ...
|
||||
* }
|
||||
* object_property_iter_free(iter);
|
||||
* </programlisting>
|
||||
* </example>
|
||||
*
|
||||
* Returns: the new iterator
|
||||
*/
|
||||
ObjectPropertyIterator *object_property_iter_init(Object *obj);
|
||||
|
||||
/**
|
||||
* object_property_iter_free:
|
||||
* @iter: the iterator instance
|
||||
*
|
||||
* Releases any resources associated with the iterator.
|
||||
*/
|
||||
void object_property_iter_free(ObjectPropertyIterator *iter);
|
||||
|
||||
/**
|
||||
* object_property_iter_next:
|
||||
* @iter: the iterator instance
|
||||
*
|
||||
* Returns: the next property, or %NULL when all properties
|
||||
* have been traversed.
|
||||
*/
|
||||
ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter);
|
||||
|
||||
void object_unparent(Object *obj);
|
||||
|
||||
/**
|
||||
@ -1488,6 +1535,9 @@ void object_property_set_description(Object *obj, const char *name,
|
||||
* Call @fn passing each child of @obj and @opaque to it, until @fn returns
|
||||
* non-zero.
|
||||
*
|
||||
* It is forbidden to add or remove children from @obj from the @fn
|
||||
* callback.
|
||||
*
|
||||
* Returns: The last value returned by @fn, or 0 if there is no child.
|
||||
*/
|
||||
int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque),
|
||||
@ -1503,6 +1553,9 @@ int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque),
|
||||
* non-zero. Calls recursively, all child nodes of @obj will also be passed
|
||||
* all the way down to the leaf nodes of the tree. Depth first ordering.
|
||||
*
|
||||
* It is forbidden to add or remove children from @obj (or its
|
||||
* child nodes) from the @fn callback.
|
||||
*
|
||||
* Returns: The last value returned by @fn, or 0 if there is no child.
|
||||
*/
|
||||
int object_child_foreach_recursive(Object *obj,
|
||||
|
@ -137,6 +137,7 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
|
||||
Error *local_err = NULL;
|
||||
char *str, *info;
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator *iter;
|
||||
StringOutputVisitor *ov;
|
||||
|
||||
if (!nf->netdev_id) {
|
||||
@ -173,7 +174,8 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
|
||||
QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
|
||||
|
||||
/* generate info str */
|
||||
QTAILQ_FOREACH(prop, &OBJECT(nf)->properties, node) {
|
||||
iter = object_property_iter_init(OBJECT(nf));
|
||||
while ((prop = object_property_iter_next(iter))) {
|
||||
if (!strcmp(prop->name, "type")) {
|
||||
continue;
|
||||
}
|
||||
@ -187,6 +189,7 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
|
||||
g_free(str);
|
||||
g_free(info);
|
||||
}
|
||||
object_property_iter_free(iter);
|
||||
}
|
||||
|
||||
static void netfilter_finalize(Object *obj)
|
||||
|
10
qmp.c
10
qmp.c
@ -210,6 +210,7 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
|
||||
bool ambiguous = false;
|
||||
ObjectPropertyInfoList *props = NULL;
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator *iter;
|
||||
|
||||
obj = object_resolve_path(path, &ambiguous);
|
||||
if (obj == NULL) {
|
||||
@ -222,7 +223,8 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
||||
iter = object_property_iter_init(obj);
|
||||
while ((prop = object_property_iter_next(iter))) {
|
||||
ObjectPropertyInfoList *entry = g_malloc0(sizeof(*entry));
|
||||
|
||||
entry->value = g_malloc0(sizeof(ObjectPropertyInfo));
|
||||
@ -232,6 +234,7 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
|
||||
entry->value->name = g_strdup(prop->name);
|
||||
entry->value->type = g_strdup(prop->type);
|
||||
}
|
||||
object_property_iter_free(iter);
|
||||
|
||||
return props;
|
||||
}
|
||||
@ -503,6 +506,7 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
||||
ObjectClass *klass;
|
||||
Object *obj;
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator *iter;
|
||||
DevicePropertyInfoList *prop_list = NULL;
|
||||
|
||||
klass = object_class_by_name(typename);
|
||||
@ -531,7 +535,8 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
||||
|
||||
obj = object_new(typename);
|
||||
|
||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
||||
iter = object_property_iter_init(obj);
|
||||
while ((prop = object_property_iter_next(iter))) {
|
||||
DevicePropertyInfo *info;
|
||||
DevicePropertyInfoList *entry;
|
||||
|
||||
@ -562,6 +567,7 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
||||
entry->next = prop_list;
|
||||
prop_list = entry;
|
||||
}
|
||||
object_property_iter_free(iter);
|
||||
|
||||
object_unref(obj);
|
||||
|
||||
|
146
qom/object.c
146
qom/object.c
@ -67,6 +67,10 @@ struct TypeImpl
|
||||
InterfaceImpl interfaces[MAX_INTERFACES];
|
||||
};
|
||||
|
||||
struct ObjectPropertyIterator {
|
||||
GHashTableIter iter;
|
||||
};
|
||||
|
||||
static Type type_interface;
|
||||
|
||||
static GHashTable *type_table_get(void)
|
||||
@ -261,7 +265,7 @@ static void type_initialize(TypeImpl *ti)
|
||||
GSList *e;
|
||||
int i;
|
||||
|
||||
g_assert(parent->class_size <= ti->class_size);
|
||||
g_assert_cmpint(parent->class_size, <=, ti->class_size);
|
||||
memcpy(ti->class, parent->class, parent->class_size);
|
||||
ti->class->interfaces = NULL;
|
||||
|
||||
@ -326,6 +330,16 @@ static void object_post_init_with_type(Object *obj, TypeImpl *ti)
|
||||
}
|
||||
}
|
||||
|
||||
static void object_property_free(gpointer data)
|
||||
{
|
||||
ObjectProperty *prop = data;
|
||||
|
||||
g_free(prop->name);
|
||||
g_free(prop->type);
|
||||
g_free(prop->description);
|
||||
g_free(prop);
|
||||
}
|
||||
|
||||
void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
|
||||
{
|
||||
Object *obj = data;
|
||||
@ -333,14 +347,15 @@ void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
|
||||
g_assert(type != NULL);
|
||||
type_initialize(type);
|
||||
|
||||
g_assert(type->instance_size >= sizeof(Object));
|
||||
g_assert_cmpint(type->instance_size, >=, sizeof(Object));
|
||||
g_assert(type->abstract == false);
|
||||
g_assert(size >= type->instance_size);
|
||||
g_assert_cmpint(size, >=, type->instance_size);
|
||||
|
||||
memset(obj, 0, type->instance_size);
|
||||
obj->class = type->class;
|
||||
object_ref(obj);
|
||||
QTAILQ_INIT(&obj->properties);
|
||||
obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
NULL, object_property_free);
|
||||
object_init_with_type(obj, type);
|
||||
object_post_init_with_type(obj, type);
|
||||
}
|
||||
@ -359,29 +374,51 @@ static inline bool object_property_is_child(ObjectProperty *prop)
|
||||
|
||||
static void object_property_del_all(Object *obj)
|
||||
{
|
||||
while (!QTAILQ_EMPTY(&obj->properties)) {
|
||||
ObjectProperty *prop = QTAILQ_FIRST(&obj->properties);
|
||||
ObjectProperty *prop;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
bool released;
|
||||
|
||||
QTAILQ_REMOVE(&obj->properties, prop, node);
|
||||
|
||||
if (prop->release) {
|
||||
prop->release(obj, prop->name, prop->opaque);
|
||||
do {
|
||||
released = false;
|
||||
g_hash_table_iter_init(&iter, obj->properties);
|
||||
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||
prop = value;
|
||||
if (prop->release) {
|
||||
prop->release(obj, prop->name, prop->opaque);
|
||||
prop->release = NULL;
|
||||
released = true;
|
||||
break;
|
||||
}
|
||||
g_hash_table_iter_remove(&iter);
|
||||
}
|
||||
} while (released);
|
||||
|
||||
g_free(prop->name);
|
||||
g_free(prop->type);
|
||||
g_free(prop->description);
|
||||
g_free(prop);
|
||||
}
|
||||
g_hash_table_unref(obj->properties);
|
||||
}
|
||||
|
||||
static void object_property_del_child(Object *obj, Object *child, Error **errp)
|
||||
{
|
||||
ObjectProperty *prop;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
||||
g_hash_table_iter_init(&iter, obj->properties);
|
||||
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||
prop = value;
|
||||
if (object_property_is_child(prop) && prop->opaque == child) {
|
||||
object_property_del(obj, prop->name, errp);
|
||||
if (prop->release) {
|
||||
prop->release(obj, prop->name, prop->opaque);
|
||||
prop->release = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_hash_table_iter_init(&iter, obj->properties);
|
||||
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||
prop = value;
|
||||
if (object_property_is_child(prop) && prop->opaque == child) {
|
||||
g_hash_table_iter_remove(&iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -413,7 +450,7 @@ static void object_finalize(void *data)
|
||||
object_property_del_all(obj);
|
||||
object_deinit(obj, ti);
|
||||
|
||||
g_assert(obj->ref == 0);
|
||||
g_assert_cmpint(obj->ref, ==, 0);
|
||||
if (obj->free) {
|
||||
obj->free(obj);
|
||||
}
|
||||
@ -779,10 +816,12 @@ static int do_object_child_foreach(Object *obj,
|
||||
int (*fn)(Object *child, void *opaque),
|
||||
void *opaque, bool recurse)
|
||||
{
|
||||
ObjectProperty *prop, *next;
|
||||
GHashTableIter iter;
|
||||
ObjectProperty *prop;
|
||||
int ret = 0;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(prop, &obj->properties, node, next) {
|
||||
g_hash_table_iter_init(&iter, obj->properties);
|
||||
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) {
|
||||
if (object_property_is_child(prop)) {
|
||||
Object *child = prop->opaque;
|
||||
|
||||
@ -833,7 +872,7 @@ void object_ref(Object *obj)
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
atomic_inc(&obj->ref);
|
||||
atomic_inc(&obj->ref);
|
||||
}
|
||||
|
||||
void object_unref(Object *obj)
|
||||
@ -841,7 +880,7 @@ void object_unref(Object *obj)
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
g_assert(obj->ref > 0);
|
||||
g_assert_cmpint(obj->ref, >, 0);
|
||||
|
||||
/* parent always holds a reference to its children */
|
||||
if (atomic_fetch_dec(&obj->ref) == 1) {
|
||||
@ -879,13 +918,11 @@ object_property_add(Object *obj, const char *name, const char *type,
|
||||
return ret;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
||||
if (strcmp(prop->name, name) == 0) {
|
||||
error_setg(errp, "attempt to add duplicate property '%s'"
|
||||
if (g_hash_table_lookup(obj->properties, name) != NULL) {
|
||||
error_setg(errp, "attempt to add duplicate property '%s'"
|
||||
" to object (type '%s')", name,
|
||||
object_get_typename(obj));
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prop = g_malloc0(sizeof(*prop));
|
||||
@ -898,7 +935,7 @@ object_property_add(Object *obj, const char *name, const char *type,
|
||||
prop->release = release;
|
||||
prop->opaque = opaque;
|
||||
|
||||
QTAILQ_INSERT_TAIL(&obj->properties, prop, node);
|
||||
g_hash_table_insert(obj->properties, prop->name, prop);
|
||||
return prop;
|
||||
}
|
||||
|
||||
@ -907,33 +944,52 @@ ObjectProperty *object_property_find(Object *obj, const char *name,
|
||||
{
|
||||
ObjectProperty *prop;
|
||||
|
||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
||||
if (strcmp(prop->name, name) == 0) {
|
||||
return prop;
|
||||
}
|
||||
prop = g_hash_table_lookup(obj->properties, name);
|
||||
if (prop) {
|
||||
return prop;
|
||||
}
|
||||
|
||||
error_setg(errp, "Property '.%s' not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ObjectPropertyIterator *object_property_iter_init(Object *obj)
|
||||
{
|
||||
ObjectPropertyIterator *ret = g_new0(ObjectPropertyIterator, 1);
|
||||
g_hash_table_iter_init(&ret->iter, obj->properties);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void object_property_iter_free(ObjectPropertyIterator *iter)
|
||||
{
|
||||
if (!iter) {
|
||||
return;
|
||||
}
|
||||
g_free(iter);
|
||||
}
|
||||
|
||||
ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
|
||||
{
|
||||
gpointer key, val;
|
||||
if (!g_hash_table_iter_next(&iter->iter, &key, &val)) {
|
||||
return NULL;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void object_property_del(Object *obj, const char *name, Error **errp)
|
||||
{
|
||||
ObjectProperty *prop = object_property_find(obj, name, errp);
|
||||
if (prop == NULL) {
|
||||
ObjectProperty *prop = g_hash_table_lookup(obj->properties, name);
|
||||
|
||||
if (!prop) {
|
||||
error_setg(errp, "Property '.%s' not found", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop->release) {
|
||||
prop->release(obj, name, prop->opaque);
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&obj->properties, prop, node);
|
||||
|
||||
g_free(prop->name);
|
||||
g_free(prop->type);
|
||||
g_free(prop->description);
|
||||
g_free(prop);
|
||||
g_hash_table_remove(obj->properties, name);
|
||||
}
|
||||
|
||||
void object_property_get(Object *obj, Visitor *v, const char *name,
|
||||
@ -1453,11 +1509,13 @@ void object_property_add_const_link(Object *obj, const char *name,
|
||||
gchar *object_get_canonical_path_component(Object *obj)
|
||||
{
|
||||
ObjectProperty *prop = NULL;
|
||||
GHashTableIter iter;
|
||||
|
||||
g_assert(obj);
|
||||
g_assert(obj->parent != NULL);
|
||||
|
||||
QTAILQ_FOREACH(prop, &obj->parent->properties, node) {
|
||||
g_hash_table_iter_init(&iter, obj->parent->properties);
|
||||
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) {
|
||||
if (!object_property_is_child(prop)) {
|
||||
continue;
|
||||
}
|
||||
@ -1541,11 +1599,13 @@ static Object *object_resolve_partial_path(Object *parent,
|
||||
bool *ambiguous)
|
||||
{
|
||||
Object *obj;
|
||||
GHashTableIter iter;
|
||||
ObjectProperty *prop;
|
||||
|
||||
obj = object_resolve_abs_path(parent, parts, typename, 0);
|
||||
|
||||
QTAILQ_FOREACH(prop, &parent->properties, node) {
|
||||
g_hash_table_iter_init(&iter, parent->properties);
|
||||
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) {
|
||||
Object *found;
|
||||
|
||||
if (!object_property_is_child(prop)) {
|
||||
|
@ -152,6 +152,148 @@ static const TypeInfo dummy_info = {
|
||||
.class_size = sizeof(DummyObjectClass),
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The following 3 object classes are used to
|
||||
* simulate the kind of relationships seen in
|
||||
* qdev, which result in complex object
|
||||
* property destruction ordering.
|
||||
*
|
||||
* DummyDev has a 'bus' child to a DummyBus
|
||||
* DummyBus has a 'backend' child to a DummyBackend
|
||||
* DummyDev has a 'backend' link to DummyBackend
|
||||
*
|
||||
* When DummyDev is finalized, it unparents the
|
||||
* DummyBackend, which unparents the DummyDev
|
||||
* which deletes the 'backend' link from DummyDev
|
||||
* to DummyBackend. This illustrates that the
|
||||
* object_property_del_all() method needs to
|
||||
* cope with the list of properties being changed
|
||||
* while it iterates over them.
|
||||
*/
|
||||
typedef struct DummyDev DummyDev;
|
||||
typedef struct DummyDevClass DummyDevClass;
|
||||
typedef struct DummyBus DummyBus;
|
||||
typedef struct DummyBusClass DummyBusClass;
|
||||
typedef struct DummyBackend DummyBackend;
|
||||
typedef struct DummyBackendClass DummyBackendClass;
|
||||
|
||||
#define TYPE_DUMMY_DEV "qemu-dummy-dev"
|
||||
#define TYPE_DUMMY_BUS "qemu-dummy-bus"
|
||||
#define TYPE_DUMMY_BACKEND "qemu-dummy-backend"
|
||||
|
||||
#define DUMMY_DEV(obj) \
|
||||
OBJECT_CHECK(DummyDev, (obj), TYPE_DUMMY_DEV)
|
||||
#define DUMMY_BUS(obj) \
|
||||
OBJECT_CHECK(DummyBus, (obj), TYPE_DUMMY_BUS)
|
||||
#define DUMMY_BACKEND(obj) \
|
||||
OBJECT_CHECK(DummyBackend, (obj), TYPE_DUMMY_BACKEND)
|
||||
|
||||
struct DummyDev {
|
||||
Object parent_obj;
|
||||
|
||||
DummyBus *bus;
|
||||
};
|
||||
|
||||
struct DummyDevClass {
|
||||
ObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct DummyBus {
|
||||
Object parent_obj;
|
||||
|
||||
DummyBackend *backend;
|
||||
};
|
||||
|
||||
struct DummyBusClass {
|
||||
ObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct DummyBackend {
|
||||
Object parent_obj;
|
||||
};
|
||||
|
||||
struct DummyBackendClass {
|
||||
ObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
static void dummy_dev_init(Object *obj)
|
||||
{
|
||||
DummyDev *dev = DUMMY_DEV(obj);
|
||||
DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS));
|
||||
DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND));
|
||||
|
||||
object_property_add_child(obj, "bus", OBJECT(bus), NULL);
|
||||
dev->bus = bus;
|
||||
object_property_add_child(OBJECT(bus), "backend", OBJECT(backend), NULL);
|
||||
bus->backend = backend;
|
||||
|
||||
object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND,
|
||||
(Object **)&bus->backend, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static void dummy_dev_unparent(Object *obj)
|
||||
{
|
||||
DummyDev *dev = DUMMY_DEV(obj);
|
||||
object_unparent(OBJECT(dev->bus));
|
||||
}
|
||||
|
||||
static void dummy_dev_class_init(ObjectClass *klass, void *opaque)
|
||||
{
|
||||
klass->unparent = dummy_dev_unparent;
|
||||
}
|
||||
|
||||
|
||||
static void dummy_bus_init(Object *obj)
|
||||
{
|
||||
}
|
||||
|
||||
static void dummy_bus_unparent(Object *obj)
|
||||
{
|
||||
DummyBus *bus = DUMMY_BUS(obj);
|
||||
object_property_del(obj->parent, "backend", NULL);
|
||||
object_unparent(OBJECT(bus->backend));
|
||||
}
|
||||
|
||||
static void dummy_bus_class_init(ObjectClass *klass, void *opaque)
|
||||
{
|
||||
klass->unparent = dummy_bus_unparent;
|
||||
}
|
||||
|
||||
static void dummy_backend_init(Object *obj)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo dummy_dev_info = {
|
||||
.name = TYPE_DUMMY_DEV,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(DummyDev),
|
||||
.instance_init = dummy_dev_init,
|
||||
.class_size = sizeof(DummyDevClass),
|
||||
.class_init = dummy_dev_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo dummy_bus_info = {
|
||||
.name = TYPE_DUMMY_BUS,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(DummyBus),
|
||||
.instance_init = dummy_bus_init,
|
||||
.class_size = sizeof(DummyBusClass),
|
||||
.class_init = dummy_bus_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo dummy_backend_info = {
|
||||
.name = TYPE_DUMMY_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(DummyBackend),
|
||||
.instance_init = dummy_backend_init,
|
||||
.class_size = sizeof(DummyBackendClass),
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void test_dummy_createv(void)
|
||||
{
|
||||
Error *err = NULL;
|
||||
@ -283,20 +425,83 @@ static void test_dummy_getenum(void)
|
||||
&err);
|
||||
g_assert(err != NULL);
|
||||
error_free(err);
|
||||
|
||||
object_unparent(OBJECT(dobj));
|
||||
}
|
||||
|
||||
|
||||
static void test_dummy_iterator(void)
|
||||
{
|
||||
Object *parent = object_get_objects_root();
|
||||
DummyObject *dobj = DUMMY_OBJECT(
|
||||
object_new_with_props(TYPE_DUMMY,
|
||||
parent,
|
||||
"dummy0",
|
||||
&error_abort,
|
||||
"bv", "yes",
|
||||
"sv", "Hiss hiss hiss",
|
||||
"av", "platypus",
|
||||
NULL));
|
||||
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator *iter;
|
||||
bool seenbv = false, seensv = false, seenav = false, seentype;
|
||||
|
||||
iter = object_property_iter_init(OBJECT(dobj));
|
||||
while ((prop = object_property_iter_next(iter))) {
|
||||
if (g_str_equal(prop->name, "bv")) {
|
||||
seenbv = true;
|
||||
} else if (g_str_equal(prop->name, "sv")) {
|
||||
seensv = true;
|
||||
} else if (g_str_equal(prop->name, "av")) {
|
||||
seenav = true;
|
||||
} else if (g_str_equal(prop->name, "type")) {
|
||||
/* This prop comes from the base Object class */
|
||||
seentype = true;
|
||||
} else {
|
||||
g_printerr("Found prop '%s'\n", prop->name);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
object_property_iter_free(iter);
|
||||
g_assert(seenbv);
|
||||
g_assert(seenav);
|
||||
g_assert(seensv);
|
||||
g_assert(seentype);
|
||||
|
||||
object_unparent(OBJECT(dobj));
|
||||
}
|
||||
|
||||
|
||||
static void test_dummy_delchild(void)
|
||||
{
|
||||
Object *parent = object_get_objects_root();
|
||||
DummyDev *dev = DUMMY_DEV(
|
||||
object_new_with_props(TYPE_DUMMY_DEV,
|
||||
parent,
|
||||
"dev0",
|
||||
&error_abort,
|
||||
NULL));
|
||||
|
||||
object_unparent(OBJECT(dev));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
type_register_static(&dummy_info);
|
||||
type_register_static(&dummy_dev_info);
|
||||
type_register_static(&dummy_bus_info);
|
||||
type_register_static(&dummy_backend_info);
|
||||
|
||||
g_test_add_func("/qom/proplist/createlist", test_dummy_createlist);
|
||||
g_test_add_func("/qom/proplist/createv", test_dummy_createv);
|
||||
g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
|
||||
g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
|
||||
g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
|
||||
g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
5
vl.c
5
vl.c
@ -1536,12 +1536,14 @@ MachineInfoList *qmp_query_machines(Error **errp)
|
||||
static int machine_help_func(QemuOpts *opts, MachineState *machine)
|
||||
{
|
||||
ObjectProperty *prop;
|
||||
ObjectPropertyIterator *iter;
|
||||
|
||||
if (!qemu_opt_has_help_opt(opts)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(prop, &OBJECT(machine)->properties, node) {
|
||||
iter = object_property_iter_init(OBJECT(machine));
|
||||
while ((prop = object_property_iter_next(iter))) {
|
||||
if (!prop->set) {
|
||||
continue;
|
||||
}
|
||||
@ -1554,6 +1556,7 @@ static int machine_help_func(QemuOpts *opts, MachineState *machine)
|
||||
error_printf("\n");
|
||||
}
|
||||
}
|
||||
object_property_iter_free(iter);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user