@samitouri / QOSamiQemu / commits / 9c3b2c9db5

qom: replace 'can_be_deleted' with 'prepare_delete'

While most objects can perform all their cleanup in the finalizer method, there can be interactions with other resources / subsystems / threads which require that some cleanup be performed on an user creatable object before unparenting it and entering finalization. The current 'can_be_deleted' method runs in the deletion path and is intended to be used to block deletion. While it could be used to perform cleanup tasks, its name suggests it should be free of side-effects. Generalize this by renaming it to 'prepare_delete', explicitly allowing for cleanup to be provided. Existing users of 'can_be_deleted' are re-written, which provides them with more detailed/tailored error messages. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20260706135824.2623960-2-berrange@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>

Daniel P. Berrangé committed Jul 6, 2026 at 14:57 UTC 9c3b2c9db50b7110db7fe294903946a07675ff3b
11 files changed +68 -39
backends/cryptodev.c
+9 -3
@@ -454,9 +454,15 @@ bool cryptodev_backend_is_ready(CryptoDevBackend *backend)
454 }
455
456 static bool
457 -cryptodev_backend_can_be_deleted(UserCreatable *uc)
457 +cryptodev_backend_prepare_delete(UserCreatable *uc, Error **errp)
458 {
459 - return !cryptodev_backend_is_used(CRYPTODEV_BACKEND(uc));
459 + if (cryptodev_backend_is_used(CRYPTODEV_BACKEND(uc))) {
460 + error_setg(errp,
461 + "Cannot delete cryptodev backend '%s' that is in use",
462 + object_get_canonical_path_component(OBJECT(uc)));
463 + return false;
464 + }
465 + return true;
466 }
467
468 static void cryptodev_backend_instance_init(Object *obj)
@@ -613,7 +619,7 @@ cryptodev_backend_class_init(ObjectClass *oc, const void *data)
619 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
620
621 ucc->complete = cryptodev_backend_complete;
616 - ucc->can_be_deleted = cryptodev_backend_can_be_deleted;
622 + ucc->prepare_delete = cryptodev_backend_prepare_delete;
623
624 QTAILQ_INIT(&crypto_clients);
625 object_class_property_add(oc, "queues", "uint32",
backends/hostmem.c
+5 -2
@@ -434,9 +434,12 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
434 }
435
436 static bool
437 -host_memory_backend_can_be_deleted(UserCreatable *uc)
437 +host_memory_backend_prepare_delete(UserCreatable *uc, Error **errp)
438 {
439 if (host_memory_backend_is_mapped(MEMORY_BACKEND(uc))) {
440 + error_setg(errp,
441 + "Cannot delete host memory backend '%s' which is mapped",
442 + object_get_canonical_path_component(OBJECT(uc)));
443 return false;
444 } else {
445 return true;
@@ -508,7 +511,7 @@ host_memory_backend_class_init(ObjectClass *oc, const void *data)
511 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
512
513 ucc->complete = host_memory_backend_memory_complete;
511 - ucc->can_be_deleted = host_memory_backend_can_be_deleted;
514 + ucc->prepare_delete = host_memory_backend_prepare_delete;
515
516 object_class_property_add_bool(oc, "merge",
517 host_memory_backend_get_merge,
backends/iommufd.c
+8 -3
@@ -63,11 +63,16 @@ static void iommufd_backend_set_fd(Object *obj, const char *str, Error **errp)
63 trace_iommu_backend_set_fd(be->fd);
64 }
65
66 -static bool iommufd_backend_can_be_deleted(UserCreatable *uc)
66 +static bool iommufd_backend_prepare_delete(UserCreatable *uc, Error **errp)
67 {
68 IOMMUFDBackend *be = IOMMUFD_BACKEND(uc);
69
70 - return !be->users;
70 + if (be->users) {
71 + error_setg(errp, "Can not delete IOMMUFD backend '%s' with %d users",
72 + object_get_canonical_path_component(OBJECT(uc)), be->users);
73 + return false;
74 + }
75 + return true;
76 }
77
78 static void iommufd_backend_complete(UserCreatable *uc, Error **errp)
@@ -92,7 +97,7 @@ static void iommufd_backend_class_init(ObjectClass *oc, const void *data)
97 {
98 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
99
95 - ucc->can_be_deleted = iommufd_backend_can_be_deleted;
100 + ucc->prepare_delete = iommufd_backend_prepare_delete;
101 ucc->complete = iommufd_backend_complete;
102
103 object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd);
block/throttle-groups.c
+9 -3
@@ -960,9 +960,15 @@ static void throttle_group_get_limits(Object *obj, Visitor *v,
960 visit_type_ThrottleLimits(v, name, &argp, errp);
961 }
962
963 -static bool throttle_group_can_be_deleted(UserCreatable *uc)
963 +static bool throttle_group_prepare_delete(UserCreatable *uc, Error **errp)
964 {
965 - return OBJECT(uc)->ref == 1;
965 + if (OBJECT(uc)->ref > 1) {
966 + error_setg(errp,
967 + "Cannot delete throttle group '%s' with active references",
968 + object_get_canonical_path_component(OBJECT(uc)));
969 + return false;
970 + }
971 + return true;
972 }
973
974 static void throttle_group_obj_class_init(ObjectClass *klass,
@@ -972,7 +978,7 @@ static void throttle_group_obj_class_init(ObjectClass *klass,
978 UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
979
980 ucc->complete = throttle_group_obj_complete;
975 - ucc->can_be_deleted = throttle_group_can_be_deleted;
981 + ucc->prepare_delete = throttle_group_prepare_delete;
982
983 /* individual properties */
984 for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) {
event-loop-base.c
+4 -4
@@ -85,13 +85,13 @@ static void event_loop_base_complete(UserCreatable *uc, Error **errp)
85 }
86 }
87
88 -static bool event_loop_base_can_be_deleted(UserCreatable *uc)
88 +static bool event_loop_base_prepare_delete(UserCreatable *uc, Error **errp)
89 {
90 EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
91 EventLoopBase *backend = EVENT_LOOP_BASE(uc);
92
93 - if (bc->can_be_deleted) {
94 - return bc->can_be_deleted(backend);
93 + if (bc->prepare_delete) {
94 + return bc->prepare_delete(backend, errp);
95 }
96
97 return true;
@@ -102,7 +102,7 @@ static void event_loop_base_class_init(ObjectClass *klass,
102 {
103 UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
104 ucc->complete = event_loop_base_complete;
105 - ucc->can_be_deleted = event_loop_base_can_be_deleted;
105 + ucc->prepare_delete = event_loop_base_prepare_delete;
106
107 object_class_property_add(klass, "aio-max-batch", "int",
108 event_loop_base_get_param,
include/qom/object_interfaces.h
+17 -9
@@ -20,8 +20,10 @@ typedef struct UserCreatable UserCreatable;
20 * UserCreatableClass:
21 * @parent_class: the base class
22 * @complete: callback to be called after @obj's properties are set.
23 - * @can_be_deleted: callback to be called before an object is removed
24 - * to check if @obj can be removed safely.
23 + * @prepare_delete: to be called before an attempt to delete @obj
24 + * to validate whether the object can be deleted and trigger any
25 + * cleanup of any resources which have to be dealt with before the
26 + * object is unparented and enters finalization.
27 *
28 * Interface is designed to work with -object/object-add/object_add
29 * commands.
@@ -36,7 +38,9 @@ typedef struct UserCreatable UserCreatable;
38 * For objects created without using -object/object-add/object_add,
39 * @user_creatable_complete() wrapper should be called manually if
40 * object's type implements USER_CREATABLE interface and needs
39 - * complete() callback to be called.
41 + * complete() callback to be called. Similarly @user_creatable_prepare_delete()
42 + * should be called manually prior to an attempt to the delete the
43 + * object.
44 */
45 struct UserCreatableClass {
46 /* <private> */
@@ -44,7 +48,7 @@ struct UserCreatableClass {
48
49 /* <public> */
50 void (*complete)(UserCreatable *uc, Error **errp);
47 - bool (*can_be_deleted)(UserCreatable *uc);
51 + bool (*prepare_delete)(UserCreatable *uc, Error **errp);
52 };
53
54 /**
@@ -61,13 +65,17 @@ struct UserCreatableClass {
65 bool user_creatable_complete(UserCreatable *uc, Error **errp);
66
67 /**
64 - * user_creatable_can_be_deleted:
65 - * @uc: the object whose can_be_deleted() method is called if implemented
68 + * user_creatable_prepare_delete:
69 + * @uc: the user-creatable object whose prepare_delete() method is called
70 + * @errp: if an error occurs, a pointer to an area to store the error
71 + *
72 + * Wrapper to call prepare_delete() class method if defined, otherwise
73 + * does nothing.
74 *
67 - * Wrapper to call can_be_deleted() method if one of types it's inherited
68 - * from implements USER_CREATABLE interface.
75 + * Returns: %true on success or if prepare_delete() is not defined,
76 + * %false on failure.
77 */
70 -bool user_creatable_can_be_deleted(UserCreatable *uc);
78 +bool user_creatable_prepare_delete(UserCreatable *uc, Error **errp);
79
80 /**
81 * user_creatable_add_qapi:
include/system/event-loop-base.h
+1 -1
@@ -24,7 +24,7 @@ struct EventLoopBaseClass {
24
25 void (*init)(EventLoopBase *base, Error **errp);
26 void (*update_params)(EventLoopBase *base, Error **errp);
27 - bool (*can_be_deleted)(EventLoopBase *base);
27 + bool (*prepare_delete)(EventLoopBase *base, Error **errp);
28 };
29
30 struct EventLoopBase {
net/can/can_core.c
+4 -2
@@ -143,8 +143,10 @@ int can_bus_client_set_filters(CanBusClientState *client,
143 }
144
145
146 -static bool can_bus_can_be_deleted(UserCreatable *uc)
146 +static bool can_bus_prepare_delete(UserCreatable *uc, Error **errp)
147 {
148 + error_setg(errp, "Deleting CAN bus device '%s' is not supported",
149 + object_get_canonical_path_component(OBJECT(uc)));
150 return false;
151 }
152
@@ -153,7 +155,7 @@ static void can_bus_class_init(ObjectClass *klass,
155 {
156 UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass);
157
156 - uc_klass->can_be_deleted = can_bus_can_be_deleted;
158 + uc_klass->prepare_delete = can_bus_prepare_delete;
159 }
160
161 static const TypeInfo can_bus_info = {
qom/object_interfaces.c
+5 -8
@@ -32,16 +32,14 @@ bool user_creatable_complete(UserCreatable *uc, Error **errp)
32 return !*errp;
33 }
34
35 -bool user_creatable_can_be_deleted(UserCreatable *uc)
35 +bool user_creatable_prepare_delete(UserCreatable *uc, Error **errp)
36 {
37 -
37 UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc);
38
40 - if (ucc->can_be_deleted) {
41 - return ucc->can_be_deleted(uc);
42 - } else {
43 - return true;
39 + if (ucc->prepare_delete) {
40 + return ucc->prepare_delete(uc, errp);
41 }
42 + return true;
43 }
44
45 void user_creatable_add_qapi(ObjectOptions *options, Error **errp)
@@ -253,8 +251,7 @@ bool user_creatable_del(const char *id, Error **errp)
251 return false;
252 }
253
256 - if (!user_creatable_can_be_deleted(USER_CREATABLE(obj))) {
257 - error_setg(errp, "object '%s' is in use, can not be deleted", id);
254 + if (!user_creatable_prepare_delete(USER_CREATABLE(obj), errp)) {
255 return false;
256 }
257
tests/qemu-iotests/245
+2 -2
@@ -801,7 +801,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
801 # Now group1 is in use, it cannot be deleted
802 result = self.vm.qmp('object-del', id = 'group1')
803 self.assert_qmp(result, 'error/class', 'GenericError')
804 - self.assert_qmp(result, 'error/desc', "object 'group1' is in use, can not be deleted")
804 + self.assert_qmp(result, 'error/desc', "Cannot delete throttle group 'group1' with active references")
805
806 # Default options, this switches the group back to group0
807 self.reopen(opts)
@@ -809,7 +809,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
809 # So now we cannot delete group0
810 result = self.vm.qmp('object-del', id = 'group0')
811 self.assert_qmp(result, 'error/class', 'GenericError')
812 - self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted")
812 + self.assert_qmp(result, 'error/desc', "Cannot delete throttle group 'group0' with active references")
813
814 # But group1 is free this time, and it can be deleted
815 self.vm.cmd('object-del', id = 'group1')
util/main-loop.c
+4 -2
@@ -218,8 +218,10 @@ static void main_loop_init(EventLoopBase *base, Error **errp)
218 mloop = m;
219 }
220
221 -static bool main_loop_can_be_deleted(EventLoopBase *base)
221 +static bool main_loop_prepare_delete(EventLoopBase *base, Error **errp)
222 {
223 + error_setg(errp, "Deleting main loop '%s' is not supported",
224 + object_get_canonical_path_component(OBJECT(base)));
225 return false;
226 }
227
@@ -229,7 +231,7 @@ static void main_loop_class_init(ObjectClass *oc, const void *class_data)
231
232 bc->init = main_loop_init;
233 bc->update_params = main_loop_update_params;
232 - bc->can_be_deleted = main_loop_can_be_deleted;
234 + bc->prepare_delete = main_loop_prepare_delete;
235 }
236
237 static const TypeInfo main_loop_info = {