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

[xen staging-4.11] evtchn: arrange for preemption in evtchn_destroy()



commit 3e565a9c603daebcf50e067c07aed7f0c4b2a6e0
Author:     Jan Beulich <jbeulich@xxxxxxxx>
AuthorDate: Tue Sep 22 17:22:28 2020 +0200
Commit:     Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Tue Sep 22 17:22:28 2020 +0200

    evtchn: arrange for preemption in evtchn_destroy()
    
    Especially closing of fully established interdomain channels can take
    quite some time, due to the locking involved. Therefore we shouldn't
    assume we can clean up still active ports all in one go. Besides adding
    the necessary preemption check, also avoid pointlessly starting from
    (or now really ending at) 0; 1 is the lowest numbered port which may
    need closing.
    
    Since we're now reducing ->valid_evtchns, free_xen_event_channel(),
    and (at least to be on the safe side) notify_via_xen_event_channel()
    need to cope with attempts to close / unbind from / send through already
    closed (and no longer valid, as per port_is_valid()) ports.
    
    This is part of XSA-344.
    
    Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
    Acked-by: Julien Grall <jgrall@xxxxxxxxxx>
    Reviewed-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>
---
 xen/common/domain.c        |  4 +++-
 xen/common/event_channel.c | 43 ++++++++++++++++++++++++++++++++++++++-----
 xen/include/xen/sched.h    |  2 +-
 3 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/xen/common/domain.c b/xen/common/domain.c
index 2ddd7986c1..cacede4cf0 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -646,7 +646,6 @@ int domain_kill(struct domain *d)
         if ( d->is_dying != DOMDYING_alive )
             return domain_kill(d);
         d->is_dying = DOMDYING_dying;
-        evtchn_destroy(d);
         gnttab_release_mappings(d);
         tmem_destroy(d->tmem_client);
         vnuma_destroy(d->vnuma);
@@ -654,6 +653,9 @@ int domain_kill(struct domain *d)
         d->tmem_client = NULL;
         /* fallthrough */
     case DOMDYING_dying:
+        rc = evtchn_destroy(d);
+        if ( rc )
+            break;
         rc = domain_relinquish_resources(d);
         if ( rc != 0 )
             break;
diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c
index 753950b81a..52ba2b0c0c 100644
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -1291,7 +1291,16 @@ int alloc_unbound_xen_event_channel(
 
 void free_xen_event_channel(struct domain *d, int port)
 {
-    BUG_ON(!port_is_valid(d, port));
+    if ( !port_is_valid(d, port) )
+    {
+        /*
+         * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing
+         * with the spin_barrier() and BUG_ON() in evtchn_destroy().
+         */
+        smp_rmb();
+        BUG_ON(!d->is_dying);
+        return;
+    }
 
     evtchn_close(d, port, 0);
 }
@@ -1303,7 +1312,17 @@ void notify_via_xen_event_channel(struct domain *ld, int 
lport)
     struct domain *rd;
     unsigned long flags;
 
-    ASSERT(port_is_valid(ld, lport));
+    if ( !port_is_valid(ld, lport) )
+    {
+        /*
+         * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing
+         * with the spin_barrier() and BUG_ON() in evtchn_destroy().
+         */
+        smp_rmb();
+        ASSERT(ld->is_dying);
+        return;
+    }
+
     lchn = evtchn_from_port(ld, lport);
 
     spin_lock_irqsave(&lchn->lock, flags);
@@ -1375,8 +1394,7 @@ int evtchn_init(struct domain *d)
     return 0;
 }
 
-
-void evtchn_destroy(struct domain *d)
+int evtchn_destroy(struct domain *d)
 {
     unsigned int i;
 
@@ -1385,14 +1403,29 @@ void evtchn_destroy(struct domain *d)
     spin_barrier(&d->event_lock);
 
     /* Close all existing event channels. */
-    for ( i = 0; port_is_valid(d, i); i++ )
+    for ( i = d->valid_evtchns; --i; )
+    {
         evtchn_close(d, i, 0);
 
+        /*
+         * Avoid preempting when called from domain_create()'s error path,
+         * and don't check too often (choice of frequency is arbitrary).
+         */
+        if ( i && !(i & 0x3f) && d->is_dying != DOMDYING_dead &&
+             hypercall_preempt_check() )
+        {
+            write_atomic(&d->valid_evtchns, i);
+            return -ERESTART;
+        }
+    }
+
     ASSERT(!d->active_evtchns);
 
     clear_global_virq_handlers(d);
 
     evtchn_fifo_destroy(d);
+
+    return 0;
 }
 
 
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index a8ebfe6d95..46958de3d1 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -135,7 +135,7 @@ struct evtchn
 } __attribute__((aligned(64)));
 
 int  evtchn_init(struct domain *d); /* from domain_create */
-void evtchn_destroy(struct domain *d); /* from domain_kill */
+int  evtchn_destroy(struct domain *d); /* from domain_kill */
 void evtchn_destroy_final(struct domain *d); /* from complete_domain_destroy */
 
 struct waitqueue_vcpu;
--
generated by git-patchbot for /home/xen/git/xen.git#staging-4.11



 


Rackspace

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