]> git.baikalelectronics.ru Git - kernel.git/commitdiff
xen/events: add a new "late EOI" evtchn framework
authorJuergen Gross <jgross@suse.com>
Mon, 7 Sep 2020 13:47:27 +0000 (15:47 +0200)
committerJuergen Gross <jgross@suse.com>
Tue, 20 Oct 2020 08:21:59 +0000 (10:21 +0200)
In order to avoid tight event channel related IRQ loops add a new
framework of "late EOI" handling: the IRQ the event channel is bound
to will be masked until the event has been handled and the related
driver is capable to handle another event. The driver is responsible
for unmasking the event channel via the new function xen_irq_lateeoi().

This is similar to binding an event channel to a threaded IRQ, but
without having to structure the driver accordingly.

In order to support a future special handling in case a rogue guest
is sending lots of unsolicited events, add a flag to xen_irq_lateeoi()
which can be set by the caller to indicate the event was a spurious
one.

This is part of XSA-332.

Cc: stable@vger.kernel.org
Reported-by: Julien Grall <julien@xen.org>
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Reviewed-by: Wei Liu <wl@xen.org>
drivers/xen/events/events_base.c
include/xen/events.h

index 407741ece0848b5f80f3977c96c915733776e121..1eba8bc209adda529824b9462ee4b7b35b3d1483 100644 (file)
@@ -113,6 +113,7 @@ static bool (*pirq_needs_eoi)(unsigned irq);
 static struct irq_info *legacy_info_ptrs[NR_IRQS_LEGACY];
 
 static struct irq_chip xen_dynamic_chip;
+static struct irq_chip xen_lateeoi_chip;
 static struct irq_chip xen_percpu_chip;
 static struct irq_chip xen_pirq_chip;
 static void enable_dynirq(struct irq_data *data);
@@ -397,6 +398,33 @@ void notify_remote_via_irq(int irq)
 }
 EXPORT_SYMBOL_GPL(notify_remote_via_irq);
 
+static void xen_irq_lateeoi_locked(struct irq_info *info)
+{
+       evtchn_port_t evtchn;
+
+       evtchn = info->evtchn;
+       if (!VALID_EVTCHN(evtchn))
+               return;
+
+       unmask_evtchn(evtchn);
+}
+
+void xen_irq_lateeoi(unsigned int irq, unsigned int eoi_flags)
+{
+       struct irq_info *info;
+       unsigned long flags;
+
+       read_lock_irqsave(&evtchn_rwlock, flags);
+
+       info = info_for_irq(irq);
+
+       if (info)
+               xen_irq_lateeoi_locked(info);
+
+       read_unlock_irqrestore(&evtchn_rwlock, flags);
+}
+EXPORT_SYMBOL_GPL(xen_irq_lateeoi);
+
 static void xen_irq_init(unsigned irq)
 {
        struct irq_info *info;
@@ -868,7 +896,7 @@ int xen_pirq_from_irq(unsigned irq)
 }
 EXPORT_SYMBOL_GPL(xen_pirq_from_irq);
 
-int bind_evtchn_to_irq(evtchn_port_t evtchn)
+static int bind_evtchn_to_irq_chip(evtchn_port_t evtchn, struct irq_chip *chip)
 {
        int irq;
        int ret;
@@ -885,7 +913,7 @@ int bind_evtchn_to_irq(evtchn_port_t evtchn)
                if (irq < 0)
                        goto out;
 
-               irq_set_chip_and_handler_name(irq, &xen_dynamic_chip,
+               irq_set_chip_and_handler_name(irq, chip,
                                              handle_edge_irq, "event");
 
                ret = xen_irq_info_evtchn_setup(irq, evtchn);
@@ -906,8 +934,19 @@ out:
 
        return irq;
 }
+
+int bind_evtchn_to_irq(evtchn_port_t evtchn)
+{
+       return bind_evtchn_to_irq_chip(evtchn, &xen_dynamic_chip);
+}
 EXPORT_SYMBOL_GPL(bind_evtchn_to_irq);
 
+int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn)
+{
+       return bind_evtchn_to_irq_chip(evtchn, &xen_lateeoi_chip);
+}
+EXPORT_SYMBOL_GPL(bind_evtchn_to_irq_lateeoi);
+
 static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
 {
        struct evtchn_bind_ipi bind_ipi;
@@ -949,8 +988,9 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
        return irq;
 }
 
-int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
-                                  evtchn_port_t remote_port)
+static int bind_interdomain_evtchn_to_irq_chip(unsigned int remote_domain,
+                                              evtchn_port_t remote_port,
+                                              struct irq_chip *chip)
 {
        struct evtchn_bind_interdomain bind_interdomain;
        int err;
@@ -961,10 +1001,26 @@ int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
        err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
                                          &bind_interdomain);
 
-       return err ? : bind_evtchn_to_irq(bind_interdomain.local_port);
+       return err ? : bind_evtchn_to_irq_chip(bind_interdomain.local_port,
+                                              chip);
+}
+
+int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
+                                  evtchn_port_t remote_port)
+{
+       return bind_interdomain_evtchn_to_irq_chip(remote_domain, remote_port,
+                                                  &xen_dynamic_chip);
 }
 EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irq);
 
+int bind_interdomain_evtchn_to_irq_lateeoi(unsigned int remote_domain,
+                                          evtchn_port_t remote_port)
+{
+       return bind_interdomain_evtchn_to_irq_chip(remote_domain, remote_port,
+                                                  &xen_lateeoi_chip);
+}
+EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irq_lateeoi);
+
 static int find_virq(unsigned int virq, unsigned int cpu, evtchn_port_t *evtchn)
 {
        struct evtchn_status status;
@@ -1061,14 +1117,15 @@ static void unbind_from_irq(unsigned int irq)
        mutex_unlock(&irq_mapping_update_lock);
 }
 
-int bind_evtchn_to_irqhandler(evtchn_port_t evtchn,
-                             irq_handler_t handler,
-                             unsigned long irqflags,
-                             const char *devname, void *dev_id)
+static int bind_evtchn_to_irqhandler_chip(evtchn_port_t evtchn,
+                                         irq_handler_t handler,
+                                         unsigned long irqflags,
+                                         const char *devname, void *dev_id,
+                                         struct irq_chip *chip)
 {
        int irq, retval;
 
-       irq = bind_evtchn_to_irq(evtchn);
+       irq = bind_evtchn_to_irq_chip(evtchn, chip);
        if (irq < 0)
                return irq;
        retval = request_irq(irq, handler, irqflags, devname, dev_id);
@@ -1079,18 +1136,38 @@ int bind_evtchn_to_irqhandler(evtchn_port_t evtchn,
 
        return irq;
 }
+
+int bind_evtchn_to_irqhandler(evtchn_port_t evtchn,
+                             irq_handler_t handler,
+                             unsigned long irqflags,
+                             const char *devname, void *dev_id)
+{
+       return bind_evtchn_to_irqhandler_chip(evtchn, handler, irqflags,
+                                             devname, dev_id,
+                                             &xen_dynamic_chip);
+}
 EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler);
 
-int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain,
-                                         evtchn_port_t remote_port,
-                                         irq_handler_t handler,
-                                         unsigned long irqflags,
-                                         const char *devname,
-                                         void *dev_id)
+int bind_evtchn_to_irqhandler_lateeoi(evtchn_port_t evtchn,
+                                     irq_handler_t handler,
+                                     unsigned long irqflags,
+                                     const char *devname, void *dev_id)
+{
+       return bind_evtchn_to_irqhandler_chip(evtchn, handler, irqflags,
+                                             devname, dev_id,
+                                             &xen_lateeoi_chip);
+}
+EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler_lateeoi);
+
+static int bind_interdomain_evtchn_to_irqhandler_chip(
+               unsigned int remote_domain, evtchn_port_t remote_port,
+               irq_handler_t handler, unsigned long irqflags,
+               const char *devname, void *dev_id, struct irq_chip *chip)
 {
        int irq, retval;
 
-       irq = bind_interdomain_evtchn_to_irq(remote_domain, remote_port);
+       irq = bind_interdomain_evtchn_to_irq_chip(remote_domain, remote_port,
+                                                 chip);
        if (irq < 0)
                return irq;
 
@@ -1102,8 +1179,33 @@ int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain,
 
        return irq;
 }
+
+int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain,
+                                         evtchn_port_t remote_port,
+                                         irq_handler_t handler,
+                                         unsigned long irqflags,
+                                         const char *devname,
+                                         void *dev_id)
+{
+       return bind_interdomain_evtchn_to_irqhandler_chip(remote_domain,
+                               remote_port, handler, irqflags, devname,
+                               dev_id, &xen_dynamic_chip);
+}
 EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irqhandler);
 
+int bind_interdomain_evtchn_to_irqhandler_lateeoi(unsigned int remote_domain,
+                                                 evtchn_port_t remote_port,
+                                                 irq_handler_t handler,
+                                                 unsigned long irqflags,
+                                                 const char *devname,
+                                                 void *dev_id)
+{
+       return bind_interdomain_evtchn_to_irqhandler_chip(remote_domain,
+                               remote_port, handler, irqflags, devname,
+                               dev_id, &xen_lateeoi_chip);
+}
+EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irqhandler_lateeoi);
+
 int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
                            irq_handler_t handler,
                            unsigned long irqflags, const char *devname, void *dev_id)
@@ -1637,6 +1739,21 @@ static struct irq_chip xen_dynamic_chip __read_mostly = {
        .irq_retrigger          = retrigger_dynirq,
 };
 
+static struct irq_chip xen_lateeoi_chip __read_mostly = {
+       /* The chip name needs to contain "xen-dyn" for irqbalance to work. */
+       .name                   = "xen-dyn-lateeoi",
+
+       .irq_disable            = disable_dynirq,
+       .irq_mask               = disable_dynirq,
+       .irq_unmask             = enable_dynirq,
+
+       .irq_ack                = mask_ack_dynirq,
+       .irq_mask_ack           = mask_ack_dynirq,
+
+       .irq_set_affinity       = set_affinity_irq,
+       .irq_retrigger          = retrigger_dynirq,
+};
+
 static struct irq_chip xen_pirq_chip __read_mostly = {
        .name                   = "xen-pirq",
 
index df1e6391f63ff6a34ec07538066acd7595e47158..3b8155c2ea034b186167b718112bf2731d0f8b6f 100644 (file)
 unsigned xen_evtchn_nr_channels(void);
 
 int bind_evtchn_to_irq(evtchn_port_t evtchn);
+int bind_evtchn_to_irq_lateeoi(evtchn_port_t evtchn);
 int bind_evtchn_to_irqhandler(evtchn_port_t evtchn,
                              irq_handler_t handler,
                              unsigned long irqflags, const char *devname,
                              void *dev_id);
+int bind_evtchn_to_irqhandler_lateeoi(evtchn_port_t evtchn,
+                             irq_handler_t handler,
+                             unsigned long irqflags, const char *devname,
+                             void *dev_id);
 int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu);
 int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
                            irq_handler_t handler,
@@ -32,12 +37,20 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi,
                           void *dev_id);
 int bind_interdomain_evtchn_to_irq(unsigned int remote_domain,
                                   evtchn_port_t remote_port);
+int bind_interdomain_evtchn_to_irq_lateeoi(unsigned int remote_domain,
+                                          evtchn_port_t remote_port);
 int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain,
                                          evtchn_port_t remote_port,
                                          irq_handler_t handler,
                                          unsigned long irqflags,
                                          const char *devname,
                                          void *dev_id);
+int bind_interdomain_evtchn_to_irqhandler_lateeoi(unsigned int remote_domain,
+                                                 evtchn_port_t remote_port,
+                                                 irq_handler_t handler,
+                                                 unsigned long irqflags,
+                                                 const char *devname,
+                                                 void *dev_id);
 
 /*
  * Common unbind function for all event sources. Takes IRQ to unbind from.
@@ -46,6 +59,14 @@ int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain,
  */
 void unbind_from_irqhandler(unsigned int irq, void *dev_id);
 
+/*
+ * Send late EOI for an IRQ bound to an event channel via one of the *_lateeoi
+ * functions above.
+ */
+void xen_irq_lateeoi(unsigned int irq, unsigned int eoi_flags);
+/* Signal an event was spurious, i.e. there was no action resulting from it. */
+#define XEN_EOI_FLAG_SPURIOUS  0x00000001
+
 #define XEN_IRQ_PRIORITY_MAX     EVTCHN_FIFO_PRIORITY_MAX
 #define XEN_IRQ_PRIORITY_DEFAULT EVTCHN_FIFO_PRIORITY_DEFAULT
 #define XEN_IRQ_PRIORITY_MIN     EVTCHN_FIFO_PRIORITY_MIN