[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v7 01/12] vpci: introduce per-domain lock to protect vpci structure
On 04.07.2023 23:03, Volodymyr Babchuk wrote: > I am currently implementing your proposal (along with Jan's > suggestions), but I am facing ABBA deadlock with IOMMU's > reassign_device() call, which has this piece of code: > > list_move(&pdev->domain_list, &target->pdev_list); > > My immediate change was: > > write_lock(&pdev->domain->pci_lock); > list_del(&pdev->domain_list); > write_unlock(&pdev->domain->pci_lock); > > write_lock(&target->pci_lock); > list_add(&pdev->domain_list, &target->pdev_list); > write_unlock(&target->pci_lock); > > But this will not work because reassign_device is called from > pci_release_devices() which iterates over d->pdev_list, so we need to > take a d->pci_lock early. > > Any suggestions on how to fix this? My idea is to remove a device from a > list one at time: > > int pci_release_devices(struct domain *d) > { > struct pci_dev *pdev; > u8 bus, devfn; > int ret; > > pcidevs_lock(); > write_lock(&d->pci_lock); > ret = arch_pci_clean_pirqs(d); > if ( ret ) > { > pcidevs_unlock(); > write_unlock(&d->pci_lock); > return ret; > } > > while ( !list_empty(&d->pdev_list) ) > { > pdev = list_entry(&d->pdev_list, struct pci_dev, domain_list); > bus = pdev->bus; > devfn = pdev->devfn; > list_del(&pdev->domain_list); > write_unlock(&d->pci_lock); > ret = deassign_device(d, pdev->seg, bus, devfn) ?: ret; > write_lock(&d->pci_lock); I think it needs doing almost like this, but with two more tweaks and no list_del() right here (first and foremost to avoid needing to figure whether removing early isn't going to subtly break anything; see below for an error case that would end up with changed behavior): while ( !list_empty(&d->pdev_list) ) { const struct pci_dev *pdev = list_first_entry(&d->pdev_list, struct pci_dev, domain_list); uint16_t seg = pdev->seg; uint8_t bus = pdev->bus; uint8_t devfn = pdev->devfn; write_unlock(&d->pci_lock); ret = deassign_device(d, seg, bus, devfn) ?: ret; write_lock(&d->pci_lock); } One caveat though: The original list_for_each_entry_safe() guarantees the loop to complete; your use of list_del() would guarantee that too, but then the device wouldn't be on the list anymore if deassign_device() failed. Therefore I guess you will need another local list where you (temporarily) put all the devices which deassign_device() left on the list, and which you would then move back to d->pdev_list after the loop has finished. (Whether it is sufficient to inspect the list head to determine whether the pdev is still on the list needs careful checking.) Jan
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |