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

[xen master] xen/evtchn: Make sure all buckets below d->valid_evtchns are allocated



commit 5d1cfe844e23cb1610ef07f60bc8076e058891d6
Author:     Julien Grall <jgrall@xxxxxxxxxx>
AuthorDate: Wed Sep 7 17:27:32 2022 +0100
Commit:     Julien Grall <jgrall@xxxxxxxxxx>
CommitDate: Thu Sep 8 12:21:58 2022 +0100

    xen/evtchn: Make sure all buckets below d->valid_evtchns are allocated
    
    Since commit 01280dc19cf3 "evtchn: simplify port_is_valid()", the event
    channels code assumes that all the buckets below d->valid_evtchns are
    always allocated.
    
    This assumption hold in most of the situation because a guest is not
    allowed to chose the port. Instead, it will be the first free from port
    0.
    
    When static event channel support will be added for dom0less domains
    user can request to allocate the evtchn port numbers that are scattered
    in nature.
    
    The existing implementation of evtchn_allocate_port() is not able to
    deal with such situation and will end up to override bucket or/and leave
    some bucket unallocated. The latter will result to a droplet crash if
    the event channel belongs to an unallocated bucket.
    
    This can be solved by making sure that all the buckets below
    d->valid_evtchns are allocated. There should be no impact for most of
    the situation but LM/LU as only one bucket would be allocated. For
    LM/LU, we may end up to allocate multiple buckets if ports in use are
    sparse.
    
    A potential alternative is to check that the bucket is valid in
    is_port_valid(). This should still possible to do it without taking
    per-domain lock but will result a couple more of memory access.
    
    Signed-off-by: Julien Grall <jgrall@xxxxxxxxxx>
    Signed-off-by: Rahul Singh <rahul.singh@xxxxxxx>
    Reviewed-by: Michal Orzel <michal.orzel@xxxxxxx>
    Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
---
 xen/common/event_channel.c | 55 +++++++++++++++++++++++++++++-----------------
 1 file changed, 35 insertions(+), 20 deletions(-)

diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c
index c2c6f8c151..f81c229358 100644
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -193,6 +193,15 @@ static struct evtchn *alloc_evtchn_bucket(struct domain 
*d, unsigned int port)
     return NULL;
 }
 
+/*
+ * Allocate a given port and ensure all the buckets up to that ports
+ * have been allocated.
+ *
+ * The last part is important because the rest of the event channel code
+ * relies on all the buckets up to d->valid_evtchns to be valid. However,
+ * event channels may be sparsed when allocating the static evtchn port
+ * numbers that are scattered in nature.
+ */
 int evtchn_allocate_port(struct domain *d, evtchn_port_t port)
 {
     if ( port > d->max_evtchn_port || port >= max_evtchns(d) )
@@ -207,30 +216,36 @@ int evtchn_allocate_port(struct domain *d, evtchn_port_t 
port)
     }
     else
     {
-        struct evtchn *chn;
-        struct evtchn **grp;
+        unsigned int alloc_port = read_atomic(&d->valid_evtchns);
 
-        if ( !group_from_port(d, port) )
+        do
         {
-            grp = xzalloc_array(struct evtchn *, BUCKETS_PER_GROUP);
-            if ( !grp )
-                return -ENOMEM;
-            group_from_port(d, port) = grp;
-        }
+            struct evtchn *chn;
+            struct evtchn **grp;
 
-        chn = alloc_evtchn_bucket(d, port);
-        if ( !chn )
-            return -ENOMEM;
-        bucket_from_port(d, port) = chn;
+            if ( !group_from_port(d, alloc_port) )
+            {
+                grp = xzalloc_array(struct evtchn *, BUCKETS_PER_GROUP);
+                if ( !grp )
+                    return -ENOMEM;
+                group_from_port(d, alloc_port) = grp;
+            }
 
-        /*
-         * d->valid_evtchns is used to check whether the bucket can be
-         * accessed without the per-domain lock. Therefore,
-         * d->valid_evtchns should be seen *after* the new bucket has
-         * been setup.
-         */
-        smp_wmb();
-        write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET);
+            chn = alloc_evtchn_bucket(d, alloc_port);
+            if ( !chn )
+                return -ENOMEM;
+            bucket_from_port(d, alloc_port) = chn;
+
+            /*
+             * d->valid_evtchns is used to check whether the bucket can be
+             * accessed without the per-domain lock. Therefore,
+             * d->valid_evtchns should be seen *after* the new bucket has
+             * been setup.
+             */
+            smp_wmb();
+            alloc_port += EVTCHNS_PER_BUCKET;
+            write_atomic(&d->valid_evtchns, alloc_port);
+        } while ( port >= alloc_port );
     }
 
     write_atomic(&d->active_evtchns, d->active_evtchns + 1);
--
generated by git-patchbot for /home/xen/git/xen.git#master



 


Rackspace

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