memory/vfio: notify region_del() when unregister listeners
cover memory/vfio: notify region_del() when unregister listeners
I encountered an event loss problem during unplugging vfio devices:
https://bugzilla.redhat.com/show_bug.cgi?id=1531393
I thought it should be a simple VT-d issue but I was wrong. The whole debugging leads me to these patches.
Basically I think what we missed is that when unregistering memory listeners, we don't really call region_del() at all. Instead we just remove ourselves from the listener list. IMHO that's not enough. A clean unregister should undo all possible changes that have done during region_add(). That's patch 1.
Patch 2 fixes a vfio issue when patch 1 is applied.
I'm marking this change as RFC since it touches the core of memory somehow, on which I am not 100% sure about. E.g., I haven't tested all the listener users, so I'm not sure whether it may broke any use case.
But what I'm sure is that it passes the docker tests on compiling/qtests, and it fixes the event loss that reported.
Let's see whether I can get some feedback first. Please review.
Thanks.
Peter Xu (2):
memory: do explicit cleanup when remove listeners
vfio: listener unregister before unset container
hw/vfio/common.c | 16 ++++++++++++---- memory.c | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-)
Signature · 1 line
2.14.3
RFC 1/2 memory: do explicit cleanup when remove listeners
When unregister memory listeners, we should call, e.g., region_del() (and possibly other undo operations) on every existing memory region sections there, otherwise we may leak resources that are held during the region_add(). This patch undo the stuff for the listeners, which emulates the case when the address space is set from current to an empty state.
I found this problem when debugging a refcount leak issue that leads to a device unplug event lost (please see the "Bug:" line below). In that case, the leakage of resource is the PCI BAR memory region refcount. And since memory regions are not keeping their own refcount but onto their owners, so the vfio-pci device's (who is the owner of the PCI BAR memory regions) refcount is leaked, and event missing.
--- memory.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
memory.c +24 -0
@@ -2609,6 +2609,29 @@ static void listener_add_address_space(MemoryListener *listener,flatview_unref (view); } +static void listener_del_address_space (MemoryListener *listener, +AddressSpace *as) +{ +FlatView *view; +FlatRange *fr; + + view =address_space_get_flatview (as); + FOR_EACH_FLAT_RANGE(fr, view) { +MemoryRegionSection section =section_from_flat_range (fr, view); + +if (fr->dirty_log_mask && listener->log_stop) { + listener->log_stop (listener, §ion, fr->dirty_log_mask,0 ); + } +if (listener->region_del) { + listener->region_del (listener, §ion); + } + } +if (listener->commit) { + listener->commit (listener); + } +flatview_unref (view); +} +void memory_listener_register (MemoryListener *listener,AddressSpace *as) {MemoryListener *other =NULL ;
@@ -2649,6 +2672,7 @@ void memory_listener_unregister(MemoryListener *listener)return ; } +listener_del_address_space (listener, listener->address_space);QTAILQ_REMOVE (&memory_listeners, listener, link);QTAILQ_REMOVE (&listener->address_space->listeners, listener, link_as); listener->address_space =NULL ;
Signature · 1 line
2.14.3
Re: [Qemu-devel] [RFC 1/2] memory: do explicit cleanup when remove listeners
Quoted 1 line · Peter Xu
+
FOR_EACH_FLAT_RANGE(fr, view){
listener->begin is missing before the loop.
Paolo
Quoted 13 lines
memory.c +13
-0+MemoryRegionSection section =section_from_flat_range (fr, view); + +if (fr->dirty_log_mask && listener->log_stop) { + listener->log_stop (listener, §ion, fr->dirty_log_mask,0 ); + } +if (listener->region_del) { + listener->region_del (listener, §ion); + } + } +if (listener->commit) { + listener->commit (listener); + } +flatview_unref (view);
RFC 2/2 vfio: listener unregister before unset container
In reply to Peter XuAfter previous patch, listener unregister will need the container to be alive. Let's move this unregister phase to be before unset container, since that operation will free the backend container in kernel, then we'll get these after previous patch:
qemu-system-x86_64: VFIO_UNMAP_DMA: -22
qemu-system-x86_64: vfio_dma_unmap(0x559bf53a4590, 0x0, 0xa0000) = -22 (Invalid argument)
--- hw/vfio/common.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
hw/vfio/common.c +12 -4
@@ -1161,19 +1161,27 @@ static void vfio_disconnect_container(VFIOGroup *group) {VFIOContainer *container = group->container; +QLIST_REMOVE (group, container_next); + group->container =NULL ; + +/* +* Explicitly release the listener first before unset container, +* since unset may destroy the backend container if it's the last +* group. +*/ +if (QLIST_EMPTY (&container->group_list)) { +vfio_listener_release (container); + } +if (ioctl (group->fd,VFIO_GROUP_UNSET_CONTAINER , &container->fd)) {error_report ("vfio: error disconnecting group %d from container" , group->groupid); } -QLIST_REMOVE (group, container_next); - group->container =NULL ; -if (QLIST_EMPTY (&container->group_list)) {VFIOAddressSpace *space = container->space;VFIOGuestIOMMU *giommu, *tmp; -vfio_listener_release (container);QLIST_REMOVE (container, next); QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) {
Signature · 1 line
2.14.3
Quoted 7 lines · Peter Xu
I encountered an event loss problem during unplugging vfio devices:
https://bugzilla.redhat.com/show_bug.cgi?id=1531393
I thought it should be a simple VT-d issue but I was wrong. The whole debugging leads me to these patches.
Quoted 7 lines
Basically I think what we missed is that when unregistering memory listeners, we don't really call
region_del()at all. Instead we just remove ourselves from the listener list. IMHO that's not enough. A clean unregister should undo all possible changes that have done duringregion_add(). That's patch 1.Patch 2 fixes a vfio issue when patch 1 is applied.
It makes sense, though of course patch 1 must come second for bisectability. Of the other listeners, most do not implement region_del, but commit must be audited as well. What matters is whether the listener is unregistered, and only few are:
- kvm_arm_machine_init_done must unregister the listener after the QSLIST_FOREACH_SAFE loop.
- Xen seems okay
- vhost needs to remove the memory_region_unref loop after unregistering the listener - and this must be done at the same time as patch 1, not before
- virtio seems okay
Thanks,
Paolo
Quoted 16 lines · Paolo Bonzini
Quoted 7 lines · Peter Xu
I encountered an event loss problem during unplugging vfio devices:
https://bugzilla.redhat.com/show_bug.cgi?id=1531393
I thought it should be a simple VT-d issue but I was wrong. The whole debugging leads me to these patches.
Quoted 7 lines
Basically I think what we missed is that when unregistering memory listeners, we don't really call
region_del()at all. Instead we just remove ourselves from the listener list. IMHO that's not enough. A clean unregister should undo all possible changes that have done duringregion_add(). That's patch 1.Patch 2 fixes a vfio issue when patch 1 is applied.
Quoted 7 lines
It makes sense, though of course patch 1 must come second for bisectability. Of the other listeners, most do not implement
region_del, but commit must be audited as well. What matters is whether the listener is unregistered, and only few are:-
kvm_arm_machine_init_donemust unregister the listener after theQSLIST_FOREACH_SAFEloop.
I'll add another patch for this. I noticed that we haven't really do the list cleanup on kvm_devices_head. Say, the items are still on the list even after they are freed in kvm_arm_machine_init_done():
QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) {
if (kd->kda.addr != -1) {
kvm_arm_set_device_addr(kd);
}
memory_region_unref(kd->mr);
g_free(kd);
}I think it's pretty safe as long as we don't access kvm_devices_head any more (that's what we did), but would it be better to clean the list head too? (CC PeterM too)
Anyway, I'll ignore this for my series and only do what's needed for the memory listener fix.
Quoted 5 lines
- Xen seems okay
- vhost needs to remove the
memory_region_unrefloop after unregistering the listener - and this must be done at the same time as patch 1, not before
Noted. I'll also fix the begin() missing problem you pointed out in the other comment. Thanks!
Quoted 6 lines
- virtio seems okay
Thanks,
Paolo
Signature · 1 line
Peter Xu