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

Re: [Xen-devel] [PATCH v6 3/5] IOMMU: Make the pcidevs_lock a recursive one



On March 04, 2016 9:59pm, <dario.faggioli@xxxxxxxxxx> wrote:
> On Fri, 2016-03-04 at 11:54 +0000, Xu, Quan wrote:
> > On March 04, 2016 5:29pm, <JBeulich@xxxxxxxx> wrote:
> > > On March 04, 2016 7:59am, <dario.faggioli@xxxxxxxxxx> wrote:
> > >
> > > > Also I'd highlight the below modification:
> > > > -    if ( !spin_trylock(&pcidevs_lock) )
> > > > -        return -ERESTART;
> > > > -
> > > > +    pcidevs_lock();
> > > >
> > > > IMO, it is right too.
> > > Well, I'll have to see where exactly this is (pulling such out of
> > > context is pretty unhelpful), but I suspect it can't be replaced
> > > like this.
> > >
> > Jan, I am looking forward to your review.
> > btw, It is in the assign_device(), in the
> > xen/drivers/passthrough/pci.c file.
> >
> Mmm... If multiple cpus calls assign_device(), and the calls race, the 
> behavior
> between before and after the patch looks indeed different to me.
> 
> In fact, in current code, the cpus that find the lock busy already, would 
> quit the
> function immediately and a continuation is created. On the other hand, with 
> the
> patch, they would spin and actually get the lock, one after the other (if 
> there's
> more of them) at some point.
> 
> Please, double check my reasoning, but I looks to me that it is indeed 
> different
> what happens when the hypercall is restarted (i.e., in current code) and what
> happens if we just let others take the lock and execute the function (i.e., 
> with
> the patch applied).
> 
> I suggest you try to figure out whether that is actually the case. Once you've
> done, feel free to report here and ask for help for finding a solution, if 
> you don't
> see one.
> 

Good idea.
For multiple cpus calls assign_device(), Iet's assume that there are 3 calls in 
parallel:
  (p1). xl pci-attach TestDom 0000:81:00.0
  (p2). xl pci-attach TestDom 0000:81:00.0
  (p3). xl pci-attach TestDom 0000:81:00.0
 
Furthermore, p1 and p2 run on pCPU1, and p3 runs on pCPU2.

After my patch,
__IIUC__ , the invoker flow might be as follow:
pCPU1                                               pCPU2
 .                                                     .
 .                                                     .
 assign_device_1()
  {                                                    .
   spin_lock_r(lock)                                      .
   .                                                   assign_device_3() 
spin_lock_r(lock) <-- blocks
   assign_device_2()
     {                                                 x <-- spins
       spin_lock_r(lock) <-- can continue                     x <-- spins
       spin_unlock_r(lock) <-- *doesn't* release lock           x <-- spins
     }                                                 x <-- spins
     .                                                 x <-- spins
  }                                                    x <-- spins
   .                                                   x <-- spins
   spin_unlock_r(lock) <-- release lock ----------->. ....... 
...............<--assign_device_3() continue, with lock held
   .                                                   .
   .                                                   .
   .                                                   spin_unlock_r(lock) 
<--lock is now free


Befer my patch,
The invoker flower might return at the point of assign_device_2() / 
assign_device_3().

So, yes, If multiple cpus calls assign_device(), and the calls race, the 
behavior between before and after the patch looks indeed different.

I try to fix it with follow:
--------patch >> -------- 

--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -118,6 +118,11 @@ int pcidevs_is_locked(void)
     return spin_is_locked(&_pcidevs_lock);
 }

+int pcidevs_trylock(void)
+{
+    return spin_trylock_recursive(&_pcidevs_lock);
+}
+
 void __init pt_pci_init(void)
 {
     radix_tree_init(&pci_segments);
@@ -1365,7 +1370,7 @@ static int assign_device(struct domain *d, u16 seg, u8 
bus, u8 devfn, u32 flag)
              p2m_get_hostp2m(d)->global_logdirty)) )
         return -EXDEV;

-    if ( !spin_trylock(&pcidevs_lock) )
+    if ( !pcidevs_trylock() )
         return -ERESTART;

     rc = iommu_construct(d);
diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
index 017aa0b..b87571d 100644
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -97,6 +97,7 @@ struct pci_dev {
 void pcidevs_lock(void);
 void pcidevs_unlock(void);
 int pcidevs_is_locked(void);
+int pcidevs_trylock(void);

 bool_t pci_known_segment(u16 seg);
 bool_t pci_device_detect(u16 seg, u8 bus, u8 dev, u8 func);

--------patch << -------- 

A quick question, is it '-ERESTART', instead of '-EBUSY' ?

There is also a similar case, cpu_hotplug:
   $cpu_up()--> cpu_hotplug_begin()-->get_cpu_maps()--> 
spin_trylock_recursive(&cpu_add_remove_lock)

Feel free to share your idea, and correct me if I'm wrong.

Quan
_______________________________________________
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®.