memory/vfio: notify region_del() when unregister listeners
v2 cover memory/vfio: notify region_del() when unregister listeners
v2
- add begin() hooks [Paolo]
- move vfio patch to front [Paolo]
- one more patch for arm devlistener unregister [Paolo]
- one more patch for vhost traces
- removing RFC tag
This series fixes bug reported here:
https://bugzilla.redhat.com/show_bug.cgi?id=1531393
The first patch only adds traces for vhost, with which I tested on vhost to make sure that my last patch works as expected with vhost. Please pick it if anyone wants, or ignore it if no one likes it.
The 2nd patch is the arm fix for preparation of the last patch.
The 3rd patch is the vfio fix for preparation of the last patch.
The 4th patch is the core fix of the problem.
Please review, thanks.
Thanks.
Peter Xu (4):
vhost: add traces for memory listeners
arm: postpone device listener unregister
vfio: listener unregister before unset container
memory: do explicit cleanup when remove listeners
hw/vfio/common.c | 16 ++++++++++++---- hw/virtio/trace-events | 6 ++++++ hw/virtio/vhost.c | 11 +++++++---- memory.c | 27 +++++++++++++++++++++++++++ target/arm/kvm.c | 2 +- 5 files changed, 53 insertions(+), 9 deletions(-)
Signature · 1 line
2.14.3
PATCH v2 1/4 vhost: add traces for memory listeners
Trace these operations on two memory listeners. It helps to verify the new memory listener fix, and good to keep them there.
--- hw/virtio/trace-events | 6 ++++++ hw/virtio/vhost.c | 7 +++++++ 2 files changed, 13 insertions(+)
hw/virtio/trace-events +6 -0
@@ -25,3 +25,9 @@ virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s g virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d actual: %d" virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d" virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: 0x%"PRIx64" num_pages: %d" + +# hw/virtio/vhost.c +vhost_region_add(void *p, const char *mr) "dev %p mr %s" +vhost_region_del(void *p, const char *mr) "dev %p mr %s" +vhost_iommu_region_add(void *p, const char *mr) "dev %p mr %s" +vhost_iommu_region_del(void *p, const char *mr) "dev %p mr %s"
hw/virtio/vhost.c +7 -0
@@ -27,6 +27,7 @@#include "hw/virtio/virtio-access.h" #include "migration/blocker.h" #include "sysemu/dma.h" +#include "trace.h" /* enabled until disconnected backend stabilizes */ #define _VHOST_DEBUG 1
@@ -687,6 +688,7 @@ static void vhost_region_add(MemoryListener *listener,return ; } +trace_vhost_region_add (dev, section->mr->name ?:NULL ); ++dev->n_mem_sections; dev->mem_sections =g_renew (MemoryRegionSection, dev->mem_sections, dev->n_mem_sections);
@@ -706,6 +708,7 @@ static void vhost_region_del(MemoryListener *listener,return ; } +trace_vhost_region_del (dev, section->mr->name ?:NULL );vhost_set_memory (listener, section, false);memory_region_unref (section->mr);for (i =0 ; i < dev->n_mem_sections; ++i) {
@@ -743,6 +746,8 @@ static void vhost_iommu_region_add(MemoryListener *listener,return ; } +trace_vhost_iommu_region_add (dev, section->mr->name ?:NULL ); + iommu =g_malloc0 (sizeof (*iommu)); end =int128_add (int128_make64 (section->offset_within_region), section->size);
@@ -771,6 +776,8 @@ static void vhost_iommu_region_del(MemoryListener *listener,return ; } +trace_vhost_iommu_region_del (dev, section->mr->name ?:NULL ); + QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) {if (iommu->mr == section->mr && iommu->n.start == section->offset_within_region) {
Signature · 1 line
2.14.3
Re: [Qemu-devel] [PATCH v2 1/4] vhost: add traces for memory listeners
Quoted 4 lines · Peter Xu
Trace these operations on two memory listeners. It helps to verify the new memory listener fix, and good to keep them there.
Signed-off-by Peter Xu <[email protected]>
I'm not sure what this have to do with vfio, but anyway:
Quoted 67 lines
--- hw/virtio/trace-events | 6 ++++++ hw/virtio/vhost.c | 7 +++++++ 2 files changed, 13 insertions(+)hw/virtio/trace-events +6
-0@@ -25,3 +25,9 @@ virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s g virtio_balloon_get_config(uint32_t num_pages, uint32_t actual) "num_pages: %d actual: %d" virtio_balloon_set_config(uint32_t actual, uint32_t oldactual) "actual: %d oldactual: %d" virtio_balloon_to_target(uint64_t target, uint32_t num_pages) "balloon target: 0x%"PRIx64" num_pages: %d" + +# hw/virtio/vhost.c +vhost_region_add(void *p, const char *mr) "dev %p mr %s" +vhost_region_del(void *p, const char *mr) "dev %p mr %s" +vhost_iommu_region_add(void *p, const char *mr) "dev %p mr %s" +vhost_iommu_region_del(void *p, const char *mr) "dev %p mr %s"hw/virtio/vhost.c +7
-0@@ -27,6 +27,7 @@#include "hw/virtio/virtio-access.h" #include "migration/blocker.h" #include "sysemu/dma.h" +#include "trace.h" /* enabled until disconnected backend stabilizes */ #define _VHOST_DEBUG 1@@ -687,6 +688,7 @@ static void vhost_region_add(MemoryListener *listener,return ; } +trace_vhost_region_add (dev, section->mr->name ?:NULL ); ++dev->n_mem_sections; dev->mem_sections =g_renew (MemoryRegionSection, dev->mem_sections, dev->n_mem_sections);@@ -706,6 +708,7 @@ static void vhost_region_del(MemoryListener *listener,return ; } +trace_vhost_region_del (dev, section->mr->name ?:NULL );vhost_set_memory (listener, section, false);memory_region_unref (section->mr);for (i =0 ; i < dev->n_mem_sections; ++i) {@@ -743,6 +746,8 @@ static void vhost_iommu_region_add(MemoryListener *listener,return ; } +trace_vhost_iommu_region_add (dev, section->mr->name ?:NULL ); + iommu =g_malloc0 (sizeof (*iommu)); end =int128_add (int128_make64 (section->offset_within_region), section->size);@@ -771,6 +776,8 @@ static void vhost_iommu_region_del(MemoryListener *listener,return ; } +trace_vhost_iommu_region_del (dev, section->mr->name ?:NULL ); + QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) {if (iommu->mr == section->mr && iommu->n.start == section->offset_within_region) {Signature · 1 line
2.14.3
PATCH v2 2/4 arm: postpone device listener unregister
In reply to Peter XuIt's a preparation for follow-up patch to call region_del() in memory_listener_unregister(), otherwise all device addr attached with kvm_devices_head will be reset before calling kvm_arm_set_device_addr.
--- target/arm/kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
target/arm/kvm.c +1 -1
@@ -266,7 +266,6 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data) {KVMDevice *kd, *tkd; -memory_listener_unregister (&devlistener); QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) {if (kd->kda.addr !=-1 ) {kvm_arm_set_device_addr (kd);
@@ -274,6 +273,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data)memory_region_unref (kd->mr);g_free (kd); } +memory_listener_unregister (&devlistener); }static Notifier notify = {
Signature · 1 line
2.14.3
PATCH v2 3/4 vfio: listener unregister before unset container
In reply to Peter XuAfter next 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, otherwise we'll get these after next 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
Re: [Qemu-devel] [PATCH v2 3/4] vfio: listener unregister before unset container
Quoted 10 lines · On Mon, 22 Jan 2018 14:02:43 +0800 Peter Xu <[email protected]> wrote:
After next 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, otherwise we'll get these after next patch:
qemu-system-x86_64:
VFIO_UNMAP_DMA: -22
qemu-system-x86_64:vfio_dma_unmap(0x559bf53a4590, 0x0, 0xa0000)= -22 (Invalid argument)Signed-off-by Peter Xu <[email protected]>---
Not sure which branch this will go through, let me know if mine. Thanks,
Alex
Quoted 39 lines
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) {
PATCH v2 4/4 memory: do explicit cleanup when remove listeners
In reply to Peter XuWhen 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.
We had encountered similar issues before and fixed in other way (ee4c112846, "vhost: Release memory references on cleanup"). This patch can be seen as a more high-level fix of similar problems that are caused by the resource leaks from memory listeners. So now we can remove the explicit unref of memory regions since that'll be done altogether during unregistering of listeners now.
--- hw/virtio/vhost.c | 4 ---- memory.c | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-)
hw/virtio/vhost.c +0 -4
@@ -1368,10 +1368,6 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)if (hdev->mem) {/* those are only safe after successful init */ memory_listener_unregister (&hdev->memory_listener); -for (i =0 ; i < hdev->n_mem_sections; ++i) { -MemoryRegionSection *section = &hdev->mem_sections[i]; -memory_region_unref (section->mr); - }QLIST_REMOVE (hdev, entry); }if (hdev->migration_blocker) {
memory.c +27 -0
@@ -2609,6 +2609,32 @@ 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; + +if (listener->begin) { + listener->begin (listener); + } + 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 +2675,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
Quoted 31 lines · Peter Xu
v2
- addbegin()hooks [Paolo]
- move vfio patch to front [Paolo]
- one more patch for arm devlistener unregister [Paolo]
- one more patch for vhost traces
- removing RFC tagThis series fixes bug reported here:
https://bugzilla.redhat.com/show_bug.cgi?id=1531393
The first patch only adds traces for vhost, with which I tested on vhost to make sure that my last patch works as expected with vhost. Please pick it if anyone wants, or ignore it if no one likes it.
The 2nd patch is the arm fix for preparation of the last patch.
The 3rd patch is the vfio fix for preparation of the last patch.
The 4th patch is the core fix of the problem.
Please review, thanks.
Thanks.
Peter Xu (4):
vhost: add traces for memory listeners
arm: postpone device listener unregister
vfio: listener unregister before unset container
memory: do explicit cleanup when remove listeners
Quoted 7 lines
hw/vfio/common.c | 16 ++++++++++++---- hw/virtio/trace-events | 6 ++++++ hw/virtio/vhost.c | 11 +++++++---- memory.c | 27 +++++++++++++++++++++++++++ target/arm/kvm.c | 2 +- 5 files changed, 53 insertions(+), 9 deletions(-)
Alex, Peter, can you ack or apply patches 2/3?
Thanks,
Paolo
Paolo Bonzini
AppliedQuoted 31 lines · Peter Xu
v2
- addbegin()hooks [Paolo]
- move vfio patch to front [Paolo]
- one more patch for arm devlistener unregister [Paolo]
- one more patch for vhost traces
- removing RFC tagThis series fixes bug reported here:
https://bugzilla.redhat.com/show_bug.cgi?id=1531393
The first patch only adds traces for vhost, with which I tested on vhost to make sure that my last patch works as expected with vhost. Please pick it if anyone wants, or ignore it if no one likes it.
The 2nd patch is the arm fix for preparation of the last patch.
The 3rd patch is the vfio fix for preparation of the last patch.
The 4th patch is the core fix of the problem.
Please review, thanks.
Thanks.
Peter Xu (4):
vhost: add traces for memory listeners
arm: postpone device listener unregister
vfio: listener unregister before unset container
memory: do explicit cleanup when remove listeners
Quoted 7 lines
hw/vfio/common.c | 16 ++++++++++++---- hw/virtio/trace-events | 6 ++++++ hw/virtio/vhost.c | 11 +++++++---- memory.c | 27 +++++++++++++++++++++++++++ target/arm/kvm.c | 2 +- 5 files changed, 53 insertions(+), 9 deletions(-)
Queued, thanks.
Paolo