mirror of https://github.com/xemu-project/xemu.git
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
|
X: include/qom/cpu.h
|
||||||
F: qom/
|
F: qom/
|
||||||
X: qom/cpu.c
|
X: qom/cpu.c
|
||||||
|
F: tests/check-qom-interface.c
|
||||||
|
F: tests/check-qom-proplist.c
|
||||||
F: tests/qom-test.c
|
F: tests/qom-test.c
|
||||||
|
|
||||||
QMP
|
QMP
|
||||||
|
|
|
@ -657,6 +657,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
|
||||||
{
|
{
|
||||||
Object *root_container;
|
Object *root_container;
|
||||||
ObjectProperty *prop;
|
ObjectProperty *prop;
|
||||||
|
ObjectPropertyIterator *iter;
|
||||||
uint32_t drc_count = 0;
|
uint32_t drc_count = 0;
|
||||||
GArray *drc_indexes, *drc_power_domains;
|
GArray *drc_indexes, *drc_power_domains;
|
||||||
GString *drc_names, *drc_types;
|
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);
|
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;
|
Object *obj;
|
||||||
sPAPRDRConnector *drc;
|
sPAPRDRConnector *drc;
|
||||||
sPAPRDRConnectorClass *drck;
|
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));
|
spapr_drc_get_type_str(drc->type));
|
||||||
drc_types = g_string_insert_len(drc_types, -1, "\0", 1);
|
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
|
/* now write the drc count into the space we reserved at the
|
||||||
* beginning of the arrays previously
|
* beginning of the arrays previously
|
||||||
|
|
|
@ -237,7 +237,7 @@ struct BusState {
|
||||||
struct Property {
|
struct Property {
|
||||||
const char *name;
|
const char *name;
|
||||||
PropertyInfo *info;
|
PropertyInfo *info;
|
||||||
int offset;
|
ptrdiff_t offset;
|
||||||
uint8_t bitnr;
|
uint8_t bitnr;
|
||||||
qtype_code qtype;
|
qtype_code qtype;
|
||||||
int64_t defval;
|
int64_t defval;
|
||||||
|
|
|
@ -344,8 +344,6 @@ typedef struct ObjectProperty
|
||||||
ObjectPropertyResolve *resolve;
|
ObjectPropertyResolve *resolve;
|
||||||
ObjectPropertyRelease *release;
|
ObjectPropertyRelease *release;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
|
|
||||||
QTAILQ_ENTRY(ObjectProperty) node;
|
|
||||||
} ObjectProperty;
|
} ObjectProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -405,7 +403,7 @@ struct Object
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
ObjectClass *class;
|
ObjectClass *class;
|
||||||
ObjectFree *free;
|
ObjectFree *free;
|
||||||
QTAILQ_HEAD(, ObjectProperty) properties;
|
GHashTable *properties;
|
||||||
uint32_t ref;
|
uint32_t ref;
|
||||||
Object *parent;
|
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,
|
ObjectProperty *object_property_find(Object *obj, const char *name,
|
||||||
Error **errp);
|
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);
|
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
|
* Call @fn passing each child of @obj and @opaque to it, until @fn returns
|
||||||
* non-zero.
|
* 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.
|
* 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),
|
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
|
* 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.
|
* 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.
|
* Returns: The last value returned by @fn, or 0 if there is no child.
|
||||||
*/
|
*/
|
||||||
int object_child_foreach_recursive(Object *obj,
|
int object_child_foreach_recursive(Object *obj,
|
||||||
|
|
|
@ -137,6 +137,7 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
char *str, *info;
|
char *str, *info;
|
||||||
ObjectProperty *prop;
|
ObjectProperty *prop;
|
||||||
|
ObjectPropertyIterator *iter;
|
||||||
StringOutputVisitor *ov;
|
StringOutputVisitor *ov;
|
||||||
|
|
||||||
if (!nf->netdev_id) {
|
if (!nf->netdev_id) {
|
||||||
|
@ -173,7 +174,8 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
|
||||||
QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
|
QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
|
||||||
|
|
||||||
/* generate info str */
|
/* 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")) {
|
if (!strcmp(prop->name, "type")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -187,6 +189,7 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
|
||||||
g_free(str);
|
g_free(str);
|
||||||
g_free(info);
|
g_free(info);
|
||||||
}
|
}
|
||||||
|
object_property_iter_free(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netfilter_finalize(Object *obj)
|
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;
|
bool ambiguous = false;
|
||||||
ObjectPropertyInfoList *props = NULL;
|
ObjectPropertyInfoList *props = NULL;
|
||||||
ObjectProperty *prop;
|
ObjectProperty *prop;
|
||||||
|
ObjectPropertyIterator *iter;
|
||||||
|
|
||||||
obj = object_resolve_path(path, &ambiguous);
|
obj = object_resolve_path(path, &ambiguous);
|
||||||
if (obj == NULL) {
|
if (obj == NULL) {
|
||||||
|
@ -222,7 +223,8 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
|
||||||
return NULL;
|
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));
|
ObjectPropertyInfoList *entry = g_malloc0(sizeof(*entry));
|
||||||
|
|
||||||
entry->value = g_malloc0(sizeof(ObjectPropertyInfo));
|
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->name = g_strdup(prop->name);
|
||||||
entry->value->type = g_strdup(prop->type);
|
entry->value->type = g_strdup(prop->type);
|
||||||
}
|
}
|
||||||
|
object_property_iter_free(iter);
|
||||||
|
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
@ -503,6 +506,7 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
||||||
ObjectClass *klass;
|
ObjectClass *klass;
|
||||||
Object *obj;
|
Object *obj;
|
||||||
ObjectProperty *prop;
|
ObjectProperty *prop;
|
||||||
|
ObjectPropertyIterator *iter;
|
||||||
DevicePropertyInfoList *prop_list = NULL;
|
DevicePropertyInfoList *prop_list = NULL;
|
||||||
|
|
||||||
klass = object_class_by_name(typename);
|
klass = object_class_by_name(typename);
|
||||||
|
@ -531,7 +535,8 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
||||||
|
|
||||||
obj = object_new(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;
|
DevicePropertyInfo *info;
|
||||||
DevicePropertyInfoList *entry;
|
DevicePropertyInfoList *entry;
|
||||||
|
|
||||||
|
@ -562,6 +567,7 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
||||||
entry->next = prop_list;
|
entry->next = prop_list;
|
||||||
prop_list = entry;
|
prop_list = entry;
|
||||||
}
|
}
|
||||||
|
object_property_iter_free(iter);
|
||||||
|
|
||||||
object_unref(obj);
|
object_unref(obj);
|
||||||
|
|
||||||
|
|
146
qom/object.c
146
qom/object.c
|
@ -67,6 +67,10 @@ struct TypeImpl
|
||||||
InterfaceImpl interfaces[MAX_INTERFACES];
|
InterfaceImpl interfaces[MAX_INTERFACES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ObjectPropertyIterator {
|
||||||
|
GHashTableIter iter;
|
||||||
|
};
|
||||||
|
|
||||||
static Type type_interface;
|
static Type type_interface;
|
||||||
|
|
||||||
static GHashTable *type_table_get(void)
|
static GHashTable *type_table_get(void)
|
||||||
|
@ -261,7 +265,7 @@ static void type_initialize(TypeImpl *ti)
|
||||||
GSList *e;
|
GSList *e;
|
||||||
int i;
|
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);
|
memcpy(ti->class, parent->class, parent->class_size);
|
||||||
ti->class->interfaces = NULL;
|
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)
|
void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
|
||||||
{
|
{
|
||||||
Object *obj = data;
|
Object *obj = data;
|
||||||
|
@ -333,14 +347,15 @@ void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
|
||||||
g_assert(type != NULL);
|
g_assert(type != NULL);
|
||||||
type_initialize(type);
|
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(type->abstract == false);
|
||||||
g_assert(size >= type->instance_size);
|
g_assert_cmpint(size, >=, type->instance_size);
|
||||||
|
|
||||||
memset(obj, 0, type->instance_size);
|
memset(obj, 0, type->instance_size);
|
||||||
obj->class = type->class;
|
obj->class = type->class;
|
||||||
object_ref(obj);
|
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_init_with_type(obj, type);
|
||||||
object_post_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)
|
static void object_property_del_all(Object *obj)
|
||||||
{
|
{
|
||||||
while (!QTAILQ_EMPTY(&obj->properties)) {
|
ObjectProperty *prop;
|
||||||
ObjectProperty *prop = QTAILQ_FIRST(&obj->properties);
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
bool released;
|
||||||
|
|
||||||
QTAILQ_REMOVE(&obj->properties, prop, node);
|
do {
|
||||||
|
released = false;
|
||||||
if (prop->release) {
|
g_hash_table_iter_init(&iter, obj->properties);
|
||||||
prop->release(obj, prop->name, prop->opaque);
|
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_hash_table_unref(obj->properties);
|
||||||
g_free(prop->type);
|
|
||||||
g_free(prop->description);
|
|
||||||
g_free(prop);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void object_property_del_child(Object *obj, Object *child, Error **errp)
|
static void object_property_del_child(Object *obj, Object *child, Error **errp)
|
||||||
{
|
{
|
||||||
ObjectProperty *prop;
|
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) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,7 +450,7 @@ static void object_finalize(void *data)
|
||||||
object_property_del_all(obj);
|
object_property_del_all(obj);
|
||||||
object_deinit(obj, ti);
|
object_deinit(obj, ti);
|
||||||
|
|
||||||
g_assert(obj->ref == 0);
|
g_assert_cmpint(obj->ref, ==, 0);
|
||||||
if (obj->free) {
|
if (obj->free) {
|
||||||
obj->free(obj);
|
obj->free(obj);
|
||||||
}
|
}
|
||||||
|
@ -779,10 +816,12 @@ static int do_object_child_foreach(Object *obj,
|
||||||
int (*fn)(Object *child, void *opaque),
|
int (*fn)(Object *child, void *opaque),
|
||||||
void *opaque, bool recurse)
|
void *opaque, bool recurse)
|
||||||
{
|
{
|
||||||
ObjectProperty *prop, *next;
|
GHashTableIter iter;
|
||||||
|
ObjectProperty *prop;
|
||||||
int ret = 0;
|
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)) {
|
if (object_property_is_child(prop)) {
|
||||||
Object *child = prop->opaque;
|
Object *child = prop->opaque;
|
||||||
|
|
||||||
|
@ -833,7 +872,7 @@ void object_ref(Object *obj)
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
atomic_inc(&obj->ref);
|
atomic_inc(&obj->ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void object_unref(Object *obj)
|
void object_unref(Object *obj)
|
||||||
|
@ -841,7 +880,7 @@ void object_unref(Object *obj)
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_assert(obj->ref > 0);
|
g_assert_cmpint(obj->ref, >, 0);
|
||||||
|
|
||||||
/* parent always holds a reference to its children */
|
/* parent always holds a reference to its children */
|
||||||
if (atomic_fetch_dec(&obj->ref) == 1) {
|
if (atomic_fetch_dec(&obj->ref) == 1) {
|
||||||
|
@ -879,13 +918,11 @@ object_property_add(Object *obj, const char *name, const char *type,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
if (g_hash_table_lookup(obj->properties, name) != NULL) {
|
||||||
if (strcmp(prop->name, name) == 0) {
|
error_setg(errp, "attempt to add duplicate property '%s'"
|
||||||
error_setg(errp, "attempt to add duplicate property '%s'"
|
|
||||||
" to object (type '%s')", name,
|
" to object (type '%s')", name,
|
||||||
object_get_typename(obj));
|
object_get_typename(obj));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prop = g_malloc0(sizeof(*prop));
|
prop = g_malloc0(sizeof(*prop));
|
||||||
|
@ -898,7 +935,7 @@ object_property_add(Object *obj, const char *name, const char *type,
|
||||||
prop->release = release;
|
prop->release = release;
|
||||||
prop->opaque = opaque;
|
prop->opaque = opaque;
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&obj->properties, prop, node);
|
g_hash_table_insert(obj->properties, prop->name, prop);
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,33 +944,52 @@ ObjectProperty *object_property_find(Object *obj, const char *name,
|
||||||
{
|
{
|
||||||
ObjectProperty *prop;
|
ObjectProperty *prop;
|
||||||
|
|
||||||
QTAILQ_FOREACH(prop, &obj->properties, node) {
|
prop = g_hash_table_lookup(obj->properties, name);
|
||||||
if (strcmp(prop->name, name) == 0) {
|
if (prop) {
|
||||||
return prop;
|
return prop;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_setg(errp, "Property '.%s' not found", name);
|
error_setg(errp, "Property '.%s' not found", name);
|
||||||
return NULL;
|
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)
|
void object_property_del(Object *obj, const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
ObjectProperty *prop = object_property_find(obj, name, errp);
|
ObjectProperty *prop = g_hash_table_lookup(obj->properties, name);
|
||||||
if (prop == NULL) {
|
|
||||||
|
if (!prop) {
|
||||||
|
error_setg(errp, "Property '.%s' not found", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prop->release) {
|
if (prop->release) {
|
||||||
prop->release(obj, name, prop->opaque);
|
prop->release(obj, name, prop->opaque);
|
||||||
}
|
}
|
||||||
|
g_hash_table_remove(obj->properties, name);
|
||||||
QTAILQ_REMOVE(&obj->properties, prop, node);
|
|
||||||
|
|
||||||
g_free(prop->name);
|
|
||||||
g_free(prop->type);
|
|
||||||
g_free(prop->description);
|
|
||||||
g_free(prop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void object_property_get(Object *obj, Visitor *v, const char *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)
|
gchar *object_get_canonical_path_component(Object *obj)
|
||||||
{
|
{
|
||||||
ObjectProperty *prop = NULL;
|
ObjectProperty *prop = NULL;
|
||||||
|
GHashTableIter iter;
|
||||||
|
|
||||||
g_assert(obj);
|
g_assert(obj);
|
||||||
g_assert(obj->parent != NULL);
|
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)) {
|
if (!object_property_is_child(prop)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1541,11 +1599,13 @@ static Object *object_resolve_partial_path(Object *parent,
|
||||||
bool *ambiguous)
|
bool *ambiguous)
|
||||||
{
|
{
|
||||||
Object *obj;
|
Object *obj;
|
||||||
|
GHashTableIter iter;
|
||||||
ObjectProperty *prop;
|
ObjectProperty *prop;
|
||||||
|
|
||||||
obj = object_resolve_abs_path(parent, parts, typename, 0);
|
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;
|
Object *found;
|
||||||
|
|
||||||
if (!object_property_is_child(prop)) {
|
if (!object_property_is_child(prop)) {
|
||||||
|
|
|
@ -152,6 +152,148 @@ static const TypeInfo dummy_info = {
|
||||||
.class_size = sizeof(DummyObjectClass),
|
.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)
|
static void test_dummy_createv(void)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
@ -283,20 +425,83 @@ static void test_dummy_getenum(void)
|
||||||
&err);
|
&err);
|
||||||
g_assert(err != NULL);
|
g_assert(err != NULL);
|
||||||
error_free(err);
|
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)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
g_test_init(&argc, &argv, NULL);
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
|
||||||
module_call_init(MODULE_INIT_QOM);
|
module_call_init(MODULE_INIT_QOM);
|
||||||
type_register_static(&dummy_info);
|
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/createlist", test_dummy_createlist);
|
||||||
g_test_add_func("/qom/proplist/createv", test_dummy_createv);
|
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/badenum", test_dummy_badenum);
|
||||||
g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
|
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();
|
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)
|
static int machine_help_func(QemuOpts *opts, MachineState *machine)
|
||||||
{
|
{
|
||||||
ObjectProperty *prop;
|
ObjectProperty *prop;
|
||||||
|
ObjectPropertyIterator *iter;
|
||||||
|
|
||||||
if (!qemu_opt_has_help_opt(opts)) {
|
if (!qemu_opt_has_help_opt(opts)) {
|
||||||
return 0;
|
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) {
|
if (!prop->set) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1554,6 +1556,7 @@ static int machine_help_func(QemuOpts *opts, MachineState *machine)
|
||||||
error_printf("\n");
|
error_printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
object_property_iter_free(iter);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue