diff mbox

[1/4] apic: add deliver_msi to APICCommonClass

Message ID 1462568028-31037-2-git-send-email-rkrcmar@redhat.com
State New
Headers show

Commit Message

Radim Krčmář May 6, 2016, 8:53 p.m. UTC
The only way to send interrupts from outside of APIC devices is to use
the MSI-inspired memory mapped interface.  The interface is not
sufficient because interrupt remapping in extended mode can exceed 8 bit
destination ids.

The proper way to deliver interrupts would be to design a bus (QPI), but
that is a big undertaking.  Real IR unit stores excessive bits in the
high work of MSI address register, which would be bit [63:40] in memory
address space, so I was considering two options:
 - a new special address space for APIC to handle MSI-compatible writes
 - one callback to the APIC class that delivers extended MSIs

This patch uses latter option, because it was easier for me, but I think
the former one could be a tad nicer.

Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
---
 hw/i386/kvm/apic.c              | 21 ++++++++++++++-------
 hw/i386/xen/xen_apic.c          |  7 +++++++
 hw/intc/apic.c                  |  7 +++++++
 include/hw/i386/apic_internal.h |  5 +++++
 4 files changed, 33 insertions(+), 7 deletions(-)

Comments

Jan Kiszka May 7, 2016, 1:50 p.m. UTC | #1
On 2016-05-06 22:53, Radim Krčmář wrote:
> The only way to send interrupts from outside of APIC devices is to use
> the MSI-inspired memory mapped interface.  The interface is not
> sufficient because interrupt remapping in extended mode can exceed 8 bit
> destination ids.
> 
> The proper way to deliver interrupts would be to design a bus (QPI), but
> that is a big undertaking.  Real IR unit stores excessive bits in the
> high work of MSI address register, which would be bit [63:40] in memory

s/work/word/

> address space, so I was considering two options:
>  - a new special address space for APIC to handle MSI-compatible writes
>  - one callback to the APIC class that delivers extended MSIs
> 
> This patch uses latter option, because it was easier for me, but I think
> the former one could be a tad nicer.

Makes sense.

Eventually, we should also finally untangle the MSI DMA request handling
from the xAPIC MMIO processing. The former could become pretty generic
(instead of being reimplemented by each APIC flavour), just potentially
redirected to an IOMMU when one is present. But that could come as
separate patches.

Jan
Eduardo Habkost May 13, 2016, 5:17 p.m. UTC | #2
On Fri, May 06, 2016 at 10:53:45PM +0200, Radim Krčmář wrote:
> The only way to send interrupts from outside of APIC devices is to use
> the MSI-inspired memory mapped interface.  The interface is not
> sufficient because interrupt remapping in extended mode can exceed 8 bit
> destination ids.
> 
> The proper way to deliver interrupts would be to design a bus (QPI), but
> that is a big undertaking.  Real IR unit stores excessive bits in the
> high work of MSI address register, which would be bit [63:40] in memory
> address space, so I was considering two options:
>  - a new special address space for APIC to handle MSI-compatible writes
>  - one callback to the APIC class that delivers extended MSIs
> 
> This patch uses latter option, because it was easier for me, but I think
> the former one could be a tad nicer.
> 
> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>

Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>

But I have a suggestion below:

> ---
>  hw/i386/kvm/apic.c              | 21 ++++++++++++++-------
>  hw/i386/xen/xen_apic.c          |  7 +++++++
>  hw/intc/apic.c                  |  7 +++++++
>  include/hw/i386/apic_internal.h |  5 +++++
>  4 files changed, 33 insertions(+), 7 deletions(-)
> 
> diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
> index 3c7c8fa00709..7881f13abb96 100644
> --- a/hw/i386/kvm/apic.c
> +++ b/hw/i386/kvm/apic.c
> @@ -147,6 +147,17 @@ static void kvm_apic_external_nmi(APICCommonState *s)
>      run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s);
>  }
>  
> +static void kvm_deliver_msi(MSIMessage *msg)
> +{
> +    int ret;
> +
> +    ret = kvm_irqchip_send_msi(kvm_state, *msg);
> +    if (ret < 0) {
> +        fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
> +                strerror(-ret));
> +    }
> +}
> +
>  static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr,
>                                    unsigned size)
>  {
> @@ -157,13 +168,7 @@ static void kvm_apic_mem_write(void *opaque, hwaddr addr,
>                                 uint64_t data, unsigned size)
>  {
>      MSIMessage msg = { .address = addr, .data = data };
> -    int ret;
> -
> -    ret = kvm_irqchip_send_msi(kvm_state, msg);
> -    if (ret < 0) {
> -        fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
> -                strerror(-ret));
> -    }
> +    kvm_deliver_msi(&msg);
>  }

You could call APICCommonClass::deliver_msi() here. Then (in a
follow-up patch?) both kvm_apic_mem_write() and
xen_apic_mem_write() could be replaced by a common function like:

  void apic_msi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
  {
      APICommonState *s = opaque;
      APICCommonClass *k = APIC_COMMON_GET_CLASS(s);
      MSIMessage msg = { .address = addr, .data = data };
      k->deliver_msi(&msg);
  }

(apic_mem_writel() is more complex, but maybe it could call the
common function instead of calling apic_send_msi() directly)

>  
>  static const MemoryRegionOps kvm_apic_io_ops = {
> @@ -202,6 +207,8 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data)
>      k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting;
>      k->vapic_base_update = kvm_apic_vapic_base_update;
>      k->external_nmi = kvm_apic_external_nmi;
> +
> +    k->deliver_msi = kvm_deliver_msi;
>  }
>  
>  static const TypeInfo kvm_apic_info = {
> diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c
> index 21d68ee04b0a..0f34f63d449d 100644
> --- a/hw/i386/xen/xen_apic.c
> +++ b/hw/i386/xen/xen_apic.c
> @@ -68,6 +68,11 @@ static void xen_apic_external_nmi(APICCommonState *s)
>  {
>  }
>  
> +static void xen_deliver_msi(MSIMessage *msi)
> +{
> +    xen_hvm_inject_msi(msi->address, msi->data);
> +}
> +
>  static void xen_apic_class_init(ObjectClass *klass, void *data)
>  {
>      APICCommonClass *k = APIC_COMMON_CLASS(klass);
> @@ -78,6 +83,8 @@ static void xen_apic_class_init(ObjectClass *klass, void *data)
>      k->get_tpr = xen_apic_get_tpr;
>      k->vapic_base_update = xen_apic_vapic_base_update;
>      k->external_nmi = xen_apic_external_nmi;
> +
> +    k->deliver_msi = xen_deliver_msi;
>  }
>  
>  static const TypeInfo xen_apic_info = {
> diff --git a/hw/intc/apic.c b/hw/intc/apic.c
> index 28c2ea540608..b893942c6e73 100644
> --- a/hw/intc/apic.c
> +++ b/hw/intc/apic.c
> @@ -877,6 +877,11 @@ static void apic_realize(DeviceState *dev, Error **errp)
>      msi_nonbroken = true;
>  }
>  
> +static void apic_deliver_msi(MSIMessage *msi)
> +{
> +    apic_send_msi(msi->address, msi->data);
> +}
> +
>  static void apic_class_init(ObjectClass *klass, void *data)
>  {
>      APICCommonClass *k = APIC_COMMON_CLASS(klass);
> @@ -889,6 +894,8 @@ static void apic_class_init(ObjectClass *klass, void *data)
>      k->external_nmi = apic_external_nmi;
>      k->pre_save = apic_pre_save;
>      k->post_load = apic_post_load;
> +
> +    k->deliver_msi = apic_deliver_msi;
>  }
>  
>  static const TypeInfo apic_info = {
> diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
> index 74fe935e8eab..227ef30c5027 100644
> --- a/include/hw/i386/apic_internal.h
> +++ b/include/hw/i386/apic_internal.h
> @@ -146,6 +146,11 @@ typedef struct APICCommonClass
>      void (*pre_save)(APICCommonState *s);
>      void (*post_load)(APICCommonState *s);
>      void (*reset)(APICCommonState *s);
> +
> +    /* deliver_msi emulates an APIC bus and its proper place would be in a new
> +     * device, but it's convenient to have it here for now.
> +     */
> +    void (*deliver_msi)(MSIMessage *msi);
>  } APICCommonClass;
>  
>  struct APICCommonState {
> -- 
> 2.8.2
>
diff mbox

Patch

diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index 3c7c8fa00709..7881f13abb96 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -147,6 +147,17 @@  static void kvm_apic_external_nmi(APICCommonState *s)
     run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s);
 }
 
+static void kvm_deliver_msi(MSIMessage *msg)
+{
+    int ret;
+
+    ret = kvm_irqchip_send_msi(kvm_state, *msg);
+    if (ret < 0) {
+        fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
+                strerror(-ret));
+    }
+}
+
 static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr,
                                   unsigned size)
 {
@@ -157,13 +168,7 @@  static void kvm_apic_mem_write(void *opaque, hwaddr addr,
                                uint64_t data, unsigned size)
 {
     MSIMessage msg = { .address = addr, .data = data };
-    int ret;
-
-    ret = kvm_irqchip_send_msi(kvm_state, msg);
-    if (ret < 0) {
-        fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
-                strerror(-ret));
-    }
+    kvm_deliver_msi(&msg);
 }
 
 static const MemoryRegionOps kvm_apic_io_ops = {
@@ -202,6 +207,8 @@  static void kvm_apic_class_init(ObjectClass *klass, void *data)
     k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting;
     k->vapic_base_update = kvm_apic_vapic_base_update;
     k->external_nmi = kvm_apic_external_nmi;
+
+    k->deliver_msi = kvm_deliver_msi;
 }
 
 static const TypeInfo kvm_apic_info = {
diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c
index 21d68ee04b0a..0f34f63d449d 100644
--- a/hw/i386/xen/xen_apic.c
+++ b/hw/i386/xen/xen_apic.c
@@ -68,6 +68,11 @@  static void xen_apic_external_nmi(APICCommonState *s)
 {
 }
 
+static void xen_deliver_msi(MSIMessage *msi)
+{
+    xen_hvm_inject_msi(msi->address, msi->data);
+}
+
 static void xen_apic_class_init(ObjectClass *klass, void *data)
 {
     APICCommonClass *k = APIC_COMMON_CLASS(klass);
@@ -78,6 +83,8 @@  static void xen_apic_class_init(ObjectClass *klass, void *data)
     k->get_tpr = xen_apic_get_tpr;
     k->vapic_base_update = xen_apic_vapic_base_update;
     k->external_nmi = xen_apic_external_nmi;
+
+    k->deliver_msi = xen_deliver_msi;
 }
 
 static const TypeInfo xen_apic_info = {
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 28c2ea540608..b893942c6e73 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -877,6 +877,11 @@  static void apic_realize(DeviceState *dev, Error **errp)
     msi_nonbroken = true;
 }
 
+static void apic_deliver_msi(MSIMessage *msi)
+{
+    apic_send_msi(msi->address, msi->data);
+}
+
 static void apic_class_init(ObjectClass *klass, void *data)
 {
     APICCommonClass *k = APIC_COMMON_CLASS(klass);
@@ -889,6 +894,8 @@  static void apic_class_init(ObjectClass *klass, void *data)
     k->external_nmi = apic_external_nmi;
     k->pre_save = apic_pre_save;
     k->post_load = apic_post_load;
+
+    k->deliver_msi = apic_deliver_msi;
 }
 
 static const TypeInfo apic_info = {
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 74fe935e8eab..227ef30c5027 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -146,6 +146,11 @@  typedef struct APICCommonClass
     void (*pre_save)(APICCommonState *s);
     void (*post_load)(APICCommonState *s);
     void (*reset)(APICCommonState *s);
+
+    /* deliver_msi emulates an APIC bus and its proper place would be in a new
+     * device, but it's convenient to have it here for now.
+     */
+    void (*deliver_msi)(MSIMessage *msi);
 } APICCommonClass;
 
 struct APICCommonState {