]> xenbits.xensource.com Git - xen.git/commitdiff
evtchn: don't reuse ports that are still "busy"
authorDavid Vrabel <david.vrabel@citrix.com>
Tue, 15 Dec 2015 14:55:23 +0000 (15:55 +0100)
committerJan Beulich <jbeulich@suse.com>
Tue, 15 Dec 2015 14:55:23 +0000 (15:55 +0100)
When using the FIFO ABI a guest may close an event channel that is
still LINKED.  If this port is reused, subsequent events may be lost
because they may become pending on the wrong queue.

This could be fixed by requiring guests to only close event channels
that are not linked.  This is difficult since: a) irq cleanup in the
guest may be done in a context that cannot wait for the event to be
unlinked; b) the guest may attempt to rebind a PIRQ whose previous
close is still pending; and c) existing guests already have the
problematic behaviour.

Instead, simply check a port is not "busy" (i.e., it's not linked)
before reusing it.

Guests should still drain any queues for VCPUs that are being
offlined, or the port will become unusable until the VCPU is onlined
and starts processing events again.

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
master commit: 78e24c269b0a4a8b864ece725e6d4209ed95dfa7
master date: 2015-12-02 15:21:46 +0100

xen/common/event_channel.c
xen/common/event_fifo.c
xen/include/xen/event.h

index 2774cbba16e2f4248a8e88bac8bdfdabf52eb873..0e4c1953d64b0c2c956ef456c77d0e32d24f4054 100644 (file)
@@ -171,7 +171,8 @@ static int get_free_port(struct domain *d)
     {
         if ( port > d->max_evtchn_port )
             return -ENOSPC;
-        if ( evtchn_from_port(d, port)->state == ECS_FREE )
+        if ( evtchn_from_port(d, port)->state == ECS_FREE
+             && !evtchn_port_is_busy(d, port) )
             return port;
     }
 
index e9c1fbe4d5cddf931ce8c45e960873f898209765..a443c980184448bd421b86d9425875b7354b47b3 100644 (file)
@@ -313,6 +313,17 @@ static bool_t evtchn_fifo_is_masked(struct domain *d,
     return test_bit(EVTCHN_FIFO_MASKED, word);
 }
 
+static bool_t evtchn_fifo_is_busy(struct domain *d, evtchn_port_t port)
+{
+    event_word_t *word;
+
+    word = evtchn_fifo_word_from_port(d, port);
+    if ( unlikely(!word) )
+        return 0;
+
+    return test_bit(EVTCHN_FIFO_LINKED, word);
+}
+
 static int evtchn_fifo_set_priority(struct domain *d, struct evtchn *evtchn,
                                     unsigned int priority)
 {
@@ -352,6 +363,7 @@ static const struct evtchn_port_ops evtchn_port_ops_fifo =
     .unmask        = evtchn_fifo_unmask,
     .is_pending    = evtchn_fifo_is_pending,
     .is_masked     = evtchn_fifo_is_masked,
+    .is_busy       = evtchn_fifo_is_busy,
     .set_priority  = evtchn_fifo_set_priority,
     .print_state   = evtchn_fifo_print_state,
 };
index 06c0654fa6edcf122957641af8010f91c6ebf7d1..075f4da451e53a828b5a23df8c08cca84645209f 100644 (file)
@@ -138,6 +138,11 @@ struct evtchn_port_ops {
     void (*unmask)(struct domain *d, struct evtchn *evtchn);
     bool_t (*is_pending)(struct domain *d, const struct evtchn *evtchn);
     bool_t (*is_masked)(struct domain *d, const struct evtchn *evtchn);
+    /*
+     * Is the port unavailable because it's still being cleaned up
+     * after being closed?
+     */
+    bool_t (*is_busy)(struct domain *d, evtchn_port_t port);
     int (*set_priority)(struct domain *d, struct evtchn *evtchn,
                         unsigned int priority);
     void (*print_state)(struct domain *d, const struct evtchn *evtchn);
@@ -179,6 +184,13 @@ static inline bool_t evtchn_port_is_masked(struct domain *d,
     return d->evtchn_port_ops->is_masked(d, evtchn);
 }
 
+static inline bool_t evtchn_port_is_busy(struct domain *d, evtchn_port_t port)
+{
+    if ( d->evtchn_port_ops->is_busy )
+        return d->evtchn_port_ops->is_busy(d, port);
+    return 0;
+}
+
 static inline int evtchn_port_set_priority(struct domain *d,
                                            struct evtchn *evtchn,
                                            unsigned int priority)