[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCHv1 1/2] passthrough: use per-interrupt lock when injecting an interrupt



.snip..
> > @@ -481,6 +489,8 @@ int pt_irq_destroy_bind(
> >      pirq = pirq_info(d, machine_gsi);
> >      pirq_dpci = pirq_dpci(pirq);
> >  
> > +    spin_lock(&pirq_dpci->lock);
> 
> Considering that code further down in this function checks
> pirq_dpci to be non-NULL, this doesn't look safe (or else those
> checks should go away, as after this addition they would be
> likely to trigger e.g. Coverity warnings).

?  The checks are for pirq_dpci->dom.

> 
> > @@ -621,7 +633,7 @@ int hvm_do_IRQ_dpci(struct domain *d, struct pirq *pirq)
> >      return 1;
> >  }
> >  
> > -/* called with d->event_lock held */
> > +/* called with pirq_dhci->lock held */
> >  static void __msi_pirq_eoi(struct hvm_pirq_dpci *pirq_dpci)
> 
> The fact that this is a safe change to the locking model imo needs
> to be spelled out explicitly in the commit message. Afaict it's safe
> only because desc_guest_eoi() uses pirq for nothing else than to
> (atomically!) clear pirq->masked.
> 
> > @@ -675,7 +687,7 @@ static void hvm_dirq_assist(struct domain *d, struct 
> > hvm_pirq_dpci *pirq_dpci)
> >  {
> >      ASSERT(d->arch.hvm_domain.irq.dpci);
> >  
> > -    spin_lock(&d->event_lock);
> > +    spin_lock(&pirq_dpci->lock);
> >      if ( test_and_clear_bool(pirq_dpci->masked) )
> >      {
> >          struct pirq *pirq = dpci_pirq(pirq_dpci);
> 
> Along the same lines - it's not obvious that the uses of pirq here are
> safe with event_lock no longer held. In particular I wonder about
> send_guest_pirq() - despite the other use in __do_IRQ_guest() not
> doing any locking either I'm not convinced this is correct.


It seems that the event channel mechanism only uses the event channel
lock when expanding and initializing (FIFO). For the old mechanism
it was for binding, closing (uhuh), status, reset, and set_priority.

The 'closing' is interesting. What if the guest was closing the port
while we are to notify it of an interrupt (so inside hvm_dirq_assist).

evtchn_close will take the event_lock call pirq_cleanup_check
and calls pt_pirq_cleanup_check. We check for STATE_RUN (still set
as dpci_softirq hasn't finished calling hvm_dirq_assist) so it returns
zero .. and does not call radix_tree_delete.

Then evtchn_close goes to 'unlink_pirq_port' and then follows up with
'unmap_domain_pirq_emuirq'. That sets emuirq to IRQ_UNBOUND.

OK, now hvm_dirq_assist is now inside the first 'if' and checks:

684         if ( hvm_domain_use_pirq(d, pirq) )

which would have been true, but now is false (as emuirq is now
IRQ_UNBOUND). 

Lets also assume this is for an MSI.

The 'pt_irq_need_timer' returns 0:

142     return !(flags & (HVM_IRQ_DPCI_GUEST_MSI | HVM_IRQ_DPCI_TRANSLATE));

which hits this:

723         ASSERT(pt_irq_need_timer(pirq_dpci->flags));

and blows up.

If we are not compiled with 'debug=y' then we would set a timer which
8 ms later calls 'pt_irq_time_out'. It then clears the STATE_RUN and
the softirq is done.

So lets switch back to 'evtchn_close'.. which unlocks the event_lock
and is done.

The 'pt_irq_time_out' is pretty much a nop - takes the event_lock,
does not iterate over the list as it is an MSI one so no legacy interrupts.
Calls pt_irq_guest_eoi over the radix tree - and pt_irq_guest_eoi does nothing
as the _HVM_IRQ_DPCI_EOI_LATCH_SHIFT is not set.

I think that is the only issue - when using the legacy event channels
and the guest fiddling with the pirq. I suppose that means anybody
using 'unmap_domain_pirq_emuirq' will hit this ASSERT(). That means:

 arch_domain_soft_reset
 physdev_unmap_pirq
 evtchn_close

Thought you could 'fix' this by doing:


diff --git a/xen/drivers/passthrough/io.c b/xen/drivers/passthrough/io.c
index bda9374..6ea59db 100644
--- a/xen/drivers/passthrough/io.c
+++ b/xen/drivers/passthrough/io.c
@@ -720,8 +720,8 @@ static void hvm_dirq_assist(struct domain *d, struct 
hvm_pirq_dpci *pirq_dpci)
          * guest will never deal with the irq, then the physical interrupt line
          * will never be deasserted.
          */
-        ASSERT(pt_irq_need_timer(pirq_dpci->flags));
-        set_timer(&pirq_dpci->timer, NOW() + PT_IRQ_TIME_OUT);
+        if (pt_irq_need_timer(pirq_dpci->flags))
+            set_timer(&pirq_dpci->timer, NOW() + PT_IRQ_TIME_OUT);
     }
     spin_unlock(&d->event_lock);
 }






> 
> Jan
> 
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxx
> http://lists.xen.org/xen-devel

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.