qom: add new dynamic property infrastructure based on Visitors (v2)
qdev properties are settable only during construction and static to classes. This isn't flexible enough for QOM. This patch introduces a property interface for qdev that provides dynamic properties that are tied to objects, instead of classes. These properties are Visitor based instead of string based too. Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
		
							parent
							
								
									85ed303bfe
								
							
						
					
					
						commit
						44677ded43
					
				
							
								
								
									
										97
									
								
								hw/qdev.c
								
								
								
								
							
							
						
						
									
										97
									
								
								hw/qdev.c
								
								
								
								
							| 
						 | 
					@ -98,6 +98,7 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
 | 
				
			||||||
        qdev_hot_added = true;
 | 
					        qdev_hot_added = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    dev->instance_id_alias = -1;
 | 
					    dev->instance_id_alias = -1;
 | 
				
			||||||
 | 
					    QTAILQ_INIT(&dev->properties);
 | 
				
			||||||
    dev->state = DEV_STATE_CREATED;
 | 
					    dev->state = DEV_STATE_CREATED;
 | 
				
			||||||
    return dev;
 | 
					    return dev;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -395,12 +396,31 @@ void qdev_init_nofail(DeviceState *dev)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void qdev_property_del_all(DeviceState *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    while (!QTAILQ_EMPTY(&dev->properties)) {
 | 
				
			||||||
 | 
					        DeviceProperty *prop = QTAILQ_FIRST(&dev->properties);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        QTAILQ_REMOVE(&dev->properties, prop, node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (prop->release) {
 | 
				
			||||||
 | 
					            prop->release(dev, prop->name, prop->opaque);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        g_free(prop->name);
 | 
				
			||||||
 | 
					        g_free(prop->type);
 | 
				
			||||||
 | 
					        g_free(prop);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Unlink device from bus and free the structure.  */
 | 
					/* Unlink device from bus and free the structure.  */
 | 
				
			||||||
void qdev_free(DeviceState *dev)
 | 
					void qdev_free(DeviceState *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    BusState *bus;
 | 
					    BusState *bus;
 | 
				
			||||||
    Property *prop;
 | 
					    Property *prop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qdev_property_del_all(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (dev->state == DEV_STATE_INITIALIZED) {
 | 
					    if (dev->state == DEV_STATE_INITIALIZED) {
 | 
				
			||||||
        while (dev->num_child_bus) {
 | 
					        while (dev->num_child_bus) {
 | 
				
			||||||
            bus = QLIST_FIRST(&dev->child_bus);
 | 
					            bus = QLIST_FIRST(&dev->child_bus);
 | 
				
			||||||
| 
						 | 
					@ -978,3 +998,80 @@ void qdev_unref(DeviceState *dev)
 | 
				
			||||||
    g_assert(dev->ref > 0);
 | 
					    g_assert(dev->ref > 0);
 | 
				
			||||||
    dev->ref--;
 | 
					    dev->ref--;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void qdev_property_add(DeviceState *dev, const char *name, const char *type,
 | 
				
			||||||
 | 
					                       DevicePropertyAccessor *get, DevicePropertyAccessor *set,
 | 
				
			||||||
 | 
					                       DevicePropertyRelease *release,
 | 
				
			||||||
 | 
					                       void *opaque, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceProperty *prop = g_malloc0(sizeof(*prop));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    prop->name = g_strdup(name);
 | 
				
			||||||
 | 
					    prop->type = g_strdup(type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    prop->get = get;
 | 
				
			||||||
 | 
					    prop->set = set;
 | 
				
			||||||
 | 
					    prop->release = release;
 | 
				
			||||||
 | 
					    prop->opaque = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTAILQ_INSERT_TAIL(&dev->properties, prop, node);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DeviceProperty *qdev_property_find(DeviceState *dev, const char *name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceProperty *prop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTAILQ_FOREACH(prop, &dev->properties, node) {
 | 
				
			||||||
 | 
					        if (strcmp(prop->name, name) == 0) {
 | 
				
			||||||
 | 
					            return prop;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void qdev_property_get(DeviceState *dev, Visitor *v, const char *name,
 | 
				
			||||||
 | 
					                       Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceProperty *prop = qdev_property_find(dev, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (prop == NULL) {
 | 
				
			||||||
 | 
					        error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!prop->get) {
 | 
				
			||||||
 | 
					        error_set(errp, QERR_PERMISSION_DENIED);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        prop->get(dev, v, prop->opaque, name, errp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void qdev_property_set(DeviceState *dev, Visitor *v, const char *name,
 | 
				
			||||||
 | 
					                       Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceProperty *prop = qdev_property_find(dev, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (prop == NULL) {
 | 
				
			||||||
 | 
					        error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!prop->set) {
 | 
				
			||||||
 | 
					        error_set(errp, QERR_PERMISSION_DENIED);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        prop->set(dev, prop->opaque, v, name, errp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *qdev_property_get_type(DeviceState *dev, const char *name, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    DeviceProperty *prop = qdev_property_find(dev, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (prop == NULL) {
 | 
				
			||||||
 | 
					        error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return prop->type;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										120
									
								
								hw/qdev.h
								
								
								
								
							
							
						
						
									
										120
									
								
								hw/qdev.h
								
								
								
								
							| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
#include "qemu-queue.h"
 | 
					#include "qemu-queue.h"
 | 
				
			||||||
#include "qemu-char.h"
 | 
					#include "qemu-char.h"
 | 
				
			||||||
#include "qemu-option.h"
 | 
					#include "qemu-option.h"
 | 
				
			||||||
 | 
					#include "qapi/qapi-visit-core.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct Property Property;
 | 
					typedef struct Property Property;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +28,44 @@ enum {
 | 
				
			||||||
    DEV_NVECTORS_UNSPECIFIED = -1,
 | 
					    DEV_NVECTORS_UNSPECIFIED = -1,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @DevicePropertyAccessor - called when trying to get/set a property
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dev the device that owns the property
 | 
				
			||||||
 | 
					 * @v the visitor that contains the property data
 | 
				
			||||||
 | 
					 * @opaque the device property opaque
 | 
				
			||||||
 | 
					 * @name the name of the property
 | 
				
			||||||
 | 
					 * @errp a pointer to an Error that is filled if getting/setting fails.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef void (DevicePropertyAccessor)(DeviceState *dev,
 | 
				
			||||||
 | 
					                                      Visitor *v,
 | 
				
			||||||
 | 
					                                      void *opaque,
 | 
				
			||||||
 | 
					                                      const char *name,
 | 
				
			||||||
 | 
					                                      Error **errp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @DevicePropertyRelease - called when a property is removed from a device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dev the device that owns the property
 | 
				
			||||||
 | 
					 * @name the name of the property
 | 
				
			||||||
 | 
					 * @opaque the opaque registered with the property
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef void (DevicePropertyRelease)(DeviceState *dev,
 | 
				
			||||||
 | 
					                                     const char *name,
 | 
				
			||||||
 | 
					                                     void *opaque);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct DeviceProperty
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    gchar *name;
 | 
				
			||||||
 | 
					    gchar *type;
 | 
				
			||||||
 | 
					    DevicePropertyAccessor *get;
 | 
				
			||||||
 | 
					    DevicePropertyAccessor *set;
 | 
				
			||||||
 | 
					    DevicePropertyRelease *release;
 | 
				
			||||||
 | 
					    void *opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTAILQ_ENTRY(DeviceProperty) node;
 | 
				
			||||||
 | 
					} DeviceProperty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This structure should not be accessed directly.  We declare it here
 | 
					/* This structure should not be accessed directly.  We declare it here
 | 
				
			||||||
   so that it can be embedded in individual device state structures.  */
 | 
					   so that it can be embedded in individual device state structures.  */
 | 
				
			||||||
struct DeviceState {
 | 
					struct DeviceState {
 | 
				
			||||||
| 
						 | 
					@ -51,6 +90,8 @@ struct DeviceState {
 | 
				
			||||||
     * more information.
 | 
					     * more information.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    uint32_t ref;
 | 
					    uint32_t ref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTAILQ_HEAD(, DeviceProperty) properties;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent);
 | 
					typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent);
 | 
				
			||||||
| 
						 | 
					@ -355,4 +396,83 @@ void qdev_ref(DeviceState *dev);
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void qdev_unref(DeviceState *dev);
 | 
					void qdev_unref(DeviceState *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @qdev_property_add - add a new property to a device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dev - the device to add a property to
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @name - the name of the property.  This can contain any character except for
 | 
				
			||||||
 | 
					 *         a forward slash.  In general, you should use hyphens '-' instead of
 | 
				
			||||||
 | 
					 *         underscores '_' when naming properties.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @type - the type name of the property.  This namespace is pretty loosely
 | 
				
			||||||
 | 
					 *         defined.  Sub namespaces are constructed by using a prefix and then
 | 
				
			||||||
 | 
					 *         to angle brackets.  For instance, the type 'virtio-net-pci' in the
 | 
				
			||||||
 | 
					 *         'link' namespace would be 'link<virtio-net-pci>'.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @get - the getter to be called to read a property.  If this is NULL, then
 | 
				
			||||||
 | 
					 *        the property cannot be read.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @set - the setter to be called to write a property.  If this is NULL,
 | 
				
			||||||
 | 
					 *        then the property cannot be written.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @release - called when the property is removed from the device.  This is
 | 
				
			||||||
 | 
					 *            meant to allow a property to free its opaque upon device
 | 
				
			||||||
 | 
					 *            destruction.  This may be NULL.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @opaque - an opaque pointer to pass to the callbacks for the property
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @errp - returns an error if this function fails
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void qdev_property_add(DeviceState *dev, const char *name, const char *type,
 | 
				
			||||||
 | 
					                       DevicePropertyAccessor *get, DevicePropertyAccessor *set,
 | 
				
			||||||
 | 
					                       DevicePropertyRelease *release,
 | 
				
			||||||
 | 
					                       void *opaque, Error **errp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @qdev_property_get - reads a property from a device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dev - the device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @v - the visitor that will receive the property value.  This should be an
 | 
				
			||||||
 | 
					 *      Output visitor and the data will be written with @name as the name.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @name - the name of the property
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @errp - returns an error if this function fails
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void qdev_property_get(DeviceState *dev, Visitor *v, const char *name,
 | 
				
			||||||
 | 
					                       Error **errp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @qdev_property_set - writes a property to a device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dev - the device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @v - the visitor that will be used to write the property value.  This should
 | 
				
			||||||
 | 
					 *      be an Input visitor and the data will be first read with @name as the
 | 
				
			||||||
 | 
					 *      name and then written as the property value.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @name - the name of the property
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @errp - returns an error if this function fails
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void qdev_property_set(DeviceState *dev, Visitor *v, const char *name,
 | 
				
			||||||
 | 
					                       Error **errp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @qdev_property_get_type - returns the type of a property
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @dev - the device
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @name - the name of the property
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @errp - returns an error if this function fails
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns:
 | 
				
			||||||
 | 
					 *   The type name of the property.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const char *qdev_property_get_type(DeviceState *dev, const char *name,
 | 
				
			||||||
 | 
					                                   Error **errp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								qerror.c
								
								
								
								
							
							
						
						
									
										4
									
								
								qerror.c
								
								
								
								
							| 
						 | 
					@ -185,6 +185,10 @@ static const QErrorStringTable qerror_table[] = {
 | 
				
			||||||
        .error_fmt = QERR_OPEN_FILE_FAILED,
 | 
					        .error_fmt = QERR_OPEN_FILE_FAILED,
 | 
				
			||||||
        .desc      = "Could not open '%(filename)'",
 | 
					        .desc      = "Could not open '%(filename)'",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        .error_fmt = QERR_PERMISSION_DENIED,
 | 
				
			||||||
 | 
					        .desc      = "Insufficient permission to perform this operation",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        .error_fmt = QERR_PROPERTY_NOT_FOUND,
 | 
					        .error_fmt = QERR_PROPERTY_NOT_FOUND,
 | 
				
			||||||
        .desc      = "Property '%(device).%(property)' not found",
 | 
					        .desc      = "Property '%(device).%(property)' not found",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								qerror.h
								
								
								
								
							
							
						
						
									
										3
									
								
								qerror.h
								
								
								
								
							| 
						 | 
					@ -156,6 +156,9 @@ QError *qobject_to_qerror(const QObject *obj);
 | 
				
			||||||
#define QERR_OPEN_FILE_FAILED \
 | 
					#define QERR_OPEN_FILE_FAILED \
 | 
				
			||||||
    "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
 | 
					    "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define QERR_PERMISSION_DENIED \
 | 
				
			||||||
 | 
					    "{ 'class': 'PermissionDenied', 'data': {} }"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define QERR_PROPERTY_NOT_FOUND \
 | 
					#define QERR_PROPERTY_NOT_FOUND \
 | 
				
			||||||
    "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }"
 | 
					    "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue