[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v6 03/13] vpci: move lock outside of struct vpci
Hi, Jan! On 04.02.22 11:15, Jan Beulich wrote: > On 04.02.2022 09:58, Oleksandr Andrushchenko wrote: >> On 04.02.22 09:52, Jan Beulich wrote: >>> On 04.02.2022 07:34, Oleksandr Andrushchenko wrote: >>>> @@ -285,6 +286,12 @@ static int modify_bars(const struct pci_dev *pdev, >>>> uint16_t cmd, bool rom_only) >>>> continue; >>>> } >>>> >>>> + spin_lock(&tmp->vpci_lock); >>>> + if ( !tmp->vpci ) >>>> + { >>>> + spin_unlock(&tmp->vpci_lock); >>>> + continue; >>>> + } >>>> for ( i = 0; i < ARRAY_SIZE(tmp->vpci->header.bars); i++ ) >>>> { >>>> const struct vpci_bar *bar = &tmp->vpci->header.bars[i]; >>>> @@ -303,12 +310,14 @@ static int modify_bars(const struct pci_dev *pdev, >>>> uint16_t cmd, bool rom_only) >>>> rc = rangeset_remove_range(mem, start, end); >>>> if ( rc ) >>>> { >>>> + spin_unlock(&tmp->vpci_lock); >>>> printk(XENLOG_G_WARNING "Failed to remove [%lx, %lx]: >>>> %d\n", >>>> start, end, rc); >>>> rangeset_destroy(mem); >>>> return rc; >>>> } >>>> } >>>> + spin_unlock(&tmp->vpci_lock); >>>> } >>> At the first glance this simply looks like another unjustified (in the >>> description) change, as you're not converting anything here but you >>> actually add locking (and I realize this was there before, so I'm sorry >>> for not pointing this out earlier). >> Well, I thought that the description already has "...the lock can be >> used (and in a few cases is used right away) to check whether vpci >> is present" and this is enough for such uses as here. >>> But then I wonder whether you >>> actually tested this, since I can't help getting the impression that >>> you're introducing a live-lock: The function is called from cmd_write() >>> and rom_write(), which in turn are called out of vpci_write(). Yet that >>> function already holds the lock, and the lock is not (currently) >>> recursive. (For the 3rd caller of the function - init_bars() - otoh >>> the locking looks to be entirely unnecessary.) >> Well, you are correct: if tmp != pdev then it is correct to acquire >> the lock. But if tmp == pdev and rom_only == true >> then we'll deadlock. >> >> It seems we need to have the locking conditional, e.g. only lock >> if tmp != pdev > Which will address the live-lock, but introduce ABBA deadlock potential > between the two locks. I am not sure I can suggest a better solution here @Roger, @Jan, could you please help here? > >>>> @@ -222,10 +239,10 @@ static int msix_read(struct vcpu *v, unsigned long >>>> addr, unsigned int len, >>>> break; >>>> } >>>> >>>> + msix_put(msix); >>>> return X86EMUL_OKAY; >>>> } >>>> >>>> - spin_lock(&msix->pdev->vpci->lock); >>>> entry = get_entry(msix, addr); >>>> offset = addr & (PCI_MSIX_ENTRY_SIZE - 1); >>> You're increasing the locked region quite a bit here. If this is really >>> needed, it wants explaining. And if this is deemed acceptable as a >>> "side effect", it wants justifying or at least stating imo. Same for >>> msix_write() then, obviously. >> Yes, I do increase the locking region here, but the msix variable needs >> to be protected all the time, so it seems to be obvious that it remains >> under the lock > What does the msix variable have to do with the vPCI lock? If you see > a need to grow the locked region here, then surely this is independent > of your conversion of the lock, and hence wants to be a prereq fix > (which may in fact want/need backporting). First of all, the implementation of msix_get is wrong and needs to be: /* * Note: if vpci_msix found, then this function returns with * pdev->vpci_lock held. Use msix_put to unlock. */ static struct vpci_msix *msix_get(const struct domain *d, unsigned long addr) { struct vpci_msix *msix; list_for_each_entry ( msix, &d->arch.hvm.msix_tables, next ) { const struct vpci_bar *bars; unsigned int i; spin_lock(&msix->pdev->vpci_lock); if ( !msix->pdev->vpci ) { spin_unlock(&msix->pdev->vpci_lock); continue; } bars = msix->pdev->vpci->header.bars; for ( i = 0; i < ARRAY_SIZE(msix->tables); i++ ) if ( bars[msix->tables[i] & PCI_MSIX_BIRMASK].enabled && VMSIX_ADDR_IN_RANGE(addr, msix->pdev->vpci, i) ) return msix; spin_unlock(&msix->pdev->vpci_lock); } return NULL; } Then, both msix_{read|write} can dereference msix->pdev->vpci early, this is why Roger suggested we move to msix_{get|put} here. And yes, we grow the locked region here and yes this might want a prereq fix. Or just be fixed while at it. > >>>> @@ -327,7 +334,12 @@ uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, >>>> unsigned int size) >>>> if ( !pdev ) >>>> return vpci_read_hw(sbdf, reg, size); >>>> >>>> - spin_lock(&pdev->vpci->lock); >>>> + spin_lock(&pdev->vpci_lock); >>>> + if ( !pdev->vpci ) >>>> + { >>>> + spin_unlock(&pdev->vpci_lock); >>>> + return vpci_read_hw(sbdf, reg, size); >>>> + } >>> Didn't you say you would add justification of this part of the change >>> (and its vpci_write() counterpart) to the description? >> Again, I am referring to the commit message as described above > No, sorry - that part applies only to what inside the parentheses of > if(). But on the intermediate version (post-v5 in a 4-patch series) I > did say: > > "In this case as well as in its write counterpart it becomes even more > important to justify (in the description) the new behavior. It is not > obvious at all that the absence of a struct vpci should be taken as > an indication that the underlying device needs accessing instead. > This also cannot be inferred from the "!pdev" case visible in context. > In that case we have no record of a device at this SBDF, and hence the > fallback pretty clearly is a "just in case" one. Yet if we know of a > device, the absence of a struct vpci may mean various possible things." > > If it wasn't obvious: The comment was on the use of vpci_read_hw() on > this path, not redundant with the earlier one regarding the added > "is vpci non-NULL" in a few places. Ok > > Jan > Thank you, Oleksandr
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |