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; | ||||
|     } | ||||
|     dev->instance_id_alias = -1; | ||||
|     QTAILQ_INIT(&dev->properties); | ||||
|     dev->state = DEV_STATE_CREATED; | ||||
|     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.  */ | ||||
| void qdev_free(DeviceState *dev) | ||||
| { | ||||
|     BusState *bus; | ||||
|     Property *prop; | ||||
| 
 | ||||
|     qdev_property_del_all(dev); | ||||
| 
 | ||||
|     if (dev->state == DEV_STATE_INITIALIZED) { | ||||
|         while (dev->num_child_bus) { | ||||
|             bus = QLIST_FIRST(&dev->child_bus); | ||||
| @ -978,3 +998,80 @@ void qdev_unref(DeviceState *dev) | ||||
|     g_assert(dev->ref > 0); | ||||
|     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-char.h" | ||||
| #include "qemu-option.h" | ||||
| #include "qapi/qapi-visit-core.h" | ||||
| 
 | ||||
| typedef struct Property Property; | ||||
| 
 | ||||
| @ -27,6 +28,44 @@ enum { | ||||
|     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
 | ||||
|    so that it can be embedded in individual device state structures.  */ | ||||
| struct DeviceState { | ||||
| @ -51,6 +90,8 @@ struct DeviceState { | ||||
|      * more information. | ||||
|      */ | ||||
|     uint32_t ref; | ||||
| 
 | ||||
|     QTAILQ_HEAD(, DeviceProperty) properties; | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| /**
 | ||||
|  * @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 | ||||
|  | ||||
							
								
								
									
										4
									
								
								qerror.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								qerror.c
									
									
									
									
									
								
							| @ -185,6 +185,10 @@ static const QErrorStringTable qerror_table[] = { | ||||
|         .error_fmt = QERR_OPEN_FILE_FAILED, | ||||
|         .desc      = "Could not open '%(filename)'", | ||||
|     }, | ||||
|     { | ||||
|         .error_fmt = QERR_PERMISSION_DENIED, | ||||
|         .desc      = "Insufficient permission to perform this operation", | ||||
|     }, | ||||
|     { | ||||
|         .error_fmt = QERR_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 \ | ||||
|     "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }" | ||||
| 
 | ||||
| #define QERR_PERMISSION_DENIED \ | ||||
|     "{ 'class': 'PermissionDenied', 'data': {} }" | ||||
| 
 | ||||
| #define QERR_PROPERTY_NOT_FOUND \ | ||||
|     "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }" | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Anthony Liguori
						Anthony Liguori