Reviewed-by: Clement Mathieu--Drif <[email protected]> Let's wait for Yi's review. Thanks for your efforts, Zhenzhong!
cmd On Tue, 2026-10-20 at 17:19 +0800, Zhenzhong Duan wrote: > Caution: External email. Do not open attachments or click links, unless this > email comes from a known sender and you know the content is safe. > > > When a PASID entry becomes invalid, all resources allocated for that entry, > including the FAULTQ object and fault_fd, must be released. > > Calling qemu_set_fd_handler() detaches the fault_fd I/O read handler and > wakes up the main loop thread from poll(). However, a race window exists > where iommufd_backend_free_id(fault_id) can be invoked before poll() exits > and releases its reference to the fault_fd file. In this scenario, freeing > the FAULTQ object returns -EBUSY because the opened fault_fd file still > holds an active reference to the FAULTQ object. > > Defer FAULTQ resource teardown to a bottom half. This ensures poll() has > fully exited and dropped the file reference before the fault_fd is closed > and the FAULTQ object ID is freed. Additionally, keep the IOMMUFD backend > FD open via iommufd_backend_connect() across the asynchronous bottom half > execution to ensure FAULTQ object freed successfully. > > Suggested-by: Shameer Kolothum > <[[email protected]](mailto:[email protected])> > Signed-off-by: Zhenzhong Duan > <[[email protected]](mailto:[email protected])> > Reviewed-by: Yi Liu <[[email protected]](mailto:[email protected])> > Tested-by: Xudong Hao <[[email protected]](mailto:[email protected])> > --- > hw/i386/intel_iommu_accel.c | 59 +++++++++++++++++++++++++++++++++++-- > 1 file changed, 56 insertions(+), 3 deletions(-) > > diff --git a/hw/i386/intel_iommu_accel.c b/hw/i386/intel_iommu_accel.c > index 243501f137..7db8319ad4 100644 > --- a/hw/i386/intel_iommu_accel.c > +++ b/hw/i386/intel_iommu_accel.c > @@ -271,17 +271,70 @@ free_faultq: > return false; > } > > +typedef struct IOMMUFaultQueue { > + IOMMUFDBackend *iommufd; > + uint32_t id; > + int fd; > + QLIST_HEAD(, VTDPRQEntry) vtd_prq_list; > +} IOMMUFaultQueue; > + > +static void faultq_teardown_bh(void *opaque) > +{ > + IOMMUFaultQueue *fq = opaque; > + VTDPRQEntry *prqe, *next; > + > + QLIST_FOREACH_SAFE(prqe, &fq->vtd_prq_list, next, next) { > + QLIST_REMOVE(prqe, next); > + g_free(prqe); > + } > + > + qemu_set_fd_handler(fq->fd, NULL, NULL, NULL); > + close(fq->fd); > + iommufd_backend_free_id(fq->iommufd, fq->id); > + iommufd_backend_disconnect(fq->iommufd); > + > + g_free(fq); > +} > + > static void vtd_destroy_old_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce) > { > + HostIOMMUDeviceIOMMUFD *idev = > + HOST_IOMMU_DEVICE_IOMMUFD(vtd_pce->vtd_hiod->hiod); > + IOMMUFaultQueue *fq; > + > if (vtd_pce->fault_fd < 0) { > return; > } > > - qemu_set_fd_handler(vtd_pce->fault_fd, NULL, NULL, NULL); > - vtd_destroy_fs_faultq(vtd_pce->vtd_hiod, vtd_pce->fault_id, > - vtd_pce->fault_fd); > + fq = g_malloc(sizeof(*fq)); > + fq->iommufd = idev->iommufd; > + fq->fd = vtd_pce->fault_fd; > + fq->id = vtd_pce->fault_id; > vtd_pce->fault_id = 0; > vtd_pce->fault_fd = -1; > + fq->vtd_prq_list.lh_first = vtd_pce->vtd_prq_list.lh_first; > + QLIST_INIT(&vtd_pce->vtd_prq_list); > + > + /* > + * During a VFIO device hot-unplug, if this is the last device connected > > + * to the IOMMUFDBackend, the backend user count (iommufd->users) will > + * drop to zero, triggering iommufd_backend_disconnect() which closes > + * the backend's opened container FD. > + * > + * Since the teardown of this fault queue (faultq_teardown_bh) is > + * scheduled asynchronously as a bottom half, we must temporarily > + * increment the connection user count of the iommufd backend here. > + * This guarantees the backend connection remains open and its opened > + * FD remains active until the async BH completes. > + * > + * Incrementing the user count is guaranteed to succeed here because the > > + * VFIO device is still registered as an active user of the backend at > + * this point in the teardown sequence. > + */ > + iommufd_backend_connect(fq->iommufd, &error_abort); > + > + aio_bh_schedule_oneshot(iohandler_get_aio_context(), > + faultq_teardown_bh, fq); > } > > static void vtd_setup_fs_faultq(VTDAccelPASIDCacheEntry *vtd_pce, > -- > 2.52.0 >
